use warnings;
use Test::More;
-use Test::Deep;
use Test::Warn;
use SQL::Abstract::Tree;
my $sqlat = SQL::Abstract::Tree->new;
-
-cmp_deeply($sqlat->parse("SELECT a, b.*, * FROM foo WHERE foo.a =1 and foo.b LIKE 'station'"), [
+is_deeply($sqlat->parse("SELECT a, b.*, * FROM foo WHERE foo.a =1 and foo.b LIKE 'station'"), [
[
+ "SELECT",
[
- "SELECT",
[
+ "-LIST",
[
- "LIST",
[
+ "-LITERAL",
[
- "LITERAL",
- [
- "a"
- ]
- ],
+ "a"
+ ]
+ ],
+ [
+ "-LITERAL",
[
- "LITERAL",
- [
- "b.*"
- ]
- ],
+ "b.*"
+ ]
+ ],
+ [
+ "-LITERAL",
[
- "LITERAL",
- [
- "*"
- ]
+ "*"
]
]
]
]
- ],
+ ]
+ ],
+ [
+ "FROM",
[
- "FROM",
[
+ "-LITERAL",
[
- "LITERAL",
- [
- "foo"
- ]
+ "foo"
]
]
]
"=",
[
[
- "LITERAL",
+ "-LITERAL",
[
"foo.a"
]
],
[
- "LITERAL",
+ "-LITERAL",
[
1
]
"LIKE",
[
[
- "LITERAL",
+ "-LITERAL",
[
"foo.b"
]
],
[
- "LITERAL",
+ "-LITERAL",
[
"'station'"
]
]
], 'simple statement parsed correctly');
-cmp_deeply($sqlat->parse( "SELECT * FROM (SELECT * FROM foobar) WHERE foo.a =1 and foo.b LIKE 'station'"), [
+is_deeply($sqlat->parse( "SELECT * FROM (SELECT * FROM foobar) foo WHERE foo.a =1 and foo.b LIKE 'station'"), [
[
+ "SELECT",
[
- "SELECT",
[
+ "-LITERAL",
[
- "LITERAL",
- [
- "*"
- ]
+ "*"
]
]
- ],
+ ]
+ ],
+ [
+ "FROM",
[
- "FROM",
[
+ "-MISC",
[
- "PAREN",
[
+ "-PAREN",
[
[
"SELECT",
[
[
- "LITERAL",
+ "-LITERAL",
[
"*"
]
"FROM",
[
[
- "LITERAL",
+ "-LITERAL",
[
"foobar"
]
]
]
]
+ ],
+ [
+ "-LITERAL",
+ [
+ "foo"
+ ]
]
]
]
"=",
[
[
- "LITERAL",
+ "-LITERAL",
[
"foo.a"
]
],
[
- "LITERAL",
+ "-LITERAL",
[
1
]
"LIKE",
[
[
- "LITERAL",
+ "-LITERAL",
[
"foo.b"
]
],
[
- "LITERAL",
+ "-LITERAL",
[
"'station'"
]
]
], 'subquery statement parsed correctly');
-cmp_deeply($sqlat->parse("SELECT * FROM lolz WHERE ( foo.a =1 ) and foo.b LIKE 'station'"), [
+is_deeply($sqlat->parse( "SELECT [screen].[id], [screen].[name], [screen].[section_id], [screen].[xtype] FROM [users_roles] [me] JOIN [roles] [role] ON [role].[id] = [me].[role_id] JOIN [roles_permissions] [role_permissions] ON [role_permissions].[role_id] = [role].[id] JOIN [permissions] [permission] ON [permission].[id] = [role_permissions].[permission_id] JOIN [permissionscreens] [permission_screens] ON [permission_screens].[permission_id] = [permission].[id] JOIN [screens] [screen] ON [screen].[id] = [permission_screens].[screen_id] WHERE ( [me].[user_id] = ? ) GROUP BY [screen].[id], [screen].[name], [screen].[section_id], [screen].[xtype]"), [
[
+ "SELECT",
[
- "SELECT",
[
+ "-LIST",
[
- "LITERAL",
[
- "*"
+ "-LITERAL",
+ [
+ "[screen].[id]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[screen].[name]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[screen].[section_id]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[screen].[xtype]"
+ ]
]
]
]
- ],
+ ]
+ ],
+ [
+ "FROM",
+ [
+ [
+ "-MISC",
+ [
+ [
+ "-LITERAL",
+ [
+ "[users_roles]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[me]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "JOIN",
[
- "FROM",
[
+ "-MISC",
[
- "LITERAL",
[
- "lolz"
+ "-LITERAL",
+ [
+ "[roles]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[role]"
+ ]
]
]
]
]
],
[
- "WHERE",
+ "ON",
[
[
- "AND",
+ "=",
[
[
- "PAREN",
+ "-LITERAL",
[
- [
- "=",
- [
- [
- "LITERAL",
- [
- "foo.a"
- ]
- ],
- [
- "LITERAL",
- [
- 1
- ]
- ]
- ]
- ]
+ "[role].[id]"
]
],
[
- "LIKE",
+ "-LITERAL",
+ [
+ "[me].[role_id]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "JOIN",
+ [
+ [
+ "-MISC",
+ [
+ [
+ "-LITERAL",
+ [
+ "[roles_permissions]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[role_permissions]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "ON",
+ [
+ [
+ "=",
+ [
+ [
+ "-LITERAL",
+ [
+ "[role_permissions].[role_id]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[role].[id]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "JOIN",
+ [
+ [
+ "-MISC",
+ [
+ [
+ "-LITERAL",
+ [
+ "[permissions]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[permission]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "ON",
+ [
+ [
+ "=",
+ [
+ [
+ "-LITERAL",
+ [
+ "[permission].[id]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[role_permissions].[permission_id]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "JOIN",
+ [
+ [
+ "-MISC",
+ [
+ [
+ "-LITERAL",
+ [
+ "[permissionscreens]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[permission_screens]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "ON",
+ [
+ [
+ "=",
+ [
+ [
+ "-LITERAL",
+ [
+ "[permission_screens].[permission_id]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[permission].[id]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "JOIN",
+ [
+ [
+ "-MISC",
+ [
+ [
+ "-LITERAL",
+ [
+ "[screens]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[screen]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "ON",
+ [
+ [
+ "=",
+ [
+ [
+ "-LITERAL",
+ [
+ "[screen].[id]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[permission_screens].[screen_id]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "WHERE",
+ [
+ [
+ "-PAREN",
+ [
+ [
+ "=",
[
[
- "LITERAL",
+ "-LITERAL",
[
- "foo.b"
+ "[me].[user_id]"
]
],
[
- "LITERAL",
+ "-PLACEHOLDER",
[
- "'station'"
+ "?"
]
]
]
]
]
]
- ]
-], 'simple statement with parens in where parsed correctly');
-
-cmp_deeply($sqlat->parse( "SELECT [screen].[id], [screen].[name], [screen].[section_id], [screen].[xtype] FROM [users_roles] [me] JOIN [roles] [role] ON [role].[id] = [me].[role_id] JOIN [roles_permissions] [role_permissions] ON [role_permissions].[role_id] = [role].[id] JOIN [permissions] [permission] ON [permission].[id] = [role_permissions].[permission_id] JOIN [permissionscreens] [permission_screens] ON [permission_screens].[permission_id] = [permission].[id] JOIN [screens] [screen] ON [screen].[id] = [permission_screens].[screen_id] WHERE ( [me].[user_id] = ? ) GROUP BY [screen].[id], [screen].[name], [screen].[section_id], [screen].[xtype]"), [
+ ],
[
+ "GROUP BY",
[
[
+ "-LIST",
[
[
+ "-LITERAL",
[
- [
- [
- [
- [
- [
- [
- [
- "SELECT",
- [
- [
- "LIST",
- [
- [
- "LITERAL",
- [
- "[screen].[id]"
- ]
- ],
- [
- "LITERAL",
- [
- "[screen].[name]"
- ]
- ],
- [
- "LITERAL",
- [
- "[screen].[section_id]"
- ]
- ],
- [
- "LITERAL",
- [
- "[screen].[xtype]"
- ]
- ]
- ]
- ]
- ]
- ],
- [
- "FROM",
- [
- [
- [
- "LITERAL",
- [
- "[users_roles]"
- ]
- ],
- [
- "LITERAL",
- [
- "[me]"
- ]
- ]
- ]
- ]
- ]
- ],
- [
- "JOIN",
- [
- [
- [
- "LITERAL",
- [
- "[roles]"
- ]
- ],
- [
- "LITERAL",
- [
- "[role]"
- ]
- ]
- ]
- ]
- ]
- ],
- [
- "ON",
- [
- [
- "=",
- [
- [
- "LITERAL",
- [
- "[role].[id]"
- ]
- ],
- [
- "LITERAL",
- [
- "[me].[role_id]"
- ]
- ]
- ]
- ]
- ]
- ]
- ],
+ "[screen].[id]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[screen].[name]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[screen].[section_id]"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "[screen].[xtype]"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+], 'real life statement 1 parsed correctly');
+
+is_deeply($sqlat->parse("SELECT x, y FROM foo WHERE x IN (?, ?, ?, ?)"), [
+ [
+ "SELECT",
+ [
+ [
+ "-LIST",
+ [
+ [
+ "-LITERAL",
+ [
+ "x"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "y"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "FROM",
+ [
+ [
+ "-LITERAL",
+ [
+ "foo"
+ ]
+ ]
+ ]
+ ],
+ [
+ "WHERE",
+ [
+ [
+ "IN",
+ [
+ [
+ "-LITERAL",
+ [
+ "x"
+ ]
+ ],
+ [
+ "-PAREN",
+ [
+ [
+ "-LIST",
+ [
+ [
+ "-PLACEHOLDER",
[
- "JOIN",
- [
- [
- [
- "LITERAL",
- [
- "[roles_permissions]"
- ]
- ],
- [
- "LITERAL",
- [
- "[role_permissions]"
- ]
- ]
- ]
- ]
+ "?"
]
],
[
- "ON",
+ "-PLACEHOLDER",
[
- [
- "=",
- [
- [
- "LITERAL",
- [
- "[role_permissions].[role_id]"
- ]
- ],
- [
- "LITERAL",
- [
- "[role].[id]"
- ]
- ]
- ]
- ]
+ "?"
+ ]
+ ],
+ [
+ "-PLACEHOLDER",
+ [
+ "?"
+ ]
+ ],
+ [
+ "-PLACEHOLDER",
+ [
+ "?"
]
]
- ],
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+], 'Lists parsed correctly');
+
+is_deeply($sqlat->parse('SELECT foo FROM bar ORDER BY x + ? DESC, oomph, y - ? DESC, unf, baz.g / ? ASC, buzz * 0 DESC, foo DESC, ickk ASC'), [
+ [
+ "SELECT",
+ [
+ [
+ "-LITERAL",
+ [
+ "foo"
+ ]
+ ]
+ ]
+ ],
+ [
+ "FROM",
+ [
+ [
+ "-LITERAL",
+ [
+ "bar"
+ ]
+ ]
+ ]
+ ],
+ [
+ "ORDER BY",
+ [
+ [
+ "-LIST",
+ [
+ [
+ "-DESC",
+ [
+ [
+ "-MISC",
[
- "JOIN",
[
+ "-LITERAL",
[
- [
- "LITERAL",
- [
- "[permissions]"
- ]
- ],
- [
- "LITERAL",
- [
- "[permission]"
- ]
- ]
+ "x"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "+"
]
]
]
],
[
- "ON",
+ "-PLACEHOLDER",
+ [
+ "?"
+ ]
+ ]
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "oomph"
+ ]
+ ],
+ [
+ "-DESC",
+ [
+ [
+ "-MISC",
[
[
- "=",
+ "-LITERAL",
[
- [
- "LITERAL",
- [
- "[permission].[id]"
- ]
- ],
- [
- "LITERAL",
- [
- "[role_permissions].[permission_id]"
- ]
- ]
+ "y"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "-"
]
]
]
+ ],
+ [
+ "-PLACEHOLDER",
+ [
+ "?"
+ ]
]
- ],
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "unf"
+ ]
+ ],
+ [
+ "-ASC",
[
- "JOIN",
[
+ "-MISC",
[
[
- "LITERAL",
+ "-LITERAL",
[
- "[permissionscreens]"
+ "baz.g"
]
],
[
- "LITERAL",
+ "-LITERAL",
[
- "[permission_screens]"
+ "/"
]
]
]
+ ],
+ [
+ "-PLACEHOLDER",
+ [
+ "?"
+ ]
]
]
],
[
- "ON",
+ "-DESC",
[
[
- "=",
+ "-MISC",
[
[
- "LITERAL",
+ "-LITERAL",
[
- "[permission_screens].[permission_id]"
+ "buzz"
]
],
[
- "LITERAL",
+ "-LITERAL",
[
- "[permission].[id]"
+ "*"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ 0
]
]
]
]
]
- ]
- ],
- [
- "JOIN",
+ ],
[
+ "-DESC",
[
[
- "LITERAL",
+ "-LITERAL",
[
- "[screens]"
+ "foo"
]
- ],
+ ]
+ ]
+ ],
+ [
+ "-ASC",
+ [
[
- "LITERAL",
+ "-LITERAL",
[
- "[screen]"
+ "ickk"
]
]
]
]
]
- ],
+ ]
+ ]
+ ]
+], 'Crazy ORDER BY parsed correctly');
+
+is_deeply( $sqlat->parse("META SELECT * * FROM (SELECT *, FROM foobar baz buzz) foo bar WHERE NOT NOT NOT EXISTS (SELECT 'cr,ap') AND foo.a = ? STUFF and not (foo.b LIKE 'station') and x = y and a = b and GROUP BY , ORDER BY x x1 x2 y asc, max(y) desc x z desc"), [
+ [
+ "-LITERAL",
+ [
+ "META"
+ ]
+ ],
+ [
+ "SELECT",
+ [
[
- "ON",
+ "-MISC",
[
[
- "=",
+ "-LITERAL",
[
- [
- "LITERAL",
- [
- "[screen].[id]"
- ]
- ],
- [
- "LITERAL",
- [
- "[permission_screens].[screen_id]"
- ]
- ]
+ "*"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "*"
]
]
]
]
- ],
+ ]
+ ],
+ [
+ "FROM",
[
- "WHERE",
[
+ "-MISC",
[
- "PAREN",
[
+ "-PAREN",
[
- "=",
[
+ "SELECT",
[
- "LITERAL",
[
- "[me].[user_id]"
+ "-LIST",
+ [
+ [
+ "-LITERAL",
+ [
+ "*"
+ ]
+ ],
+ []
+ ]
]
- ],
+ ]
+ ],
+ [
+ "FROM",
[
- "PLACEHOLDER",
[
- "?"
+ "-MISC",
+ [
+ [
+ "-LITERAL",
+ [
+ "foobar"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "baz"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "buzz"
+ ]
+ ]
+ ]
]
]
]
]
+ ],
+ [
+ "-LITERAL",
+ [
+ "foo"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "bar"
+ ]
]
]
]
]
],
[
- "GROUP BY",
+ "WHERE",
[
[
- "LIST",
+ "AND",
[
[
- "LITERAL",
- [
- "[screen].[id]"
- ]
+ "NOT",
+ []
],
[
- "LITERAL",
+ "NOT",
+ []
+ ],
+ [
+ "NOT EXISTS",
[
- "[screen].[name]"
+ [
+ "-PAREN",
+ [
+ [
+ "SELECT",
+ [
+ [
+ "-LIST",
+ [
+ [
+ "-LITERAL",
+ [
+ "'cr"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "ap'"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
]
],
[
- "LITERAL",
+ "=",
[
- "[screen].[section_id]"
+ [
+ "-LITERAL",
+ [
+ "foo.a"
+ ]
+ ],
+ [
+ "-MISC",
+ [
+ [
+ "-PLACEHOLDER",
+ [
+ "?"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "STUFF"
+ ]
+ ]
+ ],
+ ]
]
],
[
- "LITERAL",
+ "NOT",
[
- "[screen].[xtype]"
+ [
+ "-PAREN",
+ [
+ [
+ "LIKE",
+ [
+ [
+ "-LITERAL",
+ [
+ "foo.b"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "'station'"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ]
]
- ]
- ]
- ]
- ]
- ]
-], 'real life statement 1 parsed correctly');
-
-cmp_deeply($sqlat->parse("SELECT x, y FROM foo WHERE x IN (?, ?, ?, ?)"), [
- [
- [
- "SELECT",
- [
- [
- "LIST",
+ ],
[
+ "=",
[
- "LITERAL",
[
- "x"
+ "-LITERAL",
+ [
+ "x"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "y"
+ ]
]
- ],
+ ]
+ ],
+ [
+ "=",
[
- "LITERAL",
[
- "y"
+ "-LITERAL",
+ [
+ "a"
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "b"
+ ]
]
]
]
]
]
- ],
+ ]
+ ],
+ [
+ "GROUP BY",
[
- "FROM",
[
+ "-LIST",
[
- "LITERAL",
- [
- "foo"
- ]
+ [],
+ []
]
]
]
],
[
- "WHERE",
+ "ORDER BY",
[
[
- "IN",
+ "-LIST",
[
[
- "LITERAL",
- [
- "x"
- ]
- ],
- [
- "PAREN",
+ "-ASC",
[
[
- "LIST",
+ "-MISC",
[
[
- "PLACEHOLDER",
+ "-LITERAL",
[
- "?"
+ "x"
]
],
[
- "PLACEHOLDER",
+ "-LITERAL",
[
- "?"
+ "x1"
]
],
[
- "PLACEHOLDER",
+ "-LITERAL",
[
- "?"
+ "x2"
]
],
[
- "PLACEHOLDER",
+ "-LITERAL",
[
- "?"
+ "y"
+ ]
+ ]
+ ]
+ ],
+ ],
+ ],
+ [
+ "max",
+ [
+ [
+ "-DESC",
+ [
+ [
+ "-MISC",
+ [
+ [
+ "-MISC",
+ [
+ [
+ "-DESC",
+ [
+ [
+ "-PAREN",
+ [
+ [
+ "-LITERAL",
+ [
+ "y"
+ ]
+ ]
+ ]
+ ]
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "x"
+ ]
+ ],
+ ]
+ ],
+ [
+ "-LITERAL",
+ [
+ "z"
+ ]
+ ]
]
]
]
]
]
]
-], 'Lists parsed correctly');
+], 'Deliberately malformed SQL parsed "correctly"');
+
# test for recursion warnings on huge selectors
+my @lst = ('AA' .. 'zz');
+#@lst = ('AAA' .. 'zzz'); # if you really want to wait a while
warnings_are {
- my $sql = sprintf 'SELECT %s FROM foo', join (', ', map { qq|"$_"| } 'aa' .. 'zz' );
+ my $sql = sprintf 'SELECT %s FROM foo', join (', ', (map { qq|( "$_" )| } @lst), (map { qq|"$_"| } @lst), (map { qq|"$_", ( "$_" )| } @lst) );
my $tree = $sqlat->parse($sql);
is_deeply( $tree, [
"SELECT",
[
[
- "LIST",
+ "-LIST",
[
- map { [ "LITERAL", [ qq|"$_"| ] ] } ('aa' .. 'zz')
+ (map { [ -PAREN => [ [ -LITERAL => [ qq|"$_"| ] ] ] ] } @lst),
+ (map { [ -LITERAL => [ qq|"$_"| ] ] } @lst),
+ (map { [ -LITERAL => [ qq|"$_"| ] ], [ -PAREN => [ [ -LITERAL => [ qq|"$_"| ] ] ] ] } @lst),
]
]
]
"FROM",
[
[
- "LITERAL",
+ "-LITERAL",
[
"foo"
]