Author: timbo
Date: Sun Nov 27 16:32:13 2005
New Revision: 2282
Modified:
dbi/trunk/Changes
dbi/trunk/DBI.pm
dbi/trunk/DBI.xs
dbi/trunk/Driver.xst
dbi/trunk/t/03handle.t
dbi/trunk/t/40profile.t
Log:
Fixed take_imp_data to be more practical. Yeah!
Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes (original)
+++ dbi/trunk/Changes Sun Nov 27 16:32:13 2005
@@ -15,8 +15,7 @@ DBI::Changes - List of significant chang
Fixed dangling ref in $sth after parent $dbh destroyed
with thanks to [EMAIL PROTECTED] for the bug report #13151
Fixed prerequisites to include Storable thanks to Michael Schwern.
-
-XXX TODO: take_imp_data
+ Fixed take_imp_data to be more practical.
Change to require perl 5.6.1 (as advertised in 2003) not 5.6.0.
Changed internals to be more strictly coded thanks to Andy Lester.
@@ -33,7 +32,8 @@ XXX TODO: take_imp_data
when certain methods are called. For example:
$dbh->{Callbacks}->{prepare} = sub { ... };
With thanks to David Wheeler for the kick start.
- Added $h->{ChildHandles} (using weakrefs) thanks to Sam Tregar.
+ Added $h->{ChildHandles} (using weakrefs) thanks to Sam Tregar
+ I've recoded it in C so there's no significant performance impact.
Added $h->{Type} docs (returns 'dr', 'db', or 'st')
Adding trace message in DESTROY if InactiveDestroy enabled.
Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm (original)
+++ dbi/trunk/DBI.pm Sun Nov 27 16:32:13 2005
@@ -5020,7 +5020,7 @@ will generate a warning and return undef
Why would you want to do this? You don't, forget I even mentioned it.
Unless, that is, you're implementing something advanced like a
-multi-threaded connection pool.
+multi-threaded connection pool. See L<DBI::Pool>.
The returned $imp_data can be passed as a C<dbi_imp_data> attribute
to a later connect() call, even in a separate thread in the same
@@ -5032,12 +5032,15 @@ Some things to keep in mind...
B<*> the $imp_data holds the only reference to the underlying
database API connection data. That connection is still 'live' and
won't be cleaned up properly unless the $imp_data is used to create
-a new $dbh which can then disconnect() normally.
+a new $dbh which is then allowed to disconnect() normally.
B<*> using the same $imp_data to create more than one other new
$dbh at a time may well lead to unpleasant problems. Don't do that.
-The C<take_imp_data> method was added in DBI 1.36.
+Any child statement handles are effectively destroyed when take_imp_data() is
+called.
+
+The C<take_imp_data> method was added in DBI 1.36 but wasn't useful till 1.49.
=back
@@ -6894,12 +6897,12 @@ An old variable that should no longer be
=head2 DBI_PROFILE
The DBI_PROFILE environment variable can be used to enable profiling
-of DBI method calls. See <DBI::Profile> for more information.
+of DBI method calls. See L<DBI::Profile> for more information.
=head2 DBI_PUREPERL
The DBI_PUREPERL environment variable can be used to enable the
-use of DBI::PurePerl. See <DBI::PurePerl> for more information.
+use of DBI::PurePerl. See L<DBI::PurePerl> for more information.
=head1 WARNING AND ERROR MESSAGES
@@ -7322,7 +7325,7 @@ DBI::ProxyServer are part of the DBI dis
=item SQL Parser
-See also the SQL::Statement module, SQL parser and engine.
+See also the L<SQL::Statement> module, SQL parser and engine.
=back
Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs (original)
+++ dbi/trunk/DBI.xs Sun Nov 27 16:32:13 2005
@@ -3878,6 +3878,7 @@ take_imp_data(h)
D_imp_xxh(h);
MAGIC *mg;
SV *imp_xxh_sv;
+ SV **tmp_svp;
CODE:
(void)cv; /* unused */
/*
@@ -3918,16 +3919,46 @@ take_imp_data(h)
* AutoCommit) match the state of the underlying connection.
*/
+ if (!DBIc_ACTIVE(imp_xxh)) {/* sanity check, may be relaxed later */
+ set_err_char(h, imp_xxh, "1", 1, "Can't take_imp_data from handle
that's not Active", 0, "take_imp_data");
+ XSRETURN(0);
+ }
+
+ /* Ideally there should be no child statement handles existing when
+ * take_imp_data is called because when those statement handles are
+ * destroyed they may need to interact with the 'zombie' parent dbh.
+ * So we do our best to kill neautralize them.
+ */
if (DBIc_TYPE(imp_xxh) <= DBIt_DB && DBIc_CACHED_KIDS((imp_dbh_t*)imp_xxh))
clear_cached_kids(h, imp_xxh, "take_imp_data", 0);
- if (DBIc_KIDS(imp_xxh)) { /* safety check, may be relaxed later to
DBIc_ACTIVE_KIDS */
- set_err_char(h, imp_xxh, "1", 1, "Can't take_imp_data from handle while
it still has kids", 0, "take_imp_data");
- XSRETURN(0);
+ if ((tmp_svp = hv_fetch((HV*)SvRV(h), "ChildHandles", 12, FALSE)) &&
SvROK(*tmp_svp)) {
+ AV *av = (AV*)SvRV(*tmp_svp);
+ HV *zombie_stash = gv_stashpv("DBI::zombie", GV_ADDWARN);
+ I32 kidslots;
+ for (kidslots = AvFILL(av); kidslots >= 0; --kidslots) {
+ SV **hp = av_fetch(av, kidslots, FALSE);
+ if (hp && SvROK(*hp) && SvMAGICAL(SvRV(*hp))) {
+ sv_unmagic(SvRV(*hp), 'P'); /* untie */
+ sv_bless(*hp, zombie_stash); /* neutralise */
+ }
+ }
}
- if (!DBIc_ACTIVE(imp_xxh)) {/* sanity check, may be relaxed later */
- set_err_char(h, imp_xxh, "1", 1, "Can't take_imp_data from handle
that's not Active", 0, "take_imp_data");
+ /* The above measures may not be sufficient if weakrefs aren't available
+ * or something has a reference to the inner-handle of an sth.
+ * We'll require no Active kids, but just warn about others.
+ */
+ if (DBIc_ACTIVE_KIDS(imp_xxh)) {
+ set_err_char(h, imp_xxh, "1", 1, "Can't take_imp_data from handle while
it still has Active kids", 0, "take_imp_data");
XSRETURN(0);
}
+ if (DBIc_KIDS(imp_xxh))
+ warn("take_imp_data from handle while it still has kids");
+
+ /* it may be better here to return a copy and poison the original
+ * rather than detatching and returning the original
+ */
+
+ /* --- perform the surgery */
dbih_getcom2(h, &mg); /* get the MAGIC so we can change it */
imp_xxh_sv = mg->mg_obj; /* take local copy of the imp_data pointer */
mg->mg_obj = Nullsv; /* sever the link from handle to imp_xxh */
Modified: dbi/trunk/Driver.xst
==============================================================================
--- dbi/trunk/Driver.xst (original)
+++ dbi/trunk/Driver.xst Sun Nov 27 16:32:13 2005
@@ -422,7 +422,7 @@ _prepare(sth, statement, attribs=Nullsv)
#ifdef dbd_st_prepare_sv
ST(0) = dbd_st_prepare_sv(sth, imp_sth, statement, attribs) ? &sv_yes :
&sv_no;
#else
- ST(0) = dbd_st_prepare(sth, imp_sth, SVPV_nolen(statement), attribs) ?
&sv_yes : &sv_no;
+ ST(0) = dbd_st_prepare(sth, imp_sth, SvPV_nolen(statement), attribs) ?
&sv_yes : &sv_no;
#endif
}
Modified: dbi/trunk/t/03handle.t
==============================================================================
--- dbi/trunk/t/03handle.t (original)
+++ dbi/trunk/t/03handle.t Sun Nov 27 16:32:13 2005
@@ -2,7 +2,7 @@
use strict;
-use Test::More tests => 128;
+use Test::More tests => 135;
## ----------------------------------------------------------------------------
## 03handle.t - tests handles
@@ -258,13 +258,25 @@ SKIP: {
# handle take_imp_data test
SKIP: {
- skip "take_imp_data test not supported under DBI::PurePerl", 12 if
$DBI::PurePerl;
+ skip "take_imp_data test not supported under DBI::PurePerl", 19 if
$DBI::PurePerl;
my $dbh = DBI->connect("dbi:$driver:", '', '');
isa_ok($dbh, "DBI::db");
cmp_ok($drh->{Kids}, '==', 1, '... our Driver should have 1 Kid(s) here');
+ $dbh->prepare("select name from ?"); # destroyed at once
+ my $sth2 = $dbh->prepare("select name from ?"); # inactive
+ my $sth3 = $dbh->prepare("select name from ?"); # active:
+ $sth3->execute(".");
+ is $sth3->{Active}, 1;
+ is $dbh->{ActiveKids}, 1;
+
+ my $ChildHandles = $dbh->{ChildHandles};
+ ok $ChildHandles, 'we need weakrefs for take_imp_data to work safely with
child handles';
+ is @$ChildHandles, 3, 'should have 3 entries (implementation detail)';
+ is grep({ defined } @$ChildHandles), 2, 'should have 2 defined handles';
+
my $imp_data = $dbh->take_imp_data;
ok($imp_data, '... we got some imp_data to test');
# generally length($imp_data) = 112 for 32bit, 116 for 64 bit
@@ -275,6 +287,10 @@ SKIP: {
cmp_ok($drh->{Kids}, '==', 0, '... our Driver should have 0 Kid(s) after
calling take_imp_data');
+ is ref $sth3, 'DBI::zombie', 'sth should be reblessed';
+ eval { $sth3->finish };
+ like $@, qr/Can't locate object method/;
+
{
my $warn;
local $SIG{__WARN__} = sub { ++$warn if $_[0] =~ /after take_imp_data/
};
Modified: dbi/trunk/t/40profile.t
==============================================================================
--- dbi/trunk/t/40profile.t (original)
+++ dbi/trunk/t/40profile.t Sun Nov 27 16:32:13 2005
@@ -100,7 +100,7 @@ if ($shortest < 0) {
warn "Perhaps you have time sync software (like NTP) that adjusted the
clock\n";
warn "backwards by more than $shortest seconds during the test. PLEASE
RETRY.\n";
# Don't treat very small negative amounts as a failure - it's always been
due
- # due to NTP of buggy multiprocessor systems.
+ # due to NTP or buggy multiprocessor systems.
$shortest = 0 if $shortest > -0.008;
}
ok($count > 3);