Author: timbo
Date: Wed Feb 28 06:36:38 2007
New Revision: 9178

Modified:
   dbi/trunk/Changes
   dbi/trunk/DBI.pm
   dbi/trunk/DBI.xs
   dbi/trunk/lib/DBD/Gofer.pm
   dbi/trunk/lib/DBI/Gofer/Execute.pm

Log:
Changed DBD::Gofer to work around a DBD::Sybase bind_param bug.
Changed _set_fbav to not croak when given a wrongly sized array,
it now warns and adjusts the row buffer to match.
Added support for install_methods to DBD::Gofer.


Modified: dbi/trunk/Changes
==============================================================================
--- dbi/trunk/Changes   (original)
+++ dbi/trunk/Changes   Wed Feb 28 06:36:38 2007
@@ -26,6 +26,12 @@
   Fixed accuracy of profiling when perl configured to use long doubles.
   Fixed compile error in DBD::Gofer::Transport::http.
 
+  Changed DBD::Gofer to work around a DBD::Sybase bind_param bug.
+  Changed _set_fbav to not croak when given a wrongly sized array,
+    it now warns and adjusts the row buffer to match.
+
+  Added support for install_methods to DBD::Gofer.
+
 =head2 Changes in DBI 1.54 (svn rev 9157),  23rd February 2007
 
   NOTE: This release includes the 'next big thing': DBD::Gofer.

Modified: dbi/trunk/DBI.pm
==============================================================================
--- dbi/trunk/DBI.pm    (original)
+++ dbi/trunk/DBI.pm    Wed Feb 28 06:36:38 2007
@@ -288,6 +288,8 @@
 
 %DBI::installed_drh = ();  # maps driver names to installed driver handles
 sub installed_drivers { %DBI::installed_drh }
+%DBI::installed_methods = (); # XXX undocumented, may change
+sub installed_methods { %DBI::installed_methods }
 
 # Setup special DBI dynamic variables. See DBI::var::FETCH for details.
 # These are dynamically associated with the last handle used.
@@ -1357,9 +1359,13 @@
        my $prefix = $1;
        my $reg_info = $dbd_prefix_registry->{$prefix};
        Carp::carp("method name prefix '$prefix' is not associated with a 
registered driver") unless $reg_info;
-       my %attr = %{$attr||{}}; # copy so we can edit
+
+       my $full_method = "DBI::${subtype}::$method";
+       $DBI::installed_methods{$full_method} = $attr;
+
+       my (undef, $filename, $line) = caller;
        # XXX reformat $attr as needed for _install_method
-       my ($caller_pkg, $filename, $line) = caller;
+       my %attr = %{$attr||{}}; # copy so we can edit
        DBI->_install_method("DBI::${subtype}::$method", "$filename at line 
$line", \%attr);
     }
 

Modified: dbi/trunk/DBI.xs
==============================================================================
--- dbi/trunk/DBI.xs    (original)
+++ dbi/trunk/DBI.xs    Wed Feb 28 06:36:38 2007
@@ -4355,16 +4355,33 @@
     int i;
     AV *src_av;
     AV *dst_av = dbih_get_fbav(imp_sth);
-    const int num_fields = AvFILL(dst_av)+1;
+    int dst_fields = AvFILL(dst_av)+1;
+    int src_fields;
     (void)cv;
 
     if (!SvROK(src_rv) || SvTYPE(SvRV(src_rv)) != SVt_PVAV)
        croak("_set_fbav(%s): not an array ref", neatsvpv(src_rv,0));
     src_av = (AV*)SvRV(src_rv);
-    if (AvFILL(src_av)+1 != num_fields)
-       croak("_set_fbav(%s): array has %d elements, the statement handle 
expects %d",
-               neatsvpv(src_rv,0), (int)AvFILL(src_av)+1, num_fields);
-    for(i=0; i < num_fields; ++i) {    /* copy over the row    */
+    src_fields = AvFILL(src_av)+1;
+    if (src_fields != dst_fields) {
+       warn("_set_fbav(%s): array has %d elements, the statement handle row 
buffer has %d (and NUM_OF_FIELDS is %d)",
+               neatsvpv(src_rv,0), src_fields, dst_fields, 
DBIc_NUM_FIELDS(imp_sth));
+        SvREADONLY_off(dst_av);
+        if (src_fields < dst_fields) {
+            /* shrink the array - sadly this looses column bindings for the 
lost columns */
+            av_fill(dst_av, src_fields-1);
+            dst_fields = src_fields;
+        }
+        else {
+            av_fill(dst_av, src_fields-1);
+            /* av_fill pads with immutable undefs which we need to change */
+            for(i=dst_fields-1; i < src_fields; ++i) {
+                sv_setsv(AvARRAY(dst_av)[i], newSV(0));
+            }
+        }
+        SvREADONLY_on(dst_av);
+    }
+    for(i=0; i < dst_fields; ++i) {    /* copy over the row    */
         /* If we're given the values, then taint them if required */
         if (DBIc_is(imp_sth, DBIcf_TaintOut))
             SvTAINT(AvARRAY(src_av)[i]);

Modified: dbi/trunk/lib/DBD/Gofer.pm
==============================================================================
--- dbi/trunk/lib/DBD/Gofer.pm  (original)
+++ dbi/trunk/lib/DBD/Gofer.pm  Wed Feb 28 06:36:38 2007
@@ -75,14 +75,41 @@
     }
 
 
-    sub set_err_from_response {
+    sub set_err_from_response { # set error/warn/info and propagate warnings
         my ($h, $response) = @_;
-        # set error/warn/info
-        my $warnings = $response->warnings || [];
-        warn $_ for @$warnings;
+        if (my $warnings = $response->warnings) {
+            warn $_ for @$warnings;
+        }
         return $h->set_err($response->err, $response->errstr, 
$response->state);
     }
 
+
+    sub install_methods_proxy {
+        my ($installed_methods) = @_;
+        while ( my ($full_method, $attr) = each %$installed_methods ) {
+            # need to install both a DBI dispatch stub and a proxy stub
+            # (the dispatch stub may be already here due to local driver use)
+
+            DBI->_install_method($full_method, "", $attr||{})
+                unless defined &{$full_method};
+
+            # now install proxy stubs on the driver side
+            $full_method =~ m/^DBI::(\w\w)::(\w+)$/
+                or die "Invalid method name '$full_method' for install_method";
+            my ($type, $method) = ($1, $2);
+            my $driver_method = "DBD::Gofer::${type}::${method}";
+            next if defined &{$driver_method};
+            my $sub;
+            if ($type eq 'db') {
+                $sub = sub { return shift->go_dbh_method(undef, $method, @_) };
+            }
+            else {
+                $sub = sub { return shift->set_err(1, "Can't call 
\$${type}h->$method when using DBD::Gofer") };
+            }
+            no strict 'refs';
+            *$driver_method = $sub;
+        }
+    }
 }
 
 
@@ -225,6 +252,13 @@
         $dbh->{go_response} = $response;
 
         if (my $dbh_attributes = $response->dbh_attributes) {
+
+            # XXX installed_methods piggbacks on dbh_attributes for now
+            if (my $installed_methods = delete 
$dbh_attributes->{dbi_installed_methods}) {
+                DBD::Gofer::install_methods_proxy($installed_methods)
+                    if $dbh->{go_request_count}==1;
+            }
+
             # XXX we don't STORE here, we just stuff the value into the 
attribute cache
             $dbh->{$_} = $dbh_attributes->{$_}
                 for keys %$dbh_attributes;
@@ -342,8 +376,8 @@
             };
 
         # dbh attributes are set at connect-time - see connect()
-        carp("Can't alter \$dbh->{$attrib}") if $dbh->FETCH('Warn');
-        return $dbh->set_err(1, "Can't alter \$dbh->{$attrib}");
+        carp("Can't alter \$dbh->{$attrib} after handle created with 
DBD::Gofer") if $dbh->FETCH('Warn');
+        return $dbh->set_err(1, "Can't alter \$dbh->{$attrib} after handle 
created with DBD::Gofer");
     }
 
     sub disconnect {
@@ -395,10 +429,13 @@
 
         if (my $ParamValues = $sth->{ParamValues}) {
             my $ParamAttr = $sth->{ParamAttr};
-            while ( my ($p, $v) = each %$ParamValues) {
+            # XXX the sort here is a hack to work around a DBD::Sybase bug
+            # but only works properly for params 1..9
+            # (reverse because of the unshift)
+            for my $p (reverse sort keys %$ParamValues) {
                 # unshift to put binds before execute call
                 unshift @{ $sth->{go_method_calls} },
-                    [ 'bind_param', $p, $v, $ParamAttr->{$p} ];
+                    [ 'bind_param', $p, $ParamValues->{$p}, $ParamAttr->{$p} ];
             }
         }
 
@@ -681,6 +718,12 @@
 
 AutoCommit only. Transactions aren't supported.
 
+=head2 You can't use temporary tables or other per-connection persistent state
+
+Because the transport or server-side may execute your request via a different
+database connection, you can't rely on any per-connection persistent state,
+such as temporary tables, being available from one request to the next.
+
 =head2 You need to use func() to call driver-private dbh methods
 
 So instead of the new-style:
@@ -787,10 +830,15 @@
 =head3 pipeone
 
 The pipeone transport launches a subprocess for each request. It passes in the
-request and reads the response. The fact that a new subprocess is started for
-each request proves that the server side is truly stateless. It also makes
-this transport very slow. It's useful, however, both as a proof of concept and
-as a base class for the stream driver.
+request and reads the response.
+
+The fact that a new subprocess is started for each request ensures that the
+server side is truly stateless. While this does make the transport very slow it
+is useful as a way to test that your application doesn't depend on
+per-connection state, such as temporary tables, persisting between requests.
+
+It's also useful both as a proof of concept and as a base class for the stream
+driver.
 
 This transport supports a timeout parameter in the dsn which specifies
 the maximum time it can take to send a requestor receive a response.
@@ -916,4 +964,9 @@
 
 Neat way for $h->trace to enable transport tracing.
 
+Rework handling of installed_methods.
+
+Perhaps support transactions for transports where it's possible (ie null and 
stream)?
+Would make stream transport (ie ssh) more useful to more people.
+
 =cut

Modified: dbi/trunk/lib/DBI/Gofer/Execute.pm
==============================================================================
--- dbi/trunk/lib/DBI/Gofer/Execute.pm  (original)
+++ dbi/trunk/lib/DBI/Gofer/Execute.pm  Wed Feb 28 06:36:38 2007
@@ -84,8 +84,9 @@
         dbh => [qw(
             syb_dynamic_supported syb_oc_version syb_server_version 
syb_server_version_string
         )],
+        # we don't include syb_result_type as that's a *per row* attribute
         sth => [qw(
-            syb_types syb_result_type syb_proc_status
+            syb_types syb_proc_status
         )],
     },
     SQLite => {
@@ -252,6 +253,10 @@
         }
         my %dbh_attr_values;
         $dbh_attr_values{$_} = $dbh->FETCH($_) for @req_attr_names;
+
+        # XXX piggyback installed_methods onto dbh_attributes for now
+        $dbh_attr_values{dbi_installed_methods} = { DBI->installed_methods };
+        
         $response->dbh_attributes(\%dbh_attr_values);
     }
 
@@ -326,13 +331,18 @@
     };
     my $response = $self->new_response_with_err($rv, $@);
 
+    return $response if not $dbh;
+
     $response->last_insert_id( $last_insert_id )
         if defined $last_insert_id;
 
-    # XXX would be nice to be able to support streaming of results
     # even if the eval failed we still want to try to gather attribute values
-    $response->sth_resultsets( $self->gather_sth_resultsets($sth, $request) )
-        if $sth;
+    # (XXX would be nice to be able to support streaming of results.
+    # which would reduce memory usage and latency for large results)
+    if ($sth) {
+        $response->sth_resultsets( $self->gather_sth_resultsets($sth, 
$request) );
+        $sth->finish;
+    }
 
     if (my $dbh_attr = $extra_attr{$dbh->{Driver}{Name}}{dbh_after_sth}) {
         my %dbh_attr_values;
@@ -340,10 +350,7 @@
         $response->dbh_attributes(\%dbh_attr_values);
     }
 
-    # which would reduce memory usage and latency for large results
-
-    $self->reset_dbh($dbh)
-        if $dbh;
+    $self->reset_dbh($dbh);
 
     return $response;
 }

Reply via email to