feat: JSON/JSONB validation, multi-column indexes, CTE execution, wire protocol column types

- Add fkJson to wire protocol with serialization/deserialization
- Validate JSON/JSONB on INSERT/UPDATE via std/json
- Send real column type metadata in wire protocol responses
- Update all 4 clients (Nim, Python, JS, Rust) for JSON and columnTypes
- Implement multi-column index parser (CREATE INDEX idx ON t(a, b))
- Add ciColumns to AST nkCreateIndex
- Build composite index keys as table.col1.col2 with val1|val2
- Support multi-column exact match in SELECT for AND chains
- Implement non-recursive CTE execution via ctx.cteTables materialization
- Add tkRecursive token and parse WITH RECURSIVE
- Fix test isolation: use temp dirs for JOIN, migration, and RLS tests
- All tests passing (0 failures)
This commit is contained in:
2026-05-07 10:27:40 +03:00
parent ca345eb256
commit eaa5760fd4
14 changed files with 300 additions and 46 deletions
+13 -5
View File
@@ -275,18 +275,23 @@ proc parseJoinType(p: var Parser): JoinKind =
return jkInner
proc parseWith(p: var Parser): Node =
# WITH name AS (select), name2 AS (select2) SELECT ...
# WITH [RECURSIVE] name AS (select), name2 AS (select2) SELECT ...
let tok = p.expect(tkWith)
result = Node(kind: nkWith, line: tok.line, col: tok.col)
result.withBindings = @[]
var isRecursive = false
if p.peek().kind == tkRecursive:
discard p.advance()
isRecursive = true
# Parse first CTE
let cteName = p.expect(tkIdent).value
discard p.expect(tkAs)
discard p.expect(tkLParen)
let cteQuery = p.parseSelect()
discard p.expect(tkRParen)
result.withBindings.add((cteName, cteQuery))
result.withBindings.add((cteName, cteQuery, isRecursive))
# Parse additional CTEs
while p.match(tkComma):
@@ -295,7 +300,7 @@ proc parseWith(p: var Parser): Node =
discard p.expect(tkLParen)
let query = p.parseSelect()
discard p.expect(tkRParen)
result.withBindings.add((name, query))
result.withBindings.add((name, query, isRecursive))
proc parseSelect(p: var Parser): Node =
# Handle WITH (CTE)
@@ -735,10 +740,13 @@ proc parseCreateIndex(p: var Parser): Node =
discard p.match(tkOn)
let tableName = p.expect(tkIdent).value
discard p.match(tkLParen)
let colName = p.expect(tkIdent).value
var colNames: seq[string] = @[]
colNames.add(p.expect(tkIdent).value)
while p.match(tkComma):
colNames.add(p.expect(tkIdent).value)
discard p.match(tkRParen)
result = Node(kind: nkCreateIndex, ciName: idxName, ciTarget: tableName,
line: tok.line, col: tok.col)
ciColumns: colNames, line: tok.line, col: tok.col)
proc parseBeginTxn(p: var Parser): Node =
let tok = p.expect(tkBegin)