LCOV - code coverage report
Current view: top level - src/bin/pg_dump - pg_dump_sort.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 61.3 % 643 394
Test Date: 2026-03-03 13:15:30 Functions: 87.0 % 23 20
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * pg_dump_sort.c
       4              :  *    Sort the items of a dump into a safe order for dumping
       5              :  *
       6              :  *
       7              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8              :  * Portions Copyright (c) 1994, Regents of the University of California
       9              :  *
      10              :  *
      11              :  * IDENTIFICATION
      12              :  *    src/bin/pg_dump/pg_dump_sort.c
      13              :  *
      14              :  *-------------------------------------------------------------------------
      15              :  */
      16              : #include "postgres_fe.h"
      17              : 
      18              : #include "catalog/pg_class_d.h"
      19              : #include "common/int.h"
      20              : #include "lib/binaryheap.h"
      21              : #include "pg_backup_utils.h"
      22              : #include "pg_dump.h"
      23              : 
      24              : /*
      25              :  * Sort priority for database object types.
      26              :  * Objects are sorted by type, and within a type by name.
      27              :  *
      28              :  * Triggers, event triggers, and materialized views are intentionally sorted
      29              :  * late.  Triggers must be restored after all data modifications, so that
      30              :  * they don't interfere with loading data.  Event triggers are restored
      31              :  * next-to-last so that they don't interfere with object creations of any
      32              :  * kind.  Matview refreshes are last because they should execute in the
      33              :  * database's normal state (e.g., they must come after all ACLs are restored;
      34              :  * also, if they choose to look at system catalogs, they should see the final
      35              :  * restore state).  If you think to change this, see also the RestorePass
      36              :  * mechanism in pg_backup_archiver.c.
      37              :  *
      38              :  * On the other hand, casts are intentionally sorted earlier than you might
      39              :  * expect; logically they should come after functions, since they usually
      40              :  * depend on those.  This works around the backend's habit of recording
      41              :  * views that use casts as dependent on the cast's underlying function.
      42              :  * We initially sort casts first, and then any functions used by casts
      43              :  * will be hoisted above the casts, and in turn views that those functions
      44              :  * depend on will be hoisted above the functions.  But views not used that
      45              :  * way won't be hoisted.
      46              :  *
      47              :  * NOTE: object-type priorities must match the section assignments made in
      48              :  * pg_dump.c; that is, PRE_DATA objects must sort before DO_PRE_DATA_BOUNDARY,
      49              :  * POST_DATA objects must sort after DO_POST_DATA_BOUNDARY, and DATA objects
      50              :  * must sort between them.
      51              :  */
      52              : 
      53              : /* This enum lists the priority levels in order */
      54              : enum dbObjectTypePriorities
      55              : {
      56              :     PRIO_NAMESPACE = 1,
      57              :     PRIO_PROCLANG,
      58              :     PRIO_COLLATION,
      59              :     PRIO_TRANSFORM,
      60              :     PRIO_EXTENSION,
      61              :     PRIO_TYPE,                  /* used for DO_TYPE and DO_SHELL_TYPE */
      62              :     PRIO_CAST,
      63              :     PRIO_FUNC,
      64              :     PRIO_AGG,
      65              :     PRIO_ACCESS_METHOD,
      66              :     PRIO_OPERATOR,
      67              :     PRIO_OPFAMILY,              /* used for DO_OPFAMILY and DO_OPCLASS */
      68              :     PRIO_CONVERSION,
      69              :     PRIO_TSPARSER,
      70              :     PRIO_TSTEMPLATE,
      71              :     PRIO_TSDICT,
      72              :     PRIO_TSCONFIG,
      73              :     PRIO_FDW,
      74              :     PRIO_FOREIGN_SERVER,
      75              :     PRIO_TABLE,
      76              :     PRIO_TABLE_ATTACH,
      77              :     PRIO_DUMMY_TYPE,
      78              :     PRIO_ATTRDEF,
      79              :     PRIO_PRE_DATA_BOUNDARY,     /* boundary! */
      80              :     PRIO_TABLE_DATA,
      81              :     PRIO_SEQUENCE_SET,
      82              :     PRIO_LARGE_OBJECT,
      83              :     PRIO_LARGE_OBJECT_DATA,
      84              :     PRIO_STATISTICS_DATA_DATA,
      85              :     PRIO_POST_DATA_BOUNDARY,    /* boundary! */
      86              :     PRIO_CONSTRAINT,
      87              :     PRIO_INDEX,
      88              :     PRIO_INDEX_ATTACH,
      89              :     PRIO_STATSEXT,
      90              :     PRIO_RULE,
      91              :     PRIO_TRIGGER,
      92              :     PRIO_FK_CONSTRAINT,
      93              :     PRIO_POLICY,
      94              :     PRIO_PUBLICATION,
      95              :     PRIO_PUBLICATION_REL,
      96              :     PRIO_PUBLICATION_TABLE_IN_SCHEMA,
      97              :     PRIO_SUBSCRIPTION,
      98              :     PRIO_SUBSCRIPTION_REL,
      99              :     PRIO_DEFAULT_ACL,           /* done in ACL pass */
     100              :     PRIO_EVENT_TRIGGER,         /* must be next to last! */
     101              :     PRIO_REFRESH_MATVIEW        /* must be last! */
     102              : };
     103              : 
     104              : /* This table is indexed by enum DumpableObjectType */
     105              : static const int dbObjectTypePriority[] =
     106              : {
     107              :     [DO_NAMESPACE] = PRIO_NAMESPACE,
     108              :     [DO_EXTENSION] = PRIO_EXTENSION,
     109              :     [DO_TYPE] = PRIO_TYPE,
     110              :     [DO_SHELL_TYPE] = PRIO_TYPE,
     111              :     [DO_FUNC] = PRIO_FUNC,
     112              :     [DO_AGG] = PRIO_AGG,
     113              :     [DO_OPERATOR] = PRIO_OPERATOR,
     114              :     [DO_ACCESS_METHOD] = PRIO_ACCESS_METHOD,
     115              :     [DO_OPCLASS] = PRIO_OPFAMILY,
     116              :     [DO_OPFAMILY] = PRIO_OPFAMILY,
     117              :     [DO_COLLATION] = PRIO_COLLATION,
     118              :     [DO_CONVERSION] = PRIO_CONVERSION,
     119              :     [DO_TABLE] = PRIO_TABLE,
     120              :     [DO_TABLE_ATTACH] = PRIO_TABLE_ATTACH,
     121              :     [DO_ATTRDEF] = PRIO_ATTRDEF,
     122              :     [DO_INDEX] = PRIO_INDEX,
     123              :     [DO_INDEX_ATTACH] = PRIO_INDEX_ATTACH,
     124              :     [DO_STATSEXT] = PRIO_STATSEXT,
     125              :     [DO_RULE] = PRIO_RULE,
     126              :     [DO_TRIGGER] = PRIO_TRIGGER,
     127              :     [DO_CONSTRAINT] = PRIO_CONSTRAINT,
     128              :     [DO_FK_CONSTRAINT] = PRIO_FK_CONSTRAINT,
     129              :     [DO_PROCLANG] = PRIO_PROCLANG,
     130              :     [DO_CAST] = PRIO_CAST,
     131              :     [DO_TABLE_DATA] = PRIO_TABLE_DATA,
     132              :     [DO_SEQUENCE_SET] = PRIO_SEQUENCE_SET,
     133              :     [DO_DUMMY_TYPE] = PRIO_DUMMY_TYPE,
     134              :     [DO_TSPARSER] = PRIO_TSPARSER,
     135              :     [DO_TSDICT] = PRIO_TSDICT,
     136              :     [DO_TSTEMPLATE] = PRIO_TSTEMPLATE,
     137              :     [DO_TSCONFIG] = PRIO_TSCONFIG,
     138              :     [DO_FDW] = PRIO_FDW,
     139              :     [DO_FOREIGN_SERVER] = PRIO_FOREIGN_SERVER,
     140              :     [DO_DEFAULT_ACL] = PRIO_DEFAULT_ACL,
     141              :     [DO_TRANSFORM] = PRIO_TRANSFORM,
     142              :     [DO_LARGE_OBJECT] = PRIO_LARGE_OBJECT,
     143              :     [DO_LARGE_OBJECT_DATA] = PRIO_LARGE_OBJECT_DATA,
     144              :     [DO_PRE_DATA_BOUNDARY] = PRIO_PRE_DATA_BOUNDARY,
     145              :     [DO_POST_DATA_BOUNDARY] = PRIO_POST_DATA_BOUNDARY,
     146              :     [DO_EVENT_TRIGGER] = PRIO_EVENT_TRIGGER,
     147              :     [DO_REFRESH_MATVIEW] = PRIO_REFRESH_MATVIEW,
     148              :     [DO_POLICY] = PRIO_POLICY,
     149              :     [DO_PUBLICATION] = PRIO_PUBLICATION,
     150              :     [DO_PUBLICATION_REL] = PRIO_PUBLICATION_REL,
     151              :     [DO_PUBLICATION_TABLE_IN_SCHEMA] = PRIO_PUBLICATION_TABLE_IN_SCHEMA,
     152              :     [DO_REL_STATS] = PRIO_STATISTICS_DATA_DATA,
     153              :     [DO_SUBSCRIPTION] = PRIO_SUBSCRIPTION,
     154              :     [DO_SUBSCRIPTION_REL] = PRIO_SUBSCRIPTION_REL,
     155              : };
     156              : 
     157              : StaticAssertDecl(lengthof(dbObjectTypePriority) == NUM_DUMPABLE_OBJECT_TYPES,
     158              :                  "array length mismatch");
     159              : 
     160              : static DumpId preDataBoundId;
     161              : static DumpId postDataBoundId;
     162              : 
     163              : 
     164              : static int  DOTypeNameCompare(const void *p1, const void *p2);
     165              : static int  pgTypeNameCompare(Oid typid1, Oid typid2);
     166              : static int  accessMethodNameCompare(Oid am1, Oid am2);
     167              : static bool TopoSort(DumpableObject **objs,
     168              :                      int numObjs,
     169              :                      DumpableObject **ordering,
     170              :                      int *nOrdering);
     171              : static void findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs);
     172              : static int  findLoop(DumpableObject *obj,
     173              :                      DumpId startPoint,
     174              :                      bool *processed,
     175              :                      DumpId *searchFailed,
     176              :                      DumpableObject **workspace,
     177              :                      int depth);
     178              : static void repairDependencyLoop(DumpableObject **loop,
     179              :                                  int nLoop);
     180              : static void describeDumpableObject(DumpableObject *obj,
     181              :                                    char *buf, int bufsize);
     182              : static int  int_cmp(void *a, void *b, void *arg);
     183              : 
     184              : 
     185              : /*
     186              :  * Sort the given objects into a type/name-based ordering
     187              :  *
     188              :  * Normally this is just the starting point for the dependency-based
     189              :  * ordering.
     190              :  */
     191              : void
     192          249 : sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
     193              : {
     194          249 :     if (numObjs > 1)
     195          249 :         qsort(objs, numObjs, sizeof(DumpableObject *),
     196              :               DOTypeNameCompare);
     197          249 : }
     198              : 
     199              : static int
     200     11516498 : DOTypeNameCompare(const void *p1, const void *p2)
     201              : {
     202     11516498 :     DumpableObject *obj1 = *(DumpableObject *const *) p1;
     203     11516498 :     DumpableObject *obj2 = *(DumpableObject *const *) p2;
     204              :     int         cmpval;
     205              : 
     206              :     /* Sort by type's priority */
     207     11516498 :     cmpval = dbObjectTypePriority[obj1->objType] -
     208     11516498 :         dbObjectTypePriority[obj2->objType];
     209              : 
     210     11516498 :     if (cmpval != 0)
     211      2808247 :         return cmpval;
     212              : 
     213              :     /*
     214              :      * Sort by namespace.  Typically, all objects of the same priority would
     215              :      * either have or not have a namespace link, but there are exceptions.
     216              :      * Sort NULL namespace after non-NULL in such cases.
     217              :      */
     218      8708251 :     if (obj1->namespace)
     219              :     {
     220      8204491 :         if (obj2->namespace)
     221              :         {
     222      8204422 :             cmpval = strcmp(obj1->namespace->dobj.name,
     223      8204422 :                             obj2->namespace->dobj.name);
     224      8204422 :             if (cmpval != 0)
     225       374647 :                 return cmpval;
     226              :         }
     227              :         else
     228           69 :             return -1;
     229              :     }
     230       503760 :     else if (obj2->namespace)
     231           39 :         return 1;
     232              : 
     233              :     /*
     234              :      * Sort by name.  With a few exceptions, names here are single catalog
     235              :      * columns.  To get a fuller picture, grep pg_dump.c for "dobj.name = ".
     236              :      * Names here don't match "Name:" in plain format output, which is a
     237              :      * _tocEntry.tag.  For example, DumpableObject.name of a constraint is
     238              :      * pg_constraint.conname, but _tocEntry.tag of a constraint is relname and
     239              :      * conname joined with a space.
     240              :      */
     241      8333496 :     cmpval = strcmp(obj1->name, obj2->name);
     242      8333496 :     if (cmpval != 0)
     243      7085209 :         return cmpval;
     244              : 
     245              :     /*
     246              :      * Sort by type.  This helps types that share a type priority without
     247              :      * sharing a unique name constraint, e.g. opclass and opfamily.
     248              :      */
     249      1248287 :     cmpval = obj1->objType - obj2->objType;
     250      1248287 :     if (cmpval != 0)
     251        48296 :         return cmpval;
     252              : 
     253              :     /*
     254              :      * To have a stable sort order, break ties for some object types.  Most
     255              :      * catalogs have a natural key, e.g. pg_proc_proname_args_nsp_index. Where
     256              :      * the above "namespace" and "name" comparisons don't cover all natural
     257              :      * key columns, compare the rest here.
     258              :      *
     259              :      * The natural key usually refers to other catalogs by surrogate keys.
     260              :      * Hence, this translates each of those references to the natural key of
     261              :      * the referenced catalog.  That may descend through multiple levels of
     262              :      * catalog references.  For example, to sort by pg_proc.proargtypes,
     263              :      * descend to each pg_type and then further to its pg_namespace, for an
     264              :      * overall sort by (nspname, typname).
     265              :      */
     266      1199991 :     if (obj1->objType == DO_FUNC || obj1->objType == DO_AGG)
     267            0 :     {
     268           70 :         FuncInfo   *fobj1 = *(FuncInfo *const *) p1;
     269           70 :         FuncInfo   *fobj2 = *(FuncInfo *const *) p2;
     270              :         int         i;
     271              : 
     272              :         /* Sort by number of arguments, then argument type names */
     273           70 :         cmpval = fobj1->nargs - fobj2->nargs;
     274           70 :         if (cmpval != 0)
     275           16 :             return cmpval;
     276           64 :         for (i = 0; i < fobj1->nargs; i++)
     277              :         {
     278           64 :             cmpval = pgTypeNameCompare(fobj1->argtypes[i],
     279           64 :                                        fobj2->argtypes[i]);
     280           64 :             if (cmpval != 0)
     281           54 :                 return cmpval;
     282              :         }
     283              :     }
     284      1199921 :     else if (obj1->objType == DO_OPERATOR)
     285              :     {
     286       923700 :         OprInfo    *oobj1 = *(OprInfo *const *) p1;
     287       923700 :         OprInfo    *oobj2 = *(OprInfo *const *) p2;
     288              : 
     289              :         /* oprkind is 'l', 'r', or 'b'; this sorts prefix, postfix, infix */
     290       923700 :         cmpval = (oobj2->oprkind - oobj1->oprkind);
     291       923700 :         if (cmpval != 0)
     292        23560 :             return cmpval;
     293              :         /* Within an oprkind, sort by argument type names */
     294       900140 :         cmpval = pgTypeNameCompare(oobj1->oprleft, oobj2->oprleft);
     295       900140 :         if (cmpval != 0)
     296       793238 :             return cmpval;
     297       106902 :         cmpval = pgTypeNameCompare(oobj1->oprright, oobj2->oprright);
     298       106902 :         if (cmpval != 0)
     299       106902 :             return cmpval;
     300              :     }
     301       276221 :     else if (obj1->objType == DO_OPCLASS)
     302              :     {
     303        19408 :         OpclassInfo *opcobj1 = *(OpclassInfo *const *) p1;
     304        19408 :         OpclassInfo *opcobj2 = *(OpclassInfo *const *) p2;
     305              : 
     306              :         /* Sort by access method name, per pg_opclass_am_name_nsp_index */
     307        19408 :         cmpval = accessMethodNameCompare(opcobj1->opcmethod,
     308              :                                          opcobj2->opcmethod);
     309        19408 :         if (cmpval != 0)
     310        19408 :             return cmpval;
     311              :     }
     312       256813 :     else if (obj1->objType == DO_OPFAMILY)
     313              :     {
     314        15458 :         OpfamilyInfo *opfobj1 = *(OpfamilyInfo *const *) p1;
     315        15458 :         OpfamilyInfo *opfobj2 = *(OpfamilyInfo *const *) p2;
     316              : 
     317              :         /* Sort by access method name, per pg_opfamily_am_name_nsp_index */
     318        15458 :         cmpval = accessMethodNameCompare(opfobj1->opfmethod,
     319              :                                          opfobj2->opfmethod);
     320        15458 :         if (cmpval != 0)
     321        15458 :             return cmpval;
     322              :     }
     323       241355 :     else if (obj1->objType == DO_COLLATION)
     324              :     {
     325            0 :         CollInfo   *cobj1 = *(CollInfo *const *) p1;
     326            0 :         CollInfo   *cobj2 = *(CollInfo *const *) p2;
     327              : 
     328              :         /*
     329              :          * Sort by encoding, per pg_collation_name_enc_nsp_index. Technically,
     330              :          * this is not necessary, because wherever this changes dump order,
     331              :          * restoring the dump fails anyway.  CREATE COLLATION can't create a
     332              :          * tie for this to break, because it imposes restrictions to make
     333              :          * (nspname, collname) uniquely identify a collation within a given
     334              :          * DatabaseEncoding.  While pg_import_system_collations() can create a
     335              :          * tie, pg_dump+restore fails after
     336              :          * pg_import_system_collations('my_schema') does so. However, there's
     337              :          * little to gain by ignoring one natural key column on the basis of
     338              :          * those limitations elsewhere, so respect the full natural key like
     339              :          * we do for other object types.
     340              :          */
     341            0 :         cmpval = cobj1->collencoding - cobj2->collencoding;
     342            0 :         if (cmpval != 0)
     343            0 :             return cmpval;
     344              :     }
     345       241355 :     else if (obj1->objType == DO_ATTRDEF)
     346              :     {
     347          453 :         AttrDefInfo *adobj1 = *(AttrDefInfo *const *) p1;
     348          453 :         AttrDefInfo *adobj2 = *(AttrDefInfo *const *) p2;
     349              : 
     350              :         /* Sort by attribute number */
     351          453 :         cmpval = (adobj1->adnum - adobj2->adnum);
     352          453 :         if (cmpval != 0)
     353          453 :             return cmpval;
     354              :     }
     355       240902 :     else if (obj1->objType == DO_POLICY)
     356              :     {
     357           23 :         PolicyInfo *pobj1 = *(PolicyInfo *const *) p1;
     358           23 :         PolicyInfo *pobj2 = *(PolicyInfo *const *) p2;
     359              : 
     360              :         /* Sort by table name (table namespace was considered already) */
     361           23 :         cmpval = strcmp(pobj1->poltable->dobj.name,
     362           23 :                         pobj2->poltable->dobj.name);
     363           23 :         if (cmpval != 0)
     364           23 :             return cmpval;
     365              :     }
     366       240879 :     else if (obj1->objType == DO_RULE)
     367              :     {
     368       239825 :         RuleInfo   *robj1 = *(RuleInfo *const *) p1;
     369       239825 :         RuleInfo   *robj2 = *(RuleInfo *const *) p2;
     370              : 
     371              :         /* Sort by table name (table namespace was considered already) */
     372       239825 :         cmpval = strcmp(robj1->ruletable->dobj.name,
     373       239825 :                         robj2->ruletable->dobj.name);
     374       239825 :         if (cmpval != 0)
     375       239825 :             return cmpval;
     376              :     }
     377         1054 :     else if (obj1->objType == DO_TRIGGER)
     378              :     {
     379          400 :         TriggerInfo *tobj1 = *(TriggerInfo *const *) p1;
     380          400 :         TriggerInfo *tobj2 = *(TriggerInfo *const *) p2;
     381              : 
     382              :         /* Sort by table name (table namespace was considered already) */
     383          400 :         cmpval = strcmp(tobj1->tgtable->dobj.name,
     384          400 :                         tobj2->tgtable->dobj.name);
     385          400 :         if (cmpval != 0)
     386          400 :             return cmpval;
     387              :     }
     388          654 :     else if (obj1->objType == DO_CONSTRAINT ||
     389          283 :              obj1->objType == DO_FK_CONSTRAINT)
     390            0 :     {
     391          371 :         ConstraintInfo *robj1 = *(ConstraintInfo *const *) p1;
     392          371 :         ConstraintInfo *robj2 = *(ConstraintInfo *const *) p2;
     393              : 
     394              :         /*
     395              :          * Sort domain constraints before table constraints, for consistency
     396              :          * with our decision to sort CREATE DOMAIN before CREATE TABLE.
     397              :          */
     398          371 :         if (robj1->condomain)
     399              :         {
     400           28 :             if (robj2->condomain)
     401              :             {
     402              :                 /* Sort by domain name (domain namespace was considered) */
     403            0 :                 cmpval = strcmp(robj1->condomain->dobj.name,
     404            0 :                                 robj2->condomain->dobj.name);
     405            0 :                 if (cmpval != 0)
     406            0 :                     return cmpval;
     407              :             }
     408              :             else
     409           28 :                 return PRIO_TYPE - PRIO_TABLE;
     410              :         }
     411          343 :         else if (robj2->condomain)
     412            5 :             return PRIO_TABLE - PRIO_TYPE;
     413              :         else
     414              :         {
     415              :             /* Sort by table name (table namespace was considered already) */
     416          338 :             cmpval = strcmp(robj1->contable->dobj.name,
     417          338 :                             robj2->contable->dobj.name);
     418          338 :             if (cmpval != 0)
     419          338 :                 return cmpval;
     420              :         }
     421              :     }
     422          283 :     else if (obj1->objType == DO_DEFAULT_ACL)
     423              :     {
     424           14 :         DefaultACLInfo *daclobj1 = *(DefaultACLInfo *const *) p1;
     425           14 :         DefaultACLInfo *daclobj2 = *(DefaultACLInfo *const *) p2;
     426              : 
     427              :         /*
     428              :          * Sort by defaclrole, per pg_default_acl_role_nsp_obj_index.  The
     429              :          * (namespace, name) match (defaclnamespace, defaclobjtype).
     430              :          */
     431           14 :         cmpval = strcmp(daclobj1->defaclrole, daclobj2->defaclrole);
     432           14 :         if (cmpval != 0)
     433           14 :             return cmpval;
     434              :     }
     435          269 :     else if (obj1->objType == DO_PUBLICATION_REL)
     436              :     {
     437          241 :         PublicationRelInfo *probj1 = *(PublicationRelInfo *const *) p1;
     438          241 :         PublicationRelInfo *probj2 = *(PublicationRelInfo *const *) p2;
     439              : 
     440              :         /* Sort by publication name, since (namespace, name) match the rel */
     441          241 :         cmpval = strcmp(probj1->publication->dobj.name,
     442          241 :                         probj2->publication->dobj.name);
     443          241 :         if (cmpval != 0)
     444          241 :             return cmpval;
     445              :     }
     446           28 :     else if (obj1->objType == DO_PUBLICATION_TABLE_IN_SCHEMA)
     447              :     {
     448           28 :         PublicationSchemaInfo *psobj1 = *(PublicationSchemaInfo *const *) p1;
     449           28 :         PublicationSchemaInfo *psobj2 = *(PublicationSchemaInfo *const *) p2;
     450              : 
     451              :         /* Sort by publication name, since ->name is just nspname */
     452           28 :         cmpval = strcmp(psobj1->publication->dobj.name,
     453           28 :                         psobj2->publication->dobj.name);
     454           28 :         if (cmpval != 0)
     455           28 :             return cmpval;
     456              :     }
     457            0 :     else if (obj1->objType == DO_SUBSCRIPTION_REL)
     458              :     {
     459            0 :         SubRelInfo *srobj1 = *(SubRelInfo *const *) p1;
     460            0 :         SubRelInfo *srobj2 = *(SubRelInfo *const *) p2;
     461              : 
     462              :         /* Sort by subscription name, since (namespace, name) match the rel */
     463            0 :         cmpval = strcmp(srobj1->subinfo->dobj.name,
     464            0 :                         srobj2->subinfo->dobj.name);
     465            0 :         if (cmpval != 0)
     466            0 :             return cmpval;
     467              :     }
     468              : 
     469              :     /*
     470              :      * Shouldn't get here except after catalog corruption, but if we do, sort
     471              :      * by OID.  This may make logically-identical databases differ in the
     472              :      * order of objects in dump output.  Users will get spurious schema diffs.
     473              :      * Expect flaky failures of 002_pg_upgrade.pl test 'dump outputs from
     474              :      * original and restored regression databases match' if the regression
     475              :      * database contains objects allowing that test to reach here.  That's a
     476              :      * consequence of the test using "pg_restore -j", which doesn't fully
     477              :      * constrain OID assignment order.
     478              :      */
     479              :     Assert(false);
     480            0 :     return oidcmp(obj1->catId.oid, obj2->catId.oid);
     481              : }
     482              : 
     483              : /* Compare two OID-identified pg_type values by nspname, then by typname. */
     484              : static int
     485      1007106 : pgTypeNameCompare(Oid typid1, Oid typid2)
     486              : {
     487              :     TypeInfo   *typobj1;
     488              :     TypeInfo   *typobj2;
     489              :     int         cmpval;
     490              : 
     491      1007106 :     if (typid1 == typid2)
     492       106912 :         return 0;
     493              : 
     494       900194 :     typobj1 = findTypeByOid(typid1);
     495       900194 :     typobj2 = findTypeByOid(typid2);
     496              : 
     497       900194 :     if (!typobj1 || !typobj2)
     498              :     {
     499              :         /*
     500              :          * getTypes() didn't find some OID.  Assume catalog corruption, e.g.
     501              :          * an oprright value without the corresponding OID in a pg_type row.
     502              :          * Report as "equal", so the caller uses the next available basis for
     503              :          * comparison, e.g. the next function argument.
     504              :          *
     505              :          * Unary operators have InvalidOid in oprleft (if oprkind='r') or in
     506              :          * oprright (if oprkind='l').  Caller already sorted by oprkind,
     507              :          * calling us only for like-kind operators.  Hence, "typid1 == typid2"
     508              :          * took care of InvalidOid.  (v14 removed postfix operator support.
     509              :          * Hence, when dumping from v14+, only oprleft can be InvalidOid.)
     510              :          */
     511              :         Assert(false);
     512            0 :         return 0;
     513              :     }
     514              : 
     515       900194 :     if (!typobj1->dobj.namespace || !typobj2->dobj.namespace)
     516              :         Assert(false);          /* catalog corruption */
     517              :     else
     518              :     {
     519       900194 :         cmpval = strcmp(typobj1->dobj.namespace->dobj.name,
     520       900194 :                         typobj2->dobj.namespace->dobj.name);
     521       900194 :         if (cmpval != 0)
     522           31 :             return cmpval;
     523              :     }
     524       900163 :     return strcmp(typobj1->dobj.name, typobj2->dobj.name);
     525              : }
     526              : 
     527              : /* Compare two OID-identified pg_am values by amname. */
     528              : static int
     529        34866 : accessMethodNameCompare(Oid am1, Oid am2)
     530              : {
     531              :     AccessMethodInfo *amobj1;
     532              :     AccessMethodInfo *amobj2;
     533              : 
     534        34866 :     if (am1 == am2)
     535            0 :         return 0;
     536              : 
     537        34866 :     amobj1 = findAccessMethodByOid(am1);
     538        34866 :     amobj2 = findAccessMethodByOid(am2);
     539              : 
     540        34866 :     if (!amobj1 || !amobj2)
     541              :     {
     542              :         /* catalog corruption: handle like pgTypeNameCompare() does */
     543              :         Assert(false);
     544            0 :         return 0;
     545              :     }
     546              : 
     547        34866 :     return strcmp(amobj1->dobj.name, amobj2->dobj.name);
     548              : }
     549              : 
     550              : 
     551              : /*
     552              :  * Sort the given objects into a safe dump order using dependency
     553              :  * information (to the extent we have it available).
     554              :  *
     555              :  * The DumpIds of the PRE_DATA_BOUNDARY and POST_DATA_BOUNDARY objects are
     556              :  * passed in separately, in case we need them during dependency loop repair.
     557              :  */
     558              : void
     559          249 : sortDumpableObjects(DumpableObject **objs, int numObjs,
     560              :                     DumpId preBoundaryId, DumpId postBoundaryId)
     561              : {
     562              :     DumpableObject **ordering;
     563              :     int         nOrdering;
     564              : 
     565          249 :     if (numObjs <= 0)            /* can't happen anymore ... */
     566            0 :         return;
     567              : 
     568              :     /*
     569              :      * Saving the boundary IDs in static variables is a bit grotty, but seems
     570              :      * better than adding them to parameter lists of subsidiary functions.
     571              :      */
     572          249 :     preDataBoundId = preBoundaryId;
     573          249 :     postDataBoundId = postBoundaryId;
     574              : 
     575          249 :     ordering = pg_malloc_array(DumpableObject *, numObjs);
     576          670 :     while (!TopoSort(objs, numObjs, ordering, &nOrdering))
     577          421 :         findDependencyLoops(ordering, nOrdering, numObjs);
     578              : 
     579          249 :     memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
     580              : 
     581          249 :     free(ordering);
     582              : }
     583              : 
     584              : /*
     585              :  * TopoSort -- topological sort of a dump list
     586              :  *
     587              :  * Generate a re-ordering of the dump list that satisfies all the dependency
     588              :  * constraints shown in the dump list.  (Each such constraint is a fact of a
     589              :  * partial ordering.)  Minimize rearrangement of the list not needed to
     590              :  * achieve the partial ordering.
     591              :  *
     592              :  * The input is the list of numObjs objects in objs[].  This list is not
     593              :  * modified.
     594              :  *
     595              :  * Returns true if able to build an ordering that satisfies all the
     596              :  * constraints, false if not (there are contradictory constraints).
     597              :  *
     598              :  * On success (true result), ordering[] is filled with a sorted array of
     599              :  * DumpableObject pointers, of length equal to the input list length.
     600              :  *
     601              :  * On failure (false result), ordering[] is filled with an unsorted array of
     602              :  * DumpableObject pointers of length *nOrdering, listing the objects that
     603              :  * prevented the sort from being completed.  In general, these objects either
     604              :  * participate directly in a dependency cycle, or are depended on by objects
     605              :  * that are in a cycle.  (The latter objects are not actually problematic,
     606              :  * but it takes further analysis to identify which are which.)
     607              :  *
     608              :  * The caller is responsible for allocating sufficient space at *ordering.
     609              :  */
     610              : static bool
     611          670 : TopoSort(DumpableObject **objs,
     612              :          int numObjs,
     613              :          DumpableObject **ordering, /* output argument */
     614              :          int *nOrdering)        /* output argument */
     615              : {
     616          670 :     DumpId      maxDumpId = getMaxDumpId();
     617              :     binaryheap *pendingHeap;
     618              :     int        *beforeConstraints;
     619              :     int        *idMap;
     620              :     DumpableObject *obj;
     621              :     int         i,
     622              :                 j,
     623              :                 k;
     624              : 
     625              :     /*
     626              :      * This is basically the same algorithm shown for topological sorting in
     627              :      * Knuth's Volume 1.  However, we would like to minimize unnecessary
     628              :      * rearrangement of the input ordering; that is, when we have a choice of
     629              :      * which item to output next, we always want to take the one highest in
     630              :      * the original list.  Therefore, instead of maintaining an unordered
     631              :      * linked list of items-ready-to-output as Knuth does, we maintain a heap
     632              :      * of their item numbers, which we can use as a priority queue.  This
     633              :      * turns the algorithm from O(N) to O(N log N) because each insertion or
     634              :      * removal of a heap item takes O(log N) time.  However, that's still
     635              :      * plenty fast enough for this application.
     636              :      */
     637              : 
     638          670 :     *nOrdering = numObjs;       /* for success return */
     639              : 
     640              :     /* Eliminate the null case */
     641          670 :     if (numObjs <= 0)
     642            0 :         return true;
     643              : 
     644              :     /* Create workspace for the above-described heap */
     645          670 :     pendingHeap = binaryheap_allocate(numObjs, int_cmp, NULL);
     646              : 
     647              :     /*
     648              :      * Scan the constraints, and for each item in the input, generate a count
     649              :      * of the number of constraints that say it must be before something else.
     650              :      * The count for the item with dumpId j is stored in beforeConstraints[j].
     651              :      * We also make a map showing the input-order index of the item with
     652              :      * dumpId j.
     653              :      */
     654          670 :     beforeConstraints = pg_malloc0_array(int, (maxDumpId + 1));
     655          670 :     idMap = pg_malloc_array(int, (maxDumpId + 1));
     656      2638246 :     for (i = 0; i < numObjs; i++)
     657              :     {
     658      2637576 :         obj = objs[i];
     659      2637576 :         j = obj->dumpId;
     660      2637576 :         if (j <= 0 || j > maxDumpId)
     661            0 :             pg_fatal("invalid dumpId %d", j);
     662      2637576 :         idMap[j] = i;
     663      6771743 :         for (j = 0; j < obj->nDeps; j++)
     664              :         {
     665      4134167 :             k = obj->dependencies[j];
     666      4134167 :             if (k <= 0 || k > maxDumpId)
     667            0 :                 pg_fatal("invalid dependency %d", k);
     668      4134167 :             beforeConstraints[k]++;
     669              :         }
     670              :     }
     671              : 
     672              :     /*
     673              :      * Now initialize the heap of items-ready-to-output by filling it with the
     674              :      * indexes of items that already have beforeConstraints[id] == 0.
     675              :      *
     676              :      * We enter the indexes into pendingHeap in decreasing order so that the
     677              :      * heap invariant is satisfied at the completion of this loop.  This
     678              :      * reduces the amount of work that binaryheap_build() must do.
     679              :      */
     680      2638246 :     for (i = numObjs; --i >= 0;)
     681              :     {
     682      2637576 :         if (beforeConstraints[objs[i]->dumpId] == 0)
     683        31777 :             binaryheap_add_unordered(pendingHeap, (void *) (intptr_t) i);
     684              :     }
     685          670 :     binaryheap_build(pendingHeap);
     686              : 
     687              :     /*--------------------
     688              :      * Now emit objects, working backwards in the output list.  At each step,
     689              :      * we use the priority heap to select the last item that has no remaining
     690              :      * before-constraints.  We remove that item from the heap, output it to
     691              :      * ordering[], and decrease the beforeConstraints count of each of the
     692              :      * items it was constrained against.  Whenever an item's beforeConstraints
     693              :      * count is thereby decreased to zero, we insert it into the priority heap
     694              :      * to show that it is a candidate to output.  We are done when the heap
     695              :      * becomes empty; if we have output every element then we succeeded,
     696              :      * otherwise we failed.
     697              :      * i = number of ordering[] entries left to output
     698              :      * j = objs[] index of item we are outputting
     699              :      * k = temp for scanning constraint list for item j
     700              :      *--------------------
     701              :      */
     702          670 :     i = numObjs;
     703      1852803 :     while (!binaryheap_empty(pendingHeap))
     704              :     {
     705              :         /* Select object to output by removing largest heap member */
     706      1852133 :         j = (int) (intptr_t) binaryheap_remove_first(pendingHeap);
     707      1852133 :         obj = objs[j];
     708              :         /* Output candidate to ordering[] */
     709      1852133 :         ordering[--i] = obj;
     710              :         /* Update beforeConstraints counts of its predecessors */
     711      4533903 :         for (k = 0; k < obj->nDeps; k++)
     712              :         {
     713      2681770 :             int         id = obj->dependencies[k];
     714              : 
     715      2681770 :             if ((--beforeConstraints[id]) == 0)
     716      1820356 :                 binaryheap_add(pendingHeap, (void *) (intptr_t) idMap[id]);
     717              :         }
     718              :     }
     719              : 
     720              :     /*
     721              :      * If we failed, report the objects that couldn't be output; these are the
     722              :      * ones with beforeConstraints[] still nonzero.
     723              :      */
     724          670 :     if (i != 0)
     725              :     {
     726          421 :         k = 0;
     727      1712291 :         for (j = 1; j <= maxDumpId; j++)
     728              :         {
     729      1711870 :             if (beforeConstraints[j] != 0)
     730       785443 :                 ordering[k++] = objs[idMap[j]];
     731              :         }
     732          421 :         *nOrdering = k;
     733              :     }
     734              : 
     735              :     /* Done */
     736          670 :     binaryheap_free(pendingHeap);
     737          670 :     free(beforeConstraints);
     738          670 :     free(idMap);
     739              : 
     740          670 :     return (i == 0);
     741              : }
     742              : 
     743              : /*
     744              :  * findDependencyLoops - identify loops in TopoSort's failure output,
     745              :  *      and pass each such loop to repairDependencyLoop() for action
     746              :  *
     747              :  * In general there may be many loops in the set of objects returned by
     748              :  * TopoSort; for speed we should try to repair as many loops as we can
     749              :  * before trying TopoSort again.  We can safely repair loops that are
     750              :  * disjoint (have no members in common); if we find overlapping loops
     751              :  * then we repair only the first one found, because the action taken to
     752              :  * repair the first might have repaired the other as well.  (If not,
     753              :  * we'll fix it on the next go-round.)
     754              :  *
     755              :  * objs[] lists the objects TopoSort couldn't sort
     756              :  * nObjs is the number of such objects
     757              :  * totObjs is the total number of objects in the universe
     758              :  */
     759              : static void
     760          421 : findDependencyLoops(DumpableObject **objs, int nObjs, int totObjs)
     761              : {
     762              :     /*
     763              :      * We use three data structures here:
     764              :      *
     765              :      * processed[] is a bool array indexed by dump ID, marking the objects
     766              :      * already processed during this invocation of findDependencyLoops().
     767              :      *
     768              :      * searchFailed[] is another array indexed by dump ID.  searchFailed[j] is
     769              :      * set to dump ID k if we have proven that there is no dependency path
     770              :      * leading from object j back to start point k.  This allows us to skip
     771              :      * useless searching when there are multiple dependency paths from k to j,
     772              :      * which is a common situation.  We could use a simple bool array for
     773              :      * this, but then we'd need to re-zero it for each start point, resulting
     774              :      * in O(N^2) zeroing work.  Using the start point's dump ID as the "true"
     775              :      * value lets us skip clearing the array before we consider the next start
     776              :      * point.
     777              :      *
     778              :      * workspace[] is an array of DumpableObject pointers, in which we try to
     779              :      * build lists of objects constituting loops.  We make workspace[] large
     780              :      * enough to hold all the objects in TopoSort's output, which is huge
     781              :      * overkill in most cases but could theoretically be necessary if there is
     782              :      * a single dependency chain linking all the objects.
     783              :      */
     784              :     bool       *processed;
     785              :     DumpId     *searchFailed;
     786              :     DumpableObject **workspace;
     787              :     bool        fixedloop;
     788              :     int         i;
     789              : 
     790          421 :     processed = pg_malloc0_array(bool, (getMaxDumpId() + 1));
     791          421 :     searchFailed = pg_malloc0_array(DumpId, (getMaxDumpId() + 1));
     792          421 :     workspace = pg_malloc_array(DumpableObject *, totObjs);
     793          421 :     fixedloop = false;
     794              : 
     795       785864 :     for (i = 0; i < nObjs; i++)
     796              :     {
     797       785443 :         DumpableObject *obj = objs[i];
     798              :         int         looplen;
     799              :         int         j;
     800              : 
     801       785443 :         looplen = findLoop(obj,
     802              :                            obj->dumpId,
     803              :                            processed,
     804              :                            searchFailed,
     805              :                            workspace,
     806              :                            0);
     807              : 
     808       785443 :         if (looplen > 0)
     809              :         {
     810              :             /* Found a loop, repair it */
     811        40484 :             repairDependencyLoop(workspace, looplen);
     812        40484 :             fixedloop = true;
     813              :             /* Mark loop members as processed */
     814       121536 :             for (j = 0; j < looplen; j++)
     815        81052 :                 processed[workspace[j]->dumpId] = true;
     816              :         }
     817              :         else
     818              :         {
     819              :             /*
     820              :              * There's no loop starting at this object, but mark it processed
     821              :              * anyway.  This is not necessary for correctness, but saves later
     822              :              * invocations of findLoop() from uselessly chasing references to
     823              :              * such an object.
     824              :              */
     825       744959 :             processed[obj->dumpId] = true;
     826              :         }
     827              :     }
     828              : 
     829              :     /* We'd better have fixed at least one loop */
     830          421 :     if (!fixedloop)
     831            0 :         pg_fatal("could not identify dependency loop");
     832              : 
     833          421 :     free(workspace);
     834          421 :     free(searchFailed);
     835          421 :     free(processed);
     836          421 : }
     837              : 
     838              : /*
     839              :  * Recursively search for a circular dependency loop that doesn't include
     840              :  * any already-processed objects.
     841              :  *
     842              :  *  obj: object we are examining now
     843              :  *  startPoint: dumpId of starting object for the hoped-for circular loop
     844              :  *  processed[]: flag array marking already-processed objects
     845              :  *  searchFailed[]: flag array marking already-unsuccessfully-visited objects
     846              :  *  workspace[]: work array in which we are building list of loop members
     847              :  *  depth: number of valid entries in workspace[] at call
     848              :  *
     849              :  * On success, the length of the loop is returned, and workspace[] is filled
     850              :  * with pointers to the members of the loop.  On failure, we return 0.
     851              :  *
     852              :  * Note: it is possible that the given starting object is a member of more
     853              :  * than one cycle; if so, we will find an arbitrary one of the cycles.
     854              :  */
     855              : static int
     856     23070878 : findLoop(DumpableObject *obj,
     857              :          DumpId startPoint,
     858              :          bool *processed,
     859              :          DumpId *searchFailed,
     860              :          DumpableObject **workspace,
     861              :          int depth)
     862              : {
     863              :     int         i;
     864              : 
     865              :     /*
     866              :      * Reject if obj is already processed.  This test prevents us from finding
     867              :      * loops that overlap previously-processed loops.
     868              :      */
     869     23070878 :     if (processed[obj->dumpId])
     870     21465429 :         return 0;
     871              : 
     872              :     /*
     873              :      * If we've already proven there is no path from this object back to the
     874              :      * startPoint, forget it.
     875              :      */
     876      1605449 :     if (searchFailed[obj->dumpId] == startPoint)
     877       162887 :         return 0;
     878              : 
     879              :     /*
     880              :      * Reject if obj is already present in workspace.  This test prevents us
     881              :      * from going into infinite recursion if we are given a startPoint object
     882              :      * that links to a cycle it's not a member of, and it guarantees that we
     883              :      * can't overflow the allocated size of workspace[].
     884              :      */
     885      2810645 :     for (i = 0; i < depth; i++)
     886              :     {
     887      1370555 :         if (workspace[i] == obj)
     888         2472 :             return 0;
     889              :     }
     890              : 
     891              :     /*
     892              :      * Okay, tentatively add obj to workspace
     893              :      */
     894      1440090 :     workspace[depth++] = obj;
     895              : 
     896              :     /*
     897              :      * See if we've found a loop back to the desired startPoint; if so, done
     898              :      */
     899     24398590 :     for (i = 0; i < obj->nDeps; i++)
     900              :     {
     901     22998984 :         if (obj->dependencies[i] == startPoint)
     902        40484 :             return depth;
     903              :     }
     904              : 
     905              :     /*
     906              :      * Recurse down each outgoing branch
     907              :      */
     908     23644473 :     for (i = 0; i < obj->nDeps; i++)
     909              :     {
     910     22285435 :         DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
     911              :         int         newDepth;
     912              : 
     913     22285435 :         if (!nextobj)
     914            0 :             continue;           /* ignore dependencies on undumped objects */
     915     22285435 :         newDepth = findLoop(nextobj,
     916              :                             startPoint,
     917              :                             processed,
     918              :                             searchFailed,
     919              :                             workspace,
     920              :                             depth);
     921     22285435 :         if (newDepth > 0)
     922        40568 :             return newDepth;
     923              :     }
     924              : 
     925              :     /*
     926              :      * Remember there is no path from here back to startPoint
     927              :      */
     928      1359038 :     searchFailed[obj->dumpId] = startPoint;
     929              : 
     930      1359038 :     return 0;
     931              : }
     932              : 
     933              : /*
     934              :  * A user-defined datatype will have a dependency loop with each of its
     935              :  * I/O functions (since those have the datatype as input or output).
     936              :  * Similarly, a range type will have a loop with its canonicalize function,
     937              :  * if any.  Break the loop by making the function depend on the associated
     938              :  * shell type, instead.
     939              :  */
     940              : static void
     941          188 : repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
     942              : {
     943          188 :     TypeInfo   *typeInfo = (TypeInfo *) typeobj;
     944              : 
     945              :     /* remove function's dependency on type */
     946          188 :     removeObjectDependency(funcobj, typeobj->dumpId);
     947              : 
     948              :     /* add function's dependency on shell type, instead */
     949          188 :     if (typeInfo->shellType)
     950              :     {
     951          146 :         addObjectDependency(funcobj, typeInfo->shellType->dobj.dumpId);
     952              : 
     953              :         /*
     954              :          * Mark shell type (always including the definition, as we need the
     955              :          * shell type defined to identify the function fully) as to be dumped
     956              :          * if any such function is
     957              :          */
     958          146 :         if (funcobj->dump)
     959          146 :             typeInfo->shellType->dobj.dump = funcobj->dump |
     960              :                 DUMP_COMPONENT_DEFINITION;
     961              :     }
     962          188 : }
     963              : 
     964              : /*
     965              :  * Because we force a view to depend on its ON SELECT rule, while there
     966              :  * will be an implicit dependency in the other direction, we need to break
     967              :  * the loop.  If there are no other objects in the loop then we can remove
     968              :  * the implicit dependency and leave the ON SELECT rule non-separate.
     969              :  * This applies to matviews, as well.
     970              :  */
     971              : static void
     972        37472 : repairViewRuleLoop(DumpableObject *viewobj,
     973              :                    DumpableObject *ruleobj)
     974              : {
     975              :     /* remove rule's dependency on view */
     976        37472 :     removeObjectDependency(ruleobj, viewobj->dumpId);
     977              :     /* flags on the two objects are already set correctly for this case */
     978        37472 : }
     979              : 
     980              : /*
     981              :  * However, if there are other objects in the loop, we must break the loop
     982              :  * by making the ON SELECT rule a separately-dumped object.
     983              :  *
     984              :  * Because findLoop() finds shorter cycles before longer ones, it's likely
     985              :  * that we will have previously fired repairViewRuleLoop() and removed the
     986              :  * rule's dependency on the view.  Put it back to ensure the rule won't be
     987              :  * emitted before the view.
     988              :  *
     989              :  * Note: this approach does *not* work for matviews, at the moment.
     990              :  */
     991              : static void
     992           10 : repairViewRuleMultiLoop(DumpableObject *viewobj,
     993              :                         DumpableObject *ruleobj)
     994              : {
     995           10 :     TableInfo  *viewinfo = (TableInfo *) viewobj;
     996           10 :     RuleInfo   *ruleinfo = (RuleInfo *) ruleobj;
     997              : 
     998              :     /* remove view's dependency on rule */
     999           10 :     removeObjectDependency(viewobj, ruleobj->dumpId);
    1000              :     /* mark view to be printed with a dummy definition */
    1001           10 :     viewinfo->dummy_view = true;
    1002              :     /* mark rule as needing its own dump */
    1003           10 :     ruleinfo->separate = true;
    1004              :     /* put back rule's dependency on view */
    1005           10 :     addObjectDependency(ruleobj, viewobj->dumpId);
    1006              :     /* now that rule is separate, it must be post-data */
    1007           10 :     addObjectDependency(ruleobj, postDataBoundId);
    1008           10 : }
    1009              : 
    1010              : /*
    1011              :  * If a matview is involved in a multi-object loop, we can't currently fix
    1012              :  * that by splitting off the rule.  As a stopgap, we try to fix it by
    1013              :  * dropping the constraint that the matview be dumped in the pre-data section.
    1014              :  * This is sufficient to handle cases where a matview depends on some unique
    1015              :  * index, as can happen if it has a GROUP BY for example.
    1016              :  *
    1017              :  * Note that the "next object" is not necessarily the matview itself;
    1018              :  * it could be the matview's rowtype, for example.  We may come through here
    1019              :  * several times while removing all the pre-data linkages.  In particular,
    1020              :  * if there are other matviews that depend on the one with the circularity
    1021              :  * problem, we'll come through here for each such matview and mark them all
    1022              :  * as postponed.  (This works because all MVs have pre-data dependencies
    1023              :  * to begin with, so each of them will get visited.)
    1024              :  */
    1025              : static void
    1026          117 : repairMatViewBoundaryMultiLoop(DumpableObject *boundaryobj,
    1027              :                                DumpableObject *nextobj)
    1028              : {
    1029              :     /* remove boundary's dependency on object after it in loop */
    1030          117 :     removeObjectDependency(boundaryobj, nextobj->dumpId);
    1031              : 
    1032              :     /*
    1033              :      * If that object is a matview or matview stats, mark it as postponed into
    1034              :      * post-data.
    1035              :      */
    1036          117 :     if (nextobj->objType == DO_TABLE)
    1037              :     {
    1038           38 :         TableInfo  *nextinfo = (TableInfo *) nextobj;
    1039              : 
    1040           38 :         if (nextinfo->relkind == RELKIND_MATVIEW)
    1041           38 :             nextinfo->postponed_def = true;
    1042              :     }
    1043           79 :     else if (nextobj->objType == DO_REL_STATS)
    1044              :     {
    1045            3 :         RelStatsInfo *nextinfo = (RelStatsInfo *) nextobj;
    1046              : 
    1047            3 :         if (nextinfo->relkind == RELKIND_MATVIEW)
    1048            3 :             nextinfo->section = SECTION_POST_DATA;
    1049              :     }
    1050          117 : }
    1051              : 
    1052              : /*
    1053              :  * If a function is involved in a multi-object loop, we can't currently fix
    1054              :  * that by splitting it into two DumpableObjects.  As a stopgap, we try to fix
    1055              :  * it by dropping the constraint that the function be dumped in the pre-data
    1056              :  * section.  This is sufficient to handle cases where a function depends on
    1057              :  * some unique index, as can happen if it has a GROUP BY for example.
    1058              :  */
    1059              : static void
    1060           38 : repairFunctionBoundaryMultiLoop(DumpableObject *boundaryobj,
    1061              :                                 DumpableObject *nextobj)
    1062              : {
    1063              :     /* remove boundary's dependency on object after it in loop */
    1064           38 :     removeObjectDependency(boundaryobj, nextobj->dumpId);
    1065              :     /* if that object is a function, mark it as postponed into post-data */
    1066           38 :     if (nextobj->objType == DO_FUNC)
    1067              :     {
    1068           38 :         FuncInfo   *nextinfo = (FuncInfo *) nextobj;
    1069              : 
    1070           38 :         nextinfo->postponed_def = true;
    1071              :     }
    1072           38 : }
    1073              : 
    1074              : /*
    1075              :  * Because we make tables depend on their CHECK constraints, while there
    1076              :  * will be an automatic dependency in the other direction, we need to break
    1077              :  * the loop.  If there are no other objects in the loop then we can remove
    1078              :  * the automatic dependency and leave the CHECK constraint non-separate.
    1079              :  */
    1080              : static void
    1081          547 : repairTableConstraintLoop(DumpableObject *tableobj,
    1082              :                           DumpableObject *constraintobj)
    1083              : {
    1084              :     /* remove constraint's dependency on table */
    1085          547 :     removeObjectDependency(constraintobj, tableobj->dumpId);
    1086          547 : }
    1087              : 
    1088              : /*
    1089              :  * However, if there are other objects in the loop, we must break the loop
    1090              :  * by making the CHECK constraint a separately-dumped object.
    1091              :  *
    1092              :  * Because findLoop() finds shorter cycles before longer ones, it's likely
    1093              :  * that we will have previously fired repairTableConstraintLoop() and
    1094              :  * removed the constraint's dependency on the table.  Put it back to ensure
    1095              :  * the constraint won't be emitted before the table...
    1096              :  */
    1097              : static void
    1098            5 : repairTableConstraintMultiLoop(DumpableObject *tableobj,
    1099              :                                DumpableObject *constraintobj)
    1100              : {
    1101              :     /* remove table's dependency on constraint */
    1102            5 :     removeObjectDependency(tableobj, constraintobj->dumpId);
    1103              :     /* mark constraint as needing its own dump */
    1104            5 :     ((ConstraintInfo *) constraintobj)->separate = true;
    1105              :     /* put back constraint's dependency on table */
    1106            5 :     addObjectDependency(constraintobj, tableobj->dumpId);
    1107              :     /* now that constraint is separate, it must be post-data */
    1108            5 :     addObjectDependency(constraintobj, postDataBoundId);
    1109            5 : }
    1110              : 
    1111              : /*
    1112              :  * Attribute defaults behave exactly the same as CHECK constraints...
    1113              :  */
    1114              : static void
    1115          987 : repairTableAttrDefLoop(DumpableObject *tableobj,
    1116              :                        DumpableObject *attrdefobj)
    1117              : {
    1118              :     /* remove attrdef's dependency on table */
    1119          987 :     removeObjectDependency(attrdefobj, tableobj->dumpId);
    1120          987 : }
    1121              : 
    1122              : static void
    1123          152 : repairTableAttrDefMultiLoop(DumpableObject *tableobj,
    1124              :                             DumpableObject *attrdefobj)
    1125              : {
    1126              :     /* remove table's dependency on attrdef */
    1127          152 :     removeObjectDependency(tableobj, attrdefobj->dumpId);
    1128              :     /* mark attrdef as needing its own dump */
    1129          152 :     ((AttrDefInfo *) attrdefobj)->separate = true;
    1130              :     /* put back attrdef's dependency on table */
    1131          152 :     addObjectDependency(attrdefobj, tableobj->dumpId);
    1132          152 : }
    1133              : 
    1134              : /*
    1135              :  * CHECK, NOT NULL constraints on domains work just like those on tables ...
    1136              :  */
    1137              : static void
    1138          161 : repairDomainConstraintLoop(DumpableObject *domainobj,
    1139              :                            DumpableObject *constraintobj)
    1140              : {
    1141              :     /* remove constraint's dependency on domain */
    1142          161 :     removeObjectDependency(constraintobj, domainobj->dumpId);
    1143          161 : }
    1144              : 
    1145              : static void
    1146            0 : repairDomainConstraintMultiLoop(DumpableObject *domainobj,
    1147              :                                 DumpableObject *constraintobj)
    1148              : {
    1149              :     /* remove domain's dependency on constraint */
    1150            0 :     removeObjectDependency(domainobj, constraintobj->dumpId);
    1151              :     /* mark constraint as needing its own dump */
    1152            0 :     ((ConstraintInfo *) constraintobj)->separate = true;
    1153              :     /* put back constraint's dependency on domain */
    1154            0 :     addObjectDependency(constraintobj, domainobj->dumpId);
    1155              :     /* now that constraint is separate, it must be post-data */
    1156            0 :     addObjectDependency(constraintobj, postDataBoundId);
    1157            0 : }
    1158              : 
    1159              : static void
    1160            0 : repairIndexLoop(DumpableObject *partedindex,
    1161              :                 DumpableObject *partindex)
    1162              : {
    1163            0 :     removeObjectDependency(partedindex, partindex->dumpId);
    1164            0 : }
    1165              : 
    1166              : /*
    1167              :  * Fix a dependency loop, or die trying ...
    1168              :  *
    1169              :  * This routine is mainly concerned with reducing the multiple ways that
    1170              :  * a loop might appear to common cases, which it passes off to the
    1171              :  * "fixer" routines above.
    1172              :  */
    1173              : static void
    1174        40484 : repairDependencyLoop(DumpableObject **loop,
    1175              :                      int nLoop)
    1176              : {
    1177              :     int         i,
    1178              :                 j;
    1179              : 
    1180              :     /* Datatype and one of its I/O or canonicalize functions */
    1181        40484 :     if (nLoop == 2 &&
    1182        39355 :         loop[0]->objType == DO_TYPE &&
    1183          161 :         loop[1]->objType == DO_FUNC)
    1184              :     {
    1185            0 :         repairTypeFuncLoop(loop[0], loop[1]);
    1186            0 :         return;
    1187              :     }
    1188        40484 :     if (nLoop == 2 &&
    1189        39355 :         loop[1]->objType == DO_TYPE &&
    1190          188 :         loop[0]->objType == DO_FUNC)
    1191              :     {
    1192          188 :         repairTypeFuncLoop(loop[1], loop[0]);
    1193          188 :         return;
    1194              :     }
    1195              : 
    1196              :     /* View (including matview) and its ON SELECT rule */
    1197        40296 :     if (nLoop == 2 &&
    1198        39167 :         loop[0]->objType == DO_TABLE &&
    1199        39006 :         loop[1]->objType == DO_RULE &&
    1200        37472 :         (((TableInfo *) loop[0])->relkind == RELKIND_VIEW ||
    1201          468 :          ((TableInfo *) loop[0])->relkind == RELKIND_MATVIEW) &&
    1202        37472 :         ((RuleInfo *) loop[1])->ev_type == '1' &&
    1203        37472 :         ((RuleInfo *) loop[1])->is_instead &&
    1204        37472 :         ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
    1205              :     {
    1206        37472 :         repairViewRuleLoop(loop[0], loop[1]);
    1207        37472 :         return;
    1208              :     }
    1209         2824 :     if (nLoop == 2 &&
    1210         1695 :         loop[1]->objType == DO_TABLE &&
    1211            0 :         loop[0]->objType == DO_RULE &&
    1212            0 :         (((TableInfo *) loop[1])->relkind == RELKIND_VIEW ||
    1213            0 :          ((TableInfo *) loop[1])->relkind == RELKIND_MATVIEW) &&
    1214            0 :         ((RuleInfo *) loop[0])->ev_type == '1' &&
    1215            0 :         ((RuleInfo *) loop[0])->is_instead &&
    1216            0 :         ((RuleInfo *) loop[0])->ruletable == (TableInfo *) loop[1])
    1217              :     {
    1218            0 :         repairViewRuleLoop(loop[1], loop[0]);
    1219            0 :         return;
    1220              :     }
    1221              : 
    1222              :     /* Indirect loop involving view (but not matview) and ON SELECT rule */
    1223         2824 :     if (nLoop > 2)
    1224              :     {
    1225         1801 :         for (i = 0; i < nLoop; i++)
    1226              :         {
    1227         1489 :             if (loop[i]->objType == DO_TABLE &&
    1228          436 :                 ((TableInfo *) loop[i])->relkind == RELKIND_VIEW)
    1229              :             {
    1230           24 :                 for (j = 0; j < nLoop; j++)
    1231              :                 {
    1232           24 :                     if (loop[j]->objType == DO_RULE &&
    1233           10 :                         ((RuleInfo *) loop[j])->ev_type == '1' &&
    1234           10 :                         ((RuleInfo *) loop[j])->is_instead &&
    1235           10 :                         ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
    1236              :                     {
    1237           10 :                         repairViewRuleMultiLoop(loop[i], loop[j]);
    1238           10 :                         return;
    1239              :                     }
    1240              :                 }
    1241              :             }
    1242              :         }
    1243              :     }
    1244              : 
    1245              :     /* Indirect loop involving matview and data boundary */
    1246         2814 :     if (nLoop > 2)
    1247              :     {
    1248         1277 :         for (i = 0; i < nLoop; i++)
    1249              :         {
    1250         1082 :             if (loop[i]->objType == DO_TABLE &&
    1251          426 :                 ((TableInfo *) loop[i])->relkind == RELKIND_MATVIEW)
    1252              :             {
    1253          315 :                 for (j = 0; j < nLoop; j++)
    1254              :                 {
    1255          312 :                     if (loop[j]->objType == DO_PRE_DATA_BOUNDARY)
    1256              :                     {
    1257              :                         DumpableObject *nextobj;
    1258              : 
    1259          114 :                         nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
    1260          114 :                         repairMatViewBoundaryMultiLoop(loop[j], nextobj);
    1261          114 :                         return;
    1262              :                     }
    1263              :                 }
    1264              :             }
    1265          965 :             else if (loop[i]->objType == DO_REL_STATS &&
    1266          131 :                      ((RelStatsInfo *) loop[i])->relkind == RELKIND_MATVIEW)
    1267              :             {
    1268           12 :                 for (j = 0; j < nLoop; j++)
    1269              :                 {
    1270           12 :                     if (loop[j]->objType == DO_POST_DATA_BOUNDARY)
    1271              :                     {
    1272              :                         DumpableObject *nextobj;
    1273              : 
    1274            3 :                         nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
    1275            3 :                         repairMatViewBoundaryMultiLoop(loop[j], nextobj);
    1276            3 :                         return;
    1277              :                     }
    1278              :                 }
    1279              :             }
    1280              :         }
    1281              :     }
    1282              : 
    1283              :     /* Indirect loop involving function and data boundary */
    1284         2697 :     if (nLoop > 2)
    1285              :     {
    1286          735 :         for (i = 0; i < nLoop; i++)
    1287              :         {
    1288          578 :             if (loop[i]->objType == DO_FUNC)
    1289              :             {
    1290          118 :                 for (j = 0; j < nLoop; j++)
    1291              :                 {
    1292          113 :                     if (loop[j]->objType == DO_PRE_DATA_BOUNDARY)
    1293              :                     {
    1294              :                         DumpableObject *nextobj;
    1295              : 
    1296           38 :                         nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
    1297           38 :                         repairFunctionBoundaryMultiLoop(loop[j], nextobj);
    1298           38 :                         return;
    1299              :                     }
    1300              :                 }
    1301              :             }
    1302              :         }
    1303              :     }
    1304              : 
    1305              :     /* Table and CHECK constraint */
    1306         2659 :     if (nLoop == 2 &&
    1307         1695 :         loop[0]->objType == DO_TABLE &&
    1308         1534 :         loop[1]->objType == DO_CONSTRAINT &&
    1309          547 :         ((ConstraintInfo *) loop[1])->contype == 'c' &&
    1310          547 :         ((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
    1311              :     {
    1312          547 :         repairTableConstraintLoop(loop[0], loop[1]);
    1313          547 :         return;
    1314              :     }
    1315         2112 :     if (nLoop == 2 &&
    1316         1148 :         loop[1]->objType == DO_TABLE &&
    1317            0 :         loop[0]->objType == DO_CONSTRAINT &&
    1318            0 :         ((ConstraintInfo *) loop[0])->contype == 'c' &&
    1319            0 :         ((ConstraintInfo *) loop[0])->contable == (TableInfo *) loop[1])
    1320              :     {
    1321            0 :         repairTableConstraintLoop(loop[1], loop[0]);
    1322            0 :         return;
    1323              :     }
    1324              : 
    1325              :     /* Indirect loop involving table and CHECK constraint */
    1326         2112 :     if (nLoop > 2)
    1327              :     {
    1328          613 :         for (i = 0; i < nLoop; i++)
    1329              :         {
    1330          461 :             if (loop[i]->objType == DO_TABLE)
    1331              :             {
    1332         1226 :                 for (j = 0; j < nLoop; j++)
    1333              :                 {
    1334          922 :                     if (loop[j]->objType == DO_CONSTRAINT &&
    1335            5 :                         ((ConstraintInfo *) loop[j])->contype == 'c' &&
    1336            5 :                         ((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
    1337              :                     {
    1338            5 :                         repairTableConstraintMultiLoop(loop[i], loop[j]);
    1339            5 :                         return;
    1340              :                     }
    1341              :                 }
    1342              :             }
    1343              :         }
    1344              :     }
    1345              : 
    1346              :     /* Table and attribute default */
    1347         2107 :     if (nLoop == 2 &&
    1348         1148 :         loop[0]->objType == DO_TABLE &&
    1349          987 :         loop[1]->objType == DO_ATTRDEF &&
    1350          987 :         ((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
    1351              :     {
    1352          987 :         repairTableAttrDefLoop(loop[0], loop[1]);
    1353          987 :         return;
    1354              :     }
    1355         1120 :     if (nLoop == 2 &&
    1356          161 :         loop[1]->objType == DO_TABLE &&
    1357            0 :         loop[0]->objType == DO_ATTRDEF &&
    1358            0 :         ((AttrDefInfo *) loop[0])->adtable == (TableInfo *) loop[1])
    1359              :     {
    1360            0 :         repairTableAttrDefLoop(loop[1], loop[0]);
    1361            0 :         return;
    1362              :     }
    1363              : 
    1364              :     /* index on partitioned table and corresponding index on partition */
    1365         1120 :     if (nLoop == 2 &&
    1366          161 :         loop[0]->objType == DO_INDEX &&
    1367            0 :         loop[1]->objType == DO_INDEX)
    1368              :     {
    1369            0 :         if (((IndxInfo *) loop[0])->parentidx == loop[1]->catId.oid)
    1370              :         {
    1371            0 :             repairIndexLoop(loop[0], loop[1]);
    1372            0 :             return;
    1373              :         }
    1374            0 :         else if (((IndxInfo *) loop[1])->parentidx == loop[0]->catId.oid)
    1375              :         {
    1376            0 :             repairIndexLoop(loop[1], loop[0]);
    1377            0 :             return;
    1378              :         }
    1379              :     }
    1380              : 
    1381              :     /* Indirect loop involving table and attribute default */
    1382         1120 :     if (nLoop > 2)
    1383              :     {
    1384          304 :         for (i = 0; i < nLoop; i++)
    1385              :         {
    1386          304 :             if (loop[i]->objType == DO_TABLE)
    1387              :             {
    1388         1064 :                 for (j = 0; j < nLoop; j++)
    1389              :                 {
    1390          912 :                     if (loop[j]->objType == DO_ATTRDEF &&
    1391          304 :                         ((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
    1392              :                     {
    1393          152 :                         repairTableAttrDefMultiLoop(loop[i], loop[j]);
    1394          152 :                         return;
    1395              :                     }
    1396              :                 }
    1397              :             }
    1398              :         }
    1399              :     }
    1400              : 
    1401              :     /* Domain and CHECK or NOT NULL constraint */
    1402          968 :     if (nLoop == 2 &&
    1403          161 :         loop[0]->objType == DO_TYPE &&
    1404          161 :         loop[1]->objType == DO_CONSTRAINT &&
    1405          161 :         (((ConstraintInfo *) loop[1])->contype == 'c' ||
    1406           53 :          ((ConstraintInfo *) loop[1])->contype == 'n') &&
    1407          161 :         ((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
    1408              :     {
    1409          161 :         repairDomainConstraintLoop(loop[0], loop[1]);
    1410          161 :         return;
    1411              :     }
    1412          807 :     if (nLoop == 2 &&
    1413            0 :         loop[1]->objType == DO_TYPE &&
    1414            0 :         loop[0]->objType == DO_CONSTRAINT &&
    1415            0 :         (((ConstraintInfo *) loop[0])->contype == 'c' ||
    1416            0 :          ((ConstraintInfo *) loop[0])->contype == 'n') &&
    1417            0 :         ((ConstraintInfo *) loop[0])->condomain == (TypeInfo *) loop[1])
    1418              :     {
    1419            0 :         repairDomainConstraintLoop(loop[1], loop[0]);
    1420            0 :         return;
    1421              :     }
    1422              : 
    1423              :     /* Indirect loop involving domain and CHECK or NOT NULL constraint */
    1424          807 :     if (nLoop > 2)
    1425              :     {
    1426            0 :         for (i = 0; i < nLoop; i++)
    1427              :         {
    1428            0 :             if (loop[i]->objType == DO_TYPE)
    1429              :             {
    1430            0 :                 for (j = 0; j < nLoop; j++)
    1431              :                 {
    1432            0 :                     if (loop[j]->objType == DO_CONSTRAINT &&
    1433            0 :                         (((ConstraintInfo *) loop[j])->contype == 'c' ||
    1434            0 :                          ((ConstraintInfo *) loop[j])->contype == 'n') &&
    1435            0 :                         ((ConstraintInfo *) loop[j])->condomain == (TypeInfo *) loop[i])
    1436              :                     {
    1437            0 :                         repairDomainConstraintMultiLoop(loop[i], loop[j]);
    1438            0 :                         return;
    1439              :                     }
    1440              :                 }
    1441              :             }
    1442              :         }
    1443              :     }
    1444              : 
    1445              :     /*
    1446              :      * Loop of table with itself --- just ignore it.
    1447              :      *
    1448              :      * (Actually, what this arises from is a dependency of a table column on
    1449              :      * another column, which happened with generated columns before v15; or a
    1450              :      * dependency of a table column on the whole table, which happens with
    1451              :      * partitioning.  But we didn't pay attention to sub-object IDs while
    1452              :      * collecting the dependency data, so we can't see that here.)
    1453              :      */
    1454          807 :     if (nLoop == 1)
    1455              :     {
    1456          807 :         if (loop[0]->objType == DO_TABLE)
    1457              :         {
    1458          807 :             removeObjectDependency(loop[0], loop[0]->dumpId);
    1459          807 :             return;
    1460              :         }
    1461              :     }
    1462              : 
    1463              :     /*
    1464              :      * If all the objects are TABLE_DATA items, what we must have is a
    1465              :      * circular set of foreign key constraints (or a single self-referential
    1466              :      * table).  Print an appropriate complaint and break the loop arbitrarily.
    1467              :      */
    1468            0 :     for (i = 0; i < nLoop; i++)
    1469              :     {
    1470            0 :         if (loop[i]->objType != DO_TABLE_DATA)
    1471            0 :             break;
    1472              :     }
    1473            0 :     if (i >= nLoop)
    1474              :     {
    1475            0 :         pg_log_warning(ngettext("there are circular foreign-key constraints on this table:",
    1476              :                                 "there are circular foreign-key constraints among these tables:",
    1477              :                                 nLoop));
    1478            0 :         for (i = 0; i < nLoop; i++)
    1479            0 :             pg_log_warning_detail("%s", loop[i]->name);
    1480            0 :         pg_log_warning_hint("You might not be able to restore the dump without using --disable-triggers or temporarily dropping the constraints.");
    1481            0 :         pg_log_warning_hint("Consider using a full dump instead of a --data-only dump to avoid this problem.");
    1482            0 :         if (nLoop > 1)
    1483            0 :             removeObjectDependency(loop[0], loop[1]->dumpId);
    1484              :         else                    /* must be a self-dependency */
    1485            0 :             removeObjectDependency(loop[0], loop[0]->dumpId);
    1486            0 :         return;
    1487              :     }
    1488              : 
    1489              :     /*
    1490              :      * If we can't find a principled way to break the loop, complain and break
    1491              :      * it in an arbitrary fashion.
    1492              :      */
    1493            0 :     pg_log_warning("could not resolve dependency loop among these items:");
    1494            0 :     for (i = 0; i < nLoop; i++)
    1495              :     {
    1496              :         char        buf[1024];
    1497              : 
    1498            0 :         describeDumpableObject(loop[i], buf, sizeof(buf));
    1499            0 :         pg_log_warning_detail("%s", buf);
    1500              :     }
    1501              : 
    1502            0 :     if (nLoop > 1)
    1503            0 :         removeObjectDependency(loop[0], loop[1]->dumpId);
    1504              :     else                        /* must be a self-dependency */
    1505            0 :         removeObjectDependency(loop[0], loop[0]->dumpId);
    1506              : }
    1507              : 
    1508              : /*
    1509              :  * Describe a dumpable object usefully for errors
    1510              :  *
    1511              :  * This should probably go somewhere else...
    1512              :  */
    1513              : static void
    1514            0 : describeDumpableObject(DumpableObject *obj, char *buf, int bufsize)
    1515              : {
    1516            0 :     switch (obj->objType)
    1517              :     {
    1518            0 :         case DO_NAMESPACE:
    1519            0 :             snprintf(buf, bufsize,
    1520              :                      "SCHEMA %s  (ID %d OID %u)",
    1521              :                      obj->name, obj->dumpId, obj->catId.oid);
    1522            0 :             return;
    1523            0 :         case DO_EXTENSION:
    1524            0 :             snprintf(buf, bufsize,
    1525              :                      "EXTENSION %s  (ID %d OID %u)",
    1526              :                      obj->name, obj->dumpId, obj->catId.oid);
    1527            0 :             return;
    1528            0 :         case DO_TYPE:
    1529            0 :             snprintf(buf, bufsize,
    1530              :                      "TYPE %s  (ID %d OID %u)",
    1531              :                      obj->name, obj->dumpId, obj->catId.oid);
    1532            0 :             return;
    1533            0 :         case DO_SHELL_TYPE:
    1534            0 :             snprintf(buf, bufsize,
    1535              :                      "SHELL TYPE %s  (ID %d OID %u)",
    1536              :                      obj->name, obj->dumpId, obj->catId.oid);
    1537            0 :             return;
    1538            0 :         case DO_FUNC:
    1539            0 :             snprintf(buf, bufsize,
    1540              :                      "FUNCTION %s  (ID %d OID %u)",
    1541              :                      obj->name, obj->dumpId, obj->catId.oid);
    1542            0 :             return;
    1543            0 :         case DO_AGG:
    1544            0 :             snprintf(buf, bufsize,
    1545              :                      "AGGREGATE %s  (ID %d OID %u)",
    1546              :                      obj->name, obj->dumpId, obj->catId.oid);
    1547            0 :             return;
    1548            0 :         case DO_OPERATOR:
    1549            0 :             snprintf(buf, bufsize,
    1550              :                      "OPERATOR %s  (ID %d OID %u)",
    1551              :                      obj->name, obj->dumpId, obj->catId.oid);
    1552            0 :             return;
    1553            0 :         case DO_ACCESS_METHOD:
    1554            0 :             snprintf(buf, bufsize,
    1555              :                      "ACCESS METHOD %s  (ID %d OID %u)",
    1556              :                      obj->name, obj->dumpId, obj->catId.oid);
    1557            0 :             return;
    1558            0 :         case DO_OPCLASS:
    1559            0 :             snprintf(buf, bufsize,
    1560              :                      "OPERATOR CLASS %s  (ID %d OID %u)",
    1561              :                      obj->name, obj->dumpId, obj->catId.oid);
    1562            0 :             return;
    1563            0 :         case DO_OPFAMILY:
    1564            0 :             snprintf(buf, bufsize,
    1565              :                      "OPERATOR FAMILY %s  (ID %d OID %u)",
    1566              :                      obj->name, obj->dumpId, obj->catId.oid);
    1567            0 :             return;
    1568            0 :         case DO_COLLATION:
    1569            0 :             snprintf(buf, bufsize,
    1570              :                      "COLLATION %s  (ID %d OID %u)",
    1571              :                      obj->name, obj->dumpId, obj->catId.oid);
    1572            0 :             return;
    1573            0 :         case DO_CONVERSION:
    1574            0 :             snprintf(buf, bufsize,
    1575              :                      "CONVERSION %s  (ID %d OID %u)",
    1576              :                      obj->name, obj->dumpId, obj->catId.oid);
    1577            0 :             return;
    1578            0 :         case DO_TABLE:
    1579            0 :             snprintf(buf, bufsize,
    1580              :                      "TABLE %s  (ID %d OID %u)",
    1581              :                      obj->name, obj->dumpId, obj->catId.oid);
    1582            0 :             return;
    1583            0 :         case DO_TABLE_ATTACH:
    1584            0 :             snprintf(buf, bufsize,
    1585              :                      "TABLE ATTACH %s  (ID %d)",
    1586              :                      obj->name, obj->dumpId);
    1587            0 :             return;
    1588            0 :         case DO_ATTRDEF:
    1589            0 :             snprintf(buf, bufsize,
    1590              :                      "ATTRDEF %s.%s  (ID %d OID %u)",
    1591            0 :                      ((AttrDefInfo *) obj)->adtable->dobj.name,
    1592            0 :                      ((AttrDefInfo *) obj)->adtable->attnames[((AttrDefInfo *) obj)->adnum - 1],
    1593              :                      obj->dumpId, obj->catId.oid);
    1594            0 :             return;
    1595            0 :         case DO_INDEX:
    1596            0 :             snprintf(buf, bufsize,
    1597              :                      "INDEX %s  (ID %d OID %u)",
    1598              :                      obj->name, obj->dumpId, obj->catId.oid);
    1599            0 :             return;
    1600            0 :         case DO_INDEX_ATTACH:
    1601            0 :             snprintf(buf, bufsize,
    1602              :                      "INDEX ATTACH %s  (ID %d)",
    1603              :                      obj->name, obj->dumpId);
    1604            0 :             return;
    1605            0 :         case DO_STATSEXT:
    1606            0 :             snprintf(buf, bufsize,
    1607              :                      "STATISTICS %s  (ID %d OID %u)",
    1608              :                      obj->name, obj->dumpId, obj->catId.oid);
    1609            0 :             return;
    1610            0 :         case DO_REFRESH_MATVIEW:
    1611            0 :             snprintf(buf, bufsize,
    1612              :                      "REFRESH MATERIALIZED VIEW %s  (ID %d OID %u)",
    1613              :                      obj->name, obj->dumpId, obj->catId.oid);
    1614            0 :             return;
    1615            0 :         case DO_RULE:
    1616            0 :             snprintf(buf, bufsize,
    1617              :                      "RULE %s  (ID %d OID %u)",
    1618              :                      obj->name, obj->dumpId, obj->catId.oid);
    1619            0 :             return;
    1620            0 :         case DO_TRIGGER:
    1621            0 :             snprintf(buf, bufsize,
    1622              :                      "TRIGGER %s  (ID %d OID %u)",
    1623              :                      obj->name, obj->dumpId, obj->catId.oid);
    1624            0 :             return;
    1625            0 :         case DO_EVENT_TRIGGER:
    1626            0 :             snprintf(buf, bufsize,
    1627              :                      "EVENT TRIGGER %s (ID %d OID %u)",
    1628              :                      obj->name, obj->dumpId, obj->catId.oid);
    1629            0 :             return;
    1630            0 :         case DO_CONSTRAINT:
    1631            0 :             snprintf(buf, bufsize,
    1632              :                      "CONSTRAINT %s  (ID %d OID %u)",
    1633              :                      obj->name, obj->dumpId, obj->catId.oid);
    1634            0 :             return;
    1635            0 :         case DO_FK_CONSTRAINT:
    1636            0 :             snprintf(buf, bufsize,
    1637              :                      "FK CONSTRAINT %s  (ID %d OID %u)",
    1638              :                      obj->name, obj->dumpId, obj->catId.oid);
    1639            0 :             return;
    1640            0 :         case DO_PROCLANG:
    1641            0 :             snprintf(buf, bufsize,
    1642              :                      "PROCEDURAL LANGUAGE %s  (ID %d OID %u)",
    1643              :                      obj->name, obj->dumpId, obj->catId.oid);
    1644            0 :             return;
    1645            0 :         case DO_CAST:
    1646            0 :             snprintf(buf, bufsize,
    1647              :                      "CAST %u to %u  (ID %d OID %u)",
    1648              :                      ((CastInfo *) obj)->castsource,
    1649              :                      ((CastInfo *) obj)->casttarget,
    1650              :                      obj->dumpId, obj->catId.oid);
    1651            0 :             return;
    1652            0 :         case DO_TRANSFORM:
    1653            0 :             snprintf(buf, bufsize,
    1654              :                      "TRANSFORM %u lang %u  (ID %d OID %u)",
    1655              :                      ((TransformInfo *) obj)->trftype,
    1656              :                      ((TransformInfo *) obj)->trflang,
    1657              :                      obj->dumpId, obj->catId.oid);
    1658            0 :             return;
    1659            0 :         case DO_TABLE_DATA:
    1660            0 :             snprintf(buf, bufsize,
    1661              :                      "TABLE DATA %s  (ID %d OID %u)",
    1662              :                      obj->name, obj->dumpId, obj->catId.oid);
    1663            0 :             return;
    1664            0 :         case DO_SEQUENCE_SET:
    1665            0 :             snprintf(buf, bufsize,
    1666              :                      "SEQUENCE SET %s  (ID %d OID %u)",
    1667              :                      obj->name, obj->dumpId, obj->catId.oid);
    1668            0 :             return;
    1669            0 :         case DO_DUMMY_TYPE:
    1670            0 :             snprintf(buf, bufsize,
    1671              :                      "DUMMY TYPE %s  (ID %d OID %u)",
    1672              :                      obj->name, obj->dumpId, obj->catId.oid);
    1673            0 :             return;
    1674            0 :         case DO_TSPARSER:
    1675            0 :             snprintf(buf, bufsize,
    1676              :                      "TEXT SEARCH PARSER %s  (ID %d OID %u)",
    1677              :                      obj->name, obj->dumpId, obj->catId.oid);
    1678            0 :             return;
    1679            0 :         case DO_TSDICT:
    1680            0 :             snprintf(buf, bufsize,
    1681              :                      "TEXT SEARCH DICTIONARY %s  (ID %d OID %u)",
    1682              :                      obj->name, obj->dumpId, obj->catId.oid);
    1683            0 :             return;
    1684            0 :         case DO_TSTEMPLATE:
    1685            0 :             snprintf(buf, bufsize,
    1686              :                      "TEXT SEARCH TEMPLATE %s  (ID %d OID %u)",
    1687              :                      obj->name, obj->dumpId, obj->catId.oid);
    1688            0 :             return;
    1689            0 :         case DO_TSCONFIG:
    1690            0 :             snprintf(buf, bufsize,
    1691              :                      "TEXT SEARCH CONFIGURATION %s  (ID %d OID %u)",
    1692              :                      obj->name, obj->dumpId, obj->catId.oid);
    1693            0 :             return;
    1694            0 :         case DO_FDW:
    1695            0 :             snprintf(buf, bufsize,
    1696              :                      "FOREIGN DATA WRAPPER %s  (ID %d OID %u)",
    1697              :                      obj->name, obj->dumpId, obj->catId.oid);
    1698            0 :             return;
    1699            0 :         case DO_FOREIGN_SERVER:
    1700            0 :             snprintf(buf, bufsize,
    1701              :                      "FOREIGN SERVER %s  (ID %d OID %u)",
    1702              :                      obj->name, obj->dumpId, obj->catId.oid);
    1703            0 :             return;
    1704            0 :         case DO_DEFAULT_ACL:
    1705            0 :             snprintf(buf, bufsize,
    1706              :                      "DEFAULT ACL %s  (ID %d OID %u)",
    1707              :                      obj->name, obj->dumpId, obj->catId.oid);
    1708            0 :             return;
    1709            0 :         case DO_LARGE_OBJECT:
    1710            0 :             snprintf(buf, bufsize,
    1711              :                      "LARGE OBJECT  (ID %d OID %u)",
    1712              :                      obj->dumpId, obj->catId.oid);
    1713            0 :             return;
    1714            0 :         case DO_LARGE_OBJECT_DATA:
    1715            0 :             snprintf(buf, bufsize,
    1716              :                      "LARGE OBJECT DATA  (ID %d)",
    1717              :                      obj->dumpId);
    1718            0 :             return;
    1719            0 :         case DO_POLICY:
    1720            0 :             snprintf(buf, bufsize,
    1721              :                      "POLICY (ID %d OID %u)",
    1722              :                      obj->dumpId, obj->catId.oid);
    1723            0 :             return;
    1724            0 :         case DO_PUBLICATION:
    1725            0 :             snprintf(buf, bufsize,
    1726              :                      "PUBLICATION (ID %d OID %u)",
    1727              :                      obj->dumpId, obj->catId.oid);
    1728            0 :             return;
    1729            0 :         case DO_PUBLICATION_REL:
    1730            0 :             snprintf(buf, bufsize,
    1731              :                      "PUBLICATION TABLE (ID %d OID %u)",
    1732              :                      obj->dumpId, obj->catId.oid);
    1733            0 :             return;
    1734            0 :         case DO_PUBLICATION_TABLE_IN_SCHEMA:
    1735            0 :             snprintf(buf, bufsize,
    1736              :                      "PUBLICATION TABLES IN SCHEMA (ID %d OID %u)",
    1737              :                      obj->dumpId, obj->catId.oid);
    1738            0 :             return;
    1739            0 :         case DO_SUBSCRIPTION:
    1740            0 :             snprintf(buf, bufsize,
    1741              :                      "SUBSCRIPTION (ID %d OID %u)",
    1742              :                      obj->dumpId, obj->catId.oid);
    1743            0 :             return;
    1744            0 :         case DO_SUBSCRIPTION_REL:
    1745            0 :             snprintf(buf, bufsize,
    1746              :                      "SUBSCRIPTION TABLE (ID %d OID %u)",
    1747              :                      obj->dumpId, obj->catId.oid);
    1748            0 :             return;
    1749            0 :         case DO_PRE_DATA_BOUNDARY:
    1750            0 :             snprintf(buf, bufsize,
    1751              :                      "PRE-DATA BOUNDARY  (ID %d)",
    1752              :                      obj->dumpId);
    1753            0 :             return;
    1754            0 :         case DO_POST_DATA_BOUNDARY:
    1755            0 :             snprintf(buf, bufsize,
    1756              :                      "POST-DATA BOUNDARY  (ID %d)",
    1757              :                      obj->dumpId);
    1758            0 :             return;
    1759            0 :         case DO_REL_STATS:
    1760            0 :             snprintf(buf, bufsize,
    1761              :                      "RELATION STATISTICS FOR %s  (ID %d OID %u)",
    1762              :                      obj->name, obj->dumpId, obj->catId.oid);
    1763            0 :             return;
    1764              :     }
    1765              :     /* shouldn't get here */
    1766            0 :     snprintf(buf, bufsize,
    1767              :              "object type %d  (ID %d OID %u)",
    1768            0 :              (int) obj->objType,
    1769              :              obj->dumpId, obj->catId.oid);
    1770              : }
    1771              : 
    1772              : /* binaryheap comparator that compares "a" and "b" as integers */
    1773              : static int
    1774     38328354 : int_cmp(void *a, void *b, void *arg)
    1775              : {
    1776     38328354 :     int         ai = (int) (intptr_t) a;
    1777     38328354 :     int         bi = (int) (intptr_t) b;
    1778              : 
    1779     38328354 :     return pg_cmp_s32(ai, bi);
    1780              : }
        

Generated by: LCOV version 2.0-1