You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@calcite.apache.org by mb...@apache.org on 2026/08/02 17:44:53 UTC
(calcite) 01/02: Stronger validation for JOIN UNNEST
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
commit 0de42de0006b911f107ba31df20edb6b60f00873
Author: Mihai Budiu <mb...@feldera.com>
AuthorDate: Fri Jun 19 00:01:30 2026 -0700
Stronger validation for JOIN UNNEST
Signed-off-by: Mihai Budiu <mb...@feldera.com>
---
.../apache/calcite/runtime/CalciteResource.java | 3 +++
.../calcite/sql/validate/SqlValidatorImpl.java | 30 ++++++++++++++++++++++
.../calcite/runtime/CalciteResource.properties | 1 +
.../org/apache/calcite/test/SqlValidatorTest.java | 24 +++++++++++++++++
4 files changed, 58 insertions(+)
diff --git a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
index c6e1a4dbdc..c5047574a3 100644
--- a/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
+++ b/core/src/main/java/org/apache/calcite/runtime/CalciteResource.java
@@ -337,6 +337,9 @@ ExInst<CalciteException> invalidCompare(String a0, String a1, String a2,
@BaseMessage("Cannot specify condition (NATURAL keyword, or ON or USING clause) following CROSS JOIN")
ExInst<SqlValidatorException> crossJoinDisallowsCondition();
+ @BaseMessage("UNNEST is only supported with INNER, LEFT, CROSS, or COMMA join, not ''{0}''")
+ ExInst<SqlValidatorException> unnestInvalidJoinType(String a0);
+
@BaseMessage("Cannot specify NATURAL keyword with ON or USING clause")
ExInst<SqlValidatorException> naturalDisallowsOnOrUsing();
diff --git a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
index 023de1b77f..7021696b67 100644
--- a/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
+++ b/core/src/main/java/org/apache/calcite/sql/validate/SqlValidatorImpl.java
@@ -4116,6 +4116,20 @@ protected void validateJoin(SqlJoin join, SqlValidatorScope scope) {
}
}
+ // UNNEST on the right side is only meaningful with INNER, LEFT, CROSS, or COMMA.
+ if (isUnnestNode(right)) {
+ switch (joinType) {
+ case INNER:
+ case LEFT:
+ case CROSS:
+ case COMMA:
+ break;
+ default:
+ throw newValidationError(join.getJoinTypeNode(),
+ RESOURCE.unnestInvalidJoinType(joinType.name()));
+ }
+ }
+
// Which join types require/allow a ON/USING condition, or allow
// a NATURAL keyword?
switch (joinType) {
@@ -4192,6 +4206,22 @@ protected void validateJoin(SqlJoin join, SqlValidatorScope scope) {
}
}
+ /**
+ * Returns whether {@code node} is (or wraps, via AS or LATERAL) an
+ * {@code UNNEST} call.
+ */
+ private static boolean isUnnestNode(SqlNode node) {
+ switch (node.getKind()) {
+ case UNNEST:
+ return true;
+ case AS:
+ case LATERAL:
+ return isUnnestNode(((SqlCall) node).operand(0));
+ default:
+ return false;
+ }
+ }
+
/**
* Shuttle which determines whether all SqlCalls that are
* comparisons are comparing columns from both namespaces.
diff --git a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
index f4f16d7326..a90099d7cb 100644
--- a/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
+++ b/core/src/main/resources/org/apache/calcite/runtime/CalciteResource.properties
@@ -115,6 +115,7 @@ AliasListDuplicate=Duplicate name ''{0}'' in column alias list
JoinRequiresCondition=INNER, LEFT, RIGHT, FULL, or ASOF join requires a condition (NATURAL keyword or ON or USING clause)
DisallowsQualifyingCommonColumn=Cannot qualify common column ''{0}''
CrossJoinDisallowsCondition=Cannot specify condition (NATURAL keyword, or ON or USING clause) following CROSS JOIN
+UnnestInvalidJoinType=UNNEST is only supported with INNER, LEFT, CROSS, or COMMA join, not ''{0}''
NaturalDisallowsOnOrUsing=Cannot specify NATURAL keyword with ON or USING clause
ColumnInUsingNotUnique=Column name ''{0}'' in NATURAL join or USING clause is not unique on one side of join
NaturalOrUsingColumnNotCompatible=Column ''{0}'' matched using NATURAL keyword or USING clause has incompatible types: cannot compare ''{1}'' to ''{2}''
diff --git a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
index 8f95099ab9..018c27e898 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -9855,6 +9855,30 @@ void testGroupExpressionEquivalenceParams() {
.fails("Column 'ORDINALITY' not found in any table");
}
+ /** UNNEST is valid with INNER, LEFT, CROSS, and COMMA joins;
+ * all other join kinds must be rejected by the validator. */
+ @Test void testUnnestJoinType() {
+ // Allowed join kinds — these must all validate without error.
+ sql("select * from dept inner join unnest(array[1, 2]) as u(x) on true").ok();
+ sql("select * from dept left join unnest(array[1, 2]) as u(x) on true").ok();
+ sql("select * from dept cross join unnest(array[1, 2]) as u(x)").ok();
+ sql("select * from dept, unnest(array[1, 2]) as u(x)").ok();
+
+ // LATERAL wrapping must also be allowed for valid join kinds.
+ sql("select * from dept cross join lateral unnest(array[1, 2]) as u(x)").ok();
+ sql("select * from dept left join lateral unnest(array[1, 2]) as u(x) on true").ok();
+
+ // Disallowed join kinds — validator must reject these.
+ sql("select * from dept right ^join^ unnest(array[1, 2]) as u(x) on true")
+ .fails("UNNEST is only supported with INNER, LEFT, CROSS, or COMMA join, not 'RIGHT'");
+ sql("select * from dept full ^join^ unnest(array[1, 2]) as u(x) on true")
+ .fails("UNNEST is only supported with INNER, LEFT, CROSS, or COMMA join, not 'FULL'");
+
+ // LATERAL wrapping must also be rejected for invalid join kinds.
+ sql("select * from dept right ^join^ lateral unnest(array[1, 2]) as u(x) on true")
+ .fails("UNNEST is only supported with INNER, LEFT, CROSS, or COMMA join, not 'RIGHT'");
+ }
+
@Test void unnestMapMustNameColumnsKeyAndValueWhenNotAliased() {
sql("select * from unnest(map[1, 12, 2, 22])")
.type("RecordType(INTEGER NOT NULL KEY, INTEGER NOT NULL VALUE) NOT NULL");