Author: timbo
Date: Tue May  1 04:03:38 2007
New Revision: 9478

Modified:
   dbi/trunk/Changes
   dbi/trunk/DBI.pm
   dbi/trunk/DBI.xs
   dbi/trunk/lib/DBD/Gofer.pm
   dbi/trunk/lib/DBD/Gofer/Transport/Base.pm
   dbi/trunk/lib/DBI/Gofer/Execute.pm
   dbi/trunk/lib/DBI/Gofer/Request.pm
   dbi/trunk/lib/DBI/Gofer/Response.pm
   dbi/trunk/lib/DBI/PurePerl.pm
   dbi/trunk/lib/DBI/Util/_accessor.pm
   dbi/trunk/t/06attrs.t
   dbi/trunk/t/09trace.t
   dbi/trunk/t/10examp.t
   dbi/trunk/t/86gofer_fail.t

Log:
Added ReadOnly attribute.
Consider ReadOnly attribute in controling gofer retries.
Added go_retry_hook to allow application control of retries.
Add tests for ReadOnly.
Rejig trace tests.


Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes   (original)
+++ dbi/trunk/Changes   Tue May  1 04:03:38 2007
@@ -22,7 +22,7 @@
     only fetch one result set - handy for Sybase/MSSQL users
     will accept streamed resultsets
 Pedantic policy should force a fresh connect each time - add new policy item
-Add attr-passthru to prepare()? ie for gofer cache control
+Add attr-passthru to prepare()? ie for gofer cache control & ReadOnly
 Terminology for client and server ends
 Document user/passwd issues at the various levels of the gofer stack
 Policy's from pod
@@ -65,6 +65,7 @@
     Callbacks can now elect to provide a value to be returned, in which case
     the method won't be called. A callback for "*" is applied to all methods
     that don't have their own callback.
+  Added $h->{ReadOnly} attribute.
   Added support for DBI Profile Path to contain refs to scalars
     which will be de-ref'd for each profile sample.
   Added dbilogstrip utility to edit DBI logs for diff'ing (gets installed)

Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm    (original)
+++ dbi/trunk/DBI.pm    Tue May  1 04:03:38 2007
@@ -636,6 +636,7 @@
            # been called yet and so the dbh errstr would not have been copied
            # up to the drh errstr. Certainly true for connect_cached!
            my $errstr = $DBI::errstr;
+            # Getting '(no error string)' here is a symptom of a ref loop
            $errstr = '(no error string)' if !defined $errstr;
            my $msg = "$class connect('$dsn','$user',...) failed: $errstr";
            DBI->trace_msg("       $msg\n");
@@ -3804,6 +3805,25 @@
 
 The C<Profile> attribute was added in DBI 1.24.
 
+=item C<ReadOnly> (boolean, inherited)
+
+An application can set the C<ReadOnly> attribute of a handle to a true value to
+indicate that it will not be attempting to make any changes (insert, delete,
+update etc) using that handle or any children of it.
+
+If the driver can make the handle truly read-only (by issuing a statement like
+"C<set transaction read only>" as needed, for example) then it should.
+Otherwise the attribute is simply advisory.
+
+A driver can set the C<ReadOnly> attribute itself to indicate that the data it
+is connected to cannot be changed for some reason.
+
+Library modules and proxy drivers can use the attribute to influence their 
behavior.
+For example, the DBD::Gofer driver considers the C<ReadOnly> attribute when
+making a decison about whether to retry an operation that failed.
+
+The attribute should be set to 1 or 0. (Other values are reserved.)
+
 =item C<private_your_module_name_*>
 
 The DBI provides a way to store extra information in a DBI handle as

Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs    (original)
+++ dbi/trunk/DBI.xs    Tue May  1 04:03:38 2007
@@ -1091,9 +1091,8 @@
        if (parent) {
            dbih_setup_attrib(aTHX_ h,imp,"HandleSetErr",parent,0,1);
            dbih_setup_attrib(aTHX_ h,imp,"HandleError",parent,0,1);
-           if (DBIc_has(parent_imp,DBIcf_Profile)) {
-               dbih_setup_attrib(aTHX_ h,imp,"Profile",parent,0,1);
-           }
+           dbih_setup_attrib(aTHX_ h,imp,"ReadOnly",parent,0,1);
+            dbih_setup_attrib(aTHX_ h,imp,"Profile",parent,0,1);
            DBIc_LongReadLen(imp) = DBIc_LongReadLen(parent_imp);
 #ifdef sv_rvweaken
            if (1) {
@@ -1735,6 +1734,7 @@
     /* these are here due to clone() needing to set attribs through a public 
api */
     else if (htype<=DBIt_DB && (strEQ(key, "Name")
                            || strEQ(key,"ImplementorClass")
+                           || strEQ(key,"ReadOnly")
                            || strEQ(key,"Statement")
                            || strEQ(key,"Username")
        /* these are here for backwards histerical raisons */
@@ -2069,6 +2069,7 @@
                ||      (*key=='P' && strEQ(key, "ParamArrays"))
                ||      (*key=='P' && strEQ(key, "ParamValues"))
                ||      (*key=='P' && strEQ(key, "Profile"))
+               ||      (*key=='R' && strEQ(key, "ReadOnly"))
                ||      (*key=='C' && strEQ(key, "CursorName"))
                ||      (*key=='C' && strEQ(key, "Callbacks"))
                ||      (*key=='U' && strEQ(key, "Username"))

Modified: dbi/trunk/lib/DBD/Gofer.pm
==============================================================================
--- dbi/trunk/lib/DBD/Gofer.pm  (original)
+++ dbi/trunk/lib/DBD/Gofer.pm  Tue May  1 04:03:38 2007
@@ -248,10 +248,10 @@
     sub go_dbh_method {
         my $dbh = shift;
         my $meta = shift;
-        # $method and @args left in @_
+        # @_ now contains ($method_name, @args)
 
         my $request = $dbh->{go_request};
-        $request->init_request([ wantarray, @_ ]);
+        $request->init_request([ wantarray, @_ ], $dbh);
         ++$dbh->{go_request_count};
 
         my $go_policy = $dbh->{go_policy};
@@ -567,7 +567,7 @@
         ++$dbh->{go_request_count};
 
         my $request = $sth->{go_request};
-        $request->init_request($sth->{go_prepare_call});
+        $request->init_request($sth->{go_prepare_call}, $sth);
         $request->sth_method_calls(delete $sth->{go_method_calls})
             if $sth->{go_method_calls};
         $request->sth_result_attr({}); # (currently) also indicates this is an 
sth request

Modified: dbi/trunk/lib/DBD/Gofer/Transport/Base.pm
==============================================================================
--- dbi/trunk/lib/DBD/Gofer/Transport/Base.pm   (original)
+++ dbi/trunk/lib/DBD/Gofer/Transport/Base.pm   Tue May  1 04:03:38 2007
@@ -19,8 +19,13 @@
     go_dsn
     go_url
     go_timeout
+    go_retry_hook
     go_retry_limit
 ));
+__PACKAGE__->mk_accessors_using(make_accessor_autoviv_hashref => qw(
+    meta
+));
+
 
 
 sub _init_trace { $ENV{DBD_GOFER_TRACE} || 0 }
@@ -66,7 +71,7 @@
     my $response;
     do {
         $response = $transmit_sub->();
-    } while ( $response && $self->response_needs_retransmit($response, 
$request) );
+    } while ( $response && $self->response_needs_retransmit($request, 
$response) );
     return $response;
 }
 
@@ -96,42 +101,56 @@
     my $response;
     do {
         $response = $receive_sub->();
-        if ($self->response_needs_retransmit($response, $request)) {
+        if ($self->response_needs_retransmit($request, $response)) {
             $response = $self->_transmit_request_with_retries($request, 
$retransmit_sub);
             $response ||= $receive_sub->();
         }
-    } while ( $self->response_needs_retransmit($response, $request) );
+    } while ( $self->response_needs_retransmit($request, $response) );
 
     return $response;
 }
 
 
 sub response_needs_retransmit {
-    my ($self, $response, $request) = @_;
+    my ($self, $request, $response) = @_;
 
     my $err = $response->err
-        or return 0; # nothing wen't wrong
+        or return 0; # nothing went wrong
 
     my $retry;
-    my $errstr = $response->errstr || '';
 
-    my $idempotent = 0; # XXX set to 1 for idempotent requests, ie selects
+    # give the user a chance to express a preference (or undef for default)
+    if (my $go_retry_hook = $self->go_retry_hook) {
+        $retry = $go_retry_hook->($request, $response, $self);
+        $self->trace_msg(sprintf "response_needs_retransmit: go_retry_hook 
returned %s\n",
+            (defined $retry) ? $retry : 'undef');
+    }
 
-    $retry = 1 if $errstr =~ m/fake error induced by DBI_GOFER_RANDOM_FAIL/;
+    if (not defined $retry) {
+        my $errstr = $response->errstr || '';
+        $retry = 1 if $errstr =~ m/fake error induced by 
DBI_GOFER_RANDOM_FAIL/;
+    }
 
-    if (!$retry) {
+    if (not defined $retry) {
+        my $idempotent = $request->is_idempotent; # i.e. is SELECT or ReadOnly 
was set
+        $retry = 1 if $idempotent;
+    }
+
+    if (!$retry) {  # false or undef
         $self->trace_msg("response_needs_retransmit: response not suitable for 
retry\n");
         return 0;
     }
+
     my $meta = $request->meta;
-    my $retry_count = ++$meta->{retry_count};
     my $retry_limit = $self->go_retry_limit;
     $retry_limit = 2 unless defined $retry_limit;
-    if ($retry_count > $retry_limit) {
-        $self->trace_msg("response_needs_retransmit: $retry_count is too many 
retries\n");
+    if (($meta->{retry_count}||=0) >= $retry_limit) {
+        $self->trace_msg("response_needs_retransmit: $meta->{retry_count} is 
too many retries\n");
         return 0;
     }
-    $self->trace_msg("response_needs_retransmit: retry $retry_count\n");
+    ++$meta->{retry_count};                 # count for this request
+    ++$self->meta->{request_retry_count};   # cumulative transport stats
+    $self->trace_msg("response_needs_retransmit: retry 
$meta->{retry_count}\n");
     return 1;
 }
 

Modified: dbi/trunk/lib/DBI/Gofer/Execute.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Execute.pm  (original)
+++ dbi/trunk/lib/DBI/Gofer/Execute.pm  Tue May  1 04:03:38 2007
@@ -338,9 +338,9 @@
     $h = tied(%$h) || $h; # switch to inner handle
     my $attr_names = $h->{private_gofer_std_attr_names};
     return $attr_names if $attr_names;
-    # add ChopBlanks LongReadLen LongTruncOk because drivers may have 
different defaults
-    # plus Name so the client gets the real Name of the connection
-    my @attr_names = qw(ChopBlanks LongReadLen LongTruncOk Name);
+    # 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);
     if (my $pai = $h->private_attribute_info) {
         push @attr_names, keys %$pai;
     }

Modified: dbi/trunk/lib/DBI/Gofer/Request.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Request.pm  (original)
+++ dbi/trunk/lib/DBI/Gofer/Request.pm  Tue May  1 04:03:38 2007
@@ -15,9 +15,15 @@
 
 our $VERSION = sprintf("0.%06d", q$Revision$ =~ /(\d+)/o);
 
+use constant GOf_REQUEST_IDEMPOTENT => 0x0001;
+use constant GOf_REQUEST_READONLY   => 0x0002;
+    
+our @EXPORT = qw(GOf_REQUEST_IDEMPOTENT GOf_REQUEST_READONLY);
+
 
 __PACKAGE__->mk_accessors(qw(
     version
+    flags
     dbh_connect_call
     dbh_method_call
     dbh_attributes
@@ -25,7 +31,7 @@
     sth_method_calls
     sth_result_attr
 ));
-__PACKAGE__->mk_accessors_with(make_accessor_autoviv_hashref => qw(
+__PACKAGE__->mk_accessors_using(make_accessor_autoviv_hashref => qw(
     meta
 ));
 
@@ -38,21 +44,58 @@
 
 
 sub reset {
-    my $self = shift;
+    my ($self, $flags) = @_;
     # remove everything except connect and version
-    %$self = ( version => $self->{version}, dbh_connect_call => 
$self->{dbh_connect_call} );
+    %$self = (
+        version => $self->{version},
+        dbh_connect_call => $self->{dbh_connect_call},
+    );
+    $self->{flags} = $flags if $flags;
+}
+
+
+sub init_request {
+    my ($self, $method_and_args, $dbh) = @_;
+    $self->reset( $dbh->{ReadOnly} ? GOf_REQUEST_READONLY : 0 );
+    $self->dbh_method_call($method_and_args);
 }
 
+
 sub is_sth_request {
     return shift->{sth_result_attr};
 }
 
-sub init_request {
-    my ($self, $method_and_args) = @_;
-    $self->reset;
-    $self->dbh_method_call($method_and_args);
+
+sub statements {
+    my $self = shift;
+    my @statements;
+    my $statement_method_regex = qr/^(?:do|prepare)$/;
+    if (my $dbh_method_call = $self->dbh_method_call) {
+        my (undef, $method, $arg1) = @$dbh_method_call;
+        push @statements, $arg1 if $method =~ $statement_method_regex;
+    }
+    return @statements;
+}
+
+
+sub is_idempotent {
+    my $self = shift;
+
+    if (my $flags = $self->flags) {
+        return 1 if $flags & (GOf_REQUEST_IDEMPOTENT|GOf_REQUEST_READONLY);
+    }
+
+    # else check if all statements are select statement
+    my @statements = $self->statements;
+    # XXX this is very minimal for now, doesn't even allow comments before the 
select
+    # (and can't ever work for "exec stored_procedure_name" kinds of 
statements)
+    # XXX it also doesn't deal with multiple statements: prepare("select foo; 
update bar")
+    return 1 if @statements == grep { m/^ \s* SELECT \b/xmsi } @statements;
+
+    return 0;
 }
 
+
 sub summary_as_text {
     my $self = shift;
     my ($context) = @_;
@@ -74,6 +117,10 @@
     }
     push @s, sprintf "dbh= $method(%s, %s)", neat_list([$dsn, $user, $pass]), 
$tmp;
 
+    if (my $flags = $self->flags) {
+        push @s, sprintf "flags: 0x%x", $flags;
+    }
+
     if (my $dbh_attr = $self->dbh_attributes) {
         push @s, sprintf "dbh->FETCH: %s", @$dbh_attr
             if @$dbh_attr;

Modified: dbi/trunk/lib/DBI/Gofer/Response.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Response.pm (original)
+++ dbi/trunk/lib/DBI/Gofer/Response.pm Tue May  1 04:03:38 2007
@@ -33,7 +33,7 @@
     sth_resultsets
     warnings
 ));
-__PACKAGE__->mk_accessors_with(make_accessor_autoviv_hashref => qw(
+__PACKAGE__->mk_accessors_using(make_accessor_autoviv_hashref => qw(
     meta
 ));
 

Modified: dbi/trunk/lib/DBI/PurePerl.pm
==============================================================================
--- dbi/trunk/lib/DBI/PurePerl.pm       (original)
+++ dbi/trunk/lib/DBI/PurePerl.pm       Tue May  1 04:03:38 2007
@@ -177,6 +177,7 @@
        ParamValues
        Profile
        Provider
+        ReadOnly
        RootClass
        RowCacheSize
        RowsInCache
@@ -483,7 +484,7 @@
     if ($parent) {
        foreach (qw(
            RaiseError PrintError PrintWarn HandleError HandleSetErr
-           Warn LongTruncOk ChopBlanks AutoCommit
+           Warn LongTruncOk ChopBlanks AutoCommit ReadOnly
            ShowErrorStatement FetchHashKeyName LongReadLen CompatMode
        )) {
            $h_inner->{$_} = $parent->{$_}

Modified: dbi/trunk/lib/DBI/Util/_accessor.pm
==============================================================================
--- dbi/trunk/lib/DBI/Util/_accessor.pm (original)
+++ dbi/trunk/lib/DBI/Util/_accessor.pm Tue May  1 04:03:38 2007
@@ -3,7 +3,7 @@
 use Carp;
 our $VERSION = sprintf("0.%06d", q$Revision$ =~ /(\d+)/);
 
-# based (ever more loosly) on Class::Accessor::Fast
+# inspired by Class::Accessor::Fast
 
 sub new {
     my($proto, $fields) = @_;
@@ -19,10 +19,10 @@
 
 sub mk_accessors {
     my($self, @fields) = @_;
-    $self->mk_accessors_with('make_accessor', @fields);
+    $self->mk_accessors_using('make_accessor', @fields);
 }
 
-sub mk_accessors_with {
+sub mk_accessors_using {
     my($self, $maker, @fields) = @_;
     my $class = ref $self || $self;
 

Modified: dbi/trunk/t/06attrs.t
==============================================================================
--- dbi/trunk/t/06attrs.t       (original)
+++ dbi/trunk/t/06attrs.t       Tue May  1 04:03:38 2007
@@ -2,7 +2,7 @@
 
 use strict;
 
-use Test::More tests => 137;
+use Test::More tests => 142;
 
 ## ----------------------------------------------------------------------------
 ## 06attrs.t - ...
@@ -18,13 +18,12 @@
 $|=1;
 
 my $using_autoproxy = ($ENV{DBI_AUTOPROXY});
+my $dsn = 'dbi:ExampleP:dummy';
 
 # Connect to the example driver.
-my $dbh = DBI->connect('dbi:ExampleP:dummy', '', '',
-                           { 
-                                                        PrintError => 0,
-                             RaiseError => 1,
-                           });
+my $dbh = DBI->connect($dsn, '', '', { 
+    PrintError => 0, RaiseError => 1,
+});
 
 isa_ok( $dbh, 'DBI::db' );
 
@@ -66,6 +65,7 @@
 ok(!defined $dbh->{Profile},      '... checking Profile attribute for dbh');
 ok(!defined $dbh->{Statement},    '... checking Statement attribute for dbh');
 ok(!defined $dbh->{RowCacheSize}, '... checking RowCacheSize attribute for 
dbh');
+ok(!defined $dbh->{ReadOnly},     '... checking ReadOnly attribute for dbh');
 
 is($dbh->{FetchHashKeyName}, 'NAME',  '... checking FetchHashKeyName attribute 
for dbh');
 is($dbh->{Name},             'dummy', '... checking Name attribute for dbh')   
# fails for Multiplex
@@ -78,7 +78,7 @@
 eval { 
     $dbh->do('select foo from foo') 
 };
-like($@, qr/^DBD::(ExampleP|Multiplex|Gofer)::db do failed: Unknown field 
names: foo/ , '... catching exception');
+like($@, qr/^DBD::\w+::db do failed: Unknown field names: foo/ , '... catching 
exception');
 
 ok(defined $dbh->err, '... $dbh->err is undefined');
 like($dbh->errstr,  qr/^Unknown field names: foo\b/, '... checking 
$dbh->errstr');
@@ -131,6 +131,7 @@
 is($drh->{CachedKids}, undef,    '... checking CachedKids attribute for drh');
 ok(!defined $drh->{HandleError}, '... checking HandleError attribute for drh');
 ok(!defined $drh->{Profile},     '... checking Profile attribute for drh');
+ok(!defined $drh->{ReadOnly},    '... checking ReadOnly attribute for drh');
 
 cmp_ok($drh->{TraceLevel},  '==', $DBI::dbi_debug & 0xF, '... checking 
TraceLevel attribute for drh');
 cmp_ok($drh->{LongReadLen}, '==', 80,                    '... checking 
LongReadLen attribute for drh');
@@ -155,7 +156,7 @@
     $sth->execute("foo") 
 };
 # we don't check actual opendir error msg because of locale differences
-like($@, qr/^DBD::(ExampleP|Multiplex|Gofer)::st execute failed: 
.*opendir\(foo\): /msi, '... checking exception');
+like($@, qr/^DBD::\w+::st execute failed: .*opendir\(foo\): /msi, '... 
checking exception');
 
 # Test all of the statement handle attributes.
 like($sth->errstr, qr/opendir\(foo\): /, '... checking $sth->errstr');
@@ -199,6 +200,7 @@
 ok(!defined $sth->{CachedKids},  '... checking CachedKids attribute for sth');
 ok(!defined $sth->{HandleError}, '... checking HandleError attribute for sth');
 ok(!defined $sth->{Profile},     '... checking Profile attribute for sth');
+ok(!defined $sth->{ReadOnly},    '... checking ReadOnly attribute for sth');
 
 cmp_ok($sth->{TraceLevel},  '==', $DBI::dbi_debug & 0xF, '... checking 
TraceLevel attribute for sth');
 cmp_ok($sth->{LongReadLen}, '==', 80,                    '... checking 
LongReadLen attribute for sth');
@@ -274,5 +276,26 @@
 
 # $h->{TraceLevel} tests are in t/09trace.t
 
+print "Checking inheritance\n";
+
+SKIP: {
+    skip "drh->dbh->sth inheritance test skipped with DBI_AUTOPROXY", 2 if 
$ENV{DBI_AUTOPROXY};
+
+sub check_inherited {
+    my ($drh, $attr, $value, $skip_sth) = @_;
+    local $drh->{$attr} = $value;
+    local $drh->{PrintError} = 1;
+    my $dbh = $drh->connect("dummy");
+    is $dbh->{$attr}, $drh->{$attr}, "dbh $attr value should be inherited from 
drh";
+    unless ($skip_sth) {
+        my $sth = $dbh->prepare("select name from .");
+        is $sth->{$attr}, $dbh->{$attr}, "sth $attr value should be inherited 
from dbh";
+    }
+}
+
+check_inherited($drh, "ReadOnly", 1, 0);
+
+}
+
 1;
 # end

Modified: dbi/trunk/t/09trace.t
==============================================================================
--- dbi/trunk/t/09trace.t       (original)
+++ dbi/trunk/t/09trace.t       Tue May  1 04:03:38 2007
@@ -3,8 +3,7 @@
 
 use strict;
 
-# 66 tests originally
-use Test::More tests => 66;
+use Test::More tests => 67;
 
 ## ----------------------------------------------------------------------------
 ## 09trace.t
@@ -19,15 +18,33 @@
 
 $|=1;
 
-## ----------------------------------------------------------------------------
-# Connect to the example driver.
 
-my $dbh = DBI->connect('dbi:ExampleP:dummy', '', '',
-                           { PrintError => 0,
-                             RaiseError => 1,
-                             PrintWarn => 1,
-                           });
-isa_ok( $dbh, 'DBI::db' );
+my $trace_file = "dbitrace.log";
+
+if (-e $trace_file) {
+    1 while unlink $trace_file;
+    die "Can't unlink existing $trace_file: $!" if -e $trace_file;
+}
+
+my $orig_trace_level = DBI->trace;
+DBI->trace(3, $trace_file);             # enable trace before first driver load
+
+my $dbh = DBI->connect('dbi:ExampleP(AutoCommit=>1):', undef, undef);
+die "Unable to connect to ExampleP driver: $DBI::errstr" unless $dbh;
+
+isa_ok($dbh, 'DBI::db');
+
+$dbh->dump_handle("dump_handle test, write to log file", 2);
+
+DBI->trace(0, undef);   # turn off and restore to STDERR
+
+SKIP: {
+        skip "cygwin has buffer flushing bug", 1 if ($^O =~ /cygwin/i);
+        ok( -s $trace_file, "trace file size = " . -s $trace_file);
+}
+
+DBI->trace($orig_trace_level);  # no way to restore previous outfile XXX
+
 
 # Clean up when we're done.
 END { $dbh->disconnect if $dbh };
@@ -37,8 +54,6 @@
 
 cmp_ok($dbh->{TraceLevel}, '==', $DBI::dbi_debug & 0xF, '... checking 
TraceLevel attribute');
 
-my $trace_file = "dbitrace.log";
-
 1 while unlink $trace_file;
 
 $dbh->trace(0, $trace_file);
@@ -108,6 +123,8 @@
     is $warn, 2;
 }
 
+$dbh->dump_handle("dump_handle test, write to log file", 2);
+
 $dbh->trace(0);
 ok !$dbh->{TraceLevel};
 $dbh->trace(undef, "STDERR");  # close $trace_file

Modified: dbi/trunk/t/10examp.t
==============================================================================
--- dbi/trunk/t/10examp.t       (original)
+++ dbi/trunk/t/10examp.t       Tue May  1 04:03:38 2007
@@ -12,59 +12,18 @@
 my $haveFileSpec = eval { require File::Spec };
 require VMS::Filespec if $^O eq 'VMS';
 
-use Test::More tests => 209;
+use Test::More tests => 205;
 
 # "globals"
 my ($r, $dbh);
 
-## testing tracing to file
-sub trace_to_file {
-
-       my $trace_file = "dbitrace.log";
-
-        if (-e $trace_file) {
-            1 while unlink $trace_file;
-            die "Can't unlink existing $trace_file: $!" if -e $trace_file;
-        }
-
-       my $orig_trace_level = DBI->trace;
-       DBI->trace(3, $trace_file);             # enable trace before first 
driver load
-       
-       $dbh = DBI->connect('dbi:ExampleP(AutoCommit=>1):', undef, undef);
-       die "Unable to connect to ExampleP driver: $DBI::errstr" unless $dbh;
-
-       isa_ok($dbh, 'DBI::db');
-
-       $dbh->dump_handle("dump_handle test, write to log file", 2);
-
-       DBI->trace(0, undef);   # turn off and restore to STDERR
-       
-       SKIP: {
-               skip "cygwin has buffer flushing bug", 1 if ($^O =~ /cygwin/i);
-               ok( -s $trace_file, "trace file size = " . -s $trace_file);
-       }
-
-       my $unlinked = unlink( $trace_file );
-       ok( $unlinked, "Remove trace file $trace_file ($!)" );
-       ok( !-e $trace_file, "Trace file actually gone" );
-
-       DBI->trace($orig_trace_level);  # no way to restore previous outfile XXX
-}
-
-trace_to_file();
-
-# internal hack to assist debugging using DBI_TRACE env var. See DBI.pm.
-DBI->trace(@DBI::dbi_debug) if @DBI::dbi_debug;
-
-my $dbh2;
-eval {
-    $dbh2 = DBI->connect("dbi:NoneSuch:foobar", 1, 1, { RaiseError => 1, 
AutoCommit => 1 });
-};
+ok !eval {
+    $dbh = DBI->connect("dbi:NoneSuch:foobar", 1, 1, { RaiseError => 1, 
AutoCommit => 1 });
+}, 'connect should fail';
 like($@, qr/install_driver\(NoneSuch\) failed/, '... we should have an 
exception here');
-ok(!$dbh2, '... $dbh2 should not be defined');
+ok(!$dbh, '... $dbh2 should not be defined');
 
-$dbh2 = DBI->connect('dbi:ExampleP:', '', '');
-ok($dbh ne $dbh2);
+$dbh = DBI->connect('dbi:ExampleP:', '', '');
 
 sub check_connect_cached {
        # connect_cached
@@ -181,7 +140,6 @@
 my($col0, $col1, $col2, $col3, $rows);
 my(@row_a, @row_b);
 
-#$csr_a->trace(5);
 ok($csr_a->bind_columns(undef, \($col0, $col1, $col2)) );
 ok($csr_a->execute( $dir ), $DBI::errstr);
 
@@ -192,7 +150,6 @@
 is($row_a[0], $col0);
 is($row_a[1], $col1);
 is($row_a[2], $col2);
-#$csr_a->trace(0);
 
 ok( ! $csr_a->bind_columns(undef, \($col0, $col1)) );
 like $csr_a->errstr, '/bind_columns called with 2 values but 3 are needed/', 
'errstr should contain error message';
@@ -259,20 +216,17 @@
 
 print "fetchall_arrayref hash slice\n";
 ok($csr_b->execute());
-#$csr_b->trace(9);
 $r = $csr_b->fetchall_arrayref({ SizE=>1, nAMe=>1});
 ok($r && @$r);
 ok($r->[0]->{SizE} == $row_a[1]);
 ok($r->[0]->{nAMe} eq $row_a[2]);
 
-#$csr_b->trace(4);
 print "fetchall_arrayref hash\n";
 ok($csr_b->execute());
 $r = $csr_b->fetchall_arrayref({});
 ok($r);
 ok(keys %{$r->[0]} == 3);
 ok("@{$r->[0]}{qw(MODE SIZE NAME)}" eq "@row_a", "'@{$r->[0]}{qw(MODE SIZE 
NAME)}' ne '@row_a'");
-#$csr_b->trace(0);
 
 # use Data::Dumper; warn Dumper([EMAIL PROTECTED], $r]);
 
@@ -280,7 +234,6 @@
 ok($rows > 0, "row count $rows");
 ok($rows == @$r, "$rows vs "[EMAIL PROTECTED]);
 ok($rows == $DBI::rows, "$rows vs $DBI::rows");
-#$csr_b->trace(0);
 
 # ---
 
@@ -447,8 +400,6 @@
 ok(!$@, $@);
 ok(!defined($r), $r);
 
-#$dbh->trace(4);
-
 print "HandleError -> 2 -> return (modified)42\n";
 $HandleErrorReturn = 2;
 $r = eval { $csr_c = $dbh->prepare($error_sql); };
@@ -458,8 +409,6 @@
 $dbh->{HandleError} = undef;
 ok(!$dbh->{HandleError});
 
-#$dbh->trace(0); die;
-
 {
        # dump_results;
        my $sth = $dbh->prepare($std_sql);
@@ -505,7 +454,6 @@
 }
 print "Local $dir subdirs: @{[ keys %dirs ]}\n";
 closedir(DIR);
-#$dbh->trace(9);
 my $sth = $dbh->table_info($dir, undef, "%", "TABLE");
 ok($sth);
 %unexpected = %dirs;
@@ -522,8 +470,6 @@
 ok(keys %missing == 0)
     or print "Missing directories: ", join(",", keys %missing), "\n";
 
-#$dbh->trace(0); die 1;
-
 print "tables\n";
 my @tables_expected = (
     q{"schema"."table"},

Modified: dbi/trunk/t/86gofer_fail.t
==============================================================================
--- dbi/trunk/t/86gofer_fail.t  (original)
+++ dbi/trunk/t/86gofer_fail.t  Tue May  1 04:03:38 2007
@@ -8,11 +8,12 @@
 use DBI;
 use Data::Dumper;
 use Test::More;
+sub between_ok;
 
 # here we test the DBI_GOFER_RANDOM_FAIL mechanism
 # and how gofer deals with failures
 
-plan skip_all => "DBI_GOFER_RANDOM_FAIL not supported with PurePerl" if 
$DBI::PurePerl;
+plan skip_all => "requires Callbacks which are not supported with PurePerl" if 
$DBI::PurePerl;
 
 if (my $ap = $ENV{DBI_AUTOPROXY}) { # limit the insanity
     plan skip_all => "Gofer DBI_AUTOPROXY" if $ap =~ /^dbi:Gofer/i;
@@ -61,8 +62,7 @@
 ok my $dbh_50r0 = dbi_connect("policy=rush;retry_limit=0");
 $fails = precentage_exceptions(200, sub { $dbh_50r0->do("set foo=1") });
 print "target approx 50% random failures, got $fails%\n";
-cmp_ok $fails, '>', 10, 'should fail about 50% of the time, but at least 10%';
-cmp_ok $fails, '<', 90, 'should fail about 50% of the time, but not more than 
90%';
+between_ok $fails, 10, 90, "should fail about 50% of the time, but at least 
between 10% and 90%";
 
 # --- 50% failure rate, with many retries (should yield low failure rate)
 
@@ -79,13 +79,46 @@
 $fails = precentage_exceptions(200, sub { $dbh_1r10->do("set foo=1") });
 cmp_ok $fails, '<', 1, 'should fail < 1%';
 
+# --- 50% failure rate, test is_idempotent
+
+$ENV{DBI_GOFER_RANDOM_FAIL} = "2,do";   # 50%
+
+# test go_retry_hook and that ReadOnly => 1 retries a non-idempotent statement
+ok my $dbh_50r1ro = dbi_connect("policy=rush;retry_limit=1", {
+    go_retry_hook => sub { return ($_[0]->is_idempotent) ? 1 : 0 },
+    ReadOnly => 1,
+} );
+between_ok precentage_exceptions(100, sub { $dbh_50r1ro->do("set foo=1") }),
+    15, 35, 'should fail ~25% (ie 50% with one retry)';
+between_ok $dbh_50r1ro->{go_transport}->meta->{request_retry_count},
+    35, 65, 'transport request_retry_count should be around 50';
+
+# test as above but with ReadOnly => 0
+ok my $dbh_50r1rw = dbi_connect("policy=rush;retry_limit=1", {
+    go_retry_hook => sub { return ($_[0]->is_idempotent) ? 1 : 0 },
+    ReadOnly => 0,
+} );
+between_ok precentage_exceptions(100, sub { $dbh_50r1rw->do("set foo=1") }),
+    35, 65, 'should fail ~50%, ie no retries';
+ok !$dbh_50r1rw->{go_transport}->meta->{request_retry_count},
+    'transport request_retry_count should be zero or undef';
+
+
+
 
 exit 0;
 
+sub between_ok {
+    my ($got, $min, $max, $label) = @_;
+    local $Test::Builder::Level = 2;
+    cmp_ok $got, '>=', $min, "$label (got $got)";
+    cmp_ok $got, '<=', $max, "$label (got $got)";
+}
+
 sub dbi_connect {
-    my ($gdsn) = @_;
+    my ($gdsn, $attr) = @_;
     return DBI->connect("dbi:Gofer:transport=null;$gdsn;dsn=dbi:ExampleP:", 0, 
0, {
-        RaiseError => 1, PrintError => 0,
+        RaiseError => 1, PrintError => 0, ($attr) ? %$attr : ()
     });
 }
 

Reply via email to