HeliosDB Nano v3.12.0 Release Notes
HeliosDB Nano v3.12.0 Release Notes
Release Date: 2026-04-17 Theme: Constant-time deep pagination — keyset, Top-K, storage-level OFFSET pushdown
Highlights
Pagination at depth used to mean materialising and discarding the rows you didn’t want. v3.12.0 makes it constant-time. Three independent paths land:
- Top-K operator — bounded max-heap when the plan is
Limit(Sort(…)). O(N log k) instead of O(N log N). - Storage-level OFFSET pushdown — RocksDB iterator skips offset rows without bincode decode, decrypt, or dictionary lookup.
- Row-constructor keyset comparison —
WHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT Nnow plans and evaluates lexicographically.
Combined: deep LIMIT … OFFSET runs in ~30 µs regardless of offset, ~334× faster than PostgreSQL 13 on 100k-row tables.
What’s New
Top-K Operator
SELECT title, score FROM articlesORDER BY score DESCLIMIT 10;Plan rewrites to TopK(10, …). Streams the input through a bounded max-heap of size k = limit + offset. Auto-applied whenever LIMIT has a concrete bound — no syntax change.
Storage-Level OFFSET Pushdown
SELECT * FROM eventsORDER BY idLIMIT 5 OFFSET 990;storage::EmbeddedStorage::scan_table_with_offset_limit skips the first 990 rows at the RocksDB iterator level — no deserialisation, no decrypt, no dictionary/CAS resolve — then fetches the next 5 fully. ~1 ms on 1000-row tables (previously: materialise all 995+ before LimitOperator skipped).
Row-Constructor Keyset Comparison
-- Page after (last_seen_created, last_seen_id):SELECT * FROM eventsWHERE (created_at, id) < ($1, $2)ORDER BY created_at DESC, id DESCLIMIT 50;New LogicalExpr::Tuple variant + evaluate_tuple_compare. Supports =, <>, <, <=, >, >= lexicographically.
Primary-Key Range Scan API
storage::EmbeddedStorage::scan_table_pk_range — low-level building block for future planner-driven keyset pushdown. Currently exposed for callers that know the PK range up front.
Fixed
LIMIT $1 OFFSET $2via psycopg extended query protocol — root cause of SQLAlchemy’sNotImplementedError: _row_as_tuple_getter. The planner’sexpr_to_usizerejectedExpr::Value(Placeholder(_)), breaking Parse-time schema derivation;Describethen sentNoDatainstead ofRowDescription. Now accepts placeholders (real values substituted at Execute time).- Fallback
RowDescriptionforSELECT— if schema derivation fails for an exotic query, we synthesise a best-effort schema from the sqlparser projection list rather than returningNoData. Matches PostgreSQL behaviour and keeps SQLAlchemy row decoders happy.
Migration
No breaking changes. v3.12.0 is fully wire-compatible with v3.11.0.
Top-K and storage OFFSET pushdown are automatic — no SQL changes. Keyset pagination requires the row-constructor SQL (WHERE (col, id) < ($1, $2)); see KEYSET_PAGINATION_QUICKREF.
Known Limitations
- Cross-engine pagination benchmark (vs Postgres / Oracle / MSSQL) and a website marketing page tracked as follow-up (task #122).
Compatibility Matrix
| Component | Version |
|---|---|
| PostgreSQL wire | 14, 15, 16 |
| MySQL wire | 5.7, 8.0 |
| MCP protocol | 1.0 |