2014-03-06 03:53:13 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# DB MODULE
|
|
|
|
####################################################################################################################################
|
2014-06-08 00:29:11 +03:00
|
|
|
package BackRest::Db;
|
2014-03-06 03:53:13 +03:00
|
|
|
|
|
|
|
use strict;
|
2015-03-03 07:57:20 +02:00
|
|
|
use warnings FATAL => qw(all);
|
|
|
|
use Carp qw(confess);
|
2014-07-28 01:13:23 +03:00
|
|
|
|
2015-08-09 00:22:38 +02:00
|
|
|
use DBD::Pg ':async';
|
2015-08-05 18:32:12 +02:00
|
|
|
use DBI;
|
2015-06-14 00:25:49 +02:00
|
|
|
use Exporter qw(import);
|
2015-08-08 23:11:20 +02:00
|
|
|
our @EXPORT = qw();
|
2015-06-14 00:25:49 +02:00
|
|
|
use Fcntl qw(O_RDONLY);
|
2015-06-30 04:07:42 +02:00
|
|
|
use File::Basename qw(dirname);
|
2014-03-06 03:53:13 +03:00
|
|
|
|
|
|
|
use lib dirname($0);
|
2015-08-29 20:20:46 +02:00
|
|
|
use BackRest::Common::Exception;
|
|
|
|
use BackRest::Common::Log;
|
|
|
|
use BackRest::Common::String;
|
|
|
|
use BackRest::Common::Wait;
|
2015-09-08 13:31:24 +02:00
|
|
|
use BackRest::Config::Config;
|
2015-06-14 00:25:49 +02:00
|
|
|
use BackRest::File;
|
2014-03-06 03:53:13 +03:00
|
|
|
|
2015-03-12 18:15:19 +02:00
|
|
|
####################################################################################################################################
|
2015-06-14 00:25:49 +02:00
|
|
|
# Operation constants
|
2015-03-12 18:15:19 +02:00
|
|
|
####################################################################################################################################
|
2015-06-30 04:07:42 +02:00
|
|
|
use constant OP_DB => 'Db';
|
2015-03-12 18:15:19 +02:00
|
|
|
|
2015-08-05 18:32:12 +02:00
|
|
|
use constant OP_DB_NEW => OP_DB . "->new";
|
2015-08-08 23:11:20 +02:00
|
|
|
use constant OP_DB_BACKUP_START => OP_DB . "->backupStart";
|
|
|
|
use constant OP_DB_BACKUP_STOP => OP_DB . "->backupStop";
|
2015-08-29 20:20:46 +02:00
|
|
|
use constant OP_DB_DESTROY => OP_DB . "->DESTROY";
|
2015-06-30 04:07:42 +02:00
|
|
|
use constant OP_DB_EXECUTE_SQL => OP_DB . "->executeSql";
|
|
|
|
push @EXPORT, qw(OP_DB_EXECUTE_SQL);
|
2015-08-29 20:20:46 +02:00
|
|
|
use constant OP_DB_EXECUTE_SQL_ONE => OP_DB . "->executeSqlOne";
|
|
|
|
use constant OP_DB_EXECUTE_SQL_ROW => OP_DB . "->executeSqlRow";
|
2015-08-08 23:11:20 +02:00
|
|
|
use constant OP_DB_INFO => OP_DB . "->info";
|
|
|
|
push @EXPORT, qw(OP_DB_INFO);
|
|
|
|
use constant OP_DB_TABLESPACE_MAP_GET => OP_DB . "->tablespaceMapGet";
|
2015-06-30 04:07:42 +02:00
|
|
|
use constant OP_DB_VERSION_GET => OP_DB . "->versionGet";
|
2015-08-29 20:20:46 +02:00
|
|
|
use constant OP_DB_VERSION_SUPPORT => OP_DB . "->versionSupport";
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# Postmaster process Id file
|
|
|
|
####################################################################################################################################
|
2015-06-30 04:07:42 +02:00
|
|
|
use constant FILE_POSTMASTER_PID => 'postmaster.pid';
|
|
|
|
push @EXPORT, qw(FILE_POSTMASTER_PID);
|
2015-03-12 18:15:19 +02:00
|
|
|
|
2015-08-08 23:11:20 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# Backup advisory lock
|
|
|
|
####################################################################################################################################
|
|
|
|
use constant DB_BACKUP_ADVISORY_LOCK => '12340078987004321';
|
|
|
|
push @EXPORT, qw(DB_BACKUP_ADVISORY_LOCK);
|
|
|
|
|
2014-03-06 03:53:13 +03:00
|
|
|
####################################################################################################################################
|
|
|
|
# CONSTRUCTOR
|
|
|
|
####################################################################################################################################
|
2014-10-10 23:03:33 +03:00
|
|
|
sub new
|
2014-03-06 03:53:13 +03:00
|
|
|
{
|
2015-04-01 21:58:33 +02:00
|
|
|
my $class = shift; # Class name
|
2014-10-10 23:03:33 +03:00
|
|
|
|
|
|
|
# Create the class hash
|
|
|
|
my $self = {};
|
|
|
|
bless $self, $class;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
(
|
|
|
|
my $strOperation
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_NEW
|
|
|
|
);
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'self', value => $self}
|
|
|
|
);
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
2015-08-05 18:32:12 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# DESTRUCTOR
|
|
|
|
####################################################################################################################################
|
|
|
|
sub DESTROY
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_DESTROY
|
|
|
|
);
|
|
|
|
|
2015-08-05 18:32:12 +02:00
|
|
|
if (defined($self->{hDb}))
|
|
|
|
{
|
|
|
|
$self->{hDb}->disconnect();
|
|
|
|
undef($self->{hDb});
|
|
|
|
}
|
2015-08-29 20:20:46 +02:00
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation
|
|
|
|
);
|
2015-08-05 18:32:12 +02:00
|
|
|
}
|
|
|
|
|
2015-04-03 04:07:23 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# versionSupport
|
|
|
|
#
|
|
|
|
# Returns an array of the supported Postgres versions.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub versionSupport
|
|
|
|
{
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_VERSION_SUPPORT
|
|
|
|
);
|
|
|
|
|
2015-06-14 16:12:36 +02:00
|
|
|
my @strySupportVersion = ('8.3', '8.4', '9.0', '9.1', '9.2', '9.3', '9.4', '9.5');
|
2015-04-03 04:07:23 +02:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'strySupportVersion', value => \@strySupportVersion}
|
|
|
|
);
|
2015-04-03 04:07:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
push @EXPORT, qw(versionSupport);
|
|
|
|
|
2014-03-06 03:53:13 +03:00
|
|
|
####################################################################################################################################
|
2015-06-30 04:07:42 +02:00
|
|
|
# executeSql
|
2014-03-06 03:53:13 +03:00
|
|
|
####################################################################################################################################
|
2015-06-30 04:07:42 +02:00
|
|
|
sub executeSql
|
2014-03-06 03:53:13 +03:00
|
|
|
{
|
|
|
|
my $self = shift;
|
2015-06-30 04:07:42 +02:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$strSql,
|
|
|
|
$bIgnoreError
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_EXECUTE_SQL, \@_,
|
|
|
|
{name => 'strSql'},
|
|
|
|
{name => 'bIgnoreError', default => false}
|
|
|
|
);
|
2014-03-06 03:53:13 +03:00
|
|
|
|
|
|
|
# Get the user-defined command for psql
|
|
|
|
my $strResult;
|
|
|
|
|
|
|
|
# Run remotely
|
2015-06-30 04:07:42 +02:00
|
|
|
if (optionRemoteTypeTest(DB))
|
2014-03-06 03:53:13 +03:00
|
|
|
{
|
2015-06-30 04:07:42 +02:00
|
|
|
# Build param hash
|
|
|
|
my %oParamHash;
|
|
|
|
|
2015-08-08 23:11:20 +02:00
|
|
|
$oParamHash{'script'} = $strSql;
|
2015-08-29 20:20:46 +02:00
|
|
|
$oParamHash{'ignore-error'} = $bIgnoreError;
|
2014-03-06 03:53:13 +03:00
|
|
|
|
2015-06-30 04:07:42 +02:00
|
|
|
# Execute the command
|
2015-08-05 14:43:41 +02:00
|
|
|
$strResult = protocolGet()->cmdExecute(OP_DB_EXECUTE_SQL, \%oParamHash, true);
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
# Else run locally
|
|
|
|
else
|
|
|
|
{
|
2015-08-05 18:32:12 +02:00
|
|
|
if (!defined($self->{hDb}))
|
|
|
|
{
|
|
|
|
# Connect to the db
|
|
|
|
my $strDbName = 'postgres';
|
|
|
|
my $strDbUser = getpwuid($<);
|
2015-08-06 04:05:45 +02:00
|
|
|
my $strDbSocketPath = optionGet(OPTION_DB_SOCKET_PATH, false);
|
|
|
|
|
|
|
|
if (defined($strDbSocketPath) && $strDbSocketPath !~ /^\//)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "'${strDbSocketPath}' is not valid for '" . OPTION_DB_SOCKET_PATH . "' option:" .
|
|
|
|
" path must be absolute", ERROR_OPTION_INVALID_VALUE);
|
|
|
|
}
|
|
|
|
|
2015-08-05 18:32:12 +02:00
|
|
|
my $strDbUri = "dbi:Pg:dbname=${strDbName};port=" . optionGet(OPTION_DB_PORT) .
|
|
|
|
(optionTest(OPTION_DB_SOCKET_PATH) ? ';host=' . optionGet(OPTION_DB_SOCKET_PATH) : '');
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
logDebugMisc
|
|
|
|
(
|
|
|
|
$strOperation, 'db connect',
|
|
|
|
{name => 'strDbUri', value => $strDbUri},
|
|
|
|
{name => 'strDbUser', value => $strDbUser}
|
|
|
|
);
|
2015-08-05 18:32:12 +02:00
|
|
|
|
2015-09-08 20:01:19 +02:00
|
|
|
$self->{hDb} = DBI->connect($strDbUri, $strDbUser, undef,
|
|
|
|
{AutoCommit => 1, RaiseError => 0, PrintError => 0, Warn => 0});
|
2015-08-05 18:32:12 +02:00
|
|
|
|
|
|
|
if (!$self->{hDb})
|
|
|
|
{
|
|
|
|
confess &log(ERROR, $DBI::errstr, ERROR_DB_CONNECT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-08 23:11:20 +02:00
|
|
|
# Prepare the query
|
2015-08-09 00:22:38 +02:00
|
|
|
my $hStatement = $self->{hDb}->prepare($strSql, {pg_async => PG_ASYNC})
|
2015-08-05 18:32:12 +02:00
|
|
|
or confess &log(ERROR, $DBI::errstr, ERROR_DB_QUERY);
|
|
|
|
|
2015-08-08 23:11:20 +02:00
|
|
|
# Execute the query
|
2015-08-09 00:22:38 +02:00
|
|
|
$hStatement->execute();
|
2015-08-08 23:11:20 +02:00
|
|
|
|
2015-08-09 00:22:38 +02:00
|
|
|
# Wait for the query to return
|
2015-08-29 20:20:46 +02:00
|
|
|
my $oWait = waitInit(optionGet(OPTION_DB_TIMEOUT));
|
2015-08-09 00:22:38 +02:00
|
|
|
my $bTimeout = true;
|
2015-08-05 18:32:12 +02:00
|
|
|
|
|
|
|
do
|
|
|
|
{
|
2015-08-09 00:22:38 +02:00
|
|
|
# Is the statement done?
|
|
|
|
if ($hStatement->pg_ready())
|
2015-08-05 18:32:12 +02:00
|
|
|
{
|
2015-08-09 00:22:38 +02:00
|
|
|
if (!$hStatement->pg_result())
|
|
|
|
{
|
|
|
|
# Return if the error should be ignored
|
2015-08-29 20:20:46 +02:00
|
|
|
if ($bIgnoreError)
|
2015-08-09 00:22:38 +02:00
|
|
|
{
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
|
|
|
|
# Else report it
|
|
|
|
confess &log(ERROR, $DBI::errstr . ":\n${strSql}", ERROR_DB_QUERY);
|
|
|
|
}
|
|
|
|
|
|
|
|
# Get rows and return them
|
|
|
|
my @stryArray;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
@stryArray = $hStatement->fetchrow_array;
|
|
|
|
|
|
|
|
if (!@stryArray && $hStatement->err)
|
|
|
|
{
|
|
|
|
confess &log(ERROR, $DBI::errstr . ":\n${strSql}", ERROR_DB_QUERY);
|
|
|
|
}
|
|
|
|
|
|
|
|
$strResult = (defined($strResult) ? "${strResult}\n" : '') . join("\t", @stryArray);
|
|
|
|
}
|
|
|
|
while (@stryArray);
|
|
|
|
|
|
|
|
$bTimeout = false;
|
2015-08-05 18:32:12 +02:00
|
|
|
}
|
2015-08-29 20:20:46 +02:00
|
|
|
} while ($bTimeout && waitMore($oWait));
|
2015-08-05 18:32:12 +02:00
|
|
|
|
2015-08-09 00:22:38 +02:00
|
|
|
# If timeout then cancel the query and confess
|
|
|
|
if ($bTimeout)
|
|
|
|
{
|
|
|
|
$hStatement->pg_cancel();
|
2015-08-29 20:20:46 +02:00
|
|
|
confess &log(ERROR, 'statement timed out after ' . waitInterval($oWait) .
|
|
|
|
" second(s):\n${strSql}", ERROR_DB_TIMEOUT);
|
2015-08-05 18:32:12 +02:00
|
|
|
}
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'strResult', value => $strResult}
|
|
|
|
);
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2015-08-08 23:11:20 +02:00
|
|
|
# executeSqlRow
|
|
|
|
####################################################################################################################################
|
|
|
|
sub executeSqlRow
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$strSql
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_EXECUTE_SQL_ROW, \@_,
|
|
|
|
{name => 'strSql', trace => true}
|
|
|
|
);
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
my @stryResult = split("\t", trim($self->executeSql($strSql)));
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'strResult', value => \@stryResult}
|
|
|
|
);
|
2015-08-08 23:11:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# executeSqlOne
|
2014-03-06 03:53:13 +03:00
|
|
|
####################################################################################################################################
|
2015-08-08 23:11:20 +02:00
|
|
|
sub executeSqlOne
|
2014-03-06 03:53:13 +03:00
|
|
|
{
|
|
|
|
my $self = shift;
|
2015-08-08 23:11:20 +02:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$strSql
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_EXECUTE_SQL_ONE, \@_,
|
|
|
|
{name => 'strSql', trace => true}
|
|
|
|
);
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'strResult', value => ($self->executeSqlRow($strSql))[0], trace => true}
|
|
|
|
);
|
2015-08-08 23:11:20 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# tablespaceMapGet
|
|
|
|
#
|
|
|
|
# Get the mapping between oid and tablespace name.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub tablespaceMapGet
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_TABLESPACE_MAP_GET
|
|
|
|
);
|
|
|
|
|
|
|
|
dataHashBuild(my $oTablespaceMapRef = {}, "oid\tname\n" . $self->executeSql(
|
|
|
|
'select oid, spcname from pg_tablespace'), "\t");
|
|
|
|
|
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'oTablespaceMapRef', value => $oTablespaceMapRef}
|
|
|
|
);
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
2015-06-14 00:25:49 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# info
|
|
|
|
####################################################################################################################################
|
|
|
|
sub info
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$oFile,
|
|
|
|
$strDbPath
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_INFO, \@_,
|
|
|
|
{name => 'oFile'},
|
|
|
|
{name => 'strDbPath'}
|
|
|
|
);
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2015-08-06 04:05:45 +02:00
|
|
|
# Return data from the cache if it exists
|
|
|
|
if (defined($self->{info}{$strDbPath}))
|
|
|
|
{
|
|
|
|
return $self->{info}{$strDbPath}{fDbVersion},
|
|
|
|
$self->{info}{$strDbPath}{iControlVersion},
|
|
|
|
$self->{info}{$strDbPath}{iCatalogVersion},
|
|
|
|
$self->{info}{$strDbPath}{ullDbSysId};
|
|
|
|
}
|
|
|
|
|
2015-06-14 00:25:49 +02:00
|
|
|
# Database info
|
|
|
|
my $iCatalogVersion;
|
|
|
|
my $iControlVersion;
|
|
|
|
my $ullDbSysId;
|
2015-08-06 04:05:45 +02:00
|
|
|
my $fDbVersion;
|
2015-06-14 00:25:49 +02:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
if ($oFile->isRemote(PATH_DB_ABSOLUTE))
|
2015-06-14 00:25:49 +02:00
|
|
|
{
|
|
|
|
# Build param hash
|
|
|
|
my %oParamHash;
|
|
|
|
|
|
|
|
$oParamHash{'db-path'} = ${strDbPath};
|
|
|
|
|
|
|
|
# Output remote trace info
|
2015-08-05 14:43:41 +02:00
|
|
|
&log(TRACE, OP_DB_INFO . ": remote (" . $oFile->{oProtocol}->commandParamString(\%oParamHash) . ')');
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
# Execute the command
|
2015-08-05 14:43:41 +02:00
|
|
|
my $strResult = $oFile->{oProtocol}->cmdExecute(OP_DB_INFO, \%oParamHash, true);
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
# Split the result into return values
|
|
|
|
my @stryToken = split(/\t/, $strResult);
|
|
|
|
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = $stryToken[0];
|
2015-06-14 00:25:49 +02:00
|
|
|
$iControlVersion = $stryToken[1];
|
|
|
|
$iCatalogVersion = $stryToken[2];
|
|
|
|
$ullDbSysId = $stryToken[3];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
# Open the control file
|
|
|
|
my $strControlFile = "${strDbPath}/global/pg_control";
|
|
|
|
my $hFile;
|
|
|
|
my $tBlock;
|
|
|
|
|
|
|
|
sysopen($hFile, $strControlFile, O_RDONLY)
|
|
|
|
or confess &log(ERROR, "unable to open ${strControlFile}", ERROR_FILE_OPEN);
|
|
|
|
|
|
|
|
# Read system identifier
|
|
|
|
sysread($hFile, $tBlock, 8) == 8
|
|
|
|
or confess &log(ERROR, "unable to read database system identifier");
|
|
|
|
|
|
|
|
$ullDbSysId = unpack('Q', $tBlock);
|
|
|
|
|
|
|
|
# Read control version
|
|
|
|
sysread($hFile, $tBlock, 4) == 4
|
|
|
|
or confess &log(ERROR, "unable to read control version");
|
|
|
|
|
|
|
|
$iControlVersion = unpack('L', $tBlock);
|
|
|
|
|
|
|
|
# Read catalog version
|
|
|
|
sysread($hFile, $tBlock, 4) == 4
|
|
|
|
or confess &log(ERROR, "unable to read catalog version");
|
|
|
|
|
|
|
|
$iCatalogVersion = unpack('L', $tBlock);
|
|
|
|
|
|
|
|
# Close the control file
|
|
|
|
close($hFile);
|
|
|
|
|
|
|
|
# Make sure the control version is supported
|
2016-01-09 17:11:03 +02:00
|
|
|
if ($iControlVersion == 942 && $iCatalogVersion == 201510051)
|
2015-06-14 00:25:49 +02:00
|
|
|
{
|
2016-01-09 17:11:03 +02:00
|
|
|
$fDbVersion = '9.5';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
2016-01-09 17:11:03 +02:00
|
|
|
elsif ($iControlVersion == 942 && $iCatalogVersion == 201409291)
|
2015-06-14 16:12:36 +02:00
|
|
|
{
|
2016-01-09 17:11:03 +02:00
|
|
|
$fDbVersion = '9.4';
|
2015-06-14 16:12:36 +02:00
|
|
|
}
|
2015-06-14 00:25:49 +02:00
|
|
|
elsif ($iControlVersion == 937 && $iCatalogVersion == 201306121)
|
|
|
|
{
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = '9.3';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 922 && $iCatalogVersion == 201204301)
|
|
|
|
{
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = '9.2';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 903 && $iCatalogVersion == 201105231)
|
|
|
|
{
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = '9.1';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 903 && $iCatalogVersion == 201008051)
|
|
|
|
{
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = '9.0';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 843 && $iCatalogVersion == 200904091)
|
|
|
|
{
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = '8.4';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 833 && $iCatalogVersion == 200711281)
|
|
|
|
{
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = '8.3';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 822 && $iCatalogVersion == 200611241)
|
|
|
|
{
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = '8.2';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 812 && $iCatalogVersion == 200510211)
|
|
|
|
{
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = '8.1';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 74 && $iCatalogVersion == 200411041)
|
|
|
|
{
|
2015-08-06 04:05:45 +02:00
|
|
|
$fDbVersion = '8.0';
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unexpected control version = ${iControlVersion} and catalog version = ${iCatalogVersion}" .
|
|
|
|
' (unsupported PostgreSQL version?)',
|
|
|
|
ERROR_VERSION_NOT_SUPPORTED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-06 04:05:45 +02:00
|
|
|
# Store data in the cache
|
|
|
|
$self->{info}{$strDbPath}{fDbVersion} = $fDbVersion;
|
|
|
|
$self->{info}{$strDbPath}{iControlVersion} = $iControlVersion;
|
|
|
|
$self->{info}{$strDbPath}{iCatalogVersion} = $iCatalogVersion;
|
|
|
|
$self->{info}{$strDbPath}{ullDbSysId} = $ullDbSysId;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'fDbVersion', value => $fDbVersion},
|
|
|
|
{name => 'iControlVersion', value => $iControlVersion},
|
|
|
|
{name => 'iCatalogVersion', value => $iCatalogVersion},
|
|
|
|
{name => 'ullDbSysId', value => $ullDbSysId}
|
|
|
|
);
|
2015-06-14 00:25:49 +02:00
|
|
|
}
|
|
|
|
|
2014-03-06 03:53:13 +03:00
|
|
|
####################################################################################################################################
|
2015-06-30 04:07:42 +02:00
|
|
|
# versionGet
|
2014-03-06 03:53:13 +03:00
|
|
|
####################################################################################################################################
|
2015-06-30 04:07:42 +02:00
|
|
|
sub versionGet
|
2014-03-06 03:53:13 +03:00
|
|
|
{
|
|
|
|
my $self = shift;
|
2014-06-04 05:02:56 +03:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_VERSION_GET
|
|
|
|
);
|
|
|
|
|
2015-08-06 04:05:45 +02:00
|
|
|
# Get data from the cache if possible
|
2015-08-29 20:20:46 +02:00
|
|
|
if (defined($self->{fDbVersion}) && defined($self->{strDbPath}))
|
2014-03-06 03:53:13 +03:00
|
|
|
{
|
2015-08-29 20:20:46 +02:00
|
|
|
return $self->{fDbVersion}, $self->{strDbPath};
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
2015-08-06 04:05:45 +02:00
|
|
|
# Get version and db-path from
|
2015-08-29 20:20:46 +02:00
|
|
|
($self->{fDbVersion}, $self->{strDbPath}) =
|
2015-08-08 23:11:20 +02:00
|
|
|
$self->executeSqlRow("select (regexp_matches(split_part(version(), ' ', 2), '^[0-9]+\.[0-9]+'))[1], setting" .
|
|
|
|
" from pg_settings where name = 'data_directory'");
|
2014-03-06 03:53:13 +03:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
my @stryVersionSupport = versionSupport();
|
2015-04-03 04:07:23 +02:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
if ($self->{fDbVersion} < $stryVersionSupport[0])
|
2015-04-03 04:07:23 +02:00
|
|
|
{
|
2015-08-29 20:20:46 +02:00
|
|
|
confess &log(ERROR, 'unsupported Postgres version' . $self->{fDbVersion}, ERROR_VERSION_NOT_SUPPORTED);
|
2015-04-03 04:07:23 +02:00
|
|
|
}
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'fDbVersion', value => $self->{fDbVersion}},
|
|
|
|
{name => 'strDbPath', value => $self->{strDbPath}}
|
|
|
|
);
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2015-08-08 23:11:20 +02:00
|
|
|
# backupStart
|
2014-03-06 03:53:13 +03:00
|
|
|
####################################################################################################################################
|
2015-08-08 23:11:20 +02:00
|
|
|
sub backupStart
|
2014-03-06 03:53:13 +03:00
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
$oFile,
|
|
|
|
$strDbPath,
|
|
|
|
$strLabel,
|
|
|
|
$bStartFast
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_BACKUP_START, \@_,
|
|
|
|
{name => 'oFile'},
|
|
|
|
{name => 'strDbPath'},
|
|
|
|
{name => 'strLabel'},
|
|
|
|
{name => 'bStartFast'}
|
|
|
|
);
|
2015-08-08 23:11:20 +02:00
|
|
|
|
2015-08-06 04:05:45 +02:00
|
|
|
# Get the version from the control file
|
|
|
|
my ($fDbVersion) = $self->info($oFile, $strDbPath);
|
|
|
|
|
|
|
|
# Get version and db path from the database
|
|
|
|
my ($fCompareDbVersion, $strCompareDbPath) = $self->versionGet();
|
|
|
|
|
2015-09-16 22:23:19 +02:00
|
|
|
# Error if the version from the control file and the configured db-path do not match the values obtained from the database
|
2015-08-06 04:05:45 +02:00
|
|
|
if (!($fDbVersion == $fCompareDbVersion && $strDbPath eq $strCompareDbPath))
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "version '${fCompareDbVersion}' and db-path '${strCompareDbPath}' queried from cluster does not match" .
|
|
|
|
" version '${fDbVersion}' and db-path '${strDbPath}' read from '${strDbPath}/global/pg_control'\n" .
|
|
|
|
"HINT: the db-path and db-port settings likely reference different clusters", ERROR_DB_MISMATCH);
|
|
|
|
}
|
2015-04-01 21:58:33 +02:00
|
|
|
|
2015-08-05 14:43:41 +02:00
|
|
|
# Only allow start-fast option for version >= 8.4
|
2015-08-29 20:20:46 +02:00
|
|
|
if ($self->{fDbVersion} < 8.4 && $bStartFast)
|
2015-04-01 21:58:33 +02:00
|
|
|
{
|
2015-08-08 23:11:20 +02:00
|
|
|
&log(WARN, OPTION_START_FAST . ' option is only available in PostgreSQL >= 8.4');
|
2015-04-01 21:58:33 +02:00
|
|
|
$bStartFast = false;
|
|
|
|
}
|
|
|
|
|
2015-09-16 22:23:19 +02:00
|
|
|
# Acquire the backup advisory lock to make sure that backups are not running from multiple backup servers against the same
|
|
|
|
# database cluster. This lock helps make the stop-auto option safe.
|
2015-08-08 23:11:20 +02:00
|
|
|
if (!$self->executeSqlOne('select pg_try_advisory_lock(' . DB_BACKUP_ADVISORY_LOCK . ')'))
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unable to acquire backup lock\n" .
|
|
|
|
'HINT: is another backup already running on this cluster?', ERROR_LOCK_ACQUIRE);
|
|
|
|
}
|
|
|
|
|
2015-09-16 22:23:19 +02:00
|
|
|
# If stop-auto is enabled check for a running backup
|
2015-08-08 23:11:20 +02:00
|
|
|
if (optionGet(OPTION_STOP_AUTO))
|
|
|
|
{
|
2015-09-16 22:23:19 +02:00
|
|
|
# Running backups can only be detected in PostgreSQL >= 9.3
|
2015-08-29 20:20:46 +02:00
|
|
|
if ($self->{fDbVersion} >= 9.3)
|
2015-08-08 23:11:20 +02:00
|
|
|
{
|
2015-09-16 22:23:19 +02:00
|
|
|
# If a backup is currently in progress emit a warning and then stop it
|
2015-08-08 23:11:20 +02:00
|
|
|
if ($self->executeSqlOne('select pg_is_in_backup()'))
|
|
|
|
{
|
|
|
|
&log(WARN, 'the cluster is already in backup mode but no backup process is running. pg_stop_backup() will be called' .
|
|
|
|
' so a new backup can be started.');
|
|
|
|
$self->backupStop();
|
|
|
|
}
|
|
|
|
}
|
2015-09-16 22:23:19 +02:00
|
|
|
# Else emit a warning that the feature is not supported and continue. If a backup is running then an error will be
|
|
|
|
# generated later on.
|
2015-08-08 23:11:20 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
&log(WARN, OPTION_STOP_AUTO . 'option is only available in PostgreSQL >= 9.3');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-16 22:23:19 +02:00
|
|
|
# Start the backup
|
2015-08-29 20:20:46 +02:00
|
|
|
&log(INFO, "execute pg_start_backup() with label \"${strLabel}\": backup begins after " .
|
2015-05-26 18:26:59 +02:00
|
|
|
($bStartFast ? "the requested immediate checkpoint" : "the next regular checkpoint") . " completes");
|
|
|
|
|
2015-08-08 23:11:20 +02:00
|
|
|
my ($strTimestampDbStart, $strArchiveStart) =
|
|
|
|
$self->executeSqlRow("select to_char(current_timestamp, 'YYYY-MM-DD HH24:MI:SS.US TZ'), " .
|
|
|
|
"pg_xlogfile_name(xlog) from pg_start_backup('${strLabel}'" .
|
|
|
|
($bStartFast ? ', true' : '') . ') as xlog');
|
2015-01-03 23:49:26 +02:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'strArchiveStart', value => $strArchiveStart},
|
|
|
|
{name => 'strTimestampDbStart', value => $strTimestampDbStart}
|
|
|
|
);
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
2015-08-08 23:11:20 +02:00
|
|
|
# backupStop
|
2014-03-06 03:53:13 +03:00
|
|
|
####################################################################################################################################
|
2015-08-08 23:11:20 +02:00
|
|
|
sub backupStop
|
2014-03-06 03:53:13 +03:00
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Assign function parameters, defaults, and log debug info
|
|
|
|
my
|
|
|
|
(
|
|
|
|
$strOperation
|
|
|
|
) =
|
|
|
|
logDebugParam
|
|
|
|
(
|
|
|
|
OP_DB_BACKUP_STOP
|
|
|
|
);
|
|
|
|
|
2015-09-16 22:23:19 +02:00
|
|
|
# Stop the backup
|
2015-08-29 20:20:46 +02:00
|
|
|
&log(INFO, 'execute pg_stop_backup() and wait for all WAL segments to archive');
|
2015-05-26 18:26:59 +02:00
|
|
|
|
2015-08-08 23:11:20 +02:00
|
|
|
my ($strTimestampDbStop, $strArchiveStop) =
|
|
|
|
$self->executeSqlRow("select to_char(clock_timestamp(), 'YYYY-MM-DD HH24:MI:SS.US TZ')," .
|
|
|
|
" pg_xlogfile_name(xlog) from pg_stop_backup() as xlog");
|
2015-01-03 23:49:26 +02:00
|
|
|
|
2015-08-29 20:20:46 +02:00
|
|
|
# Return from function and log return values if any
|
|
|
|
return logDebugReturn
|
|
|
|
(
|
|
|
|
$strOperation,
|
|
|
|
{name => 'strArchiveStop', value => $strArchiveStop},
|
|
|
|
{name => 'strTimestampDbStop', value => $strTimestampDbStop}
|
|
|
|
);
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
2014-10-10 23:03:33 +03:00
|
|
|
1;
|