You are viewing a plain text version of this content. The canonical link for it is here.
Posted to github@arrow.apache.org by "alamb (via GitHub)" <gi...@apache.org> on 2023/07/17 21:39:09 UTC

[GitHub] [arrow-datafusion] alamb commented on a diff in pull request #6942: functions: support trunc() function with one or two args

alamb commented on code in PR #6942:
URL: https://github.com/apache/arrow-datafusion/pull/6942#discussion_r1265922038


##########
datafusion/expr/src/expr_fn.rs:
##########
@@ -501,7 +501,7 @@ scalar_expr!(
 scalar_expr!(Degrees, degrees, num, "converts radians to degrees");
 scalar_expr!(Radians, radians, num, "converts degrees to radians");
 nary_scalar_expr!(Round, round, "round to nearest integer");
-scalar_expr!(Trunc, trunc, num, "truncate toward zero");
+nary_scalar_expr!(Trunc, trunc, "truncate toward to precision");

Review Comment:
   I don't think this function truncates toward `precision` -- instead it truncates towards zero with an optional precision
   
   ```suggestion
   nary_scalar_expr!(Trunc, trunc, "truncate toward zero, with optional precision");
   ```



##########
datafusion/physical-expr/src/math_expressions.rs:
##########
@@ -739,4 +824,76 @@ mod tests {
         assert_eq!(ints.value(2), 75);
         assert_eq!(ints.value(3), 16);
     }
+
+    #[test]
+    fn test_truncate_32() {

Review Comment:
   I think this test would be better written using sqllogictest (I have left comment about how to do this)



##########
datafusion/physical-expr/src/math_expressions.rs:
##########
@@ -497,6 +497,91 @@ pub fn log(args: &[ArrayRef]) -> Result<ArrayRef> {
     }
 }
 
+/// Truncate(numeric, decimalPrecision) and trunc(numeric) SQL function
+pub fn trunc(args: &[ArrayRef]) -> Result<ArrayRef> {
+    if args.len() != 1 && args.len() != 2 {
+        return Err(DataFusionError::Internal(format!(
+            "truncate function requires one or two arguments, got {}",
+            args.len()
+        )));
+    }
+
+    //if only one arg then invoke toolchain trunc(num) and precision = 0 by default
+    //or then invoke the compute_truncate method to process precision
+    let mut _precision = ColumnarValue::Scalar(Int32(Some(0)));
+
+    let num = &args[0];
+    if args.len() == 2 {
+        _precision = ColumnarValue::Array(args[1].clone());
+    }
+
+    match args[0].data_type() {
+        DataType::Float64 => match _precision {
+            ColumnarValue::Scalar(Int32(Some(_precision))) => Ok(Arc::new(
+                make_function_scalar_inputs!(num, "num", Float64Array, { f64::trunc }),
+            )
+                as ArrayRef),
+            ColumnarValue::Array(_precision) => Ok(Arc::new(make_function_inputs2!(
+                num,
+                _precision,
+                "x",
+                "y",
+                Float64Array,
+                Int32Array,
+                { compute_truncate64 }
+            )) as ArrayRef),
+            _ => Err(DataFusionError::Internal(
+                "trunc function requires a scalar or array for precision".to_string(),
+            )),
+        },
+        DataType::Float32 => match _precision {
+            ColumnarValue::Scalar(Int32(Some(_precision))) => Ok(Arc::new(
+                make_function_scalar_inputs!(num, "num", Float32Array, { f32::trunc }),
+            )
+                as ArrayRef),
+            ColumnarValue::Array(_precision) => Ok(Arc::new(make_function_inputs2!(
+                num,
+                _precision,
+                "x",
+                "y",
+                Float32Array,
+                Int32Array,
+                { compute_truncate32 }
+            )) as ArrayRef),
+            _ => Err(DataFusionError::Internal(
+                "trunc function requires a scalar or array for precision".to_string(),
+            )),
+        },
+        other => Err(DataFusionError::Internal(format!(
+            "Unsupported data type {other:?} for function trunc"
+        ))),
+    }
+}
+
+fn compute_truncate32(x: f32, y: usize) -> f32 {
+    let s = format!("{:.precision$}", x, precision = y);

Review Comment:
   You can probably make this quite a bit more efficient by avoiding strings, for example something like (untested):
   
   ```rust
   let factor = 10.0f32.pow(precision);
   (x * factor).round() / factor;
   ```



##########
datafusion/physical-expr/src/math_expressions.rs:
##########
@@ -497,6 +497,91 @@ pub fn log(args: &[ArrayRef]) -> Result<ArrayRef> {
     }
 }
 
+/// Truncate(numeric, decimalPrecision) and trunc(numeric) SQL function
+pub fn trunc(args: &[ArrayRef]) -> Result<ArrayRef> {
+    if args.len() != 1 && args.len() != 2 {
+        return Err(DataFusionError::Internal(format!(
+            "truncate function requires one or two arguments, got {}",
+            args.len()
+        )));
+    }
+
+    //if only one arg then invoke toolchain trunc(num) and precision = 0 by default
+    //or then invoke the compute_truncate method to process precision
+    let mut _precision = ColumnarValue::Scalar(Int32(Some(0)));
+
+    let num = &args[0];
+    if args.len() == 2 {
+        _precision = ColumnarValue::Array(args[1].clone());
+    }

Review Comment:
   The use of `_` as a prefix of a variable is used to tell the compiler it can be unused. In this case I think it is used and thus the variable should be named `precision`
   
   A more "idiomatic" rust way (and avoiding the `mut`) of writing this might be
   ```suggestion
       let precision = if args.len() == 1 {
         ColumnarValue::Scalar(Int32(Some(0)))
        } else {
          ColumnarValue::Array(args[1].clone())
       };
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@arrow.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org