Author: timbo
Date: Mon Feb 12 09:35:33 2007
New Revision: 9091

Modified:
   dbi/trunk/Changes
   dbi/trunk/DBI.pm
   dbi/trunk/DBI.xs
   dbi/trunk/lib/DBD/ExampleP.pm
   dbi/trunk/lib/DBD/Gofer.pm
   dbi/trunk/lib/DBD/Gofer/Transport/pipeone.pm
   dbi/trunk/lib/DBD/Gofer/Transport/stream.pm
   dbi/trunk/lib/DBI/Gofer/Execute.pm
   dbi/trunk/lib/DBI/Gofer/Transport/Base.pm
   dbi/trunk/lib/DBI/Gofer/Transport/mod_perl.pm
   dbi/trunk/lib/DBI/PurePerl.pm
   dbi/trunk/lib/DBI/Util/_accessor.pm
   dbi/trunk/t/10examp.t
   dbi/trunk/t/50dbm.t

Log:
set_err won't duplicate an error message now.
Integrate gofer tracing into DBI tracing some more.
Make dir-sensitive tests use absolute dir so they'll work with gofer mod_perl.
Polish up gofer mod_perl config mechanism.
Handle Username and Password as attributes to integrate with gofer config.
Purge all cached dbh (and thus sth) from time-to-time (will config later).


Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes   (original)
+++ dbi/trunk/Changes   Mon Feb 12 09:35:33 2007
@@ -31,6 +31,8 @@
     to only be given for dbh that have active sth or are not AutoCommit.
   Changed take_imp_data to call finish on all Active child sth.
   Changed DBI::PurePerl trace() method to be more consistent.
+  Changed set_err method to effectively not append to errstr if the new errstr
+    is the same as the current one.
   Changed handle factory methods, like connect, prepare, and table_info,
     to copy any error/warn/info state of the handle being returned
     up into the handle the method was called on.

Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm    (original)
+++ dbi/trunk/DBI.pm    Mon Feb 12 09:35:33 2007
@@ -3148,11 +3148,12 @@
 Some special rules apply if the C<err> or C<errstr>
 values for the handle are I<already> set...
 
-If C<errstr> is true then: "C< [err was %s now %s]>" is appended if
-$err is true and C<err> is already true; "C< [state was %s now %s]>"
-is appended if $state is true and C<state> is already true; then
-"C<\n>" and the new $errstr are appended. Obviously the C<%s>'s
-above are replaced by the corresponding values.
+If C<errstr> is true then: "C< [err was %s now %s]>" is appended if $err is
+true and C<err> is already true and the new err value differs from the original
+one. Similarly "C< [state was %s now %s]>" is appended if $state is true and 
C<state> is
+already true and the new state value differs from the original one. Finally
+"C<\n>" and the new $errstr are appended if $errstr differs from the existing
+errstr value. Obviously the C<%s>'s above are replaced by the corresponding 
values.
 
 The handle C<err> value is set to $err if: $err is true; or handle
 C<err> value is undef; or $err is defined and the length is greater

Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs    (original)
+++ dbi/trunk/DBI.xs    Mon Feb 12 09:35:33 2007
@@ -513,12 +513,14 @@
 
     if (SvTRUE(h_errstr)) {
        /* append current err, if any, to errstr if it's going to change */
-       if (SvTRUE(h_err) && SvTRUE(err))
+       if (SvTRUE(h_err) && SvTRUE(err) && strNE(SvPV_nolen(h_err), 
SvPV_nolen(err)))
            sv_catpvf(h_errstr, " [err was %s now %s]", SvPV_nolen(h_err), 
SvPV_nolen(err));
-       if (SvTRUE(h_state) && SvTRUE(state))
+       if (SvTRUE(h_state) && SvTRUE(state) && strNE(SvPV_nolen(h_state), 
SvPV_nolen(state)))
            sv_catpvf(h_errstr, " [state was %s now %s]", SvPV_nolen(h_state), 
SvPV_nolen(state));
-       sv_catpvn(h_errstr, "\n", 1);
-        sv_catsv(h_errstr, errstr);
+        if (strNE(SvPV_nolen(h_errstr), SvPV_nolen(errstr))) {
+            sv_catpvn(h_errstr, "\n", 1);
+            sv_catsv(h_errstr, errstr);
+        }
     }
     else
        sv_setsv(h_errstr, errstr);

Modified: dbi/trunk/lib/DBD/ExampleP.pm
==============================================================================
--- dbi/trunk/lib/DBD/ExampleP.pm       (original)
+++ dbi/trunk/lib/DBD/ExampleP.pm       Mon Feb 12 09:35:33 2007
@@ -135,7 +135,7 @@
        # Return a list of all subdirectories
        my $dh = Symbol::gensym(); # "DBD::ExampleP::".++$DBD::ExampleP::gensym;
        my $haveFileSpec = eval { require File::Spec };
-       my $dir = $haveFileSpec ? File::Spec->curdir() : ".";
+       my $dir = $catalog || ($haveFileSpec ? File::Spec->curdir() : ".");
        my @list;
        if ($types{VIEW}) {     # for use by test harness
            push @list, [ undef, "schema",  "table",  'VIEW', undef ];
@@ -147,13 +147,13 @@
        if ($types{TABLE}) {
            no strict 'refs';
            opendir($dh, $dir)
-               or return $dbh->set_err(int($!),
-                                       "Failed to open directory $dir: $!");
-           while (defined(my $file = readdir($dh))) {
+               or return $dbh->set_err(int($!), "Failed to open directory 
$dir: $!");
+           while (defined(my $item = readdir($dh))) {
+                my $file = ($haveFileSpec) ? File::Spec->catdir($dir,$item) : 
$item;
                next unless -d $file;
                my($dev, $ino, $mode, $nlink, $uid) = lstat($file);
                my $pwnam = undef; # eval { scalar(getpwnam($uid)) } || $uid;
-               push @list, [ $dir, $pwnam, $file, 'TABLE', undef ];
+               push @list, [ $dir, $pwnam, $item, 'TABLE', undef ];
            }
            close($dh);
        }

Modified: dbi/trunk/lib/DBD/Gofer.pm
==============================================================================
--- dbi/trunk/lib/DBD/Gofer.pm  (original)
+++ dbi/trunk/lib/DBD/Gofer.pm  Mon Feb 12 09:35:33 2007
@@ -134,14 +134,18 @@
         my $go_trans = eval { $transport_class->new(\%dsn_attr) }
             or return $drh->set_err(1, "Error instanciating $transport_class: 
$@");
 
-        # XXX user/pass of fwd server vs db server
         my $request_class = "DBI::Gofer::Request";
         my $go_request = eval {
-            # copy and delete any attributes we can't serialize (and don't 
want to)
             my $go_attr = { %$attr };
+            # XXX user/pass of fwd server vs db server ? also impact of 
autoproxy
+            if ($user) {
+                $go_attr->{Username} = $user;
+                $go_attr->{Password} = $auth;
+            }
+            # delete any attributes we can't serialize (or don't want to)
             delete @{$go_attr}{qw(Profile HandleError HandleSetErr Callbacks)};
             $request_class->new({
-                connect_args => [ $remote_dsn, $user, $auth, $go_attr ]
+                connect_args => [ $remote_dsn, $go_attr ]
             })
         } or return $drh->set_err(1, "Error instanciating $request_class $@");
 
@@ -200,6 +204,8 @@
 
         my $transport = $dbh->{go_trans}
             or return $dbh->set_err(1, "Not connected (no transport)");
+        my $TraceLevel = $dbh->FETCH('TraceLevel');
+        $transport->trace( $TraceLevel - 4 ) if $TraceLevel > 4;
 
         eval { $transport->transmit_request($request) }
             or return $dbh->set_err(1, "transmit_request failed: $@");
@@ -369,8 +375,11 @@
 
         my $transport = $sth->{go_trans}
             or return $sth->set_err(1, "Not connected (no transport)");
+        my $TraceLevel = $sth->FETCH('TraceLevel');
+        $transport->trace( $TraceLevel - 4 ) if $TraceLevel > 4;
         eval { $transport->transmit_request($request) }
             or return $sth->set_err(1, "transmit_request failed: $@");
+
         my $response = $transport->receive_response;
         $sth->{go_response} = $response;
         delete $sth->{go_method_calls};

Modified: dbi/trunk/lib/DBD/Gofer/Transport/pipeone.pm
==============================================================================
--- dbi/trunk/lib/DBD/Gofer/Transport/pipeone.pm        (original)
+++ dbi/trunk/lib/DBD/Gofer/Transport/pipeone.pm        Mon Feb 12 09:35:33 2007
@@ -38,7 +38,7 @@
     my ($wfh, $rfh, $efh) = (gensym, gensym, gensym);
     my $pid = open3($wfh, $rfh, $efh, @$cmd)
         or die "error starting $cmd: $!\n";
-    warn "Started pid $pid: $cmd\n" if $self->trace;
+    $self->trace_msg("Started pid $pid: $cmd\n") if $self->trace;
 
     return {
         cmd=>$cmd,

Modified: dbi/trunk/lib/DBD/Gofer/Transport/stream.pm
==============================================================================
--- dbi/trunk/lib/DBD/Gofer/Transport/stream.pm (original)
+++ dbi/trunk/lib/DBD/Gofer/Transport/stream.pm Mon Feb 12 09:35:33 2007
@@ -56,7 +56,7 @@
         # send frozen request
         print $wfh $frozen_request # autoflush enabled
             or die "Error sending request: $!";
-        warn "Request: $frozen_request" if $self->trace >= 3;
+        $self->trace_msg("Request: $frozen_request\n") if $self->trace >= 3;
     };
     if ($@) {
         my $response = DBI::Gofer::Response->new({ err => 1, errstr => $@ }); 
@@ -101,7 +101,8 @@
         return DBI::Gofer::Response->new({ err => 1, errstr => $msg }); 
     }
     #warn DBI::neat($frozen_response);
-    warn "Gofer stream stderr message: $stderr_msg\n" if $stderr_msg && 
$self->trace;
+    $self->trace_msg("Gofer stream stderr message: $stderr_msg\n")
+        if $stderr_msg && $self->trace;
 
     # XXX need to be able to detect and deal with corruption
     $response = $self->thaw_data(pack("H*",$frozen_response));

Modified: dbi/trunk/lib/DBI/Gofer/Execute.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Execute.pm  (original)
+++ dbi/trunk/lib/DBI/Gofer/Execute.pm  Mon Feb 12 09:35:33 2007
@@ -19,16 +19,23 @@
 our $VERSION = sprintf("0.%06d", q$Revision$ =~ /(\d+)/o);
 
 __PACKAGE__->mk_accessors(qw(
-    connect_args
-    dbh_method_name
-    dbh_method_args
-    dbh_wantarray
-    dbh_last_insert_id_args
-    sth_method_calls
-    sth_result_attr
+    check_connect
+    default_connect_dsn
+    forced_connect_dsn
+    default_connect_attributes
+    forced_connect_attributes
+    requests_served_count
 )); 
 
 
+sub new {
+    my ($self, $args) = @_;
+    $args->{default_connect_attributes} ||= {};
+    $args->{forced_connect_attributes}  ||= {};
+    return $self->SUPER::new($args);
+}
+
+
 my @sth_std_attr = qw(
     NUM_OF_PARAMS
     NUM_OF_FIELDS
@@ -84,20 +91,58 @@
 sub _connect {
     my ($self, $request) = @_;
 
-    local $ENV{DBI_AUTOPROXY}; # limit the insanity
+    # just a quick hack for now
+    if (++$self->{request_count} % 100 == 0) { # XXX config
+        # discard CachedKids from time to time
+        my %drivers = DBI->installed_drivers();
+        while ( my ($driver, $drh) = each %drivers ) {
+            next if $driver eq 'Gofer'; # ie transport=null when testing
+            next unless my $CK = $drh->{CachedKids};
+            # XXX currently we discard all regardless
+            # because that avoids the need to also handle
+            # limiting the prepared statement cache
+            my $cached_dbh_count = keys %$CK;
+            #next unless $cached_dbh_count > 20; # XXX config
+
+            DBI->trace_msg("Clearing $cached_dbh_count cached dbh from 
$driver");
+            $_->{Active} && $_->disconnect for values %$CK;
+            %$CK = ();
+        }
+    }
 
-    my ($dsn, $u, $p, $attr) = @{ $request->connect_args };
+    my ($dsn, $attr) = @{ $request->connect_args };
     # delete attributes we don't want to affect the server-side
-    delete @{$attr}{qw(Profile InactiveDestroy Warn HandleError HandleSetErr 
TraceLevel Taint TaintIn TaintOut)};
+    # (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';
 
-    # XXX need way to limit/purge connect cache over time
-    my $dbh = DBI->$connect_method($dsn, $u, $p, {
+    my $check_connect = $self->check_connect;
+    $check_connect->($dsn, $attr, $connect_method, $request) if $check_connect;
+
+    $dsn = $self->forced_connect_dsn || $dsn || $self->default_connect_dsn
+        or die "No forced_connect_dsn, requested dsn, or default_connect_dsn 
for request";
+
+    local $ENV{DBI_AUTOPROXY}; # limit the insanity
+
+    # XXX implement our own private connect_cached method?
+    my $dbh = DBI->$connect_method($dsn, undef, undef, {
+
+        # the configured default attributes, if any
+        %{ $self->default_connect_attributes },
+
+        # the requested attributes
         %$attr,
-        # force some attributes the way we want them
+
+        # force some attributes the way we'd like them
         PrintWarn  => 0,
         PrintError => 0,
+
+        # the configured default attributes, if any
+        %{ $self->forced_connect_attributes },
+
+        # RaiseError must be enabled
         RaiseError => 1,
+
         # ensure this connect_cached doesn't have the same args as the client
         # because that causes subtle issues if in the same process (ie 
transport=null)
         dbi_go_execute_unique => __PACKAGE__,
@@ -145,12 +190,16 @@
     my @warnings;
     local $SIG{__WARN__} = sub { push @warnings, @_ };
     DBI->trace_msg("-----> execute_request\n");
+
     my $response = eval {
+
         ($request->is_sth_request)
             ? $self->execute_sth_request($request)
             : $self->execute_dbh_request($request);
     };
-    $response = $self->new_response_with_err(undef, $@) if $@;
+    $response = $self->new_response_with_err(undef, $@)
+        if $@;
+
     $response->warnings([EMAIL PROTECTED]) if @warnings;
     DBI->trace_msg("<----- execute_request\n");
     return $response;
@@ -267,4 +316,5 @@
     return \%meta;
 }
 
+
 1;

Modified: dbi/trunk/lib/DBI/Gofer/Transport/Base.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Transport/Base.pm   (original)
+++ dbi/trunk/lib/DBI/Gofer/Transport/Base.pm   Mon Feb 12 09:35:33 2007
@@ -65,8 +65,23 @@
 sub _dump {
     my ($self, $label, $data) = @_;
     require Data::Dumper;
-    # XXX config dumper format
-    warn "$label=".Data::Dumper::Dumper($data);
+    local $Data::Dumper::Indent    = 1;
+    local $Data::Dumper::Terse     = 1;
+    local $Data::Dumper::Useqq     = 1;
+    local $Data::Dumper::Sortkeys  = 1;
+    local $Data::Dumper::Quotekeys = 0;
+    local $Data::Dumper::Deparse   = 0;
+    local $Data::Dumper::Purity    = 0;
+    $self->trace_msg("$label=".Data::Dumper::Dumper($data));
+}
+
+
+sub trace_msg {
+    my ($self, $msg, $min_level) = @_;
+    $min_level = 1 unless defined $min_level;
+    # modeled on DBI's trace_msg method
+    return 0 if $self->trace < $min_level;
+    return DBI->trace_msg($msg, 1); # XXX two min_levels at play here
 }
 
 1;

Modified: dbi/trunk/lib/DBI/Gofer/Transport/mod_perl.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Transport/mod_perl.pm       (original)
+++ dbi/trunk/lib/DBI/Gofer/Transport/mod_perl.pm       Mon Feb 12 09:35:33 2007
@@ -13,7 +13,7 @@
 
 my $transport = __PACKAGE__->new();
 
-my %executor_configs;
+my %executor_configs = ( default => { } );
 my %executor_cache;
 
 
@@ -35,6 +35,14 @@
 }
 
 
+my $proto_config = { # defines valid keys and types for exector config
+    default_connect_dsn => 1,
+    forced_connect_dsn  => 1,
+    default_connect_attributes => {},
+    forced_connect_attributes  => {},
+};
+
+
 sub executor_for_uri {
     my ($self, $r) = @_;
     my $uri = $r->uri;
@@ -50,17 +58,23 @@
         if (!$config) {
             # die if an unknown config is requested but not defined
             # (don't die for 'default' unless it was explicitly requested)
-            die "$uri: GoferConfig '$config_name' not defined"
-                unless $config_name eq 'default'
-                   and !$r_dir_config->get('GoferConfig');
+            die "$uri: GoferConfig '$config_name' not defined";
             next;
         }
-        for my $type (qw(require default force)) {
-            my $type_config = $config->{$type};
-            next if !$type_config or !%$type_config;
-            warn "$uri: GoferConfig $config_name $type (@{[ %$type_config 
]})\n";
-            my $merged = $merged_config{$type} ||= {};
-            $merged->{$_} = $type_config->{$_} for keys %$type_config;
+        while ( my ($item_name, $proto_type) = each %$proto_config ) {
+            next if not exists $config->{$item_name};
+            my $item_value = $config->{$item_name};
+            if (ref $proto_type) {
+                my $merged = $merged_config{$item_name} ||= {};
+                warn "$uri: GoferConfig $config_name $item_name (@{[ 
%$item_value ]})\n"
+                    if keys %$item_value;
+                $merged->{$_} = $item_value->{$_} for keys %$item_value;
+            }
+            else {
+                warn "$uri: GoferConfig $config_name $item_name: 
'$item_value'\n"
+                    if defined $item_value;
+                $merged_config{$item_name} = $item_value;
+            }
         }
     }
     my $executor = DBI::Gofer::Execute->new(\%merged_config);
@@ -70,6 +84,11 @@
 
 sub configuration { # one-time setup from httpd.conf
     my ($self, $configs) = @_;
+    while ( my ($config_name, $config) = each %$configs ) {
+        my @bad = grep { not exists $proto_config->{$_} } keys %$config;
+        warn "Unknown keys in $self configuration '$config_name': @bad\n"
+            if @bad;
+    }
     %executor_configs = %$configs;
 }
 

Modified: dbi/trunk/lib/DBI/PurePerl.pm
==============================================================================
--- dbi/trunk/lib/DBI/PurePerl.pm       (original)
+++ dbi/trunk/lib/DBI/PurePerl.pm       Mon Feb 12 09:35:33 2007
@@ -768,10 +768,10 @@
 
     if ($h->{errstr}) {
        $h->{errstr} .= sprintf " [err was %s now %s]", $h->{err}, $errnum
-               if $h->{err} && $errnum;
+               if $h->{err} && $errnum && $h->{err} ne $errnum;
        $h->{errstr} .= sprintf " [state was %s now %s]", $h->{state}, $state
-               if $h->{state} and $h->{state} ne "S1000" && $state;
-       $h->{errstr} .= "\n$msg";
+               if $h->{state} and $h->{state} ne "S1000" && $state && 
$h->{state} ne $state;
+       $h->{errstr} .= "\n$msg" if $h->{errstr} ne $msg;
        $DBI::errstr = $h->{errstr};
     }
     else {

Modified: dbi/trunk/lib/DBI/Util/_accessor.pm
==============================================================================
--- dbi/trunk/lib/DBI/Util/_accessor.pm (original)
+++ dbi/trunk/lib/DBI/Util/_accessor.pm Mon Feb 12 09:35:33 2007
@@ -1,5 +1,6 @@
 package DBI::Util::_accessor;
 use strict;
+use Carp;
 our $VERSION = sprintf("0.%06d", q$Revision$ =~ /(\d+)/);
 
 # heavily cut-down (but compatible) version of Class::Accessor::Fast to avoid 
the dependency
@@ -7,8 +8,12 @@
 sub new {
     my($proto, $fields) = @_;
     my($class) = ref $proto || $proto;
-    $fields = {} unless defined $fields;
-    # make a copy of $fields.
+    $fields ||= {};
+
+    my @dubious = grep { !m/^_/ && !$proto->can($_) } keys %$fields;
+    carp "$class doesn't have accessors for fields: @dubious" if @dubious;
+
+    # make a (shallow) copy of $fields.
     bless {%$fields}, $class;
 }
 

Modified: dbi/trunk/t/10examp.t
==============================================================================
--- dbi/trunk/t/10examp.t       (original)
+++ dbi/trunk/t/10examp.t       Mon Feb 12 09:35:33 2007
@@ -492,22 +492,24 @@
 
 print "table_info\n";
 # First generate a list of all subdirectories
-$dir = $haveFileSpec ? File::Spec->curdir() : ".";
+$dir = getcwd();
 ok(opendir(DIR, $dir));
 my(%dirs, %unexpected, %missing);
 while (defined(my $file = readdir(DIR))) {
     $dirs{$file} = 1 if -d $file;
 }
+print "Local $dir subdirs: @{[ keys %dirs ]}\n";
 closedir(DIR);
-my $sth = $dbh->table_info(undef, undef, "%", "TABLE");
+#$dbh->trace(9);
+my $sth = $dbh->table_info($dir, undef, "%", "TABLE");
 ok($sth);
 %unexpected = %dirs;
 %missing = ();
 while (my $ref = $sth->fetchrow_hashref()) {
     if (exists($unexpected{$ref->{'TABLE_NAME'}})) {
-               delete $unexpected{$ref->{'TABLE_NAME'}};
+        delete $unexpected{$ref->{'TABLE_NAME'}};
     } else {
-               $missing{$ref->{'TABLE_NAME'}} = 1;
+        $missing{$ref->{'TABLE_NAME'}} = 1;
     }
 }
 ok(keys %unexpected == 0)
@@ -515,6 +517,7 @@
 ok(keys %missing == 0)
     or print "Missing directories: ", join(",", keys %missing), "\n";
 
+#$dbh->trace(0); die 1;
 
 print "tables\n";
 my @tables_expected = (

Modified: dbi/trunk/t/50dbm.t
==============================================================================
--- dbi/trunk/t/50dbm.t (original)
+++ dbi/trunk/t/50dbm.t Mon Feb 12 09:35:33 2007
@@ -4,6 +4,7 @@
 use strict;
 use File::Path;
 use Test::More;
+use Cwd;
 use Config qw(%Config);
 
 my $using_dbd_gofer = ($ENV{DBI_AUTOPROXY}||'') =~ /^dbi:Gofer.*transport=/i;
@@ -56,7 +57,7 @@
     }
 }
 
-my $dir = './test_output';
+my $dir = getcwd().'/test_output';
 
 rmtree $dir;
 mkpath $dir;

Reply via email to