Skip to content

Commit d6b8d29

Browse files
committed
Allow a partdesc-omitting-partitions to be cached
Makes partition descriptor acquisition faster during the transient period in which a partition is in the process of being detached. This also adds the restriction that only one partition can be in pending-detach state for a partitioned table. While at it, return find_inheritance_children() API to what it was before 71f4c8c, and create a separate find_inheritance_children_extended() that returns detailed info about detached partitions. (This incidentally fixes a bug in 8aba932 whereby a memory context holding a transient partdesc is reparented to a NULL PortalContext, leading to permanent leak of that memory. The fix is to no longer rely on reparenting contexts to PortalContext. Reported by Amit Langote.) Per gripe from Amit Langote Discussion: https://postgr.es/m/CA+HiwqFgpP1LxJZOBYGt9rpvTjXXkg5qG2+Xch2Z1Q7KrqZR1A@mail.gmail.com
1 parent c93f8f3 commit d6b8d29

File tree

10 files changed

+329
-73
lines changed

10 files changed

+329
-73
lines changed

doc/src/sgml/ref/alter_table.sgml

+2
Original file line numberDiff line numberDiff line change
@@ -986,6 +986,8 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
986986
If <literal>FINALIZE</literal> is specified, a previous
987987
<literal>DETACH CONCURRENTLY</literal> invocation that was cancelled or
988988
interrupted is completed.
989+
At most one partition in a partitioned table can be pending detach at
990+
a time.
989991
</para>
990992
</listitem>
991993
</varlistentry>

src/backend/catalog/pg_inherits.c

+47-5
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@ typedef struct SeenRelsEntry
5252
* then no locks are acquired, but caller must beware of race conditions
5353
* against possible DROPs of child relations.
5454
*
55+
* Partitions marked as being detached are omitted; see
56+
* find_inheritance_children_extended for details.
57+
*/
58+
List *
59+
find_inheritance_children(Oid parentrelId, LOCKMODE lockmode)
60+
{
61+
return find_inheritance_children_extended(parentrelId, true, lockmode,
62+
NULL, NULL);
63+
}
64+
65+
/*
66+
* find_inheritance_children_extended
67+
*
68+
* As find_inheritance_children, with more options regarding detached
69+
* partitions.
70+
*
5571
* If a partition's pg_inherits row is marked "detach pending",
5672
* *detached_exist (if not null) is set true.
5773
*
@@ -60,11 +76,13 @@ typedef struct SeenRelsEntry
6076
* marked "detach pending" is visible to that snapshot, then that partition is
6177
* omitted from the output list. This makes partitions invisible depending on
6278
* whether the transaction that marked those partitions as detached appears
63-
* committed to the active snapshot.
79+
* committed to the active snapshot. In addition, *detached_xmin (if not null)
80+
* is set to the xmin of the row of the detached partition.
6481
*/
6582
List *
66-
find_inheritance_children(Oid parentrelId, bool omit_detached,
67-
LOCKMODE lockmode, bool *detached_exist)
83+
find_inheritance_children_extended(Oid parentrelId, bool omit_detached,
84+
LOCKMODE lockmode, bool *detached_exist,
85+
TransactionId *detached_xmin)
6886
{
6987
List *list = NIL;
7088
Relation relation;
@@ -132,7 +150,32 @@ find_inheritance_children(Oid parentrelId, bool omit_detached,
132150
snap = GetActiveSnapshot();
133151

134152
if (!XidInMVCCSnapshot(xmin, snap))
153+
{
154+
if (detached_xmin)
155+
{
156+
/*
157+
* Two detached partitions should not occur (see
158+
* checks in MarkInheritDetached), but if they do,
159+
* track the newer of the two. Make sure to warn the
160+
* user, so that they can clean up. Since this is
161+
* just a cross-check against potentially corrupt
162+
* catalogs, we don't make it a full-fledged error
163+
* message.
164+
*/
165+
if (*detached_xmin != InvalidTransactionId)
166+
{
167+
elog(WARNING, "more than one partition pending detach found for table with OID %u",
168+
parentrelId);
169+
if (TransactionIdFollows(xmin, *detached_xmin))
170+
*detached_xmin = xmin;
171+
}
172+
else
173+
*detached_xmin = xmin;
174+
}
175+
176+
/* Don't add the partition to the output list */
135177
continue;
178+
}
136179
}
137180
}
138181

@@ -247,8 +290,7 @@ find_all_inheritors(Oid parentrelId, LOCKMODE lockmode, List **numparents)
247290
ListCell *lc;
248291

249292
/* Get the direct children of this rel */
250-
currentchildren = find_inheritance_children(currentrel, true,
251-
lockmode, NULL);
293+
currentchildren = find_inheritance_children(currentrel, lockmode);
252294

253295
/*
254296
* Add to the queue only those children not already seen. This avoids

src/backend/commands/tablecmds.c

+39-27
Original file line numberDiff line numberDiff line change
@@ -3492,7 +3492,7 @@ renameatt_internal(Oid myrelid,
34923492
* expected_parents will only be 0 if we are not already recursing.
34933493
*/
34943494
if (expected_parents == 0 &&
3495-
find_inheritance_children(myrelid, true, NoLock, NULL) != NIL)
3495+
find_inheritance_children(myrelid, NoLock) != NIL)
34963496
ereport(ERROR,
34973497
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
34983498
errmsg("inherited column \"%s\" must be renamed in child tables too",
@@ -3691,7 +3691,7 @@ rename_constraint_internal(Oid myrelid,
36913691
else
36923692
{
36933693
if (expected_parents == 0 &&
3694-
find_inheritance_children(myrelid, true, NoLock, NULL) != NIL)
3694+
find_inheritance_children(myrelid, NoLock) != NIL)
36953695
ereport(ERROR,
36963696
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
36973697
errmsg("inherited constraint \"%s\" must be renamed in child tables too",
@@ -6565,7 +6565,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
65656565
*/
65666566
if (colDef->identity &&
65676567
recurse &&
6568-
find_inheritance_children(myrelid, true, NoLock, NULL) != NIL)
6568+
find_inheritance_children(myrelid, NoLock) != NIL)
65696569
ereport(ERROR,
65706570
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
65716571
errmsg("cannot recursively add identity column to table that has child tables")));
@@ -6811,7 +6811,7 @@ ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
68116811
* use find_all_inheritors to do it in one pass.
68126812
*/
68136813
children =
6814-
find_inheritance_children(RelationGetRelid(rel), true, lockmode, NULL);
6814+
find_inheritance_children(RelationGetRelid(rel), lockmode);
68156815

68166816
/*
68176817
* If we are told not to recurse, there had better not be any child
@@ -7674,7 +7674,7 @@ ATPrepDropExpression(Relation rel, AlterTableCmd *cmd, bool recurse, bool recurs
76747674
* resulting state can be properly dumped and restored.
76757675
*/
76767676
if (!recurse &&
7677-
find_inheritance_children(RelationGetRelid(rel), true, lockmode, NULL))
7677+
find_inheritance_children(RelationGetRelid(rel), lockmode))
76787678
ereport(ERROR,
76797679
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
76807680
errmsg("ALTER TABLE / DROP EXPRESSION must be applied to child tables too")));
@@ -8282,7 +8282,7 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
82828282
* use find_all_inheritors to do it in one pass.
82838283
*/
82848284
children =
8285-
find_inheritance_children(RelationGetRelid(rel), true, lockmode, NULL);
8285+
find_inheritance_children(RelationGetRelid(rel), lockmode);
82868286

82878287
if (children)
82888288
{
@@ -8770,7 +8770,7 @@ ATAddCheckConstraint(List **wqueue, AlteredTableInfo *tab, Relation rel,
87708770
* use find_all_inheritors to do it in one pass.
87718771
*/
87728772
children =
8773-
find_inheritance_children(RelationGetRelid(rel), true, lockmode, NULL);
8773+
find_inheritance_children(RelationGetRelid(rel), lockmode);
87748774

87758775
/*
87768776
* Check if ONLY was specified with ALTER TABLE. If so, allow the
@@ -11303,8 +11303,7 @@ ATExecDropConstraint(Relation rel, const char *constrName,
1130311303
* use find_all_inheritors to do it in one pass.
1130411304
*/
1130511305
if (!is_no_inherit_constraint)
11306-
children = find_inheritance_children(RelationGetRelid(rel), true,
11307-
lockmode, NULL);
11306+
children = find_inheritance_children(RelationGetRelid(rel), lockmode);
1130811307
else
1130911308
children = NIL;
1131011309

@@ -11688,8 +11687,7 @@ ATPrepAlterColumnType(List **wqueue,
1168811687
}
1168911688
}
1169011689
else if (!recursing &&
11691-
find_inheritance_children(RelationGetRelid(rel), true,
11692-
NoLock, NULL) != NIL)
11690+
find_inheritance_children(RelationGetRelid(rel), NoLock) != NIL)
1169311691
ereport(ERROR,
1169411692
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
1169511693
errmsg("type of inherited column \"%s\" must be changed in child tables too",
@@ -14601,7 +14599,8 @@ ATExecDropInherit(Relation rel, RangeVar *parent, LOCKMODE lockmode)
1460114599
* MarkInheritDetached
1460214600
*
1460314601
* Set inhdetachpending for a partition, for ATExecDetachPartition
14604-
* in concurrent mode.
14602+
* in concurrent mode. While at it, verify that no other partition is
14603+
* already pending detach.
1460514604
*/
1460614605
static void
1460714606
MarkInheritDetached(Relation child_rel, Relation parent_rel)
@@ -14617,32 +14616,45 @@ MarkInheritDetached(Relation child_rel, Relation parent_rel)
1461714616
Assert(parent_rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE);
1461814617

1461914618
/*
14620-
* Find pg_inherits entries by inhrelid.
14619+
* Find pg_inherits entries by inhparent. (We need to scan them all in
14620+
* order to verify that no other partition is pending detach.)
1462114621
*/
1462214622
catalogRelation = table_open(InheritsRelationId, RowExclusiveLock);
1462314623
ScanKeyInit(&key,
14624-
Anum_pg_inherits_inhrelid,
14624+
Anum_pg_inherits_inhparent,
1462514625
BTEqualStrategyNumber, F_OIDEQ,
14626-
ObjectIdGetDatum(RelationGetRelid(child_rel)));
14627-
scan = systable_beginscan(catalogRelation, InheritsRelidSeqnoIndexId,
14626+
ObjectIdGetDatum(RelationGetRelid(parent_rel)));
14627+
scan = systable_beginscan(catalogRelation, InheritsParentIndexId,
1462814628
true, NULL, 1, &key);
1462914629

1463014630
while (HeapTupleIsValid(inheritsTuple = systable_getnext(scan)))
1463114631
{
14632-
HeapTuple newtup;
14632+
Form_pg_inherits inhForm;
1463314633

14634-
if (((Form_pg_inherits) GETSTRUCT(inheritsTuple))->inhparent !=
14635-
RelationGetRelid(parent_rel))
14636-
elog(ERROR, "bad parent tuple found for partition %u",
14637-
RelationGetRelid(child_rel));
14634+
inhForm = (Form_pg_inherits) GETSTRUCT(inheritsTuple);
14635+
if (inhForm->inhdetachpending)
14636+
ereport(ERROR,
14637+
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
14638+
errmsg("partition \"%s\" already pending detach in partitioned table \"%s.%s\"",
14639+
get_rel_name(inhForm->inhrelid),
14640+
get_namespace_name(parent_rel->rd_rel->relnamespace),
14641+
RelationGetRelationName(parent_rel)),
14642+
errhint("Use ALTER TABLE ... DETACH PARTITION ... FINALIZE to complete the detach operation."));
14643+
14644+
if (inhForm->inhrelid == RelationGetRelid(child_rel))
14645+
{
14646+
HeapTuple newtup;
1463814647

14639-
newtup = heap_copytuple(inheritsTuple);
14640-
((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
14648+
newtup = heap_copytuple(inheritsTuple);
14649+
((Form_pg_inherits) GETSTRUCT(newtup))->inhdetachpending = true;
1464114650

14642-
CatalogTupleUpdate(catalogRelation,
14643-
&inheritsTuple->t_self,
14644-
newtup);
14645-
found = true;
14651+
CatalogTupleUpdate(catalogRelation,
14652+
&inheritsTuple->t_self,
14653+
newtup);
14654+
found = true;
14655+
heap_freetuple(newtup);
14656+
/* keep looking, to ensure we catch others pending detach */
14657+
}
1464614658
}
1464714659

1464814660
/* Done */

src/backend/commands/trigger.c

+1-2
Original file line numberDiff line numberDiff line change
@@ -1141,8 +1141,7 @@ CreateTrigger(CreateTrigStmt *stmt, const char *queryString,
11411141
ListCell *l;
11421142
List *idxs = NIL;
11431143

1144-
idxs = find_inheritance_children(indexOid, true,
1145-
ShareRowExclusiveLock, NULL);
1144+
idxs = find_inheritance_children(indexOid, ShareRowExclusiveLock);
11461145
foreach(l, idxs)
11471146
childTbls = lappend_oid(childTbls,
11481147
IndexGetRelation(lfirst_oid(l),

0 commit comments

Comments
 (0)