Skip to main content

diesel/mysql/types/
primitives.rs

1use crate::Queryable;
2#[cfg(feature = "mysql_backend")]
3use crate::deserialize::FromSqlRef;
4use crate::deserialize::{self, FromSql};
5use crate::mysql::{Mysql, 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
49#[cfg(feature = "mysql_backend")]
50impl FromSql<SmallInt, Mysql> for i16 {
51    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
52        match value.numeric_value()? {
53            NumericRepresentation::Tiny(x) => Ok(x.into()),
54            NumericRepresentation::Small(x) => Ok(x),
55            NumericRepresentation::Medium(x) => x.try_into().map_err(|_| {
56                Box::new(DeserializationError(
57                    "Numeric overflow/underflow occurred".into(),
58                )) as _
59            }),
60            NumericRepresentation::Big(x) => x.try_into().map_err(|_| {
61                Box::new(DeserializationError(
62                    "Numeric overflow/underflow occurred".into(),
63                )) as _
64            }),
65            NumericRepresentation::Float(x) => f32_to_i64(x)?.try_into().map_err(|_| {
66                Box::new(DeserializationError(
67                    "Numeric overflow/underflow occurred".into(),
68                )) as _
69            }),
70            NumericRepresentation::Double(x) => f64_to_i64(x)?.try_into().map_err(|_| {
71                Box::new(DeserializationError(
72                    "Numeric overflow/underflow occurred".into(),
73                )) as _
74            }),
75            NumericRepresentation::Decimal(bytes) => decimal_to_integer(bytes),
76        }
77    }
78}
79
80#[cfg(feature = "mysql_backend")]
81impl FromSql<Integer, Mysql> for i32 {
82    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
83        match value.numeric_value()? {
84            NumericRepresentation::Tiny(x) => Ok(x.into()),
85            NumericRepresentation::Small(x) => Ok(x.into()),
86            NumericRepresentation::Medium(x) => Ok(x),
87            NumericRepresentation::Big(x) => x.try_into().map_err(|_| {
88                Box::new(DeserializationError(
89                    "Numeric overflow/underflow occurred".into(),
90                )) as _
91            }),
92            NumericRepresentation::Float(x) => f32_to_i64(x).and_then(|i| {
93                i.try_into().map_err(|_| {
94                    Box::new(DeserializationError(
95                        "Numeric overflow/underflow occurred".into(),
96                    )) as _
97                })
98            }),
99            NumericRepresentation::Double(x) => f64_to_i64(x).and_then(|i| {
100                i.try_into().map_err(|_| {
101                    Box::new(DeserializationError(
102                        "Numeric overflow/underflow occurred".into(),
103                    )) as _
104                })
105            }),
106            NumericRepresentation::Decimal(bytes) => decimal_to_integer(bytes),
107        }
108    }
109}
110
111#[cfg(feature = "mysql_backend")]
112impl FromSql<BigInt, Mysql> for i64 {
113    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
114        match value.numeric_value()? {
115            NumericRepresentation::Tiny(x) => Ok(x.into()),
116            NumericRepresentation::Small(x) => Ok(x.into()),
117            NumericRepresentation::Medium(x) => Ok(x.into()),
118            NumericRepresentation::Big(x) => Ok(x),
119            NumericRepresentation::Float(x) => f32_to_i64(x),
120            NumericRepresentation::Double(x) => f64_to_i64(x),
121            NumericRepresentation::Decimal(bytes) => decimal_to_integer(bytes),
122        }
123    }
124}
125
126#[cfg(feature = "mysql_backend")]
127impl FromSql<Float, Mysql> for f32 {
128    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
129        match value.numeric_value()? {
130            NumericRepresentation::Tiny(x) => Ok(x.into()),
131            NumericRepresentation::Small(x) => Ok(x.into()),
132            NumericRepresentation::Medium(x) => Ok(x as Self),
133            NumericRepresentation::Big(x) => Ok(x as Self),
134            NumericRepresentation::Float(x) => Ok(x),
135            // there is currently no way to do this in a better way
136            #[allow(clippy::cast_possible_truncation)]
137            NumericRepresentation::Double(x) => Ok(x as Self),
138            NumericRepresentation::Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
139        }
140    }
141}
142
143#[cfg(feature = "mysql_backend")]
144impl FromSql<Double, Mysql> for f64 {
145    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
146        match value.numeric_value()? {
147            NumericRepresentation::Tiny(x) => Ok(x.into()),
148            NumericRepresentation::Small(x) => Ok(x.into()),
149            NumericRepresentation::Medium(x) => Ok(x.into()),
150            NumericRepresentation::Big(x) => Ok(x as Self),
151            NumericRepresentation::Float(x) => Ok(x.into()),
152            NumericRepresentation::Double(x) => Ok(x),
153            NumericRepresentation::Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
154        }
155    }
156}
157
158/// The returned pointer is *only* valid for the lifetime to the argument of
159/// `from_sql`. This impl is intended for uses where you want to write a new
160/// impl in terms of `String`, but don't want to allocate. We have to return a
161/// raw pointer instead of a reference with a lifetime due to the structure of
162/// `FromSql`
163#[cfg(feature = "mysql_backend")]
164impl FromSql<Text, Mysql> for *const str {
165    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
166        let string = str::from_utf8(value.as_bytes())?;
167        Ok(string as *const str)
168    }
169}
170
171#[cfg(feature = "mysql_backend")]
172impl<'a> FromSqlRef<'a, Text, Mysql> for &'a str {
173    fn from_sql(bytes: MysqlValue<'a>) -> deserialize::Result<Self> {
174        let string = str::from_utf8(bytes.as_bytes())?;
175        Ok(string)
176    }
177}
178
179#[cfg(feature = "mysql_backend")]
180impl Queryable<Text, Mysql> for *const str {
181    type Row = Self;
182
183    fn build(row: Self::Row) -> deserialize::Result<Self> {
184        Ok(row)
185    }
186}
187
188/// The returned pointer is *only* valid for the lifetime to the argument of
189/// `from_sql`. This impl is intended for uses where you want to write a new
190/// impl in terms of `Vec<u8>`, but don't want to allocate. We have to return a
191/// raw pointer instead of a reference with a lifetime due to the structure of
192/// `FromSql`
193#[cfg(feature = "mysql_backend")]
194impl FromSql<Binary, Mysql> for *const [u8] {
195    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
196        Ok(value.as_bytes() as *const [u8])
197    }
198}
199
200#[cfg(feature = "mysql_backend")]
201impl<'a> FromSqlRef<'a, Binary, Mysql> for &'a [u8] {
202    fn from_sql(bytes: MysqlValue<'a>) -> deserialize::Result<Self> {
203        Ok(bytes.as_bytes())
204    }
205}
206
207#[cfg(feature = "mysql_backend")]
208impl Queryable<Binary, Mysql> for *const [u8] {
209    type Row = Self;
210
211    fn build(row: Self::Row) -> deserialize::Result<Self> {
212        Ok(row)
213    }
214}