Skip to main content

sqlite_provider_sqlite3/
extensions_impl.rs

1use super::*;
2
3#[allow(unsafe_op_in_unsafe_fn)]
4unsafe impl Sqlite3Metadata for LibSqlite3 {
5    unsafe fn table_column_metadata(
6        &self,
7        db: NonNull<Self::Db>,
8        db_name: Option<&str>,
9        table: &str,
10        column: &str,
11    ) -> Result<ColumnMetadata> {
12        let table = CString::new(table)
13            .map_err(|_| Error::with_message(ErrorCode::Misuse, "table contains NUL"))?;
14        let column = CString::new(column)
15            .map_err(|_| Error::with_message(ErrorCode::Misuse, "column contains NUL"))?;
16        let db_name = match db_name {
17            Some(name) => Some(
18                CString::new(name)
19                    .map_err(|_| Error::with_message(ErrorCode::Misuse, "db name contains NUL"))?,
20            ),
21            None => None,
22        };
23        let func = match self.fns.table_column_metadata {
24            Some(func) => func,
25            None => {
26                return Err(Error::feature_unavailable(
27                    "table_column_metadata not available",
28                ));
29            }
30        };
31        let mut data_type = null();
32        let mut coll_seq = null();
33        let mut not_null = 0;
34        let mut primary_key = 0;
35        let mut autoinc = 0;
36        let rc = func(
37            db.as_ptr(),
38            db_name.as_ref().map(|s| s.as_ptr()).unwrap_or(null()),
39            table.as_ptr(),
40            column.as_ptr(),
41            &mut data_type,
42            &mut coll_seq,
43            &mut not_null,
44            &mut primary_key,
45            &mut autoinc,
46        );
47        if rc != SQLITE_OK {
48            return Err(self.error_from_rc(rc, Some(db)));
49        }
50        Ok(ColumnMetadata {
51            data_type: raw_cstr(data_type).map(raw_bytes_from_cstr),
52            coll_seq: raw_cstr(coll_seq).map(raw_bytes_from_cstr),
53            not_null: not_null != 0,
54            primary_key: primary_key != 0,
55            autoinc: autoinc != 0,
56        })
57    }
58
59    unsafe fn column_decltype(&self, stmt: NonNull<Self::Stmt>, col: i32) -> Option<RawBytes> {
60        let ptr = (self.fns.column_decltype)(stmt.as_ptr(), col);
61        raw_cstr(ptr).map(raw_bytes_from_cstr)
62    }
63
64    unsafe fn column_name(&self, stmt: NonNull<Self::Stmt>, col: i32) -> Option<RawBytes> {
65        let ptr = (self.fns.column_name)(stmt.as_ptr(), col);
66        raw_cstr(ptr).map(raw_bytes_from_cstr)
67    }
68
69    unsafe fn column_table_name(&self, stmt: NonNull<Self::Stmt>, col: i32) -> Option<RawBytes> {
70        let func = self.fns.column_table_name?;
71        let ptr = func(stmt.as_ptr(), col);
72        raw_cstr(ptr).map(raw_bytes_from_cstr)
73    }
74}
75
76#[allow(unsafe_op_in_unsafe_fn)]
77unsafe impl Sqlite3Hooks for LibSqlite3 {
78    unsafe fn trace_v2(
79        &self,
80        db: NonNull<Self::Db>,
81        mask: u32,
82        callback: Option<extern "C" fn(u32, *mut c_void, *mut c_void, *mut c_void)>,
83        context: *mut c_void,
84    ) -> Result<()> {
85        let trace = match self.fns.trace_v2 {
86            Some(trace) => trace,
87            None => return Err(Error::feature_unavailable("trace_v2 not available")),
88        };
89        let rc = trace(db.as_ptr(), mask, callback, context);
90        if rc == SQLITE_OK {
91            Ok(())
92        } else {
93            Err(self.error_from_rc(rc, Some(db)))
94        }
95    }
96
97    unsafe fn progress_handler(
98        &self,
99        db: NonNull<Self::Db>,
100        n: i32,
101        callback: Option<extern "C" fn(*mut c_void) -> i32>,
102        context: *mut c_void,
103    ) -> Result<()> {
104        let progress = match self.fns.progress_handler {
105            Some(progress) => progress,
106            None => return Err(Error::feature_unavailable("progress_handler not available")),
107        };
108        progress(db.as_ptr(), n, callback, context);
109        Ok(())
110    }
111
112    unsafe fn busy_timeout(&self, db: NonNull<Self::Db>, ms: i32) -> Result<()> {
113        let busy_timeout = match self.fns.busy_timeout {
114            Some(busy_timeout) => busy_timeout,
115            None => return Err(Error::feature_unavailable("busy_timeout not available")),
116        };
117        let rc = busy_timeout(db.as_ptr(), ms);
118        if rc == SQLITE_OK {
119            Ok(())
120        } else {
121            Err(self.error_from_rc(rc, Some(db)))
122        }
123    }
124
125    unsafe fn set_authorizer(
126        &self,
127        db: NonNull<Self::Db>,
128        callback: Option<
129            extern "C" fn(
130                *mut c_void,
131                i32,
132                *const c_char,
133                *const c_char,
134                *const c_char,
135                *const c_char,
136            ) -> i32,
137        >,
138        context: *mut c_void,
139    ) -> Result<()> {
140        let set_authorizer = match self.fns.set_authorizer {
141            Some(set_authorizer) => set_authorizer,
142            None => return Err(Error::feature_unavailable("set_authorizer not available")),
143        };
144        let rc = set_authorizer(db.as_ptr(), callback, context);
145        if rc == SQLITE_OK {
146            Ok(())
147        } else {
148            Err(self.error_from_rc(rc, Some(db)))
149        }
150    }
151}
152
153#[allow(unsafe_op_in_unsafe_fn)]
154unsafe impl Sqlite3Backup for LibSqlite3 {
155    type Backup = sqlite3_backup;
156
157    unsafe fn backup_init(
158        &self,
159        dest_db: NonNull<Self::Db>,
160        dest_name: &str,
161        source_db: NonNull<Self::Db>,
162        source_name: &str,
163    ) -> Result<NonNull<Self::Backup>> {
164        let backup_init = match self.fns.backup_init {
165            Some(backup_init) => backup_init,
166            None => return Err(Error::feature_unavailable("backup_init not available")),
167        };
168        let dest_name = CString::new(dest_name)
169            .map_err(|_| Error::with_message(ErrorCode::Misuse, "dest name contains NUL"))?;
170        let source_name = CString::new(source_name)
171            .map_err(|_| Error::with_message(ErrorCode::Misuse, "source name contains NUL"))?;
172        let backup = backup_init(
173            dest_db.as_ptr(),
174            dest_name.as_ptr(),
175            source_db.as_ptr(),
176            source_name.as_ptr(),
177        );
178        if let Some(backup) = NonNull::new(backup) {
179            Ok(backup)
180        } else {
181            let rc = (self.fns.errcode)(dest_db.as_ptr());
182            Err(self.error_from_rc(rc, Some(dest_db)))
183        }
184    }
185
186    unsafe fn backup_step(&self, backup: NonNull<Self::Backup>, pages: i32) -> Result<()> {
187        let backup_step = match self.fns.backup_step {
188            Some(backup_step) => backup_step,
189            None => return Err(Error::feature_unavailable("backup_step not available")),
190        };
191        let rc = backup_step(backup.as_ptr(), pages);
192        if rc == SQLITE_OK || rc == SQLITE_DONE {
193            Ok(())
194        } else {
195            Err(self.error_from_rc(rc, None))
196        }
197    }
198
199    unsafe fn backup_remaining(&self, backup: NonNull<Self::Backup>) -> i32 {
200        self.fns
201            .backup_remaining
202            .map(|f| f(backup.as_ptr()))
203            .unwrap_or(0)
204    }
205
206    unsafe fn backup_pagecount(&self, backup: NonNull<Self::Backup>) -> i32 {
207        self.fns
208            .backup_pagecount
209            .map(|f| f(backup.as_ptr()))
210            .unwrap_or(0)
211    }
212
213    unsafe fn backup_finish(&self, backup: NonNull<Self::Backup>) -> Result<()> {
214        let backup_finish = match self.fns.backup_finish {
215            Some(backup_finish) => backup_finish,
216            None => return Err(Error::feature_unavailable("backup_finish not available")),
217        };
218        let rc = backup_finish(backup.as_ptr());
219        if rc == SQLITE_OK {
220            Ok(())
221        } else {
222            Err(self.error_from_rc(rc, None))
223        }
224    }
225}
226
227#[allow(unsafe_op_in_unsafe_fn)]
228unsafe impl Sqlite3BlobIo for LibSqlite3 {
229    type Blob = sqlite3_blob;
230
231    unsafe fn blob_open(
232        &self,
233        db: NonNull<Self::Db>,
234        db_name: &str,
235        table: &str,
236        column: &str,
237        rowid: i64,
238        flags: u32,
239    ) -> Result<NonNull<Self::Blob>> {
240        let blob_open = match self.fns.blob_open {
241            Some(blob_open) => blob_open,
242            None => return Err(Error::feature_unavailable("blob_open not available")),
243        };
244        let db_name = CString::new(db_name)
245            .map_err(|_| Error::with_message(ErrorCode::Misuse, "db name contains NUL"))?;
246        let table = CString::new(table)
247            .map_err(|_| Error::with_message(ErrorCode::Misuse, "table contains NUL"))?;
248        let column = CString::new(column)
249            .map_err(|_| Error::with_message(ErrorCode::Misuse, "column contains NUL"))?;
250        let mut blob = null_mut();
251        let rc = blob_open(
252            db.as_ptr(),
253            db_name.as_ptr(),
254            table.as_ptr(),
255            column.as_ptr(),
256            rowid,
257            flags as c_int,
258            &mut blob,
259        );
260        if rc != SQLITE_OK {
261            return Err(self.error_from_rc(rc, Some(db)));
262        }
263        NonNull::new(blob).ok_or_else(|| {
264            Error::with_message(
265                ErrorCode::Error,
266                "sqlite3_blob_open returned success with null blob handle",
267            )
268        })
269    }
270
271    unsafe fn blob_read(
272        &self,
273        blob: NonNull<Self::Blob>,
274        data: &mut [u8],
275        offset: i32,
276    ) -> Result<()> {
277        if data.len() > i32::MAX as usize {
278            return Err(Error::with_message(
279                ErrorCode::TooBig,
280                "blob read buffer too large",
281            ));
282        }
283        let blob_read = match self.fns.blob_read {
284            Some(blob_read) => blob_read,
285            None => return Err(Error::feature_unavailable("blob_read not available")),
286        };
287        let ptr = if data.is_empty() {
288            null_mut()
289        } else {
290            data.as_mut_ptr() as *mut c_void
291        };
292        let rc = blob_read(blob.as_ptr(), ptr, clamp_len(data.len()), offset);
293        if rc == SQLITE_OK {
294            Ok(())
295        } else {
296            Err(self.error_from_rc(rc, None))
297        }
298    }
299
300    unsafe fn blob_write(&self, blob: NonNull<Self::Blob>, data: &[u8], offset: i32) -> Result<()> {
301        if data.len() > i32::MAX as usize {
302            return Err(Error::with_message(
303                ErrorCode::TooBig,
304                "blob write payload too large",
305            ));
306        }
307        let blob_write = match self.fns.blob_write {
308            Some(blob_write) => blob_write,
309            None => return Err(Error::feature_unavailable("blob_write not available")),
310        };
311        let ptr = if data.is_empty() {
312            null()
313        } else {
314            data.as_ptr() as *const c_void
315        };
316        let rc = blob_write(blob.as_ptr(), ptr, clamp_len(data.len()), offset);
317        if rc == SQLITE_OK {
318            Ok(())
319        } else {
320            Err(self.error_from_rc(rc, None))
321        }
322    }
323
324    unsafe fn blob_bytes(&self, blob: NonNull<Self::Blob>) -> i32 {
325        self.fns.blob_bytes.map(|f| f(blob.as_ptr())).unwrap_or(0)
326    }
327
328    unsafe fn blob_close(&self, blob: NonNull<Self::Blob>) -> Result<()> {
329        let blob_close = match self.fns.blob_close {
330            Some(blob_close) => blob_close,
331            None => return Err(Error::feature_unavailable("blob_close not available")),
332        };
333        let rc = blob_close(blob.as_ptr());
334        if rc == SQLITE_OK {
335            Ok(())
336        } else {
337            Err(self.error_from_rc(rc, None))
338        }
339    }
340}
341
342#[allow(unsafe_op_in_unsafe_fn)]
343unsafe impl Sqlite3Serialize for LibSqlite3 {
344    unsafe fn serialize(
345        &self,
346        db: NonNull<Self::Db>,
347        schema: Option<&str>,
348        flags: u32,
349    ) -> Result<OwnedBytes> {
350        let serialize = match self.fns.serialize {
351            Some(serialize) => serialize,
352            None => return Err(Error::feature_unavailable("serialize not available")),
353        };
354        let schema = match schema {
355            Some(schema) => Some(
356                CString::new(schema)
357                    .map_err(|_| Error::with_message(ErrorCode::Misuse, "schema contains NUL"))?,
358            ),
359            None => None,
360        };
361        let mut len: i64 = 0;
362        let ptr = serialize(
363            db.as_ptr(),
364            schema.as_ref().map(|s| s.as_ptr()).unwrap_or(null()),
365            &mut len,
366            flags,
367        );
368        if ptr.is_null() {
369            if len == 0 {
370                let empty_ptr = (self.fns.malloc)(1);
371                let empty_ptr = NonNull::new(empty_ptr as *mut u8)
372                    .ok_or_else(|| Error::new(ErrorCode::NoMem))?;
373                return Ok(OwnedBytes {
374                    ptr: empty_ptr,
375                    len: 0,
376                });
377            }
378            let rc = (self.fns.errcode)(db.as_ptr());
379            return Err(self.error_from_rc(rc, Some(db)));
380        }
381        let len = usize::try_from(len).map_err(|_| {
382            unsafe { (self.fns.free)(ptr as *mut c_void) };
383            Error::with_message(ErrorCode::TooBig, "serialized payload length out of range")
384        })?;
385        Ok(OwnedBytes {
386            ptr: NonNull::new_unchecked(ptr),
387            len,
388        })
389    }
390
391    unsafe fn deserialize(
392        &self,
393        db: NonNull<Self::Db>,
394        schema: Option<&str>,
395        data: &[u8],
396        flags: u32,
397    ) -> Result<()> {
398        let deserialize = match self.fns.deserialize {
399            Some(deserialize) => deserialize,
400            None => return Err(Error::feature_unavailable("deserialize not available")),
401        };
402        if data.len() > i32::MAX as usize {
403            return Err(Error::with_message(
404                ErrorCode::TooBig,
405                "deserialize payload too large",
406            ));
407        }
408        let schema = match schema {
409            Some(schema) => Some(
410                CString::new(schema)
411                    .map_err(|_| Error::with_message(ErrorCode::Misuse, "schema contains NUL"))?,
412            ),
413            None => None,
414        };
415        let mut owned = null_mut::<c_uchar>();
416        if !data.is_empty() {
417            owned = (self.fns.malloc)(data.len() as c_int) as *mut c_uchar;
418            if owned.is_null() {
419                return Err(Error::new(ErrorCode::NoMem));
420            }
421            std::ptr::copy_nonoverlapping(data.as_ptr(), owned, data.len());
422        }
423        let mut deserialize_flags = flags;
424        if !owned.is_null() {
425            deserialize_flags |= SQLITE_DESERIALIZE_FREEONCLOSE;
426        }
427        let len = i64::try_from(data.len())
428            .map_err(|_| Error::with_message(ErrorCode::TooBig, "deserialize payload too large"))?;
429        let rc = deserialize(
430            db.as_ptr(),
431            schema.as_ref().map(|s| s.as_ptr()).unwrap_or(null()),
432            owned,
433            len,
434            len,
435            deserialize_flags,
436        );
437        if rc == SQLITE_OK {
438            Ok(())
439        } else {
440            if !owned.is_null() {
441                (self.fns.free)(owned as *mut c_void);
442            }
443            Err(self.error_from_rc(rc, Some(db)))
444        }
445    }
446
447    unsafe fn free(&self, bytes: OwnedBytes) {
448        (self.fns.free)(bytes.ptr.as_ptr() as *mut c_void);
449    }
450}
451
452#[allow(unsafe_op_in_unsafe_fn)]
453unsafe impl Sqlite3Wal for LibSqlite3 {
454    unsafe fn wal_checkpoint(&self, db: NonNull<Self::Db>, db_name: Option<&str>) -> Result<()> {
455        let wal_checkpoint = match self.fns.wal_checkpoint {
456            Some(wal_checkpoint) => wal_checkpoint,
457            None => return Err(Error::feature_unavailable("wal_checkpoint not available")),
458        };
459        let db_name = match db_name {
460            Some(db_name) => Some(
461                CString::new(db_name)
462                    .map_err(|_| Error::with_message(ErrorCode::Misuse, "db name contains NUL"))?,
463            ),
464            None => None,
465        };
466        let rc = wal_checkpoint(
467            db.as_ptr(),
468            db_name.as_ref().map(|s| s.as_ptr()).unwrap_or(null()),
469        );
470        if rc == SQLITE_OK {
471            Ok(())
472        } else {
473            Err(self.error_from_rc(rc, Some(db)))
474        }
475    }
476
477    unsafe fn wal_checkpoint_v2(
478        &self,
479        db: NonNull<Self::Db>,
480        db_name: Option<&str>,
481        mode: i32,
482    ) -> Result<(i32, i32)> {
483        let wal_checkpoint_v2 = match self.fns.wal_checkpoint_v2 {
484            Some(wal_checkpoint_v2) => wal_checkpoint_v2,
485            None => {
486                return Err(Error::feature_unavailable(
487                    "wal_checkpoint_v2 not available",
488                ));
489            }
490        };
491        let db_name = match db_name {
492            Some(db_name) => Some(
493                CString::new(db_name)
494                    .map_err(|_| Error::with_message(ErrorCode::Misuse, "db name contains NUL"))?,
495            ),
496            None => None,
497        };
498        let mut log = 0;
499        let mut checkpointed = 0;
500        let rc = wal_checkpoint_v2(
501            db.as_ptr(),
502            db_name.as_ref().map(|s| s.as_ptr()).unwrap_or(null()),
503            mode,
504            &mut log,
505            &mut checkpointed,
506        );
507        if rc == SQLITE_OK {
508            Ok((log, checkpointed))
509        } else {
510            Err(self.error_from_rc(rc, Some(db)))
511        }
512    }
513
514    unsafe fn wal_frame_count(&self, db: NonNull<Self::Db>) -> Result<Option<u32>> {
515        let wal_frame_count = match self.fns.libsql_wal_frame_count {
516            Some(wal_frame_count) => wal_frame_count,
517            None => return Ok(None),
518        };
519        let mut count = 0_u32;
520        let rc = wal_frame_count(db.as_ptr(), &mut count);
521        if rc == SQLITE_OK {
522            Ok(Some(count))
523        } else {
524            Err(self.error_from_rc(rc, Some(db)))
525        }
526    }
527}