Skip to main content

diesel/mysql_like/types/
primitives.rs

1use crate::Queryable;
2use crate::deserialize::FromSqlRef;
3use crate::deserialize::{self, FromSql};
4use crate::mysql_like::MysqlLikeBackend;
5use crate::mysql_like::{MysqlValue, NumericRepresentation};
6use crate::result::Error::DeserializationError;
7use crate::sql_types::{BigInt, Binary, Double, Float, Integer, SmallInt, Text};
8use core::error::Error;
9use core::str::{self, FromStr};
10
11fn decimal_to_integer<T>(bytes: &[u8]) -> deserialize::Result<T>
12where
13    T: FromStr,
14    T::Err: Error + Send + Sync + 'static,
15{
16    let string = str::from_utf8(bytes)?;
17    let mut split = string.split('.');
18    let integer_portion = split.next().unwrap_or_default();
19    let _decimal_portion = split.next().unwrap_or_default();
20    if split.next().is_some() {
21        Err(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("Invalid decimal format: {0:?}",
                string))
    })format!("Invalid decimal format: {string:?}").into())
22    } else {
23        Ok(integer_portion.parse()?)
24    }
25}
26
27#[allow(clippy::cast_possible_truncation)] // that's what we want here
28fn f32_to_i64(f: f32) -> deserialize::Result<i64> {
29    if f <= i64::MAX as f32 && f >= i64::MIN as f32 {
30        Ok(f.trunc() as i64)
31    } else {
32        Err(Box::new(DeserializationError(
33            "Numeric overflow/underflow occurred".into(),
34        )) as _)
35    }
36}
37
38#[allow(clippy::cast_possible_truncation)] // that's what we want here
39fn f64_to_i64(f: f64) -> deserialize::Result<i64> {
40    if f <= i64::MAX as f64 && f >= i64::MIN as f64 {
41        Ok(f.trunc() as i64)
42    } else {
43        Err(Box::new(DeserializationError(
44            "Numeric overflow/underflow occurred".into(),
45        )) as _)
46    }
47}
48
49impl<DB: MysqlLikeBackend> FromSql<SmallInt, DB> for i16 {
50    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
51        match value.numeric_value()? {
52            NumericRepresentation::Tiny(x) => Ok(x.into()),
53            NumericRepresentation::Small(x) => Ok(x),
54            NumericRepresentation::Medium(x) => x.try_into().map_err(|_| {
55                Box::new(DeserializationError(
56                    "Numeric overflow/underflow occurred".into(),
57                )) as _
58            }),
59            NumericRepresentation::Big(x) => x.try_into().map_err(|_| {
60                Box::new(DeserializationError(
61                    "Numeric overflow/underflow occurred".into(),
62                )) as _
63            }),
64            NumericRepresentation::Float(x) => f32_to_i64(x)?.try_into().map_err(|_| {
65                Box::new(DeserializationError(
66                    "Numeric overflow/underflow occurred".into(),
67                )) as _
68            }),
69            NumericRepresentation::Double(x) => f64_to_i64(x)?.try_into().map_err(|_| {
70                Box::new(DeserializationError(
71                    "Numeric overflow/underflow occurred".into(),
72                )) as _
73            }),
74            NumericRepresentation::Decimal(bytes) => decimal_to_integer(bytes),
75        }
76    }
77}
78
79impl<DB: MysqlLikeBackend> FromSql<Integer, DB> for i32 {
80    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
81        match value.numeric_value()? {
82            NumericRepresentation::Tiny(x) => Ok(x.into()),
83            NumericRepresentation::Small(x) => Ok(x.into()),
84            NumericRepresentation::Medium(x) => Ok(x),
85            NumericRepresentation::Big(x) => x.try_into().map_err(|_| {
86                Box::new(DeserializationError(
87                    "Numeric overflow/underflow occurred".into(),
88                )) as _
89            }),
90            NumericRepresentation::Float(x) => f32_to_i64(x).and_then(|i| {
91                i.try_into().map_err(|_| {
92                    Box::new(DeserializationError(
93                        "Numeric overflow/underflow occurred".into(),
94                    )) as _
95                })
96            }),
97            NumericRepresentation::Double(x) => f64_to_i64(x).and_then(|i| {
98                i.try_into().map_err(|_| {
99                    Box::new(DeserializationError(
100                        "Numeric overflow/underflow occurred".into(),
101                    )) as _
102                })
103            }),
104            NumericRepresentation::Decimal(bytes) => decimal_to_integer(bytes),
105        }
106    }
107}
108
109impl<DB: MysqlLikeBackend> FromSql<BigInt, DB> for i64 {
110    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
111        match value.numeric_value()? {
112            NumericRepresentation::Tiny(x) => Ok(x.into()),
113            NumericRepresentation::Small(x) => Ok(x.into()),
114            NumericRepresentation::Medium(x) => Ok(x.into()),
115            NumericRepresentation::Big(x) => Ok(x),
116            NumericRepresentation::Float(x) => f32_to_i64(x),
117            NumericRepresentation::Double(x) => f64_to_i64(x),
118            NumericRepresentation::Decimal(bytes) => decimal_to_integer(bytes),
119        }
120    }
121}
122
123impl<DB: MysqlLikeBackend> FromSql<Float, DB> for f32 {
124    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
125        match value.numeric_value()? {
126            NumericRepresentation::Tiny(x) => Ok(x.into()),
127            NumericRepresentation::Small(x) => Ok(x.into()),
128            NumericRepresentation::Medium(x) => Ok(x as Self),
129            NumericRepresentation::Big(x) => Ok(x as Self),
130            NumericRepresentation::Float(x) => Ok(x),
131            // there is currently no way to do this in a better way
132            #[allow(clippy::cast_possible_truncation)]
133            NumericRepresentation::Double(x) => Ok(x as Self),
134            NumericRepresentation::Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
135        }
136    }
137}
138
139impl<DB: MysqlLikeBackend> FromSql<Double, DB> for f64 {
140    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
141        match value.numeric_value()? {
142            NumericRepresentation::Tiny(x) => Ok(x.into()),
143            NumericRepresentation::Small(x) => Ok(x.into()),
144            NumericRepresentation::Medium(x) => Ok(x.into()),
145            NumericRepresentation::Big(x) => Ok(x as Self),
146            NumericRepresentation::Float(x) => Ok(x.into()),
147            NumericRepresentation::Double(x) => Ok(x),
148            NumericRepresentation::Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
149        }
150    }
151}
152
153/// The returned pointer is *only* valid for the lifetime to the argument of
154/// `from_sql`. This impl is intended for uses where you want to write a new
155/// impl in terms of `String`, but don't want to allocate. We have to return a
156/// raw pointer instead of a reference with a lifetime due to the structure of
157/// `FromSql`
158impl<DB: MysqlLikeBackend> FromSql<Text, DB> for *const str {
159    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
160        let string = str::from_utf8(value.as_bytes())?;
161        Ok(string as *const str)
162    }
163}
164
165impl<'a, DB: MysqlLikeBackend> FromSqlRef<'a, Text, DB> for &'a str {
166    fn from_sql(bytes: &'a mut MysqlValue<'_>) -> deserialize::Result<Self> {
167        let string = str::from_utf8(bytes.as_bytes())?;
168        Ok(string)
169    }
170}
171
172impl<DB: MysqlLikeBackend> Queryable<Text, DB> for *const str {
173    type Row = Self;
174
175    fn build(row: Self::Row) -> deserialize::Result<Self> {
176        Ok(row)
177    }
178}
179
180/// The returned pointer is *only* valid for the lifetime to the argument of
181/// `from_sql`. This impl is intended for uses where you want to write a new
182/// impl in terms of `Vec<u8>`, but don't want to allocate. We have to return a
183/// raw pointer instead of a reference with a lifetime due to the structure of
184/// `FromSql`
185impl<DB: MysqlLikeBackend> FromSql<Binary, DB> for *const [u8] {
186    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
187        Ok(value.as_bytes() as *const [u8])
188    }
189}
190
191impl<'a, DB: MysqlLikeBackend> FromSqlRef<'a, Binary, DB> for &'a [u8] {
192    fn from_sql(bytes: &'a mut MysqlValue<'_>) -> deserialize::Result<Self> {
193        Ok(bytes.as_bytes())
194    }
195}
196
197impl<DB: MysqlLikeBackend> Queryable<Binary, DB> for *const [u8] {
198    type Row = Self;
199
200    fn build(row: Self::Row) -> deserialize::Result<Self> {
201        Ok(row)
202    }
203}