]> granicus.if.org Git - postgresql/blob - src/bin/pg_dump/common.c
Add strerror to pg_dump error messages where missing.
[postgresql] / src / bin / pg_dump / common.c
1 /*-------------------------------------------------------------------------
2  *
3  * common.c
4  *        common routines between pg_dump and pg4_dump
5  *
6  * Since pg4_dump is long-dead code, there is no longer any useful distinction
7  * between this file and pg_dump.c.
8  *
9  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
10  * Portions Copyright (c) 1994, Regents of the University of California
11  *
12  *
13  * IDENTIFICATION
14  *        $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.90 2006/05/22 11:21:54 petere Exp $
15  *
16  *-------------------------------------------------------------------------
17  */
18
19 #include "postgres_fe.h"
20 #include "pg_dump.h"
21 #include "pg_backup_archiver.h"
22
23 #include "postgres.h"
24 #include "catalog/pg_class.h"
25
26 #include <ctype.h>
27
28 #include "libpq-fe.h"
29 #ifndef HAVE_STRDUP
30 #include "strdup.h"
31 #endif
32
33
34 /*
35  * Variables for mapping DumpId to DumpableObject
36  */
37 static DumpableObject **dumpIdMap = NULL;
38 static int      allocedDumpIds = 0;
39 static DumpId lastDumpId = 0;
40
41 /*
42  * Variables for mapping CatalogId to DumpableObject
43  */
44 static bool catalogIdMapValid = false;
45 static DumpableObject **catalogIdMap = NULL;
46 static int      numCatalogIds = 0;
47
48 /*
49  * These variables are static to avoid the notational cruft of having to pass
50  * them into findTableByOid() and friends.
51  */
52 static TableInfo *tblinfo;
53 static TypeInfo *typinfo;
54 static FuncInfo *funinfo;
55 static OprInfo *oprinfo;
56 static int      numTables;
57 static int      numTypes;
58 static int      numFuncs;
59 static int      numOperators;
60
61
62 static void flagInhTables(TableInfo *tbinfo, int numTables,
63                           InhInfo *inhinfo, int numInherits);
64 static void flagInhAttrs(TableInfo *tbinfo, int numTables,
65                          InhInfo *inhinfo, int numInherits);
66 static int      DOCatalogIdCompare(const void *p1, const void *p2);
67 static void findParentsByOid(TableInfo *self,
68                                  InhInfo *inhinfo, int numInherits);
69 static int      strInArray(const char *pattern, char **arr, int arr_size);
70
71
72 /*
73  * getSchemaData
74  *        Collect information about all potentially dumpable objects
75  */
76 TableInfo *
77 getSchemaData(int *numTablesPtr,
78                           const bool schemaOnly,
79                           const bool dataOnly)
80 {
81         NamespaceInfo *nsinfo;
82         AggInfo    *agginfo;
83         InhInfo    *inhinfo;
84         RuleInfo   *ruleinfo;
85         ProcLangInfo *proclanginfo;
86         CastInfo   *castinfo;
87         OpclassInfo *opcinfo;
88         ConvInfo   *convinfo;
89         int                     numNamespaces;
90         int                     numAggregates;
91         int                     numInherits;
92         int                     numRules;
93         int                     numProcLangs;
94         int                     numCasts;
95         int                     numOpclasses;
96         int                     numConversions;
97
98         if (g_verbose)
99                 write_msg(NULL, "reading schemas\n");
100         nsinfo = getNamespaces(&numNamespaces);
101
102         if (g_verbose)
103                 write_msg(NULL, "reading user-defined functions\n");
104         funinfo = getFuncs(&numFuncs);
105
106         /* this must be after getFuncs */
107         if (g_verbose)
108                 write_msg(NULL, "reading user-defined types\n");
109         typinfo = getTypes(&numTypes);
110
111         /* this must be after getFuncs, too */
112         if (g_verbose)
113                 write_msg(NULL, "reading procedural languages\n");
114         proclanginfo = getProcLangs(&numProcLangs);
115
116         if (g_verbose)
117                 write_msg(NULL, "reading user-defined aggregate functions\n");
118         agginfo = getAggregates(&numAggregates);
119
120         if (g_verbose)
121                 write_msg(NULL, "reading user-defined operators\n");
122         oprinfo = getOperators(&numOperators);
123
124         if (g_verbose)
125                 write_msg(NULL, "reading user-defined operator classes\n");
126         opcinfo = getOpclasses(&numOpclasses);
127
128         if (g_verbose)
129                 write_msg(NULL, "reading user-defined conversions\n");
130         convinfo = getConversions(&numConversions);
131
132         if (g_verbose)
133                 write_msg(NULL, "reading user-defined tables\n");
134         tblinfo = getTables(&numTables);
135
136         if (g_verbose)
137                 write_msg(NULL, "reading table inheritance information\n");
138         inhinfo = getInherits(&numInherits);
139
140         if (g_verbose)
141                 write_msg(NULL, "reading rewrite rules\n");
142         ruleinfo = getRules(&numRules);
143
144         if (g_verbose)
145                 write_msg(NULL, "reading type casts\n");
146         castinfo = getCasts(&numCasts);
147
148         /* Link tables to parents, mark parents of target tables interesting */
149         if (g_verbose)
150                 write_msg(NULL, "finding inheritance relationships\n");
151         flagInhTables(tblinfo, numTables, inhinfo, numInherits);
152
153         if (g_verbose)
154                 write_msg(NULL, "reading column info for interesting tables\n");
155         getTableAttrs(tblinfo, numTables);
156
157         if (g_verbose)
158                 write_msg(NULL, "flagging inherited columns in subtables\n");
159         flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
160
161         if (g_verbose)
162                 write_msg(NULL, "reading indexes\n");
163         getIndexes(tblinfo, numTables);
164
165         if (g_verbose)
166                 write_msg(NULL, "reading constraints\n");
167         getConstraints(tblinfo, numTables);
168
169         if (g_verbose)
170                 write_msg(NULL, "reading triggers\n");
171         getTriggers(tblinfo, numTables);
172
173         *numTablesPtr = numTables;
174         return tblinfo;
175 }
176
177 /* flagInhTables -
178  *       Fill in parent link fields of every target table, and mark
179  *       parents of target tables as interesting
180  *
181  * Note that only direct ancestors of targets are marked interesting.
182  * This is sufficient; we don't much care whether they inherited their
183  * attributes or not.
184  *
185  * modifies tblinfo
186  */
187 static void
188 flagInhTables(TableInfo *tblinfo, int numTables,
189                           InhInfo *inhinfo, int numInherits)
190 {
191         int                     i,
192                                 j;
193         int                     numParents;
194         TableInfo **parents;
195
196         for (i = 0; i < numTables; i++)
197         {
198                 /* Sequences and views never have parents */
199                 if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
200                         tblinfo[i].relkind == RELKIND_VIEW)
201                         continue;
202
203                 /* Don't bother computing anything for non-target tables, either */
204                 if (!tblinfo[i].dobj.dump)
205                         continue;
206
207                 /* Find all the immediate parent tables */
208                 findParentsByOid(&tblinfo[i], inhinfo, numInherits);
209
210                 /* Mark the parents as interesting for getTableAttrs */
211                 numParents = tblinfo[i].numParents;
212                 parents = tblinfo[i].parents;
213                 for (j = 0; j < numParents; j++)
214                         parents[j]->interesting = true;
215         }
216 }
217
218 /* flagInhAttrs -
219  *       for each dumpable table in tblinfo, flag its inherited attributes
220  * so when we dump the table out, we don't dump out the inherited attributes
221  *
222  * modifies tblinfo
223  */
224 static void
225 flagInhAttrs(TableInfo *tblinfo, int numTables,
226                          InhInfo *inhinfo, int numInherits)
227 {
228         int                     i,
229                                 j,
230                                 k;
231
232         for (i = 0; i < numTables; i++)
233         {
234                 TableInfo  *tbinfo = &(tblinfo[i]);
235                 int                     numParents;
236                 TableInfo **parents;
237                 TableInfo  *parent;
238
239                 /* Sequences and views never have parents */
240                 if (tbinfo->relkind == RELKIND_SEQUENCE ||
241                         tbinfo->relkind == RELKIND_VIEW)
242                         continue;
243
244                 /* Don't bother computing anything for non-target tables, either */
245                 if (!tbinfo->dobj.dump)
246                         continue;
247
248                 numParents = tbinfo->numParents;
249                 parents = tbinfo->parents;
250
251                 if (numParents == 0)
252                         continue;                       /* nothing to see here, move along */
253
254                 /*----------------------------------------------------------------
255                  * For each attr, check the parent info: if no parent has an attr
256                  * with the same name, then it's not inherited. If there *is* an
257                  * attr with the same name, then only dump it if:
258                  *
259                  * - it is NOT NULL and zero parents are NOT NULL
260                  *       OR
261                  * - it has a default value AND the default value does not match
262                  *       all parent default values, or no parents specify a default.
263                  *
264                  * See discussion on -hackers around 2-Apr-2001.
265                  *----------------------------------------------------------------
266                  */
267                 for (j = 0; j < tbinfo->numatts; j++)
268                 {
269                         bool            foundAttr;              /* Attr was found in a parent */
270                         bool            foundNotNull;   /* Attr was NOT NULL in a parent */
271                         bool            defaultsMatch;  /* All non-empty defaults match */
272                         bool            defaultsFound;  /* Found a default in a parent */
273                         AttrDefInfo *attrDef;
274
275                         foundAttr = false;
276                         foundNotNull = false;
277                         defaultsMatch = true;
278                         defaultsFound = false;
279
280                         attrDef = tbinfo->attrdefs[j];
281
282                         for (k = 0; k < numParents; k++)
283                         {
284                                 int                     inhAttrInd;
285
286                                 parent = parents[k];
287                                 inhAttrInd = strInArray(tbinfo->attnames[j],
288                                                                                 parent->attnames,
289                                                                                 parent->numatts);
290
291                                 if (inhAttrInd != -1)
292                                 {
293                                         foundAttr = true;
294                                         foundNotNull |= parent->notnull[inhAttrInd];
295                                         if (attrDef != NULL)            /* If we have a default, check
296                                                                                                  * parent */
297                                         {
298                                                 AttrDefInfo *inhDef;
299
300                                                 inhDef = parent->attrdefs[inhAttrInd];
301                                                 if (inhDef != NULL)
302                                                 {
303                                                         defaultsFound = true;
304                                                         defaultsMatch &= (strcmp(attrDef->adef_expr,
305                                                                                                          inhDef->adef_expr) == 0);
306                                                 }
307                                         }
308                                 }
309                         }
310
311                         /*
312                          * Based on the scan of the parents, decide if we can rely on the
313                          * inherited attr
314                          */
315                         if (foundAttr)          /* Attr was inherited */
316                         {
317                                 /* Set inherited flag by default */
318                                 tbinfo->inhAttrs[j] = true;
319                                 tbinfo->inhAttrDef[j] = true;
320                                 tbinfo->inhNotNull[j] = true;
321
322                                 /*
323                                  * Clear it if attr had a default, but parents did not, or
324                                  * mismatch
325                                  */
326                                 if ((attrDef != NULL) && (!defaultsFound || !defaultsMatch))
327                                 {
328                                         tbinfo->inhAttrs[j] = false;
329                                         tbinfo->inhAttrDef[j] = false;
330                                 }
331
332                                 /*
333                                  * Clear it if NOT NULL and none of the parents were NOT NULL
334                                  */
335                                 if (tbinfo->notnull[j] && !foundNotNull)
336                                 {
337                                         tbinfo->inhAttrs[j] = false;
338                                         tbinfo->inhNotNull[j] = false;
339                                 }
340
341                                 /* Clear it if attr has local definition */
342                                 if (tbinfo->attislocal[j])
343                                         tbinfo->inhAttrs[j] = false;
344                         }
345                 }
346
347                 /*
348                  * Check for inherited CHECK constraints.  We assume a constraint is
349                  * inherited if its name matches the name of any constraint in the
350                  * parent.      Originally this code tried to compare the expression
351                  * texts, but that can fail if the parent and child tables are in
352                  * different schemas, because reverse-listing of function calls may
353                  * produce different text (schema-qualified or not) depending on
354                  * search path.  We really need a more bulletproof way of detecting
355                  * inherited constraints --- pg_constraint should record this
356                  * explicitly!
357                  */
358                 for (j = 0; j < tbinfo->ncheck; j++)
359                 {
360                         ConstraintInfo *constr;
361
362                         constr = &(tbinfo->checkexprs[j]);
363
364                         for (k = 0; k < numParents; k++)
365                         {
366                                 int                     l;
367
368                                 parent = parents[k];
369                                 for (l = 0; l < parent->ncheck; l++)
370                                 {
371                                         ConstraintInfo *pconstr = &(parent->checkexprs[l]);
372
373                                         if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
374                                         {
375                                                 constr->coninherited = true;
376                                                 break;
377                                         }
378                                 }
379                                 if (constr->coninherited)
380                                         break;
381                         }
382                 }
383         }
384 }
385
386 /*
387  * AssignDumpId
388  *              Given a newly-created dumpable object, assign a dump ID,
389  *              and enter the object into the lookup table.
390  *
391  * The caller is expected to have filled in objType and catId,
392  * but not any of the other standard fields of a DumpableObject.
393  */
394 void
395 AssignDumpId(DumpableObject *dobj)
396 {
397         dobj->dumpId = ++lastDumpId;
398         dobj->name = NULL;                      /* must be set later */
399         dobj->namespace = NULL;         /* may be set later */
400         dobj->dump = true;                      /* default assumption */
401         dobj->dependencies = NULL;
402         dobj->nDeps = 0;
403         dobj->allocDeps = 0;
404
405         while (dobj->dumpId >= allocedDumpIds)
406         {
407                 int                     newAlloc;
408
409                 if (allocedDumpIds <= 0)
410                 {
411                         newAlloc = 256;
412                         dumpIdMap = (DumpableObject **)
413                                 malloc(newAlloc * sizeof(DumpableObject *));
414                 }
415                 else
416                 {
417                         newAlloc = allocedDumpIds * 2;
418                         dumpIdMap = (DumpableObject **)
419                                 realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
420                 }
421                 if (dumpIdMap == NULL)
422                         exit_horribly(NULL, NULL, "out of memory\n");
423                 memset(dumpIdMap + allocedDumpIds, 0,
424                            (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
425                 allocedDumpIds = newAlloc;
426         }
427         dumpIdMap[dobj->dumpId] = dobj;
428
429         /* mark catalogIdMap invalid, but don't rebuild it yet */
430         catalogIdMapValid = false;
431 }
432
433 /*
434  * Assign a DumpId that's not tied to a DumpableObject.
435  *
436  * This is used when creating a "fixed" ArchiveEntry that doesn't need to
437  * participate in the sorting logic.
438  */
439 DumpId
440 createDumpId(void)
441 {
442         return ++lastDumpId;
443 }
444
445 /*
446  * Return the largest DumpId so far assigned
447  */
448 DumpId
449 getMaxDumpId(void)
450 {
451         return lastDumpId;
452 }
453
454 /*
455  * Find a DumpableObject by dump ID
456  *
457  * Returns NULL for invalid ID
458  */
459 DumpableObject *
460 findObjectByDumpId(DumpId dumpId)
461 {
462         if (dumpId <= 0 || dumpId >= allocedDumpIds)
463                 return NULL;                    /* out of range? */
464         return dumpIdMap[dumpId];
465 }
466
467 /*
468  * Find a DumpableObject by catalog ID
469  *
470  * Returns NULL for unknown ID
471  *
472  * We use binary search in a sorted list that is built on first call.
473  * If AssignDumpId() and findObjectByCatalogId() calls were intermixed,
474  * the code would work, but possibly be very slow.      In the current usage
475  * pattern that does not happen, indeed we only need to build the list once.
476  */
477 DumpableObject *
478 findObjectByCatalogId(CatalogId catalogId)
479 {
480         DumpableObject **low;
481         DumpableObject **high;
482
483         if (!catalogIdMapValid)
484         {
485                 if (catalogIdMap)
486                         free(catalogIdMap);
487                 getDumpableObjects(&catalogIdMap, &numCatalogIds);
488                 if (numCatalogIds > 1)
489                         qsort((void *) catalogIdMap, numCatalogIds,
490                                   sizeof(DumpableObject *), DOCatalogIdCompare);
491                 catalogIdMapValid = true;
492         }
493
494         /*
495          * We could use bsearch() here, but the notational cruft of calling
496          * bsearch is nearly as bad as doing it ourselves; and the generalized
497          * bsearch function is noticeably slower as well.
498          */
499         if (numCatalogIds <= 0)
500                 return NULL;
501         low = catalogIdMap;
502         high = catalogIdMap + (numCatalogIds - 1);
503         while (low <= high)
504         {
505                 DumpableObject **middle;
506                 int                     difference;
507
508                 middle = low + (high - low) / 2;
509                 /* comparison must match DOCatalogIdCompare, below */
510                 difference = oidcmp((*middle)->catId.oid, catalogId.oid);
511                 if (difference == 0)
512                         difference = oidcmp((*middle)->catId.tableoid, catalogId.tableoid);
513                 if (difference == 0)
514                         return *middle;
515                 else if (difference < 0)
516                         low = middle + 1;
517                 else
518                         high = middle - 1;
519         }
520         return NULL;
521 }
522
523 static int
524 DOCatalogIdCompare(const void *p1, const void *p2)
525 {
526         DumpableObject *obj1 = *(DumpableObject **) p1;
527         DumpableObject *obj2 = *(DumpableObject **) p2;
528         int                     cmpval;
529
530         /*
531          * Compare OID first since it's usually unique, whereas there will only be
532          * a few distinct values of tableoid.
533          */
534         cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
535         if (cmpval == 0)
536                 cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
537         return cmpval;
538 }
539
540 /*
541  * Build an array of pointers to all known dumpable objects
542  *
543  * This simply creates a modifiable copy of the internal map.
544  */
545 void
546 getDumpableObjects(DumpableObject ***objs, int *numObjs)
547 {
548         int                     i,
549                                 j;
550
551         *objs = (DumpableObject **)
552                 malloc(allocedDumpIds * sizeof(DumpableObject *));
553         if (*objs == NULL)
554                 exit_horribly(NULL, NULL, "out of memory\n");
555         j = 0;
556         for (i = 1; i < allocedDumpIds; i++)
557         {
558                 if (dumpIdMap[i])
559                         (*objs)[j++] = dumpIdMap[i];
560         }
561         *numObjs = j;
562 }
563
564 /*
565  * Add a dependency link to a DumpableObject
566  *
567  * Note: duplicate dependencies are currently not eliminated
568  */
569 void
570 addObjectDependency(DumpableObject *dobj, DumpId refId)
571 {
572         if (dobj->nDeps >= dobj->allocDeps)
573         {
574                 if (dobj->allocDeps <= 0)
575                 {
576                         dobj->allocDeps = 16;
577                         dobj->dependencies = (DumpId *)
578                                 malloc(dobj->allocDeps * sizeof(DumpId));
579                 }
580                 else
581                 {
582                         dobj->allocDeps *= 2;
583                         dobj->dependencies = (DumpId *)
584                                 realloc(dobj->dependencies,
585                                                 dobj->allocDeps * sizeof(DumpId));
586                 }
587                 if (dobj->dependencies == NULL)
588                         exit_horribly(NULL, NULL, "out of memory\n");
589         }
590         dobj->dependencies[dobj->nDeps++] = refId;
591 }
592
593 /*
594  * Remove a dependency link from a DumpableObject
595  *
596  * If there are multiple links, all are removed
597  */
598 void
599 removeObjectDependency(DumpableObject *dobj, DumpId refId)
600 {
601         int                     i;
602         int                     j = 0;
603
604         for (i = 0; i < dobj->nDeps; i++)
605         {
606                 if (dobj->dependencies[i] != refId)
607                         dobj->dependencies[j++] = dobj->dependencies[i];
608         }
609         dobj->nDeps = j;
610 }
611
612
613 /*
614  * findTableByOid
615  *        finds the entry (in tblinfo) of the table with the given oid
616  *        returns NULL if not found
617  *
618  * NOTE:  should hash this, but just do linear search for now
619  */
620 TableInfo *
621 findTableByOid(Oid oid)
622 {
623         int                     i;
624
625         for (i = 0; i < numTables; i++)
626         {
627                 if (tblinfo[i].dobj.catId.oid == oid)
628                         return &tblinfo[i];
629         }
630         return NULL;
631 }
632
633 /*
634  * findTypeByOid
635  *        finds the entry (in typinfo) of the type with the given oid
636  *        returns NULL if not found
637  *
638  * NOTE:  should hash this, but just do linear search for now
639  */
640 TypeInfo *
641 findTypeByOid(Oid oid)
642 {
643         int                     i;
644
645         for (i = 0; i < numTypes; i++)
646         {
647                 if (typinfo[i].dobj.catId.oid == oid)
648                         return &typinfo[i];
649         }
650         return NULL;
651 }
652
653 /*
654  * findFuncByOid
655  *        finds the entry (in funinfo) of the function with the given oid
656  *        returns NULL if not found
657  *
658  * NOTE:  should hash this, but just do linear search for now
659  */
660 FuncInfo *
661 findFuncByOid(Oid oid)
662 {
663         int                     i;
664
665         for (i = 0; i < numFuncs; i++)
666         {
667                 if (funinfo[i].dobj.catId.oid == oid)
668                         return &funinfo[i];
669         }
670         return NULL;
671 }
672
673 /*
674  * findOprByOid
675  *        finds the entry (in oprinfo) of the operator with the given oid
676  *        returns NULL if not found
677  *
678  * NOTE:  should hash this, but just do linear search for now
679  */
680 OprInfo *
681 findOprByOid(Oid oid)
682 {
683         int                     i;
684
685         for (i = 0; i < numOperators; i++)
686         {
687                 if (oprinfo[i].dobj.catId.oid == oid)
688                         return &oprinfo[i];
689         }
690         return NULL;
691 }
692
693
694 /*
695  * findParentsByOid
696  *        find a table's parents in tblinfo[]
697  */
698 static void
699 findParentsByOid(TableInfo *self,
700                                  InhInfo *inhinfo, int numInherits)
701 {
702         Oid                     oid = self->dobj.catId.oid;
703         int                     i,
704                                 j;
705         int                     numParents;
706
707         numParents = 0;
708         for (i = 0; i < numInherits; i++)
709         {
710                 if (inhinfo[i].inhrelid == oid)
711                         numParents++;
712         }
713
714         self->numParents = numParents;
715
716         if (numParents > 0)
717         {
718                 self->parents = (TableInfo **) malloc(sizeof(TableInfo *) * numParents);
719                 j = 0;
720                 for (i = 0; i < numInherits; i++)
721                 {
722                         if (inhinfo[i].inhrelid == oid)
723                         {
724                                 TableInfo  *parent;
725
726                                 parent = findTableByOid(inhinfo[i].inhparent);
727                                 if (parent == NULL)
728                                 {
729                                         write_msg(NULL, "failed sanity check, parent OID %u of table \"%s\" (OID %u) not found\n",
730                                                           inhinfo[i].inhparent,
731                                                           self->dobj.name,
732                                                           oid);
733                                         exit_nicely();
734                                 }
735                                 self->parents[j++] = parent;
736                         }
737                 }
738         }
739         else
740                 self->parents = NULL;
741 }
742
743 /*
744  * parseOidArray
745  *        parse a string of numbers delimited by spaces into a character array
746  *
747  * Note: actually this is used for both Oids and potentially-signed
748  * attribute numbers.  This should cause no trouble, but we could split
749  * the function into two functions with different argument types if it does.
750  */
751
752 void
753 parseOidArray(const char *str, Oid *array, int arraysize)
754 {
755         int                     j,
756                                 argNum;
757         char            temp[100];
758         char            s;
759
760         argNum = 0;
761         j = 0;
762         for (;;)
763         {
764                 s = *str++;
765                 if (s == ' ' || s == '\0')
766                 {
767                         if (j > 0)
768                         {
769                                 if (argNum >= arraysize)
770                                 {
771                                         write_msg(NULL, "could not parse numeric array \"%s\": too many numbers\n", str);
772                                         exit_nicely();
773                                 }
774                                 temp[j] = '\0';
775                                 array[argNum++] = atooid(temp);
776                                 j = 0;
777                         }
778                         if (s == '\0')
779                                 break;
780                 }
781                 else
782                 {
783                         if (!(isdigit((unsigned char) s) || s == '-') ||
784                                 j >= sizeof(temp) - 1)
785                         {
786                                 write_msg(NULL, "could not parse numeric array \"%s\": invalid character in number\n", str);
787                                 exit_nicely();
788                         }
789                         temp[j++] = s;
790                 }
791         }
792
793         while (argNum < arraysize)
794                 array[argNum++] = InvalidOid;
795 }
796
797
798 /*
799  * strInArray:
800  *        takes in a string and a string array and the number of elements in the
801  * string array.
802  *        returns the index if the string is somewhere in the array, -1 otherwise
803  */
804
805 static int
806 strInArray(const char *pattern, char **arr, int arr_size)
807 {
808         int                     i;
809
810         for (i = 0; i < arr_size; i++)
811         {
812                 if (strcmp(pattern, arr[i]) == 0)
813                         return i;
814         }
815         return -1;
816 }