unite_recoveryconf_postgresqlconf_v0.patch
text/x-patch
Filename: unite_recoveryconf_postgresqlconf_v0.patch
Type: text/x-patch
Part: 0
*** a/src/backend/access/transam/xlog.c
--- b/src/backend/access/transam/xlog.c
***************
*** 79,84 **** bool fullPageWrites = true;
--- 79,98 ----
bool log_checkpoints = false;
int sync_method = DEFAULT_SYNC_METHOD;
int wal_level = WAL_LEVEL_MINIMAL;
+ char *restore_command = NULL;
+ char *archive_cleanup_command = NULL;
+ char *recovery_end_command = NULL;
+ bool standby_mode = false;
+ char *primary_conninfo = NULL;
+ char *trigger_file = NULL;
+ RecoveryTargetType recovery_target = RECOVERY_TARGET_UNSET;
+ TransactionId recovery_target_xid = InvalidTransactionId;
+ TimestampTz recovery_target_time = 0;
+ char *recovery_target_name = NULL;
+ bool recovery_target_inclusive = true;
+ bool pause_at_recovery_target = true;
+ char *recovery_target_timeline_string = NULL;
+ TimeLineID recovery_target_timeline = 0;
#ifdef WAL_DEBUG
bool XLOG_DEBUG = false;
***************
*** 185,207 **** static bool InArchiveRecovery = false;
/* Was the last xlog file restored from archive, or local? */
static bool restoredFromArchive = false;
! /* options taken from recovery.conf for archive recovery */
! static char *recoveryRestoreCommand = NULL;
! static char *recoveryEndCommand = NULL;
! static char *archiveCleanupCommand = NULL;
! static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET;
! static bool recoveryTargetInclusive = true;
! static bool recoveryPauseAtTarget = true;
! static TransactionId recoveryTargetXid;
! static TimestampTz recoveryTargetTime;
! static char *recoveryTargetName;
!
! /* options taken from recovery.conf for XLOG streaming */
! static bool StandbyMode = false;
! static char *PrimaryConnInfo = NULL;
! static char *TriggerFile = NULL;
!
! /* if recoveryStopsHere returns true, it saves actual stop xid/time/name here */
static TransactionId recoveryStopXid;
static TimestampTz recoveryStopTime;
static char recoveryStopName[MAXFNAMELEN];
--- 199,206 ----
/* Was the last xlog file restored from archive, or local? */
static bool restoredFromArchive = false;
! /* if recoveryStopsHere returns true, it saves actual stop type/xid/time/name here */
! static RecoveryTargetType recoveryStopTarget;
static TransactionId recoveryStopXid;
static TimestampTz recoveryStopTime;
static char recoveryStopName[MAXFNAMELEN];
***************
*** 407,416 **** typedef struct XLogCtlData
TimeLineID RecoveryTargetTLI;
/*
! * archiveCleanupCommand is read from recovery.conf but needs to be in
! * shared memory so that the bgwriter process can access it.
*/
! char archiveCleanupCommand[MAXPGPATH];
/*
* SharedRecoveryInProgress indicates if we're still in crash or archive
--- 406,415 ----
TimeLineID RecoveryTargetTLI;
/*
! * archive_cleanup_command can be read from recovery.conf but needs
! * to be in shared memory so that the bgwriter process can access it.
*/
! char archive_cleanup_command[MAXPGPATH];
/*
* SharedRecoveryInProgress indicates if we're still in crash or archive
***************
*** 611,616 **** static void SetRecoveryPause(bool recoveryPause);
--- 610,616 ----
static void SetLatestXTime(TimestampTz xtime);
static TimestampTz GetLatestXTime(void);
static void CheckRequiredParameterValues(void);
+ static void CheckRestoreCommandSet(void);
static void XLogReportParameters(void);
static void LocalSetXLogInsertAllowed(void);
static void CheckPointGuts(XLogRecPtr checkPointRedo, int flags);
***************
*** 2931,2937 **** RestoreArchivedFile(char *path, const char *xlogfname,
uint32 restartSeg;
/* In standby mode, restore_command might not be supplied */
! if (recoveryRestoreCommand == NULL)
goto not_available;
/*
--- 2931,2937 ----
uint32 restartSeg;
/* In standby mode, restore_command might not be supplied */
! if (!restore_command[0])
goto not_available;
/*
***************
*** 2951,2957 **** RestoreArchivedFile(char *path, const char *xlogfname,
* of log segments that weren't yet transferred to the archive.
*
* Notice that we don't actually overwrite any files when we copy back
! * from archive because the recoveryRestoreCommand may inadvertently
* restore inappropriate xlogs, or they may be corrupt, so we may wish to
* fallback to the segments remaining in current XLOGDIR later. The
* copy-from-archive filename is always the same, ensuring that we don't
--- 2951,2957 ----
* of log segments that weren't yet transferred to the archive.
*
* Notice that we don't actually overwrite any files when we copy back
! * from archive because the restore_command may inadvertently
* restore inappropriate xlogs, or they may be corrupt, so we may wish to
* fallback to the segments remaining in current XLOGDIR later. The
* copy-from-archive filename is always the same, ensuring that we don't
***************
*** 3015,3021 **** RestoreArchivedFile(char *path, const char *xlogfname,
endp = xlogRestoreCmd + MAXPGPATH - 1;
*endp = '\0';
! for (sp = recoveryRestoreCommand; *sp; sp++)
{
if (*sp == '%')
{
--- 3015,3021 ----
endp = xlogRestoreCmd + MAXPGPATH - 1;
*endp = '\0';
! for (sp = restore_command; *sp; sp++)
{
if (*sp == '%')
{
***************
*** 3106,3112 **** RestoreArchivedFile(char *path, const char *xlogfname,
* incorrectly conclude we've reached the end of WAL and we're
* done recovering ...
*/
! if (StandbyMode && stat_buf.st_size < expectedSize)
elevel = DEBUG1;
else
elevel = FATAL;
--- 3106,3112 ----
* incorrectly conclude we've reached the end of WAL and we're
* done recovering ...
*/
! if (standby_mode && stat_buf.st_size < expectedSize)
elevel = DEBUG1;
else
elevel = FATAL;
***************
*** 4063,4069 **** next_record_is_invalid:
}
/* In standby-mode, keep trying */
! if (StandbyMode)
goto retry;
else
return NULL;
--- 4063,4069 ----
}
/* In standby-mode, keep trying */
! if (standby_mode)
goto retry;
else
return NULL;
***************
*** 4522,4528 **** writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
* Write comment to history file to explain why and where timeline
* changed. Comment varies according to the recovery target used.
*/
! if (recoveryTarget == RECOVERY_TARGET_XID)
snprintf(buffer, sizeof(buffer),
"%s%u\t%s\t%s transaction %u\n",
(srcfd < 0) ? "" : "\n",
--- 4522,4528 ----
* Write comment to history file to explain why and where timeline
* changed. Comment varies according to the recovery target used.
*/
! if (recoveryStopTarget == RECOVERY_TARGET_XID)
snprintf(buffer, sizeof(buffer),
"%s%u\t%s\t%s transaction %u\n",
(srcfd < 0) ? "" : "\n",
***************
*** 4530,4536 **** writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
xlogfname,
recoveryStopAfter ? "after" : "before",
recoveryStopXid);
! else if (recoveryTarget == RECOVERY_TARGET_TIME)
snprintf(buffer, sizeof(buffer),
"%s%u\t%s\t%s %s\n",
(srcfd < 0) ? "" : "\n",
--- 4530,4536 ----
xlogfname,
recoveryStopAfter ? "after" : "before",
recoveryStopXid);
! else if (recoveryStopTarget == RECOVERY_TARGET_TIME)
snprintf(buffer, sizeof(buffer),
"%s%u\t%s\t%s %s\n",
(srcfd < 0) ? "" : "\n",
***************
*** 4538,4544 **** writeTimeLineHistory(TimeLineID newTLI, TimeLineID parentTLI,
xlogfname,
recoveryStopAfter ? "after" : "before",
timestamptz_to_str(recoveryStopTime));
! else if (recoveryTarget == RECOVERY_TARGET_NAME)
snprintf(buffer, sizeof(buffer),
"%s%u\t%s\tat restore point \"%s\"\n",
(srcfd < 0) ? "" : "\n",
--- 4538,4544 ----
xlogfname,
recoveryStopAfter ? "after" : "before",
timestamptz_to_str(recoveryStopTime));
! else if (recoveryStopTarget == RECOVERY_TARGET_NAME)
snprintf(buffer, sizeof(buffer),
"%s%u\t%s\tat restore point \"%s\"\n",
(srcfd < 0) ? "" : "\n",
***************
*** 5276,5283 **** static void
readRecoveryCommandFile(void)
{
FILE *fd;
- TimeLineID rtli = 0;
- bool rtliGiven = false;
ConfigVariable *item,
*head = NULL,
*tail = NULL;
--- 5276,5281 ----
***************
*** 5302,5480 **** readRecoveryCommandFile(void)
for (item = head; item; item = item->next)
{
if (strcmp(item->name, "restore_command") == 0)
! {
! recoveryRestoreCommand = pstrdup(item->value);
! ereport(DEBUG2,
! (errmsg_internal("restore_command = '%s'",
! recoveryRestoreCommand)));
! }
else if (strcmp(item->name, "recovery_end_command") == 0)
! {
! recoveryEndCommand = pstrdup(item->value);
! ereport(DEBUG2,
! (errmsg_internal("recovery_end_command = '%s'",
! recoveryEndCommand)));
! }
else if (strcmp(item->name, "archive_cleanup_command") == 0)
! {
! archiveCleanupCommand = pstrdup(item->value);
! ereport(DEBUG2,
! (errmsg_internal("archive_cleanup_command = '%s'",
! archiveCleanupCommand)));
! }
else if (strcmp(item->name, "pause_at_recovery_target") == 0)
! {
! if (!parse_bool(item->value, &recoveryPauseAtTarget))
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("parameter \"%s\" requires a Boolean value", "pause_at_recovery_target")));
! ereport(DEBUG2,
! (errmsg_internal("pause_at_recovery_target = '%s'",
! item->value)));
! }
else if (strcmp(item->name, "recovery_target_timeline") == 0)
! {
! rtliGiven = true;
! if (strcmp(item->value, "latest") == 0)
! rtli = 0;
! else
! {
! errno = 0;
! rtli = (TimeLineID) strtoul(item->value, NULL, 0);
! if (errno == EINVAL || errno == ERANGE)
! ereport(FATAL,
! (errmsg("recovery_target_timeline is not a valid number: \"%s\"",
! item->value)));
! }
! if (rtli)
! ereport(DEBUG2,
! (errmsg_internal("recovery_target_timeline = %u", rtli)));
! else
! ereport(DEBUG2,
! (errmsg_internal("recovery_target_timeline = latest")));
! }
else if (strcmp(item->name, "recovery_target_xid") == 0)
! {
! errno = 0;
! recoveryTargetXid = (TransactionId) strtoul(item->value, NULL, 0);
! if (errno == EINVAL || errno == ERANGE)
! ereport(FATAL,
! (errmsg("recovery_target_xid is not a valid number: \"%s\"",
! item->value)));
! ereport(DEBUG2,
! (errmsg_internal("recovery_target_xid = %u",
! recoveryTargetXid)));
! recoveryTarget = RECOVERY_TARGET_XID;
! }
else if (strcmp(item->name, "recovery_target_time") == 0)
! {
! /*
! * if recovery_target_xid or recovery_target_name specified, then
! * this overrides recovery_target_time
! */
! if (recoveryTarget == RECOVERY_TARGET_XID ||
! recoveryTarget == RECOVERY_TARGET_NAME)
! continue;
! recoveryTarget = RECOVERY_TARGET_TIME;
!
! /*
! * Convert the time string given by the user to TimestampTz form.
! */
! recoveryTargetTime =
! DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
! CStringGetDatum(item->value),
! ObjectIdGetDatum(InvalidOid),
! Int32GetDatum(-1)));
! ereport(DEBUG2,
! (errmsg_internal("recovery_target_time = '%s'",
! timestamptz_to_str(recoveryTargetTime))));
! }
else if (strcmp(item->name, "recovery_target_name") == 0)
! {
! /*
! * if recovery_target_xid specified, then this overrides
! * recovery_target_name
! */
! if (recoveryTarget == RECOVERY_TARGET_XID)
! continue;
! recoveryTarget = RECOVERY_TARGET_NAME;
!
! recoveryTargetName = pstrdup(item->value);
! if (strlen(recoveryTargetName) >= MAXFNAMELEN)
! ereport(FATAL,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("recovery_target_name is too long (maximum %d characters)",
! MAXFNAMELEN - 1)));
!
! ereport(DEBUG2,
! (errmsg_internal("recovery_target_name = '%s'",
! recoveryTargetName)));
! }
else if (strcmp(item->name, "recovery_target_inclusive") == 0)
! {
! /*
! * does nothing if a recovery_target is not also set
! */
! if (!parse_bool(item->value, &recoveryTargetInclusive))
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("parameter \"%s\" requires a Boolean value",
! "recovery_target_inclusive")));
! ereport(DEBUG2,
! (errmsg_internal("recovery_target_inclusive = %s",
! item->value)));
! }
else if (strcmp(item->name, "standby_mode") == 0)
! {
! if (!parse_bool(item->value, &StandbyMode))
! ereport(ERROR,
! (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
! errmsg("parameter \"%s\" requires a Boolean value",
! "standby_mode")));
! ereport(DEBUG2,
! (errmsg_internal("standby_mode = '%s'", item->value)));
! }
else if (strcmp(item->name, "primary_conninfo") == 0)
! {
! PrimaryConnInfo = pstrdup(item->value);
! ereport(DEBUG2,
! (errmsg_internal("primary_conninfo = '%s'",
! PrimaryConnInfo)));
! }
else if (strcmp(item->name, "trigger_file") == 0)
! {
! TriggerFile = pstrdup(item->value);
! ereport(DEBUG2,
! (errmsg_internal("trigger_file = '%s'",
! TriggerFile)));
! }
else
ereport(FATAL,
(errmsg("unrecognized recovery parameter \"%s\"",
item->name)));
}
/*
* Check for compulsory parameters
*/
! if (StandbyMode)
! {
! if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL)
! ereport(WARNING,
! (errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command",
! RECOVERY_COMMAND_FILE),
! errhint("The database server will regularly poll the pg_xlog subdirectory to check for files placed there.")));
! }
! else
! {
! if (recoveryRestoreCommand == NULL)
! ereport(FATAL,
! (errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled",
! RECOVERY_COMMAND_FILE)));
! }
!
! /* Enable fetching from archive recovery area */
! InArchiveRecovery = true;
/*
* If user specified recovery_target_timeline, validate it or compute the
--- 5300,5346 ----
for (item = head; item; item = item->next)
{
if (strcmp(item->name, "restore_command") == 0)
! SetConfigOption("restore_command", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else if (strcmp(item->name, "recovery_end_command") == 0)
! SetConfigOption("recovery_end_command", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else if (strcmp(item->name, "archive_cleanup_command") == 0)
! SetConfigOption("archive_cleanup_command", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else if (strcmp(item->name, "pause_at_recovery_target") == 0)
! SetConfigOption("pause_at_recovery_target", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else if (strcmp(item->name, "recovery_target_timeline") == 0)
! SetConfigOption("recovery_target_timeline", item->value, PGC_POSTMASTER, PGC_S_OVERRIDE);
else if (strcmp(item->name, "recovery_target_xid") == 0)
! SetConfigOption("recovery_target_xid", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else if (strcmp(item->name, "recovery_target_time") == 0)
! SetConfigOption("recovery_target_time", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else if (strcmp(item->name, "recovery_target_name") == 0)
! SetConfigOption("recovery_target_name", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else if (strcmp(item->name, "recovery_target_inclusive") == 0)
! SetConfigOption("recovery_target_inclusive", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else if (strcmp(item->name, "standby_mode") == 0)
! SetConfigOption("standby_mode", item->value, PGC_POSTMASTER, PGC_S_OVERRIDE);
else if (strcmp(item->name, "primary_conninfo") == 0)
! SetConfigOption("primary_conninfo", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else if (strcmp(item->name, "trigger_file") == 0)
! SetConfigOption("trigger_file", item->value, PGC_SIGHUP, PGC_S_OVERRIDE);
else
ereport(FATAL,
(errmsg("unrecognized recovery parameter \"%s\"",
item->name)));
}
+ /* Enable fetching from archive recovery area */
+ InArchiveRecovery = true;
+
/*
* Check for compulsory parameters
*/
! if (standby_mode && !restore_command[0] && !primary_conninfo[0])
! ereport(WARNING,
! (errmsg("neither primary_conninfo nor restore_command is specified"),
! errhint("The database server will regularly poll the pg_xlog subdirectory to "
! "check for files placed there until either of them is set in postgresql.conf.")));
! CheckRestoreCommandSet();
/*
* If user specified recovery_target_timeline, validate it or compute the
***************
*** 5482,5497 **** readRecoveryCommandFile(void)
* command and set InArchiveRecovery, because we need to fetch timeline
* history files from the archive.
*/
! if (rtliGiven)
{
! if (rtli)
{
/* Timeline 1 does not have a history file, all else should */
! if (rtli != 1 && !existsTimeLineHistory(rtli))
ereport(FATAL,
(errmsg("recovery target timeline %u does not exist",
! rtli)));
! recoveryTargetTLI = rtli;
recoveryTargetIsLatest = false;
}
else
--- 5348,5363 ----
* command and set InArchiveRecovery, because we need to fetch timeline
* history files from the archive.
*/
! if (strcmp(recovery_target_timeline_string, "") != 0)
{
! if (recovery_target_timeline)
{
/* Timeline 1 does not have a history file, all else should */
! if (recovery_target_timeline != 1 && !existsTimeLineHistory(recovery_target_timeline))
ereport(FATAL,
(errmsg("recovery target timeline %u does not exist",
! recovery_target_timeline)));
! recoveryTargetTLI = recovery_target_timeline;
recoveryTargetIsLatest = false;
}
else
***************
*** 5646,5652 **** recoveryStopsHere(XLogRecord *record, bool *includeThis)
return false;
/* Do we have a PITR target at all? */
! if (recoveryTarget == RECOVERY_TARGET_UNSET)
{
/*
* Save timestamp of latest transaction commit/abort if this is a
--- 5512,5518 ----
return false;
/* Do we have a PITR target at all? */
! if (recovery_target == RECOVERY_TARGET_UNSET)
{
/*
* Save timestamp of latest transaction commit/abort if this is a
***************
*** 5657,5663 **** recoveryStopsHere(XLogRecord *record, bool *includeThis)
return false;
}
! if (recoveryTarget == RECOVERY_TARGET_XID)
{
/*
* There can be only one transaction end record with this exact
--- 5523,5529 ----
return false;
}
! if (recovery_target == RECOVERY_TARGET_XID)
{
/*
* There can be only one transaction end record with this exact
***************
*** 5668,5687 **** recoveryStopsHere(XLogRecord *record, bool *includeThis)
* they complete. A higher numbered xid will complete before you about
* 50% of the time...
*/
! stopsHere = (record->xl_xid == recoveryTargetXid);
if (stopsHere)
! *includeThis = recoveryTargetInclusive;
}
! else if (recoveryTarget == RECOVERY_TARGET_NAME)
{
/*
* There can be many restore points that share the same name, so we
* stop at the first one
*/
! stopsHere = (strcmp(recordRPName, recoveryTargetName) == 0);
/*
! * Ignore recoveryTargetInclusive because this is not a transaction
* record
*/
*includeThis = false;
--- 5534,5553 ----
* they complete. A higher numbered xid will complete before you about
* 50% of the time...
*/
! stopsHere = (record->xl_xid == recovery_target_xid);
if (stopsHere)
! *includeThis = recovery_target_inclusive;
}
! else if (recovery_target == RECOVERY_TARGET_NAME)
{
/*
* There can be many restore points that share the same name, so we
* stop at the first one
*/
! stopsHere = (strcmp(recordRPName, recovery_target_name) == 0);
/*
! * Ignore recovery_target_inclusive because this is not a transaction
* record
*/
*includeThis = false;
***************
*** 5693,5708 **** recoveryStopsHere(XLogRecord *record, bool *includeThis)
* we stop after the last one, if we are inclusive, or stop at the
* first one if we are exclusive
*/
! if (recoveryTargetInclusive)
! stopsHere = (recordXtime > recoveryTargetTime);
else
! stopsHere = (recordXtime >= recoveryTargetTime);
if (stopsHere)
*includeThis = false;
}
if (stopsHere)
{
recoveryStopXid = record->xl_xid;
recoveryStopTime = recordXtime;
recoveryStopAfter = *includeThis;
--- 5559,5575 ----
* we stop after the last one, if we are inclusive, or stop at the
* first one if we are exclusive
*/
! if (recovery_target_inclusive)
! stopsHere = (recordXtime > recovery_target_time);
else
! stopsHere = (recordXtime >= recovery_target_time);
if (stopsHere)
*includeThis = false;
}
if (stopsHere)
{
+ recoveryStopTarget = recovery_target;
recoveryStopXid = record->xl_xid;
recoveryStopTime = recordXtime;
recoveryStopAfter = *includeThis;
***************
*** 6003,6008 **** CheckRequiredParameterValues(void)
--- 5870,5888 ----
}
/*
+ * Check to see if restore_command must be set for archive recovery
+ * when standby mode is not enabled
+ */
+ static void
+ CheckRestoreCommandSet(void)
+ {
+ if (InArchiveRecovery && !standby_mode && !restore_command[0])
+ ereport(FATAL,
+ (errmsg("restore_command must be specified for archive recovery "
+ "when standby mode is not enabled")));
+ }
+
+ /*
* This must be called ONCE during postmaster or standalone-backend startup
*/
void
***************
*** 6122,6148 **** StartupXLOG(void)
* see them
*/
XLogCtl->RecoveryTargetTLI = recoveryTargetTLI;
! strncpy(XLogCtl->archiveCleanupCommand,
! archiveCleanupCommand ? archiveCleanupCommand : "",
! sizeof(XLogCtl->archiveCleanupCommand));
if (InArchiveRecovery)
{
! if (StandbyMode)
ereport(LOG,
(errmsg("entering standby mode")));
- else if (recoveryTarget == RECOVERY_TARGET_XID)
- ereport(LOG,
- (errmsg("starting point-in-time recovery to XID %u",
- recoveryTargetXid)));
- else if (recoveryTarget == RECOVERY_TARGET_TIME)
- ereport(LOG,
- (errmsg("starting point-in-time recovery to %s",
- timestamptz_to_str(recoveryTargetTime))));
- else if (recoveryTarget == RECOVERY_TARGET_NAME)
- ereport(LOG,
- (errmsg("starting point-in-time recovery to \"%s\"",
- recoveryTargetName)));
else
ereport(LOG,
(errmsg("starting archive recovery")));
--- 6002,6016 ----
* see them
*/
XLogCtl->RecoveryTargetTLI = recoveryTargetTLI;
! strncpy(XLogCtl->archive_cleanup_command,
! archive_cleanup_command,
! sizeof(XLogCtl->archive_cleanup_command));
if (InArchiveRecovery)
{
! if (standby_mode)
ereport(LOG,
(errmsg("entering standby mode")));
else
ereport(LOG,
(errmsg("starting archive recovery")));
***************
*** 6152,6158 **** StartupXLOG(void)
* Take ownership of the wakeup latch if we're going to sleep during
* recovery.
*/
! if (StandbyMode)
OwnLatch(&XLogCtl->recoveryWakeupLatch);
if (read_backup_label(&checkPointLoc, &backupEndRequired))
--- 6020,6026 ----
* Take ownership of the wakeup latch if we're going to sleep during
* recovery.
*/
! if (standby_mode)
OwnLatch(&XLogCtl->recoveryWakeupLatch);
if (read_backup_label(&checkPointLoc, &backupEndRequired))
***************
*** 6210,6216 **** StartupXLOG(void)
(errmsg("checkpoint record is at %X/%X",
checkPointLoc.xlogid, checkPointLoc.xrecoff)));
}
! else if (StandbyMode)
{
/*
* The last valid checkpoint record required for a streaming
--- 6078,6084 ----
(errmsg("checkpoint record is at %X/%X",
checkPointLoc.xlogid, checkPointLoc.xrecoff)));
}
! else if (standby_mode)
{
/*
* The last valid checkpoint record required for a streaming
***************
*** 6563,6569 **** StartupXLOG(void)
* Pause only if users can connect to send a resume
* message
*/
! if (recoveryPauseAtTarget && standbyState == STANDBY_SNAPSHOT_READY)
{
SetRecoveryPause(true);
recoveryPausesHere();
--- 6431,6437 ----
* Pause only if users can connect to send a resume
* message
*/
! if (pause_at_recovery_target && standbyState == STANDBY_SNAPSHOT_READY)
{
SetRecoveryPause(true);
recoveryPausesHere();
***************
*** 6662,6668 **** StartupXLOG(void)
* We don't need the latch anymore. It's not strictly necessary to disown
* it, but let's do it for the sake of tidiness.
*/
! if (StandbyMode)
DisownLatch(&XLogCtl->recoveryWakeupLatch);
/*
--- 6530,6536 ----
* We don't need the latch anymore. It's not strictly necessary to disown
* it, but let's do it for the sake of tidiness.
*/
! if (standby_mode)
DisownLatch(&XLogCtl->recoveryWakeupLatch);
/*
***************
*** 6670,6676 **** StartupXLOG(void)
* recovery to force fetching the files (which would be required at end of
* recovery, e.g., timeline history file) from archive or pg_xlog.
*/
! StandbyMode = false;
/*
* Re-fetch the last valid or last applied record, so we can identify the
--- 6538,6544 ----
* recovery to force fetching the files (which would be required at end of
* recovery, e.g., timeline history file) from archive or pg_xlog.
*/
! SetConfigOption("standby_mode", "false", PGC_POSTMASTER, PGC_S_OVERRIDE);
/*
* Re-fetch the last valid or last applied record, so we can identify the
***************
*** 6863,6870 **** StartupXLOG(void)
/*
* And finally, execute the recovery_end_command, if any.
*/
! if (recoveryEndCommand)
! ExecuteRecoveryCommand(recoveryEndCommand,
"recovery_end_command",
true);
}
--- 6731,6738 ----
/*
* And finally, execute the recovery_end_command, if any.
*/
! if (recovery_end_command[0])
! ExecuteRecoveryCommand(recovery_end_command,
"recovery_end_command",
true);
}
***************
*** 8187,8194 **** CreateRestartPoint(int flags)
/*
* Finally, execute archive_cleanup_command, if any.
*/
! if (XLogCtl->archiveCleanupCommand[0])
! ExecuteRecoveryCommand(XLogCtl->archiveCleanupCommand,
"archive_cleanup_command",
false);
--- 8055,8062 ----
/*
* Finally, execute archive_cleanup_command, if any.
*/
! if (XLogCtl->archive_cleanup_command[0])
! ExecuteRecoveryCommand(XLogCtl->archive_cleanup_command,
"archive_cleanup_command",
false);
***************
*** 10019,10024 **** HandleStartupProcInterrupts(void)
--- 9887,9903 ----
{
got_SIGHUP = false;
ProcessConfigFile(PGC_SIGHUP);
+
+ /* Check for compulsory parameter */
+ CheckRestoreCommandSet();
+
+ /*
+ * If primary_conninfo has been changed while walreceiver
+ * is running, stop walreceiver so that it restarts replication
+ * using new primary_conninfo.
+ */
+ if (IsPrimaryConninfoChanged() && WalRcvInProgress())
+ ShutdownWalRcv();
}
/*
***************
*** 10143,10149 **** XLogPageRead(XLogRecPtr *RecPtr, int emode, bool fetching_ckpt,
* Signal bgwriter to start a restartpoint if we've replayed too much
* xlog since the last one.
*/
! if (StandbyMode && bgwriterLaunched)
{
if (XLogCheckpointNeeded(readId, readSeg))
{
--- 10022,10028 ----
* Signal bgwriter to start a restartpoint if we've replayed too much
* xlog since the last one.
*/
! if (standby_mode && bgwriterLaunched)
{
if (XLogCheckpointNeeded(readId, readSeg))
{
***************
*** 10165,10171 **** retry:
if (readFile < 0 ||
(readSource == XLOG_FROM_STREAM && !XLByteLT(*RecPtr, receivedUpto)))
{
! if (StandbyMode)
{
/*
* In standby mode, wait for the requested record to become
--- 10044,10050 ----
if (readFile < 0 ||
(readSource == XLOG_FROM_STREAM && !XLByteLT(*RecPtr, receivedUpto)))
{
! if (standby_mode)
{
/*
* In standby mode, wait for the requested record to become
***************
*** 10329,10339 **** retry:
* backwards to start redo at RedoStartLSN, we will
* have the logs streamed already.
*/
! if (PrimaryConnInfo)
{
RequestXLogStreaming(
fetching_ckpt ? RedoStartLSN : *RecPtr,
! PrimaryConnInfo);
continue;
}
}
--- 10208,10218 ----
* backwards to start redo at RedoStartLSN, we will
* have the logs streamed already.
*/
! if (primary_conninfo[0])
{
RequestXLogStreaming(
fetching_ckpt ? RedoStartLSN : *RecPtr,
! primary_conninfo);
continue;
}
}
***************
*** 10476,10482 **** next_record_is_invalid:
readSource = 0;
/* In standby-mode, keep trying */
! if (StandbyMode)
goto retry;
else
return false;
--- 10355,10361 ----
readSource = 0;
/* In standby-mode, keep trying */
! if (standby_mode)
goto retry;
else
return false;
***************
*** 10548,10562 **** CheckForStandbyTrigger(void)
return true;
}
! if (TriggerFile == NULL)
return false;
! if (stat(TriggerFile, &stat_buf) == 0)
{
ereport(LOG,
! (errmsg("trigger file found: %s", TriggerFile)));
ShutdownWalRcv();
! unlink(TriggerFile);
triggered = true;
return true;
}
--- 10427,10441 ----
return true;
}
! if (!trigger_file[0])
return false;
! if (stat(trigger_file, &stat_buf) == 0)
{
ereport(LOG,
! (errmsg("trigger file found: %s", trigger_file)));
ShutdownWalRcv();
! unlink(trigger_file);
triggered = true;
return true;
}
*** a/src/backend/replication/walreceiverfuncs.c
--- b/src/backend/replication/walreceiverfuncs.c
***************
*** 109,114 **** WalRcvInProgress(void)
--- 109,131 ----
}
/*
+ * Is the current primary_conninfo different from that which walreceiver is using?
+ */
+ bool
+ IsPrimaryConninfoChanged(void)
+ {
+ /* use volatile pointer to prevent code rearrangement */
+ volatile WalRcvData *walrcv = WalRcv;
+ char conninfo[MAXCONNINFO];
+
+ SpinLockAcquire(&walrcv->mutex);
+ strlcpy(conninfo, (char *) walrcv->conninfo, MAXCONNINFO);
+ SpinLockRelease(&walrcv->mutex);
+
+ return strncmp(conninfo, primary_conninfo, MAXCONNINFO) != 0;
+ }
+
+ /*
* Stop walreceiver (if running) and wait for it to die.
*/
void
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 30,35 ****
--- 30,36 ----
#include "access/transam.h"
#include "access/twophase.h"
#include "access/xact.h"
+ #include "access/xlog_internal.h"
#include "catalog/namespace.h"
#include "commands/async.h"
#include "commands/prepare.h"
***************
*** 205,210 **** static bool check_application_name(char **newval, void **extra, GucSource source
--- 206,219 ----
static void assign_application_name(const char *newval, void *extra);
static const char *show_unix_socket_permissions(void);
static const char *show_log_file_mode(void);
+ static bool check_recovery_target_xid(char **newval, void **extra, GucSource source);
+ static void assign_recovery_target_xid(const char *newval, void *extra);
+ static bool check_recovery_target_name(char **newval, void **extra, GucSource source);
+ static void assign_recovery_target_name(const char *newval, void *extra);
+ static bool check_recovery_target_time(char **newval, void **extra, GucSource source);
+ static void assign_recovery_target_time(const char *newval, void *extra);
+ static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source);
+ static void assign_recovery_target_timeline(const char *newval, void *extra);
static char *config_enum_get_options(struct config_enum * record,
const char *prefix, const char *suffix,
***************
*** 476,481 **** static int wal_block_size;
--- 485,492 ----
static int wal_segment_size;
static bool integer_datetimes;
static int effective_io_concurrency;
+ static char *recovery_target_xid_string;
+ static char *recovery_target_time_string;
/* should be static, but commands/variable.c needs to get at this */
char *role_string;
***************
*** 555,560 **** const char *const config_group_names[] =
--- 566,575 ----
gettext_noop("Write-Ahead Log / Checkpoints"),
/* WAL_ARCHIVING */
gettext_noop("Write-Ahead Log / Archiving"),
+ /* WAL_ARCHIVE_RECOVERY */
+ gettext_noop("Write-Ahead Log / Archive Recovery"),
+ /* WAL_RECOVERY_TARGET */
+ gettext_noop("Write-Ahead Log / Recovery Target"),
/* REPLICATION */
gettext_noop("Replication"),
/* REPLICATION_SENDING */
***************
*** 1365,1370 **** static struct config_bool ConfigureNamesBool[] =
--- 1380,1415 ----
},
{
+ {"recovery_target_inclusive", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+ gettext_noop("Sets whether to include or exclude transaction with recovery target."),
+ NULL
+ },
+ &recovery_target_inclusive,
+ true,
+ NULL, NULL, NULL
+ },
+
+ {
+ {"pause_at_recovery_target", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+ gettext_noop("Sets whether recovery should pause when the recovery target is reached."),
+ NULL
+ },
+ &pause_at_recovery_target,
+ true,
+ NULL, NULL, NULL
+ },
+
+ {
+ {"standby_mode", PGC_POSTMASTER, REPLICATION_STANDBY,
+ gettext_noop("Sets whether to start the server as a standby."),
+ NULL
+ },
+ &standby_mode,
+ false,
+ NULL, NULL, NULL
+ },
+
+ {
{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY,
gettext_noop("Allows connections and queries during recovery."),
NULL
***************
*** 2521,2526 **** static struct config_string ConfigureNamesString[] =
--- 2566,2661 ----
},
{
+ {"restore_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+ gettext_noop("Sets the shell command that will retrieve an archived WAL file."),
+ NULL
+ },
+ &restore_command,
+ "",
+ NULL, NULL, NULL
+ },
+
+ {
+ {"archive_cleanup_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+ gettext_noop("Sets the shell command that will be executed at every restartpoint."),
+ NULL
+ },
+ &archive_cleanup_command,
+ "",
+ NULL, NULL, NULL
+ },
+
+ {
+ {"recovery_end_command", PGC_SIGHUP, WAL_ARCHIVE_RECOVERY,
+ gettext_noop("Sets the shell command that will be executed once only at the end of recovery."),
+ NULL
+ },
+ &recovery_end_command,
+ "",
+ NULL, NULL, NULL
+ },
+
+ {
+ {"recovery_target_xid", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+ gettext_noop("Sets the transaction ID up to which recovery will proceed."),
+ NULL
+ },
+ &recovery_target_xid_string,
+ "",
+ check_recovery_target_xid, assign_recovery_target_xid, NULL
+ },
+
+ {
+ {"recovery_target_name", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+ gettext_noop("Sets the named restore point."),
+ NULL
+ },
+ &recovery_target_name,
+ "",
+ check_recovery_target_name, assign_recovery_target_name, NULL
+ },
+
+ {
+ {"recovery_target_time", PGC_SIGHUP, WAL_RECOVERY_TARGET,
+ gettext_noop("Sets the time stamp up to which recovery will proceed."),
+ NULL
+ },
+ &recovery_target_time_string,
+ "",
+ check_recovery_target_time, assign_recovery_target_time, NULL
+ },
+
+ {
+ {"recovery_target_timeline", PGC_POSTMASTER, WAL_RECOVERY_TARGET,
+ gettext_noop("Sets recoverying into a particular timeline."),
+ NULL
+ },
+ &recovery_target_timeline_string,
+ "",
+ check_recovery_target_timeline, assign_recovery_target_timeline, NULL
+ },
+
+ {
+ {"primary_conninfo", PGC_SIGHUP, REPLICATION_STANDBY,
+ gettext_noop("Sets the connection string to be used to connect with the primary."),
+ NULL
+ },
+ &primary_conninfo,
+ "",
+ NULL, NULL, NULL
+ },
+
+ {
+ {"trigger_file", PGC_SIGHUP, REPLICATION_STANDBY,
+ gettext_noop("Sets the trigger file whose presence ends recovery in the standby."),
+ NULL
+ },
+ &trigger_file,
+ "",
+ NULL, NULL, NULL
+ },
+
+ {
{"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE,
gettext_noop("Sets the client's character set encoding."),
NULL,
***************
*** 8678,8681 **** show_log_file_mode(void)
--- 8813,8965 ----
return buf;
}
+ static bool
+ check_recovery_target_xid(char **newval, void **extra, GucSource source)
+ {
+ TransactionId xid;
+ TransactionId *myextra;
+
+ errno = 0;
+ xid = (TransactionId) strtoul(*newval, NULL, 0);
+ if (errno == EINVAL || errno == ERANGE)
+ {
+ GUC_check_errdetail("recovery_target_xid is not a valid number: \"%s\"",
+ *newval);
+ return false;
+ }
+
+ myextra = (TransactionId *) guc_malloc(ERROR, sizeof(TransactionId));
+ *myextra = xid;
+ *extra = (void *) myextra;
+
+ return true;
+ }
+
+ static void
+ assign_recovery_target_xid(const char *newval, void *extra)
+ {
+ recovery_target_xid = *((TransactionId *) extra);
+
+ if (recovery_target_xid != InvalidTransactionId)
+ recovery_target = RECOVERY_TARGET_XID;
+ else if (recovery_target_name[0])
+ recovery_target = RECOVERY_TARGET_NAME;
+ else if (recovery_target_time != 0)
+ recovery_target = RECOVERY_TARGET_TIME;
+ else
+ recovery_target = RECOVERY_TARGET_UNSET;
+ }
+
+ static bool
+ check_recovery_target_name(char **newval, void **extra, GucSource source)
+ {
+ if (strlen(*newval) >= MAXFNAMELEN)
+ {
+ GUC_check_errdetail("\"recovery_target_name\" is too long (maximum %d characters)",
+ MAXFNAMELEN - 1);
+ return false;
+ }
+ return true;
+ }
+
+ static void
+ assign_recovery_target_name(const char *newval, void *extra)
+ {
+ if (recovery_target_xid != InvalidTransactionId)
+ recovery_target = RECOVERY_TARGET_XID;
+ else if (newval[0])
+ recovery_target = RECOVERY_TARGET_NAME;
+ else if (recovery_target_time != 0)
+ recovery_target = RECOVERY_TARGET_TIME;
+ else
+ recovery_target = RECOVERY_TARGET_UNSET;
+ }
+
+ static bool
+ check_recovery_target_time(char **newval, void **extra, GucSource source)
+ {
+ TimestampTz time;
+ TimestampTz *myextra;
+ MemoryContext oldcontext = CurrentMemoryContext;
+
+ PG_TRY();
+ {
+ time = (strcmp(*newval, "") == 0) ?
+ 0 :
+ DatumGetTimestampTz(DirectFunctionCall3(timestamptz_in,
+ CStringGetDatum(*newval),
+ ObjectIdGetDatum(InvalidOid),
+ Int32GetDatum(-1)));
+ }
+ PG_CATCH();
+ {
+ ErrorData *edata;
+
+ /* Save error info */
+ MemoryContextSwitchTo(oldcontext);
+ edata = CopyErrorData();
+ FlushErrorState();
+
+ /* Pass the error message */
+ GUC_check_errdetail("%s", edata->message);
+ FreeErrorData(edata);
+ return false;
+ }
+ PG_END_TRY();
+
+ myextra = (TimestampTz *) guc_malloc(ERROR, sizeof(TimestampTz));
+ *myextra = time;
+ *extra = (void *) myextra;
+
+ return true;
+ }
+
+ static void
+ assign_recovery_target_time(const char *newval, void *extra)
+ {
+ recovery_target_time = *((TimestampTz *) extra);
+
+ if (recovery_target_xid != InvalidTransactionId)
+ recovery_target = RECOVERY_TARGET_XID;
+ else if (recovery_target_name[0])
+ recovery_target = RECOVERY_TARGET_NAME;
+ else if (recovery_target_time != 0)
+ recovery_target = RECOVERY_TARGET_TIME;
+ else
+ recovery_target = RECOVERY_TARGET_UNSET;
+ }
+
+ static bool
+ check_recovery_target_timeline(char **newval, void **extra, GucSource source)
+ {
+ TimeLineID tli = 0;
+ TimeLineID *myextra;
+
+ if (strcmp(*newval, "latest") == 0)
+ tli = 0;
+ else
+ {
+ errno = 0;
+ tli = (TimeLineID) strtoul(*newval, NULL, 0);
+ if (errno == EINVAL || errno == ERANGE)
+ {
+ GUC_check_errdetail("recovery_target_timeline is not a valid number: \"%s\"",
+ *newval);
+ return false;
+ }
+ }
+
+ myextra = (TimeLineID *) guc_malloc(ERROR, sizeof(TimeLineID));
+ *myextra = tli;
+ *extra = (void *) myextra;
+
+ return true;
+ }
+
+ static void
+ assign_recovery_target_timeline(const char *newval, void *extra)
+ {
+ recovery_target_timeline = *((TimeLineID *) extra);
+ }
+
#include "guc-file.c"
*** a/src/backend/utils/misc/postgresql.conf.sample
--- b/src/backend/utils/misc/postgresql.conf.sample
***************
*** 192,197 ****
--- 192,213 ----
#archive_timeout = 0 # force a logfile segment switch after this
# number of seconds; 0 disables
+ # - Archive Recovery -
+ #restore_command = '' # command to use to restore an archived logfile segment
+ # placeholders: %p = path of file to restore
+ # %f = file name only
+ # e.g. 'cp /mnt/server/archivedir/%f %p'
+ #archive_cleanup_command = '' # command to execute at every restartpoint
+ #recovery_end_command = '' # command to execute at completion of recovery
+
+ # - Recovery Target -
+ #recovery_target_xid = ''
+ #recovery_target_name = ''
+ #recovery_target_time = ''
+ #recovery_target_inclusive = on
+ #pause_at_recovery_target = on
+ #recovery_target_timeline = ''
+
#------------------------------------------------------------------------------
# REPLICATION
***************
*** 219,224 ****
--- 235,244 ----
# These settings are ignored on a master server
+ #standby_mode = off # "on" starts the server as a standby
+ # (change requires restart)
+ #primary_conninfo = '' # connection string to connect to the master
+ #trigger_file = '' # trigger file to promote the standby
#hot_standby = off # "on" allows queries during recovery
# (change requires restart)
#max_standby_archive_delay = 30s # max delay before canceling queries
*** a/src/include/access/xlog.h
--- b/src/include/access/xlog.h
***************
*** 198,203 **** extern bool XLogArchiveMode;
--- 198,217 ----
extern char *XLogArchiveCommand;
extern bool EnableHotStandby;
extern bool log_checkpoints;
+ extern char *restore_command;
+ extern char *archive_cleanup_command;
+ extern char *recovery_end_command;
+ extern bool standby_mode;
+ extern char *primary_conninfo;
+ extern char *trigger_file;
+ extern RecoveryTargetType recovery_target;
+ extern TransactionId recovery_target_xid;
+ extern TimestampTz recovery_target_time;
+ extern char *recovery_target_name;
+ extern bool recovery_target_inclusive;
+ extern bool pause_at_recovery_target;
+ extern char *recovery_target_timeline_string;
+ extern TimeLineID recovery_target_timeline;
/* WAL levels */
typedef enum WalLevel
*** a/src/include/replication/walreceiver.h
--- b/src/include/replication/walreceiver.h
***************
*** 110,115 **** extern Size WalRcvShmemSize(void);
--- 110,116 ----
extern void WalRcvShmemInit(void);
extern void ShutdownWalRcv(void);
extern bool WalRcvInProgress(void);
+ extern bool IsPrimaryConninfoChanged(void);
extern void RequestXLogStreaming(XLogRecPtr recptr, const char *conninfo);
extern XLogRecPtr GetWalRcvWriteRecPtr(XLogRecPtr *latestChunkStart);
*** a/src/include/utils/guc_tables.h
--- b/src/include/utils/guc_tables.h
***************
*** 68,73 **** enum config_group
--- 68,75 ----
WAL_SETTINGS,
WAL_CHECKPOINTS,
WAL_ARCHIVING,
+ WAL_ARCHIVE_RECOVERY,
+ WAL_RECOVERY_TARGET,
REPLICATION,
REPLICATION_SENDING,
REPLICATION_MASTER,