Compare commits
2 Commits
40616ad3c0
...
92e6b99df2
| Author | SHA1 | Date | |
|---|---|---|---|
| 92e6b99df2 | |||
| f8d466d8f3 |
+100
-11
@@ -619,6 +619,13 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
|
||||
except: discard
|
||||
return "false"
|
||||
of irIn:
|
||||
if expr.binRight.kind == irekSubquery:
|
||||
let subRows = executePlan(ctx, expr.binRight.subqueryPlan)
|
||||
for row in subRows:
|
||||
for k, v in row:
|
||||
if k.startsWith("$"): continue
|
||||
if v == left: return "true"
|
||||
return "false"
|
||||
try:
|
||||
let lv = parseFloat(left)
|
||||
let rv = parseFloat(right)
|
||||
@@ -626,6 +633,13 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
|
||||
except: discard
|
||||
return if left == right: "true" else: "false"
|
||||
of irNotIn:
|
||||
if expr.binRight.kind == irekSubquery:
|
||||
let subRows = executePlan(ctx, expr.binRight.subqueryPlan)
|
||||
for row in subRows:
|
||||
for k, v in row:
|
||||
if k.startsWith("$"): continue
|
||||
if v == left: return "false"
|
||||
return "true"
|
||||
try:
|
||||
let lv = parseFloat(left)
|
||||
let rv = parseFloat(right)
|
||||
@@ -823,6 +837,32 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
|
||||
of "current_role":
|
||||
if ctx != nil: return ctx.currentRole
|
||||
return ""
|
||||
of "datetime":
|
||||
if expr.irFuncArgs.len > 0:
|
||||
let arg = evalExpr(expr.irFuncArgs[0], row, ctx).toLower()
|
||||
if arg == "now":
|
||||
return $now().format("yyyy-MM-dd HH:mm:ss")
|
||||
return arg
|
||||
return $now().format("yyyy-MM-dd HH:mm:ss")
|
||||
of "now":
|
||||
return $now().format("yyyy-MM-dd HH:mm:ss")
|
||||
of "strftime":
|
||||
if expr.irFuncArgs.len >= 2:
|
||||
let fmt = evalExpr(expr.irFuncArgs[0], row, ctx)
|
||||
let val = evalExpr(expr.irFuncArgs[1], row, ctx)
|
||||
if fmt == "%s":
|
||||
try:
|
||||
let dt = parse(val, "yyyy-MM-dd HH:mm:ss")
|
||||
return $(dt.toTime().toUnix())
|
||||
except:
|
||||
return "0"
|
||||
elif fmt == "%Y-%m-%dT%H:%M:%SZ":
|
||||
try:
|
||||
let dt = parse(val, "yyyy-MM-dd HH:mm:ss")
|
||||
return format(dt, "yyyy-MM-dd'T'HH:mm:ss'Z'")
|
||||
except:
|
||||
return ""
|
||||
return ""
|
||||
else:
|
||||
# Unknown function: try to evaluate args and return first arg as fallback
|
||||
if expr.irFuncArgs.len > 0:
|
||||
@@ -850,6 +890,7 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext =
|
||||
else: return ""
|
||||
|
||||
proc lowerExpr*(node: Node): IRExpr
|
||||
proc lowerSelect*(node: Node): IRPlan
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Row-Level Security
|
||||
@@ -909,13 +950,12 @@ proc execScan(ctx: ExecutionContext, table: string): seq[Row] =
|
||||
if table in ctx.cteTables:
|
||||
return ctx.cteTables[table]
|
||||
let prefix = table & "."
|
||||
for entry in ctx.db.scanMemTable():
|
||||
if entry.deleted: continue
|
||||
if not entry.key.startsWith(prefix): continue
|
||||
let rest = entry.key[prefix.len..^1]
|
||||
for (key, value) in ctx.db.scanAll():
|
||||
if not key.startsWith(prefix): continue
|
||||
let rest = key[prefix.len..^1]
|
||||
var row: Table[string, string]
|
||||
row["$key"] = rest
|
||||
let valStr = cast[string](entry.value)
|
||||
let valStr = cast[string](value)
|
||||
row["$value"] = valStr
|
||||
# Also parse individual columns
|
||||
for k, v in parseRowData(valStr):
|
||||
@@ -1413,6 +1453,12 @@ proc lowerExpr*(node: Node): IRExpr =
|
||||
orNode.binLeft = result
|
||||
orNode.binRight = eqCmp
|
||||
result = orNode
|
||||
elif node.inRight.kind == nkSubquery:
|
||||
result = IRExpr(kind: irekBinary)
|
||||
result.binOp = irIn
|
||||
result.binLeft = lowerExpr(node.inLeft)
|
||||
result.binRight = IRExpr(kind: irekSubquery)
|
||||
result.binRight.subqueryPlan = lowerSelect(node.inRight.subQuery)
|
||||
else:
|
||||
result = IRExpr(kind: irekBinary)
|
||||
result.binOp = irEq
|
||||
@@ -1447,6 +1493,11 @@ proc lowerExpr*(node: Node): IRExpr =
|
||||
else:
|
||||
result = IRExpr(kind: irekLiteral, literal: IRLiteral(kind: vkNull))
|
||||
|
||||
proc evalNodeToString(node: Node): string =
|
||||
## Evaluate a simple AST node to a string value for INSERT/UPDATE.
|
||||
let ir = lowerExpr(node)
|
||||
return evalExpr(ir, initTable[string, string](), nil)
|
||||
|
||||
proc lowerSelect*(node: Node): IRPlan =
|
||||
result = IRPlan(kind: irpkScan)
|
||||
if node.selFrom != nil:
|
||||
@@ -1571,7 +1622,7 @@ proc lowerSelect*(node: Node): IRPlan =
|
||||
projectPlan.projectSource = result
|
||||
projectPlan.projectExprs = @[]
|
||||
projectPlan.projectAliases = @[]
|
||||
for e in node.selResult:
|
||||
for i, e in node.selResult:
|
||||
projectPlan.projectExprs.add(lowerExpr(e))
|
||||
if e.exprAlias.len > 0:
|
||||
projectPlan.projectAliases.add(e.exprAlias)
|
||||
@@ -1579,8 +1630,12 @@ proc lowerSelect*(node: Node): IRPlan =
|
||||
projectPlan.projectAliases.add(e.identName)
|
||||
elif e.kind == nkPath and e.pathParts.len > 0:
|
||||
projectPlan.projectAliases.add(e.pathParts[^1])
|
||||
elif e.kind == nkFuncCall:
|
||||
projectPlan.projectAliases.add(e.funcName & "()")
|
||||
elif e.kind == nkStar:
|
||||
projectPlan.projectAliases.add("*")
|
||||
else:
|
||||
projectPlan.projectAliases.add("")
|
||||
projectPlan.projectAliases.add("col" & $i)
|
||||
result = projectPlan
|
||||
|
||||
if node.selLimit != nil or node.selOffset != nil:
|
||||
@@ -2524,6 +2579,23 @@ proc bindParams*(node: Node, params: seq[WireValue]): Node =
|
||||
# High-level execute
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
proc getSelectColumns(stmt: Node): seq[string] =
|
||||
result = @[]
|
||||
if stmt.kind != nkSelect: return result
|
||||
for i, e in stmt.selResult:
|
||||
if e.exprAlias.len > 0:
|
||||
result.add(e.exprAlias)
|
||||
elif e.kind == nkIdent:
|
||||
result.add(e.identName)
|
||||
elif e.kind == nkPath and e.pathParts.len > 0:
|
||||
result.add(e.pathParts[^1])
|
||||
elif e.kind == nkFuncCall:
|
||||
result.add(e.funcName & "()")
|
||||
elif e.kind == nkStar:
|
||||
result.add("*")
|
||||
else:
|
||||
result.add("col" & $i)
|
||||
|
||||
proc executeQuery*(ctx: ExecutionContext, astNode: Node, params: seq[WireValue] = @[]): ExecResult =
|
||||
if astNode == nil or astNode.stmts.len == 0:
|
||||
return okResult()
|
||||
@@ -2831,8 +2903,20 @@ proc executeQuery*(ctx: ExecutionContext, astNode: Node, params: seq[WireValue]
|
||||
# Full pipeline execution
|
||||
let plan = lowerSelect(stmt)
|
||||
let rows = executePlan(ctx, plan)
|
||||
var cols = getSelectColumns(stmt)
|
||||
# Expand star to table columns
|
||||
if "*" in cols:
|
||||
var expandedCols: seq[string] = @[]
|
||||
let tbl = ctx.getTableDef(if stmt.selFrom != nil and stmt.selFrom.kind == nkFrom: stmt.selFrom.fromTable else: "")
|
||||
for c in cols:
|
||||
if c == "*":
|
||||
for tc in tbl.columns:
|
||||
expandedCols.add(tc.name)
|
||||
else:
|
||||
expandedCols.add(c)
|
||||
cols = expandedCols
|
||||
if cols.len == 0:
|
||||
let tbl = ctx.getTableDef(if stmt.selFrom != nil and stmt.selFrom.kind == nkFrom: stmt.selFrom.fromTable else: "")
|
||||
var cols: seq[string] = @[]
|
||||
for c in tbl.columns: cols.add(c.name)
|
||||
if cols.len == 0 and rows.len > 0:
|
||||
for k, _ in rows[0]: cols.add(k)
|
||||
@@ -2907,11 +2991,14 @@ proc executeQuery*(ctx: ExecutionContext, astNode: Node, params: seq[WireValue]
|
||||
elif v.kind == nkFloatLit: row.add($v.floatVal)
|
||||
elif v.kind == nkBoolLit: row.add($v.boolVal)
|
||||
elif v.kind == nkNullLit: row.add("")
|
||||
else: row.add("")
|
||||
else: row.add(evalNodeToString(v))
|
||||
else:
|
||||
if rowNode.kind == nkStringLit: row.add(rowNode.strVal)
|
||||
elif rowNode.kind == nkIntLit: row.add($rowNode.intVal)
|
||||
else: row.add("")
|
||||
elif rowNode.kind == nkFloatLit: row.add($rowNode.floatVal)
|
||||
elif rowNode.kind == nkBoolLit: row.add($rowNode.boolVal)
|
||||
elif rowNode.kind == nkNullLit: row.add("")
|
||||
else: row.add(evalNodeToString(rowNode))
|
||||
values.add(row)
|
||||
|
||||
if fields.len == 0:
|
||||
@@ -2955,7 +3042,9 @@ proc executeQuery*(ctx: ExecutionContext, astNode: Node, params: seq[WireValue]
|
||||
let val = if s.binRight.kind == nkStringLit: s.binRight.strVal
|
||||
elif s.binRight.kind == nkIntLit: $s.binRight.intVal
|
||||
elif s.binRight.kind == nkFloatLit: $s.binRight.floatVal
|
||||
else: ""
|
||||
elif s.binRight.kind == nkBoolLit: $s.binRight.boolVal
|
||||
elif s.binRight.kind == nkNullLit: ""
|
||||
else: evalNodeToString(s.binRight)
|
||||
sets[s.binLeft.identName] = val
|
||||
|
||||
# Scan and apply
|
||||
|
||||
@@ -57,6 +57,7 @@ type
|
||||
irekCast
|
||||
irekConditional
|
||||
irekExists
|
||||
irekSubquery
|
||||
irekStar
|
||||
irekJsonPath
|
||||
irekWindowFunc
|
||||
@@ -225,6 +226,8 @@ type
|
||||
elseExpr*: IRExpr
|
||||
of irekExists:
|
||||
existsSubquery*: IRPlan
|
||||
of irekSubquery:
|
||||
subqueryPlan*: IRPlan
|
||||
of irekStar:
|
||||
discard
|
||||
of irekJsonPath:
|
||||
@@ -322,6 +325,8 @@ proc inferExpr*(tc: TypeChecker, expr: IRExpr, context: Table[string, IRType]):
|
||||
return thenType
|
||||
of irekExists:
|
||||
return IRType(name: "bool", kind: itkScalar)
|
||||
of irekSubquery:
|
||||
return IRType(name: "set", kind: itkSet)
|
||||
of irekStar:
|
||||
return IRType(name: "star", kind: itkScalar)
|
||||
of irekJsonPath:
|
||||
|
||||
Reference in New Issue
Block a user