Line data Source code
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-2025, 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 "postgres_fe.h"
17 :
18 : #include <ctype.h>
19 :
20 : #include "catalog/pg_am_d.h"
21 : #include "catalog/pg_class_d.h"
22 : #include "catalog/pg_collation_d.h"
23 : #include "catalog/pg_extension_d.h"
24 : #include "catalog/pg_namespace_d.h"
25 : #include "catalog/pg_operator_d.h"
26 : #include "catalog/pg_proc_d.h"
27 : #include "catalog/pg_publication_d.h"
28 : #include "catalog/pg_subscription_d.h"
29 : #include "catalog/pg_type_d.h"
30 : #include "common/hashfn.h"
31 : #include "pg_backup_utils.h"
32 : #include "pg_dump.h"
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; /* Note: 0 is InvalidDumpId */
40 :
41 : /*
42 : * Infrastructure for mapping CatalogId to DumpableObject
43 : *
44 : * We use a hash table generated by simplehash.h. That infrastructure
45 : * requires all the hash table entries to be the same size, and it also
46 : * expects that it can move them around when resizing the table. So we
47 : * cannot make the DumpableObjects be elements of the hash table directly;
48 : * instead, the hash table elements contain pointers to DumpableObjects.
49 : * This does have the advantage of letting us map multiple CatalogIds
50 : * to one DumpableObject, which is useful for blobs.
51 : *
52 : * It turns out to be convenient to also use this data structure to map
53 : * CatalogIds to owning extensions, if any. Since extension membership
54 : * data is read before creating most DumpableObjects, either one of dobj
55 : * and ext could be NULL.
56 : */
57 : typedef struct _catalogIdMapEntry
58 : {
59 : CatalogId catId; /* the indexed CatalogId */
60 : uint32 status; /* hash status */
61 : uint32 hashval; /* hash code for the CatalogId */
62 : DumpableObject *dobj; /* the associated DumpableObject, if any */
63 : ExtensionInfo *ext; /* owning extension, if any */
64 : } CatalogIdMapEntry;
65 :
66 : #define SH_PREFIX catalogid
67 : #define SH_ELEMENT_TYPE CatalogIdMapEntry
68 : #define SH_KEY_TYPE CatalogId
69 : #define SH_KEY catId
70 : #define SH_HASH_KEY(tb, key) hash_bytes((const unsigned char *) &(key), sizeof(CatalogId))
71 : #define SH_EQUAL(tb, a, b) ((a).oid == (b).oid && (a).tableoid == (b).tableoid)
72 : #define SH_STORE_HASH
73 : #define SH_GET_HASH(tb, a) (a)->hashval
74 : #define SH_SCOPE static inline
75 : #define SH_RAW_ALLOCATOR pg_malloc0
76 : #define SH_DECLARE
77 : #define SH_DEFINE
78 : #include "lib/simplehash.h"
79 :
80 : #define CATALOGIDHASH_INITIAL_SIZE 10000
81 :
82 : static catalogid_hash *catalogIdHash = NULL;
83 :
84 : static void flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
85 : InhInfo *inhinfo, int numInherits);
86 : static void flagInhIndexes(Archive *fout, TableInfo *tblinfo, int numTables);
87 : static void flagInhAttrs(Archive *fout, DumpOptions *dopt, TableInfo *tblinfo,
88 : int numTables);
89 : static int strInArray(const char *pattern, char **arr, int arr_size);
90 : static IndxInfo *findIndexByOid(Oid oid);
91 :
92 :
93 : /*
94 : * getSchemaData
95 : * Collect information about all potentially dumpable objects
96 : */
97 : TableInfo *
98 364 : getSchemaData(Archive *fout, int *numTablesPtr)
99 : {
100 : TableInfo *tblinfo;
101 : ExtensionInfo *extinfo;
102 : InhInfo *inhinfo;
103 : int numTables;
104 : int numExtensions;
105 : int numInherits;
106 :
107 : /*
108 : * We must read extensions and extension membership info first, because
109 : * extension membership needs to be consultable during decisions about
110 : * whether other objects are to be dumped.
111 : */
112 364 : pg_log_info("reading extensions");
113 364 : extinfo = getExtensions(fout, &numExtensions);
114 :
115 364 : pg_log_info("identifying extension members");
116 364 : getExtensionMembership(fout, extinfo, numExtensions);
117 :
118 364 : pg_log_info("reading schemas");
119 364 : getNamespaces(fout);
120 :
121 : /*
122 : * getTables should be done as soon as possible, so as to minimize the
123 : * window between starting our transaction and acquiring per-table locks.
124 : * However, we have to do getNamespaces first because the tables get
125 : * linked to their containing namespaces during getTables.
126 : */
127 364 : pg_log_info("reading user-defined tables");
128 364 : tblinfo = getTables(fout, &numTables);
129 :
130 362 : getOwnedSeqs(fout, tblinfo, numTables);
131 :
132 362 : pg_log_info("reading user-defined functions");
133 362 : getFuncs(fout);
134 :
135 : /* this must be after getTables and getFuncs */
136 362 : pg_log_info("reading user-defined types");
137 362 : getTypes(fout);
138 :
139 : /* this must be after getFuncs, too */
140 362 : pg_log_info("reading procedural languages");
141 362 : getProcLangs(fout);
142 :
143 362 : pg_log_info("reading user-defined aggregate functions");
144 362 : getAggregates(fout);
145 :
146 362 : pg_log_info("reading user-defined operators");
147 362 : getOperators(fout);
148 :
149 362 : pg_log_info("reading user-defined access methods");
150 362 : getAccessMethods(fout);
151 :
152 362 : pg_log_info("reading user-defined operator classes");
153 362 : getOpclasses(fout);
154 :
155 362 : pg_log_info("reading user-defined operator families");
156 362 : getOpfamilies(fout);
157 :
158 362 : pg_log_info("reading user-defined text search parsers");
159 362 : getTSParsers(fout);
160 :
161 362 : pg_log_info("reading user-defined text search templates");
162 362 : getTSTemplates(fout);
163 :
164 362 : pg_log_info("reading user-defined text search dictionaries");
165 362 : getTSDictionaries(fout);
166 :
167 362 : pg_log_info("reading user-defined text search configurations");
168 362 : getTSConfigurations(fout);
169 :
170 362 : pg_log_info("reading user-defined foreign-data wrappers");
171 362 : getForeignDataWrappers(fout);
172 :
173 362 : pg_log_info("reading user-defined foreign servers");
174 362 : getForeignServers(fout);
175 :
176 362 : pg_log_info("reading default privileges");
177 362 : getDefaultACLs(fout);
178 :
179 362 : pg_log_info("reading user-defined collations");
180 362 : getCollations(fout);
181 :
182 362 : pg_log_info("reading user-defined conversions");
183 362 : getConversions(fout);
184 :
185 362 : pg_log_info("reading type casts");
186 362 : getCasts(fout);
187 :
188 362 : pg_log_info("reading transforms");
189 362 : getTransforms(fout);
190 :
191 362 : pg_log_info("reading table inheritance information");
192 362 : inhinfo = getInherits(fout, &numInherits);
193 :
194 362 : pg_log_info("reading event triggers");
195 362 : getEventTriggers(fout);
196 :
197 : /* Identify extension configuration tables that should be dumped */
198 362 : pg_log_info("finding extension tables");
199 362 : processExtensionTables(fout, extinfo, numExtensions);
200 :
201 : /* Link tables to parents, mark parents of target tables interesting */
202 362 : pg_log_info("finding inheritance relationships");
203 362 : flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits);
204 :
205 362 : pg_log_info("reading column info for interesting tables");
206 362 : getTableAttrs(fout, tblinfo, numTables);
207 :
208 362 : pg_log_info("flagging inherited columns in subtables");
209 362 : flagInhAttrs(fout, fout->dopt, tblinfo, numTables);
210 :
211 362 : pg_log_info("reading partitioning data");
212 362 : getPartitioningInfo(fout);
213 :
214 362 : pg_log_info("reading indexes");
215 362 : getIndexes(fout, tblinfo, numTables);
216 :
217 362 : pg_log_info("flagging indexes in partitioned tables");
218 362 : flagInhIndexes(fout, tblinfo, numTables);
219 :
220 362 : pg_log_info("reading extended statistics");
221 362 : getExtendedStatistics(fout);
222 :
223 362 : pg_log_info("reading constraints");
224 362 : getConstraints(fout, tblinfo, numTables);
225 :
226 362 : pg_log_info("reading triggers");
227 362 : getTriggers(fout, tblinfo, numTables);
228 :
229 362 : pg_log_info("reading rewrite rules");
230 362 : getRules(fout);
231 :
232 362 : pg_log_info("reading policies");
233 362 : getPolicies(fout, tblinfo, numTables);
234 :
235 362 : pg_log_info("reading publications");
236 362 : getPublications(fout);
237 :
238 362 : pg_log_info("reading publication membership of tables");
239 362 : getPublicationTables(fout, tblinfo, numTables);
240 :
241 362 : pg_log_info("reading publication membership of schemas");
242 362 : getPublicationNamespaces(fout);
243 :
244 362 : pg_log_info("reading subscriptions");
245 362 : getSubscriptions(fout);
246 :
247 362 : pg_log_info("reading subscription membership of tables");
248 362 : getSubscriptionTables(fout);
249 :
250 362 : free(inhinfo); /* not needed any longer */
251 :
252 362 : *numTablesPtr = numTables;
253 362 : return tblinfo;
254 : }
255 :
256 : /* flagInhTables -
257 : * Fill in parent link fields of tables for which we need that information,
258 : * mark parents of target tables as interesting, and create
259 : * TableAttachInfo objects for partitioned tables with appropriate
260 : * dependency links.
261 : *
262 : * Note that only direct ancestors of targets are marked interesting.
263 : * This is sufficient; we don't much care whether they inherited their
264 : * attributes or not.
265 : *
266 : * modifies tblinfo
267 : */
268 : static void
269 362 : flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
270 : InhInfo *inhinfo, int numInherits)
271 : {
272 362 : TableInfo *child = NULL;
273 362 : TableInfo *parent = NULL;
274 : int i,
275 : j;
276 :
277 : /*
278 : * Set up links from child tables to their parents.
279 : *
280 : * We used to attempt to skip this work for tables that are not to be
281 : * dumped; but the optimizable cases are rare in practice, and setting up
282 : * these links in bulk is cheaper than the old way. (Note in particular
283 : * that it's very rare for a child to have more than one parent.)
284 : */
285 7036 : for (i = 0; i < numInherits; i++)
286 : {
287 : /*
288 : * Skip a hashtable lookup if it's same table as last time. This is
289 : * unlikely for the child, but less so for the parent. (Maybe we
290 : * should ask the backend for a sorted array to make it more likely?
291 : * Not clear the sorting effort would be repaid, though.)
292 : */
293 6674 : if (child == NULL ||
294 5076 : child->dobj.catId.oid != inhinfo[i].inhrelid)
295 : {
296 6438 : child = findTableByOid(inhinfo[i].inhrelid);
297 :
298 : /*
299 : * If we find no TableInfo, assume the pg_inherits entry is for a
300 : * partitioned index, which we don't need to track.
301 : */
302 6438 : if (child == NULL)
303 1464 : continue;
304 : }
305 5210 : if (parent == NULL ||
306 5062 : parent->dobj.catId.oid != inhinfo[i].inhparent)
307 : {
308 3122 : parent = findTableByOid(inhinfo[i].inhparent);
309 3122 : if (parent == NULL)
310 0 : pg_fatal("failed sanity check, parent OID %u of table \"%s\" (OID %u) not found",
311 : inhinfo[i].inhparent,
312 : child->dobj.name,
313 : child->dobj.catId.oid);
314 : }
315 : /* Add this parent to the child's list of parents. */
316 5210 : if (child->numParents > 0)
317 236 : child->parents = pg_realloc_array(child->parents,
318 : TableInfo *,
319 : child->numParents + 1);
320 : else
321 4974 : child->parents = pg_malloc_array(TableInfo *, 1);
322 5210 : child->parents[child->numParents++] = parent;
323 : }
324 :
325 : /*
326 : * Now consider all child tables and mark parents interesting as needed.
327 : */
328 96100 : for (i = 0; i < numTables; i++)
329 : {
330 : /*
331 : * If needed, mark the parents as interesting for getTableAttrs and
332 : * getIndexes. We only need this for direct parents of dumpable
333 : * tables.
334 : */
335 95738 : if (tblinfo[i].dobj.dump)
336 : {
337 60528 : int numParents = tblinfo[i].numParents;
338 60528 : TableInfo **parents = tblinfo[i].parents;
339 :
340 64442 : for (j = 0; j < numParents; j++)
341 3914 : parents[j]->interesting = true;
342 : }
343 :
344 : /* Create TableAttachInfo object if needed */
345 95738 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
346 14094 : tblinfo[i].ispartition)
347 : {
348 : TableAttachInfo *attachinfo;
349 :
350 : /* With partitions there can only be one parent */
351 2656 : if (tblinfo[i].numParents != 1)
352 0 : pg_fatal("invalid number of parents %d for table \"%s\"",
353 : tblinfo[i].numParents,
354 : tblinfo[i].dobj.name);
355 :
356 2656 : attachinfo = (TableAttachInfo *) palloc(sizeof(TableAttachInfo));
357 2656 : attachinfo->dobj.objType = DO_TABLE_ATTACH;
358 2656 : attachinfo->dobj.catId.tableoid = 0;
359 2656 : attachinfo->dobj.catId.oid = 0;
360 2656 : AssignDumpId(&attachinfo->dobj);
361 2656 : attachinfo->dobj.name = pg_strdup(tblinfo[i].dobj.name);
362 2656 : attachinfo->dobj.namespace = tblinfo[i].dobj.namespace;
363 2656 : attachinfo->parentTbl = tblinfo[i].parents[0];
364 2656 : attachinfo->partitionTbl = &tblinfo[i];
365 :
366 : /*
367 : * We must state the DO_TABLE_ATTACH object's dependencies
368 : * explicitly, since it will not match anything in pg_depend.
369 : *
370 : * Give it dependencies on both the partition table and the parent
371 : * table, so that it will not be executed till both of those
372 : * exist. (There's no need to care what order those are created
373 : * in.)
374 : */
375 2656 : addObjectDependency(&attachinfo->dobj, tblinfo[i].dobj.dumpId);
376 2656 : addObjectDependency(&attachinfo->dobj, tblinfo[i].parents[0]->dobj.dumpId);
377 : }
378 : }
379 362 : }
380 :
381 : /*
382 : * flagInhIndexes -
383 : * Create IndexAttachInfo objects for partitioned indexes, and add
384 : * appropriate dependency links.
385 : */
386 : static void
387 362 : flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
388 : {
389 : int i,
390 : j;
391 :
392 96100 : for (i = 0; i < numTables; i++)
393 : {
394 95738 : if (!tblinfo[i].ispartition || tblinfo[i].numParents == 0)
395 92172 : continue;
396 :
397 : Assert(tblinfo[i].numParents == 1);
398 :
399 4852 : for (j = 0; j < tblinfo[i].numIndexes; j++)
400 : {
401 1286 : IndxInfo *index = &(tblinfo[i].indexes[j]);
402 : IndxInfo *parentidx;
403 : IndexAttachInfo *attachinfo;
404 :
405 1286 : if (index->parentidx == 0)
406 110 : continue;
407 :
408 1176 : parentidx = findIndexByOid(index->parentidx);
409 1176 : if (parentidx == NULL)
410 0 : continue;
411 :
412 1176 : attachinfo = pg_malloc_object(IndexAttachInfo);
413 :
414 1176 : attachinfo->dobj.objType = DO_INDEX_ATTACH;
415 1176 : attachinfo->dobj.catId.tableoid = 0;
416 1176 : attachinfo->dobj.catId.oid = 0;
417 1176 : AssignDumpId(&attachinfo->dobj);
418 1176 : attachinfo->dobj.name = pg_strdup(index->dobj.name);
419 1176 : attachinfo->dobj.namespace = index->indextable->dobj.namespace;
420 1176 : attachinfo->parentIdx = parentidx;
421 1176 : attachinfo->partitionIdx = index;
422 :
423 : /*
424 : * We must state the DO_INDEX_ATTACH object's dependencies
425 : * explicitly, since it will not match anything in pg_depend.
426 : *
427 : * Give it dependencies on both the partition index and the parent
428 : * index, so that it will not be executed till both of those
429 : * exist. (There's no need to care what order those are created
430 : * in.)
431 : *
432 : * In addition, give it dependencies on the indexes' underlying
433 : * tables. This does nothing of great value so far as serial
434 : * restore ordering goes, but it ensures that a parallel restore
435 : * will not try to run the ATTACH concurrently with other
436 : * operations on those tables.
437 : */
438 1176 : addObjectDependency(&attachinfo->dobj, index->dobj.dumpId);
439 1176 : addObjectDependency(&attachinfo->dobj, parentidx->dobj.dumpId);
440 1176 : addObjectDependency(&attachinfo->dobj,
441 1176 : index->indextable->dobj.dumpId);
442 1176 : addObjectDependency(&attachinfo->dobj,
443 1176 : parentidx->indextable->dobj.dumpId);
444 :
445 : /* keep track of the list of partitions in the parent index */
446 1176 : simple_ptr_list_append(&parentidx->partattaches, &attachinfo->dobj);
447 : }
448 : }
449 362 : }
450 :
451 : /* flagInhAttrs -
452 : * for each dumpable table in tblinfo, flag its inherited attributes
453 : *
454 : * What we need to do here is:
455 : *
456 : * - Detect child columns that inherit NOT NULL bits from their parents, so
457 : * that we needn't specify that again for the child. For versions 18 and
458 : * up, this is needed when the parent is NOT VALID and the child isn't.
459 : *
460 : * - Detect child columns that have DEFAULT NULL when their parents had some
461 : * non-null default. In this case, we make up a dummy AttrDefInfo object so
462 : * that we'll correctly emit the necessary DEFAULT NULL clause; otherwise
463 : * the backend will apply an inherited default to the column.
464 : *
465 : * - Detect child columns that have a generation expression and all their
466 : * parents also have the same generation expression, and if so suppress the
467 : * child's expression. The child will inherit the generation expression
468 : * automatically, so there's no need to dump it. This improves the dump's
469 : * compatibility with pre-v16 servers, which didn't allow the child's
470 : * expression to be given explicitly. Exceptions: If it's a partition or
471 : * we are in binary upgrade mode, we dump such expressions anyway because
472 : * in those cases inherited tables are recreated standalone first and then
473 : * reattached to the parent. (See also the logic in dumpTableSchema().)
474 : *
475 : * modifies tblinfo
476 : */
477 : static void
478 362 : flagInhAttrs(Archive *fout, DumpOptions *dopt, TableInfo *tblinfo, int numTables)
479 : {
480 : int i,
481 : j,
482 : k;
483 :
484 : /*
485 : * We scan the tables in OID order, since that's how tblinfo[] is sorted.
486 : * Hence we will typically visit parents before their children --- but
487 : * that is *not* guaranteed. Thus this loop must be careful that it does
488 : * not alter table properties in a way that could change decisions made at
489 : * child tables during other iterations.
490 : */
491 96100 : for (i = 0; i < numTables; i++)
492 : {
493 95738 : TableInfo *tbinfo = &(tblinfo[i]);
494 : int numParents;
495 : TableInfo **parents;
496 :
497 : /* Some kinds never have parents */
498 95738 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
499 94444 : tbinfo->relkind == RELKIND_VIEW ||
500 40782 : tbinfo->relkind == RELKIND_MATVIEW)
501 56028 : continue;
502 :
503 : /* Don't bother computing anything for non-target tables, either */
504 39710 : if (!tbinfo->dobj.dump)
505 7904 : continue;
506 :
507 31806 : numParents = tbinfo->numParents;
508 31806 : parents = tbinfo->parents;
509 :
510 31806 : if (numParents == 0)
511 28074 : continue; /* nothing to see here, move along */
512 :
513 : /* For each column, search for matching column names in parent(s) */
514 13156 : for (j = 0; j < tbinfo->numatts; j++)
515 : {
516 : bool foundNotNull; /* Attr was NOT NULL in a parent */
517 : bool foundDefault; /* Found a default in a parent */
518 : bool foundSameGenerated; /* Found matching GENERATED */
519 : bool foundDiffGenerated; /* Found non-matching GENERATED */
520 9424 : bool allNotNullsInvalid = true; /* is NOT NULL NOT VALID
521 : * on all parents? */
522 :
523 : /* no point in examining dropped columns */
524 9424 : if (tbinfo->attisdropped[j])
525 610 : continue;
526 :
527 8814 : foundNotNull = false;
528 8814 : foundDefault = false;
529 8814 : foundSameGenerated = false;
530 8814 : foundDiffGenerated = false;
531 18060 : for (k = 0; k < numParents; k++)
532 : {
533 9246 : TableInfo *parent = parents[k];
534 : int inhAttrInd;
535 :
536 9246 : inhAttrInd = strInArray(tbinfo->attnames[j],
537 : parent->attnames,
538 : parent->numatts);
539 9246 : if (inhAttrInd >= 0)
540 : {
541 8766 : AttrDefInfo *parentDef = parent->attrdefs[inhAttrInd];
542 :
543 : /*
544 : * Account for each parent having a not-null constraint.
545 : * In versions 18 and later, we don't need this (and those
546 : * didn't have NO INHERIT.)
547 : */
548 8766 : if (fout->remoteVersion < 180000 &&
549 0 : parent->notnull_constrs[inhAttrInd] != NULL)
550 0 : foundNotNull = true;
551 :
552 : /*
553 : * Keep track of whether all the parents that have a
554 : * not-null constraint on this column have it as NOT
555 : * VALID; if they all are, arrange to have it printed for
556 : * this column. If at least one parent has it as valid,
557 : * there's no need.
558 : */
559 8766 : if (fout->remoteVersion >= 180000 &&
560 8766 : parent->notnull_constrs[inhAttrInd] &&
561 1800 : !parent->notnull_invalid[inhAttrInd])
562 1800 : allNotNullsInvalid = false;
563 :
564 18560 : foundDefault |= (parentDef != NULL &&
565 9694 : strcmp(parentDef->adef_expr, "NULL") != 0 &&
566 928 : !parent->attgenerated[inhAttrInd]);
567 8766 : if (parent->attgenerated[inhAttrInd])
568 : {
569 : /* these pointer nullness checks are just paranoia */
570 568 : if (parentDef != NULL &&
571 520 : tbinfo->attrdefs[j] != NULL &&
572 520 : strcmp(parentDef->adef_expr,
573 520 : tbinfo->attrdefs[j]->adef_expr) == 0)
574 460 : foundSameGenerated = true;
575 : else
576 108 : foundDiffGenerated = true;
577 : }
578 : }
579 : }
580 :
581 : /*
582 : * In versions < 18, for lack of a better system, we arbitrarily
583 : * decide that a not-null constraint is not locally defined if at
584 : * least one of the parents has it.
585 : */
586 8814 : if (fout->remoteVersion < 180000 && foundNotNull)
587 0 : tbinfo->notnull_islocal[j] = false;
588 :
589 : /*
590 : * For versions >18, we must print the not-null constraint locally
591 : * for this table even if it isn't really locally defined, but is
592 : * valid for the child and no parent has it as valid.
593 : */
594 8814 : if (fout->remoteVersion >= 180000 && allNotNullsInvalid)
595 7024 : tbinfo->notnull_islocal[j] = true;
596 :
597 : /*
598 : * Manufacture a DEFAULT NULL clause if necessary. This breaks
599 : * the advice given above to avoid changing state that might get
600 : * inspected in other loop iterations. We prevent trouble by
601 : * having the foundDefault test above check whether adef_expr is
602 : * "NULL", so that it will reach the same conclusion before or
603 : * after this is done.
604 : */
605 8814 : if (foundDefault && tbinfo->attrdefs[j] == NULL)
606 : {
607 : AttrDefInfo *attrDef;
608 :
609 80 : attrDef = pg_malloc_object(AttrDefInfo);
610 80 : attrDef->dobj.objType = DO_ATTRDEF;
611 80 : attrDef->dobj.catId.tableoid = 0;
612 80 : attrDef->dobj.catId.oid = 0;
613 80 : AssignDumpId(&attrDef->dobj);
614 80 : attrDef->dobj.name = pg_strdup(tbinfo->dobj.name);
615 80 : attrDef->dobj.namespace = tbinfo->dobj.namespace;
616 80 : attrDef->dobj.dump = tbinfo->dobj.dump;
617 :
618 80 : attrDef->adtable = tbinfo;
619 80 : attrDef->adnum = j + 1;
620 80 : attrDef->adef_expr = pg_strdup("NULL");
621 :
622 : /* Will column be dumped explicitly? */
623 80 : if (shouldPrintColumn(dopt, tbinfo, j))
624 : {
625 80 : attrDef->separate = false;
626 : /* No dependency needed: NULL cannot have dependencies */
627 : }
628 : else
629 : {
630 : /* column will be suppressed, print default separately */
631 0 : attrDef->separate = true;
632 : /* ensure it comes out after the table */
633 0 : addObjectDependency(&attrDef->dobj,
634 : tbinfo->dobj.dumpId);
635 : }
636 :
637 80 : tbinfo->attrdefs[j] = attrDef;
638 : }
639 :
640 : /* No need to dump generation expression if it's inheritable */
641 8814 : if (foundSameGenerated && !foundDiffGenerated &&
642 460 : !tbinfo->ispartition && !dopt->binary_upgrade)
643 336 : tbinfo->attrdefs[j]->dobj.dump = DUMP_COMPONENT_NONE;
644 : }
645 : }
646 362 : }
647 :
648 : /*
649 : * AssignDumpId
650 : * Given a newly-created dumpable object, assign a dump ID,
651 : * and enter the object into the lookup tables.
652 : *
653 : * The caller is expected to have filled in objType and catId,
654 : * but not any of the other standard fields of a DumpableObject.
655 : */
656 : void
657 1347012 : AssignDumpId(DumpableObject *dobj)
658 : {
659 1347012 : dobj->dumpId = ++lastDumpId;
660 1347012 : dobj->name = NULL; /* must be set later */
661 1347012 : dobj->namespace = NULL; /* may be set later */
662 1347012 : dobj->dump = DUMP_COMPONENT_ALL; /* default assumption */
663 1347012 : dobj->dump_contains = DUMP_COMPONENT_ALL; /* default assumption */
664 : /* All objects have definitions; we may set more components bits later */
665 1347012 : dobj->components = DUMP_COMPONENT_DEFINITION;
666 1347012 : dobj->ext_member = false; /* default assumption */
667 1347012 : dobj->depends_on_ext = false; /* default assumption */
668 1347012 : dobj->dependencies = NULL;
669 1347012 : dobj->nDeps = 0;
670 1347012 : dobj->allocDeps = 0;
671 :
672 : /* Add object to dumpIdMap[], enlarging that array if need be */
673 1348852 : while (dobj->dumpId >= allocedDumpIds)
674 : {
675 : int newAlloc;
676 :
677 1840 : if (allocedDumpIds <= 0)
678 : {
679 364 : newAlloc = 256;
680 364 : dumpIdMap = pg_malloc_array(DumpableObject *, newAlloc);
681 : }
682 : else
683 : {
684 1476 : newAlloc = allocedDumpIds * 2;
685 1476 : dumpIdMap = pg_realloc_array(dumpIdMap, DumpableObject *, newAlloc);
686 : }
687 1840 : memset(dumpIdMap + allocedDumpIds, 0,
688 1840 : (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
689 1840 : allocedDumpIds = newAlloc;
690 : }
691 1347012 : dumpIdMap[dobj->dumpId] = dobj;
692 :
693 : /* If it has a valid CatalogId, enter it into the hash table */
694 1347012 : if (OidIsValid(dobj->catId.tableoid))
695 : {
696 : CatalogIdMapEntry *entry;
697 : bool found;
698 :
699 : /* Initialize CatalogId hash table if not done yet */
700 1309062 : if (catalogIdHash == NULL)
701 364 : catalogIdHash = catalogid_create(CATALOGIDHASH_INITIAL_SIZE, NULL);
702 :
703 1309062 : entry = catalogid_insert(catalogIdHash, dobj->catId, &found);
704 1309062 : if (!found)
705 : {
706 1307284 : entry->dobj = NULL;
707 1307284 : entry->ext = NULL;
708 : }
709 : Assert(entry->dobj == NULL);
710 1309062 : entry->dobj = dobj;
711 : }
712 1347012 : }
713 :
714 : /*
715 : * recordAdditionalCatalogID
716 : * Record an additional catalog ID for the given DumpableObject
717 : */
718 : void
719 20 : recordAdditionalCatalogID(CatalogId catId, DumpableObject *dobj)
720 : {
721 : CatalogIdMapEntry *entry;
722 : bool found;
723 :
724 : /* CatalogId hash table must exist, if we have a DumpableObject */
725 : Assert(catalogIdHash != NULL);
726 :
727 : /* Add reference to CatalogId hash */
728 20 : entry = catalogid_insert(catalogIdHash, catId, &found);
729 20 : if (!found)
730 : {
731 20 : entry->dobj = NULL;
732 20 : entry->ext = NULL;
733 : }
734 : Assert(entry->dobj == NULL);
735 20 : entry->dobj = dobj;
736 20 : }
737 :
738 : /*
739 : * Assign a DumpId that's not tied to a DumpableObject.
740 : *
741 : * This is used when creating a "fixed" ArchiveEntry that doesn't need to
742 : * participate in the sorting logic.
743 : */
744 : DumpId
745 26576 : createDumpId(void)
746 : {
747 26576 : return ++lastDumpId;
748 : }
749 :
750 : /*
751 : * Return the largest DumpId so far assigned
752 : */
753 : DumpId
754 2534 : getMaxDumpId(void)
755 : {
756 2534 : return lastDumpId;
757 : }
758 :
759 : /*
760 : * Find a DumpableObject by dump ID
761 : *
762 : * Returns NULL for invalid ID
763 : */
764 : DumpableObject *
765 43797266 : findObjectByDumpId(DumpId dumpId)
766 : {
767 43797266 : if (dumpId <= 0 || dumpId >= allocedDumpIds)
768 0 : return NULL; /* out of range? */
769 43797266 : return dumpIdMap[dumpId];
770 : }
771 :
772 : /*
773 : * Find a DumpableObject by catalog ID
774 : *
775 : * Returns NULL for unknown ID
776 : */
777 : DumpableObject *
778 7285470 : findObjectByCatalogId(CatalogId catalogId)
779 : {
780 : CatalogIdMapEntry *entry;
781 :
782 7285470 : if (catalogIdHash == NULL)
783 0 : return NULL; /* no objects exist yet */
784 :
785 7285470 : entry = catalogid_lookup(catalogIdHash, catalogId);
786 7285470 : if (entry == NULL)
787 1309328 : return NULL;
788 5976142 : return entry->dobj;
789 : }
790 :
791 : /*
792 : * Build an array of pointers to all known dumpable objects
793 : *
794 : * This simply creates a modifiable copy of the internal map.
795 : */
796 : void
797 376 : getDumpableObjects(DumpableObject ***objs, int *numObjs)
798 : {
799 : int i,
800 : j;
801 :
802 376 : *objs = pg_malloc_array(DumpableObject *, allocedDumpIds);
803 376 : j = 0;
804 1687552 : for (i = 1; i < allocedDumpIds; i++)
805 : {
806 1687176 : if (dumpIdMap[i])
807 1397880 : (*objs)[j++] = dumpIdMap[i];
808 : }
809 376 : *numObjs = j;
810 376 : }
811 :
812 : /*
813 : * Add a dependency link to a DumpableObject
814 : *
815 : * Note: duplicate dependencies are currently not eliminated
816 : */
817 : void
818 2122728 : addObjectDependency(DumpableObject *dobj, DumpId refId)
819 : {
820 2122728 : if (dobj->nDeps >= dobj->allocDeps)
821 : {
822 360916 : if (dobj->allocDeps <= 0)
823 : {
824 343006 : dobj->allocDeps = 16;
825 343006 : dobj->dependencies = pg_malloc_array(DumpId, dobj->allocDeps);
826 : }
827 : else
828 : {
829 17910 : dobj->allocDeps *= 2;
830 17910 : dobj->dependencies = pg_realloc_array(dobj->dependencies,
831 : DumpId, dobj->allocDeps);
832 : }
833 : }
834 2122728 : dobj->dependencies[dobj->nDeps++] = refId;
835 2122728 : }
836 :
837 : /*
838 : * Remove a dependency link from a DumpableObject
839 : *
840 : * If there are multiple links, all are removed
841 : */
842 : void
843 60864 : removeObjectDependency(DumpableObject *dobj, DumpId refId)
844 : {
845 : int i;
846 60864 : int j = 0;
847 :
848 1437642 : for (i = 0; i < dobj->nDeps; i++)
849 : {
850 1376778 : if (dobj->dependencies[i] != refId)
851 1313020 : dobj->dependencies[j++] = dobj->dependencies[i];
852 : }
853 60864 : dobj->nDeps = j;
854 60864 : }
855 :
856 :
857 : /*
858 : * findTableByOid
859 : * finds the DumpableObject for the table with the given oid
860 : * returns NULL if not found
861 : */
862 : TableInfo *
863 164034 : findTableByOid(Oid oid)
864 : {
865 : CatalogId catId;
866 : DumpableObject *dobj;
867 :
868 164034 : catId.tableoid = RelationRelationId;
869 164034 : catId.oid = oid;
870 164034 : dobj = findObjectByCatalogId(catId);
871 : Assert(dobj == NULL || dobj->objType == DO_TABLE);
872 164034 : return (TableInfo *) dobj;
873 : }
874 :
875 : /*
876 : * findIndexByOid
877 : * finds the DumpableObject for the index with the given oid
878 : * returns NULL if not found
879 : */
880 : static IndxInfo *
881 1176 : findIndexByOid(Oid oid)
882 : {
883 : CatalogId catId;
884 : DumpableObject *dobj;
885 :
886 1176 : catId.tableoid = RelationRelationId;
887 1176 : catId.oid = oid;
888 1176 : dobj = findObjectByCatalogId(catId);
889 : Assert(dobj == NULL || dobj->objType == DO_INDEX);
890 1176 : return (IndxInfo *) dobj;
891 : }
892 :
893 : /*
894 : * findTypeByOid
895 : * finds the DumpableObject for the type with the given oid
896 : * returns NULL if not found
897 : */
898 : TypeInfo *
899 2773696 : findTypeByOid(Oid oid)
900 : {
901 : CatalogId catId;
902 : DumpableObject *dobj;
903 :
904 2773696 : catId.tableoid = TypeRelationId;
905 2773696 : catId.oid = oid;
906 2773696 : dobj = findObjectByCatalogId(catId);
907 : Assert(dobj == NULL ||
908 : dobj->objType == DO_TYPE || dobj->objType == DO_DUMMY_TYPE);
909 2773696 : return (TypeInfo *) dobj;
910 : }
911 :
912 : /*
913 : * findFuncByOid
914 : * finds the DumpableObject for the function with the given oid
915 : * returns NULL if not found
916 : */
917 : FuncInfo *
918 548 : findFuncByOid(Oid oid)
919 : {
920 : CatalogId catId;
921 : DumpableObject *dobj;
922 :
923 548 : catId.tableoid = ProcedureRelationId;
924 548 : catId.oid = oid;
925 548 : dobj = findObjectByCatalogId(catId);
926 : Assert(dobj == NULL || dobj->objType == DO_FUNC);
927 548 : return (FuncInfo *) dobj;
928 : }
929 :
930 : /*
931 : * findOprByOid
932 : * finds the DumpableObject for the operator with the given oid
933 : * returns NULL if not found
934 : */
935 : OprInfo *
936 5648 : findOprByOid(Oid oid)
937 : {
938 : CatalogId catId;
939 : DumpableObject *dobj;
940 :
941 5648 : catId.tableoid = OperatorRelationId;
942 5648 : catId.oid = oid;
943 5648 : dobj = findObjectByCatalogId(catId);
944 : Assert(dobj == NULL || dobj->objType == DO_OPERATOR);
945 5648 : return (OprInfo *) dobj;
946 : }
947 :
948 : /*
949 : * findAccessMethodByOid
950 : * finds the DumpableObject for the access method with the given oid
951 : * returns NULL if not found
952 : */
953 : AccessMethodInfo *
954 99592 : findAccessMethodByOid(Oid oid)
955 : {
956 : CatalogId catId;
957 : DumpableObject *dobj;
958 :
959 99592 : catId.tableoid = AccessMethodRelationId;
960 99592 : catId.oid = oid;
961 99592 : dobj = findObjectByCatalogId(catId);
962 : Assert(dobj == NULL || dobj->objType == DO_ACCESS_METHOD);
963 99592 : return (AccessMethodInfo *) dobj;
964 : }
965 :
966 : /*
967 : * findCollationByOid
968 : * finds the DumpableObject for the collation with the given oid
969 : * returns NULL if not found
970 : */
971 : CollInfo *
972 544 : findCollationByOid(Oid oid)
973 : {
974 : CatalogId catId;
975 : DumpableObject *dobj;
976 :
977 544 : catId.tableoid = CollationRelationId;
978 544 : catId.oid = oid;
979 544 : dobj = findObjectByCatalogId(catId);
980 : Assert(dobj == NULL || dobj->objType == DO_COLLATION);
981 544 : return (CollInfo *) dobj;
982 : }
983 :
984 : /*
985 : * findNamespaceByOid
986 : * finds the DumpableObject for the namespace with the given oid
987 : * returns NULL if not found
988 : */
989 : NamespaceInfo *
990 1143902 : findNamespaceByOid(Oid oid)
991 : {
992 : CatalogId catId;
993 : DumpableObject *dobj;
994 :
995 1143902 : catId.tableoid = NamespaceRelationId;
996 1143902 : catId.oid = oid;
997 1143902 : dobj = findObjectByCatalogId(catId);
998 : Assert(dobj == NULL || dobj->objType == DO_NAMESPACE);
999 1143902 : return (NamespaceInfo *) dobj;
1000 : }
1001 :
1002 : /*
1003 : * findExtensionByOid
1004 : * finds the DumpableObject for the extension with the given oid
1005 : * returns NULL if not found
1006 : */
1007 : ExtensionInfo *
1008 414 : findExtensionByOid(Oid oid)
1009 : {
1010 : CatalogId catId;
1011 : DumpableObject *dobj;
1012 :
1013 414 : catId.tableoid = ExtensionRelationId;
1014 414 : catId.oid = oid;
1015 414 : dobj = findObjectByCatalogId(catId);
1016 : Assert(dobj == NULL || dobj->objType == DO_EXTENSION);
1017 414 : return (ExtensionInfo *) dobj;
1018 : }
1019 :
1020 : /*
1021 : * findPublicationByOid
1022 : * finds the DumpableObject for the publication with the given oid
1023 : * returns NULL if not found
1024 : */
1025 : PublicationInfo *
1026 1004 : findPublicationByOid(Oid oid)
1027 : {
1028 : CatalogId catId;
1029 : DumpableObject *dobj;
1030 :
1031 1004 : catId.tableoid = PublicationRelationId;
1032 1004 : catId.oid = oid;
1033 1004 : dobj = findObjectByCatalogId(catId);
1034 : Assert(dobj == NULL || dobj->objType == DO_PUBLICATION);
1035 1004 : return (PublicationInfo *) dobj;
1036 : }
1037 :
1038 : /*
1039 : * findSubscriptionByOid
1040 : * finds the DumpableObject for the subscription with the given oid
1041 : * returns NULL if not found
1042 : */
1043 : SubscriptionInfo *
1044 4 : findSubscriptionByOid(Oid oid)
1045 : {
1046 : CatalogId catId;
1047 : DumpableObject *dobj;
1048 :
1049 4 : catId.tableoid = SubscriptionRelationId;
1050 4 : catId.oid = oid;
1051 4 : dobj = findObjectByCatalogId(catId);
1052 : Assert(dobj == NULL || dobj->objType == DO_SUBSCRIPTION);
1053 4 : return (SubscriptionInfo *) dobj;
1054 : }
1055 :
1056 :
1057 : /*
1058 : * recordExtensionMembership
1059 : * Record that the object identified by the given catalog ID
1060 : * belongs to the given extension
1061 : */
1062 : void
1063 2656 : recordExtensionMembership(CatalogId catId, ExtensionInfo *ext)
1064 : {
1065 : CatalogIdMapEntry *entry;
1066 : bool found;
1067 :
1068 : /* CatalogId hash table must exist, if we have an ExtensionInfo */
1069 : Assert(catalogIdHash != NULL);
1070 :
1071 : /* Add reference to CatalogId hash */
1072 2656 : entry = catalogid_insert(catalogIdHash, catId, &found);
1073 2656 : if (!found)
1074 : {
1075 2656 : entry->dobj = NULL;
1076 2656 : entry->ext = NULL;
1077 : }
1078 : Assert(entry->ext == NULL);
1079 2656 : entry->ext = ext;
1080 2656 : }
1081 :
1082 : /*
1083 : * findOwningExtension
1084 : * return owning extension for specified catalog ID, or NULL if none
1085 : */
1086 : ExtensionInfo *
1087 1143066 : findOwningExtension(CatalogId catalogId)
1088 : {
1089 : CatalogIdMapEntry *entry;
1090 :
1091 1143066 : if (catalogIdHash == NULL)
1092 0 : return NULL; /* no objects exist yet */
1093 :
1094 1143066 : entry = catalogid_lookup(catalogIdHash, catalogId);
1095 1143066 : if (entry == NULL)
1096 0 : return NULL;
1097 1143066 : return entry->ext;
1098 : }
1099 :
1100 :
1101 : /*
1102 : * parseOidArray
1103 : * parse a string of numbers delimited by spaces into a character array
1104 : *
1105 : * Note: actually this is used for both Oids and potentially-signed
1106 : * attribute numbers. This should cause no trouble, but we could split
1107 : * the function into two functions with different argument types if it does.
1108 : */
1109 :
1110 : void
1111 13274 : parseOidArray(const char *str, Oid *array, int arraysize)
1112 : {
1113 : int j,
1114 : argNum;
1115 : char temp[100];
1116 : char s;
1117 :
1118 13274 : argNum = 0;
1119 13274 : j = 0;
1120 : for (;;)
1121 : {
1122 62474 : s = *str++;
1123 62474 : if (s == ' ' || s == '\0')
1124 : {
1125 21286 : if (j > 0)
1126 : {
1127 21286 : if (argNum >= arraysize)
1128 0 : pg_fatal("could not parse numeric array \"%s\": too many numbers", str);
1129 21286 : temp[j] = '\0';
1130 21286 : array[argNum++] = atooid(temp);
1131 21286 : j = 0;
1132 : }
1133 21286 : if (s == '\0')
1134 13274 : break;
1135 : }
1136 : else
1137 : {
1138 41188 : if (!(isdigit((unsigned char) s) || s == '-') ||
1139 41188 : j >= sizeof(temp) - 1)
1140 0 : pg_fatal("could not parse numeric array \"%s\": invalid character in number", str);
1141 41188 : temp[j++] = s;
1142 : }
1143 : }
1144 :
1145 13274 : while (argNum < arraysize)
1146 0 : array[argNum++] = InvalidOid;
1147 13274 : }
1148 :
1149 :
1150 : /*
1151 : * strInArray:
1152 : * takes in a string and a string array and the number of elements in the
1153 : * string array.
1154 : * returns the index if the string is somewhere in the array, -1 otherwise
1155 : */
1156 :
1157 : static int
1158 9246 : strInArray(const char *pattern, char **arr, int arr_size)
1159 : {
1160 : int i;
1161 :
1162 18310 : for (i = 0; i < arr_size; i++)
1163 : {
1164 17830 : if (strcmp(pattern, arr[i]) == 0)
1165 8766 : return i;
1166 : }
1167 480 : return -1;
1168 : }
|