ban-lock-table
Explicit LOCK TABLE in migrations blocks queries and can cause deadlocks.
- operation
- Tables
- lock taken
- ACCESS EXCLUSIVE
- remediation
- Manual rewrite
- category
- Lock safety
- Blocks reads
- Blocks writes
- Can stall the lock queue
What triggers it
Any explicit LockStmt — a LOCK TABLE statement naming one or more relations — regardless of mode. The rule reads lockStmt.mode only to report which of PostgreSQL's lock modes was requested.
What does not
The only guard is !lockStmt?.relations, so the rule fires on every LOCK TABLE the parser produces. There's no negative case beyond a statement that isn't a LockStmt at all.
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
Whatever mode the statement names, from ACCESS SHARE up through ACCESS EXCLUSIVE — the rule reports the specific mode from lockStmt.mode, defaulting the message to ACCESS EXCLUSIVE only when that field is missing. At the high end, ACCESS EXCLUSIVE blocks every other lock mode, including plain reads.
Why it matters
LOCK TABLE acquires an explicit lock that can block reads and writes. PostgreSQL DDL statements automatically acquire the correct lock — explicit LOCK TABLE is rarely needed and often indicates a flawed migration strategy. High lock modes (EXCLUSIVE, ACCESS EXCLUSIVE) block all other operations.
Unsafe, and safe
Flagged
LOCK TABLE users IN ACCESS EXCLUSIVE MODE;
Safe alternative
-- Let PostgreSQL acquire locks automatically via DDL -- No explicit LOCK TABLE needed ALTER TABLE users ADD COLUMN email TEXT;
What it assumes
The rule treats every explicit LOCK TABLE as suspect, but there are legitimate uses — deliberately serializing access to a small table, for instance — where an explicit lock is the right tool and PostgreSQL's automatic DDL locking doesn't apply.
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.
Entry 02 covers the mechanism: a blocked DDL statement blocks everything behind it.
- verified against
- PostgreSQL 17.10
- last checked
- 2026-08-11
- confidence
- High
PostgreSQL manual
Public incidents and write-ups
What the CLI prints
✗ [MP065] CRITICAL (line 1) Explicit LOCK TABLE on users in ACCESS EXCLUSIVE mode. PostgreSQL DDL acquires locks automatically. Remove the explicit lock or restructure the migration. Why: LOCK TABLE acquires an explicit lock that can block reads and writes. PostgreSQL DDL statements automatically acquire the correct lock. Explicit LOCK TABLE is rarely needed and often indicates a flawed migration strategy. High lock modes (EXCLUSIVE, ACCESS EXCLUSIVE) block all other operations. Docs: https://migrationpilot.dev/rules/mp065
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 MP065 LOCK TABLE users IN ACCESS EXCLUSIVE MODE;
For the whole project, in .migrationpilotrc.yml — by name or by id:
rules:
MP065: false
# or keep it, and downgrade it
rules:
MP065:
severity: warningTry 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 MP065 in the playground