You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@doris.apache.org by hu...@apache.org on 2025/10/24 10:58:11 UTC
(doris) 02/02: refactor TDataAccessPath
This is an automated email from the ASF dual-hosted git repository.
huajianlan pushed a commit to branch nested_column_prune
in repository https://gitbox.apache.org/repos/asf/doris.git
commit 7bfc0dac45960eca5f6756bc1363afbf42f8d8b7
Author: 924060929 <la...@selectdb.com>
AuthorDate: Fri Oct 24 18:57:41 2025 +0800
refactor TDataAccessPath
---
.../org/apache/doris/analysis/AccessPathInfo.java | 8 +-
.../org/apache/doris/analysis/SlotDescriptor.java | 26 +--
.../rewrite/AccessPathExpressionCollector.java | 34 +++-
.../nereids/rules/rewrite/NestedColumnPruning.java | 181 ++++++++++++++-------
.../nereids/rules/rewrite/SlotTypeReplacer.java | 46 ++++--
.../nereids/trees/expressions/SlotReference.java | 31 ++--
.../functions/scalar/StructElement.java | 6 +-
.../org/apache/doris/nereids/types/StructType.java | 4 +
.../java/org/apache/doris/planner/ScanNode.java | 50 ++++--
.../rules/rewrite/PruneNestedColumnTest.java | 34 ++--
gensrc/thrift/Descriptors.thrift | 23 ++-
11 files changed, 286 insertions(+), 157 deletions(-)
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/AccessPathInfo.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/AccessPathInfo.java
index 24501a469d7..3df7baada4d 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/AccessPathInfo.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/AccessPathInfo.java
@@ -21,16 +21,18 @@
package org.apache.doris.analysis;
import org.apache.doris.nereids.types.DataType;
-import org.apache.doris.thrift.TColumnAccessPaths;
+import org.apache.doris.thrift.TColumnAccessPath;
import lombok.AllArgsConstructor;
import lombok.Data;
+import java.util.List;
+
/** AccessPathInfo */
@Data
@AllArgsConstructor
public class AccessPathInfo {
private DataType prunedType;
- private TColumnAccessPaths allAccessPaths;
- private TColumnAccessPaths predicateAccessPaths;
+ private List<TColumnAccessPath> allAccessPaths;
+ private List<TColumnAccessPath> predicateAccessPaths;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotDescriptor.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotDescriptor.java
index 4160842d208..4ecec5b30aa 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotDescriptor.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/SlotDescriptor.java
@@ -24,7 +24,7 @@ import org.apache.doris.catalog.Column;
import org.apache.doris.catalog.ColumnStats;
import org.apache.doris.catalog.OlapTable;
import org.apache.doris.catalog.Type;
-import org.apache.doris.thrift.TColumnAccessPaths;
+import org.apache.doris.thrift.TColumnAccessPath;
import org.apache.doris.thrift.TSlotDescriptor;
import com.google.common.base.MoreObjects;
@@ -76,10 +76,10 @@ public class SlotDescriptor {
private boolean needMaterialize = true;
private boolean isAutoInc = false;
private Expr virtualColumn = null;
- private TColumnAccessPaths allAccessPaths;
- private TColumnAccessPaths predicateAccessPaths;
- private TColumnAccessPaths displayAllAccessPaths;
- private TColumnAccessPaths displayPredicateAccessPaths;
+ private List<TColumnAccessPath> allAccessPaths;
+ private List<TColumnAccessPath> predicateAccessPaths;
+ private List<TColumnAccessPath> displayAllAccessPaths;
+ private List<TColumnAccessPath> displayPredicateAccessPaths;
public SlotDescriptor(SlotId id, TupleDescriptor parent) {
@@ -134,35 +134,35 @@ public class SlotDescriptor {
return this.subColPath;
}
- public TColumnAccessPaths getAllAccessPaths() {
+ public List<TColumnAccessPath> getAllAccessPaths() {
return allAccessPaths;
}
- public void setAllAccessPaths(TColumnAccessPaths allAccessPaths) {
+ public void setAllAccessPaths(List<TColumnAccessPath> allAccessPaths) {
this.allAccessPaths = allAccessPaths;
}
- public TColumnAccessPaths getPredicateAccessPaths() {
+ public List<TColumnAccessPath> getPredicateAccessPaths() {
return predicateAccessPaths;
}
- public void setPredicateAccessPaths(TColumnAccessPaths predicateAccessPaths) {
+ public void setPredicateAccessPaths(List<TColumnAccessPath> predicateAccessPaths) {
this.predicateAccessPaths = predicateAccessPaths;
}
- public TColumnAccessPaths getDisplayAllAccessPaths() {
+ public List<TColumnAccessPath> getDisplayAllAccessPaths() {
return displayAllAccessPaths;
}
- public void setDisplayAllAccessPaths(TColumnAccessPaths displayAllAccessPaths) {
+ public void setDisplayAllAccessPaths(List<TColumnAccessPath> displayAllAccessPaths) {
this.displayAllAccessPaths = displayAllAccessPaths;
}
- public TColumnAccessPaths getDisplayPredicateAccessPaths() {
+ public List<TColumnAccessPath> getDisplayPredicateAccessPaths() {
return displayPredicateAccessPaths;
}
- public void setDisplayPredicateAccessPaths(TColumnAccessPaths displayPredicateAccessPaths) {
+ public void setDisplayPredicateAccessPaths(List<TColumnAccessPath> displayPredicateAccessPaths) {
this.displayPredicateAccessPaths = displayPredicateAccessPaths;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AccessPathExpressionCollector.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AccessPathExpressionCollector.java
index 8bd794f5422..57fe9fefcc5 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AccessPathExpressionCollector.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/AccessPathExpressionCollector.java
@@ -51,6 +51,7 @@ import org.apache.doris.nereids.trees.expressions.visitor.DefaultExpressionVisit
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.NestedColumnPrunable;
import org.apache.doris.nereids.util.Utils;
+import org.apache.doris.thrift.TAccessPathType;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
@@ -102,7 +103,7 @@ public class AccessPathExpressionCollector extends DefaultExpressionVisitor<Void
context.accessPathBuilder.addPrefix(slotReference.getName());
ImmutableList<String> path = Utils.fastToImmutableList(context.accessPathBuilder.accessPath);
int slotId = slotReference.getExprId().asInt();
- slotToAccessPaths.put(slotId, new CollectAccessPathResult(path, context.bottomFilter));
+ slotToAccessPaths.put(slotId, new CollectAccessPathResult(path, context.bottomFilter, context.type));
}
return null;
}
@@ -348,6 +349,15 @@ public class AccessPathExpressionCollector extends DefaultExpressionVisitor<Void
return visit(arraySortBy, context);
}
+ // @Override
+ // public Void visitIsNull(IsNull isNull, CollectorContext context) {
+ // if (context.accessPathBuilder.isEmpty()) {
+ // context.setType(TAccessPathType.META);
+ // return continueCollectAccessPath(isNull.child(), context);
+ // }
+ // return visit(isNull, context);
+ // }
+
private Void collectArrayPathInLambda(Lambda lambda, CollectorContext context) {
List<Expression> arguments = lambda.getArguments();
Map<String, Expression> nameToArray = Maps.newLinkedHashMap();
@@ -376,11 +386,21 @@ public class AccessPathExpressionCollector extends DefaultExpressionVisitor<Void
private StatementContext statementContext;
private AccessPathBuilder accessPathBuilder;
private boolean bottomFilter;
+ private TAccessPathType type;
public CollectorContext(StatementContext statementContext, boolean bottomFilter) {
this.statementContext = statementContext;
this.accessPathBuilder = new AccessPathBuilder();
this.bottomFilter = bottomFilter;
+ this.type = TAccessPathType.DATA;
+ }
+
+ public TAccessPathType getType() {
+ return type;
+ }
+
+ public void setType(TAccessPathType type) {
+ this.type = type;
}
}
@@ -405,6 +425,10 @@ public class AccessPathExpressionCollector extends DefaultExpressionVisitor<Void
return accessPath;
}
+ public boolean isEmpty() {
+ return accessPath.isEmpty();
+ }
+
@Override
public String toString() {
return String.join(".", accessPath);
@@ -415,10 +439,16 @@ public class AccessPathExpressionCollector extends DefaultExpressionVisitor<Void
public static class CollectAccessPathResult {
private final List<String> path;
private final boolean isPredicate;
+ private final TAccessPathType type;
- public CollectAccessPathResult(List<String> path, boolean isPredicate) {
+ public CollectAccessPathResult(List<String> path, boolean isPredicate, TAccessPathType type) {
this.path = path;
this.isPredicate = isPredicate;
+ this.type = type;
+ }
+
+ public TAccessPathType getType() {
+ return type;
}
public List<String> getPath() {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
index 609b577c6a2..0f99b576cae 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/NestedColumnPruning.java
@@ -24,6 +24,7 @@ import org.apache.doris.nereids.exceptions.AnalysisException;
import org.apache.doris.nereids.jobs.JobContext;
import org.apache.doris.nereids.rules.rewrite.AccessPathExpressionCollector.CollectAccessPathResult;
import org.apache.doris.nereids.trees.expressions.Slot;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
import org.apache.doris.nereids.trees.plans.Plan;
import org.apache.doris.nereids.trees.plans.visitor.CustomRewriter;
import org.apache.doris.nereids.types.ArrayType;
@@ -34,8 +35,9 @@ import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import org.apache.doris.qe.SessionVariable;
import org.apache.doris.thrift.TAccessPathType;
-import org.apache.doris.thrift.TColumnAccessPaths;
-import org.apache.doris.thrift.TColumnNameAccessPath;
+import org.apache.doris.thrift.TColumnAccessPath;
+import org.apache.doris.thrift.TDataAccessPath;
+import org.apache.doris.thrift.TMetaAccessPath;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Maps;
@@ -97,12 +99,12 @@ public class NestedColumnPruning implements CustomRewriter {
Map<Slot, DataTypeAccessTree> slotIdToAllAccessTree = new LinkedHashMap<>();
Map<Slot, DataTypeAccessTree> slotIdToPredicateAccessTree = new LinkedHashMap<>();
- Comparator<List<String>> pathComparator
- = Comparator.comparing(a -> StringUtils.join(a, "."));
+ Comparator<Pair<TAccessPathType, List<String>>> pathComparator = Comparator.comparing(
+ l -> StringUtils.join(l.second, "."));
- Multimap<Integer, List<String>> allAccessPaths = TreeMultimap.create(
+ Multimap<Integer, Pair<TAccessPathType, List<String>>> allAccessPaths = TreeMultimap.create(
Comparator.naturalOrder(), pathComparator);
- Multimap<Integer, List<String>> predicateAccessPaths = TreeMultimap.create(
+ Multimap<Integer, Pair<TAccessPathType, List<String>>> predicateAccessPaths = TreeMultimap.create(
Comparator.naturalOrder(), pathComparator);
// first: build access data type tree
@@ -111,18 +113,21 @@ public class NestedColumnPruning implements CustomRewriter {
List<CollectAccessPathResult> collectAccessPathResults = kv.getValue();
for (CollectAccessPathResult collectAccessPathResult : collectAccessPathResults) {
List<String> path = collectAccessPathResult.getPath();
+ TAccessPathType pathType = collectAccessPathResult.getType();
DataTypeAccessTree allAccessTree = slotIdToAllAccessTree.computeIfAbsent(
- slot, i -> DataTypeAccessTree.ofRoot(slot)
+ slot, i -> DataTypeAccessTree.ofRoot(slot, pathType)
);
- allAccessTree.setAccessByPath(path, 0);
- allAccessPaths.put(slot.getExprId().asInt(), path);
+ allAccessTree.setAccessByPath(path, 0, pathType);
+ allAccessPaths.put(slot.getExprId().asInt(), Pair.of(pathType, path));
if (collectAccessPathResult.isPredicate()) {
DataTypeAccessTree predicateAccessTree = slotIdToPredicateAccessTree.computeIfAbsent(
- slot, i -> DataTypeAccessTree.ofRoot(slot)
+ slot, i -> DataTypeAccessTree.ofRoot(slot, pathType)
+ );
+ predicateAccessTree.setAccessByPath(path, 0, pathType);
+ predicateAccessPaths.put(
+ slot.getExprId().asInt(), Pair.of(pathType, path)
);
- predicateAccessTree.setAccessByPath(path, 0);
- predicateAccessPaths.put(slot.getExprId().asInt(), path);
}
}
}
@@ -133,51 +138,97 @@ public class NestedColumnPruning implements CustomRewriter {
DataTypeAccessTree accessTree = kv.getValue();
DataType prunedDataType = accessTree.pruneDataType().orElse(slot.getDataType());
- List<TColumnNameAccessPath> allPaths = new ArrayList<>();
- for (List<String> path : allAccessPaths.get(slot.getExprId().asInt())) {
- if (path == null) {
- throw new AnalysisException("This is a bug, please report this");
+ List<TColumnAccessPath> allPaths = new ArrayList<>();
+ boolean accessWholeColumn = false;
+ TAccessPathType accessWholeColumnType = TAccessPathType.META;
+ for (Pair<TAccessPathType, List<String>> pathInfo : allAccessPaths.get(slot.getExprId().asInt())) {
+ if (pathInfo.first == TAccessPathType.DATA) {
+ TDataAccessPath dataAccessPath = new TDataAccessPath();
+ dataAccessPath.setPath(new ArrayList<>(pathInfo.second));
+ TColumnAccessPath accessPath = new TColumnAccessPath(TAccessPathType.DATA);
+ accessPath.setDataAccessPath(dataAccessPath);
+ allPaths.add(accessPath);
+ } else {
+ TMetaAccessPath dataAccessPath = new TMetaAccessPath();
+ dataAccessPath.setPath(new ArrayList<>(pathInfo.second));
+ TColumnAccessPath accessPath = new TColumnAccessPath(TAccessPathType.META);
+ accessPath.setMetaAccessPath(dataAccessPath);
+ allPaths.add(accessPath);
}
- TColumnNameAccessPath tPath = new TColumnNameAccessPath();
- tPath.setPath(path);
// only retain access the whole root
- if (path.size() == 1) {
- allPaths = ImmutableList.of(tPath);
- break;
+ if (pathInfo.second.size() == 1) {
+ accessWholeColumn = true;
+ if (pathInfo.first == TAccessPathType.DATA) {
+ accessWholeColumnType = TAccessPathType.DATA;
+ }
+ } else {
+ accessWholeColumnType = TAccessPathType.DATA;
}
- allPaths.add(tPath);
}
- TColumnAccessPaths allPath = new TColumnAccessPaths();
- allPath.type = TAccessPathType.NAME;
- allPath.setNameAccessPaths(allPaths);
-
- TColumnAccessPaths predicatePath = new TColumnAccessPaths();
- predicatePath.type = TAccessPathType.NAME;
- predicatePath.setNameAccessPaths(new ArrayList<>());
- result.put(slot.getExprId().asInt(), new AccessPathInfo(prunedDataType, allPath, predicatePath));
+ if (accessWholeColumn) {
+ TColumnAccessPath accessPath = new TColumnAccessPath(accessWholeColumnType);
+ SlotReference slotReference = (SlotReference) kv.getKey();
+ String wholeColumnName = slotReference.getOriginalColumn().get().getName();
+ if (accessWholeColumnType == TAccessPathType.DATA) {
+ accessPath.setDataAccessPath(new TDataAccessPath(ImmutableList.of(wholeColumnName)));
+ } else {
+ accessPath.setMetaAccessPath(new TMetaAccessPath(ImmutableList.of(wholeColumnName)));
+ }
+ allPaths = ImmutableList.of(accessPath);
+ }
+ result.put(slot.getExprId().asInt(), new AccessPathInfo(prunedDataType, allPaths, new ArrayList<>()));
}
// third: build predicate access path
for (Entry<Slot, DataTypeAccessTree> kv : slotIdToPredicateAccessTree.entrySet()) {
Slot slot = kv.getKey();
- List<TColumnNameAccessPath> predicatePaths = new ArrayList<>();
- for (List<String> path : predicateAccessPaths.get(slot.getExprId().asInt())) {
- if (path == null) {
+ List<TColumnAccessPath> predicatePaths = new ArrayList<>();
+ boolean accessWholeColumn = false;
+ TAccessPathType accessWholeColumnType = TAccessPathType.META;
+ for (Pair<TAccessPathType, List<String>> pathInfo : predicateAccessPaths.get(slot.getExprId().asInt())) {
+ if (pathInfo == null) {
throw new AnalysisException("This is a bug, please report this");
}
- TColumnNameAccessPath tPath = new TColumnNameAccessPath();
- tPath.setPath(path);
+
+ if (pathInfo.first == TAccessPathType.DATA) {
+ TDataAccessPath dataAccessPath = new TDataAccessPath();
+ dataAccessPath.setPath(new ArrayList<>(pathInfo.second));
+ TColumnAccessPath accessPath = new TColumnAccessPath(TAccessPathType.DATA);
+ accessPath.setDataAccessPath(dataAccessPath);
+ predicatePaths.add(accessPath);
+ } else {
+ TMetaAccessPath dataAccessPath = new TMetaAccessPath();
+ dataAccessPath.setPath(new ArrayList<>(pathInfo.second));
+ TColumnAccessPath accessPath = new TColumnAccessPath(TAccessPathType.META);
+ accessPath.setMetaAccessPath(dataAccessPath);
+ predicatePaths.add(accessPath);
+ }
// only retain access the whole root
- if (path.size() == 1) {
- predicatePaths = ImmutableList.of(tPath);
- break;
+ if (pathInfo.second.size() == 1) {
+ accessWholeColumn = true;
+ if (pathInfo.first == TAccessPathType.DATA) {
+ accessWholeColumnType = TAccessPathType.DATA;
+ }
+ } else {
+ accessWholeColumnType = TAccessPathType.DATA;
+ }
+ }
+
+ if (accessWholeColumn) {
+ TColumnAccessPath accessPath = new TColumnAccessPath(accessWholeColumnType);
+ SlotReference slotReference = (SlotReference) kv.getKey();
+ String wholeColumnName = slotReference.getOriginalColumn().get().getName();
+ if (accessWholeColumnType == TAccessPathType.DATA) {
+ accessPath.setDataAccessPath(new TDataAccessPath(ImmutableList.of(wholeColumnName)));
+ } else {
+ accessPath.setMetaAccessPath(new TMetaAccessPath(ImmutableList.of(wholeColumnName)));
}
- predicatePaths.add(tPath);
+ predicatePaths = ImmutableList.of(accessPath);
}
AccessPathInfo accessPathInfo = result.get(slot.getExprId().asInt());
- accessPathInfo.getPredicateAccessPaths().name_access_paths.addAll(predicatePaths);
+ accessPathInfo.getPredicateAccessPaths().addAll(predicatePaths);
}
return result;
@@ -188,18 +239,20 @@ public class NestedColumnPruning implements CustomRewriter {
private boolean isRoot;
private boolean accessPartialChild;
private boolean accessAll;
+ private TAccessPathType pathType;
private Map<String, DataTypeAccessTree> children = new LinkedHashMap<>();
- public DataTypeAccessTree(DataType type) {
- this(false, type);
+ public DataTypeAccessTree(DataType type, TAccessPathType pathType) {
+ this(false, type, pathType);
}
- public DataTypeAccessTree(boolean isRoot, DataType type) {
+ public DataTypeAccessTree(boolean isRoot, DataType type, TAccessPathType pathType) {
this.isRoot = isRoot;
this.type = type;
+ this.pathType = pathType;
}
- public void setAccessByPath(List<String> path, int accessIndex) {
+ public void setAccessByPath(List<String> path, int accessIndex, TAccessPathType pathType) {
if (accessIndex >= path.size()) {
accessAll = true;
return;
@@ -207,19 +260,23 @@ public class NestedColumnPruning implements CustomRewriter {
accessPartialChild = true;
}
- if (type.isStructType()) {
+ if (pathType == TAccessPathType.DATA) {
+ this.pathType = TAccessPathType.DATA;
+ }
+
+ if (this.type.isStructType()) {
String fieldName = path.get(accessIndex);
DataTypeAccessTree child = children.get(fieldName);
- child.setAccessByPath(path, accessIndex + 1);
+ child.setAccessByPath(path, accessIndex + 1, pathType);
return;
- } else if (type.isArrayType()) {
+ } else if (this.type.isArrayType()) {
DataTypeAccessTree child = children.get("*");
if (path.get(accessIndex).equals("*")) {
// enter this array and skip next *
- child.setAccessByPath(path, accessIndex + 1);
+ child.setAccessByPath(path, accessIndex + 1, pathType);
}
return;
- } else if (type.isMapType()) {
+ } else if (this.type.isMapType()) {
String fieldName = path.get(accessIndex);
if (fieldName.equals("*")) {
// access value by the key, so we should access key and access value, then prune the value's type.
@@ -227,7 +284,7 @@ public class NestedColumnPruning implements CustomRewriter {
DataTypeAccessTree keysChild = children.get("KEYS");
DataTypeAccessTree valuesChild = children.get("VALUES");
keysChild.accessAll = true;
- valuesChild.setAccessByPath(path, accessIndex + 1);
+ valuesChild.setAccessByPath(path, accessIndex + 1, pathType);
return;
} else if (fieldName.equals("KEYS")) {
// only access the keys and not need enter keys, because it must be primitive type.
@@ -240,35 +297,35 @@ public class NestedColumnPruning implements CustomRewriter {
// e.g. map_values(map_columns)[0] will access the array of values first,
// and then access the array, so the access path is ['VALUES', '*']
DataTypeAccessTree valuesChild = children.get("VALUES");
- valuesChild.setAccessByPath(path, accessIndex + 1);
+ valuesChild.setAccessByPath(path, accessIndex + 1, pathType);
return;
}
} else if (isRoot) {
- children.get(path.get(accessIndex)).setAccessByPath(path, accessIndex + 1);
+ children.get(path.get(accessIndex)).setAccessByPath(path, accessIndex + 1, pathType);
return;
}
- throw new AnalysisException("unsupported data type: " + type);
+ throw new AnalysisException("unsupported data type: " + this.type);
}
- public static DataTypeAccessTree ofRoot(Slot slot) {
- DataTypeAccessTree child = of(slot.getDataType());
- DataTypeAccessTree root = new DataTypeAccessTree(true, NullType.INSTANCE);
+ public static DataTypeAccessTree ofRoot(Slot slot, TAccessPathType pathType) {
+ DataTypeAccessTree child = of(slot.getDataType(), pathType);
+ DataTypeAccessTree root = new DataTypeAccessTree(true, NullType.INSTANCE, pathType);
root.children.put(slot.getName(), child);
return root;
}
- public static DataTypeAccessTree of(DataType type) {
- DataTypeAccessTree root = new DataTypeAccessTree(type);
+ public static DataTypeAccessTree of(DataType type, TAccessPathType pathType) {
+ DataTypeAccessTree root = new DataTypeAccessTree(type, pathType);
if (type instanceof StructType) {
StructType structType = (StructType) type;
for (Entry<String, StructField> kv : structType.getNameToFields().entrySet()) {
- root.children.put(kv.getKey(), of(kv.getValue().getDataType()));
+ root.children.put(kv.getKey(), of(kv.getValue().getDataType(), pathType));
}
} else if (type instanceof ArrayType) {
- root.children.put("*", of(((ArrayType) type).getItemType()));
+ root.children.put("*", of(((ArrayType) type).getItemType(), pathType));
} else if (type instanceof MapType) {
- root.children.put("KEYS", of(((MapType) type).getKeyType()));
- root.children.put("VALUES", of(((MapType) type).getValueType()));
+ root.children.put("KEYS", of(((MapType) type).getKeyType(), pathType));
+ root.children.put("VALUES", of(((MapType) type).getValueType(), pathType));
}
return root;
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/SlotTypeReplacer.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/SlotTypeReplacer.java
index ba4a82ac851..043e9c7bdc1 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/SlotTypeReplacer.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/SlotTypeReplacer.java
@@ -61,8 +61,9 @@ import org.apache.doris.nereids.types.MapType;
import org.apache.doris.nereids.types.StructType;
import org.apache.doris.nereids.util.MoreFieldsThread;
import org.apache.doris.thrift.TAccessPathType;
-import org.apache.doris.thrift.TColumnAccessPaths;
-import org.apache.doris.thrift.TColumnNameAccessPath;
+import org.apache.doris.thrift.TColumnAccessPath;
+import org.apache.doris.thrift.TDataAccessPath;
+import org.apache.doris.thrift.TMetaAccessPath;
import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableList;
@@ -380,13 +381,13 @@ public class SlotTypeReplacer extends DefaultPlanRewriter<Void> {
continue;
}
SlotReference slotReference = (SlotReference) slot;
- Optional<TColumnAccessPaths> allAccessPaths = slotReference.getAllAccessPaths();
+ Optional<List<TColumnAccessPath>> allAccessPaths = slotReference.getAllAccessPaths();
if (!allAccessPaths.isPresent() || !slotReference.getOriginalColumn().isPresent()) {
continue;
}
- TColumnAccessPaths allAccessPathsWithId
+ List<TColumnAccessPath> allAccessPathsWithId
= replaceIcebergAccessPathToId(allAccessPaths.get(), slotReference);
- TColumnAccessPaths predicateAccessPathsWithId = replaceIcebergAccessPathToId(
+ List<TColumnAccessPath> predicateAccessPathsWithId = replaceIcebergAccessPathToId(
slotReference.getPredicateAccessPaths().get(), slotReference);
replaceSlots.set(i, ((SlotReference) slot).withAccessPaths(
allAccessPathsWithId,
@@ -573,20 +574,31 @@ public class SlotTypeReplacer extends DefaultPlanRewriter<Void> {
return e.withChildren(newChildren);
}
- private TColumnAccessPaths replaceIcebergAccessPathToId(
- TColumnAccessPaths originAccessPaths, SlotReference slotReference) {
+ private List<TColumnAccessPath> replaceIcebergAccessPathToId(
+ List<TColumnAccessPath> originAccessPaths, SlotReference slotReference) {
Column column = slotReference.getOriginalColumn().get();
- List<TColumnNameAccessPath> replacedAllAccessPaths = new ArrayList<>();
- for (TColumnNameAccessPath nameAccessPath : originAccessPaths.name_access_paths) {
- List<String> icebergColumnAccessPath = new ArrayList<>(nameAccessPath.path);
- replaceIcebergAccessPathToId(
- icebergColumnAccessPath, 0, slotReference.getDataType(), column
- );
- replacedAllAccessPaths.add(new TColumnNameAccessPath(icebergColumnAccessPath));
+ List<TColumnAccessPath> replacedAccessPaths = new ArrayList<>();
+ for (TColumnAccessPath accessPath : originAccessPaths) {
+ List<String> icebergColumnAccessPath = new ArrayList<>();
+ if (accessPath.type == TAccessPathType.DATA) {
+ icebergColumnAccessPath.addAll(accessPath.data_access_path.path);
+ replaceIcebergAccessPathToId(
+ icebergColumnAccessPath, 0, slotReference.getDataType(), column
+ );
+ TColumnAccessPath newAccessPath = new TColumnAccessPath(TAccessPathType.DATA);
+ newAccessPath.data_access_path = new TDataAccessPath(icebergColumnAccessPath);
+ replacedAccessPaths.add(newAccessPath);
+ } else {
+ icebergColumnAccessPath.addAll(accessPath.meta_access_path.path);
+ replaceIcebergAccessPathToId(
+ icebergColumnAccessPath, 0, slotReference.getDataType(), column
+ );
+ TColumnAccessPath newAccessPath = new TColumnAccessPath(TAccessPathType.META);
+ newAccessPath.meta_access_path = new TMetaAccessPath(icebergColumnAccessPath);
+ replacedAccessPaths.add(newAccessPath);
+ }
}
- TColumnAccessPaths accessPathWithId = new TColumnAccessPaths(TAccessPathType.NAME);
- accessPathWithId.name_access_paths = replacedAllAccessPaths;
- return accessPathWithId;
+ return replacedAccessPaths;
}
private void replaceIcebergAccessPathToId(List<String> originPath, int index, DataType type, Column column) {
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
index cb1a45e9170..1c77dc669ac 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/SlotReference.java
@@ -24,7 +24,7 @@ import org.apache.doris.nereids.exceptions.UnboundException;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.util.Utils;
-import org.apache.doris.thrift.TColumnAccessPaths;
+import org.apache.doris.thrift.TColumnAccessPath;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
@@ -57,10 +57,10 @@ public class SlotReference extends Slot {
// that need return original table and name for view not its original table if u query a view
private final TableIf oneLevelTable;
private final Column oneLevelColumn;
- private final Optional<TColumnAccessPaths> allAccessPaths;
- private final Optional<TColumnAccessPaths> predicateAccessPaths;
- private final Optional<TColumnAccessPaths> displayAllAccessPaths;
- private final Optional<TColumnAccessPaths> displayPredicateAccessPaths;
+ private final Optional<List<TColumnAccessPath>> allAccessPaths;
+ private final Optional<List<TColumnAccessPath>> predicateAccessPaths;
+ private final Optional<List<TColumnAccessPath>> displayAllAccessPaths;
+ private final Optional<List<TColumnAccessPath>> displayPredicateAccessPaths;
public SlotReference(String name, DataType dataType) {
this(StatementScopeIdGenerator.newExprId(), name, dataType, true, ImmutableList.of(),
@@ -121,9 +121,9 @@ public class SlotReference extends Slot {
List<String> qualifier, @Nullable TableIf originalTable, @Nullable Column originalColumn,
@Nullable TableIf oneLevelTable, Column oneLevelColumn,
List<String> subPath, Optional<Pair<Integer, Integer>> indexInSql,
- Optional<TColumnAccessPaths> allAccessPaths, Optional<TColumnAccessPaths> predicateAccessPaths,
- Optional<TColumnAccessPaths> displayAllAccessPaths,
- Optional<TColumnAccessPaths> displayPredicateAccessPaths) {
+ Optional<List<TColumnAccessPath>> allAccessPaths, Optional<List<TColumnAccessPath>> predicateAccessPaths,
+ Optional<List<TColumnAccessPath>> displayAllAccessPaths,
+ Optional<List<TColumnAccessPath>> displayPredicateAccessPaths) {
super(indexInSql);
this.exprId = exprId;
this.name = name;
@@ -364,7 +364,8 @@ public class SlotReference extends Slot {
return originalColumn != null ? originalColumn.isAutoInc() : false;
}
- public SlotReference withAccessPaths(TColumnAccessPaths allAccessPaths, TColumnAccessPaths predicateAccessPaths) {
+ public SlotReference withAccessPaths(
+ List<TColumnAccessPath> allAccessPaths, List<TColumnAccessPath> predicateAccessPaths) {
return new SlotReference(exprId, name, dataType, nullable, qualifier,
originalTable, originalColumn, oneLevelTable, oneLevelColumn,
subPath, indexInSqlString, Optional.of(allAccessPaths), Optional.of(predicateAccessPaths),
@@ -372,27 +373,27 @@ public class SlotReference extends Slot {
}
public SlotReference withAccessPaths(
- TColumnAccessPaths allAccessPaths, TColumnAccessPaths predicateAccessPaths,
- TColumnAccessPaths displayAllAccessPaths, TColumnAccessPaths displayPredicateAccessPaths) {
+ List<TColumnAccessPath> allAccessPaths, List<TColumnAccessPath> predicateAccessPaths,
+ List<TColumnAccessPath> displayAllAccessPaths, List<TColumnAccessPath> displayPredicateAccessPaths) {
return new SlotReference(exprId, name, dataType, nullable, qualifier,
originalTable, originalColumn, oneLevelTable, oneLevelColumn,
subPath, indexInSqlString, Optional.of(allAccessPaths), Optional.of(predicateAccessPaths),
Optional.of(displayAllAccessPaths), Optional.of(displayPredicateAccessPaths));
}
- public Optional<TColumnAccessPaths> getAllAccessPaths() {
+ public Optional<List<TColumnAccessPath>> getAllAccessPaths() {
return allAccessPaths;
}
- public Optional<TColumnAccessPaths> getPredicateAccessPaths() {
+ public Optional<List<TColumnAccessPath>> getPredicateAccessPaths() {
return predicateAccessPaths;
}
- public Optional<TColumnAccessPaths> getDisplayAllAccessPaths() {
+ public Optional<List<TColumnAccessPath>> getDisplayAllAccessPaths() {
return displayAllAccessPaths;
}
- public Optional<TColumnAccessPaths> getDisplayPredicateAccessPaths() {
+ public Optional<List<TColumnAccessPath>> getDisplayPredicateAccessPaths() {
return displayPredicateAccessPaths;
}
}
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
index 63169513423..ed29818b688 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/StructElement.java
@@ -30,6 +30,7 @@ import org.apache.doris.nereids.trees.expressions.literal.StringLikeLiteral;
import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
import org.apache.doris.nereids.types.DataType;
import org.apache.doris.nereids.types.NullType;
+import org.apache.doris.nereids.types.StructField;
import org.apache.doris.nereids.types.StructType;
import com.google.common.base.Preconditions;
@@ -99,10 +100,11 @@ public class StructElement extends ScalarFunction
}
} else if (child(1) instanceof StringLikeLiteral) {
String name = ((StringLikeLiteral) child(1)).getStringValue();
- if (!structArgType.getNameToFields().containsKey(name.toLowerCase())) {
+ StructField field = structArgType.getField(name);
+ if (field == null) {
throw new AnalysisException("the specified field name " + name + " was not found: " + this.toSql());
} else {
- retType = structArgType.getNameToFields().get(name).getDataType();
+ retType = field.getDataType();
}
} else {
throw new AnalysisException("struct_element only allows"
diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructType.java
index a1c263976f6..8296ac28281 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructType.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/types/StructType.java
@@ -75,6 +75,10 @@ public class StructType extends DataType implements ComplexDataType, NestedColum
return nameToFields;
}
+ public StructField getField(String name) {
+ return nameToFields.get(name.toLowerCase());
+ }
+
@Override
public DataType conversion() {
return new StructType(fields.stream().map(StructField::conversion).collect(Collectors.toList()));
diff --git a/fe/fe-core/src/main/java/org/apache/doris/planner/ScanNode.java b/fe/fe-core/src/main/java/org/apache/doris/planner/ScanNode.java
index 44715603903..465a11d152c 100644
--- a/fe/fe-core/src/main/java/org/apache/doris/planner/ScanNode.java
+++ b/fe/fe-core/src/main/java/org/apache/doris/planner/ScanNode.java
@@ -53,8 +53,8 @@ import org.apache.doris.qe.ConnectContext;
import org.apache.doris.rpc.RpcException;
import org.apache.doris.statistics.StatisticalType;
import org.apache.doris.system.Backend;
-import org.apache.doris.thrift.TColumnAccessPaths;
-import org.apache.doris.thrift.TColumnNameAccessPath;
+import org.apache.doris.thrift.TAccessPathType;
+import org.apache.doris.thrift.TColumnAccessPath;
import org.apache.doris.thrift.TNetworkAddress;
import org.apache.doris.thrift.TPlanNode;
import org.apache.doris.thrift.TScanRange;
@@ -570,33 +570,45 @@ public abstract class ScanNode extends PlanNode implements SplitGenerator {
}
String displayAllAccessPathsString = null;
if (slot.getDisplayAllAccessPaths() != null
- && slot.getDisplayAllAccessPaths().name_access_paths != null
- && !slot.getDisplayAllAccessPaths().name_access_paths.isEmpty()) {
+ && slot.getDisplayAllAccessPaths() != null
+ && !slot.getDisplayAllAccessPaths().isEmpty()) {
if (this instanceof IcebergScanNode) {
displayAllAccessPathsString = mergeIcebergAccessPathsWithId(
slot.getAllAccessPaths(),
slot.getDisplayAllAccessPaths()
);
} else {
- displayAllAccessPathsString = slot.getDisplayAllAccessPaths().name_access_paths
+ displayAllAccessPathsString = slot.getDisplayAllAccessPaths()
.stream()
- .map(a -> StringUtils.join(a.path, "."))
+ .map(a -> {
+ if (a.type == TAccessPathType.DATA) {
+ return StringUtils.join(a.data_access_path.path, ".");
+ } else {
+ return StringUtils.join(a.meta_access_path.path, ".");
+ }
+ })
.collect(Collectors.joining(", "));
}
}
String displayPredicateAccessPathsString = null;
if (slot.getDisplayPredicateAccessPaths() != null
- && slot.getDisplayPredicateAccessPaths().name_access_paths != null
- && !slot.getDisplayPredicateAccessPaths().name_access_paths.isEmpty()) {
+ && slot.getDisplayPredicateAccessPaths() != null
+ && !slot.getDisplayPredicateAccessPaths().isEmpty()) {
if (this instanceof IcebergScanNode) {
displayPredicateAccessPathsString = mergeIcebergAccessPathsWithId(
slot.getPredicateAccessPaths(),
slot.getDisplayPredicateAccessPaths()
);
} else {
- displayPredicateAccessPathsString = slot.getPredicateAccessPaths().name_access_paths
+ displayPredicateAccessPathsString = slot.getPredicateAccessPaths()
.stream()
- .map(a -> StringUtils.join(a.path, "."))
+ .map(a -> {
+ if (a.type == TAccessPathType.DATA) {
+ return StringUtils.join(a.data_access_path.path, ".");
+ } else {
+ return StringUtils.join(a.meta_access_path.path, ".");
+ }
+ })
.collect(Collectors.joining(", "));
}
}
@@ -629,16 +641,20 @@ public abstract class ScanNode extends PlanNode implements SplitGenerator {
}
private String mergeIcebergAccessPathsWithId(
- TColumnAccessPaths accessPaths, TColumnAccessPaths displayAccessPaths) {
+ List<TColumnAccessPath> accessPaths, List<TColumnAccessPath> displayAccessPaths) {
List<String> mergeDisplayAccessPaths = Lists.newArrayList();
- for (int i = 0; i < displayAccessPaths.name_access_paths.size(); i++) {
- TColumnNameAccessPath nameAccessPath = displayAccessPaths.name_access_paths.get(i);
- TColumnNameAccessPath idAccessPath = accessPaths.name_access_paths.get(i);
+ for (int i = 0; i < displayAccessPaths.size(); i++) {
+ TColumnAccessPath displayAccessPath = displayAccessPaths.get(i);
+ TColumnAccessPath idAccessPath = accessPaths.get(i);
+ List<String> nameAccessPathStrings = displayAccessPath.type == TAccessPathType.DATA
+ ? displayAccessPath.data_access_path.path : displayAccessPath.meta_access_path.path;
+ List<String> idAccessPathStrings = idAccessPath.type == TAccessPathType.DATA
+ ? idAccessPath.data_access_path.path : idAccessPath.meta_access_path.path;
List<String> mergedPath = new ArrayList<>();
- for (int j = 0; j < idAccessPath.path.size(); j++) {
- String name = nameAccessPath.path.get(j);
- String id = idAccessPath.path.get(j);
+ for (int j = 0; j < idAccessPathStrings.size(); j++) {
+ String name = nameAccessPathStrings.get(j);
+ String id = idAccessPathStrings.get(j);
if (name.equals(id)) {
mergedPath.add(name);
} else {
diff --git a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
index ec8f26e1a43..40980803931 100644
--- a/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
+++ b/fe/fe-core/src/test/java/org/apache/doris/nereids/rules/rewrite/PruneNestedColumnTest.java
@@ -39,7 +39,9 @@ import org.apache.doris.nereids.util.PlanChecker;
import org.apache.doris.planner.OlapScanNode;
import org.apache.doris.planner.PlanFragment;
import org.apache.doris.thrift.TAccessPathType;
-import org.apache.doris.thrift.TColumnNameAccessPath;
+import org.apache.doris.thrift.TColumnAccessPath;
+import org.apache.doris.thrift.TDataAccessPath;
+import org.apache.doris.thrift.TMetaAccessPath;
import org.apache.doris.utframe.TestWithFeService;
import com.google.common.collect.ImmutableList;
@@ -413,8 +415,8 @@ public class PruneNestedColumnTest extends TestWithFeService implements MemoPatt
}
private void assertColumn(String sql, String expectType,
- List<TColumnNameAccessPath> expectAllAccessPaths,
- List<TColumnNameAccessPath> expectPredicateAccessPaths) throws Exception {
+ List<TColumnAccessPath> expectAllAccessPaths,
+ List<TColumnAccessPath> expectPredicateAccessPaths) throws Exception {
Pair<PhysicalPlan, List<SlotDescriptor>> result = collectComplexSlots(sql);
PhysicalPlan physicalPlan = result.first;
List<SlotDescriptor> slotDescriptors = result.second;
@@ -426,16 +428,14 @@ public class PruneNestedColumnTest extends TestWithFeService implements MemoPatt
Assertions.assertEquals(1, slotDescriptors.size());
Assertions.assertEquals(expectType, slotDescriptors.get(0).getType().toString());
- Assertions.assertEquals(TAccessPathType.NAME, slotDescriptors.get(0).getAllAccessPaths().type);
- TreeSet<TColumnNameAccessPath> expectAllAccessPathSet = new TreeSet<>(expectAllAccessPaths);
- TreeSet<TColumnNameAccessPath> actualAllAccessPaths
- = new TreeSet<>(slotDescriptors.get(0).getAllAccessPaths().name_access_paths);
+ TreeSet<TColumnAccessPath> expectAllAccessPathSet = new TreeSet<>(expectAllAccessPaths);
+ TreeSet<TColumnAccessPath> actualAllAccessPaths
+ = new TreeSet<>(slotDescriptors.get(0).getAllAccessPaths());
Assertions.assertEquals(expectAllAccessPathSet, actualAllAccessPaths);
- Assertions.assertEquals(TAccessPathType.NAME, slotDescriptors.get(0).getPredicateAccessPaths().type);
- TreeSet<TColumnNameAccessPath> expectPredicateAccessPathSet = new TreeSet<>(expectPredicateAccessPaths);
- TreeSet<TColumnNameAccessPath> actualPredicateAccessPaths
- = new TreeSet<>(slotDescriptors.get(0).getPredicateAccessPaths().name_access_paths);
+ TreeSet<TColumnAccessPath> expectPredicateAccessPathSet = new TreeSet<>(expectPredicateAccessPaths);
+ TreeSet<TColumnAccessPath> actualPredicateAccessPaths
+ = new TreeSet<>(slotDescriptors.get(0).getPredicateAccessPaths());
Assertions.assertEquals(expectPredicateAccessPathSet, actualPredicateAccessPaths);
Map<Integer, DataType> slotIdToDataTypes = new LinkedHashMap<>();
@@ -503,7 +503,15 @@ public class PruneNestedColumnTest extends TestWithFeService implements MemoPatt
return Pair.of(physicalPlan, complexSlots);
}
- private TColumnNameAccessPath path(String... path) {
- return new TColumnNameAccessPath(ImmutableList.copyOf(path));
+ private TColumnAccessPath path(String... path) {
+ TColumnAccessPath accessPath = new TColumnAccessPath(TAccessPathType.DATA);
+ accessPath.data_access_path = new TDataAccessPath(ImmutableList.copyOf(path));
+ return accessPath;
+ }
+
+ private TColumnAccessPath metaPath(String... path) {
+ TColumnAccessPath accessPath = new TColumnAccessPath(TAccessPathType.META);
+ accessPath.meta_access_path = new TMetaAccessPath(ImmutableList.copyOf(path));
+ return accessPath;
}
}
diff --git a/gensrc/thrift/Descriptors.thrift b/gensrc/thrift/Descriptors.thrift
index 4eee5d9de50..9012b7074c2 100644
--- a/gensrc/thrift/Descriptors.thrift
+++ b/gensrc/thrift/Descriptors.thrift
@@ -28,11 +28,11 @@ enum TPatternType {
}
enum TAccessPathType {
- NAME = 1,
- // ICEBERG = 2 // implement in the future
+ DATA = 1,
+ META = 2 // use to prune `where s.data is not null` by only scan the meta of s.data
}
-struct TColumnNameAccessPath {
+struct TDataAccessPath {
// the specification of special path:
// <empty>: access the whole complex column
// *:
@@ -60,17 +60,14 @@ struct TColumnNameAccessPath {
1: required list<string> path
}
-/*
-// implement in the future
-struct TIcebergColumnAccessPath {
- 1: required list<i64> path
+struct TMetaAccessPath {
+ 1: required list<string> path
}
-*/
-struct TColumnAccessPaths {
+struct TColumnAccessPath {
1: required TAccessPathType type
- 2: optional list<TColumnNameAccessPath> name_access_paths
- // 3: optional list<TIcebergColumnAccessPath> iceberg_column_access_paths // implement in the future
+ 2: optional TDataAccessPath data_access_path
+ 3: optional TMetaAccessPath meta_access_path
}
struct TColumn {
@@ -123,8 +120,8 @@ struct TSlotDescriptor {
16: optional string col_default_value
17: optional Types.TPrimitiveType primitive_type = Types.TPrimitiveType.INVALID_TYPE
18: optional Exprs.TExpr virtual_column_expr
- 19: optional TColumnAccessPaths all_access_paths
- 20: optional TColumnAccessPaths predicate_access_paths
+ 19: optional list<TColumnAccessPath> all_access_paths
+ 20: optional list<TColumnAccessPath> predicate_access_paths
}
struct TTupleDescriptor {
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@doris.apache.org
For additional commands, e-mail: commits-help@doris.apache.org