This is an automated email from the ASF dual-hosted git repository. jianliangqi 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 1cc611a913 [fix](match) fix regression case test_index_match_select and test_index_match_phrase (#20860) 1cc611a913 is described below commit 1cc611a91383b449b63be5bf1323892755d2b11c Author: YueW <45946325+tany...@users.noreply.github.com> AuthorDate: Fri Jun 16 20:18:29 2023 +0800 [fix](match) fix regression case test_index_match_select and test_index_match_phrase (#20860) 1. add more checks for match expression in nereids: - match expression only support in filter - match expression left child and right child must all be string type - left child for match expression must be sloftRef, right child for match expression must be Literal 2. to fix regression case test_index_match_select and test_index_match_phrase --- .../main/java/org/apache/doris/catalog/Type.java | 2 +- .../org/apache/doris/analysis/MatchPredicate.java | 9 +++- .../glue/translator/ExpressionTranslator.java | 35 ++++++++++-- .../doris/nereids/jobs/executor/Rewriter.java | 6 +++ .../org/apache/doris/nereids/rules/RuleType.java | 1 + .../nereids/rules/analysis/CheckAfterRewrite.java | 17 ++++++ .../rules/expression/rules/FunctionBinder.java | 22 ++++++++ .../rules/rewrite/CheckMatchExpression.java | 62 ++++++++++++++++++++++ .../doris/nereids/trees/expressions/Match.java | 26 ++++----- 9 files changed, 162 insertions(+), 18 deletions(-) diff --git a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java index 55f386d428..db3bb398ab 100644 --- a/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java +++ b/fe/fe-common/src/main/java/org/apache/doris/catalog/Type.java @@ -420,7 +420,7 @@ public abstract class Type { public boolean isStringType() { return isScalarType(PrimitiveType.VARCHAR) || isScalarType(PrimitiveType.CHAR) - || isScalarType(PrimitiveType.STRING) || isScalarType(PrimitiveType.AGG_STATE); + || isScalarType(PrimitiveType.STRING); } public boolean isVarcharOrStringType() { diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/MatchPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/MatchPredicate.java index da6a5999d4..8311a183e2 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/MatchPredicate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/MatchPredicate.java @@ -184,8 +184,15 @@ public class MatchPredicate extends Predicate { /** * use for Nereids ONLY */ - public MatchPredicate(Operator op, Expr e1, Expr e2, Type retType, NullableMode nullableMode) { + public MatchPredicate(Operator op, Expr e1, Expr e2, Type retType, + NullableMode nullableMode, String invertedIndexParser, String invertedIndexParserMode) { this(op, e1, e2); + if (invertedIndexParser != null) { + this.invertedIndexParser = invertedIndexParser; + } + if (invertedIndexParserMode != null) { + this.invertedIndexParserMode = invertedIndexParserMode; + } fn = new Function(new FunctionName(op.name), Lists.newArrayList(e1.getType(), e2.getType()), retType, false, true, nullableMode); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java index 211651e31e..d085baf02f 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/glue/translator/ExpressionTranslator.java @@ -30,13 +30,18 @@ import org.apache.doris.analysis.Expr; import org.apache.doris.analysis.FunctionCallExpr; import org.apache.doris.analysis.FunctionName; import org.apache.doris.analysis.FunctionParams; +import org.apache.doris.analysis.IndexDef; import org.apache.doris.analysis.IsNullPredicate; import org.apache.doris.analysis.MatchPredicate; import org.apache.doris.analysis.OrderByElement; +import org.apache.doris.analysis.SlotDescriptor; import org.apache.doris.analysis.SlotRef; import org.apache.doris.analysis.TimestampArithmeticExpr; +import org.apache.doris.analysis.TupleDescriptor; import org.apache.doris.catalog.Function; import org.apache.doris.catalog.Function.NullableMode; +import org.apache.doris.catalog.Index; +import org.apache.doris.catalog.OlapTable; import org.apache.doris.catalog.Type; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.trees.expressions.AggregateExpression; @@ -165,12 +170,36 @@ public class ExpressionTranslator extends DefaultExpressionVisitor<Expr, PlanTra @Override public Expr visitMatch(Match match, PlanTranslatorContext context) { + String invertedIndexParser = null; + String invertedIndexParserMode = null; + SlotRef left = (SlotRef) match.left().accept(this, context); + SlotDescriptor slotDesc = left.getDesc(); + if (slotDesc != null && slotDesc.isScanSlot()) { + TupleDescriptor slotParent = slotDesc.getParent(); + OlapTable olapTbl = (OlapTable) slotParent.getTable(); + if (olapTbl == null) { + throw new AnalysisException("slotRef in matchExpression failed to get OlapTable"); + } + List<Index> indexes = olapTbl.getIndexes(); + for (Index index : indexes) { + if (index.getIndexType() == IndexDef.IndexType.INVERTED) { + List<String> columns = index.getColumns(); + if (left.getColumnName().equals(columns.get(0))) { + invertedIndexParser = index.getInvertedIndexParser(); + invertedIndexParserMode = index.getInvertedIndexParserMode(); + break; + } + } + } + } MatchPredicate.Operator op = match.op(); return new MatchPredicate(op, - match.child(0).accept(this, context), - match.child(1).accept(this, context), + match.left().accept(this, context), + match.right().accept(this, context), match.getDataType().toCatalogDataType(), - NullableMode.DEPEND_ON_ARGUMENT); + NullableMode.DEPEND_ON_ARGUMENT, + invertedIndexParser, + invertedIndexParserMode); } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java index 43c0ae97f9..73ada5fca1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/jobs/executor/Rewriter.java @@ -38,6 +38,7 @@ import org.apache.doris.nereids.rules.rewrite.BuildCTEAnchorAndCTEProducer; import org.apache.doris.nereids.rules.rewrite.CTEProducerRewrite; import org.apache.doris.nereids.rules.rewrite.CheckAndStandardizeWindowFunctionAndFrame; import org.apache.doris.nereids.rules.rewrite.CheckDataTypes; +import org.apache.doris.nereids.rules.rewrite.CheckMatchExpression; import org.apache.doris.nereids.rules.rewrite.CollectFilterAboveConsumer; import org.apache.doris.nereids.rules.rewrite.CollectProjectAboveConsumer; import org.apache.doris.nereids.rules.rewrite.ColumnPruning; @@ -263,6 +264,11 @@ public class Rewriter extends AbstractBatchJobExecutor { bottomUp(RuleSet.PUSH_DOWN_FILTERS), custom(RuleType.ELIMINATE_UNNECESSARY_PROJECT, EliminateUnnecessaryProject::new) ), + topic("Match expression check", + topDown( + new CheckMatchExpression() + ) + ), // this rule batch must keep at the end of rewrite to do some plan check topic("Final rewrite and check", custom(RuleType.ENSURE_PROJECT_ON_TOP_JOIN, EnsureProjectOnTopJoin::new), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java index 7dd712d1e5..f2b7fcb177 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/RuleType.java @@ -97,6 +97,7 @@ public enum RuleType { NORMALIZE_REPEAT(RuleTypeClass.REWRITE), EXTRACT_AND_NORMALIZE_WINDOW_EXPRESSIONS(RuleTypeClass.REWRITE), CHECK_AND_STANDARDIZE_WINDOW_FUNCTION_AND_FRAME(RuleTypeClass.REWRITE), + CHECK_MATCH_EXPRESSION(RuleTypeClass.REWRITE), AGGREGATE_DISASSEMBLE(RuleTypeClass.REWRITE), SIMPLIFY_AGG_GROUP_BY(RuleTypeClass.REWRITE), DISTINCT_AGGREGATE_DISASSEMBLE(RuleTypeClass.REWRITE), diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAfterRewrite.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAfterRewrite.java index 961417691a..554342d406 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAfterRewrite.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/CheckAfterRewrite.java @@ -24,6 +24,7 @@ import org.apache.doris.nereids.rules.RuleType; import org.apache.doris.nereids.trees.expressions.Alias; import org.apache.doris.nereids.trees.expressions.ExprId; import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.Match; import org.apache.doris.nereids.trees.expressions.NamedExpression; import org.apache.doris.nereids.trees.expressions.Slot; import org.apache.doris.nereids.trees.expressions.SlotNotFromChildren; @@ -32,6 +33,8 @@ import org.apache.doris.nereids.trees.expressions.WindowExpression; import org.apache.doris.nereids.trees.expressions.functions.ExpressionTrait; import org.apache.doris.nereids.trees.plans.Plan; import org.apache.doris.nereids.trees.plans.logical.LogicalAggregate; +import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; +import org.apache.doris.nereids.trees.plans.logical.LogicalOlapScan; import org.apache.doris.nereids.trees.plans.logical.LogicalSort; import org.apache.doris.nereids.trees.plans.logical.LogicalTopN; import org.apache.doris.nereids.trees.plans.logical.LogicalWindow; @@ -52,6 +55,7 @@ public class CheckAfterRewrite extends OneAnalysisRuleFactory { return any().then(plan -> { checkAllSlotReferenceFromChildren(plan); checkMetricTypeIsUsedCorrectly(plan); + checkMatchIsUsedCorrectly(plan); return null; }).toRule(RuleType.CHECK_ANALYSIS); } @@ -131,4 +135,17 @@ public class CheckAfterRewrite extends OneAnalysisRuleFactory { }); } } + + private void checkMatchIsUsedCorrectly(Plan plan) { + if (plan.getExpressions().stream().anyMatch( + expression -> expression instanceof Match)) { + if (plan instanceof LogicalFilter && plan.child(0) instanceof LogicalOlapScan) { + return; + } else { + throw new AnalysisException(String.format( + "Not support match in %s in plan: %s, only support in olapScan filter", + plan.child(0), plan)); + } + } + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java index c2c48de051..2ab66cc1b6 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FunctionBinder.java @@ -39,6 +39,7 @@ import org.apache.doris.nereids.trees.expressions.InPredicate; import org.apache.doris.nereids.trees.expressions.InSubquery; import org.apache.doris.nereids.trees.expressions.IntegralDivide; import org.apache.doris.nereids.trees.expressions.ListQuery; +import org.apache.doris.nereids.trees.expressions.Match; import org.apache.doris.nereids.trees.expressions.Not; import org.apache.doris.nereids.trees.expressions.TimestampArithmetic; import org.apache.doris.nereids.trees.expressions.functions.BoundFunction; @@ -241,4 +242,25 @@ public class FunctionBinder extends AbstractExpressionRewriteRule { inSubquery.getCorrelateSlots(), ((ListQuery) afterTypeCoercion.right()).getTypeCoercionExpr(), inSubquery.isNot()); } + + @Override + public Expression visitMatch(Match match, ExpressionRewriteContext context) { + Expression left = match.left().accept(this, context); + Expression right = match.right().accept(this, context); + // check child type + if (!left.getDataType().isStringLikeType()) { + throw new AnalysisException(String.format( + "left operand '%s' part of predicate " + "'%s' should return type 'STRING' but " + + "returns type '%s'.", + left.toSql(), match.toSql(), left.getDataType())); + } + + if (!right.getDataType().isStringLikeType() && !right.getDataType().isNullType()) { + throw new AnalysisException(String.format( + "right operand '%s' part of predicate " + "'%s' should return type 'STRING' but " + + "returns type '%s'.", + right.toSql(), match.toSql(), right.getDataType())); + } + return match.withChildren(left, right); + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpression.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpression.java new file mode 100644 index 0000000000..3b04059a48 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/rewrite/CheckMatchExpression.java @@ -0,0 +1,62 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.apache.doris.nereids.rules.rewrite; + +import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.nereids.rules.Rule; +import org.apache.doris.nereids.rules.RuleType; +import org.apache.doris.nereids.trees.expressions.Expression; +import org.apache.doris.nereids.trees.expressions.Match; +import org.apache.doris.nereids.trees.expressions.SlotReference; +import org.apache.doris.nereids.trees.expressions.literal.Literal; +import org.apache.doris.nereids.trees.plans.logical.LogicalFilter; + +import java.util.List; + +/** + * Check match expression + */ +public class CheckMatchExpression extends OneRewriteRuleFactory { + + @Override + public Rule build() { + return logicalFilter(logicalOlapScan()) + .when(filter -> containsMatchExpression(filter.getExpressions())) + .then(this::checkChildren) + .toRule(RuleType.CHECK_MATCH_EXPRESSION); + } + + private LogicalFilter checkChildren(LogicalFilter filter) { + List<Expression> expressions = filter.getExpressions(); + for (Expression expr : expressions) { + if (expr instanceof Match) { + Match matchExpression = (Match) expr; + if (!(matchExpression.left() instanceof SlotReference) + || !(matchExpression.right() instanceof Literal)) { + throw new AnalysisException(String.format( + "Only support match left operand is SlotRef, right operand is Literal")); + } + } + } + return filter; + } + + private boolean containsMatchExpression(List<Expression> expressions) { + return expressions.stream().anyMatch(expr -> expr.anyMatch(Match.class::isInstance)); + } +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Match.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Match.java index ef3b79c196..01241243bb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Match.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/Match.java @@ -20,22 +20,20 @@ package org.apache.doris.nereids.trees.expressions; import org.apache.doris.analysis.MatchPredicate.Operator; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.exceptions.UnboundException; +import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; import org.apache.doris.nereids.types.BooleanType; import org.apache.doris.nereids.types.DataType; - -import com.google.common.base.Preconditions; +import org.apache.doris.nereids.types.coercion.AbstractDataType; +import org.apache.doris.nereids.types.coercion.AnyDataType; /** * like expression: a MATCH 'hello'. */ -public abstract class Match extends Expression { - - protected final String symbol; +public abstract class Match extends BinaryOperator implements PropagateNullable { public Match(Expression left, Expression right, String symbol) { - super(left, right); - this.symbol = symbol; + super(left, right, symbol); } /** @@ -60,22 +58,24 @@ public abstract class Match extends Expression { return BooleanType.INSTANCE; } + @Override + public AbstractDataType inputType() { + return AnyDataType.INSTANCE; + } + @Override public boolean nullable() throws UnboundException { - Preconditions.checkArgument(children.size() == 2); - return (children.get(0).nullable() || children.get(1).nullable()); + return left().nullable() || right().nullable(); } @Override public String toSql() { - Preconditions.checkArgument(children.size() == 2); - return "(" + children.get(0).toSql() + " " + symbol + " " + children.get(1).toSql() + ")"; + return "(" + left().toSql() + " " + symbol + " " + right().toSql() + ")"; } @Override public String toString() { - Preconditions.checkArgument(children.size() == 2); - return "(" + children.get(0).toString() + " " + symbol + " " + children.get(1).toString() + ")"; + return "(" + left().toString() + " " + symbol + " " + right().toString() + ")"; } public <R, C> R accept(ExpressionVisitor<R, C> visitor, C context) { --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org