mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 05:48:56 +08:00 
			
		
		
		
	Draft for carray module in Rust
Incomplete support for sqlite3_bind_pointer. Make Context::set_result return a Result. Add Values::get_array.
This commit is contained in:
		
							
								
								
									
										179
									
								
								src/vtab/array.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										179
									
								
								src/vtab/array.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,179 @@ | ||||
| //! Array Virtual Table | ||||
| //! Port of [carray](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/carray.c) C extension. | ||||
| use std::default::Default; | ||||
| use std::os::raw::{c_char, c_int, c_void}; | ||||
| use std::rc::Rc; | ||||
|  | ||||
| use ffi; | ||||
| use types::{ToSql, ToSqlOutput, Value}; | ||||
| use vtab::{self, declare_vtab, Context, IndexInfo, VTab, VTabCursor, Values}; | ||||
| use {Connection, Error, Result}; | ||||
|  | ||||
| // http://sqlite.org/bindptr.html | ||||
|  | ||||
| pub(crate) const ARRAY_TYPE: *const c_char = b"rarray\0" as *const u8 as *const c_char; | ||||
|  | ||||
| pub(crate) unsafe extern "C" fn free_array(p: *mut c_void) { | ||||
|     let _: Array = Rc::from_raw(p as *const Vec<Value>); | ||||
| } | ||||
|  | ||||
| pub type Array = Rc<Vec<Value>>; | ||||
|  | ||||
| impl ToSql for Array { | ||||
|     fn to_sql(&self) -> Result<ToSqlOutput> { | ||||
|         Ok(ToSqlOutput::Array(self.clone())) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Register the "rarray" module. | ||||
| pub fn load_module(conn: &Connection) -> Result<()> { | ||||
|     let aux: Option<()> = None; | ||||
|     conn.create_module("rarray", &ARRAY_MODULE, aux) | ||||
| } | ||||
|  | ||||
| eponymous_module!( | ||||
|     ARRAY_MODULE, | ||||
|     ArrayTab, | ||||
|     (), | ||||
|     ArrayTabCursor, | ||||
|     None, | ||||
|     array_connect, | ||||
|     array_best_index, | ||||
|     array_disconnect, | ||||
|     None, | ||||
|     array_open, | ||||
|     array_close, | ||||
|     array_filter, | ||||
|     array_next, | ||||
|     array_eof, | ||||
|     array_column, | ||||
|     array_rowid | ||||
| ); | ||||
|  | ||||
| // Column numbers | ||||
| // const CARRAY_COLUMN_VALUE : c_int = 0; | ||||
| const CARRAY_COLUMN_POINTER: c_int = 1; | ||||
|  | ||||
| /// An instance of the Array virtual table | ||||
| #[repr(C)] | ||||
| struct ArrayTab { | ||||
|     /// Base class. Must be first | ||||
|     base: ffi::sqlite3_vtab, | ||||
| } | ||||
|  | ||||
| impl VTab for ArrayTab { | ||||
|     type Aux = (); | ||||
|     type Cursor = ArrayTabCursor; | ||||
|  | ||||
|     unsafe fn connect(db: *mut ffi::sqlite3, _aux: *mut (), _args: &[&[u8]]) -> Result<ArrayTab> { | ||||
|         let vtab = ArrayTab { | ||||
|             base: Default::default(), | ||||
|         }; | ||||
|         try!(declare_vtab(db, "CREATE TABLE x(value,pointer hidden)")); | ||||
|         Ok(vtab) | ||||
|     } | ||||
|  | ||||
|     fn best_index(&self, info: &mut IndexInfo) -> Result<()> { | ||||
|         // Index of the pointer= constraint | ||||
|         let mut ptr_idx = None; | ||||
|         for (i, constraint) in info.constraints().enumerate() { | ||||
|             if !constraint.is_usable() { | ||||
|                 continue; | ||||
|             } | ||||
|             if constraint.operator() != vtab::IndexConstraintOp::SQLITE_INDEX_CONSTRAINT_EQ { | ||||
|                 continue; | ||||
|             } | ||||
|             match constraint.column() { | ||||
|                 CARRAY_COLUMN_POINTER => { | ||||
|                     ptr_idx = Some(i); | ||||
|                 } | ||||
|                 _ => {} | ||||
|             } | ||||
|         } | ||||
|         if let Some(ptr_idx) = ptr_idx { | ||||
|             { | ||||
|                 let mut constraint_usage = info.constraint_usage(ptr_idx); | ||||
|                 constraint_usage.set_argv_index(1); | ||||
|                 constraint_usage.set_omit(true); | ||||
|             } | ||||
|             info.set_estimated_cost(1f64); | ||||
|             info.set_estimated_rows(100); | ||||
|             info.set_idx_num(1); | ||||
|         } else { | ||||
|             info.set_estimated_cost(2_147_483_647f64); | ||||
|             info.set_estimated_rows(2_147_483_647); | ||||
|             info.set_idx_num(0); | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn open(&self) -> Result<ArrayTabCursor> { | ||||
|         Ok(ArrayTabCursor::new()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A cursor for the Array virtual table | ||||
| #[repr(C)] | ||||
| struct ArrayTabCursor { | ||||
|     /// Base class. Must be first | ||||
|     base: ffi::sqlite3_vtab_cursor, | ||||
|     /// The rowid | ||||
|     row_id: i64, | ||||
|     /// Pointer to the array of values ("pointer") | ||||
|     ptr: Option<Array>, | ||||
| } | ||||
|  | ||||
| impl ArrayTabCursor { | ||||
|     fn new() -> ArrayTabCursor { | ||||
|         ArrayTabCursor { | ||||
|             base: Default::default(), | ||||
|             row_id: 0, | ||||
|             ptr: None, | ||||
|         } | ||||
|     } | ||||
|     fn len(&self) -> i64 { | ||||
|         match self.ptr { | ||||
|             Some(ref a) => a.len() as i64, | ||||
|             _ => 0, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl VTabCursor for ArrayTabCursor { | ||||
|     type Table = ArrayTab; | ||||
|  | ||||
|     fn vtab(&self) -> &ArrayTab { | ||||
|         unsafe { &*(self.base.pVtab as *const ArrayTab) } | ||||
|     } | ||||
|     fn filter(&mut self, idx_num: c_int, _idx_str: Option<&str>, args: &Values) -> Result<()> { | ||||
|         if idx_num > 0 { | ||||
|             self.ptr = try!(args.get_array(0)); | ||||
|         } else { | ||||
|             self.ptr = None; | ||||
|         } | ||||
|         self.row_id = 1; | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn next(&mut self) -> Result<()> { | ||||
|         self.row_id += 1; | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn eof(&self) -> bool { | ||||
|         self.row_id > self.len() | ||||
|     } | ||||
|     fn column(&self, ctx: &mut Context, i: c_int) -> Result<()> { | ||||
|         match i { | ||||
|             CARRAY_COLUMN_POINTER => Ok(()), | ||||
|             _ => { | ||||
|                 if let Some(ref array) = self.ptr { | ||||
|                     let value = &array[i as usize]; | ||||
|                     ctx.set_result(&value) | ||||
|                 } else { | ||||
|                     Ok(()) | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     fn rowid(&self) -> Result<i64> { | ||||
|         Ok(self.row_id) | ||||
|     } | ||||
| } | ||||
| @@ -327,12 +327,10 @@ impl VTabCursor for CSVTabCursor { | ||||
|             ))); | ||||
|         } | ||||
|         if self.cols.is_empty() { | ||||
|             ctx.set_result(&Null); | ||||
|             return Ok(()); | ||||
|             return ctx.set_result(&Null); | ||||
|         } | ||||
|         // TODO Affinity | ||||
|         ctx.set_result(&self.cols[col as usize].to_owned()); | ||||
|         Ok(()) | ||||
|         ctx.set_result(&self.cols[col as usize].to_owned()) | ||||
|     } | ||||
|     fn rowid(&self) -> Result<i64> { | ||||
|         Ok(self.row_number as i64) | ||||
| @@ -365,7 +363,8 @@ mod test { | ||||
|                 assert_eq!(vec!["rowid", "colA", "colB", "colC"], headers); | ||||
|             } | ||||
|  | ||||
|             let ids: Result<Vec<i32>> = s.query_map(&[], |row| row.get::<i32, i32>(0)) | ||||
|             let ids: Result<Vec<i32>> = s | ||||
|                 .query_map(&[], |row| row.get::<i32, i32>(0)) | ||||
|                 .unwrap() | ||||
|                 .collect(); | ||||
|             let sum = ids.unwrap().iter().fold(0, |acc, &id| acc + id); | ||||
| @@ -382,10 +381,11 @@ mod test { | ||||
|             .unwrap(); | ||||
|  | ||||
|         { | ||||
|             let mut s = db.prepare( | ||||
|                 "SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \ | ||||
|                  v1.rowid < v2.rowid", | ||||
|             ).unwrap(); | ||||
|             let mut s = | ||||
|                 db.prepare( | ||||
|                     "SELECT v1.rowid, v1.* FROM vtab v1 NATURAL JOIN vtab v2 WHERE \ | ||||
|                      v1.rowid < v2.rowid", | ||||
|                 ).unwrap(); | ||||
|  | ||||
|             let mut rows = s.query(&[]).unwrap(); | ||||
|             let row = rows.next().unwrap().unwrap(); | ||||
|   | ||||
| @@ -139,9 +139,8 @@ impl VTabCursor for IntArrayVTabCursor { | ||||
|         let vtab = self.vtab(); | ||||
|         unsafe { | ||||
|             let array = (*vtab.array).borrow(); | ||||
|             ctx.set_result(&array[self.i]); | ||||
|             ctx.set_result(&array[self.i]) | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn rowid(&self) -> Result<i64> { | ||||
|         Ok(self.i as i64) | ||||
|   | ||||
| @@ -6,7 +6,7 @@ use std::os::raw::{c_char, c_int, c_void}; | ||||
| use std::ptr; | ||||
| use std::slice; | ||||
|  | ||||
| use context::{report_error, set_result}; | ||||
| use context::set_result; | ||||
| use error::error_from_sqlite_code; | ||||
| use ffi; | ||||
| use types::{FromSql, FromSqlError, ToSql, ValueRef}; | ||||
| @@ -207,12 +207,10 @@ pub trait VTabCursor: Sized { | ||||
| pub struct Context(*mut ffi::sqlite3_context); | ||||
|  | ||||
| impl Context { | ||||
|     pub fn set_result<T: ToSql>(&mut self, value: &T) { | ||||
|         let t = value.to_sql(); | ||||
|         match t { | ||||
|             Ok(ref value) => unsafe { set_result(self.0, value) }, | ||||
|             Err(err) => unsafe { report_error(self.0, &err) }, | ||||
|         } | ||||
|     pub fn set_result<T: ToSql>(&mut self, value: &T) -> Result<()> { | ||||
|         let t = value.to_sql()?; | ||||
|         unsafe { set_result(self.0, &t) }; | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| @@ -241,6 +239,22 @@ impl<'a> Values<'a> { | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     // `sqlite3_value_type` returns `SQLITE_NULL` for pointer. | ||||
|     // So it seems not possible to enhance `ValueRef::from_value`. | ||||
|     #[cfg(feature = "array")] | ||||
|     pub fn get_array(&self, idx: usize) -> Result<Option<array::Array>> { | ||||
|         use types::Value; | ||||
|         let arg = self.args[idx]; | ||||
|         let ptr = unsafe { ffi::sqlite3_value_pointer(arg, array::ARRAY_TYPE) }; | ||||
|         if ptr.is_null() { | ||||
|             Ok(None) | ||||
|         } else { | ||||
|             Ok(Some(unsafe { | ||||
|                 array::Array::from_raw(ptr as *const Vec<Value>) | ||||
|             })) | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn iter(&self) -> ValueIter { | ||||
|         ValueIter { | ||||
|             iter: self.args.iter(), | ||||
| @@ -361,12 +375,16 @@ pub fn dequote(s: &str) -> &str { | ||||
| /// 0 no false off | ||||
| /// ``` | ||||
| pub fn parse_boolean(s: &str) -> Option<bool> { | ||||
|     if s.eq_ignore_ascii_case("yes") || s.eq_ignore_ascii_case("on") | ||||
|         || s.eq_ignore_ascii_case("true") || s.eq("1") | ||||
|     if s.eq_ignore_ascii_case("yes") | ||||
|         || s.eq_ignore_ascii_case("on") | ||||
|         || s.eq_ignore_ascii_case("true") | ||||
|         || s.eq("1") | ||||
|     { | ||||
|         Some(true) | ||||
|     } else if s.eq_ignore_ascii_case("no") || s.eq_ignore_ascii_case("off") | ||||
|         || s.eq_ignore_ascii_case("false") || s.eq("0") | ||||
|     } else if s.eq_ignore_ascii_case("no") | ||||
|         || s.eq_ignore_ascii_case("off") | ||||
|         || s.eq_ignore_ascii_case("false") | ||||
|         || s.eq("0") | ||||
|     { | ||||
|         Some(false) | ||||
|     } else { | ||||
| @@ -530,7 +548,8 @@ macro_rules! create_or_connect { | ||||
|  | ||||
|             let aux = aux as *mut $aux; | ||||
|             let args = slice::from_raw_parts(argv, argc as usize); | ||||
|             let vec = args.iter() | ||||
|             let vec = args | ||||
|                 .iter() | ||||
|                 .map(|&cs| CStr::from_ptr(cs).to_bytes()) | ||||
|                 .collect::<Vec<_>>(); | ||||
|             match $vtab::$vtab_func(db, aux, &vec[..]) { | ||||
| @@ -756,6 +775,8 @@ pub fn mprintf(err_msg: &str) -> *mut c_char { | ||||
|     unsafe { ffi::sqlite3_mprintf(c_format.as_ptr(), c_err.as_ptr()) } | ||||
| } | ||||
|  | ||||
| #[cfg(feature = "array")] | ||||
| pub mod array; | ||||
| #[cfg(feature = "csvtab")] | ||||
| pub mod csvtab; | ||||
| pub mod int_array; | ||||
|   | ||||
| @@ -255,8 +255,7 @@ impl VTabCursor for SeriesTabCursor { | ||||
|             SERIES_COLUMN_STEP => self.step, | ||||
|             _ => self.value, | ||||
|         }; | ||||
|         ctx.set_result(&x); | ||||
|         Ok(()) | ||||
|         ctx.set_result(&x) | ||||
|     } | ||||
|     fn rowid(&self) -> Result<i64> { | ||||
|         Ok(self.row_id) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user