1 use super::{Null, Value, ValueRef}; 2 #[cfg(feature = "array")] 3 use crate::vtab::array::Array; 4 use crate::{Error, Result}; 5 use std::borrow::Cow; 6 use std::convert::TryFrom; 7 8 /// `ToSqlOutput` represents the possible output types for implementers of the 9 /// [`ToSql`] trait. 10 #[derive(Clone, Debug, PartialEq)] 11 #[non_exhaustive] 12 pub enum ToSqlOutput<'a> { 13 /// A borrowed SQLite-representable value. 14 Borrowed(ValueRef<'a>), 15 16 /// An owned SQLite-representable value. 17 Owned(Value), 18 19 /// A BLOB of the given length that is filled with 20 /// zeroes. 21 #[cfg(feature = "blob")] 22 #[cfg_attr(docsrs, doc(cfg(feature = "blob")))] 23 ZeroBlob(i32), 24 25 /// `feature = "array"` 26 #[cfg(feature = "array")] 27 #[cfg_attr(docsrs, doc(cfg(feature = "array")))] 28 Array(Array), 29 } 30 31 // Generically allow any type that can be converted into a ValueRef 32 // to be converted into a ToSqlOutput as well. 33 impl<'a, T: ?Sized> From<&'a T> for ToSqlOutput<'a> 34 where 35 &'a T: Into<ValueRef<'a>>, 36 { 37 #[inline] from(t: &'a T) -> Self38 fn from(t: &'a T) -> Self { 39 ToSqlOutput::Borrowed(t.into()) 40 } 41 } 42 43 // We cannot also generically allow any type that can be converted 44 // into a Value to be converted into a ToSqlOutput because of 45 // coherence rules (https://github.com/rust-lang/rust/pull/46192), 46 // so we'll manually implement it for all the types we know can 47 // be converted into Values. 48 macro_rules! from_value( 49 ($t:ty) => ( 50 impl From<$t> for ToSqlOutput<'_> { 51 #[inline] 52 fn from(t: $t) -> Self { ToSqlOutput::Owned(t.into())} 53 } 54 ) 55 ); 56 from_value!(String); 57 from_value!(Null); 58 from_value!(bool); 59 from_value!(i8); 60 from_value!(i16); 61 from_value!(i32); 62 from_value!(i64); 63 from_value!(isize); 64 from_value!(u8); 65 from_value!(u16); 66 from_value!(u32); 67 from_value!(f32); 68 from_value!(f64); 69 from_value!(Vec<u8>); 70 71 // It would be nice if we could avoid the heap allocation (of the `Vec`) that 72 // `i128` needs in `Into<Value>`, but it's probably fine for the moment, and not 73 // worth adding another case to Value. 74 #[cfg(feature = "i128_blob")] 75 #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))] 76 from_value!(i128); 77 78 #[cfg(feature = "uuid")] 79 #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] 80 from_value!(uuid::Uuid); 81 82 impl ToSql for ToSqlOutput<'_> { 83 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>84 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 85 Ok(match *self { 86 ToSqlOutput::Borrowed(v) => ToSqlOutput::Borrowed(v), 87 ToSqlOutput::Owned(ref v) => ToSqlOutput::Borrowed(ValueRef::from(v)), 88 89 #[cfg(feature = "blob")] 90 ToSqlOutput::ZeroBlob(i) => ToSqlOutput::ZeroBlob(i), 91 #[cfg(feature = "array")] 92 ToSqlOutput::Array(ref a) => ToSqlOutput::Array(a.clone()), 93 }) 94 } 95 } 96 97 /// A trait for types that can be converted into SQLite values. Returns 98 /// [`Error::ToSqlConversionFailure`] if the conversion fails. 99 pub trait ToSql { 100 /// Converts Rust value to SQLite value to_sql(&self) -> Result<ToSqlOutput<'_>>101 fn to_sql(&self) -> Result<ToSqlOutput<'_>>; 102 } 103 104 impl<T: ToSql + ToOwned + ?Sized> ToSql for Cow<'_, T> { 105 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>106 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 107 self.as_ref().to_sql() 108 } 109 } 110 111 impl<T: ToSql + ?Sized> ToSql for Box<T> { 112 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>113 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 114 self.as_ref().to_sql() 115 } 116 } 117 118 impl<T: ToSql + ?Sized> ToSql for std::rc::Rc<T> { 119 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>120 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 121 self.as_ref().to_sql() 122 } 123 } 124 125 impl<T: ToSql + ?Sized> ToSql for std::sync::Arc<T> { 126 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>127 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 128 self.as_ref().to_sql() 129 } 130 } 131 132 // We should be able to use a generic impl like this: 133 // 134 // impl<T: Copy> ToSql for T where T: Into<Value> { 135 // fn to_sql(&self) -> Result<ToSqlOutput> { 136 // Ok(ToSqlOutput::from((*self).into())) 137 // } 138 // } 139 // 140 // instead of the following macro, but this runs afoul of 141 // https://github.com/rust-lang/rust/issues/30191 and reports conflicting 142 // implementations even when there aren't any. 143 144 macro_rules! to_sql_self( 145 ($t:ty) => ( 146 impl ToSql for $t { 147 #[inline] 148 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 149 Ok(ToSqlOutput::from(*self)) 150 } 151 } 152 ) 153 ); 154 155 to_sql_self!(Null); 156 to_sql_self!(bool); 157 to_sql_self!(i8); 158 to_sql_self!(i16); 159 to_sql_self!(i32); 160 to_sql_self!(i64); 161 to_sql_self!(isize); 162 to_sql_self!(u8); 163 to_sql_self!(u16); 164 to_sql_self!(u32); 165 to_sql_self!(f32); 166 to_sql_self!(f64); 167 168 #[cfg(feature = "i128_blob")] 169 #[cfg_attr(docsrs, doc(cfg(feature = "i128_blob")))] 170 to_sql_self!(i128); 171 172 #[cfg(feature = "uuid")] 173 #[cfg_attr(docsrs, doc(cfg(feature = "uuid")))] 174 to_sql_self!(uuid::Uuid); 175 176 macro_rules! to_sql_self_fallible( 177 ($t:ty) => ( 178 impl ToSql for $t { 179 #[inline] 180 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 181 Ok(ToSqlOutput::Owned(Value::Integer( 182 i64::try_from(*self).map_err( 183 // TODO: Include the values in the error message. 184 |err| Error::ToSqlConversionFailure(err.into()) 185 )? 186 ))) 187 } 188 } 189 ) 190 ); 191 192 // Special implementations for usize and u64 because these conversions can fail. 193 to_sql_self_fallible!(u64); 194 to_sql_self_fallible!(usize); 195 196 impl<T: ?Sized> ToSql for &'_ T 197 where 198 T: ToSql, 199 { 200 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>201 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 202 (*self).to_sql() 203 } 204 } 205 206 impl ToSql for String { 207 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>208 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 209 Ok(ToSqlOutput::from(self.as_str())) 210 } 211 } 212 213 impl ToSql for str { 214 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>215 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 216 Ok(ToSqlOutput::from(self)) 217 } 218 } 219 220 impl ToSql for Vec<u8> { 221 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>222 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 223 Ok(ToSqlOutput::from(self.as_slice())) 224 } 225 } 226 227 impl<const N: usize> ToSql for [u8; N] { 228 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>229 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 230 Ok(ToSqlOutput::from(&self[..])) 231 } 232 } 233 234 impl ToSql for [u8] { 235 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>236 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 237 Ok(ToSqlOutput::from(self)) 238 } 239 } 240 241 impl ToSql for Value { 242 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>243 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 244 Ok(ToSqlOutput::from(self)) 245 } 246 } 247 248 impl<T: ToSql> ToSql for Option<T> { 249 #[inline] to_sql(&self) -> Result<ToSqlOutput<'_>>250 fn to_sql(&self) -> Result<ToSqlOutput<'_>> { 251 match *self { 252 None => Ok(ToSqlOutput::from(Null)), 253 Some(ref t) => t.to_sql(), 254 } 255 } 256 } 257 258 #[cfg(test)] 259 mod test { 260 use super::ToSql; 261 is_to_sql<T: ToSql>()262 fn is_to_sql<T: ToSql>() {} 263 264 #[test] test_integral_types()265 fn test_integral_types() { 266 is_to_sql::<i8>(); 267 is_to_sql::<i16>(); 268 is_to_sql::<i32>(); 269 is_to_sql::<i64>(); 270 is_to_sql::<u8>(); 271 is_to_sql::<u16>(); 272 is_to_sql::<u32>(); 273 } 274 275 #[test] test_u8_array()276 fn test_u8_array() { 277 let a: [u8; 99] = [0u8; 99]; 278 let _a: &[&dyn ToSql] = crate::params![a]; 279 let r = ToSql::to_sql(&a); 280 281 assert!(r.is_ok()); 282 } 283 284 #[test] test_cow_str()285 fn test_cow_str() { 286 use std::borrow::Cow; 287 let s = "str"; 288 let cow: Cow<str> = Cow::Borrowed(s); 289 let r = cow.to_sql(); 290 assert!(r.is_ok()); 291 let cow: Cow<str> = Cow::Owned::<str>(String::from(s)); 292 let r = cow.to_sql(); 293 assert!(r.is_ok()); 294 // Ensure this compiles. 295 let _p: &[&dyn ToSql] = crate::params![cow]; 296 } 297 298 #[test] test_box_dyn()299 fn test_box_dyn() { 300 let s: Box<dyn ToSql> = Box::new("Hello world!"); 301 let _s: &[&dyn ToSql] = crate::params![s]; 302 let r = ToSql::to_sql(&s); 303 304 assert!(r.is_ok()); 305 } 306 307 #[test] test_box_deref()308 fn test_box_deref() { 309 let s: Box<str> = "Hello world!".into(); 310 let _s: &[&dyn ToSql] = crate::params![s]; 311 let r = s.to_sql(); 312 313 assert!(r.is_ok()); 314 } 315 316 #[test] test_box_direct()317 fn test_box_direct() { 318 let s: Box<str> = "Hello world!".into(); 319 let _s: &[&dyn ToSql] = crate::params![s]; 320 let r = ToSql::to_sql(&s); 321 322 assert!(r.is_ok()); 323 } 324 325 #[test] test_cells()326 fn test_cells() { 327 use std::{rc::Rc, sync::Arc}; 328 329 let source_str: Box<str> = "Hello world!".into(); 330 331 let s: Rc<Box<str>> = Rc::new(source_str.clone()); 332 let _s: &[&dyn ToSql] = crate::params![s]; 333 let r = s.to_sql(); 334 assert!(r.is_ok()); 335 336 let s: Arc<Box<str>> = Arc::new(source_str.clone()); 337 let _s: &[&dyn ToSql] = crate::params![s]; 338 let r = s.to_sql(); 339 assert!(r.is_ok()); 340 341 let s: Arc<str> = Arc::from(&*source_str); 342 let _s: &[&dyn ToSql] = crate::params![s]; 343 let r = s.to_sql(); 344 assert!(r.is_ok()); 345 346 let s: Arc<dyn ToSql> = Arc::new(source_str.clone()); 347 let _s: &[&dyn ToSql] = crate::params![s]; 348 let r = s.to_sql(); 349 assert!(r.is_ok()); 350 351 let s: Rc<str> = Rc::from(&*source_str); 352 let _s: &[&dyn ToSql] = crate::params![s]; 353 let r = s.to_sql(); 354 assert!(r.is_ok()); 355 356 let s: Rc<dyn ToSql> = Rc::new(source_str); 357 let _s: &[&dyn ToSql] = crate::params![s]; 358 let r = s.to_sql(); 359 assert!(r.is_ok()); 360 } 361 362 #[cfg(feature = "i128_blob")] 363 #[test] test_i128() -> crate::Result<()>364 fn test_i128() -> crate::Result<()> { 365 use crate::Connection; 366 let db = Connection::open_in_memory()?; 367 db.execute_batch("CREATE TABLE foo (i128 BLOB, desc TEXT)")?; 368 db.execute( 369 " 370 INSERT INTO foo(i128, desc) VALUES 371 (?, 'zero'), 372 (?, 'neg one'), (?, 'neg two'), 373 (?, 'pos one'), (?, 'pos two'), 374 (?, 'min'), (?, 'max')", 375 [0i128, -1i128, -2i128, 1i128, 2i128, i128::MIN, i128::MAX], 376 )?; 377 378 let mut stmt = db.prepare("SELECT i128, desc FROM foo ORDER BY i128 ASC")?; 379 380 let res = stmt 381 .query_map([], |row| { 382 Ok((row.get::<_, i128>(0)?, row.get::<_, String>(1)?)) 383 })? 384 .collect::<Result<Vec<_>, _>>()?; 385 386 assert_eq!( 387 res, 388 &[ 389 (i128::MIN, "min".to_owned()), 390 (-2, "neg two".to_owned()), 391 (-1, "neg one".to_owned()), 392 (0, "zero".to_owned()), 393 (1, "pos one".to_owned()), 394 (2, "pos two".to_owned()), 395 (i128::MAX, "max".to_owned()), 396 ] 397 ); 398 Ok(()) 399 } 400 401 #[cfg(feature = "uuid")] 402 #[test] test_uuid() -> crate::Result<()>403 fn test_uuid() -> crate::Result<()> { 404 use crate::{params, Connection}; 405 use uuid::Uuid; 406 407 let db = Connection::open_in_memory()?; 408 db.execute_batch("CREATE TABLE foo (id BLOB CHECK(length(id) = 16), label TEXT);")?; 409 410 let id = Uuid::new_v4(); 411 412 db.execute( 413 "INSERT INTO foo (id, label) VALUES (?, ?)", 414 params![id, "target"], 415 )?; 416 417 let mut stmt = db.prepare("SELECT id, label FROM foo WHERE id = ?")?; 418 419 let mut rows = stmt.query(params![id])?; 420 let row = rows.next()?.unwrap(); 421 422 let found_id: Uuid = row.get_unwrap(0); 423 let found_label: String = row.get_unwrap(1); 424 425 assert_eq!(found_id, id); 426 assert_eq!(found_label, "target"); 427 Ok(()) 428 } 429 } 430