File Coverage

blib/lib/SQL/Abstract/More.pm
Criterion Covered Total %
statement 445 579 76.8
branch 198 280 70.7
condition 54 95 56.8
subroutine 51 61 83.6
pod 13 13 100.0
total 761 1028 74.0


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