File Coverage

blib/lib/DBD/Multiplex.pm
Criterion Covered Total %
statement 25 251 9.9
branch 0 114 0.0
condition 0 37 0.0
subroutine 8 29 27.5
pod 0 5 0.0
total 33 436 7.5


line stmt bran cond sub pod time code
1             #########1#########2#########3#########4#########5#########6#########7#########8
2             # vim: ts=8:sw=4
3             #
4             # $Id: Multiplex.pm,v 2.11 2002/11/11 00:01:01 timbo Exp $
5             #
6             # Copyright (c) 1999,2008 Tim Bunce & Thomas Kishel
7             #
8             # You may distribute under the terms of either the GNU General Public
9             # License or the Artistic License, as specified in the Perl README file.
10              
11             { #=================================================================== DBD ===
12              
13             package DBD::Multiplex;
14              
15 1     1   118036 use DBI;
  1         2  
  1         45  
16              
17             @EXPORT = ();
18              
19 1     1   5 use strict;
  1         1  
  1         39  
20 1     1   4 use vars qw($VERSION $drh $err $errstr $sqlstate);
  1         6  
  1         2274  
21              
22             $VERSION = sprintf("%d.%02d", q$Revision: 2.11 $ =~ /(\d+)\.(\d+)/o);
23              
24             $drh = undef; # Holds driver handle once it has been initialized.
25             $err = 0; # Holds error code for $DBI::err.
26             $errstr = ''; # Holds error string for $DBI::errstr.
27             $sqlstate = ''; # Holds SQL state for $DBI::state.
28              
29             #########################################
30             # The driver handle constructor.
31             #########################################
32              
33             sub driver {
34 0 0   0 0   return $drh if ($drh);
35 0           my ($class, $attr) = @_;
36              
37 0           $class .= "::dr";
38            
39             # $drh is not scoped with 'my',
40             # since we use it above to prevent multiple drivers.
41            
42 0           ($drh) = DBI::_new_drh ($class, {
43             'Name' => 'Multiplex',
44             'Version' => $VERSION,
45             'Attribution' => 'DBD Multiplex by Tim Bunce && Thomas Kishel',
46             });
47            
48 0           return $drh;
49             }
50              
51             ########################################
52             # Function for calling a method for each child handle of a parent handle.
53             # The parent handle is one of 'our' database or statement handles.
54             # Each of the child handles is a 'native' database or statement handle.
55             # -- called inside AUTOLOAD --
56             ########################################
57              
58             sub mx_method_all {
59             # Remember that shift modifies the parameter list.
60 0     0 0   my ($method, $parent_handle) = (shift, shift);
61              
62 0           my ($exit_mode, %modes, %multiplex_options, $results, $errors);
63 0           my ($return_result, @return_results);
64            
65 0           $exit_mode = $parent_handle->{'mx_exit_mode'};
66              
67             # TK Note:
68             # do() is a method of a database handle, not a statement handle.
69 0 0 0       if ($method eq 'do' or $method eq 'disconnect') {
70 0           delete $parent_handle->{'Statement'}; # PD
71 0           $parent_handle->{'Statement'} = $_[0];
72             }
73            
74             # Override both the default exit_mode,
75             # and the exit_mode attribute stored in the parent handle,
76             # when multiplexing the following:
77             %modes = (
78 0           'STORE' => 'first_error',
79             'FETCH' => 'first_error',
80             'finish' => 'last_result',
81             'disconnect' => 'last_result'
82             );
83              
84 0 0         $exit_mode = $modes{$method} if ($modes{$method});
85              
86 0           %multiplex_options = ('parent_handle' => $parent_handle, 'exit_mode' => $exit_mode);
87            
88 0           ($results, $errors) = &DBD::Multiplex::mx_do_calls ($method, wantarray, \%multiplex_options, @_);
89              
90             # find first defined result
91 0           for (@$results) {
92 0           $return_result = $_;
93 0 0         last if defined $return_result->[0];
94             }
95              
96             # return first defined result
97 0 0         return $return_result->[0] unless wantarray;
98            
99             # The context of fetchrow_array (and selectrow_array ?) is wantarray,
100             # and therefore cannot be distinguished from a multiplexed scalar context.
101             # In this case, returning an array of results from multiple handles is incorrect.
102 0 0         if ( $method eq 'fetchrow_array' ) {
103 0           return @$return_result;
104             }
105            
106             # find all defined results
107 0           for (@$results) {
108 0 0         push(@return_results, $_->[0]) if defined $_->[0];
109             }
110              
111             # return all defined results
112 0           return @return_results;
113             }
114              
115             ########################################
116             # 'Bottom-level' support function to multiplex the calls.
117             # See the documentation for information about $exit_mode.
118             # Currently the 'last_result' exit_mode is automagic.
119             ########################################
120              
121             sub mx_do_calls {
122             # Remember that shift modifies the parameter list.
123 0     0 0   my ($method, $wantarray, $multiplex_options) = (shift, shift, shift);
124              
125             # @errors is a sparse array paralleling $results[0..n]
126 0           my ($parent_handle, $parent_handle_list, $parent_name_list, $parent_id_list);
127 0           my ($error_proc, $exit_mode);
128 0           my ($child_handle, $child_number, @results, @errors);
129 0           my ($child_err, $child_errstr, $statement);
130              
131 0   0       $parent_handle = $multiplex_options->{'parent_handle'} || die;
132 0   0       $parent_handle_list = $parent_handle->{'mx_handle_list'} || die;
133 0           $parent_name_list = $parent_handle->{'mx_name_list'};
134 0           $parent_id_list = $parent_handle->{'mx_id_list'};
135 0   0       $exit_mode = $multiplex_options->{'exit_mode'} || 'first_error';
136 0           $error_proc = $parent_handle->{'mx_error_proc'};
137              
138 0 0         $parent_handle->trace_msg("mx_do_calls $method for " . join(", ", map{defined $_?$_:''} @$parent_handle_list) . "\n");
  0            
139              
140 0           $child_number = 0;
141 0           $statement = $parent_handle->{'Statement'};
142              
143             # EP Note:
144             # If master dsn is specified, and current statement is a
145             # modification operation, make sure this is done on the master:
146             #
147             # If ($method eq 'do' || $method eq 'execute' and
148             # the above condition is wrong, because then _any_ prepare()
149             # will definitely go to second condition.
150              
151             # RR Note:
152             # Transactions are run only on the master.
153              
154 0 0 0       if ( $parent_handle->{'mx_master_id'} &&
      0        
155             ( &DBD::Multiplex::mx_is_modify_statement(\$statement) || (! $parent_handle->{'AutoCommit'}) )
156             ) {
157            
158             # TK Note:
159             # Loop to find the master handle.
160             # Consider finding once and storing rather than finding each time.
161 0           for (@$parent_id_list) {
162 0 0         last if $_ eq $parent_handle->{'mx_master_id'};
163 0           push @results, [undef];
164 0           $child_number++;
165             }
166            
167 0 0         if ($statement) {
168 0           $parent_handle->trace_msg("mx_do_calls $method for statement $statement against child number $child_number\n");
169             }
170              
171 0           $child_handle = $parent_handle_list->[$child_number];
172 0 0         $results[$child_number] = ($wantarray)
173             ? [ $child_handle->$method(@_) ]
174             : [ scalar $child_handle->$method(@_) ];
175              
176 0 0         if ($child_err = $child_handle->err) {
177 0           $child_errstr = $child_handle->errstr;
178 0           $errors[$child_number] = [$child_err, $child_errstr];
179 0 0         if ($parent_handle) {
180 0           &DBI::set_err($parent_handle, $child_err, $child_errstr);
181             }
182 0 0         if ($error_proc) {
183 0           $error_proc->(${$parent_name_list}[$child_number], ${$parent_id_list}[$child_number], $child_err, $child_errstr);
  0            
  0            
184             }
185             }
186              
187             } else {
188              
189 0           foreach $child_handle (@$parent_handle_list) {
190              
191 0 0         if ($statement) {
192 0           $parent_handle->trace_msg("mx_do_calls $method for statement $statement against child number $child_number\n") ;
193             }
194            
195             # Here, the actual method being multiplexed is being called.
196 0 0         push @results, ($wantarray)
197             ? [ $child_handle->$method(@_) ]
198             : [ scalar $child_handle->$method(@_) ];
199              
200 0 0         if ($child_err = $child_handle->err) {
201 0           $child_errstr = $child_handle->errstr;
202 0           $errors[@results - 1] = [$child_err, $child_errstr];
203 0 0         if ($parent_handle) {
204 0           &DBI::set_err($parent_handle, $child_err, $child_errstr);
205             }
206 0 0         if ($error_proc) {
207 0           $error_proc->(${$parent_name_list}[$child_number], ${$parent_id_list}[$child_number], $child_err, $child_errstr);
  0            
  0            
208             }
209 0 0         last if ($exit_mode eq 'first_error');
210             } else {
211 0 0         last if ($exit_mode eq 'first_success');
212             }
213              
214 0           $child_number = $child_number + 1;
215             }
216              
217             }
218            
219 0           return (\@results, \@errors);
220             }
221              
222             ########################################
223             # Identify if the statement modifies data in the datasource.
224             # EP Added CREATE and DROP.
225             # MS #24220 One of these keywords must appear as the first word in the SQL. I believe this is what pgpool does.
226             # MS #24219 Support SELECTs that modify.
227             ########################################
228              
229             sub mx_is_modify_statement {
230 0     0 0   my ($statement) = @_;
231            
232 0           my ($rv) = 0;
233            
234             SWITCH: {
235 0 0         if (! $$statement) { $rv = 0; last SWITCH; }
  0            
  0            
  0            
236 0 0         if ($$statement =~ /^\s*INSERT\s|^\s*UPDATE\s|^\s*DELETE\s|^\s*CREATE\s|^\s*DROP\s/i) { $rv = 1; last SWITCH; }
  0            
  0            
237 0 0         if ($$statement =~ /^\s*SELECT(.*?)INTO\s/i) { $rv = 1; last SWITCH; }
  0            
  0            
238 0 0         if ($$statement =~ /^\s*SELECT(.*?)NEXTVAL|SETVAL\s/i) { $rv = 1; last SWITCH; }
  0            
  0            
239             }
240            
241 0           return $rv;
242             }
243              
244             ########################################
245             # Example error logging mechanism.
246             ########################################
247              
248             sub mx_error_subroutine {
249 0     0 0   my ($dsn, $mx_id, $error, $error_string) = @_;
250            
251 0           print STDERR "DSN: $dsn\;mx_id\=$mx_id\n";
252 0           print STDERR "ERR: $error\n";
253 0           print STDERR "ERRSTR: $error_string\n";
254            
255 0           return 1;
256             }
257              
258             } #=============================================================== END DBD ===
259              
260             { #================================================================ DRIVER ===
261              
262             package DBD::Multiplex::dr;
263             $imp_data_size = 0;
264              
265             ########################################
266             # The database handle constructor.
267             # This function cannot be called using mx_method_all.
268             # DW #11244
269             ########################################
270              
271             sub connect {
272 0     0     my ($drh, $dsn, $user, $auth, $attr) = @_;
273              
274 0           my (@dsn_list, $dbh, @mx_dsn_list, @mx_dbh_list, $mx_id, @mx_id_list);
275 0           my ($connect_mode, $stored_print_error, $exit_mode, $error_proc, $this);
276 0           my ($dsn_count, @dsn_order, $dsn_number);
277              
278             # Retrieve the DSNs and ATTRs from the $dsn parameter.
279 0           my ($dsn_attr, $dsn_attr_mx_connect_mode, $dsn_attr_exit_mode);
280 0           ($dsn, $dsn_attr) = split (/\#/, $dsn);
281              
282             # Retrieve the DSNs from the $dsn parameter.
283 0 0         @dsn_list = split (/\|/, $dsn) if defined $dsn;
284            
285             # Add the DSNs from the attribute hashref parameter.
286 0           foreach (@{$attr->{'mx_dsns'}}) {
  0            
287 0           push (@dsn_list, $_);
288             }
289 0           $dsn_count = @dsn_list;
290              
291             # Retrieve valid attributes from the $dsn parameter.
292 0 0 0       if (defined $dsn_attr && $dsn_attr =~ /;?mx_connect_mode=(\w+);?/i) {
293 0           $dsn_attr_mx_connect_mode = $1;
294 0 0         undef $dsn_attr_mx_connect_mode if (! &mx_valid_mx_connect_mode($dsn_attr_mx_connect_mode));
295             }
296 0 0 0       if (defined $dsn_attr && $dsn_attr =~ /;?mx_exit_mode=(\w+);?/i) {
297 0           $dsn_attr_exit_mode = $1;
298 0 0         undef $dsn_attr_exit_mode if (! &mx_valid_mx_exit_mode($dsn_attr_exit_mode));
299             }
300              
301             # Clear invalid attribute hashref parameters.
302 0 0         undef $attr->{'mx_connect_mode'} if (! &mx_valid_mx_connect_mode($attr->{'mx_connect_mode'}));
303 0 0         undef $attr->{'mx_exit_mode'} if (! &mx_valid_mx_exit_mode($attr->{'mx_exit_mode'}));
304 0 0         undef $attr->{'mx_error_proc'} if (! ref($attr->{'mx_error_proc'}));
305              
306             # Prefer attributes from the attribute hashref over $dsn parameters.
307 0   0       $attr->{'mx_connect_mode'} = ($attr->{'mx_connect_mode'} || $dsn_attr_mx_connect_mode);
308 0   0       $attr->{'mx_exit_mode'} = ($attr->{'mx_exit_mode'} || $dsn_attr_exit_mode);
309              
310             # connect_mode decides what to do with DBI->connect errors.
311             # exit_mode decides when to exit the foreach loop.
312             # error_proc is a code reference to execute in case of an execute error.
313 0   0       $connect_mode = ($attr->{'mx_connect_mode'} || 'report_errors');
314 0   0       $exit_mode = ($attr->{'mx_exit_mode'} || 'first_error');
315 0           $error_proc = $attr->{'mx_error_proc'};
316              
317             # 'first_success_random' exit_mode is implemented only at connect time.
318             # Afterwards, revert to 'first_success' exit_mode.
319            
320             # TK Note:
321             # Trying to implement randomness after this point fails.
322             # Consider creating a new parameter; changing parameter into a connect_mode; or rewriting randomness to work correctly at lower levels.
323 0 0         if ($exit_mode eq 'first_success_random') {
324 0           @dsn_order = &mx_rand_list($dsn_count - 1);
325 0           $attr->{'mx_exit_mode'} = 'first_success';
326             } else {
327 0           @dsn_order = (0..$dsn_count);
328             }
329            
330             # Connect to each dsn in the dsn_list.
331 0           for ($dsn_number = 0; $dsn_number < $dsn_count; $dsn_number++) {
332 0           $dsn = $dsn_list[$dsn_order[$dsn_number]];
333              
334             # Retrieve the datasource id for use by the error_proc.
335             # Remove the datasource id from the driver name.
336             # There is no standard for the text following the driver name.
337             # Each driver is free to use whatever syntax it wants.
338 0 0         if ($dsn =~ /;?mx_id=(\w+);?/i) {
339 0           $mx_id = $1;
340 0           $dsn =~ s/;?mx_id=$mx_id;?/;/;
341 0           $dsn =~ s/^;|;$//;
342             }
343            
344             # Suppress initial warnings when ignoring errors and not explicitly printing errors.
345 0           $stored_print_error = $attr->{'PrintError'};
346 0 0 0       if (($connect_mode eq 'ignore_errors') && ($stored_print_error)) {
347 0           $attr->{'PrintError'} = 0;
348             }
349              
350 0           $dbh = DBI->connect($dsn, $user, $auth, $attr);
351 0 0         if ($dbh) {
352 0           push (@mx_dsn_list, $dsn);
353 0           push (@mx_dbh_list, $dbh);
354 0           push (@mx_id_list, $mx_id);
355 0           $dbh->{'PrintError'} = $stored_print_error;
356             } else {
357             # Override the connect_mode if there is and this is the mx_master_id.
358 0 0 0       if ($attr->{'mx_master_id'} && ($attr->{'mx_master_id'} eq $mx_id)) {
    0          
359 0           return &DBI::set_err($drh, $DBI::err, $DBI::errstr);
360             } elsif ($connect_mode eq 'ignore_errors') {
361             # TK Note:
362             # Implement?
363             # &DBI::set_err($drh, $DBI::err, $DBI::errstr);
364 0 0         if ($error_proc) {
365 0           $error_proc->($dsn, $mx_id, $DBI::err, $DBI::errstr);
366             }
367             } else {
368 0           return &DBI::set_err($drh, $DBI::err, $DBI::errstr);
369             }
370             }
371            
372             }
373            
374             # Name is an array of all dsns, mx_name_list is an array of all connected dsns.
375 0           $this = DBI::_new_dbh ($drh, {
376             'Name' => [@dsn_list],
377             'User' => $user,
378             'mx_name_list' => [@mx_dsn_list],
379             'mx_handle_list' => [@mx_dbh_list],
380             'mx_id_list' => [@mx_id_list],
381             'mx_master_id' => $attr->{'mx_master_id'},
382             'mx_exit_mode' => $attr->{'mx_exit_mode'},
383             'mx_error_proc' => $error_proc,
384             });
385              
386 0           return $this;
387             }
388              
389             ########################################
390             # Required by the DBI.
391             ########################################
392              
393             sub disconnect_all {
394 0     0     undef;
395             }
396              
397             ########################################
398             # Required by the DBI.
399             ########################################
400              
401             sub DESTROY {
402 0     0     undef;
403             }
404              
405             ########################################
406             # A random list of numbers.
407             ########################################
408             sub mx_rand_list {
409 0     0     my (@input) = (0..$_[0]);
410 0           my (@output);
411            
412 0           srand(time() ^ ($$ + ($$ << 15)));
413 0           push(@output, splice (@input, rand(@input), 1)) while (@input);
414            
415 0           return @output;
416             }
417              
418             ########################################
419             # Parameter validation.
420             ########################################
421              
422             sub mx_valid_mx_connect_mode {
423 0     0     my ($mode) = @_;
424            
425 0           my (%modes);
426 0           %modes = (
427             'report_errors', => 1,
428             'ignore_errors' => 1
429             );
430            
431 0           return ($modes{$mode});
432             }
433              
434             ########################################
435             # Parameter validation.
436             ########################################
437              
438             sub mx_valid_mx_exit_mode {
439 0     0     my ($mode) = @_;
440            
441 0           my (%modes);
442 0           %modes = (
443             'first_error', => 1,
444             'first_success' => 1,
445             'first_success_random', => 1,
446             'last_result', => 1
447             );
448            
449 0           return ($modes{$mode});
450             }
451              
452             } #============================================================ END DRIVER ===
453              
454             { #============================================================== DATABASE ===
455              
456             package DBD::Multiplex::db;
457             $imp_data_size = 0;
458 1     1   7 use strict;
  1         1  
  1         434  
459              
460             ########################################
461             # The statement handle constructor.
462             # This function calls mx_do_calls and therefore cannot be called using mx_method_all.
463             # TK Note:
464             # Consider the interaction between do, prepare, execute, and mx_error_proc.
465             ########################################
466              
467             sub prepare {
468             # Remember that shift modifies the parameter list.
469 0     0     my ($dbh) = shift;
470 0           my ($statement, $attr) = @_;
471              
472 0           my ($parent_name_list, $parent_id_list, $parent_master_id, $parent_error_proc, $parent_exit_mode);
473 0           my ($exit_mode, %multiplex_options, $results, $errors, $outer, $sth);
474              
475 0           $parent_name_list = $dbh->{'mx_name_list'};
476 0           $parent_id_list = $dbh->{'mx_id_list'};
477 0           $parent_master_id = $dbh->{'mx_master_id'};
478 0           $parent_exit_mode = $dbh->{'mx_exit_mode'};
479 0           $parent_error_proc = $dbh->{'mx_error_proc'};
480              
481             # The user can set the exit_mode of a new or existing database handle.
482             # Otherwise, parse the SQL statement to determine the exit_mode.
483 0 0         if ($parent_exit_mode) {
484 0           $exit_mode = $parent_exit_mode;
485             } else {
486 0           $exit_mode = &DBD::Multiplex::db::mx_default_statement_mode(\$statement);
487             }
488              
489             # Don't forget this!
490 0           delete $dbh->{Statement}; # PD
491 0           $dbh->{'Statement'} = $statement;
492              
493 0           %multiplex_options = ('parent_handle' => $dbh, 'exit_mode' => $exit_mode);
494              
495 0           ($results, $errors) = &DBD::Multiplex::mx_do_calls ('prepare', wantarray, \%multiplex_options, @_);
496              
497 0 0         return if (@$errors);
498              
499             # Assign the @results of the multiple prepare calls,
500             # executed against each of the $dbh's children handles,
501             # to an array of children stored in the statement handle.
502             # $sth is a reference to the inner hash (used by the driver).
503             # $outer is a reference to the outer hash (used by the user of the DBI).
504 0           ($outer, $sth) = DBI::_new_sth ($dbh, {
505             'Statement' => $statement,
506 0           'mx_handle_list' => [map {$_->[0]} @$results],
507             'mx_name_list' => $parent_name_list,
508             'mx_id_list' => $parent_id_list,
509             'mx_master_id' => $parent_master_id,
510             'mx_exit_mode' => $parent_exit_mode,
511             'mx_error_proc' => $parent_error_proc,
512             });
513              
514 0           return $outer;
515             }
516              
517              
518             ########################################
519             # Some attributes are stored in the parent handle.
520             # some in each of the children handles.
521             # This function uses and therefore cannot be called using mx_method_all.
522             ########################################
523              
524             sub STORE {
525 0     0     my ($dbh, $attr, $val) = @_;
526              
527 0 0         if ($attr =~ /^mx_(.+)/) {
528 0 0         if ($1 eq uc($1)) {
529 0           return $dbh->SUPER::STORE($attr, $val);
530             } else {
531 0           return $dbh->{$attr} = $val;
532             }
533             }
534              
535             # Store the attribute in each of the children handles.
536 0           return &DBD::Multiplex::mx_method_all('STORE', @_);
537             }
538              
539             ########################################
540             # Some attributes are stored in the parent handle.
541             # some in each of the children handles.
542             # This function uses and therefore cannot be called using mx_method_all.
543             ########################################
544              
545             sub FETCH {
546 0     0     my ($dbh, $attr) = @_;
547              
548 0 0         if ($attr =~ /^mx_(.+)/) {
549 0 0         if ($1 eq uc($1)) {
550 0           return $dbh->SUPER::FETCH($attr);
551             } else {
552 0           return $dbh->{$attr};
553             }
554             }
555              
556             # Fetch the attribute from one of the children handles.
557 0           return &DBD::Multiplex::mx_method_all('FETCH', @_);
558             }
559              
560             ########################################
561             # Required by the DBI.
562             ########################################
563              
564             sub DESTROY {
565 0     0     undef;
566             }
567              
568             ########################################
569             # Used by AUTOLOAD below. Omit 'prepare'.
570             ########################################
571              
572             my %db_methods;
573             BEGIN {
574 1     1   2 %db_methods = %{$DBI::DBI_methods{db}};
  1         32  
575 1         33 delete($db_methods{'prepare'});
576             }
577 1     1   1080 use subs keys %db_methods;
  1         27  
  1         10  
578              
579             ########################################
580             # Call the multiplexing code for each of the database methods listed above.
581             ########################################
582              
583             sub AUTOLOAD {
584 0     0     my ($method, @results);
585            
586 0           $method = $DBD::Multiplex::db::AUTOLOAD;
587 0           $method =~ s/^DBD::Multiplex::db:://;
588            
589             # Two levels down, the actual method being multiplexed is being called.
590 0 0         @results = (wantarray)
591             ? ( &DBD::Multiplex::mx_method_all($method, @_) )
592             : ( scalar &DBD::Multiplex::mx_method_all($method, @_) );
593              
594 0 0         return $results[0] unless (wantarray);
595 0           return @results;
596             }
597              
598             ########################################
599             # The default behaviour is to not multiplex simple select statements.
600             # The resulting statement handle then contains only one child handle,
601             # automatically resulting in subsequent methods executed against the
602             # statement handle to use 'first_success' mode.
603             ########################################
604              
605             sub mx_default_statement_mode {
606 0     0     my ($statement) = @_;
607 0           my ($result);
608              
609 0           $result = '';
610              
611 0 0         if (! &DBD::Multiplex::mx_is_modify_statement($statement)) {
612 0           $result = 'first_success';
613             }
614            
615 0           return $result;
616             }
617              
618             } #========================================================== END DATABASE ===
619              
620             { #============================================================= STATEMENT ===
621              
622             package DBD::Multiplex::st;
623             $imp_data_size = 0;
624 1     1   422 use strict;
  1         2  
  1         289  
625            
626             ########################################
627             # Some attributes are stored in the parent handle.
628             # some in each of the children handles.
629             # This function uses and therefore cannot be called using mx_method_all.
630             ########################################
631              
632             sub STORE {
633 0     0     my ($sth, $attr, $val) = @_;
634              
635 0 0         if ($attr =~ /^mx_(.+)/) {
636 0 0         if ($1 eq uc($1)) {
637 0 0         return $sth->SUPER::STORE($attr, $val) if ($1 eq uc($1));
638             } else {
639 0           return $sth->{$attr} = $val;
640             }
641             }
642              
643             # Store the attribute in each of the children handles.
644 0           return &DBD::Multiplex::mx_method_all('STORE', @_);
645             }
646              
647             ########################################
648             # Some attributes are stored in the parent handle.
649             # some in each of the children handles.
650             # This function uses and therefore cannot be called using mx_method_all.
651             ########################################
652              
653             sub FETCH {
654 0     0     my ($sth, $attr) = @_;
655              
656 0 0         if ($attr =~ /^mx_(.+)/) {
657 0 0         if ($1 eq uc($1)) {
658 0           return $sth->SUPER::FETCH($attr);
659             } else {
660 0           return $sth->{$attr};
661             }
662             }
663              
664             # Fetch the attribute from each of the children handles.
665 0           return &DBD::Multiplex::mx_method_all('FETCH', @_);
666             }
667              
668             ########################################
669             # Required by the DBI.
670             ########################################
671              
672             sub DESTROY {
673 0     0     undef;
674             }
675              
676             ########################################
677             # Used by AUTOLOAD below.
678             ########################################
679              
680 1     1   7 use subs keys %{$DBI::DBI_methods{st}};
  1         2  
  1         1  
  1         13  
681              
682             ########################################
683             # Call the multiplexing code for each of the statement methods listed above.
684             ########################################
685              
686             sub AUTOLOAD {
687 0     0     my ($method, @results);
688            
689 0           $method = $DBD::Multiplex::st::AUTOLOAD;
690 0           $method =~ s/^DBD::Multiplex::st:://;
691            
692             # Two levels down, the actual method being multiplexed is being called.
693 0 0         @results = (wantarray)
694             ? ( &DBD::Multiplex::mx_method_all($method, @_) )
695             : ( scalar &DBD::Multiplex::mx_method_all($method, @_) );
696              
697 0 0         return $results[0] unless (wantarray);
698 0           return @results;
699             }
700              
701             } #========================================================= END STATEMENT ===
702              
703             1;
704              
705             __END__