Author: timbo
Date: Fri Oct 1 16:37:25 2004
New Revision: 475
Modified:
dbi/trunk/Changes
dbi/trunk/DBI.pm
dbi/trunk/DBI.xs
dbi/trunk/ToDo
dbi/trunk/lib/DBD/NullP.pm
dbi/trunk/lib/DBI/PurePerl.pm
dbi/trunk/t/02dbidrv.t
dbi/trunk/t/03handle.t
dbi/trunk/t/15array.t
Log:
Added $h1->swap_inner_handle($h2) - sponsored by BizRate.com
Added common DESTROY method to avoid AUTOLOAD lookup.
Assorted test and other tidyups.
Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes (original)
+++ dbi/trunk/Changes Fri Oct 1 16:37:25 2004
@@ -27,6 +27,9 @@
a warning and is no longer fatal. Thanks to Vadim.
Corrected fetchall_arrayref() docs example thanks to Drew Broadley.
+ Added $h1->swap_inner_handle($h2) sponsored by BizRate.com
+
+
=head2 Changes in DBI 1.43 (svn rev 377), 2nd July 2004
Fixed connect() and connect_cached() RaiseError/PrintError
Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm (original)
+++ dbi/trunk/DBI.pm Fri Oct 1 16:37:25 2004
@@ -365,6 +365,7 @@
set_err => { U =>[3,6,'$err, $errmsg [, $state, $method, $rv]'],
O=>0x0010 },
trace => { U =>[1,3,'[$trace_level, [$filename]]'], O=>0x0004 },
trace_msg => { U =>[2,3,'$message_text [, $min_level ]' ],
O=>0x0004, T=>8 },
+ swap_inner_handle => { U =>[2,3,'$h [, $allow_reparent ]'] },
},
dr => { # Database Driver Interface
'connect' => { U =>[1,5,'[$db [,$user [,$passwd [,\%attr]]]]'], H=>3 },
@@ -1279,7 +1280,6 @@
return;
}
- sub DESTROY { return } # avoid AUTOLOAD
}
@@ -1300,6 +1300,7 @@
my ($this) = DBI::_new_dbh($drh, {
'Name' => $dsn,
});
+ # $this->STORE(Active => 1); debatable as there's no "server side" here
$this;
}
@@ -2837,6 +2838,24 @@
check if $trace_flag_name is a driver specific trace flags and, if
not, then call the DBIs default parse_trace_flag().
+=item C<swap_inner_handle>
+
+ $rc = $h1->swap_inner_handle( $h2 );
+ $rc = $h1->swap_inner_handle( $h2, $allow_reparent );
+
+Brain transplants for handles. You don't need to know about this
+unless you want to become a handle surgeon.
+
+A DBI handle is a reference to a tied hash. A tied hash has an
+I<inner> hash that actually holds the contents. The swap_inner_handle()
+method swaps the inner hashes between two handles. The $h1 and $h2
+handles still point to the same tied hashes, but what those hashes
+are tied to has been swapped. In effect $h1 I<becomes> $h2 and
+vice-versa. This is powerful stuff. Use with care.
+
+As a small safety measure, the two handles, $h1 and $h2, have to
+share the same parent unless $allow_reparent is true.
+
=back
Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs (original)
+++ dbi/trunk/DBI.xs Fri Oct 1 16:37:25 2004
@@ -1587,10 +1587,6 @@
case 'D':
if (keylen==8 && strEQ(key, "Database")) {
- /* this is here but is, sadly, not called because
- * not-preloading them into the handle attrib cache caused
- * wierdness in t/proxy.t that I never got to the bottom
- * of. One day maybe. */
D_imp_from_child(imp_dbh, imp_dbh_t, imp_xxh);
valuesv = newRV((SV*)DBIc_MY_H(imp_dbh));
cacheit = FALSE; /* else creates ref loop */
@@ -3990,6 +3986,15 @@
void
+DESTROY(h)
+ SV * h
+ CODE:
+ /* DESTROY defined here just to avoid AUTOLOAD */
+ h = h;
+ ST(0) = &sv_undef;
+
+
+void
STORE(h, keysv, valuesv)
SV * h
SV * keysv
@@ -4135,6 +4140,46 @@
ST(0) = sv_2mortal(newSViv(-1));
+void
+swap_inner_handle(rh1, rh2, allow_reparent=0)
+ SV * rh1
+ SV * rh2
+ IV allow_reparent
+ CODE:
+ {
+ D_impdata(imp_xxh1, imp_xxh_t, rh1);
+ D_impdata(imp_xxh2, imp_xxh_t, rh2);
+ SV *h1i = dbih_inner(rh1, "swap_inner_handle");
+ SV *h2i = dbih_inner(rh2, "swap_inner_handle");
+ SV *h1 = (rh1 == h1i) ? (SV*)DBIc_MY_H(imp_xxh1) : SvRV(rh1);
+ SV *h2 = (rh2 == h2i) ? (SV*)DBIc_MY_H(imp_xxh2) : SvRV(rh2);
+ if (DBIc_TYPE(imp_xxh1) != DBIc_TYPE(imp_xxh2)) {
+ char buf[99];
+ sprintf(buf, "Can't swap_inner_handle between %sh and %sh",
+ dbih_htype_name(DBIc_TYPE(imp_xxh1)),
dbih_htype_name(DBIc_TYPE(imp_xxh2)));
+ DBIh_SET_ERR_CHAR(rh1, imp_xxh1, "1", 1, buf, Nullch, Nullch);
+ XSRETURN_NO;
+ }
+ if (!allow_reparent && DBIc_PARENT_COM(imp_xxh1) != DBIc_PARENT_COM(imp_xxh2)) {
+ DBIh_SET_ERR_CHAR(rh1, imp_xxh1, "1", 1,
+ "Can't swap_inner_handle with handle from different parent",
+ Nullch, Nullch);
+ XSRETURN_NO;
+ }
+ SvREFCNT_inc(h1i);
+ SvREFCNT_inc(h2i);
+ sv_unmagic(h1, 'P'); /* untie(%$h1) */
+ sv_unmagic(h2, 'P'); /* untie(%$h2) */
+ sv_magic(h1, h2i, 'P', Nullch, 0); /* tie %$h1, $h2i */
+ DBIc_MY_H(imp_xxh2) = (HV*)h1;
+ sv_magic(h2, h1i, 'P', Nullch, 0); /* tie %$h2, $h1i */
+ DBIc_MY_H(imp_xxh1) = (HV*)h2;
+ SvREFCNT_dec(h1i);
+ SvREFCNT_dec(h2i);
+ ST(0) = &sv_yes;
+ }
+
+
MODULE = DBI PACKAGE = DBD::_mem::common
void
Modified: dbi/trunk/ToDo
==============================================================================
--- dbi/trunk/ToDo (original)
+++ dbi/trunk/ToDo Fri Oct 1 16:37:25 2004
@@ -435,8 +435,6 @@
Add hook to DBI::DBD to write a myconfig.txt file into the
source directory containing key driver and config info.
-Add $h->swap_internal_handle($other_h)
-
dbish - state AutoCommit status clearly at connect time.
(And try to set AutoCommit off in eval?)
test shell "/connect user pass" etc
Modified: dbi/trunk/lib/DBD/NullP.pm
==============================================================================
--- dbi/trunk/lib/DBD/NullP.pm (original)
+++ dbi/trunk/lib/DBD/NullP.pm Fri Oct 1 16:37:25 2004
@@ -64,7 +64,7 @@
# or fetch and cache attribute values too expensive to prefetch.
return 1 if $attrib eq 'AutoCommit';
# else pass up to DBI to handle
- return $dbh->DBD::_::db::FETCH($attrib);
+ return $dbh->SUPER::FETCH($attrib);
}
sub STORE {
@@ -75,10 +75,13 @@
return 1 if $value; # is already set
croak("Can't disable AutoCommit");
}
- return $dbh->DBD::_::db::STORE($attrib, $value);
+ return $dbh->SUPER::STORE($attrib, $value);
+ }
+
+ sub disconnect {
+ shift->STORE(Active => 0);
}
- sub DESTROY { undef }
}
@@ -112,17 +115,16 @@
my ($sth, $attrib) = @_;
# would normally validate and only fetch known attributes
# else pass up to DBI to handle
- return $sth->DBD::_::st::FETCH($attrib);
+ return $sth->SUPER::FETCH($attrib);
}
sub STORE {
my ($sth, $attrib, $value) = @_;
# would normally validate and only store known attributes
# else pass up to DBI to handle
- return $sth->DBD::_::st::STORE($attrib, $value);
+ return $sth->SUPER::STORE($attrib, $value);
}
- sub DESTROY { undef }
}
1;
Modified: dbi/trunk/lib/DBI/PurePerl.pm
==============================================================================
--- dbi/trunk/lib/DBI/PurePerl.pm (original)
+++ dbi/trunk/lib/DBI/PurePerl.pm Fri Oct 1 16:37:25 2004
@@ -576,6 +576,13 @@
package
DBD::_::common;
+sub swap_inner_handle {
+ my ($h1, $h2) = @_;
+ # can't make this work till we can get the outer handle from the inner one
+ # probably via a WeakRef
+ return $h1->set_err(1, "swap_inner_handle not currently supported by
DBI::PurePerl");
+}
+
sub trace { # XXX should set per-handle level, not global
my ($h, $level, $file) = @_;
$level = $h->parse_trace_flags($level)
Modified: dbi/trunk/t/02dbidrv.t
==============================================================================
--- dbi/trunk/t/02dbidrv.t (original)
+++ dbi/trunk/t/02dbidrv.t Fri Oct 1 16:37:25 2004
@@ -1,4 +1,5 @@
#!perl -w
+# vim:sw=4:ts=8
use strict;
@@ -24,10 +25,10 @@
## main Test Driver Package
{
- package DBD::Test;
+ package DBD::Test;
use strict;
- use warnings;
+ use warnings;
my $drh = undef;
@@ -86,8 +87,6 @@
Test::More::cmp_ok($DBD::Test::db::imp_data_size, '==', 0, '... check
DBD::Test::db::imp_data_size to avoid typo');
- sub DESTROY { 1 }
-
sub do {
my $h = shift;
@@ -128,6 +127,10 @@
push @ds, "dbi:Test:baz";
return @ds;
}
+
+ sub disconnect {
+ shift->STORE(Active => 0);
+ }
}
## ----------------------------------------------------------------------------
@@ -181,6 +184,8 @@
ok($dbh->do('dummy'), '... this will trigger more driver internal tests above
in DBD::Test::db');
+ $dbh->disconnect;
+
$drh->set_err("41", "foo 41 drh");
cmp_ok($drh->err, '==', 41, '... checking Driver handle err set with set_err
method');
$dbh->set_err("42", "foo 42 dbh");
Modified: dbi/trunk/t/03handle.t
==============================================================================
--- dbi/trunk/t/03handle.t (original)
+++ dbi/trunk/t/03handle.t Fri Oct 1 16:37:25 2004
@@ -2,7 +2,7 @@
use strict;
-use Test::More tests => 101;
+use Test::More tests => 124;
## ----------------------------------------------------------------------------
## 03handle.t - tests handles
@@ -62,68 +62,115 @@
ok($sth1->{Active}, '... our first statment is Active');
- # use this to check that we are warned
- my $warn = 0;
- local $SIG{__WARN__} = sub { ++$warn if $_[0] =~ /still active/ };
-
- my $sth2 = $dbh->prepare_cached($sql);
- isa_ok($sth2, 'DBI::st');
-
- is($sth1, $sth2, '... prepare_cached returned the same statement handle');
- cmp_ok($warn,'==', 1, '... we got warned about our first statement handle being
still active');
-
- ok(!$sth1->{Active}, '... our first statment is no longer Active since we
re-prepared it');
+ {
+ my $warn = 0; # use this to check that we are warned
+ local $SIG{__WARN__} = sub { ++$warn if $_[0] =~ /still active/i };
+
+ my $sth2 = $dbh->prepare_cached($sql);
+ isa_ok($sth2, 'DBI::st');
+
+ is($sth1, $sth2, '... prepare_cached returned the same statement handle');
+ cmp_ok($warn,'==', 1, '... we got warned about our first statement handle
being still active');
+
+ ok(!$sth1->{Active}, '... our first statment is no longer Active since we
re-prepared it');
+
+ my $sth3 = $dbh->prepare_cached($sql, { foo => 1 });
+ isa_ok($sth3, 'DBI::st');
+
+ isnt($sth1, $sth3, '... prepare_cached returned a different statement handle
now');
+ cmp_ok(scalar(keys(%{$ck})), '==', 2, '... there are two CachedKids');
+ ok(eq_set(
+ [ values %{$ck} ],
+ [ $sth1, $sth3 ]
+ ),
+ '... both statment handles should be in the CachedKids');
+
+ ok($sth1->execute("."), '... executing first statement handle again');
+ ok($sth1->{Active}, '... first statement handle is now active again');
+
+ my $sth4 = $dbh->prepare_cached($sql, undef, 3);
+ isa_ok($sth4, 'DBI::st');
+
+ isnt($sth1, $sth4, '... our fourth statement handle is not the same as our
first');
+ ok($sth1->{Active}, '... first statement handle is still active');
+
+ cmp_ok(scalar(keys(%{$ck})), '==', 2, '... there are two CachedKids');
+ ok(eq_set(
+ [ values %{$ck} ],
+ [ $sth2, $sth4 ]
+ ),
+ '... second and fourth statment handles should be in the CachedKids');
+
+ $sth1->finish;
+ ok(!$sth1->{Active}, '... first statement handle is no longer active');
+
+ ok($sth4->execute("."), '... fourth statement handle executed properly');
+ ok($sth4->{Active}, '... fourth statement handle is Active');
+
+ my $sth5 = $dbh->prepare_cached($sql, undef, 1);
+ isa_ok($sth5, 'DBI::st');
+
+ cmp_ok($warn, '==', 1, '... we still only got one warning');
+
+ is($sth4, $sth5, '... fourth statement handle and fifth one match');
+ ok(!$sth4->{Active}, '... fourth statement handle is not Active');
+ ok(!$sth5->{Active}, '... fifth statement handle is not Active (shouldnt be
its the same as fifth)');
+
+ cmp_ok(scalar(keys(%{$ck})), '==', 2, '... there are two CachedKids');
+ ok(eq_set(
+ [ values %{$ck} ],
+ [ $sth2, $sth5 ]
+ ),
+ '... second and fourth/fifth statment handles should be in the CachedKids');
+ }
- my $sth3 = $dbh->prepare_cached($sql, { foo => 1 });
- isa_ok($sth3, 'DBI::st');
+ SKIP: {
+ skip "become() not supported under DBI::PurePerl", 23 if $DBI::PurePerl;
- isnt($sth1, $sth3, '... prepare_cached returned a different statement handle
now');
- cmp_ok(scalar(keys(%{$ck})), '==', 2, '... there are two CachedKids');
- ok(eq_set(
- [ values %{$ck} ],
- [ $sth1, $sth3 ]
- ),
- '... both statment handles should be in the CachedKids');
+ my $sth6 = $dbh->prepare($sql);
+ $sth6->execute(".");
- ok($sth1->execute("."), '... executing first statement handle again');
- ok($sth1->{Active}, '... first statement handle is now active again');
-
- my $sth4 = $dbh->prepare_cached($sql, undef, 3);
- isa_ok($sth4, 'DBI::st');
-
- isnt($sth1, $sth4, '... our fourth statement handle is not the same as our
first');
- ok($sth1->{Active}, '... first statement handle is still active');
-
- cmp_ok(scalar(keys(%{$ck})), '==', 2, '... there are two CachedKids');
- ok(eq_set(
- [ values %{$ck} ],
- [ $sth2, $sth4 ]
- ),
- '... second and fourth statment handles should be in the CachedKids');
-
- $sth1->finish;
- ok(!$sth1->{Active}, '... first statement handle is no longer active');
+ ok( $sth6->{Active}, '... sixth statement handle is active');
+ ok(!$sth1->{Active}, '... first statement handle is not active');
- ok($sth4->execute("."), '... fourth statement handle executed properly');
- ok($sth4->{Active}, '... fourth statement handle is Active');
-
- my $sth5 = $dbh->prepare_cached($sql, undef, 1);
- isa_ok($sth5, 'DBI::st');
-
- is($sth4, $sth5, '... fourth statement handle and fifth one match');
- ok(!$sth4->{Active}, '... fourth statement handle is not Active');
- ok(!$sth5->{Active}, '... fifth statement handle is not Active (shouldnt be its
the same as fifth)');
-
- cmp_ok(scalar(keys(%{$ck})), '==', 2, '... there are two CachedKids');
- ok(eq_set(
- [ values %{$ck} ],
- [ $sth2, $sth5 ]
- ),
- '... second and fourth/fifth statment handles should be in the CachedKids');
+ ok($sth1->swap_inner_handle($sth6), '... first statement handle becomes the
sixth');
+ ok(!$sth6->{Active}, '... sixth statement handle is now not active');
+ ok( $sth1->{Active}, '... first statement handle is now active again');
+
+ ok($sth1->swap_inner_handle($sth6), '... first statement handle becomes the
sixth');
+ ok( $sth6->{Active}, '... sixth statement handle is active');
+ ok(!$sth1->{Active}, '... first statement handle is not active');
+
+ ok($sth1->swap_inner_handle($sth6), '... first statement handle becomes the
sixth');
+ ok(!$sth6->{Active}, '... sixth statement handle is now not active');
+ ok( $sth1->{Active}, '... first statement handle is now active again');
+
+ $sth1->{PrintError} = 0;
+ ok(!$sth1->swap_inner_handle($dbh), '... can not swap a sth with a dbh');
+ cmp_ok( $sth1->errstr, 'eq', "Can't swap_inner_handle between sth and dbh");
+
+ ok($sth1->swap_inner_handle($sth6), '... first statement handle becomes the
sixth');
+ ok( $sth6->{Active}, '... sixth statement handle is active');
+ ok(!$sth1->{Active}, '... first statement handle is not active');
+
+ $sth6->finish;
+
+ ok(my $dbh_nullp = DBI->connect("dbi:NullP:"));
+ ok(my $sth7 = $dbh_nullp->prepare(""));
+
+ $sth1->{PrintError} = 0;
+ ok(!$sth1->swap_inner_handle($sth7), "... can't swap_inner_handle with handle
from different parent");
+ cmp_ok( $sth1->errstr, 'eq', "Can't swap_inner_handle with handle from
different parent");
+
+ cmp_ok( $sth1->{Database}{Driver}{Name}, 'eq', "ExampleP" );
+ ok( $sth1->swap_inner_handle($sth7,1), "... can swap to different parent if
forced");
+ cmp_ok( $sth1->{Database}{Driver}{Name}, 'eq', "NullP" );
+
+ $dbh_nullp->disconnect;
+ }
- cmp_ok($warn, '==', 1, '... we still only got one warning');
$dbh->disconnect;
-
+
SKIP: {
skip "Kids and ActiveKids attributes not supported under DBI::PurePerl", 2 if
$DBI::PurePerl;
@@ -302,6 +349,8 @@
# I don't know why this warning has the "(perhaps ...)" suffix, it shouldn't:
# Can't locate object method "nonesuch" via package "DBI::db" (perhaps you forgot
to load "DBI::db"?)
eval { ref($dbh)->nonesuch; };
+
+ $dbh->disconnect;
};
SKIP: {
Modified: dbi/trunk/t/15array.t
==============================================================================
--- dbi/trunk/t/15array.t (original)
+++ dbi/trunk/t/15array.t Fri Oct 1 16:37:25 2004
@@ -224,6 +224,8 @@
is( $sth->errstr, 'Value for parameter 2 must be a scalar or an arrayref, not a
HASH', '... errstr is as expected');
ok(!defined $sth->bind_param_array(":foo", [ qw(a b c) ]), '... bind_param_array
should return undef');
-is( $sth->errstr, "Can't use named placeholders for non-driver supported
bind_param_array", '... errstr is as expected');
+is( $sth->errstr, "Can't use named placeholder ':foo' for non-driver supported
bind_param_array", '... errstr is as expected');
+
+$dbh->disconnect;
1;