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