X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FAbstract%2FTree.pm;h=1a4560c66c058e4bb73d6941d58f663c1125c634;hb=007f08535b6449047aa3bbc02d88a41b70d2e74c;hp=8d7a36a23037a0490c0b06a6f267660017e260ea;hpb=0f9a26cb13f772fe0813987a0641baf193fa9782;p=dbsrgits%2FSQL-Abstract.git diff --git a/lib/SQL/Abstract/Tree.pm b/lib/SQL/Abstract/Tree.pm index 8d7a36a..1a4560c 100644 --- a/lib/SQL/Abstract/Tree.pm +++ b/lib/SQL/Abstract/Tree.pm @@ -50,10 +50,10 @@ my $placeholder_re = qr/(?: \? | \$\d+ )/x; my @expression_start_keywords = ( 'SELECT', 'UPDATE', + 'SET', 'INSERT \s+ INTO', 'DELETE \s+ FROM', 'FROM', - 'SET', '(?: (?: (?: (?: LEFT | RIGHT | FULL ) \s+ )? @@ -64,7 +64,7 @@ my @expression_start_keywords = ( 'ON', 'WHERE', '(?: DEFAULT \s+ )? VALUES', - '(?:NOT \s+)? EXISTS', + '(?: NOT \s+)? EXISTS', 'GROUP \s+ BY', 'HAVING', 'ORDER \s+ BY', @@ -95,6 +95,7 @@ $expr_start_re = qr/ $op_look_behind (?i: $expr_start_re ) $op_look_ahead /x; # * BETWEEN without paranthesis around the ANDed arguments (which # makes it a non-binary op) is detected and accomodated in # _recurse_parse() +# * AS is not really an operator but is handled here as it's also LHS/RHS # this will be included in the $binary_op_re, the distinction is interesting during # testing as one is tighter than the other, plus mathops have different look @@ -111,7 +112,7 @@ sub _math_op_re { $math_re } my $binary_op_re = '(?: NOT \s+)? (?:' . join ('|', qw/IN BETWEEN R?LIKE/) . ')'; $binary_op_re = join "\n\t|\n", - "$op_look_behind (?i: $binary_op_re ) $op_look_ahead", + "$op_look_behind (?i: $binary_op_re | AS ) $op_look_ahead", $math_re, $op_look_behind . 'IS (?:\s+ NOT)?' . "(?= \\s+ NULL \\b | $op_look_ahead )", ; @@ -348,14 +349,14 @@ sub _recurse_parse { elsif ($token =~ /^ (?: OR | AND | \, ) $/xi ) { my $op = ($token eq ',') ? 'LIST' : uc $token; - my $right = $self->_recurse_parse($tokens, PARSE_IN_EXPR); + my $right = $self->_recurse_parse($tokens, PARSE_IN_EXPR) || []; # Merge chunks if logic matches - if (ref $right and $op eq $right->[0]) { - $left = [ (shift @$right ), [$left||(), map { @$_ } @$right] ]; + if (ref $right and @$right and $op eq $right->[0]) { + $left = [ (shift @$right ), [$left||[], map { @$_ } @$right] ]; } else { - $left = [$op => [ $left||(), $right||() ]]; + $left = [$op => [ $left||[], $right ]]; } } # binary operator keywords @@ -476,7 +477,9 @@ sub _unparse { return ''; } + # FIXME - needs a config switch to disable $self->_parenthesis_unroll($tree); + my ($car, $cdr) = @{$tree}[0,1]; if (! defined $car or (! ref $car and ! defined $cdr) ) { @@ -541,7 +544,6 @@ sub _parenthesis_unroll { my $self = shift; my $ast = shift; - #return if $self->parenthesis_significant; return unless (ref $ast and ref $ast->[1]); my $changes; @@ -550,6 +552,7 @@ sub _parenthesis_unroll { $changes = 0; for my $child (@{$ast->[1]}) { + # the current node in this loop is *always* a PAREN if (! ref $child or ! @$child or $child->[0] ne 'PAREN') { push @children, $child; @@ -593,8 +596,8 @@ sub _parenthesis_unroll { $changes++; } - # only one element in the parenthesis which is a binary op - # and has exactly two grandchildren + # an AND/OR expression with only one binop in the parenthesis + # with exactly two grandchildren # the only time when we can *not* unroll this is when both # the parent and the child are mathops (in which case we'll # break precedence) or when the child is BETWEEN (special @@ -602,6 +605,8 @@ sub _parenthesis_unroll { elsif ( @{$child->[1]} == 1 and + ($ast->[0] eq 'AND' or $ast->[0] eq 'OR') + and $child->[1][0][0] =~ SQL::Abstract::Tree::_binary_op_re() and $child->[1][0][0] ne 'BETWEEN'