Repository: vitessio/vitess
Pull request #9133 (2021-11-03 15:07:51)

[vtadmin-web] Restructure list of shards on Keyspace detail page
## Description

A few changes that made sense to do together:

- Rewrites the list of shards on the keyspace detail page to aggregate tablet information, rather than showing all tablets grouped by shard. (All tablets for a shard can now be seen on the shard detail page, via https://github.com/vitessio/vitess/pull/9119)

- Adds a JSON tab to the keyspace detail page

Here are some screenshots from the local example. `customer` keyspace:
<img width="1792" alt="Screen Shot 2021-11-03 at 10 54 50 AM" src="https://user-images.githubusercontent.com/855595/140085233-a8390677-3104-4ecc-aedb-1d34db56c513.png">

JSON view for `customer` keyspace:
<img width="1792" alt="Screen Shot 2021-11-03 at 10 56 21 AM" src="https://user-images.githubusercontent.com/855595/140086101-9d4f7f13-03e5-4d5c-bef7-4e7e3918c4d8.png">

`commerce` keyspace:
<img width="1792" alt="Screen Shot 2021-11-03 at 10 54 46 AM" src="https://user-images.githubusercontent.com/855595/140085287-b4a86731-9849-48de-9179-dcaafe128795.png">

Here's what it looked liked before, for comparison:

<img width="1792" alt="Screen Shot 2021-11-03 at 11 02 33 AM" src="https://user-images.githubusercontent.com/855595/140086019-c7a9a4b8-6171-405a-8f4c-5c3d52e7c572.png">

And now that the nesty table is gone... 🎉 time for react-table. 

## Related Issue(s)

Builds on https://github.com/vitessio/vitess/pull/9119


## Checklist
- [ ] Should this PR be backported?
- [x] Tests were added or are not required
- [x] Documentation was added or is not required

## Deployment Notes

N/A


Patch:
diff --git a/go/mysql/binlog_event_json.go b/go/mysql/binlog_event_json.go
index 7d76d67d32..efc5648f32 100644
--- a/go/mysql/binlog_event_json.go
+++ b/go/mysql/binlog_event_json.go
@@ -23,6 +23,8 @@ import (
 
 	"vitess.io/vitess/go/vt/log"
 
+	"vitess.io/vitess/go/vt/vtgate/evalengine"
+
 	"github.com/spyzhov/ajson"
 
 	querypb "vitess.io/vitess/go/vt/proto/query"
@@ -472,7 +474,7 @@ func (oh opaquePlugin) getNode(typ jsonDataType, data []byte, pos int) (node *aj
 		if err != nil {
 			return nil, err
 		}
-		float, err := val.ToFloat64()
+		float, err := evalengine.ToFloat64(val)
 		if err != nil {
 			return nil, err
 		}
diff --git a/go/sqltypes/type.go b/go/sqltypes/type.go
index 350c8166b8..45edc7b62b 100644
--- a/go/sqltypes/type.go
+++ b/go/sqltypes/type.go
@@ -86,11 +86,6 @@ func IsNumber(t querypb.Type) bool {
 	return IsIntegral(t) || IsFloat(t) || t == Decimal
 }
 
-// IsDate returns true if the type represents a date and/or time.
-func IsDate(t querypb.Type) bool {
-	return t == Datetime || t == Date || t == Timestamp || t == Time
-}
-
 // Vitess data types. These are idiomatically
 // named synonyms for the querypb.Type values.
 // Although these constants are interchangeable,
diff --git a/go/sqltypes/value.go b/go/sqltypes/value.go
index fd504460d3..85002c8bc4 100644
--- a/go/sqltypes/value.go
+++ b/go/sqltypes/value.go
@@ -143,31 +143,6 @@ func NewVarBinary(v string) Value {
 	return MakeTrusted(VarBinary, []byte(v))
 }
 
-// NewDate builds a Date value.
-func NewDate(v string) Value {
-	return MakeTrusted(Date, []byte(v))
-}
-
-// NewTime builds a Time value.
-func NewTime(v string) Value {
-	return MakeTrusted(Time, []byte(v))
-}
-
-// NewTimestamp builds a Timestamp value.
-func NewTimestamp(v string) Value {
-	return MakeTrusted(Timestamp, []byte(v))
-}
-
-// NewDatetime builds a Datetime value.
-func NewDatetime(v string) Value {
-	return MakeTrusted(Datetime, []byte(v))
-}
-
-// NewDecimal builds a Decimal value.
-func NewDecimal(v string) Value {
-	return MakeTrusted(Decimal, []byte(v))
-}
-
 // NewIntegral builds an integral type from a string representation.
 // The type will be Int64 or Uint64. Int64 will be preferred where possible.
 func NewIntegral(val string) (n Value, err error) {
diff --git a/go/vt/sqlparser/expression_converter.go b/go/vt/sqlparser/expression_converter.go
new file mode 100644
index 0000000000..e85fce2241
--- /dev/null
+++ b/go/vt/sqlparser/expression_converter.go
@@ -0,0 +1,77 @@
+/*
+Copyright 2020 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 sqlparser
+
+import (
+	"fmt"
+
+	"vitess.io/vitess/go/vt/vtgate/evalengine"
+)
+
+// ErrExprNotSupported signals that the expression cannot be handled by expression evaluation engine.
+var ErrExprNotSupported = fmt.Errorf("Expr Not Supported")
+
+//Convert converts between AST expressions and executable expressions
+func Convert(e Expr) (evalengine.Expr, error) {
+	switch node := e.(type) {
+	case Argument:
+		return evalengine.NewBindVar(string(node)), nil
+	case *Literal:
+		switch node.Type {
+		case IntVal:
+			return evalengine.NewLiteralIntFromBytes(node.Bytes())
+		case FloatVal:
+			return evalengine.NewLiteralFloat(node.Bytes())
+		case StrVal:
+			return evalengine.NewLiteralString(node.Bytes()), nil
+		}
+	case BoolVal:
+		if node {
+			return evalengine.NewLiteralIntFromBytes([]byte("1"))
+		}
+		return evalengine.NewLiteralIntFromBytes([]byte("0"))
+	case *BinaryExpr:
+		var op evalengine.BinaryExpr
+		switch node.Operator {
+		case PlusOp:
+			op = &evalengine.Addition{}
+		case MinusOp:
+			op = &evalengine.Subtraction{}
+		case MultOp:
+			op = &evalengine.Multiplication{}
+		case DivOp:
+			op = &evalengine.Division{}
+		default:
+			return nil, ErrExprNotSupported
+		}
+		left, err := Convert(node.Left)
+		if err != nil {
+			return nil, err
+		}
+		right, err := Convert(node.Right)
+		if err != nil {
+			return nil, err
+		}
+		return &evalengine.BinaryOp{
+			Expr:  op,
+			Left:  left,
+			Right: right,
+		}, nil
+
+	}
+	return nil, ErrExprNotSupported
+}
diff --git a/go/vt/vtgate/engine/cached_size.go b/go/vt/vtgate/engine/cached_size.go
index 1af7b1076b..46d00ace60 100644
--- a/go/vt/vtgate/engine/cached_size.go
+++ b/go/vt/vtgate/engine/cached_size.go
@@ -175,28 +175,6 @@ func (cached *Distinct) CachedSize(alloc bool) int64 {
 	}
 	return size
 }
-func (cached *Filter) CachedSize(alloc bool) int64 {
-	if cached == nil {
-		return int64(0)
-	}
-	size := int64(0)
-	if alloc {
-		size += int64(48)
-	}
-	// field Predicate vitess.io/vitess/go/vt/vtgate/evalengine.Expr
-	if cc, ok := cached.Predicate.(cachedObject); ok {
-		size += cc.CachedSize(true)
-	}
-	// field ASTPredicate vitess.io/vitess/go/vt/sqlparser.Expr
-	if cc, ok := cached.ASTPredicate.(cachedObject); ok {
-		size += cc.CachedSize(true)
-	}
-	// field Input vitess.io/vitess/go/vt/vtgate/engine.Primitive
-	if cc, ok := cached.Input.(cachedObject); ok {
-		size += cc.CachedSize(true)
-	}
-	return size
-}
 func (cached *Gen4CompareV3) CachedSize(alloc bool) int64 {
 	if cached == nil {
 		return int64(0)
@@ -724,7 +702,7 @@ func (cached *SemiJoin) CachedSize(alloc bool) int64 {
 	}
 	size := int64(0)
 	if alloc {
-		size += int64(64)
+		size += int64(48)
 	}
 	// field Left vitess.io/vitess/go/vt/vtgate/engine.Primitive
 	if cc, ok := cached.Left.(cachedObject); ok {
@@ -734,10 +712,6 @@ func (cached *SemiJoin) CachedSize(alloc bool) int64 {
 	if cc, ok := cached.Right.(cachedObject); ok {
 		size += cc.CachedSize(true)
 	}
-	// field Cols []int
-	{
-		size += hack.RuntimeAllocSize(int64(cap(cached.Cols)) * int64(8))
-	}
 	// field Vars map[string]int
 	if cached.Vars != nil {
 		size += int64(48)
diff --git a/go/vt/vtgate/engine/comparer.go b/go/vt/vtgate/engine/comparer.go
index 767799492a..305593cba8 100644
--- a/go/vt/vtgate/engine/comparer.go
+++ b/go/vt/vtgate/engine/comparer.go
@@ -17,7 +17,6 @@ limitations under the License.
 package engine
 
 import (
-	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/sqltypes"
 	"vitess.io/vitess/go/vt/vtgate/evalengine"
 )
@@ -39,8 +38,7 @@ func (c *comparer) compare(r1, r2 []sqltypes.Value) (int, error) {
 	} else {
 		colIndex = c.orderBy
 	}
-	// TODO(king-11) make collation aware
-	cmp, err := evalengine.NullsafeCompare(r1[colIndex], r2[colIndex], collations.Unknown)
+	cmp, err := evalengine.NullsafeCompare(r1[colIndex], r2[colIndex])
 	if err != nil {
 		_, isComparisonErr := err.(evalengine.UnsupportedComparisonError)
 		if !(isComparisonErr && c.weightString != -1) {
@@ -49,8 +47,7 @@ func (c *comparer) compare(r1, r2 []sqltypes.Value) (int, error) {
 		// in case of a comparison error switch to using the weight string column for ordering
 		c.orderBy = c.weightString
 		c.weightString = -1
-		// TODO(king-11) make collation aware
-		cmp, err = evalengine.NullsafeCompare(r1[c.orderBy], r2[c.orderBy], collations.Unknown)
+		cmp, err = evalengine.NullsafeCompare(r1[c.orderBy], r2[c.orderBy])
 		if err != nil {
 			return 0, err
 		}
diff --git a/go/vt/vtgate/engine/distinct.go b/go/vt/vtgate/engine/distinct.go
index e96680cbd3..fe28e6fe34 100644
--- a/go/vt/vtgate/engine/distinct.go
+++ b/go/vt/vtgate/engine/distinct.go
@@ -17,7 +17,6 @@ limitations under the License.
 package engine
 
 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/vtgate/evalengine"
@@ -74,8 +73,7 @@ func (pt *probeTable) exists(inputRow row) (bool, error) {
 
 func equal(a, b []sqltypes.Value) (bool, error) {
 	for i, aVal := range a {
-		// TODO(king-11) make collation aware
-		cmp, err := evalengine.NullsafeCompare(aVal, b[i], collations.Unknown)
+		cmp, err := evalengine.NullsafeCompare(aVal, b[i])
 		if err != nil {
 			return false, err
 		}
diff --git a/go/vt/vtgate/engine/filter.go b/go/vt/vtgate/engine/filter.go
deleted file mode 100644
index 27480e17c4..0000000000
--- a/go/vt/vtgate/engine/filter.go
+++ /dev/null
@@ -1,128 +0,0 @@
-/*
-Copyright 2021 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 (
-	"vitess.io/vitess/go/vt/sqlparser"
-	"vitess.io/vitess/go/vt/vtgate/evalengine"
-
-	"vitess.io/vitess/go/sqltypes"
-
-	querypb "vitess.io/vitess/go/vt/proto/query"
-)
-
-var _ Primitive = (*Filter)(nil)
-
-// Filter is a primitive that performs the FILTER operation.
-type Filter struct {
-	Predicate    evalengine.Expr
-	ASTPredicate sqlparser.Expr
-	Input        Primitive
-
-	noTxNeeded
-}
-
-// RouteType returns a description of the query routing type used by the primitive
-func (f *Filter) RouteType() string {
-	return f.Input.RouteType()
-}
-
-// GetKeyspaceName specifies the Keyspace that this primitive routes to.
-func (f *Filter) GetKeyspaceName() string {
-	return f.Input.GetKeyspaceName()
-}
-
-// GetTableName specifies the table that this primitive routes to.
-func (f *Filter) GetTableName() string {
-	return f.Input.GetTableName()
-}
-
-// TryExecute satisfies the Primitive interface.
-func (f *Filter) TryExecute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
-	result, err := f.Input.TryExecute(vcursor, bindVars, wantfields)
-	if err != nil {
-		return nil, err
-	}
-	env := evalengine.ExpressionEnv{
-		BindVars: bindVars,
-	}
-	var rows [][]sqltypes.Value
-	for _, row := range result.Rows {
-		env.Row = row
-		evalResult, err := f.Predicate.Evaluate(env)
-		if err != nil {
-			return nil, err
-		}
-		intEvalResult, err := evalResult.Value().ToInt64()
-		if err != nil {
-			return nil, err
-		}
-		if intEvalResult == 1 {
-			rows = append(rows, row)
-		}
-	}
-	result.Rows = rows
-	return result, nil
-}
-
-// TryStreamExecute satisfies the Primitive interface.
-func (f *Filter) TryStreamExecute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error {
-	env := evalengine.ExpressionEnv{
-		BindVars: bindVars,
-	}
-	filter := func(results *sqltypes.Result) error {
-		var rows [][]sqltypes.Value
-		for _, row := range results.Rows {
-			env.Row = row
-			evalResult, err := f.Predicate.Evaluate(env)
-			if err != nil {
-				return err
-			}
-			intEvalResult, err := evalResult.Value().ToInt64()
-			if err != nil {
-				return err
-			}
-			if intEvalResult == 1 {
-				rows = append(rows, row)
-			}
-		}
-		results.Rows = rows
-		return callback(results)
-	}
-	return f.Input.TryStreamExecute(vcursor, bindVars, wantfields, filter)
-}
-
-// GetFields implements the Primitive interface.
-func (f *Filter) GetFields(vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) {
-	return f.Input.GetFields(vcursor, bindVars)
-}
-
-// Inputs returns the input to limit
-func (f *Filter) Inputs() []Primitive {
-	return []Primitive{f.Input}
-}
-
-func (f *Filter) description() PrimitiveDescription {
-	other := map[string]interface{}{
-		"Predicate": sqlparser.String(f.ASTPredicate),
-	}
-
-	return PrimitiveDescription{
-		OperatorType: "Filter",
-		Other:        other,
-	}
-}
diff --git a/go/vt/vtgate/engine/lock.go b/go/vt/vtgate/engine/lock.go
index af14984bda..dccd6354af 100644
--- a/go/vt/vtgate/engine/lock.go
+++ b/go/vt/vtgate/engine/lock.go
@@ -38,8 +38,6 @@ type Lock struct {
 	// Query specifies the query to be executed.
 	Query string
 
-	FieldQuery string
-
 	noInputs
 
 	noTxNeeded
@@ -62,10 +60,6 @@ func (l *Lock) GetTableName() string {
 
 // TryExecute is part of the Primitive interface
 func (l *Lock) TryExecute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, _ bool) (*sqltypes.Result, error) {
-	return l.execLock(vcursor, l.Query, bindVars)
-}
-
-func (l *Lock) execLock(vcursor VCursor, query string, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) {
 	rss, _, err := vcursor.ResolveDestinations(l.Keyspace.Name, nil, []key.Destination{l.TargetDestination})
 	if err != nil {
 		return nil, err
@@ -74,11 +68,11 @@ func (l *Lock) execLock(vcursor VCursor, query string, bindVars map[string]*quer
 		return nil, vterrors.Errorf(vtrpc.Code_FAILED_PRECONDITION, "lock query can be routed to single shard only: %v", rss)
 	}
 
-	boundQuery := &querypb.BoundQuery{
-		Sql:           query,
+	query := &querypb.BoundQuery{
+		Sql:           l.Query,
 		BindVariables: bindVars,
 	}
-	return vcursor.ExecuteLock(rss[0], boundQuery)
+	return vcursor.ExecuteLock(rss[0], query)
 }
 
 // TryStreamExecute is part of the Primitive interface
@@ -92,13 +86,12 @@ func (l *Lock) TryStreamExecute(vcursor VCursor, bindVars map[string]*querypb.Bi
 
 // GetFields is part of the Primitive interface
 func (l *Lock) GetFields(vcursor VCursor, bindVars map[string]*querypb.BindVariable) (*sqltypes.Result, error) {
-	return l.execLock(vcursor, l.FieldQuery, bindVars)
+	return nil, vterrors.New(vtrpc.Code_UNIMPLEMENTED, "not implements in lock primitive")
 }
 
 func (l *Lock) description() PrimitiveDescription {
 	other := map[string]interface{}{
-		"Query":      l.Query,
-		"FieldQuery": l.FieldQuery,
+		"Query": l.Query,
 	}
 	return PrimitiveDescription{
 		OperatorType:      "Lock",
diff --git a/go/vt/vtgate/engine/ordered_aggregate.go b/go/vt/vtgate/engine/ordered_aggregate.go
index be0d5f9bce..ebe0e03b35 100644
--- a/go/vt/vtgate/engine/ordered_aggregate.go
+++ b/go/vt/vtgate/engine/ordered_aggregate.go
@@ -186,17 +186,6 @@ func (oa *OrderedAggregate) GetTableName() string {
 	return oa.Input.GetTableName()
 }
 
-// getCollations specifies the collation ID value for columns.
-func (oa *OrderedAggregate) getCollations() map[int]collations.ID {
-	colls := make(map[int]collations.ID)
-	for _, key := range oa.GroupByKeys {
-		if key.CollationID != collations.Unknown {
-			colls[key.KeyCol] = key.CollationID
-		}
-	}
-	return colls
-}
-
 // SetTruncateColumnCount sets the truncate column count.
 func (oa *OrderedAggregate) SetTruncateColumnCount(count int) {
 	oa.TruncateColumnCount = count
@@ -220,7 +209,6 @@ func (oa *OrderedAggregate) execute(vcursor VCursor, bindVars map[string]*queryp
 		Fields: oa.convertFields(result.Fields),
 		Rows:   make([][]sqltypes.Value, 0, len(result.Rows)),
 	}
-	colls := oa.getCollations()
 	// This code is similar to the one in StreamExecute.
 	var current []sqltypes.Value
 	var curDistincts []sqltypes.Value
@@ -229,13 +217,14 @@ func (oa *OrderedAggregate) execute(vcursor VCursor, bindVars map[string]*queryp
 			current, curDistincts = oa.convertRow(row)
 			continue
 		}
-		equal, err := oa.keysEqual(current, row, colls)
+
+		equal, err := oa.keysEqual(current, row)
 		if err != nil {
 			return nil, err
 		}
 
 		if equal {
-			current, curDistincts, err = oa.merge(result.Fields, current, row, curDistincts, colls)
+			current, curDistincts, err = oa.merge(result.Fields, current, row, curDistincts)
 			if err != nil {
 				return nil, err
 			}
@@ -282,7 +271,6 @@ func (oa *OrderedAggregate) TryStreamExecute(vcursor VCursor, bindVars map[strin
 				return err
 			}
 		}
-		colls := oa.getCollations()
 		// This code is similar to the one in Execute.
 		for _, row := range qr.Rows {
 			if current == nil {
@@ -290,13 +278,13 @@ func (oa *OrderedAggregate) TryStreamExecute(vcursor VCursor, bindVars map[strin
 				continue
 			}
 
-			equal, err := oa.keysEqual(current, row, colls)
+			equal, err := oa.keysEqual(current, row)
 			if err != nil {
 				return err
 			}
 
 			if equal {
-				current, curDistincts, err = oa.merge(fields, current, row, curDistincts, colls)
+				current, curDistincts, err = oa.merge(fields, current, row, curDistincts)
 				if err != nil {
 					return err
 				}
@@ -407,16 +395,16 @@ func (oa *OrderedAggregate) NeedsTransaction() bool {
 	return oa.Input.NeedsTransaction()
 }
 
-func (oa *OrderedAggregate) keysEqual(row1, row2 []sqltypes.Value, colls map[int]collations.ID) (bool, error) {
+func (oa *OrderedAggregate) keysEqual(row1, row2 []sqltypes.Value) (bool, error) {
 	for _, key := range oa.GroupByKeys {
-		cmp, err := evalengine.NullsafeCompare(row1[key.KeyCol], row2[key.KeyCol], colls[key.KeyCol])
+		cmp, err := evalengine.NullsafeCompare(row1[key.KeyCol], row2[key.KeyCol])
 		if err != nil {
 			_, isComparisonErr := err.(evalengine.UnsupportedComparisonError)
 			if !(isComparisonErr && key.WeightStringCol != -1) {
 				return false, err
 			}
 			key.KeyCol = key.WeightStringCol
-			cmp, err = evalengine.NullsafeCompare(row1[key.WeightStringCol], row2[key.WeightStringCol], colls[key.KeyCol])
+			cmp, err = evalengine.NullsafeCompare(row1[key.WeightStringCol], row2[key.WeightStringCol])
 			if err != nil {
 				return false, err
 			}
@@ -428,14 +416,14 @@ func (oa *OrderedAggregate) keysEqual(row1, row2 []sqltypes.Value, colls map[int
 	return true, nil
 }
 
-func (oa *OrderedAggregate) merge(fields []*querypb.Field, row1, row2 []sqltypes.Value, curDistincts []sqltypes.Value, colls map[int]collations.ID) ([]sqltypes.Value, []sqltypes.Value, error) {
+func (oa *OrderedAggregate) merge(fields []*querypb.Field, row1, row2 []sqltypes.Value, curDistincts []sqltypes.Value) ([]sqltypes.Value, []sqltypes.Value, error) {
 	result := sqltypes.CopyRow(row1)
 	for index, aggr := range oa.Aggregates {
 		if aggr.isDistinct() {
 			if row2[aggr.KeyCol].IsNull() {
 				continue
 			}
-			cmp, err := evalengine.NullsafeCompare(curDistincts[index], row2[aggr.KeyCol], colls[aggr.KeyCol])
+			cmp, err := evalengine.NullsafeCompare(curDistincts[index], row2[aggr.KeyCol])
 			if err != nil {
 				return nil, nil, err
 			}
@@ -449,15 +437,15 @@ func (oa *OrderedAggregate) merge(fields []*querypb.Field, row1, row2 []sqltypes
 		case AggregateCount, AggregateSum:
 			value := row1[aggr.Col]
 			v2 := row2[aggr.Col]
-			result[aggr.Col], err = evalengine.NullSafeAdd(value, v2, fields[aggr.Col].Type)
+			result[aggr.Col] = evalengine.NullsafeAdd(value, v2, fields[aggr.Col].Type)
 		case AggregateMin:
-			result[aggr.Col], err = evalengine.Min(row1[aggr.Col], row2[aggr.Col], colls[aggr.Col])
+			result[aggr.Col], err = evalengine.Min(row1[aggr.Col], row2[aggr.Col])
 		case AggregateMax:
-			result[aggr.Col], err = evalengine.Max(row1[aggr.Col], row2[aggr.Col], colls[aggr.Col])
+			result[aggr.Col], err = evalengine.Max(row1[aggr.Col], row2[aggr.Col])
 		case AggregateCountDistinct:
-			result[aggr.Col], err = evalengine.NullSafeAdd(row1[aggr.Col], countOne, OpcodeType[aggr.Opcode])
+			result[aggr.Col] = evalengine.NullsafeAdd(row1[aggr.Col], countOne, OpcodeType[aggr.Opcode])
 		case AggregateSumDistinct:
-			result[aggr.Col], err = evalengine.NullSafeAdd(row1[aggr.Col], row2[aggr.Col], OpcodeType[aggr.Opcode])
+			result[aggr.Col] = evalengine.NullsafeAdd(row1[aggr.Col], row2[aggr.Col], OpcodeType[aggr.Opcode])
 		case AggregateGtid:
 			vgtid := &binlogdatapb.VGtid{}
 			err = proto.Unmarshal(row1[aggr.Col].ToBytes(), vgtid)
diff --git a/go/vt/vtgate/evalengine/arithmetic.go b/go/vt/vtgate/evalengine/arithmetic.go
index 90e6379d96..7bc883acda 100644
--- a/go/vt/vtgate/evalengine/arithmetic.go
+++ b/go/vt/vtgate/evalengine/arithmetic.go
@@ -21,7 +21,6 @@ import (
 	"fmt"
 	"math"
 
-	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/sqltypes"
 
 	"strconv"
@@ -46,20 +45,6 @@ func (err UnsupportedComparisonError) Error() string {
 	return fmt.Sprintf("types are not comparable: %v vs %v", err.Type1, err.Type2)
 }
 
-// UnsupportedCollationError represents the error where the comparison using provided collation is unsupported on vitess
-type UnsupportedCollationError struct {
-	ID collations.ID
-}
-
-// Error function implements the error interface
-func (err UnsupportedCollationError) Error() string {
-	return fmt.Sprintf("comparison using collation %d isn't possible", err.ID)
-}
-
-func dataOutOfRangeError(v1, v2 interface{}, typ, sign string) error {
-	return vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v %s %v", typ, v1, sign, v2)
-}
-
 // Add adds two values together
 // if v1 or v2 is null, then it returns null
 func Add(v1, v2 sqltypes.Value) (sqltypes.Value, error) {
@@ -162,7 +147,7 @@ func Divide(v1, v2 sqltypes.Value) (sqltypes.Value, error) {
 	return lresult.toSQLValue(lresult.typ), nil
 }
 
-// NullSafeAdd adds two Values in a null-safe manner. A null value
+// NullsafeAdd adds two Values in a null-safe manner. A null value
 // is treated as 0. If both values are null, then a null is returned.
 // If both values are not null, a numeric value is built
 // from each input: Signed->int64, Unsigned->uint64, Float->float64.
@@ -173,7 +158,7 @@ func Divide(v1, v2 sqltypes.Value) (sqltypes.Value, error) {
 // addition, if one of the input types was Decimal, then
 // a Decimal is built. Otherwise, the final type of the
 // result is preserved.
-func NullSafeAdd(v1, v2 sqltypes.Value, resultType querypb.Type) (sqltypes.Value, error) {
+func NullsafeAdd(v1, v2 sqltypes.Value, resultType querypb.Type) sqltypes.Value {
 	if v1.IsNull() {
 		v1 = sqltypes.MakeTrusted(resultType, zeroBytes)
 	}
@@ -183,17 +168,15 @@ func NullSafeAdd(v1, v2 sqltypes.Value, resultType querypb.Type) (sqltypes.Value
 
 	lv1, err := newEvalResult(v1)
 	if err != nil {
-		return sqltypes.NULL, err
+		return sqltypes.NULL
 	}
 	lv2, err := newEvalResult(v2)
 	if err != nil {
-		return sqltypes.NULL, err
+		return sqltypes.NULL
 	}
-	lresult, err := addNumericWithError(lv1, lv2)
-	if err != nil {
-		return sqltypes.NULL, err
-	}
-	return lresult.toSQLValue(resultType), nil
+	lresult := addNumeric(lv1, lv2)
+
+	return lresult.toSQLValue(resultType)
 }
 
 // NullsafeCompare returns 0 if v1==v2, -1 if v1<v2, and 1 if v1>v2.
@@ -201,7 +184,7 @@ func NullSafeAdd(v1, v2 sqltypes.Value, resultType querypb.Type) (sqltypes.Value
 // numeric, then a numeric comparison is performed after
 // necessary conversions. If none are numeric, then it's
 // a simple binary comparison. Uncomparable values return an error.
-func NullsafeCompare(v1, v2 sqltypes.Value, collationID collations.ID) (int, error) {
+func NullsafeCompare(v1, v2 sqltypes.Value) (int, error) {
 	// Based on the categorization defined for the types,
 	// we're going to allow comparison of the following:
 	// Null, isNumber, IsBinary. This will exclude IsQuoted
@@ -229,22 +212,6 @@ func NullsafeCompare(v1, v2 sqltypes.Value, collationID collations.ID) (int, err
 	if isByteComparable(v1) && isByteComparable(v2) {
 		return bytes.Compare(v1.ToBytes(), v2.ToBytes()), nil
 	}
-	if v1.IsText() && v2.IsText() && collationID != collations.Unknown {
-		collation := collations.Default().LookupByID(collationID)
-		if collation == nil {
-			return 0, UnsupportedCollationError{
-				ID: collationID,
-			}
-		}
-		switch result := collation.Collate(v1.ToBytes(), v2.ToBytes(), false); {
-		case result < 0:
-			return -1, nil
-		case result > 0:
-			return 1, nil
-		default:
-			return 0, nil
-		}
-	}
 	return 0, UnsupportedComparisonError{
 		Type1: v1.Type(),
 		Type2: v2.Type(),
@@ -285,18 +252,18 @@ func isByteComparable(v sqltypes.Value) bool {
 // Min returns the minimum of v1 and v2. If one of the
 // values is NULL, it returns the other value. If both
 // are NULL, it returns NULL.
-func Min(v1, v2 sqltypes.Value, collation collations.ID) (sqltypes.Value, error) {
-	return minmax(v1, v2, true, collation)
+func Min(v1, v2 sqltypes.Value) (sqltypes.Value, error) {
+	return minmax(v1, v2, true)
 }
 
 // Max returns the maximum of v1 and v2. If one of the
 // values is NULL, it returns the other value. If both
 // are NULL, it returns NULL.
-func Max(v1, v2 sqltypes.Value, collation collations.ID) (sqltypes.Value, error) {
-	return minmax(v1, v2, false, collation)
+func Max(v1, v2 sqltypes.Value) (sqltypes.Value, error) {
+	return minmax(v1, v2, false)
 }
 
-func minmax(v1, v2 sqltypes.Value, min bool, collation collations.ID) (sqltypes.Value, error) {
+func minmax(v1, v2 sqltypes.Value, min bool) (sqltypes.Value, error) {
 	if v1.IsNull() {
 		return v2, nil
 	}
@@ -304,7 +271,7 @@ func minmax(v1, v2 sqltypes.Value, min bool, collation collations.ID) (sqltypes.
 		return v1, nil
 	}
 
-	n, err := NullsafeCompare(v1, v2, collation)
+	n, err := NullsafeCompare(v1, v2)
 	if err != nil {
 		return sqltypes.NULL, err
 	}
@@ -318,7 +285,7 @@ func minmax(v1, v2 sqltypes.Value, min bool, collation collations.ID) (sqltypes.
 }
 
 func addNumeric(v1, v2 EvalResult) EvalResult {
-	v1, v2 = makeNumericAndPrioritize(v1, v2)
+	v1, v2 = makeNumericAndprioritize(v1, v2)
 	switch v1.typ {
 	case sqltypes.Int64:
 		return intPlusInt(v1.ival, v2.ival)
@@ -336,7 +303,7 @@ func addNumeric(v1, v2 EvalResult) EvalResult {
 }
 
 func addNumericWithError(v1, v2 EvalResult) (EvalResult, error) {
-	v1, v2 = makeNumericAndPrioritize(v1, v2)
+	v1, v2 = makeNumericAndprioritize(v1, v2)
 	switch v1.typ {
 	case sqltypes.Int64:
 		return intPlusIntWithError(v1.ival, v2.ival)
@@ -347,7 +314,7 @@ func addNumericWithError(v1, v2 EvalResult) (EvalResult, error) {
 		case sqltypes.Uint64:
 			return uintPlusUintWithError(v1.uval, v2.uval)
 		}
-	case sqltypes.Float64, sqltypes.Decimal:
+	case sqltypes.Float64:
 		return floatPlusAny(v1.fval, v2), nil
 	}
 	return EvalResult{}, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid arithmetic between: %s %s", v1.Value().String(), v2.Value().String())
@@ -383,7 +350,7 @@ func subtractNumericWithError(i1, i2 EvalResult) (EvalResult, error) {
 }
 
 func multiplyNumericWithError(v1, v2 EvalResult) (EvalResult, error) {
-	v1, v2 = makeNumericAndPrioritize(v1, v2)
+	v1, v2 = makeNumericAndprioritize(v1, v2)
 	switch v1.typ {
 	case sqltypes.Int64:
 		return intTimesIntWithError(v1.ival, v2.ival)
@@ -417,9 +384,9 @@ func divideNumericWithError(i1, i2 EvalResult) (EvalResult, error) {
 	return EvalResult{}, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid arithmetic between: %s %s", v1.Value().String(), v2.Value().String())
 }
 
-// makeNumericAndPrioritize reorders the input parameters
+// makeNumericAndprioritize reorders the input parameters
 // to be Float64, Uint64, Int64.
-func makeNumericAndPrioritize(i1, i2 EvalResult) (EvalResult, EvalResult) {
+func makeNumericAndprioritize(i1, i2 EvalResult) (EvalResult, EvalResult) {
 	v1 := makeNumeric(i1)
 	v2 := makeNumeric(i2)
 	switch v1.typ {
@@ -435,19 +402,6 @@ func makeNumericAndPrioritize(i1, i2 EvalResult) (EvalResult, EvalResult) {
 	return v1, v2
 }
 
-func makeFloat(v EvalResult) EvalResult {
-	if sqltypes.IsIntegral(v.typ) {
-		return EvalResult{fval: float64(v.ival), typ: sqltypes.Float64}
-	}
-	if sqltypes.IsFloat(v.typ) {
-		return v
-	}
-	if fval, err := strconv.ParseFloat(string(v.bytes), 64); err == nil {
-		return EvalResult{fval: fval, typ: sqltypes.Float64}
-	}
-	return EvalResult{ival: 0, typ: sqltypes.Int64}
-}
-
 func makeNumeric(v EvalResult) EvalResult {
 	if sqltypes.IsNumber(v.typ) {
 		return v
@@ -478,7 +432,7 @@ overflow:
 func intPlusIntWithError(v1, v2 int64) (EvalResult, error) {
 	result := v1 + v2
 	if (result > v1) != (v2 > 0) {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT", "+")
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v + %v", "BIGINT", v1, v2)
 	}
 	return EvalResult{typ: sqltypes.Int64, ival: result}, nil
 }
@@ -487,7 +441,7 @@ func intMinusIntWithError(v1, v2 int64) (EvalResult, error) {
 	result := v1 - v2
 
 	if (result < v1) != (v2 > 0) {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT", "-")
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v - %v", "BIGINT", v1, v2)
 	}
 	return EvalResult{typ: sqltypes.Int64, ival: result}, nil
 }
@@ -495,7 +449,7 @@ func intMinusIntWithError(v1, v2 int64) (EvalResult, error) {
 func intTimesIntWithError(v1, v2 int64) (EvalResult, error) {
 	result := v1 * v2
 	if v1 != 0 && result/v1 != v2 {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT", "*")
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v * %v", "BIGINT", v1, v2)
 	}
 	return EvalResult{typ: sqltypes.Int64, ival: result}, nil
 
@@ -503,7 +457,7 @@ func intTimesIntWithError(v1, v2 int64) (EvalResult, error) {
 
 func intMinusUintWithError(v1 int64, v2 uint64) (EvalResult, error) {
 	if v1 < 0 || v1 < int64(v2) {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT UNSIGNED", "-")
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v - %v", "BIGINT UNSIGNED", v1, v2)
 	}
 	return uintMinusUintWithError(uint64(v1), v2)
 }
@@ -513,18 +467,17 @@ func uintPlusInt(v1 uint64, v2 int64) EvalResult {
 }
 
 func uintPlusIntWithError(v1 uint64, v2 int64) (EvalResult, error) {
-	result := v1 + uint64(v2)
-	if v2 < 0 && v1 < uint64(-v2) || v2 > 0 && (result < v1 || result < uint64(v2)) {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT UNSIGNED", "+")
+	if v2 < 0 && v1 < uint64(v2) {
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v + %v", "BIGINT UNSIGNED", v1, v2)
 	}
 	// convert to int -> uint is because for numeric operators (such as + or -)
 	// where one of the operands is an unsigned integer, the result is unsigned by default.
-	return EvalResult{typ: sqltypes.Uint64, uval: result}, nil
+	return uintPlusUintWithError(v1, uint64(v2))
 }
 
 func uintMinusIntWithError(v1 uint64, v2 int64) (EvalResult, error) {
 	if int64(v1) < v2 && v2 > 0 {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT UNSIGNED", "-")
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v - %v", "BIGINT UNSIGNED", v1, v2)
 	}
 	// uint - (- int) = uint + int
 	if v2 < 0 {
@@ -535,7 +488,7 @@ func uintMinusIntWithError(v1 uint64, v2 int64) (EvalResult, error) {
 
 func uintTimesIntWithError(v1 uint64, v2 int64) (EvalResult, error) {
 	if v2 < 0 || int64(v1) < 0 {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT UNSIGNED", "*")
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v * %v", "BIGINT UNSIGNED", v1, v2)
 	}
 	return uintTimesUintWithError(v1, uint64(v2))
 }
@@ -550,8 +503,8 @@ func uintPlusUint(v1, v2 uint64) EvalResult {
 
 func uintPlusUintWithError(v1, v2 uint64) (EvalResult, error) {
 	result := v1 + v2
-	if result < v1 || result < v2 {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT UNSIGNED", "+")
+	if result < v2 {
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v + %v", "BIGINT UNSIGNED", v1, v2)
 	}
 	return EvalResult{typ: sqltypes.Uint64, uval: result}, nil
 }
@@ -559,7 +512,7 @@ func uintPlusUintWithError(v1, v2 uint64) (EvalResult, error) {
 func uintMinusUintWithError(v1, v2 uint64) (EvalResult, error) {
 	result := v1 - v2
 	if v2 > v1 {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT UNSIGNED", "-")
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v - %v", "BIGINT UNSIGNED", v1, v2)
 	}
 
 	return EvalResult{typ: sqltypes.Uint64, uval: result}, nil
@@ -568,7 +521,7 @@ func uintMinusUintWithError(v1, v2 uint64) (EvalResult, error) {
 func uintTimesUintWithError(v1, v2 uint64) (EvalResult, error) {
 	result := v1 * v2
 	if result < v2 || result < v1 {
-		return EvalResult{}, dataOutOfRangeError(v1, v2, "BIGINT UNSIGNED", "*")
+		return EvalResult{}, vterrors.NewErrorf(vtrpcpb.Code_INVALID_ARGUMENT, vterrors.DataOutOfRange, "%s value is out of range in %v * %v", "BIGINT UNSIGNED", v1, v2)
 	}
 	return EvalResult{typ: sqltypes.Uint64, uval: result}, nil
 }
diff --git a/go/vt/vtgate/evalengine/binary.go b/go/vt/vtgate/evalengine/binary.go
deleted file mode 100644
index 8637f7b302..0000000000
--- a/go/vt/vtgate/evalengine/binary.go
+++ /dev/null
@@ -1,139 +0,0 @@
-/*
-Copyright 2021 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 evalengine
-
-import (
-	"vitess.io/vitess/go/sqltypes"
-	querypb "vitess.io/vitess/go/vt/proto/query"
-)
-
-type (
-	// BinaryOp allows binary expressions to not have to evaluate child expressions - this is done by the BinaryExpr
-	BinaryOp interface {
-		Evaluate(left, right EvalResult) (EvalResult, error)
-		Type(left querypb.Type) querypb.Type
-		String() string
-	}
-
-	BinaryExpr struct {
-		Op          BinaryOp
-		Left, Right Expr
-	}
-
-	// Binary ops
-	Addition       struct{}
-	Subtraction    struct{}
-	Multiplication struct{}
-	Division       struct{}
-)
-
-var _ BinaryOp = (*Addition)(nil)
-var _ BinaryOp = (*Subtraction)(nil)
-var _ BinaryOp = (*Multiplication)(nil)
-var _ BinaryOp = (*Division)(nil)
-
-// Evaluate implements the Expr interface
-func (b *BinaryExpr) Evaluate(env ExpressionEnv) (EvalResult, error) {
-	lVal, err := b.Left.Evaluate(env)
-	if err != nil {
-		return EvalResult{}, err
-	}
-	rVal, err := b.Right.Evaluate(env)
-	if err != nil {
-		return EvalResult{}, err
-	}
-	return b.Op.Evaluate(lVal, rVal)
-}
-
-// Type implements the Expr interface
-func (b *BinaryExpr) Type(env ExpressionEnv) (querypb.Type, error) {
-	ltype, err := b.Left.Type(env)
-	if err != nil {
-		return 0, err
-	}
-	rtype, err := b.Right.Type(env)
-	if err != nil {
-		return 0, err
-	}
-	typ := mergeNumericalTypes(ltype, rtype)
-	return b.Op.Type(typ), nil
-}
-
-// String implements the Expr interface
-func (b *BinaryExpr) String() string {
-	return b.Left.String() + " " + b.Op.String() + " " + b.Right.String()
-}
-
-// Evaluate implements the BinaryOp interface
-func (a *Addition) Evaluate(left, right EvalResult) (EvalResult, error) {
-	return addNumericWithError(left, right)
-}
-
-// Evaluate implements the BinaryOp interface
-func (s *Subtraction) Evaluate(left, right EvalResult) (EvalResult, error) {
-	return subtractNumericWithError(left, right)
-}
-
-// Evaluate implements the BinaryOp interface
-func (m *Multiplication) Evaluate(left, right EvalResult) (EvalResult, error) {
-	return multiplyNumericWithError(left, right)
-}
-
-// Evaluate implements the BinaryOp interface
-func (d *Division) Evaluate(left, right EvalResult) (EvalResult, error) {
-	return divideNumericWithError(left, right)
-}
-
-// Type implements the BinaryOp interface
-func (a *Addition) Type(left querypb.Type) querypb.Type {
-	return left
-}
-
-// Type implements the BinaryOp interface
-func (s *Subtraction) Type(left querypb.Type) querypb.Type {
-	return left
-}
-
-// Type implements the BinaryOp interface
-func (m *Multiplication) Type(left querypb.Type) querypb.Type {
-	return left
-}
-
-// Type implements the BinaryOp interface
-func (d *Division) Type(querypb.Type) querypb.Type {
-	return sqltypes.Float64
-}
-
-// String implements the BinaryOp interface
-func (a *Addition) String() string {
-	return "+"
-}
-
-// String implements the BinaryOp interface
-func (s *Subtraction) String() string {
-	return "-"
-}
-
-// String implements the BinaryOp interface
-func (m *Multiplication) String() string {
-	return "*"
-}
-
-// String implements the BinaryOp interface
-func (d *Division) String() string {
-	return "/"
-}
diff --git a/go/vt/vtgate/evalengine/cached_size.go b/go/vt/vtgate/evalengine/cached_size.go
index dced4b9e4d..daee0218b6 100644
--- a/go/vt/vtgate/evalengine/cached_size.go
+++ b/go/vt/vtgate/evalengine/cached_size.go
@@ -23,7 +23,7 @@ type cachedObject interface {
 	CachedSize(alloc bool) int64
 }
 
-func (cached *BinaryExpr) CachedSize(alloc bool) int64 {
+func (cached *BinaryOp) CachedSize(alloc bool) int64 {
 	if cached == nil {
 		return int64(0)
 	}
@@ -31,8 +31,8 @@ func (cached *BinaryExpr) CachedSize(alloc bool) int64 {
 	if alloc {
 		size += int64(48)
 	}
-	// field Op vitess.io/vitess/go/vt/vtgate/evalengine.BinaryOp
-	if cc, ok := cached.Op.(cachedObject); ok {
+	// field Expr vitess.io/vitess/go/vt/vtgate/evalengine.BinaryExpr
+	if cc, ok := cached.Expr.(cachedObject); ok {
 		size += cc.CachedSize(true)
 	}
 	// field Left vitess.io/vitess/go/vt/vtgate/evalengine.Expr
@@ -51,7 +51,7 @@ func (cached *BindVariable) CachedSize(alloc bool) int64 {
 	}
 	size := int64(0)
 	if alloc {
-		size += int64(24)
+		size += int64(16)
 	}
 	// field Key string
 	size += hack.RuntimeAllocSize(int64(len(cached.Key)))
@@ -63,29 +63,7 @@ func (cached *Column) CachedSize(alloc bool) int64 {
 	}
 	size := int64(0)
 	if alloc {
-		size += int64(16)
-	}
-	return size
-}
-func (cached *ComparisonExpr) CachedSize(alloc bool) int64 {
-	if cached == nil {
-		return int64(0)
-	}
-	size := int64(0)
-	if alloc {
-		size += int64(48)
-	}
-	// field Op vitess.io/vitess/go/vt/vtgate/evalengine.ComparisonOp
-	if cc, ok := cached.Op.(cachedObject); ok {
-		size += cc.CachedSize(true)
-	}
-	// field Left vitess.io/vitess/go/vt/vtgate/evalengine.Expr
-	if cc, ok := cached.Left.(cachedObject); ok {
-		size += cc.CachedSize(true)
-	}
-	// field Right vitess.io/vitess/go/vt/vtgate/evalengine.Expr
-	if cc, ok := cached.Right.(cachedObject); ok {
-		size += cc.CachedSize(true)
+		size += int64(8)
 	}
 	return size
 }
@@ -107,7 +85,7 @@ func (cached *Literal) CachedSize(alloc bool) int64 {
 	}
 	size := int64(0)
 	if alloc {
-		size += int64(80)
+		size += int64(64)
 	}
 	// field Val vitess.io/vitess/go/vt/vtgate/evalengine.EvalResult
 	size += cached.Val.CachedSize(false)
diff --git a/go/vt/vtgate/evalengine/comparisons.go b/go/vt/vtgate/evalengine/comparisons.go
deleted file mode 100644
index 22350605ef..0000000000
--- a/go/vt/vtgate/evalengine/comparisons.go
+++ /dev/null
@@ -1,494 +0,0 @@
-/*
-Copyright 2021 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 evalengine
-
-import (
-	"vitess.io/vitess/go/sqltypes"
-	querypb "vitess.io/vitess/go/vt/proto/query"
-	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
-	"vitess.io/vitess/go/vt/vterrors"
-)
-
-type (
-	// ComparisonOp interfaces all the possible comparison operations we have, it eases the job of ComparisonExpr
-	// when evaluating the whole comparison
-	ComparisonOp interface {
-		Evaluate(left, right EvalResult) (EvalResult, error)
-		IsTrue(left, right EvalResult) (bool, error)
-		Type() querypb.Type
-		String() string
-	}
-
-	ComparisonExpr struct {
-		Op          ComparisonOp
-		Left, Right Expr
-	}
-
-	EqualOp         struct{}
-	NotEqualOp      struct{}
-	NullSafeEqualOp struct{}
-	LessThanOp      struct{}
-	LessEqualOp     struct{}
-	GreaterThanOp   struct{}
-	GreaterEqualOp  struct{}
-	InOp            struct{}
-	NotInOp         struct{}
-	LikeOp          struct{}
-	NotLikeOp       struct{}
-	RegexpOp        struct{}
-	NotRegexpOp     struct{}
-)
-
-var (
-	resultTrue  = EvalResult{typ: sqltypes.Int32, ival: 1}
-	resultFalse = EvalResult{typ: sqltypes.Int32, ival: 0}
-	resultNull  = EvalResult{typ: sqltypes.Null}
-)
-
-var _ ComparisonOp = (*EqualOp)(nil)
-var _ ComparisonOp = (*NotEqualOp)(nil)
-var _ ComparisonOp = (*NullSafeEqualOp)(nil)
-var _ ComparisonOp = (*LessThanOp)(nil)
-var _ ComparisonOp = (*LessEqualOp)(nil)
-var _ ComparisonOp = (*GreaterThanOp)(nil)
-var _ ComparisonOp = (*GreaterEqualOp)(nil)
-var _ ComparisonOp = (*InOp)(nil)
-var _ ComparisonOp = (*NotInOp)(nil)
-var _ ComparisonOp = (*LikeOp)(nil)
-var _ ComparisonOp = (*NotLikeOp)(nil)
-var _ ComparisonOp = (*RegexpOp)(nil)
-var _ ComparisonOp = (*NotRegexpOp)(nil)
-
-func (c *ComparisonExpr) evaluateComparisonExprs(env ExpressionEnv) (EvalResult, EvalResult, error) {
-	var lVal, rVal EvalResult
-	var err error
-	if lVal, err = c.Left.Evaluate(env); err != nil {
-		return EvalResult{}, EvalResult{}, err
-	}
-	if rVal, err = c.Right.Evaluate(env); err != nil {
-		return EvalResult{}, EvalResult{}, err
-	}
-	return lVal, rVal, nil
-}
-
-func hasNullEvalResult(l, r EvalResult) bool {
-	return l.typ == sqltypes.Null || r.typ == sqltypes.Null
-}
-
-func evalResultsAreStrings(l, r EvalResult) bool {
-	return (sqltypes.IsText(l.typ) || sqltypes.IsBinary(l.typ)) && (sqltypes.IsText(r.typ) || sqltypes.IsBinary(r.typ))
-}
-
-func evalResultsAreSameNumericType(l, r EvalResult) bool {
-	if sqltypes.IsIntegral(l.typ) && sqltypes.IsIntegral(r.typ) {
-		return true
-	}
-	if sqltypes.IsFloat(l.typ) && sqltypes.IsFloat(r.typ) {
-		return true
-	}
-	if l.typ == sqltypes.Decimal && r.typ == sqltypes.Decimal {
-		return true
-	}
-	return false
-}
-
-func needsDecimalHandling(l, r EvalResult) bool {
-	return l.typ == sqltypes.Decimal && (sqltypes.IsIntegral(r.typ) || sqltypes.IsFloat(r.typ)) ||
-		r.typ == sqltypes.Decimal && (sqltypes.IsIntegral(l.typ) || sqltypes.IsFloat(l.typ))
-}
-
-func evalResultsAreDates(l, r EvalResult) bool {
-	return sqltypes.IsDate(l.typ) && sqltypes.IsDate(r.typ)
-}
-
-func evalResultsAreDateAndString(l, r EvalResult) bool {
-	return sqltypes.IsDate(l.typ) && (sqltypes.IsText(r.typ) || sqltypes.IsBinary(r.typ)) ||
-		(sqltypes.IsText(l.typ) || sqltypes.IsBinary(l.typ)) && sqltypes.IsDate(r.typ)
-}
-
-func evalResultsAreDateAndNumeric(l, r EvalResult) bool {
-	return sqltypes.IsDate(l.typ) && sqltypes.IsNumber(r.typ) || sqltypes.IsNumber(l.typ) && sqltypes.IsDate(r.typ)
-}
-
-// For more details on comparison expression evaluation and type conversion:
-// 		- https://dev.mysql.com/doc/refman/8.0/en/type-conversion.html
-func executeComparison(lVal, rVal EvalResult) (int, error) {
-	switch {
-	case evalResultsAreStrings(lVal, rVal):
-		return compareStrings(lVal, rVal)
-
-	case evalResultsAreSameNumericType(lVal, rVal), needsDecimalHandling(lVal, rVal):
-		return compareNumeric(lVal, rVal)
-
-	case evalResultsAreDates(lVal, rVal):
-		return compareDates(lVal, rVal)
-
-	case evalResultsAreDateAndString(lVal, rVal):
-		return compareDateAndString(lVal, rVal)
-
-	case evalResultsAreDateAndNumeric(lVal, rVal):
-		// TODO: support comparison between a date and a numeric value
-		// 		queries like the ones below should be supported:
-		// 			- select 1 where 20210101 = cast("2021-01-01" as date)
-		// 			- select 1 where 2021210101 = cast("2021-01-01" as date)
-		// 			- select 1 where 104200 = cast("10:42:00" as time)
-		return 0, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "cannot compare a date with a numeric value")
-
-	default:
-		// Quoting MySQL Docs:
-		//
-		// 		"In all other cases, the arguments are compared as floating-point (real) numbers.
-		// 		For example, a comparison of string and numeric operands takes place as a
-		// 		comparison of floating-point numbers."
-		//
-		//		https://dev.mysql.com/doc/refman/8.0/en/type-conversion.html
-		return compareNumeric(makeFloat(lVal), makeFloat(rVal))
-	}
-}
-
-// Evaluate implements the Expr interface
-func (c *ComparisonExpr) Evaluate(env ExpressionEnv) (EvalResult, error) {
-	if c.Op == nil {
-		return EvalResult{}, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "a comparison expression needs a comparison operator")
-	}
-
-	lVal, rVal, err := c.evaluateComparisonExprs(env)
-	if err != nil {
-		return EvalResult{}, err
-	}
-
-	if hasNullEvalResult(lVal, rVal) {
-		// Comparison operation NullSafeEqual (<=>) does not care if one or two sides are NULL
-		if _, isNullsafe := c.Op.(*NullSafeEqualOp); !isNullsafe {
-			// If a side of the comparison is NULL, result will always be NULL
-			return resultNull, nil
-		}
-	}
-	return c.Op.Evaluate(lVal, rVal)
-}
-
-// Type implements the Expr interface
-func (c *ComparisonExpr) Type(ExpressionEnv) (querypb.Type, error) {
-	return querypb.Type_INT32, nil
-}
-
-// String implements the Expr interface
-func (c *ComparisonExpr) String() string {
-	return c.Left.String() + " " + c.Op.String() + " " + c.Right.String()
-}
-
-// Evaluate implements the ComparisonOp interface
-func (e *EqualOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	if out, err := e.IsTrue(left, right); err != nil || !out {
-		return resultFalse, err
-	}
-	return resultTrue, nil
-}
-
-// IsTrue implements the ComparisonOp interface
-func (e *EqualOp) IsTrue(left, right EvalResult) (bool, error) {
-	numeric, err := executeComparison(left, right)
-	if err != nil {
-		return false, err
-	}
-	return numeric == 0, nil
-}
-
-// Type implements the ComparisonOp interface
-func (e *EqualOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (e *EqualOp) String() string {
-	return "="
-}
-
-// Evaluate implements the ComparisonOp interface
-func (n *NotEqualOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	if out, err := n.IsTrue(left, right); err != nil || !out {
-		return resultFalse, err
-	}
-	return resultTrue, nil
-}
-
-// IsTrue implements the ComparisonOp interface
-func (n *NotEqualOp) IsTrue(left, right EvalResult) (bool, error) {
-	numeric, err := executeComparison(left, right)
-	if err != nil {
-		return false, err
-	}
-	return numeric != 0, nil
-}
-
-// Type implements the ComparisonOp interface
-func (n *NotEqualOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (n *NotEqualOp) String() string {
-	return "!="
-}
-
-// Evaluate implements the ComparisonOp interface
-func (n *NullSafeEqualOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	panic("implement me")
-}
-
-// IsTrue implements the ComparisonOp interface
-func (n *NullSafeEqualOp) IsTrue(left, right EvalResult) (bool, error) {
-	return false, nil
-}
-
-// Type implements the ComparisonOp interface
-func (n *NullSafeEqualOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (n *NullSafeEqualOp) String() string {
-	return "<=>"
-}
-
-// Evaluate implements the ComparisonOp interface
-func (l *LessThanOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	if out, err := l.IsTrue(left, right); err != nil || !out {
-		return resultFalse, err
-	}
-	return resultTrue, nil
-}
-
-// IsTrue implements the ComparisonOp interface
-func (l *LessThanOp) IsTrue(left, right EvalResult) (bool, error) {
-	numeric, err := executeComparison(left, right)
-	if err != nil {
-		return false, err
-	}
-	return numeric < 0, nil
-}
-
-// Type implements the ComparisonOp interface
-func (l *LessThanOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (l *LessThanOp) String() string {
-	return "<"
-}
-
-// Evaluate implements the ComparisonOp interface
-func (l *LessEqualOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	if out, err := l.IsTrue(left, right); err != nil || !out {
-		return resultFalse, err
-	}
-	return resultTrue, nil
-}
-
-// IsTrue implements the ComparisonOp interface
-func (l *LessEqualOp) IsTrue(left, right EvalResult) (bool, error) {
-	numeric, err := executeComparison(left, right)
-	if err != nil {
-		return false, err
-	}
-	return numeric <= 0, nil
-}
-
-// Type implements the ComparisonOp interface
-func (l *LessEqualOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (l *LessEqualOp) String() string {
-	return "<="
-}
-
-// Evaluate implements the ComparisonOp interface
-func (g *GreaterThanOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	if out, err := g.IsTrue(left, right); err != nil || !out {
-		return resultFalse, err
-	}
-	return resultTrue, nil
-}
-
-// IsTrue implements the ComparisonOp interface
-func (g *GreaterThanOp) IsTrue(left, right EvalResult) (bool, error) {
-	numeric, err := executeComparison(left, right)
-	if err != nil {
-		return false, err
-	}
-	return numeric > 0, nil
-}
-
-// Type implements the ComparisonOp interface
-func (g *GreaterThanOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (g *GreaterThanOp) String() string {
-	return ">"
-}
-
-// Evaluate implements the ComparisonOp interface
-func (g *GreaterEqualOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	if out, err := g.IsTrue(left, right); err != nil || !out {
-		return resultFalse, err
-	}
-	return resultTrue, nil
-}
-
-// IsTrue implements the ComparisonOp interface
-func (g *GreaterEqualOp) IsTrue(left, right EvalResult) (bool, error) {
-	numeric, err := executeComparison(left, right)
-	if err != nil {
-		return false, err
-	}
-	return numeric >= 0, nil
-}
-
-// Type implements the ComparisonOp interface
-func (g *GreaterEqualOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (g *GreaterEqualOp) String() string {
-	return ">="
-}
-
-// Evaluate implements the ComparisonOp interface
-func (i *InOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	panic("implement me")
-}
-
-// IsTrue implements the ComparisonOp interface
-func (i *InOp) IsTrue(left, right EvalResult) (bool, error) {
-	return false, nil
-}
-
-// Type implements the ComparisonOp interface
-func (i *InOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (i *InOp) String() string {
-	return "in"
-}
-
-// Evaluate implements the ComparisonOp interface
-func (n *NotInOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	panic("implement me")
-}
-
-// IsTrue implements the ComparisonOp interface
-func (n *NotInOp) IsTrue(left, right EvalResult) (bool, error) {
-	return false, nil
-}
-
-// Type implements the ComparisonOp interface
-func (n *NotInOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (n *NotInOp) String() string {
-	return "not in"
-}
-
-// Evaluate implements the ComparisonOp interface
-func (l *LikeOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	panic("implement me")
-}
-
-// IsTrue implements the ComparisonOp interface
-func (l *LikeOp) IsTrue(left, right EvalResult) (bool, error) {
-	return false, nil
-}
-
-// Type implements the ComparisonOp interface
-func (l *LikeOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (l *LikeOp) String() string {
-	return "like"
-}
-
-// Evaluate implements the ComparisonOp interface
-func (n *NotLikeOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	panic("implement me")
-}
-
-// IsTrue implements the ComparisonOp interface
-func (n *NotLikeOp) IsTrue(left, right EvalResult) (bool, error) {
-	return false, nil
-}
-
-// Type implements the ComparisonOp interface
-func (n *NotLikeOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (n *NotLikeOp) String() string {
-	return "not like"
-}
-
-// Evaluate implements the ComparisonOp interface
-func (r *RegexpOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	panic("implement me")
-}
-
-// IsTrue implements the ComparisonOp interface
-func (r *RegexpOp) IsTrue(left, right EvalResult) (bool, error) {
-	return false, nil
-}
-
-// Type implements the ComparisonOp interface
-func (r *RegexpOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (r *RegexpOp) String() string {
-	return "regexp"
-}
-
-// Evaluate implements the ComparisonOp interface
-func (n *NotRegexpOp) Evaluate(left, right EvalResult) (EvalResult, error) {
-	panic("implement me")
-}
-
-// IsTrue implements the ComparisonOp interface
-func (n *NotRegexpOp) IsTrue(left, right EvalResult) (bool, error) {
-	return false, nil
-}
-
-// Type implements the ComparisonOp interface
-func (n *NotRegexpOp) Type() querypb.Type {
-	return querypb.Type_INT32
-}
-
-// String implements the ComparisonOp interface
-func (n *NotRegexpOp) String() string {
-	return "not regexp"
-}
diff --git a/go/vt/vtgate/evalengine/convert.go b/go/vt/vtgate/evalengine/convert.go
deleted file mode 100644
index 2979a1ac29..0000000000
--- a/go/vt/vtgate/evalengine/convert.go
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
-Copyright 2021 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 evalengine
-
-import (
-	"vitess.io/vitess/go/mysql/collations"
-	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
-	"vitess.io/vitess/go/vt/sqlparser"
-	"vitess.io/vitess/go/vt/vterrors"
-)
-
-type (
-	ConverterLookup interface {
-		ColumnLookup(col *sqlparser.ColName) (int, error)
-		CollationIDLookup(expr sqlparser.Expr) collations.ID
-	}
-)
-
-var ErrConvertExprNotSupported = "expr cannot be converted, not supported"
-
-// translateComparisonOperator takes in a sqlparser.ComparisonExprOperator and
-// returns the corresponding ComparisonOp
-func translateComparisonOperator(op sqlparser.ComparisonExprOperator) ComparisonOp {
-	switch op {
-	case sqlparser.EqualOp:
-		return &EqualOp{}
-	case sqlparser.LessThanOp:
-		return &LessThanOp{}
-	case sqlparser.GreaterThanOp:
-		return &GreaterThanOp{}
-	case sqlparser.LessEqualOp:
-		return &LessEqualOp{}
-	case sqlparser.GreaterEqualOp:
-		return &GreaterEqualOp{}
-	case sqlparser.NotEqualOp:
-		return &NotEqualOp{}
-	case sqlparser.NullSafeEqualOp:
-		return &NullSafeEqualOp{}
-	case sqlparser.InOp:
-		return &InOp{}
-	case sqlparser.NotInOp:
-		return &NotInOp{}
-	case sqlparser.LikeOp:
-		return &LikeOp{}
-	case sqlparser.NotLikeOp:
-		return &NotLikeOp{}
-	case sqlparser.RegexpOp:
-		return &RegexpOp{}
-	case sqlparser.NotRegexpOp:
-		return &NotRegexpOp{}
-	default:
-		return nil
-	}
-}
-
-func getCollation(expr sqlparser.Expr, lookup ConverterLookup) collations.ID {
-	collation := collations.Unknown
-	if lookup != nil {
-		collation = lookup.CollationIDLookup(expr)
-	}
-	return collation
-}
-
-// Convert converts between AST expressions and executable expressions
-func Convert(e sqlparser.Expr, lookup ConverterLookup) (Expr, error) {
-	switch node := e.(type) {
-	case *sqlparser.ColName:
-		if lookup == nil {
-			return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "%s: cannot lookup column", ErrConvertExprNotSupported)
-		}
-		idx, err := lookup.ColumnLookup(node)
-		if err != nil {
-			return nil, err
-		}
-		collation := getCollation(node, lookup)
-		return NewColumn(idx, collation), nil
-	case *sqlparser.ComparisonExpr:
-		left, err := Convert(node.Left, lookup)
-		if err != nil {
-			return nil, err
-		}
-		right, err := Convert(node.Right, lookup)
-		if err != nil {
-			return nil, err
-		}
-		return &ComparisonExpr{
-			Op:    translateComparisonOperator(node.Operator),
-			Left:  left,
-			Right: right,
-		}, nil
-	case sqlparser.Argument:
-		collation := collations.Unknown
-		if lookup != nil {
-			collation = lookup.CollationIDLookup(e)
-		}
-		return NewBindVar(string(node), collation), nil
-	case *sqlparser.Literal:
-		switch node.Type {
-		case sqlparser.IntVal:
-			return NewLiteralIntFromBytes(node.Bytes())
-		case sqlparser.FloatVal:
-			return NewLiteralFloatFromBytes(node.Bytes())
-		case sqlparser.StrVal:
-			collation := getCollation(e, lookup)
-			return NewLiteralString(node.Bytes(), collation), nil
-		case sqlparser.HexNum:
-			return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "%s: hexadecimal value: %s", ErrConvertExprNotSupported, node.Val)
-		}
-	case sqlparser.BoolVal:
-		if node {
-			return NewLiteralIntFromBytes([]byte("1"))
-		}
-		return NewLiteralIntFromBytes([]byte("0"))
-	case *sqlparser.BinaryExpr:
-		var op BinaryOp
-		switch node.Operator {
-		case sqlparser.PlusOp:
-			op = &Addition{}
-		case sqlparser.MinusOp:
-			op = &Subtraction{}
-		case sqlparser.MultOp:
-			op = &Multiplication{}
-		case sqlparser.DivOp:
-			op = &Division{}
-		default:
-			return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "%s: %T", ErrConvertExprNotSupported, e)
-		}
-		left, err := Convert(node.Left, lookup)
-		if err != nil {
-			return nil, err
-		}
-		right, err := Convert(node.Right, lookup)
-		if err != nil {
-			return nil, err
-		}
-		return &BinaryExpr{
-			Op:    op,
-			Left:  left,
-			Right: right,
-		}, nil
-
-	}
-	return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "%s: %T", ErrConvertExprNotSupported, e)
-}
diff --git a/go/vt/vtgate/evalengine/evalengine.go b/go/vt/vtgate/evalengine/evalengine.go
index 6e51467c95..f5e123b5ab 100644
--- a/go/vt/vtgate/evalengine/evalengine.go
+++ b/go/vt/vtgate/evalengine/evalengine.go
@@ -17,10 +17,6 @@ limitations under the License.
 package evalengine
 
 import (
-	"time"
-
-	"vitess.io/vitess/go/mysql/collations"
-
 	"vitess.io/vitess/go/sqltypes"
 
 	"strconv"
@@ -166,11 +162,7 @@ func newEvalResult(v sqltypes.Value) (EvalResult, error) {
 		if err != nil {
 			return EvalResult{}, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "%v", err)
 		}
-		typ := sqltypes.Float64
-		if v.Type() == sqltypes.Decimal {
-			typ = sqltypes.Decimal
-		}
-		return EvalResult{fval: fval, typ: typ}, nil
+		return EvalResult{fval: fval, typ: sqltypes.Float64}, nil
 	default:
 		return EvalResult{typ: v.Type(), bytes: raw}, nil
 	}
@@ -261,8 +253,7 @@ func hashCode(v EvalResult) int64 {
 }
 
 func compareNumeric(v1, v2 EvalResult) (int, error) {
-	// Equalize the types the same way MySQL does
-	// https://dev.mysql.com/doc/refman/8.0/en/type-conversion.html
+	// Equalize the types.
 	switch v1.typ {
 	case sqltypes.Int64:
 		switch v2.typ {
@@ -271,8 +262,8 @@ func compareNumeric(v1, v2 EvalResult) (int, error) {
 				return -1, nil
 			}
 			v1 = EvalResult{typ: sqltypes.Uint64, uval: uint64(v1.ival)}
-		case sqltypes.Float64, sqltypes.Decimal:
-			v1 = EvalResult{typ: v2.typ, fval: float64(v1.ival)}
+		case sqltypes.Float64:
+			v1 = EvalResult{typ: sqltypes.Float64, fval: float64(v1.ival)}
 		}
 	case sqltypes.Uint64:
 		switch v2.typ {
@@ -281,32 +272,15 @@ func compareNumeric(v1, v2 EvalResult) (int, error) {
 				return 1, nil
 			}
 			v2 = EvalResult{typ: sqltypes.Uint64, uval: uint64(v2.ival)}
-		case sqltypes.Float64, sqltypes.Decimal:
-			v1 = EvalResult{typ: v2.typ, fval: float64(v1.uval)}
+		case sqltypes.Float64:
+			v1 = EvalResult{typ: sqltypes.Float64, fval: float64(v1.uval)}
 		}
 	case sqltypes.Float64:
 		switch v2.typ {
 		case sqltypes.Int64:
 			v2 = EvalResult{typ: sqltypes.Float64, fval: float64(v2.ival)}
 		case sqltypes.Uint64:
-			if v1.fval < 0 {
-				return -1, nil
-			}
 			v2 = EvalResult{typ: sqltypes.Float64, fval: float64(v2.uval)}
-		case sqltypes.Decimal:
-			v2.typ = sqltypes.Float64
-		}
-	case sqltypes.Decimal:
-		switch v2.typ {
-		case sqltypes.Int64:
-			v2 = EvalResult{typ: sqltypes.Decimal, fval: float64(v2.ival)}
-		case sqltypes.Uint64:
-			if v1.fval < 0 {
-				return -1, nil
-			}
-			v2 = EvalResult{typ: sqltypes.Decimal, fval: float64(v2.uval)}
-		case sqltypes.Float64:
-			v1.typ = sqltypes.Float64
 		}
 	}
 
@@ -326,7 +300,7 @@ func compareNumeric(v1, v2 EvalResult) (int, error) {
 		case v1.uval < v2.uval:
 			return -1, nil
 		}
-	case sqltypes.Float64, sqltypes.Decimal:
+	case sqltypes.Float64:
 		switch {
 		case v1.fval == v2.fval:
 			return 0, nil
@@ -338,113 +312,3 @@ func compareNumeric(v1, v2 EvalResult) (int, error) {
 	// v1>v2
 	return 1, nil
 }
-
-func parseDate(expr EvalResult) (t time.Time, err error) {
-	switch expr.typ {
-	case sqltypes.Date:
-		t, err = time.Parse("2006-01-02", string(expr.bytes))
-	case sqltypes.Timestamp, sqltypes.Datetime:
-		t, err = time.Parse("2006-01-02 15:04:05", string(expr.bytes))
-	case sqltypes.Time:
-		t, err = time.Parse("15:04:05", string(expr.bytes))
-		if err == nil {
-			now := time.Now()
-			// setting the date to today's date, because we use AddDate on t
-			// which is "0000-01-01 xx:xx:xx", we do minus one on the month
-			// and day to take into account the 01 in both month and day of t
-			t = t.AddDate(now.Year(), int(now.Month()-1), now.Day()-1)
-		}
-	}
-	return
-}
-
-// matchExprWithAnyDateFormat formats the given expr (usually a string) to a date using the first format
-// that does not return an error.
-func matchExprWithAnyDateFormat(expr EvalResult) (t time.Time, err error) {
-	layouts := []string{"2006-01-02", "2006-01-02 15:04:05", "15:04:05"}
-	for _, layout := range layouts {
-		t, err = time.Parse(layout, string(expr.bytes))
-		if err == nil {
-			if layout == "15:04:05" {
-				now := time.Now()
-				// setting the date to today's date, because we use AddDate on t
-				// which is "0000-01-01 xx:xx:xx", we do minus one on the month
-				// and day to take into account the 01 in both month and day of t
-				t = t.AddDate(now.Year(), int(now.Month()-1), now.Day()-1)
-			}
-			return
-		}
-	}
-	return
-}
-
-// Date comparison based on:
-// 		- https://dev.mysql.com/doc/refman/8.0/en/type-conversion.html
-// 		- https://dev.mysql.com/doc/refman/8.0/en/date-and-time-type-conversion.html
-func compareDates(l, r EvalResult) (int, error) {
-	lTime, err := parseDate(l)
-	if err != nil {
-		return 0, err
-	}
-	rTime, err := parseDate(r)
-	if err != nil {
-		return 0, err
-	}
-
-	return compareGoTimes(lTime, rTime)
-}
-
-func compareDateAndString(l, r EvalResult) (int, error) {
-	var lTime, rTime time.Time
-	var err error
-	switch {
-	case sqltypes.IsDate(l.typ):
-		lTime, err = parseDate(l)
-		if err != nil {
-			return 0, err
-		}
-		rTime, err = matchExprWithAnyDateFormat(r)
-		if err != nil {
-			return 0, err
-		}
-	case sqltypes.IsText(l.typ) || sqltypes.IsBinary(l.typ):
-		rTime, err = parseDate(r)
-		if err != nil {
-			return 0, err
-		}
-		lTime, err = matchExprWithAnyDateFormat(l)
-		if err != nil {
-			return 0, err
-		}
-	}
-	return compareGoTimes(lTime, rTime)
-}
-
-func compareGoTimes(lTime, rTime time.Time) (int, error) {
-	if lTime.Before(rTime) {
-		return -1, nil
-	}
-	if lTime.After(rTime) {
-		return 1, nil
-	}
-	return 0, nil
-}
-
-// More on string collations coercibility on MySQL documentation:
-// 		- https://dev.mysql.com/doc/refman/8.0/en/charset-collation-coercibility.html
-func compareStrings(l, r EvalResult) (int, error) {
-	// If one of the strings has an unknown collation we fail, though such error should
-	// already be handled before the execution by the planner.
-	if l.collation == collations.Unknown || r.collation == collations.Unknown {
-		return 0, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "cannot compare strings with an unknown collation")
-	}
-
-	// We cannot compare different collations for now, so we fail
-	// TODO: support multiple collations comparison
-	if r.collation != l.collation {
-		return 0, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "cannot compare strings with different collations")
-	}
-
-	collation := collations.Default().LookupByID(l.collation)
-	return collation.Collate(l.bytes, r.bytes, false), nil
-}
diff --git a/go/vt/vtgate/evalengine/expressions.go b/go/vt/vtgate/evalengine/expressions.go
index 6f9e7503cf..a32badee48 100644
--- a/go/vt/vtgate/evalengine/expressions.go
+++ b/go/vt/vtgate/evalengine/expressions.go
@@ -20,8 +20,6 @@ import (
 	"fmt"
 	"strconv"
 
-	"vitess.io/vitess/go/mysql/collations"
-
 	"vitess.io/vitess/go/sqltypes"
 
 	querypb "vitess.io/vitess/go/vt/proto/query"
@@ -31,16 +29,14 @@ import (
 
 type (
 	EvalResult struct {
-		typ       querypb.Type
-		ival      int64
-		uval      uint64
-		fval      float64
-		bytes     []byte
-		collation collations.ID
+		typ   querypb.Type
+		ival  int64
+		uval  uint64
+		fval  float64
+		bytes []byte
 	}
-
-	// ExpressionEnv contains the environment that the expression
-	// evaluates in, such as the current row and bindvars
+	//ExpressionEnv contains the environment that the expression
+	//evaluates in, such as the current row and bindvars
 	ExpressionEnv struct {
 		BindVars map[string]*querypb.BindVariable
 		Row      []sqltypes.Value
@@ -53,35 +49,35 @@ type (
 		String() string
 	}
 
-	// Expressions
-	Null    struct{}
-	Literal struct {
-		Val       EvalResult
-		Collation collations.ID
-	}
-	BindVariable struct {
-		Key       string
-		Collation collations.ID
+	//BinaryExpr allows binary expressions to not have to evaluate child expressions - this is done by the BinaryOp
+	BinaryExpr interface {
+		Evaluate(left, right EvalResult) (EvalResult, error)
+		Type(left querypb.Type) querypb.Type
+		String() string
 	}
-	Column struct {
-		Offset    int
-		Collation collations.ID
+
+	// Expressions
+	Literal      struct{ Val EvalResult }
+	BindVariable struct{ Key string }
+	Column       struct{ Offset int }
+	BinaryOp     struct {
+		Expr        BinaryExpr
+		Left, Right Expr
 	}
-)
 
-var _ Expr = (*Null)(nil)
-var _ Expr = (*Literal)(nil)
-var _ Expr = (*BindVariable)(nil)
-var _ Expr = (*Column)(nil)
-var _ Expr = (*BinaryExpr)(nil)
-var _ Expr = (*ComparisonExpr)(nil)
+	// Binary ops
+	Addition       struct{}
+	Subtraction    struct{}
+	Multiplication struct{}
+	Division       struct{}
+)
 
-// Value allows for retrieval of the value we expose for public consumption
+//Value allows for retrieval of the value we expose for public consumption
 func (e EvalResult) Value() sqltypes.Value {
 	return e.toSQLValue(e.typ)
 }
 
-// NewLiteralIntFromBytes returns a literal expression
+//NewLiteralIntFromBytes returns a literal expression
 func NewLiteralIntFromBytes(val []byte) (Expr, error) {
 	ival, err := strconv.ParseInt(string(val), 10, 64)
 	if err != nil {
@@ -90,91 +86,136 @@ func NewLiteralIntFromBytes(val []byte) (Expr, error) {
 	return NewLiteralInt(ival), nil
 }
 
-// NewLiteralInt returns a literal expression
+//NewLiteralInt returns a literal expression
 func NewLiteralInt(i int64) Expr {
-	return &Literal{Val: EvalResult{typ: sqltypes.Int64, ival: i}}
+	return &Literal{EvalResult{typ: sqltypes.Int64, ival: i}}
 }
 
-// NewLiteralFloat returns a literal expression
-func NewLiteralFloat(val float64) Expr {
-	return &Literal{Val: EvalResult{typ: sqltypes.Float64, fval: val}}
-}
-
-// NewLiteralFloatFromBytes returns a float literal expression from a slice of bytes
-func NewLiteralFloatFromBytes(val []byte) (Expr, error) {
+//NewLiteralFloat returns a literal expression
+func NewLiteralFloat(val []byte) (Expr, error) {
 	fval, err := strconv.ParseFloat(string(val), 64)
 	if err != nil {
 		return nil, err
 	}
-	return &Literal{Val: EvalResult{typ: sqltypes.Float64, fval: fval}}, nil
+	return &Literal{EvalResult{typ: sqltypes.Float64, fval: fval}}, nil
 }
 
-// NewLiteralString returns a literal expression
-func NewLiteralString(val []byte, collation collations.ID) Expr {
-	return &Literal{Val: EvalResult{typ: sqltypes.VarBinary, bytes: val}, Collation: collation}
+//NewLiteralFloat returns a literal expression
+func NewLiteralString(val []byte) Expr {
+	return &Literal{EvalResult{typ: sqltypes.VarBinary, bytes: val}}
 }
 
-// NewBindVar returns a bind variable
-func NewBindVar(key string, collation collations.ID) Expr {
-	return &BindVariable{
-		Key:       key,
-		Collation: collation,
-	}
+//NewBindVar returns a bind variable
+func NewBindVar(key string) Expr {
+	return &BindVariable{Key: key}
 }
 
-// NewColumn returns a bind variable
-func NewColumn(offset int, collation collations.ID) Expr {
+//NewColumn returns a bind variable
+func NewColumn(offset int) Expr {
 	return &Column{
-		Offset:    offset,
-		Collation: collation,
+		Offset: offset,
 	}
 }
 
-// Evaluate implements the Expr interface
-func (n Null) Evaluate(ExpressionEnv) (EvalResult, error) {
-	return EvalResult{}, nil
-}
+var _ Expr = (*Literal)(nil)
+var _ Expr = (*BindVariable)(nil)
+var _ Expr = (*BinaryOp)(nil)
+var _ Expr = (*Column)(nil)
 
-// Type implements the Expr interface
-func (n Null) Type(ExpressionEnv) (querypb.Type, error) {
-	return querypb.Type_NULL_TYPE, nil
-}
+var _ BinaryExpr = (*Addition)(nil)
+var _ BinaryExpr = (*Subtraction)(nil)
+var _ BinaryExpr = (*Multiplication)(nil)
+var _ BinaryExpr = (*Division)(nil)
 
-// String implements the Expr interface
-func (n Null) String() string {
-	return "null"
+//Evaluate implements the Expr interface
+func (b *BinaryOp) Evaluate(env ExpressionEnv) (EvalResult, error) {
+	lVal, err := b.Left.Evaluate(env)
+	if err != nil {
+		return EvalResult{}, err
+	}
+	rVal, err := b.Right.Evaluate(env)
+	if err != nil {
+		return EvalResult{}, err
+	}
+	return b.Expr.Evaluate(lVal, rVal)
 }
 
-// Evaluate implements the Expr interface
+//Evaluate implements the Expr interface
 func (l *Literal) Evaluate(ExpressionEnv) (EvalResult, error) {
-	eval := l.Val
-	eval.collation = l.Collation
-	return eval, nil
+	return l.Val, nil
 }
 
-// Evaluate implements the Expr interface
+//Evaluate implements the Expr interface
 func (b *BindVariable) Evaluate(env ExpressionEnv) (EvalResult, error) {
 	val, ok := env.BindVars[b.Key]
 	if !ok {
 		return EvalResult{}, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "Bind variable not found")
 	}
-	eval, err := evaluateByType(val)
-	if err != nil {
-		return EvalResult{}, err
-	}
-	eval.collation = b.Collation
-	return eval, nil
+	return evaluateByType(val)
 }
 
-// Evaluate implements the Expr interface
+//Evaluate implements the Expr interface
 func (c *Column) Evaluate(env ExpressionEnv) (EvalResult, error) {
 	value := env.Row[c.Offset]
 	numeric, err := newEvalResult(value)
-	numeric.collation = c.Collation
 	return numeric, err
 }
 
-// Type implements the Expr interface
+//Evaluate implements the BinaryOp interface
+func (a *Addition) Evaluate(left, right EvalResult) (EvalResult, error) {
+	return addNumericWithError(left, right)
+}
+
+//Evaluate implements the BinaryOp interface
+func (s *Subtraction) Evaluate(left, right EvalResult) (EvalResult, error) {
+	return subtractNumericWithError(left, right)
+}
+
+//Evaluate implements the BinaryOp interface
+func (m *Multiplication) Evaluate(left, right EvalResult) (EvalResult, error) {
+	return multiplyNumericWithError(left, right)
+}
+
+//Evaluate implements the BinaryOp interface
+func (d *Division) Evaluate(left, right EvalResult) (EvalResult, error) {
+	return divideNumericWithError(left, right)
+}
+
+//Type implements the BinaryExpr interface
+func (a *Addition) Type(left querypb.Type) querypb.Type {
+	return left
+}
+
+//Type implements the BinaryExpr interface
+func (m *Multiplication) Type(left querypb.Type) querypb.Type {
+	return left
+}
+
+//Type implements the BinaryExpr interface
+func (d *Division) Type(querypb.Type) querypb.Type {
+	return sqltypes.Float64
+}
+
+//Type implements the BinaryExpr interface
+func (s *Subtraction) Type(left querypb.Type) querypb.Type {
+	return left
+}
+
+//Type implements the Expr interface
+func (b *BinaryOp) Type(env ExpressionEnv) (querypb.Type, error) {
+	ltype, err := b.Left.Type(env)
+	if err != nil {
+		return 0, err
+	}
+	rtype, err := b.Right.Type(env)
+	if err != nil {
+		return 0, err
+	}
+	typ := mergeNumericalTypes(ltype, rtype)
+	return b.Expr.Type(typ), nil
+}
+
+//Type implements the Expr interface
 func (b *BindVariable) Type(env ExpressionEnv) (querypb.Type, error) {
 	e := env.BindVars
 	v, found := e[b.Key]
@@ -184,27 +225,52 @@ func (b *BindVariable) Type(env ExpressionEnv) (querypb.Type, error) {
 	return v.Type, nil
 }
 
-// Type implements the Expr interface
+//Type implements the Expr interface
 func (l *Literal) Type(ExpressionEnv) (querypb.Type, error) {
 	return l.Val.typ, nil
 }
 
-// Type implements the Expr interface
+//Type implements the Expr interface
 func (c *Column) Type(ExpressionEnv) (querypb.Type, error) {
 	return sqltypes.Float64, nil
 }
 
-// String implements the Expr interface
+//String implements the BinaryExpr interface
+func (d *Division) String() string {
+	return "/"
+}
+
+//String implements the BinaryExpr interface
+func (m *Multiplication) String() string {
+	return "*"
+}
+
+//String implements the BinaryExpr interface
+func (s *Subtraction) String() string {
+	return "-"
+}
+
+//String implements the BinaryExpr interface
+func (a *Addition) String() string {
+	return "+"
+}
+
+//String implements the Expr interface
+func (b *BinaryOp) String() string {
+	return b.Left.String() + " " + b.Expr.String() + " " + b.Right.String()
+}
+
+//String implements the Expr interface
 func (b *BindVariable) String() string {
 	return ":" + b.Key
 }
 
-// String implements the Expr interface
+//String implements the Expr interface
 func (l *Literal) String() string {
 	return l.Val.Value().String()
 }
 
-// String implements the Expr interface
+//String implements the Expr interface
 func (c *Column) String() string {
 	return fmt.Sprintf("column %d from the input", c.Offset)
 }
@@ -251,15 +317,13 @@ func evaluateByType(val *querypb.BindVariable) (EvalResult, error) {
 		return EvalResult{typ: sqltypes.Float64, fval: fval}, nil
 	case sqltypes.VarChar, sqltypes.Text, sqltypes.VarBinary:
 		return EvalResult{typ: sqltypes.VarBinary, bytes: val.Value}, nil
-	case sqltypes.Time, sqltypes.Datetime, sqltypes.Timestamp, sqltypes.Date:
-		return EvalResult{typ: val.Type, bytes: val.Value}, nil
 	case sqltypes.Null:
 		return EvalResult{typ: sqltypes.Null}, nil
 	}
 	return EvalResult{}, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "Type is not supported: %s", val.Type.String())
 }
 
-// debugString prints the entire EvalResult in a debug format
+// debugString is
 func (e *EvalResult) debugString() string {
 	return fmt.Sprintf("(%s) %d %d %f %s", querypb.Type_name[int32(e.typ)], e.ival, e.uval, e.fval, string(e.bytes))
 }
diff --git a/go/vt/vtgate/planbuilder/expression_converter.go b/go/vt/vtgate/planbuilder/expression_converter.go
index 1bcde04bbc..769c4b6f52 100644
--- a/go/vt/vtgate/planbuilder/expression_converter.go
+++ b/go/vt/vtgate/planbuilder/expression_converter.go
@@ -62,7 +62,7 @@ func booleanValues(astExpr sqlparser.Expr) evalengine.Expr {
 func identifierAsStringValue(astExpr sqlparser.Expr) evalengine.Expr {
 	colName, isColName := astExpr.(*sqlparser.ColName)
 	if isColName {
-		return evalengine.NewLiteralString([]byte(colName.Name.Lowered()), 0)
+		return evalengine.NewLiteralString([]byte(colName.Name.Lowered()))
 	}
 	return nil
 }
@@ -80,9 +80,9 @@ func (ec *expressionConverter) convert(astExpr sqlparser.Expr, boolean, identifi
 			return evalExpr, nil
 		}
 	}
-	evalExpr, err := evalengine.Convert(astExpr, nil)
+	evalExpr, err := sqlparser.Convert(astExpr)
 	if err != nil {
-		if !strings.Contains(err.Error(), evalengine.ErrConvertExprNotSupported) {
+		if err != sqlparser.ErrExprNotSupported {
 			return nil, err
 		}
 		evalExpr = &evalengine.Column{Offset: len(ec.tabletExpressions)}
diff --git a/go/vt/vtgate/planbuilder/filter.go b/go/vt/vtgate/planbuilder/filter.go
deleted file mode 100644
index 1ab52a075d..0000000000
--- a/go/vt/vtgate/planbuilder/filter.go
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
-Copyright 2021 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 (
-	"vitess.io/vitess/go/mysql/collations"
-	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 (
-	// filter is the logicalPlan for engine.Filter.
-	filter struct {
-		logicalPlanCommon
-		efilter *engine.Filter
-	}
-
-	simpleConverterLookup struct {
-		semTable *semantics.SemTable
-		plan     logicalPlan
-	}
-)
-
-var _ logicalPlan = (*filter)(nil)
-var _ evalengine.ConverterLookup = (*simpleConverterLookup)(nil)
-
-func (s *simpleConverterLookup) ColumnLookup(col *sqlparser.ColName) (int, error) {
-	offset, added, err := pushProjection(&sqlparser.AliasedExpr{Expr: col}, s.plan, s.semTable, true, true, false)
-	if err != nil {
-		return 0, err
-	}
-	if added {
-		return 0, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "column should not be pushed to projection while doing a column lookup")
-	}
-	return offset, nil
-}
-
-func (s *simpleConverterLookup) CollationIDLookup(expr sqlparser.Expr) collations.ID {
-	return s.semTable.CollationFor(expr)
-}
-
-// newFilter builds a new filter.
-func newFilter(semTable *semantics.SemTable, plan logicalPlan, expr sqlparser.Expr) (*filter, error) {
-	scl := &simpleConverterLookup{
-		semTable: semTable,
-		plan:     plan,
-	}
-	predicate, err := evalengine.Convert(expr, scl)
-	if err != nil {
-		return nil, err
-	}
-	return &filter{
-		logicalPlanCommon: newBuilderCommon(plan),
-		efilter: &engine.Filter{
-			Predicate:    predicate,
-			ASTPredicate: expr,
-		},
-	}, nil
-}
-
-// Primitive implements the logicalPlan interface
-func (l *filter) Primitive() engine.Primitive {
-	l.efilter.Input = l.input.Primitive()
-	return l.efilter
-}
diff --git a/go/vt/vtgate/planbuilder/horizon_planning.go b/go/vt/vtgate/planbuilder/horizon_planning.go
index 368a241dd3..4e748409ec 100644
--- a/go/vt/vtgate/planbuilder/horizon_planning.go
+++ b/go/vt/vtgate/planbuilder/horizon_planning.go
@@ -147,8 +147,6 @@ func (hp *horizonPlanning) truncateColumnsIfNeeded(plan logicalPlan) error {
 		p.truncater.SetTruncateColumnCount(hp.sel.GetColumnCount())
 	case *pulloutSubquery:
 		return hp.truncateColumnsIfNeeded(p.underlying)
-	case *filter:
-		return hp.truncateColumnsIfNeeded(p.input)
 	default:
 		return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "plan type not known for column truncation: %T", plan)
 	}
@@ -460,7 +458,7 @@ func (hp *horizonPlanning) planAggregations(ctx *planningContext, plan logicalPl
 		hp.haveToTruncate(added)
 	}
 
-	newPlan, err := hp.planHaving(ctx, newPlan)
+	err := hp.planHaving(ctx, newPlan)
 	if err != nil {
 		return nil, err
 	}
@@ -662,28 +660,35 @@ func (hp *horizonPlanning) planOrderBy(ctx *planningContext, orderExprs []abstra
 		return plan, nil
 	case *memorySort:
 		return plan, nil
-	case *simpleProjection:
-		return hp.createMemorySortPlan(ctx, plan, orderExprs, true)
-	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:
-		inputs := plan.Inputs()
-		if len(inputs) == 0 {
-			break
+	case *pulloutSubquery:
+		newUnderlyingPlan, err := hp.planOrderBy(ctx, orderExprs, plan.underlying)
+		if err != nil {
+			return nil, err
 		}
-		newFirstInput, err := hp.planOrderBy(ctx, orderExprs, inputs[0])
+		plan.underlying = newUnderlyingPlan
+		return plan, nil
+	case *semiJoin:
+		newUnderlyingPlan, err := hp.planOrderBy(ctx, orderExprs, plan.lhs)
 		if err != nil {
 			return nil, err
 		}
-		inputs[0] = newFirstInput
-		err = plan.Rewrite(inputs...)
+		plan.lhs = newUnderlyingPlan
+		return plan, nil
+	case *limit:
+		newUnderlyingPlan, err := hp.planOrderBy(ctx, orderExprs, plan.input)
 		if err != nil {
 			return nil, err
 		}
+		plan.input = newUnderlyingPlan
 		return plan, nil
+	case *simpleProjection:
+		return hp.createMemorySortPlan(ctx, plan, orderExprs, true)
+	case *vindexFunc:
+		// This is evaluated at VTGate only, so weight_string function cannot be used.
+		return hp.createMemorySortPlan(ctx, plan, orderExprs /* useWeightStr */, false)
+	default:
+		return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "ordering on complex query %T", plan)
 	}
-	return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "ordering on complex query %T", plan)
 }
 
 func isSpecialOrderBy(o abstract.OrderBy) bool {
@@ -1077,25 +1082,31 @@ func (hp *horizonPlanning) needDistinctHandling(ctx *planningContext, funcExpr *
 	return true, innerAliased, nil
 }
 
-func (hp *horizonPlanning) planHaving(ctx *planningContext, plan logicalPlan) (logicalPlan, error) {
+func (hp *horizonPlanning) planHaving(ctx *planningContext, plan logicalPlan) error {
 	if hp.sel.Having == nil {
-		return plan, nil
+		return nil
 	}
-	return pushHaving(hp.sel.Having.Expr, plan, ctx.semTable)
+	for _, expr := range sqlparser.SplitAndExpression(nil, hp.sel.Having.Expr) {
+		err := pushHaving(expr, plan, ctx.semTable)
+		if err != nil {
+			return err
+		}
+	}
+	return nil
 }
 
-func pushHaving(expr sqlparser.Expr, plan logicalPlan, semTable *semantics.SemTable) (logicalPlan, error) {
+func pushHaving(expr sqlparser.Expr, plan logicalPlan, semTable *semantics.SemTable) error {
 	switch node := plan.(type) {
 	case *route:
 		sel := sqlparser.GetFirstSelect(node.Select)
 		sel.AddHaving(expr)
-		return plan, nil
+		return nil
 	case *pulloutSubquery:
 		return pushHaving(expr, node.underlying, semTable)
 	case *simpleProjection:
-		return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: filtering on results of cross-shard derived table")
+		return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: filtering on results of cross-shard derived table")
 	case *orderedAggregate:
-		return newFilter(semTable, plan, expr)
+		return vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: filtering on results of aggregates")
 	}
-	return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unreachable %T.filtering", plan)
+	return vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] unreachable %T.filtering", plan)
 }
diff --git a/go/vt/vtgate/planbuilder/rewrite.go b/go/vt/vtgate/planbuilder/rewrite.go
index 143fcdbc64..99d725f5e9 100644
--- a/go/vt/vtgate/planbuilder/rewrite.go
+++ b/go/vt/vtgate/planbuilder/rewrite.go
@@ -167,36 +167,26 @@ func rewriteHavingClause(node *sqlparser.Select) {
 		selectExprMap[aliasedExpr.As.Lowered()] = aliasedExpr.Expr
 	}
 
-	// for each expression in the having clause, we check if it contains aggregation.
-	// if it does, we keep the expression in the having clause ; and if it does not
-	// and the expression is in the select list, we replace the expression by the one
-	// used in the select list and add it to the where clause instead of the having clause.
-	exprs := sqlparser.SplitAndExpression(nil, node.Having.Expr)
-	node.Having = nil
-	for _, expr := range exprs {
-		var hasAggr bool
-		sqlparser.Rewrite(expr, func(cursor *sqlparser.Cursor) bool {
-			switch x := cursor.Node().(type) {
-			case *sqlparser.ColName:
-				if !x.Qualifier.IsEmpty() {
-					return false
-				}
-				originalExpr, isInMap := selectExprMap[x.Name.Lowered()]
-				if isInMap {
-					if sqlparser.ContainsAggregation(originalExpr) {
-						hasAggr = true
-					} else {
-						cursor.Replace(originalExpr)
-					}
-				}
+	sqlparser.Rewrite(node.Having.Expr, func(cursor *sqlparser.Cursor) bool {
+		switch x := cursor.Node().(type) {
+		case *sqlparser.ColName:
+			if !x.Qualifier.IsEmpty() {
+				return false
+			}
+			originalExpr, isInMap := selectExprMap[x.Name.Lowered()]
+			if isInMap {
+				cursor.Replace(originalExpr)
 				return false
-			default:
-				hasAggr = hasAggr || sqlparser.IsAggregation(x)
 			}
-			return true
-		}, nil)
+			return false
+		}
+		return true
+	}, nil)
 
-		if hasAggr {
+	exprs := sqlparser.SplitAndExpression(nil, node.Having.Expr)
+	node.Having = nil
+	for _, expr := range exprs {
+		if sqlparser.ContainsAggregation(expr) {
 			node.AddHaving(expr)
 		} else {
 			node.AddWhere(expr)
diff --git a/go/vt/vtgate/planbuilder/select.go b/go/vt/vtgate/planbuilder/select.go
index 9d5cecb11c..8f42a68225 100644
--- a/go/vt/vtgate/planbuilder/select.go
+++ b/go/vt/vtgate/planbuilder/select.go
@@ -320,8 +320,9 @@ func handleDualSelects(sel *sqlparser.Select, vschema ContextVSchema) (engine.Pr
 		if sqlparser.IsLockingFunc(expr.Expr) {
 			// if we are using any locking functions, we bail out here and send the whole query to a single destination
 			return buildLockingPrimitive(sel, vschema)
+
 		}
-		exprs[i], err = evalengine.Convert(expr.Expr, nil)
+		exprs[i], err = sqlparser.Convert(expr.Expr)
 		if err != nil {
 			return nil, nil
 		}
@@ -342,12 +343,10 @@ func buildLockingPrimitive(sel *sqlparser.Select, vschema ContextVSchema) (engin
 	if err != nil {
 		return nil, err
 	}
-	buf := sqlparser.NewTrackedBuffer(sqlparser.FormatImpossibleQuery).WriteNode(sel)
 	return &engine.Lock{
 		Keyspace:          ks,
 		TargetDestination: key.DestinationKeyspaceID{0},
 		Query:             sqlparser.String(sel),
-		FieldQuery:        buf.String(),
 	}, nil
 }
 
diff --git a/go/vt/vtgate/planbuilder/system_tables.go b/go/vt/vtgate/planbuilder/system_tables.go
index 3859d73c59..a6cc18d01c 100644
--- a/go/vt/vtgate/planbuilder/system_tables.go
+++ b/go/vt/vtgate/planbuilder/system_tables.go
@@ -17,28 +17,11 @@ limitations under the License.
 package planbuilder
 
 import (
-	"strings"
-
-	"vitess.io/vitess/go/mysql/collations"
-
-	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
-	"vitess.io/vitess/go/vt/vterrors"
-
 	"vitess.io/vitess/go/sqltypes"
 	"vitess.io/vitess/go/vt/sqlparser"
 	"vitess.io/vitess/go/vt/vtgate/evalengine"
 )
 
-type notImplementedSchemaInfoConverter struct{}
-
-func (f *notImplementedSchemaInfoConverter) ColumnLookup(*sqlparser.ColName) (int, error) {
-	return 0, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "Comparing table schema name with a column name not yet supported")
-}
-
-func (f *notImplementedSchemaInfoConverter) CollationIDLookup(sqlparser.Expr) collations.ID {
-	return 0
-}
-
 func (pb *primitiveBuilder) findSysInfoRoutingPredicates(expr sqlparser.Expr, rut *route, reservedVars *sqlparser.ReservedVars) error {
 	isTableSchema, bvName, out, err := extractInfoSchemaRoutingPredicate(expr, reservedVars)
 	if err != nil {
@@ -121,9 +104,9 @@ func extractInfoSchemaRoutingPredicate(in sqlparser.Expr, reservedVars *sqlparse
 		if cmp.Operator == sqlparser.EqualOp {
 			isSchemaName, col, other, replaceOther := findOtherComparator(cmp)
 			if col != nil && shouldRewrite(other) {
-				evalExpr, err := evalengine.Convert(other, &notImplementedSchemaInfoConverter{})
+				evalExpr, err := sqlparser.Convert(other)
 				if err != nil {
-					if strings.Contains(err.Error(), evalengine.ErrConvertExprNotSupported) {
+					if err == sqlparser.ErrExprNotSupported {
 						// This just means we can't rewrite this particular expression,
 						// not that we have to exit altogether
 						return false, "", nil, nil
diff --git a/go/vt/vtgate/planbuilder/system_variables.go b/go/vt/vtgate/planbuilder/system_variables.go
index 86f758d765..d6e9cbeaa1 100644
--- a/go/vt/vtgate/planbuilder/system_variables.go
+++ b/go/vt/vtgate/planbuilder/system_variables.go
@@ -61,7 +61,7 @@ func parseAndBuildDefaultValue(sysvar sysvars.SystemVariable) evalengine.Expr {
 	}
 	sel := stmt.(*sqlparser.Select)
 	aliasedExpr := sel.SelectExprs[0].(*sqlparser.AliasedExpr)
-	def, err := evalengine.Convert(aliasedExpr.Expr, nil)
+	def, err := sqlparser.Convert(aliasedExpr.Expr)
 	if err != nil {
 		panic(fmt.Sprintf("bug in set plan init - default value for %s not able to convert to evalengine.Expr: %s", sysvar.Name, sysvar.Default))
 	}
diff --git a/go/vt/vtgate/vindexes/cached_size.go b/go/vt/vtgate/vindexes/cached_size.go
index 931cf2be12..10af44ae6b 100644
--- a/go/vt/vtgate/vindexes/cached_size.go
+++ b/go/vt/vtgate/vindexes/cached_size.go
@@ -93,12 +93,10 @@ func (cached *Column) CachedSize(alloc bool) int64 {
 	}
 	size := int64(0)
 	if alloc {
-		size += int64(64)
+		size += int64(48)
 	}
 	// field Name vitess.io/vitess/go/vt/sqlparser.ColIdent
 	size += cached.Name.CachedSize(false)
-	// field CollationName string
-	size += hack.RuntimeAllocSize(int64(len(cached.CollationName)))
 	return size
 }
 func (cached *ColumnVindex) CachedSize(alloc bool) int64 {
@@ -398,7 +396,7 @@ func (cached *Table) CachedSize(alloc bool) int64 {
 	size += cached.AutoIncrement.CachedSize(true)
 	// field Columns []vitess.io/vitess/go/vt/vtgate/vindexes.Column
 	{
-		size += hack.RuntimeAllocSize(int64(cap(cached.Columns)) * int64(64))
+		size += hack.RuntimeAllocSize(int64(cap(cached.Columns)) * int64(44))
 		for _, elem := range cached.Columns {
 			size += elem.CachedSize(false)
 		}
diff --git a/go/vt/vtgate/vindexes/consistent_lookup.go b/go/vt/vtgate/vindexes/consistent_lookup.go
index 863910ee0f..d06acece44 100644
--- a/go/vt/vtgate/vindexes/consistent_lookup.go
+++ b/go/vt/vtgate/vindexes/consistent_lookup.go
@@ -22,7 +22,6 @@ import (
 	"fmt"
 	"strings"
 
-	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/vt/vtgate/evalengine"
 
 	"vitess.io/vitess/go/sqltypes"
@@ -315,8 +314,7 @@ func (lu *clCommon) Delete(vcursor VCursor, rowsColValues [][]sqltypes.Value, ks
 func (lu *clCommon) Update(vcursor VCursor, oldValues []sqltypes.Value, ksid []byte, newValues []sqltypes.Value) error {
 	equal := true
 	for i := range oldValues {
-		// TODO(king-11) make collation aware
-		result, err := evalengine.NullsafeCompare(oldValues[i], newValues[i], collations.Unknown)
+		result, err := evalengine.NullsafeCompare(oldValues[i], newValues[i])
 		// errors from NullsafeCompare can be ignored. if they are real problems, we'll see them in the Create/Update
 		if err != nil || result != 0 {
 			equal = false
diff --git a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go
index ea97362303..f488b70d45 100644
--- a/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go
+++ b/go/vt/vttablet/tabletmanager/vreplication/replicator_plan.go
@@ -26,7 +26,6 @@ import (
 
 	"vitess.io/vitess/go/bytes2"
 	"vitess.io/vitess/go/mysql"
-	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/sqltypes"
 	"vitess.io/vitess/go/vt/binlog/binlogplayer"
 	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
@@ -282,8 +281,7 @@ func (tp *TablePlan) isOutsidePKRange(bindvars map[string]*querypb.BindVariable,
 		}
 
 		rowVal, _ := sqltypes.BindVariableToValue(bindvar)
-		// TODO(king-11) make collation aware
-		result, err := evalengine.NullsafeCompare(rowVal, tp.Lastpk.Rows[0][0], collations.Unknown)
+		result, err := evalengine.NullsafeCompare(rowVal, tp.Lastpk.Rows[0][0])
 		// If rowVal is > last pk, transaction will be a noop, so don't apply this statement
 		if err == nil && result > 0 {
 			tp.Stats.NoopQueryCount.Add(stmtType, 1)
diff --git a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go
index 3a5a20611c..bf1adf9c32 100644
--- a/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go
+++ b/go/vt/vttablet/tabletserver/vstreamer/planbuilder.go
@@ -22,7 +22,6 @@ import (
 	"strconv"
 	"strings"
 
-	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/vt/vtgate/evalengine"
 
 	vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
@@ -165,8 +164,7 @@ func compare(comparison Opcode, columnValue, filterValue sqltypes.Value) (bool,
 	}
 	// at this point neither values can be null
 	// NullsafeCompare returns 0 if values match, -1 if columnValue < filterValue, 1 if columnValue > filterValue
-	// TODO(king-11) make collation aware
-	result, err := evalengine.NullsafeCompare(columnValue, filterValue, collations.Unknown)
+	result, err := evalengine.NullsafeCompare(columnValue, filterValue)
 	if err != nil {
 		return false, err
 	}
diff --git a/go/vt/wrangler/vdiff.go b/go/vt/wrangler/vdiff.go
index d02b842331..fa7f214774 100644
--- a/go/vt/wrangler/vdiff.go
+++ b/go/vt/wrangler/vdiff.go
@@ -29,7 +29,6 @@ import (
 	"google.golang.org/protobuf/encoding/prototext"
 
 	"vitess.io/vitess/go/mysql"
-	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/sqltypes"
 	"vitess.io/vitess/go/vt/binlog/binlogplayer"
 	"vitess.io/vitess/go/vt/concurrency"
@@ -1058,8 +1057,7 @@ func (td *tableDiffer) compare(sourceRow, targetRow []sqltypes.Value, cols []com
 		if sourceRow[compareIndex].IsText() && targetRow[compareIndex].IsText() {
 			c = bytes.Compare(sourceRow[compareIndex].ToBytes(), targetRow[compareIndex].ToBytes())
 		} else {
-			// TODO(king-11) make collation aware
-			c, err = evalengine.NullsafeCompare(sourceRow[compareIndex], targetRow[compareIndex], collations.Unknown)
+			c, err = evalengine.NullsafeCompare(sourceRow[compareIndex], targetRow[compareIndex])
 		}
 		if err != nil {
 			return 0, err
diff --git a/web/vtadmin/src/components/routes/keyspace/Keyspace.tsx b/web/vtadmin/src/components/routes/keyspace/Keyspace.tsx
index 11458f3d59..e9944f955f 100644
--- a/web/vtadmin/src/components/routes/keyspace/Keyspace.tsx
+++ b/web/vtadmin/src/components/routes/keyspace/Keyspace.tsx
@@ -18,9 +18,13 @@ import { Link, Redirect, Route } from 'react-router-dom';
 
 import { useKeyspace } from '../../../hooks/api';
 import { useDocumentTitle } from '../../../hooks/useDocumentTitle';
+import { Code } from '../../Code';
+import { ContentContainer } from '../../layout/ContentContainer';
 import { NavCrumbs } from '../../layout/NavCrumbs';
 import { WorkspaceHeader } from '../../layout/WorkspaceHeader';
 import { WorkspaceTitle } from '../../layout/WorkspaceTitle';
+import { Tab } from '../../tabs/Tab';
+import { TabContainer } from '../../tabs/TabContainer';
 import style from './Keyspace.module.scss';
 import { KeyspaceShards } from './KeyspaceShards';
 
@@ -31,7 +35,7 @@ interface RouteParams {
 
 export const Keyspace = () => {
     const { clusterID, name } = useParams<RouteParams>();
-    const { path } = useRouteMatch();
+    const { path, url } = useRouteMatch();
     const { search } = useLocation();
 
     useDocumentTitle(`${name} (${clusterID})`);
@@ -79,16 +83,27 @@ export const Keyspace = () => {
                 </div>
             </WorkspaceHeader>
 
-            {/* TODO skeleton placeholder */}
-            {!!kq.isLoading && <div className={style.placeholder}>Loading</div>}
+            <ContentContainer>
+                <TabContainer>
+                    <Tab text="Shards" to={`${url}/shards`} />
+                    <Tab text="JSON" to={`${url}/json`} />
+                </TabContainer>
 
-            <Switch>
-                <Route path={`${path}/shards`}>
-                    <KeyspaceShards keyspace={keyspace} />
-                </Route>
+                <Switch>
+                    <Route path={`${path}/shards`}>
+                        <KeyspaceShards keyspace={keyspace} />
+                    </Route>
 
-                <Redirect exact from={path} to={{ pathname: `${path}/shards`, search }} />
-            </Switch>
+                    <Route path={`${path}/json`}>
+                        <Code code={JSON.stringify(keyspace, null, 2)} />
+                    </Route>
+
+                    <Redirect exact from={path} to={{ pathname: `${path}/shards`, search }} />
+                </Switch>
+
+                {/* TODO skeleton placeholder */}
+                {!!kq.isLoading && <div className={style.placeholder}>Loading</div>}
+            </ContentContainer>
         </div>
     );
 };
diff --git a/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.module.scss b/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.module.scss
new file mode 100644
index 0000000000..3340a8f0dc
--- /dev/null
+++ b/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.module.scss
@@ -0,0 +1,28 @@
+/**
+ * Copyright 2021 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.
+ */
+.container {
+    padding: 16px 0;
+}
+
+.counts > span {
+    white-space: nowrap;
+}
+
+.counts > span:not(:last-child)::after {
+    color: var(--colorScaffoldingHighlight);
+    content: '/';
+    margin: 0 8px;
+}
diff --git a/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.tsx b/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.tsx
index b0d42079c9..bdb6fc6068 100644
--- a/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.tsx
+++ b/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.tsx
@@ -13,12 +13,13 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import React from 'react';
-import { groupBy, orderBy, uniq } from 'lodash';
+import React, { useMemo } from 'react';
+import { isEmpty, orderBy } from 'lodash';
 
-import { topodata, vtadmin as pb } from '../../../proto/vtadmin.d';
+import style from './KeyspaceShards.module.scss';
+import { topodata, vtadmin as pb } from '../../../proto/vtadmin';
 import { useTablets } from '../../../hooks/api';
-import { formatAlias, formatDisplayType, formatState } from '../../../util/tablets';
+import { formatAlias, formatType } from '../../../util/tablets';
 import { DataTable } from '../../dataTable/DataTable';
 import { DataCell } from '../../dataTable/DataCell';
 import { ShardServingPip } from '../../pips/ShardServingPip';
@@ -26,98 +27,121 @@ import { TabletServingPip } from '../../pips/TabletServingPip';
 import { DataFilter } from '../../dataTable/DataFilter';
 import { useSyncedURLParam } from '../../../hooks/useSyncedURLParam';
 import { filterNouns } from '../../../util/filterNouns';
-import { ContentContainer } from '../../layout/ContentContainer';
 import { TabletLink } from '../../links/TabletLink';
 import { ShardLink } from '../../links/ShardLink';
+import { getShardSortRange } from '../../../util/keyspaces';
+import { Pip } from '../../pips/Pip';
+import { Tooltip } from '../../tooltip/Tooltip';
+
 interface Props {
     keyspace: pb.Keyspace | null | undefined;
 }
 
-type ShardState = 'SERVING' | 'NOT_SERVING';
-
-const TABLE_COLUMNS = ['Shard', 'Alias', 'Tablet Type', 'Tablet State', 'Hostname'];
-const PAGE_SIZE = 16;
+const TABLE_COLUMNS = ['Shard', 'Primary Serving?', 'Tablets', 'Primary Tablet'];
 
 export const KeyspaceShards = ({ keyspace }: Props) => {
     const { data: tablets = [], ...tq } = useTablets();
     const { value: filter, updateValue: updateFilter } = useSyncedURLParam('shardFilter');
 
-    const data = React.useMemo(() => {
+    const data = useMemo(() => {
         if (!keyspace || tq.isLoading) {
             return [];
         }
 
-        return formatRows(keyspace, tablets, filter);
+        const keyspaceTablets = tablets.filter(
+            (t) => t.cluster?.id === keyspace.cluster?.id && t.tablet?.keyspace === keyspace.keyspace?.name
+        );
+
+        const mapped = Object.values(keyspace?.shards).map((shard) => {
+            const sortRange = getShardSortRange(shard.name || '');
+
+            const shardTablets = keyspaceTablets.filter((t) => t.tablet?.shard === shard.name);
+
+            const primaryTablet = shardTablets.find((t) => t.tablet?.type === topodata.TabletType.PRIMARY);
+
+            return {
+                keyspace: shard.keyspace,
+                isPrimaryServing: shard.shard?.is_primary_serving,
+                name: shard.name,
+                primaryAlias: formatAlias(primaryTablet?.tablet?.alias),
+                primaryHostname: primaryTablet?.tablet?.hostname,
+                // "_" prefix excludes the property name from k/v filtering
+                _primaryTablet: primaryTablet,
+                _sortStart: sortRange.start,
+                _sortEnd: sortRange.end,
+                _tabletsByType: countTablets(shardTablets),
+            };
+        });
+
+        const filtered = filterNouns(filter, mapped);
+
+        return orderBy(filtered, ['_sortStart', '_sortEnd']);
     }, [filter, keyspace, tablets, tq.isLoading]);
 
     const renderRows = React.useCallback(
         (rows: typeof data) => {
-            return rows.reduce((acc, row) => {
-                if (!row.tablets.length) {
-                    acc.push(
-                        <tr key={row.shard}>
-                            <DataCell rowSpan={1}>
-                                <ShardLink
-                                    clusterID={keyspace?.cluster?.id}
-                                    keyspace={keyspace?.keyspace?.name}
-                                    shard={row.shard}
-                                >
-                                    <ShardServingPip isServing={row.isShardServing} /> {row.shard}
-                                </ShardLink>
-                                {row.shardState === 'NOT_SERVING' && (
-                                    <div className="font-size-small text-color-secondary white-space-nowrap">
-                                        {row.shardState}
-                                    </div>
-                                )}
-                            </DataCell>
-                            <DataCell colSpan={TABLE_COLUMNS.length - 1}>
-                                <div className="font-size-small text-color-secondary">
-                                    No tablets in {keyspace?.keyspace?.name}/{row.shard}{' '}
-                                    {!!filter && 'matching filters'}
+            return rows.map((row) => {
+                return (
+                    <tr key={row.name}>
+                        <DataCell>
+                            <ShardLink
+                                className="font-weight-bold"
+                                clusterID={keyspace?.cluster?.id}
+                                keyspace={keyspace?.keyspace?.name}
+                                shard={row.name}
+                            >
+                                {row.keyspace}/{row.name}
+                            </ShardLink>
+                        </DataCell>
+                        <DataCell>
+                            <ShardServingPip isServing={row.isPrimaryServing} />{' '}
+                            {row.isPrimaryServing ? 'SERVING' : 'NOT SERVING'}
+                        </DataCell>
+                        <DataCell>
+                            {!isEmpty(row._tabletsByType) ? (
+                                <div className={style.counts}>
+                                    {Object.keys(row._tabletsByType)
+                                        .sort()
+                                        .map((tabletType) => {
+                                            const tt = row._tabletsByType[tabletType];
+                                            const allSuccess = tt.serving === tt.total;
+                                            const tooltip = allSuccess
+                                                ? `${tt.serving}/${tt.total} ${tabletType} serving`
+                                                : `${tt.total - tt.serving}/${tt.total} ${tabletType} not serving`;
+
+                                            return (
+                                                <span key={tabletType}>
+                                                    <Tooltip text={tooltip}>
+                                                        <span>
+                                                            <Pip state={allSuccess ? 'success' : 'danger'} />
+                                                        </span>
+                                                    </Tooltip>{' '}
+                                                    {tt.total} {tabletType}
+                                                </span>
+                                            );
+                                        })}
+                                </div>
+                            ) : (
+                                <span className="text-color-secondary">No tablets</span>
+                            )}
+                        </DataCell>
+                        <DataCell>
+                            {row._primaryTablet ? (
+                                <div>
+                                    <TabletLink alias={row.primaryAlias} clusterID={keyspace?.cluster?.id}>
+                                        <TabletServingPip state={row._primaryTablet.state} /> {row.primaryAlias}
+                                    </TabletLink>
+                                    <div className="font-size-small text-color-secondary">{row.primaryHostname}</div>
                                 </div>
-                            </DataCell>
-                        </tr>
-                    );
-                }
-
-                row.tablets.forEach((tablet, tdx) => {
-                    const rowKey = `${row.shard}-${tablet.alias}`;
-                    acc.push(
-                        <tr key={rowKey}>
-                            {tdx === 0 && (
-                                <DataCell rowSpan={row.tablets.length}>
-                                    <ShardLink
-                                        clusterID={keyspace?.cluster?.id}
-                                        keyspace={keyspace?.keyspace?.name}
-                                        shard={row.shard}
-                                    >
-                                        <ShardServingPip isServing={row.isShardServing} /> {row.shard}
-                                    </ShardLink>
-                                    {row.shardState === 'NOT_SERVING' && (
-                                        <div className="font-size-small text-color-secondary white-space-nowrap">
-                                            {row.shardState}
-                                        </div>
-                                    )}
-                                </DataCell>
+                            ) : (
+                                <span className="text-color-secondary">No primary tablet</span>
                             )}
-                            <DataCell>
-                                <TabletLink alias={tablet.alias} clusterID={keyspace?.cluster?.id}>
-                                    {tablet.alias}
-                                </TabletLink>
-                            </DataCell>
-                            <DataCell>{tablet.tabletType}</DataCell>
-                            <DataCell>
-                                <TabletServingPip state={tablet._tabletStateEnum} /> {tablet.tabletState}
-                            </DataCell>
-                            <DataCell>{tablet.hostname}</DataCell>
-                        </tr>
-                    );
-                });
-
-                return acc;
-            }, [] as JSX.Element[]);
+                        </DataCell>
+                    </tr>
+                );
+            });
         },
-        [filter, keyspace?.cluster?.id, keyspace?.keyspace?.name]
+        [keyspace?.cluster?.id, keyspace?.keyspace?.name]
     );
 
     if (!keyspace) {
@@ -125,123 +149,46 @@ export const KeyspaceShards = ({ keyspace }: Props) => {
     }
 
     return (
-        <ContentContainer>
+        <div className={style.container}>
             <DataFilter
                 autoFocus
                 onChange={(e) => updateFilter(e.target.value)}
                 onClear={() => updateFilter('')}
-                placeholder="Filter shards and tablets"
+                placeholder="Filter shards"
                 value={filter || ''}
             />
 
-            <DataTable columns={TABLE_COLUMNS} data={data} pageSize={PAGE_SIZE} renderRows={renderRows} />
-        </ContentContainer>
+            <DataTable columns={TABLE_COLUMNS} data={data} renderRows={renderRows} />
+        </div>
     );
 };
 
-interface Row {
-    isShardServing: boolean;
-    shard: string | null | undefined;
-    shardState: ShardState;
-    tablets: {
-        alias: string | null;
-        hostname: string | null | undefined;
-        shard: string | null | undefined;
-        tabletState: string | pb.Tablet.ServingState.UNKNOWN;
-        tabletType: string | topodata.TabletType.UNKNOWN | null | undefined;
-        _tabletStateEnum: pb.Tablet.ServingState;
-    }[];
+interface TabletCounts {
+    // tabletType is the stringified/display version of the
+    // topodata.TabletType enum.
+    [tabletType: string]: {
+        // The number of serving tablets for this type.
+        serving: number;
+        // The total number of tablets for this type.
+        total: number;
+    };
 }
 
-// Filtering data is complicated by how we group rows by their shard,
-// since we want the filtering to apply to both shard-level properties,
-// ("shard:-80") as well as nested tablet-level properties ("hostname:some-tablet-123").
-//
-// This gets surprisingly complex: what do you do when fuzzy filtering,
-// and you match a shard, and also _some_ tablets in the shard -- do you show all
-// of the tablets (since the shard matches), or (more likely) just the filtered ones?
-//
-// Instead of doing a complicated-and-hacky solution, this approach is a
-// fairly straightforward, fairly hacky, and definitely non-generalized approach
-// that still feels intuitive to use:
-//
-//  1. Filtering all the shards in the keyspace by filter string,
-//     which includes all of the tablets in that shards.
-//
-//  2. Filtering all the tablets in the keyspace (across all shards,
-//      not just the filtered shards) by filter string.
-//
-//  3. Taking the intersection of these two sets of shards/tablets.
-//
-// Investigating a more robust + general filtering implementation
-// that supports complex use cases is ticketed here:
-// https://github.com/vitessio/vitess/projects/12#card-60968004)
-export const formatRows = (
-    keyspace: pb.Keyspace | null | undefined,
-    tablets: pb.Tablet[],
-    filter: string | null | undefined
-): Row[] => {
-    if (!keyspace) {
-        return [];
-    }
+const countTablets = (tablets: pb.Tablet[]): TabletCounts => {
+    return tablets.reduce((acc, t) => {
+        // If t.tablet.type is truly an undefined/null/otherwise invalid
+        // value (i.e,. not in the proto, which should in theory never happen),
+        // then call that "UNDEFINED" so as not to co-opt the existing "UNKNOWN"
+        // tablet state.
+        const ft = formatType(t) || 'UNDEFINED';
+        if (!(ft in acc)) acc[ft] = { serving: 0, total: 0 };
 
-    // Reduce the set of tablets to just the ones for this keyspace,
-    // and map them to simplified objects to allow key/value filtering
-    // with `filterNouns`
-    const tabletsForKeyspace = orderBy(
-        tablets
-            .filter((t) => t.cluster?.id === keyspace.cluster?.id && t.tablet?.keyspace === keyspace.keyspace?.name)
-            .map((t) => ({
-                alias: formatAlias(t.tablet?.alias),
-                hostname: t.tablet?.hostname,
-                shard: t.tablet?.shard,
-                tabletState: formatState(t),
-                tabletType: formatDisplayType(t),
-                _tabletStateEnum: t.state,
-            })),
-        ['tabletType', 'alias']
-    );
+        acc[ft].total++;
 
-    // Compare tablets against the filter string
-    const filteredTablets = filterNouns(filter, tabletsForKeyspace);
-    const filteredTabletsByShard = groupBy(filteredTablets, 'shard');
-
-    const shardsForKeyspace = Object.values(keyspace.shards || {}).map((shard) => {
-        const isShardServing = !!shard.shard?.is_primary_serving;
-        return {
-            isShardServing,
-            shard: shard.name,
-            shardState: (isShardServing ? 'SERVING' : 'NOT_SERVING') as ShardState,
-        };
-    });
-    const shardsForKeyspaceByShard = groupBy(shardsForKeyspace, 'shard');
-
-    const filteredShards = filterNouns(filter, shardsForKeyspace);
-    const filteredShardsByShard = groupBy(filteredShards, 'shard');
-
-    // Take the intersection of all shardNames across...
-    const allFilteredShards = uniq([
-        //  1. Shards with tablets matching the filter criteria
-        ...Object.keys(filteredShardsByShard),
-        //  2. Shards that themselves match the filter criteria
-        ...Object.keys(filteredTabletsByShard),
-    ]);
-
-    // "Hydrate" the filtered shard names into rows
-    const rows = allFilteredShards.reduce((acc, shardName) => {
-        const shard = shardsForKeyspaceByShard[shardName][0];
-        if (!shard) {
-            return acc;
+        if (t.state === pb.Tablet.ServingState.SERVING) {
+            acc[ft].serving++;
         }
 
-        acc.push({
-            ...shard,
-            // Make sure we only take the filtered tablets
-            tablets: filteredTabletsByShard[shardName] || [],
-        });
-
         return acc;
-    }, [] as Row[]);
-
-    return orderBy(rows, 'shard');
+    }, {} as TabletCounts);
 };
diff --git a/web/vtadmin/src/util/keyspaces.ts b/web/vtadmin/src/util/keyspaces.ts
index 5cdcd912cf..f902e575ff 100644
--- a/web/vtadmin/src/util/keyspaces.ts
+++ b/web/vtadmin/src/util/keyspaces.ts
@@ -34,3 +34,38 @@ export const getShardsByState = <K extends pb.IKeyspace>(keyspace: K | null | un
         [ShardState.nonserving]: grouped.nonserving || [],
     };
 };
+
+export interface ShardRange {
+    start: number;
+    end: number;
+}
+
+/**
+ * getShardSortRange returns the start and end described by the shard name,
+ * in base 10. Ranges at the start/end of the shard range are parsed as
+ * minimum/maximum integer values. Useful for sorting shard names numerically.
+ */
+export const getShardSortRange = (shardName: string): ShardRange => {
+    if (shardName === '0' || shardName === '-') {
+        return {
+            start: Number.MIN_VALUE,
+            end: Number.MAX_VALUE,
+        };
+    }
+
+    const parsed = shardName.split('-');
+    if (parsed.length !== 2) {
+        throw Error(`could not parse sortable range from shard ${shardName}`);
+    }
+
+    // Parse the hexadecimal values into base 10 integers, and normalize the
+    // start and end of the ranges.
+    const start = parsed[0] === '' ? Number.MIN_VALUE : parseInt(parsed[0], 16);
+    const end = parsed[1] === '' ? Number.MAX_VALUE : parseInt(parsed[1], 16);
+
+    if (isNaN(start) || isNaN(end)) {
+        throw Error(`could not parse sortable range from shard ${shardName}`);
+    }
+
+    return { start, end };
+};


Test patch:
diff --git a/go/test/endtoend/preparestmt/stmt_methods_test.go b/go/test/endtoend/preparestmt/stmt_methods_test.go
index 56621aeda0..a2e079b579 100644
--- a/go/test/endtoend/preparestmt/stmt_methods_test.go
+++ b/go/test/endtoend/preparestmt/stmt_methods_test.go
@@ -379,39 +379,3 @@ func TestSelectDBA(t *testing.T) {
 	}
 	require.Equal(t, 2, rowCount)
 }
-
-func TestSelectLock(t *testing.T) {
-	defer cluster.PanicHandler(t)
-	dbo := Connect(t)
-	defer dbo.Close()
-
-	// Get Lock
-	prepare, err := dbo.Prepare("select get_lock(?, ?)")
-	require.NoError(t, err)
-
-	rows, err := prepare.Query("a", 100000)
-	require.NoError(t, err)
-
-	var resultBytes sql.RawBytes
-	require.True(t, rows.Next(), "no rows found")
-	err = rows.Scan(&resultBytes)
-	require.NoError(t, err)
-	assert.Equal(t, "1", string(resultBytes))
-
-	// for connection to be reused.
-	err = rows.Close()
-	require.NoError(t, err)
-
-	// Release Lock
-	prepare, err = dbo.Prepare("select release_lock(?)")
-	require.NoError(t, err)
-
-	rows, err = prepare.Query("a")
-	require.NoError(t, err)
-	defer rows.Close()
-
-	require.True(t, rows.Next(), "no rows found")
-	err = rows.Scan(&resultBytes)
-	require.NoError(t, err)
-	assert.Equal(t, "1", string(resultBytes))
-}
diff --git a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go
index 197892f9ef..3183b5f97a 100644
--- a/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go
+++ b/go/test/endtoend/vtgate/queries/aggregation/aggregation_test.go
@@ -18,7 +18,6 @@ package aggregation
 
 import (
 	"context"
-	"fmt"
 	"testing"
 
 	"vitess.io/vitess/go/test/endtoend/vtgate/utils"
@@ -86,203 +85,3 @@ func TestDistinct(t *testing.T) {
 	utils.Exec(t, conn, "delete from t7_xxhash")
 	utils.Exec(t, conn, "delete from aggr_test")
 }
-
-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()
-
-	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) {
-			utils.Exec(t, conn, fmt.Sprintf("set workload = '%s'", workload))
-
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having 1 = 1", `[[INT64(5)]]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a = 5", `[[INT64(5)]]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having 5 = a", `[[INT64(5)]]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a = a", `[[INT64(5)]]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a = 3+2", `[[INT64(5)]]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having 1+4 = 3+2", `[[INT64(5)]]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a = 1", `[]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a = \"1\"", `[]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a = \"5\"", `[[INT64(5)]]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a = 5.00", `[[INT64(5)]]`)
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a, val1 from aggr_test group by val1 having a = 1.00", `[[INT64(1) VARCHAR("a")] [INT64(1) VARCHAR("b")] [INT64(1) VARCHAR("c")] [INT64(1) VARCHAR("d")] [INT64(1) VARCHAR("e")]]`)
-
-			utils.AssertContainsError(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ 1 from aggr_test having count(*) = 5", `expr cannot be converted, not supported: *sqlparser.FuncExpr`) // will fail since `count(*)` is a FuncExpr
-		})
-	}
-}
-
-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()
-
-	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) {
-			utils.Exec(t, conn, fmt.Sprintf("set workload = '%s'", workload))
-
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a != 5", `[]`)               // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having 5 != a", `[]`)               // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a != a", `[]`)               // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a != 3+2", `[]`)             // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a != 1", `[[INT64(5)]]`)     // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a != \"1\"", `[[INT64(5)]]`) // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a != \"5\"", `[]`)           // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a != 5.00", `[]`)            // having clause
-
-			utils.AssertContainsError(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ 1 from aggr_test having count(*) != 5", `expr cannot be converted, not supported: *sqlparser.FuncExpr`) // will fail since `count(*)` is a FuncExpr
-		})
-	}
-}
-
-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()
-
-	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) {
-			utils.Exec(t, conn, fmt.Sprintf("set workload = '%s'", workload))
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a < 10", `[[INT64(5)]]`)     // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having 1 < a", `[[INT64(5)]]`)      // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a < a", `[]`)                // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a < 3+2", `[]`)              // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a < 1", `[]`)                // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a < \"10\"", `[[INT64(5)]]`) // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a < \"5\"", `[]`)            // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a < 6.00", `[[INT64(5)]]`)   // having clause
-
-			utils.AssertContainsError(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ 1 from aggr_test having count(*) < 5", `expr cannot be converted, not supported: *sqlparser.FuncExpr`) // will fail since `count(*)` is a FuncExpr
-		})
-	}
-}
-
-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()
-
-	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) {
-			utils.Exec(t, conn, fmt.Sprintf("set workload = '%s'", workload))
-
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a <= 10", `[[INT64(5)]]`)     // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having 1 <= a", `[[INT64(5)]]`)      // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a <= a", `[[INT64(5)]]`)      // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a <= 3+2", `[[INT64(5)]]`)    // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a <= 1", `[]`)                // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a <= \"10\"", `[[INT64(5)]]`) // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a <= \"5\"", `[[INT64(5)]]`)  // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a <= 5.00", `[[INT64(5)]]`)   // having clause
-
-			utils.AssertContainsError(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ 1 from aggr_test having count(*) <= 5", `expr cannot be converted, not supported: *sqlparser.FuncExpr`) // will fail since `count(*)` is a FuncExpr
-		})
-	}
-}
-
-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()
-
-	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) {
-			utils.Exec(t, conn, fmt.Sprintf("set workload = '%s'", workload))
-
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a > 1", `[[INT64(5)]]`)     // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having 1 > a", `[]`)               // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a > a", `[]`)               // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a > 3+1", `[[INT64(5)]]`)   // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a > 10", `[]`)              // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a > \"1\"", `[[INT64(5)]]`) // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a > \"5\"", `[]`)           // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a > 4.00", `[[INT64(5)]]`)  // having clause
-
-			utils.AssertContainsError(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ 1 from aggr_test having count(*) > 5", `expr cannot be converted, not supported: *sqlparser.FuncExpr`) // will fail since `count(*)` is a FuncExpr
-		})
-	}
-}
-
-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()
-
-	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) {
-			utils.Exec(t, conn, fmt.Sprintf("set workload = '%s'", workload))
-
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a >= 1", `[[INT64(5)]]`)     // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having 1 >= a", `[]`)               // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a >= a", `[[INT64(5)]]`)     // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a >= 3+2", `[[INT64(5)]]`)   // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a >= 10", `[]`)              // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a >= \"1\"", `[[INT64(5)]]`) // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a >= \"5\"", `[[INT64(5)]]`) // having clause
-			utils.AssertMatches(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ count(*) as a from aggr_test having a >= 5.00", `[[INT64(5)]]`)  // having clause
-
-			utils.AssertContainsError(t, conn, "select /* GEN4_COMPARE_ONLY_GEN4 */ 1 from aggr_test having count(*) >= 5", `expr cannot be converted, not supported: *sqlparser.FuncExpr`) // will fail since `count(*)` is a FuncExpr
-		})
-	}
-}
diff --git a/go/test/endtoend/vtgate/queries/aggregation/main_test.go b/go/test/endtoend/vtgate/queries/aggregation/main_test.go
index 82ed2c7762..efe5cc5bb8 100644
--- a/go/test/endtoend/vtgate/queries/aggregation/main_test.go
+++ b/go/test/endtoend/vtgate/queries/aggregation/main_test.go
@@ -51,13 +51,6 @@ create table aggr_test(
 	primary key(id)
 ) Engine=InnoDB;
 
-create table aggr_test_dates(
-	id bigint,
-	val1 datetime default current_timestamp,
-	val2 datetime default current_timestamp,
-	primary key(id)
-) Engine=InnoDB;
-
 create table t7_xxhash(
 	uid varchar(50),
 	phone bigint,
@@ -136,24 +129,6 @@ create table t7_xxhash_idx(
           "type": "VARCHAR"
         }
       ]
-    },
-	"aggr_test_dates": {
-      "column_vindexes": [
-        {
-          "column": "id",
-          "name": "hash"
-        }
-      ],
-      "columns": [
-        {
-          "name": "val1",
-          "type": "DATETIME"
-        },
-        {
-          "name": "val2",
-          "type": "DATETIME"
-        }
-      ]
     },
 	"t7_xxhash": {
       "column_vindexes": [
diff --git a/go/test/endtoend/vtgate/reservedconn/udv_test.go b/go/test/endtoend/vtgate/reservedconn/udv_test.go
index b548489c7f..391fc722a4 100644
--- a/go/test/endtoend/vtgate/reservedconn/udv_test.go
+++ b/go/test/endtoend/vtgate/reservedconn/udv_test.go
@@ -100,7 +100,7 @@ func TestSetUDV(t *testing.T) {
 		expectedRows: `[]`,
 	}, {
 		query:        "select @foo = @bar, @dd, @tt",
-		expectedRows: `[[INT32(1) DATE("2020-10-20") TIME("10:15:00")]]`, rowsReturned: 1,
+		expectedRows: `[[INT64(1) VARCHAR("2020-10-20") VARCHAR("10:15:00")]]`, rowsReturned: 1,
 	}}
 
 	conn, err := mysql.Connect(ctx, &vtParams)
diff --git a/go/test/endtoend/vtgate/utils/utils.go b/go/test/endtoend/vtgate/utils/utils.go
index 7f6a8dc0c2..1b3722daea 100644
--- a/go/test/endtoend/vtgate/utils/utils.go
+++ b/go/test/endtoend/vtgate/utils/utils.go
@@ -39,13 +39,6 @@ func AssertMatches(t *testing.T, conn *mysql.Conn, query, expected string) {
 	}
 }
 
-func AssertContainsError(t *testing.T, conn *mysql.Conn, query, expected string) {
-	t.Helper()
-	_, err := ExecAllowError(t, conn, query)
-	require.Error(t, err)
-	require.Contains(t, err.Error(), expected)
-}
-
 func AssertMatchesNoOrder(t *testing.T, conn *mysql.Conn, query, expected string) {
 	t.Helper()
 	qr := Exec(t, conn, query)
@@ -90,8 +83,3 @@ func Exec(t *testing.T, conn *mysql.Conn, query string) *sqltypes.Result {
 	require.NoError(t, err, "for query: "+query)
 	return qr
 }
-
-func ExecAllowError(t *testing.T, conn *mysql.Conn, query string) (*sqltypes.Result, error) {
-	t.Helper()
-	return conn.ExecuteFetch(query, 1000, true)
-}
diff --git a/go/vt/vtgate/evalengine/convert_test.go b/go/vt/sqlparser/expressions_test.go
similarity index 88%
rename from go/vt/vtgate/evalengine/convert_test.go
rename to go/vt/sqlparser/expressions_test.go
index 740707ecd0..a72c50f198 100644
--- a/go/vt/vtgate/evalengine/convert_test.go
+++ b/go/vt/sqlparser/expressions_test.go
@@ -1,5 +1,5 @@
 /*
-Copyright 2021 The Vitess Authors.
+Copyright 2020 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.
@@ -14,12 +14,12 @@ See the License for the specific language governing permissions and
 limitations under the License.
 */
 
-package evalengine
+package sqlparser
 
 import (
 	"testing"
 
-	"vitess.io/vitess/go/vt/sqlparser"
+	"vitess.io/vitess/go/vt/vtgate/evalengine"
 
 	"vitess.io/vitess/go/sqltypes"
 
@@ -75,13 +75,13 @@ func TestEvaluate(t *testing.T) {
 	for _, test := range tests {
 		t.Run(test.expression, func(t *testing.T) {
 			// Given
-			stmt, err := sqlparser.Parse("select " + test.expression)
+			stmt, err := Parse("select " + test.expression)
 			require.NoError(t, err)
-			astExpr := stmt.(*sqlparser.Select).SelectExprs[0].(*sqlparser.AliasedExpr).Expr
-			sqltypesExpr, err := Convert(astExpr, nil)
+			astExpr := stmt.(*Select).SelectExprs[0].(*AliasedExpr).Expr
+			sqltypesExpr, err := Convert(astExpr)
 			require.Nil(t, err)
 			require.NotNil(t, sqltypesExpr)
-			env := ExpressionEnv{
+			env := evalengine.ExpressionEnv{
 				BindVars: map[string]*querypb.BindVariable{
 					"exp":                  sqltypes.Int64BindVariable(66),
 					"string_bind_variable": sqltypes.StringBindVariable("bar"),
diff --git a/go/vt/vtgate/engine/ordered_aggregate_test.go b/go/vt/vtgate/engine/ordered_aggregate_test.go
index 2f6a27b582..631fe2bb11 100644
--- a/go/vt/vtgate/engine/ordered_aggregate_test.go
+++ b/go/vt/vtgate/engine/ordered_aggregate_test.go
@@ -24,7 +24,6 @@ import (
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
-	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/sqltypes"
 	"vitess.io/vitess/go/test/utils"
 
@@ -448,7 +447,6 @@ func TestOrderedAggregateSumDistinctGood(t *testing.T) {
 			"c|4|1",
 			// Single null
 			"d|null|1",
-			"d|1|1",
 			// Start with null
 			"e|null|1",
 			"e|1|1",
@@ -497,16 +495,14 @@ func TestOrderedAggregateSumDistinctGood(t *testing.T) {
 		"a|1|3",
 		"b|1|1",
 		"c|7|2",
-		"d|1|2",
+		"d|null|1",
 		"e|1|2",
 		"f|1|2",
 		"g|6|4",
 		"h|6|4",
 		"i|7|2",
 	)
-	want := fmt.Sprintf("%v", wantResult.Rows)
-	got := fmt.Sprintf("%v", result.Rows)
-	assert.Equal(want, got)
+	assert.Equal(wantResult, result)
 }
 
 func TestOrderedAggregateSumDistinctTolerateError(t *testing.T) {
@@ -656,13 +652,13 @@ func TestMerge(t *testing.T) {
 		"1|3|2.8|2|bc",
 	)
 
-	merged, _, err := oa.merge(fields, r.Rows[0], r.Rows[1], nil, nil)
+	merged, _, err := oa.merge(fields, r.Rows[0], r.Rows[1], nil)
 	assert.NoError(err)
 	want := sqltypes.MakeTestResult(fields, "1|5|6|2|bc").Rows[0]
 	assert.Equal(want, merged)
 
 	// swap and retry
-	merged, _, err = oa.merge(fields, r.Rows[1], r.Rows[0], nil, nil)
+	merged, _, err = oa.merge(fields, r.Rows[1], r.Rows[0], nil)
 	assert.NoError(err)
 	assert.Equal(want, merged)
 }
@@ -1054,136 +1050,3 @@ func TestMultiDistinct(t *testing.T) {
 	require.NoError(t, err)
 	assert.Equal(t, want, results)
 }
-
-func TestOrderedAggregateCollate(t *testing.T) {
-	assert := assert.New(t)
-	fields := sqltypes.MakeTestFields(
-		"col|count(*)",
-		"varchar|decimal",
-	)
-	fp := &fakePrimitive{
-		results: []*sqltypes.Result{sqltypes.MakeTestResult(
-			fields,
-			"a|1",
-			"A|1",
-			"Ǎ|1",
-			"b|2",
-			"B|-1",
-			"c|3",
-			"c|4",
-			"ß|11",
-			"ss|2",
-		)},
-	}
-
-	collationID, _ := collations.Default().LookupID("utf8mb4_0900_ai_ci")
-	oa := &OrderedAggregate{
-		Aggregates: []*AggregateParams{{
-			Opcode: AggregateCount,
-			Col:    1,
-		}},
-		GroupByKeys: []*GroupByParams{{KeyCol: 0, CollationID: collationID}},
-		Input:       fp,
-	}
-
-	result, err := oa.TryExecute(&noopVCursor{}, nil, false)
-	assert.NoError(err)
-
-	wantResult := sqltypes.MakeTestResult(
-		fields,
-		"a|3",
-		"b|1",
-		"c|7",
-		"ß|13",
-	)
-	assert.Equal(wantResult, result)
-}
-
-func TestOrderedAggregateCollateAS(t *testing.T) {
-	assert := assert.New(t)
-	fields := sqltypes.MakeTestFields(
-		"col|count(*)",
-		"varchar|decimal",
-	)
-	fp := &fakePrimitive{
-		results: []*sqltypes.Result{sqltypes.MakeTestResult(
-			fields,
-			"a|1",
-			"A|1",
-			"Ǎ|1",
-			"b|2",
-			"c|3",
-			"c|4",
-			"Ç|4",
-		)},
-	}
-
-	collationID, _ := collations.Default().LookupID("utf8mb4_0900_as_ci")
-	oa := &OrderedAggregate{
-		Aggregates: []*AggregateParams{{
-			Opcode: AggregateCount,
-			Col:    1,
-		}},
-		GroupByKeys: []*GroupByParams{{KeyCol: 0, CollationID: collationID}},
-		Input:       fp,
-	}
-
-	result, err := oa.TryExecute(&noopVCursor{}, nil, false)
-	assert.NoError(err)
-
-	wantResult := sqltypes.MakeTestResult(
-		fields,
-		"a|2",
-		"Ǎ|1",
-		"b|2",
-		"c|7",
-		"Ç|4",
-	)
-	assert.Equal(wantResult, result)
-}
-
-func TestOrderedAggregateCollateKS(t *testing.T) {
-	assert := assert.New(t)
-	fields := sqltypes.MakeTestFields(
-		"col|count(*)",
-		"varchar|decimal",
-	)
-	fp := &fakePrimitive{
-		results: []*sqltypes.Result{sqltypes.MakeTestResult(
-			fields,
-			"a|1",
-			"A|1",
-			"Ǎ|1",
-			"b|2",
-			"c|3",
-			"c|4",
-			"\xE3\x83\x8F\xE3\x81\xAF|2",
-			"\xE3\x83\x8F\xE3\x83\x8F|1",
-		)},
-	}
-
-	collationID, _ := collations.Default().LookupID("utf8mb4_ja_0900_as_cs_ks")
-	oa := &OrderedAggregate{
-		Aggregates: []*AggregateParams{{
-			Opcode: AggregateCount,
-			Col:    1,
-		}},
-		GroupByKeys: []*GroupByParams{{KeyCol: 0, CollationID: collationID}},
-		Input:       fp,
-	}
-
-	result, err := oa.TryExecute(&noopVCursor{}, nil, false)
-	assert.NoError(err)
-
-	wantResult := sqltypes.MakeTestResult(
-		fields,
-		"a|1",
-		"A|1",
-		"Ǎ|1",
-		"b|2",
-		"c|7",
-		"\xE3\x83\x8F\xE3\x81\xAF|2",
-		"\xE3\x83\x8F\xE3\x83\x8F|1",
-	)
-	assert.Equal(wantResult, result)
-}
diff --git a/go/vt/vtgate/engine/route_test.go b/go/vt/vtgate/engine/route_test.go
index 6cf0b91f96..e2d8d5f218 100644
--- a/go/vt/vtgate/engine/route_test.go
+++ b/go/vt/vtgate/engine/route_test.go
@@ -82,7 +82,7 @@ func TestSelectInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T)
 	stringListToExprList := func(in []string) []evalengine.Expr {
 		var schema []evalengine.Expr
 		for _, s := range in {
-			schema = append(schema, evalengine.NewLiteralString([]byte(s), 0))
+			schema = append(schema, evalengine.NewLiteralString([]byte(s)))
 		}
 		return schema
 	}
@@ -97,7 +97,7 @@ func TestSelectInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T)
 	tests := []testCase{{
 		testName:    "both schema and table predicates - routed table",
 		tableSchema: []string{"schema"},
-		tableName:   map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("table"), 0)},
+		tableName:   map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("table"))},
 		routed:      true,
 		expectedLog: []string{
 			"FindTable(`schema`.`table`)",
@@ -106,7 +106,7 @@ func TestSelectInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T)
 	}, {
 		testName:    "both schema and table predicates - not routed",
 		tableSchema: []string{"schema"},
-		tableName:   map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("table"), 0)},
+		tableName:   map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("table"))},
 		routed:      false,
 		expectedLog: []string{
 			"FindTable(`schema`.`table`)",
@@ -115,7 +115,7 @@ func TestSelectInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T)
 	}, {
 		testName:    "multiple schema and table predicates",
 		tableSchema: []string{"schema", "schema", "schema"},
-		tableName:   map[string]evalengine.Expr{"t1": evalengine.NewLiteralString([]byte("table"), 0), "t2": evalengine.NewLiteralString([]byte("table"), 0), "t3": evalengine.NewLiteralString([]byte("table"), 0)},
+		tableName:   map[string]evalengine.Expr{"t1": evalengine.NewLiteralString([]byte("table")), "t2": evalengine.NewLiteralString([]byte("table")), "t3": evalengine.NewLiteralString([]byte("table"))},
 		routed:      false,
 		expectedLog: []string{
 			"FindTable(`schema`.`table`)",
@@ -125,7 +125,7 @@ func TestSelectInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T)
 			"ExecuteMultiShard schema.1: dummy_select {__replacevtschemaname: type:INT64 value:\"1\" t1: type:VARBINARY value:\"table\" t2: type:VARBINARY value:\"table\" t3: type:VARBINARY value:\"table\"} false false"},
 	}, {
 		testName:  "table name predicate - routed table",
-		tableName: map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("tableName"), 0)},
+		tableName: map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("tableName"))},
 		routed:    true,
 		expectedLog: []string{
 			"FindTable(tableName)",
@@ -133,7 +133,7 @@ func TestSelectInformationSchemaWithTableAndSchemaWithRoutedTables(t *testing.T)
 			"ExecuteMultiShard routedKeyspace.1: dummy_select {table_name: type:VARBINARY value:\"routedTable\"} false false"},
 	}, {
 		testName:  "table name predicate - not routed",
-		tableName: map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("tableName"), 0)},
+		tableName: map[string]evalengine.Expr{"table_name": evalengine.NewLiteralString([]byte("tableName"))},
 		routed:    false,
 		expectedLog: []string{
 			"FindTable(tableName)",
diff --git a/go/vt/vtgate/engine/set_test.go b/go/vt/vtgate/engine/set_test.go
index 1464b0cee4..42700b6886 100644
--- a/go/vt/vtgate/engine/set_test.go
+++ b/go/vt/vtgate/engine/set_test.go
@@ -98,7 +98,7 @@ func TestSetTable(t *testing.T) {
 		setOps: []SetOp{
 			&UserDefinedVariable{
 				Name: "x",
-				Expr: evalengine.NewColumn(0, 0),
+				Expr: evalengine.NewColumn(0),
 			},
 		},
 		qr: []*sqltypes.Result{sqltypes.MakeTestResult(
diff --git a/go/vt/vtgate/evalengine/arithmetic_test.go b/go/vt/vtgate/evalengine/arithmetic_test.go
index f8cc3160e1..5575196baa 100644
--- a/go/vt/vtgate/evalengine/arithmetic_test.go
+++ b/go/vt/vtgate/evalengine/arithmetic_test.go
@@ -24,7 +24,6 @@ import (
 	"strconv"
 	"testing"
 
-	"vitess.io/vitess/go/mysql/collations"
 	"vitess.io/vitess/go/test/utils"
 
 	"github.com/stretchr/testify/assert"
@@ -44,8 +43,6 @@ var (
 	NewUint64  = sqltypes.NewUint64
 	NewFloat64 = sqltypes.NewFloat64
 	TestValue  = sqltypes.TestValue
-
-	maxUint64 uint64 = math.MaxUint64
 )
 
 func TestArithmetics(t *testing.T) {
@@ -85,11 +82,11 @@ func TestArithmetics(t *testing.T) {
 			// testing for int64 overflow with min negative value
 			v1:  NewInt64(math.MinInt64),
 			v2:  NewInt64(1),
-			err: dataOutOfRangeError(math.MinInt64, 1, "BIGINT", "-").Error(),
+			err: "BIGINT value is out of range in -9223372036854775808 - 1",
 		}, {
 			v1:  NewUint64(4),
 			v2:  NewInt64(5),
-			err: dataOutOfRangeError(4, 5, "BIGINT UNSIGNED", "-").Error(),
+			err: "BIGINT UNSIGNED value is out of range in 4 - 5",
 		}, {
 			// testing uint - int
 			v1:  NewUint64(7),
@@ -103,7 +100,7 @@ func TestArithmetics(t *testing.T) {
 			// testing for int64 overflow
 			v1:  NewInt64(math.MinInt64),
 			v2:  NewUint64(0),
-			err: dataOutOfRangeError(math.MinInt64, 0, "BIGINT UNSIGNED", "-").Error(),
+			err: "BIGINT UNSIGNED value is out of range in -9223372036854775808 - 0",
 		}, {
 			v1:  TestValue(querypb.Type_VARCHAR, "c"),
 			v2:  NewInt64(1),
@@ -140,7 +137,7 @@ func TestArithmetics(t *testing.T) {
 		}, {
 			v1:  NewInt64(-1),
 			v2:  NewUint64(2),
-			err: dataOutOfRangeError(-1, 2, "BIGINT UNSIGNED", "-").Error(),
+			err: "BIGINT UNSIGNED value is out of range in -1 - 2",
 		}, {
 			v1:  NewInt64(2),
 			v2:  NewUint64(1),
@@ -169,7 +166,7 @@ func TestArithmetics(t *testing.T) {
 			// testing uint - uint if v2 > v1
 			v1:  NewUint64(2),
 			v2:  NewUint64(4),
-			err: dataOutOfRangeError(2, 4, "BIGINT UNSIGNED", "-").Error(),
+			err: "BIGINT UNSIGNED value is out of range in 2 - 4",
 		}, {
 			// testing uint - (- int)
 			v1:  NewUint64(1),
@@ -207,7 +204,7 @@ func TestArithmetics(t *testing.T) {
 		}, {
 			v1:  NewInt64(-2),
 			v2:  NewUint64(1),
-			err: dataOutOfRangeError(1, -2, "BIGINT UNSIGNED", "+").Error(),
+			err: "BIGINT UNSIGNED value is out of range in 1 + -2",
 		}, {
 			v1:  NewInt64(math.MaxInt64),
 			v2:  NewInt64(-2),
@@ -219,14 +216,14 @@ func TestArithmetics(t *testing.T) {
 			out: NewUint64(3),
 		}, {
 			// testing for overflow uint64
-			v1:  NewUint64(maxUint64),
+			v1:  NewUint64(math.MaxUint64),
 			v2:  NewUint64(2),
-			err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "+").Error(),
+			err: "BIGINT UNSIGNED value is out of range in 18446744073709551615 + 2",
 		}, {
 			// int64 underflow
 			v1:  NewInt64(math.MinInt64),
 			v2:  NewInt64(-2),
-			err: dataOutOfRangeError(math.MinInt64, -2, "BIGINT", "+").Error(),
+			err: "BIGINT value is out of range in -9223372036854775808 + -2",
 		}, {
 			// checking int64 max value can be returned
 			v1:  NewInt64(math.MaxInt64),
@@ -259,9 +256,9 @@ func TestArithmetics(t *testing.T) {
 			err: "strconv.ParseInt: parsing \"1.2\": invalid syntax",
 		}, {
 			// testing for uint64 overflow with max uint64 + int value
-			v1:  NewUint64(maxUint64),
+			v1:  NewUint64(math.MaxUint64),
 			v2:  NewInt64(2),
-			err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "+").Error(),
+			err: "BIGINT UNSIGNED value is out of range in 18446744073709551615 + 2",
 		}},
 	}, {
 		operator: "/",
@@ -345,7 +342,7 @@ func TestArithmetics(t *testing.T) {
 			// testing for overflow of float64
 			v1:  NewFloat64(math.MaxFloat64),
 			v2:  NewFloat64(0.5),
-			err: dataOutOfRangeError(math.MaxFloat64, 0.5, "BIGINT", "/").Error(),
+			err: "BIGINT value is out of range in 1.7976931348623157e+308 / 0.5",
 		}},
 	}, {
 		operator: "*",
@@ -409,12 +406,12 @@ func TestArithmetics(t *testing.T) {
 			// testing for overflow of int64
 			v1:  NewInt64(math.MaxInt64),
 			v2:  NewInt64(2),
-			err: dataOutOfRangeError(math.MaxInt64, 2, "BIGINT", "*").Error(),
+			err: "BIGINT value is out of range in 9223372036854775807 * 2",
 		}, {
 			// testing for underflow of uint64*max.uint64
 			v1:  NewInt64(2),
-			v2:  NewUint64(maxUint64),
-			err: dataOutOfRangeError(maxUint64, 2, "BIGINT UNSIGNED", "*").Error(),
+			v2:  NewUint64(math.MaxUint64),
+			err: "BIGINT UNSIGNED value is out of range in 18446744073709551615 * 2",
 		}, {
 			v1:  NewUint64(math.MaxUint64),
 			v2:  NewUint64(1),
@@ -423,7 +420,7 @@ func TestArithmetics(t *testing.T) {
 			//Checking whether maxInt value can be passed as uint value
 			v1:  NewUint64(math.MaxInt64),
 			v2:  NewInt64(3),
-			err: dataOutOfRangeError(math.MaxInt64, 3, "BIGINT UNSIGNED", "*").Error(),
+			err: "BIGINT UNSIGNED value is out of range in 9223372036854775807 * 3",
 		}},
 	}}
 
@@ -445,7 +442,7 @@ func TestArithmetics(t *testing.T) {
 	}
 }
 
-func TestNullSafeAdd(t *testing.T) {
+func TestNullsafeAdd(t *testing.T) {
 	tcases := []struct {
 		v1, v2 sqltypes.Value
 		out    sqltypes.Value
@@ -484,11 +481,7 @@ func TestNullSafeAdd(t *testing.T) {
 		// Make sure underlying error is returned while adding.
 		v1:  NewInt64(-1),
 		v2:  NewUint64(2),
-		out: NewInt64(1),
-	}, {
-		v1:  NewInt64(-100),
-		v2:  NewUint64(10),
-		err: dataOutOfRangeError(10, -100, "BIGINT UNSIGNED", "+"),
+		out: NewInt64(-9223372036854775808),
 	}, {
 		// Make sure underlying error is returned while converting.
 		v1:  NewFloat64(1),
@@ -496,16 +489,10 @@ func TestNullSafeAdd(t *testing.T) {
 		out: NewInt64(3),
 	}}
 	for _, tcase := range tcases {
-		got, err := NullSafeAdd(tcase.v1, tcase.v2, querypb.Type_INT64)
-
-		if tcase.err == nil {
-			require.NoError(t, err)
-		} else {
-			require.EqualError(t, err, tcase.err.Error())
-		}
+		got := NullsafeAdd(tcase.v1, tcase.v2, querypb.Type_INT64)
 
 		if !reflect.DeepEqual(got, tcase.out) {
-			t.Errorf("NullSafeAdd(%v, %v): %v, want %v", printValue(tcase.v1), printValue(tcase.v2), printValue(got), printValue(tcase.out))
+			t.Errorf("NullsafeAdd(%v, %v): %v, want %v", printValue(tcase.v1), printValue(tcase.v2), printValue(got), printValue(tcase.out))
 		}
 	}
 }
@@ -597,7 +584,7 @@ func TestNullsafeCompare(t *testing.T) {
 		out: -1,
 	}}
 	for _, tcase := range tcases {
-		got, err := NullsafeCompare(tcase.v1, tcase.v2, collations.Unknown)
+		got, err := NullsafeCompare(tcase.v1, tcase.v2)
 		if !vterrors.Equals(err, tcase.err) {
 			t.Errorf("NullsafeCompare(%v, %v) error: %v, want %v", printValue(tcase.v1), printValue(tcase.v2), vterrors.Print(err), vterrors.Print(tcase.err))
 		}
@@ -611,95 +598,6 @@ func TestNullsafeCompare(t *testing.T) {
 	}
 }
 
-func getCollationID(collation string) collations.ID {
-	id, _ := collations.Default().LookupID(collation)
-	return id
-}
-
-func TestNullsafeCompareCollate(t *testing.T) {
-	tcases := []struct {
-		v1, v2    string
-		collation collations.ID
-		out       int
-		err       error
-	}{
-		{
-			// case insensitive
-			v1:        "abCd",
-			v2:        "aBcd",
-			out:       0,
-			collation: getCollationID("utf8mb4_0900_as_ci"),
-		},
-		{
-			// accent sensitive
-			v1:        "ǍḄÇ",
-			v2:        "ÁḆĈ",
-			out:       1,
-			collation: getCollationID("utf8mb4_0900_as_ci"),
-		},
-		{
-			// hangul decomposition
-			v1:        "\uAC00",
-			v2:        "\u326E",
-			out:       0,
-			collation: getCollationID("utf8mb4_0900_as_ci"),
-		},
-		{
-			// kana sensitive
-			v1:        "\xE3\x81\xAB\xE3\x81\xBB\xE3\x82\x93\xE3\x81\x94",
-			v2:        "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4",
-			out:       -1,
-			collation: getCollationID("utf8mb4_ja_0900_as_cs_ks"),
-		},
-		{
-			// non breaking space
-			v1:        "abc ",
-			v2:        "abc\u00a0",
-			out:       -1,
-			collation: getCollationID("utf8mb4_0900_as_cs"),
-		},
-		{
-			// "cs" counts as a separate letter, where c < cs < d
-			v1:        "c",
-			v2:        "cs",
-			out:       -1,
-			collation: getCollationID("utf8mb4_hu_0900_ai_ci"),
-		},
-		{
-			v1:        "abcd",
-			v2:        "abcd",
-			collation: 0,
-			err:       vterrors.New(vtrpcpb.Code_UNKNOWN, "types are not comparable: VARCHAR vs VARCHAR"),
-		},
-		{
-			v1:        "abcd",
-			v2:        "abcd",
-			collation: 1111,
-			err:       vterrors.New(vtrpcpb.Code_UNKNOWN, "comparison using collation 1111 isn't possible"),
-		},
-		{
-			v1: "abcd",
-			v2: "abcd",
-			// unsupported collation gb18030_bin
-			collation: 249,
-			err:       vterrors.New(vtrpcpb.Code_UNKNOWN, "comparison using collation 249 isn't possible"),
-		},
-	}
-	for _, tcase := range tcases {
-		got, err := NullsafeCompare(TestValue(querypb.Type_VARCHAR, tcase.v1), TestValue(querypb.Type_VARCHAR, tcase.v2), tcase.collation)
-		if !vterrors.Equals(err, tcase.err) {
-			t.Errorf("NullsafeCompare(%v, %v) error: %v, want %v", tcase.v1, tcase.v2, vterrors.Print(err), vterrors.Print(tcase.err))
-		}
-		if tcase.err != nil {
-			continue
-		}
-
-		if got != tcase.out {
-			t.Errorf("NullsafeCompare(%v, %v): %v, want %v", tcase.v1, tcase.v2, got, tcase.out)
-		}
-	}
-}
-
 func TestCast(t *testing.T) {
 	tcases := []struct {
 		typ querypb.Type
@@ -1214,9 +1112,9 @@ func TestPrioritize(t *testing.T) {
 	}}
 	for _, tcase := range tcases {
 		t.Run(tcase.v1.Value().String()+" - "+tcase.v2.Value().String(), func(t *testing.T) {
-			got1, got2 := makeNumericAndPrioritize(tcase.v1, tcase.v2)
-			utils.MustMatch(t, tcase.out1, got1, "makeNumericAndPrioritize")
-			utils.MustMatch(t, tcase.out2, got2, "makeNumericAndPrioritize")
+			got1, got2 := makeNumericAndprioritize(tcase.v1, tcase.v2)
+			utils.MustMatch(t, tcase.out1, got1, "makeNumericAndprioritize")
+			utils.MustMatch(t, tcase.out2, got2, "makeNumericAndprioritize")
 		})
 	}
 }
@@ -1370,7 +1268,7 @@ func TestMin(t *testing.T) {
 		err: vterrors.New(vtrpcpb.Code_UNKNOWN, "types are not comparable: VARCHAR vs VARCHAR"),
 	}}
 	for _, tcase := range tcases {
-		v, err := Min(tcase.v1, tcase.v2, collations.Unknown)
+		v, err := Min(tcase.v1, tcase.v2)
 		if !vterrors.Equals(err, tcase.err) {
 			t.Errorf("Min error: %v, want %v", vterrors.Print(err), vterrors.Print(tcase.err))
 		}
@@ -1384,64 +1282,6 @@ func TestMin(t *testing.T) {
 	}
 }
 
-func TestMinCollate(t *testing.T) {
-	tcases := []struct {
-		v1, v2    string
-		collation collations.ID
-		out       string
-		err       error
-	}{
-		{
-			// accent insensitive
-			v1:        "ǍḄÇ",
-			v2:        "ÁḆĈ",
-			out:       "ǍḄÇ",
-			collation: getCollationID("utf8mb4_0900_as_ci"),
-		},
-		{
-			// kana sensitive
-			v1:        "\xE3\x81\xAB\xE3\x81\xBB\xE3\x82\x93\xE3\x81\x94",
-			v2:        "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4",
-			out:       "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4",
-			collation: getCollationID("utf8mb4_ja_0900_as_cs_ks"),
-		},
-		{
-			// non breaking space
-			v1:        "abc ",
-			v2:        "abc\u00a0",
-			out:       "abc\u00a0",
-			collation: getCollationID("utf8mb4_0900_as_cs"),
-		},
-		{
-			// "cs" counts as a separate letter, where c < cs < d
-			v1:        "c",
-			v2:        "cs",
-			out:       "cs",
-			collation: getCollationID("utf8mb4_hu_0900_ai_ci"),
-		},
-		{
-			// "cs" counts as a separate letter, where c < cs < d
-			v1:        "cukor",
-			v2:        "csak",
-			out:       "csak",
-			collation: getCollationID("utf8mb4_hu_0900_ai_ci"),
-		},
-	}
-	for _, tcase := range tcases {
-		got, err := Min(TestValue(querypb.Type_VARCHAR, tcase.v1), TestValue(querypb.Type_VARCHAR, tcase.v2), tcase.collation)
-		if !vterrors.Equals(err, tcase.err) {
-			t.Errorf("NullsafeCompare(%v, %v) error: %v, want %v", tcase.v1, tcase.v2, vterrors.Print(err), vterrors.Print(tcase.err))
-		}
-		if tcase.err != nil {
-			continue
-		}
-
-		if got.ToString() == tcase.out {
-			t.Errorf("NullsafeCompare(%v, %v): %v, want %v", tcase.v1, tcase.v2, got, tcase.out)
-		}
-	}
-}
-
 func TestMax(t *testing.T) {
 	tcases := []struct {
 		v1, v2 sqltypes.Value
@@ -1477,7 +1317,7 @@ func TestMax(t *testing.T) {
 		err: vterrors.New(vtrpcpb.Code_UNKNOWN, "types are not comparable: VARCHAR vs VARCHAR"),
 	}}
 	for _, tcase := range tcases {
-		v, err := Max(tcase.v1, tcase.v2, collations.Unknown)
+		v, err := Max(tcase.v1, tcase.v2)
 		if !vterrors.Equals(err, tcase.err) {
 			t.Errorf("Max error: %v, want %v", vterrors.Print(err), vterrors.Print(tcase.err))
 		}
@@ -1491,64 +1331,6 @@ func TestMax(t *testing.T) {
 	}
 }
 
-func TestMaxCollate(t *testing.T) {
-	tcases := []struct {
-		v1, v2    string
-		collation collations.ID
-		out       string
-		err       error
-	}{
-		{
-			// accent insensitive
-			v1:        "ǍḄÇ",
-			v2:        "ÁḆĈ",
-			out:       "ǍḄÇ",
-			collation: getCollationID("utf8mb4_0900_as_ci"),
-		},
-		{
-			// kana sensitive
-			v1:        "\xE3\x81\xAB\xE3\x81\xBB\xE3\x82\x93\xE3\x81\x94",
-			v2:        "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4",
-			out:       "\xE3\x83\x8B\xE3\x83\x9B\xE3\x83\xB3\xE3\x82\xB4",
-			collation: getCollationID("utf8mb4_ja_0900_as_cs_ks"),
-		},
-		{
-			// non breaking space
-			v1:        "abc ",
-			v2:        "abc\u00a0",
-			out:       "abc\u00a0",
-			collation: getCollationID("utf8mb4_0900_as_cs"),
-		},
-		{
-			// "cs" counts as a separate letter, where c < cs < d
-			v1:        "c",
-			v2:        "cs",
-			out:       "cs",
-			collation: getCollationID("utf8mb4_hu_0900_ai_ci"),
-		},
-		{
-			// "cs" counts as a separate letter, where c < cs < d
-			v1:        "cukor",
-			v2:        "csak",
-			out:       "csak",
-			collation: getCollationID("utf8mb4_hu_0900_ai_ci"),
-		},
-	}
-	for _, tcase := range tcases {
-		got, err := Max(TestValue(querypb.Type_VARCHAR, tcase.v1), TestValue(querypb.Type_VARCHAR, tcase.v2), tcase.collation)
-		if !vterrors.Equals(err, tcase.err) {
-			t.Errorf("NullsafeCompare(%v, %v) error: %v, want %v", tcase.v1, tcase.v2, vterrors.Print(err), vterrors.Print(tcase.err))
-		}
-		if tcase.err != nil {
-			continue
-		}
-
-		if got.ToString() != tcase.out {
-			t.Errorf("NullsafeCompare(%v, %v): %v, want %v", tcase.v1, tcase.v2, got, tcase.out)
-		}
-	}
-}
-
 func TestHashCodes(t *testing.T) {
 	n1 := sqltypes.NULL
 	n2 := sqltypes.Value{}
@@ -1593,7 +1375,7 @@ func BenchmarkAddActual(b *testing.B) {
 	v1 := sqltypes.MakeTrusted(querypb.Type_INT64, []byte("1"))
 	v2 := sqltypes.MakeTrusted(querypb.Type_INT64, []byte("12"))
 	for i := 0; i < b.N; i++ {
-		v1, _ = NullSafeAdd(v1, v2, querypb.Type_INT64)
+		v1 = NullsafeAdd(v1, v2, querypb.Type_INT64)
 	}
 }
 
diff --git a/go/vt/vtgate/evalengine/comparisons_test.go b/go/vt/vtgate/evalengine/comparisons_test.go
deleted file mode 100644
index a576515e73..0000000000
--- a/go/vt/vtgate/evalengine/comparisons_test.go
+++ /dev/null
@@ -1,896 +0,0 @@
-/*
-Copyright 2021 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 evalengine
-
-import (
-	"fmt"
-	"testing"
-	"time"
-
-	"github.com/stretchr/testify/require"
-
-	"vitess.io/vitess/go/mysql/collations"
-
-	"vitess.io/vitess/go/sqltypes"
-
-	querypb "vitess.io/vitess/go/vt/proto/query"
-)
-
-type testCase struct {
-	name   string
-	v1, v2 Expr
-	out    *bool
-	err    string
-	op     ComparisonOp
-	bv     map[string]*querypb.BindVariable
-	row    []sqltypes.Value
-}
-
-var (
-	T = true
-	F = false
-
-	defaultCollation = collations.Default().LookupByName("utf8mb4_bin").ID()
-)
-
-func (tc testCase) run(t *testing.T) {
-	if tc.bv == nil {
-		tc.bv = map[string]*querypb.BindVariable{}
-	}
-	env := ExpressionEnv{
-		BindVars: tc.bv,
-		Row:      tc.row,
-	}
-	cmp := ComparisonExpr{
-		Op:    tc.op,
-		Left:  tc.v1,
-		Right: tc.v2,
-	}
-	got, err := cmp.Evaluate(env)
-	if tc.err == "" {
-		require.NoError(t, err)
-		if tc.out != nil && *tc.out {
-			require.EqualValues(t, 1, got.ival)
-		} else if tc.out != nil && !*tc.out {
-			require.EqualValues(t, 0, got.ival)
-		} else {
-			require.EqualValues(t, 0, got.ival)
-			require.EqualValues(t, sqltypes.Null, got.typ)
-		}
-	} else {
-		require.EqualError(t, err, tc.err)
-	}
-}
-
-// This test tests the comparison of two integers
-func TestCompareIntegers(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "integers are equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(0, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewInt64(18)},
-		},
-		{
-			name: "integers are equal (2)",
-			v1:   NewLiteralInt(56), v2: NewLiteralInt(56),
-			out: &T, op: &EqualOp{},
-		},
-		{
-			name: "integers are not equal (1)",
-			v1:   NewLiteralInt(56), v2: NewLiteralInt(10),
-			out: &F, op: &EqualOp{},
-		},
-		{
-			name: "integers are not equal (2)",
-			v1:   NewLiteralInt(56), v2: NewLiteralInt(10),
-			out: &T, op: &NotEqualOp{},
-		},
-		{
-			name: "integers are not equal (3)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewInt64(18), sqltypes.NewInt64(98)},
-		},
-		{
-			name: "unsigned integers are equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(0, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewUint64(18)},
-		},
-		{
-			name: "unsigned integer and integer are equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewUint64(18), sqltypes.NewInt64(18)},
-		},
-		{
-			name: "unsigned integer and integer are not equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewUint64(18), sqltypes.NewInt64(42)},
-		},
-		{
-			name: "integer is less than integer",
-			v1:   NewLiteralInt(3549), v2: NewLiteralInt(8072),
-			out: &T, op: &LessThanOp{},
-		},
-		{
-			name: "integer is not less than integer",
-			v1:   NewLiteralInt(3549), v2: NewLiteralInt(21),
-			out: &F, op: &LessThanOp{},
-		},
-		{
-			name: "integer is less-equal to integer (1)",
-			v1:   NewLiteralInt(3549), v2: NewLiteralInt(9863),
-			out: &T, op: &LessEqualOp{},
-		},
-		{
-			name: "integer is less-equal to integer (2)",
-			v1:   NewLiteralInt(3549), v2: NewLiteralInt(3549),
-			out: &T, op: &LessEqualOp{},
-		},
-		{
-			name: "integer is greater than integer",
-			v1:   NewLiteralInt(9809), v2: NewLiteralInt(9800),
-			out: &T, op: &GreaterThanOp{},
-		},
-		{
-			name: "integer is not greater than integer",
-			v1:   NewLiteralInt(549), v2: NewLiteralInt(21579),
-			out: &F, op: &GreaterThanOp{},
-		},
-		{
-			name: "integer is greater-equal to integer (1)",
-			v1:   NewLiteralInt(987), v2: NewLiteralInt(15),
-			out: &T, op: &GreaterEqualOp{},
-		},
-		{
-			name: "integer is greater-equal to integer (2)",
-			v1:   NewLiteralInt(3549), v2: NewLiteralInt(3549),
-			out: &T, op: &GreaterEqualOp{},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
-
-// This test tests the comparison of two floats
-func TestCompareFloats(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "floats are equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(0, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewFloat64(18)},
-		},
-		{
-			name: "floats are equal (2)",
-			v1:   NewLiteralFloat(3549.9), v2: NewLiteralFloat(3549.9),
-			out: &T, op: &EqualOp{},
-		},
-		{
-			name: "floats are not equal (1)",
-			v1:   NewLiteralFloat(7858.016), v2: NewLiteralFloat(8943298.56),
-			out: &F, op: &EqualOp{},
-		},
-		{
-			name: "floats are not equal (2)",
-			v1:   NewLiteralFloat(351049.65), v2: NewLiteralFloat(62508.99),
-			out: &T, op: &NotEqualOp{},
-		},
-		{
-			name: "floats are not equal (3)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewFloat64(16516.84), sqltypes.NewFloat64(219541.01)},
-		},
-		{
-			name: "float is less than float",
-			v1:   NewLiteralFloat(3549.9), v2: NewLiteralFloat(8072),
-			out: &T, op: &LessThanOp{},
-		},
-		{
-			name: "float is not less than float",
-			v1:   NewLiteralFloat(3549.9), v2: NewLiteralFloat(21.564),
-			out: &F, op: &LessThanOp{},
-		},
-		{
-			name: "float is less-equal to float (1)",
-			v1:   NewLiteralFloat(3549.9), v2: NewLiteralFloat(9863),
-			out: &T, op: &LessEqualOp{},
-		},
-		{
-			name: "float is less-equal to float (2)",
-			v1:   NewLiteralFloat(3549.9), v2: NewLiteralFloat(3549.9),
-			out: &T, op: &LessEqualOp{},
-		},
-		{
-			name: "float is greater than float",
-			v1:   NewLiteralFloat(9808.549), v2: NewLiteralFloat(9808.540),
-			out: &T, op: &GreaterThanOp{},
-		},
-		{
-			name: "float is not greater than float",
-			v1:   NewLiteralFloat(549.02), v2: NewLiteralFloat(21579.64),
-			out: &F, op: &GreaterThanOp{},
-		},
-		{
-			name: "float is greater-equal to float (1)",
-			v1:   NewLiteralFloat(987.30), v2: NewLiteralFloat(15.5),
-			out: &T, op: &GreaterEqualOp{},
-		},
-		{
-			name: "float is greater-equal to float (2)",
-			v1:   NewLiteralFloat(3549.9), v2: NewLiteralFloat(3549.9),
-			out: &T, op: &GreaterEqualOp{},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
-
-// This test tests the comparison of two decimals
-func TestCompareDecimals(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "decimals are equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(0, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("12.9019")},
-		},
-		{
-			name: "decimals are not equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("12.9019"), sqltypes.NewDecimal("489.156849")},
-		},
-		{
-			name: "decimal is greater than decimal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("192.129"), sqltypes.NewDecimal("192.128")},
-		},
-		{
-			name: "decimal is not greater than decimal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("192.128"), sqltypes.NewDecimal("192.129")},
-		},
-		{
-			name: "decimal is less than decimal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("192.128"), sqltypes.NewDecimal("192.129")},
-		},
-		{
-			name: "decimal is not less than decimal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("192.129"), sqltypes.NewDecimal("192.128")},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
-
-// This test tests the comparison of numerical values (float, decimal, integer)
-func TestCompareNumerics(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "decimal and float are equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewFloat64(189.6), sqltypes.NewDecimal("189.6")},
-		},
-		{
-			name: "decimal and float with negative values are equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewFloat64(-98.1839), sqltypes.NewDecimal("-98.1839")},
-		},
-		{
-			name: "decimal and float with negative values are not equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewFloat64(-98.9381), sqltypes.NewDecimal("-98.1839")},
-		},
-		{
-			name: "decimal and float with negative values are not equal (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewFloat64(-98.9381), sqltypes.NewDecimal("-98.1839")},
-		},
-		{
-			name: "decimal and integer are equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewInt64(8979), sqltypes.NewDecimal("8979")},
-		},
-		{
-			name: "decimal and integer are equal (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("8979.0000"), sqltypes.NewInt64(8979)},
-		},
-		{
-			name: "decimal and unsigned integer are equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewUint64(901), sqltypes.NewDecimal("901")},
-		},
-		{
-			name: "decimal and unsigned integer are equal (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("901.00"), sqltypes.NewUint64(901)},
-		},
-		{
-			name: "decimal and unsigned integer are not equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("192.129"), sqltypes.NewUint64(192)},
-		},
-		{
-			name: "decimal and unsigned integer are not equal (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("192.129"), sqltypes.NewUint64(192)},
-		},
-		{
-			name: "decimal is greater than integer",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("1.01"), sqltypes.NewInt64(1)},
-		},
-		{
-			name: "decimal is greater-equal to integer",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("1.00"), sqltypes.NewInt64(1)},
-		},
-		{
-			name: "decimal is less than integer",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal(".99"), sqltypes.NewInt64(1)},
-		},
-		{
-			name: "decimal is less-equal to integer",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("1.00"), sqltypes.NewInt64(1)},
-		},
-		{
-			name: "decimal is greater than float",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("849.896"), sqltypes.NewFloat64(86.568)},
-		},
-		{
-			name: "decimal is not greater than float",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("15.23"), sqltypes.NewFloat64(8689.5)},
-		},
-		{
-			name: "decimal is greater-equal to float (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("65"), sqltypes.NewFloat64(65)},
-		},
-		{
-			name: "decimal is greater-equal to float (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("65"), sqltypes.NewFloat64(60)},
-		},
-		{
-			name: "decimal is less than float",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("0.998"), sqltypes.NewFloat64(0.999)},
-		},
-		{
-			name: "decimal is less-equal to float",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDecimal("1.000101"), sqltypes.NewFloat64(1.00101)},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
-
-// This test tests the comparison of two datetimes
-func TestCompareDatetime(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "datetimes are equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(0, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-22 12:00:00")},
-		},
-		{
-			name: "datetimes are not equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-22 12:00:00"), sqltypes.NewDatetime("2020-10-22 12:00:00")},
-		},
-		{
-			name: "datetimes are not equal (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-22 12:00:00"), sqltypes.NewDatetime("2021-10-22 10:23:56")},
-		},
-		{
-			name: "datetimes are not equal (3)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-01 00:00:00"), sqltypes.NewDatetime("2021-02-01 00:00:00")},
-		},
-		{
-			name: "datetime is greater than datetime",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-01 13:10:02")},
-		},
-		{
-			name: "datetime is not greater than datetime",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-01 13:10:02"), sqltypes.NewDatetime("2021-10-30 10:42:50")},
-		},
-		{
-			name: "datetime is less than datetime",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-01 13:10:02"), sqltypes.NewDatetime("2021-10-30 10:42:50")},
-		},
-		{
-			name: "datetime is not less than datetime",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-01 13:10:02")},
-		},
-		{
-			name: "datetime is greater-equal to datetime (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-30 10:42:50")},
-		},
-		{
-			name: "datetime is greater-equal to datetime (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-01 13:10:02")},
-		},
-		{
-			name: "datetime is less-equal to datetime (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-30 10:42:50"), sqltypes.NewDatetime("2021-10-30 10:42:50")},
-		},
-		{
-			name: "datetime is less-equal to datetime (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDatetime("2021-10-01 13:10:02"), sqltypes.NewDatetime("2021-10-30 10:42:50")},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
-
-// This test tests the comparison of two timestamps
-func TestCompareTimestamp(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "timestamps are equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(0, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-22 12:00:00")},
-		},
-		{
-			name: "timestamps are not equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-22 12:00:00"), sqltypes.NewTimestamp("2020-10-22 12:00:00")},
-		},
-		{
-			name: "timestamps are not equal (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-22 12:00:00"), sqltypes.NewTimestamp("2021-10-22 10:23:56")},
-		},
-		{
-			name: "timestamps are not equal (3)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-01 00:00:00"), sqltypes.NewTimestamp("2021-02-01 00:00:00")},
-		},
-		{
-			name: "timestamp is greater than timestamp",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-01 13:10:02")},
-		},
-		{
-			name: "timestamp is not greater than timestamp",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-01 13:10:02"), sqltypes.NewTimestamp("2021-10-30 10:42:50")},
-		},
-		{
-			name: "timestamp is less than timestamp",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-01 13:10:02"), sqltypes.NewTimestamp("2021-10-30 10:42:50")},
-		},
-		{
-			name: "timestamp is not less than timestamp",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-01 13:10:02")},
-		},
-		{
-			name: "timestamp is greater-equal to timestamp (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-30 10:42:50")},
-		},
-		{
-			name: "timestamp is greater-equal to timestamp (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-01 13:10:02")},
-		},
-		{
-			name: "timestamp is less-equal to timestamp (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-30 10:42:50"), sqltypes.NewTimestamp("2021-10-30 10:42:50")},
-		},
-		{
-			name: "timestamp is less-equal to timestamp (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTimestamp("2021-10-01 13:10:02"), sqltypes.NewTimestamp("2021-10-30 10:42:50")},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
-
-// This test tests the comparison of two dates
-func TestCompareDate(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "dates are equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(0, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-22")},
-		},
-		{
-			name: "dates are not equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewDate("2020-10-21")},
-		},
-		{
-			name: "dates are not equal (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-01"), sqltypes.NewDate("2021-02-01")},
-		},
-		{
-			name: "date is greater than date",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-01")},
-		},
-		{
-			name: "date is not greater than date",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-01"), sqltypes.NewDate("2021-10-30")},
-		},
-		{
-			name: "date is less than date",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-01"), sqltypes.NewDate("2021-10-30")},
-		},
-		{
-			name: "date is not less than date",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-01")},
-		},
-		{
-			name: "date is greater-equal to date (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-30")},
-		},
-		{
-			name: "date is greater-equal to date (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-01")},
-		},
-		{
-			name: "date is less-equal to date (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-30"), sqltypes.NewDate("2021-10-30")},
-		},
-		{
-			name: "date is less-equal to date (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-01"), sqltypes.NewDate("2021-10-30")},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
-
-// This test tests the comparison of two times
-func TestCompareTime(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "times are equal",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(0, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("12:00:00")},
-		},
-		{
-			name: "times are not equal (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("12:00:00"), sqltypes.NewTime("10:23:56")},
-		},
-		{
-			name: "times are not equal (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("00:00:00"), sqltypes.NewTime("10:15:00")},
-		},
-		{
-			name: "time is greater than time",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("18:14:35"), sqltypes.NewTime("13:01:38")},
-		},
-		{
-			name: "time is not greater than time",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &GreaterThanOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("02:46:02"), sqltypes.NewTime("10:42:50")},
-		},
-		{
-			name: "time is less than time",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("04:30:00"), sqltypes.NewTime("09:23:48")},
-		},
-		{
-			name: "time is not less than time",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &F, op: &LessThanOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("15:21:00"), sqltypes.NewTime("10:00:00")},
-		},
-		{
-			name: "time is greater-equal to time (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("10:42:50"), sqltypes.NewTime("10:42:50")},
-		},
-		{
-			name: "time is greater-equal to time (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &GreaterEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("19:42:50"), sqltypes.NewTime("13:10:02")},
-		},
-		{
-			name: "time is less-equal to time (1)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("10:42:50"), sqltypes.NewTime("10:42:50")},
-		},
-		{
-			name: "time is less-equal to time (2)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &LessEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewTime("10:10:02"), sqltypes.NewTime("10:42:50")},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
-
-// This test tests the comparison of two dates (datetime, date, timestamp, time)
-func TestCompareDates(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "date equal datetime",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewDatetime("2021-10-22 00:00:00")},
-		},
-		{
-			name: "date equal datetime through bind variables",
-			v1:   NewBindVar("k1", defaultCollation), v2: NewBindVar("k2", defaultCollation),
-			out: &T, op: &EqualOp{},
-			bv: map[string]*querypb.BindVariable{
-				"k1": {Type: sqltypes.Date, Value: []byte("2021-10-22")},
-				"k2": {Type: sqltypes.Datetime, Value: []byte("2021-10-22 00:00:00")},
-			},
-		},
-		{
-			name: "date not equal datetime through bind variables",
-			v1:   NewBindVar("k1", defaultCollation), v2: NewBindVar("k2", defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			bv: map[string]*querypb.BindVariable{
-				"k1": {Type: sqltypes.Date, Value: []byte("2021-02-20")},
-				"k2": {Type: sqltypes.Datetime, Value: []byte("2021-10-22 00:00:00")},
-			},
-		},
-		{
-			name: "date not equal datetime",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewDatetime("2021-10-20 00:06:00")},
-		},
-		{
-			name: "date equal timestamp",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewTimestamp("2021-10-22 00:00:00")},
-		},
-		{
-			name: "date not equal timestamp",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-10-22"), sqltypes.NewTimestamp("2021-10-22 16:00:00")},
-		},
-		{
-			name: "date equal time",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate(time.Now().Format("2006-01-02")), sqltypes.NewTime("00:00:00")},
-		},
-		{
-			name: "date not equal time",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate(time.Now().Format("2006-01-02")), sqltypes.NewTime("12:00:00")},
-		},
-		{
-			name: "string equal datetime",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewVarChar("2021-10-22"), sqltypes.NewDatetime("2021-10-22 00:00:00")},
-		},
-		{
-			name: "string equal timestamp",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewVarChar("2021-10-22 00:00:00"), sqltypes.NewTimestamp("2021-10-22 00:00:00")},
-		},
-		{
-			name: "string not equal timestamp",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewVarChar("2021-10-22 06:00:30"), sqltypes.NewTimestamp("2021-10-20 15:02:10")},
-		},
-		{
-			name: "string equal time",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewVarChar("00:05:12"), sqltypes.NewTime("00:05:12")},
-		},
-		{
-			name: "string equal date",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewVarChar("2021-02-22"), sqltypes.NewDate("2021-02-22")},
-		},
-		{
-			name: "string not equal date (1, date on the RHS)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewVarChar("2021-02-20"), sqltypes.NewDate("2021-03-30")},
-		},
-		{
-			name: "string not equal date (2, date on the LHS)",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &NotEqualOp{},
-			row: []sqltypes.Value{sqltypes.NewDate("2021-03-30"), sqltypes.NewVarChar("2021-02-20")},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
-
-// This test tests the comparison of strings
-func TestCompareStrings(t *testing.T) {
-	tests := []testCase{
-		{
-			name: "string equal string",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewVarChar("toto"), sqltypes.NewVarChar("toto")},
-		},
-		{
-			name: "string equal number",
-			v1:   NewColumn(0, defaultCollation), v2: NewColumn(1, defaultCollation),
-			out: &T, op: &EqualOp{},
-			row: []sqltypes.Value{sqltypes.NewVarChar("1"), sqltypes.NewInt64(1)},
-		},
-		{
-			name: "string equal string unknown collation",
-			v1:   NewColumn(0, collations.Unknown), v2: NewColumn(1, collations.Unknown),
-			op:  &EqualOp{},
-			err: "cannot compare strings with an unknown collation",
-			row: []sqltypes.Value{sqltypes.NewVarChar("1"), sqltypes.NewVarChar("1")},
-		},
-	}
-
-	for i, tcase := range tests {
-		t.Run(fmt.Sprintf("%d %s", i, tcase.name), func(t *testing.T) {
-			tcase.run(t)
-		})
-	}
-}
diff --git a/go/vt/vtgate/evalengine/binary_test.go b/go/vt/vtgate/evalengine/expressions_test.go
similarity index 99%
rename from go/vt/vtgate/evalengine/binary_test.go
rename to go/vt/vtgate/evalengine/expressions_test.go
index 514aa43882..0cfda38dde 100644
--- a/go/vt/vtgate/evalengine/binary_test.go
+++ b/go/vt/vtgate/evalengine/expressions_test.go
@@ -35,7 +35,7 @@ func TestBinaryOpTypes(t *testing.T) {
 		l, r, e querypb.Type
 	}
 	type ops struct {
-		op        BinaryOp
+		op        BinaryExpr
 		testcases []testcase
 	}
 
diff --git a/go/vt/vtgate/planbuilder/expression_converter_test.go b/go/vt/vtgate/planbuilder/expression_converter_test.go
index 86ad0061c2..2ff05d188f 100644
--- a/go/vt/vtgate/planbuilder/expression_converter_test.go
+++ b/go/vt/vtgate/planbuilder/expression_converter_test.go
@@ -40,7 +40,7 @@ func TestConversion(t *testing.T) {
 		expressionsOut: e(evalengine.NewLiteralInt(1)),
 	}, {
 		expressionsIn:  "@@foo",
-		expressionsOut: e(evalengine.NewColumn(0, 0)),
+		expressionsOut: e(evalengine.NewColumn(0)),
 	}}
 
 	for _, tc := range queries {
diff --git a/go/vt/vtgate/planbuilder/rewrite_test.go b/go/vt/vtgate/planbuilder/rewrite_test.go
index d4815deebb..f774fa3d56 100644
--- a/go/vt/vtgate/planbuilder/rewrite_test.go
+++ b/go/vt/vtgate/planbuilder/rewrite_test.go
@@ -120,10 +120,7 @@ func TestHavingRewrite(t *testing.T) {
 		output: "select 1 from t1 where (x = 1 or y = 2) and a = 1 having count(*) = 1",
 	}, {
 		input:  "select count(*) k from t1 where x = 1 or y = 2 having a = 1 and k = 1",
-		output: "select count(*) as k from t1 where (x = 1 or y = 2) and a = 1 having k = 1",
-	}, {
-		input:  "select count(*) k from t1 having k = 10",
-		output: "select count(*) as k from t1 having k = 10",
+		output: "select count(*) as k from t1 where (x = 1 or y = 2) and a = 1 having count(*) = 1",
 	}, {
 		input:  "select 1 from t1 where x in (select 1 from t2 having a = 1)",
 		output: "select 1 from t1 where :__sq_has_values1 = 1 and x in ::__sq1",
diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
index 777d769801..e7e8bc2a7a 100644
--- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
@@ -808,7 +808,7 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
       "Sharded": true
     },
     "FieldQuery": "select id, count(*) as c from `user` where 1 != 1 group by id",
-    "Query": "select id, count(*) as c from `user` where id = 1 group by id having c = 10",
+    "Query": "select id, count(*) as c from `user` where id = 1 group by id having count(*) = 10",
     "Table": "`user`",
     "Values": [
       1
@@ -2825,263 +2825,3 @@ Gen4 plan same as above
   }
 }
 
-# Column and Literal equality filter on scatter aggregates
-"select count(*) a from user having a = 10"
-"unsupported: filtering on results of aggregates"
-{
-  "QueryType": "SELECT",
-  "Original": "select count(*) a from user having a = 10",
-  "Instructions": {
-    "OperatorType": "Filter",
-    "Predicate": "a = 10",
-    "Inputs": [
-      {
-        "OperatorType": "Aggregate",
-        "Variant": "Ordered",
-        "Aggregates": "count(0) AS a",
-        "Inputs": [
-          {
-            "OperatorType": "Route",
-            "Variant": "SelectScatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select count(*) as a from `user` where 1 != 1",
-            "Query": "select count(*) as a from `user`",
-            "Table": "`user`"
-          }
-        ]
-      }
-    ]
-  }
-}
-
-# Equality filtering with column and string literal on scatter aggregates
-"select count(*) a from user having a = '1'"
-"unsupported: filtering on results of aggregates"
-{
-  "QueryType": "SELECT",
-  "Original": "select count(*) a from user having a = '1'",
-  "Instructions": {
-    "OperatorType": "Filter",
-    "Predicate": "a = '1'",
-    "Inputs": [
-      {
-        "OperatorType": "Aggregate",
-        "Variant": "Ordered",
-        "Aggregates": "count(0) AS a",
-        "Inputs": [
-          {
-            "OperatorType": "Route",
-            "Variant": "SelectScatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select count(*) as a from `user` where 1 != 1",
-            "Query": "select count(*) as a from `user`",
-            "Table": "`user`"
-          }
-        ]
-      }
-    ]
-  }
-}
-
-# Column and Literal not equal filter on scatter aggregates
-"select count(*) a from user having a != 10"
-"unsupported: filtering on results of aggregates"
-{
-  "QueryType": "SELECT",
-  "Original": "select count(*) a from user having a != 10",
-  "Instructions": {
-    "OperatorType": "Filter",
-    "Predicate": "a != 10",
-    "Inputs": [
-      {
-        "OperatorType": "Aggregate",
-        "Variant": "Ordered",
-        "Aggregates": "count(0) AS a",
-        "Inputs": [
-          {
-            "OperatorType": "Route",
-            "Variant": "SelectScatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select count(*) as a from `user` where 1 != 1",
-            "Query": "select count(*) as a from `user`",
-            "Table": "`user`"
-          }
-        ]
-      }
-    ]
-  }
-}
-
-# Not equal filter with column and string literal on scatter aggregates
-"select count(*) a from user having a != '1'"
-"unsupported: filtering on results of aggregates"
-{
-  "QueryType": "SELECT",
-  "Original": "select count(*) a from user having a != '1'",
-  "Instructions": {
-    "OperatorType": "Filter",
-    "Predicate": "a != '1'",
-    "Inputs": [
-      {
-        "OperatorType": "Aggregate",
-        "Variant": "Ordered",
-        "Aggregates": "count(0) AS a",
-        "Inputs": [
-          {
-            "OperatorType": "Route",
-            "Variant": "SelectScatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select count(*) as a from `user` where 1 != 1",
-            "Query": "select count(*) as a from `user`",
-            "Table": "`user`"
-          }
-        ]
-      }
-    ]
-  }
-}
-
-# Greater than filter on scatter aggregates
-"select count(*) a from user having a > 10"
-"unsupported: filtering on results of aggregates"
-{
-  "QueryType": "SELECT",
-  "Original": "select count(*) a from user having a \u003e 10",
-  "Instructions": {
-    "OperatorType": "Filter",
-    "Predicate": "a \u003e 10",
-    "Inputs": [
-      {
-        "OperatorType": "Aggregate",
-        "Variant": "Ordered",
-        "Aggregates": "count(0) AS a",
-        "Inputs": [
-          {
-            "OperatorType": "Route",
-            "Variant": "SelectScatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select count(*) as a from `user` where 1 != 1",
-            "Query": "select count(*) as a from `user`",
-            "Table": "`user`"
-          }
-        ]
-      }
-    ]
-  }
-}
-
-# Greater Equal filter on scatter aggregates
-"select count(*) a from user having a >= 10"
-"unsupported: filtering on results of aggregates"
-{
-  "QueryType": "SELECT",
-  "Original": "select count(*) a from user having a \u003e= 10",
-  "Instructions": {
-    "OperatorType": "Filter",
-    "Predicate": "a \u003e= 10",
-    "Inputs": [
-      {
-        "OperatorType": "Aggregate",
-        "Variant": "Ordered",
-        "Aggregates": "count(0) AS a",
-        "Inputs": [
-          {
-            "OperatorType": "Route",
-            "Variant": "SelectScatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select count(*) as a from `user` where 1 != 1",
-            "Query": "select count(*) as a from `user`",
-            "Table": "`user`"
-          }
-        ]
-      }
-    ]
-  }
-}
-
-# Less than filter on scatter aggregates
-"select count(*) a from user having a < 10"
-"unsupported: filtering on results of aggregates"
-{
-  "QueryType": "SELECT",
-  "Original": "select count(*) a from user having a \u003c 10",
-  "Instructions": {
-    "OperatorType": "Filter",
-    "Predicate": "a \u003c 10",
-    "Inputs": [
-      {
-        "OperatorType": "Aggregate",
-        "Variant": "Ordered",
-        "Aggregates": "count(0) AS a",
-        "Inputs": [
-          {
-            "OperatorType": "Route",
-            "Variant": "SelectScatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select count(*) as a from `user` where 1 != 1",
-            "Query": "select count(*) as a from `user`",
-            "Table": "`user`"
-          }
-        ]
-      }
-    ]
-  }
-}
-
-# Less Equal filter on scatter aggregates
-"select count(*) a from user having a <= 10"
-"unsupported: filtering on results of aggregates"
-{
-  "QueryType": "SELECT",
-  "Original": "select count(*) a from user having a \u003c= 10",
-  "Instructions": {
-    "OperatorType": "Filter",
-    "Predicate": "a \u003c= 10",
-    "Inputs": [
-      {
-        "OperatorType": "Aggregate",
-        "Variant": "Ordered",
-        "Aggregates": "count(0) AS a",
-        "Inputs": [
-          {
-            "OperatorType": "Route",
-            "Variant": "SelectScatter",
-            "Keyspace": {
-              "Name": "user",
-              "Sharded": true
-            },
-            "FieldQuery": "select count(*) as a from `user` where 1 != 1",
-            "Query": "select count(*) as a from `user`",
-            "Table": "`user`"
-          }
-        ]
-      }
-    ]
-  }
-}
-
-# Equal filter with hexadecimal value
-"select count(*) a from user having a = 0x01"
-"unsupported: filtering on results of aggregates"
-Gen4 error: expr cannot be converted, not supported: hexadecimal value: 0x01
diff --git a/go/vt/vtgate/planbuilder/testdata/lock_cases.txt b/go/vt/vtgate/planbuilder/testdata/lock_cases.txt
index e4591cd9a2..c2629626d6 100644
--- a/go/vt/vtgate/planbuilder/testdata/lock_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/lock_cases.txt
@@ -10,7 +10,6 @@
       "Sharded": false
     },
     "TargetDestination": "KeyspaceID(00)",
-    "FieldQuery": "select get_lock('xyz', 10) from dual where 1 != 1",
     "Query": "select get_lock('xyz', 10) from dual"
   }
 }
@@ -28,30 +27,11 @@ Gen4 plan same as above
       "Sharded": false
     },
     "TargetDestination": "KeyspaceID(00)",
-    "FieldQuery": "select is_free_lock('xyz') from dual where 1 != 1",
     "Query": "select is_free_lock('xyz') from dual"
   }
 }
 Gen4 plan same as above
 
-# get_lock from dual prepare query
-"select get_lock(?, ?)"
-{
-  "QueryType": "SELECT",
-  "Original": "select get_lock(?, ?)",
-  "Instructions": {
-    "OperatorType": "Lock",
-    "Keyspace": {
-      "Name": "main",
-      "Sharded": false
-    },
-    "TargetDestination": "KeyspaceID(00)",
-    "FieldQuery": "select get_lock(:v1, :v2) from dual where 1 != 1",
-    "Query": "select get_lock(:v1, :v2) from dual"
-  }
-}
-Gen4 plan same as above
-
 # lock tables read
 "lock tables t as x read local"
 {
diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
index 55aa77d7ed..372a17ddf4 100644
--- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
@@ -2581,51 +2581,6 @@ Gen4 plan same as above
   }
 }
 
-# correlated subquery in exists clause with an order by
-"select col from user where exists(select user_id from user_extra where user_id = 3 and user_id < user.id) order by col"
-"unsupported: cross-shard correlated subquery"
-{
-  "QueryType": "SELECT",
-  "Original": "select col from user where exists(select user_id from user_extra where user_id = 3 and user_id \u003c user.id) order by col",
-  "Instructions": {
-    "OperatorType": "SemiJoin",
-    "JoinVars": {
-      "user_id": 0
-    },
-    "ProjectedIndexes": "-2",
-    "TableName": "`user`_user_extra",
-    "Inputs": [
-      {
-        "OperatorType": "Route",
-        "Variant": "SelectScatter",
-        "Keyspace": {
-          "Name": "user",
-          "Sharded": true
-        },
-        "FieldQuery": "select `user`.id, col, weight_string(col) from `user` where 1 != 1",
-        "OrderBy": "(1|2) ASC",
-        "Query": "select `user`.id, col, weight_string(col) from `user` order by col asc",
-        "Table": "`user`"
-      },
-      {
-        "OperatorType": "Route",
-        "Variant": "SelectEqualUnique",
-        "Keyspace": {
-          "Name": "user",
-          "Sharded": true
-        },
-        "FieldQuery": "select 1 from user_extra where 1 != 1",
-        "Query": "select 1 from user_extra where user_id = 3 and user_id \u003c :user_id",
-        "Table": "user_extra",
-        "Values": [
-          3
-        ],
-        "Vindex": "user_index"
-      }
-    ]
-  }
-}
-
 # correlated subquery having dependencies on two tables
 "select 1 from user u1, user u2 where exists (select 1 from user_extra ue where ue.col = u1.col and ue.col = u2.col)"
 "unsupported: cross-shard correlated subquery"
diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
index d67579258e..d787040745 100644
--- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
@@ -83,6 +83,11 @@ Gen4 plan same as above
 "unsupported: '*' expression in cross-shard query"
 Gen4 error: cannot use column offsets in group statement when using `*`
 
+# Filtering on scatter aggregates
+"select count(*) a from user having a >10"
+"unsupported: filtering on results of aggregates"
+Gen4 plan same as above
+
 # group by must reference select list
 "select a from user group by b"
 "unsupported: in scatter query: group by column must reference column in SELECT list"
@@ -500,11 +505,11 @@ 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"
-Gen4 error: expr cannot be converted, not supported: *sqlparser.FuncExpr
+Gen4 error: unsupported: filtering on results of aggregates
 
 "select (select 1 from user u having count(ue.col) > 10) from user_extra ue"
 "symbol ue.col not found in subquery"
-Gen4 error: expr cannot be converted, not supported: *sqlparser.FuncExpr
+Gen4 error: unsupported: filtering on results of aggregates
 
 # aggregation filtering by having on a route with no group by
 "select 1 from user having count(id) = 10"
@@ -523,7 +528,7 @@ Gen4 error: expr cannot be converted, not supported: *sqlparser.FuncExpr
     "Table": "`user`"
   }
 }
-Gen4 error: expr cannot be converted, not supported: *sqlparser.FuncExpr
+Gen4 error: unsupported: filtering on results of 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'"
@@ -546,7 +551,7 @@ Gen4 error: expr cannot be converted, not supported: *sqlparser.FuncExpr
     "Vindex": "name_user_map"
   }
 }
-Gen4 error: expr cannot be converted, not supported: *sqlparser.FuncExpr
+Gen4 error: unsupported: filtering on results of 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)"
diff --git a/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.test.tsx b/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.test.tsx
deleted file mode 100644
index b466994cb2..0000000000
--- a/web/vtadmin/src/components/routes/keyspace/KeyspaceShards.test.tsx
+++ /dev/null
@@ -1,139 +0,0 @@
-/**
- * Copyright 2021 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.
- */
-import { topodata, vtadmin as pb } from '../../../proto/vtadmin';
-import { formatRows } from './KeyspaceShards';
-
-describe('KeyspaceShards', () => {
-    describe('formatRows', () => {
-        const tests: {
-            name: string;
-            params: Parameters<typeof formatRows>;
-            expected: ReturnType<typeof formatRows>;
-        }[] = [
-            {
-                name: 'should handle when keyspace is null',
-                params: [null, [], null],
-                expected: [],
-            },
-            {
-                name: 'should filter across shards and tablets',
-                params: [
-                    pb.Keyspace.create({
-                        cluster: {
-                            id: 'cluster1',
-                        },
-                        keyspace: {
-                            name: 'keyspace1',
-                        },
-                        shards: {
-                            '-80': {
-                                name: '-80',
-                                shard: {
-                                    is_primary_serving: false,
-                                },
-                            },
-                            '80-': {
-                                name: '80-',
-                                shard: {
-                                    is_primary_serving: true,
-                                },
-                            },
-                        },
-                    }),
-                    [
-                        pb.Tablet.create({
-                            cluster: { id: 'cluster1' },
-                            state: pb.Tablet.ServingState.SERVING,
-                            tablet: {
-                                alias: {
-                                    cell: 'zone1',
-                                    uid: 100,
-                                },
-                                keyspace: 'keyspace1',
-                                shard: '-80',
-                                type: topodata.TabletType.PRIMARY,
-                            },
-                        }),
-                        pb.Tablet.create({
-                            cluster: { id: 'cluster1' },
-                            state: pb.Tablet.ServingState.SERVING,
-                            tablet: {
-                                alias: {
-                                    cell: 'zone1',
-                                    uid: 200,
-                                },
-                                keyspace: 'keyspace2',
-                                shard: '-',
-                                type: topodata.TabletType.PRIMARY,
-                            },
-                        }),
-                        pb.Tablet.create({
-                            cluster: { id: 'cluster1' },
-                            state: pb.Tablet.ServingState.NOT_SERVING,
-                            tablet: {
-                                alias: {
-                                    cell: 'zone1',
-                                    uid: 300,
-                                },
-                                hostname: 'tablet-zone1-300-80-00',
-                                keyspace: 'keyspace1',
-                                shard: '80-',
-                                type: topodata.TabletType.PRIMARY,
-                            },
-                        }),
-                    ],
-                    'not',
-                ],
-                expected: [
-                    {
-                        // -80 matches the filter string "not" because shardState is "NOT_SERVING'",
-                        // however all of its tablets are serving and therefore none of them match.
-                        isShardServing: false,
-                        shard: '-80',
-                        shardState: 'NOT_SERVING',
-                        tablets: [],
-                    },
-                    {
-                        // 80- (the shard itself) does not match the filter string, since
-                        // its shardState is "SERVING'. However, its non-serving tablet
-                        // does match the filter string, so we include it.
-                        isShardServing: true,
-                        shard: '80-',
-                        shardState: 'SERVING',
-                        tablets: [
-                            {
-                                alias: 'zone1-300',
-                                hostname: 'tablet-zone1-300-80-00',
-                                shard: '80-',
-                                tabletState: 'NOT_SERVING',
-                                tabletType: 'PRIMARY',
-                                _tabletStateEnum: pb.Tablet.ServingState.NOT_SERVING,
-                            },
-                        ],
-                    },
-                ],
-            },
-        ];
-
-        test.each(tests.map(Object.values))(
-            '%s',
-            (name: string, params: Parameters<typeof formatRows>, expected: ReturnType<typeof formatRows>) => {
-                const result = formatRows(...params);
-                expect(result).toMatchObject(expected);
-            }
-        );
-    });
-});
diff --git a/web/vtadmin/src/util/keyspaces.test.ts b/web/vtadmin/src/util/keyspaces.test.ts
index 1511c03814..25578618ea 100644
--- a/web/vtadmin/src/util/keyspaces.test.ts
+++ b/web/vtadmin/src/util/keyspaces.test.ts
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-import { getShardsByState, ShardsByState, ShardState } from './keyspaces';
+import { getShardsByState, getShardSortRange, ShardRange, ShardsByState, ShardState } from './keyspaces';
 import { vtadmin as pb } from '../proto/vtadmin';
 
 describe('getShardsByState', () => {
@@ -102,3 +102,52 @@ describe('getShardsByState', () => {
         expect(result).toEqual(expected);
     });
 });
+
+describe('getShardSortRange', () => {
+    const tests: {
+        shardName: string;
+        expected: ShardRange;
+    }[] = [
+        {
+            shardName: '0',
+            expected: { start: Number.MIN_VALUE, end: Number.MAX_VALUE },
+        },
+        {
+            shardName: '-',
+            expected: { start: Number.MIN_VALUE, end: Number.MAX_VALUE },
+        },
+        {
+            shardName: '-40',
+            expected: { start: Number.MIN_VALUE, end: 64 },
+        },
+        {
+            shardName: '40-80',
+            expected: { start: 64, end: 128 },
+        },
+        {
+            shardName: '80-c0',
+            expected: { start: 128, end: 192 },
+        },
+        {
+            shardName: 'c0-',
+            expected: { start: 192, end: Number.MAX_VALUE },
+        },
+        {
+            shardName: 'c0-',
+            expected: { start: 192, end: Number.MAX_VALUE },
+        },
+    ];
+
+    test.each(tests.map(Object.values))('%s', (shardName: string, expected: ShardRange) => {
+        const result = getShardSortRange(shardName);
+        expect(result).toEqual(expected);
+    });
+
+    it('handles invalid shard names', () => {
+        ['nope', '--', '', '40--'].forEach((s) => {
+            expect(() => {
+                getShardSortRange(s);
+            }).toThrow(`could not parse sortable range from shard ${s}`);
+        });
+    });
+});