Add pgfence Postgres migration safety rule#294
Conversation
Adds a Postgres migration safety rule for AI coding assistants. Covers lock modes, common DDL footguns, and safe rewrite recipes for adding NOT NULL columns, foreign keys, unique constraints, and indexes. Sourced from the pgfence analyzer rule catalog (https://pgfence.com).
📝 WalkthroughWalkthroughA new Postgres migration safety ruleset document is added, providing session-level timeout configurations, lock mode references, a catalog of prohibited DDL patterns that risk production table locks or large rewrites, safe rewrite recipes with expand/backfill/contract sequences, and diagnostic guidance using pgfence tooling. ChangesPostgres Migration Safety Ruleset
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a new Cursor rule document describing Postgres migration safety practices, sourced from the pgfence analyzer.
Changes:
- Introduces
rules/pgfence-postgres-migration-safety.mdcwith guidance on lock modes, DDL footguns, and safe rewrite recipes. - Documents recommended session settings (lock_timeout, statement_timeout, etc.) for migrations.
- Provides example recipes for adding NOT NULL columns, foreign keys, unique constraints, and indexes safely.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 020459de2f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| -- Migration 2: batched backfill (separate file, no transaction wrapper) | ||
| DO $$ |
There was a problem hiding this comment.
Split the backfill into separate transactions
When this recipe is used on a large table, the DO block runs the entire loop as one transaction even if the migration tool is not wrapping the file, so every row lock/dead tuple and all WAL are retained until the loop finishes and a timeout rolls back the whole backfill. That defeats the stated batched-backfill safety; this should be shown as one committed batch per invocation/statement or a procedure that explicitly commits between batches.
Useful? React with 👍 / 👎.
|
|
||
| | Lock mode | What it blocks | | ||
| |---|---| | ||
| | ACCESS SHARE | Nothing | |
There was a problem hiding this comment.
Correct the lock conflict table
When agents use this as the lock-mode reference for production DDL, this row understates conflicts: an ACCESS SHARE lock held by ordinary reads conflicts with ACCESS EXCLUSIVE, so it does block operations that need that lock rather than blocking “Nothing”. Because the table is meant to guide migration safety decisions, this should either use PostgreSQL's actual conflict matrix or avoid the inaccurate shorthand.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@rules/pgfence-postgres-migration-safety.mdc`:
- Around line 27-36: The lock conflict matrix rows for table-level locks are
incorrect; update the table entries for ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE,
SHARE, and SHARE ROW EXCLUSIVE to match PostgreSQL semantics: change ACCESS
SHARE to indicate it conflicts only with ACCESS EXCLUSIVE; change ROW SHARE to
indicate it conflicts with EXCLUSIVE and ACCESS EXCLUSIVE only; change ROW
EXCLUSIVE to indicate it conflicts with SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE,
and ACCESS EXCLUSIVE but is compatible with SHARE UPDATE EXCLUSIVE; replace the
vague “writes”/“most writes” descriptions for SHARE and SHARE ROW EXCLUSIVE with
explicit conflict sets (SHARE conflicts with ROW EXCLUSIVE, SHARE ROW EXCLUSIVE,
EXCLUSIVE, ACCESS EXCLUSIVE; SHARE ROW EXCLUSIVE conflicts with everything
except ACCESS SHARE) so the table enumerates precise conflicting modes by name.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: f877223b-ffa2-4329-8355-5c0b82280b22
📒 Files selected for processing (1)
rules/pgfence-postgres-migration-safety.mdc
| | Lock mode | What it blocks | | ||
| |---|---| | ||
| | ACCESS SHARE | Nothing | | ||
| | ROW SHARE | ROW EXCLUSIVE+ | | ||
| | ROW EXCLUSIVE | SHARE+ | | ||
| | SHARE UPDATE EXCLUSIVE | SHARE UPDATE EXCLUSIVE+ | | ||
| | SHARE | Writes (reads allowed) | | ||
| | SHARE ROW EXCLUSIVE | Most writes | | ||
| | EXCLUSIVE | Everything except ACCESS SHARE | | ||
| | ACCESS EXCLUSIVE | Everything, reads included | |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
PostgreSQL explicit locking conflict matrix for table-level lock modes (ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE). Please provide the official docs table and a concise conflict summary for each mode.
💡 Result:
In PostgreSQL, all explicit table-level locks are governed by a conflict matrix that defines which modes can be held concurrently on the same table. The official documentation (Table 13.2) provides the definitive compatibility matrix [1][2]. PostgreSQL Table-Level Lock Conflict Matrix The following table shows the conflicts between lock modes. An 'X' indicates that the two lock modes conflict. | Requested Lock | AS | RS | RE | SUE | S | SRE | E | AE | |:--- |:---: |:---: |:---: |:---: |:---: |:---: |:---: |:---: | | ACCESS SHARE (AS) | | | | | | | | X | | ROW SHARE (RS) | | | | | | | X | X | | ROW EXCLUSIVE (RE) | | | | | X | X | X | X | | SHARE UPDATE EXCL. (SUE) | | | | X | X | X | X | X | | SHARE (S) | | | X | X | | X | X | X | | SHARE ROW EXCL. (SRE) | | | X | X | X | X | X | X | | EXCLUSIVE (E) | | X | X | X | X | X | X | X | | ACCESS EXCLUSIVE (AE) | X | X | X | X | X | X | X | X | Note: A transaction never conflicts with itself [1][2]. Concise Summary of Lock Modes 1. ACCESS SHARE: Conflicts only with ACCESS EXCLUSIVE. Acquired by SELECT statements. 2. ROW SHARE: Conflicts with EXCLUSIVE and ACCESS EXCLUSIVE. Acquired by SELECT FOR UPDATE and SELECT FOR SHARE. 3. ROW EXCLUSIVE: Conflicts with SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, and ACCESS EXCLUSIVE. Acquired by INSERT, UPDATE, and DELETE. 4. SHARE UPDATE EXCLUSIVE: Conflicts with itself, SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, and ACCESS EXCLUSIVE. Acquired by VACUUM, ANALYZE, and CREATE INDEX CONCURRENTLY. 5. SHARE: Conflicts with ROW EXCLUSIVE, SHARE UPDATE EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE, and ACCESS EXCLUSIVE. Acquired by CREATE INDEX (non-concurrent). 6. SHARE ROW EXCLUSIVE: Conflicts with all modes except ACCESS SHARE. Acquired by various DDL commands like CREATE TRIGGER. 7. EXCLUSIVE: Conflicts with all modes except ACCESS SHARE. Acquired by REFRESH MATERIALIZED VIEW CONCURRENTLY. 8. ACCESS EXCLUSIVE: Conflicts with all lock modes (including itself). Acquired by DROP, TRUNCATE, REINDEX, VACUUM FULL, and LOCK TABLE. All listed modes are table-level locks, even if the name contains the word row; these names are historical [1][2][3]. Non-conflicting lock modes can be held concurrently by many transactions [1][2].
Citations:
- 1: https://www.postgresql.org/docs/18/explicit-locking.html
- 2: https://www.postgresql.org/docs/current/explicit-locking.html
- 3: https://www.postgresql.org/docs/11/sql-lock.html
Fix lock conflict matrix for table-level locks (lines 27-36) to match PostgreSQL compatibility rules.
Several entries don’t reflect PostgreSQL’s table-lock conflict matrix and can lead to incorrect migration-safety gating:
ACCESS SHARE: conflicts only withACCESS EXCLUSIVE(not “Nothing”).ROW SHARE: conflicts only withEXCLUSIVEandACCESS EXCLUSIVE(notROW EXCLUSIVE+).ROW EXCLUSIVE: conflicts withSHARE,SHARE ROW EXCLUSIVE,EXCLUSIVE,ACCESS EXCLUSIVE(but is compatible withSHARE UPDATE EXCLUSIVE).SHARE/SHARE ROW EXCLUSIVE: the current “writes” / “most writes” wording is inaccurate;SHARE ROW EXCLUSIVEconflicts with all modes exceptACCESS SHARE.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@rules/pgfence-postgres-migration-safety.mdc` around lines 27 - 36, The lock
conflict matrix rows for table-level locks are incorrect; update the table
entries for ACCESS SHARE, ROW SHARE, ROW EXCLUSIVE, SHARE, and SHARE ROW
EXCLUSIVE to match PostgreSQL semantics: change ACCESS SHARE to indicate it
conflicts only with ACCESS EXCLUSIVE; change ROW SHARE to indicate it conflicts
with EXCLUSIVE and ACCESS EXCLUSIVE only; change ROW EXCLUSIVE to indicate it
conflicts with SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, and ACCESS EXCLUSIVE but
is compatible with SHARE UPDATE EXCLUSIVE; replace the vague “writes”/“most
writes” descriptions for SHARE and SHARE ROW EXCLUSIVE with explicit conflict
sets (SHARE conflicts with ROW EXCLUSIVE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS
EXCLUSIVE; SHARE ROW EXCLUSIVE conflicts with everything except ACCESS SHARE) so
the table enumerates precise conflicting modes by name.
Summary
Adds a new Cursor rule for writing online-safe Postgres migrations. The rule covers session-level safety settings, the Postgres lock mode reference, DDL footguns that lock production tables, and safe rewrite recipes for the most common dangerous patterns.
Contribution Type
rules/*.mdcruleValue To Cursor Users
When Cursor is asked to write or review a schema migration, it has no built-in awareness that a one-line
ALTER COLUMN ... TYPEorCREATE INDEXwithoutCONCURRENTLYcan lock a production table underACCESS EXCLUSIVEfor the duration of a rewrite. This rule gives the model the Postgres lock-mode reference, the catalog of patterns to refuse, and the expand/backfill/contract recipes to suggest instead.The
globstarget the common migration paths across the major ORMs (Prisma, TypeORM, Knex, Sequelize, Drizzle, ActiveRecord), so the rule activates only when the model is touching migration files, not on every edit.Added Or Changed Files
rules/pgfence-postgres-migration-safety.mdc: new rule file with frontmatter (description,globs,alwaysApply: false) and the rule body.Quality Checklist
react-typescript.mdc.rules/*.mdcfiles include frontmatter with a non-emptydescription, relevantglobs, andalwaysApply: falseunless the rule is universal.Notes For Maintainers
The rule body is a curated extract of the rule catalog from pgfence, an MIT-licensed open-source Postgres migration safety tool. The catalog itself is at
RULES.md. pgfence the CLI is mentioned only as the way to get an authoritative verdict on a specific statement (pgfence analyze/pgfence explain); the rule is useful without ever installing it.No marketing language, no product directory listing, no affiliate links. The rule stands on its own as reusable Postgres safety guidance for AI-assisted migration editing.