diff options
author | treeform <starplant@gmail.com> | 2017-12-13 23:34:44 +0000 |
---|---|---|
committer | treeform <starplant@gmail.com> | 2017-12-14 18:36:17 +0000 |
commit | 08d7b5d0317b9192ebb721c0e1abafac6f2853ec (patch) | |
tree | b97a340ce0f1dc35ded2a21182896e95d113d55f | |
parent | c6b33de127ada9d715c16c7215f88cde7bb5a0c6 (diff) | |
download | Nim-08d7b5d0317b9192ebb721c0e1abafac6f2853ec.tar.gz |
fix
-rw-r--r-- | lib/pure/parsesql.nim | 41 | ||||
-rw-r--r-- | tests/stdlib/tparsesql.nim | 808 |
2 files changed, 397 insertions, 452 deletions
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index b53f46f82..f266beef7 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -662,10 +662,12 @@ proc getPrecedence(p: SqlParser): int = elif isOpr(p, "=") or isOpr(p, "<") or isOpr(p, ">") or isOpr(p, ">=") or isOpr(p, "<=") or isOpr(p, "<>") or isOpr(p, "!=") or isKeyw(p, "is") or isKeyw(p, "like"): - result = 3 + result = 4 elif isKeyw(p, "and"): - result = 2 + result = 3 elif isKeyw(p, "or"): + result = 2 + elif isKeyw(p, "between"): result = 1 elif p.tok.kind == tkOperator: # user-defined operator: @@ -1015,6 +1017,8 @@ proc parseUpdate(p: var SqlParser): SqlNode = proc parseDelete(p: var SqlParser): SqlNode = getTok(p) + if isOpr(p, "*"): + getTok(p) result = newNode(nkDelete) eat(p, "from") result.add(primary(p)) @@ -1156,7 +1160,7 @@ proc open(p: var SqlParser, input: Stream, filename: string) = proc parse(p: var SqlParser): SqlNode = ## parses the content of `p`'s input stream and returns the SQL AST. - ## Syntax errors raise an `EInvalidSql` exception. + ## Syntax errors raise an `SqlParseError` exception. result = newNode(nkStmtList) while p.tok.kind != tkEof: parseStmt(p, result) @@ -1173,7 +1177,7 @@ proc close(p: var SqlParser) = proc parseSQL*(input: Stream, filename: string): SqlNode = ## parses the SQL from `input` into an AST and returns the AST. ## `filename` is only used for error messages. - ## Syntax errors raise an `EInvalidSql` exception. + ## Syntax errors raise an `SqlParseError` exception. var p: SqlParser open(p, input, filename) try: @@ -1184,7 +1188,7 @@ proc parseSQL*(input: Stream, filename: string): SqlNode = proc parseSQL*(input: string, filename=""): SqlNode = ## parses the SQL from `input` into an AST and returns the AST. ## `filename` is only used for error messages. - ## Syntax errors raise an `EInvalidSql` exception. + ## Syntax errors raise an `SqlParseError` exception. parseSQL(newStringStream(input), "") @@ -1210,7 +1214,7 @@ proc addKeyw(s: var SqlWriter, thing: string) = s.buffer.add(" ") proc rm(s: var SqlWriter, chars = " \L,") = - while s.buffer[^1] in chars: + while s.buffer.len > 0 and s.buffer[^1] in chars: s.buffer = s.buffer[0..^2] proc newLine(s: var SqlWriter) = @@ -1253,6 +1257,7 @@ proc ra(n: SqlNode, s: var SqlWriter) = else: s.add("\"" & replace(n.strVal, "\"", "\"\"") & "\"") of nkStringLit: + # TODO add e'' as an option? s.add(escape(n.strVal, "'", "'")) of nkBitStringLit: s.add("b'" & n.strVal & "'") @@ -1329,23 +1334,25 @@ proc ra(n: SqlNode, s: var SqlWriter) = assert n.len == 3 s.addKeyw("insert into") ra(n.sons[0], s) + s.add(" ") ra(n.sons[1], s) if n.sons[2].kind == nkDefault: s.addKeyw("default values") else: s.newLine() ra(n.sons[2], s) + s.rm(" ") s.add(';') of nkUpdate: - s.addKeyw("update") - ra(n.sons[0], s) - s.addKeyw("set") - var L = n.len - for i in 1 .. L-2: - if i > 1: s.add(", ") - var it = n.sons[i] - assert it.kind == nkAsgn - ra(it, s) + s.innerKeyw("update"): + ra(n.sons[0], s) + s.innerKeyw("set"): + var L = n.len + for i in 1 .. L-2: + if i > 1: s.add(", ") + var it = n.sons[i] + assert it.kind == nkAsgn + ra(it, s) ra(n.sons[L-1], s) s.add(';') of nkDelete: @@ -1404,8 +1411,8 @@ proc ra(n: SqlNode, s: var SqlWriter) = s.innerKeyw("having"): rs(n, s, "", "", ", ") of nkOrder: - s.addKeyw("order by") - rs(n, s, "", "", ", ") + s.innerKeyw("order by"): + rs(n, s, "", "", ", ") of nkJoin: var joinType = n.sons[0].strVal if joinType == "": diff --git a/tests/stdlib/tparsesql.nim b/tests/stdlib/tparsesql.nim index e73a1d7ee..fe64d3416 100644 --- a/tests/stdlib/tparsesql.nim +++ b/tests/stdlib/tparsesql.nim @@ -1,438 +1,376 @@ -import unittest - -import sequtils -import strutils -import parsesql - -proc fold(str: string): string = - var - lines = str.split("\L") - minCount = 1000 - while lines.len > 0 and lines[0].strip().len == 0: - lines.delete(0, 0) - while lines.len > 0 and lines[lines.len-1].strip().len == 0: - lines.delete(lines.len, lines.len) - for line in lines: - var count = 0 - while line[count] == ' ': - inc count - if minCount > count: - minCount = count - for i, line in lines: - lines[i] = line[minCount..^1] - return lines.join("\L") - -proc parseCheck(have: string, need: string) = - var - sql = parseSQL(have) - sqlHave = renderSQL(sql, true).strip() - sqlNeed = need.fold().strip() - var - haveLines = sqlHave.split("\L") - needLines = sqlNeed.split("\L") - for i in 0..<haveLines.len: - if haveLines[i] != needLines[i]: - echo "" - echo " --- have --- " - echo sqlHave - #echo repr(sql) - echo " --- need --- " - echo sqlNeed - echo " --- lines --- " - echo repr haveLines[i] - echo repr needLines[i] - echo "line: ", i - raise newException(Exception, "Two don't equal.") - - -suite "sql": - - test "basic": - parseCheck "SELECT foo FROM table;", """ - SELECT - foo - FROM - table; - """ - - test "dont require ; at the end": - parseCheck "SELECT foo FROM table", """ - SELECT - foo - FROM - table; - """ - - test "limit": - parseCheck "SELECT foo FROM table limit 10", """ - SELECT - foo - FROM - table - LIMIT - 10; - """ - - test "fields": - parseCheck "SELECT foo, bar, baz FROM table limit 10", """ - SELECT - foo, - bar, - baz - FROM - table - LIMIT - 10; - """ - - test "as_field": - parseCheck "SELECT foo AS bar FROM table", """ - SELECT - foo AS bar - FROM - table; - """ - - parseCheck "SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table", """ - SELECT - foo AS foo_prime, - bar AS bar_prime, - baz AS baz_prime - FROM - table; - """ - - test "select *": - parseCheck "SELECT * FROM table", """ - SELECT - * - FROM - table; - """ - # TODO: COUNT(*) - #parseCheck "SELECT COUNT(*) FROM table", """ - # SELECT * - # FROM table; - #""" - - test "where": - parseCheck """ - SELECT * FROM table - WHERE a = b and c = d - """, """ - SELECT - * - FROM - table - WHERE - ((a = b) and (c = d)); - """ - - parseCheck """ - SELECT * FROM table - WHERE not b - """, """ - SELECT - * - FROM - table - WHERE - (not b); - """ - - parseCheck """ - SELECT - * - FROM - table - WHERE - a and not b - """, """ - SELECT - * - FROM - table - WHERE - (a and (not b)); - """ - - parseCheck """ - SELECT * - FROM table - WHERE not a and not b - """, """ - SELECT - * - FROM - table - WHERE - ((not a) and (not b)); - """ - - parseCheck """ - SELECT * FROM table - WHERE a = b and c = d or n is null and not b + 1 = 3 - """, """ - SELECT - * - FROM - table - WHERE - (((a = b) and (c = d)) or ((n is null) and (((not b) + 1) = 3))); - """ - - test "having": - parseCheck """ - SELECT * FROM table - HAVING a = b and c = d - """, """ - SELECT - * - FROM - table - HAVING - ((a = b) and (c = d)); - """ - - test "group by": - parseCheck """ - SELECT a, b FROM table - GROUP BY a - """, """ - SELECT - a, - b - FROM - table - GROUP BY - a; - """ - - parseCheck """ - SELECT a, b FROM table - GROUP BY 1, 2 - """, """ - SELECT - a, - b - FROM - table - GROUP BY - 1, 2; - """ - - test "as table": - parseCheck """ - SELECT t.a FROM t as t - """, """ - SELECT - t.a - FROM - t AS t; - """ - - test "subselects": - parseCheck """ - SELECT a, b FROM ( - SELECT * FROM t - ) - """, """ - SELECT - a, - b - FROM - ( - SELECT - * - FROM - t - ); - """ - - parseCheck """ - SELECT a, b FROM ( - SELECT * FROM t - ) as foo - """, """ - SELECT - a, - b - FROM - ( - SELECT - * - FROM - t - ) AS foo; - """ - - parseCheck """ - SELECT a, b FROM ( - SELECT * FROM ( - SELECT * FROM ( - SELECT * FROM ( - SELECT * FROM inner as inner1 - ) as inner2 +discard """ + file: "tparsesql.nim" + output: '''select + foo +from + table; +select + foo +from + table; +select + foo +from + table +limit + 10; +select + foo, + bar, + baz +from + table +limit + 10; +select + foo as bar +from + table; +select + foo as foo_prime, + bar as bar_prime, + baz as baz_prime +from + table; +select + * +from + table; +select + * +from + table +where + ((a = b) and (c = d)); +select + * +from + table +where + (not b); +select + * +from + table +where + (a and (not b)); +select + * +from + table +where + (((a = b) and (c = d)) or ((n is null) and (((not b) + 1) = 3))); +select + * +from + table +having + ((a = b) and (c = d)); +select + a, + b +from + table +group by + a; +select + a, + b +from + table +group by + 1, 2; +select + t.a +from + t as t; +select + a, + b +from + ( + select + * + from + t + ); +select + a, + b +from + ( + select + * + from + t + ) as foo; +select + a, + b +from + ( + select + * + from + ( + select + * + from + ( + select + * + from + ( + select + * + from + inner as inner1 + ) as inner2 ) as inner3 - ) as inner4 - ) as inner5 - """, """ - SELECT - a, - b - FROM - ( - SELECT - * - FROM - ( - SELECT - * - FROM - ( - SELECT - * - FROM - ( - SELECT - * - FROM - inner AS inner1 - ) AS inner2 - ) AS inner3 - ) AS inner4 - ) AS inner5; - """ - - parseCheck """ - SELECT a, b FROM - (SELECT * FROM a), - (SELECT * FROM b), - (SELECT * FROM c) - """,""" - SELECT - a, - b - FROM - ( - SELECT - * - FROM - a - ), ( - SELECT - * - FROM - b - ), ( - SELECT - * - FROM - c - ); - """ - - test "original test": - parseCheck """ - CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); - CREATE TABLE holidays ( - num_weeks int, - happiness happiness - ); - CREATE INDEX table1_attr1 ON table1(attr1); - - SELECT * FROM myTab WHERE col1 = 'happy'; - """, """ - CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); - CREATE TABLE holidays( - num_weeks int, - happiness happiness); - CREATE INDEX table1_attr1 ON table1(attr1); - SELECT - * - FROM - myTab - WHERE - (col1 = 'happy'); - """ + ) as inner4 + ) as inner5; +select + a, + b +from + ( + select + * + from + a + ), ( + select + * + from + b + ), ( + select + * + from + c + ); +select + * +from + Products +where + (Price BETWEEN (10 AND 20)); +select + id +from + a +join + b +on + (a.id == b.id); +select + id +from + a +join + ( + select + id + from + c + ) as b +on + (a.id == b.id); +select + id +from + a +INNER join + b +on + (a.id == b.id); +select + id +from + a +OUTER join + b +on + (a.id == b.id); +select + id +from + a +CROSS join + b +on + (a.id == b.id); +create type happiness as enum ('happy', 'very happy', 'ecstatic'); +create table holidays( + num_weeks int, + happiness happiness); +create index table1_attr1 on table1(attr1); +select + * +from + myTab +where + (col1 = 'happy'); + +insert into Customers (CustomerName, ContactName, Address, City, PostalCode, Country) +values ('Cardinal', 'Tom B. Erichsen', 'Skagen 21', 'Stavanger', '4006', 'Norway'); +insert into TableName default values; + +update + Customers +set + ContactName = 'Alfred Schmidt', City = 'Frankfurt' +where + (CustomerID = 1); +delete from table_name; +delete from table_name; +select + * +from + Customers; +select + * +from + Customers +where + ((((CustomerName LIKE 'L%') OR (CustomerName LIKE 'R%')) OR (CustomerName LIKE 'W%')) AND (Country = 'USA')) +order by + CustomerName; + +''' +""" - test "joins": - parseCheck """ - SELECT id FROM a - JOIN b - ON a.id == b.id - """, """ - SELECT - id - FROM - a - JOIN - b - ON - (a.id == b.id); - """ - - parseCheck """ - SELECT id FROM a - JOIN (SELECT id from c) as b - ON a.id == b.id - """, """ - SELECT - id - FROM - a - JOIN - ( - SELECT - id - FROM - c - ) AS b - ON - (a.id == b.id); - """ - - parseCheck """ - SELECT id FROM a - INNER JOIN b - ON a.id == b.id - """, """ - SELECT - id - FROM - a - INNER JOIN - b - ON - (a.id == b.id); - """ - - parseCheck """ - SELECT id FROM a - OUTER JOIN b - ON a.id == b.id - """, """ - SELECT - id - FROM - a - OUTER JOIN - b - ON - (a.id == b.id); - """ +import parsesql - parseCheck """ - SELECT id FROM a - CROSS JOIN b - ON a.id == b.id - """, """ - SELECT - id - FROM - a - CROSS JOIN - b - ON - (a.id == b.id); - """ +echo $parseSQL "SELECT foo FROM table;" +echo $parseSQL "SELECT foo FROM table" +echo $parseSQL "SELECT foo FROM table limit 10" +echo $parseSQL "SELECT foo, bar, baz FROM table limit 10" +echo $parseSQL "SELECT foo AS bar FROM table" +echo $parseSQL "SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table" +echo $parseSQL "SELECT * FROM table" +#TODO add count(*) +#echo $parseSQL "SELECT COUNT(*) FROM table" +echo $parseSQL """ +SELECT * FROM table +WHERE a = b and c = d +""" +echo $parseSQL """ +SELECT * FROM table +WHERE not b +""" +echo $parseSQL """ +SELECT + * +FROM + table +WHERE + a and not b +""" +echo $parseSQL """ +SELECT * FROM table +WHERE a = b and c = d or n is null and not b + 1 = 3 +""" +echo $parseSQL """ +SELECT * FROM table +HAVING a = b and c = d +""" +echo $parseSQL """ +SELECT a, b FROM table +GROUP BY a +""" +echo $parseSQL """ +SELECT a, b FROM table +GROUP BY 1, 2 +""" +echo $parseSQL "SELECT t.a FROM t as t" +echo $parseSQL """ +SELECT a, b FROM ( + SELECT * FROM t +) +""" +echo $parseSQL """ +SELECT a, b FROM ( + SELECT * FROM t +) as foo +""" +echo $parseSQL """ +SELECT a, b FROM ( + SELECT * FROM ( + SELECT * FROM ( + SELECT * FROM ( + SELECT * FROM inner as inner1 + ) as inner2 + ) as inner3 + ) as inner4 +) as inner5 +""" +echo $parseSQL """ +SELECT a, b FROM + (SELECT * FROM a), + (SELECT * FROM b), + (SELECT * FROM c) +""" +echo $parseSQL """ +SELECT * FROM Products +WHERE Price BETWEEN 10 AND 20; +""" +echo $parseSQL """ +SELECT id FROM a +JOIN b +ON a.id == b.id +""" +echo $parseSQL """ +SELECT id FROM a +JOIN (SELECT id from c) as b +ON a.id == b.id +""" +echo $parseSQL """ +SELECT id FROM a +INNER JOIN b +ON a.id == b.id +""" +echo $parseSQL """ +SELECT id FROM a +OUTER JOIN b +ON a.id == b.id +""" +echo $parseSQL """ +SELECT id FROM a +CROSS JOIN b +ON a.id == b.id +""" +echo $parseSQL """ +CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); +CREATE TABLE holidays ( + num_weeks int, + happiness happiness +); +CREATE INDEX table1_attr1 ON table1(attr1); +SELECT * FROM myTab WHERE col1 = 'happy'; +""" +echo $parseSQL """ +INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country) +VALUES ('Cardinal', 'Tom B. Erichsen', 'Skagen 21', 'Stavanger', '4006', 'Norway'); +""" +echo $parseSQL """ +INSERT INTO TableName DEFAULT VALUES +""" +echo $parseSQL """ +UPDATE Customers +SET ContactName = 'Alfred Schmidt', City= 'Frankfurt' +WHERE CustomerID = 1; +""" +echo $parseSQL "DELETE FROM table_name;" +echo $parseSQL "DELETE * FROM table_name;" +echo $parseSQL """ +--Select all: +SELECT * FROM Customers; +""" +echo $parseSQL """ +SELECT * FROM Customers WHERE (CustomerName LIKE 'L%' +OR CustomerName LIKE 'R%' /*OR CustomerName LIKE 'S%' +OR CustomerName LIKE 'T%'*/ OR CustomerName LIKE 'W%') +AND Country='USA' +ORDER BY CustomerName; +""" |