parsesql can parse more SQL

This commit is contained in:
Araq 2017-09-01 14:42:36 +02:00
commit b2a5f97860

View file

@ -496,6 +496,7 @@ type
nkPrimaryKey, nkPrimaryKey,
nkForeignKey, nkForeignKey,
nkNotNull, nkNotNull,
nkNull,
nkStmtList, nkStmtList,
nkDot, nkDot,
@ -613,6 +614,9 @@ proc eat(p: var SqlParser, keyw: string) =
else: else:
sqlError(p, keyw.toUpper() & " expected") 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 = proc parseDataType(p: var SqlParser): SqlNode =
if isKeyw(p, "enum"): if isKeyw(p, "enum"):
result = newNode(nkEnumDef) result = newNode(nkEnumDef)
@ -705,7 +709,7 @@ proc primary(p: var SqlParser): SqlNode =
result = newNode(nkCall) result = newNode(nkCall)
result.add(a) result.add(a)
getTok(p) getTok(p)
while true: while p.tok.kind != tkParRi:
result.add(parseExpr(p)) result.add(parseExpr(p))
if p.tok.kind == tkComma: getTok(p) if p.tok.kind == tkComma: getTok(p)
else: break else: break
@ -776,9 +780,19 @@ proc parseConstraint(p: var SqlParser): SqlNode =
expectIdent(p) expectIdent(p)
result.add(newNode(nkIdent, p.tok.literal)) result.add(newNode(nkIdent, p.tok.literal))
getTok(p) getTok(p)
eat(p, "check") optKeyw(p, "check")
result.add(parseExpr(p)) 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) = proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
while true: while true:
if isKeyw(p, "default"): if isKeyw(p, "default"):
@ -795,6 +809,9 @@ proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
getTok(p) getTok(p)
eat(p, "null") eat(p, "null")
result.add(newNode(nkNotNull)) result.add(newNode(nkNotNull))
elif isKeyw(p, "null"):
getTok(p)
result.add(newNode(nkNull))
elif isKeyw(p, "identity"): elif isKeyw(p, "identity"):
getTok(p) getTok(p)
result.add(newNode(nkIdentity)) result.add(newNode(nkIdentity))
@ -807,6 +824,7 @@ proc parseColumnConstraints(p: var SqlParser, result: SqlNode) =
elif isKeyw(p, "constraint"): elif isKeyw(p, "constraint"):
result.add(parseConstraint(p)) result.add(parseConstraint(p))
elif isKeyw(p, "unique"): elif isKeyw(p, "unique"):
getTok(p)
result.add(newNode(nkUnique)) result.add(newNode(nkUnique))
else: else:
break break
@ -829,16 +847,6 @@ proc parseIfNotExists(p: var SqlParser, k: SqlNodeKind): SqlNode =
else: else:
result = newNode(k) 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 = proc parseTableConstraint(p: var SqlParser): SqlNode =
if isKeyw(p, "primary"): if isKeyw(p, "primary"):
getTok(p) getTok(p)
@ -866,20 +874,34 @@ proc parseTableConstraint(p: var SqlParser): SqlNode =
else: else:
sqlError(p, "column definition expected") 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 = proc parseTableDef(p: var SqlParser): SqlNode =
result = parseIfNotExists(p, nkCreateTable) result = parseIfNotExists(p, nkCreateTable)
expectIdent(p) expectIdent(p)
result.add(newNode(nkIdent, p.tok.literal)) result.add(newNode(nkIdent, p.tok.literal))
getTok(p) getTok(p)
if p.tok.kind == tkParLe: if p.tok.kind == tkParLe:
while true: getTok(p)
getTok(p) while p.tok.kind != tkParRi:
if p.tok.kind == tkIdentifier or p.tok.kind == tkQuotedIdentifier: 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)) result.add(parseColumnDef(p))
else: else:
result.add(parseTableConstraint(p)) result.add(parseTableConstraint(p))
if p.tok.kind != tkComma: break if p.tok.kind != tkComma: break
getTok(p)
eat(p, tkParRi) 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 = proc parseTypeDef(p: var SqlParser): SqlNode =
result = parseIfNotExists(p, nkCreateType) result = parseIfNotExists(p, nkCreateType)
@ -1046,7 +1068,7 @@ proc parseSelect(p: var SqlParser): SqlNode =
getTok(p) getTok(p)
result.add(n) result.add(n)
proc parseStmt(p: var SqlParser): SqlNode = proc parseStmt(p: var SqlParser; parent: SqlNode) =
if isKeyw(p, "create"): if isKeyw(p, "create"):
getTok(p) getTok(p)
optKeyw(p, "cached") optKeyw(p, "cached")
@ -1058,21 +1080,23 @@ proc parseStmt(p: var SqlParser): SqlNode =
optKeyw(p, "unique") optKeyw(p, "unique")
optKeyw(p, "hash") optKeyw(p, "hash")
if isKeyw(p, "table"): if isKeyw(p, "table"):
result = parseTableDef(p) parent.add parseTableDef(p)
elif isKeyw(p, "type"): elif isKeyw(p, "type"):
result = parseTypeDef(p) parent.add parseTypeDef(p)
elif isKeyw(p, "index"): elif isKeyw(p, "index"):
result = parseIndexDef(p) parent.add parseIndexDef(p)
else: else:
sqlError(p, "TABLE expected") sqlError(p, "TABLE expected")
elif isKeyw(p, "insert"): elif isKeyw(p, "insert"):
result = parseInsert(p) parent.add parseInsert(p)
elif isKeyw(p, "update"): elif isKeyw(p, "update"):
result = parseUpdate(p) parent.add parseUpdate(p)
elif isKeyw(p, "delete"): elif isKeyw(p, "delete"):
result = parseDelete(p) parent.add parseDelete(p)
elif isKeyw(p, "select"): elif isKeyw(p, "select"):
result = parseSelect(p) parent.add parseSelect(p)
elif isKeyw(p, "begin"):
getTok(p)
else: else:
sqlError(p, "CREATE expected") sqlError(p, "CREATE expected")
@ -1089,9 +1113,8 @@ proc parse(p: var SqlParser): SqlNode =
## Syntax errors raise an `EInvalidSql` exception. ## Syntax errors raise an `EInvalidSql` exception.
result = newNode(nkStmtList) result = newNode(nkStmtList)
while p.tok.kind != tkEof: while p.tok.kind != tkEof:
var s = parseStmt(p) parseStmt(p, result)
eat(p, tkSemicolon) eat(p, tkSemicolon)
result.add(s)
if result.len == 1: if result.len == 1:
result = result.sons[0] result = result.sons[0]
@ -1147,6 +1170,8 @@ proc ra(n: SqlNode, s: var string, indent: int) =
rs(n, s, indent) rs(n, s, indent)
of nkNotNull: of nkNotNull:
s.add(" not null") s.add(" not null")
of nkNull:
s.add(" null")
of nkDot: of nkDot:
ra(n.sons[0], s, indent) ra(n.sons[0], s, indent)
s.add(".") s.add(".")
@ -1330,6 +1355,10 @@ proc renderSQL*(n: SqlNode): string =
result = "" result = ""
ra(n, result, 0) ra(n, result, 0)
proc `$`*(n: SqlNode): string =
## an alias for `renderSQL`.
renderSQL(n)
when not defined(testing) and isMainModule: when not defined(testing) and isMainModule:
echo(renderSQL(parseSQL(newStringStream(""" echo(renderSQL(parseSQL(newStringStream("""
CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');