SQL Migration Lock Check · comparison
A careful reviewer genuinely catches the well-known single-statement case: a bare CREATE INDEX, a stray TRUNCATE. This page names no company or product — reading a migration file by hand before you run it is a habit most developers already have, not a competing tool.
The risks that live entirely on one line are the ones a reviewer who knows the rules already catches well: CREATE INDEX with no CONCURRENTLY, VACUUM FULL, a bare TRUNCATE. This tool's create-index-not-concurrent, full-table-rewrite, and truncate checks flag exactly the same statements, for the same reason.
| Case | Reading by hand | This tool |
|---|---|---|
A missing lock_timeout | Depends on whether a SET lock_timeout appears earlier in the same file, possibly dozens of lines away from the statement it protects | Tracks every SET lock_timeout against every exclusive-lock statement below it, every time |
NOT VALID + VALIDATE CONSTRAINT sharing one transaction | Both lines look correct individually; the bug is which BEGIN block they happen to sit inside | Flags the pair specifically when they share a transaction, via validate-in-same-transaction |
| Several exclusive-lock statements in one transaction | Each one reads as an acceptably brief lock on its own; adding five of them into one running total while reading top to bottom is easy to get wrong | many-locks-one-transaction reports the compounded hold, not each statement in isolation |
ADD COLUMN with a volatile default, e.g. DEFAULT now() | Looks identical to the cheap, metadata-only constant-default case Postgres 11+ made routine, unless the reader happens to know which functions are VOLATILE | add-column-volatile-default checks the function against Postgres's own volatility classification |
Whether an ALTER TABLE is protected by a lock_timeout, or whether a
NOT VALID constraint's later VALIDATE actually buys a cheap step, is not
visible on the risky line itself — it depends on what else the file does, sometimes many lines
away. A reviewer has to hold the whole file's SET and transaction structure in mind
while reading every later statement, which is the part that degrades fastest on a long or
generated migration. This tool checks every statement against the full file's lock_timeout and
transaction context the same way on every run, regardless of file length.
This is a textual scan over SQL, not a query planner — it has no connection to your database, so it cannot see your table's actual row count, its column's current type, or whether an index it assumes exists actually does. It reports what a statement can do under Postgres's own locking rules, not a guarantee that it will hurt your specific table. Deciding whether a flagged statement is safe for your data, and catching SQL this tool's patterns don't recognise, is still a job for reading the migration yourself.
A reviewer who knows the rules catches the well-known single-statement case reliably: CREATE INDEX with no CONCURRENTLY, or a bare TRUNCATE. Those are real risks and a careful read finds them the same way this tool's create-index-not-concurrent and truncate checks do.
Whether a given ALTER TABLE is protected depends on whether a SET lock_timeout statement appears earlier in the same file -- a relationship between two statements that can be dozens of lines apart, not a property visible on the risky line itself. A reviewer has to hold the whole file's SET statements in mind while reading every ALTER, which is easy to get right on a short migration and easy to miss on a long generated one.
Both statements are individually correct and idiomatic -- NOT VALID is the documented way to add a constraint without an immediate table scan. The trap is entirely about grouping: wrapped in the same BEGIN block as its own VALIDATE CONSTRAINT, the split buys nothing, because the transaction still holds the lock for the whole validation. Nothing about either line looks wrong on its own; the bug is which migration file boundary they happen to share.
Each statement, read on its own, is an acceptably brief lock. Five of them in one transaction is not five brief locks -- it is one lock held from the first statement until the last one finishes, because Postgres does not release an ACCESS EXCLUSIVE lock until the transaction commits. Adding up five separate impact estimates in your head while reading a migration top to bottom is exactly the kind of running total a person is bad at keeping accurate.
No. This is a textual scan over SQL, not a query planner -- it cannot see your table's actual row count, current type, or whether an index already exists, so it reports what a statement can do under Postgres's locking rules, not a guarantee that it will hurt in your specific case. Deciding whether a flagged statement is actually safe for your table, and reading SQL this tool's patterns don't recognise, is still a job for reading the migration yourself.