std/sql
On this page
Index
- type
SqlValue - type
Isolation - type
SqlErr- method
SqlErr.display
- method
- type
ExecResult - type
Db- method
Db.query - method
Db.rows - method
Db.each - method
Db.copy_from - method
Db.query_one - method
Db.exec - method
Db.query_as - method
Db.query_one_as - method
Db.begin - method
Db.ping - method
Db.close
- method
- type
Tx- method
Tx.query - method
Tx.rows - method
Tx.each - method
Tx.copy_from - method
Tx.query_one - method
Tx.exec - method
Tx.query_as - method
Tx.query_one_as - method
Tx.commit - method
Tx.rollback
- method
- type
Row- method
Row.columns - method
Row.decode - method
Row.value - method
Row.is_null - method
Row.int - method
Row.str - method
Row.float - method
Row.bool - method
Row.time - method
Row.int_opt - method
Row.str_opt - method
Row.float_opt - method
Row.bool_opt - method
Row.time_opt - method
Row.bytes - method
Row.bytes_opt - method
Row.json - method
Row.ints - method
Row.strs - method
Row.items
- method
- type
Rows- method
Rows.columns - method
Rows.next - method
Rows.next_as - method
Rows.close
- method
- type
Driver - type
DriverConn - type
DriverRows - type
DriverCursor - fn
null - fn
int - fn
str - fn
float - fn
bool - fn
time - fn
bytes - fn
json - fn
array - fn
quote_ident - fn
open - fn
memory - fn
open_driver
Types
SqlValue
enum SqlValue {
Null
Integer(n: Int)
Text(s: Str)
Real(f: Float)
Flag(b: Bool)
At(t: Time)
Blob(b: Bytes)
JsonText(s: Str)
Array(items: List[SqlValue])
}A query parameter. $1 is args[0].
Isolation
enum Isolation {
Default
ReadUncommitted
ReadCommitted
RepeatableRead
Serializable
}A transaction’s isolation level (Go’s sql.IsolationLevel, the four of standard SQL); Default is the server’s own.
SqlErr
enum SqlErr {
Syntax(msg: Str)
Constraint(msg: Str, code: Str, name: Str)
Param(msg: Str)
NoTable(name: Str)
NoRows
NoColumn(name: Str)
Mismatch(column: Str, msg: Str)
Conn(msg: Str)
Driver(msg: Str)
Server(code: Str, msg: Str)
TxDone
Closed
Canceled
}SqlErr.display
fn (e: SqlErr) display() -> StrPlain text (decision 0358): syntax error: MSG, MSG for a Constraint,
Param or Driver, no such table: NAME, no rows in result set,
no such column: NAME, column COL: MSG, connection: MSG,
MSG (SQLSTATE CODE), transaction already committed or rolled back,
database is closed, canceled.
ExecResult
struct ExecResult {
rows_affected: Int
last_insert_id: Int
}Db
struct Db[fx: Effects] {}Db.query
fn (d: Db) query(q: Str, args: List[SqlValue] = []) -> List[Row] ! SqlErr uses net.dialDb.rows
fn (d: Db) rows(q: Str, args: List[SqlValue] = []) -> Rows ! SqlErr uses net.dialquery as a cursor: the rows are read from the connection as next
asks (the Postgres driver; the in memory database and the Go backend
read the result whole), so a result larger than memory can be walked.
The connection stays taken until next returns None or an error, or
close; use each or defer rows.close().
Db.each
fn (d: Db) each(q: Str, args: List[SqlValue], f: fn(Row) -> () ! SqlErr) -> () ! SqlErr uses net.dialrows, calling f on each row and closing in every path; an error from
f ends the walk and is returned.
Db.copy_from
fn (d: Db) copy_from(table: Str, columns: List[Str], rows: fn() -> List[SqlValue]? ! SqlErr) -> Int ! SqlErr uses net.dialLoads rows with COPY FROM STDIN in the text format (decision 0479, pgx’s
CopyFrom): rows is called until it returns None, each row a list of
values in the order of columns; the result is the number of rows
copied. An error from rows cancels the COPY (the server rolls it
back) and is returned. table and columns are identifiers, quoted by
the driver (quote_ident); values are never interpolated. The in
memory database inserts the rows one by one; a driver without COPY
returns Driver.
Db.query_one
fn (d: Db) query_one(q: Str, args: List[SqlValue] = []) -> Row ! SqlErr uses net.dialDb.exec
fn (d: Db) exec(q: Str, args: List[SqlValue] = []) -> ExecResult ! SqlErr uses net.dialDb.query_as
fn (d: Db) query_as[T: SqlRow](q: Str, args: List[SqlValue] = []) -> List[T] ! SqlErr uses net.dialquery with every row decoded into T (derive SqlRow, std.md 10.4).
Db.query_one_as
fn (d: Db) query_one_as[T: SqlRow](q: Str, args: List[SqlValue] = []) -> T ! SqlErr uses net.dialquery_one with the row decoded into T.
Db.begin
fn (d: Db) begin(isolation: Isolation = Isolation.Default, read_only: Bool = false) -> Tx ! SqlErr uses net.dialA transaction (Go’s DB.BeginTx): isolation and read_only are Go’s
sql.TxOptions. A driver’s connection gets SET TRANSACTION ... with
them right after BEGIN (nothing for the defaults); the in memory
database is serializable whatever the level, and refuses a write in a
read only transaction with Server(“25006”, …), as Postgres does.
Db.ping
fn (d: Db) ping() -> () ! SqlErr uses net.dialDb.close
fn (d: Db) close() -> () ! SqlErrTx
struct Tx[fx: Effects] {}Tx.query
fn (t: Tx) query(q: Str, args: List[SqlValue] = []) -> List[Row] ! SqlErr uses net.dialTx.rows
fn (t: Tx) rows(q: Str, args: List[SqlValue] = []) -> Rows ! SqlErr uses net.dialDb.rows inside the transaction: the transaction’s connection is
busy until the cursor ends or is closed.
Tx.each
fn (t: Tx) each(q: Str, args: List[SqlValue], f: fn(Row) -> () ! SqlErr) -> () ! SqlErr uses net.dialTx.copy_from
fn (t: Tx) copy_from(table: Str, columns: List[Str], rows: fn() -> List[SqlValue]? ! SqlErr) -> Int ! SqlErr uses net.dialTx.query_one
fn (t: Tx) query_one(q: Str, args: List[SqlValue] = []) -> Row ! SqlErr uses net.dialTx.exec
fn (t: Tx) exec(q: Str, args: List[SqlValue] = []) -> ExecResult ! SqlErr uses net.dialTx.query_as
fn (t: Tx) query_as[T: SqlRow](q: Str, args: List[SqlValue] = []) -> List[T] ! SqlErr uses net.dialTx.query_one_as
fn (t: Tx) query_one_as[T: SqlRow](q: Str, args: List[SqlValue] = []) -> T ! SqlErr uses net.dialTx.commit
fn (t: Tx) commit() -> () ! SqlErr uses net.dialTx.rollback
fn (t: Tx) rollback() -> () ! SqlErr uses net.dialRow
struct Row {}Row.columns
fn (r: Row) columns() -> List[Str]Row.decode
fn (r: Row) decode[T: SqlRow]() -> T ! SqlErrDecodes the row into a struct with derive SqlRow: each field from the
column of the same name (std.md 10.4).
Row.value
fn (r: Row) value(name: Str) -> SqlValue ! SqlErrThe column’s value as the driver read it (pgx’s Rows.Values, one column; NoColumn for an unknown name).
Row.is_null
fn (r: Row) is_null(name: Str) -> Bool ! SqlErrRow.int
fn (r: Row) int(name: Str) -> Int ! SqlErrRow.str
fn (r: Row) str(name: Str) -> Str ! SqlErrRow.float
fn (r: Row) float(name: Str) -> Float ! SqlErrRow.bool
fn (r: Row) bool(name: Str) -> Bool ! SqlErrRow.time
fn (r: Row) time(name: Str) -> Time ! SqlErrRow.int_opt
fn (r: Row) int_opt(name: Str) -> Int? ! SqlErrRow.str_opt
fn (r: Row) str_opt(name: Str) -> Str? ! SqlErrRow.float_opt
fn (r: Row) float_opt(name: Str) -> Float? ! SqlErrRow.bool_opt
fn (r: Row) bool_opt(name: Str) -> Bool? ! SqlErrRow.time_opt
fn (r: Row) time_opt(name: Str) -> Time? ! SqlErrRow.bytes
fn (r: Row) bytes(name: Str) -> Bytes ! SqlErrRow.bytes_opt
fn (r: Row) bytes_opt(name: Str) -> Bytes? ! SqlErrRow.json
fn (r: Row) json[T: Json](name: Str) -> T ! SqlErrA json or jsonb column (or text) decoded into T; a decode error is Mismatch(column, msg).
Row.ints
fn (r: Row) ints(name: Str) -> List[Int] ! SqlErrAn array of integers; a NULL element is Mismatch.
Row.strs
fn (r: Row) strs(name: Str) -> List[Str] ! SqlErrRow.items
fn (r: Row) items(name: Str) -> List[SqlValue] ! SqlErrAn array’s elements as values.
Rows
struct Rows[fx: Effects] {}A result read row by row (decision 0479, Go’s *sql.Rows): holds a
connection from rows to the last row or close.
Rows.columns
fn (r: Rows) columns() -> List[Str]Rows.next
fn (r: Rows) next() -> Row? ! SqlErr uses net.dialThe next row, None after the last (the connection is then released).
Rows.next_as
fn (r: Rows) next_as[T: SqlRow]() -> T? ! SqlErr uses net.dialnext decoded into T (derive SqlRow).
Rows.close
fn (r: Rows) close() uses net.dialReads and discards the rest of the result and releases the connection; idempotent.
Driver
interface Driver[fx: Effects] {
fn connect(self, net: Net, dsn: Str) -> DriverConn[fx] ! SqlErr uses (net.dial, fx)
}A database driver written in Xo (decision 0278): opened with
open_driver, which gives it std/sql’s pool, transactions, and
cancellation. It dials through the Net it is given.
DriverConn
interface DriverConn[fx: Effects] {
fn query(self, q: Str, args: List[SqlValue]) -> DriverRows[fx] ! SqlErr uses (net.dial, fx)
fn exec(self, q: Str, args: List[SqlValue]) -> ExecResult ! SqlErr uses (net.dial, fx)
fn begin(self) -> () ! SqlErr uses (net.dial, fx)
fn commit(self) -> () ! SqlErr uses (net.dial, fx)
fn rollback(self) -> () ! SqlErr uses (net.dial, fx)
fn ping(self) -> () ! SqlErr uses (net.dial, fx)
fn copy_from(self, table: Str, columns: List[Str], rows: fn() -> List[SqlValue]? ! SqlErr) -> Int ! SqlErr uses (net.dial, fx)
fn close(self) uses (net.dial, fx)
fn is_broken(self) -> Bool
}One connection of a Driver, used by one task at a time. Its waits are cancellation points that return Canceled.
DriverRows
struct DriverRows[fx: Effects] {
columns: List[Str]
rows: List[List[SqlValue]]
cursor: DriverCursor[fx]? = None
}The result of DriverConn.query: column names and rows of values. A driver may return a few rows (or none) and a cursor for the rest (decision 0479): Db.rows reads them as asked, Db.query reads them all.
DriverCursor
interface DriverCursor[fx: Effects] {
fn next(self) -> List[SqlValue]? ! SqlErr uses (net.dial, fx)
fn rest(self) -> List[List[SqlValue]] ! SqlErr uses (net.dial, fx)
fn close(self) uses (net.dial, fx)
}The rest of a result still on the connection. The connection is not used again until the cursor is read to the end or closed.
Functions
null
fn null() -> SqlValueint
fn int(n: Int) -> SqlValuestr
fn str(s: Str) -> SqlValuefloat
fn float(f: Float) -> SqlValuebool
fn bool(b: Bool) -> SqlValuetime
fn time(t: Time) -> SqlValuebytes
fn bytes(b: Bytes) -> SqlValuejson
fn json[T: Json](v: T) -> SqlValueJsonText of json.encode_str(v).
array
fn array(items: List[SqlValue]) -> SqlValuequote_ident
fn quote_ident(name: Str) -> StrAn SQL identifier in double quotes, a double quote doubled (Postgres’s quote_ident, pgx’s Identifier.Sanitize).
open
fn open(net: Net, driver: Str, dsn: Str, max_open: Int = 10, max_idle: Int = 2, max_lifetime: Duration = 30m, roots: Bytes = b"") -> Db ! SqlErr uses net.dialOpens a database: “postgres” (decision 0278; roots is PEM for
sslmode verify-ca and verify-full) natively. max_open bounds concurrent
operations and open transactions (waiting is a cancellation point),
max_idle and max_lifetime are passed to the driver.
memory
fn memory(schema: Str = "") -> Db ! SqlErrAn in memory database, optionally created with schema (; separated
statements). Needs no capability.
open_driver
fn open_driver[fx: Effects](net: Net, d: Driver[fx], dsn: Str, max_open: Int = 10, max_idle: Int = 2, max_lifetime: Duration = 30m) -> Db[fx] ! SqlErr uses (net.dial, fx)A Db over d: max_open connections at most (operations and open
transactions; waiting is a cancellation point), max_idle kept open
when unused, each closed after max_lifetime. Connects once before it
returns, so a wrong password or host fails here.