Author: timbo
Date: Wed May 9 08:24:30 2007
New Revision: 9532
Modified:
dbi/trunk/DBI.pm
dbi/trunk/lib/DBD/ExampleP.pm
dbi/trunk/lib/DBD/Gofer.pm
dbi/trunk/lib/DBI/Gofer/Execute.pm
dbi/trunk/lib/DBI/Gofer/Request.pm
dbi/trunk/t/06attrs.t
Log:
Fix driver-private sth attributes via gofer.
Add way for gofer executor config to enable extra dbh/sth to be returned with
responses.
Add tests for above.
Add valid_configuration_attributes executor method.
Optimize caching of standard response attributes.
Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm (original)
+++ dbi/trunk/DBI.pm Wed May 9 08:24:30 2007
@@ -9,7 +9,7 @@
require 5.006_00;
BEGIN {
-$DBI::VERSION = "1.55"; # ==> ALSO update the version in the pod text below!
+$DBI::VERSION = "1.56"; # ==> ALSO update the version in the pod text below!
}
=head1 NAME
@@ -120,7 +120,7 @@
=head2 NOTES
-This is the DBI specification that corresponds to the DBI version 1.55
+This is the DBI specification that corresponds to the DBI version 1.56
($Revision$).
The DBI is evolving at a steady pace, so it's good to check that
Modified: dbi/trunk/lib/DBD/ExampleP.pm
==============================================================================
--- dbi/trunk/lib/DBD/ExampleP.pm (original)
+++ dbi/trunk/lib/DBD/ExampleP.pm Wed May 9 08:24:30 2007
@@ -59,14 +59,17 @@
sub connect { # normally overridden, but a handy default
my($drh, $dbname, $user, $auth)= @_;
- my ($outer, $dbh) = DBI::_new_dbh($drh, { Name => $dbname });
- $dbh->STORE('Active', 1);
+ my ($outer, $dbh) = DBI::_new_dbh($drh, {
+ Name => $dbname,
+ examplep_private_dbh_attrib => 42, # an example, for testing
+ });
$dbh->{examplep_get_info} = {
29 => '"', # SQL_IDENTIFIER_QUOTE_CHAR
41 => '.', # SQL_CATALOG_NAME_SEPARATOR
114 => 1, # SQL_CATALOG_LOCATION
};
- $dbh->{Name} = $dbname;
+ #$dbh->{Name} = $dbname;
+ $dbh->STORE('Active', 1);
return $outer;
}
@@ -101,6 +104,7 @@
my ($outer, $sth) = DBI::_new_sth($dbh, {
'Statement' => $statement,
+ examplep_private_sth_attrib => 24, # an example, for testing
}, ['example implementors private data '.__PACKAGE__]);
my @bad = map {
@@ -111,7 +115,7 @@
$outer->STORE('NUM_OF_FIELDS' => scalar(@fields));
- $sth->{dbd_ex_dir} = $dir if defined($dir) && $dir !~ /\?/;
+ $sth->{examplep_ex_dir} = $dir if defined($dir) && $dir !~ /\?/;
$outer->STORE('NUM_OF_PARAMS' => ($dir) ? $dir =~ tr/?/?/ : 0);
if (@fields) {
@@ -307,7 +311,7 @@
return 0 unless $sth->{NUM_OF_FIELDS}; # not a select
- $dir = $dbd_param->[0] || $sth->{dbd_ex_dir};
+ $dir = $dbd_param->[0] || $sth->{examplep_ex_dir};
return $sth->set_err(2, "No bind parameter supplied")
unless defined $dir;
Modified: dbi/trunk/lib/DBD/Gofer.pm
==============================================================================
--- dbi/trunk/lib/DBD/Gofer.pm (original)
+++ dbi/trunk/lib/DBD/Gofer.pm Wed May 9 08:24:30 2007
@@ -1035,7 +1035,7 @@
=head2 Other Transports
-Implementing a transport is very simple, and more transports are very welcome.
+Implementing a Gofer transport is I<very> simple, and more transports are very
welcome.
Just take a look at any existing transports that are similar to your needs.
=head3 http
@@ -1045,7 +1045,7 @@
=head3 Gearman
I know Ask Bj�rn Hansen has implemented a transport for the C<gearman>
distributed
-job system, though it's not yet on CPAN.
+job system, though it's not on CPAN at the time of writing this.
=head1 CONNECTING
Modified: dbi/trunk/lib/DBI/Gofer/Execute.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Execute.pm (original)
+++ dbi/trunk/lib/DBI/Gofer/Execute.pm Wed May 9 08:24:30 2007
@@ -32,18 +32,26 @@
DBI->trace(split /=/, $ENV{DBI_GOFER_TRACE}, 2) if $ENV{DBI_GOFER_TRACE};
-__PACKAGE__->mk_accessors(qw(
- check_request_sub
- default_connect_dsn
- forced_connect_dsn
- default_connect_attributes
- forced_connect_attributes
- forced_single_resultset
- max_cached_dbh_per_drh
- max_cached_sth_per_dbh
- track_recent
- stats
-));
+# define valid configuration attributes (args to new())
+# the values here indicate the basic type of values allowed
+my %configuration_attributes = (
+ default_connect_dsn => 1,
+ forced_connect_dsn => 1,
+ default_connect_attributes => {},
+ forced_connect_attributes => {},
+ track_recent => 1,
+ check_request_sub => sub {},
+ forced_single_resultset => 1,
+ max_cached_dbh_per_drh => 1,
+ max_cached_sth_per_dbh => 1,
+ forced_response_attributes => {},
+ stats => {},
+);
+
+__PACKAGE__->mk_accessors(
+ keys %configuration_attributes
+);
+
sub new {
@@ -56,19 +64,15 @@
}
-my @sth_std_attr = qw(
- NUM_OF_PARAMS
- NUM_OF_FIELDS
- NAME
- TYPE
- NULLABLE
- PRECISION
- SCALE
-);
+sub valid_configuration_attributes {
+ my $self = shift;
+ return { %configuration_attributes };
+}
+
my %extra_attr = (
# Only referenced if the driver doesn't support private_attribute_info
method.
- # what driver-specific attributes should be returned for the driver being
used?
+ # What driver-specific attributes should be returned for the driver being
used?
# keyed by $dbh->{Driver}{Name}
# XXX for sth should split into attr specific to resultsets (where
NUM_OF_FIELDS > 0) and others
# which would reduce processing/traffic for non-select statements
@@ -113,6 +117,17 @@
sth => [qw(
)],
},
+ ExampleP => {
+ dbh => [qw(
+ examplep_private_dbh_attrib
+ )],
+ sth => [qw(
+ examplep_private_sth_attrib
+ )],
+ dbh_after_sth => [qw(
+ examplep_insertid
+ )],
+ },
);
@@ -323,9 +338,10 @@
my @req_attr_names = @$dbh_attributes;
if ($req_attr_names[0] eq '*') { # auto include std + private
shift @req_attr_names;
- push @req_attr_names, @{ $self->_get_std_attributes($dbh) };
+ push @req_attr_names, @{ $self->_std_response_attribute_names($dbh) };
}
my %dbh_attr_values;
+ # XXX a FETCH_many() method implemented in C would help here
$dbh_attr_values{$_} = $dbh->FETCH($_) for @req_attr_names;
# XXX piggyback installed_methods onto dbh_attributes for now
@@ -338,24 +354,48 @@
}
-sub _get_std_attributes {
+sub _std_response_attribute_names {
my ($self, $h) = @_;
$h = tied(%$h) || $h; # switch to inner handle
- my $attr_names = $h->{private_gofer_std_attr_names};
- return $attr_names if $attr_names;
- # add some extra because drivers may have different defaults
- # add Name so the client gets the real Name of the connection
- my @attr_names = qw(ChopBlanks LongReadLen LongTruncOk ReadOnly Name);
+
+ # cache the private_attribute_info data for each handle
+ # XXX might be better to cache it in the executor
+ # as it's unlikely to change
+ # or perhaps at least cache it in the dbh even for sth
+ # as the sth are typically very short lived
+
+ my ($dbh, $h_type, $driver_name, @attr_names);
+
+ if ($dbh = $h->{Database}) { # is an sth
+
+ # does the dbh already have the answer cached?
+ return $dbh->{private_gofer_std_attr_names_sth} if
$dbh->{private_gofer_std_attr_names_sth};
+
+ ($h_type, $driver_name) = ('sth', $dbh->{Driver}{Name});
+ push @attr_names, qw(NUM_OF_PARAMS NUM_OF_FIELDS NAME TYPE NULLABLE
PRECISION SCALE);
+ }
+ else { # is a dbh
+ return $h->{private_gofer_std_attr_names_dbh} if
$h->{private_gofer_std_attr_names_dbh};
+
+ ($h_type, $driver_name, $dbh) = ('dbh', $h->{Driver}{Name}, $h);
+ # explicitly add these because drivers may have different defaults
+ # add Name so the client gets the real Name of the connection
+ push @attr_names, qw(ChopBlanks LongReadLen LongTruncOk ReadOnly Name);
+ }
+
if (my $pai = $h->private_attribute_info) {
push @attr_names, keys %$pai;
}
- elsif (my $drh = $h->{Driver}) { # is a dbh
- push @attr_names, @{ $extra_attr{ $drh->{Name} }{dbh} || []};
+ else {
+ push @attr_names, @{ $extra_attr{ $driver_name }{$h_type} || []};
}
- elsif ($drh = $h->{Driver}{Database}) { # is an sth
- push @attr_names, @{ $extra_attr{ $drh->{Name} }{sth} || []};
+ if (my $fra = $self->{forced_response_attributes}) {
+ push @attr_names, @{ $fra->{ $driver_name }{$h_type} || []}
}
- return $h->{private_gofer_std_attr_names} = [EMAIL PROTECTED];
+ $dbh->trace_msg("_std_response_attribute_names for $driver_name $h_type:
@attr_names\n");
+
+ # cache into the dbh even for sth, as the dbh is usually longer lived
+ return $dbh->{"private_gofer_std_attr_names_$h_type"} = [EMAIL PROTECTED];
}
@@ -412,6 +452,7 @@
if (my $dbh_attributes = $request->dbh_attributes) {
$dbh_attr_set = $self->gather_dbh_attributes($dbh, $dbh_attributes);
}
+ # XXX needs to be integrated with private_attribute_info() etc
if (my $dbh_attr = $extra_attr{$dbh->{Driver}{Name}}{dbh_after_sth}) {
$dbh_attr_set->{$_} = $dbh->FETCH($_) for @$dbh_attr;
}
@@ -426,11 +467,10 @@
sub gather_sth_resultsets {
my ($self, $sth, $request, $response) = @_;
my $resultsets = eval {
- my $driver_name = $sth->{Database}{Driver}{Name};
- my $extra_sth_attr = $extra_attr{$driver_name}{sth} || [];
+ my $attr_names = $self->_std_response_attribute_names($sth);
my $sth_attr = {};
- $sth_attr->{$_} = 1 for (@sth_std_attr, @$extra_sth_attr);
+ $sth_attr->{$_} = 1 for @$attr_names;
# let the client add/remove sth atributes
if (my $sth_result_attr = $request->sth_result_attr) {
@@ -669,12 +709,25 @@
Note that this setting can significantly increase memory use. Use with caution.
-=head1 AUTHOR AND COPYRIGHT
+=head1 DRIVER-SPECIFIC ISSUES
+
+Gofer needs to know about any driver-private attributes that should have their
+values sent back to the client.
+
+If the driver doesn't support private_attribute_info() method, and very few do,
+then the module fallsback to using some hard-coded details, if available, for
+the driver being used. Currently hard-coded details are available for the
+mysql, Pg, Sybase, and SQLite drivers.
+
+=head1 AUTHOR
+
+Tim Bunce, L<http://www.linkedin.com/in/timbunce>
+
+=head1 LICENCE AND COPYRIGHT
-The DBD::Gofer, DBD::Gofer::* and DBI::Gofer::* modules are
-Copyright (c) 2007 Tim Bunce. Ireland. All rights reserved.
+Copyright (c) 2007, Tim Bunce, Ireland. All rights reserved.
-You may distribute under the terms of either the GNU General Public License or
-the Artistic License, as specified in the Perl README file.
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
=cut
Modified: dbi/trunk/lib/DBI/Gofer/Request.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Request.pm (original)
+++ dbi/trunk/lib/DBI/Gofer/Request.pm Wed May 9 08:24:30 2007
@@ -151,11 +151,15 @@
1;
-=head1 AUTHOR AND COPYRIGHT
-The DBD::Gofer, DBD::Gofer::* and DBI::Gofer::* modules are
-Copyright (c) 2007 Tim Bunce. Ireland. All rights reserved.
+=head1 AUTHOR
-You may distribute under the terms of either the GNU General Public License or
-the Artistic License, as specified in the Perl README file.
+Tim Bunce, L<http://www.linkedin.com/in/timbunce>
+
+=head1 LICENCE AND COPYRIGHT
+
+Copyright (c) 2007, Tim Bunce, Ireland. All rights reserved.
+
+This module is free software; you can redistribute it and/or
+modify it under the same terms as Perl itself. See L<perlartistic>.
Modified: dbi/trunk/t/06attrs.t
==============================================================================
--- dbi/trunk/t/06attrs.t (original)
+++ dbi/trunk/t/06attrs.t Wed May 9 08:24:30 2007
@@ -2,7 +2,7 @@
use strict;
-use Test::More tests => 142;
+use Test::More tests => 144;
## ----------------------------------------------------------------------------
## 06attrs.t - ...
@@ -74,6 +74,8 @@
cmp_ok($dbh->{TraceLevel}, '==', $DBI::dbi_debug & 0xF, '... checking
TraceLevel attribute for dbh');
cmp_ok($dbh->{LongReadLen}, '==', 80, '... checking
LongReadLen attribute for dbh');
+is $dbh->{examplep_private_dbh_attrib}, 42, 'should see driver-private dbh
attribute value';
+
# Raise an error.
eval {
$dbh->do('select foo from foo')
@@ -274,6 +276,8 @@
is($sth->{Statement}, "select ctime, name from ?", '... checking Statement
attribute for sth');
ok(!defined $sth->{RowsInCache}, '... checking type of RowsInCache attribute
for sth');
+is $sth->{examplep_private_sth_attrib}, 24, 'should see driver-private sth
attribute value';
+
# $h->{TraceLevel} tests are in t/09trace.t
print "Checking inheritance\n";