sqlite bindings: nullable C-string returns as ?*u8
The null moves into the type: errmsg/errstr/libversion/sourceid/ column_text/blob/name/decltype/*_name/db_filename/sql/expanded_sql/ bind_parameter_name/serialize return ?*u8 instead of *u8 with manual cast null-checks, and the helpers take the optional directly. Verified that an optional pointer crosses the FFI with the null-pointer niche in both JIT and AOT modes. ?[:0]u8 would be the ideal shape (nullable null-terminated string) but does not resolve in sx yet — it panics LLVM emission; filed as an sx followup. The header comment records the constraint.
This commit is contained in:
@@ -37,10 +37,11 @@
|
||||
// convention — build terminated copies with `cstring`);
|
||||
// `prepare_v2/v3` and `bind_text/blob` take `[*]u8` + explicit byte
|
||||
// length instead, so un-terminated sx string views pass without
|
||||
// copying. Nullable C strings OUT are `*u8` + a cast null-check (the
|
||||
// `getenv` convention — `[:0]u8` has no null), and are COPIED into
|
||||
// `context.allocator` before returning — sqlite's buffers die on the
|
||||
// next step/finalize/close.
|
||||
// copying. Nullable C strings OUT are `?*u8` — the null lives in the
|
||||
// type (`?[:0]u8` would be ideal but does not resolve in sx yet, and a
|
||||
// sentinel-slice return would need a synthesized strlen) — and are
|
||||
// COPIED into `context.allocator` before returning — sqlite's buffers
|
||||
// die on the next step/finalize/close.
|
||||
// =====================================================================
|
||||
|
||||
#import "modules/std.sx";
|
||||
@@ -56,8 +57,8 @@ sqlite3_close_v2 :: (db: usize) -> i32 #foreign sqlib "dist_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) -> *u8 #foreign sqlib "dist_sqlite3_errmsg";
|
||||
sqlite3_errstr :: (code: i32) -> *u8 #foreign sqlib "dist_sqlite3_errstr";
|
||||
sqlite3_errmsg :: (db: usize) -> ?*u8 #foreign sqlib "dist_sqlite3_errmsg";
|
||||
sqlite3_errstr :: (code: i32) -> ?*u8 #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";
|
||||
|
||||
@@ -67,7 +68,7 @@ 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: [:0]u8) -> i32 #foreign sqlib "dist_sqlite3_txn_state";
|
||||
sqlite3_db_filename :: (db: usize, db_name: [:0]u8) -> *u8 #foreign sqlib "dist_sqlite3_db_filename";
|
||||
sqlite3_db_filename :: (db: usize, db_name: [:0]u8) -> ?*u8 #foreign sqlib "dist_sqlite3_db_filename";
|
||||
sqlite3_db_readonly :: (db: usize, db_name: [:0]u8) -> 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";
|
||||
@@ -86,8 +87,8 @@ 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) -> *u8 #foreign sqlib "dist_sqlite3_sql";
|
||||
sqlite3_expanded_sql :: (stmt: usize) -> *u8 #foreign sqlib "dist_sqlite3_expanded_sql";
|
||||
sqlite3_sql :: (stmt: usize) -> ?*u8 #foreign sqlib "dist_sqlite3_sql";
|
||||
sqlite3_expanded_sql :: (stmt: usize) -> ?*u8 #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";
|
||||
@@ -99,7 +100,7 @@ sqlite3_next_stmt :: (db: usize, stmt: usize) -> usize #foreign sqlib "dist_s
|
||||
// ── FFI: binding ──────────────────────────────────────────────────────
|
||||
sqlite3_bind_parameter_count :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_bind_parameter_count";
|
||||
sqlite3_bind_parameter_index :: (stmt: usize, name: [:0]u8) -> i32 #foreign sqlib "dist_sqlite3_bind_parameter_index";
|
||||
sqlite3_bind_parameter_name :: (stmt: usize, idx: i32) -> *u8 #foreign sqlib "dist_sqlite3_bind_parameter_name";
|
||||
sqlite3_bind_parameter_name :: (stmt: usize, idx: i32) -> ?*u8 #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";
|
||||
@@ -108,18 +109,18 @@ sqlite3_bind_null :: (stmt: usize, idx: i32) -> i32 #foreign sqlib "dist_sqli
|
||||
sqlite3_bind_zeroblob64 :: (stmt: usize, idx: i32, n: u64) -> i32 #foreign sqlib "dist_sqlite3_bind_zeroblob64";
|
||||
|
||||
// ── FFI: result columns ───────────────────────────────────────────────
|
||||
sqlite3_column_blob :: (stmt: usize, icol: i32) -> *u8 #foreign sqlib "dist_sqlite3_column_blob";
|
||||
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_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) -> *u8 #foreign sqlib "dist_sqlite3_column_name";
|
||||
sqlite3_column_decltype :: (stmt: usize, icol: i32) -> *u8 #foreign sqlib "dist_sqlite3_column_decltype";
|
||||
sqlite3_column_database_name :: (stmt: usize, icol: i32) -> *u8 #foreign sqlib "dist_sqlite3_column_database_name";
|
||||
sqlite3_column_table_name :: (stmt: usize, icol: i32) -> *u8 #foreign sqlib "dist_sqlite3_column_table_name";
|
||||
sqlite3_column_origin_name :: (stmt: usize, icol: i32) -> *u8 #foreign sqlib "dist_sqlite3_column_origin_name";
|
||||
sqlite3_column_name :: (stmt: usize, icol: i32) -> ?*u8 #foreign sqlib "dist_sqlite3_column_name";
|
||||
sqlite3_column_decltype :: (stmt: usize, icol: i32) -> ?*u8 #foreign sqlib "dist_sqlite3_column_decltype";
|
||||
sqlite3_column_database_name :: (stmt: usize, icol: i32) -> ?*u8 #foreign sqlib "dist_sqlite3_column_database_name";
|
||||
sqlite3_column_table_name :: (stmt: usize, icol: i32) -> ?*u8 #foreign sqlib "dist_sqlite3_column_table_name";
|
||||
sqlite3_column_origin_name :: (stmt: usize, icol: i32) -> ?*u8 #foreign sqlib "dist_sqlite3_column_origin_name";
|
||||
sqlite3_data_count :: (stmt: usize) -> i32 #foreign sqlib "dist_sqlite3_data_count";
|
||||
|
||||
// ── FFI: incremental blob I/O ─────────────────────────────────────────
|
||||
@@ -138,16 +139,16 @@ sqlite3_backup_remaining :: (bk: usize) -> i32 #foreign sqlib "dist_sqlite3_back
|
||||
sqlite3_backup_pagecount :: (bk: usize) -> i32 #foreign sqlib "dist_sqlite3_backup_pagecount";
|
||||
|
||||
// ── FFI: serialization ────────────────────────────────────────────────
|
||||
sqlite3_serialize :: (db: usize, schema: [:0]u8, out_size: *i64, flags: u32) -> usize #foreign sqlib "dist_sqlite3_serialize";
|
||||
sqlite3_serialize :: (db: usize, schema: [:0]u8, out_size: *i64, flags: u32) -> ?*u8 #foreign sqlib "dist_sqlite3_serialize";
|
||||
sqlite3_deserialize :: (db: usize, schema: [:0]u8, data: usize, sz_db: i64, sz_buf: i64, flags: u32) -> i32 #foreign sqlib "dist_sqlite3_deserialize";
|
||||
|
||||
// ── FFI: library utilities ────────────────────────────────────────────
|
||||
sqlite3_libversion :: () -> *u8 #foreign sqlib "dist_sqlite3_libversion";
|
||||
sqlite3_libversion :: () -> ?*u8 #foreign sqlib "dist_sqlite3_libversion";
|
||||
sqlite3_libversion_number :: () -> i32 #foreign sqlib "dist_sqlite3_libversion_number";
|
||||
sqlite3_sourceid :: () -> *u8 #foreign sqlib "dist_sqlite3_sourceid";
|
||||
sqlite3_sourceid :: () -> ?*u8 #foreign sqlib "dist_sqlite3_sourceid";
|
||||
sqlite3_threadsafe :: () -> i32 #foreign sqlib "dist_sqlite3_threadsafe";
|
||||
sqlite3_compileoption_used :: (name: [:0]u8) -> i32 #foreign sqlib "dist_sqlite3_compileoption_used";
|
||||
sqlite3_compileoption_get :: (n: i32) -> *u8 #foreign sqlib "dist_sqlite3_compileoption_get";
|
||||
sqlite3_compileoption_get :: (n: i32) -> ?*u8 #foreign sqlib "dist_sqlite3_compileoption_get";
|
||||
sqlite3_complete :: (sql: [:0]u8) -> 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";
|
||||
@@ -304,10 +305,9 @@ sq_cstr :: (s: string) -> string {
|
||||
}
|
||||
|
||||
// Copy the C string at `p` (null = empty) into context.allocator.
|
||||
sq_from_cstr :: (p: *u8) -> string {
|
||||
addr : i64 = xx p;
|
||||
if addr == 0 { return ""; }
|
||||
cp : [*]u8 = xx p;
|
||||
sq_from_cstr :: (p: ?*u8) -> string {
|
||||
if p == null { return ""; }
|
||||
cp : [*]u8 = xx p!;
|
||||
n := 0;
|
||||
while cp[n] != 0 { n += 1; }
|
||||
raw : [*]u8 = xx context.allocator.alloc_bytes(n + 1);
|
||||
@@ -317,10 +317,9 @@ sq_from_cstr :: (p: *u8) -> string {
|
||||
}
|
||||
|
||||
// `n` raw bytes at `p` as a context.allocator copy ("" when p is null).
|
||||
sq_copy_bytes :: (p: *u8, n: i64) -> string {
|
||||
addr : i64 = xx p;
|
||||
if addr == 0 or n <= 0 { return ""; }
|
||||
cp : [*]u8 = xx p;
|
||||
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;
|
||||
@@ -523,8 +522,7 @@ SqliteStmt :: struct {
|
||||
expanded_sql :: (self: *SqliteStmt) -> string {
|
||||
p := sqlite3_expanded_sql(self.handle);
|
||||
s := sq_from_cstr(p);
|
||||
addr : i64 = xx p;
|
||||
if addr != 0 { sqlite3_free(xx addr); }
|
||||
if p != null { sqlite3_free(xx p!); }
|
||||
return s;
|
||||
}
|
||||
busy :: (self: *SqliteStmt) -> bool {
|
||||
@@ -688,8 +686,8 @@ Sqlite :: struct {
|
||||
ai : i32 = 0;
|
||||
rc := sqlite3_table_column_metadata(self.handle, sq_cstr("main"), sq_cstr(table), sq_cstr(column), @dt, @cs, @nn, @pk, @ai);
|
||||
if rc != SQLITE_OK { raise error.Metadata; }
|
||||
dtp : *u8 = xx dt;
|
||||
csp : *u8 = xx cs;
|
||||
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,
|
||||
@@ -701,10 +699,9 @@ Sqlite :: struct {
|
||||
serialize :: (self: *Sqlite) -> (string, !SqliteErr) {
|
||||
size : i64 = 0;
|
||||
p := sqlite3_serialize(self.handle, sq_cstr("main"), @size, 0);
|
||||
if p == 0 { raise error.Serialize; }
|
||||
bp : *u8 = xx p;
|
||||
out := sq_copy_bytes(bp, size);
|
||||
sqlite3_free(p);
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user