Skip to content

Commit 4b97e6d

Browse files
committed
Add QueryStringFunctionTests, change folding checks
1 parent c9d4741 commit 4b97e6d

3 files changed

Lines changed: 80 additions & 10 deletions

File tree

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/FullTextFunction.java

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424

2525
import static java.util.Collections.singletonList;
2626
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT;
27+
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNullAndFoldable;
2728
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString;
2829

2930
/**
@@ -62,13 +63,7 @@ protected TypeResolution resolveType() {
6263
return new TypeResolution("Unresolved children");
6364
}
6465

65-
if (query().foldable() == false) {
66-
return new TypeResolution(
67-
"Query in " + functionName() + " function needs to be statically resolved. References to fields are not allowed."
68-
);
69-
}
70-
71-
return isString(query(), sourceText(), DEFAULT);
66+
return isString(query(), sourceText(), DEFAULT).and(isNotNullAndFoldable(query(), functionName(), DEFAULT));
7267
}
7368

7469
public Expression query() {

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/analysis/VerifierTests.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,13 +1162,15 @@ public void testQueryStringFunctionsOnlyAllowedInWhere() throws Exception {
11621162
);
11631163
}
11641164

1165-
public void testQueryStringFunctionOperands() throws Exception {
1165+
public void testQueryStringFunctionArgNotNullOrConstant() throws Exception {
11661166
assumeTrue("skipping because QSTR is not enabled", EsqlCapabilities.Cap.QSTR_FUNCTION.isEnabled());
1167-
assertEquals("1:19: argument of [qstr(10)] must be [string], found value [10] type [integer]", error("from test | where qstr(10)"));
1167+
11681168
assertEquals(
1169-
"1:19: Query in QSTR function needs to be statically resolved. References to fields are not allowed.",
1169+
"1:19: argument of [QSTR] must be a constant, received [first_name]",
11701170
error("from test | where qstr(first_name)")
11711171
);
1172+
assertEquals("1:19: argument of [QSTR] cannot be null, received [null]", error("from test | where qstr(null)"));
1173+
// Other value types are tested in QueryStringFunctionTests
11721174
}
11731175

11741176
public void testCoalesceWithMixedNumericTypes() {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.esql.expression.function.fulltext;
9+
10+
import com.carrotsearch.randomizedtesting.annotations.Name;
11+
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
12+
13+
import org.apache.lucene.util.BytesRef;
14+
import org.elasticsearch.xpack.esql.core.expression.Expression;
15+
import org.elasticsearch.xpack.esql.core.tree.Source;
16+
import org.elasticsearch.xpack.esql.core.type.DataType;
17+
import org.elasticsearch.xpack.esql.expression.function.AbstractFunctionTestCase;
18+
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
19+
import org.hamcrest.Matcher;
20+
21+
import java.util.Arrays;
22+
import java.util.LinkedList;
23+
import java.util.List;
24+
import java.util.function.Supplier;
25+
26+
import static org.hamcrest.Matchers.equalTo;
27+
28+
public class QueryStringFunctionTests extends AbstractFunctionTestCase {
29+
30+
public QueryStringFunctionTests(@Name("TestCase") Supplier<TestCaseSupplier.TestCase> testCaseSupplier) {
31+
this.testCase = testCaseSupplier.get();
32+
}
33+
34+
@ParametersFactory
35+
public static Iterable<Object[]> parameters() {
36+
List<TestCaseSupplier> suppliers = new LinkedList<>();
37+
for (DataType strType : Arrays.stream(DataType.values()).filter(DataType::isString).toList()) {
38+
suppliers.add(
39+
new TestCaseSupplier(
40+
"<" + strType + ">",
41+
List.of(strType),
42+
() -> testCase(strType, randomAlphaOfLengthBetween(1, 10), equalTo(true))
43+
)
44+
);
45+
}
46+
List<TestCaseSupplier> errorsSuppliers = errorsForCasesWithoutExamples(suppliers, (v, p) -> "string");
47+
// Don't test null, as it is not allowed but the expected message is not a type error - so we check it separately in VerifierTests
48+
return parameterSuppliersFromTypedData(errorsSuppliers.stream().filter(s -> s.types().contains(DataType.NULL) == false).toList());
49+
}
50+
51+
public final void testFold() {
52+
Expression expression = buildLiteralExpression(testCase);
53+
if (testCase.getExpectedTypeError() != null) {
54+
assertTypeResolutionFailure(expression);
55+
return;
56+
}
57+
assertFalse("expected resolved", expression.typeResolved().unresolved());
58+
}
59+
60+
private static TestCaseSupplier.TestCase testCase(DataType strType, String str, Matcher<Boolean> matcher) {
61+
return new TestCaseSupplier.TestCase(
62+
List.of(new TestCaseSupplier.TypedData(new BytesRef(str), strType, "query")),
63+
"EndsWithEvaluator[str=Attribute[channel=0], suffix=Attribute[channel=1]]",
64+
DataType.BOOLEAN,
65+
matcher
66+
);
67+
}
68+
69+
@Override
70+
protected Expression build(Source source, List<Expression> args) {
71+
return new QueryStringFunction(source, args.get(0));
72+
}
73+
}

0 commit comments

Comments
 (0)