summary refs log tree commit diff stats
diff options
context:
space:
mode:
authortreeform <starplant@gmail.com>2017-12-13 23:34:44 +0000
committertreeform <starplant@gmail.com>2017-12-14 18:36:17 +0000
commit08d7b5d0317b9192ebb721c0e1abafac6f2853ec (patch)
treeb97a340ce0f1dc35ded2a21182896e95d113d55f
parentc6b33de127ada9d715c16c7215f88cde7bb5a0c6 (diff)
downloadNim-08d7b5d0317b9192ebb721c0e1abafac6f2853ec.tar.gz
fix
-rw-r--r--lib/pure/parsesql.nim41
-rw-r--r--tests/stdlib/tparsesql.nim808
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;
+"""