-
Notifications
You must be signed in to change notification settings - Fork 3.3k
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
[feat](nereids) add rewrite rule :EliminateGroupByKeyByUniform #43391
Changes from all commits
700904e
5ec0b25
75006f1
61457b2
1ba833c
0514ab1
5fdb4b5
c008b6c
c40ade0
8502775
097c42b
f30b8fd
a1a37b4
911228e
1dd2302
de48ef5
c882faa
6b9ed01
7fa5abd
c88d9ed
670ce37
f8525df
8e9af4f
e01152e
75edcd0
9a99316
637274a
9914312
a4d02cc
4cdf7a0
e915b8e
b934881
93a5155
1c2bb29
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 |
---|---|---|
|
@@ -17,18 +17,23 @@ | |
|
||
package org.apache.doris.nereids.properties; | ||
|
||
import org.apache.doris.nereids.trees.expressions.Expression; | ||
import org.apache.doris.nereids.trees.expressions.Slot; | ||
import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait; | ||
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; | ||
import org.apache.doris.nereids.util.ImmutableEqualSet; | ||
|
||
import com.google.common.collect.ImmutableMap; | ||
import com.google.common.collect.ImmutableSet; | ||
import com.google.common.collect.Sets; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashSet; | ||
import java.util.Iterator; | ||
import java.util.LinkedHashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
|
@@ -46,16 +51,16 @@ | |
public class DataTrait { | ||
|
||
public static final DataTrait EMPTY_TRAIT | ||
= new DataTrait(new NestedSet().toImmutable(), | ||
new NestedSet().toImmutable(), new ImmutableSet.Builder<FdItem>().build(), | ||
= new DataTrait(new UniqueDescription().toImmutable(), | ||
new UniformDescription().toImmutable(), new ImmutableSet.Builder<FdItem>().build(), | ||
ImmutableEqualSet.empty(), new FuncDepsDG.Builder().build()); | ||
private final NestedSet uniqueSet; | ||
private final NestedSet uniformSet; | ||
private final UniqueDescription uniqueSet; | ||
private final UniformDescription uniformSet; | ||
private final ImmutableSet<FdItem> fdItems; | ||
private final ImmutableEqualSet<Slot> equalSet; | ||
private final FuncDepsDG fdDg; | ||
|
||
private DataTrait(NestedSet uniqueSet, NestedSet uniformSet, ImmutableSet<FdItem> fdItems, | ||
private DataTrait(UniqueDescription uniqueSet, UniformDescription uniformSet, ImmutableSet<FdItem> fdItems, | ||
ImmutableEqualSet<Slot> equalSet, FuncDepsDG fdDg) { | ||
this.uniqueSet = uniqueSet; | ||
this.uniformSet = uniformSet; | ||
|
@@ -86,8 +91,7 @@ public boolean isUniform(Slot slot) { | |
} | ||
|
||
public boolean isUniform(Set<Slot> slotSet) { | ||
return !slotSet.isEmpty() | ||
&& uniformSet.slots.containsAll(slotSet); | ||
return uniformSet.contains(slotSet); | ||
} | ||
|
||
public boolean isUniqueAndNotNull(Slot slot) { | ||
|
@@ -102,11 +106,25 @@ public boolean isUniqueAndNotNull(Set<Slot> slotSet) { | |
} | ||
|
||
public boolean isUniformAndNotNull(Slot slot) { | ||
return !slot.nullable() && isUniform(slot); | ||
return uniformSet.isUniformAndNotNull(slot); | ||
} | ||
|
||
/** isUniformAndNotNull for slot set */ | ||
public boolean isUniformAndNotNull(ImmutableSet<Slot> slotSet) { | ||
return slotSet.stream().noneMatch(Slot::nullable) && isUniform(slotSet); | ||
for (Slot slot : slotSet) { | ||
if (!uniformSet.isUniformAndNotNull(slot)) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
public boolean isUniformAndHasConstValue(Slot slot) { | ||
return uniformSet.isUniformAndHasConstValue(slot); | ||
} | ||
|
||
public Optional<Expression> getUniformValue(Slot slot) { | ||
return uniformSet.slotUniformValue.get(slot); | ||
} | ||
|
||
public boolean isNullSafeEqual(Slot l, Slot r) { | ||
|
@@ -143,23 +161,23 @@ public String toString() { | |
* Builder of trait | ||
*/ | ||
public static class Builder { | ||
private final NestedSet uniqueSet; | ||
private final NestedSet uniformSet; | ||
private final UniqueDescription uniqueSet; | ||
private final UniformDescription uniformSet; | ||
private ImmutableSet<FdItem> fdItems; | ||
private final ImmutableEqualSet.Builder<Slot> equalSetBuilder; | ||
private final FuncDepsDG.Builder fdDgBuilder; | ||
|
||
public Builder() { | ||
uniqueSet = new NestedSet(); | ||
uniformSet = new NestedSet(); | ||
uniqueSet = new UniqueDescription(); | ||
uniformSet = new UniformDescription(); | ||
fdItems = new ImmutableSet.Builder<FdItem>().build(); | ||
equalSetBuilder = new ImmutableEqualSet.Builder<>(); | ||
fdDgBuilder = new FuncDepsDG.Builder(); | ||
} | ||
|
||
public Builder(DataTrait other) { | ||
this.uniformSet = new NestedSet(other.uniformSet); | ||
this.uniqueSet = new NestedSet(other.uniqueSet); | ||
this.uniformSet = new UniformDescription(other.uniformSet); | ||
this.uniqueSet = new UniqueDescription(other.uniqueSet); | ||
this.fdItems = ImmutableSet.copyOf(other.fdItems); | ||
equalSetBuilder = new ImmutableEqualSet.Builder<>(other.equalSet); | ||
fdDgBuilder = new FuncDepsDG.Builder(other.fdDg); | ||
|
@@ -173,6 +191,14 @@ public void addUniformSlot(DataTrait dataTrait) { | |
uniformSet.add(dataTrait.uniformSet); | ||
} | ||
|
||
public void addUniformSlotForOuterJoinNullableSide(DataTrait dataTrait) { | ||
uniformSet.addUniformSlotForOuterJoinNullableSide(dataTrait.uniformSet); | ||
} | ||
|
||
public void addUniformSlotAndLiteral(Slot slot, Expression literal) { | ||
uniformSet.add(slot, literal); | ||
} | ||
|
||
public void addUniqueSlot(Slot slot) { | ||
uniqueSet.add(slot); | ||
} | ||
|
@@ -261,8 +287,21 @@ public void addUniqueByEqualSet(Set<Slot> equalSet) { | |
* if there is a uniform slot in the equivalence set, then all slots of an equivalence set are uniform | ||
*/ | ||
public void addUniformByEqualSet(Set<Slot> equalSet) { | ||
if (uniformSet.isIntersect(uniformSet.slots, equalSet)) { | ||
uniformSet.slots.addAll(equalSet); | ||
List<Slot> intersectionList = uniformSet.slotUniformValue.keySet().stream() | ||
.filter(equalSet::contains) | ||
.collect(Collectors.toList()); | ||
if (intersectionList.isEmpty()) { | ||
return; | ||
} | ||
Expression expr = null; | ||
for (Slot slot : intersectionList) { | ||
if (uniformSet.slotUniformValue.get(slot).isPresent()) { | ||
expr = uniformSet.slotUniformValue.get(slot).get(); | ||
break; | ||
} | ||
} | ||
for (Slot equal : equalSet) { | ||
uniformSet.add(equal, expr); | ||
} | ||
} | ||
|
||
|
@@ -293,9 +332,11 @@ public List<Set<Slot>> getAllUniqueAndNotNull() { | |
*/ | ||
public List<Set<Slot>> getAllUniformAndNotNull() { | ||
List<Set<Slot>> res = new ArrayList<>(); | ||
for (Slot s : uniformSet.slots) { | ||
if (!s.nullable()) { | ||
res.add(ImmutableSet.of(s)); | ||
for (Map.Entry<Slot, Optional<Expression>> entry : uniformSet.slotUniformValue.entrySet()) { | ||
if (!entry.getKey().nullable()) { | ||
res.add(ImmutableSet.of(entry.getKey())); | ||
} else if (entry.getValue().isPresent() && !entry.getValue().get().nullable()) { | ||
res.add(ImmutableSet.of(entry.getKey())); | ||
} | ||
} | ||
return res; | ||
|
@@ -338,21 +379,21 @@ public void replaceFuncDepsBy(Map<Slot, Slot> replaceMap) { | |
} | ||
} | ||
|
||
static class NestedSet { | ||
static class UniqueDescription { | ||
Set<Slot> slots; | ||
Set<ImmutableSet<Slot>> slotSets; | ||
|
||
NestedSet() { | ||
UniqueDescription() { | ||
slots = new HashSet<>(); | ||
slotSets = new HashSet<>(); | ||
} | ||
|
||
NestedSet(NestedSet o) { | ||
UniqueDescription(UniqueDescription o) { | ||
this.slots = new HashSet<>(o.slots); | ||
this.slotSets = new HashSet<>(o.slotSets); | ||
} | ||
|
||
NestedSet(Set<Slot> slots, Set<ImmutableSet<Slot>> slotSets) { | ||
UniqueDescription(Set<Slot> slots, Set<ImmutableSet<Slot>> slotSets) { | ||
this.slots = slots; | ||
this.slotSets = slotSets; | ||
} | ||
|
@@ -408,9 +449,9 @@ public void add(ImmutableSet<Slot> slotSet) { | |
slotSets.add(slotSet); | ||
} | ||
|
||
public void add(NestedSet nestedSet) { | ||
slots.addAll(nestedSet.slots); | ||
slotSets.addAll(nestedSet.slotSets); | ||
public void add(UniqueDescription uniqueDescription) { | ||
slots.addAll(uniqueDescription.slots); | ||
slotSets.addAll(uniqueDescription.slotSets); | ||
} | ||
|
||
public boolean isIntersect(Set<Slot> set1, Set<Slot> set2) { | ||
|
@@ -446,8 +487,120 @@ public void replace(Map<Slot, Slot> replaceMap) { | |
.collect(Collectors.toSet()); | ||
} | ||
|
||
public NestedSet toImmutable() { | ||
return new NestedSet(ImmutableSet.copyOf(slots), ImmutableSet.copyOf(slotSets)); | ||
public UniqueDescription toImmutable() { | ||
return new UniqueDescription(ImmutableSet.copyOf(slots), ImmutableSet.copyOf(slotSets)); | ||
} | ||
} | ||
|
||
static class UniformDescription { | ||
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. please explain the usage of UniformDescription by example |
||
// slot and its uniform expression(literal or const expression) | ||
// some slot can get uniform values, others can not. | ||
// e.g.select a from t where a=10 group by a, b; | ||
// in LogicalAggregate, a UniformDescription with map {a : 10} can be obtained. | ||
// which means a is uniform and the uniform value is 10. | ||
Map<Slot, Optional<Expression>> slotUniformValue; | ||
|
||
public UniformDescription() { | ||
slotUniformValue = new LinkedHashMap<>(); | ||
} | ||
|
||
public UniformDescription(UniformDescription ud) { | ||
slotUniformValue = new LinkedHashMap<>(ud.slotUniformValue); | ||
} | ||
|
||
public UniformDescription(Map<Slot, Optional<Expression>> slotUniformValue) { | ||
this.slotUniformValue = slotUniformValue; | ||
} | ||
|
||
public UniformDescription toImmutable() { | ||
return new UniformDescription(ImmutableMap.copyOf(slotUniformValue)); | ||
} | ||
|
||
public boolean isEmpty() { | ||
return slotUniformValue.isEmpty(); | ||
} | ||
|
||
public boolean contains(Slot slot) { | ||
return slotUniformValue.containsKey(slot); | ||
} | ||
|
||
public boolean contains(Set<Slot> slots) { | ||
return !slots.isEmpty() && slotUniformValue.keySet().containsAll(slots); | ||
} | ||
|
||
public void add(Slot slot) { | ||
slotUniformValue.putIfAbsent(slot, Optional.empty()); | ||
} | ||
|
||
public void add(Set<Slot> slots) { | ||
for (Slot s : slots) { | ||
slotUniformValue.putIfAbsent(s, Optional.empty()); | ||
} | ||
} | ||
|
||
public void add(UniformDescription ud) { | ||
slotUniformValue.putAll(ud.slotUniformValue); | ||
for (Map.Entry<Slot, Optional<Expression>> entry : ud.slotUniformValue.entrySet()) { | ||
add(entry.getKey(), entry.getValue().orElse(null)); | ||
} | ||
} | ||
|
||
public void add(Slot slot, Expression literal) { | ||
if (null == literal) { | ||
slotUniformValue.putIfAbsent(slot, Optional.empty()); | ||
} else { | ||
slotUniformValue.put(slot, Optional.of(literal)); | ||
} | ||
} | ||
|
||
public void addUniformSlotForOuterJoinNullableSide(UniformDescription ud) { | ||
for (Map.Entry<Slot, Optional<Expression>> entry : ud.slotUniformValue.entrySet()) { | ||
if ((!entry.getValue().isPresent() && entry.getKey().nullable()) | ||
|| (entry.getValue().isPresent() && entry.getValue().get() instanceof NullLiteral)) { | ||
add(entry.getKey(), entry.getValue().orElse(null)); | ||
} | ||
} | ||
} | ||
|
||
public void removeNotContain(Set<Slot> slotSet) { | ||
if (slotSet.isEmpty()) { | ||
return; | ||
} | ||
Map<Slot, Optional<Expression>> newSlotUniformValue = new LinkedHashMap<>(); | ||
for (Map.Entry<Slot, Optional<Expression>> entry : slotUniformValue.entrySet()) { | ||
if (slotSet.contains(entry.getKey())) { | ||
newSlotUniformValue.put(entry.getKey(), entry.getValue()); | ||
} | ||
} | ||
this.slotUniformValue = newSlotUniformValue; | ||
} | ||
|
||
public void replace(Map<Slot, Slot> replaceMap) { | ||
Map<Slot, Optional<Expression>> newSlotUniformValue = new LinkedHashMap<>(); | ||
for (Map.Entry<Slot, Optional<Expression>> entry : slotUniformValue.entrySet()) { | ||
Slot newKey = replaceMap.getOrDefault(entry.getKey(), entry.getKey()); | ||
newSlotUniformValue.put(newKey, entry.getValue()); | ||
} | ||
slotUniformValue = newSlotUniformValue; | ||
} | ||
|
||
// The current implementation logic is: if a slot key exists in map slotUniformValue, | ||
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. does it bring the behavior change for the current uniform judgement, in current related rules? 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. It will expand slightly. For example, the original isUniformAndNotNull interface, when given the a=1 column a as input, would not return true. After modification, it will return true. The uniform range will not be narrowed, and the meaning of uniform remains unchanged — still indicating a uniform value (which may include additional nulls). |
||
// its value is present and is not nullable, | ||
// or if a slot key exists in map slotUniformValue and the slot is not nullable | ||
// it indicates that this slot is uniform and not null. | ||
public boolean isUniformAndNotNull(Slot slot) { | ||
return slotUniformValue.containsKey(slot) | ||
&& (!slot.nullable() || slotUniformValue.get(slot).isPresent() | ||
&& !slotUniformValue.get(slot).get().nullable()); | ||
} | ||
|
||
public boolean isUniformAndHasConstValue(Slot slot) { | ||
return slotUniformValue.containsKey(slot) && slotUniformValue.get(slot).isPresent(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "{" + slotUniformValue + "}"; | ||
} | ||
} | ||
} |
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.
if this place is changed to specific UniformDescription, is the original NestedSet necessary to be changed to UniqueXXX?
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.
Unnecessary. this pr use UniformDescription to record const value expression. For unique, it is unnecessary.
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.
I mean the NestedSet is only used to desc the UniqueSet, may be just recover it to the narrowed down meaning.