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