[DBD::Pg 1/2] Add support for AutoInactiveDestroy. Committer note: Per RT #68893. Changed pg_server_prepare from 1 to 0 in the tests.
dbdpg-commits-gENoVmVU/[email protected] Sat, 16 Nov 2013 21:44:32 -0500
| Newsgroups | gmane.comp.db.postgresql.dbdpg.cvs |
|---|---|
| Message-ID | <[email protected]> |
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