1 /*-------------------------------------------------------------------------
4 * common routines between pg_dump and pg4_dump
6 * Since pg4_dump is long-dead code, there is no longer any useful distinction
7 * between this file and pg_dump.c.
9 * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
14 * $PostgreSQL: pgsql/src/bin/pg_dump/common.c,v 1.88 2006/03/02 01:18:24 tgl Exp $
16 *-------------------------------------------------------------------------
19 #include "postgres_fe.h"
21 #include "pg_backup_archiver.h"
24 #include "catalog/pg_class.h"
35 * Variables for mapping DumpId to DumpableObject
37 static DumpableObject **dumpIdMap = NULL;
38 static int allocedDumpIds = 0;
39 static DumpId lastDumpId = 0;
42 * Variables for mapping CatalogId to DumpableObject
44 static bool catalogIdMapValid = false;
45 static DumpableObject **catalogIdMap = NULL;
46 static int numCatalogIds = 0;
49 * These variables are static to avoid the notational cruft of having to pass
50 * them into findTableByOid() and friends.
52 static TableInfo *tblinfo;
53 static TypeInfo *typinfo;
54 static FuncInfo *funinfo;
55 static OprInfo *oprinfo;
59 static int numOperators;
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);
74 * Collect information about all potentially dumpable objects
77 getSchemaData(int *numTablesPtr,
78 const bool schemaOnly,
81 NamespaceInfo *nsinfo;
85 ProcLangInfo *proclanginfo;
99 write_msg(NULL, "reading schemas\n");
100 nsinfo = getNamespaces(&numNamespaces);
103 write_msg(NULL, "reading user-defined functions\n");
104 funinfo = getFuncs(&numFuncs);
106 /* this must be after getFuncs */
108 write_msg(NULL, "reading user-defined types\n");
109 typinfo = getTypes(&numTypes);
111 /* this must be after getFuncs, too */
113 write_msg(NULL, "reading procedural languages\n");
114 proclanginfo = getProcLangs(&numProcLangs);
117 write_msg(NULL, "reading user-defined aggregate functions\n");
118 agginfo = getAggregates(&numAggregates);
121 write_msg(NULL, "reading user-defined operators\n");
122 oprinfo = getOperators(&numOperators);
125 write_msg(NULL, "reading user-defined operator classes\n");
126 opcinfo = getOpclasses(&numOpclasses);
129 write_msg(NULL, "reading user-defined conversions\n");
130 convinfo = getConversions(&numConversions);
133 write_msg(NULL, "reading user-defined tables\n");
134 tblinfo = getTables(&numTables);
137 write_msg(NULL, "reading table inheritance information\n");
138 inhinfo = getInherits(&numInherits);
141 write_msg(NULL, "reading rewrite rules\n");
142 ruleinfo = getRules(&numRules);
145 write_msg(NULL, "reading type casts\n");
146 castinfo = getCasts(&numCasts);
148 /* Link tables to parents, mark parents of target tables interesting */
150 write_msg(NULL, "finding inheritance relationships\n");
151 flagInhTables(tblinfo, numTables, inhinfo, numInherits);
154 write_msg(NULL, "reading column info for interesting tables\n");
155 getTableAttrs(tblinfo, numTables);
158 write_msg(NULL, "flagging inherited columns in subtables\n");
159 flagInhAttrs(tblinfo, numTables, inhinfo, numInherits);
162 write_msg(NULL, "reading indexes\n");
163 getIndexes(tblinfo, numTables);
166 write_msg(NULL, "reading constraints\n");
167 getConstraints(tblinfo, numTables);
170 write_msg(NULL, "reading triggers\n");
171 getTriggers(tblinfo, numTables);
173 *numTablesPtr = numTables;
178 * Fill in parent link fields of every target table, and mark
179 * parents of target tables as interesting
181 * Note that only direct ancestors of targets are marked interesting.
182 * This is sufficient; we don't much care whether they inherited their
188 flagInhTables(TableInfo *tblinfo, int numTables,
189 InhInfo *inhinfo, int numInherits)
196 for (i = 0; i < numTables; i++)
198 /* Sequences and views never have parents */
199 if (tblinfo[i].relkind == RELKIND_SEQUENCE ||
200 tblinfo[i].relkind == RELKIND_VIEW)
203 /* Don't bother computing anything for non-target tables, either */
204 if (!tblinfo[i].dobj.dump)
207 /* Find all the immediate parent tables */
208 findParentsByOid(&tblinfo[i], inhinfo, numInherits);
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;
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
225 flagInhAttrs(TableInfo *tblinfo, int numTables,
226 InhInfo *inhinfo, int numInherits)
232 for (i = 0; i < numTables; i++)
234 TableInfo *tbinfo = &(tblinfo[i]);
239 /* Sequences and views never have parents */
240 if (tbinfo->relkind == RELKIND_SEQUENCE ||
241 tbinfo->relkind == RELKIND_VIEW)
244 /* Don't bother computing anything for non-target tables, either */
245 if (!tbinfo->dobj.dump)
248 numParents = tbinfo->numParents;
249 parents = tbinfo->parents;
252 continue; /* nothing to see here, move along */
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:
259 * - it is NOT NULL and zero parents are NOT NULL
261 * - it has a default value AND the default value does not match
262 * all parent default values, or no parents specify a default.
264 * See discussion on -hackers around 2-Apr-2001.
265 *----------------------------------------------------------------
267 for (j = 0; j < tbinfo->numatts; j++)
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;
276 foundNotNull = false;
277 defaultsMatch = true;
278 defaultsFound = false;
280 attrDef = tbinfo->attrdefs[j];
282 for (k = 0; k < numParents; k++)
287 inhAttrInd = strInArray(tbinfo->attnames[j],
291 if (inhAttrInd != -1)
294 foundNotNull |= parent->notnull[inhAttrInd];
295 if (attrDef != NULL) /* If we have a default, check
300 inhDef = parent->attrdefs[inhAttrInd];
303 defaultsFound = true;
304 defaultsMatch &= (strcmp(attrDef->adef_expr,
305 inhDef->adef_expr) == 0);
312 * Based on the scan of the parents, decide if we can rely on the
315 if (foundAttr) /* Attr was inherited */
317 /* Set inherited flag by default */
318 tbinfo->inhAttrs[j] = true;
319 tbinfo->inhAttrDef[j] = true;
320 tbinfo->inhNotNull[j] = true;
323 * Clear it if attr had a default, but parents did not, or
326 if ((attrDef != NULL) && (!defaultsFound || !defaultsMatch))
328 tbinfo->inhAttrs[j] = false;
329 tbinfo->inhAttrDef[j] = false;
333 * Clear it if NOT NULL and none of the parents were NOT NULL
335 if (tbinfo->notnull[j] && !foundNotNull)
337 tbinfo->inhAttrs[j] = false;
338 tbinfo->inhNotNull[j] = false;
341 /* Clear it if attr has local definition */
342 if (tbinfo->attislocal[j])
343 tbinfo->inhAttrs[j] = false;
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
358 for (j = 0; j < tbinfo->ncheck; j++)
360 ConstraintInfo *constr;
362 constr = &(tbinfo->checkexprs[j]);
364 for (k = 0; k < numParents; k++)
369 for (l = 0; l < parent->ncheck; l++)
371 ConstraintInfo *pconstr = &(parent->checkexprs[l]);
373 if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0)
375 constr->coninherited = true;
379 if (constr->coninherited)
388 * Given a newly-created dumpable object, assign a dump ID,
389 * and enter the object into the lookup table.
391 * The caller is expected to have filled in objType and catId,
392 * but not any of the other standard fields of a DumpableObject.
395 AssignDumpId(DumpableObject *dobj)
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;
405 while (dobj->dumpId >= allocedDumpIds)
409 if (allocedDumpIds <= 0)
412 dumpIdMap = (DumpableObject **)
413 malloc(newAlloc * sizeof(DumpableObject *));
417 newAlloc = allocedDumpIds * 2;
418 dumpIdMap = (DumpableObject **)
419 realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
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;
427 dumpIdMap[dobj->dumpId] = dobj;
429 /* mark catalogIdMap invalid, but don't rebuild it yet */
430 catalogIdMapValid = false;
434 * Assign a DumpId that's not tied to a DumpableObject.
436 * This is used when creating a "fixed" ArchiveEntry that doesn't need to
437 * participate in the sorting logic.
446 * Return the largest DumpId so far assigned
455 * Find a DumpableObject by dump ID
457 * Returns NULL for invalid ID
460 findObjectByDumpId(DumpId dumpId)
462 if (dumpId <= 0 || dumpId >= allocedDumpIds)
463 return NULL; /* out of range? */
464 return dumpIdMap[dumpId];
468 * Find a DumpableObject by catalog ID
470 * Returns NULL for unknown ID
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.
478 findObjectByCatalogId(CatalogId catalogId)
480 DumpableObject **low;
481 DumpableObject **high;
483 if (!catalogIdMapValid)
487 getDumpableObjects(&catalogIdMap, &numCatalogIds);
488 if (numCatalogIds > 1)
489 qsort((void *) catalogIdMap, numCatalogIds,
490 sizeof(DumpableObject *), DOCatalogIdCompare);
491 catalogIdMapValid = true;
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.
499 if (numCatalogIds <= 0)
502 high = catalogIdMap + (numCatalogIds - 1);
505 DumpableObject **middle;
508 middle = low + (high - low) / 2;
509 /* comparison must match DOCatalogIdCompare, below */
510 difference = oidcmp((*middle)->catId.oid, catalogId.oid);
512 difference = oidcmp((*middle)->catId.tableoid, catalogId.tableoid);
515 else if (difference < 0)
524 DOCatalogIdCompare(const void *p1, const void *p2)
526 DumpableObject *obj1 = *(DumpableObject **) p1;
527 DumpableObject *obj2 = *(DumpableObject **) p2;
531 * Compare OID first since it's usually unique, whereas there will only be
532 * a few distinct values of tableoid.
534 cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
536 cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
541 * Build an array of pointers to all known dumpable objects
543 * This simply creates a modifiable copy of the internal map.
546 getDumpableObjects(DumpableObject ***objs, int *numObjs)
551 *objs = (DumpableObject **)
552 malloc(allocedDumpIds * sizeof(DumpableObject *));
554 exit_horribly(NULL, NULL, "out of memory\n");
556 for (i = 1; i < allocedDumpIds; i++)
559 (*objs)[j++] = dumpIdMap[i];
565 * Add a dependency link to a DumpableObject
567 * Note: duplicate dependencies are currently not eliminated
570 addObjectDependency(DumpableObject *dobj, DumpId refId)
572 if (dobj->nDeps >= dobj->allocDeps)
574 if (dobj->allocDeps <= 0)
576 dobj->allocDeps = 16;
577 dobj->dependencies = (DumpId *)
578 malloc(dobj->allocDeps * sizeof(DumpId));
582 dobj->allocDeps *= 2;
583 dobj->dependencies = (DumpId *)
584 realloc(dobj->dependencies,
585 dobj->allocDeps * sizeof(DumpId));
587 if (dobj->dependencies == NULL)
588 exit_horribly(NULL, NULL, "out of memory\n");
590 dobj->dependencies[dobj->nDeps++] = refId;
594 * Remove a dependency link from a DumpableObject
596 * If there are multiple links, all are removed
599 removeObjectDependency(DumpableObject *dobj, DumpId refId)
604 for (i = 0; i < dobj->nDeps; i++)
606 if (dobj->dependencies[i] != refId)
607 dobj->dependencies[j++] = dobj->dependencies[i];
615 * finds the entry (in tblinfo) of the table with the given oid
616 * returns NULL if not found
618 * NOTE: should hash this, but just do linear search for now
621 findTableByOid(Oid oid)
625 for (i = 0; i < numTables; i++)
627 if (tblinfo[i].dobj.catId.oid == oid)
635 * finds the entry (in typinfo) of the type with the given oid
636 * returns NULL if not found
638 * NOTE: should hash this, but just do linear search for now
641 findTypeByOid(Oid oid)
645 for (i = 0; i < numTypes; i++)
647 if (typinfo[i].dobj.catId.oid == oid)
655 * finds the entry (in funinfo) of the function with the given oid
656 * returns NULL if not found
658 * NOTE: should hash this, but just do linear search for now
661 findFuncByOid(Oid oid)
665 for (i = 0; i < numFuncs; i++)
667 if (funinfo[i].dobj.catId.oid == oid)
675 * finds the entry (in oprinfo) of the operator with the given oid
676 * returns NULL if not found
678 * NOTE: should hash this, but just do linear search for now
681 findOprByOid(Oid oid)
685 for (i = 0; i < numOperators; i++)
687 if (oprinfo[i].dobj.catId.oid == oid)
696 * find a table's parents in tblinfo[]
699 findParentsByOid(TableInfo *self,
700 InhInfo *inhinfo, int numInherits)
702 Oid oid = self->dobj.catId.oid;
708 for (i = 0; i < numInherits; i++)
710 if (inhinfo[i].inhrelid == oid)
714 self->numParents = numParents;
718 self->parents = (TableInfo **) malloc(sizeof(TableInfo *) * numParents);
720 for (i = 0; i < numInherits; i++)
722 if (inhinfo[i].inhrelid == oid)
726 parent = findTableByOid(inhinfo[i].inhparent);
729 write_msg(NULL, "failed sanity check, parent OID %u of table \"%s\" (OID %u) not found\n",
730 inhinfo[i].inhparent,
735 self->parents[j++] = parent;
740 self->parents = NULL;
745 * parse a string of numbers delimited by spaces into a character array
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.
753 parseOidArray(const char *str, Oid *array, int arraysize)
765 if (s == ' ' || s == '\0')
769 if (argNum >= arraysize)
771 write_msg(NULL, "could not parse numeric array: too many numbers\n");
775 array[argNum++] = atooid(temp);
783 if (!(isdigit((unsigned char) s) || s == '-') ||
784 j >= sizeof(temp) - 1)
786 write_msg(NULL, "could not parse numeric array: invalid character in number\n");
793 while (argNum < arraysize)
794 array[argNum++] = InvalidOid;
800 * takes in a string and a string array and the number of elements in the
802 * returns the index if the string is somewhere in the array, -1 otherwise
806 strInArray(const char *pattern, char **arr, int arr_size)
810 for (i = 0; i < arr_size; i++)
812 if (strcmp(pattern, arr[i]) == 0)