From 338ddfa8beab5c1d487629c3a547c716a11e42bd Mon Sep 17 00:00:00 2001 From: carlosdelest Date: Fri, 17 Jan 2025 11:10:31 +0100 Subject: [PATCH] Fix merge --- .../xpack/esql/action/EsqlCapabilities.java | 123 ++++- .../expression/function/fulltext/Match.java | 6 +- .../function/fulltext/MatchErrorTests.java | 2 +- .../esql/parser/StatementParserTests.java | 498 ------------------ 4 files changed, 125 insertions(+), 504 deletions(-) diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java index 1d02005765a71..12e0edf9f6b29 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/action/EsqlCapabilities.java @@ -27,6 +27,113 @@ */ public class EsqlCapabilities { public enum Cap { + /** + * Introduction of {@code MV_SORT}, {@code MV_SLICE}, and {@code MV_ZIP}. + * Added in #106095. + */ + MV_SORT, + + /** + * When we disabled some broken optimizations around {@code nullable}. + * Fixed in #105691. + */ + DISABLE_NULLABLE_OPTS, + + /** + * Introduction of {@code ST_X} and {@code ST_Y}. Added in #105768. + */ + ST_X_Y, + + /** + * Changed precision of {@code geo_point} and {@code cartesian_point} fields, by loading from source into WKB. Done in #103691. + */ + SPATIAL_POINTS_FROM_SOURCE, + + /** + * Support for loading {@code geo_shape} and {@code cartesian_shape} fields. Done in #104269. + */ + SPATIAL_SHAPES, + + /** + * Support for spatial aggregation {@code ST_CENTROID}. Done in #104269. + */ + ST_CENTROID_AGG, + + /** + * Support for spatial aggregation {@code ST_INTERSECTS}. Done in #104907. + */ + ST_INTERSECTS, + + /** + * Support for spatial aggregation {@code ST_CONTAINS} and {@code ST_WITHIN}. Done in #106503. + */ + ST_CONTAINS_WITHIN, + + /** + * Support for spatial aggregation {@code ST_DISJOINT}. Done in #107007. + */ + ST_DISJOINT, + + /** + * The introduction of the {@code VALUES} agg. + */ + AGG_VALUES, + + /** + * Does ESQL support async queries. + */ + ASYNC_QUERY, + + /** + * Does ESQL support FROM OPTIONS? + */ + @Deprecated + FROM_OPTIONS, + + /** + * Cast string literals to a desired data type. + */ + STRING_LITERAL_AUTO_CASTING, + + /** + * Base64 encoding and decoding functions. + */ + BASE64_DECODE_ENCODE, + + /** + * Support for the :: casting operator + */ + CASTING_OPERATOR, + + /** + * Blocks can be labelled with {@link org.elasticsearch.compute.data.Block.MvOrdering#SORTED_ASCENDING} for optimizations. + */ + MV_ORDERING_SORTED_ASCENDING, + + /** + * Support for metrics counter fields + */ + METRICS_COUNTER_FIELDS, + + /** + * Cast string literals to a desired data type for IN predicate and more types for BinaryComparison. + */ + STRING_LITERAL_AUTO_CASTING_EXTENDED, + + /** + * Support for metadata fields. + */ + METADATA_FIELDS, + + /** + * Support for timespan units abbreviations + */ + TIMESPAN_ABBREVIATIONS, + + /** + * Support metrics counter types + */ + COUNTER_TYPES, /** * Support for function {@code BIT_LENGTH}. Done in #115792 @@ -189,9 +296,12 @@ public enum Cap { */ ST_DISTANCE, - /** Support for function {@code ST_EXTENT}. */ + /** Support for function {@code ST_EXTENT_AGG}. */ ST_EXTENT_AGG, + /** Optimization of ST_EXTENT_AGG with doc-values as IntBlock. */ + ST_EXTENT_AGG_DOCVALUES, + /** * Fix determination of CRS types in spatial functions when folding. */ @@ -380,6 +490,15 @@ public enum Cap { */ DATE_NANOS_AGGREGATIONS(), + /** + * Support the {@link org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.In} operator for date nanos + */ + DATE_NANOS_IN_OPERATOR(), + /** + * Support running date format function on nanosecond dates + */ + DATE_NANOS_DATE_FORMAT(), + /** * DATE_PARSE supports reading timezones */ @@ -564,7 +683,7 @@ public enum Cap { /** * LOOKUP JOIN */ - JOIN_LOOKUP_V10(Build.current().isSnapshot()), + JOIN_LOOKUP_V11(Build.current().isSnapshot()), /** * Fix for https://github.com/elastic/elasticsearch/issues/117054 diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Match.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Match.java index f49887d755b69..48b26e92eaed4 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Match.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Match.java @@ -22,6 +22,7 @@ import org.elasticsearch.xpack.esql.core.expression.Expression; import org.elasticsearch.xpack.esql.core.expression.FieldAttribute; import org.elasticsearch.xpack.esql.core.expression.FoldContext; +import org.elasticsearch.xpack.esql.core.expression.MapExpression; import org.elasticsearch.xpack.esql.core.expression.TypeResolutions; import org.elasticsearch.xpack.esql.core.planner.ExpressionTranslator; import org.elasticsearch.xpack.esql.core.querydsl.query.QueryStringQuery; @@ -34,7 +35,6 @@ import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.MapParam; import org.elasticsearch.xpack.esql.expression.function.OptionalArgument; -import org.elasticsearch.xpack.esql.expression.function.OptionalArgument; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.expression.function.scalar.convert.AbstractConvertFunction; import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; @@ -262,8 +262,8 @@ private Map parseOptions() throws InvalidArgumentException { if (resolution.unresolved()) { throw new InvalidArgumentException(resolution.message()); } - Object optionExprFold = optionExpr.fold(); - Object valueExprFold = valueExpr.fold(); + Object optionExprFold = optionExpr.fold(FoldContext.small()); + Object valueExprFold = valueExpr.fold(FoldContext.small()); String optionName = optionExprFold instanceof BytesRef br ? br.utf8ToString() : optionExprFold.toString(); String optionValue = valueExprFold instanceof BytesRef br ? br.utf8ToString() : valueExprFold.toString(); // validate the optionExpr is supported diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MatchErrorTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MatchErrorTests.java index 1f4e8e40a8259..c2854a9dbf138 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MatchErrorTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MatchErrorTests.java @@ -31,7 +31,7 @@ protected List cases() { @Override protected Expression build(Source source, List args) { - return new Match(source, args.get(0), args.get(1)); + return new Match(source, args.get(0), args.get(1), null); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java index c0f7890c8dd0e..74d97f250ef7f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/parser/StatementParserTests.java @@ -2939,502 +2939,4 @@ public void testNamedFunctionArgumentWithUnsupportedNamedParameterTypes() { ); } } - - public void testNamedFunctionArgumentInMap() { - assumeTrue( - "named function arguments require snapshot build", - EsqlCapabilities.Cap.OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION.isEnabled() - ); - // functions can be scalar, grouping and aggregation - // functions can be in eval/where/stats/sort/dissect/grok commands, commands in snapshot are not covered - // positive - // In eval and where clause as function arguments - LinkedHashMap expectedMap1 = new LinkedHashMap<>(2); - expectedMap1.put("option1", "string"); - expectedMap1.put("option2", 1); - expectedMap1.put("option3", List.of(2.0, 3.0, 4.0)); - expectedMap1.put("option4", List.of(true, false)); - LinkedHashMap expectedMap2 = new LinkedHashMap<>(2); - expectedMap2.put("option1", List.of("string1", "string2")); - expectedMap2.put("option2", List.of(1, 2, 3)); - expectedMap2.put("option3", 2.0); - expectedMap2.put("option4", true); - LinkedHashMap expectedMap3 = new LinkedHashMap<>(2); - expectedMap3.put("option1", "string"); - expectedMap3.put("option2", 2.0); - expectedMap3.put("option3", List.of(1, 2, 3)); - expectedMap3.put("option4", List.of(true, false)); - LinkedHashMap expectedMap4 = new LinkedHashMap<>(2); - expectedMap4.put("option1", 1); - expectedMap4.put("option2", true); - expectedMap4.put("option3", List.of("string1", "string2")); - expectedMap4.put("option4", List.of(2.0, 3.0, 4.0)); - - assertEquals( - new Filter( - EMPTY, - new Eval( - EMPTY, - relation("test"), - List.of( - new Alias( - EMPTY, - "x", - function( - "fn1", - List.of(attribute("f1"), new Literal(EMPTY, "testString", KEYWORD), mapExpression(expectedMap1)) - ) - ) - ) - ), - new Equals( - EMPTY, - attribute("y"), - function("fn2", List.of(new Literal(EMPTY, "testString", KEYWORD), mapExpression(expectedMap2))) - ) - ), - statement(""" - from test - | eval x = fn1(f1, "testString", {"option1":"string","option2":1,"option3":[2.0,3.0,4.0],"option4":[true,false]}) - | where y == fn2("testString", {"option1":["string1","string2"],"option2":[1,2,3],"option3":2.0,"option4":true}) - """) - ); - - // In stats, by and sort as function arguments - assertEquals( - new OrderBy( - EMPTY, - new Aggregate( - EMPTY, - relation("test"), - Aggregate.AggregateType.STANDARD, - List.of( - new Alias( - EMPTY, - "fn2(f3, {\"option1\":[\"string1\",\"string2\"],\"option2\":[1,2,3],\"option3\":2.0,\"option4\":true})", - function("fn2", List.of(attribute("f3"), mapExpression(expectedMap2))) - ) - ), - List.of( - new Alias(EMPTY, "x", function("fn1", List.of(attribute("f1"), attribute("f2"), mapExpression(expectedMap1)))), - attribute("fn2(f3, {\"option1\":[\"string1\",\"string2\"],\"option2\":[1,2,3],\"option3\":2.0,\"option4\":true})") - ) - ), - List.of( - new Order( - EMPTY, - function("fn3", List.of(attribute("f4"), mapExpression(expectedMap3))), - Order.OrderDirection.ASC, - Order.NullsPosition.LAST - ) - ) - ), - statement(""" - from test - | stats x = fn1(f1, f2, {"option1":"string","option2":1,"option3":[2.0,3.0,4.0],"option4":[true,false]}) - by fn2(f3, {"option1":["string1","string2"],"option2":[1,2,3],"option3":2.0,"option4":true}) - | sort fn3(f4, {"option1":"string","option2":2.0,"option3":[1,2,3],"option4":[true,false]}) - """) - ); - - // In dissect and grok as function arguments - LogicalPlan plan = statement(""" - from test - | dissect fn1(f1, f2, {"option1":"string", "option2":1,"option3":[2.0,3.0,4.0],"option4":[true,false]}) "%{bar}" - | grok fn2(f3, {"option1":["string1","string2"],"option2":[1,2,3],"option3":2.0,"option4":true}) "%{WORD:foo}" - """); - Grok grok = as(plan, Grok.class); - assertEquals(function("fn2", List.of(attribute("f3"), mapExpression(expectedMap2))), grok.input()); - assertEquals("%{WORD:foo}", grok.parser().pattern()); - assertEquals(List.of(referenceAttribute("foo", KEYWORD)), grok.extractedFields()); - Dissect dissect = as(grok.child(), Dissect.class); - assertEquals(function("fn1", List.of(attribute("f1"), attribute("f2"), mapExpression(expectedMap1))), dissect.input()); - assertEquals("%{bar}", dissect.parser().pattern()); - assertEquals("", dissect.parser().appendSeparator()); - assertEquals(List.of(referenceAttribute("bar", KEYWORD)), dissect.extractedFields()); - UnresolvedRelation ur = as(dissect.child(), UnresolvedRelation.class); - assertEquals(ur, relation("test")); - - // map entry values provided in named parameter, arrays are not supported by named parameters yet - assertEquals( - new Filter( - EMPTY, - new Eval( - EMPTY, - relation("test"), - List.of( - new Alias( - EMPTY, - "x", - function( - "fn1", - List.of(attribute("f1"), new Literal(EMPTY, "testString", KEYWORD), mapExpression(expectedMap1)) - ) - ) - ) - ), - new Equals( - EMPTY, - attribute("y"), - function("fn2", List.of(new Literal(EMPTY, "testString", KEYWORD), mapExpression(expectedMap2))) - ) - ), - statement( - """ - from test - | eval x = ?fn1(?n1, ?n2, {"option1":?n3,"option2":?n4,"option3":[2.0,3.0,4.0],"option4":[true,false]}) - | where y == ?fn2(?n2, {"option1":["string1","string2"],"option2":[1,2,3],"option3":?n5,"option4":?n6}) - """, - new QueryParams( - List.of( - paramAsIdentifier("fn1", "fn1"), - paramAsIdentifier("fn2", "fn2"), - paramAsIdentifier("n1", "f1"), - paramAsConstant("n2", "testString"), - paramAsConstant("n3", "string"), - paramAsConstant("n4", 1), - paramAsConstant("n5", 2.0), - paramAsConstant("n6", true) - ) - ) - ) - ); - - assertEquals( - new OrderBy( - EMPTY, - new Aggregate( - EMPTY, - relation("test"), - Aggregate.AggregateType.STANDARD, - List.of( - new Alias( - EMPTY, - "?fn2(?n7, {\"option1\":[\"string1\",\"string2\"],\"option2\":[1,2,3],\"option3\":?n5,\"option4\":?n6})", - function("fn2", List.of(attribute("f3"), mapExpression(expectedMap2))) - ) - ), - List.of( - new Alias(EMPTY, "x", function("fn1", List.of(attribute("f1"), attribute("f2"), mapExpression(expectedMap1)))), - attribute("?fn2(?n7, {\"option1\":[\"string1\",\"string2\"],\"option2\":[1,2,3],\"option3\":?n5,\"option4\":?n6})") - ) - ), - List.of( - new Order( - EMPTY, - function("fn3", List.of(attribute("f4"), mapExpression(expectedMap3))), - Order.OrderDirection.ASC, - Order.NullsPosition.LAST - ) - ) - ), - statement( - """ - from test - | stats x = ?fn1(?n1, ?n2, {"option1":?n3,"option2":?n4,"option3":[2.0,3.0,4.0],"option4":[true,false]}) - by ?fn2(?n7, {"option1":["string1","string2"],"option2":[1,2,3],"option3":?n5,"option4":?n6}) - | sort ?fn3(?n8, {"option1":?n3,"option2":?n5,"option3":[1,2,3],"option4":[true,false]}) - """, - new QueryParams( - List.of( - paramAsIdentifier("fn1", "fn1"), - paramAsIdentifier("fn2", "fn2"), - paramAsIdentifier("fn3", "fn3"), - paramAsIdentifier("n1", "f1"), - paramAsIdentifier("n2", "f2"), - paramAsConstant("n3", "string"), - paramAsConstant("n4", 1), - paramAsConstant("n5", 2.0), - paramAsConstant("n6", true), - paramAsIdentifier("n7", "f3"), - paramAsIdentifier("n8", "f4") - ) - ) - ) - ); - - plan = statement( - """ - from test - | dissect ?fn1(?n1, ?n2, {"option1":?n3,"option2":?n4,"option3":[2.0,3.0,4.0],"option4":[true,false]}) "%{bar}" - | grok ?fn2(?n7, {"option1":["string1","string2"],"option2":[1,2,3],"option3":?n5,"option4":?n6}) "%{WORD:foo}" - """, - new QueryParams( - List.of( - paramAsIdentifier("fn1", "fn1"), - paramAsIdentifier("fn2", "fn2"), - paramAsIdentifier("n1", "f1"), - paramAsIdentifier("n2", "f2"), - paramAsConstant("n3", "string"), - paramAsConstant("n4", 1), - paramAsConstant("n5", 2.0), - paramAsConstant("n6", true), - paramAsIdentifier("n7", "f3") - ) - ) - ); - grok = as(plan, Grok.class); - assertEquals(function("fn2", List.of(attribute("f3"), mapExpression(expectedMap2))), grok.input()); - assertEquals("%{WORD:foo}", grok.parser().pattern()); - assertEquals(List.of(referenceAttribute("foo", KEYWORD)), grok.extractedFields()); - dissect = as(grok.child(), Dissect.class); - assertEquals(function("fn1", List.of(attribute("f1"), attribute("f2"), mapExpression(expectedMap1))), dissect.input()); - assertEquals("%{bar}", dissect.parser().pattern()); - assertEquals("", dissect.parser().appendSeparator()); - assertEquals(List.of(referenceAttribute("bar", KEYWORD)), dissect.extractedFields()); - ur = as(dissect.child(), UnresolvedRelation.class); - assertEquals(ur, relation("test")); - } - - public void testMultipleNamedFunctionArgumentsNotAllowed() { - assumeTrue( - "named function arguments require snapshot build", - EsqlCapabilities.Cap.OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION.isEnabled() - ); - Map commands = Map.ofEntries( - Map.entry("eval x = {}", "41"), - Map.entry("where {}", "38"), - Map.entry("stats {}", "38"), - Map.entry("stats agg() by {}", "47"), - Map.entry("sort {}", "37"), - Map.entry("dissect {} \"%{bar}\"", "40"), - Map.entry("grok {} \"%{WORD:foo}\"", "37") - ); - - for (Map.Entry command : commands.entrySet()) { - String cmd = command.getKey(); - String error = command.getValue(); - String errorMessage = cmd.startsWith("dissect") || cmd.startsWith("grok") - ? "mismatched input ',' expecting ')'" - : "no viable alternative at input 'fn(f1,"; - expectError( - LoggerMessageFormat.format(null, "from test | " + cmd, "fn(f1, {\"option\":1}, {\"option\":2})"), - LoggerMessageFormat.format(null, "line 1:{}: {}", error, errorMessage) - ); - } - } - - public void testNamedFunctionArgumentNotInMap() { - assumeTrue( - "named function arguments require snapshot build", - EsqlCapabilities.Cap.OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION.isEnabled() - ); - Map commands = Map.ofEntries( - Map.entry("eval x = {}", "38"), - Map.entry("where {}", "35"), - Map.entry("stats {}", "35"), - Map.entry("stats agg() by {}", "44"), - Map.entry("sort {}", "34"), - Map.entry("dissect {} \"%{bar}\"", "37"), - Map.entry("grok {} \"%{WORD:foo}\"", "34") - ); - - for (Map.Entry command : commands.entrySet()) { - String cmd = command.getKey(); - String error = command.getValue(); - String errorMessage = cmd.startsWith("dissect") || cmd.startsWith("grok") - ? "extraneous input ':' expecting {',', ')'}" - : "no viable alternative at input 'fn(f1, \"option1\":'"; - expectError( - LoggerMessageFormat.format(null, "from test | " + cmd, "fn(f1, \"option1\":\"string\")"), - LoggerMessageFormat.format(null, "line 1:{}: {}", error, errorMessage) - ); - } - } - - public void testNamedFunctionArgumentNotConstant() { - assumeTrue( - "named function arguments require snapshot build", - EsqlCapabilities.Cap.OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION.isEnabled() - ); - Map commands = Map.ofEntries( - Map.entry("eval x = {}", new String[] { "31", "35" }), - Map.entry("where {}", new String[] { "28", "32" }), - Map.entry("stats {}", new String[] { "28", "32" }), - Map.entry("stats agg() by {}", new String[] { "37", "41" }), - Map.entry("sort {}", new String[] { "27", "31" }), - Map.entry("dissect {} \"%{bar}\"", new String[] { "30", "34" }), - Map.entry("grok {} \"%{WORD:foo}\"", new String[] { "27", "31" }) - ); - - for (Map.Entry command : commands.entrySet()) { - String cmd = command.getKey(); - String error1 = command.getValue()[0]; - String error2 = command.getValue()[1]; - String errorMessage1 = cmd.startsWith("dissect") || cmd.startsWith("grok") - ? "mismatched input '1' expecting QUOTED_STRING" - : "no viable alternative at input 'fn(f1, { 1'"; - String errorMessage2 = cmd.startsWith("dissect") || cmd.startsWith("grok") - ? "mismatched input 'string' expecting {QUOTED_STRING" - : "no viable alternative at input 'fn(f1, {"; - expectError( - LoggerMessageFormat.format(null, "from test | " + cmd, "fn(f1, { 1:\"string\" })"), - LoggerMessageFormat.format(null, "line 1:{}: {}", error1, errorMessage1) - ); - expectError( - LoggerMessageFormat.format(null, "from test | " + cmd, "fn(f1, { \"1\":string })"), - LoggerMessageFormat.format(null, "line 1:{}: {}", error2, errorMessage2) - ); - } - } - - public void testNamedFunctionArgumentEmptyMap() { - assumeTrue( - "named function arguments require snapshot build", - EsqlCapabilities.Cap.OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION.isEnabled() - ); - Map commands = Map.ofEntries( - Map.entry("eval x = {}", "30"), - Map.entry("where {}", "27"), - Map.entry("stats {}", "27"), - Map.entry("stats agg() by {}", "36"), - Map.entry("sort {}", "26"), - Map.entry("dissect {} \"%{bar}\"", "29"), - Map.entry("grok {} \"%{WORD:foo}\"", "26") - ); - - for (Map.Entry command : commands.entrySet()) { - String cmd = command.getKey(); - String error = command.getValue(); - String errorMessage = cmd.startsWith("dissect") || cmd.startsWith("grok") - ? "mismatched input '}' expecting QUOTED_STRING" - : "no viable alternative at input 'fn(f1, {}'"; - expectError( - LoggerMessageFormat.format(null, "from test | " + cmd, "fn(f1, {}})"), - LoggerMessageFormat.format(null, "line 1:{}: {}", error, errorMessage) - ); - } - } - - public void testNamedFunctionArgumentMapWithNULL() { - assumeTrue( - "named function arguments require snapshot build", - EsqlCapabilities.Cap.OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION.isEnabled() - ); - Map commands = Map.ofEntries( - Map.entry("eval x = {}", "29"), - Map.entry("where {}", "26"), - Map.entry("stats {}", "26"), - Map.entry("stats agg() by {}", "35"), - Map.entry("sort {}", "25"), - Map.entry("dissect {} \"%{bar}\"", "28"), - Map.entry("grok {} \"%{WORD:foo}\"", "25") - ); - - for (Map.Entry command : commands.entrySet()) { - String cmd = command.getKey(); - String error = command.getValue(); - expectError( - LoggerMessageFormat.format(null, "from test | " + cmd, "fn(f1, {\"option\":null})"), - LoggerMessageFormat.format( - null, - "line 1:{}: {}", - error, - "Invalid named function argument [\"option\":null], NULL is not supported" - ) - ); - } - } - - public void testNamedFunctionArgumentMapWithEmptyKey() { - assumeTrue( - "named function arguments require snapshot build", - EsqlCapabilities.Cap.OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION.isEnabled() - ); - Map commands = Map.ofEntries( - Map.entry("eval x = {}", "29"), - Map.entry("where {}", "26"), - Map.entry("stats {}", "26"), - Map.entry("stats agg() by {}", "35"), - Map.entry("sort {}", "25"), - Map.entry("dissect {} \"%{bar}\"", "28"), - Map.entry("grok {} \"%{WORD:foo}\"", "25") - ); - - for (Map.Entry command : commands.entrySet()) { - String cmd = command.getKey(); - String error = command.getValue(); - expectError( - LoggerMessageFormat.format(null, "from test | " + cmd, "fn(f1, {\"\":1})"), - LoggerMessageFormat.format( - null, - "line 1:{}: {}", - error, - "Invalid named function argument [\"\":1], empty key is not supported" - ) - ); - expectError( - LoggerMessageFormat.format(null, "from test | " + cmd, "fn(f1, {\" \":1})"), - LoggerMessageFormat.format( - null, - "line 1:{}: {}", - error, - "Invalid named function argument [\" \":1], empty key is not supported" - ) - ); - } - } - - public void testNamedFunctionArgumentMapWithDuplicatedKey() { - assumeTrue( - "named function arguments require snapshot build", - EsqlCapabilities.Cap.OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION.isEnabled() - ); - Map commands = Map.ofEntries( - Map.entry("eval x = {}", "29"), - Map.entry("where {}", "26"), - Map.entry("stats {}", "26"), - Map.entry("stats agg() by {}", "35"), - Map.entry("sort {}", "25"), - Map.entry("dissect {} \"%{bar}\"", "28"), - Map.entry("grok {} \"%{WORD:foo}\"", "25") - ); - - for (Map.Entry command : commands.entrySet()) { - String cmd = command.getKey(); - String error = command.getValue(); - expectError( - LoggerMessageFormat.format(null, "from test | " + cmd, "fn(f1, {\"dup\":1,\"dup\":2})"), - LoggerMessageFormat.format( - null, - "line 1:{}: {}", - error, - "Duplicated function arguments with the same name [dup] is not supported" - ) - ); - } - } - - public void testNamedFunctionArgumentInInvalidPositions() { - assumeTrue( - "named function arguments require snapshot build", - EsqlCapabilities.Cap.OPTIONAL_NAMED_ARGUMENT_MAP_FOR_FUNCTION.isEnabled() - ); - // negative, named arguments are not supported outside of a functionExpression where booleanExpression or indexPattern is supported - String map = "{\"option1\":\"string\", \"option2\":1}"; - - Map commands = Map.ofEntries( - Map.entry("from {}", "line 1:7: mismatched input '\"option1\"' expecting {, '|', ',', OPENING_BRACKET, 'metadata'}"), - Map.entry("row x = {}", "line 1:9: extraneous input '{' expecting {QUOTED_STRING, INTEGER_LITERAL"), - Map.entry("eval x = {}", "line 1:22: extraneous input '{' expecting {QUOTED_STRING, INTEGER_LITERAL"), - Map.entry("where x > {}", "line 1:23: no viable alternative at input 'x > {'"), - Map.entry("stats agg() by {}", "line 1:28: extraneous input '{' expecting {QUOTED_STRING, INTEGER_LITERAL"), - Map.entry("sort {}", "line 1:18: extraneous input '{' expecting {QUOTED_STRING, INTEGER_LITERAL"), - Map.entry("keep {}", "line 1:18: token recognition error at: '{'"), - Map.entry("drop {}", "line 1:18: token recognition error at: '{'"), - Map.entry("rename a as {}", "line 1:25: token recognition error at: '{'"), - Map.entry("mv_expand {}", "line 1:23: token recognition error at: '{'"), - Map.entry("limit {}", "line 1:19: mismatched input '{' expecting INTEGER_LITERAL"), - Map.entry("enrich idx2 on f1 with f2 = {}", "line 1:41: token recognition error at: '{'"), - Map.entry("dissect {} \"%{bar}\"", "line 1:21: extraneous input '{' expecting {QUOTED_STRING, INTEGER_LITERAL"), - Map.entry("grok {} \"%{WORD:foo}\"", "line 1:18: extraneous input '{' expecting {QUOTED_STRING, INTEGER_LITERAL") - ); - - for (Map.Entry command : commands.entrySet()) { - String cmd = command.getKey(); - String errorMessage = command.getValue(); - String from = cmd.startsWith("row") || cmd.startsWith("from") ? "" : "from test | "; - expectError(LoggerMessageFormat.format(null, from + cmd, map), errorMessage); - } - } }