Here's my first pass at an ActiveHandles attribute. With this patch
this code prints all live handles:
foreach my $handle (@{$dbh->{ActiveHandles}}) {
next unless defined $handle;
print $handle, "\n";
}
I'd like to take the next step and add a DBI->ActiveHandles call to do
it without a $dbh. Alternately we could just document the global
($DBI::active_handles) which offers the same functionality.
Question: is "active" the right word here? Looking at the other
"active" attributes I think maybe not. Perhaps "LiveHandles"?
-sam
diff -Naur DBI-1.48.orig/DBI.pm DBI-1.48/DBI.pm
--- DBI-1.48.orig/DBI.pm 2005-03-14 11:45:38.000000000 -0500
+++ DBI-1.48/DBI.pm 2005-06-22 13:31:01.904208160 -0400
@@ -271,8 +271,18 @@
DBI->trace_msg("DBI connect via $DBI::connect_via in
$INC{'Apache/DBI.pm'}\n");
}
+# check for weaken support, used by ActiveHandles
+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::installed_drh = (); # maps driver names to installed driver handles
+$DBI::active_handles = []; # keeps list of weak-refs to created handles
+
# Setup special DBI dynamic variables. See DBI::var::FETCH for details.
# These are dynamically associated with the last handle used.
tie $DBI::err, 'DBI::var', '*err'; # special case: referenced via IHA
list
@@ -1166,6 +1176,12 @@
# Now add magic so DBI method dispatch works
DBI::_setup_handle($h, $imp_class, $parent, $imp_data);
+ # store a weakref in ActiveHandles if possible
+ if ($HAS_WEAKEN) {
+ push @$DBI::active_handles, $h;
+ Scalar::Util::weaken($DBI::active_handles->[-1]);
+ }
+
return $h unless wantarray;
($h, $i);
}
@@ -3679,6 +3695,21 @@
The C<Profile> attribute was added in DBI 1.24.
+=item C<ActiveHandles> (global, read-only)
+
+The C<ActiveHandles> attribute contains a reference to an array of all
+the database handles currently live in your program. The references
+on this list are weak-refs (see L<Scalar::Util> for details) which
+means they will become C<undef> when the handle goes out of scope in
+your code.
+
+Example code to print out all available handles:
+
+ foreach my $handle (@{$dbh->{ActiveHandles}}) {
+ next unless defined $handle;
+ print $handle, "\n";
+ }
+
=item C<private_your_module_name_*>
The DBI provides a way to store extra information in a DBI handle as
diff -Naur DBI-1.48.orig/DBI.xs DBI-1.48/DBI.xs
--- DBI-1.48.orig/DBI.xs 2005-01-20 06:06:28.000000000 -0500
+++ DBI-1.48/DBI.xs 2005-06-22 12:33:15.000000000 -0400
@@ -1548,6 +1548,9 @@
) ) {
cacheit = 1;
}
+ else if (strEQ(key, "ActiveHandles")) {
+ croak("Can't set read-only ActiveHandles attribute.");
+ }
else { /* XXX should really be an event ? */
if (isUPPER(*key)) {
char *msg = "Can't set %s->{%s}: unrecognised attribute or invalid
value%s";
@@ -1739,6 +1742,11 @@
else if (keylen==10 && strEQ(key, "ActiveKids")) {
valuesv = newSViv(DBIc_ACTIVE_KIDS(imp_xxh));
}
+ else if (keylen==13 && strEQ(key, "ActiveHandles")) {
+ // return the global $DBI::active_handles array-ref
+ valuesv = perl_get_sv("DBI::active_handles", 0);
+ SvREFCNT_inc(valuesv);
+ }
break;
case 'B':
diff -Naur DBI-1.48.orig/lib/DBI/PurePerl.pm DBI-1.48/lib/DBI/PurePerl.pm
--- DBI-1.48.orig/lib/DBI/PurePerl.pm 2004-12-16 11:41:06.000000000 -0500
+++ DBI-1.48/lib/DBI/PurePerl.pm 2005-06-22 13:29:39.195781752 -0400
@@ -645,6 +645,7 @@
return ($h->FETCH('TaintIn') && $h->FETCH('TaintOut')) if $key
eq'Taint';
return (1==0) if $is_flag_attribute{$key}; # return perl-style sv_no,
not undef
return $DBI::dbi_debug if $key eq 'TraceLevel';
+ return $DBI::active_handles if $key eq 'ActiveHandles';
if (!$is_valid_attribute{$key} and $key =~ m/^[A-Z]/) {
local $^W; # hide undef warnings
Carp::carp( sprintf "Can't get %s->{%s}: unrecognised attribute
(@{[ %$h ]})",$h,$key )
diff -Naur DBI-1.48.orig/t/71activehandles.t DBI-1.48/t/71activehandles.t
--- DBI-1.48.orig/t/71activehandles.t 1969-12-31 19:00:00.000000000 -0500
+++ DBI-1.48/t/71activehandles.t 2005-06-22 13:21:02.982258136 -0400
@@ -0,0 +1,54 @@
+#!perl -w
+
+use strict;
+
+#
+# test script for the ActiveHandles attribute
+#
+
+use DBI;
+
+use Test;
+BEGIN { plan tests => 15; }
+
+# make one connection to keep around
+my $dbh = DBI->connect("dbi:ExampleP:", '', '', { RaiseError=>1 });
+
+{
+ # make 10 connections
+ my @dbh;
+ for (1 .. 10) {
+ my $dbh = DBI->connect("dbi:ExampleP:", '', '', { RaiseError=>1 });
+ push(@dbh, $dbh);
+ }
+
+ # get them back from ActiveHandles
+ my $handles = $dbh->{ActiveHandles};
+
+ # there should be 11 DB handles, the one at the top and the 10 new ones
+ my @db_handles = grep { $_->isa('DBI::db') } @$handles;
+ ok(scalar @db_handles, 11);
+
+ # make sure all the handles are there
+ foreach my $h (@dbh, $dbh) {
+ ok(grep { $h == $_ } @db_handles);
+ }
+}
+
+# now all the out-of-scope DB handles should be gone
+{
+ my $handles = $dbh->{ActiveHandles};
+ my @db_handles = grep { defined and $_->isa('DBI::db') } @$handles;
+ ok(scalar @db_handles, 1);
+}
+
+# do an explicit disconnect
+$dbh->disconnect;
+
+# it should still be there
+{
+ my $handles = $dbh->{ActiveHandles};
+ my @db_handles = grep { defined and $_->isa('DBI::db') } @$handles;
+ ok(scalar @db_handles, 1);
+ ok(not $db_handles[0]->{Active});
+}