fix+refactor: soft keywords as identifiers, full test wiring, ORC crash docs

- parser: clause keywords (header, format, status, user, csv, ...) now work
  as identifiers everywhere; IMPORT/EXPORT accept FORMAT csv/HEADER true
- nimble test + CI run all 13 test suites (650 checks green)
- ExecutionContext.registry is now {.cursor.} (breaks registry<->ctx cycle)
- ORC crash reproduced and bisected (tests/orc_repro.py); ARC stays the MM
This commit is contained in:
2026-07-30 13:11:28 +03:00
parent ed5a71913c
commit 2d09edd9f7
9 changed files with 952 additions and 94 deletions
+71
View File
@@ -250,3 +250,74 @@ suite "Bug fixes — IN list, nkPath exprToSql, multi-table joins":
check rmax.success
check rmax.rows.len == 1
check valueToString(rmax.rows[0]["m"]) == "30"
suite "Bug fixes — keyword 'header' usable as column name":
test "CREATE TABLE / INSERT / SELECT with column named 'header'":
var ctx = setupCtx()
defer: teardown(ctx)
# nimforum schema uses a column named 'header' (tkHeader is the CSV IMPORT keyword)
let c = executeQuery(ctx, parse("CREATE TABLE post (id INTEGER PRIMARY KEY, header TEXT, content TEXT)"))
check c.success
let i = executeQuery(ctx, parse("INSERT INTO post (id, header, content) VALUES (1, 'Hello', 'World')"))
check i.success
let r = executeQuery(ctx, parse("SELECT header FROM post WHERE id = 1"))
check r.success
check r.rows.len == 1
check valueToString(r.rows[0]["header"]) == "Hello"
let rw = executeQuery(ctx, parse("SELECT id FROM post WHERE header = 'Hello'"))
check rw.success
check rw.rows.len == 1
test "IMPORT ... HEADER clause still parses after soft-keyword change":
let ast = parse("IMPORT FROM 'data.csv' INTO post HEADER no")
check ast.stmts.len == 1
check ast.stmts[0].kind == nkImportFrom
check ast.stmts[0].impHasHeader == false
let ast2 = parse("EXPORT TO 'out.csv' FROM post HEADER yes")
check ast2.stmts.len == 1
check ast2.stmts[0].kind == nkExportTo
check ast2.stmts[0].expIncludeHeader == true
suite "Bug fixes — clause keywords usable as identifiers":
test "columns named after clause keywords (format, status, user, ...)":
var ctx = setupCtx()
defer: teardown(ctx)
let c = executeQuery(ctx, parse("""
CREATE TABLE kw (id INTEGER PRIMARY KEY, format TEXT, status TEXT, user TEXT,
batch INTEGER, csv TEXT, ndjson TEXT, delimiter TEXT,
migration TEXT, apply TEXT, up TEXT, down TEXT, dryrun TEXT,
policy TEXT, enable TEXT, disable TEXT, recover TEXT,
before TEXT, after TEXT, instead TEXT, of TEXT)
"""))
check c.success
let i = executeQuery(ctx, parse(
"INSERT INTO kw (id, format, status, user, batch, of) VALUES (1, 'csv', 'active', 'admin', 7, 'x')"))
check i.success
let u = executeQuery(ctx, parse("UPDATE kw SET status = 'done' WHERE id = 1"))
check u.success
let r = executeQuery(ctx, parse("SELECT format, status, user, batch, of FROM kw WHERE id = 1"))
check r.success
check r.rows.len == 1
check valueToString(r.rows[0]["format"]) == "csv"
check valueToString(r.rows[0]["status"]) == "done"
check valueToString(r.rows[0]["user"]) == "admin"
check valueToString(r.rows[0]["batch"]) == "7"
let rq = executeQuery(ctx, parse("SELECT kw.status FROM kw WHERE kw.status = 'done'"))
check rq.success
check rq.rows.len == 1
test "IMPORT/EXPORT accept keyword values: FORMAT csv/ndjson/json, HEADER true/false":
let a1 = parse("IMPORT FROM 'd.csv' INTO t FORMAT csv HEADER true")
check a1.stmts[0].impFormat == "csv"
check a1.stmts[0].impHasHeader == true
let a2 = parse("IMPORT FROM 'd.csv' INTO t FORMAT ndjson HEADER false")
check a2.stmts[0].impFormat == "ndjson"
check a2.stmts[0].impHasHeader == false
let a3 = parse("EXPORT TO 'o.csv' FROM t FORMAT json HEADER true")
check a3.stmts[0].expFormat == "json"
check a3.stmts[0].expIncludeHeader == true
let a4 = parse("EXPORT TO 'o.csv' FROM t FORMAT csv DELIMITER ';' HEADER false")
check a4.stmts[0].expFormat == "csv"
check a4.stmts[0].expIncludeHeader == false
+57
View File
@@ -0,0 +1,57 @@
#!/usr/bin/env python3
"""Reproducer for the ORC cycle-collector crash (markGray/trace SIGSEGV).
Usage:
1. Build the server with ORC:
nim c -d:ssl --threads:on --path:src --mm:orc -o:/tmp/baradadb_orc src/baradadb.nim
2. Start it:
BARADB_PORT=39472 BARADB_DATA_DIR=/tmp/baradb_orc_data BARADB_LOG_LEVEL=error /tmp/baradadb_orc
3. Run this script (from the repo root):
python3 tests/orc_repro.py
Expected (as of 2026-07-30, Nim 2.2.10): the server dies with
orc.nim markGray -> trace -> SIGSEGV
triggered from core/server.nim handleClient, and this script fails with
ConnectionResetError. Under ARC (the default in nim.cfg) the same load passes.
Bisect results: 200 pings + 200 SELECTs over TCP are fine; the crash lands
somewhere between 20 and 500 sequential INSERTs (INSERT path only).
Failed root-cause attempts: callback cycle breaks (ed5a719), {.cursor.} on
ExecutionContext.registry (the registry<->ctx cycle), guarding ctx.onChange
against zero WS subscribers. Conclusion: deep ORC+async issue (possibly
upstream Nim), not a single app-level cycle. ARC remains the supported MM.
"""
import asyncio
import sys
sys.path.insert(0, "clients/python")
from baradb import Client
PORT = 39472
async def worker(w: int) -> None:
base = 1000 + w * 100
async with Client("127.0.0.1", PORT) as c:
for i in range(100):
await c.query(f"INSERT INTO orc_stress (id, val) VALUES ({base + i}, 'v{base + i}')")
async def main() -> None:
async with Client("127.0.0.1", PORT) as c:
await c.query("CREATE TABLE orc_stress (id INT PRIMARY KEY, val STRING)")
# Original report: SIGSEGV after ~20 sequential INSERTs under async load
for i in range(1000):
await c.query(f"INSERT INTO orc_stress (id, val) VALUES ({i}, 'x{i}')")
r = await c.query("SELECT COUNT(*) AS n FROM orc_stress")
print("after 1000 sequential INSERTs:", r.rows)
await asyncio.gather(*[worker(w) for w in range(10)])
async with Client("127.0.0.1", PORT) as c:
r = await c.query("SELECT COUNT(*) AS n FROM orc_stress")
print("final count:", r.rows)
print("ping:", await c.ping())
print("ORC STRESS OK")
asyncio.run(main())