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 core::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"))]
89 impl<'a> From<&'a BigDecimal> for PgNumeric {
90 fn from(decimal: &'a BigDecimal) -> Self {
91 try_convert_decimal_to_pg_numeric(decimal)
92 .expect("Failed to convert BigDecimal to PgNumeric")
93 }
94 }
95
96 #[allow(clippy::assign_op_pattern, clippy::redundant_closure)]
102 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
103 fn try_convert_decimal_to_pg_numeric(
104 decimal: &BigDecimal,
105 ) -> Result<PgNumeric, Box<dyn core::error::Error + Send + Sync>> {
106 let (mut integer, scale) = decimal.as_bigint_and_exponent();
107
108 let scale = if scale < 0 {
110 for _ in 0..(-scale) {
111 integer = integer * 10;
112 }
113 0
114 } else {
115 scale
116 .try_into()
117 .map_err(|_| "Scale is expected to be 16bit large")?
118 };
119
120 integer = integer.abs();
121
122 for _ in 0..(4 - scale % 4) {
124 integer = integer * 10;
125 }
126 let integer = integer.to_biguint().ok_or("integer is always positive")?;
127
128 let mut digits = ToBase10000(Some(integer)).collect::<Vec<_>>();
129 digits.reverse();
130 let digits_after_decimal = scale / 4 + 1;
131 let weight = i16::try_from(digits.len())
132 .map_err(|_| "Max digit number is expected to fit into 16 bit")?
133 - i16::try_from(digits_after_decimal)
134 .map_err(|_| "Max digit number is expected to fit into 16 bit")?
135 - 1;
136
137 let unnecessary_zeroes = digits.iter().rev().take_while(|i| i.is_zero()).count();
138
139 let relevant_digits = digits.len() - unnecessary_zeroes;
140 digits.truncate(relevant_digits);
141
142 let result = match decimal.sign() {
143 Sign::Plus => PgNumeric::Positive {
144 digits,
145 scale,
146 weight,
147 },
148 Sign::Minus => PgNumeric::Negative {
149 digits,
150 scale,
151 weight,
152 },
153 Sign::NoSign => PgNumeric::Positive {
154 digits: ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[0]))vec![0],
155 scale: 0,
156 weight: 0,
157 },
158 };
159
160 Ok(result)
161 }
162
163 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
164 impl From<BigDecimal> for PgNumeric {
165 fn from(bigdecimal: BigDecimal) -> Self {
166 (&bigdecimal).into()
167 }
168 }
169
170 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
171 impl ToSql<Numeric, Pg> for BigDecimal {
172 fn to_sql<'b>(&'b self, out: &mut Output<'b, '_, Pg>) -> serialize::Result {
173 let numeric = try_convert_decimal_to_pg_numeric(self)?;
174 ToSql::<Numeric, Pg>::to_sql(&numeric, &mut out.reborrow())
175 }
176 }
177
178 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
179 impl FromSql<Numeric, Pg> for BigDecimal {
180 fn from_sql(numeric: PgValue<'_>) -> deserialize::Result<Self> {
181 PgNumeric::from_sql(numeric)?.try_into()
182 }
183 }
184
185 #[cfg(all(feature = "postgres_backend", feature = "numeric"))]
186 impl Defaultable for BigDecimal {
187 fn default_value() -> Self {
188 Self::default()
189 }
190 }
191
192 #[cfg(test)]
193 mod tests {
194 use crate::query_builder::ByteWrapper;
195
196 use super::*;
197 use std::str::FromStr;
198
199 #[diesel_test_helper::test]
200 fn bigdecimal_to_pgnumeric_converts_digits_to_base_10000() {
201 let decimal = BigDecimal::from_str("1").unwrap();
202 let expected = PgNumeric::Positive {
203 weight: 0,
204 scale: 0,
205 digits: vec![1],
206 };
207 assert_eq!(expected, decimal.into());
208
209 let decimal = BigDecimal::from_str("10").unwrap();
210 let expected = PgNumeric::Positive {
211 weight: 0,
212 scale: 0,
213 digits: vec![10],
214 };
215 assert_eq!(expected, decimal.into());
216
217 let decimal = BigDecimal::from_str("10000").unwrap();
218 let expected = PgNumeric::Positive {
219 weight: 1,
220 scale: 0,
221 digits: vec![1],
222 };
223 assert_eq!(expected, decimal.into());
224
225 let decimal = BigDecimal::from_str("10001").unwrap();
226 let expected = PgNumeric::Positive {
227 weight: 1,
228 scale: 0,
229 digits: vec![1, 1],
230 };
231 assert_eq!(expected, decimal.into());
232
233 let decimal = BigDecimal::from_str("100000000").unwrap();
234 let expected = PgNumeric::Positive {
235 weight: 2,
236 scale: 0,
237 digits: vec![1],
238 };
239 assert_eq!(expected, decimal.into());
240 }
241
242 #[diesel_test_helper::test]
243 fn bigdecimal_to_pg_numeric_properly_adjusts_scale() {
244 let decimal = BigDecimal::from_str("1").unwrap();
245 let expected = PgNumeric::Positive {
246 weight: 0,
247 scale: 0,
248 digits: vec![1],
249 };
250 assert_eq!(expected, decimal.into());
251
252 let decimal = BigDecimal::from_str("1.0").unwrap();
253 let expected = PgNumeric::Positive {
254 weight: 0,
255 scale: 1,
256 digits: vec![1],
257 };
258 assert_eq!(expected, decimal.into());
259
260 let decimal = BigDecimal::from_str("1.1").unwrap();
261 let expected = PgNumeric::Positive {
262 weight: 0,
263 scale: 1,
264 digits: vec![1, 1000],
265 };
266 assert_eq!(expected, decimal.into());
267
268 let decimal = BigDecimal::from_str("1.10").unwrap();
269 let expected = PgNumeric::Positive {
270 weight: 0,
271 scale: 2,
272 digits: vec![1, 1000],
273 };
274 assert_eq!(expected, decimal.into());
275
276 let decimal = BigDecimal::from_str("100000000.0001").unwrap();
277 let expected = PgNumeric::Positive {
278 weight: 2,
279 scale: 4,
280 digits: vec![1, 0, 0, 1],
281 };
282 assert_eq!(expected, decimal.into());
283
284 let decimal = BigDecimal::from_str("0.1").unwrap();
285 let expected = PgNumeric::Positive {
286 weight: -1,
287 scale: 1,
288 digits: vec![1000],
289 };
290 assert_eq!(expected, decimal.into());
291 }
292
293 #[diesel_test_helper::test]
294 fn bigdecimal_to_pg_numeric_retains_sign() {
295 let decimal = BigDecimal::from_str("123.456").unwrap();
296 let expected = PgNumeric::Positive {
297 weight: 0,
298 scale: 3,
299 digits: vec![123, 4560],
300 };
301 assert_eq!(expected, decimal.into());
302
303 let decimal = BigDecimal::from_str("-123.456").unwrap();
304 let expected = PgNumeric::Negative {
305 weight: 0,
306 scale: 3,
307 digits: vec![123, 4560],
308 };
309 assert_eq!(expected, decimal.into());
310 }
311
312 #[diesel_test_helper::test]
313 fn bigdecimal_with_negative_scale_to_pg_numeric_works() {
314 let decimal = BigDecimal::new(50.into(), -2);
315 let expected = PgNumeric::Positive {
316 weight: 0,
317 scale: 0,
318 digits: vec![5000],
319 };
320 assert_eq!(expected, decimal.into());
321
322 let decimal = BigDecimal::new(1.into(), -4);
323 let expected = PgNumeric::Positive {
324 weight: 1,
325 scale: 0,
326 digits: vec![1],
327 };
328 assert_eq!(expected, decimal.into());
329 }
330
331 #[diesel_test_helper::test]
332 fn bigdecimal_with_negative_weight_to_pg_numeric_works() {
333 let decimal = BigDecimal::from_str("0.1000000000000000").unwrap();
334 let expected = PgNumeric::Positive {
335 weight: -1,
336 scale: 16,
337 digits: vec![1000],
338 };
339 assert_eq!(expected, decimal.into());
340
341 let decimal = BigDecimal::from_str("0.00315937").unwrap();
342 let expected = PgNumeric::Positive {
343 weight: -1,
344 scale: 8,
345 digits: vec![31, 5937],
346 };
347 assert_eq!(expected, decimal.into());
348
349 let decimal = BigDecimal::from_str("0.003159370000000000").unwrap();
350 let expected = PgNumeric::Positive {
351 weight: -1,
352 scale: 18,
353 digits: vec![31, 5937],
354 };
355 assert_eq!(expected, decimal.into());
356 }
357
358 #[diesel_test_helper::test]
359 fn pg_numeric_to_bigdecimal_works() {
360 let expected = BigDecimal::from_str("123.456").unwrap();
361 let pg_numeric = PgNumeric::Positive {
362 weight: 0,
363 scale: 3,
364 digits: vec![123, 4560],
365 };
366 let res: BigDecimal = pg_numeric.try_into().unwrap();
367 assert_eq!(res, expected);
368
369 let expected = BigDecimal::from_str("-56.78").unwrap();
370 let pg_numeric = PgNumeric::Negative {
371 weight: 0,
372 scale: 2,
373 digits: vec![56, 7800],
374 };
375 let res: BigDecimal = pg_numeric.try_into().unwrap();
376 assert_eq!(res, expected);
377 }
378
379 #[diesel_test_helper::test]
380 fn bigdecimal_with_huge_scale_errors() {
381 let huge_scale = "0.".to_string() + &"0".repeat(70_000) + "1";
382 let decimal = BigDecimal::from_str(&huge_scale).unwrap();
383 let mut v = Vec::new();
384 let output = ByteWrapper(&mut v);
385
386 let mut output = Output::test(output);
387 let r = <BigDecimal as ToSql<Numeric, Pg>>::to_sql(&decimal, &mut output);
388 assert!(r.is_err());
389 }
390 }
391}