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: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to