Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * pg_backup_custom.c
4 : : *
5 : : * Implements the custom output format.
6 : : *
7 : : * The comments with the routines in this code are a good place to
8 : : * understand how to write a new format.
9 : : *
10 : : * See the headers to pg_restore for more details.
11 : : *
12 : : * Copyright (c) 2000, Philip Warner
13 : : * Rights are granted to use this software in any way so long
14 : : * as this notice is not removed.
15 : : *
16 : : * The author is not responsible for loss or damages that may
17 : : * and any liability will be limited to the time taken to fix any
18 : : * related bug.
19 : : *
20 : : *
21 : : * IDENTIFICATION
22 : : * src/bin/pg_dump/pg_backup_custom.c
23 : : *
24 : : *-------------------------------------------------------------------------
25 : : */
26 : : #include "postgres_fe.h"
27 : :
28 : : #include "common/file_utils.h"
29 : : #include "compress_io.h"
30 : : #include "pg_backup_utils.h"
31 : :
32 : : /*--------
33 : : * Routines in the format interface
34 : : *--------
35 : : */
36 : :
37 : : static void _ArchiveEntry(ArchiveHandle *AH, TocEntry *te);
38 : : static void _StartData(ArchiveHandle *AH, TocEntry *te);
39 : : static void _WriteData(ArchiveHandle *AH, const void *data, size_t dLen);
40 : : static void _EndData(ArchiveHandle *AH, TocEntry *te);
41 : : static int _WriteByte(ArchiveHandle *AH, const int i);
42 : : static int _ReadByte(ArchiveHandle *AH);
43 : : static void _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len);
44 : : static void _ReadBuf(ArchiveHandle *AH, void *buf, size_t len);
45 : : static void _CloseArchive(ArchiveHandle *AH);
46 : : static void _ReopenArchive(ArchiveHandle *AH);
47 : : static void _PrintTocData(ArchiveHandle *AH, TocEntry *te);
48 : : static void _WriteExtraToc(ArchiveHandle *AH, TocEntry *te);
49 : : static void _ReadExtraToc(ArchiveHandle *AH, TocEntry *te);
50 : : static void _PrintExtraToc(ArchiveHandle *AH, TocEntry *te);
51 : :
52 : : static void _PrintData(ArchiveHandle *AH);
53 : : static void _skipData(ArchiveHandle *AH);
54 : : static void _skipLOs(ArchiveHandle *AH);
55 : :
56 : : static void _StartLOs(ArchiveHandle *AH, TocEntry *te);
57 : : static void _StartLO(ArchiveHandle *AH, TocEntry *te, Oid oid);
58 : : static void _EndLO(ArchiveHandle *AH, TocEntry *te, Oid oid);
59 : : static void _EndLOs(ArchiveHandle *AH, TocEntry *te);
60 : : static void _LoadLOs(ArchiveHandle *AH, bool drop);
61 : :
62 : : static void _PrepParallelRestore(ArchiveHandle *AH);
63 : : static void _Clone(ArchiveHandle *AH);
64 : : static void _DeClone(ArchiveHandle *AH);
65 : :
66 : : static int _WorkerJobRestoreCustom(ArchiveHandle *AH, TocEntry *te);
67 : :
68 : : typedef struct
69 : : {
70 : : CompressorState *cs;
71 : : int hasSeek;
72 : : /* lastFilePos is used only when reading, and may be invalid if !hasSeek */
73 : : pgoff_t lastFilePos; /* position after last data block we've read */
74 : : } lclContext;
75 : :
76 : : typedef struct
77 : : {
78 : : int dataState;
79 : : pgoff_t dataPos; /* valid only if dataState=K_OFFSET_POS_SET */
80 : : } lclTocEntry;
81 : :
82 : :
83 : : /*------
84 : : * Static declarations
85 : : *------
86 : : */
87 : : static void _readBlockHeader(ArchiveHandle *AH, int *type, int *id);
88 : : static pgoff_t _getFilePos(ArchiveHandle *AH, lclContext *ctx);
89 : :
90 : : static void _CustomWriteFunc(ArchiveHandle *AH, const char *buf, size_t len);
91 : : static size_t _CustomReadFunc(ArchiveHandle *AH, char **buf, size_t *buflen);
92 : :
93 : :
94 : : /*
95 : : * Init routine required by ALL formats. This is a global routine
96 : : * and should be declared in pg_backup_archiver.h
97 : : *
98 : : * It's task is to create any extra archive context (using AH->formatData),
99 : : * and to initialize the supported function pointers.
100 : : *
101 : : * It should also prepare whatever its input source is for reading/writing,
102 : : * and in the case of a read mode connection, it should load the Header & TOC.
103 : : */
104 : : void
8934 bruce@momjian.us 105 :CBC 86 : InitArchiveFmt_Custom(ArchiveHandle *AH)
106 : : {
107 : : lclContext *ctx;
108 : :
109 : : /* Assuming static functions, this can be copied for each format. */
110 : 86 : AH->ArchiveEntryPtr = _ArchiveEntry;
111 : 86 : AH->StartDataPtr = _StartData;
112 : 86 : AH->WriteDataPtr = _WriteData;
113 : 86 : AH->EndDataPtr = _EndData;
114 : 86 : AH->WriteBytePtr = _WriteByte;
115 : 86 : AH->ReadBytePtr = _ReadByte;
116 : 86 : AH->WriteBufPtr = _WriteBuf;
117 : 86 : AH->ReadBufPtr = _ReadBuf;
118 : 86 : AH->ClosePtr = _CloseArchive;
6060 andrew@dunslane.net 119 : 86 : AH->ReopenPtr = _ReopenArchive;
8934 bruce@momjian.us 120 : 86 : AH->PrintTocDataPtr = _PrintTocData;
121 : 86 : AH->ReadExtraTocPtr = _ReadExtraToc;
122 : 86 : AH->WriteExtraTocPtr = _WriteExtraToc;
123 : 86 : AH->PrintExtraTocPtr = _PrintExtraToc;
124 : :
1006 peter@eisentraut.org 125 : 86 : AH->StartLOsPtr = _StartLOs;
126 : 86 : AH->StartLOPtr = _StartLO;
127 : 86 : AH->EndLOPtr = _EndLO;
128 : 86 : AH->EndLOsPtr = _EndLOs;
129 : :
2549 tgl@sss.pgh.pa.us 130 : 86 : AH->PrepParallelRestorePtr = _PrepParallelRestore;
5931 bruce@momjian.us 131 : 86 : AH->ClonePtr = _Clone;
132 : 86 : AH->DeClonePtr = _DeClone;
133 : :
134 : : /* no parallel dump in the custom archive, only parallel restore */
4549 andrew@dunslane.net 135 : 86 : AH->WorkerJobDumpPtr = NULL;
136 : 86 : AH->WorkerJobRestorePtr = _WorkerJobRestoreCustom;
137 : :
138 : : /* Set up a private area. */
4722 tgl@sss.pgh.pa.us 139 : 86 : ctx = (lclContext *) pg_malloc0(sizeof(lclContext));
282 peter@eisentraut.org 140 : 86 : AH->formatData = ctx;
141 : :
142 : : /*
143 : : * Now open the file
144 : : */
8934 bruce@momjian.us 145 [ + + ]: 86 : if (AH->mode == archModeWrite)
146 : : {
147 [ + - + - ]: 42 : if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
148 : : {
9178 pjw@rhyme.com.au 149 : 42 : AH->FH = fopen(AH->fSpec, PG_BINARY_W);
6523 tgl@sss.pgh.pa.us 150 [ - + ]: 42 : if (!AH->FH)
1247 tgl@sss.pgh.pa.us 151 :UBC 0 : pg_fatal("could not open output file \"%s\": %m", AH->fSpec);
152 : : }
153 : : else
154 : : {
9178 pjw@rhyme.com.au 155 : 0 : AH->FH = stdout;
6523 tgl@sss.pgh.pa.us 156 [ # # ]: 0 : if (!AH->FH)
1247 157 : 0 : pg_fatal("could not open output file: %m");
158 : : }
159 : :
8352 bruce@momjian.us 160 :CBC 42 : ctx->hasSeek = checkSeek(AH->FH);
161 : : }
162 : : else
163 : : {
8934 164 [ + - + - ]: 44 : if (AH->fSpec && strcmp(AH->fSpec, "") != 0)
165 : : {
9178 pjw@rhyme.com.au 166 : 44 : AH->FH = fopen(AH->fSpec, PG_BINARY_R);
6523 tgl@sss.pgh.pa.us 167 [ - + ]: 44 : if (!AH->FH)
1247 tgl@sss.pgh.pa.us 168 :UBC 0 : pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
169 : : }
170 : : else
171 : : {
9178 pjw@rhyme.com.au 172 : 0 : AH->FH = stdin;
6523 tgl@sss.pgh.pa.us 173 [ # # ]: 0 : if (!AH->FH)
1247 174 : 0 : pg_fatal("could not open input file: %m");
175 : : }
176 : :
8352 bruce@momjian.us 177 :CBC 44 : ctx->hasSeek = checkSeek(AH->FH);
178 : :
9178 pjw@rhyme.com.au 179 : 44 : ReadHead(AH);
180 : 44 : ReadToc(AH);
181 : :
182 : : /*
183 : : * Remember location of first data block (i.e., the point after TOC)
184 : : * in case we have to search for desired data blocks.
185 : : */
1877 tgl@sss.pgh.pa.us 186 : 44 : ctx->lastFilePos = _getFilePos(AH, ctx);
187 : : }
9178 pjw@rhyme.com.au 188 : 86 : }
189 : :
190 : : /*
191 : : * Called by the Archiver when the dumper creates a new TOC entry.
192 : : *
193 : : * Optional.
194 : : *
195 : : * Set up extract format-related TOC data.
196 : : */
197 : : static void
8934 bruce@momjian.us 198 : 5461 : _ArchiveEntry(ArchiveHandle *AH, TocEntry *te)
199 : : {
200 : : lclTocEntry *ctx;
201 : :
4722 tgl@sss.pgh.pa.us 202 : 5461 : ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
8934 bruce@momjian.us 203 [ + + ]: 5461 : if (te->dataDumper)
8355 204 : 248 : ctx->dataState = K_OFFSET_POS_NOT_SET;
205 : : else
206 : 5213 : ctx->dataState = K_OFFSET_NO_DATA;
207 : :
282 peter@eisentraut.org 208 : 5461 : te->formatData = ctx;
9178 pjw@rhyme.com.au 209 : 5461 : }
210 : :
211 : : /*
212 : : * Called by the Archiver to save any extra format-related TOC entry
213 : : * data.
214 : : *
215 : : * Optional.
216 : : *
217 : : * Use the Archiver routines to write data - they are non-endian, and
218 : : * maintain other important file information.
219 : : */
220 : : static void
8934 bruce@momjian.us 221 : 7062 : _WriteExtraToc(ArchiveHandle *AH, TocEntry *te)
222 : : {
223 : 7062 : lclTocEntry *ctx = (lclTocEntry *) te->formatData;
224 : :
8355 225 : 7062 : WriteOffset(AH, ctx->dataPos, ctx->dataState);
9178 pjw@rhyme.com.au 226 : 7062 : }
227 : :
228 : : /*
229 : : * Called by the Archiver to read any extra format-related TOC data.
230 : : *
231 : : * Optional.
232 : : *
233 : : * Needs to match the order defined in _WriteExtraToc, and should also
234 : : * use the Archiver input routines.
235 : : */
236 : : static void
8934 bruce@momjian.us 237 : 7020 : _ReadExtraToc(ArchiveHandle *AH, TocEntry *te)
238 : : {
239 : 7020 : lclTocEntry *ctx = (lclTocEntry *) te->formatData;
240 : :
241 [ + - ]: 7020 : if (ctx == NULL)
242 : : {
4722 tgl@sss.pgh.pa.us 243 : 7020 : ctx = (lclTocEntry *) pg_malloc0(sizeof(lclTocEntry));
282 peter@eisentraut.org 244 : 7020 : te->formatData = ctx;
245 : : }
246 : :
8069 bruce@momjian.us 247 : 7020 : ctx->dataState = ReadOffset(AH, &(ctx->dataPos));
248 : :
249 : : /*
250 : : * Prior to V1.7 (pg7.3), we dumped the data size as an int now we don't
251 : : * dump it at all.
252 : : */
8355 253 [ - + ]: 7020 : if (AH->version < K_VERS_1_7)
5262 peter_e@gmx.net 254 :UBC 0 : ReadInt(AH);
9178 pjw@rhyme.com.au 255 :CBC 7020 : }
256 : :
257 : : /*
258 : : * Called by the Archiver when restoring an archive to output a comment
259 : : * that includes useful information about the TOC entry.
260 : : *
261 : : * Optional.
262 : : */
263 : : static void
8934 bruce@momjian.us 264 : 1889 : _PrintExtraToc(ArchiveHandle *AH, TocEntry *te)
265 : : {
266 : 1889 : lclTocEntry *ctx = (lclTocEntry *) te->formatData;
267 : :
7857 tgl@sss.pgh.pa.us 268 [ + + ]: 1889 : if (AH->public.verbose)
269 : 331 : ahprintf(AH, "-- Data Pos: " INT64_FORMAT "\n",
270 : 331 : (int64) ctx->dataPos);
9178 pjw@rhyme.com.au 271 : 1889 : }
272 : :
273 : : /*
274 : : * Called by the archiver when saving TABLE DATA (not schema). This routine
275 : : * should save whatever format-specific information is needed to read
276 : : * the archive back.
277 : : *
278 : : * It is called just prior to the dumper's 'DataDumper' routine being called.
279 : : *
280 : : * Optional, but strongly recommended.
281 : : *
282 : : */
283 : : static void
8934 bruce@momjian.us 284 : 237 : _StartData(ArchiveHandle *AH, TocEntry *te)
285 : : {
286 : 237 : lclContext *ctx = (lclContext *) AH->formatData;
287 : 237 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
288 : :
289 : 237 : tctx->dataPos = _getFilePos(AH, ctx);
1877 tgl@sss.pgh.pa.us 290 [ + - ]: 237 : if (tctx->dataPos >= 0)
291 : 237 : tctx->dataState = K_OFFSET_POS_SET;
292 : :
8934 bruce@momjian.us 293 : 237 : _WriteByte(AH, BLK_DATA); /* Block type */
7945 tgl@sss.pgh.pa.us 294 : 237 : WriteInt(AH, te->dumpId); /* For sanity check */
295 : :
926 tomas.vondra@postgre 296 : 237 : ctx->cs = AllocateCompressor(AH->compression_spec,
297 : : NULL,
298 : : _CustomWriteFunc);
9178 pjw@rhyme.com.au 299 : 237 : }
300 : :
301 : : /*
302 : : * Called by archiver when dumper calls WriteData. This routine is
303 : : * called for both LO and table data; it is the responsibility of
304 : : * the format to manage each kind of data using StartLO/StartData.
305 : : *
306 : : * It should only be called from within a DataDumper routine.
307 : : *
308 : : * Mandatory.
309 : : */
310 : : static void
8418 peter_e@gmx.net 311 : 780 : _WriteData(ArchiveHandle *AH, const void *data, size_t dLen)
312 : : {
8934 bruce@momjian.us 313 : 780 : lclContext *ctx = (lclContext *) AH->formatData;
5263 314 : 780 : CompressorState *cs = ctx->cs;
315 : :
4142 316 [ + + ]: 780 : if (dLen > 0)
317 : : /* writeData() internally throws write errors */
926 tomas.vondra@postgre 318 : 772 : cs->writeData(AH, cs, data, dLen);
9178 pjw@rhyme.com.au 319 : 780 : }
320 : :
321 : : /*
322 : : * Called by the archiver when a dumper's 'DataDumper' routine has
323 : : * finished.
324 : : *
325 : : * Mandatory.
326 : : */
327 : : static void
8934 bruce@momjian.us 328 : 237 : _EndData(ArchiveHandle *AH, TocEntry *te)
329 : : {
5392 heikki.linnakangas@i 330 : 237 : lclContext *ctx = (lclContext *) AH->formatData;
331 : :
332 : 237 : EndCompressor(AH, ctx->cs);
926 tomas.vondra@postgre 333 : 237 : ctx->cs = NULL;
334 : :
335 : : /* Send the end marker */
5392 heikki.linnakangas@i 336 : 237 : WriteInt(AH, 0);
9178 pjw@rhyme.com.au 337 : 237 : }
338 : :
339 : : /*
340 : : * Called by the archiver when starting to save BLOB DATA (not schema).
341 : : * This routine should save whatever format-specific information is needed
342 : : * to read the LOs back into memory.
343 : : *
344 : : * It is called just prior to the dumper's DataDumper routine.
345 : : *
346 : : * Optional, but strongly recommended.
347 : : */
348 : : static void
1006 peter@eisentraut.org 349 : 8 : _StartLOs(ArchiveHandle *AH, TocEntry *te)
350 : : {
8934 bruce@momjian.us 351 : 8 : lclContext *ctx = (lclContext *) AH->formatData;
352 : 8 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
353 : :
354 : 8 : tctx->dataPos = _getFilePos(AH, ctx);
1877 tgl@sss.pgh.pa.us 355 [ + - ]: 8 : if (tctx->dataPos >= 0)
356 : 8 : tctx->dataState = K_OFFSET_POS_SET;
357 : :
8934 bruce@momjian.us 358 : 8 : _WriteByte(AH, BLK_BLOBS); /* Block type */
7945 tgl@sss.pgh.pa.us 359 : 8 : WriteInt(AH, te->dumpId); /* For sanity check */
9178 pjw@rhyme.com.au 360 : 8 : }
361 : :
362 : : /*
363 : : * Called by the archiver when the dumper calls StartLO.
364 : : *
365 : : * Mandatory.
366 : : *
367 : : * Must save the passed OID for retrieval at restore-time.
368 : : */
369 : : static void
1006 peter@eisentraut.org 370 : 8 : _StartLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
371 : : {
5392 heikki.linnakangas@i 372 : 8 : lclContext *ctx = (lclContext *) AH->formatData;
373 : :
8934 bruce@momjian.us 374 [ - + ]: 8 : if (oid == 0)
1247 tgl@sss.pgh.pa.us 375 :UBC 0 : pg_fatal("invalid OID for large object");
376 : :
8934 bruce@momjian.us 377 :CBC 8 : WriteInt(AH, oid);
378 : :
926 tomas.vondra@postgre 379 : 8 : ctx->cs = AllocateCompressor(AH->compression_spec,
380 : : NULL,
381 : : _CustomWriteFunc);
9178 pjw@rhyme.com.au 382 : 8 : }
383 : :
384 : : /*
385 : : * Called by the archiver when the dumper calls EndLO.
386 : : *
387 : : * Optional.
388 : : */
389 : : static void
1006 peter@eisentraut.org 390 : 8 : _EndLO(ArchiveHandle *AH, TocEntry *te, Oid oid)
391 : : {
5392 heikki.linnakangas@i 392 : 8 : lclContext *ctx = (lclContext *) AH->formatData;
393 : :
394 : 8 : EndCompressor(AH, ctx->cs);
395 : : /* Send the end marker */
396 : 8 : WriteInt(AH, 0);
9178 pjw@rhyme.com.au 397 : 8 : }
398 : :
399 : : /*
400 : : * Called by the archiver when finishing saving BLOB DATA.
401 : : *
402 : : * Optional.
403 : : */
404 : : static void
1006 peter@eisentraut.org 405 : 8 : _EndLOs(ArchiveHandle *AH, TocEntry *te)
406 : : {
407 : : /* Write out a fake zero OID to mark end-of-LOs. */
8934 bruce@momjian.us 408 : 8 : WriteInt(AH, 0);
9178 pjw@rhyme.com.au 409 : 8 : }
410 : :
411 : : /*
412 : : * Print data for a given TOC entry
413 : : */
414 : : static void
3524 tgl@sss.pgh.pa.us 415 : 226 : _PrintTocData(ArchiveHandle *AH, TocEntry *te)
416 : : {
8934 bruce@momjian.us 417 : 226 : lclContext *ctx = (lclContext *) AH->formatData;
418 : 226 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
419 : : int blkType;
420 : : int id;
421 : :
8355 422 [ - + ]: 226 : if (tctx->dataState == K_OFFSET_NO_DATA)
9178 pjw@rhyme.com.au 423 :UBC 0 : return;
424 : :
8355 bruce@momjian.us 425 [ + - + + ]:CBC 226 : if (!ctx->hasSeek || tctx->dataState == K_OFFSET_POS_NOT_SET)
426 : : {
427 : : /*
428 : : * We cannot seek directly to the desired block. Instead, skip over
429 : : * block headers until we find the one we want. Remember the
430 : : * positions of skipped-over blocks, so that if we later decide we
431 : : * need to read one, we'll be able to seek to it.
432 : : *
433 : : * When our input file is seekable, we can do the search starting from
434 : : * the point after the last data block we scanned in previous
435 : : * iterations of this function.
436 : : */
1877 tgl@sss.pgh.pa.us 437 [ - + ]:GBC 58 : if (ctx->hasSeek)
438 : : {
439 [ + - ]: 58 : if (fseeko(AH->FH, ctx->lastFilePos, SEEK_SET) != 0)
1247 tgl@sss.pgh.pa.us 440 :UBC 0 : pg_fatal("error during file seek: %m");
441 : : }
442 : :
443 : : for (;;)
8934 bruce@momjian.us 444 : 0 : {
1877 tgl@sss.pgh.pa.us 445 :GBC 58 : pgoff_t thisBlkPos = _getFilePos(AH, ctx);
446 : :
447 : 58 : _readBlockHeader(AH, &blkType, &id);
448 : :
449 [ + - - + ]: 58 : if (blkType == EOF || id == te->dumpId)
450 : : break;
451 : :
452 : : /* Remember the block position, if we got one */
1877 tgl@sss.pgh.pa.us 453 [ # # ]:UBC 0 : if (thisBlkPos >= 0)
454 : : {
455 : 0 : TocEntry *otherte = getTocEntryByDumpId(AH, id);
456 : :
457 [ # # # # ]: 0 : if (otherte && otherte->formatData)
458 : : {
459 : 0 : lclTocEntry *othertctx = (lclTocEntry *) otherte->formatData;
460 : :
461 : : /*
462 : : * Note: on Windows, multiple threads might access/update
463 : : * the same lclTocEntry concurrently, but that should be
464 : : * safe as long as we update dataPos before dataState.
465 : : * Ideally, we'd use pg_write_barrier() to enforce that,
466 : : * but the needed infrastructure doesn't exist in frontend
467 : : * code. But Windows only runs on machines with strong
468 : : * store ordering, so it should be okay for now.
469 : : */
470 [ # # ]: 0 : if (othertctx->dataState == K_OFFSET_POS_NOT_SET)
471 : : {
472 : 0 : othertctx->dataPos = thisBlkPos;
473 : 0 : othertctx->dataState = K_OFFSET_POS_SET;
474 : : }
475 [ # # ]: 0 : else if (othertctx->dataPos != thisBlkPos ||
476 [ # # ]: 0 : othertctx->dataState != K_OFFSET_POS_SET)
477 : : {
478 : : /* sanity check */
479 : 0 : pg_log_warning("data block %d has wrong seek position",
480 : : id);
481 : : }
482 : : }
483 : : }
484 : :
8934 bruce@momjian.us 485 [ # # # ]: 0 : switch (blkType)
486 : : {
487 : 0 : case BLK_DATA:
488 : 0 : _skipData(AH);
489 : 0 : break;
490 : :
491 : 0 : case BLK_BLOBS:
1006 peter@eisentraut.org 492 : 0 : _skipLOs(AH);
8934 bruce@momjian.us 493 : 0 : break;
494 : :
495 : 0 : default: /* Always have a default */
1247 tgl@sss.pgh.pa.us 496 : 0 : pg_fatal("unrecognized data block type (%d) while searching archive",
497 : : blkType);
498 : : break;
499 : : }
500 : : }
501 : : }
502 : : else
503 : : {
504 : : /* We can just seek to the place we need to be. */
8418 peter_e@gmx.net 505 [ - + ]:CBC 168 : if (fseeko(AH->FH, tctx->dataPos, SEEK_SET) != 0)
1247 tgl@sss.pgh.pa.us 506 :UBC 0 : pg_fatal("error during file seek: %m");
507 : :
9178 pjw@rhyme.com.au 508 :CBC 168 : _readBlockHeader(AH, &blkType, &id);
509 : : }
510 : :
511 : : /*
512 : : * If we reached EOF without finding the block we want, then either it
513 : : * doesn't exist, or it does but we lack the ability to seek back to it.
514 : : */
5550 tgl@sss.pgh.pa.us 515 [ - + ]: 226 : if (blkType == EOF)
516 : : {
1877 tgl@sss.pgh.pa.us 517 [ # # ]:UBC 0 : if (!ctx->hasSeek)
1247 518 : 0 : pg_fatal("could not find block ID %d in archive -- "
519 : : "possibly due to out-of-order restore request, "
520 : : "which cannot be handled due to non-seekable input file",
521 : : te->dumpId);
522 : : else
523 : 0 : pg_fatal("could not find block ID %d in archive -- "
524 : : "possibly corrupt archive",
525 : : te->dumpId);
526 : : }
527 : :
528 : : /* Are we sane? */
7945 tgl@sss.pgh.pa.us 529 [ - + ]:CBC 226 : if (id != te->dumpId)
1247 tgl@sss.pgh.pa.us 530 :UBC 0 : pg_fatal("found unexpected block ID (%d) when reading data -- expected %d",
531 : : id, te->dumpId);
532 : :
8934 bruce@momjian.us 533 [ + + - ]:CBC 226 : switch (blkType)
534 : : {
9178 pjw@rhyme.com.au 535 : 218 : case BLK_DATA:
536 : 218 : _PrintData(AH);
537 : 218 : break;
538 : :
539 : 8 : case BLK_BLOBS:
1006 peter@eisentraut.org 540 : 8 : _LoadLOs(AH, AH->public.ropt->dropSchema);
9178 pjw@rhyme.com.au 541 : 8 : break;
542 : :
8934 bruce@momjian.us 543 :UBC 0 : default: /* Always have a default */
1247 tgl@sss.pgh.pa.us 544 : 0 : pg_fatal("unrecognized data block type %d while restoring archive",
545 : : blkType);
546 : : break;
547 : : }
548 : :
549 : : /*
550 : : * If our input file is seekable but lacks data offsets, update our
551 : : * knowledge of where to start future searches from. (Note that we did
552 : : * not update the current TE's dataState/dataPos. We could have, but
553 : : * there is no point since it will not be visited again.)
554 : : */
1877 tgl@sss.pgh.pa.us 555 [ + - + + ]:CBC 226 : if (ctx->hasSeek && tctx->dataState == K_OFFSET_POS_NOT_SET)
556 : : {
1877 tgl@sss.pgh.pa.us 557 :GBC 58 : pgoff_t curPos = _getFilePos(AH, ctx);
558 : :
559 [ + - ]: 58 : if (curPos > ctx->lastFilePos)
560 : 58 : ctx->lastFilePos = curPos;
561 : : }
562 : : }
563 : :
564 : : /*
565 : : * Print data from current file position.
566 : : */
567 : : static void
8934 bruce@momjian.us 568 :CBC 226 : _PrintData(ArchiveHandle *AH)
569 : : {
570 : : CompressorState *cs;
571 : :
926 tomas.vondra@postgre 572 : 226 : cs = AllocateCompressor(AH->compression_spec,
573 : : _CustomReadFunc, NULL);
574 : 226 : cs->readData(AH, cs);
575 : 226 : EndCompressor(AH, cs);
9178 pjw@rhyme.com.au 576 : 226 : }
577 : :
578 : : static void
1006 peter@eisentraut.org 579 : 8 : _LoadLOs(ArchiveHandle *AH, bool drop)
580 : : {
581 : : Oid oid;
582 : :
583 : 8 : StartRestoreLOs(AH);
584 : :
8934 bruce@momjian.us 585 : 8 : oid = ReadInt(AH);
586 [ + + ]: 16 : while (oid != 0)
587 : : {
1006 peter@eisentraut.org 588 : 8 : StartRestoreLO(AH, oid, drop);
9178 pjw@rhyme.com.au 589 : 8 : _PrintData(AH);
1006 peter@eisentraut.org 590 : 8 : EndRestoreLO(AH, oid);
9178 pjw@rhyme.com.au 591 : 8 : oid = ReadInt(AH);
592 : : }
593 : :
1006 peter@eisentraut.org 594 : 8 : EndRestoreLOs(AH);
9178 pjw@rhyme.com.au 595 : 8 : }
596 : :
597 : : /*
598 : : * Skip the LOs from the current file position.
599 : : * LOs are written sequentially as data blocks (see below).
600 : : * Each LO is preceded by its original OID.
601 : : * A zero OID indicates the end of the LOs.
602 : : */
603 : : static void
1006 peter@eisentraut.org 604 :UBC 0 : _skipLOs(ArchiveHandle *AH)
605 : : {
606 : : Oid oid;
607 : :
8934 bruce@momjian.us 608 : 0 : oid = ReadInt(AH);
609 [ # # ]: 0 : while (oid != 0)
610 : : {
9076 pjw@rhyme.com.au 611 : 0 : _skipData(AH);
612 : 0 : oid = ReadInt(AH);
613 : : }
9178 614 : 0 : }
615 : :
616 : : /*
617 : : * Skip data from current file position.
618 : : * Data blocks are formatted as an integer length, followed by data.
619 : : * A zero length indicates the end of the block.
620 : : */
621 : : static void
8934 bruce@momjian.us 622 : 0 : _skipData(ArchiveHandle *AH)
623 : : {
1877 tgl@sss.pgh.pa.us 624 : 0 : lclContext *ctx = (lclContext *) AH->formatData;
625 : : size_t blkLen;
5392 heikki.linnakangas@i 626 : 0 : char *buf = NULL;
627 : 0 : int buflen = 0;
628 : :
9178 pjw@rhyme.com.au 629 : 0 : blkLen = ReadInt(AH);
8934 bruce@momjian.us 630 [ # # ]: 0 : while (blkLen != 0)
631 : : {
1877 tgl@sss.pgh.pa.us 632 [ # # ]: 0 : if (ctx->hasSeek)
633 : : {
634 [ # # ]: 0 : if (fseeko(AH->FH, blkLen, SEEK_CUR) != 0)
1247 635 : 0 : pg_fatal("error during file seek: %m");
636 : : }
637 : : else
638 : : {
1877 639 [ # # ]: 0 : if (blkLen > buflen)
640 : : {
1178 peter@eisentraut.org 641 : 0 : free(buf);
1877 tgl@sss.pgh.pa.us 642 : 0 : buf = (char *) pg_malloc(blkLen);
643 : 0 : buflen = blkLen;
644 : : }
1827 645 [ # # ]: 0 : if (fread(buf, 1, blkLen, AH->FH) != blkLen)
646 : : {
1877 647 [ # # ]: 0 : if (feof(AH->FH))
1247 648 : 0 : pg_fatal("could not read from input file: end of file");
649 : : else
650 : 0 : pg_fatal("could not read from input file: %m");
651 : : }
652 : : }
653 : :
8934 bruce@momjian.us 654 : 0 : blkLen = ReadInt(AH);
655 : : }
656 : :
1178 peter@eisentraut.org 657 : 0 : free(buf);
9178 pjw@rhyme.com.au 658 : 0 : }
659 : :
660 : : /*
661 : : * Write a byte of data to the archive.
662 : : *
663 : : * Mandatory.
664 : : *
665 : : * Called by the archiver to do integer & byte output to the archive.
666 : : */
667 : : static int
8934 bruce@momjian.us 668 :CBC 725067 : _WriteByte(ArchiveHandle *AH, const int i)
669 : : {
1827 tgl@sss.pgh.pa.us 670 [ - + ]: 725067 : if (fputc(i, AH->FH) == EOF)
4142 bruce@momjian.us 671 :UBC 0 : WRITE_ERROR_EXIT;
672 : :
4142 bruce@momjian.us 673 :CBC 725067 : return 1;
674 : : }
675 : :
676 : : /*
677 : : * Read a byte of data from the archive.
678 : : *
679 : : * Mandatory
680 : : *
681 : : * Called by the archiver to read bytes & integers from the archive.
682 : : * EOF should be treated as a fatal error.
683 : : */
684 : : static int
8934 685 : 722023 : _ReadByte(ArchiveHandle *AH)
686 : : {
687 : : int res;
688 : :
6606 tgl@sss.pgh.pa.us 689 : 722023 : res = getc(AH->FH);
690 [ - + ]: 722023 : if (res == EOF)
4142 bruce@momjian.us 691 [ # # ]:UBC 0 : READ_ERROR_EXIT(AH->FH);
8934 bruce@momjian.us 692 :CBC 722023 : return res;
693 : : }
694 : :
695 : : /*
696 : : * Write a buffer of data to the archive.
697 : : *
698 : : * Mandatory.
699 : : *
700 : : * Called by the archiver to write a block of bytes to the archive.
701 : : */
702 : : static void
8418 peter_e@gmx.net 703 : 71283 : _WriteBuf(ArchiveHandle *AH, const void *buf, size_t len)
704 : : {
4142 bruce@momjian.us 705 [ - + ]: 71283 : if (fwrite(buf, 1, len, AH->FH) != len)
4142 bruce@momjian.us 706 :UBC 0 : WRITE_ERROR_EXIT;
9178 pjw@rhyme.com.au 707 :CBC 71283 : }
708 : :
709 : : /*
710 : : * Read a block of bytes from the archive.
711 : : *
712 : : * Mandatory.
713 : : *
714 : : * Called by the archiver to read a block of bytes from the archive
715 : : */
716 : : static void
8418 peter_e@gmx.net 717 : 71289 : _ReadBuf(ArchiveHandle *AH, void *buf, size_t len)
718 : : {
4142 bruce@momjian.us 719 [ - + ]: 71289 : if (fread(buf, 1, len, AH->FH) != len)
4142 bruce@momjian.us 720 [ # # ]:UBC 0 : READ_ERROR_EXIT(AH->FH);
9178 pjw@rhyme.com.au 721 :CBC 71289 : }
722 : :
723 : : /*
724 : : * Close the archive.
725 : : *
726 : : * Mandatory.
727 : : *
728 : : * When writing the archive, this is the routine that actually starts
729 : : * the process of saving it to files. No data should be written prior
730 : : * to this point, since the user could sort the TOC after creating it.
731 : : *
732 : : * If an archive is to be written, this routine must call:
733 : : * WriteHead to save the archive header
734 : : * WriteToc to save the TOC entries
735 : : * WriteDataChunks to save all data & LOs.
736 : : *
737 : : */
738 : : static void
3524 tgl@sss.pgh.pa.us 739 : 86 : _CloseArchive(ArchiveHandle *AH)
740 : : {
8934 bruce@momjian.us 741 : 86 : lclContext *ctx = (lclContext *) AH->formatData;
742 : : pgoff_t tpos;
743 : :
744 [ + + ]: 86 : if (AH->mode == archModeWrite)
745 : : {
9178 pjw@rhyme.com.au 746 : 42 : WriteHead(AH);
747 : : /* Remember TOC's seek position for use below */
8418 peter_e@gmx.net 748 : 42 : tpos = ftello(AH->FH);
4142 tgl@sss.pgh.pa.us 749 [ - + - - ]: 42 : if (tpos < 0 && ctx->hasSeek)
1247 tgl@sss.pgh.pa.us 750 :UBC 0 : pg_fatal("could not determine seek position in archive file: %m");
9178 pjw@rhyme.com.au 751 :CBC 42 : WriteToc(AH);
3524 tgl@sss.pgh.pa.us 752 : 42 : WriteDataChunks(AH, NULL);
753 : :
754 : : /*
755 : : * If possible, re-write the TOC in order to update the data offset
756 : : * information. This is not essential, as pg_restore can cope in most
757 : : * cases without it; but it can make pg_restore significantly faster
758 : : * in some situations (especially parallel restore). We can skip this
759 : : * step if we're not dumping any data; there are no offsets to update
760 : : * in that case.
761 : : */
155 nathan@postgresql.or 762 [ + - + + : 49 : if (ctx->hasSeek && AH->public.dopt->dumpData &&
+ - ]
6060 andrew@dunslane.net 763 : 7 : fseeko(AH->FH, tpos, SEEK_SET) == 0)
9178 pjw@rhyme.com.au 764 : 7 : WriteToc(AH);
765 : : }
766 : :
8934 bruce@momjian.us 767 [ - + ]: 86 : if (fclose(AH->FH) != 0)
1247 tgl@sss.pgh.pa.us 768 :UBC 0 : pg_fatal("could not close archive file: %m");
769 : :
770 : : /* Sync the output file if one is defined */
3090 andrew@dunslane.net 771 [ + + + + :CBC 86 : if (AH->dosync && AH->mode == archModeWrite && AH->fSpec)
+ - ]
2350 peter@eisentraut.org 772 : 6 : (void) fsync_fname(AH->fSpec, false);
773 : :
8934 bruce@momjian.us 774 : 86 : AH->FH = NULL;
9178 pjw@rhyme.com.au 775 : 86 : }
776 : :
777 : : /*
778 : : * Reopen the archive's file handle.
779 : : *
780 : : * We close the original file handle, except on Windows. (The difference
781 : : * is because on Windows, this is used within a multithreading context,
782 : : * and we don't want a thread closing the parent file handle.)
783 : : */
784 : : static void
6060 andrew@dunslane.net 785 :UBC 0 : _ReopenArchive(ArchiveHandle *AH)
786 : : {
787 : 0 : lclContext *ctx = (lclContext *) AH->formatData;
788 : : pgoff_t tpos;
789 : :
790 [ # # ]: 0 : if (AH->mode == archModeWrite)
1247 tgl@sss.pgh.pa.us 791 : 0 : pg_fatal("can only reopen input archives");
792 : :
793 : : /*
794 : : * These two cases are user-facing errors since they represent unsupported
795 : : * (but not invalid) use-cases. Word the error messages appropriately.
796 : : */
6060 andrew@dunslane.net 797 [ # # # # ]: 0 : if (AH->fSpec == NULL || strcmp(AH->fSpec, "") == 0)
1247 tgl@sss.pgh.pa.us 798 : 0 : pg_fatal("parallel restore from standard input is not supported");
6060 andrew@dunslane.net 799 [ # # ]: 0 : if (!ctx->hasSeek)
1247 tgl@sss.pgh.pa.us 800 : 0 : pg_fatal("parallel restore from non-seekable file is not supported");
801 : :
6060 andrew@dunslane.net 802 : 0 : tpos = ftello(AH->FH);
4227 sfrost@snowman.net 803 [ # # ]: 0 : if (tpos < 0)
1247 tgl@sss.pgh.pa.us 804 : 0 : pg_fatal("could not determine seek position in archive file: %m");
805 : :
806 : : #ifndef WIN32
6060 andrew@dunslane.net 807 [ # # ]: 0 : if (fclose(AH->FH) != 0)
1247 tgl@sss.pgh.pa.us 808 : 0 : pg_fatal("could not close archive file: %m");
809 : : #endif
810 : :
6060 andrew@dunslane.net 811 : 0 : AH->FH = fopen(AH->fSpec, PG_BINARY_R);
812 [ # # ]: 0 : if (!AH->FH)
1247 tgl@sss.pgh.pa.us 813 : 0 : pg_fatal("could not open input file \"%s\": %m", AH->fSpec);
814 : :
6060 andrew@dunslane.net 815 [ # # ]: 0 : if (fseeko(AH->FH, tpos, SEEK_SET) != 0)
1247 tgl@sss.pgh.pa.us 816 : 0 : pg_fatal("could not set seek position in archive file: %m");
6060 andrew@dunslane.net 817 : 0 : }
818 : :
819 : : /*
820 : : * Prepare for parallel restore.
821 : : *
822 : : * The main thing that needs to happen here is to fill in TABLE DATA and BLOBS
823 : : * TOC entries' dataLength fields with appropriate values to guide the
824 : : * ordering of restore jobs. The source of said data is format-dependent,
825 : : * as is the exact meaning of the values.
826 : : *
827 : : * A format module might also choose to do other setup here.
828 : : */
829 : : static void
2549 tgl@sss.pgh.pa.us 830 : 0 : _PrepParallelRestore(ArchiveHandle *AH)
831 : : {
832 : 0 : lclContext *ctx = (lclContext *) AH->formatData;
833 : 0 : TocEntry *prev_te = NULL;
834 : 0 : lclTocEntry *prev_tctx = NULL;
835 : : TocEntry *te;
836 : :
837 : : /*
838 : : * Knowing that the data items were dumped out in TOC order, we can
839 : : * reconstruct the length of each item as the delta to the start offset of
840 : : * the next data item.
841 : : */
842 [ # # ]: 0 : for (te = AH->toc->next; te != AH->toc; te = te->next)
843 : : {
844 : 0 : lclTocEntry *tctx = (lclTocEntry *) te->formatData;
845 : :
846 : : /*
847 : : * Ignore entries without a known data offset; if we were unable to
848 : : * seek to rewrite the TOC when creating the archive, this'll be all
849 : : * of them, and we'll end up with no size estimates.
850 : : */
851 [ # # ]: 0 : if (tctx->dataState != K_OFFSET_POS_SET)
852 : 0 : continue;
853 : :
854 : : /* Compute previous data item's length */
855 [ # # ]: 0 : if (prev_te)
856 : : {
857 [ # # ]: 0 : if (tctx->dataPos > prev_tctx->dataPos)
858 : 0 : prev_te->dataLength = tctx->dataPos - prev_tctx->dataPos;
859 : : }
860 : :
861 : 0 : prev_te = te;
862 : 0 : prev_tctx = tctx;
863 : : }
864 : :
865 : : /* If OK to seek, we can determine the length of the last item */
866 [ # # # # ]: 0 : if (prev_te && ctx->hasSeek)
867 : : {
868 : : pgoff_t endpos;
869 : :
870 [ # # ]: 0 : if (fseeko(AH->FH, 0, SEEK_END) != 0)
1247 871 : 0 : pg_fatal("error during file seek: %m");
2549 872 : 0 : endpos = ftello(AH->FH);
873 [ # # ]: 0 : if (endpos > prev_tctx->dataPos)
874 : 0 : prev_te->dataLength = endpos - prev_tctx->dataPos;
875 : : }
876 : 0 : }
877 : :
878 : : /*
879 : : * Clone format-specific fields during parallel restoration.
880 : : */
881 : : static void
5391 heikki.linnakangas@i 882 : 0 : _Clone(ArchiveHandle *AH)
883 : : {
884 : 0 : lclContext *ctx = (lclContext *) AH->formatData;
885 : :
886 : : /*
887 : : * Each thread must have private lclContext working state.
888 : : */
5034 bruce@momjian.us 889 : 0 : AH->formatData = (lclContext *) pg_malloc(sizeof(lclContext));
5391 heikki.linnakangas@i 890 : 0 : memcpy(AH->formatData, ctx, sizeof(lclContext));
891 : 0 : ctx = (lclContext *) AH->formatData;
892 : :
893 : : /* sanity check, shouldn't happen */
894 [ # # ]: 0 : if (ctx->cs != NULL)
1247 tgl@sss.pgh.pa.us 895 : 0 : pg_fatal("compressor active");
896 : :
897 : : /*
898 : : * We intentionally do not clone TOC-entry-local state: it's useful to
899 : : * share knowledge about where the data blocks are across threads.
900 : : * _PrintTocData has to be careful about the order of operations on that
901 : : * state, though.
902 : : */
5391 heikki.linnakangas@i 903 : 0 : }
904 : :
905 : : static void
906 : 0 : _DeClone(ArchiveHandle *AH)
907 : : {
908 : 0 : lclContext *ctx = (lclContext *) AH->formatData;
909 : :
910 : 0 : free(ctx);
911 : 0 : }
912 : :
913 : : /*
914 : : * This function is executed in the child of a parallel restore from a
915 : : * custom-format archive and restores the actual data for one TOC entry.
916 : : */
917 : : static int
3266 tgl@sss.pgh.pa.us 918 : 0 : _WorkerJobRestoreCustom(ArchiveHandle *AH, TocEntry *te)
919 : : {
920 : 0 : return parallel_restore(AH, te);
921 : : }
922 : :
923 : : /*--------------------------------------------------
924 : : * END OF FORMAT CALLBACKS
925 : : *--------------------------------------------------
926 : : */
927 : :
928 : : /*
929 : : * Get the current position in the archive file.
930 : : *
931 : : * With a non-seekable archive file, we may not be able to obtain the
932 : : * file position. If so, just return -1. It's not too important in
933 : : * that case because we won't be able to rewrite the TOC to fill in
934 : : * data block offsets anyway.
935 : : */
936 : : static pgoff_t
8934 bruce@momjian.us 937 :CBC 405 : _getFilePos(ArchiveHandle *AH, lclContext *ctx)
938 : : {
939 : : pgoff_t pos;
940 : :
1877 tgl@sss.pgh.pa.us 941 : 405 : pos = ftello(AH->FH);
942 [ - + ]: 405 : if (pos < 0)
943 : : {
944 : : /* Not expected if we found we can seek. */
1877 tgl@sss.pgh.pa.us 945 [ # # ]:UBC 0 : if (ctx->hasSeek)
1247 946 : 0 : pg_fatal("could not determine seek position in archive file: %m");
947 : : }
8934 bruce@momjian.us 948 :CBC 405 : return pos;
949 : : }
950 : :
951 : : /*
952 : : * Read a data block header. The format changed in V1.3, so we
953 : : * centralize the code here for simplicity. Returns *type = EOF
954 : : * if at EOF.
955 : : */
956 : : static void
957 : 226 : _readBlockHeader(ArchiveHandle *AH, int *type, int *id)
958 : : {
959 : : int byt;
960 : :
961 : : /*
962 : : * Note: if we are at EOF with a pre-1.3 input file, we'll pg_fatal()
963 : : * inside ReadInt rather than returning EOF. It doesn't seem worth
964 : : * jumping through hoops to deal with that case better, because no such
965 : : * files are likely to exist in the wild: only some 7.1 development
966 : : * versions of pg_dump ever generated such files.
967 : : */
968 [ - + ]: 226 : if (AH->version < K_VERS_1_3)
9178 pjw@rhyme.com.au 969 :UBC 0 : *type = BLK_DATA;
970 : : else
971 : : {
5550 tgl@sss.pgh.pa.us 972 :CBC 226 : byt = getc(AH->FH);
973 : 226 : *type = byt;
974 [ - + ]: 226 : if (byt == EOF)
975 : : {
5550 tgl@sss.pgh.pa.us 976 :UBC 0 : *id = 0; /* don't return an uninitialized value */
977 : 0 : return;
978 : : }
979 : : }
980 : :
8934 bruce@momjian.us 981 :CBC 226 : *id = ReadInt(AH);
982 : : }
983 : :
984 : : /*
985 : : * Callback function for writeData. Writes one block of (compressed)
986 : : * data to the archive.
987 : : */
988 : : static void
5392 heikki.linnakangas@i 989 : 459 : _CustomWriteFunc(ArchiveHandle *AH, const char *buf, size_t len)
990 : : {
991 : : /* never write 0-byte blocks (this should not happen) */
4142 bruce@momjian.us 992 [ + + ]: 459 : if (len > 0)
993 : : {
994 : 287 : WriteInt(AH, len);
995 : 287 : _WriteBuf(AH, buf, len);
996 : : }
9178 pjw@rhyme.com.au 997 : 459 : }
998 : :
999 : : /*
1000 : : * Callback function for readData. To keep things simple, we
1001 : : * always read one compressed block at a time.
1002 : : */
1003 : : static size_t
5392 heikki.linnakangas@i 1004 : 494 : _CustomReadFunc(ArchiveHandle *AH, char **buf, size_t *buflen)
1005 : : {
1006 : : size_t blkLen;
1007 : :
1008 : : /* Read length */
1009 : 494 : blkLen = ReadInt(AH);
1010 [ + + ]: 494 : if (blkLen == 0)
1011 : 226 : return 0;
1012 : :
1013 : : /* If the caller's buffer is not large enough, allocate a bigger one */
1014 [ + + ]: 268 : if (blkLen > *buflen)
1015 : : {
1016 : 1 : free(*buf);
5034 bruce@momjian.us 1017 : 1 : *buf = (char *) pg_malloc(blkLen);
5392 heikki.linnakangas@i 1018 : 1 : *buflen = blkLen;
1019 : : }
1020 : :
1021 : : /* exits app on read errors */
4142 bruce@momjian.us 1022 : 268 : _ReadBuf(AH, *buf, blkLen);
1023 : :
1024 : 268 : return blkLen;
1025 : : }
|