Skip to main content

Crate keelson_exec

Crate keelson_exec 

Source
Expand description

The execution layer’s traits, owned by no driver.

Layer 1 builds a statement — Query::build() hands back (String, Vec<Value>), synchronously, with no driver in the loop. This crate is everything between that pair and a mapped Rust value coming back out of a real database, expressed as traits a backend crate implements:

  • Executor — object-safe, three methods, &self. A pool, a connection and a Transaction all implement it, so &dyn Executor is the type application code, generated models and hooks pass around.
  • Execute — the ergonomic verbs, blanket-implemented on every Query: q.fetch_all(&db), q.fetch_one(&db), q.execute(&db). Tracing (feature tracing) lives here, in the one funnel every backend flows through.
  • Transaction and Begin — an owned, lifetime-free transaction that consumes itself on commit/rollback; savepoints are closures. BeginWith adds isolation levels and access modes (TxOptions), refusing per engine anything that engine would only appear to honour. Atomic is the one a reusable unit of work takes: a transaction at the top, a savepoint inside one, and the same call site either way.
  • Row and FromRow — rows decoded once, at the driver seam, into Values; every decode error names its column.
  • RawConnection — the seam a backend implements per driver; this crate owns the transaction SQL (BEGIN/COMMIT/SAVEPOINT …) so its semantics cannot drift between backends.

§Which one does my function take?

The question every signature in an application asks, and the answer is a capability, not a style. Each row may do everything above it:

parameterwhat the function may do
db: &dyn Executorrun statements
db: impl Atomic…and carve one all-or-nothing block out of wherever it turns out to be
db: impl Begin (a pool)…and start a transaction, with an isolation level
db: &Transaction…and commit or roll it back — and, on purpose, not begin: nesting is spelled savepoint

Take the weakest row that does the job. A repository method that runs one statement takes &dyn Executor; a unit of work that must not half-apply takes impl Atomic; a usecase saying “a transaction begins here” takes a pool and calls within.

The ladder only goes downward. An impl Atomic can be handed on as &dyn Executor, and so can a Transaction — but nothing recovers a scope from &dyn Executor, because erasing it threw away whether a transaction is open. That one-way street is a safety property rather than a limitation: a hook receives &dyn Executor not because hooks are trusted, but because the type it is given has no method that could end the caller’s transaction.

It is also why the spellings differ. Executor’s three methods are object-safe, so it is erased and compiles once; Atomic::atomic takes the caller’s closure, whose type differs at every call site, so it can only be generic — and being generic is exactly what lets it open a scope. impl Atomic still accepts everything: &pool, pool, Arc<pool>, &dyn Begin, and the &Transaction a scope closure hands you.

The full design, with every rejected alternative, is docs/execution.md. The type-by-type binding contract backends implement against is docs/type-mappings.md.

No public type here carries a lifetime parameter (the house rule); the only lifetimes are the transient '_ on futures borrowed from &self for one call. Nothing here names a driver: Layer 2’s generated models depend on this crate and pick up a backend only in the application’s own Cargo.toml.

§Where this sits

Layer 2 of keelson, and the half of it that names no driver. Below: keelson-core and the dialect crates, which build the (String, Vec<Value>) these traits carry. Beside: keelson-sqlx, the backend that implements them over sqlx’s PostgreSQL, MySQL and SQLite drivers. Above: keelson-models, whose generated models execute through &dyn Executor and therefore through whatever backend the application picked. The whole map is the keelson facade crate.

Macros§

bind_newtype
Implement ToValue and FromValue for a single-field newtype by delegating to the inner type — the “derivable for newtypes” story, without a proc-macro crate.

Structs§

Column
One column of a result set’s header.
ExecResult
What a side-effect statement reports back.
Row
One decoded row: a shared column header and one Value per column.
RowStream
An owned stream of rows (house rule: no lifetime parameter).
Statement
What crosses the executor boundary: exactly what build() produces, plus the statement-kind hint core already carries.
Transaction
An open transaction. Owned, lifetime-free, and an Executor — any function written as fn f(db: &dyn Executor) accepts it, which is what lets model hooks run inside the caller’s transaction without knowing one exists.
TxConflictError
The error a backend reports for a TxConflict, carrying the engine’s own code and message and (usually) the driver error as its source.
TxOptions
What a transaction is opened with: the parts of transaction control the three engines do not agree on.

Enums§

Access
A transaction’s access mode.
ExecError
Everything that can go wrong while executing a statement.
Family
Which engine family an executor talks to.
Isolation
A SQL-standard isolation level, as asked for.
SqliteBegin
SQLite’s begin modes — not isolation levels, and named so nobody can mistake them for a portable knob.
TxConflict
A concurrency conflict the engine raised: this transaction lost, and the only correct response is to run the whole thing again.

Traits§

Atomic
All-or-nothing here, wherever “here” turns out to be: a transaction when nothing is open, a savepoint when a transaction already is.
Begin
Something a transaction can be begun on: a pool or a connection.
BeginExt
The closure form of a transaction: commit on Ok, roll back on Err.
BeginWith
Opt-in transaction options: begin_with beside begin.
BeginWithExt
BeginExt::within with options.
Bind
What a column’s Rust type must be able to do: bind in (ToValue) and read back out (FromValue).
Execute
The ergonomic verbs, hung on every Query.
Executor
Anything that can run a built statement: a pool, a connection, a Transaction.
FromRow
A type that can be built from a whole row.
RawConnection
One raw connection, exclusively held. The seam a backend implements.
StreamExecutor
Opt-in streaming. A backend that can stream implements it; nothing requires it, because drivers differ too much here for it to belong in the minimum contract (native streams borrow their connection; ours must not).

Functions§

assert_bind
Assert at compile time that T can bind as a column.

Type Aliases§

ExecFuture
The boxed future every trait method returns.
ExecHook
The hook payload the execution layer fixes QueryExtensionsHook parameter to: an async function of &dyn Executor.
ExecLoader
The loader payload: like ExecHook, plus the rows the query produced.