Skip to content

Commit c17de01

Browse files
authored
ESQL: Add interfaces to distribute the post-analysis verification (#119798) (#120048)
This adds a PostAnalysisVerificationAware interface that allows an expression, plan or even command to perform post-analysis verifications "locally", vs. having them centralized in the core verifier. (cherry picked from commit ad264f7)
1 parent e99dfac commit c17de01

File tree

15 files changed

+869
-700
lines changed

15 files changed

+869
-700
lines changed

docs/changelog/119798.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 119798
2+
summary: "Add a `PostAnalysisAware,` distribute verification"
3+
area: ES|QL
4+
type: enhancement
5+
issues: []

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/analysis/Verifier.java

Lines changed: 57 additions & 689 deletions
Large diffs are not rendered by default.
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.esql.capabilities;
9+
10+
import org.elasticsearch.xpack.esql.common.Failures;
11+
import org.elasticsearch.xpack.esql.expression.function.grouping.GroupingFunction;
12+
import org.elasticsearch.xpack.esql.plan.logical.Aggregate;
13+
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
14+
15+
import java.util.function.BiConsumer;
16+
17+
/**
18+
* Interface implemented by expressions or plans that require validation after query plan analysis,
19+
* when the indices and references have been resolved, but before the plan is transformed further by optimizations.
20+
* The interface is similar to {@link PostAnalysisVerificationAware}, but focused on the tree structure, oftentimes covering semantic
21+
* checks.
22+
*/
23+
public interface PostAnalysisPlanVerificationAware {
24+
25+
/**
26+
* Allows the implementer to return a consumer that will perform self-validation in the context of the tree structure the implementer
27+
* is part of. This usually involves checking the type and configuration of the children or that of the parent.
28+
* <p>
29+
* It is often more useful to perform the checks as extended as it makes sense, over stopping at the first failure. This will allow the
30+
* author to progress faster to a correct query.
31+
* </p>
32+
* <p>
33+
* Example: a {@link GroupingFunction} instance, which models a function to group documents to aggregate over, can only be used in
34+
* the context of the STATS command, modeled by the {@link Aggregate} class. This is how this verification is performed:
35+
* <pre>
36+
* {@code
37+
* @Override
38+
* public BiConsumer<LogicalPlan, Failures> postAnalysisPlanVerification() {
39+
* return (p, failures) -> {
40+
* if (p instanceof Aggregate == false) {
41+
* p.forEachExpression(
42+
* GroupingFunction.class,
43+
* gf -> failures.add(fail(gf, "cannot use grouping function [{}] outside of a STATS command", gf.sourceText()))
44+
* );
45+
* }
46+
* };
47+
* }
48+
* }
49+
* </pre>
50+
*
51+
* @return a consumer that will receive a tree to check and an accumulator of failures found during inspection.
52+
*/
53+
BiConsumer<LogicalPlan, Failures> postAnalysisPlanVerification();
54+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
package org.elasticsearch.xpack.esql.capabilities;
9+
10+
import org.elasticsearch.xpack.esql.common.Failures;
11+
import org.elasticsearch.xpack.esql.plan.logical.Filter;
12+
13+
/**
14+
* Interface implemented by expressions or plans that require validation after query plan analysis,
15+
* when the indices and references have been resolved, but before the plan is transformed further by optimizations.
16+
* The interface is similar to {@link PostAnalysisPlanVerificationAware}, but focused on individual expressions or plans, typically
17+
* covering syntactic checks.
18+
*/
19+
public interface PostAnalysisVerificationAware {
20+
21+
/**
22+
* Allows the implementer to validate itself. This usually involves checking its internal setup, which often means checking the
23+
* parameters it received on construction: their data or syntactic type, class, their count, expressions' structure etc.
24+
* The discovered failures are added to the given {@link Failures} object.
25+
* <p>
26+
* It is often more useful to perform the checks as extended as it makes sense, over stopping at the first failure. This will allow the
27+
* author to progress faster to a correct query.
28+
* </p>
29+
* <p>
30+
* Example: the {@link Filter} class, which models the WHERE command, checks that the expression it filters on - {@code condition}
31+
* - is of a Boolean or NULL type:
32+
* <pre>
33+
* {@code
34+
* @Override
35+
* void postAnalysisVerification(Failures failures) {
36+
* if (condition.dataType() != NULL && condition.dataType() != BOOLEAN) {
37+
* failures.add(fail(condition, "Condition expression needs to be boolean, found [{}]", condition.dataType()));
38+
* }
39+
* }
40+
* }
41+
* </pre>
42+
*
43+
* @param failures the object to add failures to.
44+
*/
45+
void postAnalysisVerification(Failures failures);
46+
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/expression/function/aggregate/AggregateFunction.java

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,32 @@
99
import org.elasticsearch.TransportVersions;
1010
import org.elasticsearch.common.io.stream.StreamInput;
1111
import org.elasticsearch.common.io.stream.StreamOutput;
12+
import org.elasticsearch.xpack.esql.capabilities.PostAnalysisPlanVerificationAware;
13+
import org.elasticsearch.xpack.esql.common.Failures;
1214
import org.elasticsearch.xpack.esql.core.expression.Expression;
1315
import org.elasticsearch.xpack.esql.core.expression.Literal;
1416
import org.elasticsearch.xpack.esql.core.expression.TypeResolutions;
1517
import org.elasticsearch.xpack.esql.core.expression.function.Function;
1618
import org.elasticsearch.xpack.esql.core.tree.Source;
1719
import org.elasticsearch.xpack.esql.core.util.CollectionUtils;
1820
import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput;
21+
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
22+
import org.elasticsearch.xpack.esql.plan.logical.OrderBy;
1923

2024
import java.io.IOException;
2125
import java.util.List;
2226
import java.util.Objects;
27+
import java.util.function.BiConsumer;
2328

2429
import static java.util.Arrays.asList;
2530
import static java.util.Collections.emptyList;
31+
import static org.elasticsearch.xpack.esql.common.Failure.fail;
2632
import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.DEFAULT;
2733

2834
/**
2935
* A type of {@code Function} that takes multiple values and extracts a single value out of them. For example, {@code AVG()}.
3036
*/
31-
public abstract class AggregateFunction extends Function {
37+
public abstract class AggregateFunction extends Function implements PostAnalysisPlanVerificationAware {
3238

3339
private final Expression field;
3440
private final List<? extends Expression> parameters;
@@ -127,4 +133,19 @@ public boolean equals(Object obj) {
127133
}
128134
return false;
129135
}
136+
137+
@Override
138+
public BiConsumer<LogicalPlan, Failures> postAnalysisPlanVerification() {
139+
return (p, failures) -> {
140+
if (p instanceof OrderBy order) {
141+
order.order().forEach(o -> {
142+
o.forEachDown(Function.class, f -> {
143+
if (f instanceof AggregateFunction) {
144+
failures.add(fail(f, "Aggregate functions are not allowed in SORT [{}]", f.functionName()));
145+
}
146+
});
147+
});
148+
}
149+
};
150+
}
130151
}

0 commit comments

Comments
 (0)