Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Rethink order of operations for dumping extension member objects.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 9 Feb 2011 19:05:34 +0000 (14:05 -0500)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 9 Feb 2011 19:05:34 +0000 (14:05 -0500)
My original idea of doing extension member identification during
getDependencies() didn't work correctly: we have to mark member tables as
not-to-be-dumped rather earlier than that, else their subsidiary objects
like indexes get dumped anyway.  Rearrange code to mark them early enough.

src/bin/pg_dump/common.c
src/bin/pg_dump/pg_dump.c
src/bin/pg_dump/pg_dump.h

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