1use duckdb::Result;
4use duckdb::types::FromSql;
5use duckdb::types::FromSqlError;
6use duckdb::types::FromSqlResult;
7use duckdb::types::TimeUnit;
8use duckdb::types::ToSql;
9use duckdb::types::ToSqlOutput;
10use duckdb::types::ValueRef;
11
12use crate::DateTime;
13use crate::Precision;
14
15macro_rules! from_sql {
16 ($($precision:ident($var:ident) => $e:expr)*) => {
17 impl FromSql for DateTime {
18 fn column_result(value: ValueRef<'_>) -> FromSqlResult<Self> {
19 match value {
20 $(ValueRef::Timestamp(TimeUnit::$precision, $var) => Ok($e),
21 ValueRef::Time64(TimeUnit::$precision, $var) => Ok($e),)*
22 _ => Err(FromSqlError::InvalidType),
23 }
24 }
25 }
26 }
27}
28
29from_sql! {
30 Second(seconds) => DateTime::from_timestamp(seconds, 0)
31 Millisecond(ms) => DateTime::from_timestamp(ms / 1_000, (ms % 1_000) as u32 * 1_000_000)
32 Microsecond(us) => DateTime::from_timestamp(us / 1_000_000, (us % 1_000_000) as u32 * 1_000)
33 Nanosecond(ns) => DateTime::from_timestamp(ns / 1_000_000_000, (ns % 1_000_000_000) as u32)
34}
35
36impl ToSql for DateTime {
37 fn to_sql(&self) -> Result<ToSqlOutput<'_>> {
38 match self.precision() {
39 Precision::Second =>
40 Ok(ToSqlOutput::Borrowed(ValueRef::Timestamp(TimeUnit::Second, self.as_seconds()))),
41 Precision::Millisecond => Ok(ToSqlOutput::Borrowed(ValueRef::Timestamp(
42 TimeUnit::Millisecond,
43 self.as_milliseconds(),
44 ))),
45 Precision::Microsecond => Ok(ToSqlOutput::Borrowed(ValueRef::Timestamp(
46 TimeUnit::Microsecond,
47 self.as_microseconds(),
48 ))),
49 Precision::Nanosecond => Ok(ToSqlOutput::Borrowed(ValueRef::Timestamp(
50 TimeUnit::Nanosecond,
51 (self.as_nanoseconds().try_into())
52 .map_err(|e| duckdb::Error::ToSqlConversionFailure(Box::new(e)))?,
53 ))),
54 }
55 }
56}
57
58#[cfg(test)]
59mod tests {
60 use assert2::check;
61
62 use super::*;
63 use crate::datetime;
64
65 #[test]
66 fn test_from_sql() -> FromSqlResult<()> {
67 use TimeUnit::*;
68 for (precision, multiplier) in
69 [(Second, 1), (Millisecond, 1_000), (Microsecond, 1_000_000), (Nanosecond, 1_000_000_000)]
70 {
71 let input = ValueRef::Timestamp(precision, 1335020400 * multiplier);
72 let dt = DateTime::column_result(input)?;
73 check!(dt == datetime! { 2012-04-21 15:00:00 });
74 let input = ValueRef::Time64(precision, 1335020400 * multiplier);
75 let dt = DateTime::column_result(input)?;
76 check!(dt == datetime! { 2012-04-21 15:00:00 });
77 }
78 Ok(())
79 }
80
81 #[test]
82 fn test_to_sql() -> Result<()> {
83 let dt = datetime! { 2012-04-21 15:00:00 };
84 let output = dt.to_sql()?;
85 if let ToSqlOutput::Borrowed(ValueRef::Timestamp(TimeUnit::Second, seconds)) = output {
86 check!(seconds == 1335020400);
87 } else {
88 check!(false, "Incorrect type");
89 }
90 Ok(())
91 }
92}