mirror of
				https://github.com/isar/rusqlite.git
				synced 2025-10-31 05:48:56 +08:00 
			
		
		
		
	Add ToSql implementations for u64 and usize
This commit is contained in:
		
				
					committed by
					
						 Thom Chiovoloni
						Thom Chiovoloni
					
				
			
			
				
	
			
			
			
						parent
						
							8841187717
						
					
				
				
					commit
					b0ade73f43
				
			| @@ -11,22 +11,22 @@ | ||||
| //! The number situation is a little complicated due to the fact that all | ||||
| //! numbers in SQLite are stored as `INTEGER` (`i64`) or `REAL` (`f64`). | ||||
| //! | ||||
| //! `ToSql` cannot fail and is therefore implemented for all number types that | ||||
| //! can be losslessly converted to one of these types, i.e. `u8`, `u16`, `u32`, | ||||
| //! `i8`, `i16`, `i32`, `i64`, `isize`, `f32` and `f64`. It is *not* implemented | ||||
| //! for `u64` or `usize`. | ||||
| //! | ||||
| //! `FromSql` can fail, and is implemented for all primitive number types, | ||||
| //! however you may get a runtime error or rounding depending on the types | ||||
| //! and values. | ||||
| //! `ToSql` and `FromSql` are implemented for all primitive number types. | ||||
| //! `FromSql` has different behaviour depending on the SQL and Rust types, and | ||||
| //! the value. | ||||
| //! | ||||
| //! * `INTEGER` to integer: returns an `Error::IntegralValueOutOfRange` error | ||||
| //!   if the value does not fit. | ||||
| //!   if the value does not fit in the Rust type. | ||||
| //! * `REAL` to integer: always returns an `Error::InvalidColumnType` error. | ||||
| //! * `INTEGER` to float: casts using `as` operator. Never fails. | ||||
| //! * `REAL` to float: casts using `as` operator. Never fails. | ||||
| //! | ||||
| //! Additionally, if the `time` feature is enabled, implementations are | ||||
| //! `ToSql` always succeeds except when storing a `u64` or `usize` value that | ||||
| //! cannot fit in an `INTEGER` (`i64`). Also note that SQLite ignores column | ||||
| //! types, so if you store an `i64` in a column with type `REAL` it will be | ||||
| //! stored as an `INTEGER`, not a `REAL`. | ||||
| //! | ||||
| //! If the `time` feature is enabled, implementations are | ||||
| //! provided for `time::OffsetDateTime` that use the RFC 3339 date/time format, | ||||
| //! `"%Y-%m-%dT%H:%M:%S.%fZ"`, to store time values as strings.  These values | ||||
| //! can be parsed by SQLite's builtin | ||||
| @@ -398,7 +398,7 @@ mod test { | ||||
|             assert_eq!(res.unwrap(), $expected_value); | ||||
|             $db_etc.delete_statement.execute(NO_PARAMS).unwrap(); | ||||
|         }; | ||||
|         ($db_etc:ident, $insert_value:expr, $get_type:ty, expect_error) => { | ||||
|         ($db_etc:ident, $insert_value:expr, $get_type:ty, expect_from_sql_error) => { | ||||
|             $db_etc | ||||
|                 .insert_statement | ||||
|                 .execute(params![$insert_value]) | ||||
| @@ -409,6 +409,12 @@ mod test { | ||||
|             res.unwrap_err(); | ||||
|             $db_etc.delete_statement.execute(NO_PARAMS).unwrap(); | ||||
|         }; | ||||
|         ($db_etc:ident, $insert_value:expr, $get_type:ty, expect_to_sql_error) => { | ||||
|             $db_etc | ||||
|                 .insert_statement | ||||
|                 .execute(params![$insert_value]) | ||||
|                 .unwrap_err(); | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -446,22 +452,28 @@ mod test { | ||||
|         test_conversion!(db_etc, i64::MIN, i64, expect i64::MIN); | ||||
|         test_conversion!(db_etc, i64::MAX, i64, expect i64::MAX); | ||||
|         test_conversion!(db_etc, i64::MAX, u64, expect i64::MAX as u64); | ||||
|         test_conversion!(db_etc, 100usize, usize, expect 100usize); | ||||
|         test_conversion!(db_etc, 100u64, u64, expect 100u64); | ||||
|         test_conversion!(db_etc, i64::MAX as u64, u64, expect i64::MAX as u64); | ||||
|  | ||||
|         // Out-of-range integral conversions. | ||||
|         test_conversion!(db_etc, 200u8, i8, expect_error); | ||||
|         test_conversion!(db_etc, 400u16, i8, expect_error); | ||||
|         test_conversion!(db_etc, 400u16, u8, expect_error); | ||||
|         test_conversion!(db_etc, -1i8, u8, expect_error); | ||||
|         test_conversion!(db_etc, i64::MIN, u64, expect_error); | ||||
|         test_conversion!(db_etc, 200u8, i8, expect_from_sql_error); | ||||
|         test_conversion!(db_etc, 400u16, i8, expect_from_sql_error); | ||||
|         test_conversion!(db_etc, 400u16, u8, expect_from_sql_error); | ||||
|         test_conversion!(db_etc, -1i8, u8, expect_from_sql_error); | ||||
|         test_conversion!(db_etc, i64::MIN, u64, expect_from_sql_error); | ||||
|         test_conversion!(db_etc, u64::MAX, i64, expect_to_sql_error); | ||||
|         test_conversion!(db_etc, u64::MAX, u64, expect_to_sql_error); | ||||
|         test_conversion!(db_etc, i64::MAX as u64 + 1, u64, expect_to_sql_error); | ||||
|  | ||||
|         // Integer to float, always works. | ||||
|         // FromSql integer to float, always works. | ||||
|         test_conversion!(db_etc, i64::MIN, f32, expect i64::MIN as f32); | ||||
|         test_conversion!(db_etc, i64::MAX, f32, expect i64::MAX as f32); | ||||
|         test_conversion!(db_etc, i64::MIN, f64, expect i64::MIN as f64); | ||||
|         test_conversion!(db_etc, i64::MAX, f64, expect i64::MAX as f64); | ||||
|  | ||||
|         // Float to int conversion, never works even if the actual value is an | ||||
|         // integer. | ||||
|         test_conversion!(db_etc, 0f64, i64, expect_error); | ||||
|         // FromSql float to int conversion, never works even if the actual value | ||||
|         // is an integer. | ||||
|         test_conversion!(db_etc, 0f64, i64, expect_from_sql_error); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,10 +1,11 @@ | ||||
| use super::{Null, Value, ValueRef}; | ||||
| #[cfg(feature = "array")] | ||||
| use crate::vtab::array::Array; | ||||
| use crate::Result; | ||||
| use crate::{Error, Result}; | ||||
| use std::borrow::Cow; | ||||
| use std::convert::TryFrom; | ||||
|  | ||||
| /// `ToSqlOutput` represents the possible output types for implementors of the | ||||
| /// `ToSqlOutput` represents the possible output types for implementers of the | ||||
| /// `ToSql` trait. | ||||
| #[derive(Clone, Debug, PartialEq)] | ||||
| #[non_exhaustive] | ||||
| @@ -86,7 +87,8 @@ impl ToSql for ToSqlOutput<'_> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// A trait for types that can be converted into SQLite values. | ||||
| /// A trait for types that can be converted into SQLite values. Returns | ||||
| /// `Error::ToSqlConversionFailure` if the conversion fails. | ||||
| pub trait ToSql { | ||||
|     /// Converts Rust value to SQLite value | ||||
|     fn to_sql(&self) -> Result<ToSqlOutput<'_>>; | ||||
| @@ -157,6 +159,25 @@ to_sql_self!(i128); | ||||
| #[cfg(feature = "uuid")] | ||||
| to_sql_self!(uuid::Uuid); | ||||
|  | ||||
| macro_rules! to_sql_self_fallible( | ||||
|     ($t:ty) => ( | ||||
|         impl ToSql for $t { | ||||
|             fn to_sql(&self) -> Result<ToSqlOutput<'_>> { | ||||
|                 Ok(ToSqlOutput::Owned(Value::Integer( | ||||
|                     i64::try_from(*self).map_err( | ||||
|                         // TODO: Include the values in the error message. | ||||
|                         |err| Error::ToSqlConversionFailure(err.into()) | ||||
|                     )? | ||||
|                 ))) | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
| ); | ||||
|  | ||||
| // Special implementations for usize and u64 because these conversions can fail. | ||||
| to_sql_self_fallible!(u64); | ||||
| to_sql_self_fallible!(usize); | ||||
|  | ||||
| impl<T: ?Sized> ToSql for &'_ T | ||||
| where | ||||
|     T: ToSql, | ||||
|   | ||||
		Reference in New Issue
	
	Block a user