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