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.85 2004-12-31 22:03:07 pgsql 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].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 */
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,
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
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
336
if (tbinfo->notnull[j] && !foundNotNull)
338
tbinfo->inhAttrs[j] = false;
339
tbinfo->inhNotNull[j] = false;
342
/* Clear it if attr has local definition */
343
if (tbinfo->attislocal[j])
344
tbinfo->inhAttrs[j] = false;
349
* Check for inherited CHECK constraints. We assume a constraint
350
* is inherited if its expression matches the parent and the name
351
* is the same, *or* both names start with '$'.
353
for (j = 0; j < tbinfo->ncheck; j++)
355
ConstraintInfo *constr;
357
constr = &(tbinfo->checkexprs[j]);
359
for (k = 0; k < numParents; k++)
364
for (l = 0; l < parent->ncheck; l++)
366
ConstraintInfo *pconstr;
368
pconstr = &(parent->checkexprs[l]);
369
if (strcmp(pconstr->condef, constr->condef) != 0)
371
if (strcmp(pconstr->dobj.name, constr->dobj.name) == 0 ||
372
(pconstr->dobj.name[0] == '$' &&
373
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 catalogId,
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->dependencies = NULL;
404
while (dobj->dumpId >= allocedDumpIds)
408
if (allocedDumpIds <= 0)
411
dumpIdMap = (DumpableObject **)
412
malloc(newAlloc * sizeof(DumpableObject *));
416
newAlloc = allocedDumpIds * 2;
417
dumpIdMap = (DumpableObject **)
418
realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *));
420
if (dumpIdMap == NULL)
421
exit_horribly(NULL, NULL, "out of memory\n");
422
memset(dumpIdMap + allocedDumpIds, 0,
423
(newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
424
allocedDumpIds = newAlloc;
426
dumpIdMap[dobj->dumpId] = dobj;
428
/* mark catalogIdMap invalid, but don't rebuild it yet */
429
catalogIdMapValid = false;
433
* Assign a DumpId that's not tied to a DumpableObject.
435
* This is used when creating a "fixed" ArchiveEntry that doesn't need to
436
* participate in the sorting logic.
445
* Return the largest DumpId so far assigned
454
* Find a DumpableObject by dump ID
456
* Returns NULL for invalid ID
459
findObjectByDumpId(DumpId dumpId)
461
if (dumpId <= 0 || dumpId >= allocedDumpIds)
462
return NULL; /* out of range? */
463
return dumpIdMap[dumpId];
467
* Find a DumpableObject by catalog ID
469
* Returns NULL for unknown ID
471
* We use binary search in a sorted list that is built on first call.
472
* If AssignDumpId() and findObjectByCatalogId() calls were intermixed,
473
* the code would work, but possibly be very slow. In the current usage
474
* pattern that does not happen, indeed we only need to build the list once.
477
findObjectByCatalogId(CatalogId catalogId)
479
DumpableObject **low;
480
DumpableObject **high;
482
if (!catalogIdMapValid)
486
getDumpableObjects(&catalogIdMap, &numCatalogIds);
487
if (numCatalogIds > 1)
488
qsort((void *) catalogIdMap, numCatalogIds,
489
sizeof(DumpableObject *), DOCatalogIdCompare);
490
catalogIdMapValid = true;
494
* We could use bsearch() here, but the notational cruft of calling
495
* bsearch is nearly as bad as doing it ourselves; and the generalized
496
* bsearch function is noticeably slower as well.
498
if (numCatalogIds <= 0)
501
high = catalogIdMap + (numCatalogIds - 1);
504
DumpableObject **middle;
507
middle = low + (high - low) / 2;
508
/* comparison must match DOCatalogIdCompare, below */
509
difference = oidcmp((*middle)->catId.oid, catalogId.oid);
511
difference = oidcmp((*middle)->catId.tableoid, catalogId.tableoid);
514
else if (difference < 0)
523
DOCatalogIdCompare(const void *p1, const void *p2)
525
DumpableObject *obj1 = *(DumpableObject **) p1;
526
DumpableObject *obj2 = *(DumpableObject **) p2;
530
* Compare OID first since it's usually unique, whereas there will
531
* only be a few distinct values of tableoid.
533
cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid);
535
cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid);
540
* Build an array of pointers to all known dumpable objects
542
* This simply creates a modifiable copy of the internal map.
545
getDumpableObjects(DumpableObject ***objs, int *numObjs)
550
*objs = (DumpableObject **)
551
malloc(allocedDumpIds * sizeof(DumpableObject *));
553
exit_horribly(NULL, NULL, "out of memory\n");
555
for (i = 1; i < allocedDumpIds; i++)
558
(*objs)[j++] = dumpIdMap[i];
564
* Add a dependency link to a DumpableObject
566
* Note: duplicate dependencies are currently not eliminated
569
addObjectDependency(DumpableObject *dobj, DumpId refId)
571
if (dobj->nDeps >= dobj->allocDeps)
573
if (dobj->allocDeps <= 0)
575
dobj->allocDeps = 16;
576
dobj->dependencies = (DumpId *)
577
malloc(dobj->allocDeps * sizeof(DumpId));
581
dobj->allocDeps *= 2;
582
dobj->dependencies = (DumpId *)
583
realloc(dobj->dependencies,
584
dobj->allocDeps * sizeof(DumpId));
586
if (dobj->dependencies == NULL)
587
exit_horribly(NULL, NULL, "out of memory\n");
589
dobj->dependencies[dobj->nDeps++] = refId;
593
* Remove a dependency link from a DumpableObject
595
* If there are multiple links, all are removed
598
removeObjectDependency(DumpableObject *dobj, DumpId refId)
603
for (i = 0; i < dobj->nDeps; i++)
605
if (dobj->dependencies[i] != refId)
606
dobj->dependencies[j++] = dobj->dependencies[i];
614
* finds the entry (in tblinfo) of the table with the given oid
615
* returns NULL if not found
617
* NOTE: should hash this, but just do linear search for now
620
findTableByOid(Oid oid)
624
for (i = 0; i < numTables; i++)
626
if (tblinfo[i].dobj.catId.oid == oid)
634
* finds the entry (in typinfo) of the type with the given oid
635
* returns NULL if not found
637
* NOTE: should hash this, but just do linear search for now
640
findTypeByOid(Oid oid)
644
for (i = 0; i < numTypes; i++)
646
if (typinfo[i].dobj.catId.oid == oid)
654
* finds the entry (in funinfo) of the function with the given oid
655
* returns NULL if not found
657
* NOTE: should hash this, but just do linear search for now
660
findFuncByOid(Oid oid)
664
for (i = 0; i < numFuncs; i++)
666
if (funinfo[i].dobj.catId.oid == oid)
674
* finds the entry (in oprinfo) of the operator with the given oid
675
* returns NULL if not found
677
* NOTE: should hash this, but just do linear search for now
680
findOprByOid(Oid oid)
684
for (i = 0; i < numOperators; i++)
686
if (oprinfo[i].dobj.catId.oid == oid)
695
* find a table's parents in tblinfo[]
698
findParentsByOid(TableInfo *self,
699
InhInfo *inhinfo, int numInherits)
701
Oid oid = self->dobj.catId.oid;
707
for (i = 0; i < numInherits; i++)
709
if (inhinfo[i].inhrelid == oid)
713
self->numParents = numParents;
717
self->parents = (TableInfo **) malloc(sizeof(TableInfo *) * numParents);
719
for (i = 0; i < numInherits; i++)
721
if (inhinfo[i].inhrelid == oid)
725
parent = findTableByOid(inhinfo[i].inhparent);
728
write_msg(NULL, "failed sanity check, parent OID %u of table \"%s\" (OID %u) not found\n",
729
inhinfo[i].inhparent,
734
self->parents[j++] = parent;
739
self->parents = NULL;
744
* parse a string of numbers delimited by spaces into a character array
746
* Note: actually this is used for both Oids and potentially-signed
747
* attribute numbers. This should cause no trouble, but we could split
748
* the function into two functions with different argument types if it does.
752
parseOidArray(const char *str, Oid *array, int arraysize)
764
if (s == ' ' || s == '\0')
768
if (argNum >= arraysize)
770
write_msg(NULL, "could not parse numeric array: too many numbers\n");
774
array[argNum++] = atooid(temp);
782
if (!(isdigit((unsigned char) s) || s == '-') ||
783
j >= sizeof(temp) - 1)
785
write_msg(NULL, "could not parse numeric array: invalid character in number\n");
792
while (argNum < arraysize)
793
array[argNum++] = InvalidOid;
799
* takes in a string and a string array and the number of elements in the
801
* returns the index if the string is somewhere in the array, -1 otherwise
805
strInArray(const char *pattern, char **arr, int arr_size)
809
for (i = 0; i < arr_size; i++)
811
if (strcmp(pattern, arr[i]) == 0)