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

mihaibudiu 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 018de2e8a3 [CALCITE-7706] ARG_MIN ignores nullability of second 
argument
018de2e8a3 is described below

commit 018de2e8a3e47a8f6d173c64ef928cefefd725e9
Author: Mihai Budiu <[email protected]>
AuthorDate: Mon Aug 10 16:05:18 2026 -0700

    [CALCITE-7706] ARG_MIN ignores nullability of second argument
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../calcite/sql/fun/SqlStdOperatorTable.java       |  9 +++++--
 core/src/test/resources/sql/agg.iq                 | 30 ++++++++++++++++++++++
 .../org/apache/calcite/test/SqlOperatorTest.java   | 15 +++++++++++
 3 files changed, 52 insertions(+), 2 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
index ef7240d35e..d50bc7299e 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlStdOperatorTable.java
@@ -68,6 +68,7 @@
 import org.apache.calcite.sql.type.SqlReturnTypeInference;
 import org.apache.calcite.sql.type.SqlTypeFamily;
 import org.apache.calcite.sql.type.SqlTypeName;
+import org.apache.calcite.sql.type.SqlTypeTransforms;
 import org.apache.calcite.sql.util.ReflectiveSqlOperatorTable;
 import org.apache.calcite.sql.validate.SqlConformance;
 import org.apache.calcite.sql.validate.SqlConformanceEnum;
@@ -1125,7 +1126,9 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
    */
   public static final SqlBasicAggFunction ARG_MAX =
       SqlBasicAggFunction.create("ARG_MAX", SqlKind.ARG_MAX,
-          ReturnTypes.ARG0_NULLABLE_IF_EMPTY, OperandTypes.ANY_COMPARABLE)
+          ReturnTypes.ARG0_NULLABLE_IF_EMPTY
+              .andThen(SqlTypeTransforms.TO_NULLABLE),
+          OperandTypes.ANY_COMPARABLE)
           .withGroupOrder(Optionality.FORBIDDEN)
           .withFunctionType(SqlFunctionCategory.SYSTEM);
 
@@ -1134,7 +1137,9 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
    */
   public static final SqlBasicAggFunction ARG_MIN =
       SqlBasicAggFunction.create("ARG_MIN", SqlKind.ARG_MIN,
-              ReturnTypes.ARG0_NULLABLE_IF_EMPTY, OperandTypes.ANY_COMPARABLE)
+          ReturnTypes.ARG0_NULLABLE_IF_EMPTY
+              .andThen(SqlTypeTransforms.TO_NULLABLE),
+          OperandTypes.ANY_COMPARABLE)
           .withGroupOrder(Optionality.FORBIDDEN)
           .withFunctionType(SqlFunctionCategory.SYSTEM);
 
diff --git a/core/src/test/resources/sql/agg.iq 
b/core/src/test/resources/sql/agg.iq
index bc24ba6014..41f2436a25 100644
--- a/core/src/test/resources/sql/agg.iq
+++ b/core/src/test/resources/sql/agg.iq
@@ -3866,6 +3866,36 @@ group by deptno;
 
 !ok
 
+# [CALCITE-7706] ARG_MIN ignores nullability of second argument
+# Rows whose comparator is NULL are skipped, so a group where every
+# comparator value is NULL yields NULL even though the value argument
+# is NOT NULL. ARG_MAX behaves the same.
+select g, arg_min(v, c) as mi, arg_max(v, c) as ma
+from (values (1, 10, cast(null as integer))) as t(g, v, c)
+group by g;
++---+----+----+
+| G | MI | MA |
++---+----+----+
+| 1 |    |    |
++---+----+----+
+(1 row)
+
+!ok
+
+# The IS NULL test must not be simplified away based on the result type.
+select mi is null as n1, ma is null as n2 from (
+  select g, arg_min(v, c) as mi, arg_max(v, c) as ma
+  from (values (1, 10, cast(null as integer))) as t(g, v, c)
+  group by g);
++------+------+
+| N1   | N2   |
++------+------+
+| true | true |
++------+------+
+(1 row)
+
+!ok
+
 # ARG_MIN, ARG_MAX applied to an integer.
 select arg_min(deptno, empno) as mi,
   arg_max(deptno, empno) as ma,
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 c6be7efedf..9473246f62 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -18138,11 +18138,26 @@ void checkBitOr(SqlOperatorFixture f0, FunctionAlias 
functionAlias) {
     final Consumer<SqlOperatorFixture> consumer = f -> {
       f.checkAgg("arg_min(mod(x, 3), x)", xValues, isSingle("2"));
       f.checkAgg("arg_max(mod(x, 3), x)", xValues, isSingle("1"));
+      f.checkAggType("arg_min(1, 2)", "INTEGER NOT NULL");
+      f.checkAggType("arg_max(1, 2)", "INTEGER NOT NULL");
+      // Test cases for [CALCITE-7706]
+      // ARG_MIN ignores nullability of second argument
+      f.checkAggType("arg_min(1, cast(null as integer))", "INTEGER");
+      f.checkAggType("arg_max(1, cast(null as integer))", "INTEGER");
+      f.checkAggType("arg_min(cast(null as integer), 2)", "INTEGER");
+      f.checkAggType("arg_max(cast(null as integer), 2)", "INTEGER");
+      // 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");
     };
 
     final Consumer<SqlOperatorFixture> consumer2 = f -> {
       f.checkAgg("min_by(mod(x, 3), x)", xValues, isSingle("2"));
       f.checkAgg("max_by(mod(x, 3), x)", xValues, isSingle("1"));
+      // Test cases for [CALCITE-7706]
+      // 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");
     };
 
     consumer.accept(f0);

Reply via email to