mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 05:48:56 +08:00 
			
		
		
		
	Merge remote-tracking branch 'origin/master' into sub_type
This commit is contained in:
		
							
								
								
									
										12
									
								
								src/busy.rs
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/busy.rs
									
									
									
									
									
								
							| @@ -90,7 +90,7 @@ mod test { | ||||
|     use std::thread; | ||||
|     use std::time::Duration; | ||||
|  | ||||
|     use crate::{Connection, Error, ErrorCode, Result, TransactionBehavior}; | ||||
|     use crate::{Connection, ErrorCode, Result, TransactionBehavior}; | ||||
|  | ||||
|     #[test] | ||||
|     fn test_default_busy() -> Result<()> { | ||||
| @@ -101,12 +101,10 @@ mod test { | ||||
|         let tx1 = db1.transaction_with_behavior(TransactionBehavior::Exclusive)?; | ||||
|         let db2 = Connection::open(&path)?; | ||||
|         let r: Result<()> = db2.query_row("PRAGMA schema_version", [], |_| unreachable!()); | ||||
|         match r.unwrap_err() { | ||||
|             Error::SqliteFailure(err, _) => { | ||||
|                 assert_eq!(err.code, ErrorCode::DatabaseBusy); | ||||
|             } | ||||
|             err => panic!("Unexpected error {}", err), | ||||
|         } | ||||
|         assert_eq!( | ||||
|             r.unwrap_err().sqlite_error_code(), | ||||
|             Some(ErrorCode::DatabaseBusy) | ||||
|         ); | ||||
|         tx1.rollback() | ||||
|     } | ||||
|  | ||||
|   | ||||
							
								
								
									
										67
									
								
								src/error.rs
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								src/error.rs
									
									
									
									
									
								
							| @@ -128,6 +128,19 @@ pub enum Error { | ||||
|     #[cfg(feature = "blob")] | ||||
|     #[cfg_attr(docsrs, doc(cfg(feature = "blob")))] | ||||
|     BlobSizeError, | ||||
|     /// Error referencing a specific token in the input SQL | ||||
|     #[cfg(feature = "modern_sqlite")] // 3.38.0 | ||||
|     #[cfg_attr(docsrs, doc(cfg(feature = "modern_sqlite")))] | ||||
|     SqlInputError { | ||||
|         /// error code | ||||
|         error: ffi::Error, | ||||
|         /// error message | ||||
|         msg: String, | ||||
|         /// SQL input | ||||
|         sql: String, | ||||
|         /// byte offset of the start of invalid token | ||||
|         offset: c_int, | ||||
|     }, | ||||
| } | ||||
|  | ||||
| impl PartialEq for Error { | ||||
| @@ -172,6 +185,21 @@ impl PartialEq for Error { | ||||
|             } | ||||
|             #[cfg(feature = "blob")] | ||||
|             (Error::BlobSizeError, Error::BlobSizeError) => true, | ||||
|             #[cfg(feature = "modern_sqlite")] | ||||
|             ( | ||||
|                 Error::SqlInputError { | ||||
|                     error: e1, | ||||
|                     msg: m1, | ||||
|                     sql: s1, | ||||
|                     offset: o1, | ||||
|                 }, | ||||
|                 Error::SqlInputError { | ||||
|                     error: e2, | ||||
|                     msg: m2, | ||||
|                     sql: s2, | ||||
|                     offset: o2, | ||||
|                 }, | ||||
|             ) => e1 == e2 && m1 == m2 && s1 == s2 && o1 == o2, | ||||
|             (..) => false, | ||||
|         } | ||||
|     } | ||||
| @@ -281,9 +309,15 @@ impl fmt::Display for Error { | ||||
|             #[cfg(feature = "functions")] | ||||
|             Error::GetAuxWrongType => write!(f, "get_aux called with wrong type"), | ||||
|             Error::MultipleStatement => write!(f, "Multiple statements provided"), | ||||
|  | ||||
|             #[cfg(feature = "blob")] | ||||
|             Error::BlobSizeError => "Blob size is insufficient".fmt(f), | ||||
|             #[cfg(feature = "modern_sqlite")] | ||||
|             Error::SqlInputError { | ||||
|                 ref msg, | ||||
|                 offset, | ||||
|                 ref sql, | ||||
|                 .. | ||||
|             } => write!(f, "{} in {} at offset {}", msg, sql, offset), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -331,6 +365,8 @@ impl error::Error for Error { | ||||
|  | ||||
|             #[cfg(feature = "blob")] | ||||
|             Error::BlobSizeError => None, | ||||
|             #[cfg(feature = "modern_sqlite")] | ||||
|             Error::SqlInputError { ref error, .. } => Some(error), | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -371,6 +407,35 @@ pub unsafe fn error_from_handle(db: *mut ffi::sqlite3, code: c_int) -> Error { | ||||
|     error_from_sqlite_code(code, message) | ||||
| } | ||||
|  | ||||
| #[cold] | ||||
| #[cfg(not(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher"))))] // SQLite >= 3.38.0 | ||||
| pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, _sql: &str) -> Error { | ||||
|     error_from_handle(db, code) | ||||
| } | ||||
|  | ||||
| #[cold] | ||||
| #[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0 | ||||
| pub unsafe fn error_with_offset(db: *mut ffi::sqlite3, code: c_int, sql: &str) -> Error { | ||||
|     if db.is_null() { | ||||
|         error_from_sqlite_code(code, None) | ||||
|     } else { | ||||
|         let error = ffi::Error::new(code); | ||||
|         let msg = errmsg_to_string(ffi::sqlite3_errmsg(db)); | ||||
|         if ffi::ErrorCode::Unknown == error.code { | ||||
|             let offset = ffi::sqlite3_error_offset(db); | ||||
|             if offset >= 0 { | ||||
|                 return Error::SqlInputError { | ||||
|                     error, | ||||
|                     msg, | ||||
|                     sql: sql.to_owned(), | ||||
|                     offset, | ||||
|                 }; | ||||
|             } | ||||
|         } | ||||
|         Error::SqliteFailure(error, Some(msg)) | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub fn check(code: c_int) -> Result<()> { | ||||
|     if code != crate::ffi::SQLITE_OK { | ||||
|         Err(crate::error::error_from_sqlite_code(code, None)) | ||||
|   | ||||
| @@ -10,7 +10,7 @@ use std::sync::{Arc, Mutex}; | ||||
| use super::ffi; | ||||
| use super::str_for_sqlite; | ||||
| use super::{Connection, InterruptHandle, OpenFlags, Result}; | ||||
| use crate::error::{error_from_handle, error_from_sqlite_code, Error}; | ||||
| use crate::error::{error_from_handle, error_from_sqlite_code, error_with_offset, Error}; | ||||
| use crate::raw_statement::RawStatement; | ||||
| use crate::statement::Statement; | ||||
| use crate::version::version_number; | ||||
| @@ -256,7 +256,9 @@ impl InnerConnection { | ||||
|             rc | ||||
|         }; | ||||
|         // If there is an error, *ppStmt is set to NULL. | ||||
|         self.decode_result(r)?; | ||||
|         if r != ffi::SQLITE_OK { | ||||
|             return Err(unsafe { error_with_offset(self.db, r, sql) }); | ||||
|         } | ||||
|         // If the input text contains no SQL (if the input is an empty string or a | ||||
|         // comment) then *ppStmt is set to NULL. | ||||
|         let c_stmt: *mut ffi::sqlite3_stmt = c_stmt; | ||||
| @@ -360,6 +362,12 @@ impl InnerConnection { | ||||
|             )), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     #[inline] | ||||
|     #[cfg(feature = "release_memory")] | ||||
|     pub fn release_memory(&self) -> Result<()> { | ||||
|         self.decode_result(unsafe { ffi::sqlite3_db_release_memory(self.db) }) | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Drop for InnerConnection { | ||||
|   | ||||
							
								
								
									
										31
									
								
								src/lib.rs
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								src/lib.rs
									
									
									
									
									
								
							| @@ -384,9 +384,8 @@ impl Connection { | ||||
|     /// | ||||
|     /// - Open the database for both reading or writing. | ||||
|     /// - Create the database if one does not exist at the path. | ||||
|     /// - Allow the filename to be interpreted as a URI (see | ||||
|     ///   <https://www.sqlite.org/uri.html#uri_filenames_in_sqlite> for | ||||
|     ///   details). | ||||
|     /// - Allow the filename to be interpreted as a URI (see <https://www.sqlite.org/uri.html#uri_filenames_in_sqlite> | ||||
|     ///   for details). | ||||
|     /// - Disables the use of a per-connection mutex. | ||||
|     /// | ||||
|     ///     Rusqlite enforces thread-safety at compile time, so additional | ||||
| @@ -596,6 +595,16 @@ impl Connection { | ||||
|         self.path.as_deref() | ||||
|     } | ||||
|  | ||||
|     /// Attempts to free as much heap memory as possible from the database | ||||
|     /// connection. | ||||
|     /// | ||||
|     /// This calls [`sqlite3_db_release_memory`](https://www.sqlite.org/c3ref/db_release_memory.html). | ||||
|     #[inline] | ||||
|     #[cfg(feature = "release_memory")] | ||||
|     pub fn release_memory(&self) -> Result<()> { | ||||
|         self.db.borrow_mut().release_memory() | ||||
|     } | ||||
|  | ||||
|     /// Convenience method to prepare and execute a single SQL statement with | ||||
|     /// named parameter(s). | ||||
|     /// | ||||
| @@ -1289,7 +1298,7 @@ mod test { | ||||
|         let filename = "no_such_file.db"; | ||||
|         let result = Connection::open_with_flags(filename, OpenFlags::SQLITE_OPEN_READ_ONLY); | ||||
|         assert!(result.is_err()); | ||||
|         let err = result.err().unwrap(); | ||||
|         let err = result.unwrap_err(); | ||||
|         if let Error::SqliteFailure(e, Some(msg)) = err { | ||||
|             assert_eq!(ErrorCode::CannotOpen, e.code); | ||||
|             assert_eq!(ffi::SQLITE_CANTOPEN, e.extended_code); | ||||
| @@ -1742,14 +1751,10 @@ mod test { | ||||
|  | ||||
|         let result: Result<Vec<i32>> = stmt.query([])?.map(|r| r.get(0)).collect(); | ||||
|  | ||||
|         match result.unwrap_err() { | ||||
|             Error::SqliteFailure(err, _) => { | ||||
|                 assert_eq!(err.code, ErrorCode::OperationInterrupted); | ||||
|             } | ||||
|             err => { | ||||
|                 panic!("Unexpected error {}", err); | ||||
|             } | ||||
|         } | ||||
|         assert_eq!( | ||||
|             result.unwrap_err().sqlite_error_code(), | ||||
|             Some(ErrorCode::OperationInterrupted) | ||||
|         ); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
| @@ -2093,7 +2098,7 @@ mod test { | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     #[cfg(all(feature = "bundled", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.35.0 | ||||
|     #[cfg(feature = "modern_sqlite")] | ||||
|     fn test_returning() -> Result<()> { | ||||
|         let db = Connection::open_in_memory()?; | ||||
|         db.execute_batch("CREATE TABLE foo(x INTEGER PRIMARY KEY)")?; | ||||
|   | ||||
| @@ -327,7 +327,7 @@ macro_rules! impl_for_array_ref { | ||||
| // don't really think it matters -- users who hit that can use `params!` anyway. | ||||
| impl_for_array_ref!( | ||||
|     1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ||||
|     18 19 20 21 22 23 24 25 26 27 29 30 31 32 | ||||
|     18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | ||||
| ); | ||||
|  | ||||
| /// Adapter type which allows any iterator over [`ToSql`] values to implement | ||||
|   | ||||
| @@ -1535,4 +1535,21 @@ mod test { | ||||
|         assert_eq!(0, stmt.is_explain()); | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     #[cfg(all(feature = "modern_sqlite", not(feature = "bundled-sqlcipher")))] // SQLite >= 3.38.0 | ||||
|     fn test_error_offset() -> Result<()> { | ||||
|         use crate::ffi::ErrorCode; | ||||
|         let db = Connection::open_in_memory()?; | ||||
|         let r = db.execute_batch("SELECT CURRENT_TIMESTANP;"); | ||||
|         assert!(r.is_err()); | ||||
|         match r.unwrap_err() { | ||||
|             Error::SqlInputError { error, offset, .. } => { | ||||
|                 assert_eq!(error.code, ErrorCode::Unknown); | ||||
|                 assert_eq!(offset, 7); | ||||
|             } | ||||
|             err => panic!("Unexpected error {}", err), | ||||
|         } | ||||
|         Ok(()) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -87,6 +87,7 @@ pub struct Transaction<'conn> { | ||||
| ///     sp.commit() | ||||
| /// } | ||||
| /// ``` | ||||
| #[derive(Debug)] | ||||
| pub struct Savepoint<'conn> { | ||||
|     conn: &'conn Connection, | ||||
|     name: String, | ||||
|   | ||||
| @@ -110,7 +110,7 @@ pub struct Null; | ||||
|  | ||||
| /// SQLite data types. | ||||
| /// See [Fundamental Datatypes](https://sqlite.org/c3ref/c_blob.html). | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[derive(Clone, Debug, PartialEq, Eq)] | ||||
| pub enum Type { | ||||
|     /// NULL | ||||
|     Null, | ||||
| @@ -272,85 +272,67 @@ mod test { | ||||
|         // check some invalid types | ||||
|  | ||||
|         // 0 is actually a blob (Vec<u8>) | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, c_int>(0).err().unwrap() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, c_int>(0).err().unwrap() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type(row.get::<_, c_int>(0).unwrap_err())); | ||||
|         assert!(is_invalid_column_type(row.get::<_, c_int>(0).unwrap_err())); | ||||
|         assert!(is_invalid_column_type(row.get::<_, i64>(0).err().unwrap())); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, c_double>(0).err().unwrap() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, String>(0).err().unwrap() | ||||
|             row.get::<_, c_double>(0).unwrap_err() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type(row.get::<_, String>(0).unwrap_err())); | ||||
|         #[cfg(feature = "time")] | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, time::OffsetDateTime>(0).err().unwrap() | ||||
|             row.get::<_, time::OffsetDateTime>(0).unwrap_err() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, Option<c_int>>(0).err().unwrap() | ||||
|             row.get::<_, Option<c_int>>(0).unwrap_err() | ||||
|         )); | ||||
|  | ||||
|         // 1 is actually a text (String) | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, c_int>(1).err().unwrap() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type(row.get::<_, c_int>(1).unwrap_err())); | ||||
|         assert!(is_invalid_column_type(row.get::<_, i64>(1).err().unwrap())); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, c_double>(1).err().unwrap() | ||||
|             row.get::<_, c_double>(1).unwrap_err() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, Vec<u8>>(1).err().unwrap() | ||||
|             row.get::<_, Vec<u8>>(1).unwrap_err() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, Option<c_int>>(1).err().unwrap() | ||||
|             row.get::<_, Option<c_int>>(1).unwrap_err() | ||||
|         )); | ||||
|  | ||||
|         // 2 is actually an integer | ||||
|         assert!(is_invalid_column_type(row.get::<_, String>(2).unwrap_err())); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, String>(2).err().unwrap() | ||||
|             row.get::<_, Vec<u8>>(2).unwrap_err() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, Vec<u8>>(2).err().unwrap() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, Option<String>>(2).err().unwrap() | ||||
|             row.get::<_, Option<String>>(2).unwrap_err() | ||||
|         )); | ||||
|  | ||||
|         // 3 is actually a float (c_double) | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, c_int>(3).err().unwrap() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type(row.get::<_, c_int>(3).unwrap_err())); | ||||
|         assert!(is_invalid_column_type(row.get::<_, i64>(3).err().unwrap())); | ||||
|         assert!(is_invalid_column_type(row.get::<_, String>(3).unwrap_err())); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, String>(3).err().unwrap() | ||||
|             row.get::<_, Vec<u8>>(3).unwrap_err() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, Vec<u8>>(3).err().unwrap() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, Option<c_int>>(3).err().unwrap() | ||||
|             row.get::<_, Option<c_int>>(3).unwrap_err() | ||||
|         )); | ||||
|  | ||||
|         // 4 is actually NULL | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, c_int>(4).err().unwrap() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type(row.get::<_, c_int>(4).unwrap_err())); | ||||
|         assert!(is_invalid_column_type(row.get::<_, i64>(4).err().unwrap())); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, c_double>(4).err().unwrap() | ||||
|             row.get::<_, c_double>(4).unwrap_err() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type(row.get::<_, String>(4).unwrap_err())); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, String>(4).err().unwrap() | ||||
|         )); | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, Vec<u8>>(4).err().unwrap() | ||||
|             row.get::<_, Vec<u8>>(4).unwrap_err() | ||||
|         )); | ||||
|         #[cfg(feature = "time")] | ||||
|         assert!(is_invalid_column_type( | ||||
|             row.get::<_, time::OffsetDateTime>(4).err().unwrap() | ||||
|             row.get::<_, time::OffsetDateTime>(4).unwrap_err() | ||||
|         )); | ||||
|         Ok(()) | ||||
|     } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user