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
+20 -1
View File
@@ -2,6 +2,7 @@
import std/asyncdispatch
import std/asyncnet
import std/strutils
import std/sequtils
import std/tables
import std/os
import std/endians
@@ -73,6 +74,19 @@ proc parseHeader(data: string): (bool, MessageHeader) =
# Query Execution (pipeline-based)
# ----------------------------------------------------------------------
proc typeToFieldKind*(colType: string): FieldKind =
let t = colType.toUpper()
if t.startsWith("INT") or t == "SERIAL" or t == "BIGINT" or t == "SMALLINT" or t == "BIGSERIAL" or t == "SMALLSERIAL":
return fkInt64
elif t.startsWith("FLOAT") or t == "REAL" or t == "DOUBLE" or t == "NUMERIC":
return fkFloat64
elif t == "BOOLEAN" or t == "BOOL":
return fkBool
elif t == "JSON" or t == "JSONB":
return fkJson
else:
return fkString
proc valueToWire(val: string, colType: string): WireValue =
if val.len == 0 or val.toLower() == "null":
return WireValue(kind: fkNull)
@@ -91,6 +105,8 @@ proc valueToWire(val: string, colType: string): WireValue =
return WireValue(kind: fkBool, boolVal: true)
elif lv in ["false", "f", "no", "0"]:
return WireValue(kind: fkBool, boolVal: false)
elif t == "JSON" or t == "JSONB":
return WireValue(kind: fkJson, jsonVal: val)
return WireValue(kind: fkString, strVal: val)
proc executeQuery(db: LSMTree, ctx: ExecutionContext, query: string, params: seq[WireValue] = @[]): (bool, QueryResult, string) =
@@ -127,7 +143,7 @@ proc executeQuery(db: LSMTree, ctx: ExecutionContext, query: string, params: seq
else:
colTypes = newSeq[string](result.columns.len)
qr.columnTypes = newSeq[FieldKind](result.columns.len)
qr.columnTypes = colTypes.mapIt(typeToFieldKind(it))
qr.rows = @[]
for row in result.rows:
var wireRow: seq[WireValue] = @[]
@@ -154,6 +170,9 @@ proc serializeResult(qr: QueryResult, requestId: uint32): seq[byte] =
# Column names
for col in qr.columns:
payload.writeString(col)
# Column types metadata
for ct in qr.columnTypes:
payload.add(byte(ct))
# Row count
payload.writeUint32(uint32(qr.rows.len))
# Rows