diff options
Diffstat (limited to 'third_party/rust/rusqlite/src/vtab/vtablog.rs')
-rw-r--r-- | third_party/rust/rusqlite/src/vtab/vtablog.rs | 300 |
1 files changed, 300 insertions, 0 deletions
diff --git a/third_party/rust/rusqlite/src/vtab/vtablog.rs b/third_party/rust/rusqlite/src/vtab/vtablog.rs new file mode 100644 index 0000000000..bc2e01f0ae --- /dev/null +++ b/third_party/rust/rusqlite/src/vtab/vtablog.rs @@ -0,0 +1,300 @@ +///! Port of C [vtablog](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/vtablog.c) +use std::default::Default; +use std::marker::PhantomData; +use std::os::raw::c_int; +use std::str::FromStr; +use std::sync::atomic::{AtomicUsize, Ordering}; + +use crate::vtab::{ + update_module, Context, CreateVTab, IndexInfo, UpdateVTab, VTab, VTabConnection, VTabCursor, + VTabKind, Values, +}; +use crate::{ffi, ValueRef}; +use crate::{Connection, Error, Result}; + +/// Register the "vtablog" module. +pub fn load_module(conn: &Connection) -> Result<()> { + let aux: Option<()> = None; + conn.create_module("vtablog", update_module::<VTabLog>(), aux) +} + +/// An instance of the vtablog virtual table +#[repr(C)] +struct VTabLog { + /// Base class. Must be first + base: ffi::sqlite3_vtab, + /// Number of rows in the table + n_row: i64, + /// Instance number for this vtablog table + i_inst: usize, + /// Number of cursors created + n_cursor: usize, +} + +impl VTabLog { + fn connect_create( + _: &mut VTabConnection, + _: Option<&()>, + args: &[&[u8]], + is_create: bool, + ) -> Result<(String, VTabLog)> { + static N_INST: AtomicUsize = AtomicUsize::new(1); + let i_inst = N_INST.fetch_add(1, Ordering::SeqCst); + println!( + "VTabLog::{}(tab={}, args={:?}):", + if is_create { "create" } else { "connect" }, + i_inst, + args, + ); + let mut schema = None; + let mut n_row = None; + + let args = &args[3..]; + for c_slice in args { + let (param, value) = super::parameter(c_slice)?; + match param { + "schema" => { + if schema.is_some() { + return Err(Error::ModuleError(format!( + "more than one '{}' parameter", + param + ))); + } + schema = Some(value.to_owned()) + } + "rows" => { + if n_row.is_some() { + return Err(Error::ModuleError(format!( + "more than one '{}' parameter", + param + ))); + } + if let Ok(n) = i64::from_str(value) { + n_row = Some(n) + } + } + _ => { + return Err(Error::ModuleError(format!( + "unrecognized parameter '{}'", + param + ))); + } + } + } + if schema.is_none() { + return Err(Error::ModuleError("no schema defined".to_owned())); + } + let vtab = VTabLog { + base: ffi::sqlite3_vtab::default(), + n_row: n_row.unwrap_or(10), + i_inst, + n_cursor: 0, + }; + Ok((schema.unwrap(), vtab)) + } +} + +impl Drop for VTabLog { + fn drop(&mut self) { + println!("VTabLog::drop({})", self.i_inst); + } +} + +unsafe impl<'vtab> VTab<'vtab> for VTabLog { + type Aux = (); + type Cursor = VTabLogCursor<'vtab>; + + fn connect( + db: &mut VTabConnection, + aux: Option<&Self::Aux>, + args: &[&[u8]], + ) -> Result<(String, Self)> { + VTabLog::connect_create(db, aux, args, false) + } + + fn best_index(&self, info: &mut IndexInfo) -> Result<()> { + println!("VTabLog::best_index({})", self.i_inst); + info.set_estimated_cost(500.); + info.set_estimated_rows(500); + Ok(()) + } + + fn open(&'vtab mut self) -> Result<Self::Cursor> { + self.n_cursor += 1; + println!( + "VTabLog::open(tab={}, cursor={})", + self.i_inst, self.n_cursor + ); + Ok(VTabLogCursor { + base: ffi::sqlite3_vtab_cursor::default(), + i_cursor: self.n_cursor, + row_id: 0, + phantom: PhantomData, + }) + } +} + +impl<'vtab> CreateVTab<'vtab> for VTabLog { + const KIND: VTabKind = VTabKind::Default; + + fn create( + db: &mut VTabConnection, + aux: Option<&Self::Aux>, + args: &[&[u8]], + ) -> Result<(String, Self)> { + VTabLog::connect_create(db, aux, args, true) + } + + fn destroy(&self) -> Result<()> { + println!("VTabLog::destroy({})", self.i_inst); + Ok(()) + } +} + +impl<'vtab> UpdateVTab<'vtab> for VTabLog { + fn delete(&mut self, arg: ValueRef<'_>) -> Result<()> { + println!("VTabLog::delete({}, {:?})", self.i_inst, arg); + Ok(()) + } + + fn insert(&mut self, args: &Values<'_>) -> Result<i64> { + println!( + "VTabLog::insert({}, {:?})", + self.i_inst, + args.iter().collect::<Vec<ValueRef<'_>>>() + ); + Ok(self.n_row as i64) + } + + fn update(&mut self, args: &Values<'_>) -> Result<()> { + println!( + "VTabLog::update({}, {:?})", + self.i_inst, + args.iter().collect::<Vec<ValueRef<'_>>>() + ); + Ok(()) + } +} + +/// A cursor for the Series virtual table +#[repr(C)] +struct VTabLogCursor<'vtab> { + /// Base class. Must be first + base: ffi::sqlite3_vtab_cursor, + /// Cursor number + i_cursor: usize, + /// The rowid + row_id: i64, + phantom: PhantomData<&'vtab VTabLog>, +} + +impl VTabLogCursor<'_> { + fn vtab(&self) -> &VTabLog { + unsafe { &*(self.base.pVtab as *const VTabLog) } + } +} + +impl Drop for VTabLogCursor<'_> { + fn drop(&mut self) { + println!( + "VTabLogCursor::drop(tab={}, cursor={})", + self.vtab().i_inst, + self.i_cursor + ); + } +} + +unsafe impl VTabCursor for VTabLogCursor<'_> { + fn filter(&mut self, _: c_int, _: Option<&str>, _: &Values<'_>) -> Result<()> { + println!( + "VTabLogCursor::filter(tab={}, cursor={})", + self.vtab().i_inst, + self.i_cursor + ); + self.row_id = 0; + Ok(()) + } + + fn next(&mut self) -> Result<()> { + println!( + "VTabLogCursor::next(tab={}, cursor={}): rowid {} -> {}", + self.vtab().i_inst, + self.i_cursor, + self.row_id, + self.row_id + 1 + ); + self.row_id += 1; + Ok(()) + } + + fn eof(&self) -> bool { + let eof = self.row_id >= self.vtab().n_row; + println!( + "VTabLogCursor::eof(tab={}, cursor={}): {}", + self.vtab().i_inst, + self.i_cursor, + eof, + ); + eof + } + + fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> { + let value = if i < 26 { + format!( + "{}{}", + "abcdefghijklmnopqrstuvwyz".chars().nth(i as usize).unwrap(), + self.row_id + ) + } else { + format!("{}{}", i, self.row_id) + }; + println!( + "VTabLogCursor::column(tab={}, cursor={}, i={}): {}", + self.vtab().i_inst, + self.i_cursor, + i, + value, + ); + ctx.set_result(&value) + } + + fn rowid(&self) -> Result<i64> { + println!( + "VTabLogCursor::rowid(tab={}, cursor={}): {}", + self.vtab().i_inst, + self.i_cursor, + self.row_id, + ); + Ok(self.row_id) + } +} + +#[cfg(test)] +mod test { + use crate::{Connection, Result}; + #[test] + fn test_module() -> Result<()> { + let db = Connection::open_in_memory()?; + super::load_module(&db)?; + + db.execute_batch( + "CREATE VIRTUAL TABLE temp.log USING vtablog( + schema='CREATE TABLE x(a,b,c)', + rows=25 + );", + )?; + let mut stmt = db.prepare("SELECT * FROM log;")?; + let mut rows = stmt.query([])?; + while rows.next()?.is_some() {} + db.execute("DELETE FROM log WHERE a = ?", ["a1"])?; + db.execute( + "INSERT INTO log (a, b, c) VALUES (?, ?, ?)", + ["a", "b", "c"], + )?; + db.execute( + "UPDATE log SET b = ?, c = ? WHERE a = ?", + ["bn", "cn", "a1"], + )?; + Ok(()) + } +} |