X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FAbstract.pm;h=0e29467d00c1b9ce26438484a6e3f38fc50c6749;hb=d7d3d1584c95a2bc501e9b783678fae97ac0bb57;hp=4d2e49f91d157e12b967e76e354bc3fa28aa034b;hpb=88a899390925fcded12aedf9efab3c545db860bd;p=dbsrgits%2FSQL-Abstract.git diff --git a/lib/SQL/Abstract.pm b/lib/SQL/Abstract.pm index 4d2e49f..0e29467 100644 --- a/lib/SQL/Abstract.pm +++ b/lib/SQL/Abstract.pm @@ -15,7 +15,7 @@ use Scalar::Util (); # GLOBALS #====================================================================== -our $VERSION = '1.68'; +our $VERSION = '1.71'; # This would confuse some packagers $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases @@ -25,17 +25,17 @@ our $AUTOLOAD; # special operators (-in, -between). May be extended/overridden by user. # See section WHERE: BUILTIN SPECIAL OPERATORS below for implementation my @BUILTIN_SPECIAL_OPS = ( - {regex => qr/^(not )?between$/i, handler => '_where_field_BETWEEN'}, - {regex => qr/^(not )?in$/i, handler => '_where_field_IN'}, + {regex => qr/^ (?: not \s )? between $/ix, handler => '_where_field_BETWEEN'}, + {regex => qr/^ (?: not \s )? in $/ix, handler => '_where_field_IN'}, ); # unaryish operators - key maps to handler my @BUILTIN_UNARY_OPS = ( # the digits are backcompat stuff - { regex => qr/^and (?: \s? \d+ )? $/xi, handler => '_where_op_ANDOR' }, - { regex => qr/^or (?: \s? \d+ )? $/xi, handler => '_where_op_ANDOR' }, - { regex => qr/^nest (?: \s? \d+ )? $/xi, handler => '_where_op_NEST' }, - { regex => qr/^ (?: not \s )? bool $/xi, handler => '_where_op_BOOL' }, + { regex => qr/^ and (?: [_\s]? \d+ )? $/xi, handler => '_where_op_ANDOR' }, + { regex => qr/^ or (?: [_\s]? \d+ )? $/xi, handler => '_where_op_ANDOR' }, + { regex => qr/^ nest (?: [_\s]? \d+ )? $/xi, handler => '_where_op_NEST' }, + { regex => qr/^ (?: not \s )? bool $/xi, handler => '_where_op_BOOL' }, ); #====================================================================== @@ -280,7 +280,19 @@ sub update { }, SCALARREF => sub { # literal SQL without bind push @set, "$label = $$v"; - }, + }, + HASHREF => sub { + my ($op, $arg, @rest) = %$v; + + puke 'Operator calls in update must be in the form { -op => $arg }' + if (@rest or not $op =~ /^\-(.+)/); + + local $self->{_nested_func_lhs} = $k; + my ($sql, @bind) = $self->_where_unary_op ($1, $arg); + + push @set, "$label = $sql"; + push @all_bind, @bind; + }, SCALAR_or_UNDEF => sub { push @set, "$label = ?"; push @all_bind, $self->_bindtype($k, $v); @@ -463,34 +475,24 @@ sub _where_HASHREF { if ($k =~ /^-./) { # put the operator in canonical form my $op = $k; - $op =~ s/^-//; # remove initial dash - $op =~ s/[_\t ]+/ /g; # underscores and whitespace become single spaces + $op = substr $op, 1; # remove initial dash $op =~ s/^\s+|\s+$//g;# remove leading/trailing space + $op =~ s/\s+/ /g; # compress whitespace - $self->_debug("Unary OP(-$op) within hashref, recursing..."); + # so that -not_foo works correctly + $op =~ s/^not_/NOT /i; - my $op_entry = List::Util::first {$op =~ $_->{regex}} @{$self->{unary_ops}}; - if (my $handler = $op_entry->{handler}) { - if (not ref $handler) { - if ($op =~ s/\s?\d+$//) { - belch 'Use of [and|or|nest]_N modifiers is deprecated and will be removed in SQLA v2.0. ' - . "You probably wanted ...-and => [ -$op => COND1, -$op => COND2 ... ]"; - } - $self->$handler ($op, $v); - } - elsif (ref $handler eq 'CODE') { - $handler->($self, $op, $v); - } - else { - puke "Illegal handler for operator $k - expecting a method name or a coderef"; - } - } - else { - $self->debug("Generic unary OP: $k - recursing as function"); - my ($sql, @bind) = $self->_where_func_generic ($op, $v); - $sql = "($sql)" unless (defined($self->{_nested_func_lhs}) && ($self->{_nested_func_lhs} eq $k)); # top level vs nested - ($sql, @bind); - } + $self->_debug("Unary OP(-$op) within hashref, recursing..."); + my ($s, @b) = $self->_where_unary_op ($op, $v); + + # top level vs nested + # we assume that handled unary ops will take care of their ()s + $s = "($s)" unless ( + List::Util::first {$op =~ $_->{regex}} @{$self->{unary_ops}} + or + defined($self->{_nested_func_lhs}) && ($self->{_nested_func_lhs} eq $k) + ); + ($s, @b); } else { my $method = $self->_METHOD_FOR_refkind("_where_hashpair", $v); @@ -505,9 +507,29 @@ sub _where_HASHREF { return $self->_join_sql_clauses('and', \@sql_clauses, \@all_bind); } -sub _where_func_generic { +sub _where_unary_op { my ($self, $op, $rhs) = @_; + if (my $op_entry = List::Util::first {$op =~ $_->{regex}} @{$self->{unary_ops}}) { + my $handler = $op_entry->{handler}; + + if (not ref $handler) { + if ($op =~ s/ [_\s]? \d+ $//x ) { + belch 'Use of [and|or|nest]_N modifiers is deprecated and will be removed in SQLA v2.0. ' + . "You probably wanted ...-and => [ -$op => COND1, -$op => COND2 ... ]"; + } + return $self->$handler ($op, $rhs); + } + elsif (ref $handler eq 'CODE') { + return $handler->($self, $op, $rhs); + } + else { + puke "Illegal handler for operator $op - expecting a method name or a coderef"; + } + } + + $self->debug("Generic unary OP: $op - recursing as function"); + my ($sql, @bind) = $self->_SWITCH_refkind ($rhs, { SCALAR => sub { puke "Illegal use of top-level '$op'" @@ -589,30 +611,22 @@ sub _where_op_NEST { sub _where_op_BOOL { my ($self, $op, $v) = @_; - my ( $prefix, $suffix ) = ( $op =~ /\bnot\b/i ) - ? ( '(NOT ', ')' ) - : ( '', '' ); - - my ($sql, @bind) = do { - $self->_SWITCH_refkind($v, { - SCALAR => sub { # interpreted as SQL column - $self->_convert($self->_quote($v)); - }, + my ($s, @b) = $self->_SWITCH_refkind($v, { + SCALAR => sub { # interpreted as SQL column + $self->_convert($self->_quote($v)); + }, - UNDEF => sub { - puke "-$op => undef not supported"; - }, + UNDEF => sub { + puke "-$op => undef not supported"; + }, - FALLBACK => sub { - $self->_recurse_where ($v); - }, - }); - }; + FALLBACK => sub { + $self->_recurse_where ($v); + }, + }); - return ( - join ('', $prefix, $sql, $suffix), - @bind, - ); + $s = "(NOT $s)" if $op =~ /^not/i; + ($s, @b); } @@ -660,9 +674,14 @@ sub _where_hashpair_HASHREF { # put the operator in canonical form my $op = $orig_op; - $op =~ s/^-//; # remove initial dash - $op =~ s/[_\t ]+/ /g; # underscores and whitespace become single spaces + + # FIXME - we need to phase out dash-less ops + $op =~ s/^-//; # remove possible initial dash $op =~ s/^\s+|\s+$//g;# remove leading/trailing space + $op =~ s/\s+/ /g; # compress whitespace + + # so that -not_foo works correctly + $op =~ s/^not_/NOT /i; my ($sql, @bind); @@ -714,7 +733,7 @@ sub _where_hashpair_HASHREF { # retain for proper column type bind $self->{_nested_func_lhs} ||= $k; - ($sql, @bind) = $self->_where_func_generic ($op, $val); + ($sql, @bind) = $self->_where_unary_op ($op, $val); $sql = join (' ', $self->_convert($self->_quote($k)), @@ -886,7 +905,7 @@ sub _where_field_BETWEEN { puke ("Only simple { -func => arg } functions accepted as sub-arguments to BETWEEN") if (@rest or $func !~ /^ \- (.+)/x); local $self->{_nested_func_lhs} = $k; - $self->_where_func_generic ($1 => $arg); + $self->_where_unary_op ($1 => $arg); } }); push @all_sql, $sql; @@ -941,7 +960,7 @@ sub _where_field_IN { puke ("Only simple { -func => arg } functions accepted as sub-arguments to IN") if (@rest or $func !~ /^ \- (.+)/x); local $self->{_nested_func_lhs} = $k; - $self->_where_func_generic ($1 => $arg); + $self->_where_unary_op ($1 => $arg); } }); push @all_sql, $sql; @@ -2682,6 +2701,15 @@ a fast interface to returning and formatting data. I frequently use these three modules together to write complex database query apps in under 50 lines. +=head1 REPO + +=over + +=item * gitweb: L + +=item * git: L + +=back =head1 CHANGES