mirror of
https://github.com/isar/rusqlite.git
synced 2025-02-01 05:30:50 +08:00
Merge pull request #1313 from itsxaos/nonzero
Implement FromSql & ToSql for std::num::NonZero types
This commit is contained in:
commit
5e079e509c
@ -96,6 +96,15 @@ macro_rules! from_sql_integral(
|
|||||||
i.try_into().map_err(|_| FromSqlError::OutOfRange(i))
|
i.try_into().map_err(|_| FromSqlError::OutOfRange(i))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
(non_zero $nz:ty, $z:ty) => (
|
||||||
|
impl FromSql for $nz {
|
||||||
|
#[inline]
|
||||||
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
|
let i = <$z>::column_result(value)?;
|
||||||
|
<$nz>::new(i).ok_or(FromSqlError::OutOfRange(0))
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -110,6 +119,22 @@ from_sql_integral!(u32);
|
|||||||
from_sql_integral!(u64);
|
from_sql_integral!(u64);
|
||||||
from_sql_integral!(usize);
|
from_sql_integral!(usize);
|
||||||
|
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroIsize, isize);
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroI8, i8);
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroI16, i16);
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroI32, i32);
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroI64, i64);
|
||||||
|
#[cfg(feature = "i128_blob")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroI128, i128);
|
||||||
|
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroUsize, usize);
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroU8, u8);
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroU16, u16);
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroU32, u32);
|
||||||
|
from_sql_integral!(non_zero std::num::NonZeroU64, u64);
|
||||||
|
// std::num::NonZeroU128 is not supported since u128 isn't either
|
||||||
|
|
||||||
impl FromSql for i64 {
|
impl FromSql for i64 {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
|
||||||
@ -273,4 +298,70 @@ mod test {
|
|||||||
check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
|
check_ranges::<u32>(&db, &[-2, -1, 4_294_967_296], &[0, 1, 4_294_967_295]);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nonzero_ranges() -> Result<()> {
|
||||||
|
let db = Connection::open_in_memory()?;
|
||||||
|
|
||||||
|
macro_rules! check_ranges {
|
||||||
|
($nz:ty, $out_of_range:expr, $in_range:expr) => {
|
||||||
|
for &n in $out_of_range {
|
||||||
|
assert_eq!(
|
||||||
|
db.query_row("SELECT ?1", [n], |r| r.get::<_, $nz>(0)),
|
||||||
|
Err(Error::IntegralValueOutOfRange(0, n)),
|
||||||
|
"{}",
|
||||||
|
std::any::type_name::<$nz>()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
for &n in $in_range {
|
||||||
|
let non_zero = <$nz>::new(n).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
Ok(non_zero),
|
||||||
|
db.query_row("SELECT ?1", [non_zero], |r| r.get::<_, $nz>(0))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
check_ranges!(std::num::NonZeroI8, &[0, -129, 128], &[-128, 1, 127]);
|
||||||
|
check_ranges!(
|
||||||
|
std::num::NonZeroI16,
|
||||||
|
&[0, -32769, 32768],
|
||||||
|
&[-32768, -1, 1, 32767]
|
||||||
|
);
|
||||||
|
check_ranges!(
|
||||||
|
std::num::NonZeroI32,
|
||||||
|
&[0, -2_147_483_649, 2_147_483_648],
|
||||||
|
&[-2_147_483_648, -1, 1, 2_147_483_647]
|
||||||
|
);
|
||||||
|
check_ranges!(
|
||||||
|
std::num::NonZeroI64,
|
||||||
|
&[0],
|
||||||
|
&[-2_147_483_648, -1, 1, 2_147_483_647, i64::MAX, i64::MIN]
|
||||||
|
);
|
||||||
|
check_ranges!(
|
||||||
|
std::num::NonZeroIsize,
|
||||||
|
&[0],
|
||||||
|
&[-2_147_483_648, -1, 1, 2_147_483_647]
|
||||||
|
);
|
||||||
|
check_ranges!(std::num::NonZeroU8, &[0, -2, -1, 256], &[1, 255]);
|
||||||
|
check_ranges!(std::num::NonZeroU16, &[0, -2, -1, 65536], &[1, 65535]);
|
||||||
|
check_ranges!(
|
||||||
|
std::num::NonZeroU32,
|
||||||
|
&[0, -2, -1, 4_294_967_296],
|
||||||
|
&[1, 4_294_967_295]
|
||||||
|
);
|
||||||
|
check_ranges!(
|
||||||
|
std::num::NonZeroU64,
|
||||||
|
&[0, -2, -1, -4_294_967_296],
|
||||||
|
&[1, 4_294_967_295, i64::MAX as u64]
|
||||||
|
);
|
||||||
|
check_ranges!(
|
||||||
|
std::num::NonZeroUsize,
|
||||||
|
&[0, -2, -1, -4_294_967_296],
|
||||||
|
&[1, 4_294_967_295]
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,8 @@
|
|||||||
//! [`ToSql`] always succeeds except when storing a `u64` or `usize` value that
|
//! [`ToSql`] always succeeds except when storing a `u64` or `usize` value that
|
||||||
//! cannot fit in an `INTEGER` (`i64`). Also note that SQLite ignores column
|
//! 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
|
//! types, so if you store an `i64` in a column with type `REAL` it will be
|
||||||
//! stored as an `INTEGER`, not a `REAL`.
|
//! stored as an `INTEGER`, not a `REAL` (unless the column is part of a
|
||||||
|
//! [STRICT table](https://www.sqlite.org/stricttables.html)).
|
||||||
//!
|
//!
|
||||||
//! If the `time` feature is enabled, implementations are
|
//! If the `time` feature is enabled, implementations are
|
||||||
//! provided for `time::OffsetDateTime` that use the RFC 3339 date/time format,
|
//! provided for `time::OffsetDateTime` that use the RFC 3339 date/time format,
|
||||||
|
@ -51,6 +51,12 @@ macro_rules! from_value(
|
|||||||
#[inline]
|
#[inline]
|
||||||
fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
|
fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
(non_zero $t:ty) => (
|
||||||
|
impl From<$t> for ToSqlOutput<'_> {
|
||||||
|
#[inline]
|
||||||
|
fn from(t: $t) -> Self { ToSqlOutput::Owned(t.get().into())}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
from_value!(String);
|
from_value!(String);
|
||||||
@ -68,6 +74,15 @@ from_value!(f32);
|
|||||||
from_value!(f64);
|
from_value!(f64);
|
||||||
from_value!(Vec<u8>);
|
from_value!(Vec<u8>);
|
||||||
|
|
||||||
|
from_value!(non_zero std::num::NonZeroI8);
|
||||||
|
from_value!(non_zero std::num::NonZeroI16);
|
||||||
|
from_value!(non_zero std::num::NonZeroI32);
|
||||||
|
from_value!(non_zero std::num::NonZeroI64);
|
||||||
|
from_value!(non_zero std::num::NonZeroIsize);
|
||||||
|
from_value!(non_zero std::num::NonZeroU8);
|
||||||
|
from_value!(non_zero std::num::NonZeroU16);
|
||||||
|
from_value!(non_zero std::num::NonZeroU32);
|
||||||
|
|
||||||
// It would be nice if we could avoid the heap allocation (of the `Vec`) that
|
// It would be nice if we could avoid the heap allocation (of the `Vec`) that
|
||||||
// `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
|
// `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
|
||||||
// worth adding another case to Value.
|
// worth adding another case to Value.
|
||||||
@ -75,6 +90,10 @@ from_value!(Vec<u8>);
|
|||||||
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
from_value!(i128);
|
from_value!(i128);
|
||||||
|
|
||||||
|
#[cfg(feature = "i128_blob")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
|
from_value!(non_zero std::num::NonZeroI128);
|
||||||
|
|
||||||
#[cfg(feature = "uuid")]
|
#[cfg(feature = "uuid")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
||||||
from_value!(uuid::Uuid);
|
from_value!(uuid::Uuid);
|
||||||
@ -165,10 +184,23 @@ to_sql_self!(u32);
|
|||||||
to_sql_self!(f32);
|
to_sql_self!(f32);
|
||||||
to_sql_self!(f64);
|
to_sql_self!(f64);
|
||||||
|
|
||||||
|
to_sql_self!(std::num::NonZeroI8);
|
||||||
|
to_sql_self!(std::num::NonZeroI16);
|
||||||
|
to_sql_self!(std::num::NonZeroI32);
|
||||||
|
to_sql_self!(std::num::NonZeroI64);
|
||||||
|
to_sql_self!(std::num::NonZeroIsize);
|
||||||
|
to_sql_self!(std::num::NonZeroU8);
|
||||||
|
to_sql_self!(std::num::NonZeroU16);
|
||||||
|
to_sql_self!(std::num::NonZeroU32);
|
||||||
|
|
||||||
#[cfg(feature = "i128_blob")]
|
#[cfg(feature = "i128_blob")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
to_sql_self!(i128);
|
to_sql_self!(i128);
|
||||||
|
|
||||||
|
#[cfg(feature = "i128_blob")]
|
||||||
|
#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
|
||||||
|
to_sql_self!(std::num::NonZeroI128);
|
||||||
|
|
||||||
#[cfg(feature = "uuid")]
|
#[cfg(feature = "uuid")]
|
||||||
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
|
||||||
to_sql_self!(uuid::Uuid);
|
to_sql_self!(uuid::Uuid);
|
||||||
@ -186,12 +218,27 @@ macro_rules! to_sql_self_fallible(
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
);
|
||||||
|
(non_zero $t:ty) => (
|
||||||
|
impl ToSql for $t {
|
||||||
|
#[inline]
|
||||||
|
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
|
||||||
|
Ok(ToSqlOutput::Owned(Value::Integer(
|
||||||
|
i64::try_from(self.get()).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.
|
// Special implementations for usize and u64 because these conversions can fail.
|
||||||
to_sql_self_fallible!(u64);
|
to_sql_self_fallible!(u64);
|
||||||
to_sql_self_fallible!(usize);
|
to_sql_self_fallible!(usize);
|
||||||
|
to_sql_self_fallible!(non_zero std::num::NonZeroU64);
|
||||||
|
to_sql_self_fallible!(non_zero std::num::NonZeroUsize);
|
||||||
|
|
||||||
impl<T: ?Sized> ToSql for &'_ T
|
impl<T: ?Sized> ToSql for &'_ T
|
||||||
where
|
where
|
||||||
@ -267,9 +314,26 @@ mod test {
|
|||||||
is_to_sql::<i16>();
|
is_to_sql::<i16>();
|
||||||
is_to_sql::<i32>();
|
is_to_sql::<i32>();
|
||||||
is_to_sql::<i64>();
|
is_to_sql::<i64>();
|
||||||
|
is_to_sql::<isize>();
|
||||||
is_to_sql::<u8>();
|
is_to_sql::<u8>();
|
||||||
is_to_sql::<u16>();
|
is_to_sql::<u16>();
|
||||||
is_to_sql::<u32>();
|
is_to_sql::<u32>();
|
||||||
|
is_to_sql::<u64>();
|
||||||
|
is_to_sql::<usize>();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nonzero_types() {
|
||||||
|
is_to_sql::<std::num::NonZeroI8>();
|
||||||
|
is_to_sql::<std::num::NonZeroI16>();
|
||||||
|
is_to_sql::<std::num::NonZeroI32>();
|
||||||
|
is_to_sql::<std::num::NonZeroI64>();
|
||||||
|
is_to_sql::<std::num::NonZeroIsize>();
|
||||||
|
is_to_sql::<std::num::NonZeroU8>();
|
||||||
|
is_to_sql::<std::num::NonZeroU16>();
|
||||||
|
is_to_sql::<std::num::NonZeroU32>();
|
||||||
|
is_to_sql::<std::num::NonZeroU64>();
|
||||||
|
is_to_sql::<std::num::NonZeroUsize>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -398,6 +462,54 @@ mod test {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "i128_blob")]
|
||||||
|
#[test]
|
||||||
|
fn test_non_zero_i128() -> crate::Result<()> {
|
||||||
|
use std::num::NonZeroI128;
|
||||||
|
macro_rules! nz {
|
||||||
|
($x:expr) => {
|
||||||
|
NonZeroI128::new($x).unwrap()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let db = crate::Connection::open_in_memory()?;
|
||||||
|
db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
|
||||||
|
db.execute(
|
||||||
|
"INSERT INTO foo(i128, desc) VALUES
|
||||||
|
(?1, 'neg one'), (?2, 'neg two'),
|
||||||
|
(?3, 'pos one'), (?4, 'pos two'),
|
||||||
|
(?5, 'min'), (?6, 'max')",
|
||||||
|
[
|
||||||
|
nz!(-1),
|
||||||
|
nz!(-2),
|
||||||
|
nz!(1),
|
||||||
|
nz!(2),
|
||||||
|
nz!(i128::MIN),
|
||||||
|
nz!(i128::MAX),
|
||||||
|
],
|
||||||
|
)?;
|
||||||
|
let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
|
||||||
|
|
||||||
|
let res = stmt
|
||||||
|
.query_map([], |row| Ok((row.get(0)?, row.get(1)?)))?
|
||||||
|
.collect::<Result<Vec<(NonZeroI128, String)>, _>>()?;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
res,
|
||||||
|
&[
|
||||||
|
(nz!(i128::MIN), "min".to_owned()),
|
||||||
|
(nz!(-2), "neg two".to_owned()),
|
||||||
|
(nz!(-1), "neg one".to_owned()),
|
||||||
|
(nz!(1), "pos one".to_owned()),
|
||||||
|
(nz!(2), "pos two".to_owned()),
|
||||||
|
(nz!(i128::MAX), "max".to_owned()),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
let err = db.query_row("SELECT ?1", [0i128], |row| row.get::<_, NonZeroI128>(0));
|
||||||
|
assert_eq!(err, Err(crate::Error::IntegralValueOutOfRange(0, 0)));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "uuid")]
|
#[cfg(feature = "uuid")]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_uuid() -> crate::Result<()> {
|
fn test_uuid() -> crate::Result<()> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user