# Bug Report — BaraDB Codebase Audit

Generated: 2026-05-21
Total: 55 bugs (7 critical, 21 high, 21 medium, 6 low)
**Fixed: 55/55** | **Remaining: 0/55**

---

## Critical — 7/7 fixed

### ~~BUG-001~~ :white_check_mark: `readIdent` infinite loop on multi-byte UTF-8
**File:** `src/barabadb/query/lexer.nim:523-538` — **FIXED:** Added `discard l.advanceRune()` to advance `l.pos` in the loop.

### ~~BUG-002~~ :white_check_mark: `Parser.peek` returns uninitialized Token at EOF
**File:** `src/barabadb/query/parser.nim:15-18` — **FIXED:** Added missing `return` keyword.

### ~~BUG-003~~ :white_check_mark: `flushUnsafe` writes WAL without `walLock`
**File:** `src/barabadb/storage/lsm.nim:808-810` — **FIXED:** Wrapped WAL writes in `acquire(db.walLock)` / `release(db.walLock)`.

### ~~BUG-004~~ :white_check_mark: `checkpoint` calls `flushUnsafe` with no lock
**File:** `src/barabadb/storage/lsm.nim:832-836` — **FIXED:** Entire checkpoint now runs under lock; WAL rotate uses `walLock`.

### ~~BUG-005~~ :white_check_mark: Compaction deletes source SSTables without verifying output
**File:** `src/barabadb/storage/compaction.nim:124-128` — **FIXED:** Added `verifySSTable` check before deleting sources.

### ~~BUG-006~~ :white_check_mark: `healthCheck` leaks lock on exception
**File:** `src/barabadb/core/replication.nim:227-248` — **FIXED:** Snapshot replicas under lock, release, then do network I/O.

### ~~BUG-007~~ :white_check_mark: `hmacSha256` truncates keys > 64 bytes to hex string
**File:** `src/barabadb/protocol/auth.nim:73-79` — **FIXED:** Changed `k = $hash` to `k = cast[string](hash)` for raw bytes.

---

## High — 21/21 fixed

### ~~BUG-008~~ :white_check_mark: `loadExistingDatabases` / `ensureDefaultDatabase` without lock
**File:** `src/barabadb/core/registry.nim:55-70, 78-90` — **FIXED:** Added lock around `reg.databases` modifications.

### ~~BUG-009~~ :white_check_mark: `compactVersions` modifies shared state without lock
**File:** `src/barabadb/core/mvcc.nim:329-354` — **FIXED:** Made private (removed `*`), only called from `commit` which holds lock.

### ~~BUG-010~~ :white_check_mark: Deadlock detector edges never cleaned up
**File:** `src/barabadb/core/mvcc.nim:191-199` — **FIXED:** Added `removeWait` before returning on conflict. Also fixed in `delete`.

### ~~BUG-011~~ :white_check_mark: `close` leaks WAL/SSTable on exception
**File:** `src/barabadb/storage/lsm.nim:842-851` — **FIXED:** Wrapped in `try/finally` to guarantee cleanup.

### ~~BUG-012~~ :white_check_mark: B-Tree `remove` does not rebalance
**File:** `src/barabadb/storage/btree.nim` — **FIXED:** Implemented full B-tree delete rebalancing: `borrowFromLeft`/`borrowFromRight` (borrow key + child from sibling, update parent separator), `mergeWithLeft`/`mergeWithRight` (merge with sibling by pulling down parent separator), `rebalanceAfterDelete` (orchestrates borrow-or-merge, recursive parent rebalancing, root shrinking).

### ~~BUG-013~~ :white_check_mark: Compaction `except: discard` swallows all exceptions
**File:** `src/barabadb/storage/compaction.nim:76-84` — **FIXED:** Catches `CatchableError`, logs, and aborts compaction without deleting sources.

### ~~BUG-014~~ :white_check_mark: `headerSize = 36` should be 40 — wrong CRC range
**File:** `src/barabadb/storage/lsm.nim:212,293,334` — **FIXED:** Changed all three occurrences from 36 to 40.

### ~~BUG-015~~ :white_check_mark: `**` power operator tokenized but never parsed
**File:** `src/barabadb/query/parser.nim:270-282` — **FIXED:** Added `parsePower` function (right-associative) between `parsePostfix` and `parseMulDiv`.

### ~~BUG-016~~ :white_check_mark: `NOT a = b` wrong precedence
**File:** `src/barabadb/query/parser.nim:297-345` — **FIXED:** Moved NOT wrap to AFTER the comparison while loop.

### ~~BUG-017~~ :white_check_mark: `COUNT(DISTINCT col)` DISTINCT flag lost
**File:** `src/barabadb/query/parser.nim:141-164` — **FIXED:** Added `funcDistinct` field to `nkFuncCall` node in AST, set in parser.

### ~~BUG-018~~ :white_check_mark: Expression UDFs always return null
**File:** `src/barabadb/query/udf.nim:73-80` — **FIXED:** Raises descriptive error directing to use query evaluator.

### ~~BUG-019~~ :white_check_mark: `substr` crashes on out-of-bounds index
**File:** `src/barabadb/query/udf.nim:176-188` — **FIXED:** Added bounds check, returns empty string for out-of-bounds.

### ~~BUG-020~~ :white_check_mark: Raft accepts stale term vote/append replies
**File:** `src/barabadb/core/raft.nim:352-391` — **FIXED:** Added `if reply.term < node.currentTerm: return` in both handlers.

### ~~BUG-021~~ :white_check_mark: Raft `becomeFollower` on `term >= currentTerm`
**File:** `src/barabadb/core/raft.nim:269-271` — **FIXED:** Changed `>=` to `>`; `leaderId` assignment still happens unconditionally.

### ~~BUG-022~~ :white_check_mark: `shipToReplica` socket leak
**File:** `src/barabadb/core/replication.nim:94-113` — **FIXED:** Used `defer: sock.close()`.

### ~~BUG-023~~ :white_check_mark: `pendingAcks` never cleaned up in sync/semi-sync
**File:** `src/barabadb/core/replication.nim:131-199` — **FIXED:** `writeLsn` now removes acked replica IDs from `pendingAcks` and deletes the LSN entry once fully acked; `ackLsn` also cleans up on replica acknowledgement.

### ~~BUG-024~~ :white_check_mark: `rebalance` loses old assignments
**File:** `src/barabadb/core/sharding.nim:221-241` — **FIXED:** `rebalance` returns the old shard assignments; callers (`addNode`/`removeNode`) pass them to `migrateData` for correct data migration.

### ~~BUG-025~~ :white_check_mark: `deserializeValue` missing bounds checks
**File:** `src/barabadb/protocol/wire.nim:216-227` — **FIXED:** Added bounds checks for `fkBool`, `fkInt8`, `fkInt16`.

### ~~BUG-026~~ :white_check_mark: `verifyToken` parseInt throws on malformed claims
**File:** `src/barabadb/protocol/auth.nim:175-177` — **FIXED:** Wrapped in `try/except ValueError`.

### ~~BUG-027~~ :white_check_mark: 2PC leaves participants prepared on network failure
**File:** `src/barabadb/core/disttxn.nim:150-200` — **FIXED:** Added `rollbackPending`/`commitPending` flags and recovery warnings.

### ~~BUG-028~~ :white_check_mark: JWT DB switch never decrements old connection count
**File:** `src/barabadb/core/server.nim:504-520` — **FIXED:** Decrement old DB count before incrementing new one.

---

## Medium — 21/21 fixed

### ~~BUG-029~~ :white_check_mark: `loadConfigFromJson` silently swallows errors
**File:** `src/barabadb/core/config.nim:121-124` — **FIXED:** Logs warnings instead of `discard`.

### ~~BUG-030~~ :white_check_mark: Default JWT secret hardcoded in binary
**File:** `src/barabadb/core/config.nim:182-185` — **FIXED:** Returns empty string; callers must handle missing secret.

### ~~BUG-031~~ :white_check_mark: `dropDatabase` closes LSMTree before removing from registry
**File:** `src/barabadb/core/registry.nim:133-161` — **FIXED:** Delete from registry first, then close/cleanup outside lock.

### ~~BUG-032~~ :white_check_mark: MERGE missing DELETE / DO NOTHING
**File:** `src/barabadb/query/parser.nim:902-958` — **FIXED:** Added `tkDo`/`tkNothing` tokens, `mergeMatchedDelete`/`mergeNotMatchedNothing`/`mergeMatchedCondition` AST fields, and loop-based parser for multiple WHEN branches.

### ~~BUG-033~~ :white_check_mark: `inferExpr` miscategorizes binary operators as unary
**File:** `src/barabadb/query/ir.nim:291-301` — **FIXED:** Removed binary operators from unary case, added `irNeg` handling.

### ~~BUG-034~~ :white_check_mark: `codegenExpr` is no-op for most expression types
**File:** `src/barabadb/query/codegen.nim:50-67` — **FIXED:** Literal/field/aggregate now return proper storage ops; unary/binary propagate children.

### ~~BUG-035~~ :white_check_mark: `readEntries` leaks FileStream on early return
**File:** `src/barabadb/storage/wal.nim:211-213` — **FIXED:** Wrapped in `try/finally` with `s.close()`.

### ~~BUG-036~~ :white_check_mark: mmap reads allow negative offset
**File:** `src/barabadb/storage/mmap.nim:83+` — **FIXED:** Added `offset < 0` checks to all read functions.

### ~~BUG-037~~ :white_check_mark: Raft log index conflates entry index with array position
**File:** `src/barabadb/core/raft.nim:287-290` — **FIXED:** Added `findLogEntryByIndex` helper that searches by logical index instead of assuming `index - 1 == array_position`.

### ~~BUG-038~~ :white_check_mark: `becomeFollower` doesn't clear `nextIndex`/`matchIndex`
**File:** `src/barabadb/core/raft.nim:209-214` — **FIXED:** Added `clear()` for both tables.

### ~~BUG-039~~ :white_check_mark: Gossip assigns wrong port to new nodes
**File:** `src/barabadb/core/gossip.nim:295-302` — **FIXED:** Extracts port from `senderAddr` ("host:port") instead of using local `gp.gossipPort`.

### ~~BUG-040~~ :white_check_mark: `getShardRange` overlapping range boundaries
**File:** `src/barabadb/core/sharding.nim:70-74` — **FIXED:** Exclusive upper bound for non-last shards.

### ~~BUG-041~~ :white_check_mark: `getShardHash` divides by zero
**File:** `src/barabadb/core/sharding.nim:66-68` — **FIXED:** Returns -1 if `shards.len == 0`.

### ~~BUG-042~~ :white_check_mark: `connectWithTimeout` missing SO_ERROR check
**File:** `src/barabadb/core/replication.nim:80-92` — **FIXED:** Added `getsockopt(SO_ERROR)` verification.

### ~~BUG-043~~ :white_check_mark: Dead code: duplicate ON UPDATE in ON DELETE
**File:** `src/barabadb/query/parser.nim:1081-1087` — **FIXED:** Removed duplicate branches.

### ~~BUG-044~~ :white_check_mark: LIMIT cost ignores offset
**File:** `src/barabadb/query/codegen.nim:242-246` — **FIXED:** Cost now factors in `offset + limit` — high offset no longer treated as cheap.

### ~~BUG-045~~ :white_check_mark: `contains` UDF cross-type mismatch
**File:** `src/barabadb/query/udf.nim:212-234` — **FIXED:** Added cross-type numeric comparison (int64/int32/float64).

### ~~BUG-046~~ :white_check_mark: MmapFile.close() potential infinite recursion
**File:** `src/barabadb/storage/mmap.nim:133-137` — **FIXED:** Qualified `close` as `posix.close`.

### ~~BUG-047~~ :white_check_mark: `sendDistTxnRpc` socket leak
**File:** `src/barabadb/core/disttxn.nim:93-109` — **FIXED:** Used `defer: sock.close()`.

### ~~BUG-048~~ :white_check_mark: Saga compensation skips on failure
**File:** `src/barabadb/core/disttxn.nim:282-293` — **FIXED:** Wrapped each `compensate()` in `try/except`, logs and continues.

### ~~BUG-049~~ :white_check_mark: SCRAM reveals user existence
**File:** `src/barabadb/protocol/auth.nim:207-208` — **FIXED:** Changed error message from "Unknown user: X" to "Authentication failed".

### ~~BUG-050~~ :white_check_mark: `isVisible` doesn't check committedTxnsSet for deleter
**File:** `src/barabadb/core/mvcc.nim:129-141` — **FIXED:** Added `committedTxnsSet` check before unconditionally hiding record.

---

## Low — 6/6 fixed

### ~~BUG-051~~ :white_check_mark: `recvExactWithTimeout` abandoned future on timeout
**File:** `src/barabadb/core/server.nim:313-320` — **FIXED:** Explicit `return ""` with comment explaining cleanup.

### ~~BUG-052~~ :white_check_mark: `readNumber` produces invalid float for trailing dot
**File:** `src/barabadb/query/lexer.nim:508-521` — **FIXED:** Appends "0" for trailing dot (`123.` → `123.0`).

### ~~BUG-053~~ :white_check_mark: `substr(s, start)` returns rest-of-string
**File:** `src/barabadb/query/udf.nim:187` — **FIXED:** Now returns single character `s[start]` for 2-arg form.

### ~~BUG-054~~ :white_check_mark: `readUint32` → `int` truncation on 32-bit
**File:** `src/barabadb/protocol/wire.nim:136,147` — **FIXED:** Store raw uint32 before cast, check for negative result after cast.

### ~~BUG-055~~ :white_check_mark: PageCache `put` resets `lastAccess` to 0
**File:** `src/barabadb/storage/compaction.nim:196-213` — **FIXED:** Removed unused `lastAccess` field entirely; LRU uses `accessOrder` seq.

---

## Summary

| Status | Count |
|--------|-------|
| Fixed | 55 |
| Remaining | 0 |

All identified bugs have been resolved.
