SecantusDB Rust PostgreSQL server
The same Rust storage as the MongoDB server, answering the PostgreSQL wire protocol instead. psql, psycopg, SQLAlchemy and any other stock PostgreSQL client connect and run real SQL — against the same WiredTiger home a MongoDB driver writes to. It is the newest of SecantusDB's servers: you build it from source until the next tagged release publishes binaries, and it is scored by psycopg's own test suite rather than by our own expectations.
Why a PostgreSQL server in a document database
Because the store underneath was never document-shaped by necessity — it is WiredTiger, the same B-tree engine MongoDB ships, and a row is as natural in it as a document. SecantusDB has served both protocols over one store since the Python implementation, where a pymongo-written collection is queryable as a table with no CREATE TABLE at all; this is that idea with a Rust server under it.
PostgreSQL's own parser
SQL is parsed by libpg_query — the real PostgreSQL grammar, vendored and compiled in — so a statement means here what it means there. No approximation layer inventing its own dialect.
One storage format
Tables live in the same WiredTiger home, in the same on-disk format every SecantusDB server uses. The Rust and Python PostgreSQL servers read and write each other's store — pinned in both directions by a regression test, because getting it wrong would be silent corruption rather than an error.
The extended protocol, properly
Parse / Bind / Describe / Execute, prepared statements, portals, and binary parameter and result formats. Not a simple-query shim — it is what every real driver actually speaks.
Real transactions
BEGIN / COMMIT / ROLLBACK backed by storage transactions, savepoints, and two-phase commit: PREPARE TRANSACTION makes the work durable and invisible, and a prepared transaction found on disk at startup is replayed when it is committed.
Error codes measured, not guessed
Every SQLSTATE, notice and detail line on the supported surface was measured against a live PostgreSQL 16 and matched — down to the DETAIL: Key (id)=(1) already exists. on a unique violation.
Scored by someone else's tests
All 5,729 of psycopg 3's own tests run against it unmodified. It passes every one that exercises the server: 5,545 pass, one fails, and that one is a static-typing check on psycopg's own class hierarchy rather than anything this server answers.
What it speaks today
The surface grew by chasing a real client's test suite rather than a feature list, so what is here is what psycopg actually asks for.
| Area | What works |
|---|---|
| DDL | CREATE / DROP TABLE, CREATE TABLE AS, TRUNCATE, constraints, CREATE TYPE (enum, composite, range, base and shell types), CREATE EXTENSION for hstore and postgis |
| DML & queries | INSERT (with RETURNING), UPDATE, DELETE, SELECT with WHERE / ORDER BY / LIMIT / OFFSET / GROUP BY, the count / sum / min / max aggregates, three-valued logic, and SELECT with no table at all |
| Types | numeric exact past 34 digits, uuid, bytea, inet / cidr, arrays including multidimensional, composites, ranges and multiranges, enums, regclass, dates and timestamptz honouring the session zone — each answered in text and binary, checked against PostgreSQL 16. psycopg's suites for every one of those pass in full |
| Sessions | GUCs with GUC_REPORT (DateStyle, client_encoding with LATIN1 / LATIN9 transcoding, standard_conforming_strings), CancelRequest, pg_cancel_backend and pg_terminate_backend that reach an idle session |
| Beyond the basics | COPY in and out, server-side cursors (DECLARE / FETCH / MOVE), LISTEN / NOTIFY with PostgreSQL's delivery and transaction semantics, DO blocks over a subset of PL/pgSQL, roles and the pg_roles / pg_authid / pg_user catalogs |
| Not yet | Secondary indexes — CREATE INDEX is refused outright (IndexStmt is not supported yet) rather than faked — pg_constraint, and password verification. Note the difference between a refusal and the unenforced UNIQUE in the notice above: a refusal is honest and you find it in one run. The project's rule is a faithful “not supported” over a half-implemented feature that diverges quietly — where something slips through that rule, as UNIQUE has, it is written down in the open backlog rather than left for you to discover |
Where that leaves it, measured on 2026-09-18 against the current build: of psycopg 3's 5,729 tests the server passes 5,545, fails one, and skips 183 that need something out of scope. The single failure is test_typing.py::test_generic_connect — a check on psycopg's own generic classes that never reaches the wire. Whole type suites that a real client leans on — uuid, inet, enums, composites, ranges, COPY, server cursors — pass end to end. We are not publishing a head-to-head against the Python SQL server here: its last report predates the widening of this gauge, so the two ran different suites and the comparison would flatter one of them by accident.
Measured against PostgreSQL itself
Not against our own expectations: a release build of secantusd-pg and PostgreSQL 16.15 on the same box, 5 s per trial, median of three, 1–5% spread.
| Workload | Clients | PostgreSQL 16 | Rust PG server | PG scaling | Our scaling |
|---|---|---|---|---|---|
| insert, one table each | 1 | 17,421 ops/s | 9,937 ops/s | 1.00x | 1.00x |
| 4 | 41,145 ops/s | 21,263 ops/s | 2.36x | 2.14x | |
| 8 | 45,544 ops/s | 22,678 ops/s | 2.61x | 2.28x | |
| insert, shared table | 8 | 46,118 ops/s | 20,531 ops/s | 2.58x | 2.07x |
| select | 1 | 27,492 ops/s | 11,075 ops/s | 1.00x | 1.00x |
| 8 | 83,092 ops/s | 23,898 ops/s | 3.02x | 2.16x |
Read it honestly: we are 1.75x slower than PostgreSQL on a single-client insert and 2.48x on a single-client select, and that per-statement cost — not a lock — is the whole gap. Concurrency is the good news: write scaling tracks PostgreSQL's closely (2.14x against its 2.36x at four clients), and a shared table costs us almost nothing extra where it costs PostgreSQL a little. There is no serialisation collapse. Reads flatten earlier than PostgreSQL's do (2.16x against 3.02x at eight clients) and closing that is the next piece of work, tracked in the open backlog with the CPU-per-operation profile that rules the alternatives out.
Get it
From source until the next tagged release publishes prebuilt archives. It is a Cargo crate in the SecantusDB repository; nothing but a Rust toolchain and the repository's usual build prerequisites is needed.
Build and run
The crate links the vendored WiredTiger, so clone with submodules; it sits outside the clean Cargo workspace and builds from its own directory. The binary takes a storage directory and an address.
git clone --recurse-submodules https://github.com/jdrumgoole/SecantusDB.git
cd SecantusDB/crates/secantus-pgserver
cargo build --release
./target/release/secantusd-pg ./data 127.0.0.1:5432
Connect
Any stock PostgreSQL client. There is no password to supply — and that is the caveat above, not a convenience.
psql -h 127.0.0.1 -p 5432 -U postgres
# or from Python
import psycopg
psycopg.connect("host=127.0.0.1 port=5432 user=postgres")
Related reading: The SQL interface · psycopg conformance report (Python SQL server) · The Rust MongoDB server · The Python reference implementation