Committed by David Dick <[email protected]>
Subject: [DBD::Pg 1/2] Add support for AutoInactiveDestroy. Committer note:
Per RT #68893. Changed pg_server_prepare from 1 to 0 in the tests.
---
Pg.pm | 7 ++++++
dbdimp.c | 10 ++++++++
t/02attribs.t | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 92 insertions(+), 1 deletion(-)
diff --git a/Pg.pm b/Pg.pm
index f7a6333..9fd8cd3 100644
--- a/Pg.pm
+++ b/Pg.pm
@@ -2244,6 +2244,13 @@ have the child process reconnect to the database with a
fresh database handle, o
rewrite your application not to use forking. See the section on
L</Asynchronous Queries>
for a way to have your script continue to work while the database is
processing a request.
+=head3 B<AutoInactiveDestroy> (boolean)
+
+The InactiveDestroy attribute, described above, needs to be explicitly set in
the child
+process after a fork. If the code that performs the fork is in a third party
module such
+as Sys::Syslog, this can present a problem. Use AutoInactiveDestroy to get
around this
+problem.
+
=head3 B<RaiseError> (boolean, inherited)
Forces errors to always raise an exception. Although it defaults to off, it is
recommended that this
diff --git a/dbdimp.c b/dbdimp.c
index 0792b86..43293a4 100644
--- a/dbdimp.c
+++ b/dbdimp.c
@@ -3737,6 +3737,16 @@ void dbd_st_destroy (SV * sth, imp_sth_t * imp_sth)
if (NULL == imp_sth->seg) /* Already been destroyed! */
croak("dbd_st_destroy called twice!");
+ /* If the AutoInactiveDestroy flag has been set, we go no further */
+ if (DBIc_AIADESTROY(imp_dbh)) {
+ if (TRACE4) {
+ TRC(DBILOGFP, "%sskipping sth destroy due to
AutoInactiveDestroy\n", THEADER);
+ }
+ DBIc_IMPSET_off(imp_sth); /* let DBI know we've done it */
+ if (TEND) TRC(DBILOGFP, "%sEnd dbd_st_destroy
(AutoInactiveDestroy set)\n", THEADER);
+ return;
+ }
+
/* If the InactiveDestroy flag has been set, we go no further */
if (DBIc_IADESTROY(imp_dbh)) {
if (TRACE4_slow) {
diff --git a/t/02attribs.t b/t/02attribs.t
index ecc540c..b97512a 100644
--- a/t/02attribs.t
+++ b/t/02attribs.t
@@ -18,7 +18,7 @@ my ($helpconnect,$connerror,$dbh) = connect_database();
if (! $dbh) {
plan skip_all => 'Connection to database failed, cannot continue
testing';
}
-plan tests => 249;
+plan tests => 260;
isnt ($dbh, undef, 'Connect to database for handle attributes testing');
@@ -101,6 +101,7 @@ a Taint
a Profile (not tested)
a ReadOnly
+d AutoInactiveDestroy (must be the last one tested)
d InactiveDestroy (must be the last one tested)
};
@@ -1539,6 +1540,79 @@ $attrib = $dbh->{Active};
is ($attrib, '', $t);
SKIP: {
+ skip ('Cannot test database handle "AutoInactiveDestroy" on a
non-forking system', 8)
+ if $^O =~ /Win/;
+
+ require Test::Simple;
+
+ skip ('Test::Simple version 0.47 or better required for testing of
attribute "AutoInactiveDestroy"', 8)
+ if $Test::Simple::VERSION < 0.47;
+
+ # Test of forking. Hang on to your hats
+
+ my $answer = 42;
+ $SQL = "SELECT $answer FROM dbd_pg_test WHERE id > ? LIMIT 1";
+
+ for my $destroy (0,1) {
+
+ $dbh = connect_database({nosetup => 1, AutoCommit => 1 });
+ $dbh->{'AutoInactiveDestroy'} = $destroy;
+ $dbh->{'pg_server_prepare'} = 0;
+ $sth = $dbh->prepare($SQL);
+ $sth->execute(1);
+ $sth->finish();
+
+ # Desired flow: parent test, child test, child kill, parent test
+
+ if (fork) {
+ $t=qq{Parent in fork test is working properly
("AutoInactiveDestroy" = $destroy)};
+ $sth->execute(1);
+ $val = $sth->fetchall_arrayref()->[0][0];
+ is ($val, $answer, $t);
+ # Let the child exit first
+ select(undef,undef,undef,0.3);
+ }
+ else { # Child
+ select(undef,undef,undef,0.1); # Age before beauty
+ exit; ## Calls disconnect via DESTROY unless
AutoInactiveDestroy set
+ }
+
+ if ($destroy) {
+ $t=qq{Ping works after the child has exited
("AutoInactiveDestroy" = $destroy)};
+ ok ($dbh->ping(), $t);
+
+ $t='Successful ping returns a SQLSTATE code of 00000
(empty string)';
+ my $state = $dbh->state();
+ is ($state, '', $t);
+
+ $t='Statement handle works after forking';
+ $sth->execute(1);
+ $val = $sth->fetchall_arrayref()->[0][0];
+ is ($val, $answer, $t);
+ }
+ else {
+ $t=qq{Ping fails after the child has exited
("AutoInactiveDestroy" = $destroy)};
+ is ( $dbh->ping(), 0, $t);
+
+ $t='Failed ping returns a SQLSTATE code of 08000';
+ my $state = $dbh->state();
+ is ($state, '08000', $t);
+
+ $t=qq{pg_ping gives an error code of -2 after the child
has exited ("AutoInactiveDestroy" = $destroy)};
+ is ( $dbh->pg_ping(), -2,$t);
+ ok ($dbh->disconnect(), 'Disconnect from database');
+ }
+ }
+}
+
+# Disconnect in preparation for the fork tests
+ok ($dbh->disconnect(), 'Disconnect from database');
+
+$t='Database handle attribute "Active" is false after disconnect';
+$attrib = $dbh->{Active};
+is ($attrib, '', $t);
+
+SKIP: {
skip ('Cannot test database handle "InactiveDestroy" on a non-forking
system', 8)
if $^O =~ /Win/;
--
1.8.4