X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FSQL%2FAbstract%2FTree.pm;h=f6076ba3881988c6f4ae957097bd23966c76f4dd;hb=1c33db5d0ccacdfc4d6028ec596b0f34a045471c;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..f6076ba 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,17 +63,25 @@ my @expression_start_keywords = (
)',
'ON',
'WHERE',
- 'VALUES',
+ '(?: DEFAULT \s+ )? VALUES',
'EXISTS',
'GROUP \s+ BY',
'HAVING',
'ORDER \s+ BY',
+ 'SKIP',
+ 'FIRST',
'LIMIT',
'OFFSET',
'FOR',
'UNION',
'INTERSECT',
'EXCEPT',
+ 'BEGIN \s+ WORK',
+ 'COMMIT',
+ 'ROLLBACK \s+ TO \s+ SAVEPOINT',
+ 'ROLLBACK',
+ 'SAVEPOINT',
+ 'RELEASE \s+ SAVEPOINT',
'RETURNING',
'ROW_NUMBER \s* \( \s* \) \s+ OVER',
);
@@ -114,6 +124,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 +142,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,
@@ -143,11 +154,16 @@ my %indents = (
join => 1,
'left join' => 1,
on => 2,
+ having => 0,
'group by' => 0,
'order by' => 0,
set => 1,
into => 1,
values => 1,
+ limit => 1,
+ offset => 1,
+ skip => 1,
+ first => 1,
);
my %profiles = (
@@ -158,31 +174,52 @@ my %profiles = (
indent_amount => 2,
newline => "\n",
colormap => {},
- indentmap => { %indents },
+ indentmap => \%indents,
eval { require Term::ANSIColor }
? do {
my $c = \&Term::ANSIColor::color;
+
+ my $red = [$c->('red') , $c->('reset')];
+ my $cyan = [$c->('cyan') , $c->('reset')];
+ my $green = [$c->('green') , $c->('reset')];
+ my $yellow = [$c->('yellow') , $c->('reset')];
+ my $blue = [$c->('blue') , $c->('reset')];
+ my $magenta = [$c->('magenta'), $c->('reset')];
+ my $b_o_w = [$c->('black on_white'), $c->('reset')];
(
- 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')],
- update => [$c->('red'), $c->('reset')],
- 'delete from' => [$c->('red'), $c->('reset')],
-
- set => [$c->('cyan'), $c->('reset')],
- from => [$c->('cyan'), $c->('reset')],
-
- where => [$c->('green'), $c->('reset')],
- values => [$c->('yellow'), $c->('reset')],
-
- join => [$c->('magenta'), $c->('reset')],
- 'left join' => [$c->('magenta'), $c->('reset')],
- on => [$c->('blue'), $c->('reset')],
-
- 'group by' => [$c->('yellow'), $c->('reset')],
- 'order by' => [$c->('yellow'), $c->('reset')],
+ 'begin work' => $b_o_w,
+ commit => $b_o_w,
+ rollback => $b_o_w,
+ savepoint => $b_o_w,
+ 'rollback to savepoint' => $b_o_w,
+ 'release savepoint' => $b_o_w,
+
+ select => $red,
+ 'insert into' => $red,
+ update => $red,
+ 'delete from' => $red,
+
+ set => $cyan,
+ from => $cyan,
+
+ where => $green,
+ values => $yellow,
+
+ join => $magenta,
+ 'left join' => $magenta,
+ on => $blue,
+
+ 'group by' => $yellow,
+ having => $yellow,
+ 'order by' => $yellow,
+
+ skip => $green,
+ first => $green,
+ limit => $green,
+ offset => $green,
}
);
} : (),
@@ -194,7 +231,7 @@ my %profiles = (
indent_amount => 2,
newline => "\n",
colormap => {},
- indentmap => { %indents },
+ indentmap => \%indents,
},
html => {
fill_in_placeholders => 1,
@@ -207,17 +244,34 @@ my %profiles = (
'insert into' => ['' , ''],
update => ['' , ''],
'delete from' => ['' , ''],
- where => ['' , ''],
+
+ set => ['', ''],
from => ['' , ''],
+
+ where => ['' , ''],
+ values => ['', ''],
+
join => ['' , ''],
+ 'left join' => ['',''],
on => ['' , ''],
+
'group by' => ['', ''],
+ having => ['', ''],
'order by' => ['', ''],
- set => ['', ''],
- into => ['', ''],
- values => ['', ''],
+
+ skip => ['', ''],
+ first => ['', ''],
+ limit => ['', ''],
+ offset => ['', ''],
+
+ 'begin work' => ['', ''],
+ commit => ['', ''],
+ rollback => ['', ''],
+ savepoint => ['', ''],
+ 'rollback to savepoint' => ['', ''],
+ 'release savepoint' => ['', ''],
},
- indentmap => { %indents },
+ indentmap => \%indents,
},
none => {
colormap => {},
@@ -230,6 +284,9 @@ sub new {
my $args = shift || {};
my $profile = delete $args->{profile} || 'none';
+
+ die "No such profile '$profile'!" unless exists $profiles{$profile};
+
my $data = $merger->merge( $profiles{$profile}, $args );
bless $data, $class
@@ -245,7 +302,7 @@ sub parse {
defined $token
and
length $token
- and
+ and
$token =~ /\S/
);
}
@@ -315,8 +372,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 +383,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 +431,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 +482,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});