diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 3eee988359..20260a8527 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -3840,7 +3840,8 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows This parameter specifies the time stamp up to which recovery will proceed. The precise stopping point is also influenced by - . + and + . @@ -3921,6 +3922,28 @@ restore_command = 'copy "C:\\server\\archivedir\\%f" "%p"' # Windows + + + recovery_target_use_origin_time (boolean) + + recovery_target_use_origin_time configuration parameter + + + + + Specifies whether to use the timestamp from the local commit + record (off), or, if one exists, to use + the timestamp as recorded by the origin, for commits that + arrive by logical replication from another server. + This allows a PITR recovery to have a single consistent + timestamp across multiple servers, if that is desirable. + Applies when + is specified. Default is on. + + + + recovery_target_timeline (string) diff --git a/src/backend/access/transam/xlog.c b/src/backend/access/transam/xlog.c index 1b3a3d9bea..d2d9eca47f 100644 --- a/src/backend/access/transam/xlog.c +++ b/src/backend/access/transam/xlog.c @@ -284,6 +284,7 @@ char *recoveryEndCommand = NULL; char *archiveCleanupCommand = NULL; RecoveryTargetType recoveryTarget = RECOVERY_TARGET_UNSET; bool recoveryTargetInclusive = true; +bool recoveryTargetUseOriginTime = false; int recoveryTargetAction = RECOVERY_TARGET_ACTION_PAUSE; TransactionId recoveryTargetXid; char *recovery_target_time_string; @@ -5699,25 +5700,81 @@ static bool getRecordTimestamp(XLogReaderState *record, TimestampTz *recordXtime) { uint8 info = XLogRecGetInfo(record) & ~XLR_INFO_MASK; - uint8 xact_info = info & XLOG_XACT_OPMASK; uint8 rmid = XLogRecGetRmid(record); - if (rmid == RM_XLOG_ID && info == XLOG_RESTORE_POINT) + if (rmid == RM_XLOG_ID) { - *recordXtime = ((xl_restore_point *) XLogRecGetData(record))->rp_time; - return true; - } - if (rmid == RM_XACT_ID && (xact_info == XLOG_XACT_COMMIT || - xact_info == XLOG_XACT_COMMIT_PREPARED)) - { - *recordXtime = ((xl_xact_commit *) XLogRecGetData(record))->xact_time; - return true; + if (info == XLOG_RESTORE_POINT) + { + *recordXtime = ((xl_restore_point *) XLogRecGetData(record))->rp_time; + return true; + } + if (info == XLOG_CHECKPOINT_ONLINE || + info == XLOG_CHECKPOINT_SHUTDOWN) + { + *recordXtime = time_t_to_timestamptz(((CheckPoint *) XLogRecGetData(record))->time); + return true; + } + if (info == XLOG_END_OF_RECOVERY) + { + *recordXtime = ((xl_end_of_recovery *) XLogRecGetData(record))->end_time; + return true; + } } - if (rmid == RM_XACT_ID && (xact_info == XLOG_XACT_ABORT || - xact_info == XLOG_XACT_ABORT_PREPARED)) + if (rmid == RM_XACT_ID) { - *recordXtime = ((xl_xact_abort *) XLogRecGetData(record))->xact_time; - return true; + uint8 xact_info = info & XLOG_XACT_OPMASK; + + if (xact_info == XLOG_XACT_COMMIT || + xact_info == XLOG_XACT_COMMIT_PREPARED) + { + if (recoveryTargetUseOriginTime) + { + xl_xact_commit *xlrec = (xl_xact_commit *) XLogRecGetData(record); + xl_xact_parsed_commit parsed; + + ParseCommitRecord(XLogRecGetInfo(record), + xlrec, + &parsed); + *recordXtime = parsed.origin_timestamp; + } + else + *recordXtime = ((xl_xact_commit *) XLogRecGetData(record))->xact_time; + return true; + } + if (xact_info == XLOG_XACT_ABORT || + xact_info == XLOG_XACT_ABORT_PREPARED) + { + if (recoveryTargetUseOriginTime) + { + xl_xact_abort *xlrec = (xl_xact_abort *) XLogRecGetData(record); + xl_xact_parsed_abort parsed; + + ParseAbortRecord(XLogRecGetInfo(record), + xlrec, + &parsed); + *recordXtime = parsed.origin_timestamp; + } + else + *recordXtime = ((xl_xact_abort *) XLogRecGetData(record))->xact_time; + return true; + } + if (xact_info == XLOG_XACT_PREPARE) + { + if (recoveryTargetUseOriginTime) + { + xl_xact_prepare *xlrec = (xl_xact_prepare *) XLogRecGetData(record); + xl_xact_parsed_prepare parsed; + + ParsePrepareRecord(XLogRecGetInfo(record), + xlrec, + &parsed); + *recordXtime = parsed.origin_timestamp; + } + else + *recordXtime = ((xl_xact_prepare *) XLogRecGetData(record))->prepared_at; + return true; + } } return false; } diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 297e705b80..b30ffccc91 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -1897,6 +1897,16 @@ static struct config_bool ConfigureNamesBool[] = NULL, NULL, NULL }, + { + {"recovery_target_use_origin_time", PGC_POSTMASTER, WAL_RECOVERY_TARGET, + gettext_noop("Sets whether to use local or origin transaction time with recovery target."), + NULL + }, + &recoveryTargetUseOriginTime, + false, + NULL, NULL, NULL + }, + { {"hot_standby", PGC_POSTMASTER, REPLICATION_STANDBY, gettext_noop("Allows connections and queries during recovery."), diff --git a/src/include/access/xlog.h b/src/include/access/xlog.h index 77187c12be..8ab4f1fc67 100644 --- a/src/include/access/xlog.h +++ b/src/include/access/xlog.h @@ -126,6 +126,7 @@ extern char *recoveryRestoreCommand; extern char *recoveryEndCommand; extern char *archiveCleanupCommand; extern bool recoveryTargetInclusive; +extern bool recoveryTargetUseOriginTime; extern int recoveryTargetAction; extern int recovery_min_apply_delay; extern char *PrimaryConnInfo;