---
title: "HeliosDB Nano v3.12.0 Release Notes"
---

# HeliosDB Nano v3.12.0 Release Notes

**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 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

```sql
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

```sql
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

```sql
-- 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](https://www.heliosdb.com/docs/nano/guides/quickstart/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 |
