shard-db 2026.07.3¶
Coverity hardening (46 true-positive fixes across two triage rounds), aggregate HAVING clause OR/nested-tree support on both the JSON and NQL paths, an NQL --join flag, and a btree-cache concurrency improvement on the cold-miss path. No on-disk format changes — wire-compatible with 2026.07.2, no ./migrate required.
Highlights¶
- Coverity hardening — 82 findings from a fresh scan triaged; 46 true positives fixed across 9 PRs (stack overflows, disk-corruption fail-safes, null derefs, resource leaks, data races, dead code, build warnings). Fail-safe behavior only — no functional or format changes.
- Aggregate
HAVINGOR/nested-tree support —having_json(JSON protocol) previously flattened to AND-only, silently dropping OR/nested conditions that NQL already supported. Both paths now share one tree-aware matcher. Also fixes a bug where a plaincount()aggregate with ahavingclause and nogroup_bybypassed having filtering entirely. - NQL
--joinflag — join support onfindvia NQL, built directly asJoinSpecC structs (no JSON intermediate). Fixes two double-free bugs and a missingasdefault uncovered during implementation. - Performance — btree-cache miss path no longer serializes cold-start syscalls (
open/mmap/munmap/close) behind one global mutex; widens parallel fan-out for cold indexed queries across many shards. - npm binding fix + rebuild — the Node.js native addon was missing the seven
query_*.ctranslation units created by 2026.07.2'squery.csplit, silently building an incomplete binding. Fixed inbinding.gyp(npm 1.0.9). The binding also compiles every source file this release touches (query.c,query_aggregate.c,btree.c,nql.c,bitmap.c,index.c,io_direct.c,slotcask.c,config.c,server.c), so npm is bumped again to 1.0.10 to ship this release's Coverity fixes, HAVING fix, join support, and btree-cache perf improvement to Node.js consumers.
Coverity hardening¶
A fresh Coverity scan (82 findings, all New/Unassigned/Unclassified) was triaged by module cluster; verdicts and full CID mapping live in docs/coverity-triage-2026-07.md. 46 true positives were fixed across 9 PRs; the remainder were classified false-positive (established idioms Coverity's flow analysis loses track of, e.g. atomics, double-checked locking) or accepted risk (documented, low-severity, not worth the complexity to fix — e.g. a startup-only TOCTOU guarded by the single-instance flock).
| PR | Category | CIDs fixed |
|---|---|---|
| #219 | Stack-buffer overflows — encode_criterion_value, group_by CSV conversion, join buffer offset accumulation |
4 |
| #220 | Disk corruption — malformed/bitrot on-disk data (btree page chains, klen/vlen fields, varchar length prefixes, bitmap dictionary walk, uninitialized-struct-on-goto-fail) now fails safe instead of OOB read/loop/corrupt | 12 |
| #221 | Null-deref — unchecked slotcask_registry_get() in keyset_count_from_or / keyset_emit_find |
3 |
| #222 | Build warnings (signed/unsigned comparison, dead helper) — no CIDs, flagged during #221 review | 0 |
| #223 | Resource leaks — NqlCommand on NQL parse failure, closedir on warmup OOM, cmd_get_multi OOM path, bulk-insert wire_keys on timeout |
4 |
| #224 | Data race — recover_one_stream wrote active_file_id/reserve_off without rotation_lock |
2 |
| #225 | Dead code — 5 unreachable v1 function bodies + a duplicated post-return block + 2 dead stores, left over from the v1→v2 slotcask migration |
8 |
| #226 | Misc hardening — unlogged accept() errno, OOB clamp in decode_field_to_buf, 2 unchecked ftruncate(), unbounded memcpy in cmd_add_indexes |
5 |
| #229 | Round-2 rescan — stack overflow in selective_reindex_dirty, plus 7 more null-deref/leak/hardening findings surfaced after round-1 fixes |
8 |
New regression tests were added alongside the disk-corruption and stack-overflow fixes (corrupting real on-disk bytes against a live daemon and asserting survival).
Aggregate HAVING clause: OR and nested trees¶
having_json on the JSON protocol previously flattened having clauses to an AND-only array, silently dropping {"or":[...]} or nested AND/OR conditions that the NQL path already supported via CriteriaNode trees. Both entry points now build a having_tree and route through a single tree-aware matcher (agg_having_match_tree).
This also fixes two pre-existing bugs:
- The
no_havinggate used by 8+ aggregate fast-path shortcuts didn't account for tree-based having clauses. - The no-group-by count-only metadata shortcut never checked
no_havingat all — a plaincount()aggregate with ahavingclause and nogroup_bysilently returned an unfiltered count.
Malformed having JSON now returns {"error":"bad having: ..."} instead of silently degrading to no filtering.
./shard-db query '{"mode":"aggregate","dir":"default","object":"orders","aggregates":[{"op":"count","as":"n"}],"group_by":["status"],"having":{"or":[{"field":"n","op":"gt","value":10},{"field":"n","op":"lt","value":2}]}}'
NQL: --join flag¶
find queries in NQL now accept --join, building JoinSpec structs directly from parsed tokens instead of routing through a JSON intermediate — matching how cmd_find_do already handles JSON joins.
find orders --join customers local=customer_id as cust fields name,email
find orders --join customers local=customer_id left
as is optional (defaults to the remote object name), matching JSON join behavior. Uncovered and fixed during implementation:
- Double-free in
cmd_find_tree(cmd_find_doalready frees joins internally) - Double-free in
dispatch_nql_query(cmd_find_dofrees, thennql_free_commandtried to free the same joins again)
NQL's docs previously understated its own coverage — --cursor and --having were already fully supported (same as JSON) but undocumented or listed under "Limitations." The NQL doc's "Limitations" section is renamed "Scope" and corrected: NQL now covers find, count, aggregate, and joins (via --join on find); only bulk operations, CAS, schema mutations, and file storage remain JSON-only.
Performance: btree-cache miss path¶
A cold-cache indexed query fans out across up to 128 index shards in parallel. Every worker thread previously funneled through one global mutex to do real syscalls (open/mmap/munmap/close) one at a time — serializing exactly the case that should be parallel (cold cache + high shard fan-out).
bt_cache_drop_slot is split into bt_cache_evict_slot (under-lock detach, no syscalls) and bt_dispose_mapping (post-unlock teardown); both eviction paths now dispose the victim's fd/mmap after releasing bt_cache_lock instead of while holding it. Readers do open+mmap outside the lock, re-probe on reacquire, and serve their mapping uncached if another thread won the install race in the meantime. Writer-side fresh-file creation is unchanged.
Verified race-free under ThreadSanitizer on the affected index/btree/cursor test subset.
npm binding fix + rebuild¶
2026.07.2's query.c split (into query_aggregate.c, query_join.c, query_plan.c, query_find.c, query_bulk.c, query_maint.c, query_schema.c) was not reflected in the npm native addon's binding.gyp, which still only listed the original query.c. This silently built an incomplete binding missing most query functionality. Fixed by adding the seven new translation units to npm/binding.gyp (npm 1.0.9).
That fix alone doesn't ship this release's actual bug fixes, though — binding.gyp compiles the same C sources this release changes (query.c, query_aggregate.c for the HAVING fix, btree.c for the cache perf fix, nql.c for --join, plus bitmap.c, index.c, io_direct.c, slotcask.c, config.c, server.c for the Coverity hardening). npm package shard-db bumped 1.0.9 → 1.0.10 so Node.js consumers get a rebuilt binding with all of this release's fixes.
Upgrade notes¶
No storage format changes in this release. No ./migrate step required.
Wire-compatible with 2026.07.2 — no client changes needed.