Backends: PostgreSQL & SQLite¶
Yara ORM selects a database backend by connection URL. The same model and queryset
code runs unchanged across backends — only the URL you pass to YaraOrm.init() differs.
from yara_orm import YaraOrm
await YaraOrm.init("postgres://user:pass@localhost/db") # PostgreSQL (tokio-postgres)
await YaraOrm.init("sqlite:///path/to/app.db") # SQLite (rusqlite)
PostgreSQL¶
The PostgreSQL backend is built on tokio-postgres with a deadpool connection pool.
- Async, pooled connections kept warm for steady-state latency.
- Prepared-statement caching (
prepare_cached) per pooled connection — on by default, disable withstatement_cache_size=0(see below). - Case-insensitive lookups (
icontains,istartswith, …) use SQLILIKE. - Column and table
description=values become SQLCOMMENTs.
URL schemes
Both postgres:// and postgresql:// style URLs are accepted, including
user:password@host:port/dbname and standard query parameters.
Pool and statement-cache tuning¶
A few pool/cache knobs ride along as URL query parameters. They are consumed by
the engine and stripped from the URL before the driver parses it, so they sit
alongside ordinary driver parameters (e.g. sslmode):
await YaraOrm.init(
"postgres://user:pass@host/db"
"?max_size=32&min_size=4&statement_cache_size=0&sslmode=require"
)
| Parameter | Default | Effect |
|---|---|---|
max_size |
16 |
Maximum pooled connections. |
min_size |
0 |
Connections pre-warmed at startup (best effort — the pool keeps no hard minimum). |
statement_cache_size |
nonzero | 0 disables per-connection prepared-statement caching. |
PgBouncer / transaction pooling
The PostgreSQL backend caches prepared statements per connection by default.
Behind a transaction-pooling proxy such as PgBouncer, set
statement_cache_size=0 so each statement is prepared and used within a
single pooled checkout — otherwise the proxy can route a Bind to a backend
that never saw the Parse. A non-numeric value (e.g. max_size=lots) raises
a ValueError at init() rather than being silently ignored.
These parameters apply to SQLite too (max_size/min_size/statement_cache_size);
in-memory databases always pin a single connection regardless of max_size.
SQLite¶
The SQLite backend is built on rusqlite (bundled SQLite), bridged to async by hopping to a blocking thread per call.
- Rich types (UUID, JSON, datetime, decimal) are mapped onto SQLite's storage classes and reconstructed on read from the declared column type — so your models behave identically.
- Case-insensitive lookups use
LIKE(SQLite'sLIKEis already case-insensitive for ASCII), sinceILIKEis PostgreSQL-only. This is handled for you by the dialect.
When to choose which
SQLite is ideal for tests, local development, embedded apps and small services; PostgreSQL for concurrent, production workloads. Because the model layer is identical, you can develop against SQLite and deploy on PostgreSQL.
Mixing backends¶
Each named connection has its own backend, so a single app can talk to a PostgreSQL database and a SQLite database at once. See Multiple databases.
await YaraOrm.init("postgres://localhost/primary") # default
await YaraOrm.add_connection("cache", "sqlite:///cache.db")
Adding a new backend¶
The backend abstraction is intentionally a two-seam extension point:
- A Rust
Backendtrait implementation (connection, execution, value conversion) plus a scheme match inrust/src/backend/mod.rs. - A
BaseDialectsubclass inpython/yara_orm/dialects.pythat renders SQL for the new database, registered viaregister_dialect(name, DialectClass).
The model and queryset layers never change. See Architecture for the full picture.
See also¶
- Migrations — backend-portable schema changes.
- Performance — PostgreSQL and SQLite benchmark results.