Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
2125697
Remove folding from Percentile
julian-elastic Jul 24, 2025
d019b65
Remove folding from CountDistinct
julian-elastic Jul 24, 2025
140af7e
Remove folding from MvSort
julian-elastic Jul 24, 2025
588a388
Remove folding from AggregateFunction and CIDRMatch
julian-elastic Jul 24, 2025
cddf15a
bugfixes, add support for IN and CIDRMatch
julian-elastic Jul 24, 2025
1b46d3c
Add support for more functions
julian-elastic Jul 24, 2025
5fa1b5b
Bugfix
julian-elastic Jul 24, 2025
82c88ef
Merge branch 'main' into foldable_part2_v2
julian-elastic Jul 29, 2025
c4f70d6
Address code review comments
julian-elastic Jul 30, 2025
bb86fbb
Update docs/changelog/131870.yaml
julian-elastic Jul 30, 2025
607af02
Merge branch 'main' into foldable_part2_v2
julian-elastic Jul 30, 2025
dcf143b
Merge branch 'main' into foldable_part2_v2
julian-elastic Jul 30, 2025
f89baab
Merge branch 'main' into foldable_part2_v2
julian-elastic Jul 31, 2025
845db85
Delete docs/changelog/131870.yaml
julian-elastic Jul 31, 2025
88edfdf
Merge branch 'main' into foldable_part2_v2
julian-elastic Aug 1, 2025
15439de
Refactoring - Remove FunctionUtils.java and move all functionality to…
julian-elastic Aug 1, 2025
fbfb4ce
Fix MvSort checks
julian-elastic Aug 1, 2025
98cbca7
Merge branch 'main' into foldable_part2_v2
julian-elastic Aug 13, 2025
7761e3f
Fix merge error
julian-elastic Aug 13, 2025
c063010
Address code review comments
julian-elastic Aug 13, 2025
a6e1f91
Address more code review comments
julian-elastic Aug 13, 2025
58b31ed
Merge branch 'main' into foldable_part2_v2
julian-elastic Aug 13, 2025
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -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;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -130,7 +129,7 @@ public List<? extends Expression> 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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -210,16 +210,18 @@ 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);
}
return SUPPLIERS.get(type).apply(precision);
}

private int precisionValue() {
return intValueOf(precision, source().text(), "Precision");
}

@Override
public Expression surrogate() {
var s = source();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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(
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -174,7 +174,7 @@ Expression limitField() {
}

private int limitValue() {
return FunctionUtils.limitValue(limitField(), sourceText());
return Foldables.limitValue(limitField(), sourceText());
}

Expression uuid() {
Expand All @@ -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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -123,7 +123,7 @@ Expression orderField() {
}

private Integer limitValue() {
return FunctionUtils.limitValue(limitField(), sourceText());
return Foldables.limitValue(limitField(), sourceText());
}

private boolean orderValue() {
Expand Down Expand Up @@ -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()));
}

/**
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -95,7 +95,7 @@ protected NodeInfo<? extends Expression> 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;

/**
Expand Down Expand Up @@ -405,7 +405,7 @@ public BiConsumer<LogicalPlan, Failures> 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) {
Expand Down
Loading