odbc_iter/
odbc_type.rs

1//! Extra types that represent SQL data values but with extra from/to implementations for `OdbcType` so they can be bound to query parameter
2
3use std::fmt;
4
5// Allow for custom type implementation
6pub use odbc::{ffi, OdbcType};
7
8#[cfg(feature = "chrono")]
9mod sql_timestamp {
10    use super::*;
11    use chrono::naive::{NaiveDate, NaiveDateTime};
12    use chrono::{Datelike, Timelike};
13    use odbc::SqlTimestamp;
14
15    /// `SqlTimestamp` type that can be created from number of seconds since epoch as represented by `f64` value.
16    #[derive(Debug)]
17    pub struct UnixTimestamp(SqlTimestamp);
18
19    impl UnixTimestamp {
20        pub fn as_naive_date_time(&self) -> NaiveDateTime {
21            NaiveDate::from_ymd(
22                i32::from(self.0.year),
23                u32::from(self.0.month),
24                u32::from(self.0.day),
25            )
26            .and_hms_nano(
27                u32::from(self.0.hour),
28                u32::from(self.0.minute),
29                u32::from(self.0.second),
30                self.0.fraction,
31            )
32        }
33
34        pub fn into_inner(self) -> SqlTimestamp {
35            self.0
36        }
37    }
38
39    impl From<f64> for UnixTimestamp {
40        fn from(ts: f64) -> UnixTimestamp {
41            let ts =
42                NaiveDateTime::from_timestamp(ts as i64, (ts.fract() * 1_000_000_000.0) as u32);
43            ts.into()
44        }
45    }
46
47    impl From<NaiveDateTime> for UnixTimestamp {
48        fn from(value: NaiveDateTime) -> UnixTimestamp {
49            UnixTimestamp(SqlTimestamp {
50                day: value.day() as u16,
51                month: value.month() as u16,
52                year: value.year() as i16,
53                hour: value.hour() as u16,
54                minute: value.minute() as u16,
55                second: value.second() as u16,
56                fraction: value.nanosecond(),
57            })
58        }
59    }
60
61    unsafe impl<'a> OdbcType<'a> for UnixTimestamp {
62        fn sql_data_type() -> ffi::SqlDataType {
63            SqlTimestamp::sql_data_type()
64        }
65        fn c_data_type() -> ffi::SqlCDataType {
66            SqlTimestamp::c_data_type()
67        }
68
69        fn convert(buffer: &'a [u8]) -> Self {
70            UnixTimestamp(SqlTimestamp::convert(buffer))
71        }
72
73        fn column_size(&self) -> ffi::SQLULEN {
74            self.0.column_size()
75        }
76        fn value_ptr(&self) -> ffi::SQLPOINTER {
77            self.0.value_ptr()
78        }
79    }
80
81    #[cfg(test)]
82    mod tests {
83        pub use super::*;
84
85        #[test]
86        fn test_timestamp() {
87            let ts: UnixTimestamp = 1547115460.2291234.into();
88
89            assert_eq!(ts.0.year, 2019);
90            assert_eq!(ts.0.month, 1);
91            assert_eq!(ts.0.day, 10);
92            assert_eq!(ts.0.hour, 10);
93            assert_eq!(ts.0.minute, 17);
94            assert_eq!(ts.0.second, 40);
95            assert_eq!(ts.0.fraction / 1000, 229123); // need to round it up as precision is not best
96        }
97
98        #[test]
99        fn test_timestamp_as_date_time() {
100            let ts: UnixTimestamp = 1547115460.2291234.into();
101            assert_eq!(ts.as_naive_date_time().timestamp_millis(), 1547115460229);
102        }
103    }
104}
105
106#[cfg(feature = "chrono")]
107pub use sql_timestamp::*;
108
109use std::borrow::Cow;
110
111/// Owned or borrowed string that can be bound as statement parameter.
112#[derive(PartialEq, Eq, Debug)]
113pub struct CowString<'s>(pub Cow<'s, str>);
114
115impl<'s> From<String> for CowString<'s> {
116    fn from(s: String) -> CowString<'static> {
117        CowString(Cow::Owned(s))
118    }
119}
120
121impl<'s> From<&'s str> for CowString<'s> {
122    fn from(s: &'s str) -> CowString<'s> {
123        CowString(Cow::Borrowed(s))
124    }
125}
126
127impl<'s> From<Cow<'s, str>> for CowString<'s> {
128    fn from(s: Cow<'s, str>) -> CowString<'s> {
129        CowString(s)
130    }
131}
132
133unsafe impl<'s> OdbcType<'s> for CowString<'s> {
134    fn sql_data_type() -> ffi::SqlDataType {
135        String::sql_data_type()
136    }
137    fn c_data_type() -> ffi::SqlCDataType {
138        String::c_data_type()
139    }
140
141    fn convert(buffer: &'s [u8]) -> Self {
142        CowString(Cow::Owned(String::convert(buffer)))
143    }
144
145    fn column_size(&self) -> ffi::SQLULEN {
146        self.0.as_ref().column_size()
147    }
148
149    fn value_ptr(&self) -> ffi::SQLPOINTER {
150        self.0.as_ref().value_ptr()
151    }
152}
153
154/// UTF-16 encoded string that can be bound as statement parameter.
155#[derive(PartialEq, Eq)]
156pub struct StringUtf16(pub Vec<u16>);
157
158impl fmt::Debug for StringUtf16 {
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        write!(f, "N{:?}", String::from_utf16(&self.0).expect("StringUtf16 is not valid UTF-16 encoded string"))
161    }
162}
163
164impl From<String> for StringUtf16 {
165    fn from(s: String) -> StringUtf16 {
166        s.as_str().into()
167    }
168}
169
170impl From<&str> for StringUtf16 {
171    fn from(s: &str) -> StringUtf16 {
172        StringUtf16(s.encode_utf16().collect())
173    }
174}
175
176unsafe impl<'a> OdbcType<'a> for StringUtf16 {
177    fn sql_data_type() -> ffi::SqlDataType {
178        <&[u16]>::sql_data_type()
179    }
180    fn c_data_type() -> ffi::SqlCDataType {
181        <&[u16]>::c_data_type()
182    }
183
184    fn convert(buffer: &[u8]) -> Self {
185        StringUtf16(<&[u16]>::convert(buffer).to_owned())
186    }
187
188    fn column_size(&self) -> ffi::SQLULEN {
189        <&[u16]>::column_size(&self.0.as_slice())
190    }
191
192    fn value_ptr(&self) -> ffi::SQLPOINTER {
193        self.0.as_ptr() as *const &[u16] as ffi::SQLPOINTER
194    }
195}
196
197#[cfg(feature = "serde")]
198impl<'de> serde::de::Deserialize<'de> for StringUtf16 {
199    fn deserialize<D>(deserializer: D) -> std::result::Result<StringUtf16, D::Error>
200    where
201        D: serde::de::Deserializer<'de>,
202    {
203        String::deserialize(deserializer)
204            .map(From::from)
205    }
206}