Bug fixes: composite PK, nl_to_sql sandbox, FK check, SQL injection, storage correctness

Critical fixes:
- Composite PK: execInsert + validateConstraints use all PK columns
- nl_to_sql: non-SELECT SQL no longer executed directly during validation
- FK check: removed O(N) scanMemTable fallback, uses db.get() with SSTables
- exprToSql: nkIdent wrapped in quotes to prevent SQL injection
- restoreSchema: try/except around tokenize/parse for crash resilience
- recovery.nim: lastTxnId tracking + putUnsafe/deleteUnsafe without WAL
- SCRAM: verifyClientProof length check + DefaultIterationCount restored

Storage fixes:
- lsm.nim: SSTable sort order fixed (ascending), close() flushes all memtables
- compaction.nim: tombstones preserved during compaction
- wal.nim: header written for empty existing files, readEntries checks magic
- btree.nim: B+ tree leaf split keeps boundary key
- bloom.nim: deserialize raises on short data
- mmap.nim: bounds checks for adviseWillNeed/DontNeed + posix.close()

Protocol fixes:
- zerocopy.nim: readString bounds check
- wire.nim: deserializeValue 32-bit underflow check
- auth.nim: JWT JSON escaping for claims
- server.nim: readUint32BE bounds check + specific exception handling
- raft.nim: readData checks + specific exception handling

Tests:
- Added Composite Primary Key test suite (4 tests)

Build: 0 warnings, 0 errors
This commit is contained in:
2026-05-18 11:33:11 +03:00
parent a28c845476
commit 967c0855a5
25 changed files with 376 additions and 171 deletions
+12 -3
View File
@@ -54,14 +54,18 @@ proc openMmap*(path: string, mode: MmapMode = mmReadOnly): MmapFile =
let mapped = mmap(nil, fileSize, prot, flags, fd, 0)
if mapped == MAP_FAILED:
discard close(fd)
discard posix.close(fd)
return MmapFile(path: path, regions: @[], totalSize: 0, pageSize: PageSize)
# Kernel keeps the mapping alive independently of the fd; close it now
# to avoid leaking one fd per SSTable.
discard posix.close(fd)
let region = MmapRegion(
data: cast[ptr UncheckedArray[byte]](mapped),
size: fileSize,
offset: 0,
fd: fd,
fd: -1,
mode: mode,
)
@@ -116,16 +120,21 @@ proc adviseRandom*(mf: MmapFile) =
proc adviseWillNeed*(mf: MmapFile, offset: int, size: int) =
if mf.regions.len > 0:
if offset < 0 or offset + size > mf.regions[0].size:
return
discard madvise(addr mf.regions[0].data[offset], size, MADV_WILLNEED)
proc adviseDontNeed*(mf: MmapFile, offset: int, size: int) =
if mf.regions.len > 0:
if offset < 0 or offset + size > mf.regions[0].size:
return
discard madvise(addr mf.regions[0].data[offset], size, MADV_DONTNEED)
proc close*(mf: MmapFile) =
for region in mf.regions:
discard munmap(region.data, region.size)
discard close(cint(region.fd))
if region.fd != -1:
discard close(cint(region.fd))
mf.regions.setLen(0)
proc size*(mf: MmapFile): int = mf.totalSize