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