torb/sql - the common database interface of TorbScript: values, rows, pools, transactions
  • Python 98.6%
  • Shell 1.4%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Orion 685cf6bb2c
Some checks failed
ci / ci (pull_request) Successful in 30s
pdisc / pdisc (pull_request) Successful in 20s
ci / ci (push) Failing after 16s
pdisc / pdisc (push) Successful in 7s
chore: install pdisc (frontmatter, INDEX, CI gate)
2026-10-07 08:10:42 +02:00
.forgejo/workflows chore: install pdisc (frontmatter, INDEX, CI gate) 2026-10-07 08:10:42 +02:00
.githooks chore: install pdisc (frontmatter, INDEX, CI gate) 2026-10-07 08:10:42 +02:00
.github/workflows ci: install the locked dependencies before checking, testing and publishing 2026-09-29 07:11:10 +02:00
src docs: the examples of Date and Uuid run as doc tests 2026-09-29 00:38:46 +02:00
tests feat: SqlError from a UriError, and the tests of the failures 2026-09-29 00:18:56 +02:00
tools/pdisc chore: install pdisc (frontmatter, INDEX, CI gate) 2026-10-07 08:10:42 +02:00
.gitignore feat: values, rows, errors, the pool and transactions every SQL driver shares 2026-09-28 23:14:46 +02:00
.pdisc.yaml chore: install pdisc (frontmatter, INDEX, CI gate) 2026-10-07 08:10:42 +02:00
INDEX.md chore: install pdisc (frontmatter, INDEX, CI gate) 2026-10-07 08:10:42 +02:00
LICENSE feat: values, rows, errors, the pool and transactions every SQL driver shares 2026-09-28 23:14:46 +02:00
project.trb feat: SqlError from a UriError, and the tests of the failures 2026-09-29 00:18:56 +02:00
README.md chore: install pdisc (frontmatter, INDEX, CI gate) 2026-10-07 08:10:42 +02:00

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.
sql
driver traits
pool
transaction
row
sqlerror
decodedrows
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}'