• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //! Port of C [vtablog](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/vtablog.c)
2 use std::marker::PhantomData;
3 use std::os::raw::c_int;
4 use std::str::FromStr;
5 use std::sync::atomic::{AtomicUsize, Ordering};
6 
7 use crate::vtab::{
8     update_module, Context, CreateVTab, IndexInfo, UpdateVTab, VTab, VTabConnection, VTabCursor,
9     VTabKind, Values,
10 };
11 use crate::{ffi, ValueRef};
12 use crate::{Connection, Error, Result};
13 
14 /// Register the "vtablog" module.
load_module(conn: &Connection) -> Result<()>15 pub fn load_module(conn: &Connection) -> Result<()> {
16     let aux: Option<()> = None;
17     conn.create_module("vtablog", update_module::<VTabLog>(), aux)
18 }
19 
20 /// An instance of the vtablog virtual table
21 #[repr(C)]
22 struct VTabLog {
23     /// Base class. Must be first
24     base: ffi::sqlite3_vtab,
25     /// Number of rows in the table
26     n_row: i64,
27     /// Instance number for this vtablog table
28     i_inst: usize,
29     /// Number of cursors created
30     n_cursor: usize,
31 }
32 
33 impl VTabLog {
connect_create( _: &mut VTabConnection, _: Option<&()>, args: &[&[u8]], is_create: bool, ) -> Result<(String, VTabLog)>34     fn connect_create(
35         _: &mut VTabConnection,
36         _: Option<&()>,
37         args: &[&[u8]],
38         is_create: bool,
39     ) -> Result<(String, VTabLog)> {
40         static N_INST: AtomicUsize = AtomicUsize::new(1);
41         let i_inst = N_INST.fetch_add(1, Ordering::SeqCst);
42         println!(
43             "VTabLog::{}(tab={}, args={:?}):",
44             if is_create { "create" } else { "connect" },
45             i_inst,
46             args,
47         );
48         let mut schema = None;
49         let mut n_row = None;
50 
51         let args = &args[3..];
52         for c_slice in args {
53             let (param, value) = super::parameter(c_slice)?;
54             match param {
55                 "schema" => {
56                     if schema.is_some() {
57                         return Err(Error::ModuleError(format!(
58                             "more than one '{param}' parameter"
59                         )));
60                     }
61                     schema = Some(value.to_owned())
62                 }
63                 "rows" => {
64                     if n_row.is_some() {
65                         return Err(Error::ModuleError(format!(
66                             "more than one '{param}' parameter"
67                         )));
68                     }
69                     if let Ok(n) = i64::from_str(value) {
70                         n_row = Some(n)
71                     }
72                 }
73                 _ => {
74                     return Err(Error::ModuleError(format!(
75                         "unrecognized parameter '{param}'"
76                     )));
77                 }
78             }
79         }
80         if schema.is_none() {
81             return Err(Error::ModuleError("no schema defined".to_owned()));
82         }
83         let vtab = VTabLog {
84             base: ffi::sqlite3_vtab::default(),
85             n_row: n_row.unwrap_or(10),
86             i_inst,
87             n_cursor: 0,
88         };
89         Ok((schema.unwrap(), vtab))
90     }
91 }
92 
93 impl Drop for VTabLog {
drop(&mut self)94     fn drop(&mut self) {
95         println!("VTabLog::drop({})", self.i_inst);
96     }
97 }
98 
99 unsafe impl<'vtab> VTab<'vtab> for VTabLog {
100     type Aux = ();
101     type Cursor = VTabLogCursor<'vtab>;
102 
connect( db: &mut VTabConnection, aux: Option<&Self::Aux>, args: &[&[u8]], ) -> Result<(String, Self)>103     fn connect(
104         db: &mut VTabConnection,
105         aux: Option<&Self::Aux>,
106         args: &[&[u8]],
107     ) -> Result<(String, Self)> {
108         VTabLog::connect_create(db, aux, args, false)
109     }
110 
best_index(&self, info: &mut IndexInfo) -> Result<()>111     fn best_index(&self, info: &mut IndexInfo) -> Result<()> {
112         println!("VTabLog::best_index({})", self.i_inst);
113         info.set_estimated_cost(500.);
114         info.set_estimated_rows(500);
115         Ok(())
116     }
117 
open(&'vtab mut self) -> Result<Self::Cursor>118     fn open(&'vtab mut self) -> Result<Self::Cursor> {
119         self.n_cursor += 1;
120         println!(
121             "VTabLog::open(tab={}, cursor={})",
122             self.i_inst, self.n_cursor
123         );
124         Ok(VTabLogCursor {
125             base: ffi::sqlite3_vtab_cursor::default(),
126             i_cursor: self.n_cursor,
127             row_id: 0,
128             phantom: PhantomData,
129         })
130     }
131 }
132 
133 impl<'vtab> CreateVTab<'vtab> for VTabLog {
134     const KIND: VTabKind = VTabKind::Default;
135 
create( db: &mut VTabConnection, aux: Option<&Self::Aux>, args: &[&[u8]], ) -> Result<(String, Self)>136     fn create(
137         db: &mut VTabConnection,
138         aux: Option<&Self::Aux>,
139         args: &[&[u8]],
140     ) -> Result<(String, Self)> {
141         VTabLog::connect_create(db, aux, args, true)
142     }
143 
destroy(&self) -> Result<()>144     fn destroy(&self) -> Result<()> {
145         println!("VTabLog::destroy({})", self.i_inst);
146         Ok(())
147     }
148 }
149 
150 impl<'vtab> UpdateVTab<'vtab> for VTabLog {
delete(&mut self, arg: ValueRef<'_>) -> Result<()>151     fn delete(&mut self, arg: ValueRef<'_>) -> Result<()> {
152         println!("VTabLog::delete({}, {arg:?})", self.i_inst);
153         Ok(())
154     }
155 
insert(&mut self, args: &Values<'_>) -> Result<i64>156     fn insert(&mut self, args: &Values<'_>) -> Result<i64> {
157         println!(
158             "VTabLog::insert({}, {:?})",
159             self.i_inst,
160             args.iter().collect::<Vec<ValueRef<'_>>>()
161         );
162         Ok(self.n_row)
163     }
164 
update(&mut self, args: &Values<'_>) -> Result<()>165     fn update(&mut self, args: &Values<'_>) -> Result<()> {
166         println!(
167             "VTabLog::update({}, {:?})",
168             self.i_inst,
169             args.iter().collect::<Vec<ValueRef<'_>>>()
170         );
171         Ok(())
172     }
173 }
174 
175 /// A cursor for the Series virtual table
176 #[repr(C)]
177 struct VTabLogCursor<'vtab> {
178     /// Base class. Must be first
179     base: ffi::sqlite3_vtab_cursor,
180     /// Cursor number
181     i_cursor: usize,
182     /// The rowid
183     row_id: i64,
184     phantom: PhantomData<&'vtab VTabLog>,
185 }
186 
187 impl VTabLogCursor<'_> {
vtab(&self) -> &VTabLog188     fn vtab(&self) -> &VTabLog {
189         unsafe { &*(self.base.pVtab as *const VTabLog) }
190     }
191 }
192 
193 impl Drop for VTabLogCursor<'_> {
drop(&mut self)194     fn drop(&mut self) {
195         println!(
196             "VTabLogCursor::drop(tab={}, cursor={})",
197             self.vtab().i_inst,
198             self.i_cursor
199         );
200     }
201 }
202 
203 unsafe impl VTabCursor for VTabLogCursor<'_> {
filter(&mut self, _: c_int, _: Option<&str>, _: &Values<'_>) -> Result<()>204     fn filter(&mut self, _: c_int, _: Option<&str>, _: &Values<'_>) -> Result<()> {
205         println!(
206             "VTabLogCursor::filter(tab={}, cursor={})",
207             self.vtab().i_inst,
208             self.i_cursor
209         );
210         self.row_id = 0;
211         Ok(())
212     }
213 
next(&mut self) -> Result<()>214     fn next(&mut self) -> Result<()> {
215         println!(
216             "VTabLogCursor::next(tab={}, cursor={}): rowid {} -> {}",
217             self.vtab().i_inst,
218             self.i_cursor,
219             self.row_id,
220             self.row_id + 1
221         );
222         self.row_id += 1;
223         Ok(())
224     }
225 
eof(&self) -> bool226     fn eof(&self) -> bool {
227         let eof = self.row_id >= self.vtab().n_row;
228         println!(
229             "VTabLogCursor::eof(tab={}, cursor={}): {}",
230             self.vtab().i_inst,
231             self.i_cursor,
232             eof,
233         );
234         eof
235     }
236 
column(&self, ctx: &mut Context, i: c_int) -> Result<()>237     fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> {
238         let value = if i < 26 {
239             format!(
240                 "{}{}",
241                 "abcdefghijklmnopqrstuvwyz".chars().nth(i as usize).unwrap(),
242                 self.row_id
243             )
244         } else {
245             format!("{i}{}", self.row_id)
246         };
247         println!(
248             "VTabLogCursor::column(tab={}, cursor={}, i={}): {}",
249             self.vtab().i_inst,
250             self.i_cursor,
251             i,
252             value,
253         );
254         ctx.set_result(&value)
255     }
256 
rowid(&self) -> Result<i64>257     fn rowid(&self) -> Result<i64> {
258         println!(
259             "VTabLogCursor::rowid(tab={}, cursor={}): {}",
260             self.vtab().i_inst,
261             self.i_cursor,
262             self.row_id,
263         );
264         Ok(self.row_id)
265     }
266 }
267 
268 #[cfg(test)]
269 mod test {
270     use crate::{Connection, Result};
271     #[test]
test_module() -> Result<()>272     fn test_module() -> Result<()> {
273         let db = Connection::open_in_memory()?;
274         super::load_module(&db)?;
275 
276         db.execute_batch(
277             "CREATE VIRTUAL TABLE temp.log USING vtablog(
278                     schema='CREATE TABLE x(a,b,c)',
279                     rows=25
280                 );",
281         )?;
282         let mut stmt = db.prepare("SELECT * FROM log;")?;
283         let mut rows = stmt.query([])?;
284         while rows.next()?.is_some() {}
285         db.execute("DELETE FROM log WHERE a = ?1", ["a1"])?;
286         db.execute(
287             "INSERT INTO log (a, b, c) VALUES (?1, ?2, ?3)",
288             ["a", "b", "c"],
289         )?;
290         db.execute(
291             "UPDATE log SET b = ?1, c = ?2 WHERE a = ?3",
292             ["bn", "cn", "a1"],
293         )?;
294         Ok(())
295     }
296 }
297