mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 22:08: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, | init_module!(CSV_MODULE, | ||||||
|              CSVTab, |              CSVTab, | ||||||
|              CSVTabCursor, |              CSVTabCursor, | ||||||
|              csv_create, |              Some(csv_connect), | ||||||
|  |              csv_connect, | ||||||
|              csv_best_index, |              csv_best_index, | ||||||
|              csv_destroy, |              csv_disconnect, | ||||||
|  |              Some(csv_disconnect), | ||||||
|              csv_open, |              csv_open, | ||||||
|              csv_close, |              csv_close, | ||||||
|              csv_filter, |              csv_filter, | ||||||
| @@ -56,7 +58,7 @@ impl CSVTab { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl VTab<CSVTabCursor> for 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 { |         if args.len() < 4 { | ||||||
|             return Err(Error::ModuleError("no CSV file specified".to_owned())); |             return Err(Error::ModuleError("no CSV file specified".to_owned())); | ||||||
|         } |         } | ||||||
|   | |||||||
| @@ -1,4 +1,5 @@ | |||||||
| //! Int array virtual table. | //! 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::cell::RefCell; | ||||||
| use std::default::Default; | use std::default::Default; | ||||||
| use std::mem; | use std::mem; | ||||||
| @@ -39,9 +40,11 @@ pub fn drop_int_array(conn: &Connection, name: &str) -> Result<()> { | |||||||
| init_module!(INT_ARRAY_MODULE, | init_module!(INT_ARRAY_MODULE, | ||||||
|              IntArrayVTab, |              IntArrayVTab, | ||||||
|              IntArrayVTabCursor, |              IntArrayVTabCursor, | ||||||
|              int_array_create, |              Some(int_array_connect), | ||||||
|  |              int_array_connect, | ||||||
|              int_array_best_index, |              int_array_best_index, | ||||||
|              int_array_destroy, |              int_array_disconnect, | ||||||
|  |              Some(int_array_disconnect), | ||||||
|              int_array_open, |              int_array_open, | ||||||
|              int_array_close, |              int_array_close, | ||||||
|              int_array_filter, |              int_array_filter, | ||||||
| @@ -58,10 +61,10 @@ struct IntArrayVTab { | |||||||
| } | } | ||||||
|  |  | ||||||
| impl VTab<IntArrayVTabCursor> for IntArrayVTab { | impl VTab<IntArrayVTabCursor> for IntArrayVTab { | ||||||
|     fn create(db: *mut ffi::sqlite3, |     fn connect(db: *mut ffi::sqlite3, | ||||||
|               aux: *mut libc::c_void, |                aux: *mut libc::c_void, | ||||||
|               _args: &[&[u8]]) |                _args: &[&[u8]]) | ||||||
|               -> Result<IntArrayVTab> { |                -> Result<IntArrayVTab> { | ||||||
|         let array = unsafe { mem::transmute(aux) }; |         let array = unsafe { mem::transmute(aux) }; | ||||||
|         let vtab = IntArrayVTab { |         let vtab = IntArrayVTab { | ||||||
|             base: Default::default(), |             base: Default::default(), | ||||||
|   | |||||||
| @@ -44,7 +44,7 @@ use types::FromSql; | |||||||
| pub trait VTab<C: VTabCursor<Self>>: Sized { | pub trait VTab<C: VTabCursor<Self>>: Sized { | ||||||
|     /// Create a new instance of a virtual table in response to a CREATE VIRTUAL TABLE statement. |     /// 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. |     /// 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. |     /// Determine the best way to access the virtual table. | ||||||
|     fn best_index(&self, info: &mut IndexInfo) -> Result<()>; |     fn best_index(&self, info: &mut IndexInfo) -> Result<()>; | ||||||
|     /// Create a new cursor used for accessing a virtual table. |     /// Create a new cursor used for accessing a virtual table. | ||||||
| @@ -90,7 +90,7 @@ impl IndexInfo { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
|     /// True if this constraint is usable |     /// 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; |         use std::slice; | ||||||
|         unsafe { |         unsafe { | ||||||
|             let constraints = slice::from_raw_parts((*self.0).aConstraint, |             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_export] | ||||||
| macro_rules! init_module { | macro_rules! init_module { | ||||||
|     ($module_name: ident, $vtab: ident, $cursor: ty, |     ($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, |         $open: ident, $close: ident, | ||||||
|         $filter: ident, $next: ident, $eof: ident, |         $filter: ident, $next: ident, $eof: ident, | ||||||
|         $column: ident, $rowid: ident) => { |         $column: ident, $rowid: ident) => { | ||||||
|  |  | ||||||
| static $module_name: ffi::sqlite3_module = ffi::sqlite3_module { | static $module_name: ffi::sqlite3_module = ffi::sqlite3_module { | ||||||
|     iVersion: 1, |     iVersion: 1, | ||||||
|     xCreate: Some($create), |     xCreate: $create, | ||||||
|     xConnect: Some($create), /* A virtual table is eponymous if its xCreate method is the exact same function as the xConnect method */ |     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), |     xBestIndex: Some($best_index), | ||||||
|     xDisconnect: Some($destroy), |     xDisconnect: Some($disconnect), | ||||||
|     xDestroy: Some($destroy), |     xDestroy: $destroy, | ||||||
|     xOpen: Some($open), |     xOpen: Some($open), | ||||||
|     xClose: Some($close), |     xClose: Some($close), | ||||||
|     xFilter: Some($filter), |     xFilter: Some($filter), | ||||||
| @@ -319,7 +320,7 @@ static $module_name: ffi::sqlite3_module = ffi::sqlite3_module { | |||||||
|     xRollbackTo: None, |     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, |                               aux: *mut libc::c_void, | ||||||
|                               argc: libc::c_int, |                               argc: libc::c_int, | ||||||
|                               argv: *const *const libc::c_char, |                               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| { |     let vec = args.iter().map(|cs| { | ||||||
|         CStr::from_ptr(*cs).to_bytes() |         CStr::from_ptr(*cs).to_bytes() | ||||||
|     }).collect::<Vec<_>>(); |     }).collect::<Vec<_>>(); | ||||||
|     match $vtab::create(db, aux, &vec[..]) { |     match $vtab::connect(db, aux, &vec[..]) { | ||||||
|         Ok(vtab) => { |         Ok(vtab) => { | ||||||
|             let boxed_vtab: *mut $vtab = Box::into_raw(Box::new(vtab)); |             let boxed_vtab: *mut $vtab = Box::into_raw(Box::new(vtab)); | ||||||
|             *pp_vtab = boxed_vtab as *mut ffi::sqlite3_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 vtab = vtab as *mut $vtab; | ||||||
|     let _: Box<$vtab> = Box::from_raw(vtab); |     let _: Box<$vtab> = Box::from_raw(vtab); | ||||||
|     ffi::SQLITE_OK |     ffi::SQLITE_OK | ||||||
| @@ -536,3 +537,4 @@ pub fn mprintf(err_msg: &str) -> *mut ::libc::c_char { | |||||||
| pub mod int_array; | pub mod int_array; | ||||||
| #[cfg(feature = "csvtab")] | #[cfg(feature = "csvtab")] | ||||||
| pub mod 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