# See section WHERE: BUILTIN SPECIAL OPERATORS below for implementation
my @BUILTIN_SPECIAL_OPS = (
{regex => qr/^ (?: not \s )? between $/ix, handler => sub { die "NOPE" }},
- {regex => qr/^ (?: not \s )? in $/ix, handler => sub { die "NOPE" }},
{regex => qr/^ is (?: \s+ not )? $/ix, handler => sub { die "NOPE" }},
);
if ($class->isa('DBIx::Class::SQLMaker')) {
push @{$opt{special_ops}}, our $DBIC_Compat_Op ||= {
- regex => qr/^(?:ident|value)$/i, handler => sub { die "NOPE" }
+ regex => qr/^(?:ident|value|(?:not\s)?in)$/i, handler => sub { die "NOPE" }
};
$opt{is_dbic_sqlmaker} = 1;
}
-and => '_expand_op_andor',
-or => '_expand_op_andor',
-nest => '_expand_nest',
+ -bind => sub { shift; +{ @_ } },
};
$opt{expand_op} = {
return wantarray ? ($sql, @bind) : $sql;
}
+{ our $Default_Scalar_To = -value }
+
sub expand_expr {
my ($self, $expr, $default_scalar_to) = @_;
local our $Default_Scalar_To = $default_scalar_to if $default_scalar_to;
$self->render_aqt($self->expand_expr($expr));
}
+sub _normalize_op {
+ my ($self, $raw) = @_;
+ s/^-(?=[a-z])//, s/\s+/_/g for my $op = lc $raw;
+ $op;
+}
+
sub _expand_expr {
my ($self, $expr) = @_;
our $Expand_Depth ||= 0; local $Expand_Depth = $Expand_Depth + 1;
return +{ -literal => $literal };
}
if (!ref($expr) or Scalar::Util::blessed($expr)) {
- if (my $d = our $Default_Scalar_To) {
- return $self->_expand_expr({ $d => $expr });
- }
- return $self->_expand_value(-value => $expr);
+ return $self->_expand_expr_scalar($expr);
}
die "notreached";
}
sub _expand_expr_hashpair_ident {
my ($self, $k, $v) = @_;
+ local our $Cur_Col_Meta = $k;
+
# hash with multiple or no elements is andor
if (ref($v) eq 'HASH' and keys %$v != 1) {
# scalars and objects get expanded as whatever requested or values
if (!ref($v) or Scalar::Util::blessed($v)) {
- my $d = our $Default_Scalar_To;
- local our $Cur_Col_Meta = $k;
- return $self->_expand_expr_hashpair_ident(
- $k,
- ($d
- ? $self->_expand_expr($d => $v)
- : { -value => $v }
- )
- );
+ return $self->_expand_expr_hashpair_scalar($k, $v);
}
+
+ # single key hashref is a hashtriple
+
if (ref($v) eq 'HASH') {
return $self->_expand_expr_hashtriple($k, %$v);
}
+
+ # arrayref needs re-engineering over the elements
+
if (ref($v) eq 'ARRAY') {
return $self->sqlfalse unless @$v;
$self->_debug("ARRAY($k) means distribute over elements");
$logic => $v, $k
);
}
+
if (my $literal = is_literal_value($v)) {
unless (length $k) {
belch 'Hash-pairs consisting of an empty string with a literal are deprecated, and will be removed in 2.0: use -and => [ $literal ] instead';
die "notreached";
}
+sub _expand_expr_scalar {
+ my ($self, $expr) = @_;
+
+ return $self->_expand_expr({ (our $Default_Scalar_To) => $expr });
+}
+
+sub _expand_expr_hashpair_scalar {
+ my ($self, $k, $v) = @_;
+
+ return $self->_expand_expr_hashpair_cmp(
+ $k, $self->_expand_expr_scalar($v),
+ );
+}
+
sub _expand_expr_hashpair_op {
my ($self, $k, $v) = @_;
- my $op = $k;
- $op =~ s/^-// if length($op) > 1;
+ my $op = $self->_normalize_op($k);
+
$self->_assert_pass_injection_guard($op);
# Ops prefixed with -not_ get converted
- if (my ($rest) = $op =~/^not[_ ](.*)$/) {
+ if (my ($rest) = $op =~/^not_(.*)$/) {
return +{ -op => [
'not',
$self->_expand_expr({ "-${rest}", $v })
{ # Old SQLA compat
+ my $op = join(' ', split '_', $op);
+
# the old special op system requires illegality for top-level use
if (
my $ik = $self->_expand_ident(-ident => $k);
- my $op = join ' ', split '_', (map lc, $vk =~ /^-?(.*)$/)[0];
+ my $op = $self->_normalize_op($vk);
$self->_assert_pass_injection_guard($op);
- if ($op =~ s/ [_\s]? \d+ $//x ) {
+
+ if ($op =~ s/ _? \d+ $//x ) {
return $self->_expand_expr($k, { $vk, $vv });
}
if (my $x = $self->{expand_op}{$op}) {
return $self->$x($op, $vv, $k);
}
{ # Old SQLA compat
+
+ my $op = join(' ', split '_', $op);
+
if (my $us = List::Util::first { $op =~ $_->{regex} } @{$self->{special_ops}}) {
return { -op => [ $op, $ik, $vv ] };
}
}
sub _dwim_op_to_is {
- my ($self, $op, $empty, $fail) = @_;
+ my ($self, $raw, $empty, $fail) = @_;
+
+ my $op = $self->_normalize_op($raw);
+
if ($op =~ /^not$/i) {
return 0;
}
{ # Old SQLA compat
+ my $op = join(' ', split '_', $op);
+
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"
sub _render_unop_prefix {
my ($self, $op, $v) = @_;
my ($expr_sql, @bind) = $self->render_aqt($v->[0]);
- my $op_sql = $self->_sqlcase($op);
+
+ my $op_sql = $self->_sqlcase(join ' ', split '_', $op);
return ("${op_sql} ${expr_sql}", @bind);
}
sub _render_unop_postfix {
my ($self, $op, $v) = @_;
my ($expr_sql, @bind) = $self->render_aqt($v->[0]);
- my $op_sql = $self->_sqlcase($op);
+ my $op_sql = $self->_sqlcase(join ' ', split '_', $op);
return ($expr_sql.' '.$op_sql, @bind);
}