no-add-column-serial
SERIAL/BIGSERIAL creates an implicit sequence with ACCESS EXCLUSIVE lock.
- operation
- Columns
- lock taken
- ACCESS EXCLUSIVE
- remediation
- Manual rewrite
- category
- Types & schema style
- Rewrites the table
What triggers it
Fires on any AT_AddColumn command whose parsed typeName.names includes serial, bigserial, smallserial, or one of their serialN aliases (serial2/serial4/serial8) — checked against the type name list, not the raw SQL text.
What does not
Any ADD COLUMN using a plain type — integer, bigint, or an explicit GENERATED ALWAYS AS IDENTITY column — never matches, since none of those produce a SERIAL-family type name. AlterTableStmts with no commands, and non-AlterTableStmt statements, are skipped too. The check itself carries no PostgreSQL-version gate; only the recommended fix text changes with ctx.pgVersion.
Where it applies
Applies to every PostgreSQL version MigrationPilot targets. It works on the SQL text alone — no database connection needed.
The recommended fix differs by version: PG 10+ gets GENERATED ALWAYS AS IDENTITY as a drop-in replacement; earlier versions get a three-step manual sequence plus a nextval() default instead.
The lock, and what it blocks
SERIAL/BIGSERIAL implicitly creates a sequence and a DEFAULT tied to it; the risk the doc describes is the table rewrite that default can force, held under ACCESS EXCLUSIVE for however long the rewrite takes.
Why it matters
SERIAL is syntactic sugar that creates a sequence and sets a DEFAULT. On PG 10+, use GENERATED ALWAYS AS IDENTITY instead — it has better semantics and avoids implicit sequence ownership issues.
Unsafe, and safe
Flagged
ALTER TABLE users ADD COLUMN id SERIAL PRIMARY KEY;
Safe alternative
ALTER TABLE users ADD COLUMN id INTEGER GENERATED ALWAYS AS IDENTITY PRIMARY KEY;
What it assumes
The rule flags SERIAL identically on every PostgreSQL version and every table size, even though the rewrite risk the doc describes is tied to older server versions — it has no way to check the real target version or row count from the static AST alone.
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.
"Adding a column with a default rewrites the table" is folklore that was true until 2018 and is now wrong in a way that matters.
- verified against
- PostgreSQL 17.10
- last checked
- 2026-08-11
- confidence
- High
PostgreSQL manual
Public incidents and write-ups
What the CLI prints
⚠ [MP015] WARNING (line 1) ADD COLUMN "id" with SERIAL on "users" creates an implicit sequence and may cause table rewrite. Safe alternative: -- PG 10+: Use GENERATED ALWAYS AS IDENTITY instead of SERIAL: ALTER TABLE users ADD COLUMN id integer GENERATED ALWAYS AS IDENTITY; Why: SERIAL/BIGSERIAL creates an implicit sequence and DEFAULT, which on PG versions before 11 causes a full table rewrite. Use GENERATED ALWAYS AS IDENTITY or manually create the sequence to avoid the rewrite. Docs: https://migrationpilot.dev/rules/mp015
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 MP015 ALTER TABLE users ADD COLUMN id SERIAL PRIMARY KEY;
For the whole project, in .migrationpilotrc.yml — by name or by id:
rules:
MP015: false
# or keep it, and downgrade it
rules:
MP015:
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 MP015 in the playground