Skip to content

Commit f1f88eb

Browse files
authored
[8.19] ESQL: Fix LIMIT NPE with null value (elastic#130914) (elastic#131029)
Manual 8.19 backport of elastic#130914
1 parent bf2063e commit f1f88eb

File tree

7 files changed

+65
-24
lines changed

7 files changed

+65
-24
lines changed

docs/changelog/130914.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pr: 130914
2+
summary: Fix LIMIT NPE with null value
3+
area: ES|QL
4+
type: bug
5+
issues:
6+
- 130908

x-pack/plugin/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ tasks.named("yamlRestTestV7CompatTransform").configure({ task ->
230230
task.skipTest("esql/191_lookup_join_on_datastreams/data streams not supported in LOOKUP JOIN", "Added support for aliases in JOINs")
231231
task.skipTest("esql/190_lookup_join/non-lookup index", "Error message changed")
232232
task.skipTest("esql/192_lookup_join_on_aliases/alias-pattern-multiple", "Error message changed")
233+
task.skipTest("esql/10_basic/Test wrong LIMIT parameter", "Error message changed")
233234
})
234235

235236

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,11 @@ public enum Cap {
956956
*/
957957
PARAMETER_FOR_LIMIT,
958958

959+
/**
960+
* Changed and normalized the LIMIT error message.
961+
*/
962+
NORMALIZED_LIMIT_ERROR_MESSAGE,
963+
959964
/**
960965
* Enable support for index aliases in lookup joins
961966
*/

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/parser/LogicalPlanBuilder.java

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -376,21 +376,22 @@ public PlanFactory visitWhereCommand(EsqlBaseParser.WhereCommandContext ctx) {
376376
public PlanFactory visitLimitCommand(EsqlBaseParser.LimitCommandContext ctx) {
377377
Source source = source(ctx);
378378
Object val = expression(ctx.constant()).fold(FoldContext.small() /* TODO remove me */);
379-
if (val instanceof Integer i) {
380-
if (i < 0) {
381-
throw new ParsingException(source, "Invalid value for LIMIT [" + i + "], expecting a non negative integer");
382-
}
379+
if (val instanceof Integer i && i >= 0) {
383380
return input -> new Limit(source, new Literal(source, i, DataType.INTEGER), input);
384-
} else {
385-
throw new ParsingException(
386-
source,
387-
"Invalid value for LIMIT ["
388-
+ BytesRefs.toString(val)
389-
+ ": "
390-
+ (expression(ctx.constant()).dataType() == KEYWORD ? "String" : val.getClass().getSimpleName())
391-
+ "], expecting a non negative integer"
392-
);
393381
}
382+
383+
String valueType = expression(ctx.constant()).dataType().typeName();
384+
385+
throw new ParsingException(
386+
source,
387+
"value of ["
388+
+ source.text()
389+
+ "] must be a non negative integer, found value ["
390+
+ ctx.constant().getText()
391+
+ "] type ["
392+
+ valueType
393+
+ "]"
394+
);
394395
}
395396

396397
@Override

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

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@
1919
import org.elasticsearch.xpack.esql.index.EsIndex;
2020
import org.elasticsearch.xpack.esql.index.IndexResolution;
2121
import org.elasticsearch.xpack.esql.parser.EsqlParser;
22+
import org.elasticsearch.xpack.esql.parser.ParserUtils;
2223
import org.elasticsearch.xpack.esql.parser.ParsingException;
24+
import org.elasticsearch.xpack.esql.parser.QueryParam;
25+
import org.elasticsearch.xpack.esql.parser.QueryParams;
2326
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
2427
import org.elasticsearch.xpack.esql.plan.logical.Row;
2528
import org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter;
@@ -153,9 +156,34 @@ public void testJoinTwiceOnTheSameField_TwoLookups() {
153156
}
154157

155158
public void testInvalidLimit() {
156-
assertEquals("1:13: Invalid value for LIMIT [foo: String], expecting a non negative integer", error("row a = 1 | limit \"foo\""));
157-
assertEquals("1:13: Invalid value for LIMIT [1.2: Double], expecting a non negative integer", error("row a = 1 | limit 1.2"));
158-
assertEquals("1:13: Invalid value for LIMIT [-1], expecting a non negative integer", error("row a = 1 | limit -1"));
159+
assertLimitWithAndWithoutParams("foo", "\"foo\"", DataType.KEYWORD);
160+
assertLimitWithAndWithoutParams(1.2, "1.2", DataType.DOUBLE);
161+
assertLimitWithAndWithoutParams(-1, "-1", DataType.INTEGER);
162+
assertLimitWithAndWithoutParams(true, "true", DataType.BOOLEAN);
163+
assertLimitWithAndWithoutParams(false, "false", DataType.BOOLEAN);
164+
assertLimitWithAndWithoutParams(null, "null", DataType.NULL);
165+
}
166+
167+
private void assertLimitWithAndWithoutParams(Object value, String valueText, DataType type) {
168+
assertEquals(
169+
"1:13: value of [limit "
170+
+ valueText
171+
+ "] must be a non negative integer, found value ["
172+
+ valueText
173+
+ "] type ["
174+
+ type.typeName()
175+
+ "]",
176+
error("row a = 1 | limit " + valueText)
177+
);
178+
179+
assertEquals(
180+
"1:13: value of [limit ?param] must be a non negative integer, found value [?param] type [" + type.typeName() + "]",
181+
error(
182+
"row a = 1 | limit ?param",
183+
new QueryParams(List.of(new QueryParam("param", value, type, ParserUtils.ParamClassification.VALUE)))
184+
)
185+
);
186+
159187
}
160188

161189
public void testInvalidSample() {
@@ -177,13 +205,17 @@ public void testInvalidSample() {
177205
);
178206
}
179207

180-
private String error(String query) {
181-
ParsingException e = expectThrows(ParsingException.class, () -> defaultAnalyzer.analyze(parser.createStatement(query)));
208+
private String error(String query, QueryParams params) {
209+
ParsingException e = expectThrows(ParsingException.class, () -> defaultAnalyzer.analyze(parser.createStatement(query, params)));
182210
String message = e.getMessage();
183211
assertTrue(message.startsWith("line "));
184212
return message.substring("line ".length());
185213
}
186214

215+
private String error(String query) {
216+
return error(query, new QueryParams());
217+
}
218+
187219
private static IndexResolution loadIndexResolution(String name) {
188220
return IndexResolution.valid(new EsIndex(INDEX_NAME, LoadMapping.loadMapping(name)));
189221
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -986,10 +986,6 @@ public void testBasicLimitCommand() {
986986
assertThat(limit.children().get(0).children().get(0), instanceOf(UnresolvedRelation.class));
987987
}
988988

989-
public void testLimitConstraints() {
990-
expectError("from text | limit -1", "line 1:13: Invalid value for LIMIT [-1], expecting a non negative integer");
991-
}
992-
993989
public void testBasicSortCommand() {
994990
LogicalPlan plan = statement("from text | where true | sort a+b asc nulls first, x desc nulls last | sort y asc | sort z desc");
995991
assertThat(plan, instanceOf(OrderBy.class));

x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/10_basic.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -555,11 +555,11 @@ FROM EVAL SORT LIMIT with documents_found:
555555
- method: POST
556556
path: /_query
557557
parameters: [ ]
558-
capabilities: [ parameter_for_limit ]
558+
capabilities: [ normalized_limit_error_message ]
559559
reason: "named or positional parameters for field names"
560560

561561
- do:
562-
catch: "/Invalid value for LIMIT \\[foo: String\\], expecting a non negative integer/"
562+
catch: "/value of \\[limit \\?l\\] must be a non negative integer, found value \\[\\?l\\] type \\[keyword\\]/"
563563
esql.query:
564564
body:
565565
query: 'from test | limit ?l'

0 commit comments

Comments
 (0)