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, 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(test)]
173 mod tests {
174 use super::*;
175 use std::str::FromStr;
176
177 #[test]
178 fn bigdecimal_to_pgnumeric_converts_digits_to_base_10000() {
179 let decimal = BigDecimal::from_str("1").unwrap();
180 let expected = PgNumeric::Positive {
181 weight: 0,
182 scale: 0,
183 digits: vec![1],
184 };
185 assert_eq!(expected, decimal.into());
186
187 let decimal = BigDecimal::from_str("10").unwrap();
188 let expected = PgNumeric::Positive {
189 weight: 0,
190 scale: 0,
191 digits: vec![10],
192 };
193 assert_eq!(expected, decimal.into());
194
195 let decimal = BigDecimal::from_str("10000").unwrap();
196 let expected = PgNumeric::Positive {
197 weight: 1,
198 scale: 0,
199 digits: vec![1],
200 };
201 assert_eq!(expected, decimal.into());
202
203 let decimal = BigDecimal::from_str("10001").unwrap();
204 let expected = PgNumeric::Positive {
205 weight: 1,
206 scale: 0,
207 digits: vec![1, 1],
208 };
209 assert_eq!(expected, decimal.into());
210
211 let decimal = BigDecimal::from_str("100000000").unwrap();
212 let expected = PgNumeric::Positive {
213 weight: 2,
214 scale: 0,
215 digits: vec![1],
216 };
217 assert_eq!(expected, decimal.into());
218 }
219
220 #[test]
221 fn bigdecimal_to_pg_numeric_properly_adjusts_scale() {
222 let decimal = BigDecimal::from_str("1").unwrap();
223 let expected = PgNumeric::Positive {
224 weight: 0,
225 scale: 0,
226 digits: vec![1],
227 };
228 assert_eq!(expected, decimal.into());
229
230 let decimal = BigDecimal::from_str("1.0").unwrap();
231 let expected = PgNumeric::Positive {
232 weight: 0,
233 scale: 1,
234 digits: vec![1],
235 };
236 assert_eq!(expected, decimal.into());
237
238 let decimal = BigDecimal::from_str("1.1").unwrap();
239 let expected = PgNumeric::Positive {
240 weight: 0,
241 scale: 1,
242 digits: vec![1, 1000],
243 };
244 assert_eq!(expected, decimal.into());
245
246 let decimal = BigDecimal::from_str("1.10").unwrap();
247 let expected = PgNumeric::Positive {
248 weight: 0,
249 scale: 2,
250 digits: vec![1, 1000],
251 };
252 assert_eq!(expected, decimal.into());
253
254 let decimal = BigDecimal::from_str("100000000.0001").unwrap();
255 let expected = PgNumeric::Positive {
256 weight: 2,
257 scale: 4,
258 digits: vec![1, 0, 0, 1],
259 };
260 assert_eq!(expected, decimal.into());
261
262 let decimal = BigDecimal::from_str("0.1").unwrap();
263 let expected = PgNumeric::Positive {
264 weight: -1,
265 scale: 1,
266 digits: vec![1000],
267 };
268 assert_eq!(expected, decimal.into());
269 }
270
271 #[test]
272 fn bigdecimal_to_pg_numeric_retains_sign() {
273 let decimal = BigDecimal::from_str("123.456").unwrap();
274 let expected = PgNumeric::Positive {
275 weight: 0,
276 scale: 3,
277 digits: vec![123, 4560],
278 };
279 assert_eq!(expected, decimal.into());
280
281 let decimal = BigDecimal::from_str("-123.456").unwrap();
282 let expected = PgNumeric::Negative {
283 weight: 0,
284 scale: 3,
285 digits: vec![123, 4560],
286 };
287 assert_eq!(expected, decimal.into());
288 }
289
290 #[test]
291 fn bigdecimal_with_negative_scale_to_pg_numeric_works() {
292 let decimal = BigDecimal::new(50.into(), -2);
293 let expected = PgNumeric::Positive {
294 weight: 0,
295 scale: 0,
296 digits: vec![5000],
297 };
298 assert_eq!(expected, decimal.into());
299
300 let decimal = BigDecimal::new(1.into(), -4);
301 let expected = PgNumeric::Positive {
302 weight: 1,
303 scale: 0,
304 digits: vec![1],
305 };
306 assert_eq!(expected, decimal.into());
307 }
308
309 #[test]
310 fn bigdecimal_with_negative_weight_to_pg_numeric_works() {
311 let decimal = BigDecimal::from_str("0.1000000000000000").unwrap();
312 let expected = PgNumeric::Positive {
313 weight: -1,
314 scale: 16,
315 digits: vec![1000],
316 };
317 assert_eq!(expected, decimal.into());
318
319 let decimal = BigDecimal::from_str("0.00315937").unwrap();
320 let expected = PgNumeric::Positive {
321 weight: -1,
322 scale: 8,
323 digits: vec![31, 5937],
324 };
325 assert_eq!(expected, decimal.into());
326
327 let decimal = BigDecimal::from_str("0.003159370000000000").unwrap();
328 let expected = PgNumeric::Positive {
329 weight: -1,
330 scale: 18,
331 digits: vec![31, 5937],
332 };
333 assert_eq!(expected, decimal.into());
334 }
335
336 #[test]
337 fn pg_numeric_to_bigdecimal_works() {
338 let expected = BigDecimal::from_str("123.456").unwrap();
339 let pg_numeric = PgNumeric::Positive {
340 weight: 0,
341 scale: 3,
342 digits: vec![123, 4560],
343 };
344 let res: BigDecimal = pg_numeric.try_into().unwrap();
345 assert_eq!(res, expected);
346
347 let expected = BigDecimal::from_str("-56.78").unwrap();
348 let pg_numeric = PgNumeric::Negative {
349 weight: 0,
350 scale: 2,
351 digits: vec![56, 7800],
352 };
353 let res: BigDecimal = pg_numeric.try_into().unwrap();
354 assert_eq!(res, expected);
355 }
356 }
357}