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