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-06-14 00:25:49 +02:00
|
|
|
use Exporter qw(import);
|
|
|
|
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-06-14 00:25:49 +02:00
|
|
|
use BackRest::Config;
|
2015-04-03 04:07:23 +02:00
|
|
|
use BackRest::Exception;
|
2015-06-14 00:25:49 +02:00
|
|
|
use BackRest::File;
|
2015-06-30 04:07:42 +02:00
|
|
|
use BackRest::Open3;
|
2014-06-22 17:30:17 +03:00
|
|
|
use BackRest::Utility;
|
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-06-30 04:07:42 +02:00
|
|
|
use constant OP_DB_INFO => OP_DB . "->info";
|
|
|
|
our @EXPORT = qw(OP_DB_INFO);
|
|
|
|
use constant OP_DB_EXECUTE_SQL => OP_DB . "->executeSql";
|
|
|
|
push @EXPORT, qw(OP_DB_EXECUTE_SQL);
|
|
|
|
use constant OP_DB_VERSION_GET => OP_DB . "->versionGet";
|
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
|
|
|
|
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;
|
|
|
|
|
|
|
|
return $self;
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
2015-04-03 04:07:23 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# versionSupport
|
|
|
|
#
|
|
|
|
# Returns an array of the supported Postgres versions.
|
|
|
|
####################################################################################################################################
|
|
|
|
sub versionSupport
|
|
|
|
{
|
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
|
|
|
|
|
|
|
return \@strySupportVersion;
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
my $strScript = shift; # psql script to execute (must be on a single line)
|
|
|
|
|
2015-08-05 14:43:41 +02:00
|
|
|
logDebug(OP_DB_EXECUTE_SQL, DEBUG_CALL, undef, {isRemote => optionRemoteTypeTest(DB), script => \$strScript});
|
2014-03-06 03:53:13 +03:00
|
|
|
|
|
|
|
# Get the user-defined command for psql
|
2015-06-30 04:07:42 +02:00
|
|
|
my $strCommand = optionGet(OPTION_COMMAND_PSQL) . " -c \"${strScript}\" postgres";
|
2014-03-06 03:53:13 +03:00
|
|
|
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;
|
|
|
|
|
|
|
|
$oParamHash{'script'} = $strScript;
|
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-06-30 04:07:42 +02:00
|
|
|
$strResult = (new BackRest::Open3($strCommand))->capture();
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return $strResult;
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# TABLESPACE_MAP_GET - Get the mapping between oid and tablespace name
|
|
|
|
####################################################################################################################################
|
|
|
|
sub tablespace_map_get
|
|
|
|
{
|
|
|
|
my $self = shift;
|
2015-05-22 20:49:14 +02:00
|
|
|
|
|
|
|
my $oHashRef = {};
|
2014-03-06 03:53:13 +03:00
|
|
|
|
2015-06-30 04:07:42 +02:00
|
|
|
data_hash_build($oHashRef, "oid\tname\n" . $self->executeSql(
|
2014-09-16 15:55:40 +03:00
|
|
|
'copy (select oid, spcname from pg_tablespace) to stdout'), "\t");
|
2015-05-22 20:49:14 +02:00
|
|
|
|
|
|
|
return $oHashRef;
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
2015-06-14 00:25:49 +02:00
|
|
|
####################################################################################################################################
|
|
|
|
# info
|
|
|
|
####################################################################################################################################
|
|
|
|
sub info
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $oFile = shift;
|
|
|
|
my $strDbPath = shift;
|
|
|
|
|
2015-06-17 21:33:58 +02:00
|
|
|
logDebug(OP_DB_INFO, DEBUG_CALL, undef, {isRemote => $oFile->is_remote(PATH_DB_ABSOLUTE), dbPath => $strDbPath});
|
2015-06-14 00:25:49 +02:00
|
|
|
|
|
|
|
# Database info
|
|
|
|
my $iCatalogVersion;
|
|
|
|
my $iControlVersion;
|
|
|
|
my $ullDbSysId;
|
|
|
|
my $strDbVersion;
|
|
|
|
|
|
|
|
if ($oFile->is_remote(PATH_DB_ABSOLUTE))
|
|
|
|
{
|
|
|
|
# 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);
|
|
|
|
|
|
|
|
$strDbVersion = $stryToken[0];
|
|
|
|
$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
|
|
|
|
if ($iControlVersion == 942 && $iCatalogVersion == 201409291)
|
|
|
|
{
|
|
|
|
$strDbVersion = '9.4';
|
|
|
|
}
|
2015-06-30 04:07:42 +02:00
|
|
|
# Leave 9.5 catalog version out until it stabilizes (then move 9.5 to the top of the list)
|
2015-06-14 16:12:36 +02:00
|
|
|
elsif ($iControlVersion == 942) # && $iCatalogVersion == 201505311)
|
|
|
|
{
|
|
|
|
$strDbVersion = '9.5';
|
|
|
|
}
|
2015-06-14 00:25:49 +02:00
|
|
|
elsif ($iControlVersion == 937 && $iCatalogVersion == 201306121)
|
|
|
|
{
|
|
|
|
$strDbVersion = '9.3';
|
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 922 && $iCatalogVersion == 201204301)
|
|
|
|
{
|
|
|
|
$strDbVersion = '9.2';
|
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 903 && $iCatalogVersion == 201105231)
|
|
|
|
{
|
|
|
|
$strDbVersion = '9.1';
|
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 903 && $iCatalogVersion == 201008051)
|
|
|
|
{
|
|
|
|
$strDbVersion = '9.0';
|
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 843 && $iCatalogVersion == 200904091)
|
|
|
|
{
|
|
|
|
$strDbVersion = '8.4';
|
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 833 && $iCatalogVersion == 200711281)
|
|
|
|
{
|
|
|
|
$strDbVersion = '8.3';
|
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 822 && $iCatalogVersion == 200611241)
|
|
|
|
{
|
|
|
|
$strDbVersion = '8.2';
|
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 812 && $iCatalogVersion == 200510211)
|
|
|
|
{
|
|
|
|
$strDbVersion = '8.1';
|
|
|
|
}
|
|
|
|
elsif ($iControlVersion == 74 && $iCatalogVersion == 200411041)
|
|
|
|
{
|
|
|
|
$strDbVersion = '8.0';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unexpected control version = ${iControlVersion} and catalog version = ${iCatalogVersion}" .
|
|
|
|
' (unsupported PostgreSQL version?)',
|
|
|
|
ERROR_VERSION_NOT_SUPPORTED);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
&log(DEBUG, OP_DB_INFO . "=>: dbVersion = ${strDbVersion}, controlVersion = ${iControlVersion}" .
|
|
|
|
", catalogVersion = ${iCatalogVersion}, dbSysId = ${ullDbSysId}");
|
|
|
|
|
|
|
|
return $strDbVersion, $iControlVersion, $iCatalogVersion, $ullDbSysId;
|
|
|
|
}
|
|
|
|
|
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
|
|
|
|
2014-03-06 03:53:13 +03:00
|
|
|
if (defined($self->{fVersion}))
|
|
|
|
{
|
|
|
|
return $self->{fVersion};
|
|
|
|
}
|
|
|
|
|
2014-06-04 05:02:56 +03:00
|
|
|
$self->{fVersion} =
|
2015-06-30 04:07:42 +02:00
|
|
|
trim($self->executeSql("copy (select (regexp_matches(split_part(version(), ' ', 2), '^[0-9]+\.[0-9]+'))[1]) to stdout"));
|
2014-03-06 03:53:13 +03:00
|
|
|
|
2015-04-03 04:07:23 +02:00
|
|
|
my $strVersionSupport = versionSupport();
|
|
|
|
|
|
|
|
if ($self->{fVersion} < ${$strVersionSupport}[0])
|
|
|
|
{
|
|
|
|
confess &log(ERROR, "unsupported Postgres version ${$strVersionSupport}[0]", ERROR_VERSION_NOT_SUPPORTED);
|
|
|
|
}
|
|
|
|
|
2015-06-30 04:07:42 +02:00
|
|
|
logDebug(OP_DB_VERSION_GET, DEBUG_RESULT, {dbVersion => $self->{fVersion}});
|
|
|
|
|
2014-03-06 03:53:13 +03:00
|
|
|
return $self->{fVersion};
|
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# BACKUP_START
|
|
|
|
####################################################################################################################################
|
|
|
|
sub backup_start
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
my $strLabel = shift;
|
2014-03-28 22:20:36 +03:00
|
|
|
my $bStartFast = shift;
|
2014-03-06 03:53:13 +03:00
|
|
|
|
2015-06-30 04:07:42 +02:00
|
|
|
$self->versionGet();
|
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-04-01 21:58:33 +02:00
|
|
|
if ($self->{fVersion} < 8.4 && $bStartFast)
|
|
|
|
{
|
|
|
|
&log(WARN, 'start-fast option is only available in PostgreSQL >= 8.4');
|
|
|
|
$bStartFast = false;
|
|
|
|
}
|
|
|
|
|
2015-05-26 18:26:59 +02:00
|
|
|
&log(INFO, "executing pg_start_backup() with label \"${strLabel}\": backup will begin after " .
|
|
|
|
($bStartFast ? "the requested immediate checkpoint" : "the next regular checkpoint") . " completes");
|
|
|
|
|
2015-06-30 04:07:42 +02:00
|
|
|
my @stryField = split("\t", trim($self->executeSql("set client_min_messages = 'warning';" .
|
2015-05-26 18:26:59 +02:00
|
|
|
"copy (select to_char(current_timestamp, 'YYYY-MM-DD HH24:MI:SS.US TZ'), " .
|
|
|
|
"pg_xlogfile_name(xlog) from pg_start_backup('${strLabel}'" .
|
2015-01-03 23:49:26 +02:00
|
|
|
($bStartFast ? ', true' : '') . ') as xlog) to stdout')));
|
|
|
|
|
|
|
|
return $stryField[1], $stryField[0];
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
####################################################################################################################################
|
|
|
|
# BACKUP_STOP
|
|
|
|
####################################################################################################################################
|
|
|
|
sub backup_stop
|
|
|
|
{
|
|
|
|
my $self = shift;
|
|
|
|
|
2015-05-26 18:26:59 +02:00
|
|
|
&log(INFO, 'executing pg_stop_backup() and waiting for all WAL segments to be archived');
|
|
|
|
|
2015-06-30 04:07:42 +02:00
|
|
|
my @stryField = split("\t", trim($self->executeSql("set client_min_messages = 'warning';" .
|
|
|
|
"copy (select to_char(clock_timestamp(), 'YYYY-MM-DD HH24:MI:SS.US TZ')," .
|
|
|
|
" pg_xlogfile_name(xlog) from pg_stop_backup() as xlog) to stdout")));
|
2015-01-03 23:49:26 +02:00
|
|
|
|
|
|
|
return $stryField[1], $stryField[0];
|
2014-03-06 03:53:13 +03:00
|
|
|
}
|
|
|
|
|
2014-10-10 23:03:33 +03:00
|
|
|
1;
|