Skip to content

Commit

Permalink
planner: check for only_full_group_by in ORDER BY and HAVING (#21216) (
Browse files Browse the repository at this point in the history
…#21697)

Signed-off-by: ti-srebot <ti-srebot@pingcap.com>
  • Loading branch information
ti-srebot authored Jan 26, 2021
1 parent 12d2482 commit c751241
Show file tree
Hide file tree
Showing 6 changed files with 58 additions and 8 deletions.
1 change: 1 addition & 0 deletions errno/errcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,7 @@ const (
ErrMaxExecTimeExceeded = 1907
ErrInvalidFieldSize = 3013
ErrInvalidArgumentForLogarithm = 3020
ErrAggregateOrderNonAggQuery = 3029
ErrIncorrectType = 3064
ErrInvalidJSONData = 3069
ErrGeneratedColumnFunctionIsNotAllowed = 3102
Expand Down
1 change: 1 addition & 0 deletions errno/errname.go
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,7 @@ var MySQLErrName = map[uint16]*mysql.ErrMessage{
ErrWarnConflictingHint: mysql.Message("Hint %s is ignored as conflicting/duplicated.", nil),
ErrInvalidFieldSize: mysql.Message("Invalid size for column '%s'.", nil),
ErrInvalidArgumentForLogarithm: mysql.Message("Invalid argument for logarithm", nil),
ErrAggregateOrderNonAggQuery: mysql.Message("Expression #%d of ORDER BY contains aggregate function and applies to the result of a non-aggregated query", nil),
ErrIncorrectType: mysql.Message("Incorrect type for argument %s in function %s.", nil),
ErrInvalidJSONData: mysql.Message("Invalid JSON data provided to function %s: %s", nil),
ErrInvalidJSONText: mysql.Message("Invalid JSON text: %-.192s", nil),
Expand Down
5 changes: 5 additions & 0 deletions errors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,11 @@ error = '''
Internal : %s
'''

["planner:3029"]
error = '''
Expression #%d of ORDER BY contains aggregate function and applies to the result of a non-aggregated query
'''

["planner:3105"]
error = '''
The value specified for generated column '%s' in table '%s' is not allowed.
Expand Down
1 change: 1 addition & 0 deletions planner/core/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ var (
ErrWrongNumberOfColumnsInSelect = dbterror.ClassOptimizer.NewStd(mysql.ErrWrongNumberOfColumnsInSelect)
ErrBadGeneratedColumn = dbterror.ClassOptimizer.NewStd(mysql.ErrBadGeneratedColumn)
ErrFieldNotInGroupBy = dbterror.ClassOptimizer.NewStd(mysql.ErrFieldNotInGroupBy)
ErrAggregateOrderNonAggQuery = dbterror.ClassOptimizer.NewStd(mysql.ErrAggregateOrderNonAggQuery)
ErrBadTable = dbterror.ClassOptimizer.NewStd(mysql.ErrBadTable)
ErrKeyDoesNotExist = dbterror.ClassOptimizer.NewStd(mysql.ErrKeyDoesNotExist)
ErrOperandColumns = dbterror.ClassOptimizer.NewStd(mysql.ErrOperandColumns)
Expand Down
12 changes: 12 additions & 0 deletions planner/core/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,18 @@ func (s *testIntegrationSuite) TestUpdateMultiUpdatePK(c *C) {
tk.MustQuery("SELECT * FROM t").Check(testkit.Rows("2 12"))
}

func (s *testIntegrationSuite) TestOrderByHavingNotInSelect(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
tk.MustExec("drop table if exists ttest")
tk.MustExec("create table ttest (v1 int, v2 int)")
tk.MustExec("insert into ttest values(1, 2), (4,6), (1, 7)")
tk.MustGetErrMsg("select v1 from ttest order by count(v2)",
"[planner:3029]Expression #1 of ORDER BY contains aggregate function and applies to the result of a non-aggregated query")
tk.MustGetErrMsg("select v1 from ttest having count(v2)",
"[planner:8123]In aggregated query without GROUP BY, expression #1 of SELECT list contains nonaggregated column 'v1'; this is incompatible with sql_mode=only_full_group_by")
}

func (s *testIntegrationSuite) TestCorrelatedColumnAggFuncPushDown(c *C) {
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test;")
Expand Down
46 changes: 38 additions & 8 deletions planner/core/logical_plan_builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -2195,7 +2195,7 @@ func (b *PlanBuilder) checkOnlyFullGroupBy(p LogicalPlan, sel *ast.SelectStmt) (
if sel.GroupBy != nil {
err = b.checkOnlyFullGroupByWithGroupClause(p, sel)
} else {
err = b.checkOnlyFullGroupByWithOutGroupClause(p, sel.Fields.Fields)
err = b.checkOnlyFullGroupByWithOutGroupClause(p, sel)
}
return err
}
Expand Down Expand Up @@ -2269,32 +2269,58 @@ func (b *PlanBuilder) checkOnlyFullGroupByWithGroupClause(p LogicalPlan, sel *as
return nil
}

func (b *PlanBuilder) checkOnlyFullGroupByWithOutGroupClause(p LogicalPlan, fields []*ast.SelectField) error {
func (b *PlanBuilder) checkOnlyFullGroupByWithOutGroupClause(p LogicalPlan, sel *ast.SelectStmt) error {
resolver := colResolverForOnlyFullGroupBy{}
for idx, field := range fields {
resolver.curClause = fieldList
for idx, field := range sel.Fields.Fields {
resolver.exprIdx = idx
field.Accept(&resolver)
err := resolver.Check()
if err != nil {
return err
}
}
if resolver.firstNonAggCol != nil {
if sel.Having != nil {
sel.Having.Expr.Accept(&resolver)
err := resolver.Check()
if err != nil {
return err
}
}
if sel.OrderBy != nil {
resolver.curClause = orderByClause
for idx, byItem := range sel.OrderBy.Items {
resolver.exprIdx = idx
byItem.Expr.Accept(&resolver)
err := resolver.Check()
if err != nil {
return err
}
}
}
}
return nil
}

// colResolverForOnlyFullGroupBy visits Expr tree to find out if an Expr tree is an aggregation function.
// If so, find out the first column name that not in an aggregation function.
type colResolverForOnlyFullGroupBy struct {
firstNonAggCol *ast.ColumnName
exprIdx int
firstNonAggColIdx int
hasAggFuncOrAnyValue bool
firstNonAggCol *ast.ColumnName
exprIdx int
firstNonAggColIdx int
hasAggFuncOrAnyValue bool
firstOrderByAggColIdx int
curClause clauseCode
}

func (c *colResolverForOnlyFullGroupBy) Enter(node ast.Node) (ast.Node, bool) {
switch t := node.(type) {
case *ast.AggregateFuncExpr:
c.hasAggFuncOrAnyValue = true
if c.curClause == orderByClause {
c.firstOrderByAggColIdx = c.exprIdx
}
return node, true
case *ast.FuncCallExpr:
// enable function `any_value` in aggregation even `ONLY_FULL_GROUP_BY` is set
Expand All @@ -2319,7 +2345,11 @@ func (c *colResolverForOnlyFullGroupBy) Leave(node ast.Node) (ast.Node, bool) {

func (c *colResolverForOnlyFullGroupBy) Check() error {
if c.hasAggFuncOrAnyValue && c.firstNonAggCol != nil {
return ErrMixOfGroupFuncAndFields.GenWithStackByArgs(c.firstNonAggColIdx+1, c.firstNonAggCol.Name.O)
if c.curClause == fieldList {
return ErrMixOfGroupFuncAndFields.GenWithStackByArgs(c.firstNonAggColIdx+1, c.firstNonAggCol.Name.O)
} else if c.curClause == orderByClause {
return ErrAggregateOrderNonAggQuery.GenWithStackByArgs(c.firstOrderByAggColIdx + 1)
}
}
return nil
}
Expand Down

0 comments on commit c751241

Please sign in to comment.