getSchemaData(int *numTablesPtr)
{
NamespaceInfo *nsinfo;
+ ExtensionInfo *extinfo;
AggInfo *agginfo;
InhInfo *inhinfo;
RuleInfo *ruleinfo;
ProcLangInfo *proclanginfo;
CastInfo *castinfo;
- ExtensionInfo *extinfo;
OpclassInfo *opcinfo;
OpfamilyInfo *opfinfo;
ConvInfo *convinfo;
ForeignServerInfo *srvinfo;
DefaultACLInfo *daclinfo;
int numNamespaces;
+ int numExtensions;
int numAggregates;
int numInherits;
int numRules;
int numProcLangs;
int numCasts;
- int numExtensions;
int numOpclasses;
int numOpfamilies;
int numConversions;
write_msg(NULL, "reading schemas\n");
nsinfo = getNamespaces(&numNamespaces);
+ if (g_verbose)
+ write_msg(NULL, "reading extensions\n");
+ extinfo = getExtensions(&numExtensions);
+
if (g_verbose)
write_msg(NULL, "reading user-defined functions\n");
funinfo = getFuncs(&numFuncs);
write_msg(NULL, "reading user-defined operator classes\n");
opcinfo = getOpclasses(&numOpclasses);
+ if (g_verbose)
+ write_msg(NULL, "reading user-defined operator families\n");
+ opfinfo = getOpfamilies(&numOpfamilies);
+
if (g_verbose)
write_msg(NULL, "reading user-defined text search parsers\n");
prsinfo = getTSParsers(&numTSParsers);
write_msg(NULL, "reading default privileges\n");
daclinfo = getDefaultACLs(&numDefaultACLs);
- if (g_verbose)
- write_msg(NULL, "reading user-defined operator families\n");
- opfinfo = getOpfamilies(&numOpfamilies);
-
if (g_verbose)
write_msg(NULL, "reading user-defined conversions\n");
convinfo = getConversions(&numConversions);
+ if (g_verbose)
+ write_msg(NULL, "reading type casts\n");
+ castinfo = getCasts(&numCasts);
+
if (g_verbose)
write_msg(NULL, "reading user-defined tables\n");
tblinfo = getTables(&numTables);
write_msg(NULL, "reading rewrite rules\n");
ruleinfo = getRules(&numRules);
+ /*
+ * Identify extension member objects and mark them as not to be dumped.
+ * This must happen after reading all objects that can be direct members
+ * of extensions, but before we begin to process table subsidiary objects.
+ */
if (g_verbose)
- write_msg(NULL, "reading type casts\n");
- castinfo = getCasts(&numCasts);
-
- /* this must be after getTables */
- if (g_verbose)
- write_msg(NULL, "reading extensions\n");
- extinfo = getExtensions(&numExtensions);
+ write_msg(NULL, "finding extension members\n");
+ getExtensionMembership(extinfo, numExtensions);
/* Link tables to parents, mark parents of target tables interesting */
if (g_verbose)
* Returns NULL for unknown ID
*
* We use binary search in a sorted list that is built on first call.
- * If AssignDumpId() and findObjectByCatalogId() calls were intermixed,
+ * If AssignDumpId() and findObjectByCatalogId() calls were freely intermixed,
* the code would work, but possibly be very slow. In the current usage
- * pattern that does not happen, indeed we only need to build the list once.
+ * pattern that does not happen, indeed we build the list at most twice.
*/
DumpableObject *
findObjectByCatalogId(CatalogId catalogId)
/*
* Collect dependency data to assist in ordering the objects.
- *
- * (In 9.1 and later, this also marks extension member objects as
- * not to be dumped.)
*/
getDependencies();
dumpTableData(Archive *fout, TableDataInfo *tdinfo)
{
TableInfo *tbinfo = tdinfo->tdtable;
- PQExpBuffer copyBuf;
+ PQExpBuffer copyBuf = createPQExpBuffer();
DataDumperPtr dumpFn;
char *copyStmt;
- if (!tdinfo->dobj.dump)
- return;
-
- copyBuf = createPQExpBuffer();
-
if (!dump_inserts)
{
/* Dump/restore using COPY */
tdinfo->dobj.dump = true;
tdinfo->tdtable = tbinfo;
tdinfo->oids = oids;
- tdinfo->ext_config = false; /* might get set later */
tdinfo->filtercond = NULL; /* might get set later */
addObjectDependency(&tdinfo->dobj, tbinfo->dobj.dumpId);
PGresult *res;
int ntups;
int i;
- int j;
PQExpBuffer query;
ExtensionInfo *extinfo;
int i_tableoid;
for (i = 0; i < ntups; i++)
{
- char *extconfig;
- char *extcondition;
- char **extconfigarray = NULL;
- char **extconditionarray = NULL;
- int nconfigitems;
- int nconditionitems;
-
extinfo[i].dobj.objType = DO_EXTENSION;
extinfo[i].dobj.catId.tableoid = atooid(PQgetvalue(res, i, i_tableoid));
extinfo[i].dobj.catId.oid = atooid(PQgetvalue(res, i, i_oid));
AssignDumpId(&extinfo[i].dobj);
extinfo[i].dobj.name = strdup(PQgetvalue(res, i, i_extname));
extinfo[i].namespace = strdup(PQgetvalue(res, i, i_nspname));
+ extinfo[i].extconfig = strdup(PQgetvalue(res, i, i_extconfig));
+ extinfo[i].extcondition = strdup(PQgetvalue(res, i, i_extcondition));
/* For the moment, all extensions are considered dumpable */
extinfo->dobj.dump = true;
-
- /*
- * Find and mark any configuration tables for this extension.
- *
- * Note that we create TableDataInfo objects even in schemaOnly mode,
- * ie, user data in a configuration table is treated like schema data.
- * This seems appropriate since system data in a config table would
- * get reloaded by CREATE EXTENSION.
- */
- extconfig = PQgetvalue(res, i, i_extconfig);
- extcondition = PQgetvalue(res, i, i_extcondition);
- if (parsePGArray(extconfig, &extconfigarray, &nconfigitems) &&
- parsePGArray(extcondition, &extconditionarray, &nconditionitems) &&
- nconfigitems == nconditionitems)
- {
- for (j = 0; j < nconfigitems; j++)
- {
- TableInfo *configtbl;
-
- configtbl = findTableByOid(atooid(extconfigarray[j]));
- if (configtbl && configtbl->dataObj == NULL)
- {
- makeTableDataInfo(configtbl, false);
- configtbl->dataObj->ext_config = true;
- if (strlen(extconditionarray[j]) > 0)
- configtbl->dataObj->filtercond = strdup(extconditionarray[j]);
- }
- }
- }
- if (extconfigarray)
- free(extconfigarray);
- if (extconditionarray)
- free(extconditionarray);
}
PQclear(res);
else
planginfo[i].lanowner = strdup("");
- /* Assume it should be dumped (getDependencies may override this) */
+ /* Assume it should be dumped (getExtensionMembership may override) */
planginfo[i].dobj.dump = true;
if (g_fout->remoteVersion < 70300)
castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
- /* Assume it should be dumped (getDependencies may override this) */
+ /* Assume it should be dumped (getExtensionMembership may override) */
castinfo[i].dobj.dump = true;
/*
destroyPQExpBuffer(delcmd);
}
+/*
+ * getExtensionMembership --- obtain extension membership data
+ */
+void
+getExtensionMembership(ExtensionInfo extinfo[], int numExtensions)
+{
+ PQExpBuffer query;
+ PGresult *res;
+ int ntups,
+ i;
+ int i_classid,
+ i_objid,
+ i_refclassid,
+ i_refobjid;
+ DumpableObject *dobj,
+ *refdobj;
+
+ /* Nothing to do if no extensions */
+ if (numExtensions == 0)
+ return;
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema("pg_catalog");
+
+ query = createPQExpBuffer();
+
+ /* refclassid constraint is redundant but may speed the search */
+ appendPQExpBuffer(query, "SELECT "
+ "classid, objid, refclassid, refobjid "
+ "FROM pg_depend "
+ "WHERE refclassid = 'pg_extension'::regclass "
+ "AND deptype = 'e' "
+ "ORDER BY 3,4");
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ ntups = PQntuples(res);
+
+ i_classid = PQfnumber(res, "classid");
+ i_objid = PQfnumber(res, "objid");
+ i_refclassid = PQfnumber(res, "refclassid");
+ i_refobjid = PQfnumber(res, "refobjid");
+
+ /*
+ * Since we ordered the SELECT by referenced ID, we can expect that
+ * multiple entries for the same extension will appear together; this
+ * saves on searches.
+ */
+ refdobj = NULL;
+
+ for (i = 0; i < ntups; i++)
+ {
+ CatalogId objId;
+ CatalogId refobjId;
+
+ objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
+ objId.oid = atooid(PQgetvalue(res, i, i_objid));
+ refobjId.tableoid = atooid(PQgetvalue(res, i, i_refclassid));
+ refobjId.oid = atooid(PQgetvalue(res, i, i_refobjid));
+
+ if (refdobj == NULL ||
+ refdobj->catId.tableoid != refobjId.tableoid ||
+ refdobj->catId.oid != refobjId.oid)
+ refdobj = findObjectByCatalogId(refobjId);
+
+ /*
+ * Failure to find objects mentioned in pg_depend is not unexpected,
+ * since for example we don't collect info about TOAST tables.
+ */
+ if (refdobj == NULL)
+ {
+#ifdef NOT_USED
+ fprintf(stderr, "no referenced object %u %u\n",
+ refobjId.tableoid, refobjId.oid);
+#endif
+ continue;
+ }
+
+ dobj = findObjectByCatalogId(objId);
+
+ if (dobj == NULL)
+ {
+#ifdef NOT_USED
+ fprintf(stderr, "no referencing object %u %u\n",
+ objId.tableoid, objId.oid);
+#endif
+ continue;
+ }
+
+ /* Record dependency so that getDependencies needn't repeat this */
+ addObjectDependency(dobj, refdobj->dumpId);
+
+ /*
+ * Mark the member object as not to be dumped. We still need the
+ * dependency link, to ensure that sorting is done correctly.
+ */
+ dobj->dump = false;
+ }
+
+ PQclear(res);
+
+ /*
+ * Now identify extension configuration tables and create TableDataInfo
+ * objects for them, ensuring their data will be dumped even though the
+ * tables themselves won't be.
+ *
+ * Note that we create TableDataInfo objects even in schemaOnly mode,
+ * ie, user data in a configuration table is treated like schema data.
+ * This seems appropriate since system data in a config table would
+ * get reloaded by CREATE EXTENSION.
+ */
+ for (i = 0; i < numExtensions; i++)
+ {
+ char *extconfig = extinfo[i].extconfig;
+ char *extcondition = extinfo[i].extcondition;
+ char **extconfigarray = NULL;
+ char **extconditionarray = NULL;
+ int nconfigitems;
+ int nconditionitems;
+
+ if (parsePGArray(extconfig, &extconfigarray, &nconfigitems) &&
+ parsePGArray(extcondition, &extconditionarray, &nconditionitems) &&
+ nconfigitems == nconditionitems)
+ {
+ int j;
+
+ for (j = 0; j < nconfigitems; j++)
+ {
+ TableInfo *configtbl;
+
+ configtbl = findTableByOid(atooid(extconfigarray[j]));
+ if (configtbl && configtbl->dataObj == NULL)
+ {
+ /*
+ * Note: config tables are dumped without OIDs regardless
+ * of the --oids setting. This is because row filtering
+ * conditions aren't compatible with dumping OIDs.
+ */
+ makeTableDataInfo(configtbl, false);
+ if (strlen(extconditionarray[j]) > 0)
+ configtbl->dataObj->filtercond = strdup(extconditionarray[j]);
+ }
+ }
+ }
+ if (extconfigarray)
+ free(extconfigarray);
+ if (extconditionarray)
+ free(extconditionarray);
+ }
+
+ destroyPQExpBuffer(query);
+}
+
/*
* getDependencies --- obtain available dependency data
*/
query = createPQExpBuffer();
+ /*
+ * PIN dependencies aren't interesting, and EXTENSION dependencies were
+ * already processed by getExtensionMembership.
+ */
appendPQExpBuffer(query, "SELECT "
"classid, objid, refclassid, refobjid, deptype "
"FROM pg_depend "
- "WHERE deptype != 'p' "
+ "WHERE deptype != 'p' AND deptype != 'e' "
"ORDER BY 1,2");
res = PQexec(g_conn, query->data);
else
/* normal case */
addObjectDependency(dobj, refdobj->dumpId);
-
- /*
- * If it's an extension-membership dependency, mark the member
- * object as not to be dumped. We still need the dependency links,
- * though, to ensure that sorting is done correctly.
- */
- if (deptype == 'e')
- {
- dobj->dump = false;
- if (dobj->objType == DO_TABLE)
- {
- /* Mark the data as not to be dumped either, unless config */
- TableDataInfo *tdinfo = ((TableInfo *) dobj)->dataObj;
-
- if (tdinfo && !tdinfo->ext_config)
- tdinfo->dobj.dump = false;
- }
- }
}
PQclear(res);