Skip to content

Commit 43d81f1

Browse files
committed
Fix pg_dump handling of extension config tables
Since 9.1, we've provided extensions with a way to denote "configuration" tables- tables created by an extension which the user may modify. By marking these as "configuration" tables, the extension is asking for the data in these tables to be pg_dump'd (tables which are not marked in this way are assumed to be entirely handled during CREATE EXTENSION and are not included at all in a pg_dump). Unfortunately, pg_dump neglected to consider foreign key relationships between extension configuration tables and therefore could end up trying to reload the data in an order which would cause FK violations. This patch teaches pg_dump about these dependencies, so that the data dumped out is done so in the best order possible. Note that there's no way to handle circular dependencies, but those have yet to be seen in the wild. The release notes for this should include a caution to users that existing pg_dump-based backups may be invalid due to this issue. The data is all there, but restoring from it will require extracting the data for the configuration tables and then loading them in the correct order by hand. Discussed initially back in bug #6738, more recently brought up by Gilles Darold, who provided an initial patch which was further reworked by Michael Paquier. Further modifications and documentation updates by me. Back-patch to 9.1 where we added the concept of extension configuration tables.
1 parent 585f16d commit 43d81f1

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

doc/src/sgml/extend.sgml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -721,6 +721,17 @@ SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entr
721721
a table as no longer a configuration table is to dissociate it from the
722722
extension with <command>ALTER EXTENSION ... DROP TABLE</>.
723723
</para>
724+
725+
<para>
726+
Note that foreign key relationships between these tables will dictate the
727+
order in which the tables are dumped out by pg_dump. Specifically, pg_dump
728+
will attempt to dump the referenced-by table before the referencing table.
729+
As the foreign key relationships are set up at CREATE EXTENSION time (prior
730+
to data being loaded into the tables) circular dependencies are not
731+
supported. When circular dependencies exist, the data will still be dumped
732+
out but the dump will not be able to be restored directly and user
733+
intervention will be required.
734+
</para>
724735
</sect2>
725736

726737
<sect2>

src/bin/pg_dump/pg_dump.c

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14571,6 +14571,33 @@ dumpRule(Archive *fout, RuleInfo *rinfo)
1457114571

1457214572
/*
1457314573
* getExtensionMembership --- obtain extension membership data
14574+
*
14575+
* There are three main parts to this process:
14576+
*
14577+
* 1. Identify objects which are members of extensions
14578+
*
14579+
* Generally speaking, this is to mark them as *not* being dumped, as most
14580+
* extension objects are created by the single CREATE EXTENSION command.
14581+
* The one exception is binary upgrades with pg_upgrade will still dump the
14582+
* non-table objects.
14583+
*
14584+
* 2. Identify and create dump records for extension configuration tables.
14585+
*
14586+
* Extensions can mark tables as "configuration", which means that the user
14587+
* is able and expected to modify those tables after the extension has been
14588+
* loaded. For these tables, we dump out only the data- the structure is
14589+
* expected to be handled at CREATE EXTENSION time, including any indexes or
14590+
* foriegn keys, which brings us to-
14591+
*
14592+
* 3. Record FK dependencies between configuration tables.
14593+
*
14594+
* Due to the FKs being created at CREATE EXTENSION time and therefore before
14595+
* the data is loaded, we have to work out what the best order for reloading
14596+
* the data is, to avoid FK violations when the tables are restored. This is
14597+
* not perfect- we can't handle circular dependencies and if any exist they
14598+
* will cause an invalid dump to be produced (though at least all of the data
14599+
* is included for a user to manually restore). This is currently documented
14600+
* but perhaps we can provide a better solution in the future.
1457414601
*/
1457514602
void
1457614603
getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
@@ -14583,7 +14610,9 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
1458314610
int i_classid,
1458414611
i_objid,
1458514612
i_refclassid,
14586-
i_refobjid;
14613+
i_refobjid,
14614+
i_conrelid,
14615+
i_confrelid;
1458714616
DumpableObject *dobj,
1458814617
*refdobj;
1458914618

@@ -14764,6 +14793,53 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
1476414793
free(extconditionarray);
1476514794
}
1476614795

14796+
/*
14797+
* Now that all the TableInfoData objects have been created for all
14798+
* the extensions, check their FK dependencies and register them to
14799+
* try and dump the data out in an order which they can be restored
14800+
* in.
14801+
*
14802+
* Note that this is not a problem for user tables as their FKs are
14803+
* recreated after the data has been loaded.
14804+
*/
14805+
printfPQExpBuffer(query,
14806+
"SELECT conrelid, confrelid "
14807+
"FROM pg_constraint "
14808+
"JOIN pg_depend ON (objid = confrelid) "
14809+
"WHERE contype = 'f' "
14810+
"AND refclassid = 'pg_extension'::regclass "
14811+
"AND classid = 'pg_class'::regclass;");
14812+
14813+
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
14814+
ntups = PQntuples(res);
14815+
14816+
i_conrelid = PQfnumber(res, "conrelid");
14817+
i_confrelid = PQfnumber(res, "confrelid");
14818+
14819+
/* Now get the dependencies and register them */
14820+
for (i = 0; i < ntups; i++)
14821+
{
14822+
Oid conrelid, confrelid;
14823+
TableInfo *reftable, *contable;
14824+
14825+
conrelid = atooid(PQgetvalue(res, i, i_conrelid));
14826+
confrelid = atooid(PQgetvalue(res, i, i_confrelid));
14827+
contable = findTableByOid(conrelid);
14828+
reftable = findTableByOid(confrelid);
14829+
14830+
if (reftable == NULL ||
14831+
reftable->dataObj == NULL ||
14832+
contable == NULL ||
14833+
contable->dataObj == NULL)
14834+
continue;
14835+
14836+
/*
14837+
* Make referencing TABLE_DATA object depend on the
14838+
* referenced table's TABLE_DATA object.
14839+
*/
14840+
addObjectDependency(&contable->dataObj->dobj,
14841+
reftable->dataObj->dobj.dumpId);
14842+
}
1476714843
destroyPQExpBuffer(query);
1476814844
}
1476914845

0 commit comments

Comments
 (0)