Skip to content

Conversation

@matt-metivier
Copy link

@matt-metivier matt-metivier commented Jan 26, 2026

What

Implements upsert support via onConflict argument on insert mutations.
Closes #190

Why

This enables upsert operations (INSERT ... ON CONFLICT DO UPDATE) through the GraphQL API, which is a common requirement for applications that need idempotent writes.

Changes

  • SQL Context: Load index names from pg_class to identify constraints
  • SQL Types: Add on_conflict_indexes() and has_upsert_support() methods to Table
  • GraphQL Types:
    • New {Table}OnConflict input type with constraint, updateColumns, and filter fields
    • New {Table}Constraint enum (unique index names)
    • New {Table}UpdateColumn enum (updatable column names)
    • Conditionally expose onConflict arg only on tables with valid constraints
  • Builder: Parse onConflict argument and map GraphQL column names back to Column objects
  • Transpile: Generate ON CONFLICT ON CONSTRAINT ... DO UPDATE SET ... WHERE ...

Example

mutation {
  insertIntoAccountCollection(
    objects: [{ id: 1, email: "new@example.com", name: "Updated" }]
    onConflict: {
      constraint: account_pkey
      updateColumns: [email, name]
      filter: { status: { eq: "active" } }
    }
  ) {
    affectedCount
    records { id email name }
  }
}

Notes

  • Tables with serial/generated primary keys will not expose onConflict (constraint columns must be insertable)
  • Empty updateColumns results in DO NOTHING
  • Based on WIP - OnConflict support for Insert mutations #503 but reimplemented on current master without unrelated changes

Tests

Added tests in test/sql/mutation_insert_on_conflict.sql

@matt-metivier matt-metivier changed the title feat: Add upsert support via onConflict argument [#190]: Add upsert support via onConflict argument Jan 26, 2026
@matt-metivier
Copy link
Author

Whenever you've time @olirice, I tried to match & build on what you had in #503

- Rename updateColumns to updateFields
- Rename {Table}UpdateColumn enum to {Table}Field
- Rename {Table}Constraint enum to {Table}OnConflictConstraint
- Keep DO NOTHING improvement for empty updateFields
- Make graphql_column_field_name method public
- Add OnConflictInput to all __Type match arms (kind, name, description, fields, interfaces, enum_values, input_fields)
- Add TableColumns and OnConflictTarget to EnumSource match in parser_util.rs
- Fix string comparison in builder.rs using as_str()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Upsert support

1 participant