Refactor creation of backup_label and backup history files

This change simplifies some of the logic related to the generation and
creation of the backup_label and backup history files, which has become
unnecessarily complicated since the removal of the exclusive backup mode
in commit 39969e2.  The code was previously generating the contents of
these files as a string (start phase for the backup_label and stop phase
for the backup history file), one problem being that the contents of the
backup_label string were scanned to grab some of its internal contents
at the stop phase.

This commit changes the logic so as we store the data required to build
these files in an intermediate structure named BackupState.  The
backup_label file and backup history file strings are generated when
they are ready to be sent back to the client.  Both files are now
generated with the same code path.  While on it, this commit renames
some variables for clarity.

Two new files named xlogbackup.{c,h} are introduced in this commit, to
remove from xlog.c some of the logic around base backups.  Note that
more could be moved to this new set of files.

Author: Bharath Rupireddy, Michael Paquier
Reviewed-by: Fujii Masao
Discussion: https://postgr.es/m/CALj2ACXWwTDgJqCjdaPyfR7djwm6SrybGcrZyrvojzcsmt4FFw@mail.gmail.com
This commit is contained in:
Michael Paquier 2022-09-26 11:15:47 +09:00
parent 216f9c1ab3
commit 7d708093b7
9 changed files with 290 additions and 200 deletions

View File

@ -29,6 +29,7 @@ OBJS = \
xact.o \
xlog.o \
xlogarchive.o \
xlogbackup.o \
xlogfuncs.o \
xloginsert.o \
xlogprefetcher.o \

View File

@ -15,6 +15,7 @@ backend_sources += files(
'xact.c',
'xlog.c',
'xlogarchive.c',
'xlogbackup.c',
'xlogfuncs.c',
'xloginsert.c',
'xlogprefetcher.c',

View File

@ -8242,24 +8242,23 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
/*
* do_pg_backup_start is the workhorse of the user-visible pg_backup_start()
* function. It creates the necessary starting checkpoint and constructs the
* backup label and tablespace map.
* backup state and tablespace map.
*
* Input parameters are "backupidstr" (the backup label string) and "fast"
* (if true, we do the checkpoint in immediate mode to make it faster).
* Input parameters are "state" (the backup state), "fast" (if true, we do
* the checkpoint in immediate mode to make it faster), and "tablespaces"
* (if non-NULL, indicates a list of tablespaceinfo structs describing the
* cluster's tablespaces.).
*
* The backup label and tablespace map contents are appended to *labelfile and
* *tblspcmapfile, and the caller is responsible for including them in the
* backup archive as 'backup_label' and 'tablespace_map'.
* tblspcmapfile is required mainly for tar format in windows as native windows
* utilities are not able to create symlinks while extracting files from tar.
* However for consistency and platform-independence, we do it the same way
* everywhere.
* The tablespace map contents are appended to passed-in parameter
* tablespace_map and the caller is responsible for including it in the backup
* archive as 'tablespace_map'. The tablespace_map file is required mainly for
* tar format in windows as native windows utilities are not able to create
* symlinks while extracting files from tar. However for consistency and
* platform-independence, we do it the same way everywhere.
*
* If "tablespaces" isn't NULL, it receives a list of tablespaceinfo structs
* describing the cluster's tablespaces.
*
* Returns the minimum WAL location that must be present to restore from this
* backup, and the corresponding timeline ID in *starttli_p.
* It fills in backup_state with the information required for the backup,
* such as the minimum WAL location that must be present to restore from
* this backup (starttli) and the corresponding timeline ID (starttli).
*
* Every successfully started backup must be stopped by calling
* do_pg_backup_stop() or do_pg_abort_backup(). There can be many
@ -8268,20 +8267,13 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
* It is the responsibility of the caller of this function to verify the
* permissions of the calling user!
*/
XLogRecPtr
do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
StringInfo labelfile, List **tablespaces,
StringInfo tblspcmapfile)
void
do_pg_backup_start(const char *backupidstr, bool fast, List **tablespaces,
BackupState *state, StringInfo tblspcmapfile)
{
bool backup_started_in_recovery = false;
XLogRecPtr checkpointloc;
XLogRecPtr startpoint;
TimeLineID starttli;
pg_time_t stamp_time;
char strfbuf[128];
char xlogfilename[MAXFNAMELEN];
XLogSegNo _logSegNo;
Assert(state != NULL);
backup_started_in_recovery = RecoveryInProgress();
/*
@ -8300,6 +8292,8 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
errmsg("backup label too long (max %d bytes)",
MAXPGPATH)));
memcpy(state->name, backupidstr, strlen(backupidstr));
/*
* Mark backup active in shared memory. We must do full-page WAL writes
* during an on-line backup even if not doing so at other times, because
@ -8391,9 +8385,9 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
* pointer.
*/
LWLockAcquire(ControlFileLock, LW_SHARED);
checkpointloc = ControlFile->checkPoint;
startpoint = ControlFile->checkPointCopy.redo;
starttli = ControlFile->checkPointCopy.ThisTimeLineID;
state->checkpointloc = ControlFile->checkPoint;
state->startpoint = ControlFile->checkPointCopy.redo;
state->starttli = ControlFile->checkPointCopy.ThisTimeLineID;
checkpointfpw = ControlFile->checkPointCopy.fullPageWrites;
LWLockRelease(ControlFileLock);
@ -8410,7 +8404,7 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
recptr = XLogCtl->lastFpwDisableRecPtr;
SpinLockRelease(&XLogCtl->info_lck);
if (!checkpointfpw || startpoint <= recptr)
if (!checkpointfpw || state->startpoint <= recptr)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("WAL generated with full_page_writes=off was replayed "
@ -8442,17 +8436,14 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
* either because only few buffers have been dirtied yet.
*/
WALInsertLockAcquireExclusive();
if (XLogCtl->Insert.lastBackupStart < startpoint)
if (XLogCtl->Insert.lastBackupStart < state->startpoint)
{
XLogCtl->Insert.lastBackupStart = startpoint;
XLogCtl->Insert.lastBackupStart = state->startpoint;
gotUniqueStartpoint = true;
}
WALInsertLockRelease();
} while (!gotUniqueStartpoint);
XLByteToSeg(startpoint, _logSegNo, wal_segment_size);
XLogFileName(xlogfilename, starttli, _logSegNo, wal_segment_size);
/*
* Construct tablespace_map file.
*/
@ -8538,39 +8529,16 @@ do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
}
FreeDir(tblspcdir);
/*
* Construct backup label file.
*/
/* Use the log timezone here, not the session timezone */
stamp_time = (pg_time_t) time(NULL);
pg_strftime(strfbuf, sizeof(strfbuf),
"%Y-%m-%d %H:%M:%S %Z",
pg_localtime(&stamp_time, log_timezone));
appendStringInfo(labelfile, "START WAL LOCATION: %X/%X (file %s)\n",
LSN_FORMAT_ARGS(startpoint), xlogfilename);
appendStringInfo(labelfile, "CHECKPOINT LOCATION: %X/%X\n",
LSN_FORMAT_ARGS(checkpointloc));
appendStringInfo(labelfile, "BACKUP METHOD: streamed\n");
appendStringInfo(labelfile, "BACKUP FROM: %s\n",
backup_started_in_recovery ? "standby" : "primary");
appendStringInfo(labelfile, "START TIME: %s\n", strfbuf);
appendStringInfo(labelfile, "LABEL: %s\n", backupidstr);
appendStringInfo(labelfile, "START TIMELINE: %u\n", starttli);
state->starttime = (pg_time_t) time(NULL);
}
PG_END_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0);
state->started_in_recovery = backup_started_in_recovery;
/*
* Mark that the start phase has correctly finished for the backup.
*/
sessionBackupState = SESSION_BACKUP_RUNNING;
/*
* We're done. As a convenience, return the starting WAL location.
*/
if (starttli_p)
*starttli_p = starttli;
return startpoint;
}
/* Error cleanup callback for pg_backup_start */
@ -8602,48 +8570,38 @@ get_backup_status(void)
/*
* do_pg_backup_stop
*
* Utility function called at the end of an online backup. It cleans up the
* backup state and can optionally wait for WAL segments to be archived.
* Utility function called at the end of an online backup. It creates history
* file (if required), resets sessionBackupState and so on. It can optionally
* wait for WAL segments to be archived.
*
* Returns the last WAL location that must be present to restore from this
* backup, and the corresponding timeline ID in *stoptli_p.
* backup_state is filled with the information necessary to restore from this
* backup with its stop LSN (stoppoint), its timeline ID (stoptli), etc.
*
* It is the responsibility of the caller of this function to verify the
* permissions of the calling user!
*/
XLogRecPtr
do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
void
do_pg_backup_stop(BackupState *state, bool waitforarchive)
{
bool backup_started_in_recovery = false;
XLogRecPtr startpoint;
XLogRecPtr stoppoint;
TimeLineID stoptli;
pg_time_t stamp_time;
char strfbuf[128];
bool backup_stopped_in_recovery = false;
char histfilepath[MAXPGPATH];
char startxlogfilename[MAXFNAMELEN];
char stopxlogfilename[MAXFNAMELEN];
char lastxlogfilename[MAXFNAMELEN];
char histfilename[MAXFNAMELEN];
char backupfrom[20];
XLogSegNo _logSegNo;
FILE *fp;
char ch;
int seconds_before_warning;
int waits = 0;
bool reported_waiting = false;
char *remaining;
char *ptr;
uint32 hi,
lo;
backup_started_in_recovery = RecoveryInProgress();
Assert(state != NULL);
backup_stopped_in_recovery = RecoveryInProgress();
/*
* During recovery, we don't need to check WAL level. Because, if WAL
* level is not sufficient, it's impossible to get here during recovery.
*/
if (!backup_started_in_recovery && !XLogIsNeeded())
if (!backup_stopped_in_recovery && !XLogIsNeeded())
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("WAL level not sufficient for making an online backup"),
@ -8684,29 +8642,10 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
WALInsertLockRelease();
/*
* Read and parse the START WAL LOCATION line (this code is pretty crude,
* but we are not expecting any variability in the file format).
* If we are taking an online backup from the standby, we confirm that the
* standby has not been promoted during the backup.
*/
if (sscanf(labelfile, "START WAL LOCATION: %X/%X (file %24s)%c",
&hi, &lo, startxlogfilename,
&ch) != 4 || ch != '\n')
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
startpoint = ((uint64) hi) << 32 | lo;
remaining = strchr(labelfile, '\n') + 1; /* %n is not portable enough */
/*
* Parse the BACKUP FROM line. If we are taking an online backup from the
* standby, we confirm that the standby has not been promoted during the
* backup.
*/
ptr = strstr(remaining, "BACKUP FROM:");
if (!ptr || sscanf(ptr, "BACKUP FROM: %19s\n", backupfrom) != 1)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
if (strcmp(backupfrom, "standby") == 0 && !backup_started_in_recovery)
if (state->started_in_recovery && !backup_stopped_in_recovery)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("the standby was promoted during online backup"),
@ -8742,7 +8681,7 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
* an archiver is not invoked. So it doesn't seem worthwhile to write a
* backup history file during recovery.
*/
if (backup_started_in_recovery)
if (backup_stopped_in_recovery)
{
XLogRecPtr recptr;
@ -8754,7 +8693,7 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
recptr = XLogCtl->lastFpwDisableRecPtr;
SpinLockRelease(&XLogCtl->info_lck);
if (startpoint <= recptr)
if (state->startpoint <= recptr)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("WAL generated with full_page_writes=off was replayed "
@ -8766,24 +8705,27 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
LWLockAcquire(ControlFileLock, LW_SHARED);
stoppoint = ControlFile->minRecoveryPoint;
stoptli = ControlFile->minRecoveryPointTLI;
state->stoppoint = ControlFile->minRecoveryPoint;
state->stoptli = ControlFile->minRecoveryPointTLI;
LWLockRelease(ControlFileLock);
}
else
{
StringInfo history_file;
/*
* Write the backup-end xlog record
*/
XLogBeginInsert();
XLogRegisterData((char *) (&startpoint), sizeof(startpoint));
stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END);
XLogRegisterData((char *) (&state->startpoint),
sizeof(state->startpoint));
state->stoppoint = XLogInsert(RM_XLOG_ID, XLOG_BACKUP_END);
/*
* Given that we're not in recovery, InsertTimeLineID is set and can't
* change, so we can read it without a lock.
*/
stoptli = XLogCtl->InsertTimeLineID;
state->stoptli = XLogCtl->InsertTimeLineID;
/*
* Force a switch to a new xlog segment file, so that the backup is
@ -8791,39 +8733,28 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
*/
RequestXLogSwitch(false);
XLByteToPrevSeg(stoppoint, _logSegNo, wal_segment_size);
XLogFileName(stopxlogfilename, stoptli, _logSegNo, wal_segment_size);
/* Use the log timezone here, not the session timezone */
stamp_time = (pg_time_t) time(NULL);
pg_strftime(strfbuf, sizeof(strfbuf),
"%Y-%m-%d %H:%M:%S %Z",
pg_localtime(&stamp_time, log_timezone));
XLByteToPrevSeg(state->stoppoint, _logSegNo, wal_segment_size);
state->stoptime = (pg_time_t) time(NULL);
/*
* Write the backup history file
*/
XLByteToSeg(startpoint, _logSegNo, wal_segment_size);
BackupHistoryFilePath(histfilepath, stoptli, _logSegNo,
startpoint, wal_segment_size);
XLByteToSeg(state->startpoint, _logSegNo, wal_segment_size);
BackupHistoryFilePath(histfilepath, state->stoptli, _logSegNo,
state->startpoint, wal_segment_size);
fp = AllocateFile(histfilepath, "w");
if (!fp)
ereport(ERROR,
(errcode_for_file_access(),
errmsg("could not create file \"%s\": %m",
histfilepath)));
fprintf(fp, "START WAL LOCATION: %X/%X (file %s)\n",
LSN_FORMAT_ARGS(startpoint), startxlogfilename);
fprintf(fp, "STOP WAL LOCATION: %X/%X (file %s)\n",
LSN_FORMAT_ARGS(stoppoint), stopxlogfilename);
/*
* Transfer remaining lines including label and start timeline to
* history file.
*/
fprintf(fp, "%s", remaining);
fprintf(fp, "STOP TIME: %s\n", strfbuf);
fprintf(fp, "STOP TIMELINE: %u\n", stoptli);
/* Build and save the contents of the backup history file */
history_file = build_backup_content(state, true);
fprintf(fp, "%s", history_file->data);
pfree(history_file->data);
pfree(history_file);
if (fflush(fp) || ferror(fp) || FreeFile(fp))
ereport(ERROR,
(errcode_for_file_access(),
@ -8861,15 +8792,16 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
*/
if (waitforarchive &&
((!backup_started_in_recovery && XLogArchivingActive()) ||
(backup_started_in_recovery && XLogArchivingAlways())))
((!backup_stopped_in_recovery && XLogArchivingActive()) ||
(backup_stopped_in_recovery && XLogArchivingAlways())))
{
XLByteToPrevSeg(stoppoint, _logSegNo, wal_segment_size);
XLogFileName(lastxlogfilename, stoptli, _logSegNo, wal_segment_size);
XLByteToPrevSeg(state->stoppoint, _logSegNo, wal_segment_size);
XLogFileName(lastxlogfilename, state->stoptli, _logSegNo,
wal_segment_size);
XLByteToSeg(startpoint, _logSegNo, wal_segment_size);
BackupHistoryFileName(histfilename, stoptli, _logSegNo,
startpoint, wal_segment_size);
XLByteToSeg(state->startpoint, _logSegNo, wal_segment_size);
BackupHistoryFileName(histfilename, state->stoptli, _logSegNo,
state->startpoint, wal_segment_size);
seconds_before_warning = 60;
waits = 0;
@ -8910,13 +8842,6 @@ do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
else if (waitforarchive)
ereport(NOTICE,
(errmsg("WAL archiving is not enabled; you must ensure that all required WAL segments are copied through other means to complete the backup")));
/*
* We're done. As a convenience, return the ending WAL location.
*/
if (stoptli_p)
*stoptli_p = stoptli;
return stoppoint;
}

View File

@ -0,0 +1,80 @@
/*-------------------------------------------------------------------------
*
* xlogbackup.c
* Internal routines for base backups.
*
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/backend/access/transam/xlogbackup.c
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/xlog.h"
#include "access/xlog_internal.h"
#include "access/xlogbackup.h"
/*
* Build contents for backup_label or backup history file.
*
* When ishistoryfile is true, it creates the contents for a backup history
* file, otherwise it creates contents for a backup_label file.
*
* Returns the result generated as a palloc'd StringInfo.
*/
StringInfo
build_backup_content(BackupState *state, bool ishistoryfile)
{
char startstrbuf[128];
char startxlogfile[MAXFNAMELEN]; /* backup start WAL file */
XLogSegNo startsegno;
StringInfo result = makeStringInfo();
Assert(state != NULL);
/* Use the log timezone here, not the session timezone */
pg_strftime(startstrbuf, sizeof(startstrbuf), "%Y-%m-%d %H:%M:%S %Z",
pg_localtime(&state->starttime, log_timezone));
XLByteToSeg(state->startpoint, startsegno, wal_segment_size);
XLogFileName(startxlogfile, state->starttli, startsegno, wal_segment_size);
appendStringInfo(result, "START WAL LOCATION: %X/%X (file %s)\n",
LSN_FORMAT_ARGS(state->startpoint), startxlogfile);
if (ishistoryfile)
{
char stopxlogfile[MAXFNAMELEN]; /* backup stop WAL file */
XLogSegNo stopsegno;
XLByteToSeg(state->stoppoint, stopsegno, wal_segment_size);
XLogFileName(stopxlogfile, state->stoptli, stopsegno, wal_segment_size);
appendStringInfo(result, "STOP WAL LOCATION: %X/%X (file %s)\n",
LSN_FORMAT_ARGS(state->stoppoint), stopxlogfile);
}
appendStringInfo(result, "CHECKPOINT LOCATION: %X/%X\n",
LSN_FORMAT_ARGS(state->checkpointloc));
appendStringInfo(result, "BACKUP METHOD: streamed\n");
appendStringInfo(result, "BACKUP FROM: %s\n",
state->started_in_recovery ? "standby" : "primary");
appendStringInfo(result, "START TIME: %s\n", startstrbuf);
appendStringInfo(result, "LABEL: %s\n", state->name);
appendStringInfo(result, "START TIMELINE: %u\n", state->starttli);
if (ishistoryfile)
{
char stopstrfbuf[128];
/* Use the log timezone here, not the session timezone */
pg_strftime(stopstrfbuf, sizeof(stopstrfbuf), "%Y-%m-%d %H:%M:%S %Z",
pg_localtime(&state->stoptime, log_timezone));
appendStringInfo(result, "STOP TIME: %s\n", stopstrfbuf);
appendStringInfo(result, "STOP TIMELINE: %u\n", state->stoptli);
}
return result;
}

View File

@ -20,6 +20,7 @@
#include "access/htup_details.h"
#include "access/xlog_internal.h"
#include "access/xlogbackup.h"
#include "access/xlogrecovery.h"
#include "access/xlogutils.h"
#include "catalog/pg_type.h"
@ -39,19 +40,16 @@
#include "utils/tuplestore.h"
/*
* Store label file and tablespace map during backups.
* Backup-related variables.
*/
static StringInfo label_file;
static StringInfo tblspc_map_file;
static BackupState *backup_state = NULL;
static StringInfo tablespace_map = NULL;
/*
* pg_backup_start: set up for taking an on-line backup dump
*
* Essentially what this does is to create a backup label file in $PGDATA,
* where it will be archived as part of the backup dump. The label file
* contains the user-supplied label string (typically this would be used
* to tell where the backup dump will be stored) and the starting time and
* starting WAL location for the dump.
* Essentially what this does is to create the contents required for the
* backup_label file and the tablespace map.
*
* Permission checking for this function is managed through the normal
* GRANT system.
@ -62,7 +60,6 @@ pg_backup_start(PG_FUNCTION_ARGS)
text *backupid = PG_GETARG_TEXT_PP(0);
bool fast = PG_GETARG_BOOL(1);
char *backupidstr;
XLogRecPtr startpoint;
SessionBackupState status = get_backup_status();
MemoryContext oldcontext;
@ -74,20 +71,35 @@ pg_backup_start(PG_FUNCTION_ARGS)
errmsg("a backup is already in progress in this session")));
/*
* Label file and tablespace map file need to be long-lived, since they
* are read in pg_backup_stop.
* backup_state and tablespace_map need to be long-lived as they are used
* in pg_backup_stop().
*/
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
label_file = makeStringInfo();
tblspc_map_file = makeStringInfo();
/* Allocate backup state or reset it, if it comes from a previous run */
if (backup_state == NULL)
backup_state = (BackupState *) palloc0(sizeof(BackupState));
else
MemSet(backup_state, 0, sizeof(BackupState));
/*
* tablespace_map may have been created in a previous backup, so take this
* occasion to clean it.
*/
if (tablespace_map != NULL)
{
pfree(tablespace_map->data);
pfree(tablespace_map);
tablespace_map = NULL;
}
tablespace_map = makeStringInfo();
MemoryContextSwitchTo(oldcontext);
register_persistent_abort_backup_handler();
do_pg_backup_start(backupidstr, fast, NULL, backup_state, tablespace_map);
startpoint = do_pg_backup_start(backupidstr, fast, NULL, label_file,
NULL, tblspc_map_file);
PG_RETURN_LSN(startpoint);
PG_RETURN_LSN(backup_state->startpoint);
}
@ -98,6 +110,15 @@ pg_backup_start(PG_FUNCTION_ARGS)
* allows the user to choose if they want to wait for the WAL to be archived
* or if we should just return as soon as the WAL record is written.
*
* This function stops an in-progress backup, creates backup_label contents and
* it returns the backup stop LSN, backup_label and tablespace_map contents.
*
* The backup_label contains the user-supplied label string (typically this
* would be used to tell where the backup dump will be stored), the starting
* time, starting WAL location for the dump and so on. It is the caller's
* responsibility to write the backup_label and tablespace_map files in the
* data folder that will be restored from this backup.
*
* Permission checking for this function is managed through the normal
* GRANT system.
*/
@ -108,9 +129,8 @@ pg_backup_stop(PG_FUNCTION_ARGS)
TupleDesc tupdesc;
Datum values[PG_BACKUP_STOP_V2_COLS] = {0};
bool nulls[PG_BACKUP_STOP_V2_COLS] = {0};
bool waitforarchive = PG_GETARG_BOOL(0);
XLogRecPtr stoppoint;
StringInfo backup_label;
SessionBackupState status = get_backup_status();
/* Initialize attributes information in the tuple descriptor */
@ -123,23 +143,27 @@ pg_backup_stop(PG_FUNCTION_ARGS)
errmsg("backup is not in progress"),
errhint("Did you call pg_backup_start()?")));
/*
* Stop the backup. Return a copy of the backup label and tablespace map
* so they can be written to disk by the caller.
*/
stoppoint = do_pg_backup_stop(label_file->data, waitforarchive, NULL);
Assert(backup_state != NULL);
Assert(tablespace_map != NULL);
values[0] = LSNGetDatum(stoppoint);
values[1] = CStringGetTextDatum(label_file->data);
values[2] = CStringGetTextDatum(tblspc_map_file->data);
/* Stop the backup */
do_pg_backup_stop(backup_state, waitforarchive);
/* Free structures allocated in TopMemoryContext */
pfree(label_file->data);
pfree(label_file);
label_file = NULL;
pfree(tblspc_map_file->data);
pfree(tblspc_map_file);
tblspc_map_file = NULL;
/* Build the contents of backup_label */
backup_label = build_backup_content(backup_state, false);
values[0] = LSNGetDatum(backup_state->stoppoint);
values[1] = CStringGetTextDatum(backup_label->data);
values[2] = CStringGetTextDatum(tablespace_map->data);
/* Deallocate backup-related variables */
pfree(backup_state);
backup_state = NULL;
pfree(tablespace_map->data);
pfree(tablespace_map);
tablespace_map = NULL;
pfree(backup_label->data);
pfree(backup_label);
/* Returns the record as Datum */
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));

View File

@ -17,6 +17,7 @@
#include <time.h>
#include "access/xlog_internal.h"
#include "access/xlogbackup.h"
#include "backup/backup_manifest.h"
#include "backup/basebackup.h"
#include "backup/basebackup_sink.h"
@ -231,9 +232,9 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
bbsink_state state;
XLogRecPtr endptr;
TimeLineID endtli;
StringInfo labelfile;
StringInfo tblspc_map_file;
backup_manifest_info manifest;
BackupState *backup_state;
StringInfo tablespace_map;
/* Initial backup state, insofar as we know it now. */
state.tablespaces = NIL;
@ -248,18 +249,21 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
backup_started_in_recovery = RecoveryInProgress();
labelfile = makeStringInfo();
tblspc_map_file = makeStringInfo();
InitializeBackupManifest(&manifest, opt->manifest,
opt->manifest_checksum_type);
total_checksum_failures = 0;
/* Allocate backup related varilables. */
backup_state = (BackupState *) palloc0(sizeof(BackupState));
tablespace_map = makeStringInfo();
basebackup_progress_wait_checkpoint();
state.startptr = do_pg_backup_start(opt->label, opt->fastcheckpoint,
&state.starttli,
labelfile, &state.tablespaces,
tblspc_map_file);
do_pg_backup_start(opt->label, opt->fastcheckpoint, &state.tablespaces,
backup_state, tablespace_map);
state.startptr = backup_state->startpoint;
state.starttli = backup_state->starttli;
/*
* Once do_pg_backup_start has been called, ensure that any failure causes
@ -313,18 +317,22 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
{
struct stat statbuf;
bool sendtblspclinks = true;
StringInfo backup_label;
bbsink_begin_archive(sink, "base.tar");
/* In the main tar, include the backup_label first... */
sendFileWithContent(sink, BACKUP_LABEL_FILE, labelfile->data,
&manifest);
backup_label = build_backup_content(backup_state, false);
sendFileWithContent(sink, BACKUP_LABEL_FILE,
backup_label->data, &manifest);
pfree(backup_label->data);
pfree(backup_label);
/* Then the tablespace_map file, if required... */
if (opt->sendtblspcmapfile)
{
sendFileWithContent(sink, TABLESPACE_MAP, tblspc_map_file->data,
&manifest);
sendFileWithContent(sink, TABLESPACE_MAP,
tablespace_map->data, &manifest);
sendtblspclinks = false;
}
@ -374,7 +382,15 @@ perform_base_backup(basebackup_options *opt, bbsink *sink)
}
basebackup_progress_wait_wal_archive(&state);
endptr = do_pg_backup_stop(labelfile->data, !opt->nowait, &endtli);
do_pg_backup_stop(backup_state, !opt->nowait);
endptr = backup_state->stoppoint;
endtli = backup_state->stoptli;
/* Deallocate backup-related variables. */
pfree(tablespace_map->data);
pfree(tablespace_map);
pfree(backup_state);
}
PG_END_ENSURE_ERROR_CLEANUP(do_pg_abort_backup, BoolGetDatum(false));

View File

@ -11,6 +11,7 @@
#ifndef XLOG_H
#define XLOG_H
#include "access/xlogbackup.h"
#include "access/xlogdefs.h"
#include "access/xlogreader.h"
#include "datatype/timestamp.h"
@ -277,11 +278,10 @@ typedef enum SessionBackupState
SESSION_BACKUP_RUNNING,
} SessionBackupState;
extern XLogRecPtr do_pg_backup_start(const char *backupidstr, bool fast,
TimeLineID *starttli_p, StringInfo labelfile,
List **tablespaces, StringInfo tblspcmapfile);
extern XLogRecPtr do_pg_backup_stop(char *labelfile, bool waitforarchive,
TimeLineID *stoptli_p);
extern void do_pg_backup_start(const char *backupidstr, bool fast,
List **tablespaces, BackupState *state,
StringInfo tblspcmapfile);
extern void do_pg_backup_stop(BackupState *state, bool waitforarchive);
extern void do_pg_abort_backup(int code, Datum arg);
extern void register_persistent_abort_backup_handler(void);
extern SessionBackupState get_backup_status(void);

View File

@ -0,0 +1,42 @@
/*-------------------------------------------------------------------------
*
* xlogbackup.h
* Definitions for internals of base backups.
*
* Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* IDENTIFICATION
* src/include/access/xlogbackup.h
*-------------------------------------------------------------------------
*/
#ifndef XLOG_BACKUP_H
#define XLOG_BACKUP_H
#include "access/xlogdefs.h"
#include "lib/stringinfo.h"
#include "pgtime.h"
/* Structure to hold backup state. */
typedef struct BackupState
{
/* Fields saved at backup start */
/* Backup label name one extra byte for null-termination */
char name[MAXPGPATH + 1];
XLogRecPtr startpoint; /* backup start WAL location */
TimeLineID starttli; /* backup start TLI */
XLogRecPtr checkpointloc; /* last checkpoint location */
pg_time_t starttime; /* backup start time */
bool started_in_recovery; /* backup started in recovery? */
/* Fields saved at the end of backup */
XLogRecPtr stoppoint; /* backup stop WAL location */
TimeLineID stoptli; /* backup stop TLI */
pg_time_t stoptime; /* backup stop time */
} BackupState;
extern StringInfo build_backup_content(BackupState *state,
bool ishistoryfile);
#endif /* XLOG_BACKUP_H */

View File

@ -233,6 +233,7 @@ BackgroundWorker
BackgroundWorkerArray
BackgroundWorkerHandle
BackgroundWorkerSlot
BackupState
Barrier
BaseBackupCmd
BaseBackupTargetHandle