Expand description
cratefield-adapter-postgres: the Database port over sqlx
Postgres for the native runtime (ADR 0004, issue #18). Native only —
it must never enter a wasm build.
§Native-only gate
The sqlx driver does not compile to wasm, so this crate is impossible
to build for a wasm target: src/lib.rs fails compilation with a
clear message on wasm32/wasm64, and the sqlx dependency itself is
target-gated to non-wasm builds in Cargo.toml. The wasm graph of
examples/venture must stay free of this crate (CI asserts it with
cargo tree --target wasm32-unknown-unknown -p venture | grep -c sqlx → 0).
§Statement pipeline
Modules build queries with sea-query; the port’s Statement carries
them rendered with SqliteQueryBuilder (the portable ?-placeholder
wire form D1 and rusqlite execute directly). This adapter rewrites ?
to Postgres $n placeholders — skipping string literals, quoted
identifiers and comments — and binds the sea-query values positionally
(src/convert.rs). The migration runner’s own
bookkeeping statements are rendered by sea-query’s
PostgresQueryBuilder directly.
§The SQLite → Postgres mapping actually used
The portable subset (ADR 0004, architecture section 7) is deliberately the intersection of SQLite and Postgres, so migration files run verbatim on both engines:
| Portable concept | SQLite | Postgres | Notes |
|---|---|---|---|
| ULID ids | TEXT PRIMARY KEY | TEXT PRIMARY KEY | no AUTOINCREMENT/SERIAL anywhere |
| timestamps | ISO-8601 TEXT | ISO-8601 TEXT | computed in Rust, never NOW()/datetime() |
| counters | INTEGER | INTEGER (int4) | referrals, position |
| booleans | INTEGER 0/1 by convention | INTEGER 0/1 by convention | no SQL BOOLEAN columns; SeaValue::Bool binds as SMALLINT 0/1, and rows read back through Row::get::<bool> accept both |
| upserts | INSERT … ON CONFLICT … | identical syntax | rendered by sea-query for both dialects |
| read-back writes | INSERT … RETURNING … | identical syntax | rendered by sea-query for both dialects |
batch runs all statements in one transaction (atomic, ADR 0004).
use cratefield_adapter_postgres::Postgres;
let db = Postgres::connect("postgres://user:pass@host:5432/venture").await?;
// db.apply_harness_migrations(&harness).await?; // library entry point
// … or through Arc<dyn Database> as with any adapterModules§
- testing
- Shared Postgres test infrastructure: per-test throwaway databases on
the server named by
FZ_TEST_POSTGRES_URL, so tests never see each other’s tables (issue #18).
Structs§
- Module
Plan - One module’s pending migration ids, in apply order.
- Pool
Limits - An open Postgres connection pool (
sqlx0.8,runtime-tokio,tls-rustls). - Pool
Registry - Pools per tenant, with both caps applied.
- Postgres
- Tenant
Plan - What
Postgres::reconcile_planwould apply for one tenant. - Tenant
Record - One tenant in the control database’s registry.
- Tenant
Report - The outcome of reconciling one tenant.
Enums§
- Tenant
Status - A tenant database’s reconciliation status.
Constants§
- TENANT_
POOL_ IDLE - How long a pool with no checkout survives before it is closed.
- TENANT_
POOL_ TOTAL - The default total across every tenant pool in this process.
Traits§
- Tenant
Dsns - Where a tenant’s connection string comes from.
Functions§
- select_
set - Selects the migration set a module contributes to a Postgres
deployment: its
postgresset when it ships one, else itssqliteset — but only when every file passes the portable-SQL lint (cratefield_core::lint_portable_sql, the same predicatefz doctorenforces, so the two can never drift). - tenant_
lock_ key - The advisory-lock key for a tenant: the first 60 bits of the sha256
of its id, as
i64. Stable across versions (it is a hash of the id, not of Rust’sDefaultHasher) and never zero for a non-empty id.