Skip to content

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 comparisonWHERE (created_at, id) < ($1, $2) ORDER BY created_at DESC, id DESC LIMIT N now 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 articles
ORDER BY score DESC
LIMIT 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 events
ORDER BY id
LIMIT 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 events
WHERE (created_at, id) < ($1, $2)
ORDER BY created_at DESC, id DESC
LIMIT 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 $2 via psycopg extended query protocol — root cause of SQLAlchemy’s NotImplementedError: _row_as_tuple_getter. The planner’s expr_to_usize rejected Expr::Value(Placeholder(_)), breaking Parse-time schema derivation; Describe then sent NoData instead of RowDescription. Now accepts placeholders (real values substituted at Execute time).
  • Fallback RowDescription for SELECT — if schema derivation fails for an exotic query, we synthesise a best-effort schema from the sqlparser projection list rather than returning NoData. 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

ComponentVersion
PostgreSQL wire14, 15, 16
MySQL wire5.7, 8.0
MCP protocol1.0