Skip to main content

sqlite_provider/
provider.rs

1use core::ffi::{c_char, c_void};
2use core::ptr::NonNull;
3
4use crate::error::{Error, ErrorCode, Result};
5
6/// SQLite API version.
7#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
8pub struct ApiVersion {
9    /// Major version component (X in X.Y.Z).
10    pub major: u16,
11    /// Minor version component (Y in X.Y.Z).
12    pub minor: u16,
13    /// Patch version component (Z in X.Y.Z).
14    pub patch: u16,
15}
16
17impl ApiVersion {
18    /// Build an API version from major/minor/patch components.
19    pub const fn new(major: u16, minor: u16, patch: u16) -> Self {
20        Self {
21            major,
22            minor,
23            patch,
24        }
25    }
26}
27
28/// Backend feature flags exposed by the provider.
29#[derive(Clone, Copy, Debug, PartialEq, Eq)]
30pub struct FeatureSet {
31    bits: u64,
32}
33
34impl FeatureSet {
35    /// `prepare_v3` is available.
36    pub const PREPARE_V3: FeatureSet = FeatureSet { bits: 1 << 0 };
37    /// `create_function_v2` is available.
38    pub const CREATE_FUNCTION_V2: FeatureSet = FeatureSet { bits: 1 << 1 };
39    /// Virtual table APIs are available.
40    pub const VIRTUAL_TABLES: FeatureSet = FeatureSet { bits: 1 << 2 };
41    /// Extended error code APIs are available.
42    pub const EXTENDED_ERRCODES: FeatureSet = FeatureSet { bits: 1 << 3 };
43    /// Window-function APIs are available.
44    pub const WINDOW_FUNCTIONS: FeatureSet = FeatureSet { bits: 1 << 4 };
45    /// Backend keying/encryption APIs are available.
46    pub const KEYING: FeatureSet = FeatureSet { bits: 1 << 5 };
47
48    /// Build an empty flag set.
49    pub const fn empty() -> Self {
50        Self { bits: 0 }
51    }
52
53    /// Build a flag set from raw bits.
54    pub const fn from_bits(bits: u64) -> Self {
55        Self { bits }
56    }
57
58    /// Return the raw bit representation.
59    pub const fn bits(self) -> u64 {
60        self.bits
61    }
62
63    /// Return whether `other` is fully contained in this set.
64    pub const fn contains(self, other: FeatureSet) -> bool {
65        (self.bits & other.bits) == other.bits
66    }
67}
68
69impl core::ops::BitOr for FeatureSet {
70    type Output = FeatureSet;
71
72    fn bitor(self, rhs: FeatureSet) -> FeatureSet {
73        FeatureSet {
74            bits: self.bits | rhs.bits,
75        }
76    }
77}
78
79impl core::ops::BitOrAssign for FeatureSet {
80    fn bitor_assign(&mut self, rhs: FeatureSet) {
81        self.bits |= rhs.bits;
82    }
83}
84
85impl core::ops::BitAnd for FeatureSet {
86    type Output = FeatureSet;
87
88    fn bitand(self, rhs: FeatureSet) -> FeatureSet {
89        FeatureSet {
90            bits: self.bits & rhs.bits,
91        }
92    }
93}
94
95impl core::ops::BitAndAssign for FeatureSet {
96    fn bitand_assign(&mut self, rhs: FeatureSet) {
97        self.bits &= rhs.bits;
98    }
99}
100
101impl core::ops::Not for FeatureSet {
102    type Output = FeatureSet;
103
104    fn not(self) -> FeatureSet {
105        FeatureSet { bits: !self.bits }
106    }
107}
108
109/// Flags for opening a database connection.
110#[derive(Clone, Copy, Debug, PartialEq, Eq)]
111pub struct OpenFlags {
112    bits: u32,
113}
114
115impl OpenFlags {
116    /// Open database in read-only mode.
117    pub const READ_ONLY: OpenFlags = OpenFlags { bits: 1 << 0 };
118    /// Open database in read-write mode.
119    pub const READ_WRITE: OpenFlags = OpenFlags { bits: 1 << 1 };
120    /// Create database file when missing.
121    pub const CREATE: OpenFlags = OpenFlags { bits: 1 << 2 };
122    /// Treat filename as URI when supported.
123    pub const URI: OpenFlags = OpenFlags { bits: 1 << 3 };
124    /// Use connection-private mutex mode.
125    pub const NO_MUTEX: OpenFlags = OpenFlags { bits: 1 << 4 };
126    /// Use fully serialized mutex mode.
127    pub const FULL_MUTEX: OpenFlags = OpenFlags { bits: 1 << 5 };
128    /// Enable shared page cache.
129    pub const SHARED_CACHE: OpenFlags = OpenFlags { bits: 1 << 6 };
130    /// Force private page cache.
131    pub const PRIVATE_CACHE: OpenFlags = OpenFlags { bits: 1 << 7 };
132    /// Request extended result codes.
133    pub const EXRESCODE: OpenFlags = OpenFlags { bits: 1 << 8 };
134
135    /// Build an empty flag set.
136    pub const fn empty() -> Self {
137        Self { bits: 0 }
138    }
139
140    /// Build a flag set from raw bits.
141    pub const fn from_bits(bits: u32) -> Self {
142        Self { bits }
143    }
144
145    /// Return the raw bit representation.
146    pub const fn bits(self) -> u32 {
147        self.bits
148    }
149
150    /// Return whether `other` is fully contained in this set.
151    pub const fn contains(self, other: OpenFlags) -> bool {
152        (self.bits & other.bits) == other.bits
153    }
154}
155
156impl core::ops::BitOr for OpenFlags {
157    type Output = OpenFlags;
158
159    fn bitor(self, rhs: OpenFlags) -> OpenFlags {
160        OpenFlags {
161            bits: self.bits | rhs.bits,
162        }
163    }
164}
165
166impl core::ops::BitOrAssign for OpenFlags {
167    fn bitor_assign(&mut self, rhs: OpenFlags) {
168        self.bits |= rhs.bits;
169    }
170}
171
172impl core::ops::BitAnd for OpenFlags {
173    type Output = OpenFlags;
174
175    fn bitand(self, rhs: OpenFlags) -> OpenFlags {
176        OpenFlags {
177            bits: self.bits & rhs.bits,
178        }
179    }
180}
181
182impl core::ops::BitAndAssign for OpenFlags {
183    fn bitand_assign(&mut self, rhs: OpenFlags) {
184        self.bits &= rhs.bits;
185    }
186}
187
188impl core::ops::Not for OpenFlags {
189    type Output = OpenFlags;
190
191    fn not(self) -> OpenFlags {
192        OpenFlags { bits: !self.bits }
193    }
194}
195
196/// Options passed to `Sqlite3Api::open`.
197pub struct OpenOptions<'a> {
198    /// Backend-open flags translated from caller intent.
199    pub flags: OpenFlags,
200    /// Optional VFS name passed through to backend open.
201    pub vfs: Option<&'a str>,
202}
203
204/// Result of a `step` call.
205#[derive(Clone, Copy, Debug, PartialEq, Eq)]
206pub enum StepResult {
207    /// The statement produced a row.
208    Row,
209    /// The statement has finished.
210    Done,
211}
212
213/// SQLite storage class for a value.
214#[derive(Clone, Copy, Debug, PartialEq, Eq)]
215pub enum ValueType {
216    /// SQL NULL.
217    Null,
218    /// 64-bit integer.
219    Integer,
220    /// 64-bit floating value.
221    Float,
222    /// UTF-8 text.
223    Text,
224    /// Binary blob.
225    Blob,
226}
227
228impl ValueType {
229    /// Decode SQLite's integer storage-class code.
230    pub const fn from_code(code: i32) -> ValueType {
231        match code {
232            1 => ValueType::Integer,
233            2 => ValueType::Float,
234            3 => ValueType::Text,
235            4 => ValueType::Blob,
236            _ => ValueType::Null,
237        }
238    }
239
240    /// Encode this storage class into SQLite's integer code.
241    pub const fn to_code(self) -> i32 {
242        match self {
243            ValueType::Null => 5,
244            ValueType::Integer => 1,
245            ValueType::Float => 2,
246            ValueType::Text => 3,
247            ValueType::Blob => 4,
248        }
249    }
250}
251
252/// Raw view into SQLite-managed bytes.
253///
254/// The pointer/length are tied to the lifetime of the current SQLite
255/// row/value snapshot and are invalidated by lifecycle transitions such as
256/// `step`, `reset`, or `finalize` on the owning statement/value context.
257/// Implementations must keep returned bytes stable across repeated text/blob
258/// reads within the same snapshot. Callers that need longer-lived data must
259/// copy the bytes.
260#[derive(Clone, Copy, Debug)]
261pub struct RawBytes {
262    /// Pointer to backend-owned bytes.
263    pub ptr: *const u8,
264    /// Byte length of `ptr`.
265    pub len: usize,
266}
267
268impl RawBytes {
269    /// Empty byte view.
270    pub const fn empty() -> Self {
271        Self {
272            ptr: core::ptr::null(),
273            len: 0,
274        }
275    }
276
277    /// # Safety
278    /// Caller must ensure the pointer/length remain valid for the returned slice.
279    pub unsafe fn as_slice<'a>(self) -> &'a [u8] {
280        if self.ptr.is_null() {
281            return &[];
282        }
283        unsafe { core::slice::from_raw_parts(self.ptr, self.len) }
284    }
285
286    /// # Safety
287    /// Caller must ensure the bytes are valid UTF-8 and remain valid for `'a`.
288    pub unsafe fn as_str<'a>(self) -> Option<&'a str> {
289        core::str::from_utf8(unsafe { self.as_slice() }).ok()
290    }
291
292    /// # Safety
293    /// Caller must ensure the bytes are valid UTF-8 and remain valid for `'a`.
294    pub unsafe fn as_str_unchecked<'a>(self) -> &'a str {
295        unsafe { core::str::from_utf8_unchecked(self.as_slice()) }
296    }
297}
298
299/// Function flags passed to `create_function_v2` / `create_window_function`.
300#[derive(Clone, Copy, Debug, PartialEq, Eq)]
301pub struct FunctionFlags {
302    bits: u32,
303}
304
305impl FunctionFlags {
306    /// Mark UDF as deterministic.
307    pub const DETERMINISTIC: FunctionFlags = FunctionFlags { bits: 1 << 0 };
308    /// Restrict UDF to direct SQL only.
309    pub const DIRECT_ONLY: FunctionFlags = FunctionFlags { bits: 1 << 1 };
310    /// Mark UDF as innocuous.
311    pub const INNOCUOUS: FunctionFlags = FunctionFlags { bits: 1 << 2 };
312
313    /// Build an empty flag set.
314    pub const fn empty() -> Self {
315        Self { bits: 0 }
316    }
317
318    /// Build a flag set from raw bits.
319    pub const fn from_bits(bits: u32) -> Self {
320        Self { bits }
321    }
322
323    /// Return the raw bit representation.
324    pub const fn bits(self) -> u32 {
325        self.bits
326    }
327
328    /// Return whether `other` is fully contained in this set.
329    pub const fn contains(self, other: FunctionFlags) -> bool {
330        (self.bits & other.bits) == other.bits
331    }
332}
333
334impl core::ops::BitOr for FunctionFlags {
335    type Output = FunctionFlags;
336
337    fn bitor(self, rhs: FunctionFlags) -> FunctionFlags {
338        FunctionFlags {
339            bits: self.bits | rhs.bits,
340        }
341    }
342}
343
344impl core::ops::BitOrAssign for FunctionFlags {
345    fn bitor_assign(&mut self, rhs: FunctionFlags) {
346        self.bits |= rhs.bits;
347    }
348}
349
350impl core::ops::BitAnd for FunctionFlags {
351    type Output = FunctionFlags;
352
353    fn bitand(self, rhs: FunctionFlags) -> FunctionFlags {
354        FunctionFlags {
355            bits: self.bits & rhs.bits,
356        }
357    }
358}
359
360impl core::ops::BitAndAssign for FunctionFlags {
361    fn bitand_assign(&mut self, rhs: FunctionFlags) {
362        self.bits &= rhs.bits;
363    }
364}
365
366impl core::ops::Not for FunctionFlags {
367    type Output = FunctionFlags;
368
369    fn not(self) -> FunctionFlags {
370        FunctionFlags { bits: !self.bits }
371    }
372}
373
374/// Provider SPI over a SQLite C API backend.
375///
376/// # Safety
377/// Implementations must uphold the SQLite C ABI contracts.
378#[allow(clippy::missing_safety_doc, clippy::too_many_arguments)]
379pub unsafe trait Sqlite3Api: Send + Sync + 'static {
380    /// Backend's opaque `sqlite3*` type.
381    type Db;
382    /// Backend's opaque `sqlite3_stmt*` type.
383    type Stmt;
384    /// Backend's opaque `sqlite3_value*` type.
385    type Value;
386    /// Backend's opaque `sqlite3_context*` type.
387    type Context;
388    /// Backend's opaque `sqlite3_vtab*` type.
389    type VTab;
390    /// Backend's opaque `sqlite3_vtab_cursor*` type.
391    type VTabCursor;
392
393    /// Declared SQLite API version supported by this provider implementation.
394    fn api_version(&self) -> ApiVersion;
395    /// Compile/runtime capability flags available through this provider.
396    fn feature_set(&self) -> FeatureSet;
397    /// Stable backend identifier (for diagnostics and capability routing).
398    fn backend_name(&self) -> &'static str;
399    /// Optional backend runtime version (if queryable).
400    fn backend_version(&self) -> Option<ApiVersion>;
401    /// Return SQLite allocator-compatible memory for cross-FFI ownership.
402    ///
403    /// Returned pointers must be releasable via `free` below and must match
404    /// SQLite allocator expectations for this backend.
405    unsafe fn malloc(&self, size: usize) -> *mut c_void;
406    /// Free memory allocated through `malloc`.
407    unsafe fn free(&self, ptr: *mut c_void);
408    /// Thread-safety capability in `sqlite3_threadsafe()` form.
409    fn threadsafe(&self) -> i32 {
410        0
411    }
412
413    /// Open a database connection using backend-specific `open_v2` semantics.
414    unsafe fn open(&self, filename: &str, options: OpenOptions<'_>) -> Result<NonNull<Self::Db>>;
415    /// Close a database connection handle.
416    unsafe fn close(&self, db: NonNull<Self::Db>) -> Result<()>;
417
418    /// Prepare SQL using legacy `prepare_v2` behavior.
419    unsafe fn prepare_v2(&self, db: NonNull<Self::Db>, sql: &str) -> Result<NonNull<Self::Stmt>>;
420    /// Prepare SQL with `prepare_v3` flags.
421    unsafe fn prepare_v3(
422        &self,
423        db: NonNull<Self::Db>,
424        sql: &str,
425        flags: u32,
426    ) -> Result<NonNull<Self::Stmt>>;
427
428    /// Execute one step of the virtual machine.
429    unsafe fn step(&self, stmt: NonNull<Self::Stmt>) -> Result<StepResult>;
430    /// Reset a prepared statement to run again.
431    unsafe fn reset(&self, stmt: NonNull<Self::Stmt>) -> Result<()>;
432    /// Finalize a prepared statement and release backend resources.
433    unsafe fn finalize(&self, stmt: NonNull<Self::Stmt>) -> Result<()>;
434
435    /// Bind SQL NULL at parameter index `idx` (1-based).
436    unsafe fn bind_null(&self, stmt: NonNull<Self::Stmt>, idx: i32) -> Result<()>;
437    /// Bind 64-bit integer at parameter index `idx` (1-based).
438    unsafe fn bind_int64(&self, stmt: NonNull<Self::Stmt>, idx: i32, v: i64) -> Result<()>;
439    /// Bind floating value at parameter index `idx` (1-based).
440    unsafe fn bind_double(&self, stmt: NonNull<Self::Stmt>, idx: i32, v: f64) -> Result<()>;
441    /// Bind UTF-8 text at parameter index `idx` (1-based).
442    ///
443    /// Implementations must copy `v` or retain it safely per SQLite lifetime rules.
444    unsafe fn bind_text(&self, stmt: NonNull<Self::Stmt>, idx: i32, v: &str) -> Result<()>;
445    /// Bind text bytes at parameter index `idx` (1-based).
446    ///
447    /// This hook lets ABI callers preserve SQLite's byte-oriented text semantics
448    /// even when bytes are not valid UTF-8. The default implementation keeps
449    /// compatibility with existing providers by requiring valid UTF-8.
450    unsafe fn bind_text_bytes(&self, stmt: NonNull<Self::Stmt>, idx: i32, v: &[u8]) -> Result<()> {
451        match core::str::from_utf8(v) {
452            Ok(text) => unsafe { self.bind_text(stmt, idx, text) },
453            Err(_) => Err(Error::with_message(ErrorCode::Misuse, "invalid utf-8 text")),
454        }
455    }
456    /// Bind bytes at parameter index `idx` (1-based).
457    ///
458    /// Implementations must copy `v` or retain it safely per SQLite lifetime rules.
459    unsafe fn bind_blob(&self, stmt: NonNull<Self::Stmt>, idx: i32, v: &[u8]) -> Result<()>;
460
461    /// Number of columns in the current result row.
462    unsafe fn column_count(&self, stmt: NonNull<Self::Stmt>) -> i32;
463    /// SQLite storage class for `col` in the current row.
464    unsafe fn column_type(&self, stmt: NonNull<Self::Stmt>, col: i32) -> ValueType;
465    /// Integer value for `col` in the current row.
466    unsafe fn column_int64(&self, stmt: NonNull<Self::Stmt>, col: i32) -> i64;
467    /// Floating value for `col` in the current row.
468    unsafe fn column_double(&self, stmt: NonNull<Self::Stmt>, col: i32) -> f64;
469    /// Raw text bytes for `col` in the current row snapshot.
470    unsafe fn column_text(&self, stmt: NonNull<Self::Stmt>, col: i32) -> RawBytes;
471    /// Raw blob bytes for `col` in the current row snapshot.
472    unsafe fn column_blob(&self, stmt: NonNull<Self::Stmt>, col: i32) -> RawBytes;
473
474    /// Primary SQLite result code for a connection.
475    unsafe fn errcode(&self, db: NonNull<Self::Db>) -> i32;
476    /// Backend-provided UTF-8 error message pointer for a connection.
477    unsafe fn errmsg(&self, db: NonNull<Self::Db>) -> *const c_char;
478    /// Extended SQLite result code, if supported.
479    unsafe fn extended_errcode(&self, db: NonNull<Self::Db>) -> Option<i32>;
480
481    /// Register scalar/aggregate callbacks.
482    ///
483    /// # Ownership contract
484    /// Ownership of `user_data` and `drop_user_data` is transferred to the
485    /// provider at call entry.
486    ///
487    /// If registration succeeds, the provider must eventually invoke
488    /// `drop_user_data` exactly once when the function definition is replaced,
489    /// removed, or the owning connection is closed.
490    ///
491    /// If registration fails and this method returns `Err`, the provider must
492    /// invoke `drop_user_data` exactly once before returning.
493    ///
494    /// Callers must not free or otherwise use `user_data` after calling this
495    /// method.
496    unsafe fn create_function_v2(
497        &self,
498        db: NonNull<Self::Db>,
499        name: &str,
500        n_args: i32,
501        flags: FunctionFlags,
502        x_func: Option<extern "C" fn(*mut Self::Context, i32, *mut *mut Self::Value)>,
503        x_step: Option<extern "C" fn(*mut Self::Context, i32, *mut *mut Self::Value)>,
504        x_final: Option<extern "C" fn(*mut Self::Context)>,
505        user_data: *mut c_void,
506        drop_user_data: Option<extern "C" fn(*mut c_void)>,
507    ) -> Result<()>;
508
509    /// Register window-function callbacks.
510    ///
511    /// # Ownership contract
512    /// Ownership of `user_data` and `drop_user_data` is transferred to the
513    /// provider at call entry.
514    ///
515    /// If registration succeeds, the provider must eventually invoke
516    /// `drop_user_data` exactly once when the window-function definition is
517    /// replaced, removed, or the owning connection is closed.
518    ///
519    /// If registration fails and this method returns `Err`, the provider must
520    /// invoke `drop_user_data` exactly once before returning.
521    ///
522    /// Callers must not free or otherwise use `user_data` after calling this
523    /// method.
524    unsafe fn create_window_function(
525        &self,
526        db: NonNull<Self::Db>,
527        name: &str,
528        n_args: i32,
529        flags: FunctionFlags,
530        x_step: Option<extern "C" fn(*mut Self::Context, i32, *mut *mut Self::Value)>,
531        x_final: Option<extern "C" fn(*mut Self::Context)>,
532        x_value: Option<extern "C" fn(*mut Self::Context)>,
533        x_inverse: Option<extern "C" fn(*mut Self::Context, i32, *mut *mut Self::Value)>,
534        user_data: *mut c_void,
535        drop_user_data: Option<extern "C" fn(*mut c_void)>,
536    ) -> Result<()>;
537
538    /// Register or replace a collation sequence.
539    ///
540    /// Providers should map this to `sqlite3_create_collation_v2` semantics.
541    /// The callback receives `(context, lhs_len, lhs_ptr, rhs_len, rhs_ptr)`
542    /// and must return negative/zero/positive ordering as in SQLite.
543    unsafe fn create_collation_v2(
544        &self,
545        _db: NonNull<Self::Db>,
546        _name: &str,
547        _enc: i32,
548        _context: *mut c_void,
549        _cmp: Option<extern "C" fn(*mut c_void, i32, *const c_void, i32, *const c_void) -> i32>,
550        _destroy: Option<extern "C" fn(*mut c_void)>,
551    ) -> Result<()> {
552        Err(Error::feature_unavailable(
553            "create_collation_v2 unsupported",
554        ))
555    }
556
557    /// Fetch or allocate aggregate state memory for `ctx`.
558    unsafe fn aggregate_context(&self, ctx: NonNull<Self::Context>, bytes: usize) -> *mut c_void;
559
560    /// Set current function result to NULL.
561    unsafe fn result_null(&self, ctx: NonNull<Self::Context>);
562    /// Set current function result to integer.
563    unsafe fn result_int64(&self, ctx: NonNull<Self::Context>, v: i64);
564    /// Set current function result to floating value.
565    unsafe fn result_double(&self, ctx: NonNull<Self::Context>, v: f64);
566    /// Providers must ensure SQLite copies or retains the buffer for `v`.
567    unsafe fn result_text(&self, ctx: NonNull<Self::Context>, v: &str);
568    /// Providers must ensure SQLite copies or retains the buffer for `v`.
569    ///
570    /// The default implementation preserves the legacy UTF-8 contract by routing
571    /// invalid bytes to `result_error`.
572    unsafe fn result_text_bytes(&self, ctx: NonNull<Self::Context>, v: &[u8]) {
573        match core::str::from_utf8(v) {
574            Ok(text) => unsafe { self.result_text(ctx, text) },
575            Err(_) => unsafe { self.result_error(ctx, "invalid utf-8") },
576        }
577    }
578    /// Providers must ensure SQLite copies or retains the buffer for `v`.
579    unsafe fn result_blob(&self, ctx: NonNull<Self::Context>, v: &[u8]);
580    /// Set current function result to an error message.
581    unsafe fn result_error(&self, ctx: NonNull<Self::Context>, msg: &str);
582    /// Return function `user_data` for `ctx`.
583    unsafe fn user_data(ctx: NonNull<Self::Context>) -> *mut c_void;
584
585    /// SQLite storage class of a UDF argument value.
586    unsafe fn value_type(&self, v: NonNull<Self::Value>) -> ValueType;
587    /// Integer view of a UDF argument value.
588    unsafe fn value_int64(&self, v: NonNull<Self::Value>) -> i64;
589    /// Floating view of a UDF argument value.
590    unsafe fn value_double(&self, v: NonNull<Self::Value>) -> f64;
591    /// Raw text bytes view of a UDF argument value.
592    unsafe fn value_text(&self, v: NonNull<Self::Value>) -> RawBytes;
593    /// Raw blob bytes view of a UDF argument value.
594    unsafe fn value_blob(&self, v: NonNull<Self::Value>) -> RawBytes;
595
596    /// Declare a virtual table schema during xCreate/xConnect.
597    unsafe fn declare_vtab(&self, db: NonNull<Self::Db>, schema: &str) -> Result<()>;
598
599    /// Register a virtual table module.
600    unsafe fn create_module_v2(
601        &self,
602        db: NonNull<Self::Db>,
603        name: &str,
604        module: &'static sqlite3_module<Self>,
605        user_data: *mut c_void,
606        drop_user_data: Option<extern "C" fn(*mut c_void)>,
607    ) -> Result<()>
608    where
609        Self: Sized;
610}
611
612/// Optional backend extension for SQLCipher-style keying.
613///
614/// # Safety
615/// Implementations must uphold the same pointer and lifetime guarantees as
616/// `Sqlite3Api` for key material and database handles.
617#[allow(clippy::missing_safety_doc)]
618pub unsafe trait Sqlite3Keying: Sqlite3Api {
619    /// Apply an encryption key to an open database handle.
620    unsafe fn key(&self, db: NonNull<Self::Db>, key: &[u8]) -> Result<()>;
621    /// Change the encryption key for an already-keyed database.
622    unsafe fn rekey(&self, db: NonNull<Self::Db>, key: &[u8]) -> Result<()>;
623}
624
625/// Bytes owned by the backend and freed via `Sqlite3Serialize::free`.
626#[derive(Clone, Copy, Debug)]
627pub struct OwnedBytes {
628    /// Pointer returned by backend serialize allocator.
629    pub ptr: NonNull<u8>,
630    /// Length of serialized byte payload.
631    pub len: usize,
632}
633
634/// Optional backend extension for hooks (trace/progress/busy/authorizer).
635///
636/// # Safety
637/// Implementations must keep callback/context pointers valid per SQLite's hook
638/// registration contract and ensure callback ABI compatibility.
639#[allow(clippy::missing_safety_doc)]
640pub unsafe trait Sqlite3Hooks: Sqlite3Api {
641    /// Register or clear a `sqlite3_trace_v2` callback.
642    unsafe fn trace_v2(
643        &self,
644        db: NonNull<Self::Db>,
645        mask: u32,
646        callback: Option<extern "C" fn(u32, *mut c_void, *mut c_void, *mut c_void)>,
647        context: *mut c_void,
648    ) -> Result<()>;
649    /// Register or clear a progress handler callback.
650    unsafe fn progress_handler(
651        &self,
652        db: NonNull<Self::Db>,
653        n: i32,
654        callback: Option<extern "C" fn(*mut c_void) -> i32>,
655        context: *mut c_void,
656    ) -> Result<()>;
657    /// Set busy timeout in milliseconds.
658    unsafe fn busy_timeout(&self, db: NonNull<Self::Db>, ms: i32) -> Result<()>;
659    /// Register or clear an authorizer callback.
660    unsafe fn set_authorizer(
661        &self,
662        db: NonNull<Self::Db>,
663        callback: Option<
664            extern "C" fn(
665                *mut c_void,
666                i32,
667                *const c_char,
668                *const c_char,
669                *const c_char,
670                *const c_char,
671            ) -> i32,
672        >,
673        context: *mut c_void,
674    ) -> Result<()>;
675}
676
677/// Optional backend extension for online backup.
678///
679/// # Safety
680/// Implementations must return backup handles tied to the provided database
681/// handles and honor SQLite's backup lifecycle requirements.
682#[allow(clippy::missing_safety_doc)]
683pub unsafe trait Sqlite3Backup: Sqlite3Api {
684    /// Backend-specific backup-handle type.
685    type Backup;
686    /// Start a backup from `source_db/source_name` into `dest_db/dest_name`.
687    unsafe fn backup_init(
688        &self,
689        dest_db: NonNull<Self::Db>,
690        dest_name: &str,
691        source_db: NonNull<Self::Db>,
692        source_name: &str,
693    ) -> Result<NonNull<Self::Backup>>;
694    /// Copy up to `pages` pages from source to destination.
695    unsafe fn backup_step(&self, backup: NonNull<Self::Backup>, pages: i32) -> Result<()>;
696    /// Return the number of pages still remaining.
697    unsafe fn backup_remaining(&self, backup: NonNull<Self::Backup>) -> i32;
698    /// Return the total page count of the source database.
699    unsafe fn backup_pagecount(&self, backup: NonNull<Self::Backup>) -> i32;
700    /// Finish and release a backup handle.
701    unsafe fn backup_finish(&self, backup: NonNull<Self::Backup>) -> Result<()>;
702}
703
704/// Optional backend extension for incremental blob I/O.
705///
706/// # Safety
707/// Implementations must validate offsets/buffer sizes and preserve SQLite blob
708/// handle validity across read/write operations.
709#[allow(clippy::missing_safety_doc)]
710pub unsafe trait Sqlite3BlobIo: Sqlite3Api {
711    /// Backend-specific incremental-blob handle type.
712    type Blob;
713    /// Open an incremental blob handle for a row/column cell.
714    unsafe fn blob_open(
715        &self,
716        db: NonNull<Self::Db>,
717        db_name: &str,
718        table: &str,
719        column: &str,
720        rowid: i64,
721        flags: u32,
722    ) -> Result<NonNull<Self::Blob>>;
723    /// Read bytes from `blob` into `data` starting at `offset`.
724    unsafe fn blob_read(
725        &self,
726        blob: NonNull<Self::Blob>,
727        data: &mut [u8],
728        offset: i32,
729    ) -> Result<()>;
730    /// Write `data` into `blob` starting at `offset`.
731    unsafe fn blob_write(&self, blob: NonNull<Self::Blob>, data: &[u8], offset: i32) -> Result<()>;
732    /// Return blob size in bytes.
733    unsafe fn blob_bytes(&self, blob: NonNull<Self::Blob>) -> i32;
734    /// Close and release a blob handle.
735    unsafe fn blob_close(&self, blob: NonNull<Self::Blob>) -> Result<()>;
736}
737
738/// Optional backend extension for serialize/deserialize.
739///
740/// # Safety
741/// Implementations must pair `serialize` allocations with `free`, and
742/// `deserialize` must not retain references to caller-owned `data`.
743#[allow(clippy::missing_safety_doc)]
744pub unsafe trait Sqlite3Serialize: Sqlite3Api {
745    /// Serialize a schema database into backend-owned bytes.
746    unsafe fn serialize(
747        &self,
748        db: NonNull<Self::Db>,
749        schema: Option<&str>,
750        flags: u32,
751    ) -> Result<OwnedBytes>;
752    /// Replace schema contents from caller-owned bytes.
753    unsafe fn deserialize(
754        &self,
755        db: NonNull<Self::Db>,
756        schema: Option<&str>,
757        data: &[u8],
758        flags: u32,
759    ) -> Result<()>;
760    /// Free serialized bytes previously returned by `serialize`.
761    unsafe fn free(&self, bytes: OwnedBytes);
762}
763
764/// Optional backend extension for WAL helpers.
765///
766/// # Safety
767/// Implementations must only operate on valid database handles and obey SQLite
768/// checkpoint and WAL-state semantics.
769#[allow(clippy::missing_safety_doc)]
770pub unsafe trait Sqlite3Wal: Sqlite3Api {
771    /// Run a passive checkpoint for `db_name` (or main when `None`).
772    unsafe fn wal_checkpoint(&self, db: NonNull<Self::Db>, db_name: Option<&str>) -> Result<()>;
773    /// Run checkpoint mode `mode`, returning `(log_frames, checkpointed_frames)`.
774    unsafe fn wal_checkpoint_v2(
775        &self,
776        db: NonNull<Self::Db>,
777        db_name: Option<&str>,
778        mode: i32,
779    ) -> Result<(i32, i32)>;
780    /// Return backend-specific WAL frame count when supported.
781    unsafe fn wal_frame_count(&self, db: NonNull<Self::Db>) -> Result<Option<u32>>;
782}
783
784/// Optional backend extension for metadata helpers.
785///
786/// # Safety
787/// Implementations must ensure returned raw metadata pointers are valid for the
788/// documented SQLite lifetime and ownership rules.
789#[allow(clippy::missing_safety_doc)]
790pub unsafe trait Sqlite3Metadata: Sqlite3Api {
791    /// Query table/column metadata for the specified schema object.
792    unsafe fn table_column_metadata(
793        &self,
794        db: NonNull<Self::Db>,
795        db_name: Option<&str>,
796        table: &str,
797        column: &str,
798    ) -> Result<ColumnMetadata>;
799    /// Return declared type text for output column `col`.
800    unsafe fn column_decltype(&self, stmt: NonNull<Self::Stmt>, col: i32) -> Option<RawBytes>;
801    /// Return output column name for `col`.
802    unsafe fn column_name(&self, stmt: NonNull<Self::Stmt>, col: i32) -> Option<RawBytes>;
803    /// Return source table name for output column `col`, when available.
804    unsafe fn column_table_name(&self, stmt: NonNull<Self::Stmt>, col: i32) -> Option<RawBytes>;
805}
806
807/// Column metadata returned by `Sqlite3Metadata`.
808#[derive(Clone, Copy, Debug)]
809pub struct ColumnMetadata {
810    /// Declared column type as raw backend bytes, if available.
811    pub data_type: Option<RawBytes>,
812    /// Declared collation sequence as raw backend bytes, if available.
813    pub coll_seq: Option<RawBytes>,
814    /// Whether the column has a `NOT NULL` constraint.
815    pub not_null: bool,
816    /// Whether the column participates in the primary key.
817    pub primary_key: bool,
818    /// Whether the column is auto-incrementing.
819    pub autoinc: bool,
820}
821
822/// Typed wrapper for `sqlite3_module`.
823#[repr(C)]
824pub struct sqlite3_module<P: Sqlite3Api> {
825    /// Module ABI version.
826    pub i_version: i32,
827    /// `xCreate` callback for `CREATE VIRTUAL TABLE`.
828    pub x_create: Option<
829        extern "C" fn(
830            *mut P::Db,
831            *mut c_void,
832            i32,
833            *const *const u8,
834            *mut *mut P::VTab,
835            *mut *mut u8,
836        ) -> i32,
837    >,
838    /// `xConnect` callback for `CONNECT`ing to an existing virtual table.
839    pub x_connect: Option<
840        extern "C" fn(
841            *mut P::Db,
842            *mut c_void,
843            i32,
844            *const *const u8,
845            *mut *mut P::VTab,
846            *mut *mut u8,
847        ) -> i32,
848    >,
849    /// `xBestIndex` query-planning callback.
850    pub x_best_index: Option<extern "C" fn(*mut P::VTab, *mut c_void) -> i32>,
851    /// `xDisconnect` callback for disconnecting a table instance.
852    pub x_disconnect: Option<extern "C" fn(*mut P::VTab) -> i32>,
853    /// `xDestroy` callback for dropping a virtual table.
854    pub x_destroy: Option<extern "C" fn(*mut P::VTab) -> i32>,
855    /// `xOpen` callback for creating a cursor.
856    pub x_open: Option<extern "C" fn(*mut P::VTab, *mut *mut P::VTabCursor) -> i32>,
857    /// `xClose` callback for closing a cursor.
858    pub x_close: Option<extern "C" fn(*mut P::VTabCursor) -> i32>,
859    /// `xFilter` callback for starting a cursor scan.
860    pub x_filter:
861        Option<extern "C" fn(*mut P::VTabCursor, i32, *const u8, i32, *mut *mut P::Value) -> i32>,
862    /// `xNext` callback for advancing a cursor.
863    pub x_next: Option<extern "C" fn(*mut P::VTabCursor) -> i32>,
864    /// `xEof` callback for cursor end-of-scan checks.
865    pub x_eof: Option<extern "C" fn(*mut P::VTabCursor) -> i32>,
866    /// `xColumn` callback for reading the current row value.
867    pub x_column: Option<extern "C" fn(*mut P::VTabCursor, *mut P::Context, i32) -> i32>,
868    /// `xRowid` callback for reading the current rowid.
869    pub x_rowid: Option<extern "C" fn(*mut P::VTabCursor, *mut i64) -> i32>,
870}