Skip to content

Commit

Permalink
[fix](nereids) partition prune fails in case of NOT expression (apach…
Browse files Browse the repository at this point in the history
…e#27047)

* handle not and add regression test
  • Loading branch information
englefly committed Nov 23, 2023
1 parent 913df76 commit ff18510
Show file tree
Hide file tree
Showing 2 changed files with 328 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -342,21 +342,32 @@ public EvaluateRangeResult visitEqualTo(EqualTo equalTo, EvaluateRangeInput cont
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<Slot, ColumnRange> 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();
if (isPartitionSlot(slot)) {
Map<Slot, ColumnRange> 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;
}
Expand All @@ -379,6 +390,7 @@ public EvaluateRangeResult visitInPredicate(InPredicate inPredicate, EvaluateRan
Map<Slot, ColumnRange> slotRanges = result.childrenResult.get(0).columnRanges;
result = intersectSlotRange(result, slotRanges, slot, unionLiteralRange);
}
result = result.withRejectNot(false);
return result;
}

Expand All @@ -388,14 +400,15 @@ public EvaluateRangeResult visitIsNull(IsNull isNull, EvaluateRangeInput context
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;
}
Expand Down Expand Up @@ -430,12 +443,16 @@ public EvaluateRangeResult visitOr(Or or, EvaluateRangeInput context) {
@Override
public EvaluateRangeResult visitNot(Not not, EvaluateRangeInput context) {
EvaluateRangeResult result = evaluateChildrenThenThis(not, context);

Map<Slot, ColumnRange> 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<Slot, ColumnRange> newRanges = Maps.newHashMap();
for (Map.Entry<Slot, ColumnRange> 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);
}

Expand Down Expand Up @@ -658,11 +675,37 @@ public static class EvaluateRangeResult {
private final Map<Slot, ColumnRange> columnRanges;
private final List<EvaluateRangeResult> 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<Slot, ColumnRange> columnRanges,
List<EvaluateRangeResult> childrenResult) {
List<EvaluateRangeResult> childrenResult, boolean rejectNot) {
this.result = result;
this.columnRanges = columnRanges;
this.childrenResult = childrenResult;
this.rejectNot = rejectNot;
}

public EvaluateRangeResult(Expression result, Map<Slot, ColumnRange> columnRanges,
List<EvaluateRangeResult> 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;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,275 @@
// 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_multi_range_partition") {
String db = context.config.getDbNameByFile(context.file)
sql "use ${db}"
sql "SET enable_nereids_planner=true"
sql "SET enable_fallback_to_original_planner=false"
sql "set partition_pruning_expand_threshold=10;"
sql "drop table if exists pt"
sql """
CREATE TABLE `pt` (
`k1` int(11) NULL COMMENT "",
`k2` int(11) NULL COMMENT "",
`k3` int(11) NULL COMMENT ""
)
PARTITION BY RANGE(`k1`, `k2`)
(PARTITION p1 VALUES LESS THAN ("3", "1"),
PARTITION p2 VALUES [("3", "1"), ("7", "10")),
PARTITION p3 VALUES [("7", "10"), ("10", "15")))
DISTRIBUTED BY HASH(`k1`) BUCKETS 10
PROPERTIES ('replication_num' = '1');
"""
sql "insert into pt values (7, 0, 0);"
sql "insert into pt(k1) values (7);"
sql "insert into pt(k1) values (0);"
sql "insert into pt values (7, 11, 0);"

// basic test
explain{
sql "select * from pt where k1=7;"
contains "partitions=2/3 (p2,p3)"
}

explain{
sql "select * from pt where k1=10;"
contains "partitions=1/3 (p3)"
}

explain{
sql "select * from pt where k1=11;"
contains "VEMPTYSET"
}

explain{
sql "select * from pt where k1=-1;"
contains "partitions=1/3 (p1)"
}

// ===============function(part_key)===================
explain{
sql "select * from pt where 2*k1=20; --漏裁 p1"
contains "partitions=2/3 (p1,p3)"
}
explain{
sql "select * from pt where 2*(k1+1)=22; --漏裁 p1"
contains "partitions=2/3 (p1,p3)"
}
explain{
sql "select * from pt where sin(k1)=0"
contains "artitions=3/3 (p1,p2,p3)"
}

// fix BUG: p1 missed
explain{
sql "select * from pt where sin(k1)<>0"
contains "partitions=3/3 (p1,p2,p3)"
}

// ============= in predicate ======================
explain{
sql "select * from pt where k1 in (7, 8);"
contains "partitions=2/3 (p2,p3)"
}

explain{
sql "select * from pt where k1 in (15, 18);"
contains "VEMPTYSET"
}

// =========== is null ===================
explain{
sql "select * from pt where k1 is null"
contains "partitions=1/3 (p1)"
}

// fix BUG: p1 missed
explain{
sql "select * from pt where k1 is not null"
contains "partitions=3/3 (p1,p2,p3)"
}

//======== the second partition key =========
explain{
sql "select * from pt where k1=7 and (k1>k2);"
contains "partitions=1/3 (p2)"
}

explain {
sql "select * from pt where k1=7 and not (k1>k2);"
contains "partitions=2/3 (p2,p3)"
}

// p3 NOT pruned
explain {
sql "select * from pt where k1=7 and (k1<k2);"
contains "partitions=2/3 (p2,p3)"
}

explain {
sql "select * from pt where k1=7 and k1=k2"
contains "partitions=1/3 (p2)"
}

//fix BUG: p2 missed
explain {
sql "select * from pt where k1=7 and k1<>k2"
contains "partitions=2/3 (p2,p3)"
}

//p3 NOT pruned
explain {
sql "select * from pt where k1=7 and (k1 > cast(k2 as bigint));"
contains "partitions=2/3 (p2,p3)"
}

//fix BUG: p2 missed
explain {
sql "select * from pt where k1=7 and not (k2 is null);"
contains "partitions=2/3 (p2,p3)"
}

//p3 NOT pruned
explain {
sql "select * from pt where k1=7 and not (k2 is not null);"
contains "partitions=2/3 (p2,p3)"
}

//fix BUG: p2 missed
explain {
sql "select * from pt where k1=7 and k2 not in (1, 2);"
contains "partitions=2/3 (p2,p3)"
}

explain {
sql "select * from pt where k1=7 and k2 in (1, 12);"
contains "partitions=2/3 (p2,p3)"
}

//fix BUG: p2,p3 pruned
explain {
sql "select * from pt where k1=7 and k2 not in (1, 12)"
contains "partitions=2/3 (p2,p3)"
}

explain {
sql" select * from pt where k1=7 and k2 in (0);"
contains "partitions=1/3 (p2)"
}

explain {
sql "select * from pt where k1=7 and k2 in (11)"
contains "partitions=1/3 (p3)"
}

explain {
sql "select * from pt where k1=7 and k2 in (null);"
contains "partitions=2/3 (p2,p3)"
}

explain {
sql "select * from pt where k1=7 and k2 not in (null);"
contains "partitions=2/3 (p2,p3)"
}

explain {
sql "select * from pt where k1=10 and k2 in (30);"
contains "VEMPTYSET"
}

explain {
sql "select * from pt where k1=7 and k1 > k3;"
contains "partitions=2/3 (p2,p3)"
}

explain {
sql "select * from pt where k1=7 and k1 <> k3;"
contains "partitions=2/3 (p2,p3)"
}

explain {
sql "select * from pt where k2 in (null);"
contains "partitions=3/3 (p1,p2,p3)"
}

// p1/p2/p3 NOT pruned
explain {
sql "select * from pt where k2 not in (null)"
contains "partitions=3/3 (p1,p2,p3)"
}

explain {
sql "select * from pt where k2 in (0)"
contains "partitions=3/3 (p1,p2,p3)"
}

explain {
sql "select * from pt where k2 > 100"
contains "partitions=3/3 (p1,p2,p3)"
}

explain {
sql "select * from pt where k2 < -1"
contains "partitions=3/3 (p1,p2,p3)"
}

explain {
sql "select * from pt where k1=7 and (k3 is null)"
contains "partitions=2/3 (p2,p3)"
}

explain {
sql "select * from pt where k1=7 and not (k3 is null);"
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)"
}
}

0 comments on commit ff18510

Please sign in to comment.