From 5b77973a43891cd527c6f2c0e10a8d02874a08fd Mon Sep 17 00:00:00 2001 From: Branden J Brown Date: Thu, 19 Mar 2026 11:19:44 -0400 Subject: [PATCH] in progress switch to using effects --- sqlite/capi.kk | 29 ++++- sqlite/effective.kk | 167 +++++++++++++++++++++++++++++ sqlite/sqlite-result.kk | 227 +++++++++++++++++++++------------------- 3 files changed, 309 insertions(+), 114 deletions(-) create mode 100644 sqlite/effective.kk diff --git a/sqlite/capi.kk b/sqlite/capi.kk index 098a441..01776aa 100644 --- a/sqlite/capi.kk +++ b/sqlite/capi.kk @@ -15,6 +15,26 @@ extern make-sqlite-ok(): int32 c inline "SQLITE_OK" pub val int32/sqlite-ok: int32 = make-sqlite-ok() +extern make-sqlite-open-readonly(): int32 { c inline "SQLITE_OPEN_READONLY" } +extern make-sqlite-open-readwrite(): int32 { c inline "SQLITE_OPEN_READWRITE" } +extern make-sqlite-open-readwritecreate(): int32 { c inline "SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE" } +pub val int32/open-readonly = make-sqlite-open-readonly() +pub val int32/open-readwrite = make-sqlite-open-readwrite() +pub val int32/open-readwritecreate = make-sqlite-open-readwritecreate() + +extern make-sqlite-open-uri(): int32 { c inline "SQLITE_OPEN_URI" } +extern make-sqlite-open-sharedcache(): int32 { c inline "SQLITE_OPEN_SHAREDCACHE" } +extern make-sqlite-open-memory(): int32 { c inline "SQLITE_OPEN_MEMORY" } +extern make-sqlite-open-exrescode(): int32 { c inline "SQLITE_OPEN_EXRESCODE" } +extern make-sqlite-open-nomutex(): int32 { c inline "SQLITE_OPEN_NOMUTEX" } +extern make-sqlite-open-fullmutex(): int32 { c inline "SQLITE_OPEN_FULLMUTEX" } +pub val int32/open-uri = make-sqlite-open-uri() +pub val int32/open-sharedcache = make-sqlite-open-sharedcache() +pub val int32/open-memory = make-sqlite-open-memory() +pub val int32/open-exrescode = make-sqlite-open-exrescode() +pub val int32/open-nomutex = make-sqlite-open-nomutex() +pub val int32/open-fullmutex = make-sqlite-open-fullmutex() + pub extern initialize(): int32 c inline "sqlite3_initialize()" @@ -53,15 +73,16 @@ pub extern column-count(^stmt: any): int pub extern column-type(^stmt: any, col: int32): int32 c "kk_sqlite3_column_type" + extern make-sqlite-integer(): int32 { c inline "SQLITE_INTEGER" } -pub val int32/integer = make-sqlite-integer() extern make-sqlite-float(): int32 { c inline "SQLITE_FLOAT" } -pub val int32/float = make-sqlite-float() extern make-sqlite-blob(): int32 { c inline "SQLITE_BLOB" } -pub val int32/blob = make-sqlite-blob() extern make-sqlite-null(): int32 { c inline "SQLITE_NULL" } -pub val int32/null = make-sqlite-null() extern make-sqlite-text(): int32 { c inline "SQLITE_TEXT" } +pub val int32/integer = make-sqlite-integer() +pub val int32/float = make-sqlite-float() +pub val int32/blob = make-sqlite-blob() +pub val int32/null = make-sqlite-null() pub val int32/text = make-sqlite-text() //TODO(zephyr): waiting on a proper bytes type in koka diff --git a/sqlite/effective.kk b/sqlite/effective.kk new file mode 100644 index 0000000..86decb8 --- /dev/null +++ b/sqlite/effective.kk @@ -0,0 +1,167 @@ +// High-level SQLite3 API. +// +// This module uses effects to enforce safety invariants; +// it is not possible to retain a statement beyond the lifetime of a database. +module sqlite/effective + +import std/core/undiv +import std/num/int32 +import std/num/int64 +import sqlite/capi +import sqlite/sqlite-result + +// Throw an `:ExnSqlite3` if a result code is not `SQLITE_OK` or `SQLITE_OK_LOAD_PERMANENTLY`. +// `:r` must be a result code produced by SQLite3 and must not be `SQLITE_ROW` or `SQLITE_DONE`. +fun trusted-check-ok(r: int32, msg: string, ?kk-file-line: string): exn () + // Explicitly check for SQLITE_OK first on the assumption that the single + // i32 comparison for the common case is cheaper than the match to convert to + // the Koka type. + if r != capi/int32/sqlite-ok then + val r' = try(fn() r.sqlite-result, fn(exn) impossible(exn.message)) + match r' + Error-Code(code) -> throw(msg, ExnSqlite3(code)) + Ok -> impossible("got ok after explicit check for ok") + Row -> impossible("SQLITE_ROW") + Done -> impossible("SQLITE_DONE") + // Ok-Load-Permanently is an uncommon case, but it's still a variety of ok. + Ok-Load-Permanently -> () + +named scoped effect database + fun prepare-stmt(sql: sslice, ?persistent: bool, ?disable-vtab: bool, ?disable-log: bool): (any, sslice) + +fun with-db(name: db-name, base-flags: int32, sync-mode: sync-mode, vfs: string, action: forall (ev>) -> ,exn|e> a): a + val flags = match sync-mode + Single-Thread -> base-flags.or(open-exrescode) + Multi-Thread -> base-flags.or(open-exrescode).or(open-nomutex) + Serialized -> base-flags.or(open-exrescode).or(open-fullmutex) + val (conn, r) = match name + Filename(path) -> capi/open-v2(path, flags, vfs) + URI(uri) -> capi/open-v2(uri, flags.or(open-uri), vfs) + Memory-Shared(name) -> capi/open-v2("file:" ++ name ++ "?mode=memory&cache=shared", flags.or(open-uri).or(open-sharedcache).or(open-memory), vfs) + Memory -> capi/open-v2(":memory:", flags.or(open-memory), vfs) + r.trusted-check-ok("opening database") + with finally + capi/close(conn).trusted-check-ok("closing database") + with db <- named handler + fun prepare-stmt(sql: sslice, persistent: bool, disable-vtab: bool, disable-log: bool) + val flags' = 0.i32 + .or(persistent.int32 * 0x01.i32) + .or(disable-vtab.int32 * 0x04.i32) + .or(disable-log.int32 * 0x10.i32) + val (stmt, rest, r') = capi/prepare-v3(conn, sql, flags') + if r' != capi/int32/sqlite-ok then + val rr = try(fn() r'.sqlite-result, fn(exn) impossible(exn.message)) + match rr + Error-Code(code) -> throw("preparing statement", ExnPrepare(code, sql, capi/error-offset(conn))) + t -> impossible(t.show) + (stmt, rest) + action(db) + +// Modes that can be used to name a database to be opened. +pub type db-name + Filename(path: string) + URI(uri: string) + Memory-Shared(name: string) // in-memory database with shared cache + Memory + +// Synchronization modes in which to open a database. +pub type sync-mode + Single-Thread // No concurrent use is safe. + Multi-Thread // The database is thread-safe, but connections are not. + Serialized // The database and connections are always thread-safe. + +// Open or create a database in read-write mode. +// +// Once `:action` returns, the database is closed automatically. +// Exceptions are thrown on error when opening or closing the database. +pub fun open(name: db-name, sync-mode: sync-mode = Serialized, vfs: string = "", action: forall (ev>) -> ,exn|e> a): a + with-db(name, capi/int32/open-readwritecreate, sync-mode, vfs, action) + +// Open an existing database in read-write mode. +// +// Once `:action` returns, the database is closed automatically. +// Exceptions are thrown on error when opening or closing the database. +pub fun open-existing(name: db-name, sync-mode: sync-mode = Serialized, vfs: string = "", action: forall (ev>) -> ,exn|e> a): a + with-db(name, capi/int32/open-readwrite, sync-mode, vfs, action) + +// Open a database in read-only mode. +// +// Once `:action` returns, the database is closed automatically. +// Exceptions are thrown on error when opening or closing the databse. +pub fun read-only(name: db-name, sync-mode: sync-mode = Serialized, vfs: string = "", action: forall (ev>) -> ,exn|e> a): a + with-db(name, capi/int32/open-readonly, sync-mode, vfs, action) + +named scoped effect statement in database + fun stmt-reset(): () + fun bind(bind: list, bind-named: list<(string, sql-value)>): () + fun stmt-step(): int32 + +// Create a prepared statement. +// +// Once `:action` returns, the statement is finalized automatically, +// which can cause an exception to be thrown. +pub fun prepare(db: ev>, sql: sslice, persistent: bool = False, disable-vtab: bool = False, disable-log: bool = False, action: forall (ev>, sslice) -> ,statement,exn|e> a): ,exn|e> a + val (cref, rest) = db.prepare-stmt(sql) + with finally + capi/finalize(cref).trusted-check-ok("finalizing statement") + with stmt <- named handler + fun stmt-reset() capi/reset(cref).trusted-check-ok("resetting statement") + fun bind(bind, bind-named) cref.set-params + fun stmt-step() capi/step(cref) + action(stmt, rest) + +pub type sql-value + // Blob(x: bytes) + Float(x: float64) + Integer(x: int64) + Text(x: string) + Null + +fun set-params(stmt: any, ?bind: list, ?bind-named: list<(string, sql-value)>): exn () + bind.foreach-indexed() fn(i, v) + val r = match v + Float(x) -> capi/bind-double(stmt, int32(i+1), x) + Integer(x) -> capi/bind-int64(stmt, int32(i+1), x) + Text(x) -> capi/bind-text(stmt, int32(i+1), x) + Null -> capi/bind-null(stmt, int32(i+1)) + r.trusted-check-ok("binding parameter " ++ show(i+1)) // TODO(zephyr): lazy? + bind-named.foreach() fn((name, v)) + val i = capi/parameter-index(stmt, name) + if i == 0.i32 then throw("no parameter named " ++ name.show, ExnRange) + val r = match v + Float(x) -> capi/bind-double(stmt, i, x) + Integer(x) -> capi/bind-int64(stmt, i, x) + Text(x) -> capi/bind-text(stmt, i, x) + Null -> capi/bind-null(stmt, i) + r.trusted-check-ok("binding parameter " ++ name.show) + +named scoped effect row in statement + fun num-column(): int + fun column(col: int): sql-value + // fun blob(col: int): bytes + fun float64(col: int): float64 + fun int(col: int): int + fun text(col: int): string + fun null(col: int): bool + +fun with-row(stmt: any, action: forall (ev>) -> |e> a): e a + with r <- named handler + fun num-column() + capi/column-count(stmt) + fun column(col) + val t = capi/column-type(stmt, col.int32) + if t == capi/int32/integer then Integer(capi/column-integer(stmt, col.int32).int64) + else if t == capi/int32/float then Float(capi/column-double(stmt, col.int32)) + // else if t == capi/int32/blob then Blob(capi/column-blob(stmt, col.int32)) + else if t == capi/int32/text then Text(capi/column-text(stmt, col.int32)) + // If the DB gives us something unimaginable, pretend it's nothing. + else Null + fun float64(col) + capi/column-double(stmt, col.int32) + fun int(col) + capi/column-integer(stmt, col.int32) + fun text(col) + capi/column-text(stmt, col.int32) + fun null(col) + capi/column-type(stmt, col.int32) == capi/int32/null + action(r) diff --git a/sqlite/sqlite-result.kk b/sqlite/sqlite-result.kk index 79029b0..1f2f01a 100644 --- a/sqlite/sqlite-result.kk +++ b/sqlite/sqlite-result.kk @@ -7,7 +7,7 @@ pub type sqlite-result Ok Row Done - Error-Code(code: sqlite-error, pos: int) + Error-Code(code: sqlite-error) Ok-Load-Permanently // Error result codes. @@ -205,7 +205,7 @@ pub fun int32(r: sqlite-result): int32 Ok -> 0.int32 Row -> 100.int32 Done -> 101.int32 - Error-Code(err, _) -> match err + Error-Code(err) -> match err Error(Nothing) -> 1.int32 Error(Just(Error-Missing-Coll-Seq)) -> 257.int32 Error(Just(Error-Retry)) -> 513.int32 @@ -305,105 +305,105 @@ pub fun int32(r: sqlite-result): int32 // Get the SQLite result corresponding to a result code from the C API. // Throws an exception if `r` does not correspond to a valid result. -pub fun int32/sqlite-result(r: int32, pos: int = -1): exn sqlite-result +pub fun int32/sqlite-result(r: int32): exn sqlite-result match r.int 0 -> Ok 256 -> Ok-Load-Permanently - 1 -> Error-Code(Error(Nothing), pos) - 257 -> Error-Code(Error(Just(Error-Missing-Coll-Seq)), pos) - 513 -> Error-Code(Error(Just(Error-Retry)), pos) - 769 -> Error-Code(Error(Just(Error-Snapshot)), pos) - 2 -> Error-Code(Internal(Nothing), pos) - 3 -> Error-Code(Perm(Nothing), pos) - 4 -> Error-Code(Abort(Nothing), pos) - 516 -> Error-Code(Abort(Just(Abort-Rollback)), pos) - 5 -> Error-Code(Busy(Nothing), pos) - 261 -> Error-Code(Busy(Just(Busy-Recovery)), pos) - 517 -> Error-Code(Busy(Just(Busy-Snapshot)), pos) - 773 -> Error-Code(Busy(Just(Busy-Timeout)), pos) - 6 -> Error-Code(Locked(Nothing), pos) - 262 -> Error-Code(Locked(Just(Locked-Shared-Cache)), pos) - 518 -> Error-Code(Locked(Just(Locked-VTab)), pos) - 7 -> Error-Code(No-Mem(Nothing), pos) - 8 -> Error-Code(Read-Only(Nothing), pos) - 264 -> Error-Code(Read-Only(Just(Read-Only-Recovery)), pos) - 520 -> Error-Code(Read-Only(Just(Read-Only-Cant-Lock)), pos) - 776 -> Error-Code(Read-Only(Just(Read-Only-Rollback)), pos) - 1032 -> Error-Code(Read-Only(Just(Read-Only-DB-Moved)), pos) - 1288 -> Error-Code(Read-Only(Just(Read-Only-Cant-Init)), pos) - 1544 -> Error-Code(Read-Only(Just(Read-Only-Directory)), pos) - 9 -> Error-Code(Interrupt(Nothing), pos) - 10 -> Error-Code(IO-Err(Nothing), pos) - 266 -> Error-Code(IO-Err(Just(IO-Err-Read)), pos) - 522 -> Error-Code(IO-Err(Just(IO-Err-Short-Read)), pos) - 778 -> Error-Code(IO-Err(Just(IO-Err-Write)), pos) - 1034 -> Error-Code(IO-Err(Just(IO-Err-Fsync)), pos) - 1290 -> Error-Code(IO-Err(Just(IO-Err-Dir-Fsync)), pos) - 1546 -> Error-Code(IO-Err(Just(IO-Err-Truncate)), pos) - 1802 -> Error-Code(IO-Err(Just(IO-Err-Fstat)), pos) - 2058 -> Error-Code(IO-Err(Just(IO-Err-Unlock)), pos) - 2314 -> Error-Code(IO-Err(Just(IO-Err-Read-Lock)), pos) - 2570 -> Error-Code(IO-Err(Just(IO-Err-Delete)), pos) - 3082 -> Error-Code(IO-Err(Just(IO-Err-No-Mem)), pos) - 3338 -> Error-Code(IO-Err(Just(IO-Err-Access)), pos) - 3594 -> Error-Code(IO-Err(Just(IO-Err-Check-Reserved-Lock)), pos) - 3850 -> Error-Code(IO-Err(Just(IO-Err-Lock)), pos) - 4106 -> Error-Code(IO-Err(Just(IO-Err-Close)), pos) - 4618 -> Error-Code(IO-Err(Just(IO-Err-Shm-Open)), pos) - 4874 -> Error-Code(IO-Err(Just(IO-Err-Shm-Size)), pos) - 5386 -> Error-Code(IO-Err(Just(IO-Err-Shm-Map)), pos) - 5642 -> Error-Code(IO-Err(Just(IO-Err-Seek)), pos) - 5898 -> Error-Code(IO-Err(Just(IO-Err-Delete-No-Ent)), pos) - 6154 -> Error-Code(IO-Err(Just(IO-Err-Mmap)), pos) - 6410 -> Error-Code(IO-Err(Just(IO-Err-Get-Temp-Path)), pos) - 6666 -> Error-Code(IO-Err(Just(IO-Err-Convpath)), pos) - 6922 -> Error-Code(IO-Err(Just(IO-Err-Vnode)), pos) - 7178 -> Error-Code(IO-Err(Just(IO-Err-Auth)), pos) - 7434 -> Error-Code(IO-Err(Just(IO-Err-Begin-Atomic)), pos) - 7690 -> Error-Code(IO-Err(Just(IO-Err-Commit-Atomic)), pos) - 7946 -> Error-Code(IO-Err(Just(IO-Err-Rollback-Atomic)), pos) - 8202 -> Error-Code(IO-Err(Just(IO-Err-Data)), pos) - 8458 -> Error-Code(IO-Err(Just(IO-Err-Corrupt-FS)), pos) - 11 -> Error-Code(Corrupt(Nothing), pos) - 267 -> Error-Code(Corrupt(Just(Corrupt-Vtab)), pos) - 523 -> Error-Code(Corrupt(Just(Corrupt-Sequence)), pos) - 779 -> Error-Code(Corrupt(Just(Corrupt-Index)), pos) - 12 -> Error-Code(Not-Found(Nothing), pos) - 13 -> Error-Code(Full(Nothing), pos) - 14 -> Error-Code(Cant-Open(Nothing), pos) - 526 -> Error-Code(Cant-Open(Just(Cant-Open-Isdir)), pos) - 782 -> Error-Code(Cant-Open(Just(Cant-Open-Fullpath)), pos) - 1038 -> Error-Code(Cant-Open(Just(Cant-Open-Convpath)), pos) - 1294 -> Error-Code(Cant-Open(Just(Cant-Open-DirtyWAL)), pos) - 1550 -> Error-Code(Cant-Open(Just(Cant-Open-Symlink)), pos) - 15 -> Error-Code(Protocol(Nothing), pos) - 17 -> Error-Code(Schema(Nothing), pos) - 18 -> Error-Code(Too-Big(Nothing), pos) - 19 -> Error-Code(Constraint(Nothing), pos) - 275 -> Error-Code(Constraint(Just(Constraint-Check)), pos) - 531 -> Error-Code(Constraint(Just(Constraint-Commithook)), pos) - 787 -> Error-Code(Constraint(Just(Constraint-Foreignkey)), pos) - 1043 -> Error-Code(Constraint(Just(Constraint-Function)), pos) - 1299 -> Error-Code(Constraint(Just(Constraint-Notnull)), pos) - 1555 -> Error-Code(Constraint(Just(Constraint-Primarykey)), pos) - 1811 -> Error-Code(Constraint(Just(Constraint-Trigger)), pos) - 2067 -> Error-Code(Constraint(Just(Constraint-Unique)), pos) - 2323 -> Error-Code(Constraint(Just(Constraint-VTab)), pos) - 2579 -> Error-Code(Constraint(Just(Constraint-Rowid)), pos) - 2835 -> Error-Code(Constraint(Just(Constraint-Pinned)), pos) - 3091 -> Error-Code(Constraint(Just(Constraint-Datatype)), pos) - 20 -> Error-Code(Mismatch(Nothing), pos) - 21 -> Error-Code(Misuse(Nothing), pos) - 21 -> Error-Code(No-LFS(Nothing), pos) - 23 -> Error-Code(Auth(Nothing), pos) - 279 -> Error-Code(Auth(Just(Auth-User)), pos) - 25 -> Error-Code(Range(Nothing), pos) - 26 -> Error-Code(Not-A-DB(Nothing), pos) - 27 -> Error-Code(Notice(Nothing), pos) - 283 -> Error-Code(Notice(Just(Notice-Recover-WAL)), pos) - 539 -> Error-Code(Notice(Just(Notice-Recover-Rollback)), pos) - 28 -> Error-Code(Warning(Nothing), pos) - 284 -> Error-Code(Warning(Just(Warning-Autoindex)), pos) + 1 -> Error-Code(Error(Nothing)) + 257 -> Error-Code(Error(Just(Error-Missing-Coll-Seq))) + 513 -> Error-Code(Error(Just(Error-Retry))) + 769 -> Error-Code(Error(Just(Error-Snapshot))) + 2 -> Error-Code(Internal(Nothing)) + 3 -> Error-Code(Perm(Nothing)) + 4 -> Error-Code(Abort(Nothing)) + 516 -> Error-Code(Abort(Just(Abort-Rollback))) + 5 -> Error-Code(Busy(Nothing)) + 261 -> Error-Code(Busy(Just(Busy-Recovery))) + 517 -> Error-Code(Busy(Just(Busy-Snapshot))) + 773 -> Error-Code(Busy(Just(Busy-Timeout))) + 6 -> Error-Code(Locked(Nothing)) + 262 -> Error-Code(Locked(Just(Locked-Shared-Cache))) + 518 -> Error-Code(Locked(Just(Locked-VTab))) + 7 -> Error-Code(No-Mem(Nothing)) + 8 -> Error-Code(Read-Only(Nothing)) + 264 -> Error-Code(Read-Only(Just(Read-Only-Recovery))) + 520 -> Error-Code(Read-Only(Just(Read-Only-Cant-Lock))) + 776 -> Error-Code(Read-Only(Just(Read-Only-Rollback))) + 1032 -> Error-Code(Read-Only(Just(Read-Only-DB-Moved))) + 1288 -> Error-Code(Read-Only(Just(Read-Only-Cant-Init))) + 1544 -> Error-Code(Read-Only(Just(Read-Only-Directory))) + 9 -> Error-Code(Interrupt(Nothing)) + 10 -> Error-Code(IO-Err(Nothing)) + 266 -> Error-Code(IO-Err(Just(IO-Err-Read))) + 522 -> Error-Code(IO-Err(Just(IO-Err-Short-Read))) + 778 -> Error-Code(IO-Err(Just(IO-Err-Write))) + 1034 -> Error-Code(IO-Err(Just(IO-Err-Fsync))) + 1290 -> Error-Code(IO-Err(Just(IO-Err-Dir-Fsync))) + 1546 -> Error-Code(IO-Err(Just(IO-Err-Truncate))) + 1802 -> Error-Code(IO-Err(Just(IO-Err-Fstat))) + 2058 -> Error-Code(IO-Err(Just(IO-Err-Unlock))) + 2314 -> Error-Code(IO-Err(Just(IO-Err-Read-Lock))) + 2570 -> Error-Code(IO-Err(Just(IO-Err-Delete))) + 3082 -> Error-Code(IO-Err(Just(IO-Err-No-Mem))) + 3338 -> Error-Code(IO-Err(Just(IO-Err-Access))) + 3594 -> Error-Code(IO-Err(Just(IO-Err-Check-Reserved-Lock))) + 3850 -> Error-Code(IO-Err(Just(IO-Err-Lock))) + 4106 -> Error-Code(IO-Err(Just(IO-Err-Close))) + 4618 -> Error-Code(IO-Err(Just(IO-Err-Shm-Open))) + 4874 -> Error-Code(IO-Err(Just(IO-Err-Shm-Size))) + 5386 -> Error-Code(IO-Err(Just(IO-Err-Shm-Map))) + 5642 -> Error-Code(IO-Err(Just(IO-Err-Seek))) + 5898 -> Error-Code(IO-Err(Just(IO-Err-Delete-No-Ent))) + 6154 -> Error-Code(IO-Err(Just(IO-Err-Mmap))) + 6410 -> Error-Code(IO-Err(Just(IO-Err-Get-Temp-Path))) + 6666 -> Error-Code(IO-Err(Just(IO-Err-Convpath))) + 6922 -> Error-Code(IO-Err(Just(IO-Err-Vnode))) + 7178 -> Error-Code(IO-Err(Just(IO-Err-Auth))) + 7434 -> Error-Code(IO-Err(Just(IO-Err-Begin-Atomic))) + 7690 -> Error-Code(IO-Err(Just(IO-Err-Commit-Atomic))) + 7946 -> Error-Code(IO-Err(Just(IO-Err-Rollback-Atomic))) + 8202 -> Error-Code(IO-Err(Just(IO-Err-Data))) + 8458 -> Error-Code(IO-Err(Just(IO-Err-Corrupt-FS))) + 11 -> Error-Code(Corrupt(Nothing)) + 267 -> Error-Code(Corrupt(Just(Corrupt-Vtab))) + 523 -> Error-Code(Corrupt(Just(Corrupt-Sequence))) + 779 -> Error-Code(Corrupt(Just(Corrupt-Index))) + 12 -> Error-Code(Not-Found(Nothing)) + 13 -> Error-Code(Full(Nothing)) + 14 -> Error-Code(Cant-Open(Nothing)) + 526 -> Error-Code(Cant-Open(Just(Cant-Open-Isdir))) + 782 -> Error-Code(Cant-Open(Just(Cant-Open-Fullpath))) + 1038 -> Error-Code(Cant-Open(Just(Cant-Open-Convpath))) + 1294 -> Error-Code(Cant-Open(Just(Cant-Open-DirtyWAL))) + 1550 -> Error-Code(Cant-Open(Just(Cant-Open-Symlink))) + 15 -> Error-Code(Protocol(Nothing)) + 17 -> Error-Code(Schema(Nothing)) + 18 -> Error-Code(Too-Big(Nothing)) + 19 -> Error-Code(Constraint(Nothing)) + 275 -> Error-Code(Constraint(Just(Constraint-Check))) + 531 -> Error-Code(Constraint(Just(Constraint-Commithook))) + 787 -> Error-Code(Constraint(Just(Constraint-Foreignkey))) + 1043 -> Error-Code(Constraint(Just(Constraint-Function))) + 1299 -> Error-Code(Constraint(Just(Constraint-Notnull))) + 1555 -> Error-Code(Constraint(Just(Constraint-Primarykey))) + 1811 -> Error-Code(Constraint(Just(Constraint-Trigger))) + 2067 -> Error-Code(Constraint(Just(Constraint-Unique))) + 2323 -> Error-Code(Constraint(Just(Constraint-VTab))) + 2579 -> Error-Code(Constraint(Just(Constraint-Rowid))) + 2835 -> Error-Code(Constraint(Just(Constraint-Pinned))) + 3091 -> Error-Code(Constraint(Just(Constraint-Datatype))) + 20 -> Error-Code(Mismatch(Nothing)) + 21 -> Error-Code(Misuse(Nothing)) + 21 -> Error-Code(No-LFS(Nothing)) + 23 -> Error-Code(Auth(Nothing)) + 279 -> Error-Code(Auth(Just(Auth-User))) + 25 -> Error-Code(Range(Nothing)) + 26 -> Error-Code(Not-A-DB(Nothing)) + 27 -> Error-Code(Notice(Nothing)) + 283 -> Error-Code(Notice(Just(Notice-Recover-WAL))) + 539 -> Error-Code(Notice(Just(Notice-Recover-Rollback))) + 28 -> Error-Code(Warning(Nothing)) + 284 -> Error-Code(Warning(Just(Warning-Autoindex))) 100 -> Row 101 -> Done v -> throw("invalid SQLite3 result code " ++ v.show, ExnRange) @@ -412,21 +412,28 @@ pub fun int32/sqlite-result(r: int32, pos: int = -1): exn sqlite-result // E.g., `Constraint(Just(Constraint-Unique))` is converted to `Constraint(Nothing)`. pub fun primary(r: sqlite-result): sqlite-result match r - Error-Code(Error(Just(_)), pos) -> Error-Code(Error(Nothing), pos) - Error-Code(Abort(Just(_)), pos) -> Error-Code(Abort(Nothing), pos) - Error-Code(Busy(Just(_)), pos) -> Error-Code(Busy(Nothing), pos) - Error-Code(Locked(Just(_)), pos) -> Error-Code(Locked(Nothing), pos) - Error-Code(Read-Only(Just(_)), pos) -> Error-Code(Read-Only(Nothing), pos) - Error-Code(IO-Err(Just(_)), pos) -> Error-Code(IO-Err(Nothing), pos) - Error-Code(Corrupt(Just(_)), pos) -> Error-Code(Corrupt(Nothing), pos) - Error-Code(Cant-Open(Just(_)), pos) -> Error-Code(Cant-Open(Nothing), pos) - Error-Code(Constraint(Just(_)), pos) -> Error-Code(Constraint(Nothing), pos) - Error-Code(Auth(Just(_)), pos) -> Error-Code(Auth(Nothing), pos) - Error-Code(Notice(Just(_)), pos) -> Error-Code(Notice(Nothing), pos) - Error-Code(Warning(Just(_)), pos) -> Error-Code(Warning(Nothing), pos) + Error-Code(Error(Just(_))) -> Error-Code(Error(Nothing)) + Error-Code(Abort(Just(_))) -> Error-Code(Abort(Nothing)) + Error-Code(Busy(Just(_))) -> Error-Code(Busy(Nothing)) + Error-Code(Locked(Just(_))) -> Error-Code(Locked(Nothing)) + Error-Code(Read-Only(Just(_))) -> Error-Code(Read-Only(Nothing)) + Error-Code(IO-Err(Just(_))) -> Error-Code(IO-Err(Nothing)) + Error-Code(Corrupt(Just(_))) -> Error-Code(Corrupt(Nothing)) + Error-Code(Cant-Open(Just(_))) -> Error-Code(Cant-Open(Nothing)) + Error-Code(Constraint(Just(_))) -> Error-Code(Constraint(Nothing)) + Error-Code(Auth(Just(_))) -> Error-Code(Auth(Nothing)) + Error-Code(Notice(Just(_))) -> Error-Code(Notice(Nothing)) + Error-Code(Warning(Just(_))) -> Error-Code(Warning(Nothing)) Ok-Load-Permanently -> Ok r -> r // Get the message corresponding to a result code. pub fun show(r: sqlite-result): string capi/errstr(r.int32) + +pub extend type exception-info + // Generic exceptions from SQLite3. + ExnSqlite3(code: sqlite-error) + // Exceptions from preparing SQL statements. + // The error may not be due to the SQL text, and therefore the `:pos` may be `-1`. + ExnPrepare(code: sqlite-error, stmt: sslice, pos: int)