Skip to main content

inillucent_sql/
vtab.rs

1//! What the front end has to know about a virtual table.
2//!
3//! Invariant: this file holds the *data* of the virtual-table contract and none
4//! of its behaviour. The planner has to be able to say "here are the
5//! constraints I can offer and the order I would like", and the catalog has to
6//! be able to hold the columns a module declared, and neither of those can wait
7//! until the layer that owns modules. The traits a module implements live above
8//! this, in `inillucent-ext`, where a pager can be named.
9//!
10//! The split is not bureaucratic: it is what lets a bound statement stay a pure
11//! function of its SQL and one catalog generation. A planner that had to call
12//! into a module to describe a plan would be a planner whose output depended on
13//! run-time state.
14
15use inillucent_base::DbResult;
16use inillucent_value::{Affinity, Value};
17
18/// The comparison a constraint applies.
19///
20/// `Match`, `Like`, `Glob` and `Regexp` are here because an operator a module
21/// understands is the reason virtual tables exist: `t MATCH 'x'` means nothing
22/// to the engine and everything to FTS5.
23#[derive(Clone, Copy, Debug, PartialEq, Eq)]
24pub enum ConstraintOp {
25    /// `=`
26    Eq,
27    /// `>`
28    Gt,
29    /// `<=`
30    Le,
31    /// `<`
32    Lt,
33    /// `>=`
34    Ge,
35    /// `MATCH`
36    Match,
37    /// `LIKE`
38    Like,
39    /// `GLOB`
40    Glob,
41    /// `REGEXP`
42    Regexp,
43    /// `!=`
44    Ne,
45    /// `IS NOT`
46    IsNot,
47    /// `IS NOT NULL`
48    IsNotNull,
49    /// `IS NULL`
50    IsNull,
51    /// `IS`
52    Is,
53}
54
55impl ConstraintOp {
56    /// Returns the number the C surface gives this operator.
57    pub fn code(self) -> i32 {
58        match self {
59            ConstraintOp::Eq => 2,
60            ConstraintOp::Gt => 4,
61            ConstraintOp::Le => 8,
62            ConstraintOp::Lt => 16,
63            ConstraintOp::Ge => 32,
64            ConstraintOp::Match => 64,
65            ConstraintOp::Like => 65,
66            ConstraintOp::Glob => 66,
67            ConstraintOp::Regexp => 67,
68            ConstraintOp::Ne => 68,
69            ConstraintOp::IsNot => 69,
70            ConstraintOp::IsNotNull => 70,
71            ConstraintOp::IsNull => 71,
72            ConstraintOp::Is => 72,
73        }
74    }
75
76    /// Returns whether the operator has a right-hand value to pass on.
77    ///
78    /// `IS NULL` and `IS NOT NULL` do not, which is why they are offered to a
79    /// module without an argument position ever being filled.
80    pub fn has_value(self) -> bool {
81        !matches!(self, ConstraintOp::IsNull | ConstraintOp::IsNotNull)
82    }
83}
84
85/// The column number that names the rowid rather than a declared column.
86pub const ROWID_COLUMN: i32 = -1;
87
88/// One constraint the query offers the module.
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
90pub struct ConstraintSpec {
91    /// Which column, or [`ROWID_COLUMN`].
92    pub column: i32,
93    /// The comparison.
94    pub op: ConstraintOp,
95    /// Whether the value is available at the time this loop runs.
96    ///
97    /// A constraint against a table the loop has not reached yet is offered but
98    /// not usable, which is how one `best_index` answer serves every position
99    /// the term could take in the join order.
100    pub usable: bool,
101}
102
103/// One `ORDER BY` term the query offers the module.
104#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub struct OrderSpec {
106    /// Which column, or [`ROWID_COLUMN`].
107    pub column: i32,
108    /// Whether the term is descending.
109    pub descending: bool,
110}
111
112/// What the module decided to do with one constraint.
113#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
114pub struct ConstraintUsage {
115    /// The one-based position the value is passed to `filter` in, or zero when
116    /// the constraint is not used.
117    pub argument: usize,
118    /// Whether the engine may stop testing this constraint itself.
119    ///
120    /// The module promising, not the engine assuming. A module that sets this
121    /// and then does not apply the constraint returns wrong rows, which is why
122    /// the default is to test it twice.
123    pub omit: bool,
124}
125
126/// The question put to a module's `best_index`, and the answer written back.
127#[derive(Clone, Debug, PartialEq)]
128pub struct IndexQuery {
129    /// The constraints the query can offer.
130    pub constraints: Vec<ConstraintSpec>,
131    /// The ordering the query would like.
132    pub order_by: Vec<OrderSpec>,
133    /// What the module decided about each constraint, in the same order.
134    pub usage: Vec<ConstraintUsage>,
135    /// The plan number the module chose, passed back to `filter`.
136    pub index_number: i32,
137    /// The plan string the module chose, passed back to `filter`.
138    pub index_string: String,
139    /// Whether the rows will already be in the requested order.
140    pub ordered: bool,
141    /// What the module thinks the scan will cost.
142    pub estimated_cost: f64,
143    /// How many rows the module thinks it will produce.
144    pub estimated_rows: i64,
145}
146
147impl IndexQuery {
148    /// Returns a question with every answer at its default.
149    pub fn new(constraints: Vec<ConstraintSpec>, order_by: Vec<OrderSpec>) -> IndexQuery {
150        let usage = vec![ConstraintUsage::default(); constraints.len()];
151        IndexQuery {
152            constraints,
153            order_by,
154            usage,
155            index_number: 0,
156            index_string: String::new(),
157            ordered: false,
158            // SQLite's own default, which is deliberately enormous: a module
159            // that says nothing about cost should not win a join order it has
160            // no claim to.
161            estimated_cost: 5.0e98,
162            estimated_rows: 25,
163        }
164    }
165
166    /// Marks one constraint as used, taking the next argument position.
167    pub fn use_constraint(&mut self, index: usize, omit: bool) -> usize {
168        let next = self
169            .usage
170            .iter()
171            .map(|usage| usage.argument)
172            .max()
173            .unwrap_or(0)
174            .saturating_add(1);
175        if let Some(usage) = self.usage.get_mut(index) {
176            usage.argument = next;
177            usage.omit = omit;
178        }
179        next
180    }
181
182    /// Returns the constraint positions that feed `filter`, in argument order.
183    pub fn argument_order(&self) -> Vec<usize> {
184        let mut claimed: Vec<(usize, usize)> = self
185            .usage
186            .iter()
187            .enumerate()
188            .filter(|(_, usage)| usage.argument > 0)
189            .map(|(index, usage)| (usage.argument, index))
190            .collect();
191        claimed.sort_unstable();
192        claimed.into_iter().map(|(_, index)| index).collect()
193    }
194}
195
196/// What `filter` is told to do.
197#[derive(Clone, Debug)]
198pub struct FilterPlan {
199    /// The plan number `best_index` chose.
200    pub index_number: i32,
201    /// The plan string `best_index` chose.
202    pub index_string: String,
203    /// The constraint values, in the argument order `best_index` assigned.
204    pub arguments: Vec<Value<'static>>,
205}
206
207/// One column a module declares.
208#[derive(Clone, Debug, PartialEq, Eq)]
209pub struct DeclaredColumn {
210    /// The column name.
211    pub name: Vec<u8>,
212    /// The declared type, exactly as the module wrote it.
213    pub declared_type: Vec<u8>,
214    /// The affinity that type maps to.
215    pub affinity: Affinity,
216    /// The folded name of the column's collation.
217    pub collation: Vec<u8>,
218    /// Whether the column is hidden from `SELECT *` and from an `INSERT` with
219    /// no column list.
220    ///
221    /// A hidden column is how a table-valued function takes its arguments:
222    /// `json_each('[1]')` is `SELECT * FROM json_each WHERE json = '[1]'`, and
223    /// `json` is a hidden column. It is the whole mechanism, not a display
224    /// preference.
225    pub hidden: bool,
226}
227
228impl DeclaredColumn {
229    /// Returns an ordinary visible column with no declared type.
230    pub fn visible(name: &str) -> DeclaredColumn {
231        DeclaredColumn {
232            name: name.as_bytes().to_vec(),
233            declared_type: Vec::new(),
234            affinity: Affinity::Blob,
235            collation: b"binary".to_vec(),
236            hidden: false,
237        }
238    }
239
240    /// Returns a hidden column, which is how an argument is declared.
241    pub fn hidden(name: &str) -> DeclaredColumn {
242        DeclaredColumn {
243            hidden: true,
244            ..DeclaredColumn::visible(name)
245        }
246    }
247
248    /// Returns the column with a declared type and the affinity it implies.
249    pub fn typed(mut self, declared: &str) -> DeclaredColumn {
250        self.declared_type = declared.as_bytes().to_vec();
251        self.affinity = inillucent_value::affinity::for_column(declared.as_bytes());
252        self
253    }
254}
255
256/// What a module says its table looks like.
257#[derive(Clone, Debug, PartialEq, Eq)]
258pub struct Declaration {
259    /// The columns, in the order `SELECT *` and `column` number them.
260    pub columns: Vec<DeclaredColumn>,
261    /// Whether the table has no rowid of its own.
262    pub without_rowid: bool,
263}
264
265/// The root page of one shadow table, by the suffix that names it.
266#[derive(Clone, Debug, PartialEq, Eq)]
267pub struct ShadowRoot {
268    /// The suffix after the virtual table's own name, such as `data`.
269    pub suffix: Vec<u8>,
270    /// The root page of the shadow table's b-tree.
271    pub root: u32,
272}
273
274/// Everything a module is handed when it is connected.
275#[derive(Clone, Debug, Default, PartialEq, Eq)]
276pub struct ModuleArguments {
277    /// The database the table lives in.
278    pub database: usize,
279    /// The schema name, for messages.
280    pub schema: Vec<u8>,
281    /// The virtual table's own name.
282    pub table: Vec<u8>,
283    /// The module's name as written.
284    pub module: Vec<u8>,
285    /// The arguments inside the parentheses, as written source slices.
286    pub arguments: Vec<Vec<u8>>,
287    /// The roots of the shadow tables the schema already holds.
288    pub shadows: Vec<ShadowRoot>,
289}
290
291impl ModuleArguments {
292    /// Returns the root page of one shadow table.
293    pub fn shadow(&self, suffix: &[u8]) -> Option<u32> {
294        self.shadows
295            .iter()
296            .find(|shadow| shadow.suffix == suffix)
297            .map(|shadow| shadow.root)
298    }
299}
300
301/// What a module needs created before it can be connected.
302#[derive(Clone, Debug, PartialEq, Eq)]
303pub struct ShadowTable {
304    /// The suffix after the virtual table's own name.
305    pub suffix: Vec<u8>,
306    /// The `CREATE` statement, with `%` standing for the table's own name.
307    pub create_sql: String,
308    /// The table these shadows belong to, when it is not the module's own.
309    ///
310    /// **How a module reads another table's storage.** `fts5vocab(f, 'row')`
311    /// is a view over the index `f` built, and the whole of what it needs is
312    /// read access to `f`'s shadow tables - which the module contract
313    /// deliberately does not give it, because "a module sees only what it was
314    /// handed" is what makes a hostile module a bounded problem.
315    ///
316    /// So it is handed them, explicitly and by name. A module that names an
317    /// owner is asking for shadows that **already exist**: they are looked up
318    /// rather than created, and a name the catalog does not have is a refusal
319    /// rather than a fresh table. The module still sees only the roots it was
320    /// given, and still cannot resolve a name for itself.
321    pub owner: Option<Vec<u8>>,
322}
323
324/// One row a write asks a module to make.
325#[derive(Clone, Debug)]
326pub enum Change {
327    /// Remove the row with this rowid or primary key.
328    Delete(Value<'static>),
329    /// Add a row.
330    Insert {
331        /// The rowid to use, or NULL for one the module chooses.
332        rowid: Value<'static>,
333        /// One value per declared column, hidden columns included.
334        values: Vec<Value<'static>>,
335    },
336    /// Replace a row.
337    Update {
338        /// The row being replaced.
339        old_rowid: Value<'static>,
340        /// The rowid it should have afterwards, which a statement may change.
341        new_rowid: Value<'static>,
342        /// One value per declared column, hidden columns included.
343        values: Vec<Value<'static>>,
344    },
345}
346
347/// The module one virtual table is implemented by.
348#[derive(Clone, Debug, Default, PartialEq, Eq)]
349pub struct ModuleRef {
350    /// The module name as written.
351    pub name: Vec<u8>,
352    /// The ASCII-folded lookup key.
353    pub folded: Vec<u8>,
354    /// The arguments inside the parentheses, as written.
355    pub arguments: Vec<Vec<u8>>,
356}
357
358/// The rows of a module's shadow tables, whatever engine holds them.
359///
360/// **This is the seam the TDD's "shadow tables become ordinary trees" needs.**
361/// FTS5 and the R-Tree keep their whole state in shadow tables and reach them
362/// only through `ShadowTables`, so the modules themselves say nothing about
363/// pages, cursors or b-trees - which is what lets the same module code run over
364/// the old engine's `sqlite_master` b-trees and the new engine's PAX trees. A
365/// module that had reached a pager directly would have to be written twice.
366///
367/// Every method names a *root*, because a module is handed the roots of its own
368/// shadow tables and nothing else. There is no name resolution here and no
369/// catalog: a module that wanted to read somebody else's table would have to be
370/// given it.
371///
372/// The rowid methods are for a rowid table, where the first value of a row *is*
373/// its rowid; the keyed ones are for a `WITHOUT ROWID` table, whose whole row is
374/// its key. FTS5 uses both.
375pub trait ShadowStore {
376    /// Reads one row by rowid, or nothing when there is not one.
377    ///
378    /// @param root - the shadow table's root
379    /// @param rowid - the row's key
380    fn read_row(&mut self, root: u32, rowid: i64) -> DbResult<Option<Vec<Value<'static>>>>;
381
382    /// Writes one row by rowid, replacing whatever was there.
383    ///
384    /// @param root - the shadow table's root
385    /// @param rowid - the row's key
386    /// @param values - the row, its rowid first
387    fn write_row(&mut self, root: u32, rowid: i64, values: &[Value<'static>]) -> DbResult<()>;
388
389    /// Removes one row by rowid, reporting nothing when there was not one.
390    ///
391    /// @param root - the shadow table's root
392    /// @param rowid - the row's key
393    fn delete_row(&mut self, root: u32, rowid: i64) -> DbResult<()>;
394
395    /// Returns the largest rowid one shadow table holds.
396    ///
397    /// @param root - the shadow table's root
398    fn max_rowid(&mut self, root: u32) -> DbResult<i64>;
399
400    /// Runs a body over every row, in rowid order, stopping when it says so.
401    ///
402    /// @param root - the shadow table's root
403    /// @param body - what to do with each row
404    fn scan(
405        &mut self,
406        root: u32,
407        body: &mut dyn FnMut(i64, &[Value<'static>]) -> DbResult<bool>,
408    ) -> DbResult<()>;
409
410    /// Runs a body over every row whose rowid is at least `from`, in rowid
411    /// order, stopping when it says so.
412    ///
413    /// **A seek, not a scan with a filter, when an implementor has one.** A
414    /// rowid table's rows are already in key order, so a caller that only
415    /// wants what is above a watermark - a delta log's `deltas_above`, chief
416    /// among them - does not need every row below it decoded and thrown
417    /// away; it needs the store to descend to `from` once and walk right
418    /// from there.
419    ///
420    /// **The default is correct rather than fast, and that is deliberate.**
421    /// It is [`Self::scan`] with a callback that skips what is below `from`,
422    /// which costs the whole table exactly as a hand-written filter would -
423    /// so an implementor with no cheap way to position by key is still right
424    /// by doing nothing, and one that can descend directly to a key
425    /// overrides this with that descent. Every rowid tree the new engine
426    /// keeps can; the retired engine's b-trees, reached only from the
427    /// differential suites that still exercise it, are left on the default
428    /// because a seek there is not worth building for a store on its way out.
429    ///
430    /// @param root - the shadow table's root
431    /// @param from - the smallest rowid to visit
432    /// @param body - what to do with each row
433    fn scan_from(
434        &mut self,
435        root: u32,
436        from: i64,
437        body: &mut dyn FnMut(i64, &[Value<'static>]) -> DbResult<bool>,
438    ) -> DbResult<()> {
439        self.scan(root, &mut |rowid, values| {
440            if rowid < from {
441                return Ok(true);
442            }
443            body(rowid, values)
444        })
445    }
446
447    /// Reads one row of a keyed shadow table, or nothing when there is not one.
448    ///
449    /// @param root - the shadow table's root
450    /// @param key - the leading columns that identify it
451    /// @param columns - how many columns to return, `usize::MAX` for all
452    fn read_keyed(
453        &mut self,
454        root: u32,
455        key: &[Value<'static>],
456        columns: usize,
457    ) -> DbResult<Option<Vec<Value<'static>>>>;
458
459    /// Writes one row of a keyed shadow table, replacing whatever was there.
460    ///
461    /// @param root - the shadow table's root
462    /// @param key_columns - how many leading columns form the key
463    /// @param values - the whole row
464    fn write_keyed(
465        &mut self,
466        root: u32,
467        key_columns: usize,
468        values: &[Value<'static>],
469    ) -> DbResult<()>;
470
471    /// Removes one row of a keyed shadow table.
472    ///
473    /// @param root - the shadow table's root
474    /// @param key - the leading columns that identify it
475    fn delete_keyed(&mut self, root: u32, key: &[Value<'static>]) -> DbResult<()>;
476
477    /// Runs a body over every row of a keyed shadow table, in key order.
478    ///
479    /// @param root - the shadow table's root
480    /// @param key_columns - how many leading columns form the key
481    /// @param body - what to do with each row
482    fn scan_keyed(
483        &mut self,
484        root: u32,
485        key_columns: usize,
486        body: &mut dyn FnMut(&[Value<'static>]) -> DbResult<bool>,
487    ) -> DbResult<()>;
488
489    /// Runs a body over every row of a keyed shadow table whose key sorts at
490    /// or after `from`, in key order, stopping when it says so.
491    ///
492    /// **The keyed twin of [`Self::scan_from`], for a key of more than one
493    /// column.** FTS5's `%_idx` is keyed `(segid, term)`, so a caller that
494    /// wants one segment's terms starting at a prefix - a term-major seek to
495    /// `(segid, prefix)`, once per live segment - needs to position by a key
496    /// that is not the whole row, the same shape a rowid seek already had and
497    /// a single-column keyed seek would not need a new method for.
498    ///
499    /// **The default is correct rather than fast, and that is deliberate**,
500    /// for the reason [`Self::scan_from`]'s default is: it is
501    /// [`Self::scan_keyed`] with a callback that skips whatever sorts below
502    /// `from`, so an implementor with no cheap way to position by key is
503    /// still right by doing nothing, and one that can descend directly to a
504    /// key overrides this with that descent.
505    ///
506    /// @param root - the shadow table's root
507    /// @param key_columns - how many leading columns form the key
508    /// @param from - the key to start at, compared column by column
509    /// @param body - what to do with each row
510    fn scan_keyed_from(
511        &mut self,
512        root: u32,
513        key_columns: usize,
514        from: &[Value<'static>],
515        body: &mut dyn FnMut(&[Value<'static>]) -> DbResult<bool>,
516    ) -> DbResult<()> {
517        self.scan_keyed(root, key_columns, &mut |values| {
518            if key_sorts_below(values, from) {
519                return Ok(true);
520            }
521            body(values)
522        })
523    }
524}
525
526/// Returns whether a keyed row's leading columns sort before `from`, compared
527/// column by column under [`inillucent_value::compare::compare_values`] with
528/// `BINARY` collation - what every shadow table's key compares under, since
529/// none of them declares a column collation of its own.
530///
531/// Shared by [`ShadowStore::scan_keyed_from`]'s default implementation and by
532/// a real implementor's seek, which still has to discard whatever a leaf
533/// below the seek's target key holds - `PagedTree::visit_range` positions at
534/// the leaf that *could* hold the key, not necessarily past everything
535/// smaller than it.
536///
537/// @param row - the row read back
538/// @param from - the key a caller asked to start at
539pub fn key_sorts_below(row: &[Value<'static>], from: &[Value<'static>]) -> bool {
540    use std::cmp::Ordering;
541    for (left, right) in row.iter().zip(from.iter()) {
542        match inillucent_value::compare::compare_values(
543            left,
544            right,
545            inillucent_value::Collation::Binary,
546        ) {
547            Ordering::Less => return true,
548            Ordering::Greater => return false,
549            Ordering::Equal => continue,
550        }
551    }
552    false
553}
554
555#[cfg(test)]
556mod tests {
557    use super::*;
558
559    /// The operator numbers are the ones the C surface publishes, because a
560    /// module written against the header compares against these constants.
561    #[test]
562    fn operator_codes_match_the_published_constants() {
563        assert_eq!(ConstraintOp::Eq.code(), 2);
564        assert_eq!(ConstraintOp::Gt.code(), 4);
565        assert_eq!(ConstraintOp::Le.code(), 8);
566        assert_eq!(ConstraintOp::Lt.code(), 16);
567        assert_eq!(ConstraintOp::Ge.code(), 32);
568        assert_eq!(ConstraintOp::Match.code(), 64);
569        assert_eq!(ConstraintOp::Is.code(), 72);
570    }
571
572    /// Argument positions are handed out in the order they are claimed, and
573    /// read back in that same order.
574    #[test]
575    fn argument_positions_are_claimed_in_order() {
576        let mut query = IndexQuery::new(
577            vec![
578                ConstraintSpec {
579                    column: 0,
580                    op: ConstraintOp::Eq,
581                    usable: true,
582                },
583                ConstraintSpec {
584                    column: 1,
585                    op: ConstraintOp::Gt,
586                    usable: true,
587                },
588            ],
589            Vec::new(),
590        );
591        assert_eq!(query.use_constraint(1, true), 1);
592        assert_eq!(query.use_constraint(0, false), 2);
593        assert_eq!(query.argument_order(), vec![1, 0]);
594        assert!(query.usage[1].omit);
595        assert!(!query.usage[0].omit);
596    }
597
598    /// A module that says nothing about cost must not win a join order.
599    #[test]
600    fn the_default_cost_is_deliberately_enormous() {
601        let query = IndexQuery::new(Vec::new(), Vec::new());
602        assert!(query.estimated_cost > 1.0e90);
603    }
604
605    /// The two null tests carry no value, so nothing is passed for them.
606    #[test]
607    fn the_null_tests_carry_no_value() {
608        assert!(!ConstraintOp::IsNull.has_value());
609        assert!(!ConstraintOp::IsNotNull.has_value());
610        assert!(ConstraintOp::Eq.has_value());
611    }
612}