mirror of
https://github.com/postgres/postgres.git
synced 2025-11-09 06:21:09 +03:00
Remove exclusive backup mode
Exclusive-mode backups have been deprecated since 9.6 (when non-exclusive backups were introduced) due to the issues they can cause should the system crash while one is running and generally because non-exclusive provides a much better interface. Further, exclusive backup mode wasn't really being tested (nor was most of the related code- like being able to log in just to stop an exclusive backup and the bits of the state machine related to that) and having to possibly deal with an exclusive backup and the backup_label file existing during pg_basebackup, pg_rewind, etc, added other complexities that we are better off without. This patch removes the exclusive backup mode, the various special cases for dealing with it, and greatly simplifies the online backup code and documentation. Authors: David Steele, Nathan Bossart Reviewed-by: Chapman Flack Discussion: https://postgr.es/m/ac7339ca-3718-3c93-929f-99e725d1172c@pgmasters.net https://postgr.es/m/CAHg+QDfiM+WU61tF6=nPZocMZvHDzCK47Kneyb0ZRULYzV5sKQ@mail.gmail.com
This commit is contained in:
@@ -385,29 +385,6 @@ typedef union WALInsertLockPadded
|
||||
char pad[PG_CACHE_LINE_SIZE];
|
||||
} WALInsertLockPadded;
|
||||
|
||||
/*
|
||||
* State of an exclusive backup, necessary to control concurrent activities
|
||||
* across sessions when working on exclusive backups.
|
||||
*
|
||||
* EXCLUSIVE_BACKUP_NONE means that there is no exclusive backup actually
|
||||
* running, to be more precise pg_start_backup() is not being executed for
|
||||
* an exclusive backup and there is no exclusive backup in progress.
|
||||
* EXCLUSIVE_BACKUP_STARTING means that pg_start_backup() is starting an
|
||||
* exclusive backup.
|
||||
* EXCLUSIVE_BACKUP_IN_PROGRESS means that pg_start_backup() has finished
|
||||
* running and an exclusive backup is in progress. pg_stop_backup() is
|
||||
* needed to finish it.
|
||||
* EXCLUSIVE_BACKUP_STOPPING means that pg_stop_backup() is stopping an
|
||||
* exclusive backup.
|
||||
*/
|
||||
typedef enum ExclusiveBackupState
|
||||
{
|
||||
EXCLUSIVE_BACKUP_NONE = 0,
|
||||
EXCLUSIVE_BACKUP_STARTING,
|
||||
EXCLUSIVE_BACKUP_IN_PROGRESS,
|
||||
EXCLUSIVE_BACKUP_STOPPING
|
||||
} ExclusiveBackupState;
|
||||
|
||||
/*
|
||||
* Session status of running backup, used for sanity checks in SQL-callable
|
||||
* functions to start and stop backups.
|
||||
@@ -456,15 +433,12 @@ typedef struct XLogCtlInsert
|
||||
bool fullPageWrites;
|
||||
|
||||
/*
|
||||
* exclusiveBackupState indicates the state of an exclusive backup (see
|
||||
* comments of ExclusiveBackupState for more details). nonExclusiveBackups
|
||||
* is a counter indicating the number of streaming base backups currently
|
||||
* in progress. forcePageWrites is set to true when either of these is
|
||||
* non-zero. lastBackupStart is the latest checkpoint redo location used
|
||||
* as a starting point for an online backup.
|
||||
* runningBackups is a counter indicating the number of backups currently in
|
||||
* progress. forcePageWrites is set to true when runningBackups is non-zero.
|
||||
* lastBackupStart is the latest checkpoint redo location used as a starting
|
||||
* point for an online backup.
|
||||
*/
|
||||
ExclusiveBackupState exclusiveBackupState;
|
||||
int nonExclusiveBackups;
|
||||
int runningBackups;
|
||||
XLogRecPtr lastBackupStart;
|
||||
|
||||
/*
|
||||
@@ -696,8 +670,7 @@ static void ReadControlFile(void);
|
||||
static void UpdateControlFile(void);
|
||||
static char *str_time(pg_time_t tnow);
|
||||
|
||||
static void pg_start_backup_callback(int code, Datum arg);
|
||||
static void pg_stop_backup_callback(int code, Datum arg);
|
||||
static void pg_backup_start_callback(int code, Datum arg);
|
||||
|
||||
static int get_sync_bit(int method);
|
||||
|
||||
@@ -5314,8 +5287,19 @@ StartupXLOG(void)
|
||||
missingContrecPtr = endOfRecoveryInfo->missingContrecPtr;
|
||||
|
||||
/*
|
||||
* Complain if we did not roll forward far enough to render the backup
|
||||
* dump consistent. Note: it is indeed okay to look at the local variable
|
||||
* When recovering from a backup (we are in recovery, and archive recovery
|
||||
* was requested), complain if we did not roll forward far enough to reach
|
||||
* the point where the database is consistent. For regular online
|
||||
* backup-from-primary, that means reaching the end-of-backup WAL record (at
|
||||
* which point we reset backupStartPoint to be Invalid), for
|
||||
* backup-from-replica (which can't inject records into the WAL stream),
|
||||
* that point is when we reach the minRecoveryPoint in pg_control (which
|
||||
* we purposfully copy last when backing up from a replica). For pg_rewind
|
||||
* (which creates a backup_label with a method of "pg_rewind") or
|
||||
* snapshot-style backups (which don't), backupEndRequired will be set to
|
||||
* false.
|
||||
*
|
||||
* Note: it is indeed okay to look at the local variable
|
||||
* LocalMinRecoveryPoint here, even though ControlFile->minRecoveryPoint
|
||||
* might be further ahead --- ControlFile->minRecoveryPoint cannot have
|
||||
* been advanced beyond the WAL we processed.
|
||||
@@ -5326,23 +5310,16 @@ StartupXLOG(void)
|
||||
{
|
||||
/*
|
||||
* Ran off end of WAL before reaching end-of-backup WAL record, or
|
||||
* minRecoveryPoint. That's usually a bad sign, indicating that you
|
||||
* tried to recover from an online backup but never called
|
||||
* pg_stop_backup(), or you didn't archive all the WAL up to that
|
||||
* point. However, this also happens in crash recovery, if the system
|
||||
* crashes while an online backup is in progress. We must not treat
|
||||
* that as an error, or the database will refuse to start up.
|
||||
* minRecoveryPoint. That's a bad sign, indicating that you tried to
|
||||
* recover from an online backup but never called pg_backup_stop(),
|
||||
* or you didn't archive all the WAL needed.
|
||||
*/
|
||||
if (ArchiveRecoveryRequested || ControlFile->backupEndRequired)
|
||||
{
|
||||
if (ControlFile->backupEndRequired)
|
||||
if (!XLogRecPtrIsInvalid(ControlFile->backupStartPoint) || ControlFile->backupEndRequired)
|
||||
ereport(FATAL,
|
||||
(errmsg("WAL ends before end of online backup"),
|
||||
errhint("All WAL generated while online backup was taken must be available at recovery.")));
|
||||
else if (!XLogRecPtrIsInvalid(ControlFile->backupStartPoint))
|
||||
ereport(FATAL,
|
||||
(errmsg("WAL ends before end of online backup"),
|
||||
errhint("Online backup started with pg_start_backup() must be ended with pg_stop_backup(), and all WAL up to that point must be available at recovery.")));
|
||||
else
|
||||
ereport(FATAL,
|
||||
(errmsg("WAL ends before consistent recovery point")));
|
||||
@@ -7036,7 +7013,7 @@ CreateRestartPoint(int flags)
|
||||
* Ensure minRecoveryPoint is past the checkpoint record. Normally,
|
||||
* this will have happened already while writing out dirty buffers,
|
||||
* but not necessarily - e.g. because no buffers were dirtied. We do
|
||||
* this because a non-exclusive base backup uses minRecoveryPoint to
|
||||
* this because a backup performed in recovery uses minRecoveryPoint to
|
||||
* determine which WAL files must be included in the backup, and the
|
||||
* file (or files) containing the checkpoint record must be included,
|
||||
* at a minimum. Note that for an ordinary restart of recovery there's
|
||||
@@ -7840,7 +7817,7 @@ xlog_redo(XLogReaderState *record)
|
||||
|
||||
/*
|
||||
* Update the LSN of the last replayed XLOG_FPW_CHANGE record so that
|
||||
* do_pg_start_backup() and do_pg_stop_backup() can check whether
|
||||
* do_pg_backup_start() and do_pg_backup_stop() can check whether
|
||||
* full_page_writes has been disabled during online backup.
|
||||
*/
|
||||
if (!fpw)
|
||||
@@ -8039,29 +8016,14 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
|
||||
}
|
||||
|
||||
/*
|
||||
* do_pg_start_backup
|
||||
* 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.
|
||||
*
|
||||
* Utility function called at the start of an online backup. It creates the
|
||||
* necessary starting checkpoint and constructs the backup label file.
|
||||
*
|
||||
* There are two kind of backups: exclusive and non-exclusive. An exclusive
|
||||
* backup is started with pg_start_backup(), and there can be only one active
|
||||
* at a time. The backup and tablespace map files of an exclusive backup are
|
||||
* written to $PGDATA/backup_label and $PGDATA/tablespace_map, and they are
|
||||
* removed by pg_stop_backup().
|
||||
*
|
||||
* A non-exclusive backup is used for the streaming base backups (see
|
||||
* src/backend/replication/basebackup.c). The difference to exclusive backups
|
||||
* is that the backup label and tablespace map files are not written to disk.
|
||||
* Instead, their would-be contents are returned in *labelfile and *tblspcmapfile,
|
||||
* and the caller is responsible for including them in the backup archive as
|
||||
* 'backup_label' and 'tablespace_map'. There can be many non-exclusive backups
|
||||
* active at the same time, and they don't conflict with an exclusive backup
|
||||
* either.
|
||||
*
|
||||
* labelfile and tblspcmapfile must be passed as NULL when starting an
|
||||
* exclusive backup, and as initially-empty StringInfos for a non-exclusive
|
||||
* backup.
|
||||
* The backup label and tablespace map contents are returned in *labelfile and
|
||||
* *tblspcmapfile, and the caller is responsible for including them in the
|
||||
* backup archive as 'backup_label' and 'tablespace_map'. There can be many
|
||||
* backups active at the same time.
|
||||
*
|
||||
* If "tablespaces" isn't NULL, it receives a list of tablespaceinfo structs
|
||||
* describing the cluster's tablespaces.
|
||||
@@ -8073,18 +8035,17 @@ issue_xlog_fsync(int fd, XLogSegNo segno, TimeLineID tli)
|
||||
* Returns the minimum WAL location that must be present to restore from this
|
||||
* backup, and the corresponding timeline ID in *starttli_p.
|
||||
*
|
||||
* Every successfully started non-exclusive backup must be stopped by calling
|
||||
* do_pg_stop_backup() or do_pg_abort_backup().
|
||||
* Every successfully started backup must be stopped by calling
|
||||
* do_pg_backup_stop() or do_pg_abort_backup().
|
||||
*
|
||||
* It is the responsibility of the caller of this function to verify the
|
||||
* permissions of the calling user!
|
||||
*/
|
||||
XLogRecPtr
|
||||
do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
|
||||
do_pg_backup_start(const char *backupidstr, bool fast, TimeLineID *starttli_p,
|
||||
StringInfo labelfile, List **tablespaces,
|
||||
StringInfo tblspcmapfile)
|
||||
{
|
||||
bool exclusive = (labelfile == NULL);
|
||||
bool backup_started_in_recovery = false;
|
||||
XLogRecPtr checkpointloc;
|
||||
XLogRecPtr startpoint;
|
||||
@@ -8093,20 +8054,9 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
|
||||
char strfbuf[128];
|
||||
char xlogfilename[MAXFNAMELEN];
|
||||
XLogSegNo _logSegNo;
|
||||
struct stat stat_buf;
|
||||
FILE *fp;
|
||||
|
||||
backup_started_in_recovery = RecoveryInProgress();
|
||||
|
||||
/*
|
||||
* Currently only non-exclusive backup can be taken during recovery.
|
||||
*/
|
||||
if (backup_started_in_recovery && exclusive)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("recovery is in progress"),
|
||||
errhint("WAL control functions cannot be executed during recovery.")));
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@@ -8145,30 +8095,12 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
|
||||
* XLogInsertRecord().
|
||||
*/
|
||||
WALInsertLockAcquireExclusive();
|
||||
if (exclusive)
|
||||
{
|
||||
/*
|
||||
* At first, mark that we're now starting an exclusive backup, to
|
||||
* ensure that there are no other sessions currently running
|
||||
* pg_start_backup() or pg_stop_backup().
|
||||
*/
|
||||
if (XLogCtl->Insert.exclusiveBackupState != EXCLUSIVE_BACKUP_NONE)
|
||||
{
|
||||
WALInsertLockRelease();
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("a backup is already in progress"),
|
||||
errhint("Run pg_stop_backup() and try again.")));
|
||||
}
|
||||
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_STARTING;
|
||||
}
|
||||
else
|
||||
XLogCtl->Insert.nonExclusiveBackups++;
|
||||
XLogCtl->Insert.runningBackups++;
|
||||
XLogCtl->Insert.forcePageWrites = true;
|
||||
WALInsertLockRelease();
|
||||
|
||||
/* Ensure we release forcePageWrites if fail below */
|
||||
PG_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive));
|
||||
PG_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0);
|
||||
{
|
||||
bool gotUniqueStartpoint = false;
|
||||
DIR *tblspcdir;
|
||||
@@ -8180,7 +8112,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
|
||||
* Force an XLOG file switch before the checkpoint, to ensure that the
|
||||
* WAL segment the checkpoint is written to doesn't contain pages with
|
||||
* old timeline IDs. That would otherwise happen if you called
|
||||
* pg_start_backup() right after restoring from a PITR archive: the
|
||||
* pg_backup_start() right after restoring from a PITR archive: the
|
||||
* first WAL segment containing the startup checkpoint has pages in
|
||||
* the beginning with the old timeline ID. That can cause trouble at
|
||||
* recovery: we won't have a history file covering the old timeline if
|
||||
@@ -8215,7 +8147,7 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
|
||||
* means that two successive backup runs can have same checkpoint
|
||||
* positions.
|
||||
*
|
||||
* Since the fact that we are executing do_pg_start_backup()
|
||||
* Since the fact that we are executing do_pg_backup_start()
|
||||
* during recovery means that checkpointer is running, we can use
|
||||
* RequestCheckpoint() to establish a restartpoint.
|
||||
*
|
||||
@@ -8416,122 +8348,19 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
|
||||
LSN_FORMAT_ARGS(startpoint), xlogfilename);
|
||||
appendStringInfo(labelfile, "CHECKPOINT LOCATION: %X/%X\n",
|
||||
LSN_FORMAT_ARGS(checkpointloc));
|
||||
appendStringInfo(labelfile, "BACKUP METHOD: %s\n",
|
||||
exclusive ? "pg_start_backup" : "streamed");
|
||||
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);
|
||||
|
||||
/*
|
||||
* Okay, write the file, or return its contents to caller.
|
||||
*/
|
||||
if (exclusive)
|
||||
{
|
||||
/*
|
||||
* Check for existing backup label --- implies a backup is already
|
||||
* running. (XXX given that we checked exclusiveBackupState
|
||||
* above, maybe it would be OK to just unlink any such label
|
||||
* file?)
|
||||
*/
|
||||
if (stat(BACKUP_LABEL_FILE, &stat_buf) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not stat file \"%s\": %m",
|
||||
BACKUP_LABEL_FILE)));
|
||||
}
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("a backup is already in progress"),
|
||||
errhint("If you're sure there is no backup in progress, remove file \"%s\" and try again.",
|
||||
BACKUP_LABEL_FILE)));
|
||||
|
||||
fp = AllocateFile(BACKUP_LABEL_FILE, "w");
|
||||
|
||||
if (!fp)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not create file \"%s\": %m",
|
||||
BACKUP_LABEL_FILE)));
|
||||
if (fwrite(labelfile->data, labelfile->len, 1, fp) != 1 ||
|
||||
fflush(fp) != 0 ||
|
||||
pg_fsync(fileno(fp)) != 0 ||
|
||||
ferror(fp) ||
|
||||
FreeFile(fp))
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not write file \"%s\": %m",
|
||||
BACKUP_LABEL_FILE)));
|
||||
/* Allocated locally for exclusive backups, so free separately */
|
||||
pfree(labelfile->data);
|
||||
pfree(labelfile);
|
||||
|
||||
/* Write backup tablespace_map file. */
|
||||
if (tblspcmapfile->len > 0)
|
||||
{
|
||||
if (stat(TABLESPACE_MAP, &stat_buf) != 0)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not stat file \"%s\": %m",
|
||||
TABLESPACE_MAP)));
|
||||
}
|
||||
else
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("a backup is already in progress"),
|
||||
errhint("If you're sure there is no backup in progress, remove file \"%s\" and try again.",
|
||||
TABLESPACE_MAP)));
|
||||
|
||||
fp = AllocateFile(TABLESPACE_MAP, "w");
|
||||
|
||||
if (!fp)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not create file \"%s\": %m",
|
||||
TABLESPACE_MAP)));
|
||||
if (fwrite(tblspcmapfile->data, tblspcmapfile->len, 1, fp) != 1 ||
|
||||
fflush(fp) != 0 ||
|
||||
pg_fsync(fileno(fp)) != 0 ||
|
||||
ferror(fp) ||
|
||||
FreeFile(fp))
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not write file \"%s\": %m",
|
||||
TABLESPACE_MAP)));
|
||||
}
|
||||
|
||||
/* Allocated locally for exclusive backups, so free separately */
|
||||
pfree(tblspcmapfile->data);
|
||||
pfree(tblspcmapfile);
|
||||
}
|
||||
}
|
||||
PG_END_ENSURE_ERROR_CLEANUP(pg_start_backup_callback, (Datum) BoolGetDatum(exclusive));
|
||||
PG_END_ENSURE_ERROR_CLEANUP(pg_backup_start_callback, (Datum) 0);
|
||||
|
||||
/*
|
||||
* Mark that start phase has correctly finished for an exclusive backup.
|
||||
* Session-level locks are updated as well to reflect that state.
|
||||
*
|
||||
* Note that CHECK_FOR_INTERRUPTS() must not occur while updating backup
|
||||
* counters and session-level lock. Otherwise they can be updated
|
||||
* inconsistently, and which might cause do_pg_abort_backup() to fail.
|
||||
* Mark that the start phase has correctly finished for the backup.
|
||||
*/
|
||||
if (exclusive)
|
||||
{
|
||||
WALInsertLockAcquireExclusive();
|
||||
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS;
|
||||
|
||||
/* Set session-level lock */
|
||||
sessionBackupState = SESSION_BACKUP_EXCLUSIVE;
|
||||
WALInsertLockRelease();
|
||||
}
|
||||
else
|
||||
sessionBackupState = SESSION_BACKUP_NON_EXCLUSIVE;
|
||||
sessionBackupState = SESSION_BACKUP_RUNNING;
|
||||
|
||||
/*
|
||||
* We're done. As a convenience, return the starting WAL location.
|
||||
@@ -8541,51 +8370,23 @@ do_pg_start_backup(const char *backupidstr, bool fast, TimeLineID *starttli_p,
|
||||
return startpoint;
|
||||
}
|
||||
|
||||
/* Error cleanup callback for pg_start_backup */
|
||||
/* Error cleanup callback for pg_backup_start */
|
||||
static void
|
||||
pg_start_backup_callback(int code, Datum arg)
|
||||
pg_backup_start_callback(int code, Datum arg)
|
||||
{
|
||||
bool exclusive = DatumGetBool(arg);
|
||||
|
||||
/* Update backup counters and forcePageWrites on failure */
|
||||
WALInsertLockAcquireExclusive();
|
||||
if (exclusive)
|
||||
{
|
||||
Assert(XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_STARTING);
|
||||
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
Assert(XLogCtl->Insert.nonExclusiveBackups > 0);
|
||||
XLogCtl->Insert.nonExclusiveBackups--;
|
||||
}
|
||||
|
||||
if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
|
||||
XLogCtl->Insert.nonExclusiveBackups == 0)
|
||||
Assert(XLogCtl->Insert.runningBackups > 0);
|
||||
XLogCtl->Insert.runningBackups--;
|
||||
|
||||
if (XLogCtl->Insert.runningBackups == 0)
|
||||
{
|
||||
XLogCtl->Insert.forcePageWrites = false;
|
||||
}
|
||||
WALInsertLockRelease();
|
||||
}
|
||||
|
||||
/*
|
||||
* Error cleanup callback for pg_stop_backup
|
||||
*/
|
||||
static void
|
||||
pg_stop_backup_callback(int code, Datum arg)
|
||||
{
|
||||
bool exclusive = DatumGetBool(arg);
|
||||
|
||||
/* Update backup status on failure */
|
||||
WALInsertLockAcquireExclusive();
|
||||
if (exclusive)
|
||||
{
|
||||
Assert(XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_STOPPING);
|
||||
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_IN_PROGRESS;
|
||||
}
|
||||
WALInsertLockRelease();
|
||||
}
|
||||
|
||||
/*
|
||||
* Utility routine to fetch the session-level status of a backup running.
|
||||
*/
|
||||
@@ -8596,14 +8397,11 @@ get_backup_status(void)
|
||||
}
|
||||
|
||||
/*
|
||||
* do_pg_stop_backup
|
||||
* 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.
|
||||
*
|
||||
* If labelfile is NULL, this stops an exclusive backup. Otherwise this stops
|
||||
* the non-exclusive backup specified by 'labelfile'.
|
||||
*
|
||||
* Returns the last WAL location that must be present to restore from this
|
||||
* backup, and the corresponding timeline ID in *stoptli_p.
|
||||
*
|
||||
@@ -8611,9 +8409,8 @@ get_backup_status(void)
|
||||
* permissions of the calling user!
|
||||
*/
|
||||
XLogRecPtr
|
||||
do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
|
||||
do_pg_backup_stop(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
|
||||
{
|
||||
bool exclusive = (labelfile == NULL);
|
||||
bool backup_started_in_recovery = false;
|
||||
XLogRecPtr startpoint;
|
||||
XLogRecPtr stoppoint;
|
||||
@@ -8627,7 +8424,6 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
|
||||
char histfilename[MAXFNAMELEN];
|
||||
char backupfrom[20];
|
||||
XLogSegNo _logSegNo;
|
||||
FILE *lfp;
|
||||
FILE *fp;
|
||||
char ch;
|
||||
int seconds_before_warning;
|
||||
@@ -8640,15 +8436,6 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
|
||||
|
||||
backup_started_in_recovery = RecoveryInProgress();
|
||||
|
||||
/*
|
||||
* Currently only non-exclusive backup can be taken during recovery.
|
||||
*/
|
||||
if (backup_started_in_recovery && exclusive)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("recovery is in progress"),
|
||||
errhint("WAL control functions cannot be executed during recovery.")));
|
||||
|
||||
/*
|
||||
* 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.
|
||||
@@ -8659,106 +8446,23 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
|
||||
errmsg("WAL level not sufficient for making an online backup"),
|
||||
errhint("wal_level must be set to \"replica\" or \"logical\" at server start.")));
|
||||
|
||||
if (exclusive)
|
||||
{
|
||||
/*
|
||||
* At first, mark that we're now stopping an exclusive backup, to
|
||||
* ensure that there are no other sessions currently running
|
||||
* pg_start_backup() or pg_stop_backup().
|
||||
*/
|
||||
WALInsertLockAcquireExclusive();
|
||||
if (XLogCtl->Insert.exclusiveBackupState != EXCLUSIVE_BACKUP_IN_PROGRESS)
|
||||
{
|
||||
WALInsertLockRelease();
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("exclusive backup not in progress")));
|
||||
}
|
||||
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_STOPPING;
|
||||
WALInsertLockRelease();
|
||||
|
||||
/*
|
||||
* Remove backup_label. In case of failure, the state for an exclusive
|
||||
* backup is switched back to in-progress.
|
||||
*/
|
||||
PG_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));
|
||||
{
|
||||
/*
|
||||
* Read the existing label file into memory.
|
||||
*/
|
||||
struct stat statbuf;
|
||||
int r;
|
||||
|
||||
if (stat(BACKUP_LABEL_FILE, &statbuf))
|
||||
{
|
||||
/* should not happen per the upper checks */
|
||||
if (errno != ENOENT)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not stat file \"%s\": %m",
|
||||
BACKUP_LABEL_FILE)));
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("a backup is not in progress")));
|
||||
}
|
||||
|
||||
lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
|
||||
if (!lfp)
|
||||
{
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read file \"%s\": %m",
|
||||
BACKUP_LABEL_FILE)));
|
||||
}
|
||||
labelfile = palloc(statbuf.st_size + 1);
|
||||
r = fread(labelfile, statbuf.st_size, 1, lfp);
|
||||
labelfile[statbuf.st_size] = '\0';
|
||||
|
||||
/*
|
||||
* Close and remove the backup label file
|
||||
*/
|
||||
if (r != 1 || ferror(lfp) || FreeFile(lfp))
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read file \"%s\": %m",
|
||||
BACKUP_LABEL_FILE)));
|
||||
durable_unlink(BACKUP_LABEL_FILE, ERROR);
|
||||
|
||||
/*
|
||||
* Remove tablespace_map file if present, it is created only if
|
||||
* there are tablespaces.
|
||||
*/
|
||||
durable_unlink(TABLESPACE_MAP, DEBUG1);
|
||||
}
|
||||
PG_END_ENSURE_ERROR_CLEANUP(pg_stop_backup_callback, (Datum) BoolGetDatum(exclusive));
|
||||
}
|
||||
|
||||
/*
|
||||
* OK to update backup counters, forcePageWrites and session-level lock.
|
||||
* OK to update backup counters, forcePageWrites, and session-level lock.
|
||||
*
|
||||
* Note that CHECK_FOR_INTERRUPTS() must not occur while updating them.
|
||||
* Otherwise they can be updated inconsistently, and which might cause
|
||||
* do_pg_abort_backup() to fail.
|
||||
*/
|
||||
WALInsertLockAcquireExclusive();
|
||||
if (exclusive)
|
||||
{
|
||||
XLogCtl->Insert.exclusiveBackupState = EXCLUSIVE_BACKUP_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/*
|
||||
* The user-visible pg_start/stop_backup() functions that operate on
|
||||
* exclusive backups can be called at any time, but for non-exclusive
|
||||
* backups, it is expected that each do_pg_start_backup() call is
|
||||
* matched by exactly one do_pg_stop_backup() call.
|
||||
*/
|
||||
Assert(XLogCtl->Insert.nonExclusiveBackups > 0);
|
||||
XLogCtl->Insert.nonExclusiveBackups--;
|
||||
}
|
||||
|
||||
if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
|
||||
XLogCtl->Insert.nonExclusiveBackups == 0)
|
||||
/*
|
||||
* It is expected that each do_pg_backup_start() call is matched by exactly
|
||||
* one do_pg_backup_stop() call.
|
||||
*/
|
||||
Assert(XLogCtl->Insert.runningBackups > 0);
|
||||
XLogCtl->Insert.runningBackups--;
|
||||
|
||||
if (XLogCtl->Insert.runningBackups == 0)
|
||||
{
|
||||
XLogCtl->Insert.forcePageWrites = false;
|
||||
}
|
||||
@@ -9016,17 +8720,13 @@ do_pg_stop_backup(char *labelfile, bool waitforarchive, TimeLineID *stoptli_p)
|
||||
/*
|
||||
* do_pg_abort_backup: abort a running backup
|
||||
*
|
||||
* This does just the most basic steps of do_pg_stop_backup(), by taking the
|
||||
* This does just the most basic steps of do_pg_backup_stop(), by taking the
|
||||
* system out of backup mode, thus making it a lot more safe to call from
|
||||
* an error handler.
|
||||
*
|
||||
* The caller can pass 'arg' as 'true' or 'false' to control whether a warning
|
||||
* is emitted.
|
||||
*
|
||||
* NB: This is only for aborting a non-exclusive backup that doesn't write
|
||||
* backup_label. A backup started with pg_start_backup() needs to be finished
|
||||
* with pg_stop_backup().
|
||||
*
|
||||
* NB: This gets used as a before_shmem_exit handler, hence the odd-looking
|
||||
* signature.
|
||||
*/
|
||||
@@ -9036,18 +8736,16 @@ do_pg_abort_backup(int code, Datum arg)
|
||||
bool emit_warning = DatumGetBool(arg);
|
||||
|
||||
/*
|
||||
* Quick exit if session is not keeping around a non-exclusive backup
|
||||
* already started.
|
||||
* Quick exit if session does not have a running backup.
|
||||
*/
|
||||
if (sessionBackupState != SESSION_BACKUP_NON_EXCLUSIVE)
|
||||
if (sessionBackupState != SESSION_BACKUP_RUNNING)
|
||||
return;
|
||||
|
||||
WALInsertLockAcquireExclusive();
|
||||
Assert(XLogCtl->Insert.nonExclusiveBackups > 0);
|
||||
XLogCtl->Insert.nonExclusiveBackups--;
|
||||
Assert(XLogCtl->Insert.runningBackups > 0);
|
||||
XLogCtl->Insert.runningBackups--;
|
||||
|
||||
if (XLogCtl->Insert.exclusiveBackupState == EXCLUSIVE_BACKUP_NONE &&
|
||||
XLogCtl->Insert.nonExclusiveBackups == 0)
|
||||
if (XLogCtl->Insert.runningBackups == 0)
|
||||
{
|
||||
XLogCtl->Insert.forcePageWrites = false;
|
||||
}
|
||||
@@ -9055,7 +8753,7 @@ do_pg_abort_backup(int code, Datum arg)
|
||||
|
||||
if (emit_warning)
|
||||
ereport(WARNING,
|
||||
(errmsg("aborting backup due to backend exiting before pg_stop_backup was called")));
|
||||
(errmsg("aborting backup due to backend exiting before pg_backup_stop was called")));
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -9115,87 +8813,6 @@ GetOldestRestartPoint(XLogRecPtr *oldrecptr, TimeLineID *oldtli)
|
||||
LWLockRelease(ControlFileLock);
|
||||
}
|
||||
|
||||
/*
|
||||
* BackupInProgress: check if online backup mode is active
|
||||
*
|
||||
* This is done by checking for existence of the "backup_label" file.
|
||||
*/
|
||||
bool
|
||||
BackupInProgress(void)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
|
||||
return (stat(BACKUP_LABEL_FILE, &stat_buf) == 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* CancelBackup: rename the "backup_label" and "tablespace_map"
|
||||
* files to cancel backup mode
|
||||
*
|
||||
* If the "backup_label" file exists, it will be renamed to "backup_label.old".
|
||||
* Similarly, if the "tablespace_map" file exists, it will be renamed to
|
||||
* "tablespace_map.old".
|
||||
*
|
||||
* Note that this will render an online backup in progress
|
||||
* useless. To correctly finish an online backup, pg_stop_backup must be
|
||||
* called.
|
||||
*/
|
||||
void
|
||||
CancelBackup(void)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
|
||||
/* if the backup_label file is not there, return */
|
||||
if (stat(BACKUP_LABEL_FILE, &stat_buf) < 0)
|
||||
return;
|
||||
|
||||
/* remove leftover file from previously canceled backup if it exists */
|
||||
unlink(BACKUP_LABEL_OLD);
|
||||
|
||||
if (durable_rename(BACKUP_LABEL_FILE, BACKUP_LABEL_OLD, DEBUG1) != 0)
|
||||
{
|
||||
ereport(WARNING,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("online backup mode was not canceled"),
|
||||
errdetail("File \"%s\" could not be renamed to \"%s\": %m.",
|
||||
BACKUP_LABEL_FILE, BACKUP_LABEL_OLD)));
|
||||
return;
|
||||
}
|
||||
|
||||
/* if the tablespace_map file is not there, return */
|
||||
if (stat(TABLESPACE_MAP, &stat_buf) < 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("online backup mode canceled"),
|
||||
errdetail("File \"%s\" was renamed to \"%s\".",
|
||||
BACKUP_LABEL_FILE, BACKUP_LABEL_OLD)));
|
||||
return;
|
||||
}
|
||||
|
||||
/* remove leftover file from previously canceled backup if it exists */
|
||||
unlink(TABLESPACE_MAP_OLD);
|
||||
|
||||
if (durable_rename(TABLESPACE_MAP, TABLESPACE_MAP_OLD, DEBUG1) == 0)
|
||||
{
|
||||
ereport(LOG,
|
||||
(errmsg("online backup mode canceled"),
|
||||
errdetail("Files \"%s\" and \"%s\" were renamed to "
|
||||
"\"%s\" and \"%s\", respectively.",
|
||||
BACKUP_LABEL_FILE, TABLESPACE_MAP,
|
||||
BACKUP_LABEL_OLD, TABLESPACE_MAP_OLD)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ereport(WARNING,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("online backup mode canceled"),
|
||||
errdetail("File \"%s\" was renamed to \"%s\", but "
|
||||
"file \"%s\" could not be renamed to \"%s\": %m.",
|
||||
BACKUP_LABEL_FILE, BACKUP_LABEL_OLD,
|
||||
TABLESPACE_MAP, TABLESPACE_MAP_OLD)));
|
||||
}
|
||||
}
|
||||
|
||||
/* Thin wrapper around ShutdownWalRcv(). */
|
||||
void
|
||||
XLogShutdownWalRcv(void)
|
||||
|
||||
@@ -39,13 +39,13 @@
|
||||
#include "utils/tuplestore.h"
|
||||
|
||||
/*
|
||||
* Store label file and tablespace map during non-exclusive backups.
|
||||
* Store label file and tablespace map during backups.
|
||||
*/
|
||||
static StringInfo label_file;
|
||||
static StringInfo tblspc_map_file;
|
||||
|
||||
/*
|
||||
* pg_start_backup: set up for taking an on-line backup dump
|
||||
* 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
|
||||
@@ -57,105 +57,44 @@ static StringInfo tblspc_map_file;
|
||||
* GRANT system.
|
||||
*/
|
||||
Datum
|
||||
pg_start_backup(PG_FUNCTION_ARGS)
|
||||
pg_backup_start(PG_FUNCTION_ARGS)
|
||||
{
|
||||
text *backupid = PG_GETARG_TEXT_PP(0);
|
||||
bool fast = PG_GETARG_BOOL(1);
|
||||
bool exclusive = PG_GETARG_BOOL(2);
|
||||
char *backupidstr;
|
||||
XLogRecPtr startpoint;
|
||||
SessionBackupState status = get_backup_status();
|
||||
MemoryContext oldcontext;
|
||||
|
||||
backupidstr = text_to_cstring(backupid);
|
||||
|
||||
if (status == SESSION_BACKUP_NON_EXCLUSIVE)
|
||||
if (status == SESSION_BACKUP_RUNNING)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("a backup is already in progress in this session")));
|
||||
|
||||
if (exclusive)
|
||||
{
|
||||
startpoint = do_pg_start_backup(backupidstr, fast, NULL, NULL,
|
||||
NULL, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryContext oldcontext;
|
||||
/*
|
||||
* Label file and tablespace map file need to be long-lived, since
|
||||
* they are read in pg_backup_stop.
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
|
||||
label_file = makeStringInfo();
|
||||
tblspc_map_file = makeStringInfo();
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
|
||||
/*
|
||||
* Label file and tablespace map file need to be long-lived, since
|
||||
* they are read in pg_stop_backup.
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(TopMemoryContext);
|
||||
label_file = makeStringInfo();
|
||||
tblspc_map_file = makeStringInfo();
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
register_persistent_abort_backup_handler();
|
||||
|
||||
register_persistent_abort_backup_handler();
|
||||
|
||||
startpoint = do_pg_start_backup(backupidstr, fast, NULL, label_file,
|
||||
NULL, tblspc_map_file);
|
||||
}
|
||||
startpoint = do_pg_backup_start(backupidstr, fast, NULL, label_file,
|
||||
NULL, tblspc_map_file);
|
||||
|
||||
PG_RETURN_LSN(startpoint);
|
||||
}
|
||||
|
||||
/*
|
||||
* pg_stop_backup: finish taking an on-line backup dump
|
||||
*
|
||||
* We write an end-of-backup WAL record, and remove the backup label file
|
||||
* created by pg_start_backup, creating a backup history file in pg_wal
|
||||
* instead (whence it will immediately be archived). The backup history file
|
||||
* contains the same info found in the label file, plus the backup-end time
|
||||
* and WAL location. Before 9.0, the backup-end time was read from the backup
|
||||
* history file at the beginning of archive recovery, but we now use the WAL
|
||||
* record for that and the file is for informational and debug purposes only.
|
||||
*
|
||||
* Note: different from CancelBackup which just cancels online backup mode.
|
||||
*
|
||||
* Note: this version is only called to stop an exclusive backup. The function
|
||||
* pg_stop_backup_v2 (overloaded as pg_stop_backup in SQL) is called to
|
||||
* stop non-exclusive backups.
|
||||
*
|
||||
* Permission checking for this function is managed through the normal
|
||||
* GRANT system.
|
||||
*/
|
||||
Datum
|
||||
pg_stop_backup(PG_FUNCTION_ARGS)
|
||||
{
|
||||
XLogRecPtr stoppoint;
|
||||
SessionBackupState status = get_backup_status();
|
||||
|
||||
if (status == SESSION_BACKUP_NON_EXCLUSIVE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("non-exclusive backup in progress"),
|
||||
errhint("Did you mean to use pg_stop_backup('f')?")));
|
||||
|
||||
/*
|
||||
* Exclusive backups were typically started in a different connection, so
|
||||
* don't try to verify that status of backup is set to
|
||||
* SESSION_BACKUP_EXCLUSIVE in this function. Actual verification that an
|
||||
* exclusive backup is in fact running is handled inside
|
||||
* do_pg_stop_backup.
|
||||
*/
|
||||
stoppoint = do_pg_stop_backup(NULL, true, NULL);
|
||||
|
||||
PG_RETURN_LSN(stoppoint);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* pg_stop_backup_v2: finish taking exclusive or nonexclusive on-line backup.
|
||||
* pg_backup_stop: finish taking an on-line backup.
|
||||
*
|
||||
* Works the same as pg_stop_backup, except for non-exclusive backups it returns
|
||||
* the backup label and tablespace map files as text fields in as part of the
|
||||
* resultset.
|
||||
*
|
||||
* The first parameter (variable 'exclusive') allows the user to tell us if
|
||||
* this is an exclusive or a non-exclusive backup.
|
||||
*
|
||||
* The second parameter (variable 'waitforarchive'), which is optional,
|
||||
* The first parameter (variable 'waitforarchive'), which is optional,
|
||||
* 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.
|
||||
*
|
||||
@@ -163,15 +102,14 @@ pg_stop_backup(PG_FUNCTION_ARGS)
|
||||
* GRANT system.
|
||||
*/
|
||||
Datum
|
||||
pg_stop_backup_v2(PG_FUNCTION_ARGS)
|
||||
pg_backup_stop(PG_FUNCTION_ARGS)
|
||||
{
|
||||
#define PG_STOP_BACKUP_V2_COLS 3
|
||||
TupleDesc tupdesc;
|
||||
Datum values[PG_STOP_BACKUP_V2_COLS];
|
||||
bool nulls[PG_STOP_BACKUP_V2_COLS];
|
||||
|
||||
bool exclusive = PG_GETARG_BOOL(0);
|
||||
bool waitforarchive = PG_GETARG_BOOL(1);
|
||||
bool waitforarchive = PG_GETARG_BOOL(0);
|
||||
XLogRecPtr stoppoint;
|
||||
SessionBackupState status = get_backup_status();
|
||||
|
||||
@@ -182,51 +120,29 @@ pg_stop_backup_v2(PG_FUNCTION_ARGS)
|
||||
MemSet(values, 0, sizeof(values));
|
||||
MemSet(nulls, 0, sizeof(nulls));
|
||||
|
||||
if (exclusive)
|
||||
{
|
||||
if (status == SESSION_BACKUP_NON_EXCLUSIVE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("non-exclusive backup in progress"),
|
||||
errhint("Did you mean to use pg_stop_backup('f')?")));
|
||||
if (status != SESSION_BACKUP_RUNNING)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("backup is not in progress"),
|
||||
errhint("Did you call pg_backup_start()?")));
|
||||
|
||||
/*
|
||||
* Stop the exclusive backup, and since we're in an exclusive backup
|
||||
* return NULL for both backup_label and tablespace_map.
|
||||
*/
|
||||
stoppoint = do_pg_stop_backup(NULL, waitforarchive, NULL);
|
||||
/*
|
||||
* 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);
|
||||
|
||||
nulls[1] = true;
|
||||
nulls[2] = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (status != SESSION_BACKUP_NON_EXCLUSIVE)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("non-exclusive backup is not in progress"),
|
||||
errhint("Did you mean to use pg_stop_backup('t')?")));
|
||||
|
||||
/*
|
||||
* Stop the non-exclusive backup. Return a copy of the backup label
|
||||
* and tablespace map so they can be written to disk by the caller.
|
||||
*/
|
||||
stoppoint = do_pg_stop_backup(label_file->data, waitforarchive, NULL);
|
||||
|
||||
values[1] = CStringGetTextDatum(label_file->data);
|
||||
values[2] = CStringGetTextDatum(tblspc_map_file->data);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* Stoppoint is included on both exclusive and nonexclusive backups */
|
||||
values[0] = LSNGetDatum(stoppoint);
|
||||
values[1] = CStringGetTextDatum(label_file->data);
|
||||
values[2] = CStringGetTextDatum(tblspc_map_file->data);
|
||||
|
||||
/* 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;
|
||||
|
||||
/* Returns the record as Datum */
|
||||
PG_RETURN_DATUM(HeapTupleGetDatum(heap_form_tuple(tupdesc, values, nulls)));
|
||||
@@ -298,7 +214,7 @@ pg_create_restore_point(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Report the current WAL write location (same format as pg_start_backup etc)
|
||||
* Report the current WAL write location (same format as pg_backup_start etc)
|
||||
*
|
||||
* This is useful for determining how much of WAL is visible to an external
|
||||
* archiving process. Note that the data before this point is written out
|
||||
@@ -321,7 +237,7 @@ pg_current_wal_lsn(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Report the current WAL insert location (same format as pg_start_backup etc)
|
||||
* Report the current WAL insert location (same format as pg_backup_start etc)
|
||||
*
|
||||
* This function is mostly for debugging purposes.
|
||||
*/
|
||||
@@ -342,7 +258,7 @@ pg_current_wal_insert_lsn(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Report the current WAL flush location (same format as pg_start_backup etc)
|
||||
* Report the current WAL flush location (same format as pg_backup_start etc)
|
||||
*
|
||||
* This function is mostly for debugging purposes.
|
||||
*/
|
||||
@@ -363,7 +279,7 @@ pg_current_wal_flush_lsn(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Report the last WAL receive location (same format as pg_start_backup etc)
|
||||
* Report the last WAL receive location (same format as pg_backup_start etc)
|
||||
*
|
||||
* This is useful for determining how much of WAL is guaranteed to be received
|
||||
* and synced to disk by walreceiver.
|
||||
@@ -382,7 +298,7 @@ pg_last_wal_receive_lsn(PG_FUNCTION_ARGS)
|
||||
}
|
||||
|
||||
/*
|
||||
* Report the last WAL replay location (same format as pg_start_backup etc)
|
||||
* Report the last WAL replay location (same format as pg_backup_start etc)
|
||||
*
|
||||
* This is useful for determining how much of WAL is visible to read-only
|
||||
* connections during recovery.
|
||||
@@ -402,7 +318,7 @@ pg_last_wal_replay_lsn(PG_FUNCTION_ARGS)
|
||||
|
||||
/*
|
||||
* Compute an xlog file name and decimal byte offset given a WAL location,
|
||||
* such as is returned by pg_stop_backup() or pg_switch_wal().
|
||||
* such as is returned by pg_backup_stop() or pg_switch_wal().
|
||||
*
|
||||
* Note that a location exactly at a segment boundary is taken to be in
|
||||
* the previous segment. This is usually the right thing, since the
|
||||
@@ -470,7 +386,7 @@ pg_walfile_name_offset(PG_FUNCTION_ARGS)
|
||||
|
||||
/*
|
||||
* Compute an xlog file name given a WAL location,
|
||||
* such as is returned by pg_stop_backup() or pg_switch_wal().
|
||||
* such as is returned by pg_backup_stop() or pg_switch_wal().
|
||||
*/
|
||||
Datum
|
||||
pg_walfile_name(PG_FUNCTION_ARGS)
|
||||
@@ -645,81 +561,6 @@ pg_wal_lsn_diff(PG_FUNCTION_ARGS)
|
||||
PG_RETURN_NUMERIC(result);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns bool with current on-line backup mode, a global state.
|
||||
*/
|
||||
Datum
|
||||
pg_is_in_backup(PG_FUNCTION_ARGS)
|
||||
{
|
||||
PG_RETURN_BOOL(BackupInProgress());
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns start time of an online exclusive backup.
|
||||
*
|
||||
* When there's no exclusive backup in progress, the function
|
||||
* returns NULL.
|
||||
*/
|
||||
Datum
|
||||
pg_backup_start_time(PG_FUNCTION_ARGS)
|
||||
{
|
||||
Datum xtime;
|
||||
FILE *lfp;
|
||||
char fline[MAXPGPATH];
|
||||
char backup_start_time[30];
|
||||
|
||||
/*
|
||||
* See if label file is present
|
||||
*/
|
||||
lfp = AllocateFile(BACKUP_LABEL_FILE, "r");
|
||||
if (lfp == NULL)
|
||||
{
|
||||
if (errno != ENOENT)
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read file \"%s\": %m",
|
||||
BACKUP_LABEL_FILE)));
|
||||
PG_RETURN_NULL();
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the file to find the START TIME line.
|
||||
*/
|
||||
backup_start_time[0] = '\0';
|
||||
while (fgets(fline, sizeof(fline), lfp) != NULL)
|
||||
{
|
||||
if (sscanf(fline, "START TIME: %25[^\n]\n", backup_start_time) == 1)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for a read error. */
|
||||
if (ferror(lfp))
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not read file \"%s\": %m", BACKUP_LABEL_FILE)));
|
||||
|
||||
/* Close the backup label file. */
|
||||
if (FreeFile(lfp))
|
||||
ereport(ERROR,
|
||||
(errcode_for_file_access(),
|
||||
errmsg("could not close file \"%s\": %m", BACKUP_LABEL_FILE)));
|
||||
|
||||
if (strlen(backup_start_time) == 0)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
|
||||
errmsg("invalid data in file \"%s\"", BACKUP_LABEL_FILE)));
|
||||
|
||||
/*
|
||||
* Convert the time string read from file to TimestampTz form.
|
||||
*/
|
||||
xtime = DirectFunctionCall3(timestamptz_in,
|
||||
CStringGetDatum(backup_start_time),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
Int32GetDatum(-1));
|
||||
|
||||
PG_RETURN_DATUM(xtime);
|
||||
}
|
||||
|
||||
/*
|
||||
* Promotes a standby server.
|
||||
*
|
||||
|
||||
@@ -1183,9 +1183,14 @@ read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI,
|
||||
*backupLabelTLI = tli_from_walseg;
|
||||
|
||||
/*
|
||||
* BACKUP METHOD and BACKUP FROM lines are new in 9.2. We can't restore
|
||||
* from an older backup anyway, but since the information on it is not
|
||||
* strictly required, don't error out if it's missing for some reason.
|
||||
* BACKUP METHOD lets us know if this was a typical backup ("streamed",
|
||||
* which could mean either pg_basebackup or the pg_backup_start/stop
|
||||
* method was used) or if this label came from somewhere else (the only
|
||||
* other option today being from pg_rewind). If this was a streamed
|
||||
* backup then we know that we need to play through until we get to the
|
||||
* end of the WAL which was generated during the backup (at which point
|
||||
* we will have reached consistency and backupEndRequired will be reset
|
||||
* to be false).
|
||||
*/
|
||||
if (fscanf(lfp, "BACKUP METHOD: %19s\n", backuptype) == 1)
|
||||
{
|
||||
@@ -1193,6 +1198,11 @@ read_backup_label(XLogRecPtr *checkPointLoc, TimeLineID *backupLabelTLI,
|
||||
*backupEndRequired = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* BACKUP FROM lets us know if this was from a primary or a standby. If
|
||||
* it was from a standby, we'll double-check that the control file state
|
||||
* matches that of a standby.
|
||||
*/
|
||||
if (fscanf(lfp, "BACKUP FROM: %19s\n", backupfrom) == 1)
|
||||
{
|
||||
if (strcmp(backupfrom, "standby") == 0)
|
||||
@@ -1970,7 +1980,7 @@ xlogrecovery_redo(XLogReaderState *record, TimeLineID replayTLI)
|
||||
{
|
||||
/*
|
||||
* We have reached the end of base backup, the point where
|
||||
* pg_stop_backup() was done. The data on disk is now consistent
|
||||
* pg_backup_stop() was done. The data on disk is now consistent
|
||||
* (assuming we have also reached minRecoveryPoint). Set
|
||||
* backupEndPoint to the current LSN, so that the next call to
|
||||
* CheckRecoveryConsistency() will notice it and do the
|
||||
@@ -2033,7 +2043,7 @@ CheckRecoveryConsistency(void)
|
||||
|
||||
/*
|
||||
* Have we passed our safe starting point? Note that minRecoveryPoint is
|
||||
* known to be incorrectly set if ControlFile->backupEndRequired, until
|
||||
* known to be incorrectly set if recovering from a backup, until
|
||||
* the XLOG_BACKUP_END arrives to advise us of the correct
|
||||
* minRecoveryPoint. All we know prior to that is that we're not
|
||||
* consistent yet.
|
||||
|
||||
Reference in New Issue
Block a user