- Python 98.6%
- Shell 1.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .forgejo/workflows | ||
| .githooks | ||
| .github/workflows | ||
| src | ||
| tests | ||
| tools/pdisc | ||
| .gitignore | ||
| .pdisc.yaml | ||
| INDEX.md | ||
| LICENSE | ||
| project.trb | ||
| README.md | ||
| title | summary | keywords | type | updated | body_hash | |||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| torb/sql: shared SQL layer for TorbScript drivers | README of the capability-free SQL base package of TorbScript: values, rows, errors, pool, transactions and the traits drivers implement. Read when writing a SQL driver or code against one. |
|
reference | 2026-10-07 | a75e8612bbc8 |
torb/sql
What every SQL driver of TorbScript shares, so a program is written once against it and a driver is written without a pool of its own: values, rows, errors, a connection pool, transactions, and the three traits a driver implements. It reaches nothing by itself - no network, no files - so importing it claims no capability.
use Pool, SqlError, decodedRows from "torb/sql"
use Postgres from "torb/postgres"
type User {
id: Int
name: String
nickname: String? = None
}
fn activeUsers(): Task<Result<List<User>, SqlError>> {
using pool = Pool(Postgres(), Uri.tryFrom("postgres://app:secret@localhost/shop")?)
const rows = pool.query("SELECT id, name, nickname FROM users WHERE active = $1", [.Bool(true)]).await()?
decodedRows<User>(rows)
}
What is in it
| Name | What it is |
|---|---|
SqlValue |
One value of a column or a parameter: Null, Bool, Integer, Real, Numeric, Text, Binary, Timestamp, LocalTimestamp, Date, Uuid, Json, Array |
Row, Column |
One row a statement answered: row.value("name"), row.get<Int>("id"), row.get<String?>("nickname"), row.decoded<User>() |
decodedRows<Value>(rows) |
Every row as a type whose fields are named like the columns, in snake case by default |
SqlError, ServerReport |
What goes wrong, as questions: isServerError(), isUniqueViolation(), isTransactionConflict(), isConnectionFailure(), isRetryable(), and the server's report with its SQLSTATE |
Database |
What a statement runs against: query answers rows, execute the count of rows touched. A connection, a pool and a transaction are each one |
Connection |
One session with a server, as a driver opens it |
Connect |
A driver: what turns a connection string into a Connection. Connect.registry([...]) is one over several, chosen by the scheme |
Pool |
Connections through any driver, kept between statements, at most maximumConnections open; pool.begin() starts a transaction |
Transaction |
Statements that take effect together at commit(); released without one, it rolls back |
Date, Uuid |
A day of the calendar and an identifier of RFC 9562, which std has no type for yet |
Permits |
A semaphore for tasks, which std/task has not yet: one permit keeps a connection's statements in order |
Placeholders are the driver's: $1 for Postgres, ? for MySQL and SQLite. The parameters travel apart from the
statement, so a value never becomes SQL, whatever it holds.
A statement that fails fails the transaction: the statements after it are refused, and commit() rolls back and
answers the failure - what Postgres does anyway, and what keeps a half-applied change from being committed anywhere.
Writing a driver
A driver is a shared type with Connect whose connect answers a shared type with Connection. The pool, the
transactions and the registry then work with it unchanged. Connection.abandonTransaction() is how a transaction that
was released unfinished tells the driver to roll back before the next statement; isReusable() is what the pool asks
before it keeps a connection. The tests of this package drive the pool through a double of a driver
(tests/pool.test.trb), which is the smallest complete example.
Developing
$ torb format --check .
$ torb check .
$ torb test # in the VM; torb test --native builds it
$ torb doc --check .
$ torb publish --dry-run
Publishing
.forgejo/workflows/publish.yml and .github/workflows/publish.yml publish on a tag v* through trusted publishing:
no token is stored anywhere. Before the first tag, an owner of torb on packages.torb.dev configures the publishers -
the forge's, and the GitHub mirror's where the package is mirrored as TorbScript/sql (drop that entry otherwise):
$ curl -X PUT https://packages.torb.dev/api/1/packages/torb/sql/trusted-publishers \
-H "Authorization: Bearer $TOKEN" \
-d '{"publishers":[{"repository":"git.torb.dev/torb/sql","workflow":"publish.yml","ref":"refs/tags/v*"},{"repository":"TorbScript/sql","workflow":"publish.yml","ref":"refs/tags/v*","environment":"release"}],"required":true}'
Related
- docs/design/FIRST-PARTY.md -
the
torb/*packages, what goes there rather than intostd, and the order they come in. - torb/postgres - the first driver.