1#![cfg_attr(
39 feature = "time",
40 doc = r##"
41For example, to store datetimes as `i64`s counting the number of seconds since
42the Unix epoch:
43
44```
45use rusqlite::types::{FromSql, FromSqlError, FromSqlResult, ToSql, ToSqlOutput, ValueRef};
46use rusqlite::Result;
47
48pub struct DateTimeSql(pub time::OffsetDateTime);
49
50impl FromSql for DateTimeSql {
51 fn column_result(value: ValueRef) -> FromSqlResult<Self> {
52 i64::column_result(value).and_then(|as_i64| {
53 time::OffsetDateTime::from_unix_timestamp(as_i64)
54 .map(|odt| DateTimeSql(odt))
55 .map_err(FromSqlError::other)
56 })
57 }
58}
59
60impl ToSql for DateTimeSql {
61 fn to_sql(&self) -> Result<ToSqlOutput> {
62 Ok(self.0.unix_timestamp().into())
63 }
64}
65```
66
67"##
68)]
69pub use self::from_sql::{FromSql, FromSqlError, FromSqlResult};
74pub use self::to_sql::{ToSql, ToSqlOutput};
75pub use self::value::Value;
76pub use self::value_ref::ValueRef;
77
78use std::fmt;
79
80#[cfg(feature = "chrono")]
81mod chrono;
82mod from_sql;
83#[cfg(feature = "jiff")]
84mod jiff;
85#[cfg(feature = "serde_json")]
86mod serde_json;
87#[cfg(feature = "time")]
88mod time;
89mod to_sql;
90#[cfg(feature = "url")]
91mod url;
92mod value;
93mod value_ref;
94
95#[derive(Copy, Clone)]
108pub struct Null;
109
110#[derive(Copy, Clone, Debug, PartialEq, Eq)]
113pub enum Type {
114 Null,
116 Integer,
118 Real,
120 Text,
122 Blob,
124}
125
126impl fmt::Display for Type {
127 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
128 match *self {
129 Self::Null => f.pad("Null"),
130 Self::Integer => f.pad("Integer"),
131 Self::Real => f.pad("Real"),
132 Self::Text => f.pad("Text"),
133 Self::Blob => f.pad("Blob"),
134 }
135 }
136}
137
138#[cfg(test)]
139mod test {
140 use super::Value;
141 use crate::{params, Connection, Error, Result, Statement};
142 use std::ffi::{c_double, c_int};
143
144 fn checked_memory_handle() -> Result<Connection> {
145 let db = Connection::open_in_memory()?;
146 db.execute_batch("CREATE TABLE foo (b BLOB, t TEXT, i INTEGER, f FLOAT, n)")?;
147 Ok(db)
148 }
149
150 #[test]
151 fn test_blob() -> Result<()> {
152 let db = checked_memory_handle()?;
153
154 let v1234 = vec![1u8, 2, 3, 4];
155 db.execute("INSERT INTO foo(b) VALUES (?1)", [&v1234])?;
156
157 let v: Vec<u8> = db.one_column("SELECT b FROM foo", [])?;
158 assert_eq!(v, v1234);
159 Ok(())
160 }
161
162 #[test]
163 fn test_empty_blob() -> Result<()> {
164 let db = checked_memory_handle()?;
165
166 let empty = vec![];
167 db.execute("INSERT INTO foo(b) VALUES (?1)", [&empty])?;
168
169 let v: Vec<u8> = db.one_column("SELECT b FROM foo", [])?;
170 assert_eq!(v, empty);
171 Ok(())
172 }
173
174 #[test]
175 fn test_str() -> Result<()> {
176 let db = checked_memory_handle()?;
177
178 let s = "hello, world!";
179 db.execute("INSERT INTO foo(t) VALUES (?1)", [&s])?;
180
181 let from: String = db.one_column("SELECT t FROM foo", [])?;
182 assert_eq!(from, s);
183 Ok(())
184 }
185
186 #[test]
187 fn test_string() -> Result<()> {
188 let db = checked_memory_handle()?;
189
190 let s = "hello, world!";
191 db.execute("INSERT INTO foo(t) VALUES (?1)", [s.to_owned()])?;
192
193 let from: String = db.one_column("SELECT t FROM foo", [])?;
194 assert_eq!(from, s);
195 Ok(())
196 }
197
198 #[test]
199 fn test_value() -> Result<()> {
200 let db = checked_memory_handle()?;
201
202 db.execute("INSERT INTO foo(i) VALUES (?1)", [Value::Integer(10)])?;
203
204 assert_eq!(10, db.one_column::<i64, _>("SELECT i FROM foo", [])?);
205 Ok(())
206 }
207
208 #[test]
209 fn test_option() -> Result<()> {
210 let db = checked_memory_handle()?;
211
212 let s = "hello, world!";
213 let b = Some(vec![1u8, 2, 3, 4]);
214
215 db.execute("INSERT INTO foo(t) VALUES (?1)", [Some(s)])?;
216 db.execute("INSERT INTO foo(b) VALUES (?1)", [&b])?;
217
218 let mut stmt = db.prepare("SELECT t, b FROM foo ORDER BY ROWID ASC")?;
219 let mut rows = stmt.query([])?;
220
221 {
222 let row1 = rows.next()?.unwrap();
223 let s1: Option<String> = row1.get_unwrap(0);
224 let b1: Option<Vec<u8>> = row1.get_unwrap(1);
225 assert_eq!(s, s1.unwrap());
226 assert!(b1.is_none());
227 }
228
229 {
230 let row2 = rows.next()?.unwrap();
231 let s2: Option<String> = row2.get_unwrap(0);
232 let b2: Option<Vec<u8>> = row2.get_unwrap(1);
233 assert!(s2.is_none());
234 assert_eq!(b, b2);
235 }
236 Ok(())
237 }
238
239 #[test]
240 #[expect(clippy::cognitive_complexity)]
241 fn test_mismatched_types() -> Result<()> {
242 fn is_invalid_column_type(err: Error) -> bool {
243 matches!(err, Error::InvalidColumnType(..))
244 }
245
246 let db = checked_memory_handle()?;
247
248 db.execute(
249 "INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
250 [],
251 )?;
252
253 let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo")?;
254 let mut rows = stmt.query([])?;
255
256 let row = rows.next()?.unwrap();
257
258 assert_eq!(vec![1, 2], row.get::<_, Vec<u8>>(0)?);
260 assert_eq!("text", row.get::<_, String>(1)?);
261 assert_eq!(1, row.get::<_, c_int>(2)?);
262 assert!((1.5 - row.get::<_, c_double>(3)?).abs() < f64::EPSILON);
263 assert_eq!(row.get::<_, Option<c_int>>(4)?, None);
264 assert_eq!(row.get::<_, Option<c_double>>(4)?, None);
265 assert_eq!(row.get::<_, Option<String>>(4)?, None);
266
267 assert!(is_invalid_column_type(row.get::<_, c_int>(0).unwrap_err()));
271 assert!(is_invalid_column_type(row.get::<_, c_int>(0).unwrap_err()));
272 assert!(is_invalid_column_type(row.get::<_, i64>(0).err().unwrap()));
273 assert!(is_invalid_column_type(
274 row.get::<_, c_double>(0).unwrap_err()
275 ));
276 assert!(is_invalid_column_type(row.get::<_, String>(0).unwrap_err()));
277 #[cfg(feature = "time")]
278 assert!(is_invalid_column_type(
279 row.get::<_, time::OffsetDateTime>(0).unwrap_err()
280 ));
281 assert!(is_invalid_column_type(
282 row.get::<_, Option<c_int>>(0).unwrap_err()
283 ));
284
285 assert!(is_invalid_column_type(row.get::<_, c_int>(1).unwrap_err()));
287 assert!(is_invalid_column_type(row.get::<_, i64>(1).err().unwrap()));
288 assert!(is_invalid_column_type(
289 row.get::<_, c_double>(1).unwrap_err()
290 ));
291 assert!(is_invalid_column_type(
292 row.get::<_, Vec<u8>>(1).unwrap_err()
293 ));
294 assert!(is_invalid_column_type(
295 row.get::<_, Option<c_int>>(1).unwrap_err()
296 ));
297
298 assert!(is_invalid_column_type(row.get::<_, String>(2).unwrap_err()));
300 assert!(is_invalid_column_type(
301 row.get::<_, Vec<u8>>(2).unwrap_err()
302 ));
303 assert!(is_invalid_column_type(
304 row.get::<_, Option<String>>(2).unwrap_err()
305 ));
306
307 assert!(is_invalid_column_type(row.get::<_, c_int>(3).unwrap_err()));
309 assert!(is_invalid_column_type(row.get::<_, i64>(3).err().unwrap()));
310 assert!(is_invalid_column_type(row.get::<_, String>(3).unwrap_err()));
311 assert!(is_invalid_column_type(
312 row.get::<_, Vec<u8>>(3).unwrap_err()
313 ));
314 assert!(is_invalid_column_type(
315 row.get::<_, Option<c_int>>(3).unwrap_err()
316 ));
317
318 assert!(is_invalid_column_type(row.get::<_, c_int>(4).unwrap_err()));
320 assert!(is_invalid_column_type(row.get::<_, i64>(4).err().unwrap()));
321 assert!(is_invalid_column_type(
322 row.get::<_, c_double>(4).unwrap_err()
323 ));
324 assert!(is_invalid_column_type(row.get::<_, String>(4).unwrap_err()));
325 assert!(is_invalid_column_type(
326 row.get::<_, Vec<u8>>(4).unwrap_err()
327 ));
328 #[cfg(feature = "time")]
329 assert!(is_invalid_column_type(
330 row.get::<_, time::OffsetDateTime>(4).unwrap_err()
331 ));
332 Ok(())
333 }
334
335 #[test]
336 fn test_dynamic_type() -> Result<()> {
337 use super::Value;
338 let db = checked_memory_handle()?;
339
340 db.execute(
341 "INSERT INTO foo(b, t, i, f) VALUES (X'0102', 'text', 1, 1.5)",
342 [],
343 )?;
344
345 let mut stmt = db.prepare("SELECT b, t, i, f, n FROM foo")?;
346 let mut rows = stmt.query([])?;
347
348 let row = rows.next()?.unwrap();
349 assert_eq!(Value::Blob(vec![1, 2]), row.get::<_, Value>(0)?);
350 assert_eq!(Value::Text(String::from("text")), row.get::<_, Value>(1)?);
351 assert_eq!(Value::Integer(1), row.get::<_, Value>(2)?);
352 match row.get::<_, Value>(3)? {
353 Value::Real(val) => assert!((1.5 - val).abs() < f64::EPSILON),
354 x => panic!("Invalid Value {x:?}"),
355 }
356 assert_eq!(Value::Null, row.get::<_, Value>(4)?);
357 Ok(())
358 }
359
360 macro_rules! test_conversion {
361 ($db_etc:ident, $insert_value:expr, $get_type:ty,expect $expected_value:expr) => {
362 $db_etc.insert_statement.execute(params![$insert_value])?;
363 let res = $db_etc
364 .query_statement
365 .query_row([], |row| row.get::<_, $get_type>(0));
366 assert_eq!(res?, $expected_value);
367 $db_etc.delete_statement.execute([])?;
368 };
369 ($db_etc:ident, $insert_value:expr, $get_type:ty,expect_from_sql_error) => {
370 $db_etc.insert_statement.execute(params![$insert_value])?;
371 let res = $db_etc
372 .query_statement
373 .query_row([], |row| row.get::<_, $get_type>(0));
374 res.unwrap_err();
375 $db_etc.delete_statement.execute([])?;
376 };
377 ($db_etc:ident, $insert_value:expr, $get_type:ty,expect_to_sql_error) => {
378 $db_etc
379 .insert_statement
380 .execute(params![$insert_value])
381 .unwrap_err();
382 };
383 }
384
385 #[test]
386 #[expect(clippy::float_cmp)]
387 fn test_numeric_conversions() -> Result<()> {
388 let db = Connection::open_in_memory()?;
390 db.execute_batch("CREATE TABLE foo (x)")?;
391
392 struct DbEtc<'conn> {
396 insert_statement: Statement<'conn>,
397 query_statement: Statement<'conn>,
398 delete_statement: Statement<'conn>,
399 }
400
401 let mut db_etc = DbEtc {
402 insert_statement: db.prepare("INSERT INTO foo VALUES (?1)")?,
403 query_statement: db.prepare("SELECT x FROM foo")?,
404 delete_statement: db.prepare("DELETE FROM foo")?,
405 };
406
407 test_conversion!(db_etc, 0u8, u8, expect 0u8);
409
410 test_conversion!(db_etc, 100u8, i8, expect 100i8);
412 test_conversion!(db_etc, 200u8, u8, expect 200u8);
413 test_conversion!(db_etc, 100u16, i8, expect 100i8);
414 test_conversion!(db_etc, 200u16, u8, expect 200u8);
415 test_conversion!(db_etc, u32::MAX, u64, expect u32::MAX as u64);
416 test_conversion!(db_etc, i64::MIN, i64, expect i64::MIN);
417 test_conversion!(db_etc, i64::MAX, i64, expect i64::MAX);
418 test_conversion!(db_etc, i64::MAX, u64, expect i64::MAX as u64);
419 test_conversion!(db_etc, 100usize, usize, expect 100usize);
420 test_conversion!(db_etc, 100u64, u64, expect 100u64);
421 test_conversion!(db_etc, i64::MAX as u64, u64, expect i64::MAX as u64);
422
423 test_conversion!(db_etc, 200u8, i8, expect_from_sql_error);
425 test_conversion!(db_etc, 400u16, i8, expect_from_sql_error);
426 test_conversion!(db_etc, 400u16, u8, expect_from_sql_error);
427 test_conversion!(db_etc, -1i8, u8, expect_from_sql_error);
428 test_conversion!(db_etc, i64::MIN, u64, expect_from_sql_error);
429 test_conversion!(db_etc, u64::MAX, i64, expect_to_sql_error);
430 test_conversion!(db_etc, u64::MAX, u64, expect_to_sql_error);
431 test_conversion!(db_etc, i64::MAX as u64 + 1, u64, expect_to_sql_error);
432
433 test_conversion!(db_etc, i64::MIN, f32, expect i64::MIN as f32);
435 test_conversion!(db_etc, i64::MAX, f32, expect i64::MAX as f32);
436 test_conversion!(db_etc, i64::MIN, f64, expect i64::MIN as f64);
437 test_conversion!(db_etc, i64::MAX, f64, expect i64::MAX as f64);
438
439 test_conversion!(db_etc, 0f64, i64, expect_from_sql_error);
442 Ok(())
443 }
444}