Class InSubqueryResolver
InSubquery expressions in Filter conditions by rewriting them into
SemiJoin, AntiJoin, or MarkJoin nodes depending on where the
InSubquery sits inside the boolean expression:
- An
InSubquery(optionally wrapped inNot) at the top of an AND-conjunct becomes a row-filteringSemiJoin/AntiJoinstacked on top of the remaining filter — the most efficient shape, used for the common conjunctive case. - An
InSubquerythat appears as a child ofOr(or ofNotbelow anOr) is replaced with a synthetic boolean attribute and aMarkJoinis stacked below the rewrittenFilter; the mark attribute carries the three-valuedINresult up into normal boolean evaluation. - An
InSubquerywrapped in any other expression (a function argument, anIS NOT NULL, an arithmetic operator, etc.) is left in place; the post-resolutionverify(org.elasticsearch.xpack.esql.plan.logical.LogicalPlan)step rejects the query with aVerificationException.
This runs before PreAnalyzer so the subquery plans, originally embedded inside
InSubquery expressions, become children of join nodes and visible to standard plan
traversals. This eliminates the need for separate InSubquery-aware traversals in
PreAnalyzer, FieldNameUtils,
and InferenceService.
The join's rightFields are left empty at this stage because the subquery output is not
yet resolved. The Analyzer's ResolveRefs fills them in during the Resolution batch.
-
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionstatic booleanstatic LogicalPlanresolve(LogicalPlan plan) Resolves allInSubqueryexpressions inFilterconditions and validates the result.static LogicalPlanresolveInSubqueryInFilter(Filter filter) Make this public, so thatViewResolvercan drive IN subquery resolution.static voidverify(LogicalPlan plan)
-
Constructor Details
-
InSubqueryResolver
public InSubqueryResolver()
-
-
Method Details
-
resolve
Resolves allInSubqueryexpressions inFilterconditions and validates the result. Throws aVerificationExceptionwhen anInSubquerysurvived rewriting (e.g. inside an EVAL, SORT, STATS BY clause, or wrapped in a non-boolean expression).Synchronous — does no I/O. Async callers should invoke this inside an
delegateFailureAndWraplambda so the thrownVerificationExceptionis routed toonFailure.Telemetry for
IN_SUBQUERYis collected separately by the session — seeEsqlSession#gatherInSubqueryMetrics, which useshasInSubqueryInFilter(org.elasticsearch.xpack.esql.plan.logical.LogicalPlan)on the pre-resolution plan because by the time this method returns the originatingInSubqueryexpressions have been replaced withSemiJoin/AntiJoin/MarkJoinand are no longer visible to plan traversals. TheWHEREcounter still picks up SemiJoin/AntiJoin/MarkJoin in the post-resolution plan walk (seeFeatureMetric#WHERE), so theWHEREbit does not need to be set up-front here. -
hasInSubqueryInFilter
Returnstrueif the pre-resolution plan contains anyInSubqueryexpression inside aFilter(i.e. as part of aWHEREcondition). Used by the session to decide whether to increment theIN_SUBQUERYtelemetry counter — once per query, in the same spirit asEsqlSession#gatherViewMetrics.Restricted to
Filterconditions becauseInSubqueryoccurrences elsewhere (EVAL, SORT, STATS BY, etc.) are rejected byverify(org.elasticsearch.xpack.esql.plan.logical.LogicalPlan)today. -
resolveInSubqueryInFilter
Make this public, so thatViewResolvercan drive IN subquery resolution. -
verify
-