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

xiedeyantu pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/calcite.git


The following commit(s) were added to refs/heads/main by this push:
     new 7b1650e31c [CALCITE-7862] ARG_MIN and ARG_MAX should support ARRAY, 
MAP and ROW keys
7b1650e31c is described below

commit 7b1650e31ca1770d2d3e049f32f9044687012595
Author: Alan Jin <[email protected]>
AuthorDate: Fri Oct 16 06:33:21 2026 +0000

    [CALCITE-7862] ARG_MIN and ARG_MAX should support ARRAY, MAP and ROW keys
---
 .../org/apache/calcite/runtime/SqlFunctions.java   | 42 ++++++++++++++++++++++
 core/src/test/resources/sql/agg.iq                 | 37 +++++++++++++++++++
 .../org/apache/calcite/test/SqlOperatorTest.java   |  7 ++++
 3 files changed, 86 insertions(+)

diff --git a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java 
b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
index 1c7159dedd..58e68cdb24 100644
--- a/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
+++ b/core/src/main/java/org/apache/calcite/runtime/SqlFunctions.java
@@ -2570,6 +2570,27 @@ public static <T extends Comparable<T>> boolean 
ltNullable(T b0, T b1) {
     return b1 == null || b0 != null && b0.compareTo(b1) < 0;
   }
 
+  /** Returns whether {@code b0} is less than {@code b1}
+   * (or {@code b1} is null). Helper for {@code ARG_MIN} on ARRAY keys. */
+  public static boolean ltNullable(@Nullable List<?> b0,
+      @Nullable List<?> b1) {
+    return b1 == null || b0 != null && lt(b0, b1);
+  }
+
+  /** Returns whether {@code b0} is less than {@code b1}
+   * (or {@code b1} is null). Helper for {@code ARG_MIN} on MAP keys. */
+  public static boolean ltNullable(@Nullable Map<?, ?> b0,
+      @Nullable Map<?, ?> b1) {
+    return b1 == null || b0 != null && lt(b0, b1);
+  }
+
+  /** Returns whether {@code b0} is less than {@code b1}
+   * (or {@code b1} is null). Helper for {@code ARG_MIN} on ROW keys. */
+  public static boolean ltNullable(@Nullable Object @Nullable [] b0,
+      @Nullable Object @Nullable [] b1) {
+    return b1 == null || b0 != null && lt(b0, b1);
+  }
+
   public static boolean lt(byte b0, byte b1) {
     return b0 < b1;
   }
@@ -2706,6 +2727,27 @@ public static <T extends Comparable<T>> boolean 
gtNullable(T b0, T b1) {
     return b1 == null || b0 != null && b0.compareTo(b1) > 0;
   }
 
+  /** Returns whether {@code b0} is greater than {@code b1}
+   * (or {@code b1} is null). Helper for {@code ARG_MAX} on ARRAY keys. */
+  public static boolean gtNullable(@Nullable List<?> b0,
+      @Nullable List<?> b1) {
+    return b1 == null || b0 != null && gt(b0, b1);
+  }
+
+  /** Returns whether {@code b0} is greater than {@code b1}
+   * (or {@code b1} is null). Helper for {@code ARG_MAX} on MAP keys. */
+  public static boolean gtNullable(@Nullable Map<?, ?> b0,
+      @Nullable Map<?, ?> b1) {
+    return b1 == null || b0 != null && gt(b0, b1);
+  }
+
+  /** Returns whether {@code b0} is greater than {@code b1}
+   * (or {@code b1} is null). Helper for {@code ARG_MAX} on ROW keys. */
+  public static boolean gtNullable(@Nullable Object @Nullable [] b0,
+      @Nullable Object @Nullable [] b1) {
+    return b1 == null || b0 != null && gt(b0, b1);
+  }
+
   public static boolean gt(byte b0, byte b1) {
     return b0 > b1;
   }
diff --git a/core/src/test/resources/sql/agg.iq 
b/core/src/test/resources/sql/agg.iq
index 55fd016f03..2b035304b8 100644
--- a/core/src/test/resources/sql/agg.iq
+++ b/core/src/test/resources/sql/agg.iq
@@ -3910,6 +3910,43 @@ from emp;
 
 !ok
 
+# [CALCITE-7862] ARG_MIN and ARG_MAX fail with "Unable to implement" when
+# the key is an ARRAY, MAP or ROW.
+# Keys compare as in MIN and MAX; a row whose key is NULL is skipped.
+select arg_min(x, v) as mi, arg_max(x, v) as ma, min(v) as mn, max(v) as mx
+from (values (1, array[3, 1]), (2, array[1, 5]), (3, array[1, 4]),
+    (4, null)) as t(x, v);
++----+----+--------+--------+
+| MI | MA | MN     | MX     |
++----+----+--------+--------+
+|  3 |  1 | [1, 4] | [3, 1] |
++----+----+--------+--------+
+(1 row)
+
+!ok
+
+select arg_min(x, v) as mi, arg_max(x, v) as ma, min(v) as mn, max(v) as mx
+from (values (1, map['a', 2]), (2, map['a', 1]), (3, map['b', 0])) as t(x, v);
++----+----+-------+-------+
+| MI | MA | MN    | MX    |
++----+----+-------+-------+
+|  2 |  3 | {a=1} | {b=0} |
++----+----+-------+-------+
+(1 row)
+
+!ok
+
+select arg_min(x, v) as mi, arg_max(x, v) as ma, min(v) as mn, max(v) as mx
+from (values (1, row(2, 'b')), (2, row(1, 'a')), (3, row(1, 'c'))) as t(x, v);
++----+----+--------+--------+
+| MI | MA | MN     | MX     |
++----+----+--------+--------+
+|  2 |  1 | {1, a} | {2, b} |
++----+----+--------+--------+
+(1 row)
+
+!ok
+
 # DISTINCT query with ORDER BY on aggregate when there is an implicit cast
 select distinct sum(deptno + '1') as deptsum from dept order by 1;
 +---------+
diff --git a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java 
b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
index 7453fecc40..644a0f92a0 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -18402,6 +18402,10 @@ void checkBitOr(SqlOperatorFixture f0, FunctionAlias 
functionAlias) {
       // Nullable without GROUP BY even for non-nullable arguments, since the
       // input may be empty
       f.checkColumnType("select arg_min(1, 2) from (values (1))", "INTEGER");
+      // Test cases for [CALCITE-7862]
+      // ARG_MIN and ARG_MAX fail when the key is an ARRAY, MAP or ROW
+      f.checkAgg("arg_min(x, array[mod(x, 3), x])", xValues, isSingle("3"));
+      f.checkAgg("arg_max(x, array[mod(x, 3), x])", xValues, isSingle("5"));
     };
 
     final Consumer<SqlOperatorFixture> consumer2 = f -> {
@@ -18411,6 +18415,9 @@ void checkBitOr(SqlOperatorFixture f0, FunctionAlias 
functionAlias) {
       // ARG_MIN ignores nullability of second argument
       f.checkAggType("min_by(1, cast(null as integer))", "INTEGER");
       f.checkAggType("max_by(1, cast(null as integer))", "INTEGER");
+      // Test cases for [CALCITE-7862]
+      f.checkAgg("min_by(x, array[mod(x, 3), x])", xValues, isSingle("3"));
+      f.checkAgg("max_by(x, array[mod(x, 3), x])", xValues, isSingle("5"));
     };
 
     consumer.accept(f0);

Reply via email to