Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit c05fa34

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 99ccda3 commit c05fa34

File tree

2 files changed

+88
-1
lines changed

2 files changed

+88
-1
lines changed

doc/src/sgml/extend.sgml

+11
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

+77-1
Original file line numberDiff line numberDiff line change
@@ -14908,6 +14908,33 @@ dumpRule(Archive *fout, RuleInfo *rinfo)
1490814908

1490914909
/*
1491014910
* getExtensionMembership --- obtain extension membership data
14911+
*
14912+
* There are three main parts to this process:
14913+
*
14914+
* 1. Identify objects which are members of extensions
14915+
*
14916+
* Generally speaking, this is to mark them as *not* being dumped, as most
14917+
* extension objects are created by the single CREATE EXTENSION command.
14918+
* The one exception is binary upgrades with pg_upgrade will still dump the
14919+
* non-table objects.
14920+
*
14921+
* 2. Identify and create dump records for extension configuration tables.
14922+
*
14923+
* Extensions can mark tables as "configuration", which means that the user
14924+
* is able and expected to modify those tables after the extension has been
14925+
* loaded. For these tables, we dump out only the data- the structure is
14926+
* expected to be handled at CREATE EXTENSION time, including any indexes or
14927+
* foriegn keys, which brings us to-
14928+
*
14929+
* 3. Record FK dependencies between configuration tables.
14930+
*
14931+
* Due to the FKs being created at CREATE EXTENSION time and therefore before
14932+
* the data is loaded, we have to work out what the best order for reloading
14933+
* the data is, to avoid FK violations when the tables are restored. This is
14934+
* not perfect- we can't handle circular dependencies and if any exist they
14935+
* will cause an invalid dump to be produced (though at least all of the data
14936+
* is included for a user to manually restore). This is currently documented
14937+
* but perhaps we can provide a better solution in the future.
1491114938
*/
1491214939
void
1491314940
getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
@@ -14920,7 +14947,9 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
1492014947
int i_classid,
1492114948
i_objid,
1492214949
i_refclassid,
14923-
i_refobjid;
14950+
i_refobjid,
14951+
i_conrelid,
14952+
i_confrelid;
1492414953
DumpableObject *dobj,
1492514954
*refdobj;
1492614955

@@ -15101,6 +15130,53 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
1510115130
free(extconditionarray);
1510215131
}
1510315132

15133+
/*
15134+
* Now that all the TableInfoData objects have been created for all
15135+
* the extensions, check their FK dependencies and register them to
15136+
* try and dump the data out in an order which they can be restored
15137+
* in.
15138+
*
15139+
* Note that this is not a problem for user tables as their FKs are
15140+
* recreated after the data has been loaded.
15141+
*/
15142+
printfPQExpBuffer(query,
15143+
"SELECT conrelid, confrelid "
15144+
"FROM pg_constraint "
15145+
"JOIN pg_depend ON (objid = confrelid) "
15146+
"WHERE contype = 'f' "
15147+
"AND refclassid = 'pg_extension'::regclass "
15148+
"AND classid = 'pg_class'::regclass;");
15149+
15150+
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
15151+
ntups = PQntuples(res);
15152+
15153+
i_conrelid = PQfnumber(res, "conrelid");
15154+
i_confrelid = PQfnumber(res, "confrelid");
15155+
15156+
/* Now get the dependencies and register them */
15157+
for (i = 0; i < ntups; i++)
15158+
{
15159+
Oid conrelid, confrelid;
15160+
TableInfo *reftable, *contable;
15161+
15162+
conrelid = atooid(PQgetvalue(res, i, i_conrelid));
15163+
confrelid = atooid(PQgetvalue(res, i, i_confrelid));
15164+
contable = findTableByOid(conrelid);
15165+
reftable = findTableByOid(confrelid);
15166+
15167+
if (reftable == NULL ||
15168+
reftable->dataObj == NULL ||
15169+
contable == NULL ||
15170+
contable->dataObj == NULL)
15171+
continue;
15172+
15173+
/*
15174+
* Make referencing TABLE_DATA object depend on the
15175+
* referenced table's TABLE_DATA object.
15176+
*/
15177+
addObjectDependency(&contable->dataObj->dobj,
15178+
reftable->dataObj->dobj.dumpId);
15179+
}
1510415180
destroyPQExpBuffer(query);
1510515181
}
1510615182

0 commit comments

Comments
 (0)