Skip to main content

tds_driver/tds/
numeric.rs

1//! Representations of numeric types.
2
3use 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/// Represent a sql Decimal / Numeric type. It is stored in a i128 and has a
16/// maximum precision of 38 decimals.
17///
18/// A recommended way of dealing with numeric values is by enabling the
19/// `rust_decimal` feature and using its `Decimal` type instead.
20#[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/// `Numeric`'s serde form, checked on the way in: the arithmetic in
29/// `int_part`, `dec_part` and `Display` assumes a scale of at most 38.
30#[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    /// Creates a new Numeric value.
55    ///
56    /// # Panic
57    /// It will panic if the scale exceeds 38.
58    pub fn new_with_scale(value: i128, scale: u8) -> Self {
59        // SQL Server allows a maximum precision of 38, and scale may equal
60        // precision (e.g. `decimal(38, 38)`), so scale 38 is valid; 10^38 still
61        // fits in i128.
62        assert!(scale <= 38);
63
64        Numeric { value, scale }
65    }
66
67    /// Extract the decimal part.
68    pub fn dec_part(self) -> i128 {
69        let scale = self.pow_scale();
70        self.value - (self.value / scale) * scale
71    }
72
73    /// Extract the integer part.
74    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    /// The scale (where is the decimal point) of the value.
84    #[inline]
85    pub fn scale(self) -> u8 {
86        self.scale
87    }
88
89    /// The internal integer value
90    #[inline]
91    pub fn value(self) -> i128 {
92        self.value
93    }
94
95    /// The precision of the `Number` as a number of digits: the smallest `p`
96    /// for which the value fits `decimal(p, scale)`.
97    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        // No leading zero digit: 0.5 is decimal(1, 1), as SQL Server types it.
105        (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    /// Builds a value from its wire form: a sign byte (0 negative, 1 positive)
137    /// then a 4, 8, 12 or 16 byte little-endian magnitude.
138    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        // A legal decimal(38, s) magnitude is below 10^38 < i128::MAX.
154        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    /// The nearest `f64`. Exact operands make one division correctly rounded;
168    /// otherwise the decimal string is, by `str::parse`.
169    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    /// The nearest `f32`, as [`Numeric::to_f64`].
187    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        // decimal(38, s) holds at most 38 digits whatever the scale.
209        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        // `len()` recomputes `precision()` via a division loop; compute it once.
216        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        // A rescale that overflows can't equal a value that fits in i128.
290        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    /// Past `Decimal`'s scale 28 or 96-bit mantissa, trailing zeros are dropped
304    /// until it fits, as SqlClient's `SqlBuffer.Decimal` drops them rather than
305    /// fail; any other loss is an error.
306    #[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    /// `ToSql` cannot fail, so a value `decimal(38, s)` cannot hold exactly
392    /// becomes a 39-digit `Numeric`, which the encoder rejects. Rounding is
393    /// left to the caller (`with_scale_round`).
394    fn to_numeric(value: &BigDecimal) -> Numeric {
395        // value = int * 10^-exp
396        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            // The server has no negative scale: 9e3 is sent as 9000. Checked
411            // before expanding, as the exponent can be huge.
412            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            // Exact only if the digits past scale 38 are trailing zeros.
418            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        // 1.23 at scale 5 (123000) equals 1.23 at scale 2 (123). A scale gap of
478        // 3 is chosen so the `self.scale - other.scale` exponent (3) differs from
479        // both `+` (7) and `/` (1) — pinning the subtraction — and the
480        // `10^gap * v` multiply differs from `+`/`/`. Both comparison directions
481        // exercise the Greater and Less arms.
482        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); // Greater arm (self.scale > other.scale)
491        assert_eq!(narrow, wide); // Less arm
492        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        // The encoder writes 1 length byte + 1 sign byte + (len-1) magnitude
504        // bytes. This pins the per-length arms (deleting the 9- or 13-byte arm
505        // would change the byte count) and the sign byte for zero.
506        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        // Zero is encoded as positive (sign byte 1), not negative.
515        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        // A magnitude whose high bytes are all non-zero: if decode_d128 wrongly
525        // short-circuited on "all high bytes non-zero" it would drop the high
526        // word and mis-decode. Positive (high byte 0x01 < i128::MAX high bit).
527        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        // 17-byte form: len, sign(1 = positive), then 16 magnitude bytes.
543        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); // magnitude exactly i128::MAX
547        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        // One past i128::MAX (high bit set) must be rejected, not wrapped.
554        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        // decimal(38, 38) is valid in SQL Server, so scale 38 must be accepted.
627        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        // 0.05 fits decimal(2, 2); no digit is spent on the leading zero.
639        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        // 38 digits, and scale 38 with significant digits past 28.
727        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        // Only as many trailing zeros are dropped as needed to fit.
735        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        // Negative values format with a single leading sign and an unsigned
763        // fractional part (see #390).
764        let n = Numeric::new_with_scale(-57705, 3);
765        assert_eq!(format!("{}", n), "-57.705");
766
767        // Zero-padded fractional part for small decimals.
768        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; // len 5
807        round_trip(42, 0).await; // len 5
808        round_trip(-42, 2).await; // negative, len 5
809        round_trip(10i128.pow(12), 0).await; // len 9
810        round_trip(10i128.pow(20), 0).await; // len 13
811        round_trip(-(10i128.pow(20)), 3).await; // negative, len 13
812        round_trip(10i128.pow(30), 0).await; // len 17
813        round_trip(10i128.pow(38) - 1, 38).await; // 38 digits, the maximum
814        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        // len = 17, sign = 1 (positive), magnitude = 2^127 (byte[15] = 0x80),
846        // which exceeds i128::MAX. Must return a protocol error rather than
847        // wrapping to a negative value (or panicking on i128::MIN * -1).
848        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        // Invalid sign byte (2 is neither 0 nor 1).
866        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        // Invalid length byte (6 is not one of 0/5/9/13/17).
876        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            // Scale 41, but the three digits past 38 are zeros.
947            (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}