Skip to main content

tds_driver/
from_sql.rs

1use crate::{tds::Numeric, xml::XmlData, ColumnData};
2use uuid::Uuid;
3
4/// A conversion trait from a TDS type by-reference.
5///
6/// A `FromSql` implementation for a Rust type is needed for using it as a
7/// return parameter from [`Row#get`] or [`Row#try_get`] methods. The following
8/// Rust types are already implemented to match the given server types:
9///
10/// |Rust type|Server type|
11/// |--------|--------|
12/// |`u8`|`tinyint`|
13/// |`i16`|`smallint`|
14/// |`i32`|`int`|
15/// |`i64`|`bigint`|
16/// |`f32`|`float(24)`; `numeric`/`decimal`/`money`/`smallmoney`, rounded|
17/// |`f64`|`float(53)`; `numeric`/`decimal`/`money`/`smallmoney`, rounded|
18/// |`bool`|`bit`|
19/// |`String`/`&str`|`nvarchar`/`varchar`/`nchar`/`char`/`ntext`/`text`|
20/// |`Vec<u8>`/`&[u8]`|`binary`/`varbinary`/`image`|
21/// |[`Uuid`]|`uniqueidentifier`|
22/// |[`Numeric`]|`numeric`/`decimal`/`money`/`smallmoney`|
23/// |[`Decimal`] (with feature flag `rust_decimal`)|`numeric`/`decimal`/`money`/`smallmoney`|
24/// |[`XmlData`]|`xml`|
25/// |[`NaiveDateTime`] (with feature flag `chrono`)|`datetime`/`datetime2`/`smalldatetime`|
26/// |[`NaiveDate`] (with feature flag `chrono`)|`date`|
27/// |[`NaiveTime`] (with feature flag `chrono`)|`time`|
28/// |[`DateTime`] (with feature flag `chrono`)|`datetimeoffset`|
29///
30/// See the [`time`] module for more information about the date and time structs.
31///
32/// [`Row#get`]: struct.Row.html#method.get
33/// [`Row#try_get`]: struct.Row.html#method.try_get
34/// [`time`]: time/index.html
35/// [`Uuid`]: struct.Uuid.html
36/// [`Numeric`]: numeric/struct.Numeric.html
37/// [`Decimal`]: numeric/struct.Decimal.html
38/// [`XmlData`]: xml/struct.XmlData.html
39/// [`NaiveDateTime`]: time/chrono/struct.NaiveDateTime.html
40/// [`NaiveDate`]: time/chrono/struct.NaiveDate.html
41/// [`NaiveTime`]: time/chrono/struct.NaiveTime.html
42/// [`DateTime`]: time/chrono/struct.DateTime.html
43pub trait FromSql<'a>
44where
45    Self: Sized + 'a,
46{
47    /// Returns the value, `None` being a null value, copying the value.
48    fn from_sql(value: &'a ColumnData<'static>) -> crate::Result<Option<Self>>;
49}
50
51/// A conversion trait from a TDS type by-value.
52pub trait FromSqlOwned
53where
54    Self: Sized,
55{
56    /// Returns the value, `None` being a null value, taking the ownership.
57    fn from_sql_owned(value: ColumnData<'static>) -> crate::Result<Option<Self>>;
58}
59
60from_sql!(bool: ColumnData::Bit(val) => (*val, val));
61from_sql!(u8: ColumnData::U8(val) => (*val, val), ColumnData::I32(None) => (None, None));
62from_sql!(i16: ColumnData::I16(val) => (*val, val), ColumnData::U8(None) => (None, None), ColumnData::I32(None) => (None, None));
63from_sql!(i32: ColumnData::I32(val) => (*val, val), ColumnData::I16(val) => (val.map(i32::from), val.map(i32::from)), ColumnData::U8(None) => (None, None));
64from_sql!(i64: ColumnData::I64(val) => (*val, val), ColumnData::U8(None) => (None, None), ColumnData::I32(None) => (None, None));
65// Numeric: money/smallmoney decode exactly to it, and used to decode to f64.
66from_sql!(f32: ColumnData::F32(val) => (*val, val), ColumnData::Numeric(n) => (n.map(Numeric::to_f32), n.map(Numeric::to_f32)));
67from_sql!(f64: ColumnData::F64(val) => (*val, val), ColumnData::Numeric(n) => (n.map(Numeric::to_f64), n.map(Numeric::to_f64)));
68from_sql!(Uuid: ColumnData::Guid(val) => (*val, val));
69from_sql!(Numeric: ColumnData::Numeric(n) => (*n, n));
70
71impl FromSqlOwned for XmlData {
72    fn from_sql_owned(value: ColumnData<'static>) -> crate::Result<Option<Self>> {
73        match value {
74            ColumnData::Xml(data) => Ok(data.map(|data| data.into_owned())),
75            v => Err(crate::Error::Conversion(
76                format!("cannot interpret {:?} as a String value", v).into(),
77            )),
78        }
79    }
80}
81
82impl<'a> FromSql<'a> for &'a XmlData {
83    fn from_sql(value: &'a ColumnData<'static>) -> crate::Result<Option<Self>> {
84        match value {
85            ColumnData::Xml(data) => Ok(data.as_ref().map(|s| s.as_ref())),
86            v => Err(crate::Error::Conversion(
87                format!("cannot interpret {:?} as a String value", v).into(),
88            )),
89        }
90    }
91}
92
93impl FromSqlOwned for String {
94    fn from_sql_owned(value: ColumnData<'static>) -> crate::Result<Option<Self>> {
95        match value {
96            ColumnData::String(s) | ColumnData::VarChar(s) => Ok(s.map(|s| s.into_owned())),
97            v => Err(crate::Error::Conversion(
98                format!("cannot interpret {:?} as a String value", v).into(),
99            )),
100        }
101    }
102}
103
104impl<'a> FromSql<'a> for &'a str {
105    fn from_sql(value: &'a ColumnData<'static>) -> crate::Result<Option<Self>> {
106        match value {
107            ColumnData::String(s) | ColumnData::VarChar(s) => Ok(s.as_ref().map(|s| s.as_ref())),
108            v => Err(crate::Error::Conversion(
109                format!("cannot interpret {:?} as a String value", v).into(),
110            )),
111        }
112    }
113}
114
115impl FromSqlOwned for Vec<u8> {
116    fn from_sql_owned(value: ColumnData<'static>) -> crate::Result<Option<Self>> {
117        match value {
118            ColumnData::Binary(b) => Ok(b.map(|s| s.into_owned())),
119            v => Err(crate::Error::Conversion(
120                format!("cannot interpret {:?} as a String value", v).into(),
121            )),
122        }
123    }
124}
125
126impl<'a> FromSql<'a> for &'a [u8] {
127    fn from_sql(value: &'a ColumnData<'static>) -> crate::Result<Option<Self>> {
128        match value {
129            ColumnData::Binary(b) => Ok(b.as_ref().map(|s| s.as_ref())),
130            v => Err(crate::Error::Conversion(
131                format!("cannot interpret {:?} as a &[u8] value", v).into(),
132            )),
133        }
134    }
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    #[test]
142    fn i16_column_converts_to_i32() {
143        let data = ColumnData::I16(Some(8));
144        assert_eq!(Some(8i32), i32::from_sql(&data).unwrap());
145        assert_eq!(Some(8i32), i32::from_sql_owned(data).unwrap());
146    }
147
148    #[test]
149    fn null_i16_column_converts_to_i32() {
150        let data = ColumnData::I16(None);
151        assert_eq!(None, i32::from_sql(&data).unwrap());
152        assert_eq!(None, i32::from_sql_owned(ColumnData::I16(None)).unwrap());
153    }
154
155    #[test]
156    fn bool_from_bit() {
157        let data = ColumnData::Bit(Some(true));
158        assert_eq!(Some(true), bool::from_sql(&data).unwrap());
159        assert_eq!(Some(true), bool::from_sql_owned(data).unwrap());
160    }
161
162    #[test]
163    fn u8_from_u8_and_null_i32() {
164        let data = ColumnData::U8(Some(5));
165        assert_eq!(Some(5u8), u8::from_sql(&data).unwrap());
166        assert_eq!(Some(5u8), u8::from_sql_owned(data).unwrap());
167
168        let null = ColumnData::I32(None);
169        assert_eq!(None, u8::from_sql(&null).unwrap());
170        assert_eq!(None, u8::from_sql_owned(null).unwrap());
171    }
172
173    #[test]
174    fn i16_from_wrong_variant_errors() {
175        let data = ColumnData::F64(Some(1.0));
176        let err = i16::from_sql(&data).unwrap_err();
177        assert!(format!("{}", err).contains("cannot interpret"));
178    }
179
180    #[test]
181    fn i64_from_i64_and_null() {
182        let data = ColumnData::I64(Some(42));
183        assert_eq!(Some(42i64), i64::from_sql(&data).unwrap());
184        assert_eq!(Some(42i64), i64::from_sql_owned(data).unwrap());
185
186        let null = ColumnData::U8(None);
187        assert_eq!(None, i64::from_sql_owned(null).unwrap());
188    }
189
190    #[test]
191    fn f32_and_f64_from_sql() {
192        let f32_data = ColumnData::F32(Some(1.5));
193        assert_eq!(Some(1.5f32), f32::from_sql(&f32_data).unwrap());
194
195        let f64_data = ColumnData::F64(Some(2.5));
196        assert_eq!(Some(2.5f64), f64::from_sql(&f64_data).unwrap());
197    }
198
199    #[test]
200    fn floats_from_numeric() {
201        let money = ColumnData::Numeric(Some(Numeric::new_with_scale(12300, 4)));
202        assert_eq!(Some(1.23f64), f64::from_sql(&money).unwrap());
203        assert_eq!(Some(1.23f32), f32::from_sql(&money).unwrap());
204        assert_eq!(Some(1.23f64), f64::from_sql_owned(money.clone()).unwrap());
205        assert_eq!(Some(1.23f32), f32::from_sql_owned(money).unwrap());
206
207        let null = ColumnData::Numeric(None);
208        assert_eq!(None, f64::from_sql(&null).unwrap());
209        assert_eq!(None, f32::from_sql_owned(null).unwrap());
210    }
211
212    #[test]
213    fn uuid_from_guid() {
214        let uuid = Uuid::new_v4();
215        let data = ColumnData::Guid(Some(uuid));
216        assert_eq!(Some(uuid), Uuid::from_sql(&data).unwrap());
217        assert_eq!(Some(uuid), Uuid::from_sql_owned(data).unwrap());
218    }
219
220    #[test]
221    fn numeric_from_numeric() {
222        let numeric = crate::tds::Numeric::new_with_scale(1234, 2);
223        let data = ColumnData::Numeric(Some(numeric));
224        assert_eq!(Some(numeric), Numeric::from_sql(&data).unwrap());
225        assert_eq!(Some(numeric), Numeric::from_sql_owned(data).unwrap());
226    }
227
228    #[test]
229    fn xml_data_owned_and_borrowed() {
230        let xml = XmlData::new("<a/>".to_string());
231        let data = ColumnData::Xml(Some(std::borrow::Cow::Owned(xml.clone())));
232
233        let borrowed = <&XmlData as FromSql>::from_sql(&data).unwrap().unwrap();
234        assert_eq!(borrowed.to_string(), xml.to_string());
235
236        let owned = XmlData::from_sql_owned(data).unwrap().unwrap();
237        assert_eq!(owned.to_string(), xml.to_string());
238    }
239
240    #[test]
241    fn xml_data_wrong_variant_errors() {
242        let data = ColumnData::I32(Some(1));
243        let err = XmlData::from_sql_owned(data).unwrap_err();
244        assert!(format!("{}", err).contains("cannot interpret"));
245
246        let data = ColumnData::I32(Some(1));
247        let err = <&XmlData as FromSql>::from_sql(&data).unwrap_err();
248        assert!(format!("{}", err).contains("cannot interpret"));
249    }
250
251    #[test]
252    fn string_owned_and_borrowed_str() {
253        let data = ColumnData::String(Some(std::borrow::Cow::Borrowed("hello")));
254        let borrowed = <&str as FromSql>::from_sql(&data).unwrap();
255        assert_eq!(Some("hello"), borrowed);
256
257        let owned = String::from_sql_owned(data).unwrap();
258        assert_eq!(Some("hello".to_string()), owned);
259    }
260
261    #[test]
262    fn string_wrong_variant_errors() {
263        let data = ColumnData::I32(Some(1));
264        let err = String::from_sql_owned(data).unwrap_err();
265        assert!(format!("{}", err).contains("cannot interpret"));
266
267        let data = ColumnData::I32(Some(1));
268        let err = <&str as FromSql>::from_sql(&data).unwrap_err();
269        assert!(format!("{}", err).contains("cannot interpret"));
270    }
271
272    #[test]
273    fn binary_owned_and_borrowed_slice() {
274        let bytes = vec![1u8, 2, 3];
275        let data = ColumnData::Binary(Some(std::borrow::Cow::Owned(bytes.clone())));
276
277        let borrowed = <&[u8] as FromSql>::from_sql(&data).unwrap();
278        assert_eq!(Some(bytes.as_slice()), borrowed);
279
280        let owned = Vec::<u8>::from_sql_owned(data).unwrap();
281        assert_eq!(Some(bytes), owned);
282    }
283
284    #[test]
285    fn binary_wrong_variant_errors() {
286        let data = ColumnData::I32(Some(1));
287        let err = Vec::<u8>::from_sql_owned(data).unwrap_err();
288        assert!(format!("{}", err).contains("cannot interpret"));
289
290        let data = ColumnData::I32(Some(1));
291        let err = <&[u8] as FromSql>::from_sql(&data).unwrap_err();
292        assert!(format!("{}", err).contains("cannot interpret"));
293    }
294
295    #[test]
296    fn null_string_and_binary_values() {
297        let data = ColumnData::String(None);
298        assert_eq!(None, String::from_sql_owned(data).unwrap());
299
300        let data = ColumnData::Binary(None);
301        assert_eq!(None, Vec::<u8>::from_sql_owned(data).unwrap());
302
303        let data = ColumnData::Xml(None);
304        assert_eq!(None, XmlData::from_sql_owned(data).unwrap());
305    }
306}