| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | { | 
| 2 |  |  |  |  |  |  | package DBD::Gofer; | 
| 3 |  |  |  |  |  |  |  | 
| 4 | 56 |  |  | 56 |  | 366 | use strict; | 
|  | 56 |  |  |  |  | 111 |  | 
|  | 56 |  |  |  |  | 36879 |  | 
| 5 |  |  |  |  |  |  |  | 
| 6 |  |  |  |  |  |  | require DBI; | 
| 7 |  |  |  |  |  |  | require DBI::Gofer::Request; | 
| 8 |  |  |  |  |  |  | require DBI::Gofer::Response; | 
| 9 |  |  |  |  |  |  | require Carp; | 
| 10 |  |  |  |  |  |  |  | 
| 11 |  |  |  |  |  |  | our $VERSION = "0.015327"; | 
| 12 |  |  |  |  |  |  |  | 
| 13 |  |  |  |  |  |  | #   $Id: Gofer.pm 15326 2012-06-06 16:32:38Z Tim $ | 
| 14 |  |  |  |  |  |  | # | 
| 15 |  |  |  |  |  |  | #   Copyright (c) 2007, Tim Bunce, Ireland | 
| 16 |  |  |  |  |  |  | # | 
| 17 |  |  |  |  |  |  | #   You may distribute under the terms of either the GNU General Public | 
| 18 |  |  |  |  |  |  | #   License or the Artistic License, as specified in the Perl README file. | 
| 19 |  |  |  |  |  |  |  | 
| 20 |  |  |  |  |  |  |  | 
| 21 |  |  |  |  |  |  |  | 
| 22 |  |  |  |  |  |  | # attributes we'll allow local STORE | 
| 23 |  |  |  |  |  |  | our %xxh_local_store_attrib = map { $_=>1 } qw( | 
| 24 |  |  |  |  |  |  | Active | 
| 25 |  |  |  |  |  |  | CachedKids | 
| 26 |  |  |  |  |  |  | Callbacks | 
| 27 |  |  |  |  |  |  | DbTypeSubclass | 
| 28 |  |  |  |  |  |  | ErrCount Executed | 
| 29 |  |  |  |  |  |  | FetchHashKeyName | 
| 30 |  |  |  |  |  |  | HandleError HandleSetErr | 
| 31 |  |  |  |  |  |  | InactiveDestroy | 
| 32 |  |  |  |  |  |  | AutoInactiveDestroy | 
| 33 |  |  |  |  |  |  | PrintError PrintWarn | 
| 34 |  |  |  |  |  |  | Profile | 
| 35 |  |  |  |  |  |  | RaiseError | 
| 36 |  |  |  |  |  |  | RootClass | 
| 37 |  |  |  |  |  |  | ShowErrorStatement | 
| 38 |  |  |  |  |  |  | Taint TaintIn TaintOut | 
| 39 |  |  |  |  |  |  | TraceLevel | 
| 40 |  |  |  |  |  |  | Warn | 
| 41 |  |  |  |  |  |  | dbi_quote_identifier_cache | 
| 42 |  |  |  |  |  |  | dbi_connect_closure | 
| 43 |  |  |  |  |  |  | dbi_go_execute_unique | 
| 44 |  |  |  |  |  |  | ); | 
| 45 |  |  |  |  |  |  | our %xxh_local_store_attrib_if_same_value = map { $_=>1 } qw( | 
| 46 |  |  |  |  |  |  | Username | 
| 47 |  |  |  |  |  |  | dbi_connect_method | 
| 48 |  |  |  |  |  |  | ); | 
| 49 |  |  |  |  |  |  |  | 
| 50 |  |  |  |  |  |  | our $drh = undef;    # holds driver handle once initialized | 
| 51 |  |  |  |  |  |  | our $methods_already_installed; | 
| 52 |  |  |  |  |  |  |  | 
| 53 |  |  |  |  |  |  | sub driver{ | 
| 54 | 56 | 50 |  | 56 | 0 | 226 | return $drh if $drh; | 
| 55 |  |  |  |  |  |  |  | 
| 56 | 56 |  |  |  |  | 244 | DBI->setup_driver('DBD::Gofer'); | 
| 57 |  |  |  |  |  |  |  | 
| 58 | 56 | 50 |  |  |  | 237 | unless ($methods_already_installed++) { | 
| 59 | 56 |  |  |  |  | 200 | my $opts = { O=> 0x0004 }; # IMA_KEEP_ERR | 
| 60 | 56 |  |  |  |  | 639 | DBD::Gofer::db->install_method('go_dbh_method', $opts); | 
| 61 | 56 |  |  |  |  | 540 | DBD::Gofer::st->install_method('go_sth_method', $opts); | 
| 62 | 56 |  |  |  |  | 350 | DBD::Gofer::st->install_method('go_clone_sth',  $opts); | 
| 63 | 56 |  |  |  |  | 395 | DBD::Gofer::db->install_method('go_cache',      $opts); | 
| 64 | 56 |  |  |  |  | 297 | DBD::Gofer::st->install_method('go_cache',      $opts); | 
| 65 |  |  |  |  |  |  | } | 
| 66 |  |  |  |  |  |  |  | 
| 67 | 56 |  |  |  |  | 227 | my($class, $attr) = @_; | 
| 68 | 56 |  |  |  |  | 171 | $class .= "::dr"; | 
| 69 | 56 |  |  |  |  | 481 | ($drh) = DBI::_new_drh($class, { | 
| 70 |  |  |  |  |  |  | 'Name' => 'Gofer', | 
| 71 |  |  |  |  |  |  | 'Version' => $VERSION, | 
| 72 |  |  |  |  |  |  | 'Attribution' => 'DBD Gofer by Tim Bunce', | 
| 73 |  |  |  |  |  |  | }); | 
| 74 |  |  |  |  |  |  |  | 
| 75 | 56 |  |  |  |  | 289 | $drh; | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  |  | 
| 79 |  |  |  |  |  |  | sub CLONE { | 
| 80 | 0 |  |  | 0 |  | 0 | undef $drh; | 
| 81 |  |  |  |  |  |  | } | 
| 82 |  |  |  |  |  |  |  | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | sub go_cache { | 
| 85 | 4 |  |  | 4 | 0 | 87 | my $h = shift; | 
| 86 | 4 | 50 |  |  |  | 20 | $h->{go_cache} = shift if @_; | 
| 87 |  |  |  |  |  |  | # return handle's override go_cache, if it has one | 
| 88 | 4 | 100 |  |  |  | 27 | return $h->{go_cache} if defined $h->{go_cache}; | 
| 89 |  |  |  |  |  |  | # or else the transports default go_cache | 
| 90 | 2 |  |  |  |  | 12 | return $h->{go_transport}->{go_cache}; | 
| 91 |  |  |  |  |  |  | } | 
| 92 |  |  |  |  |  |  |  | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | sub set_err_from_response { # set error/warn/info and propagate warnings | 
| 95 | 7138 |  |  | 7138 | 0 | 11833 | my $h = shift; | 
| 96 | 7138 |  |  |  |  | 11175 | my $response = shift; | 
| 97 | 7138 | 100 |  |  |  | 20777 | if (my $warnings = $response->warnings) { | 
| 98 | 22 |  |  |  |  | 364 | warn $_ for @$warnings; | 
| 99 |  |  |  |  |  |  | } | 
| 100 | 7138 |  |  |  |  | 25679 | my ($err, $errstr, $state) = $response->err_errstr_state; | 
| 101 |  |  |  |  |  |  | # Only set_err() if there's an error else leave the current values | 
| 102 |  |  |  |  |  |  | # (The current values will normally be set undef by the DBI dispatcher | 
| 103 |  |  |  |  |  |  | # except for methods marked KEEPERR such as ping.) | 
| 104 | 7138 | 100 |  |  |  | 28606 | $h->set_err($err, $errstr, $state) if defined $err; | 
| 105 | 7138 |  |  |  |  | 13703 | return undef; | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | sub install_methods_proxy { | 
| 110 | 580 |  |  | 580 | 0 | 1740 | my ($installed_methods) = @_; | 
| 111 | 580 |  |  |  |  | 4299 | while ( my ($full_method, $attr) = each %$installed_methods ) { | 
| 112 |  |  |  |  |  |  | # need to install both a DBI dispatch stub and a proxy stub | 
| 113 |  |  |  |  |  |  | # (the dispatch stub may be already here due to local driver use) | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | DBI->_install_method($full_method, "", $attr||{}) | 
| 116 | 4866 | 50 | 0 |  |  | 7164 | unless defined &{$full_method}; | 
|  | 4866 |  |  |  |  | 18846 |  | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | # now install proxy stubs on the driver side | 
| 119 | 4866 | 50 |  |  |  | 19116 | $full_method =~ m/^DBI::(\w\w)::(\w+)$/ | 
| 120 |  |  |  |  |  |  | or die "Invalid method name '$full_method' for install_method"; | 
| 121 | 4866 |  |  |  |  | 15326 | my ($type, $method) = ($1, $2); | 
| 122 | 4866 |  |  |  |  | 9939 | my $driver_method = "DBD::Gofer::${type}::${method}"; | 
| 123 | 4866 | 100 |  |  |  | 6094 | next if defined &{$driver_method}; | 
|  | 4866 |  |  |  |  | 27809 |  | 
| 124 | 166 |  |  |  |  | 229 | my $sub; | 
| 125 | 166 | 100 |  |  |  | 323 | if ($type eq 'db') { | 
| 126 | 126 |  |  | 20 |  | 424 | $sub = sub { return shift->go_dbh_method(undef, $method, @_) }; | 
|  | 20 |  |  |  |  | 8920 |  | 
| 127 |  |  |  |  |  |  | } | 
| 128 |  |  |  |  |  |  | else { | 
| 129 | 40 |  |  | 0 |  | 176 | $sub = sub { shift->set_err($DBI::stderr, "Can't call \$${type}h->$method when using DBD::Gofer"); return; }; | 
|  | 0 |  |  |  |  | 0 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 130 |  |  |  |  |  |  | } | 
| 131 | 56 |  |  | 56 |  | 427 | no strict 'refs'; | 
|  | 56 |  |  |  |  | 113 |  | 
|  | 56 |  |  |  |  | 5646 |  | 
| 132 | 166 |  |  |  |  | 830 | *$driver_method = $sub; | 
| 133 |  |  |  |  |  |  | } | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  | } | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | {   package DBD::Gofer::dr; # ====== DRIVER ====== | 
| 139 |  |  |  |  |  |  |  | 
| 140 |  |  |  |  |  |  | $imp_data_size = 0; | 
| 141 | 56 |  |  | 56 |  | 429 | use strict; | 
|  | 56 |  |  |  |  | 112 |  | 
|  | 56 |  |  |  |  | 51222 |  | 
| 142 |  |  |  |  |  |  |  | 
| 143 |  |  |  |  |  |  | sub connect_cached { | 
| 144 | 10 |  |  | 10 |  | 149 | my ($drh, $dsn, $user, $auth, $attr)= @_; | 
| 145 | 10 |  | 50 |  |  | 24 | $attr ||= {}; | 
| 146 |  |  |  |  |  |  | return $drh->SUPER::connect_cached($dsn, $user, $auth, { | 
| 147 |  |  |  |  |  |  | (%$attr), | 
| 148 | 10 |  | 50 |  |  | 92 | go_connect_method => $attr->{go_connect_method} || 'connect_cached', | 
| 149 |  |  |  |  |  |  | }); | 
| 150 |  |  |  |  |  |  | } | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | sub connect { | 
| 154 | 716 |  |  | 716 |  | 15368 | my($drh, $dsn, $user, $auth, $attr)= @_; | 
| 155 | 716 |  |  |  |  | 1689 | my $orig_dsn = $dsn; | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | # first remove dsn= and everything after it | 
| 158 | 716 | 50 | 33 |  |  | 13482 | my $remote_dsn = ($dsn =~ s/;?\bdsn=(.*)$// && $1) | 
| 159 |  |  |  |  |  |  | or return $drh->set_err($DBI::stderr, "No dsn= argument in '$orig_dsn'"); | 
| 160 |  |  |  |  |  |  |  | 
| 161 | 716 | 100 |  |  |  | 2962 | if ($attr->{go_bypass}) { # don't use DBD::Gofer for this connection | 
| 162 |  |  |  |  |  |  | # useful for testing with DBI_AUTOPROXY, e.g., t/03handle.t | 
| 163 | 1 |  |  |  |  | 6 | return DBI->connect($remote_dsn, $user, $auth, $attr); | 
| 164 |  |  |  |  |  |  | } | 
| 165 |  |  |  |  |  |  |  | 
| 166 | 715 |  |  |  |  | 1614 | my %go_attr; | 
| 167 |  |  |  |  |  |  | # extract any go_ attributes from the connect() attr arg | 
| 168 | 715 |  |  |  |  | 3450 | for my $k (grep { /^go_/ } keys %$attr) { | 
|  | 4932 |  |  |  |  | 10991 |  | 
| 169 | 13 |  |  |  |  | 47 | $go_attr{$k} = delete $attr->{$k}; | 
| 170 |  |  |  |  |  |  | } | 
| 171 |  |  |  |  |  |  | # then override those with any attributes embedded in our dsn (not remote_dsn) | 
| 172 | 715 |  |  |  |  | 6110 | for my $kv (grep /=/, split /;/, $dsn, -1) { | 
| 173 | 2108 |  |  |  |  | 6834 | my ($k, $v) = split /=/, $kv, 2; | 
| 174 | 2108 |  |  |  |  | 7523 | $go_attr{ "go_$k" } = $v; | 
| 175 |  |  |  |  |  |  | } | 
| 176 |  |  |  |  |  |  |  | 
| 177 | 715 | 50 |  |  |  | 3179 | if (not ref $go_attr{go_policy}) { # if not a policy object already | 
| 178 | 715 |  | 50 |  |  | 2919 | my $policy_class = $go_attr{go_policy} || 'classic'; | 
| 179 | 715 | 50 |  |  |  | 3304 | $policy_class = "DBD::Gofer::Policy::$policy_class" | 
| 180 |  |  |  |  |  |  | unless $policy_class =~ /::/; | 
| 181 | 715 | 50 |  |  |  | 3045 | _load_class($policy_class) | 
| 182 |  |  |  |  |  |  | or return $drh->set_err($DBI::stderr, "Can't load $policy_class: $@"); | 
| 183 |  |  |  |  |  |  | # replace policy name in %go_attr with policy object | 
| 184 | 715 | 50 |  |  |  | 1732 | $go_attr{go_policy} = eval { $policy_class->new(\%go_attr) } | 
|  | 715 |  |  |  |  | 5621 |  | 
| 185 |  |  |  |  |  |  | or return $drh->set_err($DBI::stderr, "Can't instanciate $policy_class: $@"); | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  | # policy object is left in $go_attr{go_policy} so transport can see it | 
| 188 | 715 |  |  |  |  | 2034 | my $go_policy = $go_attr{go_policy}; | 
| 189 |  |  |  |  |  |  |  | 
| 190 | 715 | 100 | 100 |  |  | 2879 | if ($go_attr{go_cache} and not ref $go_attr{go_cache}) { # if not a cache object already | 
| 191 | 4 |  |  |  |  | 13 | my $cache_class = $go_attr{go_cache}; | 
| 192 | 4 | 50 |  |  |  | 24 | $cache_class = "DBI::Util::CacheMemory" if $cache_class eq '1'; | 
| 193 | 4 | 50 |  |  |  | 12 | _load_class($cache_class) | 
| 194 |  |  |  |  |  |  | or return $drh->set_err($DBI::stderr, "Can't load $cache_class $@"); | 
| 195 | 4 | 50 |  |  |  | 12 | $go_attr{go_cache} = eval { $cache_class->new() } | 
|  | 4 |  |  |  |  | 38 |  | 
| 196 |  |  |  |  |  |  | or $drh->set_err(0, "Can't instanciate $cache_class: $@"); # warning | 
| 197 |  |  |  |  |  |  | } | 
| 198 |  |  |  |  |  |  |  | 
| 199 |  |  |  |  |  |  | # delete any other attributes that don't apply to transport | 
| 200 | 715 |  |  |  |  | 1666 | my $go_connect_method = delete $go_attr{go_connect_method}; | 
| 201 |  |  |  |  |  |  |  | 
| 202 |  |  |  |  |  |  | my $transport_class = delete $go_attr{go_transport} | 
| 203 | 715 | 50 |  |  |  | 5284 | or return $drh->set_err($DBI::stderr, "No transport= argument in '$orig_dsn'"); | 
| 204 | 715 | 50 |  |  |  | 3604 | $transport_class = "DBD::Gofer::Transport::$transport_class" | 
| 205 |  |  |  |  |  |  | unless $transport_class =~ /::/; | 
| 206 | 715 | 50 |  |  |  | 2157 | _load_class($transport_class) | 
| 207 |  |  |  |  |  |  | or return $drh->set_err($DBI::stderr, "Can't load $transport_class: $@"); | 
| 208 | 715 | 50 |  |  |  | 1560 | my $go_transport = eval { $transport_class->new(\%go_attr) } | 
|  | 715 |  |  |  |  | 6159 |  | 
| 209 |  |  |  |  |  |  | or return $drh->set_err($DBI::stderr, "Can't instanciate $transport_class: $@"); | 
| 210 |  |  |  |  |  |  |  | 
| 211 | 715 |  |  |  |  | 1931 | my $request_class = "DBI::Gofer::Request"; | 
| 212 | 715 | 50 |  |  |  | 1302 | my $go_request = eval { | 
| 213 | 715 |  |  |  |  | 4436 | my $go_attr = { %$attr }; | 
| 214 |  |  |  |  |  |  | # XXX user/pass of fwd server vs db server ? also impact of autoproxy | 
| 215 | 715 | 100 |  |  |  | 2449 | if ($user) { | 
| 216 | 13 |  |  |  |  | 31 | $go_attr->{Username} = $user; | 
| 217 | 13 |  |  |  |  | 29 | $go_attr->{Password} = $auth; | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  | # delete any attributes we can't serialize (or don't want to) | 
| 220 | 715 |  |  |  |  | 1649 | delete @{$go_attr}{qw(Profile HandleError HandleSetErr Callbacks)}; | 
|  | 715 |  |  |  |  | 2126 |  | 
| 221 |  |  |  |  |  |  | # delete any attributes that should only apply to the client-side | 
| 222 | 715 |  |  |  |  | 1444 | delete @{$go_attr}{qw(RootClass DbTypeSubclass)}; | 
|  | 715 |  |  |  |  | 1561 |  | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 715 |  | 50 |  |  | 6325 | $go_connect_method ||= $go_policy->connect_method($remote_dsn, $go_attr) || 'connect'; | 
|  |  |  | 66 |  |  |  |  | 
| 225 | 715 |  |  |  |  | 6000 | $request_class->new({ | 
| 226 |  |  |  |  |  |  | dbh_connect_call => [ $go_connect_method, $remote_dsn, $user, $auth, $go_attr ], | 
| 227 |  |  |  |  |  |  | }) | 
| 228 |  |  |  |  |  |  | } or return $drh->set_err($DBI::stderr, "Can't instanciate $request_class: $@"); | 
| 229 |  |  |  |  |  |  |  | 
| 230 | 715 |  |  |  |  | 7474 | my ($dbh, $dbh_inner) = DBI::_new_dbh($drh, { | 
| 231 |  |  |  |  |  |  | 'Name' => $dsn, | 
| 232 |  |  |  |  |  |  | 'USER' => $user, | 
| 233 |  |  |  |  |  |  | go_transport => $go_transport, | 
| 234 |  |  |  |  |  |  | go_request => $go_request, | 
| 235 |  |  |  |  |  |  | go_policy => $go_policy, | 
| 236 |  |  |  |  |  |  | }); | 
| 237 |  |  |  |  |  |  |  | 
| 238 |  |  |  |  |  |  | # mark as inactive temporarily for STORE. Active not set until connected() called. | 
| 239 | 715 |  |  |  |  | 5669 | $dbh->STORE(Active => 0); | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | # should we ping to check the connection | 
| 242 |  |  |  |  |  |  | # and fetch dbh attributes | 
| 243 | 715 |  |  |  |  | 7144 | my $skip_connect_check = $go_policy->skip_connect_check($attr, $dbh); | 
| 244 | 715 | 100 |  |  |  | 2402 | if (not $skip_connect_check) { | 
| 245 | 530 | 100 |  |  |  | 3035 | if (not $dbh->go_dbh_method(undef, 'ping')) { | 
| 246 | 26 | 50 |  |  |  | 765 | return undef if $dbh->err; # error already recorded, typically | 
| 247 | 0 |  |  |  |  | 0 | return $dbh->set_err($DBI::stderr, "ping failed"); | 
| 248 |  |  |  |  |  |  | } | 
| 249 |  |  |  |  |  |  | } | 
| 250 |  |  |  |  |  |  |  | 
| 251 | 689 |  |  |  |  | 17206 | return $dbh; | 
| 252 |  |  |  |  |  |  | } | 
| 253 |  |  |  |  |  |  |  | 
| 254 |  |  |  |  |  |  | sub _load_class { # return true or false+$@ | 
| 255 | 1434 |  |  | 1434 |  | 2914 | my $class = shift; | 
| 256 | 1434 |  |  |  |  | 7454 | (my $pm = $class) =~ s{::}{/}g; | 
| 257 | 1434 |  |  |  |  | 3167 | $pm .= ".pm"; | 
| 258 | 1434 | 50 |  |  |  | 2664 | return 1 if eval { require $pm }; | 
|  | 1434 |  |  |  |  | 69386 |  | 
| 259 | 0 |  |  |  |  | 0 | delete $INC{$pm}; # shouldn't be needed (perl bug?) and assigning undef isn't enough | 
| 260 | 0 |  |  |  |  | 0 | undef; # error in $@ | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  |  | 
| 263 |  |  |  |  |  |  | } | 
| 264 |  |  |  |  |  |  |  | 
| 265 |  |  |  |  |  |  |  | 
| 266 |  |  |  |  |  |  | {   package DBD::Gofer::db; # ====== DATABASE ====== | 
| 267 |  |  |  |  |  |  | $imp_data_size = 0; | 
| 268 | 56 |  |  | 56 |  | 558 | use strict; | 
|  | 56 |  |  |  |  | 138 |  | 
|  | 56 |  |  |  |  | 1771 |  | 
| 269 | 56 |  |  | 56 |  | 331 | use Carp qw(carp croak); | 
|  | 56 |  |  |  |  | 132 |  | 
|  | 56 |  |  |  |  | 49471 |  | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | my %dbh_local_store_attrib = %DBD::Gofer::xxh_local_store_attrib; | 
| 272 |  |  |  |  |  |  |  | 
| 273 |  |  |  |  |  |  | sub connected { | 
| 274 | 694 |  |  | 694 |  | 8960 | shift->STORE(Active => 1); | 
| 275 |  |  |  |  |  |  | } | 
| 276 |  |  |  |  |  |  |  | 
| 277 |  |  |  |  |  |  | sub go_dbh_method { | 
| 278 | 4077 |  |  | 4077 |  | 18608 | my $dbh = shift; | 
| 279 | 4077 |  |  |  |  | 5312 | my $meta = shift; | 
| 280 |  |  |  |  |  |  | # @_ now contains ($method_name, @args) | 
| 281 |  |  |  |  |  |  |  | 
| 282 | 4077 |  |  |  |  | 6425 | my $request = $dbh->{go_request}; | 
| 283 | 4077 |  |  |  |  | 21231 | $request->init_request([ wantarray, @_ ], $dbh); | 
| 284 | 4077 |  |  |  |  | 7595 | ++$dbh->{go_request_count}; | 
| 285 |  |  |  |  |  |  |  | 
| 286 | 4077 |  |  |  |  | 6421 | my $go_policy = $dbh->{go_policy}; | 
| 287 | 4077 |  |  |  |  | 13732 | my $dbh_attribute_update = $go_policy->dbh_attribute_update(); | 
| 288 |  |  |  |  |  |  | $request->dbh_attributes( $go_policy->dbh_attribute_list() ) | 
| 289 |  |  |  |  |  |  | if $dbh_attribute_update eq 'every' | 
| 290 | 4077 | 100 | 100 |  |  | 20960 | or $dbh->{go_request_count}==1; | 
| 291 |  |  |  |  |  |  |  | 
| 292 |  |  |  |  |  |  | $request->dbh_last_insert_id_args($meta->{go_last_insert_id_args}) | 
| 293 | 4077 | 50 |  |  |  | 9382 | if $meta->{go_last_insert_id_args}; | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | my $transport = $dbh->{go_transport} | 
| 296 | 4077 | 50 |  |  |  | 9399 | or return $dbh->set_err($DBI::stderr, "Not connected (no transport)"); | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | local $transport->{go_cache} = $dbh->{go_cache} | 
| 299 | 4077 | 50 |  |  |  | 8282 | if defined $dbh->{go_cache}; | 
| 300 |  |  |  |  |  |  |  | 
| 301 | 4077 |  |  |  |  | 11520 | my ($response, $retransmit_sub) = $transport->transmit_request($request); | 
| 302 | 4077 |  | 33 |  |  | 22790 | $response ||= $transport->receive_response($request, $retransmit_sub); | 
| 303 | 4077 | 50 |  |  |  | 22975 | $dbh->{go_response} = $response | 
| 304 |  |  |  |  |  |  | or die "No response object returned by $transport"; | 
| 305 |  |  |  |  |  |  |  | 
| 306 | 4077 | 50 |  |  |  | 15069 | die "response '$response' returned by $transport is not a response object" | 
| 307 |  |  |  |  |  |  | unless UNIVERSAL::isa($response,"DBI::Gofer::Response"); | 
| 308 |  |  |  |  |  |  |  | 
| 309 | 4077 | 100 |  |  |  | 11173 | if (my $dbh_attributes = $response->dbh_attributes) { | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | # XXX installed_methods piggybacks on dbh_attributes for now | 
| 312 | 1067 | 50 |  |  |  | 3888 | if (my $installed_methods = delete $dbh_attributes->{dbi_installed_methods}) { | 
| 313 |  |  |  |  |  |  | DBD::Gofer::install_methods_proxy($installed_methods) | 
| 314 | 1067 | 100 |  |  |  | 6506 | if $dbh->{go_request_count}==1; | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | # XXX we don't STORE here, we just stuff the value into the attribute cache | 
| 318 |  |  |  |  |  |  | $dbh->{$_} = $dbh_attributes->{$_} | 
| 319 | 1067 |  |  |  |  | 15883 | for keys %$dbh_attributes; | 
| 320 |  |  |  |  |  |  | } | 
| 321 |  |  |  |  |  |  |  | 
| 322 | 4077 |  |  |  |  | 12623 | my $rv = $response->rv; | 
| 323 | 4077 | 100 |  |  |  | 10059 | if (my $resultset_list = $response->sth_resultsets) { | 
|  |  | 50 |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | # dbh method call returned one or more resultsets | 
| 325 |  |  |  |  |  |  | # (was probably a metadata method like table_info) | 
| 326 |  |  |  |  |  |  | # | 
| 327 |  |  |  |  |  |  | # setup an sth but don't execute/forward it | 
| 328 | 10 |  |  |  |  | 119 | my $sth = $dbh->prepare(undef, { go_skip_prepare_check => 1 }); | 
| 329 |  |  |  |  |  |  | # set the sth response to our dbh response | 
| 330 | 10 |  |  |  |  | 146 | (tied %$sth)->{go_response} = $response; | 
| 331 |  |  |  |  |  |  | # setup the sth with the results in our response | 
| 332 | 10 |  |  |  |  | 63 | $sth->more_results; | 
| 333 |  |  |  |  |  |  | # and return that new sth as if it came from original request | 
| 334 | 10 |  |  |  |  | 92 | $rv = [ $sth ]; | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  | elsif (!$rv) { # should only occur for major transport-level error | 
| 337 |  |  |  |  |  |  | #carp("no rv in response { @{[ %$response ]} }"); | 
| 338 | 0 |  |  |  |  | 0 | $rv = [ ]; | 
| 339 |  |  |  |  |  |  | } | 
| 340 |  |  |  |  |  |  |  | 
| 341 | 4077 |  |  |  |  | 12612 | DBD::Gofer::set_err_from_response($dbh, $response); | 
| 342 |  |  |  |  |  |  |  | 
| 343 | 4077 | 100 |  |  |  | 60891 | return (wantarray) ? @$rv : $rv->[0]; | 
| 344 |  |  |  |  |  |  | } | 
| 345 |  |  |  |  |  |  |  | 
| 346 |  |  |  |  |  |  |  | 
| 347 |  |  |  |  |  |  | # Methods that should be forwarded but can be cached | 
| 348 |  |  |  |  |  |  | for my $method (qw( | 
| 349 |  |  |  |  |  |  | tables table_info column_info primary_key_info foreign_key_info statistics_info | 
| 350 |  |  |  |  |  |  | data_sources type_info_all get_info | 
| 351 |  |  |  |  |  |  | parse_trace_flags parse_trace_flag | 
| 352 |  |  |  |  |  |  | func | 
| 353 |  |  |  |  |  |  | )) { | 
| 354 |  |  |  |  |  |  | my $policy_name = "cache_$method"; | 
| 355 |  |  |  |  |  |  | my $super_name  = "SUPER::$method"; | 
| 356 |  |  |  |  |  |  | my $sub = sub { | 
| 357 | 209 |  |  | 209 |  | 106232 | my $dbh = shift; | 
| 358 | 209 |  |  |  |  | 411 | my $rv; | 
| 359 |  |  |  |  |  |  |  | 
| 360 |  |  |  |  |  |  | # if we know the remote side doesn't override the DBI's default method | 
| 361 |  |  |  |  |  |  | # then we might as well just call the DBI's default method on the client | 
| 362 |  |  |  |  |  |  | # (which may, in turn, call other methods that are forwarded, like get_info) | 
| 363 | 209 | 50 | 66 |  |  | 1281 | if ($dbh->{dbi_default_methods}{$method} && $dbh->{go_policy}->skip_default_methods()) { | 
| 364 | 0 |  |  |  |  | 0 | $dbh->trace_msg("    !! $method: using local default as remote method is also default\n"); | 
| 365 | 0 |  |  |  |  | 0 | return $dbh->$super_name(@_); | 
| 366 |  |  |  |  |  |  | } | 
| 367 |  |  |  |  |  |  |  | 
| 368 | 209 |  |  |  |  | 484 | my $cache; | 
| 369 |  |  |  |  |  |  | my $cache_key; | 
| 370 | 209 | 100 |  |  |  | 1619 | if (my $cache_it = $dbh->{go_policy}->$policy_name(undef, $dbh, @_)) { | 
| 371 | 185 |  | 100 |  |  | 986 | $cache = $dbh->{go_meta_cache} ||= {}; # keep separate from go_cache | 
| 372 |  |  |  |  |  |  | $cache_key = sprintf "%s_wa%d(%s)", $policy_name, wantarray||0, | 
| 373 |  |  |  |  |  |  | join(",\t", map { # XXX basic but sufficient for now | 
| 374 | 185 |  | 100 |  |  | 1404 | !ref($_)            ? DBI::neat($_,1e6) | 
| 375 |  |  |  |  |  |  | : ref($_) eq 'ARRAY' ? DBI::neat_list($_,1e6,",\001") | 
| 376 | 108 |  |  |  |  | 584 | : ref($_) eq 'HASH'  ? do { my @k = sort keys %$_; DBI::neat_list([@k,@{$_}{@k}],1e6,",\002") } | 
|  | 108 |  |  |  |  | 259 |  | 
|  | 108 |  |  |  |  | 865 |  | 
| 377 | 182 | 50 |  |  |  | 1312 | : do { warn "unhandled argument type ($_)"; $_ } | 
|  | 0 | 50 |  |  |  | 0 |  | 
|  | 0 | 100 |  |  |  | 0 |  | 
| 378 |  |  |  |  |  |  | } @_); | 
| 379 | 185 | 100 |  |  |  | 783 | if ($rv = $cache->{$cache_key}) { | 
| 380 | 58 |  |  |  |  | 637 | $dbh->trace_msg("$method(@_) returning previously cached value ($cache_key)\n",4); | 
| 381 | 58 |  |  |  |  | 432 | my @cache_rv = @$rv; | 
| 382 |  |  |  |  |  |  | # if it's an sth we have to clone it | 
| 383 | 58 | 50 |  |  |  | 311 | $cache_rv[0] = $cache_rv[0]->go_clone_sth if UNIVERSAL::isa($cache_rv[0],'DBI::st'); | 
| 384 | 58 | 50 |  |  |  | 362 | return (wantarray) ? @cache_rv : $cache_rv[0]; | 
| 385 |  |  |  |  |  |  | } | 
| 386 |  |  |  |  |  |  | } | 
| 387 |  |  |  |  |  |  |  | 
| 388 |  |  |  |  |  |  | $rv = [ (wantarray) | 
| 389 | 151 | 100 |  |  |  | 1083 | ?       ($dbh->go_dbh_method(undef, $method, @_)) | 
| 390 |  |  |  |  |  |  | : scalar $dbh->go_dbh_method(undef, $method, @_) | 
| 391 |  |  |  |  |  |  | ]; | 
| 392 |  |  |  |  |  |  |  | 
| 393 | 151 | 100 |  |  |  | 2019 | if ($cache) { | 
| 394 | 127 |  |  |  |  | 2253 | $dbh->trace_msg("$method(@_) caching return value ($cache_key)\n",4); | 
| 395 | 127 |  |  |  |  | 1056 | my @cache_rv = @$rv; | 
| 396 |  |  |  |  |  |  | # if it's an sth we have to clone it | 
| 397 |  |  |  |  |  |  | #$cache_rv[0] = $cache_rv[0]->go_clone_sth | 
| 398 |  |  |  |  |  |  | #   if UNIVERSAL::isa($cache_rv[0],'DBI::st'); | 
| 399 | 127 | 50 |  |  |  | 1336 | $cache->{$cache_key} = \@cache_rv | 
| 400 |  |  |  |  |  |  | unless UNIVERSAL::isa($cache_rv[0],'DBI::st'); # XXX cloning sth not yet done | 
| 401 |  |  |  |  |  |  | } | 
| 402 |  |  |  |  |  |  |  | 
| 403 | 151 | 100 |  |  |  | 1611 | return (wantarray) ? @$rv : $rv->[0]; | 
| 404 |  |  |  |  |  |  | }; | 
| 405 | 56 |  |  | 56 |  | 431 | no strict 'refs'; | 
|  | 56 |  |  |  |  | 129 |  | 
|  | 56 |  |  |  |  | 11159 |  | 
| 406 |  |  |  |  |  |  | *$method = $sub; | 
| 407 |  |  |  |  |  |  | } | 
| 408 |  |  |  |  |  |  |  | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | # Methods that can use the DBI defaults for some situations/drivers | 
| 411 |  |  |  |  |  |  | for my $method (qw( | 
| 412 |  |  |  |  |  |  | quote quote_identifier | 
| 413 |  |  |  |  |  |  | )) {    # XXX keep DBD::Gofer::Policy::Base in sync | 
| 414 |  |  |  |  |  |  | my $policy_name = "locally_$method"; | 
| 415 |  |  |  |  |  |  | my $super_name  = "SUPER::$method"; | 
| 416 |  |  |  |  |  |  | my $sub = sub { | 
| 417 | 18 |  |  | 18 |  | 6927 | my $dbh = shift; | 
| 418 |  |  |  |  |  |  |  | 
| 419 |  |  |  |  |  |  | # if we know the remote side doesn't override the DBI's default method | 
| 420 |  |  |  |  |  |  | # then we might as well just call the DBI's default method on the client | 
| 421 |  |  |  |  |  |  | # (which may, in turn, call other methods that are forwarded, like get_info) | 
| 422 | 18 | 50 | 33 |  |  | 114 | if ($dbh->{dbi_default_methods}{$method} && $dbh->{go_policy}->skip_default_methods()) { | 
| 423 | 0 |  |  |  |  | 0 | $dbh->trace_msg("    !! $method: using local default as remote method is also default\n"); | 
| 424 | 0 |  |  |  |  | 0 | return $dbh->$super_name(@_); | 
| 425 |  |  |  |  |  |  | } | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  | # false:    use remote gofer | 
| 428 |  |  |  |  |  |  | # 1:        use local DBI default method | 
| 429 |  |  |  |  |  |  | # code ref: use the code ref | 
| 430 | 18 |  |  |  |  | 79 | my $locally = $dbh->{go_policy}->$policy_name($dbh, @_); | 
| 431 | 18 | 50 |  |  |  | 34 | if ($locally) { | 
| 432 | 0 | 0 |  |  |  | 0 | return $locally->($dbh, @_) if ref $locally eq 'CODE'; | 
| 433 | 0 |  |  |  |  | 0 | return $dbh->$super_name(@_); | 
| 434 |  |  |  |  |  |  | } | 
| 435 | 18 |  |  |  |  | 62 | return $dbh->go_dbh_method(undef, $method, @_); # propagate context | 
| 436 |  |  |  |  |  |  | }; | 
| 437 | 56 |  |  | 56 |  | 391 | no strict 'refs'; | 
|  | 56 |  |  |  |  | 112 |  | 
|  | 56 |  |  |  |  | 4205 |  | 
| 438 |  |  |  |  |  |  | *$method = $sub; | 
| 439 |  |  |  |  |  |  | } | 
| 440 |  |  |  |  |  |  |  | 
| 441 |  |  |  |  |  |  |  | 
| 442 |  |  |  |  |  |  | # Methods that should always fail | 
| 443 |  |  |  |  |  |  | for my $method (qw( | 
| 444 |  |  |  |  |  |  | begin_work commit rollback | 
| 445 |  |  |  |  |  |  | )) { | 
| 446 | 56 |  |  | 56 |  | 364 | no strict 'refs'; | 
|  | 56 |  |  |  |  | 123 |  | 
|  | 56 |  |  |  |  | 47221 |  | 
| 447 | 1 |  |  | 1 |  | 2972 | *$method = sub { return shift->set_err($DBI::stderr, "$method not available with DBD::Gofer") } | 
| 448 |  |  |  |  |  |  | } | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | sub do { | 
| 452 | 3324 |  |  | 3324 |  | 350625 | my ($dbh, $sql, $attr, @args) = @_; | 
| 453 | 3324 |  |  |  |  | 6610 | delete $dbh->{Statement}; # avoid "Modification of non-creatable hash value attempted" | 
| 454 | 3324 |  |  |  |  | 7207 | $dbh->{Statement} = $sql; # for profiling and ShowErrorStatement | 
| 455 | 3324 |  |  |  |  | 8772 | my $meta = { go_last_insert_id_args => $attr->{go_last_insert_id_args} }; | 
| 456 | 3324 |  |  |  |  | 12053 | return $dbh->go_dbh_method($meta, 'do', $sql, $attr, @args); | 
| 457 |  |  |  |  |  |  | } | 
| 458 |  |  |  |  |  |  |  | 
| 459 |  |  |  |  |  |  | sub ping { | 
| 460 | 57 |  |  | 57 |  | 1999 | my $dbh = shift; | 
| 461 | 57 | 100 |  |  |  | 336 | return $dbh->set_err('', "can't ping while not connected") # info | 
| 462 |  |  |  |  |  |  | unless $dbh->SUPER::FETCH('Active'); | 
| 463 | 51 |  |  |  |  | 570 | my $skip_ping = $dbh->{go_policy}->skip_ping(); | 
| 464 | 51 | 100 |  |  |  | 400 | return ($skip_ping) ? 1 : $dbh->go_dbh_method(undef, 'ping', @_); | 
| 465 |  |  |  |  |  |  | } | 
| 466 |  |  |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | sub last_insert_id { | 
| 468 | 0 |  |  | 0 |  | 0 | my $dbh = shift; | 
| 469 | 0 | 0 |  |  |  | 0 | my $response = $dbh->{go_response} or return undef; | 
| 470 | 0 |  |  |  |  | 0 | return $response->last_insert_id; | 
| 471 |  |  |  |  |  |  | } | 
| 472 |  |  |  |  |  |  |  | 
| 473 |  |  |  |  |  |  | sub FETCH { | 
| 474 | 2408 |  |  | 2408 |  | 36885 | my ($dbh, $attrib) = @_; | 
| 475 |  |  |  |  |  |  |  | 
| 476 |  |  |  |  |  |  | # FETCH is effectively already cached because the DBI checks the | 
| 477 |  |  |  |  |  |  | # attribute cache in the handle before calling FETCH | 
| 478 |  |  |  |  |  |  | # and this FETCH copies the value into the attribute cache | 
| 479 |  |  |  |  |  |  |  | 
| 480 |  |  |  |  |  |  | # forward driver-private attributes (except ours) | 
| 481 | 2408 | 100 | 66 |  |  | 11192 | if ($attrib =~ m/^[a-z]/ && $attrib !~ /^go_/) { | 
| 482 | 7 |  |  |  |  | 45 | my $value = $dbh->go_dbh_method(undef, 'FETCH', $attrib); | 
| 483 | 7 |  |  |  |  | 56 | $dbh->{$attrib} = $value; # XXX forces caching by DBI | 
| 484 | 7 |  |  |  |  | 34 | return $dbh->{$attrib} = $value; | 
| 485 |  |  |  |  |  |  | } | 
| 486 |  |  |  |  |  |  |  | 
| 487 |  |  |  |  |  |  | # else pass up to DBI to handle | 
| 488 | 2401 |  |  |  |  | 21225 | return $dbh->SUPER::FETCH($attrib); | 
| 489 |  |  |  |  |  |  | } | 
| 490 |  |  |  |  |  |  |  | 
| 491 |  |  |  |  |  |  | sub STORE { | 
| 492 | 7343 |  |  | 7343 |  | 114782 | my ($dbh, $attrib, $value) = @_; | 
| 493 | 7343 | 100 |  |  |  | 17407 | if ($attrib eq 'AutoCommit') { | 
| 494 | 696 | 50 |  |  |  | 1997 | croak "Can't enable transactions when using DBD::Gofer" if !$value; | 
| 495 | 696 | 50 |  |  |  | 4888 | return $dbh->SUPER::STORE($attrib => ($value) ? -901 : -900); | 
| 496 |  |  |  |  |  |  | } | 
| 497 |  |  |  |  |  |  | return $dbh->SUPER::STORE($attrib => $value) | 
| 498 |  |  |  |  |  |  | # we handle this attribute locally | 
| 499 | 6647 | 100 | 100 |  |  | 47274 | if $dbh_local_store_attrib{$attrib} | 
|  |  |  | 100 |  |  |  |  | 
| 500 |  |  |  |  |  |  | # or it's a private_ (application) attribute | 
| 501 |  |  |  |  |  |  | or $attrib =~ /^private_/ | 
| 502 |  |  |  |  |  |  | # or not yet connected (ie being called by DBI->connect) | 
| 503 |  |  |  |  |  |  | or not $dbh->FETCH('Active'); | 
| 504 |  |  |  |  |  |  |  | 
| 505 |  |  |  |  |  |  | return $dbh->SUPER::STORE($attrib => $value) | 
| 506 |  |  |  |  |  |  | if $DBD::Gofer::xxh_local_store_attrib_if_same_value{$attrib} | 
| 507 | 14 | 50 | 66 |  |  | 124 | && do { # values are the same | 
| 508 | 10 |  |  |  |  | 30 | my $crnt = $dbh->FETCH($attrib); | 
| 509 | 10 |  |  |  |  | 63 | local $^W; | 
| 510 | 10 | 50 |  |  |  | 76 | (defined($value) ^ defined($crnt)) | 
| 511 |  |  |  |  |  |  | ? 0 # definedness differs | 
| 512 |  |  |  |  |  |  | : $value eq $crnt; | 
| 513 |  |  |  |  |  |  | }; | 
| 514 |  |  |  |  |  |  |  | 
| 515 |  |  |  |  |  |  | # dbh attributes are set at connect-time - see connect() | 
| 516 | 4 | 50 |  |  |  | 18 | carp("Can't alter \$dbh->{$attrib} after handle created with DBD::Gofer") if $dbh->FETCH('Warn'); | 
| 517 | 4 |  |  |  |  | 1204 | return $dbh->set_err($DBI::stderr, "Can't alter \$dbh->{$attrib} after handle created with DBD::Gofer"); | 
| 518 |  |  |  |  |  |  | } | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | sub disconnect { | 
| 521 | 107 |  |  | 107 |  | 303483 | my $dbh = shift; | 
| 522 | 107 |  |  |  |  | 1263 | $dbh->{go_transport} = undef; | 
| 523 | 107 |  |  |  |  | 1094 | $dbh->STORE(Active => 0); | 
| 524 |  |  |  |  |  |  | } | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | sub prepare { | 
| 527 | 1104 |  |  | 1104 |  | 254017 | my ($dbh, $statement, $attr)= @_; | 
| 528 |  |  |  |  |  |  |  | 
| 529 | 1104 | 50 |  |  |  | 5167 | return $dbh->set_err($DBI::stderr, "Can't prepare when disconnected") | 
| 530 |  |  |  |  |  |  | unless $dbh->FETCH('Active'); | 
| 531 |  |  |  |  |  |  |  | 
| 532 | 1104 | 100 |  |  |  | 9710 | $attr = { %$attr } if $attr; # copy so we can edit | 
| 533 |  |  |  |  |  |  |  | 
| 534 | 1104 |  | 33 |  |  | 7641 | my $policy     = delete($attr->{go_policy}) || $dbh->{go_policy}; | 
| 535 | 1104 |  |  |  |  | 2360 | my $lii_args   = delete $attr->{go_last_insert_id_args}; | 
| 536 |  |  |  |  |  |  | my $go_prepare = delete($attr->{go_prepare_method}) | 
| 537 |  |  |  |  |  |  | || $dbh->{go_prepare_method} | 
| 538 | 1104 |  | 100 |  |  | 11463 | || $policy->prepare_method($dbh, $statement, $attr) | 
| 539 |  |  |  |  |  |  | || 'prepare'; # e.g. for code not using placeholders | 
| 540 | 1104 |  |  |  |  | 2733 | my $go_cache = delete $attr->{go_cache}; | 
| 541 |  |  |  |  |  |  | # set to undef if there are no attributes left for the actual prepare call | 
| 542 | 1104 | 100 | 66 |  |  | 7094 | $attr = undef if $attr and not %$attr; | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | my ($sth, $sth_inner) = DBI::_new_sth($dbh, { | 
| 545 |  |  |  |  |  |  | Statement => $statement, | 
| 546 |  |  |  |  |  |  | go_prepare_call => [ 0, $go_prepare, $statement, $attr ], | 
| 547 |  |  |  |  |  |  | # go_method_calls => [], # autovivs if needed | 
| 548 |  |  |  |  |  |  | go_request => $dbh->{go_request}, | 
| 549 |  |  |  |  |  |  | go_transport => $dbh->{go_transport}, | 
| 550 | 1104 |  |  |  |  | 13431 | go_policy => $policy, | 
| 551 |  |  |  |  |  |  | go_last_insert_id_args => $lii_args, | 
| 552 |  |  |  |  |  |  | go_cache => $go_cache, | 
| 553 |  |  |  |  |  |  | }); | 
| 554 | 1104 |  |  |  |  | 7707 | $sth->STORE(Active => 0); # XXX needed? It should be the default | 
| 555 |  |  |  |  |  |  |  | 
| 556 | 1104 |  |  |  |  | 8985 | my $skip_prepare_check = $policy->skip_prepare_check($attr, $dbh, $statement, $attr, $sth); | 
| 557 | 1104 | 100 |  |  |  | 3682 | if (not $skip_prepare_check) { | 
| 558 | 594 | 100 |  |  |  | 3290 | $sth->go_sth_method() or return undef; | 
| 559 |  |  |  |  |  |  | } | 
| 560 |  |  |  |  |  |  |  | 
| 561 | 1032 |  |  |  |  | 10690 | return $sth; | 
| 562 |  |  |  |  |  |  | } | 
| 563 |  |  |  |  |  |  |  | 
| 564 |  |  |  |  |  |  | sub prepare_cached { | 
| 565 | 14 |  |  | 14 |  | 13003 | my ($dbh, $sql, $attr, $if_active)= @_; | 
| 566 | 14 |  | 100 |  |  | 71 | $attr ||= {}; | 
| 567 |  |  |  |  |  |  | return $dbh->SUPER::prepare_cached($sql, { | 
| 568 |  |  |  |  |  |  | %$attr, | 
| 569 | 14 |  | 50 |  |  | 121 | go_prepare_method => $attr->{go_prepare_method} || 'prepare_cached', | 
| 570 |  |  |  |  |  |  | }, $if_active); | 
| 571 |  |  |  |  |  |  | } | 
| 572 |  |  |  |  |  |  |  | 
| 573 |  |  |  |  |  |  | *go_cache = \&DBD::Gofer::go_cache; | 
| 574 |  |  |  |  |  |  | } | 
| 575 |  |  |  |  |  |  |  | 
| 576 |  |  |  |  |  |  |  | 
| 577 |  |  |  |  |  |  | {   package DBD::Gofer::st; # ====== STATEMENT ====== | 
| 578 |  |  |  |  |  |  | $imp_data_size = 0; | 
| 579 | 56 |  |  | 56 |  | 464 | use strict; | 
|  | 56 |  |  |  |  | 108 |  | 
|  | 56 |  |  |  |  | 74322 |  | 
| 580 |  |  |  |  |  |  |  | 
| 581 |  |  |  |  |  |  | my %sth_local_store_attrib = (%DBD::Gofer::xxh_local_store_attrib, NUM_OF_FIELDS => 1); | 
| 582 |  |  |  |  |  |  |  | 
| 583 |  |  |  |  |  |  | sub go_sth_method { | 
| 584 | 3061 |  |  | 3061 |  | 22462 | my ($sth, $meta) = @_; | 
| 585 |  |  |  |  |  |  |  | 
| 586 | 3061 | 100 |  |  |  | 10596 | if (my $ParamValues = $sth->{ParamValues}) { | 
| 587 | 2157 |  |  |  |  | 3883 | my $ParamAttr = $sth->{ParamAttr}; | 
| 588 |  |  |  |  |  |  | # XXX the sort here is a hack to work around a DBD::Sybase bug | 
| 589 |  |  |  |  |  |  | # but only works properly for params 1..9 | 
| 590 |  |  |  |  |  |  | # (reverse because of the unshift) | 
| 591 | 2157 |  |  |  |  | 11272 | my @params = reverse sort keys %$ParamValues; | 
| 592 | 2157 | 50 | 50 |  |  | 8348 | if (@params > 9 && ($sth->{Database}{go_dsn}||'') =~ /dbi:Sybase/) { | 
|  |  |  | 66 |  |  |  |  | 
| 593 |  |  |  |  |  |  | # if more than 9 then we need to do a proper numeric sort | 
| 594 |  |  |  |  |  |  | # also warn to alert user of this issue | 
| 595 | 0 |  |  |  |  | 0 | warn "Sybase param binding order hack in use"; | 
| 596 | 0 |  |  |  |  | 0 | @params = sort { $b <=> $a } @params; | 
|  | 0 |  |  |  |  | 0 |  | 
| 597 |  |  |  |  |  |  | } | 
| 598 | 2157 |  |  |  |  | 5111 | for my $p (@params) { | 
| 599 |  |  |  |  |  |  | # unshift to put binds before execute call | 
| 600 | 5418 |  |  |  |  | 19303 | unshift @{ $sth->{go_method_calls} }, | 
| 601 | 5418 |  |  |  |  | 7104 | [ 'bind_param', $p, $ParamValues->{$p}, $ParamAttr->{$p} ]; | 
| 602 |  |  |  |  |  |  | } | 
| 603 |  |  |  |  |  |  | } | 
| 604 |  |  |  |  |  |  |  | 
| 605 | 3061 | 50 |  |  |  | 10300 | my $dbh = $sth->{Database} or die "panic"; | 
| 606 | 3061 |  |  |  |  | 6415 | ++$dbh->{go_request_count}; | 
| 607 |  |  |  |  |  |  |  | 
| 608 | 3061 |  |  |  |  | 5443 | my $request = $sth->{go_request}; | 
| 609 | 3061 |  |  |  |  | 16192 | $request->init_request($sth->{go_prepare_call}, $sth); | 
| 610 |  |  |  |  |  |  | $request->sth_method_calls(delete $sth->{go_method_calls}) | 
| 611 | 3061 | 100 |  |  |  | 14050 | if $sth->{go_method_calls}; | 
| 612 | 3061 |  |  |  |  | 11102 | $request->sth_result_attr({}); # (currently) also indicates this is an sth request | 
| 613 |  |  |  |  |  |  |  | 
| 614 |  |  |  |  |  |  | $request->dbh_last_insert_id_args($meta->{go_last_insert_id_args}) | 
| 615 | 3061 | 50 |  |  |  | 8664 | if $meta->{go_last_insert_id_args}; | 
| 616 |  |  |  |  |  |  |  | 
| 617 | 3061 |  |  |  |  | 6060 | my $go_policy = $sth->{go_policy}; | 
| 618 | 3061 |  |  |  |  | 13044 | my $dbh_attribute_update = $go_policy->dbh_attribute_update(); | 
| 619 |  |  |  |  |  |  | $request->dbh_attributes( $go_policy->dbh_attribute_list() ) | 
| 620 |  |  |  |  |  |  | if $dbh_attribute_update eq 'every' | 
| 621 | 3061 | 100 | 100 |  |  | 17088 | or $dbh->{go_request_count}==1; | 
| 622 |  |  |  |  |  |  |  | 
| 623 |  |  |  |  |  |  | my $transport = $sth->{go_transport} | 
| 624 | 3061 | 50 |  |  |  | 9443 | or return $sth->set_err($DBI::stderr, "Not connected (no transport)"); | 
| 625 |  |  |  |  |  |  |  | 
| 626 |  |  |  |  |  |  | local $transport->{go_cache} = $sth->{go_cache} | 
| 627 | 3061 | 100 |  |  |  | 8439 | if defined $sth->{go_cache}; | 
| 628 |  |  |  |  |  |  |  | 
| 629 | 3061 |  |  |  |  | 11123 | my ($response, $retransmit_sub) = $transport->transmit_request($request); | 
| 630 | 3061 |  | 66 |  |  | 21053 | $response ||= $transport->receive_response($request, $retransmit_sub); | 
| 631 | 3061 | 50 |  |  |  | 14454 | $sth->{go_response} = $response | 
| 632 |  |  |  |  |  |  | or die "No response object returned by $transport"; | 
| 633 | 3061 |  |  |  |  | 19491 | $dbh->{go_response} = $response; # mainly for last_insert_id | 
| 634 |  |  |  |  |  |  |  | 
| 635 | 3061 | 100 |  |  |  | 11034 | if (my $dbh_attributes = $response->dbh_attributes) { | 
| 636 |  |  |  |  |  |  | # XXX we don't STORE here, we just stuff the value into the attribute cache | 
| 637 |  |  |  |  |  |  | $dbh->{$_} = $dbh_attributes->{$_} | 
| 638 | 2917 |  |  |  |  | 31099 | for keys %$dbh_attributes; | 
| 639 |  |  |  |  |  |  | # record the values returned, so we know that we have fetched | 
| 640 |  |  |  |  |  |  | # values are which we have fetched (see dbh->FETCH method) | 
| 641 | 2917 |  |  |  |  | 27467 | $dbh->{go_dbh_attributes_fetched} = $dbh_attributes; | 
| 642 |  |  |  |  |  |  | } | 
| 643 |  |  |  |  |  |  |  | 
| 644 | 3061 |  |  |  |  | 11171 | my $rv = $response->rv; # may be undef on error | 
| 645 | 3061 | 100 |  |  |  | 9147 | if ($response->sth_resultsets) { | 
| 646 |  |  |  |  |  |  | # setup first resultset - including sth attributes | 
| 647 | 2989 |  |  |  |  | 22756 | $sth->more_results; | 
| 648 |  |  |  |  |  |  | } | 
| 649 |  |  |  |  |  |  | else { | 
| 650 | 72 |  |  |  |  | 632 | $sth->STORE(Active => 0); | 
| 651 | 72 |  |  |  |  | 631 | $sth->{go_rows} = $rv; | 
| 652 |  |  |  |  |  |  | } | 
| 653 |  |  |  |  |  |  | # set error/warn/info (after more_results as that'll clear err) | 
| 654 | 3061 |  |  |  |  | 18437 | DBD::Gofer::set_err_from_response($sth, $response); | 
| 655 |  |  |  |  |  |  |  | 
| 656 | 3061 |  |  |  |  | 53245 | return $rv; | 
| 657 |  |  |  |  |  |  | } | 
| 658 |  |  |  |  |  |  |  | 
| 659 |  |  |  |  |  |  |  | 
| 660 |  |  |  |  |  |  | sub bind_param { | 
| 661 | 5378 |  |  | 5378 |  | 24972 | my ($sth, $param, $value, $attr) = @_; | 
| 662 | 5378 |  |  |  |  | 12990 | $sth->{ParamValues}{$param} = $value; | 
| 663 | 5378 | 50 |  |  |  | 10541 | $sth->{ParamAttr}{$param}   = $attr | 
| 664 |  |  |  |  |  |  | if defined $attr; # attr is sticky if not explicitly set | 
| 665 | 5378 |  |  |  |  | 15081 | return 1; | 
| 666 |  |  |  |  |  |  | } | 
| 667 |  |  |  |  |  |  |  | 
| 668 |  |  |  |  |  |  |  | 
| 669 |  |  |  |  |  |  | sub execute { | 
| 670 | 2467 |  |  | 2467 |  | 171692 | my $sth = shift; | 
| 671 | 2467 |  |  |  |  | 16552 | $sth->bind_param($_, $_[$_-1]) for (1..@_); | 
| 672 | 2467 |  |  |  |  | 6084 | push @{ $sth->{go_method_calls} }, [ 'execute' ]; | 
|  | 2467 |  |  |  |  | 12020 |  | 
| 673 | 2467 |  |  |  |  | 8377 | my $meta = { go_last_insert_id_args => $sth->{go_last_insert_id_args} }; | 
| 674 | 2467 |  |  |  |  | 11327 | return $sth->go_sth_method($meta); | 
| 675 |  |  |  |  |  |  | } | 
| 676 |  |  |  |  |  |  |  | 
| 677 |  |  |  |  |  |  |  | 
| 678 |  |  |  |  |  |  | sub more_results { | 
| 679 | 3275 |  |  | 3275 |  | 34323 | my $sth = shift; | 
| 680 |  |  |  |  |  |  |  | 
| 681 | 3275 |  |  |  |  | 18508 | $sth->finish; | 
| 682 |  |  |  |  |  |  |  | 
| 683 | 3275 | 100 |  |  |  | 18045 | my $response = $sth->{go_response} or do { | 
| 684 |  |  |  |  |  |  | # e.g., we haven't sent a request yet (ie prepare then more_results) | 
| 685 | 96 |  |  |  |  | 614 | $sth->trace_msg("    No response object present", 3); | 
| 686 | 96 |  |  |  |  | 1361 | return; | 
| 687 |  |  |  |  |  |  | }; | 
| 688 |  |  |  |  |  |  |  | 
| 689 | 3179 | 50 |  |  |  | 9228 | my $resultset_list = $response->sth_resultsets | 
| 690 |  |  |  |  |  |  | or return $sth->set_err($DBI::stderr, "No sth_resultsets"); | 
| 691 |  |  |  |  |  |  |  | 
| 692 | 3179 | 100 |  |  |  | 11586 | my $meta = shift @$resultset_list | 
| 693 |  |  |  |  |  |  | or return undef; # no more result sets | 
| 694 |  |  |  |  |  |  | #warn "more_results: ".Data::Dumper::Dumper($meta); | 
| 695 |  |  |  |  |  |  |  | 
| 696 |  |  |  |  |  |  | # pull out the special non-attributes first | 
| 697 |  |  |  |  |  |  | my ($rowset, $err, $errstr, $state) | 
| 698 | 2999 |  |  |  |  | 5814 | = delete @{$meta}{qw(rowset err errstr state)}; | 
|  | 2999 |  |  |  |  | 13132 |  | 
| 699 |  |  |  |  |  |  |  | 
| 700 |  |  |  |  |  |  | # copy meta attributes into attribute cache | 
| 701 | 2999 |  |  |  |  | 7357 | my $NUM_OF_FIELDS = delete $meta->{NUM_OF_FIELDS}; | 
| 702 | 2999 |  |  |  |  | 17937 | $sth->STORE('NUM_OF_FIELDS', $NUM_OF_FIELDS); | 
| 703 |  |  |  |  |  |  | # XXX need to use STORE for some? | 
| 704 | 2999 |  |  |  |  | 34051 | $sth->{$_} = $meta->{$_} for keys %$meta; | 
| 705 |  |  |  |  |  |  |  | 
| 706 | 2999 | 100 | 100 |  |  | 15315 | if (($NUM_OF_FIELDS||0) > 0) { | 
| 707 | 2472 | 100 |  |  |  | 8743 | $sth->{go_rows}           = ($rowset) ? @$rowset : -1; | 
| 708 | 2472 |  |  |  |  | 31572 | $sth->{go_current_rowset} = $rowset; | 
| 709 | 2472 | 100 |  |  |  | 7783 | $sth->{go_current_rowset_err} = [ $err, $errstr, $state ] | 
| 710 |  |  |  |  |  |  | if defined $err; | 
| 711 | 2472 | 100 |  |  |  | 11822 | $sth->STORE(Active => 1) if $rowset; | 
| 712 |  |  |  |  |  |  | } | 
| 713 |  |  |  |  |  |  |  | 
| 714 | 2999 |  |  |  |  | 14606 | return $sth; | 
| 715 |  |  |  |  |  |  | } | 
| 716 |  |  |  |  |  |  |  | 
| 717 |  |  |  |  |  |  |  | 
| 718 |  |  |  |  |  |  | sub go_clone_sth { | 
| 719 | 0 |  |  | 0 |  | 0 | my ($sth1) = @_; | 
| 720 |  |  |  |  |  |  | # clone an (un-fetched-from) sth - effectively undoes the initial more_results | 
| 721 |  |  |  |  |  |  | # not 100% so just for use in caching returned sth e.g. table_info | 
| 722 | 0 |  |  |  |  | 0 | my $sth2 = $sth1->{Database}->prepare($sth1->{Statement}, { go_skip_prepare_check => 1 }); | 
| 723 | 0 |  |  |  |  | 0 | $sth2->STORE($_, $sth1->{$_}) for qw(NUM_OF_FIELDS Active); | 
| 724 | 0 |  |  |  |  | 0 | my $sth2_inner = tied %$sth2; | 
| 725 | 0 |  |  |  |  | 0 | $sth2_inner->{$_} = $sth1->{$_} for qw(NUM_OF_PARAMS FetchHashKeyName); | 
| 726 | 0 |  |  |  |  | 0 | die "not fully implemented yet"; | 
| 727 | 0 |  |  |  |  | 0 | return $sth2; | 
| 728 |  |  |  |  |  |  | } | 
| 729 |  |  |  |  |  |  |  | 
| 730 |  |  |  |  |  |  |  | 
| 731 |  |  |  |  |  |  | sub fetchrow_arrayref { | 
| 732 | 4777 |  |  | 4777 |  | 54633 | my ($sth) = @_; | 
| 733 | 4777 |  | 33 |  |  | 11926 | my $resultset = $sth->{go_current_rowset} || do { | 
| 734 |  |  |  |  |  |  | # should only happen if fetch called after execute failed | 
| 735 |  |  |  |  |  |  | my $rowset_err = $sth->{go_current_rowset_err} | 
| 736 |  |  |  |  |  |  | || [ 1, 'no result set (did execute fail)' ]; | 
| 737 |  |  |  |  |  |  | return $sth->set_err( @$rowset_err ); | 
| 738 |  |  |  |  |  |  | }; | 
| 739 | 4777 | 100 |  |  |  | 42885 | return $sth->_set_fbav(shift @$resultset) if @$resultset; | 
| 740 | 153 |  |  |  |  | 582 | $sth->finish;     # no more data so finish | 
| 741 | 153 |  |  |  |  | 935 | return undef; | 
| 742 |  |  |  |  |  |  | } | 
| 743 |  |  |  |  |  |  | *fetch = \&fetchrow_arrayref; # alias | 
| 744 |  |  |  |  |  |  |  | 
| 745 |  |  |  |  |  |  |  | 
| 746 |  |  |  |  |  |  | sub fetchall_arrayref { | 
| 747 | 238 |  |  | 238 |  | 29254 | my ($sth, $slice, $max_rows) = @_; | 
| 748 | 238 |  | 66 |  |  | 1573 | my $resultset = $sth->{go_current_rowset} || do { | 
| 749 |  |  |  |  |  |  | # should only happen if fetch called after execute failed | 
| 750 |  |  |  |  |  |  | my $rowset_err = $sth->{go_current_rowset_err} | 
| 751 |  |  |  |  |  |  | || [ 1, 'no result set (did execute fail)' ]; | 
| 752 |  |  |  |  |  |  | return $sth->set_err( @$rowset_err ); | 
| 753 |  |  |  |  |  |  | }; | 
| 754 | 214 |  | 100 |  |  | 1850 | my $mode = ref($slice) || 'ARRAY'; | 
| 755 | 214 | 100 | 66 |  |  | 1726 | return $sth->SUPER::fetchall_arrayref($slice, $max_rows) | 
| 756 |  |  |  |  |  |  | if ref($slice) or defined $max_rows; | 
| 757 | 184 |  |  |  |  | 922 | $sth->finish;     # no more data after this so finish | 
| 758 | 184 |  |  |  |  | 1589 | return $resultset; | 
| 759 |  |  |  |  |  |  | } | 
| 760 |  |  |  |  |  |  |  | 
| 761 |  |  |  |  |  |  |  | 
| 762 |  |  |  |  |  |  | sub rows { | 
| 763 | 24 |  |  | 24 |  | 10039 | return shift->{go_rows}; | 
| 764 |  |  |  |  |  |  | } | 
| 765 |  |  |  |  |  |  |  | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  | sub STORE { | 
| 768 | 6428 |  |  | 6428 |  | 55589 | my ($sth, $attrib, $value) = @_; | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | return $sth->SUPER::STORE($attrib => $value) | 
| 771 | 6428 | 50 | 33 |  |  | 50227 | if $sth_local_store_attrib{$attrib} # handle locally | 
| 772 |  |  |  |  |  |  | # or it's a private_ (application) attribute | 
| 773 |  |  |  |  |  |  | or $attrib =~ /^private_/; | 
| 774 |  |  |  |  |  |  |  | 
| 775 |  |  |  |  |  |  | # otherwise warn but do it anyway | 
| 776 |  |  |  |  |  |  | # this will probably need refining later | 
| 777 | 0 |  |  |  |  |  | my $msg = "Altering \$sth->{$attrib} won't affect proxied handle"; | 
| 778 | 0 | 0 |  |  |  |  | Carp::carp($msg) if $sth->FETCH('Warn'); | 
| 779 |  |  |  |  |  |  |  | 
| 780 |  |  |  |  |  |  | # XXX could perhaps do | 
| 781 |  |  |  |  |  |  | #   push @{ $sth->{go_method_calls} }, [ 'STORE', $attrib, $value ] | 
| 782 |  |  |  |  |  |  | #       if not $sth->FETCH('Executed'); | 
| 783 |  |  |  |  |  |  | # but how to handle repeat executions? How to we know when an | 
| 784 |  |  |  |  |  |  | # attribute is being set to affect the current resultset or the | 
| 785 |  |  |  |  |  |  | # next execution? | 
| 786 |  |  |  |  |  |  | # Could just always use go_method_calls I guess. | 
| 787 |  |  |  |  |  |  |  | 
| 788 |  |  |  |  |  |  | # do the store locally anyway, just in case | 
| 789 | 0 |  |  |  |  |  | $sth->SUPER::STORE($attrib => $value); | 
| 790 |  |  |  |  |  |  |  | 
| 791 | 0 |  |  |  |  |  | return $sth->set_err($DBI::stderr, $msg); | 
| 792 |  |  |  |  |  |  | } | 
| 793 |  |  |  |  |  |  |  | 
| 794 |  |  |  |  |  |  | # sub bind_param_array | 
| 795 |  |  |  |  |  |  | # we use DBI's default, which sets $sth->{ParamArrays}{$param} = $value | 
| 796 |  |  |  |  |  |  | # and calls bind_param($param, undef, $attr) if $attr. | 
| 797 |  |  |  |  |  |  |  | 
| 798 |  |  |  |  |  |  | sub execute_array { | 
| 799 | 0 |  |  | 0 |  |  | my $sth = shift; | 
| 800 | 0 |  |  |  |  |  | my $attr = shift; | 
| 801 | 0 |  |  |  |  |  | $sth->bind_param_array($_, $_[$_-1]) for (1..@_); | 
| 802 | 0 |  |  |  |  |  | push @{ $sth->{go_method_calls} }, [ 'execute_array', $attr ]; | 
|  | 0 |  |  |  |  |  |  | 
| 803 | 0 |  |  |  |  |  | return $sth->go_sth_method($attr); | 
| 804 |  |  |  |  |  |  | } | 
| 805 |  |  |  |  |  |  |  | 
| 806 |  |  |  |  |  |  | *go_cache = \&DBD::Gofer::go_cache; | 
| 807 |  |  |  |  |  |  | } | 
| 808 |  |  |  |  |  |  |  | 
| 809 |  |  |  |  |  |  | 1; | 
| 810 |  |  |  |  |  |  |  | 
| 811 |  |  |  |  |  |  | __END__ |