File Coverage

blib/lib/SQL/Abstract/More.pm
Criterion Covered Total %
statement 538 612 87.9
branch 214 292 73.2
condition 59 98 60.2
subroutine 69 80 86.2
pod 13 13 100.0
total 893 1095 81.5


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