Cheat Sheet¶
Every call sequence, in one page.
Simple ad-hoc query¶
Odbc.connect(Dsn)
├── Connection.query(sql) ──► Cursor
│ ├── Cursor.fetch() ──► Row | EndOfRows | FetchError
│ ├── Cursor.values() ──► iterator of (Row | FetchError)
│ └── Cursor.close()
└── Connection.close()
See: Querying.
Prepared statement + bind + single execute¶
Connection.prepare(sql) ──► Statement
├── Statement.bind(ParamIndex, SqlValue) ──► Bound | BindError
├── Statement.bind(...) ──► Bound | BindError
├── Statement.execute_update() ──► RowCount | ExecError
└── Statement.close()
See: Binding Parameters.
Prepared statement + SELECT + fetch¶
Connection.prepare(sql) ──► Statement
├── Statement.bind(...) as needed
├── Statement.execute() ──► Executed | ExecError (opens cursor)
│ ├── Statement.fetch() ──► Row | EndOfRows | FetchError
│ └── Statement.values() ──► iterator of (Row | FetchError)
├── Statement.close_cursor()
└── Statement.close()
See: Executing, Reusing Statements.
Prepare-time metadata (no execute)¶
Connection.prepare(sql) ──► Statement
├── Statement.parameter_types() ──► Array[SqlTypeTag] val | MetadataError
├── Statement.column_types() ──► Array[ColumnMeta] val | MetadataError
└── Statement.close()
See: Prepare-Time Metadata.
Transaction¶
flowchart TD
A["Connection.begin()"] --> B{"TxBegun or TxBeginError?"}
B -->|TxBegun| C["... exec / prepare / etc. ..."]
B -->|TxBeginError| X["abort"]
C --> D{"ok or err?"}
D -->|ok| E["Connection.commit()<br/>→ TxCommitted / TxCommitError"]
D -->|err| F["Connection.rollback()<br/>→ TxRolledBack / TxRollbackError"]
See: Transactions.
Zero-allocation fetch loop¶
Connection.prepare(sql) ──► Statement
Statement.execute() ──► Executed
let row = MutableRow
loop:
Statement.fetch_into(row) ──► MutableRow | EndOfRows | FetchError
Statement.close_cursor()
Statement.close()
See: Zero-Allocation Fetch.
Async via DbSession¶
DbSession(Dsn)
├── db.exec(sql, Promise[(RowCount | ExecError)])
├── db.query(sql, Promise[(Array[Row val] val | ExecError)])
├── db.begin / commit / rollback (each with its own Promise type)
└── db.close()
See: Async with DbSession.
Cross-actor cancellation¶
// Querying actor
let token = stmt.cancel_token()
supervisor.register(token)
stmt.execute() // blocks
// Supervising actor
token.cancel() // fires SQLCancel asynchronously
// Querying actor wakes with:
// ExecError with SQLSTATE HY008 (ODBC) or 57014 (Postgres)
See: Cancellation.
Error vocabulary¶
| Class | Kind union |
|---|---|
ConnectError |
EnvAllocFailed, DbcAllocFailed, DriverConnectFailed |
ExecError |
QueryError, ConstraintViolation, SyntaxError, ConnectionLost, UnboundParams, StatementClosed, ConnectionClosed, CursorNotOpen, CursorAlreadyOpen |
PrepareError |
DriverPrepareError, PrepareConnectionClosed |
BindError |
ParamIndexOutOfRange, ParamTooLarge, DriverRejected, BindStatementClosed, BindConnectionClosed |
FetchError |
DriverFetchError, ColumnTooLarge, UnsupportedColumnType, InvalidUtf8, FetchConnectionLost, FetchConnectionClosed, CursorClosed |
MetadataError |
MetadataStatementClosed, MetadataConnectionClosed, DriverDoesNotSupportDescribeParam, DriverMetadataError |
TxBeginError |
AlreadyInTransaction, TxBeginConnectionClosed, DriverTxError |
TxCommitError |
verdict: CommitFailed, CommitAmbiguous, NotInTransaction |
TxRollbackError |
RollbackNotInTransaction, DriverRollbackError |
Every error has:
.kind()(or.verdict()onTxCommitError) — enum-ish primitive.string()— redacted, safe to log.unsafe_diag()— rawDiagChain; may contain credentials
ExecError and PrepareError also have .unsafe_sql() — the SQL that was running.
SqlValue variants¶
| Variant | Carrier | Construct | Read via |
|---|---|---|---|
SqlNull |
(primitive) | SqlNull |
row.is_null(i)? |
SqlBool |
Bool |
SqlBool(true) |
row.bool(i)? |
SqlTinyInt |
I8 |
SqlTinyInt(12) |
row.int(i)? |
SqlSmallInt |
I16 |
SqlSmallInt(12) |
row.int(i)? |
SqlInteger |
I32 |
SqlInteger(12) |
row.int(i)? |
SqlBigInt |
I64 |
SqlBigInt(12) |
row.int(i)? |
SqlFloat |
F64 |
SqlFloat(3.14) |
row.float(i)? |
SqlText |
String val |
SqlText("x") |
row.text(i)? |
SqlDate |
(I16, U16, U16) |
SqlDate(y, m, d) |
row.date(i)? |
SqlTime |
(U16, U16, U16) |
SqlTime(h, m, s) |
row.time(i)? |
SqlTimestamp |
date+time+fraction | SqlTimestamp(...) |
row.timestamp(i)? |
SqlDecimal |
String val |
SqlDecimal("1.23") |
row.decimal(i)? |
row.int() widens any of the four integer variants to I64. For the specific variant, use row.column(i)? and match on the result.