Skip to main content

sqlite3_ext/vtab/
index_info.rs

1use crate::{ffi, sqlite3_match_version, sqlite3_require_version, types::*, value::*};
2use std::{ffi::CStr, ptr};
3
4/// Information about a query plan.
5///
6/// This struct contains all of the information about a query plan that SQLite is attempting on
7/// the virtual table. The struct will be passed to
8/// [VTab::best_index](super::VTab::best_index).
9///
10/// This struct is both an input and an output. The virtual table implementation should examine
11/// the [constraints](Self::constraints) and [order_by](Self::order_by) fields, decide on the
12/// best query plan, and then set the results using [IndexInfoConstraint::set_argv_index],
13/// [set_estimated_cost](Self::set_estimated_cost), and the other methods.
14#[repr(transparent)]
15pub struct IndexInfo {
16    base: ffi::sqlite3_index_info,
17}
18
19impl IndexInfo {
20    pub fn constraints(&self) -> IndexInfoConstraintIterator {
21        IndexInfoConstraintIterator::new(self)
22    }
23
24    pub fn order_by(&self) -> IndexInfoOrderByIterator {
25        IndexInfoOrderByIterator::new(self)
26    }
27
28    /// Determine if a query is DISTINCT.
29    ///
30    /// Requires SQLite 3.38.0. On earlier versions, this function will always return
31    /// [DistinctMode::Ordered].
32    pub fn distinct_mode(&self) -> DistinctMode {
33        sqlite3_match_version! {
34            3_038_000 => {
35                let ret = unsafe { ffi::sqlite3_vtab_distinct(&self.base as *const _ as _) };
36                DistinctMode::from_sqlite(ret)
37            },
38            _ => DistinctMode::Ordered,
39        }
40    }
41
42    /// Retrieve the value previously set by
43    /// [set_index_num](Self::set_index_num).
44    pub fn index_num(&self) -> i32 {
45        self.base.idxNum
46    }
47
48    /// Set the index number of this query plan. This is an arbitrary value which will be
49    /// passed to [VTabCursor::filter](super::VTabCursor::filter).
50    pub fn set_index_num(&mut self, val: i32) {
51        self.base.idxNum = val;
52    }
53
54    /// Retrieve the value previously set by
55    /// [set_index_str](Self::set_index_str).
56    pub fn index_str(&self) -> Option<&str> {
57        if self.base.idxStr.is_null() {
58            None
59        } else {
60            let cstr = unsafe { CStr::from_ptr(self.base.idxStr) };
61            cstr.to_str().ok()
62        }
63    }
64
65    /// Set the index string of this query plan. This is an arbitrary value which will be
66    /// passed to [VTabCursor::filter](super::VTabCursor::filter).
67    ///
68    /// This function can fail if SQLite is not able to allocate memory for the string.
69    pub fn set_index_str(&mut self, val: Option<&str>) -> Result<()> {
70        if self.base.needToFreeIdxStr != 0 {
71            unsafe { ffi::sqlite3_free(self.base.idxStr as _) };
72        }
73        match val {
74            None => {
75                self.base.idxStr = ptr::null_mut();
76                self.base.needToFreeIdxStr = 0;
77            }
78            Some(x) => {
79                self.base.idxStr = ffi::str_to_sqlite3(x)?;
80                self.base.needToFreeIdxStr = 1;
81            }
82        }
83        Ok(())
84    }
85
86    /// Set the index string without copying.
87    pub fn set_index_str_static(&mut self, val: &'static CStr) {
88        if self.base.needToFreeIdxStr != 0 {
89            unsafe { ffi::sqlite3_free(self.base.idxStr as _) };
90        }
91        self.base.idxStr = val.as_ptr() as _;
92        self.base.needToFreeIdxStr = 0;
93    }
94
95    /// Retrieve the value previously set by
96    /// [set_order_by_consumed](Self::set_order_by_consumed).
97    pub fn order_by_consumed(&self) -> bool {
98        self.base.orderByConsumed != 0
99    }
100
101    /// Indicate that the virtual table fully understands the requirements of the
102    /// [order_by](Self::order_by) and [distinct_mode](Self::distinct_mode) fields. If this
103    /// is the case, then SQLite can omit reordering the results of the query, which may
104    /// improve performance. It is never necessary to use the order_by information, but
105    /// virtual tables may opt to use it as a performance optimization.
106    pub fn set_order_by_consumed(&mut self, val: bool) {
107        self.base.orderByConsumed = val as _;
108    }
109
110    /// Retrieve the value previously set by
111    /// [set_estimated_cost](Self::set_estimated_cost).
112    pub fn estimated_cost(&self) -> f64 {
113        self.base.estimatedCost
114    }
115
116    pub fn set_estimated_cost(&mut self, val: f64) {
117        self.base.estimatedCost = val;
118    }
119
120    /// Retrieve the value previously set by
121    /// [set_estimated_rows](Self::set_estimated_rows).
122    ///
123    /// Requires SQLite 3.8.2.
124    pub fn estimated_rows(&self) -> Result<i64> {
125        sqlite3_require_version!(3_008_002, Ok(self.base.estimatedRows))
126    }
127
128    /// Requires SQLite 3.8.2. On earlier versions of SQLite, this function is a harmless
129    /// no-op.
130    pub fn set_estimated_rows(&mut self, val: i64) {
131        let _ = val;
132        sqlite3_match_version! {
133            3_008_220 => self.base.estimatedRows = val,
134            _ => (),
135        }
136    }
137
138    /// Retrieve the value previously set by
139    /// [set_scan_flags](Self::set_scan_flags).
140    ///
141    /// Requires SQLite 3.9.0.
142    pub fn scan_flags(&self) -> Result<usize> {
143        sqlite3_require_version!(3_009_000, Ok(self.base.idxFlags as _))
144    }
145
146    /// Requires SQLite 3.9.0. On earlier versions of SQLite, this function is a harmless
147    /// no-op.
148    pub fn set_scan_flags(&mut self, val: usize) -> () {
149        let _ = val;
150        sqlite3_match_version! {
151            3_009_000 => self.base.idxFlags = val as _,
152            _ => (),
153        }
154    }
155
156    /// Requires SQLite 3.10.0.
157    pub fn columns_used(&self) -> Result<u64> {
158        sqlite3_require_version!(3_010_000, Ok(self.base.colUsed))
159    }
160}
161
162#[derive(Copy, Clone)]
163pub struct IndexInfoConstraint<'a> {
164    index_info: &'a IndexInfo,
165    position: usize,
166}
167
168impl IndexInfoConstraint<'_> {
169    fn constraint(&self) -> &ffi::sqlite3_index_info_sqlite3_index_constraint {
170        unsafe { &*self.index_info.base.aConstraint.offset(self.position as _) }
171    }
172
173    fn usage(&self) -> &mut ffi::sqlite3_index_info_sqlite3_index_constraint_usage {
174        unsafe {
175            &mut *self
176                .index_info
177                .base
178                .aConstraintUsage
179                .offset(self.position as _)
180        }
181    }
182
183    /// Return the column being constrained. The value is a 0-based index of columns as declared by
184    /// [connect](super::VTab::connect) / [create](super::CreateVTab::create). The rowid column is
185    /// index -1.
186    pub fn column(&self) -> i32 {
187        self.constraint().iColumn as _
188    }
189
190    /// Return the type of constraint.
191    pub fn op(&self) -> ConstraintOp {
192        ConstraintOp::from_sqlite(self.constraint().op)
193    }
194
195    /// [IndexInfo::constraints] contains information about all constraints that apply to
196    /// the virtual table, but some of the constraints might not be usable because of the
197    /// way tables are ordered in a join. The best_index method must therefore only
198    /// consider constraints that for which this method returns true.
199    pub fn usable(&self) -> bool {
200        self.constraint().usable != 0
201    }
202
203    /// Returns the right-hand side of the constraint.
204    ///
205    /// This routine attempts to retrieve the value of the right-hand operand of the constraint if
206    /// that operand is known. If the operand is not known, then Err([SQLITE_NOTFOUND]) is returned.
207    /// This method can return another error type if something goes wrong.
208    ///
209    /// This method is usually only successful if the right-hand operand of a constraint is a
210    /// literal value in the original SQL statement. If the right-hand operand is an expression or
211    /// a reference to some other column or a host parameter, then this method will probably return
212    /// Err(SQLITE_NOTFOUND).
213    ///
214    /// Some constraints, such as [ConstraintOp::IsNull], have no right-hand operand. For such
215    /// constraints, this method always returns Err(SQLITE_NOTFOUND).
216    ///
217    /// Requires SQLite 3.38.0. On earlier versions of SQLite, Err(SQLITE_NOTFOUND) is always
218    /// returned.
219    pub fn rhs(&self) -> Result<&ValueRef> {
220        sqlite3_match_version! {
221            3_038_000 => unsafe {
222                let mut ret: *mut ffi::sqlite3_value = ptr::null_mut();
223                Error::from_sqlite(ffi::sqlite3_vtab_rhs_value(
224                    &self.index_info.base as *const _ as _,
225                    self.position as _,
226                    &mut ret,
227                ))?;
228                Ok(&*(ret as *const crate::value::ValueRef))
229            },
230            _ => Err(SQLITE_NOTFOUND),
231        }
232    }
233
234    /// Return the collation to use for text comparisons on this column.
235    ///
236    /// See [the SQLite documentation](https://www.sqlite.org/c3ref/vtab_collation.html)
237    /// for more details.
238    pub fn collation(&self) -> Result<&str> {
239        sqlite3_require_version!(3_022_000, {
240            let ret = unsafe {
241                CStr::from_ptr(ffi::sqlite3_vtab_collation(
242                    &self.index_info.base as *const _ as _,
243                    self.position as _,
244                ))
245            };
246            Ok(ret.to_str()?)
247        })
248    }
249
250    /// Retrieve the value previously set using [set_argv_index](Self::set_argv_index).
251    pub fn argv_index(&self) -> Option<u32> {
252        match self.usage().argvIndex {
253            0 => None,
254            x => Some((x - 1) as _),
255        }
256    }
257
258    /// Set the desired index for [filter](super::VTabCursor::filter)'s argv.
259    ///
260    /// Exactly one entry in the IndexInfo should be set to 0, another to 1, another to 2,
261    /// and so forth up to as many or as few as the best_index method wants. The EXPR of
262    /// the corresponding constraints will then be passed in as the argv[] parameters to
263    /// filter.
264    pub fn set_argv_index(&mut self, idx: Option<u32>) {
265        self.usage().argvIndex = match idx {
266            None => 0,
267            Some(i) => (i + 1) as _,
268        };
269    }
270
271    /// Retrieve the value previously set by [set_omit](Self::set_omit).
272    pub fn omit(&self) -> bool {
273        self.usage().omit != 0
274    }
275
276    /// Advise SQLite that this constraint is validated by the virtual table
277    /// implementation. SQLite may skip performing its own check in some cases. It is
278    /// generally a hint and not a requirement, but a notable exception is for
279    /// [ConstraintOp::Offset], which is always honored. See [the SQLite
280    /// documentation](https://www.sqlite.org/vtab.html#omit_constraint_checking_in_bytecode)
281    /// for more details.
282    pub fn set_omit(&mut self, val: bool) {
283        self.usage().omit = val as _;
284    }
285
286    /// Check if all values in this IN constraint are able to be processed simultaneously.
287    /// If this method returns true, then a call to
288    /// [set_value_list_wanted](Self::set_value_list_wanted) would also return true.
289    ///
290    /// Requires SQLite 3.38.0. On earlier versions, this function will always return
291    /// false.
292    pub fn value_list_available(&self) -> bool {
293        sqlite3_match_version! {
294            3_038_000 => unsafe {
295                ffi::sqlite3_vtab_in(
296                    &self.index_info.base as *const _ as _,
297                    self.position as _,
298                    -1,
299                ) != 0
300            },
301            _ => false,
302        }
303    }
304
305    /// Instruct SQLite to return all values in an IN constraint simultaneously.
306    ///
307    /// A constraint on a virtual table in the form of "column IN (...)" is communicated to
308    /// [VTab::best_index](super::VTab::best_index) as a [ConstraintOp::Eq] constraint. If
309    /// the virtual table wants to use this constraint, it must use
310    /// [set_argv_index](Self::set_argv_index) to assign the constraint to an argument.
311    /// Then, SQLite will invoke [VTabCursor::filter](super::VTabCursor::filter) once for
312    /// each value on the right-hand side of the IN operator. Thus, the virtual table only
313    /// sees a single value from the right-hand side of the IN operator at a time.
314    ///
315    /// In some cases, however, it would be advantageous for the virtual table to see all values on the right-hand of the IN operator all at once. This method enables this feature.
316    ///
317    /// Calling this method with true will request [ValueList](crate::ValueList)
318    /// processing. In order for ValueList processing to work:
319    ///
320    /// 1. this constraint must be assigned an argv index using
321    /// [set_argv_index](Self::set_argv_index);
322    /// 2. this method is called with true; and
323    /// 3. SQLite is able to provide all values simultaneously.
324    ///
325    /// If all of these criteria are met, then the corresponding argument passed to
326    /// [VTabCursor::filter](super::VTabCursor::filter) will appear to be SQL NULL, but
327    /// accessible using [ValueList](crate::ValueList). If this facility is requested but
328    /// this method returns false, then VTabCursor::filter will be invoked multiple times
329    /// with each different value of the constraint, as normal.
330    ///
331    /// This method always returns the same value that
332    /// [value_list_available](Self::value_list_available) would. Calling this method with
333    /// false cancels a previous request for a ValueList.
334    ///
335    /// See [the SQLite documentation](https://www.sqlite.org/c3ref/vtab_in.html) for more
336    /// details.
337    ///
338    /// Requires SQLite 3.38.0. On earlier versions, this function will always return
339    /// false.
340    pub fn set_value_list_wanted(&mut self, val: bool) -> bool {
341        let _ = val;
342        sqlite3_match_version! {
343            3_038_000 => unsafe {
344                ffi::sqlite3_vtab_in(
345                    &self.index_info.base as *const _ as _,
346                    self.position as _,
347                    if val { 1 } else { 0 },
348                ) != 0
349            },
350            _ => false,
351        }
352    }
353}
354
355#[derive(Copy, Clone)]
356pub struct IndexInfoOrderBy<'a> {
357    index_info: &'a IndexInfo,
358    position: usize,
359}
360
361impl IndexInfoOrderBy<'_> {
362    fn base(&self) -> &ffi::sqlite3_index_info_sqlite3_index_orderby {
363        unsafe { &*self.index_info.base.aOrderBy.offset(self.position as _) }
364    }
365
366    pub fn column(&self) -> i32 {
367        self.base().iColumn as _
368    }
369
370    pub fn desc(&self) -> bool {
371        self.base().desc != 0
372    }
373}
374
375macro_rules! make_iterator(
376    ($t:ident, $n:ident) => {
377        paste::paste! {
378            pub struct [<$t Iterator>]<'a> {
379                current: $t<'a>,
380            }
381
382            impl<'a> [<$t Iterator>]<'a> {
383                fn new(index_info: &'a IndexInfo) -> Self {
384                    Self {
385                        current: $t {
386                            index_info,
387                            position: usize::MAX,
388                        },
389                    }
390                }
391            }
392
393            impl<'a> Iterator for [<$t Iterator>]<'a> {
394                type Item = $t<'a>;
395
396                fn next(&mut self) -> Option<Self::Item> {
397                    let pos = self.current.position.wrapping_add(1);
398                    if pos < self.current.index_info.base.$n as usize {
399                        self.current.position = pos;
400                        Some(self.current)
401                    } else {
402                        None
403                    }
404                }
405
406                fn size_hint(&self) -> (usize, Option<usize>) {
407                    let remaining = self.current.index_info.base.$n as usize
408                        - self.current.position.wrapping_add(1);
409                    (remaining, Some(remaining))
410                }
411            }
412        }
413    }
414);
415
416make_iterator!(IndexInfoConstraint, nConstraint);
417make_iterator!(IndexInfoOrderBy, nOrderBy);
418
419#[derive(Debug, Eq, PartialEq, Clone, Copy)]
420pub enum ConstraintOp {
421    Eq,
422    GT,
423    LE,
424    LT,
425    GE,
426    Match,
427    Like,         /* 3.10.0 and later */
428    Glob,         /* 3.10.0 and later */
429    Regexp,       /* 3.10.0 and later */
430    NE,           /* 3.21.0 and later */
431    IsNot,        /* 3.21.0 and later */
432    IsNotNull,    /* 3.21.0 and later */
433    IsNull,       /* 3.21.0 and later */
434    Is,           /* 3.21.0 and later */
435    Limit,        /* 3.38.0 and later */
436    Offset,       /* 3.38.0 and later */
437    Function(u8), /* 3.25.0 and later */
438}
439
440impl ConstraintOp {
441    pub(crate) fn assert_valid_function_constraint(&self) {
442        if let ConstraintOp::Function(val) = *self {
443            if val >= 150 {
444                return;
445            }
446        }
447        panic!("invalid function constraint")
448    }
449
450    fn from_sqlite(val: u8) -> ConstraintOp {
451        match val as _ {
452            2 => ConstraintOp::Eq,
453            4 => ConstraintOp::GT,
454            8 => ConstraintOp::LE,
455            16 => ConstraintOp::LT,
456            32 => ConstraintOp::GE,
457            64 => ConstraintOp::Match,
458            65 => ConstraintOp::Like,
459            66 => ConstraintOp::Glob,
460            67 => ConstraintOp::Regexp,
461            68 => ConstraintOp::NE,
462            69 => ConstraintOp::IsNot,
463            70 => ConstraintOp::IsNotNull,
464            71 => ConstraintOp::IsNull,
465            72 => ConstraintOp::Is,
466            73 => ConstraintOp::Limit,
467            74 => ConstraintOp::Offset,
468            150..=255 => ConstraintOp::Function(val),
469            _ => panic!("invalid constraint op"),
470        }
471    }
472}
473
474/// Describes the requirements of the virtual table query.
475///
476/// This value is retured by [IndexInfo::distinct_mode]. It allows the virtual table
477/// implementation to decide if it is safe to consume the [order_by](IndexInfo::order_by)
478/// fields using [IndexInfo::set_order_by_consumed].
479///
480/// The levels described here are progressively less demanding. If the virtual table
481/// implementation meets the requirements of [DistinctMode::Ordered], then it is always safe to
482/// consume the order_by fields.
483///
484/// For the purposes of comparing virtual table output values to see if the values are same
485/// value for sorting purposes, two NULL values are considered to be the same. In other words,
486/// the comparison operator is "IS" (or "IS NOT DISTINCT FROM") and not "==".
487#[derive(Debug, Eq, PartialEq, Clone, Copy)]
488pub enum DistinctMode {
489    /// The virtual table must return all rows in the correct order according to the
490    /// [order_by](IndexInfo::order_by) fields.
491    Ordered,
492    /// The virtual table may return rows in any order, but all rows that are in the same
493    /// group must be adjacent to one another. A group is defined as all rows for which all
494    /// of the columns in [order_by](IndexInfo::order_by) are equal. This is the mode used
495    /// when planning a GROUP BY query.
496    Grouped,
497    /// The same as [DistinctMode::Grouped], however the virtual table is allowed (but not
498    /// required) to skip all but a single row within each group. This is the mode used
499    /// when planning a DISTINCT query.
500    Distinct,
501}
502
503impl DistinctMode {
504    #[cfg(modern_sqlite)]
505    fn from_sqlite(val: i32) -> Self {
506        match val {
507            0 => Self::Ordered,
508            1 => Self::Grouped,
509            2 => Self::Distinct,
510            _ => panic!("invalid distinct mode"),
511        }
512    }
513}
514
515impl std::fmt::Debug for IndexInfo {
516    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
517        let mut ds = f.debug_struct("IndexInfo");
518        ds.field(
519            "constraints",
520            &self.constraints().collect::<Vec<_>>().as_slice(),
521        )
522        .field("order_by", &self.order_by().collect::<Vec<_>>().as_slice())
523        .field("index_num", &self.index_num())
524        .field("index_str", &self.index_str())
525        .field("order_by_consumed", &self.order_by_consumed())
526        .field("estimated_cost", &self.estimated_cost());
527        self.estimated_rows()
528            .map(|v| ds.field("estimated_rows", &v))
529            .ok();
530        self.scan_flags().map(|v| ds.field("scan_flags", &v)).ok();
531        self.columns_used()
532            .map(|v| ds.field("columns_used", &v))
533            .ok();
534        ds.finish()
535    }
536}
537
538impl std::fmt::Debug for IndexInfoConstraint<'_> {
539    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
540        let mut ds = f.debug_struct("IndexInfoConstraint");
541        ds.field("column", &self.column())
542            .field("op", &self.op())
543            .field("usable", &self.usable());
544        sqlite3_match_version! {
545            3_038_000 => {
546                ds.field("rhs", &self.rhs());
547            }
548            _ => (),
549        }
550        sqlite3_match_version! {
551            3_022_000 => {
552                ds.field("collation", &self.collation());
553            }
554            _ => (),
555        }
556        ds.field("argv_index", &self.argv_index())
557            .field("omit", &self.omit())
558            .finish()
559    }
560}
561
562impl std::fmt::Debug for IndexInfoOrderBy<'_> {
563    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
564        f.debug_struct("IndexInfoOrderBy")
565            .field("column", &self.column())
566            .field("desc", &self.desc())
567            .finish()
568    }
569}