1 /*-------------------------------------------------------------------------
4 * Catalog routines used by pg_dump; long ago these were shared
5 * by another dump tool, but not anymore.
7 * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
8 * Portions Copyright (c) 1994, Regents of the University of California
12 * src/bin/pg_dump/common.c
14 *-------------------------------------------------------------------------
16 #include "postgres_fe.h"
20 #include "catalog/pg_class_d.h"
21 #include "fe_utils/string_utils.h"
22 #include "pg_backup_archiver.h"
23 #include "pg_backup_utils.h"
27 * Variables for mapping DumpId to DumpableObject
29 static DumpableObject **dumpIdMap = NULL;
30 static int allocedDumpIds = 0;
31 static DumpId lastDumpId = 0;
34 * Variables for mapping CatalogId to DumpableObject
36 static bool catalogIdMapValid = false;
37 static DumpableObject **catalogIdMap = NULL;
38 static int numCatalogIds = 0;
41 * These variables are static to avoid the notational cruft of having to pass
42 * them into findTableByOid() and friends. For each of these arrays, we build
43 * a sorted-by-OID index array immediately after the objects are fetched,
44 * and then we use binary search in findTableByOid() and friends. (qsort'ing
45 * the object arrays themselves would be simpler, but it doesn't work because
46 * pg_dump.c may have already established pointers between items.)
48 static DumpableObject **tblinfoindex;
49 static DumpableObject **typinfoindex;
50 static DumpableObject **funinfoindex;
51 static DumpableObject **oprinfoindex;
52 static DumpableObject **collinfoindex;
53 static DumpableObject **nspinfoindex;
54 static DumpableObject **extinfoindex;
58 static int numOperators;
59 static int numCollations;
60 static int numNamespaces;
61 static int numExtensions;
63 /* This is an array of object identities, not actual DumpableObjects */
64 static ExtensionMemberId *extmembers;
65 static int numextmembers;
67 static void flagInhTables(Archive *fout, TableInfo *tbinfo, int numTables,
68 InhInfo *inhinfo, int numInherits);
69 static void flagInhIndexes(Archive *fout, TableInfo *tblinfo, int numTables);
70 static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables);
71 static DumpableObject **buildIndexArray(void *objArray, int numObjs,
73 static int DOCatalogIdCompare(const void *p1, const void *p2);
74 static int ExtensionMemberIdCompare(const void *p1, const void *p2);
75 static void findParentsByOid(TableInfo *self,
76 InhInfo *inhinfo, int numInherits);
77 static int strInArray(const char *pattern, char **arr, int arr_size);
78 static IndxInfo *findIndexByOid(Oid oid, DumpableObject **idxinfoindex,
84 * Collect information about all potentially dumpable objects
87 getSchemaData(Archive *fout, int *numTablesPtr)
94 NamespaceInfo *nspinfo;
95 ExtensionInfo *extinfo;
103 int numAccessMethods;
111 int numForeignDataWrappers;
112 int numForeignServers;
114 int numEventTriggers;
117 * We must read extensions and extension membership info first, because
118 * extension membership needs to be consultable during decisions about
119 * whether other objects are to be dumped.
121 pg_log_info("reading extensions");
122 extinfo = getExtensions(fout, &numExtensions);
123 extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo));
125 pg_log_info("identifying extension members");
126 getExtensionMembership(fout, extinfo, numExtensions);
128 pg_log_info("reading schemas");
129 nspinfo = getNamespaces(fout, &numNamespaces);
130 nspinfoindex = buildIndexArray(nspinfo, numNamespaces, sizeof(NamespaceInfo));
133 * getTables should be done as soon as possible, so as to minimize the
134 * window between starting our transaction and acquiring per-table locks.
135 * However, we have to do getNamespaces first because the tables get
136 * linked to their containing namespaces during getTables.
138 pg_log_info("reading user-defined tables");
139 tblinfo = getTables(fout, &numTables);
140 tblinfoindex = buildIndexArray(tblinfo, numTables, sizeof(TableInfo));
142 /* Do this after we've built tblinfoindex */
143 getOwnedSeqs(fout, tblinfo, numTables);
145 pg_log_info("reading user-defined functions");
146 funinfo = getFuncs(fout, &numFuncs);
147 funinfoindex = buildIndexArray(funinfo, numFuncs, sizeof(FuncInfo));
149 /* this must be after getTables and getFuncs */
150 pg_log_info("reading user-defined types");
151 typinfo = getTypes(fout, &numTypes);
152 typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo));
154 /* this must be after getFuncs, too */
155 pg_log_info("reading procedural languages");
156 getProcLangs(fout, &numProcLangs);
158 pg_log_info("reading user-defined aggregate functions");
159 getAggregates(fout, &numAggregates);
161 pg_log_info("reading user-defined operators");
162 oprinfo = getOperators(fout, &numOperators);
163 oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo));
165 pg_log_info("reading user-defined access methods");
166 getAccessMethods(fout, &numAccessMethods);
168 pg_log_info("reading user-defined operator classes");
169 getOpclasses(fout, &numOpclasses);
171 pg_log_info("reading user-defined operator families");
172 getOpfamilies(fout, &numOpfamilies);
174 pg_log_info("reading user-defined text search parsers");
175 getTSParsers(fout, &numTSParsers);
177 pg_log_info("reading user-defined text search templates");
178 getTSTemplates(fout, &numTSTemplates);
180 pg_log_info("reading user-defined text search dictionaries");
181 getTSDictionaries(fout, &numTSDicts);
183 pg_log_info("reading user-defined text search configurations");
184 getTSConfigurations(fout, &numTSConfigs);
186 pg_log_info("reading user-defined foreign-data wrappers");
187 getForeignDataWrappers(fout, &numForeignDataWrappers);
189 pg_log_info("reading user-defined foreign servers");
190 getForeignServers(fout, &numForeignServers);
192 pg_log_info("reading default privileges");
193 getDefaultACLs(fout, &numDefaultACLs);
195 pg_log_info("reading user-defined collations");
196 collinfo = getCollations(fout, &numCollations);
197 collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo));
199 pg_log_info("reading user-defined conversions");
200 getConversions(fout, &numConversions);
202 pg_log_info("reading type casts");
203 getCasts(fout, &numCasts);
205 pg_log_info("reading transforms");
206 getTransforms(fout, &numTransforms);
208 pg_log_info("reading table inheritance information");
209 inhinfo = getInherits(fout, &numInherits);
211 pg_log_info("reading event triggers");
212 getEventTriggers(fout, &numEventTriggers);
214 /* Identify extension configuration tables that should be dumped */
215 pg_log_info("finding extension tables");
216 processExtensionTables(fout, extinfo, numExtensions);
218 /* Link tables to parents, mark parents of target tables interesting */
219 pg_log_info("finding inheritance relationships");
220 flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits);
222 pg_log_info("reading column info for interesting tables");
223 getTableAttrs(fout, tblinfo, numTables);
225 pg_log_info("flagging inherited columns in subtables");
226 flagInhAttrs(fout->dopt, tblinfo, numTables);
228 pg_log_info("reading indexes");
229 getIndexes(fout, tblinfo, numTables);
231 pg_log_info("flagging indexes in partitioned tables");
232 flagInhIndexes(fout, tblinfo, numTables);
234 pg_log_info("reading extended statistics");
235 getExtendedStatistics(fout);
237 pg_log_info("reading constraints");
238 getConstraints(fout, tblinfo, numTables);
240 pg_log_info("reading triggers");
241 getTriggers(fout, tblinfo, numTables);
243 pg_log_info("reading rewrite rules");
244 getRules(fout, &numRules);
246 pg_log_info("reading policies");
247 getPolicies(fout, tblinfo, numTables);
249 pg_log_info("reading publications");
250 getPublications(fout);
252 pg_log_info("reading publication membership");
253 getPublicationTables(fout, tblinfo, numTables);
255 pg_log_info("reading subscriptions");
256 getSubscriptions(fout);
258 *numTablesPtr = numTables;
263 * Fill in parent link fields of tables for which we need that information,
264 * and mark parents of target tables as interesting
266 * Note that only direct ancestors of targets are marked interesting.
267 * This is sufficient; we don't much care whether they inherited their
273 flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
274 InhInfo *inhinfo, int numInherits)
276 DumpOptions *dopt = fout->dopt;
280 for (i = 0; i < numTables; i++)
282 bool find_parents = true;
283 bool mark_parents = true;
285 /* Some kinds never have parents */
286 if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
287 tblinfo[i].relkind == RELKIND_VIEW ||
288 tblinfo[i].relkind == RELKIND_MATVIEW)
292 * Normally, we don't bother computing anything for non-target tables,
293 * but if load-via-partition-root is specified, we gather information
294 * on every partition in the system so that getRootTableInfo can trace
295 * from any given to leaf partition all the way up to the root. (We
296 * don't need to mark them as interesting for getTableAttrs, though.)
298 if (!tblinfo[i].dobj.dump)
300 mark_parents = false;
302 if (!dopt->load_via_partition_root ||
303 !tblinfo[i].ispartition)
304 find_parents = false;
307 /* If needed, find all the immediate parent tables. */
309 findParentsByOid(&tblinfo[i], inhinfo, numInherits);
312 * If needed, mark the parents as interesting for getTableAttrs and
317 int numParents = tblinfo[i].numParents;
318 TableInfo **parents = tblinfo[i].parents;
320 for (j = 0; j < numParents; j++)
321 parents[j]->interesting = true;
328 * Create IndexAttachInfo objects for partitioned indexes, and add
329 * appropriate dependency links.
332 flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
337 DumpableObject ***parentIndexArray;
339 parentIndexArray = (DumpableObject ***)
340 pg_malloc0(getMaxDumpId() * sizeof(DumpableObject **));
342 for (i = 0; i < numTables; i++)
344 TableInfo *parenttbl;
345 IndexAttachInfo *attachinfo;
347 if (!tblinfo[i].ispartition || tblinfo[i].numParents == 0)
350 Assert(tblinfo[i].numParents == 1);
351 parenttbl = tblinfo[i].parents[0];
354 * We need access to each parent table's index list, but there is no
355 * index to cover them outside of this function. To avoid having to
356 * sort every parent table's indexes each time we come across each of
357 * its partitions, create an indexed array for each parent the first
358 * time it is required.
360 if (parentIndexArray[parenttbl->dobj.dumpId] == NULL)
361 parentIndexArray[parenttbl->dobj.dumpId] =
362 buildIndexArray(parenttbl->indexes,
363 parenttbl->numIndexes,
366 attachinfo = (IndexAttachInfo *)
367 pg_malloc0(tblinfo[i].numIndexes * sizeof(IndexAttachInfo));
368 for (j = 0, k = 0; j < tblinfo[i].numIndexes; j++)
370 IndxInfo *index = &(tblinfo[i].indexes[j]);
373 if (index->parentidx == 0)
376 parentidx = findIndexByOid(index->parentidx,
377 parentIndexArray[parenttbl->dobj.dumpId],
378 parenttbl->numIndexes);
379 if (parentidx == NULL)
382 attachinfo[k].dobj.objType = DO_INDEX_ATTACH;
383 attachinfo[k].dobj.catId.tableoid = 0;
384 attachinfo[k].dobj.catId.oid = 0;
385 AssignDumpId(&attachinfo[k].dobj);
386 attachinfo[k].dobj.name = pg_strdup(index->dobj.name);
387 attachinfo[k].dobj.namespace = index->indextable->dobj.namespace;
388 attachinfo[k].parentIdx = parentidx;
389 attachinfo[k].partitionIdx = index;
392 * We must state the DO_INDEX_ATTACH object's dependencies
393 * explicitly, since it will not match anything in pg_depend.
395 * Give it dependencies on both the partition index and the parent
396 * index, so that it will not be executed till both of those
397 * exist. (There's no need to care what order those are created
400 * In addition, give it dependencies on the indexes' underlying
401 * tables. This does nothing of great value so far as serial
402 * restore ordering goes, but it ensures that a parallel restore
403 * will not try to run the ATTACH concurrently with other
404 * operations on those tables.
406 addObjectDependency(&attachinfo[k].dobj, index->dobj.dumpId);
407 addObjectDependency(&attachinfo[k].dobj, parentidx->dobj.dumpId);
408 addObjectDependency(&attachinfo[k].dobj,
409 index->indextable->dobj.dumpId);
410 addObjectDependency(&attachinfo[k].dobj,
411 parentidx->indextable->dobj.dumpId);
413 /* keep track of the list of partitions in the parent index */
414 simple_ptr_list_append(&parentidx->partattaches, &attachinfo[k].dobj);
420 for (i = 0; i < numTables; i++)
421 if (parentIndexArray[i])
422 pg_free(parentIndexArray[i]);
423 pg_free(parentIndexArray);
427 * for each dumpable table in tblinfo, flag its inherited attributes
429 * What we need to do here is detect child columns that inherit NOT NULL
430 * bits from their parents (so that we needn't specify that again for the
431 * child) and child columns that have DEFAULT NULL when their parents had
432 * some non-null default. In the latter case, we make up a dummy AttrDefInfo
433 * object so that we'll correctly emit the necessary DEFAULT NULL clause;
434 * otherwise the backend will apply an inherited default to the column.
439 flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables)
445 for (i = 0; i < numTables; i++)
447 TableInfo *tbinfo = &(tblinfo[i]);
451 /* Some kinds never have parents */
452 if (tbinfo->relkind == RELKIND_SEQUENCE ||
453 tbinfo->relkind == RELKIND_VIEW ||
454 tbinfo->relkind == RELKIND_MATVIEW)
457 /* Don't bother computing anything for non-target tables, either */
458 if (!tbinfo->dobj.dump)
461 numParents = tbinfo->numParents;
462 parents = tbinfo->parents;
465 continue; /* nothing to see here, move along */
467 /* For each column, search for matching column names in parent(s) */
468 for (j = 0; j < tbinfo->numatts; j++)
470 bool foundNotNull; /* Attr was NOT NULL in a parent */
471 bool foundDefault; /* Found a default in a parent */
473 /* no point in examining dropped columns */
474 if (tbinfo->attisdropped[j])
477 foundNotNull = false;
478 foundDefault = false;
479 for (k = 0; k < numParents; k++)
481 TableInfo *parent = parents[k];
484 inhAttrInd = strInArray(tbinfo->attnames[j],
489 foundNotNull |= parent->notnull[inhAttrInd];
490 foundDefault |= (parent->attrdefs[inhAttrInd] != NULL);
494 /* Remember if we found inherited NOT NULL */
495 tbinfo->inhNotNull[j] = foundNotNull;
497 /* Manufacture a DEFAULT NULL clause if necessary */
498 if (foundDefault && tbinfo->attrdefs[j] == NULL)
500 AttrDefInfo *attrDef;
502 attrDef = (AttrDefInfo *) pg_malloc(sizeof(AttrDefInfo));
503 attrDef->dobj.objType = DO_ATTRDEF;
504 attrDef->dobj.catId.tableoid = 0;
505 attrDef->dobj.catId.oid = 0;
506 AssignDumpId(&attrDef->dobj);
507 attrDef->dobj.name = pg_strdup(tbinfo->dobj.name);
508 attrDef->dobj.namespace = tbinfo->dobj.namespace;
509 attrDef->dobj.dump = tbinfo->dobj.dump;
511 attrDef->adtable = tbinfo;
512 attrDef->adnum = j + 1;
513 attrDef->adef_expr = pg_strdup("NULL");
515 /* Will column be dumped explicitly? */
516 if (shouldPrintColumn(dopt, tbinfo, j))
518 attrDef->separate = false;
519 /* No dependency needed: NULL cannot have dependencies */
523 /* column will be suppressed, print default separately */
524 attrDef->separate = true;
525 /* ensure it comes out after the table */
526 addObjectDependency(&attrDef->dobj,
527 tbinfo->dobj.dumpId);
530 tbinfo->attrdefs[j] = attrDef;
538 * Given a newly-created dumpable object, assign a dump ID,
539 * and enter the object into the lookup table.
541 * The caller is expected to have filled in objType and catId,
542 * but not any of the other standard fields of a DumpableObject.
545 AssignDumpId(DumpableObject *dobj)
547 dobj->dumpId = ++lastDumpId;
548 dobj->name = NULL; /* must be set later */
549 dobj->namespace = NULL; /* may be set later */
550 dobj->dump = DUMP_COMPONENT_ALL; /* default assumption */
551 dobj->ext_member = false; /* default assumption */
552 dobj->dependencies = NULL;
556 while (dobj->dumpId >= allocedDumpIds)
560 if (allocedDumpIds <= 0)
563 dumpIdMap = (DumpableObject **)
564 pg_malloc(newAlloc * sizeof(DumpableObject *));
568 newAlloc = allocedDumpIds * 2;
569 dumpIdMap = (DumpableObject **)
570 pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
572 memset(dumpIdMap + allocedDumpIds, 0,
573 (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
574 allocedDumpIds = newAlloc;
576 dumpIdMap[dobj->dumpId] = dobj;
578 /* mark catalogIdMap invalid, but don't rebuild it yet */
579 catalogIdMapValid = false;
583 * Assign a DumpId that's not tied to a DumpableObject.
585 * This is used when creating a "fixed" ArchiveEntry that doesn't need to
586 * participate in the sorting logic.
595 * Return the largest DumpId so far assigned
604 * Find a DumpableObject by dump ID
606 * Returns NULL for invalid ID
609 findObjectByDumpId(DumpId dumpId)
611 if (dumpId <= 0 || dumpId >= allocedDumpIds)
612 return NULL; /* out of range? */
613 return dumpIdMap[dumpId];
617 * Find a DumpableObject by catalog ID
619 * Returns NULL for unknown ID
621 * We use binary search in a sorted list that is built on first call.
622 * If AssignDumpId() and findObjectByCatalogId() calls were freely intermixed,
623 * the code would work, but possibly be very slow. In the current usage
624 * pattern that does not happen, indeed we build the list at most twice.
627 findObjectByCatalogId(CatalogId catalogId)
629 DumpableObject **low;
630 DumpableObject **high;
632 if (!catalogIdMapValid)
636 getDumpableObjects(&catalogIdMap, &numCatalogIds);
637 if (numCatalogIds > 1)
638 qsort((void *) catalogIdMap, numCatalogIds,
639 sizeof(DumpableObject *), DOCatalogIdCompare);
640 catalogIdMapValid = true;
644 * We could use bsearch() here, but the notational cruft of calling
645 * bsearch is nearly as bad as doing it ourselves; and the generalized
646 * bsearch function is noticeably slower as well.
648 if (numCatalogIds <= 0)
651 high = catalogIdMap + (numCatalogIds - 1);
654 DumpableObject **middle;
657 middle = low + (high - low) / 2;
658 /* comparison must match DOCatalogIdCompare, below */
659 difference = oidcmp((*middle)->catId.oid, catalogId.oid);
661 difference = oidcmp((*middle)->catId.tableoid, catalogId.tableoid);
664 else if (difference < 0)
673 * Find a DumpableObject by OID, in a pre-sorted array of one type of object
675 * Returns NULL for unknown OID
677 static DumpableObject *
678 findObjectByOid(Oid oid, DumpableObject **indexArray, int numObjs)
680 DumpableObject **low;
681 DumpableObject **high;
684 * This is the same as findObjectByCatalogId except we assume we need not
685 * look at table OID because the objects are all the same type.
687 * We could use bsearch() here, but the notational cruft of calling
688 * bsearch is nearly as bad as doing it ourselves; and the generalized
689 * bsearch function is noticeably slower as well.
694 high = indexArray + (numObjs - 1);
697 DumpableObject **middle;
700 middle = low + (high - low) / 2;
701 difference = oidcmp((*middle)->catId.oid, oid);
704 else if (difference < 0)
713 * Build an index array of DumpableObject pointers, sorted by OID
715 static DumpableObject **
716 buildIndexArray(void *objArray, int numObjs, Size objSize)
718 DumpableObject **ptrs;
721 ptrs = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *));
722 for (i = 0; i < numObjs; i++)
723 ptrs[i] = (DumpableObject *) ((char *) objArray + i * objSize);
725 /* We can use DOCatalogIdCompare to sort since its first key is OID */
727 qsort((void *) ptrs, numObjs, sizeof(DumpableObject *),
734 * qsort comparator for pointers to DumpableObjects
737 DOCatalogIdCompare(const void *p1, const void *p2)
739 const DumpableObject *obj1 = *(DumpableObject *const *) p1;
740 const DumpableObject *obj2 = *(DumpableObject *const *) p2;
744 * Compare OID first since it's usually unique, whereas there will only be
745 * a few distinct values of tableoid.
747 cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
749 cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
754 * Build an array of pointers to all known dumpable objects
756 * This simply creates a modifiable copy of the internal map.
759 getDumpableObjects(DumpableObject ***objs, int *numObjs)
764 *objs = (DumpableObject **)
765 pg_malloc(allocedDumpIds * sizeof(DumpableObject *));
767 for (i = 1; i < allocedDumpIds; i++)
770 (*objs)[j++] = dumpIdMap[i];
776 * Add a dependency link to a DumpableObject
778 * Note: duplicate dependencies are currently not eliminated
781 addObjectDependency(DumpableObject *dobj, DumpId refId)
783 if (dobj->nDeps >= dobj->allocDeps)
785 if (dobj->allocDeps <= 0)
787 dobj->allocDeps = 16;
788 dobj->dependencies = (DumpId *)
789 pg_malloc(dobj->allocDeps * sizeof(DumpId));
793 dobj->allocDeps *= 2;
794 dobj->dependencies = (DumpId *)
795 pg_realloc(dobj->dependencies,
796 dobj->allocDeps * sizeof(DumpId));
799 dobj->dependencies[dobj->nDeps++] = refId;
803 * Remove a dependency link from a DumpableObject
805 * If there are multiple links, all are removed
808 removeObjectDependency(DumpableObject *dobj, DumpId refId)
813 for (i = 0; i < dobj->nDeps; i++)
815 if (dobj->dependencies[i] != refId)
816 dobj->dependencies[j++] = dobj->dependencies[i];
824 * finds the entry (in tblinfo) of the table with the given oid
825 * returns NULL if not found
828 findTableByOid(Oid oid)
830 return (TableInfo *) findObjectByOid(oid, tblinfoindex, numTables);
835 * finds the entry (in typinfo) of the type with the given oid
836 * returns NULL if not found
839 findTypeByOid(Oid oid)
841 return (TypeInfo *) findObjectByOid(oid, typinfoindex, numTypes);
846 * finds the entry (in funinfo) of the function with the given oid
847 * returns NULL if not found
850 findFuncByOid(Oid oid)
852 return (FuncInfo *) findObjectByOid(oid, funinfoindex, numFuncs);
857 * finds the entry (in oprinfo) of the operator with the given oid
858 * returns NULL if not found
861 findOprByOid(Oid oid)
863 return (OprInfo *) findObjectByOid(oid, oprinfoindex, numOperators);
868 * finds the entry (in collinfo) of the collation with the given oid
869 * returns NULL if not found
872 findCollationByOid(Oid oid)
874 return (CollInfo *) findObjectByOid(oid, collinfoindex, numCollations);
879 * finds the entry (in nspinfo) of the namespace with the given oid
880 * returns NULL if not found
883 findNamespaceByOid(Oid oid)
885 return (NamespaceInfo *) findObjectByOid(oid, nspinfoindex, numNamespaces);
890 * finds the entry (in extinfo) of the extension with the given oid
891 * returns NULL if not found
894 findExtensionByOid(Oid oid)
896 return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions);
901 * find the entry of the index with the given oid
903 * This one's signature is different from the previous ones because we lack a
904 * global array of all indexes, so caller must pass their array as argument.
907 findIndexByOid(Oid oid, DumpableObject **idxinfoindex, int numIndexes)
909 return (IndxInfo *) findObjectByOid(oid, idxinfoindex, numIndexes);
913 * setExtensionMembership
914 * accept and save data about which objects belong to extensions
917 setExtensionMembership(ExtensionMemberId *extmems, int nextmems)
919 /* Sort array in preparation for binary searches */
921 qsort((void *) extmems, nextmems, sizeof(ExtensionMemberId),
922 ExtensionMemberIdCompare);
924 extmembers = extmems;
925 numextmembers = nextmems;
929 * findOwningExtension
930 * return owning extension for specified catalog ID, or NULL if none
933 findOwningExtension(CatalogId catalogId)
935 ExtensionMemberId *low;
936 ExtensionMemberId *high;
939 * We could use bsearch() here, but the notational cruft of calling
940 * bsearch is nearly as bad as doing it ourselves; and the generalized
941 * bsearch function is noticeably slower as well.
943 if (numextmembers <= 0)
946 high = extmembers + (numextmembers - 1);
949 ExtensionMemberId *middle;
952 middle = low + (high - low) / 2;
953 /* comparison must match ExtensionMemberIdCompare, below */
954 difference = oidcmp(middle->catId.oid, catalogId.oid);
956 difference = oidcmp(middle->catId.tableoid, catalogId.tableoid);
959 else if (difference < 0)
968 * qsort comparator for ExtensionMemberIds
971 ExtensionMemberIdCompare(const void *p1, const void *p2)
973 const ExtensionMemberId *obj1 = (const ExtensionMemberId *) p1;
974 const ExtensionMemberId *obj2 = (const ExtensionMemberId *) p2;
978 * Compare OID first since it's usually unique, whereas there will only be
979 * a few distinct values of tableoid.
981 cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
983 cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
990 * find a table's parents in tblinfo[]
993 findParentsByOid(TableInfo *self,
994 InhInfo *inhinfo, int numInherits)
996 Oid oid = self->dobj.catId.oid;
1002 for (i = 0; i < numInherits; i++)
1004 if (inhinfo[i].inhrelid == oid)
1008 self->numParents = numParents;
1012 self->parents = (TableInfo **)
1013 pg_malloc(sizeof(TableInfo *) * numParents);
1015 for (i = 0; i < numInherits; i++)
1017 if (inhinfo[i].inhrelid == oid)
1021 parent = findTableByOid(inhinfo[i].inhparent);
1024 pg_log_error("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
1025 inhinfo[i].inhparent,
1030 self->parents[j++] = parent;
1035 self->parents = NULL;
1040 * parse a string of numbers delimited by spaces into a character array
1042 * Note: actually this is used for both Oids and potentially-signed
1043 * attribute numbers. This should cause no trouble, but we could split
1044 * the function into two functions with different argument types if it does.
1048 parseOidArray(const char *str, Oid *array, int arraysize)
1060 if (s == ' ' || s == '\0')
1064 if (argNum >= arraysize)
1066 pg_log_error("could not parse numeric array \"%s\": too many numbers", str);
1070 array[argNum++] = atooid(temp);
1078 if (!(isdigit((unsigned char) s) || s == '-') ||
1079 j >= sizeof(temp) - 1)
1081 pg_log_error("could not parse numeric array \"%s\": invalid character in number", str);
1088 while (argNum < arraysize)
1089 array[argNum++] = InvalidOid;
1095 * takes in a string and a string array and the number of elements in the
1097 * returns the index if the string is somewhere in the array, -1 otherwise
1101 strInArray(const char *pattern, char **arr, int arr_size)
1105 for (i = 0; i < arr_size; i++)
1107 if (strcmp(pattern, arr[i]) == 0)