1
0
mirror of https://github.com/pgbackrest/pgbackrest.git synced 2025-01-18 04:58:51 +02:00

The check command is implemented entirely in C.

Note that building the manifest on each host has been temporarily removed.

This feature will likely be brought back as a non-default option (after the manifest code has been fully migrated to C) since it can be fairly expensive.
This commit is contained in:
Cynthia Shang 2019-10-08 18:04:09 -04:00 committed by David Steele
parent ecae5e34e5
commit a1c13a50dd
32 changed files with 883 additions and 804 deletions

View File

@ -15,6 +15,14 @@
<release date="XXXX-XX-XX" version="2.19dev" title="UNDER DEVELOPMENT">
<release-doc-list>
<release-improvement-list>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="cynthia.shang"/>
</release-item-contributor-list>
<p>The <cmd>check</cmd> command is implemented entirely in C.</p>
</release-item>
<release-item>
<release-item-contributor-list>
<release-item-contributor id="cynthia.shang"/>

View File

@ -2786,7 +2786,7 @@
<execute user="postgres" output="y">
<exe-cmd>{[project-exe]} {[dash]}-stanza={[postgres-cluster-demo]} {[dash]}-log-level-console=info check</exe-cmd>
<exe-highlight>because no primary was found</exe-highlight>
<exe-highlight>because this is a standby</exe-highlight>
</execute>
</execute-list>
</section>

View File

@ -1,235 +0,0 @@
####################################################################################################################################
# CHECK MODULE
####################################################################################################################################
package pgBackRest::Check::Check;
use strict;
use warnings FATAL => qw(all);
use Carp qw(confess);
use English '-no_match_vars';
use pgBackRest::Archive::Common;
use pgBackRest::Archive::Get::File;
use pgBackRest::Backup::Info;
use pgBackRest::Common::Exception;
use pgBackRest::Common::Log;
use pgBackRest::Common::Wait;
use pgBackRest::Config::Config;
use pgBackRest::Db;
use pgBackRest::Manifest;
use pgBackRest::Protocol::Helper;
use pgBackRest::Protocol::Storage::Helper;
####################################################################################################################################
# constructor
####################################################################################################################################
sub new
{
my $class = shift; # Class name
# Assign function parameters, defaults, and log debug info
my ($strOperation) = logDebugParam(__PACKAGE__ . '->new');
# Create the class hash
my $self = {};
bless $self, $class;
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'self', value => $self}
);
}
####################################################################################################################################
# process
#
# Validates the database configuration and checks that the archive logs can be read by backup. This will alert the user to any
# misconfiguration, particularly of archiving, that would result in the inability of a backup to complete (e.g waiting at the end
# until it times out because it could not find the WAL file).
####################################################################################################################################
sub process
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my $strOperation = logDebugParam(__PACKAGE__ . '->process');
# Initialize the local variables
my $iArchiveTimeout = cfgOption(CFGOPT_ARCHIVE_TIMEOUT);
my $iResult = 0;
my $strResultMessage = undef;
my $strArchiveId = undef;
my $strArchiveFile = undef;
my $strWalSegment = undef;
# Get the master database object to test to see if the manifest can be built
my ($oDb) = dbMasterGet();
# Get the database version to pass to the manifest constructor and the system-id in the event of a failure
my ($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) = $oDb->info();
# Turn off console logging to control when to display the error
logLevelSet(undef, OFF);
# Loop through all defined databases and attempt to build a manifest
for (my $iRemoteIdx = 1; $iRemoteIdx <= cfgOptionIndexTotal(CFGOPT_PG_HOST); $iRemoteIdx++)
{
# Make sure a db is defined for this index
if (cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)) ||
cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_HOST, $iRemoteIdx)))
{
eval
{
# Passing file location dev/null so that the save will fail if it is ever attempted. Pass a miscellaneous value for
# encryption key since the file will not be saved.
my $oBackupManifest = new pgBackRest::Manifest("/dev/null/manifest.chk",
{bLoad => false, strDbVersion => $strDbVersion, iDbCatalogVersion => $iCatalogVersion,
strCipherPass => 'x', strCipherPassSub => 'x'});
# Set required settings not set during manifest instantiation
$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_ID, undef, 1);
$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CONTROL, undef, $iControlVersion);
$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_SYSTEM_ID, undef, $ullDbSysId);
$oBackupManifest->build(
storageDb({iRemoteIdx => $iRemoteIdx}), cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)), undef,
cfgOptionValid(CFGOPT_ONLINE) && cfgOption(CFGOPT_ONLINE), false, $oDb->tablespaceMapGet());
return true;
}
or do
{
# Capture error information
$strResultMessage = "Database: ${strDbVersion} ${ullDbSysId} " . exceptionMessage($EVAL_ERROR) .
(($iResult != 0) ? "\n[$iResult] : $strResultMessage" : "");
$iResult = exceptionCode($EVAL_ERROR);
};
}
}
# Reset the console logging
logLevelSet(undef, cfgOption(CFGOPT_LOG_LEVEL_CONSOLE));
# If the manifest builds are ok, then proceed with the other checks
if ($iResult == 0)
{
# Reinitialize the database object in order to check the configured replicas. This will throw an error if at least one is not
# able to be connected to and warnings for any that cannot be properly connected to.
($oDb) = dbObjectGet();
# Validate the database configuration
$oDb->configValidate();
# Turn off console logging to control when to display the error
logLevelSet(undef, OFF);
# Check backup.info - if the archive check fails below (e.g --no-archive-check set) then at least know backup.info succeeded
eval
{
# Check that the backup info file is written and is valid for the current database of the stanza
$self->backupInfoCheck();
return true;
}
# If there is an unhandled error then confess
or do
{
# Capture error information
$iResult = exceptionCode($EVAL_ERROR);
$strResultMessage = exceptionMessage($EVAL_ERROR);
};
# Check archive.info
if ($iResult == 0)
{
eval
{
# Check that the archive info file is written and is valid for the current database of the stanza
($strArchiveId) = archiveGetCheck();
return true;
}
or do
{
# Capture error information
$iResult = exceptionCode($EVAL_ERROR);
$strResultMessage = exceptionMessage($EVAL_ERROR);
};
}
# Reset the console logging
logLevelSet(undef, cfgOption(CFGOPT_LOG_LEVEL_CONSOLE));
}
# Log the captured error
if ($iResult != 0)
{
&log(ERROR, $strResultMessage, $iResult);
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'iResult', value => $iResult, trace => true}
);
}
####################################################################################################################################
# backupInfoCheck
#
# Check the backup.info file, if it exists, to confirm the DB version, system-id, control and catalog numbers match the database.
####################################################################################################################################
sub backupInfoCheck
{
my $self = shift;
# Assign function parameters, defaults, and log debug info
my
(
$strOperation,
$strDbVersion,
$iControlVersion,
$iCatalogVersion,
$ullDbSysId,
) =
logDebugParam
(
__PACKAGE__ . '->backupInfoCheck', \@_,
{name => 'strDbVersion', required => false},
{name => 'iControlVersion', required => false},
{name => 'iCatalogVersion', required => false},
{name => 'ullDbSysId', required => false}
);
# If the db info are not passed, then we need to retrieve the database information
my $iDbHistoryId;
if (!defined($strDbVersion) || !defined($iControlVersion) || !defined($iCatalogVersion) || !defined($ullDbSysId))
{
# get DB info for comparison
($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) = dbMasterGet()->info();
}
if (!isRepoLocal())
{
$iDbHistoryId = protocolGet(CFGOPTVAL_REMOTE_TYPE_BACKUP)->cmdExecute(
OP_CHECK_BACKUP_INFO_CHECK, [$strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId]);
}
else
{
$iDbHistoryId = (new pgBackRest::Backup::Info(storageRepo()->pathGet(STORAGE_REPO_BACKUP)))->check(
$strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId);
}
# Return from function and log return values if any
return logDebugReturn
(
$strOperation,
{name => 'iDbHistoryId', value => $iDbHistoryId, trace => true}
);
}
1;

View File

@ -106,17 +106,6 @@ sub main
$oRemote->process(
cfgOption(CFGOPT_LOCK_PATH), cfgOption(CFGOPT_COMMAND), cfgOption(CFGOPT_STANZA, false), cfgOption(CFGOPT_PROCESS));
}
# Process check command
# --------------------------------------------------------------------------------------------------------------------------
elsif (cfgCommandTest(CFGCMD_CHECK))
{
# Load module dynamically
require pgBackRest::Check::Check;
pgBackRest::Check::Check->import();
$iResult = new pgBackRest::Check::Check()->process();
}
else
{
# Check that the repo path exists

View File

@ -26,10 +26,6 @@ use constant OP_BACKUP_FILE => 'backupF
use constant OP_ARCHIVE_GET_CHECK => 'archiveCheck';
push @EXPORT, qw(OP_ARCHIVE_GET_CHECK);
# Check Module
use constant OP_CHECK_BACKUP_INFO_CHECK => 'backupInfoCheck';
push @EXPORT, qw(OP_CHECK_BACKUP_INFO_CHECK);
# Db Module
use constant OP_DB_CONNECT => 'dbConnect';
push @EXPORT, qw(OP_DB_CONNECT);

View File

@ -15,7 +15,6 @@ use pgBackRest::Common::Log;
use pgBackRest::Common::Io::Buffered;
use pgBackRest::Common::Wait;
use pgBackRest::Archive::Get::File;
use pgBackRest::Check::Check;
use pgBackRest::Config::Config;
use pgBackRest::Db;
use pgBackRest::Protocol::Command::Minion;
@ -68,7 +67,6 @@ sub init
# Create objects
my $oStorage = cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_DB) ? storageDb() : storageRepo();
my $oCheck = cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_BACKUP) ? new pgBackRest::Check::Check() : undef;
my $oDb = cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_DB) ? new pgBackRest::Db() : undef;
# Create anonymous subs for each command
@ -77,9 +75,6 @@ sub init
# ArchiveGet commands
&OP_ARCHIVE_GET_CHECK => sub {archiveGetCheck(@{shift()})},
# Check commands
&OP_CHECK_BACKUP_INFO_CHECK => sub {$oCheck->backupInfoCheck(@{shift()})},
# Db commands
&OP_DB_CONNECT => sub {$oDb->connect()},
&OP_DB_EXECUTE_SQL => sub {$oDb->executeSql(@{shift()})},

View File

@ -246,10 +246,10 @@ command/backup/pageChecksum.o: command/backup/pageChecksum.c build.auto.h comman
command/backup/protocol.o: command/backup/protocol.c build.auto.h command/backup/file.h command/backup/protocol.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/io.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h protocol/server.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/backup/protocol.c -o command/backup/protocol.o
command/check/check.o: command/check/check.c build.auto.h command/archive/common.h command/check/check.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h db/db.h db/helper.h info/info.h info/infoArchive.h info/infoPg.h postgres/client.h protocol/client.h protocol/command.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
command/check/check.o: command/check/check.c build.auto.h command/archive/common.h command/check/check.h command/check/common.h common/assert.h common/crypto/common.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h db/db.h db/helper.h info/info.h info/infoArchive.h info/infoPg.h postgres/client.h postgres/interface.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/check/check.c -o command/check/check.o
command/check/common.o: command/check/common.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h db/db.h db/helper.h postgres/client.h postgres/interface.h protocol/client.h protocol/command.h storage/info.h storage/read.h storage/storage.h storage/write.h
command/check/common.o: command/check/common.c build.auto.h command/backup/common.h command/check/common.h common/assert.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h db/db.h db/helper.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/client.h postgres/interface.h protocol/client.h protocol/command.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h version.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/check/common.c -o command/check/common.o
command/command.o: command/command.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/http/client.h common/io/http/header.h common/io/http/query.h common/io/read.h common/io/tls/client.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h version.h
@ -291,13 +291,13 @@ command/restore/restore.o: command/restore/restore.c build.auto.h command/backup
command/stanza/common.o: command/stanza/common.c build.auto.h command/check/common.h common/assert.h common/crypto/common.h common/debug.h common/encode.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h db/db.h db/helper.h info/info.h info/infoPg.h postgres/client.h postgres/interface.h postgres/version.h protocol/client.h protocol/command.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/stanza/common.c -o command/stanza/common.o
command/stanza/create.o: command/stanza/create.c build.auto.h command/backup/common.h command/control/common.h command/stanza/common.h command/stanza/create.h common/assert.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/interface.h postgres/version.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
command/stanza/create.o: command/stanza/create.c build.auto.h command/backup/common.h command/check/common.h command/control/common.h command/stanza/common.h command/stanza/create.h common/assert.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h db/db.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/client.h postgres/interface.h postgres/version.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/stanza/create.c -o command/stanza/create.o
command/stanza/delete.o: command/stanza/delete.c build.auto.h command/backup/common.h command/control/common.h command/stanza/delete.h common/assert.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/interface.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/stanza/delete.c -o command/stanza/delete.o
command/stanza/upgrade.o: command/stanza/upgrade.c build.auto.h command/backup/common.h command/control/common.h command/stanza/common.h command/stanza/upgrade.h common/assert.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/interface.h postgres/version.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
command/stanza/upgrade.o: command/stanza/upgrade.c build.auto.h command/backup/common.h command/check/common.h command/control/common.h command/stanza/common.h command/stanza/upgrade.h common/assert.h common/crypto/common.h common/crypto/hash.h common/debug.h common/error.auto.h common/error.h common/ini.h common/io/filter/filter.h common/io/filter/group.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h db/db.h info/info.h info/infoArchive.h info/infoBackup.h info/infoPg.h info/manifest.h postgres/client.h postgres/interface.h postgres/version.h protocol/client.h protocol/command.h protocol/helper.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h
$(CC) $(CPPFLAGS) $(CFLAGS) $(CMAKE) -c command/stanza/upgrade.c -o command/stanza/upgrade.o
command/storage/list.o: command/storage/list.c build.auto.h common/assert.h common/debug.h common/error.auto.h common/error.h common/io/filter/filter.h common/io/filter/group.h common/io/handleWrite.h common/io/read.h common/io/write.h common/lock.h common/log.h common/logLevel.h common/memContext.h common/stackTrace.h common/time.h common/type/buffer.h common/type/convert.h common/type/json.h common/type/keyValue.h common/type/list.h common/type/string.h common/type/stringList.h common/type/variant.h common/type/variantList.h config/config.auto.h config/config.h config/define.auto.h config/define.h storage/helper.h storage/info.h storage/read.h storage/storage.h storage/write.h

View File

@ -5,14 +5,157 @@ Check Command
#include "command/archive/common.h"
#include "command/check/check.h"
#include "command/check/common.h"
#include "common/debug.h"
#include "common/log.h"
#include "common/memContext.h"
#include "config/config.h"
#include "db/helper.h"
#include "info/infoArchive.h"
#include "postgres/interface.h"
#include "protocol/helper.h"
#include "storage/helper.h"
/***********************************************************************************************************************************
Helper functions (to assist with testing)
***********************************************************************************************************************************/
static unsigned int
checkManifest(void)
{
FUNCTION_LOG_VOID(logLevelTrace);
// Return the actual number of pg* defined
unsigned int result = 0;
MEM_CONTEXT_TEMP_BEGIN()
{
// Loop through all defined databases and attempt to build a manifest
for (unsigned int pgIdx = 0; pgIdx < cfgOptionIndexTotal(cfgOptPgPath); pgIdx++)
{
if (cfgOptionTest(cfgOptPgHost + pgIdx) || cfgOptionTest(cfgOptPgPath + pgIdx))
{
result++;
// ??? Placeholder for manifest build
storageListNP(storagePgId(pgIdx + 1), varStr(cfgOption(cfgOptPgPath + pgIdx)));
}
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_LOG_RETURN(UINT, result);
}
static void
checkStandby(const DbGetResult dbGroup, unsigned int pgPathDefinedTotal)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(DB_GET_RESULT, dbGroup);
FUNCTION_LOG_PARAM(UINT, pgPathDefinedTotal);
FUNCTION_LOG_END();
// If a standby is defined, check the configuration
if (dbGroup.standby != NULL)
{
// If primary was not found
if (dbGroup.primary == NULL)
{
// If the repo is local or more than one pg-path is found then a master should have been found so error
if (repoIsLocal() || pgPathDefinedTotal > 1)
{
THROW(
ConfigError,
"primary database not found\n"
"HINT: check indexed pg-path/pg-host configurations");
}
}
// Validate the standby database config
PgControl pgControl = pgControlFromFile(storagePgId(dbGroup.standbyId));
// Check the user configured path and version against the database
checkDbConfig(pgControl.version, dbGroup.standbyId, dbGroup.standby, true);
// Get the repo storage in case it is remote and encryption settings need to be pulled down (performed here for testing)
storageRepo();
// Check that the backup and archive info files exist and are valid for the current database of the stanza
checkStanzaInfoPg(
storageRepo(), pgControl.version, pgControl.systemId, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass));
LOG_INFO("switch wal not performed because this is a standby");
// Free the standby connection
dbFree(dbGroup.standby);
}
// If backup from standby is true then warn when a standby not found
else if (cfgOptionBool(cfgOptBackupStandby))
{
LOG_WARN("option '%s' is enabled but standby is not properly configured", cfgOptionName(cfgOptBackupStandby));
}
FUNCTION_LOG_RETURN_VOID();
}
static void
checkPrimary(const DbGetResult dbGroup)
{
FUNCTION_LOG_BEGIN(logLevelDebug);
FUNCTION_LOG_PARAM(DB_GET_RESULT, dbGroup);
FUNCTION_LOG_END();
// If a primary is defined, check the configuration and perform a WAL switch and make sure the WAL is archived
if (dbGroup.primary != NULL)
{
// Validate the primary database config
PgControl pgControl = pgControlFromFile(storagePgId(dbGroup.primaryId));
// Check the user configured path and version against the database
checkDbConfig(pgControl.version, dbGroup.primaryId, dbGroup.primary, false);
// Get the repo storage in case it is remote and encryption settings need to be pulled down (performed here for testing)
storageRepo();
// Check that the backup and archive info files exist and are valid for the current database of the stanza
checkStanzaInfoPg(
storageRepo(), pgControl.version, pgControl.systemId, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass));
// Attempt to load the archive info file and retrieve the archiveId
InfoArchive *archiveInfo = infoArchiveLoadFile(
storageRepo(), INFO_ARCHIVE_PATH_FILE_STR, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass));
const String *archiveId = infoArchiveId(archiveInfo);
// Perform a WAL switch
const String *walSegment = dbWalSwitch(dbGroup.primary);
dbFree(dbGroup.primary);
// Wait for the WAL to appear in the repo
TimeMSec archiveTimeout = (TimeMSec)(cfgOptionDbl(cfgOptArchiveTimeout) * MSEC_PER_SEC);
const String *walSegmentFile = walSegmentFind(storageRepo(), archiveId, walSegment, archiveTimeout);
if (walSegmentFile != NULL)
{
LOG_INFO(
"WAL segment %s successfully archived to '%s'", strPtr(walSegment),
strPtr(storagePath(storageRepo(), strNewFmt(STORAGE_REPO_ARCHIVE "/%s/%s", strPtr(archiveId),
strPtr(walSegmentFile)))));
}
else
{
THROW_FMT(
ArchiveTimeoutError,
"WAL segment %s was not archived before the %" PRIu64 "ms timeout\n"
"HINT: check the archive_command to ensure that all options are correct (especially --stanza).\n"
"HINT: check the PostgreSQL server log for errors.",
strPtr(walSegment), archiveTimeout);
}
}
FUNCTION_LOG_RETURN_VOID();
}
/***********************************************************************************************************************************
Perform standard checks
***********************************************************************************************************************************/
@ -23,53 +166,15 @@ cmdCheck(void)
MEM_CONTEXT_TEMP_BEGIN()
{
// Get the repo storage in case it is remote and encryption settings need to be pulled down
storageRepo();
// Attempt to load the archive info file
InfoArchive *archiveInfo = infoArchiveLoadFile(
storageRepo(), INFO_ARCHIVE_PATH_FILE_STR, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass));
const String *archiveId = infoArchiveId(archiveInfo);
// Get the primary/standby connections (standby is only required if backup from standby is enabled)
DbGetResult dbGroup = dbGet(false, false);
// Free the standby connection immediately since we don't need it for anything
dbFree(dbGroup.standby);
// Perform a WAL switch and make sure the WAL is archived if a primary was found
if (dbGroup.primary != NULL)
{
// Perform WAL switch
const String *walSegment = dbWalSwitch(dbGroup.primary);
dbFree(dbGroup.primary);
// Wait for the WAL to appear in the repo
TimeMSec archiveTimeout = (TimeMSec)(cfgOptionDbl(cfgOptArchiveTimeout) * MSEC_PER_SEC);
const String *walSegmentFile = walSegmentFind(storageRepo(), archiveId, walSegment, archiveTimeout);
if (walSegmentFile != NULL)
{
LOG_INFO(
"WAL segment %s successfully archived to '%s'", strPtr(walSegment),
strPtr(
storagePath(
storageRepo(), strNewFmt(STORAGE_REPO_ARCHIVE "/%s/%s", strPtr(archiveId), strPtr(walSegmentFile)))));
}
else
{
THROW_FMT(
ArchiveTimeoutError,
"WAL segment %s was not archived before the %" PRIu64 "ms timeout\n"
"HINT: check the archive_command to ensure that all options are correct (especially --stanza).\n"
"HINT: check the PostgreSQL server log for errors.",
strPtr(walSegment), archiveTimeout);
}
}
else
LOG_INFO("switch wal not performed because no primary was found");
if (dbGroup.standby == NULL && dbGroup.primary == NULL)
THROW(ConfigError, "no database found\nHINT: check indexed pg-path/pg-host configurations");
unsigned int pgPathDefinedTotal = checkManifest();
checkStandby(dbGroup, pgPathDefinedTotal);
checkPrimary(dbGroup);
}
MEM_CONTEXT_TEMP_END();

View File

@ -3,39 +3,167 @@ Check Common Handler
***********************************************************************************************************************************/
#include "build.auto.h"
#include <string.h>
#include "command/check/common.h"
#include "common/debug.h"
#include "config/config.h"
#include "db/db.h"
#include "db/helper.h"
#include "info/infoArchive.h"
#include "info/infoBackup.h"
#include "postgres/interface.h"
#include "storage/helper.h"
#include "version.h"
/***********************************************************************************************************************************
Helper function
***********************************************************************************************************************************/
static bool
checkArchiveCommand(const String *archiveCommand)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(STRING, archiveCommand);
FUNCTION_TEST_END();
bool result = archiveCommand != NULL;
if (result && strstr(strPtr(archiveCommand), PROJECT_BIN) == NULL)
result = false;
if (!result)
{
THROW_FMT(
ArchiveCommandInvalidError, "archive_command '%s' must contain %s", (archiveCommand != NULL ? strPtr(archiveCommand)
: "[null]"), PROJECT_BIN);
}
FUNCTION_TEST_RETURN(result);
}
/***********************************************************************************************************************************
Check the database path and version are configured correctly
***********************************************************************************************************************************/
void
checkDbConfig(const unsigned int pgVersion, const unsigned int dbIdx, const unsigned int dbVersion, const String *dbPath)
checkDbConfig(const unsigned int pgVersion, const unsigned int dbIdx, const Db *dbObject, bool isStandby)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(UINT, pgVersion);
FUNCTION_TEST_PARAM(UINT, dbIdx);
FUNCTION_TEST_PARAM(UINT, dbVersion);
FUNCTION_TEST_PARAM(STRING, dbPath);
FUNCTION_TEST_PARAM(DB, dbObject);
FUNCTION_TEST_PARAM(BOOL, isStandby);
FUNCTION_TEST_END();
ASSERT(dbIdx > 0);
ASSERT(dbPath != NULL);
ASSERT(dbObject != NULL);
unsigned int pgPath = cfgOptPgPath + (dbIdx - 1);
MEM_CONTEXT_TEMP_BEGIN()
{
unsigned int dbVersion = dbPgVersion(dbObject);
const String *dbPath = dbPgDataPath(dbObject);
unsigned int pgPath = cfgOptPgPath + (dbIdx - 1);
// Error if the version from the control file and the configured pg-path do not match the values obtained from the database
if (pgVersion != dbVersion || strCmp(cfgOptionStr(pgPath), dbPath) != 0)
// Error if the version from the control file and the configured pg-path do not match the values obtained from the database
if (pgVersion != dbVersion || strCmp(cfgOptionStr(pgPath), dbPath) != 0)
{
THROW_FMT(
DbMismatchError, "version '%s' and path '%s' queried from cluster do not match version '%s' and '%s' read from '%s/"
PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL "'\nHINT: the %s and %s settings likely reference different clusters.",
strPtr(pgVersionToStr(dbVersion)), strPtr(dbPath), strPtr(pgVersionToStr(pgVersion)), strPtr(cfgOptionStr(pgPath)),
strPtr(cfgOptionStr(pgPath)), cfgOptionName(pgPath), cfgOptionName(cfgOptPgPort + (dbIdx - 1)));
}
// Check archive configuration if option is valid for the command and set
if (!isStandby && cfgOptionValid(cfgOptArchiveCheck) && cfgOptionBool(cfgOptArchiveCheck))
{
// Error if archive_mode = off since pg_start_backup () will fail
if (strCmpZ(dbArchiveMode(dbObject), "off") == 0)
{
THROW(ArchiveDisabledError, "archive_mode must be enabled");
}
// Error if archive_mode = always (support has not been added yet)
if (strCmpZ(dbArchiveMode(dbObject), "always") == 0)
{
THROW(FeatureNotSupportedError, "archive_mode=always not supported");
}
// Check if archive_command is set and is valid
checkArchiveCommand(dbArchiveCommand(dbObject));
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RETURN_VOID();
}
/***********************************************************************************************************************************
Validate the archive and backup info files
***********************************************************************************************************************************/
void
checkStanzaInfo(const InfoPgData *archiveInfo, const InfoPgData *backupInfo)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(INFO_PG_DATA, archiveInfo);
FUNCTION_TEST_PARAM_P(INFO_PG_DATA, backupInfo);
FUNCTION_TEST_END();
ASSERT(archiveInfo != NULL);
ASSERT(backupInfo != NULL);
// Error if there is a mismatch between the archive and backup info files
if (archiveInfo->id != backupInfo->id || archiveInfo->systemId != backupInfo->systemId ||
archiveInfo->version != backupInfo->version)
{
THROW_FMT(
DbMismatchError, "version '%s' and path '%s' queried from cluster do not match version '%s' and '%s' read from '%s/"
PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL "'\nHINT: the %s and %s settings likely reference different clusters.",
strPtr(pgVersionToStr(dbVersion)), strPtr(dbPath), strPtr(pgVersionToStr(pgVersion)), strPtr(cfgOptionStr(pgPath)),
strPtr(cfgOptionStr(pgPath)), cfgOptionName(pgPath), cfgOptionName(cfgOptPgPort + (dbIdx - 1)));
FileInvalidError, "backup info file and archive info file do not match\n"
"archive: id = %u, version = %s, system-id = %" PRIu64 "\n"
"backup : id = %u, version = %s, system-id = %" PRIu64 "\n"
"HINT: this may be a symptom of repository corruption!",
archiveInfo->id, strPtr(pgVersionToStr(archiveInfo->version)), archiveInfo->systemId, backupInfo->id,
strPtr(pgVersionToStr(backupInfo->version)), backupInfo->systemId);
}
FUNCTION_TEST_RETURN_VOID();
}
/***********************************************************************************************************************************
Load and validate the database data of the info files against each other and the current database
***********************************************************************************************************************************/
void
checkStanzaInfoPg(
const Storage *storage, const unsigned int pgVersion, const uint64_t pgSystemId, CipherType cipherType,
const String *cipherPass)
{
FUNCTION_TEST_BEGIN();
FUNCTION_LOG_PARAM(STORAGE, storage);
FUNCTION_LOG_PARAM(UINT, pgVersion);
FUNCTION_LOG_PARAM(UINT64, pgSystemId);
FUNCTION_LOG_PARAM(ENUM, cipherType);
FUNCTION_TEST_PARAM(STRING, cipherPass);
FUNCTION_TEST_END();
ASSERT(storage != NULL);
MEM_CONTEXT_TEMP_BEGIN()
{
// Check that the backup and archive info files exist
InfoArchive *infoArchive = infoArchiveLoadFile(storage, INFO_ARCHIVE_PATH_FILE_STR, cipherType, cipherPass);
InfoPgData archiveInfoPg = infoPgData(infoArchivePg(infoArchive), infoPgDataCurrentId(infoArchivePg(infoArchive)));
InfoBackup *infoBackup = infoBackupLoadFile(storage, INFO_BACKUP_PATH_FILE_STR, cipherType, cipherPass);
InfoPgData backupInfoPg = infoPgData(infoBackupPg(infoBackup), infoPgDataCurrentId(infoBackupPg(infoBackup)));
// Check that the info files pg data match each other
checkStanzaInfo(&archiveInfoPg, &backupInfoPg);
// Check that the version and system id match the current database
if (pgVersion != archiveInfoPg.version || pgSystemId != archiveInfoPg.systemId)
{
THROW(FileInvalidError, "backup and archive info files exist but do not match the database\n"
"HINT: is this the correct stanza?\n"
"HINT: did an error occur during stanza-upgrade?");
}
}
MEM_CONTEXT_TEMP_END();
FUNCTION_TEST_RETURN_VOID();
}

View File

@ -5,10 +5,16 @@ Check Command Common
#define COMMAND_CHECK_COMMON_H
#include "common/type/string.h"
#include "db/db.h"
#include "info/infoPg.h"
/***********************************************************************************************************************************
Functions
***********************************************************************************************************************************/
void checkDbConfig(const unsigned int pgVersion, const unsigned int dbIdx, const unsigned int dbVersion, const String *dbPath);
void checkDbConfig(const unsigned int pgVersion, const unsigned int dbIdx, const Db *dbObject, bool isStandby);
void checkStanzaInfo(const InfoPgData *archiveInfo, const InfoPgData *backupInfo);
void checkStanzaInfoPg(
const Storage *storage, const unsigned int pgVersion, const uint64_t pgSystemId, CipherType cipherType,
const String *cipherPass);
#endif

View File

@ -38,36 +38,6 @@ cipherPassGen(CipherType cipherType)
FUNCTION_TEST_RETURN(result);
}
/***********************************************************************************************************************************
Validate the archive and backup info files
***********************************************************************************************************************************/
void
infoValidate(const InfoPgData *archiveInfo, const InfoPgData *backupInfo)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM_P(INFO_PG_DATA, archiveInfo);
FUNCTION_TEST_PARAM_P(INFO_PG_DATA, backupInfo);
FUNCTION_TEST_END();
ASSERT(archiveInfo != NULL);
ASSERT(backupInfo != NULL);
// Error if there is a mismatch between the archive and backup info files
if (archiveInfo->id != backupInfo->id || archiveInfo->systemId != backupInfo->systemId ||
archiveInfo->version != backupInfo->version)
{
THROW_FMT(
FileInvalidError, "backup info file and archive info file do not match\n"
"archive: id = %u, version = %s, system-id = %" PRIu64 "\n"
"backup : id = %u, version = %s, system-id = %" PRIu64 "\n"
"HINT: this may be a symptom of repository corruption!",
archiveInfo->id, strPtr(pgVersionToStr(archiveInfo->version)), archiveInfo->systemId, backupInfo->id,
strPtr(pgVersionToStr(backupInfo->version)), backupInfo->systemId);
}
FUNCTION_TEST_RETURN_VOID();
}
/***********************************************************************************************************************************
Validate and return database information
***********************************************************************************************************************************/
@ -89,7 +59,7 @@ pgValidate(void)
result = pgControlFromFile(storagePgId(dbObject.primaryId));
// Check the user configured path and version against the database
checkDbConfig(result.version, dbObject.primaryId, dbPgVersion(dbObject.primary), dbPgDataPath(dbObject.primary));
checkDbConfig(result.version, dbObject.primaryId, dbObject.primary, false);
}
// If the database is not online, assume that pg1 is the master
else

View File

@ -11,7 +11,6 @@ Stanza Commands Handler
Functions
***********************************************************************************************************************************/
String *cipherPassGen(CipherType cipherType);
void infoValidate(const InfoPgData *archiveInfo, const InfoPgData *backupInfo);
PgControl pgValidate(void);
#endif

View File

@ -7,6 +7,7 @@ Stanza Create Command
#include <string.h>
#include <inttypes.h>
#include "command/check/common.h"
#include "command/control/common.h"
#include "command/stanza/common.h"
#include "command/stanza/create.h"
@ -74,6 +75,7 @@ cmdStanzaCreate(void)
// Create and save archive info
infoArchive = infoArchiveNew(pgControl.version, pgControl.systemId, cipherPassSub);
infoArchiveSaveFile(
infoArchive, storageRepoWriteStanza, INFO_ARCHIVE_PATH_FILE_STR, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass));
@ -83,6 +85,7 @@ cmdStanzaCreate(void)
// Create and save backup info
infoBackup = infoBackupNew(pgControl.version, pgControl.systemId, cipherPassSub);
infoBackupSaveFile(
infoBackup, storageRepoWriteStanza, INFO_BACKUP_PATH_FILE_STR, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass));
@ -90,59 +93,41 @@ cmdStanzaCreate(void)
// Else if at least one archive and one backup info file exists, then ensure both are valid
else if ((archiveInfoFileExists || archiveInfoFileCopyExists) && (backupInfoFileExists || backupInfoFileCopyExists))
{
infoArchive = infoArchiveLoadFile(
storageRepoReadStanza, INFO_ARCHIVE_PATH_FILE_STR, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
// Error if there is a mismatch between the archive and backup info files or the database version/system Id matches
// current database
checkStanzaInfoPg(
storageRepoReadStanza, pgControl.version, pgControl.systemId, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass));
InfoPgData archiveInfo = infoPgData(infoArchivePg(infoArchive), infoPgDataCurrentId(infoArchivePg(infoArchive)));
infoBackup = infoBackupLoadFile(
storageRepoReadStanza, INFO_BACKUP_PATH_FILE_STR, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass));
InfoPgData backupInfo = infoPgData(infoBackupPg(infoBackup), infoPgDataCurrentId(infoBackupPg(infoBackup)));
// The files are valid - upgrade
const String *sourceFile = NULL;
const String *destinationFile = NULL;
// Error if there is a mismatch between the archive and backup info files
infoValidate(&archiveInfo, &backupInfo);
// The archive and backup info files match so check if the versions or system ids match the current database,
// if not, then an upgrade may be necessary
if (pgControl.version != archiveInfo.version || pgControl.systemId != archiveInfo.systemId)
// If the existing files are valid, then, if a file is missing, copy the existing one to the missing one to ensure
// there is both a .info and .info.copy
if (!archiveInfoFileExists || !archiveInfoFileCopyExists)
{
THROW(FileInvalidError, "backup and archive info files already exist but do not match the database\n"
"HINT: is this the correct stanza?\n"
"HINT: did an error occur during stanza-upgrade?");
sourceFile = archiveInfoFileExists ? INFO_ARCHIVE_PATH_FILE_STR : INFO_ARCHIVE_PATH_FILE_COPY_STR;
destinationFile = !archiveInfoFileExists ? INFO_ARCHIVE_PATH_FILE_STR : INFO_ARCHIVE_PATH_FILE_COPY_STR;
storageCopyNP(
storageNewReadNP(storageRepoReadStanza, sourceFile),
storageNewWriteNP(storageRepoWriteStanza, destinationFile));
}
// Else the files are valid
else
if (!backupInfoFileExists || !backupInfoFileCopyExists)
{
const String *sourceFile = NULL;
const String *destinationFile = NULL;
sourceFile = backupInfoFileExists ? INFO_BACKUP_PATH_FILE_STR : INFO_BACKUP_PATH_FILE_COPY_STR;
destinationFile = !backupInfoFileExists ? INFO_BACKUP_PATH_FILE_STR : INFO_BACKUP_PATH_FILE_COPY_STR;
// If the existing files are valid, then, if a file is missing, copy the existing one to the missing one to ensure
// there is both a .info and .info.copy
if (!archiveInfoFileExists || !archiveInfoFileCopyExists)
{
sourceFile = archiveInfoFileExists ? INFO_ARCHIVE_PATH_FILE_STR : INFO_ARCHIVE_PATH_FILE_COPY_STR;
destinationFile = !archiveInfoFileExists ? INFO_ARCHIVE_PATH_FILE_STR : INFO_ARCHIVE_PATH_FILE_COPY_STR;
storageCopyNP(
storageNewReadNP(storageRepoReadStanza, sourceFile),
storageNewWriteNP(storageRepoWriteStanza, destinationFile));
}
if (!backupInfoFileExists || !backupInfoFileCopyExists)
{
sourceFile = backupInfoFileExists ? INFO_BACKUP_PATH_FILE_STR : INFO_BACKUP_PATH_FILE_COPY_STR;
destinationFile = !backupInfoFileExists ? INFO_BACKUP_PATH_FILE_STR : INFO_BACKUP_PATH_FILE_COPY_STR;
storageCopyNP(
storageNewReadNP(storageRepoReadStanza, sourceFile),
storageNewWriteNP(storageRepoWriteStanza, destinationFile));
}
// If no files copied, then the stanza was already valid
if (sourceFile == NULL)
LOG_INFO("stanza '%s' already exists and is valid", strPtr(cfgOptionStr(cfgOptStanza)));
storageCopyNP(
storageNewReadNP(storageRepoReadStanza, sourceFile),
storageNewWriteNP(storageRepoWriteStanza, destinationFile));
}
// If no files copied, then the stanza was already valid
if (sourceFile == NULL)
LOG_INFO("stanza '%s' already exists and is valid", strPtr(cfgOptionStr(cfgOptStanza)));
}
// Else if both .info and corresponding .copy file are missing for one but not the other, then error
else

View File

@ -7,6 +7,7 @@ Stanza Update Command
#include <string.h>
#include <inttypes.h>
#include "command/check/common.h"
#include "command/control/common.h"
#include "command/stanza/common.h"
#include "command/stanza/upgrade.h"
@ -74,7 +75,7 @@ cmdStanzaUpgrade(void)
// needed to be updated)
backupInfo = infoPgData(infoBackupPg(infoBackup), infoPgDataCurrentId(infoBackupPg(infoBackup)));
archiveInfo = infoPgData(infoArchivePg(infoArchive), infoPgDataCurrentId(infoArchivePg(infoArchive)));
infoValidate(&archiveInfo, &backupInfo);
checkStanzaInfo(&archiveInfo, &backupInfo);
// Save archive info
if (infoArchiveUpgrade)

View File

@ -26,6 +26,8 @@ struct Db
unsigned int pgVersion; // Version as reported by the database
const String *pgDataPath; // Data directory reported by the database
const String *archiveMode; // The archive_mode reported by the database
const String *archiveCommand; // The archive_command reported by the database
};
OBJECT_DEFINE_MOVE(DB);
@ -203,17 +205,21 @@ dbOpen(Db *this)
this,
STRDEF(
"select (select setting from pg_catalog.pg_settings where name = 'server_version_num')::int4,"
" (select setting from pg_catalog.pg_settings where name = 'data_directory')::text"));
" (select setting from pg_catalog.pg_settings where name = 'data_directory')::text,"
" (select setting from pg_catalog.pg_settings where name = 'archive_mode')::text,"
" (select setting from pg_catalog.pg_settings where name = 'archive_command')::text"));
// Strip the minor version off since we don't need it. In the future it might be a good idea to warn users when they are
// running an old minor version.
this->pgVersion = varUIntForce(varLstGet(row, 0)) / 100 * 100;
// Store the data directory that PostgreSQL is running in. This can be compared to the configured pgBackRest directory when
// validating the configuration.
// Store the data directory that PostgreSQL is running in, the archive mode, and archive command. These can be compared to
// the configured pgBackRest directory, and archive settings checked for validity, when validating the configuration.
MEM_CONTEXT_BEGIN(this->memContext)
{
this->pgDataPath = strDup(varStr(varLstGet(row, 1)));
this->archiveMode = strDup(varStr(varLstGet(row, 2)));
this->archiveCommand = strDup(varStr(varLstGet(row, 3)));
}
MEM_CONTEXT_END();
@ -313,6 +319,36 @@ dbPgVersion(const Db *this)
FUNCTION_TEST_RETURN(this->pgVersion);
}
/***********************************************************************************************************************************
Get pg version loaded from the server_version_num GUC
***********************************************************************************************************************************/
const String *
dbArchiveMode(const Db *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(DB, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->archiveMode);
}
/***********************************************************************************************************************************
Get pg version loaded from the server_version_num GUC
***********************************************************************************************************************************/
const String *
dbArchiveCommand(const Db *this)
{
FUNCTION_TEST_BEGIN();
FUNCTION_TEST_PARAM(DB, this);
FUNCTION_TEST_END();
ASSERT(this != NULL);
FUNCTION_TEST_RETURN(this->archiveCommand);
}
/***********************************************************************************************************************************
Render as string for logging
***********************************************************************************************************************************/

View File

@ -38,6 +38,8 @@ Getters
***********************************************************************************************************************************/
const String *dbPgDataPath(const Db *this);
unsigned int dbPgVersion(const Db *this);
const String *dbArchiveMode(const Db *this);
const String *dbArchiveCommand(const Db *this);
/***********************************************************************************************************************************
Destructor

View File

@ -133,8 +133,6 @@ main(int argListSize, const char *argList[])
// -----------------------------------------------------------------------------------------------------------------
case cfgCmdCheck:
{
// Functionality is currently split between Perl and C
perlExec();
cmdCheck();
break;
}

View File

@ -3192,204 +3192,6 @@ static const EmbeddedModule embeddedModule[] =
"\n"
"1;\n"
},
{
.name = "pgBackRest/Check/Check.pm",
.data =
"\n\n\n"
"package pgBackRest::Check::Check;\n"
"\n"
"use strict;\n"
"use warnings FATAL => qw(all);\n"
"use Carp qw(confess);\n"
"use English '-no_match_vars';\n"
"\n"
"use pgBackRest::Archive::Common;\n"
"use pgBackRest::Archive::Get::File;\n"
"use pgBackRest::Backup::Info;\n"
"use pgBackRest::Common::Exception;\n"
"use pgBackRest::Common::Log;\n"
"use pgBackRest::Common::Wait;\n"
"use pgBackRest::Config::Config;\n"
"use pgBackRest::Db;\n"
"use pgBackRest::Manifest;\n"
"use pgBackRest::Protocol::Helper;\n"
"use pgBackRest::Protocol::Storage::Helper;\n"
"\n\n\n\n"
"sub new\n"
"{\n"
"my $class = shift;\n"
"\n\n"
"my ($strOperation) = logDebugParam(__PACKAGE__ . '->new');\n"
"\n\n"
"my $self = {};\n"
"bless $self, $class;\n"
"\n\n"
"return logDebugReturn\n"
"(\n"
"$strOperation,\n"
"{name => 'self', value => $self}\n"
");\n"
"}\n"
"\n\n\n\n\n\n\n\n"
"sub process\n"
"{\n"
"my $self = shift;\n"
"\n\n"
"my $strOperation = logDebugParam(__PACKAGE__ . '->process');\n"
"\n\n"
"my $iArchiveTimeout = cfgOption(CFGOPT_ARCHIVE_TIMEOUT);\n"
"\n"
"my $iResult = 0;\n"
"my $strResultMessage = undef;\n"
"\n"
"my $strArchiveId = undef;\n"
"my $strArchiveFile = undef;\n"
"my $strWalSegment = undef;\n"
"\n\n"
"my ($oDb) = dbMasterGet();\n"
"\n\n"
"my ($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) = $oDb->info();\n"
"\n\n"
"logLevelSet(undef, OFF);\n"
"\n\n"
"for (my $iRemoteIdx = 1; $iRemoteIdx <= cfgOptionIndexTotal(CFGOPT_PG_HOST); $iRemoteIdx++)\n"
"{\n"
"\n"
"if (cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)) ||\n"
"cfgOptionTest(cfgOptionIdFromIndex(CFGOPT_PG_HOST, $iRemoteIdx)))\n"
"{\n"
"eval\n"
"{\n"
"\n\n"
"my $oBackupManifest = new pgBackRest::Manifest(\"/dev/null/manifest.chk\",\n"
"{bLoad => false, strDbVersion => $strDbVersion, iDbCatalogVersion => $iCatalogVersion,\n"
"strCipherPass => 'x', strCipherPassSub => 'x'});\n"
"\n\n"
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_DB_ID, undef, 1);\n"
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_CONTROL, undef, $iControlVersion);\n"
"$oBackupManifest->numericSet(MANIFEST_SECTION_BACKUP_DB, MANIFEST_KEY_SYSTEM_ID, undef, $ullDbSysId);\n"
"\n"
"$oBackupManifest->build(\n"
"storageDb({iRemoteIdx => $iRemoteIdx}), cfgOption(cfgOptionIdFromIndex(CFGOPT_PG_PATH, $iRemoteIdx)), undef,\n"
"cfgOptionValid(CFGOPT_ONLINE) && cfgOption(CFGOPT_ONLINE), false, $oDb->tablespaceMapGet());\n"
"\n"
"return true;\n"
"}\n"
"or do\n"
"{\n"
"\n"
"$strResultMessage = \"Database: ${strDbVersion} ${ullDbSysId} \" . exceptionMessage($EVAL_ERROR) .\n"
"(($iResult != 0) ? \"\\n[$iResult] : $strResultMessage\" : \"\");\n"
"$iResult = exceptionCode($EVAL_ERROR);\n"
"};\n"
"}\n"
"}\n"
"\n\n"
"logLevelSet(undef, cfgOption(CFGOPT_LOG_LEVEL_CONSOLE));\n"
"\n\n"
"if ($iResult == 0)\n"
"{\n"
"\n\n"
"($oDb) = dbObjectGet();\n"
"\n\n"
"$oDb->configValidate();\n"
"\n\n"
"logLevelSet(undef, OFF);\n"
"\n\n"
"eval\n"
"{\n"
"\n"
"$self->backupInfoCheck();\n"
"return true;\n"
"}\n"
"\n"
"or do\n"
"{\n"
"\n"
"$iResult = exceptionCode($EVAL_ERROR);\n"
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
"};\n"
"\n\n"
"if ($iResult == 0)\n"
"{\n"
"eval\n"
"{\n"
"\n"
"($strArchiveId) = archiveGetCheck();\n"
"return true;\n"
"}\n"
"or do\n"
"{\n"
"\n"
"$iResult = exceptionCode($EVAL_ERROR);\n"
"$strResultMessage = exceptionMessage($EVAL_ERROR);\n"
"};\n"
"}\n"
"\n\n"
"logLevelSet(undef, cfgOption(CFGOPT_LOG_LEVEL_CONSOLE));\n"
"}\n"
"\n\n"
"if ($iResult != 0)\n"
"{\n"
"&log(ERROR, $strResultMessage, $iResult);\n"
"}\n"
"\n\n"
"return logDebugReturn\n"
"(\n"
"$strOperation,\n"
"{name => 'iResult', value => $iResult, trace => true}\n"
");\n"
"}\n"
"\n\n\n\n\n\n"
"sub backupInfoCheck\n"
"{\n"
"my $self = shift;\n"
"\n\n"
"my\n"
"(\n"
"$strOperation,\n"
"$strDbVersion,\n"
"$iControlVersion,\n"
"$iCatalogVersion,\n"
"$ullDbSysId,\n"
") =\n"
"logDebugParam\n"
"(\n"
"__PACKAGE__ . '->backupInfoCheck', \\@_,\n"
"{name => 'strDbVersion', required => false},\n"
"{name => 'iControlVersion', required => false},\n"
"{name => 'iCatalogVersion', required => false},\n"
"{name => 'ullDbSysId', required => false}\n"
");\n"
"\n\n"
"my $iDbHistoryId;\n"
"\n"
"if (!defined($strDbVersion) || !defined($iControlVersion) || !defined($iCatalogVersion) || !defined($ullDbSysId))\n"
"{\n"
"\n"
"($strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId) = dbMasterGet()->info();\n"
"}\n"
"\n"
"if (!isRepoLocal())\n"
"{\n"
"$iDbHistoryId = protocolGet(CFGOPTVAL_REMOTE_TYPE_BACKUP)->cmdExecute(\n"
"OP_CHECK_BACKUP_INFO_CHECK, [$strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId]);\n"
"}\n"
"else\n"
"{\n"
"$iDbHistoryId = (new pgBackRest::Backup::Info(storageRepo()->pathGet(STORAGE_REPO_BACKUP)))->check(\n"
"$strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId);\n"
"}\n"
"\n\n"
"return logDebugReturn\n"
"(\n"
"$strOperation,\n"
"{name => 'iDbHistoryId', value => $iDbHistoryId, trace => true}\n"
");\n"
"}\n"
"\n"
"1;\n"
},
{
.name = "pgBackRest/Common/Cipher.pm",
.data =
@ -8279,15 +8081,6 @@ static const EmbeddedModule embeddedModule[] =
"$oRemote->process(\n"
"cfgOption(CFGOPT_LOCK_PATH), cfgOption(CFGOPT_COMMAND), cfgOption(CFGOPT_STANZA, false), cfgOption(CFGOPT_PROCESS));\n"
"}\n"
"\n\n\n"
"elsif (cfgCommandTest(CFGCMD_CHECK))\n"
"{\n"
"\n"
"require pgBackRest::Check::Check;\n"
"pgBackRest::Check::Check->import();\n"
"\n"
"$iResult = new pgBackRest::Check::Check()->process();\n"
"}\n"
"else\n"
"{\n"
"\n"
@ -10342,9 +10135,6 @@ static const EmbeddedModule embeddedModule[] =
"use constant OP_ARCHIVE_GET_CHECK => 'archiveCheck';\n"
"push @EXPORT, qw(OP_ARCHIVE_GET_CHECK);\n"
"\n\n"
"use constant OP_CHECK_BACKUP_INFO_CHECK => 'backupInfoCheck';\n"
"push @EXPORT, qw(OP_CHECK_BACKUP_INFO_CHECK);\n"
"\n\n"
"use constant OP_DB_CONNECT => 'dbConnect';\n"
"push @EXPORT, qw(OP_DB_CONNECT);\n"
"use constant OP_DB_EXECUTE_SQL => 'dbExecSql';\n"
@ -11439,7 +11229,6 @@ static const EmbeddedModule embeddedModule[] =
"use pgBackRest::Common::Io::Buffered;\n"
"use pgBackRest::Common::Wait;\n"
"use pgBackRest::Archive::Get::File;\n"
"use pgBackRest::Check::Check;\n"
"use pgBackRest::Config::Config;\n"
"use pgBackRest::Db;\n"
"use pgBackRest::Protocol::Command::Minion;\n"
@ -11481,7 +11270,6 @@ static const EmbeddedModule embeddedModule[] =
"\n\n"
"my $oStorage = cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_DB) ? storageDb() : storageRepo();\n"
"\n"
"my $oCheck = cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_BACKUP) ? new pgBackRest::Check::Check() : undef;\n"
"my $oDb = cfgOptionTest(CFGOPT_TYPE, CFGOPTVAL_REMOTE_TYPE_DB) ? new pgBackRest::Db() : undef;\n"
"\n\n"
"my $hCommandMap =\n"
@ -11489,8 +11277,6 @@ static const EmbeddedModule embeddedModule[] =
"\n"
"&OP_ARCHIVE_GET_CHECK => sub {archiveGetCheck(@{shift()})},\n"
"\n\n"
"&OP_CHECK_BACKUP_INFO_CHECK => sub {$oCheck->backupInfoCheck(@{shift()})},\n"
"\n\n"
"&OP_DB_CONNECT => sub {$oDb->connect()},\n"
"&OP_DB_EXECUTE_SQL => sub {$oDb->executeSql(@{shift()})},\n"
"&OP_DB_INFO => sub {$oDb->info(@{shift()})},\n"

View File

@ -640,7 +640,8 @@ unit:
# ----------------------------------------------------------------------------------------------------------------------------
- name: check
total: 2
total: 4
containerReq: true
coverage:
command/check/common: full

View File

@ -111,7 +111,7 @@ stanza-create db - fail on database mismatch and warn force option deprecated (d
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --compress-level=3 --config=[TEST_PATH]/db-master/pgbackrest.conf --db-timeout=45 --force --lock-path=[TEST_PATH]/db-master/lock --log-level-console=detail --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/db-master/log --log-subprocess --no-log-timestamp --no-online --pg1-path=[TEST_PATH]/db-master/db/base --protocol-timeout=60 --repo1-path=[TEST_PATH]/db-master/repo --stanza=db
P00 WARN: option --force is no longer supported
P00 ERROR: [028]: backup and archive info files already exist but do not match the database
P00 ERROR: [028]: backup and archive info files exist but do not match the database
HINT: is this the correct stanza?
HINT: did an error occur during stanza-upgrade?
P00 INFO: stanza-create command end: aborted with exception [028]

View File

@ -129,7 +129,7 @@ stanza-create db - fail on database mismatch and warn force option deprecated (b
------------------------------------------------------------------------------------------------------------------------------------
P00 INFO: stanza-create command begin [BACKREST-VERSION]: --compress-level=3 --compress-level-network=1 --config=[TEST_PATH]/backup/pgbackrest.conf --db-timeout=45 --force --lock-path=[TEST_PATH]/backup/lock --log-level-console=detail --log-level-file=trace --log-level-stderr=off --log-path=[TEST_PATH]/backup/log --log-subprocess --no-log-timestamp --no-online --pg1-host=db-master --pg1-host-cmd=[BACKREST-BIN] --pg1-host-config=[TEST_PATH]/db-master/pgbackrest.conf --pg1-host-user=[USER-1] --pg1-path=[TEST_PATH]/db-master/db/base --protocol-timeout=60 --repo1-cipher-pass=<redacted> --repo1-cipher-type=aes-256-cbc --repo1-path=/ --repo1-s3-bucket=pgbackrest-dev --repo1-s3-endpoint=s3.amazonaws.com --repo1-s3-key=<redacted> --repo1-s3-key-secret=<redacted> --repo1-s3-region=us-east-1 --no-repo1-s3-verify-tls --repo1-type=s3 --stanza=db
P00 WARN: option --force is no longer supported
P00 ERROR: [028]: backup and archive info files already exist but do not match the database
P00 ERROR: [028]: backup and archive info files exist but do not match the database
HINT: is this the correct stanza?
HINT: did an error occur during stanza-upgrade?
P00 DETAIL: tls statistics:[TLS-STATISTICS]

View File

@ -45,10 +45,6 @@ check db - fail on backup info mismatch (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
check db - confirm master manifest->build executed (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
check db - verify success after backup (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -153,10 +153,6 @@ repo1-host-config=[TEST_PATH]/db-master/pgbackrest.conf
repo1-host-user=[USER-1]
spool-path=[TEST_PATH]/db-standby/spool
check db - confirm standby manifest->build executed (db-standby host)
> [CONTAINER-EXEC] db-standby [BACKREST-BIN] --config=[TEST_PATH]/db-standby/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
check db - verify check command on standby (db-standby host)
> [CONTAINER-EXEC] db-standby [BACKREST-BIN] --config=[TEST_PATH]/db-standby/pgbackrest.conf --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -157,10 +157,6 @@ spool-path=[TEST_PATH]/db-standby/spool
archive-copy=y
start-fast=y
check db - confirm standby manifest->build executed (db-standby host)
> [CONTAINER-EXEC] db-standby [BACKREST-BIN] --config=[TEST_PATH]/db-standby/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
incr backup - update during backup (db-standby host)
> [CONTAINER-EXEC] db-standby [BACKREST-BIN] --config=[TEST_PATH]/db-standby/pgbackrest.conf --stop-auto --buffer-size=32768 --delta --stanza=db backup --test --test-delay=1 --test-point=manifest-build=y
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -69,10 +69,6 @@ check db - fail on backup info mismatch (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
check db - confirm master manifest->build executed (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
check db - verify success after backup (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -215,10 +215,6 @@ repo1-path=[TEST_PATH]/backup/repo
archive-copy=y
start-fast=y
check db - confirm standby manifest->build executed (db-standby host)
> [CONTAINER-EXEC] db-standby [BACKREST-BIN] --config=[TEST_PATH]/db-standby/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
check db - verify check command on standby (db-standby host)
> [CONTAINER-EXEC] db-standby [BACKREST-BIN] --config=[TEST_PATH]/db-standby/pgbackrest.conf --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -69,10 +69,6 @@ check db - fail on backup info mismatch (backup host)
> [CONTAINER-EXEC] backup [BACKREST-BIN] --config=[TEST_PATH]/backup/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
check db - confirm master manifest->build executed (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------
check db - verify success after backup (db-master host)
> [CONTAINER-EXEC] db-master [BACKREST-BIN] --config=[TEST_PATH]/db-master/pgbackrest.conf --archive-timeout=5 --stanza=db check
------------------------------------------------------------------------------------------------------------------------------------

View File

@ -228,14 +228,15 @@ sub run
# load the archive info file and munge it for testing by breaking the database version
$oHostBackup->infoMunge(
storageRepo()->pathGet(STORAGE_REPO_ARCHIVE . qw{/} . ARCHIVE_INFO_FILE),
{&INFO_ARCHIVE_SECTION_DB => {&INFO_ARCHIVE_KEY_DB_VERSION => '8.0'}});
{&INFO_ARCHIVE_SECTION_DB => {&INFO_ARCHIVE_KEY_DB_VERSION => '8.0'},
&INFO_ARCHIVE_SECTION_DB_HISTORY => {1 => {&INFO_ARCHIVE_KEY_DB_VERSION => '8.0'}}});
$oHostDbMaster->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH});
$oHostDbMaster->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_FILE_INVALID});
# If running the remote tests then also need to run check locally
if ($bHostBackup)
{
$oHostBackup->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_ARCHIVE_MISMATCH});
$oHostBackup->check($strComment, {iTimeout => 0.1, iExpectedExitStatus => ERROR_FILE_INVALID});
}
# Restore the file to its original condition
@ -264,29 +265,32 @@ sub run
$oHostBackup->infoMunge(
storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO),
{&INFO_BACKUP_SECTION_DB =>
{&INFO_BACKUP_KEY_DB_VERSION => '8.0', &INFO_BACKUP_KEY_SYSTEM_ID => 6999999999999999999}});
{&INFO_BACKUP_KEY_DB_VERSION => '8.0', &INFO_BACKUP_KEY_SYSTEM_ID => 6999999999999999999},
&INFO_BACKUP_SECTION_DB_HISTORY =>
{1 => {&INFO_BACKUP_KEY_DB_VERSION => '8.0', &INFO_BACKUP_KEY_SYSTEM_ID => 6999999999999999999}}});
# Run the test
$oHostDbMaster->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_BACKUP_MISMATCH});
$oHostDbMaster->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_FILE_INVALID});
# If running the remote tests then also need to run check locally
if ($bHostBackup)
{
$oHostBackup->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_BACKUP_MISMATCH});
$oHostBackup->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_FILE_INVALID});
}
# Restore the file to its original condition
$oHostBackup->infoRestore(storageRepo()->pathGet(STORAGE_REPO_BACKUP . qw{/} . FILE_BACKUP_INFO));
# ??? Removed temporarily until manifest build can be brought back into the check command
# Create a directory in pg_data location that is only readable by root to ensure manifest->build is called by check
my $strDir = $oHostDbMaster->dbBasePath() . '/rootreaddir';
executeTest('sudo mkdir ' . $strDir);
executeTest("sudo chown root:root ${strDir}");
executeTest("sudo chmod 400 ${strDir}");
$strComment = 'confirm master manifest->build executed';
$oHostDbMaster->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_PATH_OPEN});
executeTest("sudo rmdir ${strDir}");
# my $strDir = $oHostDbMaster->dbBasePath() . '/rootreaddir';
# executeTest('sudo mkdir ' . $strDir);
# executeTest("sudo chown root:root ${strDir}");
# executeTest("sudo chmod 400 ${strDir}");
#
# $strComment = 'confirm master manifest->build executed';
# $oHostDbMaster->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_PATH_OPEN});
# executeTest("sudo rmdir ${strDir}");
# Providing a sufficient archive-timeout, verify that the check command runs successfully now with valid
# archive.info and backup.info files
@ -491,27 +495,28 @@ sub run
$strFullBackup = $strStandbyBackup;
}
# Create a directory in pg_data location that is only readable by root to ensure manifest->build is called by check
my $strDir = $oHostDbStandby->dbBasePath() . '/rootreaddir';
executeTest('sudo mkdir ' . $strDir);
executeTest("sudo chown root:root ${strDir}");
executeTest("sudo chmod 400 ${strDir}");
my $strComment = 'confirm standby manifest->build executed';
# If there is an invalid host, the final error returned from check will be the inability to resolve the name which is
# an open error instead of a read error
if (!$oHostDbStandby->bogusHost())
{
$oHostDbStandby->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_PATH_OPEN});
}
else
{
$oHostDbStandby->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_FILE_READ});
}
# Remove the directory in pg_data location that is only readable by root
executeTest("sudo rmdir ${strDir}");
# ??? Removed temporarily until manifest build can be brought back into the check command
# # Create a directory in pg_data location that is only readable by root to ensure manifest->build is called by check
# my $strDir = $oHostDbStandby->dbBasePath() . '/rootreaddir';
# executeTest('sudo mkdir ' . $strDir);
# executeTest("sudo chown root:root ${strDir}");
# executeTest("sudo chmod 400 ${strDir}");
#
# my $strComment = 'confirm standby manifest->build executed';
#
# # If there is an invalid host, the final error returned from check will be the inability to resolve the name which is
# # an open error instead of a read error
# if (!$oHostDbStandby->bogusHost())
# {
# $oHostDbStandby->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_PATH_OPEN});
# }
# else
# {
# $oHostDbStandby->check($strComment, {iTimeout => 5, iExpectedExitStatus => ERROR_FILE_READ});
# }
#
# # Remove the directory in pg_data location that is only readable by root
# executeTest("sudo rmdir ${strDir}");
# Confirm the check command runs without error on a standby (when a bogus host is not configured)
if (!$oHostDbStandby->bogusHost())

View File

@ -54,21 +54,29 @@ Macros for defining groups of functions that implement various queries and comma
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
#define HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, versionParam, pgPathParam) \
#define HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, versionParam, pgPathParam, archiveMode, archiveCommand) \
{.session = sessionParam, .function = HRNPQ_SENDQUERY, .param = \
"[\"select (select setting from pg_catalog.pg_settings where name = 'server_version_num')::int4," \
" (select setting from pg_catalog.pg_settings where name = 'data_directory')::text\"]", \
" (select setting from pg_catalog.pg_settings where name = 'data_directory')::text," \
" (select setting from pg_catalog.pg_settings where name = 'archive_mode')::text," \
" (select setting from pg_catalog.pg_settings where name = 'archive_command')::text\"]", \
.resultInt = 1}, \
{.session = sessionParam, .function = HRNPQ_CONSUMEINPUT}, \
{.session = sessionParam, .function = HRNPQ_ISBUSY}, \
{.session = sessionParam, .function = HRNPQ_GETRESULT}, \
{.session = sessionParam, .function = HRNPQ_RESULTSTATUS, .resultInt = PGRES_TUPLES_OK}, \
{.session = sessionParam, .function = HRNPQ_NTUPLES, .resultInt = 1}, \
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 2}, \
{.session = sessionParam, .function = HRNPQ_NFIELDS, .resultInt = 4}, \
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[0]", .resultInt = HRNPQ_TYPE_INT}, \
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[1]", .resultInt = HRNPQ_TYPE_TEXT}, \
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[2]", .resultInt = HRNPQ_TYPE_TEXT}, \
{.session = sessionParam, .function = HRNPQ_FTYPE, .param = "[3]", .resultInt = HRNPQ_TYPE_TEXT}, \
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,0]", .resultZ = STRINGIFY(versionParam)}, \
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,1]", .resultZ = pgPathParam}, \
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,2]", .resultZ = archiveMode == NULL ? "on" \
: archiveMode}, \
{.session = sessionParam, .function = HRNPQ_GETVALUE, .param = "[0,3]", .resultZ = archiveCommand == NULL ? PROJECT_BIN \
: archiveCommand}, \
{.session = sessionParam, .function = HRNPQ_CLEAR}, \
{.session = sessionParam, .function = HRNPQ_GETRESULT, .resultNull = true}
@ -135,15 +143,15 @@ Macros for defining groups of functions that implement various queries and comma
/***********************************************************************************************************************************
Macros to simplify dbOpen() for specific database versions
***********************************************************************************************************************************/
#define HRNPQ_MACRO_OPEN_84(sessionParam, connectParam, pgPathParam) \
#define HRNPQ_MACRO_OPEN_84(sessionParam, connectParam, pgPathParam, archiveMode, archiveCommand) \
HRNPQ_MACRO_OPEN(sessionParam, connectParam), \
HRNPQ_MACRO_SET_SEARCH_PATH(sessionParam), \
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, PG_VERSION_84, pgPathParam)
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, PG_VERSION_84, pgPathParam, archiveMode, archiveCommand)
#define HRNPQ_MACRO_OPEN_92(sessionParam, connectParam, pgPathParam, standbyParam) \
#define HRNPQ_MACRO_OPEN_92(sessionParam, connectParam, pgPathParam, standbyParam, archiveMode, archiveCommand) \
HRNPQ_MACRO_OPEN(sessionParam, connectParam), \
HRNPQ_MACRO_SET_SEARCH_PATH(sessionParam), \
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, PG_VERSION_92, pgPathParam), \
HRNPQ_MACRO_VALIDATE_QUERY(sessionParam, PG_VERSION_92, pgPathParam, archiveMode, archiveCommand), \
HRNPQ_MACRO_SET_APPLICATION_NAME(sessionParam), \
HRNPQ_MACRO_IS_STANDBY_QUERY(sessionParam, standbyParam)

View File

@ -3,11 +3,15 @@ Test Check Command
***********************************************************************************************************************************/
#include "postgres/version.h"
#include "storage/helper.h"
#include "storage/posix/storage.h"
#include "storage/storage.intern.h"
#include "command/stanza/create.h"
#include "common/harnessConfig.h"
#include "common/harnessInfo.h"
#include "common/harnessPq.h"
#include "info/infoArchive.h"
#include "info/infoBackup.h"
/***********************************************************************************************************************************
Test Run
@ -17,19 +21,173 @@ testRun(void)
{
FUNCTION_HARNESS_VOID();
String *pg1Path = strNewFmt("%s/pg1", testPath());
Storage *storageTest = storagePosixNew(
strNew(testPath()), STORAGE_MODE_FILE_DEFAULT, STORAGE_MODE_PATH_DEFAULT, true, NULL);
String *pg1 = strNew("pg1");
String *pg1Path = strNewFmt("%s/%s", testPath(), strPtr(pg1));
String *pg1PathOpt = strNewFmt("--pg1-path=%s", strPtr(pg1Path));
String *pg8 = strNew("pg8");
String *pg8Path = strNewFmt("%s/%s", testPath(), strPtr(pg8));
String *pg8PathOpt = strNewFmt("--pg8-path=%s", strPtr(pg8Path));
String *stanza = strNew("test1");
String *stanzaOpt = strNewFmt("--stanza=%s", strPtr(stanza));
StringList *argList = strLstNew();
// *****************************************************************************************************************************
if (testBegin("cmdCheck()"))
{
StringList *argList = strLstNew();
strLstAddZ(argList, "--stanza=test1");
// Load Parameters
argList = strLstNew();
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
strLstAddZ(argList, "--archive-timeout=.5");
harnessCfgLoad(cfgCmdCheck, argList);
// Set up harness to expect a failure to connect to the database
harnessPqScriptSet((HarnessPq [])
{
{.function = HRNPQ_CONNECTDB, .param = "[\"dbname='postgres' port=5432\"]"},
{.function = HRNPQ_STATUS, .resultInt = CONNECTION_BAD},
{.function = HRNPQ_ERRORMESSAGE, .resultZ = "error"},
{.function = HRNPQ_FINISH},
{.function = NULL}
});
TEST_ERROR_FMT(cmdCheck(), ConfigError, "no database found\nHINT: check indexed pg-path/pg-host configurations");
harnessLogResult(
"P00 WARN: unable to check pg-1: [DbConnectError] unable to connect to 'dbname='postgres' port=5432': error");
// Standby only, repo local
// -------------------------------------------------------------------------------------------------------------------------
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), true, NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
TEST_ERROR(cmdCheck(), ConfigError, "primary database not found\nHINT: check indexed pg-path/pg-host configurations");
// Standby only, repo remote but more than one pg-path configured
// -------------------------------------------------------------------------------------------------------------------------
argList = strLstNew();
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAddZ(argList, "--pg8-path=/path/to/standby2");
strLstAddZ(argList, "--pg8-port=5433");
strLstAddZ(argList, "--repo1-host=repo.domain.com");
strLstAddZ(argList, "--archive-timeout=.5");
harnessCfgLoad(cfgCmdCheck, argList);
// Two standbys found but no primary
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true, NULL, NULL),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", "/pgdata", true, NULL, NULL),
HRNPQ_MACRO_CLOSE(8),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
TEST_ERROR(cmdCheck(), ConfigError, "primary database not found\nHINT: check indexed pg-path/pg-host configurations");
// Standby only, repo remote but only one pg-path configured
// -------------------------------------------------------------------------------------------------------------------------
argList = strLstNew();
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAddZ(argList, "--repo1-host=repo.domain.com");
strLstAddZ(argList, "--archive-timeout=.5");
harnessCfgLoad(cfgCmdCheck, argList);
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true, NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
// Only confirming we get passed the check for isRepoLocal || more than one pg-path configured
TEST_ERROR_FMT(
cmdCheck(), FileMissingError, "unable to open missing file '%s/global/pg_control' for read", strPtr(pg1Path));
// backup-standby set without standby
// -------------------------------------------------------------------------------------------------------------------------
argList = strLstNew();
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
strLstAddZ(argList, "--archive-timeout=.5");
strLstAddZ(argList, "--backup-standby");
harnessCfgLoad(cfgCmdCheck, argList);
// Primary database connection ok
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false, NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
TEST_ERROR_FMT(
cmdCheck(), FileMissingError, "unable to open missing file '%s' for read",
strPtr(strNewFmt("%s/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL, strPtr(pg1Path))));
harnessLogResult(
strPtr(strNewFmt("P00 WARN: option '%s' is enabled but standby is not properly configured",
cfgOptionName(cfgOptBackupStandby))));
// Standby and primary database
// -------------------------------------------------------------------------------------------------------------------------
// Create pg_control for standby
storagePutNP(
storageNewWriteNP(storageTest, strNewFmt("%s/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL, strPtr(pg1))),
pgControlTestToBuffer((PgControl){.version = PG_VERSION_92, .systemId = 6569239123849665679}));
argList = strLstNew();
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
strLstAddZ(argList, "--archive-timeout=.5");
strLstAdd(argList, pg8PathOpt);
strLstAddZ(argList, "--pg8-port=5433");
harnessCfgLoad(cfgCmdCheck, argList);
// Standby database path doesn't match pg_control
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", testPath(), true, NULL, NULL),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", strPtr(pg8Path), false, NULL, NULL),
HRNPQ_MACRO_CLOSE(8),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
TEST_ERROR_FMT(
cmdCheck(), DbMismatchError, "version '%s' and path '%s' queried from cluster do not match version '%s' and '%s'"
" read from '%s/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL "'\n"
"HINT: the pg1-path and pg1-port settings likely reference different clusters.",
strPtr(pgVersionToStr(PG_VERSION_92)), testPath(), strPtr(pgVersionToStr(PG_VERSION_92)), strPtr(pg1Path),
strPtr(pg1Path));
// Standby - Stanza has not yet been created
// -------------------------------------------------------------------------------------------------------------------------
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), true, NULL, NULL),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", strPtr(pg8Path), false, NULL, NULL),
HRNPQ_MACRO_CLOSE(8),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
TEST_ERROR_FMT(
cmdCheck(), FileMissingError,
"unable to load info file '%s/repo/archive/test1/archive.info' or '%s/repo/archive/test1/archive.info.copy':\n"
@ -42,7 +200,14 @@ testRun(void)
testPath(), testPath(), strPtr(strNewFmt("%s/repo/archive/test1/archive.info", testPath())),
strPtr(strNewFmt("%s/repo/archive/test1/archive.info.copy", testPath())));
// Create archive.info file
// Standby - Stanza created
// -------------------------------------------------------------------------------------------------------------------------
// Create pg_control for primary
storagePutNP(
storageNewWriteNP(storageTest, strNewFmt("%s/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL, strPtr(pg8))),
pgControlTestToBuffer((PgControl){.version = PG_VERSION_92, .systemId = 6569239123849665679}));
// Create info files
storagePutNP(
storageNewWriteNP(storageRepoWrite(), INFO_ARCHIVE_PATH_FILE_STR),
harnessInfoChecksum(
@ -55,12 +220,49 @@ testRun(void)
"[db:history]\n"
"1={\"db-id\":6569239123849665679,\"db-version\":\"9.2\"}\n")));
storagePutNP(
storageNewWriteNP(storageRepoWrite(), INFO_BACKUP_PATH_FILE_STR),
harnessInfoChecksum(
strNew(
"[db]\n"
"db-catalog-version=201608131\n"
"db-control-version=920\n"
"db-id=1\n"
"db-system-id=6569239123849665679\n"
"db-version=\"9.2\"\n"
"\n"
"[db:history]\n"
"1={\"db-catalog-version\":201608131,\"db-control-version\":920,\"db-system-id\":6569239123849665679,"
"\"db-version\":\"9.2\"}\n")));
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), true, NULL, NULL),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", strPtr(pg8Path), false, "off", NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_CLOSE(8),
HRNPQ_MACRO_DONE()
});
// Error on primary but standby check ok
TEST_ERROR_FMT(cmdCheck(), ArchiveDisabledError, "archive_mode must be enabled");
harnessLogResult("P00 INFO: switch wal not performed because this is a standby");
// Single primary
// -------------------------------------------------------------------------------------------------------------------------
argList = strLstNew();
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
strLstAddZ(argList, "--archive-timeout=.5");
harnessCfgLoad(cfgCmdCheck, argList);
// Error when WAL segment not found
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false, NULL, NULL),
HRNPQ_MACRO_CREATE_RESTORE_POINT(1, "1/1"),
HRNPQ_MACRO_WAL_SWITCH(1, "xlog", "000000010000000100000001"),
HRNPQ_MACRO_CLOSE(1),
@ -81,7 +283,7 @@ testRun(void)
// WAL segment is found
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false, NULL, NULL),
HRNPQ_MACRO_CREATE_RESTORE_POINT(1, "1/1"),
HRNPQ_MACRO_WAL_SWITCH(1, "xlog", "000000010000000100000001"),
HRNPQ_MACRO_CLOSE(1),
@ -94,7 +296,7 @@ testRun(void)
strNew(STORAGE_REPO_ARCHIVE "/9.2-1/000000010000000100000001-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")),
buffer);
TEST_RESULT_VOID(cmdCheck(), "check");
TEST_RESULT_VOID(cmdCheck(), "check primary, WAL archived");
harnessLogResult(
strPtr(
strNewFmt(
@ -102,55 +304,228 @@ testRun(void)
"0000000100000001/000000010000000100000001-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'",
testPath())));
// Single standby
// Primary == NULL (for test coverage)
// -------------------------------------------------------------------------------------------------------------------------
DbGetResult dbGroup = {0};
TEST_RESULT_VOID(checkPrimary(dbGroup), "primary == NULL");
}
// *****************************************************************************************************************************
if (testBegin("checkManifest()"))
{
argList = strLstNew();
strLstAddZ(argList, "--stanza=test1");
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
strLstAddZ(argList, "--archive-timeout=.5");
strLstAddZ(argList, "--pg5-host=localhost");
strLstAddZ(argList, "--pg5-path=/path/to/pg5");
strLstAdd(argList, strNewFmt("--pg5-host-user=%s", testUser()));
harnessCfgLoad(cfgCmdCheck, argList);
// Set script
// Placeholder test for manifest
TEST_ERROR(
checkManifest(), UnknownError,
"remote-0 process on 'localhost' terminated unexpectedly [127]: bash: pgbackrest: command not found");
}
// *****************************************************************************************************************************
if (testBegin("checkDbConfig(), checkArchiveCommand()"))
{
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR_FMT(
checkArchiveCommand(NULL), ArchiveCommandInvalidError, "archive_command '[null]' must contain " PROJECT_BIN);
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR_FMT(
checkArchiveCommand(strNew("")), ArchiveCommandInvalidError, "archive_command '' must contain " PROJECT_BIN);
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR_FMT(
checkArchiveCommand(strNew("backrest")), ArchiveCommandInvalidError, "archive_command 'backrest' must contain "
PROJECT_BIN);
// -------------------------------------------------------------------------------------------------------------------------
TEST_RESULT_BOOL(checkArchiveCommand(strNew("pgbackrest --stanza=demo archive-push %p")), true, "archive_command valid");
// -------------------------------------------------------------------------------------------------------------------------
argList = strLstNew();
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAdd(argList, pg8PathOpt);
strLstAddZ(argList, "--pg8-port=5433");
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
harnessCfgLoad(cfgCmdCheck, argList);
DbGetResult db = {0};
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), true),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false, NULL, NULL),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", "/badpath", true, NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_CLOSE(8),
HRNPQ_MACRO_DONE()
});
TEST_ASSIGN(db, dbGet(false, false), "get primary and standby");
TEST_RESULT_VOID(checkDbConfig(PG_VERSION_92, db.primaryId, db.primary, false), "valid db config");
// Version mismatch
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR_FMT(
checkDbConfig(PG_VERSION_94, db.primaryId, db.primary, false), DbMismatchError,
"version '%s' and path '%s' queried from cluster do not match version '%s' and '%s' read from '%s/"
PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL "'\n"
"HINT: the pg1-path and pg1-port settings likely reference different clusters.",
strPtr(pgVersionToStr(PG_VERSION_92)), strPtr(pg1Path), strPtr(pgVersionToStr(PG_VERSION_94)), strPtr(pg1Path),
strPtr(pg1Path));
// Path mismatch
// -------------------------------------------------------------------------------------------------------------------------
TEST_ERROR_FMT(
checkDbConfig(PG_VERSION_92, db.standbyId, db.standby, true), DbMismatchError,
"version '%s' and path '%s' queried from cluster do not match version '%s' and '%s' read from '%s/"
PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL "'\n"
"HINT: the pg8-path and pg8-port settings likely reference different clusters.",
strPtr(pgVersionToStr(PG_VERSION_92)), strPtr(dbPgDataPath(db.standby)), strPtr(pgVersionToStr(PG_VERSION_92)),
strPtr(pg8Path), strPtr(pg8Path));
// archive-check=false
// -------------------------------------------------------------------------------------------------------------------------
strLstAddZ(argList, "--no-archive-check");
harnessCfgLoad(cfgCmdCheck, argList);
TEST_RESULT_VOID(checkDbConfig(PG_VERSION_92, db.primaryId, db.primary, false), "valid db config --no-archive-check");
// archive-check not valid for command
// -------------------------------------------------------------------------------------------------------------------------
argList = strLstNew();
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAdd(argList, pg8PathOpt);
strLstAddZ(argList, "--pg8-port=5433");
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
harnessCfgLoad(cfgCmdStanzaCreate, argList);
TEST_RESULT_VOID(
checkDbConfig(PG_VERSION_92, db.primaryId, db.primary, false), "valid db config, archive-check not valid for command");
TEST_RESULT_VOID(dbFree(db.primary), "free primary");
TEST_RESULT_VOID(dbFree(db.standby), "free standby");
// archive_mode=always not supported
// -------------------------------------------------------------------------------------------------------------------------
argList = strLstNew();
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, pg1PathOpt);
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
harnessCfgLoad(cfgCmdCheck, argList);
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false, "always", NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
TEST_RESULT_VOID(cmdCheck(), "check");
harnessLogResult("P00 INFO: switch wal not performed because no primary was found");
TEST_ASSIGN(db, dbGet(true, true), "get primary");
TEST_ERROR_FMT(
checkDbConfig(PG_VERSION_92, db.primaryId, db.primary, false), FeatureNotSupportedError,
"archive_mode=always not supported");
TEST_RESULT_VOID(dbFree(db.primary), "free primary");
}
// *****************************************************************************************************************************
if (testBegin("checkDbConfig()"))
if (testBegin("checkStanzaInfo(), checkStanzaInfoPg()"))
{
StringList *argList = strLstNew();
strLstAddZ(argList, "--stanza=test1");
strLstAdd(argList, pg1PathOpt);
InfoArchive *archiveInfo = infoArchiveNew(PG_VERSION_96, 6569239123849665679, NULL);
InfoPgData archivePg = infoPgData(infoArchivePg(archiveInfo), infoPgDataCurrentId(infoArchivePg(archiveInfo)));
InfoBackup *backupInfo = infoBackupNew(PG_VERSION_96, 6569239123849665679, NULL);
InfoPgData backupPg = infoPgData(infoBackupPg(backupInfo), infoPgDataCurrentId(infoBackupPg(backupInfo)));
TEST_RESULT_VOID(checkStanzaInfo(&archivePg, &backupPg), "stanza info files match");
// Create a corrupted backup file - system id
// -------------------------------------------------------------------------------------------------------------------------
backupInfo = infoBackupNew(PG_VERSION_96, 6569239123849665999, NULL);
backupPg = infoPgData(infoBackupPg(backupInfo), infoPgDataCurrentId(infoBackupPg(backupInfo)));
TEST_ERROR_FMT(
checkStanzaInfo(&archivePg, &backupPg), FileInvalidError, "backup info file and archive info file do not match\n"
"archive: id = 1, version = 9.6, system-id = 6569239123849665679\n"
"backup : id = 1, version = 9.6, system-id = 6569239123849665999\n"
"HINT: this may be a symptom of repository corruption!");
// Create a corrupted backup file - system id and version
// -------------------------------------------------------------------------------------------------------------------------
backupInfo = infoBackupNew(PG_VERSION_95, 6569239123849665999, NULL);
backupPg = infoPgData(infoBackupPg(backupInfo), infoPgDataCurrentId(infoBackupPg(backupInfo)));
TEST_ERROR_FMT(
checkStanzaInfo(&archivePg, &backupPg), FileInvalidError, "backup info file and archive info file do not match\n"
"archive: id = 1, version = 9.6, system-id = 6569239123849665679\n"
"backup : id = 1, version = 9.5, system-id = 6569239123849665999\n"
"HINT: this may be a symptom of repository corruption!");
// Create a corrupted backup file - version
// -------------------------------------------------------------------------------------------------------------------------
backupInfo = infoBackupNew(PG_VERSION_95, 6569239123849665679, NULL);
backupPg = infoPgData(infoBackupPg(backupInfo), infoPgDataCurrentId(infoBackupPg(backupInfo)));
TEST_ERROR_FMT(
checkStanzaInfo(&archivePg, &backupPg), FileInvalidError, "backup info file and archive info file do not match\n"
"archive: id = 1, version = 9.6, system-id = 6569239123849665679\n"
"backup : id = 1, version = 9.5, system-id = 6569239123849665679\n"
"HINT: this may be a symptom of repository corruption!");
// Create a corrupted backup file - db id
// -------------------------------------------------------------------------------------------------------------------------
infoBackupPgSet(backupInfo, PG_VERSION_96, 6569239123849665679);
backupPg = infoPgData(infoBackupPg(backupInfo), infoPgDataCurrentId(infoBackupPg(backupInfo)));
TEST_ERROR_FMT(
checkStanzaInfo(&archivePg, &backupPg), FileInvalidError, "backup info file and archive info file do not match\n"
"archive: id = 1, version = 9.6, system-id = 6569239123849665679\n"
"backup : id = 2, version = 9.6, system-id = 6569239123849665679\n"
"HINT: this may be a symptom of repository corruption!");
// checkStanzaInfoPg
// -------------------------------------------------------------------------------------------------------------------------
argList = strLstNew();
strLstAddZ(argList, "--no-online");
strLstAdd(argList, stanzaOpt);
strLstAdd(argList, strNewFmt("--pg1-path=%s/%s", testPath(), strPtr(stanza)));
strLstAdd(argList, strNewFmt("--repo1-path=%s/repo", testPath()));
harnessCfgLoad(cfgCmdCheck, argList);
strLstAddZ(argList, "--repo1-cipher-type=aes-256-cbc");
setenv("PGBACKREST_REPO1_CIPHER_PASS", "12345678", true);
harnessCfgLoad(cfgCmdStanzaCreate, argList);
TEST_RESULT_VOID(checkDbConfig(PG_VERSION_92, 1, PG_VERSION_92, pg1Path), "valid db config");
// Create pg_control
storagePutNP(
storageNewWriteNP(storageTest, strNewFmt("%s/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL, strPtr(stanza))),
pgControlTestToBuffer((PgControl){.version = PG_VERSION_96, .systemId = 6569239123849665679}));
// -------------------------------------------------------------------------------------------------------------------------
// Create info files
TEST_RESULT_VOID(cmdStanzaCreate(), "stanza create - encryption");
// Version mismatch
TEST_ERROR_FMT(
checkDbConfig(PG_VERSION_92, 1, PG_VERSION_94, pg1Path),
DbMismatchError, "version '%s' and path '%s' queried from cluster do not match version '%s' and '%s'"
" read from '%s/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL "'\n"
"HINT: the pg1-path and pg1-port settings likely reference different clusters.",
strPtr(pgVersionToStr(PG_VERSION_94)), strPtr(pg1Path), strPtr(pgVersionToStr(PG_VERSION_92)), strPtr(pg1Path),
strPtr(pg1Path));
checkStanzaInfoPg(storageRepo(), PG_VERSION_94, 6569239123849665679, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass)), FileInvalidError,
"backup and archive info files exist but do not match the database\n"
"HINT: is this the correct stanza?\n"
"HINT: did an error occur during stanza-upgrade?");
// -------------------------------------------------------------------------------------------------------------------------
// SystemId mismatch
TEST_ERROR_FMT(
checkDbConfig(PG_VERSION_92, 1, PG_VERSION_92, strNew("bogus/path")),
DbMismatchError, "version '%s' and path '%s' queried from cluster do not match version '%s' and '%s'"
" read from '%s/" PG_PATH_GLOBAL "/" PG_FILE_PGCONTROL "'\n"
"HINT: the pg1-path and pg1-port settings likely reference different clusters.",
strPtr(pgVersionToStr(PG_VERSION_92)), "bogus/path", strPtr(pgVersionToStr(PG_VERSION_92)), strPtr(pg1Path),
strPtr(pg1Path));
checkStanzaInfoPg(storageRepo(), PG_VERSION_96, 6569239123849665699, cipherType(cfgOptionStr(cfgOptRepoCipherType)),
cfgOptionStr(cfgOptRepoCipherPass)), FileInvalidError,
"backup and archive info files exist but do not match the database\n"
"HINT: is this the correct stanza?\n"
"HINT: did an error occur during stanza-upgrade?");
}
FUNCTION_HARNESS_RESULT_VOID();

View File

@ -34,7 +34,7 @@ testRun(void)
strLstAdd(argListBase, strNewFmt("--repo1-path=%s/repo", testPath()));
// *****************************************************************************************************************************
if (testBegin("cmdStanzaCreate(), infoValidate()"))
if (testBegin("cmdStanzaCreate(), checkStanzaInfo()"))
{
// Load Parameters
StringList *argList = strLstDup(argListBase);
@ -231,7 +231,7 @@ testRun(void)
cmdStanzaCreate(), FileMissingError, "archive.info exists but backup.info is missing\n"
"HINT: this may be a symptom of repository corruption!");
// infoValidate()
// checkStanzaInfo() - already checked in checkTest so just a sanity check here
//--------------------------------------------------------------------------------------------------------------------------
// Create a corrupted backup file - db id
contentBackup = strNew
@ -250,7 +250,7 @@ testRun(void)
TEST_RESULT_VOID(
storagePutNP(
storageNewWriteNP(storageTest, backupInfoFileName), harnessInfoChecksum(contentBackup)),
"put back info to file - bad db-id");
"put backup info to file - bad db-id");
TEST_ERROR_FMT(
cmdStanzaCreate(), FileInvalidError, "backup info file and archive info file do not match\n"
@ -258,57 +258,12 @@ testRun(void)
"backup : id = 2, version = 9.6, system-id = 6569239123849665679\n"
"HINT: this may be a symptom of repository corruption!");
// Create a corrupted backup file - system id
contentBackup = strNew
(
"[db]\n"
"db-catalog-version=201608131\n"
"db-control-version=960\n"
"db-id=1\n"
"db-system-id=6569239123849665999\n"
"db-version=\"9.6\"\n"
"\n"
"[db:history]\n"
"1={\"db-catalog-version\":201608131,\"db-control-version\":960,\"db-system-id\":6569239123849665999,"
"\"db-version\":\"9.6\"}\n"
);
TEST_RESULT_VOID(
storagePutNP(
storageNewWriteNP(storageTest, backupInfoFileName), harnessInfoChecksum(contentBackup)),
"put back info to file - bad system-id");
//--------------------------------------------------------------------------------------------------------------------------
// Copy files may or may not exist - remove
storageRemoveNP(storageTest, strNewFmt("%s" INFO_COPY_EXT, strPtr(archiveInfoFileName)));
storageRemoveNP(storageTest, strNewFmt("%s" INFO_COPY_EXT, strPtr(backupInfoFileName)));
TEST_ERROR_FMT(
cmdStanzaCreate(), FileInvalidError, "backup info file and archive info file do not match\n"
"archive: id = 1, version = 9.6, system-id = 6569239123849665679\n"
"backup : id = 1, version = 9.6, system-id = 6569239123849665999\n"
"HINT: this may be a symptom of repository corruption!");
// Create a corrupted backup file - system id and version
contentBackup = strNew
(
"[db]\n"
"db-catalog-version=201608131\n"
"db-control-version=960\n"
"db-id=1\n"
"db-system-id=6569239123849665999\n"
"db-version=\"9.5\"\n"
"\n"
"[db:history]\n"
"1={\"db-catalog-version\":201608131,\"db-control-version\":960,\"db-system-id\":6569239123849665999,"
"\"db-version\":\"9.5\"}\n"
);
TEST_RESULT_VOID(
storagePutNP(
storageNewWriteNP(storageTest, backupInfoFileName), harnessInfoChecksum(contentBackup)),
"put back info to file - bad system-id and version");
TEST_ERROR_FMT(
cmdStanzaCreate(), FileInvalidError, "backup info file and archive info file do not match\n"
"archive: id = 1, version = 9.6, system-id = 6569239123849665679\n"
"backup : id = 1, version = 9.5, system-id = 6569239123849665999\n"
"HINT: this may be a symptom of repository corruption!");
// Create a corrupted backup file - version
// Create an archive.info file and backup.info files that match but do not match the current database version
contentBackup = strNew
(
"[db]\n"
@ -325,20 +280,8 @@ testRun(void)
TEST_RESULT_VOID(
storagePutNP(
storageNewWriteNP(storageTest, backupInfoFileName), harnessInfoChecksum(contentBackup)),
"put back info to file - bad version");
"put backup info to file");
TEST_ERROR_FMT(
cmdStanzaCreate(), FileInvalidError, "backup info file and archive info file do not match\n"
"archive: id = 1, version = 9.6, system-id = 6569239123849665679\n"
"backup : id = 1, version = 9.5, system-id = 6569239123849665679\n"
"HINT: this may be a symptom of repository corruption!");
//--------------------------------------------------------------------------------------------------------------------------
// Copy files may or may not exist - remove
storageRemoveNP(storageTest, strNewFmt("%s" INFO_COPY_EXT, strPtr(archiveInfoFileName)));
storageRemoveNP(storageTest, strNewFmt("%s" INFO_COPY_EXT, strPtr(backupInfoFileName)));
// Create an archive.info file that matches the backup.info file but does not match the current database version
contentArchive = strNew
(
"[db]\n"
@ -355,7 +298,7 @@ testRun(void)
"put archive info file");
TEST_ERROR_FMT(
cmdStanzaCreate(), FileInvalidError, "backup and archive info files already exist but do not match the database\n"
cmdStanzaCreate(), FileInvalidError, "backup and archive info files exist but do not match the database\n"
"HINT: is this the correct stanza?\n"
"HINT: did an error occur during stanza-upgrade?");
@ -391,10 +334,10 @@ testRun(void)
TEST_RESULT_VOID(
storagePutNP(
storageNewWriteNP(storageTest, backupInfoFileName), harnessInfoChecksum(contentBackup)),
"put back info to file");
"put backup info to file");
TEST_ERROR_FMT(
cmdStanzaCreate(), FileInvalidError, "backup and archive info files already exist but do not match the database\n"
cmdStanzaCreate(), FileInvalidError, "backup and archive info files exist but do not match the database\n"
"HINT: is this the correct stanza?\n"
"HINT: did an error occur during stanza-upgrade?");
@ -449,7 +392,7 @@ testRun(void)
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false, NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
@ -460,7 +403,7 @@ testRun(void)
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false, NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
@ -477,7 +420,7 @@ testRun(void)
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(pg1Path), false, NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
@ -498,7 +441,7 @@ testRun(void)
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(strNewFmt("%s/pg2", testPath())), false),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", strPtr(strNewFmt("%s/pg2", testPath())), false, NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
@ -532,8 +475,8 @@ testRun(void)
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", testPath(), true),
HRNPQ_MACRO_OPEN_92(2, "dbname='postgres' port=5434", strPtr(pg1Path), false),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", testPath(), true, NULL, NULL),
HRNPQ_MACRO_OPEN_92(2, "dbname='postgres' port=5434", strPtr(pg1Path), false, NULL, NULL),
HRNPQ_MACRO_CLOSE(2),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()

View File

@ -43,12 +43,12 @@ testRun(void)
{
HRNPQ_MACRO_OPEN(1, "dbname='postgres' port=5432"),
HRNPQ_MACRO_SET_SEARCH_PATH(1),
HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_84, "/pgdata"),
HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_84, "/pgdata", NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_OPEN(1, "dbname='postgres' port=5432"),
HRNPQ_MACRO_SET_SEARCH_PATH(1),
HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_84, "/pgdata"),
HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_84, "/pgdata", NULL, NULL),
HRNPQ_MACRO_WAL_SWITCH(1, "xlog", "000000030000000200000003"),
HRNPQ_MACRO_CLOSE(1),
@ -128,7 +128,7 @@ testRun(void)
{
HRNPQ_MACRO_OPEN(1, "dbname='postgres' port=5432"),
HRNPQ_MACRO_SET_SEARCH_PATH(1),
HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_94, "/pgdata"),
HRNPQ_MACRO_VALIDATE_QUERY(1, PG_VERSION_94, "/pgdata", NULL, NULL),
HRNPQ_MACRO_SET_APPLICATION_NAME(1),
HRNPQ_MACRO_IS_STANDBY_QUERY(1, true),
HRNPQ_MACRO_CLOSE(1),
@ -141,7 +141,7 @@ testRun(void)
// -------------------------------------------------------------------------------------------------------------------------
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_84(1, "dbname='postgres' port=5432", "/pgdata"),
HRNPQ_MACRO_OPEN_84(1, "dbname='postgres' port=5432", "/pgdata", NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_DONE()
});
@ -169,8 +169,8 @@ testRun(void)
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_84(1, "dbname='postgres' port=5432", "/pgdata"),
HRNPQ_MACRO_OPEN_84(8, "dbname='postgres' port=5433", "/pgdata"),
HRNPQ_MACRO_OPEN_84(1, "dbname='postgres' port=5432", "/pgdata", NULL, NULL),
HRNPQ_MACRO_OPEN_84(8, "dbname='postgres' port=5433", "/pgdata", NULL, NULL),
HRNPQ_MACRO_CLOSE(1),
HRNPQ_MACRO_CLOSE(8),
@ -184,8 +184,8 @@ testRun(void)
// -------------------------------------------------------------------------------------------------------------------------
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", "/pgdata", true),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true, NULL, NULL),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", "/pgdata", true, NULL, NULL),
HRNPQ_MACRO_CLOSE(8),
HRNPQ_MACRO_CLOSE(1),
@ -199,8 +199,8 @@ testRun(void)
// -------------------------------------------------------------------------------------------------------------------------
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", "/pgdata", true),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true, NULL, NULL),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5433", "/pgdata", true, NULL, NULL),
HRNPQ_MACRO_CLOSE(8),
HRNPQ_MACRO_CLOSE(1),
@ -234,7 +234,7 @@ testRun(void)
harnessPqScriptSet((HarnessPq [])
{
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true),
HRNPQ_MACRO_OPEN_92(1, "dbname='postgres' port=5432", "/pgdata", true, NULL, NULL),
// pg-4 error
{.session = 4, .function = HRNPQ_CONNECTDB, .param = "[\"dbname='postgres' port=5433\"]"},
@ -242,7 +242,7 @@ testRun(void)
{.session = 4, .function = HRNPQ_ERRORMESSAGE, .resultZ = "error"},
{.session = 4, .function = HRNPQ_FINISH},
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5434", "/pgdata", false),
HRNPQ_MACRO_OPEN_92(8, "dbname='postgres' port=5434", "/pgdata", false, NULL, NULL),
HRNPQ_MACRO_CREATE_RESTORE_POINT(8, "2/3"),
HRNPQ_MACRO_WAL_SWITCH(8, "xlog", "000000010000000200000003"),
@ -261,6 +261,8 @@ testRun(void)
TEST_RESULT_INT(result.primaryId, 8, " check primary id");
TEST_RESULT_BOOL(result.primary != NULL, true, " check primary");
TEST_RESULT_STR(strPtr(dbArchiveMode(result.primary)), "on", " dbArchiveMode");
TEST_RESULT_STR(strPtr(dbArchiveCommand(result.primary)), PROJECT_BIN, " dbArchiveCommand");
TEST_RESULT_STR(strPtr(dbWalSwitch(result.primary)), "000000010000000200000003", " wal switch");
TEST_RESULT_INT(result.standbyId, 1, " check standby id");
TEST_RESULT_BOOL(result.standby != NULL, true, " check standby");