X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FAbstract.pm;h=d10aeae4dfb4402438e56d232ea826220c477896;hb=dbc10abd32c48b023b9ce3d2a0c458c27f48d503;hp=c065f2c02c3547bade04906421e4ce47f53c9a36;hpb=0cdafc4b91de4c1e155784af239c13327aa7faa8;p=scpubgit%2FQ-Branch.git diff --git a/lib/SQL/Abstract.pm b/lib/SQL/Abstract.pm index c065f2c..d10aeae 100644 --- a/lib/SQL/Abstract.pm +++ b/lib/SQL/Abstract.pm @@ -38,8 +38,8 @@ our $AUTOLOAD; # 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" }}, + {regex => qr/^ (?: not \s )? in $/ix, handler => sub { die "NOPE" }}, ); #====================================================================== @@ -156,8 +156,8 @@ sub new { $opt{equality_op} = qr/^( \Q$opt{cmp}\E | \= )$/ix; $opt{inequality_op} = qr/^( != | <> )$/ix; - $opt{like_op} = qr/^ (is\s+)? r?like $/xi; - $opt{not_like_op} = qr/^ (is\s+)? not \s+ r?like $/xi; + $opt{like_op} = qr/^ (is_)?r?like $/xi; + $opt{not_like_op} = qr/^ (is_)?not_r?like $/xi; # SQL booleans $opt{sqltrue} ||= '1=1'; @@ -171,7 +171,7 @@ sub new { 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; } @@ -202,16 +202,18 @@ sub new { -or => '_expand_op_andor', -nest => '_expand_nest', -bind => sub { shift; +{ @_ } }, + -in => '_expand_in', + -not_in => '_expand_in', }; $opt{expand_op} = { 'between' => '_expand_between', - 'not between' => '_expand_between', + 'not_between' => '_expand_between', 'in' => '_expand_in', - 'not in' => '_expand_in', + 'not_in' => '_expand_in', 'nest' => '_expand_nest', (map +($_ => '_expand_op_andor'), ('and', 'or')), - (map +($_ => '_expand_op_is'), ('is', 'is not')), + (map +($_ => '_expand_op_is'), ('is', 'is_not')), }; # placeholder for _expand_unop system @@ -235,13 +237,14 @@ sub new { }; $opt{render_op} = { - (map +($_ => '_render_op_between'), 'between', 'not between'), - (map +($_ => '_render_op_in'), 'in', 'not in'), + (map +($_ => '_render_op_between'), 'between', 'not_between'), + (map +($_ => '_render_op_in'), 'in', 'not_in'), (map +($_ => '_render_unop_postfix'), - 'is null', 'is not null', 'asc', 'desc', + 'is_null', 'is_not_null', 'asc', 'desc', ), (not => '_render_op_not'), (map +($_ => '_render_op_andor'), qw(and or)), + ',' => '_render_op_multop', }; return bless \%opt, $class; @@ -293,7 +296,7 @@ sub _returning { my $f = $options->{returning}; my ($sql, @bind) = $self->render_aqt( - $self->_expand_maybe_list_expr($f, undef, -ident) + $self->_expand_maybe_list_expr($f, -ident) ); return wantarray ? $self->_sqlcase(' returning ') . $sql @@ -495,7 +498,7 @@ sub _select_fields { my ($self, $fields) = @_; return $fields unless ref($fields); return $self->render_aqt( - $self->_expand_maybe_list_expr($fields, undef, '-ident') + $self->_expand_maybe_list_expr($fields, '-ident') ); } @@ -556,6 +559,8 @@ sub where { 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; @@ -577,6 +582,12 @@ sub render_expr { $self->render_aqt($self->expand_expr($expr)); } +sub _normalize_op { + my ($self, $raw) = @_; + s/^-(?=.)//, 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; @@ -687,10 +698,7 @@ sub _expand_expr_hashpair_ident { sub _expand_expr_scalar { my ($self, $expr) = @_; - if (my $d = our $Default_Scalar_To) { - return $self->_expand_expr({ $d => $expr }); - } - return $self->_expand_value(-value => $expr); + return $self->_expand_expr({ (our $Default_Scalar_To) => $expr }); } sub _expand_expr_hashpair_scalar { @@ -704,22 +712,23 @@ sub _expand_expr_hashpair_scalar { sub _expand_expr_hashpair_op { my ($self, $k, $v) = @_; - my $op = $k; - $op =~ s/^-// if length($op) > 1; - $self->_assert_pass_injection_guard($op); + $self->_assert_pass_injection_guard($k =~ /\A-(.*)\Z/s); + + my $op = $self->_normalize_op($k); # 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 ( @@ -778,9 +787,10 @@ sub _expand_expr_hashtriple { 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}) { @@ -788,6 +798,9 @@ sub _expand_expr_hashtriple { 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 ] }; } @@ -809,7 +822,7 @@ sub _expand_expr_hashtriple { or $op =~ $self->{not_like_op} ) { if (lc($logic) eq '-or' and @values > 1) { - belch "A multi-element arrayref as an argument to the inequality op '${\uc($op)}' " + belch "A multi-element arrayref as an argument to the inequality op '${\uc(join ' ', split '_', $op)}' " . 'is technically equivalent to an always-true 1=1 (you probably wanted ' . "to say ...{ \$inequality_op => [ -and => \@values ] }... instead)" ; @@ -841,7 +854,10 @@ sub _expand_expr_hashtriple { } 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; } @@ -849,14 +865,14 @@ sub _dwim_op_to_is { return 1; } if ($op =~ $self->{like_op}) { - belch(sprintf $empty, uc($op)); + belch(sprintf $empty, uc(join ' ', split '_', $op)); return 1; } if ($op =~ $self->{inequality_op}) { return 0; } if ($op =~ $self->{not_like_op}) { - belch(sprintf $empty, uc($op)); + belch(sprintf $empty, uc(join ' ', split '_', $op)); return 0; } puke(sprintf $fail, $op); @@ -956,7 +972,7 @@ sub _expand_op_is { and exists($vv->{-value}) and !defined($vv->{-value}) ); - return +{ -op => [ $op.' null', $self->_expand_ident(-ident => $k) ] }; + return +{ -op => [ $op.'_null', $self->_expand_ident(-ident => $k) ] }; } sub _expand_between { @@ -979,7 +995,10 @@ sub _expand_between { } sub _expand_in { - my ($self, $op, $vv, $k) = @_; + my ($self, $raw, $vv, $k) = @_; + $k = shift @{$vv = [ @$vv ]} unless defined $k; + local our $Cur_Col_Meta = $k; + my $op = $self->_normalize_op($raw); if (my $literal = is_literal_value($vv)) { my ($sql, @bind) = @$literal; my $opened_sql = $self->_open_outer_paren($sql); @@ -996,8 +1015,7 @@ sub _expand_in { ; puke("Argument passed to the '${\uc($op)}' operator can not be undefined") if !defined($vv); - my @rhs = map $self->_expand_expr($_), - map { ref($_) ? $_ : { -bind => [ $k, $_ ] } } + my @rhs = map $self->expand_expr($_, -value), map { defined($_) ? $_: puke($undef_err) } (ref($vv) eq 'ARRAY' ? @$vv : $vv); return $self->${\($op =~ /^not/ ? 'sqltrue' : 'sqlfalse')} unless @rhs; @@ -1092,6 +1110,8 @@ sub _render_op { { # 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" @@ -1130,7 +1150,11 @@ sub _render_op_between { }; my ($lhsql, @lhbind) = $self->render_aqt($left); return ( - join(' ', '(', $lhsql, $self->_sqlcase($op), $rhsql, ')'), + join(' ', + '(', $lhsql, + $self->_sqlcase(join ' ', split '_', $op), + $rhsql, ')' + ), @lhbind, @rhbind ); } @@ -1146,7 +1170,7 @@ sub _render_op_in { } @$rhs; my ($lhsql, @lbind) = $self->render_aqt($lhs); return ( - $lhsql.' '.$self->_sqlcase($op).' ( ' + $lhsql.' '.$self->_sqlcase(join ' ', split '_', $op).' ( ' .join(', ', @in_sql) .' )', @lbind, @in_bind @@ -1168,7 +1192,7 @@ sub _render_op_multop { return '' unless @parts; return @{$parts[0]} if @parts == 1; my ($final_sql) = join( - ' '.$self->_sqlcase($op).' ', + ' '.$self->_sqlcase(join ' ', split '_', $op).' ', map $_->[0], @parts ); return ( @@ -1185,14 +1209,15 @@ sub _render_op_not { 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($op); # 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); } @@ -1253,7 +1278,9 @@ sub _expand_order_by { ), map $self->expand_expr($_, -ident), map ref($_) eq 'ARRAY' ? @$_ : $_, @to_expand; - return (@exp > 1 ? { -list => \@exp } : $exp[0]); + return undef unless @exp; + return undef if @exp == 1 and not defined($exp[0]); + return +{ -op => [ ',', @exp ] }; }; local @{$self->{expand}}{qw(-asc -desc)} = (($expander) x 2); @@ -1292,8 +1319,9 @@ sub _chunkify_order_by { if $expanded->{-ident} or @{$expanded->{-literal}||[]} == 1; for ($expanded) { - if (ref() eq 'HASH' and my $l = $_->{-list}) { - return map $self->_chunkify_order_by($_), @$l; + if (ref() eq 'HASH' and $_->{-op} and $_->{-op}[0] eq ',') { + my ($comma, @list) = @{$_->{-op}}; + return map $self->_chunkify_order_by($_), @list; } return [ $self->render_aqt($_) ]; } @@ -1307,7 +1335,7 @@ sub _table { my $self = shift; my $from = shift; ($self->render_aqt( - $self->_expand_maybe_list_expr($from, undef, -ident) + $self->_expand_maybe_list_expr($from, -ident) ))[0]; } @@ -1317,7 +1345,7 @@ sub _table { #====================================================================== sub _expand_maybe_list_expr { - my ($self, $expr, $logic, $default) = @_; + my ($self, $expr, $default) = @_; my $e = do { if (ref($expr) eq 'ARRAY') { return { -list => [