Skip to content
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

SQL: Return error with ORDER BY on non-grouped. #34855

Merged
merged 9 commits into from
Oct 25, 2018
Original file line number Diff line number Diff line change
Expand Up @@ -238,52 +238,54 @@ private static boolean checkGroupByOrder(LogicalPlan p, Set<Failure> localFailur
Set<LogicalPlan> groupingFailures, Map<String, Function> functions) {
if (p instanceof OrderBy) {
OrderBy o = (OrderBy) p;
if (o.child() instanceof Aggregate) {
Aggregate a = (Aggregate) o.child();

Map<Expression, Node<?>> missing = new LinkedHashMap<>();
o.order().forEach(oe -> {
Expression e = oe.child();
// cannot order by aggregates (not supported by composite)
if (Functions.isAggregate(e)) {
missing.put(e, oe);
return;
}
o.forEachDown(child -> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would limit the down checks since in the future, with subqueries there might multiple Aggs underneath.
In other words an order by can have a Filter/Project&Filter before having an Agg. Checking the entire hierarchy seems too broad...

if (child instanceof Aggregate) {
Aggregate a = (Aggregate) child;

// take aliases declared inside the aggregates which point to the grouping (but are not included in there)
// to correlate them to the order
List<Expression> groupingAndMatchingAggregatesAliases = new ArrayList<>(a.groupings());

a.aggregates().forEach(as -> {
if (as instanceof Alias) {
Alias al = (Alias) as;
if (Expressions.anyMatch(a.groupings(), g -> Expressions.equalsAsAttribute(al.child(), g))) {
groupingAndMatchingAggregatesAliases.add(al);
}
Map<Expression, Node<?>> missing = new LinkedHashMap<>();
o.order().forEach(oe -> {
Expression e = oe.child();
// cannot order by aggregates (not supported by composite)
if (Functions.isAggregate(e)) {
missing.put(e, oe);
return;
}
});

// make sure to compare attributes directly
if (Expressions.anyMatch(groupingAndMatchingAggregatesAliases,

// take aliases declared inside the aggregates which point to the grouping (but are not included in there)
// to correlate them to the order
List<Expression> groupingAndMatchingAggregatesAliases = new ArrayList<>(a.groupings());

a.aggregates().forEach(as -> {
if (as instanceof Alias) {
Alias al = (Alias) as;
if (Expressions.anyMatch(a.groupings(), g -> Expressions.equalsAsAttribute(al.child(), g))) {
groupingAndMatchingAggregatesAliases.add(al);
}
}
});

// make sure to compare attributes directly
if (Expressions.anyMatch(groupingAndMatchingAggregatesAliases,
g -> e.semanticEquals(e instanceof Attribute ? Expressions.attribute(g) : g))) {
return;
}
return;
}

// nothing matched, cannot group by it
missing.put(e, oe);
});
// nothing matched, cannot group by it
missing.put(e, oe);
});

if (!missing.isEmpty()) {
String plural = missing.size() > 1 ? "s" : StringUtils.EMPTY;
// get the location of the first missing expression as the order by might be on a different line
localFailures.add(
if (!missing.isEmpty()) {
String plural = missing.size() > 1 ? "s" : StringUtils.EMPTY;
// get the location of the first missing expression as the order by might be on a different line
localFailures.add(
fail(missing.values().iterator().next(), "Cannot order by non-grouped column" + plural + " %s, expected %s",
Expressions.names(missing.keySet()),
Expressions.names(a.groupings())));
groupingFailures.add(a);
return false;
Expressions.names(missing.keySet()),
Expressions.names(a.groupings())));
groupingFailures.add(a);
}
}
}
});
}
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ public void testGroupByOrderByNonGrouped() {
verify("SELECT MAX(int) FROM test GROUP BY text ORDER BY bool"));
}

public void testGroupByOrderByNonGrouped_WithHaving() {
assertEquals("1:71: Cannot order by non-grouped column [bool], expected [text]",
verify("SELECT MAX(int) FROM test GROUP BY text HAVING MAX(int) > 10 ORDER BY bool"));
}

public void testGroupByOrderByAliasedInSelectAllowed() {
LogicalPlan lp = accepted("SELECT text t FROM test GROUP BY text ORDER BY t");
assertNotNull(lp);
Expand All @@ -128,6 +133,11 @@ public void testGroupByOrderByScalarOverNonGrouped() {
verify("SELECT MAX(int) FROM test GROUP BY text ORDER BY YEAR(date)"));
}

public void testGroupByOrderByScalarÎOverNonGrouped_WithHaving() {
assertEquals("1:71: Cannot order by non-grouped column [YEAR(date [UTC])], expected [text]",
verify("SELECT MAX(int) FROM test GROUP BY text HAVING MAX(int) > 10 ORDER BY YEAR(date)"));
}

public void testGroupByHavingNonGrouped() {
assertEquals("1:48: Cannot filter by non-grouped column [int], expected [text]",
verify("SELECT AVG(int) FROM test GROUP BY text HAVING int > 10"));
Expand Down