series/
series.rs

1//! cargo build --example series
2//! sqlite3 :memory: '.read examples/test.sql'
3
4use sqlite_loadable::prelude::*;
5use sqlite_loadable::{
6    api, define_table_function,
7    table::{BestIndexError, ConstraintOperator, IndexInfo, VTab, VTabArguments, VTabCursor},
8    Result,
9};
10
11use std::{mem, os::raw::c_int};
12
13static CREATE_SQL: &str = "CREATE TABLE x(value, start hidden, stop hidden, step hidden)";
14enum Columns {
15    Value,
16    Start,
17    Stop,
18    Step,
19}
20fn column(index: i32) -> Option<Columns> {
21    match index {
22        0 => Some(Columns::Value),
23        1 => Some(Columns::Start),
24        2 => Some(Columns::Stop),
25        3 => Some(Columns::Step),
26        _ => None,
27    }
28}
29
30#[repr(C)]
31pub struct GenerateSeriesTable {
32    /// must be first
33    base: sqlite3_vtab,
34}
35
36impl<'vtab> VTab<'vtab> for GenerateSeriesTable {
37    type Aux = ();
38    type Cursor = GenerateSeriesCursor;
39
40    fn connect(
41        _db: *mut sqlite3,
42        _aux: Option<&Self::Aux>,
43        _args: VTabArguments,
44    ) -> Result<(String, GenerateSeriesTable)> {
45        let base: sqlite3_vtab = unsafe { mem::zeroed() };
46        let vtab = GenerateSeriesTable { base };
47        // TODO db.config(VTabConfig::Innocuous)?;
48        Ok((CREATE_SQL.to_owned(), vtab))
49    }
50    fn destroy(&self) -> Result<()> {
51        Ok(())
52    }
53
54    fn best_index(&self, mut info: IndexInfo) -> core::result::Result<(), BestIndexError> {
55        let mut has_start = false;
56        let mut has_stop = false;
57        for mut constraint in info.constraints() {
58            match column(constraint.column_idx()) {
59                Some(Columns::Start) => {
60                    if constraint.usable() && constraint.op() == Some(ConstraintOperator::EQ) {
61                        constraint.set_omit(true);
62                        constraint.set_argv_index(1);
63                        has_start = true;
64                    } else {
65                        return Err(BestIndexError::Constraint);
66                    }
67                }
68                Some(Columns::Stop) => {
69                    if constraint.usable() && constraint.op() == Some(ConstraintOperator::EQ) {
70                        constraint.set_omit(true);
71                        constraint.set_argv_index(2);
72                        has_stop = true;
73                    } else {
74                        return Err(BestIndexError::Constraint);
75                    }
76                }
77                _ => todo!(),
78            }
79        }
80        if !has_start || !has_stop {
81            return Err(BestIndexError::Error);
82        }
83        info.set_estimated_cost(100000.0);
84        info.set_estimated_rows(100000);
85        info.set_idxnum(1);
86
87        Ok(())
88    }
89
90    fn open(&mut self) -> Result<GenerateSeriesCursor> {
91        Ok(GenerateSeriesCursor::new())
92    }
93}
94
95#[repr(C)]
96pub struct GenerateSeriesCursor {
97    /// Base class. Must be first
98    base: sqlite3_vtab_cursor,
99    rowid: i64,
100    value: i64,
101    min: i64,
102    max: i64,
103    step: i64,
104}
105impl GenerateSeriesCursor {
106    fn new() -> GenerateSeriesCursor {
107        let base: sqlite3_vtab_cursor = unsafe { mem::zeroed() };
108        GenerateSeriesCursor {
109            base,
110            rowid: 0,
111            value: 0,
112            min: 0,
113            max: 0,
114            step: 0,
115        }
116    }
117}
118
119impl VTabCursor for GenerateSeriesCursor {
120    fn filter(
121        &mut self,
122        _idx_num: c_int,
123        _idx_str: Option<&str>,
124        values: &[*mut sqlite3_value],
125    ) -> Result<()> {
126        //let pattern = values.get(0).unwrap().text()?;
127        //let contents = values.get(1).unwrap().text()?;
128        self.min = api::value_int64(values.get(0).expect("1st min constraint is required"));
129        self.max = api::value_int64(values.get(1).expect("2nd max constraint is required"));
130        self.value = self.min;
131        Ok(())
132    }
133
134    fn next(&mut self) -> Result<()> {
135        self.value += 1;
136        Ok(())
137    }
138
139    fn eof(&self) -> bool {
140        self.value > self.max
141    }
142
143    fn column(&self, context: *mut sqlite3_context, i: c_int) -> Result<()> {
144        match column(i) {
145            Some(Columns::Value) => {
146                api::result_int64(context, self.value);
147            }
148            Some(Columns::Start) => {
149                api::result_int64(context, self.min);
150            }
151            Some(Columns::Stop) => {
152                api::result_int64(context, self.max);
153            }
154            Some(Columns::Step) => {
155                //context_result_int(0);
156            }
157            _ => (),
158        }
159        Ok(())
160    }
161
162    fn rowid(&self) -> Result<i64> {
163        Ok(self.rowid)
164    }
165}
166
167#[sqlite_entrypoint]
168pub fn sqlite3_seriesrs_init(db: *mut sqlite3) -> Result<()> {
169    define_table_function::<GenerateSeriesTable>(db, "generate_series_rs", None)?;
170    Ok(())
171}