-
Notifications
You must be signed in to change notification settings - Fork 3.5k
[enhance](nereids) add eliminate order by key by data trait #46225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b22a436
2044dac
8bf251e
58af56f
f48fe7d
1bada3e
825637a
cc2bd5a
13500bb
dd6831a
19266c5
913c2f5
ff2481a
953db93
057ef38
d8766b2
a5cfd54
6979ea6
76edf05
3fa06ea
3505cf8
d5d783c
ac6dd2c
abf0d22
5db1548
fd6fa17
56826f5
8feb8f8
b0a5766
a1af3db
446e2db
87cc985
8ed9e50
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
// 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. | ||
|
||
package org.apache.doris.nereids.rules.rewrite; | ||
|
||
import org.apache.doris.catalog.Type; | ||
import org.apache.doris.nereids.annotation.DependsRules; | ||
import org.apache.doris.nereids.exceptions.AnalysisException; | ||
import org.apache.doris.nereids.properties.DataTrait; | ||
import org.apache.doris.nereids.properties.FuncDeps; | ||
import org.apache.doris.nereids.properties.OrderKey; | ||
import org.apache.doris.nereids.rules.Rule; | ||
import org.apache.doris.nereids.rules.RuleType; | ||
import org.apache.doris.nereids.trees.expressions.Alias; | ||
import org.apache.doris.nereids.trees.expressions.Expression; | ||
import org.apache.doris.nereids.trees.expressions.NamedExpression; | ||
import org.apache.doris.nereids.trees.expressions.OrderExpression; | ||
import org.apache.doris.nereids.trees.expressions.Slot; | ||
import org.apache.doris.nereids.trees.expressions.WindowExpression; | ||
import org.apache.doris.nereids.trees.plans.Plan; | ||
import org.apache.doris.nereids.trees.plans.logical.LogicalSort; | ||
import org.apache.doris.nereids.trees.plans.logical.LogicalWindow; | ||
|
||
import com.google.common.collect.ImmutableList; | ||
import com.google.common.collect.ImmutableSet; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Set; | ||
|
||
/** | ||
* 1.eliminate by duplicate | ||
* select a from t1 order by a, a; | ||
* -> | ||
* select a from t1 order by a; | ||
* 2.eliminate by function dependency | ||
* select a from t1 order by a, a+1; | ||
* select a from t1 order by a, abs(a) ; | ||
* select a from t1 where a=c order by a,c | ||
* -> | ||
* select a from t1 order by a; | ||
* 3.eliminate by uniform | ||
* select a,b,c from test where a=1 order by a; | ||
* -> | ||
* select a,b,c from test where a=1; | ||
* */ | ||
@DependsRules({ | ||
NormalizeSort.class, | ||
ExtractAndNormalizeWindowExpression.class, | ||
CheckAndStandardizeWindowFunctionAndFrame.class}) | ||
public class EliminateOrderByKey implements RewriteRuleFactory { | ||
@Override | ||
public List<Rule> buildRules() { | ||
return ImmutableList.of( | ||
logicalSort(any()).then(EliminateOrderByKey::eliminateSort).toRule(RuleType.ELIMINATE_ORDER_BY_KEY), | ||
logicalWindow(any()).then(EliminateOrderByKey::eliminateWindow) | ||
.toRule(RuleType.ELIMINATE_ORDER_BY_KEY)); | ||
} | ||
|
||
private static Plan eliminateWindow(LogicalWindow<Plan> window) { | ||
DataTrait dataTrait = window.child().getLogicalProperties().getTrait(); | ||
List<NamedExpression> newNamedExpressions = new ArrayList<>(); | ||
boolean changed = false; | ||
for (NamedExpression expr : window.getWindowExpressions()) { | ||
Alias alias = (Alias) expr; | ||
WindowExpression windowExpression = (WindowExpression) alias.child(); | ||
List<OrderExpression> orderExpressions = windowExpression.getOrderKeys(); | ||
if (orderExpressions.stream().anyMatch(( | ||
orderKey -> orderKey.getDataType().isOnlyMetricType()))) { | ||
throw new AnalysisException(Type.OnlyMetricTypeErrorMsg); | ||
} | ||
List<OrderKey> orderKeys = new ArrayList<>(); | ||
for (OrderExpression orderExpression : orderExpressions) { | ||
orderKeys.add(orderExpression.getOrderKey()); | ||
} | ||
List<OrderKey> retainExpression = eliminate(dataTrait, orderKeys); | ||
if (retainExpression.size() == orderKeys.size()) { | ||
newNamedExpressions.add(expr); | ||
continue; | ||
} | ||
changed = true; | ||
List<OrderExpression> newOrderExpressions = new ArrayList<>(); | ||
for (OrderKey orderKey : retainExpression) { | ||
newOrderExpressions.add(new OrderExpression(orderKey)); | ||
} | ||
WindowExpression newWindowExpression = windowExpression.withOrderKeys(newOrderExpressions); | ||
newNamedExpressions.add(alias.withChildren(ImmutableList.of(newWindowExpression))); | ||
} | ||
return changed ? window.withExpressionsAndChild(newNamedExpressions, window.child()) : window; | ||
} | ||
|
||
private static Plan eliminateSort(LogicalSort<Plan> sort) { | ||
DataTrait dataTrait = sort.child().getLogicalProperties().getTrait(); | ||
List<OrderKey> retainExpression = eliminate(dataTrait, sort.getOrderKeys()); | ||
if (retainExpression.isEmpty()) { | ||
return sort.child(); | ||
} else if (retainExpression.size() == sort.getOrderKeys().size()) { | ||
return sort; | ||
} | ||
return sort.withOrderKeys(retainExpression); | ||
} | ||
|
||
private static List<OrderKey> eliminate(DataTrait dataTrait, List<OrderKey> inputOrderKeys) { | ||
Set<Slot> validSlots = new HashSet<>(); | ||
for (OrderKey inputOrderKey : inputOrderKeys) { | ||
Expression expr = inputOrderKey.getExpr(); | ||
if (!(expr instanceof Slot)) { | ||
return inputOrderKeys; | ||
} | ||
validSlots.add((Slot) expr); | ||
validSlots.addAll(dataTrait.calEqualSet((Slot) expr)); | ||
} | ||
FuncDeps funcDeps = dataTrait.getAllValidFuncDeps(validSlots); | ||
Map<Set<Slot>, Set<Set<Slot>>> redges = funcDeps.getREdges(); | ||
|
||
List<OrderKey> retainExpression = new ArrayList<>(); | ||
Set<Expression> orderExprWithEqualSet = new HashSet<>(); | ||
for (OrderKey inputOrderKey : inputOrderKeys) { | ||
Expression expr = inputOrderKey.getExpr(); | ||
// eliminate by duplicate | ||
if (orderExprWithEqualSet.contains(expr)) { | ||
continue; | ||
} | ||
// eliminate by uniform | ||
if (dataTrait.isUniformAndNotNull((Slot) expr)) { | ||
orderExprWithEqualSet.add(expr); | ||
orderExprWithEqualSet.addAll(dataTrait.calEqualSet((Slot) expr)); | ||
continue; | ||
} | ||
// eliminate by fd | ||
Set<Slot> set = ImmutableSet.of((Slot) expr); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how about expr is a + 1, and can't be located in redges? Is there necessary to extract all referenced column slot, etc? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or the expansion should do in validSlots collection? |
||
boolean shouldRetain = true; | ||
if (redges.containsKey(set)) { | ||
Set<Set<Slot>> dominants = redges.get(set); | ||
for (Set<Slot> dominant : dominants) { | ||
if (orderExprWithEqualSet.containsAll(dominant)) { | ||
shouldRetain = false; | ||
break; | ||
} | ||
} | ||
} | ||
if (!shouldRetain) { | ||
continue; | ||
} | ||
retainExpression.add(inputOrderKey); | ||
orderExprWithEqualSet.add(expr); | ||
orderExprWithEqualSet.addAll(dataTrait.calEqualSet((Slot) expr)); | ||
} | ||
return retainExpression; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,6 +18,7 @@ | |
package org.apache.doris.nereids.trees.plans.logical; | ||
|
||
import org.apache.doris.catalog.Column; | ||
import org.apache.doris.catalog.KeysType; | ||
import org.apache.doris.catalog.MTMV; | ||
import org.apache.doris.catalog.OlapTable; | ||
import org.apache.doris.catalog.Table; | ||
|
@@ -36,6 +37,7 @@ | |
import org.apache.doris.nereids.trees.plans.algebra.OlapScan; | ||
import org.apache.doris.nereids.trees.plans.visitor.PlanVisitor; | ||
import org.apache.doris.nereids.util.Utils; | ||
import org.apache.doris.qe.ConnectContext; | ||
|
||
import com.google.common.annotations.VisibleForTesting; | ||
import com.google.common.base.Preconditions; | ||
|
@@ -536,6 +538,12 @@ AGGREGATE KEY (siteid,citycode,username) | |
builder.addUniqueSlot(originalPlan.getLogicalProperties().getTrait()); | ||
builder.replaceUniqueBy(constructReplaceMap(mtmv)); | ||
} else if (getTable().getKeysType().isAggregationFamily() && !getTable().isRandomDistribution()) { | ||
// When skipDeleteBitmap is set to true, in the unique model, rows that are replaced due to having the same | ||
// unique key will also be read. As a result, the uniqueness of the unique key cannot be guaranteed. | ||
if (ConnectContext.get().getSessionVariable().skipDeleteBitmap | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what's the story here? and pls add comments. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
&& getTable().getKeysType() == KeysType.UNIQUE_KEYS) { | ||
return; | ||
} | ||
ImmutableSet.Builder<Slot> uniqSlots = ImmutableSet.builderWithExpectedSize(outputSet.size()); | ||
for (Slot slot : outputSet) { | ||
if (!(slot instanceof SlotReference)) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -277,9 +277,14 @@ public void computeEqualSet(DataTrait.Builder builder) { | |
public void computeFd(DataTrait.Builder builder) { | ||
builder.addFuncDepsDG(child().getLogicalProperties().getTrait()); | ||
for (NamedExpression expr : getProjects()) { | ||
if (!expr.isSlot()) { | ||
builder.addDeps(expr.getInputSlots(), ImmutableSet.of(expr.toSlot())); | ||
if (!(expr instanceof Alias)) { | ||
continue; | ||
} | ||
// a+random(1,10) should continue, otherwise the a(determinant), a+random(1,10) (dependency) will be added. | ||
if (expr.containsNonfoldable()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. pls add comments and cases to explain the handling logic There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done |
||
continue; | ||
} | ||
builder.addDeps(expr.getInputSlots(), ImmutableSet.of(expr.toSlot())); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.