Skip to content

Commit 319cc69

Browse files
Add SortAware interface
1 parent f881d2d commit 319cc69

File tree

17 files changed

+124
-67
lines changed

17 files changed

+124
-67
lines changed

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

Lines changed: 20 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,15 @@
88
package org.elasticsearch.xpack.esql.optimizer.rules.logical;
99

1010
import org.elasticsearch.xpack.esql.plan.logical.Aggregate;
11-
import org.elasticsearch.xpack.esql.plan.logical.Drop;
12-
import org.elasticsearch.xpack.esql.plan.logical.Enrich;
13-
import org.elasticsearch.xpack.esql.plan.logical.Eval;
14-
import org.elasticsearch.xpack.esql.plan.logical.Filter;
15-
import org.elasticsearch.xpack.esql.plan.logical.InlineStats;
1611
import org.elasticsearch.xpack.esql.plan.logical.LogicalPlan;
17-
import org.elasticsearch.xpack.esql.plan.logical.Lookup;
18-
import org.elasticsearch.xpack.esql.plan.logical.MvExpand;
1912
import org.elasticsearch.xpack.esql.plan.logical.OrderBy;
20-
import org.elasticsearch.xpack.esql.plan.logical.Project;
21-
import org.elasticsearch.xpack.esql.plan.logical.RegexExtract;
22-
import org.elasticsearch.xpack.esql.plan.logical.Rename;
13+
import org.elasticsearch.xpack.esql.plan.logical.SortAware;
2314
import org.elasticsearch.xpack.esql.plan.logical.TopN;
2415
import org.elasticsearch.xpack.esql.plan.logical.UnaryPlan;
25-
import org.elasticsearch.xpack.esql.plan.logical.join.Join;
2616

27-
import java.util.ArrayList;
17+
import java.util.ArrayDeque;
18+
import java.util.Deque;
2819
import java.util.IdentityHashMap;
29-
import java.util.List;
3020

3121
/**
3222
* SORT cannot be executed without a LIMIT, as ES|QL doesn't support unbounded sort (yet).
@@ -56,7 +46,7 @@ protected LogicalPlan rule(LogicalPlan plan) {
5646
if (redundant.isEmpty()) {
5747
return plan;
5848
}
59-
return plan.transformUp(p -> {
49+
return plan.transformDown(p -> {
6050
if (redundant.containsKey(p)) {
6151
return ((OrderBy) p).child();
6252
}
@@ -67,50 +57,27 @@ protected LogicalPlan rule(LogicalPlan plan) {
6757
}
6858
}
6959

60+
/**
61+
* breadth-first recursion to find redundant SORTs in the children tree.
62+
*/
7063
private IdentityHashMap<OrderBy, Void> findRedundantSort(LogicalPlan plan) {
71-
List<LogicalPlan> toCheck = new ArrayList<>();
72-
toCheck.add(plan);
73-
7464
IdentityHashMap<OrderBy, Void> result = new IdentityHashMap<>();
75-
LogicalPlan p = null;
65+
66+
Deque<LogicalPlan> toCheck = new ArrayDeque<>();
67+
toCheck.push(plan);
68+
7669
while (true) {
77-
if (p == null) {
78-
if (toCheck.isEmpty()) {
79-
return result;
80-
} else {
81-
p = toCheck.remove(0);
82-
}
83-
} else if (p instanceof OrderBy ob) {
70+
if (toCheck.isEmpty()) {
71+
return result;
72+
}
73+
LogicalPlan p = toCheck.pop();
74+
if (p instanceof OrderBy ob) {
8475
result.put(ob, null);
85-
p = ob.child();
86-
} else if (p instanceof UnaryPlan unary) {
87-
if (unary instanceof Project
88-
|| unary instanceof Drop
89-
|| unary instanceof Rename
90-
|| unary instanceof MvExpand
91-
|| unary instanceof Enrich
92-
|| unary instanceof RegexExtract
93-
|| unary instanceof InlineStats
94-
|| unary instanceof Lookup
95-
// IMPORTANT
96-
// If we introduce window functions or order-sensitive aggs (eg. STREAMSTATS),
97-
// the previous sort could actually become relevant
98-
// so we have to be careful with plans that could use them, ie. the following
99-
|| unary instanceof Filter
100-
|| unary instanceof Eval
101-
|| unary instanceof Aggregate) {
102-
p = unary.child();
103-
} else {
104-
// stop here, other unary plans could be sensitive to SORT
105-
p = null;
76+
toCheck.push(ob.child());
77+
} else if (p instanceof SortAware sa && sa.dependsOnInputOrder() == false) {
78+
for (LogicalPlan child : p.children()) {
79+
toCheck.push(child);
10680
}
107-
} else if (p instanceof Join lj) {
108-
toCheck.add(lj.left());
109-
toCheck.add(lj.right());
110-
p = null;
111-
} else {
112-
// stop here, other unary plans could be sensitive to SORT
113-
p = null;
11481
}
11582
}
11683
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Aggregate.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
import static org.elasticsearch.xpack.esql.expression.NamedExpressions.mergeOutputAttributes;
4141
import static org.elasticsearch.xpack.esql.plan.logical.Filter.checkFilterConditionDataType;
4242

43-
public class Aggregate extends UnaryPlan implements PostAnalysisVerificationAware, TelemetryAware {
43+
public class Aggregate extends UnaryPlan implements PostAnalysisVerificationAware, TelemetryAware, SortAware {
4444
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
4545
LogicalPlan.class,
4646
"Aggregate",
@@ -459,4 +459,10 @@ private static void addFailureOnGroupingUsedNakedInAggs(Failures failures, Expre
459459
fail(e, "grouping {} [{}] cannot be used as an aggregate once declared in the STATS BY clause", element, e.sourceText())
460460
);
461461
}
462+
463+
@Override
464+
public boolean dependsOnInputOrder() {
465+
// TODO review this if we introduce expressions that depend on input order (eg. window functions)
466+
return SortAware.super.dependsOnInputOrder();
467+
}
462468
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Drop.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import java.util.List;
1818
import java.util.Objects;
1919

20-
public class Drop extends UnaryPlan implements TelemetryAware {
20+
public class Drop extends UnaryPlan implements TelemetryAware, SortAware {
2121
private final List<NamedExpression> removals;
2222

2323
public Drop(Source source, LogicalPlan child, List<NamedExpression> removals) {

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Enrich.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
import static org.elasticsearch.xpack.esql.core.expression.Expressions.asAttributes;
5050
import static org.elasticsearch.xpack.esql.expression.NamedExpressions.mergeOutputAttributes;
5151

52-
public class Enrich extends UnaryPlan implements GeneratingPlan<Enrich>, PostAnalysisPlanVerificationAware, TelemetryAware {
52+
public class Enrich extends UnaryPlan implements GeneratingPlan<Enrich>, PostAnalysisPlanVerificationAware, TelemetryAware, SortAware {
5353
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
5454
LogicalPlan.class,
5555
"Enrich",

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Eval.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
import static org.elasticsearch.xpack.esql.core.expression.Expressions.asAttributes;
3939
import static org.elasticsearch.xpack.esql.expression.NamedExpressions.mergeOutputAttributes;
4040

41-
public class Eval extends UnaryPlan implements GeneratingPlan<Eval>, PostAnalysisVerificationAware, TelemetryAware {
41+
public class Eval extends UnaryPlan implements GeneratingPlan<Eval>, PostAnalysisVerificationAware, TelemetryAware, SortAware {
4242
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(LogicalPlan.class, "Eval", Eval::new);
4343

4444
private final List<Alias> fields;
@@ -189,4 +189,10 @@ public void postAnalysisVerification(Failures failures) {
189189
});
190190
});
191191
}
192+
193+
@Override
194+
public boolean dependsOnInputOrder() {
195+
// TODO review this if we introduce expressions that depend on input order (eg. window functions)
196+
return SortAware.super.dependsOnInputOrder();
197+
}
192198
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Filter.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
* {@code SELECT x FROM y WHERE z ..} the "WHERE" clause is a Filter. A
3030
* {@code Filter} has a "condition" Expression that does the filtering.
3131
*/
32-
public class Filter extends UnaryPlan implements PostAnalysisVerificationAware, TelemetryAware {
32+
public class Filter extends UnaryPlan implements PostAnalysisVerificationAware, TelemetryAware, SortAware {
3333
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(LogicalPlan.class, "Filter", Filter::new);
3434

3535
private final Expression condition;
@@ -116,4 +116,10 @@ public static void checkFilterConditionDataType(Expression expression, Failures
116116
failures.add(fail(expression, "Condition expression needs to be boolean, found [{}]", expression.dataType()));
117117
}
118118
}
119+
120+
@Override
121+
public boolean dependsOnInputOrder() {
122+
// TODO review this if we introduce expressions that depend on input order (eg. window functions)
123+
return SortAware.super.dependsOnInputOrder();
124+
}
119125
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/InlineStats.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
* underlying aggregate.
3838
* </p>
3939
*/
40-
public class InlineStats extends UnaryPlan implements NamedWriteable, SurrogateLogicalPlan, TelemetryAware {
40+
public class InlineStats extends UnaryPlan implements NamedWriteable, SurrogateLogicalPlan, TelemetryAware, SortAware {
4141
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(
4242
LogicalPlan.class,
4343
"InlineStats",
@@ -143,4 +143,10 @@ public boolean equals(Object obj) {
143143
InlineStats other = (InlineStats) obj;
144144
return Objects.equals(aggregate, other.aggregate);
145145
}
146+
147+
@Override
148+
public boolean dependsOnInputOrder() {
149+
// TODO review this if we introduce expressions that depend on input order (eg. window functions)
150+
return SortAware.super.dependsOnInputOrder();
151+
}
146152
}

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Keep.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import java.util.List;
1616
import java.util.Objects;
1717

18-
public class Keep extends Project implements TelemetryAware {
18+
public class Keep extends Project implements TelemetryAware, SortAware {
1919

2020
public Keep(Source source, LogicalPlan child, List<? extends NamedExpression> projections) {
2121
super(source, child, projections);

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/Lookup.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
* Looks up values from the associated {@code tables}.
3333
* The class is supposed to be substituted by a {@link Join}.
3434
*/
35-
public class Lookup extends UnaryPlan implements SurrogateLogicalPlan, TelemetryAware {
35+
public class Lookup extends UnaryPlan implements SurrogateLogicalPlan, TelemetryAware, SortAware {
3636
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(LogicalPlan.class, "Lookup", Lookup::new);
3737

3838
private final Expression tableName;

x-pack/plugin/esql/src/main/java/org/elasticsearch/xpack/esql/plan/logical/MvExpand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
import java.util.List;
2424
import java.util.Objects;
2525

26-
public class MvExpand extends UnaryPlan implements TelemetryAware {
26+
public class MvExpand extends UnaryPlan implements TelemetryAware, SortAware {
2727
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(LogicalPlan.class, "MvExpand", MvExpand::new);
2828

2929
private final NamedExpression target;

0 commit comments

Comments
 (0)