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

shizy818 pushed a commit to branch fix_join_tables
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit 4a1f994fe8b8fc123c0d2589b46bd923a0a4ba93
Author: shizy <[email protected]>
AuthorDate: Sat Jan 17 15:54:05 2026 +0800

    fix: process tables for join scope
---
 .../relational/it/query/recent/IoTDBCteIT.java     | 27 ++++++++++++++++++++++
 .../plan/relational/analyzer/Scope.java            |  3 +--
 .../relational/analyzer/StatementAnalyzer.java     | 19 +++++++++++----
 .../plan/relational/planner/CteSubqueryTest.java   |  2 --
 4 files changed, 43 insertions(+), 8 deletions(-)

diff --git 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBCteIT.java
 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBCteIT.java
index 7f29e3f0197..ceec77e78af 100644
--- 
a/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBCteIT.java
+++ 
b/integration-test/src/test/java/org/apache/iotdb/relational/it/query/recent/IoTDBCteIT.java
@@ -405,6 +405,33 @@ public class IoTDBCteIT {
     }
   }
 
+  @Test
+  public void testExplainJoin() throws SQLException {
+    final String sql =
+        "explain with cte1 as (select * from testtb), "
+            + "cte2 as materialized (select * from cte1, testtb where 
cte1.deviceid = testtb.deviceid) "
+            + "select * from cte2";
+    try (Connection connection = 
EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT);
+        Statement statement = connection.createStatement()) {
+      statement.execute("USE testdb");
+
+      // explain
+      ResultSet resultSet = statement.executeQuery(sql);
+      ResultSetMetaData metaData = resultSet.getMetaData();
+      assertEquals(metaData.getColumnCount(), 1);
+      assertEquals(metaData.getColumnName(1), "distribution plan");
+
+      StringBuilder sb = new StringBuilder();
+      while (resultSet.next()) {
+        sb.append(resultSet.getString(1)).append(System.lineSeparator());
+      }
+      String result = sb.toString();
+      assertFalse(result.contains("CTE Query : 'cte1'"));
+      assertTrue(result.contains("CTE Query : 'cte2'"));
+      assertTrue(result.contains("Main Query"));
+    }
+  }
+
   @Test
   public void testRecursive() {
     String sqlTemplate =
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Scope.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Scope.java
index e2fd0cc7276..d06529c0050 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Scope.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Scope.java
@@ -32,7 +32,6 @@ import org.apache.iotdb.rpc.TSStatusCode;
 import com.google.common.collect.ImmutableMap;
 
 import java.util.ArrayList;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -92,7 +91,7 @@ public class Scope {
   }
 
   public List<Identifier> getTables() {
-    return Collections.unmodifiableList(tables);
+    return tables;
   }
 
   public Scope withRelationType(RelationType relationType) {
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
index 9220ad55afa..3ea9fdfc576 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java
@@ -3630,14 +3630,25 @@ public class StatementAnalyzer {
 
       joinConditionCheck(criteria);
 
-      // remember current tables in the scope
-      List<Identifier> tables = new ArrayList<>();
-      scope.ifPresent(s -> tables.addAll(s.getTables()));
+      // Remember original tables before processing left
+      List<Identifier> originalTables = new ArrayList<>();
+      scope.ifPresent(s -> originalTables.addAll(s.getTables()));
 
       Scope left = process(node.getLeft(), scope);
-      scope.ifPresent(s -> s.setTables(tables));
+
+      // Restore tables for right processing
+      scope.ifPresent(s -> s.setTables(originalTables));
       Scope right = process(node.getRight(), scope);
 
+      // Add back tables added during left processing to preserve them in the 
scope
+      if (left != null) {
+        List<Identifier> leftAddedTables =
+            left.getTables().stream()
+                .filter(table -> !originalTables.contains(table))
+                .collect(Collectors.toList());
+        scope.ifPresent(s -> s.getTables().addAll(leftAddedTables));
+      }
+
       if (criteria instanceof JoinUsing) {
         return analyzeJoinUsing(node, ((JoinUsing) criteria).getColumns(), 
scope, left, right);
       }
diff --git 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteSubqueryTest.java
 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteSubqueryTest.java
index 59fbc1a1f8d..bff1d7c93dd 100644
--- 
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteSubqueryTest.java
+++ 
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/CteSubqueryTest.java
@@ -80,8 +80,6 @@ public class CteSubqueryTest {
    */
   @Test
   public void testCteSubquery() throws IoTDBException {
-    mockExecuteForTableModel();
-
     String sql =
         "with cte1 as (select time, s2 from table1) select s1 from table1 "
             + "where s1 = (select s2 from cte1)";

Reply via email to