-
Notifications
You must be signed in to change notification settings - Fork 25.6k
[ES|QL] Resolve groupings in aggregate before resolving references to groupings in the aggregations #127524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[ES|QL] Resolve groupings in aggregate before resolving references to groupings in the aggregations #127524
Changes from 4 commits
f5377c3
ca1ebee
e8f8c46
9f014a0
d57caf7
827787f
ef989e9
c00d62c
7175648
16de00e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| pr: 127524 | ||
| summary: Resolve groupings in aggregate before resolving references to groupings in | ||
| the aggregations | ||
| area: ES|QL | ||
| type: bug | ||
| issues: [] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -20,6 +20,7 @@ | |
| import org.elasticsearch.xpack.esql.analysis.AnalyzerRules.ParameterizedAnalyzerRule; | ||
| import org.elasticsearch.xpack.esql.common.Failure; | ||
| import org.elasticsearch.xpack.esql.core.capabilities.Resolvables; | ||
| import org.elasticsearch.xpack.esql.core.capabilities.Unresolvable; | ||
| import org.elasticsearch.xpack.esql.core.expression.Alias; | ||
| import org.elasticsearch.xpack.esql.core.expression.Attribute; | ||
| import org.elasticsearch.xpack.esql.core.expression.EmptyAttribute; | ||
|
|
@@ -173,13 +174,13 @@ public class Analyzer extends ParameterizedRuleExecutor<LogicalPlan, AnalyzerCon | |
| ), | ||
| new Batch<>( | ||
| "Resolution", | ||
| new ResolveRefs(), | ||
| /* | ||
| * ImplicitCasting must be before ResolveRefs. Because a reference is created for a Bucket in Aggregate's aggregates, | ||
| * resolving this reference before implicit casting may cause this reference to have customMessage=true, it prevents further | ||
| * attempts to resolve this reference. | ||
| */ | ||
| new ImplicitCasting(), | ||
| new ResolveRefs(), | ||
| new ResolveUnionTypes() // Must be after ResolveRefs, so union types can be found | ||
| ), | ||
| new Batch<>("Finish Analysis", Limiter.ONCE, new AddImplicitLimit(), new AddImplicitForkLimit(), new UnionTypesCleanup()) | ||
|
|
@@ -574,7 +575,7 @@ private Aggregate resolveAggregate(Aggregate aggregate, List<Attribute> children | |
| } | ||
| } | ||
|
|
||
| if (Resolvables.resolved(groupings) == false || (Resolvables.resolved(aggregates) == false)) { | ||
| if (Resolvables.resolved(groupings) == false || Resolvables.resolved(aggregates) == false) { | ||
| ArrayList<Attribute> resolved = new ArrayList<>(); | ||
| for (Expression e : groupings) { | ||
| Attribute attr = Expressions.attribute(e); | ||
|
|
@@ -585,17 +586,38 @@ private Aggregate resolveAggregate(Aggregate aggregate, List<Attribute> children | |
| List<Attribute> resolvedList = NamedExpressions.mergeOutputAttributes(resolved, childrenOutput); | ||
|
|
||
| List<NamedExpression> newAggregates = new ArrayList<>(); | ||
| for (NamedExpression ag : aggregate.aggregates()) { | ||
| var agg = (NamedExpression) ag.transformUp(UnresolvedAttribute.class, ua -> { | ||
| Expression ne = ua; | ||
| Attribute maybeResolved = maybeResolveAttribute(ua, resolvedList); | ||
| if (maybeResolved != null) { | ||
| changed.set(true); | ||
| ne = maybeResolved; | ||
| } | ||
| return ne; | ||
| }); | ||
| newAggregates.add(agg); | ||
| // If the groupings are not resolved, skip the resolution of the references to groupings in the aggregates, resolve the | ||
| // aggregations that do not reference to groupings, so that the fields/attributes referenced by the aggregations can be | ||
| // resolved, and verifier doesn't report field/reference/column not found errors for them. | ||
| boolean groupingResolved = Resolvables.resolved(groupings); | ||
| int size = groupingResolved ? aggregates.size() : aggregates.size() - groupings.size(); | ||
| for (int i = 0; i < aggregates.size(); i++) { | ||
| NamedExpression ag = aggregates.get(i); | ||
|
||
| if (i < size) { | ||
| var agg = (NamedExpression) ag.transformUp(UnresolvedAttribute.class, ua -> { | ||
| Expression ne = ua; | ||
| Attribute maybeResolved = maybeResolveAttribute(ua, resolvedList); | ||
| if (groupingResolved) { | ||
| if (maybeResolved != null) { | ||
| changed.set(true); | ||
| ne = maybeResolved; | ||
| } | ||
| } else { | ||
| // An item in aggregations can reference to groupings explicitly, if groupings are not resolved yet and | ||
| // maybeResolved is not resolved, return the original UnresolvedAttribute, so that it has a another chance | ||
| // to get resolved in the next iteration. | ||
| // For example STATS c = count(emp_no), x = d::int + 1 BY d = (date == "2025-01-01") | ||
| if (maybeResolved instanceof Unresolvable == false) { | ||
| changed.set(true); | ||
| ne = maybeResolved; | ||
| } | ||
| } | ||
|
||
| return ne; | ||
| }); | ||
| newAggregates.add(agg); | ||
| } else { | ||
| newAggregates.add(ag); // Groupings are not resolved | ||
| } | ||
|
||
| } | ||
|
|
||
| // TODO: remove this when Stats interface is removed | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The reason that
ImplicitCastingwas put beforeResolveRefsis exactly because thatresolveAggregatewas attempting to resolve the references to the groupings, before the groupings are resolved, and it causes the references to the groupings marked unresolvable too early. This comment is outdated and removed. Thanks for the reminder!