Skip to content

Commit b5829f9

Browse files
committed
ESQL: Add extension point for extra plan checks
1 parent 1154086 commit b5829f9

File tree

10 files changed

+57
-14
lines changed

10 files changed

+57
-14
lines changed

benchmarks/src/main/java/org/elasticsearch/benchmark/_nightly/esql/QueryPlanningBenchmark.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
import org.openjdk.jmh.infra.Blackhole;
4848

4949
import java.util.LinkedHashMap;
50+
import java.util.List;
5051
import java.util.Locale;
5152
import java.util.Map;
5253
import java.util.concurrent.TimeUnit;
@@ -110,7 +111,7 @@ public void setup() {
110111
new EnrichResolution(),
111112
InferenceResolution.EMPTY
112113
),
113-
new Verifier(new Metrics(functionRegistry), new XPackLicenseState(() -> 0L))
114+
new Verifier(List.of(), new Metrics(functionRegistry), new XPackLicenseState(() -> 0L))
114115
);
115116
defaultOptimizer = new LogicalPlanOptimizer(new LogicalOptimizerContext(config, FoldContext.small()));
116117
}

x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/EsqlTestUtils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ public static LogicalOptimizerContext unboundLogicalOptimizerContext() {
384384
return new LogicalOptimizerContext(EsqlTestUtils.TEST_CFG, FoldContext.small());
385385
}
386386

387-
public static final Verifier TEST_VERIFIER = new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L));
387+
public static final Verifier TEST_VERIFIER = new Verifier(List.of(), new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L));
388388

389389
public static final TransportActionServices MOCK_TRANSPORT_ACTION_SERVICES = new TransportActionServices(
390390
mock(TransportService.class),

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

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,23 @@
5454
* step does type resolution and fails queries based on invalid type expressions.
5555
*/
5656
public class Verifier {
57+
public interface ExtraCheckers {
58+
/**
59+
* Build a list of checks to perform on the plan. Each one is called once per
60+
* {@link LogicalPlan} node in the plan.
61+
*/
62+
List<BiConsumer<LogicalPlan, Failures>> extra();
63+
}
5764

65+
/**
66+
* Extra plan verification checks defined in plugins.
67+
*/
68+
private final List<ExtraCheckers> extraCheckers;
5869
private final Metrics metrics;
5970
private final XPackLicenseState licenseState;
6071

61-
public Verifier(Metrics metrics, XPackLicenseState licenseState) {
72+
public Verifier(List<ExtraCheckers> extraCheckers, Metrics metrics, XPackLicenseState licenseState) {
73+
this.extraCheckers = extraCheckers;
6274
this.metrics = metrics;
6375
this.licenseState = licenseState;
6476
}
@@ -84,6 +96,9 @@ Collection<Failure> verify(LogicalPlan plan, BitSet partialMetrics) {
8496

8597
// collect plan checkers
8698
var planCheckers = planCheckers(plan);
99+
for (ExtraCheckers e : extraCheckers) {
100+
planCheckers.addAll(e.extra());
101+
}
87102

88103
// Concrete verifications
89104
plan.forEachDown(p -> {
@@ -186,6 +201,9 @@ else if (p instanceof Lookup lookup) {
186201
});
187202
}
188203

204+
/**
205+
* Build a list of checkers based on the components in the plan.
206+
*/
189207
private static List<BiConsumer<LogicalPlan, Failures>> planCheckers(LogicalPlan plan) {
190208
List<BiConsumer<LogicalPlan, Failures>> planCheckers = new ArrayList<>();
191209
Consumer<? super Node<?>> collectPlanCheckers = p -> {

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/execution/PlanExecutor.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
import org.elasticsearch.xpack.esql.action.EsqlQueryRequest;
1616
import org.elasticsearch.xpack.esql.analysis.PreAnalyzer;
1717
import org.elasticsearch.xpack.esql.analysis.Verifier;
18+
import org.elasticsearch.xpack.esql.common.Failures;
1819
import org.elasticsearch.xpack.esql.core.expression.FoldContext;
1920
import org.elasticsearch.xpack.esql.enrich.EnrichPolicyResolver;
2021
import org.elasticsearch.xpack.esql.expression.function.EsqlFunctionRegistry;
2122
import org.elasticsearch.xpack.esql.optimizer.LogicalOptimizerContext;
2223
import org.elasticsearch.xpack.esql.optimizer.LogicalPlanOptimizer;
24+
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
2325
import org.elasticsearch.xpack.esql.planner.mapper.Mapper;
2426
import org.elasticsearch.xpack.esql.plugin.TransportActionServices;
2527
import org.elasticsearch.xpack.esql.querylog.EsqlQueryLog;
@@ -32,6 +34,9 @@
3234
import org.elasticsearch.xpack.esql.telemetry.PlanTelemetryManager;
3335
import org.elasticsearch.xpack.esql.telemetry.QueryMetric;
3436

37+
import java.util.List;
38+
import java.util.function.BiConsumer;
39+
3540
import static org.elasticsearch.action.ActionListener.wrap;
3641

3742
public class PlanExecutor {
@@ -44,16 +49,24 @@ public class PlanExecutor {
4449
private final Verifier verifier;
4550
private final PlanTelemetryManager planTelemetryManager;
4651
private final EsqlQueryLog queryLog;
52+
private final List<Verifier.ExtraCheckers> extraCheckers;
4753

48-
public PlanExecutor(IndexResolver indexResolver, MeterRegistry meterRegistry, XPackLicenseState licenseState, EsqlQueryLog queryLog) {
54+
public PlanExecutor(
55+
IndexResolver indexResolver,
56+
MeterRegistry meterRegistry,
57+
XPackLicenseState licenseState,
58+
EsqlQueryLog queryLog,
59+
List<Verifier.ExtraCheckers> extraCheckers
60+
) {
4961
this.indexResolver = indexResolver;
5062
this.preAnalyzer = new PreAnalyzer();
5163
this.functionRegistry = new EsqlFunctionRegistry();
5264
this.mapper = new Mapper();
5365
this.metrics = new Metrics(functionRegistry);
54-
this.verifier = new Verifier(metrics, licenseState);
66+
this.verifier = new Verifier(extraCheckers, metrics, licenseState);
5567
this.planTelemetryManager = new PlanTelemetryManager(meterRegistry);
5668
this.queryLog = queryLog;
69+
this.extraCheckers = extraCheckers;
5770
}
5871

5972
public void esql(

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

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
*/
77
package org.elasticsearch.xpack.esql.plugin;
88

9+
import org.apache.lucene.util.SetOnce;
910
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
1011
import org.elasticsearch.cluster.node.DiscoveryNodes;
1112
import org.elasticsearch.common.breaker.CircuitBreaker;
@@ -41,6 +42,7 @@
4142
import org.elasticsearch.features.NodeFeature;
4243
import org.elasticsearch.license.XPackLicenseState;
4344
import org.elasticsearch.plugins.ActionPlugin;
45+
import org.elasticsearch.plugins.ExtensiblePlugin;
4446
import org.elasticsearch.plugins.Plugin;
4547
import org.elasticsearch.rest.RestController;
4648
import org.elasticsearch.rest.RestHandler;
@@ -66,6 +68,7 @@
6668
import org.elasticsearch.xpack.esql.action.RestEsqlListQueriesAction;
6769
import org.elasticsearch.xpack.esql.action.RestEsqlQueryAction;
6870
import org.elasticsearch.xpack.esql.action.RestEsqlStopAsyncAction;
71+
import org.elasticsearch.xpack.esql.analysis.Verifier;
6972
import org.elasticsearch.xpack.esql.enrich.EnrichLookupOperator;
7073
import org.elasticsearch.xpack.esql.enrich.LookupFromIndexOperator;
7174
import org.elasticsearch.xpack.esql.execution.PlanExecutor;
@@ -83,7 +86,7 @@
8386
import java.util.function.Predicate;
8487
import java.util.function.Supplier;
8588

86-
public class EsqlPlugin extends Plugin implements ActionPlugin {
89+
public class EsqlPlugin extends Plugin implements ActionPlugin, ExtensiblePlugin {
8790
public static final boolean INLINESTATS_FEATURE_FLAG = new FeatureFlag("esql_inlinestats").isEnabled();
8891

8992
public static final String ESQL_WORKER_THREAD_POOL_NAME = "esql_worker";
@@ -187,6 +190,8 @@ public class EsqlPlugin extends Plugin implements ActionPlugin {
187190
Setting.Property.Dynamic
188191
);
189192

193+
private SetOnce<List<Verifier.ExtraCheckers>> extraCheckers = new SetOnce<>();
194+
190195
@Override
191196
public Collection<?> createComponents(PluginServices services) {
192197
CircuitBreaker circuitBreaker = services.indicesService().getBigArrays().breakerService().getBreaker("request");
@@ -204,7 +209,8 @@ public Collection<?> createComponents(PluginServices services) {
204209
new IndexResolver(services.client()),
205210
services.telemetryProvider().getMeterRegistry(),
206211
getLicenseState(),
207-
new EsqlQueryLog(services.clusterService().getClusterSettings(), services.slowLogFieldProvider())
212+
new EsqlQueryLog(services.clusterService().getClusterSettings(), services.slowLogFieldProvider()),
213+
extraCheckers.get()
208214
),
209215
new ExchangeService(
210216
services.clusterService().getSettings(),
@@ -334,4 +340,9 @@ public List<ExecutorBuilder<?>> getExecutorBuilders(Settings settings) {
334340
)
335341
);
336342
}
343+
344+
@Override
345+
public void loadExtensions(ExtensionLoader loader) {
346+
extraCheckers.set(loader.loadExtensions(Verifier.ExtraCheckers.class));
347+
}
337348
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/CheckLicenseTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ private static Analyzer analyzer(EsqlFunctionRegistry registry, License.Operatio
9898
defaultEnrichResolution(),
9999
emptyInferenceResolution()
100100
),
101-
new Verifier(new Metrics(new EsqlFunctionRegistry()), getLicenseState(operationMode))
101+
new Verifier(List.of(), new Metrics(new EsqlFunctionRegistry()), getLicenseState(operationMode))
102102
);
103103
}
104104

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/LocalPhysicalPlanOptimizerTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ private Analyzer makeAnalyzer(String mappingFileName, EnrichResolution enrichRes
235235
enrichResolution,
236236
emptyInferenceResolution()
237237
),
238-
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
238+
new Verifier(List.of(), new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
239239
);
240240
}
241241

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/planner/QueryTranslatorTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ private static Analyzer makeAnalyzer(String mappingFileName) {
5454
emptyPolicyResolution(),
5555
emptyInferenceResolution()
5656
),
57-
new Verifier(new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
57+
new Verifier(List.of(), new Metrics(new EsqlFunctionRegistry()), new XPackLicenseState(() -> 0L))
5858
);
5959
}
6060

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/telemetry/PlanExecutorMetricsTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ public void testFailedMetric() {
150150
return null;
151151
}).when(esqlClient).execute(eq(EsqlResolveFieldsAction.TYPE), any(), any());
152152

153-
var planExecutor = new PlanExecutor(indexResolver, MeterRegistry.NOOP, new XPackLicenseState(() -> 0L), mockQueryLog());
153+
var planExecutor = new PlanExecutor(indexResolver, MeterRegistry.NOOP, new XPackLicenseState(() -> 0L), mockQueryLog(), List.of());
154154
var enrichResolver = mockEnrichResolver();
155155

156156
var request = new EsqlQueryRequest();

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/telemetry/VerifierMetricsTests.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ public void testTwoWhereQuery() {
206206

207207
public void testTwoQueriesExecuted() {
208208
Metrics metrics = new Metrics(new EsqlFunctionRegistry());
209-
Verifier verifier = new Verifier(metrics, new XPackLicenseState(() -> 0L));
209+
Verifier verifier = new Verifier(List.of(), metrics, new XPackLicenseState(() -> 0L));
210210
esqlWithVerifier("""
211211
from employees
212212
| where languages > 2
@@ -253,7 +253,7 @@ public void testTwoQueriesExecuted() {
253253

254254
public void testMultipleFunctions() {
255255
Metrics metrics = new Metrics(new EsqlFunctionRegistry());
256-
Verifier verifier = new Verifier(metrics, new XPackLicenseState(() -> 0L));
256+
Verifier verifier = new Verifier(List.of(), metrics, new XPackLicenseState(() -> 0L));
257257
esqlWithVerifier("""
258258
from employees
259259
| where languages > 2
@@ -552,7 +552,7 @@ private Counters esql(String esql, Verifier v) {
552552
Metrics metrics = null;
553553
if (v == null) {
554554
metrics = new Metrics(new EsqlFunctionRegistry());
555-
verifier = new Verifier(metrics, new XPackLicenseState(() -> 0L));
555+
verifier = new Verifier(List.of(), metrics, new XPackLicenseState(() -> 0L));
556556
}
557557
analyzer(verifier).analyze(parser.createStatement(esql));
558558

0 commit comments

Comments
 (0)