[improvement](planner) eliminating useless sort node (#14377)
This commit is contained in:
@ -20,6 +20,7 @@
|
||||
|
||||
package org.apache.doris.analysis;
|
||||
|
||||
import org.apache.doris.analysis.CompoundPredicate.Operator;
|
||||
import org.apache.doris.catalog.AggregateFunction;
|
||||
import org.apache.doris.catalog.Column;
|
||||
import org.apache.doris.catalog.DatabaseIf;
|
||||
@ -36,6 +37,7 @@ import org.apache.doris.common.ColumnAliasGenerator;
|
||||
import org.apache.doris.common.ErrorCode;
|
||||
import org.apache.doris.common.ErrorReport;
|
||||
import org.apache.doris.common.Pair;
|
||||
import org.apache.doris.common.Reference;
|
||||
import org.apache.doris.common.TableAliasGenerator;
|
||||
import org.apache.doris.common.TreeNode;
|
||||
import org.apache.doris.common.UserException;
|
||||
@ -559,6 +561,7 @@ public class SelectStmt extends QueryStmt {
|
||||
}
|
||||
analyzeAggregation(analyzer);
|
||||
createAnalyticInfo(analyzer);
|
||||
eliminatingSortNode();
|
||||
if (evaluateOrderBy) {
|
||||
createSortTupleInfo(analyzer);
|
||||
}
|
||||
@ -1734,6 +1737,79 @@ public class SelectStmt extends QueryStmt {
|
||||
return expr;
|
||||
}
|
||||
|
||||
public void eliminatingSortNode() {
|
||||
// initial sql: select * from t1 where k1 = 1 order by k1
|
||||
// optimized sql: select * from t1 where k1 = 1
|
||||
if (ConnectContext.get() == null || !ConnectContext.get().getSessionVariable().enableEliminateSortNode) {
|
||||
return;
|
||||
}
|
||||
if (!evaluateOrderBy() || getSortInfo() == null || getWhereClause() == null) {
|
||||
return;
|
||||
}
|
||||
List<SlotRef> sortSlots = new ArrayList<>();
|
||||
// get source slot ref from order by clause
|
||||
for (Expr expr : getSortInfo().getOrderingExprs()) {
|
||||
SlotRef source = expr.getSrcSlotRef();
|
||||
if (source == null) {
|
||||
return;
|
||||
}
|
||||
sortSlots.add(source);
|
||||
}
|
||||
if (sortSlots.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
if (checkSortNodeEliminable(getWhereClause(), sortSlots) && sortSlots.isEmpty()) {
|
||||
evaluateOrderBy = false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean checkSortNodeEliminable(Expr expr, List<SlotRef> sortSlotRefs) {
|
||||
// 1. Check that the CompoundPredicates in the whereClause are all AndCompound
|
||||
if (expr instanceof CompoundPredicate) {
|
||||
if (((CompoundPredicate) expr).getOp() != Operator.AND) {
|
||||
// fail to eliminate
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// 2. Check that all sort slots have:
|
||||
// 2.1 at least one BinaryPredicate expression equal to a constant
|
||||
// 2.2 OR at least one InPredicate expression containing only one constant
|
||||
// in the whereClause
|
||||
if (expr instanceof BinaryPredicate) {
|
||||
Reference<SlotRef> slotRefRef = new Reference<>();
|
||||
BinaryPredicate binaryPredicate = (BinaryPredicate) expr;
|
||||
if (binaryPredicate.isSingleColumnPredicate(slotRefRef, null)) {
|
||||
if (binaryPredicate.getOp() != BinaryPredicate.Operator.EQ) {
|
||||
// it's ok, try to check next expr
|
||||
return true;
|
||||
}
|
||||
// remove it
|
||||
sortSlotRefs.remove(slotRefRef.getRef());
|
||||
}
|
||||
} else if (expr instanceof InPredicate) {
|
||||
if (((InPredicate) expr).isNotIn()) {
|
||||
return true;
|
||||
}
|
||||
// there can only be two child nodes, one is a slotref and the other is a constant
|
||||
if (expr.getChildren().size() != 2) {
|
||||
// it's ok, try to check next expr
|
||||
return true;
|
||||
}
|
||||
if (!expr.getChild(1).isConstant()) {
|
||||
// it's ok, try to check next expr
|
||||
return true;
|
||||
}
|
||||
// remove it
|
||||
sortSlotRefs.remove(expr.getChild(0).unwrapSlotRef());
|
||||
}
|
||||
for (Expr child : expr.getChildren()) {
|
||||
if (!checkSortNodeEliminable(child, sortSlotRefs)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toSql() {
|
||||
if (sqlString != null) {
|
||||
|
||||
@ -225,6 +225,8 @@ public class SessionVariable implements Serializable, Writable {
|
||||
|
||||
public static final String ENABLE_NEREIDS_STATS_DERIVE_V2 = "enable_nereids_stats_derive_v2";
|
||||
|
||||
public static final String ENABLE_ELIMINATE_SORT_NODE = "enable_eliminate_sort_node";
|
||||
|
||||
public static final String INTERNAL_SESSION = "internal_session";
|
||||
|
||||
// session origin value
|
||||
@ -593,6 +595,9 @@ public class SessionVariable implements Serializable, Writable {
|
||||
@VariableMgr.VarAttr(name = ENABLE_NEREIDS_STATS_DERIVE_V2)
|
||||
public boolean enableNereidsStatsDeriveV2 = false;
|
||||
|
||||
@VariableMgr.VarAttr(name = ENABLE_ELIMINATE_SORT_NODE)
|
||||
public boolean enableEliminateSortNode = true;
|
||||
|
||||
@VariableMgr.VarAttr(name = INTERNAL_SESSION)
|
||||
public boolean internalSession = false;
|
||||
|
||||
|
||||
@ -526,4 +526,141 @@ public class PlannerTest extends TestWithFeService {
|
||||
Assertions.assertFalse(plan2.contains("SORT INFO:"));
|
||||
Assertions.assertFalse(plan2.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEliminatingSortNode() throws Exception {
|
||||
// fail case 1
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 = 1 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertTrue(plan1.contains("SORT INFO:\n `k1`\n `k2`"));
|
||||
Assertions.assertTrue(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
// fail case 2
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 = 1 and k3 = 2 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertTrue(plan1.contains("SORT INFO:\n `k1`\n `k2`"));
|
||||
Assertions.assertTrue(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
// fail case 3
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 = 1 and k2 != 2 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertTrue(plan1.contains("SORT INFO:\n `k1`\n `k2`"));
|
||||
Assertions.assertTrue(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
// fail case 4
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 = 1 or k2 = 2 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertTrue(plan1.contains("SORT INFO:\n `k1`\n `k2`"));
|
||||
Assertions.assertTrue(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
// fail case 5
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 = 1 and k2 = 2 or k3 = 3 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertTrue(plan1.contains("SORT INFO:\n `k1`\n `k2`"));
|
||||
Assertions.assertTrue(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
// fail case 6
|
||||
// TODO, support: in (select 1)
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 in (select 1) and k2 = 2 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertTrue(plan1.contains("order by:"));
|
||||
}
|
||||
|
||||
// fail case 7
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 not in (1) and k2 = 2 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertTrue(plan1.contains("order by:"));
|
||||
}
|
||||
|
||||
// success case 1
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 = 1 and k2 = 2 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertFalse(plan1.contains("SORT INFO:\n `k1`\n `k2`"));
|
||||
Assertions.assertFalse(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
// success case 2
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k3 = 3 and k2 = 2 and k1 = 1 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertFalse(plan1.contains("SORT INFO:\n `k1`\n `k2`"));
|
||||
Assertions.assertFalse(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
// success case 3
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 in (1) and k2 in (2) and k2 !=2 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertFalse(plan1.contains("SORT INFO:\n `k1`\n `k2`"));
|
||||
Assertions.assertFalse(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
// success case 4
|
||||
{
|
||||
String sql1 = "explain select k1 from db1.tbl1 where k1 in (concat('1','2')) and k2 = 2 order by k1, k2";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertFalse(plan1.contains("SORT INFO:\n `k1`\n `k2`"));
|
||||
Assertions.assertFalse(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
// success case 5
|
||||
{
|
||||
String sql1 = "explain select tbl1.k1 from db1.tbl1 join db1.tbl2 on tbl1.k1 = tbl2.k1"
|
||||
+ " where tbl1.k1 = 1 and tbl2.k1 = 2 and tbl1.k2 = 3 order by tbl1.k1, tbl2.k1";
|
||||
StmtExecutor stmtExecutor1 = new StmtExecutor(connectContext, sql1);
|
||||
stmtExecutor1.execute();
|
||||
Planner planner1 = stmtExecutor1.planner();
|
||||
String plan1 = planner1.getExplainString(new ExplainOptions(false, false));
|
||||
Assertions.assertFalse(plan1.contains("SORT INFO:"));
|
||||
Assertions.assertFalse(plan1.contains("SORT LIMIT:"));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user