mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 22:08:55 +08:00 
			
		
		
		
	Make index constraints and values iterable
This commit is contained in:
		
							
								
								
									
										125
									
								
								src/vtab/mod.rs
									
									
									
									
									
								
							
							
						
						
									
										125
									
								
								src/vtab/mod.rs
									
									
									
									
									
								
							| @@ -4,13 +4,14 @@ use std::borrow::Cow::{self, Borrowed, Owned}; | |||||||
| use std::ffi::CString; | use std::ffi::CString; | ||||||
| use std::mem; | use std::mem; | ||||||
| use std::ptr; | use std::ptr; | ||||||
|  | use std::slice; | ||||||
| use libc; | use libc; | ||||||
|  |  | ||||||
| use {Connection, Error, Result, InnerConnection, str_to_cstring}; | use {Connection, Error, Result, InnerConnection, str_to_cstring}; | ||||||
| use error::error_from_sqlite_code; | use error::error_from_sqlite_code; | ||||||
| use ffi; | use ffi; | ||||||
| use functions::ToResult; | use functions::ToResult; | ||||||
| use types::FromSql; | use types::{FromSql, ValueRef}; | ||||||
|  |  | ||||||
| // let conn: Connection = ...; | // let conn: Connection = ...; | ||||||
| // let mod: Module = ...; // VTab builder | // let mod: Module = ...; // VTab builder | ||||||
| @@ -43,7 +44,8 @@ use types::FromSql; | |||||||
| /// Virtual table instance trait. | /// Virtual table instance trait. | ||||||
| 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 connect(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<()>; | ||||||
| @@ -67,36 +69,10 @@ bitflags! { | |||||||
| pub struct IndexInfo(*mut ffi::sqlite3_index_info); | pub struct IndexInfo(*mut ffi::sqlite3_index_info); | ||||||
|  |  | ||||||
| impl IndexInfo { | impl IndexInfo { | ||||||
|     /// Number of constraints |     pub fn constraints(&self) -> IndexConstraintIter { | ||||||
|     pub fn num_of_constraint(&self) -> usize { |         let constraints = | ||||||
|         unsafe { (*self.0).nConstraint as usize } |             unsafe { slice::from_raw_parts((*self.0).aConstraint, (*self.0).nConstraint as usize) }; | ||||||
|     } |         IndexConstraintIter { iter: constraints.iter() } | ||||||
|     /// Column constrained.  -1 for ROWID |  | ||||||
|     pub fn constraint_column(&self, constraint_idx: usize) -> libc::c_int { |  | ||||||
|         use std::slice; |  | ||||||
|         unsafe { |  | ||||||
|             let constraints = slice::from_raw_parts((*self.0).aConstraint, |  | ||||||
|                                                     (*self.0).nConstraint as usize); |  | ||||||
|             constraints[constraint_idx].iColumn |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     /// Constraint operator |  | ||||||
|     pub fn constraint_operator(&self, constraint_idx: usize) -> IndexConstraintOp { |  | ||||||
|         use std::slice; |  | ||||||
|         unsafe { |  | ||||||
|             let constraints = slice::from_raw_parts((*self.0).aConstraint, |  | ||||||
|                                                     (*self.0).nConstraint as usize); |  | ||||||
|             IndexConstraintOp::from_bits_truncate(constraints[constraint_idx].op) |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|     /// True if this constraint is usable |  | ||||||
|     pub fn is_constraint_usable(&self, constraint_idx: usize) -> bool { |  | ||||||
|         use std::slice; |  | ||||||
|         unsafe { |  | ||||||
|             let constraints = slice::from_raw_parts((*self.0).aConstraint, |  | ||||||
|                                                     (*self.0).nConstraint as usize); |  | ||||||
|             constraints[constraint_idx].usable != 0 |  | ||||||
|         } |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     /// Number of terms in the ORDER BY clause |     /// Number of terms in the ORDER BY clause | ||||||
| @@ -105,7 +81,6 @@ impl IndexInfo { | |||||||
|     } |     } | ||||||
|     /// Column number |     /// Column number | ||||||
|     pub fn order_by_column(&self, order_by_idx: usize) -> libc::c_int { |     pub fn order_by_column(&self, order_by_idx: usize) -> libc::c_int { | ||||||
|         use std::slice; |  | ||||||
|         unsafe { |         unsafe { | ||||||
|             let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize); |             let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize); | ||||||
|             order_bys[order_by_idx].iColumn |             order_bys[order_by_idx].iColumn | ||||||
| @@ -113,7 +88,6 @@ impl IndexInfo { | |||||||
|     } |     } | ||||||
|     /// True for DESC.  False for ASC. |     /// True for DESC.  False for ASC. | ||||||
|     pub fn is_order_by_desc(&self, order_by_idx: usize) -> bool { |     pub fn is_order_by_desc(&self, order_by_idx: usize) -> bool { | ||||||
|         use std::slice; |  | ||||||
|         unsafe { |         unsafe { | ||||||
|             let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize); |             let order_bys = slice::from_raw_parts((*self.0).aOrderBy, (*self.0).nOrderBy as usize); | ||||||
|             order_bys[order_by_idx].desc != 0 |             order_bys[order_by_idx].desc != 0 | ||||||
| @@ -122,7 +96,6 @@ impl IndexInfo { | |||||||
|  |  | ||||||
|     /// if `argv_index` > 0, constraint is part of argv to xFilter |     /// if `argv_index` > 0, constraint is part of argv to xFilter | ||||||
|     pub fn set_argv_index(&mut self, constraint_idx: usize, argv_index: libc::c_int) { |     pub fn set_argv_index(&mut self, constraint_idx: usize, argv_index: libc::c_int) { | ||||||
|         use std::slice; |  | ||||||
|         unsafe { |         unsafe { | ||||||
|             let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage, |             let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage, | ||||||
|                                                                   (*self.0).nConstraint as usize); |                                                                   (*self.0).nConstraint as usize); | ||||||
| @@ -131,7 +104,6 @@ impl IndexInfo { | |||||||
|     } |     } | ||||||
|     /// if `omit`, do not code a test for this constraint |     /// if `omit`, do not code a test for this constraint | ||||||
|     pub fn set_omit(&mut self, constraint_idx: usize, omit: bool) { |     pub fn set_omit(&mut self, constraint_idx: usize, omit: bool) { | ||||||
|         use std::slice; |  | ||||||
|         unsafe { |         unsafe { | ||||||
|             let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage, |             let mut constraint_usages = slice::from_raw_parts_mut((*self.0).aConstraintUsage, | ||||||
|                                                                   (*self.0).nConstraint as usize); |                                                                   (*self.0).nConstraint as usize); | ||||||
| @@ -163,6 +135,38 @@ impl IndexInfo { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | pub struct IndexConstraintIter<'a> { | ||||||
|  |     iter: slice::Iter<'a, ffi::Struct_sqlite3_index_constraint>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<'a> Iterator for IndexConstraintIter<'a> { | ||||||
|  |     type Item = IndexConstraint<'a>; | ||||||
|  |  | ||||||
|  |     fn next(&mut self) -> Option<IndexConstraint<'a>> { | ||||||
|  |         self.iter.next().map(|raw| IndexConstraint(raw)) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn size_hint(&self) -> (usize, Option<usize>) { | ||||||
|  |         self.iter.size_hint() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub struct IndexConstraint<'a>(&'a ffi::Struct_sqlite3_index_constraint); | ||||||
|  |  | ||||||
|  | impl<'a> IndexConstraint<'a> { | ||||||
|  |     /// Column constrained.  -1 for ROWID | ||||||
|  |     pub fn column(&self) -> libc::c_int { | ||||||
|  |         self.0.iColumn | ||||||
|  |     } | ||||||
|  |     /// Constraint operator | ||||||
|  |     pub fn operator(&self) -> IndexConstraintOp { | ||||||
|  |         IndexConstraintOp::from_bits_truncate(self.0.op) | ||||||
|  |     } | ||||||
|  |     /// True if this constraint is usable | ||||||
|  |     pub fn is_usable(&self) -> bool { | ||||||
|  |         self.0.usable != 0 | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
| /// Virtual table cursor trait. | /// Virtual table cursor trait. | ||||||
| pub trait VTabCursor<V: VTab<Self>>: Sized { | pub trait VTabCursor<V: VTab<Self>>: Sized { | ||||||
| @@ -172,9 +176,11 @@ pub trait VTabCursor<V: VTab<Self>>: Sized { | |||||||
|     fn filter(&mut self, idx_num: libc::c_int, idx_str: Option<&str>, args: &Values) -> Result<()>; |     fn filter(&mut self, idx_num: libc::c_int, idx_str: Option<&str>, args: &Values) -> Result<()>; | ||||||
|     /// Advance cursor to the next row of a result set initiated by `filter`. |     /// Advance cursor to the next row of a result set initiated by `filter`. | ||||||
|     fn next(&mut self) -> Result<()>; |     fn next(&mut self) -> Result<()>; | ||||||
|     /// Must return `false` if the cursor currently points to a valid row of data, or `true` otherwise. |     /// Must return `false` if the cursor currently points to a valid row of data, | ||||||
|  |     /// or `true` otherwise. | ||||||
|     fn eof(&self) -> bool; |     fn eof(&self) -> bool; | ||||||
|     /// Find the value for the `i`-th column of the current row. `i` is zero-based so the first column is numbered 0. |     /// Find the value for the `i`-th column of the current row. | ||||||
|  |     /// `i` is zero-based so the first column is numbered 0. | ||||||
|     /// May return its result back to SQLite using one of the specified `ctx`. |     /// May return its result back to SQLite using one of the specified `ctx`. | ||||||
|     fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()>; |     fn column(&self, ctx: &mut Context, i: libc::c_int) -> Result<()>; | ||||||
|     /// Return the rowid of row that the cursor is currently pointing at. |     /// Return the rowid of row that the cursor is currently pointing at. | ||||||
| @@ -185,7 +191,7 @@ pub trait VTabCursor<V: VTab<Self>>: Sized { | |||||||
| pub struct Context(*mut ffi::sqlite3_context); | pub struct Context(*mut ffi::sqlite3_context); | ||||||
|  |  | ||||||
| impl Context { | impl Context { | ||||||
|     pub fn set_result(&mut self, value: &ToResult) { |     pub fn set_result<T: ToResult>(&mut self, value: &T) { | ||||||
|         unsafe { |         unsafe { | ||||||
|             value.set_result(self.0); |             value.set_result(self.0); | ||||||
|         } |         } | ||||||
| @@ -206,7 +212,6 @@ impl<'a> Values<'a> { | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> { |     pub fn get<T: FromSql>(&self, idx: usize) -> Result<T> { | ||||||
|         use types::ValueRef; |  | ||||||
|         let arg = self.args[idx]; |         let arg = self.args[idx]; | ||||||
|         let value = unsafe { ValueRef::from_value(arg) }; |         let value = unsafe { ValueRef::from_value(arg) }; | ||||||
|         FromSql::column_result(value).map_err(|err| match err { |         FromSql::column_result(value).map_err(|err| match err { | ||||||
| @@ -214,6 +219,35 @@ impl<'a> Values<'a> { | |||||||
|             _ => err, |             _ => err, | ||||||
|         }) |         }) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     pub fn iter(&self) -> ValueIter { | ||||||
|  |         ValueIter { iter: self.args.iter() } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<'a> IntoIterator for &'a Values<'a> { | ||||||
|  |     type Item = ValueRef<'a>; | ||||||
|  |     type IntoIter = ValueIter<'a>; | ||||||
|  |  | ||||||
|  |     fn into_iter(self) -> ValueIter<'a> { | ||||||
|  |         self.iter() | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | pub struct ValueIter<'a> { | ||||||
|  |     iter: slice::Iter<'a, *mut ffi::sqlite3_value>, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | impl<'a> Iterator for ValueIter<'a> { | ||||||
|  |     type Item = ValueRef<'a>; | ||||||
|  |  | ||||||
|  |     fn next(&mut self) -> Option<ValueRef<'a>> { | ||||||
|  |         self.iter.next().map(|raw| { unsafe { ValueRef::from_value(*raw) } }) | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     fn size_hint(&self) -> (usize, Option<usize>) { | ||||||
|  |         self.iter.size_hint() | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| impl Connection { | impl Connection { | ||||||
| @@ -297,7 +331,8 @@ macro_rules! init_module { | |||||||
| static $module_name: ffi::sqlite3_module = ffi::sqlite3_module { | static $module_name: ffi::sqlite3_module = ffi::sqlite3_module { | ||||||
|     iVersion: 1, |     iVersion: 1, | ||||||
|     xCreate: $create, /* For eponymous-only virtual tables, the xCreate method is NULL */ |     xCreate: $create, /* For eponymous-only virtual tables, the xCreate method is NULL */ | ||||||
|     xConnect: Some($connect), /* 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($disconnect), |     xDisconnect: Some($disconnect), | ||||||
|     xDestroy: $destroy, |     xDestroy: $destroy, | ||||||
| @@ -495,8 +530,9 @@ pub unsafe fn set_err_msg(vtab: *mut ffi::sqlite3_vtab, err_msg: &str) { | |||||||
|     (*vtab).zErrMsg = mprintf(err_msg); |     (*vtab).zErrMsg = mprintf(err_msg); | ||||||
| } | } | ||||||
|  |  | ||||||
| /// To raise an error, the `column` method should use this method to set the error message and return the error code. | /// To raise an error, the `column` method should use this method to set the error message | ||||||
| pub unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> libc::c_int { | /// and return the error code. | ||||||
|  | unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) -> libc::c_int { | ||||||
|     use std::error::Error as StdError; |     use std::error::Error as StdError; | ||||||
|     match result { |     match result { | ||||||
|         Ok(_) => ffi::SQLITE_OK, |         Ok(_) => ffi::SQLITE_OK, | ||||||
| @@ -527,7 +563,8 @@ pub unsafe fn result_error<T>(ctx: *mut ffi::sqlite3_context, result: Result<T>) | |||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| // Space to hold this error message string must be obtained from an SQLite memory allocation function. | // Space to hold this error message string must be obtained | ||||||
|  | // from an SQLite memory allocation function. | ||||||
| pub fn mprintf(err_msg: &str) -> *mut ::libc::c_char { | pub fn mprintf(err_msg: &str) -> *mut ::libc::c_char { | ||||||
|     let c_format = CString::new("%s").unwrap(); |     let c_format = CString::new("%s").unwrap(); | ||||||
|     let c_err = CString::new(err_msg).unwrap(); |     let c_err = CString::new(err_msg).unwrap(); | ||||||
|   | |||||||
| @@ -1,5 +1,5 @@ | |||||||
| //! generate_series virtual table. | //! generate series virtual table. | ||||||
| //! Port of C [generate_series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c). | //! Port of C [generate series "function"](http://www.sqlite.org/cgi/src/finfo?name=ext/misc/series.c). | ||||||
| use std::default::Default; | use std::default::Default; | ||||||
| use libc; | use libc; | ||||||
|  |  | ||||||
| @@ -81,14 +81,14 @@ impl VTab<SeriesTabCursor> for SeriesTab { | |||||||
|         let mut stop_idx = None; |         let mut stop_idx = None; | ||||||
|         // Index of the step= constraint |         // Index of the step= constraint | ||||||
|         let mut step_idx = None; |         let mut step_idx = None; | ||||||
|         for i in 0..info.num_of_constraint() { |         for (i, constraint) in info.constraints().enumerate() { | ||||||
|             if !info.is_constraint_usable(i) { |             if !constraint.is_usable() { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|             if info.constraint_operator(i) != vtab::SQLITE_INDEX_CONSTRAINT_EQ { |             if constraint.operator() != vtab::SQLITE_INDEX_CONSTRAINT_EQ { | ||||||
|                 continue; |                 continue; | ||||||
|             } |             } | ||||||
|             match info.constraint_column(i) { |             match constraint.column() { | ||||||
|                 SERIES_COLUMN_START => { |                 SERIES_COLUMN_START => { | ||||||
|                     start_idx = Some(i); |                     start_idx = Some(i); | ||||||
|                     idx_num |= START; |                     idx_num |= START; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user