Repository: vitessio/vitess
Pull request #9643 (2022-02-07 14:54:54)

Gen4: Rework how aggregation is planned
## Description
Rework of how group by and aggregations are planned in Gen4.

We push down aggregations using the logic from the paper [Orthogonal Optimization of Subqueries and Aggregation](https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.563.8492&rep=rep1&type=pdf), by
Cesar A. Galindo-Legaria and Milind M. Joshi from Microsoft Corp.

It explains how one can split an aggregation into local aggregates that depend on only one side of the join.
The local aggregates can then be gathered together to produce the global
group by/aggregate query that the user asked for.

In Vitess, this is particularly useful because it allows us to push aggregation down to the routes, even when
we have to join the results at the vtgate level. Instead of doing all the grouping and aggregation at the
vtgate level, we can offload most of the work to MySQL, and at the vtgate just summarize the results.

It will support a lot more queries aggregating cross shards than the current planner does.

An example might be easier to follow. Take a simple aggregation query:

```sql
select user_extra.city, sum(user.salary) 
from user join user_extra on user.id = user_extra.id
group by user_extra.city
```

Because sharding and vschema, we are not able to merge the two sides of the join, and so we are forced to collect data from each table and do the join and aggregation on the vtgate.

So, we have to do joining on the vtgate, but we still need the inputs from the vttablets, The queries we'll send to the tablets will look like:

```sql
-- LEFT
select user.id, sum(user.salary)
from user 
group by user.id

-- RIGHT
select user_extra.city, count(*) 
from user_extra where  user_extra.id = :user_id
group by user_extra.city
```

The join we are executing is a nested loop join. So, for every row we see coming from the left input, we will execute the query on the right, with the bind variable `:user_id` set.

On the left hand side, we have changed the grouping to be on `user.id`, instead of the original, which was a column that is not available to us on the left side. Since the join predicate is on `user.id`, we can group all the rows with the same `user.id` together - we know they will match the same row on the right.

We add the `count(*)` to the right hand side query to delegate as much work as possible to MySQL - if there are multiple rows with the same `user_extra.city`, we want them grouped before they are sent over the wire. 

Now, after evaluating the join, we can multiply the `sum(user.salary)` with the `count(*)` from the right, and then do the grouping on the vtgate.


systay: I want to add more end to end tests, but there is enough to start reviewing already
systay: This PR now depends on #9807 and #9810 
systay: Should not be merged yet! Still chasing a bug that only manifests before the schema tracking has had a chance to catch up

Patch:
diff --git a/go/sqlescape/ids.go b/go/sqlescape/ids.go
index 3983db1336..b3084509b8 100644
--- a/go/sqlescape/ids.go
+++ b/go/sqlescape/ids.go
@@ -14,29 +14,22 @@ limitations under the License.
 package sqlescape
 
 import (
-	"strings"
+	"bytes"
 )
 
 // EscapeID returns a backticked identifier given an input string.
 func EscapeID(in string) string {
-	var buf strings.Builder
+	var buf bytes.Buffer
 	WriteEscapeID(&buf, in)
 	return buf.String()
 }
 
 // WriteEscapeID writes a backticked identifier from an input string into buf.
-func WriteEscapeID(buf *strings.Builder, in string) {
-	// growing by 4 more than the length, gives us room
-	// for guaranteed escaping with backticks on each end,
-	// plus a small amount of room just in case there are
-	// backticks within the symbol that needs to be double
-	// escaped. This is an unlikely edge case.
-	buf.Grow(4 + len(in))
-
+func WriteEscapeID(buf *bytes.Buffer, in string) {
 	buf.WriteByte('`')
-	for i := 0; i < len(in); i++ {
-		buf.WriteByte(in[i])
-		if in[i] == '`' {
+	for _, c := range in {
+		buf.WriteRune(c)
+		if c == '`' {
 			buf.WriteByte('`')
 		}
 	}
diff --git a/go/vt/sqlparser/ast.go b/go/vt/sqlparser/ast.go
index fde3869faf..2ad88b4a44 100644
--- a/go/vt/sqlparser/ast.go
+++ b/go/vt/sqlparser/ast.go
@@ -2206,6 +2206,9 @@ type (
 		argName      string
 		alternative  Expr // this is what will be used to Format this struct
 	}
+
+	// Offset is another AST type that is used during planning and never produced by the parser
+	Offset int
 )
 
 // iExpr ensures that only expressions nodes can be assigned to a Expr
@@ -2244,6 +2247,7 @@ func (*MatchExpr) iExpr()            {}
 func (*GroupConcatExpr) iExpr()      {}
 func (*Default) iExpr()              {}
 func (*ExtractedSubquery) iExpr()    {}
+func (Offset) iExpr()                {}
 
 // iCallable marks all expressions that represent function calls
 func (*FuncExpr) iCallable()             {}
diff --git a/go/vt/sqlparser/ast_clone.go b/go/vt/sqlparser/ast_clone.go
index 5fd7f5f891..2d6bf5816c 100644
--- a/go/vt/sqlparser/ast_clone.go
+++ b/go/vt/sqlparser/ast_clone.go
@@ -201,6 +201,8 @@ func CloneSQLNode(in SQLNode) SQLNode {
 		return CloneRefOfNotExpr(in)
 	case *NullVal:
 		return CloneRefOfNullVal(in)
+	case Offset:
+		return in
 	case OnDup:
 		return CloneOnDup(in)
 	case *OptLike:
@@ -2277,6 +2279,8 @@ func CloneExpr(in Expr) Expr {
 		return CloneRefOfNotExpr(in)
 	case *NullVal:
 		return CloneRefOfNullVal(in)
+	case Offset:
+		return in
 	case *OrExpr:
 		return CloneRefOfOrExpr(in)
 	case *Subquery:
diff --git a/go/vt/sqlparser/ast_equals.go b/go/vt/sqlparser/ast_equals.go
index 6cbc00c452..8cebffa229 100644
--- a/go/vt/sqlparser/ast_equals.go
+++ b/go/vt/sqlparser/ast_equals.go
@@ -560,6 +560,12 @@ func EqualsSQLNode(inA, inB SQLNode) bool {
 			return false
 		}
 		return EqualsRefOfNullVal(a, b)
+	case Offset:
+		b, ok := inB.(Offset)
+		if !ok {
+			return false
+		}
+		return a == b
 	case OnDup:
 		b, ok := inB.(OnDup)
 		if !ok {
@@ -3549,6 +3555,12 @@ func EqualsExpr(inA, inB Expr) bool {
 			return false
 		}
 		return EqualsRefOfNullVal(a, b)
+	case Offset:
+		b, ok := inB.(Offset)
+		if !ok {
+			return false
+		}
+		return a == b
 	case *OrExpr:
 		b, ok := inB.(*OrExpr)
 		if !ok {
diff --git a/go/vt/sqlparser/ast_format.go b/go/vt/sqlparser/ast_format.go
index 7ea8cec82b..584105f561 100644
--- a/go/vt/sqlparser/ast_format.go
+++ b/go/vt/sqlparser/ast_format.go
@@ -17,6 +17,7 @@ limitations under the License.
 package sqlparser
 
 import (
+	"strconv"
 	"strings"
 
 	"vitess.io/vitess/go/sqltypes"
@@ -1902,3 +1903,10 @@ func (node *RenameTable) Format(buf *TrackedBuffer) {
 func (node *ExtractedSubquery) Format(buf *TrackedBuffer) {
 	node.alternative.Format(buf)
 }
+
+// Format formats the node.
+func (node Offset) Format(buf *TrackedBuffer) {
+	buf.WriteString("[")
+	buf.WriteString(strconv.Itoa(int(node)))
+	buf.WriteString("]")
+}
diff --git a/go/vt/sqlparser/ast_format_fast.go b/go/vt/sqlparser/ast_format_fast.go
index f32d618982..e96712b018 100644
--- a/go/vt/sqlparser/ast_format_fast.go
+++ b/go/vt/sqlparser/ast_format_fast.go
@@ -19,6 +19,7 @@ package sqlparser
 
 import (
 	"fmt"
+	"strconv"
 	"strings"
 
 	"vitess.io/vitess/go/sqltypes"
@@ -2490,3 +2491,10 @@ func (node *RenameTable) formatFast(buf *TrackedBuffer) {
 func (node *ExtractedSubquery) formatFast(buf *TrackedBuffer) {
 	node.alternative.Format(buf)
 }
+
+// formatFast formats the node.
+func (node Offset) formatFast(buf *TrackedBuffer) {
+	buf.WriteString("[")
+	buf.WriteString(strconv.Itoa(int(node)))
+	buf.WriteString("]")
+}
diff --git a/go/vt/sqlparser/ast_funcs.go b/go/vt/sqlparser/ast_funcs.go
index 0c12e77cf8..4f51f57558 100644
--- a/go/vt/sqlparser/ast_funcs.go
+++ b/go/vt/sqlparser/ast_funcs.go
@@ -878,10 +878,8 @@ func (node *Select) AddWhere(expr Expr) {
 		}
 		return
 	}
-	node.Where.Expr = &AndExpr{
-		Left:  node.Where.Expr,
-		Right: expr,
-	}
+	exprs := SplitAndExpression(nil, node.Where.Expr)
+	node.Where.Expr = AndExpressions(append(exprs, expr)...)
 }
 
 // AddHaving adds the boolean expression to the
@@ -900,6 +898,17 @@ func (node *Select) AddHaving(expr Expr) {
 	}
 }
 
+// AddGroupBy adds a grouping expression, unless it's already present
+func (node *Select) AddGroupBy(expr Expr) {
+	for _, gb := range node.GroupBy {
+		if EqualsExpr(gb, expr) {
+			// group by columns are sets - duplicates don't add anything, so we can just skip these
+			return
+		}
+	}
+	node.GroupBy = append(node.GroupBy, expr)
+}
+
 // AddWhere adds the boolean expression to the
 // WHERE clause as an AND condition.
 func (node *Update) AddWhere(expr Expr) {
diff --git a/go/vt/sqlparser/ast_rewrite.go b/go/vt/sqlparser/ast_rewrite.go
index 21c4737420..7c507c43c5 100644
--- a/go/vt/sqlparser/ast_rewrite.go
+++ b/go/vt/sqlparser/ast_rewrite.go
@@ -200,6 +200,8 @@ func (a *application) rewriteSQLNode(parent SQLNode, node SQLNode, replacer repl
 		return a.rewriteRefOfNotExpr(parent, node, replacer)
 	case *NullVal:
 		return a.rewriteRefOfNullVal(parent, node, replacer)
+	case Offset:
+		return a.rewriteOffset(parent, node, replacer)
 	case OnDup:
 		return a.rewriteOnDup(parent, node, replacer)
 	case *OptLike:
@@ -5577,6 +5579,8 @@ func (a *application) rewriteExpr(parent SQLNode, node Expr, replacer replacerFu
 		return a.rewriteRefOfNotExpr(parent, node, replacer)
 	case *NullVal:
 		return a.rewriteRefOfNullVal(parent, node, replacer)
+	case Offset:
+		return a.rewriteOffset(parent, node, replacer)
 	case *OrExpr:
 		return a.rewriteRefOfOrExpr(parent, node, replacer)
 	case *Subquery:
@@ -5916,6 +5920,27 @@ func (a *application) rewriteListArg(parent SQLNode, node ListArg, replacer repl
 	}
 	return true
 }
+func (a *application) rewriteOffset(parent SQLNode, node Offset, replacer replacerFunc) bool {
+	if a.pre != nil {
+		a.cur.replacer = replacer
+		a.cur.parent = parent
+		a.cur.node = node
+		if !a.pre(&a.cur) {
+			return true
+		}
+	}
+	if a.post != nil {
+		if a.pre == nil {
+			a.cur.replacer = replacer
+			a.cur.parent = parent
+			a.cur.node = node
+		}
+		if !a.post(&a.cur) {
+			return false
+		}
+	}
+	return true
+}
 func (a *application) rewriteReferenceAction(parent SQLNode, node ReferenceAction, replacer replacerFunc) bool {
 	if a.pre != nil {
 		a.cur.replacer = replacer
diff --git a/go/vt/sqlparser/ast_visit.go b/go/vt/sqlparser/ast_visit.go
index 1f99f43613..5d5e0d8364 100644
--- a/go/vt/sqlparser/ast_visit.go
+++ b/go/vt/sqlparser/ast_visit.go
@@ -200,6 +200,8 @@ func VisitSQLNode(in SQLNode, f Visit) error {
 		return VisitRefOfNotExpr(in, f)
 	case *NullVal:
 		return VisitRefOfNullVal(in, f)
+	case Offset:
+		return VisitOffset(in, f)
 	case OnDup:
 		return VisitOnDup(in, f)
 	case *OptLike:
@@ -2819,6 +2821,8 @@ func VisitExpr(in Expr, f Visit) error {
 		return VisitRefOfNotExpr(in, f)
 	case *NullVal:
 		return VisitRefOfNullVal(in, f)
+	case Offset:
+		return VisitOffset(in, f)
 	case *OrExpr:
 		return VisitRefOfOrExpr(in, f)
 	case *Subquery:
@@ -3056,6 +3060,10 @@ func VisitListArg(in ListArg, f Visit) error {
 	_, err := f(in)
 	return err
 }
+func VisitOffset(in Offset, f Visit) error {
+	_, err := f(in)
+	return err
+}
 func VisitReferenceAction(in ReferenceAction, f Visit) error {
 	_, err := f(in)
 	return err
diff --git a/go/vt/vtgate/engine/ordered_aggregate.go b/go/vt/vtgate/engine/ordered_aggregate.go
index 1c6b82d592..215bc83171 100644
--- a/go/vt/vtgate/engine/ordered_aggregate.go
+++ b/go/vt/vtgate/engine/ordered_aggregate.go
@@ -27,8 +27,6 @@ import (
 	"google.golang.org/protobuf/proto"
 
 	"vitess.io/vitess/go/sqltypes"
-	"vitess.io/vitess/go/vt/proto/vtrpc"
-	"vitess.io/vitess/go/vt/vterrors"
 	"vitess.io/vitess/go/vt/vtgate/evalengine"
 
 	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
@@ -488,23 +486,6 @@ func merge(
 	return result, curDistincts, nil
 }
 
-func createEmptyValueFor(opcode AggregateOpcode) (sqltypes.Value, error) {
-	switch opcode {
-	case
-		AggregateCountDistinct,
-		AggregateCount:
-		return countZero, nil
-	case
-		AggregateSumDistinct,
-		AggregateSum,
-		AggregateMin,
-		AggregateMax:
-		return sqltypes.NULL, nil
-
-	}
-	return sqltypes.NULL, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "unknown aggregation %v", opcode)
-}
-
 func aggregateParamsToString(in interface{}) string {
 	return in.(*AggregateParams).String()
 }
diff --git a/go/vt/vtgate/engine/projection.go b/go/vt/vtgate/engine/projection.go
index 81d8db4e94..da6e98b13c 100644
--- a/go/vt/vtgate/engine/projection.go
+++ b/go/vt/vtgate/engine/projection.go
@@ -1,3 +1,19 @@
+/*
+Copyright 2022 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
 package engine
 
 import (
@@ -40,18 +56,18 @@ func (p *Projection) TryExecute(vcursor VCursor, bindVars map[string]*querypb.Bi
 
 	env := evalengine.EnvWithBindVars(bindVars, vcursor.ConnCollation())
 	env.Fields = result.Fields
-	var rows [][]sqltypes.Value
+	var resultRows []sqltypes.Row
 	for _, row := range result.Rows {
+		resultRow := make(sqltypes.Row, 0, len(p.Exprs))
 		env.Row = row
-		resRow := make([]sqltypes.Value, 0, len(p.Exprs))
 		for _, exp := range p.Exprs {
 			result, err := env.Evaluate(exp)
 			if err != nil {
 				return nil, err
 			}
-			resRow = append(resRow, result.Value())
+			resultRow = append(resultRow, result.Value())
 		}
-		rows = append(rows, resRow)
+		resultRows = append(resultRows, resultRow)
 	}
 	if wantfields {
 		err := p.addFields(env, result)
@@ -59,7 +75,7 @@ func (p *Projection) TryExecute(vcursor VCursor, bindVars map[string]*querypb.Bi
 			return nil, err
 		}
 	}
-	result.Rows = rows
+	result.Rows = resultRows
 	return result, nil
 }
 
@@ -131,14 +147,18 @@ func (p *Projection) Inputs() []Primitive {
 // description implements the Primitive interface
 func (p *Projection) description() PrimitiveDescription {
 	var exprs []string
-	for _, e := range p.Exprs {
-		exprs = append(exprs, evalengine.FormatExpr(e))
+	for idx, e := range p.Exprs {
+		expr := evalengine.FormatExpr(e)
+		alias := p.Cols[idx]
+		if alias != "" {
+			expr += " as " + alias
+		}
+		exprs = append(exprs, expr)
 	}
 	return PrimitiveDescription{
 		OperatorType: "Projection",
 		Other: map[string]interface{}{
 			"Expressions": exprs,
-			"Columns":     p.Cols,
 		},
 	}
 }
diff --git a/go/vt/vtgate/engine/scalar_aggregation.go b/go/vt/vtgate/engine/scalar_aggregation.go
index 10884ae5f8..baacc6aae8 100644
--- a/go/vt/vtgate/engine/scalar_aggregation.go
+++ b/go/vt/vtgate/engine/scalar_aggregation.go
@@ -20,6 +20,8 @@ import (
 	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/sqltypes"
 	querypb "vitess.io/vitess/go/vt/proto/query"
+	"vitess.io/vitess/go/vt/proto/vtrpc"
+	"vitess.io/vitess/go/vt/vterrors"
 )
 
 var _ Primitive = (*ScalarAggregate)(nil)
@@ -170,6 +172,23 @@ func (sa *ScalarAggregate) createEmptyRow() ([]sqltypes.Value, error) {
 	return out, nil
 }
 
+func createEmptyValueFor(opcode AggregateOpcode) (sqltypes.Value, error) {
+	switch opcode {
+	case
+		AggregateCountDistinct,
+		AggregateCount:
+		return countZero, nil
+	case
+		AggregateSumDistinct,
+		AggregateSum,
+		AggregateMin,
+		AggregateMax:
+		return sqltypes.NULL, nil
+
+	}
+	return sqltypes.NULL, vterrors.Errorf(vtrpc.Code_INVALID_ARGUMENT, "unknown aggregation %v", opcode)
+}
+
 // Inputs implements the Primitive interface
 func (sa *ScalarAggregate) Inputs() []Primitive {
 	return []Primitive{sa.Input}
diff --git a/go/vt/vtgate/evalengine/translate.go b/go/vt/vtgate/evalengine/translate.go
index 355bbd52f4..259f9876b1 100644
--- a/go/vt/vtgate/evalengine/translate.go
+++ b/go/vt/vtgate/evalengine/translate.go
@@ -502,6 +502,8 @@ func translateExpr(e sqlparser.Expr, lookup TranslationLookup) (Expr, error) {
 		return NewLiteralInt(0), nil
 	case *sqlparser.ColName:
 		return translateColName(node, lookup)
+	case sqlparser.Offset:
+		return NewColumn(int(node), getCollation(node, lookup)), nil
 	case *sqlparser.ComparisonExpr:
 		return translateComparisonExpr(node.Operator, node.Left, node.Right, lookup)
 	case sqlparser.Argument:
diff --git a/go/vt/vtgate/planbuilder/abstract/queryprojection.go b/go/vt/vtgate/planbuilder/abstract/queryprojection.go
index a0809136bf..28e6de8f39 100644
--- a/go/vt/vtgate/planbuilder/abstract/queryprojection.go
+++ b/go/vt/vtgate/planbuilder/abstract/queryprojection.go
@@ -18,8 +18,11 @@ package abstract
 
 import (
 	"encoding/json"
+	"sort"
 	"strings"
 
+	"vitess.io/vitess/go/vt/vtgate/engine"
+
 	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
 	"vitess.io/vitess/go/vt/sqlparser"
 	"vitess.io/vitess/go/vt/vterrors"
@@ -39,7 +42,7 @@ type (
 		SelectExprs        []SelectExpr
 		HasAggr            bool
 		Distinct           bool
-		GroupByExprs       []GroupBy
+		groupByExprs       []GroupBy
 		OrderExprs         []OrderBy
 		CanPushDownSorting bool
 		HasStar            bool
@@ -57,12 +60,44 @@ type (
 		Inner         sqlparser.Expr
 		WeightStrExpr sqlparser.Expr
 
-		// This is to add the distinct function expression in grouping column for pushing down but not be to used as grouping key at VTGate level.
-		// Starts with 1 so that default (0) means unassigned.
-		DistinctAggrIndex int
+		// The index at which the user expects to see this column. Set to nil, if the user does not ask for it
+		InnerIndex *int
+
+		// The original aliased expression that this group by is referring
+		aliasedExpr *sqlparser.AliasedExpr
 	}
 )
 
+func (b GroupBy) AsOrderBy() OrderBy {
+	return OrderBy{
+		Inner: &sqlparser.Order{
+			Expr:      b.Inner,
+			Direction: sqlparser.AscOrder,
+		},
+		WeightStrExpr: b.WeightStrExpr,
+	}
+}
+
+func (b GroupBy) AsAliasedExpr() *sqlparser.AliasedExpr {
+	if b.aliasedExpr != nil {
+		return b.aliasedExpr
+	}
+	col, isColName := b.Inner.(*sqlparser.ColName)
+	if isColName && b.WeightStrExpr != b.Inner {
+		return &sqlparser.AliasedExpr{
+			Expr: b.WeightStrExpr,
+			As:   col.Name,
+		}
+	}
+	if !isColName && b.WeightStrExpr != b.Inner {
+		panic("this should not happen - different inner and weighStringExpr and not a column alias")
+	}
+
+	return &sqlparser.AliasedExpr{
+		Expr: b.WeightStrExpr,
+	}
+}
+
 // GetExpr returns the underlying sqlparser.Expr of our SelectExpr
 func (s SelectExpr) GetExpr() (sqlparser.Expr, error) {
 	switch sel := s.Col.(type) {
@@ -97,7 +132,8 @@ func CreateQPFromSelect(sel *sqlparser.Select, semTable *semantics.SemTable) (*Q
 		return nil, err
 	}
 	for _, group := range sel.GroupBy {
-		expr, weightStrExpr, err := qp.getSimplifiedExpr(group, semTable)
+		selectExprIdx, aliasExpr := qp.FindSelectExprIndexForExpr(group)
+		expr, weightStrExpr, err := qp.GetSimplifiedExpr(group, semTable)
 		if err != nil {
 			return nil, err
 		}
@@ -106,7 +142,14 @@ func CreateQPFromSelect(sel *sqlparser.Select, semTable *semantics.SemTable) (*Q
 			return nil, err
 		}
 
-		qp.GroupByExprs = append(qp.GroupByExprs, GroupBy{Inner: expr, WeightStrExpr: weightStrExpr})
+		groupBy := GroupBy{
+			Inner:         expr,
+			WeightStrExpr: weightStrExpr,
+			InnerIndex:    selectExprIdx,
+			aliasedExpr:   aliasExpr,
+		}
+
+		qp.groupByExprs = append(qp.groupByExprs, groupBy)
 	}
 
 	err = qp.addOrderBy(sel.OrderBy, semTable)
@@ -114,12 +157,12 @@ func CreateQPFromSelect(sel *sqlparser.Select, semTable *semantics.SemTable) (*Q
 		return nil, err
 	}
 
-	if qp.HasAggr || len(qp.GroupByExprs) > 0 {
+	if qp.HasAggr || len(qp.groupByExprs) > 0 {
 		expr := qp.getNonAggrExprNotMatchingGroupByExprs()
 		// if we have aggregation functions, non aggregating columns and GROUP BY,
 		// the non-aggregating expressions must all be listed in the GROUP BY list
 		if expr != nil {
-			if len(qp.GroupByExprs) == 0 {
+			if len(qp.groupByExprs) == 0 {
 				return nil, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.MixOfGroupFuncAndFields, "In aggregated query without GROUP BY, expression of SELECT list contains nonaggregated column '%s'; this is incompatible with sql_mode=only_full_group_by", sqlparser.String(expr))
 			}
 			qp.ProjectionError = vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.WrongFieldWithGroup, "Expression of SELECT list is not in GROUP BY clause and contains nonaggregated column '%s' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by", sqlparser.String(expr))
@@ -127,7 +170,7 @@ func CreateQPFromSelect(sel *sqlparser.Select, semTable *semantics.SemTable) (*Q
 	}
 
 	if qp.Distinct && !qp.HasAggr {
-		qp.GroupByExprs = nil
+		qp.groupByExprs = nil
 	}
 
 	return qp, nil
@@ -184,10 +227,14 @@ func CreateQPFromUnion(union *sqlparser.Union, semTable *semantics.SemTable) (*Q
 func (qp *QueryProjection) addOrderBy(orderBy sqlparser.OrderBy, semTable *semantics.SemTable) error {
 	canPushDownSorting := true
 	for _, order := range orderBy {
-		expr, weightStrExpr, err := qp.getSimplifiedExpr(order.Expr, semTable)
+		expr, weightStrExpr, err := qp.GetSimplifiedExpr(order.Expr, semTable)
 		if err != nil {
 			return err
 		}
+		if sqlparser.IsNull(weightStrExpr) {
+			// ORDER BY null can safely be ignored
+			continue
+		}
 		qp.OrderExprs = append(qp.OrderExprs, OrderBy{
 			Inner: &sqlparser.Order{
 				Expr:      expr,
@@ -201,6 +248,13 @@ func (qp *QueryProjection) addOrderBy(orderBy sqlparser.OrderBy, semTable *seman
 	return nil
 }
 
+// GetGrouping returns a copy of the grouping parameters of the QP
+func (qp *QueryProjection) GetGrouping() []GroupBy {
+	out := make([]GroupBy, len(qp.groupByExprs))
+	copy(out, qp.groupByExprs)
+	return out
+}
+
 func checkForInvalidAggregations(exp *sqlparser.AliasedExpr) error {
 	return sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
 		fExpr, ok := node.(*sqlparser.FuncExpr)
@@ -219,7 +273,7 @@ func (qp *QueryProjection) getNonAggrExprNotMatchingGroupByExprs() sqlparser.Sel
 			continue
 		}
 		isGroupByOk := false
-		for _, groupByExpr := range qp.GroupByExprs {
+		for _, groupByExpr := range qp.groupByExprs {
 			exp, err := expr.GetExpr()
 			if err != nil {
 				return expr.Col
@@ -239,7 +293,7 @@ func (qp *QueryProjection) getNonAggrExprNotMatchingGroupByExprs() sqlparser.Sel
 			continue
 		}
 		isGroupByOk := false
-		for _, groupByExpr := range qp.GroupByExprs {
+		for _, groupByExpr := range qp.groupByExprs {
 			if sqlparser.EqualsExpr(groupByExpr.WeightStrExpr, order.WeightStrExpr) {
 				isGroupByOk = true
 				break
@@ -254,8 +308,11 @@ func (qp *QueryProjection) getNonAggrExprNotMatchingGroupByExprs() sqlparser.Sel
 	return nil
 }
 
-// getSimplifiedExpr takes an expression used in ORDER BY or GROUP BY, and returns an expression that is simpler to evaluate
-func (qp *QueryProjection) getSimplifiedExpr(e sqlparser.Expr, semTable *semantics.SemTable) (expr sqlparser.Expr, weightStrExpr sqlparser.Expr, err error) {
+// GetSimplifiedExpr takes an expression used in ORDER BY or GROUP BY, and returns an expression that is simpler to evaluate
+func (qp *QueryProjection) GetSimplifiedExpr(
+	e sqlparser.Expr,
+	semTable *semantics.SemTable,
+) (expr sqlparser.Expr, weightStrExpr sqlparser.Expr, err error) {
 	// If the ORDER BY is against a column alias, we need to remember the expression
 	// behind the alias. The weightstring(.) calls needs to be done against that expression and not the alias.
 	// Eg - select music.foo as bar, weightstring(music.foo) from music order by bar
@@ -324,7 +381,7 @@ func (qp *QueryProjection) toString() string {
 		out.Select = append(out.Select, e)
 	}
 
-	for _, expr := range qp.GroupByExprs {
+	for _, expr := range qp.groupByExprs {
 		out.Grouping = append(out.Grouping, sqlparser.String(expr.Inner))
 	}
 	for _, expr := range qp.OrderExprs {
@@ -337,7 +394,7 @@ func (qp *QueryProjection) toString() string {
 
 // NeedsAggregation returns true if we either have aggregate functions or grouping defined
 func (qp *QueryProjection) NeedsAggregation() bool {
-	return qp.HasAggr || len(qp.GroupByExprs) > 0
+	return qp.HasAggr || len(qp.groupByExprs) > 0
 }
 
 func (qp QueryProjection) onlyAggr() bool {
@@ -357,12 +414,143 @@ func (qp *QueryProjection) NeedsDistinct() bool {
 	if !qp.Distinct {
 		return false
 	}
-	if qp.onlyAggr() && len(qp.GroupByExprs) == 0 {
+	if qp.onlyAggr() && len(qp.groupByExprs) == 0 {
 		return false
 	}
 	return true
 }
 
+type Aggr struct {
+	Original *sqlparser.AliasedExpr
+	Func     *sqlparser.FuncExpr
+	OpCode   engine.AggregateOpcode
+	Alias    string
+	// The index at which the user expects to see this aggregated function. Set to nil, if the user does not ask for it
+	Index    *int
+	Distinct bool
+}
+
+func (qp *QueryProjection) AggregationExpressions() (out []Aggr, err error) {
+	for idx, expr := range qp.SelectExprs {
+		if !sqlparser.ContainsAggregation(expr.Col) {
+			continue
+		}
+		aliasedExpr, err := expr.GetAliasedExpr()
+		if err != nil {
+			return nil, err
+		}
+		fExpr, isFunc := aliasedExpr.Expr.(*sqlparser.FuncExpr)
+		if !isFunc {
+			return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: in scatter query: complex aggregate expression")
+		}
+
+		funcName := fExpr.Name.Lowered()
+		opcode, found := engine.SupportedAggregates[funcName]
+		if !found {
+			return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: in scatter query: aggregation function '%s'", funcName)
+		}
+
+		if fExpr.Distinct {
+			switch opcode {
+			case engine.AggregateCount:
+				opcode = engine.AggregateCountDistinct
+			case engine.AggregateSum:
+				opcode = engine.AggregateSumDistinct
+			}
+		}
+
+		var alias string
+		if aliasedExpr.As.IsEmpty() {
+			alias = sqlparser.String(aliasedExpr.Expr)
+		} else {
+			alias = aliasedExpr.As.String()
+		}
+
+		idxCopy := idx
+		out = append(out, Aggr{
+			Original: aliasedExpr,
+			Func:     fExpr,
+			OpCode:   opcode,
+			Alias:    alias,
+			Index:    &idxCopy,
+			Distinct: fExpr.Distinct,
+		})
+	}
+	return
+}
+
+// FindSelectExprIndexForExpr returns the index of the given expression in the select expressions, if it is part of it
+// returns -1 otherwise.
+func (qp *QueryProjection) FindSelectExprIndexForExpr(expr sqlparser.Expr) (*int, *sqlparser.AliasedExpr) {
+	colExpr, isCol := expr.(*sqlparser.ColName)
+
+	for idx, selectExpr := range qp.SelectExprs {
+		aliasedExpr, isAliasedExpr := selectExpr.Col.(*sqlparser.AliasedExpr)
+		if !isAliasedExpr {
+			continue
+		}
+		if isCol {
+			isAliasExpr := !aliasedExpr.As.IsEmpty()
+			if isAliasExpr && colExpr.Name.Equal(aliasedExpr.As) {
+				return &idx, aliasedExpr
+			}
+		}
+		if sqlparser.EqualsExpr(aliasedExpr.Expr, expr) {
+			return &idx, aliasedExpr
+		}
+	}
+	return nil, nil
+}
+
+// AlignGroupByAndOrderBy aligns the group by and order by columns, so they are in the same order
+// The GROUP BY clause is a set - the order between the elements does not make any difference,
+// so we can simply re-arrange the column order
+// We are also free to add more ORDER BY columns than the user asked for which we leverage,
+// so the input is already ordered according to the GROUP BY columns used
+func (qp *QueryProjection) AlignGroupByAndOrderBy() {
+	// The ORDER BY can be performed before the OA
+
+	var newGrouping []GroupBy
+	if len(qp.OrderExprs) == 0 {
+		// The query didn't ask for any particular order, so we are free to add arbitrary ordering.
+		// We'll align the grouping and ordering by the output columns
+		newGrouping = qp.GetGrouping()
+		sort.Sort(GroupBys(newGrouping))
+		for _, groupBy := range newGrouping {
+			qp.OrderExprs = append(qp.OrderExprs, groupBy.AsOrderBy())
+		}
+	} else {
+		// Here we align the GROUP BY and ORDER BY.
+		// First step is to make sure that the GROUP BY is in the same order as the ORDER BY
+		used := make([]bool, len(qp.groupByExprs))
+		for _, orderExpr := range qp.OrderExprs {
+			for i, groupingExpr := range qp.groupByExprs {
+				if !used[i] && sqlparser.EqualsExpr(groupingExpr.WeightStrExpr, orderExpr.WeightStrExpr) {
+					newGrouping = append(newGrouping, groupingExpr)
+					used[i] = true
+				}
+			}
+		}
+		if len(newGrouping) != len(qp.groupByExprs) {
+			// we are missing some groupings. We need to add them both to the new groupings list, but also to the ORDER BY
+			for i, added := range used {
+				if !added {
+					groupBy := qp.groupByExprs[i]
+					newGrouping = append(newGrouping, groupBy)
+					qp.OrderExprs = append(qp.OrderExprs, groupBy.AsOrderBy())
+				}
+			}
+		}
+	}
+
+	qp.groupByExprs = newGrouping
+}
+
+// AddGroupBy does just that
+func (qp *QueryProjection) AddGroupBy(by GroupBy) {
+	qp.groupByExprs = append(qp.groupByExprs, by)
+}
+
 func checkForInvalidGroupingExpressions(expr sqlparser.Expr) error {
 	return sqlparser.Walk(func(node sqlparser.SQLNode) (bool, error) {
 		if sqlparser.IsAggregation(node) {
@@ -376,3 +564,49 @@ func checkForInvalidGroupingExpressions(expr sqlparser.Expr) error {
 		return true, nil
 	}, expr)
 }
+
+type Aggrs []Aggr
+
+// Len implements the sort.Interface
+func (a Aggrs) Len() int {
+	return len(a)
+}
+
+// Less implements the sort.Interface
+func (a Aggrs) Less(i, j int) bool {
+	return CompareRefInt(a[i].Index, a[j].Index)
+}
+
+// Swap implements the sort.Interface
+func (a Aggrs) Swap(i, j int) {
+	a[i], a[j] = a[j], a[i]
+}
+
+// CompareRefInt compares two references of integers.
+// In case either one is nil, it is considered to be smaller
+func CompareRefInt(a *int, b *int) bool {
+	if a == nil {
+		return false
+	}
+	if b == nil {
+		return true
+	}
+	return *a < *b
+}
+
+type GroupBys []GroupBy
+
+// Len implements the sort.Interface
+func (gbys GroupBys) Len() int {
+	return len(gbys)
+}
+
+// Less implements the sort.Interface
+func (gbys GroupBys) Less(i, j int) bool {
+	return CompareRefInt(gbys[i].InnerIndex, gbys[j].InnerIndex)
+}
+
+// Swap implements the sort.Interface
+func (gbys GroupBys) Swap(i, j int) {
+	gbys[i], gbys[j] = gbys[j], gbys[i]
+}
diff --git a/go/vt/vtgate/planbuilder/aggregation_pushing.go b/go/vt/vtgate/planbuilder/aggregation_pushing.go
new file mode 100644
index 0000000000..75997117f2
--- /dev/null
+++ b/go/vt/vtgate/planbuilder/aggregation_pushing.go
@@ -0,0 +1,505 @@
+/*
+Copyright 2022 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package planbuilder
+
+import (
+	"sort"
+
+	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
+	"vitess.io/vitess/go/vt/sqlparser"
+	"vitess.io/vitess/go/vt/vterrors"
+	"vitess.io/vitess/go/vt/vtgate/engine"
+	"vitess.io/vitess/go/vt/vtgate/planbuilder/abstract"
+	"vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext"
+)
+
+// pushAggregation pushes grouping and aggregation as far down in the tree as possible
+func (hp *horizonPlanning) pushAggregation(
+	ctx *plancontext.PlanningContext,
+	plan logicalPlan,
+	grouping []abstract.GroupBy,
+	aggregations []abstract.Aggr,
+	ignoreOutputOrder bool,
+) (groupingOffsets []offsets, outputAggrsOffset [][]offsets, err error) {
+	switch plan := plan.(type) {
+	case *routeGen4:
+		groupingOffsets, outputAggrsOffset, err = pushAggrOnRoute(ctx, plan, aggregations, grouping, ignoreOutputOrder)
+		if err != nil {
+			return nil, nil, err
+		}
+		return
+
+	case *joinGen4:
+		return hp.pushAggrOnJoin(ctx, grouping, aggregations, plan)
+
+	case *semiJoin:
+		return hp.pushAggrOnSemiJoin(ctx, grouping, aggregations, plan, ignoreOutputOrder)
+
+	default:
+		return nil, nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "using aggregation on top of a %T plan is not yet supported", plan)
+	}
+}
+
+func pushAggrOnRoute(
+	ctx *plancontext.PlanningContext,
+	plan *routeGen4,
+	aggregations []abstract.Aggr,
+	grouping []abstract.GroupBy,
+	ignoreOutputOrder bool,
+) ([]offsets, [][]offsets, error) {
+	columnOrderMatters := !ignoreOutputOrder
+	sel, isSel := plan.Select.(*sqlparser.Select)
+	if !isSel {
+		return nil, nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "can't plan aggregation on union")
+	}
+
+	var vtgateAggregation [][]offsets
+	var groupingCols []int
+	var reorg = passThrough
+
+	if columnOrderMatters {
+		// During this first run, we push the projections for the normal columns (not the weigh_string ones, that is)
+		// in the order that the user asked for it
+		// sortOffsets also returns a reorgFunc,
+		// that can be used to rearrange the produced outputs to the original order
+		var it *sortedIterator
+		var err error
+		grouping, reorg, it = sortOffsets(grouping, aggregations)
+		vtgateAggregation, groupingCols, err = pushAggrsAndGroupingInOrder(ctx, plan, it, sel, vtgateAggregation, groupingCols)
+		if err != nil {
+			return nil, nil, err
+		}
+	} else {
+		// if we haven't already pushed the aggregations, now is the time
+		for _, aggregation := range aggregations {
+			param := addAggregationToSelect(sel, aggregation)
+			vtgateAggregation = append(vtgateAggregation, []offsets{param})
+		}
+	}
+
+	groupingOffsets := make([]offsets, 0, len(grouping))
+	for idx, expr := range grouping {
+		sel.AddGroupBy(expr.Inner)
+		var pos offsets
+		if ignoreOutputOrder {
+			// we have not yet pushed anything, so we need to push the expression first
+			col, _, err := addExpressionToRoute(ctx, plan, &sqlparser.AliasedExpr{Expr: expr.Inner}, true)
+			if err != nil {
+				return nil, nil, err
+			}
+			pos = newOffset(col)
+		} else {
+			pos = newOffset(groupingCols[idx])
+		}
+
+		if ctx.SemTable.NeedsWeightString(expr.Inner) {
+			wsExpr := weightStringFor(expr.WeightStrExpr)
+			wsCol, _, err := addExpressionToRoute(ctx, plan, &sqlparser.AliasedExpr{Expr: wsExpr}, true)
+			if err != nil {
+				return nil, nil, err
+			}
+			pos.wsCol = wsCol
+			sel.AddGroupBy(wsExpr)
+		}
+		groupingOffsets = append(groupingOffsets, pos)
+	}
+
+	groupingOffsets, vtgateAggregation = reorg(groupingOffsets, vtgateAggregation)
+	return groupingOffsets, vtgateAggregation, nil
+}
+
+func pushAggrsAndGroupingInOrder(
+	ctx *plancontext.PlanningContext,
+	plan *routeGen4,
+	it *sortedIterator,
+	sel *sqlparser.Select,
+	vtgateAggregation [][]offsets,
+	groupingCols []int,
+) ([][]offsets, []int, error) {
+	for it.next() {
+		groupBy, aggregation := it.current()
+		if aggregation != nil {
+			param := addAggregationToSelect(sel, *aggregation)
+			vtgateAggregation = append(vtgateAggregation, []offsets{param})
+			continue
+		}
+		if groupBy != nil {
+			reuseCol := groupBy.InnerIndex == nil
+			col, _, err := addExpressionToRoute(ctx, plan, groupBy.AsAliasedExpr(), reuseCol)
+			groupingCols = append(groupingCols, col)
+			if err != nil {
+				return nil, nil, err
+			}
+		}
+	}
+	return vtgateAggregation, groupingCols, nil
+}
+
+// addAggregationToSelect adds the aggregation to the SELECT statement and returns the AggregateParams to be used outside
+func addAggregationToSelect(sel *sqlparser.Select, aggregation abstract.Aggr) offsets {
+	// TODO: removing duplicated aggregation expression should also be done at the join level
+	for i, expr := range sel.SelectExprs {
+		aliasedExpr, isAliasedExpr := expr.(*sqlparser.AliasedExpr)
+		if !isAliasedExpr {
+			continue
+		}
+		if sqlparser.EqualsExpr(aliasedExpr.Expr, aggregation.Func) {
+			return newOffset(i)
+		}
+	}
+
+	sel.SelectExprs = append(sel.SelectExprs, aggregation.Original)
+	return newOffset(len(sel.SelectExprs) - 1)
+}
+
+func countStarAggr() *abstract.Aggr {
+	f := &sqlparser.FuncExpr{
+		Name:     sqlparser.NewColIdent("count"),
+		Distinct: false,
+		Exprs:    []sqlparser.SelectExpr{&sqlparser.StarExpr{}},
+	}
+
+	return &abstract.Aggr{
+		Original: &sqlparser.AliasedExpr{Expr: f},
+		Func:     f,
+		OpCode:   engine.AggregateCount,
+		Alias:    "count(*)",
+	}
+}
+
+/*
+We push down aggregations using the logic from the paper Orthogonal Optimization of Subqueries and Aggregation, by
+Cesar A. Galindo-Legaria and Milind M. Joshi from Microsoft Corp.
+
+It explains how one can split an aggregation into local aggregates that depend on only one side of the join.
+The local aggregates can then be gathered together to produce the global
+group by/aggregate query that the user asked for.
+
+In Vitess, this is particularly useful because it allows us to push aggregation down to the routes, even when
+we have to join the results at the vtgate level. Instead of doing all the grouping and aggregation at the
+vtgate level, we can offload most of the work to MySQL, and at the vtgate just summarize the results.
+*/
+func (hp *horizonPlanning) pushAggrOnJoin(
+	ctx *plancontext.PlanningContext,
+	grouping []abstract.GroupBy,
+	aggregations []abstract.Aggr,
+	join *joinGen4,
+) ([]offsets, [][]offsets, error) {
+	// First we separate aggregations according to which side the dependencies are coming from
+	lhsAggrs, rhsAggrs, err := splitAggregationsToLeftAndRight(ctx, aggregations, join)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// We need to group by the columns used in the join condition.
+	// If we don't, the LHS will not be able to return the column, and it can't be used to send down to the RHS
+	lhsCols, err := hp.createGroupingsForColumns(ctx, join.LHSColumns)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// Here we split the grouping depending on if they should with the LHS or RHS of the query
+	// This is done by using the semantic table and checking dependencies
+	lhsGrouping, rhsGrouping, groupingOffsets, err := splitGroupingsToLeftAndRight(ctx, join, grouping, lhsCols)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// Next we push the aggregations to both sides
+	lhsOffsets, lhsAggrOffsets, err := hp.filteredPushAggregation(ctx, join.Left, lhsGrouping, lhsAggrs, true)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	rhsOffsets, rhsAggrOffsets, err := hp.filteredPushAggregation(ctx, join.Right, rhsGrouping, rhsAggrs, true)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// Next, we have to pass through the grouping values through the join and the projection we add on top
+	// We added new groupings to the LHS because of the join condition, so we don't want to pass through everything,
+	// just the groupings that are used by operators on top of this current one
+	wsOutputGrpOffset := len(groupingOffsets) + len(join.Cols)
+	outputGroupings := make([]offsets, 0, len(groupingOffsets))
+	var wsOffsets []int
+	for _, groupBy := range groupingOffsets {
+		var offset offsets
+		var f func(i int) int
+		if groupBy < 0 {
+			offset = lhsOffsets[-groupBy-1]
+			f = func(i int) int { return -(i + 1) }
+		} else {
+			offset = rhsOffsets[groupBy-1]
+			f = func(i int) int { return i + 1 }
+		}
+		outputGrouping := newOffset(len(join.Cols))
+		join.Cols = append(join.Cols, f(offset.col))
+		if offset.wsCol > -1 {
+			// we add the weight_string calls at the end of the join columns
+			outputGrouping.wsCol = wsOutputGrpOffset + len(wsOffsets)
+			wsOffsets = append(wsOffsets, f(offset.wsCol))
+		}
+		outputGroupings = append(outputGroupings, outputGrouping)
+	}
+	join.Cols = append(join.Cols, wsOffsets...)
+
+	outputAggrOffsets := make([][]offsets, 0, len(aggregations))
+	for idx := range aggregations {
+		l, r := lhsAggrOffsets[idx], rhsAggrOffsets[idx]
+		var offSlice []offsets
+		for _, off := range l {
+			offSlice = append(offSlice, newOffset(len(join.Cols)))
+			join.Cols = append(join.Cols, -(off.col + 1))
+		}
+		for _, off := range r {
+			offSlice = append(offSlice, newOffset(len(join.Cols)))
+			join.Cols = append(join.Cols, off.col+1)
+		}
+		outputAggrOffsets = append(outputAggrOffsets, offSlice)
+	}
+	return outputGroupings, outputAggrOffsets, err
+}
+
+/*
+pushAggrOnSemiJoin works similarly to pushAggrOnJoin, but it's simpler, because we don't get any inputs from the RHS,
+so there are no aggregations or groupings that have to be sent to the RHS
+
+We do however need to add the columns used in the subquery coming from the LHS to the grouping.
+That way we get the aggregation grouped by the column we need to use to decide if the row should
+*/
+func (hp *horizonPlanning) pushAggrOnSemiJoin(
+	ctx *plancontext.PlanningContext,
+	grouping []abstract.GroupBy,
+	aggregations []abstract.Aggr,
+	join *semiJoin,
+	ignoreOutputOrder bool,
+) ([]offsets, [][]offsets, error) {
+	// We need to group by the columns used in the join condition.
+	// If we don't, the LHS will not be able to return the column, and it can't be used to send down to the RHS
+	lhsCols, err := hp.createGroupingsForColumns(ctx, join.LHSColumns)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	totalGrouping := append(grouping, lhsCols...)
+	groupingOffsets, aggrParams, err := hp.pushAggregation(ctx, join.lhs, totalGrouping, aggregations, ignoreOutputOrder)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	outputGroupings := make([]offsets, 0, len(grouping))
+	for idx := range grouping {
+		outputGroupings = append(outputGroupings, groupingOffsets[idx])
+	}
+
+	return outputGroupings, aggrParams, nil
+}
+
+// this method takes a slice of aggregations that can have missing spots in the form of `nil`,
+// and pushes the non-empty values down.
+// during aggregation planning, it's important to know which of
+// the incoming aggregations correspond to what is sent to the LHS and RHS.
+// Some aggregations only need to be sent to one of the sides of the join, and in that case,
+// the other side will have a nil in this offset of the aggregations
+func (hp *horizonPlanning) filteredPushAggregation(
+	ctx *plancontext.PlanningContext,
+	plan logicalPlan,
+	grouping []abstract.GroupBy,
+	aggregations []*abstract.Aggr,
+	ignoreOutputOrder bool,
+) (groupingOffsets []offsets, outputAggrs [][]offsets, err error) {
+	used := make([]bool, len(aggregations))
+	var aggrs []abstract.Aggr
+
+	for idx, aggr := range aggregations {
+		if aggr != nil {
+			used[idx] = true
+			aggrs = append(aggrs, *aggr)
+		}
+	}
+	groupingOffsets, pushedAggrs, err := hp.pushAggregation(ctx, plan, grouping, aggrs, ignoreOutputOrder)
+	if err != nil {
+		return nil, nil, err
+	}
+	idx := 0
+	for _, b := range used {
+		if !b {
+			outputAggrs = append(outputAggrs, nil)
+			continue
+		}
+		outputAggrs = append(outputAggrs, pushedAggrs[idx])
+		idx++
+	}
+	return groupingOffsets, outputAggrs, nil
+}
+
+func isMinOrMax(in engine.AggregateOpcode) bool {
+	switch in {
+	case engine.AggregateMin, engine.AggregateMax:
+		return true
+	default:
+		return false
+	}
+}
+
+func splitAggregationsToLeftAndRight(
+	ctx *plancontext.PlanningContext,
+	aggregations []abstract.Aggr,
+	join *joinGen4,
+) ([]*abstract.Aggr, []*abstract.Aggr, error) {
+	var lhsAggrs, rhsAggrs []*abstract.Aggr
+	for _, aggr := range aggregations {
+		newAggr := aggr
+		if isCountStar(aggr.Func) {
+			lhsAggrs = append(lhsAggrs, &newAggr)
+			rhsAggrs = append(rhsAggrs, &newAggr)
+		} else {
+			deps := ctx.SemTable.RecursiveDeps(aggr.Func)
+			var other *abstract.Aggr
+			// if we are sending down min/max, we don't have to multiply the results with anything
+			if !isMinOrMax(aggr.OpCode) {
+				other = countStarAggr()
+			}
+			switch {
+			case deps.IsSolvedBy(join.Left.ContainsTables()):
+				lhsAggrs = append(lhsAggrs, &newAggr)
+				rhsAggrs = append(rhsAggrs, other)
+			case deps.IsSolvedBy(join.Right.ContainsTables()):
+				rhsAggrs = append(rhsAggrs, &newAggr)
+				lhsAggrs = append(lhsAggrs, other)
+			default:
+				return nil, nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "aggregation on columns from different sources not supported yet")
+			}
+		}
+	}
+	return lhsAggrs, rhsAggrs, nil
+}
+
+func splitGroupingsToLeftAndRight(
+	ctx *plancontext.PlanningContext,
+	join *joinGen4,
+	grouping, lhsGrouping []abstract.GroupBy,
+) ([]abstract.GroupBy, []abstract.GroupBy, []int, error) {
+	var rhsGrouping []abstract.GroupBy
+
+	lhsTS := join.Left.ContainsTables()
+	rhsTS := join.Right.ContainsTables()
+	// here we store information about which side the grouping value is coming from.
+	// Negative values from the left operator and positive values are offsets into the RHS
+	var groupingOffsets []int
+	for _, groupBy := range grouping {
+		deps := ctx.SemTable.RecursiveDeps(groupBy.Inner)
+		switch {
+		case deps.IsSolvedBy(lhsTS):
+			groupingOffsets = append(groupingOffsets, -(len(lhsGrouping) + 1))
+			lhsGrouping = append(lhsGrouping, groupBy)
+		case deps.IsSolvedBy(rhsTS):
+			groupingOffsets = append(groupingOffsets, len(rhsGrouping)+1)
+			rhsGrouping = append(rhsGrouping, groupBy)
+		default:
+			return nil, nil, nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "grouping on columns from different sources not supported yet")
+		}
+	}
+	return lhsGrouping, rhsGrouping, groupingOffsets, nil
+}
+
+type (
+	reorgFunc      = func(groupByOffsets []offsets, aggrOffsets [][]offsets) ([]offsets, [][]offsets)
+	sortedIterator struct {
+		grouping     []abstract.GroupBy
+		aggregations []abstract.Aggr
+		valueGB      *abstract.GroupBy
+		valueA       *abstract.Aggr
+		groupbyIdx   int
+		aggrIdx      int
+	}
+)
+
+func (it *sortedIterator) current() (*abstract.GroupBy, *abstract.Aggr) {
+	return it.valueGB, it.valueA
+}
+
+func (it *sortedIterator) next() bool {
+	if it.aggrIdx < len(it.aggregations) && it.groupbyIdx < len(it.grouping) {
+		aggregation := it.aggregations[it.aggrIdx]
+		groupBy := it.grouping[it.groupbyIdx]
+		if abstract.CompareRefInt(aggregation.Index, groupBy.InnerIndex) {
+			it.aggrIdx++
+			it.valueA, it.valueGB = &aggregation, nil
+			return true
+		}
+		it.groupbyIdx++
+		it.valueA, it.valueGB = nil, &groupBy
+		return true
+	}
+
+	if it.groupbyIdx < len(it.grouping) {
+		groupBy := it.grouping[it.groupbyIdx]
+		it.groupbyIdx++
+		it.valueA, it.valueGB = nil, &groupBy
+		return true
+	}
+	if it.aggrIdx < len(it.aggregations) {
+		aggregation := it.aggregations[it.aggrIdx]
+		it.aggrIdx++
+		it.valueA, it.valueGB = &aggregation, nil
+		return true
+	}
+	return false
+}
+
+func passThrough(groupByOffsets []offsets, aggrOffsets [][]offsets) ([]offsets, [][]offsets) {
+	return groupByOffsets, aggrOffsets
+}
+
+func sortOffsets(grouping []abstract.GroupBy, aggregations []abstract.Aggr) ([]abstract.GroupBy, reorgFunc, *sortedIterator) {
+	originalGrouping := make([]abstract.GroupBy, len(grouping))
+	originalAggr := make([]abstract.Aggr, len(aggregations))
+	copy(originalAggr, aggregations)
+	copy(originalGrouping, grouping)
+	sort.Sort(abstract.Aggrs(aggregations))
+	sort.Sort(abstract.GroupBys(grouping))
+
+	reorg := func(groupByOffsets []offsets, aggrOffsets [][]offsets) ([]offsets, [][]offsets) {
+		orderedGroupingOffsets := make([]offsets, 0, len(originalGrouping))
+		for _, og := range originalGrouping {
+			for i, g := range grouping {
+				if og.Inner == g.Inner {
+					orderedGroupingOffsets = append(orderedGroupingOffsets, groupByOffsets[i])
+					break
+				}
+			}
+		}
+
+		orderedAggrs := make([][]offsets, 0, len(originalAggr))
+		for _, og := range originalAggr {
+			for i, g := range aggregations {
+				if og.Func == g.Func {
+					orderedAggrs = append(orderedAggrs, aggrOffsets[i])
+					break
+				}
+			}
+		}
+
+		return orderedGroupingOffsets, orderedAggrs
+	}
+
+	return grouping, reorg, &sortedIterator{
+		grouping:     grouping,
+		aggregations: aggregations,
+	}
+}
diff --git a/go/vt/vtgate/planbuilder/horizon_planning.go b/go/vt/vtgate/planbuilder/horizon_planning.go
index aedd72c683..bb0267aadd 100644
--- a/go/vt/vtgate/planbuilder/horizon_planning.go
+++ b/go/vt/vtgate/planbuilder/horizon_planning.go
@@ -17,7 +17,6 @@ limitations under the License.
 package planbuilder
 
 import (
-	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/sqltypes"
 	"vitess.io/vitess/go/vt/vtgate/evalengine"
 	"vitess.io/vitess/go/vt/vtgate/planbuilder/abstract"
@@ -34,9 +33,8 @@ import (
 )
 
 type horizonPlanning struct {
-	sel            *sqlparser.Select
-	qp             *abstract.QueryProjection
-	vtgateGrouping bool
+	sel *sqlparser.Select
+	qp  *abstract.QueryProjection
 }
 
 func (hp *horizonPlanning) planHorizon(ctx *plancontext.PlanningContext, plan logicalPlan) (logicalPlan, error) {
@@ -54,30 +52,25 @@ func (hp *horizonPlanning) planHorizon(ctx *plancontext.PlanningContext, plan lo
 		return plan, nil
 	}
 
-	qp, err := abstract.CreateQPFromSelect(hp.sel, ctx.SemTable)
+	var err error
+	hp.qp, err = abstract.CreateQPFromSelect(hp.sel, ctx.SemTable)
 	if err != nil {
 		return nil, err
 	}
 
-	hp.qp = qp
-
-	needAggrOrHaving := hp.qp.NeedsAggregation() || hp.sel.Having != nil
-	canShortcut := isRoute && !needAggrOrHaving && len(hp.qp.OrderExprs) == 0
+	needsOrdering := len(hp.qp.OrderExprs) > 0
+	canShortcut := isRoute && hp.sel.Having == nil && !needsOrdering
 
-	if needAggrOrHaving {
+	// If we still have a HAVING clause, it's because it could not be pushed to the WHERE,
+	// so it probably has aggregations
+	if hp.qp.NeedsAggregation() || hp.sel.Having != nil {
 		plan, err = hp.planAggregations(ctx, plan)
 		if err != nil {
 			return nil, err
 		}
+		// if we already did sorting, we don't need to do it again
+		needsOrdering = needsOrdering && !hp.qp.CanPushDownSorting
 	} else {
-		_, isOA := plan.(*orderedAggregate)
-		if isOA {
-			plan = &simpleProjection{
-				logicalPlanCommon: newBuilderCommon(plan),
-				eSimpleProj:       &engine.SimpleProjection{},
-			}
-		}
-
 		if canShortcut {
 			err = planSingleShardRoutePlan(hp.sel, rb)
 			if err != nil {
@@ -91,21 +84,11 @@ func (hp *horizonPlanning) planHorizon(ctx *plancontext.PlanningContext, plan lo
 		}
 	}
 
-	// If we have done the shortcut that means we already planned order by
-	// and group by, thus we don't need to do it again.
-	if !canShortcut {
-		if len(hp.qp.OrderExprs) > 0 {
-			plan, err = hp.planOrderBy(ctx, hp.qp.OrderExprs, plan)
-			if err != nil {
-				return nil, err
-			}
-		}
-
-		if hp.qp.CanPushDownSorting && hp.vtgateGrouping {
-			plan, err = hp.planGroupByUsingOrderBy(ctx, plan)
-			if err != nil {
-				return nil, err
-			}
+	// If we didn't already take care of ORDER BY during aggregation planning, we need to handle it now
+	if needsOrdering {
+		plan, err = hp.planOrderBy(ctx, hp.qp.OrderExprs, plan)
+		if err != nil {
+			return nil, err
 		}
 	}
 
@@ -169,7 +152,12 @@ func (hp *horizonPlanning) truncateColumnsIfNeeded(ctx *plancontext.PlanningCont
 }
 
 // pushProjection pushes a projection to the plan.
-func pushProjection(ctx *plancontext.PlanningContext, expr *sqlparser.AliasedExpr, plan logicalPlan, inner, reuseCol, hasAggregation bool) (offset int, added bool, err error) {
+func pushProjection(
+	ctx *plancontext.PlanningContext,
+	expr *sqlparser.AliasedExpr,
+	plan logicalPlan,
+	inner, reuseCol, hasAggregation bool,
+) (offset int, added bool, err error) {
 	switch node := plan.(type) {
 	case *routeGen4:
 		_, isColName := expr.Expr.(*sqlparser.ColName)
@@ -183,28 +171,7 @@ func pushProjection(ctx *plancontext.PlanningContext, expr *sqlparser.AliasedExp
 				}
 			}
 		}
-		if reuseCol {
-			if i := checkIfAlreadyExists(expr, node.Select, ctx.SemTable); i != -1 {
-				return i, false, nil
-			}
-		}
-		expr.Expr = sqlparser.RemoveKeyspaceFromColName(expr.Expr)
-		sel, isSel := node.Select.(*sqlparser.Select)
-		if !isSel {
-			return 0, false, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.BadFieldError, "unsupported: pushing projection '%s' on %T", sqlparser.String(expr), node.Select)
-		}
-
-		// if we are trying to push a projection that belongs to a DerivedTable
-		// we rewrite that expression, so it matches the column name used inside
-		// that derived table.
-		err = rewriteProjectionOfDerivedTable(expr, ctx.SemTable)
-		if err != nil {
-			return 0, false, err
-		}
-
-		offset := len(sel.SelectExprs)
-		sel.SelectExprs = append(sel.SelectExprs, expr)
-		return offset, true, nil
+		return addExpressionToRoute(ctx, node, expr, reuseCol)
 	case *hashJoin:
 		lhsSolves := node.Left.ContainsTables()
 		rhsSolves := node.Right.ContainsTables()
@@ -317,9 +284,6 @@ func pushProjection(ctx *plancontext.PlanningContext, expr *sqlparser.AliasedExp
 		}
 		node.Cols = append(node.Cols, column)
 		return len(node.Cols) - 1, true, nil
-	case *pulloutSubquery:
-		// push projection to the outer query
-		return pushProjection(ctx, expr, node.underlying, inner, reuseCol, hasAggregation)
 	case *simpleProjection:
 		offset, _, err := pushProjection(ctx, expr, node.input, inner, true, hasAggregation)
 		if err != nil {
@@ -357,12 +321,10 @@ func pushProjection(ctx *plancontext.PlanningContext, expr *sqlparser.AliasedExp
 			return 0, false, err
 		}
 		return i /* col added */, len(node.eVindexFunc.Cols) > colsBefore, nil
-	case *limit:
-		return pushProjection(ctx, expr, node.input, inner, reuseCol, hasAggregation)
-	case *distinct:
-		return pushProjection(ctx, expr, node.input, inner, reuseCol, hasAggregation)
-	case *filter:
-		return pushProjection(ctx, expr, node.input, inner, reuseCol, hasAggregation)
+	case *limit, *projection, *pulloutSubquery, *distinct, *filter:
+		// All of these either push to the single source, or push to the LHS
+		src := node.Inputs()[0]
+		return pushProjection(ctx, expr, src, inner, reuseCol, hasAggregation)
 	case *semiJoin:
 		passDownReuseCol := reuseCol
 		if !reuseCol {
@@ -407,6 +369,31 @@ func pushProjection(ctx *plancontext.PlanningContext, expr *sqlparser.AliasedExp
 	}
 }
 
+func addExpressionToRoute(ctx *plancontext.PlanningContext, rb *routeGen4, expr *sqlparser.AliasedExpr, reuseCol bool) (int, bool, error) {
+	if reuseCol {
+		if i := checkIfAlreadyExists(expr, rb.Select, ctx.SemTable); i != -1 {
+			return i, false, nil
+		}
+	}
+	expr.Expr = sqlparser.RemoveKeyspaceFromColName(expr.Expr)
+	sel, isSel := rb.Select.(*sqlparser.Select)
+	if !isSel {
+		return 0, false, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.BadFieldError, "unsupported: pushing projection '%s' on %T", sqlparser.String(expr), rb.Select)
+	}
+
+	// if we are trying to push a projection that belongs to a DerivedTable
+	// we rewrite that expression, so it matches the column name used inside
+	// that derived table.
+	err := rewriteProjectionOfDerivedTable(expr, ctx.SemTable)
+	if err != nil {
+		return 0, false, err
+	}
+
+	offset := len(sel.SelectExprs)
+	sel.SelectExprs = append(sel.SelectExprs, expr)
+	return offset, true, nil
+}
+
 func rewriteProjectionOfDerivedTable(expr *sqlparser.AliasedExpr, semTable *semantics.SemTable) error {
 	ti, err := semTable.TableInfoForExpr(expr.Expr)
 	if err != nil && err != semantics.ErrMultipleTables {
@@ -431,6 +418,21 @@ func checkIfAlreadyExists(expr *sqlparser.AliasedExpr, node sqlparser.SelectStat
 	// all these three cases are handled by the call to GetFirstSelect.
 	sel := sqlparser.GetFirstSelect(node)
 
+	exprCol, isExprCol := expr.Expr.(*sqlparser.ColName)
+
+	// first pass - search for aliased expressions
+	for i, selectExpr := range sel.SelectExprs {
+		if !isExprCol {
+			break
+		}
+
+		selectExpr, ok := selectExpr.(*sqlparser.AliasedExpr)
+		if ok && selectExpr.As.Equal(exprCol.Name) {
+			return i
+		}
+	}
+
+	// next pass - we are searching the actual expressions and not the aliases
 	for i, selectExpr := range sel.SelectExprs {
 		selectExpr, ok := selectExpr.(*sqlparser.AliasedExpr)
 		if !ok {
@@ -438,26 +440,20 @@ func checkIfAlreadyExists(expr *sqlparser.AliasedExpr, node sqlparser.SelectStat
 		}
 
 		selectExprCol, isSelectExprCol := selectExpr.Expr.(*sqlparser.ColName)
-		exprCol, isExprCol := expr.Expr.(*sqlparser.ColName)
 		selectExprDep := semTable.RecursiveDeps(selectExpr.Expr)
 
 		// Check that the two expressions have the same dependencies
-		if selectExprDep != exprDep {
+		if !selectExprDep.Equals(exprDep) {
 			continue
 		}
 
-		if selectExpr.As.IsEmpty() {
-			// we don't have an alias
+		if isSelectExprCol && isExprCol && exprCol.Name.Equal(selectExprCol.Name) {
+			// the expressions are ColName, we compare their name
+			return i
+		}
 
-			if isSelectExprCol && isExprCol && exprCol.Name.Equal(selectExprCol.Name) {
-				// the expressions are ColName, we compare their name
-				return i
-			} else if sqlparser.EqualsExpr(selectExpr.Expr, expr.Expr) {
-				// the expressions are not ColName, so we just compare the expressions
-				return i
-			}
-		} else if isExprCol && selectExpr.As.Equal(exprCol.Name) {
-			// we have an aliased column, checking if the expression is matching the alias
+		if sqlparser.EqualsExpr(selectExpr.Expr, expr.Expr) {
+			// the expressions are not ColName, so we just compare the expressions
 			return i
 		}
 	}
@@ -465,240 +461,361 @@ func checkIfAlreadyExists(expr *sqlparser.AliasedExpr, node sqlparser.SelectStat
 }
 
 func (hp *horizonPlanning) planAggregations(ctx *plancontext.PlanningContext, plan logicalPlan) (logicalPlan, error) {
-	newPlan := plan
-	var oa *orderedAggregate
-	uniqVindex := hasUniqueVindex(ctx.VSchema, ctx.SemTable, hp.qp.GroupByExprs)
-	joinPlan := isJoin(plan)
-	if !uniqVindex || joinPlan {
-		if hp.qp.ProjectionError != nil {
-			return nil, hp.qp.ProjectionError
-		}
-		oa = &orderedAggregate{
-			resultsBuilder: resultsBuilder{
-				logicalPlanCommon: newBuilderCommon(plan),
-				weightStrings:     make(map[*resultColumn]int),
-			},
-		}
-		newPlan = oa
-		hp.vtgateGrouping = true
-	}
-
-	if joinPlan && hp.qp.HasAggr && len(hp.qp.GroupByExprs) > 0 {
-		return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: cross-shard query with aggregates")
-	}
-
-	for _, e := range hp.qp.SelectExprs {
-		aliasExpr, err := e.GetAliasedExpr()
+	isPushable := !isJoin(plan)
+	grouping := hp.qp.GetGrouping()
+	vindexOverlapWithGrouping := hasUniqueVindex(ctx.VSchema, ctx.SemTable, grouping)
+	if isPushable && vindexOverlapWithGrouping {
+		// If we have a plan that we can push the group by and aggregation through, we don't need to do aggregation
+		// at the vtgate level at all
+		err := hp.planAggregationWithoutOA(ctx, plan)
 		if err != nil {
 			return nil, err
 		}
-
-		// push all expression if they are non-aggregating or the plan is not ordered aggregated plan.
-		if !e.Aggr || oa == nil {
-			_, _, err := pushProjection(ctx, aliasExpr, plan, true, false, false)
-			if err != nil {
-				return nil, err
-			}
-			continue
-		}
-
-		fExpr, isFunc := aliasExpr.Expr.(*sqlparser.FuncExpr)
-		if !isFunc {
-			return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: in scatter query: complex aggregate expression")
-		}
-		funcName := fExpr.Name.Lowered()
-		opcode, found := engine.SupportedAggregates[funcName]
-		if !found {
-			return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: in scatter query: aggregation function '%s'", funcName)
-		}
-		handleDistinct, innerAliased, err := hp.needDistinctHandling(ctx, fExpr, opcode, plan)
+		resultPlan, err := hp.planOrderBy(ctx, hp.qp.OrderExprs, plan)
 		if err != nil {
 			return nil, err
 		}
 
-		pushExpr, param := hp.createPushExprAndAlias(ctx, e, handleDistinct, innerAliased, opcode, oa)
-		offset, _, err := pushProjection(ctx, pushExpr, plan, true, false, true)
+		newPlan, err := hp.planHaving(ctx, resultPlan)
 		if err != nil {
 			return nil, err
 		}
-		param.Col = offset
-		param.Expr = fExpr
-		oa.aggregates = append(oa.aggregates, param)
+
+		return newPlan, nil
+	}
+
+	if hp.qp.ProjectionError != nil {
+		return nil, hp.qp.ProjectionError
+	}
+
+	return hp.planAggrUsingOA(ctx, plan, grouping)
+}
+
+func (hp *horizonPlanning) planAggrUsingOA(
+	ctx *plancontext.PlanningContext,
+	plan logicalPlan,
+	grouping []abstract.GroupBy,
+) (logicalPlan, error) {
+	oa := &orderedAggregate{
+		groupByKeys: make([]*engine.GroupByParams, 0, len(grouping)),
+	}
+
+	var order []abstract.OrderBy
+	if hp.qp.CanPushDownSorting {
+		hp.qp.AlignGroupByAndOrderBy()
+		// the grouping order might have changed, so we reload the grouping expressions
+		grouping = hp.qp.GetGrouping()
+		order = hp.qp.OrderExprs
+	} else {
+		for _, expr := range grouping {
+			order = append(order, expr.AsOrderBy())
+		}
+	}
+
+	// here we are building up the grouping keys for the OA,
+	// but they are lacking the input offsets because we have yet to push the columns down
+	for _, expr := range grouping {
+		oa.groupByKeys = append(oa.groupByKeys, &engine.GroupByParams{
+			Expr:        expr.Inner,
+			FromGroupBy: true,
+			CollationID: ctx.SemTable.CollationForExpr(expr.Inner),
+		})
+	}
+
+	aggregationExprs, err := hp.qp.AggregationExpressions()
+	if err != nil {
+		return nil, err
 	}
 
-	for _, groupExpr := range hp.qp.GroupByExprs {
-		err := planGroupByGen4(ctx, groupExpr, newPlan, false)
+	// If we have a distinct aggregating expression,
+	// we handle it by pushing it down to the underlying input as a grouping column
+	distinctGroupBy, distinctOffsets, aggrs, err := hp.handleDistinctAggr(ctx, aggregationExprs)
+	if err != nil {
+		return nil, err
+	}
+
+	if len(distinctGroupBy) > 0 {
+		grouping = append(grouping, distinctGroupBy...)
+		// all the distinct grouping aggregates use the same expression, so it should be OK to just add it once
+		order = append(order, distinctGroupBy[0].AsOrderBy())
+		oa.preProcess = true
+	}
+
+	groupingOffsets, aggrParamOffsets, err := hp.pushAggregation(ctx, plan, grouping, aggrs, false)
+	if err != nil {
+		return nil, err
+	}
+
+	_, isRoute := plan.(*routeGen4)
+	needsProj := !isRoute
+	var aggPlan = plan
+	var proj *projection
+	if needsProj {
+		length := getLengthOfProjection(groupingOffsets, aggrs)
+		proj = &projection{
+			source:      plan,
+			columns:     make([]sqlparser.Expr, length),
+			columnNames: make([]string, length),
+		}
+		aggPlan = proj
+	}
+
+	aggrParams, err := generateAggregateParams(aggrs, aggrParamOffsets, proj)
+	if err != nil {
+		return nil, err
+	}
+
+	if proj != nil {
+		groupingOffsets, err = passGroupingColumns(proj, groupingOffsets, grouping)
 		if err != nil {
 			return nil, err
 		}
 	}
 
-	newPlan, err := hp.planHaving(ctx, newPlan)
+	// Next we add the aggregation expressions and grouping offsets to the OA
+	addColumnsToOA(ctx, oa, distinctGroupBy, aggrParams, distinctOffsets, groupingOffsets, aggregationExprs)
+
+	aggPlan, err = hp.planOrderBy(ctx, order, aggPlan)
 	if err != nil {
 		return nil, err
 	}
 
-	if !hp.qp.CanPushDownSorting && oa != nil {
-		var orderExprs []abstract.OrderBy
-		// if we can't at a later stage push down the sorting to our inputs, we have to do ordering here
-		for _, groupExpr := range hp.qp.GroupByExprs {
-			orderExprs = append(orderExprs, abstract.OrderBy{
-				Inner:         &sqlparser.Order{Expr: groupExpr.Inner},
-				WeightStrExpr: groupExpr.WeightStrExpr},
-			)
+	oa.resultsBuilder = resultsBuilder{
+		logicalPlanCommon: newBuilderCommon(aggPlan),
+		weightStrings:     make(map[*resultColumn]int),
+	}
+
+	return hp.planHaving(ctx, oa)
+}
+
+func passGroupingColumns(proj *projection, groupings []offsets, grouping []abstract.GroupBy) (projGrpOffsets []offsets, err error) {
+	for idx, grp := range groupings {
+		origGrp := grouping[idx]
+		var offs offsets
+		offs.col, err = proj.addColumn(origGrp.InnerIndex, sqlparser.Offset(grp.col), "")
+		if err != nil {
+			return nil, err
 		}
-		if len(orderExprs) > 0 {
-			newInput, err := hp.planOrderBy(ctx, orderExprs, plan)
+		if grp.wsCol != -1 {
+			offs.wsCol, err = proj.addColumn(nil, sqlparser.Offset(grp.wsCol), "")
 			if err != nil {
 				return nil, err
 			}
-			oa.input = newInput
-			plan = oa
 		}
-	} else {
-		plan = newPlan
+		projGrpOffsets = append(projGrpOffsets, offs)
 	}
+	return projGrpOffsets, nil
+}
+
+func generateAggregateParams(aggrs []abstract.Aggr, aggrParamOffsets [][]offsets, proj *projection) ([]*engine.AggregateParams, error) {
+	aggrParams := make([]*engine.AggregateParams, len(aggrs))
+	for idx, paramOffset := range aggrParamOffsets {
+		aggr := aggrs[idx]
+		incomingOffset := paramOffset[0].col
+		var offset int
+		if proj != nil {
+			var aggrExpr sqlparser.Expr
+			for _, ofs := range paramOffset {
+				curr := sqlparser.Offset(ofs.col)
+				if aggrExpr == nil {
+					aggrExpr = curr
+				} else {
+					aggrExpr = &sqlparser.BinaryExpr{
+						Operator: sqlparser.MultOp,
+						Left:     aggrExpr,
+						Right:    curr,
+					}
+				}
+			}
 
-	// done with aggregation planning. let's check if we should fail the query
-	if _, planIsRoute := plan.(*routeGen4); !planIsRoute {
-		// if we had to build up additional operators around the route, we have to fail this query
-		for _, expr := range hp.qp.SelectExprs {
-			colExpr, err := expr.GetExpr()
+			pos, err := proj.addColumn(aggr.Index, aggrExpr, aggr.Alias)
 			if err != nil {
 				return nil, err
 			}
-			if !sqlparser.IsAggregation(colExpr) && sqlparser.ContainsAggregation(colExpr) {
-				return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: in scatter query: complex aggregate expression")
-			}
+			offset = pos
+		} else {
+			offset = incomingOffset
 		}
-	}
 
-	return plan, nil
+		opcode := engine.AggregateSum
+		if aggr.OpCode == engine.AggregateMin ||
+			aggr.OpCode == engine.AggregateMax {
+			opcode = aggr.OpCode
+		}
+
+		aggrParams[idx] = &engine.AggregateParams{
+			Opcode: opcode,
+			Col:    offset,
+			Alias:  aggr.Alias,
+			Expr:   aggr.Func,
+		}
+	}
+	return aggrParams, nil
 }
 
-// createPushExprAndAlias creates the expression that should be pushed down to the leaves,
-// and changes the opcode, so it is a distinct one if needed
-func (hp *horizonPlanning) createPushExprAndAlias(
+func addColumnsToOA(
 	ctx *plancontext.PlanningContext,
-	expr abstract.SelectExpr,
-	handleDistinct bool,
-	innerAliased *sqlparser.AliasedExpr,
-	opcode engine.AggregateOpcode,
 	oa *orderedAggregate,
-) (*sqlparser.AliasedExpr, *engine.AggregateParams) {
-	aliasExpr, isAlias := expr.Col.(*sqlparser.AliasedExpr)
-	if !isAlias {
-		return nil, nil
-	}
-	var alias string
-	if aliasExpr.As.IsEmpty() {
-		alias = sqlparser.String(aliasExpr.Expr)
+	// these are the group by expressions that where added because we have unique aggregations
+	distinctGroupBy []abstract.GroupBy,
+	// these are the aggregate params we already have for non-distinct aggregations
+	aggrParams []*engine.AggregateParams,
+	// distinctOffsets mark out where we need to use the distinctGroupBy offsets
+	// to create *engine.AggregateParams for the distinct aggregations
+	distinctOffsets []int,
+	// these are the offsets for the group by params
+	groupings []offsets,
+	// aggregationExprs are all the original aggregation expressions the query requested
+	aggregationExprs []abstract.Aggr,
+) {
+	if len(distinctGroupBy) == 0 {
+		// no distinct aggregations
+		oa.aggregates = aggrParams
 	} else {
-		alias = aliasExpr.As.String()
-	}
-	if handleDistinct {
-		aliasExpr = innerAliased
-
-		switch opcode {
-		case engine.AggregateCount:
-			opcode = engine.AggregateCountDistinct
-		case engine.AggregateSum:
-			opcode = engine.AggregateSumDistinct
+		count := len(groupings) - len(distinctOffsets)
+		addDistinctAggr := func(offset int) {
+			// the last grouping we pushed is the one we added for the distinct aggregation
+			o := groupings[count]
+			count++
+			a := aggregationExprs[offset]
+			collID := ctx.SemTable.CollationForExpr(a.Func.Exprs[0].(*sqlparser.AliasedExpr).Expr)
+			oa.aggregates = append(oa.aggregates, &engine.AggregateParams{
+				Opcode:      a.OpCode,
+				Col:         o.col,
+				KeyCol:      o.col,
+				WAssigned:   o.wsCol >= 0,
+				WCol:        o.wsCol,
+				Alias:       a.Alias,
+				Expr:        a.Func,
+				CollationID: collID,
+			})
+		}
+		lastOffset := distinctOffsets[len(distinctOffsets)-1]
+		distinctIdx := 0
+		for i := 0; i <= lastOffset || i <= len(aggrParams); i++ {
+			for distinctIdx < len(distinctOffsets) && i == distinctOffsets[distinctIdx] {
+				// we loop here since we could be dealing with multiple distinct aggregations after each other
+				addDistinctAggr(i)
+				distinctIdx++
+			}
+			if i < len(aggrParams) {
+				oa.aggregates = append(oa.aggregates, aggrParams[i])
+			}
 		}
 
-		oa.preProcess = true
-		by := abstract.GroupBy{
-			Inner:             innerAliased.Expr,
-			WeightStrExpr:     innerAliased.Expr,
-			DistinctAggrIndex: len(oa.aggregates) + 1,
-		}
-		hp.qp.GroupByExprs = append(hp.qp.GroupByExprs, by)
-	}
-	collID := collations.Unknown
-	if innerAliased != nil {
-		collID = ctx.SemTable.CollationForExpr(innerAliased.Expr)
+		// we have to remove the tail of the grouping offsets, so we only have the offsets for the GROUP BY in the query
+		groupings = groupings[:len(groupings)-len(distinctOffsets)]
 	}
 
-	param := &engine.AggregateParams{
-		Opcode:      opcode,
-		Alias:       alias,
-		CollationID: collID,
+	for i, grouping := range groupings {
+		oa.groupByKeys[i].KeyCol = grouping.col
+		oa.groupByKeys[i].WeightStringCol = grouping.wsCol
 	}
-	return aliasExpr, param
 }
 
-func hasUniqueVindex(vschema plancontext.VSchema, semTable *semantics.SemTable, groupByExprs []abstract.GroupBy) bool {
-	for _, groupByExpr := range groupByExprs {
-		if exprHasUniqueVindex(vschema, semTable, groupByExpr.WeightStrExpr) {
-			return true
+// handleDistinctAggr takes in a slice of aggregations and returns GroupBy elements that replace
+// the distinct aggregations in the input, along with a slice of offsets and the non-distinct aggregations left,
+// so we can later reify the original aggregations
+func (hp *horizonPlanning) handleDistinctAggr(ctx *plancontext.PlanningContext, exprs []abstract.Aggr) (
+	distincts []abstract.GroupBy, offsets []int, aggrs []abstract.Aggr, err error) {
+	var distinctExpr sqlparser.Expr
+	for i, expr := range exprs {
+		if !expr.Distinct {
+			aggrs = append(aggrs, expr)
+			continue
+		}
+		aliasedExpr, ok := expr.Func.Exprs[0].(*sqlparser.AliasedExpr)
+		if !ok {
+			err = vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "syntax error: %s", sqlparser.String(expr.Original))
+			return
 		}
+		inner, innerWS, err := hp.qp.GetSimplifiedExpr(aliasedExpr.Expr, ctx.SemTable)
+		if err != nil {
+			return nil, nil, nil, err
+		}
+		if exprHasVindex(ctx.VSchema, ctx.SemTable, innerWS, false) {
+			aggrs = append(aggrs, expr)
+			continue
+		}
+		if distinctExpr == nil {
+			distinctExpr = innerWS
+		} else {
+			if !sqlparser.EqualsExpr(distinctExpr, innerWS) {
+				err = vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: only one distinct aggregation allowed in a select: %s", sqlparser.String(expr.Original))
+				return nil, nil, nil, err
+			}
+		}
+		distincts = append(distincts, abstract.GroupBy{
+			Inner:         inner,
+			WeightStrExpr: innerWS,
+			InnerIndex:    expr.Index,
+		})
+		offsets = append(offsets, i)
 	}
-	return false
+	return
 }
 
-func planGroupByGen4(ctx *plancontext.PlanningContext, groupExpr abstract.GroupBy, plan logicalPlan, wsAdded bool) error {
-	switch node := plan.(type) {
-	case *routeGen4:
-		sel := node.Select.(*sqlparser.Select)
-		sel.GroupBy = append(sel.GroupBy, groupExpr.Inner)
-		// If a weight_string function is added to the select list,
-		// then we need to add that to the group by clause otherwise the query will fail on mysql with full_group_by error
-		// as the weight_string function might not be functionally dependent on the group by.
-		if wsAdded {
-			sel.GroupBy = append(sel.GroupBy, weightStringFor(groupExpr.WeightStrExpr))
-		}
-		return nil
-	case *joinGen4, *hashJoin:
-		_, _, err := wrapAndPushExpr(ctx, groupExpr.Inner, groupExpr.WeightStrExpr, node)
-		return err
-	case *orderedAggregate:
-		keyCol, wsOffset, err := wrapAndPushExpr(ctx, groupExpr.Inner, groupExpr.WeightStrExpr, node.input)
+func (hp *horizonPlanning) planAggregationWithoutOA(ctx *plancontext.PlanningContext, plan logicalPlan) error {
+	for _, expr := range hp.qp.SelectExprs {
+		aliasedExpr, err := expr.GetAliasedExpr()
 		if err != nil {
 			return err
 		}
-		if groupExpr.DistinctAggrIndex == 0 {
-			node.groupByKeys = append(node.groupByKeys, &engine.GroupByParams{KeyCol: keyCol, WeightStringCol: wsOffset, Expr: groupExpr.WeightStrExpr, CollationID: ctx.SemTable.CollationForExpr(groupExpr.Inner)})
-		} else {
-			if wsOffset != -1 {
-				node.aggregates[groupExpr.DistinctAggrIndex-1].WAssigned = true
-				node.aggregates[groupExpr.DistinctAggrIndex-1].WCol = wsOffset
-			}
+		_, _, err = pushProjection(ctx, aliasedExpr, plan, true, false, false)
+		if err != nil {
+			return err
 		}
-		err = planGroupByGen4(ctx, groupExpr, node.input, wsOffset != -1)
+	}
+	for _, expr := range hp.qp.GetGrouping() {
+		// since all the grouping will be done at the mysql level,
+		// we know that we won't need any weight_string() calls
+		err := planGroupByGen4(ctx, expr, plan /*weighString*/, false)
 		if err != nil {
 			return err
 		}
-		return nil
-	case *pulloutSubquery:
-		return planGroupByGen4(ctx, groupExpr, node.underlying, wsAdded)
-	case *semiJoin:
-		return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: group by in a query having a correlated subquery")
-	default:
-		return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: group by on: %T", plan)
 	}
+	return nil
 }
 
-func (hp *horizonPlanning) planGroupByUsingOrderBy(ctx *plancontext.PlanningContext, plan logicalPlan) (logicalPlan, error) {
-	var orderExprs []abstract.OrderBy
-	for _, groupExpr := range hp.qp.GroupByExprs {
-		addExpr := true
-		for _, orderExpr := range hp.qp.OrderExprs {
-			if sqlparser.EqualsExpr(groupExpr.Inner, orderExpr.Inner.Expr) {
-				addExpr = false
-				break
-			}
-		}
-		if addExpr {
-			orderExprs = append(orderExprs, abstract.OrderBy{
-				Inner:         &sqlparser.Order{Expr: groupExpr.Inner},
-				WeightStrExpr: groupExpr.WeightStrExpr},
-			)
+type offsets struct {
+	col, wsCol int
+}
+
+func newOffset(col int) offsets {
+	return offsets{col: col, wsCol: -1}
+}
+
+func (hp *horizonPlanning) createGroupingsForColumns(
+	ctx *plancontext.PlanningContext,
+	columns []*sqlparser.ColName,
+) ([]abstract.GroupBy, error) {
+	var lhsGrouping []abstract.GroupBy
+	for _, lhsColumn := range columns {
+		expr, wsExpr, err := hp.qp.GetSimplifiedExpr(lhsColumn, ctx.SemTable)
+		if err != nil {
+			return nil, err
 		}
+
+		lhsGrouping = append(lhsGrouping, abstract.GroupBy{
+			Inner:         expr,
+			WeightStrExpr: wsExpr,
+		})
 	}
-	if len(orderExprs) > 0 {
-		return hp.planOrderBy(ctx, orderExprs, plan)
+	return lhsGrouping, nil
+}
+
+func isCountStar(f *sqlparser.FuncExpr) bool {
+	_, isStar := f.Exprs[0].(*sqlparser.StarExpr)
+	return isStar
+}
+
+func hasUniqueVindex(vschema plancontext.VSchema, semTable *semantics.SemTable, groupByExprs []abstract.GroupBy) bool {
+	for _, groupByExpr := range groupByExprs {
+		if exprHasUniqueVindex(vschema, semTable, groupByExpr.WeightStrExpr) {
+			return true
+		}
 	}
-	return plan, nil
+	return false
 }
 
 func (hp *horizonPlanning) planOrderBy(ctx *plancontext.PlanningContext, orderExprs []abstract.OrderBy, plan logicalPlan) (logicalPlan, error) {
@@ -736,7 +853,7 @@ func (hp *horizonPlanning) planOrderBy(ctx *plancontext.PlanningContext, orderEx
 
 		for _, order := range orderExprs {
 			if sqlparser.ContainsAggregation(order.WeightStrExpr) {
-				ms, err := createMemorySortPlanOnAggregation(plan, orderExprs)
+				ms, err := createMemorySortPlanOnAggregation(ctx, plan, orderExprs)
 				if err != nil {
 					return nil, err
 				}
@@ -756,7 +873,7 @@ func (hp *horizonPlanning) planOrderBy(ctx *plancontext.PlanningContext, orderEx
 	case *vindexFunc:
 		// This is evaluated at VTGate only, so weight_string function cannot be used.
 		return hp.createMemorySortPlan(ctx, plan, orderExprs /* useWeightStr */, false)
-	case *limit, *semiJoin, *filter, *pulloutSubquery:
+	case *limit, *semiJoin, *filter, *pulloutSubquery, *projection:
 		inputs := plan.Inputs()
 		if len(inputs) == 0 {
 			break
@@ -793,7 +910,12 @@ func planOrderByForRoute(ctx *plancontext.PlanningContext, orderExprs []abstract
 		if isSpecialOrderBy(order) {
 			continue
 		}
-		offset, weightStringOffset, err := wrapAndPushExpr(ctx, order.Inner.Expr, order.WeightStrExpr, plan)
+		var wsExpr sqlparser.Expr
+		if ctx.SemTable.NeedsWeightString(order.Inner.Expr) {
+			wsExpr = order.WeightStrExpr
+		}
+
+		offset, weightStringOffset, err := wrapAndPushExpr(ctx, order.Inner.Expr, wsExpr, plan)
 		if err != nil {
 			return nil, err
 		}
@@ -829,7 +951,7 @@ func checkOrderExprCanBePlannedInScatter(plan *routeGen4, order abstract.OrderBy
 }
 
 // wrapAndPushExpr pushes the expression and weighted_string function to the plan using semantics.SemTable
-// It returns (expr offset, weight_string offset, new_column added, error)
+// It returns (expr offset, weight_string offset, error)
 func wrapAndPushExpr(ctx *plancontext.PlanningContext, expr sqlparser.Expr, weightStrExpr sqlparser.Expr, plan logicalPlan) (int, int, error) {
 	offset, _, err := pushProjection(ctx, &sqlparser.AliasedExpr{Expr: expr}, plan, true, true, false)
 	if err != nil {
@@ -854,7 +976,8 @@ func wrapAndPushExpr(ctx *plancontext.PlanningContext, expr sqlparser.Expr, weig
 
 	weightStringOffset := -1
 	if wsNeeded {
-		weightStringOffset, _, err = pushProjection(ctx, &sqlparser.AliasedExpr{Expr: weightStringFor(weightStrExpr)}, plan, true, true, false)
+		aliasedExpr := &sqlparser.AliasedExpr{Expr: weightStringFor(weightStrExpr)}
+		weightStringOffset, _, err = pushProjection(ctx, aliasedExpr, plan, true, true, false)
 		if err != nil {
 			return 0, 0, err
 		}
@@ -912,6 +1035,8 @@ func (hp *horizonPlanning) planOrderByForJoin(ctx *plancontext.PlanningContext,
 		plan.Right = rhs
 		return plan, nil
 	}
+	// We can only push down sorting on the LHS of the join.
+	// If the order is on the RHS, we need to do the sorting on the vtgate
 	if orderExprsDependsOnTableSet(orderExprs, ctx.SemTable, plan.Left.ContainsTables()) {
 		newLeft, err := hp.planOrderBy(ctx, orderExprs, plan.Left)
 		if err != nil {
@@ -927,7 +1052,7 @@ func (hp *horizonPlanning) planOrderByForJoin(ctx *plancontext.PlanningContext,
 	return sortPlan, nil
 }
 
-func createMemorySortPlanOnAggregation(plan *orderedAggregate, orderExprs []abstract.OrderBy) (logicalPlan, error) {
+func createMemorySortPlanOnAggregation(ctx *plancontext.PlanningContext, plan *orderedAggregate, orderExprs []abstract.OrderBy) (logicalPlan, error) {
 	primitive := &engine.MemorySort{}
 	ms := &memorySort{
 		resultsBuilder: resultsBuilder{
@@ -939,15 +1064,12 @@ func createMemorySortPlanOnAggregation(plan *orderedAggregate, orderExprs []abst
 	}
 
 	for _, order := range orderExprs {
-		offset, woffset, idx, found := findExprInOrderedAggr(plan, order)
+		offset, woffset, found := findExprInOrderedAggr(plan, order)
 		if !found {
 			return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "expected to find the order by expression (%s) in orderedAggregate", sqlparser.String(order.Inner))
 		}
 
-		collationID := collations.Unknown
-		if woffset != -1 {
-			collationID = plan.groupByKeys[idx].CollationID
-		}
+		collationID := ctx.SemTable.CollationForExpr(order.WeightStrExpr)
 		ms.eMemorySort.OrderBy = append(ms.eMemorySort.OrderBy, engine.OrderByParams{
 			Col:               offset,
 			WeightStringCol:   woffset,
@@ -959,18 +1081,20 @@ func createMemorySortPlanOnAggregation(plan *orderedAggregate, orderExprs []abst
 	return ms, nil
 }
 
-func findExprInOrderedAggr(plan *orderedAggregate, order abstract.OrderBy) (keyCol int, weightStringCol int, index int, found bool) {
-	for idx, key := range plan.groupByKeys {
-		if sqlparser.EqualsExpr(order.WeightStrExpr, key.Expr) {
-			return key.KeyCol, key.WeightStringCol, idx, true
+func findExprInOrderedAggr(plan *orderedAggregate, order abstract.OrderBy) (keyCol int, weightStringCol int, found bool) {
+	for _, key := range plan.groupByKeys {
+		if sqlparser.EqualsExpr(order.WeightStrExpr, key.Expr) ||
+			sqlparser.EqualsExpr(order.Inner.Expr, key.Expr) {
+			return key.KeyCol, key.WeightStringCol, true
 		}
 	}
-	for idx, aggregate := range plan.aggregates {
-		if sqlparser.EqualsExpr(order.WeightStrExpr, aggregate.Expr) {
-			return aggregate.Col, -1, idx, true
+	for _, aggregate := range plan.aggregates {
+		if sqlparser.EqualsExpr(order.WeightStrExpr, aggregate.Expr) ||
+			sqlparser.EqualsExpr(order.Inner.Expr, aggregate.Expr) {
+			return aggregate.Col, -1, true
 		}
 	}
-	return 0, 0, 0, false
+	return 0, 0, false
 }
 
 func (hp *horizonPlanning) createMemorySortPlan(ctx *plancontext.PlanningContext, plan logicalPlan, orderExprs []abstract.OrderBy, useWeightStr bool) (logicalPlan, error) {
@@ -1227,6 +1351,10 @@ func isJoin(plan logicalPlan) bool {
 }
 
 func exprHasUniqueVindex(vschema plancontext.VSchema, semTable *semantics.SemTable, expr sqlparser.Expr) bool {
+	return exprHasVindex(vschema, semTable, expr, true)
+}
+
+func exprHasVindex(vschema plancontext.VSchema, semTable *semantics.SemTable, expr sqlparser.Expr, hasToBeUnique bool) bool {
 	col, isCol := expr.(*sqlparser.ColName)
 	if !isCol {
 		return false
@@ -1245,7 +1373,7 @@ func exprHasUniqueVindex(vschema plancontext.VSchema, semTable *semantics.SemTab
 		return false
 	}
 	for _, vindex := range vschemaTable.ColumnVindexes {
-		if len(vindex.Columns) > 1 || !vindex.IsUnique() {
+		if len(vindex.Columns) > 1 || hasToBeUnique && !vindex.IsUnique() {
 			return false
 		}
 		if col.Name.Equal(vindex.Columns[0]) {
@@ -1315,3 +1443,36 @@ func stripDownQuery(from, to sqlparser.SelectStatement) error {
 	}
 	return nil
 }
+
+func planGroupByGen4(ctx *plancontext.PlanningContext, groupExpr abstract.GroupBy, plan logicalPlan, wsAdded bool) error {
+	switch node := plan.(type) {
+	case *routeGen4:
+		sel := node.Select.(*sqlparser.Select)
+		sel.AddGroupBy(groupExpr.Inner)
+		// If a weight_string function is added to the select list,
+		// then we need to add that to the group by clause otherwise the query will fail on mysql with full_group_by error
+		// as the weight_string function might not be functionally dependent on the group by.
+		if wsAdded {
+			sel.AddGroupBy(weightStringFor(groupExpr.WeightStrExpr))
+		}
+		return nil
+	case *pulloutSubquery:
+		return planGroupByGen4(ctx, groupExpr, node.underlying, wsAdded)
+	case *semiJoin:
+		return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: group by in a query having a correlated subquery")
+	default:
+		return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: group by on: %T", plan)
+	}
+}
+
+func getLengthOfProjection(groupingOffsets []offsets, aggregations []abstract.Aggr) int {
+	length := 0
+	for _, groupBy := range groupingOffsets {
+		if groupBy.wsCol != -1 {
+			length++
+		}
+		length++
+	}
+	length += len(aggregations)
+	return length
+}
diff --git a/go/vt/vtgate/planbuilder/joinGen4.go b/go/vt/vtgate/planbuilder/joinGen4.go
index debfe6d42e..efcfacd3dd 100644
--- a/go/vt/vtgate/planbuilder/joinGen4.go
+++ b/go/vt/vtgate/planbuilder/joinGen4.go
@@ -31,9 +31,21 @@ var _ logicalPlan = (*joinGen4)(nil)
 type joinGen4 struct {
 	// Left and Right are the nodes for the join.
 	Left, Right logicalPlan
-	Opcode      engine.JoinOpcode
-	Cols        []int
-	Vars        map[string]int
+
+	// The Opcode tells us if this is an inner or outer join
+	Opcode engine.JoinOpcode
+
+	// These are the columns that will be produced by this plan.
+	// Negative offsets come from the LHS, and positive from the RHS
+	Cols []int
+
+	// Vars are the columns that will be sent from the LHS to the RHS
+	// the number is the offset on the LHS result, and the string is the bind variable name used in the RHS
+	Vars map[string]int
+
+	// LHSColumns are the columns from the LHS used for the join.
+	// These are the same columns pushed on the LHS that are now used in the Vars field
+	LHSColumns []*sqlparser.ColName
 
 	gen4Plan
 }
diff --git a/go/vt/vtgate/planbuilder/operator_to_query.go b/go/vt/vtgate/planbuilder/operator_to_query.go
index 7fcc3bc86e..864833d2c1 100644
--- a/go/vt/vtgate/planbuilder/operator_to_query.go
+++ b/go/vt/vtgate/planbuilder/operator_to_query.go
@@ -154,7 +154,9 @@ func (qb *queryBuilder) joinInnerWith(other *queryBuilder, onCondition sqlparser
 		predicate = sel.Where.Expr
 	}
 	if otherSel.Where != nil {
-		predicate = sqlparser.AndExpressions(sqlparser.SplitAndExpression(sqlparser.SplitAndExpression(nil, predicate), otherSel.Where.Expr)...)
+		predExprs := sqlparser.SplitAndExpression(nil, predicate)
+		otherExprs := sqlparser.SplitAndExpression(nil, otherSel.Where.Expr)
+		predicate = sqlparser.AndExpressions(append(predExprs, otherExprs...)...)
 	}
 	if predicate != nil {
 		sel.Where = &sqlparser.Where{Type: sqlparser.WhereClause, Expr: predicate}
diff --git a/go/vt/vtgate/planbuilder/operator_transformers.go b/go/vt/vtgate/planbuilder/operator_transformers.go
index c0bfec9580..bf0aebc5e5 100644
--- a/go/vt/vtgate/planbuilder/operator_transformers.go
+++ b/go/vt/vtgate/planbuilder/operator_transformers.go
@@ -122,11 +122,12 @@ func transformApplyJoinPlan(ctx *plancontext.PlanningContext, n *physical.ApplyJ
 	//	}, nil
 	// }
 	return &joinGen4{
-		Left:   lhs,
-		Right:  rhs,
-		Cols:   n.Columns,
-		Vars:   n.Vars,
-		Opcode: opCode,
+		Left:       lhs,
+		Right:      rhs,
+		Cols:       n.Columns,
+		Vars:       n.Vars,
+		LHSColumns: n.LHSColumns,
+		Opcode:     opCode,
 	}, nil
 }
 
diff --git a/go/vt/vtgate/planbuilder/ordered_aggregate.go b/go/vt/vtgate/planbuilder/ordered_aggregate.go
index de1fff1c97..7506ccde07 100644
--- a/go/vt/vtgate/planbuilder/ordered_aggregate.go
+++ b/go/vt/vtgate/planbuilder/ordered_aggregate.go
@@ -253,6 +253,7 @@ func (oa *orderedAggregate) Primitive() engine.Primitive {
 		Aggregates:          oa.aggregates,
 		GroupByKeys:         oa.groupByKeys,
 		TruncateColumnCount: oa.truncateColumnCount,
+		Collations:          colls,
 		Input:               input,
 	}
 }
diff --git a/go/vt/vtgate/planbuilder/physical/apply_join.go b/go/vt/vtgate/planbuilder/physical/apply_join.go
index 597216465f..d8a66de82f 100644
--- a/go/vt/vtgate/planbuilder/physical/apply_join.go
+++ b/go/vt/vtgate/planbuilder/physical/apply_join.go
@@ -37,6 +37,10 @@ type ApplyJoin struct {
 	// LeftJoin will be true in the case of an outer join
 	LeftJoin bool
 
+	// JoinCols are the columns from the LHS used for the join.
+	// These are the same columns pushed on the LHS that are now used in the Vars field
+	LHSColumns []*sqlparser.ColName
+
 	Predicate sqlparser.Expr
 }
 
@@ -82,12 +86,15 @@ func (a *ApplyJoin) Clone() abstract.PhysicalOperator {
 	}
 	columnsClone := make([]int, len(a.Columns))
 	copy(columnsClone, a.Columns)
+	lhsColumns := make([]*sqlparser.ColName, len(a.LHSColumns))
+	copy(lhsColumns, a.LHSColumns)
 	return &ApplyJoin{
-		LHS:       a.LHS.Clone(),
-		RHS:       a.RHS.Clone(),
-		Columns:   columnsClone,
-		Vars:      varsClone,
-		LeftJoin:  a.LeftJoin,
-		Predicate: sqlparser.CloneExpr(a.Predicate),
+		LHS:        a.LHS.Clone(),
+		RHS:        a.RHS.Clone(),
+		Columns:    columnsClone,
+		Vars:       varsClone,
+		LeftJoin:   a.LeftJoin,
+		Predicate:  sqlparser.CloneExpr(a.Predicate),
+		LHSColumns: lhsColumns,
 	}
 }
diff --git a/go/vt/vtgate/planbuilder/physical/correlated_subquery.go b/go/vt/vtgate/planbuilder/physical/correlated_subquery.go
index 323de1da38..03d27efc5a 100644
--- a/go/vt/vtgate/planbuilder/physical/correlated_subquery.go
+++ b/go/vt/vtgate/planbuilder/physical/correlated_subquery.go
@@ -26,6 +26,11 @@ type (
 	CorrelatedSubQueryOp struct {
 		Outer, Inner abstract.PhysicalOperator
 		Extracted    *sqlparser.ExtractedSubquery
+
+		// JoinCols are the columns from the LHS used for the join.
+		// These are the same columns pushed on the LHS that are now used in the Vars field
+		LHSColumns []*sqlparser.ColName
+
 		// arguments that need to be copied from the outer to inner
 		Vars map[string]int
 	}
@@ -106,10 +111,13 @@ func (c *CorrelatedSubQueryOp) Cost() int {
 }
 
 func (c *CorrelatedSubQueryOp) Clone() abstract.PhysicalOperator {
+	columns := make([]*sqlparser.ColName, len(c.LHSColumns))
+	copy(columns, c.LHSColumns)
 	result := &CorrelatedSubQueryOp{
-		Outer:     c.Outer.Clone(),
-		Inner:     c.Inner.Clone(),
-		Extracted: c.Extracted,
+		Outer:      c.Outer.Clone(),
+		Inner:      c.Inner.Clone(),
+		Extracted:  c.Extracted,
+		LHSColumns: columns,
 	}
 	return result
 }
diff --git a/go/vt/vtgate/planbuilder/physical/route_planning.go b/go/vt/vtgate/planbuilder/physical/route_planning.go
index 2098091b10..58c02c94d3 100644
--- a/go/vt/vtgate/planbuilder/physical/route_planning.go
+++ b/go/vt/vtgate/planbuilder/physical/route_planning.go
@@ -876,7 +876,6 @@ func pushJoinPredicateOnJoin(ctx *plancontext.PlanningContext, exprs []sqlparser
 	node = node.Clone().(*ApplyJoin)
 	var rhsPreds []sqlparser.Expr
 	var lhsPreds []sqlparser.Expr
-	var lhsColumns []*sqlparser.ColName
 	var lhsVarsName []string
 	for _, expr := range exprs {
 		// We find the dependencies for the given expression and if they are solved entirely by one
@@ -902,12 +901,12 @@ func pushJoinPredicateOnJoin(ctx *plancontext.PlanningContext, exprs []sqlparser
 		if err != nil {
 			return nil, err
 		}
-		lhsColumns = append(lhsColumns, cols...)
+		node.LHSColumns = append(node.LHSColumns, cols...)
 		lhsVarsName = append(lhsVarsName, bvName...)
 		rhsPreds = append(rhsPreds, predicate)
 	}
-	if lhsColumns != nil && lhsVarsName != nil {
-		newNode, offsets, err := PushOutputColumns(ctx, node.LHS, lhsColumns...)
+	if node.LHSColumns != nil && lhsVarsName != nil {
+		newNode, offsets, err := PushOutputColumns(ctx, node.LHS, node.LHSColumns...)
 		if err != nil {
 			return nil, err
 		}
diff --git a/go/vt/vtgate/planbuilder/physical/subquery_planning.go b/go/vt/vtgate/planbuilder/physical/subquery_planning.go
index a1e1ead150..2f31f40013 100644
--- a/go/vt/vtgate/planbuilder/physical/subquery_planning.go
+++ b/go/vt/vtgate/planbuilder/physical/subquery_planning.go
@@ -241,6 +241,7 @@ func createCorrelatedSubqueryOp(
 	resultOuterOp := newOuter
 	vars := map[string]int{}
 	bindVars := map[*sqlparser.ColName]string{}
+	var lhsCols []*sqlparser.ColName
 	for _, pred := range preds {
 		var rewriteError error
 		sqlparser.Rewrite(pred, func(cursor *sqlparser.Cursor) bool {
@@ -269,6 +270,7 @@ func createCorrelatedSubqueryOp(
 						rewriteError = err
 						return false
 					}
+					lhsCols = append(lhsCols, node)
 					columnIndex := columnIndexes[0]
 					vars[bindVar] = columnIndex
 					resultOuterOp = newOuterOp
@@ -287,9 +289,10 @@ func createCorrelatedSubqueryOp(
 		}
 	}
 	return &CorrelatedSubQueryOp{
-		Outer:     resultOuterOp,
-		Inner:     innerOp,
-		Extracted: extractedSubquery,
-		Vars:      vars,
+		Outer:      resultOuterOp,
+		Inner:      innerOp,
+		Extracted:  extractedSubquery,
+		Vars:       vars,
+		LHSColumns: lhsCols,
 	}, nil
 }
diff --git a/go/vt/vtgate/planbuilder/projection.go b/go/vt/vtgate/planbuilder/projection.go
new file mode 100644
index 0000000000..1a8cf07768
--- /dev/null
+++ b/go/vt/vtgate/planbuilder/projection.go
@@ -0,0 +1,115 @@
+/*
+Copyright 2022 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package planbuilder
+
+import (
+	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
+	"vitess.io/vitess/go/vt/sqlparser"
+	"vitess.io/vitess/go/vt/vterrors"
+	"vitess.io/vitess/go/vt/vtgate/engine"
+	"vitess.io/vitess/go/vt/vtgate/evalengine"
+	"vitess.io/vitess/go/vt/vtgate/semantics"
+)
+
+type projection struct {
+	gen4Plan
+	source      logicalPlan
+	columnNames []string
+	columns     []sqlparser.Expr
+	primitive   *engine.Projection
+	// unorderedColumnIdx is used to find the index at which we should add any column output from projection
+	// we don't care for the ordering of. It should also be updated when such a column is added
+	unorderedColumnIdx int
+}
+
+var _ logicalPlan = (*projection)(nil)
+
+// WireupGen4 implements the logicalPlan interface
+func (p *projection) WireupGen4(semTable *semantics.SemTable) error {
+	columns := make([]evalengine.Expr, 0, len(p.columns))
+	for _, expr := range p.columns {
+		convert, err := evalengine.Translate(expr, semTable)
+		if err != nil {
+			return err
+		}
+		columns = append(columns, convert)
+	}
+	p.primitive = &engine.Projection{
+		Cols:  p.columnNames,
+		Exprs: columns,
+	}
+
+	return p.source.WireupGen4(semTable)
+}
+
+// Inputs implements the logicalPlan interface
+func (p *projection) Inputs() []logicalPlan {
+	return []logicalPlan{p.source}
+}
+
+// Rewrite implements the logicalPlan interface
+func (p *projection) Rewrite(inputs ...logicalPlan) error {
+	if len(inputs) != 1 {
+		return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "wrong number of inputs")
+	}
+	p.source = inputs[0]
+	return nil
+}
+
+// ContainsTables implements the logicalPlan interface
+func (p *projection) ContainsTables() semantics.TableSet {
+	return p.source.ContainsTables()
+}
+
+// OutputColumns implements the logicalPlan interface
+func (p *projection) OutputColumns() []sqlparser.SelectExpr {
+	columns := make([]sqlparser.SelectExpr, 0, len(p.columns))
+	for i, expr := range p.columns {
+		columns = append(columns, &sqlparser.AliasedExpr{
+			Expr: expr,
+			As:   sqlparser.NewColIdent(p.columnNames[i]),
+		})
+	}
+	return columns
+}
+
+// Primitive implements the logicalPlan interface
+func (p *projection) Primitive() engine.Primitive {
+	if p.primitive == nil {
+		panic("WireUp not yet run")
+	}
+	p.primitive.Input = p.source.Primitive()
+	return p.primitive
+}
+
+// addColumn is used to add a column output for the projection.
+// This is the only function that should be used to add  columns to projection
+func (p *projection) addColumn(idx *int, column sqlparser.Expr, columnName string) (int, error) {
+	var offset int
+	if idx == nil {
+		p.unorderedColumnIdx++
+		offset = len(p.columns) - p.unorderedColumnIdx
+	} else {
+		offset = *idx
+	}
+	if p.columnNames[offset] != "" || p.columns[offset] != nil {
+		return -1, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "overwriting columns in projection is not permitted")
+	}
+	p.columns[offset] = column
+	p.columnNames[offset] = columnName
+	return offset, nil
+}
diff --git a/go/vt/vtgate/planbuilder/route.go b/go/vt/vtgate/planbuilder/route.go
index bad4a06b8a..2d74934b06 100644
--- a/go/vt/vtgate/planbuilder/route.go
+++ b/go/vt/vtgate/planbuilder/route.go
@@ -342,7 +342,7 @@ func (rb *route) SupplyWeightString(colNumber int, alsoAddToGroupBy bool) (weigh
 		if !isSelect {
 			return 0, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "cannot add weight string in %T", rb.Select)
 		}
-		sel.GroupBy = append(sel.GroupBy, weightStringExpr)
+		sel.AddGroupBy(weightStringExpr)
 	}
 
 	if weightcolNumber, ok := rb.weightStrings[rc]; ok {
diff --git a/go/vt/vtgate/planbuilder/semi_join.go b/go/vt/vtgate/planbuilder/semi_join.go
index 9a7968f2c1..1b2d6c1807 100644
--- a/go/vt/vtgate/planbuilder/semi_join.go
+++ b/go/vt/vtgate/planbuilder/semi_join.go
@@ -33,16 +33,22 @@ type semiJoin struct {
 	gen4Plan
 	rhs  logicalPlan
 	lhs  logicalPlan
-	vars map[string]int
 	cols []int
+
+	vars map[string]int
+
+	// LHSColumns are the columns from the LHS used for the join.
+	// These are the same columns pushed on the LHS that are now used in the vars field
+	LHSColumns []*sqlparser.ColName
 }
 
 // newSemiJoin builds a new semiJoin.
-func newSemiJoin(lhs, rhs logicalPlan, vars map[string]int) *semiJoin {
+func newSemiJoin(lhs, rhs logicalPlan, vars map[string]int, lhsCols []*sqlparser.ColName) *semiJoin {
 	return &semiJoin{
-		rhs:  rhs,
-		lhs:  lhs,
-		vars: vars,
+		rhs:        rhs,
+		lhs:        lhs,
+		vars:       vars,
+		LHSColumns: lhsCols,
 	}
 }
 
diff --git a/go/vt/vtgate/planbuilder/subquery_op.go b/go/vt/vtgate/planbuilder/subquery_op.go
index 10dc246afe..a1abc30942 100644
--- a/go/vt/vtgate/planbuilder/subquery_op.go
+++ b/go/vt/vtgate/planbuilder/subquery_op.go
@@ -59,7 +59,7 @@ func transformCorrelatedSubQueryPlan(ctx *plancontext.PlanningContext, op *physi
 	if err != nil {
 		return nil, err
 	}
-	return newSemiJoin(outer, inner, op.Vars), nil
+	return newSemiJoin(outer, inner, op.Vars, op.LHSColumns), nil
 }
 
 func mergeSubQueryOpPlan(ctx *plancontext.PlanningContext, inner, outer logicalPlan, n *physical.SubQueryOp) logicalPlan {
diff --git a/go/vt/vtgate/semantics/semantic_state.go b/go/vt/vtgate/semantics/semantic_state.go
index 6fb5f839db..4b33744a4c 100644
--- a/go/vt/vtgate/semantics/semantic_state.go
+++ b/go/vt/vtgate/semantics/semantic_state.go
@@ -18,6 +18,7 @@ package semantics
 
 import (
 	"vitess.io/vitess/go/mysql/collations"
+	"vitess.io/vitess/go/sqltypes"
 	"vitess.io/vitess/go/vt/key"
 	querypb "vitess.io/vitess/go/vt/proto/query"
 	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
@@ -225,6 +226,15 @@ func (st *SemTable) CollationForExpr(e sqlparser.Expr) collations.ID {
 	return collations.Unknown
 }
 
+// NeedsWeightString returns true if the given expression needs weight_string to do safe comparisons
+func (st *SemTable) NeedsWeightString(e sqlparser.Expr) bool {
+	typ, found := st.ExprTypes[e]
+	if !found {
+		return true
+	}
+	return typ.Collation == collations.Unknown && !sqltypes.IsNumber(typ.Type)
+}
+
 func (st *SemTable) DefaultCollation() collations.ID {
 	return st.Collation
 }
diff --git a/go/vt/vtgate/simplifier/simplifier.go b/go/vt/vtgate/simplifier/simplifier.go
index 5fa5e3cb37..5da8fe945f 100644
--- a/go/vt/vtgate/simplifier/simplifier.go
+++ b/go/vt/vtgate/simplifier/simplifier.go
@@ -51,8 +51,8 @@ func SimplifyStatement(
 	}
 
 	// now let's try to simplify * expressions
-	if simplifyStarExpr(sqlparser.CloneSelectStatement(in), test) {
-		return SimplifyStatement(in, currentDB, si, testF)
+	if success := simplifyStarExpr(sqlparser.CloneSelectStatement(in), test); success != nil {
+		return SimplifyStatement(success, currentDB, si, testF)
 	}
 
 	// we try to remove select expressions next
@@ -176,7 +176,7 @@ func getTables(in sqlparser.SelectStatement, currentDB string, si semantics.Sche
 	return semTable.Tables, nil
 }
 
-func simplifyStarExpr(in sqlparser.SelectStatement, test func(sqlparser.SelectStatement) bool) bool {
+func simplifyStarExpr(in sqlparser.SelectStatement, test func(sqlparser.SelectStatement) bool) sqlparser.SelectStatement {
 	simplified := false
 	sqlparser.Rewrite(in, func(cursor *sqlparser.Cursor) bool {
 		se, ok := cursor.Node().(*sqlparser.StarExpr)
@@ -195,7 +195,10 @@ func simplifyStarExpr(in sqlparser.SelectStatement, test func(sqlparser.SelectSt
 
 		return true
 	}, nil)
-	return simplified
+	if simplified {
+		return in
+	}
+	return nil
 }
 
 // removeTable removes the table with the given index from the select statement, which includes the FROM clause
diff --git a/go/vt/vttablet/tabletserver/rules/cached_size.go b/go/vt/vttablet/tabletserver/rules/cached_size.go
index 275db82276..acfd199f1f 100644
--- a/go/vt/vttablet/tabletserver/rules/cached_size.go
+++ b/go/vt/vttablet/tabletserver/rules/cached_size.go
@@ -45,7 +45,7 @@ func (cached *Rule) CachedSize(alloc bool) int64 {
 	}
 	size := int64(0)
 	if alloc {
-		size += int64(240)
+		size += int64(256)
 	}
 	// field Description string
 	size += hack.RuntimeAllocSize(int64(len(cached.Description)))
diff --git a/go/vt/worker/restartable_result_reader.go b/go/vt/worker/restartable_result_reader.go
index 620e54bda5..ccb059e55c 100644
--- a/go/vt/worker/restartable_result_reader.go
+++ b/go/vt/worker/restartable_result_reader.go
@@ -17,6 +17,7 @@ limitations under the License.
 package worker
 
 import (
+	"bytes"
 	"io"
 	"strings"
 	"time"
@@ -332,7 +333,7 @@ func (r *RestartableResultReader) generateQuery() {
 	if r.lastRow == nil {
 		// Initial query.
 		if !r.chunk.start.IsNull() {
-			var b strings.Builder
+			var b bytes.Buffer
 			sqlescape.WriteEscapeID(&b, r.td.PrimaryKeyColumns[0])
 			b.WriteString(">=")
 			r.chunk.start.EncodeSQL(&b)
@@ -350,7 +351,7 @@ func (r *RestartableResultReader) generateQuery() {
 
 	// end value.
 	if !r.chunk.end.IsNull() {
-		var b strings.Builder
+		var b bytes.Buffer
 		sqlescape.WriteEscapeID(&b, r.td.PrimaryKeyColumns[0])
 		b.WriteString("<")
 		r.chunk.end.EncodeSQL(&b)
@@ -391,14 +392,14 @@ func greaterThanTupleWhereClause(columns []string, row []sqltypes.Value) []strin
 
 	// Additional clause on the first column for multi-columns.
 	if len(columns) > 1 {
-		var b strings.Builder
+		var b bytes.Buffer
 		sqlescape.WriteEscapeID(&b, columns[0])
 		b.WriteString(">=")
 		row[0].EncodeSQL(&b)
 		clauses = append(clauses, b.String())
 	}
 
-	var b strings.Builder
+	var b bytes.Buffer
 	// List of columns.
 	if len(columns) > 1 {
 		b.WriteByte('(')
diff --git a/go/vt/worker/row_aggregator.go b/go/vt/worker/row_aggregator.go
index 854465acbe..a5c57de675 100644
--- a/go/vt/worker/row_aggregator.go
+++ b/go/vt/worker/row_aggregator.go
@@ -17,6 +17,7 @@ limitations under the License.
 package worker
 
 import (
+	"bytes"
 	"fmt"
 	"strings"
 
@@ -51,7 +52,7 @@ type RowAggregator struct {
 	builder       QueryBuilder
 	statsCounters *stats.CountersWithSingleLabel
 
-	buffer       strings.Builder
+	buffer       bytes.Buffer
 	bufferedRows int
 }
 
@@ -140,13 +141,13 @@ func (ra *RowAggregator) Flush() error {
 // build the SQL query for one or more rows.
 type QueryBuilder interface {
 	// WriteHead writes the beginning of the query into the buffer.
-	WriteHead(*strings.Builder)
+	WriteHead(*bytes.Buffer)
 	// WriteTail writes any required tailing string into the buffer.
-	WriteTail(*strings.Builder)
+	WriteTail(*bytes.Buffer)
 	// Write the separator between two rows.
-	WriteSeparator(*strings.Builder)
+	WriteSeparator(*bytes.Buffer)
 	// Write the row itself.
-	WriteRow(*strings.Builder, []sqltypes.Value)
+	WriteRow(*bytes.Buffer, []sqltypes.Value)
 }
 
 // BaseQueryBuilder partially implements the QueryBuilder interface.
@@ -159,17 +160,17 @@ type BaseQueryBuilder struct {
 }
 
 // WriteHead implements the QueryBuilder interface.
-func (b *BaseQueryBuilder) WriteHead(buffer *strings.Builder) {
+func (b *BaseQueryBuilder) WriteHead(buffer *bytes.Buffer) {
 	buffer.WriteString(b.head)
 }
 
 // WriteTail implements the QueryBuilder interface.
-func (b *BaseQueryBuilder) WriteTail(buffer *strings.Builder) {
+func (b *BaseQueryBuilder) WriteTail(buffer *bytes.Buffer) {
 	buffer.WriteString(b.tail)
 }
 
 // WriteSeparator implements the QueryBuilder interface.
-func (b *BaseQueryBuilder) WriteSeparator(buffer *strings.Builder) {
+func (b *BaseQueryBuilder) WriteSeparator(buffer *bytes.Buffer) {
 	if b.separator == "" {
 		panic("BaseQueryBuilder.WriteSeparator(): separator not defined")
 	}
@@ -193,7 +194,7 @@ func NewInsertsQueryBuilder(dbName string, td *tabletmanagerdatapb.TableDefiniti
 }
 
 // WriteRow implements the QueryBuilder interface.
-func (*InsertsQueryBuilder) WriteRow(buffer *strings.Builder, row []sqltypes.Value) {
+func (*InsertsQueryBuilder) WriteRow(buffer *bytes.Buffer, row []sqltypes.Value) {
 	// Example: (0, 10, 'a'), (1, 11, 'b')
 	buffer.WriteByte('(')
 	for i, value := range row {
@@ -236,12 +237,12 @@ func NewUpdatesQueryBuilder(dbName string, td *tabletmanagerdatapb.TableDefiniti
 
 // WriteSeparator implements the QueryBuilder interface and overrides
 // the BaseQueryBuilder implementation.
-func (b *UpdatesQueryBuilder) WriteSeparator(buffer *strings.Builder) {
+func (b *UpdatesQueryBuilder) WriteSeparator(buffer *bytes.Buffer) {
 	panic("UpdatesQueryBuilder does not support aggregating multiple rows in one query")
 }
 
 // WriteRow implements the QueryBuilder interface.
-func (b *UpdatesQueryBuilder) WriteRow(buffer *strings.Builder, row []sqltypes.Value) {
+func (b *UpdatesQueryBuilder) WriteRow(buffer *bytes.Buffer, row []sqltypes.Value) {
 	// Example: msg='a' WHERE id=0 AND sub_id=10
 	nonPrimaryOffset := len(b.td.PrimaryKeyColumns)
 	for i, column := range b.nonPrimaryKeyColumns {
@@ -286,7 +287,7 @@ func NewDeletesQueryBuilder(dbName string, td *tabletmanagerdatapb.TableDefiniti
 }
 
 // WriteRow implements the QueryBuilder interface.
-func (b *DeletesQueryBuilder) WriteRow(buffer *strings.Builder, row []sqltypes.Value) {
+func (b *DeletesQueryBuilder) WriteRow(buffer *bytes.Buffer, row []sqltypes.Value) {
 	// Example: (id=0 AND sub_id=10) OR (id=1 AND sub_id=11)
 	buffer.WriteByte('(')
 	for i, pkColumn := range b.td.PrimaryKeyColumns {


Test patch:
diff --git a/go/sqlescape/ids_test.go b/go/sqlescape/ids_test.go
index a2d2e69be6..cc62b5531d 100644
--- a/go/sqlescape/ids_test.go
+++ b/go/sqlescape/ids_test.go
@@ -34,22 +34,3 @@ func TestEscapeID(t *testing.T) {
 		}
 	}
 }
-
-var scratch string
-
-func BenchmarkEscapeID(b *testing.B) {
-	testcases := []string{
-		"aa", "a`a", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
-	}
-	for _, tc := range testcases {
-		name := tc
-		if len(name) > 10 {
-			name = "long"
-		}
-		b.Run(name, func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				scratch = EscapeID(tc)
-			}
-		})
-	}
-}
diff --git a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go
index db03db821f..26e861e96b 100644
--- a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go
+++ b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go
@@ -29,12 +29,32 @@ import (
 	"vitess.io/vitess/go/test/endtoend/cluster"
 )
 
-func TestAggregateTypes(t *testing.T) {
-	defer cluster.PanicHandler(t)
+func start(t *testing.T) (*mysql.Conn, func()) {
 	ctx := context.Background()
 	conn, err := mysql.Connect(ctx, &vtParams)
 	require.Nil(t, err)
-	defer conn.Close()
+
+	deleteAll := func() {
+		_, _ = utils.ExecAllowError(t, conn, "set workload = oltp")
+		utils.Exec(t, conn, "delete from aggr_test")
+		utils.Exec(t, conn, "delete from t3")
+		utils.Exec(t, conn, "delete from t7_xxhash")
+		utils.Exec(t, conn, "delete from aggr_test_dates")
+		utils.Exec(t, conn, "delete from t7_xxhash_idx")
+	}
+
+	deleteAll()
+
+	return conn, func() {
+		deleteAll()
+		conn.Close()
+		cluster.PanicHandler(t)
+	}
+}
+
+func TestAggregateTypes(t *testing.T) {
+	conn, closer := start(t)
+	defer closer()
 	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'A',1), (3,'b',1), (4,'c',3), (5,'c',4)")
 	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(6,'d',null), (7,'e',null), (8,'E',1)")
 	utils.AssertMatches(t, conn, "select val1, count(distinct val2), count(*) from aggr_test group by val1", `[[VARCHAR("a") INT64(1) INT64(2)] [VARCHAR("b") INT64(1) INT64(1)] [VARCHAR("c") INT64(2) INT64(2)] [VARCHAR("d") INT64(0) INT64(1)] [VARCHAR("e") INT64(1) INT64(2)]]`)
@@ -47,35 +67,23 @@ func TestAggregateTypes(t *testing.T) {
 	utils.AssertMatches(t, conn, "select val1 as a, count(*) from aggr_test group by a", `[[VARCHAR("a") INT64(2)] [VARCHAR("b") INT64(1)] [VARCHAR("c") INT64(2)] [VARCHAR("d") INT64(1)] [VARCHAR("e") INT64(2)]]`)
 	utils.AssertMatches(t, conn, "select val1 as a, count(*) from aggr_test group by a order by a", `[[VARCHAR("a") INT64(2)] [VARCHAR("b") INT64(1)] [VARCHAR("c") INT64(2)] [VARCHAR("d") INT64(1)] [VARCHAR("e") INT64(2)]]`)
 	utils.AssertMatches(t, conn, "select val1 as a, count(*) from aggr_test group by a order by 2, a", `[[VARCHAR("b") INT64(1)] [VARCHAR("d") INT64(1)] [VARCHAR("a") INT64(2)] [VARCHAR("c") INT64(2)] [VARCHAR("e") INT64(2)]]`)
-	utils.Exec(t, conn, "delete from aggr_test")
 }
 
 func TestGroupBy(t *testing.T) {
-	defer cluster.PanicHandler(t)
-	ctx := context.Background()
-	conn, err := mysql.Connect(ctx, &vtParams)
-	require.Nil(t, err)
-	defer conn.Close()
+	conn, closer := start(t)
+	defer closer()
 	utils.Exec(t, conn, "insert into t3(id5, id6, id7) values(1,1,2), (2,2,4), (3,2,4), (4,1,2), (5,1,2), (6,3,6)")
 	// test ordering and group by int column
 	utils.AssertMatches(t, conn, "select id6, id7, count(*) k from t3 group by id6, id7 order by k", `[[INT64(3) INT64(6) INT64(1)] [INT64(2) INT64(4) INT64(2)] [INT64(1) INT64(2) INT64(3)]]`)
 
-	defer func() {
-		utils.Exec(t, conn, "set workload = oltp")
-		utils.Exec(t, conn, "delete from t3")
-	}()
 	// Test the same queries in streaming mode
 	utils.Exec(t, conn, "set workload = olap")
 	utils.AssertMatches(t, conn, "select id6, id7, count(*) k from t3 group by id6, id7 order by k", `[[INT64(3) INT64(6) INT64(1)] [INT64(2) INT64(4) INT64(2)] [INT64(1) INT64(2) INT64(3)]]`)
 }
 
 func TestDistinct(t *testing.T) {
-	defer cluster.PanicHandler(t)
-
-	ctx := context.Background()
-	conn, err := mysql.Connect(ctx, &vtParams)
-	require.Nil(t, err)
-	defer conn.Close()
+	conn, closer := start(t)
+	defer closer()
 	utils.Exec(t, conn, "insert into t3(id5,id6,id7) values(1,3,3), (2,3,4), (3,3,6), (4,5,7), (5,5,6)")
 	utils.Exec(t, conn, "insert into t7_xxhash(uid,phone) values('1',4), ('2',4), ('3',3), ('4',1), ('5',1)")
 	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'A',1), (3,'b',1), (4,'c',3), (5,'c',4)")
@@ -88,19 +96,11 @@ func TestDistinct(t *testing.T) {
 }
 
 func TestEqualFilterOnScatter(t *testing.T) {
-	defer cluster.PanicHandler(t)
-	ctx := context.Background()
-	conn, err := mysql.Connect(ctx, &vtParams)
-	require.Nil(t, err)
-	defer conn.Close()
+	conn, closer := start(t)
+	defer closer()
 
 	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'b',2), (3,'c',3), (4,'d',4), (5,'e',5)")
 
-	defer func() {
-		utils.Exec(t, conn, "set workload = 'oltp'")
-		utils.Exec(t, conn, "delete from aggr_test")
-	}()
-
 	workloads := []string{"oltp", "olap"}
 	for _, workload := range workloads {
 		t.Run(workload, func(t *testing.T) {
@@ -123,20 +123,64 @@ func TestEqualFilterOnScatter(t *testing.T) {
 	}
 }
 
+func TestAggrOnJoin(t *testing.T) {
+	conn, closer := start(t)
+	defer closer()
+
+	utils.Exec(t, conn, "insert into t3(id5, id6, id7) values(1,1,1), (2,2,4), (3,2,4), (4,1,2), (5,1,1), (6,3,6)")
+	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'a',1), (3,'b',1), (4,'c',3), (5,'c',4)")
+
+	utils.AssertMatches(t, conn, "select /*vt+ PLANNER=gen4 */ count(*) from aggr_test a join t3 t on a.val2 = t.id7",
+		"[[INT64(8)]]")
+	/*
+		mysql> select count(*) from aggr_test a join t3 t on a.val2 = t.id7;
+		+----------+
+		| count(*) |
+		+----------+
+		|        8 |
+		+----------+
+		1 row in set (0.00 sec)
+	*/
+	utils.AssertMatches(
+		t,
+		conn,
+		"select /*vt+ PLANNER=gen4 */ a.val1, count(*) from aggr_test a join t3 t on a.val2 = t.id7 group by a.val1",
+		`[[VARCHAR("a") INT64(4)] [VARCHAR("b") INT64(2)] [VARCHAR("c") INT64(2)]]`,
+	)
+	/*
+		mysql> select a.val1, count(*) from aggr_test a join t3 t on a.val2 = t.id7 group by a.val1;
+		+------+----------+
+		| val1 | count(*) |
+		+------+----------+
+		| a    |        4 |
+		| b    |        2 |
+		| c    |        2 |
+		+------+----------+
+		3 rows in set (0.00 sec)
+	*/
+
+	utils.AssertMatches(t, conn, `select /*vt+ PLANNER=gen4 */ max(a1.val2), max(a2.val2), count(*) from aggr_test a1 join aggr_test a2 on a1.val2 = a2.id join t3 t on a2.val2 = t.id7`,
+		"[[INT64(3) INT64(1) INT64(8)]]")
+	/*
+		mysql> select max(a1.val2), max(a2.val2), count(*) from aggr_test a1 join aggr_test a2 on a1.val2 = a2.id join t3 t on a2.val2 = t.id7;
+		+--------------+--------------+----------+
+		| max(a1.val2) | max(a2.val2) | count(*) |
+		+--------------+--------------+----------+
+		|            3 |            1 |        8 |
+		+--------------+--------------+----------+
+		1 row in set (0.00 sec)
+	*/
+
+	utils.AssertMatches(t, conn, `select /*vt+ PLANNER=gen4 */ a1.val1, count(distinct a1.val2) from aggr_test a1 join aggr_test a2 on a1.val2 = a2.id join t3 t on a2.val2 = t.id7 group by a1.val1`,
+		`[[VARCHAR("a") INT64(1)] [VARCHAR("b") INT64(1)] [VARCHAR("c") INT64(1)]]`)
+}
+
 func TestNotEqualFilterOnScatter(t *testing.T) {
-	defer cluster.PanicHandler(t)
-	ctx := context.Background()
-	conn, err := mysql.Connect(ctx, &vtParams)
-	require.Nil(t, err)
-	defer conn.Close()
+	conn, closer := start(t)
+	defer closer()
 
 	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'b',2), (3,'c',3), (4,'d',4), (5,'e',5)")
 
-	defer func() {
-		utils.Exec(t, conn, "set workload = oltp")
-		utils.Exec(t, conn, "delete from aggr_test")
-	}()
-
 	workloads := []string{"oltp", "olap"}
 	for _, workload := range workloads {
 		t.Run(workload, func(t *testing.T) {
@@ -157,19 +201,11 @@ func TestNotEqualFilterOnScatter(t *testing.T) {
 }
 
 func TestLessFilterOnScatter(t *testing.T) {
-	defer cluster.PanicHandler(t)
-	ctx := context.Background()
-	conn, err := mysql.Connect(ctx, &vtParams)
-	require.Nil(t, err)
-	defer conn.Close()
+	conn, closer := start(t)
+	defer closer()
 
 	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'b',2), (3,'c',3), (4,'d',4), (5,'e',5)")
 
-	defer func() {
-		utils.Exec(t, conn, "set workload = oltp")
-		utils.Exec(t, conn, "delete from aggr_test")
-	}()
-
 	workloads := []string{"oltp", "olap"}
 	for _, workload := range workloads {
 		t.Run(workload, func(t *testing.T) {
@@ -189,19 +225,11 @@ func TestLessFilterOnScatter(t *testing.T) {
 }
 
 func TestLessEqualFilterOnScatter(t *testing.T) {
-	defer cluster.PanicHandler(t)
-	ctx := context.Background()
-	conn, err := mysql.Connect(ctx, &vtParams)
-	require.Nil(t, err)
-	defer conn.Close()
+	conn, closer := start(t)
+	defer closer()
 
 	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'b',2), (3,'c',3), (4,'d',4), (5,'e',5)")
 
-	defer func() {
-		utils.Exec(t, conn, "set workload = oltp")
-		utils.Exec(t, conn, "delete from aggr_test")
-	}()
-
 	workloads := []string{"oltp", "olap"}
 	for _, workload := range workloads {
 		t.Run(workload, func(t *testing.T) {
@@ -222,19 +250,11 @@ func TestLessEqualFilterOnScatter(t *testing.T) {
 }
 
 func TestGreaterFilterOnScatter(t *testing.T) {
-	defer cluster.PanicHandler(t)
-	ctx := context.Background()
-	conn, err := mysql.Connect(ctx, &vtParams)
-	require.Nil(t, err)
-	defer conn.Close()
+	conn, closer := start(t)
+	defer closer()
 
 	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'b',2), (3,'c',3), (4,'d',4), (5,'e',5)")
 
-	defer func() {
-		utils.Exec(t, conn, "set workload = oltp")
-		utils.Exec(t, conn, "delete from aggr_test")
-	}()
-
 	workloads := []string{"oltp", "olap"}
 	for _, workload := range workloads {
 		t.Run(workload, func(t *testing.T) {
@@ -255,19 +275,11 @@ func TestGreaterFilterOnScatter(t *testing.T) {
 }
 
 func TestGreaterEqualFilterOnScatter(t *testing.T) {
-	defer cluster.PanicHandler(t)
-	ctx := context.Background()
-	conn, err := mysql.Connect(ctx, &vtParams)
-	require.Nil(t, err)
-	defer conn.Close()
+	conn, closer := start(t)
+	defer closer()
 
 	utils.Exec(t, conn, "insert into aggr_test(id, val1, val2) values(1,'a',1), (2,'b',2), (3,'c',3), (4,'d',4), (5,'e',5)")
 
-	defer func() {
-		utils.Exec(t, conn, "set workload = oltp")
-		utils.Exec(t, conn, "delete from aggr_test")
-	}()
-
 	workloads := []string{"oltp", "olap"}
 	for _, workload := range workloads {
 		t.Run(workload, func(t *testing.T) {
diff --git a/go/vt/sqlparser/ast_test.go b/go/vt/sqlparser/ast_test.go
index de39986caa..7551941b40 100644
--- a/go/vt/sqlparser/ast_test.go
+++ b/go/vt/sqlparser/ast_test.go
@@ -64,7 +64,7 @@ func TestSelect(t *testing.T) {
 	sel.AddWhere(expr)
 	buf = NewTrackedBuffer(nil)
 	sel.Where.Format(buf)
-	want = " where a = 1 and a = 1"
+	want = " where a = 1"
 	if buf.String() != want {
 		t.Errorf("where: %q, want %s", buf.String(), want)
 	}
diff --git a/go/vt/vtgate/engine/projection_test.go b/go/vt/vtgate/engine/projection_test.go
new file mode 100644
index 0000000000..09bc9ff3e4
--- /dev/null
+++ b/go/vt/vtgate/engine/projection_test.go
@@ -0,0 +1,57 @@
+/*
+Copyright 2022 The Vitess Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package engine
+
+import (
+	"fmt"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+
+	"vitess.io/vitess/go/sqltypes"
+	querypb "vitess.io/vitess/go/vt/proto/query"
+	"vitess.io/vitess/go/vt/sqlparser"
+	"vitess.io/vitess/go/vt/vtgate/evalengine"
+)
+
+func TestMultiply(t *testing.T) {
+	expr := &sqlparser.BinaryExpr{
+		Operator: sqlparser.MultOp,
+		Left:     sqlparser.Offset(0),
+		Right:    sqlparser.Offset(1),
+	}
+	evalExpr, err := evalengine.Translate(expr, nil)
+	require.NoError(t, err)
+	fp := &fakePrimitive{
+		results: []*sqltypes.Result{sqltypes.MakeTestResult(
+			sqltypes.MakeTestFields("a|b", "uint64|uint64"),
+			"3|2",
+			"1|0",
+			"1|2",
+		)},
+	}
+	proj := &Projection{
+		Cols:       []string{"apa"},
+		Exprs:      []evalengine.Expr{evalExpr},
+		Input:      fp,
+		noTxNeeded: noTxNeeded{},
+	}
+	qr, err := proj.TryExecute(&noopVCursor{}, map[string]*querypb.BindVariable{}, false)
+	require.NoError(t, err)
+	assert.Equal(t, "[[UINT64(6)] [UINT64(0)] [UINT64(2)]]", fmt.Sprintf("%v", qr.Rows))
+}
diff --git a/go/vt/vtgate/planbuilder/abstract/queryprojection_test.go b/go/vt/vtgate/planbuilder/abstract/queryprojection_test.go
index e516aa1908..905398df0b 100644
--- a/go/vt/vtgate/planbuilder/abstract/queryprojection_test.go
+++ b/go/vt/vtgate/planbuilder/abstract/queryprojection_test.go
@@ -204,3 +204,46 @@ func TestQPSimplifiedExpr(t *testing.T) {
 		})
 	}
 }
+
+func TestCompareRefInt(t *testing.T) {
+	one := 1
+	two := 2
+	tests := []struct {
+		name string
+		a    *int
+		b    *int
+		want bool
+	}{
+		{
+			name: "1<2",
+			a:    &one,
+			b:    &two,
+			want: true,
+		}, {
+			name: "2<1",
+			a:    &two,
+			b:    &one,
+			want: false,
+		}, {
+			name: "2<nil",
+			a:    &two,
+			b:    nil,
+			want: true,
+		}, {
+			name: "nil<1",
+			a:    nil,
+			b:    &one,
+			want: false,
+		}, {
+			name: "nil<nil",
+			a:    nil,
+			b:    nil,
+			want: false,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			require.Equal(t, tt.want, CompareRefInt(tt.a, tt.b))
+		})
+	}
+}
diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
index 797dea2683..02c02946fe 100644
--- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
@@ -118,9 +118,9 @@ Gen4 plan same as above
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select count(*), a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(textcol1), weight_string(b)",
+        "FieldQuery": "select count(*), a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b)",
         "OrderBy": "(1|5) ASC, (2|4) ASC, (3|6) ASC",
-        "Query": "select count(*), a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) from `user` group by a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(textcol1), weight_string(b) order by a asc, textcol1 asc, b asc",
+        "Query": "select count(*), a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) from `user` group by a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) order by a asc, textcol1 asc, b asc",
         "ResultColumns": 5,
         "Table": "`user`"
       }
@@ -133,8 +133,8 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(0) AS count(*)",
-    "GroupBy": "(1|4), (2|5) COLLATE latin1_swedish_ci, (3|6)",
+    "Aggregates": "sum(0) AS count(*)",
+    "GroupBy": "(1|4), 2 COLLATE latin1_swedish_ci, (3|5)",
     "ResultColumns": 4,
     "Inputs": [
       {
@@ -144,9 +144,9 @@ Gen4 plan same as above
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select count(*), a, textcol1, b, weight_string(a), weight_string(textcol1), weight_string(b) from `user` where 1 != 1 group by a, weight_string(a), textcol1, weight_string(textcol1), b, weight_string(b)",
-        "OrderBy": "(1|4) ASC, (2|5) ASC COLLATE latin1_swedish_ci, (3|6) ASC",
-        "Query": "select count(*), a, textcol1, b, weight_string(a), weight_string(textcol1), weight_string(b) from `user` group by a, weight_string(a), textcol1, weight_string(textcol1), b, weight_string(b) order by a asc, textcol1 asc, b asc",
+        "FieldQuery": "select count(*), a, textcol1, b, weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, weight_string(a), textcol1, b, weight_string(b)",
+        "OrderBy": "(1|4) ASC, 2 ASC COLLATE latin1_swedish_ci, (3|5) ASC",
+        "Query": "select count(*), a, textcol1, b, weight_string(a), weight_string(b) from `user` group by a, weight_string(a), textcol1, b, weight_string(b) order by a asc, textcol1 asc, b asc",
         "Table": "`user`"
       }
     ]
@@ -185,7 +185,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(0) AS count(*)",
+    "Aggregates": "sum(0) AS count(*)",
     "GroupBy": "1",
     "Inputs": [
       {
@@ -229,9 +229,9 @@ Gen4 plan same as above
               "Name": "user",
               "Sharded": true
             },
-            "FieldQuery": "select count(*) as k, a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, textcol1, b, weight_string(textcol1), weight_string(textcol1), weight_string(textcol1), weight_string(a), weight_string(b)",
+            "FieldQuery": "select count(*) as k, a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b)",
             "OrderBy": "(2|4) ASC, (1|5) ASC, (3|6) ASC",
-            "Query": "select count(*) as k, a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) from `user` group by a, textcol1, b, weight_string(textcol1), weight_string(textcol1), weight_string(textcol1), weight_string(a), weight_string(b) order by textcol1 asc, a asc, b asc",
+            "Query": "select count(*) as k, a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) from `user` group by a, textcol1, b, weight_string(textcol1), weight_string(a), weight_string(b) order by textcol1 asc, a asc, b asc",
             "ResultColumns": 5,
             "Table": "`user`"
           }
@@ -246,14 +246,14 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Sort",
     "Variant": "Memory",
-    "OrderBy": "0 ASC, (2|5) ASC COLLATE latin1_swedish_ci",
+    "OrderBy": "0 ASC, 2 ASC COLLATE latin1_swedish_ci",
     "ResultColumns": 4,
     "Inputs": [
       {
         "OperatorType": "Aggregate",
         "Variant": "Ordered",
-        "Aggregates": "count(0) AS k",
-        "GroupBy": "(1|4), (2|5) COLLATE latin1_swedish_ci, (3|6)",
+        "Aggregates": "sum(0) AS k",
+        "GroupBy": "(1|4), 2 COLLATE latin1_swedish_ci, (3|5)",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -262,9 +262,9 @@ Gen4 plan same as above
               "Name": "user",
               "Sharded": true
             },
-            "FieldQuery": "select count(*) as k, a, textcol1, b, weight_string(a), weight_string(textcol1), weight_string(b) from `user` where 1 != 1 group by a, weight_string(a), textcol1, weight_string(textcol1), b, weight_string(b)",
-            "OrderBy": "(1|4) ASC, (2|5) ASC COLLATE latin1_swedish_ci, (3|6) ASC",
-            "Query": "select count(*) as k, a, textcol1, b, weight_string(a), weight_string(textcol1), weight_string(b) from `user` group by a, weight_string(a), textcol1, weight_string(textcol1), b, weight_string(b) order by a asc, textcol1 asc, b asc",
+            "FieldQuery": "select count(*) as k, a, textcol1, b, weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, weight_string(a), textcol1, b, weight_string(b)",
+            "OrderBy": "(1|4) ASC, 2 ASC COLLATE latin1_swedish_ci, (3|5) ASC",
+            "Query": "select count(*) as k, a, textcol1, b, weight_string(a), weight_string(b) from `user` group by a, weight_string(a), textcol1, b, weight_string(b) order by a asc, textcol1 asc, b asc",
             "Table": "`user`"
           }
         ]
@@ -303,7 +303,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Scalar",
-    "Aggregates": "count(0) AS count(*)",
+    "Aggregates": "sum(0) AS count(*)",
     "Inputs": [
       {
         "OperatorType": "Route",
@@ -620,7 +620,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(1) AS count(*)",
+    "Aggregates": "sum(1) AS count(*)",
     "GroupBy": "0",
     "Inputs": [
       {
@@ -648,7 +648,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(1) AS count(*)",
+    "Aggregates": "sum(1) AS count(*)",
     "GroupBy": "0, (2|3)",
     "ResultColumns": 2,
     "Inputs": [
@@ -701,7 +701,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(1) AS count(*)",
+    "Aggregates": "sum(1) AS count(*)",
     "GroupBy": "(0|2)",
     "ResultColumns": 2,
     "Inputs": [
@@ -901,7 +901,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -1051,7 +1051,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(1) AS count(distinct id)",
+    "Aggregates": "sum(1) AS count(distinct id)",
     "GroupBy": "0",
     "Inputs": [
       {
@@ -1313,7 +1313,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "min(1) AS min(distinct col2)",
+    "Aggregates": "min(1|3) AS min(distinct col2)",
     "GroupBy": "(0|2)",
     "ResultColumns": 2,
     "Inputs": [
@@ -1324,9 +1324,9 @@ Gen4 plan same as above
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select col1, min(distinct col2), weight_string(col1) from `user` where 1 != 1 group by col1, weight_string(col1)",
-        "OrderBy": "(0|2) ASC",
-        "Query": "select col1, min(distinct col2), weight_string(col1) from `user` group by col1, weight_string(col1) order by col1 asc",
+        "FieldQuery": "select col1, col2, weight_string(col1), weight_string(col2) from `user` where 1 != 1 group by col1, weight_string(col1), col2, weight_string(col2)",
+        "OrderBy": "(0|2) ASC, (1|3) ASC",
+        "Query": "select col1, col2, weight_string(col1), weight_string(col2) from `user` group by col1, weight_string(col1), col2, weight_string(col2) order by col1 asc, col2 asc",
         "Table": "`user`"
       }
     ]
@@ -1438,8 +1438,8 @@ Gen4 error: Can't group on 'count(*)'
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(2) AS count(*)",
-    "GroupBy": "(1|3), (0|4)",
+    "Aggregates": "sum(2) AS count(*)",
+    "GroupBy": "(0|3), (1|4)",
     "ResultColumns": 3,
     "Inputs": [
       {
@@ -1449,9 +1449,9 @@ Gen4 error: Can't group on 'count(*)'
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select a, b, count(*), weight_string(b), weight_string(a) from `user` where 1 != 1 group by b, weight_string(b), a, weight_string(a)",
-        "OrderBy": "(1|3) ASC, (0|4) ASC",
-        "Query": "select a, b, count(*), weight_string(b), weight_string(a) from `user` group by b, weight_string(b), a, weight_string(a) order by b asc, a asc",
+        "FieldQuery": "select a, b, count(*), weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, weight_string(a), b, weight_string(b)",
+        "OrderBy": "(0|3) ASC, (1|4) ASC",
+        "Query": "select a, b, count(*), weight_string(a), weight_string(b) from `user` group by a, weight_string(a), b, weight_string(b) order by a asc, b asc",
         "Table": "`user`"
       }
     ]
@@ -1491,8 +1491,8 @@ Gen4 error: Can't group on 'count(*)'
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(2) AS count(*)",
-    "GroupBy": "(1|3), (0|4)",
+    "Aggregates": "sum(2) AS count(*)",
+    "GroupBy": "(0|3), (1|4)",
     "ResultColumns": 3,
     "Inputs": [
       {
@@ -1502,9 +1502,9 @@ Gen4 error: Can't group on 'count(*)'
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select a, b, count(*), weight_string(b), weight_string(a) from `user` where 1 != 1 group by b, weight_string(b), a, weight_string(a)",
-        "OrderBy": "(1|3) ASC, (0|4) ASC",
-        "Query": "select a, b, count(*), weight_string(b), weight_string(a) from `user` group by b, weight_string(b), a, weight_string(a) order by b asc, a asc",
+        "FieldQuery": "select a, b, count(*), weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, weight_string(a), b, weight_string(b)",
+        "OrderBy": "(0|3) ASC, (1|4) ASC",
+        "Query": "select a, b, count(*), weight_string(a), weight_string(b) from `user` group by a, weight_string(a), b, weight_string(b) order by a asc, b asc",
         "Table": "`user`"
       }
     ]
@@ -1544,8 +1544,8 @@ Gen4 error: Can't group on 'count(*)'
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(2) AS count(*)",
-    "GroupBy": "(1|3), (0|4)",
+    "Aggregates": "sum(2) AS count(*)",
+    "GroupBy": "(0|3), (1|4)",
     "ResultColumns": 3,
     "Inputs": [
       {
@@ -1555,9 +1555,9 @@ Gen4 error: Can't group on 'count(*)'
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select a, b, count(*), weight_string(b), weight_string(a) from `user` where 1 != 1 group by b, weight_string(b), a, weight_string(a)",
-        "OrderBy": "(1|3) ASC, (0|4) ASC",
-        "Query": "select a, b, count(*), weight_string(b), weight_string(a) from `user` group by b, weight_string(b), a, weight_string(a) order by b asc, a asc",
+        "FieldQuery": "select a, b, count(*), weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, weight_string(a), b, weight_string(b)",
+        "OrderBy": "(0|3) ASC, (1|4) ASC",
+        "Query": "select a, b, count(*), weight_string(a), weight_string(b) from `user` group by a, weight_string(a), b, weight_string(b) order by a asc, b asc",
         "Table": "`user`"
       }
     ]
@@ -1648,7 +1648,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Scalar",
-    "Aggregates": "count(0) AS count(*)",
+    "Aggregates": "sum(0) AS count(*)",
     "Inputs": [
       {
         "OperatorType": "Route",
@@ -1756,8 +1756,8 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(4) AS count(*)",
-    "GroupBy": "(0|5), (1|6), (2|7), (3|8)",
+    "Aggregates": "sum(4) AS count(*)",
+    "GroupBy": "(3|8), (1|6), (0|5), (2|7)",
     "ResultColumns": 5,
     "Inputs": [
       {
@@ -1809,8 +1809,8 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(4) AS count(*)",
-    "GroupBy": "(2|5), (1|6), (0|7), (3|8)",
+    "Aggregates": "sum(4) AS count(*)",
+    "GroupBy": "(3|8), (1|6), (0|5), (2|7)",
     "ResultColumns": 5,
     "Inputs": [
       {
@@ -1820,9 +1820,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select a, b, c, d, count(*), weight_string(c), weight_string(b), weight_string(a), weight_string(d) from `user` where 1 != 1 group by c, weight_string(c), b, weight_string(b), a, weight_string(a), d, weight_string(d)",
-        "OrderBy": "(3|8) ASC, (1|6) ASC, (0|7) ASC, (2|5) ASC",
-        "Query": "select a, b, c, d, count(*), weight_string(c), weight_string(b), weight_string(a), weight_string(d) from `user` group by c, weight_string(c), b, weight_string(b), a, weight_string(a), d, weight_string(d) order by d asc, b asc, a asc, c asc",
+        "FieldQuery": "select a, b, c, d, count(*), weight_string(a), weight_string(b), weight_string(c), weight_string(d) from `user` where 1 != 1 group by a, weight_string(a), b, weight_string(b), c, weight_string(c), d, weight_string(d)",
+        "OrderBy": "(3|8) ASC, (1|6) ASC, (0|5) ASC, (2|7) ASC",
+        "Query": "select a, b, c, d, count(*), weight_string(a), weight_string(b), weight_string(c), weight_string(d) from `user` group by a, weight_string(a), b, weight_string(b), c, weight_string(c), d, weight_string(d) order by d asc, b asc, a asc, c asc",
         "Table": "`user`"
       }
     ]
@@ -1862,8 +1862,8 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(3) AS count(*)",
-    "GroupBy": "(2|4), (1|5), (0|6)",
+    "Aggregates": "sum(3) AS count(*)",
+    "GroupBy": "(0|4), (2|6), (1|5)",
     "ResultColumns": 4,
     "Inputs": [
       {
@@ -1873,9 +1873,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select a, b, c, count(*), weight_string(c), weight_string(b), weight_string(a) from `user` where 1 != 1 group by c, weight_string(c), b, weight_string(b), a, weight_string(a)",
-        "OrderBy": "(0|6) DESC, (2|4) DESC, (1|5) ASC",
-        "Query": "select a, b, c, count(*), weight_string(c), weight_string(b), weight_string(a) from `user` group by c, weight_string(c), b, weight_string(b), a, weight_string(a) order by a desc, c desc, b asc",
+        "FieldQuery": "select a, b, c, count(*), weight_string(a), weight_string(b), weight_string(c) from `user` where 1 != 1 group by a, weight_string(a), b, weight_string(b), c, weight_string(c)",
+        "OrderBy": "(0|4) DESC, (2|6) DESC, (1|5) ASC",
+        "Query": "select a, b, c, count(*), weight_string(a), weight_string(b), weight_string(c) from `user` group by a, weight_string(a), b, weight_string(b), c, weight_string(c) order by a desc, c desc, b asc",
         "Table": "`user`"
       }
     ]
@@ -1929,7 +1929,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Ordered",
-        "Aggregates": "count(1) AS count(*)",
+        "Aggregates": "sum(1) AS count(*)",
         "GroupBy": "0",
         "Inputs": [
           {
@@ -2089,7 +2089,7 @@ Gen4 error: In aggregated query without GROUP BY, expression of SELECT list cont
       {
         "OperatorType": "Aggregate",
         "Variant": "Ordered",
-        "Aggregates": "count(1) AS count(*)",
+        "Aggregates": "sum(1) AS count(*)",
         "GroupBy": "(0|2)",
         "Inputs": [
           {
@@ -2125,7 +2125,7 @@ Gen4 plan same as above
 "syntax error: count(distinct *)"
 Gen4 plan same as above
 
-# if derived table scatter and ordering, then V3 doesn't allow outer constructs to be pushed down.
+# here it is safe to remove the order by on the derived table since it will not influence the output of the count(*)
 "select count(*) from (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) a"
 "unsupported: cross-shard query with aggregates"
 {
@@ -2134,7 +2134,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Scalar",
-    "Aggregates": "count(0) AS count(*)",
+    "Aggregates": "sum(0) AS count(*)",
     "Inputs": [
       {
         "OperatorType": "Route",
@@ -2153,6 +2153,35 @@ Gen4 plan same as above
   }
 }
 
+# here we keep the order since the column is visible on the outside, and used by the orderedAggregate
+"select col, count(*) from (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) a group by col"
+"unsupported: cross-shard query with aggregates"
+{
+  "QueryType": "SELECT",
+  "Original": "select col, count(*) from (select user.col, user_extra.extra from user join user_extra on user.id = user_extra.user_id order by user_extra.extra) a group by col",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "sum(1) AS count(*)",
+    "GroupBy": "0",
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select col, count(*) from (select `user`.col, user_extra.extra, weight_string(user_extra.extra) from `user`, user_extra where 1 != 1) as a where 1 != 1 group by col",
+        "OrderBy": "(1|2) ASC, 0 ASC",
+        "Query": "select col, count(*) from (select `user`.col, user_extra.extra, weight_string(user_extra.extra) from `user`, user_extra where `user`.id = user_extra.user_id order by user_extra.extra asc) as a group by col order by col asc",
+        "ResultColumns": 2,
+        "Table": "`user`, user_extra"
+      }
+    ]
+  }
+}
+
 # optimize group by when using distinct with no aggregation
 "select distinct col1, col2 from user group by col1, col2"
 {
@@ -2239,7 +2268,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Scalar",
-    "Aggregates": "count(0) AS count(*)",
+    "Aggregates": "sum(0) AS count(*)",
     "Inputs": [
       {
         "OperatorType": "Route",
@@ -2269,33 +2298,42 @@ Gen4 plan same as above
     "ResultColumns": 1,
     "Inputs": [
       {
-        "OperatorType": "Join",
-        "Variant": "Join",
-        "JoinColumnIndexes": "-1,-2",
-        "TableName": "`user`_user_extra",
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 0]",
+          "[COLUMN 1]"
+        ],
         "Inputs": [
           {
-            "OperatorType": "Route",
-            "Variant": "Scatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select `user`.a, weight_string(`user`.a) from `user` where 1 != 1",
-            "OrderBy": "(0|1) ASC",
-            "Query": "select `user`.a, weight_string(`user`.a) from `user` order by `user`.a asc",
-            "Table": "`user`"
-          },
-          {
-            "OperatorType": "Route",
-            "Variant": "Scatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select 1 from user_extra where 1 != 1",
-            "Query": "select 1 from user_extra",
-            "Table": "user_extra"
+            "OperatorType": "Join",
+            "Variant": "Join",
+            "JoinColumnIndexes": "-1,-2",
+            "TableName": "`user`_user_extra",
+            "Inputs": [
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select `user`.a, weight_string(`user`.a) from `user` where 1 != 1 group by `user`.a, weight_string(`user`.a)",
+                "OrderBy": "(0|1) ASC",
+                "Query": "select `user`.a, weight_string(`user`.a) from `user` group by `user`.a, weight_string(`user`.a) order by `user`.a asc",
+                "Table": "`user`"
+              },
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select 1 from user_extra where 1 != 1",
+                "Query": "select 1 from user_extra",
+                "Table": "user_extra"
+              }
+            ]
           }
         ]
       }
@@ -2306,30 +2344,7 @@ Gen4 plan same as above
 # Cannot have more than one aggr(distinct...
 "select count(distinct a), count(distinct b) from user"
 "unsupported: only one distinct aggregation allowed in a select: count(distinct b)"
-{
-  "QueryType": "SELECT",
-  "Original": "select count(distinct a), count(distinct b) from user",
-  "Instructions": {
-    "OperatorType": "Aggregate",
-    "Variant": "Scalar",
-    "Aggregates": "count_distinct(0|2) AS count(distinct a), count_distinct(1|3) AS count(distinct b)",
-    "ResultColumns": 2,
-    "Inputs": [
-      {
-        "OperatorType": "Route",
-        "Variant": "Scatter",
-        "Keyspace": {
-          "Name": "user",
-          "Sharded": true
-        },
-        "FieldQuery": "select a, b, weight_string(a), weight_string(b) from `user` where 1 != 1 group by a, weight_string(a), b, weight_string(b)",
-        "OrderBy": "(0|2) ASC, (1|3) ASC",
-        "Query": "select a, b, weight_string(a), weight_string(b) from `user` group by a, weight_string(a), b, weight_string(b) order by a asc, b asc",
-        "Table": "`user`"
-      }
-    ]
-  }
-}
+Gen4 plan same as above
 
 # multiple distinct functions with grouping.
 "select col1, count(distinct col2), sum(distinct col2) from user group by col1"
@@ -2351,9 +2366,9 @@ Gen4 plan same as above
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select col1, col2, col2, weight_string(col1), weight_string(col2) from `user` where 1 != 1 group by col1, weight_string(col1), col2, weight_string(col2), col2, weight_string(col2)",
-        "OrderBy": "(0|3) ASC, (1|4) ASC, (1|4) ASC",
-        "Query": "select col1, col2, col2, weight_string(col1), weight_string(col2) from `user` group by col1, weight_string(col1), col2, weight_string(col2), col2, weight_string(col2) order by col1 asc, col2 asc, col2 asc",
+        "FieldQuery": "select col1, col2, col2, weight_string(col1), weight_string(col2) from `user` where 1 != 1 group by col1, weight_string(col1), col2, weight_string(col2)",
+        "OrderBy": "(0|3) ASC, (1|4) ASC",
+        "Query": "select col1, col2, col2, weight_string(col1), weight_string(col2) from `user` group by col1, weight_string(col1), col2, weight_string(col2) order by col1 asc, col2 asc",
         "Table": "`user`"
       }
     ]
@@ -2374,7 +2389,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Ordered",
-        "Aggregates": "count(1) AS k",
+        "Aggregates": "sum(1) AS k",
         "GroupBy": "0",
         "Inputs": [
           {
@@ -2427,7 +2442,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(1) AS k",
+    "Aggregates": "sum(1) AS k",
     "GroupBy": "0",
     "Inputs": [
       {
@@ -2495,7 +2510,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Scalar",
-    "Aggregates": "count(0) AS count(*)",
+    "Aggregates": "sum(0) AS count(*)",
     "Inputs": [
       {
         "OperatorType": "Route",
@@ -2564,7 +2579,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(1) AS count(*)",
+    "Aggregates": "sum(1) AS count(*)",
     "GroupBy": "(0|2)",
     "ResultColumns": 2,
     "Inputs": [
@@ -2617,7 +2632,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(1) AS count(*)",
+    "Aggregates": "sum(1) AS count(*)",
     "GroupBy": "(0|2)",
     "ResultColumns": 2,
     "Inputs": [
@@ -2744,7 +2759,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -2776,7 +2791,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -2808,7 +2823,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -2840,7 +2855,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -2872,7 +2887,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -2904,7 +2919,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -2936,7 +2951,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -2968,7 +2983,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -3000,7 +3015,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Ordered",
-        "Aggregates": "count(1) AS a",
+        "Aggregates": "sum(1) AS a",
         "GroupBy": "0",
         "Inputs": [
           {
@@ -3041,7 +3056,7 @@ Gen4 plan same as above
           {
             "OperatorType": "Aggregate",
             "Variant": "Ordered",
-            "Aggregates": "count(0) AS a",
+            "Aggregates": "sum(0) AS a",
             "GroupBy": "(1|2)",
             "Inputs": [
               {
@@ -3097,9 +3112,8 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count_distinct(1|2 COLLATE latin1_swedish_ci) AS count(distinct textcol1)",
+    "Aggregates": "count_distinct(1 COLLATE latin1_swedish_ci) AS count(distinct textcol1)",
     "GroupBy": "0",
-    "ResultColumns": 2,
     "Inputs": [
       {
         "OperatorType": "Route",
@@ -3108,11 +3122,737 @@ Gen4 plan same as above
           "Name": "user",
           "Sharded": true
         },
-        "FieldQuery": "select col, textcol1, weight_string(textcol1) from `user` where 1 != 1 group by col, textcol1, weight_string(textcol1)",
-        "OrderBy": "0 ASC, (1|2) ASC COLLATE latin1_swedish_ci",
-        "Query": "select col, textcol1, weight_string(textcol1) from `user` group by col, textcol1, weight_string(textcol1) order by col asc, textcol1 asc",
+        "FieldQuery": "select col, textcol1 from `user` where 1 != 1 group by col, textcol1",
+        "OrderBy": "0 ASC, 1 ASC COLLATE latin1_swedish_ci",
+        "Query": "select col, textcol1 from `user` group by col, textcol1 order by col asc, textcol1 asc",
         "Table": "`user`"
       }
     ]
   }
 }
+
+# aggregation filtering by having on a route with no group by with non-unique vindex filter
+"select 1 from user having count(id) = 10 and name = 'a'"
+{
+  "QueryType": "SELECT",
+  "Original": "select 1 from user having count(id) = 10 and name = 'a'",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "Equal",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select 1 from `user` where 1 != 1",
+    "Query": "select 1 from `user` having count(id) = 10 and `name` = 'a'",
+    "Table": "`user`",
+    "Values": [
+      "VARCHAR(\"a\")"
+    ],
+    "Vindex": "name_user_map"
+  }
+}
+Gen4 error: cannot push projections in ordered aggregates
+
+# Aggregates and joins
+"select count(*) from user join user_extra"
+"unsupported: cross-shard query with aggregates"
+{
+  "QueryType": "SELECT",
+  "Original": "select count(*) from user join user_extra",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Scalar",
+    "Aggregates": "sum(0) AS count(*)",
+    "Inputs": [
+      {
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 0] * [COLUMN 1] as count(*)"
+        ],
+        "Inputs": [
+          {
+            "OperatorType": "Join",
+            "Variant": "Join",
+            "JoinColumnIndexes": "-1,1",
+            "TableName": "`user`_user_extra",
+            "Inputs": [
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select count(*) from `user` where 1 != 1",
+                "Query": "select count(*) from `user`",
+                "Table": "`user`"
+              },
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select count(*) from user_extra where 1 != 1",
+                "Query": "select count(*) from user_extra",
+                "Table": "user_extra"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
+
+# aggregation filtering by having on a route with no group by
+"select 1 from user having count(id) = 10"
+{
+  "QueryType": "SELECT",
+  "Original": "select 1 from user having count(id) = 10",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "Scatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select 1 from `user` where 1 != 1",
+    "Query": "select 1 from `user` having count(id) = 10",
+    "Table": "`user`"
+  }
+}
+Gen4 error: cannot push projections in ordered aggregates
+
+# Aggregate on join
+"select user.a, count(*) from user join user_extra group by user.a"
+"unsupported: cross-shard query with aggregates"
+{
+  "QueryType": "SELECT",
+  "Original": "select user.a, count(*) from user join user_extra group by user.a",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "sum(1) AS count(*)",
+    "GroupBy": "(0|2)",
+    "ResultColumns": 2,
+    "Inputs": [
+      {
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 0]",
+          "[COLUMN 2] * [COLUMN 3] as count(*)",
+          "[COLUMN 1]"
+        ],
+        "Inputs": [
+          {
+            "OperatorType": "Join",
+            "Variant": "Join",
+            "JoinColumnIndexes": "-2,-3,-1,1",
+            "TableName": "`user`_user_extra",
+            "Inputs": [
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select count(*), `user`.a, weight_string(`user`.a) from `user` where 1 != 1 group by `user`.a, weight_string(`user`.a)",
+                "OrderBy": "(1|2) ASC",
+                "Query": "select count(*), `user`.a, weight_string(`user`.a) from `user` group by `user`.a, weight_string(`user`.a) order by `user`.a asc",
+                "Table": "`user`"
+              },
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select count(*) from user_extra where 1 != 1",
+                "Query": "select count(*) from user_extra",
+                "Table": "user_extra"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
+
+# Aggregate on other table in join
+"select user.a, count(user_extra.a) from user join user_extra group by user.a"
+"unsupported: cross-shard query with aggregates"
+{
+  "QueryType": "SELECT",
+  "Original": "select user.a, count(user_extra.a) from user join user_extra group by user.a",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "sum(1) AS count(user_extra.a)",
+    "GroupBy": "(0|2)",
+    "ResultColumns": 2,
+    "Inputs": [
+      {
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 0]",
+          "[COLUMN 2] * [COLUMN 3] as count(user_extra.a)",
+          "[COLUMN 1]"
+        ],
+        "Inputs": [
+          {
+            "OperatorType": "Join",
+            "Variant": "Join",
+            "JoinColumnIndexes": "-2,-3,-1,1",
+            "TableName": "`user`_user_extra",
+            "Inputs": [
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select count(*), `user`.a, weight_string(`user`.a) from `user` where 1 != 1 group by `user`.a, weight_string(`user`.a)",
+                "OrderBy": "(1|2) ASC",
+                "Query": "select count(*), `user`.a, weight_string(`user`.a) from `user` group by `user`.a, weight_string(`user`.a) order by `user`.a asc",
+                "Table": "`user`"
+              },
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select count(user_extra.a) from user_extra where 1 != 1",
+                "Query": "select count(user_extra.a) from user_extra",
+                "Table": "user_extra"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
+
+# aggregation spread out across three routes
+"select count(u.textcol1), count(ue.foo), us.bar from user u join user_extra ue on u.foo = ue.bar join unsharded us on ue.bar = us.baz group by us.bar"
+"unsupported: cross-shard query with aggregates"
+{
+  "QueryType": "SELECT",
+  "Original": "select count(u.textcol1), count(ue.foo), us.bar from user u join user_extra ue on u.foo = ue.bar join unsharded us on ue.bar = us.baz group by us.bar",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "sum(0) AS count(u.textcol1), sum(1) AS count(ue.foo)",
+    "GroupBy": "(2|3)",
+    "ResultColumns": 3,
+    "Inputs": [
+      {
+        "OperatorType": "Projection",
+        "Expressions": [
+          "([COLUMN 2] * [COLUMN 3]) * [COLUMN 4] as count(u.textcol1)",
+          "([COLUMN 5] * [COLUMN 6]) * [COLUMN 7] as count(ue.foo)",
+          "[COLUMN 0]",
+          "[COLUMN 1]"
+        ],
+        "Inputs": [
+          {
+            "OperatorType": "Sort",
+            "Variant": "Memory",
+            "OrderBy": "(0|1) ASC",
+            "Inputs": [
+              {
+                "OperatorType": "Join",
+                "Variant": "Join",
+                "JoinColumnIndexes": "1,2,-2,3,4,-3,5,6",
+                "JoinVars": {
+                  "u_foo": 0
+                },
+                "TableName": "`user`_user_extra_unsharded",
+                "Inputs": [
+                  {
+                    "OperatorType": "Route",
+                    "Variant": "Scatter",
+                    "Keyspace": {
+                      "Name": "user",
+                      "Sharded": true
+                    },
+                    "FieldQuery": "select u.foo, count(u.textcol1), count(*), weight_string(u.foo) from `user` as u where 1 != 1 group by u.foo, weight_string(u.foo)",
+                    "Query": "select u.foo, count(u.textcol1), count(*), weight_string(u.foo) from `user` as u group by u.foo, weight_string(u.foo)",
+                    "Table": "`user`"
+                  },
+                  {
+                    "OperatorType": "Join",
+                    "Variant": "Join",
+                    "JoinColumnIndexes": "2,3,-2,1,-3,1",
+                    "JoinVars": {
+                      "ue_bar": 0
+                    },
+                    "TableName": "user_extra_unsharded",
+                    "Inputs": [
+                      {
+                        "OperatorType": "Route",
+                        "Variant": "Scatter",
+                        "Keyspace": {
+                          "Name": "user",
+                          "Sharded": true
+                        },
+                        "FieldQuery": "select ue.bar, count(*), count(ue.foo), weight_string(ue.bar) from user_extra as ue where 1 != 1 group by ue.bar, weight_string(ue.bar)",
+                        "Query": "select ue.bar, count(*), count(ue.foo), weight_string(ue.bar) from user_extra as ue where ue.bar = :u_foo group by ue.bar, weight_string(ue.bar)",
+                        "Table": "user_extra"
+                      },
+                      {
+                        "OperatorType": "Route",
+                        "Variant": "Unsharded",
+                        "Keyspace": {
+                          "Name": "main",
+                          "Sharded": false
+                        },
+                        "FieldQuery": "select count(*), us.bar, weight_string(us.bar) from unsharded as us where 1 != 1 group by us.bar, weight_string(us.bar)",
+                        "Query": "select count(*), us.bar, weight_string(us.bar) from unsharded as us where us.baz = :ue_bar group by us.bar, weight_string(us.bar)",
+                        "Table": "unsharded"
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
+
+# using two distinct columns - min with distinct vindex, sum with distinct without vindex
+"select col1, min(distinct id), sum(distinct col3) from user group by col1"
+{
+  "QueryType": "SELECT",
+  "Original": "select col1, min(distinct id), sum(distinct col3) from user group by col1",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "min(1), sum_distinct(2) AS sum(distinct col3)",
+    "GroupBy": "0",
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select col1, min(distinct id), col3, weight_string(col1), weight_string(col3) from `user` where 1 != 1 group by col1, col3, weight_string(col1), weight_string(col3)",
+        "OrderBy": "(0|3) ASC, (2|4) ASC",
+        "Query": "select col1, min(distinct id), col3, weight_string(col1), weight_string(col3) from `user` group by col1, col3, weight_string(col1), weight_string(col3) order by col1 asc, col3 asc",
+        "ResultColumns": 3,
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select col1, min(distinct id), sum(distinct col3) from user group by col1",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "min(1) AS min(distinct id), sum_distinct(2|4) AS sum(distinct col3)",
+    "GroupBy": "(0|3)",
+    "ResultColumns": 3,
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select col1, min(distinct id), col3, weight_string(col1), weight_string(col3) from `user` where 1 != 1 group by col1, weight_string(col1), col3, weight_string(col3)",
+        "OrderBy": "(0|3) ASC, (2|4) ASC",
+        "Query": "select col1, min(distinct id), col3, weight_string(col1), weight_string(col3) from `user` group by col1, weight_string(col1), col3, weight_string(col3) order by col1 asc, col3 asc",
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+
+# aggregation on top of semijoin
+"select count(*) from user where exists (select 0 from user_extra where user.apa = user_extra.bar)"
+"unsupported: cross-shard correlated subquery"
+{
+  "QueryType": "SELECT",
+  "Original": "select count(*) from user where exists (select 0 from user_extra where user.apa = user_extra.bar)",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Scalar",
+    "Aggregates": "sum(0) AS count(*)",
+    "Inputs": [
+      {
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 1] as count(*)"
+        ],
+        "Inputs": [
+          {
+            "OperatorType": "SemiJoin",
+            "JoinVars": {
+              "user_apa": 0
+            },
+            "TableName": "`user`_user_extra",
+            "Inputs": [
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select `user`.apa, count(*), weight_string(`user`.apa) from `user` where 1 != 1 group by `user`.apa, weight_string(`user`.apa)",
+                "Query": "select `user`.apa, count(*), weight_string(`user`.apa) from `user` group by `user`.apa, weight_string(`user`.apa)",
+                "Table": "`user`"
+              },
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select 1 from user_extra where 1 != 1",
+                "Query": "select 1 from user_extra where user_extra.bar = :user_apa",
+                "Table": "user_extra"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
+
+# we have to track the order of distinct aggregation expressions
+"select val2, count(distinct val1), count(*) from user group by val2"
+{
+  "QueryType": "SELECT",
+  "Original": "select val2, count(distinct val1), count(*) from user group by val2",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "count_distinct(1) AS count(distinct val1), count(2)",
+    "GroupBy": "0",
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select val2, val1, count(*), weight_string(val2), weight_string(val1) from `user` where 1 != 1 group by val2, val1, weight_string(val2), weight_string(val1)",
+        "OrderBy": "(0|3) ASC, (1|4) ASC",
+        "Query": "select val2, val1, count(*), weight_string(val2), weight_string(val1) from `user` group by val2, val1, weight_string(val2), weight_string(val1) order by val2 asc, val1 asc",
+        "ResultColumns": 3,
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select val2, count(distinct val1), count(*) from user group by val2",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "count_distinct(1|4) AS count(distinct val1), sum(2) AS count(*)",
+    "GroupBy": "(0|3)",
+    "ResultColumns": 3,
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select val2, val1, count(*), weight_string(val2), weight_string(val1) from `user` where 1 != 1 group by val2, weight_string(val2), val1, weight_string(val1)",
+        "OrderBy": "(0|3) ASC, (1|4) ASC",
+        "Query": "select val2, val1, count(*), weight_string(val2), weight_string(val1) from `user` group by val2, weight_string(val2), val1, weight_string(val1) order by val2 asc, val1 asc",
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+
+# group by column alias
+"select ascii(val1) as a, count(*) from user group by a"
+{
+  "QueryType": "SELECT",
+  "Original": "select ascii(val1) as a, count(*) from user group by a",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "count(1)",
+    "GroupBy": "0",
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select ascii(val1) as a, count(*), weight_string(ascii(val1)) from `user` where 1 != 1 group by a, weight_string(ascii(val1))",
+        "OrderBy": "(0|2) ASC",
+        "Query": "select ascii(val1) as a, count(*), weight_string(ascii(val1)) from `user` group by a, weight_string(ascii(val1)) order by a asc",
+        "ResultColumns": 2,
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select ascii(val1) as a, count(*) from user group by a",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "sum(1) AS count(*)",
+    "GroupBy": "(0|2)",
+    "ResultColumns": 2,
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select ascii(val1) as a, count(*), weight_string(ascii(val1)) from `user` where 1 != 1 group by a, weight_string(ascii(val1))",
+        "OrderBy": "(0|2) ASC",
+        "Query": "select ascii(val1) as a, count(*), weight_string(ascii(val1)) from `user` group by a, weight_string(ascii(val1)) order by a asc",
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+
+# multiple distinct aggregations on the same column is allowed
+"select tcol1, count(distinct tcol2), sum(distinct tcol2) from user group by tcol1"
+"unsupported: only one distinct aggregation allowed in a select: sum(distinct tcol2)"
+{
+  "QueryType": "SELECT",
+  "Original": "select tcol1, count(distinct tcol2), sum(distinct tcol2) from user group by tcol1",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "count_distinct(1|4) AS count(distinct tcol2), sum_distinct(2|4) AS sum(distinct tcol2)",
+    "GroupBy": "(0|3)",
+    "ResultColumns": 3,
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select tcol1, tcol2, tcol2, weight_string(tcol1), weight_string(tcol2) from `user` where 1 != 1 group by tcol1, weight_string(tcol1), tcol2, weight_string(tcol2)",
+        "OrderBy": "(0|3) ASC, (1|4) ASC",
+        "Query": "select tcol1, tcol2, tcol2, weight_string(tcol1), weight_string(tcol2) from `user` group by tcol1, weight_string(tcol1), tcol2, weight_string(tcol2) order by tcol1 asc, tcol2 asc",
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+
+# multiple distinct aggregations on the same column in different positions
+"select count(distinct tcol2), tcol1, count(*), sum(distinct tcol2) from user group by tcol1"
+"unsupported: only one distinct aggregation allowed in a select: sum(distinct tcol2)"
+{
+  "QueryType": "SELECT",
+  "Original": "select count(distinct tcol2), tcol1, count(*), sum(distinct tcol2) from user group by tcol1",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "count_distinct(0|4) AS count(distinct tcol2), sum(2) AS count(*), sum_distinct(3|4) AS sum(distinct tcol2)",
+    "GroupBy": "(1|5)",
+    "ResultColumns": 4,
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select tcol2, tcol1, count(*), tcol2, weight_string(tcol2), weight_string(tcol1) from `user` where 1 != 1 group by tcol2, weight_string(tcol2), tcol1, weight_string(tcol1)",
+        "OrderBy": "(1|5) ASC, (0|4) ASC",
+        "Query": "select tcol2, tcol1, count(*), tcol2, weight_string(tcol2), weight_string(tcol1) from `user` group by tcol2, weight_string(tcol2), tcol1, weight_string(tcol1) order by tcol1 asc, tcol2 asc",
+        "Table": "`user`"
+      }
+    ]
+  }
+}
+
+# distinct aggregation will 3 table join query
+"select u.textcol1, count(distinct u.val2) from user u join user u2 on u.val2 = u2.id join music m on u2.val2 = m.id group by u.textcol1"
+"unsupported: cross-shard query with aggregates"
+{
+  "QueryType": "SELECT",
+  "Original": "select u.textcol1, count(distinct u.val2) from user u join user u2 on u.val2 = u2.id join music m on u2.val2 = m.id group by u.textcol1",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "count_distinct(1|2) AS count(distinct u.val2)",
+    "GroupBy": "0 COLLATE latin1_swedish_ci",
+    "ResultColumns": 2,
+    "Inputs": [
+      {
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 0]",
+          "[COLUMN 1]",
+          "[COLUMN 2]"
+        ],
+        "Inputs": [
+          {
+            "OperatorType": "Join",
+            "Variant": "Join",
+            "JoinColumnIndexes": "-3,-4,-6",
+            "JoinVars": {
+              "u2_val2": 0
+            },
+            "TableName": "`user`_`user`_music",
+            "Inputs": [
+              {
+                "OperatorType": "Join",
+                "Variant": "Join",
+                "JoinColumnIndexes": "1,1,-3,-1,2,-2",
+                "JoinVars": {
+                  "u_val2": 0
+                },
+                "TableName": "`user`_`user`",
+                "Inputs": [
+                  {
+                    "OperatorType": "Route",
+                    "Variant": "Scatter",
+                    "Keyspace": {
+                      "Name": "user",
+                      "Sharded": true
+                    },
+                    "FieldQuery": "select u.val2, weight_string(u.val2), u.textcol1 from `user` as u where 1 != 1 group by u.val2, weight_string(u.val2), u.textcol1",
+                    "OrderBy": "2 ASC COLLATE latin1_swedish_ci, (0|1) ASC",
+                    "Query": "select u.val2, weight_string(u.val2), u.textcol1 from `user` as u group by u.val2, weight_string(u.val2), u.textcol1 order by u.textcol1 asc, u.val2 asc",
+                    "Table": "`user`"
+                  },
+                  {
+                    "OperatorType": "Route",
+                    "Variant": "EqualUnique",
+                    "Keyspace": {
+                      "Name": "user",
+                      "Sharded": true
+                    },
+                    "FieldQuery": "select u2.val2, weight_string(u2.val2) from `user` as u2 where 1 != 1 group by u2.val2, weight_string(u2.val2)",
+                    "Query": "select u2.val2, weight_string(u2.val2) from `user` as u2 where u2.id = :u_val2 group by u2.val2, weight_string(u2.val2)",
+                    "Table": "`user`",
+                    "Values": [
+                      ":u_val2"
+                    ],
+                    "Vindex": "user_index"
+                  }
+                ]
+              },
+              {
+                "OperatorType": "Route",
+                "Variant": "EqualUnique",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select 1 from music as m where 1 != 1",
+                "Query": "select 1 from music as m where m.id = :u2_val2",
+                "Table": "music",
+                "Values": [
+                  ":u2_val2"
+                ],
+                "Vindex": "music_user_map"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
+
+# interleaving grouping, aggregation and join
+"select user.col, min(user_extra.foo), user.bar, max(user_extra.bar) from user join user_extra on user.col = user_extra.bar group by user.col, user.bar"
+"unsupported: cross-shard query with aggregates"
+{
+  "QueryType": "SELECT",
+  "Original": "select user.col, min(user_extra.foo), user.bar, max(user_extra.bar) from user join user_extra on user.col = user_extra.bar group by user.col, user.bar",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "min(1) AS min(user_extra.foo), max(3) AS max(user_extra.bar)",
+    "GroupBy": "0, (2|4)",
+    "ResultColumns": 4,
+    "Inputs": [
+      {
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 0]",
+          "[COLUMN 3] as min(user_extra.foo)",
+          "[COLUMN 1]",
+          "[COLUMN 4] as max(user_extra.bar)",
+          "[COLUMN 2]"
+        ],
+        "Inputs": [
+          {
+            "OperatorType": "Join",
+            "Variant": "Join",
+            "JoinColumnIndexes": "-1,-2,-3,1,2",
+            "JoinVars": {
+              "user_col": 0
+            },
+            "TableName": "`user`_user_extra",
+            "Inputs": [
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select `user`.col, `user`.bar, weight_string(`user`.bar) from `user` where 1 != 1 group by `user`.col, `user`.bar, weight_string(`user`.bar)",
+                "OrderBy": "0 ASC, (1|2) ASC",
+                "Query": "select `user`.col, `user`.bar, weight_string(`user`.bar) from `user` group by `user`.col, `user`.bar, weight_string(`user`.bar) order by `user`.col asc, `user`.bar asc",
+                "Table": "`user`"
+              },
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "user",
+                  "Sharded": true
+                },
+                "FieldQuery": "select min(user_extra.foo), max(user_extra.bar) from user_extra where 1 != 1",
+                "Query": "select min(user_extra.foo), max(user_extra.bar) from user_extra where user_extra.bar = :user_col",
+                "Table": "user_extra"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
diff --git a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt
index f4c7e5e207..891eb9e740 100644
--- a/go/vt/vtgate/planbuilder/testdata/dml_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/dml_cases.txt
@@ -3447,11 +3447,8 @@ Gen4 plan same as above
     "Inputs": [
       {
         "OperatorType": "Projection",
-        "Columns": [
-          "1"
-        ],
         "Expressions": [
-          "INT64(1)"
+          "INT64(1) as 1"
         ],
         "Inputs": [
           {
@@ -3488,11 +3485,8 @@ Gen4 plan same as above
     "Inputs": [
       {
         "OperatorType": "Projection",
-        "Columns": [
-          "1"
-        ],
         "Expressions": [
-          "INT64(1)"
+          "INT64(1) as 1"
         ],
         "Inputs": [
           {
diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.txt b/go/vt/vtgate/planbuilder/testdata/from_cases.txt
index 03fc842fce..4b5f62be4e 100644
--- a/go/vt/vtgate/planbuilder/testdata/from_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/from_cases.txt
@@ -3293,11 +3293,8 @@ Gen4 plan same as above
   "Original": "select last_insert_id()",
   "Instructions": {
     "OperatorType": "Projection",
-    "Columns": [
-      "last_insert_id()"
-    ],
     "Expressions": [
-      ":__lastInsertId"
+      ":__lastInsertId as last_insert_id()"
     ],
     "Inputs": [
       {
@@ -3731,7 +3728,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -4351,3 +4348,22 @@ Gen4 plan same as above
   }
 }
 Gen4 plan same as above
+
+# Column aliases in Derived Table
+"select id2 from (select id from user) as x (id2)"
+"unsupported: column aliases in derived table"
+{
+  "QueryType": "SELECT",
+  "Original": "select id2 from (select id from user) as x (id2)",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "Scatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select id2 from (select id from `user` where 1 != 1) as x(id2) where 1 != 1",
+    "Query": "select id2 from (select id from `user`) as x(id2)",
+    "Table": "`user`"
+  }
+}
diff --git a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt
index 3827d14cf2..7512388978 100644
--- a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt
@@ -98,9 +98,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
               "Name": "user",
               "Sharded": true
             },
-            "FieldQuery": "select a, b, count(*) as k, weight_string(b), weight_string(a) from `user` where 1 != 1 group by a, weight_string(a), weight_string(a)",
+            "FieldQuery": "select a, b, count(*) as k, weight_string(b), weight_string(a) from `user` where 1 != 1 group by a, weight_string(a)",
             "OrderBy": "(0|4) ASC",
-            "Query": "select a, b, count(*) as k, weight_string(b), weight_string(a) from `user` group by a, weight_string(a), weight_string(a) order by a asc",
+            "Query": "select a, b, count(*) as k, weight_string(b), weight_string(a) from `user` group by a, weight_string(a) order by a asc",
             "ResultColumns": 5,
             "Table": "`user`"
           }
@@ -178,9 +178,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
               "Name": "user",
               "Sharded": true
             },
-            "FieldQuery": "select a, b, count(*) as k, weight_string(a) from `user` where 1 != 1 group by a, weight_string(a), weight_string(a)",
+            "FieldQuery": "select a, b, count(*) as k, weight_string(a) from `user` where 1 != 1 group by a, weight_string(a)",
             "OrderBy": "(0|3) ASC",
-            "Query": "select a, b, count(*) as k, weight_string(a) from `user` group by a, weight_string(a), weight_string(a) order by 1 asc",
+            "Query": "select a, b, count(*) as k, weight_string(a) from `user` group by a, weight_string(a) order by 1 asc",
             "ResultColumns": 4,
             "Table": "`user`"
           }
@@ -217,9 +217,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
               "Name": "user",
               "Sharded": true
             },
-            "FieldQuery": "select textcol1 as t, count(*) as k, weight_string(textcol1) from `user` where 1 != 1 group by textcol1, weight_string(textcol1), weight_string(textcol1), weight_string(textcol1), weight_string(textcol1), weight_string(textcol1)",
+            "FieldQuery": "select textcol1 as t, count(*) as k, weight_string(textcol1) from `user` where 1 != 1 group by textcol1, weight_string(textcol1)",
             "OrderBy": "(0|2) ASC, (0|2) ASC",
-            "Query": "select textcol1 as t, count(*) as k, weight_string(textcol1) from `user` group by textcol1, weight_string(textcol1), weight_string(textcol1), weight_string(textcol1), weight_string(textcol1), weight_string(textcol1) order by textcol1 asc, textcol1 asc",
+            "Query": "select textcol1 as t, count(*) as k, weight_string(textcol1) from `user` group by textcol1, weight_string(textcol1) order by textcol1 asc, textcol1 asc",
             "ResultColumns": 3,
             "Table": "`user`"
           }
@@ -234,14 +234,13 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
   "Instructions": {
     "OperatorType": "Sort",
     "Variant": "Memory",
-    "OrderBy": "(2|3) ASC COLLATE latin1_swedish_ci, 1 ASC, (2|3) ASC COLLATE latin1_swedish_ci",
-    "ResultColumns": 2,
+    "OrderBy": "0 ASC COLLATE latin1_swedish_ci, 1 ASC, 0 ASC COLLATE latin1_swedish_ci",
     "Inputs": [
       {
         "OperatorType": "Aggregate",
         "Variant": "Ordered",
-        "Aggregates": "count(1) AS k",
-        "GroupBy": "(2|3) COLLATE latin1_swedish_ci",
+        "Aggregates": "sum(1) AS k",
+        "GroupBy": "0 COLLATE latin1_swedish_ci",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -250,9 +249,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
               "Name": "user",
               "Sharded": true
             },
-            "FieldQuery": "select textcol1 as t, count(*) as k, textcol1, weight_string(textcol1) from `user` where 1 != 1 group by textcol1, weight_string(textcol1)",
-            "OrderBy": "(2|3) ASC COLLATE latin1_swedish_ci",
-            "Query": "select textcol1 as t, count(*) as k, textcol1, weight_string(textcol1) from `user` group by textcol1, weight_string(textcol1) order by textcol1 asc",
+            "FieldQuery": "select textcol1 as t, count(*) as k from `user` where 1 != 1 group by textcol1",
+            "OrderBy": "0 ASC COLLATE latin1_swedish_ci",
+            "Query": "select textcol1 as t, count(*) as k from `user` group by textcol1 order by textcol1 asc",
             "Table": "`user`"
           }
         ]
diff --git a/go/vt/vtgate/planbuilder/testdata/oltp_cases.txt b/go/vt/vtgate/planbuilder/testdata/oltp_cases.txt
index c8563f514b..1a3c51b41a 100644
--- a/go/vt/vtgate/planbuilder/testdata/oltp_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/oltp_cases.txt
@@ -116,10 +116,9 @@ Gen4 plan same as above
       "Name": "main",
       "Sharded": true
     },
-    "FieldQuery": "select c, weight_string(c) from sbtest1 where 1 != 1",
-    "OrderBy": "(0|1) ASC COLLATE latin1_swedish_ci",
-    "Query": "select c, weight_string(c) from sbtest1 where id between 50 and 235 order by c asc",
-    "ResultColumns": 1,
+    "FieldQuery": "select c from sbtest1 where 1 != 1",
+    "OrderBy": "0 ASC COLLATE latin1_swedish_ci",
+    "Query": "select c from sbtest1 where id between 50 and 235 order by c asc",
     "Table": "sbtest1"
   }
 }
@@ -168,7 +167,7 @@ Gen4 plan same as above
           "Sharded": true
         },
         "FieldQuery": "select c, weight_string(c) from sbtest30 where 1 != 1",
-        "OrderBy": "(0|1) ASC COLLATE latin1_swedish_ci, (0|1) ASC COLLATE latin1_swedish_ci",
+        "OrderBy": "0 ASC COLLATE latin1_swedish_ci, 0 ASC COLLATE latin1_swedish_ci",
         "Query": "select distinct c, weight_string(c) from sbtest30 where id between 1 and 10 order by c asc, c asc",
         "Table": "sbtest30"
       }
diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
index 003543cf68..d6352ac26b 100644
--- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
@@ -416,10 +416,9 @@ Gen4 plan same as above
       "Name": "user",
       "Sharded": true
     },
-    "FieldQuery": "select user_id, col1, col2, weight_string(col1) from authoritative where 1 != 1",
-    "OrderBy": "(1|3) ASC COLLATE latin1_swedish_ci",
-    "Query": "select user_id, col1, col2, weight_string(col1) from authoritative order by col1 asc",
-    "ResultColumns": 3,
+    "FieldQuery": "select user_id, col1, col2 from authoritative where 1 != 1",
+    "OrderBy": "1 ASC COLLATE latin1_swedish_ci",
+    "Query": "select user_id, col1, col2 from authoritative order by col1 asc",
     "Table": "authoritative"
   }
 }
@@ -453,9 +452,9 @@ Gen4 plan same as above
       "Name": "user",
       "Sharded": true
     },
-    "FieldQuery": "select a, textcol1, b, weight_string(a), weight_string(textcol1), weight_string(b) from `user` where 1 != 1",
-    "OrderBy": "(0|3) ASC, (1|4) ASC COLLATE latin1_swedish_ci, (2|5) ASC",
-    "Query": "select a, textcol1, b, weight_string(a), weight_string(textcol1), weight_string(b) from `user` order by a asc, textcol1 asc, b asc",
+    "FieldQuery": "select a, textcol1, b, weight_string(a), weight_string(b) from `user` where 1 != 1",
+    "OrderBy": "(0|3) ASC, 1 ASC COLLATE latin1_swedish_ci, (2|4) ASC",
+    "Query": "select a, textcol1, b, weight_string(a), weight_string(b) from `user` order by a asc, textcol1 asc, b asc",
     "ResultColumns": 3,
     "Table": "`user`"
   }
@@ -490,9 +489,9 @@ Gen4 plan same as above
       "Name": "user",
       "Sharded": true
     },
-    "FieldQuery": "select a, `user`.textcol1, b, weight_string(a), weight_string(textcol1), weight_string(b) from `user` where 1 != 1",
-    "OrderBy": "(0|3) ASC, (1|4) ASC COLLATE latin1_swedish_ci, (2|5) ASC",
-    "Query": "select a, `user`.textcol1, b, weight_string(a), weight_string(textcol1), weight_string(b) from `user` order by a asc, textcol1 asc, b asc",
+    "FieldQuery": "select a, `user`.textcol1, b, weight_string(a), weight_string(b) from `user` where 1 != 1",
+    "OrderBy": "(0|3) ASC, 1 ASC COLLATE latin1_swedish_ci, (2|4) ASC",
+    "Query": "select a, `user`.textcol1, b, weight_string(a), weight_string(b) from `user` order by a asc, textcol1 asc, b asc",
     "ResultColumns": 3,
     "Table": "`user`"
   }
@@ -527,9 +526,9 @@ Gen4 plan same as above
       "Name": "user",
       "Sharded": true
     },
-    "FieldQuery": "select a, textcol1, b, textcol2, weight_string(a), weight_string(textcol1), weight_string(b), weight_string(textcol2) from `user` where 1 != 1",
-    "OrderBy": "(0|4) ASC, (1|5) ASC COLLATE latin1_swedish_ci, (2|6) ASC, (3|7) ASC COLLATE latin1_swedish_ci",
-    "Query": "select a, textcol1, b, textcol2, weight_string(a), weight_string(textcol1), weight_string(b), weight_string(textcol2) from `user` order by a asc, textcol1 asc, b asc, textcol2 asc",
+    "FieldQuery": "select a, textcol1, b, textcol2, weight_string(a), weight_string(b) from `user` where 1 != 1",
+    "OrderBy": "(0|4) ASC, 1 ASC COLLATE latin1_swedish_ci, (2|5) ASC, 3 ASC COLLATE latin1_swedish_ci",
+    "Query": "select a, textcol1, b, textcol2, weight_string(a), weight_string(b) from `user` order by a asc, textcol1 asc, b asc, textcol2 asc",
     "ResultColumns": 4,
     "Table": "`user`"
   }
@@ -704,7 +703,7 @@ Gen4 plan same as above
           "Sharded": true
         },
         "FieldQuery": "select `user`.id, `user`.col1 as a, `user`.col2 from `user` where 1 != 1",
-        "Query": "select `user`.id, `user`.col1 as a, `user`.col2 from `user` where `user`.id = 1 order by null",
+        "Query": "select `user`.id, `user`.col1 as a, `user`.col2 from `user` where `user`.id = 1",
         "Table": "`user`",
         "Values": [
           "INT64(1)"
@@ -719,7 +718,7 @@ Gen4 plan same as above
           "Sharded": true
         },
         "FieldQuery": "select music.col3 from music where 1 != 1",
-        "Query": "select music.col3 from music where music.id = :user_id order by null",
+        "Query": "select music.col3 from music where music.id = :user_id",
         "Table": "music",
         "Values": [
           ":user_id"
@@ -956,7 +955,42 @@ Gen4 plan same as above
     ]
   }
 }
-Gen4 plan same as above
+{
+  "QueryType": "SELECT",
+  "Original": "select col from user where col in (select col2 from user) order by null",
+  "Instructions": {
+    "OperatorType": "Subquery",
+    "Variant": "PulloutIn",
+    "PulloutVars": [
+      "__sq_has_values1",
+      "__sq1"
+    ],
+    "Inputs": [
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select col2 from `user` where 1 != 1",
+        "Query": "select col2 from `user`",
+        "Table": "`user`"
+      },
+      {
+        "OperatorType": "Route",
+        "Variant": "Scatter",
+        "Keyspace": {
+          "Name": "user",
+          "Sharded": true
+        },
+        "FieldQuery": "select col from `user` where 1 != 1",
+        "Query": "select col from `user` where :__sq_has_values1 = 1 and col in ::__sq1",
+        "Table": "`user`"
+      }
+    ]
+  }
+}
 
 # ORDER BY RAND()
 "select col from user order by RAND()"
@@ -1889,7 +1923,7 @@ Gen4 error: In aggregated query without GROUP BY, expression of SELECT list cont
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Ordered",
-    "Aggregates": "count(0) AS count(id)",
+    "Aggregates": "sum(0) AS count(id)",
     "GroupBy": "(1|2)",
     "ResultColumns": 2,
     "Inputs": [
@@ -1955,7 +1989,7 @@ Gen4 error: In aggregated query without GROUP BY, expression of SELECT list cont
       {
         "OperatorType": "Aggregate",
         "Variant": "Ordered",
-        "Aggregates": "count(0) AS count(id)",
+        "Aggregates": "sum(0) AS count(id)",
         "GroupBy": "(1|2)",
         "Inputs": [
           {
@@ -2169,7 +2203,7 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
           "Sharded": true
         },
         "FieldQuery": "select a as c, a, weight_string(a) from `user` where 1 != 1",
-        "OrderBy": "(0|2) ASC, (1|2) ASC",
+        "OrderBy": "(0|2) ASC, (0|2) ASC",
         "Query": "select distinct a as c, a, weight_string(a) from `user` order by c asc, a asc",
         "Table": "`user`"
       }
@@ -2237,7 +2271,7 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS a",
+        "Aggregates": "sum(0) AS a",
         "Inputs": [
           {
             "OperatorType": "Route",
diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
index 55ce84405e..4f4281a9b2 100644
--- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
@@ -106,7 +106,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Scalar",
-    "Aggregates": "count(0) AS count(*)",
+    "Aggregates": "sum(0) AS count(*)",
     "Inputs": [
       {
         "OperatorType": "Route",
@@ -201,7 +201,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Scalar",
-    "Aggregates": "count(0) AS count(*)",
+    "Aggregates": "sum(0) AS count(*)",
     "Inputs": [
       {
         "OperatorType": "Route",
@@ -250,7 +250,7 @@ Gen4 plan same as above
   "Instructions": {
     "OperatorType": "Aggregate",
     "Variant": "Scalar",
-    "Aggregates": "count(0) AS count(*)",
+    "Aggregates": "sum(0) AS count(*)",
     "Inputs": [
       {
         "OperatorType": "Route",
@@ -557,11 +557,8 @@ Gen4 plan same as above
   "Original": "select database() from dual",
   "Instructions": {
     "OperatorType": "Projection",
-    "Columns": [
-      "database()"
-    ],
     "Expressions": [
-      ":__vtdbname"
+      ":__vtdbname as database()"
     ],
     "Inputs": [
       {
@@ -1660,11 +1657,8 @@ Gen4 plan same as above
   "Original": "select 42",
   "Instructions": {
     "OperatorType": "Projection",
-    "Columns": [
-      "42"
-    ],
     "Expressions": [
-      "INT64(42)"
+      "INT64(42) as 42"
     ],
     "Inputs": [
       {
@@ -1701,11 +1695,8 @@ Gen4 plan same as above
   "Original": "select 42+2",
   "Instructions": {
     "OperatorType": "Projection",
-    "Columns": [
-      "42 + 2"
-    ],
     "Expressions": [
-      "INT64(44)"
+      "INT64(44) as 42 + 2"
     ],
     "Inputs": [
       {
@@ -1811,7 +1802,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS count(*)",
+        "Aggregates": "sum(0) AS count(*)",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -1949,7 +1940,7 @@ Gen4 plan same as above
       {
         "OperatorType": "Aggregate",
         "Variant": "Scalar",
-        "Aggregates": "count(0) AS count(*)",
+        "Aggregates": "sum(0) AS count(*)",
         "Inputs": [
           {
             "OperatorType": "Route",
@@ -2149,11 +2140,8 @@ Gen4 plan same as above
   "Original": "((((select 1))))",
   "Instructions": {
     "OperatorType": "Projection",
-    "Columns": [
-      "1"
-    ],
     "Expressions": [
-      "INT64(1)"
+      "INT64(1) as 1"
     ],
     "Inputs": [
       {
@@ -2530,11 +2518,8 @@ Gen4 plan same as above
   "Original": "select N'string' from dual",
   "Instructions": {
     "OperatorType": "Projection",
-    "Columns": [
-      "N'string'"
-    ],
     "Expressions": [
-      "VARCHAR(\"string\")"
+      "VARCHAR(\"string\") as N'string'"
     ],
     "Inputs": [
       {
diff --git a/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt b/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt
index 03438c7e4d..51dd2cb381 100644
--- a/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt
@@ -701,7 +701,7 @@ Gen4 plan same as above
       "Sharded": false
     },
     "FieldQuery": "select * from INFORMATION_SCHEMA.`TABLES` where 1 != 1",
-    "Query": "select * from INFORMATION_SCHEMA.`TABLES` where TABLE_SCHEMA = :__vtschemaname and TABLE_SCHEMA = :__vtschemaname",
+    "Query": "select * from INFORMATION_SCHEMA.`TABLES` where TABLE_SCHEMA = :__vtschemaname",
     "SysTableTableSchema": "[VARCHAR(\"user\"), VARCHAR(\"main\")]",
     "Table": "INFORMATION_SCHEMA.`TABLES`"
   }
@@ -1061,7 +1061,7 @@ Gen4 plan same as above
           "Sharded": false
         },
         "FieldQuery": "select 1 as found from information_schema.`tables` where 1 != 1",
-        "Query": "select 1 as found from information_schema.`tables` where table_schema = :__vtschemaname and table_schema = :__vtschemaname",
+        "Query": "select 1 as found from information_schema.`tables` where table_schema = :__vtschemaname",
         "SysTableTableSchema": "[VARCHAR(\"music\"), VARCHAR(\"Music\")]",
         "Table": "information_schema.`tables`"
       },
@@ -1073,7 +1073,7 @@ Gen4 plan same as above
           "Sharded": false
         },
         "FieldQuery": "select 1 as found from information_schema.views where 1 != 1",
-        "Query": "select 1 as found from information_schema.views where table_schema = :__vtschemaname and table_schema = :__vtschemaname limit 1",
+        "Query": "select 1 as found from information_schema.views where table_schema = :__vtschemaname limit 1",
         "SysTableTableSchema": "[VARCHAR(\"music\"), VARCHAR(\"user\")]",
         "Table": "information_schema.views"
       }
@@ -1091,7 +1091,7 @@ Gen4 plan same as above
       "Sharded": false
     },
     "FieldQuery": "select 1 as found from information_schema.`tables` where 1 != 1 union all (select 1 as found from information_schema.views where 1 != 1)",
-    "Query": "select 1 as found from information_schema.`tables` where table_schema = :__vtschemaname and table_schema = :__vtschemaname union all (select 1 as found from information_schema.views where table_schema = :__vtschemaname and table_schema = :__vtschemaname limit 1)",
+    "Query": "select 1 as found from information_schema.`tables` where table_schema = :__vtschemaname union all (select 1 as found from information_schema.views where table_schema = :__vtschemaname limit 1)",
     "SysTableTableSchema": "[VARCHAR(\"music\"), VARCHAR(\"Music\"), VARCHAR(\"music\"), VARCHAR(\"user\")]",
     "Table": "information_schema.`tables`"
   }
@@ -1113,7 +1113,7 @@ Gen4 plan same as above
           "Sharded": false
         },
         "FieldQuery": "select 1 as found from information_schema.`tables` where 1 != 1",
-        "Query": "select 1 as found from information_schema.`tables` where table_schema = :__vtschemaname and table_schema = :__vtschemaname",
+        "Query": "select 1 as found from information_schema.`tables` where table_schema = :__vtschemaname",
         "SysTableTableSchema": "[VARCHAR(\"music\"), VARCHAR(\"Music\")]",
         "Table": "information_schema.`tables`"
       },
@@ -1125,7 +1125,7 @@ Gen4 plan same as above
           "Sharded": false
         },
         "FieldQuery": "select 1 as found from information_schema.views where 1 != 1",
-        "Query": "select 1 as found from information_schema.views where table_schema = :__vtschemaname and table_schema = :__vtschemaname limit 1",
+        "Query": "select 1 as found from information_schema.views where table_schema = :__vtschemaname limit 1",
         "SysTableTableSchema": "[VARCHAR(\"music\"), VARCHAR(\"user\")]",
         "Table": "information_schema.views"
       }
@@ -1143,7 +1143,7 @@ Gen4 plan same as above
       "Sharded": false
     },
     "FieldQuery": "select 1 as found from information_schema.`tables` where 1 != 1 union all (select 1 as found from information_schema.views where 1 != 1)",
-    "Query": "select 1 as found from information_schema.`tables` where table_schema = :__vtschemaname and table_schema = :__vtschemaname union all (select 1 as found from information_schema.views where table_schema = :__vtschemaname and table_schema = :__vtschemaname limit 1)",
+    "Query": "select 1 as found from information_schema.`tables` where table_schema = :__vtschemaname union all (select 1 as found from information_schema.views where table_schema = :__vtschemaname limit 1)",
     "SysTableTableSchema": "[VARCHAR(\"music\"), VARCHAR(\"Music\"), VARCHAR(\"music\"), VARCHAR(\"user\")]",
     "Table": "information_schema.`tables`"
   }
diff --git a/go/vt/vtgate/planbuilder/testdata/tpch_cases.txt b/go/vt/vtgate/planbuilder/testdata/tpch_cases.txt
index 2c8971bb99..63a231c366 100644
--- a/go/vt/vtgate/planbuilder/testdata/tpch_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/tpch_cases.txt
@@ -11,17 +11,362 @@ Gen4 error: unsupported: cross-shard correlated subquery
 # TPC-H query 3
 "select l_orderkey, sum(l_extendedprice * (1 - l_discount)) as revenue, o_orderdate, o_shippriority from customer, orders, lineitem where c_mktsegment = 'BUILDING' and c_custkey = o_custkey and l_orderkey = o_orderkey and o_orderdate < date('1995-03-15') and l_shipdate > date('1995-03-15') group by l_orderkey, o_orderdate, o_shippriority order by revenue desc, o_orderdate limit 10"
 "unsupported: cross-shard query with aggregates"
-Gen4 plan same as above
+{
+  "QueryType": "SELECT",
+  "Original": "select l_orderkey, sum(l_extendedprice * (1 - l_discount)) as revenue, o_orderdate, o_shippriority from customer, orders, lineitem where c_mktsegment = 'BUILDING' and c_custkey = o_custkey and l_orderkey = o_orderkey and o_orderdate \u003c date('1995-03-15') and l_shipdate \u003e date('1995-03-15') group by l_orderkey, o_orderdate, o_shippriority order by revenue desc, o_orderdate limit 10",
+  "Instructions": {
+    "OperatorType": "Limit",
+    "Count": "INT64(10)",
+    "Inputs": [
+      {
+        "OperatorType": "Sort",
+        "Variant": "Memory",
+        "OrderBy": "1 DESC, (2|5) ASC",
+        "ResultColumns": 4,
+        "Inputs": [
+          {
+            "OperatorType": "Aggregate",
+            "Variant": "Ordered",
+            "Aggregates": "sum(1) AS revenue",
+            "GroupBy": "(0|6), (2|5), (3|4)",
+            "Inputs": [
+              {
+                "OperatorType": "Projection",
+                "Expressions": [
+                  "[COLUMN 0]",
+                  "([COLUMN 6] * [COLUMN 7]) * [COLUMN 8] as revenue",
+                  "[COLUMN 1]",
+                  "[COLUMN 2]",
+                  "[COLUMN 5]",
+                  "[COLUMN 4]",
+                  "[COLUMN 3]"
+                ],
+                "Inputs": [
+                  {
+                    "OperatorType": "Sort",
+                    "Variant": "Memory",
+                    "OrderBy": "(0|3) ASC, (1|4) ASC, (2|5) ASC",
+                    "Inputs": [
+                      {
+                        "OperatorType": "Join",
+                        "Variant": "Join",
+                        "JoinColumnIndexes": "-1,1,2,-3,3,4,-2,5,6",
+                        "JoinVars": {
+                          "l_orderkey": 0
+                        },
+                        "TableName": "lineitem_orders_customer",
+                        "Inputs": [
+                          {
+                            "OperatorType": "Route",
+                            "Variant": "Scatter",
+                            "Keyspace": {
+                              "Name": "main",
+                              "Sharded": true
+                            },
+                            "FieldQuery": "select l_orderkey, sum(l_extendedprice * (1 - l_discount)) as revenue, weight_string(l_orderkey) from lineitem where 1 != 1 group by l_orderkey, weight_string(l_orderkey)",
+                            "Query": "select l_orderkey, sum(l_extendedprice * (1 - l_discount)) as revenue, weight_string(l_orderkey) from lineitem where l_shipdate \u003e date('1995-03-15') group by l_orderkey, weight_string(l_orderkey)",
+                            "Table": "lineitem"
+                          },
+                          {
+                            "OperatorType": "Join",
+                            "Variant": "Join",
+                            "JoinColumnIndexes": "-4,-6,-5,-7,-2,1",
+                            "JoinVars": {
+                              "o_custkey": 0
+                            },
+                            "TableName": "orders_customer",
+                            "Inputs": [
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select o_custkey, count(*), weight_string(o_custkey), o_orderdate, weight_string(o_orderdate), o_shippriority, weight_string(o_shippriority) from orders where 1 != 1 group by o_custkey, weight_string(o_custkey), o_orderdate, weight_string(o_orderdate), o_shippriority, weight_string(o_shippriority)",
+                                "Query": "select o_custkey, count(*), weight_string(o_custkey), o_orderdate, weight_string(o_orderdate), o_shippriority, weight_string(o_shippriority) from orders where o_orderdate \u003c date('1995-03-15') and o_orderkey = :l_orderkey group by o_custkey, weight_string(o_custkey), o_orderdate, weight_string(o_orderdate), o_shippriority, weight_string(o_shippriority)",
+                                "Table": "orders",
+                                "Values": [
+                                  ":l_orderkey"
+                                ],
+                                "Vindex": "hash"
+                              },
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select count(*) from customer where 1 != 1",
+                                "Query": "select count(*) from customer where c_mktsegment = 'BUILDING' and c_custkey = :o_custkey",
+                                "Table": "customer",
+                                "Values": [
+                                  ":o_custkey"
+                                ],
+                                "Vindex": "hash"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
 
 # TPC-H query 4
 "select o_orderpriority, count(*) as order_count from orders where o_orderdate >= date('1993-07-01') and o_orderdate < date('1993-07-01') + interval '3' month and exists ( select * from lineitem where l_orderkey = o_orderkey and l_commitdate < l_receiptdate ) group by o_orderpriority order by o_orderpriority"
 "symbol o_orderkey not found in table or subquery"
-Gen4 error: unsupported: group by in a query having a correlated subquery
+{
+  "QueryType": "SELECT",
+  "Original": "select o_orderpriority, count(*) as order_count from orders where o_orderdate \u003e= date('1993-07-01') and o_orderdate \u003c date('1993-07-01') + interval '3' month and exists ( select * from lineitem where l_orderkey = o_orderkey and l_commitdate \u003c l_receiptdate ) group by o_orderpriority order by o_orderpriority",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "sum(1) AS order_count",
+    "GroupBy": "(0|2)",
+    "ResultColumns": 2,
+    "Inputs": [
+      {
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 1]",
+          "[COLUMN 2] as order_count",
+          "[COLUMN 3]"
+        ],
+        "Inputs": [
+          {
+            "OperatorType": "SemiJoin",
+            "JoinVars": {
+              "o_orderkey": 0
+            },
+            "TableName": "orders_lineitem",
+            "Inputs": [
+              {
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "main",
+                  "Sharded": true
+                },
+                "FieldQuery": "select o_orderkey, o_orderpriority, count(*) as order_count, weight_string(o_orderpriority), weight_string(o_orderkey) from orders where 1 != 1 group by o_orderpriority, weight_string(o_orderpriority), o_orderkey, weight_string(o_orderkey)",
+                "OrderBy": "(1|3) ASC",
+                "Query": "select o_orderkey, o_orderpriority, count(*) as order_count, weight_string(o_orderpriority), weight_string(o_orderkey) from orders where o_orderdate \u003e= date('1993-07-01') and o_orderdate \u003c date('1993-07-01') + interval '3' month group by o_orderpriority, weight_string(o_orderpriority), o_orderkey, weight_string(o_orderkey) order by o_orderpriority asc",
+                "Table": "orders"
+              },
+              {
+                "OperatorType": "Route",
+                "Variant": "EqualUnique",
+                "Keyspace": {
+                  "Name": "main",
+                  "Sharded": true
+                },
+                "FieldQuery": "select 1 from lineitem where 1 != 1",
+                "Query": "select 1 from lineitem where l_commitdate \u003c l_receiptdate and l_orderkey = :o_orderkey",
+                "Table": "lineitem",
+                "Values": [
+                  ":o_orderkey"
+                ],
+                "Vindex": "lineitem_map"
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
 
 # TPC-H query 5 - Gen4 produces plan but the plan output is flaky
 "select n_name, sum(l_extendedprice * (1 - l_discount)) as revenue from customer, orders, lineitem, supplier, nation, region where c_custkey = o_custkey and l_orderkey = o_orderkey and l_suppkey = s_suppkey and c_nationkey = s_nationkey and s_nationkey = n_nationkey and n_regionkey = r_regionkey and r_name = 'ASIA' and o_orderdate >= date('1994-01-01') and o_orderdate < date('1994-01-01') + interval '1' year group by n_name order by revenue desc"
 "unsupported: cross-shard query with aggregates"
-Gen4 plan same as above
+{
+  "QueryType": "SELECT",
+  "Original": "select n_name, sum(l_extendedprice * (1 - l_discount)) as revenue from customer, orders, lineitem, supplier, nation, region where c_custkey = o_custkey and l_orderkey = o_orderkey and l_suppkey = s_suppkey and c_nationkey = s_nationkey and s_nationkey = n_nationkey and n_regionkey = r_regionkey and r_name = 'ASIA' and o_orderdate \u003e= date('1994-01-01') and o_orderdate \u003c date('1994-01-01') + interval '1' year group by n_name order by revenue desc",
+  "Instructions": {
+    "OperatorType": "Sort",
+    "Variant": "Memory",
+    "OrderBy": "1 DESC",
+    "ResultColumns": 2,
+    "Inputs": [
+      {
+        "OperatorType": "Aggregate",
+        "Variant": "Ordered",
+        "Aggregates": "sum(1) AS revenue",
+        "GroupBy": "(0|2)",
+        "Inputs": [
+          {
+            "OperatorType": "Projection",
+            "Expressions": [
+              "[COLUMN 0]",
+              "(((([COLUMN 2] * [COLUMN 3]) * [COLUMN 4]) * [COLUMN 5]) * [COLUMN 6]) * [COLUMN 7] as revenue",
+              "[COLUMN 1]"
+            ],
+            "Inputs": [
+              {
+                "OperatorType": "Sort",
+                "Variant": "Memory",
+                "OrderBy": "(0|1) ASC",
+                "Inputs": [
+                  {
+                    "OperatorType": "Join",
+                    "Variant": "Join",
+                    "JoinColumnIndexes": "1,2,-4,-5,-6,-7,3,4",
+                    "JoinVars": {
+                      "s_nationkey": 0
+                    },
+                    "TableName": "orders_customer_lineitem_supplier_nation_region",
+                    "Inputs": [
+                      {
+                        "OperatorType": "Join",
+                        "Variant": "Join",
+                        "JoinColumnIndexes": "1,2,3,-7,-8,4,5",
+                        "JoinVars": {
+                          "c_nationkey": 1,
+                          "o_orderkey": 0
+                        },
+                        "TableName": "orders_customer_lineitem_supplier",
+                        "Inputs": [
+                          {
+                            "OperatorType": "Join",
+                            "Variant": "Join",
+                            "JoinColumnIndexes": "-2,1,-2,1,-5,3,-3,2",
+                            "JoinVars": {
+                              "o_custkey": 0
+                            },
+                            "TableName": "orders_customer",
+                            "Inputs": [
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "Scatter",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select o_custkey, o_orderkey, count(*), weight_string(o_custkey), weight_string(o_orderkey) from orders where 1 != 1 group by o_custkey, weight_string(o_custkey), o_orderkey, weight_string(o_orderkey)",
+                                "Query": "select o_custkey, o_orderkey, count(*), weight_string(o_custkey), weight_string(o_orderkey) from orders where o_orderdate \u003e= date('1994-01-01') and o_orderdate \u003c date('1994-01-01') + interval '1' year group by o_custkey, weight_string(o_custkey), o_orderkey, weight_string(o_orderkey)",
+                                "Table": "orders"
+                              },
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select c_nationkey, count(*), weight_string(c_nationkey) from customer where 1 != 1 group by c_nationkey, weight_string(c_nationkey)",
+                                "Query": "select c_nationkey, count(*), weight_string(c_nationkey) from customer where c_custkey = :o_custkey group by c_nationkey, weight_string(c_nationkey)",
+                                "Table": "customer",
+                                "Values": [
+                                  ":o_custkey"
+                                ],
+                                "Vindex": "hash"
+                              }
+                            ]
+                          },
+                          {
+                            "OperatorType": "Join",
+                            "Variant": "Join",
+                            "JoinColumnIndexes": "1,1,3,-2,2",
+                            "JoinVars": {
+                              "l_suppkey": 0
+                            },
+                            "TableName": "lineitem_supplier",
+                            "Inputs": [
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select l_suppkey, sum(l_extendedprice * (1 - l_discount)) as revenue, weight_string(l_suppkey) from lineitem where 1 != 1 group by l_suppkey, weight_string(l_suppkey)",
+                                "Query": "select l_suppkey, sum(l_extendedprice * (1 - l_discount)) as revenue, weight_string(l_suppkey) from lineitem where l_orderkey = :o_orderkey group by l_suppkey, weight_string(l_suppkey)",
+                                "Table": "lineitem",
+                                "Values": [
+                                  ":o_orderkey"
+                                ],
+                                "Vindex": "lineitem_map"
+                              },
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select s_nationkey, count(*), weight_string(s_nationkey) from supplier where 1 != 1 group by s_nationkey, weight_string(s_nationkey)",
+                                "Query": "select s_nationkey, count(*), weight_string(s_nationkey) from supplier where s_suppkey = :l_suppkey and s_nationkey = :c_nationkey group by s_nationkey, weight_string(s_nationkey)",
+                                "Table": "supplier",
+                                "Values": [
+                                  ":l_suppkey"
+                                ],
+                                "Vindex": "hash"
+                              }
+                            ]
+                          }
+                        ]
+                      },
+                      {
+                        "OperatorType": "Join",
+                        "Variant": "Join",
+                        "JoinColumnIndexes": "-4,-5,-2,1",
+                        "JoinVars": {
+                          "n_regionkey": 0
+                        },
+                        "TableName": "nation_region",
+                        "Inputs": [
+                          {
+                            "OperatorType": "Route",
+                            "Variant": "EqualUnique",
+                            "Keyspace": {
+                              "Name": "main",
+                              "Sharded": true
+                            },
+                            "FieldQuery": "select n_regionkey, count(*), weight_string(n_regionkey), n_name, weight_string(n_name) from nation where 1 != 1 group by n_regionkey, weight_string(n_regionkey), n_name, weight_string(n_name)",
+                            "Query": "select n_regionkey, count(*), weight_string(n_regionkey), n_name, weight_string(n_name) from nation where n_nationkey = :s_nationkey group by n_regionkey, weight_string(n_regionkey), n_name, weight_string(n_name)",
+                            "Table": "nation",
+                            "Values": [
+                              ":s_nationkey"
+                            ],
+                            "Vindex": "hash"
+                          },
+                          {
+                            "OperatorType": "Route",
+                            "Variant": "EqualUnique",
+                            "Keyspace": {
+                              "Name": "main",
+                              "Sharded": true
+                            },
+                            "FieldQuery": "select count(*) from region where 1 != 1",
+                            "Query": "select count(*) from region where r_name = 'ASIA' and r_regionkey = :n_regionkey",
+                            "Table": "region",
+                            "Values": [
+                              ":n_regionkey"
+                            ],
+                            "Vindex": "hash"
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
 
 # TPC-H query 6
 "select sum(l_extendedprice * l_discount) as revenue from lineitem where l_shipdate >= date('1994-01-01') and l_shipdate < date('1994-01-01') + interval '1' year and l_discount between 0.06 - 0.01 and 0.06 + 0.01 and l_quantity < 24"
@@ -73,7 +418,7 @@ Gen4 plan same as above
 # TPC-H query 7
 "select supp_nation, cust_nation, l_year, sum(volume) as revenue from (select n1.n_name as supp_nation, n2.n_name as cust_nation, extract(year from l_shipdate) as l_year, l_extendedprice * (1 - l_discount) as volume from supplier, lineitem, orders, customer, nation n1, nation n2 where s_suppkey = l_suppkey and o_orderkey = l_orderkey and c_custkey = o_custkey and s_nationkey = n1.n_nationkey and c_nationkey = n2.n_nationkey and ((n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY') or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE')) and l_shipdate between date('1995-01-01') and date('1996-12-31')) as shipping group by supp_nation, cust_nation, l_year order by supp_nation, cust_nation, l_year"
 "unsupported: cross-shard query with aggregates"
-Gen4 error: unsupported: group by on: *planbuilder.simpleProjection
+Gen4 error: using aggregation on top of a *planbuilder.simpleProjection plan is not yet supported
 
 # TPC-H query 8
 "select o_year, sum(case when nation = 'BRAZIL' then volume else 0 end) / sum(volume) as mkt_share from ( select extract(year from o_orderdate) as o_year, l_extendedprice * (1 - l_discount) as volume, n2.n_name as nation from part, supplier, lineitem, orders, customer, nation n1, nation n2, region where p_partkey = l_partkey and s_suppkey = l_suppkey and l_orderkey = o_orderkey and o_custkey = c_custkey and c_nationkey = n1.n_nationkey and n1.n_regionkey = r_regionkey and r_name = 'AMERICA' and s_nationkey = n2.n_nationkey and o_orderdate between date '1995-01-01' and date('1996-12-31') and p_type = 'ECONOMY ANODIZED STEEL' ) as all_nations group by o_year order by o_year"
@@ -83,12 +428,155 @@ Gen4 plan same as above
 # TPC-H query 9
 "select nation, o_year, sum(amount) as sum_profit from ( select n_name as nation, extract(year from o_orderdate) as o_year, l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity as amount from part, supplier, lineitem, partsupp, orders, nation where s_suppkey = l_suppkey and ps_suppkey = l_suppkey and ps_partkey = l_partkey and p_partkey = l_partkey and o_orderkey = l_orderkey and s_nationkey = n_nationkey and p_name like '%green%' ) as profit group by nation, o_year order by nation, o_year desc"
 "unsupported: cross-shard query with aggregates"
-Gen4 plan same as above
+Gen4 error: using aggregation on top of a *planbuilder.simpleProjection plan is not yet supported
 
 # TPC-H query 10
 "select c_custkey, c_name, sum(l_extendedprice * (1 - l_discount)) as revenue, c_acctbal, n_name, c_address, c_phone, c_comment from customer, orders, lineitem, nation where c_custkey = o_custkey and l_orderkey = o_orderkey and o_orderdate >= date('1993-10-01') and o_orderdate < date('1993-10-01') + interval '3' month and l_returnflag = 'R' and c_nationkey = n_nationkey group by c_custkey, c_name, c_acctbal, c_phone, n_name, c_address, c_comment order by revenue desc limit 20"
 "unsupported: cross-shard query with aggregates"
-Gen4 plan same as above
+{
+  "QueryType": "SELECT",
+  "Original": "select c_custkey, c_name, sum(l_extendedprice * (1 - l_discount)) as revenue, c_acctbal, n_name, c_address, c_phone, c_comment from customer, orders, lineitem, nation where c_custkey = o_custkey and l_orderkey = o_orderkey and o_orderdate \u003e= date('1993-10-01') and o_orderdate \u003c date('1993-10-01') + interval '3' month and l_returnflag = 'R' and c_nationkey = n_nationkey group by c_custkey, c_name, c_acctbal, c_phone, n_name, c_address, c_comment order by revenue desc limit 20",
+  "Instructions": {
+    "OperatorType": "Limit",
+    "Count": "INT64(20)",
+    "Inputs": [
+      {
+        "OperatorType": "Sort",
+        "Variant": "Memory",
+        "OrderBy": "2 DESC",
+        "ResultColumns": 8,
+        "Inputs": [
+          {
+            "OperatorType": "Aggregate",
+            "Variant": "Ordered",
+            "Aggregates": "sum(2) AS revenue",
+            "GroupBy": "(0|14), (1|13), (3|12), (6|11), (4|10), (5|9), (7|8)",
+            "Inputs": [
+              {
+                "OperatorType": "Projection",
+                "Expressions": [
+                  "[COLUMN 0]",
+                  "[COLUMN 1]",
+                  "(([COLUMN 14] * [COLUMN 15]) * [COLUMN 16]) * [COLUMN 17] as revenue",
+                  "[COLUMN 2]",
+                  "[COLUMN 4]",
+                  "[COLUMN 5]",
+                  "[COLUMN 3]",
+                  "[COLUMN 6]",
+                  "[COLUMN 13]",
+                  "[COLUMN 12]",
+                  "[COLUMN 11]",
+                  "[COLUMN 10]",
+                  "[COLUMN 9]",
+                  "[COLUMN 8]",
+                  "[COLUMN 7]"
+                ],
+                "Inputs": [
+                  {
+                    "OperatorType": "Sort",
+                    "Variant": "Memory",
+                    "OrderBy": "(0|7) ASC, (1|8) ASC, (2|9) ASC, (3|10) ASC, (4|11) ASC, (5|12) ASC, (6|13) ASC",
+                    "Inputs": [
+                      {
+                        "OperatorType": "Join",
+                        "Variant": "Join",
+                        "JoinColumnIndexes": "1,2,3,4,5,6,7,8,9,10,11,12,13,14,-4,-5,15,16",
+                        "JoinVars": {
+                          "o_custkey": 0
+                        },
+                        "TableName": "orders_lineitem_customer_nation",
+                        "Inputs": [
+                          {
+                            "OperatorType": "Join",
+                            "Variant": "Join",
+                            "JoinColumnIndexes": "-2,-2,-5,-3,1",
+                            "JoinVars": {
+                              "o_orderkey": 0
+                            },
+                            "TableName": "orders_lineitem",
+                            "Inputs": [
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "Scatter",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select o_orderkey, o_custkey, count(*), weight_string(o_orderkey), weight_string(o_custkey) from orders where 1 != 1 group by o_orderkey, weight_string(o_orderkey), o_custkey, weight_string(o_custkey)",
+                                "Query": "select o_orderkey, o_custkey, count(*), weight_string(o_orderkey), weight_string(o_custkey) from orders where o_orderdate \u003e= date('1993-10-01') and o_orderdate \u003c date('1993-10-01') + interval '3' month group by o_orderkey, weight_string(o_orderkey), o_custkey, weight_string(o_custkey)",
+                                "Table": "orders"
+                              },
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select sum(l_extendedprice * (1 - l_discount)) as revenue from lineitem where 1 != 1",
+                                "Query": "select sum(l_extendedprice * (1 - l_discount)) as revenue from lineitem where l_returnflag = 'R' and l_orderkey = :o_orderkey",
+                                "Table": "lineitem",
+                                "Values": [
+                                  ":o_orderkey"
+                                ],
+                                "Vindex": "lineitem_map"
+                              }
+                            ]
+                          },
+                          {
+                            "OperatorType": "Join",
+                            "Variant": "Join",
+                            "JoinColumnIndexes": "-4,-6,-8,-10,2,-12,-14,-5,-7,-9,-11,3,-13,-15,-2,1",
+                            "JoinVars": {
+                              "c_nationkey": 0
+                            },
+                            "TableName": "customer_nation",
+                            "Inputs": [
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select c_nationkey, count(*), weight_string(c_nationkey), c_custkey, weight_string(c_custkey), c_name, weight_string(c_name), c_acctbal, weight_string(c_acctbal), c_phone, weight_string(c_phone), c_address, weight_string(c_address), c_comment, weight_string(c_comment) from customer where 1 != 1 group by c_nationkey, weight_string(c_nationkey), c_custkey, weight_string(c_custkey), c_name, weight_string(c_name), c_acctbal, weight_string(c_acctbal), c_phone, weight_string(c_phone), c_address, weight_string(c_address), c_comment, weight_string(c_comment)",
+                                "Query": "select c_nationkey, count(*), weight_string(c_nationkey), c_custkey, weight_string(c_custkey), c_name, weight_string(c_name), c_acctbal, weight_string(c_acctbal), c_phone, weight_string(c_phone), c_address, weight_string(c_address), c_comment, weight_string(c_comment) from customer where c_custkey = :o_custkey group by c_nationkey, weight_string(c_nationkey), c_custkey, weight_string(c_custkey), c_name, weight_string(c_name), c_acctbal, weight_string(c_acctbal), c_phone, weight_string(c_phone), c_address, weight_string(c_address), c_comment, weight_string(c_comment)",
+                                "Table": "customer",
+                                "Values": [
+                                  ":o_custkey"
+                                ],
+                                "Vindex": "hash"
+                              },
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select count(*), n_name, weight_string(n_name) from nation where 1 != 1 group by n_name, weight_string(n_name)",
+                                "Query": "select count(*), n_name, weight_string(n_name) from nation where n_nationkey = :c_nationkey group by n_name, weight_string(n_name)",
+                                "Table": "nation",
+                                "Values": [
+                                  ":c_nationkey"
+                                ],
+                                "Vindex": "hash"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
 
 # TPC-H query 11
 "select ps_partkey, sum(ps_supplycost * ps_availqty) as value from partsupp, supplier, nation where ps_suppkey = s_suppkey and s_nationkey = n_nationkey and n_name = 'GERMANY' group by ps_partkey  having sum(ps_supplycost * ps_availqty) > ( select sum(ps_supplycost * ps_availqty) * 0.00001000000 from partsupp, supplier, nation where ps_suppkey = s_suppkey and s_nationkey = n_nationkey and n_name = 'GERMANY' ) order by value desc"
@@ -98,11 +586,78 @@ Gen4 error: unsupported: in scatter query: complex aggregate expression
 # TPC-H query 12
 "select l_shipmode, sum(case when o_orderpriority = '1-URGENT' or o_orderpriority = '2-HIGH' then 1 else 0 end) as high_line_count, sum(case when o_orderpriority <> '1-URGENT' and o_orderpriority <> '2-HIGH' then 1 else 0 end) as low_line_count from orders, lineitem where o_orderkey = l_orderkey and l_shipmode in ('MAIL', 'SHIP') and l_commitdate < l_receiptdate and l_shipdate < l_commitdate and l_receiptdate >= date('1994-01-01') and l_receiptdate < date('1994-01-01') + interval '1' year group by l_shipmode order by l_shipmode"
 "unsupported: cross-shard query with aggregates"
-Gen4 plan same as above
+{
+  "QueryType": "SELECT",
+  "Original": "select l_shipmode, sum(case when o_orderpriority = '1-URGENT' or o_orderpriority = '2-HIGH' then 1 else 0 end) as high_line_count, sum(case when o_orderpriority \u003c\u003e '1-URGENT' and o_orderpriority \u003c\u003e '2-HIGH' then 1 else 0 end) as low_line_count from orders, lineitem where o_orderkey = l_orderkey and l_shipmode in ('MAIL', 'SHIP') and l_commitdate \u003c l_receiptdate and l_shipdate \u003c l_commitdate and l_receiptdate \u003e= date('1994-01-01') and l_receiptdate \u003c date('1994-01-01') + interval '1' year group by l_shipmode order by l_shipmode",
+  "Instructions": {
+    "OperatorType": "Aggregate",
+    "Variant": "Ordered",
+    "Aggregates": "sum(1) AS high_line_count, sum(2) AS low_line_count",
+    "GroupBy": "(0|3)",
+    "ResultColumns": 3,
+    "Inputs": [
+      {
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 0]",
+          "[COLUMN 2] * [COLUMN 3] as high_line_count",
+          "[COLUMN 4] * [COLUMN 5] as low_line_count",
+          "[COLUMN 1]"
+        ],
+        "Inputs": [
+          {
+            "OperatorType": "Sort",
+            "Variant": "Memory",
+            "OrderBy": "(0|1) ASC",
+            "Inputs": [
+              {
+                "OperatorType": "Join",
+                "Variant": "Join",
+                "JoinColumnIndexes": "2,3,-2,1,-3,1",
+                "JoinVars": {
+                  "o_orderkey": 0
+                },
+                "TableName": "orders_lineitem",
+                "Inputs": [
+                  {
+                    "OperatorType": "Route",
+                    "Variant": "Scatter",
+                    "Keyspace": {
+                      "Name": "main",
+                      "Sharded": true
+                    },
+                    "FieldQuery": "select o_orderkey, sum(case when o_orderpriority = '1-URGENT' or o_orderpriority = '2-HIGH' then 1 else 0 end) as high_line_count, sum(case when o_orderpriority != '1-URGENT' and o_orderpriority != '2-HIGH' then 1 else 0 end) as low_line_count, weight_string(o_orderkey) from orders where 1 != 1 group by o_orderkey, weight_string(o_orderkey)",
+                    "Query": "select o_orderkey, sum(case when o_orderpriority = '1-URGENT' or o_orderpriority = '2-HIGH' then 1 else 0 end) as high_line_count, sum(case when o_orderpriority != '1-URGENT' and o_orderpriority != '2-HIGH' then 1 else 0 end) as low_line_count, weight_string(o_orderkey) from orders group by o_orderkey, weight_string(o_orderkey)",
+                    "Table": "orders"
+                  },
+                  {
+                    "OperatorType": "Route",
+                    "Variant": "EqualUnique",
+                    "Keyspace": {
+                      "Name": "main",
+                      "Sharded": true
+                    },
+                    "FieldQuery": "select count(*), l_shipmode, weight_string(l_shipmode) from lineitem where 1 != 1 group by l_shipmode, weight_string(l_shipmode)",
+                    "Query": "select count(*), l_shipmode, weight_string(l_shipmode) from lineitem where l_shipmode in ('MAIL', 'SHIP') and l_commitdate \u003c l_receiptdate and l_shipdate \u003c l_commitdate and l_receiptdate \u003e= date('1994-01-01') and l_receiptdate \u003c date('1994-01-01') + interval '1' year and l_orderkey = :o_orderkey group by l_shipmode, weight_string(l_shipmode)",
+                    "Table": "lineitem",
+                    "Values": [
+                      ":o_orderkey"
+                    ],
+                    "Vindex": "lineitem_map"
+                  }
+                ]
+              }
+            ]
+          }
+        ]
+      }
+    ]
+  }
+}
 
 # TPC-H query 13
 "select c_count, count(*) as custdist from ( select c_custkey, count(o_orderkey) from customer left outer join orders on c_custkey = o_custkey and o_comment not like '%special%requests%' group by c_custkey ) as c_orders(c_custkey, c_count) group by c_count order by custdist desc, c_count desc"
-"unsupported: cross-shard query with aggregates"
+"Expression of SELECT list is not in GROUP BY clause and contains nonaggregated column 'custdist' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by"
 Gen4 plan same as above
 
 # TPC-H query 14
@@ -236,91 +791,7 @@ Gen4 error: unsupported: in scatter query: complex aggregate expression
 # TPC-H query 16
 "select p_brand, p_type, p_size, count(distinct ps_suppkey) as supplier_cnt from partsupp, part where p_partkey = ps_partkey and p_brand <> 'Brand#45' and p_type not like 'MEDIUM POLISHED%' and p_size in (49, 14, 23, 45, 19, 3, 36, 9) and ps_suppkey not in ( select s_suppkey from supplier where s_comment like '%Customer%Complaints%' ) group by p_brand, p_type, p_size order by supplier_cnt desc, p_brand, p_type, p_size"
 "unsupported: cross-shard query with aggregates"
-{
-  "QueryType": "SELECT",
-  "Original": "select p_brand, p_type, p_size, count(distinct ps_suppkey) as supplier_cnt from partsupp, part where p_partkey = ps_partkey and p_brand \u003c\u003e 'Brand#45' and p_type not like 'MEDIUM POLISHED%' and p_size in (49, 14, 23, 45, 19, 3, 36, 9) and ps_suppkey not in ( select s_suppkey from supplier where s_comment like '%Customer%Complaints%' ) group by p_brand, p_type, p_size order by supplier_cnt desc, p_brand, p_type, p_size",
-  "Instructions": {
-    "OperatorType": "Sort",
-    "Variant": "Memory",
-    "OrderBy": "3 DESC, (0|4) ASC, (1|5) ASC, (2|6) ASC",
-    "ResultColumns": 4,
-    "Inputs": [
-      {
-        "OperatorType": "Aggregate",
-        "Variant": "Ordered",
-        "Aggregates": "count_distinct(3|7) AS supplier_cnt",
-        "GroupBy": "(0|4), (1|5), (2|6)",
-        "Inputs": [
-          {
-            "OperatorType": "Subquery",
-            "Variant": "PulloutNotIn",
-            "PulloutVars": [
-              "__sq_has_values1",
-              "__sq1"
-            ],
-            "Inputs": [
-              {
-                "OperatorType": "Route",
-                "Variant": "Scatter",
-                "Keyspace": {
-                  "Name": "main",
-                  "Sharded": true
-                },
-                "FieldQuery": "select s_suppkey from supplier where 1 != 1",
-                "Query": "select s_suppkey from supplier where s_comment like '%Customer%Complaints%'",
-                "Table": "supplier"
-              },
-              {
-                "OperatorType": "Sort",
-                "Variant": "Memory",
-                "OrderBy": "(0|4) ASC, (1|5) ASC, (2|6) ASC, (3|7) ASC",
-                "Inputs": [
-                  {
-                    "OperatorType": "Join",
-                    "Variant": "Join",
-                    "JoinColumnIndexes": "1,2,3,-2,4,5,6,-3",
-                    "JoinVars": {
-                      "ps_partkey": 0
-                    },
-                    "TableName": "partsupp_part",
-                    "Inputs": [
-                      {
-                        "OperatorType": "Route",
-                        "Variant": "Scatter",
-                        "Keyspace": {
-                          "Name": "main",
-                          "Sharded": true
-                        },
-                        "FieldQuery": "select ps_partkey, ps_suppkey, weight_string(ps_suppkey) from partsupp where 1 != 1",
-                        "Query": "select ps_partkey, ps_suppkey, weight_string(ps_suppkey) from partsupp where :__sq_has_values1 = 0 or ps_suppkey not in ::__sq1",
-                        "Table": "partsupp"
-                      },
-                      {
-                        "OperatorType": "Route",
-                        "Variant": "EqualUnique",
-                        "Keyspace": {
-                          "Name": "main",
-                          "Sharded": true
-                        },
-                        "FieldQuery": "select p_brand, p_type, p_size, weight_string(p_brand), weight_string(p_type), weight_string(p_size) from part where 1 != 1",
-                        "Query": "select p_brand, p_type, p_size, weight_string(p_brand), weight_string(p_type), weight_string(p_size) from part where p_brand != 'Brand#45' and p_type not like 'MEDIUM POLISHED%' and p_size in (49, 14, 23, 45, 19, 3, 36, 9) and p_partkey = :ps_partkey",
-                        "Table": "part",
-                        "Values": [
-                          ":ps_partkey"
-                        ],
-                        "Vindex": "hash"
-                      }
-                    ]
-                  }
-                ]
-              }
-            ]
-          }
-        ]
-      }
-    ]
-  }
-}
+Gen4 error: using aggregation on top of a *planbuilder.pulloutSubquery plan is not yet supported
 
 # TPC-H query 17
 "select sum(l_extendedprice) / 7.0 as avg_yearly from lineitem, part where p_partkey = l_partkey and p_brand = 'Brand#23' and p_container = 'MED BOX' and l_quantity < ( select 0.2 * avg(l_quantity) from lineitem where l_partkey = p_partkey )"
@@ -330,97 +801,58 @@ Gen4 error: unsupported: cross-shard correlated subquery
 # TPC-H query 18
 "select c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice, sum(l_quantity) from customer, orders, lineitem where o_orderkey in ( select l_orderkey from lineitem group by l_orderkey having sum(l_quantity) > 300 ) and c_custkey = o_custkey and o_orderkey = l_orderkey group by c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice order by o_totalprice desc, o_orderdate limit 100"
 "unsupported: cross-shard query with aggregates"
+Gen4 error: unsupported: group by on: *planbuilder.joinGen4
+
+# TPC-H query 19
+"select sum(l_extendedprice* (1 - l_discount)) as revenue from lineitem, part where ( p_partkey = l_partkey and p_brand = 'Brand#12' and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') and l_quantity >= 1 and l_quantity <= 1 + 10 and p_size between 1 and 5 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' ) or ( p_partkey = l_partkey and p_brand = 'Brand#23' and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') and l_quantity >= 10 and l_quantity <= 10 + 10 and p_size between 1 and 10 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' ) or ( p_partkey = l_partkey and p_brand = 'Brand#34' and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') and l_quantity >= 20 and l_quantity <= 20 + 10 and p_size between 1 and 15 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' )"
+"unsupported: cross-shard query with aggregates"
 {
   "QueryType": "SELECT",
-  "Original": "select c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice, sum(l_quantity) from customer, orders, lineitem where o_orderkey in ( select l_orderkey from lineitem group by l_orderkey having sum(l_quantity) \u003e 300 ) and c_custkey = o_custkey and o_orderkey = l_orderkey group by c_name, c_custkey, o_orderkey, o_orderdate, o_totalprice order by o_totalprice desc, o_orderdate limit 100",
+  "Original": "select sum(l_extendedprice* (1 - l_discount)) as revenue from lineitem, part where ( p_partkey = l_partkey and p_brand = 'Brand#12' and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') and l_quantity \u003e= 1 and l_quantity \u003c= 1 + 10 and p_size between 1 and 5 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' ) or ( p_partkey = l_partkey and p_brand = 'Brand#23' and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') and l_quantity \u003e= 10 and l_quantity \u003c= 10 + 10 and p_size between 1 and 10 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' ) or ( p_partkey = l_partkey and p_brand = 'Brand#34' and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') and l_quantity \u003e= 20 and l_quantity \u003c= 20 + 10 and p_size between 1 and 15 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' )",
   "Instructions": {
-    "OperatorType": "Limit",
-    "Count": "INT64(100)",
+    "OperatorType": "Aggregate",
+    "Variant": "Scalar",
+    "Aggregates": "sum(0) AS revenue",
     "Inputs": [
       {
-        "OperatorType": "Subquery",
-        "Variant": "PulloutIn",
-        "PulloutVars": [
-          "__sq_has_values1",
-          "__sq1"
+        "OperatorType": "Projection",
+        "Expressions": [
+          "[COLUMN 0] * [COLUMN 1] as revenue"
         ],
         "Inputs": [
-          {
-            "OperatorType": "Route",
-            "Variant": "Scatter",
-            "Keyspace": {
-              "Name": "main",
-              "Sharded": true
-            },
-            "FieldQuery": "select l_orderkey from lineitem where 1 != 1 group by l_orderkey",
-            "Query": "select l_orderkey from lineitem group by l_orderkey having sum(l_quantity) \u003e 300",
-            "Table": "lineitem"
-          },
           {
             "OperatorType": "Join",
             "Variant": "Join",
-            "JoinColumnIndexes": "-2,-3,-1,-4,-5,1,-6,-7,-8,-9,-10",
+            "JoinColumnIndexes": "-5,1",
             "JoinVars": {
-              "o_orderkey": 0
+              "l_partkey": 0,
+              "l_quantity": 1,
+              "l_shipinstruct": 3,
+              "l_shipmode": 2
             },
-            "TableName": "orders_customer_lineitem",
+            "TableName": "lineitem_part",
             "Inputs": [
               {
-                "OperatorType": "Join",
-                "Variant": "Join",
-                "JoinColumnIndexes": "-2,1,2,-3,-4,3,4,-5,-6,-7",
-                "JoinVars": {
-                  "o_custkey": 0
+                "OperatorType": "Route",
+                "Variant": "Scatter",
+                "Keyspace": {
+                  "Name": "main",
+                  "Sharded": true
                 },
-                "TableName": "orders_customer",
-                "Inputs": [
-                  {
-                    "OperatorType": "Route",
-                    "Variant": "IN",
-                    "Keyspace": {
-                      "Name": "main",
-                      "Sharded": true
-                    },
-                    "FieldQuery": "select o_custkey, o_orderkey, o_orderdate, o_totalprice, weight_string(o_orderkey), weight_string(o_orderdate), weight_string(o_totalprice) from orders where 1 != 1",
-                    "OrderBy": "(3|6) DESC, (2|5) ASC",
-                    "Query": "select o_custkey, o_orderkey, o_orderdate, o_totalprice, weight_string(o_orderkey), weight_string(o_orderdate), weight_string(o_totalprice) from orders where :__sq_has_values1 = 1 and o_orderkey in ::__vals order by o_totalprice desc, o_orderdate asc",
-                    "Table": "orders",
-                    "Values": [
-                      ":__sq1"
-                    ],
-                    "Vindex": "hash"
-                  },
-                  {
-                    "OperatorType": "Route",
-                    "Variant": "EqualUnique",
-                    "Keyspace": {
-                      "Name": "main",
-                      "Sharded": true
-                    },
-                    "FieldQuery": "select c_name, c_custkey, weight_string(c_name), weight_string(c_custkey) from customer where 1 != 1",
-                    "Query": "select c_name, c_custkey, weight_string(c_name), weight_string(c_custkey) from customer where c_custkey = :o_custkey",
-                    "Table": "customer",
-                    "Values": [
-                      ":o_custkey"
-                    ],
-                    "Vindex": "hash"
-                  }
-                ]
+                "FieldQuery": "select l_partkey, l_quantity, l_shipmode, l_shipinstruct, sum(l_extendedprice * (1 - l_discount)) as revenue, weight_string(l_partkey), weight_string(l_quantity), weight_string(l_shipmode), weight_string(l_shipinstruct) from lineitem where 1 != 1 group by l_partkey, weight_string(l_partkey), l_quantity, weight_string(l_quantity), l_shipmode, weight_string(l_shipmode), l_shipinstruct, weight_string(l_shipinstruct)",
+                "Query": "select l_partkey, l_quantity, l_shipmode, l_shipinstruct, sum(l_extendedprice * (1 - l_discount)) as revenue, weight_string(l_partkey), weight_string(l_quantity), weight_string(l_shipmode), weight_string(l_shipinstruct) from lineitem group by l_partkey, weight_string(l_partkey), l_quantity, weight_string(l_quantity), l_shipmode, weight_string(l_shipmode), l_shipinstruct, weight_string(l_shipinstruct)",
+                "Table": "lineitem"
               },
               {
                 "OperatorType": "Route",
-                "Variant": "EqualUnique",
+                "Variant": "Scatter",
                 "Keyspace": {
                   "Name": "main",
                   "Sharded": true
                 },
-                "FieldQuery": "select sum(l_quantity) from lineitem where 1 != 1",
-                "Query": "select sum(l_quantity) from lineitem where l_orderkey = :o_orderkey",
-                "Table": "lineitem",
-                "Values": [
-                  ":o_orderkey"
-                ],
-                "Vindex": "lineitem_map"
+                "FieldQuery": "select count(*) from part where 1 != 1",
+                "Query": "select count(*) from part where p_partkey = :l_partkey and p_brand = 'Brand#12' and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') and :l_quantity \u003e= 1 and :l_quantity \u003c= 1 + 10 and p_size between 1 and 5 and :l_shipmode in ('AIR', 'AIR REG') and :l_shipinstruct = 'DELIVER IN PERSON' or p_partkey = :l_partkey and p_brand = 'Brand#23' and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') and :l_quantity \u003e= 10 and :l_quantity \u003c= 10 + 10 and p_size between 1 and 10 and :l_shipmode in ('AIR', 'AIR REG') and :l_shipinstruct = 'DELIVER IN PERSON' or p_partkey = :l_partkey and p_brand = 'Brand#34' and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') and :l_quantity \u003e= 20 and :l_quantity \u003c= 20 + 10 and p_size between 1 and 15 and :l_shipmode in ('AIR', 'AIR REG') and :l_shipinstruct = 'DELIVER IN PERSON'",
+                "Table": "part"
               }
             ]
           }
@@ -430,50 +862,140 @@ Gen4 error: unsupported: cross-shard correlated subquery
   }
 }
 
-# TPC-H query 19
-"select sum(l_extendedprice* (1 - l_discount)) as revenue from lineitem, part where ( p_partkey = l_partkey and p_brand = 'Brand#12' and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') and l_quantity >= 1 and l_quantity <= 1 + 10 and p_size between 1 and 5 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' ) or ( p_partkey = l_partkey and p_brand = 'Brand#23' and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') and l_quantity >= 10 and l_quantity <= 10 + 10 and p_size between 1 and 10 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' ) or ( p_partkey = l_partkey and p_brand = 'Brand#34' and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') and l_quantity >= 20 and l_quantity <= 20 + 10 and p_size between 1 and 15 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' )"
+# TPC-H query 20
+"select s_name, s_address from supplier, nation where s_suppkey in ( select ps_suppkey from partsupp where ps_partkey in ( select p_partkey from part where p_name like 'forest%' ) and ps_availqty > ( select 0.5 * sum(l_quantity) from lineitem where l_partkey = ps_partkey and l_suppkey = ps_suppkey and l_shipdate >= date('1994-01-01') and l_shipdate < date('1994-01-01') + interval '1' year ) ) and s_nationkey = n_nationkey and n_name = 'CANADA' order by s_name"
+"symbol ps_partkey not found in table or subquery"
+Gen4 error: unsupported: cross-shard correlated subquery
+
+# TPC-H query 21
+"select s_name, count(*) as numwait from supplier, lineitem l1, orders, nation where s_suppkey = l1.l_suppkey and o_orderkey = l1.l_orderkey and o_orderstatus = 'F' and l1.l_receiptdate > l1.l_commitdate and exists ( select * from lineitem l2 where l2.l_orderkey = l1.l_orderkey and l2.l_suppkey <> l1.l_suppkey ) and not exists ( select * from lineitem l3 where l3.l_orderkey = l1.l_orderkey and l3.l_suppkey <> l1.l_suppkey and l3.l_receiptdate > l3.l_commitdate ) and s_nationkey = n_nationkey and n_name = 'SAUDI ARABIA' group by s_name order by numwait desc, s_name limit 100"
 "unsupported: cross-shard query with aggregates"
 {
   "QueryType": "SELECT",
-  "Original": "select sum(l_extendedprice* (1 - l_discount)) as revenue from lineitem, part where ( p_partkey = l_partkey and p_brand = 'Brand#12' and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') and l_quantity \u003e= 1 and l_quantity \u003c= 1 + 10 and p_size between 1 and 5 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' ) or ( p_partkey = l_partkey and p_brand = 'Brand#23' and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') and l_quantity \u003e= 10 and l_quantity \u003c= 10 + 10 and p_size between 1 and 10 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' ) or ( p_partkey = l_partkey and p_brand = 'Brand#34' and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') and l_quantity \u003e= 20 and l_quantity \u003c= 20 + 10 and p_size between 1 and 15 and l_shipmode in ('AIR', 'AIR REG') and l_shipinstruct = 'DELIVER IN PERSON' )",
+  "Original": "select s_name, count(*) as numwait from supplier, lineitem l1, orders, nation where s_suppkey = l1.l_suppkey and o_orderkey = l1.l_orderkey and o_orderstatus = 'F' and l1.l_receiptdate \u003e l1.l_commitdate and exists ( select * from lineitem l2 where l2.l_orderkey = l1.l_orderkey and l2.l_suppkey \u003c\u003e l1.l_suppkey ) and not exists ( select * from lineitem l3 where l3.l_orderkey = l1.l_orderkey and l3.l_suppkey \u003c\u003e l1.l_suppkey and l3.l_receiptdate \u003e l3.l_commitdate ) and s_nationkey = n_nationkey and n_name = 'SAUDI ARABIA' group by s_name order by numwait desc, s_name limit 100",
   "Instructions": {
-    "OperatorType": "Aggregate",
-    "Variant": "Scalar",
-    "Aggregates": "sum(0) AS revenue",
+    "OperatorType": "Limit",
+    "Count": "INT64(100)",
     "Inputs": [
       {
-        "OperatorType": "Join",
-        "Variant": "Join",
-        "JoinColumnIndexes": "-5",
-        "JoinVars": {
-          "l_partkey": 0,
-          "l_quantity": 1,
-          "l_shipinstruct": 3,
-          "l_shipmode": 2
-        },
-        "TableName": "lineitem_part",
+        "OperatorType": "Sort",
+        "Variant": "Memory",
+        "OrderBy": "1 DESC, (0|2) ASC",
+        "ResultColumns": 2,
         "Inputs": [
           {
-            "OperatorType": "Route",
-            "Variant": "Scatter",
-            "Keyspace": {
-              "Name": "main",
-              "Sharded": true
-            },
-            "FieldQuery": "select l_partkey, l_quantity, l_shipmode, l_shipinstruct, sum(l_extendedprice * (1 - l_discount)) as revenue from lineitem where 1 != 1",
-            "Query": "select l_partkey, l_quantity, l_shipmode, l_shipinstruct, sum(l_extendedprice * (1 - l_discount)) as revenue from lineitem",
-            "Table": "lineitem"
-          },
-          {
-            "OperatorType": "Route",
-            "Variant": "Scatter",
-            "Keyspace": {
-              "Name": "main",
-              "Sharded": true
-            },
-            "FieldQuery": "select 1 from part where 1 != 1",
-            "Query": "select 1 from part where p_partkey = :l_partkey and p_brand = 'Brand#12' and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') and :l_quantity \u003e= 1 and :l_quantity \u003c= 1 + 10 and p_size between 1 and 5 and :l_shipmode in ('AIR', 'AIR REG') and :l_shipinstruct = 'DELIVER IN PERSON' or p_partkey = :l_partkey and p_brand = 'Brand#23' and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') and :l_quantity \u003e= 10 and :l_quantity \u003c= 10 + 10 and p_size between 1 and 10 and :l_shipmode in ('AIR', 'AIR REG') and :l_shipinstruct = 'DELIVER IN PERSON' or p_partkey = :l_partkey and p_brand = 'Brand#34' and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') and :l_quantity \u003e= 20 and :l_quantity \u003c= 20 + 10 and p_size between 1 and 15 and :l_shipmode in ('AIR', 'AIR REG') and :l_shipinstruct = 'DELIVER IN PERSON'",
-            "Table": "part"
+            "OperatorType": "Aggregate",
+            "Variant": "Ordered",
+            "Aggregates": "sum(1) AS numwait",
+            "GroupBy": "(0|2)",
+            "Inputs": [
+              {
+                "OperatorType": "Projection",
+                "Expressions": [
+                  "[COLUMN 0]",
+                  "(([COLUMN 2] * [COLUMN 3]) * [COLUMN 4]) * [COLUMN 5] as numwait",
+                  "[COLUMN 1]"
+                ],
+                "Inputs": [
+                  {
+                    "OperatorType": "Sort",
+                    "Variant": "Memory",
+                    "OrderBy": "(0|1) ASC",
+                    "Inputs": [
+                      {
+                        "OperatorType": "Join",
+                        "Variant": "Join",
+                        "JoinColumnIndexes": "1,2,-4,-5,3,4",
+                        "JoinVars": {
+                          "l1_l_suppkey": 0
+                        },
+                        "TableName": "lineitem_orders_supplier_nation",
+                        "Inputs": [
+                          {
+                            "OperatorType": "Join",
+                            "Variant": "Join",
+                            "JoinColumnIndexes": "-2,-2,-5,-3,1",
+                            "JoinVars": {
+                              "l1_l_orderkey": 0
+                            },
+                            "TableName": "lineitem_orders",
+                            "Inputs": [
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "Scatter",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select l1.l_orderkey, l1.l_suppkey, count(*) as numwait, weight_string(l1.l_orderkey), weight_string(l1.l_suppkey) from lineitem as l1 where 1 != 1 group by l1.l_orderkey, weight_string(l1.l_orderkey), l1.l_suppkey, weight_string(l1.l_suppkey)",
+                                "Query": "select l1.l_orderkey, l1.l_suppkey, count(*) as numwait, weight_string(l1.l_orderkey), weight_string(l1.l_suppkey) from lineitem as l1 where l1.l_receiptdate \u003e l1.l_commitdate and exists (select l2.l_orderkey, l2.l_partkey, l2.l_suppkey, l2.l_linenumber, l2.l_quantity, l2.l_extendedprice, l2.l_discount, l2.l_tax, l2.l_returnflag, l2.l_linestatus, l2.l_shipdate, l2.l_commitdate, l2.l_receiptdate, l2.l_shipinstruct, l2.l_shipmode, l2.l_comment from lineitem as l2 where l2.l_orderkey = l1.l_orderkey and l2.l_suppkey != l1.l_suppkey) and not exists (select l3.l_orderkey, l3.l_partkey, l3.l_suppkey, l3.l_linenumber, l3.l_quantity, l3.l_extendedprice, l3.l_discount, l3.l_tax, l3.l_returnflag, l3.l_linestatus, l3.l_shipdate, l3.l_commitdate, l3.l_receiptdate, l3.l_shipinstruct, l3.l_shipmode, l3.l_comment from lineitem as l3 where l3.l_orderkey = l1.l_orderkey and l3.l_suppkey != l1.l_suppkey and l3.l_receiptdate \u003e l3.l_commitdate) group by l1.l_orderkey, weight_string(l1.l_orderkey), l1.l_suppkey, weight_string(l1.l_suppkey)",
+                                "Table": "lineitem"
+                              },
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select count(*) as numwait from orders where 1 != 1",
+                                "Query": "select count(*) as numwait from orders where o_orderstatus = 'F' and exists (select l2.l_orderkey, l2.l_partkey, l2.l_suppkey, l2.l_linenumber, l2.l_quantity, l2.l_extendedprice, l2.l_discount, l2.l_tax, l2.l_returnflag, l2.l_linestatus, l2.l_shipdate, l2.l_commitdate, l2.l_receiptdate, l2.l_shipinstruct, l2.l_shipmode, l2.l_comment from lineitem as l2 where l2.l_orderkey = l1.l_orderkey and l2.l_suppkey != l1.l_suppkey) and not exists (select l3.l_orderkey, l3.l_partkey, l3.l_suppkey, l3.l_linenumber, l3.l_quantity, l3.l_extendedprice, l3.l_discount, l3.l_tax, l3.l_returnflag, l3.l_linestatus, l3.l_shipdate, l3.l_commitdate, l3.l_receiptdate, l3.l_shipinstruct, l3.l_shipmode, l3.l_comment from lineitem as l3 where l3.l_orderkey = l1.l_orderkey and l3.l_suppkey != l1.l_suppkey and l3.l_receiptdate \u003e l3.l_commitdate) and o_orderkey = :l1_l_orderkey",
+                                "Table": "orders",
+                                "Values": [
+                                  ":l1_l_orderkey"
+                                ],
+                                "Vindex": "hash"
+                              }
+                            ]
+                          },
+                          {
+                            "OperatorType": "Join",
+                            "Variant": "Join",
+                            "JoinColumnIndexes": "-4,-5,-2,1",
+                            "JoinVars": {
+                              "s_nationkey": 0
+                            },
+                            "TableName": "supplier_nation",
+                            "Inputs": [
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select s_nationkey, count(*) as numwait, weight_string(s_nationkey), s_name, weight_string(s_name) from supplier where 1 != 1 group by s_nationkey, weight_string(s_nationkey), s_name, weight_string(s_name)",
+                                "Query": "select s_nationkey, count(*) as numwait, weight_string(s_nationkey), s_name, weight_string(s_name) from supplier where exists (select l2.l_orderkey, l2.l_partkey, l2.l_suppkey, l2.l_linenumber, l2.l_quantity, l2.l_extendedprice, l2.l_discount, l2.l_tax, l2.l_returnflag, l2.l_linestatus, l2.l_shipdate, l2.l_commitdate, l2.l_receiptdate, l2.l_shipinstruct, l2.l_shipmode, l2.l_comment from lineitem as l2 where l2.l_orderkey = l1.l_orderkey and l2.l_suppkey != l1.l_suppkey) and not exists (select l3.l_orderkey, l3.l_partkey, l3.l_suppkey, l3.l_linenumber, l3.l_quantity, l3.l_extendedprice, l3.l_discount, l3.l_tax, l3.l_returnflag, l3.l_linestatus, l3.l_shipdate, l3.l_commitdate, l3.l_receiptdate, l3.l_shipinstruct, l3.l_shipmode, l3.l_comment from lineitem as l3 where l3.l_orderkey = l1.l_orderkey and l3.l_suppkey != l1.l_suppkey and l3.l_receiptdate \u003e l3.l_commitdate) and s_suppkey = :l1_l_suppkey group by s_nationkey, weight_string(s_nationkey), s_name, weight_string(s_name)",
+                                "Table": "supplier",
+                                "Values": [
+                                  ":l1_l_suppkey"
+                                ],
+                                "Vindex": "hash"
+                              },
+                              {
+                                "OperatorType": "Route",
+                                "Variant": "EqualUnique",
+                                "Keyspace": {
+                                  "Name": "main",
+                                  "Sharded": true
+                                },
+                                "FieldQuery": "select count(*) as numwait from nation where 1 != 1",
+                                "Query": "select count(*) as numwait from nation where n_name = 'SAUDI ARABIA' and exists (select l2.l_orderkey, l2.l_partkey, l2.l_suppkey, l2.l_linenumber, l2.l_quantity, l2.l_extendedprice, l2.l_discount, l2.l_tax, l2.l_returnflag, l2.l_linestatus, l2.l_shipdate, l2.l_commitdate, l2.l_receiptdate, l2.l_shipinstruct, l2.l_shipmode, l2.l_comment from lineitem as l2 where l2.l_orderkey = l1.l_orderkey and l2.l_suppkey != l1.l_suppkey) and not exists (select l3.l_orderkey, l3.l_partkey, l3.l_suppkey, l3.l_linenumber, l3.l_quantity, l3.l_extendedprice, l3.l_discount, l3.l_tax, l3.l_returnflag, l3.l_linestatus, l3.l_shipdate, l3.l_commitdate, l3.l_receiptdate, l3.l_shipinstruct, l3.l_shipmode, l3.l_comment from lineitem as l3 where l3.l_orderkey = l1.l_orderkey and l3.l_suppkey != l1.l_suppkey and l3.l_receiptdate \u003e l3.l_commitdate) and n_nationkey = :s_nationkey",
+                                "Table": "nation",
+                                "Values": [
+                                  ":s_nationkey"
+                                ],
+                                "Vindex": "hash"
+                              }
+                            ]
+                          }
+                        ]
+                      }
+                    ]
+                  }
+                ]
+              }
+            ]
           }
         ]
       }
@@ -481,16 +1003,6 @@ Gen4 error: unsupported: cross-shard correlated subquery
   }
 }
 
-# TPC-H query 20
-"select s_name, s_address from supplier, nation where s_suppkey in ( select ps_suppkey from partsupp where ps_partkey in ( select p_partkey from part where p_name like 'forest%' ) and ps_availqty > ( select 0.5 * sum(l_quantity) from lineitem where l_partkey = ps_partkey and l_suppkey = ps_suppkey and l_shipdate >= date('1994-01-01') and l_shipdate < date('1994-01-01') + interval '1' year ) ) and s_nationkey = n_nationkey and n_name = 'CANADA' order by s_name"
-"symbol ps_partkey not found in table or subquery"
-Gen4 error: unsupported: cross-shard correlated subquery
-
-# TPC-H query 21
-"select s_name, count(*) as numwait from supplier, lineitem l1, orders, nation where s_suppkey = l1.l_suppkey and o_orderkey = l1.l_orderkey and o_orderstatus = 'F' and l1.l_receiptdate > l1.l_commitdate and exists ( select * from lineitem l2 where l2.l_orderkey = l1.l_orderkey and l2.l_suppkey <> l1.l_suppkey ) and not exists ( select * from lineitem l3 where l3.l_orderkey = l1.l_orderkey and l3.l_suppkey <> l1.l_suppkey and l3.l_receiptdate > l3.l_commitdate ) and s_nationkey = n_nationkey and n_name = 'SAUDI ARABIA' group by s_name order by numwait desc, s_name limit 100"
-"unsupported: cross-shard query with aggregates"
-Gen4 plan same as above
-
 # TPC-H query 22
 "select cntrycode, count(*) as numcust, sum(c_acctbal) as totacctbal from ( select substring(c_phone from 1 for 2) as cntrycode, c_acctbal from customer where substring(c_phone from 1 for 2) in ('13', '31', '23', '29', '30', '18', '17') and c_acctbal > ( select avg(c_acctbal) from customer where c_acctbal > 0.00 and substring(c_phone from 1 for 2) in ('13', '31', '23', '29', '30', '18', '17') ) and not exists ( select * from orders where o_custkey = c_custkey ) ) as custsale group by cntrycode order by cntrycode"
 "symbol c_custkey not found in table or subquery"
diff --git a/go/vt/vtgate/planbuilder/testdata/union_cases.txt b/go/vt/vtgate/planbuilder/testdata/union_cases.txt
index 57d8c6f504..c14f847ff2 100644
--- a/go/vt/vtgate/planbuilder/testdata/union_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/union_cases.txt
@@ -1292,7 +1292,7 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
           {
             "OperatorType": "Aggregate",
             "Variant": "Scalar",
-            "Aggregates": "count(0) AS s",
+            "Aggregates": "sum(0) AS s",
             "Inputs": [
               {
                 "OperatorType": "Route",
@@ -1310,7 +1310,7 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
           {
             "OperatorType": "Aggregate",
             "Variant": "Scalar",
-            "Aggregates": "count(0) AS s",
+            "Aggregates": "sum(0) AS s",
             "Inputs": [
               {
                 "OperatorType": "Route",
diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
index 37bf043858..25e8c5433d 100644
--- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
@@ -472,11 +472,6 @@ Gen4 error: unsupported: in scatter query: aggregation function 'avg'
 "generating order by clause: ambiguous symbol reference: a"
 Gen4 plan same as above
 
-# Aggregates and joins
-"select count(*) from user join user_extra"
-"unsupported: cross-shard query with aggregates"
-Gen4 plan same as above
-
 # TODO this should be planned without using OA and MS
 "select u.id from user u join user_extra ue on ue.id = u.id group by u.id having count(u.name) = 3"
 "unsupported: cross-shard query with aggregates"
@@ -486,48 +481,6 @@ Gen4 error: cannot push projections in ordered aggregates
 "symbol ue.col not found in subquery"
 Gen4 error: cannot push projections in ordered aggregates
 
-# aggregation filtering by having on a route with no group by
-"select 1 from user having count(id) = 10"
-{
-  "QueryType": "SELECT",
-  "Original": "select 1 from user having count(id) = 10",
-  "Instructions": {
-    "OperatorType": "Route",
-    "Variant": "Scatter",
-    "Keyspace": {
-      "Name": "user",
-      "Sharded": true
-    },
-    "FieldQuery": "select 1 from `user` where 1 != 1",
-    "Query": "select 1 from `user` having count(id) = 10",
-    "Table": "`user`"
-  }
-}
-Gen4 error: cannot push projections in ordered aggregates
-
-# aggregation filtering by having on a route with no group by with non-unique vindex filter
-"select 1 from user having count(id) = 10 and name = 'a'"
-{
-  "QueryType": "SELECT",
-  "Original": "select 1 from user having count(id) = 10 and name = 'a'",
-  "Instructions": {
-    "OperatorType": "Route",
-    "Variant": "Equal",
-    "Keyspace": {
-      "Name": "user",
-      "Sharded": true
-    },
-    "FieldQuery": "select 1 from `user` where 1 != 1",
-    "Query": "select 1 from `user` having count(id) = 10 and `name` = 'a'",
-    "Table": "`user`",
-    "Values": [
-      "VARCHAR(\"a\")"
-    ],
-    "Vindex": "name_user_map"
-  }
-}
-Gen4 error: cannot push projections in ordered aggregates
-
 # subquery of information_schema with itself and star expression in outer select
 "select a.*, u.id from information_schema.a a, user u where a.id in (select * from information_schema.b)"
 "unsupported: '*' expression in cross-shard query"
@@ -554,25 +507,6 @@ Gen4 plan same as above
 "can't do ORDER BY on top of UNION"
 Gen4 error: Column 'id' in field list is ambiguous
 
-# Column aliases in Derived Table
-"select id2 from (select id from user) as x (id2)"
-"unsupported: column aliases in derived table"
-{
-  "QueryType": "SELECT",
-  "Original": "select id2 from (select id from user) as x (id2)",
-  "Instructions": {
-    "OperatorType": "Route",
-    "Variant": "Scatter",
-    "Keyspace": {
-      "Name": "user",
-      "Sharded": true
-    },
-    "FieldQuery": "select id2 from (select id from `user` where 1 != 1) as x(id2) where 1 != 1",
-    "Query": "select id2 from (select id from `user`) as x(id2)",
-    "Table": "`user`"
-  }
-}
-
 # unsupported with clause in delete statement
 "with x as (select * from user) delete from x"
 "unsupported: with expression in delete statement"
@@ -593,16 +527,6 @@ Gen4 plan same as above
 "unsupported: with expression in union statement"
 Gen4 plan same as above
 
-# Aggregate on join
-"select user.a, count(*) from user join user_extra group by user.a"
-"unsupported: cross-shard query with aggregates"
-Gen4 plan same as above
-
-# Aggregate on other table in join
-"select user.a, count(user_extra.a) from user join user_extra group by user.a"
-"unsupported: cross-shard query with aggregates"
-Gen4 plan same as above
-
 # scatter aggregate with complex select list (can't build order by)
 "select distinct a+1 from user"
 "generating order by clause: cannot reference a complex expression"
@@ -611,7 +535,7 @@ Gen4 error: unsupported: in scatter query: complex order by expression: a + 1
 # aggregation on union
 "select sum(col) from (select col from user union all select col from unsharded) t"
 "unsupported: cross-shard query with aggregates"
-Gen4 error: unsupported: aggregation on unions
+Gen4 error: using aggregation on top of a *planbuilder.simpleProjection plan is not yet supported
 
 # systable union query in derived table with constraint on outside (without star projection)
 "select id from (select id from `information_schema`.`key_column_usage` `kcu` where `kcu`.`table_schema` = 'user' and `kcu`.`table_name` = 'user_extra' union select id from `information_schema`.`key_column_usage` `kcu` where `kcu`.`table_schema` = 'user' and `kcu`.`table_name` = 'music') `kcu` where `id` = 'primary'"