This is an automated email from the ASF dual-hosted git repository.
englefly pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new d0654cd2930 [Feat](Nereids) add use mv hint (#40167)
d0654cd2930 is described below
commit d0654cd2930859877fcbafce0b7894e2f02ce335
Author: LiBinfeng <[email protected]>
AuthorDate: Tue Sep 3 21:54:26 2024 +0800
[Feat](Nereids) add use mv hint (#40167)
support hint like:
/*+ no_use_mv(tablename mvname) / which forbid tablename(indexname) to
be choose
or /+ use_mv(tablename mvname) */ which choose specific materialize view
---
.../java/org/apache/doris/catalog/OlapTable.java | 95 ++++++++++++++
.../org/apache/doris/nereids/hint/UseMvHint.java | 144 +++++++++++++++++++++
.../doris/nereids/parser/LogicalPlanBuilder.java | 35 ++++-
.../processor/pre/PullUpSubqueryAliasToCTE.java | 2 +-
.../doris/nereids/properties/SelectHintUseMv.java | 53 ++++++++
.../rules/analysis/EliminateLogicalSelectHint.java | 44 +++++--
.../mv/AbstractSelectMaterializedIndexRule.java | 2 +-
.../trees/plans/logical/LogicalSelectHint.java | 30 +++--
.../joinorder/joinhint/DistributeHintTest.java | 9 +-
.../suites/nereids_p0/hint/test_use_mv.groovy | 111 ++++++++++++++++
10 files changed, 490 insertions(+), 35 deletions(-)
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 01f0bb900ea..533c24daa0e 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
@@ -54,6 +54,8 @@ import org.apache.doris.mtmv.MTMVRefreshContext;
import org.apache.doris.mtmv.MTMVRelatedTableIf;
import org.apache.doris.mtmv.MTMVSnapshotIf;
import org.apache.doris.mtmv.MTMVVersionSnapshot;
+import org.apache.doris.nereids.hint.Hint;
+import org.apache.doris.nereids.hint.UseMvHint;
import org.apache.doris.persist.gson.GsonPostProcessable;
import org.apache.doris.persist.gson.GsonUtils;
import org.apache.doris.qe.ConnectContext;
@@ -565,6 +567,99 @@ public class OlapTable extends Table implements
MTMVRelatedTableIf, GsonPostProc
return visibleMVs;
}
+ public Long getBestMvIdWithHint(List<Long> orderedMvs) {
+ Optional<UseMvHint> useMvHint = getUseMvHint("USE_MV");
+ Optional<UseMvHint> noUseMvHint = getUseMvHint("NO_USE_MV");
+ if (useMvHint.isPresent() && noUseMvHint.isPresent()) {
+ if
(noUseMvHint.get().getNoUseMVName(this.name).contains(useMvHint.get().getUseMvName(this.name)))
{
+ String errorMsg = "conflict mv exist in use_mv and no_use_mv
in the same time"
+ + useMvHint.get().getUseMvName(this.name);
+ useMvHint.get().setStatus(Hint.HintStatus.SYNTAX_ERROR);
+ useMvHint.get().setErrorMessage(errorMsg);
+ noUseMvHint.get().setStatus(Hint.HintStatus.SYNTAX_ERROR);
+ noUseMvHint.get().setErrorMessage(errorMsg);
+ }
+ return getMvIdWithUseMvHint(useMvHint.get(), orderedMvs);
+ } else if (useMvHint.isPresent()) {
+ return getMvIdWithUseMvHint(useMvHint.get(), orderedMvs);
+ } else if (noUseMvHint.isPresent()) {
+ return getMvIdWithNoUseMvHint(noUseMvHint.get(), orderedMvs);
+ }
+ return orderedMvs.get(0);
+ }
+
+ private Long getMvIdWithUseMvHint(UseMvHint useMvHint, List<Long>
orderedMvs) {
+ if (useMvHint.isAllMv()) {
+ useMvHint.setStatus(Hint.HintStatus.SYNTAX_ERROR);
+ useMvHint.setErrorMessage("use_mv hint should only have one mv in
one table: "
+ + this.name);
+ return orderedMvs.get(0);
+ } else {
+ String mvName = useMvHint.getUseMvName(this.name);
+ if (mvName != null) {
+ if (mvName.equals("`*`")) {
+ useMvHint.setStatus(Hint.HintStatus.SYNTAX_ERROR);
+ useMvHint.setErrorMessage("use_mv hint should only have
one mv in one table: "
+ + this.name);
+ return orderedMvs.get(0);
+ }
+ Long choosedIndexId = indexNameToId.get(mvName);
+ if (orderedMvs.contains(choosedIndexId)) {
+ useMvHint.setStatus(Hint.HintStatus.SUCCESS);
+ return choosedIndexId;
+ } else {
+ useMvHint.setStatus(Hint.HintStatus.SYNTAX_ERROR);
+ useMvHint.setErrorMessage("do not have mv: " + mvName + "
in table: " + this.name);
+ }
+ }
+ }
+ return orderedMvs.get(0);
+ }
+
+ private Long getMvIdWithNoUseMvHint(UseMvHint noUseMvHint, List<Long>
orderedMvs) {
+ if (noUseMvHint.isAllMv()) {
+ noUseMvHint.setStatus(Hint.HintStatus.SUCCESS);
+ return getBaseIndex().getId();
+ } else {
+ List<String> mvNames = noUseMvHint.getNoUseMVName(this.name);
+ Set<Long> forbiddenIndexIds = Sets.newHashSet();
+ for (int i = 0; i < mvNames.size(); i++) {
+ if (mvNames.get(i).equals("`*`")) {
+ noUseMvHint.setStatus(Hint.HintStatus.SUCCESS);
+ return getBaseIndex().getId();
+ }
+ if (hasMaterializedIndex(mvNames.get(i))) {
+ Long forbiddenIndexId = indexNameToId.get(mvNames.get(i));
+ forbiddenIndexIds.add(forbiddenIndexId);
+ } else {
+ noUseMvHint.setStatus(Hint.HintStatus.SYNTAX_ERROR);
+ noUseMvHint.setErrorMessage("do not have mv: " +
mvNames.get(i) + " in table: " + this.name);
+ break;
+ }
+ }
+ for (int i = 0; i < orderedMvs.size(); i++) {
+ if (forbiddenIndexIds.contains(orderedMvs.get(i))) {
+ noUseMvHint.setStatus(Hint.HintStatus.SUCCESS);
+ } else {
+ return orderedMvs.get(i);
+ }
+ }
+ }
+ return orderedMvs.get(0);
+ }
+
+ private Optional<UseMvHint> getUseMvHint(String useMvName) {
+ for (Hint hint :
ConnectContext.get().getStatementContext().getHints()) {
+ if (hint.isSyntaxError()) {
+ continue;
+ }
+ if (hint.getHintName().equalsIgnoreCase(useMvName)) {
+ return Optional.of((UseMvHint) hint);
+ }
+ }
+ return Optional.empty();
+ }
+
public List<MaterializedIndex> getVisibleIndex() {
Optional<Partition> partition =
idToPartition.values().stream().findFirst();
if (!partition.isPresent()) {
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/hint/UseMvHint.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/hint/UseMvHint.java
new file mode 100644
index 00000000000..5e37bdc2760
--- /dev/null
+++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/hint/UseMvHint.java
@@ -0,0 +1,144 @@
+// 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.hint;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * rule hint.
+ */
+public class UseMvHint extends Hint {
+
+ private final boolean isUseMv;
+
+ private final boolean isAllMv;
+
+ private final List<String> parameters;
+
+ private final Map<String, String> useMvTableColumnMap;
+
+ private final Map<String, List<String>> noUseMvTableColumnMap;
+
+ /**
+ * constructor of use mv hint
+ * @param hintName use mv
+ * @param parameters original parameters
+ * @param isUseMv use_mv hint or no_use_mv hint
+ * @param isAllMv should all mv be controlled
+ */
+ public UseMvHint(String hintName, List<String> parameters, boolean
isUseMv, boolean isAllMv) {
+ super(hintName);
+ this.isUseMv = isUseMv;
+ this.isAllMv = isAllMv;
+ this.parameters = parameters;
+ this.useMvTableColumnMap = initUseMvTableColumnMap(parameters);
+ this.noUseMvTableColumnMap = initNoUseMvTableColumnMap(parameters);
+ }
+
+ private Map<String, String> initUseMvTableColumnMap(List<String>
parameters) {
+ Map<String, String> tempUseMvTableColumnMap = new HashMap<>();
+ if (!isUseMv) {
+ return tempUseMvTableColumnMap;
+ }
+ if (parameters.size() % 2 == 1) {
+ this.setStatus(HintStatus.SYNTAX_ERROR);
+ this.setErrorMessage("parameter of use_mv hint must be in pairs");
+ return tempUseMvTableColumnMap;
+ }
+ for (int i = 0; i < parameters.size(); i += 2) {
+ String tableName = parameters.get(i);
+ String columnName = parameters.get(i + 1);
+ if (tempUseMvTableColumnMap.containsKey(tableName)) {
+ this.setStatus(HintStatus.SYNTAX_ERROR);
+ this.setErrorMessage("use_mv hint should only have one mv in
one table: "
+ + tableName + "." + columnName);
+ break;
+ }
+ tempUseMvTableColumnMap.put(tableName, columnName);
+ }
+ return tempUseMvTableColumnMap;
+ }
+
+ private Map<String, List<String>> initNoUseMvTableColumnMap(List<String>
parameters) {
+ Map<String, List<String>> tempNoUseMvTableColumnMap = new HashMap<>();
+ if (isUseMv) {
+ return tempNoUseMvTableColumnMap;
+ }
+ if (parameters.size() % 2 == 1) {
+ this.setStatus(HintStatus.SYNTAX_ERROR);
+ this.setErrorMessage("parameter of no_use_mv hint must be in
pairs");
+ return tempNoUseMvTableColumnMap;
+ }
+ for (int i = 0; i < parameters.size(); i += 2) {
+ String tableName = parameters.get(i);
+ String columnName = parameters.get(i + 1);
+ if (tempNoUseMvTableColumnMap.containsKey(tableName)) {
+ tempNoUseMvTableColumnMap.get(tableName).add(columnName);
+ } else {
+ List<String> list = new ArrayList<>();
+ list.add(columnName);
+ tempNoUseMvTableColumnMap.put(tableName, list);
+ }
+ }
+ return tempNoUseMvTableColumnMap;
+ }
+
+ public boolean isUseMv() {
+ return isUseMv;
+ }
+
+ public boolean isAllMv() {
+ return isAllMv;
+ }
+
+ public String getUseMvName(String tableName) {
+ return useMvTableColumnMap.get(tableName);
+ }
+
+ public List<String> getNoUseMVName(String tableName) {
+ return noUseMvTableColumnMap.get(tableName);
+ }
+
+ @Override
+ public String getExplainString() {
+ StringBuilder out = new StringBuilder();
+ if (isUseMv) {
+ out.append("use_mv");
+ } else {
+ out.append("no_use_mv");
+ }
+ if (!parameters.isEmpty()) {
+ out.append("(");
+ for (int i = 0; i < parameters.size(); i++) {
+ if (i % 2 == 0) {
+ out.append(parameters.get(i));
+ } else {
+ out.append(".");
+ out.append(parameters.get(i));
+ out.append(" ");
+ }
+ }
+ out.append(")");
+ }
+
+ return out.toString();
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
index 7369b714f74..324ab808226 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/parser/LogicalPlanBuilder.java
@@ -232,6 +232,7 @@ import
org.apache.doris.nereids.properties.SelectHintLeading;
import org.apache.doris.nereids.properties.SelectHintOrdered;
import org.apache.doris.nereids.properties.SelectHintSetVar;
import org.apache.doris.nereids.properties.SelectHintUseCboRule;
+import org.apache.doris.nereids.properties.SelectHintUseMv;
import org.apache.doris.nereids.trees.TableSample;
import org.apache.doris.nereids.trees.expressions.Add;
import org.apache.doris.nereids.trees.expressions.And;
@@ -3165,7 +3166,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
if (hintContexts.isEmpty()) {
return logicalPlan;
}
- Map<String, SelectHint> hints = Maps.newLinkedHashMap();
+ ImmutableList.Builder<SelectHint> hints = ImmutableList.builder();
for (ParserRuleContext hintContext : hintContexts) {
SelectHintContext selectHintContext = (SelectHintContext)
hintContext;
for (HintStatementContext hintStatement :
selectHintContext.hintStatements) {
@@ -3187,7 +3188,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
parameters.put(parameterName, value);
}
}
- hints.put(hintName, new SelectHintSetVar(hintName,
parameters));
+ hints.add(new SelectHintSetVar(hintName, parameters));
break;
case "leading":
List<String> leadingParameters = new ArrayList<>();
@@ -3197,10 +3198,10 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
leadingParameters.add(parameterName);
}
}
- hints.put(hintName, new SelectHintLeading(hintName,
leadingParameters));
+ hints.add(new SelectHintLeading(hintName,
leadingParameters));
break;
case "ordered":
- hints.put(hintName, new SelectHintOrdered(hintName));
+ hints.add(new SelectHintOrdered(hintName));
break;
case "use_cbo_rule":
List<String> useRuleParameters = new ArrayList<>();
@@ -3210,7 +3211,7 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
useRuleParameters.add(parameterName);
}
}
- hints.put(hintName, new SelectHintUseCboRule(hintName,
useRuleParameters, false));
+ hints.add(new SelectHintUseCboRule(hintName,
useRuleParameters, false));
break;
case "no_use_cbo_rule":
List<String> noUseRuleParameters = new ArrayList<>();
@@ -3220,14 +3221,34 @@ public class LogicalPlanBuilder extends
DorisParserBaseVisitor<Object> {
noUseRuleParameters.add(parameterName);
}
}
- hints.put(hintName, new SelectHintUseCboRule(hintName,
noUseRuleParameters, true));
+ hints.add(new SelectHintUseCboRule(hintName,
noUseRuleParameters, true));
+ break;
+ case "use_mv":
+ List<String> useIndexParameters = new
ArrayList<String>();
+ for (HintAssignmentContext kv :
hintStatement.parameters) {
+ String parameterName =
visitIdentifierOrText(kv.key);
+ if (kv.key != null) {
+ useIndexParameters.add(parameterName);
+ }
+ }
+ hints.add(new SelectHintUseMv(hintName,
useIndexParameters, true));
+ break;
+ case "no_use_mv":
+ List<String> noUseIndexParameters = new
ArrayList<String>();
+ for (HintAssignmentContext kv :
hintStatement.parameters) {
+ String parameterName =
visitIdentifierOrText(kv.key);
+ if (kv.key != null) {
+ noUseIndexParameters.add(parameterName);
+ }
+ }
+ hints.add(new SelectHintUseMv(hintName,
noUseIndexParameters, false));
break;
default:
break;
}
}
}
- return new LogicalSelectHint<>(hints, logicalPlan);
+ return new LogicalSelectHint<>(hints.build(), logicalPlan);
}
@Override
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/pre/PullUpSubqueryAliasToCTE.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/pre/PullUpSubqueryAliasToCTE.java
index 8e8889f5e62..31a205d5ed5 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/pre/PullUpSubqueryAliasToCTE.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/processor/pre/PullUpSubqueryAliasToCTE.java
@@ -59,7 +59,7 @@ public class PullUpSubqueryAliasToCTE extends
PlanPreprocessor {
public Plan visitLogicalSubQueryAlias(LogicalSubQueryAlias<? extends Plan>
alias,
StatementContext context) {
if (alias.child() instanceof LogicalSelectHint
- && ((LogicalSelectHint) alias.child()).isIncludeLeading()) {
+ && ((LogicalSelectHint)
alias.child()).isIncludeHint("Leading")) {
aliasQueries.add((LogicalSubQueryAlias<Plan>) alias);
List<String> tableName = new ArrayList<>();
tableName.add(alias.getAlias());
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/SelectHintUseMv.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/SelectHintUseMv.java
new file mode 100644
index 00000000000..35ce25fb4f4
--- /dev/null
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/properties/SelectHintUseMv.java
@@ -0,0 +1,53 @@
+// 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.properties;
+
+import java.util.List;
+
+/**
+ * select hint UseMv.
+ */
+public class SelectHintUseMv extends SelectHint {
+ private final List<String> parameters;
+
+ private final boolean isUseMv;
+
+ public SelectHintUseMv(String hintName, List<String> parameters, boolean
isUseMv) {
+ super(hintName);
+ this.parameters = parameters;
+ this.isUseMv = isUseMv;
+ }
+
+ public List<String> getParameters() {
+ return parameters;
+ }
+
+ public boolean isUseMv() {
+ return isUseMv;
+ }
+
+ @Override
+ public String getHintName() {
+ return super.getHintName();
+ }
+
+ @Override
+ public String toString() {
+ return super.getHintName();
+ }
+}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHint.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHint.java
index ea2c9994606..ebff9f838a4 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHint.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/EliminateLogicalSelectHint.java
@@ -27,10 +27,12 @@ import org.apache.doris.nereids.hint.Hint;
import org.apache.doris.nereids.hint.LeadingHint;
import org.apache.doris.nereids.hint.OrderedHint;
import org.apache.doris.nereids.hint.UseCboRuleHint;
+import org.apache.doris.nereids.hint.UseMvHint;
import org.apache.doris.nereids.properties.SelectHint;
import org.apache.doris.nereids.properties.SelectHintLeading;
import org.apache.doris.nereids.properties.SelectHintSetVar;
import org.apache.doris.nereids.properties.SelectHintUseCboRule;
+import org.apache.doris.nereids.properties.SelectHintUseMv;
import org.apache.doris.nereids.rules.Rule;
import org.apache.doris.nereids.rules.RuleType;
import org.apache.doris.nereids.rules.rewrite.OneRewriteRuleFactory;
@@ -43,7 +45,6 @@ import org.apache.doris.qe.VariableMgr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
@@ -57,10 +58,10 @@ public class EliminateLogicalSelectHint extends
OneRewriteRuleFactory {
public Rule build() {
return logicalSelectHint().thenApply(ctx -> {
LogicalSelectHint<Plan> selectHintPlan = ctx.root;
- for (Entry<String, SelectHint> hint :
selectHintPlan.getHints().entrySet()) {
- String hintName = hint.getKey();
+ for (SelectHint hint : selectHintPlan.getHints()) {
+ String hintName = hint.getHintName();
if (hintName.equalsIgnoreCase("SET_VAR")) {
- setVar((SelectHintSetVar) hint.getValue(),
ctx.statementContext);
+ setVar((SelectHintSetVar) hint, ctx.statementContext);
} else if (hintName.equalsIgnoreCase("ORDERED")) {
try {
ctx.cascadesContext.getConnectContext().getSessionVariable()
@@ -73,12 +74,16 @@ public class EliminateLogicalSelectHint extends
OneRewriteRuleFactory {
ctx.cascadesContext.getHintMap().put("Ordered", ordered);
ctx.statementContext.addHint(ordered);
} else if (hintName.equalsIgnoreCase("LEADING")) {
- extractLeading((SelectHintLeading) hint.getValue(),
ctx.cascadesContext,
- ctx.statementContext, selectHintPlan.getHints());
+ extractLeading((SelectHintLeading) hint,
ctx.cascadesContext,
+ ctx.statementContext, selectHintPlan);
} else if (hintName.equalsIgnoreCase("USE_CBO_RULE")) {
- extractRule((SelectHintUseCboRule) hint.getValue(),
ctx.statementContext);
+ extractRule((SelectHintUseCboRule) hint,
ctx.statementContext);
+ } else if (hintName.equalsIgnoreCase("USE_MV")) {
+ extractMv((SelectHintUseMv) hint,
ConnectContext.get().getStatementContext());
+ } else if (hintName.equalsIgnoreCase("NO_USE_MV")) {
+ extractMv((SelectHintUseMv) hint,
ConnectContext.get().getStatementContext());
} else {
- logger.warn("Can not process select hint '{}' and skip
it", hint.getKey());
+ logger.warn("Can not process select hint '{}' and skip
it", hint.getHintName());
}
}
return selectHintPlan.child();
@@ -116,7 +121,7 @@ public class EliminateLogicalSelectHint extends
OneRewriteRuleFactory {
}
private void extractLeading(SelectHintLeading selectHint, CascadesContext
context,
- StatementContext statementContext,
Map<String, SelectHint> hints) {
+ StatementContext statementContext,
LogicalSelectHint<Plan> selectHintPlan) {
LeadingHint hint = new LeadingHint("Leading",
selectHint.getParameters(), selectHint.toString());
if (context.getHintMap().get("Leading") != null) {
hint.setStatus(Hint.HintStatus.SYNTAX_ERROR);
@@ -139,7 +144,8 @@ public class EliminateLogicalSelectHint extends
OneRewriteRuleFactory {
if (!hint.isSyntaxError()) {
hint.setStatus(Hint.HintStatus.SUCCESS);
}
- if (hints.get("ordered") != null ||
ConnectContext.get().getSessionVariable().isDisableJoinReorder()
+ if (selectHintPlan.isIncludeHint("Ordered")
+ ||
ConnectContext.get().getSessionVariable().isDisableJoinReorder()
|| context.isLeadingDisableJoinReorder()) {
context.setLeadingJoin(false);
hint.setStatus(Hint.HintStatus.UNUSED);
@@ -158,4 +164,22 @@ public class EliminateLogicalSelectHint extends
OneRewriteRuleFactory {
}
}
+ private void extractMv(SelectHintUseMv selectHint, StatementContext
statementContext) {
+ boolean isAllMv = selectHint.getParameters().isEmpty();
+ UseMvHint useMvHint = new UseMvHint(selectHint.getHintName(),
selectHint.getParameters(),
+ selectHint.isUseMv(), isAllMv);
+ for (Hint hint : statementContext.getHints()) {
+ if (hint.getHintName().equals(selectHint.getHintName())) {
+ hint.setStatus(Hint.HintStatus.SYNTAX_ERROR);
+ hint.setErrorMessage("only one " + selectHint.getHintName() +
" hint is allowed");
+ useMvHint.setStatus(Hint.HintStatus.SYNTAX_ERROR);
+ useMvHint.setErrorMessage("only one " +
selectHint.getHintName() + " hint is allowed");
+ }
+ }
+ if (!useMvHint.isSyntaxError()) {
+
ConnectContext.get().getSessionVariable().setEnableSyncMvCostBasedRewrite(false);
+ }
+ statementContext.addHint(useMvHint);
+ }
+
}
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/mv/AbstractSelectMaterializedIndexRule.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/mv/AbstractSelectMaterializedIndexRule.java
index 1124c141416..f17ab1c96bd 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/mv/AbstractSelectMaterializedIndexRule.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/mv/AbstractSelectMaterializedIndexRule.java
@@ -276,7 +276,7 @@ public abstract class AbstractSelectMaterializedIndexRule {
.thenComparing(rid -> (Long) rid))
.collect(Collectors.toList());
- return sortedIndexIds.get(0);
+ return table.getBestMvIdWithHint(sortedIndexIds);
}
protected static List<MaterializedIndex> matchPrefixMost(
diff --git
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSelectHint.java
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSelectHint.java
index 127889ea7ed..a33e2194131 100644
---
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSelectHint.java
+++
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalSelectHint.java
@@ -29,10 +29,8 @@ import
org.apache.doris.nereids.trees.plans.visitor.PlanVisitor;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;
@@ -44,9 +42,9 @@ import java.util.stream.Collectors;
public class LogicalSelectHint<CHILD_TYPE extends Plan> extends
LogicalUnary<CHILD_TYPE>
implements BlockFuncDepsPropagation {
- private final Map<String, SelectHint> hints;
+ private final ImmutableList<SelectHint> hints;
- public LogicalSelectHint(Map<String, SelectHint> hints, CHILD_TYPE child) {
+ public LogicalSelectHint(ImmutableList<SelectHint> hints, CHILD_TYPE
child) {
this(hints, Optional.empty(), Optional.empty(), child);
}
@@ -57,19 +55,29 @@ public class LogicalSelectHint<CHILD_TYPE extends Plan>
extends LogicalUnary<CHI
* @param logicalProperties logicalProperties is use for compute output
* @param child child plan
*/
- public LogicalSelectHint(Map<String, SelectHint> hints,
+ public LogicalSelectHint(ImmutableList<SelectHint> hints,
Optional<GroupExpression> groupExpression,
Optional<LogicalProperties> logicalProperties, CHILD_TYPE child) {
super(PlanType.LOGICAL_SELECT_HINT, groupExpression,
logicalProperties, child);
- this.hints = ImmutableMap.copyOf(Objects.requireNonNull(hints, "hints
can not be null"));
+ this.hints = ImmutableList.copyOf(Objects.requireNonNull(hints, "hints
can not be null"));
}
- public Map<String, SelectHint> getHints() {
+ public List<SelectHint> getHints() {
return hints;
}
- public boolean isIncludeLeading() {
- return hints.containsKey("leading");
+ /**
+ * check if current select hint include some hint
+ * @param hintName hint name
+ * @return boolean which indicate have hint
+ */
+ public boolean isIncludeHint(String hintName) {
+ for (SelectHint hint : hints) {
+ if (hint.getHintName().equalsIgnoreCase(hintName)) {
+ return true;
+ }
+ }
+ return false;
}
@Override
@@ -107,9 +115,9 @@ public class LogicalSelectHint<CHILD_TYPE extends Plan>
extends LogicalUnary<CHI
@Override
public String toString() {
- String hintStr = this.hints.entrySet()
+ String hintStr = this.hints
.stream()
- .map(entry -> entry.getValue().toString())
+ .map(hint -> hint.toString())
.collect(Collectors.joining(", "));
return "LogicalSelectHint (" + hintStr + ")";
}
diff --git
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/joinhint/DistributeHintTest.java
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/joinhint/DistributeHintTest.java
index 0b2ed8069ad..f96fe7e918c 100644
---
a/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/joinhint/DistributeHintTest.java
+++
b/fe/fe-core/src/test/java/org/apache/doris/nereids/jobs/joinorder/joinhint/DistributeHintTest.java
@@ -28,13 +28,12 @@ import org.apache.doris.nereids.util.HyperGraphBuilder;
import org.apache.doris.nereids.util.MemoTestUtils;
import org.apache.doris.nereids.util.PlanChecker;
-import com.google.common.collect.Maps;
+import com.google.common.collect.ImmutableList;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import java.util.Map;
import java.util.Set;
public class DistributeHintTest extends TPCHTestBase {
@@ -85,15 +84,15 @@ public class DistributeHintTest extends TPCHTestBase {
}
private Plan generateLeadingHintPlan(int tableNum, Plan childPlan) {
- Map<String, SelectHint> hints = Maps.newLinkedHashMap();
+ ImmutableList.Builder<SelectHint> hints = ImmutableList.builder();
List<String> leadingParameters = new ArrayList<String>();
for (int i = 0; i < tableNum; i++) {
leadingParameters.add(String.valueOf(i));
}
Collections.shuffle(leadingParameters);
System.out.println("LeadingHint: " + leadingParameters.toString());
- hints.put("leading", new SelectHintLeading("leading",
leadingParameters));
- return new LogicalSelectHint<>(hints, childPlan);
+ hints.add(new SelectHintLeading("Leading", leadingParameters));
+ return new LogicalSelectHint<>(hints.build(), childPlan);
}
private void randomTest(int tableNum, int edgeNum, boolean withJoinHint,
boolean withLeading) {
diff --git a/regression-test/suites/nereids_p0/hint/test_use_mv.groovy
b/regression-test/suites/nereids_p0/hint/test_use_mv.groovy
new file mode 100644
index 00000000000..e511ccc11ae
--- /dev/null
+++ b/regression-test/suites/nereids_p0/hint/test_use_mv.groovy
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+suite("test_use_mv") {
+ // create database and tables
+ sql 'DROP DATABASE IF EXISTS test_use_mv'
+ sql 'CREATE DATABASE IF NOT EXISTS test_use_mv'
+ sql 'use test_use_mv'
+
+ // setting planner to nereids
+ sql 'set exec_mem_limit=21G'
+ sql 'set be_number_for_test=1'
+ sql 'set parallel_pipeline_task_num=1'
+ sql "set disable_nereids_rules=PRUNE_EMPTY_PARTITION"
+ sql 'set enable_nereids_planner=true'
+ sql 'set enable_nereids_distribute_planner=false'
+ sql "set ignore_shape_nodes='PhysicalProject'"
+ sql 'set enable_fallback_to_original_planner=false'
+ sql 'set runtime_filter_mode=OFF'
+
+ sql """drop table if exists t1;"""
+ // create tables
+ sql """
+ CREATE TABLE `t1` (
+ `k1` int(11) NULL,
+ `k2` int(11) NULL,
+ `v1` int(11) SUM NULL
+ ) ENGINE=OLAP
+ AGGREGATE KEY(`k1`, `k2`)
+ COMMENT 'OLAP'
+ DISTRIBUTED BY HASH(`k1`) BUCKETS 3
+ PROPERTIES (
+ "replication_allocation" = "tag.location.default:
1",
+ "in_memory" = "false",
+ "storage_format" = "V2",
+ "disable_auto_compaction" = "false"
+ );
+ """
+ sql """ alter table t1 add rollup r1(k2, k1); """
+ waitForRollUpJob("t1", 5000, 1)
+ sql """ alter table t1 add rollup r2(k2); """
+ waitForRollUpJob("t1", 5000, 1)
+ createMV("create materialized view k1_k2_sumk3 as select k1, k2, sum(v1)
from t1 group by k1, k2;")
+ sql """set enable_sync_mv_cost_based_rewrite = false"""
+ explain {
+ sql """select k1 from t1;"""
+ contains("t1(r1)")
+ }
+ sql """set enable_sync_mv_cost_based_rewrite = true"""
+ explain {
+ sql """select /*+ no_use_mv */ k1 from t1;"""
+ notContains("t1(r1)")
+ }
+ explain {
+ sql """select /*+ no_use_mv(t1) */ k1 from t1;"""
+ contains("parameter of no_use_mv hint must be in pairs")
+ }
+ explain {
+ sql """select /*+ no_use_mv(t1.`*`) */ k1 from t1;"""
+ contains("t1(t1)")
+ }
+ explain {
+ sql """select /*+ use_mv(t1.`*`) */ k1 from t1;"""
+ contains("use_mv hint should only have one mv in one table")
+ }
+ explain {
+ sql """select /*+ use_mv(t1.r1,t1.r2) */ k1 from t1;"""
+ contains("use_mv hint should only have one mv in one table")
+ }
+ explain {
+ sql """select /*+ use_mv(t1.r1) use_mv(t1.r2) */ k1 from t1;"""
+ contains("one use_mv hint is allowed")
+ }
+ explain {
+ sql """select /*+ no_use_mv(t1.r1) no_use_mv(t1.r2) */ k1 from t1;"""
+ contains("only one no_use_mv hint is allowed")
+ }
+ explain {
+ sql """select /*+ no_use_mv(t1.r3) */ k1 from t1;"""
+ contains("do not have mv: r3 in table: t1")
+ }
+ explain {
+ sql """select /*+ use_mv(t1.r1) no_use_mv(t1.r1) */ k1 from t1;"""
+ contains("conflict mv exist in use_mv and no_use_mv in the same time")
+ }
+ explain {
+ sql """select /*+ use_mv(t1.k1_k2_sumk3) */ k1, k2, sum(v1) from t1
group by k1, k2;"""
+ contains("t1(k1_k2_sumk3)")
+ }
+ explain {
+ sql """select /*+ use_mv(t1.k1_k2_sumk3) */ k1, k2, min(v1) from t1
group by k1, k2;"""
+ notContains("t1(k1_k2_sumk3)")
+ }
+
+}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]