zeph-db
Database abstraction layer for Zeph — unified SQLite and PostgreSQL backends with compile-time backend selection, automatic migrations, dialect-aware SQL helpers, and FTS support.
Important:
Exactly one of the
sqliteorpostgresfeatures must be enabled. The default issqlite. Enabling both simultaneously triggers acompile_error!. Using--all-featuresis intentionally unsupported — use--features fullor--features full,postgresinstead.
Features
- Compile-time backend selection —
DbPool,DbRow,DbTransaction, andDbQueryResultresolve to the correct sqlx types based on the active feature sql!macro — write?placeholders once; the macro rewrites them to$1, $2, ...for PostgreSQL and is a zero-cost no-op for SQLiteDialecttrait — backend-specific SQL constants (AUTO_PK,INSERT_IGNORE,EPOCH_NOW, etc.) and helpers (ilike,epoch_from_col) via zero-sized marker types- Automatic migrations —
DbConfig::connectrunsmigrations/sqlite/ormigrations/postgres/on startup; WAL checkpoint applied after SQLite migrations FullDriversuper-trait — reduces sqlx bound repetition in generic impl blocks across consumer crates- FTS helpers — backend-aware
WHERE/JOIN/rank fragments for messages and graph entity full-text search - Safe URL logging —
redact_urlstrips credentials from connection strings before they appear in logs - Write transactions —
begin_writeissuesBEGIN IMMEDIATEon SQLite (preventsSQLITE_BUSY); falls back to standardBEGINon PostgreSQL
Runtime backend selection
The active backend is determined at compile time by feature flag. For deployments that need to switch between SQLite and PostgreSQL without recompiling, set ZEPH_DATABASE_URL or database_url in config.toml:
[]
= "postgres://user:pass@localhost/zeph"
ZEPH_DATABASE_URL=postgres://user:pass@localhost/zeph
Important:
The URL scheme (
sqlite:/postgres:) must match the compiled feature. Apostgres://URL with thesqlitefeature (or vice versa) will fail at startup with a clear error.
CLI migrations
Run pending migrations without starting the agent:
Tip:
Use
zeph db migrate --dry-runto print the SQL that would be applied without executing it.
Installation
This crate is an internal workspace member of Zeph. To use it in a workspace crate:
[]
= { = "../zeph-db" }
# or with postgres backend:
= { = "../zeph-db", = false, = ["postgres"] }
Feature Flags
| Feature | Description |
|---|---|
sqlite (default) |
Enables SQLite backend via sqlx/sqlite |
postgres |
Enables PostgreSQL backend via sqlx/postgres |
test-utils |
Enables testcontainers + testcontainers-modules for PostgreSQL integration tests; implies postgres |
Usage
Connect and run migrations
use ;
let config = DbConfig ;
let pool: DbPool = config.connect.await?;
For in-memory SQLite (useful in tests):
let pool = DbConfig
.connect
.await?;
Write portable SQL with the sql! macro
use sql;
let rows = query
.bind
.fetch_all
.await?;
Note:
Do not use the
sql!macro for PostgreSQL JSONB queries that contain?,?|, or?&operators — use$Nplaceholders directly for those.
Dialect-aware SQL fragments
use ;
let ddl = format!;
let insert = format!;
Transactions
use ;
// Standard deferred transaction
let mut tx = begin.await?;
// Write-intent transaction (BEGIN IMMEDIATE on SQLite)
let mut tx = begin_write.await?;
query.bind.execute.await?;
tx.commit.await?;
FTS helpers
use ;
let q = sanitize_fts_query;
let sql = format!;
Generic consumer crates
Use D: DatabaseDriver + FullDriver as the single generic bound when you need both sqlx pool access and SQL dialect fragments:
use ;
async
Migrations
SQL migration files live in:
migrations/sqlite/— SQLite DDL (FTS5 virtual tables, triggers, indexes)migrations/postgres/— PostgreSQL DDL (tsvector columns, GIN indexes,plainto_tsquerysetup)
Migrations run automatically on first DbConfig::connect call. The active backend's directory is embedded at compile time via sqlx::migrate!.
MSRV
Rust 1.88 (Edition 2024, resolver 3).
License
MIT — see LICENSE.