rust_decimal/postgres/
diesel.rs

1use crate::postgres::common::*;
2use crate::Decimal;
3use diesel::{
4    deserialize::{self, FromSql},
5    pg::data_types::PgNumeric,
6    pg::Pg,
7    serialize::{self, Output, ToSql},
8    sql_types::Numeric,
9};
10use std::error;
11
12impl<'a> TryFrom<&'a PgNumeric> for Decimal {
13    type Error = Box<dyn error::Error + Send + Sync>;
14
15    fn try_from(numeric: &'a PgNumeric) -> deserialize::Result<Self> {
16        let (neg, weight, scale, digits) = match *numeric {
17            PgNumeric::Positive {
18                weight,
19                scale,
20                ref digits,
21            } => (false, weight, scale, digits),
22            PgNumeric::Negative {
23                weight,
24                scale,
25                ref digits,
26            } => (true, weight, scale, digits),
27            PgNumeric::NaN => return Err(Box::from("NaN is not supported in Decimal")),
28        };
29
30        let Some(result) = Self::checked_from_postgres(PostgresDecimal {
31            neg,
32            weight,
33            scale,
34            digits: digits.iter().copied().map(|v| v.try_into().unwrap()),
35        }) else {
36            return Err(Box::new(crate::error::Error::ExceedsMaximumPossibleValue));
37        };
38        Ok(result)
39    }
40}
41
42impl TryFrom<PgNumeric> for Decimal {
43    type Error = Box<dyn error::Error + Send + Sync>;
44
45    fn try_from(numeric: PgNumeric) -> deserialize::Result<Self> {
46        (&numeric).try_into()
47    }
48}
49
50impl<'a> From<&'a Decimal> for PgNumeric {
51    fn from(decimal: &'a Decimal) -> Self {
52        let PostgresDecimal {
53            neg,
54            weight,
55            scale,
56            digits,
57        } = decimal.to_postgres();
58
59        if neg {
60            PgNumeric::Negative { digits, scale, weight }
61        } else {
62            PgNumeric::Positive { digits, scale, weight }
63        }
64    }
65}
66
67impl From<Decimal> for PgNumeric {
68    fn from(decimal: Decimal) -> Self {
69        (&decimal).into()
70    }
71}
72
73#[cfg(feature = "diesel")]
74impl ToSql<Numeric, Pg> for Decimal {
75    fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
76        let numeric = PgNumeric::from(self);
77        ToSql::<Numeric, Pg>::to_sql(&numeric, &mut out.reborrow())
78    }
79}
80
81#[cfg(feature = "diesel")]
82impl FromSql<Numeric, Pg> for Decimal {
83    fn from_sql(numeric: diesel::pg::PgValue) -> deserialize::Result<Self> {
84        PgNumeric::from_sql(numeric)?.try_into()
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use core::str::FromStr;
92
93    #[test]
94    fn test_unnecessary_zeroes() {
95        fn extract(value: &str) -> Decimal {
96            Decimal::from_str(value).unwrap()
97        }
98
99        let tests = &[
100            ("0.000001660"),
101            ("41.120255926293000"),
102            ("0.5538973300"),
103            ("08883.55986854293100"),
104            ("0.0000_0000_0016_6000_00"),
105            ("0.00000166650000"),
106            ("1666500000000"),
107            ("1666500000000.0000054500"),
108            ("8944.000000000000"),
109        ];
110
111        for &value in tests {
112            let value = extract(value);
113            let pg = PgNumeric::from(value);
114            let dec = Decimal::try_from(pg).unwrap();
115            assert_eq!(dec, value);
116        }
117    }
118
119    #[test]
120    fn decimal_to_pgnumeric_converts_digits_to_base_10000() {
121        let decimal = Decimal::from_str("1").unwrap();
122        let expected = PgNumeric::Positive {
123            weight: 0,
124            scale: 0,
125            digits: vec![1],
126        };
127        assert_eq!(expected, decimal.into());
128
129        let decimal = Decimal::from_str("10").unwrap();
130        let expected = PgNumeric::Positive {
131            weight: 0,
132            scale: 0,
133            digits: vec![10],
134        };
135        assert_eq!(expected, decimal.into());
136
137        let decimal = Decimal::from_str("10000").unwrap();
138        let expected = PgNumeric::Positive {
139            weight: 1,
140            scale: 0,
141            digits: vec![1, 0],
142        };
143        assert_eq!(expected, decimal.into());
144
145        let decimal = Decimal::from_str("10001").unwrap();
146        let expected = PgNumeric::Positive {
147            weight: 1,
148            scale: 0,
149            digits: vec![1, 1],
150        };
151        assert_eq!(expected, decimal.into());
152
153        let decimal = Decimal::from_str("100000000").unwrap();
154        let expected = PgNumeric::Positive {
155            weight: 2,
156            scale: 0,
157            digits: vec![1, 0, 0],
158        };
159        assert_eq!(expected, decimal.into());
160    }
161
162    #[test]
163    fn decimal_to_pg_numeric_properly_adjusts_scale() {
164        let decimal = Decimal::from_str("1").unwrap();
165        let expected = PgNumeric::Positive {
166            weight: 0,
167            scale: 0,
168            digits: vec![1],
169        };
170        assert_eq!(expected, decimal.into());
171
172        let decimal = Decimal::from_str("1.0").unwrap();
173        let expected = PgNumeric::Positive {
174            weight: 0,
175            scale: 1,
176            digits: vec![1],
177        };
178        assert_eq!(expected, decimal.into());
179
180        let decimal = Decimal::from_str("1.1").unwrap();
181        let expected = PgNumeric::Positive {
182            weight: 0,
183            scale: 1,
184            digits: vec![1, 1000],
185        };
186        assert_eq!(expected, decimal.into());
187
188        let decimal = Decimal::from_str("1.10").unwrap();
189        let expected = PgNumeric::Positive {
190            weight: 0,
191            scale: 2,
192            digits: vec![1, 1000],
193        };
194        assert_eq!(expected, decimal.into());
195
196        let decimal = Decimal::from_str("100000000.0001").unwrap();
197        let expected = PgNumeric::Positive {
198            weight: 2,
199            scale: 4,
200            digits: vec![1, 0, 0, 1],
201        };
202        assert_eq!(expected, decimal.into());
203
204        let decimal = Decimal::from_str("0.1").unwrap();
205        let expected = PgNumeric::Positive {
206            weight: -1,
207            scale: 1,
208            digits: vec![1000],
209        };
210        assert_eq!(expected, decimal.into());
211    }
212
213    #[test]
214    fn decimal_to_pg_numeric_retains_sign() {
215        let decimal = Decimal::from_str("123.456").unwrap();
216        let expected = PgNumeric::Positive {
217            weight: 0,
218            scale: 3,
219            digits: vec![123, 4560],
220        };
221        assert_eq!(expected, decimal.into());
222
223        let decimal = Decimal::from_str("-123.456").unwrap();
224        let expected = PgNumeric::Negative {
225            weight: 0,
226            scale: 3,
227            digits: vec![123, 4560],
228        };
229        assert_eq!(expected, decimal.into());
230    }
231
232    #[test]
233    fn pg_numeric_to_decimal_works() {
234        let expected = Decimal::from_str("50").unwrap();
235        let pg_numeric = PgNumeric::Positive {
236            weight: 0,
237            scale: 0,
238            digits: vec![50],
239        };
240        let res: Decimal = pg_numeric.try_into().unwrap();
241        assert_eq!(res, expected);
242        let expected = Decimal::from_str("123.456").unwrap();
243        let pg_numeric = PgNumeric::Positive {
244            weight: 0,
245            scale: 3,
246            digits: vec![123, 4560],
247        };
248        let res: Decimal = pg_numeric.try_into().unwrap();
249        assert_eq!(res, expected);
250
251        let expected = Decimal::from_str("-56.78").unwrap();
252        let pg_numeric = PgNumeric::Negative {
253            weight: 0,
254            scale: 2,
255            digits: vec![56, 7800],
256        };
257        let res: Decimal = pg_numeric.try_into().unwrap();
258        assert_eq!(res, expected);
259
260        // Verify no trailing zeroes are lost.
261
262        let expected = Decimal::from_str("1.100").unwrap();
263        let pg_numeric = PgNumeric::Positive {
264            weight: 0,
265            scale: 3,
266            digits: vec![1, 1000],
267        };
268        let res: Decimal = pg_numeric.try_into().unwrap();
269        assert_eq!(res.to_string(), expected.to_string());
270
271        // To represent 5.00, Postgres can return either [5, 0] as the list of digits.
272        let expected = Decimal::from_str("5.00").unwrap();
273        let pg_numeric = PgNumeric::Positive {
274            weight: 0,
275            scale: 2,
276
277            digits: vec![5, 0],
278        };
279        let res: Decimal = pg_numeric.try_into().unwrap();
280        assert_eq!(res.to_string(), expected.to_string());
281
282        // To represent 5.00, Postgres can return [5] as the list of digits.
283        let expected = Decimal::from_str("5.00").unwrap();
284        let pg_numeric = PgNumeric::Positive {
285            weight: 0,
286            scale: 2,
287            digits: vec![5],
288        };
289        let res: Decimal = pg_numeric.try_into().unwrap();
290        assert_eq!(res.to_string(), expected.to_string());
291
292        let expected = Decimal::from_str("3.1415926535897932384626433833").unwrap();
293        let pg_numeric = PgNumeric::Positive {
294            weight: 0,
295            scale: 30,
296            digits: vec![3, 1415, 9265, 3589, 7932, 3846, 2643, 3832, 7950, 2800],
297        };
298        let res: Decimal = pg_numeric.try_into().unwrap();
299        assert_eq!(res.to_string(), expected.to_string());
300
301        let expected = Decimal::from_str("3.1415926535897932384626433833").unwrap();
302        let pg_numeric = PgNumeric::Positive {
303            weight: 0,
304            scale: 34,
305            digits: vec![3, 1415, 9265, 3589, 7932, 3846, 2643, 3832, 7950, 2800],
306        };
307
308        let res: Decimal = pg_numeric.try_into().unwrap();
309        assert_eq!(res.to_string(), expected.to_string());
310
311        let expected = Decimal::from_str("1.2345678901234567890123456790").unwrap();
312        let pg_numeric = PgNumeric::Positive {
313            weight: 0,
314            scale: 34,
315            digits: vec![1, 2345, 6789, 0123, 4567, 8901, 2345, 6789, 5000, 0],
316        };
317
318        let res: Decimal = pg_numeric.try_into().unwrap();
319        assert_eq!(res.to_string(), expected.to_string());
320    }
321}