File Coverage

blib/lib/DBD/Mock/st.pm
Criterion Covered Total %
statement 193 220 87.7
branch 104 122 85.2
condition 13 20 65.0
subroutine 19 19 100.0
pod 0 14 0.0
total 329 395 83.2


line stmt bran cond sub pod time code
1             package DBD::Mock::st;
2              
3 40     40   311 use strict;
  40         92  
  40         1322  
4 40     40   210 use warnings;
  40         82  
  40         100380  
5              
6             our $imp_data_size = 0;
7              
8             sub bind_col {
9 13     13 0 742 my ( $sth, $param_num, $ref, $attr ) = @_;
10              
11 13         33 my $tracker = $sth->FETCH('mock_my_history');
12 13         55 $tracker->bind_col( $param_num, $ref );
13 13         27 return 1;
14             }
15              
16             sub bind_param {
17 14     14 0 398 my ( $sth, $param_num, $val, $attr ) = @_;
18 14         50 my $tracker = $sth->FETCH('mock_my_history');
19 14         49 $tracker->bound_param( $param_num, $val, $attr );
20 14         34 return 1;
21             }
22              
23             sub bind_param_array {
24 2     2 0 17 bind_param(@_);
25             }
26              
27             sub bind_param_inout {
28 2     2 0 18 my ( $sth, $param_num, $val, $max_len ) = @_;
29              
30             # check that $val is a scalar ref
31             ( UNIVERSAL::isa( $val, 'SCALAR' ) )
32             || $sth->{Database}
33 2 50       8 ->set_err( 1, "need a scalar ref to bind_param_inout, not $val" );
34              
35             # check for positive $max_len
36             ( $max_len > 0 )
37             || $sth->{Database}
38 2 50       7 ->set_err( 1, "need to specify a maximum length to bind_param_inout" );
39 2         7 my $tracker = $sth->FETCH('mock_my_history');
40 2         7 $tracker->bound_param( $param_num, $val );
41 2         5 return 1;
42             }
43              
44             sub execute_array {
45 1     1 0 10 my ( $sth, $attr, @bind_values ) = @_;
46              
47             # no bind values means we're relying on prior calls to bind_param_array()
48             # for our data
49 1         3 my $tracker = $sth->FETCH('mock_my_history');
50             # don't use a reference; there's some magic attached to it somewhere
51             # so make it a lovely, simple array as soon as possible
52 1         3 my @bound = @{ $tracker->bound_params() };
  1         3  
53 1         4 foreach my $p (@bound) {
54 2         11 my $result = $sth->execute( @$p );
55             # store the result from execute() if ArrayTupleStatus attribute is
56             # passed
57 2         6 push @{ $attr->{ArrayTupleStatus} }, $result
58 2 50       6 if (exists $attr->{ArrayTupleStatus});
59             }
60              
61             # TODO: the docs say:
62             # When called in scalar context the execute_array() method returns the
63             # number of tuples executed, or undef if an error occurred. Like
64             # execute(), a successful execute_array() always returns true regardless
65             # of the number of tuples executed, even if it's zero. If there were any
66             # errors the ArrayTupleStatus array can be used to discover which tuples
67             # failed and with what errors.
68             # When called in list context the execute_array() method returns two
69             # scalars; $tuples is the same as calling execute_array() in scalar
70             # context and $rows is the number of rows affected for each tuple, if
71             # available or -1 if the driver cannot determine this.
72             # We have glossed over this...
73 1         6 return scalar @bound;
74             }
75              
76             sub execute {
77 153     153 0 25672 my ( $sth, @params ) = @_;
78 153         312 my $dbh = $sth->{Database};
79              
80 153 100       438 unless ( $dbh->{mock_can_connect} ) {
81 1         43 $dbh->set_err( 1, "No connection present" );
82 0         0 return 0;
83             }
84 152 100       385 unless ( $dbh->{mock_can_execute} ) {
85 2         29 $dbh->set_err( 1, "Cannot execute" );
86 0         0 return 0;
87             }
88 150 100       374 $dbh->{mock_can_execute}++ if $dbh->{mock_can_execute} < 0;
89              
90 150         584 my $tracker = $sth->FETCH('mock_my_history');
91              
92 150 100       453 if ( $tracker->has_failure() ) {
93 3         10 $dbh->set_err( $tracker->get_failure() );
94 0         0 return 0;
95             }
96              
97 147 100       405 if (@params) {
98 47         175 $tracker->bind_params(@params);
99             }
100              
101 147 100       499 if ( my $session = $dbh->{mock_session} ) {
102 49         96 eval {
103 49         162 my $state = $session->current_state;
104 49         186 $session->verify_statement( $sth->{Statement});
105 43         150 $session->verify_bound_params( $tracker->bound_params() );
106              
107             # Load a copy of the results to return (minus the field
108             # names) into the tracker
109 40         71 my @results = @{ $state->{results} };
  40         112  
110 40         70 shift @results;
111 40         113 $tracker->{return_data} = \@results;
112             };
113 49 100       153 if ($@) {
114 9         18 my $session_error = $@;
115 9         23 chomp $session_error;
116 9         115 $sth->set_err( 1, "Session Error: ${session_error}" );
117 9         163 return;
118             }
119             }
120              
121 138         578 $tracker->mark_executed;
122 137         415 my $fields = $tracker->fields;
123 137 50       247 $sth->STORE( NUM_OF_FIELDS => scalar @{ $fields ? $fields : [] } );
  137         666  
124 137         509 $sth->STORE( NAME => $fields );
125              
126 137         453 $sth->STORE( NUM_OF_PARAMS => $tracker->num_params );
127              
128             # handle INSERT statements and the mock_last_insert_ids
129             # We should only increment these things after the last successful INSERT.
130             # -RobK, 2007-10-12
131             #use Data::Dumper;warn Dumper $dbh->{mock_last_insert_ids};
132              
133 137 100       991 if ( $dbh->{Statement} =~ /^\s*?insert(?:\s+ignore)?\s+into\s+(\S+)/i ) {
134 23 100 66     118 if ( $tracker->{last_insert_id} ) {
    100          
135 1         4 $dbh->{mock_last_insert_id} = $tracker->{last_insert_id};
136              
137             } elsif ( $dbh->{mock_last_insert_ids}
138             && exists $dbh->{mock_last_insert_ids}{$1} )
139             {
140 7         20 $dbh->{mock_last_insert_id} = $dbh->{mock_last_insert_ids}{$1}++;
141             }
142             else {
143 15         32 $dbh->{mock_last_insert_id}++;
144             }
145             }
146              
147             #warn "$dbh->{mock_last_insert_id}\n";
148              
149             # always return 0E0 for Selects
150 137 100       571 if ( $dbh->{Statement} =~ /^\s*?select/i ) {
151 101         410 return '0E0';
152             }
153 36   100     138 return ( $sth->rows() || '0E0' );
154             }
155              
156             sub fetch {
157 146     146 0 1790 my ($sth) = @_;
158 146         238 my $dbh = $sth->{Database};
159 146 100       333 unless ( $dbh->{mock_can_connect} ) {
160 1         21 $dbh->set_err( 1, "No connection present" );
161 0         0 return;
162             }
163 145 100       309 unless ( $dbh->{mock_can_fetch} ) {
164 3         42 $dbh->set_err( 1, "Cannot fetch" );
165 0         0 return;
166             }
167 142 100       298 $dbh->{mock_can_fetch}++ if $dbh->{mock_can_fetch} < 0;
168              
169 142         387 my $tracker = $sth->FETCH('mock_my_history');
170              
171 142 100       374 my $record = $tracker->next_record
172             or return;
173              
174 123 100       304 if ( my @cols = $tracker->bind_cols() ) {
175 6         19 for my $i ( grep { ref $cols[$_] } 0 .. $#cols ) {
  14         35  
176 14         23 ${ $cols[$i] } = $record->[$i];
  14         38  
177             }
178             }
179              
180 123         632 return $record;
181             }
182              
183             sub fetchrow_array {
184 33     33 0 15192 my ($sth) = @_;
185 33         109 my $row = $sth->DBD::Mock::st::fetch();
186 32 100       108 return unless ref($row) eq 'ARRAY';
187 30         48 return @{$row};
  30         95  
188             }
189              
190             sub fetchrow_arrayref {
191 87     87 0 3291 my ($sth) = @_;
192 87         181 return $sth->DBD::Mock::st::fetch();
193             }
194              
195             sub fetchrow_hashref {
196 10     10 0 2280 my ( $sth, $name ) = @_;
197 10         20 my $dbh = $sth->{Database};
198              
199             # handle any errors since we are grabbing
200             # from the tracker directly
201 10 50       40 unless ( $dbh->{mock_can_connect} ) {
202 0         0 $dbh->set_err( 1, "No connection present" );
203 0         0 return;
204             }
205 10 100       28 unless ( $dbh->{mock_can_fetch} ) {
206 1         18 $dbh->set_err( 1, "Cannot fetch" );
207 0         0 return;
208             }
209 9 100       26 $dbh->{mock_can_fetch}++ if $dbh->{mock_can_fetch} < 0;
210              
211             # first handle the $name, it will default to NAME
212 9   50     64 $name ||= 'NAME';
213              
214             # then fetch the names from the $sth (per DBI spec)
215 9         42 my $fields = $sth->FETCH($name);
216              
217             # now check the tracker ...
218 9         31 my $tracker = $sth->FETCH('mock_my_history');
219              
220             # and collect the results
221 9 100       26 if ( my $record = $tracker->next_record() ) {
222 6         18 my @values = @{$record};
  6         26  
223 6         9 return { map { $_ => shift(@values) } @{$fields} };
  12         90  
  6         14  
224             }
225              
226 3         15 return undef;
227             }
228              
229             #XXX Isn't this supposed to return an array of hashrefs? -RobK, 2007-10-15
230             sub fetchall_hashref {
231 5     5 0 31 my ( $sth, $keyfield ) = @_;
232 5         9 my $dbh = $sth->{Database};
233              
234             # handle any errors since we are grabbing
235             # from the tracker directly
236 5 50       12 unless ( $dbh->{mock_can_connect} ) {
237 0         0 $dbh->set_err( 1, "No connection present" );
238 0         0 return;
239             }
240 5 50       10 unless ( $dbh->{mock_can_fetch} ) {
241 0         0 $dbh->set_err( 1, "Cannot fetch" );
242 0         0 return;
243             }
244 5 50       11 $dbh->{mock_can_fetch}++ if $dbh->{mock_can_fetch} < 0;
245              
246             # get the case conversion to use for hash key names (NAME/NAME_lc/NAME_uc)
247 5   50     22 my $hash_key_name = $sth->{Database}->FETCH('FetchHashKeyName') || 'NAME';
248              
249             # get a hashref mapping field names to their corresponding indexes. indexes
250             # start at zero
251 5         20 my $names_hash = $sth->FETCH("${hash_key_name}_hash");
252              
253             # as of DBI v1.48, the $keyfield argument can be either an arrayref of field
254             # names/indexes or a single field name/index
255 5 100       13 my @key_fields = ref $keyfield ? @{$keyfield} : $keyfield;
  3         6  
256              
257 5         17 my $num_fields = $sth->FETCH('NUM_OF_FIELDS');
258              
259             # get the index(es) of the given key field(s). a key field can be specified
260             # as either the name of a field or an integer column number
261 5         6 my @key_indexes;
262 5         11 foreach my $field (@key_fields) {
263 6 100 33     32 if (defined $names_hash->{$field}) {
    50 33        
264 3         7 push @key_indexes, $names_hash->{$field};
265             }
266             elsif (DBI::looks_like_number($field) && $field >= 1 && $field <= $num_fields) {
267             # convert from column number to array index. column numbers start at
268             # one, while indexes start at zero
269 3         9 push @key_indexes, $field - 1;
270             }
271             else {
272             my $err = "Could not find key field '$field' (not one of " .
273 0         0 join(' ', keys %{$names_hash}) . ')';
  0         0  
274 0         0 $dbh->set_err( 1, $err );
275 0         0 return;
276             }
277             }
278              
279 5         13 my $tracker = $sth->FETCH('mock_my_history');
280 5         9 my $rethash = {};
281              
282             # now loop through all the records ...
283 5         12 while ( my $record = $tracker->next_record() ) {
284              
285             # populate the hash, adding a layer of nesting for each key field
286             # specified by the user
287 10         15 my $ref = $rethash;
288 10         16 foreach my $index (@key_indexes) {
289 12         17 my $value = $record->[$index];
290 12 50       32 $ref->{$value} = {} if ! defined $ref->{$value};
291 12         21 $ref = $ref->{$value};
292             }
293              
294             # copy all of the returned data into the most-nested level of the hash
295 10         14 foreach my $field (keys %{$names_hash}) {
  10         26  
296 30         40 my $index = $names_hash->{$field};
297 30         74 $ref->{$field} = $record->[$index];
298             }
299             }
300              
301 5         48 return $rethash;
302             }
303              
304             sub last_insert_id {
305 6     6 0 15 my ( $sth, @params ) = @_;
306 6         28 return $sth->{Database}->last_insert_id( @params );
307             }
308              
309             sub finish {
310 38     38 0 10432 my ($sth) = @_;
311 38         175 $sth->FETCH('mock_my_history')->is_finished('yes');
312             }
313              
314             sub rows {
315 43     43 0 1225 my ($sth) = @_;
316 43         156 $sth->FETCH('mock_num_rows');
317             }
318              
319             sub FETCH {
320 131     131   49416 my ( $sth, $attrib ) = @_;
321 131         532 $sth->trace_msg("Fetching ST attribute '$attrib'\n");
322 131         253 my $tracker = $sth->{mock_my_history};
323 131         498 $sth->trace_msg( "Retrieved tracker: " . ref($tracker) . "\n" );
324              
325             # NAME attributes
326 131 100       1292 if ( $attrib eq 'NAME' ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
327 12         22 return [ @{ $tracker->fields } ];
  12         39  
328             }
329             elsif ( $attrib eq 'NAME_lc' ) {
330 2         6 return [ map { lc($_) } @{ $tracker->fields } ];
  6         32  
  2         7  
331             }
332             elsif ( $attrib eq 'NAME_uc' ) {
333 1         3 return [ map { uc($_) } @{ $tracker->fields } ];
  3         10  
  1         4  
334             }
335              
336             # NAME_hash attributes
337             elsif ( $attrib eq 'NAME_hash' ) {
338 6         14 my $i = 0;
339 6         9 return { map { $_ => $i++ } @{ $tracker->fields } };
  18         51  
  6         16  
340             }
341             elsif ( $attrib eq 'NAME_hash_lc' ) {
342 1         2 my $i = 0;
343 1         2 return { map { lc($_) => $i++ } @{ $tracker->fields } };
  3         12  
  1         4  
344             }
345             elsif ( $attrib eq 'NAME_hash_uc' ) {
346 1         2 my $i = 0;
347 1         3 return { map { uc($_) => $i++ } @{ $tracker->fields } };
  3         11  
  1         3  
348             }
349              
350             # others
351             elsif ( $attrib eq 'NUM_OF_FIELDS' ) {
352 2         8 return $tracker->num_fields;
353             }
354             elsif ( $attrib eq 'NUM_OF_PARAMS' ) {
355 1         5 return $tracker->num_params;
356             }
357             elsif ( $attrib eq 'TYPE' ) {
358 0         0 my $num_fields = $tracker->num_fields;
359 0         0 return [ map { $DBI::SQL_VARCHAR } ( 0 .. $num_fields ) ];
  0         0  
360             }
361             elsif ( $attrib eq 'Active' ) {
362 5         16 return $tracker->is_active;
363             }
364             elsif ( exists $tracker->{driver_attributes}->{$attrib} ) {
365 8         34 return $tracker->{driver_attributes}->{$attrib};
366             }
367             elsif ( $attrib !~ /^mock/ ) {
368 4 50       21 if ( $sth->{Database}->{mock_attribute_aliases} ) {
369 0 0       0 if (
370 0         0 exists ${ $sth->{Database}->{mock_attribute_aliases}->{st} }
371             {$attrib} )
372             {
373             my $mock_attrib =
374 0         0 $sth->{Database}->{mock_attribute_aliases}->{st}->{$attrib};
375 0 0       0 if ( ref($mock_attrib) eq 'CODE' ) {
376 0         0 return $mock_attrib->($sth);
377             }
378             else {
379 0         0 return $sth->FETCH($mock_attrib);
380             }
381             }
382             }
383 4         68 return $sth->SUPER::FETCH($attrib);
384             }
385              
386             # now do our stuff...
387              
388 88 50 100     669 if ( $attrib eq 'mock_my_history' ) {
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    50          
389 0         0 return $tracker;
390             }
391             elsif ( $attrib eq 'mock_execution_history' ) {
392 1         5 return $tracker->execution_history();
393             }
394             elsif ( $attrib eq 'mock_statement' ) {
395 2         10 return $tracker->statement;
396             }
397             elsif ( $attrib eq 'mock_params' ) {
398 5         20 return $tracker->bound_params;
399             }
400             elsif ( $attrib eq 'mock_param_attrs' ) {
401 3         11 return $tracker->bound_param_attrs;
402             }
403             elsif ( $attrib eq 'mock_records' ) {
404 1         5 return $tracker->return_data;
405             }
406             elsif ( $attrib eq 'mock_num_records' || $attrib eq 'mock_num_rows' ) {
407 48         169 return $tracker->num_rows;
408             }
409             elsif ( $attrib eq 'mock_current_record_num' ) {
410 11         38 return $tracker->current_record_num;
411             }
412             elsif ( $attrib eq 'mock_fields' ) {
413 1         4 return $tracker->fields;
414             }
415             elsif ( $attrib eq 'mock_is_executed' ) {
416 4         29 return $tracker->is_executed;
417             }
418             elsif ( $attrib eq 'mock_is_finished' ) {
419 5         18 return $tracker->is_finished;
420             }
421             elsif ( $attrib eq 'mock_is_depleted' ) {
422 7         23 return $tracker->is_depleted;
423             }
424             else {
425 0         0 die "I don't know how to retrieve statement attribute '$attrib'\n";
426             }
427             }
428              
429             sub STORE {
430 718     718   1366 my ( $sth, $attrib, $value ) = @_;
431 718         2577 $sth->trace_msg("Storing ST attribute '$attrib'\n");
432 718 100       2646 if ( $attrib =~ /^mock/ ) {
    100          
433 145         684 return $sth->{$attrib} = $value;
434             }
435             elsif ( $attrib =~ /^NAME/ ) {
436              
437             # no-op...
438 218         549 return;
439             }
440             else {
441 355   100     1068 $value ||= 0;
442 355         1954 return $sth->SUPER::STORE( $attrib, $value );
443             }
444             }
445              
446 145     145   42276 sub DESTROY { undef }
447              
448             1;