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) {
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;
} 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";
}
die "notreached";
}
+my $Nest_Warned = 0;
+
sub _expand_expr_hashpair {
my ($self, $k, $v, $logic) = @_;
unless (defined($k) and length($k)) {
. "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') {
$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 ] };
}
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)) {
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 ] };
#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 ]);
# 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) {
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]);
? "${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
);
}
}
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]);
};
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($_) ];