diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/OneRangePartitionEvaluator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/OneRangePartitionEvaluator.java index f4aa327647..b4fb931ec1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/OneRangePartitionEvaluator.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/OneRangePartitionEvaluator.java @@ -346,13 +346,16 @@ public class OneRangePartitionEvaluator if (!(result.result instanceof EqualTo)) { return result; } - equalTo = (EqualTo) result.result; + boolean isRejectNot = false; if (equalTo.left() instanceof Slot && equalTo.right() instanceof Literal) { Slot slot = (Slot) equalTo.left(); if (isPartitionSlot(slot)) { Map leftColumnRanges = result.childrenResult.get(0).columnRanges; ColumnRange atLeastRange = ColumnRange.singleton((Literal) equalTo.right()); result = intersectSlotRange(result, leftColumnRanges, slot, atLeastRange); + if (leftColumnRanges.get(slot).isSingleton()) { + isRejectNot = true; + } } } else if (equalTo.left() instanceof Literal && equalTo.right() instanceof Slot) { Slot slot = (Slot) equalTo.right(); @@ -360,7 +363,15 @@ public class OneRangePartitionEvaluator Map rightColumnRanges = result.childrenResult.get(1).columnRanges; ColumnRange atMostRange = ColumnRange.singleton((Literal) equalTo.left()); result = intersectSlotRange(result, rightColumnRanges, slot, atMostRange); + if (rightColumnRanges.get(slot).isSingleton()) { + isRejectNot = true; + } } + } else { + isRejectNot = false; + } + if (!isRejectNot) { + result = result.withRejectNot(false); } return result; } @@ -383,6 +394,7 @@ public class OneRangePartitionEvaluator Map slotRanges = result.childrenResult.get(0).columnRanges; result = intersectSlotRange(result, slotRanges, slot, unionLiteralRange); } + result = result.withRejectNot(false); return result; } @@ -392,14 +404,15 @@ public class OneRangePartitionEvaluator if (!(result.result instanceof IsNull)) { return result; } - + result = result.withRejectNot(false); Expression child = isNull.child(); if (!(child instanceof Slot) || !isPartitionSlot((Slot) child)) { return result; } if (!partitionSlotContainsNull.get((Slot) child)) { - return new EvaluateRangeResult(BooleanLiteral.FALSE, result.columnRanges, result.childrenResult); + return new EvaluateRangeResult(BooleanLiteral.FALSE, + result.columnRanges, result.childrenResult, false); } return result; } @@ -434,12 +447,16 @@ public class OneRangePartitionEvaluator @Override public EvaluateRangeResult visitNot(Not not, EvaluateRangeInput context) { EvaluateRangeResult result = evaluateChildrenThenThis(not, context); - - Map newRanges = result.childrenResult.get(0).columnRanges.entrySet() - .stream() - .map(slotToRange -> Pair.of(slotToRange.getKey(), slotToRange.getValue().complete())) - .collect(ImmutableMap.toImmutableMap(Pair::key, Pair::value)); - result = new EvaluateRangeResult(result.result, newRanges, result.childrenResult); + if (result.isRejectNot()) { + Map newRanges = Maps.newHashMap(); + for (Map.Entry entry : result.childrenResult.get(0).columnRanges.entrySet()) { + Slot slot = entry.getKey(); + ColumnRange childRange = entry.getValue(); + ColumnRange partitionRange = result.columnRanges.get(slot); + newRanges.put(slot, partitionRange.intersect(childRange.complete())); + } + result = new EvaluateRangeResult(result.result, newRanges, result.childrenResult); + } return returnFalseIfExistEmptyRange(result); } @@ -662,11 +679,37 @@ public class OneRangePartitionEvaluator private final Map columnRanges; private final List childrenResult; + // rejectNot = true, if \exist e \in R, pred(e)=true, then we have \forAll e \in R, !pred(e)=false + // that is, if pred holds true over R, then !pred does not hold true over R. + // example 1. rejectNot=false + // R=(1,10), pred: k = 5. "k = 5" holds true over R, and "NOT k = 5" holds true over R. + // example 2. rejectNot=false + // R=(1,10), pred: k = 11. "k=10" dose not holds over R + // example 3. rejectNot=false + // R=(1,10), pred: k in (4, 5). "k in (4, 5)" holds true over R, and "NOT k in (4, 5)" holds over R + // example 3. rejectNot=true + // R=(1,10), pred: k < 11. "k<11" holds true over R, and "NOT k<11" dose not hold over R + private final boolean rejectNot; + public EvaluateRangeResult(Expression result, Map columnRanges, - List childrenResult) { + List childrenResult, boolean rejectNot) { this.result = result; this.columnRanges = columnRanges; this.childrenResult = childrenResult; + this.rejectNot = rejectNot; + } + + public EvaluateRangeResult(Expression result, Map columnRanges, + List childrenResult) { + this(result, columnRanges, childrenResult, childrenResult.stream().allMatch(r -> r.isRejectNot())); + } + + public EvaluateRangeResult withRejectNot(boolean rejectNot) { + return new EvaluateRangeResult(result, columnRanges, childrenResult, rejectNot); + } + + public boolean isRejectNot() { + return rejectNot; } } diff --git a/regression-test/suites/nereids_rules_p0/partition_prune/test_multi_range_partition.groovy b/regression-test/suites/nereids_rules_p0/partition_prune/test_multi_range_partition.groovy index 12304b4a71..afb1026381 100644 --- a/regression-test/suites/nereids_rules_p0/partition_prune/test_multi_range_partition.groovy +++ b/regression-test/suites/nereids_rules_p0/partition_prune/test_multi_range_partition.groovy @@ -75,10 +75,10 @@ suite("test_multi_range_partition") { contains "artitions=3/3 (p1,p2,p3)" } - // BUG: p1 missed + // fix BUG: p1 missed explain{ sql "select * from pt where sin(k1)<>0" - contains "partitions=2/3 (p2,p3)" + contains "partitions=3/3 (p1,p2,p3)" } // ============= in predicate ====================== @@ -98,10 +98,10 @@ suite("test_multi_range_partition") { contains "partitions=1/3 (p1)" } - // BUG: p1 missed + // fix BUG: p1 missed explain{ sql "select * from pt where k1 is not null" - contains "partitions=2/3 (p2,p3)" + contains "partitions=3/3 (p1,p2,p3)" } //======== the second partition key ========= @@ -126,10 +126,10 @@ suite("test_multi_range_partition") { contains "partitions=1/3 (p2)" } - //BUG: p2 missed + //fix BUG: p2 missed explain { sql "select * from pt where k1=7 and k1<>k2" - contains "partitions=1/3 (p3)" + contains "partitions=2/3 (p2,p3)" } //p3 NOT pruned @@ -138,10 +138,10 @@ suite("test_multi_range_partition") { contains "partitions=2/3 (p2,p3)" } - //BUG: p2 missed + //fix BUG: p2 missed explain { sql "select * from pt where k1=7 and not (k2 is null);" - contains "VEMPTYSET" + contains "partitions=2/3 (p2,p3)" } //p3 NOT pruned @@ -150,10 +150,10 @@ suite("test_multi_range_partition") { contains "partitions=2/3 (p2,p3)" } - //BUG: p2 missed + //fix BUG: p2 missed explain { sql "select * from pt where k1=7 and k2 not in (1, 2);" - contains "partitions=1/3 (p3)" + contains "partitions=2/3 (p2,p3)" } explain { @@ -161,10 +161,10 @@ suite("test_multi_range_partition") { contains "partitions=2/3 (p2,p3)" } - //BUG: p2,p3 pruned + //fix BUG: p2,p3 pruned explain { sql "select * from pt where k1=7 and k2 not in (1, 12)" - contains "VEMPTYSET" + contains "partitions=2/3 (p2,p3)" } explain { @@ -238,4 +238,38 @@ suite("test_multi_range_partition") { contains "partitions=2/3 (p2,p3)" } + + // test if a range is not sliced to multiple single point + // for example: range [3,7) is sliced to [3,3], [4,4],[5,5],[6,6] + sql "set partition_pruning_expand_threshold=1;" + + explain { + sql "select * from pt where k1 < 5;" + contains "partitions=2/3 (p1,p2)" + } + + explain { + sql "select * from pt where not k1 < 5;" + contains "partitions=2/3 (p2,p3)" + } + + explain { + sql "select * from pt where k2 < 5;" + contains "partitions=3/3 (p1,p2,p3)" + } + + explain { + sql "select * from pt where not k2 < 5;" + contains "partitions=3/3 (p1,p2,p3)" + } + + explain { + sql "select * from pt where k3 < 5;" + contains "partitions=3/3 (p1,p2,p3)" + } + + explain { + sql "select * from pt where not k3 < 5;" + contains "partitions=3/3 (p1,p2,p3)" + } }