File Coverage

blib/lib/Test/PostgreSQL.pm
Criterion Covered Total %
statement 86 344 25.0
branch 17 220 7.7
condition 1 44 2.2
subroutine 20 39 51.2
pod 7 10 70.0
total 131 657 19.9


line stmt bran cond sub pod time code
1             package Test::PostgreSQL;
2 11     11   723239 use 5.014;
  11         124  
3 11     11   53 use strict;
  11         20  
  11         236  
4 11     11   51 use warnings;
  11         15  
  11         346  
5 11     11   6821 use Moo;
  11         116338  
  11         45  
6 11     11   21249 use Types::Standard -all;
  11         759720  
  11         156  
7 11     11   467414 use Function::Parameters qw(:strict);
  11         38349  
  11         52  
8 11     11   10525 use Try::Tiny;
  11         13992  
  11         558  
9 11     11   1469 use DBI;
  11         15782  
  11         415  
10 11     11   61 use File::Spec;
  11         21  
  11         205  
11 11     11   8227 use File::Temp;
  11         192191  
  11         792  
12 11     11   5046 use File::Which;
  11         10060  
  11         637  
13 11     11   5114 use POSIX qw(SIGQUIT SIGKILL WNOHANG getuid setuid);
  11         64828  
  11         72  
14 11     11   19114 use User::pwent;
  11         61529  
  11         45  
15              
16             our $VERSION = '1.29';
17             our $errstr;
18              
19             # Deprecate use of %Defaults as we want to remove this package global
20 11     11   5983 use Tie::Hash::Method;
  11         91296  
  11         5738  
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   2262 method _search_paths() {
  10 50       37  
  10         20  
  10         18  
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         845 (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       166 if (defined $ENV{POSTGRES_HOME}) {
80 0         0 return [$ENV{POSTGRES_HOME}, @base_paths];
81             }
82 10         242 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 () {
120             my $dir = File::Spec->catdir( $self->base_dir, 'tmp' );
121              
122             # This magic number is based on the Unixy systems limit for
123             # Unix socket path lengths: on Linuxes it is at 108 characters,
124             # MacOS allows no more than 103. Considering that we are
125             # generating a directory path, and the actual socket file name
126             # will be appended to it, 80 characters seemed a safe assumption.
127             if (length $dir > 80) {
128             $self->{_socket_dir} = File::Temp->newdir(
129             'pgtest_sock.XXXXX',
130            
131             # base_dir is preserved if environment variable
132             # TEST_POSTGRESQL_PRESERVE is truthy; this is done
133             # for diagnostics. Socket directory should always
134             # be cleaned up since there is nothing of interest there.
135             CLEANUP => 1,
136             TMPDIR => 1,
137              
138             # TODO Come up with something better if this ever cause
139             # any trouble. Straightforward approach with using File::Temp
140             # that consults $TMPDIR environment variable is exactly
141             # the cause for the Unix socket path being too long;
142             # other approaches at sniffing what system we're running on
143             # and what temporary paths are available also fail at various
144             # edge cases, which is exactly the reason File::Temp was created
145             # in the first place. So, no easy answer here; simply falling back
146             # to the timeless default.
147             DIR => '/tmp',
148             );
149              
150             $dir = $self->{_socket_dir}->dirname;
151             }
152              
153             return $dir;
154             },
155             );
156              
157             has initdb => (
158             is => "ro",
159             isa => Str,
160             lazy => 1,
161             default => method () { $self->_find_program('initdb') || die $errstr },
162             );
163              
164             has initdb_args => (
165             is => "lazy",
166             isa => Str,
167             );
168              
169 0 0   0   0 method _build_initdb_args() {
  0 0       0  
  0         0  
  0         0  
170 0         0 return '-U '. $self->dbowner . ' -A trust ' . $self->extra_initdb_args;
171             }
172              
173             has extra_initdb_args => (
174             is => "ro",
175             isa => Str,
176             default => "",
177             );
178              
179             has unix_socket => (
180             is => "ro",
181             isa => Bool,
182             default => 0,
183             );
184              
185             has pg_version => (
186             is => 'ro',
187             isa => Str,
188             lazy => 1,
189             predicate => 1,
190             builder => "_pg_version_builder",
191             );
192              
193 0 0   0   0 method _pg_version_builder() {
  0 0       0  
  0         0  
  0         0  
194 0         0 my $ver_cmd = join ' ', (
195             $self->postmaster,
196             '--version'
197             );
198            
199 0         0 my ($ver) = qx{$ver_cmd} =~ /(\d+(?:\.\d+)?)/;
200            
201 0         0 return $ver;
202             }
203              
204             has pg_ctl => (
205             is => "ro",
206             isa => Maybe[Str],
207             lazy => 1,
208             builder => "_pg_ctl_builder",
209             );
210              
211 10 50   10   136 method _pg_ctl_builder() {
  10 50       36  
  10         19  
  10         17  
212 10         46 my $prog = $self->_find_program('pg_ctl');
213 10 50       31 if ( $prog ) {
214             # we only use pg_ctl if Pg version is >= 9
215 0         0 my $ret = qx/"$prog" --version/;
216 0 0 0     0 if ( $ret =~ /(\d+)(?:\.|devel|beta)/ && $1 >= 9 ) {
217 0         0 return $prog;
218             }
219 0         0 warn "pg_ctl version earlier than 9";
220 0         0 return;
221             }
222 10         305 return;
223             }
224              
225             has pg_config => (
226             is => 'ro',
227             isa => Str,
228             );
229              
230             has psql => (
231             is => 'ro',
232             isa => Str,
233             lazy => 1,
234             default => method () { $self->_find_program('psql') || die $errstr },
235             );
236              
237             has psql_args => (
238             is => 'lazy',
239             isa => Str,
240             );
241              
242 0 0   0   0 method _build_psql_args() {
  0 0       0  
  0         0  
  0         0  
243 0 0       0 return '-U ' . $self->dbowner . ' -d ' . $self->dbname . ' -h '.
244             ($self->unix_socket ? $self->socket_dir : '127.0.0.1') .
245             ' -p ' . $self->port
246             . $self->extra_psql_args;
247             }
248              
249             has extra_psql_args => (
250             is => 'ro',
251             isa => Str,
252             default => '',
253             );
254              
255             has run_psql_args => (
256             is => 'ro',
257             isa => Str,
258             lazy => 1,
259             builder => "_build_run_psql_args",
260             );
261              
262 0 0   0   0 method _build_run_psql_args() {
  0 0       0  
  0         0  
  0         0  
263 0         0 my @args = (
264             '-1', # Single transaction
265             '-X', # Ignore .psqlrc
266             '-q', # Quiet
267             '-v ON_ERROR_STOP=1', # Stop on first error
268             );
269            
270             # Echo errors, available in psql 9.5+
271 0 0       0 push @args, '-b' if $self->pg_version >= 9.5;
272            
273 0         0 return join ' ', @args;
274             }
275              
276             has seed_scripts => (
277             is => 'ro',
278             isa => ArrayRef[Str],
279             default => sub { [] },
280             );
281              
282             has pid => (
283             is => "rw",
284             isa => Maybe[Int],
285             );
286              
287             has port => (
288             is => "rw",
289             isa => Maybe[Int],
290             );
291              
292             has uid => (
293             is => "rw",
294             isa => Maybe[Int],
295             );
296              
297             # Are we running as root? (Typical when run inside Docker containers)
298             has is_root => (
299             is => "ro",
300             isa => Bool,
301             default => sub { getuid == 0 }
302             );
303              
304             has postmaster => (
305             is => "rw",
306             isa => Str,
307             lazy => 1,
308             default => method () {
309             $self->_find_program("postgres")
310             || $self->_find_program("postmaster")
311             || die $errstr
312             },
313             );
314              
315             has postmaster_args => (
316             is => "lazy",
317             isa => Str,
318             );
319              
320 0 0   0   0 method _build_postmaster_args() {
  0 0       0  
  0         0  
  0         0  
321 0 0       0 return "-h ".
322             ($self->unix_socket ? "''" : "127.0.0.1") .
323             " -F " . $self->extra_postmaster_args;
324             }
325              
326             has extra_postmaster_args => (
327             is => "ro",
328             isa => Str,
329             default => "",
330             );
331              
332             has _owner_pid => (
333             is => "ro",
334             isa => Int,
335             default => sub { $$ },
336             );
337              
338 10 50   10 0 492 method BUILD($) {
  10 50       52  
  10         24  
  10         23  
  10         21  
339             # Ensure we have one or the other ways of starting Postgres:
340 10 50   10   109 try { $self->pg_ctl or $self->postmaster } catch { die $_ };
  10         770  
  10         257  
341              
342 0 0 0     0 if (defined $self->uid and $self->uid == 0) {
343 0         0 die "uid() must be set to a non-root user id.";
344             }
345              
346 0 0 0     0 if (not defined($self->uid) and $self->is_root) {
347 0         0 my $ent = getpwnam("nobody");
348 0 0       0 unless (defined $ent) {
349 0         0 die "user nobody does not exist, use uid() to specify a non-root user.";
350             }
351 0 0       0 unless ($ent->uid > 0) {
352 0         0 die "user nobody has uid 0; confused and exiting. use uid() to specify a non-root user.";
353             }
354 0         0 $self->uid($ent->uid);
355             }
356              
357             # Ensure base dir is writable by our target uid, if we were running as root
358 0 0       0 chown $self->uid, -1, $self->base_dir
359             if defined $self->uid;
360              
361 0 0       0 if ($self->auto_start) {
362 0 0       0 $self->setup
363             if $self->auto_start >= 2;
364 0         0 $self->start;
365             }
366             }
367              
368 10 50   10 0 11906 method DEMOLISH($in_global_destruction) {
  10 50       37  
  10         23  
  10         26  
  10         18  
369 10         35 local $?;
370 10 50 33     181 if (defined $self->pid && $self->_owner_pid == $$) {
371 0         0 $self->stop
372             }
373 10         220 undef $self->{_socket_dir};
374 10         174 return;
375             }
376              
377             sub dsn {
378 0     0 1 0 my %args = shift->_default_args(@_);
379              
380 0         0 return 'DBI:Pg:' . join(';', map { "$_=$args{$_}" } sort keys %args);
  0         0  
381             }
382              
383             sub _default_args {
384 0     0   0 my ($self, %args) = @_;
385             # If we're doing socket-only (i.e., not listening on localhost),
386             # then provide the path to the socket
387 0 0       0 if ($self->{unix_socket}) {
388 0   0     0 $args{host} //= $self->socket_dir;
389             } else {
390 0   0     0 $args{host} ||= $self->host;
391             }
392              
393 0   0     0 $args{port} ||= $self->port;
394 0   0     0 $args{user} ||= $self->dbowner;
395 0   0     0 $args{dbname} ||= $self->dbname;
396 0         0 return %args;
397             }
398              
399             sub uri {
400 0     0 1 0 my $self = shift;
401 0         0 my %args = $self->_default_args(@_);
402              
403 0         0 return sprintf('postgresql://%s@%s:%d/%s', @args{qw/user host port dbname/});
404             }
405              
406 0 0   0 1 0 method start() {
  0 0       0  
  0         0  
  0         0  
407 0 0       0 if (defined $self->pid) {
408 0         0 warn "Apparently already started on " . $self->pid . "; not restarting.";
409 0         0 return;
410             }
411              
412             # If the user specified a port, try only that port:
413 0 0       0 if ($self->port) {
414 0         0 $self->_try_start($self->port);
415             }
416             else {
417 0         0 $self->_find_port_and_launch;
418             }
419              
420             # create "test" database
421 0         0 $self->_create_test_database($self->dbname);
422             }
423              
424             # This whole method was mostly cargo-culted from the earlier test-postgresql;
425             # It could probably be made more sane.
426 0 0   0   0 method _find_port_and_launch() {
  0 0       0  
  0         0  
  0         0  
427 0         0 my $tries = 10;
428              
429 0         0 srand(); # Re-seed the RNG in case the caller forked the process
430              
431             # There is a significant chance that there might be more than
432             # one Test::Postgresql test in flight so we better randomize
433             # the port before even trying to start the first time. Does
434             # no harm if there is no concurrent Postgres running; if there is
435             # then our process will start up a bit faster.
436 0         0 my $port = $self->base_port + int(rand(10)) + 1;
437              
438             # try by incrementing port number until PostgreSQL starts
439 0         0 while (1) {
440 0         0 my $good;
441             try {
442             #warn "Trying to start postgres on port $port...";
443 0     0   0 $self->_try_start($port);
444 0         0 $good = 1;
445             }
446             catch {
447             #warn "Postgres failed to start on port $port\n";
448 0 0   0   0 unless ($tries--) {
449 0         0 die "Failed to start postgres after trying 10 potential ports: $_";
450             }
451 0         0 undef;
452 0         0 };
453 0 0       0 return if $good;
454             # Increment port by a random number to avoid clashes with other Test::Postgresql processes
455             # Keep in mind that this increment is going to be made up to 10 times, so avoid exceeding 64k
456 0         0 $port += int(rand(500)) + 1;
457             }
458             }
459              
460 0 0   0   0 method _try_start($port) {
  0 0       0  
  0         0  
  0         0  
  0         0  
461 0         0 my $logfile = File::Spec->catfile($self->base_dir, 'postgres.log');
462              
463 0 0       0 if ( $self->pg_ctl ) {
464 0         0 my @cmd = (
465             $self->pg_ctl,
466             'start', '-w', '-s', '-D',
467             File::Spec->catdir( $self->base_dir, 'data' ),
468             '-l', $logfile, '-o',
469             join( ' ',
470             $self->postmaster_args, '-p',
471             $port, '-k',
472             $self->socket_dir)
473             );
474             #warn "Postgres starting command: " . join(' ', @cmd) . "\n";
475 0         0 $self->setuid_cmd(\@cmd, 1);
476              
477 0         0 my $pid_path = File::Spec->catfile( $self->base_dir, 'data', 'postmaster.pid' );
478              
479 0         0 my $pidfh;
480 0 0       0 if (not open($pidfh, '<', $pid_path)) {
481             #open( my $logh, '<', $logfile );
482             #my @loglines = <$logh>;
483             #warn "Postgres log: \n" . join("\n", @loglines);
484 0         0 die "Failed to open $pid_path: $!";
485             }
486              
487             # Note that the file contains several lines; we only want the PID from the first.
488 0         0 my $pid = <$pidfh>;
489 0         0 chomp $pid;
490 0         0 $self->pid($pid);
491 0         0 close $pidfh;
492              
493 0         0 $self->port($port);
494             }
495             else {
496             # old style - open log and fork
497 0 0       0 open my $logfh, '>>', $logfile
498             or die "failed to create log file: $logfile: $!";
499 0         0 my $pid = fork;
500 0 0       0 die "fork(2) failed:$!"
501             unless defined $pid;
502 0 0       0 if ($pid == 0) {
503 0 0       0 open STDOUT, '>>&', $logfh
504             or die "dup(2) failed:$!";
505 0 0       0 open STDERR, '>>&', $logfh
506             or die "dup(2) failed:$!";
507 0 0       0 chdir $self->base_dir
508             or die "failed to chdir to:" . $self->base_dir . ":$!";
509 0 0       0 if (defined $self->uid) {
510 0 0       0 setuid($self->uid) or die "setuid failed: $!";
511             }
512 0         0 my $cmd = join(
513             ' ',
514             $self->postmaster,
515             $self->postmaster_args,
516             '-p', $port,
517             '-D', File::Spec->catdir($self->base_dir, 'data'),
518             '-k', $self->socket_dir,
519             );
520 0         0 exec($cmd);
521 0         0 die "failed to launch postmaster:$?";
522             }
523 0         0 close $logfh;
524             # wait until server becomes ready (or dies)
525 0         0 for (my $i = 0; $i < 100; $i++) {
526 0 0       0 open $logfh, '<', $logfile
527             or die "failed to create log file: $logfile: $!";
528 0         0 my $lines = do { join '', <$logfh> };
  0         0  
529 0         0 close $logfh;
530             last
531 0 0       0 if $lines =~ /is ready to accept connections/;
532 0 0       0 if (waitpid($pid, WNOHANG) > 0) {
533             # failed
534 0         0 die "Failed to start Postgres: $lines\n";
535             }
536 0         0 sleep 1;
537             }
538             # PostgreSQL is ready
539 0         0 $self->pid($pid);
540 0         0 $self->port($port);
541             }
542 0         0 return;
543             }
544              
545 0 0   0 1 0 method stop($sig = SIGQUIT) {
  0 0       0  
  0 0       0  
  0         0  
  0         0  
546 0 0 0     0 if ( $self->pg_ctl && defined $self->base_dir ) {
547 0         0 my @cmd = (
548             $self->pg_ctl, 'stop', '-s', '-D',
549             File::Spec->catdir( $self->base_dir, 'data' ),
550             '-m', 'fast'
551             );
552 0         0 $self->setuid_cmd(\@cmd);
553             }
554             else {
555             # old style or $self->base_dir File::Temp obj already DESTROYed
556 0 0       0 return unless defined $self->pid;
557              
558 0         0 kill $sig, $self->pid;
559 0         0 my $timeout = 10;
560 0   0     0 while ($timeout > 0 and waitpid($self->pid, WNOHANG) == 0) {
561 0         0 $timeout -= sleep(1);
562             }
563              
564 0 0       0 if ($timeout <= 0) {
565 0         0 warn "Pg refused to die gracefully; killing it violently.\n";
566 0         0 kill SIGKILL, $self->pid;
567 0         0 $timeout = 5;
568 0   0     0 while ($timeout > 0 and waitpid($self->pid, WNOHANG) == 0) {
569 0         0 $timeout -= sleep(1);
570             }
571 0 0       0 if ($timeout <= 0) {
572 0         0 warn "Pg really didn't die.. WTF?\n";
573             }
574             }
575             }
576 0         0 $self->pid(undef);
577 0         0 return;
578             }
579              
580 0 0   0   0 method _create_test_database($dbname) {
  0 0       0  
  0         0  
  0         0  
  0         0  
581 0         0 my $tries = 5;
582 0         0 my $dbh;
583 0         0 while ($tries) {
584 0         0 $tries -= 1;
585 0         0 $dbh = DBI->connect($self->dsn(dbname => 'template1'), '', '', {
586             PrintError => 0,
587             RaiseError => 0
588             });
589 0 0       0 last if $dbh;
590              
591             # waiting for database to start up
592 0 0 0     0 if ($DBI::errstr =~ /the database system is starting up/
593             || $DBI::errstr =~ /Connection refused/) {
594 0         0 sleep(1);
595 0         0 next;
596             }
597 0         0 die $DBI::errstr;
598             }
599              
600 0 0       0 die "Connection to the database failed even after 5 tries"
601             unless ($dbh);
602              
603 0 0       0 if ($dbh->selectrow_arrayref(qq{SELECT COUNT(*) FROM pg_database WHERE datname='$dbname'})->[0] == 0) {
604 0 0       0 $dbh->do("CREATE DATABASE $dbname")
605             or die $dbh->errstr;
606             }
607              
608 0   0     0 my $seed_scripts = $self->seed_scripts || [];
609            
610 0 0       0 $self->run_psql_scripts(@$seed_scripts)
611             if @$seed_scripts;
612            
613 0         0 return;
614             }
615              
616 0 0   0 1 0 method setup() {
  0 0       0  
  0         0  
  0         0  
617             # (re)create directory structure
618 0         0 mkdir $self->base_dir;
619 0 0       0 chmod 0755, $self->base_dir
620             or die "failed to chmod 0755 dir:" . $self->base_dir . ":$!";
621 0 0 0     0 if ($ENV{USER} && $ENV{USER} eq 'root') {
622 0 0       0 chown $self->uid, -1, $self->base_dir
623             or die "failed to chown dir:" . $self->base_dir . ":$!";
624             }
625 0         0 my $tmpdir = $self->socket_dir;
626 0 0       0 if (mkdir $tmpdir) {
627 0 0       0 if ($self->uid) {
628 0 0       0 chown $self->uid, -1, $tmpdir
629             or die "failed to chown dir:$tmpdir:$!";
630             }
631             }
632             # initdb
633 0 0       0 if (! -d File::Spec->catdir($self->base_dir, 'data')) {
634 0 0       0 if ( $self->pg_ctl ) {
635 0         0 my @cmd = (
636             $self->pg_ctl,
637             'init',
638             '-s',
639             '-D', File::Spec->catdir($self->base_dir, 'data'),
640             '-o',
641             $self->initdb_args,
642             );
643 0         0 $self->setuid_cmd(\@cmd);
644             }
645             else {
646             # old style
647 0 0       0 pipe my $rfh, my $wfh
648             or die "failed to create pipe:$!";
649 0         0 my $pid = fork;
650 0 0       0 die "fork failed:$!"
651             unless defined $pid;
652 0 0       0 if ($pid == 0) {
653 0         0 close $rfh;
654 0 0       0 open STDOUT, '>&', $wfh
655             or die "dup(2) failed:$!";
656 0 0       0 open STDERR, '>&', $wfh
657             or die "dup(2) failed:$!";
658 0 0       0 chdir $self->base_dir
659             or die "failed to chdir to:" . $self->base_dir . ":$!";
660 0 0       0 if (defined $self->uid) {
661 0 0       0 setuid($self->uid)
662             or die "setuid failed:$!";
663             }
664 0         0 my $cmd = join(
665             ' ',
666             $self->initdb,
667             $self->initdb_args,
668             '-D', File::Spec->catdir($self->base_dir, 'data'),
669             );
670 0         0 exec($cmd);
671 0         0 die "failed to exec:$cmd:$!";
672             }
673 0         0 close $wfh;
674 0         0 my $output = '';
675 0         0 while (my $l = <$rfh>) {
676 0         0 $output .= $l;
677             }
678 0         0 close $rfh;
679 0         0 while (waitpid($pid, 0) <= 0) {
680             }
681 0 0       0 die "*** initdb failed ***\n$output\n"
682             if $? != 0;
683              
684             }
685            
686 0         0 my $conf_file
687             = File::Spec->catfile($self->base_dir, 'data', 'postgresql.conf');
688            
689 0 0       0 if (my $pg_config = $self->pg_config) {
690 0 0       0 open my $fh, '>', $conf_file or die "Can't open $conf_file: $!";
691 0         0 print $fh $pg_config;
692 0         0 close $fh;
693             }
694             else {
695             # use postgres hard-coded configuration as some packagers mess
696             # around with postgresql.conf.sample too much:
697 0         0 truncate $conf_file, 0;
698             }
699             }
700             }
701              
702 30 50   30   86 method _find_program($prog) {
  30 50       65  
  30         45  
  30         80  
  30         40  
703 30         54 undef $errstr;
704 30         102 my $path = which $prog;
705 30 50       8354 return $path if $path;
706 30         48 for my $sp (@{$self->search_paths}) {
  30         187  
707 60 50       524 return "$sp/bin/$prog" if -x "$sp/bin/$prog";
708 60 50       4759 return "$sp/$prog" if -x "$sp/$prog";
709             }
710 30         129 $errstr = "could not find $prog, please set appropriate PATH or POSTGRES_HOME";
711 30         309 return;
712             }
713              
714 0 0   0 0   method setuid_cmd($cmd, $suppress_errors = !1) {
  0 0          
  0 0          
  0            
  0            
715 0           my $pid = fork;
716 0 0         if ($pid == 0) {
717 0           chdir $self->base_dir;
718 0 0         if (defined $self->uid) {
719 0 0         setuid($self->uid) or die "setuid failed: $!";
720             }
721 0 0         close STDERR if $suppress_errors;
722 0 0         exec(@$cmd) or die "Failed to exec pg_ctl: $!";
723             }
724             else {
725 0           waitpid($pid, 0);
726             }
727             }
728              
729 0 0   0 1   method run_psql(@psql_args) {
  0            
  0            
  0            
730 0           my $cmd = join ' ', (
731             $self->psql,
732            
733             # Default connection settings
734             $self->psql_args,
735            
736             # Extra connection settings or something else
737             $self->extra_psql_args,
738            
739             # run_psql specific arguments
740             $self->run_psql_args,
741            
742             @psql_args,
743             );
744            
745             # Usually anything less than WARNING is not really helpful
746             # in batch mode. Does it make sense to make this configurable?
747 0           local $ENV{PGOPTIONS} = '--client-min-messages=warning';
748            
749 0           my $psql_out = qx{$cmd 2>&1};
750            
751 0 0         die "Error executing psql: $psql_out" unless $? == 0;
752             }
753              
754 0 0   0 1   method run_psql_scripts(@script_paths) {
  0            
  0            
  0            
755 0           my @psql_commands;
756            
757             # psql 9.6+ supports multiple -c and -f commands invoked at once,
758             # older psql does not. Executing psql multiple times breaks single
759             # transaction semantics but is unlikely to cause problems in real world.
760 0 0         if ( $self->pg_version > 9.6 ) {
761 0           push @psql_commands, join ' ', map {; "-f $_" } @script_paths;
  0            
762             }
763             else {
764 0           @psql_commands = map {; "-f $_" } @script_paths;
  0            
765             }
766            
767 0           $self->run_psql($_) for @psql_commands;
768             }
769              
770             1;
771             __END__