SQL database access, backed by sqlx. PostgreSQL, MySQL, and SQLite are
selected from the connection URL scheme.
A Database is a linear reference to a shared pool. Query streams and
transactions are independent linear leases. .split creates another linear
pool handle; .close drops only this handle and never waits for siblings or
outstanding leases.
Temporal values travel as text. The portable driver does not decode
dedicated temporal column types, so on PostgreSQL and MySQL, cast temporal
columns to text when reading (e.g. at::text) and cast text parameters
when writing (e.g. $1::timestamptz); SQLite stores temporals as text
natively.
type Sql.Database = iterative choice {
.close => !,
.execute(String, List<Sql.Value>) => (Try<Sql.Error, Nat>) self,
.query(String, List<Sql.Value>) => (Try<Sql.Error, Stream<Sql.Error, Sql.Row>>) self,
.split => (self) self,
.transaction => (Try<Sql.Error, Sql.Transaction>) self,
}
A linear handle to a shared pool. It is not a connection. Splitting creates
another handle to the same pool; both handles must eventually be closed.
type Sql.Transaction = iterative choice {
.commit => Try<Sql.Error, !>,
.execute(String, List<Sql.Value>) => Try<Sql.Error, (Nat) self>,
.query(String, List<Sql.Value>) => Try<Sql.Error, (List<Sql.Row>) self>,
.rollback => Try<Sql.Error, !>,
}
A transaction owns one connection. Statement failure consumes the
transaction and the implementation rolls back or drops the connection before
reporting the error. Successful statements return self. A .commit error
means the commit may or may not have taken effect (e.g. when it times out).
A portable SQL scalar for parameters and result cells.
This intentionally remains ordinary Par data. Par comparison/display on
Value is structural Par behavior, not SQL comparison semantics.
Only PostgreSQL produces .bool result cells; SQLite and MySQL surface
stored booleans as integers (AsBool accepts both). Columns declared
BOOLEAN on SQLite and MySQL cannot be decoded by the portable driver at
all — store booleans in integer columns there.
Required text format; accepts exactly .text values.
Connect and validate connectivity. The URL scheme picks the backend:
postgres://..., mysql://..., sqlite://file.db, sqlite::memory:.
SQLite does not create a missing database file by default; open with
sqlite://file.db?mode=rwc to create it. An in-memory SQLite database is
pinned to a single connection, so drain or cancel its query streams before
running further statements.