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 0f8414c0bf [CALCITE-7834] Type inferred for LAG/LEAD is not always 
nullable
0f8414c0bf is described below

commit 0f8414c0bfda63930254103ee700b76bf200724a
Author: Mihai Budiu <[email protected]>
AuthorDate: Sun Oct 4 18:26:28 2026 -0700

    [CALCITE-7834] Type inferred for LAG/LEAD is not always nullable
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../calcite/sql/fun/SqlLeadLagAggFunction.java     |  5 +-
 .../java/org/apache/calcite/test/JdbcTest.java     |  2 +-
 core/src/test/resources/sql/winagg.iq              | 59 ++++++++++++++++++++++
 3 files changed, 63 insertions(+), 3 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java 
b/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
index fb1c24ff0d..3f11218fda 100644
--- a/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
+++ b/core/src/main/java/org/apache/calcite/sql/fun/SqlLeadLagAggFunction.java
@@ -87,11 +87,12 @@ public SqlLeadLagAggFunction(boolean isLead) {
     this(isLead ? SqlKind.LEAD : SqlKind.LAG);
   }
 
-  // Result is NOT NULL if NOT NULL default value is provided
+  // Result is NOT NULL only if a default value is given and no operand is 
nullable
   private static RelDataType transformType(SqlOperatorBinding binding,
       RelDataType type) {
     SqlTypeTransform transform =
-        binding.getOperandCount() < 3 || binding.getOperandType(2).isNullable()
+        binding.getOperandCount() < 3
+            || 
binding.collectOperandTypes().stream().anyMatch(RelDataType::isNullable)
             ? SqlTypeTransforms.FORCE_NULLABLE
             : SqlTypeTransforms.TO_NOT_NULLABLE;
     return transform.transformType(binding, type);
diff --git a/core/src/test/java/org/apache/calcite/test/JdbcTest.java 
b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
index 0109d3f954..d975c5a562 100644
--- a/core/src/test/java/org/apache/calcite/test/JdbcTest.java
+++ b/core/src/test/java/org/apache/calcite/test/JdbcTest.java
@@ -4760,7 +4760,7 @@ void testOrderByOnSortedTable2(String format) {
             + "   \"hr\".\"emps\"\n"
             + " )\n")
         .typeIs(
-            "[LAGX INTEGER NOT NULL]")
+            "[LAGX INTEGER]")
         .returnsUnordered(
             "LAGX=0",
             "LAGX=0",
diff --git a/core/src/test/resources/sql/winagg.iq 
b/core/src/test/resources/sql/winagg.iq
index 768d73fa16..36a6d88247 100644
--- a/core/src/test/resources/sql/winagg.iq
+++ b/core/src/test/resources/sql/winagg.iq
@@ -1523,4 +1523,63 @@ from (values (1, 10), (2, cast(null as integer)), (3, 
30)) as t(o, v);
 
 !ok
 
+# Test case for [CALCITE-7834] Type inferred for LAG/LEAD is not always 
nullable
+# Validated on Postgres.
+SELECT y, x, LAG(x, 1, 0) OVER (ORDER BY y) AS l
+FROM (VALUES (1, 10), (2, CAST(NULL AS INTEGER)), (3, 30)) AS t(y, x);
++---+----+----+
+| Y | X  | L  |
++---+----+----+
+| 1 | 10 |  0 |
+| 2 |    | 10 |
+| 3 | 30 |    |
++---+----+----+
+(3 rows)
+
+!ok
+Y INTEGER(10) NOT NULL
+X INTEGER(10)
+L INTEGER(10)
+!type
+
+# Offset 0. Validated on Postgres.
+SELECT y, x, LEAD(x, 0, 0) OVER (ORDER BY y) AS l
+FROM (VALUES (1, 10), (2, CAST(NULL AS INTEGER)), (3, 30)) AS t(y, x);
++---+----+----+
+| Y | X  | L  |
++---+----+----+
+| 1 | 10 | 10 |
+| 2 |    |    |
+| 3 | 30 | 30 |
++---+----+----+
+(3 rows)
+
+!ok
+
+# Validated on Postgres.
+SELECT y, x, LAG(x, 1, 'z') OVER (ORDER BY y) AS l
+FROM (VALUES (1, 'a'), (2, CAST(NULL AS CHAR(1))), (3, 'c')) AS t(y, x);
++---+---+---+
+| Y | X | L |
++---+---+---+
+| 1 | a | z |
+| 2 |   | a |
+| 3 | c |   |
++---+---+---+
+(3 rows)
+
+!ok
+Y INTEGER(10) NOT NULL
+X CHAR(1)
+L VARCHAR
+!type
+
+# A NULL offset gives NULL, so the result type is nullable.  Validated on 
Postgres.
+SELECT y, x, LAG(x, o, 0) OVER (ORDER BY y) AS l
+FROM (VALUES (1, 10, 1), (2, 20, CAST(NULL AS INTEGER)), (3, 30, 1)) AS t(y, 
x, o);
+Y INTEGER(10) NOT NULL
+X INTEGER(10) NOT NULL
+L INTEGER(10)
+!type
+
 # End winagg.iq

Reply via email to