Author: timbo
Date: Tue Jan 23 15:42:34 2007
New Revision: 8686
Added:
dbi/trunk/lib/DBD/Forward/
dbi/trunk/lib/DBD/Forward/Transport/
dbi/trunk/lib/DBD/Forward/Transport/Base.pm
dbi/trunk/lib/DBD/Forward/Transport/null.pm
dbi/trunk/t/12quote.t
dbi/trunk/t/13taint.t
Removed:
dbi/trunk/lib/DBI/Forward/Transport/null.pm
Modified:
dbi/trunk/Changes
dbi/trunk/DBI.pm
dbi/trunk/DBI.xs
dbi/trunk/MANIFEST
dbi/trunk/lib/DBD/ExampleP.pm
dbi/trunk/lib/DBD/Forward.pm
dbi/trunk/lib/DBD/NullP.pm
dbi/trunk/lib/DBI/DBD.pm
dbi/trunk/lib/DBI/Forward/Execute.pm
dbi/trunk/lib/DBI/Forward/Transport/Base.pm
dbi/trunk/lib/DBI/PurePerl.pm
dbi/trunk/t/03handle.t
dbi/trunk/t/08keeperr.t
dbi/trunk/t/09trace.t
dbi/trunk/t/10examp.t
dbi/trunk/t/40profile.t
dbi/trunk/t/42prof_data.t
dbi/trunk/t/50dbm.t
dbi/trunk/t/72childhandles.t
Log:
Next big chunk of DBD::Forward development.
All tests pass undef DBD::Forward except t/10example.t
Also t/05thrclone reports leaked scalars under threaded perl.
Other changes:
+ Fixed rare error when profiling access to $DBI::err etc tied variables.
+ Changed setting trace file to no longer write "Trace file set" to new file.
+ Changed 'handle cleared whilst still active' warning for dbh
+ to only be given for dbh that have active sth or are not AutoCommit.
+ Changed take_imp_data to call finish on any Active child sth.
+ Added new DBD::Forward 'stateless proxy' driver and framework,
+ and the DBI test suite is now also executed via DBD::Forward.
+ Added ability for drivers to implement func() method
+ so proxy drivers can proxy the func method itself.
Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes (original)
+++ dbi/trunk/Changes Tue Jan 23 15:42:34 2007
@@ -14,11 +14,23 @@
Fixed to compile for perl built with -DNO_MATHOMS thanks to Jerry D. Hedden.
Fixed to work for bleadperl (r29544) thanks to Nicholas Clark.
Users of Perl >= 5.9.5 will require DBI >= 1.54.
- Changed t/40profile.t to skip tests for perl < 5.8.0.
- Updated DBI::DBD docs for driver authors thanks to Ammon Riley.
+ Fixed rare error when profiling access to $DBI::err etc tied variables.
+ Changed t/40profile.t to skip tests for perl < 5.8.0.
+ Changed setting trace file to no longer write "Trace file set" to new file.
+ Changed 'handle cleared whilst still active' warning for dbh
+ to only be given for dbh that have active sth or are not AutoCommit.
+ Changed take_imp_data to call finish on any Active child sth.
+ Updated DBI::DBD docs for driver authors thanks to Ammon Riley
+ and Dean Arnold.
+
+ Added new DBD::Forward 'stateless proxy' driver and framework,
+ and the DBI test suite is now also executed via DBD::Forward.
+ Added ability for drivers to implement func() method
+ so proxy drivers can proxy the func method itself.
Added SQL_BIGINT type code (resolved to the ODBC/JDBC value (-5))
- Updated trace to support filehandle argument.
+ Added ability for trace() to support filehandle argument,
+ including tracing into a string, thanks to Dean Arnold.
=head2 Changes in DBI 1.53 (svn rev 7995), 31st October 2006
Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm (original)
+++ dbi/trunk/DBI.pm Tue Jan 23 15:42:34 2007
@@ -577,8 +577,9 @@
my $proxy = 'Proxy';
if ($dbi_autoproxy =~ s/^dbi:(\w*?)(?:\((.*?)\))?://i) {
$proxy = $1;
- my $attr_spec = $2 || '';
- $driver_attrib_spec = ($driver_attrib_spec) ?
"$driver_attrib_spec,$attr_spec" : $attr_spec;
+ $driver_attrib_spec = join ",",
+ ($driver_attrib_spec) ? $driver_attrib_spec : (),
+ ($2 ) ? $2 : ();
}
$dsn = "$dbi_autoproxy;dsn=dbi:$driver:$dsn";
$driver = $proxy;
Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs (original)
+++ dbi/trunk/DBI.xs Tue Jan 23 15:42:34 2007
@@ -673,7 +673,6 @@
close_trace_file();
}
DBILOGFP = fp;
- PerlIO_printf(DBILOGFP," Trace file set\n");
/* if this line causes your compiler or linker to choke */
/* then just comment it out, it's not essential. */
PerlIO_setlinebuf(fp); /* force line buffered output */
@@ -1297,9 +1296,14 @@
}
}
- if (DBIc_ACTIVE(imp_xxh)) { /* bad news */
- warn("DBI handle 0x%x cleared whilst still active",
(unsigned)DBIc_MY_H(imp_xxh));
- dump = TRUE;
+ if (DBIc_ACTIVE(imp_xxh)) { /* bad news, potentially */
+ /* warn for sth, warn for dbh only if it has active sth or isn't
AutoCommit */
+ if (DBIc_TYPE(imp_xxh) >= DBIt_ST
+ || (DBIc_ACTIVE_KIDS(imp_xxh) || !DBIc_has(imp_xxh,
DBIcf_AutoCommit))
+ ) {
+ warn("DBI handle 0x%x cleared whilst still active",
(unsigned)DBIc_MY_H(imp_xxh));
+ dump = TRUE;
+ }
}
/* check that the implementor has done its own housekeeping */
@@ -1697,8 +1701,19 @@
}
else if (htype==DBIt_ST && strEQ(key, "NUM_OF_FIELDS")) {
D_imp_sth(h);
- if (DBIc_NUM_FIELDS(imp_sth) > 0) /* don't change NUM_FIELDS! */
- croak("NUM_OF_FIELDS already set to %d", DBIc_NUM_FIELDS(imp_sth));
+ if (DBIc_NUM_FIELDS(imp_sth) > 0 /* don't change NUM_FIELDS! */
+ && DBIc_ACTIVE(imp_sth) /* if sth is Active */
+ ) {
+ croak("Can't change NUM_OF_FIELDS (already set to %d)",
DBIc_NUM_FIELDS(imp_sth));
+ }
+ if (DBIc_NUM_FIELDS(imp_sth) > 0
+ && SvIV(valuesv) != DBIc_NUM_FIELDS(imp_sth)
+ && DBIc_TRACE_LEVEL(imp_sth)
+ && DBIc_FIELDS_AV(imp_sth)
+ ) {
+ PerlIO_printf(DBILOGFP,"Warning: changing NUM_OF_FIELDS (from %d
to %d) after row buffer already allocated",
+ SvIV(valuesv), DBIc_NUM_FIELDS(imp_sth));
+ }
DBIc_NUM_FIELDS(imp_sth) = SvIV(valuesv);
cacheit = 1;
}
@@ -2301,7 +2316,6 @@
return;
h_hv = (HV*)SvRV(dbih_inner(h, "dbi_profile"));
- /*h_hv = (SvROK(h)) ? (HV*)SvRV(h) : (HV*)h; */
profile = *hv_fetch(h_hv, "Profile", 7, 1);
if (profile && SvMAGICAL(profile))
@@ -2322,9 +2336,9 @@
statement_pv = SvPV_nolen(statement_sv);
if (DBIc_DBISTATE(imp_xxh)->debug >= 4)
- PerlIO_printf(DBIc_LOGPIO(imp_xxh), "dbi_profile %s %f q{%s}\n",
- neatsvpv((SvTYPE(method)==SVt_PVCV) ? (SV*)CvGV(method) :
method, 0),
- ti, neatsvpv(statement_sv,0));
+ PerlIO_printf(DBIc_LOGPIO(imp_xxh), " dbi_profile %s %fs %s\n",
+ neatsvpv((SvTYPE(method)==SVt_PVCV) ? (SV*)CvGV(method) : method,
0),
+ ti, neatsvpv(statement_sv,0));
dest_node = _profile_next_node(profile, "Data");
@@ -2953,6 +2967,18 @@
imp_msv = (SV*)gv_fetchmethod_autoload(DBIc_IMP_STASH(imp_xxh),
meth_name, FALSE);
+ /* if method was a 'func' then try falling back to real 'func' method
*/
+ if (!imp_msv && (ima_flags & IMA_FUNC_REDIRECT)) {
+ imp_msv = (SV*)gv_fetchmethod_autoload(DBIc_IMP_STASH(imp_xxh),
"func", FALSE);
+ if (imp_msv) {
+ /* driver does have func method so undo the earlier 'func'
stack changes */
+ PUSHs(sv_2mortal(newSVpv(meth_name,0)));
+ PUTBACK;
+ ++items;
+ meth_name = "func";
+ }
+ }
+
if (trace_level >= 2) {
PerlIO *logfp = DBILOGFP;
/* Full pkg method name (or just meth_name for ANON CODE) */
@@ -4110,8 +4136,10 @@
}
if (trace)
PerlIO_printf(DBILOGFP," <- $DBI::%s= %s\n", meth,
neatsvpv(ST(0),0));
- if (profile_t1)
- dbi_profile(DBI_LAST_HANDLE, imp_xxh, &sv_undef, (SV*)cv, profile_t1,
dbi_time());
+ if (profile_t1) {
+ SV *h = sv_2mortal(newRV(DBI_LAST_HANDLE));
+ dbi_profile(h, imp_xxh, &sv_undef, (SV*)cv, profile_t1, dbi_time());
+ }
MODULE = DBI PACKAGE = DBD::_::db
@@ -4148,8 +4176,8 @@
*
* If the drivers imp_xxh_t structure contains SV*'s, or other interpreter
* specific items, they should be freed by the drivers own take_imp_data()
- * method. The drivers take_imp_data() method (or Driver.xst code) can
then call
- * SUPER::take_imp_data() to finalize the removal of the imp_xxh_t
structure.
+ * method before it then calls SUPER::take_imp_data() to finalize removal
+ * of the imp_xxh_t structure.
*
* The driver needs to view the take_imp_data method as being nearly the
* same as disconnect+DESTROY only not actually calling the database API to
@@ -4158,9 +4186,8 @@
* in a 'clean' state such that if the drivers own DESTROY method was
* called it would be able to properly handle the contents of the
* structure. This is important in case a new handle created using this
- * imp_data, possibly in a new thread, might end up being DESTROY's before
- * the driver has had a chance to 're-setup' the data. See
- * dbih_setup_handle()
+ * imp_data, possibly in a new thread, might end up being DESTROY'd before
+ * the driver has had a chance to 're-setup' the data. See
dbih_setup_handle()
*
* All the above relates to the 'typical use case' for a compiled driver.
* For a pure-perl driver using a socket pair, for example, the drivers
@@ -4182,7 +4209,7 @@
/* 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.
+ * So we do our best to neautralize them (finish & rebless)
*/
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);
@@ -4193,6 +4220,12 @@
for (kidslots = AvFILL(av); kidslots >= 0; --kidslots) {
SV **hp = av_fetch(av, kidslots, FALSE);
if (hp && SvROK(*hp) && SvMAGICAL(SvRV(*hp))) {
+ PUSHMARK(sp);
+ XPUSHs(*hp);
+ PUTBACK;
+ perl_call_method("finish", G_SCALAR|G_DISCARD);
+ SPAGAIN;
+ PUTBACK;
sv_unmagic(SvRV(*hp), 'P'); /* untie */
sv_bless(*hp, zombie_stash); /* neutralise */
}
Modified: dbi/trunk/MANIFEST
==============================================================================
--- dbi/trunk/MANIFEST (original)
+++ dbi/trunk/MANIFEST Tue Jan 23 15:42:34 2007
@@ -10,8 +10,8 @@
Perl.xs Test harness (currently) for Driver.xst
README
Roadmap.pod Planned changes and enhancements for the DBI
-TODO_2005.txt Old (but still mostly relevant) occasional
random notes about what's missing
TASKS.pod Want to help? These things need doing...
+TODO_2005.txt Old (but still mostly relevant) occasional
random notes about what's missing
dbd_xsh.h Prototypes for standard Driver.xst interface
dbi_sql.h Definitions based on SQL CLI / ODBC (#inc'd by
DBIXS.h)
dbipport.h Perl portability macros (from Devel::PPort)
@@ -24,6 +24,9 @@
lib/DBD/DBM.pm A driver for DBM files (uses DBD::File)
lib/DBD/ExampleP.pm A very simple example Driver module
lib/DBD/File.pm A driver base class for simple drivers
+lib/DBD/Forward.pm DBD::Forward 'stateless proxy' driver
+lib/DBD/Forward/Transport/Base.pm Base class for DBD::Forward driver transport
classes
+lib/DBD/Forward/Transport/null.pm DBD::Forward transport that executes locally
(for testing)
lib/DBD/NullP.pm An empty example Driver module
lib/DBD/Proxy.pm Proxy driver
lib/DBD/Sponge.pm A driver for fake cursors (precached data)
@@ -34,6 +37,10 @@
lib/DBI/DBD.pm Some basic help for people writing DBI drivers
lib/DBI/DBD/Metadata.pm Metadata tools for people writing DBI
drivers
lib/DBI/FAQ.pm The DBI FAQ in module form for perldoc
+lib/DBI/Forward/Execute.pm Execution logic for DBD::Forward server
+lib/DBI/Forward/Request.pm Request object from DBD::Forward
+lib/DBI/Forward/Response.pm Response object for DBD::Forward
+lib/DBI/Forward/Transport/Base.pm Base class for DBD::Forward server transport
classes
lib/DBI/Profile.pm Manage DBI usage profile data
lib/DBI/ProfileData.pm
lib/DBI/ProfileDumper.pm
@@ -55,6 +62,8 @@
t/09trace.t
t/10examp.t
t/11fetch.t
+t/12quote.t
+t/13taint.t
t/14utf8.t
t/15array.t
t/19fhtrace.t
@@ -69,6 +78,7 @@
t/70callbacks.t
t/72childhandles.t
t/80proxy.t
+t/85forward.t
t/pod.t
test.pl Assorted informal tests, including tests for
memory leaks
typemap
Modified: dbi/trunk/lib/DBD/ExampleP.pm
==============================================================================
--- dbi/trunk/lib/DBD/ExampleP.pm (original)
+++ dbi/trunk/lib/DBD/ExampleP.pm Tue Jan 23 15:42:34 2007
@@ -60,7 +60,11 @@
my($drh, $dbname, $user, $auth)= @_;
my ($outer, $dbh) = DBI::_new_dbh($drh, { Name => $dbname });
$dbh->STORE('Active', 1);
- $dbh->{examplep_get_info} = {};
+ $dbh->{examplep_get_info} = {
+ 29 => '"', # SQL_IDENTIFIER_QUOTE_CHAR
+ 41 => '.', # SQL_CATALOG_NAME_SEPARATOR
+ 114 => 1, # SQL_CATALOG_LOCATION
+ };
$dbh->{Name} = $dbname;
return $outer;
}
@@ -329,8 +333,6 @@
sub fetch {
my $sth = shift;
- my $dh = $sth->{dbd_datahandle}
- or return $sth->set_err(1, "fetch without successful execute");
my $dir = $sth->{dbd_dir};
my %s;
@@ -346,6 +348,8 @@
$time, $time, $time, 512, 2, "file$num")
}
else { # normal mode
+ my $dh = $sth->{dbd_datahandle}
+ or return $sth->set_err(1, "fetch without successful execute");
my $f = readdir($dh);
unless ($f) {
$sth->finish;
@@ -409,7 +413,7 @@
sub DESTROY {
my $sth = shift;
- $sth->finish if $sth->SUPER::FETCH('Active');
+ #$sth->finish if $sth->SUPER::FETCH('Active');
}
*parse_trace_flag = \&DBD::ExampleP::db::parse_trace_flag;
Modified: dbi/trunk/lib/DBD/Forward.pm
==============================================================================
--- dbi/trunk/lib/DBD/Forward.pm (original)
+++ dbi/trunk/lib/DBD/Forward.pm Tue Jan 23 15:42:34 2007
@@ -17,10 +17,13 @@
# You may distribute under the terms of either the GNU General Public
# License or the Artistic License, as specified in the Perl README file.
+# XXX track installed_methods and install proxies on client side after connect?
+
# attributes we'll allow local STORE
our %xxh_local_store_attrib = map { $_=>1 } qw(
Active
CachedKids
+ Callbacks
ErrCount Executed
FetchHashKeyName
HandleError HandleSetErr
@@ -28,11 +31,13 @@
PrintError PrintWarn
Profile
RaiseError
+ RootClass
ShowErrorStatement
Taint TaintIn TaintOut
TraceLevel
Warn
dbi_connect_closure
+ dbi_quote_identifier_cache
);
our $drh = undef; # holds driver handle once initialised
@@ -100,7 +105,7 @@
my $transport_class = $dsn_attr{fwd_transport}
or return $drh->set_err(1, "No transport= argument in
'$orig_dsn'");
- $transport_class = "DBI::Forward::Transport::$dsn_attr{fwd_transport}"
+ $transport_class = "DBD::Forward::Transport::$dsn_attr{fwd_transport}"
unless $transport_class =~ /::/;
eval "require $transport_class"
or return $drh->set_err(1, "Error loading $transport_class: $@");
@@ -122,8 +127,17 @@
fwd_request => $fwd_request,
});
- # store and delete the attributes before marking connection Active
- $dbh->STORE($_ => delete $attr->{$_}) for keys %$attr;
+ $dbh->STORE(Active => 0); # mark as inactive temporarily for STORE
+
+ # Store and delete the attributes before marking connection Active
+ # Leave RaiseError & PrintError in %$attr so DBI's connect can
+ # act on them if the connect fails
+ $dbh->STORE($_ => delete $attr->{$_})
+ for grep { !m/^(RaiseError|PrintError)$/ } keys %$attr;
+
+ # test the connection XXX control via a policy later
+ $dbh->fwd_dbh_method('ping', undef)
+ or return;
$dbh->STORE(Active => 1);
@@ -149,7 +163,11 @@
my $transport = $dbh->{fwd_trans}
or return $dbh->set_err(1, "Not connected (no transport)");
- my $response = $transport->execute($request);
+
+ eval { $transport->transmit_request($request) }
+ or return $dbh->set_err(1, "transmit_request failed: $@");
+
+ my $response = $transport->receive_response;
$dbh->{fwd_response} = $response;
@@ -162,9 +180,11 @@
# Methods that should be forwarded
# XXX get_info? special sub to lazy-cache individual values
for my $method (qw(
- do data_sources
+ data_sources
table_info column_info primary_key_info foreign_key_info
statistics_info
type_info_all get_info
+ parse_trace_flags parse_trace_flag
+ func
)) {
no strict 'refs';
*$method = sub { return shift->fwd_dbh_method($method, undef, @_) }
@@ -181,6 +201,13 @@
# for quote we rely on the default method + type_info_all
# for quote_identifier we rely on the default method + get_info
+ sub do {
+ my $dbh = shift;
+ delete $dbh->{Statement}; # avoid "Modification of non-creatable hash
value attempted"
+ $dbh->{Statement} = $_[0]; # for profiling and ShowErrorStatement
+ return $dbh->fwd_dbh_method('do', undef, @_);
+ }
+
sub ping {
my $dbh = shift;
# XXX local or remote - add policy attribute
@@ -197,8 +224,15 @@
sub FETCH {
my ($dbh, $attrib) = @_;
- # AutoCommit needs special handling
- return 1 if $attrib eq 'AutoCommit';
+ return 1 if $attrib eq 'AutoCommit'; # AutoCommit needs special handling
+
+ # forward driver-private attributes
+ if ($attrib =~ m/^[a-z]/) { # XXX policy? precache on connect?
+ my $value = $dbh->fwd_dbh_method('FETCH', undef, $attrib);
+ $dbh->{$attrib} = $value;
+ return $value;
+ }
+
# else pass up to DBI to handle
return $dbh->SUPER::FETCH($attrib);
}
@@ -206,7 +240,7 @@
sub STORE {
my ($dbh, $attrib, $value) = @_;
if ($attrib eq 'AutoCommit') {
- return 1 if $value;
+ return $dbh->SUPER::STORE($attrib => -901) if $value;
croak "Can't enable transactions when using DBD::Forward";
}
return $dbh->SUPER::STORE($attrib => $value)
@@ -286,7 +320,13 @@
$request->sth_method_calls($sth->{fwd_method_calls});
$request->sth_result_attr({});
- my $response = $sth->{fwd_trans}->execute($request);
+ my $transport = $sth->{fwd_trans}
+ or return $sth->set_err(1, "Not connected (no transport)");
+
+ eval { $transport->transmit_request($request) }
+ or return $sth->set_err(1, "transmit_request failed: $@");
+
+ my $response = $transport->receive_response;
$sth->{fwd_response} = $response;
$sth->{fwd_method_calls} = [];
@@ -296,6 +336,7 @@
$sth->more_results;
}
+ # set error/warn/info (after more_results as that'll clear err)
$sth->set_err($response->err, $response->errstr, $response->state);
return $response->rv;
@@ -324,7 +365,8 @@
if ($NUM_OF_FIELDS > 0) {
$sth->{fwd_current_rowset} = $rowset;
- $sth->{fwd_current_rowset_err} = [ $err, $errstr, $state ] if $err;
+ $sth->{fwd_current_rowset_err} = [ $err, $errstr, $state ]
+ if defined $err;
$sth->STORE(Active => 1) if $rowset;
}
@@ -336,13 +378,19 @@
my ($sth) = @_;
my $resultset = $sth->{fwd_current_rowset}
or return $sth->set_err( @{ $sth->{fwd_current_rowset_err} } );
- return shift @$resultset if @$resultset;
+ return $sth->_set_fbav(shift @$resultset) if @$resultset;
$sth->finish; # no more data so finish
return undef;
}
*fetch = \&fetchrow_arrayref; # alias
- # XXX fetchall_arrayref - for speed
+ sub fetchall_arrayref {
+ my ($sth) = @_;
+ my $resultset = $sth->{fwd_current_rowset}
+ or return $sth->set_err( @{ $sth->{fwd_current_rowset_err} } );
+ $sth->finish; # no more data so finish
+ return $resultset;
+ }
sub rows {
my $sth = shift;
@@ -358,9 +406,9 @@
or $attrib =~ m/^[a-z]/; # driver-private
# ignore values that aren't actually being changed
- my $prev = $sth->FETCH($attrib);
- return 1 if !defined $value && !defined $prev
- or defined $value && defined $prev && $value eq $prev;
+ #my $prev = $sth->FETCH($attrib);
+ #return 1 if !defined $value && !defined $prev
+ # or defined $value && defined $prev && $value eq $prev;
# sth attributes are set at connect-time - see connect()
Carp::carp("Can't alter \$sth->{$attrib}");
Added: dbi/trunk/lib/DBD/Forward/Transport/Base.pm
==============================================================================
--- (empty file)
+++ dbi/trunk/lib/DBD/Forward/Transport/Base.pm Tue Jan 23 15:42:34 2007
@@ -0,0 +1,39 @@
+package DBD::Forward::Transport::Base;
+
+use strict;
+use warnings;
+
+use Storable qw(freeze thaw);
+
+use base qw(Class::Accessor::Fast);
+
+our $debug = $ENV{DBD_FORWARD_DEBUG} || 0;
+
+
+__PACKAGE__->mk_accessors(qw(
+ fwd_dsn
+));
+
+
+sub freeze_data {
+ my ($self, $data) = @_;
+ $self->_dump(ref($data), $data) if $debug;
+ return freeze($data);
+}
+
+sub thaw_data {
+ my ($self, $frozen_data) = @_;
+ my $data = thaw($frozen_data);
+ $self->_dump(ref($data), $data) if $debug;
+ return $data;
+}
+
+
+sub _dump {
+ my ($self, $label, $data) = @_;
+ require Data::Dumper;
+ warn "$label=".Dumper($data);
+
+}
+
+1;
Added: dbi/trunk/lib/DBD/Forward/Transport/null.pm
==============================================================================
--- (empty file)
+++ dbi/trunk/lib/DBD/Forward/Transport/null.pm Tue Jan 23 15:42:34 2007
@@ -0,0 +1,48 @@
+package DBD::Forward::Transport::null;
+
+use strict;
+use warnings;
+
+use base qw(DBD::Forward::Transport::Base);
+
+use DBI::Forward::Execute qw(execute_request);
+
+__PACKAGE__->mk_accessors(qw(
+ fwd_pending_response
+));
+
+
+sub transmit_request {
+ my ($self, $request) = @_;
+
+ my $frozen_request = $self->freeze_data($request);
+
+ # ...
+ # the request is magically transported over to ... ourselves
+ # ...
+
+ my $response = execute_request( $self->thaw_data($frozen_request) );
+
+ # put response 'on the shelf' ready for receive_response()
+ $self->fwd_pending_response( $response );
+
+ return 1;
+}
+
+
+sub receive_response {
+ my $self = shift;
+
+ my $response = $self->fwd_pending_response;
+
+ my $frozen_response = $self->freeze_data($response);
+
+ # ...
+ # the response is magically transported back to ... ourselves
+ # ...
+
+ return $self->thaw_data($frozen_response);
+}
+
+
+1;
Modified: dbi/trunk/lib/DBD/NullP.pm
==============================================================================
--- dbi/trunk/lib/DBD/NullP.pm (original)
+++ dbi/trunk/lib/DBD/NullP.pm Tue Jan 23 15:42:34 2007
@@ -79,6 +79,8 @@
return $dbh->SUPER::STORE($attrib, $value);
}
+ sub ping { 1 }
+
sub disconnect {
shift->STORE(Active => 0);
}
Modified: dbi/trunk/lib/DBI/DBD.pm
==============================================================================
--- dbi/trunk/lib/DBI/DBD.pm (original)
+++ dbi/trunk/lib/DBI/DBD.pm Tue Jan 23 15:42:34 2007
@@ -3002,6 +3002,9 @@
pp => { name => "DBI::PurePerl",
add => [ 'local $ENV{DBI_PUREPERL} = 2;' ],
},
+ fw => { name => "DBD::Forward",
+ add => [ q{local $ENV{DBI_AUTOPROXY} =
'dbi:Forward:transport=null';} ],
+ },
mx => { name => "DBD::Multiplex",
add => [ q{local $ENV{DBI_AUTOPROXY} =
'dbi:Multiplex:';} ],
}
Modified: dbi/trunk/lib/DBI/Forward/Execute.pm
==============================================================================
--- dbi/trunk/lib/DBI/Forward/Execute.pm (original)
+++ dbi/trunk/lib/DBI/Forward/Execute.pm Tue Jan 23 15:42:34 2007
@@ -41,15 +41,36 @@
PrintError => 0,
RaiseError => 1,
});
- #$dbh->trace(1);
+ #$dbh->trace(0);
return $dbh;
}
+
sub _reset_dbh {
my ($dbh) = @_;
- $dbh->trace(0, \*STDERR);
+ $dbh->set_err(undef, undef); # clear any error state
+ #$dbh->trace(0, \*STDERR);
+}
+
+
+sub _new_response_with_err {
+ my ($rv) = @_;
+
+ my ($err, $errstr, $state) = ($DBI::err, $DBI::errstr, $DBI::state);
+ if ($@ and !$errstr || $@ !~ /^DBD::/) {
+ $err ||= 1;
+ $errstr = ($errstr) ? "$errstr; $@" : $@;
+ }
+ my $response = DBI::Forward::Response->new({
+ rv => $rv,
+ err => $err,
+ errstr => $errstr,
+ state => $state,
+ });
+ return $response;
}
+
sub execute_request {
my $request = shift;
my $response = eval {
@@ -78,18 +99,16 @@
: scalar $dbh->$meth(@$args);
[EMAIL PROTECTED];
};
- my $response = DBI::Forward::Response->new({
- rv => $rv,
- err => $DBI::err,
- errstr => $DBI::errstr,
- state => $DBI::state,
- });
- $response->last_insert_id = $dbh->last_insert_id( @{
$request->dbh_last_insert_id_args })
- if $dbh && $rv && $request->dbh_last_insert_id_args;
- _reset_dbh($dbh);
+ my $response = _new_response_with_err($rv);
+ if ($dbh) {
+ $response->last_insert_id = $dbh->last_insert_id( @{
$request->dbh_last_insert_id_args })
+ if $rv && $request->dbh_last_insert_id_args;
+ _reset_dbh($dbh);
+ }
return $response;
}
+
sub execute_sth_request {
my $request = shift;
my $dbh;
@@ -109,15 +128,10 @@
$sth->execute();
};
- my $response = DBI::Forward::Response->new({
- rv => $rv,
- err => $DBI::err,
- errstr => $DBI::errstr,
- state => $DBI::state,
- });
+ my $response = _new_response_with_err($rv);
# even if the eval failed we still want to gather attribute values
- my $resultset_list = eval {
+ my $resultset_list = $sth && eval {
my $attr_list = $request->sth_result_attr;
$attr_list = [ keys %$attr_list ] if ref $attr_list eq 'HASH';
my $rs_list = [];
@@ -133,11 +147,12 @@
# XXX would be nice to be able to support streaming of results
# which would reduce memory usage and latency for large results
- _reset_dbh($dbh);
+ _reset_dbh($dbh) if $dbh;
return $response;
}
+
sub fetch_result_set {
my ($sth, $extra_attr) = @_;
my %meta;
Modified: dbi/trunk/lib/DBI/Forward/Transport/Base.pm
==============================================================================
--- dbi/trunk/lib/DBI/Forward/Transport/Base.pm (original)
+++ dbi/trunk/lib/DBI/Forward/Transport/Base.pm Tue Jan 23 15:42:34 2007
@@ -1,4 +1,4 @@
-package DBI::Forward::Transport::Base;
+package DBD::Forward::Transport::Base;
use strict;
use warnings;
@@ -7,14 +7,32 @@
use base qw(Class::Accessor::Fast);
+our $debug = $ENV{DBD_FORWARD_DEBUG} || 0;
+
+
__PACKAGE__->mk_accessors(qw(
fwd_dsn
));
-sub execute {
- my ($self, $request) = @_;
- die ref($self)." has not implemented a transport method";
+
+sub freeze_data {
+ my ($self, $data) = @_;
+ $self->_dump(ref($data), $data) if $debug;
+ return freeze($data);
+}
+
+sub thaw_data {
+ my ($self, $frozen_data) = @_;
+ my $data = thaw($frozen_data);
+ $self->_dump(ref($data), $data) if $debug;
+ return $data;
}
+sub _dump {
+ my ($self, $label, $data) = @_;
+ require Data::Dumper;
+ warn "$label=".Dumper($request);
+}
+
1;
Modified: dbi/trunk/lib/DBI/PurePerl.pm
==============================================================================
--- dbi/trunk/lib/DBI/PurePerl.pm (original)
+++ dbi/trunk/lib/DBI/PurePerl.pm Tue Jan 23 15:42:34 2007
@@ -232,7 +232,6 @@
if IMA_STUB & $bitmask;
push @pre_call_frag, q{
- #$method_name = $imp . '::' . pop @_;
$method_name = pop @_;
} if IMA_FUNC_REDIRECT & $bitmask;
@@ -398,6 +397,9 @@
my @ret;
my $sub = $imp->can($method_name);
+ if (!$sub and IMA_FUNC_REDIRECT & $bitmask and $sub =
$imp->can('func')) {
+ push @_, $method_name;
+ }
if ($sub) {
(wantarray) ? (@ret = &$sub($h,@_)) : (@ret = scalar &$sub($h,@_));
}
@@ -405,7 +407,7 @@
# XXX could try explicit fallback to $imp->can('AUTOLOAD') etc
# which would then let Multiplex pass PurePerl tests, but some
# hook into install_method may be better.
- croak "Can't find DBI method $method_name for $h (via $imp)"
+ croak "Can't locate DBI object method \"$method_name\" via package
\"$imp\""
if ] . ((IMA_NOT_FOUND_OKAY & $bitmask) ? 0 : 1) . q[;
}
@@ -475,6 +477,7 @@
$h_inner->{FetchHashKeyName} ||= 'NAME';
$h_inner->{LongReadLen} ||= 80;
$h_inner->{ChildHandles} ||= [] if $HAS_WEAKEN;
+ $h_inner->{Type} ||= 'dr';
}
$h_inner->{"_call_depth"} = 0;
$h_inner->{ErrCount} = 0;
@@ -674,15 +677,15 @@
sub FETCH {
my($h,$key)= @_;
my $v = $h->{$key};
-#warn ((exists $h->{$key}) ? "$key=$v\n" : "$key NONEXISTANT\n");
+ #warn ((exists $h->{$key}) ? "$key=$v\n" : "$key NONEXISTANT\n");
return $v if defined $v;
if ($key =~ /^NAME_.c$/) {
my $cols = $h->FETCH('NAME');
return undef unless $cols;
my @lcols = map { lc $_ } @$cols;
- $h->STORE('NAME_lc', [EMAIL PROTECTED]);
+ $h->{NAME_lc} = [EMAIL PROTECTED];
my @ucols = map { uc $_ } @$cols;
- $h->STORE('NAME_uc',[EMAIL PROTECTED]);
+ $h->{NAME_uc} = [EMAIL PROTECTED];
return $h->FETCH($key);
}
if ($key =~ /^NAME.*_hash$/) {
@@ -704,7 +707,7 @@
return "dr" if $h->isa('DBI::dr');
return "db" if $h->isa('DBI::db');
return "st" if $h->isa('DBI::st');
- Carp::carp( sprintf "Can't get determine Type for %s",$h );
+ Carp::carp( sprintf "Can't determine Type for %s",$h );
}
if (!$is_valid_attribute{$key} and $key =~ m/^[A-Z]/) {
local $^W; # hide undef warnings
Modified: dbi/trunk/t/03handle.t
==============================================================================
--- dbi/trunk/t/03handle.t (original)
+++ dbi/trunk/t/03handle.t Tue Jan 23 15:42:34 2007
@@ -120,7 +120,7 @@
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');
Modified: dbi/trunk/t/08keeperr.t
==============================================================================
--- dbi/trunk/t/08keeperr.t (original)
+++ dbi/trunk/t/08keeperr.t Tue Jan 23 15:42:34 2007
@@ -36,7 +36,7 @@
my $sth = shift;
# we localize an attribute here to check that the correpoding STORE
# at scope exit doesn't clear any recorded error
- local $sth->{CompatMode} = 0;
+ local $sth->{dbd_dummy} = 0;
my $rv = $sth->SUPER::execute(@_);
return $rv;
}
@@ -62,15 +62,15 @@
}
my $err1 = test_select( My::DBI->connect(@con_info) );
-Test::More::like($err1, qr/^DBD::(ExampleP|Multiplex)::db selectrow_arrayref
failed: opendir/, '... checking error');
+Test::More::like($err1, qr/^DBD::(ExampleP|Multiplex|Forward)::db
selectrow_arrayref failed: opendir/, '... checking error');
my $err2 = test_select( DBI->connect(@con_info) );
-Test::More::like($err2, qr/^DBD::(ExampleP|Multiplex)::db selectrow_arrayref
failed: opendir/, '... checking error');
+Test::More::like($err2, qr/^DBD::(ExampleP|Multiplex|Forward)::db
selectrow_arrayref failed: opendir/, '... checking error');
package main;
## ----------------------------------------------------------------------------
-# test HandleSetErr
+print "Test HandleSetErr\n";
my $dbh = DBI->connect(@con_info);
isa_ok($dbh, "DBI::db");
@@ -84,7 +84,7 @@
my @handlewarn = (0,0,0);
$SIG{__WARN__} = sub {
my $msg = shift;
- if ($msg =~ /^DBD::ExampleP::\S+\s+(\S+)\s+(\w+)/) {
+ if ($msg =~ /^DBD::\w+::\S+\s+(\S+)\s+(\w+)/) {
++$warn{$2};
$msg =~ s/\n/\\n/g;
print "warn: '$msg'\n";
@@ -261,5 +261,7 @@
is($dbh->errstr, "errstr99", '... $dbh->errstr is as we expected');
is($dbh->state, "OV123", '... $dbh->state is as we expected');
+$dbh->disconnect;
+
1;
# end
Modified: dbi/trunk/t/09trace.t
==============================================================================
--- dbi/trunk/t/09trace.t (original)
+++ dbi/trunk/t/09trace.t Tue Jan 23 15:42:34 2007
@@ -94,7 +94,7 @@
ok $dbh->{TraceLevel};
{
- print "unknown parse_trace_flag\n";
+ print "test unknown parse_trace_flag\n";
my $warn = 0;
local $SIG{__WARN__} = sub {
if ($_[0] =~ /unknown/i) { ++$warn; print "warn: ",@_ }else{ warn @_ }
Modified: dbi/trunk/t/10examp.t
==============================================================================
--- dbi/trunk/t/10examp.t (original)
+++ dbi/trunk/t/10examp.t Tue Jan 23 15:42:34 2007
@@ -1,4 +1,4 @@
-#!perl -Tw
+#!perl -w
use lib qw(blib/arch blib/lib); # needed since -T ignores PERL5LIB
use DBI qw(:sql_types);
@@ -12,7 +12,7 @@
my $haveFileSpec = eval { require File::Spec };
require VMS::Filespec if $^O eq 'VMS';
-use Test::More tests => 263;
+use Test::More tests => 216;
# "globals"
my ($r, $dbh);
@@ -59,7 +59,7 @@
my $dbh2;
eval {
- $dbh2 = DBI->connect("dbi:NoneSuch:foobar", 1, 1, { RaiseError => 1,
AutoCommit => 0 });
+ $dbh2 = DBI->connect("dbi:NoneSuch:foobar", 1, 1, { RaiseError => 1,
AutoCommit => 1 });
};
like($@, qr/install_driver\(NoneSuch\) failed/, '... we should have an
exception here');
ok(!$dbh2, '... $dbh2 should not be defined');
@@ -103,50 +103,10 @@
ok($dbh->{AutoCommit} == 1);
cmp_ok($dbh->{PrintError}, '==', 0, '... PrintError should be 0');
-SKIP: {
- skip "cant test this if we have DBI::PurePerl", 1 if $DBI::PurePerl;
- $dbh->{Taint} = 1;
- ok($dbh->{Taint} == 1);
-}
-
is($dbh->{FetchHashKeyName}, 'NAME', '... FetchHashKey is NAME');
-like($dbh->{example_driver_path}, qr/DBD\/ExampleP\.pm$/, '... checking the
example driver_path');
-
-sub check_quote {
- # checking quote
- is($dbh->quote("quote's"), "'quote''s'", '... quoting strings
with embedded single quotes');
- is($dbh->quote("42", SQL_VARCHAR), "'42'", '... quoting number as
SQL_VARCHAR');
- is($dbh->quote("42", SQL_INTEGER), "42", '... quoting number as
SQL_INTEGER');
- is($dbh->quote(undef), "NULL", '...
quoting undef as NULL');
-}
-
-check_quote();
-
-my $get_info = $dbh->{examplep_get_info} || {};
-
-sub check_quote_identifier {
- # quote_identifier
- $get_info->{29} ='"'; #
SQL_IDENTIFIER_QUOTE_CHAR
- $dbh->{examplep_get_info} = $get_info; # trigger STORE
-
- is($dbh->quote_identifier('foo'), '"foo"', '...
properly quotes foo as "foo"');
- is($dbh->quote_identifier('f"o'), '"f""o"', '...
properly quotes f"o as "f""o"');
- is($dbh->quote_identifier('foo','bar'), '"foo"."bar"', '...
properly quotes foo, bar as "foo"."bar"');
- is($dbh->quote_identifier(undef,undef,'bar'), '"bar"', '...
properly quotes undef, undef, bar as "bar"');
-
- is($dbh->quote_identifier('foo',undef,'bar'), '"foo"."bar"', '...
properly quotes foo, undef, bar as "foo"."bar"');
-
- $get_info->{41} ='@'; # SQL_CATALOG_NAME_SEPARATOR
- $get_info->{114} = 2; # SQL_CATALOG_LOCATION
- $dbh->{examplep_get_info} = $get_info; # trigger STORE
-
- # force cache refresh
- $dbh->{dbi_quote_identifier_cache} = undef;
- is($dbh->quote_identifier('foo',undef,'bar'), '"bar"@"foo"', '... now
quotes it as "bar"@"foo" after flushing cache');
-}
-
-check_quote_identifier();
+# test access to driver-private attributes
+like($dbh->{example_driver_path}, qr/DBD\/ExampleP\.pm$/, '... checking the
example driver_path');
print "others\n";
eval { $dbh->commit('dummy') };
@@ -200,42 +160,12 @@
ok("@{[sort keys %{$csr_b->{NAME_uc_hash}}]}" eq "MODE NAME SIZE");
ok("@{[sort values %{$csr_b->{NAME_uc_hash}}]}" eq "0 1 2");
-SKIP: {
- skip "do not test with DBI::PurePerl", 15 if $DBI::PurePerl;
-
- # Check Taint* attribute switching
-
- #$dbh->{'Taint'} = 1; # set in connect
- ok($dbh->{'Taint'});
- ok($dbh->{'TaintIn'} == 1);
- ok($dbh->{'TaintOut'} == 1);
-
- $dbh->{'TaintOut'} = 0;
- ok($dbh->{'Taint'} == 0);
- ok($dbh->{'TaintIn'} == 1);
- ok($dbh->{'TaintOut'} == 0);
-
- $dbh->{'Taint'} = 0;
- ok($dbh->{'Taint'} == 0);
- ok($dbh->{'TaintIn'} == 0);
- ok($dbh->{'TaintOut'} == 0);
-
- $dbh->{'TaintIn'} = 1;
- ok($dbh->{'Taint'} == 0);
- ok($dbh->{'TaintIn'} == 1);
- ok($dbh->{'TaintOut'} == 0);
-
- $dbh->{'TaintOut'} = 1;
- ok($dbh->{'Taint'} == 1);
- ok($dbh->{'TaintIn'} == 1);
- ok($dbh->{'TaintOut'} == 1);
-}
# get a dir always readable on all platforms
my $dir = getcwd() || cwd();
$dir = VMS::Filespec::unixify($dir) if $^O eq 'VMS';
# untaint $dir
-$dir =~ m/(.*)/; $dir = $1 || die;
+#$dir =~ m/(.*)/; $dir = $1 || die;
# ---
@@ -243,7 +173,6 @@
my($col0, $col1, $col2, $col3, $rows);
my(@row_a, @row_b);
-ok($csr_a->{Taint} = 1) unless $DBI::PurePerl && ok(1);
#$csr_a->trace(5);
ok($csr_a->bind_columns(undef, \($col0, $col1, $col2)) );
ok($csr_a->execute( $dir ), $DBI::errstr);
@@ -268,87 +197,6 @@
ok ! eval { $csr_a->bind_col(4, undef) };
like $@, '/bind_col: column 4 is not a valid column \(1..3\)/', 'errstr should
contain error message';
-SKIP: {
-
- # Check Taint attribute works. This requires this test to be run
- # manually with the -T flag: "perl -T -Mblib t/examp.t"
- sub is_tainted {
- my $foo;
- return ! eval { ($foo=join('',@_)), kill 0; 1; };
- }
-
- skip " Taint attribute tests skipped\n", 19 unless(is_tainted($^X) &&
!$DBI::PurePerl);
-
- $dbh->{'Taint'} = 0;
- my $st;
- eval { $st = $dbh->prepare($std_sql); };
- ok(ref $st);
-
- ok($st->{'Taint'} == 0);
-
- ok($st->execute( $dir ));
-
- my @row = $st->fetchrow_array;
- ok(@row);
-
- ok(!is_tainted($row[0]));
- ok(!is_tainted($row[1]));
- ok(!is_tainted($row[2]));
-
- $st->{'TaintIn'} = 1;
-
- @row = $st->fetchrow_array;
- ok(@row);
-
- ok(!is_tainted($row[0]));
- ok(!is_tainted($row[1]));
- ok(!is_tainted($row[2]));
-
- $st->{'TaintOut'} = 1;
-
- @row = $st->fetchrow_array;
- ok(@row);
-
- ok(is_tainted($row[0]));
- ok(is_tainted($row[1]));
- ok(is_tainted($row[2]));
-
- $st->finish;
-
- # check simple method call values
- #ok(1);
- # check simple attribute values
- #ok(1); # is_tainted($dbh->{AutoCommit}) );
- # check nested attribute values (where a ref is returned)
- #ok(is_tainted($csr_a->{NAME}->[0]) );
- # check checking for tainted values
-
- $dbh->{'Taint'} = $csr_a->{'Taint'} = 1;
- eval { $dbh->prepare($^X); 1; };
- ok($@ =~ /Insecure dependency/, $@);
- eval { $csr_a->execute($^X); 1; };
- ok($@ =~ /Insecure dependency/, $@);
- undef $@;
-
- $dbh->{'TaintIn'} = $csr_a->{'TaintIn'} = 0;
-
- eval { $dbh->prepare($^X); 1; };
- ok(!$@);
- eval { $csr_a->execute($^X); 1; };
- ok(!$@);
-
- # Reset taint status to what it was before this block, so that
- # tests later in the file don't get confused
- $dbh->{'Taint'} = $csr_a->{'Taint'} = 1;
-}
-
-
-SKIP: {
- skip "do not test with DBI::PurePerl", 1 if $DBI::PurePerl;
- $csr_a->{Taint} = 0;
- ok($csr_a->{Taint} == 0);
-}
-
ok($csr_b->bind_param(1, $dir));
ok($csr_b->execute());
@row_b = @{ $csr_b->fetchrow_arrayref };
@@ -644,7 +492,7 @@
File::Spec->catfile($dump_dir,
'dumpcsr.tst')
:
"$dump_dir/dumpcsr.tst";
- ($dump_file) = ($dump_file =~ m/^(.*)$/); # untaint
+ #($dump_file) = ($dump_file =~ m/^(.*)$/); # untaint
SKIP: {
skip "# dump_results test skipped: unable to open $dump_file:
$!\n", 2 unless (open(DUMP_RESULTS, ">$dump_file"));
@@ -697,7 +545,6 @@
ok($tables[$_] eq $tables_expected[$_], "$tables[$_] ne $tables_expected[$_]")
foreach (0..$#tables_expected);
-
for (my $i = 0; $i < 300; $i += 100) {
print "Testing the fake directories ($i).\n";
ok($csr_a = $dbh->prepare("SELECT name, mode FROM long_list_$i"));
Added: dbi/trunk/t/12quote.t
==============================================================================
--- (empty file)
+++ dbi/trunk/t/12quote.t Tue Jan 23 15:42:34 2007
@@ -0,0 +1,47 @@
+#!perl -w
+
+use lib qw(blib/arch blib/lib); # needed since -T ignores PERL5LIB
+use strict;
+
+use Test::More tests => 10;
+
+use DBI qw(:sql_types);
+use Config;
+use Cwd;
+
+$^W = 1;
+$| = 1;
+
+my $dbh = DBI->connect('dbi:ExampleP:', '', '');
+
+sub check_quote {
+ # checking quote
+ is($dbh->quote("quote's"), "'quote''s'", '... quoting strings
with embedded single quotes');
+ is($dbh->quote("42", SQL_VARCHAR), "'42'", '... quoting number as
SQL_VARCHAR');
+ is($dbh->quote("42", SQL_INTEGER), "42", '... quoting number as
SQL_INTEGER');
+ is($dbh->quote(undef), "NULL", '...
quoting undef as NULL');
+}
+
+check_quote();
+
+sub check_quote_identifier {
+
+ my $qi = $dbh->{dbi_quote_identifier_cache} = [
+ '"', # 29: SQL_IDENTIFIER_QUOTE_CHAR
+ '.', # 41: SQL_CATALOG_NAME_SEPARATOR
+ 1, # 114: SSQL_CATALOG_LOCATION
+ ];
+
+ is($dbh->quote_identifier('foo'), '"foo"', '...
properly quotes foo as "foo"');
+ is($dbh->quote_identifier('f"o'), '"f""o"', '...
properly quotes f"o as "f""o"');
+ is($dbh->quote_identifier('foo','bar'), '"foo"."bar"', '...
properly quotes foo, bar as "foo"."bar"');
+ is($dbh->quote_identifier(undef,undef,'bar'), '"bar"', '...
properly quotes undef, undef, bar as "bar"');
+
+ is($dbh->quote_identifier('foo',undef,'bar'), '"foo"."bar"', '...
properly quotes foo, undef, bar as "foo"."bar"');
+
+ $qi->[1] = '@'; # SQL_CATALOG_NAME_SEPARATOR
+ $qi->[2] = 2; # SQL_CATALOG_LOCATION
+ is($dbh->quote_identifier('foo',undef,'bar'), '"bar"@"foo"', '... now
quotes it as "bar"@"foo" after flushing cache');
+}
+
+check_quote_identifier();
Added: dbi/trunk/t/13taint.t
==============================================================================
--- (empty file)
+++ dbi/trunk/t/13taint.t Tue Jan 23 15:42:34 2007
@@ -0,0 +1,134 @@
+#!perl -wT
+
+use lib qw(blib/arch blib/lib); # needed since -T ignores PERL5LIB
+use DBI qw(:sql_types);
+use Config;
+use Cwd;
+use strict;
+
+
+$^W = 1;
+$| = 1;
+
+my $haveFileSpec = eval { require File::Spec };
+require VMS::Filespec if $^O eq 'VMS';
+
+use Test::More;
+
+# Check Taint attribute works. This requires this test to be run
+# manually with the -T flag: "perl -T -Mblib t/examp.t"
+sub is_tainted {
+ my $foo;
+ return ! eval { ($foo=join('',@_)), kill 0; 1; };
+}
+sub mk_tainted {
+ my $string = shift;
+ return substr($string.$^X, 0, length($string));
+}
+
+plan skip_all => "Taint attributes not supported with DBI::PurePerl" if
$DBI::PurePerl;
+plan skip_all => "Taint attribute tests require taint mode (perl -T)" unless
is_tainted($^X);
+plan skip_all => "Taint attribute tests not functional with DBI_AUTOPROXY" if
$ENV{DBI_AUTOPROXY};
+
+plan tests => 36;
+
+# get a dir always readable on all platforms
+my $dir = getcwd() || cwd();
+$dir = VMS::Filespec::unixify($dir) if $^O eq 'VMS';
+$dir =~ m/(.*)/; $dir = $1 || die; # untaint $dir
+
+my ($r, $dbh);
+
+$dbh = DBI->connect('dbi:ExampleP:', '', '', { PrintError=>0, RaiseError=>1,
Taint => 1 });
+
+my $std_sql = "select mode,size,name from ?";
+my $csr_a = $dbh->prepare($std_sql);
+ok(ref $csr_a);
+
+ok($dbh->{'Taint'});
+ok($dbh->{'TaintIn'} == 1);
+ok($dbh->{'TaintOut'} == 1);
+
+$dbh->{'TaintOut'} = 0;
+ok($dbh->{'Taint'} == 0);
+ok($dbh->{'TaintIn'} == 1);
+ok($dbh->{'TaintOut'} == 0);
+
+$dbh->{'Taint'} = 0;
+ok($dbh->{'Taint'} == 0);
+ok($dbh->{'TaintIn'} == 0);
+ok($dbh->{'TaintOut'} == 0);
+
+$dbh->{'TaintIn'} = 1;
+ok($dbh->{'Taint'} == 0);
+ok($dbh->{'TaintIn'} == 1);
+ok($dbh->{'TaintOut'} == 0);
+
+$dbh->{'TaintOut'} = 1;
+ok($dbh->{'Taint'} == 1);
+ok($dbh->{'TaintIn'} == 1);
+ok($dbh->{'TaintOut'} == 1);
+
+$dbh->{'Taint'} = 0;
+my $st;
+eval { $st = $dbh->prepare($std_sql); };
+ok(ref $st);
+
+ok($st->{'Taint'} == 0);
+
+ok($st->execute( $dir ), 'should execute ok');
+
+my @row = $st->fetchrow_array;
+ok(@row);
+
+ok(!is_tainted($row[0]));
+ok(!is_tainted($row[1]));
+ok(!is_tainted($row[2]));
+
+print "TaintIn\n";
+$st->{'TaintIn'} = 1;
+
[EMAIL PROTECTED] = $st->fetchrow_array;
+ok(@row);
+
+ok(!is_tainted($row[0]));
+ok(!is_tainted($row[1]));
+ok(!is_tainted($row[2]));
+
+print "TaintOut\n";
+$st->{'TaintOut'} = 1;
+
[EMAIL PROTECTED] = $st->fetchrow_array;
+ok(@row);
+
+ok(is_tainted($row[0]));
+ok(is_tainted($row[1]));
+ok(is_tainted($row[2]));
+
+$st->finish;
+
+my $tainted_sql = mk_tainted($std_sql);
+my $tainted_dot = mk_tainted('.');
+
+$dbh->{'Taint'} = $csr_a->{'Taint'} = 1;
+eval { $dbh->prepare($tainted_sql); 1; };
+ok($@ =~ /Insecure dependency/, $@);
+eval { $csr_a->execute($tainted_dot); 1; };
+ok($@ =~ /Insecure dependency/, $@);
+undef $@;
+
+$dbh->{'TaintIn'} = $csr_a->{'TaintIn'} = 0;
+
+eval { $dbh->prepare($tainted_sql); 1; };
+ok(!$@, $@);
+eval { $csr_a->execute($tainted_dot); 1; };
+ok(!$@, $@);
+
+$csr_a->{Taint} = 0;
+ok($csr_a->{Taint} == 0);
+
+$csr_a->finish;
+
+$dbh->disconnect;
+
+1;
Modified: dbi/trunk/t/40profile.t
==============================================================================
--- dbi/trunk/t/40profile.t (original)
+++ dbi/trunk/t/40profile.t Tue Jan 23 15:42:34 2007
@@ -21,7 +21,7 @@
# tie methods (STORE/FETCH etc) get called different number of times
plan skip_all => "test results assume perl >= 5.8.2"
if $] <= 5.008001;
- plan tests => 45;
+ plan tests => 51;
}
$Data::Dumper::Indent = 1;
@@ -66,11 +66,15 @@
'Path' => [ '!MethodName', '!Caller2' ],
} => 'DBI::Profile';
+my $t_file = __FILE__;
$dbh->do("set foo=1"); my $line = __LINE__;
+my $expected_caller = "40profile.t line $line";
+$expected_caller .= " via zvfw_40profile.t line 3"
+ if $0 =~ /zvfw_/;
is_deeply sanitize_tree($dbh->{Profile}), bless {
'Path' => [ '!MethodName', '!Caller2' ],
'Data' => { 'do' => {
- "40profile.t line $line" => [ 1, 0, 0, 0, 0, 0, 0 ]
+ $expected_caller => [ 1, 0, 0, 0, 0, 0, 0 ]
} }
} => 'DBI::Profile';
#die Dumper $dbh->{Profile};
@@ -78,42 +82,31 @@
# can turn it on at connect
$dbh = DBI->connect("dbi:ExampleP:", '', '', { RaiseError=>1, Profile=>6 });
-is_deeply sanitize_tree($dbh->{Profile}), bless {
- 'Path' => [ '!Statement', '!MethodName' ],
- 'Data' => {
- '' => {
- 'FETCH' => [ 1, 0, 0, 0, 0, 0, 0 ],
- 'STORE' => [ 2, 0, 0, 0, 0, 0, 0 ]
- }
- }
-} => 'DBI::Profile';
+is_deeply $dbh->{Profile}{Path}, [ '!Statement', '!MethodName' ];
+cmp_ok(keys %{ $dbh->{Profile}{Data} }, '==', 1);
+cmp_ok(keys %{ $dbh->{Profile}{Data}{""} }, '>=', 1); # at least STORE
+ok( ref $dbh->{Profile}{Data}{""}{STORE} );
print "dbi_profile\n";
# Try to avoid rounding problem on double precision systems
# $got->[5] = '1150962858.01596498'
# $expected->[5] = '1150962858.015965'
-# (looks like is_deeply stringifies) by treating as a string:
-my $t1 = DBI::dbi_time() . "";
-dbi_profile($dbh, "Hi, mom", "my_method_name", $t1, $t1 + 1);
-is_deeply sanitize_tree($dbh->{Profile}), bless {
- 'Path' => [ '!Statement', '!MethodName' ],
- 'Data' => {
- '' => {
- 'FETCH' => [ 1, 0, 0, 0, 0, 0, 0 ], # +0
- 'STORE' => [ 2, 0, 0, 0, 0, 0, 0 ]
- },
- "Hi, mom" => {
- my_method_name => [ 1, 0, 0, 0, 0, 0, 0 ],
- },
- }
-} => 'DBI::Profile';
+# by treating as a string (because is_deeply stringifies)
+my $t1 = DBI::dbi_time() . "";
+my $dummy_statement = "Hi mom";
+my $dummy_methname = "my_method_name";
+dbi_profile($dbh, $dummy_statement, $dummy_methname, $t1, $t1 + 1);
+print Dumper($dbh->{Profile});
+cmp_ok(keys %{ $dbh->{Profile}{Data} }, '==', 2);
+cmp_ok(keys %{ $dbh->{Profile}{Data}{$dummy_statement} }, '==', 1);
+ok( ref $dbh->{Profile}{Data}{$dummy_statement}{$dummy_methname} );
-my $mine = $dbh->{Profile}{Data}{"Hi, mom"}{my_method_name};
+my $mine = $dbh->{Profile}{Data}{$dummy_statement}{$dummy_methname};
print "@$mine\n";
is_deeply $mine, [ 1, 1, 1, 1, 1, $t1, $t1 ];
my $t2 = DBI::dbi_time() . "";
-dbi_profile($dbh, "Hi, mom", "my_method_name", $t2, $t2 + 2);
+dbi_profile($dbh, $dummy_statement, $dummy_methname, $t2, $t2 + 2);
print "@$mine\n";
is_deeply $mine, [ 2, 3, 1, 1, 2, $t1, $t2 ];
@@ -197,19 +190,19 @@
$sth = $dbh->prepare($sql);
$sth->execute();
$sth->fetchrow_hashref;
+$sth->finish;
undef $sth; # DESTROY
$tmp = sanitize_tree($dbh->{Profile});
+ok $tmp->{Data}{usrnam}{""}{foo}{STORE};
+$tmp->{Data}{usrnam}{""}{foo} = {};
# make test insentitive to number of local files
is_deeply $tmp, bless {
'Path' => [ '{Username}', '!Statement', 'foo', '!MethodName' ],
'Data' => {
'usrnam' => {
'' => {
- 'foo' => {
- 'FETCH' => [ 1, 0, 0, 0, 0, 0, 0 ],
- 'STORE' => [ 2, 0, 0, 0, 0, 0, 0 ],
- },
+ 'foo' => { },
},
'select name from .' => {
'foo' => {
@@ -217,8 +210,6 @@
'fetchrow_hashref' => [ 1, 0, 0, 0, 0, 0, 0 ],
'DESTROY' => [ 1, 0, 0, 0, 0, 0, 0 ],
'prepare' => [ 1, 0, 0, 0, 0, 0, 0 ],
- # XXX finish shouldn't be profiled as it's not called
explicitly
- # but currently the finish triggered by DESTROY does
get profiled
'finish' => [ 1, 0, 0, 0, 0, 0, 0 ],
},
},
@@ -278,6 +269,7 @@
$sth = $dbh->prepare($sql);
$sth->execute();
$sth->fetchrow_hashref;
+ $sth->finish;
undef $sth; # DESTROY
return sanitize_profile_data_nodes($dbh->{Profile}{Data});
}
@@ -299,18 +291,15 @@
}, '$_ should contain statement';
# check what code ref sees in @_
-$tmp = run_test1( { Path => [ sub { my ($h,$method) = @_; return (ref $h,
$method) } ] });
+$tmp = run_test1( { Path => [ sub { my ($h,$method) = @_; return \undef if
$method =~ /^[A-Z]+$/; return (ref $h, $method) } ] });
is_deeply $tmp, {
'DBI::db' => {
- 'FETCH' => [ 1, 0, 0, 0, 0, 0, 0 ],
'prepare' => [ 1, 0, 0, 0, 0, 0, 0 ],
- 'STORE' => [ 2, 0, 0, 0, 0, 0, 0 ],
},
'DBI::st' => {
'fetchrow_hashref' => [ 1, 0, 0, 0, 0, 0, 0 ],
'execute' => [ 1, 0, 0, 0, 0, 0, 0 ],
'finish' => [ 1, 0, 0, 0, 0, 0, 0 ],
- 'DESTROY' => [ 1, 0, 0, 0, 0, 0, 0 ],
},
}, 'should have @_ as keys';
@@ -344,7 +333,7 @@
is($total_time, 2.93);
DBI->trace(0, "STDOUT"); # close current log to flush it
-ok(-s $LOG_FILE); # check that output went into the log file
+ok(-s $LOG_FILE, 'output should go to log file');
exit 0;
Modified: dbi/trunk/t/42prof_data.t
==============================================================================
--- dbi/trunk/t/42prof_data.t (original)
+++ dbi/trunk/t/42prof_data.t Tue Jan 23 15:42:34 2007
@@ -115,6 +115,7 @@
$sth3->fetchrow_hashref;
$sth3->finish;
}
+$dbh->disconnect;
undef $dbh;
# load dbi.prof
Modified: dbi/trunk/t/50dbm.t
==============================================================================
--- dbi/trunk/t/50dbm.t (original)
+++ dbi/trunk/t/50dbm.t Tue Jan 23 15:42:34 2007
@@ -5,6 +5,8 @@
use Test::More;
use Config qw(%Config);
+my $using_dbd_forward_null = ($ENV{DBI_AUTOPROXY}||'') =~
/dbi:Forward.*transport=null/i;
+
use DBI;
use vars qw( @mldbm_types @dbm_types );
BEGIN {
@@ -42,7 +44,7 @@
print "Using DBM modules: @dbm_types\n";
print "Using MLDBM serializers: @mldbm_types\n" if @mldbm_types;
- my $num_tests = ([EMAIL PROTECTED]) * @dbm_types * 11;
+ my $num_tests = ([EMAIL PROTECTED]) * @dbm_types * 12;
if (!$num_tests) {
plan skip_all => "No DBM modules available";
@@ -80,26 +82,42 @@
# (This test script doesn't test that locking actually works anyway.)
my $dsn
="dbi:DBM(RaiseError=1,PrintError=0):dbm_type=$dtype;mldbm=$mldbm;lockfile=0";
+
+ if ($using_dbd_forward_null) {
+ $dsn .= ";f_dir=$dir";
+ }
+
my $dbh = DBI->connect( $dsn );
- if ($DBI::VERSION >= 1.37 ) { # needed for install_method
- print $dbh->dbm_versions;
+ my $dbm_versions;
+ if ($DBI::VERSION >= 1.37 # needed for install_method
+ && !$ENV{DBI_AUTOPROXY} # can't transparently proxy driver-private
methods
+ ) {
+ $dbm_versions = $dbh->dbm_versions;
}
else {
- print $dbh->func('dbm_versions');
+ $dbm_versions = $dbh->func('dbm_versions');
}
+ print $dbm_versions;
+ ok($dbm_versions);
isa_ok($dbh, 'DBI::db');
# test if it correctly accepts valid $dbh attributes
- #
- eval {$dbh->{f_dir}=$dir};
- ok(!$@);
- eval {$dbh->{dbm_mldbm}=$mldbm};
- ok(!$@);
+ SKIP: {
+ skip "Can't set attributes after connect using DBD::Forward", 2
+ if $using_dbd_forward_null;
+ eval {$dbh->{f_dir}=$dir};
+ ok(!$@);
+ eval {$dbh->{dbm_mldbm}=$mldbm};
+ ok(!$@);
+ }
# test if it correctly rejects invalid $dbh attributes
#
- eval {$dbh->{dbm_bad_name}=1};
+ eval {
+ local $SIG{__WARN__} = sub { } if $using_dbd_forward_null;
+ $dbh->{dbm_bad_name}=1;
+ };
ok($@);
for my $sql ( @$stmts ) {
Modified: dbi/trunk/t/72childhandles.t
==============================================================================
--- dbi/trunk/t/72childhandles.t (original)
+++ dbi/trunk/t/72childhandles.t Tue Jan 23 15:42:34 2007
@@ -24,6 +24,8 @@
plan tests => 14;
+my $drh;
+
{
# make 10 connections
my @dbh;
@@ -33,12 +35,11 @@
}
# get the driver handle
- my %drivers = DBI->installed_drivers();
- my $driver = $drivers{ExampleP};
- ok $driver;
+ $drh = $dbh[0]->{Driver};
+ ok $drh;
# get the kids, should be the same list of connections
- my $db_handles = $driver->{ChildHandles};
+ my $db_handles = $drh->{ChildHandles};
is ref $db_handles, 'ARRAY';
is scalar @$db_handles, scalar @dbh;
@@ -52,10 +53,7 @@
# now all the out-of-scope DB handles should be gone
{
- my %drivers = DBI->installed_drivers();
- my $driver = $drivers{ExampleP};
-
- my $handles = $driver->{ChildHandles};
+ my $handles = $drh->{ChildHandles};
my @db_handles = grep { defined } @$handles;
is scalar @db_handles, 0, "All handles should be undef now";
}
@@ -68,9 +66,10 @@
# test child handles for statement handles
{
my @sth;
- for (1 .. 200) {
+ my $sth_count = 200;
+ for (1 .. $sth_count) {
my $sth = $dbh->prepare('SELECT name FROM t');
- push(@sth, $sth);
+ push @sth, $sth;
}
my $handles = $dbh->{ChildHandles};
is scalar @$handles, scalar @sth;
@@ -85,10 +84,11 @@
show_child_handles($_, $level + 1)
for (grep { defined } @{$h->{ChildHandles}});
}
- show_child_handles($_) for (values %{{DBI->installed_drivers()}});
+ my $drh = $dbh->{Driver};
+ show_child_handles($drh, 0);
print @lines[0..4];
- is scalar @lines, 202;
+ is scalar @lines, $sth_count + 2;
like $lines[0], qr/^drh/;
like $lines[1], qr/^dbh/;
like $lines[2], qr/^sth/;