Author: timbo Date: Tue Apr 10 08:16:05 2007 New Revision: 9391 Modified: dbi/trunk/Changes dbi/trunk/DBI.xs dbi/trunk/lib/DBD/Gofer.pm dbi/trunk/lib/DBD/Gofer/Policy/Base.pm dbi/trunk/lib/DBD/Gofer/Policy/classic.pm dbi/trunk/lib/DBD/Gofer/Policy/rush.pm dbi/trunk/lib/DBI/Gofer/Execute.pm dbi/trunk/lib/DBI/Gofer/Request.pm
Log: Add connect_method and prepare_method gofer policies. Add connect_cached to gofer like prepare_cached. Change connect_args to dbh_connect_call and include method name and all args. (Some t/zvg_09trace tests fail, oddly) Modified: dbi/trunk/Changes ============================================================================== --- dbi/trunk/Changes (original) +++ dbi/trunk/Changes Tue Apr 10 08:16:05 2007 @@ -8,9 +8,16 @@ http://buildd.debian.org/fetch.cgi?&pkg=libdbi-perl&ver=1.54-1&arch=m68k&stamp=1174636818&file=log +Policy principle: + Designed to influence behaviour of unaltered programs + ie go_* attributes take precidence over policy + on the basis that if you can alter the code to add go_* attributes + then you can also put your required logic at that place + +Gofer request flags: + return current executor stats as an attribute - handy for tests + only fetch one result set - handy for Sybase/MSSQL users Pedantic policy should force a fresh connect each time - add new policy item -Allow connect via subclass to support DBIx::HA -prepare(...,{ Err=>\my $isolated_err, ...}) Add attr-passthru to prepare()? ie for gofer cache control Terminology for client and server ends Document user/passwd issues at the various levels of the stack @@ -23,6 +30,8 @@ Call method on transport timeout so transport can cleanup/reset it it wants Implement tie in C. +Make CachedKids a plain attrib to avoid FETCH in connect_cached/prepare_cached +prepare(...,{ Err=>\my $isolated_err, ...}) Add trace modules that just records the last N trace messages into an array and prepends them to any error message. Modified: dbi/trunk/DBI.xs ============================================================================== --- dbi/trunk/DBI.xs (original) +++ dbi/trunk/DBI.xs Tue Apr 10 08:16:05 2007 @@ -792,8 +792,8 @@ if (!what) return NULL; sv_dump(orv); - croak("panic: %s inner handle %s is not a hash ref", - what, neatsvpv(hrv,0)); + croak("panic: %s inner handle %s is not a hash ref (isref=%d, type=%d)", + what, neatsvpv(hrv,0), SvROK(hrv), (SvROK(hrv))?SvTYPE(SvRV(hrv)):-1); } return hrv; } Modified: dbi/trunk/lib/DBD/Gofer.pm ============================================================================== --- dbi/trunk/lib/DBD/Gofer.pm (original) +++ dbi/trunk/lib/DBD/Gofer.pm Tue Apr 10 08:16:05 2007 @@ -120,6 +120,16 @@ $imp_data_size = 0; use strict; + sub connect_cached { + my ($drh, $dsn, $user, $auth, $attr)= @_; + $attr ||= {}; + return $drh->SUPER::connect_cached($dsn, $user, $auth, { + (%$attr), + go_connect_method => $attr->{go_connect_method} || 'connect_cached', + }); + } + + sub connect { my($drh, $dsn, $user, $auth, $attr)= @_; my $orig_dsn = $dsn; @@ -157,6 +167,9 @@ # policy object is left in $go_attr{go_policy} so transport can see it my $go_policy = $go_attr{go_policy}; + # but delete any other attributes that don't appy to transport + my $go_connect_method = delete $go_attr{go_connect_method}; + my $transport_class = delete $go_attr{go_transport} or return $drh->set_err(1, "No transport= argument in '$orig_dsn'"); $transport_class = "DBD::Gofer::Transport::$transport_class" @@ -178,8 +191,10 @@ delete @{$go_attr}{qw(Profile HandleError HandleSetErr Callbacks)}; # delete any attributes that should only apply to the client-side delete @{$go_attr}{qw(RootClass DbTypeSubclass)}; + + $go_connect_method ||= $go_policy->connect_method($remote_dsn, $go_attr) || 'connect'; $request_class->new({ - connect_args => [ $remote_dsn, $go_attr ] + dbh_connect_call => [ $go_connect_method, $remote_dsn, $user, $auth, $go_attr ], }) } or return $drh->set_err(1, "Can't instanciate $request_class: $@"); @@ -482,7 +497,11 @@ my $policy = delete($attr->{go_policy}) || $dbh->{go_policy}; my $lii_args = delete $attr->{go_last_insert_id_args}; - my $go_prepare = delete($attr->{go_prepare}) || $dbh->{go_prepare} || 'prepare'; + my $go_prepare = delete($attr->{go_prepare_method}) + || $dbh->{go_prepare_method} + || $policy->prepare_method($dbh, $statement, $attr) + || 'prepare'; # e.g. for code not using placeholders + # set to undef if there are no attributes left for the actual prepare call $attr = undef if $attr and not %$attr; my ($sth, $sth_inner) = DBI::_new_sth($dbh, { @@ -506,9 +525,10 @@ sub prepare_cached { my ($dbh, $sql, $attr, $if_active)= @_; + $attr ||= {}; return $dbh->SUPER::prepare_cached($sql, { - ($attr ? %$attr : ()), - go_prepare => 'prepare_cached', + %$attr, + go_prepare_method => $attr->{go_prepare_method} || 'prepare_cached', }, $if_active); } Modified: dbi/trunk/lib/DBD/Gofer/Policy/Base.pm ============================================================================== --- dbi/trunk/lib/DBD/Gofer/Policy/Base.pm (original) +++ dbi/trunk/lib/DBD/Gofer/Policy/Base.pm Tue Apr 10 08:16:05 2007 @@ -15,6 +15,12 @@ our $AUTOLOAD; my %policy_defaults = ( + # force connect method (unless overridden by go_connect_method=>'...' attribute) + # if false: call same method on client as on server + connect_method => 'connect', + # force prepare method (unless overridden by go_prepare_method=>'...' attribute) + # if false: call same method on client as on server + prepare_method => 'prepare', skip_connect_check => 0, skip_default_methods => 0, skip_prepare_check => 0, @@ -50,7 +56,7 @@ # $policy->foo($attr, ...) #carp "$policy_name($_[1],...)"; # return the policy default value unless an attribute overrides it - return ($_[1] && exists $_[1]->{$policy_attr_name}) + return (ref $_[1] && exists $_[1]->{$policy_attr_name}) ? $_[1]->{$policy_attr_name} : $policy_default; }; Modified: dbi/trunk/lib/DBD/Gofer/Policy/classic.pm ============================================================================== --- dbi/trunk/lib/DBD/Gofer/Policy/classic.pm (original) +++ dbi/trunk/lib/DBD/Gofer/Policy/classic.pm Tue Apr 10 08:16:05 2007 @@ -16,6 +16,12 @@ __PACKAGE__->create_policy_subs({ + # always use connect_cached on server + connect_method => 'connect_cached', + + # use same methods on server as is called on client + prepare_method => '', + # don't skip the connect check since that also sets dbh attributes # although this makes connect more expensive, that's partly offset # by skip_ping=>1 below, which makes connect_cached very fast. Modified: dbi/trunk/lib/DBD/Gofer/Policy/rush.pm ============================================================================== --- dbi/trunk/lib/DBD/Gofer/Policy/rush.pm (original) +++ dbi/trunk/lib/DBD/Gofer/Policy/rush.pm Tue Apr 10 08:16:05 2007 @@ -16,6 +16,13 @@ __PACKAGE__->create_policy_subs({ + # always use connect_cached on server + connect_method => 'connect_cached', + + # use same methods on server as is called on client + # (because code not using placeholders would bloat the sth cache) + prepare_method => '', + # Skipping the connect check is fast, but it also skips # fetching the remote dbh attributes! # Make sure that your application doesn't need access to dbh attributes. Modified: dbi/trunk/lib/DBI/Gofer/Execute.pm ============================================================================== --- dbi/trunk/lib/DBI/Gofer/Execute.pm (original) +++ dbi/trunk/lib/DBI/Gofer/Execute.pm Tue Apr 10 08:16:05 2007 @@ -133,14 +133,16 @@ } } - my ($dsn, $attr) = @{ $request->connect_args }; + my ($connect_method, $dsn, $username, $password, $attr) = @{ $request->dbh_connect_call }; + $connect_method ||= 'connect_cached'; + # delete attributes we don't want to affect the server-side # (Could just do this on client-side and trust the client. DoS?) delete @{$attr}{qw(Profile InactiveDestroy HandleError HandleSetErr TraceLevel Taint TaintIn TaintOut)}; - my $connect_method = 'connect_cached'; - my $check_connect = $self->check_connect; - $check_connect->($dsn, $attr, $connect_method, $request) if $check_connect; + if (my $check_connect = $self->check_connect) { + $check_connect->($dsn, $attr, $connect_method, $request); + } $dsn = $self->forced_connect_dsn || $dsn || $self->default_connect_dsn or die "No forced_connect_dsn, requested dsn, or default_connect_dsn for request"; Modified: dbi/trunk/lib/DBI/Gofer/Request.pm ============================================================================== --- dbi/trunk/lib/DBI/Gofer/Request.pm (original) +++ dbi/trunk/lib/DBI/Gofer/Request.pm Tue Apr 10 08:16:05 2007 @@ -18,7 +18,7 @@ __PACKAGE__->mk_accessors(qw( version - connect_args + dbh_connect_call dbh_method_call dbh_wantarray dbh_attributes @@ -38,7 +38,7 @@ sub reset { my $self = shift; # remove everything except connect and version - %$self = ( version => $self->{version}, connect_args => $self->{connect_args} ); + %$self = ( version => $self->{version}, dbh_connect_call => $self->{dbh_connect_call} ); } sub is_sth_request { @@ -62,10 +62,16 @@ push @s, join(", ", map { "$_=>".$context->{$_} } @keys); } - my ($dsn, $attr) = @{ $self->connect_args }; - my $tmp = { %{$attr||{}} }; # copy so we can edit - $tmp->{Password} = '***' if exists $tmp->{Password}; - push @s, sprintf "dbh= connect('%s', , , { %s })", $dsn, neat_list([ %$tmp ]); + my ($method, $dsn, $user, $pass, $attr) = @{ $self->dbh_connect_call }; + $method ||= 'connect_cached'; + $pass = '***' if defined $pass; + my $tmp = ''; + if ($attr) { + $tmp = { %{$attr||{}} }; # copy so we can edit + $tmp->{Password} = '***' if exists $tmp->{Password}; + $tmp = "{ ".neat_list([ %$tmp ])." }"; + } + push @s, sprintf "dbh= $method(%s, %s)", neat_list([$dsn, $user, $pass]), $tmp; if (my $dbh_attr = $self->dbh_attributes) { push @s, sprintf "dbh->FETCH: %s", @$dbh_attr
