diff options
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/catalog/heap.c | 84 |
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; } |