Skip to content

Commit 95cdc2e

Browse files
authored
Push down offset into TableScanNode when there is only one data region
1 parent 5700de6 commit 95cdc2e

File tree

4 files changed

+101
-1
lines changed

4 files changed

+101
-1
lines changed

integration-test/src/test/java/org/apache/iotdb/relational/it/query/view/old/query/IoTDBPaginationTableViewIT.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,5 +214,20 @@ public void limitOffsetPushDownTest() {
214214
expectedHeader,
215215
retArray,
216216
DATABASE_NAME);
217+
218+
retArray =
219+
new String[] {
220+
"1,",
221+
};
222+
tableResultSetEqualTest(
223+
"select s1 from db order by time limit 1 offset 1",
224+
expectedHeader,
225+
retArray,
226+
DATABASE_NAME);
227+
tableResultSetEqualTest(
228+
"select s1 from db order by device limit 1 offset 1",
229+
expectedHeader,
230+
retArray,
231+
DATABASE_NAME);
217232
}
218233
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule;
21+
22+
import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.Rule;
23+
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.DeviceTableScanNode;
24+
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.OffsetNode;
25+
import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode;
26+
import org.apache.iotdb.db.queryengine.plan.relational.utils.matching.Capture;
27+
import org.apache.iotdb.db.queryengine.plan.relational.utils.matching.Captures;
28+
import org.apache.iotdb.db.queryengine.plan.relational.utils.matching.Pattern;
29+
30+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.offset;
31+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.source;
32+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.node.Patterns.tableScan;
33+
import static org.apache.iotdb.db.queryengine.plan.relational.utils.matching.Capture.newCapture;
34+
35+
/**
36+
* <b>Optimization phase:</b> Distributed plan planning.
37+
*
38+
* <p>The OFFSET can be eliminated when the following conditions are met:
39+
* <li>Its child is DeviceTableScan, which means there OFFSET is effect on only one region
40+
* <li>The query expressions are all scalar expression.
41+
*/
42+
public class PushDownOffsetIntoTableScan implements Rule<OffsetNode> {
43+
private static final Capture<TableScanNode> CHILD = newCapture();
44+
45+
private static final Pattern<OffsetNode> PATTERN =
46+
offset().with(source().matching(tableScan().capturedAs(CHILD)));
47+
48+
@Override
49+
public Pattern<OffsetNode> getPattern() {
50+
return PATTERN;
51+
}
52+
53+
@Override
54+
public Result apply(OffsetNode parent, Captures captures, Context context) {
55+
TableScanNode tableScanNode = captures.get(CHILD);
56+
if (tableScanNode instanceof DeviceTableScanNode
57+
&& !((DeviceTableScanNode) tableScanNode).isPushLimitToEachDevice()) {
58+
tableScanNode.setPushDownOffset(parent.getCount());
59+
// consider case that there is no limit
60+
tableScanNode.setPushDownLimit(
61+
tableScanNode.getPushDownLimit() == 0
62+
? 0
63+
: tableScanNode.getPushDownLimit() - parent.getCount());
64+
return Result.ofPlanNode(tableScanNode);
65+
}
66+
67+
return Result.empty();
68+
}
69+
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.EliminateLimitWithTableScan;
2727
import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.MergeLimitOverProjectWithMergeSort;
2828
import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.MergeLimitWithMergeSort;
29+
import org.apache.iotdb.db.queryengine.plan.relational.planner.iterative.rule.PushDownOffsetIntoTableScan;
2930

3031
import com.google.common.collect.ImmutableList;
3132
import com.google.common.collect.ImmutableSet;
@@ -53,7 +54,9 @@ public DistributedOptimizeFactory(PlannerContext plannerContext) {
5354
plannerContext,
5455
ruleStats,
5556
ImmutableSet.of(
56-
new EliminateLimitWithTableScan(), new EliminateLimitProjectWithTableScan())));
57+
new EliminateLimitWithTableScan(),
58+
new EliminateLimitProjectWithTableScan(),
59+
new PushDownOffsetIntoTableScan())));
5760
}
5861

5962
public List<PlanOptimizer> getPlanOptimizers() {

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.iotdb.db.queryengine.plan.planner.plan.LogicalQueryPlan;
2828
import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNode;
2929
import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata;
30+
import org.apache.iotdb.db.queryengine.plan.relational.planner.PlanTester;
3031
import org.apache.iotdb.db.queryengine.plan.relational.planner.SymbolAllocator;
3132
import org.apache.iotdb.db.queryengine.plan.relational.planner.TableLogicalPlanner;
3233
import org.apache.iotdb.db.queryengine.plan.relational.planner.distribute.TableDistributedPlanner;
@@ -51,6 +52,9 @@
5152
import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.assertTableScan;
5253
import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.assertTableScanWithoutEntryOrder;
5354
import static org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.getChildrenNode;
55+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanAssert.assertPlan;
56+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.output;
57+
import static org.apache.iotdb.db.queryengine.plan.relational.planner.assertions.PlanMatchPattern.tableScan;
5458
import static org.apache.iotdb.db.queryengine.plan.statement.component.Ordering.ASC;
5559
import static org.apache.iotdb.db.queryengine.plan.statement.component.Ordering.DESC;
5660
import static org.junit.Assert.assertEquals;
@@ -303,4 +307,13 @@ public void limitDiffProjectTest() {
303307
assertTrue(getChildrenNode(rootNode, 2) instanceof LimitNode);
304308
assertTrue(getChildrenNode(rootNode, 3) instanceof DeviceTableScanNode);
305309
}
310+
311+
@Test
312+
public void PushDownOffsetIntoTableScan() {
313+
PlanTester planTester = new PlanTester();
314+
sql = "select * from table1 where tag1='Beijing' and tag2='A1' limit 1 offset 1";
315+
logicalQueryPlan = planTester.createPlan(sql);
316+
// the offset node has been push down into TableScanNode
317+
assertPlan(planTester.getFragmentPlan(0), output(tableScan("testdb.table1")));
318+
}
306319
}

0 commit comments

Comments
 (0)