Skip to content

Commit 193d8f7

Browse files
non-correlated subquery in from command
1 parent f15018c commit 193d8f7

File tree

35 files changed

+6373
-2592
lines changed

35 files changed

+6373
-2592
lines changed

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/expression/Attribute.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ public abstract class Attribute extends NamedExpression {
3535
/**
3636
* Changing this will break bwc with 8.15, see {@link FieldAttribute#fieldName()}.
3737
*/
38-
protected static final String SYNTHETIC_ATTRIBUTE_NAME_PREFIX = "$$";
38+
public static final String SYNTHETIC_ATTRIBUTE_NAME_PREFIX = "$$";
39+
40+
public static final String SYNTHETIC_ATTRIBUTE_NAME_SEPARATOR = "$";
3941

4042
private static final TransportVersion ESQL_QUALIFIERS_IN_ATTRIBUTES = TransportVersion.fromName("esql_qualifiers_in_attributes");
4143

@@ -77,7 +79,7 @@ public Attribute(
7779
}
7880

7981
public static String rawTemporaryName(String... parts) {
80-
var name = String.join("$", parts);
82+
var name = String.join(SYNTHETIC_ATTRIBUTE_NAME_SEPARATOR, parts);
8183
return name.isEmpty() || name.startsWith(SYNTHETIC_ATTRIBUTE_NAME_PREFIX) ? name : SYNTHETIC_ATTRIBUTE_NAME_PREFIX + name;
8284
}
8385

x-pack/plugin/esql/qa/server/src/main/java/org/elasticsearch/xpack/esql/qa/rest/generative/GenerativeForkRestTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ protected void shouldSkipTest(String testName) throws IOException {
5555
testCase.requiredCapabilities.contains(UNMAPPED_FIELDS.capabilityName())
5656
);
5757

58+
assumeFalse(
59+
"Tests using subqueries are skipped since we don't support nested subqueries",
60+
testCase.requiredCapabilities.contains(SUBQUERY_IN_FROM_COMMAND.capabilityName())
61+
);
62+
5863
assumeTrue("Cluster needs to support FORK", hasCapabilities(adminClient(), List.of(FORK_V9.capabilityName())));
5964
}
6065
}

x-pack/plugin/esql/qa/testFixtures/src/main/resources/subquery.csv-spec

Lines changed: 805 additions & 0 deletions
Large diffs are not rendered by default.

x-pack/plugin/esql/src/main/antlr/EsqlBaseParser.g4

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,8 +108,21 @@ timeSeriesCommand
108108
: TS indexPatternAndMetadataFields
109109
;
110110

111-
indexPatternAndMetadataFields:
112-
indexPattern (COMMA indexPattern)* metadata?
111+
indexPatternAndMetadataFields
112+
: indexPatternOrSubquery (COMMA indexPatternOrSubquery)* metadata?
113+
;
114+
115+
indexPatternOrSubquery
116+
: indexPattern
117+
| {this.isDevVersion()}? subquery
118+
;
119+
120+
subquery
121+
: LP fromCommand (PIPE subqueryProcessingCommand)* RP
122+
;
123+
124+
subqueryProcessingCommand
125+
: processingCommand
113126
;
114127

115128
indexPattern

x-pack/plugin/esql/src/main/antlr/lexer/From.g4

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@ FROM_ASSIGN : ASSIGN -> type(ASSIGN);
2323
METADATA : 'metadata';
2424

2525
// we need this for EXPLAIN
26-
FROM_RP : RP -> type(RP), popMode;
26+
// change to double popMode to accommodate subquerys in FROM, when see ')' pop out of subquery(default) mode and from mode
27+
FROM_RP : RP -> type(RP), popMode, popMode;
28+
29+
// accommodate subQuery inside FROM
30+
FROM_LP : LP -> type(LP), pushMode(DEFAULT_MODE);
2731

2832
// in 8.14 ` were not allowed
2933
// this has been relaxed in 8.15 since " is used for quoting

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,11 @@ public enum Cap {
11201120
*/
11211121
FORK_UNION_TYPES,
11221122

1123+
/**
1124+
* Support non-correlated subqueries in the FROM clause.
1125+
*/
1126+
SUBQUERY_IN_FROM_COMMAND(Build.current().isSnapshot()),
1127+
11231128
/**
11241129
* Support for the {@code leading_zeros} named parameter.
11251130
*/

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ public boolean includeCCSMetadata() {
151151
return includeCCSMetadata;
152152
}
153153

154+
public Predicate<String> skipOnFailurePredicate() {
155+
return skipOnFailurePredicate;
156+
}
157+
154158
/**
155159
* Call when ES|QL "planning" phase is complete and query execution (in ComputeService) is about to start.
156160
* Note this is currently only built for a single phase planning/execution model. When INLINE STATS

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

Lines changed: 346 additions & 8 deletions
Large diffs are not rendered by default.

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ public record AnalyzerContext(
2020
IndexResolution indexResolution,
2121
Map<String, IndexResolution> lookupResolution,
2222
EnrichResolution enrichResolution,
23-
InferenceResolution inferenceResolution
23+
InferenceResolution inferenceResolution,
24+
Map<String, IndexResolution> subqueryResolution
2425
) {
2526
// Currently for tests only, since most do not test lookups
2627
// TODO: make this even simpler, remove the enrichResolution for tests that do not require it (most tests)
@@ -31,6 +32,17 @@ public AnalyzerContext(
3132
EnrichResolution enrichResolution,
3233
InferenceResolution inferenceResolution
3334
) {
34-
this(configuration, functionRegistry, indexResolution, Map.of(), enrichResolution, inferenceResolution);
35+
this(configuration, functionRegistry, indexResolution, Map.of(), enrichResolution, inferenceResolution, Map.of());
36+
}
37+
38+
public AnalyzerContext(
39+
Configuration configuration,
40+
EsqlFunctionRegistry functionRegistry,
41+
IndexResolution indexResolution,
42+
Map<String, IndexResolution> lookupResolution,
43+
EnrichResolution enrichResolution,
44+
InferenceResolution inferenceResolution
45+
) {
46+
this(configuration, functionRegistry, indexResolution, lookupResolution, enrichResolution, inferenceResolution, Map.of());
3547
}
3648
}

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

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
package org.elasticsearch.xpack.esql.analysis;
99

1010
import org.elasticsearch.index.IndexMode;
11+
import org.elasticsearch.xpack.esql.action.EsqlCapabilities;
1112
import org.elasticsearch.xpack.esql.core.util.Holder;
1213
import org.elasticsearch.xpack.esql.expression.function.UnresolvedFunction;
1314
import org.elasticsearch.xpack.esql.plan.IndexPattern;
@@ -16,7 +17,9 @@
1617
import org.elasticsearch.xpack.esql.plan.logical.UnresolvedRelation;
1718

1819
import java.util.ArrayList;
20+
import java.util.HashSet;
1921
import java.util.List;
22+
import java.util.Set;
2023

2124
/**
2225
* This class is part of the planner. Acts somewhat like a linker, to find the indices and enrich policies referenced by the query.
@@ -29,9 +32,10 @@ public record PreAnalysis(
2932
List<Enrich> enriches,
3033
List<IndexPattern> lookupIndices,
3134
boolean supportsAggregateMetricDouble,
32-
boolean supportsDenseVector
35+
boolean supportsDenseVector,
36+
Set<IndexPattern> subqueryIndices
3337
) {
34-
public static final PreAnalysis EMPTY = new PreAnalysis(null, null, List.of(), List.of(), false, false);
38+
public static final PreAnalysis EMPTY = new PreAnalysis(null, null, List.of(), List.of(), false, false, Set.of());
3539
}
3640

3741
public PreAnalysis preAnalyze(LogicalPlan plan) {
@@ -46,12 +50,18 @@ protected PreAnalysis doPreAnalyze(LogicalPlan plan) {
4650
Holder<IndexMode> indexMode = new Holder<>();
4751
Holder<IndexPattern> index = new Holder<>();
4852
List<IndexPattern> lookupIndices = new ArrayList<>();
53+
Set<IndexPattern> subqueryIndices = new HashSet<>();
4954
plan.forEachUp(UnresolvedRelation.class, p -> {
5055
if (p.indexMode() == IndexMode.LOOKUP) {
5156
lookupIndices.add(p.indexPattern());
5257
} else if (indexMode.get() == null || indexMode.get() == p.indexMode()) {
5358
indexMode.set(p.indexMode());
54-
index.set(p.indexPattern());
59+
// the index pattern from main query is always the first to be seen
60+
index.setIfAbsent(p.indexPattern());
61+
// collect subquery index patterns
62+
if (EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled()) {
63+
collectSubqueryIndexPattern(p, subqueryIndices, index.get());
64+
}
5565
} else {
5666
throw new IllegalStateException("index mode is already set");
5767
}
@@ -96,7 +106,32 @@ protected PreAnalysis doPreAnalyze(LogicalPlan plan) {
96106
unresolvedEnriches,
97107
lookupIndices,
98108
indexMode.get() == IndexMode.TIME_SERIES || supportsAggregateMetricDouble.get(),
99-
supportsDenseVector.get()
109+
supportsDenseVector.get(),
110+
subqueryIndices
100111
);
101112
}
113+
114+
private void collectSubqueryIndexPattern(
115+
UnresolvedRelation relation,
116+
Set<IndexPattern> subqueryIndices,
117+
IndexPattern mainIndexPattern
118+
) {
119+
if (relation.preAnalyzed()) {
120+
return;
121+
}
122+
123+
IndexPattern pattern = relation.indexPattern();
124+
boolean isLookup = relation.indexMode() == IndexMode.LOOKUP;
125+
boolean isMainIndexPattern = pattern == mainIndexPattern;
126+
/*if the subquery's index pattern is the same as the main query, it won't be added
127+
* to the subquery indices set, if Analyzer doesn't find the subquery' indexResolution,
128+
* it falls back to the main query's indexResolution
129+
*/
130+
if (isLookup || isMainIndexPattern) {
131+
return;
132+
}
133+
subqueryIndices.add(pattern);
134+
System.out.println("collected subquery index pattern: " + pattern);
135+
System.out.println("subquery indices now: " + subqueryIndices);
136+
}
102137
}

0 commit comments

Comments
 (0)