sqlite moves into the sx library: import vendors/sqlite/sqlite.sx
The amalgamation and the bindings now ship with sx itself (sx library/vendors/sqlite/ — bindings + c/ amalgamation); every import flips from ../src/db/sqlite.sx to vendors/sqlite/sqlite.sx, resolved through the compiler's stdlib search paths. vendor/ and src/db/ leave this repo entirely. make test 22/22 — the object cache keys on content, not path, so the relocated source still hits the existing cache entries.
This commit is contained in:
13
Makefile
13
Makefile
@@ -10,12 +10,13 @@ BUILD_DIR := build
|
|||||||
# product entry point. Further entry points under src/ get added here as
|
# product entry point. Further entry points under src/ get added here as
|
||||||
# they land.
|
# they land.
|
||||||
#
|
#
|
||||||
# The vendored SQLite (vendor/sqlite/) is part of the PROGRAM, not the
|
# SQLite ships with sx itself (its library's vendors/sqlite module,
|
||||||
# build system: src/db/sqlite.sx declares it as a named `#import c` unit
|
# imported as `vendors/sqlite/sqlite.sx`): a named `#import c` unit the
|
||||||
# (pinned defines + -O2 + #source), so sx compiles and links it through
|
# compiler builds through its content-addressed object cache
|
||||||
# its content-addressed object cache (.sx-cache/) — once per checkout.
|
# (.sx-cache/) — once per machine. `sx build` links the unit's objects
|
||||||
# `sx build` links the unit's objects into the binary; `sx run` loads
|
# into the binary; `sx run` loads them as a priority symbol target the
|
||||||
# them as a priority symbol target the OS libsqlite3 cannot shadow.
|
# OS libsqlite3 cannot shadow. Nothing SQLite-related lives in this
|
||||||
|
# repo anymore.
|
||||||
SMOKE := tests/smoke.sx
|
SMOKE := tests/smoke.sx
|
||||||
DIST := src/dist.sx
|
DIST := src/dist.sx
|
||||||
|
|
||||||
|
|||||||
811
src/db/sqlite.sx
811
src/db/sqlite.sx
@@ -1,811 +0,0 @@
|
|||||||
// =====================================================================
|
|
||||||
// sqlite.sx — sx bindings over the VENDORED SQLite amalgamation
|
|
||||||
// (vendor/sqlite/, see its README for version + upgrade notes).
|
|
||||||
//
|
|
||||||
// The amalgamation is part of THIS program: the named `#import c`
|
|
||||||
// unit below compiles vendor/sqlite/sqlite3.c with the pinned options
|
|
||||||
// (through sx's content-addressed object cache, so it compiles once
|
|
||||||
// per checkout, not once per run) and every `#foreign sqlib` decl
|
|
||||||
// binds against it. Under `sx run` the unit's dylib is a PRIORITY
|
|
||||||
// symbol-search target consulted before the process images, so the OS
|
|
||||||
// libsqlite3 (loaded into the compiler via CoreServices) can never
|
|
||||||
// shadow the vendored copy; under `sx build` the unit's objects link
|
|
||||||
// directly into the binary. tests/sqlite_smoke.sx pins
|
|
||||||
// `sqlite3_libversion()` to the vendored version so any fallback
|
|
||||||
// fails the suite.
|
|
||||||
//
|
|
||||||
// COVERAGE — the full practical C API, ONE variant per function (the
|
|
||||||
// modern/64-bit form where duplicates exist; `bind_text`/`bind_blob`
|
|
||||||
// keep the 32-bit length forms, which skip text64's encoding arg —
|
|
||||||
// per-value sizes beyond 2 GiB are out of scope). Deliberately NOT
|
|
||||||
// bound, each a real boundary rather than an omission:
|
|
||||||
// * callback-taking APIs (exec's row callback, busy_handler,
|
|
||||||
// hooks, trace/progress, authorizer, create_function/collation)
|
|
||||||
// — they need C->sx function-pointer callbacks; busy_timeout and
|
|
||||||
// callback-less exec cover the storage layer's needs;
|
|
||||||
// * the sqlite3_value_* family + bind/column_value — only
|
|
||||||
// meaningful with user-defined functions (callbacks, above);
|
|
||||||
// * varargs APIs (sqlite3_config, db_config, log, mprintf) —
|
|
||||||
// configuration happens via the unit's compile-time defines;
|
|
||||||
// * UTF-16 variants (we are UTF-8 only), the mutex/VFS layer
|
|
||||||
// (built SQLITE_THREADSAFE=0), sessions/snapshots/vtabs/loadable
|
|
||||||
// extensions (not compiled in), deprecated API (compiled out via
|
|
||||||
// SQLITE_OMIT_DEPRECATED).
|
|
||||||
//
|
|
||||||
// FFI type choices: handles cross as `usize` (`sqlite3*`,
|
|
||||||
// `sqlite3_stmt*`, `sqlite3_blob*`, `sqlite3_backup*` are opaque and
|
|
||||||
// never dereferenced; `usize` keeps out-params single-level `*usize`
|
|
||||||
// instead of `**void`). Whole C strings cross as `cstring` — IN via
|
|
||||||
// `to_cstring` (literals coerce), OUT as `?cstring` copied through
|
|
||||||
// `sq_from_cstr` before sqlite's buffer dies on the next
|
|
||||||
// step/finalize/close. `prepare_v2/v3` and `bind_text/blob` take
|
|
||||||
// `[*]u8` + explicit byte length instead, so un-terminated sx string
|
|
||||||
// views pass without copying — and `column_text`/`column_blob`/
|
|
||||||
// `serialize` return `?*u8` + explicit byte counts, never `cstring`:
|
|
||||||
// their payloads may carry interior NULs that a strlen would truncate.
|
|
||||||
// Integer widths are SQLite's own, on the wrappers too: `i32` where
|
|
||||||
// the C API says `int` (result codes, counts, indexes, byte sizes),
|
|
||||||
// `u32`/`u64` where it says unsigned, and `i64` only for the genuinely
|
|
||||||
// 64-bit APIs (rowids, changes64, memory accounting, serialize sizes).
|
|
||||||
// =====================================================================
|
|
||||||
|
|
||||||
#import "modules/std.sx";
|
|
||||||
|
|
||||||
sqlib :: #import c {
|
|
||||||
#define "SQLITE_DQS=0";
|
|
||||||
#define "SQLITE_THREADSAFE=0";
|
|
||||||
#define "SQLITE_DEFAULT_MEMSTATUS=0";
|
|
||||||
#define "SQLITE_OMIT_DEPRECATED";
|
|
||||||
#define "SQLITE_OMIT_SHARED_CACHE";
|
|
||||||
#define "SQLITE_LIKE_DOESNT_MATCH_BLOBS";
|
|
||||||
#define "SQLITE_ENABLE_COLUMN_METADATA";
|
|
||||||
#flags "-O2";
|
|
||||||
#source "../../vendor/sqlite/sqlite3.c";
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── FFI: connection lifecycle ─────────────────────────────────────────
|
|
||||||
sqlite3_open :: (path: cstring, out_db: *usize) -> i32 #foreign sqlib "sqlite3_open";
|
|
||||||
sqlite3_open_v2 :: (path: cstring, out_db: *usize, flags: i32, vfs: usize) -> i32 #foreign sqlib "sqlite3_open_v2";
|
|
||||||
sqlite3_close :: (db: usize) -> i32 #foreign sqlib "sqlite3_close";
|
|
||||||
sqlite3_close_v2 :: (db: usize) -> i32 #foreign sqlib "sqlite3_close_v2";
|
|
||||||
|
|
||||||
// ── FFI: errors ───────────────────────────────────────────────────────
|
|
||||||
sqlite3_errcode :: (db: usize) -> i32 #foreign sqlib "sqlite3_errcode";
|
|
||||||
sqlite3_extended_errcode :: (db: usize) -> i32 #foreign sqlib "sqlite3_extended_errcode";
|
|
||||||
sqlite3_errmsg :: (db: usize) -> ?cstring #foreign sqlib "sqlite3_errmsg";
|
|
||||||
sqlite3_errstr :: (code: i32) -> ?cstring #foreign sqlib "sqlite3_errstr";
|
|
||||||
sqlite3_error_offset :: (db: usize) -> i32 #foreign sqlib "sqlite3_error_offset";
|
|
||||||
sqlite3_extended_result_codes :: (db: usize, onoff: i32) -> i32 #foreign sqlib "sqlite3_extended_result_codes";
|
|
||||||
|
|
||||||
// ── FFI: connection state & control ───────────────────────────────────
|
|
||||||
sqlite3_busy_timeout :: (db: usize, ms: i32) -> i32 #foreign sqlib "sqlite3_busy_timeout";
|
|
||||||
sqlite3_interrupt :: (db: usize) #foreign sqlib "sqlite3_interrupt";
|
|
||||||
sqlite3_is_interrupted :: (db: usize) -> i32 #foreign sqlib "sqlite3_is_interrupted";
|
|
||||||
sqlite3_get_autocommit :: (db: usize) -> i32 #foreign sqlib "sqlite3_get_autocommit";
|
|
||||||
sqlite3_txn_state :: (db: usize, schema: cstring) -> i32 #foreign sqlib "sqlite3_txn_state";
|
|
||||||
sqlite3_db_filename :: (db: usize, db_name: cstring) -> ?cstring #foreign sqlib "sqlite3_db_filename";
|
|
||||||
sqlite3_db_readonly :: (db: usize, db_name: cstring) -> i32 #foreign sqlib "sqlite3_db_readonly";
|
|
||||||
sqlite3_db_cacheflush :: (db: usize) -> i32 #foreign sqlib "sqlite3_db_cacheflush";
|
|
||||||
sqlite3_db_release_memory :: (db: usize) -> i32 #foreign sqlib "sqlite3_db_release_memory";
|
|
||||||
sqlite3_last_insert_rowid :: (db: usize) -> i64 #foreign sqlib "sqlite3_last_insert_rowid";
|
|
||||||
sqlite3_set_last_insert_rowid :: (db: usize, rowid: i64) #foreign sqlib "sqlite3_set_last_insert_rowid";
|
|
||||||
sqlite3_changes64 :: (db: usize) -> i64 #foreign sqlib "sqlite3_changes64";
|
|
||||||
sqlite3_total_changes64 :: (db: usize) -> i64 #foreign sqlib "sqlite3_total_changes64";
|
|
||||||
sqlite3_limit :: (db: usize, id: i32, new_val: i32) -> i32 #foreign sqlib "sqlite3_limit";
|
|
||||||
sqlite3_exec :: (db: usize, sql: cstring, cb: usize, arg: usize, errmsg: usize) -> i32 #foreign sqlib "sqlite3_exec";
|
|
||||||
sqlite3_table_column_metadata :: (db: usize, db_name: cstring, table: cstring, column: cstring, out_data_type: *usize, out_coll_seq: *usize, out_not_null: *i32, out_primary_key: *i32, out_autoinc: *i32) -> i32 #foreign sqlib "sqlite3_table_column_metadata";
|
|
||||||
|
|
||||||
// ── FFI: statements ───────────────────────────────────────────────────
|
|
||||||
sqlite3_prepare_v2 :: (db: usize, sql: [*]u8, nbyte: i32, out_stmt: *usize, out_tail: usize) -> i32 #foreign sqlib "sqlite3_prepare_v2";
|
|
||||||
sqlite3_prepare_v3 :: (db: usize, sql: [*]u8, nbyte: i32, prep_flags: u32, out_stmt: *usize, out_tail: usize) -> i32 #foreign sqlib "sqlite3_prepare_v3";
|
|
||||||
sqlite3_step :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_step";
|
|
||||||
sqlite3_reset :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_reset";
|
|
||||||
sqlite3_finalize :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_finalize";
|
|
||||||
sqlite3_clear_bindings :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_clear_bindings";
|
|
||||||
sqlite3_sql :: (stmt: usize) -> ?cstring #foreign sqlib "sqlite3_sql";
|
|
||||||
sqlite3_expanded_sql :: (stmt: usize) -> ?cstring #foreign sqlib "sqlite3_expanded_sql";
|
|
||||||
sqlite3_stmt_busy :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_stmt_busy";
|
|
||||||
sqlite3_stmt_readonly :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_stmt_readonly";
|
|
||||||
sqlite3_stmt_isexplain :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_stmt_isexplain";
|
|
||||||
sqlite3_stmt_explain :: (stmt: usize, mode: i32) -> i32 #foreign sqlib "sqlite3_stmt_explain";
|
|
||||||
sqlite3_stmt_status :: (stmt: usize, op: i32, reset: i32) -> i32 #foreign sqlib "sqlite3_stmt_status";
|
|
||||||
sqlite3_db_handle :: (stmt: usize) -> usize #foreign sqlib "sqlite3_db_handle";
|
|
||||||
sqlite3_next_stmt :: (db: usize, stmt: usize) -> usize #foreign sqlib "sqlite3_next_stmt";
|
|
||||||
|
|
||||||
// ── FFI: binding ──────────────────────────────────────────────────────
|
|
||||||
sqlite3_bind_parameter_count :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_bind_parameter_count";
|
|
||||||
sqlite3_bind_parameter_index :: (stmt: usize, name: cstring) -> i32 #foreign sqlib "sqlite3_bind_parameter_index";
|
|
||||||
sqlite3_bind_parameter_name :: (stmt: usize, idx: i32) -> ?cstring #foreign sqlib "sqlite3_bind_parameter_name";
|
|
||||||
sqlite3_bind_text :: (stmt: usize, idx: i32, text: [*]u8, n: i32, destructor: isize) -> i32 #foreign sqlib "sqlite3_bind_text";
|
|
||||||
sqlite3_bind_blob :: (stmt: usize, idx: i32, data: [*]u8, n: i32, destructor: isize) -> i32 #foreign sqlib "sqlite3_bind_blob";
|
|
||||||
sqlite3_bind_double :: (stmt: usize, idx: i32, v: f64) -> i32 #foreign sqlib "sqlite3_bind_double";
|
|
||||||
sqlite3_bind_int64 :: (stmt: usize, idx: i32, v: i64) -> i32 #foreign sqlib "sqlite3_bind_int64";
|
|
||||||
sqlite3_bind_null :: (stmt: usize, idx: i32) -> i32 #foreign sqlib "sqlite3_bind_null";
|
|
||||||
sqlite3_bind_zeroblob64 :: (stmt: usize, idx: i32, n: u64) -> i32 #foreign sqlib "sqlite3_bind_zeroblob64";
|
|
||||||
|
|
||||||
// ── FFI: result columns ───────────────────────────────────────────────
|
|
||||||
sqlite3_column_blob :: (stmt: usize, icol: i32) -> ?*u8 #foreign sqlib "sqlite3_column_blob";
|
|
||||||
sqlite3_column_double :: (stmt: usize, icol: i32) -> f64 #foreign sqlib "sqlite3_column_double";
|
|
||||||
sqlite3_column_int64 :: (stmt: usize, icol: i32) -> i64 #foreign sqlib "sqlite3_column_int64";
|
|
||||||
sqlite3_column_text :: (stmt: usize, icol: i32) -> ?*u8 #foreign sqlib "sqlite3_column_text";
|
|
||||||
sqlite3_column_bytes :: (stmt: usize, icol: i32) -> i32 #foreign sqlib "sqlite3_column_bytes";
|
|
||||||
sqlite3_column_type :: (stmt: usize, icol: i32) -> i32 #foreign sqlib "sqlite3_column_type";
|
|
||||||
sqlite3_column_count :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_column_count";
|
|
||||||
sqlite3_column_name :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "sqlite3_column_name";
|
|
||||||
sqlite3_column_decltype :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "sqlite3_column_decltype";
|
|
||||||
sqlite3_column_database_name :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "sqlite3_column_database_name";
|
|
||||||
sqlite3_column_table_name :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "sqlite3_column_table_name";
|
|
||||||
sqlite3_column_origin_name :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "sqlite3_column_origin_name";
|
|
||||||
sqlite3_data_count :: (stmt: usize) -> i32 #foreign sqlib "sqlite3_data_count";
|
|
||||||
|
|
||||||
// ── FFI: incremental blob I/O ─────────────────────────────────────────
|
|
||||||
sqlite3_blob_open :: (db: usize, db_name: cstring, table: cstring, column: cstring, rowid: i64, flags: i32, out_blob: *usize) -> i32 #foreign sqlib "sqlite3_blob_open";
|
|
||||||
sqlite3_blob_reopen :: (blob: usize, rowid: i64) -> i32 #foreign sqlib "sqlite3_blob_reopen";
|
|
||||||
sqlite3_blob_close :: (blob: usize) -> i32 #foreign sqlib "sqlite3_blob_close";
|
|
||||||
sqlite3_blob_bytes :: (blob: usize) -> i32 #foreign sqlib "sqlite3_blob_bytes";
|
|
||||||
sqlite3_blob_read :: (blob: usize, buf: [*]u8, n: i32, offset: i32) -> i32 #foreign sqlib "sqlite3_blob_read";
|
|
||||||
sqlite3_blob_write :: (blob: usize, buf: [*]u8, n: i32, offset: i32) -> i32 #foreign sqlib "sqlite3_blob_write";
|
|
||||||
|
|
||||||
// ── FFI: online backup ────────────────────────────────────────────────
|
|
||||||
sqlite3_backup_init :: (dst: usize, dst_name: cstring, src: usize, src_name: cstring) -> usize #foreign sqlib "sqlite3_backup_init";
|
|
||||||
sqlite3_backup_step :: (bk: usize, n_page: i32) -> i32 #foreign sqlib "sqlite3_backup_step";
|
|
||||||
sqlite3_backup_finish :: (bk: usize) -> i32 #foreign sqlib "sqlite3_backup_finish";
|
|
||||||
sqlite3_backup_remaining :: (bk: usize) -> i32 #foreign sqlib "sqlite3_backup_remaining";
|
|
||||||
sqlite3_backup_pagecount :: (bk: usize) -> i32 #foreign sqlib "sqlite3_backup_pagecount";
|
|
||||||
|
|
||||||
// ── FFI: serialization ────────────────────────────────────────────────
|
|
||||||
sqlite3_serialize :: (db: usize, schema: cstring, out_size: *i64, flags: u32) -> ?*u8 #foreign sqlib "sqlite3_serialize";
|
|
||||||
sqlite3_deserialize :: (db: usize, schema: cstring, data: usize, sz_db: i64, sz_buf: i64, flags: u32) -> i32 #foreign sqlib "sqlite3_deserialize";
|
|
||||||
|
|
||||||
// ── FFI: library utilities ────────────────────────────────────────────
|
|
||||||
sqlite3_libversion :: () -> ?cstring #foreign sqlib "sqlite3_libversion";
|
|
||||||
sqlite3_libversion_number :: () -> i32 #foreign sqlib "sqlite3_libversion_number";
|
|
||||||
sqlite3_sourceid :: () -> ?cstring #foreign sqlib "sqlite3_sourceid";
|
|
||||||
sqlite3_threadsafe :: () -> i32 #foreign sqlib "sqlite3_threadsafe";
|
|
||||||
sqlite3_compileoption_used :: (name: cstring) -> i32 #foreign sqlib "sqlite3_compileoption_used";
|
|
||||||
sqlite3_compileoption_get :: (n: i32) -> ?cstring #foreign sqlib "sqlite3_compileoption_get";
|
|
||||||
sqlite3_complete :: (sql: cstring) -> i32 #foreign sqlib "sqlite3_complete";
|
|
||||||
sqlite3_free :: (p: usize) #foreign sqlib "sqlite3_free";
|
|
||||||
sqlite3_malloc64 :: (n: u64) -> usize #foreign sqlib "sqlite3_malloc64";
|
|
||||||
sqlite3_msize :: (p: usize) -> u64 #foreign sqlib "sqlite3_msize";
|
|
||||||
sqlite3_memory_used :: () -> i64 #foreign sqlib "sqlite3_memory_used";
|
|
||||||
sqlite3_memory_highwater :: (reset: i32) -> i64 #foreign sqlib "sqlite3_memory_highwater";
|
|
||||||
sqlite3_release_memory :: (n: i32) -> i32 #foreign sqlib "sqlite3_release_memory";
|
|
||||||
sqlite3_soft_heap_limit64 :: (n: i64) -> i64 #foreign sqlib "sqlite3_soft_heap_limit64";
|
|
||||||
sqlite3_hard_heap_limit64 :: (n: i64) -> i64 #foreign sqlib "sqlite3_hard_heap_limit64";
|
|
||||||
sqlite3_randomness :: (n: i32, buf: [*]u8) #foreign sqlib "sqlite3_randomness";
|
|
||||||
sqlite3_sleep :: (ms: i32) -> i32 #foreign sqlib "sqlite3_sleep";
|
|
||||||
sqlite3_stricmp :: (a: cstring, b: cstring) -> i32 #foreign sqlib "sqlite3_stricmp";
|
|
||||||
sqlite3_strnicmp :: (a: cstring, b: cstring, n: i32) -> i32 #foreign sqlib "sqlite3_strnicmp";
|
|
||||||
sqlite3_strglob :: (glob: cstring, s: cstring) -> i32 #foreign sqlib "sqlite3_strglob";
|
|
||||||
sqlite3_strlike :: (like: cstring, s: cstring, esc: u32) -> i32 #foreign sqlib "sqlite3_strlike";
|
|
||||||
|
|
||||||
// ── Result codes (primary; full set) ──────────────────────────────────
|
|
||||||
SQLITE_OK :: 0;
|
|
||||||
SQLITE_ERROR :: 1;
|
|
||||||
SQLITE_INTERNAL :: 2;
|
|
||||||
SQLITE_PERM :: 3;
|
|
||||||
SQLITE_ABORT :: 4;
|
|
||||||
SQLITE_BUSY :: 5;
|
|
||||||
SQLITE_LOCKED :: 6;
|
|
||||||
SQLITE_NOMEM :: 7;
|
|
||||||
SQLITE_READONLY :: 8;
|
|
||||||
SQLITE_INTERRUPT :: 9;
|
|
||||||
SQLITE_IOERR :: 10;
|
|
||||||
SQLITE_CORRUPT :: 11;
|
|
||||||
SQLITE_NOTFOUND :: 12;
|
|
||||||
SQLITE_FULL :: 13;
|
|
||||||
SQLITE_CANTOPEN :: 14;
|
|
||||||
SQLITE_PROTOCOL :: 15;
|
|
||||||
SQLITE_EMPTY :: 16;
|
|
||||||
SQLITE_SCHEMA :: 17;
|
|
||||||
SQLITE_TOOBIG :: 18;
|
|
||||||
SQLITE_CONSTRAINT :: 19;
|
|
||||||
SQLITE_MISMATCH :: 20;
|
|
||||||
SQLITE_MISUSE :: 21;
|
|
||||||
SQLITE_NOLFS :: 22;
|
|
||||||
SQLITE_AUTH :: 23;
|
|
||||||
SQLITE_FORMAT :: 24;
|
|
||||||
SQLITE_RANGE :: 25;
|
|
||||||
SQLITE_NOTADB :: 26;
|
|
||||||
SQLITE_NOTICE :: 27;
|
|
||||||
SQLITE_WARNING :: 28;
|
|
||||||
SQLITE_ROW :: 100;
|
|
||||||
SQLITE_DONE :: 101;
|
|
||||||
|
|
||||||
// Extended result codes are `primary | (n << 8)`; the families a storage
|
|
||||||
// layer actually branches on are spelled out, others compute by formula.
|
|
||||||
SQLITE_ABORT_ROLLBACK :: 516; // ABORT | (2<<8)
|
|
||||||
SQLITE_BUSY_RECOVERY :: 261; // BUSY | (1<<8)
|
|
||||||
SQLITE_BUSY_SNAPSHOT :: 517; // BUSY | (2<<8)
|
|
||||||
SQLITE_BUSY_TIMEOUT :: 773; // BUSY | (3<<8)
|
|
||||||
SQLITE_LOCKED_SHAREDCACHE :: 262; // LOCKED | (1<<8)
|
|
||||||
SQLITE_LOCKED_VTAB :: 518; // LOCKED | (2<<8)
|
|
||||||
SQLITE_CONSTRAINT_CHECK :: 275; // CONSTRAINT | (1<<8)
|
|
||||||
SQLITE_CONSTRAINT_COMMITHOOK :: 531;
|
|
||||||
SQLITE_CONSTRAINT_FOREIGNKEY :: 787;
|
|
||||||
SQLITE_CONSTRAINT_FUNCTION :: 1043;
|
|
||||||
SQLITE_CONSTRAINT_NOTNULL :: 1299;
|
|
||||||
SQLITE_CONSTRAINT_PRIMARYKEY :: 1555;
|
|
||||||
SQLITE_CONSTRAINT_TRIGGER :: 1811;
|
|
||||||
SQLITE_CONSTRAINT_UNIQUE :: 2067;
|
|
||||||
SQLITE_CONSTRAINT_VTAB :: 2323;
|
|
||||||
SQLITE_CONSTRAINT_ROWID :: 2579;
|
|
||||||
SQLITE_CONSTRAINT_PINNED :: 2835;
|
|
||||||
SQLITE_CONSTRAINT_DATATYPE :: 3091;
|
|
||||||
|
|
||||||
// ── Fundamental datatypes (column_type) ───────────────────────────────
|
|
||||||
SQLITE_INTEGER :: 1;
|
|
||||||
SQLITE_FLOAT :: 2;
|
|
||||||
SQLITE_TEXT :: 3;
|
|
||||||
SQLITE_BLOB :: 4;
|
|
||||||
SQLITE_NULL :: 5;
|
|
||||||
|
|
||||||
// ── open_v2 flags ─────────────────────────────────────────────────────
|
|
||||||
SQLITE_OPEN_READONLY :: 0x00000001;
|
|
||||||
SQLITE_OPEN_READWRITE :: 0x00000002;
|
|
||||||
SQLITE_OPEN_CREATE :: 0x00000004;
|
|
||||||
SQLITE_OPEN_URI :: 0x00000040;
|
|
||||||
SQLITE_OPEN_MEMORY :: 0x00000080;
|
|
||||||
SQLITE_OPEN_NOMUTEX :: 0x00008000;
|
|
||||||
SQLITE_OPEN_FULLMUTEX :: 0x00010000;
|
|
||||||
SQLITE_OPEN_NOFOLLOW :: 0x01000000;
|
|
||||||
SQLITE_OPEN_EXRESCODE :: 0x02000000;
|
|
||||||
|
|
||||||
// ── prepare_v3 flags ──────────────────────────────────────────────────
|
|
||||||
SQLITE_PREPARE_PERSISTENT :: 0x01;
|
|
||||||
SQLITE_PREPARE_NO_VTAB :: 0x04;
|
|
||||||
|
|
||||||
// ── txn_state values ──────────────────────────────────────────────────
|
|
||||||
SQLITE_TXN_NONE :: 0;
|
|
||||||
SQLITE_TXN_READ :: 1;
|
|
||||||
SQLITE_TXN_WRITE :: 2;
|
|
||||||
|
|
||||||
// ── limit ids ─────────────────────────────────────────────────────────
|
|
||||||
SQLITE_LIMIT_LENGTH :: 0;
|
|
||||||
SQLITE_LIMIT_SQL_LENGTH :: 1;
|
|
||||||
SQLITE_LIMIT_COLUMN :: 2;
|
|
||||||
SQLITE_LIMIT_EXPR_DEPTH :: 3;
|
|
||||||
SQLITE_LIMIT_COMPOUND_SELECT :: 4;
|
|
||||||
SQLITE_LIMIT_VDBE_OP :: 5;
|
|
||||||
SQLITE_LIMIT_FUNCTION_ARG :: 6;
|
|
||||||
SQLITE_LIMIT_ATTACHED :: 7;
|
|
||||||
SQLITE_LIMIT_LIKE_PATTERN_LENGTH :: 8;
|
|
||||||
SQLITE_LIMIT_VARIABLE_NUMBER :: 9;
|
|
||||||
SQLITE_LIMIT_TRIGGER_DEPTH :: 10;
|
|
||||||
SQLITE_LIMIT_WORKER_THREADS :: 11;
|
|
||||||
|
|
||||||
// ── stmt_status ops ───────────────────────────────────────────────────
|
|
||||||
SQLITE_STMTSTATUS_FULLSCAN_STEP :: 1;
|
|
||||||
SQLITE_STMTSTATUS_SORT :: 2;
|
|
||||||
SQLITE_STMTSTATUS_AUTOINDEX :: 3;
|
|
||||||
SQLITE_STMTSTATUS_VM_STEP :: 4;
|
|
||||||
SQLITE_STMTSTATUS_REPREPARE :: 5;
|
|
||||||
SQLITE_STMTSTATUS_RUN :: 6;
|
|
||||||
SQLITE_STMTSTATUS_FILTER_MISS :: 7;
|
|
||||||
SQLITE_STMTSTATUS_FILTER_HIT :: 8;
|
|
||||||
SQLITE_STMTSTATUS_MEMUSED :: 99;
|
|
||||||
|
|
||||||
// ── serialize / deserialize flags ─────────────────────────────────────
|
|
||||||
SQLITE_SERIALIZE_NOCOPY :: 1;
|
|
||||||
SQLITE_DESERIALIZE_FREEONCLOSE :: 1;
|
|
||||||
SQLITE_DESERIALIZE_RESIZEABLE :: 2;
|
|
||||||
SQLITE_DESERIALIZE_READONLY :: 4;
|
|
||||||
|
|
||||||
// bind destructor sentinels: TRANSIENT = copy the bytes NOW (sx-side
|
|
||||||
// buffers — arena strings, stack temporaries — don't outlive the call);
|
|
||||||
// STATIC = the caller guarantees the bytes outlive the statement.
|
|
||||||
SQLITE_STATIC : isize : 0;
|
|
||||||
SQLITE_TRANSIENT : isize : -1;
|
|
||||||
|
|
||||||
SqliteErr :: error {
|
|
||||||
Open,
|
|
||||||
Exec,
|
|
||||||
Prepare,
|
|
||||||
Bind,
|
|
||||||
Step,
|
|
||||||
Blob,
|
|
||||||
Backup,
|
|
||||||
Serialize,
|
|
||||||
Metadata,
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── string helpers ────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
// Copy the C string at `p` (null = empty) into context.allocator —
|
|
||||||
// sqlite's buffer dies on the next step/finalize/close, so the view from
|
|
||||||
// `from_cstring` must not escape.
|
|
||||||
sq_from_cstr :: (p: ?cstring) -> string {
|
|
||||||
if p == null { return ""; }
|
|
||||||
v := from_cstring(p!);
|
|
||||||
if v.len == 0 { return ""; }
|
|
||||||
return substr(v, 0, v.len);
|
|
||||||
}
|
|
||||||
|
|
||||||
// `n` raw bytes at `p` as a context.allocator copy ("" when p is null).
|
|
||||||
sq_copy_bytes :: (p: ?*u8, n: i64) -> string {
|
|
||||||
if p == null or n <= 0 { return ""; }
|
|
||||||
cp : [*]u8 = xx p!;
|
|
||||||
raw : [*]u8 = xx context.allocator.alloc_bytes(n + 1);
|
|
||||||
memcpy(raw, cp, n);
|
|
||||||
raw[n] = 0;
|
|
||||||
return string.{ ptr = raw, len = n };
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── library-level utilities ───────────────────────────────────────────
|
|
||||||
|
|
||||||
// The linked/loaded SQLite's version string (e.g. "3.53.2").
|
|
||||||
sqlite_version :: () -> string {
|
|
||||||
return sq_from_cstr(sqlite3_libversion());
|
|
||||||
}
|
|
||||||
sqlite_version_number :: () -> i32 {
|
|
||||||
return sqlite3_libversion_number();
|
|
||||||
}
|
|
||||||
sqlite_sourceid :: () -> string {
|
|
||||||
return sq_from_cstr(sqlite3_sourceid());
|
|
||||||
}
|
|
||||||
sqlite_threadsafe :: () -> bool {
|
|
||||||
return sqlite3_threadsafe() != 0;
|
|
||||||
}
|
|
||||||
sqlite_compileoption_used :: (name: string) -> bool {
|
|
||||||
return sqlite3_compileoption_used(to_cstring(name)) != 0;
|
|
||||||
}
|
|
||||||
// The n'th compile option ("" past the end).
|
|
||||||
sqlite_compileoption_get :: (n: i32) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_compileoption_get(n));
|
|
||||||
}
|
|
||||||
// Human text for a result code (static storage; copied anyway).
|
|
||||||
sqlite_errstr :: (code: i32) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_errstr(code));
|
|
||||||
}
|
|
||||||
// True iff `sql` ends in a complete SQL statement.
|
|
||||||
sqlite_complete :: (sql: string) -> bool {
|
|
||||||
return sqlite3_complete(to_cstring(sql)) != 0;
|
|
||||||
}
|
|
||||||
// `n` bytes from SQLite's CSPRNG.
|
|
||||||
sqlite_randomness :: (n: i32) -> string {
|
|
||||||
len : i64 = n;
|
|
||||||
raw : [*]u8 = xx context.allocator.alloc_bytes(len + 1);
|
|
||||||
sqlite3_randomness(n, raw);
|
|
||||||
raw[len] = 0;
|
|
||||||
return string.{ ptr = raw, len = len };
|
|
||||||
}
|
|
||||||
sqlite_sleep :: (ms: i32) -> i32 {
|
|
||||||
return sqlite3_sleep(ms);
|
|
||||||
}
|
|
||||||
// memory_used/highwater read 0 in this build: SQLITE_DEFAULT_MEMSTATUS=0.
|
|
||||||
sqlite_memory_used :: () -> i64 {
|
|
||||||
return sqlite3_memory_used();
|
|
||||||
}
|
|
||||||
sqlite_memory_highwater :: (reset: bool) -> i64 {
|
|
||||||
return sqlite3_memory_highwater(if reset then 1 else 0);
|
|
||||||
}
|
|
||||||
sqlite_release_memory :: (n: i32) -> i32 {
|
|
||||||
return sqlite3_release_memory(n);
|
|
||||||
}
|
|
||||||
sqlite_soft_heap_limit :: (n: i64) -> i64 {
|
|
||||||
return sqlite3_soft_heap_limit64(n);
|
|
||||||
}
|
|
||||||
sqlite_hard_heap_limit :: (n: i64) -> i64 {
|
|
||||||
return sqlite3_hard_heap_limit64(n);
|
|
||||||
}
|
|
||||||
// GLOB / LIKE / case-insensitive compare, sqlite's own semantics.
|
|
||||||
// strglob/strlike answer 0 on MATCH (C convention) — exposed as bools.
|
|
||||||
sqlite_strglob :: (glob: string, s: string) -> bool {
|
|
||||||
return sqlite3_strglob(to_cstring(glob), to_cstring(s)) == 0;
|
|
||||||
}
|
|
||||||
sqlite_strlike :: (like: string, s: string, esc: u32) -> bool {
|
|
||||||
return sqlite3_strlike(to_cstring(like), to_cstring(s), esc) == 0;
|
|
||||||
}
|
|
||||||
sqlite_stricmp :: (a: string, b: string) -> i32 {
|
|
||||||
return sqlite3_stricmp(to_cstring(a), to_cstring(b));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── prepared statements ───────────────────────────────────────────────
|
|
||||||
|
|
||||||
// One prepared statement. `finalize` releases it; bind indexes are
|
|
||||||
// 1-based and column indexes 0-based, as in the C API.
|
|
||||||
SqliteStmt :: struct {
|
|
||||||
handle: usize;
|
|
||||||
db: usize;
|
|
||||||
|
|
||||||
// ── binding ──
|
|
||||||
bind_text :: (self: *SqliteStmt, idx: i32, s: string) -> !SqliteErr {
|
|
||||||
rc := sqlite3_bind_text(self.handle, idx, s.ptr, xx s.len, SQLITE_TRANSIENT);
|
|
||||||
if rc != SQLITE_OK { raise error.Bind; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bind_blob :: (self: *SqliteStmt, idx: i32, bytes: string) -> !SqliteErr {
|
|
||||||
rc := sqlite3_bind_blob(self.handle, idx, bytes.ptr, xx bytes.len, SQLITE_TRANSIENT);
|
|
||||||
if rc != SQLITE_OK { raise error.Bind; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bind_double :: (self: *SqliteStmt, idx: i32, v: f64) -> !SqliteErr {
|
|
||||||
rc := sqlite3_bind_double(self.handle, idx, v);
|
|
||||||
if rc != SQLITE_OK { raise error.Bind; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bind_int64 :: (self: *SqliteStmt, idx: i32, v: i64) -> !SqliteErr {
|
|
||||||
rc := sqlite3_bind_int64(self.handle, idx, v);
|
|
||||||
if rc != SQLITE_OK { raise error.Bind; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bind_null :: (self: *SqliteStmt, idx: i32) -> !SqliteErr {
|
|
||||||
rc := sqlite3_bind_null(self.handle, idx);
|
|
||||||
if rc != SQLITE_OK { raise error.Bind; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Reserve an `n`-byte zero-filled blob (fill it via SqliteBlob I/O).
|
|
||||||
bind_zeroblob :: (self: *SqliteStmt, idx: i32, n: u64) -> !SqliteErr {
|
|
||||||
rc := sqlite3_bind_zeroblob64(self.handle, idx, n);
|
|
||||||
if rc != SQLITE_OK { raise error.Bind; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
clear_bindings :: (self: *SqliteStmt) {
|
|
||||||
sqlite3_clear_bindings(self.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── parameters ──
|
|
||||||
parameter_count :: (self: *SqliteStmt) -> i32 {
|
|
||||||
return sqlite3_bind_parameter_count(self.handle);
|
|
||||||
}
|
|
||||||
// 1-based index of a named parameter (":name" / "@name" / "?N"), 0 if absent.
|
|
||||||
parameter_index :: (self: *SqliteStmt, name: string) -> i32 {
|
|
||||||
return sqlite3_bind_parameter_index(self.handle, to_cstring(name));
|
|
||||||
}
|
|
||||||
// The name of parameter `idx` ("" for nameless `?` parameters).
|
|
||||||
parameter_name :: (self: *SqliteStmt, idx: i32) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_bind_parameter_name(self.handle, idx));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── execution ──
|
|
||||||
// SQLITE_ROW / SQLITE_DONE on success; anything else raises with the
|
|
||||||
// detail left in the connection's errmsg.
|
|
||||||
step :: (self: *SqliteStmt) -> (i32, !SqliteErr) {
|
|
||||||
rc := sqlite3_step(self.handle);
|
|
||||||
if rc != SQLITE_ROW and rc != SQLITE_DONE { raise error.Step; }
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
reset :: (self: *SqliteStmt) {
|
|
||||||
sqlite3_reset(self.handle);
|
|
||||||
}
|
|
||||||
finalize :: (self: *SqliteStmt) {
|
|
||||||
sqlite3_finalize(self.handle);
|
|
||||||
self.handle = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── columns (copies into context.allocator; NULL reads as ""/0) ──
|
|
||||||
column_int64 :: (self: *SqliteStmt, icol: i32) -> i64 {
|
|
||||||
return sqlite3_column_int64(self.handle, icol);
|
|
||||||
}
|
|
||||||
column_double :: (self: *SqliteStmt, icol: i32) -> f64 {
|
|
||||||
return sqlite3_column_double(self.handle, icol);
|
|
||||||
}
|
|
||||||
column_text :: (self: *SqliteStmt, icol: i32) -> string {
|
|
||||||
p := sqlite3_column_text(self.handle, icol);
|
|
||||||
n := sqlite3_column_bytes(self.handle, icol);
|
|
||||||
return sq_copy_bytes(p, xx n);
|
|
||||||
}
|
|
||||||
column_blob :: (self: *SqliteStmt, icol: i32) -> string {
|
|
||||||
p := sqlite3_column_blob(self.handle, icol);
|
|
||||||
n := sqlite3_column_bytes(self.handle, icol);
|
|
||||||
return sq_copy_bytes(p, xx n);
|
|
||||||
}
|
|
||||||
column_bytes :: (self: *SqliteStmt, icol: i32) -> i32 {
|
|
||||||
return sqlite3_column_bytes(self.handle, icol);
|
|
||||||
}
|
|
||||||
column_type :: (self: *SqliteStmt, icol: i32) -> i32 {
|
|
||||||
return sqlite3_column_type(self.handle, icol);
|
|
||||||
}
|
|
||||||
column_count :: (self: *SqliteStmt) -> i32 {
|
|
||||||
return sqlite3_column_count(self.handle);
|
|
||||||
}
|
|
||||||
column_name :: (self: *SqliteStmt, icol: i32) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_column_name(self.handle, icol));
|
|
||||||
}
|
|
||||||
// The declared type from the schema ("" for expressions).
|
|
||||||
column_decltype :: (self: *SqliteStmt, icol: i32) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_column_decltype(self.handle, icol));
|
|
||||||
}
|
|
||||||
column_database_name :: (self: *SqliteStmt, icol: i32) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_column_database_name(self.handle, icol));
|
|
||||||
}
|
|
||||||
column_table_name :: (self: *SqliteStmt, icol: i32) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_column_table_name(self.handle, icol));
|
|
||||||
}
|
|
||||||
column_origin_name :: (self: *SqliteStmt, icol: i32) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_column_origin_name(self.handle, icol));
|
|
||||||
}
|
|
||||||
data_count :: (self: *SqliteStmt) -> i32 {
|
|
||||||
return sqlite3_data_count(self.handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── introspection ──
|
|
||||||
sql :: (self: *SqliteStmt) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_sql(self.handle));
|
|
||||||
}
|
|
||||||
// The SQL with bound parameters substituted; sqlite allocates, we
|
|
||||||
// copy + free.
|
|
||||||
expanded_sql :: (self: *SqliteStmt) -> string {
|
|
||||||
p := sqlite3_expanded_sql(self.handle);
|
|
||||||
s := sq_from_cstr(p);
|
|
||||||
if p != null { sqlite3_free(xx p!); }
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
busy :: (self: *SqliteStmt) -> bool {
|
|
||||||
return sqlite3_stmt_busy(self.handle) != 0;
|
|
||||||
}
|
|
||||||
readonly :: (self: *SqliteStmt) -> bool {
|
|
||||||
return sqlite3_stmt_readonly(self.handle) != 0;
|
|
||||||
}
|
|
||||||
// 0 = normal, 1 = EXPLAIN, 2 = EXPLAIN QUERY PLAN.
|
|
||||||
isexplain :: (self: *SqliteStmt) -> i32 {
|
|
||||||
return sqlite3_stmt_isexplain(self.handle);
|
|
||||||
}
|
|
||||||
status :: (self: *SqliteStmt, op: i32, reset: bool) -> i32 {
|
|
||||||
return sqlite3_stmt_status(self.handle, op, if reset then 1 else 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── table column metadata (ENABLE_COLUMN_METADATA build) ─────────────
|
|
||||||
ColumnMeta :: struct {
|
|
||||||
data_type: string;
|
|
||||||
coll_seq: string;
|
|
||||||
not_null: bool;
|
|
||||||
primary_key: bool;
|
|
||||||
autoinc: bool;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── connections ───────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
// One connection. `open` creates the file per SQLite's defaults;
|
|
||||||
// `open_v2` takes SQLITE_OPEN_* flags; `close` is safe on an
|
|
||||||
// already-closed handle. Schema-qualified queries default to "main".
|
|
||||||
Sqlite :: struct {
|
|
||||||
handle: usize;
|
|
||||||
|
|
||||||
open :: (path: string) -> (Sqlite, !SqliteErr) {
|
|
||||||
h : usize = 0;
|
|
||||||
rc := sqlite3_open(to_cstring(path), @h);
|
|
||||||
if rc != SQLITE_OK {
|
|
||||||
if h != 0 { sqlite3_close(h); }
|
|
||||||
raise error.Open;
|
|
||||||
}
|
|
||||||
return Sqlite.{ handle = h };
|
|
||||||
}
|
|
||||||
|
|
||||||
open_v2 :: (path: string, flags: i32) -> (Sqlite, !SqliteErr) {
|
|
||||||
h : usize = 0;
|
|
||||||
rc := sqlite3_open_v2(to_cstring(path), @h, flags, 0);
|
|
||||||
if rc != SQLITE_OK {
|
|
||||||
if h != 0 { sqlite3_close(h); }
|
|
||||||
raise error.Open;
|
|
||||||
}
|
|
||||||
return Sqlite.{ handle = h };
|
|
||||||
}
|
|
||||||
|
|
||||||
close :: (self: *Sqlite) {
|
|
||||||
if self.handle != 0 { sqlite3_close(self.handle); }
|
|
||||||
self.handle = 0;
|
|
||||||
}
|
|
||||||
// Deferred close: succeeds even with unfinalized statements, the
|
|
||||||
// handle dies once the last one is finalized.
|
|
||||||
close_v2 :: (self: *Sqlite) {
|
|
||||||
if self.handle != 0 { sqlite3_close_v2(self.handle); }
|
|
||||||
self.handle = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run one or more ;-separated statements with no result rows
|
|
||||||
// (DDL, pragmas, BEGIN/COMMIT). Detail via errmsg on failure.
|
|
||||||
exec :: (self: *Sqlite, sql: string) -> !SqliteErr {
|
|
||||||
rc := sqlite3_exec(self.handle, to_cstring(sql), 0, 0, 0);
|
|
||||||
if rc != SQLITE_OK { raise error.Exec; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
prepare :: (self: *Sqlite, sql: string) -> (SqliteStmt, !SqliteErr) {
|
|
||||||
sh : usize = 0;
|
|
||||||
rc := sqlite3_prepare_v2(self.handle, sql.ptr, xx sql.len, @sh, 0);
|
|
||||||
if rc != SQLITE_OK { raise error.Prepare; }
|
|
||||||
return SqliteStmt.{ handle = sh, db = self.handle };
|
|
||||||
}
|
|
||||||
// prepare with SQLITE_PREPARE_* flags (e.g. PERSISTENT for the
|
|
||||||
// statement cache a storage layer keeps hot).
|
|
||||||
prepare_v3 :: (self: *Sqlite, sql: string, flags: u32) -> (SqliteStmt, !SqliteErr) {
|
|
||||||
sh : usize = 0;
|
|
||||||
rc := sqlite3_prepare_v3(self.handle, sql.ptr, xx sql.len, flags, @sh, 0);
|
|
||||||
if rc != SQLITE_OK { raise error.Prepare; }
|
|
||||||
return SqliteStmt.{ handle = sh, db = self.handle };
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── errors ──
|
|
||||||
errmsg :: (self: *Sqlite) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_errmsg(self.handle));
|
|
||||||
}
|
|
||||||
errcode :: (self: *Sqlite) -> i32 {
|
|
||||||
return sqlite3_errcode(self.handle);
|
|
||||||
}
|
|
||||||
extended_errcode :: (self: *Sqlite) -> i32 {
|
|
||||||
return sqlite3_extended_errcode(self.handle);
|
|
||||||
}
|
|
||||||
// Byte offset of the most recent error's token in its SQL, -1 if n/a.
|
|
||||||
error_offset :: (self: *Sqlite) -> i32 {
|
|
||||||
return sqlite3_error_offset(self.handle);
|
|
||||||
}
|
|
||||||
extended_result_codes :: (self: *Sqlite, on: bool) {
|
|
||||||
sqlite3_extended_result_codes(self.handle, if on then 1 else 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── state & control ──
|
|
||||||
busy_timeout :: (self: *Sqlite, ms: i32) {
|
|
||||||
sqlite3_busy_timeout(self.handle, ms);
|
|
||||||
}
|
|
||||||
interrupt :: (self: *Sqlite) {
|
|
||||||
sqlite3_interrupt(self.handle);
|
|
||||||
}
|
|
||||||
is_interrupted :: (self: *Sqlite) -> bool {
|
|
||||||
return sqlite3_is_interrupted(self.handle) != 0;
|
|
||||||
}
|
|
||||||
get_autocommit :: (self: *Sqlite) -> bool {
|
|
||||||
return sqlite3_get_autocommit(self.handle) != 0;
|
|
||||||
}
|
|
||||||
// SQLITE_TXN_NONE / _READ / _WRITE for the "main" schema.
|
|
||||||
txn_state :: (self: *Sqlite) -> i32 {
|
|
||||||
return sqlite3_txn_state(self.handle, to_cstring("main"));
|
|
||||||
}
|
|
||||||
db_filename :: (self: *Sqlite) -> string {
|
|
||||||
return sq_from_cstr(sqlite3_db_filename(self.handle, to_cstring("main")));
|
|
||||||
}
|
|
||||||
// 1 readonly, 0 read-write, -1 no such database name.
|
|
||||||
db_readonly :: (self: *Sqlite) -> i32 {
|
|
||||||
return sqlite3_db_readonly(self.handle, to_cstring("main"));
|
|
||||||
}
|
|
||||||
cacheflush :: (self: *Sqlite) {
|
|
||||||
sqlite3_db_cacheflush(self.handle);
|
|
||||||
}
|
|
||||||
db_release_memory :: (self: *Sqlite) {
|
|
||||||
sqlite3_db_release_memory(self.handle);
|
|
||||||
}
|
|
||||||
last_insert_rowid :: (self: *Sqlite) -> i64 {
|
|
||||||
return sqlite3_last_insert_rowid(self.handle);
|
|
||||||
}
|
|
||||||
set_last_insert_rowid :: (self: *Sqlite, rowid: i64) {
|
|
||||||
sqlite3_set_last_insert_rowid(self.handle, rowid);
|
|
||||||
}
|
|
||||||
changes :: (self: *Sqlite) -> i64 {
|
|
||||||
return sqlite3_changes64(self.handle);
|
|
||||||
}
|
|
||||||
total_changes :: (self: *Sqlite) -> i64 {
|
|
||||||
return sqlite3_total_changes64(self.handle);
|
|
||||||
}
|
|
||||||
// Set a SQLITE_LIMIT_* runtime limit; answers the PRIOR value.
|
|
||||||
// new_val < 0 reads without changing.
|
|
||||||
limit :: (self: *Sqlite, id: i32, new_val: i32) -> i32 {
|
|
||||||
return sqlite3_limit(self.handle, id, new_val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Schema introspection for one column of "main".`table`.
|
|
||||||
table_column_metadata :: (self: *Sqlite, table: string, column: string) -> (ColumnMeta, !SqliteErr) {
|
|
||||||
dt : usize = 0;
|
|
||||||
cs : usize = 0;
|
|
||||||
nn : i32 = 0;
|
|
||||||
pk : i32 = 0;
|
|
||||||
ai : i32 = 0;
|
|
||||||
rc := sqlite3_table_column_metadata(self.handle, to_cstring("main"), to_cstring(table), to_cstring(column), @dt, @cs, @nn, @pk, @ai);
|
|
||||||
if rc != SQLITE_OK { raise error.Metadata; }
|
|
||||||
dtp : ?*u8 = if dt != 0 then cast(?*u8) cast(*u8) dt else null;
|
|
||||||
csp : ?*u8 = if cs != 0 then cast(?*u8) cast(*u8) cs else null;
|
|
||||||
return ColumnMeta.{
|
|
||||||
data_type = sq_from_cstr(dtp), coll_seq = sq_from_cstr(csp),
|
|
||||||
not_null = nn != 0, primary_key = pk != 0, autoinc = ai != 0,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── serialization ──
|
|
||||||
// The whole "main" database as bytes (a valid database image).
|
|
||||||
serialize :: (self: *Sqlite) -> (string, !SqliteErr) {
|
|
||||||
size : i64 = 0;
|
|
||||||
p := sqlite3_serialize(self.handle, to_cstring("main"), @size, 0);
|
|
||||||
if p == null { raise error.Serialize; }
|
|
||||||
out := sq_copy_bytes(p, size);
|
|
||||||
sqlite3_free(xx p!);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
// Replace "main" with the database image in `bytes`. The image is
|
|
||||||
// copied into sqlite-owned memory (FREEONCLOSE+RESIZEABLE), so
|
|
||||||
// `bytes` may die immediately after.
|
|
||||||
deserialize :: (self: *Sqlite, bytes: string) -> !SqliteErr {
|
|
||||||
buf := sqlite3_malloc64(xx (bytes.len));
|
|
||||||
if buf == 0 { raise error.Serialize; }
|
|
||||||
bp : [*]u8 = xx buf;
|
|
||||||
memcpy(bp, bytes.ptr, bytes.len);
|
|
||||||
rc := sqlite3_deserialize(self.handle, to_cstring("main"), buf, bytes.len, bytes.len,
|
|
||||||
SQLITE_DESERIALIZE_FREEONCLOSE | SQLITE_DESERIALIZE_RESIZEABLE);
|
|
||||||
if rc != SQLITE_OK { raise error.Serialize; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── incremental blob I/O ──────────────────────────────────────────────
|
|
||||||
|
|
||||||
// An open handle onto one blob cell ("main".`table`.`column` at `rowid`).
|
|
||||||
// Reserve space with bind_zeroblob, then stream through this. The handle
|
|
||||||
// cannot change a blob's SIZE — that's an UPDATE.
|
|
||||||
SqliteBlob :: struct {
|
|
||||||
handle: usize;
|
|
||||||
|
|
||||||
open :: (db: *Sqlite, table: string, column: string, rowid: i64, writable: bool) -> (SqliteBlob, !SqliteErr) {
|
|
||||||
h : usize = 0;
|
|
||||||
rc := sqlite3_blob_open(db.handle, to_cstring("main"), to_cstring(table), to_cstring(column),
|
|
||||||
rowid, if writable then 1 else 0, @h);
|
|
||||||
if rc != SQLITE_OK { raise error.Blob; }
|
|
||||||
return SqliteBlob.{ handle = h };
|
|
||||||
}
|
|
||||||
// Point this handle at the same column in a DIFFERENT row (cheaper
|
|
||||||
// than close + open).
|
|
||||||
reopen :: (self: *SqliteBlob, rowid: i64) -> !SqliteErr {
|
|
||||||
rc := sqlite3_blob_reopen(self.handle, rowid);
|
|
||||||
if rc != SQLITE_OK { raise error.Blob; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
bytes :: (self: *SqliteBlob) -> i32 {
|
|
||||||
return sqlite3_blob_bytes(self.handle);
|
|
||||||
}
|
|
||||||
read :: (self: *SqliteBlob, offset: i32, n: i32) -> (string, !SqliteErr) {
|
|
||||||
len : i64 = n;
|
|
||||||
raw : [*]u8 = xx context.allocator.alloc_bytes(len + 1);
|
|
||||||
rc := sqlite3_blob_read(self.handle, raw, n, offset);
|
|
||||||
if rc != SQLITE_OK { raise error.Blob; }
|
|
||||||
raw[len] = 0;
|
|
||||||
return string.{ ptr = raw, len = len };
|
|
||||||
}
|
|
||||||
write :: (self: *SqliteBlob, offset: i32, data: string) -> !SqliteErr {
|
|
||||||
rc := sqlite3_blob_write(self.handle, data.ptr, xx data.len, offset);
|
|
||||||
if rc != SQLITE_OK { raise error.Blob; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
close :: (self: *SqliteBlob) {
|
|
||||||
if self.handle != 0 { sqlite3_blob_close(self.handle); }
|
|
||||||
self.handle = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── online backup ─────────────────────────────────────────────────────
|
|
||||||
|
|
||||||
// Copy `src`'s "main" into `dst`'s "main" page by page while both stay
|
|
||||||
// usable. `run` drives it to completion in one call; `step` exposes the
|
|
||||||
// incremental form.
|
|
||||||
SqliteBackup :: struct {
|
|
||||||
handle: usize;
|
|
||||||
|
|
||||||
init :: (dst: *Sqlite, src: *Sqlite) -> (SqliteBackup, !SqliteErr) {
|
|
||||||
h := sqlite3_backup_init(dst.handle, to_cstring("main"), src.handle, to_cstring("main"));
|
|
||||||
if h == 0 { raise error.Backup; }
|
|
||||||
return SqliteBackup.{ handle = h };
|
|
||||||
}
|
|
||||||
// Copy up to `n` pages (-1 = all remaining). SQLITE_OK = more to do,
|
|
||||||
// SQLITE_DONE = complete; BUSY/LOCKED are retryable per the C API.
|
|
||||||
step :: (self: *SqliteBackup, n: i32) -> i32 {
|
|
||||||
return sqlite3_backup_step(self.handle, n);
|
|
||||||
}
|
|
||||||
remaining :: (self: *SqliteBackup) -> i32 {
|
|
||||||
return sqlite3_backup_remaining(self.handle);
|
|
||||||
}
|
|
||||||
pagecount :: (self: *SqliteBackup) -> i32 {
|
|
||||||
return sqlite3_backup_pagecount(self.handle);
|
|
||||||
}
|
|
||||||
finish :: (self: *SqliteBackup) -> i32 {
|
|
||||||
rc := sqlite3_backup_finish(self.handle);
|
|
||||||
self.handle = 0;
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// One-shot full copy of src."main" into dst."main".
|
|
||||||
sqlite_backup_run :: (dst: *Sqlite, src: *Sqlite) -> !SqliteErr {
|
|
||||||
bk := try SqliteBackup.init(dst, src);
|
|
||||||
rc := bk.step(-1);
|
|
||||||
frc := bk.finish();
|
|
||||||
if rc != SQLITE_DONE or frc != SQLITE_OK { raise error.Backup; }
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
@@ -39,7 +39,7 @@
|
|||||||
// one program (the `dist` CLI), which returns a different type.
|
// one program (the `dist` CLI), which returns a different type.
|
||||||
jsonp :: #import "modules/std/json.sx";
|
jsonp :: #import "modules/std/json.sx";
|
||||||
#import "modules/std/fs.sx";
|
#import "modules/std/fs.sx";
|
||||||
#import "../db/sqlite.sx";
|
#import "vendors/sqlite/sqlite.sx";
|
||||||
#import "../domain/platform.sx";
|
#import "../domain/platform.sx";
|
||||||
#import "../domain/app.sx";
|
#import "../domain/app.sx";
|
||||||
#import "../domain/release.sx";
|
#import "../domain/release.sx";
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
#import "modules/std/json.sx";
|
#import "modules/std/json.sx";
|
||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
fs :: #import "modules/std/fs.sx";
|
fs :: #import "modules/std/fs.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
STORE :: ".sx-tmp/db_import";
|
STORE :: ".sx-tmp/db_import";
|
||||||
EMPTY :: ".sx-tmp/db_import_empty";
|
EMPTY :: ".sx-tmp/db_import_empty";
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
#import "modules/std/json.sx";
|
#import "modules/std/json.sx";
|
||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
fs :: #import "modules/std/fs.sx";
|
fs :: #import "modules/std/fs.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
STORE :: ".sx-tmp/publish_fail";
|
STORE :: ".sx-tmp/publish_fail";
|
||||||
MDIR :: ".sx-tmp/publish_fail_m";
|
MDIR :: ".sx-tmp/publish_fail_m";
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
fs :: #import "modules/std/fs.sx";
|
fs :: #import "modules/std/fs.sx";
|
||||||
hash :: #import "modules/std/hash.sx";
|
hash :: #import "modules/std/hash.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
cstd :: #library "c";
|
cstd :: #library "c";
|
||||||
c_getcwd :: (buf: [*]u8, size: usize) -> *u8 #foreign cstd "getcwd";
|
c_getcwd :: (buf: [*]u8, size: usize) -> *u8 #foreign cstd "getcwd";
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#import "modules/std/json.sx";
|
#import "modules/std/json.sx";
|
||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
fs :: #import "modules/std/fs.sx";
|
fs :: #import "modules/std/fs.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
STORE :: ".sx-tmp/publish_persist";
|
STORE :: ".sx-tmp/publish_persist";
|
||||||
MDIR :: ".sx-tmp/publish_persist_m";
|
MDIR :: ".sx-tmp/publish_persist_m";
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
#import "modules/std/json.sx";
|
#import "modules/std/json.sx";
|
||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
fs :: #import "modules/std/fs.sx";
|
fs :: #import "modules/std/fs.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
STORE :: ".sx-tmp/release_ops";
|
STORE :: ".sx-tmp/release_ops";
|
||||||
MDIR :: ".sx-tmp/release_ops_m";
|
MDIR :: ".sx-tmp/release_ops_m";
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
fs :: #import "modules/std/fs.sx";
|
fs :: #import "modules/std/fs.sx";
|
||||||
hash :: #import "modules/std/hash.sx";
|
hash :: #import "modules/std/hash.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
STORE :: ".sx-tmp/remote_publish";
|
STORE :: ".sx-tmp/remote_publish";
|
||||||
MDIR :: ".sx-tmp/remote_publish_m";
|
MDIR :: ".sx-tmp/remote_publish_m";
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
#import "modules/std/fs.sx";
|
#import "modules/std/fs.sx";
|
||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
#import "../src/domain/platform.sx";
|
#import "../src/domain/platform.sx";
|
||||||
#import "../src/domain/app.sx";
|
#import "../src/domain/app.sx";
|
||||||
#import "../src/domain/release.sx";
|
#import "../src/domain/release.sx";
|
||||||
|
|||||||
@@ -8,9 +8,9 @@
|
|||||||
#
|
#
|
||||||
# Locate the compiler via SX (overridable); defaults to the sibling sx repo.
|
# Locate the compiler via SX (overridable); defaults to the sibling sx repo.
|
||||||
#
|
#
|
||||||
# The vendored SQLite needs no flags here: src/db/sqlite.sx declares it
|
# SQLite needs no flags here: the sx library ships it (vendors/sqlite),
|
||||||
# as a `#import c` unit, so `sx run` compiles (cached) and loads it as a
|
# declared as a `#import c` unit — `sx run` compiles (cached) and loads it
|
||||||
# priority symbol target — the version assert in tests/sqlite_smoke.sx
|
# as a priority symbol target; the version assert in tests/sqlite_smoke.sx
|
||||||
# proves the OS copy never shadows it.
|
# proves the OS copy never shadows it.
|
||||||
set -u
|
set -u
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
#import "modules/std/json.sx";
|
#import "modules/std/json.sx";
|
||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
fs :: #import "modules/std/fs.sx";
|
fs :: #import "modules/std/fs.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
STORE :: ".sx-tmp/server_http";
|
STORE :: ".sx-tmp/server_http";
|
||||||
MDIR :: ".sx-tmp/server_http_m";
|
MDIR :: ".sx-tmp/server_http_m";
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
fs :: #import "modules/std/fs.sx";
|
fs :: #import "modules/std/fs.sx";
|
||||||
hash :: #import "modules/std/hash.sx";
|
hash :: #import "modules/std/hash.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
STORE :: ".sx-tmp/server_write";
|
STORE :: ".sx-tmp/server_write";
|
||||||
PORT :: "18793";
|
PORT :: "18793";
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Pinned acceptance for the FULL SQLite mapping (src/db/sqlite.sx) —
|
// Pinned acceptance for the FULL SQLite mapping (vendors/sqlite, shipped with sx) —
|
||||||
// every wrapper family beyond the P5.1 smoke (which keeps owning the
|
// every wrapper family beyond the P5.1 smoke (which keeps owning the
|
||||||
// vendored-version pin):
|
// vendored-version pin):
|
||||||
//
|
//
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
// query answers match.
|
// query answers match.
|
||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
DBDIR :: ".sx-tmp/sqlite_api";
|
DBDIR :: ".sx-tmp/sqlite_api";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// Pinned acceptance for P5.1 — the VENDORED SQLite is compiled and
|
// Pinned acceptance for P5.1 — the sx-shipped SQLite (vendors/sqlite) is
|
||||||
// usable through src/db/sqlite.sx's `#import c` unit.
|
// compiled and usable through its `#import c` unit.
|
||||||
//
|
//
|
||||||
// * version: sqlite3_libversion() equals the vendored amalgamation's
|
// * version: sqlite3_libversion() equals the vendored amalgamation's
|
||||||
// version (vendor/sqlite/README.md) — a silent fallback to the OS
|
// version (vendor/sqlite/README.md) — a silent fallback to the OS
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
#import "modules/std.sx";
|
#import "modules/std.sx";
|
||||||
#import "modules/std/fs.sx";
|
#import "modules/std/fs.sx";
|
||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
VENDORED_VERSION :: "3.53.2";
|
VENDORED_VERSION :: "3.53.2";
|
||||||
DBDIR :: ".sx-tmp/sqlite_smoke";
|
DBDIR :: ".sx-tmp/sqlite_smoke";
|
||||||
|
|||||||
@@ -24,7 +24,7 @@
|
|||||||
process :: #import "modules/std/process.sx";
|
process :: #import "modules/std/process.sx";
|
||||||
fs :: #import "modules/std/fs.sx";
|
fs :: #import "modules/std/fs.sx";
|
||||||
hash :: #import "modules/std/hash.sx";
|
hash :: #import "modules/std/hash.sx";
|
||||||
sq :: #import "../src/db/sqlite.sx";
|
sq :: #import "vendors/sqlite/sqlite.sx";
|
||||||
|
|
||||||
STORE :: ".sx-tmp/token_ops";
|
STORE :: ".sx-tmp/token_ops";
|
||||||
MDIR :: ".sx-tmp/token_ops_m";
|
MDIR :: ".sx-tmp/token_ops_m";
|
||||||
|
|||||||
42
vendor/sqlite/README.md
vendored
42
vendor/sqlite/README.md
vendored
@@ -1,42 +0,0 @@
|
|||||||
# Vendored SQLite
|
|
||||||
|
|
||||||
- Version: **3.53.2** (`SQLITE_VERSION` in `sqlite3.h`)
|
|
||||||
- Source: <https://sqlite.org/2026/sqlite-amalgamation-3530200.zip>
|
|
||||||
- Zip sha256: `8a310d0a16c7a90cacd4c884e70faa51c902afed2a89f63aaa0126ab83558a32`
|
|
||||||
- Files kept: `sqlite3.c`, `sqlite3.h` (the amalgamation; `shell.c` and
|
|
||||||
`sqlite3ext.h` dropped — no shell, no loadable extensions)
|
|
||||||
- License: public domain (<https://sqlite.org/copyright.html>)
|
|
||||||
|
|
||||||
The amalgamation is part of the program, not the build system:
|
|
||||||
`src/db/sqlite.sx` declares it as a named `#import c` unit carrying the
|
|
||||||
pinned compile options (`SQLITE_DQS=0`, `SQLITE_THREADSAFE=0`,
|
|
||||||
`SQLITE_DEFAULT_MEMSTATUS=0`, `SQLITE_OMIT_DEPRECATED`,
|
|
||||||
`SQLITE_OMIT_SHARED_CACHE`, `SQLITE_LIKE_DOESNT_MATCH_BLOBS`,
|
|
||||||
`SQLITE_ENABLE_COLUMN_METADATA`, `-O2`), and every `#foreign sqlib`
|
|
||||||
binding resolves against that unit. sx compiles the unit through its
|
|
||||||
content-addressed object cache (`.sx-cache/`), so the 250k-line source
|
|
||||||
builds once per checkout — `sx build` links the objects into the
|
|
||||||
binary, `sx run` loads them as a PRIORITY symbol-search target ahead of
|
|
||||||
the process images, which is why the OS libsqlite3 (a different
|
|
||||||
version, loaded into the compiler process by CoreServices) can never
|
|
||||||
shadow this copy. `tests/sqlite_smoke.sx` asserts
|
|
||||||
`sqlite3_libversion()` equals the version above, so any fallback fails
|
|
||||||
loudly in both modes.
|
|
||||||
|
|
||||||
## Bound surface
|
|
||||||
|
|
||||||
`src/db/sqlite.sx` maps the full practical C API (~100 functions):
|
|
||||||
connection lifecycle + open_v2 flags, errors (extended codes included),
|
|
||||||
statements with the complete bind/column families, parameter and column
|
|
||||||
introspection (built with `SQLITE_ENABLE_COLUMN_METADATA`), incremental
|
|
||||||
blob I/O, the online backup API, serialize/deserialize, and the library
|
|
||||||
utilities. Not bound, by design: callback-taking APIs (hooks, UDFs,
|
|
||||||
collations, authorizers — they need C→sx callbacks), the
|
|
||||||
`sqlite3_value_*` family (UDF-coupled), varargs configuration, UTF-16
|
|
||||||
variants, and subsystems this build omits (mutex/VFS under
|
|
||||||
`SQLITE_THREADSAFE=0`, sessions/snapshots/vtabs, deprecated API).
|
|
||||||
|
|
||||||
To upgrade: replace `sqlite3.c`/`sqlite3.h` with a newer amalgamation,
|
|
||||||
update this file and the version constant in `tests/sqlite_smoke.sx`,
|
|
||||||
and run `make clean test` (the object cache keys on the source bytes,
|
|
||||||
so the new amalgamation recompiles automatically).
|
|
||||||
269376
vendor/sqlite/sqlite3.c
vendored
269376
vendor/sqlite/sqlite3.c
vendored
File diff suppressed because it is too large
Load Diff
14347
vendor/sqlite/sqlite3.h
vendored
14347
vendor/sqlite/sqlite3.h
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user