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