File Coverage

blib/lib/SQL/Abstract/More.pm
Criterion Covered Total %
statement 513 579 88.6
branch 210 280 75.0
condition 57 95 60.0
subroutine 64 74 86.4
pod 13 13 100.0
total 857 1041 82.3


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