1use std::borrow::Cow::{self, Borrowed, Owned};
13use std::ffi::{c_char, c_int, c_void, CStr};
14use std::marker::PhantomData;
15use std::ops::Deref;
16use std::ptr;
17use std::slice;
18
19use crate::ffi::sqlite3_free;
20
21use crate::context::set_result;
22use crate::error::{check, error_from_sqlite_code, to_sqlite_error};
23use crate::ffi;
24pub use crate::ffi::{sqlite3_vtab, sqlite3_vtab_cursor};
25use crate::types::{FromSql, FromSqlError, ToSql, ValueRef};
26use crate::util::{alloc, free_boxed_value};
27use crate::{str_to_cstring, Connection, Error, InnerConnection, Name, Result};
28
29pub enum VTabKind {
65 Default,
67 Eponymous,
71 EponymousOnly,
78}
79
80#[repr(transparent)]
84pub struct Module<'vtab, T: VTab<'vtab>> {
85 base: ffi::sqlite3_module,
86 phantom: PhantomData<&'vtab T>,
87}
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
104impl<'vtab, T: VTab<'vtab>> Module<'vtab, T> {
105 #[must_use]
109 pub const fn eponymous_only_module() -> Self {
110 Module {
112 base: ffi::sqlite3_module {
113 iVersion: 1,
115 xCreate: None,
116 xConnect: Some(rust_connect::<T>),
117 xBestIndex: Some(rust_best_index::<T>),
118 xDisconnect: Some(rust_disconnect::<T>),
119 xDestroy: None,
120 xOpen: Some(rust_open::<T>),
121 xClose: Some(rust_close::<T::Cursor>),
122 xFilter: Some(rust_filter::<T::Cursor>),
123 xNext: Some(rust_next::<T::Cursor>),
124 xEof: Some(rust_eof::<T::Cursor>),
125 xColumn: Some(rust_column::<T::Cursor>),
126 xRowid: Some(rust_rowid::<T::Cursor>),
127 xUpdate: None,
128 xBegin: None,
129 xSync: None,
130 xCommit: None,
131 xRollback: None,
132 xFindFunction: None,
133 xRename: None,
134 ..ZERO_MODULE
135 },
136 phantom: PhantomData::<&'vtab T>,
137 }
138 }
139
140 #[must_use]
144 pub const fn read_only_module() -> Self
145 where
146 T: CreateVTab<'vtab>,
147 {
148 let mut module = Self::eponymous_only_module();
149 match T::KIND {
150 VTabKind::EponymousOnly => module,
151 VTabKind::Eponymous => {
152 module.base.xCreate = module.base.xConnect;
155 module.base.xDestroy = module.base.xDisconnect;
156 module
157 }
158 _ => {
159 module.base.xCreate = Some(rust_create::<T>);
162 module.base.xDestroy = Some(rust_destroy::<T>);
163 module
164 }
165 }
166 }
167
168 #[must_use]
172 pub const fn update_module() -> Self
173 where
174 T: UpdateVTab<'vtab>,
175 {
176 let mut module = Self::read_only_module();
177 module.base.xUpdate = Some(rust_update::<T>);
178 module
179 }
180
181 #[must_use]
185 pub const fn update_module_with_tx() -> Self
186 where
187 T: TransactionVTab<'vtab>,
188 {
189 let mut module = Self::update_module();
190 module.base.xBegin = Some(rust_begin::<T>);
191 module.base.xSync = Some(rust_sync::<T>);
192 module.base.xCommit = Some(rust_commit::<T>);
193 module.base.xRollback = Some(rust_rollback::<T>);
194 module
195 }
196
197 pub const fn without_rowid(mut self) -> Self {
199 self.base.xRowid = None;
200 self
201 }
202
203 pub const fn without_sync(mut self) -> Self
205 where
206 T: TransactionVTab<'vtab>,
207 {
208 self.base.xSync = None;
209 self
210 }
211}
212
213#[repr(i32)]
215#[non_exhaustive]
216#[derive(Debug, Clone, Copy, Eq, PartialEq)]
217pub enum VTabConfig {
218 ConstraintSupport = 1,
220 Innocuous = 2,
222 DirectOnly = 3,
224 UsesAllSchemas = 4,
226}
227
228pub struct VTabConnection(*mut ffi::sqlite3);
230
231impl VTabConnection {
232 pub fn config(&mut self, config: VTabConfig) -> Result<()> {
234 check(unsafe { ffi::sqlite3_vtab_config(self.0, config as c_int) })
235 }
236
237 pub unsafe fn handle(&mut self) -> *mut ffi::sqlite3 {
251 self.0
252 }
253}
254
255pub unsafe trait VTab<'vtab>: Sized {
273 type Aux: Send + Sync + 'static;
275 type Cursor: VTabCursor;
277
278 fn connect(
282 db: &mut VTabConnection,
283 aux: Option<&Self::Aux>,
284 module_name: &[u8],
285 database_name: &[u8],
286 table_name: &[u8],
287 args: &[&[u8]],
288 ) -> Result<(Cow<'static, CStr>, Self)>;
289
290 fn best_index(&self, info: &mut IndexInfo) -> Result<bool>;
294
295 fn open(&'vtab mut self) -> Result<Self::Cursor>;
298}
299
300pub trait CreateVTab<'vtab>: VTab<'vtab> {
304 const KIND: VTabKind;
308 fn create(
316 db: &mut VTabConnection,
317 aux: Option<&Self::Aux>,
318 module_name: &[u8],
319 database_name: &[u8],
320 table_name: &[u8],
321 args: &[&[u8]],
322 ) -> Result<(Cow<'static, CStr>, Self)> {
323 Self::connect(db, aux, module_name, database_name, table_name, args)
324 }
325
326 fn destroy(&self) -> Result<()> {
332 Ok(())
333 }
334}
335
336pub trait UpdateVTab<'vtab>: CreateVTab<'vtab> {
340 fn delete(&mut self, arg: ValueRef<'_>) -> Result<()>;
342 fn insert(&mut self, args: &Inserts<'_>) -> Result<i64>;
348 fn update(&mut self, args: &Updates<'_>) -> Result<()>;
351}
352
353pub trait TransactionVTab<'vtab>: UpdateVTab<'vtab> {
357 fn begin(&mut self) -> Result<()> {
359 Ok(())
360 }
361 fn sync(&mut self) -> Result<()> {
363 Ok(())
364 }
365 fn commit(&mut self) -> Result<()> {
367 Ok(())
368 }
369 fn rollback(&mut self) -> Result<()> {
371 Ok(())
372 }
373}
374
375#[derive(Debug, Eq, PartialEq)]
378#[allow(missing_docs)]
379#[expect(non_camel_case_types)]
380pub enum IndexConstraintOp {
381 SQLITE_INDEX_CONSTRAINT_EQ,
382 SQLITE_INDEX_CONSTRAINT_GT,
383 SQLITE_INDEX_CONSTRAINT_LE,
384 SQLITE_INDEX_CONSTRAINT_LT,
385 SQLITE_INDEX_CONSTRAINT_GE,
386 SQLITE_INDEX_CONSTRAINT_MATCH,
387 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), }
399
400impl From<u8> for IndexConstraintOp {
401 fn from(code: u8) -> Self {
402 match code {
403 2 => Self::SQLITE_INDEX_CONSTRAINT_EQ,
404 4 => Self::SQLITE_INDEX_CONSTRAINT_GT,
405 8 => Self::SQLITE_INDEX_CONSTRAINT_LE,
406 16 => Self::SQLITE_INDEX_CONSTRAINT_LT,
407 32 => Self::SQLITE_INDEX_CONSTRAINT_GE,
408 64 => Self::SQLITE_INDEX_CONSTRAINT_MATCH,
409 65 => Self::SQLITE_INDEX_CONSTRAINT_LIKE,
410 66 => Self::SQLITE_INDEX_CONSTRAINT_GLOB,
411 67 => Self::SQLITE_INDEX_CONSTRAINT_REGEXP,
412 68 => Self::SQLITE_INDEX_CONSTRAINT_NE,
413 69 => Self::SQLITE_INDEX_CONSTRAINT_ISNOT,
414 70 => Self::SQLITE_INDEX_CONSTRAINT_ISNOTNULL,
415 71 => Self::SQLITE_INDEX_CONSTRAINT_ISNULL,
416 72 => Self::SQLITE_INDEX_CONSTRAINT_IS,
417 73 => Self::SQLITE_INDEX_CONSTRAINT_LIMIT,
418 74 => Self::SQLITE_INDEX_CONSTRAINT_OFFSET,
419 v => Self::SQLITE_INDEX_CONSTRAINT_FUNCTION(v),
420 }
421 }
422}
423
424bitflags::bitflags! {
425 #[repr(C)]
428 #[derive(Copy, Clone, Debug)]
429 pub struct IndexFlags: c_int {
430 const NONE = 0;
432 const SQLITE_INDEX_SCAN_UNIQUE = ffi::SQLITE_INDEX_SCAN_UNIQUE;
434 const SQLITE_INDEX_SCAN_HEX = 0x0000_0002; }
437}
438
439#[derive(Debug)]
444pub struct IndexInfo(*mut ffi::sqlite3_index_info);
445
446impl IndexInfo {
447 #[inline]
449 pub fn constraints_and_usages(&mut self) -> IndexConstraintAndUsageIter<'_> {
450 let constraints =
451 unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
452 let constraint_usages = unsafe {
453 slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
454 };
455 IndexConstraintAndUsageIter {
456 iter: constraints.iter().zip(constraint_usages.iter_mut()),
457 }
458 }
459
460 #[inline]
462 #[must_use]
463 pub fn constraints(&self) -> IndexConstraintIter<'_> {
464 let constraints =
465 unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
466 IndexConstraintIter {
467 iter: constraints.iter(),
468 }
469 }
470
471 #[inline]
473 #[must_use]
474 pub fn order_bys(&self) -> OrderByIter<'_> {
475 let order_bys =
476 unsafe { slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize) };
477 OrderByIter {
478 iter: order_bys.iter(),
479 }
480 }
481
482 #[inline]
484 #[must_use]
485 pub fn num_of_order_by(&self) -> usize {
486 unsafe { (*self.0).nOrderBy as usize }
487 }
488
489 #[inline]
491 pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage<'_> {
492 let constraint_usages = unsafe {
493 slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
494 };
495 IndexConstraintUsage(&mut constraint_usages[constraint_idx])
496 }
497
498 #[inline]
500 pub fn set_idx_num(&mut self, idx_num: c_int) {
501 unsafe {
502 (*self.0).idxNum = idx_num;
503 }
504 }
505
506 pub fn set_idx_str(&mut self, idx_str: &str) {
508 unsafe {
509 if (*self.0).needToFreeIdxStr == 1 {
510 sqlite3_free((*self.0).idxStr as _);
511 }
512 (*self.0).idxStr = alloc(idx_str);
513 (*self.0).needToFreeIdxStr = 1;
514 }
515 }
516 pub fn set_idx_cstr(&mut self, idx_str: &'static CStr) {
518 unsafe {
519 if (*self.0).needToFreeIdxStr == 1 {
520 sqlite3_free((*self.0).idxStr as _);
521 }
522 (*self.0).idxStr = idx_str.as_ptr() as _;
523 (*self.0).needToFreeIdxStr = 0;
524 }
525 }
526
527 #[inline]
529 pub fn set_order_by_consumed(&mut self, order_by_consumed: bool) {
530 unsafe {
531 (*self.0).orderByConsumed = order_by_consumed as c_int;
532 }
533 }
534
535 #[inline]
537 pub fn set_estimated_cost(&mut self, estimated_ost: f64) {
538 unsafe {
539 (*self.0).estimatedCost = estimated_ost;
540 }
541 }
542
543 #[inline]
545 pub fn set_estimated_rows(&mut self, estimated_rows: i64) {
546 unsafe {
547 (*self.0).estimatedRows = estimated_rows;
548 }
549 }
550
551 #[inline]
553 pub fn set_idx_flags(&mut self, flags: IndexFlags) {
554 unsafe { (*self.0).idxFlags = flags.bits() };
555 }
556
557 #[inline]
559 pub fn col_used(&self) -> u64 {
560 unsafe { (*self.0).colUsed }
561 }
562
563 pub fn collation(&self, constraint_idx: usize) -> Result<&str> {
565 let idx = constraint_idx as c_int;
566 let collation = unsafe { ffi::sqlite3_vtab_collation(self.0, idx) };
567 if collation.is_null() {
568 return Err(err!(ffi::SQLITE_MISUSE, "{constraint_idx} is out of range"));
569 }
570 Ok(unsafe { CStr::from_ptr(collation) }.to_str()?)
571 }
572
573 #[must_use]
575 #[cfg(feature = "modern_sqlite")] pub fn distinct(&self) -> DistinctMode {
577 match unsafe { ffi::sqlite3_vtab_distinct(self.0) } {
578 0 => DistinctMode::Ordered,
579 1 => DistinctMode::Grouped,
580 2 => DistinctMode::Distinct,
581 3 => DistinctMode::DistinctOrdered,
582 _ => DistinctMode::Ordered,
583 }
584 }
585
586 #[cfg(feature = "modern_sqlite")] pub fn rhs_value(&self, constraint_idx: usize) -> Result<Option<ValueRef<'_>>> {
589 let idx = constraint_idx as c_int;
590 let mut p_value: *mut ffi::sqlite3_value = ptr::null_mut();
591 let rc = unsafe { ffi::sqlite3_vtab_rhs_value(self.0, idx, &mut p_value) };
592 if rc == ffi::SQLITE_NOTFOUND {
593 return Ok(None);
594 }
595 check(rc)?;
596 assert!(!p_value.is_null());
597 Ok(Some(unsafe { ValueRef::from_value(p_value) }))
598 }
599
600 #[cfg(feature = "modern_sqlite")] pub fn is_in_constraint(&self, constraint_idx: usize) -> Result<bool> {
603 self.check_constraint_index(constraint_idx)?;
604 let idx = constraint_idx as c_int;
605 Ok(unsafe { ffi::sqlite3_vtab_in(self.0, idx, -1) != 0 })
606 }
607 #[cfg(feature = "modern_sqlite")] pub fn set_in_constraint(&mut self, constraint_idx: usize, filter_all: bool) -> Result<bool> {
610 self.check_constraint_index(constraint_idx)?;
611 let idx = constraint_idx as c_int;
612 Ok(unsafe { ffi::sqlite3_vtab_in(self.0, idx, filter_all as c_int) != 0 })
613 }
614
615 #[cfg(feature = "modern_sqlite")] fn check_constraint_index(&self, idx: usize) -> Result<()> {
617 if idx >= unsafe { (*self.0).nConstraint } as usize {
618 return Err(err!(ffi::SQLITE_MISUSE, "{idx} is out of range"));
619 }
620 Ok(())
621 }
622}
623
624#[non_exhaustive]
626#[derive(Debug, Eq, PartialEq)]
627pub enum DistinctMode {
628 Ordered,
630 Grouped,
632 Distinct,
634 DistinctOrdered,
636}
637
638pub struct IndexConstraintAndUsageIter<'a> {
640 iter: std::iter::Zip<
641 slice::Iter<'a, ffi::sqlite3_index_constraint>,
642 slice::IterMut<'a, ffi::sqlite3_index_constraint_usage>,
643 >,
644}
645
646impl<'a> Iterator for IndexConstraintAndUsageIter<'a> {
647 type Item = (IndexConstraint<'a>, IndexConstraintUsage<'a>);
648
649 #[inline]
650 fn next(&mut self) -> Option<(IndexConstraint<'a>, IndexConstraintUsage<'a>)> {
651 self.iter
652 .next()
653 .map(|raw| (IndexConstraint(raw.0), IndexConstraintUsage(raw.1)))
654 }
655
656 #[inline]
657 fn size_hint(&self) -> (usize, Option<usize>) {
658 self.iter.size_hint()
659 }
660}
661
662pub struct IndexConstraintIter<'a> {
664 iter: slice::Iter<'a, ffi::sqlite3_index_constraint>,
665}
666
667impl<'a> Iterator for IndexConstraintIter<'a> {
668 type Item = IndexConstraint<'a>;
669
670 #[inline]
671 fn next(&mut self) -> Option<IndexConstraint<'a>> {
672 self.iter.next().map(IndexConstraint)
673 }
674
675 #[inline]
676 fn size_hint(&self) -> (usize, Option<usize>) {
677 self.iter.size_hint()
678 }
679}
680
681pub struct IndexConstraint<'a>(&'a ffi::sqlite3_index_constraint);
683
684impl IndexConstraint<'_> {
685 #[inline]
687 #[must_use]
688 pub fn column(&self) -> c_int {
689 self.0.iColumn
690 }
691
692 #[inline]
694 #[must_use]
695 pub fn operator(&self) -> IndexConstraintOp {
696 IndexConstraintOp::from(self.0.op)
697 }
698
699 #[inline]
701 #[must_use]
702 pub fn is_usable(&self) -> bool {
703 self.0.usable != 0
704 }
705}
706
707pub struct IndexConstraintUsage<'a>(&'a mut ffi::sqlite3_index_constraint_usage);
710
711impl IndexConstraintUsage<'_> {
712 #[inline]
715 pub fn set_argv_index(&mut self, argv_index: c_int) {
716 self.0.argvIndex = argv_index;
717 }
718
719 #[inline]
721 pub fn set_omit(&mut self, omit: bool) {
722 self.0.omit = omit as std::ffi::c_uchar;
723 }
724}
725
726pub struct OrderByIter<'a> {
728 iter: slice::Iter<'a, ffi::sqlite3_index_orderby>,
729}
730
731impl<'a> Iterator for OrderByIter<'a> {
732 type Item = OrderBy<'a>;
733
734 #[inline]
735 fn next(&mut self) -> Option<OrderBy<'a>> {
736 self.iter.next().map(OrderBy)
737 }
738
739 #[inline]
740 fn size_hint(&self) -> (usize, Option<usize>) {
741 self.iter.size_hint()
742 }
743}
744
745pub struct OrderBy<'a>(&'a ffi::sqlite3_index_orderby);
747
748impl OrderBy<'_> {
749 #[inline]
751 #[must_use]
752 pub fn column(&self) -> c_int {
753 self.0.iColumn
754 }
755
756 #[inline]
758 #[must_use]
759 pub fn is_order_by_desc(&self) -> bool {
760 self.0.desc != 0
761 }
762}
763
764pub unsafe trait VTabCursor: Sized {
780 fn filter(&mut self, idx_num: c_int, idx_str: Option<&str>, args: &Filters<'_>) -> Result<()>;
783 fn next(&mut self) -> Result<()>;
786 fn eof(&self) -> bool;
790 fn column(&self, ctx: &mut Context, i: c_int) -> Result<()>;
795 fn rowid(&self) -> Result<i64>;
798}
799
800pub struct Context(*mut ffi::sqlite3_context);
803
804impl Context {
805 #[inline]
807 pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
808 let t = value.to_sql()?;
809 unsafe { set_result(self.0, &[], &t) };
810 Ok(())
811 }
812
813 #[inline]
815 #[must_use]
816 pub fn no_change(&self) -> bool {
817 unsafe { ffi::sqlite3_vtab_nochange(self.0) != 0 }
818 }
819
820 pub unsafe fn get_connection(&self) -> Result<ConnectionRef<'_>> {
826 let handle = ffi::sqlite3_context_db_handle(self.0);
827 Ok(ConnectionRef {
828 conn: Connection::from_handle(handle)?,
829 phantom: PhantomData,
830 })
831 }
832}
833
834pub struct ConnectionRef<'ctx> {
836 conn: Connection,
839 phantom: PhantomData<&'ctx Context>,
840}
841
842impl Deref for ConnectionRef<'_> {
843 type Target = Connection;
844
845 #[inline]
846 fn deref(&self) -> &Connection {
847 &self.conn
848 }
849}
850
851pub struct Filters<'a> {
854 values: Values<'a>,
855}
856impl<'a> Deref for Filters<'a> {
857 type Target = Values<'a>;
858
859 fn deref(&self) -> &Self::Target {
860 &self.values
861 }
862}
863#[cfg(feature = "modern_sqlite")] impl<'a> Filters<'a> {
865 pub fn in_values(&self, idx: usize) -> Result<InValues<'_>> {
867 let list = self.args[idx];
868 Ok(InValues {
869 list,
870 phantom: PhantomData,
871 first: true,
872 })
873 }
874}
875
876#[cfg(feature = "modern_sqlite")] pub struct InValues<'a> {
879 list: *mut ffi::sqlite3_value,
880 phantom: PhantomData<Filters<'a>>,
881 first: bool,
882}
883#[cfg(feature = "modern_sqlite")] impl<'a> fallible_iterator::FallibleIterator for InValues<'a> {
885 type Error = Error;
886 type Item = ValueRef<'a>;
887
888 fn next(&mut self) -> Result<Option<Self::Item>> {
889 let mut val: *mut ffi::sqlite3_value = ptr::null_mut();
890 let rc = unsafe {
891 if self.first {
892 self.first = false;
893 ffi::sqlite3_vtab_in_first(self.list, &mut val)
894 } else {
895 ffi::sqlite3_vtab_in_next(self.list, &mut val)
896 }
897 };
898 match rc {
899 ffi::SQLITE_OK => Ok(Some(unsafe { ValueRef::from_value(val) })),
900 ffi::SQLITE_DONE => Ok(None),
901 _ => Err(error_from_sqlite_code(rc, None)),
902 }
903 }
904}
905
906pub struct Values<'a> {
908 args: &'a [*mut ffi::sqlite3_value],
909}
910
911impl Values<'_> {
912 #[inline]
914 #[must_use]
915 pub fn len(&self) -> usize {
916 self.args.len()
917 }
918
919 #[inline]
921 #[must_use]
922 pub fn is_empty(&self) -> bool {
923 self.args.is_empty()
924 }
925
926 pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> {
928 let arg = self.args[idx];
929 let value = unsafe { ValueRef::from_value(arg) };
930 FromSql::column_result(value).map_err(|err| match err {
931 FromSqlError::InvalidType => Error::InvalidFilterParameterType(idx, value.data_type()),
932 FromSqlError::Other(err) => {
933 Error::FromSqlConversionFailure(idx, value.data_type(), err)
934 }
935 FromSqlError::InvalidBlobSize { .. } => {
936 Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err))
937 }
938 FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
939 FromSqlError::Utf8Error(err) => Error::Utf8Error(idx, err),
940 })
941 }
942
943 #[cfg(feature = "pointer")]
949 pub unsafe fn get_pointer<'a, T: 'static>(
950 &self,
951 idx: usize,
952 ptr_type: &'static CStr,
953 ) -> Option<&'a T> {
954 let arg = self.args[idx];
955 debug_assert_eq!(unsafe { ffi::sqlite3_value_type(arg) }, ffi::SQLITE_NULL);
956 unsafe {
957 ffi::sqlite3_value_pointer(arg, ptr_type.as_ptr())
958 .cast::<T>()
959 .as_ref()
960 }
961 }
962
963 #[inline]
965 #[must_use]
966 pub fn iter(&self) -> ValueIter<'_> {
967 ValueIter {
968 iter: self.args.iter(),
969 }
970 }
971}
972
973impl<'a> IntoIterator for &'a Values<'a> {
974 type IntoIter = ValueIter<'a>;
975 type Item = ValueRef<'a>;
976
977 #[inline]
978 fn into_iter(self) -> ValueIter<'a> {
979 self.iter()
980 }
981}
982
983pub struct ValueIter<'a> {
985 iter: slice::Iter<'a, *mut ffi::sqlite3_value>,
986}
987
988impl<'a> Iterator for ValueIter<'a> {
989 type Item = ValueRef<'a>;
990
991 #[inline]
992 fn next(&mut self) -> Option<ValueRef<'a>> {
993 self.iter
994 .next()
995 .map(|&raw| unsafe { ValueRef::from_value(raw) })
996 }
997
998 #[inline]
999 fn size_hint(&self) -> (usize, Option<usize>) {
1000 self.iter.size_hint()
1001 }
1002}
1003
1004pub struct Inserts<'a> {
1006 values: Values<'a>,
1007}
1008impl<'a> Deref for Inserts<'a> {
1009 type Target = Values<'a>;
1010
1011 fn deref(&self) -> &Self::Target {
1012 &self.values
1013 }
1014}
1015impl Inserts<'_> {
1016 #[must_use]
1021 pub unsafe fn on_conflict(&self, db: *mut ffi::sqlite3) -> ConflictMode {
1022 ConflictMode::from(unsafe { ffi::sqlite3_vtab_on_conflict(db) })
1023 }
1024}
1025
1026pub struct Updates<'a> {
1028 values: Values<'a>,
1029}
1030impl<'a> Deref for Updates<'a> {
1031 type Target = Values<'a>;
1032
1033 fn deref(&self) -> &Self::Target {
1034 &self.values
1035 }
1036}
1037impl Updates<'_> {
1038 #[inline]
1042 #[must_use]
1043 pub fn no_change(&self, idx: usize) -> bool {
1044 unsafe { ffi::sqlite3_value_nochange(self.values.args[idx]) != 0 }
1045 }
1046
1047 #[must_use]
1052 pub unsafe fn on_conflict(&self, db: *mut ffi::sqlite3) -> ConflictMode {
1053 ConflictMode::from(unsafe { ffi::sqlite3_vtab_on_conflict(db) })
1054 }
1055}
1056
1057#[non_exhaustive]
1059#[derive(Debug, Eq, PartialEq)]
1060pub enum ConflictMode {
1061 Rollback,
1063 Ignore,
1065 Fail,
1067 Abort,
1069 Replace,
1071}
1072impl From<c_int> for ConflictMode {
1073 fn from(value: c_int) -> Self {
1074 match value {
1075 ffi::SQLITE_ROLLBACK => ConflictMode::Rollback,
1076 ffi::SQLITE_IGNORE => ConflictMode::Ignore,
1077 ffi::SQLITE_FAIL => ConflictMode::Fail,
1078 ffi::SQLITE_ABORT => ConflictMode::Abort,
1079 ffi::SQLITE_REPLACE => ConflictMode::Replace,
1080 _ => unreachable!("sqlite3_vtab_on_conflict returned invalid value"),
1081 }
1082 }
1083}
1084
1085impl Connection {
1086 #[inline]
1091 pub fn create_module<'vtab, T: VTab<'vtab>, M: Name>(
1092 &self,
1093 module_name: M,
1094 module: &'static Module<'vtab, T>,
1095 aux: Option<T::Aux>,
1096 ) -> Result<()> {
1097 self.db.borrow_mut().create_module(module_name, module, aux)
1098 }
1099}
1100
1101impl InnerConnection {
1102 fn create_module<'vtab, T: VTab<'vtab>, M: Name>(
1103 &mut self,
1104 module_name: M,
1105 module: &'static Module<'vtab, T>,
1106 aux: Option<T::Aux>,
1107 ) -> Result<()> {
1108 use crate::version;
1109 if version::version_number() < 3_009_000 && module.base.xCreate.is_none() {
1110 return Err(Error::ModuleError(format!(
1111 "Eponymous-only virtual table not supported by SQLite version {}",
1112 version::version()
1113 )));
1114 }
1115 let c_name = module_name.as_cstr()?;
1116 let r = match aux {
1117 Some(aux) => {
1118 let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
1119 unsafe {
1120 ffi::sqlite3_create_module_v2(
1121 self.db(),
1122 c_name.as_ptr(),
1123 &module.base,
1124 boxed_aux.cast::<c_void>(),
1125 Some(free_boxed_value::<T::Aux>),
1126 )
1127 }
1128 }
1129 None => unsafe {
1130 ffi::sqlite3_create_module_v2(
1131 self.db(),
1132 c_name.as_ptr(),
1133 &module.base,
1134 ptr::null_mut(),
1135 None,
1136 )
1137 },
1138 };
1139 self.decode_result(r)
1140 }
1141}
1142
1143#[must_use]
1146pub fn escape_double_quote(identifier: &str) -> Cow<'_, str> {
1147 if identifier.contains('"') {
1148 Owned(identifier.replace('"', "\"\""))
1150 } else {
1151 Borrowed(identifier)
1152 }
1153}
1154#[must_use]
1156pub fn dequote(mut s: &str) -> Cow<'_, str> {
1157 let mut chars = s.chars();
1158 let (Some(first), Some(last)) = (chars.next(), chars.next_back()) else {
1159 return Cow::Borrowed(s);
1160 };
1161 if (first == '"' || first == '\'' || first == '`' || first == '[')
1162 && (last == first || first == '[' && last == ']')
1163 {
1164 s = chars.as_str();
1165 if first != '[' && s.contains(first) {
1166 let mut owned = String::with_capacity(s.len());
1168 let mut escaped = false;
1169 for c in s.chars() {
1170 if c == first {
1171 if !escaped {
1172 escaped = true;
1173 continue;
1174 } else {
1175 escaped = false;
1176 }
1177 } else if escaped {
1178 return Cow::Borrowed(s);
1180 }
1181 owned.push(c);
1182 }
1183 if !escaped {
1184 return Cow::Owned(owned);
1185 }
1186 }
1187 }
1188 Cow::Borrowed(s)
1189}
1190#[must_use]
1196pub fn parse_boolean(s: &str) -> Option<bool> {
1197 if s.eq_ignore_ascii_case("yes")
1198 || s.eq_ignore_ascii_case("on")
1199 || s.eq_ignore_ascii_case("true")
1200 || s.eq("1")
1201 {
1202 Some(true)
1203 } else if s.eq_ignore_ascii_case("no")
1204 || s.eq_ignore_ascii_case("off")
1205 || s.eq_ignore_ascii_case("false")
1206 || s.eq("0")
1207 {
1208 Some(false)
1209 } else {
1210 None
1211 }
1212}
1213
1214pub fn parameter(c_slice: &[u8]) -> Result<(&str, Cow<'_, str>)> {
1216 let arg = std::str::from_utf8(c_slice)?.trim();
1217 match arg.split_once('=') {
1218 Some((key, value)) => {
1219 let param = key.trim();
1220 let value = dequote(value.trim());
1221 Ok((param, value))
1222 }
1223 _ => Err(Error::ModuleError(format!("illegal argument: '{arg}'"))),
1224 }
1225}
1226
1227unsafe extern "C" fn rust_create<'vtab, T>(
1228 db: *mut ffi::sqlite3,
1229 aux: *mut c_void,
1230 argc: c_int,
1231 argv: *const *const c_char,
1232 pp_vtab: *mut *mut sqlite3_vtab,
1233 err_msg: *mut *mut c_char,
1234) -> c_int
1235where
1236 T: CreateVTab<'vtab>,
1237{
1238 let mut conn = VTabConnection(db);
1239 let aux = aux.cast::<T::Aux>();
1240 let args = slice::from_raw_parts(argv, argc as usize);
1241 let vec = args
1242 .iter()
1243 .map(|&cs| CStr::from_ptr(cs).to_bytes()) .collect::<Vec<_>>();
1245 match T::create(&mut conn, aux.as_ref(), vec[0], vec[1], vec[2], &vec[3..]) {
1246 Ok((sql, vtab)) => {
1247 let rc = ffi::sqlite3_declare_vtab(db, sql.as_ptr());
1248 if rc == ffi::SQLITE_OK {
1249 let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
1250 *pp_vtab = boxed_vtab.cast::<sqlite3_vtab>();
1251 ffi::SQLITE_OK
1252 } else {
1253 let err = error_from_sqlite_code(rc, None);
1254 to_sqlite_error(&err, err_msg)
1255 }
1256 }
1257 Err(err) => to_sqlite_error(&err, err_msg),
1258 }
1259}
1260
1261unsafe extern "C" fn rust_connect<'vtab, T>(
1262 db: *mut ffi::sqlite3,
1263 aux: *mut c_void,
1264 argc: c_int,
1265 argv: *const *const c_char,
1266 pp_vtab: *mut *mut sqlite3_vtab,
1267 err_msg: *mut *mut c_char,
1268) -> c_int
1269where
1270 T: VTab<'vtab>,
1271{
1272 let mut conn = VTabConnection(db);
1273 let aux = aux.cast::<T::Aux>();
1274 let args = slice::from_raw_parts(argv, argc as usize);
1275 let vec = args
1276 .iter()
1277 .map(|&cs| CStr::from_ptr(cs).to_bytes()) .collect::<Vec<_>>();
1279 match T::connect(&mut conn, aux.as_ref(), vec[0], vec[1], vec[2], &vec[3..]) {
1280 Ok((sql, vtab)) => {
1281 let rc = ffi::sqlite3_declare_vtab(db, sql.as_ptr());
1282 if rc == ffi::SQLITE_OK {
1283 let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
1284 *pp_vtab = boxed_vtab.cast::<sqlite3_vtab>();
1285 ffi::SQLITE_OK
1286 } else {
1287 let err = error_from_sqlite_code(rc, None);
1288 to_sqlite_error(&err, err_msg)
1289 }
1290 }
1291 Err(err) => to_sqlite_error(&err, err_msg),
1292 }
1293}
1294
1295unsafe extern "C" fn rust_best_index<'vtab, T>(
1296 vtab: *mut sqlite3_vtab,
1297 info: *mut ffi::sqlite3_index_info,
1298) -> c_int
1299where
1300 T: VTab<'vtab>,
1301{
1302 let vt = vtab.cast::<T>();
1303 let mut idx_info = IndexInfo(info);
1304 match (*vt).best_index(&mut idx_info) {
1305 Ok(true) => ffi::SQLITE_OK,
1306 Ok(false) => ffi::SQLITE_CONSTRAINT,
1307 err => vtab_error(vtab, err),
1308 }
1309}
1310
1311unsafe extern "C" fn rust_disconnect<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
1312where
1313 T: VTab<'vtab>,
1314{
1315 if vtab.is_null() {
1316 return ffi::SQLITE_OK;
1317 }
1318 let vtab = vtab.cast::<T>();
1319 drop(Box::from_raw(vtab));
1320 ffi::SQLITE_OK
1321}
1322
1323unsafe extern "C" fn rust_destroy<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
1324where
1325 T: CreateVTab<'vtab>,
1326{
1327 if vtab.is_null() {
1328 return ffi::SQLITE_OK;
1329 }
1330 let vt = vtab.cast::<T>();
1331 match (*vt).destroy() {
1332 Ok(_) => {
1333 drop(Box::from_raw(vt));
1334 ffi::SQLITE_OK
1335 }
1336 err => vtab_error(vtab, err),
1337 }
1338}
1339
1340unsafe extern "C" fn rust_open<'vtab, T>(
1341 vtab: *mut sqlite3_vtab,
1342 pp_cursor: *mut *mut sqlite3_vtab_cursor,
1343) -> c_int
1344where
1345 T: VTab<'vtab> + 'vtab,
1346{
1347 let vt = vtab.cast::<T>();
1348 match (*vt).open() {
1349 Ok(cursor) => {
1350 let boxed_cursor: *mut T::Cursor = Box::into_raw(Box::new(cursor));
1351 *pp_cursor = boxed_cursor.cast::<sqlite3_vtab_cursor>();
1352 ffi::SQLITE_OK
1353 }
1354 err => vtab_error(vtab, err),
1355 }
1356}
1357
1358unsafe extern "C" fn rust_close<C>(cursor: *mut sqlite3_vtab_cursor) -> c_int
1359where
1360 C: VTabCursor,
1361{
1362 let cr = cursor.cast::<C>();
1363 drop(Box::from_raw(cr));
1364 ffi::SQLITE_OK
1365}
1366
1367unsafe extern "C" fn rust_filter<C>(
1368 cursor: *mut sqlite3_vtab_cursor,
1369 idx_num: c_int,
1370 idx_str: *const c_char,
1371 argc: c_int,
1372 argv: *mut *mut ffi::sqlite3_value,
1373) -> c_int
1374where
1375 C: VTabCursor,
1376{
1377 use std::str;
1378 let idx_name = if idx_str.is_null() {
1379 None
1380 } else {
1381 let c_slice = CStr::from_ptr(idx_str).to_bytes();
1382 Some(str::from_utf8_unchecked(c_slice))
1383 };
1384 let args = slice::from_raw_parts_mut(argv, argc as usize);
1385 let values = Values { args };
1386 let cr = cursor as *mut C;
1387 cursor_error(cursor, (*cr).filter(idx_num, idx_name, &Filters { values }))
1388}
1389
1390unsafe extern "C" fn rust_next<C>(cursor: *mut sqlite3_vtab_cursor) -> c_int
1391where
1392 C: VTabCursor,
1393{
1394 let cr = cursor as *mut C;
1395 cursor_error(cursor, (*cr).next())
1396}
1397
1398unsafe extern "C" fn rust_eof<C>(cursor: *mut sqlite3_vtab_cursor) -> c_int
1399where
1400 C: VTabCursor,
1401{
1402 let cr = cursor.cast::<C>();
1403 (*cr).eof() as c_int
1404}
1405
1406unsafe extern "C" fn rust_column<C>(
1407 cursor: *mut sqlite3_vtab_cursor,
1408 ctx: *mut ffi::sqlite3_context,
1409 i: c_int,
1410) -> c_int
1411where
1412 C: VTabCursor,
1413{
1414 let cr = cursor.cast::<C>();
1415 let mut ctxt = Context(ctx);
1416 result_error(ctx, (*cr).column(&mut ctxt, i))
1417}
1418
1419unsafe extern "C" fn rust_rowid<C>(
1420 cursor: *mut sqlite3_vtab_cursor,
1421 p_rowid: *mut ffi::sqlite3_int64,
1422) -> c_int
1423where
1424 C: VTabCursor,
1425{
1426 let cr = cursor.cast::<C>();
1427 match (*cr).rowid() {
1428 Ok(rowid) => {
1429 *p_rowid = rowid;
1430 ffi::SQLITE_OK
1431 }
1432 err => cursor_error(cursor, err),
1433 }
1434}
1435
1436unsafe extern "C" fn rust_update<'vtab, T>(
1437 vtab: *mut sqlite3_vtab,
1438 argc: c_int,
1439 argv: *mut *mut ffi::sqlite3_value,
1440 p_rowid: *mut ffi::sqlite3_int64,
1441) -> c_int
1442where
1443 T: UpdateVTab<'vtab> + 'vtab,
1444{
1445 assert!(argc >= 1);
1446 let args = slice::from_raw_parts_mut(argv, argc as usize);
1447 let vt = vtab.cast::<T>();
1448 let r = if args.len() == 1 {
1449 (*vt).delete(ValueRef::from_value(args[0]))
1450 } else if ffi::sqlite3_value_type(args[0]) == ffi::SQLITE_NULL {
1451 let values = Values { args };
1453 match (*vt).insert(&Inserts { values }) {
1454 Ok(rowid) => {
1455 *p_rowid = rowid;
1456 Ok(())
1457 }
1458 Err(e) => Err(e),
1459 }
1460 } else {
1461 let values = Values { args };
1462 (*vt).update(&Updates { values })
1463 };
1464 vtab_error(vtab, r)
1465}
1466
1467unsafe extern "C" fn rust_begin<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
1468where
1469 T: TransactionVTab<'vtab>,
1470{
1471 let vt = vtab.cast::<T>();
1472 vtab_error(vtab, (*vt).begin())
1473}
1474unsafe extern "C" fn rust_sync<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
1475where
1476 T: TransactionVTab<'vtab>,
1477{
1478 let vt = vtab.cast::<T>();
1479 vtab_error(vtab, (*vt).sync())
1480}
1481unsafe extern "C" fn rust_commit<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
1482where
1483 T: TransactionVTab<'vtab>,
1484{
1485 let vt = vtab.cast::<T>();
1486 vtab_error(vtab, (*vt).commit())
1487}
1488unsafe extern "C" fn rust_rollback<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
1489where
1490 T: TransactionVTab<'vtab>,
1491{
1492 let vt = vtab.cast::<T>();
1493 vtab_error(vtab, (*vt).rollback())
1494}
1495
1496unsafe fn cursor_error<T>(cursor: *mut sqlite3_vtab_cursor, result: Result<T>) -> c_int {
1499 vtab_error((*cursor).pVtab, result)
1500}
1501
1502unsafe fn vtab_error<T>(vtab: *mut sqlite3_vtab, result: Result<T>) -> c_int {
1505 match result {
1506 Ok(_) => ffi::SQLITE_OK,
1507 Err(Error::SqliteFailure(err, s)) => {
1508 if let Some(err_msg) = s {
1509 set_err_msg(vtab, &err_msg);
1510 }
1511 err.extended_code
1512 }
1513 Err(err) => {
1514 set_err_msg(vtab, &err.to_string());
1515 ffi::SQLITE_ERROR
1516 }
1517 }
1518}
1519
1520#[cold]
1523unsafe fn set_err_msg(vtab: *mut sqlite3_vtab, err_msg: &str) {
1524 if !(*vtab).zErrMsg.is_null() {
1525 ffi::sqlite3_free((*vtab).zErrMsg.cast::<c_void>());
1526 }
1527 (*vtab).zErrMsg = alloc(err_msg);
1528}
1529
1530#[cold]
1533unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
1534 match result {
1535 Ok(_) => ffi::SQLITE_OK,
1536 Err(Error::SqliteFailure(err, s)) => {
1537 match err.extended_code {
1538 ffi::SQLITE_TOOBIG => {
1539 ffi::sqlite3_result_error_toobig(ctx);
1540 }
1541 ffi::SQLITE_NOMEM => {
1542 ffi::sqlite3_result_error_nomem(ctx);
1543 }
1544 code => {
1545 ffi::sqlite3_result_error_code(ctx, code);
1546 if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
1547 ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
1548 }
1549 }
1550 }
1551 err.extended_code
1552 }
1553 Err(err) => {
1554 ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_ERROR);
1555 if let Ok(cstr) = str_to_cstring(&err.to_string()) {
1556 ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
1557 }
1558 ffi::SQLITE_ERROR
1559 }
1560 }
1561}
1562
1563#[cfg(feature = "array")]
1564pub mod array;
1565#[cfg(feature = "csvtab")]
1566pub mod csvtab;
1567#[cfg(feature = "series")]
1568pub mod series; #[cfg(all(test, feature = "modern_sqlite", not(miri)))]
1570mod vtablog;
1571
1572#[cfg(test)]
1573mod test {
1574 use std::borrow::Cow;
1575
1576 #[cfg(all(target_family = "wasm", target_os = "unknown"))]
1577 use wasm_bindgen_test::wasm_bindgen_test as test;
1578
1579 #[test]
1580 fn test_dequote() {
1581 assert_eq!("", super::dequote(""));
1582 assert_eq!("'", super::dequote("'"));
1583 assert_eq!("\"", super::dequote("\""));
1584
1585 assert_eq!("'\"", super::dequote("'\""));
1586
1587 assert_eq!("", super::dequote("''"));
1588 assert_eq!("", super::dequote("\"\""));
1589 assert_eq!("", super::dequote("``"));
1590 assert_eq!("", super::dequote("[]"));
1591
1592 assert_eq!("x", super::dequote("'x'"));
1593 assert_eq!("x", super::dequote("\"x\""));
1594 assert_eq!("x", super::dequote("x"));
1595 assert_eq!("x", super::dequote("`x`"));
1596 assert_eq!("x", super::dequote("[x]"));
1597
1598 assert_eq!("x'", super::dequote("'x'''"));
1599 assert_eq!("x`", super::dequote("`x```"));
1600
1601 assert_eq!("x'", super::dequote("'x''"));
1602 assert_eq!("x`", super::dequote("`x``"));
1603 }
1604 #[test]
1605 fn test_parse_boolean() {
1606 assert_eq!(None, super::parse_boolean(""));
1607 assert_eq!(Some(true), super::parse_boolean("1"));
1608 assert_eq!(Some(true), super::parse_boolean("yes"));
1609 assert_eq!(Some(true), super::parse_boolean("on"));
1610 assert_eq!(Some(true), super::parse_boolean("true"));
1611 assert_eq!(Some(false), super::parse_boolean("0"));
1612 assert_eq!(Some(false), super::parse_boolean("no"));
1613 assert_eq!(Some(false), super::parse_boolean("off"));
1614 assert_eq!(Some(false), super::parse_boolean("false"));
1615 }
1616 #[test]
1617 fn test_parse_parameters() {
1618 assert_eq!(
1619 Ok(("key", Cow::Borrowed("value"))),
1620 super::parameter(b"key='value'")
1621 );
1622 assert_eq!(
1623 Ok(("key", Cow::Borrowed("foo=bar"))),
1624 super::parameter(b"key='foo=bar'")
1625 );
1626 }
1627}