Branch data 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 : 194 : 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 : 194 : pg_log_info("reading extensions");
113 : 194 : extinfo = getExtensions(fout, &numExtensions);
114 : :
115 : 194 : pg_log_info("identifying extension members");
116 : 194 : getExtensionMembership(fout, extinfo, numExtensions);
117 : :
118 : 194 : pg_log_info("reading schemas");
119 : 194 : 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 : 194 : pg_log_info("reading user-defined tables");
128 : 194 : tblinfo = getTables(fout, &numTables);
129 : :
130 : 193 : getOwnedSeqs(fout, tblinfo, numTables);
131 : :
132 : 193 : pg_log_info("reading user-defined functions");
133 : 193 : getFuncs(fout);
134 : :
135 : : /* this must be after getTables and getFuncs */
136 : 193 : pg_log_info("reading user-defined types");
137 : 193 : getTypes(fout);
138 : :
139 : : /* this must be after getFuncs, too */
140 : 193 : pg_log_info("reading procedural languages");
141 : 193 : getProcLangs(fout);
142 : :
143 : 193 : pg_log_info("reading user-defined aggregate functions");
144 : 193 : getAggregates(fout);
145 : :
146 : 193 : pg_log_info("reading user-defined operators");
147 : 193 : getOperators(fout);
148 : :
149 : 193 : pg_log_info("reading user-defined access methods");
150 : 193 : getAccessMethods(fout);
151 : :
152 : 193 : pg_log_info("reading user-defined operator classes");
153 : 193 : getOpclasses(fout);
154 : :
155 : 193 : pg_log_info("reading user-defined operator families");
156 : 193 : getOpfamilies(fout);
157 : :
158 : 193 : pg_log_info("reading user-defined text search parsers");
159 : 193 : getTSParsers(fout);
160 : :
161 : 193 : pg_log_info("reading user-defined text search templates");
162 : 193 : getTSTemplates(fout);
163 : :
164 : 193 : pg_log_info("reading user-defined text search dictionaries");
165 : 193 : getTSDictionaries(fout);
166 : :
167 : 193 : pg_log_info("reading user-defined text search configurations");
168 : 193 : getTSConfigurations(fout);
169 : :
170 : 193 : pg_log_info("reading user-defined foreign-data wrappers");
171 : 193 : getForeignDataWrappers(fout);
172 : :
173 : 193 : pg_log_info("reading user-defined foreign servers");
174 : 193 : getForeignServers(fout);
175 : :
176 : 193 : pg_log_info("reading default privileges");
177 : 193 : getDefaultACLs(fout);
178 : :
179 : 193 : pg_log_info("reading user-defined collations");
180 : 193 : getCollations(fout);
181 : :
182 : 193 : pg_log_info("reading user-defined conversions");
183 : 193 : getConversions(fout);
184 : :
185 : 193 : pg_log_info("reading type casts");
186 : 193 : getCasts(fout);
187 : :
188 : 193 : pg_log_info("reading transforms");
189 : 193 : getTransforms(fout);
190 : :
191 : 193 : pg_log_info("reading table inheritance information");
192 : 193 : inhinfo = getInherits(fout, &numInherits);
193 : :
194 : 193 : pg_log_info("reading event triggers");
195 : 193 : getEventTriggers(fout);
196 : :
197 : : /* Identify extension configuration tables that should be dumped */
198 : 193 : pg_log_info("finding extension tables");
199 : 193 : processExtensionTables(fout, extinfo, numExtensions);
200 : :
201 : : /* Link tables to parents, mark parents of target tables interesting */
202 : 193 : pg_log_info("finding inheritance relationships");
203 : 193 : flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits);
204 : :
205 : 193 : pg_log_info("reading column info for interesting tables");
206 : 193 : getTableAttrs(fout, tblinfo, numTables);
207 : :
208 : 193 : pg_log_info("flagging inherited columns in subtables");
209 : 193 : flagInhAttrs(fout, fout->dopt, tblinfo, numTables);
210 : :
211 : 193 : pg_log_info("reading partitioning data");
212 : 193 : getPartitioningInfo(fout);
213 : :
214 : 193 : pg_log_info("reading indexes");
215 : 193 : getIndexes(fout, tblinfo, numTables);
216 : :
217 : 193 : pg_log_info("flagging indexes in partitioned tables");
218 : 193 : flagInhIndexes(fout, tblinfo, numTables);
219 : :
220 : 193 : pg_log_info("reading extended statistics");
221 : 193 : getExtendedStatistics(fout);
222 : :
223 : 193 : pg_log_info("reading constraints");
224 : 193 : getConstraints(fout, tblinfo, numTables);
225 : :
226 : 193 : pg_log_info("reading triggers");
227 : 193 : getTriggers(fout, tblinfo, numTables);
228 : :
229 : 193 : pg_log_info("reading rewrite rules");
230 : 193 : getRules(fout);
231 : :
232 : 193 : pg_log_info("reading policies");
233 : 193 : getPolicies(fout, tblinfo, numTables);
234 : :
235 : 193 : pg_log_info("reading publications");
236 : 193 : getPublications(fout);
237 : :
238 : 193 : pg_log_info("reading publication membership of tables");
239 : 193 : getPublicationTables(fout, tblinfo, numTables);
240 : :
241 : 193 : pg_log_info("reading publication membership of schemas");
242 : 193 : getPublicationNamespaces(fout);
243 : :
244 : 193 : pg_log_info("reading subscriptions");
245 : 193 : getSubscriptions(fout);
246 : :
247 : 193 : pg_log_info("reading subscription membership of relations");
248 : 193 : getSubscriptionRelations(fout);
249 : :
250 : 193 : free(inhinfo); /* not needed any longer */
251 : :
252 : 193 : *numTablesPtr = numTables;
253 : 193 : 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 : 193 : flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables,
271 : : InhInfo *inhinfo, int numInherits)
272 : : {
273 : 193 : TableInfo *child = NULL;
274 : 193 : 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 [ + + ]: 3821 : 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 [ + + ]: 2762 : child->dobj.catId.oid != inhinfo[i].inhrelid)
296 : : {
297 : 3510 : 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 [ + + ]: 3510 : if (child == NULL)
304 : 795 : continue;
305 : : }
306 [ + + ]: 2833 : if (parent == NULL ||
307 [ + + ]: 2759 : parent->dobj.catId.oid != inhinfo[i].inhparent)
308 : : {
309 : 1718 : parent = findTableByOid(inhinfo[i].inhparent);
310 [ - + ]: 1718 : 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 [ + + ]: 2833 : if (child->numParents > 0)
318 : 118 : child->parents = pg_realloc_array(child->parents,
319 : : TableInfo *,
320 : : child->numParents + 1);
321 : : else
322 : 2715 : child->parents = pg_malloc_array(TableInfo *, 1);
323 : 2833 : child->parents[child->numParents++] = parent;
324 : : }
325 : :
326 : : /*
327 : : * Now consider all child tables and mark parents interesting as needed.
328 : : */
329 [ + + ]: 52309 : 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 [ + + ]: 52116 : if (tblinfo[i].dobj.dump)
337 : : {
338 : 33321 : int numParents = tblinfo[i].numParents;
339 : 33321 : TableInfo **parents = tblinfo[i].parents;
340 : :
341 [ + + ]: 35453 : for (j = 0; j < numParents; j++)
342 : 2132 : parents[j]->interesting = true;
343 : : }
344 : :
345 : : /* Create TableAttachInfo object if needed */
346 [ + + ]: 52116 : if ((tblinfo[i].dobj.dump & DUMP_COMPONENT_DEFINITION) &&
347 [ + + ]: 7319 : tblinfo[i].ispartition)
348 : : {
349 : : TableAttachInfo *attachinfo;
350 : :
351 : : /* With partitions there can only be one parent */
352 [ - + ]: 1457 : 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 : 1457 : attachinfo = palloc_object(TableAttachInfo);
358 : 1457 : attachinfo->dobj.objType = DO_TABLE_ATTACH;
359 : 1457 : attachinfo->dobj.catId.tableoid = 0;
360 : 1457 : attachinfo->dobj.catId.oid = 0;
361 : 1457 : AssignDumpId(&attachinfo->dobj);
362 : 1457 : attachinfo->dobj.name = pg_strdup(tblinfo[i].dobj.name);
363 : 1457 : attachinfo->dobj.namespace = tblinfo[i].dobj.namespace;
364 : 1457 : attachinfo->parentTbl = tblinfo[i].parents[0];
365 : 1457 : 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 : 1457 : addObjectDependency(&attachinfo->dobj, tblinfo[i].dobj.dumpId);
377 : 1457 : addObjectDependency(&attachinfo->dobj, tblinfo[i].parents[0]->dobj.dumpId);
378 : : }
379 : : }
380 : 193 : }
381 : :
382 : : /*
383 : : * flagInhIndexes -
384 : : * Create IndexAttachInfo objects for partitioned indexes, and add
385 : : * appropriate dependency links.
386 : : */
387 : : static void
388 : 193 : flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables)
389 : : {
390 : : int i,
391 : : j;
392 : :
393 [ + + ]: 52309 : for (i = 0; i < numTables; i++)
394 : : {
395 [ + + - + ]: 52116 : if (!tblinfo[i].ispartition || tblinfo[i].numParents == 0)
396 : 50160 : continue;
397 : :
398 : : Assert(tblinfo[i].numParents == 1);
399 : :
400 [ + + ]: 2641 : for (j = 0; j < tblinfo[i].numIndexes; j++)
401 : : {
402 : 685 : IndxInfo *index = &(tblinfo[i].indexes[j]);
403 : : IndxInfo *parentidx;
404 : : IndexAttachInfo *attachinfo;
405 : :
406 [ + + ]: 685 : if (index->parentidx == 0)
407 : 60 : continue;
408 : :
409 : 625 : parentidx = findIndexByOid(index->parentidx);
410 [ - + ]: 625 : if (parentidx == NULL)
411 : 0 : continue;
412 : :
413 : 625 : attachinfo = pg_malloc_object(IndexAttachInfo);
414 : :
415 : 625 : attachinfo->dobj.objType = DO_INDEX_ATTACH;
416 : 625 : attachinfo->dobj.catId.tableoid = 0;
417 : 625 : attachinfo->dobj.catId.oid = 0;
418 : 625 : AssignDumpId(&attachinfo->dobj);
419 : 625 : attachinfo->dobj.name = pg_strdup(index->dobj.name);
420 : 625 : attachinfo->dobj.namespace = index->indextable->dobj.namespace;
421 : 625 : attachinfo->parentIdx = parentidx;
422 : 625 : 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 : 625 : addObjectDependency(&attachinfo->dobj, index->dobj.dumpId);
440 : 625 : addObjectDependency(&attachinfo->dobj, parentidx->dobj.dumpId);
441 : 625 : addObjectDependency(&attachinfo->dobj,
442 : 625 : index->indextable->dobj.dumpId);
443 : 625 : addObjectDependency(&attachinfo->dobj,
444 : 625 : parentidx->indextable->dobj.dumpId);
445 : :
446 : : /* keep track of the list of partitions in the parent index */
447 : 625 : simple_ptr_list_append(&parentidx->partattaches, &attachinfo->dobj);
448 : : }
449 : : }
450 : 193 : }
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 : 193 : 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 [ + + ]: 52309 : for (i = 0; i < numTables; i++)
494 : : {
495 : 52116 : TableInfo *tbinfo = &(tblinfo[i]);
496 : : int numParents;
497 : : TableInfo **parents;
498 : :
499 : : /* Some kinds never have parents */
500 [ + + ]: 52116 : if (tbinfo->relkind == RELKIND_SEQUENCE ||
501 [ + + ]: 51469 : tbinfo->relkind == RELKIND_VIEW ||
502 [ + + ]: 21528 : tbinfo->relkind == RELKIND_MATVIEW)
503 : 31083 : continue;
504 : :
505 : : /* Don't bother computing anything for non-target tables, either */
506 [ + + ]: 21033 : if (!tbinfo->dobj.dump)
507 : 4146 : continue;
508 : :
509 : 16887 : numParents = tbinfo->numParents;
510 : 16887 : parents = tbinfo->parents;
511 : :
512 [ + + ]: 16887 : if (numParents == 0)
513 : 14846 : continue; /* nothing to see here, move along */
514 : :
515 : : /* For each column, search for matching column names in parent(s) */
516 [ + + ]: 7052 : for (j = 0; j < tbinfo->numatts; j++)
517 : : {
518 : : bool foundNotNull; /* Attr was NOT NULL in a parent */
519 : : bool foundDefault; /* Found a default in a parent */
520 : : bool foundSameGenerated; /* Found matching GENERATED */
521 : : bool foundDiffGenerated; /* Found non-matching GENERATED */
522 : 5011 : bool allNotNullsInvalid = true; /* is NOT NULL NOT VALID
523 : : * on all parents? */
524 : :
525 : : /* no point in examining dropped columns */
526 [ + + ]: 5011 : if (tbinfo->attisdropped[j])
527 : 305 : continue;
528 : :
529 : 4706 : foundNotNull = false;
530 : 4706 : foundDefault = false;
531 : 4706 : foundSameGenerated = false;
532 : 4706 : foundDiffGenerated = false;
533 [ + + ]: 9628 : for (k = 0; k < numParents; k++)
534 : : {
535 : 4922 : TableInfo *parent = parents[k];
536 : : int inhAttrInd;
537 : :
538 : 4922 : inhAttrInd = strInArray(tbinfo->attnames[j],
539 : : parent->attnames,
540 : : parent->numatts);
541 [ + + ]: 4922 : if (inhAttrInd >= 0)
542 : : {
543 : 4682 : AttrDefInfo *parentDef = parent->attrdefs[inhAttrInd];
544 : :
545 : : /*
546 : : * Account for each parent having a not-null constraint.
547 : : * In versions 18 and later, we don't need this (and those
548 : : * didn't have NO INHERIT.)
549 : : */
550 [ - + ]: 4682 : if (fout->remoteVersion < 180000 &&
551 [ # # ]: 0 : parent->notnull_constrs[inhAttrInd] != NULL)
552 : 0 : foundNotNull = true;
553 : :
554 : : /*
555 : : * Keep track of whether all the parents that have a
556 : : * not-null constraint on this column have it as NOT
557 : : * VALID; if they all are, arrange to have it printed for
558 : : * this column. If at least one parent has it as valid,
559 : : * there's no need.
560 : : */
561 [ + - ]: 4682 : if (fout->remoteVersion >= 180000 &&
562 [ + + ]: 4682 : parent->notnull_constrs[inhAttrInd] &&
563 [ + - ]: 963 : !parent->notnull_invalid[inhAttrInd])
564 : 963 : allNotNullsInvalid = false;
565 : :
566 : 9890 : foundDefault |= (parentDef != NULL &&
567 [ + + + + ]: 5158 : strcmp(parentDef->adef_expr, "NULL") != 0 &&
568 [ + + ]: 476 : !parent->attgenerated[inhAttrInd]);
569 [ + + ]: 4682 : if (parent->attgenerated[inhAttrInd])
570 : : {
571 : : /* these pointer nullness checks are just paranoia */
572 [ + + ]: 304 : if (parentDef != NULL &&
573 [ + - ]: 276 : tbinfo->attrdefs[j] != NULL &&
574 : 276 : strcmp(parentDef->adef_expr,
575 [ + + ]: 276 : tbinfo->attrdefs[j]->adef_expr) == 0)
576 : 246 : foundSameGenerated = true;
577 : : else
578 : 58 : foundDiffGenerated = true;
579 : : }
580 : : }
581 : : }
582 : :
583 : : /*
584 : : * In versions < 18, for lack of a better system, we arbitrarily
585 : : * decide that a not-null constraint is not locally defined if at
586 : : * least one of the parents has it.
587 : : */
588 [ - + - - ]: 4706 : if (fout->remoteVersion < 180000 && foundNotNull)
589 : 0 : tbinfo->notnull_islocal[j] = false;
590 : :
591 : : /*
592 : : * For versions >18, we must print the not-null constraint locally
593 : : * for this table even if it isn't really locally defined, but is
594 : : * valid for the child and no parent has it as valid.
595 : : */
596 [ + - + + ]: 4706 : if (fout->remoteVersion >= 180000 && allNotNullsInvalid)
597 : 3748 : tbinfo->notnull_islocal[j] = true;
598 : :
599 : : /*
600 : : * Manufacture a DEFAULT NULL clause if necessary. This breaks
601 : : * the advice given above to avoid changing state that might get
602 : : * inspected in other loop iterations. We prevent trouble by
603 : : * having the foundDefault test above check whether adef_expr is
604 : : * "NULL", so that it will reach the same conclusion before or
605 : : * after this is done.
606 : : */
607 [ + + + + ]: 4706 : if (foundDefault && tbinfo->attrdefs[j] == NULL)
608 : : {
609 : : AttrDefInfo *attrDef;
610 : :
611 : 40 : attrDef = pg_malloc_object(AttrDefInfo);
612 : 40 : attrDef->dobj.objType = DO_ATTRDEF;
613 : 40 : attrDef->dobj.catId.tableoid = 0;
614 : 40 : attrDef->dobj.catId.oid = 0;
615 : 40 : AssignDumpId(&attrDef->dobj);
616 : 40 : attrDef->dobj.name = pg_strdup(tbinfo->dobj.name);
617 : 40 : attrDef->dobj.namespace = tbinfo->dobj.namespace;
618 : 40 : attrDef->dobj.dump = tbinfo->dobj.dump;
619 : :
620 : 40 : attrDef->adtable = tbinfo;
621 : 40 : attrDef->adnum = j + 1;
622 : 40 : attrDef->adef_expr = pg_strdup("NULL");
623 : :
624 : : /* Will column be dumped explicitly? */
625 [ + - ]: 40 : if (shouldPrintColumn(dopt, tbinfo, j))
626 : : {
627 : 40 : attrDef->separate = false;
628 : : /* No dependency needed: NULL cannot have dependencies */
629 : : }
630 : : else
631 : : {
632 : : /* column will be suppressed, print default separately */
633 : 0 : attrDef->separate = true;
634 : : /* ensure it comes out after the table */
635 : 0 : addObjectDependency(&attrDef->dobj,
636 : : tbinfo->dobj.dumpId);
637 : : }
638 : :
639 : 40 : tbinfo->attrdefs[j] = attrDef;
640 : : }
641 : :
642 : : /* No need to dump generation expression if it's inheritable */
643 [ + + + - ]: 4706 : if (foundSameGenerated && !foundDiffGenerated &&
644 [ + + + + ]: 246 : !tbinfo->ispartition && !dopt->binary_upgrade)
645 : 164 : tbinfo->attrdefs[j]->dobj.dump = DUMP_COMPONENT_NONE;
646 : : }
647 : : }
648 : 193 : }
649 : :
650 : : /*
651 : : * AssignDumpId
652 : : * Given a newly-created dumpable object, assign a dump ID,
653 : : * and enter the object into the lookup tables.
654 : : *
655 : : * The caller is expected to have filled in objType and catId,
656 : : * but not any of the other standard fields of a DumpableObject.
657 : : */
658 : : void
659 : 733187 : AssignDumpId(DumpableObject *dobj)
660 : : {
661 : 733187 : dobj->dumpId = ++lastDumpId;
662 : 733187 : dobj->name = NULL; /* must be set later */
663 : 733187 : dobj->namespace = NULL; /* may be set later */
664 : 733187 : dobj->dump = DUMP_COMPONENT_ALL; /* default assumption */
665 : 733187 : dobj->dump_contains = DUMP_COMPONENT_ALL; /* default assumption */
666 : : /* All objects have definitions; we may set more components bits later */
667 : 733187 : dobj->components = DUMP_COMPONENT_DEFINITION;
668 : 733187 : dobj->ext_member = false; /* default assumption */
669 : 733187 : dobj->depends_on_ext = false; /* default assumption */
670 : 733187 : dobj->dependencies = NULL;
671 : 733187 : dobj->nDeps = 0;
672 : 733187 : dobj->allocDeps = 0;
673 : :
674 : : /* Add object to dumpIdMap[], enlarging that array if need be */
675 [ + + ]: 734167 : while (dobj->dumpId >= allocedDumpIds)
676 : : {
677 : : int newAlloc;
678 : :
679 [ + + ]: 980 : if (allocedDumpIds <= 0)
680 : : {
681 : 194 : newAlloc = 256;
682 : 194 : dumpIdMap = pg_malloc_array(DumpableObject *, newAlloc);
683 : : }
684 : : else
685 : : {
686 : 786 : newAlloc = allocedDumpIds * 2;
687 : 786 : dumpIdMap = pg_realloc_array(dumpIdMap, DumpableObject *, newAlloc);
688 : : }
689 : 980 : memset(dumpIdMap + allocedDumpIds, 0,
690 : 980 : (newAlloc - allocedDumpIds) * sizeof(DumpableObject *));
691 : 980 : allocedDumpIds = newAlloc;
692 : : }
693 : 733187 : dumpIdMap[dobj->dumpId] = dobj;
694 : :
695 : : /* If it has a valid CatalogId, enter it into the hash table */
696 [ + + ]: 733187 : if (OidIsValid(dobj->catId.tableoid))
697 : : {
698 : : CatalogIdMapEntry *entry;
699 : : bool found;
700 : :
701 : : /* Initialize CatalogId hash table if not done yet */
702 [ + + ]: 713430 : if (catalogIdHash == NULL)
703 : 194 : catalogIdHash = catalogid_create(CATALOGIDHASH_INITIAL_SIZE, NULL);
704 : :
705 : 713430 : entry = catalogid_insert(catalogIdHash, dobj->catId, &found);
706 [ + + ]: 713430 : if (!found)
707 : : {
708 : 712505 : entry->dobj = NULL;
709 : 712505 : entry->ext = NULL;
710 : : }
711 : : Assert(entry->dobj == NULL);
712 : 713430 : entry->dobj = dobj;
713 : : }
714 : 733187 : }
715 : :
716 : : /*
717 : : * recordAdditionalCatalogID
718 : : * Record an additional catalog ID for the given DumpableObject
719 : : */
720 : : void
721 : 14 : recordAdditionalCatalogID(CatalogId catId, DumpableObject *dobj)
722 : : {
723 : : CatalogIdMapEntry *entry;
724 : : bool found;
725 : :
726 : : /* CatalogId hash table must exist, if we have a DumpableObject */
727 : : Assert(catalogIdHash != NULL);
728 : :
729 : : /* Add reference to CatalogId hash */
730 : 14 : entry = catalogid_insert(catalogIdHash, catId, &found);
731 [ + - ]: 14 : if (!found)
732 : : {
733 : 14 : entry->dobj = NULL;
734 : 14 : entry->ext = NULL;
735 : : }
736 : : Assert(entry->dobj == NULL);
737 : 14 : entry->dobj = dobj;
738 : 14 : }
739 : :
740 : : /*
741 : : * Assign a DumpId that's not tied to a DumpableObject.
742 : : *
743 : : * This is used when creating a "fixed" ArchiveEntry that doesn't need to
744 : : * participate in the sorting logic.
745 : : */
746 : : DumpId
747 : 13694 : createDumpId(void)
748 : : {
749 : 13694 : return ++lastDumpId;
750 : : }
751 : :
752 : : /*
753 : : * Return the largest DumpId so far assigned
754 : : */
755 : : DumpId
756 : 1318 : getMaxDumpId(void)
757 : : {
758 : 1318 : return lastDumpId;
759 : : }
760 : :
761 : : /*
762 : : * Find a DumpableObject by dump ID
763 : : *
764 : : * Returns NULL for invalid ID
765 : : */
766 : : DumpableObject *
767 : 23285599 : findObjectByDumpId(DumpId dumpId)
768 : : {
769 [ + - - + ]: 23285599 : if (dumpId <= 0 || dumpId >= allocedDumpIds)
770 : 0 : return NULL; /* out of range? */
771 : 23285599 : return dumpIdMap[dumpId];
772 : : }
773 : :
774 : : /*
775 : : * Find a DumpableObject by catalog ID
776 : : *
777 : : * Returns NULL for unknown ID
778 : : */
779 : : DumpableObject *
780 : 3935920 : findObjectByCatalogId(CatalogId catalogId)
781 : : {
782 : : CatalogIdMapEntry *entry;
783 : :
784 [ - + ]: 3935920 : if (catalogIdHash == NULL)
785 : 0 : return NULL; /* no objects exist yet */
786 : :
787 : 3935920 : entry = catalogid_lookup(catalogIdHash, catalogId);
788 [ + + ]: 3935920 : if (entry == NULL)
789 : 704663 : return NULL;
790 : 3231257 : return entry->dobj;
791 : : }
792 : :
793 : : /*
794 : : * Build an array of pointers to all known dumpable objects
795 : : *
796 : : * This simply creates a modifiable copy of the internal map.
797 : : */
798 : : void
799 : 200 : getDumpableObjects(DumpableObject ***objs, int *numObjs)
800 : : {
801 : : int i,
802 : : j;
803 : :
804 : 200 : *objs = pg_malloc_array(DumpableObject *, allocedDumpIds);
805 : 200 : j = 0;
806 [ + + ]: 892928 : for (i = 1; i < allocedDumpIds; i++)
807 : : {
808 [ + + ]: 892728 : if (dumpIdMap[i])
809 : 759327 : (*objs)[j++] = dumpIdMap[i];
810 : : }
811 : 200 : *numObjs = j;
812 : 200 : }
813 : :
814 : : /*
815 : : * Add a dependency link to a DumpableObject
816 : : *
817 : : * Note: duplicate dependencies are currently not eliminated
818 : : */
819 : : void
820 : 1153811 : addObjectDependency(DumpableObject *dobj, DumpId refId)
821 : : {
822 [ + + ]: 1153811 : if (dobj->nDeps >= dobj->allocDeps)
823 : : {
824 [ + + ]: 197188 : if (dobj->allocDeps <= 0)
825 : : {
826 : 187814 : dobj->allocDeps = 16;
827 : 187814 : dobj->dependencies = pg_malloc_array(DumpId, dobj->allocDeps);
828 : : }
829 : : else
830 : : {
831 : 9374 : dobj->allocDeps *= 2;
832 : 9374 : dobj->dependencies = pg_realloc_array(dobj->dependencies,
833 : : DumpId, dobj->allocDeps);
834 : : }
835 : : }
836 : 1153811 : dobj->dependencies[dobj->nDeps++] = refId;
837 : 1153811 : }
838 : :
839 : : /*
840 : : * Remove a dependency link from a DumpableObject
841 : : *
842 : : * If there are multiple links, all are removed
843 : : */
844 : : void
845 : 33632 : removeObjectDependency(DumpableObject *dobj, DumpId refId)
846 : : {
847 : : int i;
848 : 33632 : int j = 0;
849 : :
850 [ + + ]: 750489 : for (i = 0; i < dobj->nDeps; i++)
851 : : {
852 [ + + ]: 716857 : if (dobj->dependencies[i] != refId)
853 : 681726 : dobj->dependencies[j++] = dobj->dependencies[i];
854 : : }
855 : 33632 : dobj->nDeps = j;
856 : 33632 : }
857 : :
858 : :
859 : : /*
860 : : * findTableByOid
861 : : * finds the DumpableObject for the table with the given oid
862 : : * returns NULL if not found
863 : : */
864 : : TableInfo *
865 : 90116 : findTableByOid(Oid oid)
866 : : {
867 : : CatalogId catId;
868 : : DumpableObject *dobj;
869 : :
870 : 90116 : catId.tableoid = RelationRelationId;
871 : 90116 : catId.oid = oid;
872 : 90116 : dobj = findObjectByCatalogId(catId);
873 : : Assert(dobj == NULL || dobj->objType == DO_TABLE);
874 : 90116 : return (TableInfo *) dobj;
875 : : }
876 : :
877 : : /*
878 : : * findIndexByOid
879 : : * finds the DumpableObject for the index with the given oid
880 : : * returns NULL if not found
881 : : */
882 : : static IndxInfo *
883 : 625 : findIndexByOid(Oid oid)
884 : : {
885 : : CatalogId catId;
886 : : DumpableObject *dobj;
887 : :
888 : 625 : catId.tableoid = RelationRelationId;
889 : 625 : catId.oid = oid;
890 : 625 : dobj = findObjectByCatalogId(catId);
891 : : Assert(dobj == NULL || dobj->objType == DO_INDEX);
892 : 625 : return (IndxInfo *) dobj;
893 : : }
894 : :
895 : : /*
896 : : * findTypeByOid
897 : : * finds the DumpableObject for the type with the given oid
898 : : * returns NULL if not found
899 : : */
900 : : TypeInfo *
901 : 1486335 : findTypeByOid(Oid oid)
902 : : {
903 : : CatalogId catId;
904 : : DumpableObject *dobj;
905 : :
906 : 1486335 : catId.tableoid = TypeRelationId;
907 : 1486335 : catId.oid = oid;
908 : 1486335 : dobj = findObjectByCatalogId(catId);
909 : : Assert(dobj == NULL ||
910 : : dobj->objType == DO_TYPE || dobj->objType == DO_DUMMY_TYPE);
911 : 1486335 : return (TypeInfo *) dobj;
912 : : }
913 : :
914 : : /*
915 : : * findFuncByOid
916 : : * finds the DumpableObject for the function with the given oid
917 : : * returns NULL if not found
918 : : */
919 : : FuncInfo *
920 : 267 : findFuncByOid(Oid oid)
921 : : {
922 : : CatalogId catId;
923 : : DumpableObject *dobj;
924 : :
925 : 267 : catId.tableoid = ProcedureRelationId;
926 : 267 : catId.oid = oid;
927 : 267 : dobj = findObjectByCatalogId(catId);
928 : : Assert(dobj == NULL || dobj->objType == DO_FUNC);
929 : 267 : return (FuncInfo *) dobj;
930 : : }
931 : :
932 : : /*
933 : : * findOprByOid
934 : : * finds the DumpableObject for the operator with the given oid
935 : : * returns NULL if not found
936 : : */
937 : : OprInfo *
938 : 2860 : findOprByOid(Oid oid)
939 : : {
940 : : CatalogId catId;
941 : : DumpableObject *dobj;
942 : :
943 : 2860 : catId.tableoid = OperatorRelationId;
944 : 2860 : catId.oid = oid;
945 : 2860 : dobj = findObjectByCatalogId(catId);
946 : : Assert(dobj == NULL || dobj->objType == DO_OPERATOR);
947 : 2860 : return (OprInfo *) dobj;
948 : : }
949 : :
950 : : /*
951 : : * findAccessMethodByOid
952 : : * finds the DumpableObject for the access method with the given oid
953 : : * returns NULL if not found
954 : : */
955 : : AccessMethodInfo *
956 : 53346 : findAccessMethodByOid(Oid oid)
957 : : {
958 : : CatalogId catId;
959 : : DumpableObject *dobj;
960 : :
961 : 53346 : catId.tableoid = AccessMethodRelationId;
962 : 53346 : catId.oid = oid;
963 : 53346 : dobj = findObjectByCatalogId(catId);
964 : : Assert(dobj == NULL || dobj->objType == DO_ACCESS_METHOD);
965 : 53346 : return (AccessMethodInfo *) dobj;
966 : : }
967 : :
968 : : /*
969 : : * findCollationByOid
970 : : * finds the DumpableObject for the collation with the given oid
971 : : * returns NULL if not found
972 : : */
973 : : CollInfo *
974 : 276 : findCollationByOid(Oid oid)
975 : : {
976 : : CatalogId catId;
977 : : DumpableObject *dobj;
978 : :
979 : 276 : catId.tableoid = CollationRelationId;
980 : 276 : catId.oid = oid;
981 : 276 : dobj = findObjectByCatalogId(catId);
982 : : Assert(dobj == NULL || dobj->objType == DO_COLLATION);
983 : 276 : return (CollInfo *) dobj;
984 : : }
985 : :
986 : : /*
987 : : * findNamespaceByOid
988 : : * finds the DumpableObject for the namespace with the given oid
989 : : * returns NULL if not found
990 : : */
991 : : NamespaceInfo *
992 : 622775 : findNamespaceByOid(Oid oid)
993 : : {
994 : : CatalogId catId;
995 : : DumpableObject *dobj;
996 : :
997 : 622775 : catId.tableoid = NamespaceRelationId;
998 : 622775 : catId.oid = oid;
999 : 622775 : dobj = findObjectByCatalogId(catId);
1000 : : Assert(dobj == NULL || dobj->objType == DO_NAMESPACE);
1001 : 622775 : return (NamespaceInfo *) dobj;
1002 : : }
1003 : :
1004 : : /*
1005 : : * findExtensionByOid
1006 : : * finds the DumpableObject for the extension with the given oid
1007 : : * returns NULL if not found
1008 : : */
1009 : : ExtensionInfo *
1010 : 225 : findExtensionByOid(Oid oid)
1011 : : {
1012 : : CatalogId catId;
1013 : : DumpableObject *dobj;
1014 : :
1015 : 225 : catId.tableoid = ExtensionRelationId;
1016 : 225 : catId.oid = oid;
1017 : 225 : dobj = findObjectByCatalogId(catId);
1018 : : Assert(dobj == NULL || dobj->objType == DO_EXTENSION);
1019 : 225 : return (ExtensionInfo *) dobj;
1020 : : }
1021 : :
1022 : : /*
1023 : : * findPublicationByOid
1024 : : * finds the DumpableObject for the publication with the given oid
1025 : : * returns NULL if not found
1026 : : */
1027 : : PublicationInfo *
1028 : 502 : findPublicationByOid(Oid oid)
1029 : : {
1030 : : CatalogId catId;
1031 : : DumpableObject *dobj;
1032 : :
1033 : 502 : catId.tableoid = PublicationRelationId;
1034 : 502 : catId.oid = oid;
1035 : 502 : dobj = findObjectByCatalogId(catId);
1036 : : Assert(dobj == NULL || dobj->objType == DO_PUBLICATION);
1037 : 502 : return (PublicationInfo *) dobj;
1038 : : }
1039 : :
1040 : : /*
1041 : : * findSubscriptionByOid
1042 : : * finds the DumpableObject for the subscription with the given oid
1043 : : * returns NULL if not found
1044 : : */
1045 : : SubscriptionInfo *
1046 : 2 : findSubscriptionByOid(Oid oid)
1047 : : {
1048 : : CatalogId catId;
1049 : : DumpableObject *dobj;
1050 : :
1051 : 2 : catId.tableoid = SubscriptionRelationId;
1052 : 2 : catId.oid = oid;
1053 : 2 : dobj = findObjectByCatalogId(catId);
1054 : : Assert(dobj == NULL || dobj->objType == DO_SUBSCRIPTION);
1055 : 2 : return (SubscriptionInfo *) dobj;
1056 : : }
1057 : :
1058 : :
1059 : : /*
1060 : : * recordExtensionMembership
1061 : : * Record that the object identified by the given catalog ID
1062 : : * belongs to the given extension
1063 : : */
1064 : : void
1065 : 1382 : recordExtensionMembership(CatalogId catId, ExtensionInfo *ext)
1066 : : {
1067 : : CatalogIdMapEntry *entry;
1068 : : bool found;
1069 : :
1070 : : /* CatalogId hash table must exist, if we have an ExtensionInfo */
1071 : : Assert(catalogIdHash != NULL);
1072 : :
1073 : : /* Add reference to CatalogId hash */
1074 : 1382 : entry = catalogid_insert(catalogIdHash, catId, &found);
1075 [ + - ]: 1382 : if (!found)
1076 : : {
1077 : 1382 : entry->dobj = NULL;
1078 : 1382 : entry->ext = NULL;
1079 : : }
1080 : : Assert(entry->ext == NULL);
1081 : 1382 : entry->ext = ext;
1082 : 1382 : }
1083 : :
1084 : : /*
1085 : : * findOwningExtension
1086 : : * return owning extension for specified catalog ID, or NULL if none
1087 : : */
1088 : : ExtensionInfo *
1089 : 622881 : findOwningExtension(CatalogId catalogId)
1090 : : {
1091 : : CatalogIdMapEntry *entry;
1092 : :
1093 [ - + ]: 622881 : if (catalogIdHash == NULL)
1094 : 0 : return NULL; /* no objects exist yet */
1095 : :
1096 : 622881 : entry = catalogid_lookup(catalogIdHash, catalogId);
1097 [ - + ]: 622881 : if (entry == NULL)
1098 : 0 : return NULL;
1099 : 622881 : return entry->ext;
1100 : : }
1101 : :
1102 : :
1103 : : /*
1104 : : * parseOidArray
1105 : : * parse a string of unsigned numbers separated by spaces
1106 : : * into an array of OIDs
1107 : : *
1108 : : * The result is a malloc'd array.
1109 : : *
1110 : : * If arraysize >= 0, we insist that the input contain exactly that many
1111 : : * OIDs, and the allocated array is of that length too. If arraysize < 0,
1112 : : * we dynamically size the array to have one more entry than the input
1113 : : * provides, and fill the extra entry with zero.
1114 : : */
1115 : : Oid *
1116 : 4202 : parseOidArray(const char *str, int arraysize)
1117 : : {
1118 : : Oid *array;
1119 : : int allocsize,
1120 : : argNum,
1121 : : templen;
1122 : : char temp[32];
1123 : :
1124 [ + + ]: 4202 : if (arraysize >= 0)
1125 : 4197 : allocsize = arraysize;
1126 : : else
1127 : : {
1128 : : /*
1129 : : * Make enough room for input + one extra entry (could be more than
1130 : : * enough, if there are redundant spaces in the input).
1131 : : */
1132 : 5 : allocsize = 2;
1133 [ + + ]: 15 : for (const char *s1 = str; *s1; s1++)
1134 : : {
1135 [ - + ]: 10 : if (*s1 == ' ')
1136 : 0 : allocsize++;
1137 : : }
1138 : : }
1139 : 4202 : array = pg_malloc_array(Oid, allocsize);
1140 : 4202 : argNum = 0;
1141 : 4202 : templen = 0;
1142 : 4202 : for (const char *s1 = str;; s1++)
1143 : 19560 : {
1144 : 23762 : char s = *s1;
1145 : :
1146 [ + + + + ]: 23762 : if (s == ' ' || s == '\0')
1147 : : {
1148 [ + - ]: 6764 : if (templen > 0)
1149 : : {
1150 [ + + - + ]: 6764 : if (arraysize >= 0 && argNum >= arraysize)
1151 : 0 : pg_fatal("could not parse numeric array \"%s\": too many numbers", str);
1152 : 6764 : temp[templen] = '\0';
1153 : 6764 : array[argNum++] = atooid(temp);
1154 : 6764 : templen = 0;
1155 : : }
1156 [ + + ]: 6764 : if (s == '\0')
1157 : 4202 : break;
1158 : : }
1159 : : else
1160 : : {
1161 [ + - ]: 16998 : if (!isdigit((unsigned char) s) ||
1162 [ - + ]: 16998 : templen >= sizeof(temp) - 1)
1163 : 0 : pg_fatal("could not parse numeric array \"%s\": invalid character in number", str);
1164 : 16998 : temp[templen++] = s;
1165 : : }
1166 : : }
1167 : :
1168 [ + + - + ]: 4202 : if (arraysize >= 0 && argNum != arraysize)
1169 : 0 : pg_fatal("could not parse numeric array \"%s\": too few numbers", str);
1170 : :
1171 [ + + ]: 4207 : while (argNum < allocsize)
1172 : 5 : array[argNum++] = InvalidOid;
1173 : :
1174 : 4202 : return array;
1175 : : }
1176 : :
1177 : :
1178 : : /*
1179 : : * parseIntArray
1180 : : * parse a string of possibly-signed numbers separated by spaces
1181 : : * into an array of ints
1182 : : *
1183 : : * This is exactly like parseOidArray, but for integers.
1184 : : */
1185 : : int *
1186 : 2743 : parseIntArray(const char *str, int arraysize)
1187 : : {
1188 : : int *array;
1189 : : int allocsize,
1190 : : argNum,
1191 : : templen;
1192 : : char temp[32];
1193 : :
1194 [ + - ]: 2743 : if (arraysize >= 0)
1195 : 2743 : allocsize = arraysize;
1196 : : else
1197 : : {
1198 : : /*
1199 : : * Make enough room for input + one extra entry (could be more than
1200 : : * enough, if there are redundant spaces in the input).
1201 : : */
1202 : 0 : allocsize = 2;
1203 [ # # ]: 0 : for (const char *s1 = str; *s1; s1++)
1204 : : {
1205 [ # # ]: 0 : if (*s1 == ' ')
1206 : 0 : allocsize++;
1207 : : }
1208 : : }
1209 : 2743 : array = pg_malloc_array(int, allocsize);
1210 : 2743 : argNum = 0;
1211 : 2743 : templen = 0;
1212 : 2743 : for (const char *s1 = str;; s1++)
1213 : 6269 : {
1214 : 9012 : char s = *s1;
1215 : :
1216 [ + + + + ]: 9012 : if (s == ' ' || s == '\0')
1217 : : {
1218 [ + - ]: 4376 : if (templen > 0)
1219 : : {
1220 [ + - - + ]: 4376 : if (arraysize >= 0 && argNum >= arraysize)
1221 : 0 : pg_fatal("could not parse numeric array \"%s\": too many numbers", str);
1222 : 4376 : temp[templen] = '\0';
1223 : 4376 : array[argNum++] = atoi(temp);
1224 : 4376 : templen = 0;
1225 : : }
1226 [ + + ]: 4376 : if (s == '\0')
1227 : 2743 : break;
1228 : : }
1229 : : else
1230 : : {
1231 [ - + - - ]: 4636 : if (!(isdigit((unsigned char) s) || s == '-') ||
1232 [ - + ]: 4636 : templen >= sizeof(temp) - 1)
1233 : 0 : pg_fatal("could not parse numeric array \"%s\": invalid character in number", str);
1234 : 4636 : temp[templen++] = s;
1235 : : }
1236 : : }
1237 : :
1238 [ + - - + ]: 2743 : if (arraysize >= 0 && argNum != arraysize)
1239 : 0 : pg_fatal("could not parse numeric array \"%s\": too few numbers", str);
1240 : :
1241 [ - + ]: 2743 : while (argNum < allocsize)
1242 : 0 : array[argNum++] = 0;
1243 : :
1244 : 2743 : return array;
1245 : : }
1246 : :
1247 : :
1248 : : /*
1249 : : * strInArray:
1250 : : * takes in a string and a string array and the number of elements in the
1251 : : * string array.
1252 : : * returns the index if the string is somewhere in the array, -1 otherwise
1253 : : */
1254 : :
1255 : : static int
1256 : 4922 : strInArray(const char *pattern, char **arr, int arr_size)
1257 : : {
1258 : : int i;
1259 : :
1260 [ + + ]: 9626 : for (i = 0; i < arr_size; i++)
1261 : : {
1262 [ + + ]: 9386 : if (strcmp(pattern, arr[i]) == 0)
1263 : 4682 : return i;
1264 : : }
1265 : 240 : return -1;
1266 : : }
|