Skip to content

Commit d32c9ce

Browse files
authored
Merge pull request #3194 from dolthub/angela/groupby
Group by validation for aggregated queries
2 parents e37200c + fefcc1a commit d32c9ce

File tree

7 files changed

+74
-88
lines changed

7 files changed

+74
-88
lines changed

enginetest/queries/column_alias_queries.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,11 +269,17 @@ var ColumnAliasQueries = []ScriptTest{
269269
Assertions: []ScriptTestAssertion{
270270
{
271271
// GMS returns "expression 'dt.two' doesn't appear in the group by expressions", but MySQL will execute
272-
// this query.
272+
// this query. https://github.com/dolthub/dolt/issues/9717
273273
Query: "select 1 as a, one + 1 as mod1, dt.* from mytable as t1, (select 1, 2 from mytable) as dt (one, two) where dt.one > 0 group by one;",
274274
// column names: a, mod1, one, two
275275
Expected: []sql.Row{{1, 2, 1, 2}},
276276
},
277+
{
278+
// MySQL will execute this query but group by validation isn't able to recognize that exprAlias is a
279+
// literal https://github.com/dolthub/dolt/issues/9717
280+
Query: "select 1 as exprAlias, 2, 3, (select exprAlias + count(*) from one_pk_three_idx a cross join one_pk_three_idx b);",
281+
Expected: []sql.Row{{1, 2, 3, 65}},
282+
},
277283
},
278284
},
279285
}

enginetest/queries/join_queries.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ inner join pq on true order by 1,2,3,4,5,6,7,8 limit 5;`,
187187
},
188188
{
189189
// test cross join used as projected subquery expression
190-
Query: "select 1 as exprAlias, 2, 3, (select exprAlias + count(*) from one_pk_three_idx a cross join one_pk_three_idx b);",
190+
Query: "select 1, 2, 3, (select 1 + count(*) from one_pk_three_idx a cross join one_pk_three_idx b);",
191191
Expected: []sql.Row{{1, 2, 3, 65}},
192192
},
193193
{

enginetest/queries/order_by_group_by_queries.go

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -199,9 +199,6 @@ var OrderByGroupByScriptTests = []ScriptTest{
199199
ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
200200
},
201201
{
202-
// GMS currently allows this query to execute and chooses the first result for val.
203-
// To match MySQL's behavior, GMS should be throwing an ErrNonAggregatedColumnWithoutGroupBy error.
204-
Skip: true,
205202
Query: "select AVG(val), val from t;",
206203
ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
207204
},

enginetest/queries/queries.go

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1238,18 +1238,6 @@ SELECT * FROM cte WHERE d = 2;`,
12381238
{int64(3), "third row"},
12391239
},
12401240
},
1241-
{
1242-
Query: "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM emptytable;",
1243-
Expected: []sql.Row{
1244-
{0, nil, nil, 123, "abc", "abcdef"},
1245-
},
1246-
},
1247-
{
1248-
Query: "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM mytable where false;",
1249-
Expected: []sql.Row{
1250-
{0, nil, nil, 123, "abc", "abcdef"},
1251-
},
1252-
},
12531241
{
12541242
Query: "SELECT pk, u, v FROM one_pk JOIN (SELECT count(*) AS u, 123 AS v FROM emptytable) uv WHERE pk = u;",
12551243
Expected: []sql.Row{
@@ -7877,10 +7865,6 @@ SELECT * FROM ladder;`,
78777865
Query: "with a(j) as (select 1) ( with c(k) as (select 3) select k from c union select 6) union select k from c;",
78787866
Expected: []sql.Row{{3}, {6}},
78797867
},
7880-
{
7881-
Query: "SELECT pk1, SUM(c1) FROM two_pk",
7882-
Expected: []sql.Row{{0, 60.0}},
7883-
},
78847868
{
78857869
Query: `SELECT pk,
78867870
(SELECT sum(c1) FROM two_pk WHERE c1 IN (SELECT c4 FROM two_pk WHERE c3 > opk.c5)) AS sum,

enginetest/queries/query_plans.go

Lines changed: 0 additions & 55 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

enginetest/queries/script_queries.go

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2795,7 +2795,7 @@ CREATE TABLE tab3 (
27952795
},
27962796
{
27972797
// Test with subquery using multiple columns errors
2798-
Query: "SELECT category, GROUP_CONCAT(name ORDER BY (SELECT AVG(value), name FROM complex_test c2 WHERE c2.id <= complex_test.id HAVING AVG(value) > 50) DESC) FROM complex_test GROUP BY category ORDER BY category",
2798+
Query: "SELECT category, GROUP_CONCAT(name ORDER BY (SELECT value, name FROM complex_test c2 WHERE c2.id <= complex_test.id) DESC) FROM complex_test GROUP BY category ORDER BY category",
27992799
ExpectedErr: sql.ErrInvalidOperandColumns,
28002800
},
28012801
{
@@ -11697,11 +11697,9 @@ select * from t1 except (
1169711697
Assertions: []ScriptTestAssertion{
1169811698
{
1169911699
// https://github.com/dolthub/dolt/issues/9761
11700-
Skip: true,
11701-
Query: "SELECT pk, count(pk), MATCH(doc) AGAINST('aaaa') AS relevancy FROM test ORDER BY relevancy DESC;",
11702-
// TODO: replace with corresponding error once validation for aggregated query without group by
11703-
// has been implemented https://github.com/dolthub/dolt/issues/9761
11704-
ExpectedErr: nil,
11700+
Skip: true,
11701+
Query: "SELECT pk, count(pk), MATCH(doc) AGAINST('aaaa') AS relevancy FROM test ORDER BY relevancy DESC;",
11702+
ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
1170511703
},
1170611704
{
1170711705
Query: "SET SESSION sql_mode = REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', '');",
@@ -11714,6 +11712,51 @@ select * from t1 except (
1171411712
},
1171511713
},
1171611714
},
11715+
{
11716+
Name: "nonaggregated column in aggregated query",
11717+
Dialect: "mysql",
11718+
SetUpScript: []string{
11719+
"create table emptytable(i mediumint primary key, s varchar(20))",
11720+
"create table mytable(i bigint primary key, s varchar(20))",
11721+
"insert into mytable values (1, 'first'), (2, 'second'), (3, 'third');",
11722+
"create table two_pk (pk1 tinyint, pk2 tinyint, c1 tinyint NOT NULL, primary key (pk1, pk2))",
11723+
"insert into two_pk values (0,0,0), (0,1,10), (1,0,20), (1,1,30)",
11724+
},
11725+
Assertions: []ScriptTestAssertion{
11726+
{
11727+
Query: "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM emptytable;",
11728+
ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
11729+
},
11730+
{
11731+
Query: "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM mytable where false;",
11732+
ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
11733+
},
11734+
{
11735+
Query: "SELECT pk1, SUM(c1) FROM two_pk",
11736+
ExpectedErr: sql.ErrNonAggregatedColumnWithoutGroupBy,
11737+
},
11738+
{
11739+
Query: "SET SESSION sql_mode = REPLACE(@@SESSION.sql_mode, 'ONLY_FULL_GROUP_BY', '');",
11740+
Expected: []sql.Row{{types.NewOkResult(0)}},
11741+
},
11742+
{
11743+
Query: "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM emptytable;",
11744+
Expected: []sql.Row{
11745+
{0, nil, nil, 123, "abc", "abcdef"},
11746+
},
11747+
},
11748+
{
11749+
Query: "SELECT count(*), i, concat(i, i), 123, 'abc', concat('abc', 'def') FROM mytable where false;",
11750+
Expected: []sql.Row{
11751+
{0, nil, nil, 123, "abc", "abcdef"},
11752+
},
11753+
},
11754+
{
11755+
Query: "SELECT pk1, SUM(c1) FROM two_pk",
11756+
Expected: []sql.Row{{0, 60.0}},
11757+
},
11758+
},
11759+
},
1171711760
}
1171811761

1171911762
var SpatialScriptTests = []ScriptTest{

sql/analyzer/validation_rules.go

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -255,10 +255,11 @@ func validateGroupBy(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scop
255255
transform.Inspect(n, func(n sql.Node) bool {
256256
switch n := n.(type) {
257257
case *plan.GroupBy:
258+
var noGroupBy bool
258259
// Allow the parser use the GroupBy node to eval the aggregation functions for sql statements that don't
259260
// make use of the GROUP BY expression.
260261
if len(n.GroupByExprs) == 0 {
261-
return true
262+
noGroupBy = true
262263
}
263264

264265
primaryKeys := make(map[string]bool)
@@ -309,8 +310,12 @@ func validateGroupBy(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scop
309310
selectExprs := getSelectExprs(project, n.SelectDeps, groupBys)
310311

311312
for i, expr := range selectExprs {
312-
if !expressionReferencesOnlyGroupBys(groupBys, expr) {
313-
err = analyzererrors.ErrValidationGroupBy.New(i + 1)
313+
if valid, col := expressionReferencesOnlyGroupBys(groupBys, expr, noGroupBy); !valid {
314+
if noGroupBy {
315+
err = sql.ErrNonAggregatedColumnWithoutGroupBy.New(i+1, col)
316+
} else {
317+
err = analyzererrors.ErrValidationGroupBy.New(i + 1)
318+
}
314319
return false
315320
}
316321
}
@@ -383,7 +388,8 @@ func getSelectExprs(project *plan.Project, selectDeps []sql.Expression, groupBys
383388
}
384389

385390
// expressionReferencesOnlyGroupBys validates that an expression is dependent on only group by expressions
386-
func expressionReferencesOnlyGroupBys(groupBys map[string]bool, expr sql.Expression) bool {
391+
func expressionReferencesOnlyGroupBys(groupBys map[string]bool, expr sql.Expression, noGroupBy bool) (bool, string) {
392+
var col string
387393
valid := true
388394
sql.Inspect(expr, func(expr sql.Expression) bool {
389395
switch expr := expr.(type) {
@@ -401,15 +407,20 @@ func expressionReferencesOnlyGroupBys(groupBys map[string]bool, expr sql.Express
401407
}
402408

403409
if len(expr.Children()) == 0 {
404-
valid = false
410+
// Allow non-matching subqueries when no explicit group by clause. If the subquery returns more than
411+
// one row for an aggregated query, we will error out later on.
412+
if _, isSubquery := expr.(*plan.Subquery); !(isSubquery && noGroupBy) {
413+
valid = false
414+
col = expr.String()
415+
}
405416
return false
406417
}
407418

408419
return true
409420
}
410421
})
411422

412-
return valid
423+
return valid, col
413424
}
414425

415426
func validateSchemaSource(ctx *sql.Context, a *Analyzer, n sql.Node, scope *plan.Scope, sel RuleSelector, qFlags *sql.QueryFlags) (sql.Node, transform.TreeIdentity, error) {

0 commit comments

Comments
 (0)