Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public class MergeProjectPostProcessor extends PlanPostProcessor {
public PhysicalProject visitPhysicalProject(PhysicalProject<? extends Plan> project, CascadesContext ctx) {
project = (PhysicalProject<? extends Plan>) super.visit(project, ctx);
Plan child = project.child();
if (child instanceof PhysicalProject) {
if (child instanceof PhysicalProject && project.canMergeProjections((PhysicalProject) child)) {
List<NamedExpression> projections = project.mergeProjections((PhysicalProject) child);
return (PhysicalProject) project
.withProjectionsAndChild(projections, child.child(0))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,6 @@ public class Validator extends PlanPostProcessor {
@Override
public Plan visitPhysicalProject(PhysicalProject<? extends Plan> project, CascadesContext context) {
Preconditions.checkArgument(!project.getProjects().isEmpty(), "Project list can't be empty");

Plan child = project.child();
// Forbidden project-project, we must merge project.
if (child instanceof PhysicalProject) {
throw new AnalysisException("Nereids must merge a project-project plan");
}

return visit(project, context);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class MergeProjectsCBO extends OneExplorationRuleFactory {
@Override
public Rule build() {
return logicalProject(logicalProject())
.when(project -> project.canMergeProjections(project.child()))
.then(project -> MergeProjects.mergeProjects(project))
.toRule(RuleType.MERGE_PROJECTS);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ public List<Rule> buildRules() {
}
return true;
})
)).then(r -> {
).when(project -> project.canMergeProjections(project.child().child()))).then(r -> {
LogicalProject<?> upperProject = r.child();
LogicalProject<LogicalFilter<LogicalOlapScan>> bottomProject = r.child().child().child();
List<NamedExpression> projections = upperProject.mergeProjections(bottomProject);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import org.apache.doris.nereids.trees.expressions.NamedExpression;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.logical.LogicalProject;
import org.apache.doris.nereids.util.ExpressionUtils;

import java.util.List;

Expand All @@ -43,11 +42,11 @@ public Rule build() {
// TODO modify ExtractAndNormalizeWindowExpression to handle nested window functions
// here we just don't merge two projects if there is any window function
return logicalProject(logicalProject())
.whenNot(project -> ExpressionUtils.containsWindowExpression(project.getProjects())
&& ExpressionUtils.containsWindowExpression(project.child().getProjects()))
.when(project -> project.canMergeProjections(project.child()))
.then(MergeProjects::mergeProjects).toRule(RuleType.MERGE_PROJECTS);
}

/** merge projects */
public static Plan mergeProjects(LogicalProject<?> project) {
LogicalProject<? extends Plan> childProject = (LogicalProject<?>) project.child();
List<NamedExpression> projectExpressions = project.mergeProjections(childProject);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ default List<NamedExpression> mergeProjections(Project childProject) {
return projects;
}

/** check can merge two projects */
default boolean canMergeProjections(Project childProject) {
if (ExpressionUtils.containsWindowExpression(getProjects())
&& ExpressionUtils.containsWindowExpression(childProject.getProjects())) {
return false;
}

return PlanUtils.canReplaceWithProjections(childProject.getProjects(), getProjects());
}

/**
* find projects, if not found the slot, then throw AnalysisException
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.Utils;
import org.apache.doris.qe.ConnectContext;
import org.apache.doris.statistics.Statistics;

import com.google.common.base.Preconditions;
Expand All @@ -49,6 +50,7 @@
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Physical project plan.
Expand Down Expand Up @@ -100,6 +102,23 @@ public String toString() {
);
}

@Override
public String shapeInfo() {
ConnectContext context = ConnectContext.get();
if (context != null
&& context.getSessionVariable().getDetailShapePlanNodesSet().contains(getClass().getSimpleName())) {
StringBuilder builder = new StringBuilder();
builder.append(getClass().getSimpleName());
// the internal project list's order may be unstable, especial for join tables,
// so sort the projects to make it stable
builder.append(projects.stream().map(Expression::shapeInfo).sorted()
.collect(Collectors.joining(", ", "[", "]")));
return builder.toString();
} else {
return super.shapeInfo();
}
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -133,6 +134,29 @@ public static List<Expression> replaceExpressionByProjections(List<NamedExpressi
return ExpressionUtils.replace(targetExpression, replaceMap);
}

/**
* replace targetExpressions with project.
* if the target expression contains a slot which is an alias and its origin expression contains
* non-foldable expression and the slot exits multiple times, then can not replace.
* for example, target expressions: [a, a + 10], child project: [ t + random() as a ],
* if replace with the projects, then result expressions: [ t + random(), t + random() + 10 ],
* it will calculate random two times, this is error.
*/
public static boolean canReplaceWithProjections(List<? extends NamedExpression> childProjects,
List<? extends Expression> targetExpressions) {
Set<Slot> nonfoldableSlots = ExpressionUtils.generateReplaceMap(childProjects).entrySet().stream()
.filter(entry -> entry.getValue().containsNonfoldable())
.map(Entry::getKey)
.collect(Collectors.toSet());
if (nonfoldableSlots.isEmpty()) {
return true;
}

Set<Slot> counterSet = Sets.newHashSet();
return targetExpressions.stream().noneMatch(target -> target.anyMatch(
e -> (e instanceof Slot) && nonfoldableSlots.contains(e) && !counterSet.add((Slot) e)));
}

public static Plan skipProjectFilterLimit(Plan plan) {
if (plan instanceof LogicalProject && ((LogicalProject<?>) plan).isAllSlots()
|| plan instanceof LogicalFilter || plan instanceof LogicalLimit) {
Expand Down
19 changes: 19 additions & 0 deletions fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
Original file line number Diff line number Diff line change
Expand Up @@ -2202,6 +2202,8 @@ public void setIgnoreRuntimeFilterIds(String ignoreRuntimeFilterIds) {

public static final String IGNORE_SHAPE_NODE = "ignore_shape_nodes";

public static final String DETAIL_SHAPE_NODES = "detail_shape_nodes";

public static final String ENABLE_SEGMENT_CACHE = "enable_segment_cache";

public Set<String> getIgnoreShapePlanNodes() {
Expand All @@ -2217,6 +2219,23 @@ public void setIgnoreShapePlanNodes(String ignoreShapePlanNodes) {
"the plan node type which is ignored in 'explain shape plan' command"})
public String ignoreShapePlanNodes = "";

@VariableMgr.VarAttr(name = DETAIL_SHAPE_NODES, needForward = true, setter = "setDetailShapePlanNodes",
description = {"'explain shape plan' 命令中显示详细信息的PlanNode 类型",
"the plan node type show detail in 'explain shape plan' command"})
public String detailShapePlanNodes = "";

private Set<String> detailShapePlanNodesSet = ImmutableSet.of();

public Set<String> getDetailShapePlanNodesSet() {
return detailShapePlanNodesSet;
}

public void setDetailShapePlanNodes(String detailShapePlanNodes) {
this.detailShapePlanNodesSet = Arrays.stream(detailShapePlanNodes.split(",[\\s]*"))
.collect(ImmutableSet.toImmutableSet());
this.detailShapePlanNodes = detailShapePlanNodes;
}

@VariableMgr.VarAttr(name = ENABLE_DECIMAL256, needForward = true, description = { "控制是否在计算过程中使用Decimal256类型",
"Set to true to enable Decimal256 type" })
public boolean enableDecimal256 = false;
Expand Down
87 changes: 87 additions & 0 deletions regression-test/data/nereids_rules_p0/test_nonfoldable.out
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
-- This file is automatically generated. You should know what you did if you want to edit this
-- !filter_through_project_1 --
PhysicalResultSink
--PhysicalProject[(id + 100) AS `a`, (id + 200) AS `b`, (id + 300) AS `c`]
----filter((cast(id as BIGINT) > 899))
------PhysicalOlapScan[t1]

-- !filter_through_project_2 --
PhysicalResultSink
--filter((t.a > 999))
----PhysicalProject[((id + random(1, 10)) + 100) AS `a`, (id + 200) AS `b`, (id + 300) AS `c`]
------filter((cast(id as BIGINT) > 799))
--------PhysicalOlapScan[t1]

-- !filter_through_project_3 --
PhysicalResultSink
--filter((t.a > 999) and (t.b > 999))
----PhysicalProject[((id + random(1, 10)) + 100) AS `a`, ((id + random(1, 10)) + 200) AS `b`, ((id + random(1, 10)) + 300) AS `c`]
------PhysicalOlapScan[t1]

-- !filter_through_project_4 --
PhysicalResultSink
--PhysicalProject[(id + 100) AS `a`, (id + 200) AS `b`, (id + 300) AS `c`]
----filter(((t1.id + random(1, 10)) > 899))
------PhysicalOlapScan[t1]

-- !filter_through_project_5 --
PhysicalResultSink
--PhysicalLimit[GLOBAL]
----PhysicalLimit[LOCAL]
------PhysicalProject[(id + 100) AS `a`, (id + 200) AS `b`, (id + 300) AS `c`]
--------filter((cast(id as BIGINT) > 899))
----------PhysicalOlapScan[t1]

-- !filter_through_project_6 --
PhysicalResultSink
--PhysicalLimit[GLOBAL]
----PhysicalLimit[LOCAL]
------filter((t.a > 999))
--------PhysicalProject[((id + random(1, 10)) + 100) AS `a`, (id + 200) AS `b`, (id + 300) AS `c`]
----------filter((cast(id as BIGINT) > 799))
------------PhysicalOlapScan[t1]

-- !filter_through_project_7 --
PhysicalResultSink
--PhysicalLimit[GLOBAL]
----PhysicalLimit[LOCAL]
------filter((t.a > 999) and (t.b > 999))
--------PhysicalProject[((id + random(1, 10)) + 100) AS `a`, ((id + random(1, 10)) + 200) AS `b`, ((id + random(1, 10)) + 300) AS `c`]
----------PhysicalOlapScan[t1]

-- !filter_through_project_8 --
PhysicalResultSink
--PhysicalLimit[GLOBAL]
----PhysicalLimit[LOCAL]
------PhysicalProject[(id + 100) AS `a`, (id + 200) AS `b`, (id + 300) AS `c`]
--------filter(((t1.id + random(1, 10)) > 899))
----------PhysicalOlapScan[t1]

-- !merge_project_1 --
PhysicalResultSink
--PhysicalProject[(id + 100) AS `b`, (id + 100) AS `c`]
----PhysicalOlapScan[t1]

-- !merge_project_2 --
PhysicalResultSink
--PhysicalProject[a AS `b`, a AS `c`]
----PhysicalProject[(id + random(1, 10)) AS `a`]
------PhysicalOlapScan[t1]

-- !merge_project_3 --
PhysicalResultSink
--PhysicalProject[(id + random(1, 10)) AS `b`]
----PhysicalOlapScan[t1]

-- !merge_project_4 --
PhysicalResultSink
--PhysicalProject[((a + a) + 10) AS `b`]
----PhysicalProject[(id + random(1, 10)) AS `a`]
------PhysicalOlapScan[t1]

-- !merge_project_5 --
PhysicalResultSink
--PhysicalProject[(a + 10) AS `c`, a AS `b`]
----PhysicalProject[(id + random(1, 10)) AS `a`]
------PhysicalOlapScan[t1]

77 changes: 77 additions & 0 deletions regression-test/suites/nereids_rules_p0/test_nonfoldable.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

suite('test_nonfoldable') {
sql 'SET enable_nereids_planner=true'
sql 'SET runtime_filter_mode=OFF'
sql 'SET enable_fallback_to_original_planner=false'
sql "SET ignore_shape_nodes='PhysicalDistribute'"
sql "SET detail_shape_nodes='PhysicalProject'"
sql 'SET disable_nereids_rules=PRUNE_EMPTY_PARTITION'

qt_filter_through_project_1 '''
explain shape plan select * from (select id + 100 as a, id + 200 as b, id + 300 as c from t1) t where a > 999 and b > 999
'''

qt_filter_through_project_2 '''
explain shape plan select * from (select id + random(1, 10) + 100 as a, id + 200 as b, id + 300 as c from t1) t where a > 999 and b > 999
'''

qt_filter_through_project_3 '''
explain shape plan select * from (select id + random(1, 10) + 100 as a, id + random(1, 10) + 200 as b, id + random(1, 10) + 300 as c from t1) t where a > 999 and b > 999
'''

qt_filter_through_project_4 '''
explain shape plan select * from (select id + 100 as a, id + 200 as b, id + 300 as c from t1) t where a + random(1, 10) > 999 and b + random(1, 10) > 999
'''

qt_filter_through_project_5 '''
explain shape plan select * from (select id + 100 as a, id + 200 as b, id + 300 as c from t1) t where a > 999 and b > 999 limit 10
'''

qt_filter_through_project_6 '''
explain shape plan select * from (select id + random(1, 10) + 100 as a, id + 200 as b, id + 300 as c from t1) t where a > 999 and b > 999 limit 10
'''

qt_filter_through_project_7 '''
explain shape plan select * from (select id + random(1, 10) + 100 as a, id + random(1, 10) + 200 as b, id + random(1, 10) + 300 as c from t1) t where a > 999 and b > 999 limit 10
'''

qt_filter_through_project_8 '''
explain shape plan select * from (select id + 100 as a, id + 200 as b, id + 300 as c from t1) t where a + random(1, 10) > 999 and b + random(1, 10) > 999 limit 10
'''

qt_merge_project_1 '''
explain shape plan select a as b, a as c from (select id + 100 as a from t1) t
'''

qt_merge_project_2 '''
explain shape plan select a as b, a as c from (select id + random(1, 10) as a from t1) t
'''

qt_merge_project_3 '''
explain shape plan select a as b from (select id + random(1, 10) as a from t1) t
'''

qt_merge_project_4 '''
explain shape plan select a + 10 + a as b from (select id + random(1, 10) as a from t1) t
'''

qt_merge_project_5 '''
explain shape plan select a as b, a + 10 as c from (select id + random(1, 10) as a from t1) t
'''
}
Loading