sqlite_loadable/
table.rs

1//! Defining virtual tables and table functions on sqlite3 database connections.
2
3// ![allow(clippy::not_unsafe_ptr_arg_deref)]
4
5use sqlite3ext_sys::sqlite3_index_info_sqlite3_index_constraint_usage;
6use sqlite3ext_sys::{
7    sqlite3, sqlite3_context, sqlite3_index_info, sqlite3_index_info_sqlite3_index_constraint,
8    sqlite3_index_info_sqlite3_index_orderby, sqlite3_module, sqlite3_value, sqlite3_vtab,
9    sqlite3_vtab_cursor,
10};
11
12use crate::constants::*;
13use std::ffi::CString;
14use std::marker::PhantomData;
15use std::marker::Sync;
16use std::os::raw::{c_char, c_int, c_void};
17use std::ptr;
18use std::slice;
19use std::str::Utf8Error;
20
21use crate::api::{mprintf, value_type, MprintfError, ValueType};
22use crate::errors::{Error, ErrorKind, Result};
23use crate::ext::sqlitex_declare_vtab;
24use crate::ext::{sqlite3ext_create_module_v2, sqlite3ext_vtab_distinct};
25use serde::{Deserialize, Serialize};
26
27/// Possible operators for a given constraint, found and used in xBestIndex and xFilter.
28/// <https://www.sqlite.org/c3ref/c_index_constraint_eq.html>
29/// TODO EQ=Equals, GT=GreaterThan, etc.
30#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)]
31pub enum ConstraintOperator {
32    /// 'Equals', ex "="
33    EQ,
34
35    /// 'Greater Than', ex ">"
36    GT,
37
38    /// 'Less than or equal to', ex "<="
39    LE,
40
41    /// 'Less than', ex "<"
42    LT,
43
44    /// 'Greater than or equal to', ex ">="
45    GE,
46
47    /// 'Match' function
48    MATCH,
49
50    /// 'LIKE' function
51    LIKE,
52
53    /// 'Glob' function
54    GLOB,
55
56    /// 'REGEXP' function
57    REGEXP,
58
59    /// 'Not equal to', ex "!=" or "<>"
60    NE,
61
62    /// 'is not' operation
63    ISNOT,
64
65    /// 'is not null' operation
66    ISNOTNULL,
67
68    /// 'is null' operation
69    ISNULL,
70
71    /// 'is' operation
72    IS,
73
74    /// 'LIMIT' constraint
75    LIMIT,
76
77    /// 'OFFSET' operation
78    OFFSET,
79
80    /// custom funciton overload, used in xFindFunction, see <https://www.sqlite.org/vtab.html#xfindfunction>
81    FUNCTION(u8),
82}
83
84/// Return the `ConstraintOperator` for the given raw operator, usually
85/// from sqlite3_index_info.sqlite3_index_constraint.op .
86/// Values from <https://www.sqlite.org/c3ref/c_index_constraint_eq.html>
87pub fn operator(op: u8) -> Option<ConstraintOperator> {
88    match op {
89        2 => Some(ConstraintOperator::EQ),
90        4 => Some(ConstraintOperator::GT),
91        8 => Some(ConstraintOperator::LE),
92        16 => Some(ConstraintOperator::LT),
93        32 => Some(ConstraintOperator::GE),
94        64 => Some(ConstraintOperator::MATCH),
95        65 => Some(ConstraintOperator::LIKE),
96        66 => Some(ConstraintOperator::GLOB),
97        67 => Some(ConstraintOperator::REGEXP),
98        68 => Some(ConstraintOperator::NE),
99        69 => Some(ConstraintOperator::ISNOT),
100        70 => Some(ConstraintOperator::ISNOTNULL),
101        71 => Some(ConstraintOperator::ISNULL),
102        72 => Some(ConstraintOperator::IS),
103        73 => Some(ConstraintOperator::LIMIT),
104        74 => Some(ConstraintOperator::OFFSET),
105        150..=255 => Some(ConstraintOperator::FUNCTION(op)),
106        _ => None,
107    }
108}
109
110/// Wraps the raw sqlite3_index_info C struct, which represents
111/// the possible constraints and outputs the xBestIndex method
112/// should use and return.
113/// <https://www.sqlite.org/c3ref/index_info.html>
114#[derive(Debug)]
115pub struct IndexInfo {
116    index_info: *mut sqlite3_index_info,
117}
118impl IndexInfo {
119    /// "Mask of SQLITE_INDEX_SCAN_* flags"
120    pub fn idx_flag(&self) -> i32 {
121        unsafe { (*self.index_info).idxFlags }
122    }
123    pub fn constraints(&self) -> Vec<Constraint> {
124        let constraints = unsafe {
125            slice::from_raw_parts(
126                (*self.index_info).aConstraint,
127                (*self.index_info).nConstraint as usize,
128            )
129        };
130
131        let constraint_usages = unsafe {
132            slice::from_raw_parts_mut(
133                (*self.index_info).aConstraintUsage,
134                (*self.index_info).nConstraint as usize,
135            )
136        };
137
138        return constraints
139            .iter()
140            .zip(constraint_usages.iter_mut())
141            .map(|z| Constraint {
142                constraint: *z.0,
143                usage: z.1,
144            })
145            .collect();
146    }
147    pub fn order_bys(&self) -> Vec<OrderBy> {
148        let order_bys = unsafe {
149            slice::from_raw_parts(
150                (*self.index_info).aOrderBy,
151                (*self.index_info).nOrderBy as usize,
152            )
153        };
154        return order_bys.iter().map(|o| OrderBy { order_by: *o }).collect();
155    }
156    pub fn set_idxnum(&mut self, value: i32) {
157        unsafe {
158            (*self.index_info).idxNum = value;
159        }
160    }
161    pub fn set_idxstr(&mut self, value: &str) -> crate::Result<()> {
162        let idxstr = match mprintf(value) {
163            Ok(idxstr) => idxstr,
164            Err(err) => {
165                return match err {
166                    MprintfError::Oom => Err(Error::new_message("OOM todo change to code?")),
167                    MprintfError::Nul(err) => Err(err.into()),
168                }
169            }
170        };
171        unsafe {
172            (*self.index_info).idxStr = idxstr;
173            (*self.index_info).needToFreeIdxStr = 1;
174        }
175        Ok(())
176    }
177    pub fn set_estimated_rows(&mut self, value: i64) {
178        unsafe {
179            (*self.index_info).estimatedRows = value;
180        }
181    }
182
183    /// "The estimatedCost field should be set to the estimated number of disk
184    /// access operations required to execute this query against the virtual table."
185    /// <https://www.sqlite.org/vtab.html#outputs>
186    pub fn set_estimated_cost(&mut self, value: f64) {
187        unsafe {
188            (*self.index_info).estimatedCost = value;
189        }
190    }
191    // TODO ORDER BY
192
193    // TODO the u64 by itself isn't very useful - offer a func that does the
194    // manually bitshifting/checks internally.
195    pub fn columns_used(&self) -> u64 {
196        unsafe { (*self.index_info).colUsed }
197    }
198    pub fn distinct(&self) -> i32 {
199        unsafe { sqlite3ext_vtab_distinct(self.index_info) }
200    }
201    // TODO idxFlags
202}
203
204/// Wraps the raw sqlite3_index_constraint and sqlite3_index_constraint_usage
205/// C structs for ergonomic use in Rust.
206#[derive(Debug)]
207pub struct Constraint {
208    pub constraint: sqlite3_index_info_sqlite3_index_constraint,
209    pub usage: *mut sqlite3_index_info_sqlite3_index_constraint_usage,
210}
211
212impl Constraint {
213    pub fn column_idx(&self) -> i32 {
214        (self.constraint).iColumn
215    }
216
217    pub fn usable(&self) -> bool {
218        (self.constraint).usable != 0
219    }
220    pub fn op(&self) -> Option<ConstraintOperator> {
221        operator((self.constraint).op)
222    }
223
224    pub fn set_argv_index(&mut self, i: i32) {
225        unsafe { (*self.usage).argvIndex = i };
226    }
227    pub fn set_omit(&mut self, value: bool) {
228        unsafe { (*self.usage).omit = u8::from(value) }
229    }
230}
231
232#[derive(Debug)]
233pub enum OrderByDirection {
234    Ascending,
235    Descending,
236}
237#[derive(Debug)]
238pub struct OrderBy {
239    order_by: sqlite3_index_info_sqlite3_index_orderby,
240}
241impl OrderBy {
242    pub fn icolumn(&self) -> i32 {
243        (self.order_by).iColumn
244    }
245    pub fn direction(&self) -> OrderByDirection {
246        if (self.order_by).desc == 1 {
247            OrderByDirection::Descending
248        } else {
249            OrderByDirection::Ascending
250        }
251    }
252}
253
254/// Possible errors to return in xBestIndex.
255pub enum BestIndexError {
256    /// Returns SQLITE_CONSTRAINT. See <https://www.sqlite.org/vtab.html#return_value>
257    Constraint,
258    Error,
259}
260#[repr(transparent)]
261struct Module<'vtab, T: VTab<'vtab>> {
262    base: sqlite3_module,
263    phantom: PhantomData<&'vtab T>,
264}
265
266unsafe impl<'vtab, T: VTab<'vtab>> Send for Module<'vtab, T> {}
267unsafe impl<'vtab, T: VTab<'vtab>> Sync for Module<'vtab, T> {}
268
269/// Define a table function on the given sqlite3 database.
270/// "Table function" is the same as "eponymous-only" virtual table
271/// described at <https://www.sqlite.org/vtab.html#eponymous_only_virtual_tables>
272pub fn define_table_function<'vtab, T: VTab<'vtab> + 'vtab>(
273    db: *mut sqlite3,
274    name: &str,
275    aux: Option<T::Aux>,
276) -> Result<()> {
277    let m = &Module {
278        base: sqlite3_module {
279            iVersion: 2,
280            xCreate: None,
281            xConnect: Some(rust_connect::<T>),
282            xBestIndex: Some(rust_best_index::<T>),
283            xDisconnect: Some(rust_disconnect::<T>),
284            xDestroy: Some(rust_destroy::<T>),
285            xOpen: Some(rust_open::<T>),
286            xClose: Some(rust_close::<T::Cursor>),
287            xFilter: Some(rust_filter::<T::Cursor>),
288            xNext: Some(rust_next::<T::Cursor>),
289            xEof: Some(rust_eof::<T::Cursor>),
290            xColumn: Some(rust_column::<T::Cursor>),
291            xRowid: Some(rust_rowid::<T::Cursor>),
292            xUpdate: None,
293            xBegin: None,
294            xSync: None,
295            xCommit: None,
296            xRollback: None,
297            xFindFunction: None,
298            xRename: None,
299            xSavepoint: None,
300            xRelease: None,
301            xRollbackTo: None,
302            xShadowName: None,
303        },
304        phantom: PhantomData::<&'vtab T>,
305    };
306    let cname = CString::new(name)?;
307    let p_app = match aux {
308        Some(aux) => {
309            let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
310            boxed_aux.cast::<c_void>()
311        }
312        None => ptr::null_mut(),
313    };
314    let result = unsafe {
315        sqlite3ext_create_module_v2(
316            db,
317            cname.as_ptr(),
318            &m.base,
319            p_app,
320            Some(destroy_aux::<T::Aux>),
321        )
322    };
323    if result != SQLITE_OKAY {
324        return Err(Error::new(ErrorKind::TableFunction(result)));
325    }
326    Ok(())
327}
328
329// source: https://github.com/rusqlite/rusqlite/blob/12a6d3c1b1bdd58ca7103619b8a133e76d30decd/src/vtab/mod.rs#L931
330unsafe extern "C" fn destroy_aux<T>(p: *mut c_void) {
331    if !p.is_null() {
332        drop(Box::from_raw(p.cast::<T>()));
333    }
334}
335
336/// Define a virtual table on the sqlite3 database connection. Optionally
337/// pass in an auxillary object, which
338pub fn define_virtual_table<'vtab, T: VTab<'vtab> + 'vtab>(
339    db: *mut sqlite3,
340    name: &str,
341    aux: Option<T::Aux>,
342) -> Result<()> {
343    let m = &Module {
344        base: sqlite3_module {
345            iVersion: 2,
346            xCreate: Some(rust_create::<T>),
347            xConnect: Some(rust_connect::<T>),
348            xBestIndex: Some(rust_best_index::<T>),
349            xDisconnect: Some(rust_disconnect::<T>),
350            xDestroy: Some(rust_destroy::<T>),
351            xOpen: Some(rust_open::<T>),
352            xClose: Some(rust_close::<T::Cursor>),
353            xFilter: Some(rust_filter::<T::Cursor>),
354            xNext: Some(rust_next::<T::Cursor>),
355            xEof: Some(rust_eof::<T::Cursor>),
356            xColumn: Some(rust_column::<T::Cursor>),
357            xRowid: Some(rust_rowid::<T::Cursor>),
358            xUpdate: None,
359            xBegin: None,
360            xSync: None,
361            xCommit: None,
362            xRollback: None,
363            xFindFunction: None,
364            xRename: None,
365            xSavepoint: None,
366            xRelease: None,
367            xRollbackTo: None,
368            xShadowName: None,
369        },
370        phantom: PhantomData::<&'vtab T>,
371    };
372    let cname = CString::new(name)?;
373    let app_pointer = match aux {
374        Some(aux) => Box::into_raw(Box::new(aux)).cast::<c_void>(),
375        None => ptr::null_mut(),
376    };
377    let result = unsafe {
378        sqlite3ext_create_module_v2(
379            db,
380            cname.as_ptr(),
381            &m.base,
382            app_pointer,
383            Some(destroy_aux::<T::Aux>),
384        )
385    };
386    if result != SQLITE_OKAY {
387        return Err(Error::new(ErrorKind::TableFunction(result)));
388    }
389    Ok(())
390}
391
392pub fn define_virtual_table_writeable<'vtab, T: VTabWriteable<'vtab> + 'vtab>(
393    db: *mut sqlite3,
394    name: &str,
395    aux: Option<T::Aux>,
396) -> Result<()> {
397    let m = &Module {
398        base: sqlite3_module {
399            iVersion: 2,
400            xCreate: Some(rust_create::<T>),
401            xConnect: Some(rust_connect::<T>),
402            xBestIndex: Some(rust_best_index::<T>),
403            xDisconnect: Some(rust_disconnect::<T>),
404            xDestroy: Some(rust_destroy::<T>),
405            xOpen: Some(rust_open::<T>),
406            xClose: Some(rust_close::<T::Cursor>),
407            xFilter: Some(rust_filter::<T::Cursor>),
408            xNext: Some(rust_next::<T::Cursor>),
409            xEof: Some(rust_eof::<T::Cursor>),
410            xColumn: Some(rust_column::<T::Cursor>),
411            xRowid: Some(rust_rowid::<T::Cursor>),
412            xUpdate: Some(rust_update::<T>),
413            xBegin: None,    //Some(rust_begin::<T>),
414            xSync: None,     //Some(rust_sync::<T>),
415            xCommit: None,   //Some(rust_commit::<T>),
416            xRollback: None, //Some(rust_rollback::<T>),
417            xFindFunction: Some(rust_find_function::<T>),
418            xRename: None,
419            xSavepoint: None,
420            xRelease: None,
421            xRollbackTo: None,
422            xShadowName: None,
423        },
424        phantom: PhantomData::<&'vtab T>,
425    };
426    let cname = CString::new(name)?;
427    let p_app = match aux {
428        Some(aux) => {
429            let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
430            boxed_aux.cast::<c_void>()
431        }
432        None => ptr::null_mut(),
433    };
434    let result = unsafe {
435        sqlite3ext_create_module_v2(
436            db,
437            cname.as_ptr(),
438            &m.base,
439            p_app,
440            Some(destroy_aux::<T::Aux>),
441        )
442    };
443    if result != SQLITE_OKAY {
444        return Err(Error::new(ErrorKind::TableFunction(result)));
445    }
446    Ok(())
447}
448
449pub fn define_virtual_table_writeable_with_transactions<
450    'vtab,
451    T: VTabWriteableWithTransactions<'vtab> + 'vtab,
452>(
453    db: *mut sqlite3,
454    name: &str,
455    aux: Option<T::Aux>,
456) -> Result<()> {
457    let m = &Module {
458        base: sqlite3_module {
459            iVersion: 2,
460            xCreate: Some(rust_create::<T>),
461            xConnect: Some(rust_connect::<T>),
462            xBestIndex: Some(rust_best_index::<T>),
463            xDisconnect: Some(rust_disconnect::<T>),
464            xDestroy: Some(rust_destroy::<T>),
465            xOpen: Some(rust_open::<T>),
466            xClose: Some(rust_close::<T::Cursor>),
467            xFilter: Some(rust_filter::<T::Cursor>),
468            xNext: Some(rust_next::<T::Cursor>),
469            xEof: Some(rust_eof::<T::Cursor>),
470            xColumn: Some(rust_column::<T::Cursor>),
471            xRowid: Some(rust_rowid::<T::Cursor>),
472            xUpdate: Some(rust_update::<T>),
473            xBegin: Some(rust_begin::<T>),
474            xSync: Some(rust_sync::<T>),
475            xCommit: Some(rust_commit::<T>),
476            xRollback: Some(rust_rollback::<T>),
477            xFindFunction: Some(rust_find_function::<T>),
478            xRename: None,
479            xSavepoint: None,
480            xRelease: None,
481            xRollbackTo: None,
482            xShadowName: None,
483        },
484        phantom: PhantomData::<&'vtab T>,
485    };
486    let cname = CString::new(name)?;
487    let p_app = match aux {
488        Some(aux) => {
489            let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
490            boxed_aux.cast::<c_void>()
491        }
492        None => ptr::null_mut(),
493    };
494    let result = unsafe {
495        sqlite3ext_create_module_v2(
496            db,
497            cname.as_ptr(),
498            &m.base,
499            p_app,
500            Some(destroy_aux::<T::Aux>),
501        )
502    };
503    if result != SQLITE_OKAY {
504        return Err(Error::new(ErrorKind::TableFunction(result)));
505    }
506    Ok(())
507}
508
509pub fn define_virtual_table_writeablex<'vtab, T: VTabWriteable<'vtab> + 'vtab>(
510    db: *mut sqlite3,
511    name: &str,
512    aux: Option<T::Aux>,
513) -> Result<()> {
514    let m = &Module {
515        base: sqlite3_module {
516            iVersion: 2,
517            xCreate: None,
518            xConnect: Some(rust_connect::<T>),
519            xBestIndex: Some(rust_best_index::<T>),
520            xDisconnect: Some(rust_disconnect::<T>),
521            xDestroy: Some(rust_destroy::<T>),
522            xOpen: Some(rust_open::<T>),
523            xClose: Some(rust_close::<T::Cursor>),
524            xFilter: Some(rust_filter::<T::Cursor>),
525            xNext: Some(rust_next::<T::Cursor>),
526            xEof: Some(rust_eof::<T::Cursor>),
527            xColumn: Some(rust_column::<T::Cursor>),
528            xRowid: Some(rust_rowid::<T::Cursor>),
529            xUpdate: Some(rust_update::<T>),
530            xBegin: None,    //Some(rust_begin::<T>),
531            xSync: None,     //Some(rust_sync::<T>),
532            xCommit: None,   //Some(rust_commit::<T>),
533            xRollback: None, //Some(rust_rollback::<T>),
534            xFindFunction: Some(rust_find_function::<T>),
535            xRename: None,
536            xSavepoint: None,
537            xRelease: None,
538            xRollbackTo: None,
539            xShadowName: None,
540        },
541        phantom: PhantomData::<&'vtab T>,
542    };
543    let cname = CString::new(name)?;
544    let p_app = match aux {
545        Some(aux) => {
546            let boxed_aux: *mut T::Aux = Box::into_raw(Box::new(aux));
547            boxed_aux.cast::<c_void>()
548        }
549        None => ptr::null_mut(),
550    };
551    let result = unsafe {
552        sqlite3ext_create_module_v2(
553            db,
554            cname.as_ptr(),
555            &m.base,
556            p_app,
557            Some(destroy_aux::<T::Aux>),
558        )
559    };
560    if result != SQLITE_OKAY {
561        return Err(Error::new(ErrorKind::TableFunction(result)));
562    }
563    Ok(())
564}
565
566pub trait VTab<'vtab>: Sized {
567    type Aux;
568    type Cursor: VTabCursor;
569
570    fn create(
571        db: *mut sqlite3,
572        aux: Option<&Self::Aux>,
573        args: VTabArguments,
574    ) -> Result<(String, Self)> {
575        Self::connect(db, aux, args)
576    }
577
578    fn connect(
579        db: *mut sqlite3,
580        aux: Option<&Self::Aux>,
581        args: VTabArguments,
582    ) -> Result<(String, Self)>;
583
584    fn best_index(&self, info: IndexInfo) -> core::result::Result<(), BestIndexError>;
585
586    fn open(&'vtab mut self) -> Result<Self::Cursor>;
587
588    fn destroy(&self) -> Result<()> {
589        Ok(())
590    }
591}
592
593pub trait VTabWriteable<'vtab>: VTab<'vtab> {
594    fn update(&'vtab mut self, operation: UpdateOperation, p_rowid: *mut i64) -> Result<()>;
595}
596
597pub trait VTabWriteableWithTransactions<'vtab>: VTabWriteable<'vtab> {
598    fn begin(&'vtab mut self) -> Result<()>;
599    fn sync(&'vtab mut self) -> Result<()>;
600    fn commit(&'vtab mut self) -> Result<()>;
601    fn rollback(&'vtab mut self) -> Result<()>;
602}
603
604pub trait VTabWriteableNestedTransactions<'vtab>: VTabWriteable<'vtab> {
605    fn savepoint(&'vtab mut self, id: c_int) -> Result<()>;
606    fn release(&'vtab mut self, id: c_int) -> Result<()>;
607    fn rollback_to(&'vtab mut self, id: c_int) -> Result<()>;
608}
609
610pub trait VTabCursor: Sized {
611    fn filter(
612        &mut self,
613        idx_num: c_int,
614        idx_str: Option<&str>,
615        values: &[*mut sqlite3_value],
616    ) -> Result<()>;
617    fn next(&mut self) -> Result<()>;
618    fn eof(&self) -> bool;
619    fn column(&self, ctx: *mut sqlite3_context, i: c_int) -> Result<()>;
620    fn rowid(&self) -> Result<i64>;
621}
622
623use std::ffi::CStr;
624
625/// Represents all the arguments given to the virtual table implementation
626/// during [xCreate](https://www.sqlite.org/vtab.html#xcreate), from the
627/// underlying `argv`/`argc` strings. Parsed to be more easily readable.
628///
629/// You most likely want to pass in `.arguments` into
630/// [vtab_argparse::parse_argument](crate::vtab_argparse::parse_argument).
631pub struct VTabArguments {
632    /// Name of the module being invoked, the argument in the USING clause.
633    /// Example: `"CREATE VIRTUAL TABLE xxx USING custom_vtab"` would have
634    /// a `module_name` of `"custom_vtab"`.
635    /// Sourced from `argv[0]`
636    pub module_name: String,
637    /// Name of the database where the virtual table will be created,
638    /// typically `"main"` or `"temp"` or another name from an
639    /// [`ATTACH`'ed database](https://www.sqlite.org/lang_attach.html).
640    /// Sourced from `argv[1]`
641    pub database_name: String,
642
643    /// Name of the table being created.
644    /// Example: `"CREATE VIRTUAL TABLE xxx USING custom_vtab"` would
645    /// have a `table_name` of `"xxx"`.
646    /// Sourced from `argv[2]`
647    pub table_name: String,
648    /// The remaining arguments given in the constructor of the virtual
649    /// table, inside `CREATE VIRTUAL TABLE xxx USING custom_vtab(...)`.
650    /// Sourced from `argv[3:]`
651    pub arguments: Vec<String>,
652}
653
654fn c_string_to_string(c: &*const c_char) -> std::result::Result<String, Utf8Error> {
655    let bytes = unsafe { CStr::from_ptr(c.to_owned()).to_bytes() };
656    Ok(std::str::from_utf8(bytes)?.to_string())
657}
658fn process_create_args(
659    argc: c_int,
660    argv: *const *const c_char,
661) -> std::result::Result<VTabArguments, Utf8Error> {
662    let raw_args = unsafe { slice::from_raw_parts(argv, argc as usize) };
663    let mut args = Vec::with_capacity(argc as usize);
664    for arg in raw_args {
665        args.push(c_string_to_string(arg)?);
666    }
667
668    // SQLite guarantees that argv[0-2] will be filled, hence the .expects() -
669    // If SQLite is wrong, then may god save our souls
670    let module_name = args
671        .get(0)
672        .expect("argv[0] should be the name of the module")
673        .to_owned();
674    let database_name = args
675        .get(1)
676        .expect("argv[1] should be the name of the database the module is in")
677        .to_owned();
678    let table_name = args
679        .get(2)
680        .expect("argv[2] should be the name of the virtual table")
681        .to_owned();
682    let arguments = &args[3..];
683
684    Ok(VTabArguments {
685        module_name,
686        database_name,
687        table_name,
688        arguments: arguments.to_vec(),
689    })
690}
691/// <https://www.sqlite.org/vtab.html#the_xcreate_method>
692// TODO set error message properly
693unsafe extern "C" fn rust_create<'vtab, T>(
694    db: *mut sqlite3,
695    aux: *mut c_void,
696    argc: c_int,
697    argv: *const *const c_char,
698    pp_vtab: *mut *mut sqlite3_vtab,
699    err_msg: *mut *mut c_char,
700) -> c_int
701where
702    T: VTab<'vtab>,
703{
704    let aux = aux.cast::<T::Aux>();
705    let args = match process_create_args(argc, argv) {
706        Ok(args) => args,
707        Err(_) => return SQLITE_ERROR,
708    };
709    match T::create(db, aux.as_ref(), args) {
710        Ok((sql, vtab)) => match CString::new(sql) {
711            Ok(c_sql) => {
712                let rc = sqlitex_declare_vtab(db, c_sql.as_ptr());
713                if rc == SQLITE_OKAY {
714                    let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
715                    *pp_vtab = boxed_vtab.cast::<sqlite3_vtab>();
716                    SQLITE_OKAY
717                } else {
718                    rc
719                }
720            }
721            Err(_err) => SQLITE_ERROR,
722        },
723        Err(err) => {
724            if let ErrorKind::Message(msg) = err.kind() {
725                if let Ok(err) = mprintf(msg) {
726                    *err_msg = err;
727                }
728            };
729            err.code()
730        }
731    }
732}
733
734/// <https://www.sqlite.org/vtab.html#the_xconnect_method>
735// TODO set error message properly
736unsafe extern "C" fn rust_connect<'vtab, T>(
737    db: *mut sqlite3,
738    aux: *mut c_void,
739    argc: c_int,
740    argv: *const *const c_char,
741    pp_vtab: *mut *mut sqlite3_vtab,
742    err_msg: *mut *mut c_char,
743) -> c_int
744where
745    T: VTab<'vtab>,
746{
747    let aux = aux.cast::<T::Aux>();
748    let args = match process_create_args(argc, argv) {
749        Ok(args) => args,
750        Err(_) => return SQLITE_ERROR,
751    };
752    match T::connect(db, aux.as_ref(), args) {
753        Ok((sql, vtab)) => match CString::new(sql) {
754            Ok(c_sql) => {
755                let rc = sqlitex_declare_vtab(db, c_sql.as_ptr());
756                if rc == SQLITE_OKAY {
757                    let boxed_vtab: *mut T = Box::into_raw(Box::new(vtab));
758                    *pp_vtab = boxed_vtab.cast::<sqlite3_vtab>();
759                    SQLITE_OKAY
760                } else {
761                    rc
762                }
763            }
764            Err(_err) => SQLITE_ERROR,
765        },
766        Err(err) => {
767            if let ErrorKind::Message(msg) = err.kind() {
768                if let Ok(err) = mprintf(msg) {
769                    *err_msg = err;
770                }
771            };
772            err.code()
773        }
774    }
775}
776
777/// <https://www.sqlite.org/vtab.html#the_xbestindex_method>
778// TODO set error message properly
779unsafe extern "C" fn rust_best_index<'vtab, T>(
780    vtab: *mut sqlite3_vtab,
781    index_info: *mut sqlite3_index_info,
782) -> c_int
783where
784    T: VTab<'vtab>,
785{
786    let vt = vtab.cast::<T>();
787    match (*vt).best_index(IndexInfo { index_info }) {
788        Ok(_) => SQLITE_OKAY,
789        Err(e) => match e {
790            BestIndexError::Constraint => SQLITE_CONSTRAINT,
791            BestIndexError::Error => SQLITE_ERROR,
792        },
793    }
794}
795
796/// <https://www.sqlite.org/vtab.html#the_xdisconnect_method>
797// TODO set error message properly
798unsafe extern "C" fn rust_disconnect<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
799where
800    T: VTab<'vtab>,
801{
802    if vtab.is_null() {
803        return SQLITE_OKAY;
804    }
805    let vtab = vtab.cast::<T>();
806    drop(Box::from_raw(vtab));
807    SQLITE_OKAY
808}
809
810/// <https://www.sqlite.org/vtab.html#the_xdestroy_method>
811// TODO set error message properly
812unsafe extern "C" fn rust_destroy<'vtab, T>(vtab: *mut sqlite3_vtab) -> c_int
813where
814    T: VTab<'vtab>,
815{
816    if vtab.is_null() {
817        return SQLITE_OKAY;
818    }
819    let vt = vtab.cast::<T>();
820    match (*vt).destroy() {
821        Ok(_) => SQLITE_OKAY,
822        Err(err) => err.code(),
823    }
824}
825
826/// <https://www.sqlite.org/vtab.html#the_xopen_method>
827// TODO set error message properly
828unsafe extern "C" fn rust_open<'vtab, T: 'vtab>(
829    vtab: *mut sqlite3_vtab,
830    pp_cursor: *mut *mut sqlite3_vtab_cursor,
831) -> c_int
832where
833    T: VTab<'vtab>,
834{
835    let vt = vtab.cast::<T>();
836    match (*vt).open() {
837        Ok(cursor) => {
838            let boxed_cursor: *mut T::Cursor = Box::into_raw(Box::new(cursor));
839            *pp_cursor = boxed_cursor.cast::<sqlite3_vtab_cursor>();
840            SQLITE_OKAY
841        }
842        Err(err) => err.code(),
843    }
844}
845
846// https://www.sqlite.org/vtab.html#the_xupdate_method
847#[derive(Debug)]
848pub enum UpdateOperation<'a> {
849    Delete(&'a *mut sqlite3_value),
850    Insert {
851        values: &'a [*mut sqlite3_value],
852        rowid: Option<&'a *mut sqlite3_value>,
853    },
854    Update {
855        _values: &'a [*mut sqlite3_value],
856    },
857}
858
859fn determine_update_operation<'a>(
860    argc: c_int,
861    argv: *mut *mut sqlite3_value,
862) -> UpdateOperation<'a> {
863    let args = unsafe { slice::from_raw_parts(argv, argc as usize) };
864
865    // "The value of argc will be 1 for a pure delete operation"
866    if argc == 1 {
867        return UpdateOperation::Delete(
868            args.get(0)
869                .expect("argv[0] should be non-null for DELETE operations"),
870        );
871    }
872
873    let argv0 = args
874        .get(0)
875        .expect("argv[0] should be defined on all non-delete operations");
876    let argv1 = args
877        .get(1)
878        .expect("argv[1] should be defined on all non-delete operations");
879
880    //  argc > 1 AND argv[0] = NULL
881    // "INSERT: A new row is inserted with column values taken from argv[2] and following."
882    if value_type(argv1) == ValueType::Null {
883        let rowid = if value_type(argv0) == ValueType::Null {
884            None
885        } else {
886            Some(argv1)
887        };
888        UpdateOperation::Insert {
889            values: args
890                .get(2..)
891                .expect("argv[0-1] should be defined on INSERT operations"),
892            rowid,
893        }
894    }
895    // argc > 1 AND argv[0] ≠ NULL AND argv[0] = argv[1]
896    // "UPDATE: The row with rowid or PRIMARY KEY argv[0] is updated with new values in argv[2] and following parameters.'
897    else if argv0 == argv1 {
898        UpdateOperation::Update {
899            _values: args
900                .get(2..)
901                .expect("argv[0-1] should be defined on INSERT operations"),
902        }
903    }
904    //argc > 1 AND argv[0] ≠ NULL AND argv[0] ≠ argv[1]
905    // "UPDATE with rowid or PRIMARY KEY change: The row with rowid or PRIMARY KEY argv[0] is updated with
906    // the rowid or PRIMARY KEY in argv[1] and new values in argv[2] and following parameters. "
907    // what the hell does this even mean
908    else if true {
909        todo!();
910    } else {
911        todo!("some unsupported update operation?")
912    }
913}
914/// <https://www.sqlite.org/vtab.html#the_xupdate_method>
915// TODO set error message properly
916unsafe extern "C" fn rust_update<'vtab, T: 'vtab>(
917    vtab: *mut sqlite3_vtab,
918    argc: c_int,
919    argv: *mut *mut sqlite3_value,
920    p_rowid: *mut i64,
921) -> c_int
922where
923    T: VTabWriteable<'vtab>,
924{
925    let vt = vtab.cast::<T>();
926
927    match (*vt).update(determine_update_operation(argc, argv), p_rowid) {
928        Ok(_) => SQLITE_OKAY,
929        Err(err) => err.code(),
930    }
931}
932
933/// <https://www.sqlite.org/vtab.html#the_xbegin_method>
934// TODO set error message properly
935unsafe extern "C" fn rust_begin<'vtab, T: 'vtab>(vtab: *mut sqlite3_vtab) -> c_int
936where
937    T: VTabWriteableWithTransactions<'vtab>,
938{
939    let vt = vtab.cast::<T>();
940    match (*vt).begin() {
941        Ok(_) => SQLITE_OKAY,
942        Err(err) => err.code(),
943    }
944}
945
946/// <https://www.sqlite.org/vtab.html#the_xsync_method>
947// TODO set error message properly
948unsafe extern "C" fn rust_sync<'vtab, T: 'vtab>(vtab: *mut sqlite3_vtab) -> c_int
949where
950    T: VTabWriteableWithTransactions<'vtab>,
951{
952    let vt = vtab.cast::<T>();
953    match (*vt).sync() {
954        Ok(_) => SQLITE_OKAY,
955        Err(err) => err.code(),
956    }
957}
958
959/// <https://www.sqlite.org/vtab.html#the_xrollback_method>
960// TODO set error message properly
961unsafe extern "C" fn rust_rollback<'vtab, T: 'vtab>(vtab: *mut sqlite3_vtab) -> c_int
962where
963    T: VTabWriteableWithTransactions<'vtab>,
964{
965    let vt = vtab.cast::<T>();
966    match (*vt).rollback() {
967        Ok(_) => SQLITE_OKAY,
968        Err(err) => err.code(),
969    }
970}
971
972/// <https://www.sqlite.org/vtab.html#the_xcommit_method>
973// TODO set error message properly
974unsafe extern "C" fn rust_commit<'vtab, T: 'vtab>(vtab: *mut sqlite3_vtab) -> c_int
975where
976    T: VTabWriteableWithTransactions<'vtab>,
977{
978    let vt = vtab.cast::<T>();
979    match (*vt).commit() {
980        Ok(_) => SQLITE_OKAY,
981        Err(err) => err.code(),
982    }
983}
984
985/// <https://www.sqlite.org/vtab.html#the_xfindfunction_method>
986// TODO set error message properly
987unsafe extern "C" fn rust_find_function<'vtab, T: 'vtab>(
988    _vtab: *mut sqlite3_vtab,
989    _n_arg: c_int,
990    _name: *const c_char,
991    _p_xfunc: *mut Option<unsafe extern "C" fn(*mut sqlite3_context, i32, *mut *mut sqlite3_value)>,
992    _p_p_arg: *mut *mut c_void,
993) -> c_int
994where
995    T: VTabWriteable<'vtab>,
996{
997    0
998}
999
1000/// <https://www.sqlite.org/vtab.html#the_xclose_method>
1001// TODO set error message properly
1002unsafe extern "C" fn rust_close<C>(cursor: *mut sqlite3_vtab_cursor) -> c_int
1003where
1004    C: VTabCursor,
1005{
1006    let cr = cursor.cast::<C>();
1007    drop(Box::from_raw(cr));
1008    SQLITE_OKAY
1009}
1010
1011/// <https://www.sqlite.org/vtab.html#the_xfilter_method>
1012// TODO set error message properly
1013unsafe extern "C" fn rust_filter<C>(
1014    cursor: *mut sqlite3_vtab_cursor,
1015    idx_num: c_int,
1016    idx_str: *const c_char,
1017    argc: c_int,
1018    argv: *mut *mut sqlite3_value,
1019) -> c_int
1020where
1021    C: VTabCursor,
1022{
1023    use std::str;
1024    let idx_name = if idx_str.is_null() {
1025        None
1026    } else {
1027        let c_slice = CStr::from_ptr(idx_str).to_bytes();
1028        Some(str::from_utf8_unchecked(c_slice))
1029    };
1030    let cr = cursor.cast::<C>();
1031    //cursor_error(cursor, )
1032    let args = slice::from_raw_parts_mut(argv, argc as usize);
1033    match (*cr).filter(idx_num, idx_name, args) {
1034        Ok(()) => SQLITE_OKAY,
1035        Err(err) => {
1036            if let ErrorKind::Message(msg) = err.kind() {
1037                if let Ok(err) = mprintf(msg) {
1038                    (*(*cursor).pVtab).zErrMsg = err;
1039                }
1040            };
1041            err.code()
1042        }
1043    }
1044}
1045
1046/// <https://www.sqlite.org/vtab.html#the_xnext_method>
1047// TODO set error message properly
1048unsafe extern "C" fn rust_next<C>(cursor: *mut sqlite3_vtab_cursor) -> c_int
1049where
1050    C: VTabCursor,
1051{
1052    let cr = cursor.cast::<C>();
1053    //cursor_error(cursor, (*cr).next())
1054    match (*cr).next() {
1055        Ok(()) => SQLITE_OKAY,
1056        Err(err) => {
1057            if let ErrorKind::Message(msg) = err.kind() {
1058                if let Ok(err) = mprintf(msg) {
1059                    (*(*cursor).pVtab).zErrMsg = err;
1060                }
1061            };
1062            err.code()
1063        }
1064    }
1065}
1066
1067/// <https://www.sqlite.org/vtab.html#the_xeof_method>
1068// TODO set error message properly
1069unsafe extern "C" fn rust_eof<C>(cursor: *mut sqlite3_vtab_cursor) -> c_int
1070where
1071    C: VTabCursor,
1072{
1073    let cr = cursor.cast::<C>();
1074    (*cr).eof() as c_int
1075}
1076
1077/// <https://www.sqlite.org/vtab.html#the_xcolumn_method>
1078// TODO set error message properly
1079unsafe extern "C" fn rust_column<C>(
1080    cursor: *mut sqlite3_vtab_cursor,
1081    ctx: *mut sqlite3_context,
1082    i: c_int,
1083) -> c_int
1084where
1085    C: VTabCursor,
1086{
1087    let cr = cursor.cast::<C>();
1088    //result_error(ctx, (*cr).column(&mut ctxt, i))
1089    match (*cr).column(ctx, i) {
1090        Ok(()) => SQLITE_OKAY,
1091        Err(err) => {
1092            if let ErrorKind::Message(msg) = err.kind() {
1093                if let Ok(err) = mprintf(msg) {
1094                    (*(*cursor).pVtab).zErrMsg = err;
1095                }
1096            };
1097            err.code()
1098        }
1099    }
1100}
1101
1102/// "A successful invocation of this method will cause *pRowid to be filled with the rowid of row
1103/// that the virtual table cursor pCur is currently pointing at.
1104/// This method returns SQLITE_OKAY on success. It returns an appropriate error code on failure."
1105/// <https://www.sqlite.org/vtab.html#the_xrowid_method>
1106// TODO set error message properly
1107unsafe extern "C" fn rust_rowid<C>(cursor: *mut sqlite3_vtab_cursor, p_rowid: *mut i64) -> c_int
1108where
1109    C: VTabCursor,
1110{
1111    let cr = cursor.cast::<C>();
1112    match (*cr).rowid() {
1113        Ok(rowid) => {
1114            *p_rowid = rowid;
1115            SQLITE_OKAY
1116        }
1117        Err(err) => err.code(),
1118    }
1119}