aboutsummaryrefslogtreecommitdiff
path: root/src/backend
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend')
-rw-r--r--src/backend/catalog/heap.c84
1 files changed, 82 insertions, 2 deletions
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 8dbc5c88db1..76169b64adc 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -3399,9 +3399,16 @@ List *
heap_truncate_find_FKs(List *relationIds)
{
List *result = NIL;
+ List *oids = list_copy(relationIds);
+ List *parent_cons;
+ ListCell *cell;
+ ScanKeyData key;
Relation fkeyRel;
SysScanDesc fkeyScan;
HeapTuple tuple;
+ bool restart;
+
+ oids = list_copy(relationIds);
/*
* Must scan pg_constraint. Right now, it is a seqscan because there is
@@ -3409,6 +3416,10 @@ heap_truncate_find_FKs(List *relationIds)
*/
fkeyRel = table_open(ConstraintRelationId, AccessShareLock);
+restart:
+ restart = false;
+ parent_cons = NIL;
+
fkeyScan = systable_beginscan(fkeyRel, InvalidOid, false,
NULL, 0, NULL);
@@ -3421,16 +3432,85 @@ heap_truncate_find_FKs(List *relationIds)
continue;
/* Not referencing one of our list of tables */
- if (!list_member_oid(relationIds, con->confrelid))
+ if (!list_member_oid(oids, con->confrelid))
continue;
- /* Add referencer unless already in input or result list */
+ /*
+ * If this constraint has a parent constraint which we have not seen
+ * yet, keep track of it for the second loop, below. Tracking parent
+ * constraints allows us to climb up to the top-level level constraint
+ * and look for all possible relations referencing the partitioned
+ * table.
+ */
+ if (OidIsValid(con->conparentid) &&
+ !list_member_oid(parent_cons, con->conparentid))
+ parent_cons = lappend_oid(parent_cons, con->conparentid);
+
+ /*
+ * Add referencer to result, unless already present in input or result
+ * list.
+ */
if (!list_member_oid(relationIds, con->conrelid))
result = insert_ordered_unique_oid(result, con->conrelid);
}
systable_endscan(fkeyScan);
+
+ /*
+ * Process each parent constraint we found to add the list of referenced
+ * relations by them to the oids list. If we do add any new such
+ * relations, redo the first loop above. Also, if we see that the parent
+ * constraint in turn has a parent, add that so that we process all
+ * relations in a single additional pass.
+ */
+ foreach(cell, parent_cons)
+ {
+ Oid parent = lfirst_oid(cell);
+
+ ScanKeyInit(&key,
+ Anum_pg_constraint_oid,
+ BTEqualStrategyNumber, F_OIDEQ,
+ ObjectIdGetDatum(parent));
+
+ fkeyScan = systable_beginscan(fkeyRel, ConstraintOidIndexId,
+ true, NULL, 1, &key);
+
+ tuple = systable_getnext(fkeyScan);
+ if (HeapTupleIsValid(tuple))
+ {
+ Form_pg_constraint con = (Form_pg_constraint) GETSTRUCT(tuple);
+
+ /*
+ * pg_constraint rows always appear for partitioned hierarchies
+ * this way: on the each side of the constraint, one row appears
+ * for each partition that points to the top-most table on the
+ * other side.
+ *
+ * Because of this arrangement, we can correctly catch all
+ * relevant relations by adding to 'parent_cons' all rows with
+ * valid conparentid, and to the 'oids' list all rows with a
+ * zero conparentid. If any oids are added to 'oids', redo the
+ * first loop above by setting 'restart'.
+ */
+ if (OidIsValid(con->conparentid))
+ parent_cons = list_append_unique_oid(parent_cons,
+ con->conparentid);
+ else if (!list_member_oid(oids, con->confrelid))
+ {
+ oids = lappend_oid(oids, con->confrelid);
+ restart = true;
+ }
+ }
+
+ systable_endscan(fkeyScan);
+ }
+
+ list_free(parent_cons);
+ if (restart)
+ goto restart;
+
table_close(fkeyRel, AccessShareLock);
+ list_free(oids);
return result;
}