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