Skip to content

Commit 986b927

Browse files
committed
validate order by expressions
1 parent 60a7f34 commit 986b927

File tree

4 files changed

+66
-31
lines changed

4 files changed

+66
-31
lines changed

enginetest/enginetests.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -828,7 +828,7 @@ func TestOrderByGroupBy(t *testing.T, harness Harness) {
828828
// group by with any_value or non-strict are non-deterministic (unless there's only one value), so we must accept multiple
829829
// group by with any_value()
830830

831-
_, rowIter, _, err = e.Query(ctx, "select any_value(id), team from members group by team order by id")
831+
_, rowIter, _, err = e.Query(ctx, "select any_value(id), team from members group by team")
832832
require.NoError(t, err)
833833
rowCount = 0
834834

@@ -867,6 +867,7 @@ func TestOrderByGroupBy(t *testing.T, harness Harness) {
867867
require.Equal(t, rowCount, 3)
868868

869869
AssertErr(t, e, harness, "select id, team from members group by team order by id", nil, analyzererrors.ErrValidationGroupBy)
870+
AssertErr(t, e, harness, "select any_value(id), team from members group by team order by id", nil, analyzererrors.ErrValidationGroupByOrderBy)
870871
})
871872
}
872873

enginetest/queries/order_by_group_by_queries.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,11 +109,8 @@ var OrderByGroupByScriptTests = []ScriptTest{
109109
},
110110
},
111111
{
112-
Query: "select binary s from t group by binary s order by s",
113-
Expected: []sql.Row{
114-
{[]uint8("abc")},
115-
{[]uint8("def")},
116-
},
112+
Query: "select binary s from t group by binary s order by s",
113+
ExpectedErr: analyzererrors.ErrValidationGroupByOrderBy,
117114
},
118115
},
119116
},

sql/analyzer/analyzererrors/errors.go

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,17 @@ var (
2222
// ErrValidationOrderBy is returned when the order by contains aggregation
2323
// expressions.
2424
ErrValidationOrderBy = errors.NewKind("OrderBy does not support aggregation expressions")
25-
// ErrValidationGroupBy is returned when the aggregation expression does not
26-
// appear in the grouping columns.
25+
// ErrValidationGroupBy is returned when a selected expression contains a nonaggregated column that does not appear
26+
// in the group by clauses.
2727
ErrValidationGroupBy = errors.NewKind(
28-
"Expression #%d of SELECT list is not in GROUP BY clause; " +
28+
"Expression #%d of SELECT list is not in GROUP BY clause and contains nonaggregated column '%s' which " +
29+
"is not functionally dependent on columns in GROUP BY clause; " +
30+
"this is incompatible with sql_mode=only_full_group_by",
31+
)
32+
33+
ErrValidationGroupByOrderBy = errors.NewKind(
34+
"Expression #%d of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column '%s' which " +
35+
"is not functionally dependent on columns in GROUP BY clause; " +
2936
"this is incompatible with sql_mode=only_full_group_by",
3037
)
3138
// ErrValidationSchemaSource is returned when there is any column source

sql/analyzer/validation_rules.go

Lines changed: 52 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ func validateGroupBy(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scop
252252

253253
var err error
254254
var project *plan.Project
255+
var orderBy *plan.Sort
255256
transform.Inspect(n, func(n sql.Node) bool {
256257
switch n := n.(type) {
257258
case *plan.GroupBy:
@@ -307,20 +308,35 @@ func validateGroupBy(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scop
307308
return true
308309
}
309310

310-
selectExprs := getSelectExprs(project, n.SelectDeps, groupBys)
311+
selectExprs, orderByExprs := getSelectAndOrderByExprs(project, orderBy, n.SelectDeps, groupBys)
311312

312313
for i, expr := range selectExprs {
313314
if valid, col := expressionReferencesOnlyGroupBys(groupBys, expr, noGroupBy); !valid {
314315
if noGroupBy {
315316
err = sql.ErrNonAggregatedColumnWithoutGroupBy.New(i+1, col)
316317
} else {
317-
err = analyzererrors.ErrValidationGroupBy.New(i + 1)
318+
err = analyzererrors.ErrValidationGroupBy.New(i+1, col)
318319
}
319320
return false
320321
}
321322
}
323+
// According to MySQL documentation, we should still be validating ORDER BY expressions ("If a query has
324+
// aggregate functions and no GROUP BY clause, it cannot have nonaggregated columns in the select list, HAVING
325+
// condition, or ORDER BY list with ONLY_FULL_GROUP_BY enabled"). But when testing actual queries in MySQL,
326+
// it doesn't seem like they validate ORDER BY expressions in aggregate queries without an explicit GROUP BY
327+
if !noGroupBy {
328+
for i, expr := range orderByExprs {
329+
if valid, col := expressionReferencesOnlyGroupBys(groupBys, expr, noGroupBy); !valid {
330+
err = analyzererrors.ErrValidationGroupByOrderBy.New(i+1, col)
331+
return false
332+
}
333+
}
334+
}
322335
case *plan.Project:
323336
project = n
337+
orderBy = nil
338+
case *plan.Sort:
339+
orderBy = n
324340
}
325341
return true
326342
})
@@ -351,42 +367,56 @@ func getEqualsDependencies(expr sql.Expression) []sql.Expression {
351367

352368
// getSelectExprs transforms the projection expressions from a Project node such that it uses the appropriate select
353369
// dependency expressions.
354-
func getSelectExprs(project *plan.Project, selectDeps []sql.Expression, groupBys map[string]bool) []sql.Expression {
355-
if project == nil {
356-
return selectDeps
370+
func getSelectAndOrderByExprs(project *plan.Project, orderBy *plan.Sort, selectDeps []sql.Expression, groupBys map[string]bool) ([]sql.Expression, []sql.Expression) {
371+
if project == nil && orderBy == nil {
372+
return selectDeps, make([]sql.Expression, 0)
357373
} else {
358374
sd := make(map[string]sql.Expression, len(selectDeps))
359375
for _, dep := range selectDeps {
360376
sd[strings.ToLower(dep.String())] = dep
361377
}
362378

363379
selectExprs := make([]sql.Expression, 0)
380+
orderByExprs := make([]sql.Expression, 0)
364381

365382
for _, expr := range project.Projections {
366383
if !project.AliasDeps[strings.ToLower(expr.String())] {
367-
resolvedExpr, _, _ := transform.Expr(expr, func(expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
368-
if groupBys[strings.ToLower(expr.String())] {
369-
return expr, transform.SameTree, nil
370-
}
371-
switch expr := expr.(type) {
372-
case *expression.Alias:
373-
if dep, ok := sd[strings.ToLower(expr.Child.String())]; ok {
374-
return dep, transform.NewTree, nil
375-
}
376-
case *expression.GetField:
377-
if dep, ok := sd[strings.ToLower(expr.String())]; ok {
378-
return dep, transform.NewTree, nil
379-
}
380-
}
381-
return expr, transform.SameTree, nil
382-
})
384+
resolvedExpr := resolveExpr(expr, sd, groupBys)
383385
selectExprs = append(selectExprs, resolvedExpr)
384386
}
385387
}
386-
return selectExprs
388+
389+
if orderBy != nil {
390+
for _, expr := range orderBy.Expressions() {
391+
resolvedExpr := resolveExpr(expr, sd, groupBys)
392+
orderByExprs = append(orderByExprs, resolvedExpr)
393+
}
394+
}
395+
396+
return selectExprs, orderByExprs
387397
}
388398
}
389399

400+
func resolveExpr(expr sql.Expression, selectDeps map[string]sql.Expression, groupBys map[string]bool) sql.Expression {
401+
resolvedExpr, _, _ := transform.Expr(expr, func(expr sql.Expression) (sql.Expression, transform.TreeIdentity, error) {
402+
if groupBys[strings.ToLower(expr.String())] {
403+
return expr, transform.SameTree, nil
404+
}
405+
switch expr := expr.(type) {
406+
case *expression.Alias:
407+
if dep, ok := selectDeps[strings.ToLower(expr.Child.String())]; ok {
408+
return dep, transform.NewTree, nil
409+
}
410+
case *expression.GetField:
411+
if dep, ok := selectDeps[strings.ToLower(expr.String())]; ok {
412+
return dep, transform.NewTree, nil
413+
}
414+
}
415+
return expr, transform.SameTree, nil
416+
})
417+
return resolvedExpr
418+
}
419+
390420
// expressionReferencesOnlyGroupBys validates that an expression is dependent on only group by expressions
391421
func expressionReferencesOnlyGroupBys(groupBys map[string]bool, expr sql.Expression, noGroupBy bool) (bool, string) {
392422
var col string

0 commit comments

Comments
 (0)