This is an automated email from the ASF dual-hosted git repository. caogaofei pushed a commit to branch beyyes/fix_node_ordering in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 50dda9837afe5f1beebfcd27a05b27ae1c908e7d Author: Beyyes <[email protected]> AuthorDate: Wed Apr 2 14:09:20 2025 +0800 Reapply "Fix optimizers doesn't work when execute Explain Analyze query" This reverts commit a2f49dce98e6890fa7754e669700d848735f5816. --- .../plan/planner/plan/node/PlanGraphPrinter.java | 9 ++++ .../relational/planner/TableLogicalPlanner.java | 13 +++++- .../iterative/rule/PruneExplainAnalyzeColumns.java | 49 ++++++++++++++++++++++ .../planner/node/ExplainAnalyzeNode.java | 20 +++++++-- .../plan/relational/planner/node/Patterns.java | 4 ++ .../optimizations/LogicalOptimizeFactory.java | 2 + .../optimizations/UnaliasSymbolReferences.java | 15 ++++++- .../relational/analyzer/ExplainAnalyzeTest.java | 43 +++++++++++++++++++ .../plan/relational/planner/PlanTester.java | 8 ++-- .../planner/assertions/PlanMatchPattern.java | 13 ++++++ 10 files changed, 165 insertions(+), 11 deletions(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java index f6d90ce4a6b..d9eef926e92 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/PlanGraphPrinter.java @@ -71,6 +71,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.AssignUnique import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.EnforceSingleRowNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExchangeNode; +import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExplainAnalyzeNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.GapFillNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.GroupNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.LinearFillNode; @@ -822,6 +823,14 @@ public class PlanGraphPrinter extends PlanVisitor<List<String>, PlanGraphPrinter return render(node, boxValue, context); } + @Override + public List<String> visitExplainAnalyze(ExplainAnalyzeNode node, GraphContext context) { + List<String> boxValue = new ArrayList<>(); + boxValue.add(String.format("ExplainAnalyze-%s", node.getPlanNodeId().getId())); + boxValue.add(String.format("ChildPermittedOutputs: %s", node.getChildPermittedOutputs())); + return render(node, boxValue, context); + } + @Override public List<String> visitGapFill(GapFillNode node, GraphContext context) { List<String> boxValue = new ArrayList<>(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java index c9bfb879181..6f92192c092 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableLogicalPlanner.java @@ -480,6 +480,16 @@ public class TableLogicalPlanner { createRelationPlan(analysis, (Query) (statement.getStatement())); Symbol symbol = symbolAllocator.newSymbol(ColumnHeaderConstant.EXPLAIN_ANALYZE, StringType.getInstance()); + + // recording permittedOutputs of ExplainAnalyzeNode's child + RelationType outputDescriptor = analysis.getOutputDescriptor(statement.getStatement()); + ImmutableList.Builder<Symbol> childPermittedOutputs = ImmutableList.builder(); + for (Field field : outputDescriptor.getVisibleFields()) { + int fieldIndex = outputDescriptor.indexOf(field); + Symbol columnSymbol = originalQueryPlan.getSymbol(fieldIndex); + childPermittedOutputs.add(columnSymbol); + } + PlanNode newRoot = new ExplainAnalyzeNode( queryContext.getQueryId().genPlanNodeId(), @@ -487,7 +497,8 @@ public class TableLogicalPlanner { statement.isVerbose(), queryContext.getLocalQueryId(), queryContext.getTimeOut(), - symbol); + symbol, + childPermittedOutputs.build()); return new RelationPlan( newRoot, originalQueryPlan.getScope(), diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExplainAnalyzeColumns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExplainAnalyzeColumns.java new file mode 100644 index 00000000000..697c1a55a8d --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/iterative/rule/PruneExplainAnalyzeColumns.java @@ -0,0 +1,49 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule; + +import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule; +import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExplainAnalyzeNode; +import org.apache.iotdb.db.queryengine.plan.relational.utils.matching.Captures; +import org.apache.iotdb.db.queryengine.plan.relational.utils.matching.Pattern; + +import com.google.common.collect.ImmutableSet; + +import static org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Util.restrictChildOutputs; +import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.explainAnalyze; + +public class PruneExplainAnalyzeColumns implements Rule<ExplainAnalyzeNode> { + private static final Pattern<ExplainAnalyzeNode> PATTERN = explainAnalyze(); + + @Override + public Pattern<ExplainAnalyzeNode> getPattern() { + return PATTERN; + } + + @Override + public Result apply(ExplainAnalyzeNode analyzeNode, Captures captures, Context context) { + return restrictChildOutputs( + context.getIdAllocator(), + analyzeNode, + ImmutableSet.copyOf(analyzeNode.getChildPermittedOutputs())) + .map(Result::ofPlanNode) + .orElse(Result.empty()); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExplainAnalyzeNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExplainAnalyzeNode.java index 7332743050c..23aa8baad2d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExplainAnalyzeNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/ExplainAnalyzeNode.java @@ -37,6 +37,7 @@ public class ExplainAnalyzeNode extends SingleChildProcessNode { private final long queryId; private final long timeout; private final Symbol outputSymbol; + private final List<Symbol> childPermittedOutputs; public ExplainAnalyzeNode( PlanNodeId id, @@ -44,17 +45,20 @@ public class ExplainAnalyzeNode extends SingleChildProcessNode { boolean verbose, long queryId, long timeout, - Symbol outputSymbol) { + Symbol outputSymbol, + List<Symbol> childPermittedOutputs) { super(id, child); this.verbose = verbose; this.timeout = timeout; this.queryId = queryId; this.outputSymbol = outputSymbol; + this.childPermittedOutputs = childPermittedOutputs; } @Override public PlanNode clone() { - return new ExplainAnalyzeNode(getPlanNodeId(), child, verbose, queryId, timeout, outputSymbol); + return new ExplainAnalyzeNode( + getPlanNodeId(), child, verbose, queryId, timeout, outputSymbol, childPermittedOutputs); } @Override @@ -72,10 +76,20 @@ public class ExplainAnalyzeNode extends SingleChildProcessNode { return Collections.singletonList(outputSymbol); } + public List<Symbol> getChildPermittedOutputs() { + return childPermittedOutputs; + } + @Override public PlanNode replaceChildren(List<PlanNode> newChildren) { return new org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExplainAnalyzeNode( - getPlanNodeId(), newChildren.get(0), verbose, queryId, timeout, outputSymbol); + getPlanNodeId(), + newChildren.get(0), + verbose, + queryId, + timeout, + outputSymbol, + childPermittedOutputs); } // ExplainAnalyze should be at the same region as Coordinator all the time. Therefore, there will diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/Patterns.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/Patterns.java index 83391f24d3f..1ebc30a72ed 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/Patterns.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/node/Patterns.java @@ -133,6 +133,10 @@ public final class Patterns { return typeOf(OutputNode.class); } + public static Pattern<ExplainAnalyzeNode> explainAnalyze() { + return typeOf(ExplainAnalyzeNode.class); + } + public static Pattern<ProjectNode> project() { return typeOf(ProjectNode.class); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/LogicalOptimizeFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/LogicalOptimizeFactory.java index 57f7f93b19f..f3010fb3539 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/LogicalOptimizeFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/LogicalOptimizeFactory.java @@ -43,6 +43,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.Pr import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PruneCorrelatedJoinCorrelation; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PruneDistinctAggregation; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PruneEnforceSingleRowColumns; +import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PruneExplainAnalyzeColumns; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PruneFillColumns; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PruneFilterColumns; import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PruneGapFillColumns; @@ -115,6 +116,7 @@ public class LogicalOptimizeFactory { new PruneMarkDistinctColumns(), new PruneOffsetColumns(), new PruneOutputSourceColumns(), + new PruneExplainAnalyzeColumns(), new PruneProjectColumns(), new PruneSortColumns(), new PruneTableFunctionProcessorColumns(), diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java index 0a08d748742..d12db4d3034 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/UnaliasSymbolReferences.java @@ -352,10 +352,21 @@ public class UnaliasSymbolReferences implements PlanOptimizer { @Override public PlanAndMappings visitExplainAnalyze(ExplainAnalyzeNode node, UnaliasContext context) { PlanAndMappings rewrittenSource = node.getChild().accept(this, context); + Map<Symbol, Symbol> mapping = new HashMap<>(rewrittenSource.getMappings()); + SymbolMapper mapper = symbolMapper(mapping); + + List<Symbol> newChildPermittedOutputs = mapper.map(node.getChildPermittedOutputs()); return new PlanAndMappings( - node.replaceChildren(ImmutableList.of(rewrittenSource.getRoot())), - rewrittenSource.getMappings()); + new ExplainAnalyzeNode( + node.getPlanNodeId(), + rewrittenSource.getRoot(), + node.isVerbose(), + node.getQueryId(), + node.getTimeout(), + node.getOutputSymbols().get(0), + newChildPermittedOutputs), + mapping); } @Override diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExplainAnalyzeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExplainAnalyzeTest.java new file mode 100644 index 00000000000..f20ebab3e98 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/ExplainAnalyzeTest.java @@ -0,0 +1,43 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.db.queryengine.plan.relational.analyzer; + +import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan; +import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester; + +import org.junit.Test; + +import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanAssert.assertPlan; +import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.aggregation; +import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.aggregationTableScan; +import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.explainAnalyze; +import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.output; +import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.project; + +public class ExplainAnalyzeTest { + private final PlanTester planTester = new PlanTester(); + + @Test + public void testColumnPrune() { + LogicalQueryPlan logicalQueryPlan = + planTester.createPlan("explain analyze select count(s1) from table1 group by tag1"); + assertPlan( + logicalQueryPlan, output(explainAnalyze(project(aggregation(aggregationTableScan()))))); + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java index 9a98ab96c7b..8eb019db607 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java @@ -115,7 +115,7 @@ public class PlanTester { distributedQueryPlan = null; MPPQueryContext context = new MPPQueryContext(sql, queryId, sessionInfo, null, null); - Analysis analysis = analyze(sql, metadata); + Analysis analysis = analyze(sql, metadata, context); this.analysis = analysis; this.symbolAllocator = new SymbolAllocator(); @@ -137,7 +137,7 @@ public class PlanTester { distributedQueryPlan = null; MPPQueryContext context = new MPPQueryContext(sql, queryId, sessionInfo, null, null); - Analysis analysis = analyze(sql, metadata); + Analysis analysis = analyze(sql, metadata, context); TableLogicalPlanner logicalPlanner = new TableLogicalPlanner( @@ -146,7 +146,7 @@ public class PlanTester { return logicalPlanner.plan(analysis); } - public static Analysis analyze(String sql, Metadata metadata) { + public static Analysis analyze(String sql, Metadata metadata, MPPQueryContext context) { SqlParser sqlParser = new SqlParser(); String databaseName; if (metadata instanceof TSBSMetadata) { @@ -160,8 +160,6 @@ public class PlanTester { SessionInfo session = new SessionInfo( 0, "test", ZoneId.systemDefault(), databaseName, IClientSession.SqlDialect.TABLE); - final MPPQueryContext context = - new MPPQueryContext(sql, new QueryId("test_query"), session, null, null); return analyzeStatement( statement, metadata, context, sqlParser, session, new AllowAllAccessControl()); } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java index 5fc2d03e53e..b09530d898c 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/assertions/PlanMatchPattern.java @@ -34,6 +34,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.CollectNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.EnforceSingleRowNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExchangeNode; +import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExplainAnalyzeNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.FilterNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.GroupNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.InformationSchemaTableScanNode; @@ -251,6 +252,10 @@ public final class PlanMatchPattern { return this; } + public static PlanMatchPattern aggregation(PlanMatchPattern source) { + return node(AggregationNode.class, source); + } + public static PlanMatchPattern aggregation( Map<String, ExpectedValueProvider<AggregationFunction>> aggregations, PlanMatchPattern source) { @@ -416,6 +421,10 @@ public final class PlanMatchPattern { return result; } + public static PlanMatchPattern aggregationTableScan() { + return node(AggregationTableScanNode.class); + } + public static PlanMatchPattern markDistinct( String markerSymbol, List<String> distinctSymbols, PlanMatchPattern source) { return node(MarkDistinctNode.class, source) @@ -571,6 +580,10 @@ public final class PlanMatchPattern { return output(outputs, source).withExactOutputs(outputs); } + public static PlanMatchPattern explainAnalyze(PlanMatchPattern source) { + return node(ExplainAnalyzeNode.class, source); + } + public static PlanMatchPattern project(PlanMatchPattern source) { return node(ProjectNode.class, source); }
