parsesql can parse more SQL
This commit is contained in:
parent
dcc72ea7a9
commit
b2a5f97860
1 changed files with 54 additions and 25 deletions
|
|
@ -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');
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue