This is an automated email from the ASF dual-hosted git repository.
yiguolei pushed a commit to branch branch-4.0
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-4.0 by this push:
new 2a264808005 branch-4.0: [feature](partition prune) support remove
predicates that are always true after partition prune in list partition #57169
#57688 #57817 (#58040)
2a264808005 is described below
commit 2a2648080058d6bad5039d6b4ffcd11c307a23c3
Author: feiniaofeiafei <[email protected]>
AuthorDate: Thu Nov 27 16:04:01 2025 +0800
branch-4.0: [feature](partition prune) support remove predicates that are
always true after partition prune in list partition #57169 #57688 #57817
(#58040)
picked from #57169 #57688 #57817
---
.../org/apache/doris/nereids/StatementContext.java | 9 +
.../nereids/load/NereidsLoadPlanInfoCollector.java | 3 +-
.../rules/expression/rules/PartitionPruner.java | 131 ++++-
.../rules/rewrite/PruneFileScanPartition.java | 8 +-
.../rules/rewrite/PruneOlapScanPartition.java | 41 +-
.../trees/plans/commands/DeleteFromCommand.java | 7 +-
.../trees/plans/commands/ExplainCommand.java | 3 +
.../java/org/apache/doris/qe/SessionVariable.java | 11 +
.../nereids/mv/OptimizeGetAvailableMvsTest.java | 4 +-
.../nereids/rules/rewrite/PartitionPrunerTest.java | 570 +++++++++++++++++++++
.../infer_predicate/infer_unequal_predicates.out | 19 +-
.../partition_prune/list_prune_predicate.out | 16 +
.../partition_prune/hive_partition_prune.groovy | 2 +-
.../partition_prune/list_prune_predicate.groovy | 367 +++++++++++++
14 files changed, 1130 insertions(+), 61 deletions(-)
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java
index f3beb227528..8fd9aba8b1e 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/StatementContext.java
@@ -282,6 +282,7 @@ public class StatementContext implements Closeable {
private final Set<List<String>> materializationRewrittenSuccessSet = new
HashSet<>();
private boolean isInsert = false;
+ private boolean skipPrunePredicate = false;
private Optional<Map<TableIf, Set<Expression>>> mvRefreshPredicates =
Optional.empty();
@@ -1039,4 +1040,12 @@ public class StatementContext implements Closeable {
this.icebergRewriteFileScanTasks = null;
return tasks;
}
+
+ public boolean isSkipPrunePredicate() {
+ return skipPrunePredicate;
+ }
+
+ public void setSkipPrunePredicate(boolean skipPrunePredicate) {
+ this.skipPrunePredicate = skipPrunePredicate;
+ }
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsLoadPlanInfoCollector.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsLoadPlanInfoCollector.java
index 210249263a6..6cc6ca60cf0 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsLoadPlanInfoCollector.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/load/NereidsLoadPlanInfoCollector.java
@@ -49,6 +49,7 @@ import org.apache.doris.nereids.properties.PhysicalProperties;
import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer;
import org.apache.doris.nereids.rules.expression.rules.ConvertAggStateCast;
import org.apache.doris.nereids.rules.expression.rules.PartitionPruner;
+import
org.apache.doris.nereids.rules.expression.rules.PartitionPruner.PartitionTableType;
import org.apache.doris.nereids.rules.expression.rules.SortedPartitionRanges;
import org.apache.doris.nereids.trees.expressions.Alias;
import org.apache.doris.nereids.trees.expressions.Cast;
@@ -499,7 +500,7 @@ public class NereidsLoadPlanInfoCollector extends
DefaultPlanVisitor<Void, PlanT
List<Long> prunedPartitions = PartitionPruner.prune(
partitionSlots, filterPredicate, idToPartitions,
CascadesContext.initContext(new StatementContext(),
logicalPlan, PhysicalProperties.ANY),
- PartitionPruner.PartitionTableType.OLAP,
sortedPartitionRanges);
+ PartitionTableType.OLAP, sortedPartitionRanges).first;
return prunedPartitions;
} else {
return null;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPruner.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPruner.java
index 6e1f04f354d..badc925ad38 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPruner.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPruner.java
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.rules.expression.rules;
import org.apache.doris.catalog.ListPartitionItem;
import org.apache.doris.catalog.PartitionItem;
import org.apache.doris.catalog.RangePartitionItem;
+import org.apache.doris.common.Pair;
import org.apache.doris.common.profile.SummaryProfile;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
@@ -35,7 +36,11 @@ import
org.apache.doris.nereids.trees.expressions.literal.DateLiteral;
import org.apache.doris.nereids.trees.expressions.literal.DateTimeLiteral;
import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionRewriter;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalRelation;
import org.apache.doris.nereids.types.DateTimeType;
+import org.apache.doris.nereids.util.ExpressionUtils;
import org.apache.doris.nereids.util.Utils;
import com.google.common.collect.ImmutableList;
@@ -46,6 +51,7 @@ import com.google.common.collect.Range;
import com.google.common.collect.RangeSet;
import com.google.common.collect.Sets;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -110,17 +116,21 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
}
/** prune */
- public <K extends Comparable<K>> List<K> prune() {
+ public <K extends Comparable<K>> Pair<List<K>, Boolean> prune() {
Builder<K> scanPartitionIdents = ImmutableList.builder();
+ boolean canPredicatePruned = true;
for (OnePartitionEvaluator partition : partitions) {
- if (!canBePrunedOut(partitionPredicate, partition)) {
+ Pair<Boolean, Boolean> res = canBePrunedOut(partitionPredicate,
partition);
+ if (!res.first) {
+ canPredicatePruned = canPredicatePruned && res.second;
scanPartitionIdents.add((K) partition.getPartitionIdent());
}
}
- return scanPartitionIdents.build();
+ return Pair.of(scanPartitionIdents.build(), canPredicatePruned);
}
- public static <K extends Comparable<K>> List<K> prune(List<Slot>
partitionSlots, Expression partitionPredicate,
+ public static <K extends Comparable<K>> Pair<List<K>,
Optional<Expression>> prune(List<Slot> partitionSlots,
+ Expression partitionPredicate,
Map<K, PartitionItem> idToPartitions, CascadesContext
cascadesContext,
PartitionTableType partitionTableType) {
return prune(partitionSlots, partitionPredicate, idToPartitions,
@@ -130,7 +140,8 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
/**
* prune partition with `idToPartitions` as parameter.
*/
- public static <K extends Comparable<K>> List<K> prune(List<Slot>
partitionSlots, Expression partitionPredicate,
+ public static <K extends Comparable<K>> Pair<List<K>,
Optional<Expression>> prune(List<Slot> partitionSlots,
+ Expression partitionPredicate,
Map<K, PartitionItem> idToPartitions, CascadesContext
cascadesContext,
PartitionTableType partitionTableType,
Optional<SortedPartitionRanges<K>> sortedPartitionRanges) {
long startAt = System.currentTimeMillis();
@@ -146,14 +157,15 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
}
}
- private static <K extends Comparable<K>> List<K> pruneInternal(List<Slot>
partitionSlots,
+ private static <K extends Comparable<K>> Pair<List<K>,
Optional<Expression>> pruneInternal(
+ List<Slot> partitionSlots,
Expression partitionPredicate,
Map<K, PartitionItem> idToPartitions, CascadesContext
cascadesContext,
PartitionTableType partitionTableType,
Optional<SortedPartitionRanges<K>> sortedPartitionRanges) {
partitionPredicate = PartitionPruneExpressionExtractor.extract(
partitionPredicate, ImmutableSet.copyOf(partitionSlots),
cascadesContext);
+ Expression originalPartitionPredicate = partitionPredicate;
partitionPredicate =
PredicateRewriteForPartitionPrune.rewrite(partitionPredicate, cascadesContext);
-
int expandThreshold = cascadesContext.getAndCacheSessionVariable(
"partitionPruningExpandThreshold",
10, sessionVariable ->
sessionVariable.partitionPruningExpandThreshold);
@@ -161,25 +173,37 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
partitionPredicate = OrToIn.EXTRACT_MODE_INSTANCE.rewriteTree(
partitionPredicate, new
ExpressionRewriteContext(cascadesContext));
if (BooleanLiteral.TRUE.equals(partitionPredicate)) {
- return Utils.fastToImmutableList(idToPartitions.keySet());
+ // The partition column predicate is always true and can be
deleted, the partition cannot be pruned
+ return Pair.of(Utils.fastToImmutableList(idToPartitions.keySet()),
Optional.of(originalPartitionPredicate));
} else if (BooleanLiteral.FALSE.equals(partitionPredicate) ||
partitionPredicate.isNullLiteral()) {
- return ImmutableList.of();
+ // The partition column predicate is always false, and all
partitions can be pruned.
+ return Pair.of(ImmutableList.of(), Optional.empty());
}
if (sortedPartitionRanges.isPresent()) {
RangeSet<MultiColumnBound> predicateRanges =
partitionPredicate.accept(
new PartitionPredicateToRange(partitionSlots), null);
if (predicateRanges != null) {
- return binarySearchFiltering(
+ Pair<List<K>, Boolean> res = binarySearchFiltering(
sortedPartitionRanges.get(), partitionSlots,
partitionPredicate, cascadesContext,
expandThreshold, predicateRanges
);
+ if (res.second) {
+ return Pair.of(res.first,
Optional.of(originalPartitionPredicate));
+ } else {
+ return Pair.of(res.first, Optional.empty());
+ }
}
}
- return sequentialFiltering(
+ Pair<List<K>, Boolean> res = sequentialFiltering(
idToPartitions, partitionSlots, partitionPredicate,
cascadesContext, expandThreshold
);
+ if (res.second) {
+ return Pair.of(res.first, Optional.of(originalPartitionPredicate));
+ } else {
+ return Pair.of(res.first, Optional.empty());
+ }
}
/**
@@ -198,7 +222,7 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
}
}
- private static <K extends Comparable<K>> List<K> binarySearchFiltering(
+ private static <K extends Comparable<K>> Pair<List<K>, Boolean>
binarySearchFiltering(
SortedPartitionRanges<K> sortedPartitionRanges, List<Slot>
partitionSlots,
Expression partitionPredicate, CascadesContext cascadesContext,
int expandThreshold,
RangeSet<MultiColumnBound> predicateRanges) {
@@ -206,6 +230,7 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
Set<K> selectedIdSets = Sets.newTreeSet();
int leftIndex = 0;
+ boolean canPredicatePruned = true;
for (Range<MultiColumnBound> predicateRange :
predicateRanges.asRanges()) {
int rightIndex = sortedPartitions.size();
if (leftIndex >= rightIndex) {
@@ -246,8 +271,10 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
OnePartitionEvaluator<K> partitionEvaluator =
toPartitionEvaluator(
partitionId, partition.partitionItem, partitionSlots,
cascadesContext, expandThreshold);
- if (!canBePrunedOut(partitionPredicate, partitionEvaluator)) {
+ Pair<Boolean, Boolean> res =
canBePrunedOut(partitionPredicate, partitionEvaluator);
+ if (!res.first) {
selectedIdSets.add(partitionId);
+ canPredicatePruned = canPredicatePruned && res.second;
}
}
}
@@ -256,15 +283,17 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
K partitionId = defaultPartition.id;
OnePartitionEvaluator<K> partitionEvaluator = toPartitionEvaluator(
partitionId, defaultPartition.partitionItem,
partitionSlots, cascadesContext, expandThreshold);
- if (!canBePrunedOut(partitionPredicate, partitionEvaluator)) {
+ Pair<Boolean, Boolean> res = canBePrunedOut(partitionPredicate,
partitionEvaluator);
+ if (!res.first) {
selectedIdSets.add(partitionId);
+ canPredicatePruned = canPredicatePruned && res.second;
}
}
- return Utils.fastToImmutableList(selectedIdSets);
+ return Pair.of(Utils.fastToImmutableList(selectedIdSets),
canPredicatePruned);
}
- private static <K extends Comparable<K>> List<K> sequentialFiltering(
+ private static <K extends Comparable<K>> Pair<List<K>, Boolean>
sequentialFiltering(
Map<K, PartitionItem> idToPartitions, List<Slot> partitionSlots,
Expression partitionPredicate, CascadesContext cascadesContext,
int expandThreshold) {
List<OnePartitionEvaluator<?>> evaluators =
Lists.newArrayListWithCapacity(idToPartitions.size());
@@ -278,18 +307,70 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
}
/**
- * return true if partition is not qualified. that is, can be pruned out.
+ * return Pair
+ * pair.first is true if partition can be pruned
+ * pair.second is true if partitionPredicate is always true in this
partition
*/
- private static <K> boolean canBePrunedOut(Expression partitionPredicate,
OnePartitionEvaluator<K> evaluator) {
+ private static <K> Pair<Boolean, Boolean> canBePrunedOut(Expression
partitionPredicate,
+ OnePartitionEvaluator<K> evaluator) {
List<Map<Slot, PartitionSlotInput>> onePartitionInputs =
evaluator.getOnePartitionInputs();
- for (Map<Slot, PartitionSlotInput> currentInputs : onePartitionInputs)
{
- // evaluate whether there's possible for this partition to accept
this predicate
- Expression result =
evaluator.evaluateWithDefaultPartition(partitionPredicate, currentInputs);
- if (!result.equals(BooleanLiteral.FALSE) && !(result instanceof
NullLiteral)) {
- return false;
+ if (evaluator instanceof OneListPartitionEvaluator) {
+ // if a table has default partition, the predicate should not be
pruned,
+ // because evaluateWithDefaultPartition always return true in
default partition
+ // e.g. PARTITION BY LIST(k1) (
+ // PARTITION p1 VALUES IN ("1","2","3","4"),
+ // PARTITION p2 VALUES IN ("5","6","7","8"),
+ // PARTITION p3 ) p3 is default partition
+ boolean notDefaultPartition = !evaluator.isDefaultPartition();
+ Pair<Boolean, Boolean> res = Pair.of(notDefaultPartition,
notDefaultPartition);
+ for (Map<Slot, PartitionSlotInput> currentInputs :
onePartitionInputs) {
+ // evaluate whether there's possible for this partition to
accept this predicate
+ Expression result =
evaluator.evaluateWithDefaultPartition(partitionPredicate, currentInputs);
+ if (result.equals(BooleanLiteral.FALSE) || (result instanceof
NullLiteral)) {
+ // Indicates that there is a partition value that does not
satisfy the predicate
+ res.second = false;
+ } else if (result.equals(BooleanLiteral.TRUE)) {
+ // Indicates that there is a partition value that
satisfies the predicate
+ res.first = false;
+ } else {
+ // Indicates that this partition value may or may not
satisfy the predicate
+ res.second = false;
+ res.first = false;
+ }
+ if (!res.first && !res.second) {
+ break;
+ }
}
+ return res;
+ } else {
+ // only prune partition predicates in list partition, therefore
set pair.second always be false,
+ // meaning not to prune partition predicates in range partition
+ for (Map<Slot, PartitionSlotInput> currentInputs :
onePartitionInputs) {
+ Expression result =
evaluator.evaluateWithDefaultPartition(partitionPredicate, currentInputs);
+ if (!result.equals(BooleanLiteral.FALSE) && !(result
instanceof NullLiteral)) {
+ return Pair.of(false, false);
+ }
+ }
+ // only have false result: Can be pruned out. have other exprs:
CanNot be pruned out
+ return Pair.of(true, false);
+ }
+ }
+
+ /** remove predicates that are always true*/
+ public static Plan prunePredicate(boolean skipPrunePredicate,
Optional<Expression> prunedPredicates,
+ LogicalFilter<? extends Plan> filter, LogicalRelation scan) {
+ if (!skipPrunePredicate && prunedPredicates.isPresent()) {
+ Set<Expression> conjuncts = new
LinkedHashSet<>(filter.getConjuncts());
+ Expression deletedPredicate = prunedPredicates.get();
+ Set<Expression> deletedPredicateSet =
ExpressionUtils.extractConjunctionToSet(deletedPredicate);
+ conjuncts.removeAll(deletedPredicateSet);
+ if (conjuncts.isEmpty()) {
+ return scan;
+ } else {
+ return filter.withConjunctsAndChild(conjuncts, scan);
+ }
+ } else {
+ return filter.withChildren(ImmutableList.of(scan));
}
- // only have false result: Can be pruned out. have other exprs: CanNot
be pruned out
- return true;
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneFileScanPartition.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneFileScanPartition.java
index 0fb8e68c486..456dde5c5bc 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneFileScanPartition.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneFileScanPartition.java
@@ -20,6 +20,7 @@ package org.apache.doris.nereids.rules.rewrite;
import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.PartitionItem;
import org.apache.doris.catalog.SupportBinarySearchFilteringPartitions;
+import org.apache.doris.common.Pair;
import org.apache.doris.common.cache.NereidsSortedPartitionsCacheManager;
import org.apache.doris.datasource.ExternalTable;
import org.apache.doris.nereids.CascadesContext;
@@ -28,6 +29,7 @@ import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.expression.rules.PartitionPruner;
import
org.apache.doris.nereids.rules.expression.rules.PartitionPruner.PartitionTableType;
import org.apache.doris.nereids.rules.expression.rules.SortedPartitionRanges;
+import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.plans.logical.LogicalFileScan;
import
org.apache.doris.nereids.trees.plans.logical.LogicalFileScan.SelectedPartitions;
@@ -100,10 +102,10 @@ public class PruneFileScanPartition extends
OneRewriteRuleFactory {
sortedPartitionRanges = (Optional) partitionsCacheManager.get(
(SupportBinarySearchFilteringPartitions)
externalTable, scan);
}
-
- List<String> prunedPartitions = new ArrayList<>(PartitionPruner.prune(
+ Pair<List<String>, Optional<Expression>> res = PartitionPruner.prune(
partitionSlots, filter.getPredicate(), nameToPartitionItem,
ctx,
- PartitionTableType.EXTERNAL, sortedPartitionRanges));
+ PartitionTableType.EXTERNAL, sortedPartitionRanges);
+ List<String> prunedPartitions = new ArrayList<>(res.first);
for (String name : prunedPartitions) {
selectedPartitionItems.put(name, nameToPartitionItem.get(name));
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneOlapScanPartition.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneOlapScanPartition.java
index 023a17bec5a..937f7ed5d50 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneOlapScanPartition.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/PruneOlapScanPartition.java
@@ -25,6 +25,7 @@ import org.apache.doris.catalog.Partition;
import org.apache.doris.catalog.PartitionInfo;
import org.apache.doris.catalog.PartitionItem;
import org.apache.doris.catalog.Tablet;
+import org.apache.doris.common.Pair;
import org.apache.doris.common.cache.NereidsSortedPartitionsCacheManager;
import org.apache.doris.nereids.pattern.MatchingContext;
import org.apache.doris.nereids.rules.Rule;
@@ -32,6 +33,7 @@ import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.expression.rules.PartitionPruner;
import
org.apache.doris.nereids.rules.expression.rules.PartitionPruner.PartitionTableType;
import org.apache.doris.nereids.rules.expression.rules.SortedPartitionRanges;
+import org.apache.doris.nereids.trees.expressions.Expression;
import org.apache.doris.nereids.trees.expressions.Slot;
import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
@@ -70,7 +72,7 @@ public class PruneOlapScanPartition implements
RewriteRuleFactory {
// Case1: sql without filter condition, e.g. SELECT *
FROM tbl (${tabletID})
LogicalOlapScan scan = ctx.root;
OlapTable table = scan.getTable();
- return prunePartition(scan, table, null, ctx);
+ return prunePartition(scan, table, null, ctx).first;
}).toRule(RuleType.OLAP_SCAN_PARTITION_PRUNE),
logicalFilter(logicalOlapScan()
.whenNot(LogicalOlapScan::isPartitionPruned))
@@ -79,44 +81,49 @@ public class PruneOlapScanPartition implements
RewriteRuleFactory {
LogicalFilter<LogicalOlapScan> filter = ctx.root;
LogicalOlapScan scan = filter.child();
OlapTable table = scan.getTable();
- LogicalRelation rewrittenLogicalRelation =
prunePartition(scan, table, filter, ctx);
+ Pair<LogicalRelation, Optional<Expression>> prunedRes
+ = prunePartition(scan, table, filter, ctx);
+ LogicalRelation rewrittenLogicalRelation =
prunedRes.first;
if (rewrittenLogicalRelation == null) {
return null;
}
if (rewrittenLogicalRelation instanceof
LogicalEmptyRelation) {
return rewrittenLogicalRelation;
} else {
- LogicalOlapScan rewrittenScan = (LogicalOlapScan)
rewrittenLogicalRelation;
- return
filter.withChildren(ImmutableList.of(rewrittenScan));
+ return PartitionPruner.prunePredicate(
+
ctx.connectContext.getSessionVariable().skipPrunePredicate
+ ||
ctx.statementContext.isSkipPrunePredicate(),
+ prunedRes.second, filter,
rewrittenLogicalRelation);
}
}).toRule(RuleType.OLAP_SCAN_PARTITION_PRUNE)
);
}
- private LogicalRelation prunePartition(LogicalOlapScan scan,
+ private Pair<LogicalRelation, Optional<Expression>>
prunePartition(LogicalOlapScan scan,
OlapTable table,
LogicalFilter filter,
MatchingContext ctx) {
- List<Long> prunedPartitionsByFilters = prunePartitionByFilters(scan,
table, filter, ctx);
- List<Long> prunedPartitions = prunePartitionByTabletIds(scan, table,
prunedPartitionsByFilters);
+ Pair<List<Long>, Optional<Expression>> prunedPartitionsByFilters =
+ prunePartitionByFilters(scan, table, filter, ctx);
+ List<Long> prunedPartitions = prunePartitionByTabletIds(scan, table,
prunedPartitionsByFilters.first);
if (prunedPartitions == null) {
- return null;
+ return Pair.of(null, Optional.empty());
}
if (prunedPartitions.isEmpty()) {
- return new LogicalEmptyRelation(
+ return Pair.of(new LogicalEmptyRelation(
ConnectContext.get().getStatementContext().getNextRelationId(),
- ctx.root.getOutput());
+ ctx.root.getOutput()), Optional.empty());
}
- return scan.withSelectedPartitionIds(prunedPartitions);
+ return Pair.of(scan.withSelectedPartitionIds(prunedPartitions),
prunedPartitionsByFilters.second);
}
- private List<Long> prunePartitionByFilters(LogicalOlapScan scan,
+ private Pair<List<Long>, Optional<Expression>>
prunePartitionByFilters(LogicalOlapScan scan,
OlapTable table,
LogicalFilter filter,
MatchingContext ctx) {
Set<String> partitionColumnNameSet =
Utils.execWithReturnVal(table::getPartitionColumnNames);
if (partitionColumnNameSet.isEmpty()) {
- return null;
+ return Pair.of(null, Optional.empty());
}
List<Slot> output = scan.getOutput();
PartitionInfo partitionInfo = table.getPartitionInfo();
@@ -132,7 +139,7 @@ public class PruneOlapScanPartition implements
RewriteRuleFactory {
}
}
if (partitionSlot == null) {
- return null;
+ return Pair.of(null, Optional.empty());
} else {
partitionSlots.add(partitionSlot);
}
@@ -156,14 +163,14 @@ public class PruneOlapScanPartition implements
RewriteRuleFactory {
.collect(Collectors.toMap(Function.identity(),
allPartitions::get));
}
if (filter != null) {
- List<Long> prunedPartitions = PartitionPruner.prune(
+ Pair<List<Long>, Optional<Expression>> prunedPartitions =
PartitionPruner.prune(
partitionSlots, filter.getPredicate(), idToPartitions,
ctx.cascadesContext,
PartitionTableType.OLAP, sortedPartitionRanges);
return prunedPartitions;
} else if (!manuallySpecifiedPartitions.isEmpty()) {
- return Utils.fastToImmutableList(idToPartitions.keySet());
+ return Pair.of(Utils.fastToImmutableList(idToPartitions.keySet()),
Optional.empty());
} else {
- return null;
+ return Pair.of(null, Optional.empty());
}
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromCommand.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromCommand.java
index 944378ed528..bc2bba1b325 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromCommand.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/DeleteFromCommand.java
@@ -129,7 +129,10 @@ public class DeleteFromCommand extends Command implements
ForwardWithSync, Expla
public void run(ConnectContext ctx, StmtExecutor executor) throws
Exception {
LogicalPlanAdapter logicalPlanAdapter = new
LogicalPlanAdapter(logicalQuery, ctx.getStatementContext());
updateSessionVariableForDelete(ctx.getSessionVariable());
- NereidsPlanner planner = new NereidsPlanner(ctx.getStatementContext());
+ StatementContext statementContext = ctx.getStatementContext();
+ // delete not prune predicate after partition prune
+ statementContext.setSkipPrunePredicate(true);
+ NereidsPlanner planner = new NereidsPlanner(statementContext);
boolean originalIsSkipAuth = ctx.isSkipAuth();
// delete not need select priv
ctx.setSkipAuth(true);
@@ -293,7 +296,7 @@ public class DeleteFromCommand extends Command implements
ForwardWithSync, Expla
List<Long> prunedPartitions = PartitionPruner.prune(
partitionSlots, filter.getPredicate(), idToPartitions,
CascadesContext.initContext(new StatementContext(), this,
PhysicalProperties.ANY),
- PartitionTableType.OLAP, sortedPartitionRanges);
+ PartitionTableType.OLAP, sortedPartitionRanges).first;
return
prunedPartitions.stream().map(olapTable::getPartition).collect(Collectors.toList());
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExplainCommand.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExplainCommand.java
index c13b284e2b4..834007ad81d 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExplainCommand.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/commands/ExplainCommand.java
@@ -87,6 +87,9 @@ public class ExplainCommand extends Command implements
NoForward {
|| explainable instanceof UpdateCommand) {
ctx.getStatementContext().setIsInsert(true);
}
+ if (explainable instanceof DeleteFromCommand) {
+ ctx.getStatementContext().setSkipPrunePredicate(true);
+ }
explainPlan = ((LogicalPlan) explainable.getExplainPlan(ctx));
NereidsPlanner planner = explainable.getExplainPlanner(explainPlan,
ctx.getStatementContext()).orElseGet(() ->
new NereidsPlanner(ctx.getStatementContext())
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
index 2ed41fdde85..13c1543f7c7 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/SessionVariable.java
@@ -185,6 +185,7 @@ public class SessionVariable implements Serializable,
Writable {
public static final String ENABLE_REWRITE_ELEMENT_AT_TO_SLOT =
"enable_rewrite_element_at_to_slot";
public static final String ENABLE_ODBC_TRANSCATION =
"enable_odbc_transcation";
public static final String ENABLE_BINARY_SEARCH_FILTERING_PARTITIONS =
"enable_binary_search_filtering_partitions";
+ public static final String SKIP_PRUNE_PREDICATE = "skip_prune_predicate";
public static final String ENABLE_SQL_CACHE = "enable_sql_cache";
public static final String ENABLE_HIVE_SQL_CACHE = "enable_hive_sql_cache";
public static final String ENABLE_QUERY_CACHE = "enable_query_cache";
@@ -1287,6 +1288,16 @@ public class SessionVariable implements Serializable,
Writable {
)
public boolean enableBinarySearchFilteringPartitions = true;
+ @VariableMgr.VarAttr(name = SKIP_PRUNE_PREDICATE, fuzzy = true,
+ description = {
+ "是否跳过“在分区裁剪后删除恒真谓词”的优化。默认为OFF(即执行此优化)。",
+ "Skips the removal of always-true predicates after
partition pruning. "
+ + "Defaults to OFF (optimization is active)."
+ }
+ )
+ public boolean skipPrunePredicate = false;
+
+
@VariableMgr.VarAttr(name = ENABLE_SQL_CACHE, fuzzy = true)
public boolean enableSqlCache = true;
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/OptimizeGetAvailableMvsTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/OptimizeGetAvailableMvsTest.java
index 7508890be96..44c9cd6b86a 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/OptimizeGetAvailableMvsTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/mv/OptimizeGetAvailableMvsTest.java
@@ -173,10 +173,10 @@ public class OptimizeGetAvailableMvsTest extends
SqlTestBase {
new MockUp<PartitionPruner>() {
@Mock
- public <K extends Comparable<K>> List<Long> prune(List<Slot>
partitionSlots, Expression partitionPredicate,
+ public <K extends Comparable<K>> Pair<List<K>,
Optional<Expression>> prune(List<Slot> partitionSlots, Expression
partitionPredicate,
Map<K, PartitionItem> idToPartitions, CascadesContext
cascadesContext,
PartitionTableType partitionTableType,
Optional<SortedPartitionRanges<K>> sortedPartitionRanges) {
- return Lists.newArrayList(1L);
+ return Pair.of((List) Lists.newArrayList(1L),
Optional.empty());
}
};
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PartitionPrunerTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PartitionPrunerTest.java
new file mode 100644
index 00000000000..0378cc86dba
--- /dev/null
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PartitionPrunerTest.java
@@ -0,0 +1,570 @@
+// 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.analysis.PartitionValue;
+import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.ListPartitionItem;
+import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.PartitionKey;
+import org.apache.doris.catalog.PrimitiveType;
+import org.apache.doris.common.AnalysisException;
+import org.apache.doris.common.Pair;
+import org.apache.doris.nereids.CascadesContext;
+import
org.apache.doris.nereids.rules.expression.rules.OneListPartitionEvaluator;
+import org.apache.doris.nereids.rules.expression.rules.OnePartitionEvaluator;
+import org.apache.doris.nereids.rules.expression.rules.PartitionPruner;
+import org.apache.doris.nereids.trees.expressions.And;
+import org.apache.doris.nereids.trees.expressions.EqualTo;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.GreaterThan;
+import org.apache.doris.nereids.trees.expressions.InPredicate;
+import org.apache.doris.nereids.trees.expressions.LessThan;
+import org.apache.doris.nereids.trees.expressions.Not;
+import org.apache.doris.nereids.trees.expressions.Or;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.plans.Plan;
+import org.apache.doris.nereids.trees.plans.RelationId;
+import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
+import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan;
+import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.types.VarcharType;
+import org.apache.doris.utframe.TestWithFeService;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+public class PartitionPrunerTest extends TestWithFeService {
+ private Method canBePrunedOutMethod;
+ private final Column partitionColumn = new Column("a", PrimitiveType.INT);
+ private CascadesContext cascadesContext;
+ private final SlotReference slotA = new SlotReference("a",
IntegerType.INSTANCE);
+ private final SlotReference slotB = new SlotReference("b",
IntegerType.INSTANCE);
+ private final SlotReference slotC = new SlotReference("c",
IntegerType.INSTANCE);
+
+ @Override
+ protected void runBeforeAll() throws Exception {
+ Class<?> clazz = PartitionPruner.class;
+ canBePrunedOutMethod = clazz.getDeclaredMethod("canBePrunedOut",
Expression.class, OnePartitionEvaluator.class);
+ canBePrunedOutMethod.setAccessible(true);
+ cascadesContext = createCascadesContext("select * from t1");
+ }
+
+ // test canBePrunedOut res
+ // list partition p1, partition value is 1
+ // predicate: a = 1
+ @Test
+ public void testEqualPredicate()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue = new PartitionValue("1");
+ PartitionKey partitionKey =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue),
ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+ Expression predicate = new EqualTo(slotA, Literal.of(1));
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertFalse(result.first);
+ Assertions.assertTrue(result.second);
+ }
+
+ // list partition p1, partition value is 1, 2
+ // predicate: a = 1
+ @Test
+ public void testEqualPredicate2()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue1 = new PartitionValue("1");
+ PartitionValue partitionValue2 = new PartitionValue("2");
+ PartitionKey partitionKey1 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue1),
+ ImmutableList.of(partitionColumn));
+ PartitionKey partitionKey2 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue2),
+ ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey1,
partitionKey2);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+ Expression predicate = new EqualTo(slotA, Literal.of(1));
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertFalse(result.first);
+ Assertions.assertFalse(result.second);
+ }
+
+ // list partition p1, partition value is 1
+ // predicate: a = 2
+ @Test
+ public void testEqualPredicate3()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue = new PartitionValue("1");
+ PartitionKey partitionKey =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue),
ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+ Expression predicate = new EqualTo(slotA, Literal.of(2));
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertTrue(result.first);
+ Assertions.assertFalse(result.second);
+ }
+
+ // list partition p1, partition value is 1
+ // predicate: a = NULL
+ @Test
+ public void testNullPredicate()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue = new PartitionValue("1");
+ PartitionKey partitionKey =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue),
ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+
+ Expression predicate = new EqualTo(slotA, NullLiteral.INSTANCE);
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertTrue(result.first);
+ Assertions.assertFalse(result.second);
+ }
+
+ // list partition p1, partition value is 1, 2, 3
+ // predicate: a IN (1, 2)
+ @Test
+ public void testInPredicate()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue1 = new PartitionValue("1");
+ PartitionValue partitionValue2 = new PartitionValue("2");
+ PartitionValue partitionValue3 = new PartitionValue("3");
+ PartitionKey partitionKey1 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue1),
ImmutableList.of(partitionColumn));
+ PartitionKey partitionKey2 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue2),
ImmutableList.of(partitionColumn));
+ PartitionKey partitionKey3 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue3),
ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey1,
partitionKey2, partitionKey3);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+
+ Expression predicate = new InPredicate(slotA,
ImmutableList.of(Literal.of(1), Literal.of(2)));
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertFalse(result.first);
+ Assertions.assertFalse(result.second);
+ }
+
+ // list partition p1, partition value is 1, 2
+ // predicate: a IN (1, 2)
+ @Test
+ public void testInPredicateExactMatch()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue1 = new PartitionValue("1");
+ PartitionValue partitionValue2 = new PartitionValue("2");
+ PartitionKey partitionKey1 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue1),
ImmutableList.of(partitionColumn));
+ PartitionKey partitionKey2 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue2),
ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey1,
partitionKey2);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+
+ Expression predicate = new InPredicate(slotA,
ImmutableList.of(Literal.of(1), Literal.of(2)));
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertFalse(result.first);
+ Assertions.assertTrue(result.second);
+ }
+
+
+ // list partition p1, partition value (1, "a"), (2, "b")
+ // predicate: a = 1 AND b = "a"
+ @Test
+ public void testMultiColumnPartition()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ Column partitionColumn2 = new Column("b", PrimitiveType.VARCHAR);
+ SlotReference slot2 = new SlotReference("b",
VarcharType.createVarcharType(10));
+
+ PartitionValue partitionValue1a = new PartitionValue("1");
+ PartitionValue partitionValue1b = new PartitionValue("a");
+ PartitionValue partitionValue2a = new PartitionValue("2");
+ PartitionValue partitionValue2b = new PartitionValue("b");
+
+ PartitionKey partitionKey1 = PartitionKey.createPartitionKey(
+ ImmutableList.of(partitionValue1a, partitionValue1b),
+ ImmutableList.of(partitionColumn, partitionColumn2));
+ PartitionKey partitionKey2 = PartitionKey.createPartitionKey(
+ ImmutableList.of(partitionValue2a, partitionValue2b),
+ ImmutableList.of(partitionColumn, partitionColumn2));
+
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey1,
partitionKey2);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA, slot2), partitionItem,
cascadesContext);
+
+ Expression predicate = new And(
+ new EqualTo(slotA, Literal.of(1)),
+ new EqualTo(slot2, Literal.of("a"))
+ );
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertFalse(result.first);
+ Assertions.assertFalse(result.second);
+ }
+
+ // list partition p1, partition value is 1, 2
+ // predicate: a = 1 OR a = 3
+ @Test
+ public void testOrPredicate()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue1 = new PartitionValue("1");
+ PartitionValue partitionValue2 = new PartitionValue("2");
+ PartitionKey partitionKey1 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue1),
ImmutableList.of(partitionColumn));
+ PartitionKey partitionKey2 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue2),
ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey1,
partitionKey2);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+
+ Expression predicate = new Or(
+ new EqualTo(slotA, Literal.of(1)),
+ new EqualTo(slotA, Literal.of(3))
+ );
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertFalse(result.first);
+ Assertions.assertFalse(result.second);
+ }
+
+ // list partition p1, partition value is 1
+ // predicate: NOT (a = 1)
+ @Test
+ public void testNotPredicate()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue = new PartitionValue("1");
+ PartitionKey partitionKey =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue),
ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+
+ Expression predicate = new Not(new EqualTo(slotA, Literal.of(1)));
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertTrue(result.first);
+ Assertions.assertFalse(result.second);
+ }
+
+ // list partition p1, partition value is 1, 2, 3
+ // predicate: a > 2
+ @Test
+ public void testGreaterThanPredicate()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue1 = new PartitionValue("1");
+ PartitionValue partitionValue2 = new PartitionValue("2");
+ PartitionValue partitionValue3 = new PartitionValue("3");
+ PartitionKey partitionKey1 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue1),
ImmutableList.of(partitionColumn));
+ PartitionKey partitionKey2 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue2),
ImmutableList.of(partitionColumn));
+ PartitionKey partitionKey3 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue3),
ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey1,
partitionKey2, partitionKey3);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+
+ Expression predicate = new GreaterThan(slotA, Literal.of(2));
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertFalse(result.first);
+ Assertions.assertFalse(result.second);
+ }
+
+ // list partition p1, partition value is 1, 2, 3
+ // predicate: (a = 1 OR a = 2) AND a > 0
+ @Test
+ public void testComplexNestedPredicate()
+ throws AnalysisException, InvocationTargetException,
IllegalAccessException {
+ PartitionValue partitionValue1 = new PartitionValue("1");
+ PartitionValue partitionValue2 = new PartitionValue("2");
+ PartitionValue partitionValue3 = new PartitionValue("3");
+ PartitionKey partitionKey1 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue1),
ImmutableList.of(partitionColumn));
+ PartitionKey partitionKey2 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue2),
ImmutableList.of(partitionColumn));
+ PartitionKey partitionKey3 =
PartitionKey.createPartitionKey(ImmutableList.of(partitionValue3),
ImmutableList.of(partitionColumn));
+ List<PartitionKey> partitionKeys = ImmutableList.of(partitionKey1,
partitionKey2, partitionKey3);
+ ListPartitionItem partitionItem = new ListPartitionItem(partitionKeys);
+ OneListPartitionEvaluator<String> partitionEvaluator = new
OneListPartitionEvaluator<>(
+ "p1", ImmutableList.of(slotA), partitionItem, cascadesContext);
+
+ Expression predicate = new And(
+ new Or(
+ new EqualTo(slotA, Literal.of(1)),
+ new EqualTo(slotA, Literal.of(2))
+ ),
+ new GreaterThan(slotA, Literal.of(0))
+ );
+ Pair<Boolean, Boolean> result = (Pair<Boolean, Boolean>)
canBePrunedOutMethod.invoke(null, predicate, partitionEvaluator);
+ Assertions.assertFalse(result.first);
+ Assertions.assertFalse(result.second);
+ }
+
+ // test prune predicate
+ // Test basis: some predicates are pruned
+ @Test
+ public void testPrunePartialPredicates() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ LessThan lt = new LessThan(slotB, Literal.of(20));
+ predicates.add(gt);
+ predicates.add(lt);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(gt), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalFilter.class, prunedPlan);
+ LogicalFilter<?> prunedFilter = (LogicalFilter<?>) prunedPlan;
+ Assertions.assertEquals(1, prunedFilter.getConjuncts().size());
+ Assertions.assertTrue(prunedFilter.getConjuncts().contains(lt));
+ Assertions.assertFalse(prunedFilter.getConjuncts().contains(gt));
+ }
+
+ // all predicates are pruned
+ @Test
+ public void testPruneAllPredicates() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ predicates.add(gt);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(gt), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalOlapScan.class, prunedPlan);
+ }
+
+ // no predicates are pruned
+ @Test
+ public void testPruneNoPredicates() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ LessThan lt = new LessThan(slotB, Literal.of(20));
+ predicates.add(gt);
+ predicates.add(lt);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ EqualTo nonExistentPredicate = new EqualTo(slotC, Literal.of(30));
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(nonExistentPredicate), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalFilter.class, prunedPlan);
+ LogicalFilter<?> prunedFilter = (LogicalFilter<?>) prunedPlan;
+ Assertions.assertEquals(2, prunedFilter.getConjuncts().size());
+ Assertions.assertTrue(prunedFilter.getConjuncts().contains(gt));
+ Assertions.assertTrue(prunedFilter.getConjuncts().contains(lt));
+ }
+
+ @Test
+ public void testPruneCompoundPredicate() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ LessThan lt = new LessThan(slotB, Literal.of(20));
+ EqualTo eq = new EqualTo(slotC, Literal.of(30));
+ predicates.add(gt);
+ predicates.add(lt);
+ predicates.add(eq);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ // (a > 10 AND b < 20)
+ And compoundPredicate = new And(gt, lt);
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(compoundPredicate), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalFilter.class, prunedPlan);
+ LogicalFilter<?> prunedFilter = (LogicalFilter<?>) prunedPlan;
+ Assertions.assertEquals(1, prunedFilter.getConjuncts().size());
+ Assertions.assertTrue(prunedFilter.getConjuncts().contains(eq));
+ Assertions.assertFalse(prunedFilter.getConjuncts().contains(gt));
+ Assertions.assertFalse(prunedFilter.getConjuncts().contains(lt));
+ }
+
+ @Test
+ public void testSkipPrunePredicate() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ predicates.add(gt);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ Plan prunedPlan = PartitionPruner.prunePredicate(true,
Optional.of(gt), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalFilter.class, prunedPlan);
+ LogicalFilter<?> prunedFilter = (LogicalFilter<?>) prunedPlan;
+ Assertions.assertEquals(1, prunedFilter.getConjuncts().size());
+ Assertions.assertTrue(prunedFilter.getConjuncts().contains(gt));
+ }
+
+ @Test
+ public void testEmptyPrunedPredicates() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ predicates.add(gt);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ // prunedPredicates is empty
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.empty(), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalFilter.class, prunedPlan);
+ LogicalFilter<?> prunedFilter = (LogicalFilter<?>) prunedPlan;
+ Assertions.assertEquals(1, prunedFilter.getConjuncts().size());
+ Assertions.assertTrue(prunedFilter.getConjuncts().contains(gt));
+ }
+
+ @Test
+ public void testPruneDuplicatePredicates() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt1 = new GreaterThan(slotA, Literal.of(10));
+ GreaterThan gt2 = new GreaterThan(slotA, Literal.of(10)); //
duplicated predicate
+ predicates.add(gt1);
+ predicates.add(gt2);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(gt1), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalOlapScan.class, prunedPlan);
+ }
+
+ @Test
+ public void testPruneWithNullLiteral() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ EqualTo nullEq = new EqualTo(slotB, new NullLiteral());
+ predicates.add(gt);
+ predicates.add(nullEq);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(gt), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalFilter.class, prunedPlan);
+ LogicalFilter<?> prunedFilter = (LogicalFilter<?>) prunedPlan;
+ Assertions.assertEquals(1, prunedFilter.getConjuncts().size());
+ Assertions.assertTrue(prunedFilter.getConjuncts().contains(nullEq));
+ }
+
+ @Test
+ public void testPruneMultiplePredicatesPartially() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ LessThan lt = new LessThan(slotB, Literal.of(20));
+ EqualTo eq1 = new EqualTo(slotC, Literal.of(30));
+ EqualTo eq2 = new EqualTo(slotC, Literal.of(40));
+ predicates.add(gt);
+ predicates.add(lt);
+ predicates.add(eq1);
+ predicates.add(eq2);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ // (a > 10 AND b < 20)
+ And compoundPredicate = new And(gt, lt);
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(compoundPredicate), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalFilter.class, prunedPlan);
+ LogicalFilter<?> prunedFilter = (LogicalFilter<?>) prunedPlan;
+ Assertions.assertEquals(2, prunedFilter.getConjuncts().size());
+ Assertions.assertTrue(prunedFilter.getConjuncts().contains(eq1));
+ Assertions.assertTrue(prunedFilter.getConjuncts().contains(eq2));
+ Assertions.assertFalse(prunedFilter.getConjuncts().contains(gt));
+ Assertions.assertFalse(prunedFilter.getConjuncts().contains(lt));
+ }
+
+ @Test
+ public void testPruneNestedCompoundPredicate() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ LessThan lt = new LessThan(slotB, Literal.of(20));
+ EqualTo eq = new EqualTo(slotC, Literal.of(30));
+ predicates.add(gt);
+ predicates.add(lt);
+ predicates.add(eq);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ // (a > 10 AND (b < 20 AND c = 30))
+ And innerAnd = new And(lt, eq);
+ And outerAnd = new And(gt, innerAnd);
+
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(outerAnd), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalOlapScan.class, prunedPlan);
+ }
+
+ @Test
+ public void testPruneWhenFilterContainsOr() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ Or orPredicate = new Or(
+ new GreaterThan(slotA, Literal.of(10)),
+ new LessThan(slotB, Literal.of(20))
+ );
+ predicates.add(orPredicate);
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(gt), filter, scan);
+
+ Assertions.assertInstanceOf(LogicalFilter.class, prunedPlan);
+ LogicalFilter<?> prunedFilter = (LogicalFilter<?>) prunedPlan;
+ Assertions.assertEquals(1, prunedFilter.getConjuncts().size());
+
Assertions.assertTrue(prunedFilter.getConjuncts().contains(orPredicate));
+ }
+
+ @Test
+ public void testPruneWhenFilterContainsAndOrMix() {
+ Set<Expression> predicates = new LinkedHashSet<>();
+ // filter :a > 10 AND (b < 20 OR c = 30)
+ Or orPredicate = new Or(
+ new LessThan(slotB, Literal.of(20)),
+ new EqualTo(slotC, Literal.of(30))
+ );
+ GreaterThan gt = new GreaterThan(slotA, Literal.of(10));
+
+ predicates.add(gt);
+ predicates.add(orPredicate);
+
+ LogicalOlapScan scan = new LogicalOlapScan(new RelationId(1), new
OlapTable());
+ LogicalFilter<Plan> filter = new LogicalFilter<>(predicates, scan);
+ // a > 10
+ Plan prunedPlan = PartitionPruner.prunePredicate(false,
Optional.of(gt), filter, scan);
+ Assertions.assertInstanceOf(LogicalFilter.class, prunedPlan);
+ LogicalFilter<?> prunedFilter = (LogicalFilter<?>) prunedPlan;
+
+ Assertions.assertEquals(1, prunedFilter.getConjuncts().size());
+
Assertions.assertTrue(prunedFilter.getConjuncts().contains(orPredicate));
+ }
+}
+
+
diff --git
a/regression-test/data/nereids_rules_p0/infer_predicate/infer_unequal_predicates.out
b/regression-test/data/nereids_rules_p0/infer_predicate/infer_unequal_predicates.out
index 30e82ec957c..aa498a3bf47 100644
---
a/regression-test/data/nereids_rules_p0/infer_predicate/infer_unequal_predicates.out
+++
b/regression-test/data/nereids_rules_p0/infer_predicate/infer_unequal_predicates.out
@@ -32,7 +32,7 @@ PhysicalResultSink
-- !should_infer_because_d_is_partition_column --
PhysicalResultSink
---filter((t1.c < 10) and (t1.d < 10) and (t1.d < t1.c))
+--filter((t1.c < 10) and (t1.d < t1.c))
----PhysicalOlapScan[infer_unequal_predicates_t1]
-- !infer_with_equal --
@@ -53,17 +53,17 @@ PhysicalResultSink
-- !infer_long_chain_same_table_infer_a_and_d --
PhysicalResultSink
---filter((t1.a < 10) and (t1.a < t1.d) and (t1.c < 10) and (t1.d < 10) and
(t1.d < t1.c))
+--filter((t1.a < 10) and (t1.a < t1.d) and (t1.c < 10) and (t1.d < t1.c))
----PhysicalOlapScan[infer_unequal_predicates_t1]
-- !infer_long_chain_same_table_not_infer_c --
PhysicalResultSink
---filter((t1.a < 10) and (t1.a < t1.c) and (t1.c < t1.d) and (t1.d < 10))
+--filter((t1.a < 10) and (t1.a < t1.c) and (t1.c < t1.d))
----PhysicalOlapScan[infer_unequal_predicates_t1]
-- !remove_useless_input_predicate_c_less_than_10 --
PhysicalResultSink
---filter((t1.a < 10) and (t1.a < t1.c) and (t1.c < t1.d) and (t1.d < 10))
+--filter((t1.a < 10) and (t1.a < t1.c) and (t1.c < t1.d))
----PhysicalOlapScan[infer_unequal_predicates_t1]
-- !remove_useless_predicate --
@@ -78,7 +78,7 @@ PhysicalResultSink
--NestedLoopJoin[INNER_JOIN](t1.a < t2.d)
----filter((t1.a < 10))
------PhysicalOlapScan[infer_unequal_predicates_t1]
-----filter((t2.c < 10) and (t2.d < 10) and (t2.d < t2.c))
+----filter((t2.c < 10) and (t2.d < t2.c))
------PhysicalOlapScan[infer_unequal_predicates_t2]
-- !infer_with_constant_and_columns --
@@ -86,7 +86,7 @@ PhysicalResultSink
--hashJoin[INNER_JOIN] hashCondition=((t1.a = t2.c)) otherCondition=()
----filter((t1.a > 1))
------PhysicalOlapScan[infer_unequal_predicates_t1]
-----filter((t2.c < t2.d) and (t2.c > 1) and (t2.d > 1))
+----filter((t2.c < t2.d) and (t2.c > 1))
------PhysicalOlapScan[infer_unequal_predicates_t2]
-- !no_infer --
@@ -107,7 +107,7 @@ PhysicalResultSink
--NestedLoopJoin[INNER_JOIN](t1.a < t2.a)
----filter((t1.a < 10))
------PhysicalOlapScan[infer_unequal_predicates_t1]
-----filter((t2.a < 10) and (t2.a < t2.c) and (t2.c < t2.d) and (t2.d < 10))
+----filter((t2.a < 10) and (t2.a < t2.c) and (t2.c < t2.d))
------PhysicalOlapScan[infer_unequal_predicates_t2]
-- !infer_cast_int --
@@ -126,10 +126,9 @@ PhysicalResultSink
-- !no_redundant_predicates --
PhysicalResultSink
--hashJoin[INNER_JOIN] hashCondition=((t1.d = t2.d)) otherCondition=()
-----filter((t1.c > 1) and (t1.d < 10) and (t1.d = t1.c) and (t1.d > 1))
+----filter((t1.c > 1) and (t1.d = t1.c))
------PhysicalOlapScan[infer_unequal_predicates_t1]
-----filter((t2.d < 10) and (t2.d > 1))
-------PhysicalOlapScan[infer_unequal_predicates_t2]
+----PhysicalOlapScan[infer_unequal_predicates_t2]
-- !expr_unequal_infer_same_table1 --
PhysicalResultSink
diff --git
a/regression-test/data/nereids_rules_p0/partition_prune/list_prune_predicate.out
b/regression-test/data/nereids_rules_p0/partition_prune/list_prune_predicate.out
new file mode 100644
index 00000000000..fd6fe7e046c
--- /dev/null
+++
b/regression-test/data/nereids_rules_p0/partition_prune/list_prune_predicate.out
@@ -0,0 +1,16 @@
+-- This file is automatically generated. You should know what you did if you
want to edit this
+-- !default_partition --
+10 1 1 1 24453.325 1.0 1.0
+
+-- !update --
+1 2025-01-01 10
+2 2025-01-02 1
+3 2025-01-03 1
+4 2025-01-04 1
+5 2025-01-05 1
+6 2025-01-06 1
+7 2025-01-07 1
+8 2025-01-08 1
+9 2025-01-09 1
+10 2025-01-10 1
+
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/hive_partition_prune.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/hive_partition_prune.groovy
index ebf4d35d3a1..452e5224d16 100644
---
a/regression-test/suites/nereids_rules_p0/partition_prune/hive_partition_prune.groovy
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/hive_partition_prune.groovy
@@ -68,7 +68,7 @@ suite("hive_partition_prune") {
}
explain {
sql "SELECT * FROM test_hive_partition WHERE p not in (15,6,1,
'2021-01-02 00:00:00')"
- contains("partition=4/6")
+ contains("partition=0/6")
}
explain {
sql "SELECT * FROM test_hive_partition WHERE p not in (1, 5,6,null)"
diff --git
a/regression-test/suites/nereids_rules_p0/partition_prune/list_prune_predicate.groovy
b/regression-test/suites/nereids_rules_p0/partition_prune/list_prune_predicate.groovy
new file mode 100644
index 00000000000..b44dd8aa873
--- /dev/null
+++
b/regression-test/suites/nereids_rules_p0/partition_prune/list_prune_predicate.groovy
@@ -0,0 +1,367 @@
+// 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("list_prune_predicate") {
+ multi_sql """
+ drop table if exists test_list_date;
+ create table test_list_date (id int, d_date date)
+ PARTITION BY LIST(d_date)
+ (
+ PARTITION `p20250101` VALUES IN ('2025-01-01'),
+ PARTITION `p20250102` VALUES IN ('2025-01-02'),
+ PARTITION `p20250103` VALUES IN ('2025-01-03'),
+ PARTITION `p20250104` VALUES IN ('2025-01-04'),
+ PARTITION `p20250105` VALUES IN ('2025-01-05'),
+ PARTITION `p20250106` VALUES IN ('2025-01-06'),
+ PARTITION `p20250107` VALUES IN ('2025-01-07'),
+ PARTITION `p20250108` VALUES IN ('2025-01-08'),
+ PARTITION `p20250109` VALUES IN ('2025-01-09'),
+ PARTITION `p20250110` VALUES IN ('2025-01-10')
+ )
+ properties('replication_num'='1');
+
+ insert into test_list_date values
+ (1,'2025-01-01') , (2, '2025-01-02') , (3, '2025-01-03'), (4,
'2025-01-04') , (5, '2025-01-05'), (6,'2025-01-06') ,
+ (7, '2025-01-07') , (8, '2025-01-08'), (9, '2025-01-09') , (10,
'2025-01-10'); """
+
+ // =
+ explain {
+ sql "select * from test_list_date where d_date='2025-01-07';"
+ contains("partitions=1/10 (p20250107)")
+ notContains("PREDICATES")
+ }
+ // >
+ explain {
+ sql "select * from test_list_date where d_date>'2025-01-07';"
+ contains("partitions=3/10 (p20250108,p20250109,p20250110)")
+ notContains("PREDICATES")
+ }
+
+ // <
+ explain {
+ sql "select * from test_list_date where d_date<'2025-01-07';"
+ contains("partitions=6/10
(p20250101,p20250102,p20250103,p20250104,p20250105,p20250106)")
+ notContains("PREDICATES")
+ }
+ // >=
+ explain {
+ sql "select * from test_list_date where d_date>='2025-01-07';"
+ contains("partitions=4/10 (p20250107,p20250108,p20250109,p20250110)")
+ notContains("PREDICATES")
+ }
+ // <=
+ explain {
+ sql "select * from test_list_date where d_date<='2025-01-07';"
+ contains("partitions=7/10
(p20250101,p20250102,p20250103,p20250104,p20250105,p20250106,p20250107)")
+ notContains("PREDICATES")
+ }
+ // empty paritition
+ explain {
+ sql "select * from test_list_date where d_date<='2024-01-01'"
+ contains("VEMPTYSET")
+ notContains("PREDICATES")
+ }
+
+ // and
+ explain {
+ sql "select * from test_list_date where d_date>='2025-01-06' and
d_date<='2025-01-07';"
+ contains("partitions=2/10 (p20250106,p20250107)")
+ notContains("PREDICATES")
+ }
+ // or
+ explain {
+ sql "select * from test_list_date where d_date>='2025-01-08' or
d_date<='2025-01-03';"
+ contains("partitions=6/10
(p20250101,p20250102,p20250103,p20250108,p20250109,p20250110)")
+ notContains("PREDICATES")
+ }
+
+ // or
+ explain {
+ sql "select * from test_list_date where d_date>='2025-01-06' or
d_date<='2025-01-07';"
+ contains("partitions=10/10
(p20250101,p20250102,p20250103,p20250104,p20250105,p20250106,p20250107,p20250108,p20250109,p20250110)")
+ notContains("PREDICATES")
+ }
+
+ // have other predicates
+ // =
+ explain {
+ sql "select * from test_list_date where d_date='2025-01-07' and id >
8;"
+ contains("partitions=1/10 (p20250107)")
+ contains("PREDICATES: (id[#0] > 8)")
+ }
+ // >
+ explain {
+ sql "select * from test_list_date where d_date>'2025-01-07' and id >
8;"
+ contains("partitions=3/10 (p20250108,p20250109,p20250110)")
+ contains("PREDICATES: (id[#0] > 8)")
+ }
+ // and
+ explain {
+ sql "select * from test_list_date where d_date>='2025-01-06' and
d_date<='2025-01-07' and id <2;"
+ contains("partitions=2/10 (p20250106,p20250107)")
+ contains("PREDICATES: (id[#0] < 2)")
+ }
+ // or
+ explain {
+ sql "select * from test_list_date where (d_date>='2025-01-08' or
d_date<='2025-01-03') and id < 2;"
+ contains("partitions=6/10
(p20250101,p20250102,p20250103,p20250108,p20250109,p20250110)")
+ contains("PREDICATES: (id[#0] < 2)")
+ }
+
+ // or
+ explain {
+ sql "select * from test_list_date where d_date>='2025-01-08' or
(d_date<='2025-01-03' and id < 2);"
+ contains("partitions=6/10
(p20250101,p20250102,p20250103,p20250108,p20250109,p20250110)")
+ contains("PREDICATES: ((d_date[#1] >= '2025-01-08') OR ((d_date[#1] <=
'2025-01-03') AND (id[#0] < 2)))")
+ }
+ // or
+ explain {
+ sql "select * from test_list_date where (d_date>='2025-01-06' or
d_date<='2025-01-07') and id < 2;"
+ contains("PREDICATES: (id[#0] < 2)")
+ contains("partitions=10/10
(p20250101,p20250102,p20250103,p20250104,p20250105,p20250106,p20250107,p20250108,p20250109,p20250110)")
+ }
+
+ // not in
+ explain {
+ sql "select * from test_list_date where d_date not in ('2025-01-01',
'2025-01-02');"
+ contains("partitions=8/10
(p20250103,p20250104,p20250105,p20250106,p20250107,p20250108,p20250109,p20250110)")
+ notContains("PREDICATES")
+ }
+
+ // in
+ explain {
+ sql "select * from test_list_date where d_date in ('2025-01-01',
'2025-01-03', '2025-01-05');"
+ contains("partitions=3/10 (p20250101,p20250103,p20250105)")
+ notContains("PREDICATES")
+ }
+
+ // between
+ explain {
+ sql "select * from test_list_date where d_date between '2025-01-03'
and '2025-01-05';"
+ contains("partitions=3/10 (p20250103,p20250104,p20250105)")
+ notContains("PREDICATES")
+ }
+
+ // and or
+ explain {
+ sql "select * from test_list_date where (d_date >= '2025-01-03' and
d_date <= '2025-01-05') or (d_date >= '2025-01-08' and d_date <= '2025-01-09');"
+ contains("partitions=5/10
(p20250103,p20250104,p20250105,p20250108,p20250109)")
+ notContains("PREDICATES")
+ }
+
+ explain {
+ sql "select * from test_list_date where (d_date = '2025-01-05' or
(d_date = '2025-01-06' and id > 5)) and id < 10;"
+ contains("partitions=2/10 (p20250105,p20250106)")
+ }
+
+ explain {
+ sql "select * from test_list_date where ((d_date >= '2025-01-03' and
d_date <= '2025-01-05') or d_date = '2025-01-08') and (id = 3 or id = 4 or id =
8);"
+ contains("partitions=4/10 (p20250103,p20250104,p20250105,p20250108)")
+ contains("PREDICATES: id[#0] IN (3, 4, 8)")
+ }
+
+ explain {
+ sql "select * from test_list_date where (d_date = '2025-01-03' and id
> 2) or (d_date = '2025-01-07' and id < 8);"
+ contains("partitions=2/10 (p20250103,p20250107)")
+ }
+
+ explain {
+ sql "select * from test_list_date where (d_date in
('2025-01-02','2025-01-04') and id % 2 = 0) or (d_date = '2025-01-06' and id %
2 = 1);"
+ contains("partitions=3/10 (p20250102,p20250104,p20250106)")
+ }
+ // is null
+ explain {
+ sql "select * from test_list_date where d_date is null;"
+ contains("VEMPTYSET")
+ }
+
+ // func
+ explain {
+ sql "select * from test_list_date where year(d_date) = 2025;"
+ contains("partitions=10/10
(p20250101,p20250102,p20250103,p20250104,p20250105,p20250106,p20250107,p20250108,p20250109,p20250110)")
+ }
+
+ // test date(dt) predicate rewrite before partition prune
+ multi_sql """
+ drop table if exists test_list_datetime;
+ create table test_list_datetime (id int, d_date datetime)
+ PARTITION BY LIST(d_date)
+ (
+ PARTITION `p20250101` VALUES IN ('2025-01-01 00:00:00'),
+ PARTITION `p20250102` VALUES IN ('2025-01-02 00:00:00'),
+ PARTITION `p20250103` VALUES IN ('2025-01-03 00:00:00'),
+ PARTITION `p20250104` VALUES IN ('2025-01-04 00:00:00'),
+ PARTITION `p20250105` VALUES IN ('2025-01-05 00:00:00'),
+ PARTITION `p20250106` VALUES IN ('2025-01-06 00:00:00'),
+ PARTITION `p20250107` VALUES IN ('2025-01-07 00:00:00'),
+ PARTITION `p20250108` VALUES IN ('2025-01-08 00:00:00'),
+ PARTITION `p20250109` VALUES IN ('2025-01-09 00:00:00'),
+ PARTITION `p20250110` VALUES IN ('2025-01-10 00:00:00')
+ )
+ properties('replication_num'='1');
+ insert into test_list_datetime values
+ (1,'2025-01-01') , (2, '2025-01-02') , (3, '2025-01-03'), (4,
'2025-01-04') , (5, '2025-01-05'), (6,'2025-01-06') ,
+ (7, '2025-01-07') , (8, '2025-01-08'), (9, '2025-01-09') , (10,
'2025-01-10');"""
+
+ explain {
+ sql "select * from test_list_datetime where date(d_date) in
('2025-01-09','2025-01-10');"
+ contains("partitions=2/10 (p20250109,p20250110)")
+ notContains("PREDICATES")
+ }
+
+ // multi column partition
+ multi_sql """
+ drop table if exists test_list_multi_column;
+ create table test_list_multi_column (id int, d_date date)
+ PARTITION BY LIST(id,d_date)
+ (
+ PARTITION `p20250101` VALUES IN ((1,'2025-01-01')),
+ PARTITION `p20250102` VALUES IN ((2,'2025-01-02')),
+ PARTITION `p20250103` VALUES IN ((3,'2025-01-03')),
+ PARTITION `p20250104` VALUES IN ((4,'2025-01-04')),
+ PARTITION `p20250105` VALUES IN ((5,'2025-01-05')),
+ PARTITION `p20250106` VALUES IN ((6,'2025-01-06')),
+ PARTITION `p20250107` VALUES IN ((7,'2025-01-07')),
+ PARTITION `p20250108` VALUES IN ((8,'2025-01-08')),
+ PARTITION `p20250109` VALUES IN ((9,'2025-01-09')),
+ PARTITION `p20250110` VALUES IN ((10,'2025-01-10'))
+ )
+ properties('replication_num'='1');
+ insert into test_list_multi_column values
+ (1,'2025-01-01') , (2, '2025-01-02') , (3, '2025-01-03'), (4,
'2025-01-04') , (5, '2025-01-05'), (6,'2025-01-06') ,
+ (7, '2025-01-07') , (8, '2025-01-08'), (9, '2025-01-09') , (10,
'2025-01-10');
+ """
+ explain {
+ sql "select * from test_list_multi_column where id=1 and
d_date='2025-01-01';"
+ contains("partitions=1/10 (p20250101)")
+ notContains("PREDICATES")
+ }
+ explain {
+ sql "select * from test_list_multi_column where id=1 and
d_date='2025-01-02';"
+ contains("VEMPTYSET")
+ notContains("PREDICATES")
+ }
+ explain {
+ sql "select * from test_list_multi_column where (id=1 and
d_date='2025-01-01') or id=2;"
+ contains("partitions=2/10 (p20250101,p20250102)")
+ notContains("PREDICATES")
+ }
+
+ explain {
+ sql "select * from test_list_multi_column where (id = 1 and d_date =
'2025-01-01') or (id = 2 and d_date = '2025-01-02');"
+ contains("partitions=2/10 (p20250101,p20250102)")
+ notContains("PREDICATES")
+ }
+
+ explain {
+ sql "select * from test_list_multi_column where id = 1 and (d_date =
'2025-01-01' or d_date = '2025-01-02');"
+ contains("partitions=1/10 (p20250101)")
+ notContains("PREDICATES")
+ }
+
+ explain {
+ sql "select * from test_list_multi_column where (id = 1 or id = 2) and
d_date = '2025-01-01';"
+ contains("partitions=1/10 (p20250101)")
+ notContains("PREDICATES")
+ }
+
+ // test default partition
+ multi_sql """
+ drop table if exists list_par_data_migration;
+ CREATE TABLE IF NOT EXISTS list_par_data_migration (
+ k1 tinyint NOT NULL,
+ k2 smallint NOT NULL,
+ k3 int NOT NULL,
+ k4 bigint NOT NULL,
+ k5 decimal(9, 3) NOT NULL,
+ k8 double max NOT NULL,
+ k9 float sum NOT NULL )
+ AGGREGATE KEY(k1,k2,k3,k4,k5)
+ PARTITION BY LIST(k1) (
+ PARTITION p1 VALUES IN ("1","2","3","4"),
+ PARTITION p2 VALUES IN ("5","6","7","8"),
+ PARTITION p3 )
+ DISTRIBUTED BY HASH(k1) BUCKETS 5 properties("replication_num" = "1");
+ """
+ sql """insert into list_par_data_migration values
(1,1,1,1,24453.325,1,1)"""
+ sql """insert into list_par_data_migration values
(10,1,1,1,24453.325,1,1)"""
+ sql """insert into list_par_data_migration values
(11,1,1,1,24453.325,1,1)"""
+ qt_default_partition """
+ select * from list_par_data_migration partition p3 where k1=10 order by
k1;"""
+ // test manuallySpecifiedPartitions
+ explain {
+ sql "select * from list_par_data_migration partition p2 where k1=2
order by k1;"
+ contains("VEMPTYSET")
+ }
+ explain {
+ sql "select * from list_par_data_migration partition p1 where k1=2
order by k1;"
+ contains("partitions=1/3 (p1)")
+ // k1=2 can not be deleted because k1=2 is not always true in p1
+ contains("PREDICATES: (k1[#0] = 2)")
+ }
+ explain {
+ sql "select * from list_par_data_migration partition p1 where k1=1 or
k1=2 or k1=3 or k1=4 order by k1;"
+ contains("partitions=1/3 (p1)")
+ notContains("PREDICATES")
+ }
+
+ // test variable skip_prune_predicate
+ sql "set skip_prune_predicate = true"
+ explain {
+ sql "select * from list_par_data_migration partition p1 where k1=1 or
k1=2 or k1=3 or k1=4 order by k1;"
+ contains("partitions=1/3 (p1)")
+ contains("PREDICATES")
+ }
+ sql "set skip_prune_predicate = false"
+
+ sql " drop table if exists test_list_multi_column_unique;"
+ sql """ create table test_list_multi_column_unique (id int, d_date date,
c02 int default 1) unique key (id, d_date)
+ PARTITION BY LIST(id,d_date)
+ (
+ PARTITION `p20250101` VALUES IN ((1,'2025-01-01')),
+ PARTITION `p20250102` VALUES IN ((2,'2025-01-02')),
+ PARTITION `p20250103` VALUES IN ((3,'2025-01-03')),
+ PARTITION `p20250104` VALUES IN ((4,'2025-01-04')),
+ PARTITION `p20250105` VALUES IN ((5,'2025-01-05')),
+ PARTITION `p20250106` VALUES IN ((6,'2025-01-06')),
+ PARTITION `p20250107` VALUES IN ((7,'2025-01-07')),
+ PARTITION `p20250108` VALUES IN ((8,'2025-01-08')),
+ PARTITION `p20250109` VALUES IN ((9,'2025-01-09')),
+ PARTITION `p20250110` VALUES IN ((10,'2025-01-10'))
+ ) DISTRIBUTED BY HASH(id) BUCKETS 10
+ properties('replication_num'='1');"""
+ sql """insert into test_list_multi_column_unique(id, d_date) values
+ (1,'2025-01-01') , (2, '2025-01-02') , (3, '2025-01-03'), (4,
'2025-01-04') , (5, '2025-01-05'), (6,'2025-01-06') ,
+ (7, '2025-01-07') , (8, '2025-01-08'), (9, '2025-01-09') , (10,
'2025-01-10');
+ """
+
+ // add test for delete(not support optimize)
+ explain {
+ sql "delete from test_list_multi_column_unique where id=1 and
d_date='2025-01-01';"
+ contains("partitions=1/10 (p20250101)")
+ contains("(id[#0] = 1) AND (d_date[#1] = '2025-01-01')")
+ }
+
+ // add test for update(support optimize)
+ explain {
+ sql "update test_list_multi_column_unique set c02 = 1 where id=1 and
d_date='2025-01-01';"
+ contains("partitions=1/10 (p20250101)")
+ notContains("(id[#0] = 1) AND (d_date[#1] = '2025-01-01')")
+ }
+ sql """update test_list_multi_column_unique set c02 = 10 where id=1 and
d_date='2025-01-01';"""
+ qt_update "select * from test_list_multi_column_unique order by id;"
+}
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]