Skip to main content

diesel/mysql/types/
primitives.rs

1#[cfg(feature = "mysql_backend")]
2use crate::deserialize::FromSqlRef;
3use crate::deserialize::{self, FromSql};
4use crate::mysql::{Mysql, MysqlValue, NumericRepresentation};
5use crate::result::Error::DeserializationError;
6use crate::sql_types::{BigInt, Binary, Double, Float, Integer, SmallInt, Text};
7use crate::Queryable;
8use core::error::Error;
9use core::str::{self, FromStr};
10
11pub(super) fn 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
27pub(super) fn overflow() -> Box<dyn Error + Send + Sync> {
28    Box::new(DeserializationError(
29        "Numeric overflow/underflow occurred".into(),
30    ))
31}
32
33/// Converts between the integer widths MySQL transmits, reporting an error
34/// rather than wrapping when the value does not fit the target.
35pub(super) fn narrow<T, U: TryFrom<T>>(value: T) -> deserialize::Result<U> {
36    U::try_from(value).map_err(|_| overflow())
37}
38
39#[allow(clippy::cast_possible_truncation)] // that's what we want here
40pub(super) fn f32_to_i64(f: f32) -> deserialize::Result<i64> {
41    if f <= i64::MAX as f32 && f >= i64::MIN as f32 {
42        Ok(f.trunc() as i64)
43    } else {
44        Err(overflow())
45    }
46}
47
48#[allow(clippy::cast_possible_truncation)] // that's what we want here
49pub(super) fn f64_to_i64(f: f64) -> deserialize::Result<i64> {
50    if f <= i64::MAX as f64 && f >= i64::MIN as f64 {
51        Ok(f.trunc() as i64)
52    } else {
53        Err(overflow())
54    }
55}
56
57#[cfg(feature = "mysql_backend")]
58impl FromSql<SmallInt, Mysql> for i16 {
59    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
60        match value.numeric_value()? {
61            NumericRepresentation::Tiny(x) => Ok(x.into()),
62            NumericRepresentation::UnsignedTiny(x) => Ok(x.into()),
63            NumericRepresentation::Small(x) => Ok(x),
64            NumericRepresentation::UnsignedSmall(x) => narrow(x),
65            NumericRepresentation::Medium(x) => narrow(x),
66            NumericRepresentation::UnsignedMedium(x) => narrow(x),
67            NumericRepresentation::Big(x) => narrow(x),
68            NumericRepresentation::UnsignedBig(x) => narrow(x),
69            NumericRepresentation::Float(x) => narrow(f32_to_i64(x)?),
70            NumericRepresentation::Double(x) => narrow(f64_to_i64(x)?),
71            NumericRepresentation::Decimal(bytes) => decimal_to_integer(bytes),
72        }
73    }
74}
75
76#[cfg(feature = "mysql_backend")]
77impl FromSql<Integer, Mysql> for i32 {
78    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
79        match value.numeric_value()? {
80            NumericRepresentation::Tiny(x) => Ok(x.into()),
81            NumericRepresentation::UnsignedTiny(x) => Ok(x.into()),
82            NumericRepresentation::Small(x) => Ok(x.into()),
83            NumericRepresentation::UnsignedSmall(x) => Ok(x.into()),
84            NumericRepresentation::Medium(x) => Ok(x),
85            NumericRepresentation::UnsignedMedium(x) => narrow(x),
86            NumericRepresentation::Big(x) => narrow(x),
87            NumericRepresentation::UnsignedBig(x) => narrow(x),
88            NumericRepresentation::Float(x) => narrow(f32_to_i64(x)?),
89            NumericRepresentation::Double(x) => narrow(f64_to_i64(x)?),
90            NumericRepresentation::Decimal(bytes) => decimal_to_integer(bytes),
91        }
92    }
93}
94
95#[cfg(feature = "mysql_backend")]
96impl FromSql<BigInt, Mysql> for i64 {
97    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
98        match value.numeric_value()? {
99            NumericRepresentation::Tiny(x) => Ok(x.into()),
100            NumericRepresentation::UnsignedTiny(x) => Ok(x.into()),
101            NumericRepresentation::Small(x) => Ok(x.into()),
102            NumericRepresentation::UnsignedSmall(x) => Ok(x.into()),
103            NumericRepresentation::Medium(x) => Ok(x.into()),
104            NumericRepresentation::UnsignedMedium(x) => Ok(x.into()),
105            NumericRepresentation::Big(x) => Ok(x),
106            NumericRepresentation::UnsignedBig(x) => narrow(x),
107            NumericRepresentation::Float(x) => f32_to_i64(x),
108            NumericRepresentation::Double(x) => f64_to_i64(x),
109            NumericRepresentation::Decimal(bytes) => decimal_to_integer(bytes),
110        }
111    }
112}
113
114#[cfg(feature = "mysql_backend")]
115impl FromSql<Float, Mysql> for f32 {
116    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
117        // Precision loss beyond the mantissa is intended for a float column.
118        match value.numeric_value()? {
119            NumericRepresentation::Tiny(x) => Ok(x.into()),
120            NumericRepresentation::UnsignedTiny(x) => Ok(x.into()),
121            NumericRepresentation::Small(x) => Ok(x.into()),
122            NumericRepresentation::UnsignedSmall(x) => Ok(x.into()),
123            NumericRepresentation::Medium(x) => Ok(x as Self),
124            NumericRepresentation::UnsignedMedium(x) => Ok(x as Self),
125            NumericRepresentation::Big(x) => Ok(x as Self),
126            NumericRepresentation::UnsignedBig(x) => Ok(x as Self),
127            NumericRepresentation::Float(x) => Ok(x),
128            // there is currently no way to do this in a better way
129            #[allow(clippy::cast_possible_truncation)]
130            NumericRepresentation::Double(x) => Ok(x as Self),
131            NumericRepresentation::Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
132        }
133    }
134}
135
136#[cfg(feature = "mysql_backend")]
137impl FromSql<Double, Mysql> for f64 {
138    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
139        // Precision loss beyond the mantissa is intended for a double column.
140        match value.numeric_value()? {
141            NumericRepresentation::Tiny(x) => Ok(x.into()),
142            NumericRepresentation::UnsignedTiny(x) => Ok(x.into()),
143            NumericRepresentation::Small(x) => Ok(x.into()),
144            NumericRepresentation::UnsignedSmall(x) => Ok(x.into()),
145            NumericRepresentation::Medium(x) => Ok(x.into()),
146            NumericRepresentation::UnsignedMedium(x) => Ok(x.into()),
147            NumericRepresentation::Big(x) => Ok(x as Self),
148            NumericRepresentation::UnsignedBig(x) => Ok(x as Self),
149            NumericRepresentation::Float(x) => Ok(x.into()),
150            NumericRepresentation::Double(x) => Ok(x),
151            NumericRepresentation::Decimal(bytes) => Ok(str::from_utf8(bytes)?.parse()?),
152        }
153    }
154}
155
156/// The returned pointer is *only* valid for the lifetime to the argument of
157/// `from_sql`. This impl is intended for uses where you want to write a new
158/// impl in terms of `String`, but don't want to allocate. We have to return a
159/// raw pointer instead of a reference with a lifetime due to the structure of
160/// `FromSql`
161#[cfg(feature = "mysql_backend")]
162impl FromSql<Text, Mysql> for *const str {
163    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
164        let string = str::from_utf8(value.as_bytes())?;
165        Ok(string as *const str)
166    }
167}
168
169#[cfg(feature = "mysql_backend")]
170impl<'a> FromSqlRef<'a, Text, Mysql> for &'a str {
171    fn from_sql(bytes: &'a mut MysqlValue<'_>) -> deserialize::Result<Self> {
172        let string = str::from_utf8(bytes.as_bytes())?;
173        Ok(string)
174    }
175}
176
177#[cfg(feature = "mysql_backend")]
178impl Queryable<Text, Mysql> for *const str {
179    type Row = Self;
180
181    fn build(row: Self::Row) -> deserialize::Result<Self> {
182        Ok(row)
183    }
184}
185
186/// The returned pointer is *only* valid for the lifetime to the argument of
187/// `from_sql`. This impl is intended for uses where you want to write a new
188/// impl in terms of `Vec<u8>`, but don't want to allocate. We have to return a
189/// raw pointer instead of a reference with a lifetime due to the structure of
190/// `FromSql`
191#[cfg(feature = "mysql_backend")]
192impl FromSql<Binary, Mysql> for *const [u8] {
193    fn from_sql(value: MysqlValue<'_>) -> deserialize::Result<Self> {
194        Ok(value.as_bytes() as *const [u8])
195    }
196}
197
198#[cfg(feature = "mysql_backend")]
199impl<'a> FromSqlRef<'a, Binary, Mysql> for &'a [u8] {
200    fn from_sql(bytes: &'a mut MysqlValue<'_>) -> deserialize::Result<Self> {
201        Ok(bytes.as_bytes())
202    }
203}
204
205#[cfg(feature = "mysql_backend")]
206impl Queryable<Binary, Mysql> for *const [u8] {
207    type Row = Self;
208
209    fn build(row: Self::Row) -> deserialize::Result<Self> {
210        Ok(row)
211    }
212}