feat: Phase 5-6 — CREATE VIEW, migrations, deadlock detection, JSON logging, admin UI

Phase 5 — ERP Features:
- CREATE VIEW name AS SELECT ... — parser + executor with view expansion
- DROP VIEW — parser + executor
- CREATE MIGRATION name AS 'sql' — parser + executor, stored in LSM-Tree
- APPLY MIGRATION name — parser + executor, replays stored migration SQL
- Views table in ExecutionContext, expanded on SELECT FROM view

Phase 6 — Production Readiness:
- Deadlock detection: timeout-based auto-abort for stale transactions (30s default)
- TxnManager.txnTimeoutMs configurable
- Structured JSON logging module (logging.nim) with levels: debug/info/warn/error
- Admin dashboard Web UI at GET /admin — SQL playground, schema browser, metrics
- Dark theme HTML/CSS with tabs

Lexer: added tkView, tkMigration, tkApply tokens
Parser: parseCreateView, parseDropView, parseCreateMigration, parseApplyMigration
AST: nkCreateView, nkDropView, nkCreateMigration, nkApplyMigration nodes

All 216 tests pass
This commit is contained in:
2026-05-06 13:54:16 +03:00
parent 633c5d127c
commit 675ab26a6e
7 changed files with 307 additions and 8 deletions
+12 -2
View File
@@ -42,14 +42,15 @@ type
nextLsn: uint64
activeTxns: Table[TxnId, Transaction]
committedTxns: seq[TxnId]
globalVersions*: Table[string, seq[VersionedRecord]] # key -> versions
globalVersions*: Table[string, seq[VersionedRecord]]
txnTimeoutMs: int64
proc `==`*(a, b: TxnId): bool {.borrow.}
proc `==`*(a, b: Lsn): bool {.borrow.}
proc `$`*(id: TxnId): string = $uint64(id)
proc `$`*(lsn: Lsn): string = $uint64(lsn)
proc newTxnManager*(): TxnManager =
proc newTxnManager*(txnTimeoutMs: int64 = 30000): TxnManager =
new(result)
initLock(result.lock)
result.nextTxnId = 1
@@ -57,6 +58,7 @@ proc newTxnManager*(): TxnManager =
result.activeTxns = initTable[TxnId, Transaction]()
result.committedTxns = @[]
result.globalVersions = initTable[string, seq[VersionedRecord]]()
result.txnTimeoutMs = txnTimeoutMs
proc allocTxnId(tm: TxnManager): TxnId =
result = TxnId(tm.nextTxnId)
@@ -157,6 +159,14 @@ proc write*(tm: TxnManager, txn: Transaction, key: string, value: seq[byte]): bo
release(tm.lock)
return false
# Timeout-based deadlock detection: abort stale transactions
let now = getMonoTime().ticks()
for otherId, otherTxn in tm.activeTxns:
if otherId != txn.id and otherTxn.state == tsActive:
if now - otherTxn.startTime > tm.txnTimeoutMs * 1_000_000:
otherTxn.state = tsAborted
tm.activeTxns.del(otherId)
# Check for write-write conflict
if key notin txn.writeSet:
if key in tm.globalVersions: