From d7e94aba8eea2fc5d18a670cfa5e068bdcbcdf26 Mon Sep 17 00:00:00 2001 From: Manu Date: Tue, 29 Sep 2026 13:01:10 -0300 Subject: [PATCH v1] Index pg_constraint.confrelid to avoid a seqscan in ATTACH PARTITION CloneFkReferenced() collects the foreign keys that reference a partitioned table by looking for pg_constraint rows whose confrelid is the table. pg_constraint had no index on confrelid, so this was a sequential scan of the whole catalog on every ATTACH PARTITION. That was cheap while pg_constraint stayed small. Since not-null constraints gained pg_constraint rows (commit 14e87ffa5c5), the catalog holds a row per not-null column, and the scan's cost now grows with the total number of constraints in the database. Attaching partitions to a table in a large schema became noticeably slow as a result. Add a btree index on pg_constraint.confrelid and scan through it in CloneFkReferenced(). Only foreign keys set confrelid, so the scan keys on confrelid alone and filters contype in the loop. With ~1M not-null constraints this drops the per-ATTACH time from ~25 ms to ~0.3 ms and keeps it flat as the catalog grows. Catalog indexes cannot be partial, so the index covers every row, including the confrelid = 0 majority; a confrelid <> 0 partial index would be far smaller but is not supported by the bootstrap and CatalogIndexInsert paths. Reported-by: Bernhard Wonisch Discussion: https://postgr.es/m/trinity-08d3329b-f6b2-4c11-91d7-2198e50bf4fc-1790681922354@trinity-msg-rest-gmx-gmx-live-58cc8f554d-c56lm --- src/backend/commands/tablecmds.c | 17 +++++++++++------ src/include/catalog/catversion.h | 2 +- src/include/catalog/pg_constraint.h | 1 + 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 0274d892f2e..c246fa475d5 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -11355,16 +11355,21 @@ CloneFkReferenced(Relation parentRel, Relation partitionRel) ScanKeyInit(&key[0], Anum_pg_constraint_confrelid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(RelationGetRelid(parentRel))); - ScanKeyInit(&key[1], - Anum_pg_constraint_contype, BTEqualStrategyNumber, - F_CHAREQ, CharGetDatum(CONSTRAINT_FOREIGN)); - /* This is a seqscan, as we don't have a usable index ... */ - scan = systable_beginscan(pg_constraint, InvalidOid, true, - NULL, 2, key); + /* + * Look this up through the index on confrelid rather than seqscanning all + * of pg_constraint. That scan grew expensive once not-null constraints + * started to have pg_constraint rows, making its cost scale with the total + * number of constraints in the database. Only foreign keys set confrelid, + * so filtering on contype in the loop is just belt-and-suspenders. + */ + scan = systable_beginscan(pg_constraint, ConstraintConfRelidIndexId, true, + NULL, 1, key); while ((tuple = systable_getnext(scan)) != NULL) { Form_pg_constraint constrForm = (Form_pg_constraint) GETSTRUCT(tuple); + if (constrForm->contype != CONSTRAINT_FOREIGN) + continue; clone = lappend_oid(clone, constrForm->oid); } systable_endscan(scan); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 6f3e526de96..d636e7a7f30 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -57,6 +57,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 202609152 +#define CATALOG_VERSION_NO 202609291 #endif diff --git a/src/include/catalog/pg_constraint.h b/src/include/catalog/pg_constraint.h index e8d27546ed9..47d57f29488 100644 --- a/src/include/catalog/pg_constraint.h +++ b/src/include/catalog/pg_constraint.h @@ -185,6 +185,7 @@ DECLARE_UNIQUE_INDEX(pg_constraint_conrelid_contypid_conname_index, 2665, Constr DECLARE_INDEX(pg_constraint_contypid_index, 2666, ConstraintTypidIndexId, pg_constraint, btree(contypid oid_ops)); DECLARE_UNIQUE_INDEX_PKEY(pg_constraint_oid_index, 2667, ConstraintOidIndexId, pg_constraint, btree(oid oid_ops)); DECLARE_INDEX(pg_constraint_conparentid_index, 2579, ConstraintParentIndexId, pg_constraint, btree(conparentid oid_ops)); +DECLARE_INDEX(pg_constraint_confrelid_index, 9370, ConstraintConfRelidIndexId, pg_constraint, btree(confrelid oid_ops)); MAKE_SYSCACHE(CONSTROID, pg_constraint_oid_index, 16); -- 2.55.0