From 5231724e770ceb3c2b9d4030b6e82f41341dfc05 Mon Sep 17 00:00:00 2001 From: dimgigov Date: Sun, 17 May 2026 03:29:25 +0300 Subject: [PATCH] fix(executor): handle subqueries in comparisons and per-row UPDATE expressions - Add missing nkSubquery case in lowerExpr so scalar subqueries (e.g. budget > (SELECT AVG(budget) FROM projects)) are lowered correctly instead of being treated as NULL literals. - Move UPDATE SET expression evaluation inside the row loop so column references (e.g. salary + 5000) are resolved against the current row, fixing type validation failures. - Make irAdd/irSub/irMul/irDiv return integer strings when the result is a whole number, avoiding spurious float output for INT columns (e.g. MERGE UPDATE qty = 100 + 50 now yields 150). These fix regressions introduced by switching NULL representation from empty string to \N, which exposed the pre-existing bugs above. Refs: issue #9, issue #10 --- src/barabadb/query/executor.nim | 51 ++++++++++++++++++++++----------- tests/test_all.nim | 2 +- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/barabadb/query/executor.nim b/src/barabadb/query/executor.nim index 620135e..b055a1b 100644 --- a/src/barabadb/query/executor.nim +++ b/src/barabadb/query/executor.nim @@ -581,18 +581,34 @@ proc evalExpr*(expr: IRExpr, row: Table[string, string], ctx: ExecutionContext = if left == "true" or right == "true": return "true" return "false" of irAdd: - try: return $(parseFloat(left) + parseFloat(right)) + try: + let sum = parseFloat(left) + parseFloat(right) + if sum == float(int(sum)): + return $int(sum) + return $sum except: return left & right of irSub: - try: return $(parseFloat(left) - parseFloat(right)) + try: + let diff = parseFloat(left) - parseFloat(right) + if diff == float(int(diff)): + return $int(diff) + return $diff except: return "0" of irMul: - try: return $(parseFloat(left) * parseFloat(right)) + try: + let prod = parseFloat(left) * parseFloat(right) + if prod == float(int(prod)): + return $int(prod) + return $prod except: return "0" of irDiv: try: let r = parseFloat(right) - if r != 0: return $(parseFloat(left) / r) + if r != 0: + let quot = parseFloat(left) / r + if quot == float(int(quot)): + return $int(quot) + return $quot return "0" except: return "0" of irMod: @@ -1528,6 +1544,9 @@ proc lowerExpr*(node: Node): IRExpr = result.binRight = lowerExpr(node.inRight) of nkExists: result = IRExpr(kind: irekExists) + of nkSubquery: + result = IRExpr(kind: irekSubquery) + result.subqueryPlan = lowerSelect(node.subQuery) of nkStar: result = IRExpr(kind: irekStar) of nkWindowExpr: @@ -3166,23 +3185,23 @@ proc executeQuery*(ctx: ExecutionContext, astNode: Node, params: seq[WireValue] of nkUpdate: if stmt.updSet.len == 0: return okResult() # Simple UPDATE: scan table, filter by WHERE, apply SET - var sets = initTable[string, string]() - for s in stmt.updSet: - if s.kind == nkBinOp and s.binOp == bkAssign: - if s.binLeft.kind == nkIdent: - 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 - elif s.binRight.kind == nkBoolLit: $s.binRight.boolVal - elif s.binRight.kind == nkNullLit: "\\N" - else: evalNodeToString(s.binRight) - sets[s.binLeft.identName] = val - # Scan and apply let rows = execScan(ctx, stmt.updTarget) var count = 0 var kvPairs: seq[(string, seq[byte])] for row in rows: + # Compute sets for this row (expressions may reference columns) + var sets = initTable[string, string]() + for s in stmt.updSet: + if s.kind == nkBinOp and s.binOp == bkAssign: + if s.binLeft.kind == nkIdent: + 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 + elif s.binRight.kind == nkBoolLit: $s.binRight.boolVal + elif s.binRight.kind == nkNullLit: "\\N" + else: evalExpr(lowerExpr(s.binRight), row, ctx) + sets[s.binLeft.identName] = val # Check WHERE if stmt.updWhere != nil and stmt.updWhere.whereExpr != nil: let whereExpr = lowerExpr(stmt.updWhere.whereExpr) diff --git a/tests/test_all.nim b/tests/test_all.nim index 9194249..81c7f6d 100644 --- a/tests/test_all.nim +++ b/tests/test_all.nim @@ -2997,7 +2997,7 @@ suite "MERGE Statement": check r.success check r.affectedRows == 1 let verify = qexec.executeQuery(ctx, parse("SELECT * FROM inventory WHERE sku = 'SKU001'")) - check verify.rows[0]["qty"] == "150.0" + check verify.rows[0]["qty"] == "150" test "MERGE WHEN NOT MATCHED INSERT": let r = qexec.executeQuery(ctx, parse("""