martin-g commented on code in PR #19688:
URL: https://github.com/apache/datafusion/pull/19688#discussion_r2672183846
##########
datafusion/expr/src/udaf.rs:
##########
@@ -1424,4 +1458,71 @@ mod test {
value.hash(hasher);
hasher.finish()
}
+
+ #[test]
+ fn test_return_field_nullability_from_nullable_input() {
+ // Test that return_field derives nullability from input field
nullability
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
Review Comment:
```suggestion
let udf = AggregateUDF::from(AMeanUdf::new());
```
##########
datafusion/expr/src/udaf.rs:
##########
@@ -1424,4 +1458,71 @@ mod test {
value.hash(hasher);
hasher.finish()
}
+
+ #[test]
+ fn test_return_field_nullability_from_nullable_input() {
+ // Test that return_field derives nullability from input field
nullability
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a nullable input field
+ let nullable_field = Arc::new(Field::new("col", DataType::Float64,
true));
+ let return_field = a.return_field(&[nullable_field]).unwrap();
+
+ // When input is nullable, output should be nullable
+ assert!(return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_from_non_nullable_input() {
+ // Test that return_field respects non-nullable input fields
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
Review Comment:
```suggestion
let udf = AggregateUDF::from(AMeanUdf::new());
```
##########
datafusion/expr/src/udaf.rs:
##########
@@ -1424,4 +1458,71 @@ mod test {
value.hash(hasher);
hasher.finish()
}
+
+ #[test]
+ fn test_return_field_nullability_from_nullable_input() {
+ // Test that return_field derives nullability from input field
nullability
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a nullable input field
+ let nullable_field = Arc::new(Field::new("col", DataType::Float64,
true));
+ let return_field = a.return_field(&[nullable_field]).unwrap();
+
+ // When input is nullable, output should be nullable
+ assert!(return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_from_non_nullable_input() {
+ // Test that return_field respects non-nullable input fields
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a non-nullable input field
+ let non_nullable_field = Arc::new(Field::new("col", DataType::Float64,
false));
+ let return_field = a.return_field(&[non_nullable_field]).unwrap();
+
+ // When input is non-nullable, output should also be non-nullable
+ assert!(!return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_with_mixed_inputs() {
+ // Test that return_field is nullable if ANY input is nullable
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // With multiple inputs (typical for aggregates in more complex
scenarios)
+ let nullable_field = Arc::new(Field::new("col1", DataType::Float64,
true));
+ let non_nullable_field = Arc::new(Field::new("col2",
DataType::Float64, false));
+
+ let return_field = a.return_field(&[non_nullable_field,
nullable_field]).unwrap();
Review Comment:
```suggestion
let return_field = udf.return_field(&[non_nullable_field,
nullable_field]).unwrap();
```
##########
datafusion/expr/src/udaf.rs:
##########
@@ -528,10 +529,32 @@ pub trait AggregateUDFImpl: Debug + DynEq + DynHash +
Send + Sync {
/// Whether the aggregate function is nullable.
///
+ /// **DEPRECATED**: This method is deprecated and will be removed in a
future version.
+ /// Nullability should instead be specified in [`Self::return_field`]
which can provide
+ /// more context-aware nullability based on input field properties.
+ ///
/// Nullable means that the function could return `null` for any inputs.
/// For example, aggregate functions like `COUNT` always return a non null
value
/// but others like `MIN` will return `NULL` if there is nullable input.
/// Note that if the function is declared as *not* nullable, make sure the
[`AggregateUDFImpl::default_value`] is `non-null`
+ ///
+ /// # Migration Guide
+ ///
+ /// If you need to override nullability, implement [`Self::return_field`]
instead:
+ ///
+ /// ```ignore
+ /// fn return_field(&self, arg_fields: &[FieldRef]) -> Result<FieldRef> {
+ /// let arg_types: Vec<_> = arg_fields.iter().map(|f|
f.data_type()).cloned().collect();
+ /// let data_type = self.return_type(&arg_types)?;
+ /// // Specify nullability based on your function's logic
+ /// let nullable = arg_fields.iter().any(|f| f.is_nullable());
+ /// Ok(Arc::new(Field::new(self.name(), data_type, nullable)))
+ /// }
+ /// ```
+ #[deprecated(
+ since = "42.0.0",
Review Comment:
```suggestion
since = "52.0.0",
```
52.0.0 is being prepared - https://github.com/apache/datafusion/pull/19661
So, the value here might need to be updated if this PR is not merged to
branch-52
##########
datafusion/functions-aggregate/src/percentile_cont.rs:
##########
@@ -427,14 +428,48 @@ impl<T: ArrowNumericType + Debug> Accumulator for
PercentileContAccumulator<T> {
}
fn evaluate(&mut self) -> Result<ScalarValue> {
- let d = std::mem::take(&mut self.all_values);
- let value = calculate_percentile::<T>(d, self.percentile);
+ let value = calculate_percentile::<T>(&mut self.all_values,
self.percentile);
ScalarValue::new_primitive::<T>(value, &T::DATA_TYPE)
}
fn size(&self) -> usize {
size_of_val(self) + self.all_values.capacity() * size_of::<T::Native>()
}
+
+ fn retract_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
Review Comment:
```suggestion
fn retract_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
if values.is_empty() {
return Ok(());
}
```
##########
datafusion/expr/src/udaf.rs:
##########
@@ -1424,4 +1458,71 @@ mod test {
value.hash(hasher);
hasher.finish()
}
+
+ #[test]
+ fn test_return_field_nullability_from_nullable_input() {
+ // Test that return_field derives nullability from input field
nullability
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a nullable input field
+ let nullable_field = Arc::new(Field::new("col", DataType::Float64,
true));
+ let return_field = a.return_field(&[nullable_field]).unwrap();
+
+ // When input is nullable, output should be nullable
+ assert!(return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_from_non_nullable_input() {
+ // Test that return_field respects non-nullable input fields
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a non-nullable input field
+ let non_nullable_field = Arc::new(Field::new("col", DataType::Float64,
false));
+ let return_field = a.return_field(&[non_nullable_field]).unwrap();
Review Comment:
```suggestion
let return_field = udf.return_field(&[non_nullable_field]).unwrap();
```
##########
datafusion/functions-aggregate/src/percentile_cont.rs:
##########
@@ -652,17 +687,31 @@ impl<T: ArrowNumericType + Debug> Accumulator for
DistinctPercentileContAccumula
}
fn evaluate(&mut self) -> Result<ScalarValue> {
- let d = std::mem::take(&mut self.distinct_values.values)
- .into_iter()
- .map(|v| v.0)
- .collect::<Vec<_>>();
- let value = calculate_percentile::<T>(d, self.percentile);
+ let mut values: Vec<T::Native> =
+ self.distinct_values.values.iter().map(|v| v.0).collect();
+ let value = calculate_percentile::<T>(&mut values, self.percentile);
ScalarValue::new_primitive::<T>(value, &T::DATA_TYPE)
}
fn size(&self) -> usize {
size_of_val(self) + self.distinct_values.size()
}
+
+ fn retract_batch(&mut self, values: &[ArrayRef]) -> Result<()> {
+ if values.is_empty() {
+ return Ok(());
+ }
+
+ let arr = values[0].as_primitive::<T>();
+ for value in arr.iter().flatten() {
+ self.distinct_values.values.remove(&Hashable(value));
Review Comment:
Is there a .slt test for this ?
##########
datafusion/expr/src/udaf.rs:
##########
@@ -1424,4 +1458,71 @@ mod test {
value.hash(hasher);
hasher.finish()
}
+
+ #[test]
+ fn test_return_field_nullability_from_nullable_input() {
+ // Test that return_field derives nullability from input field
nullability
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a nullable input field
+ let nullable_field = Arc::new(Field::new("col", DataType::Float64,
true));
+ let return_field = a.return_field(&[nullable_field]).unwrap();
Review Comment:
```suggestion
let return_field = udf.return_field(&[nullable_field]).unwrap();
```
##########
datafusion/expr/src/udaf.rs:
##########
@@ -1424,4 +1458,71 @@ mod test {
value.hash(hasher);
hasher.finish()
}
+
+ #[test]
+ fn test_return_field_nullability_from_nullable_input() {
+ // Test that return_field derives nullability from input field
nullability
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a nullable input field
+ let nullable_field = Arc::new(Field::new("col", DataType::Float64,
true));
+ let return_field = a.return_field(&[nullable_field]).unwrap();
+
+ // When input is nullable, output should be nullable
+ assert!(return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_from_non_nullable_input() {
+ // Test that return_field respects non-nullable input fields
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a non-nullable input field
+ let non_nullable_field = Arc::new(Field::new("col", DataType::Float64,
false));
+ let return_field = a.return_field(&[non_nullable_field]).unwrap();
+
+ // When input is non-nullable, output should also be non-nullable
+ assert!(!return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_with_mixed_inputs() {
+ // Test that return_field is nullable if ANY input is nullable
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
Review Comment:
```suggestion
let udf = AggregateUDF::from(AMeanUdf::new());
```
##########
datafusion/expr/src/udaf.rs:
##########
@@ -1424,4 +1458,71 @@ mod test {
value.hash(hasher);
hasher.finish()
}
+
+ #[test]
+ fn test_return_field_nullability_from_nullable_input() {
+ // Test that return_field derives nullability from input field
nullability
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a nullable input field
+ let nullable_field = Arc::new(Field::new("col", DataType::Float64,
true));
+ let return_field = a.return_field(&[nullable_field]).unwrap();
+
+ // When input is nullable, output should be nullable
+ assert!(return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_from_non_nullable_input() {
+ // Test that return_field respects non-nullable input fields
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a non-nullable input field
+ let non_nullable_field = Arc::new(Field::new("col", DataType::Float64,
false));
+ let return_field = a.return_field(&[non_nullable_field]).unwrap();
+
+ // When input is non-nullable, output should also be non-nullable
+ assert!(!return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_with_mixed_inputs() {
+ // Test that return_field is nullable if ANY input is nullable
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // With multiple inputs (typical for aggregates in more complex
scenarios)
+ let nullable_field = Arc::new(Field::new("col1", DataType::Float64,
true));
+ let non_nullable_field = Arc::new(Field::new("col2",
DataType::Float64, false));
+
+ let return_field = a.return_field(&[non_nullable_field,
nullable_field]).unwrap();
+
+ // If ANY input is nullable, result should be nullable
+ assert!(return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_preserves_return_type() {
+ // Test that return_field correctly preserves the return type
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
Review Comment:
```suggestion
let udf = AggregateUDF::from(AMeanUdf::new());
```
##########
docs/source/library-user-guide/functions/udf-nullability.md:
##########
@@ -0,0 +1,268 @@
+<!---
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+
+# Aggregate UDF Nullability
+
+## Overview
+
+DataFusion distinguishes between the nullability of aggregate function outputs
and their input fields. This document explains how nullability is computed for
aggregate User Defined Functions (UDAFs) and how to correctly specify it in
your custom functions.
+
+## The Change: From `is_nullable()` to `return_field()`
+
+### What Changed?
+
+In earlier versions of DataFusion, aggregate function nullability was
controlled by the `is_nullable()` method on `AggregateUDFImpl`. This method
returned a simple boolean indicating whether the function could ever return
`NULL`, regardless of input characteristics.
+
+**This approach has been deprecated** in favor of computing nullability more
accurately via the `return_field()` method, which has access to the actual
field metadata of the inputs. This allows for more precise nullability
inference based on whether input fields are nullable.
+
+### Why the Change?
+
+1. **Input-aware nullability**: The new approach allows the function's
nullability to depend on the nullability of its inputs. For example,
`MIN(column)` should only be nullable if `column` is nullable.
+
+2. **Consistency with scalar UDFs**: Scalar UDFs already follow this pattern
using `return_field_from_args()`, and aggregate UDFs now align with this design.
+
+3. **More accurate schema inference**: Query optimizers and executors can now
make better decisions about whether intermediate or final results can be null.
+
+## How Nullability Works Now
+
+By default, the `return_field()` method in `AggregateUDFImpl` computes the
output field using this logic:
+
+```text
+output_is_nullable = ANY input field is nullable
+```
+
+In other words:
+- **If ALL input fields are non-nullable**, the output is **non-nullable**
+- **If ANY input field is nullable**, the output is **nullable**
+
+This default behavior works well for most aggregate functions like `MIN`,
`MAX`, `SUM`, and `AVG`.
+
+## Implementing Custom Aggregate UDFs
+
+### Default Behavior (Recommended)
+
+For most aggregate functions, you don't need to override `return_field()`. The
default implementation will correctly infer nullability from inputs:
+
+```rust
+use std::sync::Arc;
+use std::any::Any;
+use arrow::datatypes::{DataType, Field, FieldRef};
+use datafusion_common::Result;
+use datafusion_expr::{AggregateUDFImpl, Signature, Volatility};
+use datafusion_functions_aggregate_common::accumulator::AccumulatorArgs;
+
+#[derive(Debug)]
+struct MyAggregateFunction {
+ signature: Signature,
+}
+
+impl MyAggregateFunction {
+ fn new() -> Self {
+ Self {
+ signature: Signature::uniform(
+ 1,
+ vec![DataType::Float64],
+ Volatility::Immutable,
+ ),
+ }
+ }
+}
+
+impl AggregateUDFImpl for MyAggregateFunction {
+ fn as_any(&self) -> &dyn Any {
+ self
+ }
+
+ fn name(&self) -> &str {
+ "my_agg"
+ }
+
+ fn signature(&self) -> &Signature {
+ &self.signature
+ }
+
+ fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
+ Ok(DataType::Float64)
+ }
+
+ // Don't override return_field() - let the default handle nullability
+ // The default will make the output nullable if any input is nullable
+
+ fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn
Accumulator>> {
+ // Implementation...
+ # unimplemented!()
+ }
+
+ // ... other required methods
+}
+```
+
+### Custom Nullability (Advanced)
+
+If your function has special nullability semantics, you can override
`return_field()`:
+
+```rust
+use std::sync::Arc;
+use arrow::datatypes::Field;
+
+impl AggregateUDFImpl for MyAggregateFunction {
+ // ... other methods ...
+
+ fn return_field(&self, arg_fields: &[FieldRef]) -> Result<FieldRef> {
+ let arg_types: Vec<_> = arg_fields
+ .iter()
+ .map(|f| f.data_type())
+ .cloned()
+ .collect();
+ let data_type = self.return_type(&arg_types)?;
+
+ // Example: COUNT always returns non-nullable i64, regardless of input
+ let is_nullable = false; // COUNT never returns NULL
+
+ Ok(Arc::new(Field::new(self.name(), data_type, is_nullable)))
+ }
+}
+```
+
+## Migration Guide
+
+If you have existing code that uses `is_nullable()`, here's how to migrate:
+
+### Before (Deprecated)
+
+```rust
+impl AggregateUDFImpl for MyFunction {
+ fn is_nullable(&self) -> bool {
+ // Only returns true or false, independent of inputs
+ true
+ }
+}
+```
+
+### After (Recommended)
+
+**Option 1: Use the default (simplest)**
+
+```rust
+impl AggregateUDFImpl for MyFunction {
+ // Remove is_nullable() entirely
+ // The default return_field() will compute nullability from inputs
+}
+```
+
+**Option 2: Override return_field() for custom logic**
+
+```rust
+impl AggregateUDFImpl for MyFunction {
+ fn return_field(&self, arg_fields: &[FieldRef]) -> Result<FieldRef> {
+ let arg_types: Vec<_> = arg_fields
+ .iter()
+ .map(|f| f.data_type())
+ .cloned()
+ .collect();
+ let data_type = self.return_type(&arg_types)?;
+
+ // Your custom nullability logic here
+ let is_nullable = arg_fields.iter().any(|f| f.is_nullable());
+
+ Ok(Arc::new(Field::new(self.name(), data_type, is_nullable)))
+ }
+}
+```
+
+## Examples
+
+### Example 1: MIN Function
+
+`MIN` returns the smallest value from a group. It should be nullable if and
only if its input is nullable:
+
+```rust
+impl AggregateUDFImpl for MinFunction {
+ fn signature(&self) -> &Signature {
+ &self.signature
+ }
+
+ fn return_type(&self, arg_types: &[DataType]) -> Result<DataType> {
+ Ok(arg_types[0].clone())
+ }
+
+ // No need to override return_field() - the default handles it correctly:
+ // - If input is nullable, output is nullable
+ // - If input is non-nullable, output is non-nullable
+
+ fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn
Accumulator>> {
+ // Implementation...
+ }
+}
+```
+
+### Example 2: COUNT Function
+
+`COUNT` always returns a non-nullable integer, regardless of input nullability:
+
+```rust
+impl AggregateUDFImpl for CountFunction {
+ fn signature(&self) -> &Signature {
+ &self.signature
+ }
+
+ fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
+ Ok(DataType::Int64)
+ }
+
+ // Override return_field to always return non-nullable
+ fn return_field(&self, _arg_fields: &[FieldRef]) -> Result<FieldRef> {
+ Ok(Arc::new(Field::new(self.name(), DataType::Int64, false)))
+ }
+
+ fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn
Accumulator>> {
+ // Implementation...
+ }
+}
+```
+
+## Deprecation Timeline
+
+- **Deprecated in v42.0.0**: The `is_nullable()` method in `AggregateUDFImpl`
is now deprecated
+- **Will be removed in a future version**: `is_nullable()` will be removed
entirely
+
+During the deprecation period, both `is_nullable()` and the new
`return_field()` approach work, but new code should use `return_field()`.
+
+## Troubleshooting
+
+### Issue: "Function `X` uses deprecated `is_nullable()`"
+
+**Solution**: Remove the `is_nullable()` implementation and let the default
`return_field()` handle nullability, or override `return_field()` directly.
+
+### Issue: "Output field nullability is incorrect"
+
+**Check**:
+1. Are your input fields correctly marked as nullable/non-nullable?
+2. Does your function need custom nullability logic? If so, override
`return_field()`.
+
+### Issue: "Tests fail with null value where non-null expected"
+
+**Check**:
+1. Verify that your function's accumulator actually returns a non-null default
value when the input is empty and your function declares non-nullable output
+2. Override `return_field()` to adjust the nullability if needed
+
+## See Also
+
+- [Adding User Defined Functions](adding-udfs.md) - General guide to
implementing UDFs
+- [Scalar UDF Nullability](#) - Similar concepts for scalar UDFs (which
already use `return_field_from_args()`)
Review Comment:
A link target is missing here
##########
datafusion/expr/src/udaf.rs:
##########
@@ -1424,4 +1458,71 @@ mod test {
value.hash(hasher);
hasher.finish()
}
+
+ #[test]
+ fn test_return_field_nullability_from_nullable_input() {
+ // Test that return_field derives nullability from input field
nullability
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a nullable input field
+ let nullable_field = Arc::new(Field::new("col", DataType::Float64,
true));
+ let return_field = a.return_field(&[nullable_field]).unwrap();
+
+ // When input is nullable, output should be nullable
+ assert!(return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_from_non_nullable_input() {
+ // Test that return_field respects non-nullable input fields
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // Create a non-nullable input field
+ let non_nullable_field = Arc::new(Field::new("col", DataType::Float64,
false));
+ let return_field = a.return_field(&[non_nullable_field]).unwrap();
+
+ // When input is non-nullable, output should also be non-nullable
+ assert!(!return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_nullability_with_mixed_inputs() {
+ // Test that return_field is nullable if ANY input is nullable
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ // With multiple inputs (typical for aggregates in more complex
scenarios)
+ let nullable_field = Arc::new(Field::new("col1", DataType::Float64,
true));
+ let non_nullable_field = Arc::new(Field::new("col2",
DataType::Float64, false));
+
+ let return_field = a.return_field(&[non_nullable_field,
nullable_field]).unwrap();
+
+ // If ANY input is nullable, result should be nullable
+ assert!(return_field.is_nullable());
+ }
+
+ #[test]
+ fn test_return_field_preserves_return_type() {
+ // Test that return_field correctly preserves the return type
+ use arrow::datatypes::Field;
+ use std::sync::Arc;
+
+ let a = AggregateUDF::from(AMeanUdf::new());
+
+ let nullable_field = Arc::new(Field::new("col", DataType::Float64,
true));
+ let return_field = a.return_field(&[nullable_field]).unwrap();
Review Comment:
```suggestion
let return_field = udf.return_field(&[nullable_field]).unwrap();
```
--
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]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]