initial commit

This commit is contained in:
2026-02-25 22:15:35 -05:00
commit 738483f281
11 changed files with 280694 additions and 0 deletions

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.koka
.vscode
koka-test.sqlite3

27
README.md Normal file
View File

@@ -0,0 +1,27 @@
# sqlite
Koka binding for [SQLite3](https://www.sqlite.org/index.html).
This package uses the SQLite3 standard amalgamation, version 3.51.2.
The following compile-time options are enabled:
```
#define SQLITE_DQS 0
#define SQLITE_DEFAULT_MEMSTATUS 0
#define SQLITE_LIKE_DOESNT_MATCH_BLOBS
#define SQLITE_OMIT_AUTOINIT
#define SQLITE_STRICT_SUBTYPE 1
#define SQLITE_ENABLE_CARRAY
#define SQLITE_ENABLE_COLUMN_METADATA
#define SQLITE_ENABLE_DBSTAT_VTAB
#define SQLITE_ENABLE_FTS5
#define SQLITE_ENABLE_GEOPOLY
#define SQLITE_ENABLE_HIDDEN_COLUMNS
#define SQLITE_ENABLE_ICU
#define SQLITE_ENABLE_MATH_FUNCTIONS
#define SQLITE_ENABLE_NORMALIZE
#define SQLITE_ENABLE_PERCENTILE
#define SQLITE_ENABLE_RTREE
#define SQLITE_ENABLE_SESSION
#define SQLITE_SOUNDEX
```

49
sqlite/capi.kk Normal file
View File

@@ -0,0 +1,49 @@
// Minimal SQLite3 C API wrapper.
// This module is for internal use and should not be used directly.
module sqlite/capi
pub import std/num/int32
extern import
c file "sqlite-define.c"
extern import
c file "sqlite3.c"
extern import
c file "sqlite-inline.c"
extern make-sqlite-ok(): int32
c inline "SQLITE_OK"
pub val int32/sqlite-ok: int32 = make-sqlite-ok()
pub extern initialize(): int32
c inline "sqlite3_initialize()"
pub extern shutdown(): int32
c inline "sqlite3_shutdown()"
pub extern errstr(x: int32): string
c "kk_sqlite3_errstr"
pub extern extended-errcode(^db: any): int32
c "kk_sqlite3_extended_errcode"
pub extern error-offset(^db: any): int
c "kk_sqlite3_error_offset"
pub extern open-v2(^filename: string, flags: int32, ^vfs: string): (any, int32)
c "kk_sqlite3_open_v2"
pub extern close(^db: any): int32
c "kk_sqlite3_close"
pub extern prepare-v3(^db: any, ^sql: sslice, flags: int32): (any, sslice, int32)
c "kk_sqlite3_prepare_v3"
pub extern finalize(^stmt: any): int32
c "kk_sqlite3_finalize"
pub extern step(^stmt: any): int32
c "kk_sqlite3_step"
pub extern reset(^stmt: any): int32
c "kk_sqlite3_reset"

20
sqlite/sqlite-define.c Normal file
View File

@@ -0,0 +1,20 @@
// Rather than requiring every importer to manage defines,
// set some here and include the SQLite3 amalgamation.
#define SQLITE_DQS 0
#define SQLITE_DEFAULT_MEMSTATUS 0
#define SQLITE_LIKE_DOESNT_MATCH_BLOBS
#define SQLITE_OMIT_AUTOINIT
#define SQLITE_STRICT_SUBTYPE 1
#define SQLITE_ENABLE_CARRAY
#define SQLITE_ENABLE_COLUMN_METADATA
#define SQLITE_ENABLE_DBSTAT_VTAB
#define SQLITE_ENABLE_FTS5
#define SQLITE_ENABLE_GEOPOLY
// #define SQLITE_ENABLE_HIDDEN_COLUMNS
// #define SQLITE_ENABLE_ICU
#define SQLITE_ENABLE_MATH_FUNCTIONS
#define SQLITE_ENABLE_NORMALIZE
#define SQLITE_ENABLE_PERCENTILE
#define SQLITE_ENABLE_RTREE
#define SQLITE_ENABLE_SESSION
#define SQLITE_SOUNDEX

83
sqlite/sqlite-inline.c Normal file
View File

@@ -0,0 +1,83 @@
#include <kklib.h>
#include <sqlite3.h>
static inline sqlite3 *borrow_db(kk_box_t cref, kk_context_t *_ctx) {
return (sqlite3 *)kk_cptr_unbox_borrowed(cref, _ctx);
}
kk_string_t kk_sqlite3_errstr(int32_t x, kk_context_t *_ctx) {
const char *s = sqlite3_errstr((int)x);
if (s == NULL) {
return kk_string_empty();
}
return kk_string_alloc_from_utf8(s, _ctx);
}
int32_t kk_sqlite3_extended_errcode(kk_box_t cref, kk_context_t *_ctx) {
sqlite3 *db = borrow_db(cref, _ctx);
return (int32_t)sqlite3_extended_errcode(db);
}
kk_integer_t kk_sqlite3_error_offset(kk_box_t cref, kk_context_t *_ctx) {
sqlite3 *db = borrow_db(cref, _ctx);
int r = sqlite3_error_offset(db);
return kk_integer_from_int(r, _ctx);
}
kk_std_core_types__tuple2 kk_sqlite3_open_v2(kk_string_t filename, int32_t flags, kk_string_t vfs, kk_context_t *_ctx) {
kk_ssize_t vfs_len = 0;
const char *name = kk_string_cbuf_borrow(filename, NULL, _ctx);
const char *v = kk_string_cbuf_borrow(vfs, &vfs_len, _ctx);
sqlite3 *db = NULL;
int r = sqlite3_open_v2(name, &db, (int)flags, vfs_len != 0 ? v : NULL);
return kk_std_core_types__new_Tuple2(kk_box_box(kk_cptr_box(db, _ctx), _ctx), kk_int32_box((int32_t)r, _ctx), _ctx);
}
int32_t kk_sqlite3_close(kk_box_t cref, kk_context_t *_ctx) {
sqlite3 *db = borrow_db(cref, _ctx);
return (int32_t)sqlite3_close(db);
}
kk_std_core_types__tuple3 kk_sqlite3_prepare_v3(kk_box_t cref, kk_std_core_sslice__sslice sql, int32_t flags, kk_context_t *_ctx) {
kk_box_t fst, snd, thd;
sqlite3 *db = borrow_db(cref, _ctx);
kk_intx_t sql_start = kk_integer_clamp_borrow(sql.start, _ctx);
kk_intx_t sql_len = kk_integer_clamp_borrow(sql.len, _ctx);
// TODO(zephyr): instead of borrowing and then duping to construct left,
// check whether we can replace the reference
const char *sql_bytes = kk_string_cbuf_borrow(sql.str, NULL, _ctx) + sql_start;
sqlite3_stmt *stmt = NULL;
const char *left_bytes = NULL;
int r = sqlite3_prepare_v3(db, sql_bytes, (int)sql_len, flags, &stmt, &left_bytes);
kk_std_core_sslice__sslice left = {
.str = kk_string_dup(sql.str, _ctx),
.start = kk_integer_from_int(sql_start + (left_bytes - sql_bytes), _ctx),
.len = kk_integer_from_int(sql_len - (left_bytes - sql_bytes), _ctx),
};
fst = kk_box_box(kk_cptr_box(stmt, _ctx), _ctx);
snd = kk_std_core_sslice__sslice_box(left, _ctx);
thd = kk_int32_box((int32_t)r, _ctx);
return kk_std_core_types__new_Tuple3(fst, snd, thd, _ctx);
}
static inline sqlite3_stmt *borrow_stmt(kk_box_t cref, kk_context_t *_ctx) {
return (sqlite3_stmt *)kk_cptr_unbox_borrowed(cref, _ctx);
}
int32_t kk_sqlite3_finalize(kk_box_t cref, kk_context_t *_ctx) {
sqlite3_stmt *stmt = borrow_stmt(cref, _ctx);
return (int32_t)sqlite3_finalize(stmt);
}
int32_t kk_sqlite3_step(kk_box_t cref, kk_context_t *_ctx) {
sqlite3_stmt *stmt = borrow_stmt(cref, _ctx);
return (int32_t)sqlite3_step(stmt);
}
int32_t kk_sqlite3_reset(kk_box_t cref, kk_context_t *_ctx) {
sqlite3_stmt *stmt = borrow_stmt(cref, _ctx);
return (int32_t)sqlite3_reset(stmt);
}

432
sqlite/sqlite-result.kk Normal file
View File

@@ -0,0 +1,432 @@
module sqlite/sqlite-result
import sqlite/capi
// Result codes returned from SQLite3 C API routines.
pub type sqlite-result
Ok
Row
Done
Error-Code(code: sqlite-error, pos: int)
Ok-Load-Permanently
// Error result codes.
//
// The base variants correspond to the primary result codes.
// With the exception of `SQLITE_OK_LOAD_PERMANENTLY`, extended result codes
// are represented by an additional argument to the primary result code's constructor.
// All error codes carry the field for such arguments, even if there is no
// extended result code associated with it, so that wildcard pattern matches
// do not need to update if new extended result codes are added in the future.
pub type sqlite-error
Error(ext: maybe<ext-error>)
Internal(ext: maybe<ext-internal>)
Perm(ext: maybe<ext-perm>)
Abort(ext: maybe<ext-abort>)
Busy(ext: maybe<ext-busy>)
Locked(ext: maybe<ext-locked>)
No-Mem(ext: maybe<ext-no-mem>)
Read-Only(ext: maybe<ext-read-only>)
Interrupt(ext: maybe<ext-interrupt>)
IO-Err(ext: maybe<ext-io-err>)
Corrupt(ext: maybe<ext-corrupt>)
Not-Found(ext: maybe<ext-not-found>)
Full(ext: maybe<ext-full>)
Cant-Open(ext: maybe<ext-cant-open>)
Protocol(ext: maybe<ext-protocol>)
Schema(ext: maybe<ext-schema>)
Too-Big(ext: maybe<ext-too-big>)
Constraint(ext: maybe<ext-constraint>)
Mismatch(ext: maybe<ext-mismatch>)
Misuse(ext: maybe<ext-misuse>)
No-LFS(ext: maybe<ext-no-lfs>)
Auth(ext: maybe<ext-auth>)
Range(ext: maybe<ext-range>)
Not-A-DB(ext: maybe<ext-not-a-db>)
Notice(ext: maybe<ext-notice>)
Warning(ext: maybe<ext-warning>)
// Empty(ext: maybe<ext-empty>) // unused
// Format(ext: maybe<ext-format>) // unused
// Extended result codes for the `ERROR` primary result code.
pub type ext-error
Error-Missing-Coll-Seq
Error-Retry
Error-Snapshot
// Extended result codes for the `INTERNAL` primary result code.
pub type ext-internal
// Extended result codes for the `PERM` primary result code.
pub type ext-perm
// Extended result codes for the `ABORT` primary result code.
pub type ext-abort
Abort-Rollback
// Extended result codes for the `BUSY` primary result code.
pub type ext-busy
Busy-Recovery
Busy-Snapshot
Busy-Timeout
// Extended result codes for the `LOCKED` primary result code.
pub type ext-locked
Locked-Shared-Cache
Locked-VTab
// Extended result codes for the `NOMEM` primary result code.
pub type ext-no-mem
// Extended result codes for the `READONLY` primary result code.
pub type ext-read-only
Read-Only-Recovery
Read-Only-Cant-Lock
Read-Only-Rollback
Read-Only-DB-Moved
Read-Only-Cant-Init
Read-Only-Directory
// Extended result codes for the `INTERRUPT` primary result code.
pub type ext-interrupt
// Extended result codes for the `IOERR` primary result code.
pub type ext-io-err
IO-Err-Read
IO-Err-Short-Read
IO-Err-Write
IO-Err-Fsync
IO-Err-Dir-Fsync
IO-Err-Truncate
IO-Err-Fstat
IO-Err-Unlock
IO-Err-Read-Lock
IO-Err-Delete
IO-Err-No-Mem
IO-Err-Access
IO-Err-Check-Reserved-Lock
IO-Err-Lock
IO-Err-Close
IO-Err-Shm-Open
IO-Err-Shm-Size
IO-Err-Shm-Map
IO-Err-Seek
IO-Err-Delete-No-Ent
IO-Err-Mmap
IO-Err-Get-Temp-Path
IO-Err-Convpath
IO-Err-Vnode
IO-Err-Auth
IO-Err-Begin-Atomic
IO-Err-Commit-Atomic
IO-Err-Rollback-Atomic
IO-Err-Data
IO-Err-Corrupt-FS
// IO-Err-Blocked // unused
// IO-Err-Dir-Close // unused
// IO-Err-Shm-Lock // unused
// Extended result codes for the `CORRUPT` primary result code.
pub type ext-corrupt
Corrupt-Vtab
Corrupt-Sequence
Corrupt-Index
// Extended result codes for the `NOTFOUND` primary result code.
pub type ext-not-found
// Extended result codes for the `FULL` primary result code.
pub type ext-full
// Extended result codes for the `CANTOPEN` primary result code.
pub type ext-cant-open
Cant-Open-Isdir
Cant-Open-Fullpath
Cant-Open-Convpath
Cant-Open-DirtyWAL
Cant-Open-Symlink
// Cant-Open-Notempdir // unused
// Extended result codes for the `PROTOCOL` primary result code.
pub type ext-protocol
// Extended result codes for the `SCHEMA` primary result code.
pub type ext-schema
// Extended result codes for the `TOOBIG` primary result code.
pub type ext-too-big
// Extended result codes for the `CONSTRAINT` primary result code.
pub type ext-constraint
Constraint-Check
Constraint-Commithook
Constraint-Foreignkey
Constraint-Function
Constraint-Notnull
Constraint-Primarykey
Constraint-Trigger
Constraint-Unique
Constraint-VTab
Constraint-Rowid
Constraint-Pinned
Constraint-Datatype
// Extended result codes for the `MISMATCH` primary result code.
pub type ext-mismatch
// Extended result codes for the `MISUSE` primary result code.
pub type ext-misuse
// Extended result codes for the `NOLFS` primary result code.
pub type ext-no-lfs
// Extended result codes for the `AUTH` primary result code.
pub type ext-auth
Auth-User
// Extended result codes for the `RANGE` primary result code.
pub type ext-range
// Extended result codes for the `NOTADB` primary result code.
pub type ext-not-a-db
// Extended result codes for the `NOTICE` primary result code.
pub type ext-notice
Notice-Recover-WAL
Notice-Recover-Rollback
// Extended result codes for the `WARNING` primary result code.
pub type ext-warning
Warning-Autoindex
// Convert an SQLite3 result to its integer value.
pub fun int32(r: sqlite-result): int32
match r
Ok -> 0.int32
Row -> 100.int32
Done -> 101.int32
Error-Code(err, _) -> match err
Error(Nothing) -> 1.int32
Error(Just(Error-Missing-Coll-Seq)) -> 257.int32
Error(Just(Error-Retry)) -> 513.int32
Error(Just(Error-Snapshot)) -> 769.int32
Internal(_) -> 2.int32
Perm(_) -> 3.int32
Abort(Nothing) -> 4.int32
Abort(Just(Abort-Rollback)) -> 516.int32
Busy(Nothing) -> 5.int32
Busy(Just(Busy-Recovery)) -> 261.int32
Busy(Just(Busy-Snapshot)) -> 517.int32
Busy(Just(Busy-Timeout)) -> 773.int32
Locked(Nothing) -> 6.int32
Locked(Just(Locked-Shared-Cache)) -> 262.int32
Locked(Just(Locked-VTab)) -> 518.int32
No-Mem(_) -> 7.int32
Read-Only(Nothing) -> 8.int32
Read-Only(Just(Read-Only-Recovery)) -> 264.int32
Read-Only(Just(Read-Only-Cant-Lock)) -> 520.int32
Read-Only(Just(Read-Only-Rollback)) -> 776.int32
Read-Only(Just(Read-Only-DB-Moved)) -> 1032.int32
Read-Only(Just(Read-Only-Cant-Init)) -> 1288.int32
Read-Only(Just(Read-Only-Directory)) -> 1544.int32
Interrupt(_) -> 9.int32
IO-Err(Nothing) -> 10.int32
IO-Err(Just(IO-Err-Read)) -> 266.int32
IO-Err(Just(IO-Err-Short-Read)) -> 522.int32
IO-Err(Just(IO-Err-Write)) -> 778.int32
IO-Err(Just(IO-Err-Fsync)) -> 1034.int32
IO-Err(Just(IO-Err-Dir-Fsync)) -> 1290.int32
IO-Err(Just(IO-Err-Truncate)) -> 1546.int32
IO-Err(Just(IO-Err-Fstat)) -> 1802.int32
IO-Err(Just(IO-Err-Unlock)) -> 2058.int32
IO-Err(Just(IO-Err-Read-Lock)) -> 2314.int32
IO-Err(Just(IO-Err-Delete)) -> 2570.int32
IO-Err(Just(IO-Err-No-Mem)) -> 3082.int32
IO-Err(Just(IO-Err-Access)) -> 3338.int32
IO-Err(Just(IO-Err-Check-Reserved-Lock)) -> 3594.int32
IO-Err(Just(IO-Err-Lock)) -> 3850.int32
IO-Err(Just(IO-Err-Close)) -> 4106.int32
IO-Err(Just(IO-Err-Shm-Open)) -> 4618.int32
IO-Err(Just(IO-Err-Shm-Size)) -> 4874.int32
IO-Err(Just(IO-Err-Shm-Map)) -> 5386.int32
IO-Err(Just(IO-Err-Seek)) -> 5642.int32
IO-Err(Just(IO-Err-Delete-No-Ent)) -> 5898.int32
IO-Err(Just(IO-Err-Mmap)) -> 6154.int32
IO-Err(Just(IO-Err-Get-Temp-Path)) -> 6410.int32
IO-Err(Just(IO-Err-Convpath)) -> 6666.int32
IO-Err(Just(IO-Err-Vnode)) -> 6922.int32
IO-Err(Just(IO-Err-Auth)) -> 7178.int32
IO-Err(Just(IO-Err-Begin-Atomic)) -> 7434.int32
IO-Err(Just(IO-Err-Commit-Atomic)) -> 7690.int32
IO-Err(Just(IO-Err-Rollback-Atomic)) -> 7946.int32
IO-Err(Just(IO-Err-Data)) -> 8202.int32
IO-Err(Just(IO-Err-Corrupt-FS)) -> 8458.int32
Corrupt(Nothing) -> 11.int32
Corrupt(Just(Corrupt-Vtab)) -> 267.int32
Corrupt(Just(Corrupt-Sequence)) -> 523.int32
Corrupt(Just(Corrupt-Index)) -> 779.int32
Not-Found(_) -> 12.int32
Full(_) -> 13.int32
Cant-Open(Nothing) -> 14.int32
Cant-Open(Just(Cant-Open-Isdir)) -> 526.int32
Cant-Open(Just(Cant-Open-Fullpath)) -> 782.int32
Cant-Open(Just(Cant-Open-Convpath)) -> 1038.int32
Cant-Open(Just(Cant-Open-DirtyWAL)) -> 1294.int32
Cant-Open(Just(Cant-Open-Symlink)) -> 1550.int32
Protocol(_) -> 15.int32
Schema(_) -> 17.int32
Too-Big(_) -> 18.int32
Constraint(Nothing) -> 19.int32
Constraint(Just(Constraint-Check)) -> 275.int32
Constraint(Just(Constraint-Commithook)) -> 531.int32
Constraint(Just(Constraint-Foreignkey)) -> 787.int32
Constraint(Just(Constraint-Function)) -> 1043.int32
Constraint(Just(Constraint-Notnull)) -> 1299.int32
Constraint(Just(Constraint-Primarykey)) -> 1555.int32
Constraint(Just(Constraint-Trigger)) -> 1811.int32
Constraint(Just(Constraint-Unique)) -> 2067.int32
Constraint(Just(Constraint-VTab)) -> 2323.int32
Constraint(Just(Constraint-Rowid)) -> 2579.int32
Constraint(Just(Constraint-Pinned)) -> 2835.int32
Constraint(Just(Constraint-Datatype)) -> 3091.int32
Mismatch(_) -> 20.int32
Misuse(_) -> 21.int32
No-LFS(_) -> 21.int32
Auth(Nothing) -> 23.int32
Auth(Just(Auth-User)) -> 279.int32
Range(_) -> 25.int32
Not-A-DB(_) -> 26.int32
Notice(Nothing) -> 27.int32
Notice(Just(Notice-Recover-WAL)) -> 283.int32
Notice(Just(Notice-Recover-Rollback)) -> 539.int32
Warning(Nothing) -> 28.int32
Warning(Just(Warning-Autoindex)) -> 284.int32
Ok-Load-Permanently -> 256.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
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)
100 -> Row
101 -> Done
v -> throw("invalid SQLite3 result code " ++ v.show, ExnRange)
// Convert an SQLite result to its corresponding primary 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)
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)

265952
sqlite/sqlite3.c Normal file

File diff suppressed because it is too large Load Diff

13968
sqlite/sqlite3.h Normal file

File diff suppressed because it is too large Load Diff

116
sqlite/sqlite3.kk Normal file
View File

@@ -0,0 +1,116 @@
// Low level wrapper around a modern subset of the SQLite3 API.
//
// Using this module involves manual resource management.
//
module sqlite/sqlite3
import sqlite/capi
pub import sqlite/sqlite-result
// Helper to convert error codes returned directly from SQLite3 to the
// corresponding objects.
fun trusted-result(r: int32, pos: int = -1): sqlite-result
with final ctl throw-exn(exn) impossible(exn.message)
r.sqlite-result(pos)
// Database handle. An sqlite3 object is obtained by calling one of `open`,
// `open-existing`, or `read-only` and must be `close`d once all use is finished.
abstract struct sqlite3
cref: any
// Get the result code and position of the last operation.
pub fun last-result(db: sqlite3): sqlite-result
capi/extended-errcode(db.cref).trusted-result(capi/error-offset(db.cref))
///////////// System lifetime /////////////
// NOTE(zephyr): sqlite3/initialize has the qualifier on its name because
// type inference doesn't know whether to select this one or the capi function
// when constructing startup-initialized below, and there's no way to specify
// this one without the qualifier.
// This is probably a Koka bug.
// Initialize SQLite3.
// This function is automatically called during program initialization;
// see `startup-initialized`.
// If there is a call to `shutdown`, then there must be another call to
// `initialize` before the next use of any other SQLite3 function.
pub fun sqlite3/initialize(): sqlite-result
capi/initialize().trusted-result
// Release resources held by SQLite3.
// There must not be any `sqlite3` object which is not closed.
pub fun sqlite3/shutdown(): sqlite-result
capi/shutdown().trusted-result
// The result of initializing SQLite3 during program startup.
pub val startup-initialized = sqlite3/initialize()
///////////// Database lifetime /////////////
// 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.
fun open-raw(name: db-name, base-flags: int32, sync-mode: sync-mode, vfs: string): (sqlite3, sqlite-result)
val flags = match sync-mode
Single-Thread -> base-flags.or(0x02000000.i32) // | EXRESCODE
Multi-Thread -> base-flags.or(0x02008000.i32) // | EXRESCODE | NOMUTEX
Serialized -> base-flags.or(0x02010000.i32) // | EXRESCODE | FULLMUTEX
val (db, r) = match name
Filename(path) -> capi/open-v2(path, flags, vfs)
URI(uri) -> capi/open-v2(uri, flags.or(0x00000040.i32), vfs) // | URI
Memory-Shared(name) -> capi/open-v2("file:" ++ name ++ "?mode=memory&cache=shared", flags.or(0x000200C0.i32), vfs) // | URI | SHAREDCACHE | MEMORY
Memory -> capi/open-v2(":memory:", flags.or(0x00000080.i32), vfs) // | MEMORY
(Sqlite3(db), r.trusted-result)
// Open or create a database in read-write mode.
pub fun open(name: db-name, sync-mode: sync-mode = Serialized, vfs: string = ""): (sqlite3, sqlite-result)
open-raw(name, 6.i32, sync-mode, vfs)
// Open an existing database in read-write mode.
pub fun open-existing(name: db-name, sync-mode: sync-mode = Serialized, vfs: string = ""): (sqlite3, sqlite-result)
open-raw(name, 2.i32, sync-mode, vfs)
// Open a database in read-only mode.
pub fun read-only(name: db-name, sync-mode: sync-mode = Serialized, vfs: string = ""): (sqlite3, sqlite-result)
open-raw(name, 1.i32, sync-mode, vfs)
pub fun close(db: sqlite3): sqlite-result
capi/close(db.cref).trusted-result
///////////// Statements /////////////
// Prepared SQL statement.
// Corresponds to `sqlite3_stmt *` in the C API.
abstract struct statement
cref: any
pub fun prepare(db: sqlite3, sql: sslice, persistent: bool = False, disable-vtab: bool = False, disable-log: bool = False): either<sqlite-result, (statement, sslice)>
val flags = 0.i32
.or(persistent.int32 * 0x01.i32)
.or(disable-vtab.int32 * 0x04.i32)
.or(disable-log.int32 * 0x10.i32)
val (stmt, left, r) = capi/prepare-v3(db.cref, sql, flags)
if r == int32/sqlite-ok then
Right((Statement(stmt), left))
else
Left(r.trusted-result(capi/error-offset(db.cref)))
pub fun statement/finalize(stmt: statement): sqlite-result
capi/finalize(stmt.cref).trusted-result
pub fun statement/step(stmt: statement): sqlite-result
capi/step(stmt.cref).trusted-result
pub fun statement/reset(stmt: statement): sqlite-result
capi/reset(stmt.cref).trusted-result

17
test.nix.sh Executable file
View File

@@ -0,0 +1,17 @@
#!/usr/bin/env nix-shell
#! nix-shell -i bash --pure
#! nix-shell -p sqlite koka
#! nix-shell -I https://github.com/NixOS/nixpkgs/archive/e764fc9a405871f1f6ca3d1394fb422e0a0c3951.tar.gz
set -e
koka -e --ccincdir=./sqlite test/test.kk
RESULT=$(sqlite3 koka-test.sqlite3 'SELECT * FROM koka')
if [[ "$RESULT" != "value inserted from koka" ]]; then
echo "FAIL"
echo "want: value inserted from koka"
echo " got: $RESULT"
else
echo "PASS"
fi

27
test/test.kk Normal file
View File

@@ -0,0 +1,27 @@
module test
import sqlite/sqlite3
// TODO(zephyr): For now, we're just doing a very basic test to estimate whether
// I'm doing Koka FFI correctly. An actual test suite would be excellent.
fun stmt(db: sqlite3, text: string)
println(text)
match db.prepare(text.slice)
Left(r-prep) -> println(" prepare result: " ++ r-prep.show)
Right((stmt, rest)) ->
println(" prepare remainder: " ++ rest.show)
val r-step = stmt.step
println(" step result: " ++ r-step.show)
val r-fin = stmt.finalize
println(" finalize result: " ++ r-fin.show)
pub fun main()
println("initialize result: " ++ startup-initialized.show)
val (db, r-open) = open(Filename("koka-test.sqlite3"))
println("open result: " ++ r-open.show)
stmt(db, "DROP TABLE IF EXISTS koka; -- reset previous test runs")
stmt(db, "CREATE TABLE koka(v TEXT NOT NULL)")
stmt(db, "INSERT INTO koka VALUES ('value inserted from koka');")
val r-close = db.close
println("close result: " ++ r-close.show)