sqlite is part of the program: #import c unit replaces the Makefile cc build

src/db/sqlite.sx declares the vendored amalgamation as a named
'#import c' unit (pinned defines + -O2 + #source); every #foreign
binding resolves against it with UNPREFIXED sqlite3_* names. sx
compiles the unit through its content-addressed object cache — once
per checkout — links the objects into 'sx build' binaries, and loads
them as a priority symbol target under 'sx run', so the OS libsqlite3
can never shadow the vendored copy (the version pin in sqlite_smoke
proves it).

Retired: the Makefile vendor targets (cc -> .a + jit/.dylib), the
GENERATED dist_sqlite3_* rename.h (the JIT no longer resolves
program-owned symbols through the process images, so the rename's
reason is gone), and the -L plumbing in make build + tests/run.sh.
make test 22/22; otool -L build/dist carries no libsqlite3.
This commit is contained in:
agra
2026-06-12 17:27:21 +03:00
parent a1f13c4356
commit 06f99b3606
6 changed files with 154 additions and 172 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -9,48 +9,23 @@ BUILD_DIR := build
# Programs compiled by `make build`: the smoke program and the `dist`
# product entry point. Further entry points under src/ get added here as
# they land.
#
# The vendored SQLite (vendor/sqlite/) is part of the PROGRAM, not the
# build system: src/db/sqlite.sx declares it as a named `#import c` unit
# (pinned defines + -O2 + #source), so sx compiles and links it through
# its content-addressed object cache (.sx-cache/) — once per checkout.
# `sx build` links the unit's objects into the binary; `sx run` loads
# them as a priority symbol target the OS libsqlite3 cannot shadow.
SMOKE := tests/smoke.sx
DIST := src/dist.sx
# Vendored SQLite (vendor/sqlite/README.md): one amalgamation, two build
# products in two DIRECTORIES — the macOS linker prefers a dylib over an
# archive in the same -L directory, and `sx build` must link the static
# copy while `sx run` (the test runner) dlopens the dylib.
#
# rename.h is GENERATED from the bindings: every `dist_sqlite3_*` symbol
# named in src/db/sqlite.sx gets a #define, so the rename list and the
# bound surface cannot drift (see the README for why renaming exists).
VENDOR_DIR := $(BUILD_DIR)/vendor
SQLITE_SRC := vendor/sqlite/sqlite3.c
SQLITE_DEFS := -DSQLITE_DQS=0 -DSQLITE_THREADSAFE=0 -DSQLITE_DEFAULT_MEMSTATUS=0 \
-DSQLITE_OMIT_DEPRECATED -DSQLITE_OMIT_SHARED_CACHE \
-DSQLITE_LIKE_DOESNT_MATCH_BLOBS -DSQLITE_ENABLE_COLUMN_METADATA \
-include $(VENDOR_DIR)/rename.h
$(VENDOR_DIR)/rename.h: src/db/sqlite.sx
@mkdir -p $(VENDOR_DIR)
@{ echo '/* GENERATED by make from src/db/sqlite.sx — do not edit. */'; \
grep -o '"dist_sqlite3_[a-z0-9_]*"' src/db/sqlite.sx | tr -d '"' | sort -u | \
sed 's/^dist_\(.*\)/#define \1 dist_\1/'; } > $@
$(VENDOR_DIR)/libsqlite3.a: $(SQLITE_SRC) vendor/sqlite/sqlite3.h $(VENDOR_DIR)/rename.h
@mkdir -p $(VENDOR_DIR)
cc $(SQLITE_DEFS) -O2 -c $(SQLITE_SRC) -o $(VENDOR_DIR)/sqlite3.o
ar rcs $@ $(VENDOR_DIR)/sqlite3.o
$(VENDOR_DIR)/jit/libsqlite3.dylib: $(SQLITE_SRC) vendor/sqlite/sqlite3.h $(VENDOR_DIR)/rename.h
@mkdir -p $(VENDOR_DIR)/jit
cc $(SQLITE_DEFS) -O2 -dynamiclib $(SQLITE_SRC) -o $@
.PHONY: build test publish-example vendor clean
vendor: $(VENDOR_DIR)/libsqlite3.a $(VENDOR_DIR)/jit/libsqlite3.dylib
.PHONY: build test publish-example clean
# Compile the product sources (and the smoke program) without running.
build: vendor
build:
@mkdir -p $(BUILD_DIR)
$(SX) build -o $(BUILD_DIR)/smoke $(SMOKE)
$(SX) build -o $(BUILD_DIR)/dist $(DIST) -L $(VENDOR_DIR)
$(SX) build -o $(BUILD_DIR)/dist $(DIST)
# Run the test runner over every tests/**/*.sx. Exits non-zero on any
# failure. Depends on `build` so the CLI acceptance test (tests/cli_*.sx)
@@ -67,4 +42,4 @@ publish-example: build
./$(BUILD_DIR)/dist ci publish --manifest examples/dist.json --local-store .sx-tmp/publish-example --json
clean:
@rm -rf $(BUILD_DIR)
@rm -rf $(BUILD_DIR) .sx-cache

View File

@@ -2,15 +2,17 @@
// sqlite.sx — sx bindings over the VENDORED SQLite amalgamation
// (vendor/sqlite/, see its README for version + upgrade notes).
//
// `#library "sqlite3"` resolves to the vendored build products under
// build/vendor/ (never the OS copy): `sx build` links the static
// archive via `-L build/vendor`; `sx run` dlopens the dylib via
// `-L build/vendor/jit` (tests/run.sh). Every bound symbol is renamed
// `dist_sqlite3_*` in the vendored build — `build/vendor/rename.h` is
// GENERATED by the Makefile from THIS FILE's `#foreign` names, so the
// rename list and the bindings cannot drift. tests/sqlite_smoke.sx pins
// `sqlite3_libversion()` to the vendored version so a silent fallback
// to the system library fails the suite.
// 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`
@@ -24,7 +26,7 @@
// * 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 compile-time defines in the Makefile;
// 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
@@ -49,124 +51,134 @@
#import "modules/std.sx";
sqlib :: #library "sqlite3";
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 "dist_sqlite3_open";
sqlite3_open_v2 :: (path: cstring, out_db: *usize, flags: i32, vfs: usize) -> i32 #foreign sqlib "dist_sqlite3_open_v2";
sqlite3_close :: (db: usize) -> i32 #foreign sqlib "dist_sqlite3_close";
sqlite3_close_v2 :: (db: usize) -> i32 #foreign sqlib "dist_sqlite3_close_v2";
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 "dist_sqlite3_errcode";
sqlite3_extended_errcode :: (db: usize) -> i32 #foreign sqlib "dist_sqlite3_extended_errcode";
sqlite3_errmsg :: (db: usize) -> ?cstring #foreign sqlib "dist_sqlite3_errmsg";
sqlite3_errstr :: (code: i32) -> ?cstring #foreign sqlib "dist_sqlite3_errstr";
sqlite3_error_offset :: (db: usize) -> i32 #foreign sqlib "dist_sqlite3_error_offset";
sqlite3_extended_result_codes :: (db: usize, onoff: i32) -> i32 #foreign sqlib "dist_sqlite3_extended_result_codes";
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 "dist_sqlite3_busy_timeout";
sqlite3_interrupt :: (db: usize) #foreign sqlib "dist_sqlite3_interrupt";
sqlite3_is_interrupted :: (db: usize) -> i32 #foreign sqlib "dist_sqlite3_is_interrupted";
sqlite3_get_autocommit :: (db: usize) -> i32 #foreign sqlib "dist_sqlite3_get_autocommit";
sqlite3_txn_state :: (db: usize, schema: cstring) -> i32 #foreign sqlib "dist_sqlite3_txn_state";
sqlite3_db_filename :: (db: usize, db_name: cstring) -> ?cstring #foreign sqlib "dist_sqlite3_db_filename";
sqlite3_db_readonly :: (db: usize, db_name: cstring) -> i32 #foreign sqlib "dist_sqlite3_db_readonly";
sqlite3_db_cacheflush :: (db: usize) -> i32 #foreign sqlib "dist_sqlite3_db_cacheflush";
sqlite3_db_release_memory :: (db: usize) -> i32 #foreign sqlib "dist_sqlite3_db_release_memory";
sqlite3_last_insert_rowid :: (db: usize) -> i64 #foreign sqlib "dist_sqlite3_last_insert_rowid";
sqlite3_set_last_insert_rowid :: (db: usize, rowid: i64) #foreign sqlib "dist_sqlite3_set_last_insert_rowid";
sqlite3_changes64 :: (db: usize) -> i64 #foreign sqlib "dist_sqlite3_changes64";
sqlite3_total_changes64 :: (db: usize) -> i64 #foreign sqlib "dist_sqlite3_total_changes64";
sqlite3_limit :: (db: usize, id: i32, new_val: i32) -> i32 #foreign sqlib "dist_sqlite3_limit";
sqlite3_exec :: (db: usize, sql: cstring, cb: usize, arg: usize, errmsg: usize) -> i32 #foreign sqlib "dist_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 "dist_sqlite3_table_column_metadata";
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 "dist_sqlite3_prepare_v2";
sqlite3_prepare_v3 :: (db: usize, sql: [*]u8, nbyte: i32, prep_flags: u32, out_stmt: *usize, out_tail: usize) -> i32 #foreign sqlib "dist_sqlite3_prepare_v3";
sqlite3_step :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_step";
sqlite3_reset :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_reset";
sqlite3_finalize :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_finalize";
sqlite3_clear_bindings :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_clear_bindings";
sqlite3_sql :: (stmt: usize) -> ?cstring #foreign sqlib "dist_sqlite3_sql";
sqlite3_expanded_sql :: (stmt: usize) -> ?cstring #foreign sqlib "dist_sqlite3_expanded_sql";
sqlite3_stmt_busy :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_stmt_busy";
sqlite3_stmt_readonly :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_stmt_readonly";
sqlite3_stmt_isexplain :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_stmt_isexplain";
sqlite3_stmt_explain :: (stmt: usize, mode: i32) -> i32 #foreign sqlib "dist_sqlite3_stmt_explain";
sqlite3_stmt_status :: (stmt: usize, op: i32, reset: i32) -> i32 #foreign sqlib "dist_sqlite3_stmt_status";
sqlite3_db_handle :: (stmt: usize) -> usize #foreign sqlib "dist_sqlite3_db_handle";
sqlite3_next_stmt :: (db: usize, stmt: usize) -> usize #foreign sqlib "dist_sqlite3_next_stmt";
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 "dist_sqlite3_bind_parameter_count";
sqlite3_bind_parameter_index :: (stmt: usize, name: cstring) -> i32 #foreign sqlib "dist_sqlite3_bind_parameter_index";
sqlite3_bind_parameter_name :: (stmt: usize, idx: i32) -> ?cstring #foreign sqlib "dist_sqlite3_bind_parameter_name";
sqlite3_bind_text :: (stmt: usize, idx: i32, text: [*]u8, n: i32, destructor: isize) -> i32 #foreign sqlib "dist_sqlite3_bind_text";
sqlite3_bind_blob :: (stmt: usize, idx: i32, data: [*]u8, n: i32, destructor: isize) -> i32 #foreign sqlib "dist_sqlite3_bind_blob";
sqlite3_bind_double :: (stmt: usize, idx: i32, v: f64) -> i32 #foreign sqlib "dist_sqlite3_bind_double";
sqlite3_bind_int64 :: (stmt: usize, idx: i32, v: i64) -> i32 #foreign sqlib "dist_sqlite3_bind_int64";
sqlite3_bind_null :: (stmt: usize, idx: i32) -> i32 #foreign sqlib "dist_sqlite3_bind_null";
sqlite3_bind_zeroblob64 :: (stmt: usize, idx: i32, n: u64) -> i32 #foreign sqlib "dist_sqlite3_bind_zeroblob64";
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 "dist_sqlite3_column_blob";
sqlite3_column_double :: (stmt: usize, icol: i32) -> f64 #foreign sqlib "dist_sqlite3_column_double";
sqlite3_column_int64 :: (stmt: usize, icol: i32) -> i64 #foreign sqlib "dist_sqlite3_column_int64";
sqlite3_column_text :: (stmt: usize, icol: i32) -> ?*u8 #foreign sqlib "dist_sqlite3_column_text";
sqlite3_column_bytes :: (stmt: usize, icol: i32) -> i32 #foreign sqlib "dist_sqlite3_column_bytes";
sqlite3_column_type :: (stmt: usize, icol: i32) -> i32 #foreign sqlib "dist_sqlite3_column_type";
sqlite3_column_count :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_column_count";
sqlite3_column_name :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "dist_sqlite3_column_name";
sqlite3_column_decltype :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "dist_sqlite3_column_decltype";
sqlite3_column_database_name :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "dist_sqlite3_column_database_name";
sqlite3_column_table_name :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "dist_sqlite3_column_table_name";
sqlite3_column_origin_name :: (stmt: usize, icol: i32) -> ?cstring #foreign sqlib "dist_sqlite3_column_origin_name";
sqlite3_data_count :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_data_count";
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 "dist_sqlite3_blob_open";
sqlite3_blob_reopen :: (blob: usize, rowid: i64) -> i32 #foreign sqlib "dist_sqlite3_blob_reopen";
sqlite3_blob_close :: (blob: usize) -> i32 #foreign sqlib "dist_sqlite3_blob_close";
sqlite3_blob_bytes :: (blob: usize) -> i32 #foreign sqlib "dist_sqlite3_blob_bytes";
sqlite3_blob_read :: (blob: usize, buf: [*]u8, n: i32, offset: i32) -> i32 #foreign sqlib "dist_sqlite3_blob_read";
sqlite3_blob_write :: (blob: usize, buf: [*]u8, n: i32, offset: i32) -> i32 #foreign sqlib "dist_sqlite3_blob_write";
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 "dist_sqlite3_backup_init";
sqlite3_backup_step :: (bk: usize, n_page: i32) -> i32 #foreign sqlib "dist_sqlite3_backup_step";
sqlite3_backup_finish :: (bk: usize) -> i32 #foreign sqlib "dist_sqlite3_backup_finish";
sqlite3_backup_remaining :: (bk: usize) -> i32 #foreign sqlib "dist_sqlite3_backup_remaining";
sqlite3_backup_pagecount :: (bk: usize) -> i32 #foreign sqlib "dist_sqlite3_backup_pagecount";
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 "dist_sqlite3_serialize";
sqlite3_deserialize :: (db: usize, schema: cstring, data: usize, sz_db: i64, sz_buf: i64, flags: u32) -> i32 #foreign sqlib "dist_sqlite3_deserialize";
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 "dist_sqlite3_libversion";
sqlite3_libversion_number :: () -> i32 #foreign sqlib "dist_sqlite3_libversion_number";
sqlite3_sourceid :: () -> ?cstring #foreign sqlib "dist_sqlite3_sourceid";
sqlite3_threadsafe :: () -> i32 #foreign sqlib "dist_sqlite3_threadsafe";
sqlite3_compileoption_used :: (name: cstring) -> i32 #foreign sqlib "dist_sqlite3_compileoption_used";
sqlite3_compileoption_get :: (n: i32) -> ?cstring #foreign sqlib "dist_sqlite3_compileoption_get";
sqlite3_complete :: (sql: cstring) -> i32 #foreign sqlib "dist_sqlite3_complete";
sqlite3_free :: (p: usize) #foreign sqlib "dist_sqlite3_free";
sqlite3_malloc64 :: (n: u64) -> usize #foreign sqlib "dist_sqlite3_malloc64";
sqlite3_msize :: (p: usize) -> u64 #foreign sqlib "dist_sqlite3_msize";
sqlite3_memory_used :: () -> i64 #foreign sqlib "dist_sqlite3_memory_used";
sqlite3_memory_highwater :: (reset: i32) -> i64 #foreign sqlib "dist_sqlite3_memory_highwater";
sqlite3_release_memory :: (n: i32) -> i32 #foreign sqlib "dist_sqlite3_release_memory";
sqlite3_soft_heap_limit64 :: (n: i64) -> i64 #foreign sqlib "dist_sqlite3_soft_heap_limit64";
sqlite3_hard_heap_limit64 :: (n: i64) -> i64 #foreign sqlib "dist_sqlite3_hard_heap_limit64";
sqlite3_randomness :: (n: i32, buf: [*]u8) #foreign sqlib "dist_sqlite3_randomness";
sqlite3_sleep :: (ms: i32) -> i32 #foreign sqlib "dist_sqlite3_sleep";
sqlite3_stricmp :: (a: cstring, b: cstring) -> i32 #foreign sqlib "dist_sqlite3_stricmp";
sqlite3_strnicmp :: (a: cstring, b: cstring, n: i32) -> i32 #foreign sqlib "dist_sqlite3_strnicmp";
sqlite3_strglob :: (glob: cstring, s: cstring) -> i32 #foreign sqlib "dist_sqlite3_strglob";
sqlite3_strlike :: (like: cstring, s: cstring, esc: u32) -> i32 #foreign sqlib "dist_sqlite3_strlike";
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;

View File

@@ -8,15 +8,14 @@
#
# Locate the compiler via SX (overridable); defaults to the sibling sx repo.
#
# `-L build/vendor/jit` lets the JIT dlopen the VENDORED libsqlite3.dylib
# (built by `make build`) instead of falling back to the OS copy — the
# version assert in tests/sqlite_smoke.sx depends on it.
# The vendored SQLite needs no flags here: src/db/sqlite.sx declares it
# as a `#import c` unit, so `sx run` compiles (cached) and loads it as a
# priority symbol target — the version assert in tests/sqlite_smoke.sx
# proves the OS copy never shadows it.
set -u
SX="${SX:-/Users/agra/projects/sx/zig-out/bin/sx}"
TESTS_DIR=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
REPO_DIR=$(CDPATH= cd -- "$TESTS_DIR/.." && pwd)
SX_RUN_FLAGS="-L $REPO_DIR/build/vendor/jit"
pass=0
fail=0
@@ -26,7 +25,7 @@ fail=0
# pass/fail counters survive).
for t in $(find "$TESTS_DIR" -name '*.sx' -type f | sort); do
name=${t#"$TESTS_DIR"/}
if "$SX" run "$t" $SX_RUN_FLAGS >/dev/null 2>&1; then
if "$SX" run "$t" >/dev/null 2>&1; then
printf ' %-44s ok\n' "$name"
pass=$((pass + 1))
else

View File

@@ -7,26 +7,21 @@
`sqlite3ext.h` dropped — no shell, no loadable extensions)
- License: public domain (<https://sqlite.org/copyright.html>)
`make build` compiles this into `build/vendor/libsqlite3.a` (statically
linked into the `dist` binary via `-L build/vendor`) and
`build/vendor/jit/libsqlite3.dylib` (dlopen'd by `sx run`, which is how
`make test` executes the test programs). The two locations are separate
on purpose: the macOS linker prefers a dylib over an archive in the same
search directory, and the AOT binary must link the static copy.
`tests/sqlite_smoke.sx` asserts `sqlite3_libversion()` equals the version
above, so a fallback to the OS libsqlite3 fails loudly in both modes.
## Symbol renaming
The sx JIT resolves `#foreign` symbols via `dlsym(RTLD_DEFAULT)`, which
searches every image already loaded into the process — the OS libsqlite3
is usually among them and wins by load order. So every API function the
bindings use is renamed `dist_sqlite3_*` in the vendored build: those
symbols exist ONLY here, making resolution unambiguous in both JIT and
AOT modes. The rename header is GENERATED by `make` into
`build/vendor/rename.h` from the `#foreign` names in
`src/db/sqlite.sx` — the bindings file is the single source of truth,
and the rename list cannot drift from it.
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
@@ -42,5 +37,6 @@ 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`.
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).