LCOV - code coverage report
Current view: top level - src/bin/pg_dump - common.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 370 383 96.6 %
Date: 2024-05-09 08:11:23 Functions: 27 27 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * common.c
       4             :  *  Catalog routines used by pg_dump; long ago these were shared
       5             :  *  by another dump tool, but not anymore.
       6             :  *
       7             :  * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
       8             :  * Portions Copyright (c) 1994, Regents of the University of California
       9             :  *
      10             :  *
      11             :  * IDENTIFICATION
      12             :  *    src/bin/pg_dump/common.c
      13             :  *
      14             :  *-------------------------------------------------------------------------
      15             :  */
      16             : #include "postgres_fe.h"
      17             : 
      18             : #include <ctype.h>
      19             : 
      20             : #include "catalog/pg_class_d.h"
      21             : #include "catalog/pg_collation_d.h"
      22             : #include "catalog/pg_extension_d.h"
      23             : #include "catalog/pg_namespace_d.h"
      24             : #include "catalog/pg_operator_d.h"
      25             : #include "catalog/pg_proc_d.h"
      26             : #include "catalog/pg_publication_d.h"
      27             : #include "catalog/pg_subscription_d.h"
      28             : #include "catalog/pg_type_d.h"
      29             : #include "common/hashfn.h"
      30             : #include "fe_utils/string_utils.h"
      31             : #include "pg_backup_archiver.h"
      32             : #include "pg_backup_utils.h"
      33             : #include "pg_dump.h"
      34             : 
      35             : /*
      36             :  * Variables for mapping DumpId to DumpableObject
      37             :  */
      38             : static DumpableObject **dumpIdMap = NULL;
      39             : static int  allocedDumpIds = 0;
      40             : static DumpId lastDumpId = 0;   /* Note: 0 is InvalidDumpId */
      41             : 
      42             : /*
      43             :  * Infrastructure for mapping CatalogId to DumpableObject
      44             :  *
      45             :  * We use a hash table generated by simplehash.h.  That infrastructure
      46             :  * requires all the hash table entries to be the same size, and it also
      47             :  * expects that it can move them around when resizing the table.  So we
      48             :  * cannot make the DumpableObjects be elements of the hash table directly;
      49             :  * instead, the hash table elements contain pointers to DumpableObjects.
      50             :  * This does have the advantage of letting us map multiple CatalogIds
      51             :  * to one DumpableObject, which is useful for blobs.
      52             :  *
      53             :  * It turns out to be convenient to also use this data structure to map
      54             :  * CatalogIds to owning extensions, if any.  Since extension membership
      55             :  * data is read before creating most DumpableObjects, either one of dobj
      56             :  * and ext could be NULL.
      57             :  */
      58             : typedef struct _catalogIdMapEntry
      59             : {
      60             :     CatalogId   catId;          /* the indexed CatalogId */
      61             :     uint32      status;         /* hash status */
      62             :     uint32      hashval;        /* hash code for the CatalogId */
      63             :     DumpableObject *dobj;       /* the associated DumpableObject, if any */
      64             :     ExtensionInfo *ext;         /* owning extension, if any */
      65             : } CatalogIdMapEntry;
      66             : 
      67             : #define SH_PREFIX       catalogid
      68             : #define SH_ELEMENT_TYPE CatalogIdMapEntry
      69             : #define SH_KEY_TYPE     CatalogId
      70             : #define SH_KEY          catId
      71             : #define SH_HASH_KEY(tb, key)    hash_bytes((const unsigned char *) &(key), sizeof(CatalogId))
      72             : #define SH_EQUAL(tb, a, b)      ((a).oid == (b).oid && (a).tableoid == (b).tableoid)
      73             : #define SH_STORE_HASH
      74             : #define SH_GET_HASH(tb, a) (a)->hashval
      75             : #define SH_SCOPE        static inline
      76             : #define SH_RAW_ALLOCATOR    pg_malloc0
      77             : #define SH_DECLARE
      78             : #define SH_DEFINE
      79             : #include "lib/simplehash.h"
      80             : 
      81             : #define CATALOGIDHASH_INITIAL_SIZE  10000
      82             : 
      83             : static catalogid_hash *catalogIdHash = NULL;
      84             : 
      85             : static void flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
      86             :                           InhInfo *inhinfo, int numInherits);
      87             : static void flagInhIndexes(Archive *fout, TableInfo *tblinfo, int numTables);
      88             : static void flagInhAttrs(Archive *fout, DumpOptions *dopt, TableInfo *tblinfo,
      89             :                          int numTables);
      90             : static int  strInArray(const char *pattern, char **arr, int arr_size);
      91             : static IndxInfo *findIndexByOid(Oid oid);
      92             : 
      93             : 
      94             : /*
      95             :  * getSchemaData
      96             :  *    Collect information about all potentially dumpable objects
      97             :  */
      98             : TableInfo *
      99         306 : getSchemaData(Archive *fout, int *numTablesPtr)
     100             : {
     101             :     TableInfo  *tblinfo;
     102             :     ExtensionInfo *extinfo;
     103             :     InhInfo    *inhinfo;
     104             :     int         numTables;
     105             :     int         numTypes;
     106             :     int         numFuncs;
     107             :     int         numOperators;
     108             :     int         numCollations;
     109             :     int         numNamespaces;
     110             :     int         numExtensions;
     111             :     int         numPublications;
     112             :     int         numAggregates;
     113             :     int         numInherits;
     114             :     int         numRules;
     115             :     int         numProcLangs;
     116             :     int         numCasts;
     117             :     int         numTransforms;
     118             :     int         numAccessMethods;
     119             :     int         numOpclasses;
     120             :     int         numOpfamilies;
     121             :     int         numConversions;
     122             :     int         numTSParsers;
     123             :     int         numTSTemplates;
     124             :     int         numTSDicts;
     125             :     int         numTSConfigs;
     126             :     int         numForeignDataWrappers;
     127             :     int         numForeignServers;
     128             :     int         numDefaultACLs;
     129             :     int         numEventTriggers;
     130             : 
     131             :     /*
     132             :      * We must read extensions and extension membership info first, because
     133             :      * extension membership needs to be consultable during decisions about
     134             :      * whether other objects are to be dumped.
     135             :      */
     136         306 :     pg_log_info("reading extensions");
     137         306 :     extinfo = getExtensions(fout, &numExtensions);
     138             : 
     139         306 :     pg_log_info("identifying extension members");
     140         306 :     getExtensionMembership(fout, extinfo, numExtensions);
     141             : 
     142         306 :     pg_log_info("reading schemas");
     143         306 :     (void) getNamespaces(fout, &numNamespaces);
     144             : 
     145             :     /*
     146             :      * getTables should be done as soon as possible, so as to minimize the
     147             :      * window between starting our transaction and acquiring per-table locks.
     148             :      * However, we have to do getNamespaces first because the tables get
     149             :      * linked to their containing namespaces during getTables.
     150             :      */
     151         306 :     pg_log_info("reading user-defined tables");
     152         306 :     tblinfo = getTables(fout, &numTables);
     153             : 
     154         304 :     getOwnedSeqs(fout, tblinfo, numTables);
     155             : 
     156         304 :     pg_log_info("reading user-defined functions");
     157         304 :     (void) getFuncs(fout, &numFuncs);
     158             : 
     159             :     /* this must be after getTables and getFuncs */
     160         304 :     pg_log_info("reading user-defined types");
     161         304 :     (void) getTypes(fout, &numTypes);
     162             : 
     163             :     /* this must be after getFuncs, too */
     164         304 :     pg_log_info("reading procedural languages");
     165         304 :     getProcLangs(fout, &numProcLangs);
     166             : 
     167         304 :     pg_log_info("reading user-defined aggregate functions");
     168         304 :     getAggregates(fout, &numAggregates);
     169             : 
     170         304 :     pg_log_info("reading user-defined operators");
     171         304 :     (void) getOperators(fout, &numOperators);
     172             : 
     173         304 :     pg_log_info("reading user-defined access methods");
     174         304 :     getAccessMethods(fout, &numAccessMethods);
     175             : 
     176         304 :     pg_log_info("reading user-defined operator classes");
     177         304 :     getOpclasses(fout, &numOpclasses);
     178             : 
     179         304 :     pg_log_info("reading user-defined operator families");
     180         304 :     getOpfamilies(fout, &numOpfamilies);
     181             : 
     182         304 :     pg_log_info("reading user-defined text search parsers");
     183         304 :     getTSParsers(fout, &numTSParsers);
     184             : 
     185         304 :     pg_log_info("reading user-defined text search templates");
     186         304 :     getTSTemplates(fout, &numTSTemplates);
     187             : 
     188         304 :     pg_log_info("reading user-defined text search dictionaries");
     189         304 :     getTSDictionaries(fout, &numTSDicts);
     190             : 
     191         304 :     pg_log_info("reading user-defined text search configurations");
     192         304 :     getTSConfigurations(fout, &numTSConfigs);
     193             : 
     194         304 :     pg_log_info("reading user-defined foreign-data wrappers");
     195         304 :     getForeignDataWrappers(fout, &numForeignDataWrappers);
     196             : 
     197         304 :     pg_log_info("reading user-defined foreign servers");
     198         304 :     getForeignServers(fout, &numForeignServers);
     199             : 
     200         304 :     pg_log_info("reading default privileges");
     201         304 :     getDefaultACLs(fout, &numDefaultACLs);
     202             : 
     203         304 :     pg_log_info("reading user-defined collations");
     204         304 :     (void) getCollations(fout, &numCollations);
     205             : 
     206         304 :     pg_log_info("reading user-defined conversions");
     207         304 :     getConversions(fout, &numConversions);
     208             : 
     209         304 :     pg_log_info("reading type casts");
     210         304 :     getCasts(fout, &numCasts);
     211             : 
     212         304 :     pg_log_info("reading transforms");
     213         304 :     getTransforms(fout, &numTransforms);
     214             : 
     215         304 :     pg_log_info("reading table inheritance information");
     216         304 :     inhinfo = getInherits(fout, &numInherits);
     217             : 
     218         304 :     pg_log_info("reading event triggers");
     219         304 :     getEventTriggers(fout, &numEventTriggers);
     220             : 
     221             :     /* Identify extension configuration tables that should be dumped */
     222         304 :     pg_log_info("finding extension tables");
     223         304 :     processExtensionTables(fout, extinfo, numExtensions);
     224             : 
     225             :     /* Link tables to parents, mark parents of target tables interesting */
     226         304 :     pg_log_info("finding inheritance relationships");
     227         304 :     flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits);
     228             : 
     229         304 :     pg_log_info("reading column info for interesting tables");
     230         304 :     getTableAttrs(fout, tblinfo, numTables);
     231             : 
     232         304 :     pg_log_info("flagging inherited columns in subtables");
     233         304 :     flagInhAttrs(fout, fout->dopt, tblinfo, numTables);
     234             : 
     235         304 :     pg_log_info("reading partitioning data");
     236         304 :     getPartitioningInfo(fout);
     237             : 
     238         304 :     pg_log_info("reading indexes");
     239         304 :     getIndexes(fout, tblinfo, numTables);
     240             : 
     241         304 :     pg_log_info("flagging indexes in partitioned tables");
     242         304 :     flagInhIndexes(fout, tblinfo, numTables);
     243             : 
     244         304 :     pg_log_info("reading extended statistics");
     245         304 :     getExtendedStatistics(fout);
     246             : 
     247         304 :     pg_log_info("reading constraints");
     248         304 :     getConstraints(fout, tblinfo, numTables);
     249             : 
     250         304 :     pg_log_info("reading triggers");
     251         304 :     getTriggers(fout, tblinfo, numTables);
     252             : 
     253         304 :     pg_log_info("reading rewrite rules");
     254         304 :     getRules(fout, &numRules);
     255             : 
     256         304 :     pg_log_info("reading policies");
     257         304 :     getPolicies(fout, tblinfo, numTables);
     258             : 
     259         304 :     pg_log_info("reading publications");
     260         304 :     (void) getPublications(fout, &numPublications);
     261             : 
     262         304 :     pg_log_info("reading publication membership of tables");
     263         304 :     getPublicationTables(fout, tblinfo, numTables);
     264             : 
     265         304 :     pg_log_info("reading publication membership of schemas");
     266         304 :     getPublicationNamespaces(fout);
     267             : 
     268         304 :     pg_log_info("reading subscriptions");
     269         304 :     getSubscriptions(fout);
     270             : 
     271         304 :     pg_log_info("reading subscription membership of tables");
     272         304 :     getSubscriptionTables(fout);
     273             : 
     274         304 :     free(inhinfo);              /* not needed any longer */
     275             : 
     276         304 :     *numTablesPtr = numTables;
     277         304 :     return tblinfo;
     278             : }
     279             : 
     280             : /* flagInhTables -
     281             :  *   Fill in parent link fields of tables for which we need that information,
     282             :  *   mark parents of target tables as interesting, and create
     283             :  *   TableAttachInfo objects for partitioned tables with appropriate
     284             :  *   dependency links.
     285             :  *
     286             :  * Note that only direct ancestors of targets are marked interesting.
     287             :  * This is sufficient; we don't much care whether they inherited their
     288             :  * attributes or not.
     289             :  *
     290             :  * modifies tblinfo
     291             :  */
     292             : static void
     293         304 : flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
     294             :               InhInfo *inhinfo, int numInherits)
     295             : {
     296         304 :     TableInfo  *child = NULL;
     297         304 :     TableInfo  *parent = NULL;
     298             :     int         i,
     299             :                 j;
     300             : 
     301             :     /*
     302             :      * Set up links from child tables to their parents.
     303             :      *
     304             :      * We used to attempt to skip this work for tables that are not to be
     305             :      * dumped; but the optimizable cases are rare in practice, and setting up
     306             :      * these links in bulk is cheaper than the old way.  (Note in particular
     307             :      * that it's very rare for a child to have more than one parent.)
     308             :      */
     309        4914 :     for (i = 0; i < numInherits; i++)
     310             :     {
     311             :         /*
     312             :          * Skip a hashtable lookup if it's same table as last time.  This is
     313             :          * unlikely for the child, but less so for the parent.  (Maybe we
     314             :          * should ask the backend for a sorted array to make it more likely?
     315             :          * Not clear the sorting effort would be repaid, though.)
     316             :          */
     317        4610 :         if (child == NULL ||
     318        3386 :             child->dobj.catId.oid != inhinfo[i].inhrelid)
     319             :         {
     320        4530 :             child = findTableByOid(inhinfo[i].inhrelid);
     321             : 
     322             :             /*
     323             :              * If we find no TableInfo, assume the pg_inherits entry is for a
     324             :              * partitioned index, which we don't need to track.
     325             :              */
     326        4530 :             if (child == NULL)
     327        1182 :                 continue;
     328             :         }
     329        3428 :         if (parent == NULL ||
     330        3294 :             parent->dobj.catId.oid != inhinfo[i].inhparent)
     331             :         {
     332        2044 :             parent = findTableByOid(inhinfo[i].inhparent);
     333        2044 :             if (parent == NULL)
     334           0 :                 pg_fatal("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
     335             :                          inhinfo[i].inhparent,
     336             :                          child->dobj.name,
     337             :                          child->dobj.catId.oid);
     338             :         }
     339             :         /* Add this parent to the child's list of parents. */
     340        3428 :         if (child->numParents > 0)
     341          80 :             child->parents = pg_realloc_array(child->parents,
     342             :                                               TableInfo *,
     343             :                                               child->numParents + 1);
     344             :         else
     345        3348 :             child->parents = pg_malloc_array(TableInfo *, 1);
     346        3428 :         child->parents[child->numParents++] = parent;
     347             :     }
     348             : 
     349             :     /*
     350             :      * Now consider all child tables and mark parents interesting as needed.
     351             :      */
     352       77210 :     for (i = 0; i < numTables; i++)
     353             :     {
     354             :         /*
     355             :          * If needed, mark the parents as interesting for getTableAttrs and
     356             :          * getIndexes.  We only need this for direct parents of dumpable
     357             :          * tables.
     358             :          */
     359       76906 :         if (tblinfo[i].dobj.dump)
     360             :         {
     361       49336 :             int         numParents = tblinfo[i].numParents;
     362       49336 :             TableInfo **parents = tblinfo[i].parents;
     363             : 
     364       52612 :             for (j = 0; j < numParents; j++)
     365        3276 :                 parents[j]->interesting = true;
     366             :         }
     367             : 
     368             :         /* Create TableAttachInfo object if needed */
     369       76906 :         if ((tblinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
     370       11524 :             tblinfo[i].ispartition)
     371             :         {
     372             :             TableAttachInfo *attachinfo;
     373             : 
     374             :             /* With partitions there can only be one parent */
     375        2476 :             if (tblinfo[i].numParents != 1)
     376           0 :                 pg_fatal("invalid number of parents %d for table \"%s\"",
     377             :                          tblinfo[i].numParents,
     378             :                          tblinfo[i].dobj.name);
     379             : 
     380        2476 :             attachinfo = (TableAttachInfo *) palloc(sizeof(TableAttachInfo));
     381        2476 :             attachinfo->dobj.objType = DO_TABLE_ATTACH;
     382        2476 :             attachinfo->dobj.catId.tableoid = 0;
     383        2476 :             attachinfo->dobj.catId.oid = 0;
     384        2476 :             AssignDumpId(&attachinfo->dobj);
     385        2476 :             attachinfo->dobj.name = pg_strdup(tblinfo[i].dobj.name);
     386        2476 :             attachinfo->dobj.namespace = tblinfo[i].dobj.namespace;
     387        2476 :             attachinfo->parentTbl = tblinfo[i].parents[0];
     388        2476 :             attachinfo->partitionTbl = &tblinfo[i];
     389             : 
     390             :             /*
     391             :              * We must state the DO_TABLE_ATTACH object's dependencies
     392             :              * explicitly, since it will not match anything in pg_depend.
     393             :              *
     394             :              * Give it dependencies on both the partition table and the parent
     395             :              * table, so that it will not be executed till both of those
     396             :              * exist.  (There's no need to care what order those are created
     397             :              * in.)
     398             :              */
     399        2476 :             addObjectDependency(&attachinfo->dobj, tblinfo[i].dobj.dumpId);
     400        2476 :             addObjectDependency(&attachinfo->dobj, tblinfo[i].parents[0]->dobj.dumpId);
     401             :         }
     402             :     }
     403         304 : }
     404             : 
     405             : /*
     406             :  * flagInhIndexes -
     407             :  *   Create IndexAttachInfo objects for partitioned indexes, and add
     408             :  *   appropriate dependency links.
     409             :  */
     410             : static void
     411         304 : flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
     412             : {
     413             :     int         i,
     414             :                 j;
     415             : 
     416       77210 :     for (i = 0; i < numTables; i++)
     417             :     {
     418       76906 :         if (!tblinfo[i].ispartition || tblinfo[i].numParents == 0)
     419       74340 :             continue;
     420             : 
     421             :         Assert(tblinfo[i].numParents == 1);
     422             : 
     423        3752 :         for (j = 0; j < tblinfo[i].numIndexes; j++)
     424             :         {
     425        1186 :             IndxInfo   *index = &(tblinfo[i].indexes[j]);
     426             :             IndxInfo   *parentidx;
     427             :             IndexAttachInfo *attachinfo;
     428             : 
     429        1186 :             if (index->parentidx == 0)
     430         100 :                 continue;
     431             : 
     432        1086 :             parentidx = findIndexByOid(index->parentidx);
     433        1086 :             if (parentidx == NULL)
     434           0 :                 continue;
     435             : 
     436        1086 :             attachinfo = pg_malloc_object(IndexAttachInfo);
     437             : 
     438        1086 :             attachinfo->dobj.objType = DO_INDEX_ATTACH;
     439        1086 :             attachinfo->dobj.catId.tableoid = 0;
     440        1086 :             attachinfo->dobj.catId.oid = 0;
     441        1086 :             AssignDumpId(&attachinfo->dobj);
     442        1086 :             attachinfo->dobj.name = pg_strdup(index->dobj.name);
     443        1086 :             attachinfo->dobj.namespace = index->indextable->dobj.namespace;
     444        1086 :             attachinfo->parentIdx = parentidx;
     445        1086 :             attachinfo->partitionIdx = index;
     446             : 
     447             :             /*
     448             :              * We must state the DO_INDEX_ATTACH object's dependencies
     449             :              * explicitly, since it will not match anything in pg_depend.
     450             :              *
     451             :              * Give it dependencies on both the partition index and the parent
     452             :              * index, so that it will not be executed till both of those
     453             :              * exist.  (There's no need to care what order those are created
     454             :              * in.)
     455             :              *
     456             :              * In addition, give it dependencies on the indexes' underlying
     457             :              * tables.  This does nothing of great value so far as serial
     458             :              * restore ordering goes, but it ensures that a parallel restore
     459             :              * will not try to run the ATTACH concurrently with other
     460             :              * operations on those tables.
     461             :              */
     462        1086 :             addObjectDependency(&attachinfo->dobj, index->dobj.dumpId);
     463        1086 :             addObjectDependency(&attachinfo->dobj, parentidx->dobj.dumpId);
     464        1086 :             addObjectDependency(&attachinfo->dobj,
     465        1086 :                                 index->indextable->dobj.dumpId);
     466        1086 :             addObjectDependency(&attachinfo->dobj,
     467        1086 :                                 parentidx->indextable->dobj.dumpId);
     468             : 
     469             :             /* keep track of the list of partitions in the parent index */
     470        1086 :             simple_ptr_list_append(&parentidx->partattaches, &attachinfo->dobj);
     471             :         }
     472             :     }
     473         304 : }
     474             : 
     475             : /* flagInhAttrs -
     476             :  *   for each dumpable table in tblinfo, flag its inherited attributes
     477             :  *
     478             :  * What we need to do here is:
     479             :  *
     480             :  * - Detect child columns that inherit NOT NULL bits from their parents, so
     481             :  *   that we needn't specify that again for the child. (Versions >= 16 no
     482             :  *   longer need this.)
     483             :  *
     484             :  * - Detect child columns that have DEFAULT NULL when their parents had some
     485             :  *   non-null default.  In this case, we make up a dummy AttrDefInfo object so
     486             :  *   that we'll correctly emit the necessary DEFAULT NULL clause; otherwise
     487             :  *   the backend will apply an inherited default to the column.
     488             :  *
     489             :  * - Detect child columns that have a generation expression and all their
     490             :  *   parents also have the same generation expression, and if so suppress the
     491             :  *   child's expression.  The child will inherit the generation expression
     492             :  *   automatically, so there's no need to dump it.  This improves the dump's
     493             :  *   compatibility with pre-v16 servers, which didn't allow the child's
     494             :  *   expression to be given explicitly.  Exceptions: If it's a partition or
     495             :  *   we are in binary upgrade mode, we dump such expressions anyway because
     496             :  *   in those cases inherited tables are recreated standalone first and then
     497             :  *   reattached to the parent.  (See also the logic in dumpTableSchema().)
     498             :  *
     499             :  * modifies tblinfo
     500             :  */
     501             : static void
     502         304 : flagInhAttrs(Archive *fout, DumpOptions *dopt, TableInfo *tblinfo, int numTables)
     503             : {
     504             :     int         i,
     505             :                 j,
     506             :                 k;
     507             : 
     508             :     /*
     509             :      * We scan the tables in OID order, since that's how tblinfo[] is sorted.
     510             :      * Hence we will typically visit parents before their children --- but
     511             :      * that is *not* guaranteed.  Thus this loop must be careful that it does
     512             :      * not alter table properties in a way that could change decisions made at
     513             :      * child tables during other iterations.
     514             :      */
     515       77210 :     for (i = 0; i < numTables; i++)
     516             :     {
     517       76906 :         TableInfo  *tbinfo = &(tblinfo[i]);
     518             :         int         numParents;
     519             :         TableInfo **parents;
     520             : 
     521             :         /* Some kinds never have parents */
     522       76906 :         if (tbinfo->relkind == RELKIND_SEQUENCE ||
     523       75924 :             tbinfo->relkind == RELKIND_VIEW ||
     524       31896 :             tbinfo->relkind == RELKIND_MATVIEW)
     525       45864 :             continue;
     526             : 
     527             :         /* Don't bother computing anything for non-target tables, either */
     528       31042 :         if (!tbinfo->dobj.dump)
     529        4610 :             continue;
     530             : 
     531       26432 :         numParents = tbinfo->numParents;
     532       26432 :         parents = tbinfo->parents;
     533             : 
     534       26432 :         if (numParents == 0)
     535       23236 :             continue;           /* nothing to see here, move along */
     536             : 
     537             :         /* For each column, search for matching column names in parent(s) */
     538       11530 :         for (j = 0; j < tbinfo->numatts; j++)
     539             :         {
     540             :             bool        foundNotNull;   /* Attr was NOT NULL in a parent */
     541             :             bool        foundDefault;   /* Found a default in a parent */
     542             :             bool        foundSameGenerated; /* Found matching GENERATED */
     543             :             bool        foundDiffGenerated; /* Found non-matching GENERATED */
     544             : 
     545             :             /* no point in examining dropped columns */
     546        8334 :             if (tbinfo->attisdropped[j])
     547         610 :                 continue;
     548             : 
     549        7724 :             foundNotNull = false;
     550        7724 :             foundDefault = false;
     551        7724 :             foundSameGenerated = false;
     552        7724 :             foundDiffGenerated = false;
     553       15768 :             for (k = 0; k < numParents; k++)
     554             :             {
     555        8044 :                 TableInfo  *parent = parents[k];
     556             :                 int         inhAttrInd;
     557             : 
     558        8044 :                 inhAttrInd = strInArray(tbinfo->attnames[j],
     559             :                                         parent->attnames,
     560             :                                         parent->numatts);
     561        8044 :                 if (inhAttrInd >= 0)
     562             :                 {
     563        7594 :                     AttrDefInfo *parentDef = parent->attrdefs[inhAttrInd];
     564             : 
     565        8862 :                     foundNotNull |= (parent->notnull_constrs[inhAttrInd] != NULL &&
     566        1268 :                                      !parent->notnull_noinh[inhAttrInd]);
     567       15912 :                     foundDefault |= (parentDef != NULL &&
     568        8218 :                                      strcmp(parentDef->adef_expr, "NULL") != 0 &&
     569         624 :                                      !parent->attgenerated[inhAttrInd]);
     570        7594 :                     if (parent->attgenerated[inhAttrInd])
     571             :                     {
     572             :                         /* these pointer nullness checks are just paranoia */
     573         244 :                         if (parentDef != NULL &&
     574         232 :                             tbinfo->attrdefs[j] != NULL &&
     575         232 :                             strcmp(parentDef->adef_expr,
     576         232 :                                    tbinfo->attrdefs[j]->adef_expr) == 0)
     577         202 :                             foundSameGenerated = true;
     578             :                         else
     579          42 :                             foundDiffGenerated = true;
     580             :                     }
     581             :                 }
     582             :             }
     583             : 
     584             :             /* In versions < 17, remember if we found inherited NOT NULL */
     585        7724 :             if (fout->remoteVersion < 170000)
     586           0 :                 tbinfo->notnull_inh[j] = foundNotNull;
     587             : 
     588             :             /*
     589             :              * Manufacture a DEFAULT NULL clause if necessary.  This breaks
     590             :              * the advice given above to avoid changing state that might get
     591             :              * inspected in other loop iterations.  We prevent trouble by
     592             :              * having the foundDefault test above check whether adef_expr is
     593             :              * "NULL", so that it will reach the same conclusion before or
     594             :              * after this is done.
     595             :              */
     596        7724 :             if (foundDefault && tbinfo->attrdefs[j] == NULL)
     597             :             {
     598             :                 AttrDefInfo *attrDef;
     599             : 
     600          80 :                 attrDef = pg_malloc_object(AttrDefInfo);
     601          80 :                 attrDef->dobj.objType = DO_ATTRDEF;
     602          80 :                 attrDef->dobj.catId.tableoid = 0;
     603          80 :                 attrDef->dobj.catId.oid = 0;
     604          80 :                 AssignDumpId(&attrDef->dobj);
     605          80 :                 attrDef->dobj.name = pg_strdup(tbinfo->dobj.name);
     606          80 :                 attrDef->dobj.namespace = tbinfo->dobj.namespace;
     607          80 :                 attrDef->dobj.dump = tbinfo->dobj.dump;
     608             : 
     609          80 :                 attrDef->adtable = tbinfo;
     610          80 :                 attrDef->adnum = j + 1;
     611          80 :                 attrDef->adef_expr = pg_strdup("NULL");
     612             : 
     613             :                 /* Will column be dumped explicitly? */
     614          80 :                 if (shouldPrintColumn(dopt, tbinfo, j))
     615             :                 {
     616          80 :                     attrDef->separate = false;
     617             :                     /* No dependency needed: NULL cannot have dependencies */
     618             :                 }
     619             :                 else
     620             :                 {
     621             :                     /* column will be suppressed, print default separately */
     622           0 :                     attrDef->separate = true;
     623             :                     /* ensure it comes out after the table */
     624           0 :                     addObjectDependency(&attrDef->dobj,
     625             :                                         tbinfo->dobj.dumpId);
     626             :                 }
     627             : 
     628          80 :                 tbinfo->attrdefs[j] = attrDef;
     629             :             }
     630             : 
     631             :             /* No need to dump generation expression if it's inheritable */
     632        7724 :             if (foundSameGenerated && !foundDiffGenerated &&
     633         202 :                 !tbinfo->ispartition && !dopt->binary_upgrade)
     634         160 :                 tbinfo->attrdefs[j]->dobj.dump = DUMP_COMPONENT_NONE;
     635             :         }
     636             :     }
     637         304 : }
     638             : 
     639             : /*
     640             :  * AssignDumpId
     641             :  *      Given a newly-created dumpable object, assign a dump ID,
     642             :  *      and enter the object into the lookup tables.
     643             :  *
     644             :  * The caller is expected to have filled in objType and catId,
     645             :  * but not any of the other standard fields of a DumpableObject.
     646             :  */
     647             : void
     648     1098852 : AssignDumpId(DumpableObject *dobj)
     649             : {
     650     1098852 :     dobj->dumpId = ++lastDumpId;
     651     1098852 :     dobj->name = NULL;           /* must be set later */
     652     1098852 :     dobj->namespace = NULL;      /* may be set later */
     653     1098852 :     dobj->dump = DUMP_COMPONENT_ALL; /* default assumption */
     654     1098852 :     dobj->dump_contains = DUMP_COMPONENT_ALL;    /* default assumption */
     655             :     /* All objects have definitions; we may set more components bits later */
     656     1098852 :     dobj->components = DUMP_COMPONENT_DEFINITION;
     657     1098852 :     dobj->ext_member = false;    /* default assumption */
     658     1098852 :     dobj->depends_on_ext = false;    /* default assumption */
     659     1098852 :     dobj->dependencies = NULL;
     660     1098852 :     dobj->nDeps = 0;
     661     1098852 :     dobj->allocDeps = 0;
     662             : 
     663             :     /* Add object to dumpIdMap[], enlarging that array if need be */
     664     1100396 :     while (dobj->dumpId >= allocedDumpIds)
     665             :     {
     666             :         int         newAlloc;
     667             : 
     668        1544 :         if (allocedDumpIds <= 0)
     669             :         {
     670         306 :             newAlloc = 256;
     671         306 :             dumpIdMap = pg_malloc_array(DumpableObject *, newAlloc);
     672             :         }
     673             :         else
     674             :         {
     675        1238 :             newAlloc = allocedDumpIds * 2;
     676        1238 :             dumpIdMap = pg_realloc_array(dumpIdMap, DumpableObject *, newAlloc);
     677             :         }
     678        1544 :         memset(dumpIdMap + allocedDumpIds, 0,
     679        1544 :                (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
     680        1544 :         allocedDumpIds = newAlloc;
     681             :     }
     682     1098852 :     dumpIdMap[dobj->dumpId] = dobj;
     683             : 
     684             :     /* If it has a valid CatalogId, enter it into the hash table */
     685     1098852 :     if (OidIsValid(dobj->catId.tableoid))
     686             :     {
     687             :         CatalogIdMapEntry *entry;
     688             :         bool        found;
     689             : 
     690             :         /* Initialize CatalogId hash table if not done yet */
     691     1073376 :         if (catalogIdHash == NULL)
     692         306 :             catalogIdHash = catalogid_create(CATALOGIDHASH_INITIAL_SIZE, NULL);
     693             : 
     694     1073376 :         entry = catalogid_insert(catalogIdHash, dobj->catId, &found);
     695     1073376 :         if (!found)
     696             :         {
     697     1071788 :             entry->dobj = NULL;
     698     1071788 :             entry->ext = NULL;
     699             :         }
     700             :         Assert(entry->dobj == NULL);
     701     1073376 :         entry->dobj = dobj;
     702             :     }
     703     1098852 : }
     704             : 
     705             : /*
     706             :  * recordAdditionalCatalogID
     707             :  *    Record an additional catalog ID for the given DumpableObject
     708             :  */
     709             : void
     710          20 : recordAdditionalCatalogID(CatalogId catId, DumpableObject *dobj)
     711             : {
     712             :     CatalogIdMapEntry *entry;
     713             :     bool        found;
     714             : 
     715             :     /* CatalogId hash table must exist, if we have a DumpableObject */
     716             :     Assert(catalogIdHash != NULL);
     717             : 
     718             :     /* Add reference to CatalogId hash */
     719          20 :     entry = catalogid_insert(catalogIdHash, catId, &found);
     720          20 :     if (!found)
     721             :     {
     722          20 :         entry->dobj = NULL;
     723          20 :         entry->ext = NULL;
     724             :     }
     725             :     Assert(entry->dobj == NULL);
     726          20 :     entry->dobj = dobj;
     727          20 : }
     728             : 
     729             : /*
     730             :  * Assign a DumpId that's not tied to a DumpableObject.
     731             :  *
     732             :  * This is used when creating a "fixed" ArchiveEntry that doesn't need to
     733             :  * participate in the sorting logic.
     734             :  */
     735             : DumpId
     736       10998 : createDumpId(void)
     737             : {
     738       10998 :     return ++lastDumpId;
     739             : }
     740             : 
     741             : /*
     742             :  * Return the largest DumpId so far assigned
     743             :  */
     744             : DumpId
     745        2080 : getMaxDumpId(void)
     746             : {
     747        2080 :     return lastDumpId;
     748             : }
     749             : 
     750             : /*
     751             :  * Find a DumpableObject by dump ID
     752             :  *
     753             :  * Returns NULL for invalid ID
     754             :  */
     755             : DumpableObject *
     756    35951716 : findObjectByDumpId(DumpId dumpId)
     757             : {
     758    35951716 :     if (dumpId <= 0 || dumpId >= allocedDumpIds)
     759           0 :         return NULL;            /* out of range? */
     760    35951716 :     return dumpIdMap[dumpId];
     761             : }
     762             : 
     763             : /*
     764             :  * Find a DumpableObject by catalog ID
     765             :  *
     766             :  * Returns NULL for unknown ID
     767             :  */
     768             : DumpableObject *
     769     3741228 : findObjectByCatalogId(CatalogId catalogId)
     770             : {
     771             :     CatalogIdMapEntry *entry;
     772             : 
     773     3741228 :     if (catalogIdHash == NULL)
     774           0 :         return NULL;            /* no objects exist yet */
     775             : 
     776     3741228 :     entry = catalogid_lookup(catalogIdHash, catalogId);
     777     3741228 :     if (entry == NULL)
     778     1061354 :         return NULL;
     779     2679874 :     return entry->dobj;
     780             : }
     781             : 
     782             : /*
     783             :  * Build an array of pointers to all known dumpable objects
     784             :  *
     785             :  * This simply creates a modifiable copy of the internal map.
     786             :  */
     787             : void
     788         316 : getDumpableObjects(DumpableObject ***objs, int *numObjs)
     789             : {
     790             :     int         i,
     791             :                 j;
     792             : 
     793         316 :     *objs = pg_malloc_array(DumpableObject *, allocedDumpIds);
     794         316 :     j = 0;
     795     1409024 :     for (i = 1; i < allocedDumpIds; i++)
     796             :     {
     797     1408708 :         if (dumpIdMap[i])
     798     1141078 :             (*objs)[j++] = dumpIdMap[i];
     799             :     }
     800         316 :     *numObjs = j;
     801         316 : }
     802             : 
     803             : /*
     804             :  * Add a dependency link to a DumpableObject
     805             :  *
     806             :  * Note: duplicate dependencies are currently not eliminated
     807             :  */
     808             : void
     809     1728096 : addObjectDependency(DumpableObject *dobj, DumpId refId)
     810             : {
     811     1728096 :     if (dobj->nDeps >= dobj->allocDeps)
     812             :     {
     813      281822 :         if (dobj->allocDeps <= 0)
     814             :         {
     815      274148 :             dobj->allocDeps = 16;
     816      274148 :             dobj->dependencies = pg_malloc_array(DumpId, dobj->allocDeps);
     817             :         }
     818             :         else
     819             :         {
     820        7674 :             dobj->allocDeps *= 2;
     821        7674 :             dobj->dependencies = pg_realloc_array(dobj->dependencies,
     822             :                                                   DumpId, dobj->allocDeps);
     823             :         }
     824             :     }
     825     1728096 :     dobj->dependencies[dobj->nDeps++] = refId;
     826     1728096 : }
     827             : 
     828             : /*
     829             :  * Remove a dependency link from a DumpableObject
     830             :  *
     831             :  * If there are multiple links, all are removed
     832             :  */
     833             : void
     834       49308 : removeObjectDependency(DumpableObject *dobj, DumpId refId)
     835             : {
     836             :     int         i;
     837       49308 :     int         j = 0;
     838             : 
     839     1221036 :     for (i = 0; i < dobj->nDeps; i++)
     840             :     {
     841     1171728 :         if (dobj->dependencies[i] != refId)
     842     1120414 :             dobj->dependencies[j++] = dobj->dependencies[i];
     843             :     }
     844       49308 :     dobj->nDeps = j;
     845       49308 : }
     846             : 
     847             : 
     848             : /*
     849             :  * findTableByOid
     850             :  *    finds the DumpableObject for the table with the given oid
     851             :  *    returns NULL if not found
     852             :  */
     853             : TableInfo *
     854      131686 : findTableByOid(Oid oid)
     855             : {
     856             :     CatalogId   catId;
     857             :     DumpableObject *dobj;
     858             : 
     859      131686 :     catId.tableoid = RelationRelationId;
     860      131686 :     catId.oid = oid;
     861      131686 :     dobj = findObjectByCatalogId(catId);
     862             :     Assert(dobj == NULL || dobj->objType == DO_TABLE);
     863      131686 :     return (TableInfo *) dobj;
     864             : }
     865             : 
     866             : /*
     867             :  * findIndexByOid
     868             :  *    finds the DumpableObject for the index with the given oid
     869             :  *    returns NULL if not found
     870             :  */
     871             : static IndxInfo *
     872        1086 : findIndexByOid(Oid oid)
     873             : {
     874             :     CatalogId   catId;
     875             :     DumpableObject *dobj;
     876             : 
     877        1086 :     catId.tableoid = RelationRelationId;
     878        1086 :     catId.oid = oid;
     879        1086 :     dobj = findObjectByCatalogId(catId);
     880             :     Assert(dobj == NULL || dobj->objType == DO_INDEX);
     881        1086 :     return (IndxInfo *) dobj;
     882             : }
     883             : 
     884             : /*
     885             :  * findTypeByOid
     886             :  *    finds the DumpableObject for the type with the given oid
     887             :  *    returns NULL if not found
     888             :  */
     889             : TypeInfo *
     890      140782 : findTypeByOid(Oid oid)
     891             : {
     892             :     CatalogId   catId;
     893             :     DumpableObject *dobj;
     894             : 
     895      140782 :     catId.tableoid = TypeRelationId;
     896      140782 :     catId.oid = oid;
     897      140782 :     dobj = findObjectByCatalogId(catId);
     898             :     Assert(dobj == NULL ||
     899             :            dobj->objType == DO_TYPE || dobj->objType == DO_DUMMY_TYPE);
     900      140782 :     return (TypeInfo *) dobj;
     901             : }
     902             : 
     903             : /*
     904             :  * findFuncByOid
     905             :  *    finds the DumpableObject for the function with the given oid
     906             :  *    returns NULL if not found
     907             :  */
     908             : FuncInfo *
     909         520 : findFuncByOid(Oid oid)
     910             : {
     911             :     CatalogId   catId;
     912             :     DumpableObject *dobj;
     913             : 
     914         520 :     catId.tableoid = ProcedureRelationId;
     915         520 :     catId.oid = oid;
     916         520 :     dobj = findObjectByCatalogId(catId);
     917             :     Assert(dobj == NULL || dobj->objType == DO_FUNC);
     918         520 :     return (FuncInfo *) dobj;
     919             : }
     920             : 
     921             : /*
     922             :  * findOprByOid
     923             :  *    finds the DumpableObject for the operator with the given oid
     924             :  *    returns NULL if not found
     925             :  */
     926             : OprInfo *
     927        1916 : findOprByOid(Oid oid)
     928             : {
     929             :     CatalogId   catId;
     930             :     DumpableObject *dobj;
     931             : 
     932        1916 :     catId.tableoid = OperatorRelationId;
     933        1916 :     catId.oid = oid;
     934        1916 :     dobj = findObjectByCatalogId(catId);
     935             :     Assert(dobj == NULL || dobj->objType == DO_OPERATOR);
     936        1916 :     return (OprInfo *) dobj;
     937             : }
     938             : 
     939             : /*
     940             :  * findCollationByOid
     941             :  *    finds the DumpableObject for the collation with the given oid
     942             :  *    returns NULL if not found
     943             :  */
     944             : CollInfo *
     945         280 : findCollationByOid(Oid oid)
     946             : {
     947             :     CatalogId   catId;
     948             :     DumpableObject *dobj;
     949             : 
     950         280 :     catId.tableoid = CollationRelationId;
     951         280 :     catId.oid = oid;
     952         280 :     dobj = findObjectByCatalogId(catId);
     953             :     Assert(dobj == NULL || dobj->objType == DO_COLLATION);
     954         280 :     return (CollInfo *) dobj;
     955             : }
     956             : 
     957             : /*
     958             :  * findNamespaceByOid
     959             :  *    finds the DumpableObject for the namespace with the given oid
     960             :  *    returns NULL if not found
     961             :  */
     962             : NamespaceInfo *
     963      940980 : findNamespaceByOid(Oid oid)
     964             : {
     965             :     CatalogId   catId;
     966             :     DumpableObject *dobj;
     967             : 
     968      940980 :     catId.tableoid = NamespaceRelationId;
     969      940980 :     catId.oid = oid;
     970      940980 :     dobj = findObjectByCatalogId(catId);
     971             :     Assert(dobj == NULL || dobj->objType == DO_NAMESPACE);
     972      940980 :     return (NamespaceInfo *) dobj;
     973             : }
     974             : 
     975             : /*
     976             :  * findExtensionByOid
     977             :  *    finds the DumpableObject for the extension with the given oid
     978             :  *    returns NULL if not found
     979             :  */
     980             : ExtensionInfo *
     981         356 : findExtensionByOid(Oid oid)
     982             : {
     983             :     CatalogId   catId;
     984             :     DumpableObject *dobj;
     985             : 
     986         356 :     catId.tableoid = ExtensionRelationId;
     987         356 :     catId.oid = oid;
     988         356 :     dobj = findObjectByCatalogId(catId);
     989             :     Assert(dobj == NULL || dobj->objType == DO_EXTENSION);
     990         356 :     return (ExtensionInfo *) dobj;
     991             : }
     992             : 
     993             : /*
     994             :  * findPublicationByOid
     995             :  *    finds the DumpableObject for the publication with the given oid
     996             :  *    returns NULL if not found
     997             :  */
     998             : PublicationInfo *
     999         774 : findPublicationByOid(Oid oid)
    1000             : {
    1001             :     CatalogId   catId;
    1002             :     DumpableObject *dobj;
    1003             : 
    1004         774 :     catId.tableoid = PublicationRelationId;
    1005         774 :     catId.oid = oid;
    1006         774 :     dobj = findObjectByCatalogId(catId);
    1007             :     Assert(dobj == NULL || dobj->objType == DO_PUBLICATION);
    1008         774 :     return (PublicationInfo *) dobj;
    1009             : }
    1010             : 
    1011             : /*
    1012             :  * findSubscriptionByOid
    1013             :  *    finds the DumpableObject for the subscription with the given oid
    1014             :  *    returns NULL if not found
    1015             :  */
    1016             : SubscriptionInfo *
    1017           4 : findSubscriptionByOid(Oid oid)
    1018             : {
    1019             :     CatalogId   catId;
    1020             :     DumpableObject *dobj;
    1021             : 
    1022           4 :     catId.tableoid = SubscriptionRelationId;
    1023           4 :     catId.oid = oid;
    1024           4 :     dobj = findObjectByCatalogId(catId);
    1025             :     Assert(dobj == NULL || dobj->objType == DO_SUBSCRIPTION);
    1026           4 :     return (SubscriptionInfo *) dobj;
    1027             : }
    1028             : 
    1029             : 
    1030             : /*
    1031             :  * recordExtensionMembership
    1032             :  *    Record that the object identified by the given catalog ID
    1033             :  *    belongs to the given extension
    1034             :  */
    1035             : void
    1036        2424 : recordExtensionMembership(CatalogId catId, ExtensionInfo *ext)
    1037             : {
    1038             :     CatalogIdMapEntry *entry;
    1039             :     bool        found;
    1040             : 
    1041             :     /* CatalogId hash table must exist, if we have an ExtensionInfo */
    1042             :     Assert(catalogIdHash != NULL);
    1043             : 
    1044             :     /* Add reference to CatalogId hash */
    1045        2424 :     entry = catalogid_insert(catalogIdHash, catId, &found);
    1046        2424 :     if (!found)
    1047             :     {
    1048        2424 :         entry->dobj = NULL;
    1049        2424 :         entry->ext = NULL;
    1050             :     }
    1051             :     Assert(entry->ext == NULL);
    1052        2424 :     entry->ext = ext;
    1053        2424 : }
    1054             : 
    1055             : /*
    1056             :  * findOwningExtension
    1057             :  *    return owning extension for specified catalog ID, or NULL if none
    1058             :  */
    1059             : ExtensionInfo *
    1060      939410 : findOwningExtension(CatalogId catalogId)
    1061             : {
    1062             :     CatalogIdMapEntry *entry;
    1063             : 
    1064      939410 :     if (catalogIdHash == NULL)
    1065           0 :         return NULL;            /* no objects exist yet */
    1066             : 
    1067      939410 :     entry = catalogid_lookup(catalogIdHash, catalogId);
    1068      939410 :     if (entry == NULL)
    1069           0 :         return NULL;
    1070      939410 :     return entry->ext;
    1071             : }
    1072             : 
    1073             : 
    1074             : /*
    1075             :  * parseOidArray
    1076             :  *    parse a string of numbers delimited by spaces into a character array
    1077             :  *
    1078             :  * Note: actually this is used for both Oids and potentially-signed
    1079             :  * attribute numbers.  This should cause no trouble, but we could split
    1080             :  * the function into two functions with different argument types if it does.
    1081             :  */
    1082             : 
    1083             : void
    1084       10546 : parseOidArray(const char *str, Oid *array, int arraysize)
    1085             : {
    1086             :     int         j,
    1087             :                 argNum;
    1088             :     char        temp[100];
    1089             :     char        s;
    1090             : 
    1091       10546 :     argNum = 0;
    1092       10546 :     j = 0;
    1093             :     for (;;)
    1094             :     {
    1095       49664 :         s = *str++;
    1096       49664 :         if (s == ' ' || s == '\0')
    1097             :         {
    1098       17062 :             if (j > 0)
    1099             :             {
    1100       17062 :                 if (argNum >= arraysize)
    1101           0 :                     pg_fatal("could not parse numeric array \"%s\": too many numbers", str);
    1102       17062 :                 temp[j] = '\0';
    1103       17062 :                 array[argNum++] = atooid(temp);
    1104       17062 :                 j = 0;
    1105             :             }
    1106       17062 :             if (s == '\0')
    1107       10546 :                 break;
    1108             :         }
    1109             :         else
    1110             :         {
    1111       32602 :             if (!(isdigit((unsigned char) s) || s == '-') ||
    1112       32602 :                 j >= sizeof(temp) - 1)
    1113           0 :                 pg_fatal("could not parse numeric array \"%s\": invalid character in number", str);
    1114       32602 :             temp[j++] = s;
    1115             :         }
    1116             :     }
    1117             : 
    1118       10546 :     while (argNum < arraysize)
    1119           0 :         array[argNum++] = InvalidOid;
    1120       10546 : }
    1121             : 
    1122             : 
    1123             : /*
    1124             :  * strInArray:
    1125             :  *    takes in a string and a string array and the number of elements in the
    1126             :  * string array.
    1127             :  *    returns the index if the string is somewhere in the array, -1 otherwise
    1128             :  */
    1129             : 
    1130             : static int
    1131        8044 : strInArray(const char *pattern, char **arr, int arr_size)
    1132             : {
    1133             :     int         i;
    1134             : 
    1135       16090 :     for (i = 0; i < arr_size; i++)
    1136             :     {
    1137       15640 :         if (strcmp(pattern, arr[i]) == 0)
    1138        7594 :             return i;
    1139             :     }
    1140         450 :     return -1;
    1141             : }

Generated by: LCOV version 1.14