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)";
