diff --git a/Changes b/Changes index fc48f64..c99296d 100644 --- a/Changes +++ b/Changes @@ -1,6 +1,12 @@ Revision history for Perl extension Test::PostgreSQL. {{$NEXT}} + - Use pg_version attribute to better handle -b and -f psql switches + depending on installed PostgreSQL version + - Updated docs to reflect the changes + - Tests are passing with PostgreSQL 9.3+ + - pg_version attribute holds PostgreSQL version detected at startup + - Improved test diagnostics 1.26 - Fix postgresql.conf test on PostgreSQL prior to 9.2 diff --git a/README.md b/README.md index 95f7c41..b09da6a 100644 --- a/README.md +++ b/README.md @@ -170,7 +170,7 @@ Default is `-1Xqb -v ON_ERROR_STOP=1`. This means: - 1: Run all SQL statements in passed scripts as single transaction - X: Skip `.psqlrc` files - q: Run quietly, print only notices and errors on stderr (if any) -- b: Echo SQL statements that cause PostgreSQL exceptions +- b: Echo SQL statements that cause PostgreSQL exceptions (version 9.5+) - -v ON\_ERROR\_STOP=1: Stop processing SQL statements after the first error ## seed\_scripts @@ -178,6 +178,11 @@ Default is `-1Xqb -v ON_ERROR_STOP=1`. This means: Arrayref with the list of SQL scripts to run after the database was instanced and set up. Default is `[]`. +**NOTE** that `psql` older than 9.6 does not support multiple `-c` and `-f` +switches in arguments so `seed_scripts` will be executed one by one. This +implies multiple transactions instead of just one; if you need all seed statements +to apply within a single transaction, combine them into one seed script. + ## auto\_start Integer value that controls whether PostgreSQL server is started and setup @@ -257,10 +262,14 @@ to escape them manually like shown above. `run_psql` will not quote them for you The actual command line to execute `psql` will be concatenated from ["psql\_args"](#psql_args), ["extra\_psql\_args"](#extra_psql_args), and ["run\_psql\_args"](#run_psql_args). +**NOTE** that `psql` older than 9.6 does not support multiple `-c` and/or `-f` +switches in arguments. + ## run\_psql\_scripts Given a list of script file paths, invoke ["run\_psql"](#run_psql) once with `-f 'script'` -for every path. +for every path in PostgreSQL 9.6+, or once per `-f 'script'` for older PostgreSQL +versions. # ENVIRONMENT diff --git a/lib/Test/PostgreSQL.pm b/lib/Test/PostgreSQL.pm index 7e5b66e..abdcc8a 100644 --- a/lib/Test/PostgreSQL.pm +++ b/lib/Test/PostgreSQL.pm @@ -147,6 +147,25 @@ has unix_socket => ( default => 0, ); +has pg_version => ( + is => 'ro', + isa => Str, + lazy => 1, + predicate => 1, + builder => "_pg_version_builder", +); + +method _pg_version_builder() { + my $ver_cmd = join ' ', ( + $self->postmaster, + '--version' + ); + + my ($ver) = qx{$ver_cmd} =~ /(\d+(?:\.\d+)?)/; + + return $ver; +} + has pg_ctl => ( is => "ro", isa => Maybe[Str], @@ -201,10 +220,24 @@ has extra_psql_args => ( has run_psql_args => ( is => 'ro', isa => Str, - # Single transaction, skip .psqlrc, be quiet, echo errors, stop on first error - default => '-1Xqb -v ON_ERROR_STOP=1', + lazy => 1, + builder => "_build_run_psql_args", ); +method _build_run_psql_args() { + my @args = ( + '-1', # Single transaction + '-X', # Ignore .psqlrc + '-q', # Quiet + '-v ON_ERROR_STOP=1', # Stop on first error + ); + + # Echo errors, available in psql 9.5+ + push @args, '-b' if $self->pg_version >= 9.5; + + return join ' ', @args; +} + has seed_scripts => ( is => 'ro', isa => ArrayRef[Str], @@ -613,12 +646,12 @@ method setup() { method _find_program($prog) { undef $errstr; - my $path = which $prog; - return $path if $path; for my $sp (@{$self->search_paths}) { return "$sp/bin/$prog" if -x "$sp/bin/$prog"; return "$sp/$prog" if -x "$sp/$prog"; } + my $path = which $prog; + return $path if $path; $errstr = "could not find $prog, please set appropriate PATH or POSTGRES_HOME"; return; } @@ -664,9 +697,19 @@ method run_psql(@psql_args) { } method run_psql_scripts(@script_paths) { - my $psql_args = join ' ', map {; "-f $_" } @script_paths; + my @psql_commands; - $self->run_psql($psql_args); + # psql 9.6+ supports multiple -c and -f commands invoked at once, + # older psql does not. Executing psql multiple times breaks single + # transaction semantics but is unlikely to cause problems in real world. + if ( $self->pg_version > 9.6 ) { + push @psql_commands, join ' ', map {; "-f $_" } @script_paths; + } + else { + @psql_commands = map {; "-f $_" } @script_paths; + } + + $self->run_psql($_) for @psql_commands; } 1; @@ -860,7 +903,7 @@ q: Run quietly, print only notices and errors on stderr (if any) =item * -b: Echo SQL statements that cause PostgreSQL exceptions +b: Echo SQL statements that cause PostgreSQL exceptions (version 9.5+) =item * @@ -873,6 +916,11 @@ b: Echo SQL statements that cause PostgreSQL exceptions Arrayref with the list of SQL scripts to run after the database was instanced and set up. Default is C<[]>. +B that C older than 9.6 does not support multiple C<-c> and C<-f> +switches in arguments so C will be executed one by one. This +implies multiple transactions instead of just one; if you need all seed statements +to apply within a single transaction, combine them into one seed script. + =head2 auto_start Integer value that controls whether PostgreSQL server is started and setup @@ -956,10 +1004,14 @@ to escape them manually like shown above. C will not quote them for yo The actual command line to execute C will be concatenated from L, L, and L. +B that C older than 9.6 does not support multiple C<-c> and/or C<-f> +switches in arguments. + =head2 run_psql_scripts Given a list of script file paths, invoke L once with C<-f 'script'> -for every path. +for every path in PostgreSQL 9.6+, or once per C<-f 'script'> for older PostgreSQL +versions. =head1 ENVIRONMENT diff --git a/t/01-raii.t b/t/01-raii.t index 621e5ee..e7806f3 100644 --- a/t/01-raii.t +++ b/t/01-raii.t @@ -9,6 +9,22 @@ use Try::Tiny; my $pgsql = try { Test::PostgreSQL->new } catch { plan skip_all => $_ }; +plan tests => 5; + +my $version_cmd = join ' ', ( + $pgsql->postmaster, '--version' +); + +my $version_str = qx{$version_cmd}; + +my ($want_version) = $version_str =~ /(\d+(?:\.\d+)?)/; +my $have_version = $pgsql->pg_version; + +is $have_version, $want_version, "pg_version"; + +# diag() here is deliberate, to show Postgres version in smoker reports +diag "PostgreSQL version: $want_version"; + my $dsn = $pgsql->dsn; is( @@ -22,12 +38,10 @@ ok($dbh->ping, 'connected to PostgreSQL'); undef $dbh; my $uri = $pgsql->uri; -like($uri, qr/^postgresql:\/\/postgres\@127.0.0.1/); +like($uri, qr/^postgresql:\/\/postgres\@127.0.0.1/, "uri"); undef $pgsql; ok( ! DBI->connect($dsn, undef, undef, { PrintError => 0 }), "Removing variable causes shutdown of postgresql" ); - -done_testing; diff --git a/t/08-postgresql_conf.t b/t/08-postgresql_conf.t index 5d96e67..1bf98f8 100644 --- a/t/08-postgresql_conf.t +++ b/t/08-postgresql_conf.t @@ -19,16 +19,11 @@ ok defined($pg), "test instance 1 created"; my $datadir = File::Spec->catfile($pg->base_dir, 'data'); my $conf_file = File::Spec->catfile($datadir, 'postgresql.conf'); -my $ver_cmd = join' ', ( - $pg->postmaster, - '--version' -); - -my ($ver) = qx{$ver_cmd} =~ /(\d+(?:\.\d+)?)/; - # By default postgresql.conf is truncated is -s $conf_file, 0, "test 1 postgresql.conf size 0"; +my $ver = $pg->pg_version; + SKIP: { skip "No -C switch on PostgreSQL $ver (9.2 required)", 1 if $ver < 9.2; skip "Can't run postgres as root", 1 if (getuid == 0); diff --git a/t/09-run_psql.t b/t/09-run_psql.t index b54d2e4..6643c1b 100644 --- a/t/09-run_psql.t +++ b/t/09-run_psql.t @@ -14,13 +14,22 @@ plan tests => 3; ok defined($pg), "new instance created"; -eval { - $pg->run_psql( +my @psql_command; + +# psql 9.6+ supports multiple -c commands +if ( $pg->pg_version >= 9.6 ) { + @psql_command = ( '-c', q|'CREATE TABLE foo (bar int)'|, '-c', q|'INSERT INTO foo (bar) VALUES (42)'|, - ) -}; - + ); +} +else { + @psql_command = ( + '-c', q|'CREATE TABLE foo (bar int); INSERT INTO foo (bar) VALUES (42);'|, + ); +} + +eval { $pg->run_psql(@psql_command) }; is $@, '', "run_psql no exception" . ($@ ? ": $@" : "");