This is an automated email from the ASF dual-hosted git repository.
morrysnow pushed a commit to branch branch-3.1
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/branch-3.1 by this push:
new 3b868c9bbb7 branch-3.1: [opt](nereids) use binary search to prune
partitions #44586 (#51966)
3b868c9bbb7 is described below
commit 3b868c9bbb7e8d8adfd0e00a505388551bbaf160
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Jun 20 10:30:57 2025 +0800
branch-3.1: [opt](nereids) use binary search to prune partitions #44586
(#51966)
Cherry-picked from #44586
Co-authored-by: 924060929 <[email protected]>
---
.../main/java/org/apache/doris/common/Config.java | 42 +++-
.../java/org/apache/doris/common/ConfigBase.java | 2 +-
.../main/java/org/apache/doris/catalog/Env.java | 10 +-
.../java/org/apache/doris/catalog/OlapTable.java | 4 +-
.../cache/NereidsSortedPartitionsCacheManager.java | 197 +++++++++++++++
.../common/{ => cache}/NereidsSqlCacheManager.java | 5 +-
.../rules/expression/rules/MultiColumnBound.java | 59 +++++
.../expression/rules/PartitionItemToRange.java | 87 +++++++
.../rules/PartitionPredicateToRange.java | 267 +++++++++++++++++++++
.../rules/expression/rules/PartitionPruner.java | 137 +++++++++--
.../expression/rules/SortedPartitionRanges.java | 72 ++++++
.../rules/rewrite/PruneOlapScanPartition.java | 13 +-
.../java/org/apache/doris/qe/StmtExecutor.java | 2 +-
13 files changed, 868 insertions(+), 29 deletions(-)
diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
index e6eaf35b3ab..877c596043e 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/common/Config.java
@@ -1449,10 +1449,34 @@ public class Config extends ConfigBase {
@ConfField(
mutable = true,
masterOnly = false,
- callbackClassString =
"org.apache.doris.common.NereidsSqlCacheManager$UpdateConfig"
+ callbackClassString =
"org.apache.doris.common.cache.NereidsSqlCacheManager$UpdateConfig",
+ description = {
+ "当前默认设置为 300,用来控制控制NereidsSqlCacheManager中sql
cache过期时间,超过一段时间不访问cache会被回收",
+ "The current default setting is 300, which is used to
control the expiration time of SQL cache"
+ + "in NereidsSqlCacheManager. If the cache is not
accessed for a period of time, "
+ + "it will be reclaimed"
+ }
)
public static int expire_sql_cache_in_fe_second = 300;
+
+ /**
+ * Expire sql sql in frontend time
+ */
+ @ConfField(
+ mutable = true,
+ masterOnly = false,
+ callbackClassString =
"org.apache.doris.common.cache.NereidsSortedPartitionsCacheManager$UpdateConfig",
+ description = {
+ "当前默认设置为
300,用来控制控制NereidsSortedPartitionsCacheManager中分区元数据缓存过期时间,"
+ + "超过一段时间不访问cache会被回收",
+ "The current default setting is 300, which is used to control
the expiration time of "
+ + "the partition metadata cache in
NereidsSortedPartitionsCheManager. "
+ + "If the cache is not accessed for a period of time, it
will be reclaimed"
+ }
+ )
+ public static int expire_cache_partition_meta_table_in_fe_second = 300;
+
/**
* Set the maximum number of rows that can be cached
*/
@@ -2278,8 +2302,7 @@ public class Config extends ConfigBase {
*/
@ConfField(
mutable = true,
- varType = VariableAnnotation.EXPERIMENTAL,
- callbackClassString =
"org.apache.doris.common.NereidsSqlCacheManager$UpdateConfig",
+ callbackClassString =
"org.apache.doris.common.cache.NereidsSqlCacheManager$UpdateConfig",
description = {
"当前默认设置为 100,用来控制控制NereidsSqlCacheManager管理的sql cache数量。",
"Now default set to 100, this config is used to control the
number of "
@@ -2288,6 +2311,19 @@ public class Config extends ConfigBase {
)
public static int sql_cache_manage_num = 100;
+ @ConfField(
+ mutable = true,
+ callbackClassString =
"org.apache.doris.common.cache.NereidsSortedPartitionsCacheManager$UpdateConfig",
+ description = {
+ "当前默认设置为
100,用来控制控制NereidsSortedPartitionsCacheManager中有序分区元数据的缓存个数,"
+ + "用于加速分区裁剪",
+ "The current default setting is 100, which is used to
control the number of ordered "
+ + "partition metadata caches in
NereidsSortedPartitionsCacheManager, "
+ + "and to accelerate partition pruning"
+ }
+ )
+ public static int cache_partition_meta_table_manage_num = 100;
+
/**
* Maximum number of events to poll in each RPC.
*/
diff --git a/fe/fe-common/src/main/java/org/apache/doris/common/ConfigBase.java
b/fe/fe-common/src/main/java/org/apache/doris/common/ConfigBase.java
index 18ae1dc1c01..71819217925 100644
--- a/fe/fe-common/src/main/java/org/apache/doris/common/ConfigBase.java
+++ b/fe/fe-common/src/main/java/org/apache/doris/common/ConfigBase.java
@@ -72,7 +72,7 @@ public class ConfigBase {
void handle(Field field, String confVal) throws Exception;
}
- static class DefaultConfHandler implements ConfHandler {
+ public static class DefaultConfHandler implements ConfHandler {
@Override
public void handle(Field field, String confVal) throws Exception {
setConfigField(field, confVal);
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
index 90fbb1fad34..6b17802ef45 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/Env.java
@@ -107,10 +107,11 @@ import org.apache.doris.common.FeConstants;
import org.apache.doris.common.FeMetaVersion;
import org.apache.doris.common.LogUtils;
import org.apache.doris.common.MetaNotFoundException;
-import org.apache.doris.common.NereidsSqlCacheManager;
import org.apache.doris.common.Pair;
import org.apache.doris.common.ThreadPoolManager;
import org.apache.doris.common.UserException;
+import org.apache.doris.common.cache.NereidsSortedPartitionsCacheManager;
+import org.apache.doris.common.cache.NereidsSqlCacheManager;
import org.apache.doris.common.io.CountingDataOutputStream;
import org.apache.doris.common.io.Text;
import org.apache.doris.common.lock.MonitoredReentrantLock;
@@ -567,6 +568,8 @@ public class Env {
private final NereidsSqlCacheManager sqlCacheManager;
+ private final NereidsSortedPartitionsCacheManager
sortedPartitionsCacheManager;
+
private final SplitSourceManager splitSourceManager;
private final GlobalExternalTransactionInfoMgr
globalExternalTransactionInfoMgr;
@@ -819,6 +822,7 @@ public class Env {
this.insertOverwriteManager = new InsertOverwriteManager();
this.dnsCache = new DNSCache();
this.sqlCacheManager = new NereidsSqlCacheManager();
+ this.sortedPartitionsCacheManager = new
NereidsSortedPartitionsCacheManager();
this.splitSourceManager = new SplitSourceManager();
this.globalExternalTransactionInfoMgr = new
GlobalExternalTransactionInfoMgr();
this.tokenManager = new TokenManager();
@@ -6735,6 +6739,10 @@ public class Env {
return sqlCacheManager;
}
+ public NereidsSortedPartitionsCacheManager
getSortedPartitionsCacheManager() {
+ return sortedPartitionsCacheManager;
+ }
+
public SplitSourceManager getSplitSourceManager() {
return splitSourceManager;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
index 06e29ae84c2..3a2a3b1f60a 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/OlapTable.java
@@ -1272,12 +1272,12 @@ public class OlapTable extends Table implements
MTMVRelatedTableIf, GsonPostProc
}
// get only temp partitions
- public Collection<Partition> getAllTempPartitions() {
+ public List<Partition> getAllTempPartitions() {
return tempPartitions.getAllPartitions();
}
// get all partitions including temp partitions
- public Collection<Partition> getAllPartitions() {
+ public List<Partition> getAllPartitions() {
List<Partition> partitions =
Lists.newArrayList(idToPartition.values());
partitions.addAll(tempPartitions.getAllPartitions());
return partitions;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/cache/NereidsSortedPartitionsCacheManager.java
b/fe/fe-core/src/main/java/org/apache/doris/common/cache/NereidsSortedPartitionsCacheManager.java
new file mode 100644
index 00000000000..499c3b46709
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/common/cache/NereidsSortedPartitionsCacheManager.java
@@ -0,0 +1,197 @@
+// 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.common.cache;
+
+import org.apache.doris.catalog.DatabaseIf;
+import org.apache.doris.catalog.Env;
+import org.apache.doris.catalog.OlapTable;
+import org.apache.doris.catalog.PartitionInfo;
+import org.apache.doris.catalog.PartitionItem;
+import org.apache.doris.common.Config;
+import org.apache.doris.common.ConfigBase.DefaultConfHandler;
+import org.apache.doris.datasource.CatalogIf;
+import org.apache.doris.nereids.rules.expression.rules.MultiColumnBound;
+import org.apache.doris.nereids.rules.expression.rules.PartitionItemToRange;
+import org.apache.doris.nereids.rules.expression.rules.SortedPartitionRanges;
+import
org.apache.doris.nereids.rules.expression.rules.SortedPartitionRanges.PartitionItemAndId;
+import
org.apache.doris.nereids.rules.expression.rules.SortedPartitionRanges.PartitionItemAndRange;
+
+import com.github.benmanes.caffeine.cache.Cache;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.google.common.collect.Range;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import org.apache.hadoop.util.Lists;
+
+import java.lang.reflect.Field;
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+/**
+ * This cache is used to sort the table partitions by range, so we can do
binary search to skip
+ * filter the huge numbers of partitions, for example, the table partition
column is `dt date`
+ * and one date for one partition, range from '2017-01-01' to '2025-01-01',
for partition predicate
+ * `where dt = '2024-12-24'`, we can fast jump to '2024-12-24' within few
partition range comparison,
+ * and the QPS can be improved
+ */
+public class NereidsSortedPartitionsCacheManager {
+ private volatile Cache<TableIdentifier, PartitionCacheContext>
partitionCaches;
+
+ public NereidsSortedPartitionsCacheManager() {
+ partitionCaches = buildCaches(
+ Config.cache_partition_meta_table_manage_num,
+ Config.expire_cache_partition_meta_table_in_fe_second
+ );
+ }
+
+ public Optional<SortedPartitionRanges<?>> get(OlapTable table) {
+ DatabaseIf<?> database = table.getDatabase();
+ if (database == null) {
+ return Optional.empty();
+ }
+ CatalogIf<?> catalog = database.getCatalog();
+ if (catalog == null) {
+ return Optional.empty();
+ }
+ TableIdentifier key = new TableIdentifier(
+ catalog.getName(), database.getFullName(), table.getName());
+ PartitionCacheContext partitionCacheContext =
partitionCaches.getIfPresent(key);
+ if (partitionCacheContext == null) {
+ return Optional.of(loadCache(key, table));
+ }
+ if (table.getId() != partitionCacheContext.tableId
+ || table.getVisibleVersion() !=
partitionCacheContext.tableVersion) {
+ partitionCaches.invalidate(key);
+ return Optional.of(loadCache(key, table));
+ }
+ return Optional.of(partitionCacheContext.sortedPartitionRanges);
+ }
+
+ private SortedPartitionRanges<?> loadCache(TableIdentifier key, OlapTable
olapTable) {
+ PartitionInfo partitionInfo = olapTable.getPartitionInfo();
+ Map<Long, PartitionItem> allPartitions =
partitionInfo.getIdToItem(false);
+ List<Entry<Long, PartitionItem>> sortedList =
Lists.newArrayList(allPartitions.entrySet());
+ List<PartitionItemAndRange<?>> sortedRanges =
Lists.newArrayListWithCapacity(allPartitions.size());
+ List<PartitionItemAndId<?>> defaultPartitions = Lists.newArrayList();
+ for (Entry<Long, PartitionItem> entry : sortedList) {
+ PartitionItem partitionItem = entry.getValue();
+ Long id = entry.getKey();
+ if (!partitionItem.isDefaultPartition()) {
+ List<Range<MultiColumnBound>> ranges =
PartitionItemToRange.toRanges(partitionItem);
+ for (Range<MultiColumnBound> range : ranges) {
+ sortedRanges.add(new PartitionItemAndRange<>(id,
partitionItem, range));
+ }
+ } else {
+ defaultPartitions.add(new PartitionItemAndId<>(id,
partitionItem));
+ }
+ }
+
+ sortedRanges.sort((o1, o2) -> {
+ Range<MultiColumnBound> span1 = o1.range;
+ Range<MultiColumnBound> span2 = o2.range;
+ int result =
span1.lowerEndpoint().compareTo(span2.lowerEndpoint());
+ if (result != 0) {
+ return result;
+ }
+ result = span1.upperEndpoint().compareTo(span2.upperEndpoint());
+ return result;
+ });
+ SortedPartitionRanges<?> sortedPartitionRanges = new
SortedPartitionRanges(
+ sortedRanges, defaultPartitions
+ );
+ PartitionCacheContext context = new PartitionCacheContext(
+ olapTable.getId(), olapTable.getVisibleVersion(),
sortedPartitionRanges);
+ partitionCaches.put(key, context);
+ return sortedPartitionRanges;
+ }
+
+ private static Cache<TableIdentifier, PartitionCacheContext> buildCaches(
+ int sortedPartitionTableManageNum, int
expireSortedPartitionTableInFeSecond) {
+ Caffeine<Object, Object> cacheBuilder = Caffeine.newBuilder()
+ // auto evict cache when jvm memory too low
+ .softValues();
+ if (sortedPartitionTableManageNum > 0) {
+ cacheBuilder =
cacheBuilder.maximumSize(sortedPartitionTableManageNum);
+ }
+ if (expireSortedPartitionTableInFeSecond > 0) {
+ cacheBuilder =
cacheBuilder.expireAfterAccess(Duration.ofSeconds(expireSortedPartitionTableInFeSecond));
+ }
+
+ return cacheBuilder.build();
+ }
+
+ public static synchronized void updateConfig() {
+ Env currentEnv = Env.getCurrentEnv();
+ if (currentEnv == null) {
+ return;
+ }
+ NereidsSortedPartitionsCacheManager cacheManager =
currentEnv.getSortedPartitionsCacheManager();
+ if (cacheManager == null) {
+ return;
+ }
+
+ Cache<TableIdentifier, PartitionCacheContext> caches = buildCaches(
+ Config.cache_partition_meta_table_manage_num,
+ Config.expire_cache_partition_meta_table_in_fe_second
+ );
+ caches.putAll(cacheManager.partitionCaches.asMap());
+ cacheManager.partitionCaches = caches;
+ }
+
+ @Data
+ @AllArgsConstructor
+ private static class TableIdentifier {
+ public final String catalog;
+ public final String db;
+ public final String table;
+ }
+
+ private static class PartitionCacheContext {
+ private final long tableId;
+ private final long tableVersion;
+ private final SortedPartitionRanges sortedPartitionRanges;
+
+ public PartitionCacheContext(
+ long tableId, long tableVersion, SortedPartitionRanges
sortedPartitionRanges) {
+ this.tableId = tableId;
+ this.tableVersion = tableVersion;
+ this.sortedPartitionRanges = sortedPartitionRanges;
+ }
+
+ @Override
+ public String toString() {
+ return "PartitionCacheContext(tableId="
+ + tableId + ", tableVersion=" + tableVersion
+ + ", partitionNum=" +
sortedPartitionRanges.sortedPartitions.size() + ")";
+ }
+ }
+
+ // NOTE: used in
Config.cache_partition_meta_table_manage_num.callbackClassString and
+ //
Config.expire_cache_partition_meta_table_in_fe_second.callbackClassString,
+ // don't remove it!
+ public static class UpdateConfig extends DefaultConfHandler {
+ @Override
+ public void handle(Field field, String confVal) throws Exception {
+ super.handle(field, confVal);
+ NereidsSortedPartitionsCacheManager.updateConfig();
+ }
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/common/NereidsSqlCacheManager.java
b/fe/fe-core/src/main/java/org/apache/doris/common/cache/NereidsSqlCacheManager.java
similarity index 99%
rename from
fe/fe-core/src/main/java/org/apache/doris/common/NereidsSqlCacheManager.java
rename to
fe/fe-core/src/main/java/org/apache/doris/common/cache/NereidsSqlCacheManager.java
index 62d052f18b6..5aecf5ff4bb 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/common/NereidsSqlCacheManager.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/common/cache/NereidsSqlCacheManager.java
@@ -15,7 +15,7 @@
// specific language governing permissions and limitations
// under the License.
-package org.apache.doris.common;
+package org.apache.doris.common.cache;
import org.apache.doris.analysis.UserIdentity;
import org.apache.doris.catalog.DatabaseIf;
@@ -25,7 +25,10 @@ import org.apache.doris.catalog.Partition;
import org.apache.doris.catalog.TableIf;
import org.apache.doris.catalog.TableIf.TableType;
import org.apache.doris.catalog.View;
+import org.apache.doris.common.Config;
import org.apache.doris.common.ConfigBase.DefaultConfHandler;
+import org.apache.doris.common.Pair;
+import org.apache.doris.common.Status;
import org.apache.doris.common.util.DebugUtil;
import org.apache.doris.datasource.CatalogIf;
import org.apache.doris.metric.MetricRepo;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/MultiColumnBound.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/MultiColumnBound.java
new file mode 100644
index 00000000000..078cc03733c
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/MultiColumnBound.java
@@ -0,0 +1,59 @@
+// 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.expression.rules;
+
+import org.apache.doris.nereids.util.Utils;
+
+import java.util.List;
+import java.util.Objects;
+
+/** MultiColumnBound */
+public class MultiColumnBound implements Comparable<MultiColumnBound> {
+ private final List<ColumnBound> columnBounds;
+
+ public MultiColumnBound(List<ColumnBound> columnBounds) {
+ this.columnBounds = Utils.fastToImmutableList(
+ Objects.requireNonNull(columnBounds, "column bounds can not be
null")
+ );
+ }
+
+ @Override
+ public int compareTo(MultiColumnBound o) {
+ for (int i = 0; i < columnBounds.size(); i++) {
+ ColumnBound columnBound = columnBounds.get(i);
+ ColumnBound otherColumnBound = o.columnBounds.get(i);
+ int result = columnBound.compareTo(otherColumnBound);
+ if (result != 0) {
+ return result;
+ }
+ }
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < columnBounds.size(); i++) {
+ if (i > 0) {
+ sb.append(",");
+ }
+ sb.append(columnBounds.get(i));
+ }
+ return sb.toString();
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionItemToRange.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionItemToRange.java
new file mode 100644
index 00000000000..435dd700713
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionItemToRange.java
@@ -0,0 +1,87 @@
+// 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.expression.rules;
+
+import org.apache.doris.analysis.LiteralExpr;
+import org.apache.doris.catalog.ListPartitionItem;
+import org.apache.doris.catalog.PartitionItem;
+import org.apache.doris.catalog.PartitionKey;
+import org.apache.doris.catalog.RangePartitionItem;
+import org.apache.doris.nereids.trees.expressions.literal.Literal;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.types.DataType;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Range;
+
+import java.util.List;
+
+/** PartitionItemToRange */
+public class PartitionItemToRange {
+ /** toRangeSets */
+ public static List<Range<MultiColumnBound>> toRanges(PartitionItem
partitionItem) {
+ if (partitionItem instanceof RangePartitionItem) {
+ Range<PartitionKey> range = partitionItem.getItems();
+ PartitionKey lowerKey = range.lowerEndpoint();
+ ImmutableList.Builder<ColumnBound> lowerBounds
+ =
ImmutableList.builderWithExpectedSize(lowerKey.getKeys().size());
+ for (LiteralExpr key : lowerKey.getKeys()) {
+ Literal literal = toNereidsLiteral(key);
+ lowerBounds.add(ColumnBound.of(literal));
+ }
+
+ PartitionKey upperKey = range.upperEndpoint();
+ ImmutableList.Builder<ColumnBound> upperBounds
+ =
ImmutableList.builderWithExpectedSize(lowerKey.getKeys().size());
+ for (LiteralExpr key : upperKey.getKeys()) {
+ Literal literal = Literal.fromLegacyLiteral(key,
key.getType());
+ upperBounds.add(ColumnBound.of(literal));
+ }
+
+ return ImmutableList.of(Range.closedOpen(
+ new MultiColumnBound(lowerBounds.build()),
+ new MultiColumnBound(upperBounds.build())
+ ));
+ } else if (partitionItem instanceof ListPartitionItem) {
+ List<PartitionKey> partitionKeys = partitionItem.getItems();
+ ImmutableList.Builder<Range<MultiColumnBound>> ranges
+ =
ImmutableList.builderWithExpectedSize(partitionKeys.size());
+ for (PartitionKey partitionKey : partitionKeys) {
+ ImmutableList.Builder<ColumnBound> bounds
+ =
ImmutableList.builderWithExpectedSize(partitionKeys.size());
+ for (LiteralExpr key : partitionKey.getKeys()) {
+ Literal literal = toNereidsLiteral(key);
+ bounds.add(ColumnBound.of(literal));
+ }
+ MultiColumnBound bound = new MultiColumnBound(bounds.build());
+ ranges.add(Range.singleton(bound));
+ }
+ return ranges.build();
+ } else {
+ throw new
UnsupportedOperationException(partitionItem.getClass().getName());
+ }
+ }
+
+ private static Literal toNereidsLiteral(LiteralExpr partitionKeyLiteral) {
+ if (!partitionKeyLiteral.isMinValue()) {
+ return Literal.fromLegacyLiteral(partitionKeyLiteral,
partitionKeyLiteral.getType());
+ } else {
+ return new
NullLiteral(DataType.fromCatalogType(partitionKeyLiteral.getType()));
+ }
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPredicateToRange.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPredicateToRange.java
new file mode 100644
index 00000000000..88ecdab607c
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/PartitionPredicateToRange.java
@@ -0,0 +1,267 @@
+// 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.expression.rules;
+
+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.GreaterThanEqual;
+import org.apache.doris.nereids.trees.expressions.InPredicate;
+import org.apache.doris.nereids.trees.expressions.IsNull;
+import org.apache.doris.nereids.trees.expressions.LessThan;
+import org.apache.doris.nereids.trees.expressions.LessThanEqual;
+import org.apache.doris.nereids.trees.expressions.Not;
+import org.apache.doris.nereids.trees.expressions.NullSafeEqual;
+import org.apache.doris.nereids.trees.expressions.Or;
+import org.apache.doris.nereids.trees.expressions.Slot;
+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.MaxLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import
org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisitor;
+import org.apache.doris.nereids.types.DataType;
+import org.apache.doris.nereids.util.Utils;
+
+import com.google.common.collect.BoundType;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+import com.google.common.collect.TreeRangeSet;
+import org.apache.hadoop.util.Lists;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.Set;
+
+/** PartitionPredicateToRange */
+public class PartitionPredicateToRange extends
DefaultExpressionVisitor<RangeSet<MultiColumnBound>, Void> {
+ private List<Slot> columns;
+ private Set<Integer> slotIds;
+
+ /** PartitionPredicateToRange */
+ public PartitionPredicateToRange(List<Slot> columns) {
+ this.columns = Utils.fastToImmutableList(
+ Objects.requireNonNull(columns, "columns can not be null")
+ );
+
+ ImmutableSet.Builder<Integer> slotIds =
ImmutableSet.builderWithExpectedSize(columns.size());
+ for (Slot column : columns) {
+ slotIds.add(column.getExprId().asInt());
+ }
+ this.slotIds = slotIds.build();
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitAnd(And and, Void context) {
+ boolean first = true;
+ RangeSet<MultiColumnBound> intersects = null;
+ for (Expression child : and.children()) {
+ RangeSet<MultiColumnBound> childRanges = child.accept(this,
context);
+
+ // if some conjunct not supported, just skip it safety because the
big ranges contains
+ // all partitions the predicates need
+ if (childRanges == null) {
+ continue;
+ } else if (first) {
+ first = false;
+ intersects = childRanges;
+ continue;
+ }
+
+ for (Range<MultiColumnBound> childRange : childRanges.asRanges()) {
+ intersects = intersects.subRangeSet(childRange);
+ if (intersects.isEmpty()) {
+ break;
+ }
+ }
+ if (intersects.isEmpty()) {
+ break;
+ }
+ }
+ return intersects;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitOr(Or or, Void context) {
+ RangeSet<MultiColumnBound> intersects = TreeRangeSet.create();
+ for (Expression child : or.children()) {
+ RangeSet<MultiColumnBound> childRanges = child.accept(this,
context);
+
+ // if any predicate can not parse to range, we can not do binary
search
+ if (childRanges == null) {
+ return null;
+ }
+ intersects.addAll(childRanges);
+ }
+ return intersects;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitNot(Not not, Void context) {
+ Expression child = not.child();
+ if (child instanceof IsNull && ((IsNull) child).child() instanceof
SlotReference) {
+ SlotReference slot = (SlotReference) ((IsNull) child).child();
+ int slotId = slot.getExprId().asInt();
+ if (slotIds.contains(slotId)) {
+ DataType dataType = child.getDataType();
+ return toRangeSet(slot,
+ new NullLiteral(dataType), BoundType.OPEN,
+ new MaxLiteral(dataType), BoundType.CLOSED
+ );
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitIsNull(IsNull isNull, Void context)
{
+ Expression child = isNull.child();
+ if (child instanceof SlotReference &&
slotIds.contains(((SlotReference) child).getExprId().asInt())) {
+ NullLiteral nullLiteral = new NullLiteral(child.getDataType());
+ return toRangeSet((SlotReference) child, nullLiteral,
BoundType.CLOSED, nullLiteral, BoundType.CLOSED);
+ }
+ return null;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitEqualTo(EqualTo equalTo, Void
context) {
+ Expression left = equalTo.left();
+ Expression right = equalTo.right();
+ if (left instanceof SlotReference && right instanceof Literal) {
+ if (slotIds.contains(((SlotReference) left).getExprId().asInt())) {
+ Literal literal = (Literal) right;
+ return toRangeSet((SlotReference) left, literal,
BoundType.CLOSED, literal, BoundType.CLOSED);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitNullSafeEqual(NullSafeEqual
nullSafeEqual, Void context) {
+ Expression left = nullSafeEqual.left();
+ Expression right = nullSafeEqual.right();
+ if (left instanceof SlotReference && right instanceof Literal) {
+ if (slotIds.contains(((SlotReference) left).getExprId().asInt())) {
+ Literal literal = (Literal) right;
+ return toRangeSet((SlotReference) left, literal,
BoundType.CLOSED, literal, BoundType.CLOSED);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitInPredicate(InPredicate
inPredicate, Void context) {
+ Expression compareExpr = inPredicate.getCompareExpr();
+ if (compareExpr instanceof SlotReference) {
+ SlotReference slot = (SlotReference) compareExpr;
+ if (slotIds.contains((slot).getExprId().asInt())) {
+ RangeSet<MultiColumnBound> union = TreeRangeSet.create();
+ for (Expression option : inPredicate.getOptions()) {
+ if (!(option instanceof Literal)) {
+ return null;
+ }
+ Literal literal = (Literal) option;
+ union.addAll(
+ toRangeSet(slot, literal, BoundType.CLOSED,
literal, BoundType.CLOSED)
+ );
+ }
+ return union;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitLessThan(LessThan lessThan, Void
context) {
+ Expression left = lessThan.left();
+ Expression right = lessThan.right();
+ if (left instanceof SlotReference && right instanceof Literal) {
+ if (slotIds.contains(((SlotReference) left).getExprId().asInt())) {
+ NullLiteral nullLiteral = new NullLiteral(right.getDataType());
+ Literal literal = (Literal) right;
+ return toRangeSet((SlotReference) left, nullLiteral,
BoundType.OPEN, literal, BoundType.OPEN);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitLessThanEqual(LessThanEqual
lessThanEqual, Void context) {
+ Expression left = lessThanEqual.left();
+ Expression right = lessThanEqual.right();
+ if (left instanceof SlotReference && right instanceof Literal) {
+ if (slotIds.contains(((SlotReference) left).getExprId().asInt())) {
+ NullLiteral nullLiteral = new NullLiteral(right.getDataType());
+ Literal literal = (Literal) right;
+ return toRangeSet((SlotReference) left, nullLiteral,
BoundType.OPEN, literal, BoundType.CLOSED);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitGreaterThan(GreaterThan
greaterThan, Void context) {
+ Expression left = greaterThan.left();
+ Expression right = greaterThan.right();
+ if (left instanceof SlotReference && right instanceof Literal) {
+ if (slotIds.contains(((SlotReference) left).getExprId().asInt())) {
+ Literal literal = (Literal) right;
+ MaxLiteral maxLiteral = new MaxLiteral(right.getDataType());
+ return toRangeSet((SlotReference) left, literal,
BoundType.OPEN, maxLiteral, BoundType.CLOSED);
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public RangeSet<MultiColumnBound> visitGreaterThanEqual(GreaterThanEqual
greaterThanEqual, Void context) {
+ Expression left = greaterThanEqual.left();
+ Expression right = greaterThanEqual.right();
+ if (left instanceof SlotReference && right instanceof Literal) {
+ if (slotIds.contains(((SlotReference) left).getExprId().asInt())) {
+ Literal literal = (Literal) right;
+ MaxLiteral maxLiteral = new MaxLiteral(right.getDataType());
+ return toRangeSet((SlotReference) left, literal,
BoundType.CLOSED, maxLiteral, BoundType.CLOSED);
+ }
+ }
+ return null;
+ }
+
+ private RangeSet<MultiColumnBound> toRangeSet(SlotReference slotReference,
+ Literal columnLowerBound, BoundType lowerBoundType,
+ Literal columnUpperBound, BoundType upperBoundType) {
+ List<ColumnBound> lowerBounds =
Lists.newArrayListWithCapacity(columns.size());
+ List<ColumnBound> upperBounds =
Lists.newArrayListWithCapacity(columns.size());
+ for (Slot column : columns) {
+ if (column.getExprId().asInt() ==
slotReference.getExprId().asInt()) {
+ lowerBounds.add(ColumnBound.of(columnLowerBound));
+ upperBounds.add(ColumnBound.of(columnUpperBound));
+ } else {
+ lowerBounds.add(ColumnBound.of(new
NullLiteral(slotReference.getDataType())));
+ upperBounds.add(ColumnBound.of(new
MaxLiteral(slotReference.getDataType())));
+ }
+ }
+ MultiColumnBound lowerBound = new MultiColumnBound(lowerBounds);
+ MultiColumnBound upperBound = new MultiColumnBound(upperBounds);
+
+ Range<MultiColumnBound> range = Range.range(lowerBound,
lowerBoundType, upperBound, upperBoundType);
+ return ImmutableRangeSet.of(range);
+ }
+}
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 ed783aa3d5a..6fc3aabf25e 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
@@ -22,6 +22,8 @@ import org.apache.doris.catalog.PartitionItem;
import org.apache.doris.catalog.RangePartitionItem;
import org.apache.doris.nereids.CascadesContext;
import org.apache.doris.nereids.rules.expression.ExpressionRewriteContext;
+import
org.apache.doris.nereids.rules.expression.rules.SortedPartitionRanges.PartitionItemAndId;
+import
org.apache.doris.nereids.rules.expression.rules.SortedPartitionRanges.PartitionItemAndRange;
import org.apache.doris.nereids.trees.expressions.Cast;
import org.apache.doris.nereids.trees.expressions.ComparisonPredicate;
import org.apache.doris.nereids.trees.expressions.Expression;
@@ -39,17 +41,22 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableList.Builder;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeSet;
+import com.google.common.collect.Sets;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
+import java.util.Optional;
+import java.util.Set;
/**
* PartitionPruner
*/
public class PartitionPruner extends DefaultExpressionRewriter<Void> {
- private final List<OnePartitionEvaluator> partitions;
+ private final List<OnePartitionEvaluator<?>> partitions;
private final Expression partitionPredicate;
/** Different type of table may have different partition prune behavior. */
@@ -58,7 +65,7 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
EXTERNAL
}
- private PartitionPruner(List<OnePartitionEvaluator> partitions, Expression
partitionPredicate) {
+ private PartitionPruner(List<OnePartitionEvaluator<?>> partitions,
Expression partitionPredicate) {
this.partitions = Objects.requireNonNull(partitions, "partitions
cannot be null");
this.partitionPredicate =
Objects.requireNonNull(partitionPredicate.accept(this, null),
"partitionPredicate cannot be null");
@@ -102,22 +109,29 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
}
/** prune */
- public <K> List<K> prune() {
+ public <K extends Comparable<K>> List<K> prune() {
Builder<K> scanPartitionIdents = ImmutableList.builder();
for (OnePartitionEvaluator partition : partitions) {
- if (!canBePrunedOut(partition)) {
+ if (!canBePrunedOut(partitionPredicate, partition)) {
scanPartitionIdents.add((K) partition.getPartitionIdent());
}
}
return scanPartitionIdents.build();
}
+ public static <K extends Comparable<K>> List<K> prune(List<Slot>
partitionSlots, Expression partitionPredicate,
+ Map<K, PartitionItem> idToPartitions, CascadesContext
cascadesContext,
+ PartitionTableType partitionTableType) {
+ return prune(partitionSlots, partitionPredicate, idToPartitions,
+ cascadesContext, partitionTableType, Optional.empty());
+ }
+
/**
* prune partition with `idToPartitions` as parameter.
*/
- public static <K> List<K> prune(List<Slot> partitionSlots, Expression
partitionPredicate,
+ public static <K extends Comparable<K>> List<K> prune(List<Slot>
partitionSlots, Expression partitionPredicate,
Map<K, PartitionItem> idToPartitions, CascadesContext
cascadesContext,
- PartitionTableType partitionTableType) {
+ PartitionTableType partitionTableType,
Optional<SortedPartitionRanges<K>> sortedPartitionRanges) {
partitionPredicate = PartitionPruneExpressionExtractor.extract(
partitionPredicate, ImmutableSet.copyOf(partitionSlots),
cascadesContext);
partitionPredicate =
PredicateRewriteForPartitionPrune.rewrite(partitionPredicate, cascadesContext);
@@ -134,39 +148,124 @@ public class PartitionPruner extends
DefaultExpressionRewriter<Void> {
return ImmutableList.of();
}
- List<OnePartitionEvaluator> evaluators =
Lists.newArrayListWithCapacity(idToPartitions.size());
- for (Entry<K, PartitionItem> kv : idToPartitions.entrySet()) {
- evaluators.add(toPartitionEvaluator(
- kv.getKey(), kv.getValue(), partitionSlots,
cascadesContext, expandThreshold));
+ if (sortedPartitionRanges.isPresent()) {
+ RangeSet<MultiColumnBound> predicateRanges =
partitionPredicate.accept(
+ new PartitionPredicateToRange(partitionSlots), null);
+ if (predicateRanges != null) {
+ return binarySearchFiltering(
+ sortedPartitionRanges.get(), partitionSlots,
partitionPredicate, cascadesContext,
+ expandThreshold, predicateRanges
+ );
+ }
}
- PartitionPruner partitionPruner = new PartitionPruner(evaluators,
partitionPredicate);
- //TODO: we keep default partition because it's too hard to prune it,
we return false in canPrune().
- return partitionPruner.prune();
+
+ return sequentialFiltering(
+ idToPartitions, partitionSlots, partitionPredicate,
cascadesContext, expandThreshold
+ );
}
/**
* convert partition item to partition evaluator
*/
- public static final <K> OnePartitionEvaluator<K> toPartitionEvaluator(K
id, PartitionItem partitionItem,
+ public static <K> OnePartitionEvaluator<K> toPartitionEvaluator(K id,
PartitionItem partitionItem,
List<Slot> partitionSlots, CascadesContext cascadesContext, int
expandThreshold) {
if (partitionItem instanceof ListPartitionItem) {
- return new OneListPartitionEvaluator(
+ return new OneListPartitionEvaluator<>(
id, partitionSlots, (ListPartitionItem) partitionItem,
cascadesContext);
} else if (partitionItem instanceof RangePartitionItem) {
- return new OneRangePartitionEvaluator(
+ return new OneRangePartitionEvaluator<>(
id, partitionSlots, (RangePartitionItem) partitionItem,
cascadesContext, expandThreshold);
} else {
- return new UnknownPartitionEvaluator(id, partitionItem);
+ return new UnknownPartitionEvaluator<>(id, partitionItem);
+ }
+ }
+
+ private static <K extends Comparable<K>> List<K> binarySearchFiltering(
+ SortedPartitionRanges<K> sortedPartitionRanges, List<Slot>
partitionSlots,
+ Expression partitionPredicate, CascadesContext cascadesContext,
int expandThreshold,
+ RangeSet<MultiColumnBound> predicateRanges) {
+ List<PartitionItemAndRange<K>> sortedPartitions =
sortedPartitionRanges.sortedPartitions;
+
+ Set<K> selectedIdSets = Sets.newTreeSet();
+ int leftIndex = 0;
+ for (Range<MultiColumnBound> predicateRange :
predicateRanges.asRanges()) {
+ int rightIndex = sortedPartitions.size();
+ if (leftIndex >= rightIndex) {
+ break;
+ }
+
+ int midIndex;
+ MultiColumnBound predicateUpperBound =
predicateRange.upperEndpoint();
+ MultiColumnBound predicateLowerBound =
predicateRange.lowerEndpoint();
+
+ while (leftIndex + 1 < rightIndex) {
+ midIndex = (leftIndex + rightIndex) / 2;
+ PartitionItemAndRange<K> partition =
sortedPartitions.get(midIndex);
+ Range<MultiColumnBound> partitionSpan = partition.range;
+
+ if
(predicateUpperBound.compareTo(partitionSpan.lowerEndpoint()) < 0) {
+ rightIndex = midIndex;
+ } else if
(predicateLowerBound.compareTo(partitionSpan.upperEndpoint()) > 0) {
+ leftIndex = midIndex;
+ } else {
+ break;
+ }
+ }
+
+ for (; leftIndex < sortedPartitions.size(); leftIndex++) {
+ PartitionItemAndRange<K> partition =
sortedPartitions.get(leftIndex);
+
+ K partitionId = partition.id;
+ // list partition will expand to multiple
PartitionItemAndRange, we should skip evaluate it again
+ if (selectedIdSets.contains(partitionId)) {
+ continue;
+ }
+
+ Range<MultiColumnBound> partitionSpan = partition.range;
+ if
(predicateUpperBound.compareTo(partitionSpan.lowerEndpoint()) < 0) {
+ break;
+ }
+
+ OnePartitionEvaluator<K> partitionEvaluator =
toPartitionEvaluator(
+ partitionId, partition.partitionItem, partitionSlots,
cascadesContext, expandThreshold);
+ if (!canBePrunedOut(partitionPredicate, partitionEvaluator)) {
+ selectedIdSets.add(partitionId);
+ }
+ }
+ }
+
+ for (PartitionItemAndId<K> defaultPartition :
sortedPartitionRanges.defaultPartitions) {
+ K partitionId = defaultPartition.id;
+ OnePartitionEvaluator<K> partitionEvaluator = toPartitionEvaluator(
+ partitionId, defaultPartition.partitionItem,
partitionSlots, cascadesContext, expandThreshold);
+ if (!canBePrunedOut(partitionPredicate, partitionEvaluator)) {
+ selectedIdSets.add(partitionId);
+ }
}
+
+ return Utils.fastToImmutableList(selectedIdSets);
+ }
+
+ private static <K extends Comparable<K>> List<K> sequentialFiltering(
+ Map<K, PartitionItem> idToPartitions, List<Slot> partitionSlots,
+ Expression partitionPredicate, CascadesContext cascadesContext,
int expandThreshold) {
+ List<OnePartitionEvaluator<?>> evaluators =
Lists.newArrayListWithCapacity(idToPartitions.size());
+ for (Entry<K, PartitionItem> kv : idToPartitions.entrySet()) {
+ evaluators.add(toPartitionEvaluator(
+ kv.getKey(), kv.getValue(), partitionSlots,
cascadesContext, expandThreshold));
+ }
+ PartitionPruner partitionPruner = new PartitionPruner(evaluators,
partitionPredicate);
+ //TODO: we keep default partition because it's too hard to prune it,
we return false in canPrune().
+ return partitionPruner.prune();
}
/**
* return true if partition is not qualified. that is, can be pruned out.
*/
- private boolean canBePrunedOut(OnePartitionEvaluator evaluator) {
+ private static <K> boolean canBePrunedOut(Expression partitionPredicate,
OnePartitionEvaluator<K> evaluator) {
List<Map<Slot, PartitionSlotInput>> onePartitionInputs =
evaluator.getOnePartitionInputs();
for (Map<Slot, PartitionSlotInput> currentInputs : onePartitionInputs)
{
- // evaluate wether there's possible for this partition to accept
this predicate
+ // 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;
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SortedPartitionRanges.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SortedPartitionRanges.java
new file mode 100644
index 00000000000..50d4cb3befa
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/SortedPartitionRanges.java
@@ -0,0 +1,72 @@
+// 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.expression.rules;
+
+import org.apache.doris.catalog.PartitionItem;
+import org.apache.doris.nereids.util.Utils;
+
+import com.google.common.collect.Range;
+
+import java.util.List;
+import java.util.Objects;
+
+/** SortedPartitionRanges */
+public class SortedPartitionRanges<K> {
+ public final List<PartitionItemAndRange<K>> sortedPartitions;
+ public final List<PartitionItemAndId<K>> defaultPartitions;
+
+ /** SortedPartitionRanges */
+ public SortedPartitionRanges(
+ List<PartitionItemAndRange<K>> sortedPartitions,
List<PartitionItemAndId<K>> defaultPartitions) {
+ this.sortedPartitions = Utils.fastToImmutableList(
+ Objects.requireNonNull(sortedPartitions, "sortedPartitions
bounds can not be null")
+ );
+ this.defaultPartitions = Utils.fastToImmutableList(
+ Objects.requireNonNull(defaultPartitions, "defaultPartitions
bounds can not be null")
+ );
+ }
+
+ /** PartitionItemAndRange */
+ public static class PartitionItemAndRange<K> {
+ public final K id;
+ public final PartitionItem partitionItem;
+ public final Range<MultiColumnBound> range;
+
+ public PartitionItemAndRange(K id, PartitionItem partitionItem,
Range<MultiColumnBound> range) {
+ this.id = id;
+ this.partitionItem = Objects.requireNonNull(partitionItem,
"partitionItem can not be null");
+ this.range = Objects.requireNonNull(range, "range can not be
null");
+ }
+
+ @Override
+ public String toString() {
+ return range.toString();
+ }
+ }
+
+ /** PartitionItemAndId */
+ public static class PartitionItemAndId<K> {
+ public final K id;
+ public final PartitionItem partitionItem;
+
+ public PartitionItemAndId(K id, PartitionItem partitionItem) {
+ this.id = id;
+ this.partitionItem = Objects.requireNonNull(partitionItem,
"partitionItem can not be null");
+ }
+ }
+}
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 0d505408611..c5868b4b68a 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
@@ -18,13 +18,16 @@
package org.apache.doris.nereids.rules.rewrite;
import org.apache.doris.catalog.Column;
+import org.apache.doris.catalog.Env;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.PartitionInfo;
import org.apache.doris.catalog.PartitionItem;
+import org.apache.doris.common.cache.NereidsSortedPartitionsCacheManager;
import org.apache.doris.nereids.rules.Rule;
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.Slot;
import org.apache.doris.nereids.trees.plans.logical.LogicalEmptyRelation;
import org.apache.doris.nereids.trees.plans.logical.LogicalFilter;
@@ -37,6 +40,7 @@ import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -76,9 +80,16 @@ public class PruneOlapScanPartition extends
OneRewriteRuleFactory {
partitionSlots.add(partitionSlot);
}
}
+ NereidsSortedPartitionsCacheManager sortedPartitionsCacheManager =
Env.getCurrentEnv()
+ .getSortedPartitionsCacheManager();
List<Long> manuallySpecifiedPartitions =
scan.getManuallySpecifiedPartitions();
Map<Long, PartitionItem> idToPartitions;
+ Optional<SortedPartitionRanges<Long>> sortedPartitionRanges =
Optional.empty();
if (manuallySpecifiedPartitions.isEmpty()) {
+ Optional<SortedPartitionRanges<?>> sortedPartitionRangesOpt =
sortedPartitionsCacheManager.get(table);
+ if (sortedPartitionRangesOpt.isPresent()) {
+ sortedPartitionRanges = (Optional)
sortedPartitionRangesOpt;
+ }
idToPartitions = partitionInfo.getIdToItem(false);
} else {
Map<Long, PartitionItem> allPartitions =
partitionInfo.getAllPartitions();
@@ -88,7 +99,7 @@ public class PruneOlapScanPartition extends
OneRewriteRuleFactory {
}
List<Long> prunedPartitions = PartitionPruner.prune(
partitionSlots, filter.getPredicate(), idToPartitions,
ctx.cascadesContext,
- PartitionTableType.OLAP);
+ PartitionTableType.OLAP, sortedPartitionRanges);
if (prunedPartitions.isEmpty()) {
return new LogicalEmptyRelation(
ConnectContext.get().getStatementContext().getNextRelationId(),
diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
index 139abb74597..c7debefcb93 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java
@@ -106,10 +106,10 @@ import org.apache.doris.common.FeConstants;
import org.apache.doris.common.FormatOptions;
import org.apache.doris.common.MetaNotFoundException;
import org.apache.doris.common.NereidsException;
-import org.apache.doris.common.NereidsSqlCacheManager;
import org.apache.doris.common.Status;
import org.apache.doris.common.UserException;
import org.apache.doris.common.Version;
+import org.apache.doris.common.cache.NereidsSqlCacheManager;
import org.apache.doris.common.profile.Profile;
import org.apache.doris.common.profile.ProfileManager.ProfileType;
import org.apache.doris.common.profile.SummaryProfile;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]