Skip to content

Commit 4c4eaf3

Browse files
committed
Make PostgresNode version aware
A new PostgresVersion object type is created and this is used in PostgresNode using the output of `pg_config --version` and the result stored in the PostgresNode object. This object can be compared to other PostgresVersion objects, or to a number or string. PostgresNode is currently believed to be compatible with versions down to release 12, so PostgresNode will issue a warning if used with a version prior to that. No attempt has been made to deal with incompatibilities in older versions - that remains work to be undertaken in a subsequent development cycle. Based on code from Mark Dilger and Jehan-Guillaume de Rorthais. Discussion: https://postgr.es/m/[email protected]
1 parent f3b141c commit 4c4eaf3

File tree

2 files changed

+193
-0
lines changed

2 files changed

+193
-0
lines changed

src/test/perl/PostgresNode.pm

+56
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ use File::Spec;
9696
use File::stat qw(stat);
9797
use File::Temp ();
9898
use IPC::Run;
99+
use PostgresVersion;
99100
use RecursiveCopy;
100101
use Socket;
101102
use Test::More;
@@ -350,6 +351,8 @@ sub info
350351
my $_info = '';
351352
open my $fh, '>', \$_info or die;
352353
print $fh "Name: " . $self->name . "\n";
354+
print $fh "Version: " . $self->{_pg_version} . "\n"
355+
if $self->{_pg_version};
353356
print $fh "Data directory: " . $self->data_dir . "\n";
354357
print $fh "Backup directory: " . $self->backup_dir . "\n";
355358
print $fh "Archive directory: " . $self->archive_dir . "\n";
@@ -1196,9 +1199,62 @@ sub get_new_node
11961199
# Add node to list of nodes
11971200
push(@all_nodes, $node);
11981201

1202+
$node->_set_pg_version;
1203+
1204+
my $v = $node->{_pg_version};
1205+
1206+
carp("PostgresNode isn't fully compatible with version " . $v)
1207+
if $v < 12;
1208+
11991209
return $node;
12001210
}
12011211

1212+
# Private routine to run the pg_config binary found in our environment (or in
1213+
# our install_path, if we have one), and set the version from it
1214+
#
1215+
sub _set_pg_version
1216+
{
1217+
my ($self) = @_;
1218+
my $inst = $self->{_install_path};
1219+
my $pg_config = "pg_config";
1220+
1221+
if (defined $inst)
1222+
{
1223+
# If the _install_path is invalid, our PATH variables might find an
1224+
# unrelated pg_config executable elsewhere. Sanity check the
1225+
# directory.
1226+
BAIL_OUT("directory not found: $inst")
1227+
unless -d $inst;
1228+
1229+
# If the directory exists but is not the root of a postgresql
1230+
# installation, or if the user configured using
1231+
# --bindir=$SOMEWHERE_ELSE, we're not going to find pg_config, so
1232+
# complain about that, too.
1233+
$pg_config = "$inst/bin/pg_config";
1234+
BAIL_OUT("pg_config not found: $pg_config")
1235+
unless -e $pg_config;
1236+
BAIL_OUT("pg_config not executable: $pg_config")
1237+
unless -x $pg_config;
1238+
1239+
# Leave $pg_config install_path qualified, to be sure we get the right
1240+
# version information, below, or die trying
1241+
}
1242+
1243+
local %ENV = $self->_get_env();
1244+
1245+
# We only want the version field
1246+
open my $fh, "-|", $pg_config, "--version"
1247+
or
1248+
BAIL_OUT("$pg_config failed: $!");
1249+
my $version_line = <$fh>;
1250+
close $fh or die;
1251+
1252+
$self->{_pg_version} = PostgresVersion->new($version_line);
1253+
1254+
BAIL_OUT("could not parse pg_config --version output: $version_line")
1255+
unless defined $self->{_pg_version};
1256+
}
1257+
12021258
# Private routine to return a copy of the environment with the PATH and
12031259
# (DY)LD_LIBRARY_PATH correctly set when there is an install path set for
12041260
# the node.

src/test/perl/PostgresVersion.pm

+137
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
############################################################################
2+
#
3+
# PostgresVersion.pm
4+
#
5+
# Module encapsulating Postgres Version numbers
6+
#
7+
# Copyright (c) 2021, PostgreSQL Global Development Group
8+
#
9+
############################################################################
10+
11+
=pod
12+
13+
=head1 NAME
14+
15+
PostgresVersion - class representing PostgreSQL version numbers
16+
17+
=head1 SYNOPSIS
18+
19+
use PostgresVersion;
20+
21+
my $version = PostgresVersion->new($version_arg);
22+
23+
# compare two versions
24+
my $bool = $version1 <= $version2;
25+
26+
# or compare with a number
27+
$bool = $version < 12;
28+
29+
# or with a string
30+
$bool = $version lt "13.1";
31+
32+
# interpolate in a string
33+
my $stringyval = "version: $version";
34+
35+
=head1 DESCRIPTION
36+
37+
PostgresVersion encapsulated Postgres version numbers, providing parsing
38+
of common version formats and comparison operations.
39+
40+
=cut
41+
42+
package PostgresVersion;
43+
44+
use strict;
45+
use warnings;
46+
47+
use Scalar::Util qw(blessed);
48+
49+
use overload
50+
'<=>' => \&_version_cmp,
51+
'cmp' => \&_version_cmp,
52+
'""' => \&_stringify;
53+
54+
=pod
55+
56+
=head1 METHODS
57+
58+
=over
59+
60+
=item PostgresVersion->new($version)
61+
62+
Create a new PostgresVersion instance.
63+
64+
The argument can be a number like 12, or a string like '12.2' or the output
65+
of a Postgres command like `psql --version` or `pg_config --version`;
66+
67+
=back
68+
69+
=cut
70+
71+
sub new
72+
{
73+
my $class = shift;
74+
my $arg = shift;
75+
76+
# Accept standard formats, in case caller has handed us the output of a
77+
# postgres command line tool
78+
$arg = $1
79+
if ($arg =~ m/\(?PostgreSQL\)? (\d+(?:\.\d+)*(?:devel)?)/);
80+
81+
# Split into an array
82+
my @result = split(/\./, $arg);
83+
84+
# Treat development versions as having a minor/micro version one less than
85+
# the first released version of that branch.
86+
if ($result[$#result] =~ m/^(\d+)devel$/)
87+
{
88+
pop(@result);
89+
push(@result, $1, -1);
90+
}
91+
92+
my $res = [@result];
93+
bless $res, $class;
94+
return $res;
95+
}
96+
97+
98+
# Routine which compares the _pg_version_array obtained for the two
99+
# arguments and returns -1, 0, or 1, allowing comparison between two
100+
# PostgresVersion objects or a PostgresVersion and a version string or number.
101+
#
102+
# If the second argument is not a blessed object we call the constructor
103+
# to make one.
104+
#
105+
# Because we're overloading '<=>' and 'cmp' this function supplies us with
106+
# all the comparison operators ('<' and friends, 'gt' and friends)
107+
#
108+
sub _version_cmp
109+
{
110+
my ($a, $b) = @_;
111+
112+
$b = __PACKAGE__->new($b) unless blessed($b);
113+
114+
for (my $idx = 0;; $idx++)
115+
{
116+
return 0 unless (defined $a->[$idx] && defined $b->[$idx]);
117+
return $a->[$idx] <=> $b->[$idx]
118+
if ($a->[$idx] <=> $b->[$idx]);
119+
}
120+
}
121+
122+
# Render the version number in the standard "joined by dots" notation if
123+
# interpolated into a string. Put back 'devel' if we previously turned it
124+
# into a -1.
125+
sub _stringify
126+
{
127+
my $self = shift;
128+
my @sections = @$self;
129+
if ($sections[-1] == -1)
130+
{
131+
pop @sections;
132+
$sections[-1] = "$sections[-1]devel";
133+
}
134+
return join('.', @sections);
135+
}
136+
137+
1;

0 commit comments

Comments
 (0)