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