mirror of
				https://github.com/postgres/postgres.git
				synced 2025-10-29 22:49:41 +03:00 
			
		
		
		
	Integrate recovery.conf into postgresql.conf
recovery.conf settings are now set in postgresql.conf (or other GUC sources). Currently, all the affected settings are PGC_POSTMASTER; this could be refined in the future case by case. Recovery is now initiated by a file recovery.signal. Standby mode is initiated by a file standby.signal. The standby_mode setting is gone. If a recovery.conf file is found, an error is issued. The trigger_file setting has been renamed to promote_trigger_file as part of the move. The documentation chapter "Recovery Configuration" has been integrated into "Server Configuration". pg_basebackup -R now appends settings to postgresql.auto.conf and creates a standby.signal file. Author: Fujii Masao <masao.fujii@gmail.com> Author: Simon Riggs <simon@2ndquadrant.com> Author: Abhijit Menon-Sen <ams@2ndquadrant.com> Author: Sergei Kornilov <sk@zsrv.org> Discussion: https://www.postgresql.org/message-id/flat/607741529606767@web3g.yandex.ru/
This commit is contained in:
		| @@ -209,7 +209,6 @@ endif | ||||
| 	$(INSTALL_DATA) $(srcdir)/libpq/pg_hba.conf.sample '$(DESTDIR)$(datadir)/pg_hba.conf.sample' | ||||
| 	$(INSTALL_DATA) $(srcdir)/libpq/pg_ident.conf.sample '$(DESTDIR)$(datadir)/pg_ident.conf.sample' | ||||
| 	$(INSTALL_DATA) $(srcdir)/utils/misc/postgresql.conf.sample '$(DESTDIR)$(datadir)/postgresql.conf.sample' | ||||
| 	$(INSTALL_DATA) $(srcdir)/access/transam/recovery.conf.sample '$(DESTDIR)$(datadir)/recovery.conf.sample' | ||||
|  | ||||
| ifeq ($(with_llvm), yes) | ||||
| install-bin: install-postgres-bitcode | ||||
| @@ -274,8 +273,7 @@ endif | ||||
| 	$(MAKE) -C utils uninstall-data | ||||
| 	rm -f '$(DESTDIR)$(datadir)/pg_hba.conf.sample' \ | ||||
| 	      '$(DESTDIR)$(datadir)/pg_ident.conf.sample' \ | ||||
| 	      '$(DESTDIR)$(datadir)/postgresql.conf.sample' \ | ||||
| 	      '$(DESTDIR)$(datadir)/recovery.conf.sample' | ||||
| 	      '$(DESTDIR)$(datadir)/postgresql.conf.sample' | ||||
| ifeq ($(with_llvm), yes) | ||||
| 	$(call uninstall_llvm_module,postgres) | ||||
| endif | ||||
|   | ||||
| @@ -1,158 +0,0 @@ | ||||
| # ------------------------------- | ||||
| # PostgreSQL recovery config file | ||||
| # ------------------------------- | ||||
| # | ||||
| # Edit this file to provide the parameters that PostgreSQL needs to | ||||
| # perform an archive recovery of a database, or to act as a replication | ||||
| # standby. | ||||
| # | ||||
| # If "recovery.conf" is present in the PostgreSQL data directory, it is | ||||
| # read on postmaster startup.  After successful recovery, it is renamed | ||||
| # to "recovery.done" to ensure that we do not accidentally re-enter | ||||
| # archive recovery or standby mode. | ||||
| # | ||||
| # This file consists of lines of the form: | ||||
| # | ||||
| #   name = value | ||||
| # | ||||
| # Comments are introduced with '#'. | ||||
| # | ||||
| # The complete list of option names and allowed values can be found | ||||
| # in the PostgreSQL documentation. | ||||
| # | ||||
| #--------------------------------------------------------------------------- | ||||
| # ARCHIVE RECOVERY PARAMETERS | ||||
| #--------------------------------------------------------------------------- | ||||
| # | ||||
| # restore_command | ||||
| # | ||||
| # specifies the shell command that is executed to copy log files | ||||
| # back from archival storage.  The command string may contain %f, | ||||
| # which is replaced by the name of the desired log file, and %p, | ||||
| # which is replaced by the absolute path to copy the log file to. | ||||
| # | ||||
| # This parameter is *required* for an archive recovery, but optional | ||||
| # for streaming replication. | ||||
| # | ||||
| # It is important that the command return nonzero exit status on failure. | ||||
| # The command *will* be asked for log files that are not present in the | ||||
| # archive; it must return nonzero when so asked. | ||||
| # | ||||
| # NOTE that the basename of %p will be different from %f; do not | ||||
| # expect them to be interchangeable. | ||||
| # | ||||
| #restore_command = ''		# e.g. 'cp /mnt/server/archivedir/%f %p' | ||||
| # | ||||
| # | ||||
| # archive_cleanup_command | ||||
| # | ||||
| # specifies an optional shell command to execute at every restartpoint. | ||||
| # This can be useful for cleaning up the archive of a standby server. | ||||
| # | ||||
| #archive_cleanup_command = '' | ||||
| # | ||||
| # recovery_end_command | ||||
| # | ||||
| # specifies an optional shell command to execute at completion of recovery. | ||||
| # This can be useful for cleaning up after the restore_command. | ||||
| # | ||||
| #recovery_end_command = '' | ||||
| # | ||||
| #--------------------------------------------------------------------------- | ||||
| # RECOVERY TARGET PARAMETERS | ||||
| #--------------------------------------------------------------------------- | ||||
| # | ||||
| # By default, recovery will rollforward to the end of the WAL log. | ||||
| # If you want to stop rollforward at a specific point, you | ||||
| # must set a recovery target. | ||||
| # | ||||
| # You may set a recovery target either by transactionId, by name, by | ||||
| # timestamp or by WAL location (LSN). Recovery may either include or | ||||
| # exclude the transaction(s) with the recovery target value (i.e., | ||||
| # stop either just after or just before the given target, | ||||
| # respectively). | ||||
| # | ||||
| # | ||||
| #recovery_target_name = ''	# e.g. 'daily backup 2011-01-26' | ||||
| # | ||||
| #recovery_target_time = ''	# e.g. '2004-07-14 22:39:00 EST' | ||||
| # | ||||
| #recovery_target_xid = '' | ||||
| # | ||||
| #recovery_target_lsn = ''	# e.g. '0/70006B8' | ||||
| # | ||||
| #recovery_target_inclusive = true | ||||
| # | ||||
| # | ||||
| # Alternatively, you can request stopping as soon as a consistent state | ||||
| # is reached, by uncommenting this option. | ||||
| # | ||||
| #recovery_target = 'immediate' | ||||
| # | ||||
| # | ||||
| # If you want to recover into a timeline other than the "main line" shown in | ||||
| # pg_control, specify the timeline number here, or write 'latest' to get | ||||
| # the latest branch for which there's a history file. | ||||
| # | ||||
| #recovery_target_timeline = 'latest' | ||||
| # | ||||
| # | ||||
| # If recovery_target_action = 'pause', recovery will pause when the | ||||
| # recovery target is reached. The pause state will continue until | ||||
| # pg_wal_replay_resume() is called. This setting has no effect if | ||||
| # no recovery target is set. If hot_standby is not enabled then the | ||||
| # server will shutdown instead, though you may request this in | ||||
| # any case by specifying 'shutdown'. | ||||
| # | ||||
| #recovery_target_action = 'pause' | ||||
| # | ||||
| #--------------------------------------------------------------------------- | ||||
| # STANDBY SERVER PARAMETERS | ||||
| #--------------------------------------------------------------------------- | ||||
| # | ||||
| # standby_mode | ||||
| # | ||||
| # When standby_mode is enabled, the PostgreSQL server will work as a | ||||
| # standby. It will continuously wait for the additional XLOG records, using | ||||
| # restore_command and/or primary_conninfo. | ||||
| # | ||||
| #standby_mode = off | ||||
| # | ||||
| # primary_conninfo | ||||
| # | ||||
| # If set, the PostgreSQL server will try to connect to the primary using this | ||||
| # connection string and receive XLOG records continuously. | ||||
| # | ||||
| #primary_conninfo = ''		# e.g. 'host=localhost port=5432' | ||||
| # | ||||
| # If set, the PostgreSQL server will use the specified replication slot when | ||||
| # connecting to the primary via streaming replication to control resource | ||||
| # removal on the upstream node. This setting has no effect if primary_conninfo | ||||
| # is not set. | ||||
| # | ||||
| #primary_slot_name = '' | ||||
| # | ||||
| # By default, a standby server keeps restoring XLOG records from the | ||||
| # primary indefinitely. If you want to stop the standby mode, finish recovery | ||||
| # and open the system in read/write mode, specify a path to a trigger file. | ||||
| # The server will poll the trigger file path periodically and start as a | ||||
| # primary server when it's found. | ||||
| # | ||||
| #trigger_file = '' | ||||
| # | ||||
| # By default, a standby server restores XLOG records from the primary as | ||||
| # soon as possible. If you want to explicitly delay the replay of committed | ||||
| # transactions from the master, specify a minimum apply delay. For example, | ||||
| # if you set this parameter to 5min, the standby will replay each transaction | ||||
| # commit only when the system time on the standby is at least five minutes | ||||
| # past the commit time reported by the master. | ||||
| # | ||||
| #recovery_min_apply_delay = 0 | ||||
| # | ||||
| #--------------------------------------------------------------------------- | ||||
| # HOT STANDBY PARAMETERS | ||||
| #--------------------------------------------------------------------------- | ||||
| # | ||||
| # Hot Standby related parameters are listed in postgresql.conf | ||||
| # | ||||
| #--------------------------------------------------------------------------- | ||||
| @@ -69,7 +69,6 @@ | ||||
| #include "utils/builtins.h" | ||||
| #include "utils/guc.h" | ||||
| #include "utils/memutils.h" | ||||
| #include "utils/pg_lsn.h" | ||||
| #include "utils/ps_status.h" | ||||
| #include "utils/relmapper.h" | ||||
| #include "utils/snapmgr.h" | ||||
| @@ -78,6 +77,9 @@ | ||||
|  | ||||
| extern uint32 bootstrap_data_checksum_version; | ||||
|  | ||||
| /* Unsupported old recovery command file names (relative to $PGDATA) */ | ||||
| #define RECOVERY_COMMAND_FILE	"recovery.conf" | ||||
| #define RECOVERY_COMMAND_DONE	"recovery.done" | ||||
|  | ||||
| /* User-settable parameters */ | ||||
| int			max_wal_size_mb = 1024; /* 1 GB */ | ||||
| @@ -161,6 +163,13 @@ const struct config_enum_entry archive_mode_options[] = { | ||||
| 	{NULL, 0, false} | ||||
| }; | ||||
|  | ||||
| const struct config_enum_entry recovery_target_action_options[] = { | ||||
| 	{"pause", RECOVERY_TARGET_ACTION_PAUSE, false}, | ||||
| 	{"promote", RECOVERY_TARGET_ACTION_PROMOTE, false}, | ||||
| 	{"shutdown", RECOVERY_TARGET_ACTION_SHUTDOWN, false}, | ||||
| 	{NULL, 0, false} | ||||
| }; | ||||
|  | ||||
| /* | ||||
|  * Statistics for current checkpoint are collected in this global struct. | ||||
|  * Because only the checkpointer or a stand-alone backend can perform | ||||
| @@ -230,7 +239,7 @@ static int	LocalXLogInsertAllowed = -1; | ||||
|  | ||||
| /* | ||||
|  * When ArchiveRecoveryRequested is set, archive recovery was requested, | ||||
|  * ie. recovery.conf file was present. When InArchiveRecovery is set, we are | ||||
|  * ie. signal files were present. When InArchiveRecovery is set, we are | ||||
|  * currently recovering using offline XLOG archives. These variables are only | ||||
|  * valid in the startup process. | ||||
|  * | ||||
| @@ -242,6 +251,9 @@ static int	LocalXLogInsertAllowed = -1; | ||||
| bool		ArchiveRecoveryRequested = false; | ||||
| bool		InArchiveRecovery = false; | ||||
|  | ||||
| static bool standby_signal_file_found = false; | ||||
| static bool recovery_signal_file_found = false; | ||||
|  | ||||
| /* Was the last xlog file restored from archive, or local? */ | ||||
| static bool restoredFromArchive = false; | ||||
|  | ||||
| @@ -249,25 +261,25 @@ static bool restoredFromArchive = false; | ||||
| static char *replay_image_masked = NULL; | ||||
| static char *master_image_masked = NULL; | ||||
|  | ||||
| /* options taken from recovery.conf for archive recovery */ | ||||
| /* options formerly taken from recovery.conf for archive recovery */ | ||||
| char	   *recoveryRestoreCommand = NULL; | ||||
| static char *recoveryEndCommand = NULL; | ||||
| static char *archiveCleanupCommand = NULL; | ||||
| static RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; | ||||
| static bool recoveryTargetInclusive = true; | ||||
| static RecoveryTargetAction recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE; | ||||
| static TransactionId recoveryTargetXid; | ||||
| static TimestampTz recoveryTargetTime; | ||||
| static char *recoveryTargetName; | ||||
| static XLogRecPtr recoveryTargetLSN; | ||||
| static int	recovery_min_apply_delay = 0; | ||||
| static TimestampTz recoveryDelayUntilTime; | ||||
| char	   *recoveryEndCommand = NULL; | ||||
| char	   *archiveCleanupCommand = NULL; | ||||
| RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; | ||||
| bool		recoveryTargetInclusive = true; | ||||
| int			recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE; | ||||
| TransactionId recoveryTargetXid; | ||||
| TimestampTz recoveryTargetTime; | ||||
| char	   *recoveryTargetName; | ||||
| XLogRecPtr	recoveryTargetLSN; | ||||
| int			recovery_min_apply_delay = 0; | ||||
| TimestampTz recoveryDelayUntilTime; | ||||
|  | ||||
| /* options taken from recovery.conf for XLOG streaming */ | ||||
| static bool StandbyModeRequested = false; | ||||
| static char *PrimaryConnInfo = NULL; | ||||
| static char *PrimarySlotName = NULL; | ||||
| static char *TriggerFile = NULL; | ||||
| /* options formerly taken from recovery.conf for XLOG streaming */ | ||||
| bool		StandbyModeRequested = false; | ||||
| char	   *PrimaryConnInfo = NULL; | ||||
| char	   *PrimarySlotName = NULL; | ||||
| char	   *PromoteTriggerFile = NULL; | ||||
|  | ||||
| /* are we currently in standby mode? */ | ||||
| bool		StandbyMode = false; | ||||
| @@ -293,7 +305,11 @@ static bool recoveryStopAfter; | ||||
|  * the currently-scanned WAL record was generated).  We also need these | ||||
|  * timeline values: | ||||
|  * | ||||
|  * recoveryTargetTLI: the desired timeline that we want to end in. | ||||
|  * recoveryTargetTimeLineGoal: what the user requested, if any | ||||
|  * | ||||
|  * recoveryTargetTLIRequested: numeric value of requested timeline, if constant | ||||
|  * | ||||
|  * recoveryTargetTLI: the currently understood target timeline; changes | ||||
|  * | ||||
|  * recoveryTargetIsLatest: was the requested target timeline 'latest'? | ||||
|  * | ||||
| @@ -309,8 +325,9 @@ static bool recoveryStopAfter; | ||||
|  * file was created.)  During a sequential scan we do not allow this value | ||||
|  * to decrease. | ||||
|  */ | ||||
| static TimeLineID recoveryTargetTLI; | ||||
| static bool recoveryTargetIsLatest = false; | ||||
| RecoveryTargetTimeLineGoal recoveryTargetTimeLineGoal = RECOVERY_TARGET_TIMELINE_CONTROLFILE; | ||||
| TimeLineID	recoveryTargetTLIRequested = 0; | ||||
| TimeLineID	recoveryTargetTLI = 0; | ||||
| static List *expectedTLEs; | ||||
| static TimeLineID curFileTLI; | ||||
|  | ||||
| @@ -624,12 +641,6 @@ typedef struct XLogCtlData | ||||
| 	TimeLineID	ThisTimeLineID; | ||||
| 	TimeLineID	PrevTimeLineID; | ||||
|  | ||||
| 	/* | ||||
| 	 * archiveCleanupCommand is read from recovery.conf but needs to be in | ||||
| 	 * shared memory so that the checkpointer process can access it. | ||||
| 	 */ | ||||
| 	char		archiveCleanupCommand[MAXPGPATH]; | ||||
|  | ||||
| 	/* | ||||
| 	 * SharedRecoveryInProgress indicates if we're still in crash or archive | ||||
| 	 * recovery.  Protected by info_lck. | ||||
| @@ -846,7 +857,8 @@ static bool holdingAllLocks = false; | ||||
| static MemoryContext walDebugCxt = NULL; | ||||
| #endif | ||||
|  | ||||
| static void readRecoveryCommandFile(void); | ||||
| static void readRecoverySignalFile(void); | ||||
| static void validateRecoveryParameters(void); | ||||
| static void exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog); | ||||
| static bool recoveryStopsBefore(XLogReaderState *record); | ||||
| static bool recoveryStopsAfter(XLogReaderState *record); | ||||
| @@ -5285,283 +5297,111 @@ str_time(pg_time_t tnow) | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * See if there is a recovery command file (recovery.conf), and if so | ||||
|  * read in parameters for archive recovery and XLOG streaming. | ||||
|  * See if there are any recovery signal files and if so, set state for | ||||
|  * recovery. | ||||
|  * | ||||
|  * The file is parsed using the main configuration parser. | ||||
|  * See if there is a recovery command file (recovery.conf), and if so | ||||
|  * throw an ERROR since as of PG12 we no longer recognize that. | ||||
|  */ | ||||
| static void | ||||
| readRecoveryCommandFile(void) | ||||
| readRecoverySignalFile(void) | ||||
| { | ||||
| 	FILE	   *fd; | ||||
| 	TimeLineID	rtli = 0; | ||||
| 	bool		rtliGiven = false; | ||||
| 	ConfigVariable *item, | ||||
| 			   *head = NULL, | ||||
| 			   *tail = NULL; | ||||
| 	bool		recoveryTargetActionSet = false; | ||||
| 	struct stat stat_buf; | ||||
|  | ||||
|  | ||||
| 	fd = AllocateFile(RECOVERY_COMMAND_FILE, "r"); | ||||
| 	if (fd == NULL) | ||||
| 	{ | ||||
| 		if (errno == ENOENT) | ||||
| 			return;				/* not there, so no archive recovery */ | ||||
| 		ereport(FATAL, | ||||
| 				(errcode_for_file_access(), | ||||
| 				 errmsg("could not open recovery command file \"%s\": %m", | ||||
| 						RECOVERY_COMMAND_FILE))); | ||||
| 	} | ||||
| 	if (IsBootstrapProcessingMode()) | ||||
| 		return; | ||||
|  | ||||
| 	/* | ||||
| 	 * Since we're asking ParseConfigFp() to report errors as FATAL, there's | ||||
| 	 * no need to check the return value. | ||||
| 	 * Check for old recovery API file: recovery.conf | ||||
| 	 */ | ||||
| 	(void) ParseConfigFp(fd, RECOVERY_COMMAND_FILE, 0, FATAL, &head, &tail); | ||||
| 	if (stat(RECOVERY_COMMAND_FILE, &stat_buf) == 0) | ||||
| 		ereport(FATAL, | ||||
| 				(errcode_for_file_access(), | ||||
| 				 errmsg("using recovery command file \"%s\" is not supported", | ||||
| 						RECOVERY_COMMAND_FILE))); | ||||
|  | ||||
| 	FreeFile(fd); | ||||
| 	/* | ||||
| 	 * Remove unused .done file, if present. Ignore if absent. | ||||
| 	 */ | ||||
| 	unlink(RECOVERY_COMMAND_DONE); | ||||
|  | ||||
| 	for (item = head; item; item = item->next) | ||||
| 	/* | ||||
| 	 * Check for recovery signal files and if found, fsync them since they | ||||
| 	 * represent server state information. | ||||
| 	 * | ||||
| 	 * If present, standby signal file takes precedence. If neither is present | ||||
| 	 * then we won't enter archive recovery. | ||||
| 	 */ | ||||
| 	if (stat(STANDBY_SIGNAL_FILE, &stat_buf) == 0) | ||||
| 	{ | ||||
| 		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, "recovery_target_action") == 0) | ||||
| 		{ | ||||
| 			if (strcmp(item->value, "pause") == 0) | ||||
| 				recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE; | ||||
| 			else if (strcmp(item->value, "promote") == 0) | ||||
| 				recoveryTargetAction = RECOVERY_TARGET_ACTION_PROMOTE; | ||||
| 			else if (strcmp(item->value, "shutdown") == 0) | ||||
| 				recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN; | ||||
| 			else | ||||
| 				ereport(ERROR, | ||||
| 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 						 errmsg("invalid value for recovery parameter \"%s\": \"%s\"", | ||||
| 								"recovery_target_action", | ||||
| 								item->value), | ||||
| 						 errhint("Valid values are \"pause\", \"promote\", and \"shutdown\"."))); | ||||
| 		int			fd; | ||||
|  | ||||
| 			ereport(DEBUG2, | ||||
| 					(errmsg_internal("recovery_target_action = '%s'", | ||||
| 									 item->value))); | ||||
|  | ||||
| 			recoveryTargetActionSet = true; | ||||
| 		} | ||||
| 		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, | ||||
| 							(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 							 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, | ||||
| 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 						 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) | ||||
| 		{ | ||||
| 			recoveryTarget = RECOVERY_TARGET_TIME; | ||||
|  | ||||
| 			if (strcmp(item->value, "epoch") == 0 || | ||||
| 				strcmp(item->value, "infinity") == 0 || | ||||
| 				strcmp(item->value, "-infinity") == 0 || | ||||
| 				strcmp(item->value, "now") == 0 || | ||||
| 				strcmp(item->value, "today") == 0 || | ||||
| 				strcmp(item->value, "tomorrow") == 0 || | ||||
| 				strcmp(item->value, "yesterday") == 0) | ||||
| 				ereport(FATAL, | ||||
| 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 						 errmsg("recovery_target_time is not a valid timestamp: \"%s\"", | ||||
| 								item->value))); | ||||
|  | ||||
| 			/* | ||||
| 			 * 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) | ||||
| 		{ | ||||
| 			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_lsn") == 0) | ||||
| 		{ | ||||
| 			recoveryTarget = RECOVERY_TARGET_LSN; | ||||
|  | ||||
| 			/* | ||||
| 			 * Convert the LSN string given by the user to XLogRecPtr form. | ||||
| 			 */ | ||||
| 			recoveryTargetLSN = | ||||
| 				DatumGetLSN(DirectFunctionCall3(pg_lsn_in, | ||||
| 												CStringGetDatum(item->value), | ||||
| 												ObjectIdGetDatum(InvalidOid), | ||||
| 												Int32GetDatum(-1))); | ||||
| 			ereport(DEBUG2, | ||||
| 					(errmsg_internal("recovery_target_lsn = '%X/%X'", | ||||
| 									 (uint32) (recoveryTargetLSN >> 32), | ||||
| 									 (uint32) recoveryTargetLSN))); | ||||
| 		} | ||||
| 		else if (strcmp(item->name, "recovery_target") == 0) | ||||
| 		{ | ||||
| 			if (strcmp(item->value, "immediate") == 0) | ||||
| 				recoveryTarget = RECOVERY_TARGET_IMMEDIATE; | ||||
| 			else | ||||
| 				ereport(ERROR, | ||||
| 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 						 errmsg("invalid value for recovery parameter \"%s\": \"%s\"", | ||||
| 								"recovery_target", | ||||
| 								item->value), | ||||
| 						 errhint("The only allowed value is \"immediate\"."))); | ||||
| 			ereport(DEBUG2, | ||||
| 					(errmsg_internal("recovery_target = '%s'", | ||||
| 									 item->value))); | ||||
| 		} | ||||
| 		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, &StandbyModeRequested)) | ||||
| 				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, "primary_slot_name") == 0) | ||||
| 		{ | ||||
| 			ReplicationSlotValidateName(item->value, ERROR); | ||||
| 			PrimarySlotName = pstrdup(item->value); | ||||
| 			ereport(DEBUG2, | ||||
| 					(errmsg_internal("primary_slot_name = '%s'", | ||||
| 									 PrimarySlotName))); | ||||
| 		} | ||||
| 		else if (strcmp(item->name, "trigger_file") == 0) | ||||
| 		{ | ||||
| 			TriggerFile = pstrdup(item->value); | ||||
| 			ereport(DEBUG2, | ||||
| 					(errmsg_internal("trigger_file = '%s'", | ||||
| 									 TriggerFile))); | ||||
| 		} | ||||
| 		else if (strcmp(item->name, "recovery_min_apply_delay") == 0) | ||||
| 		{ | ||||
| 			const char *hintmsg; | ||||
|  | ||||
| 			if (!parse_int(item->value, &recovery_min_apply_delay, GUC_UNIT_MS, | ||||
| 						   &hintmsg)) | ||||
| 				ereport(ERROR, | ||||
| 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 						 errmsg("parameter \"%s\" requires a temporal value", | ||||
| 								"recovery_min_apply_delay"), | ||||
| 						 hintmsg ? errhint("%s", _(hintmsg)) : 0)); | ||||
| 			ereport(DEBUG2, | ||||
| 					(errmsg_internal("recovery_min_apply_delay = '%s'", item->value))); | ||||
| 		} | ||||
| 		else | ||||
| 			ereport(FATAL, | ||||
| 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 					 errmsg("unrecognized recovery parameter \"%s\"", | ||||
| 							item->name))); | ||||
| 		fd = BasicOpenFilePerm(STANDBY_SIGNAL_FILE, O_RDWR | PG_BINARY | get_sync_bit(sync_method), | ||||
| 							   S_IRUSR | S_IWUSR); | ||||
| 		pg_fsync(fd); | ||||
| 		close(fd); | ||||
| 		standby_signal_file_found = true; | ||||
| 	} | ||||
| 	else if (stat(RECOVERY_SIGNAL_FILE, &stat_buf) == 0) | ||||
| 	{ | ||||
| 		int			fd; | ||||
|  | ||||
| 		fd = BasicOpenFilePerm(RECOVERY_SIGNAL_FILE, O_RDWR | PG_BINARY | get_sync_bit(sync_method), | ||||
| 							   S_IRUSR | S_IWUSR); | ||||
| 		pg_fsync(fd); | ||||
| 		close(fd); | ||||
| 		recovery_signal_file_found = true; | ||||
| 	} | ||||
|  | ||||
| 	StandbyModeRequested = false; | ||||
| 	ArchiveRecoveryRequested = false; | ||||
| 	if (standby_signal_file_found) | ||||
| 	{ | ||||
| 		StandbyModeRequested = true; | ||||
| 		ArchiveRecoveryRequested = true; | ||||
| 	} | ||||
| 	else if (recovery_signal_file_found) | ||||
| 	{ | ||||
| 		StandbyModeRequested = false; | ||||
| 		ArchiveRecoveryRequested = true; | ||||
| 	} | ||||
| 	else | ||||
| 		return; | ||||
|  | ||||
| 	/* | ||||
| 	 * We don't support standby mode in standalone backends; that requires | ||||
| 	 * other processes such as the WAL receiver to be alive. | ||||
| 	 */ | ||||
| 	if (StandbyModeRequested && !IsUnderPostmaster) | ||||
| 		ereport(FATAL, | ||||
| 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 				 errmsg("standby mode is not supported by single-user servers"))); | ||||
| } | ||||
|  | ||||
| static void | ||||
| validateRecoveryParameters(void) | ||||
| { | ||||
| 	if (!ArchiveRecoveryRequested) | ||||
| 		return; | ||||
|  | ||||
| 	/* | ||||
| 	 * Check for compulsory parameters | ||||
| 	 */ | ||||
| 	if (StandbyModeRequested) | ||||
| 	{ | ||||
| 		if (PrimaryConnInfo == NULL && recoveryRestoreCommand == NULL) | ||||
| 		if ((PrimaryConnInfo == NULL || strcmp(PrimaryConnInfo, "") == 0) && | ||||
| 			(recoveryRestoreCommand == NULL || strcmp(recoveryRestoreCommand, "") == 0)) | ||||
| 			ereport(WARNING, | ||||
| 					(errmsg("recovery command file \"%s\" specified neither primary_conninfo nor restore_command", | ||||
| 							RECOVERY_COMMAND_FILE), | ||||
| 					(errmsg("specified neither primary_conninfo nor restore_command"), | ||||
| 					 errhint("The database server will regularly poll the pg_wal subdirectory to check for files placed there."))); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		if (recoveryRestoreCommand == NULL) | ||||
| 		if (recoveryRestoreCommand == NULL || | ||||
| 			strcmp(recoveryRestoreCommand, "") == 0) | ||||
| 			ereport(FATAL, | ||||
| 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 					 errmsg("recovery command file \"%s\" must specify restore_command when standby mode is not enabled", | ||||
| 							RECOVERY_COMMAND_FILE))); | ||||
| 					 errmsg("must specify restore_command when standby mode is not enabled"))); | ||||
| 	} | ||||
|  | ||||
| 	/* | ||||
| @@ -5570,50 +5410,40 @@ readRecoveryCommandFile(void) | ||||
| 	 * hot_standby = off, which was surprising behaviour. | ||||
| 	 */ | ||||
| 	if (recoveryTargetAction == RECOVERY_TARGET_ACTION_PAUSE && | ||||
| 		recoveryTargetActionSet && | ||||
| 		!EnableHotStandby) | ||||
| 		recoveryTargetAction = RECOVERY_TARGET_ACTION_SHUTDOWN; | ||||
|  | ||||
| 	/* | ||||
| 	 * We don't support standby_mode in standalone backends; that requires | ||||
| 	 * other processes such as the WAL receiver to be alive. | ||||
| 	 */ | ||||
| 	if (StandbyModeRequested && !IsUnderPostmaster) | ||||
| 		ereport(FATAL, | ||||
| 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), | ||||
| 				 errmsg("standby mode is not supported by single-user servers"))); | ||||
|  | ||||
| 	/* Enable fetching from archive recovery area */ | ||||
| 	ArchiveRecoveryRequested = true; | ||||
|  | ||||
| 	/* | ||||
| 	 * If user specified recovery_target_timeline, validate it or compute the | ||||
| 	 * "latest" value.  We can't do this until after we've gotten the restore | ||||
| 	 * command and set InArchiveRecovery, because we need to fetch timeline | ||||
| 	 * history files from the archive. | ||||
| 	 */ | ||||
| 	if (rtliGiven) | ||||
| 	if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC) | ||||
| 	{ | ||||
| 		if (rtli) | ||||
| 		{ | ||||
| 			/* Timeline 1 does not have a history file, all else should */ | ||||
| 			if (rtli != 1 && !existsTimeLineHistory(rtli)) | ||||
| 				ereport(FATAL, | ||||
| 						(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 						 errmsg("recovery target timeline %u does not exist", | ||||
| 								rtli))); | ||||
| 			recoveryTargetTLI = rtli; | ||||
| 			recoveryTargetIsLatest = false; | ||||
| 		} | ||||
| 		else | ||||
| 		{ | ||||
| 			/* We start the "latest" search from pg_control's timeline */ | ||||
| 			recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI); | ||||
| 			recoveryTargetIsLatest = true; | ||||
| 		} | ||||
| 	} | ||||
| 		TimeLineID	rtli = recoveryTargetTLIRequested; | ||||
|  | ||||
| 	FreeConfigVariables(head); | ||||
| 		/* Timeline 1 does not have a history file, all else should */ | ||||
| 		if (rtli != 1 && !existsTimeLineHistory(rtli)) | ||||
| 			ereport(FATAL, | ||||
| 					(errcode(ERRCODE_INVALID_PARAMETER_VALUE), | ||||
| 					 errmsg("recovery target timeline %u does not exist", | ||||
| 							rtli))); | ||||
| 		recoveryTargetTLI = rtli; | ||||
| 	} | ||||
| 	else if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_LATEST) | ||||
| 	{ | ||||
| 		/* We start the "latest" search from pg_control's timeline */ | ||||
| 		recoveryTargetTLI = findNewestTimeLine(recoveryTargetTLI); | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 		/* | ||||
| 		 * else we just use the recoveryTargetTLI as already read from | ||||
| 		 * ControlFile | ||||
| 		 */ | ||||
| 		Assert(recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_CONTROLFILE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /* | ||||
| @@ -5714,11 +5544,14 @@ exitArchiveRecovery(TimeLineID endTLI, XLogRecPtr endOfLog) | ||||
| 	unlink(recoveryPath);		/* ignore any error */ | ||||
|  | ||||
| 	/* | ||||
| 	 * Rename the config file out of the way, so that we don't accidentally | ||||
| 	 * Remove the signal files out of the way, so that we don't accidentally | ||||
| 	 * re-enter archive recovery mode in a subsequent crash. | ||||
| 	 */ | ||||
| 	unlink(RECOVERY_COMMAND_DONE); | ||||
| 	durable_rename(RECOVERY_COMMAND_FILE, RECOVERY_COMMAND_DONE, FATAL); | ||||
| 	if (standby_signal_file_found) | ||||
| 		durable_unlink(STANDBY_SIGNAL_FILE, FATAL); | ||||
|  | ||||
| 	if (recovery_signal_file_found) | ||||
| 		durable_unlink(RECOVERY_SIGNAL_FILE, FATAL); | ||||
|  | ||||
| 	ereport(LOG, | ||||
| 			(errmsg("archive recovery complete"))); | ||||
| @@ -6461,18 +6294,10 @@ StartupXLOG(void) | ||||
| 		recoveryTargetTLI = ControlFile->checkPointCopy.ThisTimeLineID; | ||||
|  | ||||
| 	/* | ||||
| 	 * Check for recovery control file, and if so set up state for offline | ||||
| 	 * recovery | ||||
| 	 * Check for signal files, and if so set up state for offline recovery | ||||
| 	 */ | ||||
| 	readRecoveryCommandFile(); | ||||
|  | ||||
| 	/* | ||||
| 	 * Save archive_cleanup_command in shared memory so that other processes | ||||
| 	 * can see it. | ||||
| 	 */ | ||||
| 	strlcpy(XLogCtl->archiveCleanupCommand, | ||||
| 			archiveCleanupCommand ? archiveCleanupCommand : "", | ||||
| 			sizeof(XLogCtl->archiveCleanupCommand)); | ||||
| 	readRecoverySignalFile(); | ||||
| 	validateRecoveryParameters(); | ||||
|  | ||||
| 	if (ArchiveRecoveryRequested) | ||||
| 	{ | ||||
| @@ -6652,7 +6477,8 @@ StartupXLOG(void) | ||||
| 		 * This can happen for example if a base backup is taken from a | ||||
| 		 * running server using an atomic filesystem snapshot, without calling | ||||
| 		 * pg_start/stop_backup. Or if you just kill a running master server | ||||
| 		 * and put it into archive recovery by creating a recovery.conf file. | ||||
| 		 * and put it into archive recovery by creating a recovery signal | ||||
| 		 * file. | ||||
| 		 * | ||||
| 		 * Our strategy in that case is to perform crash recovery first, | ||||
| 		 * replaying all the WAL present in pg_wal, and only enter archive | ||||
| @@ -6687,7 +6513,7 @@ StartupXLOG(void) | ||||
| 		{ | ||||
| 			/* | ||||
| 			 * We used to attempt to go back to a secondary checkpoint record | ||||
| 			 * here, but only when not in standby_mode. We now just fail if we | ||||
| 			 * here, but only when not in standby mode. We now just fail if we | ||||
| 			 * can't read the last checkpoint because this allows us to | ||||
| 			 * simplify processing around checkpoints. | ||||
| 			 */ | ||||
| @@ -6878,7 +6704,7 @@ StartupXLOG(void) | ||||
|  | ||||
| 	/* | ||||
| 	 * Check whether we need to force recovery from WAL.  If it appears to | ||||
| 	 * have been a clean shutdown and we did not have a recovery.conf file, | ||||
| 	 * have been a clean shutdown and we did not have a recovery signal file, | ||||
| 	 * then assume no recovery needed. | ||||
| 	 */ | ||||
| 	if (checkPoint.redo < RecPtr) | ||||
| @@ -6892,7 +6718,7 @@ StartupXLOG(void) | ||||
| 		InRecovery = true; | ||||
| 	else if (ArchiveRecoveryRequested) | ||||
| 	{ | ||||
| 		/* force recovery due to presence of recovery.conf */ | ||||
| 		/* force recovery due to presence of recovery signal file */ | ||||
| 		InRecovery = true; | ||||
| 	} | ||||
|  | ||||
| @@ -7763,7 +7589,7 @@ StartupXLOG(void) | ||||
| 		/* | ||||
| 		 * And finally, execute the recovery_end_command, if any. | ||||
| 		 */ | ||||
| 		if (recoveryEndCommand) | ||||
| 		if (recoveryEndCommand && strcmp(recoveryEndCommand, "") != 0) | ||||
| 			ExecuteRecoveryCommand(recoveryEndCommand, | ||||
| 								   "recovery_end_command", | ||||
| 								   true); | ||||
| @@ -9485,8 +9311,8 @@ CreateRestartPoint(int flags) | ||||
| 	/* | ||||
| 	 * Finally, execute archive_cleanup_command, if any. | ||||
| 	 */ | ||||
| 	if (XLogCtl->archiveCleanupCommand[0]) | ||||
| 		ExecuteRecoveryCommand(XLogCtl->archiveCleanupCommand, | ||||
| 	if (archiveCleanupCommand && strcmp(archiveCleanupCommand, "") != 0) | ||||
| 		ExecuteRecoveryCommand(archiveCleanupCommand, | ||||
| 							   "archive_cleanup_command", | ||||
| 							   false); | ||||
|  | ||||
| @@ -11995,7 +11821,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, | ||||
| 					 * that when we later jump backwards to start redo at | ||||
| 					 * RedoStartLSN, we will have the logs streamed already. | ||||
| 					 */ | ||||
| 					if (PrimaryConnInfo) | ||||
| 					if (PrimaryConnInfo && strcmp(PrimaryConnInfo, "") != 0) | ||||
| 					{ | ||||
| 						XLogRecPtr	ptr; | ||||
| 						TimeLineID	tli; | ||||
| @@ -12064,7 +11890,7 @@ WaitForWALToBecomeAvailable(XLogRecPtr RecPtr, bool randAccess, | ||||
| 					 * Before we sleep, re-scan for possible new timelines if | ||||
| 					 * we were requested to recover to the latest timeline. | ||||
| 					 */ | ||||
| 					if (recoveryTargetIsLatest) | ||||
| 					if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_LATEST) | ||||
| 					{ | ||||
| 						if (rescanLatestTimeLine()) | ||||
| 						{ | ||||
| @@ -12367,14 +12193,14 @@ CheckForStandbyTrigger(void) | ||||
| 		return true; | ||||
| 	} | ||||
|  | ||||
| 	if (TriggerFile == NULL) | ||||
| 	if (PromoteTriggerFile == NULL || strcmp(PromoteTriggerFile, "") == 0) | ||||
| 		return false; | ||||
|  | ||||
| 	if (stat(TriggerFile, &stat_buf) == 0) | ||||
| 	if (stat(PromoteTriggerFile, &stat_buf) == 0) | ||||
| 	{ | ||||
| 		ereport(LOG, | ||||
| 				(errmsg("trigger file found: %s", TriggerFile))); | ||||
| 		unlink(TriggerFile); | ||||
| 				(errmsg("promote trigger file found: %s", PromoteTriggerFile))); | ||||
| 		unlink(PromoteTriggerFile); | ||||
| 		triggered = true; | ||||
| 		fast_promote = true; | ||||
| 		return true; | ||||
| @@ -12382,8 +12208,8 @@ CheckForStandbyTrigger(void) | ||||
| 	else if (errno != ENOENT) | ||||
| 		ereport(ERROR, | ||||
| 				(errcode_for_file_access(), | ||||
| 				 errmsg("could not stat trigger file \"%s\": %m", | ||||
| 						TriggerFile))); | ||||
| 				 errmsg("could not stat promote trigger file \"%s\": %m", | ||||
| 						PromoteTriggerFile))); | ||||
|  | ||||
| 	return false; | ||||
| } | ||||
|   | ||||
| @@ -66,7 +66,7 @@ RestoreArchivedFile(char *path, const char *xlogfname, | ||||
| 	TimeLineID	restartTli; | ||||
|  | ||||
| 	/* In standby mode, restore_command might not be supplied */ | ||||
| 	if (recoveryRestoreCommand == NULL) | ||||
| 	if (recoveryRestoreCommand == NULL || strcmp(recoveryRestoreCommand, "") == 0) | ||||
| 		goto not_available; | ||||
|  | ||||
| 	/* | ||||
| @@ -410,7 +410,7 @@ ExecuteRecoveryCommand(const char *command, const char *commandName, bool failOn | ||||
|  | ||||
| 		ereport((signaled && failOnSignal) ? FATAL : WARNING, | ||||
| 		/*------ | ||||
| 		   translator: First %s represents a recovery.conf parameter name like | ||||
| 		   translator: First %s represents a postgresql.conf parameter name like | ||||
| 		  "recovery_end_command", the 2nd is the value of that parameter, the | ||||
| 		  third an already translated error message. */ | ||||
| 				(errmsg("%s \"%s\": %s", commandName, | ||||
|   | ||||
| @@ -9,8 +9,8 @@ | ||||
|  * dependent objects can be associated with it.  An extension is created by | ||||
|  * populating the pg_extension catalog from a "control" file. | ||||
|  * The extension control file is parsed with the same parser we use for | ||||
|  * postgresql.conf and recovery.conf.  An extension also has an installation | ||||
|  * script file, containing SQL commands to create the extension's objects. | ||||
|  * postgresql.conf.  An extension also has an installation script file, | ||||
|  * containing SQL commands to create the extension's objects. | ||||
|  * | ||||
|  * Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group | ||||
|  * Portions Copyright (c) 1994, Regents of the University of California | ||||
|   | ||||
| @@ -84,6 +84,7 @@ | ||||
| #include "utils/float.h" | ||||
| #include "utils/memutils.h" | ||||
| #include "utils/pg_locale.h" | ||||
| #include "utils/pg_lsn.h" | ||||
| #include "utils/plancache.h" | ||||
| #include "utils/portal.h" | ||||
| #include "utils/ps_status.h" | ||||
| @@ -195,6 +196,19 @@ static bool check_cluster_name(char **newval, void **extra, GucSource source); | ||||
| static const char *show_unix_socket_permissions(void); | ||||
| static const char *show_log_file_mode(void); | ||||
| static const char *show_data_directory_mode(void); | ||||
| static bool check_recovery_target_timeline(char **newval, void **extra, GucSource source); | ||||
| static void assign_recovery_target_timeline(const char *newval, void *extra); | ||||
| static bool check_recovery_target(char **newval, void **extra, GucSource source); | ||||
| static void assign_recovery_target(const char *newval, void *extra); | ||||
| 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_time(char **newval, void **extra, GucSource source); | ||||
| static void assign_recovery_target_time(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_lsn(char **newval, void **extra, GucSource source); | ||||
| static void assign_recovery_target_lsn(const char *newval, void *extra); | ||||
| static bool check_primary_slot_name(char **newval, void **extra, GucSource source); | ||||
|  | ||||
| /* Private functions in guc-file.l that need to be called from guc.c */ | ||||
| static ConfigVariable *ProcessConfigFileInternal(GucContext context, | ||||
| @@ -442,6 +456,7 @@ const struct config_enum_entry ssl_protocol_versions_info[] = { | ||||
|  */ | ||||
| extern const struct config_enum_entry wal_level_options[]; | ||||
| extern const struct config_enum_entry archive_mode_options[]; | ||||
| extern const struct config_enum_entry recovery_target_action_options[]; | ||||
| extern const struct config_enum_entry sync_method_options[]; | ||||
| extern const struct config_enum_entry dynamic_shared_memory_options[]; | ||||
|  | ||||
| @@ -533,6 +548,13 @@ static int	wal_block_size; | ||||
| static bool data_checksums; | ||||
| static bool integer_datetimes; | ||||
| static bool assert_enabled; | ||||
| static char *recovery_target_timeline_string; | ||||
| static char *recovery_target_string; | ||||
| static char *recovery_target_xid_string; | ||||
| static char *recovery_target_time_string; | ||||
| static char *recovery_target_name_string; | ||||
| static char *recovery_target_lsn_string; | ||||
|  | ||||
|  | ||||
| /* should be static, but commands/variable.c needs to get at this */ | ||||
| char	   *role_string; | ||||
| @@ -616,6 +638,10 @@ const char *const config_group_names[] = | ||||
| 	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 */ | ||||
| @@ -1637,6 +1663,16 @@ static struct config_bool ConfigureNamesBool[] = | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"recovery_target_inclusive", PGC_POSTMASTER, WAL_RECOVERY_TARGET, | ||||
| 			gettext_noop("Sets whether to include or exclude transaction with recovery target."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&recoveryTargetInclusive, | ||||
| 		true, | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY, | ||||
| 			gettext_noop("Allows connections and queries during recovery."), | ||||
| @@ -1973,6 +2009,17 @@ static struct config_int ConfigureNamesInt[] = | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"recovery_min_apply_delay", PGC_POSTMASTER, REPLICATION_STANDBY, | ||||
| 			gettext_noop("Sets the minimum delay for applying changes during recovery."), | ||||
| 			NULL, | ||||
| 			GUC_UNIT_MS | ||||
| 		}, | ||||
| 		&recovery_min_apply_delay, | ||||
| 		0, 0, INT_MAX, | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"wal_receiver_status_interval", PGC_SIGHUP, REPLICATION_STANDBY, | ||||
| 			gettext_noop("Sets the maximum interval between WAL receiver status reports to the sending server."), | ||||
| @@ -3291,6 +3338,123 @@ static struct config_string ConfigureNamesString[] = | ||||
| 		NULL, NULL, show_archive_command | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"restore_command", PGC_POSTMASTER, WAL_ARCHIVE_RECOVERY, | ||||
| 			gettext_noop("Sets the shell command that will retrieve an archived WAL file."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&recoveryRestoreCommand, | ||||
| 		"", | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"archive_cleanup_command", PGC_POSTMASTER, WAL_ARCHIVE_RECOVERY, | ||||
| 			gettext_noop("Sets the shell command that will be executed at every restart point."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&archiveCleanupCommand, | ||||
| 		"", | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"recovery_end_command", PGC_POSTMASTER, WAL_ARCHIVE_RECOVERY, | ||||
| 			gettext_noop("Sets the shell command that will be executed once at the end of recovery."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&recoveryEndCommand, | ||||
| 		"", | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"recovery_target_timeline", PGC_POSTMASTER, WAL_RECOVERY_TARGET, | ||||
| 			gettext_noop("Specifies the timeline to recovery into."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&recovery_target_timeline_string, | ||||
| 		"", | ||||
| 		check_recovery_target_timeline, assign_recovery_target_timeline, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"recovery_target", PGC_POSTMASTER, WAL_RECOVERY_TARGET, | ||||
| 			gettext_noop("Set to 'immediate' to end recovery as soon as a consistent state is reached."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&recovery_target_string, | ||||
| 		"", | ||||
| 		check_recovery_target, assign_recovery_target, NULL | ||||
| 	}, | ||||
| 	{ | ||||
| 		{"recovery_target_xid", PGC_POSTMASTER, 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_time", PGC_POSTMASTER, 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_name", PGC_POSTMASTER, WAL_RECOVERY_TARGET, | ||||
| 			gettext_noop("Sets the named restore point up to which recovery will proceed."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&recovery_target_name_string, | ||||
| 		"", | ||||
| 		check_recovery_target_name, assign_recovery_target_name, NULL | ||||
| 	}, | ||||
| 	{ | ||||
| 		{"recovery_target_lsn", PGC_POSTMASTER, WAL_RECOVERY_TARGET, | ||||
| 			gettext_noop("Sets the LSN of the write-ahead log location up to which recovery will proceed."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&recovery_target_lsn_string, | ||||
| 		"", | ||||
| 		check_recovery_target_lsn, assign_recovery_target_lsn, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"promote_trigger_file", PGC_POSTMASTER, REPLICATION_STANDBY, | ||||
| 			gettext_noop("Specifies a file name whose presence ends recovery in the standby."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&PromoteTriggerFile, | ||||
| 		"", | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"primary_conninfo", PGC_POSTMASTER, REPLICATION_STANDBY, | ||||
| 			gettext_noop("Sets the connection string to be used to connect to the sending server."), | ||||
| 			NULL, | ||||
| 			GUC_SUPERUSER_ONLY | ||||
| 		}, | ||||
| 		&PrimaryConnInfo, | ||||
| 		"", | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"primary_slot_name", PGC_POSTMASTER, REPLICATION_STANDBY, | ||||
| 			gettext_noop("Sets the name of the replication slot to use on the sending server."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&PrimarySlotName, | ||||
| 		"", | ||||
| 		check_primary_slot_name, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"client_encoding", PGC_USERSET, CLIENT_CONN_LOCALE, | ||||
| 			gettext_noop("Sets the client's character set encoding."), | ||||
| @@ -4071,6 +4235,16 @@ static struct config_enum ConfigureNamesEnum[] = | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"recovery_target_action", PGC_POSTMASTER, WAL_RECOVERY_TARGET, | ||||
| 			gettext_noop("Sets the action to perform upon reaching the recovery target."), | ||||
| 			NULL | ||||
| 		}, | ||||
| 		&recoveryTargetAction, | ||||
| 		RECOVERY_TARGET_ACTION_PAUSE, recovery_target_action_options, | ||||
| 		NULL, NULL, NULL | ||||
| 	}, | ||||
|  | ||||
| 	{ | ||||
| 		{"trace_recovery_messages", PGC_SIGHUP, DEVELOPER_OPTIONS, | ||||
| 			gettext_noop("Enables logging of recovery-related debugging information."), | ||||
| @@ -10838,4 +11012,251 @@ show_data_directory_mode(void) | ||||
| 	return buf; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| check_recovery_target_timeline(char **newval, void **extra, GucSource source) | ||||
| { | ||||
| 	RecoveryTargetTimeLineGoal rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE; | ||||
| 	RecoveryTargetTimeLineGoal *myextra; | ||||
|  | ||||
| 	if (strcmp(*newval, "") == 0) | ||||
| 		rttg = RECOVERY_TARGET_TIMELINE_CONTROLFILE; | ||||
| 	else if (strcmp(*newval, "latest") == 0) | ||||
| 		rttg = RECOVERY_TARGET_TIMELINE_LATEST; | ||||
| 	else | ||||
| 	{ | ||||
| 		errno = 0; | ||||
| 		strtoul(*newval, NULL, 0); | ||||
| 		if (errno == EINVAL || errno == ERANGE) | ||||
| 		{ | ||||
| 			GUC_check_errdetail("recovery_target_timeline is not a valid number"); | ||||
| 			return false; | ||||
| 		} | ||||
| 		rttg = RECOVERY_TARGET_TIMELINE_NUMERIC; | ||||
| 	} | ||||
|  | ||||
| 	myextra = (RecoveryTargetTimeLineGoal *) guc_malloc(ERROR, sizeof(RecoveryTargetTimeLineGoal)); | ||||
| 	*myextra = rttg; | ||||
| 	*extra = (void *) myextra; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static void | ||||
| assign_recovery_target_timeline(const char *newval, void *extra) | ||||
| { | ||||
| 	recoveryTargetTimeLineGoal = *((RecoveryTargetTimeLineGoal *) extra); | ||||
| 	if (recoveryTargetTimeLineGoal == RECOVERY_TARGET_TIMELINE_NUMERIC) | ||||
| 		recoveryTargetTLIRequested = (TimeLineID) strtoul(newval, NULL, 0); | ||||
| 	else | ||||
| 		recoveryTargetTLIRequested = 0; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| check_recovery_target(char **newval, void **extra, GucSource source) | ||||
| { | ||||
| 	if (strcmp(*newval, "immediate") != 0 && strcmp(*newval, "") != 0) | ||||
| 	{ | ||||
| 		GUC_check_errdetail("The only allowed value is \"immediate\"."); | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static void | ||||
| assign_recovery_target(const char *newval, void *extra) | ||||
| { | ||||
| 	if (newval && strcmp(newval, "") != 0) | ||||
| 		recoveryTarget = RECOVERY_TARGET_IMMEDIATE; | ||||
| 	else | ||||
| 		/* | ||||
| 		 * Reset recoveryTarget to RECOVERY_TARGET_UNSET to proper handle user | ||||
| 		 * setting multiple recovery_target with blank value on last. | ||||
| 		 */ | ||||
| 		recoveryTarget = RECOVERY_TARGET_UNSET; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| check_recovery_target_xid(char **newval, void **extra, GucSource source) | ||||
| { | ||||
| 	if (strcmp(*newval, "") != 0) | ||||
| 	{ | ||||
| 		TransactionId xid; | ||||
| 		TransactionId *myextra; | ||||
|  | ||||
| 		errno = 0; | ||||
| 		xid = (TransactionId) strtoul(*newval, NULL, 0); | ||||
| 		if (errno == EINVAL || errno == ERANGE) | ||||
| 			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) | ||||
| { | ||||
| 	if (newval && strcmp(newval, "") != 0) | ||||
| 	{ | ||||
| 		recoveryTarget = RECOVERY_TARGET_XID; | ||||
| 		recoveryTargetXid = *((TransactionId *) extra); | ||||
| 	} | ||||
| 	else | ||||
| 		recoveryTarget = RECOVERY_TARGET_UNSET; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| check_recovery_target_time(char **newval, void **extra, GucSource source) | ||||
| { | ||||
| 	if (strcmp(*newval, "") != 0) | ||||
| 	{ | ||||
| 		TimestampTz time; | ||||
| 		TimestampTz *myextra; | ||||
| 		MemoryContext oldcontext = CurrentMemoryContext; | ||||
|  | ||||
| 		/* reject some special values */ | ||||
| 		if (strcmp(*newval, "epoch") == 0 || | ||||
| 			strcmp(*newval, "infinity") == 0 || | ||||
| 			strcmp(*newval, "-infinity") == 0 || | ||||
| 			strcmp(*newval, "now") == 0 || | ||||
| 			strcmp(*newval, "today") == 0 || | ||||
| 			strcmp(*newval, "tomorrow") == 0 || | ||||
| 			strcmp(*newval, "yesterday") == 0) | ||||
| 		{ | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		PG_TRY(); | ||||
| 		{ | ||||
| 			time = 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) | ||||
| { | ||||
| 	if (newval && strcmp(newval, "") != 0) | ||||
| 	{ | ||||
| 		recoveryTarget = RECOVERY_TARGET_TIME; | ||||
| 		recoveryTargetTime = *((TimestampTz *) extra); | ||||
| 	} | ||||
| 	else | ||||
| 		recoveryTarget = RECOVERY_TARGET_UNSET; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| check_recovery_target_name(char **newval, void **extra, GucSource source) | ||||
| { | ||||
| 	/* Use the value of newval directly */ | ||||
| 	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 (newval && strcmp(newval, "") != 0) | ||||
| 	{ | ||||
| 		recoveryTarget = RECOVERY_TARGET_NAME; | ||||
| 		recoveryTargetName = (char *) newval; | ||||
| 	} | ||||
| 	else | ||||
| 		recoveryTarget = RECOVERY_TARGET_UNSET; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| check_recovery_target_lsn(char **newval, void **extra, GucSource source) | ||||
| { | ||||
| 	if (strcmp(*newval, "") != 0) | ||||
| 	{ | ||||
| 		XLogRecPtr	lsn; | ||||
| 		XLogRecPtr *myextra; | ||||
| 		MemoryContext oldcontext = CurrentMemoryContext; | ||||
|  | ||||
| 		/* | ||||
| 		 * Convert the LSN string given by the user to XLogRecPtr form. | ||||
| 		 */ | ||||
| 		PG_TRY(); | ||||
| 		{ | ||||
| 			lsn = DatumGetLSN(DirectFunctionCall3(pg_lsn_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 = (XLogRecPtr *) guc_malloc(ERROR, sizeof(XLogRecPtr)); | ||||
| 		*myextra = lsn; | ||||
| 		*extra = (void *) myextra; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| static void | ||||
| assign_recovery_target_lsn(const char *newval, void *extra) | ||||
| { | ||||
| 	if (newval && strcmp(newval, "") != 0) | ||||
| 	{ | ||||
| 		recoveryTarget = RECOVERY_TARGET_LSN; | ||||
| 		recoveryTargetLSN = *((XLogRecPtr *) extra); | ||||
| 	} | ||||
| 	else | ||||
| 		recoveryTarget = RECOVERY_TARGET_UNSET; | ||||
| } | ||||
|  | ||||
| static bool | ||||
| check_primary_slot_name(char **newval, void **extra, GucSource source) | ||||
| { | ||||
| 	if (*newval && strcmp(*newval, "") != 0 && | ||||
| 		!ReplicationSlotValidateName(*newval, WARNING)) | ||||
| 		return false; | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| #include "guc-file.c" | ||||
|   | ||||
| @@ -228,6 +228,45 @@ | ||||
| #archive_timeout = 0		# force a logfile segment switch after this | ||||
| 				# number of seconds; 0 disables | ||||
|  | ||||
| # - Archive Recovery - | ||||
|  | ||||
| # These are only used in recovery mode. | ||||
|  | ||||
| #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' | ||||
| 				# (change requires restart) | ||||
| #archive_cleanup_command = ''	# command to execute at every restartpoint | ||||
| 				# (change requires restart) | ||||
| #recovery_end_command = ''	# command to execute at completion of recovery | ||||
| 				# (change requires restart) | ||||
|  | ||||
| # - Recovery Target - | ||||
|  | ||||
| # Set these only when performing a targeted recovery. | ||||
|  | ||||
| #recovery_target = ''		# 'immediate' to end recovery as soon as a | ||||
|                                 # consistent state is reached | ||||
| 				# (change requires restart) | ||||
| #recovery_target_name = ''	# the named restore point to which recovery will proceed | ||||
| 				# (change requires restart) | ||||
| #recovery_target_time = ''	# the time stamp up to which recovery will proceed | ||||
| 				# (change requires restart) | ||||
| #recovery_target_xid = ''	# the transaction ID up to which recovery will proceed | ||||
| 				# (change requires restart) | ||||
| #recovery_target_lsn = ''	# the WAL LSN up to which recovery will proceed | ||||
| 				# (change requires restart) | ||||
| #recovery_target_inclusive = on # Specifies whether to stop: | ||||
| 				# just after the specified recovery target (on) | ||||
| 				# just before the recovery target (off) | ||||
| 				# (change requires restart) | ||||
| #recovery_target_timeline = ''	# unset means read from control file (default), | ||||
| 				# or set to 'latest' or timeline ID | ||||
| 				# (change requires restart) | ||||
| #recovery_target_action = 'pause'	# 'pause', 'promote', 'shutdown' | ||||
| 				# (change requires restart) | ||||
|  | ||||
|  | ||||
| #------------------------------------------------------------------------------ | ||||
| # REPLICATION | ||||
| @@ -261,6 +300,12 @@ | ||||
|  | ||||
| # These settings are ignored on a master server. | ||||
|  | ||||
| #primary_conninfo = ''			# connection string to sending server | ||||
| 					# (change requires restart) | ||||
| #primary_slot_name = ''			# replication slot on sending server | ||||
| 					# (change requires restart) | ||||
| #promote_trigger_file = ''		# file name whose presence ends recovery | ||||
| 					# (change requires restart) | ||||
| #hot_standby = on			# "off" disallows queries during recovery | ||||
| 					# (change requires restart) | ||||
| #max_standby_archive_delay = 30s	# max delay before canceling queries | ||||
| @@ -278,6 +323,8 @@ | ||||
| 					# in milliseconds; 0 disables | ||||
| #wal_retrieve_retry_interval = 5s	# time to wait before retrying to | ||||
| 					# retrieve WAL after a failed attempt | ||||
| #recovery_min_apply_delay = 0		# minimum delay for applying changes during recovery | ||||
| 					# (change requires restart) | ||||
|  | ||||
| # - Subscribers - | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user