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