]> granicus.if.org Git - postgresql/commitdiff
Handle extension members when first setting object dump flags in pg_dump.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 13 Jan 2016 23:55:27 +0000 (18:55 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 13 Jan 2016 23:55:27 +0000 (18:55 -0500)
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
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h

index 3b4e478ad94ae0f65de1963faa7083e8d44fcb9f..f798b15e3c47ee6edf1902f9f6c7701c655ea43b 100644 (file)
@@ -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.)
- */
-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);
@@ -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
index 1408e761f70f4bf8d10ca1552ba362d03265e06b..9196cf44d981227801990bbcc8ab82bb4cff36a4 100644 (file)
@@ -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);
+               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.
         *
@@ -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 "
index ec83d02345315ceeef6543b1072f5bd171dde305..78b2584ce6299c2a8b70ebb0db45ec6086262a45 100644 (file)
@@ -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);