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