This is an automated email from the ASF dual-hosted git repository.

yiguolei pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/doris.git


The following commit(s) were added to refs/heads/branch-4.2 by this push:
     new 8ae89bb6504 branch-4.2: [fix](fe) Accept constant expressions as the 
gram_num of ngram_search #68311 (#70134)
8ae89bb6504 is described below

commit 8ae89bb65047243a89e7bf87058aa267fbda7f21
Author: morrySnow <[email protected]>
AuthorDate: Wed Nov 4 08:38:59 2026 +0800

    branch-4.2: [fix](fe) Accept constant expressions as the gram_num of 
ngram_search #68311 (#70134)
    
    picked from master #68311
    
    Co-authored-by: Jerry Hu <[email protected]>
---
 be/src/exprs/function/function_string_misc.cpp     |   7 +
 be/test/exprs/function/function_string_test.cpp    |  28 ++++
 .../expression/rules/FoldConstantRuleOnFE.java     |  40 ++++++
 .../expressions/functions/scalar/NgramSearch.java  |  28 +++-
 .../functions/scalar/NgramSearchTest.java          | 148 +++++++++++++++++++++
 .../test_ngram_search_gram_num.out                 |  73 ++++++++++
 .../test_ngram_search_gram_num.groovy              |  87 ++++++++++++
 7 files changed, 407 insertions(+), 4 deletions(-)

diff --git a/be/src/exprs/function/function_string_misc.cpp 
b/be/src/exprs/function/function_string_misc.cpp
index 86918be9556..fdbedae326c 100644
--- a/be/src/exprs/function/function_string_misc.cpp
+++ b/be/src/exprs/function/function_string_misc.cpp
@@ -826,6 +826,13 @@ public:
         }
         auto pattern = assert_cast<const 
ColumnString*>(argument_columns[1].get())->get_data_at(0);
         auto gram_num = assert_cast<const 
ColumnInt32*>(argument_columns[2].get())->get_element(0);
+        // FE only rejects a nonpositive gram_num once it is a literal. A 
constant expression that
+        // FE cannot evaluate (e.g. `crc32('abc') % 3 - 3`) reaches BE 
unchecked when the whole
+        // call is folded on BE, so validate here before it is used as a 
substring length.
+        if (gram_num <= 0) {
+            return Status::InvalidArgument(
+                    "ngram_search(text,pattern,gram_num): gram_num must be a 
positive constant.");
+        }
         const auto* text_col = assert_cast<const 
ColumnString*>(argument_columns[0].get());
 
         if (col_const[0]) {
diff --git a/be/test/exprs/function/function_string_test.cpp 
b/be/test/exprs/function/function_string_test.cpp
index 7805e08eb16..0152f704cd5 100644
--- a/be/test/exprs/function/function_string_test.cpp
+++ b/be/test/exprs/function/function_string_test.cpp
@@ -81,6 +81,34 @@ DataSet make_md5_varbinary_dataset(const 
std::vector<std::string>& inputs) {
 
 } // namespace
 
+TEST(function_string_test, ngram_search_gram_num) {
+    const InputTypeSet input_types = {Notnull {TYPE_STRING}, ConstedNotnull 
{TYPE_STRING},
+                                      ConstedNotnull {TYPE_INT}};
+    // 2 * |intersection| / (|text grams| + |pattern grams|); one row per data 
set because the
+    // const arguments are built from a single value.
+    const DataSet data_set = {{{std::string("abc"), std::string("abc"), 
int32_t(3)}, 1.0},
+                              {{std::string("ab"), std::string("abc"), 
int32_t(3)}, 0.0},
+                              {{std::string("ab"), std::string("abc"), 
int32_t(1)}, 0.8}};
+    for (const auto& row : data_set) {
+        ASSERT_TRUE(check_function<DataTypeFloat64>("ngram_search", 
input_types, {row}).ok());
+    }
+}
+
+TEST(function_string_test, ngram_search_nonpositive_gram_num) {
+    // FE folds a constant expression like `crc32('abc') % 3 - 3` on BE before 
it can check the
+    // value, so BE must reject a nonpositive gram_num instead of using it as 
a substring length.
+    const InputTypeSet input_types = {Notnull {TYPE_STRING}, ConstedNotnull 
{TYPE_STRING},
+                                      ConstedNotnull {TYPE_INT}};
+    for (int32_t gram_num : {0, -1}) {
+        const DataSet data_set = {{{std::string("abc"), std::string("abc"), 
gram_num}, 0.0}};
+        auto st = check_function<DataTypeFloat64>("ngram_search", input_types, 
data_set, -1, -1,
+                                                  true);
+        EXPECT_TRUE(st.is<ErrorCode::INVALID_ARGUMENT>()) << st;
+        EXPECT_NE(st.to_string().find("gram_num must be a positive constant"), 
std::string::npos)
+                << st;
+    }
+}
+
 TEST(function_string_test, parse_data_size_nullable) {
     const InputTypeSet input_types = {PrimitiveType::TYPE_STRING};
     const DataSet data_set = {{{Null()}, Null()},
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
index 3522ad85e8a..a4064f09e91 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/expression/rules/FoldConstantRuleOnFE.java
@@ -124,6 +124,8 @@ public class FoldConstantRuleOnFE extends 
AbstractExpressionRewriteRule
 
     public static final FoldConstantRuleOnFE VISITOR_INSTANCE = new 
FoldConstantRuleOnFE(true);
     public static final FoldConstantRuleOnFE PATTERN_MATCH_INSTANCE = new 
FoldConstantRuleOnFE(false);
+    private static final FoldConstantRuleOnFE CONTEXT_FREE_VISITOR_INSTANCE
+            = new FoldConstantRuleOnFE(true, false);
 
     // record whether current expression is in an aggregate function with 
distinct,
     // if is, we will skip to fold constant
@@ -131,15 +133,26 @@ public class FoldConstantRuleOnFE extends 
AbstractExpressionRewriteRule
     private static final CheckWhetherUnderAggDistinct NOT_UNDER_AGG_DISTINCT = 
new CheckWhetherUnderAggDistinct();
 
     private final boolean deepRewrite;
+    private final boolean foldContextDependentExpressions;
 
     public FoldConstantRuleOnFE(boolean deepRewrite) {
+        this(deepRewrite, true);
+    }
+
+    private FoldConstantRuleOnFE(boolean deepRewrite, boolean 
foldContextDependentExpressions) {
         this.deepRewrite = deepRewrite;
+        this.foldContextDependentExpressions = foldContextDependentExpressions;
     }
 
     public static Expression evaluate(Expression expression, 
ExpressionRewriteContext expressionRewriteContext) {
         return VISITOR_INSTANCE.rewrite(expression, expressionRewriteContext);
     }
 
+    /** Evaluate expressions that do not require a rewrite or connection 
context. */
+    public static Expression evaluateWithoutContext(Expression expression) {
+        return CONTEXT_FREE_VISITOR_INSTANCE.rewrite(expression, null);
+    }
+
     @Override
     public List<ExpressionListenerMatcher<? extends Expression>> 
buildListeners() {
         return ImmutableList.of(
@@ -233,12 +246,18 @@ public class FoldConstantRuleOnFE extends 
AbstractExpressionRewriteRule
 
     @Override
     public Expression visitUnboundVariable(UnboundVariable unboundVariable, 
ExpressionRewriteContext context) {
+        if (!foldContextDependentExpressions) {
+            return unboundVariable;
+        }
         Variable variable = 
ExpressionAnalyzer.resolveUnboundVariable(unboundVariable);
         return variable.getRealExpression();
     }
 
     @Override
     public Expression visitEncryptKeyRef(EncryptKeyRef encryptKeyRef, 
ExpressionRewriteContext context) {
+        if (!foldContextDependentExpressions) {
+            return encryptKeyRef;
+        }
         String dbName = encryptKeyRef.getDbName();
         ConnectContext connectContext = 
context.cascadesContext.getConnectContext();
         if (Strings.isNullOrEmpty(dbName)) {
@@ -361,36 +380,54 @@ public class FoldConstantRuleOnFE extends 
AbstractExpressionRewriteRule
 
     @Override
     public Expression visitDatabase(Database database, 
ExpressionRewriteContext context) {
+        if (!foldContextDependentExpressions) {
+            return database;
+        }
         String res = 
ClusterNamespace.getNameFromFullName(context.cascadesContext.getConnectContext().getDatabase());
         return new VarcharLiteral(res);
     }
 
     @Override
     public Expression visitCurrentUser(CurrentUser currentUser, 
ExpressionRewriteContext context) {
+        if (!foldContextDependentExpressions) {
+            return currentUser;
+        }
         String res = 
context.cascadesContext.getConnectContext().getCurrentUserIdentity().toString();
         return new VarcharLiteral(res);
     }
 
     @Override
     public Expression visitCurrentCatalog(CurrentCatalog currentCatalog, 
ExpressionRewriteContext context) {
+        if (!foldContextDependentExpressions) {
+            return currentCatalog;
+        }
         String res = 
context.cascadesContext.getConnectContext().getDefaultCatalog();
         return new VarcharLiteral(res);
     }
 
     @Override
     public Expression visitUser(User user, ExpressionRewriteContext context) {
+        if (!foldContextDependentExpressions) {
+            return user;
+        }
         String res = 
context.cascadesContext.getConnectContext().getUserWithLoginRemoteIpString();
         return new VarcharLiteral(res);
     }
 
     @Override
     public Expression visitSessionUser(SessionUser user, 
ExpressionRewriteContext context) {
+        if (!foldContextDependentExpressions) {
+            return user;
+        }
         String res = 
context.cascadesContext.getConnectContext().getUserWithLoginRemoteIpString();
         return new VarcharLiteral(res);
     }
 
     @Override
     public Expression visitLastQueryId(LastQueryId queryId, 
ExpressionRewriteContext context) {
+        if (!foldContextDependentExpressions) {
+            return queryId;
+        }
         String res = "Not Available";
         TUniqueId id = 
context.cascadesContext.getConnectContext().getLastQueryId();
         if (id != null) {
@@ -401,6 +438,9 @@ public class FoldConstantRuleOnFE extends 
AbstractExpressionRewriteRule
 
     @Override
     public Expression visitConnectionId(ConnectionId connectionId, 
ExpressionRewriteContext context) {
+        if (!foldContextDependentExpressions) {
+            return connectionId;
+        }
         return new 
BigIntLiteral(context.cascadesContext.getConnectContext().getConnectionId());
     }
 
diff --git 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NgramSearch.java
 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NgramSearch.java
index 396244657bc..9f2616dbd2d 100644
--- 
a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NgramSearch.java
+++ 
b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NgramSearch.java
@@ -19,10 +19,12 @@ package 
org.apache.doris.nereids.trees.expressions.functions.scalar;
 
 import org.apache.doris.catalog.FunctionSignature;
 import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.rules.expression.rules.FoldConstantRuleOnFE;
 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.NullLiteral;
 import org.apache.doris.nereids.trees.expressions.visitor.ExpressionVisitor;
 import org.apache.doris.nereids.types.DoubleType;
 import org.apache.doris.nereids.types.IntegerType;
@@ -57,16 +59,34 @@ public class NgramSearch extends ScalarFunction
 
     @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) {
+        // Constant folding has not run yet, so a constant expression such as 
`1 + 2` is not a
+        // literal here. Reject the values FE can already determine now, 
before NULL propagation or
+        // plan pruning can drop the whole call and skip 
checkLegalityAfterRewrite.
+        
checkGramNumValue(FoldConstantRuleOnFE.evaluateWithoutContext(gramNum));
+    }
+
+    @Override
+    public void checkLegalityAfterRewrite() {
+        // Constant folding (FE or BE) may have produced the literal by now. A 
constant that is
+        // still not a literal is evaluated by BE, which rejects a nonpositive 
gram_num itself.
+        checkGramNumValue(getArgument(2));
+    }
+
+    private static void checkGramNumValue(Expression 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).getLongValue() <= 0) {
             throw new AnalysisException(
                     "ngram_search(text,pattern,gram_num): gram_num must be a 
positive constant.");
         }
diff --git 
a/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NgramSearchTest.java
 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NgramSearchTest.java
new file mode 100644
index 00000000000..d085a0f508f
--- /dev/null
+++ 
b/fe/fe-core/src/test/java/org/apache/doris/nereids/trees/expressions/functions/scalar/NgramSearchTest.java
@@ -0,0 +1,148 @@
+// 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.trees.expressions.functions.scalar;
+
+import org.apache.doris.nereids.exceptions.AnalysisException;
+import org.apache.doris.nereids.rules.analysis.ExpressionAnalyzer;
+import org.apache.doris.nereids.rules.expression.rules.FoldConstantRuleOnFE;
+import org.apache.doris.nereids.trees.expressions.Add;
+import org.apache.doris.nereids.trees.expressions.Cast;
+import org.apache.doris.nereids.trees.expressions.Expression;
+import org.apache.doris.nereids.trees.expressions.Mod;
+import org.apache.doris.nereids.trees.expressions.SlotReference;
+import org.apache.doris.nereids.trees.expressions.Subtract;
+import org.apache.doris.nereids.trees.expressions.literal.DoubleLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.IntegerLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.NullLiteral;
+import org.apache.doris.nereids.trees.expressions.literal.StringLiteral;
+import org.apache.doris.nereids.types.IntegerType;
+import org.apache.doris.nereids.types.StringType;
+
+import com.google.common.collect.ImmutableList;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class NgramSearchTest {
+
+    @Test
+    void testLiteralGramNumber() {
+        assertFoldableGramNumber(new IntegerLiteral(3));
+    }
+
+    @Test
+    void testFoldableArithmeticGramNumber() {
+        assertFoldableGramNumber(new Add(new IntegerLiteral(1), new 
IntegerLiteral(2)));
+    }
+
+    @Test
+    void testFoldableCastGramNumber() {
+        assertFoldableGramNumber(new Cast(new StringLiteral("3"), 
IntegerType.INSTANCE));
+    }
+
+    @Test
+    void testFoldableFunctionGramNumber() {
+        assertFoldableGramNumber(new Abs(new IntegerLiteral(-3)));
+    }
+
+    @Test
+    void testConstantGramNumberBeyondFeEvaluator() {
+        // FE cannot evaluate crc32, so the constant stays unfolded and is 
left for BE to
+        // evaluate and validate; FE must not reject it as nonconstant at 
either stage.
+        Expression gram = new Add(new Mod(new Crc32(new StringLiteral("abc")), 
new IntegerLiteral(3)),
+                new IntegerLiteral(1));
+        NgramSearch analyzed = analyze(gram);
+        Assertions.assertFalse(analyzed.child(2).isLiteral());
+        Assertions.assertDoesNotThrow(analyzed::checkLegalityAfterRewrite);
+    }
+
+    @Test
+    void testAfterRewriteRejectsInvalidLiteral() {
+        NgramSearch analyzed = analyze(new IntegerLiteral(3));
+        assertAfterRewriteFails(withGramNumber(analyzed, new 
IntegerLiteral(0)),
+                "gram_num must be a positive constant");
+        assertAfterRewriteFails(withGramNumber(analyzed, new 
IntegerLiteral(-1)),
+                "gram_num must be a positive constant");
+        assertAfterRewriteFails(withGramNumber(analyzed, new 
NullLiteral(IntegerType.INSTANCE)),
+                "gram_num support const value only");
+    }
+
+    @Test
+    void testNonPositiveGramNumber() {
+        assertAnalyzeFails(new IntegerLiteral(0), "gram_num must be a positive 
constant");
+        assertAnalyzeFails(new IntegerLiteral(-1), "gram_num must be a 
positive constant");
+        assertAnalyzeFails(new Subtract(new IntegerLiteral(1), new 
IntegerLiteral(1)),
+                "gram_num must be a positive constant");
+        assertAnalyzeFails(new Subtract(new IntegerLiteral(1), new 
IntegerLiteral(2)),
+                "gram_num must be a positive constant");
+        assertAnalyzeFails(new Cast(new StringLiteral("0"), 
IntegerType.INSTANCE),
+                "gram_num must be a positive constant");
+    }
+
+    @Test
+    void testNonConstantGramNumber() {
+        SlotReference gram = SlotReference.of("gram", IntegerType.INSTANCE);
+        assertAnalyzeFails(gram, "gram_num support const value only");
+        assertAnalyzeFails(new Add(gram, new IntegerLiteral(1)), "gram_num 
support const value only");
+        assertAnalyzeFails(new Cast(new Random(), IntegerType.INSTANCE), 
"gram_num support const value only");
+    }
+
+    @Test
+    void testNonIntegerGramNumber() {
+        assertAnalyzeFails(new StringLiteral("3"), "gram_num support const 
value only");
+        assertAnalyzeFails(new DoubleLiteral(3.0), "gram_num support const 
value only");
+        assertAnalyzeFails(new NullLiteral(), "gram_num support const value 
only");
+        assertAnalyzeFails(new Cast(new NullLiteral(), IntegerType.INSTANCE), 
"gram_num support const value only");
+    }
+
+    @Test
+    void testNonConstantPattern() {
+        NgramSearch function = new NgramSearch(new StringLiteral("abc"),
+                SlotReference.of("pattern", StringType.INSTANCE), new 
IntegerLiteral(3));
+        AnalysisException exception = 
Assertions.assertThrows(AnalysisException.class,
+                () -> ExpressionAnalyzer.analyzeFunction(null, null, 
function));
+        Assertions.assertTrue(exception.getMessage().contains("pattern support 
const value only"));
+    }
+
+    private NgramSearch analyze(Expression gram) {
+        Expression analyzed = ExpressionAnalyzer.analyzeFunction(null, null,
+                new NgramSearch(new StringLiteral("abc"), new 
StringLiteral("abc"), gram));
+        return (NgramSearch) analyzed;
+    }
+
+    private NgramSearch withGramNumber(NgramSearch function, Expression gram) {
+        return function.withChildren(ImmutableList.of(function.child(0), 
function.child(1), gram));
+    }
+
+    private void assertFoldableGramNumber(Expression gram) {
+        NgramSearch analyzed = analyze(gram);
+        Expression folded = 
FoldConstantRuleOnFE.evaluateWithoutContext(analyzed);
+        Assertions.assertEquals(new IntegerLiteral(3), folded.child(2));
+        Assertions.assertDoesNotThrow(folded::checkLegalityAfterRewrite);
+    }
+
+    private void assertAnalyzeFails(Expression gram, String message) {
+        AnalysisException exception = 
Assertions.assertThrows(AnalysisException.class, () -> analyze(gram));
+        Assertions.assertTrue(exception.getMessage().contains(message), 
exception.getMessage());
+    }
+
+    private void assertAfterRewriteFails(NgramSearch function, String message) 
{
+        AnalysisException exception = 
Assertions.assertThrows(AnalysisException.class,
+                function::checkLegalityAfterRewrite);
+        Assertions.assertTrue(exception.getMessage().contains(message), 
exception.getMessage());
+    }
+}
diff --git 
a/regression-test/data/query_p0/sql_functions/string_functions/test_ngram_search_gram_num.out
 
b/regression-test/data/query_p0/sql_functions/string_functions/test_ngram_search_gram_num.out
new file mode 100644
index 00000000000..29dd047597c
--- /dev/null
+++ 
b/regression-test/data/query_p0/sql_functions/string_functions/test_ngram_search_gram_num.out
@@ -0,0 +1,73 @@
+-- This file is automatically generated. You should know what you did if you 
want to edit this
+-- !literal --
+1
+
+-- !arithmetic --
+1
+
+-- !cast --
+1
+
+-- !function --
+1
+
+-- !nested --
+1
+
+-- !null_text --
+\N
+
+-- !rows --
+1      1       1
+2      0       0.8
+3      0.3333333333333333      0.75
+4      \N      \N
+
+-- !be_function --
+1
+
+-- !be_null_gram --
+\N
+
+-- !be_rows --
+1      1       \N
+2      0.8     \N
+3      0.75    \N
+4      \N      \N
+
+-- !literal --
+1
+
+-- !arithmetic --
+1
+
+-- !cast --
+1
+
+-- !function --
+1
+
+-- !nested --
+1
+
+-- !null_text --
+\N
+
+-- !rows --
+1      1       1
+2      0       0.8
+3      0.3333333333333333      0.75
+4      \N      \N
+
+-- !be_function --
+1
+
+-- !be_null_gram --
+\N
+
+-- !be_rows --
+1      1       \N
+2      0.8     \N
+3      0.75    \N
+4      \N      \N
+
diff --git 
a/regression-test/suites/query_p0/sql_functions/string_functions/test_ngram_search_gram_num.groovy
 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_ngram_search_gram_num.groovy
new file mode 100644
index 00000000000..11b7ceed464
--- /dev/null
+++ 
b/regression-test/suites/query_p0/sql_functions/string_functions/test_ngram_search_gram_num.groovy
@@ -0,0 +1,87 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+suite("test_ngram_search_gram_num") {
+    sql "drop table if exists test_ngram_search_gram_num"
+    sql """
+        create table test_ngram_search_gram_num (
+            k1 int not null,
+            s string null
+        ) distributed by hash (k1) buckets 1
+        properties ("replication_num" = "1")
+    """
+    sql """insert into test_ngram_search_gram_num values (1, 'abc'), (2, 
'ab'), (3, 'abc1313131'), (4, null)"""
+
+    for (def foldOnBe in [false, true]) {
+        sql "set enable_fold_constant_by_be = ${foldOnBe}"
+
+        // constant expressions folded on FE are equivalent to the literal
+        qt_literal "select ngram_search('abc', 'abc', 3)"
+        qt_arithmetic "select ngram_search('abc', 'abc', 1 + 2)"
+        qt_cast "select ngram_search('abc', 'abc', cast('3' as int))"
+        qt_function "select ngram_search('abc', 'abc', abs(-3))"
+        qt_nested "select ngram_search('abc', 'abc', cast(abs(-2) + 1 as int))"
+        qt_null_text "select ngram_search(cast(null as string), 'abc', 1 + 2)"
+        order_qt_rows """
+            select k1, ngram_search(s, 'abc', 1 + 2), ngram_search(s, 'abc', 
cast('1' as int))
+            from test_ngram_search_gram_num
+        """
+
+        // constant expressions that only BE can evaluate are executed and 
validated by BE
+        qt_be_function "select ngram_search('abc', 'abc', crc32('abc') % 3 + 
1)"
+        qt_be_null_gram "select ngram_search('abc', 'abc', crc32('abc') % 0)"
+        order_qt_be_rows """
+            select k1, ngram_search(s, 'abc', crc32('abc') % 3 + 1), 
ngram_search(s, 'abc', crc32('abc') % 0)
+            from test_ngram_search_gram_num
+        """
+        for (def gram in ["crc32('abc') % 3 - 3", "crc32('abc') % 3 - 4"]) {
+            test {
+                sql "select ngram_search('abc', 'abc', ${gram})"
+                exception "gram_num must be a positive constant"
+            }
+            test {
+                sql "select k1, ngram_search(s, 'abc', ${gram}) from 
test_ngram_search_gram_num"
+                exception "gram_num must be a positive constant"
+            }
+        }
+
+        for (def gram in ["0", "-1", "1 - 1", "1 - 2", "cast('0' as int)"]) {
+            test {
+                sql "select ngram_search('abc', 'abc', ${gram})"
+                exception "gram_num must be a positive constant"
+            }
+            test {
+                sql "select ngram_search(cast(null as string), 'abc', ${gram})"
+                exception "gram_num must be a positive constant"
+            }
+            test {
+                sql "select k1, ngram_search(s, 'abc', ${gram}) from 
test_ngram_search_gram_num"
+                exception "gram_num must be a positive constant"
+            }
+        }
+        for (def gram in ["'3'", "3.5", "null", "cast(null as int)", 
"cast(rand() as int)", "k1"]) {
+            test {
+                sql "select k1, ngram_search(s, 'abc', ${gram}) from 
test_ngram_search_gram_num"
+                exception "gram_num support const value only"
+            }
+        }
+        test {
+            sql "select k1, ngram_search('abc', s, 3) from 
test_ngram_search_gram_num"
+            exception "pattern support const value only"
+        }
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to