1use 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 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 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: 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 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 }
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}