feat: harden storage, schema persistence, fair benches, fix wire crash
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
CI / test (push) Has been cancelled
CI / verify (push) Has been cancelled
Clients CI / build-server (push) Has been cancelled
Clients CI / test-python (push) Has been cancelled
Clients CI / test-javascript (push) Has been cancelled
Clients CI / test-nim (push) Has been cancelled
Clients CI / test-rust (push) Has been cancelled
Core storage: hash MemTable, WAL group commit, L0 compaction rebuild, reader-writer lock, and a global StorageGate so HTTP workers and TCP share the LSM safely under multi-thread access. Schema: durable CREATE/ALTER/DROP under _schema:tables:* with full LSM restore on open. Executor types/values/schema split into query/exec/. Wire protocol: switch default MM to ARC — ORC cycle collector segfaulted after ~20 async INSERTs. Fair multi-tier benchmarks (SQLite/HTTP/wire/PG) and honesty docs for mixed-tier comparisons.
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
## Value / row serialization helpers used across the executor
|
||||
import std/strutils
|
||||
import std/tables
|
||||
import std/json
|
||||
import ../../core/types
|
||||
import types
|
||||
|
||||
proc isNull*(value: string): bool =
|
||||
value == "\\N" or value.toLower() == "null"
|
||||
|
||||
proc valueToString*(v: Value): string =
|
||||
case v.kind
|
||||
of vkNull: return "\\N"
|
||||
of vkString: return v.strVal
|
||||
of vkInt64: return $v.int64Val
|
||||
of vkFloat64: return $v.float64Val
|
||||
of vkBool: return $v.boolVal
|
||||
else: return ""
|
||||
|
||||
proc `%`*(v: Value): JsonNode =
|
||||
case v.kind
|
||||
of vkNull: return newJNull()
|
||||
of vkString: return %v.strVal
|
||||
of vkInt64: return %v.int64Val
|
||||
of vkFloat64: return %v.float64Val
|
||||
of vkBool: return %v.boolVal
|
||||
else: return newJNull()
|
||||
|
||||
proc toString*(v: Value): string = valueToString(v)
|
||||
|
||||
proc `[]=`*(t: var Row, key: string, val: string) =
|
||||
t[key] = Value(kind: vkString, strVal: val)
|
||||
|
||||
proc escapeRowVal*(v: string): string =
|
||||
v.replace("\\", "\\\\").replace(",", "\\,").replace("=", "\\=")
|
||||
|
||||
proc unescapeRowVal*(v: string): string =
|
||||
result = ""
|
||||
var i = 0
|
||||
while i < v.len:
|
||||
if v[i] == '\\' and i + 1 < v.len:
|
||||
case v[i+1]
|
||||
of '\\', ',', '=':
|
||||
result &= v[i+1]
|
||||
i += 2
|
||||
continue
|
||||
else: discard
|
||||
result &= v[i]
|
||||
inc i
|
||||
|
||||
proc parseRowData*(valStr: string): Table[string, string] =
|
||||
## Parse "col1=val1,col2=val2" into a table
|
||||
result = initTable[string, string]()
|
||||
var i = 0
|
||||
var part = ""
|
||||
while i < valStr.len:
|
||||
if valStr[i] == '\\' and i + 1 < valStr.len:
|
||||
part &= valStr[i]
|
||||
part &= valStr[i+1]
|
||||
i += 2
|
||||
continue
|
||||
if valStr[i] == ',':
|
||||
let eqPos = part.find('=')
|
||||
if eqPos >= 0:
|
||||
let k = part[0..<eqPos].strip()
|
||||
let v = unescapeRowVal(part[eqPos+1..^1].strip())
|
||||
result[k] = v
|
||||
part = ""
|
||||
else:
|
||||
part &= valStr[i]
|
||||
inc i
|
||||
if part.len > 0:
|
||||
let eqPos = part.find('=')
|
||||
if eqPos >= 0:
|
||||
let k = part[0..<eqPos].strip()
|
||||
let v = unescapeRowVal(part[eqPos+1..^1].strip())
|
||||
result[k] = v
|
||||
|
||||
proc parseRowDataToValueRow*(valStr: string): Row =
|
||||
result = initTable[string, Value]()
|
||||
for k, v in parseRowData(valStr):
|
||||
result[k] = v
|
||||
|
||||
proc sqlEscapeIdent*(ident: string): string =
|
||||
## Escape SQL identifiers by doubling double-quotes.
|
||||
result = ident.replace("\"", "\"\"")
|
||||
|
||||
proc sqlEscapeString*(s: string): string =
|
||||
## Escape SQL string literals by doubling single-quotes.
|
||||
result = s.replace("'", "''")
|
||||
|
||||
proc buildInsertSql*(table: string, columns: seq[string], rows: seq[seq[string]]): string =
|
||||
## Build a multi-row INSERT statement for bulk import.
|
||||
result = "INSERT INTO \"" & sqlEscapeIdent(table) & "\" ("
|
||||
for i, col in columns:
|
||||
if i > 0: result &= ", "
|
||||
result &= "\"" & sqlEscapeIdent(col) & "\""
|
||||
result &= ") VALUES "
|
||||
for ri, row in rows:
|
||||
if ri > 0: result &= ", "
|
||||
result &= "("
|
||||
for ci, val in row:
|
||||
if ci > 0: result &= ", "
|
||||
if val.len == 0 or val == "\\N":
|
||||
result &= "NULL"
|
||||
else:
|
||||
result &= "'" & sqlEscapeString(val) & "'"
|
||||
result &= ")"
|
||||
|
||||
proc getValue*(values: seq[string], fields: seq[string], colName: string): string =
|
||||
for i, f in fields:
|
||||
if f.toLower() == colName.toLower():
|
||||
if i < values.len: return values[i]
|
||||
return "\\N"
|
||||
return "\\N"
|
||||
|
||||
proc getTableDef*(ctx: ExecutionContext, tableName: string): TableDef =
|
||||
if tableName in ctx.tables: return ctx.tables[tableName]
|
||||
return TableDef(name: tableName, columns: @[], pkColumns: @[], foreignKeys: @[], checks: @[])
|
||||
Reference in New Issue
Block a user