Skip to content

Commit 9cac3d2

Browse files
authored
ESQL: Account for missing StubRelation due to SurrogateExpressions replacement (elastic#142882)
1 parent df16cba commit 9cac3d2

5 files changed

Lines changed: 528 additions & 2 deletions

File tree

docs/changelog/142882.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
area: ES|QL
2+
issues:
3+
- 142219
4+
pr: 142882
5+
summary: Account for missing `StubRelation` due to `SurrogateExpressions` replacement
6+
type: bug

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

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5474,3 +5474,262 @@ null | 1
54745474
null | 1
54755475
null | 1
54765476
;
5477+
5478+
constantAggsPruneFieldAggViaKeep
5479+
required_capability: inline_stats_with_constants
5480+
5481+
FROM employees
5482+
| KEEP emp_no, languages
5483+
| INLINE STATS x = MIN(123) + MAX(123), y = MAX(languages)
5484+
| KEEP x, emp_no
5485+
| SORT emp_no
5486+
| LIMIT 3
5487+
;
5488+
5489+
x:i | emp_no:i
5490+
246 | 10001
5491+
246 | 10002
5492+
246 | 10003
5493+
;
5494+
5495+
constantSurrogateAggAndFieldAgg_ByConstantAndLanguageAlias_WithExpression
5496+
required_capability: inline_stats_with_constants
5497+
5498+
FROM employees
5499+
| KEEP emp_no, languages, salary
5500+
| INLINE STATS base = AVG(123), max_salary = MAX(salary) BY k = 1, l = languages
5501+
| EVAL z = base + max_salary
5502+
| KEEP emp_no, l, k, base, max_salary, z
5503+
| SORT emp_no
5504+
| LIMIT 2
5505+
;
5506+
5507+
emp_no:integer | l:integer | k:integer | base:double | max_salary:integer | z:double
5508+
10001 | 2 | 1 | 123.0 | 73578 | 73701.0
5509+
10002 | 5 | 1 | 123.0 | 66817 | 66940.0
5510+
;
5511+
5512+
chainedInlineStats_ConstantAggThenGroupedAgg_UsesConstantInPostEval
5513+
required_capability: inline_stats_with_constants
5514+
5515+
FROM employees
5516+
| KEEP emp_no, languages, salary
5517+
| INLINE STATS const = MIN(123) + MAX(123)
5518+
| INLINE STATS max_salary = MAX(salary) BY l = languages
5519+
| EVAL z = const + max_salary
5520+
| KEEP emp_no, l, z
5521+
| SORT emp_no
5522+
| LIMIT 2
5523+
;
5524+
5525+
emp_no:integer | l:integer | z:integer
5526+
10001 | 2 | 73824
5527+
10002 | 5 | 67063
5528+
;
5529+
5530+
issue142219_minConstant_NoBy
5531+
required_capability: inline_stats_with_constants
5532+
5533+
FROM employees
5534+
| KEEP emp_no
5535+
| INLINE STATS x = MIN(123)
5536+
| SORT emp_no
5537+
| LIMIT 3
5538+
;
5539+
5540+
emp_no:integer | x:integer
5541+
10001 | 123
5542+
10002 | 123
5543+
10003 | 123
5544+
;
5545+
5546+
issue142219_minPlusMaxConstant_NoBy
5547+
required_capability: inline_stats_with_constants
5548+
5549+
FROM employees
5550+
| KEEP emp_no
5551+
| INLINE STATS x = MIN(123) + MAX(123)
5552+
| SORT emp_no
5553+
| LIMIT 3
5554+
;
5555+
5556+
emp_no:integer | x:integer
5557+
10001 | 246
5558+
10002 | 246
5559+
10003 | 246
5560+
;
5561+
5562+
issue142219_minNullPlusMedianNull_WithFieldAgg_PruneFieldAgg
5563+
required_capability: inline_stats_with_constants
5564+
required_capability: fix_agg_on_null_by_replacing_with_eval
5565+
5566+
FROM employees
5567+
| KEEP emp_no, languages
5568+
| INLINE STATS x = MIN(null) + MEDIAN(null), y = PRESENT(languages)
5569+
| KEEP emp_no, x
5570+
| SORT emp_no
5571+
| LIMIT 3
5572+
;
5573+
5574+
emp_no:integer | x:double
5575+
10001 | null
5576+
10002 | null
5577+
10003 | null
5578+
;
5579+
5580+
issue142219_unmappedNullify_MinPlusMedian_PruneFieldAgg
5581+
required_capability: optional_fields_nullify_tech_preview
5582+
required_capability: inline_stats_with_constants
5583+
required_capability: fix_agg_on_null_by_replacing_with_eval
5584+
5585+
SET unmapped_fields="nullify"\;
5586+
FROM employees
5587+
| INLINE STATS x = MIN(foobar) + MEDIAN(foobar), y = PRESENT(languages)
5588+
| KEEP emp_no, x
5589+
| SORT emp_no
5590+
| LIMIT 3
5591+
;
5592+
5593+
emp_no:integer | x:double
5594+
10001 | null
5595+
10002 | null
5596+
10003 | null
5597+
;
5598+
5599+
constantAggAndFilteredFieldAgg_NoBy
5600+
required_capability: inline_stats_with_constants
5601+
5602+
FROM employees
5603+
| KEEP emp_no, salary
5604+
| INLINE STATS const = MIN(123) + MAX(123), x = MAX(salary) WHERE false
5605+
| KEEP emp_no, const, x
5606+
| SORT emp_no
5607+
| LIMIT 2
5608+
;
5609+
5610+
emp_no:integer | const:integer | x:integer
5611+
10001 | 246 | null
5612+
10002 | 246 | null
5613+
;
5614+
5615+
globalAggGroupedByConstant
5616+
required_capability: inline_stats
5617+
5618+
FROM employees
5619+
| KEEP emp_no, salary
5620+
| INLINE STATS max_salary = MAX(salary) BY k = 1
5621+
| KEEP emp_no, k, max_salary
5622+
| SORT emp_no
5623+
| LIMIT 2
5624+
;
5625+
5626+
emp_no:integer | k:integer | max_salary:integer
5627+
10001 | 1 | 74999
5628+
10002 | 1 | 74999
5629+
;
5630+
5631+
rowChainedInlineStats_ConstantThenGroupByIt
5632+
required_capability: inline_stats_with_constants
5633+
5634+
ROW x = 1, y = 2
5635+
| INLINE STATS c = MIN(123) + MAX(123)
5636+
| WHERE c == 246
5637+
| INLINE STATS s = SUM(y) BY k = c
5638+
| KEEP c, k, s
5639+
;
5640+
5641+
c:integer | k:integer | s:long
5642+
246 | 246 | 2
5643+
;
5644+
5645+
inlineStatsFoldableExpressionAggs_NoBy
5646+
required_capability: inline_stats_with_constants
5647+
5648+
FROM employees
5649+
| KEEP emp_no
5650+
| INLINE STATS x = MIN(100 + 23) + MAX(120 + 3)
5651+
| SORT emp_no
5652+
| LIMIT 3
5653+
;
5654+
5655+
emp_no:integer | x:integer
5656+
10001 | 246
5657+
10002 | 246
5658+
10003 | 246
5659+
;
5660+
5661+
inlineStatsFoldableExpressionByConstant_PruneFieldAgg
5662+
required_capability: inline_stats_with_constants
5663+
5664+
FROM employees
5665+
| KEEP emp_no, salary
5666+
| INLINE STATS x = MIN(100 + 23), max_salary = MAX(salary) BY k = 1 + 0
5667+
| KEEP emp_no, k, x
5668+
| SORT emp_no
5669+
| LIMIT 2
5670+
;
5671+
5672+
emp_no:integer | k:integer | x:integer
5673+
10001 | 1 | 123
5674+
10002 | 1 | 123
5675+
;
5676+
5677+
chainedInlineStats_GroupByPrevConstAndLanguages
5678+
required_capability: inline_stats_with_constants
5679+
5680+
FROM employees
5681+
| KEEP emp_no, languages, salary
5682+
| INLINE STATS c = MIN(123) + MAX(123)
5683+
| INLINE STATS max_salary = MAX(salary) BY k = c, l = languages
5684+
| KEEP emp_no, k, l, max_salary
5685+
| SORT emp_no
5686+
| LIMIT 2
5687+
;
5688+
5689+
emp_no:integer | k:integer | l:integer | max_salary:integer
5690+
10001 | 246 | 2 | 73578
5691+
10002 | 246 | 5 | 66817
5692+
;
5693+
5694+
rowInlineStats_FoldableAggsAndChainedGroupBy
5695+
required_capability: inline_stats_with_constants
5696+
5697+
ROW y = 5
5698+
| INLINE STATS c = MIN(1 + 2) + MAX(10 - 7)
5699+
| INLINE STATS s = SUM(y) BY k = c
5700+
| KEEP c, k, s
5701+
;
5702+
5703+
c:integer | k:integer | s:long
5704+
6 | 6 | 5
5705+
;
5706+
5707+
rowInlineStats_FoldableAggs_WithMVs
5708+
required_capability: inline_stats_with_constants
5709+
5710+
ROW x = [1,2,3], y = [100,101,102]
5711+
| INLINE STATS minX = MV_MIN(x) BY x
5712+
;
5713+
5714+
y:i | minX:i | x:i
5715+
[100, 101, 102]|[1, 2, 3] |[1, 2, 3]
5716+
;
5717+
5718+
inlineStats_FoldableAggs_WithMVs
5719+
required_capability: inline_stats_with_constants
5720+
5721+
from employees
5722+
| eval w = [1, 2, 3]
5723+
| keep languages, emp_no, gender, w
5724+
| inline stats w_avg = weighted_avg(mv_min(w), languages) by w
5725+
| sort emp_no
5726+
| limit 5
5727+
;
5728+
5729+
languages:i | emp_no:i | gender:s | w_avg:d | w:i
5730+
2 |10001 |M |[1.0, 1.0, 1.0]|[1, 2, 3]
5731+
5 |10002 |F |[1.0, 1.0, 1.0]|[1, 2, 3]
5732+
4 |10003 |M |[1.0, 1.0, 1.0]|[1, 2, 3]
5733+
5 |10004 |M |[1.0, 1.0, 1.0]|[1, 2, 3]
5734+
1 |10005 |M |[1.0, 1.0, 1.0]|[1, 2, 3]
5735+
;

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
@@ -2193,6 +2193,11 @@ public enum Cap {
21932193
*/
21942194
EXTERNAL_COMMAND(Build.current().isSnapshot()),
21952195

2196+
/**
2197+
* https://github.com/elastic/elasticsearch/issues/142219
2198+
*/
2199+
INLINE_STATS_WITH_CONSTANTS(INLINE_STATS.enabled),
2200+
21962201
// Last capability should still have a comma for fewer merge conflicts when adding new ones :)
21972202
// This comment prevents the semicolon from being on the previous capability when Spotless formats the file.
21982203
;

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/optimizer/rules/logical/PropagateInlineEvals.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,10 +82,20 @@ protected LogicalPlan rule(InlineJoin plan) {
8282
return p;
8383
});
8484

85+
// If no evals were moved, there is nothing to propagate. In particular, INLINE STATS without groupings (or after other rewrites
86+
// that removed the stubbed source) can have no StubRelation on the right side, and attempting to replace it would fail.
87+
if (groupingAlias.isEmpty()) {
88+
return plan;
89+
}
90+
8591
// copy found evals on the left side
86-
if (groupingAlias.size() > 0) {
87-
left = new Eval(plan.source(), plan.left(), groupingAlias);
92+
left = new Eval(plan.source(), plan.left(), groupingAlias);
93+
// if the StubRelation has been optimized away, remove the inline join altogether. This can happen when the aggregation is a
94+
// SurrogateExpression and the aggregation itself is replaced usually by an Eval and Project.
95+
if (right.anyMatch(p -> p instanceof StubRelation) == false) {
96+
return plan.replaceChildren(left, right);
8897
}
98+
8999
// replace the old stub with the new out to capture the new output
90100
return plan.replaceChildren(left, replaceStub(new StubRelation(right.source(), computeOutput(right, left)), right));
91101
}

0 commit comments

Comments
 (0)