From 08fb391ac151432d375ace9a70984f383ea2d9fc Mon Sep 17 00:00:00 2001 From: dimgigov Date: Thu, 30 Jul 2026 13:18:10 +0300 Subject: [PATCH] refactor(exec): extract context management into exec/context.nim --- src/barabadb/query/exec/context.nim | 172 ++++++++++++++++++++++++++++ src/barabadb/query/executor.nim | 158 +------------------------ 2 files changed, 174 insertions(+), 156 deletions(-) create mode 100644 src/barabadb/query/exec/context.nim diff --git a/src/barabadb/query/exec/context.nim b/src/barabadb/query/exec/context.nim new file mode 100644 index 0000000..9668871 --- /dev/null +++ b/src/barabadb/query/exec/context.nim @@ -0,0 +1,172 @@ +## Execution context lifecycle — creation and per-connection cloning. +## +## Extracted from `executor.nim` (Task 1 of the executor split). +## Also hosts the AST-to-SQL serializer used for VIEW DDL persistence. +import std/strutils +import std/tables +import std/locks +import ../ast +import ../../storage/lsm +import ../../storage/btree +import ../../core/mvcc +import ../../core/registry +import ../../fts/engine as fts +import ../../vector/engine as vengine +import types +import schema + +# ---------------------------------------------------------------------- +# Context management +# ---------------------------------------------------------------------- + +proc newExecutionContext*(db: LSMTree, registry: DatabaseRegistry = nil): ExecutionContext = + result = ExecutionContext(db: db, tables: initTable[string, TableDef](), + btrees: initTable[string, BTreeIndex[string, IndexEntry]](), + views: initTable[string, Node](), + cteTables: initTable[string, seq[Row]](), + ftsIndexes: initTable[string, fts.InvertedIndex](), + vectorIndexes: initTable[string, vengine.HNSWIndex](), + users: initTable[string, UserDef](), + policies: initTable[string, seq[PolicyDef]](), + currentUser: "", currentRole: "", + sessionVars: initTable[string, string](), + autoIncCounters: initTable[string, int64](), + sequences: initTable[string, int64](), + txnManager: newTxnManager(), + onChange: nil, + currentDatabase: "default", + registry: registry) + result.sharedLock = SharedLock() + initLock(result.sharedLock.lock) + restoreSchema(result) + +# ---------------------------------------------------------------------- +# AST to SQL serializer (for VIEW DDL persistence) +# ---------------------------------------------------------------------- + +proc exprToSql*(node: Node): string = + if node == nil: + return "" + case node.kind + of nkIntLit: + return $node.intVal + of nkFloatLit: + return $node.floatVal + of nkStringLit: + return "'" & node.strVal.replace("'", "''") & "'" + of nkBoolLit: + return if node.boolVal: "true" else: "false" + of nkNullLit: + return "null" + of nkIdent: + return "\"" & node.identName.replace("\"", "\"\"") & "\"" + of nkStar: + return "*" + of nkBinOp: + let opStr = case node.binOp + of bkEq: "=" + of bkNotEq: "!=" + of bkLt: "<" + of bkLtEq: "<=" + of bkGt: ">" + of bkGtEq: ">=" + of bkAnd: " AND " + of bkOr: " OR " + of bkAdd: " + " + of bkSub: " - " + of bkMul: " * " + of bkDiv: " / " + else: " " & $node.binOp & " " + return exprToSql(node.binLeft) & opStr & exprToSql(node.binRight) + of nkFuncCall: + if node.funcArgs.len > 0: + return node.funcName & "(" & exprToSql(node.funcArgs[0]) & ")" + else: + return node.funcName & "()" + of nkUnaryOp: + return $node.unOp & " " & exprToSql(node.unOperand) + of nkPath: + return node.pathParts.join(".") + else: + return $node.kind + +proc selectToSql*(node: Node): string = + if node == nil: + return "" + result = "SELECT " + # Column list + for i, e in node.selResult: + if i > 0: result.add(", ") + result.add(exprToSql(e)) + if e.exprAlias.len > 0: + result.add(" AS " & e.exprAlias) + # FROM + if node.selFrom != nil and node.selFrom.kind == nkFrom and node.selFrom.fromTable.len > 0: + result.add(" FROM " & node.selFrom.fromTable) + if node.selFrom.fromAlias.len > 0: + result.add(" AS " & node.selFrom.fromAlias) + # JOINs + for j in node.selJoins: + if j.kind == nkJoin: + let jkStr = case j.joinKind + of jkInner: "INNER JOIN" + of jkLeft: "LEFT JOIN" + of jkRight: "RIGHT JOIN" + of jkFull: "FULL JOIN" + of jkCross: "CROSS JOIN" + if j.joinLateral: + result.add(" " & jkStr & " LATERAL (subquery)") + else: + result.add(" " & jkStr & " " & j.joinTarget.fromTable) + if j.joinAlias.len > 0: + result.add(" AS " & j.joinAlias) + if j.joinOn != nil: + result.add(" ON " & exprToSql(j.joinOn)) + # WHERE + if node.selWhere != nil and node.selWhere.whereExpr != nil: + result.add(" WHERE " & exprToSql(node.selWhere.whereExpr)) + # GROUP BY + if node.selGroupBy.len > 0: + result.add(" GROUP BY ") + for i, g in node.selGroupBy: + if i > 0: result.add(", ") + result.add(exprToSql(g)) + # HAVING + if node.selHaving != nil and node.selHaving.havingExpr != nil: + result.add(" HAVING " & exprToSql(node.selHaving.havingExpr)) + # ORDER BY + if node.selOrderBy.len > 0: + result.add(" ORDER BY ") + for i, o in node.selOrderBy: + if i > 0: result.add(", ") + result.add(exprToSql(o.orderByExpr)) + if o.orderByDir == sdDesc: + result.add(" DESC") + # LIMIT / OFFSET + if node.selLimit != nil and node.selLimit.limitExpr.kind == nkIntLit: + result.add(" LIMIT " & $node.selLimit.limitExpr.intVal) + if node.selOffset != nil and node.selOffset.offsetExpr.kind == nkIntLit: + result.add(" OFFSET " & $node.selOffset.offsetExpr.intVal) + +proc cloneForConnection*(ctx: ExecutionContext): ExecutionContext = + var svCopy = initTable[string, string]() + for k, v in ctx.sessionVars: + svCopy[k] = v + result = ExecutionContext(db: ctx.db, tables: ctx.tables, + btrees: ctx.btrees, views: ctx.views, + cteTables: initTable[string, seq[Row]](), + ftsIndexes: ctx.ftsIndexes, + vectorIndexes: ctx.vectorIndexes, + graphs: ctx.graphs, + users: ctx.users, policies: ctx.policies, + txnManager: ctx.txnManager, + currentUser: ctx.currentUser, currentRole: ctx.currentRole, + sessionVars: svCopy, + autoIncCounters: ctx.autoIncCounters, + sequences: ctx.sequences, + pendingTxn: nil, onChange: ctx.onChange, + embedder: ctx.embedder, + llmClient: ctx.llmClient, + currentDatabase: ctx.currentDatabase, + registry: ctx.registry) + result.sharedLock = ctx.sharedLock diff --git a/src/barabadb/query/executor.nim b/src/barabadb/query/executor.nim index e7726be..a91ee44 100644 --- a/src/barabadb/query/executor.nim +++ b/src/barabadb/query/executor.nim @@ -59,168 +59,14 @@ import ../graph/cypher as cyphermod import exec/types import exec/values import exec/schema +import exec/context export types export values export schema - -# ---------------------------------------------------------------------- -# Context management -# ---------------------------------------------------------------------- +export context proc evalNodeToString(node: Node): string -proc newExecutionContext*(db: LSMTree, registry: DatabaseRegistry = nil): ExecutionContext = - result = ExecutionContext(db: db, tables: initTable[string, TableDef](), - btrees: initTable[string, BTreeIndex[string, IndexEntry]](), - views: initTable[string, Node](), - cteTables: initTable[string, seq[Row]](), - ftsIndexes: initTable[string, fts.InvertedIndex](), - vectorIndexes: initTable[string, vengine.HNSWIndex](), - users: initTable[string, UserDef](), - policies: initTable[string, seq[PolicyDef]](), - currentUser: "", currentRole: "", - sessionVars: initTable[string, string](), - autoIncCounters: initTable[string, int64](), - sequences: initTable[string, int64](), - txnManager: newTxnManager(), - onChange: nil, - currentDatabase: "default", - registry: registry) - result.sharedLock = SharedLock() - initLock(result.sharedLock.lock) - restoreSchema(result) - -# ---------------------------------------------------------------------- -# AST to SQL serializer (for VIEW DDL persistence) -# ---------------------------------------------------------------------- - -proc exprToSql(node: Node): string = - if node == nil: - return "" - case node.kind - of nkIntLit: - return $node.intVal - of nkFloatLit: - return $node.floatVal - of nkStringLit: - return "'" & node.strVal.replace("'", "''") & "'" - of nkBoolLit: - return if node.boolVal: "true" else: "false" - of nkNullLit: - return "null" - of nkIdent: - return "\"" & node.identName.replace("\"", "\"\"") & "\"" - of nkStar: - return "*" - of nkBinOp: - let opStr = case node.binOp - of bkEq: "=" - of bkNotEq: "!=" - of bkLt: "<" - of bkLtEq: "<=" - of bkGt: ">" - of bkGtEq: ">=" - of bkAnd: " AND " - of bkOr: " OR " - of bkAdd: " + " - of bkSub: " - " - of bkMul: " * " - of bkDiv: " / " - else: " " & $node.binOp & " " - return exprToSql(node.binLeft) & opStr & exprToSql(node.binRight) - of nkFuncCall: - if node.funcArgs.len > 0: - return node.funcName & "(" & exprToSql(node.funcArgs[0]) & ")" - else: - return node.funcName & "()" - of nkUnaryOp: - return $node.unOp & " " & exprToSql(node.unOperand) - of nkPath: - return node.pathParts.join(".") - else: - return $node.kind - -proc selectToSql(node: Node): string = - if node == nil: - return "" - result = "SELECT " - # Column list - for i, e in node.selResult: - if i > 0: result.add(", ") - result.add(exprToSql(e)) - if e.exprAlias.len > 0: - result.add(" AS " & e.exprAlias) - # FROM - if node.selFrom != nil and node.selFrom.kind == nkFrom and node.selFrom.fromTable.len > 0: - result.add(" FROM " & node.selFrom.fromTable) - if node.selFrom.fromAlias.len > 0: - result.add(" AS " & node.selFrom.fromAlias) - # JOINs - for j in node.selJoins: - if j.kind == nkJoin: - let jkStr = case j.joinKind - of jkInner: "INNER JOIN" - of jkLeft: "LEFT JOIN" - of jkRight: "RIGHT JOIN" - of jkFull: "FULL JOIN" - of jkCross: "CROSS JOIN" - if j.joinLateral: - result.add(" " & jkStr & " LATERAL (subquery)") - else: - result.add(" " & jkStr & " " & j.joinTarget.fromTable) - if j.joinAlias.len > 0: - result.add(" AS " & j.joinAlias) - if j.joinOn != nil: - result.add(" ON " & exprToSql(j.joinOn)) - # WHERE - if node.selWhere != nil and node.selWhere.whereExpr != nil: - result.add(" WHERE " & exprToSql(node.selWhere.whereExpr)) - # GROUP BY - if node.selGroupBy.len > 0: - result.add(" GROUP BY ") - for i, g in node.selGroupBy: - if i > 0: result.add(", ") - result.add(exprToSql(g)) - # HAVING - if node.selHaving != nil and node.selHaving.havingExpr != nil: - result.add(" HAVING " & exprToSql(node.selHaving.havingExpr)) - # ORDER BY - if node.selOrderBy.len > 0: - result.add(" ORDER BY ") - for i, o in node.selOrderBy: - if i > 0: result.add(", ") - result.add(exprToSql(o.orderByExpr)) - if o.orderByDir == sdDesc: - result.add(" DESC") - # LIMIT / OFFSET - if node.selLimit != nil and node.selLimit.limitExpr.kind == nkIntLit: - result.add(" LIMIT " & $node.selLimit.limitExpr.intVal) - if node.selOffset != nil and node.selOffset.offsetExpr.kind == nkIntLit: - result.add(" OFFSET " & $node.selOffset.offsetExpr.intVal) - -proc cloneForConnection*(ctx: ExecutionContext): ExecutionContext = - var svCopy = initTable[string, string]() - for k, v in ctx.sessionVars: - svCopy[k] = v - result = ExecutionContext(db: ctx.db, tables: ctx.tables, - btrees: ctx.btrees, views: ctx.views, - cteTables: initTable[string, seq[Row]](), - ftsIndexes: ctx.ftsIndexes, - vectorIndexes: ctx.vectorIndexes, - graphs: ctx.graphs, - users: ctx.users, policies: ctx.policies, - txnManager: ctx.txnManager, - currentUser: ctx.currentUser, currentRole: ctx.currentRole, - sessionVars: svCopy, - autoIncCounters: ctx.autoIncCounters, - sequences: ctx.sequences, - pendingTxn: nil, onChange: ctx.onChange, - embedder: ctx.embedder, - llmClient: ctx.llmClient, - currentDatabase: ctx.currentDatabase, - registry: ctx.registry) - result.sharedLock = ctx.sharedLock - # ---------------------------------------------------------------------- # Migration Helpers # ----------------------------------------------------------------------