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