Skip to content

Commit 83353fb

Browse files
add subquery serialization tests and add comments to each subquery test in PushDownAndCombineFiltersTests to include the plan strings
1 parent 7f4f372 commit 83353fb

File tree

2 files changed

+183
-12
lines changed

2 files changed

+183
-12
lines changed

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PushDownAndCombineFiltersTests.java

Lines changed: 150 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,6 +1014,27 @@ public void testPushDownLookupJoinExpressionMultipleWhere() {
10141014
assertEquals(expectedPushedFilters, actualPushedFilters);
10151015
}
10161016

1017+
/**
1018+
* Limit[1000[INTEGER],false]
1019+
* \_UnionAll[[_meta_field{r}#44, emp_no{r}#45, first_name{r}#46, gender{r}#47, hire_date{r}#48, job{r}#49, job.raw{r}#50, l
1020+
* anguages{r}#51, last_name{r}#52, long_noidx{r}#53, salary{r}#54, language_code{r}#55, language_name{r}#56]]
1021+
* |_EsqlProject[[_meta_field{f}#11, emp_no{f}#5, first_name{f}#6, gender{f}#7, hire_date{f}#12, job{f}#13, job.raw{f}#14, lang
1022+
* uages{f}#8, last_name{f}#9, long_noidx{f}#15, salary{f}#10, language_code{r}#29, language_name{r}#30]]
1023+
* | \_Eval[[null[INTEGER] AS language_code#29, null[KEYWORD] AS language_name#30]]
1024+
* | \_Limit[1000[INTEGER],false]
1025+
* | \_Filter[emp_no{f}#5 > 10000[INTEGER]]
1026+
* | \_EsRelation[test][_meta_field{f}#11, emp_no{f}#5, first_name{f}#6, ge..]
1027+
* |_EsqlProject[[_meta_field{f}#22, emp_no{f}#16, first_name{f}#17, gender{f}#18, hire_date{f}#23, job{f}#24, job.raw{f}#25, l
1028+
* anguages{f}#19, last_name{f}#20, long_noidx{f}#26, salary{f}#21, language_code{r}#31, language_name{r}#32]]
1029+
* | \_Eval[[null[INTEGER] AS language_code#31, null[KEYWORD] AS language_name#32]]
1030+
* | \_Limit[1000[INTEGER],false]
1031+
* | \_Subquery[]
1032+
* | \_Filter[languages{f}#19 > 0[INTEGER] AND emp_no{f}#16 > 10000[INTEGER]]
1033+
* | \_EsRelation[test1][_meta_field{f}#22, emp_no{f}#16, first_name{f}#17, ..]
1034+
* \_LocalRelation[[_meta_field{r}#33, emp_no{r}#34, first_name{r}#35, gender{r}#36, hire_date{r}#37, job{r}#38, job.raw{r}#39, l
1035+
* anguages{r}#40, last_name{r}#41, long_noidx{r}#42, salary{r}#43, language_code{f}#27, language_name{f}#28],EMPT
1036+
* Y]
1037+
*/
10171038
public void testPushDownSimpleFilterPastUnionAll() {
10181039
assumeTrue("Requires subquery in FROM command support", EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled());
10191040
var plan = planSubquery("""
@@ -1059,6 +1080,27 @@ public void testPushDownSimpleFilterPastUnionAll() {
10591080
LocalRelation localRelation = as(unionAll.children().get(2), LocalRelation.class);
10601081
}
10611082

1083+
/**
1084+
* Limit[1000[INTEGER],false]
1085+
* \_UnionAll[[_meta_field{r}#45, emp_no{r}#46, first_name{r}#47, gender{r}#48, hire_date{r}#49, job{r}#50, job.raw{r}#51, l
1086+
* anguages{r}#52, last_name{r}#53, long_noidx{r}#54, salary{r}#55, language_code{r}#56, language_name{r}#57]]
1087+
* |_EsqlProject[[_meta_field{f}#12, emp_no{f}#6, first_name{f}#7, gender{f}#8, hire_date{f}#13, job{f}#14, job.raw{f}#15, lang
1088+
* uages{f}#9, last_name{f}#10, long_noidx{f}#16, salary{f}#11, language_code{r}#30, language_name{r}#31]]
1089+
* | \_Eval[[null[INTEGER] AS language_code#30, null[KEYWORD] AS language_name#31]]
1090+
* | \_Limit[1000[INTEGER],false]
1091+
* | \_Filter[emp_no{f}#6 > 10000[INTEGER] AND salary{f}#11 > 50000[INTEGER]]
1092+
* | \_EsRelation[test][_meta_field{f}#12, emp_no{f}#6, first_name{f}#7, ge..]
1093+
* |_EsqlProject[[_meta_field{f}#23, emp_no{f}#17, first_name{f}#18, gender{f}#19, hire_date{f}#24, job{f}#25, job.raw{f}#26, l
1094+
* anguages{f}#20, last_name{f}#21, long_noidx{f}#27, salary{f}#22, language_code{r}#32, language_name{r}#33]]
1095+
* | \_Eval[[null[INTEGER] AS language_code#32, null[KEYWORD] AS language_name#33]]
1096+
* | \_Limit[1000[INTEGER],false]
1097+
* | \_Subquery[]
1098+
* | \_Filter[languages{f}#20 > 0[INTEGER] AND emp_no{f}#17 > 10000[INTEGER] AND salary{f}#22 > 50000[INTEGER]]
1099+
* | \_EsRelation[test1][_meta_field{f}#23, emp_no{f}#17, first_name{f}#18, ..]
1100+
* \_LocalRelation[[_meta_field{r}#34, emp_no{r}#35, first_name{r}#36, gender{r}#37, hire_date{r}#38, job{r}#39, job.raw{r}#40, l
1101+
* anguages{r}#41, last_name{r}#42, long_noidx{r}#43, salary{r}#44, language_code{f}#28, language_name{f}#29],EMPT
1102+
* Y]
1103+
*/
10621104
public void testPushDownConjunctiveFilterPastUnionAll() {
10631105
assumeTrue("Requires subquery in FROM command support", EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled());
10641106
var plan = planSubquery("""
@@ -1116,6 +1158,27 @@ public void testPushDownConjunctiveFilterPastUnionAll() {
11161158
LocalRelation localRelation = as(unionAll.children().get(2), LocalRelation.class);
11171159
}
11181160

1161+
/**
1162+
* Limit[1000[INTEGER],false]
1163+
* \_UnionAll[[_meta_field{r}#45, emp_no{r}#46, first_name{r}#47, gender{r}#48, hire_date{r}#49, job{r}#50, job.raw{r}#51, l
1164+
* anguages{r}#52, last_name{r}#53, long_noidx{r}#54, salary{r}#55, language_code{r}#56, language_name{r}#57]]
1165+
* |_EsqlProject[[_meta_field{f}#12, emp_no{f}#6, first_name{f}#7, gender{f}#8, hire_date{f}#13, job{f}#14, job.raw{f}#15, lang
1166+
* uages{f}#9, last_name{f}#10, long_noidx{f}#16, salary{f}#11, language_code{r}#30, language_name{r}#31]]
1167+
* | \_Eval[[null[INTEGER] AS language_code#30, null[KEYWORD] AS language_name#31]]
1168+
* | \_Limit[1000[INTEGER],false]
1169+
* | \_Filter[emp_no{f}#6 > 10000[INTEGER] OR salary{f}#11 > 50000[INTEGER]]
1170+
* | \_EsRelation[test][_meta_field{f}#12, emp_no{f}#6, first_name{f}#7, ge..]
1171+
* |_EsqlProject[[_meta_field{f}#23, emp_no{f}#17, first_name{f}#18, gender{f}#19, hire_date{f}#24, job{f}#25, job.raw{f}#26, l
1172+
* anguages{f}#20, last_name{f}#21, long_noidx{f}#27, salary{f}#22, language_code{r}#32, language_name{r}#33]]
1173+
* | \_Eval[[null[INTEGER] AS language_code#32, null[KEYWORD] AS language_name#33]]
1174+
* | \_Limit[1000[INTEGER],false]
1175+
* | \_Subquery[]
1176+
* | \_Filter[languages{f}#20 > 0[INTEGER] AND emp_no{f}#17 > 10000[INTEGER] OR salary{f}#22 > 50000[INTEGER]]
1177+
* | \_EsRelation[test1][_meta_field{f}#23, emp_no{f}#17, first_name{f}#18, ..]
1178+
* \_LocalRelation[[_meta_field{r}#34, emp_no{r}#35, first_name{r}#36, gender{r}#37, hire_date{r}#38, job{r}#39, job.raw{r}#40, l
1179+
* anguages{r}#41, last_name{r}#42, long_noidx{r}#43, salary{r}#44, language_code{f}#28, language_name{f}#29],EMPT
1180+
* Y]
1181+
*/
11191182
public void testPushDownDisjunctiveFilterPastUnionAll() {
11201183
assumeTrue("Requires subquery in FROM command support", EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled());
11211184
var plan = planSubquery("""
@@ -1173,16 +1236,38 @@ public void testPushDownDisjunctiveFilterPastUnionAll() {
11731236
LocalRelation localRelation = as(unionAll.children().get(2), LocalRelation.class);
11741237
}
11751238

1239+
/**
1240+
* Limit[1000[INTEGER],false]
1241+
* \_UnionAll[[_meta_field{r}#45, emp_no{r}#46, first_name{r}#47, gender{r}#48, hire_date{r}#49, job{r}#50, job.raw{r}#51, l
1242+
* anguages{r}#52, last_name{r}#53, long_noidx{r}#54, salary{r}#55, language_code{r}#56, language_name{r}#57]]
1243+
* |_EsqlProject[[_meta_field{f}#12, emp_no{f}#6, first_name{f}#7, gender{f}#8, hire_date{f}#13, job{f}#14, job.raw{f}#15, lang
1244+
* uages{f}#9, last_name{f}#10, long_noidx{f}#16, salary{f}#11, language_code{r}#30, language_name{r}#31]]
1245+
* | \_Eval[[null[INTEGER] AS language_code#30, null[KEYWORD] AS language_name#31]]
1246+
* | \_Limit[1000[INTEGER],false]
1247+
* | \_Filter[emp_no{f}#6 > 10000[INTEGER] AND salary{f}#11 < 50000[INTEGER]]
1248+
* | \_EsRelation[test][_meta_field{f}#12, emp_no{f}#6, first_name{f}#7, ge..]
1249+
* |_EsqlProject[[_meta_field{f}#23, emp_no{f}#17, first_name{f}#18, gender{f}#19, hire_date{f}#24, job{f}#25, job.raw{f}#26, l
1250+
* anguages{f}#20, last_name{f}#21, long_noidx{f}#27, salary{f}#22, language_code{r}#32, language_name{r}#33]]
1251+
* | \_Eval[[null[INTEGER] AS language_code#32, null[KEYWORD] AS language_name#33]]
1252+
* | \_Limit[1000[INTEGER],false]
1253+
* | \_Subquery[]
1254+
* | \_Filter[salary{f}#22 < 50000[INTEGER] AND emp_no{f}#17 > 10000[INTEGER]]
1255+
* | \_EsRelation[test1][_meta_field{f}#23, emp_no{f}#17, first_name{f}#18, ..]
1256+
* \_LocalRelation[[_meta_field{r}#34, emp_no{r}#35, first_name{r}#36, gender{r}#37, hire_date{r}#38, job{r}#39, job.raw{r}#40, l
1257+
* anguages{r}#41, last_name{r}#42, long_noidx{r}#43, salary{r}#44, language_code{f}#28, language_name{f}#29],EMPT
1258+
* Y]
1259+
*/
11761260
public void testPushDownFilterPastUnionAllAndCombineWithFilterInSubquery() {
11771261
assumeTrue("Requires subquery in FROM command support", EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled());
11781262
var plan = planSubquery("""
11791263
FROM test, (FROM test1 | where salary < 100000), (FROM languages | WHERE language_code > 0)
1180-
| WHERE emp_no > 10000 and salary > 50000
1264+
| WHERE emp_no > 10000 and salary < 50000
11811265
""");
11821266

11831267
Limit limit = as(plan, Limit.class);
11841268
UnionAll unionAll = as(limit.child(), UnionAll.class);
11851269
assertEquals(3, unionAll.children().size());
1270+
11861271
EsqlProject child1 = as(unionAll.children().get(0), EsqlProject.class);
11871272
Eval eval = as(child1.child(), Eval.class);
11881273
Limit childLimit = as(eval.child(), Limit.class);
@@ -1193,7 +1278,7 @@ public void testPushDownFilterPastUnionAllAndCombineWithFilterInSubquery() {
11931278
assertEquals("emp_no", empNo.name());
11941279
Literal right = as(emp_no.right(), Literal.class);
11951280
assertEquals(10000, right.value());
1196-
GreaterThan salary = as(and.right(), GreaterThan.class);
1281+
LessThan salary = as(and.right(), LessThan.class);
11971282
FieldAttribute salaryField = as(salary.left(), FieldAttribute.class);
11981283
assertEquals("salary", salaryField.name());
11991284
right = as(salary.right(), Literal.class);
@@ -1207,29 +1292,67 @@ public void testPushDownFilterPastUnionAllAndCombineWithFilterInSubquery() {
12071292
Subquery subquery = as(childLimit.child(), Subquery.class);
12081293
childFilter = as(subquery.child(), Filter.class);
12091294
and = as(childFilter.condition(), And.class);
1210-
And empNoAndSalary = as(and.right(), And.class);
1211-
emp_no = as(empNoAndSalary.left(), GreaterThan.class);
1295+
emp_no = as(and.right(), GreaterThan.class);
12121296
empNo = as(emp_no.left(), FieldAttribute.class);
12131297
assertEquals("emp_no", empNo.name());
12141298
right = as(emp_no.right(), Literal.class);
12151299
assertEquals(10000, right.value());
1216-
salary = as(empNoAndSalary.right(), GreaterThan.class);
1300+
salary = as(and.left(), LessThan.class);
12171301
salaryField = as(salary.left(), FieldAttribute.class);
12181302
assertEquals("salary", salaryField.name());
12191303
right = as(salary.right(), Literal.class);
12201304
assertEquals(50000, right.value());
1221-
LessThan lessThan = as(and.left(), LessThan.class);
1222-
salaryField = as(lessThan.left(), FieldAttribute.class);
1223-
assertEquals("salary", salaryField.name());
1224-
right = as(lessThan.right(), Literal.class);
1225-
assertEquals(100000, right.value());
12261305
relation = as(childFilter.child(), EsRelation.class);
12271306
assertEquals("test1", relation.indexPattern());
12281307

12291308
LocalRelation localRelation = as(unionAll.children().get(2), LocalRelation.class);
12301309
}
12311310

1232-
public void testPushDownFilterOnReferenceAttributesPastUnionAllDebug() {
1311+
/**
1312+
* Limit[1000[INTEGER],false]
1313+
* \_UnionAll[[_meta_field{r}#95, emp_no{r}#96, first_name{r}#97, gender{r}#98, hire_date{r}#99, job{r}#100, job.raw{r}#101,
1314+
* languages{r}#102, last_name{r}#103, long_noidx{r}#104, salary{r}#105, x{r}#106, y{r}#107, z{r}#108, language_name{r}#109]]
1315+
* |_LocalRelation[[_meta_field{f}#44, emp_no{f}#38, first_name{f}#39, gender{f}#40, hire_date{f}#45, job{f}#46, job.raw{f}#47, l
1316+
* anguages{f}#41, last_name{f}#42, long_noidx{f}#48, salary{f}#43, x{r}#75, y{r}#110, z{r}#77, language_name{r}#78],
1317+
* EMPTY]
1318+
* |_EsqlProject[[_meta_field{f}#55, emp_no{f}#49, first_name{f}#50, gender{f}#51, hire_date{f}#56, job{f}#57, job.raw{f}#58, l
1319+
* anguages{f}#52, last_name{f}#53, long_noidx{f}#59, salary{f}#54, x{r}#4, y{r}#111, z{r}#10, language_name{r}#79]]
1320+
* | \_Filter[ISNOTNULL(y{r}#111)]
1321+
* | \_Eval[[null[KEYWORD] AS language_name#79, TOLONG(y{r}#7) AS y#111]]
1322+
* | \_Limit[1000[INTEGER],false]
1323+
* | \_Subquery[]
1324+
* | \_Project[[_meta_field{f}#55, emp_no{f}#49, first_name{f}#50, gender{f}#51, hire_date{f}#56, job{f}#57, job.raw{f}#58, l
1325+
* anguages{f}#52, last_name{f}#53, long_noidx{f}#59, salary{f}#54, x{r}#4, emp_no{f}#49 AS y#7, z{r}#10]]
1326+
* | \_Filter[z{r}#10 &gt; 0[INTEGER]]
1327+
* | \_Eval[[1[INTEGER] AS x#4, emp_no{f}#49 + 1[INTEGER] AS z#10]]
1328+
* | \_Filter[salary{f}#54 &lt; 100000[INTEGER]]
1329+
* | \_EsRelation[test1][_meta_field{f}#55, emp_no{f}#49, first_name{f}#50, ..]
1330+
* |_EsqlProject[[_meta_field{r}#80, emp_no{r}#81, first_name{r}#82, gender{r}#83, hire_date{r}#84, job{r}#85, job.raw{r}#86, l
1331+
* anguages{r}#87, last_name{r}#88, long_noidx{r}#89, salary{r}#90, x{r}#21, y{r}#19, z{r}#16, language_name{r}#91]]
1332+
* | \_Eval[[null[KEYWORD] AS _meta_field#80, null[INTEGER] AS emp_no#81, null[KEYWORD] AS first_name#82, null[TEXT] AS ge
1333+
* nder#83, null[DATETIME] AS hire_date#84, null[TEXT] AS job#85, null[KEYWORD] AS job.raw#86, null[INTEGER] AS languages#87,
1334+
* null[KEYWORD] AS last_name#88, null[LONG] AS long_noidx#89, null[INTEGER] AS salary#90, null[KEYWORD] AS language_name#91]]
1335+
* | \_Limit[1000[INTEGER],false]
1336+
* | \_Subquery[]
1337+
* | \_Eval[[1[INTEGER] AS x#21]]
1338+
* | \_Filter[ISNOTNULL(y{r}#19) AND z{r}#16 &gt; 0[INTEGER]]
1339+
* | \_Aggregate[[language_code{f}#60],[COUNT(*[KEYWORD],true[BOOLEAN]) AS y#19, language_code{f}#60 AS z#16]]
1340+
* | \_EsRelation[languages][language_code{f}#60, language_name{f}#61]
1341+
* \_EsqlProject[[_meta_field{f}#68, emp_no{r}#92, first_name{f}#63, gender{f}#64, hire_date{f}#69, job{f}#70, job.raw{f}#71, l
1342+
* anguages{r}#93, last_name{f}#66, long_noidx{f}#72, salary{r}#94, x{r}#28, y{r}#112, z{r}#34, language_name{f}#74]]
1343+
* \_Filter[ISNOTNULL(y{r}#112)]
1344+
* \_Eval[[null[INTEGER] AS emp_no#92, null[INTEGER] AS languages#93, null[INTEGER] AS salary#94, TOLONG(y{r}#31) AS y#1
1345+
* 12]]
1346+
* \_Limit[1000[INTEGER],false]
1347+
* \_Subquery[]
1348+
* \_Project[[_meta_field{f}#68, emp_no{f}#62 AS x#28, first_name{f}#63, gender{f}#64, hire_date{f}#69, job{f}#70, job.raw{
1349+
* f}#71, languages{f}#65 AS z#34, last_name{f}#66, long_noidx{f}#72, salary{f}#67 AS y#31, language_name{f}#74]]
1350+
* \_Join[LEFT,[languages{f}#65],[language_code{f}#73],null]
1351+
* |_Filter[ISNOTNULL(emp_no{f}#62) AND languages{f}#65 &gt; 0[INTEGER]]
1352+
* | \_EsRelation[test1][_meta_field{f}#68, emp_no{f}#62, first_name{f}#63, ..]
1353+
* \_EsRelation[languages_lookup][LOOKUP][language_code{f}#73, language_name{f}#74]
1354+
*/
1355+
public void testPushDownFilterOnReferenceAttributesPastUnionAll() {
12331356
assumeTrue("Requires subquery in FROM command support", EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled());
12341357
var plan = planSubquery("""
12351358
FROM test
@@ -1345,7 +1468,22 @@ public void testPushDownFilterOnReferenceAttributesPastUnionAllDebug() {
13451468
assertEquals("languages_lookup", relation.indexPattern());
13461469
}
13471470

1348-
public void testPushDownFilterOnReferenceAttributesAndFieldAttributesPastUnionAllDebug() {
1471+
/**
1472+
* Limit[1000[INTEGER],false]
1473+
* \_UnionAll[[_meta_field{r}#35, emp_no{r}#36, first_name{r}#37, gender{r}#38, hire_date{r}#39, job{r}#40, job.raw{r}#41, l
1474+
* anguages{r}#42, last_name{r}#43, long_noidx{r}#44, salary{r}#45, x{r}#46, y{r}#47]]
1475+
* |_LocalRelation[[_meta_field{f}#17, emp_no{f}#11, first_name{f}#12, gender{f}#13, hire_date{f}#18, job{f}#19, job.raw{f}#20, l
1476+
* anguages{f}#14, last_name{f}#15, long_noidx{f}#21, salary{f}#16, x{r}#33, y{r}#34],EMPTY]
1477+
* \_EsqlProject[[_meta_field{f}#28, emp_no{f}#22, first_name{f}#23, gender{f}#24, hire_date{f}#29, job{f}#30, job.raw{f}#31, l
1478+
* anguages{f}#25, last_name{f}#26, long_noidx{f}#32, salary{f}#27, x{r}#4, y{r}#7]]
1479+
* \_Limit[1000[INTEGER],false]
1480+
* \_Subquery[]
1481+
* \_Filter[y{r}#7 &gt; 0[INTEGER]]
1482+
* \_Eval[[1[INTEGER] AS x#4, emp_no{f}#22 + 1[INTEGER] AS y#7]]
1483+
* \_Filter[salary{f}#27 &lt; 100000[INTEGER] AND emp_no{f}#22 &gt; 0[INTEGER]]
1484+
* \_EsRelation[test1][_meta_field{f}#28, emp_no{f}#22, first_name{f}#23, ..]
1485+
*/
1486+
public void testPushDownFilterOnReferenceAttributesAndFieldAttributesPastUnionAll() {
13491487
assumeTrue("Requires subquery in FROM command support", EsqlCapabilities.Cap.SUBQUERY_IN_FROM_COMMAND.isEnabled());
13501488
var plan = planSubquery("""
13511489
FROM test, (FROM test1 | where salary < 100000 | EVAL x = 1, y = emp_no + 1)
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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.plan.logical;
9+
10+
import org.elasticsearch.xpack.esql.core.tree.Source;
11+
12+
import java.io.IOException;
13+
14+
public class SubquerySerializationTests extends AbstractLogicalPlanSerializationTests<Subquery> {
15+
@Override
16+
protected Subquery createTestInstance() {
17+
Source source = randomSource();
18+
LogicalPlan child = randomChild(0);
19+
return new Subquery(source, child);
20+
}
21+
22+
@Override
23+
protected Subquery mutateInstance(Subquery instance) throws IOException {
24+
LogicalPlan child = instance.child();
25+
child = randomValueOtherThan(child, () -> randomChild(0));
26+
return new Subquery(instance.source(), child);
27+
}
28+
29+
@Override
30+
protected boolean alwaysEmptySource() {
31+
return true;
32+
}
33+
}

0 commit comments

Comments
 (0)