X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FAbstract.pm;h=1e5deff19180b14b4d4467e94a0cc6ad9bf8d0cf;hb=497e9bed5af16d7461b509e2e6b526073e953b79;hp=f8518deeeeb2be216383e8624102ecf18d7bb96c;hpb=2af21cb852b6c6c629ce0eda1116fd366cea666b;p=dbsrgits%2FSQL-Abstract.git diff --git a/lib/SQL/Abstract.pm b/lib/SQL/Abstract.pm index f8518de..1e5deff 100644 --- a/lib/SQL/Abstract.pm +++ b/lib/SQL/Abstract.pm @@ -499,6 +499,7 @@ sub where { sub _expand_expr { my ($self, $expr, $logic, $default_scalar_to) = @_; local our $Default_Scalar_To = $default_scalar_to if $default_scalar_to; + our $Expand_Depth ||= 0; local $Expand_Depth = $Expand_Depth + 1; return undef unless defined($expr); if (ref($expr) eq 'HASH') { if (keys %$expr > 1) { @@ -509,14 +510,19 @@ sub _expand_expr { sort keys %$expr ] }; } - return unless %$expr; + return { -literal => [ '' ] } unless keys %$expr; return $self->_expand_expr_hashpair(%$expr, $logic); } if (ref($expr) eq 'ARRAY') { my $logic = lc($logic || $self->{logic}); $logic eq 'and' or $logic eq 'or' or puke "unknown logic: $logic"; - my @expr = @$expr; + #my @expr = @$expr; + my @expr = grep { + (ref($_) eq 'ARRAY' and @$_) + or (ref($_) eq 'HASH' and %$_) + or 1 + } @$expr; my @res; @@ -531,7 +537,7 @@ sub _expand_expr { } elsif (my $l = is_literal_value($el)) { push @res, { -literal => $l }; } elsif ($elref eq 'HASH') { - push @res, $self->_expand_expr($el); + push @res, $self->_expand_expr($el) if %$el; } else { die "notreached"; } @@ -553,6 +559,8 @@ sub _expand_expr { die "notreached"; } +my $Nest_Warned = 0; + sub _expand_expr_hashpair { my ($self, $k, $v, $logic) = @_; unless (defined($k) and length($k)) { @@ -569,6 +577,17 @@ sub _expand_expr_hashpair { . "You probably wanted ...-and => [ $k => COND1, $k => COND2 ... ]"; } if ($k eq '-nest') { + # DBIx::Class requires a nest warning to be emitted once but the private + # method it overrode to do so no longer exists + if (ref($self) =~ /^DBIx::Class::SQLMaker/) { + unless ($Nest_Warned) { + belch( + "-nest in search conditions is deprecated, you most probably wanted:\n" + .q|{..., -and => [ \%cond0, \@cond1, \'cond2', \[ 'cond3', [ col => bind ] ], etc. ], ... }| + ); + $Nest_Warned = 1; + } + } return $self->_expand_expr($v); } if ($k eq '-bool') { @@ -600,8 +619,15 @@ sub _expand_expr_hashpair { $op =~ s/^-// if length($op) > 1; # top level special ops are illegal in general - puke "Illegal use of top-level '-$op'" - if List::Util::first { $op =~ $_->{regex} } @{$self->{special_ops}}; + # note that, arguably, if it makes no sense at top level, it also + # makes no sense on the other side of an = sign or similar but DBIC + # gets disappointingly upset if I disallow it + if ( + (our $Expand_Depth) == 1 + and List::Util::first { $op =~ $_->{regex} } @{$self->{special_ops}} + ) { + puke "Illegal use of top-level '-$op'" + } if (my $us = List::Util::first { $op =~ $_->{regex} } @{$self->{unary_ops}}) { return { -op => [ $op, $v ] }; } @@ -621,6 +647,9 @@ sub _expand_expr_hashpair { and (keys %$v)[0] =~ /^-/ ) { my ($func) = $k =~ /^-(.*)$/; + if (List::Util::first { $func =~ $_->{regex} } @{$self->{special_ops}}) { + return +{ -op => [ $func, $self->_expand_expr($v) ] }; + } return +{ -func => [ $func, $self->_expand_expr($v) ] }; } if (!ref($v) or is_literal_value($v)) { @@ -837,9 +866,7 @@ sub _expand_expr_hashpair { my ($sql, @bind) = @$literal; if ($self->{bindtype} eq 'columns') { for (@bind) { - if (!defined $_ || ref($_) ne 'ARRAY' || @$_ != 2) { - puke "bindtype 'columns' selected, you need to pass: [column_name => bind_value]" - } + $self->_assert_bindval_matches_bindtype($_); } } return +{ -literal => [ $self->_quote($k).' '.$sql, @bind ] }; @@ -862,7 +889,12 @@ sub _recurse_where { #print STDERR Data::Dumper::Concise::Dumper([ $where, $logic ]); - my $where_exp = $self->_expand_expr($where, $logic); + # Special case: top level simple string treated as literal + + my $where_exp = (ref($where) + ? $self->_expand_expr($where, $logic) + : { -literal => [ $where ] }); +#::Dwarn([ EXPANDED => $where_exp ]); #print STDERR Data::Dumper::Concise::Dumper([ EXP => $where_exp ]); @@ -872,7 +904,6 @@ sub _recurse_where { # my ($sql, @bind) = $self->$method($where_exp, $logic); my ($sql, @bind) = defined($where_exp) ? $self->_render_expr($where_exp) : (undef); - # DBIx::Class used to call _recurse_where in scalar context # something else might too... if (wantarray) { @@ -955,7 +986,8 @@ sub _render_op { if (my $h = $special{$op}) { return $self->$h(\@args); } - if (my $us = List::Util::first { $op =~ $_->{regex} } @{$self->{special_ops}}) { + my $us = List::Util::first { $op =~ $_->{regex} } @{$self->{special_ops}}; + if ($us and @args > 1) { puke "Special op '${op}' requires first value to be identifier" unless my ($k) = map $_->{-ident}, grep ref($_) eq 'HASH', $args[0]; return $self->${\($us->{handler})}($k, $op, $args[1]); @@ -972,10 +1004,15 @@ sub _render_op { ? "${expr_sql} ${op_sql}" : "${op_sql} ${expr_sql}" ); - return (($op eq 'not' ? '('.$final_sql.')' : $final_sql), @bind); + return (($op eq 'not' || $us ? '('.$final_sql.')' : $final_sql), @bind); + #} elsif (@args == 0) { + # return ''; } else { - my @parts = map [ $self->_render_expr($_) ], @args; - my ($final_sql) = map +($op =~ /^(and|or)$/ ? "(${_})" : $_), join( + my @parts = grep length($_->[0]), map [ $self->_render_expr($_) ], @args; + return '' unless @parts; + my $is_andor = !!($op =~ /^(and|or)$/); + return @{$parts[0]} if $is_andor and @parts == 1; + my ($final_sql) = map +($is_andor ? "( ${_} )" : $_), join( ($final_op eq ',' ? '' : ' ').$self->_sqlcase($final_op).' ', map $_->[0], @parts ); @@ -1063,7 +1100,8 @@ sub _expand_order_by { } } my @exp = map +(defined($dir) ? { -op => [ $dir => $_ ] } : $_), - map $self->_expand_expr($_, undef, -ident), @to_expand; + map $self->_expand_expr($_, undef, -ident), + map ref($_) eq 'ARRAY' ? @$_ : $_, @to_expand; return (@exp > 1 ? { -op => [ ',', @exp ] } : $exp[0]); }; @@ -1087,15 +1125,26 @@ sub _order_by { return wantarray ? ($final_sql, @bind) : $final_sql; } +# _order_by no longer needs to call this so doesn't but DBIC uses it. + sub _order_by_chunks { my ($self, $arg) = @_; return () unless defined(my $expanded = $self->_expand_order_by($arg)); + return $self->_chunkify_order_by($expanded); +} + +sub _chunkify_order_by { + my ($self, $expanded) = @_; + + return $self->_render_expr($expanded) + if $expanded->{-ident} or @{$expanded->{-literal}||[]} == 1; + for ($expanded) { if (ref() eq 'HASH' and my $op = $_->{-op}) { if ($op->[0] eq ',') { - return map [ $self->_render_expr($_) ], @{$op}[1..$#$op]; + return map $self->_chunkify_order_by($_), @{$op}[1..$#$op]; } } return [ $self->_render_expr($_) ];