File Coverage

blib/lib/Test/PostgreSQL.pm
Criterion Covered Total %
statement 85 340 25.0
branch 17 220 7.7
condition 1 44 2.2
subroutine 20 39 51.2
pod 7 10 70.0
total 130 653 19.9


line stmt bran cond sub pod time code
1             package Test::PostgreSQL;
2 11     11   847047 use 5.014;
  11         188  
3 11     11   60 use strict;
  11         23  
  11         229  
4 11     11   85 use warnings;
  11         23  
  11         346  
5 11     11   6549 use Moo;
  11         132035  
  11         54  
6 11     11   24232 use Types::Standard -all;
  11         860281  
  11         120  
7 11     11   550252 use Function::Parameters qw(:strict);
  11         43794  
  11         58  
8 11     11   10785 use Try::Tiny;
  11         15996  
  11         661  
9 11     11   1687 use DBI;
  11         18266  
  11         426  
10 11     11   71 use File::Spec;
  11         44  
  11         270  
11 11     11   8830 use File::Temp;
  11         225860  
  11         944  
12 11     11   5374 use File::Which;
  11         12118  
  11         674  
13 11     11   6074 use POSIX qw(SIGQUIT SIGKILL WNOHANG getuid setuid);
  11         75362  
  11         79  
14 11     11   23110 use User::pwent;
  11         72228  
  11         48  
15              
16             our $VERSION = '1.28';
17             our $errstr;
18              
19             # Deprecate use of %Defaults as we want to remove this package global
20 11     11   7068 use Tie::Hash::Method;
  11         102538  
  11         6599  
21             tie our %Defaults, 'Tie::Hash::Method', FETCH => sub {
22             my $msg = "\nWARNING: using \$Test::PostgreSQL::Defaults is DEPRECATED.";
23             if ( $_[1] =~ /^(initdb|postmaster)_args$/ ) {
24             $msg .= " Use Test::PostgreSQL->new( extra_$_[1] => ... ) instead.";
25             }
26             warn $msg;
27             return $_[0]->base_hash->{ $_[1] };
28             };
29              
30             %Defaults = (
31             auto_start => 2,
32             initdb_args => '-U postgres -A trust',
33             postmaster_args => '-h 127.0.0.1 -F',
34             );
35              
36             has dbname => (
37             is => 'ro',
38             isa => Str,
39             default => 'test',
40             );
41              
42             has dbowner => (
43             is => 'ro',
44             isa => Str,
45             default => 'postgres',
46             );
47              
48             has host => (
49             is => 'ro',
50             isa => Str,
51             default => '127.0.0.1',
52             );
53              
54             # Various paths that Postgres gets installed under, sometimes with a version on the end,
55             # in which case take the highest version. We append /bin/ and so forth to the path later.
56             # *Note that these are used only if the program isn't already in the path!*
57             has search_paths => (
58             is => "ro",
59             isa => ArrayRef,
60             builder => "_search_paths",
61             );
62              
63 10 50   10   2462 method _search_paths() {
  10 50       44  
  10         25  
  10         21  
64             my @base_paths = (
65             # popular installation dir?
66             qw(/usr/local/pgsql),
67             # ubuntu (maybe debian as well, find the newest version)
68 0         0 (sort { $b cmp $a } grep { -d $_ } glob "/usr/lib/postgresql/*"),
  0         0  
69             # macport
70 0         0 (sort { $b cmp $a } grep { -d $_ } glob "/opt/local/lib/postgresql*"),
  0         0  
71             # Postgresapp.com
72 10         877 (sort { $b cmp $a } grep { -d $_ } glob "/Applications/Postgres.app/Contents/Versions/*"),
  0         0  
  0         0  
73             # BSDs end up with it in /usr/local/bin which doesn't appear to be in the path sometimes:
74             "/usr/local",
75             );
76              
77             # This environment variable is used to override the default, so it gets
78             # prefixed to the start of the search paths.
79 10 50       87 if (defined $ENV{POSTGRES_HOME}) {
80 0         0 return [$ENV{POSTGRES_HOME}, @base_paths];
81             }
82 10         272 return \@base_paths;
83             }
84              
85             # We attempt to use this port first, and will increment from there.
86             # The final port ends up in the ->port attribute.
87             has base_port => (
88             is => "ro",
89             isa => Int,
90             default => 15432,
91             );
92              
93             has auto_start => (
94             is => "ro",
95             default => 2,
96             );
97              
98             has base_dir => (
99             is => "rw",
100             default => sub {
101             File::Temp->newdir(
102             'pgtest.XXXXX',
103             CLEANUP => $ENV{TEST_POSTGRESQL_PRESERVE} ? undef : 1,
104             EXLOCK => 0,
105             TMPDIR => 1
106             );
107             },
108             coerce => fun ($newval) {
109             # Ensure base_dir is absolute; usually only the case if the user set it.
110             # Avoid munging objects such as File::Temp
111             ref $newval ? $newval : File::Spec->rel2abs($newval);
112             },
113             );
114              
115             has socket_dir => (
116             is => "ro",
117             isa => Str,
118             lazy => 1,
119             default => method () { File::Spec->catdir( $self->base_dir, 'tmp' ) },
120             );
121              
122             has initdb => (
123             is => "ro",
124             isa => Str,
125             lazy => 1,
126             default => method () { $self->_find_program('initdb') || die $errstr },
127             );
128              
129             has initdb_args => (
130             is => "lazy",
131             isa => Str,
132             );
133              
134 0 0   0   0 method _build_initdb_args() {
  0 0       0  
  0         0  
  0         0  
135 0         0 return '-U '. $self->dbowner . ' -A trust ' . $self->extra_initdb_args;
136             }
137              
138             has extra_initdb_args => (
139             is => "ro",
140             isa => Str,
141             default => "",
142             );
143              
144             has unix_socket => (
145             is => "ro",
146             isa => Bool,
147             default => 0,
148             );
149              
150             has pg_version => (
151             is => 'ro',
152             isa => Str,
153             lazy => 1,
154             predicate => 1,
155             builder => "_pg_version_builder",
156             );
157              
158 0 0   0   0 method _pg_version_builder() {
  0 0       0  
  0         0  
  0         0  
159 0         0 my $ver_cmd = join ' ', (
160             $self->postmaster,
161             '--version'
162             );
163            
164 0         0 my ($ver) = qx{$ver_cmd} =~ /(\d+(?:\.\d+)?)/;
165            
166 0         0 return $ver;
167             }
168              
169             has pg_ctl => (
170             is => "ro",
171             isa => Maybe[Str],
172             lazy => 1,
173             builder => "_pg_ctl_builder",
174             );
175              
176 10 50   10   163 method _pg_ctl_builder() {
  10 50       39  
  10         23  
  10         20  
177 10         50 my $prog = $self->_find_program('pg_ctl');
178 10 50       42 if ( $prog ) {
179             # we only use pg_ctl if Pg version is >= 9
180 0         0 my $ret = qx/"$prog" --version/;
181 0 0 0     0 if ( $ret =~ /(\d+)(?:\.|devel|beta)/ && $1 >= 9 ) {
182 0         0 return $prog;
183             }
184 0         0 warn "pg_ctl version earlier than 9";
185 0         0 return;
186             }
187 10         243 return;
188             }
189              
190             has pg_config => (
191             is => 'ro',
192             isa => Str,
193             );
194              
195             has psql => (
196             is => 'ro',
197             isa => Str,
198             lazy => 1,
199             default => method () { $self->_find_program('psql') || die $errstr },
200             );
201              
202             has psql_args => (
203             is => 'lazy',
204             isa => Str,
205             );
206              
207 0 0   0   0 method _build_psql_args() {
  0 0       0  
  0         0  
  0         0  
208 0 0       0 return '-U ' . $self->dbowner . ' -d ' . $self->dbname . ' -h '.
209             ($self->unix_socket ? $self->socket_dir : '127.0.0.1') .
210             ' -p ' . $self->port
211             . $self->extra_psql_args;
212             }
213              
214             has extra_psql_args => (
215             is => 'ro',
216             isa => Str,
217             default => '',
218             );
219              
220             has run_psql_args => (
221             is => 'ro',
222             isa => Str,
223             lazy => 1,
224             builder => "_build_run_psql_args",
225             );
226              
227 0 0   0   0 method _build_run_psql_args() {
  0 0       0  
  0         0  
  0         0  
228 0         0 my @args = (
229             '-1', # Single transaction
230             '-X', # Ignore .psqlrc
231             '-q', # Quiet
232             '-v ON_ERROR_STOP=1', # Stop on first error
233             );
234            
235             # Echo errors, available in psql 9.5+
236 0 0       0 push @args, '-b' if $self->pg_version >= 9.5;
237            
238 0         0 return join ' ', @args;
239             }
240              
241             has seed_scripts => (
242             is => 'ro',
243             isa => ArrayRef[Str],
244             default => sub { [] },
245             );
246              
247             has pid => (
248             is => "rw",
249             isa => Maybe[Int],
250             );
251              
252             has port => (
253             is => "rw",
254             isa => Maybe[Int],
255             );
256              
257             has uid => (
258             is => "rw",
259             isa => Maybe[Int],
260             );
261              
262             # Are we running as root? (Typical when run inside Docker containers)
263             has is_root => (
264             is => "ro",
265             isa => Bool,
266             default => sub { getuid == 0 }
267             );
268              
269             has postmaster => (
270             is => "rw",
271             isa => Str,
272             lazy => 1,
273             default => method () {
274             $self->_find_program("postgres")
275             || $self->_find_program("postmaster")
276             || die $errstr
277             },
278             );
279              
280             has postmaster_args => (
281             is => "lazy",
282             isa => Str,
283             );
284              
285 0 0   0   0 method _build_postmaster_args() {
  0 0       0  
  0         0  
  0         0  
286 0 0       0 return "-h ".
287             ($self->unix_socket ? "''" : "127.0.0.1") .
288             " -F " . $self->extra_postmaster_args;
289             }
290              
291             has extra_postmaster_args => (
292             is => "ro",
293             isa => Str,
294             default => "",
295             );
296              
297             has _owner_pid => (
298             is => "ro",
299             isa => Int,
300             default => sub { $$ },
301             );
302              
303 10 50   10 0 576 method BUILD($) {
  10 50       60  
  10         27  
  10         33  
  10         24  
304             # Ensure we have one or the other ways of starting Postgres:
305 10 50   10   109 try { $self->pg_ctl or $self->postmaster } catch { die $_ };
  10         801  
  10         250  
306              
307 0 0 0     0 if (defined $self->uid and $self->uid == 0) {
308 0         0 die "uid() must be set to a non-root user id.";
309             }
310              
311 0 0 0     0 if (not defined($self->uid) and $self->is_root) {
312 0         0 my $ent = getpwnam("nobody");
313 0 0       0 unless (defined $ent) {
314 0         0 die "user nobody does not exist, use uid() to specify a non-root user.";
315             }
316 0 0       0 unless ($ent->uid > 0) {
317 0         0 die "user nobody has uid 0; confused and exiting. use uid() to specify a non-root user.";
318             }
319 0         0 $self->uid($ent->uid);
320             }
321              
322             # Ensure base dir is writable by our target uid, if we were running as root
323 0 0       0 chown $self->uid, -1, $self->base_dir
324             if defined $self->uid;
325              
326 0 0       0 if ($self->auto_start) {
327 0 0       0 $self->setup
328             if $self->auto_start >= 2;
329 0         0 $self->start;
330             }
331             }
332              
333 10 50   10 0 12450 method DEMOLISH($in_global_destruction) {
  10 50       41  
  10         26  
  10         30  
  10         18  
334 10         42 local $?;
335 10 50 33     196 if (defined $self->pid && $self->_owner_pid == $$) {
336 0         0 $self->stop
337             }
338 10         337 return;
339             }
340              
341             sub dsn {
342 0     0 1 0 my %args = shift->_default_args(@_);
343              
344 0         0 return 'DBI:Pg:' . join(';', map { "$_=$args{$_}" } sort keys %args);
  0         0  
345             }
346              
347             sub _default_args {
348 0     0   0 my ($self, %args) = @_;
349             # If we're doing socket-only (i.e., not listening on localhost),
350             # then provide the path to the socket
351 0 0       0 if ($self->{unix_socket}) {
352 0   0     0 $args{host} //= $self->socket_dir;
353             } else {
354 0   0     0 $args{host} ||= $self->host;
355             }
356              
357 0   0     0 $args{port} ||= $self->port;
358 0   0     0 $args{user} ||= $self->dbowner;
359 0   0     0 $args{dbname} ||= $self->dbname;
360 0         0 return %args;
361             }
362              
363             sub uri {
364 0     0 1 0 my $self = shift;
365 0         0 my %args = $self->_default_args(@_);
366              
367 0         0 return sprintf('postgresql://%s@%s:%d/%s', @args{qw/user host port dbname/});
368             }
369              
370 0 0   0 1 0 method start() {
  0 0       0  
  0         0  
  0         0  
371 0 0       0 if (defined $self->pid) {
372 0         0 warn "Apparently already started on " . $self->pid . "; not restarting.";
373 0         0 return;
374             }
375              
376             # If the user specified a port, try only that port:
377 0 0       0 if ($self->port) {
378 0         0 $self->_try_start($self->port);
379             }
380             else {
381 0         0 $self->_find_port_and_launch;
382             }
383              
384             # create "test" database
385 0         0 $self->_create_test_database($self->dbname);
386             }
387              
388             # This whole method was mostly cargo-culted from the earlier test-postgresql;
389             # It could probably be made more sane.
390 0 0   0   0 method _find_port_and_launch() {
  0 0       0  
  0         0  
  0         0  
391 0         0 my $tries = 10;
392 0         0 my $port = $self->base_port;
393 0         0 srand(); # Re-seed the RNG in case the caller forked the process
394             # try by incrementing port number until PostgreSQL starts
395 0         0 while (1) {
396             my $good = try {
397 0     0   0 $self->_try_start($port);
398 0         0 1;
399             }
400             catch {
401             # warn "Postgres failed to start on port $port\n";
402 0 0   0   0 unless ($tries--) {
403 0         0 die "Failed to start postgres after trying 10 potential ports: $_";
404             }
405 0         0 undef;
406 0         0 };
407 0 0       0 return if $good;
408             # Increment port by a random number to avoid clashes with other Test::Postgresql processes
409             # Keep in mind that this increment is going to be made up to 10 times, so avoid exceeding 64k
410 0         0 $port += int(rand(500)) + 1;
411             }
412             }
413              
414 0 0   0   0 method _try_start($port) {
  0 0       0  
  0         0  
  0         0  
  0         0  
415 0         0 my $logfile = File::Spec->catfile($self->base_dir, 'postgres.log');
416              
417 0 0       0 if ( $self->pg_ctl ) {
418 0         0 my @cmd = (
419             $self->pg_ctl,
420             'start', '-w', '-s', '-D',
421             File::Spec->catdir( $self->base_dir, 'data' ),
422             '-l', $logfile, '-o',
423             join( ' ',
424             $self->postmaster_args, '-p',
425             $port, '-k',
426             $self->socket_dir)
427             );
428 0         0 $self->setuid_cmd(\@cmd, 1);
429              
430 0         0 my $pid_path = File::Spec->catfile( $self->base_dir, 'data', 'postmaster.pid' );
431              
432 0 0       0 open( my $pidfh, '<', $pid_path )
433             or die "Failed to open $pid_path: $!";
434              
435             # Note that the file contains several lines; we only want the PID from the first.
436 0         0 my $pid = <$pidfh>;
437 0         0 chomp $pid;
438 0         0 $self->pid($pid);
439 0         0 close $pidfh;
440              
441 0         0 $self->port($port);
442             }
443             else {
444             # old style - open log and fork
445 0 0       0 open my $logfh, '>>', $logfile
446             or die "failed to create log file: $logfile: $!";
447 0         0 my $pid = fork;
448 0 0       0 die "fork(2) failed:$!"
449             unless defined $pid;
450 0 0       0 if ($pid == 0) {
451 0 0       0 open STDOUT, '>>&', $logfh
452             or die "dup(2) failed:$!";
453 0 0       0 open STDERR, '>>&', $logfh
454             or die "dup(2) failed:$!";
455 0 0       0 chdir $self->base_dir
456             or die "failed to chdir to:" . $self->base_dir . ":$!";
457 0 0       0 if (defined $self->uid) {
458 0 0       0 setuid($self->uid) or die "setuid failed: $!";
459             }
460 0         0 my $cmd = join(
461             ' ',
462             $self->postmaster,
463             $self->postmaster_args,
464             '-p', $port,
465             '-D', File::Spec->catdir($self->base_dir, 'data'),
466             '-k', $self->socket_dir,
467             );
468 0         0 exec($cmd);
469 0         0 die "failed to launch postmaster:$?";
470             }
471 0         0 close $logfh;
472             # wait until server becomes ready (or dies)
473 0         0 for (my $i = 0; $i < 100; $i++) {
474 0 0       0 open $logfh, '<', $logfile
475             or die "failed to create log file: $logfile: $!";
476 0         0 my $lines = do { join '', <$logfh> };
  0         0  
477 0         0 close $logfh;
478             last
479 0 0       0 if $lines =~ /is ready to accept connections/;
480 0 0       0 if (waitpid($pid, WNOHANG) > 0) {
481             # failed
482 0         0 die "Failed to start Postgres: $lines\n";
483             }
484 0         0 sleep 1;
485             }
486             # PostgreSQL is ready
487 0         0 $self->pid($pid);
488 0         0 $self->port($port);
489             }
490 0         0 return;
491             }
492              
493 0 0   0 1 0 method stop($sig = SIGQUIT) {
  0 0       0  
  0 0       0  
  0         0  
  0         0  
494 0 0 0     0 if ( $self->pg_ctl && defined $self->base_dir ) {
495 0         0 my @cmd = (
496             $self->pg_ctl, 'stop', '-s', '-D',
497             File::Spec->catdir( $self->base_dir, 'data' ),
498             '-m', 'fast'
499             );
500 0         0 $self->setuid_cmd(\@cmd);
501             }
502             else {
503             # old style or $self->base_dir File::Temp obj already DESTROYed
504 0 0       0 return unless defined $self->pid;
505              
506 0         0 kill $sig, $self->pid;
507 0         0 my $timeout = 10;
508 0   0     0 while ($timeout > 0 and waitpid($self->pid, WNOHANG) == 0) {
509 0         0 $timeout -= sleep(1);
510             }
511              
512 0 0       0 if ($timeout <= 0) {
513 0         0 warn "Pg refused to die gracefully; killing it violently.\n";
514 0         0 kill SIGKILL, $self->pid;
515 0         0 $timeout = 5;
516 0   0     0 while ($timeout > 0 and waitpid($self->pid, WNOHANG) == 0) {
517 0         0 $timeout -= sleep(1);
518             }
519 0 0       0 if ($timeout <= 0) {
520 0         0 warn "Pg really didn't die.. WTF?\n";
521             }
522             }
523             }
524 0         0 $self->pid(undef);
525 0         0 return;
526             }
527              
528 0 0   0   0 method _create_test_database($dbname) {
  0 0       0  
  0         0  
  0         0  
  0         0  
529 0         0 my $tries = 5;
530 0         0 my $dbh;
531 0         0 while ($tries) {
532 0         0 $tries -= 1;
533 0         0 $dbh = DBI->connect($self->dsn(dbname => 'template1'), '', '', {
534             PrintError => 0,
535             RaiseError => 0
536             });
537 0 0       0 last if $dbh;
538              
539             # waiting for database to start up
540 0 0 0     0 if ($DBI::errstr =~ /the database system is starting up/
541             || $DBI::errstr =~ /Connection refused/) {
542 0         0 sleep(1);
543 0         0 next;
544             }
545 0         0 die $DBI::errstr;
546             }
547              
548 0 0       0 die "Connection to the database failed even after 5 tries"
549             unless ($dbh);
550              
551 0 0       0 if ($dbh->selectrow_arrayref(qq{SELECT COUNT(*) FROM pg_database WHERE datname='$dbname'})->[0] == 0) {
552 0 0       0 $dbh->do("CREATE DATABASE $dbname")
553             or die $dbh->errstr;
554             }
555              
556 0   0     0 my $seed_scripts = $self->seed_scripts || [];
557            
558 0 0       0 $self->run_psql_scripts(@$seed_scripts)
559             if @$seed_scripts;
560            
561 0         0 return;
562             }
563              
564 0 0   0 1 0 method setup() {
  0 0       0  
  0         0  
  0         0  
565             # (re)create directory structure
566 0         0 mkdir $self->base_dir;
567 0 0       0 chmod 0755, $self->base_dir
568             or die "failed to chmod 0755 dir:" . $self->base_dir . ":$!";
569 0 0 0     0 if ($ENV{USER} && $ENV{USER} eq 'root') {
570 0 0       0 chown $self->uid, -1, $self->base_dir
571             or die "failed to chown dir:" . $self->base_dir . ":$!";
572             }
573 0         0 my $tmpdir = $self->socket_dir;
574 0 0       0 if (mkdir $tmpdir) {
575 0 0       0 if ($self->uid) {
576 0 0       0 chown $self->uid, -1, $tmpdir
577             or die "failed to chown dir:$tmpdir:$!";
578             }
579             }
580             # initdb
581 0 0       0 if (! -d File::Spec->catdir($self->base_dir, 'data')) {
582 0 0       0 if ( $self->pg_ctl ) {
583 0         0 my @cmd = (
584             $self->pg_ctl,
585             'init',
586             '-s',
587             '-D', File::Spec->catdir($self->base_dir, 'data'),
588             '-o',
589             $self->initdb_args,
590             );
591 0         0 $self->setuid_cmd(\@cmd);
592             }
593             else {
594             # old style
595 0 0       0 pipe my $rfh, my $wfh
596             or die "failed to create pipe:$!";
597 0         0 my $pid = fork;
598 0 0       0 die "fork failed:$!"
599             unless defined $pid;
600 0 0       0 if ($pid == 0) {
601 0         0 close $rfh;
602 0 0       0 open STDOUT, '>&', $wfh
603             or die "dup(2) failed:$!";
604 0 0       0 open STDERR, '>&', $wfh
605             or die "dup(2) failed:$!";
606 0 0       0 chdir $self->base_dir
607             or die "failed to chdir to:" . $self->base_dir . ":$!";
608 0 0       0 if (defined $self->uid) {
609 0 0       0 setuid($self->uid)
610             or die "setuid failed:$!";
611             }
612 0         0 my $cmd = join(
613             ' ',
614             $self->initdb,
615             $self->initdb_args,
616             '-D', File::Spec->catdir($self->base_dir, 'data'),
617             );
618 0         0 exec($cmd);
619 0         0 die "failed to exec:$cmd:$!";
620             }
621 0         0 close $wfh;
622 0         0 my $output = '';
623 0         0 while (my $l = <$rfh>) {
624 0         0 $output .= $l;
625             }
626 0         0 close $rfh;
627 0         0 while (waitpid($pid, 0) <= 0) {
628             }
629 0 0       0 die "*** initdb failed ***\n$output\n"
630             if $? != 0;
631              
632             }
633            
634 0         0 my $conf_file
635             = File::Spec->catfile($self->base_dir, 'data', 'postgresql.conf');
636            
637 0 0       0 if (my $pg_config = $self->pg_config) {
638 0 0       0 open my $fh, '>', $conf_file or die "Can't open $conf_file: $!";
639 0         0 print $fh $pg_config;
640 0         0 close $fh;
641             }
642             else {
643             # use postgres hard-coded configuration as some packagers mess
644             # around with postgresql.conf.sample too much:
645 0         0 truncate $conf_file, 0;
646             }
647             }
648             }
649              
650 30 50   30   86 method _find_program($prog) {
  30 50       75  
  30         52  
  30         68  
  30         45  
651 30         58 undef $errstr;
652 30         123 my $path = which $prog;
653 30 50       6686 return $path if $path;
654 30         58 for my $sp (@{$self->search_paths}) {
  30         180  
655 60 50       597 return "$sp/bin/$prog" if -x "$sp/bin/$prog";
656 60 50       669 return "$sp/$prog" if -x "$sp/$prog";
657             }
658 30         166 $errstr = "could not find $prog, please set appropriate PATH or POSTGRES_HOME";
659 30         299 return;
660             }
661              
662 0 0   0 0   method setuid_cmd($cmd, $suppress_errors = !1) {
  0 0          
  0 0          
  0            
  0            
663 0           my $pid = fork;
664 0 0         if ($pid == 0) {
665 0           chdir $self->base_dir;
666 0 0         if (defined $self->uid) {
667 0 0         setuid($self->uid) or die "setuid failed: $!";
668             }
669 0 0         close STDERR if $suppress_errors;
670 0 0         exec(@$cmd) or die "Failed to exec pg_ctl: $!";
671             }
672             else {
673 0           waitpid($pid, 0);
674             }
675             }
676              
677 0 0   0 1   method run_psql(@psql_args) {
  0            
  0            
  0            
678 0           my $cmd = join ' ', (
679             $self->psql,
680            
681             # Default connection settings
682             $self->psql_args,
683            
684             # Extra connection settings or something else
685             $self->extra_psql_args,
686            
687             # run_psql specific arguments
688             $self->run_psql_args,
689            
690             @psql_args,
691             );
692            
693             # Usually anything less than WARNING is not really helpful
694             # in batch mode. Does it make sense to make this configurable?
695 0           local $ENV{PGOPTIONS} = '--client-min-messages=warning';
696            
697 0           my $psql_out = qx{$cmd 2>&1};
698            
699 0 0         die "Error executing psql: $psql_out" unless $? == 0;
700             }
701              
702 0 0   0 1   method run_psql_scripts(@script_paths) {
  0            
  0            
  0            
703 0           my @psql_commands;
704            
705             # psql 9.6+ supports multiple -c and -f commands invoked at once,
706             # older psql does not. Executing psql multiple times breaks single
707             # transaction semantics but is unlikely to cause problems in real world.
708 0 0         if ( $self->pg_version > 9.6 ) {
709 0           push @psql_commands, join ' ', map {; "-f $_" } @script_paths;
  0            
710             }
711             else {
712 0           @psql_commands = map {; "-f $_" } @script_paths;
  0            
713             }
714            
715 0           $self->run_psql($_) for @psql_commands;
716             }
717              
718             1;
719             __END__