File Coverage

blib/lib/DBIx/DataModel/Statement.pm
Criterion Covered Total %
statement 319 350 91.1
branch 144 192 75.0
condition 30 54 55.5
subroutine 51 58 87.9
pod 0 29 0.0
total 544 683 79.6


line stmt bran cond sub pod time code
1             #----------------------------------------------------------------------
2             package DBIx::DataModel::Statement;
3             #----------------------------------------------------------------------
4             # see POD doc at end of file
5              
6 15     15   10774 use warnings;
  15         32  
  15         965  
7 15     15   112 use strict;
  15         31  
  15         555  
8 15     15   76 use List::MoreUtils qw/firstval any/;
  15         31  
  15         185  
9 15     15   13608 use Scalar::Util qw/weaken dualvar/;
  15         31  
  15         1179  
10 15     15   3749 use POSIX qw/LONG_MAX/;
  15         47281  
  15         112  
11 15     15   12786 use Clone qw/clone/;
  15         54  
  15         854  
12 15     15   90 use DBIx::DataModel::Carp;
  15         29  
  15         125  
13 15     15   842 use Try::Tiny qw/try catch/;
  15         28  
  15         980  
14 15     15   93 use mro qw/c3/;
  15         32  
  15         113  
15              
16 15     15   523 use DBIx::DataModel;
  15         29  
  15         103  
17 15     15   80 use DBIx::DataModel::Meta::Utils qw/define_readonly_accessors does/;
  15         28  
  15         955  
18 15     15   90 use namespace::clean;
  15         69  
  15         133  
19              
20             #----------------------------------------------------------------------
21             # internals
22             #----------------------------------------------------------------------
23              
24             use overload
25              
26             # overload the stringification operator so that Devel::StackTrace is happy;
27             # also useful to show the SQL (if in sqlized state)
28             '""' => sub {
29 0     0   0 my $self = shift;
30 0     0   0 my $string = try {my ($sql, @bind) = $self->sql;
31 0         0 __PACKAGE__ . "($sql // " . join(", ", @bind) . ")"; }
32 0   0     0 || overload::StrVal($self);
33             }
34 15     15   7707 ;
  15         33  
  15         151  
35              
36              
37             # sequence of states. Stored as dualvars for both ordering and printing
38             use constant {
39 15         23149 NEW => dualvar(1, "new" ),
40             REFINED => dualvar(2, "refined" ),
41             SQLIZED => dualvar(3, "sqlized" ),
42             PREPARED => dualvar(4, "prepared"),
43             EXECUTED => dualvar(5, "executed"),
44 15     15   1733 };
  15         65  
45              
46              
47             # arguments accepted by the refine() method, and their associated handlers
48             my %REFINABLE_ARGS = (
49             -where => \&_merge_into_where_arg,
50             -fetch => \&_fetch_from_primary_key,
51             -columns => \&_restrict_columns,
52             map {(-$_ => \&_just_store_arg)} qw/order_by group_by having for
53             union union_all intersect except minus
54             result_as post_SQL pre_exec post_exec post_bless
55             limit offset page_size page_index as
56             column_types prepare_attrs dbi_prepare_method
57             where_on join_with_USING sql_abstract/,
58             );
59              
60              
61             #----------------------------------------------------------------------
62             # PUBLIC METHODS
63             #----------------------------------------------------------------------
64              
65             sub new {
66 192     192 0 742 my ($class, $source, %other_args) = @_;
67              
68             # check $source
69 192 50 33     1912 $source
70             && $source->isa('DBIx::DataModel::Source')
71             or croak "invalid source for DBIx::DataModel::Statement->new()";
72              
73             # build the object
74 192         1450 my $self = bless {status => NEW,
75             args => {},
76             pre_bound_params => {},
77             bound_params => [],
78             source => $source}, $class;
79              
80             # add placeholder_regex
81 192         890 my $prefix = $source->schema->{placeholder_prefix};
82 192         2350 $self->{placeholder_regex} = qr/^\Q$prefix\E(.+)/;
83              
84             # parse remaining args, if any
85 192 100       674 $self->refine(%other_args) if %other_args;
86              
87 192         564 return $self;
88             }
89              
90              
91             # accessors
92             define_readonly_accessors( __PACKAGE__, qw/source status/);
93              
94             # proxy methods
95 586     586 0 1543 sub meta_source {shift->{source}->metadm}
96 1562     1562 0 4057 sub schema {shift->{source}->schema}
97              
98              
99             # back to the original state
100             sub reset {
101 0     0 0 0 my ($self, %other_args) = @_;
102              
103 0         0 my $new = (ref $self)->new($self->{source}, %other_args);
104 0         0 %$self = (%$new);
105              
106 0         0 return $self;
107             }
108              
109              
110             sub arg {
111 636     636 0 1072 my ($self, $arg_name) = @_;
112              
113 636   50     1463 my $args = $self->{args} || {};
114 636         2149 return $args->{$arg_name};
115             }
116              
117              
118             sub sql_abstract {
119 455     455 0 829 my ($self, $arg_name) = @_;
120              
121 455   66     1007 return $self->arg(-sql_abstract) || $self->schema->sql_abstract;
122             }
123            
124              
125              
126             #----------------------------------------------------------------------
127             # PUBLIC METHODS IN RELATION WITH SELECT()
128             #----------------------------------------------------------------------
129              
130              
131             sub sql {
132 14     14 0 35 my ($self) = @_;
133              
134 14 50       40 $self->status >= SQLIZED
135             or croak "can't call sql() when in status ". $self->status;
136              
137 13         70 return wantarray ? ($self->{sql}, @{$self->{bound_params}})
138 14 100       62 : $self->{sql};
139             }
140              
141              
142             sub bind {
143 194     194 0 540 my ($self, @args) = @_;
144              
145             # arguments can be a list, a hashref or an arrayref
146 194 100       561 if (@args == 1) {
    100          
    50          
147 171 50       561 if (does $args[0], 'HASH') {
    0          
148 171         1580 @args = %{$args[0]};
  171         572  
149             }
150             elsif (does $args[0], 'ARRAY') {
151 0         0 my $i = 0; @args = map {($i++, $_)} @{$args[0]};
  0         0  
  0         0  
  0         0  
152             }
153             else {
154 0         0 croak "unexpected arg type to bind()";
155             }
156             }
157             elsif (@args == 3) { # name => value, \%datatype (see L)
158             # transform into ->bind($name => [$value, \%datatype])
159 1         3 @args = ($args[0], [$args[1], $args[2]]);
160             }
161             elsif (@args % 2 == 1) {
162 0         0 croak "odd number of args to bind()";
163             }
164              
165             # do bind (different behaviour according to status)
166 194         496 my %args = @args;
167 194 100       649 if ($self->status < SQLIZED) {
168 27         98 while (my ($k, $v) = each %args) {
169 35         127 $self->{pre_bound_params}{$k} = $v;
170             }
171             }
172             else {
173 167         634 while (my ($k, $v) = each %args) {
174 54 100       211 my $indices = $self->{param_indices}{$k}
175             or next; # silently ignore that binding (named placeholder unused)
176 32         200 $self->{bound_params}[$_] = $v foreach @$indices;
177             }
178             }
179              
180             # THINK : probably we should check here that $args{__schema}, if present,
181             # is the same as $self->schema (same database connection) ... but how
182             # to check for "sameness" on database handles ?
183              
184 194         474 return $self;
185             }
186              
187              
188             sub refine {
189 131     131 0 450 my ($self, @more_args) = @_;
190              
191             # check statement status
192 131 50       381 $self->status <= REFINED
193             or croak "can't refine() when in status " . $self->status;
194 131         316 $self->{status} = REFINED;
195              
196             # process all key-value pairs
197 131         560 while (my ($k, $v) = splice @more_args, 0, 2) {
198              
199             # special case : -with can be used as synonym for -sql_abstract (for making it more similar to SQL's "WITH RECURSIVE...")
200 199 100       1165 $k = '-sql_abstract' if $k eq '-with';
201              
202             # find the proper arg handler and invoke it
203 199 50       568 my $refine_handler = $REFINABLE_ARGS{$k}
204             or croak "invalid arg : $k";
205 199         480 $self->$refine_handler($k, $v);
206             }
207              
208 130         2041 return $self;
209             }
210              
211              
212              
213             sub sqlize {
214 164     164 0 340 my ($self, @args) = @_;
215              
216 164 50       448 $self->status < SQLIZED
217             or croak "can't sqlize() when in status ". $self->status;
218              
219             # merge new args into $self->{args}
220 164 100       370 $self->refine(@args) if @args;
221              
222             # shortcuts
223 164         307 my $args = $self->{args};
224 164         342 my $meta_source = $self->meta_source;
225 164         339 my $source_where = $meta_source->{where};
226 164   100     727 my $result_as = $args->{-result_as} || "";
227              
228             # build arguments for SQL::Abstract::More
229 164 100       384 $self->refine(-where => $source_where) if $source_where;
230 164         730 my @args_to_copy = qw/-columns -where
231             -union -union_all -intersect -except -minus
232             -order_by -group_by -having
233             -limit -offset -page_size -page_index -as/;
234 164         517 my %sqla_args = (-from => clone($self->source->db_from),
235             -want_details => 1);
236 164   66     2249 defined $args->{$_} and $sqla_args{$_} = $args->{$_} for @args_to_copy;
237 164   66     862 $sqla_args{-columns} ||= $meta_source->default_columns;
238 164 100 50     550 $sqla_args{-limit} ||= 1
      100        
239             if $result_as eq 'firstrow' && $self->schema->autolimit_firstrow;
240              
241             # "-for" (e.g. "update", "read only")
242 164 100       416 if ($result_as ne 'subquery') {
243 161 100       555 if ($args->{-for}) {
    50          
244 2         8 $sqla_args{-for} = $args->{-for};
245             }
246             elsif (!exists $args->{-for}) {
247 159         405 $sqla_args{-for} = $self->schema->select_implicitly_for;
248             }
249             }
250              
251             # "where_on" : conditions to be added in joins
252 164 100       570 if (my $where_on = $args->{-where_on}) {
253              
254             # check proper usage
255 6 100       22 does $sqla_args{-from}, 'ARRAY'
256             or croak "datasource for '-where_on' was not a join";
257              
258             # retrieve components of the join and check again for proper usage
259 5         40 my ($join_op, $first_table, @other_join_args) = @{$sqla_args{-from}};
  5         16  
260 5 50       13 $join_op eq '-join'
261             or croak "the '-where_on' argument can only be used on a 'join' datasource";
262              
263             # build a hash where keys are the database table names, and values are the join conditions (hashes)
264 5         16 my %by_dest_table = reverse @other_join_args;
265              
266             # additional conditions coming from the -where_on hash are inserted as additional join criteria
267 5         19 while (my ($table, $additional_cond) = each %$where_on) {
268 9         16 my $db_table = $meta_source->{db_table_by_source}{$table};
269 15     15   130 no warnings 'uninitialized';
  15         32  
  15         64130  
270             my $join_cond = $by_dest_table{$db_table} # new preferred syntax : through association or alias names
271 9 100 100     106 || $by_dest_table{$table} # backwards compat : database names are accepted too
272             or croak "-where_on => {'$table' => ..}: there is no such table in the join ", $meta_source->class;
273             $join_cond->{condition}
274             = $self->sql_abstract->merge_conditions($join_cond->{condition},
275 8         17 $additional_cond);
276 8         213 delete $join_cond->{using};
277             }
278             }
279              
280             # adjust join conditions for ON clause or for USING clause
281 162 100       537 if (does $sqla_args{-from}, 'ARRAY') {
282 51 50       478 $sqla_args{-from}[0] eq '-join'
283             or croak "datasource is an arrayref but does not start with -join";
284             my $join_with_USING
285             = exists $args->{-join_with_USING} ? $args->{-join_with_USING}
286 51 100       181 : $self->schema->{join_with_USING};
287 51         120 for (my $i = 2; $i < @{$sqla_args{-from}}; $i += 2) {
  135         288  
288 84         127 my $join_cond = $sqla_args{-from}[$i];
289 84 100       148 if ($join_with_USING) {
290 5 50       21 delete $join_cond->{condition} if $join_cond->{using};
291             }
292             else {
293 79         212 delete $join_cond->{using};
294             }
295             }
296             }
297              
298             # generate SQL
299 162         1139 my $sqla_result = $self->sql_abstract->select(%sqla_args);
300              
301             # maybe post-process the SQL
302 162 50       150810 if ($args->{-post_SQL}) {
303 0         0 ($sqla_result->{sql}, @{$sqla_result->{bind}})
304 0         0 = $args->{-post_SQL}->($sqla_result->{sql}, @{$sqla_result->{bind}});
  0         0  
305             }
306              
307             # keep $sql / @bind / aliases in $self, and set new status
308 162         508 $self->{bound_params} = $sqla_result->{bind};
309 162         904 $self->{$_} = $sqla_result->{$_} for qw/sql aliased_tables aliased_columns/;
310 162         441 $self->{status} = SQLIZED;
311              
312             # analyze placeholders, and replace by pre_bound params if applicable
313 162 50       498 if (my $regex = $self->{placeholder_regex}) {
314 162         397 for (my $i = 0; $i < @{$self->{bound_params}}; $i++) {
  340         877  
315             $self->{bound_params}[$i] =~ $regex
316 178 100       905 and push @{$self->{param_indices}{$1}}, $i;
  32         212  
317             }
318             }
319 162 50       892 $self->bind($self->{pre_bound_params}) if $self->{pre_bound_params};
320              
321             # compute callback to apply to data rows
322 162         350 my $callback = $self->{args}{-post_bless};
323 162         374 weaken(my $weak_self = $self); # weaken to avoid a circular ref in closure
324             $self->{row_callback} = sub {
325 162     162   260 my $row = shift;
326 162         396 $weak_self->bless_from_DB($row);
327 162 50       332 $callback->($row) if $callback;
328 162         831 };
329              
330 162         1028 return $self;
331             }
332              
333              
334              
335             sub prepare {
336 156     156 0 346 my ($self, @args) = @_;
337              
338 156         354 my $meta_source = $self->meta_source;
339              
340 156 50 66     562 $self->sqlize(@args) if @args or $self->status < SQLIZED;
341              
342 154 50       429 $self->status == SQLIZED
343             or croak "can't prepare() when in status " . $self->status;
344              
345             # log the statement and bind values
346 154         464 $self->schema->_debug("PREPARE $self->{sql} / @{$self->{bound_params}}");
  154         1024  
347              
348             # assemble stuff for calling the database
349 154 50       485 my $dbh = $self->schema->dbh or croak "Schema has no dbh";
350 154   33     568 my $method = $self->{args}{-dbi_prepare_method} || $self->schema->dbi_prepare_method;
351 154         383 my @prepare_args = ($self->{sql});
352 154 50       426 if (my $prepare_attrs = $self->{args}{-prepare_attrs}) {
353 0         0 push @prepare_args, $prepare_attrs;
354             }
355              
356             # call the database
357 154         1561 $self->{sth} = $dbh->$method(@prepare_args);
358              
359             # new status and return
360 147         28035 $self->{status} = PREPARED;
361 147         381 return $self;
362             }
363              
364              
365              
366             sub sth {
367 535     535 0 955 my ($self) = @_;
368              
369 535 50       1398 $self->prepare if $self->status < PREPARED;
370 535         1969 return $self->{sth};
371             }
372              
373              
374              
375             sub execute {
376 156     156 0 367 my ($self, @bind_args) = @_;
377              
378             # if not prepared yet, prepare it
379 156 100       381 $self->prepare if $self->status < PREPARED;
380              
381             # bind arguments if any
382 147 100       411 $self->bind(@bind_args) if @bind_args;
383              
384             # shortcuts
385 147         318 my $args = $self->{args};
386 147         378 my $sth = $self->sth;
387              
388             # previous row_count, row_num and reuse_row are no longer valid
389 147         327 delete $self->{reuse_row};
390 147         300 delete $self->{row_count};
391 147         402 $self->{row_num} = $self->offset;
392              
393             # pre_exec callback
394 147 100       380 $args->{-pre_exec}->($sth) if $args->{-pre_exec};
395              
396             # check that all placeholders were properly bound to values
397 147         213 my @unbound;
398 147 100       248 while (my ($k, $indices) = each %{$self->{param_indices} || {}}) {
  179         989  
399 32 50       136 exists $self->{bound_params}[$indices->[0]] or push @unbound, $k;
400             }
401             not @unbound
402 147 50       450 or croak "unbound placeholders (probably a missing foreign key) : "
403             . CORE::join(", ", @unbound);
404              
405             # bind parameters and execute
406 147         439 $self->sql_abstract->bind_params($sth, @{$self->{bound_params}});
  147         2761  
407 147         10448 $sth->execute;
408              
409             # post_exec callback
410 147 100       17515 $args->{-post_exec}->($sth) if $args->{-post_exec};
411              
412 147         388 $self->{status} = EXECUTED;
413 147         358 return $self;
414             }
415              
416              
417              
418             sub select {
419 164     164 0 282 my $self = shift;
420              
421 164 100       777 $self->refine(@_) if @_;
422              
423             # parse -result_as arg
424 162   100     494 my $arg_result_as = $self->arg(-result_as) || 'rows';
425 162 50       501 my ($result_as, @resultclass_args)
    100          
426             = does($arg_result_as, 'ARRAY') ? @$arg_result_as
427             : does($arg_result_as, 'HASH') ? die("-result_as => {...} is invalid; use -result_as => [...] instead")
428             : ($arg_result_as);
429              
430             # historically,some kinds of results accepted various aliases
431 162         2171 $result_as =~ s/^flat(?:_array|)$/flat_arrayref/;
432 162         301 $result_as =~ s/^arrayref$/rows/;
433 162         281 $result_as =~ s/^fast-statement$/fast_statement/;
434              
435             # produce result through a ResultAs instance
436 162 50       418 my $result_class = $self->schema->metadm->find_result_class($result_as)
437             or croak "didn't find any ResultAs subclass to implement -result_as => '$result_as'";
438 161         1026 my $result_maker = $result_class->new(@resultclass_args);
439 161         584 return $result_maker->get_result($self);
440             }
441              
442              
443             sub row_count {
444 6     6 0 20159 my ($self) = @_;
445              
446 6 50       21 if (! exists $self->{row_count}) {
447 6 100       21 $self->sqlize if $self->status < SQLIZED;
448 6         16 my ($sql, @bind) = $self->sql;
449              
450             # get syntax used for LIMIT clauses ...
451 6         32 my ($limit_sql, undef, undef) = $self->sql_abstract->limit_offset(0, 0);
452 6         165 $limit_sql =~ s/([()?*])/\\$1/g;
453              
454             # ...and use it to remove the LIMIT clause and associated bind vals, if any
455 6 100       102 if ($limit_sql =~ /ROWNUM/) { # special case for Oracle syntax, complex ...
    100          
456             # see source code of SQL::Abstract::More
457 2         8 $limit_sql =~ s/%s/(.*)/;
458 2 100       75 if ($sql =~ s/^$limit_sql/$1/) {
459 1         7 splice @bind, -2;
460             }
461             }
462             elsif ($sql =~ s[\b$limit_sql][]i) { # regular LIMIT/OFFSET syntaxes
463 3         8 splice @bind, -2;
464             }
465              
466             # decide if the SELECT COUNT should wrap the original SQL in a subquery;
467             # this is needed with clauses like below that change the number of rows
468 6         34 my $should_wrap = $sql =~ /\b(UNION|INTERSECT|MINUS|EXCEPT|DISTINCT)\b/i;
469              
470             # if no wrap required, attempt to directly substitute COUNT(*) for the
471             # column names ...but if it fails, wrap anyway
472 6   66     38 $should_wrap ||= ! ($sql =~ s[^SELECT\b.*?\bFROM\b][SELECT COUNT(*) FROM]i);
473              
474             # wrap SQL if needed, using a subquery alias because it's required for
475             # some DBMS (like PostgreSQL)
476 6 100       18 $should_wrap and $sql = "SELECT COUNT(*) FROM "
477             . $self->sql_abstract->table_alias("( $sql )", "count_wrapper");
478              
479             # log the statement and bind values
480 6         106 $self->schema->_debug("PREPARE $sql / @bind");
481              
482             # call the database
483 6 50       47 my $dbh = $self->schema->dbh or croak "Schema has no dbh";
484 6         14 my $method = $self->schema->dbi_prepare_method;
485 6         44 my $sth = $dbh->$method($sql);
486 6         793 $sth->execute(@bind);
487 6         574 ($self->{row_count}) = $sth->fetchrow_array;
488 6         205 $sth->finish;
489             }
490              
491 6         155 return $self->{row_count};
492             }
493              
494              
495             sub row_num {
496 143     143 0 287 my ($self) = @_;
497 143         362 return $self->{row_num};
498             }
499              
500              
501             sub next {
502 199     199 0 391 my ($self, $n_rows) = @_;
503              
504 199 100       556 $self->execute if $self->status < EXECUTED;
505              
506 190 50       532 my $sth = $self->sth or croak "absent sth in statement";
507 190 50       512 my $callback = $self->{row_callback} or croak "absent callback in statement";
508              
509 190 100       522 if (not defined $n_rows) { # if user wants a single row
510             # fetch a single record, either into the reusable row, or into a fresh hash
511             my $row = $self->{reuse_row} ? ($sth->fetch ? $self->{reuse_row} : undef)
512 85 100       579 : $sth->fetchrow_hashref;
    100          
513 85 100       22617 if ($row) {
514 51         180 $callback->($row);
515 51         110 $self->{row_num} +=1;
516             }
517 85         370 return $row;
518             }
519             else { # if user wants an arrayref of size $n_rows
520 105 50       246 $n_rows > 0 or croak "->next() : invalid argument, $n_rows";
521 105 50       222 not $self->{reuse_row} or croak "reusable row, cannot retrieve several";
522 105         149 my @rows;
523 105         2172 while ($n_rows--) {
524 216 100       1091 my $row = $sth->fetchrow_hashref or last;
525 111         4577 push @rows, $row;
526             }
527 105         5551 $callback->($_) foreach @rows;
528 105         243 $self->{row_num} += @rows;
529 105         414 return \@rows;
530             }
531              
532             # NOTE: ->next() returns a $row, while ->next(1) returns an arrayref of 1 row
533             }
534              
535              
536             sub all {
537 114     114 0 304 my ($self) = @_;
538              
539             # just call next() with a huge number
540 114         313 return $self->_next_and_finish(POSIX::LONG_MAX);
541             }
542              
543              
544 146 100   146 0 778 sub page_size { shift->{args}{-page_size} || POSIX::LONG_MAX }
545 146 100   146 0 840 sub page_index { shift->{args}{-page_index} || 1 }
546              
547             sub offset {
548 291     291 0 473 my ($self) = @_;
549              
550 291 100       700 if (!exists $self->{offset}) {
551             # compute on demand -- will default to 0 if there is no pagination
552             $self->{offset} = exists $self->{args}{-offset} ? $self->{args}{-offset}
553 147 100       1858 : ($self->page_index - 1) * $self->page_size;
554             }
555              
556 291         750 return $self->{offset};
557             }
558              
559              
560              
561             sub page_count {
562 0     0 0 0 my ($self) = @_;
563              
564 0 0       0 my $row_count = $self->row_count or return 0;
565 0   0     0 my $page_size = $self->page_size || 1;
566              
567 0         0 return int(($row_count - 1) / $page_size) + 1;
568             }
569              
570              
571             sub page_boundaries {
572 1     1 0 5 my ($self) = @_;
573              
574 1         3 my $first = $self->offset + 1;
575 1         3 my $last = $self->offset + $self->nb_fetched_rows;
576              
577 1         8 return ($first, $last);
578             }
579              
580              
581             sub page_rows {
582 0     0 0 0 my ($self) = @_;
583 0         0 return $self->_next_and_finish($self->page_size);
584             }
585              
586              
587             sub bless_from_DB {
588 183     183 0 303 my ($self, $row) = @_;
589              
590             # inject ref to $schema if in multi-schema mode or if temporary
591             # db_schema is set
592 183         386 my $schema = $self->schema;
593             $row->{__schema} = $schema unless $schema->{is_singleton}
594 183 100 66     716 && !$schema->{db_schema};
595              
596             # bless into appropriate class
597 183         370 bless $row, $self->meta_source->class;
598             # apply handlers
599 183 100       548 $self->{from_DB_handlers} or $self->_compute_from_DB_handlers;
600 183         262 while (my ($column_name, $handler)
601 440         1173 = each %{$self->{from_DB_handlers}}) {
602             exists $row->{$column_name}
603 257 100       612 and $handler->($row->{$column_name}, $row, $column_name, 'from_DB');
604             }
605              
606 183         487 return $row;
607             }
608              
609              
610             sub headers {
611 22     22 0 73 my $self = shift;
612              
613 22 50       53 $self->status == EXECUTED
614             or $self->execute(@_);
615              
616 22   50     61 my $hash_key_name = $self->sth->{FetchHashKeyName} || 'NAME';
617 22         55 return @{$self->sth->{$hash_key_name}};
  22         51  
618             }
619              
620              
621             sub finish {
622 142     142 0 217 my $self = shift;
623              
624 142         358 $self->{nb_fetched_rows} = $self->row_num - $self->offset;
625 142         352 $self->sth->finish;
626             }
627              
628              
629             sub nb_fetched_rows {
630 2     2 0 7 my ($self) = @_;
631              
632             exists $self->{nb_fetched_rows}
633 2 50       8 or croak "->nb_fetched_rows() can only be called on a finished statement";
634              
635 2         9 return $self->{nb_fetched_rows};
636             }
637              
638              
639              
640              
641             sub make_fast {
642 11     11 0 35 my ($self) = @_;
643              
644 11 50       57 $self->status == EXECUTED
645             or croak "cannot make_fast() when in state " . $self->status;
646              
647             # create a reusable hash and bind_columns to it (see L)
648 11         26 my %row;
649 11         35 $self->sth->bind_columns(\(@row{$self->headers}));
650 11         1077 $self->{reuse_row} = \%row;
651             }
652              
653              
654             #----------------------------------------------------------------------
655             # PRIVATE METHODS IN RELATION WITH refine()
656             #----------------------------------------------------------------------
657              
658              
659             sub _just_store_arg {
660 103     103   245 my ($self, $k, $v) = @_;
661 103         441 $self->{args}{$k} = $v;
662             }
663              
664             sub _merge_into_where_arg {
665 117     117   267 my ($self, $k, $v) = @_;
666 117         394 $self->{args}{-where} = $self->sql_abstract->merge_conditions($self->{args}{-where}, $v);
667             }
668              
669             sub _fetch_from_primary_key {
670 14     14   36 my ($self, $k, $v) = @_;
671              
672             # gather info for primary key
673 14 100       40 my $primary_key = ref($v) ? $v : [$v];
674 14         61 my @pk_columns = $self->meta_source->primary_key;
675             @pk_columns
676 14 50       40 or croak "fetch: no primary key in source " . $self->meta_source;
677 14 50       41 @pk_columns == @$primary_key
678             or croak sprintf "fetch from %s: primary key should have %d values",
679             $self->meta_source, scalar(@pk_columns);
680 14 100   14   121 List::MoreUtils::all {defined $_} @$primary_key
  14         66  
681             or croak "fetch from " . $self->meta_source . ": "
682             . "undefined val in primary key";
683              
684             # build a -where clause on primary key
685 12         64 my %where = ();
686 12         40 @where{@pk_columns} = @$primary_key;
687 12         61 $self->{args}{-where} = $self->sql_abstract->merge_conditions($self->{args}{-where}, \%where);
688              
689             # want a single record as result
690 12         462 $self->{args}{-result_as} = "firstrow";
691             }
692              
693             sub _restrict_columns {
694 74     74   228 my ($self, $k, $v) = @_;
695              
696 74 100       283 my @cols = does($v, 'ARRAY') ? @$v : ($v);
697 74 50       893 if (my $old_cols = $self->{args}{-columns}) {
698 0 0 0     0 unless (@$old_cols == 1 && $old_cols->[0] eq '*' ) {
699 0         0 foreach my $col (@cols) {
700 0 0   0   0 any {$_ eq $col} @$old_cols
  0         0  
701             or croak "can't restrict -columns on '$col' (was not in the) "
702             . "previous -columns list";
703             }
704             }
705             }
706 74         376 $self->{args}{-columns} = \@cols;
707             }
708              
709              
710              
711              
712             #----------------------------------------------------------------------
713             # PRIVATE METHODS IN RELATION WITH select()
714             #----------------------------------------------------------------------
715              
716              
717             sub _forbid_callbacks {
718 6     6   18 my ($self, $subclass) = @_;
719              
720 6         18 my $callbacks = CORE::join ", ", grep {$self->arg($_)}
  18         37  
721             qw/-pre_exec -post_exec -post_bless/;
722 6 50       28 if ($callbacks) {
723 0         0 $subclass =~ s/^.*:://;
724 0         0 croak "$callbacks incompatible with -result_as=>'$subclass'";
725             }
726             }
727              
728              
729              
730             sub _next_and_finish {
731 132     132   234 my $self = shift;
732 132         387 my $row_or_rows = $self->next( @_ ); # pass original parameters
733 123         394 $self->finish;
734 123         4176 return $row_or_rows;
735             }
736              
737             sub _compute_from_DB_handlers {
738 65     65   127 my ($self) = @_;
739 65         212 my $meta_source = $self->meta_source;
740 65         191 my $meta_schema = $self->schema->metadm;
741 65         392 my %handlers = $meta_source->_consolidate_hash('column_handlers');
742 65         274 my %aliased_tables = $meta_source->aliased_tables;
743              
744             # iterate over aliased_columns
745 65 100       116 while (my ($alias, $column) = each %{$self->{aliased_columns} || {}}) {
  86         416  
746 21         30 my $table_name;
747 21 100       102 $column =~ s{^([^()]+) # supposed table name (without parens)
748             \. # followed by a dot
749             (?=[^()]+$) # followed by supposed col name (without parens)
750             }{}x
751             and $table_name = $1;
752 21 100       43 if (!$table_name) {
753 12         33 $handlers{$alias} = $handlers{$column};
754             }
755             else {
756 9   66     28 $table_name = $aliased_tables{$table_name} || $table_name;
757              
758             my $table = $meta_schema->table($table_name)
759 16     16   53 || (firstval {($_->{db_name} || '') eq $table_name}
760             ($meta_source, $meta_source->ancestors))
761 0     0     || (firstval {uc($_->{db_name} || '') eq uc($table_name)}
762 9 50 33     31 ($meta_source, $meta_source->ancestors))
763             or croak "unknown table name: $table_name";
764              
765 9         36 $handlers{$alias} = $table->{column_handlers}->{$column};
766             }
767             }
768              
769             # handlers may be overridden from args{-column_types}
770 65 100       242 if (my $col_types = $self->{args}{-column_types}) {
771 1         8 while (my ($type_name, $columns) = each %$col_types) {
772 1 50       6 $columns = [$columns] unless does $columns, 'ARRAY';
773 1 50       15 my $type = $self->schema->metadm->type($type_name)
774             or croak "no such column type: $type_name";
775 1         11 $handlers{$_} = $type->{handlers} foreach @$columns;
776             }
777             }
778              
779             # just keep the "from_DB" handlers
780 65         131 my $from_DB_handlers = {};
781 65         262 while (my ($column, $col_handlers) = each %handlers) {
782 161 100       404 my $from_DB_handler = $col_handlers->{from_DB} or next;
783 113         280 $from_DB_handlers->{$column} = $from_DB_handler;
784             }
785 65         189 $self->{from_DB_handlers} = $from_DB_handlers;
786              
787 65         1445 return $self;
788             }
789              
790              
791             1; # End of DBIx::DataModel::Statement
792              
793             __END__