Author: timbo
Date: Fri Mar 23 06:48:54 2007
New Revision: 9306

Modified:
   dbi/trunk/Changes
   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/t/85gofer.t

Log:
Implement policy-controlled caching of (non sth) metadata methods (tables, 
parse_trace_flags, get_info etc)


Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes   (original)
+++ dbi/trunk/Changes   Fri Mar 23 06:48:54 2007
@@ -7,11 +7,10 @@
 =cut
 
 Extract http transport into new distribution.
-Ping via policy!
 Add attr-passthru to prepare()?
 Terminology for client and server ends
-I could make the short transport/policy name do a lookup in both 
DBD::Gofer::Transport and DBIx::Gofer::Transport.
 Document user/passwd issues at the various levels of the stack
+Policy's from pod
 Policy for dbh attr FETCH (ie example_driver_path)
     or piggyback on skip_connect_check
     could also remember which attr have been returned to us

Modified: dbi/trunk/lib/DBD/Gofer.pm
==============================================================================
--- dbi/trunk/lib/DBD/Gofer.pm  (original)
+++ dbi/trunk/lib/DBD/Gofer.pm  Fri Mar 23 06:48:54 2007
@@ -57,6 +57,7 @@
         unless ($methods_already_installed++) {
             DBD::Gofer::db->install_method('go_dbh_method', { O=> 0x0004 }); # 
IMA_KEEP_ERR
             DBD::Gofer::st->install_method('go_sth_method', { O=> 0x0004 }); # 
IMA_KEEP_ERR
+            DBD::Gofer::st->install_method('go_clone_sth',  { O=> 0x0004 }); # 
IMA_KEEP_ERR
         }
 
         my($class, $attr) = @_;
@@ -162,7 +163,7 @@
             unless $transport_class =~ /::/;
         _load_class($transport_class)
             or return $drh->set_err(1, "Can't load $transport_class: $@");
-        my $go_trans = eval { $transport_class->new(\%go_attr) }
+        my $go_transport = eval { $transport_class->new(\%go_attr) }
             or return $drh->set_err(1, "Can't instanciate $transport_class: 
$@");
 
         my $request_class = "DBI::Gofer::Request";
@@ -185,7 +186,7 @@
         my ($dbh, $dbh_inner) = DBI::_new_dbh($drh, {
             'Name' => $dsn,
             'USER' => $user,
-            go_trans => $go_trans,
+            go_transport => $go_transport,
             go_request => $go_request,
             go_policy => $go_policy,
         });
@@ -247,7 +248,7 @@
         $request->dbh_last_insert_id_args($meta->{go_last_insert_id_args})
             if $meta->{go_last_insert_id_args};
 
-        my $transport = $dbh->{go_trans}
+        my $transport = $dbh->{go_transport}
             or return $dbh->set_err(1, "Not connected (no transport)");
 
         my $response = $transport->transmit_request($request);
@@ -299,11 +300,44 @@
         my $policy_name = "cache_$method";
         my $sub = sub {
             my $dbh = shift;
-            # XXX add local (in-handle) cache logic
-            my @rv = (wantarray)
+            my $rv;
+
+            my $cache;
+            my $cache_key;
+            if (my $cache_it = $dbh->{go_policy}->$policy_name(undef, $dbh, 
@_)) {
+                $cache = $dbh->{go_cache} ||= {};
+                $cache_key = sprintf "%s_wa%d(%s)", $policy_name, wantarray||0,
+                    join(",\t", map { # XXX basic but sufficient for now
+                         !ref($_)            ? DBI::neat($_,1e6)
+                        : ref($_) eq 'ARRAY' ? DBI::neat_list($_,1e6,",\001")
+                        : ref($_) eq 'HASH'  ? do { my @k = sort keys %$_; 
DBI::neat_list([EMAIL PROTECTED],@[EMAIL PROTECTED],1e6,",\002") }
+                        : do { warn "unhandled argument type ($_)"; $_ }
+                    } @_);
+                if ($rv = $cache->{$cache_key}) {
+                    $dbh->trace_msg("$method(@_) returning previously cached 
value ($cache_key)\n",4);
+                    my @cache_rv = @$rv;
+                    # if it's an sth we have to clone it
+                    $cache_rv[0] = $cache_rv[0]->go_clone_sth if 
UNIVERSAL::isa($cache_rv[0],'DBI::st');
+                    return (wantarray) ? @cache_rv : $cache_rv[0];
+                }
+            }
+
+            $rv = [ (wantarray)
                 ?       ($dbh->go_dbh_method(undef, $method, @_))
-                : scalar $dbh->go_dbh_method(undef, $method, @_);
-            return (wantarray) ? @rv : $rv[0];
+                : scalar $dbh->go_dbh_method(undef, $method, @_)
+            ];
+
+            if ($cache) {
+                $dbh->trace_msg("$method(@_) caching return value 
($cache_key)\n",4);
+                my @cache_rv = @$rv;
+                # if it's an sth we have to clone it
+                #$cache_rv[0] = $cache_rv[0]->go_clone_sth
+                #   if UNIVERSAL::isa($cache_rv[0],'DBI::st');
+                $cache->{$cache_key} = [EMAIL PROTECTED]
+                    unless UNIVERSAL::isa($cache_rv[0],'DBI::st'); # XXX 
cloning sth not yet done
+            }
+
+            return (wantarray) ? @$rv : $rv->[0];
         };
         no strict 'refs';
         *$method = $sub;
@@ -326,7 +360,7 @@
                 return $locally->($dbh, @_) if ref $locally eq 'CODE';
                 return $dbh->$super_name(@_);
             }
-            return $dbh->go_dbh_method(undef, $method, @_);
+            return $dbh->go_dbh_method(undef, $method, @_); # propagate context
         };
         no strict 'refs';
         *$method = $sub;
@@ -367,8 +401,9 @@
     sub FETCH {
         my ($dbh, $attrib) = @_;
 
-        # forward driver-private attributes
-        if ($attrib =~ m/^[a-z]/) { # XXX policy? precache on connect?
+        # forward driver-private attributes (except ours)
+        # XXX policy? precache on connect?
+        if ($attrib =~ m/^[a-z]/ && $attrib !~ /^go_/) {
             my $value = $dbh->go_dbh_method(undef, 'FETCH', $attrib);
             $dbh->{$attrib} = $value;
             return $value;
@@ -381,8 +416,8 @@
     sub STORE {
         my ($dbh, $attrib, $value) = @_;
         if ($attrib eq 'AutoCommit') {
-            return $dbh->SUPER::STORE($attrib => -901) if $value;
-            croak "Can't enable transactions when using DBD::Gofer";
+            croak "Can't enable transactions when using DBD::Gofer" if !$value;
+            return $dbh->SUPER::STORE($attrib => ($value) ? -901 : -900);
         }
         return $dbh->SUPER::STORE($attrib => $value)
             # we handle this attribute locally
@@ -409,7 +444,7 @@
 
     sub disconnect {
         my $dbh = shift;
-        $dbh->{go_trans} = undef;
+        $dbh->{go_transport} = undef;
         $dbh->STORE(Active => 0);
     }
 
@@ -429,7 +464,7 @@
             ],
             # go_method_calls => [], # autovivs if needed
             go_request => $dbh->{go_request},
-            go_trans => $dbh->{go_trans},
+            go_transport => $dbh->{go_transport},
             go_policy => $policy,
             go_last_insert_id_args => $attr->{go_last_insert_id_args},
         });
@@ -500,7 +535,7 @@
             if $dbh_attribute_update eq 'every'
             or $dbh_attribute_update eq 'first' && $dbh->{go_request_count}==1;
 
-        my $transport = $sth->{go_trans}
+        my $transport = $sth->{go_transport}
             or return $sth->set_err(1, "Not connected (no transport)");
 
         my $response = $transport->transmit_request($request);
@@ -589,6 +624,19 @@
     }
 
 
+    sub go_clone_sth {
+        my ($sth1) = @_;
+        # clone an (un-fetched-from) sth - effectively undoes the initial 
more_results
+        # not 100% so just for use in caching returned sth e.g. table_info
+        my $sth2 = $sth1->{Database}->prepare($sth1->{Statement}, { 
go_skip_prepare_check => 1 });
+        $sth2->STORE($_, $sth1->{$_}) for qw(NUM_OF_FIELDS Active);
+        my $sth2_inner = tied %$sth2;
+        $sth2_inner->{$_} = $sth1->{$_} for qw(NUM_OF_PARAMS FetchHashKeyName);
+        die "not fully implemented yet";
+        return $sth2;
+    }
+
+
     sub fetchrow_arrayref {
         my ($sth) = @_;
         my $resultset = $sth->{go_current_rowset} || do {

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      Fri Mar 23 06:48:54 2007
@@ -22,13 +22,25 @@
     dbh_attribute_list => ['*'],
     locally_quote => 0,
     locally_quote_identifier => 0,
+    cache_parse_trace_flags => 1,
+    cache_parse_trace_flag => 1,
+    cache_data_sources => 1,
+    cache_type_info_all => 1,
+    cache_tables => 0,
+    cache_table_info => 0,
+    cache_column_info => 0,
+    cache_primary_key_info => 0,
+    cache_foreign_key_info => 0,
+    cache_statistics_info => 0,
+    cache_get_info => 0,
+    cache_func => 0,
 );
 
 my $base_policy_file = $INC{"DBD/Gofer/Policy/Base.pm"};
 
-__PACKAGE__->create_default_policy_subs(\%policy_defaults);
+__PACKAGE__->create_policy_subs(\%policy_defaults);
 
-sub create_default_policy_subs {
+sub create_policy_subs {
     my ($class, $policy_defaults) = @_;
 
     while ( my ($policy_name, $policy_default) = each %$policy_defaults) { 

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   Fri Mar 23 06:48:54 2007
@@ -14,7 +14,7 @@
 
 use base qw(DBD::Gofer::Policy::Base);
 
-__PACKAGE__->create_default_policy_subs({
+__PACKAGE__->create_policy_subs({
 
     # don't skip the connect check since that also sets dbh attributes
     # although this makes connect more expensive, that's partly offset
@@ -24,12 +24,13 @@
     # most code doesn't rely on sth attributes being set after prepare
     skip_prepare_check => 1,
 
-    # ping is almost meaningless for DBD::Gofer and most transports anyway
+    # ping is not important for DBD::Gofer and most transports
     skip_ping => 1,
 
     # we'd like to set locally_* but can't because drivers differ
 
-    # XXX we could set some cache_* though
+    # get_info results usually don't change
+    cache_get_info => 1,
 });
 
 

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      Fri Mar 23 06:48:54 2007
@@ -14,7 +14,7 @@
 
 use base qw(DBD::Gofer::Policy::Base);
 
-__PACKAGE__->create_default_policy_subs({
+__PACKAGE__->create_policy_subs({
 
     # Skipping the connect check is fast, but it also skips
     # fetching the remote dbh attributes!
@@ -31,6 +31,16 @@
     dbh_attribute_update => 'none',
     dbh_attribute_list => undef,
 
+    # we'd like to set locally_* but can't because drivers differ
+
+    # in a rush assume metadata doesn't change
+    cache_tables => 1,
+    cache_table_info => 1,
+    cache_column_info => 1,
+    cache_primary_key_info => 1,
+    cache_foreign_key_info => 1,
+    cache_statistics_info => 1,
+    cache_get_info => 1,
 });
 
 

Modified: dbi/trunk/t/85gofer.t
==============================================================================
--- dbi/trunk/t/85gofer.t       (original)
+++ dbi/trunk/t/85gofer.t       Fri Mar 23 06:48:54 2007
@@ -165,7 +165,31 @@
         $durations{insert}{"$transport+$policy_name"} = time() - $start;
     }
 
+    print "Testing go_request_count and caching of simple values\n";
+    my $go_request_count = $dbh->{go_request_count};
+    ok $go_request_count;
+
     ok $dbh->do("DROP TABLE fruit");
+    is $go_request_count + 1, $dbh->{go_request_count};
+
+    $dbh->data_sources({ foo_bar => $go_request_count });
+    is $go_request_count + 2, $dbh->{go_request_count};
+    $dbh->data_sources({ foo_bar => $go_request_count }); # should use cache
+    is $go_request_count + 2, $dbh->{go_request_count};
+    @_=$dbh->data_sources({ foo_bar => $go_request_count }); # no cached yet 
due to wantarray
+    is $go_request_count + 3, $dbh->{go_request_count};
+
+SKIP: {
+    skip "caching of metadata methods returning sth not yet implemented", 2;
+    print "Testing go_request_count and caching of sth\n";
+    $go_request_count = $dbh->{go_request_count};
+    my $sth_ti1 = $dbh->table_info("%", "%", "%", "TABLE", { foo_bar => 
$go_request_count });
+    is $go_request_count + 1, $dbh->{go_request_count};
+
+    my $sth_ti2 = $dbh->table_info("%", "%", "%", "TABLE", { foo_bar => 
$go_request_count }); # should use cache
+    is $go_request_count + 1, $dbh->{go_request_count};
+}
+
     ok $dbh->disconnect;
 }
 

Reply via email to