/Users/deen/code/yugabyte-db/src/postgres/src/bin/pg_dump/common.c
Line | Count | Source (jump to first uncovered line) |
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-2018, 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 "pg_backup_archiver.h" |
19 | | #include "pg_backup_utils.h" |
20 | | #include "pg_dump.h" |
21 | | |
22 | | #include <ctype.h> |
23 | | |
24 | | #include "catalog/pg_class_d.h" |
25 | | #include "fe_utils/string_utils.h" |
26 | | |
27 | | |
28 | | /* |
29 | | * Variables for mapping DumpId to DumpableObject |
30 | | */ |
31 | | static DumpableObject **dumpIdMap = NULL; |
32 | | static int allocedDumpIds = 0; |
33 | | static DumpId lastDumpId = 0; |
34 | | |
35 | | /* |
36 | | * Variables for mapping CatalogId to DumpableObject |
37 | | */ |
38 | | static bool catalogIdMapValid = false; |
39 | | static DumpableObject **catalogIdMap = NULL; |
40 | | static int numCatalogIds = 0; |
41 | | |
42 | | /* |
43 | | * These variables are static to avoid the notational cruft of having to pass |
44 | | * them into findTableByOid() and friends. For each of these arrays, we build |
45 | | * a sorted-by-OID index array immediately after the objects are fetched, |
46 | | * and then we use binary search in findTableByOid() and friends. (qsort'ing |
47 | | * the object arrays themselves would be simpler, but it doesn't work because |
48 | | * pg_dump.c may have already established pointers between items.) |
49 | | */ |
50 | | static DumpableObject **tblinfoindex; |
51 | | static DumpableObject **typinfoindex; |
52 | | static DumpableObject **funinfoindex; |
53 | | static DumpableObject **oprinfoindex; |
54 | | static DumpableObject **collinfoindex; |
55 | | static DumpableObject **nspinfoindex; |
56 | | static DumpableObject **extinfoindex; |
57 | | static DumpableObject **tblgrpinfoindex; |
58 | | static int numTables; |
59 | | static int numTablegroups; |
60 | | static int numTypes; |
61 | | static int numFuncs; |
62 | | static int numOperators; |
63 | | static int numCollations; |
64 | | static int numNamespaces; |
65 | | static int numExtensions; |
66 | | |
67 | | /* This is an array of object identities, not actual DumpableObjects */ |
68 | | static ExtensionMemberId *extmembers; |
69 | | static int numextmembers; |
70 | | |
71 | | static void flagInhTables(Archive *fout, TableInfo *tbinfo, int numTables, |
72 | | InhInfo *inhinfo, int numInherits); |
73 | | static void flagInhIndexes(Archive *fout, TableInfo *tblinfo, int numTables); |
74 | | static void flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables); |
75 | | static DumpableObject **buildIndexArray(void *objArray, int numObjs, |
76 | | Size objSize); |
77 | | static int DOCatalogIdCompare(const void *p1, const void *p2); |
78 | | static int ExtensionMemberIdCompare(const void *p1, const void *p2); |
79 | | static void findParentsByOid(TableInfo *self, |
80 | | InhInfo *inhinfo, int numInherits); |
81 | | static int strInArray(const char *pattern, char **arr, int arr_size); |
82 | | static IndxInfo *findIndexByOid(Oid oid, DumpableObject **idxinfoindex, |
83 | | int numIndexes); |
84 | | |
85 | | |
86 | | /* |
87 | | * getSchemaData |
88 | | * Collect information about all potentially dumpable objects |
89 | | */ |
90 | | TableInfo * |
91 | | getSchemaData(Archive *fout, int *numTablesPtr) |
92 | 0 | { |
93 | 0 | TableInfo *tblinfo; |
94 | 0 | TypeInfo *typinfo; |
95 | 0 | FuncInfo *funinfo; |
96 | 0 | OprInfo *oprinfo; |
97 | 0 | CollInfo *collinfo; |
98 | 0 | NamespaceInfo *nspinfo; |
99 | 0 | ExtensionInfo *extinfo; |
100 | 0 | InhInfo *inhinfo; |
101 | 0 | TablegroupInfo *tblgrpinfo; |
102 | 0 | int numAggregates; |
103 | 0 | int numInherits; |
104 | 0 | int numRules; |
105 | 0 | int numProcLangs; |
106 | 0 | int numCasts; |
107 | 0 | int numTransforms; |
108 | 0 | int numAccessMethods; |
109 | 0 | int numOpclasses; |
110 | 0 | int numOpfamilies; |
111 | 0 | int numConversions; |
112 | 0 | int numTSParsers; |
113 | 0 | int numTSTemplates; |
114 | 0 | int numTSDicts; |
115 | 0 | int numTSConfigs; |
116 | 0 | int numForeignDataWrappers; |
117 | 0 | int numForeignServers; |
118 | 0 | int numDefaultACLs; |
119 | 0 | int numEventTriggers; |
120 | | |
121 | | /* |
122 | | * We must read extensions and extension membership info first, because |
123 | | * extension membership needs to be consultable during decisions about |
124 | | * whether other objects are to be dumped. |
125 | | */ |
126 | 0 | if (g_verbose) |
127 | 0 | write_msg(NULL, "reading extensions\n"); |
128 | 0 | extinfo = getExtensions(fout, &numExtensions); |
129 | 0 | extinfoindex = buildIndexArray(extinfo, numExtensions, sizeof(ExtensionInfo)); |
130 | |
|
131 | 0 | if (g_verbose) |
132 | 0 | write_msg(NULL, "identifying extension members\n"); |
133 | 0 | getExtensionMembership(fout, extinfo, numExtensions); |
134 | |
|
135 | 0 | if (g_verbose) |
136 | 0 | write_msg(NULL, "reading schemas\n"); |
137 | 0 | nspinfo = getNamespaces(fout, &numNamespaces); |
138 | 0 | nspinfoindex = buildIndexArray(nspinfo, numNamespaces, sizeof(NamespaceInfo)); |
139 | | |
140 | | /* |
141 | | * getTables should be done as soon as possible, so as to minimize the |
142 | | * window between starting our transaction and acquiring per-table locks. |
143 | | * However, we have to do getNamespaces first because the tables get |
144 | | * linked to their containing namespaces during getTables. |
145 | | */ |
146 | 0 | if (g_verbose) |
147 | 0 | write_msg(NULL, "reading user-defined tables\n"); |
148 | 0 | tblinfo = getTables(fout, &numTables); |
149 | 0 | tblinfoindex = buildIndexArray(tblinfo, numTables, sizeof(TableInfo)); |
150 | | |
151 | | /* Do this after we've built tblinfoindex */ |
152 | 0 | getOwnedSeqs(fout, tblinfo, numTables); |
153 | |
|
154 | 0 | if (g_verbose) |
155 | 0 | write_msg(NULL, "reading user-defined functions\n"); |
156 | 0 | funinfo = getFuncs(fout, &numFuncs); |
157 | 0 | funinfoindex = buildIndexArray(funinfo, numFuncs, sizeof(FuncInfo)); |
158 | | |
159 | | /* this must be after getTables and getFuncs */ |
160 | 0 | if (g_verbose) |
161 | 0 | write_msg(NULL, "reading user-defined types\n"); |
162 | 0 | typinfo = getTypes(fout, &numTypes); |
163 | 0 | typinfoindex = buildIndexArray(typinfo, numTypes, sizeof(TypeInfo)); |
164 | | |
165 | | /* this must be after getFuncs, too */ |
166 | 0 | if (g_verbose) |
167 | 0 | write_msg(NULL, "reading procedural languages\n"); |
168 | 0 | getProcLangs(fout, &numProcLangs); |
169 | |
|
170 | 0 | if (g_verbose) |
171 | 0 | write_msg(NULL, "reading user-defined aggregate functions\n"); |
172 | 0 | getAggregates(fout, &numAggregates); |
173 | |
|
174 | 0 | if (g_verbose) |
175 | 0 | write_msg(NULL, "reading user-defined operators\n"); |
176 | 0 | oprinfo = getOperators(fout, &numOperators); |
177 | 0 | oprinfoindex = buildIndexArray(oprinfo, numOperators, sizeof(OprInfo)); |
178 | |
|
179 | 0 | if (g_verbose) |
180 | 0 | write_msg(NULL, "reading user-defined access methods\n"); |
181 | 0 | getAccessMethods(fout, &numAccessMethods); |
182 | |
|
183 | 0 | if (g_verbose) |
184 | 0 | write_msg(NULL, "reading user-defined tablegroups\n"); |
185 | 0 | tblgrpinfo = getTablegroups(fout, &numTablegroups); |
186 | 0 | tblgrpinfoindex = buildIndexArray(tblgrpinfo, numTablegroups, sizeof(TablegroupInfo)); |
187 | |
|
188 | 0 | if (g_verbose) |
189 | 0 | write_msg(NULL, "reading user-defined operator classes\n"); |
190 | 0 | getOpclasses(fout, &numOpclasses); |
191 | |
|
192 | 0 | if (g_verbose) |
193 | 0 | write_msg(NULL, "reading user-defined operator families\n"); |
194 | 0 | getOpfamilies(fout, &numOpfamilies); |
195 | |
|
196 | 0 | if (g_verbose) |
197 | 0 | write_msg(NULL, "reading user-defined text search parsers\n"); |
198 | 0 | getTSParsers(fout, &numTSParsers); |
199 | |
|
200 | 0 | if (g_verbose) |
201 | 0 | write_msg(NULL, "reading user-defined text search templates\n"); |
202 | 0 | getTSTemplates(fout, &numTSTemplates); |
203 | |
|
204 | 0 | if (g_verbose) |
205 | 0 | write_msg(NULL, "reading user-defined text search dictionaries\n"); |
206 | 0 | getTSDictionaries(fout, &numTSDicts); |
207 | |
|
208 | 0 | if (g_verbose) |
209 | 0 | write_msg(NULL, "reading user-defined text search configurations\n"); |
210 | 0 | getTSConfigurations(fout, &numTSConfigs); |
211 | |
|
212 | 0 | if (g_verbose) |
213 | 0 | write_msg(NULL, "reading user-defined foreign-data wrappers\n"); |
214 | 0 | getForeignDataWrappers(fout, &numForeignDataWrappers); |
215 | |
|
216 | 0 | if (g_verbose) |
217 | 0 | write_msg(NULL, "reading user-defined foreign servers\n"); |
218 | 0 | getForeignServers(fout, &numForeignServers); |
219 | |
|
220 | 0 | if (g_verbose) |
221 | 0 | write_msg(NULL, "reading default privileges\n"); |
222 | 0 | getDefaultACLs(fout, &numDefaultACLs); |
223 | |
|
224 | 0 | if (g_verbose) |
225 | 0 | write_msg(NULL, "reading user-defined collations\n"); |
226 | 0 | collinfo = getCollations(fout, &numCollations); |
227 | 0 | collinfoindex = buildIndexArray(collinfo, numCollations, sizeof(CollInfo)); |
228 | |
|
229 | 0 | if (g_verbose) |
230 | 0 | write_msg(NULL, "reading user-defined conversions\n"); |
231 | 0 | getConversions(fout, &numConversions); |
232 | |
|
233 | 0 | if (g_verbose) |
234 | 0 | write_msg(NULL, "reading type casts\n"); |
235 | 0 | getCasts(fout, &numCasts); |
236 | |
|
237 | 0 | if (g_verbose) |
238 | 0 | write_msg(NULL, "reading transforms\n"); |
239 | 0 | getTransforms(fout, &numTransforms); |
240 | |
|
241 | 0 | if (g_verbose) |
242 | 0 | write_msg(NULL, "reading table inheritance information\n"); |
243 | 0 | inhinfo = getInherits(fout, &numInherits); |
244 | |
|
245 | 0 | if (g_verbose) |
246 | 0 | write_msg(NULL, "reading event triggers\n"); |
247 | 0 | getEventTriggers(fout, &numEventTriggers); |
248 | | |
249 | | /* Identify extension configuration tables that should be dumped */ |
250 | 0 | if (g_verbose) |
251 | 0 | write_msg(NULL, "finding extension tables\n"); |
252 | 0 | processExtensionTables(fout, extinfo, numExtensions); |
253 | | |
254 | | /* Link tables to parents, mark parents of target tables interesting */ |
255 | 0 | if (g_verbose) |
256 | 0 | write_msg(NULL, "finding inheritance relationships\n"); |
257 | 0 | flagInhTables(fout, tblinfo, numTables, inhinfo, numInherits); |
258 | |
|
259 | 0 | if (g_verbose) |
260 | 0 | write_msg(NULL, "reading column info for interesting tables\n"); |
261 | 0 | getTableAttrs(fout, tblinfo, numTables); |
262 | |
|
263 | 0 | if (g_verbose) |
264 | 0 | write_msg(NULL, "flagging inherited columns in subtables\n"); |
265 | 0 | flagInhAttrs(fout->dopt, tblinfo, numTables); |
266 | |
|
267 | 0 | if (g_verbose) |
268 | 0 | write_msg(NULL, "reading indexes\n"); |
269 | 0 | getIndexes(fout, tblinfo, numTables); |
270 | |
|
271 | 0 | if (g_verbose) |
272 | 0 | write_msg(NULL, "flagging indexes in partitioned tables\n"); |
273 | 0 | flagInhIndexes(fout, tblinfo, numTables); |
274 | |
|
275 | 0 | if (g_verbose) |
276 | 0 | write_msg(NULL, "reading extended statistics\n"); |
277 | 0 | getExtendedStatistics(fout); |
278 | |
|
279 | 0 | if (g_verbose) |
280 | 0 | write_msg(NULL, "reading constraints\n"); |
281 | 0 | getConstraints(fout, tblinfo, numTables); |
282 | |
|
283 | 0 | if (g_verbose) |
284 | 0 | write_msg(NULL, "reading triggers\n"); |
285 | 0 | getTriggers(fout, tblinfo, numTables); |
286 | |
|
287 | 0 | if (g_verbose) |
288 | 0 | write_msg(NULL, "reading rewrite rules\n"); |
289 | 0 | getRules(fout, &numRules); |
290 | |
|
291 | 0 | if (g_verbose) |
292 | 0 | write_msg(NULL, "reading policies\n"); |
293 | 0 | getPolicies(fout, tblinfo, numTables); |
294 | |
|
295 | 0 | if (g_verbose) |
296 | 0 | write_msg(NULL, "reading publications\n"); |
297 | 0 | getPublications(fout); |
298 | |
|
299 | 0 | if (g_verbose) |
300 | 0 | write_msg(NULL, "reading publication membership\n"); |
301 | 0 | getPublicationTables(fout, tblinfo, numTables); |
302 | |
|
303 | 0 | if (g_verbose) |
304 | 0 | write_msg(NULL, "reading subscriptions\n"); |
305 | 0 | getSubscriptions(fout); |
306 | |
|
307 | 0 | *numTablesPtr = numTables; |
308 | 0 | return tblinfo; |
309 | 0 | } |
310 | | |
311 | | /* flagInhTables - |
312 | | * Fill in parent link fields of tables for which we need that information, |
313 | | * and mark parents of target tables as interesting |
314 | | * |
315 | | * Note that only direct ancestors of targets are marked interesting. |
316 | | * This is sufficient; we don't much care whether they inherited their |
317 | | * attributes or not. |
318 | | * |
319 | | * modifies tblinfo |
320 | | */ |
321 | | static void |
322 | | flagInhTables(Archive *fout, TableInfo *tblinfo, int numTables, |
323 | | InhInfo *inhinfo, int numInherits) |
324 | 0 | { |
325 | 0 | DumpOptions *dopt = fout->dopt; |
326 | 0 | int i, |
327 | 0 | j; |
328 | |
|
329 | 0 | for (i = 0; i < numTables; i++) |
330 | 0 | { |
331 | 0 | bool find_parents = true; |
332 | 0 | bool mark_parents = true; |
333 | | |
334 | | /* Some kinds never have parents */ |
335 | 0 | if (tblinfo[i].relkind == RELKIND_SEQUENCE || |
336 | 0 | tblinfo[i].relkind == RELKIND_VIEW || |
337 | 0 | tblinfo[i].relkind == RELKIND_MATVIEW) |
338 | 0 | continue; |
339 | | |
340 | | /* |
341 | | * Normally, we don't bother computing anything for non-target tables, |
342 | | * but if load-via-partition-root is specified, we gather information |
343 | | * on every partition in the system so that getRootTableInfo can trace |
344 | | * from any given to leaf partition all the way up to the root. (We |
345 | | * don't need to mark them as interesting for getTableAttrs, though.) |
346 | | */ |
347 | 0 | if (!tblinfo[i].dobj.dump) |
348 | 0 | { |
349 | 0 | mark_parents = false; |
350 | |
|
351 | 0 | if (!dopt->load_via_partition_root || |
352 | 0 | !tblinfo[i].ispartition) |
353 | 0 | find_parents = false; |
354 | 0 | } |
355 | | |
356 | | /* If needed, find all the immediate parent tables. */ |
357 | 0 | if (find_parents) |
358 | 0 | findParentsByOid(&tblinfo[i], inhinfo, numInherits); |
359 | | |
360 | | /* |
361 | | * If needed, mark the parents as interesting for getTableAttrs and |
362 | | * getIndexes. |
363 | | */ |
364 | 0 | if (mark_parents) |
365 | 0 | { |
366 | 0 | int numParents = tblinfo[i].numParents; |
367 | 0 | TableInfo **parents = tblinfo[i].parents; |
368 | |
|
369 | 0 | for (j = 0; j < numParents; j++) |
370 | 0 | parents[j]->interesting = true; |
371 | 0 | } |
372 | 0 | } |
373 | 0 | } |
374 | | |
375 | | /* |
376 | | * flagInhIndexes - |
377 | | * Create AttachIndexInfo objects for partitioned indexes, and add |
378 | | * appropriate dependency links. |
379 | | */ |
380 | | static void |
381 | | flagInhIndexes(Archive *fout, TableInfo tblinfo[], int numTables) |
382 | 0 | { |
383 | 0 | int i, |
384 | 0 | j, |
385 | 0 | k; |
386 | 0 | DumpableObject ***parentIndexArray; |
387 | |
|
388 | 0 | parentIndexArray = (DumpableObject ***) |
389 | 0 | pg_malloc0(getMaxDumpId() * sizeof(DumpableObject **)); |
390 | |
|
391 | 0 | for (i = 0; i < numTables; i++) |
392 | 0 | { |
393 | 0 | TableInfo *parenttbl; |
394 | 0 | IndexAttachInfo *attachinfo; |
395 | |
|
396 | 0 | if (!tblinfo[i].ispartition || tblinfo[i].numParents == 0) |
397 | 0 | continue; |
398 | | |
399 | 0 | Assert(tblinfo[i].numParents == 1); |
400 | 0 | parenttbl = tblinfo[i].parents[0]; |
401 | | |
402 | | /* |
403 | | * We need access to each parent table's index list, but there is no |
404 | | * index to cover them outside of this function. To avoid having to |
405 | | * sort every parent table's indexes each time we come across each of |
406 | | * its partitions, create an indexed array for each parent the first |
407 | | * time it is required. |
408 | | */ |
409 | 0 | if (parentIndexArray[parenttbl->dobj.dumpId] == NULL) |
410 | 0 | parentIndexArray[parenttbl->dobj.dumpId] = |
411 | 0 | buildIndexArray(parenttbl->indexes, |
412 | 0 | parenttbl->numIndexes, |
413 | 0 | sizeof(IndxInfo)); |
414 | |
|
415 | 0 | attachinfo = (IndexAttachInfo *) |
416 | 0 | pg_malloc0(tblinfo[i].numIndexes * sizeof(IndexAttachInfo)); |
417 | 0 | for (j = 0, k = 0; j < tblinfo[i].numIndexes; j++) |
418 | 0 | { |
419 | 0 | IndxInfo *index = &(tblinfo[i].indexes[j]); |
420 | 0 | IndxInfo *parentidx; |
421 | |
|
422 | 0 | if (index->parentidx == 0) |
423 | 0 | continue; |
424 | | |
425 | 0 | parentidx = findIndexByOid(index->parentidx, |
426 | 0 | parentIndexArray[parenttbl->dobj.dumpId], |
427 | 0 | parenttbl->numIndexes); |
428 | 0 | if (parentidx == NULL) |
429 | 0 | continue; |
430 | | |
431 | 0 | attachinfo[k].dobj.objType = DO_INDEX_ATTACH; |
432 | 0 | attachinfo[k].dobj.catId.tableoid = 0; |
433 | 0 | attachinfo[k].dobj.catId.oid = 0; |
434 | 0 | AssignDumpId(&attachinfo[k].dobj); |
435 | 0 | attachinfo[k].dobj.name = pg_strdup(index->dobj.name); |
436 | 0 | attachinfo[k].dobj.namespace = index->indextable->dobj.namespace; |
437 | 0 | attachinfo[k].parentIdx = parentidx; |
438 | 0 | attachinfo[k].partitionIdx = index; |
439 | | |
440 | | /* |
441 | | * We must state the DO_INDEX_ATTACH object's dependencies |
442 | | * explicitly, since it will not match anything in pg_depend. |
443 | | * |
444 | | * Give it dependencies on both the partition index and the parent |
445 | | * index, so that it will not be executed till both of those |
446 | | * exist. (There's no need to care what order those are created |
447 | | * in.) |
448 | | * |
449 | | * In addition, give it dependencies on the indexes' underlying |
450 | | * tables. This does nothing of great value so far as serial |
451 | | * restore ordering goes, but it ensures that a parallel restore |
452 | | * will not try to run the ATTACH concurrently with other |
453 | | * operations on those tables. |
454 | | */ |
455 | 0 | addObjectDependency(&attachinfo[k].dobj, index->dobj.dumpId); |
456 | 0 | addObjectDependency(&attachinfo[k].dobj, parentidx->dobj.dumpId); |
457 | 0 | addObjectDependency(&attachinfo[k].dobj, |
458 | 0 | index->indextable->dobj.dumpId); |
459 | 0 | addObjectDependency(&attachinfo[k].dobj, |
460 | 0 | parentidx->indextable->dobj.dumpId); |
461 | |
|
462 | 0 | k++; |
463 | 0 | } |
464 | 0 | } |
465 | |
|
466 | 0 | for (i = 0; i < numTables; i++) |
467 | 0 | if (parentIndexArray[i]) |
468 | 0 | pg_free(parentIndexArray[i]); |
469 | 0 | pg_free(parentIndexArray); |
470 | 0 | } |
471 | | |
472 | | /* flagInhAttrs - |
473 | | * for each dumpable table in tblinfo, flag its inherited attributes |
474 | | * |
475 | | * What we need to do here is detect child columns that inherit NOT NULL |
476 | | * bits from their parents (so that we needn't specify that again for the |
477 | | * child) and child columns that have DEFAULT NULL when their parents had |
478 | | * some non-null default. In the latter case, we make up a dummy AttrDefInfo |
479 | | * object so that we'll correctly emit the necessary DEFAULT NULL clause; |
480 | | * otherwise the backend will apply an inherited default to the column. |
481 | | * |
482 | | * modifies tblinfo |
483 | | */ |
484 | | static void |
485 | | flagInhAttrs(DumpOptions *dopt, TableInfo *tblinfo, int numTables) |
486 | 0 | { |
487 | 0 | int i, |
488 | 0 | j, |
489 | 0 | k; |
490 | |
|
491 | 0 | for (i = 0; i < numTables; i++) |
492 | 0 | { |
493 | 0 | TableInfo *tbinfo = &(tblinfo[i]); |
494 | 0 | int numParents; |
495 | 0 | TableInfo **parents; |
496 | | |
497 | | /* Some kinds never have parents */ |
498 | 0 | if (tbinfo->relkind == RELKIND_SEQUENCE || |
499 | 0 | tbinfo->relkind == RELKIND_VIEW || |
500 | 0 | tbinfo->relkind == RELKIND_MATVIEW) |
501 | 0 | continue; |
502 | | |
503 | | /* Don't bother computing anything for non-target tables, either */ |
504 | 0 | if (!tbinfo->dobj.dump) |
505 | 0 | continue; |
506 | | |
507 | 0 | numParents = tbinfo->numParents; |
508 | 0 | parents = tbinfo->parents; |
509 | |
|
510 | 0 | if (numParents == 0) |
511 | 0 | continue; /* nothing to see here, move along */ |
512 | | |
513 | | /* For each column, search for matching column names in parent(s) */ |
514 | 0 | for (j = 0; j < tbinfo->numatts; j++) |
515 | 0 | { |
516 | 0 | bool foundNotNull; /* Attr was NOT NULL in a parent */ |
517 | 0 | bool foundDefault; /* Found a default in a parent */ |
518 | | |
519 | | /* no point in examining dropped columns */ |
520 | 0 | if (tbinfo->attisdropped[j]) |
521 | 0 | continue; |
522 | | |
523 | 0 | foundNotNull = false; |
524 | 0 | foundDefault = false; |
525 | 0 | for (k = 0; k < numParents; k++) |
526 | 0 | { |
527 | 0 | TableInfo *parent = parents[k]; |
528 | 0 | int inhAttrInd; |
529 | |
|
530 | 0 | inhAttrInd = strInArray(tbinfo->attnames[j], |
531 | 0 | parent->attnames, |
532 | 0 | parent->numatts); |
533 | 0 | if (inhAttrInd >= 0) |
534 | 0 | { |
535 | 0 | foundNotNull |= parent->notnull[inhAttrInd]; |
536 | 0 | foundDefault |= (parent->attrdefs[inhAttrInd] != NULL); |
537 | 0 | } |
538 | 0 | } |
539 | | |
540 | | /* Remember if we found inherited NOT NULL */ |
541 | 0 | tbinfo->inhNotNull[j] = foundNotNull; |
542 | | |
543 | | /* Manufacture a DEFAULT NULL clause if necessary */ |
544 | 0 | if (foundDefault && tbinfo->attrdefs[j] == NULL) |
545 | 0 | { |
546 | 0 | AttrDefInfo *attrDef; |
547 | |
|
548 | 0 | attrDef = (AttrDefInfo *) pg_malloc(sizeof(AttrDefInfo)); |
549 | 0 | attrDef->dobj.objType = DO_ATTRDEF; |
550 | 0 | attrDef->dobj.catId.tableoid = 0; |
551 | 0 | attrDef->dobj.catId.oid = 0; |
552 | 0 | AssignDumpId(&attrDef->dobj); |
553 | 0 | attrDef->dobj.name = pg_strdup(tbinfo->dobj.name); |
554 | 0 | attrDef->dobj.namespace = tbinfo->dobj.namespace; |
555 | 0 | attrDef->dobj.dump = tbinfo->dobj.dump; |
556 | |
|
557 | 0 | attrDef->adtable = tbinfo; |
558 | 0 | attrDef->adnum = j + 1; |
559 | 0 | attrDef->adef_expr = pg_strdup("NULL"); |
560 | | |
561 | | /* Will column be dumped explicitly? */ |
562 | 0 | if (shouldPrintColumn(dopt, tbinfo, j)) |
563 | 0 | { |
564 | 0 | attrDef->separate = false; |
565 | | /* No dependency needed: NULL cannot have dependencies */ |
566 | 0 | } |
567 | 0 | else |
568 | 0 | { |
569 | | /* column will be suppressed, print default separately */ |
570 | 0 | attrDef->separate = true; |
571 | | /* ensure it comes out after the table */ |
572 | 0 | addObjectDependency(&attrDef->dobj, |
573 | 0 | tbinfo->dobj.dumpId); |
574 | 0 | } |
575 | |
|
576 | 0 | tbinfo->attrdefs[j] = attrDef; |
577 | 0 | } |
578 | 0 | } |
579 | 0 | } |
580 | 0 | } |
581 | | |
582 | | /* |
583 | | * AssignDumpId |
584 | | * Given a newly-created dumpable object, assign a dump ID, |
585 | | * and enter the object into the lookup table. |
586 | | * |
587 | | * The caller is expected to have filled in objType and catId, |
588 | | * but not any of the other standard fields of a DumpableObject. |
589 | | */ |
590 | | void |
591 | | AssignDumpId(DumpableObject *dobj) |
592 | 0 | { |
593 | 0 | dobj->dumpId = ++lastDumpId; |
594 | 0 | dobj->name = NULL; /* must be set later */ |
595 | 0 | dobj->namespace = NULL; /* may be set later */ |
596 | 0 | dobj->dump = DUMP_COMPONENT_ALL; /* default assumption */ |
597 | 0 | dobj->ext_member = false; /* default assumption */ |
598 | 0 | dobj->dependencies = NULL; |
599 | 0 | dobj->nDeps = 0; |
600 | 0 | dobj->allocDeps = 0; |
601 | |
|
602 | 0 | while (dobj->dumpId >= allocedDumpIds) |
603 | 0 | { |
604 | 0 | int newAlloc; |
605 | |
|
606 | 0 | if (allocedDumpIds <= 0) |
607 | 0 | { |
608 | 0 | newAlloc = 256; |
609 | 0 | dumpIdMap = (DumpableObject **) |
610 | 0 | pg_malloc(newAlloc * sizeof(DumpableObject *)); |
611 | 0 | } |
612 | 0 | else |
613 | 0 | { |
614 | 0 | newAlloc = allocedDumpIds * 2; |
615 | 0 | dumpIdMap = (DumpableObject **) |
616 | 0 | pg_realloc(dumpIdMap, newAlloc * sizeof(DumpableObject *)); |
617 | 0 | } |
618 | 0 | memset(dumpIdMap + allocedDumpIds, 0, |
619 | 0 | (newAlloc - allocedDumpIds) * sizeof(DumpableObject *)); |
620 | 0 | allocedDumpIds = newAlloc; |
621 | 0 | } |
622 | 0 | dumpIdMap[dobj->dumpId] = dobj; |
623 | | |
624 | | /* mark catalogIdMap invalid, but don't rebuild it yet */ |
625 | 0 | catalogIdMapValid = false; |
626 | 0 | } |
627 | | |
628 | | /* |
629 | | * Assign a DumpId that's not tied to a DumpableObject. |
630 | | * |
631 | | * This is used when creating a "fixed" ArchiveEntry that doesn't need to |
632 | | * participate in the sorting logic. |
633 | | */ |
634 | | DumpId |
635 | | createDumpId(void) |
636 | 0 | { |
637 | 0 | return ++lastDumpId; |
638 | 0 | } |
639 | | |
640 | | /* |
641 | | * Return the largest DumpId so far assigned |
642 | | */ |
643 | | DumpId |
644 | | getMaxDumpId(void) |
645 | 0 | { |
646 | 0 | return lastDumpId; |
647 | 0 | } |
648 | | |
649 | | /* |
650 | | * Find a DumpableObject by dump ID |
651 | | * |
652 | | * Returns NULL for invalid ID |
653 | | */ |
654 | | DumpableObject * |
655 | | findObjectByDumpId(DumpId dumpId) |
656 | 0 | { |
657 | 0 | if (dumpId <= 0 || dumpId >= allocedDumpIds) |
658 | 0 | return NULL; /* out of range? */ |
659 | 0 | return dumpIdMap[dumpId]; |
660 | 0 | } |
661 | | |
662 | | /* |
663 | | * Find a DumpableObject by catalog ID |
664 | | * |
665 | | * Returns NULL for unknown ID |
666 | | * |
667 | | * We use binary search in a sorted list that is built on first call. |
668 | | * If AssignDumpId() and findObjectByCatalogId() calls were freely intermixed, |
669 | | * the code would work, but possibly be very slow. In the current usage |
670 | | * pattern that does not happen, indeed we build the list at most twice. |
671 | | */ |
672 | | DumpableObject * |
673 | | findObjectByCatalogId(CatalogId catalogId) |
674 | 0 | { |
675 | 0 | DumpableObject **low; |
676 | 0 | DumpableObject **high; |
677 | |
|
678 | 0 | if (!catalogIdMapValid) |
679 | 0 | { |
680 | 0 | if (catalogIdMap) |
681 | 0 | free(catalogIdMap); |
682 | 0 | getDumpableObjects(&catalogIdMap, &numCatalogIds); |
683 | 0 | if (numCatalogIds > 1) |
684 | 0 | qsort((void *) catalogIdMap, numCatalogIds, |
685 | 0 | sizeof(DumpableObject *), DOCatalogIdCompare); |
686 | 0 | catalogIdMapValid = true; |
687 | 0 | } |
688 | | |
689 | | /* |
690 | | * We could use bsearch() here, but the notational cruft of calling |
691 | | * bsearch is nearly as bad as doing it ourselves; and the generalized |
692 | | * bsearch function is noticeably slower as well. |
693 | | */ |
694 | 0 | if (numCatalogIds <= 0) |
695 | 0 | return NULL; |
696 | 0 | low = catalogIdMap; |
697 | 0 | high = catalogIdMap + (numCatalogIds - 1); |
698 | 0 | while (low <= high) |
699 | 0 | { |
700 | 0 | DumpableObject **middle; |
701 | 0 | int difference; |
702 | |
|
703 | 0 | middle = low + (high - low) / 2; |
704 | | /* comparison must match DOCatalogIdCompare, below */ |
705 | 0 | difference = oidcmp((*middle)->catId.oid, catalogId.oid); |
706 | 0 | if (difference == 0) |
707 | 0 | difference = oidcmp((*middle)->catId.tableoid, catalogId.tableoid); |
708 | 0 | if (difference == 0) |
709 | 0 | return *middle; |
710 | 0 | else if (difference < 0) |
711 | 0 | low = middle + 1; |
712 | 0 | else |
713 | 0 | high = middle - 1; |
714 | 0 | } |
715 | 0 | return NULL; |
716 | 0 | } |
717 | | |
718 | | /* |
719 | | * Find a DumpableObject by OID, in a pre-sorted array of one type of object |
720 | | * |
721 | | * Returns NULL for unknown OID |
722 | | */ |
723 | | static DumpableObject * |
724 | | findObjectByOid(Oid oid, DumpableObject **indexArray, int numObjs) |
725 | 0 | { |
726 | 0 | DumpableObject **low; |
727 | 0 | DumpableObject **high; |
728 | | |
729 | | /* |
730 | | * This is the same as findObjectByCatalogId except we assume we need not |
731 | | * look at table OID because the objects are all the same type. |
732 | | * |
733 | | * We could use bsearch() here, but the notational cruft of calling |
734 | | * bsearch is nearly as bad as doing it ourselves; and the generalized |
735 | | * bsearch function is noticeably slower as well. |
736 | | */ |
737 | 0 | if (numObjs <= 0) |
738 | 0 | return NULL; |
739 | 0 | low = indexArray; |
740 | 0 | high = indexArray + (numObjs - 1); |
741 | 0 | while (low <= high) |
742 | 0 | { |
743 | 0 | DumpableObject **middle; |
744 | 0 | int difference; |
745 | |
|
746 | 0 | middle = low + (high - low) / 2; |
747 | 0 | difference = oidcmp((*middle)->catId.oid, oid); |
748 | 0 | if (difference == 0) |
749 | 0 | return *middle; |
750 | 0 | else if (difference < 0) |
751 | 0 | low = middle + 1; |
752 | 0 | else |
753 | 0 | high = middle - 1; |
754 | 0 | } |
755 | 0 | return NULL; |
756 | 0 | } |
757 | | |
758 | | /* |
759 | | * Build an index array of DumpableObject pointers, sorted by OID |
760 | | */ |
761 | | static DumpableObject ** |
762 | | buildIndexArray(void *objArray, int numObjs, Size objSize) |
763 | 0 | { |
764 | 0 | DumpableObject **ptrs; |
765 | 0 | int i; |
766 | |
|
767 | 0 | ptrs = (DumpableObject **) pg_malloc(numObjs * sizeof(DumpableObject *)); |
768 | 0 | for (i = 0; i < numObjs; i++) |
769 | 0 | ptrs[i] = (DumpableObject *) ((char *) objArray + i * objSize); |
770 | | |
771 | | /* We can use DOCatalogIdCompare to sort since its first key is OID */ |
772 | 0 | if (numObjs > 1) |
773 | 0 | qsort((void *) ptrs, numObjs, sizeof(DumpableObject *), |
774 | 0 | DOCatalogIdCompare); |
775 | |
|
776 | 0 | return ptrs; |
777 | 0 | } |
778 | | |
779 | | /* |
780 | | * qsort comparator for pointers to DumpableObjects |
781 | | */ |
782 | | static int |
783 | | DOCatalogIdCompare(const void *p1, const void *p2) |
784 | 0 | { |
785 | 0 | const DumpableObject *obj1 = *(DumpableObject *const *) p1; |
786 | 0 | const DumpableObject *obj2 = *(DumpableObject *const *) p2; |
787 | 0 | int cmpval; |
788 | | |
789 | | /* |
790 | | * Compare OID first since it's usually unique, whereas there will only be |
791 | | * a few distinct values of tableoid. |
792 | | */ |
793 | 0 | cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid); |
794 | 0 | if (cmpval == 0) |
795 | 0 | cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid); |
796 | 0 | return cmpval; |
797 | 0 | } |
798 | | |
799 | | /* |
800 | | * Build an array of pointers to all known dumpable objects |
801 | | * |
802 | | * This simply creates a modifiable copy of the internal map. |
803 | | */ |
804 | | void |
805 | | getDumpableObjects(DumpableObject ***objs, int *numObjs) |
806 | 0 | { |
807 | 0 | int i, |
808 | 0 | j; |
809 | |
|
810 | 0 | *objs = (DumpableObject **) |
811 | 0 | pg_malloc(allocedDumpIds * sizeof(DumpableObject *)); |
812 | 0 | j = 0; |
813 | 0 | for (i = 1; i < allocedDumpIds; i++) |
814 | 0 | { |
815 | 0 | if (dumpIdMap[i]) |
816 | 0 | (*objs)[j++] = dumpIdMap[i]; |
817 | 0 | } |
818 | 0 | *numObjs = j; |
819 | 0 | } |
820 | | |
821 | | /* |
822 | | * Add a dependency link to a DumpableObject |
823 | | * |
824 | | * Note: duplicate dependencies are currently not eliminated |
825 | | */ |
826 | | void |
827 | | addObjectDependency(DumpableObject *dobj, DumpId refId) |
828 | 0 | { |
829 | 0 | if (dobj->nDeps >= dobj->allocDeps) |
830 | 0 | { |
831 | 0 | if (dobj->allocDeps <= 0) |
832 | 0 | { |
833 | 0 | dobj->allocDeps = 16; |
834 | 0 | dobj->dependencies = (DumpId *) |
835 | 0 | pg_malloc(dobj->allocDeps * sizeof(DumpId)); |
836 | 0 | } |
837 | 0 | else |
838 | 0 | { |
839 | 0 | dobj->allocDeps *= 2; |
840 | 0 | dobj->dependencies = (DumpId *) |
841 | 0 | pg_realloc(dobj->dependencies, |
842 | 0 | dobj->allocDeps * sizeof(DumpId)); |
843 | 0 | } |
844 | 0 | } |
845 | 0 | dobj->dependencies[dobj->nDeps++] = refId; |
846 | 0 | } |
847 | | |
848 | | /* |
849 | | * Remove a dependency link from a DumpableObject |
850 | | * |
851 | | * If there are multiple links, all are removed |
852 | | */ |
853 | | void |
854 | | removeObjectDependency(DumpableObject *dobj, DumpId refId) |
855 | 0 | { |
856 | 0 | int i; |
857 | 0 | int j = 0; |
858 | |
|
859 | 0 | for (i = 0; i < dobj->nDeps; i++) |
860 | 0 | { |
861 | 0 | if (dobj->dependencies[i] != refId) |
862 | 0 | dobj->dependencies[j++] = dobj->dependencies[i]; |
863 | 0 | } |
864 | 0 | dobj->nDeps = j; |
865 | 0 | } |
866 | | |
867 | | |
868 | | /* |
869 | | * findTableByOid |
870 | | * finds the entry (in tblinfo) of the table with the given oid |
871 | | * returns NULL if not found |
872 | | */ |
873 | | TableInfo * |
874 | | findTableByOid(Oid oid) |
875 | 0 | { |
876 | 0 | return (TableInfo *) findObjectByOid(oid, tblinfoindex, numTables); |
877 | 0 | } |
878 | | |
879 | | /* |
880 | | * findTypeByOid |
881 | | * finds the entry (in typinfo) of the type with the given oid |
882 | | * returns NULL if not found |
883 | | */ |
884 | | TypeInfo * |
885 | | findTypeByOid(Oid oid) |
886 | 0 | { |
887 | 0 | return (TypeInfo *) findObjectByOid(oid, typinfoindex, numTypes); |
888 | 0 | } |
889 | | |
890 | | /* |
891 | | * findFuncByOid |
892 | | * finds the entry (in funinfo) of the function with the given oid |
893 | | * returns NULL if not found |
894 | | */ |
895 | | FuncInfo * |
896 | | findFuncByOid(Oid oid) |
897 | 0 | { |
898 | 0 | return (FuncInfo *) findObjectByOid(oid, funinfoindex, numFuncs); |
899 | 0 | } |
900 | | |
901 | | /* |
902 | | * findOprByOid |
903 | | * finds the entry (in oprinfo) of the operator with the given oid |
904 | | * returns NULL if not found |
905 | | */ |
906 | | OprInfo * |
907 | | findOprByOid(Oid oid) |
908 | 0 | { |
909 | 0 | return (OprInfo *) findObjectByOid(oid, oprinfoindex, numOperators); |
910 | 0 | } |
911 | | |
912 | | /* |
913 | | * findCollationByOid |
914 | | * finds the entry (in collinfo) of the collation with the given oid |
915 | | * returns NULL if not found |
916 | | */ |
917 | | CollInfo * |
918 | | findCollationByOid(Oid oid) |
919 | 0 | { |
920 | 0 | return (CollInfo *) findObjectByOid(oid, collinfoindex, numCollations); |
921 | 0 | } |
922 | | |
923 | | /* |
924 | | * findNamespaceByOid |
925 | | * finds the entry (in nspinfo) of the namespace with the given oid |
926 | | * returns NULL if not found |
927 | | */ |
928 | | NamespaceInfo * |
929 | | findNamespaceByOid(Oid oid) |
930 | 0 | { |
931 | 0 | return (NamespaceInfo *) findObjectByOid(oid, nspinfoindex, numNamespaces); |
932 | 0 | } |
933 | | |
934 | | /* |
935 | | * findExtensionByOid |
936 | | * finds the entry (in extinfo) of the extension with the given oid |
937 | | * returns NULL if not found |
938 | | */ |
939 | | ExtensionInfo * |
940 | | findExtensionByOid(Oid oid) |
941 | 0 | { |
942 | 0 | return (ExtensionInfo *) findObjectByOid(oid, extinfoindex, numExtensions); |
943 | 0 | } |
944 | | |
945 | | /* |
946 | | * findTablegroupByOid |
947 | | * finds the entry (in tblgrpinfo) of the tablegroup with the given oid |
948 | | * returns NULL if not found |
949 | | */ |
950 | | TablegroupInfo * |
951 | | findTablegroupByOid(Oid oid) |
952 | 0 | { |
953 | 0 | return (TablegroupInfo *) findObjectByOid(oid, tblgrpinfoindex, numTablegroups); |
954 | 0 | } |
955 | | |
956 | | /* |
957 | | * findIndexByOid |
958 | | * find the entry of the index with the given oid |
959 | | * |
960 | | * This one's signature is different from the previous ones because we lack a |
961 | | * global array of all indexes, so caller must pass their array as argument. |
962 | | */ |
963 | | static IndxInfo * |
964 | | findIndexByOid(Oid oid, DumpableObject **idxinfoindex, int numIndexes) |
965 | 0 | { |
966 | 0 | return (IndxInfo *) findObjectByOid(oid, idxinfoindex, numIndexes); |
967 | 0 | } |
968 | | |
969 | | /* |
970 | | * setExtensionMembership |
971 | | * accept and save data about which objects belong to extensions |
972 | | */ |
973 | | void |
974 | | setExtensionMembership(ExtensionMemberId *extmems, int nextmems) |
975 | 0 | { |
976 | | /* Sort array in preparation for binary searches */ |
977 | 0 | if (nextmems > 1) |
978 | 0 | qsort((void *) extmems, nextmems, sizeof(ExtensionMemberId), |
979 | 0 | ExtensionMemberIdCompare); |
980 | | /* And save */ |
981 | 0 | extmembers = extmems; |
982 | 0 | numextmembers = nextmems; |
983 | 0 | } |
984 | | |
985 | | /* |
986 | | * findOwningExtension |
987 | | * return owning extension for specified catalog ID, or NULL if none |
988 | | */ |
989 | | ExtensionInfo * |
990 | | findOwningExtension(CatalogId catalogId) |
991 | 0 | { |
992 | 0 | ExtensionMemberId *low; |
993 | 0 | ExtensionMemberId *high; |
994 | | |
995 | | /* |
996 | | * We could use bsearch() here, but the notational cruft of calling |
997 | | * bsearch is nearly as bad as doing it ourselves; and the generalized |
998 | | * bsearch function is noticeably slower as well. |
999 | | */ |
1000 | 0 | if (numextmembers <= 0) |
1001 | 0 | return NULL; |
1002 | 0 | low = extmembers; |
1003 | 0 | high = extmembers + (numextmembers - 1); |
1004 | 0 | while (low <= high) |
1005 | 0 | { |
1006 | 0 | ExtensionMemberId *middle; |
1007 | 0 | int difference; |
1008 | |
|
1009 | 0 | middle = low + (high - low) / 2; |
1010 | | /* comparison must match ExtensionMemberIdCompare, below */ |
1011 | 0 | difference = oidcmp(middle->catId.oid, catalogId.oid); |
1012 | 0 | if (difference == 0) |
1013 | 0 | difference = oidcmp(middle->catId.tableoid, catalogId.tableoid); |
1014 | 0 | if (difference == 0) |
1015 | 0 | return middle->ext; |
1016 | 0 | else if (difference < 0) |
1017 | 0 | low = middle + 1; |
1018 | 0 | else |
1019 | 0 | high = middle - 1; |
1020 | 0 | } |
1021 | 0 | return NULL; |
1022 | 0 | } |
1023 | | |
1024 | | /* |
1025 | | * qsort comparator for ExtensionMemberIds |
1026 | | */ |
1027 | | static int |
1028 | | ExtensionMemberIdCompare(const void *p1, const void *p2) |
1029 | 0 | { |
1030 | 0 | const ExtensionMemberId *obj1 = (const ExtensionMemberId *) p1; |
1031 | 0 | const ExtensionMemberId *obj2 = (const ExtensionMemberId *) p2; |
1032 | 0 | int cmpval; |
1033 | | |
1034 | | /* |
1035 | | * Compare OID first since it's usually unique, whereas there will only be |
1036 | | * a few distinct values of tableoid. |
1037 | | */ |
1038 | 0 | cmpval = oidcmp(obj1->catId.oid, obj2->catId.oid); |
1039 | 0 | if (cmpval == 0) |
1040 | 0 | cmpval = oidcmp(obj1->catId.tableoid, obj2->catId.tableoid); |
1041 | 0 | return cmpval; |
1042 | 0 | } |
1043 | | |
1044 | | |
1045 | | /* |
1046 | | * findParentsByOid |
1047 | | * find a table's parents in tblinfo[] |
1048 | | */ |
1049 | | static void |
1050 | | findParentsByOid(TableInfo *self, |
1051 | | InhInfo *inhinfo, int numInherits) |
1052 | 0 | { |
1053 | 0 | Oid oid = self->dobj.catId.oid; |
1054 | 0 | int i, |
1055 | 0 | j; |
1056 | 0 | int numParents; |
1057 | |
|
1058 | 0 | numParents = 0; |
1059 | 0 | for (i = 0; i < numInherits; i++) |
1060 | 0 | { |
1061 | 0 | if (inhinfo[i].inhrelid == oid) |
1062 | 0 | numParents++; |
1063 | 0 | } |
1064 | |
|
1065 | 0 | self->numParents = numParents; |
1066 | |
|
1067 | 0 | if (numParents > 0) |
1068 | 0 | { |
1069 | 0 | self->parents = (TableInfo **) |
1070 | 0 | pg_malloc(sizeof(TableInfo *) * numParents); |
1071 | 0 | j = 0; |
1072 | 0 | for (i = 0; i < numInherits; i++) |
1073 | 0 | { |
1074 | 0 | if (inhinfo[i].inhrelid == oid) |
1075 | 0 | { |
1076 | 0 | TableInfo *parent; |
1077 | |
|
1078 | 0 | parent = findTableByOid(inhinfo[i].inhparent); |
1079 | 0 | if (parent == NULL) |
1080 | 0 | { |
1081 | 0 | write_msg(NULL, "failed sanity check, parent OID %u of table \"%s\" (OID %u) not found\n", |
1082 | 0 | inhinfo[i].inhparent, |
1083 | 0 | self->dobj.name, |
1084 | 0 | oid); |
1085 | 0 | exit_nicely(1); |
1086 | 0 | } |
1087 | 0 | self->parents[j++] = parent; |
1088 | 0 | } |
1089 | 0 | } |
1090 | 0 | } |
1091 | 0 | else |
1092 | 0 | self->parents = NULL; |
1093 | 0 | } |
1094 | | |
1095 | | /* |
1096 | | * parseOidArray |
1097 | | * parse a string of numbers delimited by spaces into a character array |
1098 | | * |
1099 | | * Note: actually this is used for both Oids and potentially-signed |
1100 | | * attribute numbers. This should cause no trouble, but we could split |
1101 | | * the function into two functions with different argument types if it does. |
1102 | | */ |
1103 | | |
1104 | | void |
1105 | | parseOidArray(const char *str, Oid *array, int arraysize) |
1106 | 0 | { |
1107 | 0 | int j, |
1108 | 0 | argNum; |
1109 | 0 | char temp[100]; |
1110 | 0 | char s; |
1111 | |
|
1112 | 0 | argNum = 0; |
1113 | 0 | j = 0; |
1114 | 0 | for (;;) |
1115 | 0 | { |
1116 | 0 | s = *str++; |
1117 | 0 | if (s == ' ' || s == '\0') |
1118 | 0 | { |
1119 | 0 | if (j > 0) |
1120 | 0 | { |
1121 | 0 | if (argNum >= arraysize) |
1122 | 0 | { |
1123 | 0 | write_msg(NULL, "could not parse numeric array \"%s\": too many numbers\n", str); |
1124 | 0 | exit_nicely(1); |
1125 | 0 | } |
1126 | 0 | temp[j] = '\0'; |
1127 | 0 | array[argNum++] = atooid(temp); |
1128 | 0 | j = 0; |
1129 | 0 | } |
1130 | 0 | if (s == '\0') |
1131 | 0 | break; |
1132 | 0 | } |
1133 | 0 | else |
1134 | 0 | { |
1135 | 0 | if (!(isdigit((unsigned char) s) || s == '-') || |
1136 | 0 | j >= sizeof(temp) - 1) |
1137 | 0 | { |
1138 | 0 | write_msg(NULL, "could not parse numeric array \"%s\": invalid character in number\n", str); |
1139 | 0 | exit_nicely(1); |
1140 | 0 | } |
1141 | 0 | temp[j++] = s; |
1142 | 0 | } |
1143 | 0 | } |
1144 | |
|
1145 | 0 | while (argNum < arraysize) |
1146 | 0 | array[argNum++] = InvalidOid; |
1147 | 0 | } |
1148 | | |
1149 | | |
1150 | | /* |
1151 | | * strInArray: |
1152 | | * takes in a string and a string array and the number of elements in the |
1153 | | * string array. |
1154 | | * returns the index if the string is somewhere in the array, -1 otherwise |
1155 | | */ |
1156 | | |
1157 | | static int |
1158 | | strInArray(const char *pattern, char **arr, int arr_size) |
1159 | 0 | { |
1160 | 0 | int i; |
1161 | |
|
1162 | 0 | for (i = 0; i < arr_size; i++) |
1163 | 0 | { |
1164 | 0 | if (strcmp(pattern, arr[i]) == 0) |
1165 | 0 | return i; |
1166 | 0 | } |
1167 | 0 | return -1; |
1168 | 0 | } |