HeliosDB Nano v3.22 → v3.30 — Eight Releases of Postgres-Compat, Auth, and psql Tooling

If you tried HeliosDB Nano a couple of months ago and bounced off because psql \d returned stubs, or because your JDBC driver couldn't authenticate, this is the post for you. Eight Nano releases between v3.22.1 and v3.30.0 closed the Postgres-compat gaps most evaluators hit in the first half-hour. Nothing flashy — just the unglamorous wire-protocol and meta-command work that turns "interesting" into "drop-in".

SCRAM-SHA-256 with a proper GS2 header (v3.26.0)

Pre-v3.26.0, Nano implemented SCRAM-SHA-256 close enough that most clients worked, but a handful — recent libpq, modern JDBC, pg8000 with channel_binding=disable — would refuse the handshake because the GS2 header on the client-first message wasn't being parsed strictly. v3.26.0 implements RFC 5802 GS2 parsing end-to-end. The wire shape, for the record:

client-first: n,,n=user,r=<client-nonce>
server-first:           r=<client-nonce + server-nonce>,s=<salt>,i=<iterations>
client-final: c=biws,r=<...>,p=<client-proof>
server-final: v=<server-signature>

The fix is in the n,, prefix — the empty authzid in particular — not the cryptographic core. Net effect: psql, libpq, JDBC, pg8000, SQLAlchemy, and Diesel all authenticate against a Nano server without configuration acrobatics.

CREATE DATABASE / DROP DATABASE + StartupMessage validation (v3.25.0)

v3.25.0 added CREATE DATABASE foo and DROP DATABASE foo as first-class SQL statements, and — this is the load-bearing change — made the wire-protocol StartupMessage actually validate the requested database name. A client connecting with dbname=acme_tenant_42 now lands in acme_tenant_42 or gets ERROR: database "acme_tenant_42" does not exist. Previously the parameter was accepted but ignored, which made multi-tenant routing impossible from the wire.

This isn't multi-tenancy with row-level security — it's the simpler model where each tenant gets a logical database in one Nano process, selected at connection time. Combined with SCRAM-SHA-256, you can now run a tenant-per-database pool from any Postgres driver:

$ psql "host=nano.local user=app password=<...> dbname=tenant_42"
tenant_42=> SELECT current_database();
 current_database
------------------
 tenant_42

information_schema completion (v3.24.0)

Before v3.24.0, Nano's information_schema covered tables, columns, and key_column_usage, but introspection tools that walked further hit empty views. v3.24.0 populates the remaining catalog surface:

  • information_schema.routines — PL/pgSQL functions and stored procedures
  • information_schema.parameters — routine signatures
  • information_schema.character_sets, collations — encoding metadata
  • information_schema.view_table_usage, view_column_usage — view dependency graph
  • information_schema.role_table_grants, role_column_grants — GRANT introspection

The pragmatic effect: tools like DBeaver, DataGrip, and Drizzle's introspection command now produce complete schema dumps. Strict-unknown-view behaviour is preserved — a query against a view that doesn't exist returns a Postgres-shaped undefined_table error rather than an empty result, so tooling that gates on errors sees what it expects.

Real \d, \l, \dt in psql (v3.26.1)

The psql backslash commands are mostly client-side macros, but they expand into specific SQL queries against pg_catalog and information_schema. v3.26.1 wires the catalog views these commands hit to live data, so \d, \l, and \dt now return your actual schema instead of stubs. Migration scripts that pipe psql -c '\dt' | awk ... stop being a special case.

Keyset row-constructor pagination (v3.23.0 → v3.30.0)

If you've ever pulled a million-row result set in 1 000-row pages and watched query time drift up linearly, you've felt the LIMIT/OFFSET problem — each page rescans the offset rows it's about to throw away. v3.23.0 wired up keyset cursors as a stateless alternative, and v3.30.0 extended them to compose across joins. The row-constructor form is a one-line edit:

-- Old (drifts under load)
SELECT * FROM events
ORDER BY created_at, id
LIMIT 1000 OFFSET 50000;

-- New (constant cost per page)
SELECT * FROM events
WHERE (created_at, id) > ('2026-04-30 14:22:00', 9981234)
ORDER BY created_at, id
LIMIT 1000;

Composes across joins from v3.23.0+, and from v3.30.0 the planner pushes the row-constructor predicate down through joined tables that share an index prefix. Worked examples and the side-by-side LIMIT/OFFSET comparison live on pagination-performance.

FK enforcement on the index path (v3.30.0)

The v3.30.0 release moved ON CONFLICT DO UPDATE and DELETE on foreign-key parents off the SQL planner and onto direct index lookup. The change is uninteresting at one row and meaningful at scale — measured on representative bulk-load fixtures (10K rows, single transaction), the FK enforcement path is between two and three orders of magnitude faster than v3.22.x. Reproduce it on your own and report variance; the magnitude depends heavily on schema depth and index width.

Upgrade path

For most users, v3.22.x → v3.30.0 is a drop-in upgrade — the data directory format hasn't changed in this window. The full matrix lives at docs.heliosdb.com/docs/nano/guides/upgrade and covers the pre-3.6.0 dump/restore path for the genuinely-old upgraders. If you're on v3.22.x, just swap binaries and restart.

$ cargo install heliosdb-nano --version 3.30.0
$ heliosdb-nano start --data-dir ./data
HeliosDB Nano v3.30.0 listening on 127.0.0.1:5432 (PG) and 127.0.0.1:3307 (MySQL)

HeliosDB Nano is Apache 2.0. Repository: github.com/dimensigon/HDB-HeliosDB-Nano.

← Back to Blog