Skip to content

Add pgfence Postgres migration safety rule#294

Open
flvmnt wants to merge 1 commit into
PatrickJS:mainfrom
flvmnt:add-pgfence-migration-safety-rule
Open

Add pgfence Postgres migration safety rule#294
flvmnt wants to merge 1 commit into
PatrickJS:mainfrom
flvmnt:add-pgfence-migration-safety-rule

Conversation

@flvmnt
Copy link
Copy Markdown

@flvmnt flvmnt commented May 27, 2026

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

  • New rules/*.mdc rule

Value 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 ... TYPE or CREATE INDEX without CONCURRENTLY can lock a production table under ACCESS EXCLUSIVE for 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 globs target 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

  • The contribution includes original rule content, or clearly credits the source.
  • New rule files use a descriptive kebab-case filename, such as react-typescript.mdc.
  • New rules/*.mdc files include frontmatter with a non-empty description, relevant globs, and alwaysApply: false unless the rule is universal.
  • README links use canonical GitHub URLs for repo files and point to the correct category.
  • The text is neutral and useful, not sales copy.
  • This is not a standalone external tool, product, directory, marketplace, or service listing.
  • No secrets, tokens, affiliate links, tracking links, or unrelated product claims are included.
  • I checked for duplicate or near-duplicate existing entries.

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.

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).
Copilot AI review requested due to automatic review settings May 27, 2026 14:26
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

📝 Walkthrough

Walkthrough

A 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.

Changes

Postgres Migration Safety Ruleset

Layer / File(s) Summary
Ruleset metadata and core migration safety guidance
rules/pgfence-postgres-migration-safety.mdc
Ruleset metadata and description establish intent; foundational migration safety guidance and a SQL snippet to prepend to every migration (lock/statement/idle timeouts, application_name).
Lock mode reference and prohibited DDL footguns
rules/pgfence-postgres-migration-safety.mdc
Lock mode reference table maps Postgres lock modes to what they block; comprehensive enumeration of unsafe operations (table rewrites under ACCESS EXCLUSIVE, unsafe constraints/indexes, destructive operations, enum/materialized view hazards).
Safe rewrite recipes and diagnostic guidance
rules/pgfence-postgres-migration-safety.mdc
Expand/backfill/contract patterns for adding NOT NULL columns with defaults, foreign keys, unique constraints, and creating indexes; guidance to use pgfence analyze/explain for lock-mode and safety assessment of uncertain DDL statements.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 A rabbit hops through Postgres paths so bright,
With timeouts set and locks held tight,
No ACCESS EXCLUSIVE shall cause dismay,
Safe rewrites bloom in the expand-backfill way! 🌱

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a Postgres migration safety rule for pgfence.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The PR description is comprehensive and follows the template structure with all required sections completed, including Summary, Contribution Type, Value To Cursor Users, Added Or Changed Files, Quality Checklist, and Notes For Maintainers.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.mdc with 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.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +134 to +135
-- Migration 2: batched backfill (separate file, no transaction wrapper)
DO $$
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 |
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 9ec9007 and 020459d.

📒 Files selected for processing (1)
  • rules/pgfence-postgres-migration-safety.mdc

Comment on lines +27 to +36
| 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 |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 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:


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 with ACCESS EXCLUSIVE (not “Nothing”).
  • ROW SHARE: conflicts only with EXCLUSIVE and ACCESS EXCLUSIVE (not ROW EXCLUSIVE+).
  • ROW EXCLUSIVE: conflicts with SHARE, SHARE ROW EXCLUSIVE, EXCLUSIVE, ACCESS EXCLUSIVE (but is compatible with SHARE UPDATE EXCLUSIVE).
  • SHARE / SHARE ROW EXCLUSIVE: the current “writes” / “most writes” wording is inaccurate; SHARE ROW EXCLUSIVE conflicts with all modes except ACCESS 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.

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.

2 participants