From b7cc21c57d738b41f6116f46d566b9949a433747 Mon Sep 17 00:00:00 2001 From: Alvaro Herrera Date: Fri, 14 Aug 2020 17:33:31 -0400 Subject: [PATCH] pg_dump: fix dependencies on FKs to partitioned tables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parallel-restoring a foreign key that references a partitioned table with several levels of partitions can fail: pg_restore: while PROCESSING TOC: pg_restore: from TOC entry 6684; 2606 29166 FK CONSTRAINT fk fk_a_fkey postgres pg_restore: error: could not execute query: ERROR: there is no unique constraint matching given keys for referenced table "pk" Command was: ALTER TABLE fkpart3.fk ADD CONSTRAINT fk_a_fkey FOREIGN KEY (a) REFERENCES fkpart3.pk(a); This happens in parallel restore mode because some index partitions aren't yet attached to the topmost partitioned index that the FK uses, and so the index is still invalid. The current code marks the FK as dependent on the first level of index-attach dump objects; the bug is fixed by recursively marking the FK on their children. Backpatch to 12, where FKs to partitioned tables were introduced. Reported-by: Tom Lane Author: Álvaro Herrera Discussion: https://postgr.es/m/3170626.1594842723@sss.pgh.pa.us Backpatch: 12-master --- src/bin/pg_dump/pg_dump.c | 41 ++++++++++++++++++++++++++++++++------- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 7fb775e6463..dda48675497 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -235,6 +235,7 @@ static DumpableObject *createBoundaryObjects(void); static void addBoundaryDependencies(DumpableObject **dobjs, int numObjs, DumpableObject *boundaryObjs); +static void addConstrChildIdxDeps(DumpableObject *dobj, IndxInfo *refidx); static void getDomainConstraints(Archive *fout, TypeInfo *tyinfo); static void getTableData(DumpOptions *dopt, TableInfo *tblinfo, int numTables, char relkind); static void makeTableDataInfo(DumpOptions *dopt, TableInfo *tbinfo); @@ -7514,25 +7515,20 @@ getConstraints(Archive *fout, TableInfo tblinfo[], int numTables) reftable = findTableByOid(constrinfo[j].confrelid); if (reftable && reftable->relkind == RELKIND_PARTITIONED_TABLE) { - IndxInfo *refidx; Oid indexOid = atooid(PQgetvalue(res, j, i_conindid)); if (indexOid != InvalidOid) { for (int k = 0; k < reftable->numIndexes; k++) { - SimplePtrListCell *cell; + IndxInfo *refidx; /* not our index? */ if (reftable->indexes[k].dobj.catId.oid != indexOid) continue; refidx = &reftable->indexes[k]; - for (cell = refidx->partattaches.head; cell; - cell = cell->next) - addObjectDependency(&constrinfo[j].dobj, - ((DumpableObject *) - cell->ptr)->dumpId); + addConstrChildIdxDeps(&constrinfo[j].dobj, refidx); break; } } @@ -7545,6 +7541,37 @@ getConstraints(Archive *fout, TableInfo tblinfo[], int numTables) destroyPQExpBuffer(query); } +/* + * addConstrChildIdxDeps + * + * Recursive subroutine for getConstraints + * + * Given an object representing a foreign key constraint and an index on the + * partitioned table it references, mark the constraint object as dependent + * on each partition, recursing to children until all leaves are found. + * This ensures that the FK is not restored until the index is fully marked + * valid. + */ +static void +addConstrChildIdxDeps(DumpableObject *dobj, IndxInfo *refidx) +{ + SimplePtrListCell *cell; + + Assert(dobj->objType == DO_FK_CONSTRAINT); + + for (cell = refidx->partattaches.head; cell; cell = cell->next) + { + DumpableObject *childobj = (DumpableObject *) cell->ptr; + IndexAttachInfo *attach; + + addObjectDependency(dobj, childobj->dumpId); + + attach = (IndexAttachInfo *) childobj; + if (attach->partitionIdx->partattaches.head != NULL) + addConstrChildIdxDeps(dobj, attach->partitionIdx); + } +} + /* * getDomainConstraints *