Author: timbo
Date: Mon Nov 21 16:47:06 2005
New Revision: 2254
Modified:
dbi/trunk/Changes
dbi/trunk/DBI.pm
dbi/trunk/DBI.xs
dbi/trunk/lib/DBI/PurePerl.pm
dbi/trunk/t/72childhandles.t
Log:
Move ChildHandles support into C code (and PurePerl).
Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes (original)
+++ dbi/trunk/Changes Mon Nov 21 16:47:06 2005
@@ -17,7 +17,8 @@ DBI::Changes - List of significant chang
Fixed prerequisites to include Storable thanks to Michael Schwern.
XXX TODO: take_imp_data
-XXX TODO: move ChildHandles code into .xs and PurePerl
+XXX TODO: add skip to t/72childhandles if weaken isn't available
+XXX TODO: fix bus error from t/03handles.t
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.
Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm (original)
+++ dbi/trunk/DBI.pm Mon Nov 21 16:47:06 2005
@@ -277,7 +277,7 @@ my $HAS_WEAKEN = eval {
require Scalar::Util;
# this will croak() if this Scalar::Util doesn't have a working weaken().
Scalar::Util::weaken(my $test = \"foo");
- 0;
+ 1;
};
%DBI::installed_drh = (); # maps driver names to installed driver handles
@@ -458,7 +458,7 @@ my $keeperr = { O=>0x0004 };
);
while ( my ($class, $meths) = each %DBI::DBI_methods ) {
- my $ima_trace = 0+$ENV{DBI_IMA_TRACE}||0;
+ my $ima_trace = 0+($ENV{DBI_IMA_TRACE}||0);
while ( my ($method, $info) = each %$meths ) {
my $fullmeth = "DBI::${class}::$method";
if ($DBI::dbi_debug >= 15) { # quick hack to list DBI methods
@@ -1181,20 +1181,6 @@ sub _new_handle {
# Now add magic so DBI method dispatch works
DBI::_setup_handle($h, $imp_class, $parent, $imp_data);
- # add to the parent's ChildHandles
- if ($HAS_WEAKEN && $parent) {
- my $handles = $parent->{ChildHandles} ||= [];
-
- # purge destroyed handles occasionally
- if (@$handles % 120 == 0 and @$handles) {
- @$handles = grep { defined } @$handles;
- Scalar::Util::weaken($_) for @$handles; # re-weaken after grep
- }
-
- push @$handles, $h;
- Scalar::Util::weaken($handles->[-1]);
- }
-
return $h unless wantarray;
($h, $i);
}
@@ -3335,9 +3321,9 @@ statement handles.
The ChildHandles attribute contains a reference to an array of all the
handles created by this handle which are still accessible. The
contents of the array are weak-refs and will become undef when the
-handle goes out of scope. C<ChildHandles> is only available if you
-have the L<Scalar::Util|Scalar::Util> module installed and
-C<Scalar::Util::weaken()> is working.
+handle goes out of scope. C<ChildHandles> returns undef if your perl version
+does not support weak references (check the L<Scalar::Util|Scalar::Util>
+module). The referenced array returned should be treated as read-only.
For example, to enumerate all driver handles, database handles and
statement handles:
Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs (original)
+++ dbi/trunk/DBI.xs Mon Nov 21 16:47:06 2005
@@ -1033,6 +1033,26 @@ dbih_setup_handle(SV *orv, char *imp_cla
dbih_setup_attrib(h,imp,"Profile",parent,0,1);
}
DBIc_LongReadLen(imp) = DBIc_LongReadLen(parent_imp);
+#ifdef sv_rvweaken
+ if (1) {
+ AV *av;
+ /* add weakref to new (outer) handle into parents ChildHandles
array */
+ tmp_svp = hv_fetch((HV*)SvRV(parent), "ChildHandles", 12, 1);
+ if (!SvROK(*tmp_svp))
+ sv_setsv(*tmp_svp, (SV*)newRV_noinc((SV*)newAV()));
+ av = (AV*)SvRV(*tmp_svp);
+ av_push(av, (SV*)sv_rvweaken(newRV((SV*)SvRV(orv))));
+ if (av_len(av) % 120 == 0) {
+ /* time to do some housekeeping to remove dead handles */
+ I32 i = av_len(av); /* 0 = 1 element */
+ while (i-- >= 0) {
+ SV *sv = av_shift(av);
+ if (SvOK(sv))
+ av_push(av, sv);
+ }
+ }
+ }
+#endif
}
else {
DBIc_LongReadLen(imp) = DBIc_LongReadLen_init;
@@ -1452,19 +1472,22 @@ dbih_set_attr_k(SV *h, SV *keysv, int db
}
else if (strEQ(key, "HandleError")) {
if ( on && (!SvROK(valuesv) || (SvTYPE(SvRV(valuesv)) != SVt_PVCV)) ) {
- croak("Can't set HandleError to '%s'",neatsvpv(valuesv,0));
+ croak("Can't set %s to '%s'", "HandleError", neatsvpv(valuesv,0));
}
DBIc_set(imp_xxh,DBIcf_HandleError, on);
cacheit = 1; /* child copy setup by dbih_setup_handle() */
}
else if (strEQ(key, "HandleSetErr")) {
if ( on && (!SvROK(valuesv) || (SvTYPE(SvRV(valuesv)) != SVt_PVCV)) ) {
- croak("Can't set HandleSetErr to '%s'",neatsvpv(valuesv,0));
+ croak("Can't set %s to '%s'","HandleSetErr",neatsvpv(valuesv,0));
}
DBIc_set(imp_xxh,DBIcf_HandleSetErr, on);
cacheit = 1; /* child copy setup by dbih_setup_handle() */
}
else if (strEQ(key, "ChildHandles")) {
+ if ( on && (!SvROK(valuesv) || (SvTYPE(SvRV(valuesv)) != SVt_PVAV)) ) {
+ croak("Can't set %s to '%s'", "ChildHandles", neatsvpv(valuesv,0));
+ }
cacheit = 1; /* just save it in the hash */
}
else if (strEQ(key, "Profile")) {
@@ -1782,21 +1805,27 @@ dbih_get_attr_k(SV *h, SV *keysv, int db
break;
case 'C':
- if (strEQ(key, "ChopBlanks")) {
+ if (strEQ(key, "ChildHandles")) {
+ svp = hv_fetch((HV*)SvRV(h), key, keylen, FALSE);
+ /* if something has been stored then return it.
+ * otherwise return a dummy empty array if weakrefs are
+ * available, else an undef to indicate that they're not */
+ if (svp) {
+ valuesv = newSVsv(*svp);
+ } else {
+#ifdef sv_rvweaken
+ valuesv = newRV_noinc((SV*)newAV());
+#else
+ valuesv = &sv_undef;
+#endif
+ }
+ }
+ else if (strEQ(key, "ChopBlanks")) {
valuesv = boolSV(DBIc_has(imp_xxh,DBIcf_ChopBlanks));
}
else if (strEQ(key, "CachedKids")) {
valuesv = &sv_undef;
}
- else if (strEQ(key, "ChildHandles")) {
- /* get the value from the hash, otherwise return a [] */
- svp = hv_fetch((HV*)SvRV(h), key, keylen, FALSE);
- if (svp) {
- valuesv = newSVsv(*svp);
- } else {
- valuesv = newRV_noinc((SV*)newAV());
- }
- }
else if (strEQ(key, "CompatMode")) {
valuesv = boolSV(DBIc_COMPAT(imp_xxh));
}
@@ -2404,7 +2433,7 @@ XS(XS_DBI_dispatch) /* prototype
/* However, we must at least modify DBIc_MY_H() as that is */
/* pointing (without a refcnt inc) to the scalar that is */
/* being destroyed, so it'll contain random values later. */
- DBIc_MY_H(imp_xxh) = SvRV(mg->mg_obj); /* inner (untied) HV */
+ DBIc_MY_H(imp_xxh) = (void*)SvRV(mg->mg_obj); /* inner (untied) HV
*/
XSRETURN(0);
}
Modified: dbi/trunk/lib/DBI/PurePerl.pm
==============================================================================
--- dbi/trunk/lib/DBI/PurePerl.pm (original)
+++ dbi/trunk/lib/DBI/PurePerl.pm Mon Nov 21 16:47:06 2005
@@ -38,6 +38,13 @@ $DBI::tfh = Symbol::gensym();
open $DBI::tfh, ">&STDERR" or warn "Can't dup STDERR: $!";
select( (select($DBI::tfh), $| = 1)[0] ); # autoflush
+# check for weaken support, used by ChildHandles
+my $HAS_WEAKEN = eval {
+ require Scalar::Util;
+ # this will croak() if this Scalar::Util doesn't have a working weaken().
+ Scalar::Util::weaken(my $test = \"foo");
+ 1;
+};
%DBI::last_method_except = map { $_=>1 } qw(DESTROY _set_fbav set_err);
@@ -443,6 +450,18 @@ sub _setup_handle {
$h_inner->{Driver} = $parent;
}
$h_inner->{_parent} = $parent;
+
+ # add to the parent's ChildHandles
+ if ($HAS_WEAKEN) {
+ my $handles = $parent->{ChildHandles} ||= [];
+ push @$handles, $h;
+ Scalar::Util::weaken($handles->[-1]);
+ # purge destroyed handles occasionally
+ if (@$handles % 120 == 0) {
+ @$handles = grep { defined } @$handles;
+ Scalar::Util::weaken($_) for @$handles; # re-weaken after grep
+ }
+ }
}
else { # setting up a driver handle
$h_inner->{Warn} = 1;
@@ -452,6 +471,7 @@ sub _setup_handle {
$h_inner->{CompatMode} = (1==0);
$h_inner->{FetchHashKeyName} ||= 'NAME';
$h_inner->{LongReadLen} ||= 80;
+ $h_inner->{ChildHandles} ||= [] if $HAS_WEAKEN;
}
$h_inner->{"_call_depth"} = 0;
$h_inner->{ErrCount} = 0;
Modified: dbi/trunk/t/72childhandles.t
==============================================================================
--- dbi/trunk/t/72childhandles.t (original)
+++ dbi/trunk/t/72childhandles.t Mon Nov 21 16:47:06 2005
@@ -8,29 +8,45 @@ use strict;
use DBI;
-use Test;
-BEGIN { plan tests => 22; }
+use Test::More;
+
+my $HAS_WEAKEN = eval {
+ require Scalar::Util;
+ # this will croak() if this Scalar::Util doesn't have a working weaken().
+ Scalar::Util::weaken(my $test = \"foo");
+ 1;
+};
+if (!$HAS_WEAKEN) {
+ print "1..0 # Skipped: Scalar::Util::weaken not available\n";
+ exit 0;
+}
+
+plan tests => 15;
+
{
# make 10 connections
my @dbh;
for (1 .. 10) {
my $dbh = DBI->connect("dbi:ExampleP:", '', '', { RaiseError=>1 });
- push(@dbh, $dbh);
+ push @dbh, $dbh;
}
# get the driver handle
my %drivers = DBI->installed_drivers();
my $driver = $drivers{ExampleP};
- ok($driver);
+ ok $driver;
- # get the kids, should be the 10 connections
+ # get the kids, should be the same list of connections
my $db_handles = $driver->{ChildHandles};
- ok(scalar @$db_handles, 10);
+ is ref $db_handles, 'ARRAY';
+ is scalar @$db_handles, scalar @dbh;
# make sure all the handles are there
+ my $found = 0;
foreach my $h (@dbh) {
- ok(grep { $h == $_ } @$db_handles);
+ ++$found if grep { $h == $_ } @$db_handles;
}
+ is $found, scalar @dbh;
}
# now all the out-of-scope DB handles should be gone
@@ -40,15 +56,15 @@ BEGIN { plan tests => 22; }
my $handles = $driver->{ChildHandles};
my @db_handles = grep { defined } @$handles;
- ok(scalar @db_handles, 0);
+ is scalar @db_handles, 0, "All handles should be undef now";
}
my $dbh = DBI->connect("dbi:ExampleP:", '', '', { RaiseError=>1 });
-# ChildHandles should start with an empty array-ref
my $empty = $dbh->{ChildHandles};
-ok(scalar @$empty, 0);
+is ref $empty, 'ARRAY', "ChildHandles should be an array-ref if wekref is
available";
+is scalar @$empty, 0, "ChildHandles should start with an empty array-ref";
# test child handles for statement handles
{
@@ -58,7 +74,7 @@ ok(scalar @$empty, 0);
push(@sth, $sth);
}
my $handles = $dbh->{ChildHandles};
- ok(scalar @$handles, 200);
+ is scalar @$handles, scalar @sth;
# test a recursive walk like the one in the docs
my @lines;
@@ -71,17 +87,17 @@ ok(scalar @$empty, 0);
for (grep { defined } @{$h->{ChildHandles}});
}
show_child_handles($_) for (values %{{DBI->installed_drivers()}});
+ print @lines[0..4];
- ok(scalar @lines, 202);
- ok($lines[0] =~ /^drh/);
- ok($lines[1] =~ /^dbh/);
- ok($lines[2] =~ /^sth/);
+ is scalar @lines, 202;
+ like $lines[0], qr/^drh/;
+ like $lines[1], qr/^dbh/;
+ like $lines[2], qr/^sth/;
}
-# they should be gone now
my $handles = $dbh->{ChildHandles};
my @live = grep { defined $_ } @$handles;
-ok(scalar @live, 0);
+is scalar @live, 0, "handles should be gone now";
# test that the childhandle array does not grow uncontrollably
{
@@ -89,7 +105,7 @@ ok(scalar @live, 0);
my $sth = $dbh->prepare('SELECT name FROM t');
}
my $handles = $dbh->{ChildHandles};
- ok(scalar @$handles < 1000);
+ cmp_ok scalar @$handles, '<', 1000;
my @live = grep { defined } @$handles;
- ok(scalar @live, 0);
+ is scalar @live, 0;
}