Skip to content

Commit 2cd8691

Browse files
author
Commitfest Bot
committed
[CF 4997] Return pg_control from pg_backup_stop().
This branch was automatically generated by a robot using patches from an email thread registered at: https://commitfest.postgresql.org/patch/4997 The branch will be overwritten each time a new patch version is posted to the thread, and also periodically to check for bitrot caused by changes on the master branch. Patch(es): https://www.postgresql.org/message-id/[email protected] Author(s): David Steele
2 parents 2648eab + bf3858c commit 2cd8691

File tree

16 files changed

+178
-28
lines changed

16 files changed

+178
-28
lines changed

doc/src/sgml/backup.sgml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,9 +1027,12 @@ SELECT * FROM pg_backup_stop(wait_for_archive => true);
10271027
values. The second of these fields should be written to a file named
10281028
<filename>backup_label</filename> in the root directory of the backup. The
10291029
third field should be written to a file named
1030-
<filename>tablespace_map</filename> unless the field is empty. These files are
1030+
<filename>tablespace_map</filename> unless the field is empty. The fourth
1031+
field should be written into a file named
1032+
<filename>global/pg_control</filename> (overwriting the existing file when
1033+
present). These files are
10311034
vital to the backup working and must be written byte for byte without
1032-
modification, which may require opening the file in binary mode.
1035+
modification, which will require opening the file in binary mode.
10331036
</para>
10341037
</listitem>
10351038
<listitem>
@@ -1101,7 +1104,16 @@ SELECT * FROM pg_backup_stop(wait_for_archive => true);
11011104
</para>
11021105

11031106
<para>
1104-
You should, however, omit from the backup the files within the
1107+
You should exclude <filename>global/pg_control</filename> from your backup
1108+
and put the contents of the <parameter>controlfile</parameter> column
1109+
returned from <function>pg_backup_stop</function> in your backup at
1110+
<filename>global/pg_control</filename>. This version of pg_control contains
1111+
safeguards against recovery without backup_label present and is guaranteed
1112+
not to be torn.
1113+
</para>
1114+
1115+
<para>
1116+
You should also omit from the backup the files within the
11051117
cluster's <filename>pg_wal/</filename> subdirectory. This
11061118
slight adjustment is worthwhile because it reduces the risk
11071119
of mistakes when restoring. This is easy to arrange if

doc/src/sgml/func/func-info.sgml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3598,6 +3598,11 @@ acl | {postgres=arwdDxtm/postgres,foo=r/postgres}
35983598
<entry><type>boolean</type></entry>
35993599
</row>
36003600

3601+
<row>
3602+
<entry><structfield>backup_label_required</structfield></entry>
3603+
<entry><type>boolean</type></entry>
3604+
</row>
3605+
36013606
</tbody>
36023607
</tgroup>
36033608
</table>

src/backend/access/transam/xlog.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9459,6 +9459,29 @@ do_pg_abort_backup(int code, Datum arg)
94599459
}
94609460
}
94619461

9462+
/*
9463+
* Create a consistent copy of control data to be used for backup and update it
9464+
* to require a backup label for recovery. Also recalculate the CRC.
9465+
*/
9466+
void
9467+
backup_control_file(uint8_t *controlFile)
9468+
{
9469+
ControlFileData *controlData = ((ControlFileData *)controlFile);
9470+
9471+
memset(controlFile + sizeof(ControlFileData), 0,
9472+
PG_CONTROL_FILE_SIZE - sizeof(ControlFileData));
9473+
9474+
LWLockAcquire(ControlFileLock, LW_SHARED);
9475+
memcpy(controlFile, ControlFile, sizeof(ControlFileData));
9476+
LWLockRelease(ControlFileLock);
9477+
9478+
controlData->backupLabelRequired = true;
9479+
9480+
INIT_CRC32C(controlData->crc);
9481+
COMP_CRC32C(controlData->crc, controlFile, offsetof(ControlFileData, crc));
9482+
FIN_CRC32C(controlData->crc);
9483+
}
9484+
94629485
/*
94639486
* Register a handler that will warn about unterminated backups at end of
94649487
* session, unless this has already been done.

src/backend/access/transam/xlogfuncs.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,22 +113,26 @@ pg_backup_start(PG_FUNCTION_ARGS)
113113
*
114114
* The backup_label contains the user-supplied label string (typically this
115115
* would be used to tell where the backup dump will be stored), the starting
116-
* time, starting WAL location for the dump and so on. It is the caller's
117-
* responsibility to write the backup_label and tablespace_map files in the
118-
* data folder that will be restored from this backup.
116+
* time, starting WAL location for the dump and so on. The pg_control file
117+
* contains a consistent copy of pg_control that also has a safeguard against
118+
* being used without backup_label. It is the caller's responsibility to write
119+
* the backup_label, pg_control, and tablespace_map files in the data folder
120+
* that will be restored from this backup.
119121
*
120122
* Permission checking for this function is managed through the normal
121123
* GRANT system.
122124
*/
123125
Datum
124126
pg_backup_stop(PG_FUNCTION_ARGS)
125127
{
126-
#define PG_BACKUP_STOP_V2_COLS 3
128+
#define PG_BACKUP_STOP_V2_COLS 4
127129
TupleDesc tupdesc;
128130
Datum values[PG_BACKUP_STOP_V2_COLS] = {0};
129131
bool nulls[PG_BACKUP_STOP_V2_COLS] = {0};
130132
bool waitforarchive = PG_GETARG_BOOL(0);
131133
char *backup_label;
134+
bytea *pg_control_bytea;
135+
uint8_t pg_control[PG_CONTROL_FILE_SIZE];
132136
SessionBackupState status = get_backup_status();
133137

134138
/* Initialize attributes information in the tuple descriptor */
@@ -150,9 +154,17 @@ pg_backup_stop(PG_FUNCTION_ARGS)
150154
/* Build the contents of backup_label */
151155
backup_label = build_backup_content(backup_state, false);
152156

157+
/* Build the contents of pg_control */
158+
backup_control_file(pg_control);
159+
160+
pg_control_bytea = (bytea *) palloc(PG_CONTROL_FILE_SIZE + VARHDRSZ);
161+
SET_VARSIZE(pg_control_bytea, PG_CONTROL_FILE_SIZE + VARHDRSZ);
162+
memcpy(VARDATA(pg_control_bytea), pg_control, PG_CONTROL_FILE_SIZE);
163+
153164
values[0] = LSNGetDatum(backup_state->stoppoint);
154165
values[1] = CStringGetTextDatum(backup_label);
155166
values[2] = CStringGetTextDatum(tablespace_map->data);
167+
values[3] = PointerGetDatum(pg_control_bytea);
156168

157169
/* Deallocate backup-related variables */
158170
pfree(backup_label);

src/backend/access/transam/xlogrecovery.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -710,7 +710,14 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
710710
}
711711
else
712712
{
713-
/* No backup_label file has been found if we are here. */
713+
/*
714+
* No backup_label file has been found if we are here. Error if the
715+
* control file requires backup_label.
716+
*/
717+
if (ControlFile->backupLabelRequired)
718+
ereport(FATAL,
719+
(errmsg("could not find backup_label required for recovery"),
720+
errhint("backup_label must be present for recovery to proceed")));
714721

715722
/*
716723
* If tablespace_map file is present without backup_label file, there
@@ -985,6 +992,7 @@ InitWalRecovery(ControlFileData *ControlFile, bool *wasShutdown_ptr,
985992
{
986993
ControlFile->backupStartPoint = checkPoint.redo;
987994
ControlFile->backupEndRequired = backupEndRequired;
995+
ControlFile->backupLabelRequired = false;
988996

989997
if (backupFromStandby)
990998
{

src/backend/backup/basebackup.c

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include "backup/basebackup_incremental.h"
2424
#include "backup/basebackup_sink.h"
2525
#include "backup/basebackup_target.h"
26+
#include "catalog/pg_control.h"
2627
#include "catalog/pg_tablespace_d.h"
2728
#include "commands/defrem.h"
2829
#include "common/compression.h"
@@ -326,9 +327,9 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
326327

327328
if (ti->path == NULL)
328329
{
329-
struct stat statbuf;
330330
bool sendtblspclinks = true;
331331
char *backup_label;
332+
uint8_t controlFile[PG_CONTROL_FILE_SIZE];
332333

333334
bbsink_begin_archive(sink, "base.tar");
334335

@@ -351,14 +352,10 @@ perform_base_backup(basebackup_options *opt, bbsink *sink,
351352
sendtblspclinks, &manifest, InvalidOid, ib);
352353

353354
/* ... and pg_control after everything else. */
354-
if (lstat(XLOG_CONTROL_FILE, &statbuf) != 0)
355-
ereport(ERROR,
356-
(errcode_for_file_access(),
357-
errmsg("could not stat file \"%s\": %m",
358-
XLOG_CONTROL_FILE)));
359-
sendFile(sink, XLOG_CONTROL_FILE, XLOG_CONTROL_FILE, &statbuf,
360-
false, InvalidOid, InvalidOid,
361-
InvalidRelFileNumber, 0, &manifest, 0, NULL, 0);
355+
backup_control_file(controlFile);
356+
sendFileWithContent(sink, XLOG_CONTROL_FILE,
357+
(char *)controlFile, PG_CONTROL_FILE_SIZE,
358+
&manifest);
362359
}
363360
else
364361
{

src/backend/catalog/system_functions.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -390,7 +390,7 @@ CREATE OR REPLACE FUNCTION
390390

391391
CREATE OR REPLACE FUNCTION pg_backup_stop (
392392
wait_for_archive boolean DEFAULT true, OUT lsn pg_lsn,
393-
OUT labelfile text, OUT spcmapfile text)
393+
OUT labelfile text, OUT spcmapfile text, OUT controlfile bytea)
394394
RETURNS record STRICT VOLATILE LANGUAGE internal as 'pg_backup_stop'
395395
PARALLEL RESTRICTED;
396396

src/backend/utils/misc/pg_controldata.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,8 +162,8 @@ pg_control_checkpoint(PG_FUNCTION_ARGS)
162162
Datum
163163
pg_control_recovery(PG_FUNCTION_ARGS)
164164
{
165-
Datum values[5];
166-
bool nulls[5];
165+
Datum values[6];
166+
bool nulls[6];
167167
TupleDesc tupdesc;
168168
HeapTuple htup;
169169
ControlFileData *ControlFile;
@@ -195,6 +195,9 @@ pg_control_recovery(PG_FUNCTION_ARGS)
195195
values[4] = BoolGetDatum(ControlFile->backupEndRequired);
196196
nulls[4] = false;
197197

198+
values[5] = BoolGetDatum(ControlFile->backupLabelRequired);
199+
nulls[5] = false;
200+
198201
htup = heap_form_tuple(tupdesc, values, nulls);
199202

200203
PG_RETURN_DATUM(HeapTupleGetDatum(htup));

src/bin/pg_controldata/pg_controldata.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,8 @@ main(int argc, char *argv[])
294294
LSN_FORMAT_ARGS(ControlFile->backupEndPoint));
295295
printf(_("End-of-backup record required: %s\n"),
296296
ControlFile->backupEndRequired ? _("yes") : _("no"));
297+
printf(_("Backup label required: %s\n"),
298+
ControlFile->backupLabelRequired ? _("yes") : _("no"));
297299
printf(_("wal_level setting: %s\n"),
298300
wal_level_str(ControlFile->wal_level));
299301
printf(_("wal_log_hints setting: %s\n"),

src/bin/pg_resetwal/pg_resetwal.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -883,6 +883,7 @@ RewriteControlFile(void)
883883
ControlFile.backupStartPoint = 0;
884884
ControlFile.backupEndPoint = 0;
885885
ControlFile.backupEndRequired = false;
886+
ControlFile.backupLabelRequired = false;
886887

887888
/*
888889
* Force the defaults for max_* settings. The values don't really matter

0 commit comments

Comments
 (0)