diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index 60fa1a8922cfad4ed6b826a5acf27479ded1eef1..b980be45c188a39f0e3b7343ed0417edcd03bd58 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -721,6 +721,17 @@ SELECT pg_catalog.pg_extension_config_dump('my_config', 'WHERE NOT standard_entr a table as no longer a configuration table is to dissociate it from the extension with <command>ALTER EXTENSION ... DROP TABLE</>. </para> + + <para> + Note that foreign key relationships between these tables will dictate the + order in which the tables are dumped out by pg_dump. Specifically, pg_dump + will attempt to dump the referenced-by table before the referencing table. + As the foreign key relationships are set up at CREATE EXTENSION time (prior + to data being loaded into the tables) circular dependencies are not + supported. When circular dependencies exist, the data will still be dumped + out but the dump will not be able to be restored directly and user + intervention will be required. + </para> </sect2> <sect2> diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 782599c77b5c6fa6277d5990fb44e7eb2021841e..e10c30a8fe1c15cf327c170bb109a718d708d1ad 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -13886,6 +13886,33 @@ dumpRule(Archive *fout, RuleInfo *rinfo) /* * getExtensionMembership --- obtain extension membership data + * + * There are three main parts to this process: + * + * 1. Identify objects which are members of extensions + * + * Generally speaking, this is to mark them as *not* being dumped, as most + * extension objects are created by the single CREATE EXTENSION command. + * The one exception is binary upgrades with pg_upgrade will still dump the + * non-table objects. + * + * 2. Identify and create dump records for extension configuration tables. + * + * Extensions can mark tables as "configuration", which means that the user + * is able and expected to modify those tables after the extension has been + * loaded. For these tables, we dump out only the data- the structure is + * expected to be handled at CREATE EXTENSION time, including any indexes or + * foriegn keys, which brings us to- + * + * 3. Record FK dependencies between configuration tables. + * + * Due to the FKs being created at CREATE EXTENSION time and therefore before + * the data is loaded, we have to work out what the best order for reloading + * the data is, to avoid FK violations when the tables are restored. This is + * not perfect- we can't handle circular dependencies and if any exist they + * will cause an invalid dump to be produced (though at least all of the data + * is included for a user to manually restore). This is currently documented + * but perhaps we can provide a better solution in the future. */ void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], @@ -13898,7 +13925,9 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int i_classid, i_objid, i_refclassid, - i_refobjid; + i_refobjid, + i_conrelid, + i_confrelid; DumpableObject *dobj, *refdobj; @@ -14079,6 +14108,53 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], free(extconditionarray); } + /* + * Now that all the TableInfoData objects have been created for all + * the extensions, check their FK dependencies and register them to + * try and dump the data out in an order which they can be restored + * in. + * + * Note that this is not a problem for user tables as their FKs are + * recreated after the data has been loaded. + */ + printfPQExpBuffer(query, + "SELECT conrelid, confrelid " + "FROM pg_constraint " + "JOIN pg_depend ON (objid = confrelid) " + "WHERE contype = 'f' " + "AND refclassid = 'pg_extension'::regclass " + "AND classid = 'pg_class'::regclass;"); + + res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK); + ntups = PQntuples(res); + + i_conrelid = PQfnumber(res, "conrelid"); + i_confrelid = PQfnumber(res, "confrelid"); + + /* Now get the dependencies and register them */ + for (i = 0; i < ntups; i++) + { + Oid conrelid, confrelid; + TableInfo *reftable, *contable; + + conrelid = atooid(PQgetvalue(res, i, i_conrelid)); + confrelid = atooid(PQgetvalue(res, i, i_confrelid)); + contable = findTableByOid(conrelid); + reftable = findTableByOid(confrelid); + + if (reftable == NULL || + reftable->dataObj == NULL || + contable == NULL || + contable->dataObj == NULL) + continue; + + /* + * Make referencing TABLE_DATA object depend on the + * referenced table's TABLE_DATA object. + */ + addObjectDependency(&contable->dataObj->dobj, + reftable->dataObj->dobj.dumpId); + } destroyPQExpBuffer(query); }