mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 13:58:55 +08:00 
			
		
		
		
	Add series table-valued-function.
This commit is contained in:
		| @@ -20,9 +20,11 @@ pub fn load_module(conn: &Connection) -> Result<()> { | ||||
| init_module!(CSV_MODULE, | ||||
|              CSVTab, | ||||
|              CSVTabCursor, | ||||
|              csv_create, | ||||
|              Some(csv_connect), | ||||
|              csv_connect, | ||||
|              csv_best_index, | ||||
|              csv_destroy, | ||||
|              csv_disconnect, | ||||
|              Some(csv_disconnect), | ||||
|              csv_open, | ||||
|              csv_close, | ||||
|              csv_filter, | ||||
| @@ -56,7 +58,7 @@ impl CSVTab { | ||||
| } | ||||
|  | ||||
| impl VTab<CSVTabCursor> for CSVTab { | ||||
|     fn create(db: *mut ffi::sqlite3, _aux: *mut libc::c_void, args: &[&[u8]]) -> Result<CSVTab> { | ||||
|     fn connect(db: *mut ffi::sqlite3, _aux: *mut libc::c_void, args: &[&[u8]]) -> Result<CSVTab> { | ||||
|         if args.len() < 4 { | ||||
|             return Err(Error::ModuleError("no CSV file specified".to_owned())); | ||||
|         } | ||||
|   | ||||
| @@ -1,4 +1,5 @@ | ||||
| //! Int array virtual table. | ||||
| //! Port of C ["intarray"](http://www.sqlite.org/cgi/src/finfo?name=src/test_intarray.h). | ||||
| use std::cell::RefCell; | ||||
| use std::default::Default; | ||||
| use std::mem; | ||||
| @@ -39,9 +40,11 @@ pub fn drop_int_array(conn: &Connection, name: &str) -> Result<()> { | ||||
| init_module!(INT_ARRAY_MODULE, | ||||
|              IntArrayVTab, | ||||
|              IntArrayVTabCursor, | ||||
|              int_array_create, | ||||
|              Some(int_array_connect), | ||||
|              int_array_connect, | ||||
|              int_array_best_index, | ||||
|              int_array_destroy, | ||||
|              int_array_disconnect, | ||||
|              Some(int_array_disconnect), | ||||
|              int_array_open, | ||||
|              int_array_close, | ||||
|              int_array_filter, | ||||
| @@ -58,10 +61,10 @@ struct IntArrayVTab { | ||||
| } | ||||
|  | ||||
| impl VTab<IntArrayVTabCursor> for IntArrayVTab { | ||||
|     fn create(db: *mut ffi::sqlite3, | ||||
|               aux: *mut libc::c_void, | ||||
|               _args: &[&[u8]]) | ||||
|               -> Result<IntArrayVTab> { | ||||
|     fn connect(db: *mut ffi::sqlite3, | ||||
|                aux: *mut libc::c_void, | ||||
|                _args: &[&[u8]]) | ||||
|                -> Result<IntArrayVTab> { | ||||
|         let array = unsafe { mem::transmute(aux) }; | ||||
|         let vtab = IntArrayVTab { | ||||
|             base: Default::default(), | ||||
|   | ||||
| @@ -44,7 +44,7 @@ use types::FromSql; | ||||
| pub trait VTab<C: VTabCursor<Self>>: Sized { | ||||
|     /// Create a new instance of a virtual table in response to a CREATE VIRTUAL TABLE statement. | ||||
|     /// The `db` parameter is a pointer to the SQLite database connection that is executing the CREATE VIRTUAL TABLE statement. | ||||
|     fn create(db: *mut ffi::sqlite3, aux: *mut libc::c_void, args: &[&[u8]]) -> Result<Self>; | ||||
|     fn connect(db: *mut ffi::sqlite3, aux: *mut libc::c_void, args: &[&[u8]]) -> Result<Self>; | ||||
|     /// Determine the best way to access the virtual table. | ||||
|     fn best_index(&self, info: &mut IndexInfo) -> Result<()>; | ||||
|     /// Create a new cursor used for accessing a virtual table. | ||||
| @@ -90,7 +90,7 @@ impl IndexInfo { | ||||
|         } | ||||
|     } | ||||
|     /// True if this constraint is usable | ||||
|     pub fn constraint_usable(&self, constraint_idx: usize) -> bool { | ||||
|     pub fn is_constraint_usable(&self, constraint_idx: usize) -> bool { | ||||
|         use std::slice; | ||||
|         unsafe { | ||||
|             let constraints = slice::from_raw_parts((*self.0).aConstraint, | ||||
| @@ -288,18 +288,19 @@ unsafe extern "C" fn free_boxed_value<T>(p: *mut libc::c_void) { | ||||
| #[macro_export] | ||||
| macro_rules! init_module { | ||||
|     ($module_name: ident, $vtab: ident, $cursor: ty, | ||||
|         $create: ident, $best_index: ident, $destroy: ident, | ||||
|         $create: expr, $connect: ident, $best_index: ident, | ||||
|         $disconnect: ident, $destroy: expr, | ||||
|         $open: ident, $close: ident, | ||||
|         $filter: ident, $next: ident, $eof: ident, | ||||
|         $column: ident, $rowid: ident) => { | ||||
|  | ||||
| static $module_name: ffi::sqlite3_module = ffi::sqlite3_module { | ||||
|     iVersion: 1, | ||||
|     xCreate: Some($create), | ||||
|     xConnect: Some($create), /* A virtual table is eponymous if its xCreate method is the exact same function as the xConnect method */ | ||||
|     xCreate: $create, | ||||
|     xConnect: Some($connect), /* A virtual table is eponymous if its xCreate method is the exact same function as the xConnect method */ | ||||
|     xBestIndex: Some($best_index), | ||||
|     xDisconnect: Some($destroy), | ||||
|     xDestroy: Some($destroy), | ||||
|     xDisconnect: Some($disconnect), | ||||
|     xDestroy: $destroy, | ||||
|     xOpen: Some($open), | ||||
|     xClose: Some($close), | ||||
|     xFilter: Some($filter), | ||||
| @@ -319,7 +320,7 @@ static $module_name: ffi::sqlite3_module = ffi::sqlite3_module { | ||||
|     xRollbackTo: None, | ||||
| }; | ||||
|  | ||||
| unsafe extern "C" fn $create(db: *mut ffi::sqlite3, | ||||
| unsafe extern "C" fn $connect(db: *mut ffi::sqlite3, | ||||
|                               aux: *mut libc::c_void, | ||||
|                               argc: libc::c_int, | ||||
|                               argv: *const *const libc::c_char, | ||||
| @@ -334,7 +335,7 @@ unsafe extern "C" fn $create(db: *mut ffi::sqlite3, | ||||
|     let vec = args.iter().map(|cs| { | ||||
|         CStr::from_ptr(*cs).to_bytes() | ||||
|     }).collect::<Vec<_>>(); | ||||
|     match $vtab::create(db, aux, &vec[..]) { | ||||
|     match $vtab::connect(db, aux, &vec[..]) { | ||||
|         Ok(vtab) => { | ||||
|             let boxed_vtab: *mut $vtab = Box::into_raw(Box::new(vtab)); | ||||
|             *pp_vtab = boxed_vtab as *mut ffi::sqlite3_vtab; | ||||
| @@ -375,7 +376,7 @@ unsafe extern "C" fn $best_index(vtab: *mut ffi::sqlite3_vtab, | ||||
|     } | ||||
|  | ||||
| } | ||||
| unsafe extern "C" fn $destroy(vtab: *mut ffi::sqlite3_vtab) -> libc::c_int { | ||||
| unsafe extern "C" fn $disconnect(vtab: *mut ffi::sqlite3_vtab) -> libc::c_int { | ||||
|     let vtab = vtab as *mut $vtab; | ||||
|     let _: Box<$vtab> = Box::from_raw(vtab); | ||||
|     ffi::SQLITE_OK | ||||
| @@ -536,3 +537,4 @@ pub fn mprintf(err_msg: &str) -> *mut ::libc::c_char { | ||||
| pub mod int_array; | ||||
| #[cfg(feature = "csvtab")] | ||||
| pub mod csvtab; | ||||
| pub mod series; | ||||
|   | ||||
							
								
								
									
										244
									
								
								src/vtab/series.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										244
									
								
								src/vtab/series.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,244 @@ | ||||
| //! generate_series virtual table. | ||||
| //! Port of C [generate_series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c). | ||||
| use std::default::Default; | ||||
| use libc; | ||||
|  | ||||
| use {Connection, Error, Result}; | ||||
| use ffi; | ||||
| use vtab::{self, declare_vtab, Context, IndexInfo, Values, VTab, VTabCursor}; | ||||
|  | ||||
| /// Register the "generate_series" module. | ||||
| pub fn load_module(conn: &Connection) -> Result<()> { | ||||
|     let aux: Option<()> = None; | ||||
|     conn.create_module("generate_series", &SERIES_MODULE, aux) | ||||
| } | ||||
|  | ||||
|  | ||||
| init_module!(SERIES_MODULE, | ||||
|              SeriesTab, | ||||
|              SeriesTabCursor, | ||||
|              None, | ||||
|              series_connect, | ||||
|              series_best_index, | ||||
|              series_disconnect, | ||||
|              None, | ||||
|              series_open, | ||||
|              series_close, | ||||
|              series_filter, | ||||
|              series_next, | ||||
|              series_eof, | ||||
|              series_column, | ||||
|              series_rowid); | ||||
|  | ||||
| // Column numbers | ||||
| // const SERIES_COLUMN_VALUE : libc::c_int = 0; | ||||
| const SERIES_COLUMN_START: libc::c_int = 1; | ||||
| const SERIES_COLUMN_STOP: libc::c_int = 2; | ||||
| const SERIES_COLUMN_STEP: libc::c_int = 3; | ||||
|  | ||||
| bitflags! { | ||||
|     #[repr(C)] | ||||
|     flags QueryPlanFlags: ::libc::c_int { | ||||
|         // start = $value  -- constraint exists | ||||
|         const START = 1, | ||||
|         // stop = $value   -- constraint exists | ||||
|         const STOP  = 2, | ||||
|         // step = $value   -- constraint exists | ||||
|         const STEP  = 4, | ||||
|         // output in descending order | ||||
|         const DESC  = 8, | ||||
|         // Both start and stop | ||||
|         const BOTH  = START.bits | STOP.bits, | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| /// An instance of the Series virtual table | ||||
| #[repr(C)] | ||||
| struct SeriesTab { | ||||
|     /// Base class. Must be first | ||||
|     base: ffi::sqlite3_vtab, | ||||
| } | ||||
|  | ||||
|  | ||||
| impl VTab<SeriesTabCursor> for SeriesTab { | ||||
|     fn connect(db: *mut ffi::sqlite3, | ||||
|                _aux: *mut libc::c_void, | ||||
|                _args: &[&[u8]]) | ||||
|                -> Result<SeriesTab> { | ||||
|         let vtab = SeriesTab { base: Default::default() }; | ||||
|         try!(declare_vtab(db, | ||||
|                           "CREATE TABLE x(value,start hidden,stop hidden,step hidden)")); | ||||
|         Ok(vtab) | ||||
|     } | ||||
|  | ||||
|     fn best_index(&self, info: &mut IndexInfo) -> Result<()> { | ||||
|         // The query plan bitmask | ||||
|         let mut idx_num: QueryPlanFlags = QueryPlanFlags::empty(); | ||||
|         // Index of the start= constraint | ||||
|         let mut start_idx = None; | ||||
|         // Index of the stop= constraint | ||||
|         let mut stop_idx = None; | ||||
|         // Index of the step= constraint | ||||
|         let mut step_idx = None; | ||||
|         for i in 0..info.num_of_constraint() { | ||||
|             if !info.is_constraint_usable(i) { | ||||
|                 continue; | ||||
|             } | ||||
|             if info.constraint_operator(i) != vtab::SQLITE_INDEX_CONSTRAINT_EQ { | ||||
|                 continue; | ||||
|             } | ||||
|             match info.constraint_column(i) { | ||||
|                 SERIES_COLUMN_START => { | ||||
|                     start_idx = Some(i); | ||||
|                     idx_num |= START; | ||||
|                 } | ||||
|                 SERIES_COLUMN_STOP => { | ||||
|                     stop_idx = Some(i); | ||||
|                     idx_num |= STOP; | ||||
|                 } | ||||
|                 SERIES_COLUMN_STEP => { | ||||
|                     step_idx = Some(i); | ||||
|                     idx_num |= STEP; | ||||
|                 } | ||||
|                 _ => {} | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         let mut num_of_arg = 0; | ||||
|         if let Some(start_idx) = start_idx { | ||||
|             num_of_arg += 1; | ||||
|             info.set_argv_index(start_idx, num_of_arg); | ||||
|             info.set_omit(start_idx, true); | ||||
|         } | ||||
|         if let Some(stop_idx) = stop_idx { | ||||
|             num_of_arg += 1; | ||||
|             info.set_argv_index(stop_idx, num_of_arg); | ||||
|             info.set_omit(stop_idx, true); | ||||
|         } | ||||
|         if let Some(step_idx) = step_idx { | ||||
|             num_of_arg += 1; | ||||
|             info.set_argv_index(step_idx, num_of_arg); | ||||
|             info.set_omit(step_idx, true); | ||||
|         } | ||||
|         if idx_num.contains(BOTH) { | ||||
|             // Both start= and stop= boundaries are available. | ||||
|             info.set_estimated_cost((2 - if idx_num.contains(STEP) { 1 } else { 0 }) as f64); | ||||
|             info.set_estimated_rows(1000); | ||||
|             if info.num_of_order_by() == 1 { | ||||
|                 if info.is_order_by_desc(0) { | ||||
|                     idx_num |= DESC; | ||||
|                 } | ||||
|                 info.set_order_by_consumed(true); | ||||
|             } | ||||
|         } else { | ||||
|             info.set_estimated_cost(2147483647f64); | ||||
|             info.set_estimated_rows(2147483647); | ||||
|         } | ||||
|         info.set_idx_num(idx_num.bits()); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn open(&self) -> Result<SeriesTabCursor> { | ||||
|         Ok(SeriesTabCursor::new()) | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A cursor for the Series virtual table | ||||
| #[derive(Default)] | ||||
| #[repr(C)] | ||||
| struct SeriesTabCursor { | ||||
|     /// Base class. Must be first | ||||
|     base: ffi::sqlite3_vtab_cursor, | ||||
|     /// True to count down rather than up | ||||
|     is_desc: bool, | ||||
|     /// The rowid | ||||
|     row_id: i64, | ||||
|     /// Current value ("value") | ||||
|     value: i64, | ||||
|     /// Mimimum value ("start") | ||||
|     min_value: i64, | ||||
|     /// Maximum value ("stop") | ||||
|     max_value: i64, | ||||
|     /// Increment ("step") | ||||
|     step: i64, | ||||
| } | ||||
|  | ||||
| impl SeriesTabCursor { | ||||
|     fn new() -> SeriesTabCursor { | ||||
|         Default::default() | ||||
|     } | ||||
| } | ||||
| impl VTabCursor<SeriesTab> for SeriesTabCursor { | ||||
|     fn vtab(&self) -> &mut SeriesTab { | ||||
|         unsafe { &mut *(self.base.pVtab as *mut SeriesTab) } | ||||
|     } | ||||
|     fn filter(&mut self, | ||||
|               idx_num: libc::c_int, | ||||
|               _idx_str: Option<&str>, | ||||
|               args: &Values) | ||||
|               -> Result<()> { | ||||
|         let idx_num = QueryPlanFlags::from_bits_truncate(idx_num); | ||||
|         let mut i = 0; | ||||
|         if idx_num.contains(START) { | ||||
|             self.min_value = try!(args.get(i)); | ||||
|             i += 1; | ||||
|         } else { | ||||
|             self.min_value = 0; | ||||
|         } | ||||
|         if idx_num.contains(STOP) { | ||||
|             self.max_value = try!(args.get(i)); | ||||
|             i += 1; | ||||
|         } else { | ||||
|             self.max_value = 0xffffffff; | ||||
|         } | ||||
|         if idx_num.contains(STEP) { | ||||
|             self.step = try!(args.get(i)); | ||||
|             if self.step < 1 { | ||||
|                 self.step = 1; | ||||
|             } | ||||
|         } else { | ||||
|             self.step = 1; | ||||
|         }; | ||||
|         self.is_desc = idx_num.contains(DESC); | ||||
|         if self.is_desc { | ||||
|             self.value = self.max_value; | ||||
|             if self.step > 1 { | ||||
|                 self.value -= (self.max_value - self.min_value) % self.step; | ||||
|             } | ||||
|         } else { | ||||
|             self.value = self.min_value; | ||||
|         } | ||||
|         self.row_id = 0; | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn next(&mut self) -> Result<()> { | ||||
|         if self.is_desc { | ||||
|             self.value -= self.step; | ||||
|         } else { | ||||
|             self.value += self.step; | ||||
|         } | ||||
|         self.row_id += 1; | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn eof(&self) -> bool { | ||||
|         if self.is_desc { | ||||
|             self.value < self.min_value | ||||
|         } else { | ||||
|             self.value > self.max_value | ||||
|         } | ||||
|     } | ||||
|     fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()> { | ||||
|         let x = match i { | ||||
|             SERIES_COLUMN_START => self.min_value, | ||||
|             SERIES_COLUMN_STOP => self.max_value, | ||||
|             SERIES_COLUMN_STEP => self.step, | ||||
|             _ => self.value, | ||||
|         }; | ||||
|         ctx.set_result(&x); | ||||
|         Ok(()) | ||||
|     } | ||||
|     fn rowid(&self) -> Result<i64> { | ||||
|         Ok(self.row_id) | ||||
|     } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user