Skip to content

Commit

Permalink
[CALCITE-6038] Remove 'ORDER BY ... LIMIT n' when input has at most o…
Browse files Browse the repository at this point in the history
…ne row, n >= 1, and there is no 'OFFSET' clause
  • Loading branch information
LakeShen committed Oct 13, 2023
1 parent 454899a commit 0963738
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,12 @@

import java.util.Optional;

/** Rule that removes redundant {@code Order By} or {@code Limit} when its input RelNode's
* max row count is less than or equal to specified row count.All of them
* are represented by {@link Sort}
/**
* Rule that removes redundant {@code Order By} or {@code Limit}
* when its input RelNode's max row count is less than or equal to specified row count.
* All of them are represented by {@link Sort}
*
* <p> If a {@code Sort} is pure order by,and its offset is null,when its input RelNode's
* <p> If a {@code Sort} is order by,and its offset is null,when its input RelNode's
* max row count is less than or equal to 1,then we could remove the redundant sort.
*
* <p> For example:
Expand All @@ -43,9 +44,19 @@
* select max(totalprice) from orders}
* </pre></blockquote>
*
* <p> For example:
* <blockquote><pre>{@code
* SELECT count(*) FROM orders ORDER BY 1 LIMIT 10 }
* </pre></blockquote>
*
* <p> could be converted to
* <blockquote><pre>{@code
* SELECT count(*) FROM orders}
* </pre></blockquote>
*
* <p> If a {@code Sort} is pure limit,and its offset is null, when its input
* RelNode's max row count is less than or equal to the limit's fetch,then we could
* remove the redundant sort.
* remove the redundant {@code Limit}.
*
* <p> For example:
* <blockquote><pre>{@code
Expand Down Expand Up @@ -81,8 +92,10 @@ protected SortRemoveRedundantRule(final SortRemoveRedundantRule.Config config) {
final Double inputMaxRowCount = call.getMetadataQuery().getMaxRowCount(sort.getInput());

// Get the target max row count with sort's semantics.
// If sort is pure order by, the target max row count is 1.
// If sort is 'order by x' or 'order by x limit n', the target max row count is 1.
// If sort is pure limit, the target max row count is the limit's fetch.
// If the limit's fetch is 0, we could use CoreRules.SORT_FETCH_ZERO_INSTANCE to deal with it,
// so we don't need to deal with it in this rule.
final Optional<Double> targetMaxRowCount = getSortInputSpecificMaxRowCount(sort);

if (!targetMaxRowCount.isPresent()) {
Expand All @@ -97,13 +110,15 @@ protected SortRemoveRedundantRule(final SortRemoveRedundantRule.Config config) {
}

private Optional<Double> getSortInputSpecificMaxRowCount(Sort sort) {
// If the sort is pure limit, the specific max row count is limit's fetch.
if (RelOptUtil.isPureLimit(sort)) {
final double limit =
sort.fetch instanceof RexLiteral ? RexLiteral.intValue(sort.fetch) : -1D;
return Optional.of(limit);
if (RelOptUtil.isLimit(sort)) {
final double fetch =
sort.fetch instanceof RexLiteral ? RexLiteral.intValue(sort.fetch) : 0D;
if (fetch == 0D) {
return Optional.empty();
}
// If sort is 'order by x limit n', the target max row count is 1.
return RelOptUtil.isOrder(sort) ? Optional.of(1D) : Optional.of(fetch);
} else if (RelOptUtil.isPureOrder(sort)) {
// If the sort is pure order by, the specific max row count is 1.
return Optional.of(1D);
}
return Optional.empty();
Expand Down
24 changes: 24 additions & 0 deletions core/src/test/java/org/apache/calcite/test/RelOptRulesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -1368,6 +1368,30 @@ private void checkSemiOrAntiJoinProjectTranspose(JoinRelType type) {
.checkUnchanged();
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-6038">[CALCITE-6038]
* Remove 'ORDER BY ... LIMIT n' when input has at most one row, n >= 1,
* and there is no 'OFFSET' clause</a>. */
@Test void testSortRemoveWhenIsOrderAndLimit() {
final String sql = "SELECT count(*) FROM sales.emp ORDER BY 1 LIMIT 10";
sql(sql)
.withRule(CoreRules.SORT_REMOVE_REDUNDANT)
.check();
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-6038">[CALCITE-6038]
* Remove 'ORDER BY ... LIMIT n' when input has at most one row, n >= 1,
* and there is no 'OFFSET' clause</a>. */
@Test void testSortNotRemoveWhenIsOrderAndLimit() {
final String sql = "select * from\n"
+ "(SELECT * FROM sales.emp limit 100)\n"
+ "ORDER BY 1 LIMIT 10";
sql(sql)
.withRule(CoreRules.SORT_REMOVE_REDUNDANT)
.checkUnchanged();
}

/** Tests that an {@link EnumerableLimit} and {@link EnumerableSort} are
* replaced by an {@link EnumerableLimitSort}, per
* <a href="https://issues.apache.org/jira/browse/CALCITE-3920">[CALCITE-3920]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14100,6 +14100,22 @@ LogicalProject(DEPTNO=[$0], EMPNO=[$2])
LogicalJoin(condition=[=($0, $9)], joinType=[left])
LogicalTableScan(table=[[CATALOG, SALES, DEPT]])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
<TestCase name="testSortNotRemoveWhenIsOrderAndLimit">
<Resource name="sql">
<![CDATA[select * from
(SELECT * FROM sales.emp limit 100)
ORDER BY 1 LIMIT 10]]>
</Resource>
<Resource name="planBefore">
<![CDATA[
LogicalSort(sort0=[$0], dir0=[ASC], fetch=[10])
LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
LogicalSort(fetch=[100])
LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
Expand Down Expand Up @@ -14295,6 +14311,26 @@ LogicalSort(fetch=[10])
<![CDATA[
LogicalProject(T1=[$0])
LogicalValues(tuples=[[{ 1 }, { 2 }, { 3 }, { 4 }, { 5 }, { 6 }]])
]]>
</Resource>
</TestCase>
<TestCase name="testSortRemoveWhenIsOrderAndLimit">
<Resource name="sql">
<![CDATA[SELECT count(*) FROM sales.emp ORDER BY 1 LIMIT 10]]>
</Resource>
<Resource name="planBefore">
<![CDATA[
LogicalSort(sort0=[$0], dir0=[ASC], fetch=[10])
LogicalAggregate(group=[{}], EXPR$0=[COUNT()])
LogicalProject($f0=[0])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
<Resource name="planAfter">
<![CDATA[
LogicalAggregate(group=[{}], EXPR$0=[COUNT()])
LogicalProject($f0=[0])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
Expand Down

0 comments on commit 0963738

Please sign in to comment.