MP058warning

multi-alter-table-same-table

Independent ALTER TABLE statements on the same table acquire the lock once each. Combine them into a single statement.

operation
Tables
lock taken
ACCESS EXCLUSIVE
remediation
Manual rewrite
category
Lock safety

What triggers it

Two or more ALTER TABLE statements on the same table inside the same transaction block (or each its own autocommit statement) where merging their subcommands into one statement would cost nothing. The rule collects every AlterTableStmt targeting the table in the enclosing block and reports once, on the first member, when more than one exists and isMergeFree finds none of the split-on-purpose shapes.

What does not

A group where any member carries an AT_ValidateConstraint subtype, or where AT_SetNotNull and AT_DropConstraint appear together — both patterns the handbook says to keep split. Also stands down when a constraint added NOT VALID in the group is dropped later, or when a statement between two members reads or writes the table and would have its position moved by a merge. A table with only one ALTER TABLE statement never reaches the check.

Where it applies

Applies to every PostgreSQL version MigrationPilot targets. It works on the SQL text alone — no database connection needed.

The lock, and what it blocks

Each separate ALTER TABLE acquires ACCESS EXCLUSIVE independently, so N statements mean N lock/unlock cycles, each queueing behind whatever holds the lock and blocking every reader and writer that arrives after it. Merging the mergeable subcommands into one statement cuts that to a single cycle.

Why it matters

Each ALTER TABLE acquires ACCESS EXCLUSIVE lock independently. Multiple separate statements mean multiple lock/unlock cycles. Combining into a single ALTER TABLE reduces the blocking window from N lock cycles to one. This only holds for subcommands that are independent: a NOT VALID constraint and its VALIDATE, or a SET NOT NULL and the CHECK constraint proving it, are deliberately kept apart, and merging them puts the scan they were written to avoid back under ACCESS EXCLUSIVE.

Unsafe, and safe

Flagged

ALTER TABLE users ADD COLUMN bio text;
ALTER TABLE users ADD COLUMN avatar text;
-- 2 separate lock acquisitions

Safe alternative

ALTER TABLE users
  ADD COLUMN bio text,
  ADD COLUMN avatar text;
-- 1 lock acquisition

What it assumes

The rule assumes a merge is free once the three split-on-purpose shapes are ruled out, but its only check for an ordering dependency is whether an intervening statement literally names the table — a subtler dependency wouldn't be caught. It also has no idea how contended the table's lock actually is, so on a quiet table the extra cycles may not matter in practice.

What backs this rule

Every rule is a claim about PostgreSQL, so it carries what the claim rests on: a handbook chapter that cites the manual, the incidents that put it there, and the version it was last checked against.

Locks in PostgreSQL are held until the end of the transaction, never released early.

verified against
PostgreSQL 17.10
last checked
2026-08-11
confidence
High

What the CLI prints

migrationpilot analyze migration.sql
⚠ [MP058] WARNING (line 1)
  2 separate ALTER TABLE statements on "users". Each acquires ACCESS EXCLUSIVE lock independently. Combine into a single ALTER TABLE with multiple subcommands to reduce lock acquisitions from 2 to 1.

  Safe alternative:
  -- Combine into a single statement:
  -- ALTER TABLE users
  --   ADD COLUMN ...,
  --   ALTER COLUMN ...,
  --   ADD CONSTRAINT ...;

  Why: Each ALTER TABLE acquires ACCESS EXCLUSIVE lock independently. Multiple separate ALTER TABLE statements on the same table means multiple lock/unlock cycles, each going through the lock queue. Long-running queries must finish before each lock acquisition. Combining subcommands into a single ALTER TABLE reduces the blocking window from N separate lock cycles to one. This only holds for subcommands that are independent of each other: a NOT VALID constraint and its VALIDATE, or a SET NOT NULL and the CHECK constraint proving it, are deliberately kept apart, and merging them takes the scan they were written to avoid and puts it back under ACCESS EXCLUSIVE.
  Docs: https://migrationpilot.dev/rules/mp058

Generated by running the CLI's own formatter over the flagged example above, so it is the text the tool actually produces. A real run also reports the other rules that fire on the same statement; those blocks are left out here.

Turning it off

For one statement, put a comment on the line before it:

-- migrationpilot-disable MP058
ALTER TABLE users ADD COLUMN bio text;

For the whole project, in .migrationpilotrc.yml — by name or by id:

.migrationpilotrc.yml
rules:
  MP058: false

# or keep it, and downgrade it
rules:
  MP058:
    severity: warning

Try it

Open this rule's flagged example in the playground. It runs in your browser — edit it and watch the finding appear and disappear.

Run MP058 in the playground

Related rules