This is an automated email from the ASF dual-hosted git repository.

github-merge-queue[bot] pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datafusion.git


The following commit(s) were added to refs/heads/main by this push:
     new 67947b6b90 Add any_value aggregate function (#23043)
67947b6b90 is described below

commit 67947b6b90c891843eb167da2fd3e562587834c1
Author: Yin Li <[email protected]>
AuthorDate: Sat Jul 18 21:26:49 2026 +0800

    Add any_value aggregate function (#23043)
    
    ## Which issue does this PR close?
    
    - Closes #22799.
    
    ## Rationale for this change
    
    `any_value` is a common aggregate in SQL engines for queries that need
    one representative non-null value from each group without imposing an
    ordering requirement. DataFusion currently has `first_value`, but that
    aggregate is order-sensitive, so exposing `any_value` gives users the
    intended arbitrary-value semantics directly.
    
    ## What changes are included in this PR?
    
    - Adds an `any_value(expression)` aggregate UDF and registers it with
    the default aggregate functions.
    - Reuses the existing trivial first-value accumulator with nulls
    ignored, so evaluation short-circuits after the first non-null value.
    - Marks the aggregate as order-insensitive and preserves the input field
    metadata/type in the return field.
    - Adds sqllogictest coverage for scalar, grouped, all-null, empty-input,
    and string return-type cases.
    
    ## Are these changes tested?
    
    Yes. I ran:
    
    ```
    cargo fmt --all
    cargo test -p datafusion-functions-aggregate
    cargo test -p datafusion-sqllogictest --test sqllogictests -- 
aggregate_any_value.slt
    cargo clippy --all-targets --all-features -- -D warnings
    ```
    
    ## Are there any user-facing changes?
    
    Yes. This adds a new SQL aggregate function, `any_value`.
    
    I used AI assistance to help inspect the codebase and run validation,
    and I reviewed the resulting implementation and tests.
    
    ---------
    
    Signed-off-by: Yin Li <[email protected]>
    Co-authored-by: Kevin-Li-2025 <[email protected]>
    Co-authored-by: Jeffrey Vo <[email protected]>
---
 datafusion/functions-aggregate/src/any_value.rs    | 125 +++++++++++++++++++++
 datafusion/functions-aggregate/src/lib.rs          |   3 +
 .../test_files/aggregate_any_value.slt             |  57 ++++++++++
 docs/source/user-guide/sql/aggregate_functions.md  |  24 ++++
 4 files changed, 209 insertions(+)

diff --git a/datafusion/functions-aggregate/src/any_value.rs 
b/datafusion/functions-aggregate/src/any_value.rs
new file mode 100644
index 0000000000..dc3bd23d80
--- /dev/null
+++ b/datafusion/functions-aggregate/src/any_value.rs
@@ -0,0 +1,125 @@
+// 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.
+
+//! Defines the ANY_VALUE aggregation.
+
+use std::fmt::Debug;
+use std::hash::Hash;
+use std::sync::Arc;
+
+use arrow::datatypes::{DataType, Field, FieldRef};
+use datafusion_common::{Result, not_impl_err};
+use datafusion_expr::function::{AccumulatorArgs, StateFieldsArgs};
+use datafusion_expr::utils::{AggregateOrderSensitivity, format_state_name};
+use datafusion_expr::{
+    Accumulator, AggregateUDFImpl, Documentation, Signature, Volatility,
+};
+use datafusion_macros::user_doc;
+
+use crate::first_last::TrivialFirstValueAccumulator;
+
+make_udaf_expr_and_func!(
+    AnyValue,
+    any_value,
+    expression,
+    "Returns an arbitrary non-null value",
+    any_value_udaf
+);
+
+#[user_doc(
+    doc_section(label = "General Functions"),
+    description = "Returns an arbitrary non-null value from a group, or NULL 
if the group contains only NULL values.",
+    syntax_example = "any_value(expression)",
+    sql_example = r#"```sql
+> SELECT any_value(column_name) FROM table_name;
++------------------------+
+| any_value(column_name) |
++------------------------+
+| arbitrary_value        |
++------------------------+
+```"#,
+    standard_argument(name = "expression",)
+)]
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub struct AnyValue {
+    signature: Signature,
+}
+
+impl Default for AnyValue {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+impl AnyValue {
+    pub fn new() -> Self {
+        Self {
+            signature: Signature::any(1, Volatility::Immutable),
+        }
+    }
+}
+
+impl AggregateUDFImpl for AnyValue {
+    fn name(&self) -> &str {
+        "any_value"
+    }
+
+    fn signature(&self) -> &Signature {
+        &self.signature
+    }
+
+    fn return_type(&self, _arg_types: &[DataType]) -> Result<DataType> {
+        not_impl_err!("Not called because return_field is implemented")
+    }
+
+    fn return_field(&self, arg_fields: &[FieldRef]) -> Result<FieldRef> {
+        Ok(Arc::new(
+            Field::new(self.name(), arg_fields[0].data_type().clone(), true)
+                .with_metadata(arg_fields[0].metadata().clone()),
+        ))
+    }
+
+    fn accumulator(&self, acc_args: AccumulatorArgs) -> Result<Box<dyn 
Accumulator>> {
+        
TrivialFirstValueAccumulator::try_new(acc_args.return_field.data_type(), true)
+            .map(|acc| Box::new(acc) as _)
+    }
+
+    fn state_fields(&self, args: StateFieldsArgs) -> Result<Vec<FieldRef>> {
+        Ok(vec![
+            Field::new(
+                format_state_name(args.name, "any_value"),
+                args.return_type().clone(),
+                true,
+            )
+            .into(),
+            Field::new(
+                format_state_name(args.name, "any_value_is_set"),
+                DataType::Boolean,
+                true,
+            )
+            .into(),
+        ])
+    }
+
+    fn order_sensitivity(&self) -> AggregateOrderSensitivity {
+        AggregateOrderSensitivity::Insensitive
+    }
+
+    fn documentation(&self) -> Option<&Documentation> {
+        self.doc()
+    }
+}
diff --git a/datafusion/functions-aggregate/src/lib.rs 
b/datafusion/functions-aggregate/src/lib.rs
index 1b9996220d..e3f2714abb 100644
--- a/datafusion/functions-aggregate/src/lib.rs
+++ b/datafusion/functions-aggregate/src/lib.rs
@@ -65,6 +65,7 @@
 #[macro_use]
 pub mod macros;
 
+pub mod any_value;
 pub mod approx_distinct;
 pub mod approx_median;
 pub mod approx_percentile_cont;
@@ -102,6 +103,7 @@ use std::sync::Arc;
 
 /// Fluent-style API for creating `Expr`s
 pub mod expr_fn {
+    pub use super::any_value::any_value;
     pub use super::approx_distinct::approx_distinct;
     pub use super::approx_median::approx_median;
     pub use super::approx_percentile_cont::approx_percentile_cont;
@@ -147,6 +149,7 @@ pub mod expr_fn {
 /// Returns all default aggregate functions
 pub fn all_default_aggregate_functions() -> Vec<Arc<AggregateUDF>> {
     vec![
+        any_value::any_value_udaf(),
         array_agg::array_agg_udaf(),
         first_last::first_value_udaf(),
         first_last::last_value_udaf(),
diff --git a/datafusion/sqllogictest/test_files/aggregate_any_value.slt 
b/datafusion/sqllogictest/test_files/aggregate_any_value.slt
new file mode 100644
index 0000000000..3fe6f787d3
--- /dev/null
+++ b/datafusion/sqllogictest/test_files/aggregate_any_value.slt
@@ -0,0 +1,57 @@
+# 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.
+
+statement ok
+CREATE TABLE any_value_test AS VALUES
+  (1, NULL, NULL),
+  (1, 10, 'first'),
+  (1, 20, 'second'),
+  (2, NULL, NULL),
+  (2, NULL, NULL),
+  (3, 30, 'third');
+
+query B
+SELECT any_value(column2) IN (10, 20) FROM any_value_test;
+----
+true
+
+query IBB rowsort
+SELECT
+  column1,
+  any_value(column2) IN (10, 20, 30),
+  any_value(column3) IN ('first', 'second', 'third')
+FROM any_value_test
+GROUP BY column1;
+----
+1 true true
+2 NULL NULL
+3 true true
+
+query T
+SELECT arrow_typeof(any_value(column3)) FROM any_value_test;
+----
+Utf8
+
+query I
+SELECT any_value(column2) FROM any_value_test WHERE false;
+----
+NULL
+
+query I
+SELECT any_value(column2) FROM any_value_test WHERE column1 = 2;
+----
+NULL
diff --git a/docs/source/user-guide/sql/aggregate_functions.md 
b/docs/source/user-guide/sql/aggregate_functions.md
index ba9c6ae124..c681ccb28e 100644
--- a/docs/source/user-guide/sql/aggregate_functions.md
+++ b/docs/source/user-guide/sql/aggregate_functions.md
@@ -80,6 +80,7 @@ SELECT SUM(x) WITHIN GROUP (ORDER BY x) FROM t;
 
 ## General Functions
 
+- [any_value](#any_value)
 - [array_agg](#array_agg)
 - [avg](#avg)
 - [bit_and](#bit_and)
@@ -105,6 +106,29 @@ SELECT SUM(x) WITHIN GROUP (ORDER BY x) FROM t;
 - [var_samp](#var_samp)
 - [var_sample](#var_sample)
 
+### `any_value`
+
+Returns an arbitrary non-null value from a group, or NULL if the group 
contains only NULL values.
+
+```sql
+any_value(expression)
+```
+
+#### Arguments
+
+- **expression**: The expression to operate on. Can be a constant, column, or 
function, and any combination of operators.
+
+#### Example
+
+```sql
+> SELECT any_value(column_name) FROM table_name;
++------------------------+
+| any_value(column_name) |
++------------------------+
+| arbitrary_value        |
++------------------------+
+```
+
 ### `array_agg`
 
 Returns an array created from the expression elements. If ordering is 
required, elements are inserted in the specified order.


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to