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