File Coverage

blib/lib/SQL/Abstract/More.pm
Criterion Covered Total %
statement 547 645 84.8
branch 227 316 71.8
condition 57 93 61.2
subroutine 69 82 84.1
pod 16 16 100.0
total 916 1152 79.5


line stmt bran cond sub pod time code
1             package SQL::Abstract::More;
2 11     11   1526503 use strict;
  11         23  
  11         397  
3 11     11   50 use warnings;
  11         20  
  11         752  
4              
5             # no "use parent ..." here -- for the moment the inheritance is specified dynamically in the
6             # import() method -- inheriting either from SQL::Abstract or SQL::Abstract::Classic.
7             # This is deprecated and will no longer be supported in the next version
8              
9 11     11   997 use mro 'c3'; # implements next::method
  11         1160  
  11         93  
10              
11 11         1206 use Params::Validate qw/validate SCALAR SCALARREF CODEREF ARRAYREF HASHREF
12 11     11   6402 UNDEF BOOLEAN/;
  11         103136  
13 11     11   102 use Scalar::Util qw/blessed reftype/;
  11         17  
  11         794  
14              
15              
16             # remove all previously defined or imported functions
17 11     11   5077 use namespace::clean;
  11         116479  
  11         73  
18              
19             # declare error-reporting functions from SQL::Abstract
20             sub puke(@); sub belch(@); # these will be defined later in import()
21              
22             our $VERSION = '1.49_01';
23             our @ISA;
24              
25             sub import {
26 14     14   1419 my $class = shift;
27              
28             # the parent class may be specified from an environment variable ...
29 14         3857 my $parent_sqla = $ENV{SQL_ABSTRACT_MORE_EXTENDS};
30              
31             # ... or from param -extends => .. when calling import() ...
32 14 100 66     1661 $parent_sqla = $_[1] if @_ >= 2 && $_[0] eq '-extends';
33              
34             # ... but choosing the parent is deprecated!
35 14 100       93 ! $parent_sqla
36             or warn "explicitly specification of $parent_sqla as parent class to SQL::Abstract::More is deprecated; "
37             . "future versions will no longer offer that possibility";
38              
39             # default parent class .. this will be mandatory in the next version. Later versions may have no parent at all.
40 14   100     80 $parent_sqla ||= 'SQL::Abstract::Classic';
41              
42             # syntactic sugar : 'Classic' is expanded into SQLA::Classic
43 14 100       34 $parent_sqla = 'SQL::Abstract::Classic' if $parent_sqla eq 'Classic';
44              
45             # make sure that import() is never called with different parents
46 14 100       72 if (my $already_isa = $ISA[0]) {
47 3 100       32 $already_isa eq $parent_sqla
48             or die "cannot use SQL::Abstract::More -extends => '$parent_sqla', "
49             . "this module was already loaded with -extends => '$already_isa'";
50              
51             # the rest of the import() job was already performed, so just return from here
52 1         19 return;
53             }
54              
55             # load the parent, inherit from it, import puke() and belch()
56 11     11   4274 eval qq{use parent '$parent_sqla';
  11         2763  
  11         62  
  11         990  
57             *puke = \\&${parent_sqla}::puke;
58             *belch = \\&${parent_sqla}::belch;
59             };
60              
61             # local override of some methods for insert() and update()
62 11         83 _setup_insert_inheritance($parent_sqla);
63 11         54 _setup_update_inheritance($parent_sqla);
64             }
65              
66              
67              
68             #----------------------------------------------------------------------
69             # Utility functions -- not methods -- declared _after_
70             # namespace::clean so that they can remain visible by external
71             # modules. In particular, DBIx::DataModel imports these functions.
72             #----------------------------------------------------------------------
73              
74             # shallow_clone(): copies of the top-level keys and values, blessed into the same class
75             sub shallow_clone {
76 6     6 1 22 my ($orig, %override) = @_;
77              
78 6 50       26 my $class = ref $orig
79             or puke "arg must be an object";
80 6         115 my $clone = {%$orig, %override};
81 6         32 return bless $clone, $class;
82             }
83              
84              
85             # does(): cheap version of Scalar::Does
86             my %meth_for = (
87             ARRAY => '@{}',
88             HASH => '%{}',
89             SCALAR => '${}',
90             CODE => '&{}',
91             );
92             sub does ($$) {
93 1424     1424 1 2550 my ($data, $type) = @_;
94 1424         2172 my $reft = reftype $data;
95             return defined $reft && $reft eq $type
96 1424   66     7008 || blessed $data && overload::Method($data, $meth_for{$type});
97             }
98              
99              
100              
101             #----------------------------------------------------------------------
102             # global variables
103             #----------------------------------------------------------------------
104              
105             # builtin methods for "Limit-Offset" dialects
106             my %limit_offset_dialects = (
107             LimitOffset => sub {my ($self, $limit, $offset) = @_;
108             $offset ||= 0;
109             return "LIMIT ? OFFSET ?", $limit, $offset;},
110             LimitXY => sub {my ($self, $limit, $offset) = @_;
111             $offset ||= 0;
112             return "LIMIT ?, ?", $offset, $limit;},
113             LimitYX => sub {my ($self, $limit, $offset) = @_;
114             $offset ||= 0;
115             return "LIMIT ?, ?", $limit, $offset;},
116             OffsetFetchRows => sub {my ($self, $limit, $offset) = @_;
117             $offset ||= 0;
118             return "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY", $offset, $limit;},
119             RowNum => sub {
120             my ($self, $limit, $offset) = @_;
121             # HACK below borrowed from SQL::Abstract::Limit. Not perfect, though,
122             # because it brings back an additional column. Should borrow from
123             # DBIx::Class::SQLMaker::LimitDialects, which does the proper job ...
124             # but it says : "!!! THIS IS ALSO HORRIFIC !!! /me ashamed"; so
125             # I'll only take it as last resort; still exploring other ways.
126             # See also L : within that ORM an additional layer is
127             # added to take advantage of Oracle scrollable cursors (for Oracle < 12c).
128             my $sql = "SELECT * FROM ("
129             . "SELECT subq_A.*, ROWNUM rownum__index FROM (%s) subq_A "
130             . "WHERE ROWNUM <= ?"
131             . ") subq_B WHERE rownum__index >= ?";
132              
133 11     11   9938 no warnings 'uninitialized'; # in case $limit or $offset is undef
  11         17  
  11         76920  
134             # row numbers start at 1
135             return $sql, $offset + $limit, $offset + 1;
136             },
137             );
138              
139             # builtin join operators with associated sprintf syntax
140             my %common_join_syntax = (
141             '<=>' => '%s INNER JOIN %s ON %s',
142             '=>' => '%s LEFT OUTER JOIN %s ON %s',
143             '<=' => '%s RIGHT OUTER JOIN %s ON %s',
144             '==' => '%s NATURAL JOIN %s',
145             '>=<' => '%s FULL OUTER JOIN %s ON %s',
146             );
147             my %right_assoc_join_syntax = %common_join_syntax;
148             s/JOIN %s/JOIN (%s)/ foreach values %right_assoc_join_syntax;
149              
150             # specification of parameters accepted by the new() method
151             my %params_for_new = (
152             table_alias => {type => SCALAR|CODEREF, default => '%s AS %s' },
153             column_alias => {type => SCALAR|CODEREF, default => '%s AS %s' },
154             limit_offset => {type => SCALAR|CODEREF, default => 'LimitOffset' },
155             join_syntax => {type => HASHREF, default => \%common_join_syntax},
156             join_assoc_right => {type => BOOLEAN, default => 0 },
157             max_members_IN => {type => SCALAR, optional => 1 },
158             multicols_sep => {type => SCALAR|SCALARREF, optional => 1 },
159             has_multicols_in_SQL => {type => BOOLEAN, optional => 1 },
160             sql_dialect => {type => SCALAR, optional => 1 },
161             select_implicitly_for => {type => SCALAR|UNDEF, optional => 1 },
162             );
163              
164             # builtin collection of parameters, for various databases
165             my %sql_dialects = (
166             MsAccess => {join_assoc_right => 1,
167             join_syntax => \%right_assoc_join_syntax},
168             BasisJDBC => {column_alias => "%s %s",
169             max_members_IN => 255 },
170             MySQL_old => {limit_offset => "LimitXY" },
171             Oracle => {limit_offset => "RowNum",
172             max_members_IN => 999,
173             table_alias => '%s %s',
174             column_alias => '%s %s',
175             has_multicols_in_SQL => 1, },
176             );
177             $sql_dialects{Oracle12c} = {%{$sql_dialects{Oracle}}, limit_offset => "OffsetFetchRows"};
178              
179              
180             # operators for compound queries
181             my @set_operators = qw/union union_all intersect minus except/;
182              
183             # specification of parameters accepted by select, insert, update, delete
184             my %params_for_select = (
185             -columns => {type => SCALAR|ARRAYREF, default => '*'},
186             -from => {type => SCALAR|SCALARREF|ARRAYREF},
187             -where => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
188             (map {-$_ => {type => ARRAYREF, optional => 1}} @set_operators),
189             -group_by => {type => SCALAR|ARRAYREF, optional => 1},
190             -having => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
191             -order_by => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
192             -page_size => {type => SCALAR, optional => 1},
193             -page_index => {type => SCALAR, optional => 1,
194             depends => '-page_size'},
195             -limit => {type => SCALAR, optional => 1},
196             -offset => {type => SCALAR, optional => 1,
197             depends => '-limit'},
198             -for => {type => SCALAR|UNDEF, optional => 1},
199             -want_details => {type => BOOLEAN, optional => 1},
200             -as => {type => SCALAR, optional => 1},
201             );
202             my %params_for_insert = (
203             -into => {type => SCALAR},
204             -values => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
205             -select => {type => HASHREF, optional => 1},
206             -columns => {type => ARRAYREF, optional => 1},
207             -returning => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
208             -add_sql => {type => SCALAR, optional => 1},
209             );
210             my %params_for_update = (
211             -table => {type => SCALAR|SCALARREF|ARRAYREF},
212             -set => {type => HASHREF},
213             -where => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
214             -order_by => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
215             -limit => {type => SCALAR, optional => 1},
216             -returning => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
217             -add_sql => {type => SCALAR, optional => 1},
218             );
219             my %params_for_delete = (
220             -from => {type => SCALAR},
221             -where => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
222             -order_by => {type => SCALAR|ARRAYREF|HASHREF, optional => 1},
223             -limit => {type => SCALAR, optional => 1},
224             -add_sql => {type => SCALAR, optional => 1},
225             );
226             my %params_for_WITH = (
227             -table => {type => SCALAR},
228             -columns => {type => SCALAR|ARRAYREF, optional => 1},
229             -as_select => {type => HASHREF},
230             -final_clause => {type => SCALAR, optional => 1},
231             );
232              
233              
234              
235             #----------------------------------------------------------------------
236             # object creation
237             #----------------------------------------------------------------------
238              
239             sub new {
240 28     28 1 2317735 my $class = shift;
241              
242             # make sure @ISA is populated, in case import() was not already called
243 28 100       142 $class->import() if !@ISA;
244              
245             # accept params either as hash or as hashref
246 28 50       115 my %params = does($_[0], 'HASH') ? %{$_[0]} : @_;
  0         0  
247              
248             # extract params for this subclass
249 28         97 my %more_params;
250 28         168 foreach my $key (keys %params_for_new) {
251 280 100       548 $more_params{$key} = delete $params{$key} if exists $params{$key};
252             }
253              
254             # import params from SQL dialect, if any ... but explict params above had precedence
255 28         125 my $dialect = delete $more_params{sql_dialect};
256 28 100       102 if ($dialect) {
257 6 50       27 my $dialect_params = $sql_dialects{$dialect}
258             or puke "no such sql dialect: $dialect";
259 6   33     78 $more_params{$_} ||= $dialect_params->{$_} foreach keys %$dialect_params;
260             }
261              
262             # check parameters for this class
263 28         111 my @more_params = %more_params;
264 28         853 my $more_self = validate(@more_params, \%params_for_new);
265              
266             # check some of the params for parent -- because SQLA doesn't do it :-(
267             !$params{quote_char} || exists $params{name_sep}
268 28 50 66     258 or belch "when 'quote_char' is present, 'name_sep' should be present too";
269              
270             # call parent constructor
271 28         175 my $self = $class->next::method(%params);
272              
273             # inject additional attributes into $self
274 28         2525 $self->{$_} = $more_self->{$_} foreach keys %$more_self;
275              
276             # arguments supplied as scalars are transformed into coderefs
277 28 50       226 ref $self->{column_alias} or $self->_make_aliasing_sub('column_alias');
278 28 50       110 ref $self->{table_alias} or $self->_make_aliasing_sub('table_alias');
279 28 100       154 ref $self->{limit_offset} or $self->_choose_LIMIT_OFFSET_dialect;
280              
281             # compute the regex for parsing join specifications - depending on 'join_syntax' attribute
282 220 50       499 my @join_ops = sort {length($b) <=> length($a) || $a cmp $b}
283 28         44 keys %{$self->{join_syntax}};
  28         265  
284 28         197 my $joined_ops = join '|', map quotemeta, @join_ops;
285 28         753 $self->{join_regex} = qr[
286             ^ # initial anchor
287             ($joined_ops)? # $1: join operator (i.e. '<=>', '=>', etc.))
288             ([[{])? # $2: opening '[' or '{'
289             (.*?) # $3: content of brackets
290             []}]? # closing ']' or '}'
291             $ # final anchor
292             ]x;
293              
294 28         242 return $self;
295             }
296              
297              
298              
299             #----------------------------------------------------------------------
300             # support for WITH or WITH RECURSIVE
301             #----------------------------------------------------------------------
302              
303             sub with {
304 6     6 1 11 my $self = shift;
305              
306             ! $self->{WITH}
307 6 50       25 or puke "calls to methods with() or with_recursive() cannot be chained";
308              
309             @_
310 6 50       17 or puke "->with() : missing arguments";
311              
312             # create a copy of the current object with an additional attribute WITH
313 6         34 my $clone = shallow_clone($self, WITH => {sql => "", bind => []});
314              
315             # assemble SQL and bind values for each table expression
316 6 100       25 my @table_expressions = does($_[0], 'ARRAY') ? @_ : ( [ @_]);
317 6         17 foreach my $table_expression (@table_expressions) {
318 7         208 my %args = validate(@$table_expression, \%params_for_WITH);
319 7         22 my @cols = does($args{-columns}, 'ARRAY') ? @{$args{-columns}}
320             : $args{-columns} ? ($args{-columns})
321 7 0       49 : ();
    50          
322 7         14 my ($sql, @bind) = $self->select(%{$args{-as_select}});
  7         31  
323 7 100       22 $clone->{WITH}{sql} .= ", " if $clone->{WITH}{sql};
324 7         21 $clone->{WITH}{sql} .= $self->_quote($args{-table});
325 7 50       146 $clone->{WITH}{sql} .= "(" . join(", ", map {$self->_quote($_)} @cols) . ")" if @cols;
  9         34  
326 7         79 $clone->{WITH}{sql} .= " AS ($sql) ";
327 7 100       17 $clone->{WITH}{sql} .= $args{-final_clause} . " " if $args{-final_clause};
328 7         11 push @{$clone->{WITH}{bind}}, @bind;
  7         25  
329             }
330              
331             # add the initial keyword WITH
332 6         14 substr($clone->{WITH}{sql}, 0, 0) = 'WITH ';
333              
334 6         20 return $clone;
335             }
336              
337             sub with_recursive {
338 5     5 1 40388 my $self = shift;
339              
340 5         22 my $new_instance = $self->with(@_);
341 5         21 $new_instance->{WITH}{sql} =~ s/^WITH\b/WITH RECURSIVE/;
342              
343 5         24 return $new_instance;
344             }
345              
346             sub _prepend_WITH_clause {
347 193     193   303 my ($self, $ref_sql, $ref_bind) = @_;
348              
349 193 100       512 return if !$self->{WITH};
350              
351 10         32 substr($$ref_sql, 0, 0) = $self->{WITH}{sql};
352 10         15 unshift @$ref_bind, @{$self->{WITH}{bind}};
  10         20  
353             }
354              
355              
356             #----------------------------------------------------------------------
357             # the select method
358             #----------------------------------------------------------------------
359              
360             sub select {
361 140     140 1 322059 my $self = shift;
362              
363             # if this method was called with positional args, just delegate to the parent
364 140 100       377 return $self->next::method(@_) if !&_called_with_named_args;
365              
366             # parse arguments
367 136         5136 my %args = validate(@_, \%params_for_select);
368              
369             # infrastructure for collecting fragments of sql and bind args
370 136         883 my ($sql, @bind) = ("");
371 136     194   670 my $add_sql_bind = sub {$sql .= shift; push @bind, @_}; # closure to add to ($sql, @bind)
  194         3713  
  194         380  
372              
373             # parse columns and datasource
374 136         500 my $from = $self->_parse_datasource($args{-from});
375 136         381 my ($cols, $post_select, $cols_bind, $aliased_columns) = $self->_parse_columns($args{-columns});
376              
377             # parse the WHERE conditions
378 136         536 my ($where_sql, @where_bind) = $self->where($args{-where});
379            
380             # assemble the SELECT statement
381 136 50       18036 my $fields = ref $cols ? join ", ", @$cols : $cols;
382 136         384 my $select_sql = join(' ', $self->_sqlcase('select'), $fields, $self->_sqlcase('from'), $from->{sql}) . $where_sql;
383 136         1009 $add_sql_bind->($select_sql, @$cols_bind, @{$from->{bind}}, @where_bind);
  136         445  
384            
385             # add @post_select clauses if needed (for ex. -distinct)
386 136         222 my $all_post_select = join " ", @$post_select;
387 136 100       326 $sql =~ s[^SELECT ][SELECT $all_post_select ]i if $all_post_select;
388              
389             # add set operators (UNION, INTERSECT, etc) if needed
390 136         244 foreach my $set_op (@set_operators) {
391 680 100       1413 if (my $val_set_op = $args{-$set_op}) {
392 16         57 my ($sql_set_op, @bind_set_op) = $self->_parse_set_operator($set_op => $val_set_op, $args{-columns}, $args{-from});
393 16         31 $add_sql_bind->($sql_set_op, @bind_set_op);
394             }
395             }
396            
397             # add GROUP BY if needed
398 136 100       290 if ($args{-group_by}) {
399 8         24 my $sql_grp = $self->where(undef, $args{-group_by});
400 8         1419 $sql_grp =~ s/\bORDER\b/GROUP/i;
401 8         21 $add_sql_bind->($sql_grp);
402             }
403              
404             # add HAVING if needed (often together with -group_by, but not always)
405 136 100       287 if ($args{-having}) {
406 5         15 my ($sql_having, @bind_having) = $self->where($args{-having});
407 5         1728 $sql_having =~ s/\bWHERE\b/HAVING/i;
408 5         20 $add_sql_bind->(" $sql_having", @bind_having);
409             }
410              
411             # add ORDER BY if needed
412 136 100       320 if (my $order = $args{-order_by}) {
413 15         44 $add_sql_bind->($self->_order_by($order));
414             }
415              
416             # add pagination if needed (either -page_* args or -limit/-offset)
417 136 100 66     549 $self->_translate_page_into_limit_offset(\%args) if $args{-page_index} or $args{-page_size};
418 136 100       327 if (defined $args{-limit}) {
419 10         67 my ($limit_sql, @limit_bind) = $self->limit_offset(@args{qw/-limit -offset/});
420 10 100       62 if ($limit_sql =~ /%s/) {
421 2         7 $sql = sprintf $limit_sql, $sql; # rewrite the whole $sql
422 2         6 push @bind, @limit_bind;
423             }
424             else {
425 8         40 $add_sql_bind->(" $limit_sql", @limit_bind);
426             }
427             }
428              
429             # add FOR clause if needed
430 136 100       277 my $for = exists $args{-for} ? $args{-for} : $self->{select_implicitly_for};
431 136 100       238 $add_sql_bind->(" FOR $for") if $for;
432              
433             # add alias if select() is used as a subquery
434 136 100       244 if (my $alias = $args{-as}) {
435 8         18 $sql = "($sql)|$alias";
436             }
437              
438             # initial WITH clause
439 136         387 $self->_prepend_WITH_clause(\$sql, \@bind);
440              
441             # return results
442             return $args{-want_details} ? {aliased_tables => $from->{aliased_tables},
443 136 100       1422 aliased_columns => $aliased_columns,
444             sql => $sql,
445             bind => \@bind}
446             : ($sql, @bind);
447             }
448              
449              
450             sub _parse_columns {
451 136     136   264 my ($self, $columns) = @_;
452              
453             # the -columns arg can be an arrayref or a plain scalar => unify into an array
454 136 100       488 my @cols = ref $columns ? @$columns : ($columns);
455              
456             # initial members of the columns list starting with "-" are extracted into a
457             # separate list @post_select, later re-injected into the SQL (for ex. '-distinct')
458 136         226 my @post_select;
459 136   66     709 push @post_select, shift @cols while @cols && $cols[0] =~ s/^-//;
460              
461             # loop over columns, handling aliases and subqueries
462 136         222 my @cols_bind;
463             my %aliased_columns;
464 136         259 foreach my $col (@cols) {
465             # deal with subquery of shape \ [$sql, @bind]
466 205 100       367 if (_is_subquery($col)) {
467 6         18 my ($sql, @col_bind) = @$$col;
468 6         15 $col = _parenthesize_select($sql);
469 6         18 push @cols_bind, @col_bind;
470             }
471              
472             # check for a column alias; if present, register it in the alias table
473 205         435 ($col, my $alias) = $self->_parse_alias($col);
474 205 100       479 $aliased_columns{$alias} = $col if $alias ;
475              
476             # quote the column SQL if necessary - excluding expressions with parens or commas
477             # (such as SQL functions or parenthesized subqueries)
478 205 100       780 $col = $self->_quote($col) unless $col =~ /[(),]/;
479              
480             # insert SQL aliasing if necessary
481 205 100       3352 $col = $self->column_alias($col, $self->_quote($alias)) if $alias;
482             }
483              
484 136         478 return (\@cols, \@post_select, \@cols_bind, \%aliased_columns);
485             }
486            
487              
488              
489              
490              
491             sub _parse_set_operator {
492 16     16   59 my ($self, $set_op, $val_set_op, $cols, $from) = @_;
493              
494 16         59 my %sub_args = @$val_set_op;
495 16   66     47 $sub_args{-columns} ||= $cols;
496 16   66     46 $sub_args{-from} ||= $from;
497 16         31 local $self->{WITH}; # temporarily disable the WITH part during the recursive call to select()
498 16         86 my ($sql, @bind) = $self->select(%sub_args);
499 16         64 (my $sql_op = uc($set_op)) =~ s/_/ /g; # for ex. -union_all becomes 'UNION ALL'
500 16         78 return (" $sql_op $sql", @bind);
501             }
502              
503              
504             sub _translate_page_into_limit_offset {
505 2     2   6 my ($self, $args) = @_;
506              
507 2   33     53 not exists $args->{$_} or puke "-page_size conflicts with $_" for qw/-limit -offset/;
508 2         7 $args->{-limit} = $args->{-page_size};
509 2 50       6 if ($args->{-page_index}) {
510 2         22 $args->{-offset} = ($args->{-page_index} - 1) * $args->{-page_size};
511             }
512             }
513              
514              
515              
516             #----------------------------------------------------------------------
517             # insert
518             #----------------------------------------------------------------------
519              
520             sub _setup_insert_inheritance {
521 11     11   42 my ($parent_sqla) = @_;
522              
523             # if the parent has method '_expand_insert_value' (SQL::Abstract >= v2.0),
524             # we need to override it in this subclass
525 11 100       261 if ($parent_sqla->can('_expand_insert_value')) {
    50          
526             *_expand_insert_value = sub {
527 0     0   0 my ($self, $v) = @_;
528              
529 0         0 my $k = our $Cur_Col_Meta;
530              
531 0 0       0 if (ref($v) eq 'ARRAY') {
532 0 0 0     0 if ($self->{array_datatypes} || $self->is_bind_value_with_type($v)) {
533 0         0 return +{ -bind => [ $k, $v ] };
534             }
535 0         0 my ($sql, @bind) = @$v;
536 0         0 $self->_assert_bindval_matches_bindtype(@bind);
537 0         0 return +{ -literal => $v };
538             }
539 0 0       0 if (ref($v) eq 'HASH') {
540 0 0       0 if (grep !/^-/, keys %$v) {
541 0         0 belch "HASH ref as bind value in insert is not supported";
542 0         0 return +{ -bind => [ $k, $v ] };
543             }
544             }
545 0 0       0 if (!defined($v)) {
546 0         0 return +{ -bind => [ $k, undef ] };
547             }
548 0         0 return $self->expand_expr($v);
549 1         6 };
550              
551             # we also need to back-implement the _insert_values method from earlier versions of SQLA
552             *_insert_values = sub {
553 0     0   0 my $self = shift;
554 0         0 my ($sql, @bind) = $self->SUPER::insert(FAKE_TABLE => @_);
555 0         0 $sql =~ s/^.*\bVALUES\b/VALUES/i;
556 0         0 return ($sql, @bind);
557 1         3 };
558            
559             }
560              
561             # otherwise, if the parent is an old SQL::Abstract or it is SQL::Abstract::Classic
562             elsif ($parent_sqla->can('_insert_values')) {
563              
564             # if the parent has no method '_insert_value', this is the old
565             # monolithic _insert_values() method. We must override it
566 10 50       83 if (!$parent_sqla->can('_insert_value')) {
567             *_insert_values = sub {
568 20     20   310 my ($self, $data) = @_;
569              
570 20         30 my (@values, @all_bind);
571 20         62 foreach my $column (sort keys %$data) {
572 38         91 my ($values, @bind) = $self->_insert_value($column, $data->{$column});
573 38         62 push @values, $values;
574 38         70 push @all_bind, @bind;
575             }
576 20         39 my $sql = $self->_sqlcase('values')." ( ".join(", ", @values)." )";
577 20         121 return ($sql, @all_bind);
578 10         83 };
579             }
580              
581             # now override the _insert_value() method
582             *_insert_value = sub {
583              
584             # unfortunately, we can't just override the ARRAYREF part, so the whole
585             # parent method is copied here
586              
587 38     38   75 my ($self, $column, $v) = @_;
588              
589 38         46 my (@values, @all_bind);
590             $self->_SWITCH_refkind($v, {
591              
592             ARRAYREF => sub {
593 2 50 33 2   65 if ($self->{array_datatypes} # if array datatype are activated
594             || $self->is_bind_value_with_type($v)) { # or if this is a bind val
595 2         3 push @values, '?';
596 2         7 push @all_bind, $self->_bindtype($column, $v);
597             }
598             else { # else literal SQL with bind
599 0         0 my ($sql, @bind) = @$v;
600 0         0 $self->_assert_bindval_matches_bindtype(@bind);
601 0         0 push @values, $sql;
602 0         0 push @all_bind, @bind;
603             }
604             },
605              
606             ARRAYREFREF => sub { # literal SQL with bind
607 0     0   0 my ($sql, @bind) = @${$v};
  0         0  
608 0         0 $self->_assert_bindval_matches_bindtype(@bind);
609 0         0 push @values, $sql;
610 0         0 push @all_bind, @bind;
611             },
612              
613             # THINK : anything useful to do with a HASHREF ?
614             HASHREF => sub { # (nothing, but old SQLA passed it through)
615             #TODO in SQLA >= 2.0 it will die instead
616 0     0   0 belch "HASH ref as bind value in insert is not supported";
617 0         0 push @values, '?';
618 0         0 push @all_bind, $self->_bindtype($column, $v);
619             },
620              
621             SCALARREF => sub { # literal SQL without bind
622 0     0   0 push @values, $$v;
623             },
624              
625             SCALAR_or_UNDEF => sub {
626 36     36   946 push @values, '?';
627 36         83 push @all_bind, $self->_bindtype($column, $v);
628             },
629              
630 38         437 });
631              
632 38         558 my $sql = CORE::join(", ", @values);
633 38         128 return ($sql, @all_bind);
634             }
635 10         66 }
636             else {
637 0         0 puke "unexpected parent class, cannot setup inheritance rules for insert()";
638             }
639             }
640              
641              
642              
643             sub insert {
644 25     25 1 60472 my $self = shift;
645              
646 25         66 my ($sql, @bind) = ("");
647 25     69   121 my $add_sql_bind = sub {$sql .= shift; push @bind, @_}; # closure for adding to ($sql, @bind)
  69         156  
  69         153  
648              
649 25 100       64 if (not &_called_with_named_args) {
650 2         11 ($sql, @bind) = $self->next::method(@_);
651             }
652             else {
653             # extract named args
654 23         1110 my %args = validate(@_, \%params_for_insert);
655              
656 21 50       143 $args{-into} or puke "insert(..) : need -into arg";
657 21         74 my $source = $self->_parse_datasource($args{-into});
658             $add_sql_bind->($self->_sqlcase('insert into') . " " . $source->{sql},
659 21         62 @{$source->{bind}}, # in principle always empty for an insert, but just for consistency's sake
  21         179  
660             );
661            
662 21 100       62 if (my $values = $args{-values}) {
    50          
663 18 50       72 !$args{-select} or puke "insert(-into => .., -values => ...) : incompatible with -select => ";
664              
665 18 100       35 if (does($values, 'HASH')) {
    50          
666 14 50       87 !$args{-columns} or puke "insert(-into => .., -values => {...}) : incompatible with -columns => ";
667 14         66 my $quoted_cols = join ", ", map {$self->_quote($_)} sort keys %$values;
  26         260  
668 14         260 $add_sql_bind->("($quoted_cols)");
669             }
670             elsif (does($values, 'ARRAY')) {
671 4 100       12 if (my $cols = $args{-columns}) {
672 2 50       8 $cols = [$cols] if ! ref $cols;
673 2 50       7 @$cols == @$values or puke "insert(-into => .., -columns => [...], -values => [...]) : numbers of columns do not match";
674 2         5 my %merged; @merged{@$cols} = @$values;
  2         27  
675 2         5 $values = \%merged;
676             # NOTE: in doing so we lost the order of @$cols. In principle this has no impact on the final
677             # result, but nevertheless is not very satisfactory. In a future version when we no longer need
678             # to support SQLA 2.0, we should have our own version of $self->_insert_cols_values($cols, $values);
679              
680 2         10 my $quoted_cols = join ", ", map {$self->_quote($_)} sort @$cols;
  4         45  
681 2         38 $add_sql_bind->("($quoted_cols)");
682             }
683             else {
684             # Code and comments below borrowed from SQL::Abstract!
685             #
686             # fold the list of values into a hash of column name - value pairs
687             # (where the column names are artificially generated, and their
688             # lexicographical ordering keep the ordering of the original list)
689 2         4 my $i = "a"; # incremented values will be in lexicographical order
690 2         7 $values = { map { ($i++ => $_) } @$values };
  4         15  
691             }
692             }
693             else {
694 0         0 puke "insert(-into => .., -values => ...) : -values must be a hashref or an arrayref";
695             }
696              
697 18         59 my ($val_sql, @val_bind) = $self->_insert_values($values);
698 18         47 $add_sql_bind->(" $val_sql", @val_bind);
699             }
700             elsif ($args{-select}) {
701 3         8 local $self->{WITH}; # temporarily disable the WITH part during the subquery
702 3 50       13 if (my $cols = $args{-columns}) {
703 3 50       8 $cols = [$cols] if ! ref $cols;
704 3         7 my $quoted_cols = join ", ", map {$self->_quote($_)} @$cols;
  5         47  
705 3         73 $add_sql_bind->("($quoted_cols)");
706             }
707 3         6 my ($select_sql, @select_bind) = $self->select(%{$args{-select}});
  3         57  
708 3         12 $add_sql_bind->(" $select_sql", @select_bind);
709             }
710             else {
711 0         0 puke "insert(-into => ..) : need either -values arg or -select arg";
712             }
713              
714             # THINK: should also support some syntax for generating INSERT INTO Foo(a, b, c) VALUES (1, 2, 3), (4, 5, 6), ...
715             # Probably a variant like -columns => [qw/a b c/], -values => [[1, 2, 3], [4, 5, 6], ..]
716              
717             # deal with -returning arg
718 21 100       44 if ($args{-returning}) {
719 6         17 my ($into, $returning) = $self->_compute_returning($args{-returning});
720 6         22 my ($r_sql, @r_bind) = $self->_insert_returning($returning); # TODO : implement directly instead of calling SQLA
721 6         372 $add_sql_bind->($r_sql, @r_bind);
722            
723             # inject more stuff if using Oracle's "RETURNING ... INTO ..."
724 6 100       15 if ($into) {
725 2         5 $add_sql_bind->($self->_sqlcase(' into ') . join(", ", ("?") x @$into),
726             @$into);
727             }
728             }
729            
730             # SQL to add after the INSERT keyword
731 21 100       118 $sql =~ s/\b(INSERT)\b/$1 $args{-add_sql}/i if $args{-add_sql};
732             };
733              
734             # initial WITH clause
735 23         179 $self->_prepend_WITH_clause(\$sql, \@bind);
736              
737 23         136 return ($sql, @bind);
738             }
739              
740             #----------------------------------------------------------------------
741             # update
742             #----------------------------------------------------------------------
743              
744              
745             sub _setup_update_inheritance {
746 11     11   35 my ($parent_sqla) = @_;
747              
748             # if the parent has method '_expand_update_set_value' (SQL::Abstract >= v2.0),
749             # we need to override it in this subclass
750 11 100       71 if ($parent_sqla->can('_expand_update_set_values')) {
751 1         3 *_parent_update = $parent_sqla->can('update');
752             *_expand_update_set_values = sub {
753 0     0   0 my ($self, undef, $data) = @_;
754             $self->expand_expr({ -list => [
755             map {
756 0         0 my ($k, $set) = @$_;
757 0 0       0 $set = { -bind => $_ } unless defined $set;
758 0         0 +{ -op => [ '=', { -ident => $k }, $set ] };
759             }
760             map {
761 0         0 my $k = $_;
  0         0  
762 0         0 my $v = $data->{$k};
763             (ref($v) eq 'ARRAY'
764             ? ($self->{array_datatypes} || $self->is_bind_value_with_type($v)
765             ? [ $k, +{ -bind => [ $k, $v ] } ]
766             : [ $k, +{ -literal => $v } ])
767 0 0 0     0 : do {
    0          
768 0         0 local our $Cur_Col_Meta = $k;
769 0         0 [ $k, $self->_expand_expr($v) ]
770             }
771             );
772             } sort keys %$data
773             ] });
774 1         25 };
775             }
776              
777              
778             # otherwise, if the parent is an old SQL::Abstract or it is SQL::Abstract::Classic
779             else {
780             # if the parent has method '_update_set_values()', it is a SQLA version >=1.85.
781             # We can just use its update() method as _parent_update().
782 10 50       60 if ($parent_sqla->can('_update_set_values')) {
783 0         0 *_parent_update = $parent_sqla->can('update');
784             }
785              
786             # otherwise, it's the old monolithic update() method. We need to supply our own
787             # version as _parent_update().
788             else {
789             *_parent_update = sub {
790 0     0   0 my $self = shift;
791 0         0 my $table = $self->_table(shift);
792 0   0     0 my $data = shift || return;
793 0         0 my $where = shift;
794 0         0 my $options = shift;
795              
796             # first build the 'SET' part of the sql statement
797 0 0       0 puke "Unsupported data type specified to \$sql->update"
798             unless ref $data eq 'HASH';
799              
800 0         0 my ($sql, @all_bind) = $self->_update_set_values($data);
801 0         0 $sql = $self->_sqlcase('update ') . $table . $self->_sqlcase(' set ')
802             . $sql;
803              
804 0 0       0 if ($where) {
805 0         0 my($where_sql, @where_bind) = $self->where($where);
806 0         0 $sql .= $where_sql;
807 0         0 push @all_bind, @where_bind;
808             }
809              
810 0 0       0 if ($options->{returning}) {
811 0         0 my ($returning_sql, @returning_bind) = $self->_update_returning($options);
812 0         0 $sql .= $returning_sql;
813 0         0 push @all_bind, @returning_bind;
814             }
815              
816 0 0       0 return wantarray ? ($sql, @all_bind) : $sql;
817 10         65 };
818             *_update_returning = sub {
819 6     6   11 my ($self, $options) = @_;
820              
821 6         11 my $f = $options->{returning};
822              
823             my $fieldlist = $self->_SWITCH_refkind($f, {
824 4     4   137 ARRAYREF => sub {join ', ', map { $self->_quote($_) } @$f;},
  8         82  
825 2     2   85 SCALAR => sub {$self->_quote($f)},
826 0     0   0 SCALARREF => sub {$$f},
827 6         53 });
828 6         137 return $self->_sqlcase(' returning ') . $fieldlist;
829 10         51 };
830             }
831              
832             # now override or supply the _update_set_values() method
833             *_update_set_values = sub {
834 21     21   45 my ($self, $data) = @_;
835              
836 21         32 my (@set, @all_bind);
837 21         79 for my $k (sort keys %$data) {
838 31         245 my $v = $data->{$k};
839 31         81 my $r = ref $v;
840 31         103 my $label = $self->_quote($k);
841              
842             $self->_SWITCH_refkind($v, {
843             ARRAYREF => sub {
844 2 50 33 2   73 if ($self->{array_datatypes} # array datatype
845             || $self->is_bind_value_with_type($v)) { # or bind value with type
846 2         7 push @set, "$label = ?";
847 2         7 push @all_bind, $self->_bindtype($k, $v);
848             }
849             else { # literal SQL with bind
850 0         0 my ($sql, @bind) = @$v;
851 0         0 $self->_assert_bindval_matches_bindtype(@bind);
852 0         0 push @set, "$label = $sql";
853 0         0 push @all_bind, @bind;
854             }
855             },
856             ARRAYREFREF => sub { # literal SQL with bind
857 0     0   0 my ($sql, @bind) = @${$v};
  0         0  
858 0         0 $self->_assert_bindval_matches_bindtype(@bind);
859 0         0 push @set, "$label = $sql";
860 0         0 push @all_bind, @bind;
861             },
862             SCALARREF => sub { # literal SQL without bind
863 0     0   0 push @set, "$label = $$v";
864             },
865             HASHREF => sub {
866 0     0   0 my ($op, $arg, @rest) = %$v;
867              
868 0 0 0     0 puke 'Operator calls in update must be in the form { -op => $arg }'
869             if (@rest or not $op =~ /^\-(.+)/);
870              
871 0         0 local $self->{_nested_func_lhs} = $k;
872 0         0 my ($sql, @bind) = $self->_where_unary_op($1, $arg);
873              
874 0         0 push @set, "$label = $sql";
875 0         0 push @all_bind, @bind;
876             },
877             SCALAR_or_UNDEF => sub {
878 29     29   992 push @set, "$label = ?";
879 29         78 push @all_bind, $self->_bindtype($k, $v);
880             },
881 31         1165 });
882             }
883             # generate sql
884 21         416 my $sql = CORE::join ', ', @set;
885 21         81 return ($sql, @all_bind);
886 10         442 };
887             }
888             }
889              
890             sub update {
891 23     23 1 73068 my $self = shift;
892              
893 23         49 my ($sql, @bind);
894              
895 23 100       57 if (not &_called_with_named_args) {
896 2         15 ($sql, @bind) = $self->next::method(@_);
897             }
898             else {
899 21         570 my %args = validate(@_, \%params_for_update);
900              
901             # first build the 'SET' part of the sql statement
902 21 50       143 does($args{-set}, 'HASH') or puke "Unsupported data type specified to \$sql->update";
903 21         76 ($sql, @bind) = $self->_update_set_values($args{-set});
904              
905             # add the initial part of the statement
906 21         70 my $join_info = $self->_compute_join_info($args{-table});
907             my $table = defined $join_info ? $join_info->{sql}
908 21 100       74 : $self->_parse_table($args{-table})->{sql};
909 21         74 substr($sql, 0, 0) = $self->_sqlcase('update ') . $table . $self->_sqlcase(' set ');
910              
911             # add the 'WHERE' part
912 21 100       244 if ($args{-where}) {
913 9         33 my ($where_sql, @where_bind) = $self->where($args{-where});
914 9         1281 $sql .= $where_sql;
915 9         17 push @bind, @where_bind;
916             }
917              
918             # deal with -returning arg
919 21         65 my ($into, $returning) = $self->_compute_returning($args{-returning});
920 21 100       56 $sql .= $self->_update_returning($returning) if $returning;
921              
922             # merge with bind values from $join_info
923 21 100       66 unshift @bind, @{$join_info->{bind}} if $join_info;
  2         4  
924              
925             # handle additional args if needed
926 21         109 $self->_handle_additional_args_for_update_delete(\%args, \$sql, \@bind, qr/UPDATE/);
927              
928             # deal with Oracle's "RETURNING ... INTO ..."
929 21 100       93 if ($into) {
930 2         9 $sql .= ' INTO ' . join(", ", ("?") x @$into);
931 2         7 push @bind, @$into;
932             }
933             }
934            
935              
936             # initial WITH clause
937 23         918 $self->_prepend_WITH_clause(\$sql, \@bind);
938              
939 23         124 return ($sql, @bind);
940             }
941              
942              
943              
944              
945              
946              
947             #----------------------------------------------------------------------
948             # delete
949             #----------------------------------------------------------------------
950              
951             sub delete {
952 11     11 1 27832 my $self = shift;
953              
954 11         21 my ($sql, @bind);
955              
956 11 100       28 if (not &_called_with_named_args) {
957 2         14 ($sql, @bind) = $self->next::method(@_);
958             }
959             else {
960 9         233 my %args = validate(@_, \%params_for_delete);
961 9         74 ($sql, @bind) = $self->where($args{-where});
962 9         1583 substr($sql, 0, 0) = $self->_sqlcase('delete from ') . $self->_parse_table($args{-from})->{sql};
963              
964             # handle additional args if needed
965 9         62 $self->_handle_additional_args_for_update_delete(\%args, \$sql, \@bind, qr/DELETE/);
966             }
967            
968             # initial WITH clause
969 11         543 $self->_prepend_WITH_clause(\$sql, \@bind);
970              
971 11         62 return ($sql, @bind);
972             }
973              
974              
975              
976             #----------------------------------------------------------------------
977             # auxiliary methods for insert(), update() and delete()
978             #----------------------------------------------------------------------
979              
980             sub _compute_returning {
981 27     27   62 my ($self, $arg_returning) = @_;
982              
983 27         43 my ($returning_into, $old_API_options);
984              
985 27 100       90 if ($arg_returning) {
986             # if present, "-returning" may be a scalar, arrayref or hashref; the latter
987             # is interpreted as .. RETURNING ... INTO ...
988              
989 12 100       23 if (does $arg_returning, 'HASH') {
990 4 50       24 my @keys = sort keys %$arg_returning
991             or puke "-returning => {} : the hash is empty";
992              
993 4         14 $old_API_options = {returning => \@keys};
994 4         8 $returning_into = [@{$arg_returning}{@keys}];
  4         12  
995             }
996             else {
997 8         20 $old_API_options = {returning => $arg_returning};
998             }
999             }
1000              
1001 27         58 return ($returning_into, $old_API_options);
1002             }
1003              
1004              
1005             sub _handle_additional_args_for_update_delete {
1006 30     30   66 my ($self, $args, $sql_ref, $bind_ref, $keyword_regex) = @_;
1007              
1008 30 100       90 if (defined $args->{-order_by}) {
1009 4         13 my ($sql_ob, @bind_ob) = $self->_order_by($args->{-order_by});
1010 4         671 $$sql_ref .= $sql_ob;
1011 4         7 push @$bind_ref, @bind_ob;
1012             }
1013 30 100       77 if (defined $args->{-limit}) {
1014             # can't call $self->limit_offset(..) because there shouldn't be any offset
1015 4         9 $$sql_ref .= $self->_sqlcase(' limit ?');
1016 4         17 push @$bind_ref, $args->{-limit};
1017             }
1018 30 100       66 if (defined $args->{-add_sql}) {
1019 4         120 $$sql_ref =~ s/\b($keyword_regex)\b/$1 $args->{-add_sql}/i;
1020             }
1021             }
1022              
1023              
1024             sub _order_by {
1025 71     71   82565 my ($self, $order) = @_;
1026              
1027             # force scalar into an arrayref
1028 71 100       186 $order = [$order] if not ref $order;
1029              
1030             # restructure array data
1031 71 100       173 if (does $order, 'ARRAY') {
1032 59         156 my @clone = @$order; # because we will modify items
1033              
1034             # '-' and '+' prefixes are translated into {-desc/asc => } hashrefs
1035 59         92 foreach my $item (@clone) {
1036 89 100 100     269 next if !$item or ref $item;
1037 55 100 100     191 $item =~ s/^-// and $item = {-desc => $item} and next;
1038 51 100       146 $item =~ s/^\+// and $item = {-asc => $item};
1039             }
1040 59         100 $order = \@clone;
1041             }
1042              
1043 71         264 return $self->next::method($order);
1044             }
1045              
1046             #----------------------------------------------------------------------
1047             # other public methods
1048             #----------------------------------------------------------------------
1049              
1050             # same pattern for 3 invocation methods
1051 45     45 1 4212 sub table_alias {my $self = shift; my $meth = $self->{table_alias}; $self->$meth(@_)}
  45         92  
  45         118  
1052 64     64 1 10859 sub column_alias {my $self = shift; my $meth = $self->{column_alias}; $self->$meth(@_)}
  64         146  
  64         155  
1053 18     18 1 9253 sub limit_offset {my $self = shift; my $meth = $self->{limit_offset}; $self->$meth(@_)}
  18         37  
  18         55  
1054              
1055              
1056              
1057             # readonly accessor methods
1058             foreach my $key (qw/join_syntax join_assoc_right
1059             max_members_IN multicols_sep has_multicols_in_SQL/) {
1060 11     11   182 no strict 'refs';
  11         29  
  11         22736  
1061 2     2   3467 *{$key} = sub {shift->{$key}};
1062             }
1063              
1064              
1065             # invocation method for 'join'
1066             sub join {
1067 47     47 1 65812 my $self = shift;
1068              
1069             # start from the right if right-associative
1070 47 100       144 @_ = reverse @_ if $self->{join_assoc_right};
1071              
1072             # shift first single item (a table) before reducing pairs (op, table)
1073 47         75 my $first_source = shift;
1074 47         135 my $accumulator = $self->_parse_datasource($first_source);
1075              
1076             # reduce pairs (op, table)
1077 47         126 while (@_) {
1078             # shift 2 items : next join specification and next table
1079 54         87 my $join_spec = shift;
1080 54 50       158 my $next_source = shift or puke "->join(): improper number of operands";
1081              
1082 54 100       198 $join_spec = $self->_parse_join_spec($join_spec) unless ref $join_spec;
1083 54         124 $next_source = $self->_parse_datasource($next_source);
1084 54         152 $accumulator = $self->_single_join($accumulator, $join_spec, $next_source);
1085             }
1086              
1087 46         167 return $accumulator; # {sql=> .., bind => [..], aliased_tables => {..}}
1088             }
1089              
1090              
1091             # utility for merging several "where" clauses
1092             sub merge_conditions {
1093 2     2 1 5045 my $self = shift;
1094 2         5 my %merged;
1095              
1096 2         7 foreach my $cond (@_) {
1097 4 50       9 if (does $cond, 'HASH') {
    0          
    0          
1098 4         15 foreach my $col (sort keys %$cond) {
1099             $merged{$col} = $merged{$col} ? [-and => $merged{$col}, $cond->{$col}]
1100 8 100       27 : $cond->{$col};
1101             }
1102             }
1103             elsif (does $cond, 'ARRAY') {
1104 0 0       0 $merged{-nest} = $merged{-nest} ? {-and => [$merged{-nest}, $cond]}
1105             : $cond;
1106             }
1107             elsif ($cond) {
1108 0         0 $merged{$cond} = \"";
1109             }
1110             }
1111 2         8 return \%merged;
1112             }
1113              
1114             # utility for calling either bind_param or bind_param_inout
1115             our $INOUT_MAX_LEN = 99; # chosen arbitrarily; see L
1116             sub bind_params {
1117 0     0 1 0 my ($self, $sth, @bind) = @_;
1118 0 0       0 $sth->isa('DBI::st') or puke "sth argument is not a DBI statement handle";
1119 0         0 foreach my $i (0 .. $#bind) {
1120 0         0 my $val = $bind[$i];
1121 0 0 0     0 if (does $val, 'SCALAR') {
    0          
1122             # a scalarref is interpreted as an INOUT parameter
1123 0         0 $sth->bind_param_inout($i+1, $val, $INOUT_MAX_LEN);
1124             }
1125             elsif (does $val, 'ARRAY' and
1126             my ($bind_meth, @args) = $self->is_bind_value_with_type($val)) {
1127             # either 'bind_param' or 'bind_param_inout', with 2 or 3 args
1128 0         0 $sth->$bind_meth($i+1, @args);
1129             }
1130             else {
1131             # other cases are passed directly to DBI::bind_param
1132 0         0 $sth->bind_param($i+1, $val);
1133             }
1134             }
1135             }
1136              
1137             sub is_bind_value_with_type {
1138 18     18 1 34 my ($self, $val) = @_;
1139              
1140             # compatibility with DBIx::Class syntax of shape [\%args => $val],
1141             # see L
1142 18 100 66     59 if ( @$val == 2
      100        
1143             && does($val->[0], 'HASH')
1144 40         81 && grep {$val->[0]{$_}} qw/dbd_attrs sqlt_size
1145             sqlt_datatype dbic_colname/) {
1146 8         11 my $args = $val->[0];
1147 8 50       22 if (my $attrs = $args->{dbd_attrs}) {
    0          
1148 8         30 return (bind_param => $val->[1], $attrs);
1149             }
1150             elsif (my $size = $args->{sqlt_size}) {
1151 0         0 return (bind_param_inout => $val, $size);
1152             }
1153             # other options like 'sqlt_datatype', 'dbic_colname' are not supported
1154             else {
1155 0         0 puke "unsupported options for bind type : " . CORE::join(", ", sort keys %$args);
1156             }
1157              
1158             # NOTE : the following DBIx::Class shortcuts are not supported
1159             # [ $name => $val ] === [ { dbic_colname => $name }, $val ]
1160             # [ \$dt => $val ] === [ { sqlt_datatype => $dt }, $val ]
1161             # [ undef, $val ] === [ {}, $val ]
1162             }
1163              
1164             # in all other cases, this is not a bind value with type
1165 10         22 return ();
1166             }
1167              
1168             #----------------------------------------------------------------------
1169             # private utility methods for 'join'
1170             #----------------------------------------------------------------------
1171              
1172             sub _compute_join_info {
1173 279     279   487 my ($self, $table_arg) = @_;
1174              
1175 279 100 100     589 if (does($table_arg, 'ARRAY') && $table_arg->[0] eq '-join') {
1176 22         63 return $self->join(@{$table_arg}[1 .. $#$table_arg]); # drop initial '-join'
  22         111  
1177             }
1178             else {
1179 257         915 return;
1180             }
1181             }
1182              
1183              
1184             sub _parse_alias {
1185 475     475   838 my ($self, $input_sql) = @_;
1186              
1187 475 100       2430 return $input_sql =~ /^\s* # ignore insignificant leading spaces
1188             (\S.*?) # $1: any non-empty string, no starting with space (but may include '|')
1189             (?
1190             \| # literal '|' (the last in the string)
1191             (\pL[\w\s]*?) # $2 : alias name: initial letter, then word chars or spaces
1192             $/x
1193              
1194             # sql alias
1195             # === =====
1196             ? ($1, $2 )
1197             : ($input_sql, undef);
1198             }
1199              
1200              
1201             sub _parse_datasource {
1202 258     258   538 my ($self, $source) = @_;
1203              
1204 258 100       694 if (my $join_info = $self->_compute_join_info($source)) {
    100          
    100          
    100          
1205 20         38 return $join_info;
1206             }
1207             elsif (_is_subquery($source)) {
1208             # separate the $sql and @bind parts
1209 8         22 my ($sql, @bind) = @$$source;
1210              
1211 8         20 my $table_spec = $self->_parse_table(_parenthesize_select($sql), -dont_quote_table);
1212 8         41 $table_spec->{bind} = \@bind;
1213 8         24 return $table_spec;
1214             }
1215             elsif (does($source, 'ARRAY')) {
1216             # compatibility with old SQL::Abstract -- deprecated
1217 4         9 my @table_specs = map {$self->_parse_table($_)} @$source;
  8         20  
1218 8         22 return {sql => (CORE::join ', ', map {$_->{sql}} @table_specs),
1219 8         12 bind => [ map {@{$_->{bind}}} @table_specs],
  8         21  
1220 4         10 aliased_tables => { map {%{$_->{aliased_tables}}} @table_specs}};
  8         11  
  8         33  
1221             }
1222             elsif (does($source, 'SCALAR')) {
1223             # reference to a SQL string --> handled as is without quoting
1224 2         7 return $self->_parse_table($$source, -dont_quote_table);
1225             }
1226             else {
1227 224         567 return $self->_parse_table($source);
1228             }
1229             }
1230              
1231              
1232              
1233             sub _parse_table {
1234 270     270   566 my ($self, $table, $dont_quote_table) = @_; # $dont_quote_table: just a boolean
1235              
1236 270         581 ($table, my $alias) = $self->_parse_alias($table);
1237            
1238 270 100 66     1651 my $table_spec = {
1239             bind => [],
1240             name => ($alias || $table),
1241             aliased_tables => {$alias ? ($alias => $table) : ()},
1242             };
1243              
1244 270 100       1119 $table = $self->_quote($table) unless $dont_quote_table;
1245              
1246 270 100       6360 $table_spec->{sql} = $alias ? $self->table_alias($table, $self->_quote($alias)) : $table;
1247              
1248 270         831 return $table_spec;
1249             }
1250              
1251             sub _parse_join_spec {
1252 49     49   153 my ($self, $join_spec) = @_;
1253              
1254             # parse the join specification
1255 49 50       127 $join_spec
1256             or puke "empty join specification";
1257             my ($op, $bracket, $cond_list) = ($join_spec =~ $self->{join_regex})
1258 49 50       623 or puke "incorrect join specification : $join_spec\n$self->{join_regex}";
1259 49   100     212 $op ||= '<=>';
1260 49   100     125 $bracket ||= '{';
1261 49   100     97 $cond_list ||= '';
1262              
1263             # extract constants (strings between quotes), replaced by placeholders
1264 49         162 my $regex = qr/' # initial quote
1265             ( # begin capturing group
1266             [^']* # any non-quote chars
1267             (?: # begin non-capturing group
1268             '' # pair of quotes
1269             [^']* # any non-quote chars
1270             )* # this non-capturing group 0 or more times
1271             ) # end of capturing group
1272             ' # ending quote
1273             /x;
1274 49         91 my $placeholder = '_?_'; # unlikely to be counfounded with any value
1275 49         66 my @constants;
1276 49         242 while ($cond_list =~ s/$regex/$placeholder/) {
1277 8         42 push @constants, $1;
1278             };
1279 49         128 s/''/'/g for @constants; # replace pairs of quotes by single quotes
1280              
1281             # accumulate conditions as pairs ($left => \"$op $right")
1282 49         139 my @conditions;
1283             my @using;
1284 49         217 foreach my $cond (split /,\s*/, $cond_list) {
1285             # parse the condition (left and right operands + comparison operator)
1286 64         298 my ($left, $cmp, $right) = split /([<>=!^]{1,2})/, $cond;
1287 64 100 66     235 if ($cmp && $right) {
    50          
1288             # if operands are not qualified by table/alias name, add sprintf hooks
1289 56 100       145 $left = '%1$s.' . $left unless $left =~ /\./;
1290 56 100 100     245 $right = '%2$s.' . $right unless $right =~ /\./ or $right eq $placeholder;
1291              
1292             # add this pair into the list; right operand is either a bind value
1293             # or an identifier within the right table
1294 56 100       178 $right = $right eq $placeholder ? shift @constants : {-ident => $right};
1295 56         206 push @conditions, $left, {$cmp => $right};
1296             }
1297             elsif ($cond =~ /^\w+$/) {
1298 8         23 push @using, $cond;
1299             }
1300 0         0 else {puke "can't parse join condition: $cond"}
1301             }
1302              
1303             # build join hashref
1304 49         125 my $join_hash = {operator => $op};
1305 49 100       126 $join_hash->{using} = \@using if @using;
1306             $join_hash->{condition}
1307 49 100       221 = $bracket eq '[' ? [@conditions] : {@conditions} if @conditions;
    100          
1308              
1309 49         174 return $join_hash;
1310             }
1311              
1312             sub _single_join {
1313 54     54   76 my $self = shift;
1314              
1315             # if right-associative, restore proper left-right order in pair
1316 54 100       122 @_ = reverse @_ if $self->{join_assoc_right};
1317 54         109 my ($left, $join_spec, $right) = @_;
1318              
1319             # syntax for assembling all elements
1320 54         160 my $syntax = $self->{join_syntax}{$join_spec->{operator}};
1321              
1322 54         78 my ($sql, @bind);
1323              
1324 11     11   138 { no if $] ge '5.022000', warnings => 'redundant';
  11         20  
  11         23165  
  54         61  
1325             # because sprintf instructions may _intentionally_ omit %.. parameters
1326              
1327 54 100       147 if ($join_spec->{using}) {
    100          
1328             not $join_spec->{condition}
1329 11 100       29 or puke "join specification has both {condition} and {using} fields";
1330              
1331 10         61 $syntax =~ s/\bON\s+%s/USING (%s)/;
1332 10         17 $sql = CORE::join ",", map {$self->_quote($_)} @{$join_spec->{using}};
  12         38  
  10         24  
1333             }
1334             elsif ($join_spec->{condition}) {
1335             not $join_spec->{using}
1336 41 50       119 or puke "join specification has both {condition} and {using} fields";
1337              
1338             # compute the "ON" clause
1339 41         127 ($sql, @bind) = $self->where($join_spec->{condition});
1340 41         21980 $sql =~ s/^\s*WHERE\s+//;
1341              
1342             # substitute left/right tables names for '%1$s', '%2$s'
1343 41         146 $sql = sprintf $sql, $left->{name}, $right->{name};
1344             }
1345              
1346             # build the final sql
1347 53         313 $sql = sprintf $syntax, $left->{sql}, $right->{sql}, $sql;
1348             }
1349              
1350             # add left/right bind parameters (if any) into the list
1351 53         96 unshift @bind, @{$left->{bind}}, @{$right->{bind}};
  53         98  
  53         95  
1352              
1353             # build result and return
1354 53         173 my %result = (sql => $sql, bind => \@bind);
1355 53 100       166 $result{name} = ($self->{join_assoc_right} ? $left : $right)->{name};
1356 53         112 $result{aliased_tables} = $left->{aliased_tables};
1357 53         61 foreach my $alias (keys %{$right->{aliased_tables}}) {
  53         131  
1358 14         35 $result{aliased_tables}{$alias} = $right->{aliased_tables}{$alias};
1359             }
1360              
1361 53         366 return \%result;
1362             }
1363              
1364              
1365             #----------------------------------------------------------------------
1366             # override of parent's "_where_field_IN"
1367             #----------------------------------------------------------------------
1368              
1369             sub _where_field_IN {
1370 54     54   84477 my ($self, $k, $op, $vals) = @_;
1371              
1372             # special algorithm if the key is multi-columns (contains a multicols_sep)
1373 54 100       214 if ($self->{multicols_sep}) {
1374 8         74 my @cols = split m[$self->{multicols_sep}], $k;
1375 8 50       23 if (@cols > 1) {
1376 8 100       29 if ($self->{has_multicols_in_SQL}) {
1377             # DBMS accepts special SQL syntax for multicolumns
1378 6         25 return $self->_multicols_IN_through_SQL(\@cols, $op, $vals);
1379             }
1380             else {
1381             # DBMS doesn't accept special syntax, so we must use boolean logic
1382 2         9 return $self->_multicols_IN_through_boolean(\@cols, $op, $vals);
1383             }
1384             }
1385             }
1386              
1387             # special algorithm if the number of values exceeds the allowed maximum
1388 46         67 my $max_members_IN = $self->{max_members_IN};
1389 46 100 100     132 if ($max_members_IN && does($vals, 'ARRAY')
      100        
1390             && @$vals > $max_members_IN) {
1391 8         24 my @vals = @$vals;
1392 8         11 my @slices;
1393 8         25 while (my @slice = splice(@vals, 0, $max_members_IN)) {
1394 24         60 push @slices, \@slice;
1395             }
1396 8         14 my @clauses = map {{-$op, $_}} @slices;
  24         56  
1397 8 100       45 my $connector = $op =~ /^not/i ? '-and' : '-or';
1398 8         17 unshift @clauses, $connector;
1399 8         27 my ($sql, @bind) = $self->where({$k => \@clauses});
1400 8         1999 $sql =~ s/\s*where\s*\((.*)\)/$1/i;
1401 8         48 return ($sql, @bind);
1402             }
1403              
1404              
1405             # otherwise, call parent method
1406 38 100       74 $vals = [@$vals] if blessed $vals; # because SQLA dies on blessed arrayrefs
1407 38         131 return $self->next::method($k, $op, $vals);
1408             }
1409              
1410              
1411             sub _multicols_IN_through_SQL {
1412 6     6   16 my ($self, $cols, $op, $vals) = @_;
1413              
1414             # build initial sql
1415 6         12 my $n_cols = @$cols;
1416 6         14 my $sql_cols = CORE::join(',', map {$self->_quote($_)} @$cols);
  14         160  
1417 6         103 my $sql = "($sql_cols) " . $self->_sqlcase($op);
1418              
1419             # dispatch according to structure of $vals
1420             return $self->_SWITCH_refkind($vals, {
1421              
1422             ARRAYREF => sub { # list of tuples
1423             # deal with special case of empty list (like the parent class)
1424 4     4   125 my $n_tuples = @$vals;
1425 4 50       22 if (!$n_tuples) {
1426 0 0       0 my $sql = ($op =~ /\bnot\b/i) ? $self->{sqltrue} : $self->{sqlfalse};
1427 0         0 return ($sql);
1428             }
1429              
1430             # otherwise, build SQL and bind values for the list of tuples
1431 4         24 my @bind;
1432 4         11 foreach my $val (@$vals) {
1433 6 100       18 does($val, 'ARRAY')
1434             or $val = [split m[$self->{multicols_sep}], $val];
1435 6 50       20 @$val == $n_cols
1436             or puke "op '$op' with multicols: tuple with improper number of cols";
1437 6         17 push @bind, @$val;
1438             }
1439 4         16 my $single_tuple = "(" . CORE::join(',', (('?') x $n_cols)) . ")";
1440              
1441 4         11 my $all_tuples = CORE::join(', ', (($single_tuple) x $n_tuples));
1442 4         13 $sql .= " ($all_tuples)";
1443 4         82 return ($sql, @bind);
1444             },
1445              
1446             SCALARREF => sub { # literal SQL
1447 1     1   40 $sql .= " ($$vals)";
1448 1         51 return ($sql);
1449             },
1450              
1451             ARRAYREFREF => sub { # literal SQL with bind
1452 1     1   43 my ($inner_sql, @bind) = @$$vals;
1453 1         3 $sql .= " ($inner_sql)";
1454 1         19 return ($sql, @bind);
1455             },
1456              
1457             FALLBACK => sub {
1458 0     0   0 puke "op '$op' with multicols requires a list of tuples or literal SQL";
1459             },
1460              
1461 6         119 });
1462             }
1463              
1464              
1465             sub _multicols_IN_through_boolean {
1466 2     2   6 my ($self, $cols, $op, $vals) = @_;
1467              
1468             # can't handle anything else than a list of tuples
1469 2 50 33     7 does($vals, 'ARRAY') && @$vals
1470             or puke "op '$op' with multicols requires a non-empty list of tuples";
1471              
1472             # assemble SQL
1473 2         5 my $n_cols = @$cols;
1474 2         7 my $sql_cols = CORE::join(' AND ', map {$self->_quote($_) . " = ?"} @$cols);
  5         66  
1475 2         65 my $sql = "(" . CORE::join(' OR ', (("($sql_cols)") x @$vals)) . ")";
1476 2 100       13 $sql = "NOT $sql" if $op =~ /\bnot\b/i;
1477              
1478             # assemble bind values
1479 2         5 my @bind;
1480 2         5 foreach my $val (@$vals) {
1481 3 50       8 does($val, 'ARRAY')
1482             or $val = [split m[$self->{multicols_sep}], $val];
1483 3 50       12 @$val == $n_cols
1484             or puke "op '$op' with multicols: tuple with improper number of cols";
1485 3         10 push @bind, @$val;
1486             }
1487              
1488             # return the whole thing
1489 2         18 return ($sql, @bind);
1490             }
1491              
1492              
1493              
1494             #----------------------------------------------------------------------
1495             # override parent's methods for decoding arrayrefs
1496             #----------------------------------------------------------------------
1497              
1498             sub _where_hashpair_ARRAYREF {
1499 12     12   889 my ($self, $k, $v) = @_;
1500              
1501 12 100       38 if ($self->is_bind_value_with_type($v)) {
1502 2         7 $self->_assert_no_bindtype_columns;
1503             my $sql = CORE::join ' ', $self->_convert($self->_quote($k)),
1504 2         6 $self->_sqlcase($self->{cmp}),
1505             $self->_convert('?');
1506 2         53 my @bind = ($v);
1507 2         8 return ($sql, @bind);
1508             }
1509             else {
1510 10         38 return $self->next::method($k, $v);
1511             }
1512             }
1513              
1514              
1515             sub _where_field_op_ARRAYREF {
1516 2     2   463 my ($self, $k, $op, $vals) = @_;
1517              
1518 2 50       8 if ($self->is_bind_value_with_type($vals)) {
1519 2         6 $self->_assert_no_bindtype_columns;
1520 2         7 my $sql = CORE::join ' ', $self->_convert($self->_quote($k)),
1521             $self->_sqlcase($op),
1522             $self->_convert('?');
1523 2         57 my @bind = ($vals);
1524 2         10 return ($sql, @bind);
1525             }
1526             else {
1527 0         0 return $self->next::method($k, $op, $vals);
1528             }
1529             }
1530              
1531             sub _assert_no_bindtype_columns {
1532 4     4   6 my ($self) = @_;
1533 4 50       13 $self->{bindtype} ne 'columns'
1534             or puke 'values of shape [$val, \%type] are not compatible'
1535             . 'with ...->new(bindtype => "columns")';
1536             }
1537              
1538              
1539              
1540             #----------------------------------------------------------------------
1541             # method creations through closures
1542             #----------------------------------------------------------------------
1543              
1544             sub _make_aliasing_sub {
1545 56     56   118 my ($self, $syntax_field) = @_;
1546 56         106 my $syntax = $self->{$syntax_field};
1547             $self->{$syntax_field} = sub {
1548 109     109   280 my ($self, $name, $alias) = @_;
1549 109 100       541 return $alias ? sprintf($syntax, $name, $alias) : $name;
1550 56         277 };
1551             }
1552              
1553             sub _choose_LIMIT_OFFSET_dialect {
1554 26     26   47 my $self = shift;
1555 26         49 my $dialect = $self->{limit_offset};
1556 26 50       126 my $method = $limit_offset_dialects{$dialect}
1557             or puke "no such limit_offset dialect: $dialect";
1558 26         66 $self->{limit_offset} = $method;
1559             }
1560              
1561              
1562             #----------------------------------------------------------------------
1563             # utility functions (not methods)
1564             #----------------------------------------------------------------------
1565              
1566             sub _called_with_named_args {
1567 199   66 199   1607 return $_[0] && !ref $_[0] && substr($_[0], 0, 1) eq '-';
1568             }
1569              
1570              
1571             sub _is_subquery {
1572 443     443   739 my $arg = shift;
1573 443   66     782 return does($arg, 'REF') && does($$arg, 'ARRAY');
1574             }
1575              
1576              
1577             sub _parenthesize_select {
1578 14     14   25 my ($sql) = @_;
1579 14         47 $sql =~ s{^(select\b.*)}{($1)}is; # if subquery is a plain SELECT, put it in parenthesis
1580 14         37 return $sql;
1581             }
1582              
1583              
1584              
1585              
1586             1; # End of SQL::Abstract::More
1587              
1588             __END__