diff --git a/src/barabadb/query/exec/params.nim b/src/barabadb/query/exec/params.nim new file mode 100644 index 0000000..cd195b7 --- /dev/null +++ b/src/barabadb/query/exec/params.nim @@ -0,0 +1,180 @@ +## Parameter binding — placeholder substitution and statement column metadata. +## +## Extracted from `executor.nim` (Task 3 of the executor split). +import std/strutils +import std/tables +import ../ast +import ../../protocol/wire +import context + +# ---------------------------------------------------------------------- +# Parameter binding +# ---------------------------------------------------------------------- + +proc doBindParams(node: Node, params: seq[WireValue], idx: var int): Node = + if node == nil: return nil + case node.kind + of nkPlaceholder: + if idx < params.len: + let p = params[idx] + inc idx + case p.kind + of fkString: return Node(kind: nkStringLit, strVal: p.strVal) + of fkInt64: return Node(kind: nkIntLit, intVal: int(p.int64Val)) + of fkInt32: return Node(kind: nkIntLit, intVal: int(p.int32Val)) + of fkInt16: return Node(kind: nkIntLit, intVal: int(p.int16Val)) + of fkInt8: return Node(kind: nkIntLit, intVal: int(p.int8Val)) + of fkFloat64: return Node(kind: nkFloatLit, floatVal: p.float64Val) + of fkFloat32: return Node(kind: nkFloatLit, floatVal: float(p.float32Val)) + of fkBool: return Node(kind: nkBoolLit, boolVal: p.boolVal) + of fkNull: return Node(kind: nkNullLit) + else: return Node(kind: nkNullLit) + else: + return Node(kind: nkNullLit) + of nkBinOp: + result = Node(kind: nkBinOp, binOp: node.binOp, + line: node.line, col: node.col) + result.binLeft = doBindParams(node.binLeft, params, idx) + result.binRight = doBindParams(node.binRight, params, idx) + of nkUnaryOp: + result = Node(kind: nkUnaryOp, unOp: node.unOp, + line: node.line, col: node.col) + result.unOperand = doBindParams(node.unOperand, params, idx) + of nkFuncCall: + result = Node(kind: nkFuncCall, funcName: node.funcName, + line: node.line, col: node.col) + result.funcArgs = @[] + for arg in node.funcArgs: + result.funcArgs.add(doBindParams(arg, params, idx)) + of nkArrayLit: + result = Node(kind: nkArrayLit, line: node.line, col: node.col) + result.arrayElems = @[] + for e in node.arrayElems: + result.arrayElems.add(doBindParams(e, params, idx)) + of nkStatementList: + result = Node(kind: nkStatementList, line: node.line, col: node.col) + result.stmts = @[] + for s in node.stmts: + result.stmts.add(doBindParams(s, params, idx)) + of nkSelect: + result = Node(kind: nkSelect, line: node.line, col: node.col) + result.selDistinct = node.selDistinct + result.selResult = @[] + for e in node.selResult: + result.selResult.add(doBindParams(e, params, idx)) + result.selFrom = node.selFrom # FROM doesn't have placeholders + result.selJoins = @[] + for j in node.selJoins: + var nj = Node(kind: nkJoin, joinKind: j.joinKind, + joinTarget: j.joinTarget, joinAlias: j.joinAlias, + line: j.line, col: j.col) + nj.joinOn = doBindParams(j.joinOn, params, idx) + result.selJoins.add(nj) + result.selWhere = doBindParams(node.selWhere, params, idx) + result.selGroupBy = @[] + for g in node.selGroupBy: + result.selGroupBy.add(doBindParams(g, params, idx)) + result.selHaving = doBindParams(node.selHaving, params, idx) + result.selOrderBy = @[] + for o in node.selOrderBy: + var no = Node(kind: nkOrderBy, orderByDir: o.orderByDir, + line: o.line, col: o.col) + no.orderByExpr = doBindParams(o.orderByExpr, params, idx) + result.selOrderBy.add(no) + result.selLimit = doBindParams(node.selLimit, params, idx) + result.selOffset = doBindParams(node.selOffset, params, idx) + of nkInsert: + result = Node(kind: nkInsert, insTarget: node.insTarget, + line: node.line, col: node.col) + result.insFields = node.insFields + result.insValues = @[] + for v in node.insValues: + result.insValues.add(doBindParams(v, params, idx)) + result.insReturning = node.insReturning + of nkUpdate: + result = Node(kind: nkUpdate, updTarget: node.updTarget, + updAlias: node.updAlias, line: node.line, col: node.col) + result.updSet = @[] + for s in node.updSet: + var ns = Node(kind: nkBinOp, binOp: s.binOp, line: s.line, col: s.col) + ns.binLeft = s.binLeft + ns.binRight = doBindParams(s.binRight, params, idx) + result.updSet.add(ns) + result.updWhere = doBindParams(node.updWhere, params, idx) + result.updReturning = node.updReturning + of nkWhere: + result = Node(kind: nkWhere, line: node.line, col: node.col) + result.whereExpr = doBindParams(node.whereExpr, params, idx) + of nkHaving: + result = Node(kind: nkHaving, line: node.line, col: node.col) + result.havingExpr = doBindParams(node.havingExpr, params, idx) + of nkLimit: + result = Node(kind: nkLimit, line: node.line, col: node.col) + result.limitExpr = doBindParams(node.limitExpr, params, idx) + of nkOffset: + result = Node(kind: nkOffset, line: node.line, col: node.col) + result.offsetExpr = doBindParams(node.offsetExpr, params, idx) + of nkReturning: + result = Node(kind: nkReturning, line: node.line, col: node.col) + result.retExprs = @[] + for e in node.retExprs: + result.retExprs.add(doBindParams(e, params, idx)) + of nkDelete: + result = Node(kind: nkDelete, delTarget: node.delTarget, + delAlias: node.delAlias, line: node.line, col: node.col) + result.delWhere = doBindParams(node.delWhere, params, idx) + result.delReturning = node.delReturning + else: + result = node + +proc bindParams*(node: Node, params: seq[WireValue]): Node = + var idx = 0 + result = doBindParams(node, params, idx) + +# ---------------------------------------------------------------------- +# Statement metadata +# ---------------------------------------------------------------------- + +proc getSelectColumns*(stmt: Node): seq[string] = + result = @[] + if stmt.kind != nkSelect: return result + var seenAliases = initTable[string, int]() + for i, e in stmt.selResult: + var alias = "" + if e.exprAlias.len > 0: + alias = e.exprAlias + elif e.kind == nkIdent: + alias = e.identName + elif e.kind == nkPath and e.pathParts.len > 0: + alias = e.pathParts.join(".") + elif e.kind == nkFuncCall: + var aliasArgs: seq[string] = @[] + for arg in e.funcArgs: + aliasArgs.add(exprToSql(arg)) + alias = e.funcName & "(" & aliasArgs.join(", ") & ")" + elif e.kind == nkStar: + alias = "*" + else: + alias = "col" & $i + if alias in seenAliases: + seenAliases[alias] += 1 + alias = alias & "_" & $seenAliases[alias] + else: + seenAliases[alias] = 0 + result.add(alias) + +proc isDDL*(stmt: Node): bool = + case stmt.kind + of nkCreateTable, nkDropTable, nkAlterTable, + nkCreateView, nkDropView, + nkCreateIndex, nkDropIndex, + nkCreateTrigger, nkDropTrigger, + nkCreateUser, nkDropUser, + nkCreatePolicy, nkDropPolicy, + nkCreateGraph, nkDropGraph, + nkCreateDatabase, nkDropDatabase, + nkGrant, nkRevoke, + nkEnableRLS, nkDisableRLS: + result = true + else: + result = false diff --git a/src/barabadb/query/executor.nim b/src/barabadb/query/executor.nim index f050047..5c984b9 100644 --- a/src/barabadb/query/executor.nim +++ b/src/barabadb/query/executor.nim @@ -44,11 +44,13 @@ import exec/values import exec/schema import exec/context import exec/helpers +import exec/params export types export values export schema export context export helpers +export params proc evalNodeToString(node: Node): string @@ -3433,178 +3435,10 @@ proc executePlan*(ctx: ExecutionContext, plan: IRPlan): seq[Row] = else: return @[] -# ---------------------------------------------------------------------- -# Parameter binding -# ---------------------------------------------------------------------- - -proc doBindParams(node: Node, params: seq[WireValue], idx: var int): Node = - if node == nil: return nil - case node.kind - of nkPlaceholder: - if idx < params.len: - let p = params[idx] - inc idx - case p.kind - of fkString: return Node(kind: nkStringLit, strVal: p.strVal) - of fkInt64: return Node(kind: nkIntLit, intVal: int(p.int64Val)) - of fkInt32: return Node(kind: nkIntLit, intVal: int(p.int32Val)) - of fkInt16: return Node(kind: nkIntLit, intVal: int(p.int16Val)) - of fkInt8: return Node(kind: nkIntLit, intVal: int(p.int8Val)) - of fkFloat64: return Node(kind: nkFloatLit, floatVal: p.float64Val) - of fkFloat32: return Node(kind: nkFloatLit, floatVal: float(p.float32Val)) - of fkBool: return Node(kind: nkBoolLit, boolVal: p.boolVal) - of fkNull: return Node(kind: nkNullLit) - else: return Node(kind: nkNullLit) - else: - return Node(kind: nkNullLit) - of nkBinOp: - result = Node(kind: nkBinOp, binOp: node.binOp, - line: node.line, col: node.col) - result.binLeft = doBindParams(node.binLeft, params, idx) - result.binRight = doBindParams(node.binRight, params, idx) - of nkUnaryOp: - result = Node(kind: nkUnaryOp, unOp: node.unOp, - line: node.line, col: node.col) - result.unOperand = doBindParams(node.unOperand, params, idx) - of nkFuncCall: - result = Node(kind: nkFuncCall, funcName: node.funcName, - line: node.line, col: node.col) - result.funcArgs = @[] - for arg in node.funcArgs: - result.funcArgs.add(doBindParams(arg, params, idx)) - of nkArrayLit: - result = Node(kind: nkArrayLit, line: node.line, col: node.col) - result.arrayElems = @[] - for e in node.arrayElems: - result.arrayElems.add(doBindParams(e, params, idx)) - of nkStatementList: - result = Node(kind: nkStatementList, line: node.line, col: node.col) - result.stmts = @[] - for s in node.stmts: - result.stmts.add(doBindParams(s, params, idx)) - of nkSelect: - result = Node(kind: nkSelect, line: node.line, col: node.col) - result.selDistinct = node.selDistinct - result.selResult = @[] - for e in node.selResult: - result.selResult.add(doBindParams(e, params, idx)) - result.selFrom = node.selFrom # FROM doesn't have placeholders - result.selJoins = @[] - for j in node.selJoins: - var nj = Node(kind: nkJoin, joinKind: j.joinKind, - joinTarget: j.joinTarget, joinAlias: j.joinAlias, - line: j.line, col: j.col) - nj.joinOn = doBindParams(j.joinOn, params, idx) - result.selJoins.add(nj) - result.selWhere = doBindParams(node.selWhere, params, idx) - result.selGroupBy = @[] - for g in node.selGroupBy: - result.selGroupBy.add(doBindParams(g, params, idx)) - result.selHaving = doBindParams(node.selHaving, params, idx) - result.selOrderBy = @[] - for o in node.selOrderBy: - var no = Node(kind: nkOrderBy, orderByDir: o.orderByDir, - line: o.line, col: o.col) - no.orderByExpr = doBindParams(o.orderByExpr, params, idx) - result.selOrderBy.add(no) - result.selLimit = doBindParams(node.selLimit, params, idx) - result.selOffset = doBindParams(node.selOffset, params, idx) - of nkInsert: - result = Node(kind: nkInsert, insTarget: node.insTarget, - line: node.line, col: node.col) - result.insFields = node.insFields - result.insValues = @[] - for v in node.insValues: - result.insValues.add(doBindParams(v, params, idx)) - result.insReturning = node.insReturning - of nkUpdate: - result = Node(kind: nkUpdate, updTarget: node.updTarget, - updAlias: node.updAlias, line: node.line, col: node.col) - result.updSet = @[] - for s in node.updSet: - var ns = Node(kind: nkBinOp, binOp: s.binOp, line: s.line, col: s.col) - ns.binLeft = s.binLeft - ns.binRight = doBindParams(s.binRight, params, idx) - result.updSet.add(ns) - result.updWhere = doBindParams(node.updWhere, params, idx) - result.updReturning = node.updReturning - of nkWhere: - result = Node(kind: nkWhere, line: node.line, col: node.col) - result.whereExpr = doBindParams(node.whereExpr, params, idx) - of nkHaving: - result = Node(kind: nkHaving, line: node.line, col: node.col) - result.havingExpr = doBindParams(node.havingExpr, params, idx) - of nkLimit: - result = Node(kind: nkLimit, line: node.line, col: node.col) - result.limitExpr = doBindParams(node.limitExpr, params, idx) - of nkOffset: - result = Node(kind: nkOffset, line: node.line, col: node.col) - result.offsetExpr = doBindParams(node.offsetExpr, params, idx) - of nkReturning: - result = Node(kind: nkReturning, line: node.line, col: node.col) - result.retExprs = @[] - for e in node.retExprs: - result.retExprs.add(doBindParams(e, params, idx)) - of nkDelete: - result = Node(kind: nkDelete, delTarget: node.delTarget, - delAlias: node.delAlias, line: node.line, col: node.col) - result.delWhere = doBindParams(node.delWhere, params, idx) - result.delReturning = node.delReturning - else: - result = node - -proc bindParams*(node: Node, params: seq[WireValue]): Node = - var idx = 0 - result = doBindParams(node, params, idx) - # ---------------------------------------------------------------------- # High-level execute # ---------------------------------------------------------------------- -proc getSelectColumns(stmt: Node): seq[string] = - result = @[] - if stmt.kind != nkSelect: return result - var seenAliases = initTable[string, int]() - for i, e in stmt.selResult: - var alias = "" - if e.exprAlias.len > 0: - alias = e.exprAlias - elif e.kind == nkIdent: - alias = e.identName - elif e.kind == nkPath and e.pathParts.len > 0: - alias = e.pathParts.join(".") - elif e.kind == nkFuncCall: - var aliasArgs: seq[string] = @[] - for arg in e.funcArgs: - aliasArgs.add(exprToSql(arg)) - alias = e.funcName & "(" & aliasArgs.join(", ") & ")" - elif e.kind == nkStar: - alias = "*" - else: - alias = "col" & $i - if alias in seenAliases: - seenAliases[alias] += 1 - alias = alias & "_" & $seenAliases[alias] - else: - seenAliases[alias] = 0 - result.add(alias) - -proc isDDL(stmt: Node): bool = - case stmt.kind - of nkCreateTable, nkDropTable, nkAlterTable, - nkCreateView, nkDropView, - nkCreateIndex, nkDropIndex, - nkCreateTrigger, nkDropTrigger, - nkCreateUser, nkDropUser, - nkCreatePolicy, nkDropPolicy, - nkCreateGraph, nkDropGraph, - nkCreateDatabase, nkDropDatabase, - nkGrant, nkRevoke, - nkEnableRLS, nkDisableRLS: - result = true - else: - result = false - proc executeQueryImpl(ctx: ExecutionContext, astNode: Node, params: seq[WireValue] = @[]): ExecResult = if astNode == nil or astNode.stmts.len == 0: return okResult()