Skip to content

Commit 2aac4ae

Browse files
committed
fix
Signed-off-by: Weihao Li <18110526956@163.com>
1 parent 44d4f6d commit 2aac4ae

2 files changed

Lines changed: 101 additions & 3 deletions

File tree

  • iotdb-core/datanode/src
    • main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations
    • test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer

iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/optimizations/SortElimination.java

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,21 @@
2525
import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.FillNode;
2626
import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.GapFillNode;
2727
import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.PatternRecognitionNode;
28+
import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ProjectNode;
2829
import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.SortNode;
2930
import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.StreamSortNode;
3031
import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.ValueFillNode;
3132
import org.apache.iotdb.commons.queryengine.plan.relational.planner.node.WindowNode;
3233
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanVisitor;
34+
import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PruneTableScanColumns;
3335
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode;
3436

37+
import com.google.common.collect.ImmutableSet;
38+
3539
import java.util.Collections;
40+
import java.util.List;
41+
import java.util.Optional;
42+
import java.util.Set;
3643

3744
/**
3845
* <b>Optimization phase:</b> Distributed plan planning.
@@ -42,6 +49,8 @@
4249
* SortNode can be eliminated.
4350
* <li>When order by all IDColumns and time, the SortNode can be eliminated.
4451
* <li>When StreamSortIndex==OrderBy size()-1, remove this StreamSortNode
52+
* <li>After SortNode elimination, visitProject will remove redundant identity ProjectNodes above
53+
* TableScan and pushes column pruning into the scan.
4554
*/
4655
public class SortElimination implements PlanOptimizer {
4756

@@ -64,6 +73,43 @@ public PlanNode visitPlan(PlanNode node, Context context) {
6473
return newNode;
6574
}
6675

76+
@Override
77+
public PlanNode visitProject(ProjectNode node, Context context) {
78+
Context newContext = new Context();
79+
PlanNode child = node.getChild().accept(this, newContext);
80+
context.setCannotEliminateSort(newContext.cannotEliminateSort);
81+
82+
// Remove useless ProjectNode and prune columns of TableScanNode
83+
return eliminateProjectOverTableScan(node, child)
84+
.orElseGet(() -> node.replaceChildren(Collections.singletonList(child)));
85+
}
86+
87+
private static Optional<PlanNode> eliminateProjectOverTableScan(
88+
ProjectNode project, PlanNode child) {
89+
if (!(child instanceof DeviceTableScanNode) || !project.isIdentity()) {
90+
return Optional.empty();
91+
}
92+
93+
// Notice that SortNode may have been eliminated in TableDistributedPlanGenerator
94+
DeviceTableScanNode tableScan = (DeviceTableScanNode) child;
95+
int projectOutputsSize = project.getOutputSymbols().size();
96+
int scanOutputsSize = tableScan.getOutputSymbols().size();
97+
if (projectOutputsSize > scanOutputsSize) {
98+
return Optional.empty();
99+
}
100+
101+
List<Symbol> projectOutputs = project.getOutputSymbols();
102+
Set<Symbol> scanOutputs = ImmutableSet.copyOf(tableScan.getOutputSymbols());
103+
if (!scanOutputs.containsAll(projectOutputs)) {
104+
return Optional.empty();
105+
}
106+
107+
if (projectOutputsSize == scanOutputsSize) {
108+
return Optional.of(tableScan);
109+
}
110+
return PruneTableScanColumns.pruneColumns(tableScan, ImmutableSet.copyOf(projectOutputs));
111+
}
112+
67113
@Override
68114
public PlanNode visitSort(SortNode node, Context context) {
69115
Context newContext = new Context();
@@ -75,9 +121,7 @@ public PlanNode visitSort(SortNode node, Context context) {
75121
&& orderingScheme.getOrderBy().get(0).getName().equals(context.getTimeColumnName())) {
76122
return child;
77123
}
78-
return context.canEliminateSort() && node.isOrderByAllIdsAndTime()
79-
? child
80-
: node.replaceChildren(Collections.singletonList(child));
124+
return node.replaceChildren(Collections.singletonList(child));
81125
}
82126

83127
@Override
@@ -152,6 +196,8 @@ private static class Context {
152196

153197
private String timeColumnName = null;
154198

199+
private boolean sortEliminated = false;
200+
155201
Context() {}
156202

157203
public void addDeviceEntrySize(int deviceEntrySize) {
@@ -177,5 +223,13 @@ public String getTimeColumnName() {
177223
public void setTimeColumnName(String timeColumnName) {
178224
this.timeColumnName = timeColumnName;
179225
}
226+
227+
public boolean isSortEliminated() {
228+
return sortEliminated;
229+
}
230+
231+
public void markSortEliminated() {
232+
this.sortEliminated = true;
233+
}
180234
}
181235
}

iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/SortTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,16 @@
3535
import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan;
3636
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.sink.IdentitySinkNode;
3737
import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
38+
import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester;
3839
import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator;
3940
import org.apache.iotdb.db.queryengine.plan.relational.planner.TableLogicalPlanner;
4041
import org.apache.iotdb.db.queryengine.plan.relational.planner.distribute.TableDistributedPlanner;
4142
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode;
4243
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ExchangeNode;
4344
import org.apache.iotdb.db.queryengine.plan.statement.component.Ordering;
4445

46+
import com.google.common.collect.ImmutableList;
47+
import com.google.common.collect.ImmutableSet;
4548
import org.junit.BeforeClass;
4649
import org.junit.Test;
4750

@@ -58,6 +61,12 @@
5861
import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.TEST_MATADATA;
5962
import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.assertTableScan;
6063
import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.getChildrenNode;
64+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanAssert.assertPlan;
65+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.exchange;
66+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.mergeSort;
67+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.output;
68+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.project;
69+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.tableScan;
6170
import static org.apache.iotdb.db.queryengine.plan.statement.component.Ordering.ASC;
6271
import static org.apache.iotdb.db.queryengine.plan.statement.component.Ordering.DESC;
6372
import static org.junit.Assert.assertEquals;
@@ -767,4 +776,39 @@ public void assertTopKNoFilter(
767776
expectedPushDownOffset,
768777
isPushLimitToEachDevice);
769778
}
779+
780+
@Test
781+
public void singleDeviceOrderByAllIdsAndTimeDescTest() {
782+
PlanTester planTester = new PlanTester();
783+
planTester.createPlan(
784+
"SELECT s1, s2, s3 FROM table1 "
785+
+ "WHERE time >= 1000 AND time <= 2000 AND tag1='beijing' AND tag2='A1'"
786+
+ "ORDER BY tag1, tag2, tag3, time DESC");
787+
assertPlan(
788+
planTester.getFragmentPlan(0),
789+
output(
790+
tableScan(
791+
"testdb.table1",
792+
ImmutableList.of("s1", "s2", "s3"),
793+
ImmutableSet.of("time", "s1", "s2", "s3"))));
794+
}
795+
796+
@Test
797+
public void multiDeviceOrderByAllIdsAndTimeDescTest() {
798+
PlanTester planTester = new PlanTester();
799+
planTester.createPlan(
800+
"SELECT s1, s2, s3 FROM table1 "
801+
+ "WHERE time >= 1000 AND time <= 2000 AND tag1='beijing' "
802+
+ "ORDER BY tag1, tag2, tag3, time DESC");
803+
assertPlan(
804+
planTester.getFragmentPlan(0),
805+
output(project(mergeSort(exchange(), exchange(), exchange()))));
806+
// Device in multi-region
807+
assertPlan(
808+
planTester.getFragmentPlan(1),
809+
tableScan(
810+
"testdb.table1",
811+
ImmutableList.of("time", "tag1", "tag2", "tag3", "s1", "s2", "s3"),
812+
ImmutableSet.of("time", "tag1", "tag2", "tag3", "s1", "s2", "s3")));
813+
}
770814
}

0 commit comments

Comments
 (0)