This is an automated email from the ASF dual-hosted git repository.
morrysnow pushed a commit to branch branch-2.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-2.1 by this push:
new c4a10849d54 branch-2.1: [fix](nereids) fix nereids dead loop due to
simplify range output in-predicate's options in random order #47830 (#47828)
c4a10849d54 is described below
commit c4a10849d543f65c714a2ca7d419a0765cae5590
Author: yujun <[email protected]>
AuthorDate: Thu Feb 13 11:10:22 2025 +0800
branch-2.1: [fix](nereids) fix nereids dead loop due to simplify range
output in-predicate's options in random order #47830 (#47828)
### What problem does this PR solve?
cherry-pick: #47830
#45181 introduce a dead loop for nereids optimizer, then cause error
'Nereids cost too much time'.
simplify range process in-predicate steps as follow:
1. Extract in-predicate's options into a set S1;
2. Use a discrete value desc to hold the value, use a new set S2, and
S2.addAll(S1);
3. Turn discrete value desc back to a new in-predicate, the new
in-predicate's options add all S2.
Before #45181, S2 is an order set(TreeSet), so the result in-predicate's
options are always order. But this PR change S2 as a unorder
set(HashSet). Then will cause dead loop: in-predicate => S1 => new S2 =>
new in-predicate => new S1 => new S2 => ...
fix:
1. S1 use linked hash set;
2. don't use a new S2, just let S2 = S1;
then the result in-predicate's options' order will be kept.
---
.../rules/expression/rules/SimplifyRange.java | 55 ++++++++++++----------
.../expression/test_simplify_range.groovy | 34 +++++++++++++
2 files changed, 65 insertions(+), 24 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SimplifyRange.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SimplifyRange.java
index 7931cef1019..d5fd8e24783 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SimplifyRange.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SimplifyRange.java
@@ -49,7 +49,6 @@ import com.google.common.collect.Range;
import com.google.common.collect.Sets;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashMap;
@@ -231,10 +230,16 @@ public class SimplifyRange implements
ExpressionPatternRuleFactory {
public abstract ValueDesc intersect(ValueDesc other);
public static ValueDesc intersect(ExpressionRewriteContext context,
RangeValue range, DiscreteValue discrete) {
- DiscreteValue result = new DiscreteValue(context,
discrete.reference, discrete.toExpr);
- discrete.values.stream().filter(x ->
range.range.contains(x)).forEach(result.values::add);
- if (!result.values.isEmpty()) {
- return result;
+ // Since in-predicate's options is a list, the discrete values
need to kept options' order.
+ // If not keep options' order, the result in-predicate's option
list will not equals to
+ // the input in-predicate, later nereids will need to simplify the
new in-predicate,
+ // then cause dead loop.
+ Set<Literal> newValues = discrete.values.stream()
+ .filter(x -> range.range.contains(x))
+ .collect(Collectors.toCollection(
+ () ->
Sets.newLinkedHashSetWithExpectedSize(discrete.values.size())));
+ if (!newValues.isEmpty()) {
+ return new DiscreteValue(context, discrete.reference,
discrete.toExpr, newValues);
}
Expression originExpr = FoldConstantRuleOnFE.evaluate(
ExpressionUtils.and(range.toExpr, discrete.toExpr),
context);
@@ -246,7 +251,7 @@ public class SimplifyRange implements
ExpressionPatternRuleFactory {
public static ValueDesc range(ExpressionRewriteContext context,
ComparisonPredicate predicate) {
Literal value = (Literal) predicate.right();
if (predicate instanceof EqualTo) {
- return new DiscreteValue(context, predicate.left(), predicate,
value);
+ return new DiscreteValue(context, predicate.left(), predicate,
Sets.newHashSet(value));
}
RangeValue rangeValue = new RangeValue(context, predicate.left(),
predicate);
if (predicate instanceof GreaterThanEqual) {
@@ -263,8 +268,15 @@ public class SimplifyRange implements
ExpressionPatternRuleFactory {
}
public static ValueDesc discrete(ExpressionRewriteContext context,
InPredicate in) {
+ // Since in-predicate's options is a list, the discrete values
need to kept options' order.
+ // If not keep options' order, the result in-predicate's option
list will not equals to
+ // the input in-predicate, later nereids will need to simplify the
new in-predicate,
+ // then cause dead loop.
// Set<Literal> literals = (Set)
Utils.fastToImmutableSet(in.getOptions());
- Set<Literal> literals =
in.getOptions().stream().map(Literal.class::cast).collect(Collectors.toSet());
+ Set<Literal> literals = in.getOptions().stream()
+ .map(Literal.class::cast)
+ .collect(Collectors.toCollection(
+ () ->
Sets.newLinkedHashSetWithExpectedSize(in.getOptions().size())));
return new DiscreteValue(context, in.getCompareExpr(), in,
literals);
}
}
@@ -398,17 +410,12 @@ public class SimplifyRange implements
ExpressionPatternRuleFactory {
* a in (1,2,3) => [1,2,3]
*/
private static class DiscreteValue extends ValueDesc {
- Set<Literal> values;
+ final Set<Literal> values;
public DiscreteValue(ExpressionRewriteContext context,
- Expression reference, Expression toExpr, Literal... values) {
- this(context, reference, toExpr, Arrays.asList(values));
- }
-
- public DiscreteValue(ExpressionRewriteContext context,
- Expression reference, Expression toExpr, Collection<Literal>
values) {
+ Expression reference, Expression toExpr, Set<Literal> values) {
super(context, reference, toExpr);
- this.values = Sets.newHashSet(values);
+ this.values = values;
}
@Override
@@ -419,10 +426,11 @@ public class SimplifyRange implements
ExpressionPatternRuleFactory {
if (other instanceof DiscreteValue) {
Expression originExpr = FoldConstantRuleOnFE.evaluate(
ExpressionUtils.or(toExpr, other.toExpr), context);
- DiscreteValue discreteValue = new DiscreteValue(context,
reference, originExpr);
- discreteValue.values.addAll(((DiscreteValue) other).values);
- discreteValue.values.addAll(this.values);
- return discreteValue;
+ Set<Literal> otherValues = ((DiscreteValue) other).values;
+ Set<Literal> newValues =
Sets.newLinkedHashSetWithExpectedSize(values.size() + otherValues.size());
+ newValues.addAll(values);
+ newValues.addAll(otherValues);
+ return new DiscreteValue(context, reference, originExpr,
newValues);
}
if (other instanceof RangeValue) {
return union(context, (RangeValue) other, this, true);
@@ -441,13 +449,12 @@ public class SimplifyRange implements
ExpressionPatternRuleFactory {
if (other instanceof DiscreteValue) {
Expression originExpr = FoldConstantRuleOnFE.evaluate(
ExpressionUtils.and(toExpr, other.toExpr), context);
- DiscreteValue discreteValue = new DiscreteValue(context,
reference, originExpr);
- discreteValue.values.addAll(((DiscreteValue) other).values);
- discreteValue.values.retainAll(this.values);
- if (discreteValue.values.isEmpty()) {
+ Set<Literal> newValues = Sets.newLinkedHashSet(values);
+ newValues.retainAll(((DiscreteValue) other).values);
+ if (newValues.isEmpty()) {
return new EmptyValue(context, reference, originExpr);
} else {
- return discreteValue;
+ return new DiscreteValue(context, reference, originExpr,
newValues);
}
}
if (other instanceof RangeValue) {
diff --git
a/regression-test/suites/nereids_rules_p0/expression/test_simplify_range.groovy
b/regression-test/suites/nereids_rules_p0/expression/test_simplify_range.groovy
index c6d12f3e6bc..945036bcac2 100644
---
a/regression-test/suites/nereids_rules_p0/expression/test_simplify_range.groovy
+++
b/regression-test/suites/nereids_rules_p0/expression/test_simplify_range.groovy
@@ -17,6 +17,8 @@
suite('test_simplify_range') {
def tbl_1 = 'test_simplify_range_tbl_1'
+ sql "set disable_nereids_rules='PRUNE_EMPTY_PARTITION'"
+
sql "DROP TABLE IF EXISTS ${tbl_1} FORCE"
sql "CREATE TABLE ${tbl_1}(a DECIMAL(16,8), b INT) PROPERTIES
('replication_num' = '1')"
sql "INSERT INTO ${tbl_1} VALUES(null, 10)"
@@ -25,4 +27,36 @@ suite('test_simplify_range') {
result([[null]])
}
sql "DROP TABLE IF EXISTS ${tbl_1} FORCE"
+
+ sql """
+ create table ${tbl_1}
+ (
+ pk_id varchar(32) not null,
+ collect_time varchar(16),
+ item_id bigint null,
+ )
+ unique key(pk_id, collect_time)
+ auto partition by list(collect_time)()
+ distributed by hash(pk_id) buckets auto
+ properties (
+ "replication_allocation" = "tag.location.default: 1",
+ "enable_unique_key_merge_on_write" = "true"
+ );
+ """
+
+ // may cause dead loop because simplify range will output new in-predicate
for options in new order.
+ // test the in predicate again.
+ test {
+ sql """SELECT *
+ FROM ${tbl_1}
+ WHERE ITEM_ID IN
+ (1595012731474280448, 1595013220588847104,
1595013220634984448, 1595013220672733184, 1595013220718870528,
+ 1595013220760813568, 1595013220798562304, 1595013220832116736,
1595013220869865472, 1595013220911808512,
+ 1595013220945362944, 1595013220983111680, 1595013221025054720,
1595013221066997760, 1595013221104746496,
+ 1595013221146689536, 1595013221188632576, 1595013221222187008,
1595013221255741440, 1595013221289295872)
+ AND COLLECT_TIME IN ('2025-02-09','2025-02-10','2025-02-11') """
+ result([])
+ }
+
+ sql "DROP TABLE IF EXISTS ${tbl_1} FORCE"
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]