@@ -228,7 +228,7 @@ static char *recoveryEndCommand = NULL;
228228static char * archiveCleanupCommand = NULL ;
229229static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET ;
230230static bool recoveryTargetInclusive = true;
231- static bool recoveryPauseAtTarget = true ;
231+ static RecoveryTargetAction actionAtRecoveryTarget = RECOVERY_TARGET_ACTION_PAUSE ;
232232static TransactionId recoveryTargetXid ;
233233static TimestampTz recoveryTargetTime ;
234234static char * recoveryTargetName ;
@@ -4647,6 +4647,9 @@ readRecoveryCommandFile(void)
46474647 ConfigVariable * item ,
46484648 * head = NULL ,
46494649 * tail = NULL ;
4650+ bool recoveryPauseAtTargetSet = false;
4651+ bool actionAtRecoveryTargetSet = false;
4652+
46504653
46514654 fd = AllocateFile (RECOVERY_COMMAND_FILE , "r" );
46524655 if (fd == NULL )
@@ -4692,13 +4695,43 @@ readRecoveryCommandFile(void)
46924695 }
46934696 else if (strcmp (item -> name , "pause_at_recovery_target" ) == 0 )
46944697 {
4698+ bool recoveryPauseAtTarget ;
4699+
46954700 if (!parse_bool (item -> value , & recoveryPauseAtTarget ))
46964701 ereport (ERROR ,
46974702 (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
46984703 errmsg ("parameter \"%s\" requires a Boolean value" , "pause_at_recovery_target" )));
4704+
46994705 ereport (DEBUG2 ,
47004706 (errmsg_internal ("pause_at_recovery_target = '%s'" ,
47014707 item -> value )));
4708+
4709+ actionAtRecoveryTarget = recoveryPauseAtTarget ?
4710+ RECOVERY_TARGET_ACTION_PAUSE :
4711+ RECOVERY_TARGET_ACTION_PROMOTE ;
4712+
4713+ recoveryPauseAtTargetSet = true;
4714+ }
4715+ else if (strcmp (item -> name , "action_at_recovery_target" ) == 0 )
4716+ {
4717+ if (strcmp (item -> value , "pause" ) == 0 )
4718+ actionAtRecoveryTarget = RECOVERY_TARGET_ACTION_PAUSE ;
4719+ else if (strcmp (item -> value , "promote" ) == 0 )
4720+ actionAtRecoveryTarget = RECOVERY_TARGET_ACTION_PROMOTE ;
4721+ else if (strcmp (item -> value , "shutdown" ) == 0 )
4722+ actionAtRecoveryTarget = RECOVERY_TARGET_ACTION_SHUTDOWN ;
4723+ else
4724+ ereport (ERROR ,
4725+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
4726+ errmsg ("invalid value for recovery parameter \"%s\"" ,
4727+ "action_at_recovery_target" ),
4728+ errhint ("The allowed values are \"pause\", \"promote\" and \"shutdown\"." )));
4729+
4730+ ereport (DEBUG2 ,
4731+ (errmsg_internal ("action_at_recovery_target = '%s'" ,
4732+ item -> value )));
4733+
4734+ actionAtRecoveryTargetSet = true;
47024735 }
47034736 else if (strcmp (item -> name , "recovery_target_timeline" ) == 0 )
47044737 {
@@ -4863,6 +4896,28 @@ readRecoveryCommandFile(void)
48634896 RECOVERY_COMMAND_FILE )));
48644897 }
48654898
4899+ /*
4900+ * Check for mutually exclusive parameters
4901+ */
4902+ if (recoveryPauseAtTargetSet && actionAtRecoveryTargetSet )
4903+ ereport (ERROR ,
4904+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
4905+ errmsg ("cannot set both \"%s\" and \"%s\" recovery parameters" ,
4906+ "pause_at_recovery_target" ,
4907+ "action_at_recovery_target" ),
4908+ errhint ("The \"pause_at_recovery_target\" is deprecated." )));
4909+
4910+
4911+ /*
4912+ * Override any inconsistent requests. Not that this is a change
4913+ * of behaviour in 9.5; prior to this we simply ignored a request
4914+ * to pause if hot_standby = off, which was surprising behaviour.
4915+ */
4916+ if (actionAtRecoveryTarget == RECOVERY_TARGET_ACTION_PAUSE &&
4917+ actionAtRecoveryTargetSet &&
4918+ standbyState == STANDBY_DISABLED )
4919+ actionAtRecoveryTarget = RECOVERY_TARGET_ACTION_SHUTDOWN ;
4920+
48664921 /* Enable fetching from archive recovery area */
48674922 ArchiveRecoveryRequested = true;
48684923
@@ -6415,10 +6470,37 @@ StartupXLOG(void)
64156470 * end of main redo apply loop
64166471 */
64176472
6418- if (recoveryPauseAtTarget && reachedStopPoint )
6473+ if (reachedStopPoint )
64196474 {
6420- SetRecoveryPause (true);
6421- recoveryPausesHere ();
6475+ if (!reachedConsistency )
6476+ ereport (FATAL ,
6477+ (errmsg ("requested recovery stop point is before consistent recovery point" )));
6478+
6479+ /*
6480+ * This is the last point where we can restart recovery with a
6481+ * new recovery target, if we shutdown and begin again. After
6482+ * this, Resource Managers may choose to do permanent corrective
6483+ * actions at end of recovery.
6484+ */
6485+ switch (actionAtRecoveryTarget )
6486+ {
6487+ case RECOVERY_TARGET_ACTION_SHUTDOWN :
6488+ /*
6489+ * exit with special return code to request shutdown
6490+ * of postmaster. Log messages issued from
6491+ * postmaster.
6492+ */
6493+ proc_exit (3 );
6494+
6495+ case RECOVERY_TARGET_ACTION_PAUSE :
6496+ SetRecoveryPause (true);
6497+ recoveryPausesHere ();
6498+
6499+ /* drop into promote */
6500+
6501+ case RECOVERY_TARGET_ACTION_PROMOTE :
6502+ break ;
6503+ }
64226504 }
64236505
64246506 /* Allow resource managers to do any required cleanup. */
@@ -6436,6 +6518,7 @@ StartupXLOG(void)
64366518 ereport (LOG ,
64376519 (errmsg ("last completed transaction was at log time %s" ,
64386520 timestamptz_to_str (xtime ))));
6521+
64396522 InRedo = false;
64406523 }
64416524 else
@@ -6496,13 +6579,6 @@ StartupXLOG(void)
64966579 (EndOfLog < minRecoveryPoint ||
64976580 !XLogRecPtrIsInvalid (ControlFile -> backupStartPoint )))
64986581 {
6499- if (reachedStopPoint )
6500- {
6501- /* stopped because of stop request */
6502- ereport (FATAL ,
6503- (errmsg ("requested recovery stop point is before consistent recovery point" )));
6504- }
6505-
65066582 /*
65076583 * Ran off end of WAL before reaching end-of-backup WAL record, or
65086584 * minRecoveryPoint. That's usually a bad sign, indicating that you
0 commit comments