2015-06-14 00:25:49 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# ARCHIVE INFO MODULE
|
2016-07-29 20:02:11 +02:00
|
|
|
#
|
|
|
|
# The archive.info file is created when archiving begins. It is located under the stanza directory. The file contains information
|
|
|
|
# regarding the stanza database version, database WAL segment system id and other information to ensure that archiving is being
|
|
|
|
# performed on the proper database.
|
2015-06-14 00:25:49 +02:00
|
|
|
####################################################################################################################################
|
2017-06-21 14:02:21 +02:00
|
|
|
package pgBackRest::Archive::Info;
|
2016-04-14 15:30:54 +02:00
|
|
|
use parent 'pgBackRest::Common::Ini';
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings FATAL => qw(all);
|
|
|
|
use Carp qw(confess);
|
2017-04-03 16:42:55 +02:00
|
|
|
use English '-no_match_vars';
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
use Exporter qw(import);
|
2017-06-09 23:51:41 +02:00
|
|
|
our @EXPORT = qw();
|
2015-06-14 00:25:49 +02:00
|
|
|
use File::Basename qw(dirname basename);
|
|
|
|
|
2017-06-21 14:02:21 +02:00
|
|
|
use pgBackRest::Archive::Common;
|
2016-04-14 15:30:54 +02:00
|
|
|
use pgBackRest::Common::Exception;
|
2017-06-09 23:51:41 +02:00
|
|
|
use pgBackRest::Config::Config;
|
2016-04-14 15:30:54 +02:00
|
|
|
use pgBackRest::Common::Ini;
|
|
|
|
use pgBackRest::Common::Log;
|
2016-12-20 23:52:20 +02:00
|
|
|
use pgBackRest::DbVersion;
|
2017-04-03 16:42:55 +02:00
|
|
|
use pgBackRest::InfoCommon;
|
2016-04-14 15:30:54 +02:00
|
|
|
use pgBackRest::Manifest;
|
2017-06-09 23:51:41 +02:00
|
|
|
use pgBackRest::Protocol::Storage::Helper;
|
|
|
|
use pgBackRest::Storage::Base;
|
|
|
|
use pgBackRest::Storage::Helper;
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# File/path constants
|
|
|
|
####################################################################################################################################
|
|
|
|
use constant ARCHIVE_INFO_FILE => 'archive.info';
|
2017-06-09 23:51:41 +02:00
|
|
|
push @EXPORT, qw(ARCHIVE_INFO_FILE);
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
####################################################################################################################################
|
2016-12-03 17:28:08 +02:00
|
|
|
# Archive info constants
|
2015-06-14 00:25:49 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
use constant INFO_ARCHIVE_SECTION_DB => INFO_BACKUP_SECTION_DB;
|
|
|
|
push @EXPORT, qw(INFO_ARCHIVE_SECTION_DB);
|
|
|
|
use constant INFO_ARCHIVE_SECTION_DB_HISTORY => INFO_BACKUP_SECTION_DB_HISTORY;
|
2017-04-03 16:42:55 +02:00
|
|
|
push @EXPORT, qw(INFO_ARCHIVE_SECTION_DB_HISTORY);
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
use constant INFO_ARCHIVE_KEY_DB_VERSION => MANIFEST_KEY_DB_VERSION;
|
|
|
|
push @EXPORT, qw(INFO_ARCHIVE_KEY_DB_VERSION);
|
2017-04-03 16:42:55 +02:00
|
|
|
use constant INFO_ARCHIVE_KEY_DB_ID => MANIFEST_KEY_DB_ID;
|
2015-06-14 00:25:49 +02:00
|
|
|
push @EXPORT, qw(INFO_ARCHIVE_KEY_DB_ID);
|
|
|
|
use constant INFO_ARCHIVE_KEY_DB_SYSTEM_ID => MANIFEST_KEY_SYSTEM_ID;
|
|
|
|
push @EXPORT, qw(INFO_ARCHIVE_KEY_DB_SYSTEM_ID);
|
|
|
|
|
2016-12-20 23:52:20 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# Global variables
|
|
|
|
####################################################################################################################################
|
|
|
|
my $strArchiveInfoMissingMsg =
|
|
|
|
ARCHIVE_INFO_FILE . " does not exist but is required to push/get WAL segments\n" .
|
|
|
|
"HINT: is archive_command configured in postgresql.conf?\n" .
|
|
|
|
"HINT: has a stanza-create been performed?\n" .
|
|
|
|
"HINT: use --no-archive-check to disable archive checks during backup if you have an alternate archiving scheme.";
|
|
|
|
|
2015-06-14 00:25:49 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# CONSTRUCTOR
|
|
|
|
####################################################################################################################################
|
|
|
|
sub new
|
|
|
|
{
|
2015-08-29 20:20:46 +02:00
|
|
|
my $class = shift; # Class name
|
|
|
|
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
2017-06-09 23:51:41 +02:00
|
|
|
$strArchiveClusterPath, # Archive cluster path
|
|
|
|
$bRequired, # Is archive info required?
|
|
|
|
$bLoad, # Should the file attempt to be loaded?
|
|
|
|
$bIgnoreMissing, # Don't error on missing files
|
2017-11-06 19:51:12 +02:00
|
|
|
$strCipherPassSub, # Passphrase to encrypt the subsequent archive files if repo is encrypted
|
2015-08-29 20:20:46 +02:00
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
2016-08-11 23:32:28 +02:00
|
|
|
__PACKAGE__ . '->new', \@_,
|
2015-08-29 20:20:46 +02:00
|
|
|
{name => 'strArchiveClusterPath'},
|
2017-06-09 23:51:41 +02:00
|
|
|
{name => 'bRequired', default => true},
|
|
|
|
{name => 'bLoad', optional => true, default => true},
|
|
|
|
{name => 'bIgnoreMissing', optional => true, default => false},
|
2017-11-06 19:51:12 +02:00
|
|
|
{name => 'strCipherPassSub', optional => true},
|
2015-08-29 20:20:46 +02:00
|
|
|
);
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
# Build the archive info path/file name
|
|
|
|
my $strArchiveInfoFile = "${strArchiveClusterPath}/" . ARCHIVE_INFO_FILE;
|
2017-06-09 23:51:41 +02:00
|
|
|
my $self = {};
|
|
|
|
my $iResult = 0;
|
|
|
|
my $strResultMessage;
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2017-06-09 23:51:41 +02:00
|
|
|
# Init object and store variables
|
|
|
|
eval
|
2015-06-22 15:51:16 +02:00
|
|
|
{
|
2017-06-09 23:51:41 +02:00
|
|
|
$self = $class->SUPER::new($strArchiveInfoFile, {bLoad => $bLoad, bIgnoreMissing => $bIgnoreMissing,
|
2017-11-06 19:51:12 +02:00
|
|
|
oStorage => storageRepo(), strCipherPass => storageRepo()->cipherPassUser(),
|
|
|
|
strCipherPassSub => $strCipherPassSub});
|
2017-06-09 23:51:41 +02:00
|
|
|
return true;
|
2015-06-22 15:51:16 +02:00
|
|
|
}
|
2017-06-09 23:51:41 +02:00
|
|
|
or do
|
|
|
|
{
|
|
|
|
# Capture error information
|
|
|
|
$iResult = exceptionCode($EVAL_ERROR);
|
2017-10-16 16:47:31 +02:00
|
|
|
$strResultMessage = exceptionMessage($EVAL_ERROR);
|
2017-06-09 23:51:41 +02:00
|
|
|
};
|
2015-06-22 15:51:16 +02:00
|
|
|
|
2017-06-09 23:51:41 +02:00
|
|
|
if ($iResult != 0)
|
|
|
|
{
|
|
|
|
# If the file does not exist but is required to exist, then error
|
|
|
|
# The archive info is only allowed not to exist when running a stanza-create on a new install
|
|
|
|
if ($iResult == ERROR_FILE_MISSING)
|
|
|
|
{
|
|
|
|
if ($bRequired)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, $strArchiveInfoMissingMsg, ERROR_FILE_MISSING);
|
|
|
|
}
|
|
|
|
}
|
2018-11-07 02:38:38 +02:00
|
|
|
elsif ($iResult == ERROR_CRYPTO && $strResultMessage =~ "^unable to flush")
|
2017-11-06 19:51:12 +02:00
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable to parse '$strArchiveInfoFile'\nHINT: Is or was the repo encrypted?", $iResult);
|
|
|
|
}
|
2017-06-09 23:51:41 +02:00
|
|
|
else
|
|
|
|
{
|
2017-11-06 19:51:12 +02:00
|
|
|
confess $EVAL_ERROR;
|
2017-06-09 23:51:41 +02:00
|
|
|
}
|
|
|
|
}
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
$self->{strArchiveClusterPath} = $strArchiveClusterPath;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'self', value => $self}
|
|
|
|
);
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# check
|
|
|
|
#
|
2016-07-29 20:02:11 +02:00
|
|
|
# Check archive info file and make sure it is compatible with the current version of the database for the stanza. If the file does
|
2016-12-20 23:52:20 +02:00
|
|
|
# not exist an error will occur.
|
2015-06-14 00:25:49 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
sub check
|
|
|
|
{
|
|
|
|
my $self = shift;
|
2015-08-29 20:20:46 +02:00
|
|
|
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$strDbVersion,
|
2016-12-10 16:06:45 +02:00
|
|
|
$ullDbSysId,
|
2016-12-20 23:52:20 +02:00
|
|
|
$bRequired,
|
2015-08-29 20:20:46 +02:00
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
2016-08-11 23:32:28 +02:00
|
|
|
__PACKAGE__ . '->check', \@_,
|
2015-08-29 20:20:46 +02:00
|
|
|
{name => 'strDbVersion'},
|
2016-12-10 16:06:45 +02:00
|
|
|
{name => 'ullDbSysId'},
|
2016-12-20 23:52:20 +02:00
|
|
|
{name => 'bRequired', default => true},
|
2015-08-29 20:20:46 +02:00
|
|
|
);
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2016-12-20 23:52:20 +02:00
|
|
|
# ??? remove bRequired after stanza-upgrade
|
|
|
|
if ($bRequired)
|
|
|
|
{
|
|
|
|
# Confirm the info file exists with the DB section
|
|
|
|
$self->confirmExists();
|
|
|
|
}
|
|
|
|
|
|
|
|
my $strError = undef;
|
|
|
|
|
|
|
|
if (!$self->test(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION, undef, $strDbVersion))
|
|
|
|
{
|
|
|
|
$strError = "WAL segment version ${strDbVersion} does not match archive version " .
|
|
|
|
$self->get(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$self->test(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_SYSTEM_ID, undef, $ullDbSysId))
|
|
|
|
{
|
|
|
|
$strError = (defined($strError) ? ($strError . "\n") : "") .
|
|
|
|
"WAL segment system-id ${ullDbSysId} does not match archive system-id " .
|
|
|
|
$self->get(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_SYSTEM_ID);
|
|
|
|
}
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2016-12-20 23:52:20 +02:00
|
|
|
if (defined($strError))
|
2015-06-14 00:25:49 +02:00
|
|
|
{
|
2016-12-20 23:52:20 +02:00
|
|
|
confess &log(ERROR, "${strError}\nHINT: are you archiving to the correct stanza?", ERROR_ARCHIVE_MISMATCH);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'strArchiveId', value => $self->archiveId()}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# archiveId
|
|
|
|
#
|
|
|
|
# Get the archive id which is a combination of the DB version and the db-id setting (e.g. 9.4-1)
|
|
|
|
####################################################################################################################################
|
|
|
|
sub archiveId
|
|
|
|
{
|
|
|
|
my $self = shift;
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2016-12-20 23:52:20 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
2017-05-12 21:49:14 +02:00
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$strDbVersion,
|
|
|
|
$ullDbSysId,
|
|
|
|
) = logDebugParam
|
|
|
|
(
|
|
|
|
__PACKAGE__ . '->archiveId', \@_,
|
|
|
|
{name => 'strDbVersion', optional => true},
|
|
|
|
{name => 'ullDbSysId', optional => true},
|
|
|
|
);
|
|
|
|
|
|
|
|
my $strArchiveId = undef;
|
|
|
|
|
|
|
|
# If neither optional version and system-id are passed then set the archive id to the current one
|
|
|
|
if (!defined($strDbVersion) && !defined($ullDbSysId))
|
|
|
|
{
|
|
|
|
$strArchiveId = $self->get(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION) . "-" .
|
|
|
|
$self->get(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_ID);
|
|
|
|
}
|
|
|
|
# If both the optional version and system-id are passed
|
|
|
|
elsif (defined($strDbVersion) && defined($ullDbSysId))
|
|
|
|
{
|
2017-11-17 00:18:51 +02:00
|
|
|
# Get the newest archiveId for the version/system-id passed
|
|
|
|
$strArchiveId = ($self->archiveIdList($strDbVersion, $ullDbSysId))[0];
|
|
|
|
}
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'strArchiveId', value => $strArchiveId}
|
|
|
|
);
|
|
|
|
}
|
2017-05-12 21:49:14 +02:00
|
|
|
|
2017-11-17 00:18:51 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# archiveIdList
|
|
|
|
#
|
|
|
|
# Get a sorted list of the archive ids for the db-version and db-system-id passed.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub archiveIdList
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$strDbVersion,
|
|
|
|
$ullDbSysId,
|
|
|
|
) = logDebugParam
|
|
|
|
(
|
|
|
|
__PACKAGE__ . '->archiveIdList', \@_,
|
|
|
|
{name => 'strDbVersion'},
|
|
|
|
{name => 'ullDbSysId'},
|
|
|
|
);
|
|
|
|
|
|
|
|
my @stryArchiveId;
|
|
|
|
|
|
|
|
# Get the version and system-id for all known databases
|
|
|
|
my $hDbList = $self->dbHistoryList();
|
|
|
|
|
|
|
|
foreach my $iDbHistoryId (sort {$a <=> $b} keys %$hDbList)
|
|
|
|
{
|
|
|
|
# If the version and system-id match then construct the archive id so that the constructed array has the newest match first
|
|
|
|
if (($hDbList->{$iDbHistoryId}{&INFO_DB_VERSION} eq $strDbVersion) &&
|
|
|
|
($hDbList->{$iDbHistoryId}{&INFO_SYSTEM_ID} eq $ullDbSysId))
|
2017-05-12 21:49:14 +02:00
|
|
|
{
|
2017-11-17 00:18:51 +02:00
|
|
|
unshift(@stryArchiveId, $strDbVersion . "-" . $iDbHistoryId);
|
2017-05-12 21:49:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
# If the archive id has still not been found, then error
|
2017-11-17 00:18:51 +02:00
|
|
|
if (@stryArchiveId == 0)
|
2017-05-12 21:49:14 +02:00
|
|
|
{
|
2018-08-24 18:13:10 +02:00
|
|
|
confess &log(
|
|
|
|
ERROR, "unable to retrieve the archive id for database version '$strDbVersion' and system-id '$ullDbSysId'",
|
|
|
|
ERROR_ARCHIVE_MISMATCH);
|
2017-05-12 21:49:14 +02:00
|
|
|
}
|
2016-12-20 23:52:20 +02:00
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
2017-11-17 00:18:51 +02:00
|
|
|
{name => 'stryArchiveId', value => \@stryArchiveId}
|
2016-12-20 23:52:20 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# create
|
|
|
|
#
|
2017-11-06 19:51:12 +02:00
|
|
|
# Creates the archive.info file. WARNING - this function should only be called from stanza-create or tests.
|
2016-12-20 23:52:20 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
sub create
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$strDbVersion,
|
|
|
|
$ullDbSysId,
|
|
|
|
$bSave,
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
__PACKAGE__ . '->create', \@_,
|
|
|
|
{name => 'strDbVersion'},
|
|
|
|
{name => 'ullDbSysId'},
|
|
|
|
{name => 'bSave', default => true},
|
|
|
|
);
|
|
|
|
|
|
|
|
# Fill db section and db history section
|
|
|
|
$self->dbSectionSet($strDbVersion, $ullDbSysId, $self->dbHistoryIdGet(false));
|
|
|
|
|
|
|
|
if ($bSave)
|
|
|
|
{
|
|
|
|
$self->save();
|
|
|
|
}
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn($strOperation);
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# reconstruct
|
|
|
|
#
|
|
|
|
# Reconstruct the info file from the existing directory and files
|
|
|
|
####################################################################################################################################
|
|
|
|
sub reconstruct
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$strCurrentDbVersion,
|
|
|
|
$ullCurrentDbSysId,
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
__PACKAGE__ . '->reconstruct', \@_,
|
|
|
|
{name => 'strCurrentDbVersion'},
|
|
|
|
{name => 'ullCurrentDbSysId'},
|
|
|
|
);
|
|
|
|
|
|
|
|
my $strInvalidFileStructure = undef;
|
|
|
|
|
2017-06-09 23:51:41 +02:00
|
|
|
my @stryArchiveId = storageRepo()->list(
|
2017-04-05 03:17:19 +02:00
|
|
|
$self->{strArchiveClusterPath}, {strExpression => REGEX_ARCHIVE_DIR_DB_VERSION, bIgnoreMissing => true});
|
2017-04-03 16:42:55 +02:00
|
|
|
my %hDbHistoryVersion;
|
|
|
|
|
|
|
|
# Get the db-version and db-id (history id) from the upper level directory names, e.g. 9.4-1
|
|
|
|
foreach my $strArchiveId (@stryArchiveId)
|
|
|
|
{
|
|
|
|
my ($strDbVersion, $iDbHistoryId) = split("-", $strArchiveId);
|
|
|
|
$hDbHistoryVersion{$iDbHistoryId} = $strDbVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
# Loop through the DBs in the order they were created as indicated by the db-id so that the last one is set in the db section
|
|
|
|
foreach my $iDbHistoryId (sort {$a <=> $b} keys %hDbHistoryVersion)
|
2016-12-20 23:52:20 +02:00
|
|
|
{
|
2017-04-03 16:42:55 +02:00
|
|
|
my $strDbVersion = $hDbHistoryVersion{$iDbHistoryId};
|
|
|
|
my $strVersionDir = $strDbVersion . "-" . $iDbHistoryId;
|
2016-12-20 23:52:20 +02:00
|
|
|
|
|
|
|
# Get the name of the first archive directory
|
2017-06-09 23:51:41 +02:00
|
|
|
my $strArchiveDir = (storageRepo()->list(
|
2017-04-05 03:17:19 +02:00
|
|
|
$self->{strArchiveClusterPath} . "/${strVersionDir}",
|
|
|
|
{strExpression => REGEX_ARCHIVE_DIR_WAL, bIgnoreMissing => true}))[0];
|
2016-12-20 23:52:20 +02:00
|
|
|
|
|
|
|
# Continue if any file structure or missing files info
|
|
|
|
if (!defined($strArchiveDir))
|
2015-06-14 00:25:49 +02:00
|
|
|
{
|
2016-12-20 23:52:20 +02:00
|
|
|
$strInvalidFileStructure = "found empty directory " . $self->{strArchiveClusterPath} . "/${strVersionDir}";
|
|
|
|
next;
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
|
2016-12-20 23:52:20 +02:00
|
|
|
# ??? Should probably make a function in ArchiveCommon
|
2017-06-09 23:51:41 +02:00
|
|
|
my $strArchiveFile = (storageRepo()->list(
|
2017-04-05 03:17:19 +02:00
|
|
|
$self->{strArchiveClusterPath} . "/${strVersionDir}/${strArchiveDir}",
|
2017-06-09 23:51:41 +02:00
|
|
|
{strExpression => "^[0-F]{24}(\\.partial){0,1}(-[0-f]+){0,1}(\\." . COMPRESS_EXT . "){0,1}\$",
|
2017-04-05 03:17:19 +02:00
|
|
|
bIgnoreMissing => true}))[0];
|
2016-12-20 23:52:20 +02:00
|
|
|
|
|
|
|
# Continue if any file structure or missing files info
|
|
|
|
if (!defined($strArchiveFile))
|
2015-06-14 00:25:49 +02:00
|
|
|
{
|
2016-12-20 23:52:20 +02:00
|
|
|
$strInvalidFileStructure =
|
|
|
|
"found empty directory " . $self->{strArchiveClusterPath} . "/${strVersionDir}/${strArchiveDir}";
|
|
|
|
next;
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
|
2016-12-20 23:52:20 +02:00
|
|
|
# Get the full path for the file
|
|
|
|
my $strArchiveFilePath = $self->{strArchiveClusterPath}."/${strVersionDir}/${strArchiveDir}/${strArchiveFile}";
|
|
|
|
|
|
|
|
# Get the db-system-id from the WAL file depending on the version of postgres
|
|
|
|
my $iSysIdOffset = $strDbVersion >= PG_VERSION_93 ? PG_WAL_SYSTEM_ID_OFFSET_GTE_93 : PG_WAL_SYSTEM_ID_OFFSET_LT_93;
|
|
|
|
|
2017-11-06 19:51:12 +02:00
|
|
|
# Error if the file encryption setting is not valid for the repo
|
|
|
|
if (!storageRepo()->encryptionValid(storageRepo()->encrypted($strArchiveFilePath)))
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "encryption incompatible for '$strArchiveFilePath'" .
|
2018-11-07 02:38:38 +02:00
|
|
|
"\nHINT: Is or was the repo encrypted?", ERROR_CRYPTO);
|
2017-11-06 19:51:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
# If the file is encrypted, then the passprase from the info file is required, else getEncryptionKeySub returns undefined
|
2017-06-09 23:51:41 +02:00
|
|
|
my $oFileIo = storageRepo()->openRead(
|
|
|
|
$strArchiveFilePath,
|
|
|
|
{rhyFilter => $strArchiveFile =~ ('\.' . COMPRESS_EXT . '$') ?
|
2019-06-26 14:24:58 +02:00
|
|
|
[{strClass => STORAGE_FILTER_GZIP, rxyParam => [STORAGE_DECOMPRESS, false]}] : undef,
|
2017-11-06 19:51:12 +02:00
|
|
|
strCipherPass => $self->cipherPassSub()});
|
2019-06-26 14:24:58 +02:00
|
|
|
$oFileIo->open();
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2019-06-26 14:24:58 +02:00
|
|
|
my $tBlock;
|
|
|
|
$oFileIo->read(\$tBlock, 512);
|
2017-06-09 23:51:41 +02:00
|
|
|
$oFileIo->close();
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2016-12-20 23:52:20 +02:00
|
|
|
# Get the required data from the file that was pulled into scalar $tBlock
|
|
|
|
my ($iMagic, $iFlag, $junk, $ullDbSysId) = unpack('SSa' . $iSysIdOffset . 'Q', $tBlock);
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2016-12-20 23:52:20 +02:00
|
|
|
if (!defined($ullDbSysId))
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable to read database system identifier", ERROR_FILE_READ);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Fill db section and db history section
|
|
|
|
$self->dbSectionSet($strDbVersion, $ullDbSysId, $iDbHistoryId);
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
|
2017-04-03 16:42:55 +02:00
|
|
|
# If the DB section does not exist, then there were no valid directories to read from so create the DB and History sections.
|
2016-12-20 23:52:20 +02:00
|
|
|
if (!$self->test(INFO_ARCHIVE_SECTION_DB))
|
2015-06-14 00:25:49 +02:00
|
|
|
{
|
2016-12-20 23:52:20 +02:00
|
|
|
$self->create($strCurrentDbVersion, $ullCurrentDbSysId, false);
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
2017-04-03 16:42:55 +02:00
|
|
|
# Else if it does exist but does not match the current DB, then update the DB section
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Turn off console logging to control when to display the error
|
|
|
|
logDisable();
|
|
|
|
|
|
|
|
eval
|
|
|
|
{
|
|
|
|
$self->check($strCurrentDbVersion, $ullCurrentDbSysId, false);
|
|
|
|
logEnable();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
or do
|
|
|
|
{
|
|
|
|
# Reset the console logging
|
|
|
|
logEnable();
|
|
|
|
|
|
|
|
# Confess unhandled errors
|
|
|
|
confess $EVAL_ERROR if (exceptionCode($EVAL_ERROR) != ERROR_ARCHIVE_MISMATCH);
|
|
|
|
|
|
|
|
# Update the DB section if it does not match the current database
|
|
|
|
$self->dbSectionSet($strCurrentDbVersion, $ullCurrentDbSysId, $self->dbHistoryIdGet(false)+1);
|
|
|
|
};
|
|
|
|
}
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
2016-12-20 23:52:20 +02:00
|
|
|
{name => 'strInvalidFileStructure', value => $strInvalidFileStructure}
|
2015-08-29 20:20:46 +02:00
|
|
|
);
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2016-12-20 23:52:20 +02:00
|
|
|
# dbHistoryIdGet
|
2015-06-14 00:25:49 +02:00
|
|
|
#
|
2016-12-20 23:52:20 +02:00
|
|
|
# Get the db history ID
|
2015-06-14 00:25:49 +02:00
|
|
|
####################################################################################################################################
|
2016-12-20 23:52:20 +02:00
|
|
|
sub dbHistoryIdGet
|
2015-06-14 00:25:49 +02:00
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
2016-12-20 23:52:20 +02:00
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$bFileRequired,
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
__PACKAGE__ . '->dbHistoryIdGet', \@_,
|
|
|
|
{name => 'bFileRequired', default => true},
|
|
|
|
);
|
|
|
|
|
|
|
|
# Confirm the info file exists if it is required
|
|
|
|
if ($bFileRequired)
|
|
|
|
{
|
|
|
|
$self->confirmExists();
|
|
|
|
}
|
|
|
|
|
|
|
|
# If the DB section does not exist, initialize the history to one, else return the latest ID
|
|
|
|
my $iDbHistoryId = (!$self->test(INFO_ARCHIVE_SECTION_DB))
|
|
|
|
? 1 : $self->numericGet(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_ID);
|
2015-08-29 20:20:46 +02:00
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
2016-12-20 23:52:20 +02:00
|
|
|
{name => 'iDbHistoryId', value => $iDbHistoryId}
|
2015-08-29 20:20:46 +02:00
|
|
|
);
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
|
2016-12-20 23:52:20 +02:00
|
|
|
####################################################################################################################################
|
2017-04-03 16:42:55 +02:00
|
|
|
# dbHistoryList
|
|
|
|
#
|
|
|
|
# Get the data from the db history section.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub dbHistoryList
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
) = logDebugParam
|
|
|
|
(
|
|
|
|
__PACKAGE__ . '->dbHistoryList',
|
|
|
|
);
|
|
|
|
|
|
|
|
my %hDbHash;
|
|
|
|
|
|
|
|
foreach my $iHistoryId ($self->keys(INFO_ARCHIVE_SECTION_DB_HISTORY))
|
|
|
|
{
|
|
|
|
$hDbHash{$iHistoryId}{&INFO_DB_VERSION} =
|
|
|
|
$self->get(INFO_ARCHIVE_SECTION_DB_HISTORY, $iHistoryId, INFO_ARCHIVE_KEY_DB_VERSION);
|
|
|
|
$hDbHash{$iHistoryId}{&INFO_SYSTEM_ID} =
|
|
|
|
$self->get(INFO_ARCHIVE_SECTION_DB_HISTORY, $iHistoryId, INFO_ARCHIVE_KEY_DB_ID);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'hDbHash', value => \%hDbHash}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2016-12-20 23:52:20 +02:00
|
|
|
# dbSectionSet
|
|
|
|
#
|
|
|
|
# Set the db and db:history sections.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub dbSectionSet
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$strDbVersion,
|
|
|
|
$ullDbSysId,
|
|
|
|
$iDbHistoryId,
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
__PACKAGE__ . '->dbSectionSet', \@_,
|
|
|
|
{name => 'strDbVersion', trace => true},
|
|
|
|
{name => 'ullDbSysId', trace => true},
|
|
|
|
{name => 'iDbHistoryId', trace => true}
|
|
|
|
);
|
|
|
|
|
|
|
|
# Fill db section
|
|
|
|
$self->numericSet(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_SYSTEM_ID, undef, $ullDbSysId);
|
|
|
|
$self->set(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_VERSION, undef, $strDbVersion);
|
|
|
|
$self->numericSet(INFO_ARCHIVE_SECTION_DB, INFO_ARCHIVE_KEY_DB_ID, undef, $iDbHistoryId);
|
|
|
|
|
|
|
|
# Fill db history
|
|
|
|
$self->numericSet(INFO_ARCHIVE_SECTION_DB_HISTORY, $iDbHistoryId, INFO_ARCHIVE_KEY_DB_ID, $ullDbSysId);
|
|
|
|
$self->set(INFO_ARCHIVE_SECTION_DB_HISTORY, $iDbHistoryId, INFO_ARCHIVE_KEY_DB_VERSION, $strDbVersion);
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn($strOperation);
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# confirmExists
|
|
|
|
#
|
|
|
|
# Ensure that the archive.info file and the db section exist.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub confirmExists
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
|
|
|
# Confirm the file exists and the DB section is filled out
|
|
|
|
if (!$self->test(INFO_ARCHIVE_SECTION_DB) || !$self->{bExists})
|
|
|
|
{
|
|
|
|
confess &log(ERROR, $strArchiveInfoMissingMsg, ERROR_FILE_MISSING);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-06-14 00:25:49 +02:00
|
|
|
1;
|