1#[cfg(feature = "numeric")]
2mod bigdecimal {
3 extern crate bigdecimal;
4 extern crate num_bigint;
5 extern crate num_integer;
6 extern crate num_traits;
7
8 use self::bigdecimal::BigDecimal;
9 use self::num_bigint::{BigInt, BigUint, Sign};
10 use self::num_integer::Integer;
11 use self::num_traits::{Signed, ToPrimitive, Zero};
12
13 use crate::deserialize::{self, Defaultable, FromSql};
14 use crate::pg::data_types::PgNumeric;
15 use crate::pg::{Pg, PgValue};
16 use crate::serialize::{self, Output, ToSql};
17 use crate::sql_types::Numeric;
18
19 use std::error::Error;
20
21 struct ToBase10000(Option<BigUint>);
24
25 impl Iterator for ToBase10000 {
26 type Item = i16;
27
28 fn next(&mut self) -> Option<Self::Item> {
29 self.0.take().map(|v| {
30 let (div, rem) = v.div_rem(&BigUint::from(10_000u16));
31 if !div.is_zero() {
32 self.0 = Some(div);
33 }
34 rem.to_i16().expect("10000 always fits in an i16")
35 })
36 }
37 }
38
39 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
40 impl<'a> TryFrom<&'a PgNumeric> for BigDecimal {
41 type Error = Box<dyn Error + Send + Sync>;
42
43 fn try_from(numeric: &'a PgNumeric) -> deserialize::Result<Self> {
44 let (sign, weight, scale, digits) = match *numeric {
45 PgNumeric::Positive {
46 weight,
47 scale,
48 ref digits,
49 } => (Sign::Plus, weight, scale, digits),
50 PgNumeric::Negative {
51 weight,
52 scale,
53 ref digits,
54 } => (Sign::Minus, weight, scale, digits),
55 PgNumeric::NaN => {
56 return Err(Box::from("NaN is not (yet) supported in BigDecimal"))
57 }
58 };
59
60 let mut result = BigUint::default();
61 let count = i64::try_from(digits.len())?;
62 for digit in digits {
63 result *= BigUint::from(10_000u64);
64 result += BigUint::from(u64::try_from(*digit)?);
65 }
66 let correction_exp = 4 * (i64::from(weight) - count + 1);
68 let result = BigDecimal::new(BigInt::from_biguint(sign, result), -correction_exp)
69 .with_scale(i64::from(scale));
70 Ok(result)
71 }
72 }
73
74 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
75 impl TryFrom<PgNumeric> for BigDecimal {
76 type Error = Box<dyn Error + Send + Sync>;
77
78 fn try_from(numeric: PgNumeric) -> deserialize::Result<Self> {
79 (&numeric).try_into()
80 }
81 }
82
83 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
86 impl<'a> From<&'a BigDecimal> for PgNumeric {
87 #[allow(clippy::assign_op_pattern, clippy::redundant_closure)]
93 fn from(decimal: &'a BigDecimal) -> Self {
94 let (mut integer, scale) = decimal.as_bigint_and_exponent();
95
96 let scale = if scale < 0 {
98 for _ in 0..(-scale) {
99 integer = integer * 10;
100 }
101 0
102 } else {
103 scale
104 .try_into()
105 .expect("Scale is expected to be 16bit large")
106 };
107
108 integer = integer.abs();
109
110 for _ in 0..(4 - scale % 4) {
112 integer = integer * 10;
113 }
114 let integer = integer.to_biguint().expect("integer is always positive");
115
116 let mut digits = ToBase10000(Some(integer)).collect::<Vec<_>>();
117 digits.reverse();
118 let digits_after_decimal = scale / 4 + 1;
119 let weight = i16::try_from(digits.len())
120 .expect("Max digit number is expected to fit into 16 bit")
121 - i16::try_from(digits_after_decimal)
122 .expect("Max digit number is expected to fit into 16 bit")
123 - 1;
124
125 let unnecessary_zeroes = digits.iter().rev().take_while(|i| i.is_zero()).count();
126
127 let relevant_digits = digits.len() - unnecessary_zeroes;
128 digits.truncate(relevant_digits);
129
130 match decimal.sign() {
131 Sign::Plus => PgNumeric::Positive {
132 digits,
133 scale,
134 weight,
135 },
136 Sign::Minus => PgNumeric::Negative {
137 digits,
138 scale,
139 weight,
140 },
141 Sign::NoSign => PgNumeric::Positive {
142 digits: vec![0],
143 scale: 0,
144 weight: 0,
145 },
146 }
147 }
148 }
149
150 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
151 impl From<BigDecimal> for PgNumeric {
152 fn from(bigdecimal: BigDecimal) -> Self {
153 (&bigdecimal).into()
154 }
155 }
156
157 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
158 impl ToSql<Numeric, Pg> for BigDecimal {
159 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
160 let numeric = PgNumeric::from(self);
161 ToSql::<Numeric, Pg>::to_sql(&numeric, &mut out.reborrow())
162 }
163 }
164
165 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
166 impl FromSql<Numeric, Pg> for BigDecimal {
167 fn from_sql(numeric: PgValue<'_>) -> deserialize::Result<Self> {
168 PgNumeric::from_sql(numeric)?.try_into()
169 }
170 }
171
172 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
173 impl Defaultable for BigDecimal {
174 fn default_value() -> Self {
175 Self::default()
176 }
177 }
178
179 #[cfg(test)]
180 mod tests {
181 use super::*;
182 use std::str::FromStr;
183
184 #[diesel_test_helper::test]
185 fn bigdecimal_to_pgnumeric_converts_digits_to_base_10000() {
186 let decimal = BigDecimal::from_str("1").unwrap();
187 let expected = PgNumeric::Positive {
188 weight: 0,
189 scale: 0,
190 digits: vec![1],
191 };
192 assert_eq!(expected, decimal.into());
193
194 let decimal = BigDecimal::from_str("10").unwrap();
195 let expected = PgNumeric::Positive {
196 weight: 0,
197 scale: 0,
198 digits: vec![10],
199 };
200 assert_eq!(expected, decimal.into());
201
202 let decimal = BigDecimal::from_str("10000").unwrap();
203 let expected = PgNumeric::Positive {
204 weight: 1,
205 scale: 0,
206 digits: vec![1],
207 };
208 assert_eq!(expected, decimal.into());
209
210 let decimal = BigDecimal::from_str("10001").unwrap();
211 let expected = PgNumeric::Positive {
212 weight: 1,
213 scale: 0,
214 digits: vec![1, 1],
215 };
216 assert_eq!(expected, decimal.into());
217
218 let decimal = BigDecimal::from_str("100000000").unwrap();
219 let expected = PgNumeric::Positive {
220 weight: 2,
221 scale: 0,
222 digits: vec![1],
223 };
224 assert_eq!(expected, decimal.into());
225 }
226
227 #[diesel_test_helper::test]
228 fn bigdecimal_to_pg_numeric_properly_adjusts_scale() {
229 let decimal = BigDecimal::from_str("1").unwrap();
230 let expected = PgNumeric::Positive {
231 weight: 0,
232 scale: 0,
233 digits: vec![1],
234 };
235 assert_eq!(expected, decimal.into());
236
237 let decimal = BigDecimal::from_str("1.0").unwrap();
238 let expected = PgNumeric::Positive {
239 weight: 0,
240 scale: 1,
241 digits: vec![1],
242 };
243 assert_eq!(expected, decimal.into());
244
245 let decimal = BigDecimal::from_str("1.1").unwrap();
246 let expected = PgNumeric::Positive {
247 weight: 0,
248 scale: 1,
249 digits: vec![1, 1000],
250 };
251 assert_eq!(expected, decimal.into());
252
253 let decimal = BigDecimal::from_str("1.10").unwrap();
254 let expected = PgNumeric::Positive {
255 weight: 0,
256 scale: 2,
257 digits: vec![1, 1000],
258 };
259 assert_eq!(expected, decimal.into());
260
261 let decimal = BigDecimal::from_str("100000000.0001").unwrap();
262 let expected = PgNumeric::Positive {
263 weight: 2,
264 scale: 4,
265 digits: vec![1, 0, 0, 1],
266 };
267 assert_eq!(expected, decimal.into());
268
269 let decimal = BigDecimal::from_str("0.1").unwrap();
270 let expected = PgNumeric::Positive {
271 weight: -1,
272 scale: 1,
273 digits: vec![1000],
274 };
275 assert_eq!(expected, decimal.into());
276 }
277
278 #[diesel_test_helper::test]
279 fn bigdecimal_to_pg_numeric_retains_sign() {
280 let decimal = BigDecimal::from_str("123.456").unwrap();
281 let expected = PgNumeric::Positive {
282 weight: 0,
283 scale: 3,
284 digits: vec![123, 4560],
285 };
286 assert_eq!(expected, decimal.into());
287
288 let decimal = BigDecimal::from_str("-123.456").unwrap();
289 let expected = PgNumeric::Negative {
290 weight: 0,
291 scale: 3,
292 digits: vec![123, 4560],
293 };
294 assert_eq!(expected, decimal.into());
295 }
296
297 #[diesel_test_helper::test]
298 fn bigdecimal_with_negative_scale_to_pg_numeric_works() {
299 let decimal = BigDecimal::new(50.into(), -2);
300 let expected = PgNumeric::Positive {
301 weight: 0,
302 scale: 0,
303 digits: vec![5000],
304 };
305 assert_eq!(expected, decimal.into());
306
307 let decimal = BigDecimal::new(1.into(), -4);
308 let expected = PgNumeric::Positive {
309 weight: 1,
310 scale: 0,
311 digits: vec![1],
312 };
313 assert_eq!(expected, decimal.into());
314 }
315
316 #[diesel_test_helper::test]
317 fn bigdecimal_with_negative_weight_to_pg_numeric_works() {
318 let decimal = BigDecimal::from_str("0.1000000000000000").unwrap();
319 let expected = PgNumeric::Positive {
320 weight: -1,
321 scale: 16,
322 digits: vec![1000],
323 };
324 assert_eq!(expected, decimal.into());
325
326 let decimal = BigDecimal::from_str("0.00315937").unwrap();
327 let expected = PgNumeric::Positive {
328 weight: -1,
329 scale: 8,
330 digits: vec![31, 5937],
331 };
332 assert_eq!(expected, decimal.into());
333
334 let decimal = BigDecimal::from_str("0.003159370000000000").unwrap();
335 let expected = PgNumeric::Positive {
336 weight: -1,
337 scale: 18,
338 digits: vec![31, 5937],
339 };
340 assert_eq!(expected, decimal.into());
341 }
342
343 #[diesel_test_helper::test]
344 fn pg_numeric_to_bigdecimal_works() {
345 let expected = BigDecimal::from_str("123.456").unwrap();
346 let pg_numeric = PgNumeric::Positive {
347 weight: 0,
348 scale: 3,
349 digits: vec![123, 4560],
350 };
351 let res: BigDecimal = pg_numeric.try_into().unwrap();
352 assert_eq!(res, expected);
353
354 let expected = BigDecimal::from_str("-56.78").unwrap();
355 let pg_numeric = PgNumeric::Negative {
356 weight: 0,
357 scale: 2,
358 digits: vec![56, 7800],
359 };
360 let res: BigDecimal = pg_numeric.try_into().unwrap();
361 assert_eq!(res, expected);
362 }
363 }
364}