warn-default-partition-growth
A DEFAULT partition silently absorbs rows that belong to missing partitions, making later ATTACH operations expensive.
- operation
- Partitions
- lock taken
- no table lock
- remediation
- Manual rewrite
- category
- Partitioning
- Scans every row
What triggers it
CREATE TABLE ... PARTITION OF ... DEFAULT — detected via partbound.is_default === true on the CreateStmt.
What does not
Non-CreateStmt statements. Bounded PARTITION OF ... FOR VALUES partitions and partitioned-parent declarations (PARTITION BY) don't set is_default, so they're left alone.
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
None for the statement this rule flags — creating an empty default partition is fast. The lock lands later: attaching the partition those absorbed rows should have gone to forces PostgreSQL to prove none of them overlap the new bound, under ACCESS EXCLUSIVE on both the default partition and the parent, and that ATTACH fails outright if any row does overlap.
Why it matters
A default partition converts a loud failure into a silent one. Without it, inserting a row that matches no partition raises an error somebody notices the same day; with it, the row lands in the catch-all and nothing is logged. Miss a month of partition creation and the default partition quietly becomes the largest table in the database — and getting out is expensive in proportion to how long it went unnoticed, because attaching the partition those rows belong to scans the whole default partition under ACCESS EXCLUSIVE.
Unsafe, and safe
Flagged
CREATE TABLE events_default PARTITION OF events DEFAULT; -- Silently absorbs every row with no home, forever
Safe alternative
-- Create partitions ahead of time so a gap fails loudly instead of silently CREATE TABLE events_2026_01 PARTITION OF events FOR VALUES FROM ('2026-01-01') TO ('2026-02-01'); CREATE TABLE events_2026_02 PARTITION OF events FOR VALUES FROM ('2026-02-01') TO ('2026-03-01');
What it assumes
Assumes creating a DEFAULT partition is risky regardless of how it's operated — a default partition that's actively monitored and kept near-empty, which the rule's own guidance treats as reasonable, is flagged the same as one nobody is watching, since the rule sees the DDL, not runtime row counts.
What the CLI prints
⚠ [MP093] WARNING (line 1)
"events_default" is a DEFAULT partition of "events". It will silently absorb every row that matches no other partition, and each later ATTACH PARTITION on "events" must scan it in full under ACCESS EXCLUSIVE.
Safe alternative:
-- Prefer creating partitions ahead of time so a missing one fails loudly:
CREATE TABLE events_2026_01 PARTITION OF events
FOR VALUES FROM ('2026-01-01') TO ('2026-02-01');
-- If you keep the default partition, monitor it so it stays near-empty:
SELECT count(*) FROM events_default;
-- and drain it before attaching the partition its rows belong to, so the
-- ATTACH scan has nothing to find.
Why: The default partition turns a missing partition from a loud failure into a silent one. Without it, an insert with no matching partition raises an error somebody notices the same day; with it, the row lands in the catch-all and the gap goes unnoticed until the default partition is the largest table in the database. Getting out is the expensive part: attaching the partition those rows belonged to requires a full scan of the default partition under ACCESS EXCLUSIVE to prove no row overlaps the new bound, and if one does, the ATTACH fails and the rows have to be moved out by hand first.
Docs: https://migrationpilot.dev/rules/mp093Generated 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 MP093 CREATE TABLE events_default PARTITION OF events DEFAULT;
For the whole project, in .migrationpilotrc.yml — by name or by id:
rules:
MP093: false
# or keep it, and downgrade it
rules:
MP093:
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 MP093 in the playground