Expand description
§rainier-database
Rainier’s data layer, built on Rainier ORM — the multi-dialect DBAL
where one #[derive(Entity)] serves SQLite, Cloudflare D1, MySQL and
Postgres, and where the stack of hand-written per-engine repositories
collapses to zero.
This crate adds the four things a framework needs on top of that:
Connection / Database | a dyn-safe port over Rainier ORM’s Executor, so a backend can live in the IoC container |
Model | an entity the framework manages, with lifecycle hooks |
Repository / EntityRepository | a contract to depend on, implemented once for every model |
Criteria / Paginated | composable scopes and paging |
use rainier_orm::{repo, Entity, SeaOrmExecutor, PoolConfig};
use rainier_database::{bind_executor, Criteria, Database, EntityRepository, Model, Repository};
#[derive(Entity, Clone)]
#[orm(table = "posts")]
struct Post {
#[orm(pk, auto_increment)] id: u64,
title: String,
published: bool,
}
impl Model for Post {}
bind_executor!(SeaOrmExecutor);
let executor = SeaOrmExecutor::connect("sqlite::memory:", &PoolConfig::default()).await?;
let db = Database::new(executor);
let posts = EntityRepository::<Post>::new(db.clone());
// Through the repository contract…
let page = posts
.paginate_matching(Criteria::new().where_eq("published", true), 1, 20)
.await?;
// …or straight through Rainier ORM, since `Database` is an `Executor`.
let newest: Option<Post> = repo::query::<Post>().order_by_desc("id").first(&db).await?;§The Executor problem, and how it is solved
Rainier ORM’s Executor uses async fn in trait and carries no Send + Sync
bound — deliberately, so the same code runs in a single-threaded Cloudflare
Worker over a !Send D1 binding. That makes it unusable as dyn Executor,
which is exactly what a container needs to store.
Database resolves this by holding an Arc<dyn Connection> (Rainier’s
dyn-safe mirror) and re-implementing Executor on top of it. The
whole ORM surface therefore works against a Database unchanged — no
wrapper per operation, and no gap between what Rainier ORM can do and what
Rainier exposes. Register a concrete backend with
bind_executor!.
Re-exports§
pub use connection::Connection;pub use connection::Database;pub use criteria::Constraint;pub use criteria::Criteria;pub use migrator::Down;pub use migrator::Migration;pub use migrator::Migrator;pub use migrator::Step;pub use model::Created;pub use model::Creating;pub use model::Deleted;pub use model::Deleting;pub use model::Model;pub use model::Updated;pub use model::Updating;pub use pagination::Paginated;pub use raw::RawQuery;pub use relation::BelongsTo;pub use relation::BelongsToMany;pub use relation::HasMany;pub use relation::HasOne;pub use relation::PivotQuery;pub use relation::Related;pub use relation::RelatedCounts;pub use relation::Relation;pub use relation::RelationKey;pub use repository::EntityRepository;pub use repository::Repository;pub use row::Cell;pub use row::ColumnRequest;pub use row::OwnedRow;pub use statement::Prepared;pub use rainier_orm;
Modules§
- connection
- The connection port —
ConnectionandDatabase. - criteria
- Composable query scopes —
Criteria. - ddl
- A declarative, dialect-portable schema-change language — the single way to express an incremental migration step.
- migrate
- Schema migrations — an ordered list of steps applied idempotently,
tracked in a
_orm_migrationstable, against one executor or many. - migrator
- Schema migrations — the
Migrationcontract, and theMigratorthat runs and rolls them back. - model
- The
Modelcontract and its lifecycle hooks. - pagination
- Paginated results —
Paginated. - raw
- Raw SQL, bound safely —
RawQuery. - relation
- Relationships — Eloquent’s
hasOne,hasMany,belongsToandbelongsToMany, as values you load rather than properties that query themselves. - repo
- Generic CRUD over any
Entity, plus a keysetCursorfor walking large tables without holding them in memory. - repository
- Repositories — the
Repositorycontract and its generic implementation. - row
- Owned row snapshots —
OwnedRow,CellandColumnRequest. - schema
- Generic DDL: turn an
Entity’s metadata intoCREATE TABLE+CREATE INDEXstatements, rendered for anyDialect. - statement
- Statement preparation — building SQL synchronously, so the futures that
run it are
Send. - testing
- Test doubles —
MemoryConnectionandfake_database.
Macros§
- bind_
executor - Writes the
Connectionimpl for a concrete Rainier ORMExecutor.
Structs§
- Blueprint
- A table being created — Laravel’s
Blueprint. - Index
Def - One index, described.
- Query
- A builder for reads (and
WHERE-scoped deletes) overE. Conditions are AND-combined; usefilterto add an OR group. - Table
Changes - A change to a table that already exists — Laravel’s
Schema::table.
Enums§
- Action
- What to do to rows on the other side when a referenced row goes.
- Column
Kind - What a column holds.
- Dialect
- The SQL dialect a backend speaks. Cloudflare D1 maps to
Sqlite(it is SQLite over HTTP); the executor handles the transport difference.
Traits§
- Entity
- A struct that maps to one table. Implemented by
#[derive(Entity)]; hand impls are possible but the derive is the intended path. - Executor
- A database backend. The single integration point: implement this and the generic CRUD layer works against your store unchanged.