Skip to content

Commit b4bb8b7

Browse files
committed
SQL: Verify GROUP BY ordering on grouped columns
Due to the way composite aggregation works, ordering in GROUP BY can be applied only through grouped columns which now the analyzer verifier enforces. Fix 29900
1 parent 4a4e3d7 commit b4bb8b7

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

x-pack/plugin/sql/src/main/java/org/elasticsearch/xpack/sql/analysis/analyzer/Verifier.java

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -211,12 +211,13 @@ static Collection<Failure> verify(LogicalPlan plan) {
211211

212212
/**
213213
* Check validity of Aggregate/GroupBy.
214-
* This rule is needed for two reasons:
214+
* This rule is needed for multiple reasons:
215215
* 1. a user might specify an invalid aggregate (SELECT foo GROUP BY bar)
216216
* 2. the order/having might contain a non-grouped attribute. This is typically
217217
* caught by the Analyzer however if wrapped in a function (ABS()) it gets resolved
218218
* (because the expression gets resolved little by little without being pushed down,
219219
* without the Analyzer modifying anything.
220+
* 3. composite agg (used for GROUP BY) allows ordering only on the group keys
220221
*/
221222
private static boolean checkGroupBy(LogicalPlan p, Set<Failure> localFailures,
222223
Map<String, Function> resolvedFunctions, Set<LogicalPlan> groupingFailures) {
@@ -225,7 +226,7 @@ && checkGroupByOrder(p, localFailures, groupingFailures, resolvedFunctions)
225226
&& checkGroupByHaving(p, localFailures, groupingFailures, resolvedFunctions);
226227
}
227228

228-
// check whether an orderBy failed
229+
// check whether an orderBy failed or if it occurs on a non-key
229230
private static boolean checkGroupByOrder(LogicalPlan p, Set<Failure> localFailures,
230231
Set<LogicalPlan> groupingFailures, Map<String, Function> functions) {
231232
if (p instanceof OrderBy) {
@@ -234,7 +235,25 @@ private static boolean checkGroupByOrder(LogicalPlan p, Set<Failure> localFailur
234235
Aggregate a = (Aggregate) o.child();
235236

236237
Map<Expression, Node<?>> missing = new LinkedHashMap<>();
237-
o.order().forEach(oe -> oe.collectFirstChildren(c -> checkGroupMatch(c, oe, a.groupings(), missing, functions)));
238+
o.order().forEach(oe -> {
239+
Expression e = oe.child();
240+
// skip score
241+
if (e instanceof Score) {
242+
return;
243+
}
244+
// cannot order by aggregates (not supported by composite)
245+
if (Functions.isAggregate(e)) {
246+
missing.put(e, oe);
247+
return;
248+
}
249+
250+
if (Expressions.anyMatch(a.groupings(), e::semanticEquals)) {
251+
return;
252+
}
253+
254+
// nothing matched, cannot group by it
255+
missing.put(e, oe);
256+
});
238257

239258
if (!missing.isEmpty()) {
240259
String plural = missing.size() > 1 ? "s" : StringUtils.EMPTY;

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/analysis/analyzer/VerifierErrorMessagesTests.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public void testGroupByOrderByNonGrouped() {
111111
}
112112

113113
public void testGroupByOrderByScalarOverNonGrouped() {
114-
assertEquals("1:50: Cannot order by non-grouped column [date], expected [text]",
114+
assertEquals("1:50: Cannot order by non-grouped column [YEAR(date [UTC])], expected [text]",
115115
verify("SELECT MAX(int) FROM test GROUP BY text ORDER BY YEAR(date)"));
116116
}
117117

@@ -144,4 +144,14 @@ public void testUnsupportedType() {
144144
assertEquals("1:8: Cannot use field [unsupported] type [ip_range] as is unsupported",
145145
verify("SELECT unsupported FROM test"));
146146
}
147-
}
147+
148+
public void testGroupByOrderByNonKey() {
149+
assertEquals("1:52: Cannot order by non-grouped column [a], expected [bool]",
150+
verify("SELECT AVG(int) a FROM test GROUP BY bool ORDER BY a"));
151+
}
152+
153+
public void testGroupByOrderByFunctionOverKey() {
154+
assertEquals("1:44: Cannot order by non-grouped column [MAX(int)], expected [int]",
155+
verify("SELECT int FROM test GROUP BY int ORDER BY MAX(int)"));
156+
}
157+
}

x-pack/plugin/sql/src/test/java/org/elasticsearch/xpack/sql/planner/VerifierErrorMessagesTests.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,18 @@ public void testMultiGroupBy() {
4949
assertEquals("1:32: Currently, only a single expression can be used with GROUP BY; please select one of [bool, keyword]",
5050
verify("SELECT bool FROM test GROUP BY bool, keyword"));
5151
}
52+
53+
//
54+
// TODO potential improvements
55+
//
56+
// regarding resolution
57+
// public void testGroupByOrderByKeyAlias() {
58+
// assertEquals("1:8: Cannot use field [unsupported] type [ip_range] as is unsupported",
59+
// verify("SELECT int i FROM test GROUP BY int ORDER BY i"));
60+
// }
61+
//
62+
// public void testGroupByAlias() {
63+
// assertEquals("1:8: Cannot use field [unsupported] type [ip_range] as is unsupported",
64+
// verify("SELECT int i FROM test GROUP BY i ORDER BY int"));
65+
// }
5266
}

0 commit comments

Comments
 (0)