summary refs log tree commit diff stats
path: root/lib/pure/parsesql.nim
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pure/parsesql.nim')
-rw-r--r--lib/pure/parsesql.nim79
1 files changed, 54 insertions, 25 deletions
diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim
index d6fafec08..59a93df91 100644
--- a/lib/pure/parsesql.nim
+++ b/lib/pure/parsesql.nim
@@ -496,6 +496,7 @@ type
     nkPrimaryKey,
     nkForeignKey,
     nkNotNull,
+    nkNull,
 
     nkStmtList,
     nkDot,
@@ -613,6 +614,9 @@ proc eat(p: var SqlParser, keyw: string) =
   else:
     sqlError(p, keyw.toUpper() & " expected")
 
+proc opt(p: var SqlParser, kind: TokKind) =
+  if p.tok.kind == kind: getTok(p)
+
 proc parseDataType(p: var SqlParser): SqlNode =
   if isKeyw(p, "enum"):
     result = newNode(nkEnumDef)
@@ -705,7 +709,7 @@ proc primary(p: var SqlParser): SqlNode =
       result = newNode(nkCall)
       result.add(a)
       getTok(p)
-      while true:
+      while p.tok.kind != tkParRi:
         result.add(parseExpr(p))
         if p.tok.kind == tkComma: getTok(p)
         else: break
@@ -776,9 +780,19 @@ proc parseConstraint(p: var SqlParser): SqlNode =
   expectIdent(p)
   result.add(newNode(nkIdent, p.tok.literal))
   getTok(p)
-  eat(p, "check")
+  optKeyw(p, "check")
   result.add(parseExpr(p))
 
+proc parseParIdentList(p: var SqlParser, father: SqlNode) =
+  eat(p, tkParLe)
+  while true:
+    expectIdent(p)
+    father.add(newNode(nkIdent, p.tok.literal))
+    getTok(p)
+    if p.tok.kind != tkComma: break
+    getTok(p)
+  eat(p, tkParRi)
+
 proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
   while true:
     if isKeyw(p, "default"):
@@ -795,6 +809,9 @@ proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
       getTok(p)
       eat(p, "null")
       result.add(newNode(nkNotNull))
+    elif isKeyw(p, "null"):
+      getTok(p)
+      result.add(newNode(nkNull))
     elif isKeyw(p, "identity"):
       getTok(p)
       result.add(newNode(nkIdentity))
@@ -807,6 +824,7 @@ proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
     elif isKeyw(p, "constraint"):
       result.add(parseConstraint(p))
     elif isKeyw(p, "unique"):
+      getTok(p)
       result.add(newNode(nkUnique))
     else:
       break
@@ -829,16 +847,6 @@ proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode =
   else:
     result = newNode(k)
 
-proc parseParIdentList(p: var SqlParser, father: SqlNode) =
-  eat(p, tkParLe)
-  while true:
-    expectIdent(p)
-    father.add(newNode(nkIdent, p.tok.literal))
-    getTok(p)
-    if p.tok.kind != tkComma: break
-    getTok(p)
-  eat(p, tkParRi)
-
 proc parseTableConstraint(p: var SqlParser): SqlNode =
   if isKeyw(p, "primary"):
     getTok(p)
@@ -866,20 +874,34 @@ proc parseTableConstraint(p: var SqlParser): SqlNode =
   else:
     sqlError(p, "column definition expected")
 
+proc parseUnique(p: var SqlParser): SqlNode =
+  result = parseExpr(p)
+  if result.kind == nkCall: result.kind = nkUnique
+
 proc parseTableDef(p: var SqlParser): SqlNode =
   result = parseIfNotExists(p, nkCreateTable)
   expectIdent(p)
   result.add(newNode(nkIdent, p.tok.literal))
   getTok(p)
   if p.tok.kind == tkParLe:
-    while true:
-      getTok(p)
-      if p.tok.kind == tkIdentifier or p.tok.kind == tkQuotedIdentifier:
+    getTok(p)
+    while p.tok.kind != tkParRi:
+      if isKeyw(p, "constraint"):
+        result.add parseConstraint(p)
+      elif isKeyw(p, "primary") or isKeyw(p, "foreign"):
+        result.add parseTableConstraint(p)
+      elif isKeyw(p, "unique"):
+        result.add parseUnique(p)
+      elif p.tok.kind == tkIdentifier or p.tok.kind == tkQuotedIdentifier:
         result.add(parseColumnDef(p))
       else:
         result.add(parseTableConstraint(p))
       if p.tok.kind != tkComma: break
+      getTok(p)
     eat(p, tkParRi)
+    # skip additional crap after 'create table (...) crap;'
+    while p.tok.kind notin {tkSemicolon, tkEof}:
+      getTok(p)
 
 proc parseTypeDef(p: var SqlParser): SqlNode =
   result = parseIfNotExists(p, nkCreateType)
@@ -1046,7 +1068,7 @@ proc parseSelect(p: var SqlParser): SqlNode =
       getTok(p)
     result.add(n)
 
-proc parseStmt(p: var SqlParser): SqlNode =
+proc parseStmt(p: var SqlParser; parent: SqlNode) =
   if isKeyw(p, "create"):
     getTok(p)
     optKeyw(p, "cached")
@@ -1058,21 +1080,23 @@ proc parseStmt(p: var SqlParser): SqlNode =
     optKeyw(p, "unique")
     optKeyw(p, "hash")
     if isKeyw(p, "table"):
-      result = parseTableDef(p)
+      parent.add parseTableDef(p)
     elif isKeyw(p, "type"):
-      result = parseTypeDef(p)
+      parent.add parseTypeDef(p)
     elif isKeyw(p, "index"):
-      result = parseIndexDef(p)
+      parent.add parseIndexDef(p)
     else:
       sqlError(p, "TABLE expected")
   elif isKeyw(p, "insert"):
-    result = parseInsert(p)
+    parent.add parseInsert(p)
   elif isKeyw(p, "update"):
-    result = parseUpdate(p)
+    parent.add parseUpdate(p)
   elif isKeyw(p, "delete"):
-    result = parseDelete(p)
+    parent.add parseDelete(p)
   elif isKeyw(p, "select"):
-    result = parseSelect(p)
+    parent.add parseSelect(p)
+  elif isKeyw(p, "begin"):
+    getTok(p)
   else:
     sqlError(p, "CREATE expected")
 
@@ -1089,9 +1113,8 @@ proc parse(p: var SqlParser): SqlNode =
   ## Syntax errors raise an `EInvalidSql` exception.
   result = newNode(nkStmtList)
   while p.tok.kind != tkEof:
-    var s = parseStmt(p)
+    parseStmt(p, result)
     eat(p, tkSemicolon)
-    result.add(s)
   if result.len == 1:
     result = result.sons[0]
 
@@ -1147,6 +1170,8 @@ proc ra(n: SqlNode, s: var string, indent: int) =
     rs(n, s, indent)
   of nkNotNull:
     s.add(" not null")
+  of nkNull:
+    s.add(" null")
   of nkDot:
     ra(n.sons[0], s, indent)
     s.add(".")
@@ -1330,6 +1355,10 @@ proc renderSQL*(n: SqlNode): string =
   result = ""
   ra(n, result, 0)
 
+proc `$`*(n: SqlNode): string =
+  ## an alias for `renderSQL`.
+  renderSQL(n)
+
 when not defined(testing) and isMainModule:
   echo(renderSQL(parseSQL(newStringStream("""
       CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');