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