[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
1 { 2 package DBD::Gofer; 3 4 use strict; 5 6 require DBI; 7 require DBI::Gofer::Request; 8 require DBI::Gofer::Response; 9 require Carp; 10 11 our $VERSION = sprintf("0.%06d", q$Revision: 11565 $ =~ /(\d+)/o); 12 13 # $Id: Gofer.pm 11565 2008-07-22 20:17:33Z timbo $ 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 PrintError PrintWarn 33 Profile 34 RaiseError 35 RootClass 36 ShowErrorStatement 37 Taint TaintIn TaintOut 38 TraceLevel 39 Warn 40 dbi_quote_identifier_cache 41 dbi_connect_closure 42 dbi_go_execute_unique 43 ); 44 our %xxh_local_store_attrib_if_same_value = map { $_=>1 } qw( 45 Username 46 dbi_connect_method 47 ); 48 49 our $drh = undef; # holds driver handle once initialised 50 our $methods_already_installed; 51 52 sub driver{ 53 return $drh if $drh; 54 55 DBI->setup_driver('DBD::Gofer'); 56 57 unless ($methods_already_installed++) { 58 my $opts = { O=> 0x0004 }; # IMA_KEEP_ERR 59 DBD::Gofer::db->install_method('go_dbh_method', $opts); 60 DBD::Gofer::st->install_method('go_sth_method', $opts); 61 DBD::Gofer::st->install_method('go_clone_sth', $opts); 62 DBD::Gofer::db->install_method('go_cache', $opts); 63 DBD::Gofer::st->install_method('go_cache', $opts); 64 } 65 66 my($class, $attr) = @_; 67 $class .= "::dr"; 68 ($drh) = DBI::_new_drh($class, { 69 'Name' => 'Gofer', 70 'Version' => $VERSION, 71 'Attribution' => 'DBD Gofer by Tim Bunce', 72 }); 73 74 $drh; 75 } 76 77 78 sub CLONE { 79 undef $drh; 80 } 81 82 83 sub go_cache { 84 my $h = shift; 85 $h->{go_cache} = shift if @_; 86 # return handle's override go_cache, if it has one 87 return $h->{go_cache} if defined $h->{go_cache}; 88 # or else the transports default go_cache 89 return $h->{go_transport}->{go_cache}; 90 } 91 92 93 sub set_err_from_response { # set error/warn/info and propagate warnings 94 my $h = shift; 95 my $response = shift; 96 if (my $warnings = $response->warnings) { 97 warn $_ for @$warnings; 98 } 99 return $h->set_err($response->err_errstr_state); 100 } 101 102 103 sub install_methods_proxy { 104 my ($installed_methods) = @_; 105 while ( my ($full_method, $attr) = each %$installed_methods ) { 106 # need to install both a DBI dispatch stub and a proxy stub 107 # (the dispatch stub may be already here due to local driver use) 108 109 DBI->_install_method($full_method, "", $attr||{}) 110 unless defined &{$full_method}; 111 112 # now install proxy stubs on the driver side 113 $full_method =~ m/^DBI::(\w\w)::(\w+)$/ 114 or die "Invalid method name '$full_method' for install_method"; 115 my ($type, $method) = ($1, $2); 116 my $driver_method = "DBD::Gofer::$type}::$method}"; 117 next if defined &{$driver_method}; 118 my $sub; 119 if ($type eq 'db') { 120 $sub = sub { return shift->go_dbh_method(undef, $method, @_) }; 121 } 122 else { 123 $sub = sub { shift->set_err($DBI::stderr, "Can't call \$$type}h->$method when using DBD::Gofer"); return; }; 124 } 125 no strict 'refs'; 126 *$driver_method = $sub; 127 } 128 } 129 } 130 131 132 { package DBD::Gofer::dr; # ====== DRIVER ====== 133 134 $imp_data_size = 0; 135 use strict; 136 137 sub connect_cached { 138 my ($drh, $dsn, $user, $auth, $attr)= @_; 139 $attr ||= {}; 140 return $drh->SUPER::connect_cached($dsn, $user, $auth, { 141 (%$attr), 142 go_connect_method => $attr->{go_connect_method} || 'connect_cached', 143 }); 144 } 145 146 147 sub connect { 148 my($drh, $dsn, $user, $auth, $attr)= @_; 149 my $orig_dsn = $dsn; 150 151 # first remove dsn= and everything after it 152 my $remote_dsn = ($dsn =~ s/;?\bdsn=(.*)$// && $1) 153 or return $drh->set_err($DBI::stderr, "No dsn= argument in '$orig_dsn'"); 154 155 if ($attr->{go_bypass}) { # don't use DBD::Gofer for this connection 156 # useful for testing with DBI_AUTOPROXY, e.g., t/03handle.t 157 return DBI->connect($remote_dsn, $user, $auth, $attr); 158 } 159 160 my %go_attr; 161 # extract any go_ attributes from the connect() attr arg 162 for my $k (grep { /^go_/ } keys %$attr) { 163 $go_attr{$k} = delete $attr->{$k}; 164 } 165 # then override those with any attributes embedded in our dsn (not remote_dsn) 166 for my $kv (grep /=/, split /;/, $dsn, -1) { 167 my ($k, $v) = split /=/, $kv, 2; 168 $go_attr{ "go_$k" } = $v; 169 } 170 171 if (not ref $go_attr{go_policy}) { # if not a policy object already 172 my $policy_class = $go_attr{go_policy} || 'classic'; 173 $policy_class = "DBD::Gofer::Policy::$policy_class" 174 unless $policy_class =~ /::/; 175 _load_class($policy_class) 176 or return $drh->set_err($DBI::stderr, "Can't load $policy_class: $@"); 177 # replace policy name in %go_attr with policy object 178 $go_attr{go_policy} = eval { $policy_class->new(\%go_attr) } 179 or return $drh->set_err($DBI::stderr, "Can't instanciate $policy_class: $@"); 180 } 181 # policy object is left in $go_attr{go_policy} so transport can see it 182 my $go_policy = $go_attr{go_policy}; 183 184 if ($go_attr{go_cache} and not ref $go_attr{go_cache}) { # if not a cache object already 185 my $cache_class = $go_attr{go_cache}; 186 $cache_class = "DBI::Util::CacheMemory" if $cache_class eq '1'; 187 _load_class($cache_class) 188 or return $drh->set_err($DBI::stderr, "Can't load $cache_class $@"); 189 $go_attr{go_cache} = eval { $cache_class->new() } 190 or $drh->set_err(0, "Can't instanciate $cache_class: $@"); # warning 191 } 192 193 # delete any other attributes that don't apply to transport 194 my $go_connect_method = delete $go_attr{go_connect_method}; 195 196 my $transport_class = delete $go_attr{go_transport} 197 or return $drh->set_err($DBI::stderr, "No transport= argument in '$orig_dsn'"); 198 $transport_class = "DBD::Gofer::Transport::$transport_class" 199 unless $transport_class =~ /::/; 200 _load_class($transport_class) 201 or return $drh->set_err($DBI::stderr, "Can't load $transport_class: $@"); 202 my $go_transport = eval { $transport_class->new(\%go_attr) } 203 or return $drh->set_err($DBI::stderr, "Can't instanciate $transport_class: $@"); 204 205 my $request_class = "DBI::Gofer::Request"; 206 my $go_request = eval { 207 my $go_attr = { %$attr }; 208 # XXX user/pass of fwd server vs db server ? also impact of autoproxy 209 if ($user) { 210 $go_attr->{Username} = $user; 211 $go_attr->{Password} = $auth; 212 } 213 # delete any attributes we can't serialize (or don't want to) 214 delete @{$go_attr}{qw(Profile HandleError HandleSetErr Callbacks)}; 215 # delete any attributes that should only apply to the client-side 216 delete @{$go_attr}{qw(RootClass DbTypeSubclass)}; 217 218 $go_connect_method ||= $go_policy->connect_method($remote_dsn, $go_attr) || 'connect'; 219 $request_class->new({ 220 dbh_connect_call => [ $go_connect_method, $remote_dsn, $user, $auth, $go_attr ], 221 }) 222 } or return $drh->set_err($DBI::stderr, "Can't instanciate $request_class: $@"); 223 224 my ($dbh, $dbh_inner) = DBI::_new_dbh($drh, { 225 'Name' => $dsn, 226 'USER' => $user, 227 go_transport => $go_transport, 228 go_request => $go_request, 229 go_policy => $go_policy, 230 }); 231 232 # mark as inactive temporarily for STORE. Active not set until connected() called. 233 $dbh->STORE(Active => 0); 234 235 # should we ping to check the connection 236 # and fetch dbh attributes 237 my $skip_connect_check = $go_policy->skip_connect_check($attr, $dbh); 238 if (not $skip_connect_check) { 239 if (not $dbh->go_dbh_method(undef, 'ping')) { 240 return undef if $dbh->err; # error already recorded, typically 241 return $dbh->set_err($DBI::stderr, "ping failed"); 242 } 243 } 244 245 return $dbh; 246 } 247 248 sub _load_class { # return true or false+$@ 249 my $class = shift; 250 (my $pm = $class) =~ s{::}{/}g; 251 $pm .= ".pm"; 252 return 1 if eval { require $pm }; 253 delete $INC{$pm}; # shouldn't be needed (perl bug?) and assigning undef isn't enough 254 undef; # error in $@ 255 } 256 257 } 258 259 260 { package DBD::Gofer::db; # ====== DATABASE ====== 261 $imp_data_size = 0; 262 use strict; 263 use Carp qw(carp croak); 264 265 my %dbh_local_store_attrib = %DBD::Gofer::xxh_local_store_attrib; 266 267 sub connected { 268 shift->STORE(Active => 1); 269 } 270 271 sub go_dbh_method { 272 my $dbh = shift; 273 my $meta = shift; 274 # @_ now contains ($method_name, @args) 275 276 my $request = $dbh->{go_request}; 277 $request->init_request([ wantarray, @_ ], $dbh); 278 ++$dbh->{go_request_count}; 279 280 my $go_policy = $dbh->{go_policy}; 281 my $dbh_attribute_update = $go_policy->dbh_attribute_update(); 282 $request->dbh_attributes( $go_policy->dbh_attribute_list() ) 283 if $dbh_attribute_update eq 'every' 284 or $dbh->{go_request_count}==1; 285 286 $request->dbh_last_insert_id_args($meta->{go_last_insert_id_args}) 287 if $meta->{go_last_insert_id_args}; 288 289 my $transport = $dbh->{go_transport} 290 or return $dbh->set_err($DBI::stderr, "Not connected (no transport)"); 291 292 local $transport->{go_cache} = $dbh->{go_cache} 293 if defined $dbh->{go_cache}; 294 295 my ($response, $retransmit_sub) = $transport->transmit_request($request); 296 $response ||= $transport->receive_response($request, $retransmit_sub); 297 $dbh->{go_response} = $response 298 or die "No response object returned by $transport"; 299 300 die "response '$response' returned by $transport is not a response object" 301 unless UNIVERSAL::isa($response,"DBI::Gofer::Response"); 302 303 if (my $dbh_attributes = $response->dbh_attributes) { 304 305 # XXX installed_methods piggbacks on dbh_attributes for now 306 if (my $installed_methods = delete $dbh_attributes->{dbi_installed_methods}) { 307 DBD::Gofer::install_methods_proxy($installed_methods) 308 if $dbh->{go_request_count}==1; 309 } 310 311 # XXX we don't STORE here, we just stuff the value into the attribute cache 312 $dbh->{$_} = $dbh_attributes->{$_} 313 for keys %$dbh_attributes; 314 } 315 316 my $rv = $response->rv; 317 if (my $resultset_list = $response->sth_resultsets) { 318 # dbh method call returned one or more resultsets 319 # (was probably a metadata method like table_info) 320 # 321 # setup an sth but don't execute/forward it 322 my $sth = $dbh->prepare(undef, { go_skip_prepare_check => 1 }); 323 # set the sth response to our dbh response 324 (tied %$sth)->{go_response} = $response; 325 # setup the sth with the results in our response 326 $sth->more_results; 327 # and return that new sth as if it came from original request 328 $rv = [ $sth ]; 329 } 330 elsif (!$rv) { # should only occur for major transport-level error 331 #carp("no rv in response { @{[ %$response ]} }"); 332 $rv = [ ]; 333 } 334 335 DBD::Gofer::set_err_from_response($dbh, $response); 336 337 return (wantarray) ? @$rv : $rv->[0]; 338 } 339 340 341 # Methods that should be forwarded but can be cached 342 for my $method (qw( 343 tables table_info column_info primary_key_info foreign_key_info statistics_info 344 data_sources type_info_all get_info 345 parse_trace_flags parse_trace_flag 346 func 347 )) { 348 my $policy_name = "cache_$method"; 349 my $super_name = "SUPER::$method"; 350 my $sub = sub { 351 my $dbh = shift; 352 my $rv; 353 354 # if we know the remote side doesn't override the DBI's default method 355 # then we might as well just call the DBI's default method on the client 356 # (which may, in turn, call other methods that are forwarded, like get_info) 357 if ($dbh->{dbi_default_methods}{$method} && $dbh->{go_policy}->skip_default_methods()) { 358 $dbh->trace_msg(" !! $method: using local default as remote method is also default\n"); 359 return $dbh->$super_name(@_); 360 } 361 362 my $cache; 363 my $cache_key; 364 if (my $cache_it = $dbh->{go_policy}->$policy_name(undef, $dbh, @_)) { 365 $cache = $dbh->{go_meta_cache} ||= {}; # keep separate from go_cache 366 $cache_key = sprintf "%s_wa%d(%s)", $policy_name, wantarray||0, 367 join(",\t", map { # XXX basic but sufficient for now 368 !ref($_) ? DBI::neat($_,1e6) 369 : ref($_) eq 'ARRAY' ? DBI::neat_list($_,1e6,",\001") 370 : ref($_) eq 'HASH' ? do { my @k = sort keys %$_; DBI::neat_list([@k,@{$_}{@k}],1e6,",\002") } 371 : do { warn "unhandled argument type ($_)"; $_ } 372 } @_); 373 if ($rv = $cache->{$cache_key}) { 374 $dbh->trace_msg("$method(@_) returning previously cached value ($cache_key)\n",4); 375 my @cache_rv = @$rv; 376 # if it's an sth we have to clone it 377 $cache_rv[0] = $cache_rv[0]->go_clone_sth if UNIVERSAL::isa($cache_rv[0],'DBI::st'); 378 return (wantarray) ? @cache_rv : $cache_rv[0]; 379 } 380 } 381 382 $rv = [ (wantarray) 383 ? ($dbh->go_dbh_method(undef, $method, @_)) 384 : scalar $dbh->go_dbh_method(undef, $method, @_) 385 ]; 386 387 if ($cache) { 388 $dbh->trace_msg("$method(@_) caching return value ($cache_key)\n",4); 389 my @cache_rv = @$rv; 390 # if it's an sth we have to clone it 391 #$cache_rv[0] = $cache_rv[0]->go_clone_sth 392 # if UNIVERSAL::isa($cache_rv[0],'DBI::st'); 393 $cache->{$cache_key} = \@cache_rv 394 unless UNIVERSAL::isa($cache_rv[0],'DBI::st'); # XXX cloning sth not yet done 395 } 396 397 return (wantarray) ? @$rv : $rv->[0]; 398 }; 399 no strict 'refs'; 400 *$method = $sub; 401 } 402 403 404 # Methods that can use the DBI defaults for some situations/drivers 405 for my $method (qw( 406 quote quote_identifier 407 )) { # XXX keep DBD::Gofer::Policy::Base in sync 408 my $policy_name = "locally_$method"; 409 my $super_name = "SUPER::$method"; 410 my $sub = sub { 411 my $dbh = shift; 412 413 # if we know the remote side doesn't override the DBI's default method 414 # then we might as well just call the DBI's default method on the client 415 # (which may, in turn, call other methods that are forwarded, like get_info) 416 if ($dbh->{dbi_default_methods}{$method} && $dbh->{go_policy}->skip_default_methods()) { 417 $dbh->trace_msg(" !! $method: using local default as remote method is also default\n"); 418 return $dbh->$super_name(@_); 419 } 420 421 # false: use remote gofer 422 # 1: use local DBI default method 423 # code ref: use the code ref 424 my $locally = $dbh->{go_policy}->$policy_name($dbh, @_); 425 if ($locally) { 426 return $locally->($dbh, @_) if ref $locally eq 'CODE'; 427 return $dbh->$super_name(@_); 428 } 429 return $dbh->go_dbh_method(undef, $method, @_); # propagate context 430 }; 431 no strict 'refs'; 432 *$method = $sub; 433 } 434 435 436 # Methods that should always fail 437 for my $method (qw( 438 begin_work commit rollback 439 )) { 440 no strict 'refs'; 441 *$method = sub { return shift->set_err($DBI::stderr, "$method not available with DBD::Gofer") } 442 } 443 444 445 sub do { 446 my ($dbh, $sql, $attr, @args) = @_; 447 delete $dbh->{Statement}; # avoid "Modification of non-creatable hash value attempted" 448 $dbh->{Statement} = $sql; # for profiling and ShowErrorStatement 449 my $meta = { go_last_insert_id_args => $attr->{go_last_insert_id_args} }; 450 return $dbh->go_dbh_method($meta, 'do', $sql, $attr, @args); 451 } 452 453 sub ping { 454 my $dbh = shift; 455 return $dbh->set_err(0, "can't ping while not connected") # warning 456 unless $dbh->SUPER::FETCH('Active'); 457 my $skip_ping = $dbh->{go_policy}->skip_ping(); 458 return ($skip_ping) ? 1 : $dbh->go_dbh_method(undef, 'ping', @_); 459 } 460 461 sub last_insert_id { 462 my $dbh = shift; 463 my $response = $dbh->{go_response} or return undef; 464 return $response->last_insert_id; 465 } 466 467 sub FETCH { 468 my ($dbh, $attrib) = @_; 469 470 # FETCH is effectively already cached because the DBI checks the 471 # attribute cache in the handle before calling FETCH 472 # and this FETCH copies the value into the attribute cache 473 474 # forward driver-private attributes (except ours) 475 if ($attrib =~ m/^[a-z]/ && $attrib !~ /^go_/) { 476 my $value = $dbh->go_dbh_method(undef, 'FETCH', $attrib); 477 $dbh->{$attrib} = $value; # XXX forces caching by DBI 478 return $dbh->{$attrib} = $value; 479 } 480 481 # else pass up to DBI to handle 482 return $dbh->SUPER::FETCH($attrib); 483 } 484 485 sub STORE { 486 my ($dbh, $attrib, $value) = @_; 487 if ($attrib eq 'AutoCommit') { 488 croak "Can't enable transactions when using DBD::Gofer" if !$value; 489 return $dbh->SUPER::STORE($attrib => ($value) ? -901 : -900); 490 } 491 return $dbh->SUPER::STORE($attrib => $value) 492 # we handle this attribute locally 493 if $dbh_local_store_attrib{$attrib} 494 # or it's a private_ (application) attribute 495 or $attrib =~ /^private_/ 496 # or not yet connected (ie being called by DBI->connect) 497 or not $dbh->FETCH('Active'); 498 499 return $dbh->SUPER::STORE($attrib => $value) 500 if $DBD::Gofer::xxh_local_store_attrib_if_same_value{$attrib} 501 && do { # values are the same 502 my $crnt = $dbh->FETCH($attrib); 503 local $^W; 504 (defined($value) ^ defined($crnt)) 505 ? 0 # definedness differs 506 : $value eq $crnt; 507 }; 508 509 # dbh attributes are set at connect-time - see connect() 510 carp("Can't alter \$dbh->{$attrib} after handle created with DBD::Gofer") if $dbh->FETCH('Warn'); 511 return $dbh->set_err($DBI::stderr, "Can't alter \$dbh->{$attrib} after handle created with DBD::Gofer"); 512 } 513 514 sub disconnect { 515 my $dbh = shift; 516 $dbh->{go_transport} = undef; 517 $dbh->STORE(Active => 0); 518 } 519 520 sub prepare { 521 my ($dbh, $statement, $attr)= @_; 522 523 return $dbh->set_err($DBI::stderr, "Can't prepare when disconnected") 524 unless $dbh->FETCH('Active'); 525 526 $attr = { %$attr } if $attr; # copy so we can edit 527 528 my $policy = delete($attr->{go_policy}) || $dbh->{go_policy}; 529 my $lii_args = delete $attr->{go_last_insert_id_args}; 530 my $go_prepare = delete($attr->{go_prepare_method}) 531 || $dbh->{go_prepare_method} 532 || $policy->prepare_method($dbh, $statement, $attr) 533 || 'prepare'; # e.g. for code not using placeholders 534 my $go_cache = delete $attr->{go_cache}; 535 # set to undef if there are no attributes left for the actual prepare call 536 $attr = undef if $attr and not %$attr; 537 538 my ($sth, $sth_inner) = DBI::_new_sth($dbh, { 539 Statement => $statement, 540 go_prepare_call => [ 0, $go_prepare, $statement, $attr ], 541 # go_method_calls => [], # autovivs if needed 542 go_request => $dbh->{go_request}, 543 go_transport => $dbh->{go_transport}, 544 go_policy => $policy, 545 go_last_insert_id_args => $lii_args, 546 go_cache => $go_cache, 547 }); 548 $sth->STORE(Active => 0); 549 550 my $skip_prepare_check = $policy->skip_prepare_check($attr, $dbh, $statement, $attr, $sth); 551 if (not $skip_prepare_check) { 552 $sth->go_sth_method() or return undef; 553 } 554 555 return $sth; 556 } 557 558 sub prepare_cached { 559 my ($dbh, $sql, $attr, $if_active)= @_; 560 $attr ||= {}; 561 return $dbh->SUPER::prepare_cached($sql, { 562 %$attr, 563 go_prepare_method => $attr->{go_prepare_method} || 'prepare_cached', 564 }, $if_active); 565 } 566 567 *go_cache = \&DBD::Gofer::go_cache; 568 } 569 570 571 { package DBD::Gofer::st; # ====== STATEMENT ====== 572 $imp_data_size = 0; 573 use strict; 574 575 my %sth_local_store_attrib = (%DBD::Gofer::xxh_local_store_attrib, NUM_OF_FIELDS => 1); 576 577 sub go_sth_method { 578 my ($sth, $meta) = @_; 579 580 if (my $ParamValues = $sth->{ParamValues}) { 581 my $ParamAttr = $sth->{ParamAttr}; 582 # XXX the sort here is a hack to work around a DBD::Sybase bug 583 # but only works properly for params 1..9 584 # (reverse because of the unshift) 585 my @params = reverse sort keys %$ParamValues; 586 if (@params > 9 && ($sth->{Database}{go_dsn}||'') =~ /dbi:Sybase/) { 587 # if more than 9 then we need to do a proper numeric sort 588 # also warn to alert user of this issue 589 warn "Sybase param binding order hack in use"; 590 @params = sort { $b <=> $a } @params; 591 } 592 for my $p (@params) { 593 # unshift to put binds before execute call 594 unshift @{ $sth->{go_method_calls} }, 595 [ 'bind_param', $p, $ParamValues->{$p}, $ParamAttr->{$p} ]; 596 } 597 } 598 599 my $dbh = $sth->{Database} or die "panic"; 600 ++$dbh->{go_request_count}; 601 602 my $request = $sth->{go_request}; 603 $request->init_request($sth->{go_prepare_call}, $sth); 604 $request->sth_method_calls(delete $sth->{go_method_calls}) 605 if $sth->{go_method_calls}; 606 $request->sth_result_attr({}); # (currently) also indicates this is an sth request 607 608 $request->dbh_last_insert_id_args($meta->{go_last_insert_id_args}) 609 if $meta->{go_last_insert_id_args}; 610 611 my $go_policy = $sth->{go_policy}; 612 my $dbh_attribute_update = $go_policy->dbh_attribute_update(); 613 $request->dbh_attributes( $go_policy->dbh_attribute_list() ) 614 if $dbh_attribute_update eq 'every' 615 or $dbh->{go_request_count}==1; 616 617 my $transport = $sth->{go_transport} 618 or return $sth->set_err($DBI::stderr, "Not connected (no transport)"); 619 620 local $transport->{go_cache} = $sth->{go_cache} 621 if defined $sth->{go_cache}; 622 623 my ($response, $retransmit_sub) = $transport->transmit_request($request); 624 $response ||= $transport->receive_response($request, $retransmit_sub); 625 $sth->{go_response} = $response 626 or die "No response object returned by $transport"; 627 $dbh->{go_response} = $response; # mainly for last_insert_id 628 629 if (my $dbh_attributes = $response->dbh_attributes) { 630 # XXX we don't STORE here, we just stuff the value into the attribute cache 631 $dbh->{$_} = $dbh_attributes->{$_} 632 for keys %$dbh_attributes; 633 # record the values returned, so we know that we have fetched 634 # values are which we have fetched (see dbh->FETCH method) 635 $dbh->{go_dbh_attributes_fetched} = $dbh_attributes; 636 } 637 638 my $rv = $response->rv; # may be undef on error 639 if ($response->sth_resultsets) { 640 # setup first resultset - including sth attributes 641 $sth->more_results; 642 } 643 else { 644 $sth->STORE(Active => 0); 645 $sth->{go_rows} = $rv; 646 } 647 # set error/warn/info (after more_results as that'll clear err) 648 DBD::Gofer::set_err_from_response($sth, $response); 649 650 return $rv; 651 } 652 653 654 sub bind_param { 655 my ($sth, $param, $value, $attr) = @_; 656 $sth->{ParamValues}{$param} = $value; 657 $sth->{ParamAttr}{$param} = $attr 658 if defined $attr; # attr is sticky if not explicitly set 659 return 1; 660 } 661 662 663 sub execute { 664 my $sth = shift; 665 $sth->bind_param($_, $_[$_-1]) for (1..@_); 666 push @{ $sth->{go_method_calls} }, [ 'execute' ]; 667 my $meta = { go_last_insert_id_args => $sth->{go_last_insert_id_args} }; 668 return $sth->go_sth_method($meta); 669 } 670 671 672 sub more_results { 673 my $sth = shift; 674 675 $sth->finish; 676 677 my $response = $sth->{go_response} or do { 678 # e.g., we haven't sent a request yet (ie prepare then more_results) 679 $sth->trace_msg(" No response object present", 3); 680 return; 681 }; 682 683 my $resultset_list = $response->sth_resultsets 684 or return $sth->set_err($DBI::stderr, "No sth_resultsets"); 685 686 my $meta = shift @$resultset_list 687 or return undef; # no more result sets 688 #warn "more_results: ".Data::Dumper::Dumper($meta); 689 690 # pull out the special non-atributes first 691 my ($rowset, $err, $errstr, $state) 692 = delete @{$meta}{qw(rowset err errstr state)}; 693 694 # copy meta attributes into attribute cache 695 my $NUM_OF_FIELDS = delete $meta->{NUM_OF_FIELDS}; 696 $sth->STORE('NUM_OF_FIELDS', $NUM_OF_FIELDS); 697 # XXX need to use STORE for some? 698 $sth->{$_} = $meta->{$_} for keys %$meta; 699 700 if (($NUM_OF_FIELDS||0) > 0) { 701 $sth->{go_rows} = ($rowset) ? @$rowset : -1; 702 $sth->{go_current_rowset} = $rowset; 703 $sth->{go_current_rowset_err} = [ $err, $errstr, $state ] 704 if defined $err; 705 $sth->STORE(Active => 1) if $rowset; 706 } 707 708 return $sth; 709 } 710 711 712 sub go_clone_sth { 713 my ($sth1) = @_; 714 # clone an (un-fetched-from) sth - effectively undoes the initial more_results 715 # not 100% so just for use in caching returned sth e.g. table_info 716 my $sth2 = $sth1->{Database}->prepare($sth1->{Statement}, { go_skip_prepare_check => 1 }); 717 $sth2->STORE($_, $sth1->{$_}) for qw(NUM_OF_FIELDS Active); 718 my $sth2_inner = tied %$sth2; 719 $sth2_inner->{$_} = $sth1->{$_} for qw(NUM_OF_PARAMS FetchHashKeyName); 720 die "not fully implemented yet"; 721 return $sth2; 722 } 723 724 725 sub fetchrow_arrayref { 726 my ($sth) = @_; 727 my $resultset = $sth->{go_current_rowset} || do { 728 # should only happen if fetch called after execute failed 729 my $rowset_err = $sth->{go_current_rowset_err} 730 || [ 1, 'no result set (did execute fail)' ]; 731 return $sth->set_err( @$rowset_err ); 732 }; 733 return $sth->_set_fbav(shift @$resultset) if @$resultset; 734 $sth->finish; # no more data so finish 735 return undef; 736 } 737 *fetch = \&fetchrow_arrayref; # alias 738 739 740 sub fetchall_arrayref { 741 my ($sth, $slice, $max_rows) = @_; 742 my $resultset = $sth->{go_current_rowset} || do { 743 # should only happen if fetch called after execute failed 744 my $rowset_err = $sth->{go_current_rowset_err} 745 || [ 1, 'no result set (did execute fail)' ]; 746 return $sth->set_err( @$rowset_err ); 747 }; 748 my $mode = ref($slice) || 'ARRAY'; 749 return $sth->SUPER::fetchall_arrayref($slice, $max_rows) 750 if ref($slice) or defined $max_rows; 751 $sth->finish; # no more data after this so finish 752 return $resultset; 753 } 754 755 756 sub rows { 757 return shift->{go_rows}; 758 } 759 760 761 sub STORE { 762 my ($sth, $attrib, $value) = @_; 763 764 return $sth->SUPER::STORE($attrib => $value) 765 if $sth_local_store_attrib{$attrib} # handle locally 766 # or it's a private_ (application) attribute 767 or $attrib =~ /^private_/; 768 769 # otherwise warn but do it anyway 770 # this will probably need refining later 771 my $msg = "Altering \$sth->{$attrib} won't affect proxied handle"; 772 Carp::carp($msg) if $sth->FETCH('Warn'); 773 774 # XXX could perhaps do 775 # push @{ $sth->{go_method_calls} }, [ 'STORE', $attrib, $value ] 776 # if not $sth->FETCH('Executed'); 777 # but how to handle repeat executions? How to we know when an 778 # attribute is being set to affect the current resultset or the 779 # next execution? 780 # Could just always use go_method_calls I guess. 781 782 # do the store locally anyway, just in case 783 $sth->SUPER::STORE($attrib => $value); 784 785 return $sth->set_err($DBI::stderr, $msg); 786 } 787 788 # sub bind_param_array 789 # we use DBI's default, which sets $sth->{ParamArrays}{$param} = $value 790 # and calls bind_param($param, undef, $attr) if $attr. 791 792 sub execute_array { 793 my $sth = shift; 794 my $attr = shift; 795 $sth->bind_param_array($_, $_[$_-1]) for (1..@_); 796 push @{ $sth->{go_method_calls} }, [ 'execute_array', $attr ]; 797 return $sth->go_sth_method($attr); 798 } 799 800 *go_cache = \&DBD::Gofer::go_cache; 801 } 802 803 1; 804 805 __END__ 806 807 =head1 NAME 808 809 DBD::Gofer - A stateless-proxy driver for communicating with a remote DBI 810 811 =head1 SYNOPSIS 812 813 use DBI; 814 815 $original_dsn = "dbi:..."; # your original DBI Data Source Name 816 817 $dbh = DBI->connect("dbi:Gofer:transport=$transport;...;dsn=$original_dsn", 818 $user, $passwd, \%attributes); 819 820 ... use $dbh as if it was connected to $original_dsn ... 821 822 823 The C<transport=$transport> part specifies the name of the module to use to 824 transport the requests to the remote DBI. If $transport doesn't contain any 825 double colons then it's prefixed with C<DBD::Gofer::Transport::>. 826 827 The C<dsn=$original_dsn> part I<must be the last element> of the DSN because 828 everything after C<dsn=> is assumed to be the DSN that the remote DBI should 829 use. 830 831 The C<...> represents attributes that influence the operation of the Gofer 832 driver or transport. These are described below or in the documentation of the 833 transport module being used. 834 835 =head1 DESCRIPTION 836 837 DBD::Gofer is a DBI database driver that forwards requests to another DBI 838 driver, usually in a seperate process, often on a separate machine. It tries to 839 be as transparent as possible so it appears that you are using the remote 840 driver directly. 841 842 DBD::Gofer is very similar to DBD::Proxy. The major difference is that with 843 DBD::Gofer no state is maintained on the remote end. That means every 844 request contains all the information needed to create the required state. (So, 845 for example, every request includes the DSN to connect to.) Each request can be 846 sent to any available server. The server executes the request and returns a 847 single response that includes all the data. 848 849 This is very similar to the way http works as a stateless protocol for the web. 850 Each request from your web browser can be handled by a different web server process. 851 852 =head2 Use Cases 853 854 This may seem like pointless overhead but there are situations where this is a 855 very good thing. Let's consider a specific case. 856 857 Imagine using DBD::Gofer with an http transport. Your application calls 858 connect(), prepare("select * from table where foo=?"), bind_param(), and execute(). 859 At this point DBD::Gofer builds a request containing all the information 860 about the method calls. It then uses the httpd transport to send that request 861 to an apache web server. 862 863 This 'dbi execute' web server executes the request (using DBI::Gofer::Execute 864 and related modules) and builds a response that contains all the rows of data, 865 if the statement returned any, along with all the attributes that describe the 866 results, such as $sth->{NAME}. This response is sent back to DBD::Gofer which 867 unpacks it and presents it to the application as if it had executed the 868 statement itself. 869 870 =head2 Advantages 871 872 Okay, but you still don't see the point? Well let's consider what we've gained: 873 874 =head3 Connection Pooling and Throttling 875 876 The 'dbi execute' web server leverages all the functionality of web 877 infrastructure in terms of load balancing, high-availability, firewalls, access 878 management, proxying, caching. 879 880 At its most basic level you get a configurable pool of persistent database connections. 881 882 =head3 Simple Scaling 883 884 Got thousands of processes all trying to connect to the database? You can use 885 DBD::Gofer to connect them to your smaller pool of 'dbi execute' web servers instead. 886 887 =head3 Caching 888 889 Client-side caching is as simple as adding "C<cache=1>" to the DSN. 890 This feature alone can be worth using DBD::Gofer for. 891 892 =head3 Fewer Network Round-trips 893 894 DBD::Gofer sends as few requests as possible (dependent on the policy being used). 895 896 =head3 Thin Clients / Unsupported Platforms 897 898 You no longer need drivers for your database on every system. DBD::Gofer is pure perl. 899 900 =head1 CONSTRAINTS 901 902 There are some natural constraints imposed by the DBD::Gofer 'stateless' approach. 903 But not many: 904 905 =head2 You can't change database handle attributes after connect() 906 907 You can't change database handle attributes after you've connected. 908 Use the connect() call to specify all the attribute settings you want. 909 910 This is because it's critical that when a request is complete the database 911 handle is left in the same state it was when first connected. 912 913 An exception is made for attributes with names starting "C<private_>": 914 They can be set after connect() but the change is only applied locally. 915 916 =head2 You can't change statement handle attributes after prepare() 917 918 You can't change statment handle attributes after prepare. 919 920 An exception is made for attributes with names starting "C<private_>": 921 They can be set after prepare() but the change is only applied locally. 922 923 =head2 You can't use transactions 924 925 AutoCommit only. Transactions aren't supported. 926 927 (In theory transactions could be supported when using a transport that 928 maintains a connection, like C<stream> does. If you're interested in this 929 please get in touch via dbi-dev@perl.org) 930 931 =head2 You can't call driver-private sth methods 932 933 But that's rarely needed anyway. 934 935 =head1 GENERAL CAVEATS 936 937 A few important things to keep in mind when using DBD::Gofer: 938 939 =head2 Temporary tables, locks, and other per-connection persistent state 940 941 You shouldn't expect any per-session state to persist between requests. 942 This includes locks and temporary tables. 943 944 Because the server-side may execute your requests via a different 945 database connections, you can't rely on any per-connection persistent state, 946 such as temporary tables, being available from one request to the next. 947 948 This is an easy trap to fall into. A good way to check for this is to test your 949 code with a Gofer policy package that sets the C<connect_method> policy to 950 'connect' to force a new connection for each request. The C<pedantic> policy does this. 951 952 =head2 Driver-private Database Handle Attributes 953 954 Some driver-private dbh attributes may not be available if the driver has not 955 implemented the private_attribute_info() method (added in DBI 1.54). 956 957 =head2 Driver-private Statement Handle Attributes 958 959 Driver-private sth attributes can be set in the prepare() call. TODO 960 961 Some driver-private dbh attributes may not be available if the driver has not 962 implemented the private_attribute_info() method (added in DBI 1.54). 963 964 =head2 Multiple Resultsets 965 966 Multiple resultsets are supported only if the driver supports the more_results() method 967 (an exception is made for DBD::Sybase). 968 969 =head2 Statement activity that also updates dbh attributes 970 971 Some drivers may update one or more dbh attributes after performing activity on 972 a child sth. For example, DBD::mysql provides $dbh->{mysql_insertid} in addition to 973 $sth->{mysql_insertid}. Currently mysql_insertid is supported via a hack but a 974 more general mechanism is needed for other drivers to use. 975 976 =head2 Methods that report an error always return undef 977 978 With DBD::Gofer, a method that sets an error always return an undef or empty list. 979 That shouldn't be a problem in practice because the DBI doesn't define any 980 methods that return meaningful values while also reporting an error. 981 982 =head2 Subclassing only applies to client-side 983 984 The RootClass and DbTypeSubclass attributes are not passed to the Gofer server. 985 986 =head1 CAVEATS FOR SPECIFIC METHODS 987 988 =head2 last_insert_id 989 990 To enable use of last_insert_id you need to indicate to DBD::Gofer that you'd 991 like to use it. You do that my adding a C<go_last_insert_id_args> attribute to 992 the do() or prepare() method calls. For example: 993 994 $dbh->do($sql, { go_last_insert_id_args => [...] }); 995 996 or 997 998 $sth = $dbh->prepare($sql, { go_last_insert_id_args => [...] }); 999 1000 The array reference should contains the args that you want passed to the 1001 last_insert_id() method. 1002 1003 =head2 execute_for_fetch 1004 1005 The array methods bind_param_array() and execute_array() are supported. 1006 When execute_array() is called the data is serialized and executed in a single 1007 round-trip to the Gofer server. This makes it very fast, but requires enough 1008 memory to store all the serialized data. 1009 1010 The execute_for_fetch() method currently isn't optimised, it uses the DBI 1011 fallback behaviour of executing each tuple individually. 1012 (It could be implemented as a wrapper for execute_array() - patches welcome.) 1013 1014 =head1 TRANSPORTS 1015 1016 DBD::Gofer doesn't concern itself with transporting requests and responses to and fro. 1017 For that it uses special Gofer transport modules. 1018 1019 Gofer transport modules usually come in pairs: one for the 'client' DBD::Gofer 1020 driver to use and one for the remote 'server' end. They have very similar names: 1021 1022 DBD::Gofer::Transport::<foo> 1023 DBI::Gofer::Transport::<foo> 1024 1025 Sometimes the transports on the DBD and DBI sides may have different names. For 1026 example DBD::Gofer::Transport::http is typically used with DBI::Gofer::Transport::mod_perl 1027 (DBD::Gofer::Transport::http and DBI::Gofer::Transport::mod_perl modules are 1028 part of the GoferTransport-http distribution). 1029 1030 =head2 Bundled Transports 1031 1032 Several transport modules are provided with DBD::Gofer: 1033 1034 =head3 null 1035 1036 The null transport is the simplest of them all. It doesn't actually transport the request anywhere. 1037 It just serializes (freezes) the request into a string, then thaws it back into 1038 a data structure before passing it to DBI::Gofer::Execute to execute. The same 1039 freeze and thaw is applied to the results. 1040 1041 The null transport is the best way to test if your application will work with Gofer. 1042 Just set the DBI_AUTOPROXY environment variable to "C<dbi:Gofer:transport=null;policy=pedantic>" 1043 (see L</Using DBI_AUTOPROXY> below) and run your application, or ideally its test suite, as usual. 1044 1045 It doesn't take any parameters. 1046 1047 =head3 pipeone 1048 1049 The pipeone transport launches a subprocess for each request. It passes in the 1050 request and reads the response. 1051 1052 The fact that a new subprocess is started for each request ensures that the 1053 server side is truly stateless. While this does make the transport I<very> slow, 1054 it is useful as a way to test that your application doesn't depend on 1055 per-connection state, such as temporary tables, persisting between requests. 1056 1057 It's also useful both as a proof of concept and as a base class for the stream 1058 driver. 1059 1060 =head3 stream 1061 1062 The stream driver also launches a subprocess and writes requests and reads 1063 responses, like the pipeone transport. In this case, however, the subprocess 1064 is expected to handle more that one request. (Though it will be automitically 1065 restarted if it exits.) 1066 1067 This is the first transport that is truly useful because it can launch the 1068 subprocess on a remote machine using C<ssh>. This means you can now use DBD::Gofer 1069 to easily access any databases that's accessible from any system you can login to. 1070 You also get all the benefits of ssh, including encryption and optional compression. 1071 1072 See L</Using DBI_AUTOPROXY> below for an example. 1073 1074 =head2 Other Transports 1075 1076 Implementing a Gofer transport is I<very> simple, and more transports are very welcome. 1077 Just take a look at any existing transports that are similar to your needs. 1078 1079 =head3 http 1080 1081 See the GoferTransport-http distribution on CPAN: http://search.cpan.org/dist/GoferTransport-http/ 1082 1083 =head3 Gearman 1084 1085 I know Ask Bjørn Hansen has implemented a transport for the C<gearman> distributed 1086 job system, though it's not on CPAN at the time of writing this. 1087 1088 =head1 CONNECTING 1089 1090 Simply prefix your existing DSN with "C<dbi:Gofer:transport=$transport;dsn=>" 1091 where $transport is the name of the Gofer transport you want to use (see L</TRANSPORTS>). 1092 The C<transport> and C<dsn> attributes must be specified and the C<dsn> attributes must be last. 1093 1094 Other attributes can be specified in the DSN to configure DBD::Gofer and/or the 1095 Gofer transport module being used. The main attributes after C<transport>, are 1096 C<url> and C<policy>. These and other attributes are described below. 1097 1098 =head2 Using DBI_AUTOPROXY 1099 1100 The simplest way to try out DBD::Gofer is to set the DBI_AUTOPROXY environment variable. 1101 In this case you don't include the C<dsn=> part. For example: 1102 1103 export DBI_AUTOPROXY="dbi:Gofer:transport=null" 1104 1105 or, for a more useful example, try: 1106 1107 export DBI_AUTOPROXY="dbi:Gofer:transport=stream;url=ssh:user@example.com" 1108 1109 =head2 Connection Attributes 1110 1111 These attributes can be specified in the DSN. They can also be passed in the 1112 \%attr parameter of the DBI connect method by adding a "C<go_>" prefix to the name. 1113 1114 =head3 transport 1115 1116 Specifies the Gofer transport class to use. Required. See L</TRANSPORTS> above. 1117 1118 If the value does not include C<::> then "C<DBD::Gofer::Transport::>" is prefixed. 1119 1120 The transport object can be accessed via $h->{go_transport}. 1121 1122 =head3 dsn 1123 1124 Specifies the DSN for the remote side to connect to. Required, and must be last. 1125 1126 =head3 url 1127 1128 Used to tell the transport where to connect to. The exact form of the value depends on the transport used. 1129 1130 =head3 policy 1131 1132 Specifies the policy to use. See L</CONFIGURING BEHAVIOUR POLICY>. 1133 1134 If the value does not include C<::> then "C<DBD::Gofer::Policy>" is prefixed. 1135 1136 The policy object can be accessed via $h->{go_policy}. 1137 1138 =head3 timeout 1139 1140 Specifies a timeout, in seconds, to use when waiting for responses from the server side. 1141 1142 =head3 retry_limit 1143 1144 Specifies the number of times a failed request will be retried. Default is 0. 1145 1146 =head3 retry_hook 1147 1148 Specifies a code reference to be called to decide if a failed request should be retried. 1149 The code reference is called like this: 1150 1151 $transport = $h->{go_transport}; 1152 $retry = $transport->go_retry_hook->($request, $response, $transport); 1153 1154 If it returns true then the request will be retried, upto the C<retry_limit>. 1155 If it returns a false but defined value then the request will not be retried. 1156 If it returns undef then the default behaviour will be used, as if C<retry_hook> 1157 had not been specified. 1158 1159 The default behaviour is to retry requests where $request->is_idempotent is true, 1160 or the error message matches C</induced by DBI_GOFER_RANDOM/>. 1161 1162 =head3 cache 1163 1164 Specifies that client-side caching should be performed. The value is the name 1165 of a cache class to use. 1166 1167 Any class implementing get($key) and set($key, $value) methods can be used. 1168 That includes a great many powerful caching classes on CPAN, including the 1169 Cache and Cache::Cache distributions. 1170 1171 You can use "C<cache=1>" is a shortcut for "C<cache=DBI::Util::CacheMemory>". 1172 See L<DBI::Util::CacheMemory> for a description of this simple fast default cache. 1173 1174 The cache object can be accessed via $h->go_cache. For example: 1175 1176 $dbh->go_cache->clear; # free up memory being used by the cache 1177 1178 The cache keys are the frozen (serialized) requests, and the values are the 1179 frozen responses. 1180 1181 The default behaviour is to only use the cache for requests where 1182 $request->is_idempotent is true (i.e., the dbh has the ReadOnly attribute set 1183 or the SQL statement is obviously a SELECT without a FOR UPDATE clause.) 1184 1185 For even more control you can use the C<go_cache> attribute to pass in an 1186 instanciated cache object. Individual methods, including prepare(), can also 1187 specify alternative caches via the C<go_cache> attribute. For example, to 1188 specify no caching for a particular query, you could use 1189 1190 $sth = $dbh->prepare( $sql, { go_cache => 0 } ); 1191 1192 This can be used to implement different caching policies for different statements. 1193 1194 It's interesting to note that DBD::Gofer can be used to add client-side caching 1195 to any (gofer compatible) application, with no code changes and no need for a 1196 gofer server. Just set the DBI_AUTOPROXY environment variable like this: 1197 1198 DBI_AUTOPROXY='dbi:Gofer:transport=null;cache=1' 1199 1200 =head1 CONFIGURING BEHAVIOUR POLICY 1201 1202 DBD::Gofer supports a 'policy' mechanism that allows you to fine-tune the number of round-trips to the Gofer server. 1203 The policies are grouped into classes (which may be subclassed) and referenced by the name of the class. 1204 1205 The L<DBD::Gofer::Policy::Base> class is the base class for all the policy 1206 packages and describes all the available policies. 1207 1208 Three policy packages are supplied with DBD::Gofer: 1209 1210 L<DBD::Gofer::Policy::pedantic> is most 'transparent' but slowest because it 1211 makes more round-trips to the Gofer server. 1212 1213 L<DBD::Gofer::Policy::classic> is a reasonable compromise - it's the default policy. 1214 1215 L<DBD::Gofer::Policy::rush> is fastest, but may require code changes in your applications. 1216 1217 Generally the default C<classic> policy is fine. When first testing an existing 1218 application with Gofer it is a good idea to start with the C<pedantic> policy 1219 first and then switch to C<classic> or a custom policy, for final testing. 1220 1221 1222 =head1 AUTHOR 1223 1224 Tim Bunce, L<http://www.tim.bunce.name> 1225 1226 =head1 LICENCE AND COPYRIGHT 1227 1228 Copyright (c) 2007, Tim Bunce, Ireland. All rights reserved. 1229 1230 This module is free software; you can redistribute it and/or 1231 modify it under the same terms as Perl itself. See L<perlartistic>. 1232 1233 =head1 ACKNOWLEDGEMENTS 1234 1235 The development of DBD::Gofer and related modules was sponsored by 1236 Shopzilla.com (L<http://Shopzilla.com>), where I currently work. 1237 1238 =head1 SEE ALSO 1239 1240 L<DBI::Gofer::Request>, L<DBI::Gofer::Response>, L<DBI::Gofer::Execute>. 1241 1242 L<DBI::Gofer::Transport::Base>, L<DBD::Gofer::Policy::Base>. 1243 1244 L<DBI> 1245 1246 =head1 Caveats for specific drivers 1247 1248 This section aims to record issues to be aware of when using Gofer with specific drivers. 1249 It usually only documents issues that are not natural consequences of the limitations 1250 of the Gofer approach - as documented avove. 1251 1252 =head1 TODO 1253 1254 This is just a random brain dump... (There's more in the source of the Changes file, not the pod) 1255 1256 Document policy mechanism 1257 1258 Add mechanism for transports to list config params and for Gofer to apply any that match (and warn if any left over?) 1259 1260 Driver-private sth attributes - set via prepare() - change DBI spec 1261 1262 add hooks into transport base class for checking & updating a result set cache 1263 ie via a standard cache interface such as: 1264 http://search.cpan.org/~robm/Cache-FastMmap/FastMmap.pm 1265 http://search.cpan.org/~bradfitz/Cache-Memcached/lib/Cache/Memcached.pm 1266 http://search.cpan.org/~dclinton/Cache-Cache/ 1267 http://search.cpan.org/~cleishman/Cache/ 1268 Also caching instructions could be passed through the httpd transport layer 1269 in such a way that appropriate http cache headers are added to the results 1270 so that web caches (squid etc) could be used to implement the caching. 1271 (MUST require the use of GET rather than POST requests.) 1272 1273 Rework handling of installed_methods to not piggback on dbh_attributes? 1274 1275 Perhaps support transactions for transports where it's possible (ie null and stream)? 1276 Would make stream transport (ie ssh) more useful to more people. 1277 1278 Make sth_result_attr more like dbh_attributes (using '*' etc) 1279 1280 Add @val = FETCH_many(@names) to DBI in C and use in Gofer/Execute? 1281 1282 Implement _new_sth in C. 1283 1284 =cut
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Tue Mar 17 22:47:18 2015 | Cross-referenced by PHPXref 0.7.1 |