Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_backup_archiver.c
4 : : *
5 : : * Private implementation of the archiver routines.
6 : : *
7 : : * See the headers to pg_restore for more details.
8 : : *
9 : : * Copyright (c) 2000, Philip Warner
10 : : * Rights are granted to use this software in any way so long
11 : : * as this notice is not removed.
12 : : *
13 : : * The author is not responsible for loss or damages that may
14 : : * result from its use.
15 : : *
16 : : *
17 : : * IDENTIFICATION
18 : : * src/bin/pg_dump/pg_backup_archiver.c
19 : : *
20 : : *-------------------------------------------------------------------------
21 : : */
22 : : #include "postgres_fe.h"
23 : :
24 : : #include <ctype.h>
25 : : #include <fcntl.h>
26 : : #include <unistd.h>
27 : : #include <sys/stat.h>
28 : : #include <sys/wait.h>
29 : : #ifdef WIN32
30 : : #include <io.h>
31 : : #endif
32 : :
33 : : #include "catalog/pg_class_d.h"
34 : : #include "catalog/pg_largeobject_metadata_d.h"
35 : : #include "catalog/pg_shdepend_d.h"
36 : : #include "common/string.h"
37 : : #include "compress_io.h"
38 : : #include "dumputils.h"
39 : : #include "fe_utils/string_utils.h"
40 : : #include "lib/binaryheap.h"
41 : : #include "lib/stringinfo.h"
42 : : #include "libpq/libpq-fs.h"
43 : : #include "parallel.h"
44 : : #include "pg_backup_archiver.h"
45 : : #include "pg_backup_db.h"
46 : : #include "pg_backup_utils.h"
47 : : #include "pgtar.h"
48 : :
49 : : #define TEXT_DUMP_HEADER "--\n-- PostgreSQL database dump\n--\n\n"
50 : : #define TEXT_DUMPALL_HEADER "--\n-- PostgreSQL database cluster dump\n--\n\n"
51 : :
52 : : #define TOC_PREFIX_NONE ""
53 : : #define TOC_PREFIX_DATA "Data for "
54 : : #define TOC_PREFIX_STATS "Statistics for "
55 : :
56 : : static ArchiveHandle *_allocAH(const char *FileSpec, const ArchiveFormat fmt,
57 : : const pg_compress_specification compression_spec,
58 : : bool dosync, ArchiveMode mode,
59 : : SetupWorkerPtrType setupWorkerPtr,
60 : : DataDirSyncMethod sync_method);
61 : : static void _getObjectDescription(PQExpBuffer buf, const TocEntry *te);
62 : : static void _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx);
63 : : static void _doSetFixedOutputState(ArchiveHandle *AH);
64 : : static void _doSetSessionAuth(ArchiveHandle *AH, const char *user);
65 : : static void _reconnectToDB(ArchiveHandle *AH, const char *dbname);
66 : : static void _becomeUser(ArchiveHandle *AH, const char *user);
67 : : static void _becomeOwner(ArchiveHandle *AH, TocEntry *te);
68 : : static void _selectOutputSchema(ArchiveHandle *AH, const char *schemaName);
69 : : static void _selectTablespace(ArchiveHandle *AH, const char *tablespace);
70 : : static void _selectTableAccessMethod(ArchiveHandle *AH, const char *tableam);
71 : : static void _printTableAccessMethodNoStorage(ArchiveHandle *AH,
72 : : TocEntry *te);
73 : : static void processEncodingEntry(ArchiveHandle *AH, TocEntry *te);
74 : : static void processStdStringsEntry(ArchiveHandle *AH, TocEntry *te);
75 : : static void processSearchPathEntry(ArchiveHandle *AH, TocEntry *te);
76 : : static int _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH);
77 : : static RestorePass _tocEntryRestorePass(TocEntry *te);
78 : : static bool _tocEntryIsACL(TocEntry *te);
79 : : static void _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te);
80 : : static void _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te);
81 : : static bool is_load_via_partition_root(TocEntry *te);
82 : : static void buildTocEntryArrays(ArchiveHandle *AH);
83 : : static void _moveBefore(TocEntry *pos, TocEntry *te);
84 : : static int _discoverArchiveFormat(ArchiveHandle *AH);
85 : :
86 : : static int RestoringToDB(ArchiveHandle *AH);
87 : : static void dump_lo_buf(ArchiveHandle *AH);
88 : : static void dumpTimestamp(ArchiveHandle *AH, const char *msg, time_t tim);
89 : : static void SetOutput(ArchiveHandle *AH, const char *filename,
90 : : const pg_compress_specification compression_spec);
91 : : static CompressFileHandle *SaveOutput(ArchiveHandle *AH);
92 : : static void RestoreOutput(ArchiveHandle *AH, CompressFileHandle *savedOutput);
93 : :
94 : : static int restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel);
95 : : static void restore_toc_entries_prefork(ArchiveHandle *AH,
96 : : TocEntry *pending_list);
97 : : static void restore_toc_entries_parallel(ArchiveHandle *AH,
98 : : ParallelState *pstate,
99 : : TocEntry *pending_list);
100 : : static void restore_toc_entries_postfork(ArchiveHandle *AH,
101 : : TocEntry *pending_list);
102 : : static void pending_list_header_init(TocEntry *l);
103 : : static void pending_list_append(TocEntry *l, TocEntry *te);
104 : : static void pending_list_remove(TocEntry *te);
105 : : static int TocEntrySizeCompareQsort(const void *p1, const void *p2);
106 : : static int TocEntrySizeCompareBinaryheap(void *p1, void *p2, void *arg);
107 : : static void move_to_ready_heap(TocEntry *pending_list,
108 : : binaryheap *ready_heap,
109 : : RestorePass pass);
110 : : static TocEntry *pop_next_work_item(binaryheap *ready_heap,
111 : : ParallelState *pstate);
112 : : static void mark_dump_job_done(ArchiveHandle *AH,
113 : : TocEntry *te,
114 : : int status,
115 : : void *callback_data);
116 : : static void mark_restore_job_done(ArchiveHandle *AH,
117 : : TocEntry *te,
118 : : int status,
119 : : void *callback_data);
120 : : static void fix_dependencies(ArchiveHandle *AH);
121 : : static bool has_lock_conflicts(TocEntry *te1, TocEntry *te2);
122 : : static void repoint_table_dependencies(ArchiveHandle *AH);
123 : : static void identify_locking_dependencies(ArchiveHandle *AH, TocEntry *te);
124 : : static void reduce_dependencies(ArchiveHandle *AH, TocEntry *te,
125 : : binaryheap *ready_heap);
126 : : static void mark_create_done(ArchiveHandle *AH, TocEntry *te);
127 : : static void inhibit_data_for_failed_table(ArchiveHandle *AH, TocEntry *te);
128 : :
129 : : static void StrictNamesCheck(RestoreOptions *ropt);
130 : :
131 : :
132 : : /*
133 : : * Allocate a new DumpOptions block containing all default values.
134 : : */
135 : : DumpOptions *
4302 alvherre@alvh.no-ip. 136 :CBC 67 : NewDumpOptions(void)
137 : : {
162 michael@paquier.xyz 138 : 67 : DumpOptions *opts = pg_malloc_object(DumpOptions);
139 : :
4213 tgl@sss.pgh.pa.us 140 : 67 : InitDumpOptions(opts);
141 : 67 : return opts;
142 : : }
143 : :
144 : : /*
145 : : * Initialize a DumpOptions struct to all default values
146 : : */
147 : : void
148 : 303 : InitDumpOptions(DumpOptions *opts)
149 : : {
150 : 303 : memset(opts, 0, sizeof(DumpOptions));
151 : : /* set any fields that shouldn't default to zeroes */
4302 alvherre@alvh.no-ip. 152 : 303 : opts->include_everything = true;
2130 tgl@sss.pgh.pa.us 153 : 303 : opts->cparams.promptPassword = TRI_DEFAULT;
4302 alvherre@alvh.no-ip. 154 : 303 : opts->dumpSections = DUMP_UNSECTIONED;
607 nathan@postgresql.or 155 : 303 : opts->dumpSchema = true;
156 : 303 : opts->dumpData = true;
424 jdavis@postgresql.or 157 : 303 : opts->dumpStatistics = false;
4302 alvherre@alvh.no-ip. 158 : 303 : }
159 : :
160 : : /*
161 : : * Create a freshly allocated DumpOptions with options equivalent to those
162 : : * found in the given RestoreOptions.
163 : : */
164 : : DumpOptions *
165 : 67 : dumpOptionsFromRestoreOptions(RestoreOptions *ropt)
166 : : {
167 : 67 : DumpOptions *dopt = NewDumpOptions();
168 : :
169 : : /* this is the inverse of what's at the end of pg_dump.c's main() */
2130 tgl@sss.pgh.pa.us 170 [ + + ]: 67 : dopt->cparams.dbname = ropt->cparams.dbname ? pg_strdup(ropt->cparams.dbname) : NULL;
171 [ + + ]: 67 : dopt->cparams.pgport = ropt->cparams.pgport ? pg_strdup(ropt->cparams.pgport) : NULL;
172 [ + + ]: 67 : dopt->cparams.pghost = ropt->cparams.pghost ? pg_strdup(ropt->cparams.pghost) : NULL;
173 [ + + ]: 67 : dopt->cparams.username = ropt->cparams.username ? pg_strdup(ropt->cparams.username) : NULL;
174 : 67 : dopt->cparams.promptPassword = ropt->cparams.promptPassword;
4302 alvherre@alvh.no-ip. 175 : 67 : dopt->outputClean = ropt->dropSchema;
607 nathan@postgresql.or 176 : 67 : dopt->dumpData = ropt->dumpData;
177 : 67 : dopt->dumpSchema = ropt->dumpSchema;
520 jdavis@postgresql.or 178 : 67 : dopt->dumpSections = ropt->dumpSections;
179 : 67 : dopt->dumpStatistics = ropt->dumpStatistics;
4302 alvherre@alvh.no-ip. 180 : 67 : dopt->if_exists = ropt->if_exists;
181 : 67 : dopt->column_inserts = ropt->column_inserts;
182 : 67 : dopt->aclsSkip = ropt->aclsSkip;
183 : 67 : dopt->outputSuperuser = ropt->superuser;
184 : 67 : dopt->outputCreateDB = ropt->createDB;
185 : 67 : dopt->outputNoOwner = ropt->noOwner;
1650 michael@paquier.xyz 186 : 67 : dopt->outputNoTableAm = ropt->noTableAm;
4302 alvherre@alvh.no-ip. 187 : 67 : dopt->outputNoTablespaces = ropt->noTablespace;
188 : 67 : dopt->disable_triggers = ropt->disable_triggers;
189 : 67 : dopt->use_setsessauth = ropt->use_setsessauth;
190 : 67 : dopt->disable_dollar_quoting = ropt->disable_dollar_quoting;
191 : 67 : dopt->dump_inserts = ropt->dump_inserts;
3103 tgl@sss.pgh.pa.us 192 : 67 : dopt->no_comments = ropt->no_comments;
496 193 : 67 : dopt->no_policies = ropt->no_policies;
3361 peter_e@gmx.net 194 : 67 : dopt->no_publications = ropt->no_publications;
4302 alvherre@alvh.no-ip. 195 : 67 : dopt->no_security_labels = ropt->no_security_labels;
3364 peter_e@gmx.net 196 : 67 : dopt->no_subscriptions = ropt->no_subscriptions;
4302 alvherre@alvh.no-ip. 197 : 67 : dopt->lockWaitTimeout = ropt->lockWaitTimeout;
198 : 67 : dopt->include_everything = ropt->include_everything;
199 : 67 : dopt->enable_row_security = ropt->enable_row_security;
3623 peter_e@gmx.net 200 : 67 : dopt->sequence_data = ropt->sequence_data;
348 nathan@postgresql.or 201 [ + + ]: 67 : dopt->restrict_key = ropt->restrict_key ? pg_strdup(ropt->restrict_key) : NULL;
202 : :
4302 alvherre@alvh.no-ip. 203 : 67 : return dopt;
204 : : }
205 : :
206 : :
207 : : /*
208 : : * Wrapper functions.
209 : : *
210 : : * The objective is to make writing new formats and dumpers as simple
211 : : * as possible, if necessary at the expense of extra function calls etc.
212 : : *
213 : : */
214 : :
215 : : /*
216 : : * The dump worker setup needs lots of knowledge of the internals of pg_dump,
217 : : * so it's defined in pg_dump.c and passed into OpenArchive. The restore worker
218 : : * setup doesn't need to know anything much, so it's defined here.
219 : : */
220 : : static void
3846 tgl@sss.pgh.pa.us 221 : 10 : setupRestoreWorker(Archive *AHX)
222 : : {
4871 andrew@dunslane.net 223 : 10 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
224 : :
3243 peter_e@gmx.net 225 : 10 : AH->ReopenPtr(AH);
4871 andrew@dunslane.net 226 : 10 : }
227 : :
228 : :
229 : : /* Create a new archive */
230 : : /* Public */
231 : : Archive *
9256 bruce@momjian.us 232 : 211 : CreateArchive(const char *FileSpec, const ArchiveFormat fmt,
233 : : const pg_compress_specification compression_spec,
234 : : bool dosync, ArchiveMode mode,
235 : : SetupWorkerPtrType setupDumpWorker,
236 : : DataDirSyncMethod sync_method)
237 : :
238 : : {
1331 michael@paquier.xyz 239 : 211 : ArchiveHandle *AH = _allocAH(FileSpec, fmt, compression_spec,
240 : : dosync, mode, setupDumpWorker, sync_method);
241 : :
9256 bruce@momjian.us 242 : 210 : return (Archive *) AH;
243 : : }
244 : :
245 : : /* Open an existing archive */
246 : : /* Public */
247 : : Archive *
248 : 65 : OpenArchive(const char *FileSpec, const ArchiveFormat fmt)
249 : : {
250 : : ArchiveHandle *AH;
1331 michael@paquier.xyz 251 : 65 : pg_compress_specification compression_spec = {0};
252 : :
253 : 65 : compression_spec.algorithm = PG_COMPRESSION_NONE;
254 : 65 : AH = _allocAH(FileSpec, fmt, compression_spec, true,
255 : : archModeRead, setupRestoreWorker,
256 : : DATA_DIR_SYNC_METHOD_FSYNC);
257 : :
9256 bruce@momjian.us 258 : 65 : return (Archive *) AH;
259 : : }
260 : :
261 : : /* Public */
262 : : void
3846 tgl@sss.pgh.pa.us 263 : 255 : CloseArchive(Archive *AHX)
264 : : {
9256 bruce@momjian.us 265 : 255 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
266 : :
3243 peter_e@gmx.net 267 : 255 : AH->ClosePtr(AH);
268 : :
269 : : /* Close the output */
1248 tomas.vondra@postgre 270 : 255 : errno = 0;
1220 271 [ - + ]: 255 : if (!EndCompressFileHandle(AH->OF))
1569 tgl@sss.pgh.pa.us 272 :UBC 0 : pg_fatal("could not close output file: %m");
9517 bruce@momjian.us 273 :CBC 255 : }
274 : :
275 : : /* Public */
276 : : void
3846 tgl@sss.pgh.pa.us 277 : 470 : SetArchiveOptions(Archive *AH, DumpOptions *dopt, RestoreOptions *ropt)
278 : : {
279 : : /* Caller can omit dump options, in which case we synthesize them */
280 [ + + + - ]: 470 : if (dopt == NULL && ropt != NULL)
281 : 67 : dopt = dumpOptionsFromRestoreOptions(ropt);
282 : :
283 : : /* Save options for later access */
284 : 470 : AH->dopt = dopt;
5170 285 : 470 : AH->ropt = ropt;
3846 286 : 470 : }
287 : :
288 : : /* Public */
289 : : void
290 : 252 : ProcessArchiveRestoreOptions(Archive *AHX)
291 : : {
292 : 252 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
293 : 252 : RestoreOptions *ropt = AH->public.ropt;
294 : : TocEntry *te;
295 : : teSection curSection;
296 : :
297 : : /* Decide which TOC entries will be dumped/restored, and mark them */
5170 298 : 252 : curSection = SECTION_PRE_DATA;
299 [ + + ]: 52181 : for (te = AH->toc->next; te != AH->toc; te = te->next)
300 : : {
301 : : /*
302 : : * When writing an archive, we also take this opportunity to check
303 : : * that we have generated the entries in a sane order that respects
304 : : * the section divisions. When reading, don't complain, since buggy
305 : : * old versions of pg_dump might generate out-of-order archives.
306 : : */
5143 307 [ + + ]: 51929 : if (AH->mode != archModeRead)
308 : : {
309 [ + + + + : 44112 : switch (te->section)
- ]
310 : : {
311 : 8734 : case SECTION_NONE:
312 : : /* ok to be anywhere */
313 : 8734 : break;
314 : 20342 : case SECTION_PRE_DATA:
315 [ - + ]: 20342 : if (curSection != SECTION_PRE_DATA)
2672 peter@eisentraut.org 316 :UBC 0 : pg_log_warning("archive items not in correct section order");
5143 tgl@sss.pgh.pa.us 317 :CBC 20342 : break;
318 : 7580 : case SECTION_DATA:
319 [ - + ]: 7580 : if (curSection == SECTION_POST_DATA)
2672 peter@eisentraut.org 320 :UBC 0 : pg_log_warning("archive items not in correct section order");
5143 tgl@sss.pgh.pa.us 321 :CBC 7580 : break;
322 : 7456 : case SECTION_POST_DATA:
323 : : /* ok no matter which section we were in */
324 : 7456 : break;
5143 tgl@sss.pgh.pa.us 325 :UBC 0 : default:
1569 326 : 0 : pg_fatal("unexpected section code %d",
327 : : (int) te->section);
328 : : break;
329 : : }
330 : : }
331 : :
5170 tgl@sss.pgh.pa.us 332 [ + + ]:CBC 51929 : if (te->section != SECTION_NONE)
333 : 42345 : curSection = te->section;
334 : :
3103 335 : 51929 : te->reqs = _tocEntryRequired(te, curSection, AH);
336 : : }
337 : :
338 : : /* Enforce strict names checking */
3967 teodor@sigaev.ru 339 [ - + ]: 252 : if (ropt->strict_names)
3967 teodor@sigaev.ru 340 :UBC 0 : StrictNamesCheck(ropt);
5170 tgl@sss.pgh.pa.us 341 :CBC 252 : }
342 : :
343 : : /* Public */
344 : : void
40 andrew@dunslane.net 345 : 191 : RestoreArchive(Archive *AHX)
346 : : {
5170 tgl@sss.pgh.pa.us 347 : 191 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
3846 348 : 191 : RestoreOptions *ropt = AH->public.ropt;
349 : : bool parallel_mode;
350 : : TocEntry *te;
351 : : CompressFileHandle *sav;
352 : :
8009 bruce@momjian.us 353 : 191 : AH->stage = STAGE_INITIALIZING;
354 : :
355 : : /*
356 : : * If we're going to do parallel restore, there are some restrictions.
357 : : */
4871 andrew@dunslane.net 358 [ + + + + ]: 191 : parallel_mode = (AH->public.numWorkers > 1 && ropt->useDB);
5445 tgl@sss.pgh.pa.us 359 [ + + ]: 191 : if (parallel_mode)
360 : : {
361 : : /* We haven't got round to making this work for all archive formats */
362 [ + - - + ]: 4 : if (AH->ClonePtr == NULL || AH->ReopenPtr == NULL)
1569 tgl@sss.pgh.pa.us 363 :UBC 0 : pg_fatal("parallel restore is not supported with this archive file format");
364 : :
365 : : /* Doesn't work if the archive represents dependencies as OIDs */
5445 tgl@sss.pgh.pa.us 366 [ - + ]:CBC 4 : if (AH->version < K_VERS_1_8)
1569 tgl@sss.pgh.pa.us 367 :UBC 0 : pg_fatal("parallel restore is not supported with archives made by pre-8.0 pg_dump");
368 : :
369 : : /*
370 : : * It's also not gonna work if we can't reopen the input file, so
371 : : * let's try that immediately.
372 : : */
3243 peter_e@gmx.net 373 :CBC 4 : AH->ReopenPtr(AH);
374 : : }
375 : :
376 : : /*
377 : : * Make sure we won't need (de)compression we haven't got
378 : : */
1248 tomas.vondra@postgre 379 [ + - ]: 191 : if (AH->PrintTocDataPtr != NULL)
380 : : {
6382 andrew@dunslane.net 381 [ + + ]: 30368 : for (te = AH->toc->next; te != AH->toc; te = te->next)
382 : : {
5170 tgl@sss.pgh.pa.us 383 [ + + + + ]: 30311 : if (te->hadDumper && (te->reqs & REQ_DATA) != 0)
384 : : {
1163 385 : 134 : char *errmsg = supports_compression(AH->compression_spec);
386 : :
1248 tomas.vondra@postgre 387 [ - + ]: 134 : if (errmsg)
1248 tomas.vondra@postgre 388 :UBC 0 : pg_fatal("cannot restore from compressed archive (%s)",
389 : : errmsg);
390 : : else
1248 tomas.vondra@postgre 391 :CBC 134 : break;
392 : : }
393 : : }
394 : : }
395 : :
396 : : /*
397 : : * Prepare index arrays, so we can assume we have them throughout restore.
398 : : * It's possible we already did this, though.
399 : : */
5171 tgl@sss.pgh.pa.us 400 [ + + ]: 191 : if (AH->tocsByDumpId == NULL)
401 : 187 : buildTocEntryArrays(AH);
402 : :
403 : : /*
404 : : * If we're using a DB connection, then connect it.
405 : : */
9500 pjw@rhyme.com.au 406 [ + + ]: 191 : if (ropt->useDB)
407 : : {
2672 peter@eisentraut.org 408 : 36 : pg_log_info("connecting to database for restore");
9500 pjw@rhyme.com.au 409 [ - + ]: 36 : if (AH->version < K_VERS_1_3)
1569 tgl@sss.pgh.pa.us 410 :UBC 0 : pg_fatal("direct database connections are not supported in pre-1.3 archives");
411 : :
412 : : /*
413 : : * We don't want to guess at whether the dump will successfully
414 : : * restore; allow the attempt regardless of the version of the restore
415 : : * target.
416 : : */
4591 kgrittn@postgresql.o 417 :CBC 36 : AHX->minRemoteVersion = 0;
3573 tgl@sss.pgh.pa.us 418 : 36 : AHX->maxRemoteVersion = 9999999;
419 : :
477 andrew@dunslane.net 420 : 36 : ConnectDatabaseAhx(AHX, &ropt->cparams, false);
421 : :
422 : : /*
423 : : * If we're talking to the DB directly, don't send comments since they
424 : : * obscure SQL when displaying errors
425 : : */
8009 bruce@momjian.us 426 : 36 : AH->noTocComments = 1;
427 : : }
428 : :
429 : : /*
430 : : * Work out if we have an implied schema-less restore. This can happen if
431 : : * the dump excluded the schema or the user has used a toc list to exclude
432 : : * all of the schema data. All we do is look for schema entries - if none
433 : : * are found then we unset the dumpSchema flag.
434 : : *
435 : : * We could scan for wanted TABLE entries, but that is not the same as
436 : : * data-only. At this stage, it seems unnecessary (6-Mar-2001).
437 : : */
607 nathan@postgresql.or 438 [ + + ]: 191 : if (ropt->dumpSchema)
439 : : {
520 jdavis@postgresql.or 440 : 179 : bool no_schema_found = true;
441 : :
7851 tgl@sss.pgh.pa.us 442 [ + + ]: 1491 : for (te = AH->toc->next; te != AH->toc; te = te->next)
443 : : {
5170 444 [ + + ]: 1471 : if ((te->reqs & REQ_SCHEMA) != 0)
445 : : {
520 jdavis@postgresql.or 446 : 159 : no_schema_found = false;
9272 pjw@rhyme.com.au 447 : 159 : break;
448 : : }
449 : : }
520 jdavis@postgresql.or 450 [ + + ]: 179 : if (no_schema_found)
451 : : {
607 nathan@postgresql.or 452 : 20 : ropt->dumpSchema = false;
520 jdavis@postgresql.or 453 : 20 : pg_log_info("implied no-schema restore");
454 : : }
455 : : }
456 : :
457 : : /*
458 : : * Setup the output file if necessary.
459 : : */
5663 tgl@sss.pgh.pa.us 460 : 191 : sav = SaveOutput(AH);
1331 michael@paquier.xyz 461 [ + + - + ]: 191 : if (ropt->filename || ropt->compression_spec.algorithm != PG_COMPRESSION_NONE)
40 andrew@dunslane.net 462 : 151 : SetOutput(AH, ropt->filename, ropt->compression_spec);
463 : :
8742 peter_e@gmx.net 464 : 191 : ahprintf(AH, "--\n-- PostgreSQL database dump\n--\n\n");
465 : :
466 : : /*
467 : : * If generating plain-text output, enter restricted mode to block any
468 : : * unexpected psql meta-commands. A malicious source might try to inject
469 : : * a variety of things via bogus responses to queries. While we cannot
470 : : * prevent such sources from affecting the destination at restore time, we
471 : : * can block psql meta-commands so that the client machine that runs psql
472 : : * with the dump output remains unaffected.
473 : : */
348 nathan@postgresql.or 474 [ + + ]: 191 : if (ropt->restrict_key)
475 : 153 : ahprintf(AH, "\\restrict %s\n\n", ropt->restrict_key);
476 : :
4401 tgl@sss.pgh.pa.us 477 [ + - ]: 191 : if (AH->archiveRemoteVersion)
478 : 191 : ahprintf(AH, "-- Dumped from database version %s\n",
479 : : AH->archiveRemoteVersion);
480 [ + - ]: 191 : if (AH->archiveDumpVersion)
481 : 191 : ahprintf(AH, "-- Dumped by pg_dump version %s\n",
482 : : AH->archiveDumpVersion);
483 : :
484 : 191 : ahprintf(AH, "\n");
485 : :
5996 bruce@momjian.us 486 [ + + ]: 191 : if (AH->public.verbose)
7771 tgl@sss.pgh.pa.us 487 : 43 : dumpTimestamp(AH, "Started on", AH->createDate);
488 : :
7467 489 [ - + ]: 191 : if (ropt->single_txn)
490 : : {
7466 tgl@sss.pgh.pa.us 491 [ # # ]:UBC 0 : if (AH->connection)
4302 alvherre@alvh.no-ip. 492 : 0 : StartTransaction(AHX);
493 : : else
7466 tgl@sss.pgh.pa.us 494 : 0 : ahprintf(AH, "BEGIN;\n\n");
495 : : }
496 : :
497 : : /*
498 : : * Establish important parameter values right away.
499 : : */
8187 tgl@sss.pgh.pa.us 500 :CBC 191 : _doSetFixedOutputState(AH);
501 : :
8009 bruce@momjian.us 502 : 191 : AH->stage = STAGE_PROCESSING;
503 : :
504 : : /*
505 : : * Drop the items at the start, in reverse order
506 : : */
9256 507 [ + + ]: 191 : if (ropt->dropSchema)
508 : : {
7771 tgl@sss.pgh.pa.us 509 [ + + ]: 1534 : for (te = AH->toc->prev; te != AH->toc; te = te->prev)
510 : : {
511 : 1507 : AH->currentTE = te;
512 : :
513 : : /*
514 : : * In createDB mode, issue a DROP *only* for the database as a
515 : : * whole. Issuing drops against anything else would be wrong,
516 : : * because at this point we're connected to the wrong database.
517 : : * (The DATABASE PROPERTIES entry, if any, should be treated like
518 : : * the DATABASE entry.)
519 : : */
5026 520 [ + + ]: 1507 : if (ropt->createDB)
521 : : {
3106 522 [ + + ]: 682 : if (strcmp(te->desc, "DATABASE") != 0 &&
523 [ + + ]: 660 : strcmp(te->desc, "DATABASE PROPERTIES") != 0)
5026 524 : 640 : continue;
525 : : }
526 : :
527 : : /* Otherwise, drop anything that's selected and has a dropStmt */
5170 528 [ + + + + ]: 867 : if (((te->reqs & (REQ_SCHEMA | REQ_DATA)) != 0) && te->dropStmt)
529 : : {
845 530 : 362 : bool not_allowed_in_txn = false;
531 : :
2672 peter@eisentraut.org 532 : 362 : pg_log_info("dropping %s %s", te->desc, te->tag);
533 : :
534 : : /*
535 : : * In --transaction-size mode, we have to temporarily exit our
536 : : * transaction block to drop objects that can't be dropped
537 : : * within a transaction.
538 : : */
845 tgl@sss.pgh.pa.us 539 [ + + ]: 362 : if (ropt->txn_size > 0)
540 : : {
541 [ + + ]: 40 : if (strcmp(te->desc, "DATABASE") == 0 ||
542 [ + - ]: 20 : strcmp(te->desc, "DATABASE PROPERTIES") == 0)
543 : : {
544 : 40 : not_allowed_in_txn = true;
545 [ + - ]: 40 : if (AH->connection)
546 : 40 : CommitTransaction(AHX);
547 : : else
845 tgl@sss.pgh.pa.us 548 :UBC 0 : ahprintf(AH, "COMMIT;\n");
549 : : }
550 : : }
551 : :
552 : : /* Select owner and schema as necessary */
8341 tgl@sss.pgh.pa.us 553 :CBC 362 : _becomeOwner(AH, te);
8842 554 : 362 : _selectOutputSchema(AH, te->namespace);
555 : :
556 : : /*
557 : : * Now emit the DROP command, if the object has one. Note we
558 : : * don't necessarily emit it verbatim; at this point we add an
559 : : * appropriate IF EXISTS clause, if the user requested it.
560 : : */
845 561 [ + + ]: 362 : if (strcmp(te->desc, "BLOB METADATA") == 0)
562 : : {
563 : : /* We must generate the per-blob commands */
564 [ + + ]: 4 : if (ropt->if_exists)
565 : 2 : IssueCommandPerBlob(AH, te,
566 : : "SELECT pg_catalog.lo_unlink(oid) "
567 : : "FROM pg_catalog.pg_largeobject_metadata "
568 : : "WHERE oid = '", "'");
569 : : else
570 : 2 : IssueCommandPerBlob(AH, te,
571 : : "SELECT pg_catalog.lo_unlink('",
572 : : "')");
573 : : }
574 [ + + ]: 358 : else if (*te->dropStmt != '\0')
575 : : {
1283 576 [ + + ]: 332 : if (!ropt->if_exists ||
577 [ + + ]: 150 : strncmp(te->dropStmt, "--", 2) == 0)
578 : : {
579 : : /*
580 : : * Without --if-exists, or if it's just a comment (as
581 : : * happens for the public schema), print the dropStmt
582 : : * as-is.
583 : : */
4527 alvherre@alvh.no-ip. 584 : 183 : ahprintf(AH, "%s", te->dropStmt);
585 : : }
586 : : else
587 : : {
588 : : /*
589 : : * Inject an appropriate spelling of "if exists". For
590 : : * old-style large objects, we have a routine that
591 : : * knows how to do it, without depending on
592 : : * te->dropStmt; use that. For other objects we need
593 : : * to parse the command.
594 : : */
845 tgl@sss.pgh.pa.us 595 [ - + ]: 149 : if (strcmp(te->desc, "BLOB") == 0)
596 : : {
1328 peter@eisentraut.org 597 :UBC 0 : DropLOIfExists(AH, te->catalogId.oid);
598 : : }
599 : : else
600 : : {
4316 alvherre@alvh.no-ip. 601 :CBC 149 : char *dropStmt = pg_strdup(te->dropStmt);
3537 tgl@sss.pgh.pa.us 602 : 149 : char *dropStmtOrig = dropStmt;
4316 alvherre@alvh.no-ip. 603 : 149 : PQExpBuffer ftStmt = createPQExpBuffer();
604 : :
605 : : /*
606 : : * Need to inject IF EXISTS clause after ALTER
607 : : * TABLE part in ALTER TABLE .. DROP statement
608 : : */
609 [ + + ]: 149 : if (strncmp(dropStmt, "ALTER TABLE", 11) == 0)
610 : : {
2578 drowley@postgresql.o 611 : 19 : appendPQExpBufferStr(ftStmt,
612 : : "ALTER TABLE IF EXISTS");
4316 alvherre@alvh.no-ip. 613 : 19 : dropStmt = dropStmt + 11;
614 : : }
615 : :
616 : : /*
617 : : * ALTER TABLE..ALTER COLUMN..DROP DEFAULT does
618 : : * not support the IF EXISTS clause, and therefore
619 : : * we simply emit the original command for DEFAULT
620 : : * objects (modulo the adjustment made above).
621 : : *
622 : : * Likewise, don't mess with DATABASE PROPERTIES.
623 : : *
624 : : * If we used CREATE OR REPLACE VIEW as a means of
625 : : * quasi-dropping an ON SELECT rule, that should
626 : : * be emitted unchanged as well.
627 : : *
628 : : * For other object types, we need to extract the
629 : : * first part of the DROP which includes the
630 : : * object type. Most of the time this matches
631 : : * te->desc, so search for that; however for the
632 : : * different kinds of CONSTRAINTs, we know to
633 : : * search for hardcoded "DROP CONSTRAINT" instead.
634 : : */
3537 tgl@sss.pgh.pa.us 635 [ + + ]: 149 : if (strcmp(te->desc, "DEFAULT") == 0 ||
3106 636 [ + - ]: 146 : strcmp(te->desc, "DATABASE PROPERTIES") == 0 ||
3537 637 [ - + ]: 146 : strncmp(dropStmt, "CREATE OR REPLACE VIEW", 22) == 0)
4041 heikki.linnakangas@i 638 : 3 : appendPQExpBufferStr(ftStmt, dropStmt);
639 : : else
640 : : {
641 : : char buffer[40];
642 : : char *mark;
643 : :
4316 alvherre@alvh.no-ip. 644 [ + + ]: 146 : if (strcmp(te->desc, "CONSTRAINT") == 0 ||
3321 tgl@sss.pgh.pa.us 645 [ + - ]: 132 : strcmp(te->desc, "CHECK CONSTRAINT") == 0 ||
4316 alvherre@alvh.no-ip. 646 [ + + ]: 132 : strcmp(te->desc, "FK CONSTRAINT") == 0)
647 : 16 : strcpy(buffer, "DROP CONSTRAINT");
648 : : else
649 : 130 : snprintf(buffer, sizeof(buffer), "DROP %s",
650 : : te->desc);
651 : :
652 : 146 : mark = strstr(dropStmt, buffer);
653 : :
3537 tgl@sss.pgh.pa.us 654 [ + - ]: 146 : if (mark)
655 : : {
656 : 146 : *mark = '\0';
657 : 146 : appendPQExpBuffer(ftStmt, "%s%s IF EXISTS%s",
658 : : dropStmt, buffer,
659 : 146 : mark + strlen(buffer));
660 : : }
661 : : else
662 : : {
663 : : /* complain and emit unmodified command */
2672 peter@eisentraut.org 664 :UBC 0 : pg_log_warning("could not find where to insert IF EXISTS in statement \"%s\"",
665 : : dropStmtOrig);
3537 tgl@sss.pgh.pa.us 666 : 0 : appendPQExpBufferStr(ftStmt, dropStmt);
667 : : }
668 : : }
669 : :
4316 alvherre@alvh.no-ip. 670 :CBC 149 : ahprintf(AH, "%s", ftStmt->data);
671 : :
672 : 149 : destroyPQExpBuffer(ftStmt);
3537 tgl@sss.pgh.pa.us 673 : 149 : pg_free(dropStmtOrig);
674 : : }
675 : : }
676 : : }
677 : :
678 : : /*
679 : : * In --transaction-size mode, re-establish the transaction
680 : : * block if needed; otherwise, commit after every N drops.
681 : : */
845 682 [ + + ]: 362 : if (ropt->txn_size > 0)
683 : : {
684 [ + - ]: 40 : if (not_allowed_in_txn)
685 : : {
686 [ + - ]: 40 : if (AH->connection)
687 : 40 : StartTransaction(AHX);
688 : : else
845 tgl@sss.pgh.pa.us 689 :UBC 0 : ahprintf(AH, "BEGIN;\n");
845 tgl@sss.pgh.pa.us 690 :CBC 40 : AH->txnCount = 0;
691 : : }
845 tgl@sss.pgh.pa.us 692 [ # # ]:UBC 0 : else if (++AH->txnCount >= ropt->txn_size)
693 : : {
694 [ # # ]: 0 : if (AH->connection)
695 : : {
696 : 0 : CommitTransaction(AHX);
697 : 0 : StartTransaction(AHX);
698 : : }
699 : : else
700 : 0 : ahprintf(AH, "COMMIT;\nBEGIN;\n");
701 : 0 : AH->txnCount = 0;
702 : : }
703 : : }
704 : : }
705 : : }
706 : :
707 : : /*
708 : : * _selectOutputSchema may have set currSchema to reflect the effect
709 : : * of a "SET search_path" command it emitted. However, by now we may
710 : : * have dropped that schema; or it might not have existed in the first
711 : : * place. In either case the effective value of search_path will not
712 : : * be what we think. Forcibly reset currSchema so that we will
713 : : * re-establish the search_path setting when needed (after creating
714 : : * the schema).
715 : : *
716 : : * If we treated users as pg_dump'able objects then we'd need to reset
717 : : * currUser here too.
718 : : */
1500 peter@eisentraut.org 719 :CBC 27 : free(AH->currSchema);
6382 andrew@dunslane.net 720 : 27 : AH->currSchema = NULL;
721 : : }
722 : :
5445 tgl@sss.pgh.pa.us 723 [ + + ]: 191 : if (parallel_mode)
724 : : {
725 : : /*
726 : : * In parallel mode, turn control over to the parallel-restore logic.
727 : : */
728 : : ParallelState *pstate;
729 : : TocEntry pending_list;
730 : :
731 : : /* The archive format module may need some setup for this */
2871 732 [ + - ]: 4 : if (AH->PrepParallelRestorePtr)
733 : 4 : AH->PrepParallelRestorePtr(AH);
734 : :
735 : 4 : pending_list_header_init(&pending_list);
736 : :
737 : : /* This runs PRE_DATA items and then disconnects from the database */
3278 738 : 4 : restore_toc_entries_prefork(AH, &pending_list);
4871 andrew@dunslane.net 739 [ - + ]: 4 : Assert(AH->connection == NULL);
740 : :
741 : : /* ParallelBackupStart() will actually fork the processes */
3846 tgl@sss.pgh.pa.us 742 : 4 : pstate = ParallelBackupStart(AH);
4871 andrew@dunslane.net 743 : 4 : restore_toc_entries_parallel(AH, pstate, &pending_list);
744 : 4 : ParallelBackupEnd(AH, pstate);
745 : :
746 : : /* reconnect the leader and see if we missed something */
747 : 4 : restore_toc_entries_postfork(AH, &pending_list);
748 [ - + ]: 4 : Assert(AH->connection != NULL);
749 : : }
750 : : else
751 : : {
752 : : /*
753 : : * In serial mode, process everything in three phases: normal items,
754 : : * then ACLs, then post-ACL items. We might be able to skip one or
755 : : * both extra phases in some cases, eg data-only restores.
756 : : */
3278 tgl@sss.pgh.pa.us 757 : 187 : bool haveACL = false;
2329 758 : 187 : bool havePostACL = false;
759 : :
6382 andrew@dunslane.net 760 [ + + ]: 44620 : for (te = AH->toc->next; te != AH->toc; te = te->next)
761 : : {
520 jdavis@postgresql.or 762 [ + + ]: 44434 : if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) == 0)
3278 tgl@sss.pgh.pa.us 763 : 2326 : continue; /* ignore if not to be dumped at all */
764 : :
477 nathan@postgresql.or 765 [ + + + - ]: 42108 : switch (_tocEntryRestorePass(te))
766 : : {
3278 tgl@sss.pgh.pa.us 767 : 38697 : case RESTORE_PASS_MAIN:
768 : 38697 : (void) restore_toc_entry(AH, te, false);
769 : 38696 : break;
770 : 1935 : case RESTORE_PASS_ACL:
771 : 1935 : haveACL = true;
772 : 1935 : break;
2329 773 : 1476 : case RESTORE_PASS_POST_ACL:
774 : 1476 : havePostACL = true;
3278 775 : 1476 : break;
776 : : }
777 : : }
778 : :
779 [ + + ]: 186 : if (haveACL)
780 : : {
781 [ + + ]: 42146 : for (te = AH->toc->next; te != AH->toc; te = te->next)
782 : : {
520 jdavis@postgresql.or 783 [ + + + + ]: 83056 : if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) != 0 &&
477 nathan@postgresql.or 784 : 40987 : _tocEntryRestorePass(te) == RESTORE_PASS_ACL)
3278 tgl@sss.pgh.pa.us 785 : 1935 : (void) restore_toc_entry(AH, te, false);
786 : : }
787 : : }
788 : :
2329 789 [ + + ]: 186 : if (havePostACL)
790 : : {
3278 791 [ + + ]: 28090 : for (te = AH->toc->next; te != AH->toc; te = te->next)
792 : : {
520 jdavis@postgresql.or 793 [ + + + + ]: 55206 : if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) != 0 &&
477 nathan@postgresql.or 794 : 27169 : _tocEntryRestorePass(te) == RESTORE_PASS_POST_ACL)
3278 tgl@sss.pgh.pa.us 795 : 1476 : (void) restore_toc_entry(AH, te, false);
796 : : }
797 : : }
798 : : }
799 : :
800 : : /*
801 : : * Close out any persistent transaction we may have. While these two
802 : : * cases are started in different places, we can end both cases here.
803 : : */
845 804 [ + - + + ]: 190 : if (ropt->single_txn || ropt->txn_size > 0)
805 : : {
7466 806 [ + - ]: 32 : if (AH->connection)
4302 alvherre@alvh.no-ip. 807 : 32 : CommitTransaction(AHX);
808 : : else
7466 tgl@sss.pgh.pa.us 809 :UBC 0 : ahprintf(AH, "COMMIT;\n\n");
810 : : }
811 : :
7771 tgl@sss.pgh.pa.us 812 [ + + ]:CBC 190 : if (AH->public.verbose)
813 : 43 : dumpTimestamp(AH, "Completed on", time(NULL));
814 : :
7799 815 : 190 : ahprintf(AH, "--\n-- PostgreSQL database dump complete\n--\n\n");
816 : :
817 : : /*
818 : : * If generating plain-text output, exit restricted mode at the very end
819 : : * of the script. This is not pro forma; in particular, pg_dumpall
820 : : * requires this when transitioning from one database to another.
821 : : */
348 nathan@postgresql.or 822 [ + + ]: 190 : if (ropt->restrict_key)
823 : 152 : ahprintf(AH, "\\unrestrict %s\n\n", ropt->restrict_key);
824 : :
825 : : /*
826 : : * Clean up & we're done.
827 : : */
8009 bruce@momjian.us 828 : 190 : AH->stage = STAGE_FINALIZING;
829 : :
1331 michael@paquier.xyz 830 [ + + - + ]: 190 : if (ropt->filename || ropt->compression_spec.algorithm != PG_COMPRESSION_NONE)
5663 tgl@sss.pgh.pa.us 831 : 151 : RestoreOutput(AH, sav);
832 : :
8179 833 [ + + ]: 190 : if (ropt->useDB)
5273 rhaas@postgresql.org 834 : 36 : DisconnectDatabase(&AH->public);
9517 bruce@momjian.us 835 : 190 : }
836 : :
837 : : /*
838 : : * Restore a single TOC item. Used in both parallel and non-parallel restore;
839 : : * is_parallel is true if we are in a worker child process.
840 : : *
841 : : * Returns 0 normally, but WORKER_CREATE_DONE or WORKER_INHIBIT_DATA if
842 : : * the parallel parent has to make the corresponding status update.
843 : : */
844 : : static int
3846 tgl@sss.pgh.pa.us 845 : 42204 : restore_toc_entry(ArchiveHandle *AH, TocEntry *te, bool is_parallel)
846 : : {
847 : 42204 : RestoreOptions *ropt = AH->public.ropt;
4871 andrew@dunslane.net 848 : 42204 : int status = WORKER_OK;
849 : : int reqs;
850 : : bool defnDumped;
851 : :
6382 852 : 42204 : AH->currentTE = te;
853 : :
854 : : /* Dump any relevant dump warnings to stderr */
855 [ + + - + ]: 42204 : if (!ropt->suppressDumpWarnings && strcmp(te->desc, "WARNING") == 0)
856 : : {
607 nathan@postgresql.or 857 [ # # # # :UBC 0 : if (ropt->dumpSchema && te->defn != NULL && strlen(te->defn) != 0)
# # ]
2672 peter@eisentraut.org 858 : 0 : pg_log_warning("warning from original dump file: %s", te->defn);
6382 andrew@dunslane.net 859 [ # # # # ]: 0 : else if (te->copyStmt != NULL && strlen(te->copyStmt) != 0)
2672 peter@eisentraut.org 860 : 0 : pg_log_warning("warning from original dump file: %s", te->copyStmt);
861 : : }
862 : :
863 : : /* Work out what, if anything, we want from this entry */
3103 tgl@sss.pgh.pa.us 864 :CBC 42204 : reqs = te->reqs;
865 : :
6382 andrew@dunslane.net 866 : 42204 : defnDumped = false;
867 : :
868 : : /*
869 : : * If it has a schema component that we want, then process that
870 : : */
3278 tgl@sss.pgh.pa.us 871 [ + + ]: 42204 : if ((reqs & REQ_SCHEMA) != 0)
872 : : {
845 873 : 33596 : bool object_is_db = false;
874 : :
875 : : /*
876 : : * In --transaction-size mode, must exit our transaction block to
877 : : * create a database or set its properties.
878 : : */
879 [ + + ]: 33596 : if (strcmp(te->desc, "DATABASE") == 0 ||
880 [ + + ]: 33532 : strcmp(te->desc, "DATABASE PROPERTIES") == 0)
881 : : {
882 : 100 : object_is_db = true;
883 [ + + ]: 100 : if (ropt->txn_size > 0)
884 : : {
885 [ + - ]: 64 : if (AH->connection)
886 : 64 : CommitTransaction(&AH->public);
887 : : else
845 tgl@sss.pgh.pa.us 888 :UBC 0 : ahprintf(AH, "COMMIT;\n\n");
889 : : }
890 : : }
891 : :
892 : : /* Show namespace in log message if available */
4351 heikki.linnakangas@i 893 [ + + ]:CBC 33596 : if (te->namespace)
2672 peter@eisentraut.org 894 : 32036 : pg_log_info("creating %s \"%s.%s\"",
895 : : te->desc, te->namespace, te->tag);
896 : : else
897 : 1560 : pg_log_info("creating %s \"%s\"",
898 : : te->desc, te->tag);
899 : :
520 jdavis@postgresql.or 900 : 33596 : _printTocEntry(AH, te, TOC_PREFIX_NONE);
6382 andrew@dunslane.net 901 : 33596 : defnDumped = true;
902 : :
903 [ + + ]: 33596 : if (strcmp(te->desc, "TABLE") == 0)
904 : : {
905 [ - + ]: 5679 : if (AH->lastErrorTE == te)
906 : : {
907 : : /*
908 : : * We failed to create the table. If
909 : : * --no-data-for-failed-tables was given, mark the
910 : : * corresponding TABLE DATA to be ignored.
911 : : *
912 : : * In the parallel case this must be done in the parent, so we
913 : : * just set the return value.
914 : : */
6382 andrew@dunslane.net 915 [ # # ]:UBC 0 : if (ropt->noDataForFailedTables)
916 : : {
917 [ # # ]: 0 : if (is_parallel)
4871 918 : 0 : status = WORKER_INHIBIT_DATA;
919 : : else
6382 920 : 0 : inhibit_data_for_failed_table(AH, te);
921 : : }
922 : : }
923 : : else
924 : : {
925 : : /*
926 : : * We created the table successfully. Mark the corresponding
927 : : * TABLE DATA for possible truncation.
928 : : *
929 : : * In the parallel case this must be done in the parent, so we
930 : : * just set the return value.
931 : : */
6382 andrew@dunslane.net 932 [ - + ]:CBC 5679 : if (is_parallel)
4871 andrew@dunslane.net 933 :UBC 0 : status = WORKER_CREATE_DONE;
934 : : else
6382 andrew@dunslane.net 935 :CBC 5679 : mark_create_done(AH, te);
936 : : }
937 : : }
938 : :
939 : : /*
940 : : * If we created a DB, connect to it. Also, if we changed DB
941 : : * properties, reconnect to ensure that relevant GUC settings are
942 : : * applied to our session. (That also restarts the transaction block
943 : : * in --transaction-size mode.)
944 : : */
845 tgl@sss.pgh.pa.us 945 [ + + ]: 33596 : if (object_is_db)
946 : : {
2672 peter@eisentraut.org 947 : 100 : pg_log_info("connecting to new database \"%s\"", te->tag);
6382 andrew@dunslane.net 948 : 100 : _reconnectToDB(AH, te->tag);
949 : : }
950 : : }
951 : :
952 : : /*
953 : : * If it has a data component that we want, then process that
954 : : */
955 [ + + ]: 42204 : if ((reqs & REQ_DATA) != 0)
956 : : {
957 : : /*
958 : : * hadDumper will be set if there is genuine data component for this
959 : : * node. Otherwise, we need to check the defn field for statements
960 : : * that need to be executed in data-only restores.
961 : : */
962 [ + + ]: 5090 : if (te->hadDumper)
963 : : {
964 : : /*
965 : : * If we can output the data, then restore it.
966 : : */
3356 bruce@momjian.us 967 [ + - ]: 4547 : if (AH->PrintTocDataPtr != NULL)
968 : : {
520 jdavis@postgresql.or 969 : 4547 : _printTocEntry(AH, te, TOC_PREFIX_DATA);
970 : :
6382 andrew@dunslane.net 971 [ + + ]: 4547 : if (strcmp(te->desc, "BLOBS") == 0 ||
972 [ - + ]: 4473 : strcmp(te->desc, "BLOB COMMENTS") == 0)
973 : : {
2672 peter@eisentraut.org 974 : 74 : pg_log_info("processing %s", te->desc);
975 : :
6382 andrew@dunslane.net 976 : 74 : _selectOutputSchema(AH, "pg_catalog");
977 : :
978 : : /* Send BLOB COMMENTS data to ExecuteSimpleCommands() */
4426 tgl@sss.pgh.pa.us 979 [ - + ]: 74 : if (strcmp(te->desc, "BLOB COMMENTS") == 0)
4426 tgl@sss.pgh.pa.us 980 :UBC 0 : AH->outputKind = OUTPUT_OTHERDATA;
981 : :
3243 peter_e@gmx.net 982 :CBC 74 : AH->PrintTocDataPtr(AH, te);
983 : :
4426 tgl@sss.pgh.pa.us 984 : 74 : AH->outputKind = OUTPUT_SQLCMDS;
985 : : }
986 : : else
987 : : {
988 : : bool use_truncate;
989 : :
3846 990 : 4473 : _disableTriggersIfNecessary(AH, te);
991 : :
992 : : /* Select owner and schema as necessary */
6382 andrew@dunslane.net 993 : 4473 : _becomeOwner(AH, te);
994 : 4473 : _selectOutputSchema(AH, te->namespace);
995 : :
2672 peter@eisentraut.org 996 : 4473 : pg_log_info("processing data for table \"%s.%s\"",
997 : : te->namespace, te->tag);
998 : :
999 : : /*
1000 : : * In parallel restore, if we created the table earlier in
1001 : : * this run (so that we know it is empty) and we are not
1002 : : * restoring a load-via-partition-root data item then we
1003 : : * wrap the COPY in a transaction and precede it with a
1004 : : * TRUNCATE. If wal_level is set to minimal this prevents
1005 : : * WAL-logging the COPY. This obtains a speedup similar
1006 : : * to that from using single_txn mode in non-parallel
1007 : : * restores.
1008 : : *
1009 : : * We mustn't do this for load-via-partition-root cases
1010 : : * because some data might get moved across partition
1011 : : * boundaries, risking deadlock and/or loss of previously
1012 : : * loaded data. (We assume that all partitions of a
1013 : : * partitioned table will be treated the same way.)
1014 : : */
1226 tgl@sss.pgh.pa.us 1015 [ + + + - ]: 4489 : use_truncate = is_parallel && te->created &&
1016 [ + + ]: 16 : !is_load_via_partition_root(te);
1017 : :
1018 [ + + ]: 4473 : if (use_truncate)
1019 : : {
1020 : : /*
1021 : : * Parallel restore is always talking directly to a
1022 : : * server, so no need to see if we should issue BEGIN.
1023 : : */
4302 alvherre@alvh.no-ip. 1024 : 10 : StartTransaction(&AH->public);
1025 : :
1026 : : /*
1027 : : * Issue TRUNCATE with ONLY so that child tables are
1028 : : * not wiped.
1029 : : */
1684 tgl@sss.pgh.pa.us 1030 : 10 : ahprintf(AH, "TRUNCATE TABLE ONLY %s;\n\n",
2899 1031 : 10 : fmtQualifiedId(te->namespace, te->tag));
1032 : : }
1033 : :
1034 : : /*
1035 : : * If we have a copy statement, use it.
1036 : : */
6382 andrew@dunslane.net 1037 [ + + + - ]: 4473 : if (te->copyStmt && strlen(te->copyStmt) > 0)
1038 : : {
1039 : 4386 : ahprintf(AH, "%s", te->copyStmt);
5314 tgl@sss.pgh.pa.us 1040 : 4386 : AH->outputKind = OUTPUT_COPYDATA;
1041 : : }
1042 : : else
1043 : 87 : AH->outputKind = OUTPUT_OTHERDATA;
1044 : :
3243 peter_e@gmx.net 1045 : 4473 : AH->PrintTocDataPtr(AH, te);
1046 : :
1047 : : /*
1048 : : * Terminate COPY if needed.
1049 : : */
5314 tgl@sss.pgh.pa.us 1050 [ + + + + ]: 8857 : if (AH->outputKind == OUTPUT_COPYDATA &&
1051 : 4385 : RestoringToDB(AH))
4302 alvherre@alvh.no-ip. 1052 : 41 : EndDBCopyMode(&AH->public, te->tag);
5314 tgl@sss.pgh.pa.us 1053 : 4472 : AH->outputKind = OUTPUT_SQLCMDS;
1054 : :
1055 : : /* close out the transaction started above */
1226 1056 [ + + ]: 4472 : if (use_truncate)
4302 alvherre@alvh.no-ip. 1057 : 10 : CommitTransaction(&AH->public);
1058 : :
3846 tgl@sss.pgh.pa.us 1059 : 4472 : _enableTriggersIfNecessary(AH, te);
1060 : : }
1061 : : }
1062 : : }
6382 andrew@dunslane.net 1063 [ + - ]: 543 : else if (!defnDumped)
1064 : : {
1065 : : /* If we haven't already dumped the defn part, do so now */
2672 peter@eisentraut.org 1066 : 543 : pg_log_info("executing %s %s", te->desc, te->tag);
520 jdavis@postgresql.or 1067 : 543 : _printTocEntry(AH, te, TOC_PREFIX_NONE);
1068 : : }
1069 : : }
1070 : :
1071 : : /*
1072 : : * If it has a statistics component that we want, then process that
1073 : : */
1074 [ + + ]: 42203 : if ((reqs & REQ_STATS) != 0)
1075 : 3503 : _printTocEntry(AH, te, TOC_PREFIX_STATS);
1076 : :
1077 : : /*
1078 : : * If we emitted anything for this TOC entry, that counts as one action
1079 : : * against the transaction-size limit. Commit if it's time to.
1080 : : */
1081 [ + + + + ]: 42203 : if ((reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) != 0 && ropt->txn_size > 0)
1082 : : {
845 tgl@sss.pgh.pa.us 1083 [ + + ]: 3716 : if (++AH->txnCount >= ropt->txn_size)
1084 : : {
1085 [ + - ]: 11 : if (AH->connection)
1086 : : {
1087 : 11 : CommitTransaction(&AH->public);
1088 : 11 : StartTransaction(&AH->public);
1089 : : }
1090 : : else
845 tgl@sss.pgh.pa.us 1091 :UBC 0 : ahprintf(AH, "COMMIT;\nBEGIN;\n\n");
845 tgl@sss.pgh.pa.us 1092 :CBC 11 : AH->txnCount = 0;
1093 : : }
1094 : : }
1095 : :
4871 andrew@dunslane.net 1096 [ - + - - ]: 42203 : if (AH->public.n_errors > 0 && status == WORKER_OK)
4871 andrew@dunslane.net 1097 :UBC 0 : status = WORKER_IGNORED_ERRORS;
1098 : :
4871 andrew@dunslane.net 1099 :CBC 42203 : return status;
1100 : : }
1101 : :
1102 : : /*
1103 : : * Allocate a new RestoreOptions block.
1104 : : * This is mainly so we can initialize it, but also for future expansion,
1105 : : */
1106 : : RestoreOptions *
9256 bruce@momjian.us 1107 : 294 : NewRestoreOptions(void)
1108 : : {
1109 : : RestoreOptions *opts;
1110 : :
162 michael@paquier.xyz 1111 : 294 : opts = pg_malloc0_object(RestoreOptions);
1112 : :
1113 : : /* set any fields that shouldn't default to zeroes */
9517 bruce@momjian.us 1114 : 294 : opts->format = archUnknown;
2130 tgl@sss.pgh.pa.us 1115 : 294 : opts->cparams.promptPassword = TRI_DEFAULT;
5335 andrew@dunslane.net 1116 : 294 : opts->dumpSections = DUMP_UNSECTIONED;
1331 michael@paquier.xyz 1117 : 294 : opts->compression_spec.algorithm = PG_COMPRESSION_NONE;
1118 : 294 : opts->compression_spec.level = 0;
607 nathan@postgresql.or 1119 : 294 : opts->dumpSchema = true;
1120 : 294 : opts->dumpData = true;
520 jdavis@postgresql.or 1121 : 294 : opts->dumpStatistics = true;
1122 : :
9517 bruce@momjian.us 1123 : 294 : return opts;
1124 : : }
1125 : :
1126 : : static void
3846 tgl@sss.pgh.pa.us 1127 : 4473 : _disableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te)
1128 : : {
1129 : 4473 : RestoreOptions *ropt = AH->public.ropt;
1130 : :
1131 : : /* This hack is only needed in a data-only restore */
607 nathan@postgresql.or 1132 [ + + + + ]: 4473 : if (ropt->dumpSchema || !ropt->disable_triggers)
9489 pjw@rhyme.com.au 1133 : 4434 : return;
1134 : :
2672 peter@eisentraut.org 1135 : 39 : pg_log_info("disabling triggers for %s", te->tag);
1136 : :
1137 : : /*
1138 : : * Become superuser if possible, since they are the only ones who can
1139 : : * disable constraint triggers. If -S was not given, assume the initial
1140 : : * user identity is a superuser. (XXX would it be better to become the
1141 : : * table owner?)
1142 : : */
8341 tgl@sss.pgh.pa.us 1143 : 39 : _becomeUser(AH, ropt->superuser);
1144 : :
1145 : : /*
1146 : : * Disable them.
1147 : : */
7641 1148 : 39 : ahprintf(AH, "ALTER TABLE %s DISABLE TRIGGER ALL;\n\n",
2899 1149 : 39 : fmtQualifiedId(te->namespace, te->tag));
1150 : : }
1151 : :
1152 : : static void
3846 1153 : 4472 : _enableTriggersIfNecessary(ArchiveHandle *AH, TocEntry *te)
1154 : : {
1155 : 4472 : RestoreOptions *ropt = AH->public.ropt;
1156 : :
1157 : : /* This hack is only needed in a data-only restore */
607 nathan@postgresql.or 1158 [ + + + + ]: 4472 : if (ropt->dumpSchema || !ropt->disable_triggers)
9489 pjw@rhyme.com.au 1159 : 4433 : return;
1160 : :
2672 peter@eisentraut.org 1161 : 39 : pg_log_info("enabling triggers for %s", te->tag);
1162 : :
1163 : : /*
1164 : : * Become superuser if possible, since they are the only ones who can
1165 : : * disable constraint triggers. If -S was not given, assume the initial
1166 : : * user identity is a superuser. (XXX would it be better to become the
1167 : : * table owner?)
1168 : : */
8341 tgl@sss.pgh.pa.us 1169 : 39 : _becomeUser(AH, ropt->superuser);
1170 : :
1171 : : /*
1172 : : * Enable them.
1173 : : */
7641 1174 : 39 : ahprintf(AH, "ALTER TABLE %s ENABLE TRIGGER ALL;\n\n",
2899 1175 : 39 : fmtQualifiedId(te->namespace, te->tag));
1176 : : }
1177 : :
1178 : : /*
1179 : : * Detect whether a TABLE DATA TOC item is performing "load via partition
1180 : : * root", that is the target table is an ancestor partition rather than the
1181 : : * table the TOC item is nominally for.
1182 : : *
1183 : : * In newer archive files this can be detected by checking for a special
1184 : : * comment placed in te->defn. In older files we have to fall back to seeing
1185 : : * if the COPY statement targets the named table or some other one. This
1186 : : * will not work for data dumped as INSERT commands, so we could give a false
1187 : : * negative in that case; fortunately, that's a rarely-used option.
1188 : : */
1189 : : static bool
1226 1190 : 16 : is_load_via_partition_root(TocEntry *te)
1191 : : {
1192 [ + + ]: 16 : if (te->defn &&
1193 [ + - ]: 6 : strncmp(te->defn, "-- load via partition root ", 27) == 0)
1194 : 6 : return true;
1195 [ + + + - ]: 10 : if (te->copyStmt && *te->copyStmt)
1196 : : {
1197 : 6 : PQExpBuffer copyStmt = createPQExpBuffer();
1198 : : bool result;
1199 : :
1200 : : /*
1201 : : * Build the initial part of the COPY as it would appear if the
1202 : : * nominal target table is the actual target. If we see anything
1203 : : * else, it must be a load-via-partition-root case.
1204 : : */
1205 : 6 : appendPQExpBuffer(copyStmt, "COPY %s ",
1206 : 6 : fmtQualifiedId(te->namespace, te->tag));
1207 : 6 : result = strncmp(te->copyStmt, copyStmt->data, copyStmt->len) != 0;
1208 : 6 : destroyPQExpBuffer(copyStmt);
1209 : 6 : return result;
1210 : : }
1211 : : /* Assume it's not load-via-partition-root */
1212 : 4 : return false;
1213 : : }
1214 : :
1215 : : /*
1216 : : * This is a routine that is part of the dumper interface, hence the 'Archive*' parameter.
1217 : : */
1218 : :
1219 : : /* Public */
1220 : : void
8740 peter_e@gmx.net 1221 : 1848872 : WriteData(Archive *AHX, const void *data, size_t dLen)
1222 : : {
9256 bruce@momjian.us 1223 : 1848872 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
1224 : :
9500 pjw@rhyme.com.au 1225 [ - + ]: 1848872 : if (!AH->currToc)
1569 tgl@sss.pgh.pa.us 1226 :UBC 0 : pg_fatal("internal error -- WriteData cannot be called outside the context of a DataDumper routine");
1227 : :
3243 peter_e@gmx.net 1228 :CBC 1848872 : AH->WriteDataPtr(AH, data, dLen);
9517 bruce@momjian.us 1229 : 1848872 : }
1230 : :
1231 : : /*
1232 : : * Create a new TOC entry. The TOC was designed as a TOC, but is now the
1233 : : * repository for all metadata. But the name has stuck.
1234 : : *
1235 : : * The new entry is added to the Archive's TOC list. Most callers can ignore
1236 : : * the result value because nothing else need be done, but a few want to
1237 : : * manipulate the TOC entry further.
1238 : : */
1239 : :
1240 : : /* Public */
1241 : : TocEntry *
2731 alvherre@alvh.no-ip. 1242 : 44112 : ArchiveEntry(Archive *AHX, CatalogId catalogId, DumpId dumpId,
1243 : : ArchiveOpts *opts)
1244 : : {
9256 bruce@momjian.us 1245 : 44112 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
1246 : : TocEntry *newToc;
1247 : :
162 michael@paquier.xyz 1248 : 44112 : newToc = pg_malloc0_object(TocEntry);
1249 : :
8267 tgl@sss.pgh.pa.us 1250 : 44112 : AH->tocCount++;
1251 [ + + ]: 44112 : if (dumpId > AH->maxDumpId)
1252 : 13817 : AH->maxDumpId = dumpId;
1253 : :
9256 bruce@momjian.us 1254 : 44112 : newToc->prev = AH->toc->prev;
1255 : 44112 : newToc->next = AH->toc;
1256 : 44112 : AH->toc->prev->next = newToc;
1257 : 44112 : AH->toc->prev = newToc;
1258 : :
8267 tgl@sss.pgh.pa.us 1259 : 44112 : newToc->catalogId = catalogId;
1260 : 44112 : newToc->dumpId = dumpId;
2731 alvherre@alvh.no-ip. 1261 : 44112 : newToc->section = opts->section;
1262 : :
1263 : 44112 : newToc->tag = pg_strdup(opts->tag);
1264 [ + + ]: 44112 : newToc->namespace = opts->namespace ? pg_strdup(opts->namespace) : NULL;
1265 [ + + ]: 44112 : newToc->tablespace = opts->tablespace ? pg_strdup(opts->tablespace) : NULL;
2698 andres@anarazel.de 1266 [ + + ]: 44112 : newToc->tableam = opts->tableam ? pg_strdup(opts->tableam) : NULL;
824 michael@paquier.xyz 1267 : 44112 : newToc->relkind = opts->relkind;
2647 alvherre@alvh.no-ip. 1268 [ + + ]: 44112 : newToc->owner = opts->owner ? pg_strdup(opts->owner) : NULL;
2731 1269 : 44112 : newToc->desc = pg_strdup(opts->description);
2647 1270 [ + + ]: 44112 : newToc->defn = opts->createStmt ? pg_strdup(opts->createStmt) : NULL;
1271 [ + + ]: 44112 : newToc->dropStmt = opts->dropStmt ? pg_strdup(opts->dropStmt) : NULL;
2731 1272 [ + + ]: 44112 : newToc->copyStmt = opts->copyStmt ? pg_strdup(opts->copyStmt) : NULL;
1273 : :
1274 [ + + ]: 44112 : if (opts->nDeps > 0)
1275 : : {
162 michael@paquier.xyz 1276 : 18044 : newToc->dependencies = pg_malloc_array(DumpId, opts->nDeps);
2731 alvherre@alvh.no-ip. 1277 : 18044 : memcpy(newToc->dependencies, opts->deps, opts->nDeps * sizeof(DumpId));
1278 : 18044 : newToc->nDeps = opts->nDeps;
1279 : : }
1280 : : else
1281 : : {
8267 tgl@sss.pgh.pa.us 1282 : 26068 : newToc->dependencies = NULL;
1283 : 26068 : newToc->nDeps = 0;
1284 : : }
1285 : :
2731 alvherre@alvh.no-ip. 1286 : 44112 : newToc->dataDumper = opts->dumpFn;
1287 : 44112 : newToc->dataDumperArg = opts->dumpArg;
1288 : 44112 : newToc->hadDumper = opts->dumpFn ? true : false;
1289 : :
477 nathan@postgresql.or 1290 : 44112 : newToc->defnDumper = opts->defnFn;
1291 : 44112 : newToc->defnDumperArg = opts->defnArg;
1292 : :
8267 tgl@sss.pgh.pa.us 1293 : 44112 : newToc->formatData = NULL;
2871 1294 : 44112 : newToc->dataLength = 0;
1295 : :
3356 bruce@momjian.us 1296 [ + + ]: 44112 : if (AH->ArchiveEntryPtr != NULL)
3243 peter_e@gmx.net 1297 : 7814 : AH->ArchiveEntryPtr(AH, newToc);
1298 : :
2871 tgl@sss.pgh.pa.us 1299 : 44112 : return newToc;
1300 : : }
1301 : :
1302 : : /* Public */
1303 : : void
3846 1304 : 4 : PrintTOCSummary(Archive *AHX)
1305 : : {
9256 bruce@momjian.us 1306 : 4 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
3846 tgl@sss.pgh.pa.us 1307 : 4 : RestoreOptions *ropt = AH->public.ropt;
1308 : : TocEntry *te;
1331 michael@paquier.xyz 1309 : 4 : pg_compress_specification out_compression_spec = {0};
1310 : : teSection curSection;
1311 : : CompressFileHandle *sav;
1312 : : const char *fmtName;
1313 : : char stamp_str[64];
1314 : :
1315 : : /* TOC is always uncompressed */
1316 : 4 : out_compression_spec.algorithm = PG_COMPRESSION_NONE;
1317 : :
5663 tgl@sss.pgh.pa.us 1318 : 4 : sav = SaveOutput(AH);
9256 bruce@momjian.us 1319 [ - + ]: 4 : if (ropt->filename)
40 andrew@dunslane.net 1320 :UBC 0 : SetOutput(AH, ropt->filename, out_compression_spec);
1321 : :
4290 tgl@sss.pgh.pa.us 1322 [ - + ]:CBC 4 : if (strftime(stamp_str, sizeof(stamp_str), PGDUMP_STRFTIME_FMT,
1323 : 4 : localtime(&AH->createDate)) == 0)
4290 tgl@sss.pgh.pa.us 1324 :UBC 0 : strcpy(stamp_str, "[unknown]");
1325 : :
4341 bruce@momjian.us 1326 :CBC 4 : ahprintf(AH, ";\n; Archive created at %s\n", stamp_str);
1248 tomas.vondra@postgre 1327 : 8 : ahprintf(AH, "; dbname: %s\n; TOC Entries: %d\n; Compression: %s\n",
2731 alvherre@alvh.no-ip. 1328 : 4 : sanitize_line(AH->archdbname, false),
1329 : : AH->tocCount,
1330 : : get_compress_algorithm_name(AH->compression_spec.algorithm));
1331 : :
9256 bruce@momjian.us 1332 [ + + - - ]: 4 : switch (AH->format)
1333 : : {
9500 pjw@rhyme.com.au 1334 : 3 : case archCustom:
1335 : 3 : fmtName = "CUSTOM";
1336 : 3 : break;
4787 fujii@postgresql.org 1337 : 1 : case archDirectory:
1338 : 1 : fmtName = "DIRECTORY";
1339 : 1 : break;
9500 pjw@rhyme.com.au 1340 :UBC 0 : case archTar:
1341 : 0 : fmtName = "TAR";
1342 : 0 : break;
1343 : 0 : default:
1344 : 0 : fmtName = "UNKNOWN";
1345 : : }
1346 : :
3560 peter_e@gmx.net 1347 :CBC 4 : ahprintf(AH, "; Dump Version: %d.%d-%d\n",
1348 : 4 : ARCHIVE_MAJOR(AH->version), ARCHIVE_MINOR(AH->version), ARCHIVE_REV(AH->version));
8677 bruce@momjian.us 1349 : 4 : ahprintf(AH, "; Format: %s\n", fmtName);
228 peter@eisentraut.org 1350 : 4 : ahprintf(AH, "; Integer: %zu bytes\n", AH->intSize);
1351 : 4 : ahprintf(AH, "; Offset: %zu bytes\n", AH->offSize);
7931 tgl@sss.pgh.pa.us 1352 [ + - ]: 4 : if (AH->archiveRemoteVersion)
1353 : 4 : ahprintf(AH, "; Dumped from database version: %s\n",
1354 : : AH->archiveRemoteVersion);
1355 [ + - ]: 4 : if (AH->archiveDumpVersion)
1356 : 4 : ahprintf(AH, "; Dumped by pg_dump version: %s\n",
1357 : : AH->archiveDumpVersion);
1358 : :
8677 bruce@momjian.us 1359 : 4 : ahprintf(AH, ";\n;\n; Selected TOC Entries:\n;\n");
1360 : :
5170 tgl@sss.pgh.pa.us 1361 : 4 : curSection = SECTION_PRE_DATA;
6382 andrew@dunslane.net 1362 [ + + ]: 824 : for (te = AH->toc->next; te != AH->toc; te = te->next)
1363 : : {
1364 : : /* This bit must match ProcessArchiveRestoreOptions' marking logic */
5170 tgl@sss.pgh.pa.us 1365 [ + + ]: 820 : if (te->section != SECTION_NONE)
1366 : 664 : curSection = te->section;
809 1367 : 820 : te->reqs = _tocEntryRequired(te, curSection, AH);
1368 : : /* Now, should we print it? */
5170 1369 [ + - ]: 820 : if (ropt->verbose ||
520 jdavis@postgresql.or 1370 [ + + ]: 820 : (te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) != 0)
1371 : : {
1372 : : char *sanitized_name;
1373 : : char *sanitized_schema;
1374 : : char *sanitized_owner;
1375 : :
1376 : : /*
1377 : : */
2731 alvherre@alvh.no-ip. 1378 : 800 : sanitized_name = sanitize_line(te->tag, false);
1379 : 800 : sanitized_schema = sanitize_line(te->namespace, true);
1380 : 800 : sanitized_owner = sanitize_line(te->owner, false);
1381 : :
7960 tgl@sss.pgh.pa.us 1382 : 800 : ahprintf(AH, "%d; %u %u %s %s %s %s\n", te->dumpId,
1383 : : te->catalogId.tableoid, te->catalogId.oid,
1384 : : te->desc, sanitized_schema, sanitized_name,
1385 : : sanitized_owner);
1386 : :
3424 1387 : 800 : free(sanitized_name);
1388 : 800 : free(sanitized_schema);
1389 : 800 : free(sanitized_owner);
1390 : : }
6382 andrew@dunslane.net 1391 [ - + - - ]: 820 : if (ropt->verbose && te->nDeps > 0)
1392 : : {
1393 : : int i;
1394 : :
6382 andrew@dunslane.net 1395 :UBC 0 : ahprintf(AH, ";\tdepends on:");
1396 [ # # ]: 0 : for (i = 0; i < te->nDeps; i++)
1397 : 0 : ahprintf(AH, " %d", te->dependencies[i]);
1398 : 0 : ahprintf(AH, "\n");
1399 : : }
1400 : : }
1401 : :
1402 : : /* Enforce strict names checking */
3967 teodor@sigaev.ru 1403 [ - + ]:CBC 4 : if (ropt->strict_names)
3967 teodor@sigaev.ru 1404 :UBC 0 : StrictNamesCheck(ropt);
1405 : :
9256 bruce@momjian.us 1406 [ - + ]:CBC 4 : if (ropt->filename)
5663 tgl@sss.pgh.pa.us 1407 :UBC 0 : RestoreOutput(AH, sav);
9517 bruce@momjian.us 1408 :CBC 4 : }
1409 : :
1410 : : /***********
1411 : : * Large Object Archival
1412 : : ***********/
1413 : :
1414 : : /* Called by a dumper to signal start of a LO */
1415 : : int
1328 peter@eisentraut.org 1416 : 85 : StartLO(Archive *AHX, Oid oid)
1417 : : {
9256 bruce@momjian.us 1418 : 85 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
1419 : :
1328 peter@eisentraut.org 1420 [ - + ]: 85 : if (!AH->StartLOPtr)
1569 tgl@sss.pgh.pa.us 1421 :UBC 0 : pg_fatal("large-object output not supported in chosen format");
1422 : :
1328 peter@eisentraut.org 1423 :CBC 85 : AH->StartLOPtr(AH, AH->currToc, oid);
1424 : :
9256 bruce@momjian.us 1425 : 85 : return 1;
1426 : : }
1427 : :
1428 : : /* Called by a dumper to signal end of a LO */
1429 : : int
1328 peter@eisentraut.org 1430 : 85 : EndLO(Archive *AHX, Oid oid)
1431 : : {
9256 bruce@momjian.us 1432 : 85 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
1433 : :
1328 peter@eisentraut.org 1434 [ + - ]: 85 : if (AH->EndLOPtr)
1435 : 85 : AH->EndLOPtr(AH, AH->currToc, oid);
1436 : :
9256 bruce@momjian.us 1437 : 85 : return 1;
1438 : : }
1439 : :
1440 : : /**********
1441 : : * Large Object Restoration
1442 : : **********/
1443 : :
1444 : : /*
1445 : : * Called by a format handler before a group of LOs is restored
1446 : : */
1447 : : void
1328 peter@eisentraut.org 1448 : 16 : StartRestoreLOs(ArchiveHandle *AH)
1449 : : {
3846 tgl@sss.pgh.pa.us 1450 : 16 : RestoreOptions *ropt = AH->public.ropt;
1451 : :
1452 : : /*
1453 : : * LOs must be restored within a transaction block, since we need the LO
1454 : : * handle to stay open while we write it. Establish a transaction unless
1455 : : * there's one being used globally.
1456 : : */
845 1457 [ + - + - ]: 16 : if (!(ropt->single_txn || ropt->txn_size > 0))
1458 : : {
7466 1459 [ - + ]: 16 : if (AH->connection)
4302 alvherre@alvh.no-ip. 1460 :UBC 0 : StartTransaction(&AH->public);
1461 : : else
7466 tgl@sss.pgh.pa.us 1462 :CBC 16 : ahprintf(AH, "BEGIN;\n\n");
1463 : : }
1464 : :
1328 peter@eisentraut.org 1465 : 16 : AH->loCount = 0;
9398 pjw@rhyme.com.au 1466 : 16 : }
1467 : :
1468 : : /*
1469 : : * Called by a format handler after a group of LOs is restored
1470 : : */
1471 : : void
1328 peter@eisentraut.org 1472 : 16 : EndRestoreLOs(ArchiveHandle *AH)
1473 : : {
3846 tgl@sss.pgh.pa.us 1474 : 16 : RestoreOptions *ropt = AH->public.ropt;
1475 : :
845 1476 [ + - + - ]: 16 : if (!(ropt->single_txn || ropt->txn_size > 0))
1477 : : {
7466 1478 [ - + ]: 16 : if (AH->connection)
4302 alvherre@alvh.no-ip. 1479 :UBC 0 : CommitTransaction(&AH->public);
1480 : : else
7466 tgl@sss.pgh.pa.us 1481 :CBC 16 : ahprintf(AH, "COMMIT;\n\n");
1482 : : }
1483 : :
2672 peter@eisentraut.org 1484 : 16 : pg_log_info(ngettext("restored %d large object",
1485 : : "restored %d large objects",
1486 : : AH->loCount),
1487 : : AH->loCount);
9398 pjw@rhyme.com.au 1488 : 16 : }
1489 : :
1490 : :
1491 : : /*
1492 : : * Called by a format handler to initiate restoration of a LO
1493 : : */
1494 : : void
1328 peter@eisentraut.org 1495 : 16 : StartRestoreLO(ArchiveHandle *AH, Oid oid, bool drop)
1496 : : {
1497 : 16 : bool old_lo_style = (AH->version < K_VERS_1_12);
1498 : : Oid loOid;
1499 : :
1500 : 16 : AH->loCount++;
1501 : :
1502 : : /* Initialize the LO Buffer */
845 tgl@sss.pgh.pa.us 1503 [ + + ]: 16 : if (AH->lo_buf == NULL)
1504 : : {
1505 : : /* First time through (in this process) so allocate the buffer */
1506 : 10 : AH->lo_buf_size = LOBBUFSIZE;
604 peter@eisentraut.org 1507 : 10 : AH->lo_buf = pg_malloc(LOBBUFSIZE);
1508 : : }
8858 bruce@momjian.us 1509 : 16 : AH->lo_buf_used = 0;
1510 : :
2672 peter@eisentraut.org 1511 : 16 : pg_log_info("restoring large object with OID %u", oid);
1512 : :
1513 : : /* With an old archive we must do drop and create logic here */
1328 1514 [ - + - - ]: 16 : if (old_lo_style && drop)
1328 peter@eisentraut.org 1515 :UBC 0 : DropLOIfExists(AH, oid);
1516 : :
7704 tgl@sss.pgh.pa.us 1517 [ - + ]:CBC 16 : if (AH->connection)
1518 : : {
1328 peter@eisentraut.org 1519 [ # # ]:UBC 0 : if (old_lo_style)
1520 : : {
6001 tgl@sss.pgh.pa.us 1521 : 0 : loOid = lo_create(AH->connection, oid);
1522 [ # # # # ]: 0 : if (loOid == 0 || loOid != oid)
1569 1523 : 0 : pg_fatal("could not create large object %u: %s",
1524 : : oid, PQerrorMessage(AH->connection));
1525 : : }
7704 1526 : 0 : AH->loFd = lo_open(AH->connection, oid, INV_WRITE);
1527 [ # # ]: 0 : if (AH->loFd == -1)
1569 1528 : 0 : pg_fatal("could not open large object %u: %s",
1529 : : oid, PQerrorMessage(AH->connection));
1530 : : }
1531 : : else
1532 : : {
1328 peter@eisentraut.org 1533 [ - + ]:CBC 16 : if (old_lo_style)
6001 tgl@sss.pgh.pa.us 1534 :UBC 0 : ahprintf(AH, "SELECT pg_catalog.lo_open(pg_catalog.lo_create('%u'), %d);\n",
1535 : : oid, INV_WRITE);
1536 : : else
6001 tgl@sss.pgh.pa.us 1537 :CBC 16 : ahprintf(AH, "SELECT pg_catalog.lo_open('%u', %d);\n",
1538 : : oid, INV_WRITE);
1539 : : }
1540 : :
1328 peter@eisentraut.org 1541 : 16 : AH->writingLO = true;
9500 pjw@rhyme.com.au 1542 : 16 : }
1543 : :
1544 : : void
1328 peter@eisentraut.org 1545 : 16 : EndRestoreLO(ArchiveHandle *AH, Oid oid)
1546 : : {
8823 tgl@sss.pgh.pa.us 1547 [ + + ]: 16 : if (AH->lo_buf_used > 0)
1548 : : {
1549 : : /* Write remaining bytes from the LO buffer */
7704 1550 : 10 : dump_lo_buf(AH);
1551 : : }
1552 : :
1328 peter@eisentraut.org 1553 : 16 : AH->writingLO = false;
1554 : :
7704 tgl@sss.pgh.pa.us 1555 [ - + ]: 16 : if (AH->connection)
1556 : : {
7704 tgl@sss.pgh.pa.us 1557 :UBC 0 : lo_close(AH->connection, AH->loFd);
1558 : 0 : AH->loFd = -1;
1559 : : }
1560 : : else
1561 : : {
6213 tgl@sss.pgh.pa.us 1562 :CBC 16 : ahprintf(AH, "SELECT pg_catalog.lo_close(0);\n\n");
1563 : : }
9500 pjw@rhyme.com.au 1564 : 16 : }
1565 : :
1566 : : /***********
1567 : : * Sorting and Reordering
1568 : : ***********/
1569 : :
1570 : : void
3846 tgl@sss.pgh.pa.us 1571 :UBC 0 : SortTocFromFile(Archive *AHX)
1572 : : {
9256 bruce@momjian.us 1573 : 0 : ArchiveHandle *AH = (ArchiveHandle *) AHX;
3846 tgl@sss.pgh.pa.us 1574 : 0 : RestoreOptions *ropt = AH->public.ropt;
1575 : : FILE *fh;
1576 : : StringInfoData linebuf;
1577 : :
1578 : : /* Allocate space for the 'wanted' array, and init it */
162 michael@paquier.xyz 1579 : 0 : ropt->idWanted = pg_malloc0_array(bool, AH->maxDumpId);
1580 : :
1581 : : /* Setup the file */
9256 bruce@momjian.us 1582 : 0 : fh = fopen(ropt->tocFile, PG_BINARY_R);
1583 [ # # ]: 0 : if (!fh)
1569 tgl@sss.pgh.pa.us 1584 : 0 : pg_fatal("could not open TOC file \"%s\": %m", ropt->tocFile);
1585 : :
2132 1586 : 0 : initStringInfo(&linebuf);
1587 : :
1588 [ # # ]: 0 : while (pg_get_line_buf(fh, &linebuf))
1589 : : {
1590 : : char *cmnt;
1591 : : char *endptr;
1592 : : DumpId id;
1593 : : TocEntry *te;
1594 : :
1595 : : /* Truncate line at comment, if any */
1596 : 0 : cmnt = strchr(linebuf.data, ';');
9256 bruce@momjian.us 1597 [ # # ]: 0 : if (cmnt != NULL)
1598 : : {
1599 : 0 : cmnt[0] = '\0';
2132 tgl@sss.pgh.pa.us 1600 : 0 : linebuf.len = cmnt - linebuf.data;
1601 : : }
1602 : :
1603 : : /* Ignore if all blank */
1604 [ # # ]: 0 : if (strspn(linebuf.data, " \t\r\n") == linebuf.len)
9256 bruce@momjian.us 1605 : 0 : continue;
1606 : :
1607 : : /* Get an ID, check it's valid and not already seen */
2132 tgl@sss.pgh.pa.us 1608 : 0 : id = strtol(linebuf.data, &endptr, 10);
1609 [ # # # # : 0 : if (endptr == linebuf.data || id <= 0 || id > AH->maxDumpId ||
# # ]
7739 1610 [ # # ]: 0 : ropt->idWanted[id - 1])
1611 : : {
2132 1612 : 0 : pg_log_warning("line ignored: %s", linebuf.data);
9256 bruce@momjian.us 1613 : 0 : continue;
1614 : : }
1615 : :
1616 : : /* Find TOC entry */
8267 tgl@sss.pgh.pa.us 1617 : 0 : te = getTocEntryByDumpId(AH, id);
9256 bruce@momjian.us 1618 [ # # ]: 0 : if (!te)
1569 tgl@sss.pgh.pa.us 1619 : 0 : pg_fatal("could not find entry for ID %d",
1620 : : id);
1621 : :
1622 : : /* Mark it wanted */
8267 1623 : 0 : ropt->idWanted[id - 1] = true;
1624 : :
1625 : : /*
1626 : : * Move each item to the end of the list as it is selected, so that
1627 : : * they are placed in the desired order. Any unwanted items will end
1628 : : * up at the front of the list, which may seem unintuitive but it's
1629 : : * what we need. In an ordinary serial restore that makes no
1630 : : * difference, but in a parallel restore we need to mark unrestored
1631 : : * items' dependencies as satisfied before we start examining
1632 : : * restorable items. Otherwise they could have surprising
1633 : : * side-effects on the order in which restorable items actually get
1634 : : * restored.
1635 : : */
2160 peter@eisentraut.org 1636 : 0 : _moveBefore(AH->toc, te);
1637 : : }
1638 : :
2132 tgl@sss.pgh.pa.us 1639 : 0 : pg_free(linebuf.data);
1640 : :
9256 bruce@momjian.us 1641 [ # # ]: 0 : if (fclose(fh) != 0)
1569 tgl@sss.pgh.pa.us 1642 : 0 : pg_fatal("could not close TOC file: %m");
9517 bruce@momjian.us 1643 : 0 : }
1644 : :
1645 : : /**********************
1646 : : * Convenience functions that look like standard IO functions
1647 : : * for writing data when in dump mode.
1648 : : **********************/
1649 : :
1650 : : /* Public */
1651 : : void
9256 bruce@momjian.us 1652 :CBC 24430 : archputs(const char *s, Archive *AH)
1653 : : {
4464 1654 : 24430 : WriteData(AH, s, strlen(s));
9517 1655 : 24430 : }
1656 : :
1657 : : /* Public */
1658 : : int
73 tgl@sss.pgh.pa.us 1659 : 4439 : archprintf(Archive *AH, const char *fmt, ...)
1660 : : {
2859 1661 : 4439 : int save_errno = errno;
1662 : : char *p;
4657 1663 : 4439 : size_t len = 128; /* initial assumption about buffer size */
1664 : : size_t cnt;
1665 : :
1666 : : for (;;)
9517 bruce@momjian.us 1667 :UBC 0 : {
1668 : : va_list args;
1669 : :
1670 : : /* Allocate work buffer. */
4657 tgl@sss.pgh.pa.us 1671 :CBC 4439 : p = (char *) pg_malloc(len);
1672 : :
1673 : : /* Try to format the data. */
2859 1674 : 4439 : errno = save_errno;
4657 1675 : 4439 : va_start(args, fmt);
1676 : 4439 : cnt = pvsnprintf(p, len, fmt, args);
1677 : 4439 : va_end(args);
1678 : :
1679 [ + - ]: 4439 : if (cnt < len)
1680 : 4439 : break; /* success */
1681 : :
1682 : : /* Release buffer and loop around to try again with larger len. */
24 peter@eisentraut.org 1683 :UNC 0 : pg_free(p);
4657 tgl@sss.pgh.pa.us 1684 :UBC 0 : len = cnt;
1685 : : }
1686 : :
9256 bruce@momjian.us 1687 :CBC 4439 : WriteData(AH, p, cnt);
24 peter@eisentraut.org 1688 :GNC 4439 : pg_free(p);
4657 tgl@sss.pgh.pa.us 1689 :CBC 4439 : return (int) cnt;
1690 : : }
1691 : :
1692 : :
1693 : : /*******************************
1694 : : * Stuff below here should be 'private' to the archiver routines
1695 : : *******************************/
1696 : :
1697 : : static void
1331 michael@paquier.xyz 1698 : 151 : SetOutput(ArchiveHandle *AH, const char *filename,
1699 : : const pg_compress_specification compression_spec)
1700 : : {
1701 : : CompressFileHandle *CFH;
1702 : : const char *mode;
1248 tomas.vondra@postgre 1703 : 151 : int fn = -1;
1704 : :
9256 bruce@momjian.us 1705 [ + - ]: 151 : if (filename)
1706 : : {
2669 alvherre@alvh.no-ip. 1707 [ - + ]: 151 : if (strcmp(filename, "-") == 0)
2669 alvherre@alvh.no-ip. 1708 :UBC 0 : fn = fileno(stdout);
1709 : : }
9256 bruce@momjian.us 1710 [ # # ]: 0 : else if (AH->FH)
1711 : 0 : fn = fileno(AH->FH);
1712 [ # # ]: 0 : else if (AH->fSpec)
1713 : : {
1714 : 0 : filename = AH->fSpec;
1715 : : }
1716 : : else
1717 : 0 : fn = fileno(stdout);
1718 : :
40 andrew@dunslane.net 1719 [ + + ]:CBC 151 : if (AH->mode == archModeAppend)
1248 tomas.vondra@postgre 1720 : 48 : mode = PG_BINARY_A;
1721 : : else
1722 : 103 : mode = PG_BINARY_W;
1723 : :
1724 : 151 : CFH = InitCompressFileHandle(compression_spec);
1725 : :
1220 1726 [ - + ]: 151 : if (!CFH->open_func(filename, fn, mode, CFH))
1727 : : {
6845 tgl@sss.pgh.pa.us 1728 [ # # ]:UBC 0 : if (filename)
1569 1729 : 0 : pg_fatal("could not open output file \"%s\": %m", filename);
1730 : : else
1731 : 0 : pg_fatal("could not open output file: %m");
1732 : : }
1733 : :
1248 tomas.vondra@postgre 1734 :CBC 151 : AH->OF = CFH;
5663 tgl@sss.pgh.pa.us 1735 : 151 : }
1736 : :
1737 : : static CompressFileHandle *
1738 : 195 : SaveOutput(ArchiveHandle *AH)
1739 : : {
1248 tomas.vondra@postgre 1740 : 195 : return (CompressFileHandle *) AH->OF;
1741 : : }
1742 : :
1743 : : static void
1744 : 151 : RestoreOutput(ArchiveHandle *AH, CompressFileHandle *savedOutput)
1745 : : {
1746 : 151 : errno = 0;
1220 1747 [ - + ]: 151 : if (!EndCompressFileHandle(AH->OF))
1569 tgl@sss.pgh.pa.us 1748 :UBC 0 : pg_fatal("could not close output file: %m");
1749 : :
1248 tomas.vondra@postgre 1750 :CBC 151 : AH->OF = savedOutput;
9517 bruce@momjian.us 1751 : 151 : }
1752 : :
1753 : :
1754 : :
1755 : : /*
1756 : : * Print formatted text to the output file (usually stdout).
1757 : : */
1758 : : int
73 tgl@sss.pgh.pa.us 1759 : 225359 : ahprintf(ArchiveHandle *AH, const char *fmt, ...)
1760 : : {
2859 1761 : 225359 : int save_errno = errno;
1762 : : char *p;
4657 1763 : 225359 : size_t len = 128; /* initial assumption about buffer size */
1764 : : size_t cnt;
1765 : :
1766 : : for (;;)
9283 1767 : 14491 : {
1768 : : va_list args;
1769 : :
1770 : : /* Allocate work buffer. */
4657 1771 : 239850 : p = (char *) pg_malloc(len);
1772 : :
1773 : : /* Try to format the data. */
2859 1774 : 239850 : errno = save_errno;
4657 1775 : 239850 : va_start(args, fmt);
1776 : 239850 : cnt = pvsnprintf(p, len, fmt, args);
1777 : 239850 : va_end(args);
1778 : :
1779 [ + + ]: 239850 : if (cnt < len)
1780 : 225359 : break; /* success */
1781 : :
1782 : : /* Release buffer and loop around to try again with larger len. */
24 peter@eisentraut.org 1783 :GNC 14491 : pg_free(p);
4657 tgl@sss.pgh.pa.us 1784 :CBC 14491 : len = cnt;
1785 : : }
1786 : :
9256 bruce@momjian.us 1787 : 225359 : ahwrite(p, 1, cnt, AH);
24 peter@eisentraut.org 1788 :GNC 225359 : pg_free(p);
4657 tgl@sss.pgh.pa.us 1789 :CBC 225359 : return (int) cnt;
1790 : : }
1791 : :
1792 : : /*
1793 : : * Single place for logic which says 'We are restoring to a direct DB connection'.
1794 : : */
1795 : : static int
9256 bruce@momjian.us 1796 : 2050891 : RestoringToDB(ArchiveHandle *AH)
1797 : : {
3846 tgl@sss.pgh.pa.us 1798 : 2050891 : RestoreOptions *ropt = AH->public.ropt;
1799 : :
1800 [ + - + + : 2050891 : return (ropt && ropt->useDB && AH->connection);
+ - ]
1801 : : }
1802 : :
1803 : : /*
1804 : : * Dump the current contents of the LO data buffer while writing a LO
1805 : : */
1806 : : static void
7704 1807 : 10 : dump_lo_buf(ArchiveHandle *AH)
1808 : : {
1809 [ - + ]: 10 : if (AH->connection)
1810 : : {
1811 : : int res;
1812 : :
7704 tgl@sss.pgh.pa.us 1813 :UBC 0 : res = lo_write(AH->connection, AH->loFd, AH->lo_buf, AH->lo_buf_used);
2106 1814 [ # # ]: 0 : pg_log_debug(ngettext("wrote %zu byte of large object data (result = %d)",
1815 : : "wrote %zu bytes of large object data (result = %d)",
1816 : : AH->lo_buf_used),
1817 : : AH->lo_buf_used, res);
1818 : : /* We assume there are no short writes, only errors */
7704 1819 [ # # ]: 0 : if (res != AH->lo_buf_used)
2106 1820 : 0 : warn_or_exit_horribly(AH, "could not write to large object: %s",
1821 : 0 : PQerrorMessage(AH->connection));
1822 : : }
1823 : : else
1824 : : {
6199 tgl@sss.pgh.pa.us 1825 :CBC 10 : PQExpBuffer buf = createPQExpBuffer();
1826 : :
1827 : 10 : appendByteaLiteralAHX(buf,
1828 : : (const unsigned char *) AH->lo_buf,
1829 : : AH->lo_buf_used,
1830 : : AH);
1831 : :
1832 : : /* Hack: turn off writingLO so ahwrite doesn't recurse to here */
1328 peter@eisentraut.org 1833 : 10 : AH->writingLO = false;
6199 tgl@sss.pgh.pa.us 1834 : 10 : ahprintf(AH, "SELECT pg_catalog.lowrite(0, %s);\n", buf->data);
1328 peter@eisentraut.org 1835 : 10 : AH->writingLO = true;
1836 : :
6199 tgl@sss.pgh.pa.us 1837 : 10 : destroyPQExpBuffer(buf);
1838 : : }
7704 1839 : 10 : AH->lo_buf_used = 0;
1840 : 10 : }
1841 : :
1842 : :
1843 : : /*
1844 : : * Write buffer to the output file (usually stdout). This is used for
1845 : : * outputting 'restore' scripts etc. It is even possible for an archive
1846 : : * format to create a custom output routine to 'fake' a restore if it
1847 : : * wants to generate a script (see TAR output).
1848 : : */
1849 : : void
9256 bruce@momjian.us 1850 : 2048215 : ahwrite(const void *ptr, size_t size, size_t nmemb, ArchiveHandle *AH)
1851 : : {
4463 1852 : 2048215 : int bytes_written = 0;
1853 : :
1328 peter@eisentraut.org 1854 [ + + ]: 2048215 : if (AH->writingLO)
1855 : : {
7588 bruce@momjian.us 1856 : 13 : size_t remaining = size * nmemb;
1857 : :
7704 tgl@sss.pgh.pa.us 1858 [ - + ]: 13 : while (AH->lo_buf_used + remaining > AH->lo_buf_size)
1859 : : {
7704 tgl@sss.pgh.pa.us 1860 :UBC 0 : size_t avail = AH->lo_buf_size - AH->lo_buf_used;
1861 : :
1862 : 0 : memcpy((char *) AH->lo_buf + AH->lo_buf_used, ptr, avail);
596 peter@eisentraut.org 1863 : 0 : ptr = (const char *) ptr + avail;
7704 tgl@sss.pgh.pa.us 1864 : 0 : remaining -= avail;
1865 : 0 : AH->lo_buf_used += avail;
1866 : 0 : dump_lo_buf(AH);
1867 : : }
1868 : :
7704 tgl@sss.pgh.pa.us 1869 :CBC 13 : memcpy((char *) AH->lo_buf + AH->lo_buf_used, ptr, remaining);
1870 : 13 : AH->lo_buf_used += remaining;
1871 : :
4464 bruce@momjian.us 1872 : 13 : bytes_written = size * nmemb;
1873 : : }
9256 1874 [ + + ]: 2048202 : else if (AH->CustomOutPtr)
3356 1875 : 2369 : bytes_written = AH->CustomOutPtr(AH, ptr, size * nmemb);
1876 : :
1877 : : /*
1878 : : * If we're doing a restore, and it's direct to DB, and we're connected
1879 : : * then send it to the DB.
1880 : : */
1248 tomas.vondra@postgre 1881 [ + + ]: 2045833 : else if (RestoringToDB(AH))
1882 : 6971 : bytes_written = ExecuteSqlCommandBuf(&AH->public, (const char *) ptr, size * nmemb);
1883 : : else
1884 : : {
1885 : 2038862 : CompressFileHandle *CFH = (CompressFileHandle *) AH->OF;
1886 : :
330 dgustafsson@postgres 1887 : 2038862 : CFH->write_func(ptr, size * nmemb, CFH);
1888 : 2038862 : bytes_written = size * nmemb;
1889 : : }
1890 : :
4464 bruce@momjian.us 1891 [ - + ]: 2048215 : if (bytes_written != size * nmemb)
4464 bruce@momjian.us 1892 :UBC 0 : WRITE_ERROR_EXIT;
9256 bruce@momjian.us 1893 :CBC 2048215 : }
1894 : :
1895 : : /* on some error, we may decide to go on... */
1896 : : void
73 tgl@sss.pgh.pa.us 1897 :UBC 0 : warn_or_exit_horribly(ArchiveHandle *AH, const char *fmt, ...)
1898 : : {
1899 : : /* Stay quiet if this is a result of our own cancellation. */
1900 : : if (!is_cancel_in_progress())
1901 : : {
1902 : : va_list ap;
1903 : :
17 heikki.linnakangas@i 1904 [ # # # # :UNC 0 : switch (AH->stage)
# ]
1905 : : {
1906 : :
1907 : 0 : case STAGE_NONE:
1908 : : /* Do nothing special */
1909 : 0 : break;
1910 : :
1911 : 0 : case STAGE_INITIALIZING:
1912 [ # # ]: 0 : if (AH->stage != AH->lastErrorStage)
1913 : 0 : pg_log_info("while INITIALIZING:");
1914 : 0 : break;
1915 : :
1916 : 0 : case STAGE_PROCESSING:
1917 [ # # ]: 0 : if (AH->stage != AH->lastErrorStage)
1918 : 0 : pg_log_info("while PROCESSING TOC:");
1919 : 0 : break;
1920 : :
1921 : 0 : case STAGE_FINALIZING:
1922 [ # # ]: 0 : if (AH->stage != AH->lastErrorStage)
1923 : 0 : pg_log_info("while FINALIZING:");
1924 : 0 : break;
1925 : : }
1926 [ # # # # ]: 0 : if (AH->currentTE != NULL && AH->currentTE != AH->lastErrorTE)
1927 : : {
1928 [ # # # # : 0 : pg_log_info("from TOC entry %d; %u %u %s %s %s",
# # ]
1929 : : AH->currentTE->dumpId,
1930 : : AH->currentTE->catalogId.tableoid,
1931 : : AH->currentTE->catalogId.oid,
1932 : : AH->currentTE->desc ? AH->currentTE->desc : "(no desc)",
1933 : : AH->currentTE->tag ? AH->currentTE->tag : "(no tag)",
1934 : : AH->currentTE->owner ? AH->currentTE->owner : "(no owner)");
1935 : : }
1936 : :
1937 : 0 : va_start(ap, fmt);
1938 : 0 : pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, ap);
1939 : 0 : va_end(ap);
1940 : : }
1941 : :
8009 bruce@momjian.us 1942 :UBC 0 : AH->lastErrorStage = AH->stage;
1943 : 0 : AH->lastErrorTE = AH->currentTE;
1944 : :
1945 [ # # ]: 0 : if (AH->public.exit_on_error)
5240 alvherre@alvh.no-ip. 1946 : 0 : exit_nicely(1);
1947 : : else
8129 bruce@momjian.us 1948 : 0 : AH->public.n_errors++;
1949 : 0 : }
1950 : :
1951 : : #ifdef NOT_USED
1952 : :
1953 : : static void
1954 : : _moveAfter(ArchiveHandle *AH, TocEntry *pos, TocEntry *te)
1955 : : {
1956 : : /* Unlink te from list */
1957 : : te->prev->next = te->next;
1958 : : te->next->prev = te->prev;
1959 : :
1960 : : /* and insert it after "pos" */
1961 : : te->prev = pos;
1962 : : te->next = pos->next;
1963 : : pos->next->prev = te;
1964 : : pos->next = te;
1965 : : }
1966 : : #endif
1967 : :
1968 : : static void
2160 peter@eisentraut.org 1969 : 0 : _moveBefore(TocEntry *pos, TocEntry *te)
1970 : : {
1971 : : /* Unlink te from list */
9256 bruce@momjian.us 1972 : 0 : te->prev->next = te->next;
1973 : 0 : te->next->prev = te->prev;
1974 : :
1975 : : /* and insert it before "pos" */
1976 : 0 : te->prev = pos->prev;
1977 : 0 : te->next = pos;
1978 : 0 : pos->prev->next = te;
1979 : 0 : pos->prev = te;
9517 1980 : 0 : }
1981 : :
1982 : : /*
1983 : : * Build index arrays for the TOC list
1984 : : *
1985 : : * This should be invoked only after we have created or read in all the TOC
1986 : : * items.
1987 : : *
1988 : : * The arrays are indexed by dump ID (so entry zero is unused). Note that the
1989 : : * array entries run only up to maxDumpId. We might see dependency dump IDs
1990 : : * beyond that (if the dump was partial); so always check the array bound
1991 : : * before trying to touch an array entry.
1992 : : */
1993 : : static void
5171 tgl@sss.pgh.pa.us 1994 :CBC 233 : buildTocEntryArrays(ArchiveHandle *AH)
1995 : : {
1996 : 233 : DumpId maxDumpId = AH->maxDumpId;
1997 : : TocEntry *te;
1998 : :
162 michael@paquier.xyz 1999 : 233 : AH->tocsByDumpId = pg_malloc0_array(TocEntry *, (maxDumpId + 1));
2000 : 233 : AH->tableDataId = pg_malloc0_array(DumpId, (maxDumpId + 1));
2001 : :
6382 andrew@dunslane.net 2002 [ + + ]: 51910 : for (te = AH->toc->next; te != AH->toc; te = te->next)
2003 : : {
2004 : : /* this check is purely paranoia, maxDumpId should be correct */
5171 tgl@sss.pgh.pa.us 2005 [ + - - + ]: 51677 : if (te->dumpId <= 0 || te->dumpId > maxDumpId)
1569 tgl@sss.pgh.pa.us 2006 :UBC 0 : pg_fatal("bad dumpId");
2007 : :
2008 : : /* tocsByDumpId indexes all TOCs by their dump ID */
5171 tgl@sss.pgh.pa.us 2009 :CBC 51677 : AH->tocsByDumpId[te->dumpId] = te;
2010 : :
2011 : : /*
2012 : : * tableDataId provides the TABLE DATA item's dump ID for each TABLE
2013 : : * TOC entry that has a DATA item. We compute this by reversing the
2014 : : * TABLE DATA item's dependency, knowing that a TABLE DATA item has
2015 : : * just one dependency and it is the TABLE item.
2016 : : */
2017 [ + + + - ]: 51677 : if (strcmp(te->desc, "TABLE DATA") == 0 && te->nDeps > 0)
2018 : : {
2019 : 5004 : DumpId tableId = te->dependencies[0];
2020 : :
2021 : : /*
2022 : : * The TABLE item might not have been in the archive, if this was
2023 : : * a data-only dump; but its dump ID should be less than its data
2024 : : * item's dump ID, so there should be a place for it in the array.
2025 : : */
2026 [ + - - + ]: 5004 : if (tableId <= 0 || tableId > maxDumpId)
1569 tgl@sss.pgh.pa.us 2027 :UBC 0 : pg_fatal("bad table dumpId for TABLE DATA item");
2028 : :
5171 tgl@sss.pgh.pa.us 2029 :CBC 5004 : AH->tableDataId[tableId] = te->dumpId;
2030 : : }
2031 : : }
2032 : 233 : }
2033 : :
2034 : : TocEntry *
2035 : 12444 : getTocEntryByDumpId(ArchiveHandle *AH, DumpId id)
2036 : : {
2037 : : /* build index arrays if we didn't already */
2038 [ + + ]: 12444 : if (AH->tocsByDumpId == NULL)
2039 : 46 : buildTocEntryArrays(AH);
2040 : :
2041 [ + - + - ]: 12444 : if (id > 0 && id <= AH->maxDumpId)
2042 : 12444 : return AH->tocsByDumpId[id];
2043 : :
9256 bruce@momjian.us 2044 :UBC 0 : return NULL;
2045 : : }
2046 : :
2047 : : int
5170 tgl@sss.pgh.pa.us 2048 :CBC 11853 : TocIDRequired(ArchiveHandle *AH, DumpId id)
2049 : : {
8267 2050 : 11853 : TocEntry *te = getTocEntryByDumpId(AH, id);
2051 : :
9256 bruce@momjian.us 2052 [ + + ]: 11853 : if (!te)
2053 : 5433 : return 0;
2054 : :
5170 tgl@sss.pgh.pa.us 2055 : 6420 : return te->reqs;
2056 : : }
2057 : :
2058 : : size_t
7096 magnus@hagander.net 2059 : 8468 : WriteOffset(ArchiveHandle *AH, pgoff_t o, int wasSet)
2060 : : {
2061 : : /* Save the flag */
3243 peter_e@gmx.net 2062 : 8468 : AH->WriteBytePtr(AH, wasSet);
2063 : :
2064 : : /* Write out pgoff_t smallest byte first, prevents endian mismatch */
14 peter@eisentraut.org 2065 [ + + ]:GNC 76212 : for (size_t off = 0; off < sizeof(pgoff_t); off++)
2066 : : {
3243 peter_e@gmx.net 2067 :CBC 67744 : AH->WriteBytePtr(AH, o & 0xFF);
8677 bruce@momjian.us 2068 : 67744 : o >>= 8;
2069 : : }
7096 magnus@hagander.net 2070 : 8468 : return sizeof(pgoff_t) + 1;
2071 : : }
2072 : :
2073 : : int
254 michael@paquier.xyz 2074 : 6829 : ReadOffset(ArchiveHandle *AH, pgoff_t *o)
2075 : : {
2076 : : int i;
2077 : : int offsetFlg;
2078 : :
2079 : : /* Initialize to zero */
8677 bruce@momjian.us 2080 : 6829 : *o = 0;
2081 : :
2082 : : /* Check for old version */
2083 [ - + ]: 6829 : if (AH->version < K_VERS_1_7)
2084 : : {
2085 : : /* Prior versions wrote offsets using WriteInt */
8677 bruce@momjian.us 2086 :UBC 0 : i = ReadInt(AH);
2087 : : /* -1 means not set */
2088 [ # # ]: 0 : if (i < 0)
8391 2089 : 0 : return K_OFFSET_POS_NOT_SET;
8677 2090 [ # # ]: 0 : else if (i == 0)
8391 2091 : 0 : return K_OFFSET_NO_DATA;
2092 : :
2093 : : /* Cast to pgoff_t because it was written as an int. */
7096 magnus@hagander.net 2094 : 0 : *o = (pgoff_t) i;
8677 bruce@momjian.us 2095 : 0 : return K_OFFSET_POS_SET;
2096 : : }
2097 : :
2098 : : /*
2099 : : * Read the flag indicating the state of the data pointer. Check if valid
2100 : : * and die if not.
2101 : : *
2102 : : * This used to be handled by a negative or zero pointer, now we use an
2103 : : * extra byte specifically for the state.
2104 : : */
3243 peter_e@gmx.net 2105 :CBC 6829 : offsetFlg = AH->ReadBytePtr(AH) & 0xFF;
2106 : :
8677 bruce@momjian.us 2107 [ + - ]: 6829 : switch (offsetFlg)
2108 : : {
2109 : 6829 : case K_OFFSET_POS_NOT_SET:
2110 : : case K_OFFSET_NO_DATA:
2111 : : case K_OFFSET_POS_SET:
2112 : :
8391 2113 : 6829 : break;
2114 : :
8677 bruce@momjian.us 2115 :UBC 0 : default:
1569 tgl@sss.pgh.pa.us 2116 : 0 : pg_fatal("unexpected data offset flag %d", offsetFlg);
2117 : : }
2118 : :
2119 : : /*
2120 : : * Read the bytes
2121 : : */
14 peter@eisentraut.org 2122 [ + + ]:GNC 61461 : for (size_t off = 0; off < AH->offSize; off++)
2123 : : {
7096 magnus@hagander.net 2124 [ + - ]:CBC 54632 : if (off < sizeof(pgoff_t))
3243 peter_e@gmx.net 2125 : 54632 : *o |= ((pgoff_t) (AH->ReadBytePtr(AH))) << (off * 8);
2126 : : else
2127 : : {
3243 peter_e@gmx.net 2128 [ # # ]:UBC 0 : if (AH->ReadBytePtr(AH) != 0)
1569 tgl@sss.pgh.pa.us 2129 : 0 : pg_fatal("file offset in dump file is too large");
2130 : : }
2131 : : }
2132 : :
8677 bruce@momjian.us 2133 :CBC 6829 : return offsetFlg;
2134 : : }
2135 : :
2136 : : size_t
9256 2137 : 186600 : WriteInt(ArchiveHandle *AH, int i)
2138 : : {
2139 : : /*
2140 : : * This is a bit yucky, but I don't want to make the binary format very
2141 : : * dependent on representation, and not knowing much about it, I write out
2142 : : * a sign byte. If you change this, don't forget to change the file
2143 : : * version #, and modify ReadInt to read the new format AS WELL AS the old
2144 : : * formats.
2145 : : */
2146 : :
2147 : : /* SIGN byte */
2148 [ + + ]: 186600 : if (i < 0)
2149 : : {
3243 peter_e@gmx.net 2150 : 45167 : AH->WriteBytePtr(AH, 1);
9500 pjw@rhyme.com.au 2151 : 45167 : i = -i;
2152 : : }
2153 : : else
3243 peter_e@gmx.net 2154 : 141433 : AH->WriteBytePtr(AH, 0);
2155 : :
14 peter@eisentraut.org 2156 [ + + ]:GNC 933000 : for (size_t b = 0; b < AH->intSize; b++)
2157 : : {
3243 peter_e@gmx.net 2158 :CBC 746400 : AH->WriteBytePtr(AH, i & 0xFF);
8823 tgl@sss.pgh.pa.us 2159 : 746400 : i >>= 8;
2160 : : }
2161 : :
9256 bruce@momjian.us 2162 : 186600 : return AH->intSize + 1;
2163 : : }
2164 : :
2165 : : int
2166 : 163682 : ReadInt(ArchiveHandle *AH)
2167 : : {
2168 : 163682 : int res = 0;
2169 : : int bv;
2170 : 163682 : int sign = 0; /* Default positive */
2171 : 163682 : int bitShift = 0;
2172 : :
2173 [ + - ]: 163682 : if (AH->version > K_VERS_1_0)
2174 : : /* Read a sign byte */
3243 peter_e@gmx.net 2175 : 163682 : sign = AH->ReadBytePtr(AH);
2176 : :
14 peter@eisentraut.org 2177 [ + + ]:GNC 818410 : for (size_t b = 0; b < AH->intSize; b++)
2178 : : {
3243 peter_e@gmx.net 2179 :CBC 654728 : bv = AH->ReadBytePtr(AH) & 0xFF;
9500 pjw@rhyme.com.au 2180 [ + + ]: 654728 : if (bv != 0)
2181 : 156158 : res = res + (bv << bitShift);
2182 : 654728 : bitShift += 8;
2183 : : }
2184 : :
9256 bruce@momjian.us 2185 [ + + ]: 163682 : if (sign)
2186 : 39240 : res = -res;
2187 : :
2188 : 163682 : return res;
2189 : : }
2190 : :
2191 : : size_t
9246 pjw@rhyme.com.au 2192 : 145684 : WriteStr(ArchiveHandle *AH, const char *c)
2193 : : {
2194 : : size_t res;
2195 : :
9500 2196 [ + + ]: 145684 : if (c)
2197 : : {
4463 bruce@momjian.us 2198 : 100517 : int len = strlen(c);
2199 : :
4464 2200 : 100517 : res = WriteInt(AH, len);
3243 peter_e@gmx.net 2201 : 100517 : AH->WriteBufPtr(AH, c, len);
4464 bruce@momjian.us 2202 : 100517 : res += len;
2203 : : }
2204 : : else
9500 pjw@rhyme.com.au 2205 : 45167 : res = WriteInt(AH, -1);
2206 : :
9256 bruce@momjian.us 2207 : 145684 : return res;
2208 : : }
2209 : :
2210 : : char *
2211 : 128074 : ReadStr(ArchiveHandle *AH)
2212 : : {
2213 : : char *buf;
2214 : : int l;
2215 : :
2216 : 128074 : l = ReadInt(AH);
6928 tgl@sss.pgh.pa.us 2217 [ + + ]: 128074 : if (l < 0)
9500 pjw@rhyme.com.au 2218 : 39240 : buf = NULL;
2219 : : else
2220 : : {
5356 bruce@momjian.us 2221 : 88834 : buf = (char *) pg_malloc(l + 1);
604 peter@eisentraut.org 2222 : 88834 : AH->ReadBufPtr(AH, buf, l);
2223 : :
9500 pjw@rhyme.com.au 2224 : 88834 : buf[l] = '\0';
2225 : : }
2226 : :
9256 bruce@momjian.us 2227 : 128074 : return buf;
2228 : : }
2229 : :
2230 : : static bool
1248 tomas.vondra@postgre 2231 : 11 : _fileExistsInDirectory(const char *dir, const char *filename)
2232 : : {
2233 : : struct stat st;
2234 : : char buf[MAXPGPATH];
2235 : :
2236 [ - + ]: 11 : if (snprintf(buf, MAXPGPATH, "%s/%s", dir, filename) >= MAXPGPATH)
1248 tomas.vondra@postgre 2237 :UBC 0 : pg_fatal("directory name too long: \"%s\"", dir);
2238 : :
1248 tomas.vondra@postgre 2239 [ + + + - ]:CBC 11 : return (stat(buf, &st) == 0 && S_ISREG(st.st_mode));
2240 : : }
2241 : :
2242 : : static int
9256 bruce@momjian.us 2243 : 50 : _discoverArchiveFormat(ArchiveHandle *AH)
2244 : : {
2245 : : FILE *fh;
2246 : : char sig[6]; /* More than enough */
2247 : : size_t cnt;
2248 : 50 : int wantClose = 0;
2249 : :
2672 peter@eisentraut.org 2250 [ - + ]: 50 : pg_log_debug("attempting to ascertain archive format");
2251 : :
24 peter@eisentraut.org 2252 :GNC 50 : pg_free(AH->lookahead);
2253 : :
1941 tgl@sss.pgh.pa.us 2254 :CBC 50 : AH->readHeader = 0;
9500 pjw@rhyme.com.au 2255 : 50 : AH->lookaheadSize = 512;
5044 tgl@sss.pgh.pa.us 2256 : 50 : AH->lookahead = pg_malloc0(512);
9500 pjw@rhyme.com.au 2257 : 50 : AH->lookaheadLen = 0;
2258 : 50 : AH->lookaheadPos = 0;
2259 : :
9256 bruce@momjian.us 2260 [ + - ]: 50 : if (AH->fSpec)
2261 : : {
2262 : : struct stat st;
2263 : :
9500 pjw@rhyme.com.au 2264 : 50 : wantClose = 1;
2265 : :
2266 : : /*
2267 : : * Check if the specified archive is a directory. If so, check if
2268 : : * there's a "toc.dat" (or "toc.dat.{gz,lz4,zst}") file in it.
2269 : : */
5662 heikki.linnakangas@i 2270 [ + - + + ]: 50 : if (stat(AH->fSpec, &st) == 0 && S_ISDIR(st.st_mode))
2271 : : {
1248 tomas.vondra@postgre 2272 : 10 : AH->format = archDirectory;
2273 [ + + ]: 10 : if (_fileExistsInDirectory(AH->fSpec, "toc.dat"))
5662 heikki.linnakangas@i 2274 : 10 : return AH->format;
2275 : : #ifdef HAVE_LIBZ
1248 tomas.vondra@postgre 2276 [ + - ]: 1 : if (_fileExistsInDirectory(AH->fSpec, "toc.dat.gz"))
5662 heikki.linnakangas@i 2277 : 1 : return AH->format;
2278 : : #endif
2279 : : #ifdef USE_LZ4
1248 tomas.vondra@postgre 2280 [ # # ]:UBC 0 : if (_fileExistsInDirectory(AH->fSpec, "toc.dat.lz4"))
2281 : 0 : return AH->format;
2282 : : #endif
2283 : : #ifdef USE_ZSTD
2284 : : if (_fileExistsInDirectory(AH->fSpec, "toc.dat.zst"))
2285 : : return AH->format;
2286 : : #endif
1569 tgl@sss.pgh.pa.us 2287 : 0 : pg_fatal("directory \"%s\" does not appear to be a valid archive (\"toc.dat\" does not exist)",
2288 : : AH->fSpec);
2289 : : fh = NULL; /* keep compiler quiet */
2290 : : }
2291 : : else
2292 : : {
5662 heikki.linnakangas@i 2293 :CBC 40 : fh = fopen(AH->fSpec, PG_BINARY_R);
2294 [ - + ]: 40 : if (!fh)
1569 tgl@sss.pgh.pa.us 2295 :UBC 0 : pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
2296 : : }
2297 : : }
2298 : : else
2299 : : {
9500 pjw@rhyme.com.au 2300 : 0 : fh = stdin;
6845 tgl@sss.pgh.pa.us 2301 [ # # ]: 0 : if (!fh)
1569 2302 : 0 : pg_fatal("could not open input file: %m");
2303 : : }
2304 : :
4464 bruce@momjian.us 2305 [ - + ]:CBC 40 : if ((cnt = fread(sig, 1, 5, fh)) != 5)
2306 : : {
9159 peter_e@gmx.net 2307 [ # # ]:UBC 0 : if (ferror(fh))
1569 tgl@sss.pgh.pa.us 2308 : 0 : pg_fatal("could not read input file: %m");
2309 : : else
228 peter@eisentraut.org 2310 : 0 : pg_fatal("input file is too short (read %zu, expected 5)", cnt);
2311 : : }
2312 : :
2313 : : /* Save it, just in case we need it later */
4200 tgl@sss.pgh.pa.us 2314 :CBC 40 : memcpy(&AH->lookahead[0], sig, 5);
9500 pjw@rhyme.com.au 2315 : 40 : AH->lookaheadLen = 5;
2316 : :
9256 bruce@momjian.us 2317 [ + + ]: 40 : if (strncmp(sig, "PGDMP", 5) == 0)
2318 : : {
2319 : : /* It's custom format, stop here */
1941 tgl@sss.pgh.pa.us 2320 : 39 : AH->format = archCustom;
2321 : 39 : AH->readHeader = 1;
2322 : : }
2323 : : else
2324 : : {
2325 : : /*
2326 : : * *Maybe* we have a tar archive format file or a text dump ... So,
2327 : : * read first 512 byte header...
2328 : : */
9500 pjw@rhyme.com.au 2329 : 1 : cnt = fread(&AH->lookahead[AH->lookaheadLen], 1, 512 - AH->lookaheadLen, fh);
2330 : : /* read failure is checked below */
2331 : 1 : AH->lookaheadLen += cnt;
2332 : :
5317 andrew@dunslane.net 2333 [ + - ]: 1 : if (AH->lookaheadLen >= strlen(TEXT_DUMPALL_HEADER) &&
2334 [ + - ]: 1 : (strncmp(AH->lookahead, TEXT_DUMP_HEADER, strlen(TEXT_DUMP_HEADER)) == 0 ||
2335 [ - + ]: 1 : strncmp(AH->lookahead, TEXT_DUMPALL_HEADER, strlen(TEXT_DUMPALL_HEADER)) == 0))
2336 : : {
2337 : : /*
2338 : : * looks like it's probably a text format dump. so suggest they
2339 : : * try psql
2340 : : */
1569 tgl@sss.pgh.pa.us 2341 :UBC 0 : pg_fatal("input file appears to be a text format dump. Please use psql.");
2342 : : }
2343 : :
4463 bruce@momjian.us 2344 [ - + ]:CBC 1 : if (AH->lookaheadLen != 512)
2345 : : {
4463 bruce@momjian.us 2346 [ # # ]:UBC 0 : if (feof(fh))
1569 tgl@sss.pgh.pa.us 2347 : 0 : pg_fatal("input file does not appear to be a valid archive (too short?)");
2348 : : else
4463 bruce@momjian.us 2349 [ # # ]: 0 : READ_ERROR_EXIT(fh);
2350 : : }
2351 : :
9500 pjw@rhyme.com.au 2352 [ - + ]:CBC 1 : if (!isValidTarHeader(AH->lookahead))
114 tgl@sss.pgh.pa.us 2353 :UBC 0 : pg_fatal("input file does not appear to be a valid tar archive");
2354 : :
9500 pjw@rhyme.com.au 2355 :CBC 1 : AH->format = archTar;
2356 : : }
2357 : :
2358 : : /* Close the file if we opened it */
9256 bruce@momjian.us 2359 [ + - ]: 40 : if (wantClose)
2360 : : {
9325 pjw@rhyme.com.au 2361 [ - + ]: 40 : if (fclose(fh) != 0)
1569 tgl@sss.pgh.pa.us 2362 :UBC 0 : pg_fatal("could not close input file: %m");
2363 : : /* Forget lookahead, since we'll re-read header after re-opening */
1941 tgl@sss.pgh.pa.us 2364 :CBC 40 : AH->readHeader = 0;
2365 : 40 : AH->lookaheadLen = 0;
2366 : : }
2367 : :
9256 bruce@momjian.us 2368 : 40 : return AH->format;
2369 : : }
2370 : :
2371 : :
2372 : : /*
2373 : : * Allocate an archive handle
2374 : : */
2375 : : static ArchiveHandle *
2376 : 276 : _allocAH(const char *FileSpec, const ArchiveFormat fmt,
2377 : : const pg_compress_specification compression_spec,
2378 : : bool dosync, ArchiveMode mode,
2379 : : SetupWorkerPtrType setupWorkerPtr, DataDirSyncMethod sync_method)
2380 : : {
2381 : : ArchiveHandle *AH;
2382 : : CompressFileHandle *CFH;
1248 tomas.vondra@postgre 2383 : 276 : pg_compress_specification out_compress_spec = {0};
2384 : :
2137 tgl@sss.pgh.pa.us 2385 [ - + - - ]: 276 : pg_log_debug("allocating AH for %s, format %d",
2386 : : FileSpec ? FileSpec : "(stdio)", fmt);
2387 : :
162 michael@paquier.xyz 2388 : 276 : AH = pg_malloc0_object(ArchiveHandle);
2389 : :
3560 peter_e@gmx.net 2390 : 276 : AH->version = K_VERS_SELF;
2391 : :
2392 : : /* initialize for backwards compatible string processing */
6860 tgl@sss.pgh.pa.us 2393 : 276 : AH->public.encoding = 0; /* PG_SQL_ASCII */
40 andrew@dunslane.net 2394 : 276 : AH->public.std_strings = false;
2395 : :
2396 : : /* sql error handling */
7363 tgl@sss.pgh.pa.us 2397 : 276 : AH->public.exit_on_error = true;
2398 : 276 : AH->public.n_errors = 0;
2399 : :
5995 2400 : 276 : AH->archiveDumpVersion = PG_VERSION;
2401 : :
9500 pjw@rhyme.com.au 2402 : 276 : AH->createDate = time(NULL);
2403 : :
9256 bruce@momjian.us 2404 : 276 : AH->intSize = sizeof(int);
7096 magnus@hagander.net 2405 : 276 : AH->offSize = sizeof(pgoff_t);
9256 bruce@momjian.us 2406 [ + + ]: 276 : if (FileSpec)
2407 : : {
5356 2408 : 258 : AH->fSpec = pg_strdup(FileSpec);
2409 : :
2410 : : /*
2411 : : * Not used; maybe later....
2412 : : *
2413 : : * AH->workDir = pg_strdup(FileSpec); for(i=strlen(FileSpec) ; i > 0 ;
2414 : : * i--) if (AH->workDir[i-1] == '/')
2415 : : */
2416 : : }
2417 : : else
9500 pjw@rhyme.com.au 2418 : 18 : AH->fSpec = NULL;
2419 : :
6382 andrew@dunslane.net 2420 : 276 : AH->currUser = NULL; /* unknown */
2421 : 276 : AH->currSchema = NULL; /* ditto */
2422 : 276 : AH->currTablespace = NULL; /* ditto */
2621 tgl@sss.pgh.pa.us 2423 : 276 : AH->currTableAm = NULL; /* ditto */
2424 : :
162 michael@paquier.xyz 2425 : 276 : AH->toc = pg_malloc0_object(TocEntry);
2426 : :
9256 bruce@momjian.us 2427 : 276 : AH->toc->next = AH->toc;
2428 : 276 : AH->toc->prev = AH->toc;
2429 : :
2430 : 276 : AH->mode = mode;
1331 michael@paquier.xyz 2431 : 276 : AH->compression_spec = compression_spec;
3412 andrew@dunslane.net 2432 : 276 : AH->dosync = dosync;
1053 nathan@postgresql.or 2433 : 276 : AH->sync_method = sync_method;
2434 : :
5314 tgl@sss.pgh.pa.us 2435 : 276 : memset(&(AH->sqlparse), 0, sizeof(AH->sqlparse));
2436 : :
2437 : : /* Open stdout with no compression for AH output handle */
1248 tomas.vondra@postgre 2438 : 276 : out_compress_spec.algorithm = PG_COMPRESSION_NONE;
2439 : 276 : CFH = InitCompressFileHandle(out_compress_spec);
1220 2440 [ - + ]: 276 : if (!CFH->open_func(NULL, fileno(stdout), PG_BINARY_A, CFH))
1248 tomas.vondra@postgre 2441 :UBC 0 : pg_fatal("could not open stdout for appending: %m");
1248 tomas.vondra@postgre 2442 :CBC 276 : AH->OF = CFH;
2443 : :
2444 : : /*
2445 : : * On Windows, we need to use binary mode to read/write non-text files,
2446 : : * which include all archive formats as well as compressed plain text.
2447 : : * Force stdin/stdout into binary mode if that is what we are using.
2448 : : */
2449 : : #ifdef WIN32
2450 : : if ((fmt != archNull || compression_spec.algorithm != PG_COMPRESSION_NONE) &&
2451 : : (AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0))
2452 : : {
2453 : : if (mode == archModeWrite)
2454 : : _setmode(fileno(stdout), O_BINARY);
2455 : : else
2456 : : _setmode(fileno(stdin), O_BINARY);
2457 : : }
2458 : : #endif
2459 : :
4871 andrew@dunslane.net 2460 : 276 : AH->SetupWorkerPtr = setupWorkerPtr;
2461 : :
9256 bruce@momjian.us 2462 [ + + ]: 276 : if (fmt == archUnknown)
9500 pjw@rhyme.com.au 2463 : 50 : AH->format = _discoverArchiveFormat(AH);
2464 : : else
2465 : 226 : AH->format = fmt;
2466 : :
9256 bruce@momjian.us 2467 [ + + + + : 276 : switch (AH->format)
- ]
2468 : : {
9500 pjw@rhyme.com.au 2469 : 103 : case archCustom:
2470 : 103 : InitArchiveFmt_Custom(AH);
2471 : 103 : break;
2472 : :
2473 : 147 : case archNull:
2474 : 147 : InitArchiveFmt_Null(AH);
2475 : 147 : break;
2476 : :
5662 heikki.linnakangas@i 2477 : 21 : case archDirectory:
2478 : 21 : InitArchiveFmt_Directory(AH);
2479 : 21 : break;
2480 : :
9500 pjw@rhyme.com.au 2481 : 5 : case archTar:
2482 : 5 : InitArchiveFmt_Tar(AH);
2483 : 4 : break;
2484 : :
9500 pjw@rhyme.com.au 2485 :UBC 0 : default:
464 fujii@postgresql.org 2486 : 0 : pg_fatal("unrecognized file format \"%d\"", AH->format);
2487 : : }
2488 : :
9256 bruce@momjian.us 2489 :CBC 275 : return AH;
2490 : : }
2491 : :
2492 : : /*
2493 : : * Write out all data (tables & LOs)
2494 : : */
2495 : : void
3846 tgl@sss.pgh.pa.us 2496 : 63 : WriteDataChunks(ArchiveHandle *AH, ParallelState *pstate)
2497 : : {
2498 : : TocEntry *te;
2499 : :
2871 2500 [ + + + + ]: 63 : if (pstate && pstate->numWorkers > 1)
9256 bruce@momjian.us 2501 : 8 : {
2502 : : /*
2503 : : * In parallel mode, this code runs in the leader process. We
2504 : : * construct an array of candidate TEs, then sort it into decreasing
2505 : : * size order, then dispatch each TE to a data-transfer worker. By
2506 : : * dumping larger tables first, we avoid getting into a situation
2507 : : * where we're down to one job and it's big, losing parallelism.
2508 : : */
2509 : : TocEntry **tes;
2510 : : int ntes;
2511 : :
162 michael@paquier.xyz 2512 : 8 : tes = pg_malloc_array(TocEntry *, AH->tocCount);
2871 tgl@sss.pgh.pa.us 2513 : 8 : ntes = 0;
2514 [ + + ]: 602 : for (te = AH->toc->next; te != AH->toc; te = te->next)
2515 : : {
2516 : : /* Consider only TEs with dataDumper functions ... */
2517 [ + + ]: 594 : if (!te->dataDumper)
2518 : 525 : continue;
2519 : : /* ... and ignore ones not enabled for dump */
2520 [ - + ]: 69 : if ((te->reqs & REQ_DATA) == 0)
2871 tgl@sss.pgh.pa.us 2521 :UBC 0 : continue;
2522 : :
2871 tgl@sss.pgh.pa.us 2523 :CBC 69 : tes[ntes++] = te;
2524 : : }
2525 : :
2526 [ + + ]: 8 : if (ntes > 1)
1040 nathan@postgresql.or 2527 : 7 : qsort(tes, ntes, sizeof(TocEntry *), TocEntrySizeCompareQsort);
2528 : :
2871 tgl@sss.pgh.pa.us 2529 [ + + ]: 77 : for (int i = 0; i < ntes; i++)
2530 : 69 : DispatchJobForTocEntry(AH, pstate, tes[i], ACT_DUMP,
2531 : : mark_dump_job_done, NULL);
2532 : :
2533 : 8 : pg_free(tes);
2534 : :
2535 : : /* Now wait for workers to finish. */
3588 2536 : 8 : WaitForWorkers(AH, pstate, WFW_ALL_IDLE);
2537 : : }
2538 : : else
2539 : : {
2540 : : /* Non-parallel mode: just dump all candidate TEs sequentially. */
2871 2541 [ + + ]: 7275 : for (te = AH->toc->next; te != AH->toc; te = te->next)
2542 : : {
2543 : : /* Must have same filter conditions as above */
2544 [ + + ]: 7220 : if (!te->dataDumper)
2545 : 6857 : continue;
2546 [ + + ]: 363 : if ((te->reqs & REQ_DATA) == 0)
2547 : 7 : continue;
2548 : :
2549 : 356 : WriteDataChunksForTocEntry(AH, te);
2550 : : }
2551 : : }
4871 andrew@dunslane.net 2552 : 63 : }
2553 : :
2554 : :
2555 : : /*
2556 : : * Callback function that's invoked in the leader process after a step has
2557 : : * been parallel dumped.
2558 : : *
2559 : : * We don't need to do anything except check for worker failure.
2560 : : */
2561 : : static void
3588 tgl@sss.pgh.pa.us 2562 : 69 : mark_dump_job_done(ArchiveHandle *AH,
2563 : : TocEntry *te,
2564 : : int status,
2565 : : void *callback_data)
2566 : : {
2672 peter@eisentraut.org 2567 : 69 : pg_log_info("finished item %d %s %s",
2568 : : te->dumpId, te->desc, te->tag);
2569 : :
3588 tgl@sss.pgh.pa.us 2570 [ - + ]: 69 : if (status != 0)
1569 tgl@sss.pgh.pa.us 2571 :UBC 0 : pg_fatal("worker process failed: exit code %d",
2572 : : status);
3588 tgl@sss.pgh.pa.us 2573 :CBC 69 : }
2574 : :
2575 : :
2576 : : void
3846 2577 : 425 : WriteDataChunksForTocEntry(ArchiveHandle *AH, TocEntry *te)
2578 : : {
2579 : : StartDataPtrType startPtr;
2580 : : EndDataPtrType endPtr;
2581 : :
4871 andrew@dunslane.net 2582 : 425 : AH->currToc = te;
2583 : :
2584 [ + + ]: 425 : if (strcmp(te->desc, "BLOBS") == 0)
2585 : : {
1328 peter@eisentraut.org 2586 : 21 : startPtr = AH->StartLOsPtr;
2587 : 21 : endPtr = AH->EndLOsPtr;
2588 : : }
2589 : : else
2590 : : {
4871 andrew@dunslane.net 2591 : 404 : startPtr = AH->StartDataPtr;
2592 : 404 : endPtr = AH->EndDataPtr;
2593 : : }
2594 : :
2595 [ + - ]: 425 : if (startPtr != NULL)
2596 : 425 : (*startPtr) (AH, te);
2597 : :
2598 : : /*
2599 : : * The user-provided DataDumper routine needs to call AH->WriteData
2600 : : */
3243 peter_e@gmx.net 2601 : 425 : te->dataDumper((Archive *) AH, te->dataDumperArg);
2602 : :
4871 andrew@dunslane.net 2603 [ + - ]: 425 : if (endPtr != NULL)
2604 : 425 : (*endPtr) (AH, te);
2605 : :
2606 : 425 : AH->currToc = NULL;
9517 bruce@momjian.us 2607 : 425 : }
2608 : :
2609 : : void
9256 2610 : 74 : WriteToc(ArchiveHandle *AH)
2611 : : {
2612 : : TocEntry *te;
2613 : : char workbuf[32];
2614 : : int tocCount;
2615 : : int i;
2616 : :
2617 : : /* count entries that will actually be dumped */
5170 tgl@sss.pgh.pa.us 2618 : 74 : tocCount = 0;
2619 [ + + ]: 9974 : for (te = AH->toc->next; te != AH->toc; te = te->next)
2620 : : {
520 jdavis@postgresql.or 2621 [ + + ]: 9900 : if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS | REQ_SPECIAL)) != 0)
5170 tgl@sss.pgh.pa.us 2622 : 9892 : tocCount++;
2623 : : }
2624 : :
2625 : : /* printf("%d TOC Entries to save\n", tocCount); */
2626 : :
2627 : 74 : WriteInt(AH, tocCount);
2628 : :
8267 2629 [ + + ]: 9974 : for (te = AH->toc->next; te != AH->toc; te = te->next)
2630 : : {
520 jdavis@postgresql.or 2631 [ + + ]: 9900 : if ((te->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS | REQ_SPECIAL)) == 0)
5170 tgl@sss.pgh.pa.us 2632 : 8 : continue;
2633 : :
8267 2634 : 9892 : WriteInt(AH, te->dumpId);
9256 bruce@momjian.us 2635 : 9892 : WriteInt(AH, te->dataDumper ? 1 : 0);
2636 : :
2637 : : /* OID is recorded as a string for historical reasons */
8267 tgl@sss.pgh.pa.us 2638 : 9892 : sprintf(workbuf, "%u", te->catalogId.tableoid);
2639 : 9892 : WriteStr(AH, workbuf);
2640 : 9892 : sprintf(workbuf, "%u", te->catalogId.oid);
2641 : 9892 : WriteStr(AH, workbuf);
2642 : :
8787 bruce@momjian.us 2643 : 9892 : WriteStr(AH, te->tag);
9256 2644 : 9892 : WriteStr(AH, te->desc);
6382 andrew@dunslane.net 2645 : 9892 : WriteInt(AH, te->section);
2646 : :
477 nathan@postgresql.or 2647 [ + + ]: 9892 : if (te->defnLen)
2648 : : {
2649 : : /*
2650 : : * defnLen should only be set for custom format's second call to
2651 : : * WriteToc(), which rewrites the TOC in place to update data
2652 : : * offsets. Instead of calling the defnDumper a second time
2653 : : * (which could involve re-executing queries), just skip writing
2654 : : * the entry. While regenerating the definition should
2655 : : * theoretically produce the same result as before, it's expensive
2656 : : * and feels risky.
2657 : : *
2658 : : * The custom format only calls WriteToc() a second time if
2659 : : * fseeko() is usable (see _CloseArchive() in pg_backup_custom.c),
2660 : : * so we can safely use it without checking. For other formats,
2661 : : * we fail because one of our assumptions must no longer hold
2662 : : * true.
2663 : : *
2664 : : * XXX This is a layering violation, but the alternative is an
2665 : : * awkward and complicated callback infrastructure for this
2666 : : * special case. This might be worth revisiting in the future.
2667 : : */
2668 [ - + ]: 369 : if (AH->format != archCustom)
477 nathan@postgresql.or 2669 :UBC 0 : pg_fatal("unexpected TOC entry in WriteToc(): %d %s %s",
2670 : : te->dumpId, te->desc, te->tag);
2671 : :
417 noah@leadboat.com 2672 [ - + ]:CBC 369 : if (fseeko(AH->FH, te->defnLen, SEEK_CUR) != 0)
477 nathan@postgresql.or 2673 :UBC 0 : pg_fatal("error during file seek: %m");
2674 : : }
477 nathan@postgresql.or 2675 [ + + ]:CBC 9523 : else if (te->defnDumper)
2676 : : {
2677 : 1878 : char *defn = te->defnDumper((Archive *) AH, te->defnDumperArg, te);
2678 : :
2679 : 1878 : te->defnLen = WriteStr(AH, defn);
2680 : 1878 : pg_free(defn);
2681 : : }
2682 : : else
2683 : 7645 : WriteStr(AH, te->defn);
2684 : :
9256 bruce@momjian.us 2685 : 9892 : WriteStr(AH, te->dropStmt);
2686 : 9892 : WriteStr(AH, te->copyStmt);
8842 tgl@sss.pgh.pa.us 2687 : 9892 : WriteStr(AH, te->namespace);
7931 2688 : 9892 : WriteStr(AH, te->tablespace);
2698 andres@anarazel.de 2689 : 9892 : WriteStr(AH, te->tableam);
824 michael@paquier.xyz 2690 : 9892 : WriteInt(AH, te->relkind);
9256 bruce@momjian.us 2691 : 9892 : WriteStr(AH, te->owner);
2804 andres@anarazel.de 2692 : 9892 : WriteStr(AH, "false");
2693 : :
2694 : : /* Dump list of dependencies */
8267 tgl@sss.pgh.pa.us 2695 [ + + ]: 25736 : for (i = 0; i < te->nDeps; i++)
2696 : : {
2697 : 15844 : sprintf(workbuf, "%d", te->dependencies[i]);
2698 : 15844 : WriteStr(AH, workbuf);
2699 : : }
9039 bruce@momjian.us 2700 : 9892 : WriteStr(AH, NULL); /* Terminate List */
2701 : :
9256 2702 [ + - ]: 9892 : if (AH->WriteExtraTocPtr)
3243 peter_e@gmx.net 2703 : 9892 : AH->WriteExtraTocPtr(AH, te);
2704 : : }
9517 bruce@momjian.us 2705 : 74 : }
2706 : :
2707 : : void
9256 2708 : 65 : ReadToc(ArchiveHandle *AH)
2709 : : {
2710 : : int i;
2711 : : char *tmp;
2712 : : DumpId *deps;
2713 : : int depIdx;
2714 : : int depSize;
2715 : : TocEntry *te;
2716 : : bool is_supported;
2717 : :
2718 : 65 : AH->tocCount = ReadInt(AH);
8267 tgl@sss.pgh.pa.us 2719 : 65 : AH->maxDumpId = 0;
2720 : :
9256 bruce@momjian.us 2721 [ + + ]: 8702 : for (i = 0; i < AH->tocCount; i++)
2722 : : {
162 michael@paquier.xyz 2723 : 8637 : te = pg_malloc0_object(TocEntry);
8267 tgl@sss.pgh.pa.us 2724 : 8637 : te->dumpId = ReadInt(AH);
2725 : :
2726 [ + + ]: 8637 : if (te->dumpId > AH->maxDumpId)
2727 : 3547 : AH->maxDumpId = te->dumpId;
2728 : :
2729 : : /* Sanity check */
2730 [ - + ]: 8637 : if (te->dumpId <= 0)
1569 tgl@sss.pgh.pa.us 2731 :UBC 0 : pg_fatal("entry ID %d out of range -- perhaps a corrupt TOC",
2732 : : te->dumpId);
2733 : :
9500 pjw@rhyme.com.au 2734 :CBC 8637 : te->hadDumper = ReadInt(AH);
2735 : :
8267 tgl@sss.pgh.pa.us 2736 [ + - ]: 8637 : if (AH->version >= K_VERS_1_8)
2737 : : {
2738 : 8637 : tmp = ReadStr(AH);
2739 : 8637 : sscanf(tmp, "%u", &te->catalogId.tableoid);
2740 : 8637 : free(tmp);
2741 : : }
2742 : : else
8267 tgl@sss.pgh.pa.us 2743 :UBC 0 : te->catalogId.tableoid = InvalidOid;
8267 tgl@sss.pgh.pa.us 2744 :CBC 8637 : tmp = ReadStr(AH);
2745 : 8637 : sscanf(tmp, "%u", &te->catalogId.oid);
2746 : 8637 : free(tmp);
2747 : :
8787 bruce@momjian.us 2748 : 8637 : te->tag = ReadStr(AH);
9500 pjw@rhyme.com.au 2749 : 8637 : te->desc = ReadStr(AH);
2750 : :
6382 andrew@dunslane.net 2751 [ + - ]: 8637 : if (AH->version >= K_VERS_1_11)
2752 : : {
2753 : 8637 : te->section = ReadInt(AH);
2754 : : }
2755 : : else
2756 : : {
2757 : : /*
2758 : : * Rules for pre-8.4 archives wherein pg_dump hasn't classified
2759 : : * the entries into sections. This list need not cover entry
2760 : : * types added later than 8.4.
2761 : : */
6382 andrew@dunslane.net 2762 [ # # ]:UBC 0 : if (strcmp(te->desc, "COMMENT") == 0 ||
6137 tgl@sss.pgh.pa.us 2763 [ # # ]: 0 : strcmp(te->desc, "ACL") == 0 ||
6001 2764 [ # # ]: 0 : strcmp(te->desc, "ACL LANGUAGE") == 0)
6382 andrew@dunslane.net 2765 : 0 : te->section = SECTION_NONE;
2766 [ # # ]: 0 : else if (strcmp(te->desc, "TABLE DATA") == 0 ||
2767 [ # # ]: 0 : strcmp(te->desc, "BLOBS") == 0 ||
2768 [ # # ]: 0 : strcmp(te->desc, "BLOB COMMENTS") == 0)
2769 : 0 : te->section = SECTION_DATA;
2770 [ # # ]: 0 : else if (strcmp(te->desc, "CONSTRAINT") == 0 ||
2771 [ # # ]: 0 : strcmp(te->desc, "CHECK CONSTRAINT") == 0 ||
2772 [ # # ]: 0 : strcmp(te->desc, "FK CONSTRAINT") == 0 ||
2773 [ # # ]: 0 : strcmp(te->desc, "INDEX") == 0 ||
2774 [ # # ]: 0 : strcmp(te->desc, "RULE") == 0 ||
2775 [ # # ]: 0 : strcmp(te->desc, "TRIGGER") == 0)
2776 : 0 : te->section = SECTION_POST_DATA;
2777 : : else
2778 : 0 : te->section = SECTION_PRE_DATA;
2779 : : }
2780 : :
9500 pjw@rhyme.com.au 2781 :CBC 8637 : te->defn = ReadStr(AH);
2782 : 8637 : te->dropStmt = ReadStr(AH);
2783 : :
2784 [ + - ]: 8637 : if (AH->version >= K_VERS_1_3)
2785 : 8637 : te->copyStmt = ReadStr(AH);
2786 : :
8842 tgl@sss.pgh.pa.us 2787 [ + - ]: 8637 : if (AH->version >= K_VERS_1_6)
2788 : 8637 : te->namespace = ReadStr(AH);
2789 : :
7931 2790 [ + - ]: 8637 : if (AH->version >= K_VERS_1_10)
2791 : 8637 : te->tablespace = ReadStr(AH);
2792 : :
2698 andres@anarazel.de 2793 [ + - ]: 8637 : if (AH->version >= K_VERS_1_14)
2794 : 8637 : te->tableam = ReadStr(AH);
2795 : :
824 michael@paquier.xyz 2796 [ + - ]: 8637 : if (AH->version >= K_VERS_1_16)
2797 : 8637 : te->relkind = ReadInt(AH);
2798 : :
9500 pjw@rhyme.com.au 2799 : 8637 : te->owner = ReadStr(AH);
1627 dgustafsson@postgres 2800 : 8637 : is_supported = true;
2801 [ - + ]: 8637 : if (AH->version < K_VERS_1_9)
1627 dgustafsson@postgres 2802 :UBC 0 : is_supported = false;
2803 : : else
2804 : : {
1535 tgl@sss.pgh.pa.us 2805 :CBC 8637 : tmp = ReadStr(AH);
2806 : :
2807 [ - + ]: 8637 : if (strcmp(tmp, "true") == 0)
1535 tgl@sss.pgh.pa.us 2808 :UBC 0 : is_supported = false;
2809 : :
1535 tgl@sss.pgh.pa.us 2810 :CBC 8637 : free(tmp);
2811 : : }
2812 : :
1627 dgustafsson@postgres 2813 [ - + ]: 8637 : if (!is_supported)
2672 peter@eisentraut.org 2814 :UBC 0 : pg_log_warning("restoring tables WITH OIDS is not supported anymore");
2815 : :
2816 : : /* Read TOC entry dependencies */
9246 pjw@rhyme.com.au 2817 [ + - ]:CBC 8637 : if (AH->version >= K_VERS_1_5)
2818 : : {
2819 : 8637 : depSize = 100;
162 michael@paquier.xyz 2820 : 8637 : deps = pg_malloc_array(DumpId, depSize);
9246 pjw@rhyme.com.au 2821 : 8637 : depIdx = 0;
2822 : : for (;;)
2823 : : {
8267 tgl@sss.pgh.pa.us 2824 : 22427 : tmp = ReadStr(AH);
2825 [ + + ]: 22427 : if (!tmp)
2826 : 8637 : break; /* end of list */
8484 2827 [ - + ]: 13790 : if (depIdx >= depSize)
2828 : : {
9246 pjw@rhyme.com.au 2829 :UBC 0 : depSize *= 2;
162 michael@paquier.xyz 2830 : 0 : deps = pg_realloc_array(deps, DumpId, depSize);
2831 : : }
8267 tgl@sss.pgh.pa.us 2832 :CBC 13790 : sscanf(tmp, "%d", &deps[depIdx]);
2833 : 13790 : free(tmp);
2834 : 13790 : depIdx++;
2835 : : }
2836 : :
2837 [ + + ]: 8637 : if (depIdx > 0) /* We have a non-null entry */
2838 : : {
162 michael@paquier.xyz 2839 : 7133 : deps = pg_realloc_array(deps, DumpId, depIdx);
8267 tgl@sss.pgh.pa.us 2840 : 7133 : te->dependencies = deps;
2841 : 7133 : te->nDeps = depIdx;
2842 : : }
2843 : : else
2844 : : {
24 peter@eisentraut.org 2845 :GNC 1504 : pg_free(deps);
8267 tgl@sss.pgh.pa.us 2846 :CBC 1504 : te->dependencies = NULL;
2847 : 1504 : te->nDeps = 0;
2848 : : }
2849 : : }
2850 : : else
2851 : : {
8267 tgl@sss.pgh.pa.us 2852 :UBC 0 : te->dependencies = NULL;
2853 : 0 : te->nDeps = 0;
2854 : : }
2871 tgl@sss.pgh.pa.us 2855 :CBC 8637 : te->dataLength = 0;
2856 : :
9256 bruce@momjian.us 2857 [ + - ]: 8637 : if (AH->ReadExtraTocPtr)
3243 peter_e@gmx.net 2858 : 8637 : AH->ReadExtraTocPtr(AH, te);
2859 : :
2672 peter@eisentraut.org 2860 [ - + ]: 8637 : pg_log_debug("read TOC entry %d (ID %d) for %s %s",
2861 : : i, te->dumpId, te->desc, te->tag);
2862 : :
2863 : : /* link completed entry into TOC circular list */
9500 pjw@rhyme.com.au 2864 : 8637 : te->prev = AH->toc->prev;
2865 : 8637 : AH->toc->prev->next = te;
2866 : 8637 : AH->toc->prev = te;
2867 : 8637 : te->next = AH->toc;
2868 : :
2869 : : /* special processing immediately upon read for some items */
7363 tgl@sss.pgh.pa.us 2870 [ + + ]: 8637 : if (strcmp(te->desc, "ENCODING") == 0)
2871 : 65 : processEncodingEntry(AH, te);
2872 [ + + ]: 8572 : else if (strcmp(te->desc, "STDSTRINGS") == 0)
2873 : 65 : processStdStringsEntry(AH, te);
3071 2874 [ + + ]: 8507 : else if (strcmp(te->desc, "SEARCHPATH") == 0)
2875 : 65 : processSearchPathEntry(AH, te);
2876 : : }
9517 bruce@momjian.us 2877 : 65 : }
2878 : :
2879 : : static void
7363 tgl@sss.pgh.pa.us 2880 : 65 : processEncodingEntry(ArchiveHandle *AH, TocEntry *te)
2881 : : {
2882 : : /* te->defn should have the form SET client_encoding = 'foo'; */
5356 bruce@momjian.us 2883 : 65 : char *defn = pg_strdup(te->defn);
2884 : : char *ptr1;
7363 tgl@sss.pgh.pa.us 2885 : 65 : char *ptr2 = NULL;
2886 : : int encoding;
2887 : :
2888 : 65 : ptr1 = strchr(defn, '\'');
2889 [ + - ]: 65 : if (ptr1)
2890 : 65 : ptr2 = strchr(++ptr1, '\'');
2891 [ + - ]: 65 : if (ptr2)
2892 : : {
2893 : 65 : *ptr2 = '\0';
2894 : 65 : encoding = pg_char_to_encoding(ptr1);
2895 [ - + ]: 65 : if (encoding < 0)
1569 tgl@sss.pgh.pa.us 2896 :UBC 0 : pg_fatal("unrecognized encoding \"%s\"",
2897 : : ptr1);
7363 tgl@sss.pgh.pa.us 2898 :CBC 65 : AH->public.encoding = encoding;
530 andres@anarazel.de 2899 : 65 : setFmtEncoding(encoding);
2900 : : }
2901 : : else
1569 tgl@sss.pgh.pa.us 2902 :UBC 0 : pg_fatal("invalid ENCODING item: %s",
2903 : : te->defn);
2904 : :
24 peter@eisentraut.org 2905 :GNC 65 : pg_free(defn);
7363 tgl@sss.pgh.pa.us 2906 :CBC 65 : }
2907 : :
2908 : : static void
2909 : 65 : processStdStringsEntry(ArchiveHandle *AH, TocEntry *te)
2910 : : {
2911 : : /* te->defn should have the form SET standard_conforming_strings = 'x'; */
2912 : : char *ptr1;
2913 : :
2914 : 65 : ptr1 = strchr(te->defn, '\'');
2915 [ + - + - ]: 65 : if (ptr1 && strncmp(ptr1, "'on'", 4) == 0)
2916 : 65 : AH->public.std_strings = true;
7363 tgl@sss.pgh.pa.us 2917 [ # # # # ]:UBC 0 : else if (ptr1 && strncmp(ptr1, "'off'", 5) == 0)
2918 : 0 : AH->public.std_strings = false;
2919 : : else
1569 2920 : 0 : pg_fatal("invalid STDSTRINGS item: %s",
2921 : : te->defn);
7363 tgl@sss.pgh.pa.us 2922 :CBC 65 : }
2923 : :
2924 : : static void
3071 2925 : 65 : processSearchPathEntry(ArchiveHandle *AH, TocEntry *te)
2926 : : {
2927 : : /*
2928 : : * te->defn should contain a command to set search_path. We just copy it
2929 : : * verbatim for use later.
2930 : : */
2931 : 65 : AH->public.searchpath = pg_strdup(te->defn);
2932 : 65 : }
2933 : :
2934 : : static void
3967 teodor@sigaev.ru 2935 :UBC 0 : StrictNamesCheck(RestoreOptions *ropt)
2936 : : {
2937 : : const char *missing_name;
2938 : :
2939 [ # # ]: 0 : Assert(ropt->strict_names);
2940 : :
2941 [ # # ]: 0 : if (ropt->schemaNames.head != NULL)
2942 : : {
2943 : 0 : missing_name = simple_string_list_not_touched(&ropt->schemaNames);
2944 [ # # ]: 0 : if (missing_name != NULL)
1569 tgl@sss.pgh.pa.us 2945 : 0 : pg_fatal("schema \"%s\" not found", missing_name);
2946 : : }
2947 : :
3967 teodor@sigaev.ru 2948 [ # # ]: 0 : if (ropt->tableNames.head != NULL)
2949 : : {
2950 : 0 : missing_name = simple_string_list_not_touched(&ropt->tableNames);
2951 [ # # ]: 0 : if (missing_name != NULL)
1569 tgl@sss.pgh.pa.us 2952 : 0 : pg_fatal("table \"%s\" not found", missing_name);
2953 : : }
2954 : :
3967 teodor@sigaev.ru 2955 [ # # ]: 0 : if (ropt->indexNames.head != NULL)
2956 : : {
2957 : 0 : missing_name = simple_string_list_not_touched(&ropt->indexNames);
2958 [ # # ]: 0 : if (missing_name != NULL)
1569 tgl@sss.pgh.pa.us 2959 : 0 : pg_fatal("index \"%s\" not found", missing_name);
2960 : : }
2961 : :
3967 teodor@sigaev.ru 2962 [ # # ]: 0 : if (ropt->functionNames.head != NULL)
2963 : : {
2964 : 0 : missing_name = simple_string_list_not_touched(&ropt->functionNames);
2965 [ # # ]: 0 : if (missing_name != NULL)
1569 tgl@sss.pgh.pa.us 2966 : 0 : pg_fatal("function \"%s\" not found", missing_name);
2967 : : }
2968 : :
3967 teodor@sigaev.ru 2969 [ # # ]: 0 : if (ropt->triggerNames.head != NULL)
2970 : : {
2971 : 0 : missing_name = simple_string_list_not_touched(&ropt->triggerNames);
2972 [ # # ]: 0 : if (missing_name != NULL)
1569 tgl@sss.pgh.pa.us 2973 : 0 : pg_fatal("trigger \"%s\" not found", missing_name);
2974 : : }
3967 teodor@sigaev.ru 2975 : 0 : }
2976 : :
2977 : : /*
2978 : : * Determine whether we want to restore this TOC entry.
2979 : : *
2980 : : * Returns 0 if entry should be skipped, or some combination of the
2981 : : * REQ_SCHEMA, REQ_DATA, and REQ_STATS bits if we want to restore schema, data
2982 : : * and/or statistics portions of this TOC entry, or REQ_SPECIAL if it's a
2983 : : * special entry.
2984 : : */
2985 : : static int
3103 tgl@sss.pgh.pa.us 2986 :CBC 52749 : _tocEntryRequired(TocEntry *te, teSection curSection, ArchiveHandle *AH)
2987 : : {
2052 peter@eisentraut.org 2988 : 52749 : int res = REQ_SCHEMA | REQ_DATA;
3103 tgl@sss.pgh.pa.us 2989 : 52749 : RestoreOptions *ropt = AH->public.ropt;
2990 : :
2991 : : /*
2992 : : * For binary upgrade mode, dump pg_largeobject_metadata and the
2993 : : * associated pg_shdepend rows. This is faster to restore than the
2994 : : * equivalent set of large object commands.
2995 : : */
159 nathan@postgresql.or 2996 [ + + + + ]: 52749 : if (ropt->binary_upgrade && strcmp(te->desc, "TABLE DATA") == 0 &&
372 2997 [ + - ]: 41 : (te->catalogId.oid == LargeObjectMetadataRelationId ||
2998 [ + + ]: 41 : te->catalogId.oid == SharedDependRelationId))
2999 : 40 : return REQ_DATA;
3000 : :
3001 : : /* These items are treated specially */
7363 tgl@sss.pgh.pa.us 3002 [ + + ]: 52709 : if (strcmp(te->desc, "ENCODING") == 0 ||
3071 3003 [ + + ]: 52453 : strcmp(te->desc, "STDSTRINGS") == 0 ||
1885 3004 [ + + ]: 52197 : strcmp(te->desc, "SEARCHPATH") == 0)
5170 3005 : 768 : return REQ_SPECIAL;
3006 : :
179 michael@paquier.xyz 3007 [ + + ]: 51941 : if ((strcmp(te->desc, "STATISTICS DATA") == 0) ||
3008 [ + + ]: 46232 : (strcmp(te->desc, "EXTENDED STATISTICS DATA") == 0))
3009 : : {
520 jdavis@postgresql.or 3010 [ - + ]: 5764 : if (!ropt->dumpStatistics)
520 jdavis@postgresql.or 3011 :UBC 0 : return 0;
3012 : :
506 jdavis@postgresql.or 3013 :CBC 5764 : res = REQ_STATS;
3014 : : }
3015 : :
3016 : : /*
3017 : : * DATABASE and DATABASE PROPERTIES also have a special rule: they are
3018 : : * restored in createDB mode, and not restored otherwise, independently of
3019 : : * all else.
3020 : : */
3103 tgl@sss.pgh.pa.us 3021 [ + + ]: 51941 : if (strcmp(te->desc, "DATABASE") == 0 ||
3022 [ + + ]: 51784 : strcmp(te->desc, "DATABASE PROPERTIES") == 0)
3023 : : {
3024 [ + + ]: 233 : if (ropt->createDB)
3025 : 200 : return REQ_SCHEMA;
3026 : : else
3027 : 33 : return 0;
3028 : : }
3029 : :
3030 : : /*
3031 : : * Process exclusions that affect certain classes of TOC entries.
3032 : : */
3033 : :
3034 : : /* If it's an ACL, maybe ignore it */
5170 3035 [ + + - + ]: 51708 : if (ropt->aclsSkip && _tocEntryIsACL(te))
9500 pjw@rhyme.com.au 3036 :UBC 0 : return 0;
3037 : :
3038 : : /* If it's a comment, maybe ignore it */
3103 tgl@sss.pgh.pa.us 3039 [ - + - - ]:CBC 51708 : if (ropt->no_comments && strcmp(te->desc, "COMMENT") == 0)
3103 tgl@sss.pgh.pa.us 3040 :UBC 0 : return 0;
3041 : :
3042 : : /* If it's a policy, maybe ignore it */
496 tgl@sss.pgh.pa.us 3043 [ + + ]:CBC 51708 : if (ropt->no_policies &&
3044 [ + + ]: 771 : (strcmp(te->desc, "POLICY") == 0 ||
3045 [ + + ]: 765 : strcmp(te->desc, "ROW SECURITY") == 0))
3046 : 7 : return 0;
3047 : :
3048 : : /*
3049 : : * If it's a comment on a policy, a publication, or a subscription, maybe
3050 : : * ignore it.
3051 : : */
312 fujii@postgresql.org 3052 [ + + ]: 51701 : if (strcmp(te->desc, "COMMENT") == 0)
3053 : : {
3054 [ + + ]: 7126 : if (ropt->no_policies &&
3055 [ + + ]: 48 : strncmp(te->tag, "POLICY", strlen("POLICY")) == 0)
3056 : 1 : return 0;
3057 : :
3058 [ - + ]: 7125 : if (ropt->no_publications &&
312 fujii@postgresql.org 3059 [ # # ]:UBC 0 : strncmp(te->tag, "PUBLICATION", strlen("PUBLICATION")) == 0)
3060 : 0 : return 0;
3061 : :
312 fujii@postgresql.org 3062 [ + + ]:CBC 7125 : if (ropt->no_subscriptions &&
3063 [ + + ]: 48 : strncmp(te->tag, "SUBSCRIPTION", strlen("SUBSCRIPTION")) == 0)
3064 : 1 : return 0;
3065 : : }
3066 : :
3067 : : /*
3068 : : * If it's a publication or a table part of a publication, maybe ignore
3069 : : * it.
3070 : : */
2860 michael@paquier.xyz 3071 [ - + ]: 51699 : if (ropt->no_publications &&
2860 michael@paquier.xyz 3072 [ # # ]:UBC 0 : (strcmp(te->desc, "PUBLICATION") == 0 ||
1732 akapila@postgresql.o 3073 [ # # ]: 0 : strcmp(te->desc, "PUBLICATION TABLE") == 0 ||
3074 [ # # ]: 0 : strcmp(te->desc, "PUBLICATION TABLES IN SCHEMA") == 0))
3361 peter_e@gmx.net 3075 : 0 : return 0;
3076 : :
3077 : : /* If it's a security label, maybe ignore it */
5546 peter_e@gmx.net 3078 [ - + - - ]:CBC 51699 : if (ropt->no_security_labels && strcmp(te->desc, "SECURITY LABEL") == 0)
5780 rhaas@postgresql.org 3079 :UBC 0 : return 0;
3080 : :
3081 : : /*
3082 : : * If it's a security label on a publication or a subscription, maybe
3083 : : * ignore it.
3084 : : */
310 fujii@postgresql.org 3085 [ + + ]:CBC 51699 : if (strcmp(te->desc, "SECURITY LABEL") == 0)
3086 : : {
3087 [ - + ]: 9 : if (ropt->no_publications &&
310 fujii@postgresql.org 3088 [ # # ]:UBC 0 : strncmp(te->tag, "PUBLICATION", strlen("PUBLICATION")) == 0)
3089 : 0 : return 0;
3090 : :
310 fujii@postgresql.org 3091 [ - + ]:CBC 9 : if (ropt->no_subscriptions &&
310 fujii@postgresql.org 3092 [ # # ]:UBC 0 : strncmp(te->tag, "SUBSCRIPTION", strlen("SUBSCRIPTION")) == 0)
3093 : 0 : return 0;
3094 : : }
3095 : :
3096 : : /* If it's a subscription, maybe ignore it */
3364 peter_e@gmx.net 3097 [ + + + + ]:CBC 51699 : if (ropt->no_subscriptions && strcmp(te->desc, "SUBSCRIPTION") == 0)
3098 : 3 : return 0;
3099 : :
3100 : : /* Ignore it if section is not to be dumped/restored */
5170 tgl@sss.pgh.pa.us 3101 [ + + + - ]: 51696 : switch (curSection)
3102 : : {
3103 : 32143 : case SECTION_PRE_DATA:
3104 [ + + ]: 32143 : if (!(ropt->dumpSections & DUMP_PRE_DATA))
3105 : 384 : return 0;
3106 : 31759 : break;
3107 : 9679 : case SECTION_DATA:
3108 [ + + ]: 9679 : if (!(ropt->dumpSections & DUMP_DATA))
3109 : 192 : return 0;
3110 : 9487 : break;
3111 : 9874 : case SECTION_POST_DATA:
3112 [ + + ]: 9874 : if (!(ropt->dumpSections & DUMP_POST_DATA))
3113 : 234 : return 0;
3114 : 9640 : break;
5170 tgl@sss.pgh.pa.us 3115 :UBC 0 : default:
3116 : : /* shouldn't get here, really, but ignore it */
5335 andrew@dunslane.net 3117 : 0 : return 0;
3118 : : }
3119 : :
3120 : : /* Ignore it if rejected by idWanted[] (cf. SortTocFromFile) */
3103 tgl@sss.pgh.pa.us 3121 [ - + - - ]:CBC 50886 : if (ropt->idWanted && !ropt->idWanted[te->dumpId - 1])
3595 peter_e@gmx.net 3122 :UBC 0 : return 0;
3123 : :
3124 : : /*
3125 : : * Check options for selective dump/restore.
3126 : : */
3103 tgl@sss.pgh.pa.us 3127 [ + + ]:CBC 50886 : if (strcmp(te->desc, "ACL") == 0 ||
3128 [ + + ]: 48397 : strcmp(te->desc, "COMMENT") == 0 ||
3129 [ + + ]: 41323 : strcmp(te->desc, "SECURITY LABEL") == 0)
3130 : : {
3131 : : /* Database properties react to createDB, not selectivity options. */
3132 [ + + ]: 18969 : if (strncmp(te->tag, "DATABASE ", 9) == 0)
3133 : : {
3134 [ + + ]: 114 : if (!ropt->createDB)
9500 pjw@rhyme.com.au 3135 : 23 : return 0;
3136 : : }
3103 tgl@sss.pgh.pa.us 3137 [ + + ]: 9458 : else if (ropt->schemaNames.head != NULL ||
3138 [ + - ]: 9306 : ropt->schemaExcludeNames.head != NULL ||
3139 [ - + ]: 9306 : ropt->selTypes)
3140 : : {
3141 : : /*
3142 : : * In a selective dump/restore, we want to restore these dependent
3143 : : * TOC entry types only if their parent object is being restored.
3144 : : * Without selectivity options, we let through everything in the
3145 : : * archive. Note there may be such entries with no parent, eg
3146 : : * non-default ACLs for built-in objects. Also, we make
3147 : : * per-column ACLs additionally depend on the table's ACL if any
3148 : : * to ensure correct restore order, so those dependencies should
3149 : : * be ignored in this check.
3150 : : *
3151 : : * This code depends on the parent having been marked already,
3152 : : * which should be the case; if it isn't, perhaps due to
3153 : : * SortTocFromFile rearrangement, skipping the dependent entry
3154 : : * seems prudent anyway.
3155 : : *
3156 : : * Ideally we'd handle, eg, table CHECK constraints this way too.
3157 : : * But it's hard to tell which of their dependencies is the one to
3158 : : * consult.
3159 : : */
1027 3160 : 152 : bool dumpthis = false;
3161 : :
3162 [ + + ]: 366 : for (int i = 0; i < te->nDeps; i++)
3163 : : {
3164 : 214 : TocEntry *pte = getTocEntryByDumpId(AH, te->dependencies[i]);
3165 : :
3166 [ + + ]: 214 : if (!pte)
3167 : 68 : continue; /* probably shouldn't happen */
3168 [ + + ]: 146 : if (strcmp(pte->desc, "ACL") == 0)
3169 : 62 : continue; /* ignore dependency on another ACL */
3170 [ + - ]: 84 : if (pte->reqs == 0)
3171 : 84 : continue; /* this object isn't marked, so ignore it */
3172 : : /* Found a parent to be dumped, so we want to dump this too */
1027 tgl@sss.pgh.pa.us 3173 :UBC 0 : dumpthis = true;
3174 : 0 : break;
3175 : : }
1027 tgl@sss.pgh.pa.us 3176 [ + - ]:CBC 152 : if (!dumpthis)
9500 pjw@rhyme.com.au 3177 : 152 : return 0;
3178 : : }
3179 : : }
3180 : : else
3181 : : {
3182 : : /* Apply selective-restore rules for standalone TOC entries. */
3103 tgl@sss.pgh.pa.us 3183 [ + + ]: 41314 : if (ropt->schemaNames.head != NULL)
3184 : : {
3185 : : /* If no namespace is specified, it means all. */
3186 [ + + ]: 704 : if (!te->namespace)
9500 pjw@rhyme.com.au 3187 : 64 : return 0;
3103 tgl@sss.pgh.pa.us 3188 [ + + ]: 640 : if (!simple_string_list_member(&ropt->schemaNames, te->namespace))
9500 pjw@rhyme.com.au 3189 : 128 : return 0;
3190 : : }
3191 : :
3103 tgl@sss.pgh.pa.us 3192 [ + + ]: 41122 : if (ropt->schemaExcludeNames.head != NULL &&
3193 [ + + + + ]: 38 : te->namespace &&
3194 : 18 : simple_string_list_member(&ropt->schemaExcludeNames, te->namespace))
3195 : 4 : return 0;
3196 : :
3197 [ + + ]: 41118 : if (ropt->selTypes)
3198 : : {
39 michael@paquier.xyz 3199 [ + + ]: 528 : if (strcmp(te->desc, "STATISTICS DATA") == 0)
3200 : : {
3201 : 114 : bool dumpthis = false;
3202 : :
3203 : : /*
3204 : : * Statistics data entries can be for tables or indexes. Check
3205 : : * the parent dependency to determine which type this entry
3206 : : * belongs to, then apply the appropriate name filter.
3207 : : */
3208 [ + + ]: 376 : for (int i = 0; i < te->nDeps; i++)
3209 : : {
3210 : 262 : TocEntry *pte = getTocEntryByDumpId(AH, te->dependencies[i]);
3211 : :
3212 [ + + ]: 262 : if (!pte)
3213 : 134 : continue;
3214 : :
3215 [ + + ]: 128 : if (ropt->selTable &&
3216 [ + + ]: 64 : (strcmp(pte->desc, "TABLE") == 0 ||
3217 [ + - ]: 28 : strcmp(pte->desc, "VIEW") == 0 ||
3218 [ + + ]: 28 : strcmp(pte->desc, "FOREIGN TABLE") == 0 ||
3219 [ + + ]: 27 : strcmp(pte->desc, "MATERIALIZED VIEW") == 0))
3220 : : {
3221 [ + - + + ]: 92 : if (ropt->tableNames.head == NULL ||
3222 : 46 : simple_string_list_member(&ropt->tableNames, pte->tag))
3223 : 1 : dumpthis = true;
3224 : : }
3225 : :
3226 [ + + ]: 128 : if (ropt->selIndex &&
3227 [ + + ]: 64 : strcmp(pte->desc, "INDEX") == 0)
3228 : : {
3229 [ + - + + ]: 6 : if (ropt->indexNames.head == NULL ||
3230 : 3 : simple_string_list_member(&ropt->indexNames, pte->tag))
3231 : 1 : dumpthis = true;
3232 : : }
3233 : : }
3234 [ + + ]: 114 : if (!dumpthis)
3235 : 112 : return 0;
3236 : : }
3237 [ + + ]: 414 : else if (strcmp(te->desc, "TABLE") == 0 ||
3238 [ + + ]: 322 : strcmp(te->desc, "TABLE DATA") == 0 ||
3239 [ + + ]: 236 : strcmp(te->desc, "VIEW") == 0 ||
3240 [ + + ]: 234 : strcmp(te->desc, "FOREIGN TABLE") == 0 ||
3241 [ + + ]: 232 : strcmp(te->desc, "MATERIALIZED VIEW") == 0 ||
3242 [ + + ]: 214 : strcmp(te->desc, "MATERIALIZED VIEW DATA") == 0 ||
3243 [ + + ]: 200 : strcmp(te->desc, "SEQUENCE") == 0 ||
3244 [ + + ]: 191 : strcmp(te->desc, "SEQUENCE SET") == 0)
3245 : : {
3103 tgl@sss.pgh.pa.us 3246 [ + + ]: 232 : if (!ropt->selTable)
3247 : 123 : return 0;
3248 [ + - ]: 109 : if (ropt->tableNames.head != NULL &&
3249 [ + + ]: 109 : !simple_string_list_member(&ropt->tableNames, te->tag))
3250 : 105 : return 0;
3251 : : }
3252 [ + + ]: 182 : else if (strcmp(te->desc, "INDEX") == 0)
3253 : : {
3254 [ + + ]: 12 : if (!ropt->selIndex)
3255 : 7 : return 0;
3256 [ + - ]: 5 : if (ropt->indexNames.head != NULL &&
3257 [ + + ]: 5 : !simple_string_list_member(&ropt->indexNames, te->tag))
3258 : 3 : return 0;
3259 : : }
3260 [ + + ]: 170 : else if (strcmp(te->desc, "FUNCTION") == 0 ||
3261 [ + + ]: 144 : strcmp(te->desc, "AGGREGATE") == 0 ||
3262 [ + + ]: 142 : strcmp(te->desc, "PROCEDURE") == 0)
3263 : : {
3264 [ + + ]: 30 : if (!ropt->selFunction)
3265 : 22 : return 0;
3266 [ + - ]: 8 : if (ropt->functionNames.head != NULL &&
3267 [ + + ]: 8 : !simple_string_list_member(&ropt->functionNames, te->tag))
3268 : 6 : return 0;
3269 : : }
3270 [ + + ]: 140 : else if (strcmp(te->desc, "TRIGGER") == 0)
3271 : : {
3272 [ + + ]: 10 : if (!ropt->selTrigger)
3273 : 8 : return 0;
3274 [ + - ]: 2 : if (ropt->triggerNames.head != NULL &&
3275 [ + + ]: 2 : !simple_string_list_member(&ropt->triggerNames, te->tag))
3276 : 1 : return 0;
3277 : : }
3278 : : else
9500 pjw@rhyme.com.au 3279 : 130 : return 0;
3280 : : }
3281 : : }
3282 : :
3283 : :
3284 : : /*
3285 : : * Determine whether the TOC entry contains schema and/or data components,
3286 : : * and mask off inapplicable REQ bits. If it had a dataDumper, assume
3287 : : * it's both schema and data. Otherwise it's probably schema-only, but
3288 : : * there are exceptions.
3289 : : */
9028 bruce@momjian.us 3290 [ + + ]: 49998 : if (!te->hadDumper)
3291 : : {
3292 : : /*
3293 : : * Special Case: If 'SEQUENCE SET' or anything to do with LOs, then it
3294 : : * is considered a data entry. We don't need to check for BLOBS or
3295 : : * old-style BLOB COMMENTS entries, because they will have hadDumper =
3296 : : * true ... but we do need to check new-style BLOB ACLs, comments,
3297 : : * etc.
3298 : : */
6001 tgl@sss.pgh.pa.us 3299 [ + + ]: 45012 : if (strcmp(te->desc, "SEQUENCE SET") == 0 ||
3300 [ + - ]: 44534 : strcmp(te->desc, "BLOB") == 0 ||
845 3301 [ + + ]: 44534 : strcmp(te->desc, "BLOB METADATA") == 0 ||
6001 3302 [ + + ]: 44435 : (strcmp(te->desc, "ACL") == 0 &&
845 3303 [ + + ]: 2385 : strncmp(te->tag, "LARGE OBJECT", 12) == 0) ||
6001 3304 [ + + ]: 44395 : (strcmp(te->desc, "COMMENT") == 0 &&
845 3305 [ + + ]: 7003 : strncmp(te->tag, "LARGE OBJECT", 12) == 0) ||
5780 rhaas@postgresql.org 3306 [ + + ]: 44331 : (strcmp(te->desc, "SECURITY LABEL") == 0 &&
845 tgl@sss.pgh.pa.us 3307 [ + - ]: 9 : strncmp(te->tag, "LARGE OBJECT", 12) == 0))
9028 bruce@momjian.us 3308 : 690 : res = res & REQ_DATA;
3309 : : else
9029 pjw@rhyme.com.au 3310 : 44322 : res = res & ~REQ_DATA;
3311 : : }
3312 : :
3313 : : /*
3314 : : * If there's no definition command, there's no schema component. Treat
3315 : : * "load via partition root" comments as not schema.
3316 : : */
1226 tgl@sss.pgh.pa.us 3317 [ + + + - ]: 49998 : if (!te->defn || !te->defn[0] ||
3318 [ + + ]: 41562 : strncmp(te->defn, "-- load via partition root ", 27) == 0)
3103 3319 : 8530 : res = res & ~REQ_SCHEMA;
3320 : :
3321 : : /*
3322 : : * Special case: <Init> type with <Max OID> tag; this is obsolete and we
3323 : : * always ignore it.
3324 : : */
8787 bruce@momjian.us 3325 [ - + - - ]: 49998 : if ((strcmp(te->desc, "<Init>") == 0) && (strcmp(te->tag, "Max OID") == 0))
7652 tgl@sss.pgh.pa.us 3326 :UBC 0 : return 0;
3327 : :
3328 : : /* Mask it if we don't want data */
607 nathan@postgresql.or 3329 [ + + ]:CBC 49998 : if (!ropt->dumpData)
3330 : : {
3331 : : /*
3332 : : * The sequence_data option overrides dumpData for SEQUENCE SET.
3333 : : *
3334 : : * In binary-upgrade mode, even with dumpData unset, we do not mask
3335 : : * out large objects. (Only large object definitions, comments and
3336 : : * other metadata should be generated in binary-upgrade mode, not the
3337 : : * actual data, but that need not concern us here.)
3338 : : */
3428 sfrost@snowman.net 3339 [ + + + + ]: 4593 : if (!(ropt->sequence_data && strcmp(te->desc, "SEQUENCE SET") == 0) &&
3106 tgl@sss.pgh.pa.us 3340 [ + + ]: 4528 : !(ropt->binary_upgrade &&
3341 [ + - ]: 3986 : (strcmp(te->desc, "BLOB") == 0 ||
845 3342 [ + - ]: 3986 : strcmp(te->desc, "BLOB METADATA") == 0 ||
3106 3343 [ + + ]: 3986 : (strcmp(te->desc, "ACL") == 0 &&
845 3344 [ + - ]: 101 : strncmp(te->tag, "LARGE OBJECT", 12) == 0) ||
3106 3345 [ + + ]: 3986 : (strcmp(te->desc, "COMMENT") == 0 &&
845 3346 [ + + ]: 115 : strncmp(te->tag, "LARGE OBJECT", 12) == 0) ||
3106 3347 [ + + ]: 3978 : (strcmp(te->desc, "SECURITY LABEL") == 0 &&
845 3348 [ - + ]: 5 : strncmp(te->tag, "LARGE OBJECT", 12) == 0))))
520 jdavis@postgresql.or 3349 : 4515 : res = res & (REQ_SCHEMA | REQ_STATS);
3350 : : }
3351 : :
3352 : : /* Mask it if we don't want schema */
607 nathan@postgresql.or 3353 [ + + ]: 49998 : if (!ropt->dumpSchema)
520 jdavis@postgresql.or 3354 : 568 : res = res & (REQ_DATA | REQ_STATS);
3355 : :
9256 bruce@momjian.us 3356 : 49998 : return res;
3357 : : }
3358 : :
3359 : : /*
3360 : : * Identify which pass we should restore this TOC entry in.
3361 : : *
3362 : : * See notes with the RestorePass typedef in pg_backup_archiver.h.
3363 : : */
3364 : : static RestorePass
477 nathan@postgresql.or 3365 : 110454 : _tocEntryRestorePass(TocEntry *te)
3366 : : {
3367 : : /* "ACL LANGUAGE" was a crock emitted only in PG 7.4 */
3278 tgl@sss.pgh.pa.us 3368 [ + + ]: 110454 : if (strcmp(te->desc, "ACL") == 0 ||
3369 [ + - ]: 105245 : strcmp(te->desc, "ACL LANGUAGE") == 0 ||
3370 [ + + ]: 105245 : strcmp(te->desc, "DEFAULT ACL") == 0)
3371 : 5593 : return RESTORE_PASS_ACL;
2329 3372 [ + + ]: 104861 : if (strcmp(te->desc, "EVENT TRIGGER") == 0 ||
3373 [ + + ]: 104757 : strcmp(te->desc, "MATERIALIZED VIEW DATA") == 0)
3374 : 814 : return RESTORE_PASS_POST_ACL;
3375 : :
3376 : : /*
3377 : : * Comments and security labels need to be emitted in the same pass as
3378 : : * their parent objects. ACLs haven't got comments and security labels,
3379 : : * and neither do matview data objects, but event triggers do.
3380 : : * (Fortunately, event triggers haven't got ACLs, or we'd need yet another
3381 : : * weird special case.)
3382 : : */
312 fujii@postgresql.org 3383 [ + + ]: 104047 : if ((strcmp(te->desc, "COMMENT") == 0 ||
3384 [ + + ]: 90013 : strcmp(te->desc, "SECURITY LABEL") == 0) &&
2299 tgl@sss.pgh.pa.us 3385 [ - + ]: 14038 : strncmp(te->tag, "EVENT TRIGGER ", 14) == 0)
2299 tgl@sss.pgh.pa.us 3386 :UBC 0 : return RESTORE_PASS_POST_ACL;
3387 : :
3388 : : /*
3389 : : * If statistics data is dependent on materialized view data, it must be
3390 : : * deferred to RESTORE_PASS_POST_ACL. Those entries are already marked as
3391 : : * SECTION_POST_DATA, and some other stats entries (e.g., index stats)
3392 : : * will also be marked as SECTION_POST_DATA. Additionally, our lookahead
3393 : : * code in fetchAttributeStats() assumes that we dump all statistics data
3394 : : * entries in TOC order. To ensure this assumption holds, we move all
3395 : : * statistics data entries in SECTION_POST_DATA to RESTORE_PASS_POST_ACL.
3396 : : */
477 nathan@postgresql.or 3397 [ + + ]:CBC 104047 : if (strcmp(te->desc, "STATISTICS DATA") == 0 &&
3398 [ + + ]: 10021 : te->section == SECTION_POST_DATA)
3399 : 3512 : return RESTORE_PASS_POST_ACL;
3400 : :
3401 : : /* All else can be handled in the main pass. */
3278 tgl@sss.pgh.pa.us 3402 : 100535 : return RESTORE_PASS_MAIN;
3403 : : }
3404 : :
3405 : : /*
3406 : : * Identify TOC entries that are ACLs.
3407 : : *
3408 : : * Note: it seems worth duplicating some code here to avoid a hard-wired
3409 : : * assumption that these are exactly the same entries that we restore during
3410 : : * the RESTORE_PASS_ACL phase.
3411 : : */
3412 : : static bool
6001 3413 : 42532 : _tocEntryIsACL(TocEntry *te)
3414 : : {
3415 : : /* "ACL LANGUAGE" was a crock emitted only in PG 7.4 */
3416 [ + + ]: 42532 : if (strcmp(te->desc, "ACL") == 0 ||
3417 [ + - ]: 40725 : strcmp(te->desc, "ACL LANGUAGE") == 0 ||
3418 [ + + ]: 40725 : strcmp(te->desc, "DEFAULT ACL") == 0)
3419 : 1935 : return true;
3420 : 40597 : return false;
3421 : : }
3422 : :
3423 : : /*
3424 : : * Issue SET commands for parameters that we want to have set the same way
3425 : : * at all times during execution of a restore script.
3426 : : */
3427 : : static void
8187 3428 : 305 : _doSetFixedOutputState(ArchiveHandle *AH)
3429 : : {
3846 3430 : 305 : RestoreOptions *ropt = AH->public.ropt;
3431 : :
3432 : : /*
3433 : : * Disable timeouts to allow for slow commands, idle parallel workers, etc
3434 : : */
6656 andrew@dunslane.net 3435 : 305 : ahprintf(AH, "SET statement_timeout = 0;\n");
4879 tgl@sss.pgh.pa.us 3436 : 305 : ahprintf(AH, "SET lock_timeout = 0;\n");
3692 3437 : 305 : ahprintf(AH, "SET idle_in_transaction_session_timeout = 0;\n");
891 akorotkov@postgresql 3438 : 305 : ahprintf(AH, "SET transaction_timeout = 0;\n");
3439 : :
3440 : : /* Select the correct character set encoding */
7363 tgl@sss.pgh.pa.us 3441 : 305 : ahprintf(AH, "SET client_encoding = '%s';\n",
3442 : : pg_encoding_to_char(AH->public.encoding));
3443 : :
3444 : : /* Select the correct string literal syntax */
3445 : 305 : ahprintf(AH, "SET standard_conforming_strings = %s;\n",
3446 [ + - ]: 305 : AH->public.std_strings ? "on" : "off");
3447 : :
3448 : : /* Select the role to be used during restore */
3846 3449 [ + - - + ]: 305 : if (ropt && ropt->use_role)
3846 tgl@sss.pgh.pa.us 3450 :UBC 0 : ahprintf(AH, "SET ROLE %s;\n", fmtId(ropt->use_role));
3451 : :
3452 : : /* Select the dump-time search_path */
3071 tgl@sss.pgh.pa.us 3453 [ + - ]:CBC 305 : if (AH->public.searchpath)
3454 : 305 : ahprintf(AH, "%s", AH->public.searchpath);
3455 : :
3456 : : /* Make sure function checking is disabled */
8187 3457 : 305 : ahprintf(AH, "SET check_function_bodies = false;\n");
3458 : :
3459 : : /* Ensure that all valid XML data will be accepted */
2681 3460 : 305 : ahprintf(AH, "SET xmloption = content;\n");
3461 : :
3462 : : /* Avoid annoying notices etc */
8009 bruce@momjian.us 3463 : 305 : ahprintf(AH, "SET client_min_messages = warning;\n");
3464 : :
3465 : : /* Adjust row-security state */
3846 tgl@sss.pgh.pa.us 3466 [ + - - + ]: 305 : if (ropt && ropt->enable_row_security)
4175 tgl@sss.pgh.pa.us 3467 :UBC 0 : ahprintf(AH, "SET row_security = on;\n");
3468 : : else
4175 tgl@sss.pgh.pa.us 3469 :CBC 305 : ahprintf(AH, "SET row_security = off;\n");
3470 : :
3471 : : /*
3472 : : * In --transaction-size mode, we should always be in a transaction when
3473 : : * we begin to restore objects.
3474 : : */
845 3475 [ + - + + ]: 305 : if (ropt && ropt->txn_size > 0)
3476 : : {
3477 [ + - ]: 96 : if (AH->connection)
3478 : 96 : StartTransaction(&AH->public);
3479 : : else
845 tgl@sss.pgh.pa.us 3480 :UBC 0 : ahprintf(AH, "\nBEGIN;\n");
845 tgl@sss.pgh.pa.us 3481 :CBC 96 : AH->txnCount = 0;
3482 : : }
3483 : :
8187 3484 : 305 : ahprintf(AH, "\n");
3485 : 305 : }
3486 : :
3487 : : /*
3488 : : * Issue a SET SESSION AUTHORIZATION command. Caller is responsible
3489 : : * for updating state if appropriate. If user is NULL or an empty string,
3490 : : * the specification DEFAULT will be used.
3491 : : */
3492 : : static void
8742 peter_e@gmx.net 3493 : 1 : _doSetSessionAuth(ArchiveHandle *AH, const char *user)
3494 : : {
3495 : 1 : PQExpBuffer cmd = createPQExpBuffer();
3496 : :
4632 heikki.linnakangas@i 3497 : 1 : appendPQExpBufferStr(cmd, "SET SESSION AUTHORIZATION ");
3498 : :
3499 : : /*
3500 : : * SQL requires a string literal here. Might as well be correct.
3501 : : */
8341 tgl@sss.pgh.pa.us 3502 [ + - + - ]: 1 : if (user && *user)
7363 3503 : 1 : appendStringLiteralAHX(cmd, user, AH);
3504 : : else
4632 heikki.linnakangas@i 3505 :UBC 0 : appendPQExpBufferStr(cmd, "DEFAULT");
4632 heikki.linnakangas@i 3506 :CBC 1 : appendPQExpBufferChar(cmd, ';');
3507 : :
8842 tgl@sss.pgh.pa.us 3508 [ - + ]: 1 : if (RestoringToDB(AH))
3509 : : {
3510 : : PGresult *res;
3511 : :
8742 peter_e@gmx.net 3512 :UBC 0 : res = PQexec(AH->connection, cmd->data);
3513 : :
8842 tgl@sss.pgh.pa.us 3514 [ # # # # ]: 0 : if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
3515 : : /* NOT warn_or_exit_horribly... use -O instead to skip this. */
1569 3516 : 0 : pg_fatal("could not set session user to \"%s\": %s",
3517 : : user, PQerrorMessage(AH->connection));
3518 : :
8842 3519 : 0 : PQclear(res);
3520 : : }
3521 : : else
8742 peter_e@gmx.net 3522 :CBC 1 : ahprintf(AH, "%s\n\n", cmd->data);
3523 : :
3524 : 1 : destroyPQExpBuffer(cmd);
8842 tgl@sss.pgh.pa.us 3525 : 1 : }
3526 : :
3527 : :
3528 : : /*
3529 : : * Issue the commands to connect to the specified database.
3530 : : *
3531 : : * If we're currently restoring right into a database, this will
3532 : : * actually establish a connection. Otherwise it puts a \connect into
3533 : : * the script output.
3534 : : */
3535 : : static void
7988 3536 : 100 : _reconnectToDB(ArchiveHandle *AH, const char *dbname)
3537 : : {
8341 3538 [ + + ]: 100 : if (RestoringToDB(AH))
2130 3539 : 65 : ReconnectToServer(AH, dbname);
3540 : : else
3541 : : {
3542 : : PQExpBufferData connectbuf;
348 nathan@postgresql.or 3543 : 35 : RestoreOptions *ropt = AH->public.ropt;
3544 : :
3545 : : /*
3546 : : * We must temporarily exit restricted mode for \connect, etc.
3547 : : * Anything added between this line and the following \restrict must
3548 : : * be careful to avoid any possible meta-command injection vectors.
3549 : : */
3550 : 35 : ahprintf(AH, "\\unrestrict %s\n", ropt->restrict_key);
3551 : :
2130 tgl@sss.pgh.pa.us 3552 : 35 : initPQExpBuffer(&connectbuf);
3553 : 35 : appendPsqlMetaConnect(&connectbuf, dbname);
348 nathan@postgresql.or 3554 : 35 : ahprintf(AH, "%s", connectbuf.data);
2130 tgl@sss.pgh.pa.us 3555 : 35 : termPQExpBuffer(&connectbuf);
3556 : :
348 nathan@postgresql.or 3557 : 35 : ahprintf(AH, "\\restrict %s\n\n", ropt->restrict_key);
3558 : : }
3559 : :
3560 : : /*
3561 : : * NOTE: currUser keeps track of what the imaginary session user in our
3562 : : * script is. It's now effectively reset to the original userID.
3563 : : */
1500 peter@eisentraut.org 3564 : 100 : free(AH->currUser);
6382 andrew@dunslane.net 3565 : 100 : AH->currUser = NULL;
3566 : :
3567 : : /* don't assume we still know the output schema, tablespace, etc either */
1500 peter@eisentraut.org 3568 : 100 : free(AH->currSchema);
6382 andrew@dunslane.net 3569 : 100 : AH->currSchema = NULL;
3570 : :
1500 peter@eisentraut.org 3571 : 100 : free(AH->currTableAm);
1650 michael@paquier.xyz 3572 : 100 : AH->currTableAm = NULL;
3573 : :
1500 peter@eisentraut.org 3574 : 100 : free(AH->currTablespace);
6382 andrew@dunslane.net 3575 : 100 : AH->currTablespace = NULL;
3576 : :
3577 : : /* re-establish fixed state */
8187 tgl@sss.pgh.pa.us 3578 : 100 : _doSetFixedOutputState(AH);
9489 pjw@rhyme.com.au 3579 : 100 : }
3580 : :
3581 : : /*
3582 : : * Become the specified user, and update state to avoid redundant commands
3583 : : *
3584 : : * NULL or empty argument is taken to mean restoring the session default
3585 : : */
3586 : : static void
8341 tgl@sss.pgh.pa.us 3587 : 78 : _becomeUser(ArchiveHandle *AH, const char *user)
3588 : : {
3589 [ - + ]: 78 : if (!user)
8341 tgl@sss.pgh.pa.us 3590 :UBC 0 : user = ""; /* avoid null pointers */
3591 : :
8341 tgl@sss.pgh.pa.us 3592 [ + + + - ]:CBC 78 : if (AH->currUser && strcmp(AH->currUser, user) == 0)
3593 : 77 : return; /* no need to do anything */
3594 : :
3595 : 1 : _doSetSessionAuth(AH, user);
3596 : :
3597 : : /*
3598 : : * NOTE: currUser keeps track of what the imaginary session user in our
3599 : : * script is
3600 : : */
24 peter@eisentraut.org 3601 :GNC 1 : pg_free(AH->currUser);
5356 bruce@momjian.us 3602 :CBC 1 : AH->currUser = pg_strdup(user);
3603 : : }
3604 : :
3605 : : /*
3606 : : * Become the owner of the given TOC entry object. If
3607 : : * changes in ownership are not allowed, this doesn't do anything.
3608 : : */
3609 : : static void
8341 tgl@sss.pgh.pa.us 3610 : 47024 : _becomeOwner(ArchiveHandle *AH, TocEntry *te)
3611 : : {
3846 3612 : 47024 : RestoreOptions *ropt = AH->public.ropt;
3613 : :
3614 [ + - + + : 47024 : if (ropt && (ropt->noOwner || !ropt->use_setsessauth))
+ - ]
9489 pjw@rhyme.com.au 3615 : 47024 : return;
3616 : :
8341 tgl@sss.pgh.pa.us 3617 :UBC 0 : _becomeUser(AH, te->owner);
3618 : : }
3619 : :
3620 : :
3621 : : /*
3622 : : * Issue the commands to select the specified schema as the current schema
3623 : : * in the target database.
3624 : : */
3625 : : static void
8842 tgl@sss.pgh.pa.us 3626 :CBC 47098 : _selectOutputSchema(ArchiveHandle *AH, const char *schemaName)
3627 : : {
3628 : : PQExpBuffer qry;
3629 : :
3630 : : /*
3631 : : * If there was a SEARCHPATH TOC entry, we're supposed to just stay with
3632 : : * that search_path rather than switching to entry-specific paths.
3633 : : * Otherwise, it's an old archive that will not restore correctly unless
3634 : : * we set the search_path as it's expecting.
3635 : : */
3071 3636 [ + - ]: 47098 : if (AH->public.searchpath)
3637 : 47098 : return;
3638 : :
8842 tgl@sss.pgh.pa.us 3639 [ # # # # ]:UBC 0 : if (!schemaName || *schemaName == '\0' ||
7402 3640 [ # # # # ]: 0 : (AH->currSchema && strcmp(AH->currSchema, schemaName) == 0))
8842 3641 : 0 : return; /* no need to do anything */
3642 : :
8824 3643 : 0 : qry = createPQExpBuffer();
3644 : :
3645 : 0 : appendPQExpBuffer(qry, "SET search_path = %s",
3646 : : fmtId(schemaName));
3647 [ # # ]: 0 : if (strcmp(schemaName, "pg_catalog") != 0)
4632 heikki.linnakangas@i 3648 : 0 : appendPQExpBufferStr(qry, ", pg_catalog");
3649 : :
8842 tgl@sss.pgh.pa.us 3650 [ # # ]: 0 : if (RestoringToDB(AH))
3651 : : {
3652 : : PGresult *res;
3653 : :
3654 : 0 : res = PQexec(AH->connection, qry->data);
3655 : :
3656 [ # # # # ]: 0 : if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
2672 peter@eisentraut.org 3657 : 0 : warn_or_exit_horribly(AH,
3658 : : "could not set \"search_path\" to \"%s\": %s",
5240 alvherre@alvh.no-ip. 3659 : 0 : schemaName, PQerrorMessage(AH->connection));
3660 : :
8842 tgl@sss.pgh.pa.us 3661 : 0 : PQclear(res);
3662 : : }
3663 : : else
8824 3664 : 0 : ahprintf(AH, "%s;\n\n", qry->data);
3665 : :
24 peter@eisentraut.org 3666 :UNC 0 : pg_free(AH->currSchema);
5356 bruce@momjian.us 3667 :UBC 0 : AH->currSchema = pg_strdup(schemaName);
3668 : :
8824 tgl@sss.pgh.pa.us 3669 : 0 : destroyPQExpBuffer(qry);
3670 : : }
3671 : :
3672 : : /*
3673 : : * Issue the commands to select the specified tablespace as the current one
3674 : : * in the target database.
3675 : : */
3676 : : static void
7931 tgl@sss.pgh.pa.us 3677 :CBC 42189 : _selectTablespace(ArchiveHandle *AH, const char *tablespace)
3678 : : {
3846 3679 : 42189 : RestoreOptions *ropt = AH->public.ropt;
3680 : : PQExpBuffer qry;
3681 : : const char *want,
3682 : : *have;
3683 : :
3684 : : /* do nothing in --no-tablespaces mode */
3685 [ - + ]: 42189 : if (ropt->noTablespace)
6701 tgl@sss.pgh.pa.us 3686 :UBC 0 : return;
3687 : :
7931 tgl@sss.pgh.pa.us 3688 :CBC 42189 : have = AH->currTablespace;
3689 : 42189 : want = tablespace;
3690 : :
3691 : : /* no need to do anything for non-tablespace object */
3692 [ + + ]: 42189 : if (!want)
3693 : 33524 : return;
3694 : :
3695 [ + + + + ]: 8665 : if (have && strcmp(want, have) == 0)
3696 : 8434 : return; /* no need to do anything */
3697 : :
3698 : 231 : qry = createPQExpBuffer();
3699 : :
3700 [ + + ]: 231 : if (strcmp(want, "") == 0)
3701 : : {
3702 : : /* We want the tablespace to be the database's default */
4632 heikki.linnakangas@i 3703 : 173 : appendPQExpBufferStr(qry, "SET default_tablespace = ''");
3704 : : }
3705 : : else
3706 : : {
3707 : : /* We want an explicit tablespace */
7931 tgl@sss.pgh.pa.us 3708 : 58 : appendPQExpBuffer(qry, "SET default_tablespace = %s", fmtId(want));
3709 : : }
3710 : :
3711 [ + + ]: 231 : if (RestoringToDB(AH))
3712 : : {
3713 : : PGresult *res;
3714 : :
3715 : 29 : res = PQexec(AH->connection, qry->data);
3716 : :
3717 [ + - - + ]: 29 : if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
2672 peter@eisentraut.org 3718 :UBC 0 : warn_or_exit_horribly(AH,
3719 : : "could not set \"default_tablespace\" to %s: %s",
3321 tgl@sss.pgh.pa.us 3720 : 0 : fmtId(want), PQerrorMessage(AH->connection));
3721 : :
7931 tgl@sss.pgh.pa.us 3722 :CBC 29 : PQclear(res);
3723 : : }
3724 : : else
3725 : 202 : ahprintf(AH, "%s;\n\n", qry->data);
3726 : :
24 peter@eisentraut.org 3727 :GNC 231 : pg_free(AH->currTablespace);
5356 bruce@momjian.us 3728 :CBC 231 : AH->currTablespace = pg_strdup(want);
3729 : :
7931 tgl@sss.pgh.pa.us 3730 : 231 : destroyPQExpBuffer(qry);
3731 : : }
3732 : :
3733 : : /*
3734 : : * Set the proper default_table_access_method value for the table.
3735 : : */
3736 : : static void
2698 andres@anarazel.de 3737 : 41592 : _selectTableAccessMethod(ArchiveHandle *AH, const char *tableam)
3738 : : {
1650 michael@paquier.xyz 3739 : 41592 : RestoreOptions *ropt = AH->public.ropt;
3740 : : PQExpBuffer cmd;
3741 : : const char *want,
3742 : : *have;
3743 : :
3744 : : /* do nothing in --no-table-access-method mode */
3745 [ + + ]: 41592 : if (ropt->noTableAm)
3746 : 385 : return;
3747 : :
2698 andres@anarazel.de 3748 : 41207 : have = AH->currTableAm;
3749 : 41207 : want = tableam;
3750 : :
3751 [ + + ]: 41207 : if (!want)
3752 : 35848 : return;
3753 : :
3754 [ + + + + ]: 5359 : if (have && strcmp(want, have) == 0)
3755 : 5048 : return;
3756 : :
3757 : 311 : cmd = createPQExpBuffer();
3758 : 311 : appendPQExpBuffer(cmd, "SET default_table_access_method = %s;", fmtId(want));
3759 : :
3760 [ + + ]: 311 : if (RestoringToDB(AH))
3761 : : {
3762 : : PGresult *res;
3763 : :
3764 : 24 : res = PQexec(AH->connection, cmd->data);
3765 : :
3766 [ + - - + ]: 24 : if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
2672 peter@eisentraut.org 3767 :UBC 0 : warn_or_exit_horribly(AH,
3768 : : "could not set \"default_table_access_method\": %s",
2698 andres@anarazel.de 3769 : 0 : PQerrorMessage(AH->connection));
3770 : :
2698 andres@anarazel.de 3771 :CBC 24 : PQclear(res);
3772 : : }
3773 : : else
3774 : 287 : ahprintf(AH, "%s\n\n", cmd->data);
3775 : :
3776 : 311 : destroyPQExpBuffer(cmd);
3777 : :
24 peter@eisentraut.org 3778 :GNC 311 : pg_free(AH->currTableAm);
2698 andres@anarazel.de 3779 :CBC 311 : AH->currTableAm = pg_strdup(want);
3780 : : }
3781 : :
3782 : : /*
3783 : : * Set the proper default table access method for a table without storage.
3784 : : * Currently, this is required only for partitioned tables with a table AM.
3785 : : */
3786 : : static void
824 michael@paquier.xyz 3787 : 597 : _printTableAccessMethodNoStorage(ArchiveHandle *AH, TocEntry *te)
3788 : : {
3789 : 597 : RestoreOptions *ropt = AH->public.ropt;
3790 : 597 : const char *tableam = te->tableam;
3791 : : PQExpBuffer cmd;
3792 : :
3793 : : /* do nothing in --no-table-access-method mode */
3794 [ + + ]: 597 : if (ropt->noTableAm)
3795 : 4 : return;
3796 : :
3797 [ + + ]: 593 : if (!tableam)
3798 : 563 : return;
3799 : :
3800 [ - + ]: 30 : Assert(te->relkind == RELKIND_PARTITIONED_TABLE);
3801 : :
3802 : 30 : cmd = createPQExpBuffer();
3803 : :
3804 : 30 : appendPQExpBufferStr(cmd, "ALTER TABLE ");
3805 : 30 : appendPQExpBuffer(cmd, "%s ", fmtQualifiedId(te->namespace, te->tag));
3806 : 30 : appendPQExpBuffer(cmd, "SET ACCESS METHOD %s;",
3807 : : fmtId(tableam));
3808 : :
3809 [ - + ]: 30 : if (RestoringToDB(AH))
3810 : : {
3811 : : PGresult *res;
3812 : :
824 michael@paquier.xyz 3813 :UBC 0 : res = PQexec(AH->connection, cmd->data);
3814 : :
3815 [ # # # # ]: 0 : if (!res || PQresultStatus(res) != PGRES_COMMAND_OK)
3816 : 0 : warn_or_exit_horribly(AH,
3817 : : "could not alter table access method: %s",
3818 : 0 : PQerrorMessage(AH->connection));
3819 : 0 : PQclear(res);
3820 : : }
3821 : : else
824 michael@paquier.xyz 3822 :CBC 30 : ahprintf(AH, "%s\n\n", cmd->data);
3823 : :
3824 : 30 : destroyPQExpBuffer(cmd);
3825 : : }
3826 : :
3827 : : /*
3828 : : * Extract an object description for a TOC entry, and append it to buf.
3829 : : *
3830 : : * This is used for ALTER ... OWNER TO.
3831 : : *
3832 : : * If the object type has no owner, do nothing.
3833 : : */
3834 : : static void
1361 peter@eisentraut.org 3835 : 21431 : _getObjectDescription(PQExpBuffer buf, const TocEntry *te)
3836 : : {
7865 tgl@sss.pgh.pa.us 3837 : 21431 : const char *type = te->desc;
3838 : :
3839 : : /* objects that don't require special decoration */
5642 peter_e@gmx.net 3840 [ + + ]: 21431 : if (strcmp(type, "COLLATION") == 0 ||
3841 [ + + ]: 18718 : strcmp(type, "CONVERSION") == 0 ||
7865 tgl@sss.pgh.pa.us 3842 [ + + ]: 18394 : strcmp(type, "DOMAIN") == 0 ||
5684 rhaas@postgresql.org 3843 [ + + ]: 18224 : strcmp(type, "FOREIGN TABLE") == 0 ||
1361 peter@eisentraut.org 3844 [ + + ]: 18192 : strcmp(type, "MATERIALIZED VIEW") == 0 ||
131 3845 [ + + ]: 17876 : strcmp(type, "PROPERTY GRAPH") == 0 ||
1361 3846 [ + + ]: 17776 : strcmp(type, "SEQUENCE") == 0 ||
3847 [ + + ]: 17548 : strcmp(type, "STATISTICS") == 0 ||
3848 [ + + ]: 17409 : strcmp(type, "TABLE") == 0 ||
6913 tgl@sss.pgh.pa.us 3849 [ + + ]: 11775 : strcmp(type, "TEXT SEARCH DICTIONARY") == 0 ||
4729 bruce@momjian.us 3850 [ + + ]: 11604 : strcmp(type, "TEXT SEARCH CONFIGURATION") == 0 ||
1361 peter@eisentraut.org 3851 [ + + ]: 11458 : strcmp(type, "TYPE") == 0 ||
3852 [ + + ]: 10837 : strcmp(type, "VIEW") == 0 ||
3853 : : /* non-schema-specified objects */
4729 bruce@momjian.us 3854 [ + + ]: 10268 : strcmp(type, "DATABASE") == 0 ||
7061 tgl@sss.pgh.pa.us 3855 [ + + ]: 10204 : strcmp(type, "PROCEDURAL LANGUAGE") == 0 ||
6427 peter_e@gmx.net 3856 [ + + ]: 10175 : strcmp(type, "SCHEMA") == 0 ||
3290 tgl@sss.pgh.pa.us 3857 [ + + ]: 9976 : strcmp(type, "EVENT TRIGGER") == 0 ||
6427 peter_e@gmx.net 3858 [ + + ]: 9942 : strcmp(type, "FOREIGN DATA WRAPPER") == 0 ||
3859 [ + + ]: 9900 : strcmp(type, "SERVER") == 0 ||
3474 3860 [ + + ]: 9856 : strcmp(type, "PUBLICATION") == 0 ||
1361 peter@eisentraut.org 3861 [ + + ]: 9540 : strcmp(type, "SUBSCRIPTION") == 0)
3862 : : {
3071 tgl@sss.pgh.pa.us 3863 : 11974 : appendPQExpBuffer(buf, "%s ", type);
3864 [ + + + - ]: 11974 : if (te->namespace && *te->namespace)
3865 : 11163 : appendPQExpBuffer(buf, "%s.", fmtId(te->namespace));
3866 : 11974 : appendPQExpBufferStr(buf, fmtId(te->tag));
3867 : : }
3868 : : /* LOs just have a name, but it's numeric so must not use fmtId */
1361 peter@eisentraut.org 3869 [ - + ]: 9457 : else if (strcmp(type, "BLOB") == 0)
3870 : : {
6001 tgl@sss.pgh.pa.us 3871 :UBC 0 : appendPQExpBuffer(buf, "LARGE OBJECT %s", te->tag);
3872 : : }
3873 : :
3874 : : /*
3875 : : * These object types require additional decoration. Fortunately, the
3876 : : * information needed is exactly what's in the DROP command.
3877 : : */
1361 peter@eisentraut.org 3878 [ + + ]:CBC 9457 : else if (strcmp(type, "AGGREGATE") == 0 ||
3879 [ + + ]: 9191 : strcmp(type, "FUNCTION") == 0 ||
3880 [ + + ]: 7565 : strcmp(type, "OPERATOR") == 0 ||
3881 [ + + ]: 5065 : strcmp(type, "OPERATOR CLASS") == 0 ||
3882 [ + + ]: 4423 : strcmp(type, "OPERATOR FAMILY") == 0 ||
3883 [ + + ]: 3884 : strcmp(type, "PROCEDURE") == 0)
3884 : : {
3885 : : /* Chop "DROP " off the front and make a modifiable copy */
5356 bruce@momjian.us 3886 : 5663 : char *first = pg_strdup(te->dropStmt + 5);
3887 : : char *last;
3888 : :
3889 : : /* point to last character in string */
7865 tgl@sss.pgh.pa.us 3890 : 5663 : last = first + strlen(first) - 1;
3891 : :
3892 : : /* Strip off any ';' or '\n' at the end */
3893 [ + - + + : 16989 : while (last >= first && (*last == '\n' || *last == ';'))
+ + ]
3894 : 11326 : last--;
3895 : 5663 : *(last + 1) = '\0';
3896 : :
3897 : 5663 : appendPQExpBufferStr(buf, first);
3898 : :
24 peter@eisentraut.org 3899 :GNC 5663 : pg_free(first);
7865 tgl@sss.pgh.pa.us 3900 :CBC 5663 : return;
3901 : : }
3902 : : /* these object types don't have separate owners */
1361 peter@eisentraut.org 3903 [ + - ]: 3794 : else if (strcmp(type, "CAST") == 0 ||
3904 [ + + ]: 3794 : strcmp(type, "CHECK CONSTRAINT") == 0 ||
3905 [ + + ]: 3744 : strcmp(type, "CONSTRAINT") == 0 ||
3906 [ + + ]: 2097 : strcmp(type, "DATABASE PROPERTIES") == 0 ||
3907 [ + + ]: 2087 : strcmp(type, "DEFAULT") == 0 ||
3908 [ + + ]: 1927 : strcmp(type, "FK CONSTRAINT") == 0 ||
3909 [ + + ]: 1712 : strcmp(type, "INDEX") == 0 ||
3910 [ + + ]: 833 : strcmp(type, "RULE") == 0 ||
3911 [ + + ]: 622 : strcmp(type, "TRIGGER") == 0 ||
3912 [ + - ]: 242 : strcmp(type, "ROW SECURITY") == 0 ||
3913 [ + + ]: 242 : strcmp(type, "POLICY") == 0 ||
3914 [ - + ]: 29 : strcmp(type, "USER MAPPING") == 0)
3915 : : {
3916 : : /* do nothing */
3917 : : }
3918 : : else
1361 peter@eisentraut.org 3919 :UBC 0 : pg_fatal("don't know how to set owner for object type \"%s\"", type);
3920 : : }
3921 : :
3922 : : /*
3923 : : * Emit the SQL commands to create the object represented by a TOC entry
3924 : : *
3925 : : * This now also includes issuing an ALTER OWNER command to restore the
3926 : : * object's ownership, if wanted. But note that the object's permissions
3927 : : * will remain at default, until the matching ACL TOC entry is restored.
3928 : : */
3929 : : static void
520 jdavis@postgresql.or 3930 :CBC 42189 : _printTocEntry(ArchiveHandle *AH, TocEntry *te, const char *pfx)
3931 : : {
3846 tgl@sss.pgh.pa.us 3932 : 42189 : RestoreOptions *ropt = AH->public.ropt;
3933 : :
3934 : : /*
3935 : : * Select owner, schema, tablespace and default AM as necessary. The
3936 : : * default access method for partitioned tables is handled after
3937 : : * generating the object definition, as it requires an ALTER command
3938 : : * rather than SET.
3939 : : */
8016 3940 : 42189 : _becomeOwner(AH, te);
3941 : 42189 : _selectOutputSchema(AH, te->namespace);
7931 3942 : 42189 : _selectTablespace(AH, te->tablespace);
824 michael@paquier.xyz 3943 [ + + ]: 42189 : if (te->relkind != RELKIND_PARTITIONED_TABLE)
3944 : 41592 : _selectTableAccessMethod(AH, te->tableam);
3945 : :
3946 : : /* Emit header comment for item */
7999 tgl@sss.pgh.pa.us 3947 [ + + ]: 42189 : if (!AH->noTocComments)
3948 : : {
3949 : : char *sanitized_name;
3950 : : char *sanitized_schema;
3951 : : char *sanitized_owner;
3952 : :
3953 : 38392 : ahprintf(AH, "--\n");
3954 [ + + ]: 38392 : if (AH->public.verbose)
3955 : : {
3956 : 1176 : ahprintf(AH, "-- TOC entry %d (class %u OID %u)\n",
3957 : : te->dumpId, te->catalogId.tableoid, te->catalogId.oid);
3958 [ + + ]: 1176 : if (te->nDeps > 0)
3959 : : {
3960 : : int i;
3961 : :
3962 : 772 : ahprintf(AH, "-- Dependencies:");
3963 [ + + ]: 2102 : for (i = 0; i < te->nDeps; i++)
3964 : 1330 : ahprintf(AH, " %d", te->dependencies[i]);
3965 : 772 : ahprintf(AH, "\n");
3966 : : }
3967 : : }
3968 : :
2731 alvherre@alvh.no-ip. 3969 : 38392 : sanitized_name = sanitize_line(te->tag, false);
3970 : 38392 : sanitized_schema = sanitize_line(te->namespace, true);
3971 [ + + ]: 38392 : sanitized_owner = sanitize_line(ropt->noOwner ? NULL : te->owner, true);
3972 : :
7931 tgl@sss.pgh.pa.us 3973 : 38392 : ahprintf(AH, "-- %sName: %s; Type: %s; Schema: %s; Owner: %s",
3974 : : pfx, sanitized_name, te->desc, sanitized_schema,
3975 : : sanitized_owner);
3976 : :
5266 3977 : 38392 : free(sanitized_name);
3978 : 38392 : free(sanitized_schema);
3979 : 38392 : free(sanitized_owner);
3980 : :
4093 bruce@momjian.us 3981 [ + + + + : 38392 : if (te->tablespace && strlen(te->tablespace) > 0 && !ropt->noTablespace)
+ - ]
3982 : : {
3983 : : char *sanitized_tablespace;
3984 : :
2731 alvherre@alvh.no-ip. 3985 : 102 : sanitized_tablespace = sanitize_line(te->tablespace, false);
5266 tgl@sss.pgh.pa.us 3986 : 102 : ahprintf(AH, "; Tablespace: %s", sanitized_tablespace);
3987 : 102 : free(sanitized_tablespace);
3988 : : }
7931 3989 : 38392 : ahprintf(AH, "\n");
3990 : :
3356 bruce@momjian.us 3991 [ + + ]: 38392 : if (AH->PrintExtraTocPtr != NULL)
3243 peter_e@gmx.net 3992 : 3318 : AH->PrintExtraTocPtr(AH, te);
7999 tgl@sss.pgh.pa.us 3993 : 38392 : ahprintf(AH, "--\n\n");
3994 : : }
3995 : :
3996 : : /*
3997 : : * Actually print the definition. Normally we can just print the defn
3998 : : * string if any, but we have four special cases:
3999 : : *
4000 : : * 1. A crude hack for suppressing AUTHORIZATION clause that old pg_dump
4001 : : * versions put into CREATE SCHEMA. Don't mutate the variant for schema
4002 : : * "public" that is a comment. We have to do this when --no-owner mode is
4003 : : * selected. This is ugly, but I see no other good way ...
4004 : : *
4005 : : * 2. BLOB METADATA entries need special processing since their defn
4006 : : * strings are just lists of OIDs, not complete SQL commands.
4007 : : *
4008 : : * 3. ACL LARGE OBJECTS entries need special processing because they
4009 : : * contain only one copy of the ACL GRANT/REVOKE commands, which we must
4010 : : * apply to each large object listed in the associated BLOB METADATA.
4011 : : *
4012 : : * 4. Entries with a defnDumper need to call it to generate the
4013 : : * definition. This is primarily intended to provide a way to save memory
4014 : : * for objects that would otherwise need a lot of it (e.g., statistics
4015 : : * data).
4016 : : */
1853 noah@leadboat.com 4017 [ + + ]: 42189 : if (ropt->noOwner &&
4018 [ + + + + ]: 405 : strcmp(te->desc, "SCHEMA") == 0 && strncmp(te->defn, "--", 2) != 0)
4019 : : {
7865 tgl@sss.pgh.pa.us 4020 : 2 : ahprintf(AH, "CREATE SCHEMA %s;\n\n\n", fmtId(te->tag));
4021 : : }
845 4022 [ + + ]: 42187 : else if (strcmp(te->desc, "BLOB METADATA") == 0)
4023 : : {
4024 : 74 : IssueCommandPerBlob(AH, te, "SELECT pg_catalog.lo_create('", "')");
4025 : : }
4026 [ + + ]: 42113 : else if (strcmp(te->desc, "ACL") == 0 &&
4027 [ - + ]: 1807 : strncmp(te->tag, "LARGE OBJECTS", 13) == 0)
4028 : : {
845 tgl@sss.pgh.pa.us 4029 :UBC 0 : IssueACLPerBlob(AH, te);
4030 : : }
477 nathan@postgresql.or 4031 [ + + - + ]:CBC 42113 : else if (te->defnLen && AH->format != archTar)
4032 : : {
4033 : : /*
4034 : : * If defnLen is set, the defnDumper has already been called for this
4035 : : * TOC entry. We don't normally expect a defnDumper to be called for
4036 : : * a TOC entry a second time in _printTocEntry(), but there's an
4037 : : * exception. The tar format first calls WriteToc(), which scans the
4038 : : * entire TOC, and then it later calls RestoreArchive() to generate
4039 : : * restore.sql, which scans the TOC again. There doesn't appear to be
4040 : : * a good way to prevent a second defnDumper call in this case without
4041 : : * storing the definition in memory, which defeats the purpose. This
4042 : : * second defnDumper invocation should generate the same output as the
4043 : : * first, but even if it doesn't, the worst-case scenario is that
4044 : : * restore.sql might have different statistics data than the archive.
4045 : : *
4046 : : * In all other cases, encountering a TOC entry a second time in
4047 : : * _printTocEntry() is unexpected, so we fail because one of our
4048 : : * assumptions must no longer hold true.
4049 : : *
4050 : : * XXX This is a layering violation, but the alternative is an awkward
4051 : : * and complicated callback infrastructure for this special case. This
4052 : : * might be worth revisiting in the future.
4053 : : */
477 nathan@postgresql.or 4054 :UBC 0 : pg_fatal("unexpected TOC entry in _printTocEntry(): %d %s %s",
4055 : : te->dumpId, te->desc, te->tag);
4056 : : }
477 nathan@postgresql.or 4057 [ + + ]:CBC 42113 : else if (te->defnDumper)
4058 : : {
4059 : 1738 : char *defn = te->defnDumper((Archive *) AH, te->defnDumperArg, te);
4060 : :
4061 : 1738 : te->defnLen = ahprintf(AH, "%s\n\n", defn);
4062 : 1738 : pg_free(defn);
4063 : : }
726 tgl@sss.pgh.pa.us 4064 [ + + + - ]: 40375 : else if (te->defn && strlen(te->defn) > 0)
4065 : : {
4066 : 35898 : ahprintf(AH, "%s\n\n", te->defn);
4067 : :
4068 : : /*
4069 : : * If the defn string contains multiple SQL commands, txn_size mode
4070 : : * should count it as N actions not one. But rather than build a full
4071 : : * SQL parser, approximate this by counting semicolons. One case
4072 : : * where that tends to be badly fooled is function definitions, so
4073 : : * ignore them. (restore_toc_entry will count one action anyway.)
4074 : : */
4075 [ + + ]: 35898 : if (ropt->txn_size > 0 &&
4076 [ + + ]: 3684 : strcmp(te->desc, "FUNCTION") != 0 &&
4077 [ + + ]: 3401 : strcmp(te->desc, "PROCEDURE") != 0)
4078 : : {
4079 : 3389 : const char *p = te->defn;
4080 : 3389 : int nsemis = 0;
4081 : :
4082 [ + + ]: 14983 : while ((p = strchr(p, ';')) != NULL)
4083 : : {
4084 : 11594 : nsemis++;
4085 : 11594 : p++;
4086 : : }
4087 [ + + ]: 3389 : if (nsemis > 1)
4088 : 1652 : AH->txnCount += nsemis - 1;
4089 : : }
4090 : : }
4091 : :
4092 : : /*
4093 : : * If we aren't using SET SESSION AUTH to determine ownership, we must
4094 : : * instead issue an ALTER OWNER command. Schema "public" is special; when
4095 : : * a dump emits a comment in lieu of creating it, we use ALTER OWNER even
4096 : : * when using SET SESSION for all other objects. We assume that anything
4097 : : * without a DROP command is not a separately ownable object.
4098 : : */
1853 noah@leadboat.com 4099 [ + + ]: 42189 : if (!ropt->noOwner &&
4100 [ - + ]: 41784 : (!ropt->use_setsessauth ||
1853 noah@leadboat.com 4101 [ # # ]:UBC 0 : (strcmp(te->desc, "SCHEMA") == 0 &&
4102 [ # # ]: 0 : strncmp(te->defn, "--", 2) == 0)) &&
2647 alvherre@alvh.no-ip. 4103 [ + + + + ]:CBC 41784 : te->owner && strlen(te->owner) > 0 &&
4104 [ + + + + ]: 37968 : te->dropStmt && strlen(te->dropStmt) > 0)
4105 : : {
845 tgl@sss.pgh.pa.us 4106 [ + + ]: 21503 : if (strcmp(te->desc, "BLOB METADATA") == 0)
4107 : : {
4108 : : /* BLOB METADATA needs special code to handle multiple LOs */
4109 : 72 : char *cmdEnd = psprintf(" OWNER TO %s", fmtId(te->owner));
4110 : :
4111 : 72 : IssueCommandPerBlob(AH, te, "ALTER LARGE OBJECT ", cmdEnd);
24 peter@eisentraut.org 4112 :GNC 72 : pfree(cmdEnd);
4113 : : }
4114 : : else
4115 : : {
4116 : : /* For all other cases, we can use _getObjectDescription */
4117 : : PQExpBufferData temp;
4118 : :
845 tgl@sss.pgh.pa.us 4119 :CBC 21431 : initPQExpBuffer(&temp);
4120 : 21431 : _getObjectDescription(&temp, te);
4121 : :
4122 : : /*
4123 : : * If _getObjectDescription() didn't fill the buffer, then there
4124 : : * is no owner.
4125 : : */
4126 [ + + ]: 21431 : if (temp.data[0])
4127 : 17637 : ahprintf(AH, "ALTER %s OWNER TO %s;\n\n",
4128 : 17637 : temp.data, fmtId(te->owner));
4129 : 21431 : termPQExpBuffer(&temp);
4130 : : }
4131 : : }
4132 : :
4133 : : /*
4134 : : * Select a partitioned table's default AM, once the table definition has
4135 : : * been generated.
4136 : : */
824 michael@paquier.xyz 4137 [ + + ]: 42189 : if (te->relkind == RELKIND_PARTITIONED_TABLE)
4138 : 597 : _printTableAccessMethodNoStorage(AH, te);
4139 : :
4140 : : /*
4141 : : * If it's an ACL entry, it might contain SET SESSION AUTHORIZATION
4142 : : * commands, so we can no longer assume we know the current auth setting.
4143 : : */
3278 tgl@sss.pgh.pa.us 4144 [ + + ]: 42189 : if (_tocEntryIsACL(te))
4145 : : {
1500 peter@eisentraut.org 4146 : 1935 : free(AH->currUser);
8041 tgl@sss.pgh.pa.us 4147 : 1935 : AH->currUser = NULL;
4148 : : }
9517 bruce@momjian.us 4149 : 42189 : }
4150 : :
4151 : : /*
4152 : : * Write the file header for a custom-format archive
4153 : : */
4154 : : void
9256 4155 : 63 : WriteHead(ArchiveHandle *AH)
4156 : : {
4157 : : struct tm crtm;
4158 : :
3243 peter_e@gmx.net 4159 : 63 : AH->WriteBufPtr(AH, "PGDMP", 5); /* Magic code */
4160 : 63 : AH->WriteBytePtr(AH, ARCHIVE_MAJOR(AH->version));
4161 : 63 : AH->WriteBytePtr(AH, ARCHIVE_MINOR(AH->version));
4162 : 63 : AH->WriteBytePtr(AH, ARCHIVE_REV(AH->version));
4163 : 63 : AH->WriteBytePtr(AH, AH->intSize);
4164 : 63 : AH->WriteBytePtr(AH, AH->offSize);
4165 : 63 : AH->WriteBytePtr(AH, AH->format);
1248 tomas.vondra@postgre 4166 : 63 : AH->WriteBytePtr(AH, AH->compression_spec.algorithm);
9500 pjw@rhyme.com.au 4167 : 63 : crtm = *localtime(&AH->createDate);
4168 : 63 : WriteInt(AH, crtm.tm_sec);
4169 : 63 : WriteInt(AH, crtm.tm_min);
4170 : 63 : WriteInt(AH, crtm.tm_hour);
4171 : 63 : WriteInt(AH, crtm.tm_mday);
4172 : 63 : WriteInt(AH, crtm.tm_mon);
4173 : 63 : WriteInt(AH, crtm.tm_year);
4174 : 63 : WriteInt(AH, crtm.tm_isdst);
8750 peter_e@gmx.net 4175 : 63 : WriteStr(AH, PQdb(AH->connection));
7931 tgl@sss.pgh.pa.us 4176 : 63 : WriteStr(AH, AH->public.remoteVersionStr);
4177 : 63 : WriteStr(AH, PG_VERSION);
9517 bruce@momjian.us 4178 : 63 : }
4179 : :
4180 : : void
9256 4181 : 65 : ReadHead(ArchiveHandle *AH)
4182 : : {
4183 : : char *errmsg;
4184 : : char vmaj,
4185 : : vmin,
4186 : : vrev;
4187 : : int fmt;
4188 : :
4189 : : /*
4190 : : * If we haven't already read the header, do so.
4191 : : *
4192 : : * NB: this code must agree with _discoverArchiveFormat(). Maybe find a
4193 : : * way to unify the cases?
4194 : : */
4195 [ + - ]: 65 : if (!AH->readHeader)
4196 : : {
4197 : : char tmpMag[7];
4198 : :
3243 peter_e@gmx.net 4199 : 65 : AH->ReadBufPtr(AH, tmpMag, 5);
4200 : :
9256 bruce@momjian.us 4201 [ - + ]: 65 : if (strncmp(tmpMag, "PGDMP", 5) != 0)
1569 tgl@sss.pgh.pa.us 4202 :UBC 0 : pg_fatal("did not find magic string in file header");
4203 : : }
4204 : :
1941 tgl@sss.pgh.pa.us 4205 :CBC 65 : vmaj = AH->ReadBytePtr(AH);
4206 : 65 : vmin = AH->ReadBytePtr(AH);
4207 : :
4208 [ + - + - : 65 : if (vmaj > 1 || (vmaj == 1 && vmin > 0)) /* Version > 1.0 */
+ - ]
4209 : 65 : vrev = AH->ReadBytePtr(AH);
4210 : : else
1941 tgl@sss.pgh.pa.us 4211 :UBC 0 : vrev = 0;
4212 : :
1941 tgl@sss.pgh.pa.us 4213 :CBC 65 : AH->version = MAKE_ARCHIVE_VERSION(vmaj, vmin, vrev);
4214 : :
4215 [ + - - + ]: 65 : if (AH->version < K_VERS_1_0 || AH->version > K_VERS_MAX)
1569 tgl@sss.pgh.pa.us 4216 :UBC 0 : pg_fatal("unsupported version (%d.%d) in file header",
4217 : : vmaj, vmin);
4218 : :
1941 tgl@sss.pgh.pa.us 4219 :CBC 65 : AH->intSize = AH->ReadBytePtr(AH);
4220 [ - + ]: 65 : if (AH->intSize > 32)
228 peter@eisentraut.org 4221 :UBC 0 : pg_fatal("sanity check on integer size (%zu) failed", AH->intSize);
4222 : :
1941 tgl@sss.pgh.pa.us 4223 [ - + ]:CBC 65 : if (AH->intSize > sizeof(int))
1941 tgl@sss.pgh.pa.us 4224 :UBC 0 : pg_log_warning("archive was made on a machine with larger integers, some operations might fail");
4225 : :
1941 tgl@sss.pgh.pa.us 4226 [ + - ]:CBC 65 : if (AH->version >= K_VERS_1_7)
4227 : 65 : AH->offSize = AH->ReadBytePtr(AH);
4228 : : else
1941 tgl@sss.pgh.pa.us 4229 :UBC 0 : AH->offSize = AH->intSize;
4230 : :
1941 tgl@sss.pgh.pa.us 4231 :CBC 65 : fmt = AH->ReadBytePtr(AH);
4232 : :
4233 [ - + ]: 65 : if (AH->format != fmt)
1569 tgl@sss.pgh.pa.us 4234 :UBC 0 : pg_fatal("expected format (%d) differs from format found in file (%d)",
4235 : : AH->format, fmt);
4236 : :
1248 tomas.vondra@postgre 4237 [ + - ]:CBC 65 : if (AH->version >= K_VERS_1_15)
4238 : 65 : AH->compression_spec.algorithm = AH->ReadBytePtr(AH);
1248 tomas.vondra@postgre 4239 [ # # ]:UBC 0 : else if (AH->version >= K_VERS_1_2)
4240 : : {
4241 : : /* Guess the compression method based on the level */
9500 pjw@rhyme.com.au 4242 [ # # ]: 0 : if (AH->version < K_VERS_1_4)
1331 michael@paquier.xyz 4243 : 0 : AH->compression_spec.level = AH->ReadBytePtr(AH);
4244 : : else
4245 : 0 : AH->compression_spec.level = ReadInt(AH);
4246 : :
4247 [ # # ]: 0 : if (AH->compression_spec.level != 0)
4248 : 0 : AH->compression_spec.algorithm = PG_COMPRESSION_GZIP;
4249 : : }
4250 : : else
4251 : 0 : AH->compression_spec.algorithm = PG_COMPRESSION_GZIP;
4252 : :
1248 tomas.vondra@postgre 4253 :CBC 65 : errmsg = supports_compression(AH->compression_spec);
4254 [ - + ]: 65 : if (errmsg)
4255 : : {
1248 tomas.vondra@postgre 4256 :UBC 0 : pg_log_warning("archive is compressed, but this installation does not support compression (%s) -- no data will be available",
4257 : : errmsg);
4258 : 0 : pg_free(errmsg);
4259 : : }
4260 : :
9500 pjw@rhyme.com.au 4261 [ + - ]:CBC 65 : if (AH->version >= K_VERS_1_4)
4262 : : {
4263 : : struct tm crtm;
4264 : :
4265 : 65 : crtm.tm_sec = ReadInt(AH);
4266 : 65 : crtm.tm_min = ReadInt(AH);
4267 : 65 : crtm.tm_hour = ReadInt(AH);
4268 : 65 : crtm.tm_mday = ReadInt(AH);
4269 : 65 : crtm.tm_mon = ReadInt(AH);
4270 : 65 : crtm.tm_year = ReadInt(AH);
4271 : 65 : crtm.tm_isdst = ReadInt(AH);
4272 : :
4273 : : /*
4274 : : * Newer versions of glibc have mktime() report failure if tm_isdst is
4275 : : * inconsistent with the prevailing timezone, e.g. tm_isdst = 1 when
4276 : : * TZ=UTC. This is problematic when restoring an archive under a
4277 : : * different timezone setting. If we get a failure, try again with
4278 : : * tm_isdst set to -1 ("don't know").
4279 : : *
4280 : : * XXX with or without this hack, we reconstruct createDate
4281 : : * incorrectly when the prevailing timezone is different from
4282 : : * pg_dump's. Next time we bump the archive version, we should flush
4283 : : * this representation and store a plain seconds-since-the-Epoch
4284 : : * timestamp instead.
4285 : : */
4286 : 65 : AH->createDate = mktime(&crtm);
9256 bruce@momjian.us 4287 [ - + ]: 65 : if (AH->createDate == (time_t) -1)
4288 : : {
1868 tgl@sss.pgh.pa.us 4289 :UBC 0 : crtm.tm_isdst = -1;
4290 : 0 : AH->createDate = mktime(&crtm);
4291 [ # # ]: 0 : if (AH->createDate == (time_t) -1)
4292 : 0 : pg_log_warning("invalid creation date in header");
4293 : : }
4294 : : }
4295 : :
1868 tgl@sss.pgh.pa.us 4296 [ + - ]:CBC 65 : if (AH->version >= K_VERS_1_4)
4297 : : {
4298 : 65 : AH->archdbname = ReadStr(AH);
4299 : : }
4300 : :
7931 4301 [ + - ]: 65 : if (AH->version >= K_VERS_1_10)
4302 : : {
4303 : 65 : AH->archiveRemoteVersion = ReadStr(AH);
4304 : 65 : AH->archiveDumpVersion = ReadStr(AH);
4305 : : }
9517 bruce@momjian.us 4306 : 65 : }
4307 : :
4308 : :
4309 : : /*
4310 : : * checkSeek
4311 : : * check to see if ftell/fseek can be performed.
4312 : : */
4313 : : bool
8674 4314 : 108 : checkSeek(FILE *fp)
4315 : : {
4316 : : pgoff_t tpos;
4317 : :
4318 : : /* Check that ftello works on this file */
5871 tgl@sss.pgh.pa.us 4319 : 108 : tpos = ftello(fp);
4549 sfrost@snowman.net 4320 [ + + ]: 108 : if (tpos < 0)
5871 tgl@sss.pgh.pa.us 4321 : 1 : return false;
4322 : :
4323 : : /*
4324 : : * Check that fseeko(SEEK_SET) works, too. NB: we used to try to test
4325 : : * this with fseeko(fp, 0, SEEK_CUR). But some platforms treat that as a
4326 : : * successful no-op even on files that are otherwise unseekable.
4327 : : */
4328 [ - + ]: 107 : if (fseeko(fp, tpos, SEEK_SET) != 0)
5871 tgl@sss.pgh.pa.us 4329 :UBC 0 : return false;
4330 : :
5871 tgl@sss.pgh.pa.us 4331 :CBC 107 : return true;
4332 : : }
4333 : :
4334 : :
4335 : : /*
4336 : : * dumpTimestamp
4337 : : */
4338 : : static void
7771 4339 : 86 : dumpTimestamp(ArchiveHandle *AH, const char *msg, time_t tim)
4340 : : {
4341 : : char buf[64];
4342 : :
4290 4343 [ + - ]: 86 : if (strftime(buf, sizeof(buf), PGDUMP_STRFTIME_FMT, localtime(&tim)) != 0)
7771 4344 : 86 : ahprintf(AH, "-- %s %s\n\n", msg, buf);
4345 : 86 : }
4346 : :
4347 : : /*
4348 : : * Main engine for parallel restore.
4349 : : *
4350 : : * Parallel restore is done in three phases. In this first phase,
4351 : : * we'll process all SECTION_PRE_DATA TOC entries that are allowed to be
4352 : : * processed in the RESTORE_PASS_MAIN pass. (In practice, that's all
4353 : : * PRE_DATA items other than ACLs.) Entries we can't process now are
4354 : : * added to the pending_list for later phases to deal with.
4355 : : */
4356 : : static void
3278 4357 : 4 : restore_toc_entries_prefork(ArchiveHandle *AH, TocEntry *pending_list)
4358 : : {
4359 : : bool skipped_some;
4360 : : TocEntry *next_work_item;
4361 : :
2672 peter@eisentraut.org 4362 [ - + ]: 4 : pg_log_debug("entering restore_toc_entries_prefork");
4363 : :
4364 : : /* Adjust dependency information */
6382 andrew@dunslane.net 4365 : 4 : fix_dependencies(AH);
4366 : :
4367 : : /*
4368 : : * Do all the early stuff in a single connection in the parent. There's no
4369 : : * great point in running it in parallel, in fact it will actually run
4370 : : * faster in a single connection because we avoid all the connection and
4371 : : * setup overhead. Also, pre-9.2 pg_dump versions were not very good
4372 : : * about showing all the dependencies of SECTION_PRE_DATA items, so we do
4373 : : * not risk trying to process them out-of-order.
4374 : : *
4375 : : * Stuff that we can't do immediately gets added to the pending_list.
4376 : : * Note: we don't yet filter out entries that aren't going to be restored.
4377 : : * They might participate in dependency chains connecting entries that
4378 : : * should be restored, so we treat them as live until we actually process
4379 : : * them.
4380 : : *
4381 : : * Note: as of 9.2, it should be guaranteed that all PRE_DATA items appear
4382 : : * before DATA items, and all DATA items before POST_DATA items. That is
4383 : : * not certain to be true in older archives, though, and in any case use
4384 : : * of a list file would destroy that ordering (cf. SortTocFromFile). So
4385 : : * this loop cannot assume that it holds.
4386 : : */
3278 tgl@sss.pgh.pa.us 4387 : 4 : AH->restorePass = RESTORE_PASS_MAIN;
5636 4388 : 4 : skipped_some = false;
6196 4389 [ + + ]: 100 : for (next_work_item = AH->toc->next; next_work_item != AH->toc; next_work_item = next_work_item->next)
4390 : : {
3278 4391 : 96 : bool do_now = true;
4392 : :
5636 4393 [ + + ]: 96 : if (next_work_item->section != SECTION_PRE_DATA)
4394 : : {
4395 : : /* DATA and POST_DATA items are just ignored for now */
4396 [ + + ]: 46 : if (next_work_item->section == SECTION_DATA ||
4397 [ + - ]: 30 : next_work_item->section == SECTION_POST_DATA)
4398 : : {
3278 4399 : 46 : do_now = false;
5636 4400 : 46 : skipped_some = true;
4401 : : }
4402 : : else
4403 : : {
4404 : : /*
4405 : : * SECTION_NONE items, such as comments, can be processed now
4406 : : * if we are still in the PRE_DATA part of the archive. Once
4407 : : * we've skipped any items, we have to consider whether the
4408 : : * comment's dependencies are satisfied, so skip it for now.
4409 : : */
5636 tgl@sss.pgh.pa.us 4410 [ # # ]:UBC 0 : if (skipped_some)
3278 4411 : 0 : do_now = false;
4412 : : }
4413 : : }
4414 : :
4415 : : /*
4416 : : * Also skip items that need to be forced into later passes. We need
4417 : : * not set skipped_some in this case, since by assumption no main-pass
4418 : : * items could depend on these.
4419 : : */
477 nathan@postgresql.or 4420 [ - + ]:CBC 96 : if (_tocEntryRestorePass(next_work_item) != RESTORE_PASS_MAIN)
3278 tgl@sss.pgh.pa.us 4421 :UBC 0 : do_now = false;
4422 : :
3278 tgl@sss.pgh.pa.us 4423 [ + + ]:CBC 96 : if (do_now)
4424 : : {
4425 : : /* OK, restore the item and update its dependencies */
2672 peter@eisentraut.org 4426 : 50 : pg_log_info("processing item %d %s %s",
4427 : : next_work_item->dumpId,
4428 : : next_work_item->desc, next_work_item->tag);
4429 : :
3278 tgl@sss.pgh.pa.us 4430 : 50 : (void) restore_toc_entry(AH, next_work_item, false);
4431 : :
4432 : : /* Reduce dependencies, but don't move anything to ready_heap */
4433 : 50 : reduce_dependencies(AH, next_work_item, NULL);
4434 : : }
4435 : : else
4436 : : {
4437 : : /* Nope, so add it to pending_list */
2871 4438 : 46 : pending_list_append(pending_list, next_work_item);
4439 : : }
4440 : : }
4441 : :
4442 : : /*
4443 : : * In --transaction-size mode, we must commit the open transaction before
4444 : : * dropping the database connection. This also ensures that child workers
4445 : : * can see the objects we've created so far.
4446 : : */
845 4447 [ - + ]: 4 : if (AH->public.ropt->txn_size > 0)
845 tgl@sss.pgh.pa.us 4448 :UBC 0 : CommitTransaction(&AH->public);
4449 : :
4450 : : /*
4451 : : * Now close parent connection in prep for parallel steps. We do this
4452 : : * mainly to ensure that we don't exceed the specified number of parallel
4453 : : * connections.
4454 : : */
5273 rhaas@postgresql.org 4455 :CBC 4 : DisconnectDatabase(&AH->public);
4456 : :
4457 : : /* blow away any transient state from the old connection */
1500 peter@eisentraut.org 4458 : 4 : free(AH->currUser);
6382 andrew@dunslane.net 4459 : 4 : AH->currUser = NULL;
1500 peter@eisentraut.org 4460 : 4 : free(AH->currSchema);
6382 andrew@dunslane.net 4461 : 4 : AH->currSchema = NULL;
1500 peter@eisentraut.org 4462 : 4 : free(AH->currTablespace);
6382 andrew@dunslane.net 4463 : 4 : AH->currTablespace = NULL;
1500 peter@eisentraut.org 4464 : 4 : free(AH->currTableAm);
2698 andres@anarazel.de 4465 : 4 : AH->currTableAm = NULL;
4871 andrew@dunslane.net 4466 : 4 : }
4467 : :
4468 : : /*
4469 : : * Main engine for parallel restore.
4470 : : *
4471 : : * Parallel restore is done in three phases. In this second phase,
4472 : : * we process entries by dispatching them to parallel worker children
4473 : : * (processes on Unix, threads on Windows), each of which connects
4474 : : * separately to the database. Inter-entry dependencies are respected,
4475 : : * and so is the RestorePass multi-pass structure. When we can no longer
4476 : : * make any entries ready to process, we exit. Normally, there will be
4477 : : * nothing left to do; but if there is, the third phase will mop up.
4478 : : */
4479 : : static void
4480 : 4 : restore_toc_entries_parallel(ArchiveHandle *AH, ParallelState *pstate,
4481 : : TocEntry *pending_list)
4482 : : {
4483 : : binaryheap *ready_heap;
4484 : : TocEntry *next_work_item;
4485 : :
2672 peter@eisentraut.org 4486 [ - + ]: 4 : pg_log_debug("entering restore_toc_entries_parallel");
4487 : :
4488 : : /* Set up ready_heap with enough room for all known TocEntrys */
1040 nathan@postgresql.or 4489 : 4 : ready_heap = binaryheap_allocate(AH->tocCount,
4490 : : TocEntrySizeCompareBinaryheap,
4491 : : NULL);
4492 : :
4493 : : /*
4494 : : * The pending_list contains all items that we need to restore. Move all
4495 : : * items that are available to process immediately into the ready_heap.
4496 : : * After this setup, the pending list is everything that needs to be done
4497 : : * but is blocked by one or more dependencies, while the ready heap
4498 : : * contains items that have no remaining dependencies and are OK to
4499 : : * process in the current restore pass.
4500 : : */
3278 tgl@sss.pgh.pa.us 4501 : 4 : AH->restorePass = RESTORE_PASS_MAIN;
477 nathan@postgresql.or 4502 : 4 : move_to_ready_heap(pending_list, ready_heap, AH->restorePass);
4503 : :
4504 : : /*
4505 : : * main parent loop
4506 : : *
4507 : : * Keep going until there is no worker still running AND there is no work
4508 : : * left to be done. Note invariant: at top of loop, there should always
4509 : : * be at least one worker available to dispatch a job to.
4510 : : */
2672 peter@eisentraut.org 4511 : 4 : pg_log_info("entering main parallel loop");
4512 : :
4513 : : for (;;)
4514 : : {
4515 : : /* Look for an item ready to be dispatched to a worker */
1040 nathan@postgresql.or 4516 : 69 : next_work_item = pop_next_work_item(ready_heap, pstate);
6382 andrew@dunslane.net 4517 [ + + ]: 69 : if (next_work_item != NULL)
4518 : : {
4519 : : /* If not to be restored, don't waste time launching a worker */
520 jdavis@postgresql.or 4520 [ - + ]: 46 : if ((next_work_item->reqs & (REQ_SCHEMA | REQ_DATA | REQ_STATS)) == 0)
4521 : : {
2672 peter@eisentraut.org 4522 :UBC 0 : pg_log_info("skipping item %d %s %s",
4523 : : next_work_item->dumpId,
4524 : : next_work_item->desc, next_work_item->tag);
4525 : : /* Update its dependencies as though we'd completed it */
1040 nathan@postgresql.or 4526 : 0 : reduce_dependencies(AH, next_work_item, ready_heap);
4527 : : /* Loop around to see if anything else can be dispatched */
6382 andrew@dunslane.net 4528 : 0 : continue;
4529 : : }
4530 : :
2672 peter@eisentraut.org 4531 :CBC 46 : pg_log_info("launching item %d %s %s",
4532 : : next_work_item->dumpId,
4533 : : next_work_item->desc, next_work_item->tag);
4534 : :
4535 : : /* Dispatch to some worker */
3588 tgl@sss.pgh.pa.us 4536 : 46 : DispatchJobForTocEntry(AH, pstate, next_work_item, ACT_RESTORE,
4537 : : mark_restore_job_done, ready_heap);
4538 : : }
3278 4539 [ + + ]: 23 : else if (IsEveryWorkerIdle(pstate))
4540 : : {
4541 : : /*
4542 : : * Nothing is ready and no worker is running, so we're done with
4543 : : * the current pass or maybe with the whole process.
4544 : : */
4545 [ + + ]: 12 : if (AH->restorePass == RESTORE_PASS_LAST)
4546 : 4 : break; /* No more parallel processing is possible */
4547 : :
4548 : : /* Advance to next restore pass */
4549 : 8 : AH->restorePass++;
4550 : : /* That probably allows some stuff to be made ready */
477 nathan@postgresql.or 4551 : 8 : move_to_ready_heap(pending_list, ready_heap, AH->restorePass);
4552 : : /* Loop around to see if anything's now ready */
3278 tgl@sss.pgh.pa.us 4553 : 8 : continue;
4554 : : }
4555 : : else
4556 : : {
4557 : : /*
4558 : : * We have nothing ready, but at least one child is working, so
4559 : : * wait for some subjob to finish.
4560 : : */
4561 : : }
4562 : :
4563 : : /*
4564 : : * Before dispatching another job, check to see if anything has
4565 : : * finished. We should check every time through the loop so as to
4566 : : * reduce dependencies as soon as possible. If we were unable to
4567 : : * dispatch any job this time through, wait until some worker finishes
4568 : : * (and, hopefully, unblocks some pending item). If we did dispatch
4569 : : * something, continue as soon as there's at least one idle worker.
4570 : : * Note that in either case, there's guaranteed to be at least one
4571 : : * idle worker when we return to the top of the loop. This ensures we
4572 : : * won't block inside DispatchJobForTocEntry, which would be
4573 : : * undesirable: we'd rather postpone dispatching until we see what's
4574 : : * been unblocked by finished jobs.
4575 : : */
3588 4576 [ + + ]: 57 : WaitForWorkers(AH, pstate,
4577 : : next_work_item ? WFW_ONE_IDLE : WFW_GOT_STATUS);
4578 : : }
4579 : :
4580 : : /* There should now be nothing in ready_heap. */
1040 nathan@postgresql.or 4581 [ - + ]: 4 : Assert(binaryheap_empty(ready_heap));
4582 : :
4583 : 4 : binaryheap_free(ready_heap);
4584 : :
2672 peter@eisentraut.org 4585 : 4 : pg_log_info("finished main parallel loop");
4871 andrew@dunslane.net 4586 : 4 : }
4587 : :
4588 : : /*
4589 : : * Main engine for parallel restore.
4590 : : *
4591 : : * Parallel restore is done in three phases. In this third phase,
4592 : : * we mop up any remaining TOC entries by processing them serially.
4593 : : * This phase normally should have nothing to do, but if we've somehow
4594 : : * gotten stuck due to circular dependencies or some such, this provides
4595 : : * at least some chance of completing the restore successfully.
4596 : : */
4597 : : static void
4598 : 4 : restore_toc_entries_postfork(ArchiveHandle *AH, TocEntry *pending_list)
4599 : : {
3846 tgl@sss.pgh.pa.us 4600 : 4 : RestoreOptions *ropt = AH->public.ropt;
4601 : : TocEntry *te;
4602 : :
2672 peter@eisentraut.org 4603 [ - + ]: 4 : pg_log_debug("entering restore_toc_entries_postfork");
4604 : :
4605 : : /*
4606 : : * Now reconnect the single parent connection.
4607 : : */
477 andrew@dunslane.net 4608 : 4 : ConnectDatabaseAhx((Archive *) AH, &ropt->cparams, true);
4609 : :
4610 : : /* re-establish fixed state */
6382 4611 : 4 : _doSetFixedOutputState(AH);
4612 : :
4613 : : /*
4614 : : * Make sure there is no work left due to, say, circular dependencies, or
4615 : : * some other pathological condition. If so, do it in the single parent
4616 : : * connection. We don't sweat about RestorePass ordering; it's likely we
4617 : : * already violated that.
4618 : : */
2871 tgl@sss.pgh.pa.us 4619 [ - + ]: 4 : for (te = pending_list->pending_next; te != pending_list; te = te->pending_next)
4620 : : {
2672 peter@eisentraut.org 4621 :UBC 0 : pg_log_info("processing missed item %d %s %s",
4622 : : te->dumpId, te->desc, te->tag);
3846 tgl@sss.pgh.pa.us 4623 : 0 : (void) restore_toc_entry(AH, te, false);
4624 : : }
6382 andrew@dunslane.net 4625 :CBC 4 : }
4626 : :
4627 : : /*
4628 : : * Check if te1 has an exclusive lock requirement for an item that te2 also
4629 : : * requires, whether or not te2's requirement is for an exclusive lock.
4630 : : */
4631 : : static bool
6313 4632 : 148 : has_lock_conflicts(TocEntry *te1, TocEntry *te2)
4633 : : {
4634 : : int j,
4635 : : k;
4636 : :
4637 [ + + ]: 361 : for (j = 0; j < te1->nLockDeps; j++)
4638 : : {
4639 [ + + ]: 894 : for (k = 0; k < te2->nDeps; k++)
4640 : : {
4641 [ - + ]: 681 : if (te1->lockDeps[j] == te2->dependencies[k])
6313 andrew@dunslane.net 4642 :LBC (1) : return true;
4643 : : }
4644 : : }
6313 andrew@dunslane.net 4645 :CBC 148 : return false;
4646 : : }
4647 : :
4648 : :
4649 : : /*
4650 : : * Initialize the header of the pending-items list.
4651 : : *
4652 : : * This is a circular list with a dummy TocEntry as header, just like the
4653 : : * main TOC list; but we use separate list links so that an entry can be in
4654 : : * the main TOC list as well as in the pending list.
4655 : : */
4656 : : static void
2871 tgl@sss.pgh.pa.us 4657 : 4 : pending_list_header_init(TocEntry *l)
4658 : : {
4659 : 4 : l->pending_prev = l->pending_next = l;
4660 : 4 : }
4661 : :
4662 : : /* Append te to the end of the pending-list headed by l */
4663 : : static void
4664 : 46 : pending_list_append(TocEntry *l, TocEntry *te)
4665 : : {
4666 : 46 : te->pending_prev = l->pending_prev;
4667 : 46 : l->pending_prev->pending_next = te;
4668 : 46 : l->pending_prev = te;
4669 : 46 : te->pending_next = l;
4670 : 46 : }
4671 : :
4672 : : /* Remove te from the pending-list */
4673 : : static void
4674 : 46 : pending_list_remove(TocEntry *te)
4675 : : {
4676 : 46 : te->pending_prev->pending_next = te->pending_next;
4677 : 46 : te->pending_next->pending_prev = te->pending_prev;
4678 : 46 : te->pending_prev = NULL;
4679 : 46 : te->pending_next = NULL;
4680 : 46 : }
4681 : :
4682 : :
4683 : : /* qsort comparator for sorting TocEntries by dataLength */
4684 : : static int
1040 nathan@postgresql.or 4685 : 419 : TocEntrySizeCompareQsort(const void *p1, const void *p2)
4686 : : {
2871 tgl@sss.pgh.pa.us 4687 : 419 : const TocEntry *te1 = *(const TocEntry *const *) p1;
4688 : 419 : const TocEntry *te2 = *(const TocEntry *const *) p2;
4689 : :
4690 : : /* Sort by decreasing dataLength */
4691 [ + + ]: 419 : if (te1->dataLength > te2->dataLength)
4692 : 60 : return -1;
4693 [ + + ]: 359 : if (te1->dataLength < te2->dataLength)
4694 : 98 : return 1;
4695 : :
4696 : : /* For equal dataLengths, sort by dumpId, just to be stable */
4697 [ + + ]: 261 : if (te1->dumpId < te2->dumpId)
4698 : 105 : return -1;
4699 [ + + ]: 156 : if (te1->dumpId > te2->dumpId)
4700 : 145 : return 1;
4701 : :
4702 : 11 : return 0;
4703 : : }
4704 : :
4705 : : /* binaryheap comparator for sorting TocEntries by dataLength */
4706 : : static int
1040 nathan@postgresql.or 4707 : 147 : TocEntrySizeCompareBinaryheap(void *p1, void *p2, void *arg)
4708 : : {
4709 : : /* return opposite of qsort comparator for max-heap */
4710 : 147 : return -TocEntrySizeCompareQsort(&p1, &p2);
4711 : : }
4712 : :
4713 : :
4714 : : /*
4715 : : * Move all immediately-ready items from pending_list to ready_heap.
4716 : : *
4717 : : * Items are considered ready if they have no remaining dependencies and
4718 : : * they belong in the current restore pass. (See also reduce_dependencies,
4719 : : * which applies the same logic one-at-a-time.)
4720 : : */
4721 : : static void
477 4722 : 12 : move_to_ready_heap(TocEntry *pending_list,
4723 : : binaryheap *ready_heap,
4724 : : RestorePass pass)
4725 : : {
4726 : : TocEntry *te;
4727 : : TocEntry *next_te;
4728 : :
2871 tgl@sss.pgh.pa.us 4729 [ + + ]: 58 : for (te = pending_list->pending_next; te != pending_list; te = next_te)
4730 : : {
4731 : : /* must save list link before possibly removing te from list */
4732 : 46 : next_te = te->pending_next;
4733 : :
3278 4734 [ + + + - ]: 66 : if (te->depCount == 0 &&
477 nathan@postgresql.or 4735 : 20 : _tocEntryRestorePass(te) == pass)
4736 : : {
4737 : : /* Remove it from pending_list ... */
2871 tgl@sss.pgh.pa.us 4738 : 20 : pending_list_remove(te);
4739 : : /* ... and add to ready_heap */
1040 nathan@postgresql.or 4740 : 20 : binaryheap_add(ready_heap, te);
4741 : : }
4742 : : }
3278 tgl@sss.pgh.pa.us 4743 : 12 : }
4744 : :
4745 : : /*
4746 : : * Find the next work item (if any) that is capable of being run now,
4747 : : * and remove it from the ready_heap.
4748 : : *
4749 : : * Returns the item, or NULL if nothing is runnable.
4750 : : *
4751 : : * To qualify, the item must have no remaining dependencies
4752 : : * and no requirements for locks that are incompatible with
4753 : : * items currently running. Items in the ready_heap are known to have
4754 : : * no remaining dependencies, but we have to check for lock conflicts.
4755 : : */
4756 : : static TocEntry *
1040 nathan@postgresql.or 4757 : 69 : pop_next_work_item(binaryheap *ready_heap,
4758 : : ParallelState *pstate)
4759 : : {
4760 : : /*
4761 : : * Search the ready_heap until we find a suitable item. Note that we do a
4762 : : * sequential scan through the heap nodes, so even though we will first
4763 : : * try to choose the highest-priority item, we might end up picking
4764 : : * something with a much lower priority. However, we expect that we will
4765 : : * typically be able to pick one of the first few items, which should
4766 : : * usually have a relatively high priority.
4767 : : */
4768 [ + + ]: 69 : for (int i = 0; i < binaryheap_size(ready_heap); i++)
4769 : : {
4770 : 46 : TocEntry *te = (TocEntry *) binaryheap_get_node(ready_heap, i);
6253 bruce@momjian.us 4771 : 46 : bool conflicts = false;
4772 : :
4773 : : /*
4774 : : * Check to see if the item would need exclusive lock on something
4775 : : * that a currently running item also needs lock on, or vice versa. If
4776 : : * so, we don't want to schedule them together.
4777 : : */
2871 tgl@sss.pgh.pa.us 4778 [ + + ]: 182 : for (int k = 0; k < pstate->numWorkers; k++)
4779 : : {
4780 : 136 : TocEntry *running_te = pstate->te[k];
4781 : :
3588 4782 [ + + ]: 136 : if (running_te == NULL)
6382 andrew@dunslane.net 4783 : 62 : continue;
6313 4784 [ + - - + ]: 148 : if (has_lock_conflicts(te, running_te) ||
4785 : 74 : has_lock_conflicts(running_te, te))
4786 : : {
6313 andrew@dunslane.net 4787 :LBC (1) : conflicts = true;
4788 : (1) : break;
4789 : : }
4790 : : }
4791 : :
6382 andrew@dunslane.net 4792 [ - + ]:CBC 46 : if (conflicts)
6382 andrew@dunslane.net 4793 :LBC (1) : continue;
4794 : :
4795 : : /* passed all tests, so this item can run */
1040 nathan@postgresql.or 4796 :CBC 46 : binaryheap_remove_node(ready_heap, i);
6382 andrew@dunslane.net 4797 : 46 : return te;
4798 : : }
4799 : :
2672 peter@eisentraut.org 4800 [ - + ]: 23 : pg_log_debug("no item ready");
6382 andrew@dunslane.net 4801 : 23 : return NULL;
4802 : : }
4803 : :
4804 : :
4805 : : /*
4806 : : * Restore a single TOC item in parallel with others
4807 : : *
4808 : : * this is run in the worker, i.e. in a thread (Windows) or a separate process
4809 : : * (everything else). A worker process executes several such work items during
4810 : : * a parallel backup or restore. Once we terminate here and report back that
4811 : : * our work is finished, the leader process will assign us a new work item.
4812 : : */
4813 : : int
3588 tgl@sss.pgh.pa.us 4814 : 46 : parallel_restore(ArchiveHandle *AH, TocEntry *te)
4815 : : {
4816 : : int status;
4817 : :
4871 andrew@dunslane.net 4818 [ - + ]: 46 : Assert(AH->connection != NULL);
4819 : :
4820 : : /* Count only errors associated with this TOC entry */
4821 : 46 : AH->public.n_errors = 0;
4822 : :
4823 : : /* Restore the TOC item */
3846 tgl@sss.pgh.pa.us 4824 : 46 : status = restore_toc_entry(AH, te, true);
4825 : :
4871 andrew@dunslane.net 4826 : 46 : return status;
4827 : : }
4828 : :
4829 : :
4830 : : /*
4831 : : * Callback function that's invoked in the leader process after a step has
4832 : : * been parallel restored.
4833 : : *
4834 : : * Update status and reduce the dependency count of any dependent items.
4835 : : */
4836 : : static void
3588 tgl@sss.pgh.pa.us 4837 : 46 : mark_restore_job_done(ArchiveHandle *AH,
4838 : : TocEntry *te,
4839 : : int status,
4840 : : void *callback_data)
4841 : : {
1040 nathan@postgresql.or 4842 : 46 : binaryheap *ready_heap = (binaryheap *) callback_data;
4843 : :
2672 peter@eisentraut.org 4844 : 46 : pg_log_info("finished item %d %s %s",
4845 : : te->dumpId, te->desc, te->tag);
4846 : :
6382 andrew@dunslane.net 4847 [ - + ]: 46 : if (status == WORKER_CREATE_DONE)
6382 andrew@dunslane.net 4848 :UBC 0 : mark_create_done(AH, te);
6382 andrew@dunslane.net 4849 [ - + ]:CBC 46 : else if (status == WORKER_INHIBIT_DATA)
4850 : : {
6382 andrew@dunslane.net 4851 :UBC 0 : inhibit_data_for_failed_table(AH, te);
4852 : 0 : AH->public.n_errors++;
4853 : : }
6382 andrew@dunslane.net 4854 [ - + ]:CBC 46 : else if (status == WORKER_IGNORED_ERRORS)
6382 andrew@dunslane.net 4855 :UBC 0 : AH->public.n_errors++;
6382 andrew@dunslane.net 4856 [ - + ]:CBC 46 : else if (status != 0)
1569 tgl@sss.pgh.pa.us 4857 :UBC 0 : pg_fatal("worker process failed: exit code %d",
4858 : : status);
4859 : :
1040 nathan@postgresql.or 4860 :CBC 46 : reduce_dependencies(AH, te, ready_heap);
6382 andrew@dunslane.net 4861 : 46 : }
4862 : :
4863 : :
4864 : : /*
4865 : : * Process the dependency information into a form useful for parallel restore.
4866 : : *
4867 : : * This function takes care of fixing up some missing or badly designed
4868 : : * dependencies, and then prepares subsidiary data structures that will be
4869 : : * used in the main parallel-restore logic, including:
4870 : : * 1. We build the revDeps[] arrays of incoming dependency dumpIds.
4871 : : * 2. We set up depCount fields that are the number of as-yet-unprocessed
4872 : : * dependencies for each TOC entry.
4873 : : *
4874 : : * We also identify locking dependencies so that we can avoid trying to
4875 : : * schedule conflicting items at the same time.
4876 : : */
4877 : : static void
4878 : 4 : fix_dependencies(ArchiveHandle *AH)
4879 : : {
4880 : : TocEntry *te;
4881 : : int i;
4882 : :
4883 : : /*
4884 : : * Initialize the depCount/revDeps/nRevDeps fields, and make sure the TOC
4885 : : * items are marked as not being in any parallel-processing list.
4886 : : */
4887 [ + + ]: 100 : for (te = AH->toc->next; te != AH->toc; te = te->next)
4888 : : {
4889 : 96 : te->depCount = te->nDeps;
5707 tgl@sss.pgh.pa.us 4890 : 96 : te->revDeps = NULL;
4891 : 96 : te->nRevDeps = 0;
2871 4892 : 96 : te->pending_prev = NULL;
4893 : 96 : te->pending_next = NULL;
4894 : : }
4895 : :
4896 : : /*
4897 : : * POST_DATA items that are shown as depending on a table need to be
4898 : : * re-pointed to depend on that table's data, instead. This ensures they
4899 : : * won't get scheduled until the data has been loaded.
4900 : : */
5171 4901 : 4 : repoint_table_dependencies(AH);
4902 : :
4903 : : /*
4904 : : * Pre-8.4 versions of pg_dump neglected to set up a dependency from BLOB
4905 : : * COMMENTS to BLOBS. Cope. (We assume there's only one BLOBS and only
4906 : : * one BLOB COMMENTS in such files.)
4907 : : */
6382 andrew@dunslane.net 4908 [ - + ]: 4 : if (AH->version < K_VERS_1_11)
4909 : : {
6382 andrew@dunslane.net 4910 [ # # ]:UBC 0 : for (te = AH->toc->next; te != AH->toc; te = te->next)
4911 : : {
4912 [ # # # # ]: 0 : if (strcmp(te->desc, "BLOB COMMENTS") == 0 && te->nDeps == 0)
4913 : : {
4914 : : TocEntry *te2;
4915 : :
4916 [ # # ]: 0 : for (te2 = AH->toc->next; te2 != AH->toc; te2 = te2->next)
4917 : : {
4918 [ # # ]: 0 : if (strcmp(te2->desc, "BLOBS") == 0)
4919 : : {
162 michael@paquier.xyz 4920 : 0 : te->dependencies = pg_malloc_object(DumpId);
6382 andrew@dunslane.net 4921 : 0 : te->dependencies[0] = te2->dumpId;
4922 : 0 : te->nDeps++;
4923 : 0 : te->depCount++;
4924 : 0 : break;
4925 : : }
4926 : : }
4927 : 0 : break;
4928 : : }
4929 : : }
4930 : : }
4931 : :
4932 : : /*
4933 : : * At this point we start to build the revDeps reverse-dependency arrays,
4934 : : * so all changes of dependencies must be complete.
4935 : : */
4936 : :
4937 : : /*
4938 : : * Count the incoming dependencies for each item. Also, it is possible
4939 : : * that the dependencies list items that are not in the archive at all
4940 : : * (that should not happen in 9.2 and later, but is highly likely in older
4941 : : * archives). Subtract such items from the depCounts.
4942 : : */
6382 andrew@dunslane.net 4943 [ + + ]:CBC 100 : for (te = AH->toc->next; te != AH->toc; te = te->next)
4944 : : {
4945 [ + + ]: 288 : for (i = 0; i < te->nDeps; i++)
4946 : : {
6031 tgl@sss.pgh.pa.us 4947 : 192 : DumpId depid = te->dependencies[i];
4948 : :
5171 4949 [ + - + - ]: 192 : if (depid <= AH->maxDumpId && AH->tocsByDumpId[depid] != NULL)
4950 : 192 : AH->tocsByDumpId[depid]->nRevDeps++;
4951 : : else
6382 andrew@dunslane.net 4952 :UBC 0 : te->depCount--;
4953 : : }
4954 : : }
4955 : :
4956 : : /*
4957 : : * Allocate space for revDeps[] arrays, and reset nRevDeps so we can use
4958 : : * it as a counter below.
4959 : : */
5707 tgl@sss.pgh.pa.us 4960 [ + + ]:CBC 100 : for (te = AH->toc->next; te != AH->toc; te = te->next)
4961 : : {
4962 [ + + ]: 96 : if (te->nRevDeps > 0)
162 michael@paquier.xyz 4963 : 52 : te->revDeps = pg_malloc_array(DumpId, te->nRevDeps);
5707 tgl@sss.pgh.pa.us 4964 : 96 : te->nRevDeps = 0;
4965 : : }
4966 : :
4967 : : /*
4968 : : * Build the revDeps[] arrays of incoming-dependency dumpIds. This had
4969 : : * better agree with the loops above.
4970 : : */
4971 [ + + ]: 100 : for (te = AH->toc->next; te != AH->toc; te = te->next)
4972 : : {
4973 [ + + ]: 288 : for (i = 0; i < te->nDeps; i++)
4974 : : {
4975 : 192 : DumpId depid = te->dependencies[i];
4976 : :
5171 4977 [ + - + - ]: 192 : if (depid <= AH->maxDumpId && AH->tocsByDumpId[depid] != NULL)
4978 : : {
4979 : 192 : TocEntry *otherte = AH->tocsByDumpId[depid];
4980 : :
5707 4981 : 192 : otherte->revDeps[otherte->nRevDeps++] = te->dumpId;
4982 : : }
4983 : : }
4984 : : }
4985 : :
4986 : : /*
4987 : : * Lastly, work out the locking dependencies.
4988 : : */
6382 andrew@dunslane.net 4989 [ + + ]: 100 : for (te = AH->toc->next; te != AH->toc; te = te->next)
4990 : : {
4991 : 96 : te->lockDeps = NULL;
4992 : 96 : te->nLockDeps = 0;
5171 tgl@sss.pgh.pa.us 4993 : 96 : identify_locking_dependencies(AH, te);
4994 : : }
6382 andrew@dunslane.net 4995 : 4 : }
4996 : :
4997 : : /*
4998 : : * Change dependencies on table items to depend on table data items instead,
4999 : : * but only in POST_DATA items.
5000 : : *
5001 : : * Also, for any item having such dependency(s), set its dataLength to the
5002 : : * largest dataLength of the table data items it depends on. This ensures
5003 : : * that parallel restore will prioritize larger jobs (index builds, FK
5004 : : * constraint checks, etc) over smaller ones, avoiding situations where we
5005 : : * end a restore with only one active job working on a large table.
5006 : : */
5007 : : static void
5171 tgl@sss.pgh.pa.us 5008 : 4 : repoint_table_dependencies(ArchiveHandle *AH)
5009 : : {
5010 : : TocEntry *te;
5011 : : int i;
5012 : : DumpId olddep;
5013 : :
6382 andrew@dunslane.net 5014 [ + + ]: 100 : for (te = AH->toc->next; te != AH->toc; te = te->next)
5015 : : {
5016 [ + + ]: 96 : if (te->section != SECTION_POST_DATA)
5017 : 66 : continue;
5018 [ + + ]: 160 : for (i = 0; i < te->nDeps; i++)
5019 : : {
5171 tgl@sss.pgh.pa.us 5020 : 130 : olddep = te->dependencies[i];
5021 [ + - ]: 130 : if (olddep <= AH->maxDumpId &&
5022 [ + + ]: 130 : AH->tableDataId[olddep] != 0)
5023 : : {
2871 5024 : 62 : DumpId tabledataid = AH->tableDataId[olddep];
5025 : 62 : TocEntry *tabledatate = AH->tocsByDumpId[tabledataid];
5026 : :
5027 : 62 : te->dependencies[i] = tabledataid;
5028 : 62 : te->dataLength = Max(te->dataLength, tabledatate->dataLength);
2672 peter@eisentraut.org 5029 [ - + ]: 62 : pg_log_debug("transferring dependency %d -> %d to %d",
5030 : : te->dumpId, olddep, tabledataid);
5031 : : }
5032 : : }
5033 : : }
6382 andrew@dunslane.net 5034 : 4 : }
5035 : :
5036 : : /*
5037 : : * Identify which objects we'll need exclusive lock on in order to restore
5038 : : * the given TOC entry (*other* than the one identified by the TOC entry
5039 : : * itself). Record their dump IDs in the entry's lockDeps[] array.
5040 : : */
5041 : : static void
5171 tgl@sss.pgh.pa.us 5042 : 96 : identify_locking_dependencies(ArchiveHandle *AH, TocEntry *te)
5043 : : {
5044 : : DumpId *lockids;
5045 : : int nlockids;
5046 : : int i;
5047 : :
5048 : : /*
5049 : : * We only care about this for POST_DATA items. PRE_DATA items are not
5050 : : * run in parallel, and DATA items are all independent by assumption.
5051 : : */
2888 5052 [ + + ]: 96 : if (te->section != SECTION_POST_DATA)
5053 : 66 : return;
5054 : :
5055 : : /* Quick exit if no dependencies at all */
6382 andrew@dunslane.net 5056 [ - + ]: 30 : if (te->nDeps == 0)
6382 andrew@dunslane.net 5057 :UBC 0 : return;
5058 : :
5059 : : /*
5060 : : * Most POST_DATA items are ALTER TABLEs or some moral equivalent of that,
5061 : : * and hence require exclusive lock. However, we know that CREATE INDEX
5062 : : * does not. (Maybe someday index-creating CONSTRAINTs will fall in that
5063 : : * category too ... but today is not that day.)
5064 : : */
2888 tgl@sss.pgh.pa.us 5065 [ - + ]:CBC 30 : if (strcmp(te->desc, "INDEX") == 0)
6382 andrew@dunslane.net 5066 :UBC 0 : return;
5067 : :
5068 : : /*
5069 : : * We assume the entry requires exclusive lock on each TABLE or TABLE DATA
5070 : : * item listed among its dependencies. Originally all of these would have
5071 : : * been TABLE items, but repoint_table_dependencies would have repointed
5072 : : * them to the TABLE DATA items if those are present (which they might not
5073 : : * be, eg in a schema-only dump). Note that all of the entries we are
5074 : : * processing here are POST_DATA; otherwise there might be a significant
5075 : : * difference between a dependency on a table and a dependency on its
5076 : : * data, so that closer analysis would be needed here.
5077 : : */
162 michael@paquier.xyz 5078 :CBC 30 : lockids = pg_malloc_array(DumpId, te->nDeps);
6382 andrew@dunslane.net 5079 : 30 : nlockids = 0;
5080 [ + + ]: 160 : for (i = 0; i < te->nDeps; i++)
5081 : : {
6253 bruce@momjian.us 5082 : 130 : DumpId depid = te->dependencies[i];
5083 : :
5171 tgl@sss.pgh.pa.us 5084 [ + - + - ]: 130 : if (depid <= AH->maxDumpId && AH->tocsByDumpId[depid] != NULL &&
4320 rhaas@postgresql.org 5085 [ + + ]: 130 : ((strcmp(AH->tocsByDumpId[depid]->desc, "TABLE DATA") == 0) ||
4299 tgl@sss.pgh.pa.us 5086 [ + + ]: 68 : strcmp(AH->tocsByDumpId[depid]->desc, "TABLE") == 0))
6382 andrew@dunslane.net 5087 : 82 : lockids[nlockids++] = depid;
5088 : : }
5089 : :
5090 [ - + ]: 30 : if (nlockids == 0)
5091 : : {
24 peter@eisentraut.org 5092 :UNC 0 : pg_free(lockids);
6382 andrew@dunslane.net 5093 :UBC 0 : return;
5094 : : }
5095 : :
162 michael@paquier.xyz 5096 :CBC 30 : te->lockDeps = pg_realloc_array(lockids, DumpId, nlockids);
6382 andrew@dunslane.net 5097 : 30 : te->nLockDeps = nlockids;
5098 : : }
5099 : :
5100 : : /*
5101 : : * Remove the specified TOC entry from the depCounts of items that depend on
5102 : : * it, thereby possibly making them ready-to-run. Any pending item that
5103 : : * becomes ready should be moved to the ready_heap, if that's provided.
5104 : : */
5105 : : static void
2871 tgl@sss.pgh.pa.us 5106 : 96 : reduce_dependencies(ArchiveHandle *AH, TocEntry *te,
5107 : : binaryheap *ready_heap)
5108 : : {
5109 : : int i;
5110 : :
2672 peter@eisentraut.org 5111 [ - + ]: 96 : pg_log_debug("reducing dependencies for %d", te->dumpId);
5112 : :
5707 tgl@sss.pgh.pa.us 5113 [ + + ]: 288 : for (i = 0; i < te->nRevDeps; i++)
5114 : : {
5171 5115 : 192 : TocEntry *otherte = AH->tocsByDumpId[te->revDeps[i]];
5116 : :
3278 5117 [ - + ]: 192 : Assert(otherte->depCount > 0);
5707 5118 : 192 : otherte->depCount--;
5119 : :
5120 : : /*
5121 : : * It's ready if it has no remaining dependencies, and it belongs in
5122 : : * the current restore pass, and it is currently a member of the
5123 : : * pending list (that check is needed to prevent double restore in
5124 : : * some cases where a list-file forces out-of-order restoring).
5125 : : * However, if ready_heap == NULL then caller doesn't want any list
5126 : : * memberships changed.
5127 : : */
3278 5128 [ + + ]: 192 : if (otherte->depCount == 0 &&
477 nathan@postgresql.or 5129 [ + - ]: 74 : _tocEntryRestorePass(otherte) == AH->restorePass &&
2871 tgl@sss.pgh.pa.us 5130 [ + + + - ]: 74 : otherte->pending_prev != NULL &&
5131 : : ready_heap != NULL)
5132 : : {
5133 : : /* Remove it from pending list ... */
5134 : 26 : pending_list_remove(otherte);
5135 : : /* ... and add to ready_heap */
1040 nathan@postgresql.or 5136 : 26 : binaryheap_add(ready_heap, otherte);
5137 : : }
5138 : : }
6382 andrew@dunslane.net 5139 : 96 : }
5140 : :
5141 : : /*
5142 : : * Set the created flag on the DATA member corresponding to the given
5143 : : * TABLE member
5144 : : */
5145 : : static void
5146 : 5679 : mark_create_done(ArchiveHandle *AH, TocEntry *te)
5147 : : {
5171 tgl@sss.pgh.pa.us 5148 [ + + ]: 5679 : if (AH->tableDataId[te->dumpId] != 0)
5149 : : {
5150 : 4263 : TocEntry *ted = AH->tocsByDumpId[AH->tableDataId[te->dumpId]];
5151 : :
5152 : 4263 : ted->created = true;
5153 : : }
6382 andrew@dunslane.net 5154 : 5679 : }
5155 : :
5156 : : /*
5157 : : * Mark the DATA member corresponding to the given TABLE member
5158 : : * as not wanted
5159 : : */
5160 : : static void
6382 andrew@dunslane.net 5161 :UBC 0 : inhibit_data_for_failed_table(ArchiveHandle *AH, TocEntry *te)
5162 : : {
2672 peter@eisentraut.org 5163 : 0 : pg_log_info("table \"%s\" could not be created, will not restore its data",
5164 : : te->tag);
5165 : :
5171 tgl@sss.pgh.pa.us 5166 [ # # ]: 0 : if (AH->tableDataId[te->dumpId] != 0)
5167 : : {
5170 5168 : 0 : TocEntry *ted = AH->tocsByDumpId[AH->tableDataId[te->dumpId]];
5169 : :
5170 : 0 : ted->reqs = 0;
5171 : : }
6382 andrew@dunslane.net 5172 : 0 : }
5173 : :
5174 : : /*
5175 : : * Clone and de-clone routines used in parallel restoration.
5176 : : *
5177 : : * Enough of the structure is cloned to ensure that there is no
5178 : : * conflict between different threads each with their own clone.
5179 : : */
5180 : : ArchiveHandle *
6382 andrew@dunslane.net 5181 :CBC 26 : CloneArchive(ArchiveHandle *AH)
5182 : : {
5183 : : ArchiveHandle *clone;
5184 : :
5185 : : /* Make a "flat" copy */
162 michael@paquier.xyz 5186 : 26 : clone = pg_malloc_object(ArchiveHandle);
6382 andrew@dunslane.net 5187 : 26 : memcpy(clone, AH, sizeof(ArchiveHandle));
5188 : :
5189 : : /* Likewise flat-copy the RestoreOptions, so we can alter them locally */
162 michael@paquier.xyz 5190 : 26 : clone->public.ropt = pg_malloc_object(RestoreOptions);
845 tgl@sss.pgh.pa.us 5191 : 26 : memcpy(clone->public.ropt, AH->public.ropt, sizeof(RestoreOptions));
5192 : :
5193 : : /* Handle format-independent fields */
5314 5194 : 26 : memset(&(clone->sqlparse), 0, sizeof(clone->sqlparse));
5195 : :
5196 : : /* The clone will have its own connection, so disregard connection state */
6382 andrew@dunslane.net 5197 : 26 : clone->connection = NULL;
3705 tgl@sss.pgh.pa.us 5198 : 26 : clone->connCancel = NULL;
6382 andrew@dunslane.net 5199 : 26 : clone->currUser = NULL;
5200 : 26 : clone->currSchema = NULL;
1650 michael@paquier.xyz 5201 : 26 : clone->currTableAm = NULL;
6382 andrew@dunslane.net 5202 : 26 : clone->currTablespace = NULL;
5203 : :
5204 : : /* savedPassword must be local in case we change it while connecting */
5205 [ - + ]: 26 : if (clone->savedPassword)
5356 bruce@momjian.us 5206 :UBC 0 : clone->savedPassword = pg_strdup(clone->savedPassword);
5207 : :
5208 : : /* clone has its own error count, too */
6382 andrew@dunslane.net 5209 :CBC 26 : clone->public.n_errors = 0;
5210 : :
5211 : : /* clones should not share lo_buf */
845 tgl@sss.pgh.pa.us 5212 : 26 : clone->lo_buf = NULL;
5213 : :
5214 : : /*
5215 : : * Clone connections disregard --transaction-size; they must commit after
5216 : : * each command so that the results are immediately visible to other
5217 : : * workers.
5218 : : */
5219 : 26 : clone->public.ropt->txn_size = 0;
5220 : :
5221 : : /*
5222 : : * Connect our new clone object to the database, using the same connection
5223 : : * parameters used for the original connection.
5224 : : */
477 andrew@dunslane.net 5225 : 26 : ConnectDatabaseAhx((Archive *) clone, &clone->public.ropt->cparams, true);
5226 : :
5227 : : /* re-establish fixed state */
2130 tgl@sss.pgh.pa.us 5228 [ + + ]: 26 : if (AH->mode == archModeRead)
3706 5229 : 10 : _doSetFixedOutputState(clone);
5230 : : /* in write case, setupDumpWorker will fix up connection state */
5231 : :
5232 : : /* Let the format-specific code have a chance too */
3243 peter_e@gmx.net 5233 : 26 : clone->ClonePtr(clone);
5234 : :
4871 andrew@dunslane.net 5235 [ - + ]: 26 : Assert(clone->connection != NULL);
6382 5236 : 26 : return clone;
5237 : : }
5238 : :
5239 : : /*
5240 : : * Release clone-local storage.
5241 : : *
5242 : : * Note: we assume any clone-local connection was already closed.
5243 : : */
5244 : : void
5245 : 26 : DeCloneArchive(ArchiveHandle *AH)
5246 : : {
5247 : : /* Should not have an open database connection */
3705 tgl@sss.pgh.pa.us 5248 [ - + ]: 26 : Assert(AH->connection == NULL);
5249 : :
5250 : : /* Clear format-specific state */
3243 peter_e@gmx.net 5251 : 26 : AH->DeClonePtr(AH);
5252 : :
5253 : : /* Clear state allocated by CloneArchive */
5314 tgl@sss.pgh.pa.us 5254 [ + + ]: 26 : if (AH->sqlparse.curCmd)
5255 : 3 : destroyPQExpBuffer(AH->sqlparse.curCmd);
5256 : :
5257 : : /* Clear any connection-local state */
1500 peter@eisentraut.org 5258 : 26 : free(AH->currUser);
5259 : 26 : free(AH->currSchema);
5260 : 26 : free(AH->currTablespace);
5261 : 26 : free(AH->currTableAm);
5262 : 26 : free(AH->savedPassword);
5263 : :
6382 andrew@dunslane.net 5264 : 26 : free(AH);
5265 : 26 : }
|