Сесия 9: Седмици 1+2 — Stabilization + Type Safety
- Поправени deficiencies #5-#8 (GROUP BY bare columns, aggregate names, sync client с blocking socket, thread-safe Lock в SyncClient) - Добавен BlockingClient (net.Socket + Lock) в clients/nim/src/baradb/client.nim - Обновен src/barabadb/client/client.nim със sync клиент без waitFor - Regression тестове за всички 10 deficiencies - Добавен valueKind в IRExpr за типова информация - evalExprValue връща Value discriminated union - Премахнати parseFloat евристики от irAdd/Sub/Mul/Div/Mod/Pow/Neg - INT+INT→INT, INT+FLOAT→FLOAT, FLOAT/INT→FLOAT - 12 нови теста за type safety Тестове: 311 — 0 failures Build: 0 warnings
This commit is contained in:
@@ -3498,3 +3498,159 @@ suite "Auto-Increment & ID Generators":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT snowflake_id(1) AS a, snowflake_id(2) AS b"))
|
||||
check r.success
|
||||
check r.rows[0]["a"] != r.rows[0]["b"]
|
||||
|
||||
|
||||
suite "Deficiency Regression Tests":
|
||||
# Setup for deficiency tests
|
||||
setup:
|
||||
var testDir = getTempDir() / "baradb_def_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks
|
||||
createDir(testDir)
|
||||
var db = newLSMTree(testDir)
|
||||
var ctx = qexec.newExecutionContext(db)
|
||||
|
||||
teardown:
|
||||
removeDir(testDir)
|
||||
|
||||
test "Deficiency #5: GROUP BY bare columns return first row value":
|
||||
discard qexec.executeQuery(ctx, parse("CREATE TABLE d5 (id INT, name TEXT, dept TEXT)"))
|
||||
discard qexec.executeQuery(ctx, parse("INSERT INTO d5 VALUES (1, 'Alice', 'A')"))
|
||||
discard qexec.executeQuery(ctx, parse("INSERT INTO d5 VALUES (2, 'Bob', 'A')"))
|
||||
discard qexec.executeQuery(ctx, parse("INSERT INTO d5 VALUES (3, 'Carol', 'B')"))
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT dept, name, COUNT(*) FROM d5 GROUP BY dept"))
|
||||
check r.success
|
||||
check r.rows.len == 2
|
||||
# dept A: first row name is Alice
|
||||
var foundA, foundB = false
|
||||
for row in r.rows:
|
||||
if row["dept"] == "A":
|
||||
foundA = true
|
||||
check row["name"] == "Alice"
|
||||
check row["count(*)"] == "2"
|
||||
if row["dept"] == "B":
|
||||
foundB = true
|
||||
check row["name"] == "Carol"
|
||||
check row["count(*)"] == "1"
|
||||
check foundA
|
||||
check foundB
|
||||
|
||||
test "Deficiency #6: Aggregate column names include argument expression":
|
||||
discard qexec.executeQuery(ctx, parse("CREATE TABLE d6 (id INT)"))
|
||||
discard qexec.executeQuery(ctx, parse("INSERT INTO d6 VALUES (1), (2)"))
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT count(*) AS cnt, max(id) AS mx FROM d6"))
|
||||
check r.success
|
||||
check r.rows.len == 1
|
||||
check "cnt" in r.rows[0]
|
||||
check "mx" in r.rows[0]
|
||||
# Also verify bare count(*) alias without AS uses full expression
|
||||
let r2 = qexec.executeQuery(ctx, parse("SELECT count(*) FROM d6"))
|
||||
check r2.success
|
||||
check "count(*)" in r2.rows[0]
|
||||
check r2.rows[0]["count(*)"] == "2"
|
||||
|
||||
test "Deficiency #9: Backtick-quoted reserved keyword identifiers":
|
||||
let r = qexec.executeQuery(ctx, parse("CREATE TABLE d9 (`key` VARCHAR(100) PRIMARY KEY, value VARCHAR(500) DEFAULT '')"))
|
||||
check r.success
|
||||
let r2 = qexec.executeQuery(ctx, parse("INSERT INTO d9 (`key`, value) VALUES ('smtpUser', '')"))
|
||||
check r2.success
|
||||
let r3 = qexec.executeQuery(ctx, parse("SELECT `key`, value FROM d9"))
|
||||
check r3.success
|
||||
check r3.rows.len == 1
|
||||
check r3.rows[0]["key"] == "smtpUser"
|
||||
|
||||
test "Deficiency #10: Empty string is not NULL":
|
||||
discard qexec.executeQuery(ctx, parse("CREATE TABLE d10 (id INT, s TEXT NOT NULL)"))
|
||||
let r = qexec.executeQuery(ctx, parse("INSERT INTO d10 (id, s) VALUES (1, '')"))
|
||||
check r.success
|
||||
let r2 = qexec.executeQuery(ctx, parse("SELECT s FROM d10"))
|
||||
check r2.success
|
||||
check r2.rows[0]["s"] == ""
|
||||
let r3 = qexec.executeQuery(ctx, parse("SELECT count(*) FROM d10 WHERE s = ''"))
|
||||
check r3.success
|
||||
check r3.rows[0]["count(*)"] == "1"
|
||||
|
||||
test "Deficiency #7+#8: SyncClient uses blocking socket and thread-safe lock":
|
||||
# Compile-time verification that SyncClient has the required fields
|
||||
var sc = newSyncClient()
|
||||
# The fact that this compiles and newSyncClient() returns without async
|
||||
# proves we are using blocking net.Socket, not AsyncSocket + waitFor.
|
||||
# The internal lock is initialized in newSyncClient and protects query().
|
||||
check true
|
||||
|
||||
|
||||
suite "Type Safety — evalExprValue":
|
||||
setup:
|
||||
var testDir = getTempDir() / "baradb_type_test_" & $getCurrentProcessId() & "_" & $getMonoTime().ticks
|
||||
createDir(testDir)
|
||||
var db = newLSMTree(testDir)
|
||||
var ctx = qexec.newExecutionContext(db)
|
||||
discard qexec.executeQuery(ctx, parse("CREATE TABLE tsi (a INT, b INT)"))
|
||||
discard qexec.executeQuery(ctx, parse("INSERT INTO tsi VALUES (10, 20)"))
|
||||
discard qexec.executeQuery(ctx, parse("CREATE TABLE tsf (x FLOAT, y INT)"))
|
||||
discard qexec.executeQuery(ctx, parse("INSERT INTO tsf VALUES (7.5, 2)"))
|
||||
|
||||
teardown:
|
||||
removeDir(testDir)
|
||||
|
||||
test "INT literal + INT literal = INT":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT 1 + 2 AS r"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "3"
|
||||
|
||||
test "INT literal + FLOAT literal = FLOAT":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT 1 + 2.5 AS r"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "3.5"
|
||||
|
||||
test "FLOAT literal / INT literal = FLOAT":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT 5.0 / 2 AS r"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "2.5"
|
||||
|
||||
test "INT field + INT field = INT":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT a + b AS r FROM tsi"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "30"
|
||||
|
||||
test "INT field * INT field = INT":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT a * b AS r FROM tsi"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "200"
|
||||
|
||||
test "INT field - INT field = INT":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT b - a AS r FROM tsi"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "10"
|
||||
|
||||
test "FLOAT field / INT field = FLOAT":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT x / y AS r FROM tsf"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "3.75"
|
||||
|
||||
test "Unary negation of INT = INT":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT -a AS r FROM tsi"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "-10"
|
||||
|
||||
test "Unary negation of FLOAT = FLOAT":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT -x AS r FROM tsf"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "-7.5"
|
||||
|
||||
test "Arithmetic with table data preserves types":
|
||||
let r = qexec.executeQuery(ctx, parse("SELECT a + x AS r FROM tsi, tsf"))
|
||||
check r.success
|
||||
check r.rows[0]["r"] == "17.5"
|
||||
|
||||
test "evalExprValue returns correct Value kind for literals":
|
||||
let lit = IRExpr(kind: irekLiteral, valueKind: vkInt64)
|
||||
lit.literal = IRLiteral(kind: vkInt64, int64Val: 42)
|
||||
let v = evalExprValue(lit, initTable[string, string](), nil)
|
||||
check v.kind == vkInt64
|
||||
check v.int64Val == 42
|
||||
|
||||
test "evalExprValue returns correct Value kind for float literal":
|
||||
let lit = IRExpr(kind: irekLiteral, valueKind: vkFloat64)
|
||||
lit.literal = IRLiteral(kind: vkFloat64, float64Val: 3.14)
|
||||
let v = evalExprValue(lit, initTable[string, string](), nil)
|
||||
check v.kind == vkFloat64
|
||||
check v.float64Val == 3.14
|
||||
|
||||
Reference in New Issue
Block a user