Skip to content

Commit 20a8513

Browse files
[ES|QL] Non-Correlated Subquery in FROM command (#135744)
* non-correlated subquery in from command
1 parent c0342b0 commit 20a8513

File tree

52 files changed

+8812
-2691
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

52 files changed

+8812
-2691
lines changed

docs/changelog/135744.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 135744
2+
summary: Non-Correlated Subquery in FROM command
3+
area: ES|QL
4+
type: enhancement
5+
issues: []

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
@@ -74,7 +74,9 @@ public IdIgnoringWrapper ignoreId() {
7474
/**
7575
* Changing this will break bwc with 8.15, see {@link FieldAttribute#fieldName()}.
7676
*/
77-
protected static final String SYNTHETIC_ATTRIBUTE_NAME_PREFIX = "$$";
77+
public static final String SYNTHETIC_ATTRIBUTE_NAME_PREFIX = "$$";
78+
79+
public static final String SYNTHETIC_ATTRIBUTE_NAME_SEPARATOR = "$";
7880

7981
private static final TransportVersion ESQL_QUALIFIERS_IN_ATTRIBUTES = TransportVersion.fromName("esql_qualifiers_in_attributes");
8082

@@ -116,7 +118,7 @@ public Attribute(
116118
}
117119

118120
public static String rawTemporaryName(String... parts) {
119-
var name = String.join("$", parts);
121+
var name = String.join(SYNTHETIC_ATTRIBUTE_NAME_SEPARATOR, parts);
120122
return name.isEmpty() || name.startsWith(SYNTHETIC_ATTRIBUTE_NAME_PREFIX) ? name : SYNTHETIC_ATTRIBUTE_NAME_PREFIX + name;
121123
}
122124

x-pack/plugin/esql/qa/server/multi-clusters/src/javaRestTest/java/org/elasticsearch/xpack/esql/ccq/MultiClusterSpecIT.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_LOOKUP_V12;
5656
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.JOIN_PLANNING_V1;
5757
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.METADATA_FIELDS_REMOTE_TEST;
58+
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND;
5859
import static org.elasticsearch.xpack.esql.action.EsqlCapabilities.Cap.UNMAPPED_FIELDS;
5960
import static org.elasticsearch.xpack.esql.qa.rest.RestEsqlTestCase.hasCapabilities;
6061
import static org.mockito.ArgumentMatchers.any;
@@ -167,6 +168,12 @@ protected void shouldSkipTest(String testName) throws IOException {
167168
assumeFalse("LOOKUP JOIN after SORT not yet supported in CCS", testName.contains("OnTheCoordinator"));
168169

169170
assumeFalse("FORK not yet supported with CCS", testCase.requiredCapabilities.contains(FORK_V9.capabilityName()));
171+
172+
// And convertToRemoteIndices does not generate correct queries with subqueries in the FROM command yet
173+
assumeFalse(
174+
"Subqueries in FROM command not yet supported in CCS",
175+
testCase.requiredCapabilities.contains(SUBQUERY_IN_FROM_COMMAND.capabilityName())
176+
);
170177
}
171178

172179
private TestFeatureService remoteFeaturesService() throws IOException {

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

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1336,6 +1336,89 @@ public void testTopLevelFilterBoolMerged() throws IOException {
13361336
}
13371337
}
13381338

1339+
public void testTopLevelFilterWithSubqueriesInFromCommand() throws IOException {
1340+
assumeTrue("subqueries in from command", EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled());
1341+
1342+
bulkLoadTestData(10);
1343+
1344+
String query = format(null, "FROM {} , (FROM {} | WHERE integer < 8) | STATS count(*)", testIndexName(), testIndexName());
1345+
1346+
RequestObjectBuilder builder = requestObjectBuilder().filter(b -> {
1347+
b.startObject("range");
1348+
{
1349+
b.startObject("integer").field("gte", "5").endObject();
1350+
}
1351+
b.endObject();
1352+
}).query(query);
1353+
1354+
Map<String, Object> result = runEsql(builder);
1355+
assertResultMap(result, matchesList().item(matchesMap().entry("name", "count(*)").entry("type", "long")), List.of(List.of(8)));
1356+
}
1357+
1358+
public void testNestedSubqueries() throws IOException {
1359+
assumeTrue("subqueries in from command", EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled());
1360+
1361+
bulkLoadTestData(10);
1362+
1363+
ResponseException re = expectThrows(
1364+
ResponseException.class,
1365+
() -> runEsqlSync(
1366+
requestObjectBuilder().query(
1367+
format(
1368+
null,
1369+
"from {}, (from {}, (from {} | where integer > 1) | where integer < 8) | stats count(*)",
1370+
testIndexName(),
1371+
testIndexName(),
1372+
testIndexName()
1373+
)
1374+
)
1375+
)
1376+
);
1377+
String error = re.getMessage().replaceAll("\\\\\n\s+\\\\", "");
1378+
assertThat(error, containsString("VerificationException"));
1379+
assertThat(error, containsString("Nested subqueries are not supported"));
1380+
}
1381+
1382+
public void testSubqueryWithFork() throws IOException {
1383+
assumeTrue("subqueries in from command", EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled());
1384+
1385+
bulkLoadTestData(10);
1386+
1387+
ResponseException re = expectThrows(
1388+
ResponseException.class,
1389+
() -> runEsqlSync(
1390+
requestObjectBuilder().query(
1391+
format(
1392+
null,
1393+
"from {}, (from {} | where integer > 1) | fork (where long > 2) (where ip == \"127.0.0.1\") | stats count(*)",
1394+
testIndexName(),
1395+
testIndexName()
1396+
)
1397+
)
1398+
)
1399+
);
1400+
String error = re.getMessage().replaceAll("\\\\\n\s+\\\\", "");
1401+
assertThat(error, containsString("VerificationException"));
1402+
assertThat(error, containsString("FORK after subquery is not supported"));
1403+
1404+
re = expectThrows(
1405+
ResponseException.class,
1406+
() -> runEsqlSync(
1407+
requestObjectBuilder().query(
1408+
format(
1409+
null,
1410+
"from {}, (from {} | where integer > 1 | fork (where long > 2) ( where ip == \"127.0.0.1\")) | stats count(*)",
1411+
testIndexName(),
1412+
testIndexName()
1413+
)
1414+
)
1415+
)
1416+
);
1417+
error = re.getMessage().replaceAll("\\\\\n\s+\\\\", "");
1418+
assertThat(error, containsString("VerificationException"));
1419+
assertThat(error, containsString("FORK inside subquery is not supported"));
1420+
}
1421+
13391422
private static String queryWithComplexFieldNames(int field) {
13401423
StringBuilder query = new StringBuilder();
13411424
query.append(" | keep ").append(randomAlphaOfLength(10)).append(1);

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
}

0 commit comments

Comments
 (0)