Merge remote-tracking branch 'upstream/master' into progress_handler

This commit is contained in:
gwenn 2020-10-28 21:19:34 +01:00
commit b448d95510
3 changed files with 59 additions and 25 deletions

View File

@ -111,8 +111,9 @@ pub enum Error {
InvalidParameterCount(usize, usize),
/// Returned from various functions in the Blob IO positional API. For
/// example, [`Blob::raw_read_at_exact`](crate::blob::Blob::
/// raw_read_at_exact) will return it if the blob has insufficient data.
/// example,
/// [`Blob::raw_read_at_exact`](crate::blob::Blob::raw_read_at_exact) will
/// return it if the blob has insufficient data.
#[cfg(feature = "blob")]
BlobSizeError,
}

View File

@ -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.
//! 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
@ -397,7 +397,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])
@ -408,6 +408,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]
@ -445,22 +451,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);
}
}

View File

@ -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,