]> granicus.if.org Git - postgresql/blob - src/bin/pg_dump/common.c
Make the order of the header file includes consistent in non-backend modules.
[postgresql] / src / bin / pg_dump / common.c
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-2019, 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 "fe_utils/string_utils.h"
22 #include "pg_backup_archiver.h"
23 #include "pg_backup_utils.h"
24 #include "pg_dump.h"
25
26 /*
27  * Variables for mapping DumpId to DumpableObject
28  */
29 static DumpableObject **dumpIdMap = NULL;
30 static int      allocedDumpIds = 0;
31 static DumpId lastDumpId = 0;
32
33 /*
34  * Variables for mapping CatalogId to DumpableObject
35  */
36 static bool catalogIdMapValid = false;
37 static DumpableObject **catalogIdMap = NULL;
38 static int      numCatalogIds = 0;
39
40 /*
41  * These variables are static to avoid the notational cruft of having to pass
42  * them into findTableByOid() and friends.  For each of these arrays, we build
43  * a sorted-by-OID index array immediately after the objects are fetched,
44  * and then we use binary search in findTableByOid() and friends.  (qsort'ing
45  * the object arrays themselves would be simpler, but it doesn't work because
46  * pg_dump.c may have already established pointers between items.)
47  */
48 static DumpableObject **tblinfoindex;
49 static DumpableObject **typinfoindex;
50 static DumpableObject **funinfoindex;
51 static DumpableObject **oprinfoindex;
52 static DumpableObject **collinfoindex;
53 static DumpableObject **nspinfoindex;
54 static DumpableObject **extinfoindex;
55 static int      numTables;
56 static int      numTypes;
57 static int      numFuncs;
58 static int      numOperators;
59 static int      numCollations;
60 static int      numNamespaces;
61 static int      numExtensions;
62
63 /* This is an array of object identities, not actual DumpableObjects */
64 static ExtensionMemberId *extmembers;
65 static int      numextmembers;
66
67 static void flagInhTables(Archive *fout, TableInfo *tbinfo, int numTables,
68                                                   InhInfo *inhinfo, int numInherits);
69 static void flagInhIndexes(Archive *fout, TableInfo *tblinfo, int numTables);
70 static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables);
71 static DumpableObject **buildIndexArray(void *objArray, int numObjs,
72                                                                                 Size objSize);
73 static int      DOCatalogIdCompare(const void *p1, const void *p2);
74 static int      ExtensionMemberIdCompare(const void *p1, const void *p2);
75 static void findParentsByOid(TableInfo *self,
76                                                          InhInfo *inhinfo, int numInherits);
77 static int      strInArray(const char *pattern, char **arr, int arr_size);
78 static IndxInfo *findIndexByOid(Oid oid, DumpableObject **idxinfoindex,
79                                                                 int numIndexes);
80
81
82 /*
83  * getSchemaData
84  *        Collect information about all potentially dumpable objects
85  */
86 TableInfo *
87 getSchemaData(Archive *fout, int *numTablesPtr)
88 {
89         TableInfo  *tblinfo;
90         TypeInfo   *typinfo;
91         FuncInfo   *funinfo;
92         OprInfo    *oprinfo;
93         CollInfo   *collinfo;
94         NamespaceInfo *nspinfo;
95         ExtensionInfo *extinfo;
96         InhInfo    *inhinfo;
97         int                     numAggregates;
98         int                     numInherits;
99         int                     numRules;
100         int                     numProcLangs;
101         int                     numCasts;
102         int                     numTransforms;
103         int                     numAccessMethods;
104         int                     numOpclasses;
105         int                     numOpfamilies;
106         int                     numConversions;
107         int                     numTSParsers;
108         int                     numTSTemplates;
109         int                     numTSDicts;
110         int                     numTSConfigs;
111         int                     numForeignDataWrappers;
112         int                     numForeignServers;
113         int                     numDefaultACLs;
114         int                     numEventTriggers;
115
116         /*
117          * We must read extensions and extension membership info first, because
118          * extension membership needs to be consultable during decisions about
119          * whether other objects are to be dumped.
120          */
121         pg_log_info("reading extensions");
122         extinfo = getExtensions(fout, &numExtensions);
123         extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo));
124
125         pg_log_info("identifying extension members");
126         getExtensionMembership(fout, extinfo, numExtensions);
127
128         pg_log_info("reading schemas");
129         nspinfo = getNamespaces(fout, &numNamespaces);
130         nspinfoindex = buildIndexArray(nspinfo, numNamespaces, sizeof(NamespaceInfo));
131
132         /*
133          * getTables should be done as soon as possible, so as to minimize the
134          * window between starting our transaction and acquiring per-table locks.
135          * However, we have to do getNamespaces first because the tables get
136          * linked to their containing namespaces during getTables.
137          */
138         pg_log_info("reading user-defined tables");
139         tblinfo = getTables(fout, &numTables);
140         tblinfoindex = buildIndexArray(tblinfo, numTables, sizeof(TableInfo));
141
142         /* Do this after we've built tblinfoindex */
143         getOwnedSeqs(fout, tblinfo, numTables);
144
145         pg_log_info("reading user-defined functions");
146         funinfo = getFuncs(fout, &numFuncs);
147         funinfoindex = buildIndexArray(funinfo, numFuncs, sizeof(FuncInfo));
148
149         /* this must be after getTables and getFuncs */
150         pg_log_info("reading user-defined types");
151         typinfo = getTypes(fout, &numTypes);
152         typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo));
153
154         /* this must be after getFuncs, too */
155         pg_log_info("reading procedural languages");
156         getProcLangs(fout, &numProcLangs);
157
158         pg_log_info("reading user-defined aggregate functions");
159         getAggregates(fout, &numAggregates);
160
161         pg_log_info("reading user-defined operators");
162         oprinfo = getOperators(fout, &numOperators);
163         oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
164
165         pg_log_info("reading user-defined access methods");
166         getAccessMethods(fout, &numAccessMethods);
167
168         pg_log_info("reading user-defined operator classes");
169         getOpclasses(fout, &numOpclasses);
170
171         pg_log_info("reading user-defined operator families");
172         getOpfamilies(fout, &numOpfamilies);
173
174         pg_log_info("reading user-defined text search parsers");
175         getTSParsers(fout, &numTSParsers);
176
177         pg_log_info("reading user-defined text search templates");
178         getTSTemplates(fout, &numTSTemplates);
179
180         pg_log_info("reading user-defined text search dictionaries");
181         getTSDictionaries(fout, &numTSDicts);
182
183         pg_log_info("reading user-defined text search configurations");
184         getTSConfigurations(fout, &numTSConfigs);
185
186         pg_log_info("reading user-defined foreign-data wrappers");
187         getForeignDataWrappers(fout, &numForeignDataWrappers);
188
189         pg_log_info("reading user-defined foreign servers");
190         getForeignServers(fout, &numForeignServers);
191
192         pg_log_info("reading default privileges");
193         getDefaultACLs(fout, &numDefaultACLs);
194
195         pg_log_info("reading user-defined collations");
196         collinfo = getCollations(fout, &numCollations);
197         collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo));
198
199         pg_log_info("reading user-defined conversions");
200         getConversions(fout, &numConversions);
201
202         pg_log_info("reading type casts");
203         getCasts(fout, &numCasts);
204
205         pg_log_info("reading transforms");
206         getTransforms(fout, &numTransforms);
207
208         pg_log_info("reading table inheritance information");
209         inhinfo = getInherits(fout, &numInherits);
210
211         pg_log_info("reading event triggers");
212         getEventTriggers(fout, &numEventTriggers);
213
214         /* Identify extension configuration tables that should be dumped */
215         pg_log_info("finding extension tables");
216         processExtensionTables(fout, extinfo, numExtensions);
217
218         /* Link tables to parents, mark parents of target tables interesting */
219         pg_log_info("finding inheritance relationships");
220         flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits);
221
222         pg_log_info("reading column info for interesting tables");
223         getTableAttrs(fout, tblinfo, numTables);
224
225         pg_log_info("flagging inherited columns in subtables");
226         flagInhAttrs(fout->dopt, tblinfo, numTables);
227
228         pg_log_info("reading indexes");
229         getIndexes(fout, tblinfo, numTables);
230
231         pg_log_info("flagging indexes in partitioned tables");
232         flagInhIndexes(fout, tblinfo, numTables);
233
234         pg_log_info("reading extended statistics");
235         getExtendedStatistics(fout);
236
237         pg_log_info("reading constraints");
238         getConstraints(fout, tblinfo, numTables);
239
240         pg_log_info("reading triggers");
241         getTriggers(fout, tblinfo, numTables);
242
243         pg_log_info("reading rewrite rules");
244         getRules(fout, &numRules);
245
246         pg_log_info("reading policies");
247         getPolicies(fout, tblinfo, numTables);
248
249         pg_log_info("reading publications");
250         getPublications(fout);
251
252         pg_log_info("reading publication membership");
253         getPublicationTables(fout, tblinfo, numTables);
254
255         pg_log_info("reading subscriptions");
256         getSubscriptions(fout);
257
258         *numTablesPtr = numTables;
259         return tblinfo;
260 }
261
262 /* flagInhTables -
263  *       Fill in parent link fields of tables for which we need that information,
264  *       and mark parents of target tables as interesting
265  *
266  * Note that only direct ancestors of targets are marked interesting.
267  * This is sufficient; we don't much care whether they inherited their
268  * attributes or not.
269  *
270  * modifies tblinfo
271  */
272 static void
273 flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
274                           InhInfo *inhinfo, int numInherits)
275 {
276         DumpOptions *dopt = fout->dopt;
277         int                     i,
278                                 j;
279
280         for (i = 0; i < numTables; i++)
281         {
282                 bool            find_parents = true;
283                 bool            mark_parents = true;
284
285                 /* Some kinds never have parents */
286                 if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
287                         tblinfo[i].relkind == RELKIND_VIEW ||
288                         tblinfo[i].relkind == RELKIND_MATVIEW)
289                         continue;
290
291                 /*
292                  * Normally, we don't bother computing anything for non-target tables,
293                  * but if load-via-partition-root is specified, we gather information
294                  * on every partition in the system so that getRootTableInfo can trace
295                  * from any given to leaf partition all the way up to the root.  (We
296                  * don't need to mark them as interesting for getTableAttrs, though.)
297                  */
298                 if (!tblinfo[i].dobj.dump)
299                 {
300                         mark_parents = false;
301
302                         if (!dopt->load_via_partition_root ||
303                                 !tblinfo[i].ispartition)
304                                 find_parents = false;
305                 }
306
307                 /* If needed, find all the immediate parent tables. */
308                 if (find_parents)
309                         findParentsByOid(&tblinfo[i], inhinfo, numInherits);
310
311                 /*
312                  * If needed, mark the parents as interesting for getTableAttrs and
313                  * getIndexes.
314                  */
315                 if (mark_parents)
316                 {
317                         int                     numParents = tblinfo[i].numParents;
318                         TableInfo **parents = tblinfo[i].parents;
319
320                         for (j = 0; j < numParents; j++)
321                                 parents[j]->interesting = true;
322                 }
323         }
324 }
325
326 /*
327  * flagInhIndexes -
328  *       Create IndexAttachInfo objects for partitioned indexes, and add
329  *       appropriate dependency links.
330  */
331 static void
332 flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
333 {
334         int                     i,
335                                 j,
336                                 k;
337         DumpableObject ***parentIndexArray;
338
339         parentIndexArray = (DumpableObject ***)
340                 pg_malloc0(getMaxDumpId() * sizeof(DumpableObject **));
341
342         for (i = 0; i < numTables; i++)
343         {
344                 TableInfo  *parenttbl;
345                 IndexAttachInfo *attachinfo;
346
347                 if (!tblinfo[i].ispartition || tblinfo[i].numParents == 0)
348                         continue;
349
350                 Assert(tblinfo[i].numParents == 1);
351                 parenttbl = tblinfo[i].parents[0];
352
353                 /*
354                  * We need access to each parent table's index list, but there is no
355                  * index to cover them outside of this function.  To avoid having to
356                  * sort every parent table's indexes each time we come across each of
357                  * its partitions, create an indexed array for each parent the first
358                  * time it is required.
359                  */
360                 if (parentIndexArray[parenttbl->dobj.dumpId] == NULL)
361                         parentIndexArray[parenttbl->dobj.dumpId] =
362                                 buildIndexArray(parenttbl->indexes,
363                                                                 parenttbl->numIndexes,
364                                                                 sizeof(IndxInfo));
365
366                 attachinfo = (IndexAttachInfo *)
367                         pg_malloc0(tblinfo[i].numIndexes * sizeof(IndexAttachInfo));
368                 for (j = 0, k = 0; j < tblinfo[i].numIndexes; j++)
369                 {
370                         IndxInfo   *index = &(tblinfo[i].indexes[j]);
371                         IndxInfo   *parentidx;
372
373                         if (index->parentidx == 0)
374                                 continue;
375
376                         parentidx = findIndexByOid(index->parentidx,
377                                                                            parentIndexArray[parenttbl->dobj.dumpId],
378                                                                            parenttbl->numIndexes);
379                         if (parentidx == NULL)
380                                 continue;
381
382                         attachinfo[k].dobj.objType = DO_INDEX_ATTACH;
383                         attachinfo[k].dobj.catId.tableoid = 0;
384                         attachinfo[k].dobj.catId.oid = 0;
385                         AssignDumpId(&attachinfo[k].dobj);
386                         attachinfo[k].dobj.name = pg_strdup(index->dobj.name);
387                         attachinfo[k].dobj.namespace = index->indextable->dobj.namespace;
388                         attachinfo[k].parentIdx = parentidx;
389                         attachinfo[k].partitionIdx = index;
390
391                         /*
392                          * We must state the DO_INDEX_ATTACH object's dependencies
393                          * explicitly, since it will not match anything in pg_depend.
394                          *
395                          * Give it dependencies on both the partition index and the parent
396                          * index, so that it will not be executed till both of those
397                          * exist.  (There's no need to care what order those are created
398                          * in.)
399                          *
400                          * In addition, give it dependencies on the indexes' underlying
401                          * tables.  This does nothing of great value so far as serial
402                          * restore ordering goes, but it ensures that a parallel restore
403                          * will not try to run the ATTACH concurrently with other
404                          * operations on those tables.
405                          */
406                         addObjectDependency(&attachinfo[k].dobj, index->dobj.dumpId);
407                         addObjectDependency(&attachinfo[k].dobj, parentidx->dobj.dumpId);
408                         addObjectDependency(&attachinfo[k].dobj,
409                                                                 index->indextable->dobj.dumpId);
410                         addObjectDependency(&attachinfo[k].dobj,
411                                                                 parentidx->indextable->dobj.dumpId);
412
413                         /* keep track of the list of partitions in the parent index */
414                         simple_ptr_list_append(&parentidx->partattaches, &attachinfo[k].dobj);
415
416                         k++;
417                 }
418         }
419
420         for (i = 0; i < numTables; i++)
421                 if (parentIndexArray[i])
422                         pg_free(parentIndexArray[i]);
423         pg_free(parentIndexArray);
424 }
425
426 /* flagInhAttrs -
427  *       for each dumpable table in tblinfo, flag its inherited attributes
428  *
429  * What we need to do here is detect child columns that inherit NOT NULL
430  * bits from their parents (so that we needn't specify that again for the
431  * child) and child columns that have DEFAULT NULL when their parents had
432  * some non-null default.  In the latter case, we make up a dummy AttrDefInfo
433  * object so that we'll correctly emit the necessary DEFAULT NULL clause;
434  * otherwise the backend will apply an inherited default to the column.
435  *
436  * modifies tblinfo
437  */
438 static void
439 flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables)
440 {
441         int                     i,
442                                 j,
443                                 k;
444
445         for (i = 0; i < numTables; i++)
446         {
447                 TableInfo  *tbinfo = &(tblinfo[i]);
448                 int                     numParents;
449                 TableInfo **parents;
450
451                 /* Some kinds never have parents */
452                 if (tbinfo->relkind == RELKIND_SEQUENCE ||
453                         tbinfo->relkind == RELKIND_VIEW ||
454                         tbinfo->relkind == RELKIND_MATVIEW)
455                         continue;
456
457                 /* Don't bother computing anything for non-target tables, either */
458                 if (!tbinfo->dobj.dump)
459                         continue;
460
461                 numParents = tbinfo->numParents;
462                 parents = tbinfo->parents;
463
464                 if (numParents == 0)
465                         continue;                       /* nothing to see here, move along */
466
467                 /* For each column, search for matching column names in parent(s) */
468                 for (j = 0; j < tbinfo->numatts; j++)
469                 {
470                         bool            foundNotNull;   /* Attr was NOT NULL in a parent */
471                         bool            foundDefault;   /* Found a default in a parent */
472
473                         /* no point in examining dropped columns */
474                         if (tbinfo->attisdropped[j])
475                                 continue;
476
477                         foundNotNull = false;
478                         foundDefault = false;
479                         for (k = 0; k < numParents; k++)
480                         {
481                                 TableInfo  *parent = parents[k];
482                                 int                     inhAttrInd;
483
484                                 inhAttrInd = strInArray(tbinfo->attnames[j],
485                                                                                 parent->attnames,
486                                                                                 parent->numatts);
487                                 if (inhAttrInd >= 0)
488                                 {
489                                         foundNotNull |= parent->notnull[inhAttrInd];
490                                         foundDefault |= (parent->attrdefs[inhAttrInd] != NULL);
491                                 }
492                         }
493
494                         /* Remember if we found inherited NOT NULL */
495                         tbinfo->inhNotNull[j] = foundNotNull;
496
497                         /* Manufacture a DEFAULT NULL clause if necessary */
498                         if (foundDefault && tbinfo->attrdefs[j] == NULL)
499                         {
500                                 AttrDefInfo *attrDef;
501
502                                 attrDef = (AttrDefInfo *) pg_malloc(sizeof(AttrDefInfo));
503                                 attrDef->dobj.objType = DO_ATTRDEF;
504                                 attrDef->dobj.catId.tableoid = 0;
505                                 attrDef->dobj.catId.oid = 0;
506                                 AssignDumpId(&attrDef->dobj);
507                                 attrDef->dobj.name = pg_strdup(tbinfo->dobj.name);
508                                 attrDef->dobj.namespace = tbinfo->dobj.namespace;
509                                 attrDef->dobj.dump = tbinfo->dobj.dump;
510
511                                 attrDef->adtable = tbinfo;
512                                 attrDef->adnum = j + 1;
513                                 attrDef->adef_expr = pg_strdup("NULL");
514
515                                 /* Will column be dumped explicitly? */
516                                 if (shouldPrintColumn(dopt, tbinfo, j))
517                                 {
518                                         attrDef->separate = false;
519                                         /* No dependency needed: NULL cannot have dependencies */
520                                 }
521                                 else
522                                 {
523                                         /* column will be suppressed, print default separately */
524                                         attrDef->separate = true;
525                                         /* ensure it comes out after the table */
526                                         addObjectDependency(&attrDef->dobj,
527                                                                                 tbinfo->dobj.dumpId);
528                                 }
529
530                                 tbinfo->attrdefs[j] = attrDef;
531                         }
532                 }
533         }
534 }
535
536 /*
537  * AssignDumpId
538  *              Given a newly-created dumpable object, assign a dump ID,
539  *              and enter the object into the lookup table.
540  *
541  * The caller is expected to have filled in objType and catId,
542  * but not any of the other standard fields of a DumpableObject.
543  */
544 void
545 AssignDumpId(DumpableObject *dobj)
546 {
547         dobj->dumpId = ++lastDumpId;
548         dobj->name = NULL;                      /* must be set later */
549         dobj->namespace = NULL;         /* may be set later */
550         dobj->dump = DUMP_COMPONENT_ALL;        /* default assumption */
551         dobj->ext_member = false;       /* default assumption */
552         dobj->dependencies = NULL;
553         dobj->nDeps = 0;
554         dobj->allocDeps = 0;
555
556         while (dobj->dumpId >= allocedDumpIds)
557         {
558                 int                     newAlloc;
559
560                 if (allocedDumpIds <= 0)
561                 {
562                         newAlloc = 256;
563                         dumpIdMap = (DumpableObject **)
564                                 pg_malloc(newAlloc * sizeof(DumpableObject *));
565                 }
566                 else
567                 {
568                         newAlloc = allocedDumpIds * 2;
569                         dumpIdMap = (DumpableObject **)
570                                 pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
571                 }
572                 memset(dumpIdMap + allocedDumpIds, 0,
573                            (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
574                 allocedDumpIds = newAlloc;
575         }
576         dumpIdMap[dobj->dumpId] = dobj;
577
578         /* mark catalogIdMap invalid, but don't rebuild it yet */
579         catalogIdMapValid = false;
580 }
581
582 /*
583  * Assign a DumpId that's not tied to a DumpableObject.
584  *
585  * This is used when creating a "fixed" ArchiveEntry that doesn't need to
586  * participate in the sorting logic.
587  */
588 DumpId
589 createDumpId(void)
590 {
591         return ++lastDumpId;
592 }
593
594 /*
595  * Return the largest DumpId so far assigned
596  */
597 DumpId
598 getMaxDumpId(void)
599 {
600         return lastDumpId;
601 }
602
603 /*
604  * Find a DumpableObject by dump ID
605  *
606  * Returns NULL for invalid ID
607  */
608 DumpableObject *
609 findObjectByDumpId(DumpId dumpId)
610 {
611         if (dumpId <= 0 || dumpId >= allocedDumpIds)
612                 return NULL;                    /* out of range? */
613         return dumpIdMap[dumpId];
614 }
615
616 /*
617  * Find a DumpableObject by catalog ID
618  *
619  * Returns NULL for unknown ID
620  *
621  * We use binary search in a sorted list that is built on first call.
622  * If AssignDumpId() and findObjectByCatalogId() calls were freely intermixed,
623  * the code would work, but possibly be very slow.  In the current usage
624  * pattern that does not happen, indeed we build the list at most twice.
625  */
626 DumpableObject *
627 findObjectByCatalogId(CatalogId catalogId)
628 {
629         DumpableObject **low;
630         DumpableObject **high;
631
632         if (!catalogIdMapValid)
633         {
634                 if (catalogIdMap)
635                         free(catalogIdMap);
636                 getDumpableObjects(&catalogIdMap, &numCatalogIds);
637                 if (numCatalogIds > 1)
638                         qsort((void *) catalogIdMap, numCatalogIds,
639                                   sizeof(DumpableObject *), DOCatalogIdCompare);
640                 catalogIdMapValid = true;
641         }
642
643         /*
644          * We could use bsearch() here, but the notational cruft of calling
645          * bsearch is nearly as bad as doing it ourselves; and the generalized
646          * bsearch function is noticeably slower as well.
647          */
648         if (numCatalogIds <= 0)
649                 return NULL;
650         low = catalogIdMap;
651         high = catalogIdMap + (numCatalogIds - 1);
652         while (low <= high)
653         {
654                 DumpableObject **middle;
655                 int                     difference;
656
657                 middle = low + (high - low) / 2;
658                 /* comparison must match DOCatalogIdCompare, below */
659                 difference = oidcmp((*middle)->catId.oid, catalogId.oid);
660                 if (difference == 0)
661                         difference = oidcmp((*middle)->catId.tableoid, catalogId.tableoid);
662                 if (difference == 0)
663                         return *middle;
664                 else if (difference < 0)
665                         low = middle + 1;
666                 else
667                         high = middle - 1;
668         }
669         return NULL;
670 }
671
672 /*
673  * Find a DumpableObject by OID, in a pre-sorted array of one type of object
674  *
675  * Returns NULL for unknown OID
676  */
677 static DumpableObject *
678 findObjectByOid(Oid oid, DumpableObject **indexArray, int numObjs)
679 {
680         DumpableObject **low;
681         DumpableObject **high;
682
683         /*
684          * This is the same as findObjectByCatalogId except we assume we need not
685          * look at table OID because the objects are all the same type.
686          *
687          * We could use bsearch() here, but the notational cruft of calling
688          * bsearch is nearly as bad as doing it ourselves; and the generalized
689          * bsearch function is noticeably slower as well.
690          */
691         if (numObjs <= 0)
692                 return NULL;
693         low = indexArray;
694         high = indexArray + (numObjs - 1);
695         while (low <= high)
696         {
697                 DumpableObject **middle;
698                 int                     difference;
699
700                 middle = low + (high - low) / 2;
701                 difference = oidcmp((*middle)->catId.oid, oid);
702                 if (difference == 0)
703                         return *middle;
704                 else if (difference < 0)
705                         low = middle + 1;
706                 else
707                         high = middle - 1;
708         }
709         return NULL;
710 }
711
712 /*
713  * Build an index array of DumpableObject pointers, sorted by OID
714  */
715 static DumpableObject **
716 buildIndexArray(void *objArray, int numObjs, Size objSize)
717 {
718         DumpableObject **ptrs;
719         int                     i;
720
721         ptrs = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *));
722         for (i = 0; i < numObjs; i++)
723                 ptrs[i] = (DumpableObject *) ((char *) objArray + i * objSize);
724
725         /* We can use DOCatalogIdCompare to sort since its first key is OID */
726         if (numObjs > 1)
727                 qsort((void *) ptrs, numObjs, sizeof(DumpableObject *),
728                           DOCatalogIdCompare);
729
730         return ptrs;
731 }
732
733 /*
734  * qsort comparator for pointers to DumpableObjects
735  */
736 static int
737 DOCatalogIdCompare(const void *p1, const void *p2)
738 {
739         const DumpableObject *obj1 = *(DumpableObject *const *) p1;
740         const DumpableObject *obj2 = *(DumpableObject *const *) p2;
741         int                     cmpval;
742
743         /*
744          * Compare OID first since it's usually unique, whereas there will only be
745          * a few distinct values of tableoid.
746          */
747         cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
748         if (cmpval == 0)
749                 cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
750         return cmpval;
751 }
752
753 /*
754  * Build an array of pointers to all known dumpable objects
755  *
756  * This simply creates a modifiable copy of the internal map.
757  */
758 void
759 getDumpableObjects(DumpableObject ***objs, int *numObjs)
760 {
761         int                     i,
762                                 j;
763
764         *objs = (DumpableObject **)
765                 pg_malloc(allocedDumpIds * sizeof(DumpableObject *));
766         j = 0;
767         for (i = 1; i < allocedDumpIds; i++)
768         {
769                 if (dumpIdMap[i])
770                         (*objs)[j++] = dumpIdMap[i];
771         }
772         *numObjs = j;
773 }
774
775 /*
776  * Add a dependency link to a DumpableObject
777  *
778  * Note: duplicate dependencies are currently not eliminated
779  */
780 void
781 addObjectDependency(DumpableObject *dobj, DumpId refId)
782 {
783         if (dobj->nDeps >= dobj->allocDeps)
784         {
785                 if (dobj->allocDeps <= 0)
786                 {
787                         dobj->allocDeps = 16;
788                         dobj->dependencies = (DumpId *)
789                                 pg_malloc(dobj->allocDeps * sizeof(DumpId));
790                 }
791                 else
792                 {
793                         dobj->allocDeps *= 2;
794                         dobj->dependencies = (DumpId *)
795                                 pg_realloc(dobj->dependencies,
796                                                    dobj->allocDeps * sizeof(DumpId));
797                 }
798         }
799         dobj->dependencies[dobj->nDeps++] = refId;
800 }
801
802 /*
803  * Remove a dependency link from a DumpableObject
804  *
805  * If there are multiple links, all are removed
806  */
807 void
808 removeObjectDependency(DumpableObject *dobj, DumpId refId)
809 {
810         int                     i;
811         int                     j = 0;
812
813         for (i = 0; i < dobj->nDeps; i++)
814         {
815                 if (dobj->dependencies[i] != refId)
816                         dobj->dependencies[j++] = dobj->dependencies[i];
817         }
818         dobj->nDeps = j;
819 }
820
821
822 /*
823  * findTableByOid
824  *        finds the entry (in tblinfo) of the table with the given oid
825  *        returns NULL if not found
826  */
827 TableInfo *
828 findTableByOid(Oid oid)
829 {
830         return (TableInfo *) findObjectByOid(oid, tblinfoindex, numTables);
831 }
832
833 /*
834  * findTypeByOid
835  *        finds the entry (in typinfo) of the type with the given oid
836  *        returns NULL if not found
837  */
838 TypeInfo *
839 findTypeByOid(Oid oid)
840 {
841         return (TypeInfo *) findObjectByOid(oid, typinfoindex, numTypes);
842 }
843
844 /*
845  * findFuncByOid
846  *        finds the entry (in funinfo) of the function with the given oid
847  *        returns NULL if not found
848  */
849 FuncInfo *
850 findFuncByOid(Oid oid)
851 {
852         return (FuncInfo *) findObjectByOid(oid, funinfoindex, numFuncs);
853 }
854
855 /*
856  * findOprByOid
857  *        finds the entry (in oprinfo) of the operator with the given oid
858  *        returns NULL if not found
859  */
860 OprInfo *
861 findOprByOid(Oid oid)
862 {
863         return (OprInfo *) findObjectByOid(oid, oprinfoindex, numOperators);
864 }
865
866 /*
867  * findCollationByOid
868  *        finds the entry (in collinfo) of the collation with the given oid
869  *        returns NULL if not found
870  */
871 CollInfo *
872 findCollationByOid(Oid oid)
873 {
874         return (CollInfo *) findObjectByOid(oid, collinfoindex, numCollations);
875 }
876
877 /*
878  * findNamespaceByOid
879  *        finds the entry (in nspinfo) of the namespace with the given oid
880  *        returns NULL if not found
881  */
882 NamespaceInfo *
883 findNamespaceByOid(Oid oid)
884 {
885         return (NamespaceInfo *) findObjectByOid(oid, nspinfoindex, numNamespaces);
886 }
887
888 /*
889  * findExtensionByOid
890  *        finds the entry (in extinfo) of the extension with the given oid
891  *        returns NULL if not found
892  */
893 ExtensionInfo *
894 findExtensionByOid(Oid oid)
895 {
896         return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
897 }
898
899 /*
900  * findIndexByOid
901  *              find the entry of the index with the given oid
902  *
903  * This one's signature is different from the previous ones because we lack a
904  * global array of all indexes, so caller must pass their array as argument.
905  */
906 static IndxInfo *
907 findIndexByOid(Oid oid, DumpableObject **idxinfoindex, int numIndexes)
908 {
909         return (IndxInfo *) findObjectByOid(oid, idxinfoindex, numIndexes);
910 }
911
912 /*
913  * setExtensionMembership
914  *        accept and save data about which objects belong to extensions
915  */
916 void
917 setExtensionMembership(ExtensionMemberId *extmems, int nextmems)
918 {
919         /* Sort array in preparation for binary searches */
920         if (nextmems > 1)
921                 qsort((void *) extmems, nextmems, sizeof(ExtensionMemberId),
922                           ExtensionMemberIdCompare);
923         /* And save */
924         extmembers = extmems;
925         numextmembers = nextmems;
926 }
927
928 /*
929  * findOwningExtension
930  *        return owning extension for specified catalog ID, or NULL if none
931  */
932 ExtensionInfo *
933 findOwningExtension(CatalogId catalogId)
934 {
935         ExtensionMemberId *low;
936         ExtensionMemberId *high;
937
938         /*
939          * We could use bsearch() here, but the notational cruft of calling
940          * bsearch is nearly as bad as doing it ourselves; and the generalized
941          * bsearch function is noticeably slower as well.
942          */
943         if (numextmembers <= 0)
944                 return NULL;
945         low = extmembers;
946         high = extmembers + (numextmembers - 1);
947         while (low <= high)
948         {
949                 ExtensionMemberId *middle;
950                 int                     difference;
951
952                 middle = low + (high - low) / 2;
953                 /* comparison must match ExtensionMemberIdCompare, below */
954                 difference = oidcmp(middle->catId.oid, catalogId.oid);
955                 if (difference == 0)
956                         difference = oidcmp(middle->catId.tableoid, catalogId.tableoid);
957                 if (difference == 0)
958                         return middle->ext;
959                 else if (difference < 0)
960                         low = middle + 1;
961                 else
962                         high = middle - 1;
963         }
964         return NULL;
965 }
966
967 /*
968  * qsort comparator for ExtensionMemberIds
969  */
970 static int
971 ExtensionMemberIdCompare(const void *p1, const void *p2)
972 {
973         const ExtensionMemberId *obj1 = (const ExtensionMemberId *) p1;
974         const ExtensionMemberId *obj2 = (const ExtensionMemberId *) p2;
975         int                     cmpval;
976
977         /*
978          * Compare OID first since it's usually unique, whereas there will only be
979          * a few distinct values of tableoid.
980          */
981         cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
982         if (cmpval == 0)
983                 cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
984         return cmpval;
985 }
986
987
988 /*
989  * findParentsByOid
990  *        find a table's parents in tblinfo[]
991  */
992 static void
993 findParentsByOid(TableInfo *self,
994                                  InhInfo *inhinfo, int numInherits)
995 {
996         Oid                     oid = self->dobj.catId.oid;
997         int                     i,
998                                 j;
999         int                     numParents;
1000
1001         numParents = 0;
1002         for (i = 0; i < numInherits; i++)
1003         {
1004                 if (inhinfo[i].inhrelid == oid)
1005                         numParents++;
1006         }
1007
1008         self->numParents = numParents;
1009
1010         if (numParents > 0)
1011         {
1012                 self->parents = (TableInfo **)
1013                         pg_malloc(sizeof(TableInfo *) * numParents);
1014                 j = 0;
1015                 for (i = 0; i < numInherits; i++)
1016                 {
1017                         if (inhinfo[i].inhrelid == oid)
1018                         {
1019                                 TableInfo  *parent;
1020
1021                                 parent = findTableByOid(inhinfo[i].inhparent);
1022                                 if (parent == NULL)
1023                                 {
1024                                         pg_log_error("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
1025                                                                  inhinfo[i].inhparent,
1026                                                                  self->dobj.name,
1027                                                                  oid);
1028                                         exit_nicely(1);
1029                                 }
1030                                 self->parents[j++] = parent;
1031                         }
1032                 }
1033         }
1034         else
1035                 self->parents = NULL;
1036 }
1037
1038 /*
1039  * parseOidArray
1040  *        parse a string of numbers delimited by spaces into a character array
1041  *
1042  * Note: actually this is used for both Oids and potentially-signed
1043  * attribute numbers.  This should cause no trouble, but we could split
1044  * the function into two functions with different argument types if it does.
1045  */
1046
1047 void
1048 parseOidArray(const char *str, Oid *array, int arraysize)
1049 {
1050         int                     j,
1051                                 argNum;
1052         char            temp[100];
1053         char            s;
1054
1055         argNum = 0;
1056         j = 0;
1057         for (;;)
1058         {
1059                 s = *str++;
1060                 if (s == ' ' || s == '\0')
1061                 {
1062                         if (j > 0)
1063                         {
1064                                 if (argNum >= arraysize)
1065                                 {
1066                                         pg_log_error("could not parse numeric array \"%s\": too many numbers", str);
1067                                         exit_nicely(1);
1068                                 }
1069                                 temp[j] = '\0';
1070                                 array[argNum++] = atooid(temp);
1071                                 j = 0;
1072                         }
1073                         if (s == '\0')
1074                                 break;
1075                 }
1076                 else
1077                 {
1078                         if (!(isdigit((unsigned char) s) || s == '-') ||
1079                                 j >= sizeof(temp) - 1)
1080                         {
1081                                 pg_log_error("could not parse numeric array \"%s\": invalid character in number", str);
1082                                 exit_nicely(1);
1083                         }
1084                         temp[j++] = s;
1085                 }
1086         }
1087
1088         while (argNum < arraysize)
1089                 array[argNum++] = InvalidOid;
1090 }
1091
1092
1093 /*
1094  * strInArray:
1095  *        takes in a string and a string array and the number of elements in the
1096  * string array.
1097  *        returns the index if the string is somewhere in the array, -1 otherwise
1098  */
1099
1100 static int
1101 strInArray(const char *pattern, char **arr, int arr_size)
1102 {
1103         int                     i;
1104
1105         for (i = 0; i < arr_size; i++)
1106         {
1107                 if (strcmp(pattern, arr[i]) == 0)
1108                         return i;
1109         }
1110         return -1;
1111 }