From e72d7d85310c397a94748db72d73a59c57e0b0dc Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Wed, 13 Jan 2016 18:55:27 -0500 Subject: [PATCH] Handle extension members when first setting object dump flags in pg_dump. pg_dump's original approach to handling extension member objects was to run around and clear (or set) their dump flags rather late in its data collection process. Unfortunately, quite a lot of code expects those flags to be valid before that; which was an entirely reasonable expectation before we added extensions. In particular, this explains Karsten Hilbert's recent report of pg_upgrade failing on a database in which an extension has been installed into the pg_catalog schema. Its objects are initially marked as not-to-be-dumped on the strength of their schema, and later we change them to must-dump because we're doing a binary upgrade of their extension; but we've already skipped essential tasks like making associated DO_SHELL_TYPE objects. To fix, collect extension membership data first, and incorporate it in the initial setting of the dump flags, so that those are once again correct from the get-go. This has the undesirable side effect of slightly lengthening the time taken before pg_dump acquires table locks, but testing suggests that the increase in that window is not very much. Along the way, get rid of ugly special-case logic for deciding whether to dump procedural languages, FDWs, and foreign servers; dump decisions for those are now correct up-front, too. In 9.3 and up, this also fixes erroneous logic about when to dump event triggers (basically, they were *always* dumped before). In 9.5 and up, transform objects had that problem too. Since this problem came in with extensions, back-patch to all supported versions. --- src/bin/pg_dump/common.c | 156 +++++++++++++--- src/bin/pg_dump/pg_dump.c | 377 ++++++++++++++++++++++---------------- src/bin/pg_dump/pg_dump.h | 16 ++ 3 files changed, 359 insertions(+), 190 deletions(-) diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c index 3b4e478ad9..f798b15e3c 100644 --- a/src/bin/pg_dump/common.c +++ b/src/bin/pg_dump/common.c @@ -40,30 +40,30 @@ static int numCatalogIds = 0; /* * 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.) + * 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 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; 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); @@ -71,6 +71,7 @@ static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables); 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); @@ -83,10 +84,14 @@ 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; @@ -105,6 +110,20 @@ getSchemaData(Archive *fout, int *numTablesPtr) 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); @@ -124,10 +143,6 @@ getSchemaData(Archive *fout, int *numTablesPtr) /* 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); @@ -214,14 +229,10 @@ getSchemaData(Archive *fout, int *numTablesPtr) 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) @@ -764,6 +775,93 @@ findNamespaceByOid(Oid oid) 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 diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index 1408e761f7..9196cf44d9 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -1256,13 +1256,54 @@ expand_table_name_patterns(Archive *fout, 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 @@ -1293,8 +1334,11 @@ selectDumpableNamespace(NamespaceInfo *nsinfo) * 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. @@ -1328,7 +1372,7 @@ selectDumpableTable(TableInfo *tbinfo) * 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) && @@ -1357,6 +1401,9 @@ selectDumpableType(TypeInfo *tyinfo) */ } + 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; @@ -1373,8 +1420,10 @@ selectDumpableType(TypeInfo *tyinfo) * 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 @@ -1391,14 +1440,37 @@ selectDumpableDefaultACL(DumpOptions *dopt, DefaultACLInfo *dinfo) * 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 @@ -1410,7 +1482,7 @@ selectDumpableCast(DumpOptions *dopt, CastInfo *cast) * 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; @@ -1425,16 +1497,19 @@ selectDumpableExtension(DumpOptions *dopt, ExtensionInfo *extinfo) * 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; } /* @@ -3261,6 +3336,7 @@ binary_upgrade_extension_member(PQExpBuffer upgrade_buffer, NamespaceInfo * getNamespaces(Archive *fout, int *numNamespaces) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -3288,7 +3364,7 @@ getNamespaces(Archive *fout, int *numNamespaces) 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; @@ -3298,7 +3374,7 @@ getNamespaces(Archive *fout, int *numNamespaces) nsinfo[1].rolname = pg_strdup(""); nsinfo[1].nspacl = pg_strdup(""); - selectDumpableNamespace(&nsinfo[1]); + selectDumpableNamespace(&nsinfo[1], dopt); *numNamespaces = 2; @@ -3342,7 +3418,7 @@ getNamespaces(Archive *fout, int *numNamespaces) 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", @@ -3466,7 +3542,7 @@ getExtensions(Archive *fout, int *numExtensions) 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); @@ -3490,6 +3566,7 @@ getExtensions(Archive *fout, int *numExtensions) TypeInfo * getTypes(Archive *fout, int *numTypes) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -3656,7 +3733,7 @@ getTypes(Archive *fout, int *numTypes) 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 @@ -3762,6 +3839,7 @@ getTypes(Archive *fout, int *numTypes) OprInfo * getOperators(Archive *fout, int *numOprs) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -3847,7 +3925,7 @@ getOperators(Archive *fout, int *numOprs) 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", @@ -3871,6 +3949,7 @@ getOperators(Archive *fout, int *numOprs) CollInfo * getCollations(Archive *fout, int *numCollations) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -3932,7 +4011,7 @@ getCollations(Archive *fout, int *numCollations) 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); @@ -3952,6 +4031,7 @@ getCollations(Archive *fout, int *numCollations) ConvInfo * getConversions(Archive *fout, int *numConversions) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -4013,7 +4093,7 @@ getConversions(Archive *fout, int *numConversions) 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); @@ -4033,6 +4113,7 @@ getConversions(Archive *fout, int *numConversions) OpclassInfo * getOpclasses(Archive *fout, int *numOpclasses) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -4104,7 +4185,7 @@ getOpclasses(Archive *fout, int *numOpclasses) 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) { @@ -4131,6 +4212,7 @@ getOpclasses(Archive *fout, int *numOpclasses) OpfamilyInfo * getOpfamilies(Archive *fout, int *numOpfamilies) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -4192,7 +4274,7 @@ getOpfamilies(Archive *fout, int *numOpfamilies) 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) { @@ -4357,7 +4439,7 @@ getAggregates(Archive *fout, int *numAggs) } /* Decide whether we want to dump it */ - selectDumpableObject(&(agginfo[i].aggfn.dobj)); + selectDumpableObject(&(agginfo[i].aggfn.dobj), dopt); } PQclear(res); @@ -4515,7 +4597,7 @@ getFuncs(Archive *fout, int *numFuncs) } /* 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, @@ -5178,7 +5260,7 @@ getTables(Archive *fout, int *numTables) 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 */ @@ -6258,6 +6340,7 @@ getTriggers(Archive *fout, TableInfo tblinfo[], int numTables) EventTriggerInfo * getEventTriggers(Archive *fout, int *numEventTriggers) { + DumpOptions *dopt = fout->dopt; int i; PQExpBuffer query; PGresult *res; @@ -6325,6 +6408,9 @@ getEventTriggers(Archive *fout, int *numEventTriggers) 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); @@ -6346,6 +6432,7 @@ getEventTriggers(Archive *fout, int *numEventTriggers) ProcLangInfo * getProcLangs(Archive *fout, int *numProcLangs) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -6479,6 +6566,9 @@ getProcLangs(Archive *fout, int *numProcLangs) 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) { /* @@ -6616,7 +6706,7 @@ getCasts(Archive *fout, int *numCasts) } /* Decide whether we want to dump it */ - selectDumpableCast(dopt, &(castinfo[i])); + selectDumpableCast(&(castinfo[i]), dopt); } PQclear(res); @@ -6652,6 +6742,7 @@ get_language_name(Archive *fout, Oid langid) TransformInfo * getTransforms(Archive *fout, int *numTransforms) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -6724,6 +6815,9 @@ getTransforms(Archive *fout, int *numTransforms) 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); @@ -7334,6 +7428,7 @@ shouldPrintColumn(DumpOptions *dopt, TableInfo *tbinfo, int colno) TSParserInfo * getTSParsers(Archive *fout, int *numTSParsers) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7406,7 +7501,7 @@ getTSParsers(Archive *fout, int *numTSParsers) 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); @@ -7426,6 +7521,7 @@ getTSParsers(Archive *fout, int *numTSParsers) TSDictInfo * getTSDictionaries(Archive *fout, int *numTSDicts) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7491,7 +7587,7 @@ getTSDictionaries(Archive *fout, int *numTSDicts) 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); @@ -7511,6 +7607,7 @@ getTSDictionaries(Archive *fout, int *numTSDicts) TSTemplateInfo * getTSTemplates(Archive *fout, int *numTSTemplates) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7568,7 +7665,7 @@ getTSTemplates(Archive *fout, int *numTSTemplates) 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); @@ -7588,6 +7685,7 @@ getTSTemplates(Archive *fout, int *numTSTemplates) TSConfigInfo * getTSConfigurations(Archive *fout, int *numTSConfigs) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7646,7 +7744,7 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) 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); @@ -7666,6 +7764,7 @@ getTSConfigurations(Archive *fout, int *numTSConfigs) FdwInfo * getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7754,7 +7853,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) 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); @@ -7774,6 +7873,7 @@ getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers) ForeignServerInfo * getForeignServers(Archive *fout, int *numForeignServers) { + DumpOptions *dopt = fout->dopt; PGresult *res; int ntups; int i; @@ -7846,7 +7946,7 @@ getForeignServers(Archive *fout, int *numForeignServers) 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); @@ -7934,7 +8034,7 @@ getDefaultACLs(Archive *fout, int *numDefaultACLs) 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); @@ -9898,32 +9998,6 @@ dumpShellType(Archive *fout, ShellTypeInfo *stinfo) 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 @@ -9975,25 +10049,13 @@ dumpProcLang(Archive *fout, ProcLangInfo *plang) /* * 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(); @@ -13062,14 +13124,6 @@ dumpForeignDataWrapper(Archive *fout, FdwInfo *fdwinfo) 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(); @@ -13145,7 +13199,7 @@ dumpForeignServer(Archive *fout, ForeignServerInfo *srvinfo) 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(); @@ -15695,50 +15749,27 @@ 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 - * 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) @@ -15751,11 +15782,11 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], /* 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); @@ -15763,76 +15794,94 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], 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); - - 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); - - dobj->ext_member = true; - - /* - * 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; + extmembers[nextmembers].catId = objId; + extmembers[nextmembers].ext = ext; + nextmembers++; } PQclear(res); + /* Remember the data for use later */ + setExtensionMembership(extmembers, nextmembers); + + destroyPQExpBuffer(query); +} + +/* + * 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; + + /* 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. * @@ -15920,11 +15969,17 @@ getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], /* * 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 " diff --git a/src/bin/pg_dump/pg_dump.h b/src/bin/pg_dump/pg_dump.h index ec83d02345..78b2584ce6 100644 --- a/src/bin/pg_dump/pg_dump.h +++ b/src/bin/pg_dump/pg_dump.h @@ -479,6 +479,16 @@ typedef struct _policyInfo char *polwithcheck; } PolicyInfo; +/* + * We build an array of these with an entry for each object that is an + * extension member according to pg_depend. + */ +typedef struct _extensionMemberId +{ + CatalogId catId; /* tableoid+oid of some member object */ + ExtensionInfo *ext; /* owning extension */ +} ExtensionMemberId; + /* global decls */ extern bool force_quotes; /* double-quotes for identifiers flag */ extern bool g_verbose; /* verbose flag */ @@ -511,6 +521,10 @@ extern FuncInfo *findFuncByOid(Oid oid); extern OprInfo *findOprByOid(Oid oid); extern CollInfo *findCollationByOid(Oid oid); extern NamespaceInfo *findNamespaceByOid(Oid oid); +extern ExtensionInfo *findExtensionByOid(Oid oid); + +extern void setExtensionMembership(ExtensionMemberId *extmems, int nextmems); +extern ExtensionInfo *findOwningExtension(CatalogId catalogId); extern void simple_oid_list_append(SimpleOidList *list, Oid val); extern bool simple_oid_list_member(SimpleOidList *list, Oid val); @@ -559,6 +573,8 @@ extern ForeignServerInfo *getForeignServers(Archive *fout, extern DefaultACLInfo *getDefaultACLs(Archive *fout, int *numDefaultACLs); extern void getExtensionMembership(Archive *fout, ExtensionInfo extinfo[], int numExtensions); +extern void processExtensionTables(Archive *fout, ExtensionInfo extinfo[], + int numExtensions); extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers); extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);