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 ae7e2e042c [CALCITE-7705] LISTAGG result type is never nullable
ae7e2e042c is described below

commit ae7e2e042c326b9e37b85ee7f3df608cd9a6ee8f
Author: Mihai Budiu <[email protected]>
AuthorDate: Mon Aug 10 16:21:57 2026 -0700

    [CALCITE-7705] LISTAGG result type is never nullable
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../calcite/sql/fun/SqlLibraryOperators.java       |  4 ++--
 .../calcite/sql/fun/SqlStdOperatorTable.java       |  3 ++-
 core/src/test/resources/sql/agg.iq                 | 24 ++++++++++++++++++++++
 .../org/apache/calcite/test/SqlOperatorTest.java   | 15 ++++++++++++++
 4 files changed, 43 insertions(+), 3 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
index 6e328261c6..aef50bbc63 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLibraryOperators.java
@@ -846,7 +846,7 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding 
operatorBinding,
   @LibraryOperator(libraries = {BIG_QUERY, POSTGRESQL}, exceptLibraries = 
{REDSHIFT})
   public static final SqlAggFunction STRING_AGG =
       SqlBasicAggFunction
-          .create(SqlKind.STRING_AGG, ReturnTypes.ARG0_NULLABLE,
+          .create(SqlKind.STRING_AGG, ReturnTypes.ARG0_NULLABLE_IF_EMPTY,
               OperandTypes.STRING.or(OperandTypes.STRING_STRING))
           .withFunctionType(SqlFunctionCategory.SYSTEM)
           .withSyntax(SqlSyntax.ORDERED_FUNCTION);
@@ -862,7 +862,7 @@ static RelDataType deriveTypeSplit(SqlOperatorBinding 
operatorBinding,
       SqlBasicAggFunction
           .create(SqlKind.GROUP_CONCAT,
               ReturnTypes.andThen(ReturnTypes::stripOrderBy,
-                  ReturnTypes.ARG0_NULLABLE),
+                  ReturnTypes.ARG0_NULLABLE_IF_EMPTY),
               OperandTypes.STRING.or(OperandTypes.STRING_STRING))
           .withFunctionType(SqlFunctionCategory.SYSTEM)
           .withAllowsNullTreatment(false)
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 be9f23abe6..ef7240d35e 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
@@ -2540,7 +2540,8 @@ public class SqlStdOperatorTable extends 
ReflectiveSqlOperatorTable {
    * The LISTAGG operator. String aggregator function.
    */
   public static final SqlAggFunction LISTAGG =
-      new SqlListaggAggFunction(SqlKind.LISTAGG, ReturnTypes.ARG0_NULLABLE);
+      new SqlListaggAggFunction(SqlKind.LISTAGG,
+          ReturnTypes.ARG0_NULLABLE_IF_EMPTY);
 
   /**
    * The FUSION operator. Multiset aggregator function.
diff --git a/core/src/test/resources/sql/agg.iq 
b/core/src/test/resources/sql/agg.iq
index cbd50747b3..d6557e34f1 100644
--- a/core/src/test/resources/sql/agg.iq
+++ b/core/src/test/resources/sql/agg.iq
@@ -3380,6 +3380,30 @@ select listagg(ename) as combined_name from emp;
 
 !ok
 
+# [CALCITE-7705] LISTAGG result type is never nullable
+# Empty input yields NULL even though the argument is NOT NULL.
+select listagg(v, ',') as r from (values ('a')) as t(v) where false;
++---+
+| R |
++---+
+|   |
++---+
+(1 row)
+
+!ok
+
+# The IS NULL test must not be simplified away based on the result type.
+select r is null as n from (
+  select listagg(v, ',') as r from (values ('a')) as t(v) where false);
++------+
+| N    |
++------+
+| true |
++------+
+(1 row)
+
+!ok
+
 select listagg(ename) within group(order by gender, ename) as combined_name 
from emp;
 +-------------------------------------------------------+
 | COMBINED_NAME                                         |
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 cdae823649..c6be7efedf 100644
--- a/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
+++ b/testkit/src/main/java/org/apache/calcite/test/SqlOperatorTest.java
@@ -12935,6 +12935,14 @@ private static void checkDecodeFunc(SqlOperatorFixture 
f) {
         false);
     f.checkAggType("listagg('test')", "CHAR(4) NOT NULL");
     f.checkAggType("listagg('test', ', ')", "CHAR(4) NOT NULL");
+    // Test case for [CALCITE-7705]
+    // LISTAGG result type is never nullable
+    // Nullable without GROUP BY even for a non-nullable argument, since the
+    // input may be empty
+    f.checkColumnType("select listagg('test') from (values (1))", "CHAR(4)");
+    // A FILTER clause may exclude all rows, so the result is nullable
+    f.checkColumnType("select listagg('test') filter (where x > 1) "
+        + "from (values (1)) as t(x) group by x", "CHAR(4)");
     final String[] values1 = {"'hello'", "CAST(null AS CHAR)", "'world'", 
"'!'"};
     f.checkAgg("listagg(x)", values1, isSingle("hello,world,!    "));
     final String[] values2 = {"0", "1", "2", "3"};
@@ -12950,6 +12958,10 @@ private static void checkDecodeFunc(SqlOperatorFixture 
f) {
 
   private static void checkStringAggFunc(SqlOperatorFixture f) {
     final String[] values = {"'x'", "null", "'yz'"};
+    // Test case for [CALCITE-7705]
+    // LISTAGG result type is never nullable
+    f.checkColumnType("select string_agg('x', ',') from (values (1))",
+        "CHAR(1)");
     f.checkAgg("string_agg(x)", values, isSingle("x ,yz"));
     f.checkAgg("string_agg(x,':')", values, isSingle("x :yz"));
     f.checkAgg("string_agg(x,':' order by x)", values, isSingle("x :yz"));
@@ -13008,6 +13020,9 @@ private static void 
checkStringAggFuncFails(SqlOperatorFixture f) {
 
   private static void checkGroupConcatFunc(SqlOperatorFixture f) {
     final String[] values = {"'x'", "null", "'yz'"};
+    // Test case for [CALCITE-7705]
+    // LISTAGG result type is never nullable
+    f.checkColumnType("select group_concat('x') from (values (1))", "CHAR(1)");
     f.checkAgg("group_concat(x)", values, isSingle("x ,yz"));
     f.checkAgg("group_concat(x,':')", values, isSingle("x :yz"));
     f.checkAgg("group_concat(x,':' order by x)", values, isSingle("x :yz"));

Reply via email to