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