Skip to content

Commit

Permalink
[CALCITE-5994] When a sort's input max row cnt is 1,remove the redund…
Browse files Browse the repository at this point in the history
…ant sort
  • Loading branch information
LakeShen committed Sep 11, 2023
1 parent d4046d0 commit 68ccac1
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 17 deletions.
44 changes: 27 additions & 17 deletions core/src/main/java/org/apache/calcite/rel/rules/SortRemoveRule.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,25 +52,35 @@ public SortRemoveRule(RelBuilderFactory relBuilderFactory) {
}

@Override public void onMatch(RelOptRuleCall call) {
if (!call.getPlanner().getRelTraitDefs()
.contains(RelCollationTraitDef.INSTANCE)) {
// Collation is not an active trait.
return;
}
final Sort sort = call.rel(0);
if (sort.offset != null || sort.fetch != null) {
// Don't remove sort if would also remove OFFSET or LIMIT.
return;
// Get the max row count for sort's input RelNode.
final Double maxRowCount = call.getMetadataQuery().getMaxRowCount(sort.getInput());
// If the max row count is not null and less than or equal to 1,
// then we could remove the sort.
if (maxRowCount != null && maxRowCount <= 1D) {
call.transformTo(sort.getInput());
} else {
if (!call.getPlanner().getRelTraitDefs()
.contains(RelCollationTraitDef.INSTANCE)) {
// Collation is not an active trait.
return;
}

if (sort.offset != null || sort.fetch != null) {
// Don't remove sort if would also remove OFFSET or LIMIT.
return;
}

// Express the "sortedness" requirement in terms of a collation trait and
// we can get rid of the sort. This allows us to use rels that just happen
// to be sorted but get the same effect.
final RelCollation collation = sort.getCollation();
assert collation == sort.getTraitSet()
.getTrait(RelCollationTraitDef.INSTANCE);
final RelTraitSet traits = sort.getInput().getTraitSet()
.replace(collation).replaceIf(ConventionTraitDef.INSTANCE, sort::getConvention);
call.transformTo(convert(call.getPlanner(), sort.getInput(), traits));
}
// Express the "sortedness" requirement in terms of a collation trait and
// we can get rid of the sort. This allows us to use rels that just happen
// to be sorted but get the same effect.
final RelCollation collation = sort.getCollation();
assert collation == sort.getTraitSet()
.getTrait(RelCollationTraitDef.INSTANCE);
final RelTraitSet traits = sort.getInput().getTraitSet()
.replace(collation).replaceIf(ConventionTraitDef.INSTANCE, sort::getConvention);
call.transformTo(convert(call.getPlanner(), sort.getInput(), traits));
}

/** Rule configuration. */
Expand Down
25 changes: 25 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 @@ -1214,6 +1214,31 @@ private void checkSemiOrAntiJoinProjectTranspose(JoinRelType type) {
.check();
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-5994">[CALCITE-5994]
* When a sort's input max row cnt is 1,remove the redundant sort</a>. */
@Test void testSortRemoveWhenAggregateMaxRowCntIsOne() {
final String sql = "select count(*) as c\n"
+ "from sales.emp order by c";
sql(sql)
.withRule(CoreRules.SORT_REMOVE)
.check();
}

/** Test case for
* <a href="https://issues.apache.org/jira/browse/CALCITE-5994">[CALCITE-5994]
* When a sort's input max row cnt is 1,remove the redundant sort</a>. */
@Test void testSortRemoveWhenLimitMaxRowCntIsOne() {
final String sql = "select *\n"
+ "from (select * from sales.emp limit 1)\n"
+ "order by deptno";
sql(sql)
.withRule(CoreRules.SORT_REMOVE,
CoreRules.SORT_PROJECT_TRANSPOSE,
CoreRules.PROJECT_REMOVE)
.check();
}

/** 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 @@ -13781,6 +13781,49 @@ LogicalProject(C=[$0])
LogicalProject(DEPTNO=[$7], SAL=[$5])
LogicalFilter(condition=[=($7, 10)])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
<TestCase name="testSortRemoveWhenAggregateMaxRowCntIsOne">
<Resource name="sql">
<![CDATA[select count(*) as c
from sales.emp order by c]]>
</Resource>
<Resource name="planBefore">
<![CDATA[
LogicalSort(sort0=[$0], dir0=[ASC])
LogicalAggregate(group=[{}], C=[COUNT()])
LogicalProject($f0=[0])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
<Resource name="planAfter">
<![CDATA[
LogicalAggregate(group=[{}], C=[COUNT()])
LogicalProject($f0=[0])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
<TestCase name="testSortRemoveWhenLimitMaxRowCntIsOne">
<Resource name="sql">
<![CDATA[select *
from (select * from sales.emp limit 1)
order by deptno]]>
</Resource>
<Resource name="planBefore">
<![CDATA[
LogicalSort(sort0=[$7], dir0=[ASC])
LogicalProject(EMPNO=[$0], ENAME=[$1], JOB=[$2], MGR=[$3], HIREDATE=[$4], SAL=[$5], COMM=[$6], DEPTNO=[$7], SLACKER=[$8])
LogicalSort(fetch=[1])
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>
<Resource name="planAfter">
<![CDATA[
LogicalSort(fetch=[1])
LogicalTableScan(table=[[CATALOG, SALES, EMP]])
]]>
</Resource>
</TestCase>
Expand Down

0 comments on commit 68ccac1

Please sign in to comment.