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

mbudiu 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 8f2d350670 [CALCITE-7305] Subqueries in ASOF JOIN MATCH_CONDITION 
cause an assertion failure
8f2d350670 is described below

commit 8f2d350670420ae4f5b0dc46b826cbf4f8e671eb
Author: Mihai Budiu <[email protected]>
AuthorDate: Fri Nov 28 14:32:40 2025 -0800

    [CALCITE-7305] Subqueries in ASOF JOIN MATCH_CONDITION cause an assertion 
failure
    
    Signed-off-by: Mihai Budiu <[email protected]>
---
 .../calcite/sql/validate/IdentifierNamespace.java  |  1 +
 .../calcite/sql/validate/SqlValidatorImpl.java     | 18 ++++++++++++------
 .../org/apache/calcite/test/SqlValidatorTest.java  | 22 ++++++++++++++++++++++
 3 files changed, 35 insertions(+), 6 deletions(-)

diff --git 
a/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java 
b/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
index 9291752b90..f23ddec087 100644
--- 
a/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
+++ 
b/core/src/main/java/org/apache/calcite/sql/validate/IdentifierNamespace.java
@@ -69,6 +69,7 @@ public class IdentifierNamespace extends AbstractNamespace {
    * @param extendList    Extension columns, or null
    * @param enclosingNode Enclosing node
    * @param parentScope   Parent scope which this namespace turns to in order 
to
+   *                      resolve objects
    */
   IdentifierNamespace(SqlValidatorImpl validator, SqlIdentifier id,
       @Nullable SqlNodeList extendList, @Nullable SqlNode enclosingNode,
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 00a02a3cc4..96d46174d4 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
@@ -2600,6 +2600,9 @@ private SqlNode registerFrom(
       scopes.putIfAbsent(stripAs(join.getRight()), parentScope);
       scopes.putIfAbsent(stripAs(join.getLeft()), parentScope);
       registerSubQueries(joinScope, join.getCondition());
+      if (join.getJoinType() == JoinType.ASOF || join.getJoinType() == 
JoinType.LEFT_ASOF) {
+        registerSubQueries(joinScope, ((SqlAsofJoin) 
join).getMatchCondition());
+      }
       final JoinNamespace joinNamespace = new JoinNamespace(this, join);
       registerNamespace(null, null, joinNamespace, forceNullable);
       return join;
@@ -3702,17 +3705,20 @@ private void checkRollUpInUsing(SqlIdentifier 
identifier,
 
   /** Get the number of scopes referenced by the specified node; the node
    * represents a computation that will be converted to a Rel node eventually. 
*/
-  private int getScopeCount(SqlNode node) {
-    SqlValidatorScope scope = scopes.get(node);
+  private int getScopeCount(@Nullable SqlValidatorScope scope) {
     if (scope == null) {
       // Not all nodes have an associated scope; count these as "1".
       // For example, a VALUES node.
       return 1;
-    }
-    if (scope instanceof ListScope) {
-      ListScope join = (ListScope) scope;
+    } else if (scope instanceof JoinScope) {
+      JoinScope join = (JoinScope) scope;
       return join.children.size();
+    } else if (scope instanceof WithScope) {
+      return getScopeCount(((WithScope) scope).getParent());
     }
+    // We don't need to handle arbitrary scopes here, because the argument 
scope
+    // is always from the left side of a join, and the SQL syntax constrains 
the
+    // kinds of subqueries that can appear in the left side of a join.
     return 1;
   }
 
@@ -3815,7 +3821,7 @@ protected void validateJoin(SqlJoin join, 
SqlValidatorScope scope) {
         throw newValidationError(condition, 
RESOURCE.asofConditionMustBeComparison());
       }
 
-      int leftScopeCount = getScopeCount(left);
+      int leftScopeCount = getScopeCount(scopes.get(left));
       CompareFromBothSides validateCompare =
           new CompareFromBothSides(joinScope, leftScopeCount,
               catalogReader, RESOURCE.asofConditionMustBeComparison());
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 105e428efe..2de4ae6704 100644
--- a/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
+++ b/core/src/test/java/org/apache/calcite/test/SqlValidatorTest.java
@@ -3507,6 +3507,28 @@ void testWinPartClause() {
   }
 
   @Test void testAsOfJoin() {
+    sql("WITH "
+        + "   T2(id, intt) AS (VALUES(1, 0)),\n"
+        + "   T1(id, intt) as (VALUES(1, 0)),\n"
+        + "   T3(id) AS (VALUES(1))\n"
+        + "SELECT t1.id, t2.intt\n"
+        + "FROM T1 LEFT ASOF JOIN T2\n"
+        + "    MATCH_CONDITION t2.intt < t1.intt\n"
+        + "    ON t1.id = t2.id")
+        .ok();
+
+    // Test case for [CALCITE-7305]
+    // Subqueries in ASOF JOIN MATCH_CONDITION cause an assertion failure
+    sql("WITH T1(id, intt) as (VALUES(1, 0)),\n"
+        + "   T2(id, intt) AS (VALUES(1, 0)),\n"
+        + "   T3(id) AS (VALUES(1))\n"
+        + "SELECT t1.id, t2.intt\n"
+        + "FROM T1 LEFT ASOF JOIN T2\n"
+        + "    MATCH_CONDITION ^(t2.intt IN (SELECT id FROM T3))^\n"
+        + "    ON t1.id = t2.id")
+        .fails("ASOF JOIN MATCH_CONDITION must be a comparison between columns 
"
+            + "from the two inputs");
+
     final String type0 = "RecordType(INTEGER NOT NULL EMPNO, INTEGER NOT NULL 
DEPTNO) NOT NULL";
     final String sql0 = "select emp.empno, dept.deptno from emp asof join 
dept\n"
         + "match_condition emp.deptno <= dept.deptno\n"

Reply via email to