diff --git a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/Foldables.java b/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/Foldables.java deleted file mode 100644 index 4052d41d7eb5f..0000000000000 --- a/x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/Foldables.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one - * or more contributor license agreements. Licensed under the Elastic License - * 2.0; you may not use this file except in compliance with the Elastic License - * 2.0. - */ -package org.elasticsearch.xpack.esql.core.expression; - -import org.apache.lucene.util.BytesRef; -import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException; - -public abstract class Foldables { - - public static Object valueOf(FoldContext ctx, Expression e) { - if (e.foldable()) { - return e.fold(ctx); - } - throw new QlIllegalArgumentException("Cannot determine value for {}", e); - } - - public static String stringLiteralValueOf(Expression expression, String message) { - if (expression instanceof Literal literal && literal.value() instanceof BytesRef bytesRef) { - return bytesRef.utf8ToString(); - } - throw new QlIllegalArgumentException(message); - } -} diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichPolicyResolver.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichPolicyResolver.java index ee68de83ad67e..20923a04d976d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichPolicyResolver.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/enrich/EnrichPolicyResolver.java @@ -59,7 +59,7 @@ import java.util.Set; import java.util.stream.Collectors; -import static org.elasticsearch.xpack.esql.core.expression.Foldables.stringLiteralValueOf; +import static org.elasticsearch.xpack.esql.expression.Foldables.stringLiteralValueOf; import static org.elasticsearch.xpack.esql.session.EsqlCCSUtils.markClusterWithFinalStateAndNoShards; /** diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionUtils.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/Foldables.java similarity index 80% rename from x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionUtils.java rename to x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/Foldables.java index bbde965bc927f..77e61869694b5 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/FunctionUtils.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/Foldables.java @@ -4,20 +4,24 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -package org.elasticsearch.xpack.esql.expression.function; +package org.elasticsearch.xpack.esql.expression; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.core.Nullable; +import org.elasticsearch.core.Strings; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.common.Failure; import org.elasticsearch.xpack.esql.common.Failures; +import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException; import org.elasticsearch.xpack.esql.core.expression.Expression; +import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.Literal; import static org.elasticsearch.common.logging.LoggerMessageFormat.format; import static org.elasticsearch.xpack.esql.common.Failure.fail; -public class FunctionUtils { +public abstract class Foldables { /** * A utility class to validate the type resolution of expressions before and after logical planning. * If null is passed for Failures to the constructor, it means we are only type resolution. @@ -64,6 +68,34 @@ public Expression.TypeResolution getResolvedType() { } } + public static Object valueOf(FoldContext ctx, Expression e) { + if (e.foldable()) { + return e.fold(ctx); + } + throw new QlIllegalArgumentException("Cannot determine value for {}", e); + } + + public static String stringLiteralValueOf(Expression expression, String message) { + if (expression instanceof Literal literal && literal.value() instanceof BytesRef bytesRef) { + return bytesRef.utf8ToString(); + } + throw new QlIllegalArgumentException(message); + } + + public static Object literalValueOf(Expression e) { + if (e instanceof Literal literal) { + return literal.value(); + } + throw new QlIllegalArgumentException("Expected literal, but got {}", e); + } + + public static Object extractLiteralOrReturnSelf(Expression e) { + if (e instanceof Literal literal) { + return literal.value(); + } + return e; + } + public static Integer limitValue(Expression limitField, String sourceText) { if (limitField instanceof Literal literal) { Object value = literal.value(); @@ -148,4 +180,13 @@ public static String queryAsString(Expression queryField, String sourceText) { format(null, "Query value must be a constant string in [{}], found [{}]", sourceText, queryField) ); } + + public static int intValueOf(Expression field, String sourceText, String fieldName) { + if (field instanceof Literal literal && literal.value() instanceof Number n) { + return n.intValue(); + } + throw new EsqlIllegalArgumentException( + Strings.format(null, "[{}] value must be a constant number in [{}], found [{}]", fieldName, sourceText, field) + ); + } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/AggregateFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/AggregateFunction.java index 60ae8a4ba57c7..d82bd4f446df0 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/AggregateFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/AggregateFunction.java @@ -12,7 +12,6 @@ import org.elasticsearch.xpack.esql.capabilities.PostAnalysisPlanVerificationAware; import org.elasticsearch.xpack.esql.common.Failures; import org.elasticsearch.xpack.esql.core.expression.Expression; -import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.expression.TypeResolutions; import org.elasticsearch.xpack.esql.core.expression.function.Function; @@ -130,7 +129,7 @@ public List parameters() { public boolean hasFilter() { return filter != null - && (filter.foldable() == false || Boolean.TRUE.equals(filter.fold(FoldContext.small() /* TODO remove me */)) == false); + && (filter.foldable() == false || (filter instanceof Literal literal && Boolean.TRUE.equals(literal.value()) == false)); } public Expression filter() { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountDistinct.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountDistinct.java index 2dd3acc600d26..6d0734922946f 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountDistinct.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/CountDistinct.java @@ -19,7 +19,6 @@ import org.elasticsearch.compute.aggregation.CountDistinctLongAggregatorFunctionSupplier; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.core.expression.Expression; -import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; @@ -49,6 +48,7 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isWholeNumber; import static org.elasticsearch.xpack.esql.core.util.CollectionUtils.nullSafeList; +import static org.elasticsearch.xpack.esql.expression.Foldables.intValueOf; public class CountDistinct extends AggregateFunction implements OptionalArgument, ToAggregator, SurrogateExpression { public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry( @@ -210,9 +210,7 @@ protected TypeResolution resolveType() { @Override public AggregatorFunctionSupplier supplier() { DataType type = field().dataType(); - int precision = this.precision == null - ? DEFAULT_PRECISION - : ((Number) this.precision.fold(FoldContext.small() /* TODO remove me */)).intValue(); + int precision = this.precision == null ? DEFAULT_PRECISION : precisionValue(); if (SUPPLIERS.containsKey(type) == false) { // If the type checking did its job, this should never happen throw EsqlIllegalArgumentException.illegalDataType(type); @@ -220,6 +218,10 @@ public AggregatorFunctionSupplier supplier() { return SUPPLIERS.get(type).apply(precision); } + private int precisionValue() { + return intValueOf(precision, source().text(), "Precision"); + } + @Override public Expression surrogate() { var s = source(); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Percentile.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Percentile.java index 4ac79f57dbc17..e00339babee3e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Percentile.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Percentile.java @@ -16,7 +16,6 @@ import org.elasticsearch.compute.aggregation.PercentileIntAggregatorFunctionSupplier; import org.elasticsearch.compute.aggregation.PercentileLongAggregatorFunctionSupplier; import org.elasticsearch.xpack.esql.core.expression.Expression; -import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; @@ -38,6 +37,7 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isFoldable; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; +import static org.elasticsearch.xpack.esql.expression.Foldables.intValueOf; public class Percentile extends NumericAggregate implements SurrogateExpression { public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry( @@ -169,7 +169,7 @@ protected AggregatorFunctionSupplier doubleSupplier() { } private int percentileValue() { - return ((Number) percentile.fold(FoldContext.small() /* TODO remove me */)).intValue(); + return intValueOf(percentile(), source().text(), "Percentile"); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Sample.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Sample.java index a988fb16be6a5..50cb44b186045 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Sample.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Sample.java @@ -24,12 +24,12 @@ import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.FunctionType; -import org.elasticsearch.xpack.esql.expression.function.FunctionUtils; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.planner.PlannerUtils; import org.elasticsearch.xpack.esql.planner.ToAggregator; @@ -42,9 +42,9 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isRepresentableExceptCounters; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPostOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPreOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.resolveTypeLimit; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPostOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPreOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.resolveTypeLimit; public class Sample extends AggregateFunction implements ToAggregator, PostOptimizationVerificationAware { public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "Sample", Sample::new); @@ -174,7 +174,7 @@ Expression limitField() { } private int limitValue() { - return FunctionUtils.limitValue(limitField(), sourceText()); + return Foldables.limitValue(limitField(), sourceText()); } Expression uuid() { @@ -183,6 +183,6 @@ Expression uuid() { @Override public void postOptimizationVerification(Failures failures) { - FunctionUtils.resolveTypeLimit(limitField(), sourceText(), forPostOptimizationValidation(limitField(), failures)); + Foldables.resolveTypeLimit(limitField(), sourceText(), forPostOptimizationValidation(limitField(), failures)); } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Top.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Top.java index 017fef8e917ea..b213f2120a7ac 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Top.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/Top.java @@ -27,12 +27,12 @@ import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.Foldables; +import org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator; import org.elasticsearch.xpack.esql.expression.SurrogateExpression; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.FunctionType; -import org.elasticsearch.xpack.esql.expression.function.FunctionUtils; -import org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; import org.elasticsearch.xpack.esql.planner.ToAggregator; @@ -49,8 +49,8 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPostOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPreOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPostOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPreOptimizationValidation; public class Top extends AggregateFunction implements ToAggregator, SurrogateExpression, PostOptimizationVerificationAware { public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "Top", Top::new); @@ -123,7 +123,7 @@ Expression orderField() { } private Integer limitValue() { - return FunctionUtils.limitValue(limitField(), sourceText()); + return Foldables.limitValue(limitField(), sourceText()); } private boolean orderValue() { @@ -181,7 +181,7 @@ protected TypeResolution resolveType() { * During postOptimizationVerification folding is already done, so we also verify that it is definitively a literal */ private TypeResolution resolveTypeLimit() { - return FunctionUtils.resolveTypeLimit(limitField(), sourceText(), forPreOptimizationValidation(limitField())); + return Foldables.resolveTypeLimit(limitField(), sourceText(), forPreOptimizationValidation(limitField())); } /** @@ -238,7 +238,7 @@ public void postOptimizationVerification(Failures failures) { } private void postOptimizationVerificationLimit(Failures failures) { - FunctionUtils.resolveTypeLimit(limitField(), sourceText(), forPostOptimizationValidation(limitField(), failures)); + Foldables.resolveTypeLimit(limitField(), sourceText(), forPostOptimizationValidation(limitField(), failures)); } private void postOptimizationVerificationOrder(Failures failures) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/FullTextFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/FullTextFunction.java index fd11b3eff29ca..452b18e0d7185 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/FullTextFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/FullTextFunction.java @@ -56,9 +56,9 @@ import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isNotNull; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isString; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPostOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPreOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.resolveTypeQuery; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPostOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPreOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.resolveTypeQuery; /** * Base class for full-text functions that use ES queries to match documents. diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Kql.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Kql.java index db9a7da19ebc6..a9eb30ab3e52e 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Kql.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Kql.java @@ -16,11 +16,11 @@ import org.elasticsearch.xpack.esql.core.querydsl.query.Query; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; -import org.elasticsearch.xpack.esql.expression.function.FunctionUtils; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; import org.elasticsearch.xpack.esql.optimizer.rules.physical.local.LucenePushdownPredicates; @@ -95,7 +95,7 @@ protected NodeInfo info() { @Override protected Query translate(LucenePushdownPredicates pushdownPredicates, TranslatorHandler handler) { - return new KqlQuery(source(), FunctionUtils.queryAsString(query(), sourceText())); + return new KqlQuery(source(), Foldables.queryAsString(query(), sourceText())); } @Override 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 c9663939e19a7..de3c827e06427 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 @@ -26,11 +26,11 @@ import org.elasticsearch.xpack.esql.core.type.DataType; import org.elasticsearch.xpack.esql.core.util.Check; import org.elasticsearch.xpack.esql.core.util.NumericUtils; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; -import org.elasticsearch.xpack.esql.expression.function.FunctionUtils; import org.elasticsearch.xpack.esql.expression.function.MapParam; import org.elasticsearch.xpack.esql.expression.function.OptionalArgument; import org.elasticsearch.xpack.esql.expression.function.Options; @@ -79,8 +79,8 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT; import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG; import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPreOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.resolveTypeQuery; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPreOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.resolveTypeQuery; import static org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.EsqlBinaryComparison.formatIncompatibleTypesMessage; /** @@ -405,7 +405,7 @@ public BiConsumer postAnalysisPlanVerification() { } public Object queryAsObject() { - Object queryAsObject = FunctionUtils.queryAsObject(query(), sourceText()); + Object queryAsObject = Foldables.queryAsObject(query(), sourceText()); // Convert BytesRef to string for string-based values if (queryAsObject instanceof BytesRef bytesRef) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MatchPhrase.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MatchPhrase.java index 0ecba5a3994d5..6db259d9a67ab 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MatchPhrase.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MatchPhrase.java @@ -23,11 +23,11 @@ import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; import org.elasticsearch.xpack.esql.core.util.Check; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; -import org.elasticsearch.xpack.esql.expression.function.FunctionUtils; import org.elasticsearch.xpack.esql.expression.function.MapParam; import org.elasticsearch.xpack.esql.expression.function.OptionalArgument; import org.elasticsearch.xpack.esql.expression.function.Options; @@ -62,8 +62,8 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER; import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD; import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPreOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.resolveTypeQuery; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPreOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.resolveTypeQuery; /** * Full text function that performs a {@link org.elasticsearch.xpack.esql.querydsl.query.MatchPhraseQuery} . @@ -258,7 +258,7 @@ public BiConsumer postAnalysisPlanVerification() { } public Object queryAsObject() { - Object queryAsObject = FunctionUtils.queryAsObject(query(), sourceText()); + Object queryAsObject = Foldables.queryAsObject(query(), sourceText()); // Convert BytesRef to string for string-based values if (queryAsObject instanceof BytesRef bytesRef) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MultiMatch.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MultiMatch.java index 74138a88367f8..b5a05e208a324 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MultiMatch.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/MultiMatch.java @@ -23,11 +23,11 @@ import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; import org.elasticsearch.xpack.esql.core.util.Check; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; -import org.elasticsearch.xpack.esql.expression.function.FunctionUtils; import org.elasticsearch.xpack.esql.expression.function.MapParam; import org.elasticsearch.xpack.esql.expression.function.OptionalArgument; import org.elasticsearch.xpack.esql.expression.function.Options; @@ -80,8 +80,8 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT; import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG; import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPreOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.resolveTypeQuery; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPreOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.resolveTypeQuery; /** * Full text function that performs a {@link org.elasticsearch.xpack.esql.querydsl.query.MultiMatchQuery} . @@ -347,7 +347,7 @@ protected Query translate(LucenePushdownPredicates pushdownPredicates, Translato String fieldName = Match.getNameFromFieldAttribute(fieldAttribute); fieldsWithBoost.put(fieldName, 1.0f); } - return new MultiMatchQuery(source(), FunctionUtils.queryAsString(query(), sourceText()), fieldsWithBoost, getOptions()); + return new MultiMatchQuery(source(), Foldables.queryAsString(query(), sourceText()), fieldsWithBoost, getOptions()); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/QueryString.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/QueryString.java index 1678ba696a1fb..b73d619308eaa 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/QueryString.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/QueryString.java @@ -20,11 +20,11 @@ import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; -import org.elasticsearch.xpack.esql.expression.function.FunctionUtils; import org.elasticsearch.xpack.esql.expression.function.MapParam; import org.elasticsearch.xpack.esql.expression.function.OptionalArgument; import org.elasticsearch.xpack.esql.expression.function.Options; @@ -70,8 +70,8 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER; import static org.elasticsearch.xpack.esql.core.type.DataType.KEYWORD; import static org.elasticsearch.xpack.esql.core.type.DataType.TEXT; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPreOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.resolveTypeQuery; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPreOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.resolveTypeQuery; /** * Full text function that performs a {@link QueryStringQuery} . @@ -354,7 +354,7 @@ protected NodeInfo info() { @Override protected Query translate(LucenePushdownPredicates pushdownPredicates, TranslatorHandler handler) { - return new QueryStringQuery(source(), FunctionUtils.queryAsString(query(), sourceText()), Map.of(), queryStringOptions()); + return new QueryStringQuery(source(), Foldables.queryAsString(query(), sourceText()), Map.of(), queryStringOptions()); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Term.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Term.java index fc62c451ba589..ad7c79bc1aa14 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Term.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/fulltext/Term.java @@ -21,11 +21,11 @@ import org.elasticsearch.xpack.esql.core.querydsl.query.TermQuery; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; -import org.elasticsearch.xpack.esql.expression.function.FunctionUtils; import org.elasticsearch.xpack.esql.expression.function.Param; import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; import org.elasticsearch.xpack.esql.optimizer.rules.physical.local.LucenePushdownPredicates; @@ -134,7 +134,7 @@ protected TypeResolutions.ParamOrdinal queryParamOrdinal() { @Override protected Query translate(LucenePushdownPredicates pushdownPredicates, TranslatorHandler handler) { // Uses a term query that contributes to scoring - return new TermQuery(source(), ((FieldAttribute) field()).name(), FunctionUtils.queryAsObject(query(), sourceText()), false, true); + return new TermQuery(source(), ((FieldAttribute) field()).name(), Foldables.queryAsObject(query(), sourceText()), false, true); } @Override diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/grouping/Bucket.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/grouping/Bucket.java index 131544e560e5c..7b60b382f5769 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/grouping/Bucket.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/grouping/Bucket.java @@ -19,12 +19,12 @@ import org.elasticsearch.xpack.esql.common.Failures; import org.elasticsearch.xpack.esql.core.expression.Expression; import org.elasticsearch.xpack.esql.core.expression.FoldContext; -import org.elasticsearch.xpack.esql.core.expression.Foldables; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.expression.TypeResolutions; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.FunctionType; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatch.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatch.java index 547022e0fbde5..109cfe45ba098 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatch.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/ip/CIDRMatch.java @@ -18,7 +18,6 @@ import org.elasticsearch.xpack.esql.capabilities.TranslationAware; import org.elasticsearch.xpack.esql.core.expression.Expression; import org.elasticsearch.xpack.esql.core.expression.Expressions; -import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.querydsl.query.Query; import org.elasticsearch.xpack.esql.core.querydsl.query.TermsQuery; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; @@ -26,6 +25,7 @@ import org.elasticsearch.xpack.esql.core.type.DataType; import org.elasticsearch.xpack.esql.core.util.Check; import org.elasticsearch.xpack.esql.core.util.CollectionUtils; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; import org.elasticsearch.xpack.esql.expression.function.Param; @@ -189,7 +189,7 @@ public Query asQuery(LucenePushdownPredicates pushdownPredicates, TranslatorHand Check.isTrue(Expressions.foldable(matches), "Expected foldable matches, but got [{}]", matches); String targetFieldName = handler.nameOf(fa.exactAttribute()); - Set set = new LinkedHashSet<>(Expressions.fold(FoldContext.small() /* TODO remove me */, matches)); + Set set = new LinkedHashSet<>(matches.stream().map(Foldables::literalValueOf).toList()); return new TermsQuery(source(), targetFieldName, set); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundTo.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundTo.java index ef1ac8337bc59..1b98db093e258 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundTo.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/math/RoundTo.java @@ -13,12 +13,12 @@ import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; import org.elasticsearch.xpack.esql.core.expression.Expression; -import org.elasticsearch.xpack.esql.core.expression.Foldables; import org.elasticsearch.xpack.esql.core.expression.Nullability; import org.elasticsearch.xpack.esql.core.expression.TypeResolutions; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.function.Example; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo; import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSort.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSort.java index b3c6110dc2b83..6053e81d89d3a 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSort.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/multivalue/MvSort.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar.multivalue; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.TriFunction; import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.StreamInput; @@ -34,7 +33,6 @@ import org.elasticsearch.xpack.esql.common.Failure; import org.elasticsearch.xpack.esql.common.Failures; import org.elasticsearch.xpack.esql.core.expression.Expression; -import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.tree.NodeInfo; import org.elasticsearch.xpack.esql.core.tree.Source; @@ -149,20 +147,27 @@ public boolean foldable() { @Override public EvalOperator.ExpressionEvaluator.Factory toEvaluator(ToEvaluator toEvaluator) { boolean ordering = true; - if (isValidOrder() == false) { - throw new IllegalArgumentException( - LoggerMessageFormat.format( - null, - INVALID_ORDER_ERROR, - sourceText(), - BytesRefs.toString(ASC.value()), - BytesRefs.toString(DESC.value()), - BytesRefs.toString(order.fold(toEvaluator.foldCtx())) - ) - ); - } - if (order != null && order.foldable()) { - ordering = BytesRefs.toString(order.fold(toEvaluator.foldCtx())).equalsIgnoreCase(BytesRefs.toString(ASC.value())); + if (order != null) { + if (order.foldable() == false) { + throw new IllegalStateException( + LoggerMessageFormat.format(null, "Order expression must be foldable, but got [{}]", sourceText()) + ); + } + String orderValue = BytesRefs.toString(order.fold(toEvaluator.foldCtx())); + if (orderValue.equalsIgnoreCase(BytesRefs.toString(DESC.value())) == false + && orderValue.equalsIgnoreCase(BytesRefs.toString(ASC.value())) == false) { + throw new IllegalArgumentException( + LoggerMessageFormat.format( + null, + INVALID_ORDER_ERROR, + sourceText(), + BytesRefs.toString(ASC.value()), + BytesRefs.toString(DESC.value()), + orderValue + ) + ); + } + ordering = orderValue.equalsIgnoreCase(BytesRefs.toString(ASC.value())); } return switch (PlannerUtils.toElementType(field.dataType())) { @@ -246,7 +251,7 @@ public void postOptimizationVerification(Failures failures) { sourceText(), BytesRefs.toString(ASC.value()), BytesRefs.toString(DESC.value()), - BytesRefs.toString(order.fold(FoldContext.small() /* TODO remove me */)) + BytesRefs.toString(order) ) ); } @@ -255,16 +260,16 @@ public void postOptimizationVerification(Failures failures) { private boolean isValidOrder() { boolean isValidOrder = true; if (order != null && order.foldable()) { - Object obj = order.fold(FoldContext.small() /* TODO remove me */); - String o = null; - if (obj instanceof BytesRef ob) { - o = ob.utf8ToString(); - } else if (obj instanceof String os) { - o = os; - } - if (o == null - || o.equalsIgnoreCase(BytesRefs.toString(ASC.value())) == false - && o.equalsIgnoreCase(BytesRefs.toString(DESC.value())) == false) { + if (order instanceof Literal literal) { + Object obj = literal.value(); + String o = BytesRefs.toString(obj); + if (o == null + || o.equalsIgnoreCase(BytesRefs.toString(ASC.value())) == false + && o.equalsIgnoreCase(BytesRefs.toString(DESC.value())) == false) { + isValidOrder = false; + } + } else { + // order should be folded already, so if it is not literal it is invalid isValidOrder = false; } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunction.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunction.java index 4af5230e9d328..1aa6faea83860 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunction.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesFunction.java @@ -26,7 +26,6 @@ import org.elasticsearch.xpack.esql.core.QlIllegalArgumentException; import org.elasticsearch.xpack.esql.core.expression.Expression; import org.elasticsearch.xpack.esql.core.expression.Expressions; -import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.TypedAttribute; import org.elasticsearch.xpack.esql.core.querydsl.query.Query; import org.elasticsearch.xpack.esql.core.tree.Source; @@ -213,7 +212,7 @@ private Query translate(TranslatorHandler handler, Expression spatialExpression, String name = handler.nameOf(attribute); try { - Geometry shape = SpatialRelatesUtils.makeGeometryFromLiteral(FoldContext.small() /* TODO remove me */, constantExpression); + Geometry shape = SpatialRelatesUtils.makeGeometryFromLiteral(constantExpression); return new SpatialRelatesQuery(source(), name, queryRelation(), shape, attribute.dataType()); } catch (IllegalArgumentException e) { throw new QlIllegalArgumentException(e.getMessage(), e); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesUtils.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesUtils.java index 1b06c6dfd3dd5..0d323ad8cb856 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesUtils.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/scalar/spatial/SpatialRelatesUtils.java @@ -38,7 +38,8 @@ import java.util.List; import java.util.function.Function; -import static org.elasticsearch.xpack.esql.core.expression.Foldables.valueOf; +import static org.elasticsearch.xpack.esql.expression.Foldables.literalValueOf; +import static org.elasticsearch.xpack.esql.expression.Foldables.valueOf; public class SpatialRelatesUtils { @@ -168,13 +169,20 @@ private static Geometry asGeometry(BytesRefBlock valueBlock, int position) { } /** - * This function is used in two places, when evaluating a spatial constant in the SpatialRelatesFunction, as well as when - * we do lucene-pushdown of spatial functions. + * This function is used when evaluating a spatial constant in the SpatialRelatesFunction */ public static Geometry makeGeometryFromLiteral(FoldContext ctx, Expression expr) { return makeGeometryFromLiteralValue(valueOf(ctx, expr), expr.dataType()); } + /** + * This function is used when we do lucene-pushdown of spatial functions. + * The expression is expected to be folded already and a literal + */ + public static Geometry makeGeometryFromLiteral(Expression expr) { + return makeGeometryFromLiteralValue(literalValueOf(expr), expr.dataType()); + } + private static Geometry makeGeometryFromLiteralValue(Object value, DataType dataType) { if (value instanceof BytesRef bytesRef) { // Single value expression diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/vector/Knn.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/vector/Knn.java index bb80f828b19ae..0b64fb43909df 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/vector/Knn.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/vector/Knn.java @@ -67,8 +67,8 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.DENSE_VECTOR; import static org.elasticsearch.xpack.esql.core.type.DataType.FLOAT; import static org.elasticsearch.xpack.esql.core.type.DataType.INTEGER; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.TypeResolutionValidator.forPreOptimizationValidation; -import static org.elasticsearch.xpack.esql.expression.function.FunctionUtils.resolveTypeQuery; +import static org.elasticsearch.xpack.esql.expression.Foldables.TypeResolutionValidator.forPreOptimizationValidation; +import static org.elasticsearch.xpack.esql.expression.Foldables.resolveTypeQuery; public class Knn extends FullTextFunction implements OptionalArgument, VectorFunction, PostAnalysisPlanVerificationAware { private final Logger log = LogManager.getLogger(getClass()); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/Range.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/Range.java index e8953be7348d7..d1bfb1d3241e3 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/Range.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/Range.java @@ -33,13 +33,13 @@ import java.util.Objects; import static java.util.Arrays.asList; -import static org.elasticsearch.xpack.esql.core.expression.Foldables.valueOf; import static org.elasticsearch.xpack.esql.core.type.DataType.DATE_NANOS; import static org.elasticsearch.xpack.esql.core.type.DataType.IP; import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG; import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION; import static org.elasticsearch.xpack.esql.core.util.DateUtils.asDateTime; import static org.elasticsearch.xpack.esql.core.util.NumericUtils.unsignedLongAsNumber; +import static org.elasticsearch.xpack.esql.expression.Foldables.literalValueOf; import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.DEFAULT_DATE_NANOS_FORMATTER; import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.DEFAULT_DATE_TIME_FORMATTER; import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.dateWithTypeToString; @@ -232,8 +232,8 @@ public Query asQuery(LucenePushdownPredicates pushdownPredicates, TranslatorHand } private RangeQuery translate(TranslatorHandler handler) { - Object l = valueOf(FoldContext.small() /* TODO remove me */, lower); - Object u = valueOf(FoldContext.small() /* TODO remove me */, upper); + Object l = literalValueOf(lower); + Object u = literalValueOf(upper); String format = null; DataType dataType = value.dataType(); diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/EsqlBinaryComparison.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/EsqlBinaryComparison.java index 69ef99ba04d15..1c483ebc14744 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/EsqlBinaryComparison.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/EsqlBinaryComparison.java @@ -51,13 +51,13 @@ import java.util.Map; import static org.elasticsearch.common.logging.LoggerMessageFormat.format; -import static org.elasticsearch.xpack.esql.core.expression.Foldables.valueOf; import static org.elasticsearch.xpack.esql.core.type.DataType.DATETIME; import static org.elasticsearch.xpack.esql.core.type.DataType.DATE_NANOS; import static org.elasticsearch.xpack.esql.core.type.DataType.IP; import static org.elasticsearch.xpack.esql.core.type.DataType.UNSIGNED_LONG; import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION; import static org.elasticsearch.xpack.esql.core.util.NumericUtils.unsignedLongAsNumber; +import static org.elasticsearch.xpack.esql.expression.Foldables.literalValueOf; import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.DEFAULT_DATE_NANOS_FORMATTER; import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.DEFAULT_DATE_TIME_FORMATTER; import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.HOUR_MINUTE_SECOND; @@ -378,7 +378,7 @@ public Expression singleValueField() { private Query translate(TranslatorHandler handler) { TypedAttribute attribute = LucenePushdownPredicates.checkIsPushableAttribute(left()); String name = handler.nameOf(attribute); - Object value = valueOf(FoldContext.small() /* TODO remove me */, right()); + Object value = literalValueOf(right()); String format = null; boolean isDateLiteralComparison = false; @@ -478,7 +478,7 @@ private Query translateOutOfRangeComparisons() { if ((left() instanceof FieldAttribute) == false || left().dataType().isNumeric() == false) { return null; } - Object value = valueOf(FoldContext.small() /* TODO remove me */, right()); + Object value = literalValueOf(right()); // Comparisons with multi-values always return null in ESQL. if (value instanceof List) { diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/In.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/In.java index c38d7abd7243e..fe4e7ed069bd9 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/In.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/In.java @@ -52,7 +52,6 @@ import java.util.Set; import static org.elasticsearch.common.logging.LoggerMessageFormat.format; -import static org.elasticsearch.xpack.esql.core.expression.Foldables.valueOf; import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT; import static org.elasticsearch.xpack.esql.core.type.DataType.BOOLEAN; import static org.elasticsearch.xpack.esql.core.type.DataType.DATETIME; @@ -68,6 +67,7 @@ import static org.elasticsearch.xpack.esql.core.type.DataType.UNSUPPORTED; import static org.elasticsearch.xpack.esql.core.type.DataType.VERSION; import static org.elasticsearch.xpack.esql.core.util.StringUtils.ordinal; +import static org.elasticsearch.xpack.esql.expression.Foldables.literalValueOf; /** * The {@code IN} operator. @@ -491,7 +491,7 @@ private Query translate(LucenePushdownPredicates pushdownPredicates, TranslatorH queries.add(query); } } else { - terms.add(valueOf(FoldContext.small() /* TODO remove me */, rhs)); + terms.add(literalValueOf(rhs)); } } } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InsensitiveEquals.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InsensitiveEquals.java index ccd2f5bbbad5b..221816ce97000 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InsensitiveEquals.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InsensitiveEquals.java @@ -31,7 +31,7 @@ import java.io.IOException; -import static org.elasticsearch.xpack.esql.core.expression.Foldables.valueOf; +import static org.elasticsearch.xpack.esql.expression.Foldables.literalValueOf; public class InsensitiveEquals extends InsensitiveBinaryComparison { public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry( @@ -125,7 +125,7 @@ private void checkInsensitiveComparison() { private Query translate() { TypedAttribute attribute = LucenePushdownPredicates.checkIsPushableAttribute(left()); - BytesRef value = BytesRefs.toBytesRef(valueOf(FoldContext.small() /* TODO remove me */, right())); + BytesRef value = BytesRefs.toBytesRef(literalValueOf(right())); String name = LucenePushdownPredicates.pushableAttributeName(attribute); return new TermQuery(source(), name, value.utf8ToString(), true); } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InsensitiveEqualsMapper.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InsensitiveEqualsMapper.java index 70d87b7cc77ff..50cf43a77cff7 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InsensitiveEqualsMapper.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/predicate/operator/comparison/InsensitiveEqualsMapper.java @@ -44,7 +44,7 @@ public final ExpressionEvaluator.Factory map( var rightEval = toEvaluator(foldCtx, bc.right(), layout, shardContexts); if (DataType.isString(leftType)) { if (bc.right().foldable() && DataType.isString(rightType)) { - BytesRef rightVal = BytesRefs.toBytesRef(bc.right().fold(FoldContext.small() /* TODO remove me */)); + BytesRef rightVal = BytesRefs.toBytesRef(bc.right().fold(foldCtx)); Automaton automaton = InsensitiveEquals.automaton(rightVal); return dvrCtx -> new InsensitiveEqualsConstantEvaluator( bc.source(), diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineSample.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineSample.java index 3c2f2a17650e5..35c95f200f4ff 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineSample.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineSample.java @@ -8,8 +8,8 @@ package org.elasticsearch.xpack.esql.optimizer.rules.logical; import org.elasticsearch.xpack.esql.core.expression.Expression; -import org.elasticsearch.xpack.esql.core.expression.Foldables; import org.elasticsearch.xpack.esql.core.expression.Literal; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.optimizer.LogicalOptimizerContext; import org.elasticsearch.xpack.esql.plan.logical.Enrich; import org.elasticsearch.xpack.esql.plan.logical.Eval; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushSampleToSource.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushSampleToSource.java index 4e41b748ece08..1920a7441cda7 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushSampleToSource.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/physical/local/PushSampleToSource.java @@ -8,7 +8,7 @@ package org.elasticsearch.xpack.esql.optimizer.rules.physical.local; import org.elasticsearch.search.aggregations.bucket.sampler.random.RandomSamplingQueryBuilder; -import org.elasticsearch.xpack.esql.core.expression.Foldables; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.optimizer.LocalPhysicalOptimizerContext; import org.elasticsearch.xpack.esql.optimizer.PhysicalOptimizerRules; import org.elasticsearch.xpack.esql.plan.physical.EsQueryExec; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Enrich.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Enrich.java index 413fe4d6d36ab..af56345438c21 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Enrich.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Enrich.java @@ -26,7 +26,6 @@ import org.elasticsearch.xpack.esql.core.expression.AttributeSet; import org.elasticsearch.xpack.esql.core.expression.EmptyAttribute; import org.elasticsearch.xpack.esql.core.expression.Expression; -import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.NameId; import org.elasticsearch.xpack.esql.core.expression.NamedExpression; import org.elasticsearch.xpack.esql.core.expression.ReferenceAttribute; @@ -49,6 +48,7 @@ import static org.elasticsearch.xpack.esql.common.Failure.fail; import static org.elasticsearch.xpack.esql.core.expression.Expressions.asAttributes; +import static org.elasticsearch.xpack.esql.expression.Foldables.literalValueOf; import static org.elasticsearch.xpack.esql.expression.NamedExpressions.mergeOutputAttributes; public class Enrich extends UnaryPlan implements GeneratingPlan, PostAnalysisPlanVerificationAware, TelemetryAware, SortAgnostic { @@ -57,7 +57,7 @@ public class Enrich extends UnaryPlan implements GeneratingPlan, PostAna "Enrich", Enrich::readFrom ); - + // policyName can only be a string literal once it's resolved private final Expression policyName; private final NamedExpression matchField; private final EnrichPolicy policy; @@ -153,7 +153,8 @@ public void writeTo(StreamOutput out) throws IOException { out.writeNamedWriteable(policyName()); out.writeNamedWriteable(matchField()); if (out.getTransportVersion().before(TransportVersions.V_8_13_0)) { - out.writeString(BytesRefs.toString(policyName().fold(FoldContext.small() /* TODO remove me */))); // old policy name + // policyName can only be a string literal once it's resolved + out.writeString(BytesRefs.toString(literalValueOf(policyName()))); // old policy name } policy().writeTo(out); if (out.getTransportVersion().onOrAfter(TransportVersions.V_8_13_0)) { @@ -196,6 +197,10 @@ public Expression policyName() { return policyName; } + public String resolvedPolicyName() { + return BytesRefs.toString(literalValueOf(policyName)); + } + public Mode mode() { return mode; } diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java index da48c4f1dbca3..ec1539fa3ae38 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/LocalExecutionPlanner.java @@ -70,7 +70,6 @@ import org.elasticsearch.xpack.esql.core.expression.Expressions; import org.elasticsearch.xpack.esql.core.expression.FieldAttribute; import org.elasticsearch.xpack.esql.core.expression.FoldContext; -import org.elasticsearch.xpack.esql.core.expression.Foldables; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.expression.MetadataAttribute; import org.elasticsearch.xpack.esql.core.expression.NameId; @@ -86,6 +85,7 @@ import org.elasticsearch.xpack.esql.enrich.MatchConfig; import org.elasticsearch.xpack.esql.evaluator.EvalMapper; import org.elasticsearch.xpack.esql.evaluator.command.GrokEvaluatorExtracter; +import org.elasticsearch.xpack.esql.expression.Foldables; import org.elasticsearch.xpack.esql.expression.Order; import org.elasticsearch.xpack.esql.inference.InferenceService; import org.elasticsearch.xpack.esql.inference.XContentRowEncoder; diff --git a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/MapperUtils.java b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/MapperUtils.java index 4db37cf6547eb..5cbf8f4844b2d 100644 --- a/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/MapperUtils.java +++ b/x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/planner/mapper/MapperUtils.java @@ -7,11 +7,9 @@ package org.elasticsearch.xpack.esql.planner.mapper; -import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.compute.aggregation.AggregatorMode; import org.elasticsearch.xpack.esql.EsqlIllegalArgumentException; import org.elasticsearch.xpack.esql.core.expression.Attribute; -import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.MetadataAttribute; import org.elasticsearch.xpack.esql.plan.logical.Aggregate; import org.elasticsearch.xpack.esql.plan.logical.ChangePoint; @@ -115,7 +113,7 @@ static PhysicalPlan mapUnary(UnaryPlan p, PhysicalPlan child) { enrich.mode(), enrich.policy().getType(), enrich.matchField(), - BytesRefs.toString(enrich.policyName().fold(FoldContext.small() /* TODO remove me */)), + enrich.resolvedPolicyName(), enrich.policy().getMatchField(), enrich.concreteIndices(), enrich.enrichFields() diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java index 373c8d86e6be7..830e4bd546c25 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LogicalPlanOptimizerTests.java @@ -628,12 +628,13 @@ public void testReplaceStatsFilteredAggWithEvalMixedFilterAndNoFilter() { /** *
{@code
-     * Project[[sum(salary) + 1 where false{r}#3, sum(salary) + 3{r}#5, sum(salary) + 2 where false{r}#7]]
-     * \_Eval[[null[LONG] AS sum(salary) + 1 where false, $$SUM$sum(salary)_+_3$1{r$}#19 + 3[INTEGER] AS sum(salary) + 3, nu
-     * ll[LONG] AS sum(salary) + 2 where false]]
-     *   \_Limit[1000[INTEGER]]
-     *     \_Aggregate[STANDARD,[],[SUM(salary{f}#13,true[BOOLEAN]) AS $$SUM$sum(salary)_+_3$1]]
-     *       \_EsRelation[test][_meta_field{f}#14, emp_no{f}#8, first_name{f}#9, ge..]
+     * Project[[sum(salary) + 1 where false{r}#3, sum(salary) + 3{r}#5, sum(salary) + 2 where null{r}#7,
+     * sum(salary) + 4 where not true{r}#9]]
+     * \_Eval[[null[LONG] AS sum(salary) + 1 where false#3, $$SUM$sum(salary)_+_3$1{r$}#22 + 3[INTEGER] AS sum(salary) + 3#5
+     * , null[LONG] AS sum(salary) + 2 where null#7, null[LONG] AS sum(salary) + 4 where not true#9]]
+     *   \_Limit[1000[INTEGER],false]
+     *     \_Aggregate[[],[SUM(salary{f}#15,true[BOOLEAN],compensated[KEYWORD]) AS $$SUM$sum(salary)_+_3$1#22]]
+     *       \_EsRelation[test][_meta_field{f}#16, emp_no{f}#10, first_name{f}#11, ..]
      * }
* */ @@ -642,25 +643,35 @@ public void testReplaceStatsFilteredAggWithEvalFilterFalseAndNull() { from test | stats sum(salary) + 1 where false, sum(salary) + 3, - sum(salary) + 2 where null + sum(salary) + 2 where null, + sum(salary) + 4 where not true """); var project = as(plan, Project.class); assertThat( Expressions.names(project.projections()), - contains("sum(salary) + 1 where false", "sum(salary) + 3", "sum(salary) + 2 where null") + contains("sum(salary) + 1 where false", "sum(salary) + 3", "sum(salary) + 2 where null", "sum(salary) + 4 where not true") ); var eval = as(project.child(), Eval.class); - assertThat(eval.fields().size(), is(3)); + assertThat(eval.fields().size(), is(4)); var alias = as(eval.fields().getFirst(), Alias.class); assertTrue(alias.child().foldable()); assertThat(alias.child().fold(FoldContext.small()), nullValue()); assertThat(alias.child().dataType(), is(LONG)); + alias = as(eval.fields().get(0), Alias.class); + assertThat(Expressions.name(alias.child()), containsString("sum(salary) + 1")); + alias = as(eval.fields().get(1), Alias.class); assertThat(Expressions.name(alias.child()), containsString("sum(salary) + 3")); + alias = as(eval.fields().get(2), Alias.class); + assertThat(Expressions.name(alias.child()), containsString("sum(salary) + 2")); + + alias = as(eval.fields().get(3), Alias.class); + assertThat(Expressions.name(alias.child()), containsString("sum(salary) + 4")); + alias = as(eval.fields().getLast(), Alias.class); assertTrue(alias.child().foldable()); assertThat(alias.child().fold(FoldContext.small()), nullValue()); @@ -671,6 +682,46 @@ public void testReplaceStatsFilteredAggWithEvalFilterFalseAndNull() { var source = as(aggregate.child(), EsRelation.class); } + /* + * Limit[1000[INTEGER]] + * \_LocalRelation[[count(salary) where false{r}#3],[LongVectorBlock[vector=ConstantLongVector[positions=1, value=0]]]] + */ + public void testReplaceStatsFilteredAggWithEvalNotTrue() { + var plan = plan(""" + from test + | stats count(salary) where not true + """); + + var limit = as(plan, Limit.class); + var source = as(limit.child(), LocalRelation.class); + assertThat(Expressions.names(source.output()), contains("count(salary) where not true")); + Block[] blocks = source.supplier().get(); + assertThat(blocks.length, is(1)); + var block = as(blocks[0], LongVectorBlock.class); + assertThat(block.getPositionCount(), is(1)); + assertThat(block.asVector().getLong(0), is(0L)); + } + + /* + * Limit[1000[INTEGER],false] + * \_Aggregate[[],[COUNT(salary{f}#10,true[BOOLEAN]) AS m1#4]] + * \_EsRelation[test][_meta_field{f}#11, emp_no{f}#5, first_name{f}#6, ge..] + */ + public void testReplaceStatsFilteredAggWithEvalNotFalse() { + var plan = plan(""" + from test + | stats m1 = count(salary) where not false + """); + var limit = as(plan, Limit.class); + var aggregate = as(limit.child(), Aggregate.class); + assertEquals(1, aggregate.aggregates().size()); + assertEquals(1, aggregate.aggregates().get(0).children().size()); + assertTrue(aggregate.aggregates().get(0).children().get(0) instanceof Count); + assertEquals("true", (((Count) aggregate.aggregates().get(0).children().get(0)).filter().toString())); + assertThat(Expressions.names(aggregate.aggregates()), contains("m1")); + var source = as(aggregate.child(), EsRelation.class); + } + /** *
{@code
      * Limit[1000[INTEGER]]
@@ -853,6 +904,39 @@ public void testExtractStatsCommonFilter() {
         var source = as(filter.child(), EsRelation.class);
     }
 
+    /**
+     * 
{@code
+     * Limit[1000[INTEGER],false]
+     * \_Aggregate[[],[MIN(salary{f}#15,true[BOOLEAN]) AS m1#4, MAX(salary{f}#15,true[BOOLEAN]) AS m2#8]]
+     * \_Filter[emp_no{f}#10 > 1[INTEGER]]
+     * \_EsRelation[test][_meta_field{f}#16, emp_no{f}#10, first_name{f}#11, ..]
+     * }
+ */ + public void testExtractStatsCommonAlwaysTruePlusOtherFilter() { + var plan = plan(""" + from test + | stats m1 = min(salary) where (true and emp_no > 1), + m2 = max(salary) where (1==1 and emp_no > 1) + """); + + var limit = as(plan, Limit.class); + var agg = as(limit.child(), Aggregate.class); + assertThat(agg.aggregates().size(), is(2)); + + var alias = as(agg.aggregates().get(0), Alias.class); + var aggFunc = as(alias.child(), AggregateFunction.class); + assertThat(aggFunc.filter(), is(Literal.TRUE)); + + alias = as(agg.aggregates().get(1), Alias.class); + aggFunc = as(alias.child(), AggregateFunction.class); + assertThat(aggFunc.filter(), is(Literal.TRUE)); + + var filter = as(agg.child(), Filter.class); + assertThat(Expressions.name(filter.condition()), is("emp_no > 1")); + + var source = as(filter.child(), EsRelation.class); + } + public void testExtractStatsCommonFilterUsingAliases() { var plan = plan(""" from test diff --git a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/230_folding.yml b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/230_folding.yml index af47b69566ce8..83236dff9163c 100644 --- a/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/230_folding.yml +++ b/x-pack/plugin/src/yamlRestTest/resources/rest-api-spec/test/esql/230_folding.yml @@ -24,6 +24,10 @@ setup: dims: 3 index: true similarity: l2_norm + ip: + type: ip + favorite_numbers: + type: integer - do: bulk: @@ -31,11 +35,11 @@ setup: refresh: true body: - { "index": { } } - - { "hire_date": "2020-01-01", "salary_change": 100.5, "salary": 50000, "salary_change_long": 100, "name": "Alice Smith", "image_vector": [ 0.1, 0.2, 0.3 ] } + - { "hire_date": "2020-01-01", "salary_change": 100.5, "salary": 50000, "salary_change_long": 100, "name": "Alice Smith", "image_vector": [ 0.1, 0.2, 0.3 ], "ip": "192.168.1.1" , "favorite_numbers": [ 4, 2, 3 ] } - { "index": { } } - - { "hire_date": "2021-01-01", "salary_change": 200.5, "salary": 60000, "salary_change_long": 200, "name": "Bob Johnson", "image_vector": [ 0.4, 0.5, 0.6 ] } + - { "hire_date": "2021-01-01", "salary_change": 200.5, "salary": 60000, "salary_change_long": 200, "name": "Bob Johnson", "image_vector": [ 0.4, 0.5, 0.6 ], "ip": "192.168.1.2" , "favorite_numbers": [ 4, 7, -2 ] } - { "index": { } } - - { "hire_date": "2019-01-01", "salary_change": 50.5, "salary": 40000, "salary_change_long": 50, "name": "Charlie Smith", "image_vector": [ 0.7, 0.8, 0.9 ] } + - { "hire_date": "2019-01-01", "salary_change": 50.5, "salary": 40000, "salary_change_long": 50, "name": "Charlie Smith", "image_vector": [ 0.7, 0.8, 0.9 ], "ip": "10.168.1.2" , "favorite_numbers": [ 2, 1, 3 ] } --- TOP function with constant folding: @@ -559,3 +563,165 @@ Query using TERM function on name but with non constant query: - match: { error.type: "verification_exception" } - contains: { error.reason: "second argument of [TERM(name, salary)] must be [string], found value [salary] type [integer]" } +--- + +Percentile with foldable value: + - do: + esql.query: + body: + query: | + ROW x = 100 | STATS p = PERCENTILE(x, 19.8 + 13.4454545) | LIMIT 1 + - match: { columns.0.name: "p" } + - match: { values.0.0: 100 } + +--- + +Percentile with non-foldable value: + - do: + catch: bad_request + esql.query: + body: + query: | + FROM employees | STATS p = PERCENTILE(salary, salary + 1) | LIMIT 1 + - match: { error.type: "verification_exception" } + - contains: { error.reason: "second argument of [PERCENTILE(salary, salary + 1)] must be a constant, received [salary + 1]" } + +--- +COUNT_DISTINCT with foldable precision: + - do: + esql.query: + body: + query: | + FROM employees + | STATS c1 = COUNT_DISTINCT(salary, 234 + 543) | LIMIT 1 + - match: { columns.0.name: "c1" } + - length: { values: 1 } + +--- +COUNT_DISTINCT with no precision: + - do: + esql.query: + body: + query: | + FROM employees + | STATS c1 = COUNT_DISTINCT(salary) | LIMIT 1 + - match: { columns.0.name: "c1" } + - length: { values: 1 } + +--- +COUNT_DISTINCT with non-foldable precision: + - do: + catch: bad_request + esql.query: + body: + query: | + FROM employees + | STATS COUNT_DISTINCT(salary, salary + 1) | LIMIT 1 + - match: { error.type: "verification_exception" } + - contains: { error.reason: "second argument of [COUNT_DISTINCT(salary, salary + 1)] must be a constant, received [salary + 1]" } + +--- +MV_SORT with foldable order: + - do: + esql.query: + body: + query: | + ROW a = [4, 2, -3, 2] + | EVAL sa = mv_sort(a, CONCAT("AS", "C")), sd = mv_sort(a, CONCAT("DE", "SC")) | LIMIT 1 + - match: { columns.0.name: "a" } + - match: { columns.1.name: "sa" } + - match: { columns.2.name: "sd" } + - match: { values.0.1: [ -3, 2, 2, 4 ] } + - match: { values.0.2: [ 4, 2, 2, -3 ] } +--- +MV_SORT with foldable order on employee favorite numbers: + - do: + esql.query: + body: + query: | + FROM employees + | WHERE salary == 50000 + | EVAL sa = mv_sort(favorite_numbers, CONCAT("AS", "C")) + | KEEP sa + | LIMIT 1 + + - match: { columns.0.name: "sa" } + - match: { values.0.0: [ 2,3,4 ] } + +--- +MV_SORT with invalid foldable order: + - do: + catch: bad_request + esql.query: + body: + query: | + ROW a = [4, 2, -3, 2] + | EVAL sa = mv_sort(a, CONCAT("ASC", "DESC")) | LIMIT 1 + - match: { error.type: "illegal_argument_exception" } + - contains: { error.reason: "Invalid order value in [mv_sort(a, CONCAT(\"ASC\", \"DESC\"))], expected one of [ASC, DESC] but got [ASCDESC]" } + + +--- +MV_SORT with invalid non-foldable order: + - do: + catch: bad_request + esql.query: + body: + query: | + ROW a = [4, 2, -3, 2], str = "REVERSE" + | EVAL sa = mv_sort(a, CONCAT(str, "DESC")) | LIMIT 1 + - match: { error.type: "illegal_argument_exception" } + - contains: { error.reason: "Invalid order value in [mv_sort(a, CONCAT(str, \"DESC\"))], expected one of [ASC, DESC] but got [REVERSEDESC]" } + +--- +CIDR_MATCH with multiple CIDRs: + - do: + esql.query: + body: + query: | + ROW ip1 = "127.0.0.3" + | WHERE CIDR_MATCH(TO_IP(ip1), CONCAT("127", ".0.0.2/32"), "127.0.0.3/32") | LIMIT 5 + - match: { columns.0.name: "ip1" } + - match: { values.0.0: "127.0.0.3" } + +--- +CIDR_MATCH with non-foldable CIDRs: + - do: + esql.query: + body: + query: | + FROM employees + | WHERE CIDR_MATCH(ip, CONCAT(TO_STRING(ip), "/8"), "127.0.0.3/32") | LIMIT 5 + - length: { values: 3 } + +--- +Employee name equals: + - do: + esql.query: + body: + query: | + FROM employees + | WHERE CONCAT("Alice"," Smith") == name + | KEEP hire_date, salary, name + | LIMIT 5 + - match: { columns.0.name: "hire_date" } + - match: { columns.1.name: "salary" } + - match: { columns.2.name: "name" } + - length: { values: 1 } + - match: { values.0.2: "Alice Smith" } + +--- +Employee name in list: + - do: + esql.query: + body: + query: | + FROM employees + | WHERE name in (CONCAT("Alice"," Smith"), CONCAT("fake", " name")) + | KEEP hire_date, salary, name + | LIMIT 5 + - match: { columns.0.name: "hire_date" } + - match: { columns.1.name: "salary" } + - match: { columns.2.name: "name" } + - length: { values: 1 } + - match: { values.0.2: "Alice Smith" }