QL: Introduce infrastructure for surrogate functions (#54795)
Some functions act as shortcuts for more verbose declarations (sometimes with certain constraints). This PR removes the boilerplate around declaring such functions as well as a dedicated rule for the optimizer to perform the actual substitution. Fix #54334 (cherry picked from commit 3231d01b0c583deb89252fafe84db48878da3246)
This commit is contained in:
parent
0013dd4528
commit
99846f47b7
|
@ -6,14 +6,11 @@
|
||||||
|
|
||||||
package org.elasticsearch.xpack.eql.expression.function.scalar.string;
|
package org.elasticsearch.xpack.eql.expression.function.scalar.string;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.eql.EqlIllegalArgumentException;
|
|
||||||
import org.elasticsearch.xpack.eql.util.StringUtils;
|
import org.elasticsearch.xpack.eql.util.StringUtils;
|
||||||
import org.elasticsearch.xpack.ql.expression.Expression;
|
import org.elasticsearch.xpack.ql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.ql.expression.Expressions;
|
|
||||||
import org.elasticsearch.xpack.ql.expression.Expressions.ParamOrdinal;
|
import org.elasticsearch.xpack.ql.expression.Expressions.ParamOrdinal;
|
||||||
import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction;
|
import org.elasticsearch.xpack.ql.expression.function.scalar.ScalarFunction;
|
||||||
import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe;
|
import org.elasticsearch.xpack.ql.expression.function.scalar.BaseSurrogateFunction;
|
||||||
import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate;
|
|
||||||
import org.elasticsearch.xpack.ql.expression.predicate.logical.Or;
|
import org.elasticsearch.xpack.ql.expression.predicate.logical.Or;
|
||||||
import org.elasticsearch.xpack.ql.expression.predicate.regex.Like;
|
import org.elasticsearch.xpack.ql.expression.predicate.regex.Like;
|
||||||
import org.elasticsearch.xpack.ql.tree.NodeInfo;
|
import org.elasticsearch.xpack.ql.tree.NodeInfo;
|
||||||
|
@ -33,7 +30,7 @@ import static org.elasticsearch.xpack.ql.expression.TypeResolutions.isStringAndE
|
||||||
* EQL wildcard function. Matches the form:
|
* EQL wildcard function. Matches the form:
|
||||||
* wildcard(field, "*wildcard*pattern*", ...)
|
* wildcard(field, "*wildcard*pattern*", ...)
|
||||||
*/
|
*/
|
||||||
public class Wildcard extends ScalarFunction {
|
public class Wildcard extends BaseSurrogateFunction {
|
||||||
|
|
||||||
private final Expression field;
|
private final Expression field;
|
||||||
private final List<Expression> patterns;
|
private final List<Expression> patterns;
|
||||||
|
@ -95,26 +92,7 @@ public class Wildcard extends ScalarFunction {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean foldable() {
|
public ScalarFunction makeSubstitute() {
|
||||||
return Expressions.foldable(children()) && asLikes().foldable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object fold() {
|
|
||||||
return asLikes().fold();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Pipe makePipe() {
|
|
||||||
throw new EqlIllegalArgumentException("Wildcard.makePipe() should not be called directly");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ScriptTemplate asScript() {
|
|
||||||
throw new EqlIllegalArgumentException("Wildcard.asScript() should not be called directly");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ScalarFunction asLikes() {
|
|
||||||
ScalarFunction result = null;
|
ScalarFunction result = null;
|
||||||
|
|
||||||
for (Expression pattern: patterns) {
|
for (Expression pattern: patterns) {
|
||||||
|
@ -125,4 +103,4 @@ public class Wildcard extends ScalarFunction {
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
package org.elasticsearch.xpack.eql.optimizer;
|
package org.elasticsearch.xpack.eql.optimizer;
|
||||||
|
|
||||||
import org.elasticsearch.xpack.eql.expression.function.scalar.string.Wildcard;
|
|
||||||
import org.elasticsearch.xpack.eql.util.StringUtils;
|
import org.elasticsearch.xpack.eql.util.StringUtils;
|
||||||
import org.elasticsearch.xpack.ql.expression.Expression;
|
import org.elasticsearch.xpack.ql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.ql.expression.predicate.logical.Not;
|
import org.elasticsearch.xpack.ql.expression.predicate.logical.Not;
|
||||||
|
@ -24,6 +23,7 @@ import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.OptimizerRule;
|
||||||
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.PropagateEquals;
|
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.PropagateEquals;
|
||||||
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.PruneFilters;
|
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.PruneFilters;
|
||||||
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.PruneLiteralsInOrderBy;
|
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.PruneLiteralsInOrderBy;
|
||||||
|
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.ReplaceSurrogateFunction;
|
||||||
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.SetAsOptimized;
|
import org.elasticsearch.xpack.ql.optimizer.OptimizerRules.SetAsOptimized;
|
||||||
import org.elasticsearch.xpack.ql.plan.logical.Filter;
|
import org.elasticsearch.xpack.ql.plan.logical.Filter;
|
||||||
import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan;
|
import org.elasticsearch.xpack.ql.plan.logical.LogicalPlan;
|
||||||
|
@ -39,6 +39,9 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Iterable<RuleExecutor<LogicalPlan>.Batch> batches() {
|
protected Iterable<RuleExecutor<LogicalPlan>.Batch> batches() {
|
||||||
|
Batch substitutions = new Batch("Operator Replacement", Limiter.ONCE,
|
||||||
|
new ReplaceSurrogateFunction());
|
||||||
|
|
||||||
Batch operators = new Batch("Operator Optimization",
|
Batch operators = new Batch("Operator Optimization",
|
||||||
new ConstantFolding(),
|
new ConstantFolding(),
|
||||||
// boolean
|
// boolean
|
||||||
|
@ -49,7 +52,6 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
||||||
new ReplaceNullChecks(),
|
new ReplaceNullChecks(),
|
||||||
new PropagateEquals(),
|
new PropagateEquals(),
|
||||||
new CombineBinaryComparisons(),
|
new CombineBinaryComparisons(),
|
||||||
new ReplaceWildcardFunction(),
|
|
||||||
// prune/elimination
|
// prune/elimination
|
||||||
new PruneFilters(),
|
new PruneFilters(),
|
||||||
new PruneLiteralsInOrderBy()
|
new PruneLiteralsInOrderBy()
|
||||||
|
@ -58,16 +60,7 @@ public class Optimizer extends RuleExecutor<LogicalPlan> {
|
||||||
Batch label = new Batch("Set as Optimized", Limiter.ONCE,
|
Batch label = new Batch("Set as Optimized", Limiter.ONCE,
|
||||||
new SetAsOptimized());
|
new SetAsOptimized());
|
||||||
|
|
||||||
return Arrays.asList(operators, label);
|
return Arrays.asList(substitutions, operators, label);
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class ReplaceWildcardFunction extends OptimizerRule<Filter> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LogicalPlan rule(Filter filter) {
|
|
||||||
return filter.transformExpressionsUp(e -> e instanceof Wildcard ? ((Wildcard) e).asLikes() : e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class ReplaceWildcards extends OptimizerRule<Filter> {
|
private static class ReplaceWildcards extends OptimizerRule<Filter> {
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.xpack.ql.expression.function.scalar;
|
||||||
|
|
||||||
|
import org.elasticsearch.xpack.ql.expression.Expression;
|
||||||
|
import org.elasticsearch.xpack.ql.expression.gen.pipeline.Pipe;
|
||||||
|
import org.elasticsearch.xpack.ql.expression.gen.script.ScriptTemplate;
|
||||||
|
import org.elasticsearch.xpack.ql.tree.Source;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public abstract class BaseSurrogateFunction extends ScalarFunction implements SurrogateFunction {
|
||||||
|
|
||||||
|
private ScalarFunction lazySubstitute;
|
||||||
|
|
||||||
|
public BaseSurrogateFunction(Source source) {
|
||||||
|
super(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseSurrogateFunction(Source source, List<Expression> fields) {
|
||||||
|
super(source, fields);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScalarFunction substitute() {
|
||||||
|
if (lazySubstitute == null) {
|
||||||
|
lazySubstitute = makeSubstitute();
|
||||||
|
}
|
||||||
|
return lazySubstitute;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract ScalarFunction makeSubstitute();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean foldable() {
|
||||||
|
return substitute().foldable();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object fold() {
|
||||||
|
return substitute().fold();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Pipe makePipe() {
|
||||||
|
return substitute().asPipe();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ScriptTemplate asScript() {
|
||||||
|
return substitute().asScript();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.elasticsearch.xpack.ql.expression.function.scalar;
|
||||||
|
|
||||||
|
public interface SurrogateFunction {
|
||||||
|
|
||||||
|
ScalarFunction substitute();
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import org.elasticsearch.xpack.ql.expression.Expression;
|
||||||
import org.elasticsearch.xpack.ql.expression.Expressions;
|
import org.elasticsearch.xpack.ql.expression.Expressions;
|
||||||
import org.elasticsearch.xpack.ql.expression.Literal;
|
import org.elasticsearch.xpack.ql.expression.Literal;
|
||||||
import org.elasticsearch.xpack.ql.expression.Order;
|
import org.elasticsearch.xpack.ql.expression.Order;
|
||||||
|
import org.elasticsearch.xpack.ql.expression.function.scalar.SurrogateFunction;
|
||||||
import org.elasticsearch.xpack.ql.expression.predicate.BinaryOperator;
|
import org.elasticsearch.xpack.ql.expression.predicate.BinaryOperator;
|
||||||
import org.elasticsearch.xpack.ql.expression.predicate.BinaryPredicate;
|
import org.elasticsearch.xpack.ql.expression.predicate.BinaryPredicate;
|
||||||
import org.elasticsearch.xpack.ql.expression.predicate.Negatable;
|
import org.elasticsearch.xpack.ql.expression.predicate.Negatable;
|
||||||
|
@ -1000,6 +1001,21 @@ public final class OptimizerRules {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class ReplaceSurrogateFunction extends OptimizerExpressionRule {
|
||||||
|
|
||||||
|
public ReplaceSurrogateFunction() {
|
||||||
|
super(TransformDirection.DOWN);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Expression rule(Expression e) {
|
||||||
|
if (e instanceof SurrogateFunction) {
|
||||||
|
e = ((SurrogateFunction) e).substitute();
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static final class PruneFilters extends OptimizerRule<Filter> {
|
public static final class PruneFilters extends OptimizerRule<Filter> {
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue