Repository: risingwavelabs/risingwave
File: src/expr/impl/src/scalar/arithmetic_op.rs (Rust)

Code before:
/// statement error underflow
/// SELECT gamma('-1000.5'::float8);
///
/// statement error overflow
/// SELECT gamma('0'::float8);
///
/// statement error overflow
/// SELECT gamma('1000'::float8);
/// ```
#[function("gamma(float8) -> float8")]
pub fn gamma_f64(input: F64) -> Result<F64> {
    let mut result = input;
    if input.is_nan() {
        return Ok(result);
    } else if input.is_infinite() {
        if input.is_negative() {
            return Err(ExprError::NumericOverflow);
        }
    } else {
        result = input.gamma();
        if result.is_nan() || result.is_infinite() {
            if !result.is_zero() {
                return Err(ExprError::NumericOverflow);
            } else {
                return Err(ExprError::NumericUnderflow);
            }
        } else if result.is_zero() {
            return Err(ExprError::NumericUnderflow);
        }
    }
    Ok(result)
}

/// ```slt
/// query R
/// SELECT lgamma('+inf'::float8);
/// ----
/// Infinity
///
/// query R
/// SELECT lgamma('nan'::float8);
/// ----
/// NaN
///
/// query R
/// SELECT lgamma('-inf'::float8);
/// ----
/// Infinity
///
/// query R
/// SELECT lgamma('0.5'::float8);

Review comment (bug):
The conditional logic here is incorrect. When result.is_nan() or result.is_infinite() is true, the condition checks if result.is_zero(). However, NaN and Infinity can never be zero, so the else branch at line 510 will never execute. The logic should check the original result value separately: if result.is_nan(), return NumericOverflow; if result.is_infinite(), return NumericOverflow; if result.is_zero(), return NumericUnderflow.
```suggestion
        if result.is_nan() {
            return Err(ExprError::NumericOverflow);
        } else if result.is_infinite() {
            return Err(ExprError::NumericOverflow);
```

Code after:
    #[test]
    fn test_arithmetic() {
        assert_eq!(
            general_add::<Decimal, i32, Decimal>(dec("1.0"), 1).unwrap(),
            dec("2.0")
        );
        assert_eq!(
            general_sub::<Decimal, i32, Decimal>(dec("1.0"), 2).unwrap(),
            dec("-1.0")
        );
        assert_eq!(
            general_mul::<Decimal, i32, Decimal>(dec("1.0"), 2).unwrap(),
            dec("2.0")
        );
        assert_eq!(
            general_div::<Decimal, i32, Decimal>(dec("2.0"), 2).unwrap(),
            dec("1.0")
        );
        assert_eq!(
            general_mod::<Decimal, i32, Decimal>(dec("2.0"), 2).unwrap(),
            dec("0")
        );
        assert_eq!(general_neg::<Decimal>(dec("1.0")).unwrap(), dec("-1.0"));
        assert_eq!(general_add::<i16, i32, i32>(1i16, 1i32).unwrap(), 2i32);
        assert_eq!(general_sub::<i16, i32, i32>(1i16, 1i32).unwrap(), 0i32);
        assert_eq!(general_mul::<i16, i32, i32>(1i16, 1i32).unwrap(), 1i32);
        assert_eq!(general_div::<i16, i32, i32>(1i16, 1i32).unwrap(), 1i32);
        assert_eq!(general_mod::<i16, i32, i32>(1i16, 1i32).unwrap(), 0i32);
        assert_eq!(general_neg::<i16>(1i16).unwrap(), -1i16);

        assert!(
            general_add::<i32, F32, F64>(-1i32, 1f32.into())
                .unwrap()
                .is_zero()
        );
        assert!(
            general_sub::<i32, F32, F64>(1i32, 1f32.into())
                .unwrap()
                .is_zero()
        );
        assert!(
            general_mul::<i32, F32, F64>(0i32, 1f32.into())
                .unwrap()
                .is_zero()
        );
        assert!(
            general_div::<i32, F32, F64>(0i32, 1f32.into())
                .unwrap()
                .is_zero()
        );
        assert_eq!(general_neg::<F32>(1f32.into()).unwrap(), F32::from(-1f32));