X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FAbstract%2FTree.pm;h=43c5fb73b565103e54e7c2b7413f62ba851cdd7a;hb=c0eaa9fd4c4b58970a8586adf1f598291b4cd9a7;hp=8d8449fb34c6ac6d9ec413ca8a22911959a99744;hpb=3a247d2317bce9466e2153662317caa7a01d9fcc;p=dbsrgits%2FSQL-Abstract.git diff --git a/lib/SQL/Abstract/Tree.pm b/lib/SQL/Abstract/Tree.pm index 8d8449f..43c5fb7 100644 --- a/lib/SQL/Abstract/Tree.pm +++ b/lib/SQL/Abstract/Tree.pm @@ -40,6 +40,8 @@ my $op_look_behind = '(?: (?<= [\,\s\)\(] ) | \A )'; my $quote_left = qr/[\`\'\"\[]/; my $quote_right = qr/[\`\'\"\]]/; +my $placeholder_re = qr/(?: \? | \$\d+ )/x; + # These SQL keywords always signal end of the current expression (except inside # of a parenthesized subexpression). # Format: A list of strings that will be compiled to extended syntax ie. @@ -61,11 +63,13 @@ my @expression_start_keywords = ( )', 'ON', 'WHERE', - 'VALUES', + '(?: DEFAULT \s+ )? VALUES', 'EXISTS', 'GROUP \s+ BY', 'HAVING', 'ORDER \s+ BY', + 'SKIP', + 'FIRST', 'LIMIT', 'OFFSET', 'FOR', @@ -114,6 +118,7 @@ my $all_known_re = join("\n\t|\n", $binary_op_re, "$op_look_behind (?i: AND|OR|NOT ) $op_look_ahead", (map { quotemeta $_ } qw/, ( ) */), + $placeholder_re, ); $all_known_re = qr/$all_known_re/x; @@ -131,7 +136,7 @@ use constant PARSE_RHS => 4; my $expr_term_re = qr/ ^ (?: $expr_start_re | \) ) $/x; my $rhs_term_re = qr/ ^ (?: $expr_term_re | $binary_op_re | (?i: AND | OR | NOT | \, ) ) $/x; -my $func_start_re = qr/^ (?: \? | \$\d+ | \( ) $/x; +my $func_start_re = qr/^ (?: \* | $placeholder_re | \( ) $/x; my %indents = ( select => 0, @@ -148,6 +153,10 @@ my %indents = ( set => 1, into => 1, values => 1, + limit => 1, + offset => 1, + skip => 1, + first => 1, ); my %profiles = ( @@ -164,7 +173,7 @@ my %profiles = ( ? do { my $c = \&Term::ANSIColor::color; ( - placeholder_surround => [$c->('black on_cyan'), $c->('reset')], + placeholder_surround => [q(') . $c->('black on_magenta'), $c->('reset') . q(')], colormap => { select => [$c->('red'), $c->('reset')], 'insert into' => [$c->('red'), $c->('reset')], @@ -183,6 +192,11 @@ my %profiles = ( 'group by' => [$c->('yellow'), $c->('reset')], 'order by' => [$c->('yellow'), $c->('reset')], + + skip => [$c->('green'), $c->('reset')], + first => [$c->('green'), $c->('reset')], + limit => [$c->('green'), $c->('reset')], + offset => [$c->('green'), $c->('reset')], } ); } : (), @@ -207,15 +221,24 @@ my %profiles = ( 'insert into' => ['' , ''], update => ['' , ''], 'delete from' => ['' , ''], - where => ['' , ''], + + set => ['', ''], from => ['' , ''], + + where => ['' , ''], + values => ['', ''], + join => ['' , ''], + 'left join' => ['',''], on => ['' , ''], + 'group by' => ['', ''], 'order by' => ['', ''], - set => ['', ''], - into => ['', ''], - values => ['', ''], + + skip => ['', ''], + first => ['', ''], + limit => ['', ''], + offset => ['', ''], }, indentmap => { %indents }, }, @@ -245,7 +268,7 @@ sub parse { defined $token and length $token - and + and $token =~ /\S/ ); } @@ -315,8 +338,8 @@ sub _recurse_parse { elsif ( $token =~ / ^ $expr_start_re $ /x ) { my $op = uc $token; my $right = $self->_recurse_parse($tokens, PARSE_IN_EXPR); - $left = $left ? [ $left, [$op => [$right] ]] - : [ $op => [$right] ]; + $left = $left ? [ $left, [$op => [$right||()] ]] + : [ $op => [$right||()] ]; } # NOT elsif ( $token =~ /^ NOT $/ix ) { @@ -326,6 +349,10 @@ sub _recurse_parse { : [ $op => [$right] ]; } + elsif ( $token =~ $placeholder_re) { + $left = $left ? [ $left, [ PLACEHOLDER => [ $token ] ] ] + : [ PLACEHOLDER => [ $token ] ]; + } # we're now in "unknown token" land - start eating tokens until # we see something familiar else { @@ -370,7 +397,7 @@ sub pad_keyword { $before = $self->newline . $self->indent($depth + $self->indentmap->{lc $keyword}); } $before = '' if $depth == 0 and defined $starters{lc $keyword}; - return [$before, ' ']; + return [$before, '']; } sub indent { ($_[0]->indent_string||'') x ( ( $_[0]->indent_amount || 0 ) * $_[1] ) } @@ -421,16 +448,20 @@ sub _unparse { return join (' ', map $self->_unparse($_, $bindargs, $depth), @$tree); } elsif ($car eq 'LITERAL') { - if ($cdr->[0] eq '?') { - return $self->fill_in_placeholder($bindargs) - } return $cdr->[0]; } + elsif ($car eq 'PLACEHOLDER') { + return $self->fill_in_placeholder($bindargs); + } elsif ($car eq 'PAREN') { - return '(' . - join(' ', - map $self->_unparse($_, $bindargs, $depth + 2), @{$cdr}) . - ($self->_is_key($cdr)?( $self->newline||'' ).$self->indent($depth + 1):'') . ') '; + return sprintf ('(%s)', + join (' ', map { $self->_unparse($_, $bindargs, $depth + 2) } @{$cdr} ) + . + ($self->_is_key($cdr) + ? ( $self->newline||'' ) . $self->indent($depth + 1) + : '' + ) + ); } elsif ($car eq 'AND' or $car eq 'OR' or $car =~ / ^ $binary_op_re $ /x ) { return join (" $car ", map $self->_unparse($_, $bindargs, $depth), @{$cdr});