When I attempt to run a command that does not exist using
IPC::Open3::Simple a postgres database handle in the script gets closed.
The following code reproduces the problem. There are two
$ipc->run lines. Comment one or the other out to see the different behaviour. There are several
$dbh->ping lines used to narrow down where the connection was being dropped.
#! /usr/bin/perl -w
use strict;
use Data::Dumper;
use DBD::Pg;
use IPC::Open3::Simple;
$| ++;
my $DB_NAME = 'db_name';
my $DB_USERNAME = 'db_username';
my $DB_PASSWORD = '12345678';
# log in to the database
my $dbh = DBI->connect("dbi:Pg:dbname=$DB_NAME",
$DB_USERNAME,
$DB_PASSWORD,
) or die $DBI::errstr;
$dbh->trace(0);
# now run a query to make sure the database connection works
my $sth;
unless ($sth = $dbh->prepare("select now()")) { warn "Unable to prepar
+e SQL."; }
unless ($sth->execute()) { warn "Unable to execute SQL."; }
$sth->finish;
print "ping " , __LINE__ , " " , $dbh->ping, "\n";
# now run a command that fails
my (@cmd_output, @err_output);
my $ipc = IPC::Open3::Simple->new(
in => sub { my $fh = shift; print $fh "mydata"; close $fh; } ,
out => sub { push @cmd_output, $_[0]; } ,
err => sub { push @err_output, $_[0]; } ,
);
print "ping " , __LINE__ , " " , $dbh->ping, "\n";
#$ipc->run('date'); # this line is fine
$ipc->run('no-such-command'); # this line causes the problem
print "ping " , __LINE__ , " " , $dbh->ping, "\n";
print Dumper("output:", \@cmd_output, "error:", \@err_output), "\n";
print "ping " , __LINE__ , " " , $dbh->ping, "\n";
# now run another database query
unless ($sth = $dbh->prepare("select now()")) { warn "Unable to prepar
+e SQL."; }
print "ping " , __LINE__ , " " , $dbh->ping, "\n";
unless ($sth->execute()) { warn "Unable to execute SQL."; }
print "ping " , __LINE__ , " " , $dbh->ping, "\n";
$sth->finish;
$dbh->disconnect() or die $DBI::errstr;
exit 0;
The successful run looks like:
ping 30 1
ping 41 1
ping 46 1
$VAR1 = 'output:';
$VAR2 = [
'Fri Apr 18 17:34:43 MDT 2008'
];
$VAR3 = 'error:';
$VAR4 = [];
ping 50 1
ping 54 1
ping 56 1
The failing run looks like:
ping 30 1
ping 41 1
ping 46 0
$VAR1 = 'output:';
$VAR2 = [];
$VAR3 = 'error:';
$VAR4 = [
'Can\'t exec "no-such-command": No such file or directory at
+ /usr/lib/perl5/5.8.8/IPC/Open3.pm line 246.',
'open3: exec of no-such-command failed at /usr/lib/perl5/sit
+e_perl/5.8.8/IPC/Open3/Simple.pm line 61'
];
ping 50 0
ping 54 0
DBD::Pg::st execute failed: no connection to the server at reproduce-c
+rash.pl line 55.
Unable to execute SQL. at reproduce-crash.pl line 55.
ping 56 0
After working on it for a while I suspect the
IPC::Open3::Simple code ends up doing something like the following:
*
fork a child process. This process is a copy of the parent (ie it is a perl process).
* The child process tries to
exec the command.
* The command does not exist so
exec fails.
* The child perl process exits and closes the database handle it copied from the parent.
I am thinking of adding code to check to see that the first argument in the command to be executed is a readable, executable file.
Any other ideas of how to not have the database connection close?
Update: I should also mention there is nothing showing up in the postgres server log file (just autovacuum entries).