vitess.io/vitess@v0.16.2/go/vt/vtgate/planbuilder/operators/queryprojection.go (about)

     1  /*
     2  Copyright 2021 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package operators
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"sort"
    23  	"strings"
    24  
    25  	"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
    26  	"vitess.io/vitess/go/vt/vtgate/semantics"
    27  
    28  	"vitess.io/vitess/go/vt/vtgate/engine"
    29  
    30  	"vitess.io/vitess/go/vt/sqlparser"
    31  	"vitess.io/vitess/go/vt/vterrors"
    32  )
    33  
    34  type (
    35  	// SelectExpr provides whether the columns is aggregation expression or not.
    36  	SelectExpr struct {
    37  		Col  sqlparser.SelectExpr
    38  		Aggr bool
    39  	}
    40  
    41  	// QueryProjection contains the information about the projections, group by and order by expressions used to do horizon planning.
    42  	QueryProjection struct {
    43  		// If you change the contents here, please update the toString() method
    44  		SelectExprs        []SelectExpr
    45  		HasAggr            bool
    46  		Distinct           bool
    47  		groupByExprs       []GroupBy
    48  		OrderExprs         []OrderBy
    49  		CanPushDownSorting bool
    50  		HasStar            bool
    51  
    52  		// AddedColumn keeps a counter for expressions added to solve HAVING expressions the user is not selecting
    53  		AddedColumn int
    54  	}
    55  
    56  	// OrderBy contains the expression to used in order by and also if ordering is needed at VTGate level then what the weight_string function expression to be sent down for evaluation.
    57  	OrderBy struct {
    58  		Inner         *sqlparser.Order
    59  		WeightStrExpr sqlparser.Expr
    60  	}
    61  
    62  	// GroupBy contains the expression to used in group by and also if grouping is needed at VTGate level then what the weight_string function expression to be sent down for evaluation.
    63  	GroupBy struct {
    64  		Inner         sqlparser.Expr
    65  		WeightStrExpr sqlparser.Expr
    66  
    67  		// The index at which the user expects to see this column. Set to nil, if the user does not ask for it
    68  		InnerIndex *int
    69  
    70  		// The original aliased expression that this group by is referring
    71  		aliasedExpr *sqlparser.AliasedExpr
    72  	}
    73  
    74  	// Aggr encodes all information needed for aggregation functions
    75  	Aggr struct {
    76  		Original *sqlparser.AliasedExpr
    77  		Func     sqlparser.AggrFunc
    78  		OpCode   engine.AggregateOpcode
    79  		Alias    string
    80  		// The index at which the user expects to see this aggregated function. Set to nil, if the user does not ask for it
    81  		Index    *int
    82  		Distinct bool
    83  	}
    84  
    85  	AggrRewriter struct {
    86  		qp  *QueryProjection
    87  		st  *semantics.SemTable
    88  		Err error
    89  	}
    90  )
    91  
    92  func (b GroupBy) AsOrderBy() OrderBy {
    93  	return OrderBy{
    94  		Inner: &sqlparser.Order{
    95  			Expr:      b.Inner,
    96  			Direction: sqlparser.AscOrder,
    97  		},
    98  		WeightStrExpr: b.WeightStrExpr,
    99  	}
   100  }
   101  
   102  func (b GroupBy) AsAliasedExpr() *sqlparser.AliasedExpr {
   103  	if b.aliasedExpr != nil {
   104  		return b.aliasedExpr
   105  	}
   106  	col, isColName := b.Inner.(*sqlparser.ColName)
   107  	if isColName && b.WeightStrExpr != b.Inner {
   108  		return &sqlparser.AliasedExpr{
   109  			Expr: b.WeightStrExpr,
   110  			As:   col.Name,
   111  		}
   112  	}
   113  	if !isColName && b.WeightStrExpr != b.Inner {
   114  		panic("this should not happen - different inner and weighStringExpr and not a column alias")
   115  	}
   116  
   117  	return &sqlparser.AliasedExpr{
   118  		Expr: b.WeightStrExpr,
   119  	}
   120  }
   121  
   122  // GetExpr returns the underlying sqlparser.Expr of our SelectExpr
   123  func (s SelectExpr) GetExpr() (sqlparser.Expr, error) {
   124  	switch sel := s.Col.(type) {
   125  	case *sqlparser.AliasedExpr:
   126  		return sel.Expr, nil
   127  	default:
   128  		return nil, vterrors.VT13001(fmt.Sprintf("%T does not have an expression", s.Col))
   129  	}
   130  }
   131  
   132  // GetAliasedExpr returns the SelectExpr as a *sqlparser.AliasedExpr if its type allows it,
   133  // otherwise an error is returned.
   134  func (s SelectExpr) GetAliasedExpr() (*sqlparser.AliasedExpr, error) {
   135  	switch expr := s.Col.(type) {
   136  	case *sqlparser.AliasedExpr:
   137  		return expr, nil
   138  	case *sqlparser.StarExpr:
   139  		return nil, vterrors.VT12001("'*' expression in cross-shard query")
   140  	default:
   141  		return nil, vterrors.VT12001(fmt.Sprintf("not an aliased expression: %T", expr))
   142  	}
   143  }
   144  
   145  // CreateQPFromSelect creates the QueryProjection for the input *sqlparser.Select
   146  func CreateQPFromSelect(ctx *plancontext.PlanningContext, sel *sqlparser.Select) (*QueryProjection, error) {
   147  	qp := &QueryProjection{
   148  		Distinct: sel.Distinct,
   149  	}
   150  
   151  	err := qp.addSelectExpressions(sel)
   152  	if err != nil {
   153  		return nil, err
   154  	}
   155  	for _, group := range sel.GroupBy {
   156  		selectExprIdx, aliasExpr := qp.FindSelectExprIndexForExpr(ctx, group)
   157  		expr, weightStrExpr, err := qp.GetSimplifiedExpr(group)
   158  		if err != nil {
   159  			return nil, err
   160  		}
   161  		err = checkForInvalidGroupingExpressions(weightStrExpr)
   162  		if err != nil {
   163  			return nil, err
   164  		}
   165  
   166  		groupBy := GroupBy{
   167  			Inner:         expr,
   168  			WeightStrExpr: weightStrExpr,
   169  			InnerIndex:    selectExprIdx,
   170  			aliasedExpr:   aliasExpr,
   171  		}
   172  
   173  		qp.groupByExprs = append(qp.groupByExprs, groupBy)
   174  	}
   175  
   176  	err = qp.addOrderBy(sel.OrderBy)
   177  	if err != nil {
   178  		return nil, err
   179  	}
   180  
   181  	if qp.Distinct && !qp.HasAggr {
   182  		qp.groupByExprs = nil
   183  	}
   184  
   185  	return qp, nil
   186  }
   187  
   188  // RewriteDown stops the walker from entering inside aggregation functions
   189  func (ar *AggrRewriter) RewriteDown() func(sqlparser.SQLNode, sqlparser.SQLNode) bool {
   190  	return func(node, _ sqlparser.SQLNode) bool {
   191  		if ar.Err != nil {
   192  			return true
   193  		}
   194  		_, ok := node.(sqlparser.AggrFunc)
   195  		return !ok
   196  	}
   197  }
   198  
   199  // RewriteUp will go through an expression, add aggregations to the QP, and rewrite them to use column offset
   200  func (ar *AggrRewriter) RewriteUp() func(*sqlparser.Cursor) bool {
   201  	return func(cursor *sqlparser.Cursor) bool {
   202  		if ar.Err != nil {
   203  			return false
   204  		}
   205  		sqlNode := cursor.Node()
   206  		fExp, ok := sqlNode.(sqlparser.AggrFunc)
   207  		if !ok {
   208  			return true
   209  		}
   210  		for offset, expr := range ar.qp.SelectExprs {
   211  			ae, err := expr.GetAliasedExpr()
   212  			if err != nil {
   213  				ar.Err = err
   214  				return false
   215  			}
   216  			if ar.st.EqualsExpr(ae.Expr, fExp) {
   217  				cursor.Replace(sqlparser.NewOffset(offset, fExp))
   218  				return true
   219  			}
   220  		}
   221  
   222  		col := SelectExpr{
   223  			Aggr: true,
   224  			Col:  &sqlparser.AliasedExpr{Expr: fExp},
   225  		}
   226  		ar.qp.HasAggr = true
   227  
   228  		cursor.Replace(sqlparser.NewOffset(len(ar.qp.SelectExprs), fExp))
   229  		ar.qp.SelectExprs = append(ar.qp.SelectExprs, col)
   230  		ar.qp.AddedColumn++
   231  
   232  		return true
   233  	}
   234  }
   235  
   236  // AggrRewriter extracts
   237  func (qp *QueryProjection) AggrRewriter(ctx *plancontext.PlanningContext) *AggrRewriter {
   238  	return &AggrRewriter{
   239  		qp: qp,
   240  		st: ctx.SemTable,
   241  	}
   242  }
   243  
   244  func (qp *QueryProjection) addSelectExpressions(sel *sqlparser.Select) error {
   245  	for _, selExp := range sel.SelectExprs {
   246  		switch selExp := selExp.(type) {
   247  		case *sqlparser.AliasedExpr:
   248  			err := checkForInvalidAggregations(selExp)
   249  			if err != nil {
   250  				return err
   251  			}
   252  			col := SelectExpr{
   253  				Col: selExp,
   254  			}
   255  			if sqlparser.ContainsAggregation(selExp.Expr) {
   256  				col.Aggr = true
   257  				qp.HasAggr = true
   258  			}
   259  
   260  			qp.SelectExprs = append(qp.SelectExprs, col)
   261  		case *sqlparser.StarExpr:
   262  			qp.HasStar = true
   263  			col := SelectExpr{
   264  				Col: selExp,
   265  			}
   266  			qp.SelectExprs = append(qp.SelectExprs, col)
   267  		default:
   268  			return vterrors.VT13001(fmt.Sprintf("%T in select list", selExp))
   269  		}
   270  	}
   271  	return nil
   272  }
   273  
   274  // CreateQPFromUnion creates the QueryProjection for the input *sqlparser.Union
   275  func CreateQPFromUnion(union *sqlparser.Union) (*QueryProjection, error) {
   276  	qp := &QueryProjection{}
   277  
   278  	sel := sqlparser.GetFirstSelect(union)
   279  	err := qp.addSelectExpressions(sel)
   280  	if err != nil {
   281  		return nil, err
   282  	}
   283  
   284  	err = qp.addOrderBy(union.OrderBy)
   285  	if err != nil {
   286  		return nil, err
   287  	}
   288  
   289  	return qp, nil
   290  }
   291  
   292  func (qp *QueryProjection) addOrderBy(orderBy sqlparser.OrderBy) error {
   293  	canPushDownSorting := true
   294  	for _, order := range orderBy {
   295  		expr, weightStrExpr, err := qp.GetSimplifiedExpr(order.Expr)
   296  		if err != nil {
   297  			return err
   298  		}
   299  		if sqlparser.IsNull(weightStrExpr) {
   300  			// ORDER BY null can safely be ignored
   301  			continue
   302  		}
   303  		qp.OrderExprs = append(qp.OrderExprs, OrderBy{
   304  			Inner: &sqlparser.Order{
   305  				Expr:      expr,
   306  				Direction: order.Direction,
   307  			},
   308  			WeightStrExpr: weightStrExpr,
   309  		})
   310  		canPushDownSorting = canPushDownSorting && !sqlparser.ContainsAggregation(weightStrExpr)
   311  	}
   312  	qp.CanPushDownSorting = canPushDownSorting
   313  	return nil
   314  }
   315  
   316  // GetGrouping returns a copy of the grouping parameters of the QP
   317  func (qp *QueryProjection) GetGrouping() []GroupBy {
   318  	out := make([]GroupBy, len(qp.groupByExprs))
   319  	copy(out, qp.groupByExprs)
   320  	return out
   321  }
   322  
   323  func checkForInvalidAggregations(exp *sqlparser.AliasedExpr) error {
   324  	return sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
   325  		if aggrFunc, isAggregate := node.(sqlparser.AggrFunc); isAggregate {
   326  			if aggrFunc.GetArgs() != nil &&
   327  				len(aggrFunc.GetArgs()) != 1 {
   328  				return false, vterrors.VT03001(sqlparser.String(node))
   329  			}
   330  			return true, nil
   331  		}
   332  
   333  		return true, nil
   334  	}, exp.Expr)
   335  }
   336  
   337  func (qp *QueryProjection) isExprInGroupByExprs(ctx *plancontext.PlanningContext, expr SelectExpr) bool {
   338  	for _, groupByExpr := range qp.groupByExprs {
   339  		exp, err := expr.GetExpr()
   340  		if err != nil {
   341  			return false
   342  		}
   343  		if ctx.SemTable.EqualsExpr(groupByExpr.WeightStrExpr, exp) {
   344  			return true
   345  		}
   346  	}
   347  	return false
   348  }
   349  
   350  // GetSimplifiedExpr takes an expression used in ORDER BY or GROUP BY, and returns an expression that is simpler to evaluate
   351  func (qp *QueryProjection) GetSimplifiedExpr(e sqlparser.Expr) (expr sqlparser.Expr, weightStrExpr sqlparser.Expr, err error) {
   352  	// If the ORDER BY is against a column alias, we need to remember the expression
   353  	// behind the alias. The weightstring(.) calls needs to be done against that expression and not the alias.
   354  	// Eg - select music.foo as bar, weightstring(music.foo) from music order by bar
   355  
   356  	colExpr, isColName := e.(*sqlparser.ColName)
   357  	if !isColName {
   358  		return e, e, nil
   359  	}
   360  
   361  	if sqlparser.IsNull(e) {
   362  		return e, nil, nil
   363  	}
   364  
   365  	if colExpr.Qualifier.IsEmpty() {
   366  		for _, selectExpr := range qp.SelectExprs {
   367  			aliasedExpr, isAliasedExpr := selectExpr.Col.(*sqlparser.AliasedExpr)
   368  			if !isAliasedExpr {
   369  				continue
   370  			}
   371  			isAliasExpr := !aliasedExpr.As.IsEmpty()
   372  			if isAliasExpr && colExpr.Name.Equal(aliasedExpr.As) {
   373  				return e, aliasedExpr.Expr, nil
   374  			}
   375  		}
   376  	}
   377  
   378  	return e, e, nil
   379  }
   380  
   381  // toString should only be used for tests
   382  func (qp *QueryProjection) toString() string {
   383  	type output struct {
   384  		Select   []string
   385  		Grouping []string
   386  		OrderBy  []string
   387  		Distinct bool
   388  	}
   389  	out := output{
   390  		Select:   []string{},
   391  		Grouping: []string{},
   392  		OrderBy:  []string{},
   393  		Distinct: qp.NeedsDistinct(),
   394  	}
   395  
   396  	for _, expr := range qp.SelectExprs {
   397  		e := sqlparser.String(expr.Col)
   398  
   399  		if expr.Aggr {
   400  			e = "aggr: " + e
   401  		}
   402  		out.Select = append(out.Select, e)
   403  	}
   404  
   405  	for _, expr := range qp.groupByExprs {
   406  		out.Grouping = append(out.Grouping, sqlparser.String(expr.Inner))
   407  	}
   408  	for _, expr := range qp.OrderExprs {
   409  		out.OrderBy = append(out.OrderBy, sqlparser.String(expr.Inner))
   410  	}
   411  
   412  	bytes, _ := json.MarshalIndent(out, "", "  ")
   413  	return string(bytes)
   414  }
   415  
   416  // NeedsAggregation returns true if we either have aggregate functions or grouping defined
   417  func (qp *QueryProjection) NeedsAggregation() bool {
   418  	return qp.HasAggr || len(qp.groupByExprs) > 0
   419  }
   420  
   421  // NeedsProjecting returns true if we have projections that need to be evaluated at the vtgate level
   422  // and can't be pushed down to MySQL
   423  func (qp *QueryProjection) NeedsProjecting(
   424  	ctx *plancontext.PlanningContext,
   425  	pusher func(expr *sqlparser.AliasedExpr) (int, error),
   426  ) (needsVtGateEval bool, expressions []sqlparser.Expr, colNames []string, err error) {
   427  	for _, se := range qp.SelectExprs {
   428  		var ae *sqlparser.AliasedExpr
   429  		ae, err = se.GetAliasedExpr()
   430  		if err != nil {
   431  			return false, nil, nil, err
   432  		}
   433  
   434  		expr := ae.Expr
   435  		colNames = append(colNames, ae.ColumnName())
   436  
   437  		if _, isCol := expr.(*sqlparser.ColName); isCol {
   438  			offset, err := pusher(ae)
   439  			if err != nil {
   440  				return false, nil, nil, err
   441  			}
   442  			expressions = append(expressions, sqlparser.NewOffset(offset, expr))
   443  			continue
   444  		}
   445  
   446  		stopOnError := func(sqlparser.SQLNode, sqlparser.SQLNode) bool {
   447  			return err == nil
   448  		}
   449  		rewriter := func(cursor *sqlparser.CopyOnWriteCursor) {
   450  			col, isCol := cursor.Node().(*sqlparser.ColName)
   451  			if !isCol {
   452  				return
   453  			}
   454  			var tableInfo semantics.TableInfo
   455  			tableInfo, err = ctx.SemTable.TableInfoForExpr(col)
   456  			if err != nil {
   457  				return
   458  			}
   459  			dt, isDT := tableInfo.(*semantics.DerivedTable)
   460  			if !isDT {
   461  				return
   462  			}
   463  
   464  			rewritten := semantics.RewriteDerivedTableExpression(col, dt)
   465  			if sqlparser.ContainsAggregation(rewritten) {
   466  				offset, tErr := pusher(&sqlparser.AliasedExpr{Expr: col})
   467  				if tErr != nil {
   468  					err = tErr
   469  					return
   470  				}
   471  
   472  				cursor.Replace(sqlparser.NewOffset(offset, col))
   473  			}
   474  		}
   475  		newExpr := sqlparser.CopyOnRewrite(expr, stopOnError, rewriter, nil)
   476  
   477  		if err != nil {
   478  			return
   479  		}
   480  
   481  		if newExpr != expr {
   482  			// if we changed the expression, it means that we have to evaluate the rest at the vtgate level
   483  			expressions = append(expressions, newExpr.(sqlparser.Expr))
   484  			needsVtGateEval = true
   485  			continue
   486  		}
   487  
   488  		// we did not need to push any parts of this expression down. Let's check if we can push all of it
   489  		offset, err := pusher(ae)
   490  		if err != nil {
   491  			return false, nil, nil, err
   492  		}
   493  		expressions = append(expressions, sqlparser.NewOffset(offset, expr))
   494  	}
   495  
   496  	return
   497  }
   498  
   499  func (qp *QueryProjection) onlyAggr() bool {
   500  	if !qp.HasAggr {
   501  		return false
   502  	}
   503  	for _, expr := range qp.SelectExprs {
   504  		if !expr.Aggr {
   505  			return false
   506  		}
   507  	}
   508  	return true
   509  }
   510  
   511  // NeedsDistinct returns true if the query needs explicit distinct
   512  func (qp *QueryProjection) NeedsDistinct() bool {
   513  	if !qp.Distinct {
   514  		return false
   515  	}
   516  	if qp.onlyAggr() && len(qp.groupByExprs) == 0 {
   517  		return false
   518  	}
   519  	return true
   520  }
   521  
   522  func (qp *QueryProjection) AggregationExpressions(ctx *plancontext.PlanningContext) (out []Aggr, err error) {
   523  orderBy:
   524  	for _, orderExpr := range qp.OrderExprs {
   525  		orderExpr := orderExpr.WeightStrExpr
   526  		for _, expr := range qp.SelectExprs {
   527  			col, ok := expr.Col.(*sqlparser.AliasedExpr)
   528  			if !ok {
   529  				continue
   530  			}
   531  			if ctx.SemTable.EqualsExpr(col.Expr, orderExpr) {
   532  				continue orderBy // we found the expression we were looking for!
   533  			}
   534  		}
   535  		qp.SelectExprs = append(qp.SelectExprs, SelectExpr{
   536  			Col:  &sqlparser.AliasedExpr{Expr: orderExpr},
   537  			Aggr: sqlparser.ContainsAggregation(orderExpr),
   538  		})
   539  		qp.AddedColumn++
   540  	}
   541  
   542  	for idx, expr := range qp.SelectExprs {
   543  		aliasedExpr, err := expr.GetAliasedExpr()
   544  		if err != nil {
   545  			return nil, err
   546  		}
   547  
   548  		idxCopy := idx
   549  
   550  		if !sqlparser.ContainsAggregation(expr.Col) {
   551  			if !qp.isExprInGroupByExprs(ctx, expr) {
   552  				out = append(out, Aggr{
   553  					Original: aliasedExpr,
   554  					OpCode:   engine.AggregateRandom,
   555  					Alias:    aliasedExpr.ColumnName(),
   556  					Index:    &idxCopy,
   557  				})
   558  			}
   559  			continue
   560  		}
   561  		fnc, isAggregate := aliasedExpr.Expr.(sqlparser.AggrFunc)
   562  		if !isAggregate {
   563  			return nil, vterrors.VT12001("in scatter query: complex aggregate expression")
   564  		}
   565  
   566  		opcode, found := engine.SupportedAggregates[strings.ToLower(fnc.AggrName())]
   567  		if !found {
   568  			return nil, vterrors.VT12001(fmt.Sprintf("in scatter query: aggregation function '%s'", fnc.AggrName()))
   569  		}
   570  
   571  		if opcode == engine.AggregateCount {
   572  			if _, isStar := fnc.(*sqlparser.CountStar); isStar {
   573  				opcode = engine.AggregateCountStar
   574  			}
   575  		}
   576  
   577  		aggr, _ := aliasedExpr.Expr.(sqlparser.AggrFunc)
   578  
   579  		if aggr.IsDistinct() {
   580  			switch opcode {
   581  			case engine.AggregateCount:
   582  				opcode = engine.AggregateCountDistinct
   583  			case engine.AggregateSum:
   584  				opcode = engine.AggregateSumDistinct
   585  			}
   586  		}
   587  
   588  		out = append(out, Aggr{
   589  			Original: aliasedExpr,
   590  			Func:     aggr,
   591  			OpCode:   opcode,
   592  			Alias:    aliasedExpr.ColumnName(),
   593  			Index:    &idxCopy,
   594  			Distinct: aggr.IsDistinct(),
   595  		})
   596  	}
   597  	return
   598  }
   599  
   600  // FindSelectExprIndexForExpr returns the index of the given expression in the select expressions, if it is part of it
   601  // returns -1 otherwise.
   602  func (qp *QueryProjection) FindSelectExprIndexForExpr(ctx *plancontext.PlanningContext, expr sqlparser.Expr) (*int, *sqlparser.AliasedExpr) {
   603  	colExpr, isCol := expr.(*sqlparser.ColName)
   604  
   605  	for idx, selectExpr := range qp.SelectExprs {
   606  		aliasedExpr, isAliasedExpr := selectExpr.Col.(*sqlparser.AliasedExpr)
   607  		if !isAliasedExpr {
   608  			continue
   609  		}
   610  		if isCol {
   611  			isAliasExpr := !aliasedExpr.As.IsEmpty()
   612  			if isAliasExpr && colExpr.Name.Equal(aliasedExpr.As) {
   613  				return &idx, aliasedExpr
   614  			}
   615  		}
   616  		if ctx.SemTable.EqualsExpr(aliasedExpr.Expr, expr) {
   617  			return &idx, aliasedExpr
   618  		}
   619  	}
   620  	return nil, nil
   621  }
   622  
   623  // AlignGroupByAndOrderBy aligns the group by and order by columns, so they are in the same order
   624  // The GROUP BY clause is a set - the order between the elements does not make any difference,
   625  // so we can simply re-arrange the column order
   626  // We are also free to add more ORDER BY columns than the user asked for which we leverage,
   627  // so the input is already ordered according to the GROUP BY columns used
   628  func (qp *QueryProjection) AlignGroupByAndOrderBy(ctx *plancontext.PlanningContext) {
   629  	// The ORDER BY can be performed before the OA
   630  
   631  	var newGrouping []GroupBy
   632  	if len(qp.OrderExprs) == 0 {
   633  		// The query didn't ask for any particular order, so we are free to add arbitrary ordering.
   634  		// We'll align the grouping and ordering by the output columns
   635  		newGrouping = qp.GetGrouping()
   636  		SortGrouping(newGrouping)
   637  		for _, groupBy := range newGrouping {
   638  			qp.OrderExprs = append(qp.OrderExprs, groupBy.AsOrderBy())
   639  		}
   640  	} else {
   641  		// Here we align the GROUP BY and ORDER BY.
   642  		// First step is to make sure that the GROUP BY is in the same order as the ORDER BY
   643  		used := make([]bool, len(qp.groupByExprs))
   644  		for _, orderExpr := range qp.OrderExprs {
   645  			for i, groupingExpr := range qp.groupByExprs {
   646  				if !used[i] && ctx.SemTable.EqualsExpr(groupingExpr.WeightStrExpr, orderExpr.WeightStrExpr) {
   647  					newGrouping = append(newGrouping, groupingExpr)
   648  					used[i] = true
   649  				}
   650  			}
   651  		}
   652  		if len(newGrouping) != len(qp.groupByExprs) {
   653  			// we are missing some groupings. We need to add them both to the new groupings list, but also to the ORDER BY
   654  			for i, added := range used {
   655  				if !added {
   656  					groupBy := qp.groupByExprs[i]
   657  					newGrouping = append(newGrouping, groupBy)
   658  					qp.OrderExprs = append(qp.OrderExprs, groupBy.AsOrderBy())
   659  				}
   660  			}
   661  		}
   662  	}
   663  
   664  	qp.groupByExprs = newGrouping
   665  }
   666  
   667  // AddGroupBy does just that
   668  func (qp *QueryProjection) AddGroupBy(by GroupBy) {
   669  	qp.groupByExprs = append(qp.groupByExprs, by)
   670  }
   671  
   672  func (qp *QueryProjection) GetColumnCount() int {
   673  	return len(qp.SelectExprs) - qp.AddedColumn
   674  }
   675  
   676  func checkForInvalidGroupingExpressions(expr sqlparser.Expr) error {
   677  	return sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) {
   678  		if _, isAggregate := node.(sqlparser.AggrFunc); isAggregate {
   679  			return false, vterrors.VT03005(sqlparser.String(expr))
   680  		}
   681  		_, isSubQ := node.(*sqlparser.Subquery)
   682  		arg, isArg := node.(sqlparser.Argument)
   683  		if isSubQ || (isArg && strings.HasPrefix(string(arg), "__sq")) {
   684  			return false, vterrors.VT12001("subqueries in GROUP BY")
   685  		}
   686  		return true, nil
   687  	}, expr)
   688  }
   689  
   690  func SortAggregations(a []Aggr) {
   691  	sort.Slice(a, func(i, j int) bool {
   692  		return CompareRefInt(a[i].Index, a[j].Index)
   693  	})
   694  }
   695  
   696  func SortGrouping(a []GroupBy) {
   697  	sort.Slice(a, func(i, j int) bool {
   698  		return CompareRefInt(a[i].InnerIndex, a[j].InnerIndex)
   699  	})
   700  }
   701  
   702  // CompareRefInt compares two references of integers.
   703  // In case either one is nil, it is considered to be smaller
   704  func CompareRefInt(a *int, b *int) bool {
   705  	if a == nil {
   706  		return false
   707  	}
   708  	if b == nil {
   709  		return true
   710  	}
   711  	return *a < *b
   712  }