Skip to main content

tiberius/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 byteorder::{ByteOrder, LittleEndian};
9use bytes::{BufMut, BytesMut};
10#[cfg(feature = "rust_decimal")]
11#[cfg_attr(docsrs, doc(cfg(feature = "rust_decimal")))]
12pub use rust_decimal::Decimal;
13use std::cmp::{Ordering, PartialEq};
14use std::fmt::{self, Debug, Display, Formatter};
15
16/// Represent a sql Decimal / Numeric type. It is stored in a i128 and has a
17/// maximum precision of 38 decimals.
18///
19/// A recommended way of dealing with numeric values is by enabling the
20/// `rust_decimal` feature and using its `Decimal` type instead.
21#[derive(Copy, Clone)]
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23pub struct Numeric {
24    value: i128,
25    scale: u8,
26}
27
28impl Numeric {
29    /// Creates a new Numeric value.
30    ///
31    /// # Panic
32    /// It will panic if the scale exceeds 38.
33    pub fn new_with_scale(value: i128, scale: u8) -> Self {
34        // SQL Server allows a maximum precision of 38, and scale may equal
35        // precision (e.g. `decimal(38, 38)`), so scale 38 is valid; 10^38 still
36        // fits in i128.
37        assert!(scale <= 38);
38
39        Numeric { value, scale }
40    }
41
42    /// Extract the decimal part.
43    pub fn dec_part(self) -> i128 {
44        let scale = self.pow_scale();
45        self.value - (self.value / scale) * scale
46    }
47
48    /// Extract the integer part.
49    pub fn int_part(self) -> i128 {
50        self.value / self.pow_scale()
51    }
52
53    #[inline]
54    fn pow_scale(self) -> i128 {
55        10i128.pow(self.scale as u32)
56    }
57
58    /// The scale (where is the decimal point) of the value.
59    #[inline]
60    pub fn scale(self) -> u8 {
61        self.scale
62    }
63
64    /// The internal integer value
65    #[inline]
66    pub fn value(self) -> i128 {
67        self.value
68    }
69
70    /// The precision of the `Number` as a number of digits.
71    pub fn precision(self) -> u8 {
72        let mut result = 0;
73        let mut n = self.int_part();
74
75        while n != 0 {
76            n /= 10;
77            result += 1;
78        }
79
80        if result == 0 {
81            1 + self.scale()
82        } else {
83            result + self.scale()
84        }
85    }
86
87    pub(crate) fn len(self) -> u8 {
88        match self.precision() {
89            1..=9 => 5,
90            10..=19 => 9,
91            20..=28 => 13,
92            _ => 17,
93        }
94    }
95
96    pub(crate) async fn decode<R>(src: &mut R, scale: u8) -> crate::Result<Option<Self>>
97    where
98        R: SqlReadBytes + Unpin,
99    {
100        fn decode_d128(buf: &[u8]) -> u128 {
101            let low_part = LittleEndian::read_u64(&buf[0..]) as u128;
102
103            if !buf[8..].iter().any(|x| *x != 0) {
104                return low_part;
105            }
106
107            let high_part = match buf.len() {
108                12 => LittleEndian::read_u32(&buf[8..]) as u128,
109                16 => LittleEndian::read_u64(&buf[8..]) as u128,
110                _ => unreachable!(),
111            };
112
113            // `byteorder::LittleEndian` already yields the correct host-native
114            // integer regardless of target endianness, so `low_part`/`high_part`
115            // need no further swapping (a previous `cfg(target_endian = "big")`
116            // swap here corrupted large decimals on big-endian hosts).
117            let high_part = high_part * (u64::MAX as u128 + 1);
118            low_part + high_part
119        }
120
121        let len = src.read_u8().await?;
122
123        if len == 0 {
124            Ok(None)
125        } else {
126            let sign = match src.read_u8().await? {
127                0 => -1i128,
128                1 => 1i128,
129                _ => return Err(Error::Protocol("decimal: invalid sign".into())),
130            };
131
132            let value = match len {
133                5 => src.read_u32_le().await? as i128 * sign,
134                9 => src.read_u64_le().await? as i128 * sign,
135                13 => {
136                    let mut bytes = [0u8; 12]; //u96
137                    for item in &mut bytes {
138                        *item = src.read_u8().await?;
139                    }
140                    decode_d128(&bytes) as i128 * sign
141                }
142                17 => {
143                    let mut bytes = [0u8; 16];
144                    for item in &mut bytes {
145                        *item = src.read_u8().await?;
146                    }
147                    let magnitude = decode_d128(&bytes);
148                    // A legal `decimal(38, s)` magnitude is < 10^38 < i128::MAX,
149                    // so any 16-byte magnitude that does not fit in i128 is
150                    // malformed. Reject it rather than letting `as i128` wrap to
151                    // a negative value (and `i128::MIN * -1` overflow-panic).
152                    if magnitude > i128::MAX as u128 {
153                        return Err(Error::Protocol(
154                            "decimal/numeric: magnitude exceeds the representable range".into(),
155                        ));
156                    }
157                    magnitude as i128 * sign
158                }
159                x => {
160                    return Err(Error::Protocol(
161                        format!("decimal/numeric: invalid length of {} received", x).into(),
162                    ))
163                }
164            };
165
166            Ok(Some(Numeric::new_with_scale(value, scale)))
167        }
168    }
169}
170
171impl Encode<BytesMut> for Numeric {
172    fn encode(self, dst: &mut BytesMut) -> crate::Result<()> {
173        // `len()` recomputes `precision()` via a division loop; compute it once.
174        let len = self.len();
175        dst.put_u8(len);
176
177        if self.value < 0 {
178            dst.put_u8(0);
179        } else {
180            dst.put_u8(1);
181        }
182
183        let value = self.value().abs();
184
185        match len {
186            5 => dst.put_u32_le(value as u32),
187            9 => dst.put_u64_le(value as u64),
188            13 => {
189                dst.put_u64_le(value as u64);
190                dst.put_u32_le((value >> 64) as u32)
191            }
192            _ => dst.put_u128_le(value as u128),
193        }
194
195        Ok(())
196    }
197}
198
199impl Debug for Numeric {
200    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
201        write!(
202            f,
203            "{}{}.{:0pad$}",
204            if self.value() < 0 { "-" } else { "" },
205            self.int_part().abs(),
206            self.dec_part().abs(),
207            pad = self.scale as usize
208        )
209    }
210}
211
212impl Display for Numeric {
213    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
214        write!(f, "{:?}", self)
215    }
216}
217
218impl Eq for Numeric {}
219
220impl From<Numeric> for f64 {
221    fn from(n: Numeric) -> f64 {
222        n.dec_part() as f64 / n.pow_scale() as f64 + n.int_part() as f64
223    }
224}
225
226impl From<Numeric> for i128 {
227    fn from(n: Numeric) -> i128 {
228        n.int_part()
229    }
230}
231
232impl From<Numeric> for u128 {
233    fn from(n: Numeric) -> u128 {
234        n.int_part() as u128
235    }
236}
237
238impl PartialEq for Numeric {
239    fn eq(&self, other: &Self) -> bool {
240        match self.scale.cmp(&other.scale) {
241            Ordering::Greater => {
242                10i128.pow((self.scale - other.scale) as u32) * other.value == self.value
243            }
244            Ordering::Less => {
245                10i128.pow((other.scale - self.scale) as u32) * self.value == other.value
246            }
247            Ordering::Equal => self.value == other.value,
248        }
249    }
250}
251
252#[cfg(feature = "rust_decimal")]
253mod decimal {
254    use super::{Decimal, Numeric};
255    use crate::ColumnData;
256
257    #[cfg(feature = "tds73")]
258    from_sql!(Decimal: ColumnData::Numeric(ref num) => num.map(|num| {
259        Decimal::from_i128_with_scale(
260            num.value(),
261            num.scale() as u32,
262        )})
263    );
264
265    #[cfg(feature = "tds73")]
266    to_sql!(self_,
267            Decimal: (ColumnData::Numeric, {
268                let unpacked = self_.unpack();
269
270                let mut value = (((unpacked.hi as u128) << 64)
271                                 + ((unpacked.mid as u128) << 32)
272                                 + unpacked.lo as u128) as i128;
273
274                if self_.is_sign_negative() {
275                    value = -value;
276                }
277
278                Numeric::new_with_scale(value, self_.scale() as u8)
279            });
280    );
281
282    #[cfg(feature = "tds73")]
283    into_sql!(self_,
284            Decimal: (ColumnData::Numeric, {
285                let unpacked = self_.unpack();
286
287                let mut value = (((unpacked.hi as u128) << 64)
288                                 + ((unpacked.mid as u128) << 32)
289                                 + unpacked.lo as u128) as i128;
290
291                if self_.is_sign_negative() {
292                    value = -value;
293                }
294
295                Numeric::new_with_scale(value, self_.scale() as u8)
296            });
297    );
298}
299
300#[cfg(feature = "bigdecimal")]
301mod bigdecimal_ {
302    use super::{BigDecimal, BigInt, Numeric};
303    use crate::ColumnData;
304    use num_traits::ToPrimitive;
305    use std::convert::TryFrom;
306
307    #[cfg(feature = "tds73")]
308    from_sql!(BigDecimal: ColumnData::Numeric(ref num) => num.map(|num| {
309        let int = BigInt::from(num.value());
310
311        BigDecimal::new(int, num.scale() as i64)
312    }));
313
314    #[cfg(feature = "tds73")]
315    to_sql!(self_,
316            BigDecimal: (ColumnData::Numeric, {
317                let (int, exp) = self_.as_bigint_and_exponent();
318                // SQL Server cannot store negative scales, so we have
319                // to convert the number to the correct exponent
320                // before storing.
321                //
322                // E.g. `Decimal(9, -3)` would be stored as
323                // `Decimal(9000, 0)`.
324                let (int, exp) = if exp < 0 {
325                    self_.with_scale(0).into_bigint_and_exponent()
326                } else {
327                    (int, exp)
328                };
329
330                let value = int.to_i128().expect("Given BigDecimal overflowing the maximum accepted value.");
331
332                let scale = u8::try_from(std::cmp::max(exp, 0))
333                    .expect("Given BigDecimal exponent overflowing the maximum accepted scale (255).");
334
335                Numeric::new_with_scale(value, scale)
336            });
337    );
338
339    #[cfg(feature = "tds73")]
340    into_sql!(self_,
341            BigDecimal: (ColumnData::Numeric, {
342                let (int, exp) = self_.as_bigint_and_exponent();
343                // SQL Server cannot store negative scales, so we have
344                // to convert the number to the correct exponent
345                // before storing.
346                //
347                // E.g. `Decimal(9, -3)` would be stored as
348                // `Decimal(9000, 0)`.
349                let (int, exp) = if exp < 0 {
350                    self_.with_scale(0).into_bigint_and_exponent()
351                } else {
352                    (int, exp)
353                };
354                let value = int.to_i128().expect("Given BigDecimal overflowing the maximum accepted value.");
355
356                let scale = u8::try_from(std::cmp::max(exp, 0))
357                    .expect("Given BigDecimal exponent overflowing the maximum accepted scale (255).");
358
359                Numeric::new_with_scale(value, scale)
360            });
361    );
362}
363
364#[cfg(test)]
365mod tests {
366    use super::*;
367
368    #[test]
369    fn numeric_eq() {
370        assert_eq!(
371            Numeric {
372                value: 100501,
373                scale: 2
374            },
375            Numeric {
376                value: 1005010,
377                scale: 3
378            }
379        );
380        assert!(
381            Numeric {
382                value: 100501,
383                scale: 2
384            } != Numeric {
385                value: 10050,
386                scale: 1
387            }
388        );
389    }
390
391    #[test]
392    fn numeric_eq_normalizes_across_a_scale_gap() {
393        // 1.23 at scale 5 (123000) equals 1.23 at scale 2 (123). A scale gap of
394        // 3 is chosen so the `self.scale - other.scale` exponent (3) differs from
395        // both `+` (7) and `/` (1) — pinning the subtraction — and the
396        // `10^gap * v` multiply differs from `+`/`/`. Both comparison directions
397        // exercise the Greater and Less arms.
398        let wide = Numeric {
399            value: 123_000,
400            scale: 5,
401        };
402        let narrow = Numeric {
403            value: 123,
404            scale: 2,
405        };
406        assert_eq!(wide, narrow); // Greater arm (self.scale > other.scale)
407        assert_eq!(narrow, wide); // Less arm
408        assert!(
409            narrow
410                != Numeric {
411                    value: 124,
412                    scale: 2
413                }
414        );
415    }
416
417    #[test]
418    fn encode_byte_layout_matches_length_bucket() {
419        // The encoder writes 1 length byte + 1 sign byte + (len-1) magnitude
420        // bytes. This pins the per-length arms (deleting the 9- or 13-byte arm
421        // would change the byte count) and the sign byte for zero.
422        for value in [1i128, 10i128.pow(12), 10i128.pow(20), 10i128.pow(30)] {
423            let n = Numeric::new_with_scale(value, 0);
424            let expected = n.len() as usize + 1;
425            let mut buf = BytesMut::new();
426            n.encode(&mut buf).unwrap();
427            assert_eq!(buf.len(), expected, "byte count for {value}");
428        }
429
430        // Zero is encoded as positive (sign byte 1), not negative.
431        let mut zero = BytesMut::new();
432        Numeric::new_with_scale(0, 0).encode(&mut zero).unwrap();
433        assert_eq!(zero[1], 1, "zero must carry the positive sign byte");
434    }
435
436    #[tokio::test]
437    async fn decode_d128_keeps_high_and_low_words() {
438        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
439
440        // A magnitude whose high bytes are all non-zero: if decode_d128 wrongly
441        // short-circuited on "all high bytes non-zero" it would drop the high
442        // word and mis-decode. Positive (high byte 0x01 < i128::MAX high bit).
443        let value = 0x0101_0101_0101_0101_0101_0101_0101_0101i128;
444        let n = Numeric::new_with_scale(value, 0);
445        let mut buf = BytesMut::new();
446        n.encode(&mut buf).unwrap();
447        let decoded = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
448            .await
449            .unwrap()
450            .unwrap();
451        assert_eq!(decoded.value(), value);
452    }
453
454    #[tokio::test]
455    async fn decode_accepts_magnitude_at_i128_max_but_rejects_beyond() {
456        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
457
458        // 17-byte form: len, sign(1 = positive), then 16 magnitude bytes.
459        let mut at_max = BytesMut::new();
460        at_max.put_u8(17);
461        at_max.put_u8(1);
462        at_max.put_i128_le(i128::MAX); // magnitude exactly i128::MAX
463        let decoded = Numeric::decode(&mut at_max.into_sql_read_bytes(), 0)
464            .await
465            .expect("i128::MAX magnitude is representable")
466            .unwrap();
467        assert_eq!(decoded.value(), i128::MAX);
468
469        // One past i128::MAX (high bit set) must be rejected, not wrapped.
470        let mut beyond = BytesMut::new();
471        beyond.put_u8(17);
472        beyond.put_u8(1);
473        beyond.put_u128_le((i128::MAX as u128) + 1);
474        assert!(Numeric::decode(&mut beyond.into_sql_read_bytes(), 0)
475            .await
476            .is_err());
477    }
478
479    #[test]
480    fn numeric_to_f64() {
481        assert_eq!(f64::from(Numeric::new_with_scale(57705, 2)), 577.05);
482    }
483
484    #[test]
485    fn numeric_to_int_dec_part() {
486        let n = Numeric::new_with_scale(57705, 2);
487        assert_eq!(n.int_part(), 577);
488        assert_eq!(n.dec_part(), 5);
489    }
490
491    #[test]
492    fn numeric_to_string() {
493        assert_eq!(Numeric::new_with_scale(123, 0).to_string(), "123.0");
494        assert_eq!(Numeric::new_with_scale(123, 1).to_string(), "12.3");
495        assert_eq!(Numeric::new_with_scale(123, 2).to_string(), "1.23");
496        assert_eq!(Numeric::new_with_scale(123, 3).to_string(), "0.123");
497        assert_eq!(Numeric::new_with_scale(123, 4).to_string(), "0.0123");
498        assert_eq!(
499            Numeric::new_with_scale(123, 36).to_string(),
500            "0.000000000000000000000000000000000123"
501        );
502        assert_eq!(
503            Numeric::new_with_scale(123, 37).to_string(),
504            "0.0000000000000000000000000000000000123"
505        );
506        assert_eq!(Numeric::new_with_scale(-123, 0).to_string(), "-123.0");
507        assert_eq!(Numeric::new_with_scale(-123, 1).to_string(), "-12.3");
508        assert_eq!(Numeric::new_with_scale(-123, 2).to_string(), "-1.23");
509        assert_eq!(Numeric::new_with_scale(-123, 3).to_string(), "-0.123");
510        assert_eq!(Numeric::new_with_scale(-123, 4).to_string(), "-0.0123");
511        assert_eq!(
512            Numeric::new_with_scale(-123, 36).to_string(),
513            "-0.000000000000000000000000000000000123"
514        );
515        assert_eq!(
516            Numeric::new_with_scale(-123, 37).to_string(),
517            "-0.0000000000000000000000000000000000123"
518        );
519    }
520
521    #[test]
522    fn calculates_precision_correctly() {
523        let n = Numeric::new_with_scale(57705, 2);
524        assert_eq!(5, n.precision());
525    }
526
527    #[test]
528    fn new_with_scale_accessors() {
529        let n = Numeric::new_with_scale(12345, 3);
530        assert_eq!(n.value(), 12345);
531        assert_eq!(n.scale(), 3);
532        assert_eq!(n.int_part(), 12);
533        assert_eq!(n.dec_part(), 345);
534    }
535
536    #[test]
537    fn new_with_scale_allows_max_scale() {
538        // decimal(38, 38) is valid in SQL Server, so scale 38 must be accepted.
539        assert_eq!(Numeric::new_with_scale(1, 38).scale(), 38);
540    }
541
542    #[test]
543    #[should_panic]
544    fn new_with_scale_panics_on_too_large_scale() {
545        Numeric::new_with_scale(1, 39);
546    }
547
548    #[test]
549    fn precision_with_zero_int_part() {
550        // int_part == 0 -> precision is 1 + scale.
551        let n = Numeric::new_with_scale(5, 2);
552        assert_eq!(n.int_part(), 0);
553        assert_eq!(n.precision(), 3);
554    }
555
556    #[test]
557    fn precision_scaling_by_length_buckets() {
558        assert_eq!(Numeric::new_with_scale(1, 0).len(), 5);
559        assert_eq!(Numeric::new_with_scale(1_000_000_000, 0).len(), 9);
560        assert_eq!(Numeric::new_with_scale(10i128.pow(19), 0).len(), 13);
561        assert_eq!(Numeric::new_with_scale(10i128.pow(28), 0).len(), 17);
562    }
563
564    #[test]
565    fn display_and_debug() {
566        let n = Numeric::new_with_scale(57705, 2);
567        assert_eq!(format!("{:?}", n), "577.05");
568        assert_eq!(format!("{}", n), "577.05");
569
570        // Negative values format with a single leading sign and an unsigned
571        // fractional part (see #390).
572        let n = Numeric::new_with_scale(-57705, 3);
573        assert_eq!(format!("{}", n), "-57.705");
574
575        // Zero-padded fractional part for small decimals.
576        let n = Numeric::new_with_scale(102, 4);
577        assert_eq!(format!("{}", n), "0.0102");
578    }
579
580    #[test]
581    fn from_numeric_conversions() {
582        let n = Numeric::new_with_scale(57705, 2);
583        assert_eq!(i128::from(n), 577);
584        assert_eq!(u128::from(n), 577);
585        assert!((f64::from(n) - 577.05).abs() < f64::EPSILON);
586    }
587
588    #[test]
589    fn eq_across_scales_negative() {
590        assert_eq!(
591            Numeric::new_with_scale(-100501, 2),
592            Numeric::new_with_scale(-1005010, 3),
593        );
594    }
595
596    async fn round_trip(value: i128, scale: u8) {
597        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
598
599        let n = Numeric::new_with_scale(value, scale);
600        let mut buf = BytesMut::new();
601        n.encode(&mut buf).expect("encode must succeed");
602
603        let decoded = Numeric::decode(&mut buf.into_sql_read_bytes(), scale)
604            .await
605            .expect("decode must succeed")
606            .expect("value must be present");
607
608        assert_eq!(decoded, n);
609        assert_eq!(decoded.value(), value);
610    }
611
612    #[tokio::test]
613    async fn encode_decode_round_trip() {
614        round_trip(0, 0).await; // len 5
615        round_trip(42, 0).await; // len 5
616        round_trip(-42, 2).await; // negative, len 5
617        round_trip(10i128.pow(12), 0).await; // len 9
618        round_trip(10i128.pow(20), 0).await; // len 13
619        round_trip(-(10i128.pow(20)), 3).await; // negative, len 13
620        round_trip(10i128.pow(30), 0).await; // len 17
621    }
622
623    #[tokio::test]
624    async fn decode_zero_length_is_none() {
625        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
626
627        let mut buf = BytesMut::new();
628        buf.put_u8(0);
629
630        let decoded = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
631            .await
632            .expect("decode must succeed");
633
634        assert!(decoded.is_none());
635    }
636
637    #[tokio::test]
638    async fn decode_rejects_len17_magnitude_over_i128_max() {
639        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
640
641        // len = 17, sign = 1 (positive), magnitude = 2^127 (byte[15] = 0x80),
642        // which exceeds i128::MAX. Must return a protocol error rather than
643        // wrapping to a negative value (or panicking on i128::MIN * -1).
644        let mut buf = BytesMut::new();
645        buf.put_u8(17);
646        buf.put_u8(1);
647        let mut mag = [0u8; 16];
648        mag[15] = 0x80;
649        buf.extend_from_slice(&mag);
650
651        let err = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
652            .await
653            .expect_err("out-of-range magnitude must error");
654        assert!(matches!(err, Error::Protocol(_)));
655    }
656
657    #[tokio::test]
658    async fn decode_rejects_invalid_sign_and_length() {
659        use crate::sql_read_bytes::test_utils::IntoSqlReadBytes;
660
661        // Invalid sign byte (2 is neither 0 nor 1).
662        let mut buf = BytesMut::new();
663        buf.put_u8(5);
664        buf.put_u8(2);
665        buf.put_u32_le(1);
666        let err = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
667            .await
668            .expect_err("invalid sign must error");
669        assert!(matches!(err, Error::Protocol(_)));
670
671        // Invalid length byte (6 is not one of 0/5/9/13/17).
672        let mut buf = BytesMut::new();
673        buf.put_u8(6);
674        buf.put_u8(1);
675        buf.extend_from_slice(&[0u8; 4]);
676        let err = Numeric::decode(&mut buf.into_sql_read_bytes(), 0)
677            .await
678            .expect_err("invalid length must error");
679        assert!(matches!(err, Error::Protocol(_)));
680    }
681
682    #[test]
683    #[cfg(feature = "bigdecimal")]
684    fn no_overflowing_pow() {
685        use crate::{ColumnData, ToSql};
686        use bigdecimal::FromPrimitive;
687
688        let dec = BigDecimal::new(BigInt::from_i8(1).unwrap(), -20);
689        let res = dec.to_sql();
690
691        assert_eq!(
692            ColumnData::Numeric(Some(Numeric::new_with_scale(100000000000000000000i128, 0))),
693            res
694        );
695    }
696}