warn-partman-managed-parent
Manual partition DDL on a parent table managed by pg_partman.
- operation
- Partitions
- lock taken
- no table lock
- remediation
- Manual rewrite
- category
- Extensions
- Can lose data
What triggers it
Either a CreateStmt that has a partbound and names a parent in inhRelations[0].RangeVar.relname, or an AlterTableStmt carrying an AT_AttachPartition or AT_DetachPartition subcommand whose parent is relation.relname — where that parent resolves through lookupTableExtensions() to a TableExtensionInfo with isPartmanParent true, meaning it appears in pg_partman's part_config.
What does not
Every run without --database-url: part_config is the only thing that says a parent is managed, so without it a CREATE TABLE ... PARTITION OF reads as ordinary partition DDL (requiresDatabaseUrl is set). A CreateStmt with no partbound, or one with no inhRelations entry to name the parent. Parents absent from part_config. Every other statement shape, including a DROP TABLE on a child partman believes it owns.
Where it applies
Applies to every PostgreSQL version MigrationPilot targets. It only fires on tables managed by pg_partman. It needs --database-url: without a connection it has nothing to read and stays silent.
The lock, and what it blocks
No lock claim. ATTACH PARTITION takes SHARE UPDATE EXCLUSIVE on the parent and ACCESS EXCLUSIVE on the table being attached, and a non-concurrent DETACH takes ACCESS EXCLUSIVE on the parent — but none of that is what the rule reports. The finding is a bookkeeping divergence: part_config will not record the partition, and the collision surfaces later on a run_maintenance run rather than during the migration.
Why it matters
pg_partman decides which children exist, from what part_config says: run_maintenance pre-creates the next few partitions according to premake and drops old ones according to retention, on its own schedule. A partition created, attached, or detached by hand is not recorded there, so the next maintenance run can try to create a partition whose range you have already covered — which fails — or drop one it believes it owns. Nothing goes wrong at migration time; it surfaces later on a scheduled run nobody is watching.
Unsafe, and safe
Flagged
-- events is managed by pg_partman (interval 1 day, premake 4, retention 90 days) CREATE TABLE events_p2024_01 PARTITION OF events FOR VALUES FROM ('2024-01-01') TO ('2024-02-01');
Safe alternative
-- Check what partman thinks it owns, then let it make the partitions -- by widening how far ahead it pre-creates. SELECT parent_table, control, partition_interval, premake, retention FROM part_config WHERE parent_table LIKE '%events';
What it assumes
The rule cannot compare your partition's range against what partman is about to pre-create, so it cannot distinguish a genuine collision from a manual partition sitting harmlessly outside the premake window — every hand-written partition on a managed parent is reported. Parents are matched by bare relation name against what was read from part_config, so the same parent name in two schemas is ambiguous. The configuration quoted in the message (control column, interval, premake, retention) is each optional and is used for wording only, never for a decision, so a partly-readable part_config still produces the same finding.
This rule reads live catalogue state, so it says nothing at all without --database-url. That is the trade: no connection, no guess.
What the CLI prints
⚠ [MP108] WARNING (line 2)
CREATE TABLE events_p2024_01 PARTITION OF on "events", which pg_partman manages (control column "created_at", interval 1 day, premake 4, retention 90 days). partman creates and removes children from part_config on its own schedule; a partition changed by hand is not recorded there, so the next run_maintenance can collide with it. Its retention policy is 90 days, so partman will drop children on that schedule whether or not this migration made them.
Safe alternative:
-- Check what partman thinks it owns before changing anything:
SELECT parent_table, control, partition_interval, premake, retention, automatic_maintenance
FROM part_config WHERE parent_table LIKE '%events';
-- Let partman make the partitions, by widening how far ahead it pre-creates:
UPDATE part_config SET premake = 8 WHERE parent_table = 'public.events';
CALL run_maintenance_proc();
-- To move existing rows into the partition set, use partman's own procedure so
-- it commits in batches instead of one long transaction:
CALL partition_data_proc('public.events');
-- If the partition set really should be managed by hand from now on, take it out
-- of partman first: DELETE FROM part_config WHERE parent_table = '...';
Why: pg_partman decides which children exist from part_config: run_maintenance pre-creates the next few partitions and applies the retention policy. A partition created, attached, or detached by hand is invisible to that bookkeeping, so the next maintenance run can try to create a partition whose range you already covered, which fails, or drop one it believes it owns. The two systems disagree quietly and the failure surfaces later, on a maintenance run nobody was watching.
Docs: https://migrationpilot.dev/rules/mp108Generated 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. The catalogue figures come from the production context this rule documents.
Turning it off
For one statement, put a comment on the line before it:
-- migrationpilot-disable MP108 CREATE TABLE events_p2024_01 PARTITION OF events
For the whole project, in .migrationpilotrc.yml — by name or by id:
rules:
MP108: false
# or keep it, and downgrade it
rules:
MP108:
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 MP108 in the playground