Skip to main content

sqlite_provider_sqlite3/
lib.rs

1//! Dynamic `libsqlite3` backend adapter for `sqlite-provider`.
2
3#![allow(non_camel_case_types)]
4
5use libc::{c_char, c_int, c_uchar, c_void};
6use sqlite_provider::{
7    ApiVersion, ColumnMetadata, Error, ErrorCode, FeatureSet, FunctionFlags, OpenFlags,
8    OpenOptions, OwnedBytes, RawBytes, Result, Sqlite3Api, Sqlite3Backup, Sqlite3BlobIo,
9    Sqlite3Hooks, Sqlite3Metadata, Sqlite3Serialize, Sqlite3Wal, StepResult, ValueType,
10};
11use std::ffi::{CStr, CString};
12use std::ptr::{NonNull, null, null_mut};
13use std::sync::OnceLock;
14
15#[cfg(target_os = "linux")]
16#[link(name = "dl")]
17unsafe extern "C" {}
18
19const SQLITE_OK: i32 = 0;
20const SQLITE_MISUSE: i32 = 21;
21const SQLITE_ROW: i32 = 100;
22const SQLITE_DONE: i32 = 101;
23const SQLITE_DESERIALIZE_FREEONCLOSE: u32 = 0x0000_0001;
24
25const SQLITE_OPEN_READONLY: i32 = 0x0000_0001;
26const SQLITE_OPEN_READWRITE: i32 = 0x0000_0002;
27const SQLITE_OPEN_CREATE: i32 = 0x0000_0004;
28const SQLITE_OPEN_URI: i32 = 0x0000_0040;
29const SQLITE_OPEN_NOMUTEX: i32 = 0x0000_8000;
30const SQLITE_OPEN_FULLMUTEX: i32 = 0x0001_0000;
31const SQLITE_OPEN_SHAREDCACHE: i32 = 0x0002_0000;
32const SQLITE_OPEN_PRIVATECACHE: i32 = 0x0004_0000;
33const SQLITE_OPEN_EXRESCODE: i32 = 0x0200_0000;
34
35const SQLITE_UTF8: i32 = 0x0000_0001;
36const SQLITE_DETERMINISTIC: i32 = 0x0000_0800;
37const SQLITE_INNOCUOUS: i32 = 0x0002_0000;
38const SQLITE_DIRECTONLY: i32 = 0x0008_0000;
39const EMPTY_BYTE: u8 = 0;
40
41type sqlite3 = c_void;
42type sqlite3_stmt = c_void;
43type sqlite3_value = c_void;
44type sqlite3_context = c_void;
45type sqlite3_backup = c_void;
46type sqlite3_blob = c_void;
47
48type sqlite3_destructor_type = Option<unsafe extern "C" fn(*mut c_void)>;
49
50type OpenV2 = unsafe extern "C" fn(*const c_char, *mut *mut sqlite3, c_int, *const c_char) -> c_int;
51type Close = unsafe extern "C" fn(*mut sqlite3) -> c_int;
52type PrepareV2 = unsafe extern "C" fn(
53    *mut sqlite3,
54    *const c_char,
55    c_int,
56    *mut *mut sqlite3_stmt,
57    *mut *const c_char,
58) -> c_int;
59type PrepareV3 = unsafe extern "C" fn(
60    *mut sqlite3,
61    *const c_char,
62    c_int,
63    u32,
64    *mut *mut sqlite3_stmt,
65    *mut *const c_char,
66) -> c_int;
67type Step = unsafe extern "C" fn(*mut sqlite3_stmt) -> c_int;
68type Reset = unsafe extern "C" fn(*mut sqlite3_stmt) -> c_int;
69type Finalize = unsafe extern "C" fn(*mut sqlite3_stmt) -> c_int;
70
71type BindNull = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> c_int;
72type BindInt64 = unsafe extern "C" fn(*mut sqlite3_stmt, c_int, i64) -> c_int;
73type BindDouble = unsafe extern "C" fn(*mut sqlite3_stmt, c_int, f64) -> c_int;
74type BindText = unsafe extern "C" fn(
75    *mut sqlite3_stmt,
76    c_int,
77    *const c_char,
78    c_int,
79    sqlite3_destructor_type,
80) -> c_int;
81type BindBlob = unsafe extern "C" fn(
82    *mut sqlite3_stmt,
83    c_int,
84    *const c_void,
85    c_int,
86    sqlite3_destructor_type,
87) -> c_int;
88
89type ColumnCount = unsafe extern "C" fn(*mut sqlite3_stmt) -> c_int;
90type ColumnType = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> c_int;
91type ColumnInt64 = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> i64;
92type ColumnDouble = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> f64;
93type ColumnText = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> *const c_uchar;
94type ColumnBlob = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> *const c_void;
95type ColumnBytes = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> c_int;
96
97type ErrCode = unsafe extern "C" fn(*mut sqlite3) -> c_int;
98type ErrMsg = unsafe extern "C" fn(*mut sqlite3) -> *const c_char;
99type ExtendedErrCode = unsafe extern "C" fn(*mut sqlite3) -> c_int;
100
101type CreateFunctionV2 = unsafe extern "C" fn(
102    *mut sqlite3,
103    *const c_char,
104    c_int,
105    c_int,
106    *mut c_void,
107    Option<extern "C" fn(*mut sqlite3_context, c_int, *mut *mut sqlite3_value)>,
108    Option<extern "C" fn(*mut sqlite3_context, c_int, *mut *mut sqlite3_value)>,
109    Option<extern "C" fn(*mut sqlite3_context)>,
110    Option<extern "C" fn(*mut c_void)>,
111) -> c_int;
112
113type CreateWindowFunction = unsafe extern "C" fn(
114    *mut sqlite3,
115    *const c_char,
116    c_int,
117    c_int,
118    *mut c_void,
119    Option<extern "C" fn(*mut sqlite3_context, c_int, *mut *mut sqlite3_value)>,
120    Option<extern "C" fn(*mut sqlite3_context)>,
121    Option<extern "C" fn(*mut sqlite3_context)>,
122    Option<extern "C" fn(*mut sqlite3_context, c_int, *mut *mut sqlite3_value)>,
123    Option<extern "C" fn(*mut c_void)>,
124) -> c_int;
125type CreateCollationV2 = unsafe extern "C" fn(
126    *mut sqlite3,
127    *const c_char,
128    c_int,
129    *mut c_void,
130    Option<extern "C" fn(*mut c_void, c_int, *const c_void, c_int, *const c_void) -> c_int>,
131    Option<extern "C" fn(*mut c_void)>,
132) -> c_int;
133
134type AggregateContext = unsafe extern "C" fn(*mut sqlite3_context, c_int) -> *mut c_void;
135type ResultNull = unsafe extern "C" fn(*mut sqlite3_context);
136type ResultInt64 = unsafe extern "C" fn(*mut sqlite3_context, i64);
137type ResultDouble = unsafe extern "C" fn(*mut sqlite3_context, f64);
138type ResultText =
139    unsafe extern "C" fn(*mut sqlite3_context, *const c_char, c_int, sqlite3_destructor_type);
140type ResultBlob =
141    unsafe extern "C" fn(*mut sqlite3_context, *const c_void, c_int, sqlite3_destructor_type);
142type ResultError = unsafe extern "C" fn(*mut sqlite3_context, *const c_char, c_int);
143type UserData = unsafe extern "C" fn(*mut sqlite3_context) -> *mut c_void;
144
145type ValueTypeFn = unsafe extern "C" fn(*mut sqlite3_value) -> c_int;
146type ValueInt64Fn = unsafe extern "C" fn(*mut sqlite3_value) -> i64;
147type ValueDoubleFn = unsafe extern "C" fn(*mut sqlite3_value) -> f64;
148type ValueTextFn = unsafe extern "C" fn(*mut sqlite3_value) -> *const c_uchar;
149type ValueBlobFn = unsafe extern "C" fn(*mut sqlite3_value) -> *const c_void;
150type ValueBytesFn = unsafe extern "C" fn(*mut sqlite3_value) -> c_int;
151
152type DeclareVTab = unsafe extern "C" fn(*mut sqlite3, *const c_char) -> c_int;
153type CreateModuleV2 = unsafe extern "C" fn(
154    *mut sqlite3,
155    *const c_char,
156    *const c_void,
157    *mut c_void,
158    Option<extern "C" fn(*mut c_void)>,
159) -> c_int;
160
161type LibversionNumber = unsafe extern "C" fn() -> c_int;
162type Threadsafe = unsafe extern "C" fn() -> c_int;
163
164type Malloc = unsafe extern "C" fn(c_int) -> *mut c_void;
165type Free = unsafe extern "C" fn(*mut c_void);
166
167type ColumnDecltype = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> *const c_char;
168type ColumnName = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> *const c_char;
169type ColumnTableName = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> *const c_char;
170type TableColumnMetadata = unsafe extern "C" fn(
171    *mut sqlite3,
172    *const c_char,
173    *const c_char,
174    *const c_char,
175    *mut *const c_char,
176    *mut *const c_char,
177    *mut c_int,
178    *mut c_int,
179    *mut c_int,
180) -> c_int;
181type DbFilename = unsafe extern "C" fn(*mut sqlite3, *const c_char) -> *const c_char;
182type GetAutocommit = unsafe extern "C" fn(*mut sqlite3) -> c_int;
183type TotalChanges = unsafe extern "C" fn(*mut sqlite3) -> c_int;
184type Changes = unsafe extern "C" fn(*mut sqlite3) -> c_int;
185type Changes64 = unsafe extern "C" fn(*mut sqlite3) -> i64;
186type LastInsertRowid = unsafe extern "C" fn(*mut sqlite3) -> i64;
187type Interrupt = unsafe extern "C" fn(*mut sqlite3);
188type DbConfig = unsafe extern "C" fn(*mut sqlite3, c_int, ...) -> c_int;
189type Limit = unsafe extern "C" fn(*mut sqlite3, c_int, c_int) -> c_int;
190type StmtReadonly = unsafe extern "C" fn(*mut sqlite3_stmt) -> c_int;
191type StmtBusy = unsafe extern "C" fn(*mut sqlite3_stmt) -> c_int;
192type BindParameterCount = unsafe extern "C" fn(*mut sqlite3_stmt) -> c_int;
193type BindParameterName = unsafe extern "C" fn(*mut sqlite3_stmt, c_int) -> *const c_char;
194type BindParameterIndex = unsafe extern "C" fn(*mut sqlite3_stmt, *const c_char) -> c_int;
195type ContextDbHandle = unsafe extern "C" fn(*mut sqlite3_context) -> *mut sqlite3;
196type TraceV2 = unsafe extern "C" fn(
197    *mut sqlite3,
198    u32,
199    Option<extern "C" fn(u32, *mut c_void, *mut c_void, *mut c_void)>,
200    *mut c_void,
201) -> c_int;
202type ProgressHandler = unsafe extern "C" fn(
203    *mut sqlite3,
204    c_int,
205    Option<extern "C" fn(*mut c_void) -> c_int>,
206    *mut c_void,
207);
208type BusyTimeout = unsafe extern "C" fn(*mut sqlite3, c_int) -> c_int;
209type SetAuthorizer = unsafe extern "C" fn(
210    *mut sqlite3,
211    Option<
212        extern "C" fn(
213            *mut c_void,
214            c_int,
215            *const c_char,
216            *const c_char,
217            *const c_char,
218            *const c_char,
219        ) -> c_int,
220    >,
221    *mut c_void,
222) -> c_int;
223type BackupInit = unsafe extern "C" fn(
224    *mut sqlite3,
225    *const c_char,
226    *mut sqlite3,
227    *const c_char,
228) -> *mut sqlite3_backup;
229type BackupStep = unsafe extern "C" fn(*mut sqlite3_backup, c_int) -> c_int;
230type BackupRemaining = unsafe extern "C" fn(*mut sqlite3_backup) -> c_int;
231type BackupPagecount = unsafe extern "C" fn(*mut sqlite3_backup) -> c_int;
232type BackupFinish = unsafe extern "C" fn(*mut sqlite3_backup) -> c_int;
233type BlobOpen = unsafe extern "C" fn(
234    *mut sqlite3,
235    *const c_char,
236    *const c_char,
237    *const c_char,
238    i64,
239    c_int,
240    *mut *mut sqlite3_blob,
241) -> c_int;
242type BlobRead = unsafe extern "C" fn(*mut sqlite3_blob, *mut c_void, c_int, c_int) -> c_int;
243type BlobWrite = unsafe extern "C" fn(*mut sqlite3_blob, *const c_void, c_int, c_int) -> c_int;
244type BlobBytes = unsafe extern "C" fn(*mut sqlite3_blob) -> c_int;
245type BlobClose = unsafe extern "C" fn(*mut sqlite3_blob) -> c_int;
246type Serialize = unsafe extern "C" fn(*mut sqlite3, *const c_char, *mut i64, u32) -> *mut c_uchar;
247type Deserialize =
248    unsafe extern "C" fn(*mut sqlite3, *const c_char, *mut c_uchar, i64, i64, u32) -> c_int;
249type WalCheckpoint = unsafe extern "C" fn(*mut sqlite3, *const c_char) -> c_int;
250type WalCheckpointV2 =
251    unsafe extern "C" fn(*mut sqlite3, *const c_char, c_int, *mut c_int, *mut c_int) -> c_int;
252type LibsqlWalFrameCount = unsafe extern "C" fn(*mut sqlite3, *mut u32) -> c_int;
253
254struct LibHandle {
255    handle: *mut c_void,
256}
257
258unsafe impl Send for LibHandle {}
259unsafe impl Sync for LibHandle {}
260
261#[allow(unsafe_op_in_unsafe_fn)]
262impl LibHandle {
263    unsafe fn open() -> Option<Self> {
264        let mut handle = null_mut();
265        for name in lib_names() {
266            let cstr = CStr::from_bytes_with_nul_unchecked(name);
267            handle = libc::dlopen(cstr.as_ptr(), libc::RTLD_LAZY | libc::RTLD_LOCAL);
268            if !handle.is_null() {
269                break;
270            }
271        }
272        if handle.is_null() {
273            None
274        } else {
275            Some(Self { handle })
276        }
277    }
278
279    unsafe fn symbol<T>(&self, name: &'static [u8]) -> Option<T>
280    where
281        T: Copy,
282    {
283        let sym = libc::dlsym(self.handle, name.as_ptr() as *const c_char);
284        if sym.is_null() {
285            None
286        } else {
287            debug_assert_eq!(std::mem::size_of::<T>(), std::mem::size_of::<*mut c_void>());
288            Some(std::mem::transmute_copy(&sym))
289        }
290    }
291}
292
293struct LibSqlite3Fns {
294    open_v2: OpenV2,
295    close: Close,
296    prepare_v2: PrepareV2,
297    prepare_v3: Option<PrepareV3>,
298    step: Step,
299    reset: Reset,
300    finalize: Finalize,
301    bind_null: BindNull,
302    bind_int64: BindInt64,
303    bind_double: BindDouble,
304    bind_text: BindText,
305    bind_blob: BindBlob,
306    column_count: ColumnCount,
307    column_type: ColumnType,
308    column_int64: ColumnInt64,
309    column_double: ColumnDouble,
310    column_text: ColumnText,
311    column_blob: ColumnBlob,
312    column_bytes: ColumnBytes,
313    errcode: ErrCode,
314    errmsg: ErrMsg,
315    extended_errcode: Option<ExtendedErrCode>,
316    create_function_v2: CreateFunctionV2,
317    create_window_function: Option<CreateWindowFunction>,
318    create_collation_v2: Option<CreateCollationV2>,
319    aggregate_context: AggregateContext,
320    result_null: ResultNull,
321    result_int64: ResultInt64,
322    result_double: ResultDouble,
323    result_text: ResultText,
324    result_blob: ResultBlob,
325    result_error: ResultError,
326    user_data: UserData,
327    value_type: ValueTypeFn,
328    value_int64: ValueInt64Fn,
329    value_double: ValueDoubleFn,
330    value_text: ValueTextFn,
331    value_blob: ValueBlobFn,
332    value_bytes: ValueBytesFn,
333    declare_vtab: DeclareVTab,
334    create_module_v2: Option<CreateModuleV2>,
335    libversion_number: LibversionNumber,
336    threadsafe: Option<Threadsafe>,
337    malloc: Malloc,
338    free: Free,
339    column_decltype: ColumnDecltype,
340    column_name: ColumnName,
341    column_table_name: Option<ColumnTableName>,
342    table_column_metadata: Option<TableColumnMetadata>,
343    db_filename: Option<DbFilename>,
344    get_autocommit: Option<GetAutocommit>,
345    total_changes: Option<TotalChanges>,
346    changes: Option<Changes>,
347    changes64: Option<Changes64>,
348    last_insert_rowid: Option<LastInsertRowid>,
349    interrupt: Option<Interrupt>,
350    db_config: Option<DbConfig>,
351    limit: Option<Limit>,
352    stmt_readonly: Option<StmtReadonly>,
353    stmt_busy: Option<StmtBusy>,
354    bind_parameter_count: Option<BindParameterCount>,
355    bind_parameter_name: Option<BindParameterName>,
356    bind_parameter_index: Option<BindParameterIndex>,
357    context_db_handle: Option<ContextDbHandle>,
358    trace_v2: Option<TraceV2>,
359    progress_handler: Option<ProgressHandler>,
360    busy_timeout: Option<BusyTimeout>,
361    set_authorizer: Option<SetAuthorizer>,
362    backup_init: Option<BackupInit>,
363    backup_step: Option<BackupStep>,
364    backup_remaining: Option<BackupRemaining>,
365    backup_pagecount: Option<BackupPagecount>,
366    backup_finish: Option<BackupFinish>,
367    blob_open: Option<BlobOpen>,
368    blob_read: Option<BlobRead>,
369    blob_write: Option<BlobWrite>,
370    blob_bytes: Option<BlobBytes>,
371    blob_close: Option<BlobClose>,
372    serialize: Option<Serialize>,
373    deserialize: Option<Deserialize>,
374    wal_checkpoint: Option<WalCheckpoint>,
375    wal_checkpoint_v2: Option<WalCheckpointV2>,
376    libsql_wal_frame_count: Option<LibsqlWalFrameCount>,
377}
378
379#[allow(unsafe_op_in_unsafe_fn)]
380impl LibSqlite3Fns {
381    unsafe fn load(lib: &LibHandle) -> Option<Self> {
382        Some(Self {
383            open_v2: lib.symbol(b"sqlite3_open_v2\0")?,
384            close: lib.symbol(b"sqlite3_close\0")?,
385            prepare_v2: lib.symbol(b"sqlite3_prepare_v2\0")?,
386            prepare_v3: lib.symbol(b"sqlite3_prepare_v3\0"),
387            step: lib.symbol(b"sqlite3_step\0")?,
388            reset: lib.symbol(b"sqlite3_reset\0")?,
389            finalize: lib.symbol(b"sqlite3_finalize\0")?,
390            bind_null: lib.symbol(b"sqlite3_bind_null\0")?,
391            bind_int64: lib.symbol(b"sqlite3_bind_int64\0")?,
392            bind_double: lib.symbol(b"sqlite3_bind_double\0")?,
393            bind_text: lib.symbol(b"sqlite3_bind_text\0")?,
394            bind_blob: lib.symbol(b"sqlite3_bind_blob\0")?,
395            column_count: lib.symbol(b"sqlite3_column_count\0")?,
396            column_type: lib.symbol(b"sqlite3_column_type\0")?,
397            column_int64: lib.symbol(b"sqlite3_column_int64\0")?,
398            column_double: lib.symbol(b"sqlite3_column_double\0")?,
399            column_text: lib.symbol(b"sqlite3_column_text\0")?,
400            column_blob: lib.symbol(b"sqlite3_column_blob\0")?,
401            column_bytes: lib.symbol(b"sqlite3_column_bytes\0")?,
402            errcode: lib.symbol(b"sqlite3_errcode\0")?,
403            errmsg: lib.symbol(b"sqlite3_errmsg\0")?,
404            extended_errcode: lib.symbol(b"sqlite3_extended_errcode\0"),
405            create_function_v2: lib.symbol(b"sqlite3_create_function_v2\0")?,
406            create_window_function: lib.symbol(b"sqlite3_create_window_function\0"),
407            create_collation_v2: lib.symbol(b"sqlite3_create_collation_v2\0"),
408            aggregate_context: lib.symbol(b"sqlite3_aggregate_context\0")?,
409            result_null: lib.symbol(b"sqlite3_result_null\0")?,
410            result_int64: lib.symbol(b"sqlite3_result_int64\0")?,
411            result_double: lib.symbol(b"sqlite3_result_double\0")?,
412            result_text: lib.symbol(b"sqlite3_result_text\0")?,
413            result_blob: lib.symbol(b"sqlite3_result_blob\0")?,
414            result_error: lib.symbol(b"sqlite3_result_error\0")?,
415            user_data: lib.symbol(b"sqlite3_user_data\0")?,
416            value_type: lib.symbol(b"sqlite3_value_type\0")?,
417            value_int64: lib.symbol(b"sqlite3_value_int64\0")?,
418            value_double: lib.symbol(b"sqlite3_value_double\0")?,
419            value_text: lib.symbol(b"sqlite3_value_text\0")?,
420            value_blob: lib.symbol(b"sqlite3_value_blob\0")?,
421            value_bytes: lib.symbol(b"sqlite3_value_bytes\0")?,
422            declare_vtab: lib.symbol(b"sqlite3_declare_vtab\0")?,
423            create_module_v2: lib.symbol(b"sqlite3_create_module_v2\0"),
424            libversion_number: lib.symbol(b"sqlite3_libversion_number\0")?,
425            threadsafe: lib.symbol(b"sqlite3_threadsafe\0"),
426            malloc: lib.symbol(b"sqlite3_malloc\0")?,
427            free: lib.symbol(b"sqlite3_free\0")?,
428            column_decltype: lib.symbol(b"sqlite3_column_decltype\0")?,
429            column_name: lib.symbol(b"sqlite3_column_name\0")?,
430            column_table_name: lib.symbol(b"sqlite3_column_table_name\0"),
431            table_column_metadata: lib.symbol(b"sqlite3_table_column_metadata\0"),
432            db_filename: lib.symbol(b"sqlite3_db_filename\0"),
433            get_autocommit: lib.symbol(b"sqlite3_get_autocommit\0"),
434            total_changes: lib.symbol(b"sqlite3_total_changes\0"),
435            changes: lib.symbol(b"sqlite3_changes\0"),
436            changes64: lib.symbol(b"sqlite3_changes64\0"),
437            last_insert_rowid: lib.symbol(b"sqlite3_last_insert_rowid\0"),
438            interrupt: lib.symbol(b"sqlite3_interrupt\0"),
439            db_config: lib.symbol(b"sqlite3_db_config\0"),
440            limit: lib.symbol(b"sqlite3_limit\0"),
441            stmt_readonly: lib.symbol(b"sqlite3_stmt_readonly\0"),
442            stmt_busy: lib.symbol(b"sqlite3_stmt_busy\0"),
443            bind_parameter_count: lib.symbol(b"sqlite3_bind_parameter_count\0"),
444            bind_parameter_name: lib.symbol(b"sqlite3_bind_parameter_name\0"),
445            bind_parameter_index: lib.symbol(b"sqlite3_bind_parameter_index\0"),
446            context_db_handle: lib.symbol(b"sqlite3_context_db_handle\0"),
447            trace_v2: lib.symbol(b"sqlite3_trace_v2\0"),
448            progress_handler: lib.symbol(b"sqlite3_progress_handler\0"),
449            busy_timeout: lib.symbol(b"sqlite3_busy_timeout\0"),
450            set_authorizer: lib.symbol(b"sqlite3_set_authorizer\0"),
451            backup_init: lib.symbol(b"sqlite3_backup_init\0"),
452            backup_step: lib.symbol(b"sqlite3_backup_step\0"),
453            backup_remaining: lib.symbol(b"sqlite3_backup_remaining\0"),
454            backup_pagecount: lib.symbol(b"sqlite3_backup_pagecount\0"),
455            backup_finish: lib.symbol(b"sqlite3_backup_finish\0"),
456            blob_open: lib.symbol(b"sqlite3_blob_open\0"),
457            blob_read: lib.symbol(b"sqlite3_blob_read\0"),
458            blob_write: lib.symbol(b"sqlite3_blob_write\0"),
459            blob_bytes: lib.symbol(b"sqlite3_blob_bytes\0"),
460            blob_close: lib.symbol(b"sqlite3_blob_close\0"),
461            serialize: lib.symbol(b"sqlite3_serialize\0"),
462            deserialize: lib.symbol(b"sqlite3_deserialize\0"),
463            wal_checkpoint: lib.symbol(b"sqlite3_wal_checkpoint\0"),
464            wal_checkpoint_v2: lib.symbol(b"sqlite3_wal_checkpoint_v2\0"),
465            libsql_wal_frame_count: lib.symbol(b"libsql_wal_frame_count\0"),
466        })
467    }
468}
469
470static USER_DATA_FN: OnceLock<UserData> = OnceLock::new();
471static ADAPTER_INSTANCE: OnceLock<Option<&'static LibSqlite3>> = OnceLock::new();
472
473/// Dynamic `libsqlite3` backend adapter loaded via `dlopen`.
474pub struct LibSqlite3 {
475    fns: LibSqlite3Fns,
476    features: FeatureSet,
477    api_version: ApiVersion,
478    _lib: LibHandle,
479}
480
481#[allow(unsafe_op_in_unsafe_fn)]
482impl LibSqlite3 {
483    /// Load `libsqlite3` and return a process-wide adapter instance.
484    ///
485    /// Returns `None` if the library or required symbols are unavailable.
486    pub fn load() -> Option<&'static LibSqlite3> {
487        ADAPTER_INSTANCE
488            .get_or_init(|| unsafe { Self::load_inner() })
489            .as_ref()
490            .copied()
491    }
492
493    unsafe fn load_inner() -> Option<&'static LibSqlite3> {
494        unsafe {
495            let lib = LibHandle::open()?;
496            let fns = LibSqlite3Fns::load(&lib)?;
497            let version_number = (fns.libversion_number)();
498            let api_version = api_version_from_number(version_number);
499            let mut features = FeatureSet::CREATE_FUNCTION_V2;
500            if fns.prepare_v3.is_some() {
501                features |= FeatureSet::PREPARE_V3;
502            }
503            if fns.create_window_function.is_some() {
504                features |= FeatureSet::WINDOW_FUNCTIONS;
505            }
506            if fns.extended_errcode.is_some() {
507                features |= FeatureSet::EXTENDED_ERRCODES;
508            }
509            if fns.create_module_v2.is_some() {
510                features |= FeatureSet::VIRTUAL_TABLES;
511            }
512            let _ = USER_DATA_FN.set(fns.user_data);
513            let adapter = LibSqlite3 {
514                fns,
515                features,
516                api_version,
517                _lib: lib,
518            };
519            Some(Box::leak(Box::new(adapter)))
520        }
521    }
522
523    fn error_from_rc(&self, rc: i32, db: Option<NonNull<sqlite3>>) -> Error {
524        let message = db
525            .and_then(|db| unsafe { raw_cstr((self.fns.errmsg)(db.as_ptr())) })
526            .map(|c| c.to_string_lossy().into_owned());
527        let extended =
528            db.and_then(|db| self.fns.extended_errcode.map(|f| unsafe { f(db.as_ptr()) }));
529        Error::from_code(rc, message, extended)
530    }
531
532    // Allocate with sqlite3_malloc so SQLite can free via sqlite3_free.
533    fn alloc_copy(&self, bytes: &[u8]) -> Result<(*const c_void, sqlite3_destructor_type)> {
534        if bytes.is_empty() {
535            return Ok((&EMPTY_BYTE as *const u8 as *const c_void, None));
536        }
537        if bytes.len() > i32::MAX as usize {
538            return Err(Error::with_message(ErrorCode::Misuse, "value too large"));
539        }
540        let ptr = unsafe { (self.fns.malloc)(bytes.len() as i32) };
541        if ptr.is_null() {
542            return Err(Error::new(ErrorCode::NoMem));
543        }
544        unsafe {
545            std::ptr::copy_nonoverlapping(bytes.as_ptr(), ptr as *mut u8, bytes.len());
546        }
547        Ok((ptr, Some(self.fns.free)))
548    }
549
550    /// ABI helper: `sqlite3_db_filename`.
551    ///
552    /// The returned bytes are backend-managed and follow SQLite pointer
553    /// lifetime rules.
554    ///
555    /// # Safety
556    /// `db` must be a valid `sqlite3*` from this loaded backend, and must
557    /// remain valid for the duration of the call.
558    pub unsafe fn abi_db_filename(&self, db: *mut c_void, name: Option<&str>) -> Option<RawBytes> {
559        let func = self.fns.db_filename?;
560        let name = match name {
561            Some(name) => Some(CString::new(name).ok()?),
562            None => None,
563        };
564        let ptr = func(
565            db as *mut sqlite3,
566            name.as_ref().map(|s| s.as_ptr()).unwrap_or(null()),
567        );
568        raw_cstr(ptr).map(raw_bytes_from_cstr)
569    }
570
571    /// ABI helper: `sqlite3_get_autocommit`.
572    ///
573    /// # Safety
574    /// `db` must be a valid `sqlite3*` from this loaded backend.
575    pub unsafe fn abi_get_autocommit(&self, db: *mut c_void) -> i32 {
576        self.fns
577            .get_autocommit
578            .map(|f| f(db as *mut sqlite3))
579            .unwrap_or(1)
580    }
581
582    /// ABI helper: `sqlite3_total_changes`.
583    ///
584    /// # Safety
585    /// `db` must be a valid `sqlite3*` from this loaded backend.
586    pub unsafe fn abi_total_changes(&self, db: *mut c_void) -> i32 {
587        self.fns
588            .total_changes
589            .map(|f| f(db as *mut sqlite3))
590            .unwrap_or(0)
591    }
592
593    /// ABI helper: `sqlite3_changes`.
594    ///
595    /// # Safety
596    /// `db` must be a valid `sqlite3*` from this loaded backend.
597    pub unsafe fn abi_changes(&self, db: *mut c_void) -> i32 {
598        self.fns.changes.map(|f| f(db as *mut sqlite3)).unwrap_or(0)
599    }
600
601    /// ABI helper: `sqlite3_changes64`.
602    ///
603    /// # Safety
604    /// `db` must be a valid `sqlite3*` from this loaded backend.
605    pub unsafe fn abi_changes64(&self, db: *mut c_void) -> i64 {
606        self.fns
607            .changes64
608            .map(|f| f(db as *mut sqlite3))
609            .unwrap_or(0)
610    }
611
612    /// ABI helper: `sqlite3_last_insert_rowid`.
613    ///
614    /// # Safety
615    /// `db` must be a valid `sqlite3*` from this loaded backend.
616    pub unsafe fn abi_last_insert_rowid(&self, db: *mut c_void) -> i64 {
617        self.fns
618            .last_insert_rowid
619            .map(|f| f(db as *mut sqlite3))
620            .unwrap_or(0)
621    }
622
623    /// ABI helper: `sqlite3_interrupt`.
624    ///
625    /// # Safety
626    /// `db` must be a valid `sqlite3*` from this loaded backend.
627    pub unsafe fn abi_interrupt(&self, db: *mut c_void) {
628        if let Some(func) = self.fns.interrupt {
629            func(db as *mut sqlite3);
630        }
631    }
632
633    /// ABI helper: `sqlite3_db_config`.
634    ///
635    /// # Safety
636    /// `db` must be a valid `sqlite3*` from this loaded backend.
637    pub unsafe fn abi_db_config(&self, db: *mut c_void, op: i32) -> i32 {
638        // The shim currently exposes the fixed-arity form (`db`, `op`) only.
639        self.fns
640            .db_config
641            .map(|f| f(db as *mut sqlite3, op))
642            .unwrap_or(SQLITE_MISUSE)
643    }
644
645    /// ABI helper: `sqlite3_limit`.
646    ///
647    /// # Safety
648    /// `db` must be a valid `sqlite3*` from this loaded backend.
649    pub unsafe fn abi_limit(&self, db: *mut c_void, id: i32, new_value: i32) -> i32 {
650        self.fns
651            .limit
652            .map(|f| f(db as *mut sqlite3, id, new_value))
653            .unwrap_or(-1)
654    }
655
656    /// ABI helper: `sqlite3_stmt_readonly`.
657    ///
658    /// # Safety
659    /// `stmt` must be a valid `sqlite3_stmt*` from this loaded backend.
660    pub unsafe fn abi_stmt_readonly(&self, stmt: *mut c_void) -> i32 {
661        self.fns
662            .stmt_readonly
663            .map(|f| f(stmt as *mut sqlite3_stmt))
664            .unwrap_or(0)
665    }
666
667    /// ABI helper: `sqlite3_stmt_busy`.
668    ///
669    /// # Safety
670    /// `stmt` must be a valid `sqlite3_stmt*` from this loaded backend.
671    pub unsafe fn abi_stmt_busy(&self, stmt: *mut c_void) -> i32 {
672        self.fns
673            .stmt_busy
674            .map(|f| f(stmt as *mut sqlite3_stmt))
675            .unwrap_or(0)
676    }
677
678    /// ABI helper: `sqlite3_bind_parameter_count`.
679    ///
680    /// # Safety
681    /// `stmt` must be a valid `sqlite3_stmt*` from this loaded backend.
682    pub unsafe fn abi_bind_parameter_count(&self, stmt: *mut c_void) -> i32 {
683        self.fns
684            .bind_parameter_count
685            .map(|f| f(stmt as *mut sqlite3_stmt))
686            .unwrap_or(0)
687    }
688
689    /// ABI helper: `sqlite3_bind_parameter_name`.
690    ///
691    /// The returned bytes are backend-managed and follow SQLite pointer
692    /// lifetime rules.
693    ///
694    /// # Safety
695    /// `stmt` must be a valid `sqlite3_stmt*` from this loaded backend.
696    pub unsafe fn abi_bind_parameter_name(&self, stmt: *mut c_void, idx: i32) -> Option<RawBytes> {
697        let func = self.fns.bind_parameter_name?;
698        let ptr = func(stmt as *mut sqlite3_stmt, idx);
699        raw_cstr(ptr).map(raw_bytes_from_cstr)
700    }
701
702    /// ABI helper: `sqlite3_bind_parameter_index`.
703    ///
704    /// # Safety
705    /// `stmt` must be a valid `sqlite3_stmt*` from this loaded backend.
706    pub unsafe fn abi_bind_parameter_index(&self, stmt: *mut c_void, name: &str) -> i32 {
707        let func = match self.fns.bind_parameter_index {
708            Some(func) => func,
709            None => return 0,
710        };
711        let name = match CString::new(name) {
712            Ok(name) => name,
713            Err(_) => return 0,
714        };
715        func(stmt as *mut sqlite3_stmt, name.as_ptr())
716    }
717
718    /// ABI helper: `sqlite3_context_db_handle`.
719    ///
720    /// # Safety
721    /// `ctx` must be a valid `sqlite3_context*` provided by SQLite for an
722    /// active scalar/aggregate/window callback.
723    pub unsafe fn abi_context_db_handle(&self, ctx: *mut c_void) -> Option<NonNull<c_void>> {
724        let func = self.fns.context_db_handle?;
725        NonNull::new(func(ctx as *mut sqlite3_context))
726    }
727}
728
729mod core_impl;
730mod extensions_impl;
731
732fn api_version_from_number(number: i32) -> ApiVersion {
733    let major = (number / 1_000_000) as u16;
734    let minor = ((number / 1_000) % 1_000) as u16;
735    let patch = (number % 1_000) as u16;
736    ApiVersion::new(major, minor, patch)
737}
738
739fn map_open_flags(flags: OpenFlags) -> i32 {
740    let mut out = 0;
741    if flags.contains(OpenFlags::READ_ONLY) {
742        out |= SQLITE_OPEN_READONLY;
743    }
744    if flags.contains(OpenFlags::READ_WRITE) {
745        out |= SQLITE_OPEN_READWRITE;
746    }
747    if flags.contains(OpenFlags::CREATE) {
748        out |= SQLITE_OPEN_CREATE;
749    }
750    if flags.contains(OpenFlags::URI) {
751        out |= SQLITE_OPEN_URI;
752    }
753    if flags.contains(OpenFlags::NO_MUTEX) {
754        out |= SQLITE_OPEN_NOMUTEX;
755    }
756    if flags.contains(OpenFlags::FULL_MUTEX) {
757        out |= SQLITE_OPEN_FULLMUTEX;
758    }
759    if flags.contains(OpenFlags::SHARED_CACHE) {
760        out |= SQLITE_OPEN_SHAREDCACHE;
761    }
762    if flags.contains(OpenFlags::PRIVATE_CACHE) {
763        out |= SQLITE_OPEN_PRIVATECACHE;
764    }
765    if flags.contains(OpenFlags::EXRESCODE) {
766        out |= SQLITE_OPEN_EXRESCODE;
767    }
768    out
769}
770
771fn map_function_flags(flags: FunctionFlags) -> i32 {
772    let mut out = SQLITE_UTF8;
773    if flags.contains(FunctionFlags::DETERMINISTIC) {
774        out |= SQLITE_DETERMINISTIC;
775    }
776    if flags.contains(FunctionFlags::DIRECT_ONLY) {
777        out |= SQLITE_DIRECTONLY;
778    }
779    if flags.contains(FunctionFlags::INNOCUOUS) {
780        out |= SQLITE_INNOCUOUS;
781    }
782    out
783}
784
785fn clamp_len(len: usize) -> i32 {
786    if len > i32::MAX as usize {
787        i32::MAX
788    } else {
789        len as i32
790    }
791}
792
793#[allow(unsafe_op_in_unsafe_fn)]
794unsafe fn raw_cstr<'a>(ptr: *const c_char) -> Option<&'a CStr> {
795    if ptr.is_null() {
796        None
797    } else {
798        Some(CStr::from_ptr(ptr))
799    }
800}
801
802fn raw_bytes_from_cstr(cstr: &CStr) -> RawBytes {
803    RawBytes {
804        ptr: cstr.as_ptr(),
805        len: cstr.to_bytes().len(),
806    }
807}
808
809fn lib_names() -> &'static [&'static [u8]] {
810    #[cfg(target_os = "macos")]
811    const NAMES: [&[u8]; 3] = [
812        b"libsqlite3.dylib\0",
813        b"libsqlite3.so.0\0",
814        b"libsqlite3.so\0",
815    ];
816    #[cfg(not(target_os = "macos"))]
817    const NAMES: [&[u8]; 2] = [b"libsqlite3.so.0\0", b"libsqlite3.so\0"];
818    &NAMES
819}
820
821#[cfg(test)]
822mod tests {
823    use super::LibSqlite3;
824
825    #[test]
826    fn load_returns_stable_process_singleton() {
827        let first = LibSqlite3::load();
828        let second = LibSqlite3::load();
829        match (first, second) {
830            (Some(first), Some(second)) => {
831                assert!(std::ptr::eq(first, second));
832            }
833            (None, None) => {}
834            _ => panic!("LibSqlite3::load result changed across calls"),
835        }
836    }
837}