LCOV - code coverage report
Current view: top level - src/bin/pg_dump - pg_dump_sort.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 0.0 % 758 0
Test Date: 2026-01-26 10:56:24 Functions: 0.0 % 23 0
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            0 : sortDumpableObjectsByTypeName(DumpableObject **objs, int numObjs)
     193              : {
     194            0 :         if (numObjs > 1)
     195            0 :                 qsort(objs, numObjs, sizeof(DumpableObject *),
     196              :                           DOTypeNameCompare);
     197            0 : }
     198              : 
     199              : static int
     200            0 : DOTypeNameCompare(const void *p1, const void *p2)
     201              : {
     202            0 :         DumpableObject *obj1 = *(DumpableObject *const *) p1;
     203            0 :         DumpableObject *obj2 = *(DumpableObject *const *) p2;
     204            0 :         int                     cmpval;
     205              : 
     206              :         /* Sort by type's priority */
     207            0 :         cmpval = dbObjectTypePriority[obj1->objType] -
     208            0 :                 dbObjectTypePriority[obj2->objType];
     209              : 
     210            0 :         if (cmpval != 0)
     211            0 :                 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            0 :         if (obj1->namespace)
     219              :         {
     220            0 :                 if (obj2->namespace)
     221              :                 {
     222            0 :                         cmpval = strcmp(obj1->namespace->dobj.name,
     223            0 :                                                         obj2->namespace->dobj.name);
     224            0 :                         if (cmpval != 0)
     225            0 :                                 return cmpval;
     226            0 :                 }
     227              :                 else
     228            0 :                         return -1;
     229            0 :         }
     230            0 :         else if (obj2->namespace)
     231            0 :                 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            0 :         cmpval = strcmp(obj1->name, obj2->name);
     242            0 :         if (cmpval != 0)
     243            0 :                 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            0 :         cmpval = obj1->objType - obj2->objType;
     250            0 :         if (cmpval != 0)
     251            0 :                 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            0 :         if (obj1->objType == DO_FUNC || obj1->objType == DO_AGG)
     267              :         {
     268            0 :                 FuncInfo   *fobj1 = *(FuncInfo *const *) p1;
     269            0 :                 FuncInfo   *fobj2 = *(FuncInfo *const *) p2;
     270            0 :                 int                     i;
     271              : 
     272              :                 /* Sort by number of arguments, then argument type names */
     273            0 :                 cmpval = fobj1->nargs - fobj2->nargs;
     274            0 :                 if (cmpval != 0)
     275            0 :                         return cmpval;
     276            0 :                 for (i = 0; i < fobj1->nargs; i++)
     277              :                 {
     278            0 :                         cmpval = pgTypeNameCompare(fobj1->argtypes[i],
     279            0 :                                                                            fobj2->argtypes[i]);
     280            0 :                         if (cmpval != 0)
     281            0 :                                 return cmpval;
     282            0 :                 }
     283            0 :         }
     284            0 :         else if (obj1->objType == DO_OPERATOR)
     285              :         {
     286            0 :                 OprInfo    *oobj1 = *(OprInfo *const *) p1;
     287            0 :                 OprInfo    *oobj2 = *(OprInfo *const *) p2;
     288              : 
     289              :                 /* oprkind is 'l', 'r', or 'b'; this sorts prefix, postfix, infix */
     290            0 :                 cmpval = (oobj2->oprkind - oobj1->oprkind);
     291            0 :                 if (cmpval != 0)
     292            0 :                         return cmpval;
     293              :                 /* Within an oprkind, sort by argument type names */
     294            0 :                 cmpval = pgTypeNameCompare(oobj1->oprleft, oobj2->oprleft);
     295            0 :                 if (cmpval != 0)
     296            0 :                         return cmpval;
     297            0 :                 cmpval = pgTypeNameCompare(oobj1->oprright, oobj2->oprright);
     298            0 :                 if (cmpval != 0)
     299            0 :                         return cmpval;
     300            0 :         }
     301            0 :         else if (obj1->objType == DO_OPCLASS)
     302              :         {
     303            0 :                 OpclassInfo *opcobj1 = *(OpclassInfo *const *) p1;
     304            0 :                 OpclassInfo *opcobj2 = *(OpclassInfo *const *) p2;
     305              : 
     306              :                 /* Sort by access method name, per pg_opclass_am_name_nsp_index */
     307            0 :                 cmpval = accessMethodNameCompare(opcobj1->opcmethod,
     308            0 :                                                                                  opcobj2->opcmethod);
     309            0 :                 if (cmpval != 0)
     310            0 :                         return cmpval;
     311            0 :         }
     312            0 :         else if (obj1->objType == DO_OPFAMILY)
     313              :         {
     314            0 :                 OpfamilyInfo *opfobj1 = *(OpfamilyInfo *const *) p1;
     315            0 :                 OpfamilyInfo *opfobj2 = *(OpfamilyInfo *const *) p2;
     316              : 
     317              :                 /* Sort by access method name, per pg_opfamily_am_name_nsp_index */
     318            0 :                 cmpval = accessMethodNameCompare(opfobj1->opfmethod,
     319            0 :                                                                                  opfobj2->opfmethod);
     320            0 :                 if (cmpval != 0)
     321            0 :                         return cmpval;
     322            0 :         }
     323            0 :         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            0 :         }
     345            0 :         else if (obj1->objType == DO_ATTRDEF)
     346              :         {
     347            0 :                 AttrDefInfo *adobj1 = *(AttrDefInfo *const *) p1;
     348            0 :                 AttrDefInfo *adobj2 = *(AttrDefInfo *const *) p2;
     349              : 
     350              :                 /* Sort by attribute number */
     351            0 :                 cmpval = (adobj1->adnum - adobj2->adnum);
     352            0 :                 if (cmpval != 0)
     353            0 :                         return cmpval;
     354            0 :         }
     355            0 :         else if (obj1->objType == DO_POLICY)
     356              :         {
     357            0 :                 PolicyInfo *pobj1 = *(PolicyInfo *const *) p1;
     358            0 :                 PolicyInfo *pobj2 = *(PolicyInfo *const *) p2;
     359              : 
     360              :                 /* Sort by table name (table namespace was considered already) */
     361            0 :                 cmpval = strcmp(pobj1->poltable->dobj.name,
     362            0 :                                                 pobj2->poltable->dobj.name);
     363            0 :                 if (cmpval != 0)
     364            0 :                         return cmpval;
     365            0 :         }
     366            0 :         else if (obj1->objType == DO_RULE)
     367              :         {
     368            0 :                 RuleInfo   *robj1 = *(RuleInfo *const *) p1;
     369            0 :                 RuleInfo   *robj2 = *(RuleInfo *const *) p2;
     370              : 
     371              :                 /* Sort by table name (table namespace was considered already) */
     372            0 :                 cmpval = strcmp(robj1->ruletable->dobj.name,
     373            0 :                                                 robj2->ruletable->dobj.name);
     374            0 :                 if (cmpval != 0)
     375            0 :                         return cmpval;
     376            0 :         }
     377            0 :         else if (obj1->objType == DO_TRIGGER)
     378              :         {
     379            0 :                 TriggerInfo *tobj1 = *(TriggerInfo *const *) p1;
     380            0 :                 TriggerInfo *tobj2 = *(TriggerInfo *const *) p2;
     381              : 
     382              :                 /* Sort by table name (table namespace was considered already) */
     383            0 :                 cmpval = strcmp(tobj1->tgtable->dobj.name,
     384            0 :                                                 tobj2->tgtable->dobj.name);
     385            0 :                 if (cmpval != 0)
     386            0 :                         return cmpval;
     387            0 :         }
     388            0 :         else if (obj1->objType == DO_CONSTRAINT ||
     389            0 :                          obj1->objType == DO_FK_CONSTRAINT)
     390              :         {
     391            0 :                 ConstraintInfo *robj1 = *(ConstraintInfo *const *) p1;
     392            0 :                 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            0 :                 if (robj1->condomain)
     399              :                 {
     400            0 :                         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            0 :                         }
     408              :                         else
     409            0 :                                 return PRIO_TYPE - PRIO_TABLE;
     410            0 :                 }
     411            0 :                 else if (robj2->condomain)
     412            0 :                         return PRIO_TABLE - PRIO_TYPE;
     413              :                 else
     414              :                 {
     415              :                         /* Sort by table name (table namespace was considered already) */
     416            0 :                         cmpval = strcmp(robj1->contable->dobj.name,
     417            0 :                                                         robj2->contable->dobj.name);
     418            0 :                         if (cmpval != 0)
     419            0 :                                 return cmpval;
     420              :                 }
     421            0 :         }
     422            0 :         else if (obj1->objType == DO_DEFAULT_ACL)
     423              :         {
     424            0 :                 DefaultACLInfo *daclobj1 = *(DefaultACLInfo *const *) p1;
     425            0 :                 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            0 :                 cmpval = strcmp(daclobj1->defaclrole, daclobj2->defaclrole);
     432            0 :                 if (cmpval != 0)
     433            0 :                         return cmpval;
     434            0 :         }
     435            0 :         else if (obj1->objType == DO_PUBLICATION_REL)
     436              :         {
     437            0 :                 PublicationRelInfo *probj1 = *(PublicationRelInfo *const *) p1;
     438            0 :                 PublicationRelInfo *probj2 = *(PublicationRelInfo *const *) p2;
     439              : 
     440              :                 /* Sort by publication name, since (namespace, name) match the rel */
     441            0 :                 cmpval = strcmp(probj1->publication->dobj.name,
     442            0 :                                                 probj2->publication->dobj.name);
     443            0 :                 if (cmpval != 0)
     444            0 :                         return cmpval;
     445            0 :         }
     446            0 :         else if (obj1->objType == DO_PUBLICATION_TABLE_IN_SCHEMA)
     447              :         {
     448            0 :                 PublicationSchemaInfo *psobj1 = *(PublicationSchemaInfo *const *) p1;
     449            0 :                 PublicationSchemaInfo *psobj2 = *(PublicationSchemaInfo *const *) p2;
     450              : 
     451              :                 /* Sort by publication name, since ->name is just nspname */
     452            0 :                 cmpval = strcmp(psobj1->publication->dobj.name,
     453            0 :                                                 psobj2->publication->dobj.name);
     454            0 :                 if (cmpval != 0)
     455            0 :                         return cmpval;
     456            0 :         }
     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            0 :         }
     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            0 :         Assert(false);
     480              :         return oidcmp(obj1->catId.oid, obj2->catId.oid);
     481            0 : }
     482              : 
     483              : /* Compare two OID-identified pg_type values by nspname, then by typname. */
     484              : static int
     485            0 : pgTypeNameCompare(Oid typid1, Oid typid2)
     486              : {
     487            0 :         TypeInfo   *typobj1;
     488            0 :         TypeInfo   *typobj2;
     489            0 :         int                     cmpval;
     490              : 
     491            0 :         if (typid1 == typid2)
     492            0 :                 return 0;
     493              : 
     494            0 :         typobj1 = findTypeByOid(typid1);
     495            0 :         typobj2 = findTypeByOid(typid2);
     496              : 
     497            0 :         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            0 :                 Assert(false);
     512              :                 return 0;
     513              :         }
     514              : 
     515            0 :         if (!typobj1->dobj.namespace || !typobj2->dobj.namespace)
     516            0 :                 Assert(false);                  /* catalog corruption */
     517              :         else
     518              :         {
     519            0 :                 cmpval = strcmp(typobj1->dobj.namespace->dobj.name,
     520            0 :                                                 typobj2->dobj.namespace->dobj.name);
     521            0 :                 if (cmpval != 0)
     522            0 :                         return cmpval;
     523              :         }
     524            0 :         return strcmp(typobj1->dobj.name, typobj2->dobj.name);
     525            0 : }
     526              : 
     527              : /* Compare two OID-identified pg_am values by amname. */
     528              : static int
     529            0 : accessMethodNameCompare(Oid am1, Oid am2)
     530              : {
     531            0 :         AccessMethodInfo *amobj1;
     532            0 :         AccessMethodInfo *amobj2;
     533              : 
     534            0 :         if (am1 == am2)
     535            0 :                 return 0;
     536              : 
     537            0 :         amobj1 = findAccessMethodByOid(am1);
     538            0 :         amobj2 = findAccessMethodByOid(am2);
     539              : 
     540            0 :         if (!amobj1 || !amobj2)
     541              :         {
     542              :                 /* catalog corruption: handle like pgTypeNameCompare() does */
     543            0 :                 Assert(false);
     544              :                 return 0;
     545              :         }
     546              : 
     547            0 :         return strcmp(amobj1->dobj.name, amobj2->dobj.name);
     548            0 : }
     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            0 : sortDumpableObjects(DumpableObject **objs, int numObjs,
     560              :                                         DumpId preBoundaryId, DumpId postBoundaryId)
     561              : {
     562            0 :         DumpableObject **ordering;
     563            0 :         int                     nOrdering;
     564              : 
     565            0 :         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            0 :         preDataBoundId = preBoundaryId;
     573            0 :         postDataBoundId = postBoundaryId;
     574              : 
     575            0 :         ordering = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *));
     576            0 :         while (!TopoSort(objs, numObjs, ordering, &nOrdering))
     577            0 :                 findDependencyLoops(ordering, nOrdering, numObjs);
     578              : 
     579            0 :         memcpy(objs, ordering, numObjs * sizeof(DumpableObject *));
     580              : 
     581            0 :         free(ordering);
     582            0 : }
     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            0 : TopoSort(DumpableObject **objs,
     612              :                  int numObjs,
     613              :                  DumpableObject **ordering, /* output argument */
     614              :                  int *nOrdering)                /* output argument */
     615              : {
     616            0 :         DumpId          maxDumpId = getMaxDumpId();
     617            0 :         binaryheap *pendingHeap;
     618            0 :         int                *beforeConstraints;
     619            0 :         int                *idMap;
     620            0 :         DumpableObject *obj;
     621            0 :         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            0 :         *nOrdering = numObjs;           /* for success return */
     639              : 
     640              :         /* Eliminate the null case */
     641            0 :         if (numObjs <= 0)
     642            0 :                 return true;
     643              : 
     644              :         /* Create workspace for the above-described heap */
     645            0 :         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            0 :         beforeConstraints = (int *) pg_malloc0((maxDumpId + 1) * sizeof(int));
     655            0 :         idMap = (int *) pg_malloc((maxDumpId + 1) * sizeof(int));
     656            0 :         for (i = 0; i < numObjs; i++)
     657              :         {
     658            0 :                 obj = objs[i];
     659            0 :                 j = obj->dumpId;
     660            0 :                 if (j <= 0 || j > maxDumpId)
     661            0 :                         pg_fatal("invalid dumpId %d", j);
     662            0 :                 idMap[j] = i;
     663            0 :                 for (j = 0; j < obj->nDeps; j++)
     664              :                 {
     665            0 :                         k = obj->dependencies[j];
     666            0 :                         if (k <= 0 || k > maxDumpId)
     667            0 :                                 pg_fatal("invalid dependency %d", k);
     668            0 :                         beforeConstraints[k]++;
     669            0 :                 }
     670            0 :         }
     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            0 :         for (i = numObjs; --i >= 0;)
     681              :         {
     682            0 :                 if (beforeConstraints[objs[i]->dumpId] == 0)
     683            0 :                         binaryheap_add_unordered(pendingHeap, (void *) (intptr_t) i);
     684              :         }
     685            0 :         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            0 :         i = numObjs;
     703            0 :         while (!binaryheap_empty(pendingHeap))
     704              :         {
     705              :                 /* Select object to output by removing largest heap member */
     706            0 :                 j = (int) (intptr_t) binaryheap_remove_first(pendingHeap);
     707            0 :                 obj = objs[j];
     708              :                 /* Output candidate to ordering[] */
     709            0 :                 ordering[--i] = obj;
     710              :                 /* Update beforeConstraints counts of its predecessors */
     711            0 :                 for (k = 0; k < obj->nDeps; k++)
     712              :                 {
     713            0 :                         int                     id = obj->dependencies[k];
     714              : 
     715            0 :                         if ((--beforeConstraints[id]) == 0)
     716            0 :                                 binaryheap_add(pendingHeap, (void *) (intptr_t) idMap[id]);
     717            0 :                 }
     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            0 :         if (i != 0)
     725              :         {
     726            0 :                 k = 0;
     727            0 :                 for (j = 1; j <= maxDumpId; j++)
     728              :                 {
     729            0 :                         if (beforeConstraints[j] != 0)
     730            0 :                                 ordering[k++] = objs[idMap[j]];
     731            0 :                 }
     732            0 :                 *nOrdering = k;
     733            0 :         }
     734              : 
     735              :         /* Done */
     736            0 :         binaryheap_free(pendingHeap);
     737            0 :         free(beforeConstraints);
     738            0 :         free(idMap);
     739              : 
     740            0 :         return (i == 0);
     741            0 : }
     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            0 : 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            0 :         bool       *processed;
     785            0 :         DumpId     *searchFailed;
     786            0 :         DumpableObject **workspace;
     787            0 :         bool            fixedloop;
     788            0 :         int                     i;
     789              : 
     790            0 :         processed = (bool *) pg_malloc0((getMaxDumpId() + 1) * sizeof(bool));
     791            0 :         searchFailed = (DumpId *) pg_malloc0((getMaxDumpId() + 1) * sizeof(DumpId));
     792            0 :         workspace = (DumpableObject **) pg_malloc(totObjs * sizeof(DumpableObject *));
     793            0 :         fixedloop = false;
     794              : 
     795            0 :         for (i = 0; i < nObjs; i++)
     796              :         {
     797            0 :                 DumpableObject *obj = objs[i];
     798            0 :                 int                     looplen;
     799            0 :                 int                     j;
     800              : 
     801            0 :                 looplen = findLoop(obj,
     802            0 :                                                    obj->dumpId,
     803            0 :                                                    processed,
     804            0 :                                                    searchFailed,
     805            0 :                                                    workspace,
     806              :                                                    0);
     807              : 
     808            0 :                 if (looplen > 0)
     809              :                 {
     810              :                         /* Found a loop, repair it */
     811            0 :                         repairDependencyLoop(workspace, looplen);
     812            0 :                         fixedloop = true;
     813              :                         /* Mark loop members as processed */
     814            0 :                         for (j = 0; j < looplen; j++)
     815            0 :                                 processed[workspace[j]->dumpId] = true;
     816            0 :                 }
     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            0 :                         processed[obj->dumpId] = true;
     826              :                 }
     827            0 :         }
     828              : 
     829              :         /* We'd better have fixed at least one loop */
     830            0 :         if (!fixedloop)
     831            0 :                 pg_fatal("could not identify dependency loop");
     832              : 
     833            0 :         free(workspace);
     834            0 :         free(searchFailed);
     835            0 :         free(processed);
     836            0 : }
     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            0 : findLoop(DumpableObject *obj,
     857              :                  DumpId startPoint,
     858              :                  bool *processed,
     859              :                  DumpId *searchFailed,
     860              :                  DumpableObject **workspace,
     861              :                  int depth)
     862              : {
     863            0 :         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            0 :         if (processed[obj->dumpId])
     870            0 :                 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            0 :         if (searchFailed[obj->dumpId] == startPoint)
     877            0 :                 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            0 :         for (i = 0; i < depth; i++)
     886              :         {
     887            0 :                 if (workspace[i] == obj)
     888            0 :                         return 0;
     889            0 :         }
     890              : 
     891              :         /*
     892              :          * Okay, tentatively add obj to workspace
     893              :          */
     894            0 :         workspace[depth++] = obj;
     895              : 
     896              :         /*
     897              :          * See if we've found a loop back to the desired startPoint; if so, done
     898              :          */
     899            0 :         for (i = 0; i < obj->nDeps; i++)
     900              :         {
     901            0 :                 if (obj->dependencies[i] == startPoint)
     902            0 :                         return depth;
     903            0 :         }
     904              : 
     905              :         /*
     906              :          * Recurse down each outgoing branch
     907              :          */
     908            0 :         for (i = 0; i < obj->nDeps; i++)
     909              :         {
     910            0 :                 DumpableObject *nextobj = findObjectByDumpId(obj->dependencies[i]);
     911            0 :                 int                     newDepth;
     912              : 
     913            0 :                 if (!nextobj)
     914            0 :                         continue;                       /* ignore dependencies on undumped objects */
     915            0 :                 newDepth = findLoop(nextobj,
     916            0 :                                                         startPoint,
     917            0 :                                                         processed,
     918            0 :                                                         searchFailed,
     919            0 :                                                         workspace,
     920            0 :                                                         depth);
     921            0 :                 if (newDepth > 0)
     922            0 :                         return newDepth;
     923            0 :         }
     924              : 
     925              :         /*
     926              :          * Remember there is no path from here back to startPoint
     927              :          */
     928            0 :         searchFailed[obj->dumpId] = startPoint;
     929              : 
     930            0 :         return 0;
     931            0 : }
     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            0 : repairTypeFuncLoop(DumpableObject *typeobj, DumpableObject *funcobj)
     942              : {
     943            0 :         TypeInfo   *typeInfo = (TypeInfo *) typeobj;
     944              : 
     945              :         /* remove function's dependency on type */
     946            0 :         removeObjectDependency(funcobj, typeobj->dumpId);
     947              : 
     948              :         /* add function's dependency on shell type, instead */
     949            0 :         if (typeInfo->shellType)
     950              :         {
     951            0 :                 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            0 :                 if (funcobj->dump)
     959            0 :                         typeInfo->shellType->dobj.dump = funcobj->dump |
     960              :                                 DUMP_COMPONENT_DEFINITION;
     961            0 :         }
     962            0 : }
     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            0 : repairViewRuleLoop(DumpableObject *viewobj,
     973              :                                    DumpableObject *ruleobj)
     974              : {
     975              :         /* remove rule's dependency on view */
     976            0 :         removeObjectDependency(ruleobj, viewobj->dumpId);
     977              :         /* flags on the two objects are already set correctly for this case */
     978            0 : }
     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            0 : repairViewRuleMultiLoop(DumpableObject *viewobj,
     993              :                                                 DumpableObject *ruleobj)
     994              : {
     995            0 :         TableInfo  *viewinfo = (TableInfo *) viewobj;
     996            0 :         RuleInfo   *ruleinfo = (RuleInfo *) ruleobj;
     997              : 
     998              :         /* remove view's dependency on rule */
     999            0 :         removeObjectDependency(viewobj, ruleobj->dumpId);
    1000              :         /* mark view to be printed with a dummy definition */
    1001            0 :         viewinfo->dummy_view = true;
    1002              :         /* mark rule as needing its own dump */
    1003            0 :         ruleinfo->separate = true;
    1004              :         /* put back rule's dependency on view */
    1005            0 :         addObjectDependency(ruleobj, viewobj->dumpId);
    1006              :         /* now that rule is separate, it must be post-data */
    1007            0 :         addObjectDependency(ruleobj, postDataBoundId);
    1008            0 : }
    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            0 : repairMatViewBoundaryMultiLoop(DumpableObject *boundaryobj,
    1027              :                                                            DumpableObject *nextobj)
    1028              : {
    1029              :         /* remove boundary's dependency on object after it in loop */
    1030            0 :         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            0 :         if (nextobj->objType == DO_TABLE)
    1037              :         {
    1038            0 :                 TableInfo  *nextinfo = (TableInfo *) nextobj;
    1039              : 
    1040            0 :                 if (nextinfo->relkind == RELKIND_MATVIEW)
    1041            0 :                         nextinfo->postponed_def = true;
    1042            0 :         }
    1043            0 :         else if (nextobj->objType == DO_REL_STATS)
    1044              :         {
    1045            0 :                 RelStatsInfo *nextinfo = (RelStatsInfo *) nextobj;
    1046              : 
    1047            0 :                 if (nextinfo->relkind == RELKIND_MATVIEW)
    1048            0 :                         nextinfo->section = SECTION_POST_DATA;
    1049            0 :         }
    1050            0 : }
    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            0 : repairFunctionBoundaryMultiLoop(DumpableObject *boundaryobj,
    1061              :                                                                 DumpableObject *nextobj)
    1062              : {
    1063              :         /* remove boundary's dependency on object after it in loop */
    1064            0 :         removeObjectDependency(boundaryobj, nextobj->dumpId);
    1065              :         /* if that object is a function, mark it as postponed into post-data */
    1066            0 :         if (nextobj->objType == DO_FUNC)
    1067              :         {
    1068            0 :                 FuncInfo   *nextinfo = (FuncInfo *) nextobj;
    1069              : 
    1070            0 :                 nextinfo->postponed_def = true;
    1071            0 :         }
    1072            0 : }
    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            0 : repairTableConstraintLoop(DumpableObject *tableobj,
    1082              :                                                   DumpableObject *constraintobj)
    1083              : {
    1084              :         /* remove constraint's dependency on table */
    1085            0 :         removeObjectDependency(constraintobj, tableobj->dumpId);
    1086            0 : }
    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            0 : repairTableConstraintMultiLoop(DumpableObject *tableobj,
    1099              :                                                            DumpableObject *constraintobj)
    1100              : {
    1101              :         /* remove table's dependency on constraint */
    1102            0 :         removeObjectDependency(tableobj, constraintobj->dumpId);
    1103              :         /* mark constraint as needing its own dump */
    1104            0 :         ((ConstraintInfo *) constraintobj)->separate = true;
    1105              :         /* put back constraint's dependency on table */
    1106            0 :         addObjectDependency(constraintobj, tableobj->dumpId);
    1107              :         /* now that constraint is separate, it must be post-data */
    1108            0 :         addObjectDependency(constraintobj, postDataBoundId);
    1109            0 : }
    1110              : 
    1111              : /*
    1112              :  * Attribute defaults behave exactly the same as CHECK constraints...
    1113              :  */
    1114              : static void
    1115            0 : repairTableAttrDefLoop(DumpableObject *tableobj,
    1116              :                                            DumpableObject *attrdefobj)
    1117              : {
    1118              :         /* remove attrdef's dependency on table */
    1119            0 :         removeObjectDependency(attrdefobj, tableobj->dumpId);
    1120            0 : }
    1121              : 
    1122              : static void
    1123            0 : repairTableAttrDefMultiLoop(DumpableObject *tableobj,
    1124              :                                                         DumpableObject *attrdefobj)
    1125              : {
    1126              :         /* remove table's dependency on attrdef */
    1127            0 :         removeObjectDependency(tableobj, attrdefobj->dumpId);
    1128              :         /* mark attrdef as needing its own dump */
    1129            0 :         ((AttrDefInfo *) attrdefobj)->separate = true;
    1130              :         /* put back attrdef's dependency on table */
    1131            0 :         addObjectDependency(attrdefobj, tableobj->dumpId);
    1132            0 : }
    1133              : 
    1134              : /*
    1135              :  * CHECK, NOT NULL constraints on domains work just like those on tables ...
    1136              :  */
    1137              : static void
    1138            0 : repairDomainConstraintLoop(DumpableObject *domainobj,
    1139              :                                                    DumpableObject *constraintobj)
    1140              : {
    1141              :         /* remove constraint's dependency on domain */
    1142            0 :         removeObjectDependency(constraintobj, domainobj->dumpId);
    1143            0 : }
    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            0 : repairDependencyLoop(DumpableObject **loop,
    1175              :                                          int nLoop)
    1176              : {
    1177            0 :         int                     i,
    1178              :                                 j;
    1179              : 
    1180              :         /* Datatype and one of its I/O or canonicalize functions */
    1181            0 :         if (nLoop == 2 &&
    1182            0 :                 loop[0]->objType == DO_TYPE &&
    1183            0 :                 loop[1]->objType == DO_FUNC)
    1184              :         {
    1185            0 :                 repairTypeFuncLoop(loop[0], loop[1]);
    1186            0 :                 return;
    1187              :         }
    1188            0 :         if (nLoop == 2 &&
    1189            0 :                 loop[1]->objType == DO_TYPE &&
    1190            0 :                 loop[0]->objType == DO_FUNC)
    1191              :         {
    1192            0 :                 repairTypeFuncLoop(loop[1], loop[0]);
    1193            0 :                 return;
    1194              :         }
    1195              : 
    1196              :         /* View (including matview) and its ON SELECT rule */
    1197            0 :         if (nLoop == 2 &&
    1198            0 :                 loop[0]->objType == DO_TABLE &&
    1199            0 :                 loop[1]->objType == DO_RULE &&
    1200            0 :                 (((TableInfo *) loop[0])->relkind == RELKIND_VIEW ||
    1201            0 :                  ((TableInfo *) loop[0])->relkind == RELKIND_MATVIEW) &&
    1202            0 :                 ((RuleInfo *) loop[1])->ev_type == '1' &&
    1203            0 :                 ((RuleInfo *) loop[1])->is_instead &&
    1204            0 :                 ((RuleInfo *) loop[1])->ruletable == (TableInfo *) loop[0])
    1205              :         {
    1206            0 :                 repairViewRuleLoop(loop[0], loop[1]);
    1207            0 :                 return;
    1208              :         }
    1209            0 :         if (nLoop == 2 &&
    1210            0 :                 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            0 :         if (nLoop > 2)
    1224              :         {
    1225            0 :                 for (i = 0; i < nLoop; i++)
    1226              :                 {
    1227            0 :                         if (loop[i]->objType == DO_TABLE &&
    1228            0 :                                 ((TableInfo *) loop[i])->relkind == RELKIND_VIEW)
    1229              :                         {
    1230            0 :                                 for (j = 0; j < nLoop; j++)
    1231              :                                 {
    1232            0 :                                         if (loop[j]->objType == DO_RULE &&
    1233            0 :                                                 ((RuleInfo *) loop[j])->ev_type == '1' &&
    1234            0 :                                                 ((RuleInfo *) loop[j])->is_instead &&
    1235            0 :                                                 ((RuleInfo *) loop[j])->ruletable == (TableInfo *) loop[i])
    1236              :                                         {
    1237            0 :                                                 repairViewRuleMultiLoop(loop[i], loop[j]);
    1238            0 :                                                 return;
    1239              :                                         }
    1240            0 :                                 }
    1241            0 :                         }
    1242            0 :                 }
    1243            0 :         }
    1244              : 
    1245              :         /* Indirect loop involving matview and data boundary */
    1246            0 :         if (nLoop > 2)
    1247              :         {
    1248            0 :                 for (i = 0; i < nLoop; i++)
    1249              :                 {
    1250            0 :                         if (loop[i]->objType == DO_TABLE &&
    1251            0 :                                 ((TableInfo *) loop[i])->relkind == RELKIND_MATVIEW)
    1252              :                         {
    1253            0 :                                 for (j = 0; j < nLoop; j++)
    1254              :                                 {
    1255            0 :                                         if (loop[j]->objType == DO_PRE_DATA_BOUNDARY)
    1256              :                                         {
    1257            0 :                                                 DumpableObject *nextobj;
    1258              : 
    1259            0 :                                                 nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
    1260            0 :                                                 repairMatViewBoundaryMultiLoop(loop[j], nextobj);
    1261              :                                                 return;
    1262            0 :                                         }
    1263            0 :                                 }
    1264            0 :                         }
    1265            0 :                         else if (loop[i]->objType == DO_REL_STATS &&
    1266            0 :                                          ((RelStatsInfo *) loop[i])->relkind == RELKIND_MATVIEW)
    1267              :                         {
    1268            0 :                                 for (j = 0; j < nLoop; j++)
    1269              :                                 {
    1270            0 :                                         if (loop[j]->objType == DO_POST_DATA_BOUNDARY)
    1271              :                                         {
    1272            0 :                                                 DumpableObject *nextobj;
    1273              : 
    1274            0 :                                                 nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
    1275            0 :                                                 repairMatViewBoundaryMultiLoop(loop[j], nextobj);
    1276              :                                                 return;
    1277            0 :                                         }
    1278            0 :                                 }
    1279            0 :                         }
    1280            0 :                 }
    1281            0 :         }
    1282              : 
    1283              :         /* Indirect loop involving function and data boundary */
    1284            0 :         if (nLoop > 2)
    1285              :         {
    1286            0 :                 for (i = 0; i < nLoop; i++)
    1287              :                 {
    1288            0 :                         if (loop[i]->objType == DO_FUNC)
    1289              :                         {
    1290            0 :                                 for (j = 0; j < nLoop; j++)
    1291              :                                 {
    1292            0 :                                         if (loop[j]->objType == DO_PRE_DATA_BOUNDARY)
    1293              :                                         {
    1294            0 :                                                 DumpableObject *nextobj;
    1295              : 
    1296            0 :                                                 nextobj = (j < nLoop - 1) ? loop[j + 1] : loop[0];
    1297            0 :                                                 repairFunctionBoundaryMultiLoop(loop[j], nextobj);
    1298              :                                                 return;
    1299            0 :                                         }
    1300            0 :                                 }
    1301            0 :                         }
    1302            0 :                 }
    1303            0 :         }
    1304              : 
    1305              :         /* Table and CHECK constraint */
    1306            0 :         if (nLoop == 2 &&
    1307            0 :                 loop[0]->objType == DO_TABLE &&
    1308            0 :                 loop[1]->objType == DO_CONSTRAINT &&
    1309            0 :                 ((ConstraintInfo *) loop[1])->contype == 'c' &&
    1310            0 :                 ((ConstraintInfo *) loop[1])->contable == (TableInfo *) loop[0])
    1311              :         {
    1312            0 :                 repairTableConstraintLoop(loop[0], loop[1]);
    1313            0 :                 return;
    1314              :         }
    1315            0 :         if (nLoop == 2 &&
    1316            0 :                 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            0 :         if (nLoop > 2)
    1327              :         {
    1328            0 :                 for (i = 0; i < nLoop; i++)
    1329              :                 {
    1330            0 :                         if (loop[i]->objType == DO_TABLE)
    1331              :                         {
    1332            0 :                                 for (j = 0; j < nLoop; j++)
    1333              :                                 {
    1334            0 :                                         if (loop[j]->objType == DO_CONSTRAINT &&
    1335            0 :                                                 ((ConstraintInfo *) loop[j])->contype == 'c' &&
    1336            0 :                                                 ((ConstraintInfo *) loop[j])->contable == (TableInfo *) loop[i])
    1337              :                                         {
    1338            0 :                                                 repairTableConstraintMultiLoop(loop[i], loop[j]);
    1339            0 :                                                 return;
    1340              :                                         }
    1341            0 :                                 }
    1342            0 :                         }
    1343            0 :                 }
    1344            0 :         }
    1345              : 
    1346              :         /* Table and attribute default */
    1347            0 :         if (nLoop == 2 &&
    1348            0 :                 loop[0]->objType == DO_TABLE &&
    1349            0 :                 loop[1]->objType == DO_ATTRDEF &&
    1350            0 :                 ((AttrDefInfo *) loop[1])->adtable == (TableInfo *) loop[0])
    1351              :         {
    1352            0 :                 repairTableAttrDefLoop(loop[0], loop[1]);
    1353            0 :                 return;
    1354              :         }
    1355            0 :         if (nLoop == 2 &&
    1356            0 :                 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            0 :         if (nLoop == 2 &&
    1366            0 :                 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            0 :         }
    1380              : 
    1381              :         /* Indirect loop involving table and attribute default */
    1382            0 :         if (nLoop > 2)
    1383              :         {
    1384            0 :                 for (i = 0; i < nLoop; i++)
    1385              :                 {
    1386            0 :                         if (loop[i]->objType == DO_TABLE)
    1387              :                         {
    1388            0 :                                 for (j = 0; j < nLoop; j++)
    1389              :                                 {
    1390            0 :                                         if (loop[j]->objType == DO_ATTRDEF &&
    1391            0 :                                                 ((AttrDefInfo *) loop[j])->adtable == (TableInfo *) loop[i])
    1392              :                                         {
    1393            0 :                                                 repairTableAttrDefMultiLoop(loop[i], loop[j]);
    1394            0 :                                                 return;
    1395              :                                         }
    1396            0 :                                 }
    1397            0 :                         }
    1398            0 :                 }
    1399            0 :         }
    1400              : 
    1401              :         /* Domain and CHECK or NOT NULL constraint */
    1402            0 :         if (nLoop == 2 &&
    1403            0 :                 loop[0]->objType == DO_TYPE &&
    1404            0 :                 loop[1]->objType == DO_CONSTRAINT &&
    1405            0 :                 (((ConstraintInfo *) loop[1])->contype == 'c' ||
    1406            0 :                  ((ConstraintInfo *) loop[1])->contype == 'n') &&
    1407            0 :                 ((ConstraintInfo *) loop[1])->condomain == (TypeInfo *) loop[0])
    1408              :         {
    1409            0 :                 repairDomainConstraintLoop(loop[0], loop[1]);
    1410            0 :                 return;
    1411              :         }
    1412            0 :         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            0 :         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            0 :                                 }
    1441            0 :                         }
    1442            0 :                 }
    1443            0 :         }
    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            0 :         if (nLoop == 1)
    1455              :         {
    1456            0 :                 if (loop[0]->objType == DO_TABLE)
    1457              :                 {
    1458            0 :                         removeObjectDependency(loop[0], loop[0]->dumpId);
    1459            0 :                         return;
    1460              :                 }
    1461            0 :         }
    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            0 :         }
    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            0 :                 char            buf[1024];
    1497              : 
    1498            0 :                 describeDumpableObject(loop[i], buf, sizeof(buf));
    1499            0 :                 pg_log_warning_detail("%s", buf);
    1500            0 :         }
    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            0 : }
    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              :                 case DO_NAMESPACE:
    1519            0 :                         snprintf(buf, bufsize,
    1520              :                                          "SCHEMA %s  (ID %d OID %u)",
    1521            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1522            0 :                         return;
    1523              :                 case DO_EXTENSION:
    1524            0 :                         snprintf(buf, bufsize,
    1525              :                                          "EXTENSION %s  (ID %d OID %u)",
    1526            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1527            0 :                         return;
    1528              :                 case DO_TYPE:
    1529            0 :                         snprintf(buf, bufsize,
    1530              :                                          "TYPE %s  (ID %d OID %u)",
    1531            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1532            0 :                         return;
    1533              :                 case DO_SHELL_TYPE:
    1534            0 :                         snprintf(buf, bufsize,
    1535              :                                          "SHELL TYPE %s  (ID %d OID %u)",
    1536            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1537            0 :                         return;
    1538              :                 case DO_FUNC:
    1539            0 :                         snprintf(buf, bufsize,
    1540              :                                          "FUNCTION %s  (ID %d OID %u)",
    1541            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1542            0 :                         return;
    1543              :                 case DO_AGG:
    1544            0 :                         snprintf(buf, bufsize,
    1545              :                                          "AGGREGATE %s  (ID %d OID %u)",
    1546            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1547            0 :                         return;
    1548              :                 case DO_OPERATOR:
    1549            0 :                         snprintf(buf, bufsize,
    1550              :                                          "OPERATOR %s  (ID %d OID %u)",
    1551            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1552            0 :                         return;
    1553              :                 case DO_ACCESS_METHOD:
    1554            0 :                         snprintf(buf, bufsize,
    1555              :                                          "ACCESS METHOD %s  (ID %d OID %u)",
    1556            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1557            0 :                         return;
    1558              :                 case DO_OPCLASS:
    1559            0 :                         snprintf(buf, bufsize,
    1560              :                                          "OPERATOR CLASS %s  (ID %d OID %u)",
    1561            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1562            0 :                         return;
    1563              :                 case DO_OPFAMILY:
    1564            0 :                         snprintf(buf, bufsize,
    1565              :                                          "OPERATOR FAMILY %s  (ID %d OID %u)",
    1566            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1567            0 :                         return;
    1568              :                 case DO_COLLATION:
    1569            0 :                         snprintf(buf, bufsize,
    1570              :                                          "COLLATION %s  (ID %d OID %u)",
    1571            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1572            0 :                         return;
    1573              :                 case DO_CONVERSION:
    1574            0 :                         snprintf(buf, bufsize,
    1575              :                                          "CONVERSION %s  (ID %d OID %u)",
    1576            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1577            0 :                         return;
    1578              :                 case DO_TABLE:
    1579            0 :                         snprintf(buf, bufsize,
    1580              :                                          "TABLE %s  (ID %d OID %u)",
    1581            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1582            0 :                         return;
    1583              :                 case DO_TABLE_ATTACH:
    1584            0 :                         snprintf(buf, bufsize,
    1585              :                                          "TABLE ATTACH %s  (ID %d)",
    1586            0 :                                          obj->name, obj->dumpId);
    1587            0 :                         return;
    1588              :                 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            0 :                                          obj->dumpId, obj->catId.oid);
    1594            0 :                         return;
    1595              :                 case DO_INDEX:
    1596            0 :                         snprintf(buf, bufsize,
    1597              :                                          "INDEX %s  (ID %d OID %u)",
    1598            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1599            0 :                         return;
    1600              :                 case DO_INDEX_ATTACH:
    1601            0 :                         snprintf(buf, bufsize,
    1602              :                                          "INDEX ATTACH %s  (ID %d)",
    1603            0 :                                          obj->name, obj->dumpId);
    1604            0 :                         return;
    1605              :                 case DO_STATSEXT:
    1606            0 :                         snprintf(buf, bufsize,
    1607              :                                          "STATISTICS %s  (ID %d OID %u)",
    1608            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1609            0 :                         return;
    1610              :                 case DO_REFRESH_MATVIEW:
    1611            0 :                         snprintf(buf, bufsize,
    1612              :                                          "REFRESH MATERIALIZED VIEW %s  (ID %d OID %u)",
    1613            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1614            0 :                         return;
    1615              :                 case DO_RULE:
    1616            0 :                         snprintf(buf, bufsize,
    1617              :                                          "RULE %s  (ID %d OID %u)",
    1618            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1619            0 :                         return;
    1620              :                 case DO_TRIGGER:
    1621            0 :                         snprintf(buf, bufsize,
    1622              :                                          "TRIGGER %s  (ID %d OID %u)",
    1623            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1624            0 :                         return;
    1625              :                 case DO_EVENT_TRIGGER:
    1626            0 :                         snprintf(buf, bufsize,
    1627              :                                          "EVENT TRIGGER %s (ID %d OID %u)",
    1628            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1629            0 :                         return;
    1630              :                 case DO_CONSTRAINT:
    1631            0 :                         snprintf(buf, bufsize,
    1632              :                                          "CONSTRAINT %s  (ID %d OID %u)",
    1633            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1634            0 :                         return;
    1635              :                 case DO_FK_CONSTRAINT:
    1636            0 :                         snprintf(buf, bufsize,
    1637              :                                          "FK CONSTRAINT %s  (ID %d OID %u)",
    1638            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1639            0 :                         return;
    1640              :                 case DO_PROCLANG:
    1641            0 :                         snprintf(buf, bufsize,
    1642              :                                          "PROCEDURAL LANGUAGE %s  (ID %d OID %u)",
    1643            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1644            0 :                         return;
    1645              :                 case DO_CAST:
    1646            0 :                         snprintf(buf, bufsize,
    1647              :                                          "CAST %u to %u  (ID %d OID %u)",
    1648            0 :                                          ((CastInfo *) obj)->castsource,
    1649            0 :                                          ((CastInfo *) obj)->casttarget,
    1650            0 :                                          obj->dumpId, obj->catId.oid);
    1651            0 :                         return;
    1652              :                 case DO_TRANSFORM:
    1653            0 :                         snprintf(buf, bufsize,
    1654              :                                          "TRANSFORM %u lang %u  (ID %d OID %u)",
    1655            0 :                                          ((TransformInfo *) obj)->trftype,
    1656            0 :                                          ((TransformInfo *) obj)->trflang,
    1657            0 :                                          obj->dumpId, obj->catId.oid);
    1658            0 :                         return;
    1659              :                 case DO_TABLE_DATA:
    1660            0 :                         snprintf(buf, bufsize,
    1661              :                                          "TABLE DATA %s  (ID %d OID %u)",
    1662            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1663            0 :                         return;
    1664              :                 case DO_SEQUENCE_SET:
    1665            0 :                         snprintf(buf, bufsize,
    1666              :                                          "SEQUENCE SET %s  (ID %d OID %u)",
    1667            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1668            0 :                         return;
    1669              :                 case DO_DUMMY_TYPE:
    1670            0 :                         snprintf(buf, bufsize,
    1671              :                                          "DUMMY TYPE %s  (ID %d OID %u)",
    1672            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1673            0 :                         return;
    1674              :                 case DO_TSPARSER:
    1675            0 :                         snprintf(buf, bufsize,
    1676              :                                          "TEXT SEARCH PARSER %s  (ID %d OID %u)",
    1677            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1678            0 :                         return;
    1679              :                 case DO_TSDICT:
    1680            0 :                         snprintf(buf, bufsize,
    1681              :                                          "TEXT SEARCH DICTIONARY %s  (ID %d OID %u)",
    1682            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1683            0 :                         return;
    1684              :                 case DO_TSTEMPLATE:
    1685            0 :                         snprintf(buf, bufsize,
    1686              :                                          "TEXT SEARCH TEMPLATE %s  (ID %d OID %u)",
    1687            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1688            0 :                         return;
    1689              :                 case DO_TSCONFIG:
    1690            0 :                         snprintf(buf, bufsize,
    1691              :                                          "TEXT SEARCH CONFIGURATION %s  (ID %d OID %u)",
    1692            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1693            0 :                         return;
    1694              :                 case DO_FDW:
    1695            0 :                         snprintf(buf, bufsize,
    1696              :                                          "FOREIGN DATA WRAPPER %s  (ID %d OID %u)",
    1697            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1698            0 :                         return;
    1699              :                 case DO_FOREIGN_SERVER:
    1700            0 :                         snprintf(buf, bufsize,
    1701              :                                          "FOREIGN SERVER %s  (ID %d OID %u)",
    1702            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1703            0 :                         return;
    1704              :                 case DO_DEFAULT_ACL:
    1705            0 :                         snprintf(buf, bufsize,
    1706              :                                          "DEFAULT ACL %s  (ID %d OID %u)",
    1707            0 :                                          obj->name, obj->dumpId, obj->catId.oid);
    1708            0 :                         return;
    1709              :                 case DO_LARGE_OBJECT:
    1710            0 :                         snprintf(buf, bufsize,
    1711              :                                          "LARGE OBJECT  (ID %d OID %u)",
    1712            0 :                                          obj->dumpId, obj->catId.oid);
    1713            0 :                         return;
    1714              :                 case DO_LARGE_OBJECT_DATA:
    1715            0 :                         snprintf(buf, bufsize,
    1716              :                                          "LARGE OBJECT DATA  (ID %d)",
    1717            0 :                                          obj->dumpId);
    1718            0 :                         return;
    1719              :                 case DO_POLICY:
    1720            0 :                         snprintf(buf, bufsize,
    1721              :                                          "POLICY (ID %d OID %u)",
    1722            0 :                                          obj->dumpId, obj->catId.oid);
    1723            0 :                         return;
    1724              :                 case DO_PUBLICATION:
    1725            0 :                         snprintf(buf, bufsize,
    1726              :                                          "PUBLICATION (ID %d OID %u)",
    1727            0 :                                          obj->dumpId, obj->catId.oid);
    1728            0 :                         return;
    1729              :                 case DO_PUBLICATION_REL:
    1730            0 :                         snprintf(buf, bufsize,
    1731              :                                          "PUBLICATION TABLE (ID %d OID %u)",
    1732            0 :                                          obj->dumpId, obj->catId.oid);
    1733            0 :                         return;
    1734              :                 case DO_PUBLICATION_TABLE_IN_SCHEMA:
    1735            0 :                         snprintf(buf, bufsize,
    1736              :                                          "PUBLICATION TABLES IN SCHEMA (ID %d OID %u)",
    1737            0 :                                          obj->dumpId, obj->catId.oid);
    1738            0 :                         return;
    1739              :                 case DO_SUBSCRIPTION:
    1740            0 :                         snprintf(buf, bufsize,
    1741              :                                          "SUBSCRIPTION (ID %d OID %u)",
    1742            0 :                                          obj->dumpId, obj->catId.oid);
    1743            0 :                         return;
    1744              :                 case DO_SUBSCRIPTION_REL:
    1745            0 :                         snprintf(buf, bufsize,
    1746              :                                          "SUBSCRIPTION TABLE (ID %d OID %u)",
    1747            0 :                                          obj->dumpId, obj->catId.oid);
    1748            0 :                         return;
    1749              :                 case DO_PRE_DATA_BOUNDARY:
    1750            0 :                         snprintf(buf, bufsize,
    1751              :                                          "PRE-DATA BOUNDARY  (ID %d)",
    1752            0 :                                          obj->dumpId);
    1753            0 :                         return;
    1754              :                 case DO_POST_DATA_BOUNDARY:
    1755            0 :                         snprintf(buf, bufsize,
    1756              :                                          "POST-DATA BOUNDARY  (ID %d)",
    1757            0 :                                          obj->dumpId);
    1758            0 :                         return;
    1759              :                 case DO_REL_STATS:
    1760            0 :                         snprintf(buf, bufsize,
    1761              :                                          "RELATION STATISTICS FOR %s  (ID %d OID %u)",
    1762            0 :                                          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            0 :                          obj->dumpId, obj->catId.oid);
    1770            0 : }
    1771              : 
    1772              : /* binaryheap comparator that compares "a" and "b" as integers */
    1773              : static int
    1774            0 : int_cmp(void *a, void *b, void *arg)
    1775              : {
    1776            0 :         int                     ai = (int) (intptr_t) a;
    1777            0 :         int                     bi = (int) (intptr_t) b;
    1778              : 
    1779            0 :         return pg_cmp_s32(ai, bi);
    1780            0 : }
        

Generated by: LCOV version 2.3.2-1