aboutsummaryrefslogtreecommitdiff
path: root/src/types/to_sql.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/types/to_sql.rs')
-rw-r--r--src/types/to_sql.rs117
1 files changed, 85 insertions, 32 deletions
diff --git a/src/types/to_sql.rs b/src/types/to_sql.rs
index 937c0f8..2445339 100644
--- a/src/types/to_sql.rs
+++ b/src/types/to_sql.rs
@@ -1,11 +1,12 @@
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
-/// `ToSql` trait.
+/// `ToSqlOutput` represents the possible output types for implementers of the
+/// [`ToSql`] trait.
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub enum ToSqlOutput<'a> {
@@ -15,13 +16,15 @@ pub enum ToSqlOutput<'a> {
/// An owned SQLite-representable value.
Owned(Value),
- /// `feature = "blob"` A BLOB of the given length that is filled with
+ /// A BLOB of the given length that is filled with
/// zeroes.
#[cfg(feature = "blob")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "blob")))]
ZeroBlob(i32),
/// `feature = "array"`
#[cfg(feature = "array")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "array")))]
Array(Array),
}
@@ -31,6 +34,7 @@ impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a>
where
&'a T: Into<ValueRef<'a>>,
{
+ #[inline]
fn from(t: &'a T) -> Self {
ToSqlOutput::Borrowed(t.into())
}
@@ -44,6 +48,7 @@ where
macro_rules! from_value(
($t:ty) => (
impl From<$t> for ToSqlOutput<'_> {
+ #[inline]
fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())}
}
)
@@ -59,6 +64,7 @@ from_value!(isize);
from_value!(u8);
from_value!(u16);
from_value!(u32);
+from_value!(f32);
from_value!(f64);
from_value!(Vec<u8>);
@@ -66,12 +72,15 @@ from_value!(Vec<u8>);
// `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not
// worth adding another case to Value.
#[cfg(feature = "i128_blob")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
from_value!(i128);
#[cfg(feature = "uuid")]
+#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
from_value!(uuid::Uuid);
impl ToSql for ToSqlOutput<'_> {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(match *self {
ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v),
@@ -85,31 +94,36 @@ 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<'_>>;
}
impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
self.as_ref().to_sql()
}
}
impl<T: ToSql + ?Sized> ToSql for Box<T> {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
self.as_ref().to_sql()
}
}
impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
self.as_ref().to_sql()
}
}
impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
self.as_ref().to_sql()
}
@@ -130,6 +144,7 @@ impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> {
macro_rules! to_sql_self(
($t:ty) => (
impl ToSql for $t {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(*self))
}
@@ -147,54 +162,91 @@ to_sql_self!(isize);
to_sql_self!(u8);
to_sql_self!(u16);
to_sql_self!(u32);
+to_sql_self!(f32);
to_sql_self!(f64);
#[cfg(feature = "i128_blob")]
+#[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))]
to_sql_self!(i128);
#[cfg(feature = "uuid")]
+#[cfg_attr(docsrs, doc(cfg(feature = "uuid")))]
to_sql_self!(uuid::Uuid);
+macro_rules! to_sql_self_fallible(
+ ($t:ty) => (
+ impl ToSql for $t {
+ #[inline]
+ 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,
{
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
(*self).to_sql()
}
}
impl ToSql for String {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.as_str()))
}
}
impl ToSql for str {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self))
}
}
impl ToSql for Vec<u8> {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self.as_slice()))
}
}
+impl<const N: usize> ToSql for [u8; N] {
+ #[inline]
+ fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
+ Ok(ToSqlOutput::from(&self[..]))
+ }
+}
+
impl ToSql for [u8] {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self))
}
}
impl ToSql for Value {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
Ok(ToSqlOutput::from(self))
}
}
impl<T: ToSql> ToSql for Option<T> {
+ #[inline]
fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
match *self {
None => Ok(ToSqlOutput::from(Null)),
@@ -221,6 +273,15 @@ mod test {
}
#[test]
+ fn test_u8_array() {
+ let a: [u8; 99] = [0u8; 99];
+ let _a: &[&dyn ToSql] = crate::params![a];
+ let r = ToSql::to_sql(&a);
+
+ assert!(r.is_ok());
+ }
+
+ #[test]
fn test_cow_str() {
use std::borrow::Cow;
let s = "str";
@@ -300,12 +361,11 @@ mod test {
#[cfg(feature = "i128_blob")]
#[test]
- fn test_i128() {
- use crate::{Connection, NO_PARAMS};
+ fn test_i128() -> crate::Result<()> {
+ use crate::Connection;
use std::i128;
- let db = Connection::open_in_memory().unwrap();
- db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")
- .unwrap();
+ let db = Connection::open_in_memory()?;
+ db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?;
db.execute(
"
INSERT INTO foo(i128, desc) VALUES
@@ -313,21 +373,16 @@ mod test {
(?, 'neg one'), (?, 'neg two'),
(?, 'pos one'), (?, 'pos two'),
(?, 'min'), (?, 'max')",
- &[0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
- )
- .unwrap();
+ [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX],
+ )?;
- let mut stmt = db
- .prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")
- .unwrap();
+ let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?;
let res = stmt
- .query_map(NO_PARAMS, |row| {
+ .query_map([], |row| {
Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?))
- })
- .unwrap()
- .collect::<Result<Vec<_>, _>>()
- .unwrap();
+ })?
+ .collect::<Result<Vec<_>, _>>()?;
assert_eq!(
res,
@@ -341,37 +396,35 @@ mod test {
(i128::MAX, "max".to_owned()),
]
);
+ Ok(())
}
#[cfg(feature = "uuid")]
#[test]
- fn test_uuid() {
+ fn test_uuid() -> crate::Result<()> {
use crate::{params, Connection};
use uuid::Uuid;
- let db = Connection::open_in_memory().unwrap();
- db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")
- .unwrap();
+ let db = Connection::open_in_memory()?;
+ db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")?;
let id = Uuid::new_v4();
db.execute(
"INSERT INTO foo (id, label) VALUES (?, ?)",
params![id, "target"],
- )
- .unwrap();
+ )?;
- let mut stmt = db
- .prepare("SELECT id, label FROM foo WHERE id = ?")
- .unwrap();
+ let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?")?;
- let mut rows = stmt.query(params![id]).unwrap();
- let row = rows.next().unwrap().unwrap();
+ let mut rows = stmt.query(params![id])?;
+ let row = rows.next()?.unwrap();
let found_id: Uuid = row.get_unwrap(0);
let found_label: String = row.get_unwrap(1);
assert_eq!(found_id, id);
assert_eq!(found_label, "target");
+ Ok(())
}
}