1use super::codec::Encode;
4use crate::{sql_read_bytes::SqlReadBytes, Error};
5#[cfg(feature = "bigdecimal")]
6#[cfg_attr(docsrs, doc(cfg(feature = "bigdecimal")))]
7pub use bigdecimal::{num_bigint::BigInt, BigDecimal};
8use bytes::{BufMut, BytesMut};
9#[cfg(feature = "rust_decimal")]
10#[cfg_attr(docsrs, doc(cfg(feature = "rust_decimal")))]
11pub use rust_decimal::Decimal;
12use std::cmp::{Ordering, PartialEq};
13use std::fmt::{self, Debug, Display, Formatter};
14
15#[derive(Copy, Clone)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[cfg_attr(feature = "serde", serde(try_from = "NumericParts"))]
23pub struct Numeric {
24 value: i128,
25 scale: u8,
26}
27
28#[cfg(feature = "serde")]
31#[derive(serde::Deserialize)]
32#[serde(rename = "Numeric")]
33struct NumericParts {
34 value: i128,
35 scale: u8,
36}
37
38#[cfg(feature = "serde")]
39impl TryFrom<NumericParts> for Numeric {
40 type Error = String;
41
42 fn try_from(n: NumericParts) -> Result<Self, String> {
43 match n.scale {
44 0..=38 => Ok(Numeric {
45 value: n.value,
46 scale: n.scale,
47 }),
48 s => Err(format!("Numeric scale {s} is over 38")),
49 }
50 }
51}
52
53impl Numeric {
54 pub fn new_with_scale(value: i128, scale: u8) -> Self {
59 assert!(scale <= 38);
63
64 Numeric { value, scale }
65 }
66
67 pub fn dec_part(self) -> i128 {
69 let scale = self.pow_scale();
70 self.value - (self.value / scale) * scale
71 }
72
73 pub fn int_part(self) -> i128 {
75 self.value / self.pow_scale()
76 }
77
78 #[inline]
79 fn pow_scale(self) -> i128 {
80 10i128.pow(self.scale as u32)
81 }
82
83 #[inline]
85 pub fn scale(self) -> u8 {
86 self.scale
87 }
88
89 #[inline]
91 pub fn value(self) -> i128 {
92 self.value
93 }
94
95 pub fn precision(self) -> u8 {
98 let digits = self
99 .value
100 .unsigned_abs()
101 .checked_ilog10()
102 .map_or(0, |l| l + 1);
103
104 (digits as u8).max(self.scale).max(1)
106 }
107
108 pub(crate) fn len(self) -> u8 {
109 match self.precision() {
110 1..=9 => 5,
111 10..=19 => 9,
112 20..=28 => 13,
113 _ => 17,
114 }
115 }
116
117 pub(crate) async fn decode<R>(src: &mut R, scale: u8) -> crate::Result<Option<Self>>
118 where
119 R: SqlReadBytes + Unpin,
120 {
121 let len = src.read_u8().await? as usize;
122 if len == 0 {
123 return Ok(None);
124 }
125
126 if !matches!(len, 5 | 9 | 13 | 17) {
127 return Err(invalid_length(len));
128 }
129
130 let mut buf = [0u8; 17];
131 futures_util::io::AsyncReadExt::read_exact(src, &mut buf[..len]).await?;
132
133 Self::from_wire(&buf[..len], scale).map(Some)
134 }
135
136 pub(crate) fn from_wire(bytes: &[u8], scale: u8) -> crate::Result<Self> {
139 if scale > 38 {
140 return Err(Error::Protocol(
141 format!("decimal/numeric: invalid scale {scale}").into(),
142 ));
143 }
144
145 let (&sign, magnitude) = bytes.split_first().ok_or_else(|| invalid_length(0))?;
146 if !matches!(magnitude.len(), 4 | 8 | 12 | 16) {
147 return Err(invalid_length(bytes.len()));
148 }
149
150 let mut le = [0u8; 16];
151 le[..magnitude.len()].copy_from_slice(magnitude);
152
153 let value = i128::try_from(u128::from_le_bytes(le)).map_err(|_| {
155 Error::Protocol("decimal/numeric: magnitude exceeds the representable range".into())
156 })?;
157
158 let value = match sign {
159 0 => -value,
160 1 => value,
161 _ => return Err(Error::Protocol("decimal/numeric: invalid sign".into())),
162 };
163
164 Ok(Numeric { value, scale })
165 }
166
167 pub(crate) fn to_f64(self) -> f64 {
170 const POW10: [f64; 23] = [
171 1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14, 1e15,
172 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22,
173 ];
174
175 match POW10.get(self.scale as usize) {
176 Some(pow) if self.value.unsigned_abs() < 1 << f64::MANTISSA_DIGITS => {
177 self.value as f64 / pow
178 }
179 _ => self
180 .to_string()
181 .parse()
182 .expect("Display is a float literal"),
183 }
184 }
185
186 pub(crate) fn to_f32(self) -> f32 {
188 const POW10: [f32; 11] = [1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10];
189
190 match POW10.get(self.scale as usize) {
191 Some(pow) if self.value.unsigned_abs() < 1 << f32::MANTISSA_DIGITS => {
192 self.value as f32 / pow
193 }
194 _ => self
195 .to_string()
196 .parse()
197 .expect("Display is a float literal"),
198 }
199 }
200}
201
202fn invalid_length(len: usize) -> Error {
203 Error::Protocol(format!("decimal/numeric: invalid length of {len} received").into())
204}
205
206impl Encode<BytesMut> for Numeric {
207 fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
208 if self.value.unsigned_abs() >= 10u128.pow(38) {
210 return Err(Error::Conversion(
211 "numeric value out of range: SQL Server's decimal holds at most 38 digits".into(),
212 ));
213 }
214
215 let len = self.len();
217 dst.put_u8(len);
218
219 if self.value < 0 {
220 dst.put_u8(0);
221 } else {
222 dst.put_u8(1);
223 }
224
225 let value = self.value().abs();
226
227 match len {
228 5 => dst.put_u32_le(value as u32),
229 9 => dst.put_u64_le(value as u64),
230 13 => {
231 dst.put_u64_le(value as u64);
232 dst.put_u32_le((value >> 64) as u32)
233 }
234 _ => dst.put_u128_le(value as u128),
235 }
236
237 Ok(())
238 }
239}
240
241impl Debug for Numeric {
242 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
243 write!(f, "{}", self)
244 }
245}
246
247impl Display for Numeric {
248 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
249 let sign = if self.value < 0 { "-" } else { "" };
250 let abs = self.value.unsigned_abs();
251
252 if self.scale == 0 {
253 return write!(f, "{sign}{abs}");
254 }
255
256 let pow = 10u128.pow(self.scale as u32);
257 let scale = self.scale as usize;
258 write!(f, "{sign}{}.{:0scale$}", abs / pow, abs % pow)
259 }
260}
261
262impl Eq for Numeric {}
263
264impl From<Numeric> for f64 {
265 fn from(n: Numeric) -> f64 {
266 n.to_f64()
267 }
268}
269
270impl From<Numeric> for i128 {
271 fn from(n: Numeric) -> i128 {
272 n.int_part()
273 }
274}
275
276impl From<Numeric> for u128 {
277 fn from(n: Numeric) -> u128 {
278 n.int_part() as u128
279 }
280}
281
282impl PartialEq for Numeric {
283 fn eq(&self, other: &Self) -> bool {
284 let (wide, narrow) = match self.scale.cmp(&other.scale) {
285 Ordering::Less => (other, self),
286 _ => (self, other),
287 };
288
289 10i128
291 .checked_pow((wide.scale - narrow.scale) as u32)
292 .and_then(|pow| pow.checked_mul(narrow.value))
293 .is_some_and(|v| v == wide.value)
294 }
295}
296
297#[cfg(feature = "rust_decimal")]
298mod decimal {
299 use super::{Decimal, Numeric};
300 #[cfg(feature = "tds73")]
301 use crate::{ColumnData, FromSql, FromSqlOwned};
302
303 #[cfg(feature = "tds73")]
307 fn to_decimal(num: Numeric) -> crate::Result<Decimal> {
308 let (mut value, mut scale) = (num.value(), num.scale() as u32);
309
310 loop {
311 if let Ok(dec) = Decimal::try_from_i128_with_scale(value, scale) {
312 return Ok(dec);
313 }
314 if scale == 0 || value % 10 != 0 {
315 return Err(crate::Error::Conversion(
316 format!("{num} does not fit in a rust_decimal::Decimal").into(),
317 ));
318 }
319 value /= 10;
320 scale -= 1;
321 }
322 }
323
324 #[cfg(feature = "tds73")]
325 impl<'a> FromSql<'a> for Decimal {
326 fn from_sql(data: &'a ColumnData<'static>) -> crate::Result<Option<Self>> {
327 match data {
328 ColumnData::Numeric(num) => num.map(to_decimal).transpose(),
329 _ => Err(crate::Error::Conversion(
330 format!("cannot interpret {:?} as an Decimal value", data).into(),
331 )),
332 }
333 }
334 }
335
336 #[cfg(feature = "tds73")]
337 impl FromSqlOwned for Decimal {
338 fn from_sql_owned(data: ColumnData<'static>) -> crate::Result<Option<Self>> {
339 Self::from_sql(&data)
340 }
341 }
342
343 #[cfg(feature = "tds73")]
344 to_sql!(self_,
345 Decimal: (ColumnData::Numeric, {
346 let unpacked = self_.unpack();
347
348 let mut value = (((unpacked.hi as u128) << 64)
349 + ((unpacked.mid as u128) << 32)
350 + unpacked.lo as u128) as i128;
351
352 if self_.is_sign_negative() {
353 value = -value;
354 }
355
356 Numeric::new_with_scale(value, self_.scale() as u8)
357 });
358 );
359
360 #[cfg(feature = "tds73")]
361 into_sql!(self_,
362 Decimal: (ColumnData::Numeric, {
363 let unpacked = self_.unpack();
364
365 let mut value = (((unpacked.hi as u128) << 64)
366 + ((unpacked.mid as u128) << 32)
367 + unpacked.lo as u128) as i128;
368
369 if self_.is_sign_negative() {
370 value = -value;
371 }
372
373 Numeric::new_with_scale(value, self_.scale() as u8)
374 });
375 );
376}
377
378#[cfg(feature = "bigdecimal")]
379mod bigdecimal_ {
380 use super::{BigDecimal, BigInt, Numeric};
381 use crate::ColumnData;
382 use num_traits::{Signed, ToPrimitive, Zero};
383
384 #[cfg(feature = "tds73")]
385 from_sql!(BigDecimal: ColumnData::Numeric(ref num) => num.map(|num| {
386 let int = BigInt::from(num.value());
387
388 BigDecimal::new(int, num.scale() as i64)
389 }));
390
391 fn to_numeric(value: &BigDecimal) -> Numeric {
395 let (int, exp) = value.as_bigint_and_exponent();
397
398 if int.is_zero() {
399 return Numeric::new_with_scale(0, exp.clamp(0, 38) as u8);
400 }
401
402 let max = if int.is_negative() {
403 -i128::MAX
404 } else {
405 i128::MAX
406 };
407 let out_of_range = || Numeric::new_with_scale(max, 0);
408
409 let (int, scale) = if exp < 0 {
410 if value.digits().saturating_add(exp.unsigned_abs()) > 38 {
413 return out_of_range();
414 }
415 (int * BigInt::from(10u8).pow(exp.unsigned_abs() as u32), 0)
416 } else if exp > 38 {
417 let excess = exp - 38;
419 if excess.unsigned_abs() >= value.digits() {
420 return out_of_range();
421 }
422 let divisor = BigInt::from(10u8).pow(excess as u32);
423 if !(&int % &divisor).is_zero() {
424 return out_of_range();
425 }
426 (int / divisor, 38)
427 } else {
428 (int, exp as u8)
429 };
430
431 match int.to_i128() {
432 Some(v) if v.unsigned_abs() < 10u128.pow(38) => Numeric::new_with_scale(v, scale),
433 _ => out_of_range(),
434 }
435 }
436
437 #[cfg(feature = "tds73")]
438 to_sql!(self_,
439 BigDecimal: (ColumnData::Numeric, to_numeric(self_));
440 );
441
442 #[cfg(feature = "tds73")]
443 into_sql!(self_,
444 BigDecimal: (ColumnData::Numeric, to_numeric(&self_));
445 );
446}
447
448#[cfg(test)]
449mod tests {
450 use super::*;
451
452 #[test]
453 fn numeric_eq() {
454 assert_eq!(
455 Numeric {
456 value: 100501,
457 scale: 2
458 },
459 Numeric {
460 value: 1005010,
461 scale: 3
462 }
463 );
464 assert!(
465 Numeric {
466 value: 100501,
467 scale: 2
468 } != Numeric {
469 value: 10050,
470 scale: 1
471 }
472 );
473 }
474
475 #[test]
476 fn numeric_eq_normalizes_across_a_scale_gap() {
477 let wide = Numeric {
483 value: 123_000,
484 scale: 5,
485 };
486 let narrow = Numeric {
487 value: 123,
488 scale: 2,
489 };
490 assert_eq!(wide, narrow); assert_eq!(narrow, wide); assert!(
493 narrow
494 != Numeric {
495 value: 124,
496 scale: 2
497 }
498 );
499 }
500
501 #[test]
502 fn encode_byte_layout_matches_length_bucket() {
503 for value in [1i128, 10i128.pow(12), 10i128.pow(20), 10i128.pow(30)] {
507 let n = Numeric::new_with_scale(value, 0);
508 let expected = n.len() as usize + 1;
509 let mut buf = BytesMut::new();
510 n.encode(&mut buf).unwrap();
511 assert_eq!(buf.len(), expected, "byte count for {value}");
512 }
513
514 let mut zero = BytesMut::new();
516 Numeric::new_with_scale(0, 0).encode(&mut zero).unwrap();
517 assert_eq!(zero[1], 1, "zero must carry the positive sign byte");
518 }
519
520 #[tokio::test]
521 async fn decode_d128_keeps_high_and_low_words() {
522 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
523
524 let value = 0x0101_0101_0101_0101_0101_0101_0101_0101i128;
528 let n = Numeric::new_with_scale(value, 0);
529 let mut buf = BytesMut::new();
530 n.encode(&mut buf).unwrap();
531 let decoded = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
532 .await
533 .unwrap()
534 .unwrap();
535 assert_eq!(decoded.value(), value);
536 }
537
538 #[tokio::test]
539 async fn decode_accepts_magnitude_at_i128_max_but_rejects_beyond() {
540 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
541
542 let mut at_max = BytesMut::new();
544 at_max.put_u8(17);
545 at_max.put_u8(1);
546 at_max.put_i128_le(i128::MAX); let decoded = Numeric::decode(&mut at_max.into_sql_read_bytes(), 0)
548 .await
549 .expect("i128::MAX magnitude is representable")
550 .unwrap();
551 assert_eq!(decoded.value(), i128::MAX);
552
553 let mut beyond = BytesMut::new();
555 beyond.put_u8(17);
556 beyond.put_u8(1);
557 beyond.put_u128_le((i128::MAX as u128) + 1);
558 assert!(Numeric::decode(&mut beyond.into_sql_read_bytes(), 0)
559 .await
560 .is_err());
561 }
562
563 #[test]
564 fn numeric_to_f64() {
565 assert_eq!(f64::from(Numeric::new_with_scale(57705, 2)), 577.05);
566 }
567
568 #[test]
569 fn numeric_to_int_dec_part() {
570 let n = Numeric::new_with_scale(57705, 2);
571 assert_eq!(n.int_part(), 577);
572 assert_eq!(n.dec_part(), 5);
573 }
574
575 #[test]
576 fn numeric_to_string() {
577 assert_eq!(Numeric::new_with_scale(123, 0).to_string(), "123");
578 assert_eq!(Numeric::new_with_scale(123, 1).to_string(), "12.3");
579 assert_eq!(Numeric::new_with_scale(123, 2).to_string(), "1.23");
580 assert_eq!(Numeric::new_with_scale(123, 3).to_string(), "0.123");
581 assert_eq!(Numeric::new_with_scale(123, 4).to_string(), "0.0123");
582 assert_eq!(
583 Numeric::new_with_scale(123, 36).to_string(),
584 "0.000000000000000000000000000000000123"
585 );
586 assert_eq!(
587 Numeric::new_with_scale(123, 37).to_string(),
588 "0.0000000000000000000000000000000000123"
589 );
590 assert_eq!(Numeric::new_with_scale(-123, 0).to_string(), "-123");
591 assert_eq!(
592 Numeric::new_with_scale(i128::MIN, 0).to_string(),
593 "-170141183460469231731687303715884105728"
594 );
595 assert_eq!(Numeric::new_with_scale(-123, 1).to_string(), "-12.3");
596 assert_eq!(Numeric::new_with_scale(-123, 2).to_string(), "-1.23");
597 assert_eq!(Numeric::new_with_scale(-123, 3).to_string(), "-0.123");
598 assert_eq!(Numeric::new_with_scale(-123, 4).to_string(), "-0.0123");
599 assert_eq!(
600 Numeric::new_with_scale(-123, 36).to_string(),
601 "-0.000000000000000000000000000000000123"
602 );
603 assert_eq!(
604 Numeric::new_with_scale(-123, 37).to_string(),
605 "-0.0000000000000000000000000000000000123"
606 );
607 }
608
609 #[test]
610 fn calculates_precision_correctly() {
611 let n = Numeric::new_with_scale(57705, 2);
612 assert_eq!(5, n.precision());
613 }
614
615 #[test]
616 fn new_with_scale_accessors() {
617 let n = Numeric::new_with_scale(12345, 3);
618 assert_eq!(n.value(), 12345);
619 assert_eq!(n.scale(), 3);
620 assert_eq!(n.int_part(), 12);
621 assert_eq!(n.dec_part(), 345);
622 }
623
624 #[test]
625 fn new_with_scale_allows_max_scale() {
626 assert_eq!(Numeric::new_with_scale(1, 38).scale(), 38);
628 }
629
630 #[test]
631 #[should_panic]
632 fn new_with_scale_panics_on_too_large_scale() {
633 Numeric::new_with_scale(1, 39);
634 }
635
636 #[test]
637 fn precision_with_zero_int_part() {
638 let n = Numeric::new_with_scale(5, 2);
640 assert_eq!(n.int_part(), 0);
641 assert_eq!(n.precision(), 2);
642
643 assert_eq!(Numeric::new_with_scale(0, 0).precision(), 1);
644 assert_eq!(Numeric::new_with_scale(0, 3).precision(), 3);
645 assert_eq!(Numeric::new_with_scale(-5, 1).precision(), 1);
646 }
647
648 #[test]
649 fn precision_of_decimal_38_38_is_38() {
650 let half = Numeric::new_with_scale(5 * 10i128.pow(37), 38);
651 assert_eq!(half.precision(), 38);
652 let max = Numeric::new_with_scale(-(10i128.pow(38) - 1), 38);
653 assert_eq!(max.precision(), 38);
654 assert_eq!(max.len(), 17);
655 assert_eq!(
656 Numeric::new_with_scale(10i128.pow(38) - 1, 0).precision(),
657 38
658 );
659 }
660
661 #[test]
662 fn eq_does_not_overflow_on_a_wide_scale_gap() {
663 let big = Numeric::new_with_scale(10i128.pow(37), 0);
664 let tiny = Numeric::new_with_scale(1, 38);
665 assert_ne!(big, tiny);
666 assert_ne!(tiny, big);
667 assert_ne!(
668 Numeric::new_with_scale(i128::MAX, 38),
669 Numeric::new_with_scale(i128::MAX, 0)
670 );
671 assert_eq!(
672 Numeric::new_with_scale(0, 0),
673 Numeric::new_with_scale(0, 38)
674 );
675 assert_eq!(
676 Numeric::new_with_scale(1, 0),
677 Numeric::new_with_scale(10i128.pow(38), 38)
678 );
679 }
680
681 #[test]
682 fn to_float_is_correctly_rounded() {
683 let money_max = Numeric::new_with_scale(i64::MAX as i128, 4);
684 let nearest: f64 = "922337203685477.5807".parse().unwrap();
685 assert_eq!(money_max.to_f64(), nearest);
686 assert_eq!(f64::from(money_max), nearest);
687 assert_eq!(Numeric::new_with_scale(12300, 4).to_f64(), 1.23);
688 assert_eq!(Numeric::new_with_scale(-233, 2).to_f64(), -2.33);
689 let tiny = Numeric::new_with_scale(123, 38);
690 assert_eq!(tiny.to_f64(), 1.23e-36);
691
692 assert_eq!(Numeric::new_with_scale(23300, 4).to_f32(), 2.33f32);
693 assert_eq!(
694 money_max.to_f32(),
695 "922337203685477.5807".parse::<f32>().unwrap()
696 );
697 }
698
699 #[tokio::test]
700 async fn decode_rejects_a_scale_over_38() {
701 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
702
703 let mut buf = BytesMut::new();
704 buf.put_u8(5);
705 buf.put_u8(1);
706 buf.put_u32_le(1);
707 let err = Numeric::decode(&mut buf.into_sql_read_bytes(), 39)
708 .await
709 .expect_err("scale 39 must error, not panic");
710 assert!(matches!(err, Error::Protocol(_)));
711 }
712
713 #[cfg(all(feature = "rust_decimal", feature = "tds73"))]
714 #[test]
715 fn rust_decimal_from_sql_errors_instead_of_panicking() {
716 use crate::{ColumnData, FromSql, FromSqlOwned};
717
718 let get = |value: i128, scale: u8| {
719 let data = ColumnData::Numeric(Some(Numeric::new_with_scale(value, scale)));
720 let borrowed = Decimal::from_sql(&data).map(|d| d.unwrap().to_string());
721 let owned = Decimal::from_sql_owned(data).map(|d| d.unwrap().to_string());
722 assert_eq!(borrowed.is_ok(), owned.is_ok());
723 borrowed
724 };
725
726 assert!(get(12345678901234567890123456789012345678, 0).is_err());
728 assert!(get(12345678901234567890123456789012345678, 38).is_err());
729 assert!(matches!(
730 get(10i128.pow(29), 0),
731 Err(crate::Error::Conversion(_))
732 ));
733
734 assert_eq!(
736 get(5 * 10i128.pow(29), 30).unwrap(),
737 "0.5000000000000000000000000000"
738 );
739 assert_eq!(get(0, 38).unwrap(), "0.0000000000000000000000000000");
740 assert_eq!(
741 get(-(10i128.pow(31)), 3).unwrap(),
742 "-10000000000000000000000000000"
743 );
744 assert_eq!(get(-12345, 2).unwrap(), "-123.45");
745 assert_eq!(Decimal::from_sql(&ColumnData::Numeric(None)).unwrap(), None);
746 }
747
748 #[test]
749 fn precision_scaling_by_length_buckets() {
750 assert_eq!(Numeric::new_with_scale(1, 0).len(), 5);
751 assert_eq!(Numeric::new_with_scale(1_000_000_000, 0).len(), 9);
752 assert_eq!(Numeric::new_with_scale(10i128.pow(19), 0).len(), 13);
753 assert_eq!(Numeric::new_with_scale(10i128.pow(28), 0).len(), 17);
754 }
755
756 #[test]
757 fn display_and_debug() {
758 let n = Numeric::new_with_scale(57705, 2);
759 assert_eq!(format!("{:?}", n), "577.05");
760 assert_eq!(format!("{}", n), "577.05");
761
762 let n = Numeric::new_with_scale(-57705, 3);
765 assert_eq!(format!("{}", n), "-57.705");
766
767 let n = Numeric::new_with_scale(102, 4);
769 assert_eq!(format!("{}", n), "0.0102");
770 }
771
772 #[test]
773 fn from_numeric_conversions() {
774 let n = Numeric::new_with_scale(57705, 2);
775 assert_eq!(i128::from(n), 577);
776 assert_eq!(u128::from(n), 577);
777 assert!((f64::from(n) - 577.05).abs() < f64::EPSILON);
778 }
779
780 #[test]
781 fn eq_across_scales_negative() {
782 assert_eq!(
783 Numeric::new_with_scale(-100501, 2),
784 Numeric::new_with_scale(-1005010, 3),
785 );
786 }
787
788 async fn round_trip(value: i128, scale: u8) {
789 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
790
791 let n = Numeric::new_with_scale(value, scale);
792 let mut buf = BytesMut::new();
793 n.encode(&mut buf).expect("encode must succeed");
794
795 let decoded = Numeric::decode(&mut buf.into_sql_read_bytes(), scale)
796 .await
797 .expect("decode must succeed")
798 .expect("value must be present");
799
800 assert_eq!(decoded, n);
801 assert_eq!(decoded.value(), value);
802 }
803
804 #[tokio::test]
805 async fn encode_decode_round_trip() {
806 round_trip(0, 0).await; round_trip(42, 0).await; round_trip(-42, 2).await; round_trip(10i128.pow(12), 0).await; round_trip(10i128.pow(20), 0).await; round_trip(-(10i128.pow(20)), 3).await; round_trip(10i128.pow(30), 0).await; round_trip(10i128.pow(38) - 1, 38).await; round_trip(-(10i128.pow(38) - 1), 0).await;
815 }
816
817 #[test]
818 fn encode_rejects_more_than_38_digits() {
819 for value in [10i128.pow(38), -(10i128.pow(38)), i128::MAX, -i128::MAX] {
820 let err = Numeric::new_with_scale(value, 0)
821 .encode(&mut BytesMut::new())
822 .unwrap_err();
823 assert!(matches!(err, Error::Conversion(_)), "{value}: {err:?}");
824 }
825 }
826
827 #[tokio::test]
828 async fn decode_zero_length_is_none() {
829 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
830
831 let mut buf = BytesMut::new();
832 buf.put_u8(0);
833
834 let decoded = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
835 .await
836 .expect("decode must succeed");
837
838 assert!(decoded.is_none());
839 }
840
841 #[tokio::test]
842 async fn decode_rejects_len17_magnitude_over_i128_max() {
843 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
844
845 let mut buf = BytesMut::new();
849 buf.put_u8(17);
850 buf.put_u8(1);
851 let mut mag = [0u8; 16];
852 mag[15] = 0x80;
853 buf.extend_from_slice(&mag);
854
855 let err = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
856 .await
857 .expect_err("out-of-range magnitude must error");
858 assert!(matches!(err, Error::Protocol(_)));
859 }
860
861 #[tokio::test]
862 async fn decode_rejects_invalid_sign_and_length() {
863 use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
864
865 let mut buf = BytesMut::new();
867 buf.put_u8(5);
868 buf.put_u8(2);
869 buf.put_u32_le(1);
870 let err = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
871 .await
872 .expect_err("invalid sign must error");
873 assert!(matches!(err, Error::Protocol(_)));
874
875 let mut buf = BytesMut::new();
877 buf.put_u8(6);
878 buf.put_u8(1);
879 buf.extend_from_slice(&[0u8; 4]);
880 let err = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
881 .await
882 .expect_err("invalid length must error");
883 assert!(matches!(err, Error::Protocol(_)));
884 }
885
886 #[cfg(all(feature = "bigdecimal", feature = "tds73"))]
887 fn bigdecimal_to_numeric(value: &BigDecimal) -> Numeric {
888 use crate::{ColumnData, IntoSql, ToSql};
889
890 let by_ref = match value.to_sql() {
891 ColumnData::Numeric(Some(n)) => n,
892 other => panic!("{other:?}"),
893 };
894 let by_value = match value.clone().into_sql() {
895 ColumnData::Numeric(Some(n)) => n,
896 other => panic!("{other:?}"),
897 };
898 assert_eq!(
899 (by_ref.value(), by_ref.scale()),
900 (by_value.value(), by_value.scale())
901 );
902 by_ref
903 }
904
905 #[test]
906 #[cfg(all(feature = "bigdecimal", feature = "tds73"))]
907 fn bigdecimal_out_of_range_is_an_encode_error() {
908 use std::str::FromStr;
909
910 let mut values = vec![BigDecimal::from(1) / BigDecimal::from(3)];
911 for s in [
912 "1e40",
913 "-1e40",
914 "1e38",
915 "1e-39",
916 "-1e-39",
917 "0.1234567890123456789012345678901234567890",
918 "1e-1000000000",
919 "1e1000000000",
920 "1e-9223372036854775807",
921 ] {
922 values.push(BigDecimal::from_str(s).unwrap());
923 }
924
925 for value in values {
926 let err = bigdecimal_to_numeric(&value)
927 .encode(&mut BytesMut::new())
928 .unwrap_err();
929 assert!(matches!(err, Error::Conversion(_)), "{value}: {err:?}");
930 }
931 }
932
933 #[test]
934 #[cfg(all(feature = "bigdecimal", feature = "tds73"))]
935 fn bigdecimal_exact_values_keep_value_and_scale() {
936 use std::str::FromStr;
937
938 let nines = |n: usize| "9".repeat(n);
939 let cases = [
940 ("1.5e3".to_string(), 1500, 0),
941 ("1e-3".to_string(), 1, 3),
942 ("-12.345".to_string(), -12345, 3),
943 (nines(38), 10i128.pow(38) - 1, 0),
944 (format!("0.{}", nines(38)), 10i128.pow(38) - 1, 38),
945 (format!("-{}e-38", nines(38)), -(10i128.pow(38) - 1), 38),
946 (format!("0.15{}", "0".repeat(39)), 15 * 10i128.pow(36), 38),
948 ("0e-50".to_string(), 0, 38),
949 ("0e50".to_string(), 0, 0),
950 ];
951
952 for (s, value, scale) in cases {
953 let n = bigdecimal_to_numeric(&BigDecimal::from_str(&s).unwrap());
954 assert_eq!((n.value(), n.scale()), (value, scale), "{s}");
955 n.encode(&mut BytesMut::new()).unwrap();
956 }
957 }
958
959 #[test]
960 #[cfg(feature = "bigdecimal")]
961 fn no_overflowing_pow() {
962 use crate::{ColumnData, ToSql};
963 use bigdecimal::FromPrimitive;
964
965 let dec = BigDecimal::new(BigInt::from_i8(1).unwrap(), -20);
966 let res = dec.to_sql();
967
968 assert_eq!(
969 ColumnData::Numeric(Some(Numeric::new_with_scale(100000000000000000000i128, 0))),
970 res
971 );
972 }
973}