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

Reply via email to