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});