Repository: launchbadge/sqlx
File: sqlx-postgres/src/types/bigdecimal.rs (Rust)

Code before:
        while let Some(&0) = digits.last() {
            digits.pop();
        }

        Ok(PgNumeric::Number {
            sign: match sign {
                Sign::Plus | Sign::NoSign => PgNumericSign::Positive,
                Sign::Minus => PgNumericSign::Negative,
            },
            scale,
            weight,
            digits,
        })
    }
}

impl Encode<'_, Postgres> for BigDecimal {
    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
        use std::str::FromStr;
        // If the argument is too big, then we replace it with a less big argument.
        // This less big argument is already outside the range of allowed PostgreSQL DECIMAL, which
        // means that PostgreSQL will return the 22P03 error kind upon receiving it. This is the
        // expected error, and the user should be ready to handle it anyway.
        PgNumeric::try_from(self)
            .unwrap_or_else(|_| {
                PgNumeric::try_from(&BigDecimal::from_str(&format!("{:030000}", 0)).unwrap())
                    .unwrap()
            })
            .encode(buf);

        IsNull::No
    }

    fn size_hint(&self) -> usize {
        // BigDecimal::digits() gives us base-10 digits, so we divide by 4 to get base-10000 digits
        // and since this is just a hint we just always round up
        8 + (self.digits() / 4 + 1) as usize * 2
    }
}

impl Decode<'_, Postgres> for BigDecimal {
    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
        match value.format() {
            PgValueFormat::Binary => PgNumeric::decode(value.as_bytes()?)?.try_into(),
            PgValueFormat::Text => Ok(value.as_str()?.parse::<BigDecimal>()?),
        }
    }
}

#[cfg(test)]
mod bigdecimal_to_pgnumeric {

Review comment (suggestion):
This is a really clever way to trigger an error but if the only point is to generate a `scale` outside `0x3FFF` you can do it like so:

```rust
PgNumeric {
    digits: vec![0],
    scale: 0x400,
    weight: 0
}
```

Code after:
        while let Some(&0) = digits.last() {
            digits.pop();
        }

        Ok(PgNumeric::Number {
            sign: sign_to_pg(sign),
            scale,
            weight,
            digits,
        })
    }
}

#[doc=include_str!("bigdecimal-range.md")]
impl Encode<'_, Postgres> for BigDecimal {
    fn encode_by_ref(&self, buf: &mut PgArgumentBuffer) -> IsNull {
        // If the argument is too big, then we replace it with a less big argument.
        // This less big argument is already outside the range of allowed PostgreSQL DECIMAL, which
        // means that PostgreSQL will return the 22P03 error kind upon receiving it. This is the
        // expected error, and the user should be ready to handle it anyway.
        PgNumeric::try_from(self)
            .unwrap_or_else(|_| {
                PgNumeric::Number {
                    digits: vec![1],
                    // This is larger than the maximum allowed value, so Postgres should return an error.
                    scale: 0x4000,
                    weight: 0,
                    sign: sign_to_pg(self.sign()),
                }
            })
            .encode(buf);

        IsNull::No
    }

    fn size_hint(&self) -> usize {
        // BigDecimal::digits() gives us base-10 digits, so we divide by 4 to get base-10000 digits
        // and since this is just a hint we just always round up
        8 + (self.digits() / 4 + 1) as usize * 2
    }
}

/// ### Note: `NaN`
/// `BigDecimal` has a greater range than `NUMERIC` (see the corresponding `Encode` impl for details)
/// but cannot represent `NaN`, so decoding may return an error.
impl Decode<'_, Postgres> for BigDecimal {
    fn decode(value: PgValueRef<'_>) -> Result<Self, BoxDynError> {
        match value.format() {
            PgValueFormat::Binary => PgNumeric::decode(value.as_bytes()?)?.try_into(),
            PgValueFormat::Text => Ok(value.as_str()?.parse::<BigDecimal>()?),
        }