Author: theory
Date: Sun Jul 25 17:12:54 2010
New Revision: 14282
Added:
dbi/trunk/t/16destroy.t
Modified:
dbi/trunk/DBI.pm
dbi/trunk/DBI.xs
dbi/trunk/DBIXS.h
dbi/trunk/Driver.xst
dbi/trunk/dbixs_rev.h
dbi/trunk/lib/DBD/File.pm
dbi/trunk/lib/DBD/Gofer.pm
dbi/trunk/lib/DBI/Gofer/Execute.pm
dbi/trunk/lib/DBI/PurePerl.pm
dbi/trunk/t/06attrs.t
Log:
Add AutoInactiveDestroy.
It doesn't actually work yet, but there is documentation and a failing test.
What it needs is:
* The ability to properly set the attribute
* Caching of $$ on connect (not sure about connect_cached)
* Checking of that cached value to $$ in DESTROY.
Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm (original)
+++ dbi/trunk/DBI.pm Sun Jul 25 17:12:54 2010
@@ -1520,7 +1520,7 @@
# explicitly set attributes which are unlikely to be in the
# attribute cache, i.e., boolean's and some others
$attr->{$_} = $old_dbh->FETCH($_) for (qw(
- AutoCommit ChopBlanks InactiveDestroy
+ AutoCommit ChopBlanks InactiveDestroy AutoInactiveDestroy
LongTruncOk PrintError PrintWarn Profile RaiseError
ShowErrorStatement TaintIn TaintOut
));
@@ -3636,6 +3636,34 @@
level (not handle trace level) is set high enough to show the trace
from the DBI's method dispatcher, e.g. >= 9.
+=head3 C<AutoInactiveDestroy>
+
+Type: boolean
+
+While its best to set C<InactiveDestroy> on a handle when you've C<fork>ed off
+a child process, sometimes you might call code that C<fork>s without your
+knowledge. In such a case, if a the child exits and then the parent tries to
+use the original handle, it might fail, as the child might have closed the
+socket the parent was using.
+
+Use C<AutoInactiveDestroy> to get around this situation. Like
+C<InactiveDestroy>, when set to a true value the handle will be treated by the
+DESTROY as if it was no longer Active, and so the I<database engine> related
+effects of DESTROYing a handle will be skipped. This only happens, however, if
+the DBI detects that the process ID in which the handle is being DESTROYed is
+different than the process ID in which it was created.
+
+This is the example it's designed to deal with:
+
+ my $dbh = DBI->connect(...);
+ some_code_that_forks(); # Perhaps without your knowlege.
+ $dbh->do(...); # dies.
+
+The issue is that C<$dbh> is DESTROYed in the fork and closes the socket from
+the parent, too. Pass a true value for C<AutoInactiveDestroy> and the DBI will
+automatically detect when a forked database handle is being DESTROYed and
+treat the handle as non-active.
+
=head3 C<PrintWarn>
Type: boolean, inherited
Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs (original)
+++ dbi/trunk/DBI.xs Sun Jul 25 17:12:54 2010
@@ -1866,6 +1866,9 @@
else if (strEQ(key, "Warn")) {
(on) ? DBIc_WARN_on(imp_xxh) : DBIc_WARN_off(imp_xxh);
}
+ else if (strEQ(key, "AutoInactiveDestroy")) {
+ (on) ? DBIc_AIADESTROY_on(imp_xxh) : DBIc_AIADESTROY_off(imp_xxh);
+ }
else if (strEQ(key, "InactiveDestroy")) {
(on) ? DBIc_IADESTROY_on(imp_xxh) : DBIc_IADESTROY_off(imp_xxh);
}
@@ -2226,6 +2229,9 @@
else if (keylen==10 && strEQ(key, "ActiveKids")) {
valuesv = newSViv(DBIc_ACTIVE_KIDS(imp_xxh));
}
+ else if (strEQ(key, "AutoInactiveDestroy")) {
+ valuesv = boolSV(DBIc_AIADESTROY(imp_xxh));
+ }
break;
case 'B':
@@ -3194,7 +3200,10 @@
}
}
- if (DBIc_IADESTROY(imp_xxh)) { /* want's ineffective destroy */
+ if (DBIc_AIADESTROY(imp_xxh)) { /* wants ineffective after fork */
+ /* Compare $$ to its value set in constructor ans set IADESTROY if
different. */
+ }
+ if (DBIc_IADESTROY(imp_xxh)) { /* wants ineffective destroy */
DBIc_ACTIVE_off(imp_xxh);
}
call_depth = 0;
@@ -5027,6 +5036,9 @@
D_imp_sth(sth);
ST(0) = &PL_sv_yes;
/* we don't test IMPSET here because this code applies to pure-perl
drivers */
+ if (DBIc_AIADESTROY(imp_sth)) { /* wants ineffective after fork */
+ /* Compare $$ to its value set in constructor ans set IADESTROY if
different. */
+ }
if (DBIc_IADESTROY(imp_sth)) { /* want's ineffective destroy */
DBIc_ACTIVE_off(imp_sth);
if (DBIc_DBISTATE(imp_sth)->debug)
Modified: dbi/trunk/DBIXS.h
==============================================================================
--- dbi/trunk/DBIXS.h (original)
+++ dbi/trunk/DBIXS.h Sun Jul 25 17:12:54 2010
@@ -244,6 +244,7 @@
#define DBIcf_IMPSET 0x000002 /* has implementor data to be clear'd
*/
#define DBIcf_ACTIVE 0x000004 /* needs finish/disconnect before
clear */
#define DBIcf_IADESTROY 0x000008 /* do DBIc_ACTIVE_off before DESTROY
*/
+#define DBIcf_AIADESTROY 0x000009 /* autod DBIc_ACTIVE_off before
DESTROY */
#define DBIcf_WARN 0x000010 /* warn about poor practice etc
*/
#define DBIcf_COMPAT 0x000020 /* compat/emulation mode (eg oraperl)
*/
#define DBIcf_ChopBlanks 0x000040 /* rtrim spaces from fetch char
columns */
@@ -265,7 +266,7 @@
/* NOTE: new flags may require clone() to be updated */
#define DBIcf_INHERITMASK /* what NOT to pass on to children */
\
- (U32)( DBIcf_COMSET | DBIcf_IMPSET | DBIcf_ACTIVE | DBIcf_IADESTROY
\
+ (U32)( DBIcf_COMSET | DBIcf_IMPSET | DBIcf_ACTIVE | DBIcf_IADESTROY |
DBIcf_AIADESTROY \
| DBIcf_AutoCommit | DBIcf_BegunWork | DBIcf_Executed | DBIcf_Callbacks )
/* general purpose bit setting and testing macros */
@@ -317,6 +318,10 @@
#define DBIc_IADESTROY_on(imp) (DBIc_FLAGS(imp) |= DBIcf_IADESTROY)
#define DBIc_IADESTROY_off(imp) (DBIc_FLAGS(imp) &= ~DBIcf_IADESTROY)
+#define DBIc_AIADESTROY(imp) (DBIc_FLAGS(imp) & DBIcf_AIADESTROY)
+#define DBIc_AIADESTROY_on(imp) (DBIc_FLAGS(imp) |= DBIcf_AIADESTROY)
+#define DBIc_AIADESTROY_off(imp) (DBIc_FLAGS(imp) &= ~DBIcf_AIADESTROY)
+
#define DBIc_WARN(imp) (DBIc_FLAGS(imp) & DBIcf_WARN)
#define DBIc_WARN_on(imp) (DBIc_FLAGS(imp) |= DBIcf_WARN)
#define DBIc_WARN_off(imp) (DBIc_FLAGS(imp) &= ~DBIcf_WARN)
Modified: dbi/trunk/Driver.xst
==============================================================================
--- dbi/trunk/Driver.xst (original)
+++ dbi/trunk/Driver.xst Sun Jul 25 17:12:54 2010
@@ -350,7 +350,10 @@
SvPV(dbh,lna));
}
else {
- if (DBIc_IADESTROY(imp_dbh)) { /* want's ineffective
destroy */
+ if (DBIc_AIADESTROY(imp_dbh)) { /* wants ineffective after
fork */
+ /* Compare $$ to its value set in constructor ans set IADESTROY if
different. */
+ }
+ if (DBIc_IADESTROY(imp_dbh)) { /* wants ineffective destroy
*/
DBIc_ACTIVE_off(imp_dbh);
if (DBIc_DBISTATE(imp_dbh)->debug)
PerlIO_printf(DBIc_LOGPIO(imp_dbh), " DESTROY %s
skipped due to InactiveDestroy\n", SvPV_nolen(dbh));
@@ -751,7 +754,10 @@
SvPV(sth,lna));
}
else {
- if (DBIc_IADESTROY(imp_sth)) { /* want's ineffective destroy */
+ if (DBIc_AIADESTROY(imp_sth)) { /* wants ineffective after fork */
+ /* Compare $$ to its value set in constructor ans set IADESTROY if
different. */
+ }
+ if (DBIc_IADESTROY(imp_sth)) { /* wants ineffective destroy */
DBIc_ACTIVE_off(imp_sth);
if (DBIc_DBISTATE(imp_sth)->debug)
PerlIO_printf(DBIc_LOGPIO(imp_sth), " DESTROY %s
skipped due to InactiveDestroy\n", SvPV_nolen(sth));
Modified: dbi/trunk/dbixs_rev.h
==============================================================================
--- dbi/trunk/dbixs_rev.h (original)
+++ dbi/trunk/dbixs_rev.h Sun Jul 25 17:12:54 2010
@@ -1,3 +1,3 @@
-/* Thu Apr 15 13:40:28 2010 */
+/* Sun Jul 25 16:30:05 2010 */
/* Code modified since last checkin */
-#define DBIXS_REVISION 13903
+#define DBIXS_REVISION 14281
Modified: dbi/trunk/lib/DBD/File.pm
==============================================================================
--- dbi/trunk/lib/DBD/File.pm (original)
+++ dbi/trunk/lib/DBD/File.pm Sun Jul 25 17:12:54 2010
@@ -1085,6 +1085,7 @@
CachedKids
CompatMode (Not used)
InactiveDestroy
+ AutoInactiveDestroy
Kids
PrintError
RaiseError
Modified: dbi/trunk/lib/DBD/Gofer.pm
==============================================================================
--- dbi/trunk/lib/DBD/Gofer.pm (original)
+++ dbi/trunk/lib/DBD/Gofer.pm Sun Jul 25 17:12:54 2010
@@ -29,6 +29,7 @@
FetchHashKeyName
HandleError HandleSetErr
InactiveDestroy
+ AutoInactiveDestroy
PrintError PrintWarn
Profile
RaiseError
Modified: dbi/trunk/lib/DBI/Gofer/Execute.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Execute.pm (original)
+++ dbi/trunk/lib/DBI/Gofer/Execute.pm Sun Jul 25 17:12:54 2010
@@ -166,7 +166,7 @@
# delete attributes we don't want to affect the server-side
# (Could just do this on client-side and trust the client. DoS?)
- delete @{$attr}{qw(Profile InactiveDestroy HandleError HandleSetErr
TraceLevel Taint TaintIn TaintOut)};
+ delete @{$attr}{qw(Profile InactiveDestroy AutoInactiveDestroy HandleError
HandleSetErr TraceLevel Taint TaintIn TaintOut)};
$dsn = $self->forced_connect_dsn || $dsn || $self->default_connect_dsn
or die "No forced_connect_dsn, requested dsn, or default_connect_dsn
for request";
Modified: dbi/trunk/lib/DBI/PurePerl.pm
==============================================================================
--- dbi/trunk/lib/DBI/PurePerl.pm (original)
+++ dbi/trunk/lib/DBI/PurePerl.pm Sun Jul 25 17:12:54 2010
@@ -143,6 +143,7 @@
TaintIn
TaintOut
InactiveDestroy
+ AutoInactiveDestroy
LongTruncOk
MultiThread
PrintError
@@ -1168,6 +1169,7 @@
ActiveKids
InactiveDestroy
+ AutoInactiveDestroy
Kids
Taint
TaintIn
Modified: dbi/trunk/t/06attrs.t
==============================================================================
--- dbi/trunk/t/06attrs.t (original)
+++ dbi/trunk/t/06attrs.t Sun Jul 25 17:12:54 2010
@@ -2,7 +2,7 @@
use strict;
-use Test::More tests => 145;
+use Test::More tests => 148;
## ----------------------------------------------------------------------------
## 06attrs.t - ...
@@ -39,6 +39,7 @@
ok( $dbh->{AutoCommit}, '... checking AutoCommit attribute for dbh');
ok(!$dbh->{CompatMode}, '... checking CompatMode attribute for dbh');
ok(!$dbh->{InactiveDestroy}, '... checking InactiveDestory attribute for
dbh');
+ok(!$dbh->{AutoInactiveDestroy}, '... checking AutoInactiveDestory attribute
for dbh');
ok(!$dbh->{PrintError}, '... checking PrintError attribute for dbh');
ok( $dbh->{PrintWarn}, '... checking PrintWarn attribute for dbh');
# true because of perl -w above
ok( $dbh->{RaiseError}, '... checking RaiseError attribute for dbh');
@@ -111,6 +112,7 @@
ok( $drh->{AutoCommit}, '... checking AutoCommit attribute for drh');
ok(!$drh->{CompatMode}, '... checking CompatMode attribute for drh');
ok(!$drh->{InactiveDestroy}, '... checking InactiveDestory attribute for
drh');
+ok(!$drh->{AutoInactiveDestroy}, '... checking AutoInactiveDestory attribute
for drh');
ok(!$drh->{PrintError}, '... checking PrintError attribute for drh');
ok( $drh->{PrintWarn}, '... checking PrintWarn attribute for drh');
# true because of perl -w above
ok(!$drh->{RaiseError}, '... checking RaiseError attribute for drh');
@@ -185,6 +187,7 @@
ok(!$sth->{Active}, '... checking Active attribute for sth');
ok(!$sth->{CompatMode}, '... checking CompatMode attribute for sth');
ok(!$sth->{InactiveDestroy}, '... checking InactiveDestroy attribute for
sth');
+ok(!$sth->{AutoInactiveDestroy}, '... checking AutoInactiveDestroy attribute
for sth');
ok(!$sth->{PrintError}, '... checking PrintError attribute for sth');
ok( $sth->{PrintWarn}, '... checking PrintWarn attribute for sth');
ok( $sth->{RaiseError}, '... checking RaiseError attribute for sth');
@@ -283,7 +286,7 @@
# $h->{TraceLevel} tests are in t/09trace.t
-print "Checking inheritance\n";
+diag "Checking inheritance\n";
SKIP: {
skip "drh->dbh->sth inheritance test skipped with DBI_AUTOPROXY", 2 if
$ENV{DBI_AUTOPROXY};
Added: dbi/trunk/t/16destroy.t
==============================================================================
--- (empty file)
+++ dbi/trunk/t/16destroy.t Sun Jul 25 17:12:54 2010
@@ -0,0 +1,47 @@
+#!perl -w
+
+use strict;
+
+use Test::More tests => 14;
+
+BEGIN{ use_ok( 'DBI' ) }
+
+my $dsn = 'dbi:ExampleP:dummy';
+
+# Connect to the example driver.
+ok my $dbh = DBI->connect($dsn, '', ''),
+ 'Create plain dbh';
+
+isa_ok( $dbh, 'DBI::db' );
+
+# Clean up when we're done.
+END { $dbh->disconnect if $dbh };
+
+ok $dbh->{Active}, 'Should start active';
+$dbh->DESTROY;
+ok $dbh->{Active}, 'Should still be active';
+
+# Try InactiveDestroy.
+ok $dbh = DBI->connect($dsn, '', '', { InactiveDestroy => 1 }),
+ 'Create with ActiveDestroy';
+ok $dbh->{Active}, 'Should start active';
+$dbh->DESTROY;
+ok !$dbh->{Active}, 'Should no longer be active';
+
+# Try AutoInactiveDestroy.
+ok $dbh = DBI->connect($dsn, '', '', { AutoInactiveDestroy => 1 }),
+ 'Create with AutoInactiveDestroy';
+ok $dbh->{Active}, 'Should start active';
+$dbh->DESTROY;
+ok $dbh->{Active}, 'Should still be active';
+
+# Try AutoInactiveDestroy and "fork".
+ok $dbh = DBI->connect($dsn, '', '', { AutoInactiveDestroy => 1 }),
+ 'Create with AutoInactiveDestroy again';
+ok $dbh->{Active}, 'Should start active';
+do {
+ # Fake fork.
+ local $$ = 0;
+ $dbh->DESTROY;
+};
+ok !$dbh->{Active}, 'Should not still be active';