Re: pg_dump and premature optimizations for objects not to be dumped

Поиск
Список
Период
Сортировка
От Tom Lane
Тема Re: pg_dump and premature optimizations for objects not to be dumped
Дата
Msg-id 17765.1452644414@sss.pgh.pa.us
обсуждение исходный текст
Ответ на pg_dump and premature optimizations for objects not to be dumped  (Tom Lane <tgl@sss.pgh.pa.us>)
Ответы Re: pg_dump and premature optimizations for objects not to be dumped  (Tom Lane <tgl@sss.pgh.pa.us>)
Список pgsql-hackers
I wrote:
> I looked into Karsten Hilbert's report here
> http://www.postgresql.org/message-id/20160108114529.GB22446@hermes.hilbert.loc
> of being unable to run pg_upgrade on a database containing the pg_trgm
> extension.  After some investigation, the problem is explained like this:
> ...
> The thing that seems possibly most robust to me is to pull in the
> extension membership data *first* and incorporate it into the initial
> selectDumpableObject tests, thus getting us back to the pre-9.1 state
> of affairs where the initial settings of the object dump flags could
> be trusted.  This might be expensive though; and it would certainly add
> a good chunk of work to the race-condition window where we've taken
> pg_dump's transaction snapshot but not yet acquired lock on any of
> the tables.

Attached is a draft patch that does things this way.  Some simple testing
suggests it's just about exactly the same speed as 9.5 pg_dump, which
means that the "expensive" objection is dismissible.  It's hard to tell
though whether the pre-table-lock race-condition window has gotten
meaningfully wider.

In addition to fixing Karsten's problem, this deals with the bugs I noted
earlier about event triggers and transforms being dumped unconditionally.

I was also able to get rid of some unsightly special cases for procedural
languages, FDWs, and foreign servers, in favor of setting their dump
flags according to standard rules up front.

If we were to put a test rejecting initdb-created objects (those with
OID < FirstNormalObjectId) into selectDumpableObject, it'd be possible
to get rid of selectDumpableDefaultACL, selectDumpableCast, and/or
selectDumpableProcLang, since those would then have various subsets
of the selectDumpableObject rules.  I'm not sure if this would be an
improvement or just confusing; any thoughts?

I'm not very sure what to do with this patch.  I think it should
definitely go into 9.5: it applies cleanly there and it will fix our two
new-in-9.5 bugs with event triggers and transforms.  I'm less enthused
about back-porting it further.  In principle, the extension membership
issues exist all the way back to 9.1, but we haven't had complaints before,
and there's a nonzero chance of changing corner-case behaviors.  (I think
any such changes would likely be for the better, but nonetheless they
would be changes.)  Back-porting it further than about 9.4 would also
be quite a lot of work :-(

Comments?

            regards, tom lane

diff --git a/src/bin/pg_dump/common.c b/src/bin/pg_dump/common.c
index 7869ee9..ded14f3 100644
*** a/src/bin/pg_dump/common.c
--- b/src/bin/pg_dump/common.c
*************** static int    numCatalogIds = 0;
*** 40,69 ****

  /*
   * 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;
  static DumpableObject **tblinfoindex;
  static DumpableObject **typinfoindex;
  static DumpableObject **funinfoindex;
  static DumpableObject **oprinfoindex;
  static DumpableObject **collinfoindex;
  static DumpableObject **nspinfoindex;


  static void flagInhTables(TableInfo *tbinfo, int numTables,
                InhInfo *inhinfo, int numInherits);
--- 40,69 ----

  /*
   * 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 the objects are fetched,
!  * and then we use binary search in findTableByOid() and friends.  (qsort'ing
!  * the object arrays themselves would be simpler, but it doesn't work because
!  * pg_dump.c may have already established pointers between items.)
   */
  static DumpableObject **tblinfoindex;
  static DumpableObject **typinfoindex;
  static DumpableObject **funinfoindex;
  static DumpableObject **oprinfoindex;
  static DumpableObject **collinfoindex;
  static DumpableObject **nspinfoindex;
+ static DumpableObject **extinfoindex;
+ static int    numTables;
+ static int    numTypes;
+ static int    numFuncs;
+ static int    numOperators;
+ static int    numCollations;
+ static int    numNamespaces;
+ static int    numExtensions;

+ /* This is an array of object identities, not actual DumpableObjects */
+ static ExtensionMemberId *extmembers;
+ static int    numextmembers;

  static void flagInhTables(TableInfo *tbinfo, int numTables,
                InhInfo *inhinfo, int numInherits);
*************** static void flagInhAttrs(DumpOptions *do
*** 71,76 ****
--- 71,77 ----
  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);
*************** static int    strInArray(const char *patter
*** 83,92 ****
  TableInfo *
  getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
  {
      ExtensionInfo *extinfo;
      InhInfo    *inhinfo;
-     CollInfo   *collinfo;
-     int            numExtensions;
      int            numAggregates;
      int            numInherits;
      int            numRules;
--- 84,97 ----
  TableInfo *
  getSchemaData(Archive *fout, DumpOptions *dopt, int *numTablesPtr)
  {
+     TableInfo  *tblinfo;
+     TypeInfo   *typinfo;
+     FuncInfo   *funinfo;
+     OprInfo    *oprinfo;
+     CollInfo   *collinfo;
+     NamespaceInfo *nspinfo;
      ExtensionInfo *extinfo;
      InhInfo    *inhinfo;
      int            numAggregates;
      int            numInherits;
      int            numRules;
*************** getSchemaData(Archive *fout, DumpOptions
*** 105,113 ****
      int            numDefaultACLs;
      int            numEventTriggers;

      if (g_verbose)
          write_msg(NULL, "reading schemas\n");
!     nspinfo = getNamespaces(fout, &numNamespaces);
      nspinfoindex = buildIndexArray(nspinfo, numNamespaces, sizeof(NamespaceInfo));

      /*
--- 110,132 ----
      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, dopt, &numExtensions);
+     extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo));
+
+     if (g_verbose)
+         write_msg(NULL, "identifying extension members\n");
+     getExtensionMembership(fout, dopt, extinfo, numExtensions);
+
      if (g_verbose)
          write_msg(NULL, "reading schemas\n");
!     nspinfo = getNamespaces(fout, dopt, &numNamespaces);
      nspinfoindex = buildIndexArray(nspinfo, numNamespaces, sizeof(NamespaceInfo));

      /*
*************** getSchemaData(Archive *fout, DumpOptions
*** 125,134 ****
      getOwnedSeqs(fout, tblinfo, numTables);

      if (g_verbose)
-         write_msg(NULL, "reading extensions\n");
-     extinfo = getExtensions(fout, dopt, &numExtensions);
-
-     if (g_verbose)
          write_msg(NULL, "reading user-defined functions\n");
      funinfo = getFuncs(fout, dopt, &numFuncs);
      funinfoindex = buildIndexArray(funinfo, numFuncs, sizeof(FuncInfo));
--- 144,149 ----
*************** getSchemaData(Archive *fout, DumpOptions
*** 136,148 ****
      /* this must be after getTables and getFuncs */
      if (g_verbose)
          write_msg(NULL, "reading user-defined types\n");
!     typinfo = getTypes(fout, &numTypes);
      typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo));

      /* this must be after getFuncs, too */
      if (g_verbose)
          write_msg(NULL, "reading procedural languages\n");
!     getProcLangs(fout, &numProcLangs);

      if (g_verbose)
          write_msg(NULL, "reading user-defined aggregate functions\n");
--- 151,163 ----
      /* this must be after getTables and getFuncs */
      if (g_verbose)
          write_msg(NULL, "reading user-defined types\n");
!     typinfo = getTypes(fout, dopt, &numTypes);
      typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo));

      /* this must be after getFuncs, too */
      if (g_verbose)
          write_msg(NULL, "reading procedural languages\n");
!     getProcLangs(fout, dopt, &numProcLangs);

      if (g_verbose)
          write_msg(NULL, "reading user-defined aggregate functions\n");
*************** getSchemaData(Archive *fout, DumpOptions
*** 150,189 ****

      if (g_verbose)
          write_msg(NULL, "reading user-defined operators\n");
!     oprinfo = getOperators(fout, &numOperators);
      oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));

      if (g_verbose)
          write_msg(NULL, "reading user-defined operator classes\n");
!     getOpclasses(fout, &numOpclasses);

      if (g_verbose)
          write_msg(NULL, "reading user-defined operator families\n");
!     getOpfamilies(fout, &numOpfamilies);

      if (g_verbose)
          write_msg(NULL, "reading user-defined text search parsers\n");
!     getTSParsers(fout, &numTSParsers);

      if (g_verbose)
          write_msg(NULL, "reading user-defined text search templates\n");
!     getTSTemplates(fout, &numTSTemplates);

      if (g_verbose)
          write_msg(NULL, "reading user-defined text search dictionaries\n");
!     getTSDictionaries(fout, &numTSDicts);

      if (g_verbose)
          write_msg(NULL, "reading user-defined text search configurations\n");
!     getTSConfigurations(fout, &numTSConfigs);

      if (g_verbose)
          write_msg(NULL, "reading user-defined foreign-data wrappers\n");
!     getForeignDataWrappers(fout, &numForeignDataWrappers);

      if (g_verbose)
          write_msg(NULL, "reading user-defined foreign servers\n");
!     getForeignServers(fout, &numForeignServers);

      if (g_verbose)
          write_msg(NULL, "reading default privileges\n");
--- 165,204 ----

      if (g_verbose)
          write_msg(NULL, "reading user-defined operators\n");
!     oprinfo = getOperators(fout, dopt, &numOperators);
      oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));

      if (g_verbose)
          write_msg(NULL, "reading user-defined operator classes\n");
!     getOpclasses(fout, dopt, &numOpclasses);

      if (g_verbose)
          write_msg(NULL, "reading user-defined operator families\n");
!     getOpfamilies(fout, dopt, &numOpfamilies);

      if (g_verbose)
          write_msg(NULL, "reading user-defined text search parsers\n");
!     getTSParsers(fout, dopt, &numTSParsers);

      if (g_verbose)
          write_msg(NULL, "reading user-defined text search templates\n");
!     getTSTemplates(fout, dopt, &numTSTemplates);

      if (g_verbose)
          write_msg(NULL, "reading user-defined text search dictionaries\n");
!     getTSDictionaries(fout, dopt, &numTSDicts);

      if (g_verbose)
          write_msg(NULL, "reading user-defined text search configurations\n");
!     getTSConfigurations(fout, dopt, &numTSConfigs);

      if (g_verbose)
          write_msg(NULL, "reading user-defined foreign-data wrappers\n");
!     getForeignDataWrappers(fout, dopt, &numForeignDataWrappers);

      if (g_verbose)
          write_msg(NULL, "reading user-defined foreign servers\n");
!     getForeignServers(fout, dopt, &numForeignServers);

      if (g_verbose)
          write_msg(NULL, "reading default privileges\n");
*************** getSchemaData(Archive *fout, DumpOptions
*** 191,202 ****

      if (g_verbose)
          write_msg(NULL, "reading user-defined collations\n");
!     collinfo = getCollations(fout, &numCollations);
      collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo));

      if (g_verbose)
          write_msg(NULL, "reading user-defined conversions\n");
!     getConversions(fout, &numConversions);

      if (g_verbose)
          write_msg(NULL, "reading type casts\n");
--- 206,217 ----

      if (g_verbose)
          write_msg(NULL, "reading user-defined collations\n");
!     collinfo = getCollations(fout, dopt, &numCollations);
      collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo));

      if (g_verbose)
          write_msg(NULL, "reading user-defined conversions\n");
!     getConversions(fout, dopt, &numConversions);

      if (g_verbose)
          write_msg(NULL, "reading type casts\n");
*************** getSchemaData(Archive *fout, DumpOptions
*** 204,210 ****

      if (g_verbose)
          write_msg(NULL, "reading transforms\n");
!     getTransforms(fout, &numTransforms);

      if (g_verbose)
          write_msg(NULL, "reading table inheritance information\n");
--- 219,225 ----

      if (g_verbose)
          write_msg(NULL, "reading transforms\n");
!     getTransforms(fout, dopt, &numTransforms);

      if (g_verbose)
          write_msg(NULL, "reading table inheritance information\n");
*************** getSchemaData(Archive *fout, DumpOptions
*** 212,227 ****

      if (g_verbose)
          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.
!      */
      if (g_verbose)
!         write_msg(NULL, "finding extension members\n");
!     getExtensionMembership(fout, dopt, extinfo, numExtensions);

      /* Link tables to parents, mark parents of target tables interesting */
      if (g_verbose)
--- 227,238 ----

      if (g_verbose)
          write_msg(NULL, "reading event triggers\n");
!     getEventTriggers(fout, dopt, &numEventTriggers);

!     /* Identify extension configuration tables that should be dumped */
      if (g_verbose)
!         write_msg(NULL, "finding extension tables\n");
!     processExtensionTables(fout, dopt, extinfo, numExtensions);

      /* Link tables to parents, mark parents of target tables interesting */
      if (g_verbose)
*************** findNamespaceByOid(Oid oid)
*** 764,769 ****
--- 775,867 ----
      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 56c0528..ba14fe1 100644
*** a/src/bin/pg_dump/pg_dump.c
--- b/src/bin/pg_dump/pg_dump.c
*************** expand_table_name_patterns(Archive *fout
*** 1250,1261 ****
  }

  /*
   * selectDumpableNamespace: policy-setting subroutine
   *        Mark a namespace as to be dumped or not
   */
  static void
! selectDumpableNamespace(NamespaceInfo *nsinfo)
  {
      /*
       * If specific tables are being dumped, do not dump any complete
       * namespaces. If specific namespaces are being dumped, dump just those
--- 1250,1302 ----
  }

  /*
+  * 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, 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
*************** selectDumpableNamespace(NamespaceInfo *n
*** 1286,1293 ****
   *        Mark a table as to be dumped or not
   */
  static void
! selectDumpableTable(TableInfo *tbinfo)
  {
      /*
       * If specific tables are being dumped, dump just those tables; else, dump
       * according to the parent namespace's dump flag.
--- 1327,1337 ----
   *        Mark a table as to be dumped or not
   */
  static void
! 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.
*************** selectDumpableTable(TableInfo *tbinfo)
*** 1321,1327 ****
   * object (the table or base type).
   */
  static void
! selectDumpableType(TypeInfo *tyinfo)
  {
      /* skip complex types, except for standalone composite types */
      if (OidIsValid(tyinfo->typrelid) &&
--- 1365,1371 ----
   * object (the table or base type).
   */
  static void
! selectDumpableType(TypeInfo *tyinfo, DumpOptions *dopt)
  {
      /* skip complex types, except for standalone composite types */
      if (OidIsValid(tyinfo->typrelid) &&
*************** selectDumpableType(TypeInfo *tyinfo)
*** 1350,1355 ****
--- 1394,1402 ----
           */
      }

+     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;
*************** selectDumpableType(TypeInfo *tyinfo)
*** 1366,1373 ****
   * and aclsSkip are checked separately.
   */
  static void
! selectDumpableDefaultACL(DumpOptions *dopt, DefaultACLInfo *dinfo)
  {
      if (dinfo->dobj.namespace)
          dinfo->dobj.dump = dinfo->dobj.namespace->dobj.dump;
      else
--- 1413,1422 ----
   * and aclsSkip are checked separately.
   */
  static void
! 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
*************** selectDumpableDefaultACL(DumpOptions *do
*** 1384,1391 ****
   * OID is in the range reserved for initdb.
   */
  static void
! selectDumpableCast(DumpOptions *dopt, CastInfo *cast)
  {
      if (cast->dobj.catId.oid < (Oid) FirstNormalObjectId)
          cast->dobj.dump = false;
      else
--- 1433,1443 ----
   * OID is in the range reserved for initdb.
   */
  static void
! 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
*************** selectDumpableCast(DumpOptions *dopt, Ca
*** 1393,1398 ****
--- 1445,1470 ----
  }

  /*
+  * 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
   *
*************** selectDumpableCast(DumpOptions *dopt, Ca
*** 1403,1409 ****
   * such extensions by their having OIDs in the range reserved for initdb.
   */
  static void
! selectDumpableExtension(DumpOptions *dopt, ExtensionInfo *extinfo)
  {
      if (dopt->binary_upgrade && extinfo->dobj.catId.oid < (Oid) FirstNormalObjectId)
          extinfo->dobj.dump = false;
--- 1475,1481 ----
   * such extensions by their having OIDs in the range reserved for initdb.
   */
  static void
! selectDumpableExtension(ExtensionInfo *extinfo, DumpOptions *dopt)
  {
      if (dopt->binary_upgrade && extinfo->dobj.catId.oid < (Oid) FirstNormalObjectId)
          extinfo->dobj.dump = false;
*************** selectDumpableExtension(DumpOptions *dop
*** 1418,1433 ****
   * Use this only for object types without a special-case routine above.
   */
  static void
! selectDumpableObject(DumpableObject *dobj)
  {
      /*
!      * Default policy is to dump if parent namespace is dumpable, or always
!      * for non-namespace-associated items.
       */
      if (dobj->namespace)
          dobj->dump = dobj->namespace->dobj.dump;
      else
!         dobj->dump = true;
  }

  /*
--- 1490,1508 ----
   * Use this only for object types without a special-case routine above.
   */
  static void
! 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 for
!      * non-namespace-associated items, dump if we're dumping "everything".
       */
      if (dobj->namespace)
          dobj->dump = dobj->namespace->dobj.dump;
      else
!         dobj->dump = dopt->include_everything;
  }

  /*
*************** binary_upgrade_extension_member(PQExpBuf
*** 3248,3254 ****
   *    numNamespaces is set to the number of namespaces read in
   */
  NamespaceInfo *
! getNamespaces(Archive *fout, int *numNamespaces)
  {
      PGresult   *res;
      int            ntups;
--- 3323,3329 ----
   *    numNamespaces is set to the number of namespaces read in
   */
  NamespaceInfo *
! getNamespaces(Archive *fout, DumpOptions *dopt, int *numNamespaces)
  {
      PGresult   *res;
      int            ntups;
*************** getNamespaces(Archive *fout, int *numNam
*** 3277,3283 ****
          nsinfo[0].rolname = pg_strdup("");
          nsinfo[0].nspacl = pg_strdup("");

!         selectDumpableNamespace(&nsinfo[0]);

          nsinfo[1].dobj.objType = DO_NAMESPACE;
          nsinfo[1].dobj.catId.tableoid = 0;
--- 3352,3358 ----
          nsinfo[0].rolname = pg_strdup("");
          nsinfo[0].nspacl = pg_strdup("");

!         selectDumpableNamespace(&nsinfo[0], dopt);

          nsinfo[1].dobj.objType = DO_NAMESPACE;
          nsinfo[1].dobj.catId.tableoid = 0;
*************** getNamespaces(Archive *fout, int *numNam
*** 3287,3293 ****
          nsinfo[1].rolname = pg_strdup("");
          nsinfo[1].nspacl = pg_strdup("");

!         selectDumpableNamespace(&nsinfo[1]);

          *numNamespaces = 2;

--- 3362,3368 ----
          nsinfo[1].rolname = pg_strdup("");
          nsinfo[1].nspacl = pg_strdup("");

!         selectDumpableNamespace(&nsinfo[1], dopt);

          *numNamespaces = 2;

*************** getNamespaces(Archive *fout, int *numNam
*** 3331,3337 ****
          nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl));

          /* Decide whether to dump this namespace */
!         selectDumpableNamespace(&nsinfo[i]);

          if (strlen(nsinfo[i].rolname) == 0)
              write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n",
--- 3406,3412 ----
          nsinfo[i].nspacl = pg_strdup(PQgetvalue(res, i, i_nspacl));

          /* Decide whether to dump this namespace */
!         selectDumpableNamespace(&nsinfo[i], dopt);

          if (strlen(nsinfo[i].rolname) == 0)
              write_msg(NULL, "WARNING: owner of schema \"%s\" appears to be invalid\n",
*************** getExtensions(Archive *fout, DumpOptions
*** 3454,3460 ****
          extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));

          /* Decide whether we want to dump it */
!         selectDumpableExtension(dopt, &(extinfo[i]));
      }

      PQclear(res);
--- 3529,3535 ----
          extinfo[i].extcondition = pg_strdup(PQgetvalue(res, i, i_extcondition));

          /* Decide whether we want to dump it */
!         selectDumpableExtension(&(extinfo[i]), dopt);
      }

      PQclear(res);
*************** getExtensions(Archive *fout, DumpOptions
*** 3476,3482 ****
   * findFuncByOid().
   */
  TypeInfo *
! getTypes(Archive *fout, int *numTypes)
  {
      PGresult   *res;
      int            ntups;
--- 3551,3557 ----
   * findFuncByOid().
   */
  TypeInfo *
! getTypes(Archive *fout, DumpOptions *dopt, int *numTypes)
  {
      PGresult   *res;
      int            ntups;
*************** getTypes(Archive *fout, int *numTypes)
*** 3644,3650 ****
              tyinfo[i].isArray = false;

          /* Decide whether we want to dump it */
!         selectDumpableType(&tyinfo[i]);

          /*
           * If it's a domain, fetch info about its constraints, if any
--- 3719,3725 ----
              tyinfo[i].isArray = false;

          /* Decide whether we want to dump it */
!         selectDumpableType(&tyinfo[i], dopt);

          /*
           * If it's a domain, fetch info about its constraints, if any
*************** getTypes(Archive *fout, int *numTypes)
*** 3748,3754 ****
   *    numOprs is set to the number of operators read in
   */
  OprInfo *
! getOperators(Archive *fout, int *numOprs)
  {
      PGresult   *res;
      int            ntups;
--- 3823,3829 ----
   *    numOprs is set to the number of operators read in
   */
  OprInfo *
! getOperators(Archive *fout, DumpOptions *dopt, int *numOprs)
  {
      PGresult   *res;
      int            ntups;
*************** getOperators(Archive *fout, int *numOprs
*** 3835,3841 ****
          oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(oprinfo[i].dobj));

          if (strlen(oprinfo[i].rolname) == 0)
              write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
--- 3910,3916 ----
          oprinfo[i].oprcode = atooid(PQgetvalue(res, i, i_oprcode));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(oprinfo[i].dobj), dopt);

          if (strlen(oprinfo[i].rolname) == 0)
              write_msg(NULL, "WARNING: owner of operator \"%s\" appears to be invalid\n",
*************** getOperators(Archive *fout, int *numOprs
*** 3857,3863 ****
   *    numCollations is set to the number of collations read in
   */
  CollInfo *
! getCollations(Archive *fout, int *numCollations)
  {
      PGresult   *res;
      int            ntups;
--- 3932,3938 ----
   *    numCollations is set to the number of collations read in
   */
  CollInfo *
! getCollations(Archive *fout, DumpOptions *dopt, int *numCollations)
  {
      PGresult   *res;
      int            ntups;
*************** getCollations(Archive *fout, int *numCol
*** 3920,3926 ****
          collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(collinfo[i].dobj));
      }

      PQclear(res);
--- 3995,4001 ----
          collinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(collinfo[i].dobj), dopt);
      }

      PQclear(res);
*************** getCollations(Archive *fout, int *numCol
*** 3938,3944 ****
   *    numConversions is set to the number of conversions read in
   */
  ConvInfo *
! getConversions(Archive *fout, int *numConversions)
  {
      PGresult   *res;
      int            ntups;
--- 4013,4019 ----
   *    numConversions is set to the number of conversions read in
   */
  ConvInfo *
! getConversions(Archive *fout, DumpOptions *dopt, int *numConversions)
  {
      PGresult   *res;
      int            ntups;
*************** getConversions(Archive *fout, int *numCo
*** 4001,4007 ****
          convinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(convinfo[i].dobj));
      }

      PQclear(res);
--- 4076,4082 ----
          convinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(convinfo[i].dobj), dopt);
      }

      PQclear(res);
*************** getConversions(Archive *fout, int *numCo
*** 4019,4025 ****
   *    numOpclasses is set to the number of opclasses read in
   */
  OpclassInfo *
! getOpclasses(Archive *fout, int *numOpclasses)
  {
      PGresult   *res;
      int            ntups;
--- 4094,4100 ----
   *    numOpclasses is set to the number of opclasses read in
   */
  OpclassInfo *
! getOpclasses(Archive *fout, DumpOptions *dopt, int *numOpclasses)
  {
      PGresult   *res;
      int            ntups;
*************** getOpclasses(Archive *fout, int *numOpcl
*** 4092,4098 ****
          opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(opcinfo[i].dobj));

          if (fout->remoteVersion >= 70300)
          {
--- 4167,4173 ----
          opcinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(opcinfo[i].dobj), dopt);

          if (fout->remoteVersion >= 70300)
          {
*************** getOpclasses(Archive *fout, int *numOpcl
*** 4117,4123 ****
   *    numOpfamilies is set to the number of opfamilies read in
   */
  OpfamilyInfo *
! getOpfamilies(Archive *fout, int *numOpfamilies)
  {
      PGresult   *res;
      int            ntups;
--- 4192,4198 ----
   *    numOpfamilies is set to the number of opfamilies read in
   */
  OpfamilyInfo *
! getOpfamilies(Archive *fout, DumpOptions *dopt, int *numOpfamilies)
  {
      PGresult   *res;
      int            ntups;
*************** getOpfamilies(Archive *fout, int *numOpf
*** 4180,4186 ****
          opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(opfinfo[i].dobj));

          if (fout->remoteVersion >= 70300)
          {
--- 4255,4261 ----
          opfinfo[i].rolname = pg_strdup(PQgetvalue(res, i, i_rolname));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(opfinfo[i].dobj), dopt);

          if (fout->remoteVersion >= 70300)
          {
*************** getAggregates(Archive *fout, DumpOptions
*** 4344,4350 ****
          }

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(agginfo[i].aggfn.dobj));
      }

      PQclear(res);
--- 4419,4425 ----
          }

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(agginfo[i].aggfn.dobj), dopt);
      }

      PQclear(res);
*************** getFuncs(Archive *fout, DumpOptions *dop
*** 4501,4507 ****
          }

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(finfo[i].dobj));

          if (strlen(finfo[i].rolname) == 0)
              write_msg(NULL,
--- 4576,4582 ----
          }

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(finfo[i].dobj), dopt);

          if (strlen(finfo[i].rolname) == 0)
              write_msg(NULL,
*************** getTables(Archive *fout, DumpOptions *do
*** 5163,5169 ****
          if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
              tblinfo[i].dobj.dump = false;
          else
!             selectDumpableTable(&tblinfo[i]);
          tblinfo[i].interesting = tblinfo[i].dobj.dump;

          tblinfo[i].postponed_def = false;        /* might get set during sort */
--- 5238,5244 ----
          if (tblinfo[i].relkind == RELKIND_COMPOSITE_TYPE)
              tblinfo[i].dobj.dump = false;
          else
!             selectDumpableTable(&tblinfo[i], dopt);
          tblinfo[i].interesting = tblinfo[i].dobj.dump;

          tblinfo[i].postponed_def = false;        /* might get set during sort */
*************** getTriggers(Archive *fout, TableInfo tbl
*** 6241,6247 ****
   *      get information about event triggers
   */
  EventTriggerInfo *
! getEventTriggers(Archive *fout, int *numEventTriggers)
  {
      int            i;
      PQExpBuffer query;
--- 6316,6322 ----
   *      get information about event triggers
   */
  EventTriggerInfo *
! getEventTriggers(Archive *fout, DumpOptions *dopt, int *numEventTriggers)
  {
      int            i;
      PQExpBuffer query;
*************** getEventTriggers(Archive *fout, int *num
*** 6310,6315 ****
--- 6385,6393 ----
          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);
*************** getEventTriggers(Archive *fout, int *num
*** 6329,6335 ****
   * findFuncByOid().
   */
  ProcLangInfo *
! getProcLangs(Archive *fout, int *numProcLangs)
  {
      PGresult   *res;
      int            ntups;
--- 6407,6413 ----
   * findFuncByOid().
   */
  ProcLangInfo *
! getProcLangs(Archive *fout, DumpOptions *dopt, int *numProcLangs)
  {
      PGresult   *res;
      int            ntups;
*************** getProcLangs(Archive *fout, int *numProc
*** 6464,6469 ****
--- 6542,6550 ----
          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)
          {
              /*
*************** getCasts(Archive *fout, DumpOptions *dop
*** 6600,6606 ****
          }

          /* Decide whether we want to dump it */
!         selectDumpableCast(dopt, &(castinfo[i]));
      }

      PQclear(res);
--- 6681,6687 ----
          }

          /* Decide whether we want to dump it */
!         selectDumpableCast(&(castinfo[i]), dopt);
      }

      PQclear(res);
*************** get_language_name(Archive *fout, Oid lan
*** 6634,6640 ****
   * numTransforms is set to the number of transforms read in
   */
  TransformInfo *
! getTransforms(Archive *fout, int *numTransforms)
  {
      PGresult   *res;
      int            ntups;
--- 6715,6721 ----
   * numTransforms is set to the number of transforms read in
   */
  TransformInfo *
! getTransforms(Archive *fout, DumpOptions *dopt, int *numTransforms)
  {
      PGresult   *res;
      int            ntups;
*************** getTransforms(Archive *fout, int *numTra
*** 6708,6713 ****
--- 6789,6797 ----
                                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);
*************** shouldPrintColumn(DumpOptions *dopt, Tab
*** 7315,7321 ****
   *    numTSParsers is set to the number of parsers read in
   */
  TSParserInfo *
! getTSParsers(Archive *fout, int *numTSParsers)
  {
      PGresult   *res;
      int            ntups;
--- 7399,7405 ----
   *    numTSParsers is set to the number of parsers read in
   */
  TSParserInfo *
! getTSParsers(Archive *fout, DumpOptions *dopt, int *numTSParsers)
  {
      PGresult   *res;
      int            ntups;
*************** getTSParsers(Archive *fout, int *numTSPa
*** 7389,7395 ****
          prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(prsinfo[i].dobj));
      }

      PQclear(res);
--- 7473,7479 ----
          prsinfo[i].prslextype = atooid(PQgetvalue(res, i, i_prslextype));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(prsinfo[i].dobj), dopt);
      }

      PQclear(res);
*************** getTSParsers(Archive *fout, int *numTSPa
*** 7407,7413 ****
   *    numTSDicts is set to the number of dictionaries read in
   */
  TSDictInfo *
! getTSDictionaries(Archive *fout, int *numTSDicts)
  {
      PGresult   *res;
      int            ntups;
--- 7491,7497 ----
   *    numTSDicts is set to the number of dictionaries read in
   */
  TSDictInfo *
! getTSDictionaries(Archive *fout, DumpOptions *dopt, int *numTSDicts)
  {
      PGresult   *res;
      int            ntups;
*************** getTSDictionaries(Archive *fout, int *nu
*** 7474,7480 ****
              dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(dictinfo[i].dobj));
      }

      PQclear(res);
--- 7558,7564 ----
              dictinfo[i].dictinitoption = pg_strdup(PQgetvalue(res, i, i_dictinitoption));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(dictinfo[i].dobj), dopt);
      }

      PQclear(res);
*************** getTSDictionaries(Archive *fout, int *nu
*** 7492,7498 ****
   *    numTSTemplates is set to the number of templates read in
   */
  TSTemplateInfo *
! getTSTemplates(Archive *fout, int *numTSTemplates)
  {
      PGresult   *res;
      int            ntups;
--- 7576,7582 ----
   *    numTSTemplates is set to the number of templates read in
   */
  TSTemplateInfo *
! getTSTemplates(Archive *fout, DumpOptions *dopt, int *numTSTemplates)
  {
      PGresult   *res;
      int            ntups;
*************** getTSTemplates(Archive *fout, int *numTS
*** 7551,7557 ****
          tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(tmplinfo[i].dobj));
      }

      PQclear(res);
--- 7635,7641 ----
          tmplinfo[i].tmpllexize = atooid(PQgetvalue(res, i, i_tmpllexize));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(tmplinfo[i].dobj), dopt);
      }

      PQclear(res);
*************** getTSTemplates(Archive *fout, int *numTS
*** 7569,7575 ****
   *    numTSConfigs is set to the number of configurations read in
   */
  TSConfigInfo *
! getTSConfigurations(Archive *fout, int *numTSConfigs)
  {
      PGresult   *res;
      int            ntups;
--- 7653,7659 ----
   *    numTSConfigs is set to the number of configurations read in
   */
  TSConfigInfo *
! getTSConfigurations(Archive *fout, DumpOptions *dopt, int *numTSConfigs)
  {
      PGresult   *res;
      int            ntups;
*************** getTSConfigurations(Archive *fout, int *
*** 7629,7635 ****
          cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(cfginfo[i].dobj));
      }

      PQclear(res);
--- 7713,7719 ----
          cfginfo[i].cfgparser = atooid(PQgetvalue(res, i, i_cfgparser));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(cfginfo[i].dobj), dopt);
      }

      PQclear(res);
*************** getTSConfigurations(Archive *fout, int *
*** 7647,7653 ****
   *    numForeignDataWrappers is set to the number of fdws read in
   */
  FdwInfo *
! getForeignDataWrappers(Archive *fout, int *numForeignDataWrappers)
  {
      PGresult   *res;
      int            ntups;
--- 7731,7737 ----
   *    numForeignDataWrappers is set to the number of fdws read in
   */
  FdwInfo *
! getForeignDataWrappers(Archive *fout, DumpOptions *dopt, int *numForeignDataWrappers)
  {
      PGresult   *res;
      int            ntups;
*************** getForeignDataWrappers(Archive *fout, in
*** 7737,7743 ****
          fdwinfo[i].fdwacl = pg_strdup(PQgetvalue(res, i, i_fdwacl));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(fdwinfo[i].dobj));
      }

      PQclear(res);
--- 7821,7827 ----
          fdwinfo[i].fdwacl = pg_strdup(PQgetvalue(res, i, i_fdwacl));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(fdwinfo[i].dobj), dopt);
      }

      PQclear(res);
*************** getForeignDataWrappers(Archive *fout, in
*** 7755,7761 ****
   *    numForeignServers is set to the number of servers read in
   */
  ForeignServerInfo *
! getForeignServers(Archive *fout, int *numForeignServers)
  {
      PGresult   *res;
      int            ntups;
--- 7839,7845 ----
   *    numForeignServers is set to the number of servers read in
   */
  ForeignServerInfo *
! getForeignServers(Archive *fout, DumpOptions *dopt, int *numForeignServers)
  {
      PGresult   *res;
      int            ntups;
*************** getForeignServers(Archive *fout, int *nu
*** 7829,7835 ****
          srvinfo[i].srvacl = pg_strdup(PQgetvalue(res, i, i_srvacl));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(srvinfo[i].dobj));
      }

      PQclear(res);
--- 7913,7919 ----
          srvinfo[i].srvacl = pg_strdup(PQgetvalue(res, i, i_srvacl));

          /* Decide whether we want to dump it */
!         selectDumpableObject(&(srvinfo[i].dobj), dopt);
      }

      PQclear(res);
*************** getDefaultACLs(Archive *fout, DumpOption
*** 7916,7922 ****
          daclinfo[i].defaclacl = pg_strdup(PQgetvalue(res, i, i_defaclacl));

          /* Decide whether we want to dump it */
!         selectDumpableDefaultACL(dopt, &(daclinfo[i]));
      }

      PQclear(res);
--- 8000,8006 ----
          daclinfo[i].defaclacl = pg_strdup(PQgetvalue(res, i, i_defaclacl));

          /* Decide whether we want to dump it */
!         selectDumpableDefaultACL(&(daclinfo[i]), dopt);
      }

      PQclear(res);
*************** dumpShellType(Archive *fout, DumpOptions
*** 9868,9899 ****
  }

  /*
-  * 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
   *          procedural language
--- 9952,9957 ----
*************** dumpProcLang(Archive *fout, DumpOptions
*** 9943,9967 ****

      /*
       * 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.
       */
      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();
--- 10001,10013 ----

      /*
       * If the functions are dumpable then emit a traditional CREATE LANGUAGE
!      * 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)));

      defqry = createPQExpBuffer();
      delqry = createPQExpBuffer();
      labelq = createPQExpBuffer();
*************** dumpForeignDataWrapper(Archive *fout, Du
*** 13016,13029 ****
      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();
--- 13062,13067 ----
*************** dumpForeignServer(Archive *fout, DumpOpt
*** 13098,13104 ****
      char       *fdwname;

      /* Skip if not to be dumped */
!     if (!srvinfo->dobj.dump || dopt->dataOnly || !dopt->include_everything)
          return;

      q = createPQExpBuffer();
--- 13136,13142 ----
      char       *fdwname;

      /* Skip if not to be dumped */
!     if (!srvinfo->dobj.dump || dopt->dataOnly)
          return;

      q = createPQExpBuffer();
*************** dumpRule(Archive *fout, DumpOptions *dop
*** 15634,15682 ****
  /*
   * 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.
   */
  void
! getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionInfo extinfo[],
!                        int numExtensions)
  {
      PQExpBuffer query;
      PGresult   *res;
      int            ntups,
                  i;
      int            i_classid,
                  i_objid,
!                 i_refclassid,
!                 i_refobjid,
!                 i_conrelid,
!                 i_confrelid;
!     DumpableObject *dobj,
!                *refdobj;

      /* Nothing to do if no extensions */
      if (numExtensions == 0)
--- 15672,15698 ----
  /*
   * getExtensionMembership --- obtain extension membership data
   *
!  * 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, DumpOptions *dopt,
!                        ExtensionInfo extinfo[], int numExtensions)
  {
      PQExpBuffer query;
      PGresult   *res;
      int            ntups,
+                 nextmembers,
                  i;
      int            i_classid,
                  i_objid,
!                 i_refobjid;
!     ExtensionMemberId *extmembers;
!     ExtensionInfo *ext;

      /* Nothing to do if no extensions */
      if (numExtensions == 0)
*************** getExtensionMembership(Archive *fout, Du
*** 15689,15699 ****

      /* refclassid constraint is redundant but may speed the search */
      appendPQExpBufferStr(query, "SELECT "
!                          "classid, objid, refclassid, refobjid "
                           "FROM pg_depend "
                           "WHERE refclassid = 'pg_extension'::regclass "
                           "AND deptype = 'e' "
!                          "ORDER BY 3,4");

      res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

--- 15705,15715 ----

      /* refclassid constraint is redundant but may speed the search */
      appendPQExpBufferStr(query, "SELECT "
!                          "classid, objid, refobjid "
                           "FROM pg_depend "
                           "WHERE refclassid = 'pg_extension'::regclass "
                           "AND deptype = 'e' "
!                          "ORDER BY 3");

      res = ExecuteSqlQuery(fout, query->data, PGRES_TUPLES_OK);

*************** getExtensionMembership(Archive *fout, Du
*** 15701,15776 ****

      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);

!         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;
!     }

!     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.
       *
--- 15717,15809 ----

      i_classid = PQfnumber(res, "classid");
      i_objid = PQfnumber(res, "objid");
      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.
       */
!     ext = NULL;

      for (i = 0; i < ntups; i++)
      {
          CatalogId    objId;
!         Oid            extId;

          objId.tableoid = atooid(PQgetvalue(res, i, i_classid));
          objId.oid = atooid(PQgetvalue(res, i, i_objid));
!         extId = atooid(PQgetvalue(res, i, i_refobjid));

!         if (ext == NULL ||
!             ext->dobj.catId.oid != extId)
!             ext = findExtensionByOid(extId);

!         if (ext == NULL)
          {
!             /* shouldn't happen */
!             fprintf(stderr, "could not find referenced extension %u\n", extId);
              continue;
          }

!         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, DumpOptions *dopt,
!                        ExtensionInfo extinfo[], int numExtensions)
! {
!     PQExpBuffer query;
!     PGresult   *res;
!     int            ntups,
!                 i;
!     int            i_conrelid,
!                 i_confrelid;

!     /* Nothing to do if no extensions */
!     if (numExtensions == 0)
!         return;

      /*
!      * Identify extension configuration tables and create TableDataInfo
       * objects for them, ensuring their data will be dumped even though the
       * tables themselves won't be.
       *
*************** getExtensionMembership(Archive *fout, Du
*** 15858,15868 ****
      /*
       * 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.
       *
       * Note that this is not a problem for user tables as their FKs are
       * recreated after the data has been loaded.
       */
      printfPQExpBuffer(query,
                        "SELECT conrelid, confrelid "
                        "FROM pg_constraint "
--- 15891,15907 ----
      /*
       * 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 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 ba37c4c..6b0af59 100644
*** a/src/bin/pg_dump/pg_dump.h
--- b/src/bin/pg_dump/pg_dump.h
*************** typedef struct _policyInfo
*** 479,484 ****
--- 479,494 ----
      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 */
*************** extern FuncInfo *findFuncByOid(Oid oid);
*** 511,516 ****
--- 521,530 ----
  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);
*************** extern void sortDataAndIndexObjectsBySiz
*** 526,541 ****
  /*
   * version specific routines
   */
! extern NamespaceInfo *getNamespaces(Archive *fout, int *numNamespaces);
  extern ExtensionInfo *getExtensions(Archive *fout, DumpOptions *dopt, int *numExtensions);
! extern TypeInfo *getTypes(Archive *fout, int *numTypes);
  extern FuncInfo *getFuncs(Archive *fout, DumpOptions *dopt, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, DumpOptions *dopt, int *numAggregates);
! extern OprInfo *getOperators(Archive *fout, int *numOperators);
! extern OpclassInfo *getOpclasses(Archive *fout, int *numOpclasses);
! extern OpfamilyInfo *getOpfamilies(Archive *fout, int *numOpfamilies);
! extern CollInfo *getCollations(Archive *fout, int *numCollations);
! extern ConvInfo *getConversions(Archive *fout, int *numConversions);
  extern TableInfo *getTables(Archive *fout, DumpOptions *dopt, int *numTables);
  extern void getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables);
  extern InhInfo *getInherits(Archive *fout, int *numInherits);
--- 540,555 ----
  /*
   * version specific routines
   */
! extern NamespaceInfo *getNamespaces(Archive *fout, DumpOptions *dopt, int *numNamespaces);
  extern ExtensionInfo *getExtensions(Archive *fout, DumpOptions *dopt, int *numExtensions);
! extern TypeInfo *getTypes(Archive *fout, DumpOptions *dopt, int *numTypes);
  extern FuncInfo *getFuncs(Archive *fout, DumpOptions *dopt, int *numFuncs);
  extern AggInfo *getAggregates(Archive *fout, DumpOptions *dopt, int *numAggregates);
! extern OprInfo *getOperators(Archive *fout, DumpOptions *dopt, int *numOperators);
! extern OpclassInfo *getOpclasses(Archive *fout, DumpOptions *dopt, int *numOpclasses);
! extern OpfamilyInfo *getOpfamilies(Archive *fout, DumpOptions *dopt, int *numOpfamilies);
! extern CollInfo *getCollations(Archive *fout, DumpOptions *dopt, int *numCollations);
! extern ConvInfo *getConversions(Archive *fout, DumpOptions *dopt, int *numConversions);
  extern TableInfo *getTables(Archive *fout, DumpOptions *dopt, int *numTables);
  extern void getOwnedSeqs(Archive *fout, TableInfo tblinfo[], int numTables);
  extern InhInfo *getInherits(Archive *fout, int *numInherits);
*************** extern void getIndexes(Archive *fout, Ta
*** 543,565 ****
  extern void getConstraints(Archive *fout, TableInfo tblinfo[], int numTables);
  extern RuleInfo *getRules(Archive *fout, int *numRules);
  extern void getTriggers(Archive *fout, TableInfo tblinfo[], int numTables);
! extern ProcLangInfo *getProcLangs(Archive *fout, int *numProcLangs);
  extern CastInfo *getCasts(Archive *fout, DumpOptions *dopt, int *numCasts);
! extern TransformInfo *getTransforms(Archive *fout, int *numTransforms);
  extern void getTableAttrs(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo, int numTables);
  extern bool shouldPrintColumn(DumpOptions *dopt, TableInfo *tbinfo, int colno);
! extern TSParserInfo *getTSParsers(Archive *fout, int *numTSParsers);
! extern TSDictInfo *getTSDictionaries(Archive *fout, int *numTSDicts);
! extern TSTemplateInfo *getTSTemplates(Archive *fout, int *numTSTemplates);
! extern TSConfigInfo *getTSConfigurations(Archive *fout, int *numTSConfigs);
! extern FdwInfo *getForeignDataWrappers(Archive *fout,
                         int *numForeignDataWrappers);
! extern ForeignServerInfo *getForeignServers(Archive *fout,
                    int *numForeignServers);
  extern DefaultACLInfo *getDefaultACLs(Archive *fout, DumpOptions *dopt, int *numDefaultACLs);
! extern void getExtensionMembership(Archive *fout, DumpOptions *dopt, ExtensionInfo extinfo[],
!                        int numExtensions);
! extern EventTriggerInfo *getEventTriggers(Archive *fout, int *numEventTriggers);
  extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);

  #endif   /* PG_DUMP_H */
--- 557,581 ----
  extern void getConstraints(Archive *fout, TableInfo tblinfo[], int numTables);
  extern RuleInfo *getRules(Archive *fout, int *numRules);
  extern void getTriggers(Archive *fout, TableInfo tblinfo[], int numTables);
! extern ProcLangInfo *getProcLangs(Archive *fout, DumpOptions *dopt, int *numProcLangs);
  extern CastInfo *getCasts(Archive *fout, DumpOptions *dopt, int *numCasts);
! extern TransformInfo *getTransforms(Archive *fout, DumpOptions *dopt, int *numTransforms);
  extern void getTableAttrs(Archive *fout, DumpOptions *dopt, TableInfo *tbinfo, int numTables);
  extern bool shouldPrintColumn(DumpOptions *dopt, TableInfo *tbinfo, int colno);
! extern TSParserInfo *getTSParsers(Archive *fout, DumpOptions *dopt, int *numTSParsers);
! extern TSDictInfo *getTSDictionaries(Archive *fout, DumpOptions *dopt, int *numTSDicts);
! extern TSTemplateInfo *getTSTemplates(Archive *fout, DumpOptions *dopt, int *numTSTemplates);
! extern TSConfigInfo *getTSConfigurations(Archive *fout, DumpOptions *dopt, int *numTSConfigs);
! extern FdwInfo *getForeignDataWrappers(Archive *fout, DumpOptions *dopt,
                         int *numForeignDataWrappers);
! extern ForeignServerInfo *getForeignServers(Archive *fout, DumpOptions *dopt,
                    int *numForeignServers);
  extern DefaultACLInfo *getDefaultACLs(Archive *fout, DumpOptions *dopt, int *numDefaultACLs);
! extern void getExtensionMembership(Archive *fout, DumpOptions *dopt,
!                        ExtensionInfo extinfo[], int numExtensions);
! extern void processExtensionTables(Archive *fout, DumpOptions *dopt,
!                        ExtensionInfo extinfo[], int numExtensions);
! extern EventTriggerInfo *getEventTriggers(Archive *fout, DumpOptions *dopt, int *numEventTriggers);
  extern void getPolicies(Archive *fout, TableInfo tblinfo[], int numTables);

  #endif   /* PG_DUMP_H */

В списке pgsql-hackers по дате отправления:

Предыдущее
От: Tom Lane
Дата:
Сообщение: Re: Weird behavior during CREATE EXTENSION
Следующее
От: Dan Langille
Дата:
Сообщение: PGCon 2016 CFP - one week left