• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Create virtual tables.
2 //!
3 //! Follow these steps to create your own virtual table:
4 //! 1. Write implementation of [`VTab`] and [`VTabCursor`] traits.
5 //! 2. Create an instance of the [`Module`] structure specialized for [`VTab`]
6 //! impl. from step 1.
7 //! 3. Register your [`Module`] structure using [`Connection::create_module`].
8 //! 4. Run a `CREATE VIRTUAL TABLE` command that specifies the new module in the
9 //! `USING` clause.
10 //!
11 //! (See [SQLite doc](http://sqlite.org/vtab.html))
12 use std::borrow::Cow::{self, Borrowed, Owned};
13 use std::marker::PhantomData;
14 use std::marker::Sync;
15 use std::os::raw::{c_char, c_int, c_void};
16 use std::ptr;
17 use std::slice;
18 
19 use crate::context::set_result;
20 use crate::error::error_from_sqlite_code;
21 use crate::ffi;
22 pub use crate::ffi::{sqlite3_vtab, sqlite3_vtab_cursor};
23 use crate::types::{FromSql, FromSqlError, ToSql, ValueRef};
24 use crate::{str_to_cstring, Connection, Error, InnerConnection, Result};
25 
26 // let conn: Connection = ...;
27 // let mod: Module = ...; // VTab builder
28 // conn.create_module("module", mod);
29 //
30 // conn.execute("CREATE VIRTUAL TABLE foo USING module(...)");
31 // \-> Module::xcreate
32 //  |-> let vtab: VTab = ...; // on the heap
33 //  \-> conn.declare_vtab("CREATE TABLE foo (...)");
34 // conn = Connection::open(...);
35 // \-> Module::xconnect
36 //  |-> let vtab: VTab = ...; // on the heap
37 //  \-> conn.declare_vtab("CREATE TABLE foo (...)");
38 //
39 // conn.close();
40 // \-> vtab.xdisconnect
41 // conn.execute("DROP TABLE foo");
42 // \-> vtab.xDestroy
43 //
44 // let stmt = conn.prepare("SELECT ... FROM foo WHERE ...");
45 // \-> vtab.xbestindex
46 // stmt.query().next();
47 // \-> vtab.xopen
48 //  |-> let cursor: VTabCursor = ...; // on the heap
49 //  |-> cursor.xfilter or xnext
50 //  |-> cursor.xeof
51 //  \-> if not eof { cursor.column or xrowid } else { cursor.xclose }
52 //
53 
54 // db: *mut ffi::sqlite3 => VTabConnection
55 // module: *const ffi::sqlite3_module => Module
56 // aux: *mut c_void => Module::Aux
57 // ffi::sqlite3_vtab => VTab
58 // ffi::sqlite3_vtab_cursor => VTabCursor
59 
60 /// Virtual table module
61 ///
62 /// (See [SQLite doc](https://sqlite.org/c3ref/module.html))
63 #[repr(transparent)]
64 pub struct Module<'vtab, T: VTab<'vtab>> {
65     base: ffi::sqlite3_module,
66     phantom: PhantomData<&'vtab T>,
67 }
68 
69 unsafe impl<'vtab, T: VTab<'vtab>> Send for Module<'vtab, T> {}
70 unsafe impl<'vtab, T: VTab<'vtab>> Sync for Module<'vtab, T> {}
71 
72 union ModuleZeroHack {
73     bytes: [u8; std::mem::size_of::<ffi::sqlite3_module>()],
74     module: ffi::sqlite3_module,
75 }
76 
77 // Used as a trailing initializer for sqlite3_module -- this way we avoid having
78 // the build fail if buildtime_bindgen is on. This is safe, as bindgen-generated
79 // structs are allowed to be zeroed.
80 const ZERO_MODULE: ffi::sqlite3_module = unsafe {
81     ModuleZeroHack {
82         bytes: [0_u8; std::mem::size_of::<ffi::sqlite3_module>()],
83     }
84     .module
85 };
86 
87 /// Create a read-only virtual table implementation.
88 ///
89 /// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
90 #[must_use]
read_only_module<'vtab, T: CreateVTab<'vtab>>() -> &'static Module<'vtab, T>91 pub fn read_only_module<'vtab, T: CreateVTab<'vtab>>() -> &'static Module<'vtab, T> {
92     // The xConnect and xCreate methods do the same thing, but they must be
93     // different so that the virtual table is not an eponymous virtual table.
94     #[allow(clippy::needless_update)]
95     &Module {
96         base: ffi::sqlite3_module {
97             // We don't use V3
98             iVersion: 2, // We don't use V2 or V3 features in read_only_module types
99             xCreate: Some(rust_create::<T>),
100             xConnect: Some(rust_connect::<T>),
101             xBestIndex: Some(rust_best_index::<T>),
102             xDisconnect: Some(rust_disconnect::<T>),
103             xDestroy: Some(rust_destroy::<T>),
104             xOpen: Some(rust_open::<T>),
105             xClose: Some(rust_close::<T::Cursor>),
106             xFilter: Some(rust_filter::<T::Cursor>),
107             xNext: Some(rust_next::<T::Cursor>),
108             xEof: Some(rust_eof::<T::Cursor>),
109             xColumn: Some(rust_column::<T::Cursor>),
110             xRowid: Some(rust_rowid::<T::Cursor>),
111             xUpdate: None,
112             xBegin: None,
113             xSync: None,
114             xCommit: None,
115             xRollback: None,
116             xFindFunction: None,
117             xRename: None,
118             xSavepoint: None,
119             xRelease: None,
120             xRollbackTo: None,
121             ..ZERO_MODULE
122         },
123         phantom: PhantomData::<&'vtab T>,
124     }
125 }
126 
127 /// Create an eponymous only virtual table implementation.
128 ///
129 /// Step 2 of [Creating New Virtual Table Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
130 #[must_use]
eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab, T>131 pub fn eponymous_only_module<'vtab, T: VTab<'vtab>>() -> &'static Module<'vtab, T> {
132     // A virtual table is eponymous if its xCreate method is the exact same function
133     // as the xConnect method For eponymous-only virtual tables, the xCreate
134     // method is NULL
135     #[allow(clippy::needless_update)]
136     &Module {
137         base: ffi::sqlite3_module {
138             // We don't use V3
139             iVersion: 2,
140             xCreate: None,
141             xConnect: Some(rust_connect::<T>),
142             xBestIndex: Some(rust_best_index::<T>),
143             xDisconnect: Some(rust_disconnect::<T>),
144             xDestroy: None,
145             xOpen: Some(rust_open::<T>),
146             xClose: Some(rust_close::<T::Cursor>),
147             xFilter: Some(rust_filter::<T::Cursor>),
148             xNext: Some(rust_next::<T::Cursor>),
149             xEof: Some(rust_eof::<T::Cursor>),
150             xColumn: Some(rust_column::<T::Cursor>),
151             xRowid: Some(rust_rowid::<T::Cursor>),
152             xUpdate: None,
153             xBegin: None,
154             xSync: None,
155             xCommit: None,
156             xRollback: None,
157             xFindFunction: None,
158             xRename: None,
159             xSavepoint: None,
160             xRelease: None,
161             xRollbackTo: None,
162             ..ZERO_MODULE
163         },
164         phantom: PhantomData::<&'vtab T>,
165     }
166 }
167 
168 /// `feature = "vtab"`
169 pub struct VTabConnection(*mut ffi::sqlite3);
170 
171 impl VTabConnection {
172     // TODO sqlite3_vtab_config (http://sqlite.org/c3ref/vtab_config.html)
173 
174     // TODO sqlite3_vtab_on_conflict (http://sqlite.org/c3ref/vtab_on_conflict.html)
175 
176     /// Get access to the underlying SQLite database connection handle.
177     ///
178     /// # Warning
179     ///
180     /// You should not need to use this function. If you do need to, please
181     /// [open an issue on the rusqlite repository](https://github.com/rusqlite/rusqlite/issues) and describe
182     /// your use case.
183     ///
184     /// # Safety
185     ///
186     /// This function is unsafe because it gives you raw access
187     /// to the SQLite connection, and what you do with it could impact the
188     /// safety of this `Connection`.
handle(&mut self) -> *mut ffi::sqlite3189     pub unsafe fn handle(&mut self) -> *mut ffi::sqlite3 {
190         self.0
191     }
192 }
193 
194 /// Virtual table instance trait.
195 ///
196 /// # Safety
197 ///
198 /// The first item in a struct implementing `VTab` must be
199 /// `rusqlite::sqlite3_vtab`, and the struct must be `#[repr(C)]`.
200 ///
201 /// ```rust,ignore
202 /// #[repr(C)]
203 /// struct MyTab {
204 ///    /// Base class. Must be first
205 ///    base: rusqlite::vtab::sqlite3_vtab,
206 ///    /* Virtual table implementations will typically add additional fields */
207 /// }
208 /// ```
209 ///
210 /// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
211 pub unsafe trait VTab<'vtab>: Sized {
212     /// Client data passed to [`Connection::create_module`].
213     type Aux;
214     /// Specific cursor implementation
215     type Cursor: VTabCursor;
216 
217     /// Establish a new connection to an existing virtual table.
218     ///
219     /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xconnect_method))
connect( db: &mut VTabConnection, aux: Option<&Self::Aux>, args: &[&[u8]], ) -> Result<(String, Self)>220     fn connect(
221         db: &mut VTabConnection,
222         aux: Option<&Self::Aux>,
223         args: &[&[u8]],
224     ) -> Result<(String, Self)>;
225 
226     /// Determine the best way to access the virtual table.
227     /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xbestindex_method))
best_index(&self, info: &mut IndexInfo) -> Result<()>228     fn best_index(&self, info: &mut IndexInfo) -> Result<()>;
229 
230     /// Create a new cursor used for accessing a virtual table.
231     /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xopen_method))
open(&'vtab self) -> Result<Self::Cursor>232     fn open(&'vtab self) -> Result<Self::Cursor>;
233 }
234 
235 /// Non-eponymous virtual table instance trait.
236 ///
237 /// (See [SQLite doc](https://sqlite.org/c3ref/vtab.html))
238 pub trait CreateVTab<'vtab>: VTab<'vtab> {
239     /// Create a new instance of a virtual table in response to a CREATE VIRTUAL
240     /// TABLE statement. The `db` parameter is a pointer to the SQLite
241     /// database connection that is executing the CREATE VIRTUAL TABLE
242     /// statement.
243     ///
244     /// Call [`connect`](VTab::connect) by default.
245     /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xcreate_method))
create( db: &mut VTabConnection, aux: Option<&Self::Aux>, args: &[&[u8]], ) -> Result<(String, Self)>246     fn create(
247         db: &mut VTabConnection,
248         aux: Option<&Self::Aux>,
249         args: &[&[u8]],
250     ) -> Result<(String, Self)> {
251         Self::connect(db, aux, args)
252     }
253 
254     /// Destroy the underlying table implementation. This method undoes the work
255     /// of [`create`](CreateVTab::create).
256     ///
257     /// Do nothing by default.
258     /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xdestroy_method))
destroy(&self) -> Result<()>259     fn destroy(&self) -> Result<()> {
260         Ok(())
261     }
262 }
263 
264 /// Index constraint operator.
265 /// See [Virtual Table Constraint Operator Codes](https://sqlite.org/c3ref/c_index_constraint_eq.html) for details.
266 #[derive(Debug, PartialEq)]
267 #[allow(non_snake_case, non_camel_case_types, missing_docs)]
268 #[allow(clippy::upper_case_acronyms)]
269 pub enum IndexConstraintOp {
270     SQLITE_INDEX_CONSTRAINT_EQ,
271     SQLITE_INDEX_CONSTRAINT_GT,
272     SQLITE_INDEX_CONSTRAINT_LE,
273     SQLITE_INDEX_CONSTRAINT_LT,
274     SQLITE_INDEX_CONSTRAINT_GE,
275     SQLITE_INDEX_CONSTRAINT_MATCH,
276     SQLITE_INDEX_CONSTRAINT_LIKE,         // 3.10.0
277     SQLITE_INDEX_CONSTRAINT_GLOB,         // 3.10.0
278     SQLITE_INDEX_CONSTRAINT_REGEXP,       // 3.10.0
279     SQLITE_INDEX_CONSTRAINT_NE,           // 3.21.0
280     SQLITE_INDEX_CONSTRAINT_ISNOT,        // 3.21.0
281     SQLITE_INDEX_CONSTRAINT_ISNOTNULL,    // 3.21.0
282     SQLITE_INDEX_CONSTRAINT_ISNULL,       // 3.21.0
283     SQLITE_INDEX_CONSTRAINT_IS,           // 3.21.0
284     SQLITE_INDEX_CONSTRAINT_LIMIT,        // 3.38.0
285     SQLITE_INDEX_CONSTRAINT_OFFSET,       // 3.38.0
286     SQLITE_INDEX_CONSTRAINT_FUNCTION(u8), // 3.25.0
287 }
288 
289 impl From<u8> for IndexConstraintOp {
from(code: u8) -> IndexConstraintOp290     fn from(code: u8) -> IndexConstraintOp {
291         match code {
292             2 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ,
293             4 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_GT,
294             8 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_LE,
295             16 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_LT,
296             32 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_GE,
297             64 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_MATCH,
298             65 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_LIKE,
299             66 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_GLOB,
300             67 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_REGEXP,
301             68 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_NE,
302             69 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_ISNOT,
303             70 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_ISNOTNULL,
304             71 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_ISNULL,
305             72 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_IS,
306             73 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_LIMIT,
307             74 => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_OFFSET,
308             v => IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_FUNCTION(v),
309         }
310     }
311 }
312 
313 /// Pass information into and receive the reply from the
314 /// [`VTab::best_index`] method.
315 ///
316 /// (See [SQLite doc](http://sqlite.org/c3ref/index_info.html))
317 pub struct IndexInfo(*mut ffi::sqlite3_index_info);
318 
319 impl IndexInfo {
320     /// Iterate on index constraint and its associated usage.
321     #[inline]
constraints_and_usages(&mut self) -> IndexConstraintAndUsageIter<'_>322     pub fn constraints_and_usages(&mut self) -> IndexConstraintAndUsageIter<'_> {
323         let constraints =
324             unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
325         let constraint_usages = unsafe {
326             slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
327         };
328         IndexConstraintAndUsageIter {
329             iter: constraints.iter().zip(constraint_usages.iter_mut()),
330         }
331     }
332 
333     /// Record WHERE clause constraints.
334     #[inline]
335     #[must_use]
constraints(&self) -> IndexConstraintIter<'_>336     pub fn constraints(&self) -> IndexConstraintIter<'_> {
337         let constraints =
338             unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) };
339         IndexConstraintIter {
340             iter: constraints.iter(),
341         }
342     }
343 
344     /// Information about the ORDER BY clause.
345     #[inline]
346     #[must_use]
order_bys(&self) -> OrderByIter<'_>347     pub fn order_bys(&self) -> OrderByIter<'_> {
348         let order_bys =
349             unsafe { slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize) };
350         OrderByIter {
351             iter: order_bys.iter(),
352         }
353     }
354 
355     /// Number of terms in the ORDER BY clause
356     #[inline]
357     #[must_use]
num_of_order_by(&self) -> usize358     pub fn num_of_order_by(&self) -> usize {
359         unsafe { (*self.0).nOrderBy as usize }
360     }
361 
362     /// Information about what parameters to pass to [`VTabCursor::filter`].
363     #[inline]
constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage<'_>364     pub fn constraint_usage(&mut self, constraint_idx: usize) -> IndexConstraintUsage<'_> {
365         let constraint_usages = unsafe {
366             slice::from_raw_parts_mut((*self.0).aConstraintUsage, (*self.0).nConstraint as usize)
367         };
368         IndexConstraintUsage(&mut constraint_usages[constraint_idx])
369     }
370 
371     /// Number used to identify the index
372     #[inline]
set_idx_num(&mut self, idx_num: c_int)373     pub fn set_idx_num(&mut self, idx_num: c_int) {
374         unsafe {
375             (*self.0).idxNum = idx_num;
376         }
377     }
378 
379     /// True if output is already ordered
380     #[inline]
set_order_by_consumed(&mut self, order_by_consumed: bool)381     pub fn set_order_by_consumed(&mut self, order_by_consumed: bool) {
382         unsafe {
383             (*self.0).orderByConsumed = if order_by_consumed { 1 } else { 0 };
384         }
385     }
386 
387     /// Estimated cost of using this index
388     #[inline]
set_estimated_cost(&mut self, estimated_ost: f64)389     pub fn set_estimated_cost(&mut self, estimated_ost: f64) {
390         unsafe {
391             (*self.0).estimatedCost = estimated_ost;
392         }
393     }
394 
395     /// Estimated number of rows returned.
396     #[cfg(feature = "modern_sqlite")] // SQLite >= 3.8.2
397     #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))]
398     #[inline]
set_estimated_rows(&mut self, estimated_rows: i64)399     pub fn set_estimated_rows(&mut self, estimated_rows: i64) {
400         unsafe {
401             (*self.0).estimatedRows = estimated_rows;
402         }
403     }
404 
405     // TODO idxFlags
406     // TODO colUsed
407 
408     // TODO sqlite3_vtab_collation (http://sqlite.org/c3ref/vtab_collation.html)
409 }
410 
411 /// Iterate on index constraint and its associated usage.
412 pub struct IndexConstraintAndUsageIter<'a> {
413     iter: std::iter::Zip<
414         slice::Iter<'a, ffi::sqlite3_index_constraint>,
415         slice::IterMut<'a, ffi::sqlite3_index_constraint_usage>,
416     >,
417 }
418 
419 impl<'a> Iterator for IndexConstraintAndUsageIter<'a> {
420     type Item = (IndexConstraint<'a>, IndexConstraintUsage<'a>);
421 
422     #[inline]
next(&mut self) -> Option<(IndexConstraint<'a>, IndexConstraintUsage<'a>)>423     fn next(&mut self) -> Option<(IndexConstraint<'a>, IndexConstraintUsage<'a>)> {
424         self.iter
425             .next()
426             .map(|raw| (IndexConstraint(raw.0), IndexConstraintUsage(raw.1)))
427     }
428 
429     #[inline]
size_hint(&self) -> (usize, Option<usize>)430     fn size_hint(&self) -> (usize, Option<usize>) {
431         self.iter.size_hint()
432     }
433 }
434 
435 /// `feature = "vtab"`
436 pub struct IndexConstraintIter<'a> {
437     iter: slice::Iter<'a, ffi::sqlite3_index_constraint>,
438 }
439 
440 impl<'a> Iterator for IndexConstraintIter<'a> {
441     type Item = IndexConstraint<'a>;
442 
443     #[inline]
next(&mut self) -> Option<IndexConstraint<'a>>444     fn next(&mut self) -> Option<IndexConstraint<'a>> {
445         self.iter.next().map(IndexConstraint)
446     }
447 
448     #[inline]
size_hint(&self) -> (usize, Option<usize>)449     fn size_hint(&self) -> (usize, Option<usize>) {
450         self.iter.size_hint()
451     }
452 }
453 
454 /// WHERE clause constraint.
455 pub struct IndexConstraint<'a>(&'a ffi::sqlite3_index_constraint);
456 
457 impl IndexConstraint<'_> {
458     /// Column constrained.  -1 for ROWID
459     #[inline]
460     #[must_use]
column(&self) -> c_int461     pub fn column(&self) -> c_int {
462         self.0.iColumn
463     }
464 
465     /// Constraint operator
466     #[inline]
467     #[must_use]
operator(&self) -> IndexConstraintOp468     pub fn operator(&self) -> IndexConstraintOp {
469         IndexConstraintOp::from(self.0.op)
470     }
471 
472     /// True if this constraint is usable
473     #[inline]
474     #[must_use]
is_usable(&self) -> bool475     pub fn is_usable(&self) -> bool {
476         self.0.usable != 0
477     }
478 }
479 
480 /// Information about what parameters to pass to
481 /// [`VTabCursor::filter`].
482 pub struct IndexConstraintUsage<'a>(&'a mut ffi::sqlite3_index_constraint_usage);
483 
484 impl IndexConstraintUsage<'_> {
485     /// if `argv_index` > 0, constraint is part of argv to
486     /// [`VTabCursor::filter`]
487     #[inline]
set_argv_index(&mut self, argv_index: c_int)488     pub fn set_argv_index(&mut self, argv_index: c_int) {
489         self.0.argvIndex = argv_index;
490     }
491 
492     /// if `omit`, do not code a test for this constraint
493     #[inline]
set_omit(&mut self, omit: bool)494     pub fn set_omit(&mut self, omit: bool) {
495         self.0.omit = if omit { 1 } else { 0 };
496     }
497 }
498 
499 /// `feature = "vtab"`
500 pub struct OrderByIter<'a> {
501     iter: slice::Iter<'a, ffi::sqlite3_index_info_sqlite3_index_orderby>,
502 }
503 
504 impl<'a> Iterator for OrderByIter<'a> {
505     type Item = OrderBy<'a>;
506 
507     #[inline]
next(&mut self) -> Option<OrderBy<'a>>508     fn next(&mut self) -> Option<OrderBy<'a>> {
509         self.iter.next().map(OrderBy)
510     }
511 
512     #[inline]
size_hint(&self) -> (usize, Option<usize>)513     fn size_hint(&self) -> (usize, Option<usize>) {
514         self.iter.size_hint()
515     }
516 }
517 
518 /// A column of the ORDER BY clause.
519 pub struct OrderBy<'a>(&'a ffi::sqlite3_index_info_sqlite3_index_orderby);
520 
521 impl OrderBy<'_> {
522     /// Column number
523     #[inline]
524     #[must_use]
column(&self) -> c_int525     pub fn column(&self) -> c_int {
526         self.0.iColumn
527     }
528 
529     /// True for DESC.  False for ASC.
530     #[inline]
531     #[must_use]
is_order_by_desc(&self) -> bool532     pub fn is_order_by_desc(&self) -> bool {
533         self.0.desc != 0
534     }
535 }
536 
537 /// Virtual table cursor trait.
538 ///
539 /// # Safety
540 ///
541 /// Implementations must be like:
542 /// ```rust,ignore
543 /// #[repr(C)]
544 /// struct MyTabCursor {
545 ///    /// Base class. Must be first
546 ///    base: rusqlite::vtab::sqlite3_vtab_cursor,
547 ///    /* Virtual table implementations will typically add additional fields */
548 /// }
549 /// ```
550 ///
551 /// (See [SQLite doc](https://sqlite.org/c3ref/vtab_cursor.html))
552 pub unsafe trait VTabCursor: Sized {
553     /// Begin a search of a virtual table.
554     /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xfilter_method))
filter(&mut self, idx_num: c_int, idx_str: Option<&str>, args: &Values<'_>) -> Result<()>555     fn filter(&mut self, idx_num: c_int, idx_str: Option<&str>, args: &Values<'_>) -> Result<()>;
556     /// Advance cursor to the next row of a result set initiated by
557     /// [`filter`](VTabCursor::filter). (See [SQLite doc](https://sqlite.org/vtab.html#the_xnext_method))
next(&mut self) -> Result<()>558     fn next(&mut self) -> Result<()>;
559     /// Must return `false` if the cursor currently points to a valid row of
560     /// data, or `true` otherwise.
561     /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xeof_method))
eof(&self) -> bool562     fn eof(&self) -> bool;
563     /// Find the value for the `i`-th column of the current row.
564     /// `i` is zero-based so the first column is numbered 0.
565     /// May return its result back to SQLite using one of the specified `ctx`.
566     /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xcolumn_method))
column(&self, ctx: &mut Context, i: c_int) -> Result<()>567     fn column(&self, ctx: &mut Context, i: c_int) -> Result<()>;
568     /// Return the rowid of row that the cursor is currently pointing at.
569     /// (See [SQLite doc](https://sqlite.org/vtab.html#the_xrowid_method))
rowid(&self) -> Result<i64>570     fn rowid(&self) -> Result<i64>;
571 }
572 
573 /// Context is used by [`VTabCursor::column`] to specify the
574 /// cell value.
575 pub struct Context(*mut ffi::sqlite3_context);
576 
577 impl Context {
578     /// Set current cell value
579     #[inline]
set_result<T: ToSql>(&mut self, value: &T) -> Result<()>580     pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> {
581         let t = value.to_sql()?;
582         unsafe { set_result(self.0, &t) };
583         Ok(())
584     }
585 
586     // TODO sqlite3_vtab_nochange (http://sqlite.org/c3ref/vtab_nochange.html)
587 }
588 
589 /// Wrapper to [`VTabCursor::filter`] arguments, the values
590 /// requested by [`VTab::best_index`].
591 pub struct Values<'a> {
592     args: &'a [*mut ffi::sqlite3_value],
593 }
594 
595 impl Values<'_> {
596     /// Returns the number of values.
597     #[inline]
598     #[must_use]
len(&self) -> usize599     pub fn len(&self) -> usize {
600         self.args.len()
601     }
602 
603     /// Returns `true` if there is no value.
604     #[inline]
605     #[must_use]
is_empty(&self) -> bool606     pub fn is_empty(&self) -> bool {
607         self.args.is_empty()
608     }
609 
610     /// Returns value at `idx`
get<T: FromSql>(&self, idx: usize) -> Result<T>611     pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> {
612         let arg = self.args[idx];
613         let value = unsafe { ValueRef::from_value(arg) };
614         FromSql::column_result(value).map_err(|err| match err {
615             FromSqlError::InvalidType => Error::InvalidFilterParameterType(idx, value.data_type()),
616             FromSqlError::Other(err) => {
617                 Error::FromSqlConversionFailure(idx, value.data_type(), err)
618             }
619             FromSqlError::InvalidBlobSize { .. } => {
620                 Error::FromSqlConversionFailure(idx, value.data_type(), Box::new(err))
621             }
622             FromSqlError::OutOfRange(i) => Error::IntegralValueOutOfRange(idx, i),
623         })
624     }
625 
626     // `sqlite3_value_type` returns `SQLITE_NULL` for pointer.
627     // So it seems not possible to enhance `ValueRef::from_value`.
628     #[cfg(feature = "array")]
629     #[cfg_attr(docsrs, doc(cfg(feature = "array")))]
get_array(&self, idx: usize) -> Option<array::Array>630     fn get_array(&self, idx: usize) -> Option<array::Array> {
631         use crate::types::Value;
632         let arg = self.args[idx];
633         let ptr = unsafe { ffi::sqlite3_value_pointer(arg, array::ARRAY_TYPE) };
634         if ptr.is_null() {
635             None
636         } else {
637             Some(unsafe {
638                 let rc = array::Array::from_raw(ptr as *const Vec<Value>);
639                 let array = rc.clone();
640                 array::Array::into_raw(rc); // don't consume it
641                 array
642             })
643         }
644     }
645 
646     /// Turns `Values` into an iterator.
647     #[inline]
648     #[must_use]
iter(&self) -> ValueIter<'_>649     pub fn iter(&self) -> ValueIter<'_> {
650         ValueIter {
651             iter: self.args.iter(),
652         }
653     }
654 }
655 
656 impl<'a> IntoIterator for &'a Values<'a> {
657     type IntoIter = ValueIter<'a>;
658     type Item = ValueRef<'a>;
659 
660     #[inline]
into_iter(self) -> ValueIter<'a>661     fn into_iter(self) -> ValueIter<'a> {
662         self.iter()
663     }
664 }
665 
666 /// [`Values`] iterator.
667 pub struct ValueIter<'a> {
668     iter: slice::Iter<'a, *mut ffi::sqlite3_value>,
669 }
670 
671 impl<'a> Iterator for ValueIter<'a> {
672     type Item = ValueRef<'a>;
673 
674     #[inline]
next(&mut self) -> Option<ValueRef<'a>>675     fn next(&mut self) -> Option<ValueRef<'a>> {
676         self.iter
677             .next()
678             .map(|&raw| unsafe { ValueRef::from_value(raw) })
679     }
680 
681     #[inline]
size_hint(&self) -> (usize, Option<usize>)682     fn size_hint(&self) -> (usize, Option<usize>) {
683         self.iter.size_hint()
684     }
685 }
686 
687 impl Connection {
688     /// Register a virtual table implementation.
689     ///
690     /// Step 3 of [Creating New Virtual Table
691     /// Implementations](https://sqlite.org/vtab.html#creating_new_virtual_table_implementations).
692     #[inline]
create_module<'vtab, T: VTab<'vtab>>( &self, module_name: &str, module: &'static Module<'vtab, T>, aux: Option<T::Aux>, ) -> Result<()>693     pub fn create_module<'vtab, T: VTab<'vtab>>(
694         &self,
695         module_name: &str,
696         module: &'static Module<'vtab, T>,
697         aux: Option<T::Aux>,
698     ) -> Result<()> {
699         self.db.borrow_mut().create_module(module_name, module, aux)
700     }
701 }
702 
703 impl InnerConnection {
create_module<'vtab, T: VTab<'vtab>>( &mut self, module_name: &str, module: &'static Module<'vtab, T>, aux: Option<T::Aux>, ) -> Result<()>704     fn create_module<'vtab, T: VTab<'vtab>>(
705         &mut self,
706         module_name: &str,
707         module: &'static Module<'vtab, T>,
708         aux: Option<T::Aux>,
709     ) -> Result<()> {
710         let c_name = str_to_cstring(module_name)?;
711         let r = match aux {
712             Some(aux) => {
713                 let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
714                 unsafe {
715                     ffi::sqlite3_create_module_v2(
716                         self.db(),
717                         c_name.as_ptr(),
718                         &module.base,
719                         boxed_aux.cast::<c_void>(),
720                         Some(free_boxed_value::<T::Aux>),
721                     )
722                 }
723             }
724             None => unsafe {
725                 ffi::sqlite3_create_module_v2(
726                     self.db(),
727                     c_name.as_ptr(),
728                     &module.base,
729                     ptr::null_mut(),
730                     None,
731                 )
732             },
733         };
734         self.decode_result(r)
735     }
736 }
737 
738 /// Escape double-quote (`"`) character occurrences by
739 /// doubling them (`""`).
740 #[must_use]
escape_double_quote(identifier: &str) -> Cow<'_, str>741 pub fn escape_double_quote(identifier: &str) -> Cow<'_, str> {
742     if identifier.contains('"') {
743         // escape quote by doubling them
744         Owned(identifier.replace('"', "\"\""))
745     } else {
746         Borrowed(identifier)
747     }
748 }
749 /// Dequote string
750 #[must_use]
dequote(s: &str) -> &str751 pub fn dequote(s: &str) -> &str {
752     if s.len() < 2 {
753         return s;
754     }
755     match s.bytes().next() {
756         Some(b) if b == b'"' || b == b'\'' => match s.bytes().rev().next() {
757             Some(e) if e == b => &s[1..s.len() - 1],
758             _ => s,
759         },
760         _ => s,
761     }
762 }
763 /// The boolean can be one of:
764 /// ```text
765 /// 1 yes true on
766 /// 0 no false off
767 /// ```
768 #[must_use]
parse_boolean(s: &str) -> Option<bool>769 pub fn parse_boolean(s: &str) -> Option<bool> {
770     if s.eq_ignore_ascii_case("yes")
771         || s.eq_ignore_ascii_case("on")
772         || s.eq_ignore_ascii_case("true")
773         || s.eq("1")
774     {
775         Some(true)
776     } else if s.eq_ignore_ascii_case("no")
777         || s.eq_ignore_ascii_case("off")
778         || s.eq_ignore_ascii_case("false")
779         || s.eq("0")
780     {
781         Some(false)
782     } else {
783         None
784     }
785 }
786 
787 // FIXME copy/paste from function.rs
free_boxed_value<T>(p: *mut c_void)788 unsafe extern "C" fn free_boxed_value<T>(p: *mut c_void) {
789     drop(Box::from_raw(p.cast::<T>()));
790 }
791 
rust_create<'vtab, T>( db: *mut ffi::sqlite3, aux: *mut c_void, argc: c_int, argv: *const *const c_char, pp_vtab: *mut *mut ffi::sqlite3_vtab, err_msg: *mut *mut c_char, ) -> c_int where T: CreateVTab<'vtab>,792 unsafe extern "C" fn rust_create<'vtab, T>(
793     db: *mut ffi::sqlite3,
794     aux: *mut c_void,
795     argc: c_int,
796     argv: *const *const c_char,
797     pp_vtab: *mut *mut ffi::sqlite3_vtab,
798     err_msg: *mut *mut c_char,
799 ) -> c_int
800 where
801     T: CreateVTab<'vtab>,
802 {
803     use std::ffi::CStr;
804 
805     let mut conn = VTabConnection(db);
806     let aux = aux.cast::<T::Aux>();
807     let args = slice::from_raw_parts(argv, argc as usize);
808     let vec = args
809         .iter()
810         .map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error>
811         .collect::<Vec<_>>();
812     match T::create(&mut conn, aux.as_ref(), &vec[..]) {
813         Ok((sql, vtab)) => match ::std::ffi::CString::new(sql) {
814             Ok(c_sql) => {
815                 let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
816                 if rc == ffi::SQLITE_OK {
817                     let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
818                     *pp_vtab = boxed_vtab.cast::<ffi::sqlite3_vtab>();
819                     ffi::SQLITE_OK
820                 } else {
821                     let err = error_from_sqlite_code(rc, None);
822                     *err_msg = alloc(&err.to_string());
823                     rc
824                 }
825             }
826             Err(err) => {
827                 *err_msg = alloc(&err.to_string());
828                 ffi::SQLITE_ERROR
829             }
830         },
831         Err(Error::SqliteFailure(err, s)) => {
832             if let Some(s) = s {
833                 *err_msg = alloc(&s);
834             }
835             err.extended_code
836         }
837         Err(err) => {
838             *err_msg = alloc(&err.to_string());
839             ffi::SQLITE_ERROR
840         }
841     }
842 }
843 
rust_connect<'vtab, T>( db: *mut ffi::sqlite3, aux: *mut c_void, argc: c_int, argv: *const *const c_char, pp_vtab: *mut *mut ffi::sqlite3_vtab, err_msg: *mut *mut c_char, ) -> c_int where T: VTab<'vtab>,844 unsafe extern "C" fn rust_connect<'vtab, T>(
845     db: *mut ffi::sqlite3,
846     aux: *mut c_void,
847     argc: c_int,
848     argv: *const *const c_char,
849     pp_vtab: *mut *mut ffi::sqlite3_vtab,
850     err_msg: *mut *mut c_char,
851 ) -> c_int
852 where
853     T: VTab<'vtab>,
854 {
855     use std::ffi::CStr;
856 
857     let mut conn = VTabConnection(db);
858     let aux = aux.cast::<T::Aux>();
859     let args = slice::from_raw_parts(argv, argc as usize);
860     let vec = args
861         .iter()
862         .map(|&cs| CStr::from_ptr(cs).to_bytes()) // FIXME .to_str() -> Result<&str, Utf8Error>
863         .collect::<Vec<_>>();
864     match T::connect(&mut conn, aux.as_ref(), &vec[..]) {
865         Ok((sql, vtab)) => match ::std::ffi::CString::new(sql) {
866             Ok(c_sql) => {
867                 let rc = ffi::sqlite3_declare_vtab(db, c_sql.as_ptr());
868                 if rc == ffi::SQLITE_OK {
869                     let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
870                     *pp_vtab = boxed_vtab.cast::<ffi::sqlite3_vtab>();
871                     ffi::SQLITE_OK
872                 } else {
873                     let err = error_from_sqlite_code(rc, None);
874                     *err_msg = alloc(&err.to_string());
875                     rc
876                 }
877             }
878             Err(err) => {
879                 *err_msg = alloc(&err.to_string());
880                 ffi::SQLITE_ERROR
881             }
882         },
883         Err(Error::SqliteFailure(err, s)) => {
884             if let Some(s) = s {
885                 *err_msg = alloc(&s);
886             }
887             err.extended_code
888         }
889         Err(err) => {
890             *err_msg = alloc(&err.to_string());
891             ffi::SQLITE_ERROR
892         }
893     }
894 }
895 
rust_best_index<'vtab, T>( vtab: *mut ffi::sqlite3_vtab, info: *mut ffi::sqlite3_index_info, ) -> c_int where T: VTab<'vtab>,896 unsafe extern "C" fn rust_best_index<'vtab, T>(
897     vtab: *mut ffi::sqlite3_vtab,
898     info: *mut ffi::sqlite3_index_info,
899 ) -> c_int
900 where
901     T: VTab<'vtab>,
902 {
903     let vt = vtab.cast::<T>();
904     let mut idx_info = IndexInfo(info);
905     match (*vt).best_index(&mut idx_info) {
906         Ok(_) => ffi::SQLITE_OK,
907         Err(Error::SqliteFailure(err, s)) => {
908             if let Some(err_msg) = s {
909                 set_err_msg(vtab, &err_msg);
910             }
911             err.extended_code
912         }
913         Err(err) => {
914             set_err_msg(vtab, &err.to_string());
915             ffi::SQLITE_ERROR
916         }
917     }
918 }
919 
rust_disconnect<'vtab, T>(vtab: *mut ffi::sqlite3_vtab) -> c_int where T: VTab<'vtab>,920 unsafe extern "C" fn rust_disconnect<'vtab, T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
921 where
922     T: VTab<'vtab>,
923 {
924     if vtab.is_null() {
925         return ffi::SQLITE_OK;
926     }
927     let vtab = vtab.cast::<T>();
928     drop(Box::from_raw(vtab));
929     ffi::SQLITE_OK
930 }
931 
rust_destroy<'vtab, T>(vtab: *mut ffi::sqlite3_vtab) -> c_int where T: CreateVTab<'vtab>,932 unsafe extern "C" fn rust_destroy<'vtab, T>(vtab: *mut ffi::sqlite3_vtab) -> c_int
933 where
934     T: CreateVTab<'vtab>,
935 {
936     if vtab.is_null() {
937         return ffi::SQLITE_OK;
938     }
939     let vt = vtab.cast::<T>();
940     match (*vt).destroy() {
941         Ok(_) => {
942             drop(Box::from_raw(vt));
943             ffi::SQLITE_OK
944         }
945         Err(Error::SqliteFailure(err, s)) => {
946             if let Some(err_msg) = s {
947                 set_err_msg(vtab, &err_msg);
948             }
949             err.extended_code
950         }
951         Err(err) => {
952             set_err_msg(vtab, &err.to_string());
953             ffi::SQLITE_ERROR
954         }
955     }
956 }
957 
rust_open<'vtab, T: 'vtab>( vtab: *mut ffi::sqlite3_vtab, pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor, ) -> c_int where T: VTab<'vtab>,958 unsafe extern "C" fn rust_open<'vtab, T: 'vtab>(
959     vtab: *mut ffi::sqlite3_vtab,
960     pp_cursor: *mut *mut ffi::sqlite3_vtab_cursor,
961 ) -> c_int
962 where
963     T: VTab<'vtab>,
964 {
965     let vt = vtab.cast::<T>();
966     match (*vt).open() {
967         Ok(cursor) => {
968             let boxed_cursor: *mut T::Cursor = Box::into_raw(Box::new(cursor));
969             *pp_cursor = boxed_cursor.cast::<ffi::sqlite3_vtab_cursor>();
970             ffi::SQLITE_OK
971         }
972         Err(Error::SqliteFailure(err, s)) => {
973             if let Some(err_msg) = s {
974                 set_err_msg(vtab, &err_msg);
975             }
976             err.extended_code
977         }
978         Err(err) => {
979             set_err_msg(vtab, &err.to_string());
980             ffi::SQLITE_ERROR
981         }
982     }
983 }
984 
rust_close<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int where C: VTabCursor,985 unsafe extern "C" fn rust_close<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
986 where
987     C: VTabCursor,
988 {
989     let cr = cursor.cast::<C>();
990     drop(Box::from_raw(cr));
991     ffi::SQLITE_OK
992 }
993 
rust_filter<C>( cursor: *mut ffi::sqlite3_vtab_cursor, idx_num: c_int, idx_str: *const c_char, argc: c_int, argv: *mut *mut ffi::sqlite3_value, ) -> c_int where C: VTabCursor,994 unsafe extern "C" fn rust_filter<C>(
995     cursor: *mut ffi::sqlite3_vtab_cursor,
996     idx_num: c_int,
997     idx_str: *const c_char,
998     argc: c_int,
999     argv: *mut *mut ffi::sqlite3_value,
1000 ) -> c_int
1001 where
1002     C: VTabCursor,
1003 {
1004     use std::ffi::CStr;
1005     use std::str;
1006     let idx_name = if idx_str.is_null() {
1007         None
1008     } else {
1009         let c_slice = CStr::from_ptr(idx_str).to_bytes();
1010         Some(str::from_utf8_unchecked(c_slice))
1011     };
1012     let args = slice::from_raw_parts_mut(argv, argc as usize);
1013     let values = Values { args };
1014     let cr = cursor as *mut C;
1015     cursor_error(cursor, (*cr).filter(idx_num, idx_name, &values))
1016 }
1017 
rust_next<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int where C: VTabCursor,1018 unsafe extern "C" fn rust_next<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
1019 where
1020     C: VTabCursor,
1021 {
1022     let cr = cursor as *mut C;
1023     cursor_error(cursor, (*cr).next())
1024 }
1025 
rust_eof<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int where C: VTabCursor,1026 unsafe extern "C" fn rust_eof<C>(cursor: *mut ffi::sqlite3_vtab_cursor) -> c_int
1027 where
1028     C: VTabCursor,
1029 {
1030     let cr = cursor.cast::<C>();
1031     (*cr).eof() as c_int
1032 }
1033 
rust_column<C>( cursor: *mut ffi::sqlite3_vtab_cursor, ctx: *mut ffi::sqlite3_context, i: c_int, ) -> c_int where C: VTabCursor,1034 unsafe extern "C" fn rust_column<C>(
1035     cursor: *mut ffi::sqlite3_vtab_cursor,
1036     ctx: *mut ffi::sqlite3_context,
1037     i: c_int,
1038 ) -> c_int
1039 where
1040     C: VTabCursor,
1041 {
1042     let cr = cursor.cast::<C>();
1043     let mut ctxt = Context(ctx);
1044     result_error(ctx, (*cr).column(&mut ctxt, i))
1045 }
1046 
rust_rowid<C>( cursor: *mut ffi::sqlite3_vtab_cursor, p_rowid: *mut ffi::sqlite3_int64, ) -> c_int where C: VTabCursor,1047 unsafe extern "C" fn rust_rowid<C>(
1048     cursor: *mut ffi::sqlite3_vtab_cursor,
1049     p_rowid: *mut ffi::sqlite3_int64,
1050 ) -> c_int
1051 where
1052     C: VTabCursor,
1053 {
1054     let cr = cursor.cast::<C>();
1055     match (*cr).rowid() {
1056         Ok(rowid) => {
1057             *p_rowid = rowid;
1058             ffi::SQLITE_OK
1059         }
1060         err => cursor_error(cursor, err),
1061     }
1062 }
1063 
1064 /// Virtual table cursors can set an error message by assigning a string to
1065 /// `zErrMsg`.
1066 #[cold]
cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<T>) -> c_int1067 unsafe fn cursor_error<T>(cursor: *mut ffi::sqlite3_vtab_cursor, result: Result<T>) -> c_int {
1068     match result {
1069         Ok(_) => ffi::SQLITE_OK,
1070         Err(Error::SqliteFailure(err, s)) => {
1071             if let Some(err_msg) = s {
1072                 set_err_msg((*cursor).pVtab, &err_msg);
1073             }
1074             err.extended_code
1075         }
1076         Err(err) => {
1077             set_err_msg((*cursor).pVtab, &err.to_string());
1078             ffi::SQLITE_ERROR
1079         }
1080     }
1081 }
1082 
1083 /// Virtual tables methods can set an error message by assigning a string to
1084 /// `zErrMsg`.
1085 #[cold]
set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str)1086 unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) {
1087     if !(*vtab).zErrMsg.is_null() {
1088         ffi::sqlite3_free((*vtab).zErrMsg.cast::<c_void>());
1089     }
1090     (*vtab).zErrMsg = alloc(err_msg);
1091 }
1092 
1093 /// To raise an error, the `column` method should use this method to set the
1094 /// error message and return the error code.
1095 #[cold]
result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int1096 unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> c_int {
1097     match result {
1098         Ok(_) => ffi::SQLITE_OK,
1099         Err(Error::SqliteFailure(err, s)) => {
1100             match err.extended_code {
1101                 ffi::SQLITE_TOOBIG => {
1102                     ffi::sqlite3_result_error_toobig(ctx);
1103                 }
1104                 ffi::SQLITE_NOMEM => {
1105                     ffi::sqlite3_result_error_nomem(ctx);
1106                 }
1107                 code => {
1108                     ffi::sqlite3_result_error_code(ctx, code);
1109                     if let Some(Ok(cstr)) = s.map(|s| str_to_cstring(&s)) {
1110                         ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
1111                     }
1112                 }
1113             };
1114             err.extended_code
1115         }
1116         Err(err) => {
1117             ffi::sqlite3_result_error_code(ctx, ffi::SQLITE_ERROR);
1118             if let Ok(cstr) = str_to_cstring(&err.to_string()) {
1119                 ffi::sqlite3_result_error(ctx, cstr.as_ptr(), -1);
1120             }
1121             ffi::SQLITE_ERROR
1122         }
1123     }
1124 }
1125 
1126 // Space to hold this string must be obtained
1127 // from an SQLite memory allocation function
alloc(s: &str) -> *mut c_char1128 fn alloc(s: &str) -> *mut c_char {
1129     crate::util::SqliteMallocString::from_str(s).into_raw()
1130 }
1131 
1132 #[cfg(feature = "array")]
1133 #[cfg_attr(docsrs, doc(cfg(feature = "array")))]
1134 pub mod array;
1135 #[cfg(feature = "csvtab")]
1136 #[cfg_attr(docsrs, doc(cfg(feature = "csvtab")))]
1137 pub mod csvtab;
1138 #[cfg(feature = "series")]
1139 #[cfg_attr(docsrs, doc(cfg(feature = "series")))]
1140 pub mod series; // SQLite >= 3.9.0
1141 
1142 #[cfg(test)]
1143 mod test {
1144     #[test]
test_dequote()1145     fn test_dequote() {
1146         assert_eq!("", super::dequote(""));
1147         assert_eq!("'", super::dequote("'"));
1148         assert_eq!("\"", super::dequote("\""));
1149         assert_eq!("'\"", super::dequote("'\""));
1150         assert_eq!("", super::dequote("''"));
1151         assert_eq!("", super::dequote("\"\""));
1152         assert_eq!("x", super::dequote("'x'"));
1153         assert_eq!("x", super::dequote("\"x\""));
1154         assert_eq!("x", super::dequote("x"));
1155     }
1156     #[test]
test_parse_boolean()1157     fn test_parse_boolean() {
1158         assert_eq!(None, super::parse_boolean(""));
1159         assert_eq!(Some(true), super::parse_boolean("1"));
1160         assert_eq!(Some(true), super::parse_boolean("yes"));
1161         assert_eq!(Some(true), super::parse_boolean("on"));
1162         assert_eq!(Some(true), super::parse_boolean("true"));
1163         assert_eq!(Some(false), super::parse_boolean("0"));
1164         assert_eq!(Some(false), super::parse_boolean("no"));
1165         assert_eq!(Some(false), super::parse_boolean("off"));
1166         assert_eq!(Some(false), super::parse_boolean("false"));
1167     }
1168 }
1169