Known Limitations
This page lists behaviour that differs from PostgreSQL (or from what the rest of these docs might lead you to expect) in the current release, plus what the latest release fixed. Every open item links to its public issue on HeliosDatabase/HeliosDB-Nano, which is where status updates land first.
Recently fixed
If you worked around any of these on an earlier release, the workaround is no longer needed on the current release:
- Read-your-own-writes in
UPDATE/DELETE. InsideBEGIN … COMMIT, anUPDATEorDELETEnow sees rowsINSERTed earlier in the same transaction (the create-then-patch pattern ORMs emit). Previously it reported 0 rows affected and the change was lost. (#41) Transactions on a database branch still do not see their own branch writes fromUPDATE/DELETE. CALLinside a transaction. A procedure body invoked withCALLinsideBEGINnow runs in that transaction and is undone byROLLBACK; previously its writes committed immediately.- Savepoints. A savepoint belongs to the transaction that created it, is
cleared at
COMMIT/ROLLBACK, andSAVEPOINT/RELEASE/ROLLBACK TO SAVEPOINTwork over the extended query protocol. Naming a savepoint not established in the current transaction raises3B001. CREATE TEMPORARY TABLE. Temporary tables are now private to the session that creates them and are dropped when it ends. Previously they were created as permanent, globally visible tables.- Session-less statements and the embedded transaction. A wire, REST or MCP
statement executed without a session can no longer run inside, commit, or roll
back a transaction opened by the embedding application; it is refused with
No active transaction to commit.POST /v1/branches/:name/executerefuses transaction-control statements. - Multiple databases in one process. Advisory locks
(
pg_advisory_lock),pg_stat_activity, MCP SSE sessions andjoin_memory_limit_mbare scoped per open database. Within one database,pg_stat_activitymasksclient_addr,client_port,state,backend_startandstate_changefor backends owned by another login role, as PostgreSQL does.
Open issues
Concurrency
- Concurrent
UPDATE … SET n = n + 1on one row can lose increments. Under the defaultREAD COMMITTED, concurrent autocommit read-modify-write updates of the same row can each report success while some increments are lost, and no40001is raised. (#50) Until fixed, serialise such updates in the application (for example withpg_advisory_lockaround the update, or a single writer) rather than relying on the database to merge them. - See Concurrency and Transactions for the embedded-mode transaction model (one transaction slot per process) and the isolation levels actually served.
SQL behaviour
NULLS FIRST/NULLS LASTare accepted but ignored. (#43) Workaround:ORDER BY CASE WHEN col IS NULL THEN 1 ELSE 0 END, col.ORDER BYafterGROUP BYcannot reference a select-list alias mixed with a grouped column, or repeat an aggregate; the error names internal placeholders such asagg_0/group_N. (#44) Workaround: order by select-list position (ORDER BY 3 DESC, 2 ASC).setval()returns its argument but does not move the sequence. (#51)- A boolean function whose argument is a bound parameter is returned as text
(
't'/'f'), e.g.pg_try_advisory_lock($1). (#52) CREATE INDEXon an expression is rejected. (#34)::vectorcast on a bound parameter fails; only inline literals work. (#33)LISTEN/NOTIFY/pg_notifyare not implemented; clients must poll. (#40)- Trigger bodies do not execute, apart from
BEFORE INSERT … FOR EACH ROWfunctions that only assignNEW.<col>orRETURN NULL. Do not rely on triggers for audit rows or derived data; use a second statement in the same transaction or a procedure invoked withCALL. - SQLite-style
TEXT DEFAULT CURRENT_TIMESTAMPstores a quoted ISO value that differs from SQLite. (#39)
Driver and ORM compatibility
- psycopg 3 defaults: a
datetime.datebound with%s(binary format) is rejected — use%t;DEALLOCATE ALLis not implemented. (#54) SELECT * FROM current_schema()fails with42P01, which blocks TypeORM initialisation. (#46)pg_catalog.set_config()is missing, so an unmodifiedpg_dumpscript does not restore. (#47)
Databases, roles and security
- No switch to require TLS. With
--tls-cert/--tls-keythe PostgreSQL listener offers TLS, but a client that does not request it is still served in plaintext. (#53) To enforce encryption, bind to loopback or a private network and terminate TLS in front of Nano, or requiresslmode=require/verify-fullon every client. - A role created with
CREATE ROLE … LOGINcannot authenticate with SCRAM; the bootstrappostgresuser can. (#49) - Credentials can only be given on the command line, so
--passwordis visible in the process list. (#38) - A
CREATE DATABASEregistration is lost after restart; the rows remain reachable through the default database. (#48) - Offline tools cannot open a data directory encrypted at rest (no key source on that path). (#45)
- Encryption key rotation is not implemented. AES-256-GCM runs under a single static key with random nonces; very high-volume deployments should track write volume against the 2^32-invocations-per-key bound.
- The MySQL listener is trust-only: the user name is asserted by the client,
not verified. Do not expose
--mysqlbeyond trusted networks.
Realtime
- Known issue: realtime events are not delivered in the current release;
tracked by the Nano team. Subscriptions to
/realtime/v1/websocketare accepted, but change events do not arrive. Poll with SQL in the meantime.
Replication, CDC and backup
ALTER TABLE … RENAMEhas no logical-log entry, so replay and CDC consumers can diverge after a rename. (#36)- A standby can transiently observe a partially-applied transaction; see Concurrency and Transactions.
restorereportsTables: 0, Rows: 0although the data is restored. (#42)
Planned
These capabilities are described elsewhere in the docs or in older material but are not part of the current release. Each is planned.
- Row-level security outside the embedded API. In the stock Nano server,
row-level security is enforced through the embedded API and the REPL; SQL
CREATE POLICYand enforcement for REST and wire clients are planned. The REST API does not apply RLS policies, so enforce per-user access in your service layer. Realtime (WebSocket) subscriptions are not filtered by row-level security; secured realtime is planned. - Row-level security for set operations and WITH queries. RLS policies
filter table scans, joins, aggregates, sorts and limits; filtering of set
operations (
UNION/INTERSECT/EXCEPT),WITHqueries and table functions is planned. - PL/pgSQL control flow and trigger bodies. Nano runs plain-SQL
DOblocks and simple SQL/PL/pgSQL functions (SQL statements,SELECT INTO,RETURN); PL/pgSQL control flow (IF,LOOP,WHILE,FOR,RAISE,:=, dynamicEXECUTE) and trigger bodies are planned. See PL/pgSQL for the exact scope. - REST filter operators. Nano’s REST filters support
eq,neq,gt,gte,lt,lte,like,ilike,isandin, withnot.negation;cs,cd,ov, full-text andor/andfilters are planned, and the/rest/v1table routes are a known issue in the current release. Use the PostgreSQL wire protocol for table access in the meantime. - Full-text search stemming.
tsvector,tsquery,@@,to_tsvector/to_tsqueryandts_rank/ts_rank_cdwork, but the text search configuration argument is ignored: language stemming, booleantsqueryoperators and phrase/proximity search are planned. Matching today is any-term over lowercase tokens. See Full-text search. - Geospatial. Geospatial functions and indexes (
ST_DWithin,ST_Distance, geometry types) are planned; they are not in the current release. - File storage and edge functions. File storage (buckets and signed URLs) and edge functions are on the HeliosDB roadmap; today Nano serves REST, auth and realtime, and stores data in its data directory.
- API key authentication for the HTTP API. Keep the HTTP and MySQL listeners on loopback (the default): API-key authentication for the HTTP API is planned, and a smaller listening surface is good practice anyway.
- Cloud KMS key sources and online key rotation for encryption at rest are planned; keys come from an environment variable or a key file today.
- MCP, Code-Graph and Graph-RAG in the prebuilt binaries. Code-Graph
(
hdb_code), Graph-RAG (WITH CONTEXT) and the native MCP server ship in HeliosDB Nano as build-from-source features (--features code-graph,graph-rag,mcp-endpoint); the prebuilt binaries and the container image use the default features and do not include them.
Distribution
- Prebuilt Linux binaries require glibc 2.39 or newer.
- A container image is published at
ghcr.io/heliosdatabase/heliosdb-nano(one tag per release pluslatest, multi-arch amd64/arm64); it contains the default-feature build. Docker Hub is not an official channel. - Nano is published on crates.io as
heliosdb-nano, with prebuilt binaries for Linux, macOS and Windows on GitHub Releases. - The PyPI binding
heliosdb-nano-embeddedis released separately and can lag the engine release; check PyPI for the published version. - The TypeScript client
@heliosdb/clientis built from the HeliosDB-SDKs source and is not published on npm; Node apps can also use thepgdriver over the PostgreSQL wire protocol.
Found something not listed here? Open an issue on GitHub, ask on Discord, or email support@heliosdb.com. Report security issues privately to security@heliosdb.com.