-
Notifications
You must be signed in to change notification settings - Fork 4k
[fix](fe) Accept foldable gram numbers in ngram_search #68311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
c808a29
ea279c4
d487595
38b7a46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -174,6 +174,21 @@ public static Expression foldByBE(ExpressionMatchingContext<Expression> context) | |
| return root; | ||
| } | ||
|
|
||
| /** Evaluate a semantic constant whose value is required for argument validation. */ | ||
| public static Expression evaluateConstant(Expression expression, ConnectContext context) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Preserve the BE-fold safety exclusions here This direct path skips the
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 已在 38b7a46 修复。
补充验证:
本次保留的是执行准入限制,并未将 FE 的五秒等待上限描述为 BE 取消保证。 |
||
| // Required evaluation must also honor exclusions such as Sleep, which can outlive | ||
| // the RPC timeout. Leave excluded expressions unevaluated for the caller to reject. | ||
| if (expression.anyMatch(e -> shouldSkipFold((Expression) e))) { | ||
| return expression; | ||
| } | ||
| Expr legacyExpr = ExpressionTranslator.translate(expression, null); | ||
| Map<String, Expression> constants = Collections.singletonMap("0", expression); | ||
| Map<String, TExpr> thriftExpressions = Collections.singletonMap( | ||
| "0", ExprToThriftVisitor.treeToThrift(legacyExpr)); | ||
| return evalOnBE(Collections.singletonMap("0", thriftExpressions), constants, context) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P2] Retry another healthy peer before rejecting the gram This required path turns |
||
| .getOrDefault("0", expression); | ||
| } | ||
|
|
||
| private static Expression replace( | ||
| Expression root, Map<String, Expression> constMap, Map<String, Expression> resultMap) { | ||
| for (Entry<String, Expression> entry : constMap.entrySet()) { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -19,14 +19,20 @@ | |
|
|
||
| import org.apache.doris.catalog.FunctionSignature; | ||
| import org.apache.doris.nereids.exceptions.AnalysisException; | ||
| import org.apache.doris.nereids.rules.expression.rules.FoldConstantRuleOnBE; | ||
| import org.apache.doris.nereids.rules.expression.rules.FoldConstantRuleOnFE; | ||
| import org.apache.doris.nereids.trees.expressions.Cast; | ||
| import org.apache.doris.nereids.trees.expressions.Expression; | ||
| import org.apache.doris.nereids.trees.expressions.functions.ExplicitlyCastableSignature; | ||
| import org.apache.doris.nereids.trees.expressions.functions.PropagateNullable; | ||
| import org.apache.doris.nereids.trees.expressions.literal.IntegerLikeLiteral; | ||
| import org.apache.doris.nereids.trees.expressions.literal.Literal; | ||
| import org.apache.doris.nereids.trees.expressions.literal.NullLiteral; | ||
| import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor; | ||
| import org.apache.doris.nereids.types.DoubleType; | ||
| import org.apache.doris.nereids.types.IntegerType; | ||
| import org.apache.doris.nereids.types.StringType; | ||
| import org.apache.doris.qe.ConnectContext; | ||
|
|
||
| import com.google.common.base.Preconditions; | ||
| import com.google.common.collect.ImmutableList; | ||
|
|
@@ -57,21 +63,47 @@ private NgramSearch(ScalarFunctionParams functionParams) { | |
|
|
||
| @Override | ||
| public void checkLegalityBeforeTypeCoercion() { | ||
| if (!child(1).isConstant()) { | ||
| if (!getArgument(1).isConstant()) { | ||
| throw new AnalysisException( | ||
| "ngram_search(text,pattern,gram_num): pattern support const value only."); | ||
| } | ||
| Expression gramNum = child(2); | ||
| if (!(gramNum instanceof IntegerLikeLiteral)) { | ||
| Expression gramNum = getArgument(2); | ||
| if (!gramNum.isConstant() || !gramNum.getDataType().isIntegralType()) { | ||
| throw new AnalysisException( | ||
| "ngram_search(text,pattern,gram_num): gram_num support const value only."); | ||
| } | ||
| if (((IntegerLikeLiteral) gramNum).getIntValue() <= 0) { | ||
| gramNum = FoldConstantRuleOnFE.evaluateWithoutContext(gramNum); | ||
| if (gramNum instanceof NullLiteral) { | ||
| throw new AnalysisException( | ||
| "ngram_search(text,pattern,gram_num): gram_num support const value only."); | ||
| } | ||
| if (gramNum instanceof IntegerLikeLiteral && ((IntegerLikeLiteral) gramNum).getIntValue() <= 0) { | ||
| throw new AnalysisException( | ||
| "ngram_search(text,pattern,gram_num): gram_num must be a positive constant."); | ||
| } | ||
| } | ||
|
|
||
| /** Resolve the required constant before rewrites can discard the function call. */ | ||
| public NgramSearch withFoldedGramNumber() { | ||
| Expression gramNum = getArgument(2); | ||
| if (!gramNum.getDataType().equals(IntegerType.INSTANCE)) { | ||
| gramNum = new Cast(gramNum, IntegerType.INSTANCE); | ||
| } | ||
| gramNum = FoldConstantRuleOnFE.evaluateWithoutContext(gramNum); | ||
| // Argument validation is independent of the optional BE folding setting. Keep the | ||
| // evaluated value in the plan so CSE and execution use exactly the value we validate. | ||
| if (!(gramNum instanceof Literal)) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1] Batch required folds before binding under table locks For a cold plan over an internal table, |
||
| gramNum = FoldConstantRuleOnBE.evaluateConstant(gramNum, ConnectContext.get()); | ||
| if (!(gramNum instanceof Literal)) { | ||
| throw new AnalysisException( | ||
| "ngram_search(text,pattern,gram_num): failed to evaluate constant gram_num."); | ||
| } | ||
| } | ||
| NgramSearch folded = withChildren(ImmutableList.of(getArgument(0), getArgument(1), gramNum)); | ||
| folded.checkLegalityBeforeTypeCoercion(); | ||
| return folded; | ||
| } | ||
|
|
||
| /** | ||
| * withChildren. | ||
| */ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[P2] Validate the gram even when execution is skipped
This is the only value check, but
execute_implis not guaranteed to run.crc32('abc') % 0remains a constant integral tree in FE and evaluates to NULL on BE, where default NULL propagation returns before this line. Also,select ngram_search(cast(number as string), 'abc', crc32('abc') % 3) from numbers("number"="0")has a row-dependent root, so the empty projection skips the function and the known-zero gram is never rejected. Literal NULL/zero grams are rejected during analysis regardless of these shapes, so newly admitted BE-only constants change the contract. Please validate semantic constants on a path that survives CSE and zero-row execution, while retaining a pre-NULL batch check for materialized nonempty slots, and cover both cases in both fold modes.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed in d487595.
The gram is now resolved and validated during function binding, before NULL propagation, CSE, or empty-plan rewrites can discard the call. FE-evaluable constants stay local; other constants use the existing BE evaluator regardless of the optional BE-fold setting. The evaluated INT literal is retained in the plan, and failed evaluation is reported rather than silently deferring validation to execution.
BE also checks materialized grams before propagating NULL from text/pattern. Regression coverage includes
crc32('abc') % 0, zero/negative grams with zero rows, CSE-shaped expressions, NULL text,WHERE false, andLIMIT 0, in both fold modes. This also covers the case where NULL text previously removed the entire function on FE, which a BE-onlyopen()fix would miss.Validation: FE UT 12/12, ASAN BE UT 7/7, ASAN FE/BE build, and the new/existing string regression suites 2/2 passed. The prior 26 SQL probes now produce the expected outcomes. clang-tidy remains blocked by the existing unmatched NOLINTEND in core/types.h, with no emitted changed-line diagnostic.
The PR description explicitly records the additional planning RPC for BE-only grams, its existing five-second timeout, and the planner-lock waiting tradeoff. This thread is left for re-review rather than manually resolved.