1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-07-17 01:12:23 +02:00

Auto-select backup set on restore when time target is specified.

Auto-selection is performed only when --set is not specified. If a backup set for the given target time cannot not be found, the latest (default) backup set will be used.

Currently a limited number of date formats are recognized and timezone names are not allowed, only timezone offsets.
This commit is contained in:
Cynthia Shang
2020-01-30 14:38:05 -07:00
committed by David Steele
parent f46d1fa74c
commit 856980ae99
14 changed files with 276 additions and 41 deletions

View File

@ -14,6 +14,18 @@
<release-list>
<release date="XXXX-XX-XX" version="2.24dev" title="UNDER DEVELOPMENT">
<release-core-list>
<release-feature-list>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="cynthia.shang"/>
</release-item-contributor-list>
<p>Auto-select backup set on restore when time target is specified.</p>
<p>Auto-selection is performed only when <br-option>--set</br-option> is not specified. If a backup set for the given target time cannot not be found, the latest (default) backup set will be used.</p>
</release-item>
</release-feature-list>
<release-improvement-list>
<release-item>
<release-item-contributor-list>

View File

@ -1944,18 +1944,27 @@
</execute>
</execute-list>
<p>Now take a new backup and attempt recovery from the new backup.</p>
<p>Now take a new backup and attempt recovery from the new backup by specifying the <br-option>{[dash]}-set</br-option> option. The <cmd>info</cmd> command can be used to find the new backup label.</p>
<execute-list host="{[host-pg1]}">
<title>Perform a backup then attempt recovery from that backup</title>
<title>Perform a backup and get backup info</title>
<execute user="postgres">
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-type=incr backup</exe-cmd>
</execute>
<execute user="postgres" show="n" variable-key="backup-last">
<exe-cmd>{[cmd-backup-last]}</exe-cmd>
</execute>
<execute user="postgres">
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-type=incr backup</exe-cmd>
<execute user="postgres" filter="n" output="y">
<exe-cmd>{[project-exe]} info</exe-cmd>
<exe-highlight>{[backup-last]}</exe-highlight>
</execute>
</execute-list>
<execute-list host="{[host-pg1]}">
<title>Attempt recovery from the specified backup</title>
<execute user="root">
<exe-cmd>{[pg-cluster-stop]}</exe-cmd>
@ -1963,7 +1972,8 @@
<execute user="postgres">
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-delta
{[dash]}-type=time "{[dash]}-target={[time-recovery-timestamp]}" --target-action=promote restore</exe-cmd>
{[dash]}-set={[backup-last]}
{[dash]}-type=time "{[dash]}-target={[time-recovery-timestamp]}" {[dash]}-target-action=promote restore</exe-cmd>
</execute>
<execute user="root" show="n">
@ -1995,21 +2005,10 @@
</execute>
</execute-list>
<p>Using an earlier backup will allow <postgres/> to play forward to the correct time. The <cmd>info</cmd> command can be used to find the next to last backup.</p>
<p>The default behavior for time-based restore, if the <br-option>{[dash]}-set</br-option> option is not specified, is to attempt to discover an earlier backup to play forward from. If a backup set cannot be found, then restore will default to the latest backup which, as shown earlier, may not give the desired result.</p>
<execute-list host="{[host-pg1]}">
<title>Get backup info for the {[postgres-cluster-demo]} cluster</title>
<execute user="postgres" filter="n" output="y">
<exe-cmd>{[project-exe]} info</exe-cmd>
<exe-highlight>{[backup-last]}</exe-highlight>
</execute>
</execute-list>
<p>The default behavior for restore is to use the last backup but an earlier backup can be specified with the <br-option>{[dash]}-set</br-option> option.</p>
<execute-list host="{[host-pg1]}">
<title>Stop <postgres/>, restore from the selected backup, and start <postgres/></title>
<title>Stop <postgres/>, restore from auto-selected backup, and start <postgres/></title>
<execute user="root">
<exe-cmd>{[pg-cluster-stop]}</exe-cmd>
@ -2019,7 +2018,7 @@
<exe-cmd>
{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-delta
{[dash]}-type=time "{[dash]}-target={[time-recovery-timestamp]}"
{[dash]}-set={[backup-last]} --target-action=promote restore
{[dash]}-target-action=promote restore
</exe-cmd>
</execute>

View File

@ -59,6 +59,8 @@ Recovery constants
STRING_STATIC(RECOVERY_TYPE_PRESERVE_STR, RECOVERY_TYPE_PRESERVE);
#define RECOVERY_TYPE_STANDBY "standby"
STRING_STATIC(RECOVERY_TYPE_STANDBY_STR, RECOVERY_TYPE_STANDBY);
#define RECOVERY_TYPE_TIME "time"
STRING_STATIC(RECOVERY_TYPE_TIME_STR, RECOVERY_TYPE_TIME);
/***********************************************************************************************************************************
Validate restore path
@ -106,6 +108,95 @@ restorePathValidate(void)
FUNCTION_LOG_RETURN_VOID();
}
/***********************************************************************************************************************************
Get epoch time from formatted string
***********************************************************************************************************************************/
static time_t
getEpoch(const String *targetTime)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(STRING, targetTime);
FUNCTION_LOG_END();
ASSERT(targetTime != NULL);
time_t result = 0;
MEM_CONTEXT_TEMP_BEGIN()
{
// Build the regex to accept formats: YYYY-MM-DD HH:MM:SS with optional msec (up to 6 digits and separated from minutes by
// a comma or period), optional timezone offset +/- HH or HHMM or HH:MM, where offset boundaries are UTC-12 to UTC+14
const String *expression = STRDEF(
"^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}((\\,|\\.)[0-9]{1,6})?((\\+|\\-)[0-9]{2}(:?)([0-9]{2})?)?$");
RegExp *regExp = regExpNew(expression);
// If the target-recovery time matches the regular expression then validate it
if (regExpMatch(regExp, targetTime))
{
// Strip off the date and time and put the remainder into another string
String *datetime = strSubN(targetTime, 0, 19);
int dtYear = cvtZToInt(strPtr(strSubN(datetime, 0, 4)));
int dtMonth = cvtZToInt(strPtr(strSubN(datetime, 5, 2)));
int dtDay = cvtZToInt(strPtr(strSubN(datetime, 8, 2)));
int dtHour = cvtZToInt(strPtr(strSubN(datetime, 11, 2)));
int dtMinute = cvtZToInt(strPtr(strSubN(datetime, 14, 2)));
int dtSecond = cvtZToInt(strPtr(strSubN(datetime, 17, 2)));
// Confirm date and time parts are valid
datePartsValid(dtYear, dtMonth, dtDay);
timePartsValid(dtHour, dtMinute, dtSecond);
String *timeTargetZone = strSub(targetTime, 19);
// Find the + or - indicating a timezone offset was provided (there may be milliseconds before the timezone, so need to
// skip). If a timezone offset was not provided, then local time is assumed.
int idxSign = strChr(timeTargetZone, '+');
if (idxSign == -1)
idxSign = strChr(timeTargetZone, '-');
if (idxSign != -1)
{
String *timezoneOffset = strSub(timeTargetZone, (size_t)idxSign);
// Include the sign with the hour
int tzHour = cvtZToInt(strPtr(strSubN(timezoneOffset, 0, 3)));
int tzMinute = 0;
// If minutes are included in timezone offset then extract the minutes based on whether a colon separates them from
// the hour
if (strSize(timezoneOffset) > 3)
tzMinute = cvtZToInt(strPtr(strSubN(timezoneOffset, 3 + (strChr(timezoneOffset, ':') == -1 ? 0 : 1), 2)));
result = epochFromParts(dtYear, dtMonth, dtDay, dtHour, dtMinute, dtSecond, tzOffsetSeconds(tzHour, tzMinute));
}
// If there is no timezone offset, then assume it is local time
else
{
// Set tm_isdst to -1 to force mktime to consider if DST. For example, if system time is America/New_York then
// 2019-09-14 20:02:49 was a time in DST so the Epoch value should be 1568505769 (and not 1568509369 which would be
// 2019-09-14 21:02:49 - an hour too late)
result = mktime(
&(struct tm){.tm_sec = dtSecond, .tm_min = dtMinute, .tm_hour = dtHour, .tm_mday = dtDay, .tm_mon = dtMonth - 1,
.tm_year = dtYear - 1900, .tm_isdst = -1});
}
}
else
{
LOG_WARN_FMT(
"automatic backup set selection cannot be performed with provided time '%s', latest backup set will be used"
"\nHINT: time format must be YYYY-MM-DD HH:MM:SS with optional msec and optional timezone (+/- HH or HHMM or HH:MM)"
" - if timezone is ommitted, local time is assumed (for UTC use +00)",
strPtr(targetTime));
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(TIME, result);
}
/***********************************************************************************************************************************
Get the backup set to restore
***********************************************************************************************************************************/
@ -130,6 +221,41 @@ restoreBackupSet(InfoBackup *infoBackup)
if (infoBackupDataTotal(infoBackup) == 0)
THROW(BackupSetInvalidError, "no backup sets to restore");
time_t timeTargetEpoch = 0;
// If the recovery type is time, attempt to determine the backup set
if (strEq(cfgOptionStr(cfgOptType), RECOVERY_TYPE_TIME_STR))
{
timeTargetEpoch = getEpoch(cfgOptionStr(cfgOptTarget));
// Try to find the newest backup set with a stop time before the target recovery time
if (timeTargetEpoch != 0)
{
// Search current backups from newest to oldest
for (unsigned int keyIdx = infoBackupDataTotal(infoBackup) - 1; (int)keyIdx >= 0; keyIdx--)
{
// Get the backup data
InfoBackupData backupData = infoBackupData(infoBackup, keyIdx);
// If the end of the backup is before the target time, then select this backup
if (backupData.backupTimestampStop < timeTargetEpoch)
{
backupSet = backupData.backupLabel;
break;
}
}
if (backupSet == NULL)
{
LOG_WARN_FMT(
"unable to find backup set with stop time less than '%s', latest backup set will be used",
strPtr(cfgOptionStr(cfgOptTarget)));
}
}
}
// If a backup set was not found or the recovery type was not time, then use the latest backup
if (backupSet == NULL)
backupSet = infoBackupData(infoBackup, infoBackupDataTotal(infoBackup) - 1).backupLabel;
}
// Otherwise check to make sure the specified backup set is valid

View File

@ -623,7 +623,7 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: restore
total: 11
total: 12
containerReq: true
binReq: true

View File

@ -202,8 +202,8 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf
recovery_target_xid = '[XID-TARGET-1]'
recovery_target_action = 'promote'
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
------------------------------------------------------------------------------------------------------------------------------------
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf

View File

@ -251,8 +251,8 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf
recovery_target = 'immediate'
recovery_target_action = 'promote'
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
------------------------------------------------------------------------------------------------------------------------------------
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf

View File

@ -253,8 +253,8 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf
recovery_target = 'immediate'
recovery_target_action = 'promote'
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
------------------------------------------------------------------------------------------------------------------------------------
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf

View File

@ -241,8 +241,8 @@ restore, type 'default' (db-master host)
# Recovery settings generated by pgBackRest restore on [TIMESTAMP]
restore_command = '[BACKREST-BIN] --buffer-size=[BUFFER-SIZE] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db archive-get %f "%p"'
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
------------------------------------------------------------------------------------------------------------------------------------
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf

View File

@ -347,8 +347,8 @@ restore_command = '[BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf
recovery_target = 'immediate'
recovery_target_action = 'promote'
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
------------------------------------------------------------------------------------------------------------------------------------
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf

View File

@ -243,8 +243,8 @@ restore, type 'default' (db-master host)
# Recovery settings generated by pgBackRest restore on [TIMESTAMP]
restore_command = '[BACKREST-BIN] --buffer-size=[BUFFER-SIZE] --config=[TEST_PATH]/db-master/pgbackrest.conf --stanza=db archive-get %f "%p"'
restore delta, backup '[BACKUP-FULL-1]', type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --set=[BACKUP-FULL-1] --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
restore delta, type 'time', target '[TIMESTAMP-TARGET-1]', target-action=promote (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --delta --type=time --target="[TIMESTAMP-TARGET-1]" --link-all --target-action=promote --stanza=db restore
------------------------------------------------------------------------------------------------------------------------------------
+ supplemental file: [TEST_PATH]/db-master/db/base/recovery.conf

View File

@ -464,6 +464,10 @@ sub regExpReplaceAll
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', " \\-\\-target\\=\\\".*UTC", "[^\\\"]+UTC\$");
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', "^recovery_target_time \\= \\'.*UTC", "[^\\']+UTC\$");
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', "\\, target \\'.*\\+00", "[^\\']+\\+00\$");
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', " \\-\\-target\\=\\\".*\\+00", "[^\\\"]+\\+00\$");
$strLine = $self->regExpReplace($strLine, 'TIMESTAMP-TARGET', "^recovery_target_time \\= \\'.*\\+00", "[^\\']+\\+00\$");
# Full test xid-based recovery (this expressions only work when time-based expressions above have already been applied
$strLine = $self->regExpReplace($strLine, 'XID-TARGET', "\\, target \\'[0-9]+", "[0-9]+\$");
$strLine = $self->regExpReplace($strLine, 'XID-TARGET', " \\-\\-target\\=\\\"[0-9]+", "[0-9]+\$");

View File

@ -1537,6 +1537,7 @@ sub restore
$strOptionalParam,
$bTablespace,
$strUser,
$strBackupExpected,
) =
logDebugParam
(
@ -1557,6 +1558,7 @@ sub restore
{name => 'strOptionalParam', optional => true},
{name => 'bTablespace', optional => true},
{name => 'strUser', optional => true},
{name => 'strBackupExpected', optional => true},
);
# Build link map options
@ -1593,10 +1595,15 @@ sub restore
if (!defined($rhExpectedManifest))
{
if (!defined($strBackupExpected))
{
$strBackupExpected = $strBackup eq 'latest' ? $oHostBackup->backupLast() : $strBackup;
}
# Load the manifest
my $oExpectedManifest = new pgBackRest::Manifest(
storageRepo()->pathGet(
STORAGE_REPO_BACKUP . qw{/} . ($strBackup eq 'latest' ? $oHostBackup->backupLast() : $strBackup) . qw{/} .
STORAGE_REPO_BACKUP . qw{/} . $strBackupExpected. qw{/} .
FILE_MANIFEST),
{strCipherPass => $oHostBackup->cipherPassManifest()});

View File

@ -543,11 +543,11 @@ sub run
$oHostDbMaster->start();
}
# Setup the time target
# Setup the time targets
#---------------------------------------------------------------------------------------------------------------------------
$oHostDbMaster->sqlExecute("update test set message = '$strTimeMessage'");
$oHostDbMaster->sqlWalRotate();
my $strTimeTarget = $oHostDbMaster->sqlSelectOne("select to_char(current_timestamp, 'YYYY-MM-DD HH24:MI:SS.US TZ')");
my $strTimeTarget = $oHostDbMaster->sqlSelectOne("select current_timestamp");
&log(INFO, " time target is ${strTimeTarget}");
# Incr backup - fail on archive_mode=always when version >= 9.5
@ -904,18 +904,19 @@ sub run
$oHostDbMaster->sqlExecute("update test set message = '$strTimelineMessage'");
}
# Restore (restore type = time, inclusive) - there is no exclusive time test because I can't find a way to find the
# exact commit time of a transaction.
# Restore (restore type = time, inclusive, automatically select backup) - there is no exclusive time test because I can't
# find a way to find the exact commit time of a transaction.
#---------------------------------------------------------------------------------------------------------------------------
&log(INFO, ' testing recovery type = ' . CFGOPTVAL_RESTORE_TYPE_TIME);
$oHostDbMaster->clusterStop();
$oHostDbMaster->restore(
undef, $strFullBackup,
undef, cfgDefOptionDefault(CFGCMD_RESTORE, CFGOPT_SET),
{bDelta => true, strType => CFGOPTVAL_RESTORE_TYPE_TIME, strTarget => $strTimeTarget,
strTargetAction => $oHostDbMaster->pgVersion() >= PG_VERSION_91 ? 'promote' : undef,
strTargetTimeline => $oHostDbMaster->pgVersion() >= PG_VERSION_12 ? 'current' : undef});
strTargetTimeline => $oHostDbMaster->pgVersion() >= PG_VERSION_12 ? 'current' : undef,
strBackupExpected => $strFullBackup});
$oHostDbMaster->clusterStart();
$oHostDbMaster->sqlSelectOneTest('select message from test', $strTimeMessage);

View File

@ -55,7 +55,7 @@ Test data for backup.info
"\"backup-info-repo-size\":3159811,\"backup-info-repo-size-delta\":15765,\"backup-info-size\":26897030," \
"\"backup-info-size-delta\":163866,\"backup-prior\":\"20161219-212741F\",\"backup-reference\":[\"20161219-212741F\"," \
"\"20161219-212741F_20161219-212803D\"]," \
"\"backup-timestamp-start\":1482182877,\"backup-timestamp-stop\":1482182883,\"backup-type\":\"incr\",\"db-id\":1," \
"\"backup-timestamp-start\":1482182884,\"backup-timestamp-stop\":1482182985,\"backup-type\":\"incr\",\"db-id\":1," \
"\"option-archive-check\":true,\"option-archive-copy\":false,\"option-backup-standby\":false," \
"\"option-checksum-page\":false,\"option-compress\":true,\"option-hardlink\":false,\"option-online\":true}\n"
@ -446,6 +446,44 @@ testRun(void)
storageRemoveP(storagePgWrite(), strNew(PG_FILE_PGVERSION), .errorOnMissing = true);
}
// *****************************************************************************************************************************
if (testBegin("getEpoch()"))
{
TEST_TITLE("system time UTC");
setenv("TZ", "UTC", true);
TEST_RESULT_UINT(getEpoch(strNew("2020-01-08 09:18:15-0700")), 1578500295, "epoch with timezone");
TEST_RESULT_UINT(getEpoch(strNew("2020-01-08 16:18:15.0000")), 1578500295, "same epoch no timezone");
TEST_RESULT_UINT(getEpoch(strNew("2020-01-08 16:18:15.0000+00")), 1578500295, "same epoch timezone 0");
TEST_ERROR_FMT(getEpoch(strNew("2020-13-08 16:18:15")), FormatError, "invalid date 2020-13-08");
TEST_ERROR_FMT(getEpoch(strNew("2020-01-08 16:68:15")), FormatError, "invalid time 16:68:15");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("system time America/New_York");
setenv("TZ", "America/New_York", true);
time_t testTime = 1573754569;
char timeBuffer[20];
strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %H:%M:%S", localtime(&testTime));
TEST_RESULT_Z(timeBuffer, "2019-11-14 13:02:49", "check timezone set");
TEST_RESULT_UINT(getEpoch(strNew("2019-11-14 13:02:49-0500")), 1573754569, "offset same as local");
TEST_RESULT_UINT(getEpoch(strNew("2019-11-14 13:02:49")), 1573754569, "GMT-0500 (EST)");
TEST_RESULT_UINT(getEpoch(strNew("2019-09-14 20:02:49")), 1568505769, "GMT-0400 (EDT)");
TEST_RESULT_UINT(getEpoch(strNew("2018-04-27 04:29:00+04:30")), 1524787140, "GMT+0430");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("invalid target time format");
TEST_RESULT_UINT(getEpoch(strNew("Tue, 15 Nov 1994 12:45:26")), 0, "invalid date time format");
TEST_RESULT_LOG(
"P00 WARN: automatic backup set selection cannot be performed with provided time 'Tue, 15 Nov 1994 12:45:26',"
" latest backup set will be used\n"
" HINT: time format must be YYYY-MM-DD HH:MM:SS with optional msec and optional timezone"
" (+/- HH or HHMM or HH:MM) - if timezone is ommitted, local time is assumed (for UTC use +00)");
setenv("TZ", "UTC", true);
}
// *****************************************************************************************************************************
if (testBegin("restoreBackupSet()"))
{
@ -478,6 +516,54 @@ testRun(void)
harnessCfgLoad(cfgCmdRestore, argList);
TEST_ERROR(restoreBackupSet(infoBackup), BackupSetInvalidError, "backup set BOGUS is not valid");
// -------------------------------------------------------------------------------------------------------------------------
TEST_TITLE("target time");
setenv("TZ", "UTC", true);
infoBackup = infoBackupNewLoad(
ioBufferReadNew(harnessInfoChecksumZ(TEST_RESTORE_BACKUP_INFO "\n" TEST_RESTORE_BACKUP_INFO_DB)));
argList = strLstNew();
strLstAddZ(argList, "--stanza=test1");
strLstAdd(argList, strNewFmt("--repo1-path=%s", strPtr(repoPath)));
strLstAdd(argList, strNewFmt("--pg1-path=%s", strPtr(pgPath)));
strLstAddZ(argList, "--type=time");
strLstAddZ(argList, "--target=2016-12-19 16:28:04-0500");
harnessCfgLoad(cfgCmdRestore, argList);
TEST_RESULT_STR_Z(restoreBackupSet(infoBackup), "20161219-212741F_20161219-212803D", "backup set found");
argList = strLstNew();
strLstAddZ(argList, "--stanza=test1");
strLstAdd(argList, strNewFmt("--repo1-path=%s", strPtr(repoPath)));
strLstAdd(argList, strNewFmt("--pg1-path=%s", strPtr(pgPath)));
strLstAddZ(argList, "--type=time");
strLstAddZ(argList, "--target=2016-12-19 16:27:30-0500");
harnessCfgLoad(cfgCmdRestore, argList);
TEST_RESULT_STR_Z(restoreBackupSet(infoBackup), "20161219-212741F_20161219-212918I", "default to latest backup set");
TEST_RESULT_LOG(
"P00 WARN: unable to find backup set with stop time less than '2016-12-19 16:27:30-0500', latest backup set will be"
" used");
argList = strLstNew();
strLstAddZ(argList, "--stanza=test1");
strLstAdd(argList, strNewFmt("--repo1-path=%s", strPtr(repoPath)));
strLstAdd(argList, strNewFmt("--pg1-path=%s", strPtr(pgPath)));
strLstAddZ(argList, "--type=time");
strLstAddZ(argList, "--target=Tue, 15 Nov 1994 12:45:26");
harnessCfgLoad(cfgCmdRestore, argList);
TEST_RESULT_STR_Z(restoreBackupSet(infoBackup), "20161219-212741F_20161219-212918I", "time invalid format, default latest");
TEST_RESULT_LOG(
"P00 WARN: automatic backup set selection cannot be performed with provided time 'Tue, 15 Nov 1994 12:45:26',"
" latest backup set will be used\n"
" HINT: time format must be YYYY-MM-DD HH:MM:SS with optional msec and optional timezone"
" (+/- HH or HHMM or HH:MM) - if timezone is ommitted, local time is assumed (for UTC use +00)");
}
// *****************************************************************************************************************************