Skip to content

feat: add graphile-bulk-mutations plugin#1140

Open
pyramation wants to merge 2 commits into
mainfrom
feat/graphile-bulk-mutations
Open

feat: add graphile-bulk-mutations plugin#1140
pyramation wants to merge 2 commits into
mainfrom
feat/graphile-bulk-mutations

Conversation

@pyramation
Copy link
Copy Markdown
Contributor

@pyramation pyramation commented May 12, 2026

Summary

Adds graphile-bulk-mutations — a PostGraphile v5 plugin for bulk insert, upsert, update, and delete mutations with ON CONFLICT handling and column-level grant safety.

Features:

  • Bulk insert with optional ON CONFLICT DO NOTHING
  • Bulk upsert with ON CONFLICT DO UPDATE SET (selective column updates via updateColumns)
  • Bulk update/delete with condition-based WHERE clauses (supports both Condition and Filter types)
  • Column-level SELECT grant safety — RETURNING <pk_columns> + follow-up SELECT instead of RETURNING *
  • PG parameter batching (auto-split at 32K limit)
  • Opt-in via smart tags (@behavior +bulkInsert +bulkUpsert +bulkUpdate +bulkDelete)
  • Column-based conflict targeting (ON CONFLICT (col1, col2))
  • Configurable: bulkMaxRows, bulkRequireWhere, bulkNaming

Architecture: 6 plugins (BulkInflectionPlugin, BulkTypesPlugin, BulkInsertPlugin, BulkUpsertPlugin, BulkUpdatePlugin, BulkDeletePlugin) using sideEffectWithPgClient for raw SQL execution.

Tests: 10 integration tests via graphile-test — 4 schema generation, 3 insert, 1 upsert, 1 update, 1 delete.

Related: Planning issue #844

Review & Testing Checklist for Human

  • Verify ON CONFLICT behavior with real tables that have column-level SELECT grants (the RETURNING <pk> + follow-up SELECT pattern should handle this, but hasn't been tested with restricted roles)
  • Test with graphile-connection-filter preset to verify operator-based WHERE conditions work (current tests only cover simple equality via Condition type)
  • Verify upsert updateColumns option works correctly (selectively updating specific columns on conflict)
  • Consider edge cases: multi-column unique constraints, tables with no primary key, composite PKs

Recommended test plan

  1. Add @behavior +bulkInsert +bulkUpsert to a test table with column-level SELECT grants
  2. Run a bulk insert with onConflict: { constraint: <ENUM>, action: IGNORE }
  3. Verify that restricted columns are not leaked in the response
  4. Test with connection-filter enabled: where: { name: { startsWith: "A" } } on bulk update/delete

Notes

  • The plugin uses column-based conflict targeting (ON CONFLICT ("col1", "col2")) instead of constraint names because PgResourceUnique doesn't store the actual PostgreSQL constraint name
  • WHERE clause builder supports both simple equality (PostGraphile's built-in Condition type) and operator-based filtering (Filter type from graphile-connection-filter)
  • ESLint v9 config migration issue is pre-existing across the monorepo (not caused by this PR)

Link to Devin session: https://app.devin.ai/sessions/b5701e74c8b44bbf89db370f3323acf2
Requested by: @pyramation

Adds a PostGraphile v5 plugin for bulk insert, upsert, update, and delete
mutations with ON CONFLICT handling. Features:

- bulkCreateX: INSERT ... VALUES with optional ON CONFLICT DO NOTHING
- bulkUpsertX: INSERT ... ON CONFLICT DO UPDATE SET (per-column control)
- bulkUpdateX: UPDATE ... SET ... WHERE (connection-filter conditions)
- bulkDeleteX: DELETE ... WHERE (connection-filter conditions)

Design decisions:
- Opt-in per table via smart tags (@behavior +bulkInsert etc.)
- Three-layer gating: database enable_bulk + api enable_bulk + smart tags
- Configurable naming: bulk (default), pluralized, many
- Auto-batch at PostgreSQL 32K parameter limit
- Per-table constraint enums and column enums for ON CONFLICT
- Safety guards: bulkMaxRows (1000), bulkRequireWhere (true)

Closes constructive-io/constructive-planning#844
@devin-ai-integration
Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@socket-security
Copy link
Copy Markdown

socket-security Bot commented May 12, 2026

No dependency changes detected. Learn more about Socket for GitHub.

👍 No dependency changes detected in pull request

- Fix plan functions: use getRaw('input') for proper Grafast step resolution
- Fix constraint targeting: use column-based ON CONFLICT (col1, col2) instead of
  constraint names, since PgResourceUnique doesn't store PG constraint names
- Fix WHERE clause builder: support both simple equality (Condition type) and
  operator-based (Filter type) conditions
- Fix sql-builder: use conflictColumns instead of constraintName
- Add integration tests: 4 schema generation + 3 insert + 1 upsert + 1 update + 1 delete
- Add test seed SQL with smart tags for bulk behavior opt-in
- Align README with other graphile packages (logo, badges, examples)
- Remove debug logging
- Column-level SELECT grant safety (RETURNING <pk> + follow-up SELECT)
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.

1 participant