1use std::borrow::Cow::{self, Borrowed, Owned};
13use std::marker::PhantomData;
14use std::os::raw::{c_char, c_int, c_void};
15use std::ptr;
16use std::slice;
17
18use crate::context::set_result;
19use crate::error::{error_from_sqlite_code, to_sqlite_error};
20use crate::ffi;
21pub use crate::ffi::{sqlite3_vtab, sqlite3_vtab_cursor};
22use crate::types::{FromSql, FromSqlError, ToSql, ValueRef};
23use crate::util::alloc;
24use crate::{str_to_cstring, Connection, Error, InnerConnection, Result};
25
26pub enum VTabKind {
62 Default,
64 Eponymous,
68 EponymousOnly,
75}
76
77#[repr(transparent)]
81pub struct Module<'vtab, T: VTab<'vtab>> {
82 base: ffi::sqlite3_module,
83 phantom: PhantomData<&'vtab T>,
84}
85
86unsafe impl<'vtab, T: VTab<'vtab>> Send for Module<'vtab, T> {}
87unsafe impl<'vtab, T: VTab<'vtab>> Sync for Module<'vtab, T> {}
88
89union ModuleZeroHack {
90 bytes: [u8; size_of::<ffi::sqlite3_module>()],
91 module: ffi::sqlite3_module,
92}
93
94const ZERO_MODULE: ffi::sqlite3_module = unsafe {
98 ModuleZeroHack {
99 bytes: [0_u8; size_of::<ffi::sqlite3_module>()],
100 }
101 .module
102};
103
104macro_rules! module {
105 ($lt:lifetime, $vt:ty, $ct:ty, $xc:expr, $xd:expr, $xu:expr, $xbegin:expr, $xsync:expr, $xcommit:expr, $xrollback:expr) => {
106 &Module {
107 base: ffi::sqlite3_module {
108 iVersion: 2,
110 xCreate: $xc,
111 xConnect: Some(rust_connect::<$vt>),
112 xBestIndex: Some(rust_best_index::<$vt>),
113 xDisconnect: Some(rust_disconnect::<$vt>),
114 xDestroy: $xd,
115 xOpen: Some(rust_open::<$vt>),
116 xClose: Some(rust_close::<$ct>),
117 xFilter: Some(rust_filter::<$ct>),
118 xNext: Some(rust_next::<$ct>),
119 xEof: Some(rust_eof::<$ct>),
120 xColumn: Some(rust_column::<$ct>),
121 xRowid: Some(rust_rowid::<$ct>), xUpdate: $xu,
123 xBegin: $xbegin,
124 xSync: $xsync,
125 xCommit: $xcommit,
126 xRollback: $xrollback,
127 xFindFunction: None,
128 xRename: None,
129 xSavepoint: None,
130 xRelease: None,
131 xRollbackTo: None,
132 ..ZERO_MODULE
133 },
134 phantom: PhantomData::<&$lt $vt>,
135 }
136 };
137}
138
139#[must_use]
143pub fn update_module<'vtab, T: UpdateVTab<'vtab>>() -> &'static Module<'vtab, T> {
144 match T::KIND {
145 VTabKind::EponymousOnly => {
146 module!('vtab, T, T::Cursor, None, None, Some(rust_update::<T>), None, None, None, None)
147 }
148 VTabKind::Eponymous => {
149 module!('vtab, T, T::Cursor, Some(rust_connect::<T>), Some(rust_disconnect::<T>), Some(rust_update::<T>), None, None, None, None)
150 }
151 _ => {
152 module!('vtab, T, T::Cursor, Some(rust_create::<T>), Some(rust_destroy::<T>), Some(rust_update::<T>), None, None, None, None)
153 }
154 }
155}
156
157#[must_use]
161pub fn update_module_with_tx<'vtab, T: TransactionVTab<'vtab>>() -> &'static Module<'vtab, T> {
162 match T::KIND {
163 VTabKind::EponymousOnly => {
164 module!('vtab, T, T::Cursor, None, None, Some(rust_update::<T>), Some(rust_begin::<T>), Some(rust_sync::<T>), Some(rust_commit::<T>), Some(rust_rollback::<T>))
165 }
166 VTabKind::Eponymous => {
167 module!('vtab, T, T::Cursor, Some(rust_connect::<T>), Some(rust_disconnect::<T>), Some(rust_update::<T>), Some(rust_begin::<T>), Some(rust_sync::<T>), Some(rust_commit::<T>), Some(rust_rollback::<T>))
168 }
169 _ => {
170 module!('vtab, T, T::Cursor, Some(rust_create::<T>), Some(rust_destroy::<T>), Some(rust_update::<T>), Some(rust_begin::<T>), Some(rust_sync::<T>), Some(rust_commit::<T>), Some(rust_rollback::<T>))
171 }
172 }
173}
174
175#[must_use]
179pub fn read_only_module<'vtab, T: CreateVTab<'vtab>>() -> &'static Module<'vtab, T> {
180 match T::KIND {
181 VTabKind::EponymousOnly => eponymous_only_module(),
182 VTabKind::Eponymous => {
183 module!('vtab, T, T::Cursor, Some(rust_connect::<T>), Some(rust_disconnect::<T>), None, None, None, None, None)
186 }
187 _ => {
188 module!('vtab, T, T::Cursor, Some(rust_create::<T>), Some(rust_destroy::<T>), None, None, None, None, None)
191 }
192 }
193}
194
195#[must_use]
199pub fn eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab, T> {
200 module!('vtab, T, T::Cursor, None, None, None, None, None, None, None)
202}
203
204#[repr(i32)]
206#[non_exhaustive]
207#[derive(Debug, Clone, Copy, Eq, PartialEq)]
208pub enum VTabConfig {
209 ConstraintSupport = 1,
211 Innocuous = 2,
213 DirectOnly = 3,
215 UsesAllSchemas = 4,
217}
218
219pub struct VTabConnection(*mut ffi::sqlite3);
221
222impl VTabConnection {
223 pub fn config(&mut self, config: VTabConfig) -> Result<()> {
225 crate::error::check(unsafe { ffi::sqlite3_vtab_config(self.0, config as c_int) })
226 }
227
228 pub unsafe fn handle(&mut self) -> *mut ffi::sqlite3 {
244 self.0
245 }
246}
247
248pub unsafe trait VTab<'vtab>: Sized {
266 type Aux;
268 type Cursor: VTabCursor;
270
271 fn connect(
275 db: &mut VTabConnection,
276 aux: Option<&Self::Aux>,
277 args: &[&[u8]],
278 ) -> Result<(String, Self)>;
279
280 fn best_index(&self, info: &mut IndexInfo) -> Result<()>;
283
284 fn open(&'vtab mut self) -> Result<Self::Cursor>;
287}
288
289pub trait CreateVTab<'vtab>: VTab<'vtab> {
293 const KIND: VTabKind;
297 fn create(
305 db: &mut VTabConnection,
306 aux: Option<&Self::Aux>,
307 args: &[&[u8]],
308 ) -> Result<(String, Self)> {
309 Self::connect(db, aux, args)
310 }
311
312 fn destroy(&self) -> Result<()> {
318 Ok(())
319 }
320}
321
322pub trait UpdateVTab<'vtab>: CreateVTab<'vtab> {
326 fn delete(&mut self, arg: ValueRef<'_>) -> Result<()>;
328 fn insert(&mut self, args: &Values<'_>) -> Result<i64>;
334 fn update(&mut self, args: &Values<'_>) -> Result<()>;
337}
338
339pub trait TransactionVTab<'vtab>: UpdateVTab<'vtab> {
343 fn begin(&mut self) -> Result<()>;
345 fn sync(&mut self) -> Result<()>;
347 fn commit(&mut self) -> Result<()>;
349 fn rollback(&mut self) -> Result<()>;
351}
352
353#[derive(Debug, Eq, PartialEq)]
356#[allow(missing_docs)]
357#[expect(non_camel_case_types)]
358pub enum IndexConstraintOp {
359 SQLITE_INDEX_CONSTRAINT_EQ,
360 SQLITE_INDEX_CONSTRAINT_GT,
361 SQLITE_INDEX_CONSTRAINT_LE,
362 SQLITE_INDEX_CONSTRAINT_LT,
363 SQLITE_INDEX_CONSTRAINT_GE,
364 SQLITE_INDEX_CONSTRAINT_MATCH,
365 SQLITE_INDEX_CONSTRAINT_LIKE, SQLITE_INDEX_CONSTRAINT_GLOB, SQLITE_INDEX_CONSTRAINT_REGEXP, SQLITE_INDEX_CONSTRAINT_NE, SQLITE_INDEX_CONSTRAINT_ISNOT, SQLITE_INDEX_CONSTRAINT_ISNOTNULL, SQLITE_INDEX_CONSTRAINT_ISNULL, SQLITE_INDEX_CONSTRAINT_IS, SQLITE_INDEX_CONSTRAINT_LIMIT, SQLITE_INDEX_CONSTRAINT_OFFSET, SQLITE_INDEX_CONSTRAINT_FUNCTION(u8), }
377
378impl From<u8> for IndexConstraintOp {
379 fn from(code: u8) -> Self {
380 match code {
381 2 => Self::SQLITE_INDEX_CONSTRAINT_EQ,
382 4 => Self::SQLITE_INDEX_CONSTRAINT_GT,
383 8 => Self::SQLITE_INDEX_CONSTRAINT_LE,
384 16 => Self::SQLITE_INDEX_CONSTRAINT_LT,
385 32 => Self::SQLITE_INDEX_CONSTRAINT_GE,
386 64 => Self::SQLITE_INDEX_CONSTRAINT_MATCH,
387 65 => Self::SQLITE_INDEX_CONSTRAINT_LIKE,
388 66 => Self::SQLITE_INDEX_CONSTRAINT_GLOB,
389 67 => Self::SQLITE_INDEX_CONSTRAINT_REGEXP,
390 68 => Self::SQLITE_INDEX_CONSTRAINT_NE,
391 69 => Self::SQLITE_INDEX_CONSTRAINT_ISNOT,
392 70 => Self::SQLITE_INDEX_CONSTRAINT_ISNOTNULL,
393 71 => Self::SQLITE_INDEX_CONSTRAINT_ISNULL,
394 72 => Self::SQLITE_INDEX_CONSTRAINT_IS,
395 73 => Self::SQLITE_INDEX_CONSTRAINT_LIMIT,
396 74 => Self::SQLITE_INDEX_CONSTRAINT_OFFSET,
397 v => Self::SQLITE_INDEX_CONSTRAINT_FUNCTION(v),
398 }
399 }
400}
401
402bitflags::bitflags! {
403 #[repr(C)]
406 #[derive(Copy, Clone, Debug)]
407 pub struct IndexFlags: c_int {
408 const NONE = 0;
410 const SQLITE_INDEX_SCAN_UNIQUE = ffi::SQLITE_INDEX_SCAN_UNIQUE;
412 const SQLITE_INDEX_SCAN_HEX = 0x0000_0002; }
415}
416
417#[derive(Debug)]
422pub struct IndexInfo(*mut ffi::sqlite3_index_info);
423
424impl IndexInfo {
425 #[inline]
427 pub fn constraints_and_usages(&mut self) -> IndexConstraintAndUsageIter<'_> {
428 let constraints =
429 unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
430 let constraint_usages = unsafe {
431 slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
432 };
433 IndexConstraintAndUsageIter {
434 iter: constraints.iter().zip(constraint_usages.iter_mut()),
435 }
436 }
437
438 #[inline]
440 #[must_use]
441 pub fn constraints(&self) -> IndexConstraintIter<'_> {
442 let constraints =
443 unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
444 IndexConstraintIter {
445 iter: constraints.iter(),
446 }
447 }
448
449 #[inline]
451 #[must_use]
452 pub fn order_bys(&self) -> OrderByIter<'_> {
453 let order_bys =
454 unsafe { slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize) };
455 OrderByIter {
456 iter: order_bys.iter(),
457 }
458 }
459
460 #[inline]
462 #[must_use]
463 pub fn num_of_order_by(&self) -> usize {
464 unsafe { (*self.0).nOrderBy as usize }
465 }
466
467 #[inline]
469 pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage<'_> {
470 let constraint_usages = unsafe {
471 slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
472 };
473 IndexConstraintUsage(&mut constraint_usages[constraint_idx])
474 }
475
476 #[inline]
478 pub fn set_idx_num(&mut self, idx_num: c_int) {
479 unsafe {
480 (*self.0).idxNum = idx_num;
481 }
482 }
483
484 pub fn set_idx_str(&mut self, idx_str: &str) {
486 unsafe {
487 (*self.0).idxStr = alloc(idx_str);
488 (*self.0).needToFreeIdxStr = 1;
489 }
490 }
491
492 #[inline]
494 pub fn set_order_by_consumed(&mut self, order_by_consumed: bool) {
495 unsafe {
496 (*self.0).orderByConsumed = order_by_consumed as c_int;
497 }
498 }
499
500 #[inline]
502 pub fn set_estimated_cost(&mut self, estimated_ost: f64) {
503 unsafe {
504 (*self.0).estimatedCost = estimated_ost;
505 }
506 }
507
508 #[inline]
510 pub fn set_estimated_rows(&mut self, estimated_rows: i64) {
511 unsafe {
512 (*self.0).estimatedRows = estimated_rows;
513 }
514 }
515
516 #[inline]
518 pub fn set_idx_flags(&mut self, flags: IndexFlags) {
519 unsafe { (*self.0).idxFlags = flags.bits() };
520 }
521
522 #[inline]
524 pub fn col_used(&self) -> u64 {
525 unsafe { (*self.0).colUsed }
526 }
527
528 #[cfg(feature = "modern_sqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
531 pub fn collation(&self, constraint_idx: usize) -> Result<&str> {
532 use std::ffi::CStr;
533 let idx = constraint_idx as c_int;
534 let collation = unsafe { ffi::sqlite3_vtab_collation(self.0, idx) };
535 if collation.is_null() {
536 return Err(err!(ffi::SQLITE_MISUSE, "{constraint_idx} is out of range"));
537 }
538 Ok(unsafe { CStr::from_ptr(collation) }.to_str()?)
539 }
540
541 }
564
565pub struct IndexConstraintAndUsageIter<'a> {
567 iter: std::iter::Zip<
568 slice::Iter<'a, ffi::sqlite3_index_constraint>,
569 slice::IterMut<'a, ffi::sqlite3_index_constraint_usage>,
570 >,
571}
572
573impl<'a> Iterator for IndexConstraintAndUsageIter<'a> {
574 type Item = (IndexConstraint<'a>, IndexConstraintUsage<'a>);
575
576 #[inline]
577 fn next(&mut self) -> Option<(IndexConstraint<'a>, IndexConstraintUsage<'a>)> {
578 self.iter
579 .next()
580 .map(|raw| (IndexConstraint(raw.0), IndexConstraintUsage(raw.1)))
581 }
582
583 #[inline]
584 fn size_hint(&self) -> (usize, Option<usize>) {
585 self.iter.size_hint()
586 }
587}
588
589pub struct IndexConstraintIter<'a> {
591 iter: slice::Iter<'a, ffi::sqlite3_index_constraint>,
592}
593
594impl<'a> Iterator for IndexConstraintIter<'a> {
595 type Item = IndexConstraint<'a>;
596
597 #[inline]
598 fn next(&mut self) -> Option<IndexConstraint<'a>> {
599 self.iter.next().map(IndexConstraint)
600 }
601
602 #[inline]
603 fn size_hint(&self) -> (usize, Option<usize>) {
604 self.iter.size_hint()
605 }
606}
607
608pub struct IndexConstraint<'a>(&'a ffi::sqlite3_index_constraint);
610
611impl IndexConstraint<'_> {
612 #[inline]
614 #[must_use]
615 pub fn column(&self) -> c_int {
616 self.0.iColumn
617 }
618
619 #[inline]
621 #[must_use]
622 pub fn operator(&self) -> IndexConstraintOp {
623 IndexConstraintOp::from(self.0.op)
624 }
625
626 #[inline]
628 #[must_use]
629 pub fn is_usable(&self) -> bool {
630 self.0.usable != 0
631 }
632}
633
634pub struct IndexConstraintUsage<'a>(&'a mut ffi::sqlite3_index_constraint_usage);
637
638impl IndexConstraintUsage<'_> {
639 #[inline]
642 pub fn set_argv_index(&mut self, argv_index: c_int) {
643 self.0.argvIndex = argv_index;
644 }
645
646 #[inline]
648 pub fn set_omit(&mut self, omit: bool) {
649 self.0.omit = omit as std::os::raw::c_uchar;
650 }
651}
652
653pub struct OrderByIter<'a> {
655 iter: slice::Iter<'a, ffi::sqlite3_index_orderby>,
656}
657
658impl<'a> Iterator for OrderByIter<'a> {
659 type Item = OrderBy<'a>;
660
661 #[inline]
662 fn next(&mut self) -> Option<OrderBy<'a>> {
663 self.iter.next().map(OrderBy)
664 }
665
666 #[inline]
667 fn size_hint(&self) -> (usize, Option<usize>) {
668 self.iter.size_hint()
669 }
670}
671
672pub struct OrderBy<'a>(&'a ffi::sqlite3_index_orderby);
674
675impl OrderBy<'_> {
676 #[inline]
678 #[must_use]
679 pub fn column(&self) -> c_int {
680 self.0.iColumn
681 }
682
683 #[inline]
685 #[must_use]
686 pub fn is_order_by_desc(&self) -> bool {
687 self.0.desc != 0
688 }
689}
690
691pub unsafe trait VTabCursor: Sized {
707 fn filter(&mut self, idx_num: c_int, idx_str: Option<&str>, args: &Values<'_>) -> Result<()>;
710 fn next(&mut self) -> Result<()>;
713 fn eof(&self) -> bool;
717 fn column(&self, ctx: &mut Context, i: c_int) -> Result<()>;
722 fn rowid(&self) -> Result<i64>;
725}
726
727pub struct Context(*mut ffi::sqlite3_context);
730
731impl Context {
732 #[inline]
734 pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
735 let t = value.to_sql()?;
736 unsafe { set_result(self.0, &[], &t) };
737 Ok(())
738 }
739
740 }
742
743pub struct Values<'a> {
746 args: &'a [*mut ffi::sqlite3_value],
747}
748
749impl Values<'_> {
750 #[inline]
752 #[must_use]
753 pub fn len(&self) -> usize {
754 self.args.len()
755 }
756
757 #[inline]
759 #[must_use]
760 pub fn is_empty(&self) -> bool {
761 self.args.is_empty()
762 }
763
764 pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> {
766 let arg = self.args[idx];
767 let value = unsafe { ValueRef::from_value(arg) };
768 FromSql::column_result(value).map_err(|err| match err {
769 FromSqlError::InvalidType => Error::InvalidFilterParameterType(idx, value.data_type()),
770 FromSqlError::Other(err) => {
771 Error::FromSqlConversionFailure(idx, value.data_type(), err)
772 }
773 FromSqlError::InvalidBlobSize { .. } => {
774 Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err))
775 }
776 FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
777 })
778 }
779
780 #[cfg(feature = "array")]
783 #[cfg_attr(docsrs, doc(cfg(feature = "array")))]
784 fn get_array(&self, idx: usize) -> Option<array::Array> {
785 use crate::types::Value;
786 let arg = self.args[idx];
787 let ptr = unsafe { ffi::sqlite3_value_pointer(arg, array::ARRAY_TYPE) };
788 if ptr.is_null() {
789 None
790 } else {
791 Some(unsafe {
792 let ptr = ptr as *const Vec<Value>;
793 array::Array::increment_strong_count(ptr); array::Array::from_raw(ptr)
795 })
796 }
797 }
798
799 #[inline]
801 #[must_use]
802 pub fn iter(&self) -> ValueIter<'_> {
803 ValueIter {
804 iter: self.args.iter(),
805 }
806 }
807 }
809
810impl<'a> IntoIterator for &'a Values<'a> {
811 type IntoIter = ValueIter<'a>;
812 type Item = ValueRef<'a>;
813
814 #[inline]
815 fn into_iter(self) -> ValueIter<'a> {
816 self.iter()
817 }
818}
819
820pub struct ValueIter<'a> {
822 iter: slice::Iter<'a, *mut ffi::sqlite3_value>,
823}
824
825impl<'a> Iterator for ValueIter<'a> {
826 type Item = ValueRef<'a>;
827
828 #[inline]
829 fn next(&mut self) -> Option<ValueRef<'a>> {
830 self.iter
831 .next()
832 .map(|&raw| unsafe { ValueRef::from_value(raw) })
833 }
834
835 #[inline]
836 fn size_hint(&self) -> (usize, Option<usize>) {
837 self.iter.size_hint()
838 }
839}
840
841impl Connection {
842 #[inline]
847 pub fn create_module<'vtab, T: VTab<'vtab>>(
848 &self,
849 module_name: &str,
850 module: &'static Module<'vtab, T>,
851 aux: Option<T::Aux>,
852 ) -> Result<()> {
853 self.db.borrow_mut().create_module(module_name, module, aux)
854 }
855}
856
857impl InnerConnection {
858 fn create_module<'vtab, T: VTab<'vtab>>(
859 &mut self,
860 module_name: &str,
861 module: &'static Module<'vtab, T>,
862 aux: Option<T::Aux>,
863 ) -> Result<()> {
864 use crate::version;
865 if version::version_number() < 3_009_000 && module.base.xCreate.is_none() {
866 return Err(Error::ModuleError(format!(
867 "Eponymous-only virtual table not supported by SQLite version {}",
868 version::version()
869 )));
870 }
871 let c_name = str_to_cstring(module_name)?;
872 let r = match aux {
873 Some(aux) => {
874 let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
875 unsafe {
876 ffi::sqlite3_create_module_v2(
877 self.db(),
878 c_name.as_ptr(),
879 &module.base,
880 boxed_aux.cast::<c_void>(),
881 Some(free_boxed_value::<T::Aux>),
882 )
883 }
884 }
885 None => unsafe {
886 ffi::sqlite3_create_module_v2(
887 self.db(),
888 c_name.as_ptr(),
889 &module.base,
890 ptr::null_mut(),
891 None,
892 )
893 },
894 };
895 self.decode_result(r)
896 }
897}
898
899#[must_use]
902pub fn escape_double_quote(identifier: &str) -> Cow<'_, str> {
903 if identifier.contains('"') {
904 Owned(identifier.replace('"', "\"\""))
906 } else {
907 Borrowed(identifier)
908 }
909}
910#[must_use]
912pub fn dequote(s: &str) -> &str {
913 if s.len() < 2 {
914 return s;
915 }
916 match s.bytes().next() {
917 Some(b) if b == b'"' || b == b'\'' => match s.bytes().next_back() {
918 Some(e) if e == b => &s[1..s.len() - 1], _ => s,
920 },
921 _ => s,
922 }
923}
924#[must_use]
930pub fn parse_boolean(s: &str) -> Option<bool> {
931 if s.eq_ignore_ascii_case("yes")
932 || s.eq_ignore_ascii_case("on")
933 || s.eq_ignore_ascii_case("true")
934 || s.eq("1")
935 {
936 Some(true)
937 } else if s.eq_ignore_ascii_case("no")
938 || s.eq_ignore_ascii_case("off")
939 || s.eq_ignore_ascii_case("false")
940 || s.eq("0")
941 {
942 Some(false)
943 } else {
944 None
945 }
946}
947
948pub fn parameter(c_slice: &[u8]) -> Result<(&str, &str)> {
950 let arg = std::str::from_utf8(c_slice)?.trim();
951 let mut split = arg.split('=');
952 if let Some(key) = split.next() {
953 if let Some(value) = split.next() {
954 let param = key.trim();
955 let value = dequote(value.trim());
956 return Ok((param, value));
957 }
958 }
959 Err(Error::ModuleError(format!("illegal argument: '{arg}'")))
960}
961
962unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
964 drop(Box::from_raw(p.cast::<T>()));
965}
966
967unsafe extern "C" fn rust_create<'vtab, T>(
968 db: *mut ffi::sqlite3,
969 aux: *mut c_void,
970 argc: c_int,
971 argv: *const *const c_char,
972 pp_vtab: *mut *mut sqlite3_vtab,
973 err_msg: *mut *mut c_char,
974) -> c_int
975where
976 T: CreateVTab<'vtab>,
977{
978 use std::ffi::CStr;
979
980 let mut conn = VTabConnection(db);
981 let aux = aux.cast::<T::Aux>();
982 let args = slice::from_raw_parts(argv, argc as usize);
983 let vec = args
984 .iter()
985 .map(|&cs| CStr::from_ptr(cs).to_bytes()) .collect::<Vec<_>>();
987 match T::create(&mut conn, aux.as_ref(), &vec[..]) {
988 Ok((sql, vtab)) => match std::ffi::CString::new(sql) {
989 Ok(c_sql) => {
990 let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
991 if rc == ffi::SQLITE_OK {
992 let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
993 *pp_vtab = boxed_vtab.cast::<sqlite3_vtab>();
994 ffi::SQLITE_OK
995 } else {
996 let err = error_from_sqlite_code(rc, None);
997 to_sqlite_error(&err, err_msg)
998 }
999 }
1000 Err(err) => {
1001 *err_msg = alloc(&err.to_string());
1002 ffi::SQLITE_ERROR
1003 }
1004 },
1005 Err(err) => to_sqlite_error(&err, err_msg),
1006 }
1007}
1008
1009unsafe extern "C" fn rust_connect<'vtab, T>(
1010 db: *mut ffi::sqlite3,
1011 aux: *mut c_void,
1012 argc: c_int,
1013 argv: *const *const c_char,
1014 pp_vtab: *mut *mut sqlite3_vtab,
1015 err_msg: *mut *mut c_char,
1016) -> c_int
1017where
1018 T: VTab<'vtab>,
1019{
1020 use std::ffi::CStr;
1021
1022 let mut conn = VTabConnection(db);
1023 let aux = aux.cast::<T::Aux>();
1024 let args = slice::from_raw_parts(argv, argc as usize);
1025 let vec = args
1026 .iter()
1027 .map(|&cs| CStr::from_ptr(cs).to_bytes()) .collect::<Vec<_>>();
1029 match T::connect(&mut conn, aux.as_ref(), &vec[..]) {
1030 Ok((sql, vtab)) => match std::ffi::CString::new(sql) {
1031 Ok(c_sql) => {
1032 let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
1033 if rc == ffi::SQLITE_OK {
1034 let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
1035 *pp_vtab = boxed_vtab.cast::<sqlite3_vtab>();
1036 ffi::SQLITE_OK
1037 } else {
1038 let err = error_from_sqlite_code(rc, None);
1039 to_sqlite_error(&err, err_msg)
1040 }
1041 }
1042 Err(err) => {
1043 *err_msg = alloc(&err.to_string());
1044 ffi::SQLITE_ERROR
1045 }
1046 },
1047 Err(err) => to_sqlite_error(&err, err_msg),
1048 }
1049}
1050
1051unsafe extern "C" fn rust_best_index<'vtab, T>(
1052 vtab: *mut sqlite3_vtab,
1053 info: *mut ffi::sqlite3_index_info,
1054) -> c_int
1055where
1056 T: VTab<'vtab>,
1057{
1058 let vt = vtab.cast::<T>();
1059 let mut idx_info = IndexInfo(info);
1060 match (*vt).best_index(&mut idx_info) {
1061 Ok(_) => ffi::SQLITE_OK,
1062 Err(Error::SqliteFailure(err, s)) => {
1063 if let Some(err_msg) = s {
1064 set_err_msg(vtab, &err_msg);
1065 }
1066 err.extended_code
1067 }
1068 Err(err) => {
1069 set_err_msg(vtab, &err.to_string());
1070 ffi::SQLITE_ERROR
1071 }
1072 }
1073}
1074
1075unsafe extern "C" fn rust_disconnect<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
1076where
1077 T: VTab<'vtab>,
1078{
1079 if vtab.is_null() {
1080 return ffi::SQLITE_OK;
1081 }
1082 let vtab = vtab.cast::<T>();
1083 drop(Box::from_raw(vtab));
1084 ffi::SQLITE_OK
1085}
1086
1087unsafe extern "C" fn rust_destroy<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
1088where
1089 T: CreateVTab<'vtab>,
1090{
1091 if vtab.is_null() {
1092 return ffi::SQLITE_OK;
1093 }
1094 let vt = vtab.cast::<T>();
1095 match (*vt).destroy() {
1096 Ok(_) => {
1097 drop(Box::from_raw(vt));
1098 ffi::SQLITE_OK
1099 }
1100 Err(Error::SqliteFailure(err, s)) => {
1101 if let Some(err_msg) = s {
1102 set_err_msg(vtab, &err_msg);
1103 }
1104 err.extended_code
1105 }
1106 Err(err) => {
1107 set_err_msg(vtab, &err.to_string());
1108 ffi::SQLITE_ERROR
1109 }
1110 }
1111}
1112
1113unsafe extern "C" fn rust_open<'vtab, T>(
1114 vtab: *mut sqlite3_vtab,
1115 pp_cursor: *mut *mut sqlite3_vtab_cursor,
1116) -> c_int
1117where
1118 T: VTab<'vtab> + 'vtab,
1119{
1120 let vt = vtab.cast::<T>();
1121 match (*vt).open() {
1122 Ok(cursor) => {
1123 let boxed_cursor: *mut T::Cursor = Box::into_raw(Box::new(cursor));
1124 *pp_cursor = boxed_cursor.cast::<sqlite3_vtab_cursor>();
1125 ffi::SQLITE_OK
1126 }
1127 Err(Error::SqliteFailure(err, s)) => {
1128 if let Some(err_msg) = s {
1129 set_err_msg(vtab, &err_msg);
1130 }
1131 err.extended_code
1132 }
1133 Err(err) => {
1134 set_err_msg(vtab, &err.to_string());
1135 ffi::SQLITE_ERROR
1136 }
1137 }
1138}
1139
1140unsafe extern "C" fn rust_close<C>(cursor: *mut sqlite3_vtab_cursor) -> c_int
1141where
1142 C: VTabCursor,
1143{
1144 let cr = cursor.cast::<C>();
1145 drop(Box::from_raw(cr));
1146 ffi::SQLITE_OK
1147}
1148
1149unsafe extern "C" fn rust_filter<C>(
1150 cursor: *mut sqlite3_vtab_cursor,
1151 idx_num: c_int,
1152 idx_str: *const c_char,
1153 argc: c_int,
1154 argv: *mut *mut ffi::sqlite3_value,
1155) -> c_int
1156where
1157 C: VTabCursor,
1158{
1159 use std::ffi::CStr;
1160 use std::str;
1161 let idx_name = if idx_str.is_null() {
1162 None
1163 } else {
1164 let c_slice = CStr::from_ptr(idx_str).to_bytes();
1165 Some(str::from_utf8_unchecked(c_slice))
1166 };
1167 let args = slice::from_raw_parts_mut(argv, argc as usize);
1168 let values = Values { args };
1169 let cr = cursor as *mut C;
1170 cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
1171}
1172
1173unsafe extern "C" fn rust_next<C>(cursor: *mut sqlite3_vtab_cursor) -> c_int
1174where
1175 C: VTabCursor,
1176{
1177 let cr = cursor as *mut C;
1178 cursor_error(cursor, (*cr).next())
1179}
1180
1181unsafe extern "C" fn rust_eof<C>(cursor: *mut sqlite3_vtab_cursor) -> c_int
1182where
1183 C: VTabCursor,
1184{
1185 let cr = cursor.cast::<C>();
1186 (*cr).eof() as c_int
1187}
1188
1189unsafe extern "C" fn rust_column<C>(
1190 cursor: *mut sqlite3_vtab_cursor,
1191 ctx: *mut ffi::sqlite3_context,
1192 i: c_int,
1193) -> c_int
1194where
1195 C: VTabCursor,
1196{
1197 let cr = cursor.cast::<C>();
1198 let mut ctxt = Context(ctx);
1199 result_error(ctx, (*cr).column(&mut ctxt, i))
1200}
1201
1202unsafe extern "C" fn rust_rowid<C>(
1203 cursor: *mut sqlite3_vtab_cursor,
1204 p_rowid: *mut ffi::sqlite3_int64,
1205) -> c_int
1206where
1207 C: VTabCursor,
1208{
1209 let cr = cursor.cast::<C>();
1210 match (*cr).rowid() {
1211 Ok(rowid) => {
1212 *p_rowid = rowid;
1213 ffi::SQLITE_OK
1214 }
1215 err => cursor_error(cursor, err),
1216 }
1217}
1218
1219unsafe extern "C" fn rust_update<'vtab, T>(
1220 vtab: *mut sqlite3_vtab,
1221 argc: c_int,
1222 argv: *mut *mut ffi::sqlite3_value,
1223 p_rowid: *mut ffi::sqlite3_int64,
1224) -> c_int
1225where
1226 T: UpdateVTab<'vtab> + 'vtab,
1227{
1228 assert!(argc >= 1);
1229 let args = slice::from_raw_parts_mut(argv, argc as usize);
1230 let vt = vtab.cast::<T>();
1231 let r = if args.len() == 1 {
1232 (*vt).delete(ValueRef::from_value(args[0]))
1233 } else if ffi::sqlite3_value_type(args[0]) == ffi::SQLITE_NULL {
1234 let values = Values { args };
1236 match (*vt).insert(&values) {
1237 Ok(rowid) => {
1238 *p_rowid = rowid;
1239 Ok(())
1240 }
1241 Err(e) => Err(e),
1242 }
1243 } else {
1244 let values = Values { args };
1245 (*vt).update(&values)
1246 };
1247 match r {
1248 Ok(_) => ffi::SQLITE_OK,
1249 Err(Error::SqliteFailure(err, s)) => {
1250 if let Some(err_msg) = s {
1251 set_err_msg(vtab, &err_msg);
1252 }
1253 err.extended_code
1254 }
1255 Err(err) => {
1256 set_err_msg(vtab, &err.to_string());
1257 ffi::SQLITE_ERROR
1258 }
1259 }
1260}
1261
1262macro_rules! transaction_method {
1263 ($c_name:ident, $method:ident) => {
1264 unsafe extern "C" fn $c_name<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
1265 where
1266 T: TransactionVTab<'vtab>,
1267 {
1268 if vtab.is_null() {
1269 return ffi::SQLITE_OK;
1270 }
1271 let vt = vtab.cast::<T>();
1272 match (*vt).$method() {
1273 Ok(_) => ffi::SQLITE_OK,
1274 Err(Error::SqliteFailure(err, s)) => {
1275 if let Some(err_msg) = s {
1276 set_err_msg(vtab, &err_msg);
1277 }
1278 err.extended_code
1279 }
1280 Err(err) => {
1281 set_err_msg(vtab, &err.to_string());
1282 ffi::SQLITE_ERROR
1283 }
1284 }
1285 }
1286 };
1287}
1288transaction_method!(rust_begin, begin);
1289transaction_method!(rust_sync, sync);
1290transaction_method!(rust_commit, commit);
1291transaction_method!(rust_rollback, rollback);
1292
1293#[cold]
1296unsafe fn cursor_error<T>(cursor: *mut sqlite3_vtab_cursor, result: Result<T>) -> c_int {
1297 match result {
1298 Ok(_) => ffi::SQLITE_OK,
1299 Err(Error::SqliteFailure(err, s)) => {
1300 if let Some(err_msg) = s {
1301 set_err_msg((*cursor).pVtab, &err_msg);
1302 }
1303 err.extended_code
1304 }
1305 Err(err) => {
1306 set_err_msg((*cursor).pVtab, &err.to_string());
1307 ffi::SQLITE_ERROR
1308 }
1309 }
1310}
1311
1312#[cold]
1315unsafe fn set_err_msg(vtab: *mut sqlite3_vtab, err_msg: &str) {
1316 if !(*vtab).zErrMsg.is_null() {
1317 ffi::sqlite3_free((*vtab).zErrMsg.cast::<c_void>());
1318 }
1319 (*vtab).zErrMsg = alloc(err_msg);
1320}
1321
1322#[cold]
1325unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
1326 match result {
1327 Ok(_) => ffi::SQLITE_OK,
1328 Err(Error::SqliteFailure(err, s)) => {
1329 match err.extended_code {
1330 ffi::SQLITE_TOOBIG => {
1331 ffi::sqlite3_result_error_toobig(ctx);
1332 }
1333 ffi::SQLITE_NOMEM => {
1334 ffi::sqlite3_result_error_nomem(ctx);
1335 }
1336 code => {
1337 ffi::sqlite3_result_error_code(ctx, code);
1338 if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
1339 ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
1340 }
1341 }
1342 };
1343 err.extended_code
1344 }
1345 Err(err) => {
1346 ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_ERROR);
1347 if let Ok(cstr) = str_to_cstring(&err.to_string()) {
1348 ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
1349 }
1350 ffi::SQLITE_ERROR
1351 }
1352 }
1353}
1354
1355#[cfg(feature = "array")]
1356#[cfg_attr(docsrs, doc(cfg(feature = "array")))]
1357pub mod array;
1358#[cfg(feature = "csvtab")]
1359#[cfg_attr(docsrs, doc(cfg(feature = "csvtab")))]
1360pub mod csvtab;
1361#[cfg(feature = "series")]
1362#[cfg_attr(docsrs, doc(cfg(feature = "series")))]
1363pub mod series; #[cfg(all(test, feature = "modern_sqlite"))]
1365mod vtablog;
1366
1367#[cfg(test)]
1368mod test {
1369 #[test]
1370 fn test_dequote() {
1371 assert_eq!("", super::dequote(""));
1372 assert_eq!("'", super::dequote("'"));
1373 assert_eq!("\"", super::dequote("\""));
1374 assert_eq!("'\"", super::dequote("'\""));
1375 assert_eq!("", super::dequote("''"));
1376 assert_eq!("", super::dequote("\"\""));
1377 assert_eq!("x", super::dequote("'x'"));
1378 assert_eq!("x", super::dequote("\"x\""));
1379 assert_eq!("x", super::dequote("x"));
1380 }
1381 #[test]
1382 fn test_parse_boolean() {
1383 assert_eq!(None, super::parse_boolean(""));
1384 assert_eq!(Some(true), super::parse_boolean("1"));
1385 assert_eq!(Some(true), super::parse_boolean("yes"));
1386 assert_eq!(Some(true), super::parse_boolean("on"));
1387 assert_eq!(Some(true), super::parse_boolean("true"));
1388 assert_eq!(Some(false), super::parse_boolean("0"));
1389 assert_eq!(Some(false), super::parse_boolean("no"));
1390 assert_eq!(Some(false), super::parse_boolean("off"));
1391 assert_eq!(Some(false), super::parse_boolean("false"));
1392 }
1393}