MP015warning

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

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

What the CLI prints

migrationpilot analyze migration.sql
⚠ [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:

.migrationpilotrc.yml
rules:
  MP015: false

# or keep it, and downgrade it
rules:
  MP015:
    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 MP015 in the playground

Related rules