/*
* These variables are static to avoid the notational cruft of having to pass
- * them into findTableByOid() and friends. For each of these arrays, we
- * build a sorted-by-OID index array immediately after it's built, and then
- * we use binary search in findTableByOid() and friends. (qsort'ing the base
- * arrays themselves would be simpler, but it doesn't work because pg_dump.c
- * may have already established pointers between items.)
- */
-static TableInfo *tblinfo;
-static TypeInfo *typinfo;
-static FuncInfo *funinfo;
-static OprInfo *oprinfo;
-static NamespaceInfo *nspinfo;
-static int numTables;
-static int numTypes;
-static int numFuncs;
-static int numOperators;
-static int numCollations;
-static int numNamespaces;
+ * them into findTableByOid() and friends. For each of these arrays, we build
+ * a sorted-by-OID index array immediately after the objects are fetched,
+ * and then we use binary search in findTableByOid() and friends. (qsort'ing
+ * the object arrays themselves would be simpler, but it doesn't work because
+ * pg_dump.c may have already established pointers between items.)
+ */
static DumpableObject **tblinfoindex;
static DumpableObject **typinfoindex;
static DumpableObject **funinfoindex;
static DumpableObject **oprinfoindex;
static DumpableObject **collinfoindex;
static DumpableObject **nspinfoindex;
+static DumpableObject **extinfoindex;
+static int numTables;
+static int numTypes;
+static int numFuncs;
+static int numOperators;
+static int numCollations;
+static int numNamespaces;
+static int numExtensions;
+/* This is an array of object identities, not actual DumpableObjects */
+static ExtensionMemberId *extmembers;
+static int numextmembers;
static void flagInhTables(TableInfo *tbinfo, int numTables,
InhInfo *inhinfo, int numInherits);
static DumpableObject **buildIndexArray(void *objArray, int numObjs,
Size objSize);
static int DOCatalogIdCompare(const void *p1, const void *p2);
+static int ExtensionMemberIdCompare(const void *p1, const void *p2);
static void findParentsByOid(TableInfo *self,
InhInfo *inhinfo, int numInherits);
static int strInArray(const char *pattern, char **arr, int arr_size);
TableInfo *
getSchemaData(Archive *fout, int *numTablesPtr)
{
+ TableInfo *tblinfo;
+ TypeInfo *typinfo;
+ FuncInfo *funinfo;
+ OprInfo *oprinfo;
+ CollInfo *collinfo;
+ NamespaceInfo *nspinfo;
ExtensionInfo *extinfo;
InhInfo *inhinfo;
- CollInfo *collinfo;
- int numExtensions;
int numAggregates;
int numInherits;
int numRules;
int numDefaultACLs;
int numEventTriggers;
+ /*
+ * We must read extensions and extension membership info first, because
+ * extension membership needs to be consultable during decisions about
+ * whether other objects are to be dumped.
+ */
+ if (g_verbose)
+ write_msg(NULL, "reading extensions\n");
+ extinfo = getExtensions(fout, &numExtensions);
+ extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo));
+
+ if (g_verbose)
+ write_msg(NULL, "identifying extension members\n");
+ getExtensionMembership(fout, extinfo, numExtensions);
+
if (g_verbose)
write_msg(NULL, "reading schemas\n");
nspinfo = getNamespaces(fout, &numNamespaces);
/* Do this after we've built tblinfoindex */
getOwnedSeqs(fout, tblinfo, numTables);
- if (g_verbose)
- write_msg(NULL, "reading extensions\n");
- extinfo = getExtensions(fout, &numExtensions);
-
if (g_verbose)
write_msg(NULL, "reading user-defined functions\n");
funinfo = getFuncs(fout, &numFuncs);
write_msg(NULL, "reading event triggers\n");
getEventTriggers(fout, &numEventTriggers);
- /*
- * 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.
- */
+ /* Identify extension configuration tables that should be dumped */
if (g_verbose)
- write_msg(NULL, "finding extension members\n");
- getExtensionMembership(fout, extinfo, numExtensions);
+ write_msg(NULL, "finding extension tables\n");
+ processExtensionTables(fout, extinfo, numExtensions);
/* Link tables to parents, mark parents of target tables interesting */
if (g_verbose)
return (NamespaceInfo *) findObjectByOid(oid, nspinfoindex, numNamespaces);
}
+/*
+ * findExtensionByOid
+ * finds the entry (in extinfo) of the extension with the given oid
+ * returns NULL if not found
+ */
+ExtensionInfo *
+findExtensionByOid(Oid oid)
+{
+ return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
+}
+
+
+/*
+ * setExtensionMembership
+ * accept and save data about which objects belong to extensions
+ */
+void
+setExtensionMembership(ExtensionMemberId *extmems, int nextmems)
+{
+ /* Sort array in preparation for binary searches */
+ if (nextmems > 1)
+ qsort((void *) extmems, nextmems, sizeof(ExtensionMemberId),
+ ExtensionMemberIdCompare);
+ /* And save */
+ extmembers = extmems;
+ numextmembers = nextmems;
+}
+
+/*
+ * findOwningExtension
+ * return owning extension for specified catalog ID, or NULL if none
+ */
+ExtensionInfo *
+findOwningExtension(CatalogId catalogId)
+{
+ ExtensionMemberId *low;
+ ExtensionMemberId *high;
+
+ /*
+ * We could use bsearch() here, but the notational cruft of calling
+ * bsearch is nearly as bad as doing it ourselves; and the generalized
+ * bsearch function is noticeably slower as well.
+ */
+ if (numextmembers <= 0)
+ return NULL;
+ low = extmembers;
+ high = extmembers + (numextmembers - 1);
+ while (low <= high)
+ {
+ ExtensionMemberId *middle;
+ int difference;
+
+ middle = low + (high - low) / 2;
+ /* comparison must match ExtensionMemberIdCompare, below */
+ difference = oidcmp(middle->catId.oid, catalogId.oid);
+ if (difference == 0)
+ difference = oidcmp(middle->catId.tableoid, catalogId.tableoid);
+ if (difference == 0)
+ return middle->ext;
+ else if (difference < 0)
+ low = middle + 1;
+ else
+ high = middle - 1;
+ }
+ return NULL;
+}
+
+/*
+ * qsort comparator for ExtensionMemberIds
+ */
+static int
+ExtensionMemberIdCompare(const void *p1, const void *p2)
+{
+ const ExtensionMemberId *obj1 = (const ExtensionMemberId *) p1;
+ const ExtensionMemberId *obj2 = (const ExtensionMemberId *) p2;
+ int cmpval;
+
+ /*
+ * Compare OID first since it's usually unique, whereas there will only be
+ * a few distinct values of tableoid.
+ */
+ cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
+ if (cmpval == 0)
+ cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
+ return cmpval;
+}
+
/*
* findParentsByOid
destroyPQExpBuffer(query);
}
+/*
+ * checkExtensionMembership
+ * Determine whether object is an extension member, and if so,
+ * record an appropriate dependency and set the object's dump flag.
+ *
+ * It's important to call this for each object that could be an extension
+ * member. Generally, we integrate this with determining the object's
+ * to-be-dumped-ness, since extension membership overrides other rules for that.
+ *
+ * Returns true if object is an extension member, else false.
+ */
+static bool
+checkExtensionMembership(DumpableObject *dobj, DumpOptions *dopt)
+{
+ ExtensionInfo *ext = findOwningExtension(dobj->catId);
+
+ if (ext == NULL)
+ return false;
+
+ dobj->ext_member = true;
+
+ /* Record dependency so that getDependencies needn't deal with that */
+ addObjectDependency(dobj, ext->dobj.dumpId);
+
+ /*
+ * Normally, mark the member object as not to be dumped. But in binary
+ * upgrades, we still dump the members individually, since the idea is to
+ * exactly reproduce the database contents rather than replace the
+ * extension contents with something different.
+ */
+ if (!dopt->binary_upgrade)
+ dobj->dump = false;
+ else
+ dobj->dump = ext->dobj.dump;
+
+ return true;
+}
+
/*
* selectDumpableNamespace: policy-setting subroutine
* Mark a namespace as to be dumped or not
*/
static void
-selectDumpableNamespace(NamespaceInfo *nsinfo)
+selectDumpableNamespace(NamespaceInfo *nsinfo, DumpOptions *dopt)
{
+ if (checkExtensionMembership(&nsinfo->dobj, dopt))
+ return; /* extension membership overrides all else */
+
/*
* If specific tables are being dumped, do not dump any complete
* namespaces. If specific namespaces are being dumped, dump just those
* Mark a table as to be dumped or not
*/
static void
-selectDumpableTable(TableInfo *tbinfo)
+selectDumpableTable(TableInfo *tbinfo, DumpOptions *dopt)
{
+ if (checkExtensionMembership(&tbinfo->dobj, dopt))
+ return; /* extension membership overrides all else */
+
/*
* If specific tables are being dumped, dump just those tables; else, dump
* according to the parent namespace's dump flag.
* object (the table or base type).
*/
static void
-selectDumpableType(TypeInfo *tyinfo)
+selectDumpableType(TypeInfo *tyinfo, DumpOptions *dopt)
{
/* skip complex types, except for standalone composite types */
if (OidIsValid(tyinfo->typrelid) &&
*/
}
+ if (checkExtensionMembership(&tyinfo->dobj, dopt))
+ return; /* extension membership overrides all else */
+
/* dump only types in dumpable namespaces */
if (!tyinfo->dobj.namespace->dobj.dump)
tyinfo->dobj.dump = false;
* and aclsSkip are checked separately.
*/
static void
-selectDumpableDefaultACL(DumpOptions *dopt, DefaultACLInfo *dinfo)
+selectDumpableDefaultACL(DefaultACLInfo *dinfo, DumpOptions *dopt)
{
+ /* Default ACLs can't be extension members */
+
if (dinfo->dobj.namespace)
dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
else
* OID is in the range reserved for initdb.
*/
static void
-selectDumpableCast(DumpOptions *dopt, CastInfo *cast)
+selectDumpableCast(CastInfo *cast, DumpOptions *dopt)
{
+ if (checkExtensionMembership(&cast->dobj, dopt))
+ return; /* extension membership overrides all else */
+
if (cast->dobj.catId.oid < (Oid) FirstNormalObjectId)
cast->dobj.dump = false;
else
cast->dobj.dump = dopt->include_everything;
}
+/*
+ * selectDumpableProcLang: policy-setting subroutine
+ * Mark a procedural language as to be dumped or not
+ *
+ * Procedural languages do not belong to any particular namespace. To
+ * identify built-in languages, we must resort to checking whether the
+ * language's OID is in the range reserved for initdb.
+ */
+static void
+selectDumpableProcLang(ProcLangInfo *plang, DumpOptions *dopt)
+{
+ if (checkExtensionMembership(&plang->dobj, dopt))
+ return; /* extension membership overrides all else */
+
+ if (plang->dobj.catId.oid < (Oid) FirstNormalObjectId)
+ plang->dobj.dump = false;
+ else
+ plang->dobj.dump = dopt->include_everything;
+}
+
/*
* selectDumpableExtension: policy-setting subroutine
* Mark an extension as to be dumped or not
* such extensions by their having OIDs in the range reserved for initdb.
*/
static void
-selectDumpableExtension(DumpOptions *dopt, ExtensionInfo *extinfo)
+selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
{
if (dopt->binary_upgrade && extinfo->dobj.catId.oid < (Oid) FirstNormalObjectId)
extinfo->dobj.dump = false;
* Use this only for object types without a special-case routine above.
*/
static void
-selectDumpableObject(DumpableObject *dobj)
+selectDumpableObject(DumpableObject *dobj, DumpOptions *dopt)
{
+ if (checkExtensionMembership(dobj, dopt))
+ return; /* extension membership overrides all else */
+
/*
- * Default policy is to dump if parent namespace is dumpable, or always
- * for non-namespace-associated items.
+ * Default policy is to dump if parent namespace is dumpable, or for
+ * non-namespace-associated items, dump if we're dumping "everything".
*/
if (dobj->namespace)
dobj->dump = dobj->namespace->dobj.dump;
else
- dobj->dump = true;
+ dobj->dump = dopt->include_everything;
}
/*
NamespaceInfo *
getNamespaces(Archive *fout, int *numNamespaces)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
nsinfo[0].rolname = pg_strdup("");
nsinfo[0].nspacl = pg_strdup("");
- selectDumpableNamespace(&nsinfo[0]);
+ selectDumpableNamespace(&nsinfo[0], dopt);
nsinfo[1].dobj.objType = DO_NAMESPACE;
nsinfo[1].dobj.catId.tableoid = 0;
nsinfo[1].rolname = pg_strdup("");
nsinfo[1].nspacl = pg_strdup("");
- selectDumpableNamespace(&nsinfo[1]);
+ selectDumpableNamespace(&nsinfo[1], dopt);
*numNamespaces = 2;
nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl));
/* Decide whether to dump this namespace */
- selectDumpableNamespace(&nsinfo[i]);
+ selectDumpableNamespace(&nsinfo[i], dopt);
if (strlen(nsinfo[i].rolname) == 0)
write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n",
extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));
/* Decide whether we want to dump it */
- selectDumpableExtension(dopt, &(extinfo[i]));
+ selectDumpableExtension(&(extinfo[i]), dopt);
}
PQclear(res);
TypeInfo *
getTypes(Archive *fout, int *numTypes)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
tyinfo[i].isArray = false;
/* Decide whether we want to dump it */
- selectDumpableType(&tyinfo[i]);
+ selectDumpableType(&tyinfo[i], dopt);
/*
* If it's a domain, fetch info about its constraints, if any
OprInfo *
getOperators(Archive *fout, int *numOprs)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));
/* Decide whether we want to dump it */
- selectDumpableObject(&(oprinfo[i].dobj));
+ selectDumpableObject(&(oprinfo[i].dobj), dopt);
if (strlen(oprinfo[i].rolname) == 0)
write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
CollInfo *
getCollations(Archive *fout, int *numCollations)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
/* Decide whether we want to dump it */
- selectDumpableObject(&(collinfo[i].dobj));
+ selectDumpableObject(&(collinfo[i].dobj), dopt);
}
PQclear(res);
ConvInfo *
getConversions(Archive *fout, int *numConversions)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
convinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
/* Decide whether we want to dump it */
- selectDumpableObject(&(convinfo[i].dobj));
+ selectDumpableObject(&(convinfo[i].dobj), dopt);
}
PQclear(res);
OpclassInfo *
getOpclasses(Archive *fout, int *numOpclasses)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
/* Decide whether we want to dump it */
- selectDumpableObject(&(opcinfo[i].dobj));
+ selectDumpableObject(&(opcinfo[i].dobj), dopt);
if (fout->remoteVersion >= 70300)
{
OpfamilyInfo *
getOpfamilies(Archive *fout, int *numOpfamilies)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));
/* Decide whether we want to dump it */
- selectDumpableObject(&(opfinfo[i].dobj));
+ selectDumpableObject(&(opfinfo[i].dobj), dopt);
if (fout->remoteVersion >= 70300)
{
}
/* Decide whether we want to dump it */
- selectDumpableObject(&(agginfo[i].aggfn.dobj));
+ selectDumpableObject(&(agginfo[i].aggfn.dobj), dopt);
}
PQclear(res);
}
/* Decide whether we want to dump it */
- selectDumpableObject(&(finfo[i].dobj));
+ selectDumpableObject(&(finfo[i].dobj), dopt);
if (strlen(finfo[i].rolname) == 0)
write_msg(NULL,
if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
tblinfo[i].dobj.dump = false;
else
- selectDumpableTable(&tblinfo[i]);
+ selectDumpableTable(&tblinfo[i], dopt);
tblinfo[i].interesting = tblinfo[i].dobj.dump;
tblinfo[i].postponed_def = false; /* might get set during sort */
EventTriggerInfo *
getEventTriggers(Archive *fout, int *numEventTriggers)
{
+ DumpOptions *dopt = fout->dopt;
int i;
PQExpBuffer query;
PGresult *res;
evtinfo[i].evttags = pg_strdup(PQgetvalue(res, i, i_evttags));
evtinfo[i].evtfname = pg_strdup(PQgetvalue(res, i, i_evtfname));
evtinfo[i].evtenabled = *(PQgetvalue(res, i, i_evtenabled));
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(evtinfo[i].dobj), dopt);
}
PQclear(res);
ProcLangInfo *
getProcLangs(Archive *fout, int *numProcLangs)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
planginfo[i].lanacl = pg_strdup(PQgetvalue(res, i, i_lanacl));
planginfo[i].lanowner = pg_strdup(PQgetvalue(res, i, i_lanowner));
+ /* Decide whether we want to dump it */
+ selectDumpableProcLang(&(planginfo[i]), dopt);
+
if (fout->remoteVersion < 70300)
{
/*
}
/* Decide whether we want to dump it */
- selectDumpableCast(dopt, &(castinfo[i]));
+ selectDumpableCast(&(castinfo[i]), dopt);
}
PQclear(res);
TransformInfo *
getTransforms(Archive *fout, int *numTransforms)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
typeInfo->dobj.name, lanname);
transforminfo[i].dobj.name = namebuf.data;
free(lanname);
+
+ /* Decide whether we want to dump it */
+ selectDumpableObject(&(transforminfo[i].dobj), dopt);
}
PQclear(res);
TSParserInfo *
getTSParsers(Archive *fout, int *numTSParsers)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));
/* Decide whether we want to dump it */
- selectDumpableObject(&(prsinfo[i].dobj));
+ selectDumpableObject(&(prsinfo[i].dobj), dopt);
}
PQclear(res);
TSDictInfo *
getTSDictionaries(Archive *fout, int *numTSDicts)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));
/* Decide whether we want to dump it */
- selectDumpableObject(&(dictinfo[i].dobj));
+ selectDumpableObject(&(dictinfo[i].dobj), dopt);
}
PQclear(res);
TSTemplateInfo *
getTSTemplates(Archive *fout, int *numTSTemplates)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));
/* Decide whether we want to dump it */
- selectDumpableObject(&(tmplinfo[i].dobj));
+ selectDumpableObject(&(tmplinfo[i].dobj), dopt);
}
PQclear(res);
TSConfigInfo *
getTSConfigurations(Archive *fout, int *numTSConfigs)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));
/* Decide whether we want to dump it */
- selectDumpableObject(&(cfginfo[i].dobj));
+ selectDumpableObject(&(cfginfo[i].dobj), dopt);
}
PQclear(res);
FdwInfo *
getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
fdwinfo[i].fdwacl = pg_strdup(PQgetvalue(res, i, i_fdwacl));
/* Decide whether we want to dump it */
- selectDumpableObject(&(fdwinfo[i].dobj));
+ selectDumpableObject(&(fdwinfo[i].dobj), dopt);
}
PQclear(res);
ForeignServerInfo *
getForeignServers(Archive *fout, int *numForeignServers)
{
+ DumpOptions *dopt = fout->dopt;
PGresult *res;
int ntups;
int i;
srvinfo[i].srvacl = pg_strdup(PQgetvalue(res, i, i_srvacl));
/* Decide whether we want to dump it */
- selectDumpableObject(&(srvinfo[i].dobj));
+ selectDumpableObject(&(srvinfo[i].dobj), dopt);
}
PQclear(res);
daclinfo[i].defaclacl = pg_strdup(PQgetvalue(res, i, i_defaclacl));
/* Decide whether we want to dump it */
- selectDumpableDefaultACL(dopt, &(daclinfo[i]));
+ selectDumpableDefaultACL(&(daclinfo[i]), dopt);
}
PQclear(res);
destroyPQExpBuffer(q);
}
-/*
- * Determine whether we want to dump definitions for procedural languages.
- * Since the languages themselves don't have schemas, we can't rely on
- * the normal schema-based selection mechanism. We choose to dump them
- * whenever neither --schema nor --table was given. (Before 8.1, we used
- * the dump flag of the PL's call handler function, but in 8.1 this will
- * probably always be false since call handlers are created in pg_catalog.)
- *
- * For some backwards compatibility with the older behavior, we forcibly
- * dump a PL if its handler function (and validator if any) are in a
- * dumpable namespace. That case is not checked here.
- *
- * Also, if the PL belongs to an extension, we do not use this heuristic.
- * That case isn't checked here either.
- */
-static bool
-shouldDumpProcLangs(DumpOptions *dopt)
-{
- if (!dopt->include_everything)
- return false;
- /* And they're schema not data */
- if (dopt->dataOnly)
- return false;
- return true;
-}
-
/*
* dumpProcLang
* writes out to fout the queries to recreate a user-defined
/*
* If the functions are dumpable then emit a traditional CREATE LANGUAGE
- * with parameters. Otherwise, dump only if shouldDumpProcLangs() says to
- * dump it.
- *
- * However, for a language that belongs to an extension, we must not use
- * the shouldDumpProcLangs heuristic, but just dump the language iff we're
- * told to (via dobj.dump). Generally the support functions will belong
- * to the same extension and so have the same dump flags ... if they
- * don't, this might not work terribly nicely.
+ * with parameters. Otherwise, we'll write a parameterless command, which
+ * will rely on data from pg_pltemplate.
*/
useParams = (funcInfo != NULL &&
(inlineInfo != NULL || !OidIsValid(plang->laninline)) &&
(validatorInfo != NULL || !OidIsValid(plang->lanvalidator)));
- if (!plang->dobj.ext_member)
- {
- if (!useParams && !shouldDumpProcLangs(dopt))
- return;
- }
-
defqry = createPQExpBuffer();
delqry = createPQExpBuffer();
labelq = createPQExpBuffer();
if (!fdwinfo->dobj.dump || dopt->dataOnly)
return;
- /*
- * FDWs that belong to an extension are dumped based on their "dump"
- * field. Otherwise omit them if we are only dumping some specific object.
- */
- if (!fdwinfo->dobj.ext_member)
- if (!dopt->include_everything)
- return;
-
q = createPQExpBuffer();
delq = createPQExpBuffer();
labelq = createPQExpBuffer();
char *fdwname;
/* Skip if not to be dumped */
- if (!srvinfo->dobj.dump || dopt->dataOnly || !dopt->include_everything)
+ if (!srvinfo->dobj.dump || dopt->dataOnly)
return;
q = createPQExpBuffer();
/*
* 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
- * foreign 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.
+ * We need to identify objects that are extension members as soon as they're
+ * loaded, so that we can correctly determine whether they need to be dumped.
+ * Generally speaking, extension member objects will get marked as *not* to
+ * be dumped, as they will be recreated by the single CREATE EXTENSION
+ * command. However, in binary upgrade mode we still need to dump the members
+ * individually.
*/
void
getExtensionMembership(Archive *fout, ExtensionInfo extinfo[],
int numExtensions)
{
- DumpOptions *dopt = fout->dopt;
PQExpBuffer query;
PGresult *res;
int ntups,
+ nextmembers,
i;
int i_classid,
i_objid,
- i_refclassid,
- i_refobjid,
- i_conrelid,
- i_confrelid;
- DumpableObject *dobj,
- *refdobj;
+ i_refobjid;
+ ExtensionMemberId *extmembers;
+ ExtensionInfo *ext;
/* Nothing to do if no extensions */
if (numExtensions == 0)
/* refclassid constraint is redundant but may speed the search */
appendPQExpBufferStr(query, "SELECT "
- "classid, objid, refclassid, refobjid "
+ "classid, objid, refobjid "
"FROM pg_depend "
"WHERE refclassid = 'pg_extension'::regclass "
"AND deptype = 'e' "
- "ORDER BY 3,4");
+ "ORDER BY 3");
res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);
i_classid = PQfnumber(res, "classid");
i_objid = PQfnumber(res, "objid");
- i_refclassid = PQfnumber(res, "refclassid");
i_refobjid = PQfnumber(res, "refobjid");
+ extmembers = (ExtensionMemberId *) pg_malloc(ntups * sizeof(ExtensionMemberId));
+ nextmembers = 0;
+
/*
+ * Accumulate data into extmembers[].
+ *
* 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;
+ ext = NULL;
for (i = 0; i < ntups; i++)
{
CatalogId objId;
- CatalogId refobjId;
+ Oid extId;
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));
+ extId = atooid(PQgetvalue(res, i, i_refobjid));
- if (refdobj == NULL ||
- refdobj->catId.tableoid != refobjId.tableoid ||
- refdobj->catId.oid != refobjId.oid)
- refdobj = findObjectByCatalogId(refobjId);
+ if (ext == NULL ||
+ ext->dobj.catId.oid != extId)
+ ext = findExtensionByOid(extId);
- /*
- * 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)
+ if (ext == NULL)
{
-#ifdef NOT_USED
- fprintf(stderr, "no referenced object %u %u\n",
- refobjId.tableoid, refobjId.oid);
-#endif
+ /* shouldn't happen */
+ fprintf(stderr, "could not find referenced extension %u\n", extId);
continue;
}
- dobj = findObjectByCatalogId(objId);
+ extmembers[nextmembers].catId = objId;
+ extmembers[nextmembers].ext = ext;
+ nextmembers++;
+ }
- if (dobj == NULL)
- {
-#ifdef NOT_USED
- fprintf(stderr, "no referencing object %u %u\n",
- objId.tableoid, objId.oid);
-#endif
- continue;
- }
+ PQclear(res);
- /* Record dependency so that getDependencies needn't repeat this */
- addObjectDependency(dobj, refdobj->dumpId);
+ /* Remember the data for use later */
+ setExtensionMembership(extmembers, nextmembers);
- dobj->ext_member = true;
+ destroyPQExpBuffer(query);
+}
- /*
- * Normally, mark the member object as not to be dumped. But in
- * binary upgrades, we still dump the members individually, since the
- * idea is to exactly reproduce the database contents rather than
- * replace the extension contents with something different.
- */
- if (!dopt->binary_upgrade)
- dobj->dump = false;
- else
- dobj->dump = refdobj->dump;
- }
+/*
+ * processExtensionTables --- deal with extension configuration tables
+ *
+ * There are two parts to this process:
+ *
+ * 1. 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
+ * foreign keys, which brings us to-
+ *
+ * 2. 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
+processExtensionTables(Archive *fout, ExtensionInfo extinfo[],
+ int numExtensions)
+{
+ DumpOptions *dopt = fout->dopt;
+ PQExpBuffer query;
+ PGresult *res;
+ int ntups,
+ i;
+ int i_conrelid,
+ i_confrelid;
- PQclear(res);
+ /* Nothing to do if no extensions */
+ if (numExtensions == 0)
+ return;
/*
- * Now identify extension configuration tables and create TableDataInfo
+ * Identify extension configuration tables and create TableDataInfo
* objects for them, ensuring their data will be dumped even though the
* tables themselves won't be.
*
/*
* 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.
+ * dump the data out in an order that 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.
*/
+
+ /* Make sure we are in proper schema */
+ selectSourceSchema(fout, "pg_catalog");
+
+ query = createPQExpBuffer();
+
printfPQExpBuffer(query,
"SELECT conrelid, confrelid "
"FROM pg_constraint "