Repository: vitessio/vitess
Pull request #9326 (2021-12-06 14:07:13)

Multi column vindex support in select statement
<!--
  Thank you for your contribution to the Vitess project.
  How to contribute: https://vitess.io/docs/contributing/
  Please first make sure there is an open Issue to discuss the feature/fix suggested in this PR.
  If this is a new feature, please mark the Issue as "RFC".
 -->

<!-- if this PR is Work in Progress please create it as a Draft Pull Request -->

## Description
<!-- A few sentences describing the overall goals of the pull request's commits. -->
<!-- If this is a bug fix and you think the fix should be backported, please write so. -->

This PR adds support for multi-column vindex selection in select sql queries.
This only works with Gen4 planner.

## Related Issue(s)
<!-- List related issues and pull requests. If this PR fixes an issue, please add it using Fixes #????  -->

- #3481

## Checklist
- [x] Tests were added or are not required
- [x] Documentation was added or is not required

## Deployment Notes
<!-- Notes regarding deployment of the contained body of work. These should note any db migrations, etc. -->


Patch:
diff --git a/go/vt/srvtopo/resolver.go b/go/vt/srvtopo/resolver.go
index aec38dd9e9..8ee15b0d91 100644
--- a/go/vt/srvtopo/resolver.go
+++ b/go/vt/srvtopo/resolver.go
@@ -19,6 +19,8 @@ package srvtopo
 import (
 	"sort"
 
+	"vitess.io/vitess/go/sqltypes"
+
 	"context"
 
 	"google.golang.org/protobuf/proto"
@@ -167,6 +169,83 @@ func (r *Resolver) GetAllKeyspaces(ctx context.Context) ([]string, error) {
 	return keyspaces, nil
 }
 
+// ResolveDestinationsMultiCol resolves values and their destinations into their
+// respective shards for multi col vindex.
+//
+// If ids is nil, the returned [][][]sqltypes.Value is also nil.
+// Otherwise, len(ids) has to match len(destinations), and then the returned
+// [][][]sqltypes.Value is populated with all the values that go in each shard,
+// and len([]*ResolvedShard) matches len([][][]sqltypes.Value).
+//
+// Sample input / output:
+// - destinations: dst1, 			dst2, 		dst3
+// - ids:          [id1a,id1b],  [id2a,id2b],  [id3a,id3b]
+// If dst1 is in shard1, and dst2 and dst3 are in shard2, the output will be:
+// - []*ResolvedShard:   shard1, 			shard2
+// - [][][]sqltypes.Value: [[id1a,id1b]],  [[id2a,id2b], [id3a,id3b]]
+func (r *Resolver) ResolveDestinationsMultiCol(ctx context.Context, keyspace string, tabletType topodatapb.TabletType, ids [][]sqltypes.Value, destinations []key.Destination) ([]*ResolvedShard, [][][]sqltypes.Value, error) {
+	keyspace, _, allShards, err := r.GetKeyspaceShards(ctx, keyspace, tabletType)
+	if err != nil {
+		return nil, nil, err
+	}
+	accumulator := &resultAcc{
+		resolved:   make(map[string]int),
+		resolver:   r,
+		ids:        ids,
+		keyspace:   keyspace,
+		tabletType: tabletType,
+	}
+
+	for i, destination := range destinations {
+		if err := destination.Resolve(allShards, accumulator.resolveShard(i)); err != nil {
+			return nil, nil, err
+		}
+	}
+	return accumulator.shards, accumulator.values, nil
+}
+
+type resultAcc struct {
+	shards     []*ResolvedShard
+	values     [][][]sqltypes.Value
+	resolved   map[string]int
+	resolver   *Resolver
+	ids        [][]sqltypes.Value
+	keyspace   string
+	tabletType topodatapb.TabletType
+}
+
+// resolveShard is called once per shard that is resolved. It will keep track of which shards that are
+// the destinations resolved, and the values that are bound to each shard
+func (acc *resultAcc) resolveShard(idx int) func(shard string) error {
+	return func(shard string) error {
+		offsetInValues, ok := acc.resolved[shard]
+		if !ok {
+			target := &querypb.Target{
+				Keyspace:   acc.keyspace,
+				Shard:      shard,
+				TabletType: acc.tabletType,
+			}
+			// Right now we always set the Cell to ""
+			// Later we can fallback to another cell if needed.
+			// We would then need to read the SrvKeyspace there too.
+			target.Cell = ""
+			offsetInValues = len(acc.shards)
+			acc.shards = append(acc.shards, &ResolvedShard{
+				Target:  target,
+				Gateway: acc.resolver.gateway,
+			})
+			if acc.ids != nil {
+				acc.values = append(acc.values, nil)
+			}
+			acc.resolved[shard] = offsetInValues
+		}
+		if acc.ids != nil {
+			acc.values[offsetInValues] = append(acc.values[offsetInValues], acc.ids[idx])
+		}
+		return nil
+	}
+}
+
 // ResolveDestinations resolves values and their destinations into their
 // respective shards.
 //
diff --git a/go/vt/vtgate/engine/cached_size.go b/go/vt/vtgate/engine/cached_size.go
index 5d9f440d23..a2358f5d70 100644
--- a/go/vt/vtgate/engine/cached_size.go
+++ b/go/vt/vtgate/engine/cached_size.go
@@ -662,13 +662,18 @@ func (cached *Route) CachedSize(alloc bool) int64 {
 	size += hack.RuntimeAllocSize(int64(len(cached.TableName)))
 	// field FieldQuery string
 	size += hack.RuntimeAllocSize(int64(len(cached.FieldQuery)))
-	// field Vindex vitess.io/vitess/go/vt/vtgate/vindexes.SingleColumn
+	// field Vindex vitess.io/vitess/go/vt/vtgate/vindexes.Vindex
 	if cc, ok := cached.Vindex.(cachedObject); ok {
 		size += cc.CachedSize(true)
 	}
-	// field Value vitess.io/vitess/go/vt/vtgate/engine.RouteValue
-	if cc, ok := cached.Value.(cachedObject); ok {
-		size += cc.CachedSize(true)
+	// field Values []vitess.io/vitess/go/vt/vtgate/engine.RouteValue
+	{
+		size += hack.RuntimeAllocSize(int64(cap(cached.Values)) * int64(16))
+		for _, elem := range cached.Values {
+			if cc, ok := elem.(cachedObject); ok {
+				size += cc.CachedSize(true)
+			}
+		}
 	}
 	// field OrderBy []vitess.io/vitess/go/vt/vtgate/engine.OrderByParams
 	{
diff --git a/go/vt/vtgate/engine/primitive.go b/go/vt/vtgate/engine/primitive.go
index 4c067b64f7..76105adf96 100644
--- a/go/vt/vtgate/engine/primitive.go
+++ b/go/vt/vtgate/engine/primitive.go
@@ -88,6 +88,8 @@ type (
 		// Will replace all of the Topo functions.
 		ResolveDestinations(keyspace string, ids []*querypb.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][]*querypb.Value, error)
 
+		ResolveDestinationsMultiCol(keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error)
+
 		ExecuteVSchema(keyspace string, vschemaDDL *sqlparser.AlterVschema) error
 
 		SubmitOnlineDDL(onlineDDl *schema.OnlineDDL) error
diff --git a/go/vt/vtgate/engine/route.go b/go/vt/vtgate/engine/route.go
index dd4cb932fd..d403f518b5 100644
--- a/go/vt/vtgate/engine/route.go
+++ b/go/vt/vtgate/engine/route.go
@@ -75,10 +75,10 @@ type Route struct {
 	FieldQuery string
 
 	// Vindex specifies the vindex to be used.
-	Vindex vindexes.SingleColumn
+	Vindex vindexes.Vindex
 
-	// Value specifies the vindex value to use for routing.
-	Value RouteValue
+	// Values specifies the vindex values to use for routing.
+	Values []RouteValue
 
 	// OrderBy specifies the key order for merge sorting. This will be
 	// set only for scatter queries that need the results to be
@@ -274,28 +274,7 @@ func (route *Route) TryExecute(vcursor VCursor, bindVars map[string]*querypb.Bin
 }
 
 func (route *Route) executeInternal(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool) (*sqltypes.Result, error) {
-	var rss []*srvtopo.ResolvedShard
-	var bvs []map[string]*querypb.BindVariable
-	var err error
-	switch route.Opcode {
-	case SelectDBA:
-		rss, bvs, err = route.paramsSystemQuery(vcursor, bindVars)
-	case SelectUnsharded, SelectNext, SelectReference:
-		rss, bvs, err = route.paramsAnyShard(vcursor, bindVars)
-	case SelectScatter:
-		rss, bvs, err = route.paramsAllShards(vcursor, bindVars)
-	case SelectEqual, SelectEqualUnique:
-		rss, bvs, err = route.paramsSelectEqual(vcursor, bindVars)
-	case SelectIN:
-		rss, bvs, err = route.paramsSelectIn(vcursor, bindVars)
-	case SelectMultiEqual:
-		rss, bvs, err = route.paramsSelectMultiEqual(vcursor, bindVars)
-	case SelectNone:
-		rss, bvs, err = nil, nil, nil
-	default:
-		// Unreachable.
-		return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unsupported query route: %v", route)
-	}
+	rss, bvs, err := route.findRoute(vcursor, bindVars)
 	if err != nil {
 		return nil, err
 	}
@@ -332,6 +311,43 @@ func (route *Route) executeInternal(vcursor VCursor, bindVars map[string]*queryp
 	return route.sort(result)
 }
 
+func (route *Route) findRoute(vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]*srvtopo.ResolvedShard, []map[string]*querypb.BindVariable, error) {
+	switch route.Opcode {
+	case SelectDBA:
+		return route.paramsSystemQuery(vcursor, bindVars)
+	case SelectUnsharded, SelectNext, SelectReference:
+		return route.paramsAnyShard(vcursor, bindVars)
+	case SelectScatter:
+		return route.paramsAllShards(vcursor, bindVars)
+	case SelectEqual, SelectEqualUnique:
+		switch route.Vindex.(type) {
+		case vindexes.MultiColumn:
+			return route.paramsSelectEqualMultiCol(vcursor, bindVars)
+		default:
+			return route.paramsSelectEqual(vcursor, bindVars)
+		}
+	case SelectIN:
+		switch route.Vindex.(type) {
+		case vindexes.MultiColumn:
+			return route.paramsSelectInMultiCol(vcursor, bindVars)
+		default:
+			return route.paramsSelectIn(vcursor, bindVars)
+		}
+	case SelectMultiEqual:
+		switch route.Vindex.(type) {
+		case vindexes.MultiColumn:
+			return route.paramsSelectMultiEqualMultiCol(vcursor, bindVars)
+		default:
+			return route.paramsSelectMultiEqual(vcursor, bindVars)
+		}
+	case SelectNone:
+		return nil, nil, nil
+	default:
+		// Unreachable.
+		return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unsupported query route: %v", route)
+	}
+}
+
 func filterOutNilErrors(errs []error) []error {
 	var errors []error
 	for _, err := range errs {
@@ -344,31 +360,11 @@ func filterOutNilErrors(errs []error) []error {
 
 // TryStreamExecute performs a streaming exec.
 func (route *Route) TryStreamExecute(vcursor VCursor, bindVars map[string]*querypb.BindVariable, wantfields bool, callback func(*sqltypes.Result) error) error {
-	var rss []*srvtopo.ResolvedShard
-	var bvs []map[string]*querypb.BindVariable
-	var err error
 	if route.QueryTimeout != 0 {
 		cancel := vcursor.SetContextTimeout(time.Duration(route.QueryTimeout) * time.Millisecond)
 		defer cancel()
 	}
-	switch route.Opcode {
-	case SelectDBA:
-		rss, bvs, err = route.paramsSystemQuery(vcursor, bindVars)
-	case SelectUnsharded, SelectNext, SelectReference:
-		rss, bvs, err = route.paramsAnyShard(vcursor, bindVars)
-	case SelectScatter:
-		rss, bvs, err = route.paramsAllShards(vcursor, bindVars)
-	case SelectEqual, SelectEqualUnique:
-		rss, bvs, err = route.paramsSelectEqual(vcursor, bindVars)
-	case SelectIN:
-		rss, bvs, err = route.paramsSelectIn(vcursor, bindVars)
-	case SelectMultiEqual:
-		rss, bvs, err = route.paramsSelectMultiEqual(vcursor, bindVars)
-	case SelectNone:
-		rss, bvs, err = nil, nil, nil
-	default:
-		return fmt.Errorf("query %q cannot be used for streaming", route.Query)
-	}
+	rss, bvs, err := route.findRoute(vcursor, bindVars)
 	if err != nil {
 		return err
 	}
@@ -599,11 +595,11 @@ func (route *Route) paramsAnyShard(vcursor VCursor, bindVars map[string]*querypb
 }
 
 func (route *Route) paramsSelectEqual(vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]*srvtopo.ResolvedShard, []map[string]*querypb.BindVariable, error) {
-	value, err := route.Value.ResolveValue(bindVars)
+	value, err := route.Values[0].ResolveValue(bindVars)
 	if err != nil {
 		return nil, nil, err
 	}
-	rss, _, err := resolveShards(vcursor, route.Vindex, route.Keyspace, []sqltypes.Value{value})
+	rss, _, err := resolveShards(vcursor, route.Vindex.(vindexes.SingleColumn), route.Keyspace, []sqltypes.Value{value})
 	if err != nil {
 		return nil, nil, err
 	}
@@ -614,24 +610,208 @@ func (route *Route) paramsSelectEqual(vcursor VCursor, bindVars map[string]*quer
 	return rss, multiBindVars, nil
 }
 
+func (route *Route) paramsSelectEqualMultiCol(vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]*srvtopo.ResolvedShard, []map[string]*querypb.BindVariable, error) {
+	var rowValue []sqltypes.Value
+	for _, rvalue := range route.Values {
+		v, err := rvalue.ResolveValue(bindVars)
+		if err != nil {
+			return nil, nil, err
+		}
+		rowValue = append(rowValue, v)
+	}
+
+	rss, _, err := resolveShardsMultiCol(vcursor, route.Vindex.(vindexes.MultiColumn), route.Keyspace, [][]sqltypes.Value{rowValue}, false /* shardIdsNeeded */)
+	if err != nil {
+		return nil, nil, err
+	}
+	multiBindVars := make([]map[string]*querypb.BindVariable, len(rss))
+	for i := range multiBindVars {
+		multiBindVars[i] = bindVars
+	}
+	return rss, multiBindVars, nil
+}
+
+func resolveShardsMultiCol(vcursor VCursor, vindex vindexes.MultiColumn, keyspace *vindexes.Keyspace, rowColValues [][]sqltypes.Value, shardIdsNeeded bool) ([]*srvtopo.ResolvedShard, [][][]*querypb.Value, error) {
+	destinations, err := vindex.Map(vcursor, rowColValues)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	// And use the Resolver to map to ResolvedShards.
+	rss, shardsValues, err := vcursor.ResolveDestinationsMultiCol(keyspace.Name, rowColValues, destinations)
+	if err != nil {
+		return nil, nil, err
+	}
+
+	if shardIdsNeeded {
+		return rss, buildMultiColumnVindexValues(shardsValues), nil
+	}
+	return rss, nil, nil
+}
+
+// buildMultiColumnVindexValues takes in the values resolved for each shard and transposes them
+// and eliminates duplicates, returning the values to be used for each column for a multi column
+// vindex in each shard.
+func buildMultiColumnVindexValues(shardsValues [][][]sqltypes.Value) [][][]*querypb.Value {
+	var shardsIds [][][]*querypb.Value
+	for _, shardValues := range shardsValues {
+		// shardValues -> [[0,1], [0,2], [0,3]]
+		// shardIds -> [[0,0,0], [1,2,3]]
+		// cols = 2
+		cols := len(shardValues[0])
+		shardIds := make([][]*querypb.Value, cols)
+		colValSeen := make([]map[string]interface{}, cols)
+		for _, values := range shardValues {
+			for colIdx, value := range values {
+				if colValSeen[colIdx] == nil {
+					colValSeen[colIdx] = map[string]interface{}{}
+				}
+				if _, found := colValSeen[colIdx][value.String()]; found {
+					continue
+				}
+				shardIds[colIdx] = append(shardIds[colIdx], sqltypes.ValueToProto(value))
+				colValSeen[colIdx][value.String()] = nil
+			}
+		}
+		shardsIds = append(shardsIds, shardIds)
+	}
+	return shardsIds
+}
+
+func shardVarsMultiCol(bv map[string]*querypb.BindVariable, mapVals [][][]*querypb.Value, isSingleVal map[int]interface{}) []map[string]*querypb.BindVariable {
+	shardVars := make([]map[string]*querypb.BindVariable, len(mapVals))
+	for i, shardVals := range mapVals {
+		newbv := make(map[string]*querypb.BindVariable, len(bv)+len(shardVals)-len(isSingleVal))
+		for k, v := range bv {
+			newbv[k] = v
+		}
+		for j, vals := range shardVals {
+			if _, found := isSingleVal[j]; found {
+				// this vindex column is non-tuple column hence listVal bind variable is not required to be set.
+				continue
+			}
+			newbv[ListVarName+strconv.Itoa(j)] = &querypb.BindVariable{
+				Type:   querypb.Type_TUPLE,
+				Values: vals,
+			}
+		}
+		shardVars[i] = newbv
+	}
+	return shardVars
+}
+
 func (route *Route) paramsSelectIn(vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]*srvtopo.ResolvedShard, []map[string]*querypb.BindVariable, error) {
-	value, err := route.Value.ResolveList(bindVars)
+	value, err := route.Values[0].ResolveList(bindVars)
 	if err != nil {
 		return nil, nil, err
 	}
-	rss, values, err := resolveShards(vcursor, route.Vindex, route.Keyspace, value)
+	rss, values, err := resolveShards(vcursor, route.Vindex.(vindexes.SingleColumn), route.Keyspace, value)
 	if err != nil {
 		return nil, nil, err
 	}
 	return rss, shardVars(bindVars, values), nil
 }
 
+func (route *Route) paramsSelectInMultiCol(vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]*srvtopo.ResolvedShard, []map[string]*querypb.BindVariable, error) {
+	// gather values from all the column in the vindex
+	var multiColValues [][]sqltypes.Value
+	var err error
+	var lv []sqltypes.Value
+	isSingleVal := map[int]interface{}{}
+	for colIdx, rvalue := range route.Values {
+		lv, err = rvalue.ResolveList(bindVars)
+		if err != nil {
+			return nil, nil, err
+		}
+		if lv == nil {
+			v, err := rvalue.ResolveValue(bindVars)
+			if err != nil {
+				return nil, nil, err
+			}
+			isSingleVal[colIdx] = nil
+			lv = []sqltypes.Value{v}
+		}
+		multiColValues = append(multiColValues, lv)
+	}
+
+	/*
+		need to convert them into vindex keys
+		from: cola (1,2) colb (3,4,5)
+		to: keys (1,3) (1,4) (1,5) (2,3) (2,4) (2,5)
+
+		so that the vindex can map them into correct destination.
+	*/
+
+	var rowColValues [][]sqltypes.Value
+	for _, firstCol := range multiColValues[0] {
+		rowColValues = append(rowColValues, []sqltypes.Value{firstCol})
+	}
+	for idx := 1; idx < len(multiColValues); idx++ {
+		rowColValues = buildRowColValues(rowColValues, multiColValues[idx])
+	}
+
+	rss, mapVals, err := resolveShardsMultiCol(vcursor, route.Vindex.(vindexes.MultiColumn), route.Keyspace, rowColValues, true /* shardIdsNeeded */)
+	if err != nil {
+		return nil, nil, err
+	}
+	return rss, shardVarsMultiCol(bindVars, mapVals, isSingleVal), nil
+}
+
+// buildRowColValues will take [1,2][1,3] as left input and [4,5] as right input
+// convert it into [1,2,4][1,2,5][1,3,4][1,3,5]
+// all combination of left and right side.
+func buildRowColValues(left [][]sqltypes.Value, right []sqltypes.Value) [][]sqltypes.Value {
+	var allCombinations [][]sqltypes.Value
+	for _, firstPart := range left {
+		for _, secondPart := range right {
+			allCombinations = append(allCombinations, append(firstPart, secondPart))
+		}
+	}
+	return allCombinations
+}
+
 func (route *Route) paramsSelectMultiEqual(vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]*srvtopo.ResolvedShard, []map[string]*querypb.BindVariable, error) {
-	value, err := route.Value.ResolveList(bindVars)
+	value, err := route.Values[0].ResolveList(bindVars)
 	if err != nil {
 		return nil, nil, err
 	}
-	rss, _, err := resolveShards(vcursor, route.Vindex, route.Keyspace, value)
+	rss, _, err := resolveShards(vcursor, route.Vindex.(vindexes.SingleColumn), route.Keyspace, value)
+	if err != nil {
+		return nil, nil, err
+	}
+	multiBindVars := make([]map[string]*querypb.BindVariable, len(rss))
+	for i := range multiBindVars {
+		multiBindVars[i] = bindVars
+	}
+	return rss, multiBindVars, nil
+}
+
+func (route *Route) paramsSelectMultiEqualMultiCol(vcursor VCursor, bindVars map[string]*querypb.BindVariable) ([]*srvtopo.ResolvedShard, []map[string]*querypb.BindVariable, error) {
+	var multiColValues [][]sqltypes.Value
+	for _, rvalue := range route.Values {
+		v, err := rvalue.ResolveList(bindVars)
+		if err != nil {
+			return nil, nil, err
+		}
+		multiColValues = append(multiColValues, v)
+	}
+
+	// transpose from multi col value to vindex keys with one value from each multi column values.
+	// [1,3]
+	// [2,4]
+	// [5,6]
+	// change
+	// [1,2,5]
+	// [3,4,6]
+
+	rowColValues := make([][]sqltypes.Value, len(multiColValues[0]))
+	for _, colValues := range multiColValues {
+		for row, colVal := range colValues {
+			rowColValues[row] = append(rowColValues[row], colVal)
+		}
+	}
+
+	rss, _, err := resolveShardsMultiCol(vcursor, route.Vindex.(vindexes.MultiColumn), route.Keyspace, rowColValues, false /* shardIdsNotNeeded */)
 	if err != nil {
 		return nil, nil, err
 	}
@@ -653,6 +833,7 @@ func resolveShards(vcursor VCursor, vindex vindexes.SingleColumn, keyspace *vind
 	destinations, err := vindex.Map(vcursor, vindexKeys)
 	if err != nil {
 		return nil, nil, err
+
 	}
 
 	// And use the Resolver to map to ResolvedShards.
@@ -806,8 +987,8 @@ func (route *Route) description() PrimitiveDescription {
 	if route.Vindex != nil {
 		other["Vindex"] = route.Vindex.String()
 	}
-	if route.Value != nil {
-		other["Values"] = route.Value
+	if route.Values != nil {
+		other["Values"] = route.Values
 	}
 	if len(route.SysTableTableSchema) != 0 {
 		sysTabSchema := "["
diff --git a/go/vt/vtgate/evalengine/expressions.go b/go/vt/vtgate/evalengine/expressions.go
index 192c8160fc..f79e4ff3c1 100644
--- a/go/vt/vtgate/evalengine/expressions.go
+++ b/go/vt/vtgate/evalengine/expressions.go
@@ -265,7 +265,7 @@ func NewBindVar(key string, collation collations.TypedCollation) Expr {
 	}
 }
 
-// NewColumn returns a bind variable
+// NewColumn returns a column expression
 func NewColumn(offset int, collation collations.TypedCollation) Expr {
 	return &Column{
 		Offset:    offset,
@@ -273,6 +273,15 @@ func NewColumn(offset int, collation collations.TypedCollation) Expr {
 	}
 }
 
+// NewTupleExpr returns a tuple expression
+func NewTupleExpr(exprs ...Expr) Expr {
+	tupleExpr := make(TupleExpr, 0, len(exprs))
+	for _, f := range exprs {
+		tupleExpr = append(tupleExpr, f)
+	}
+	return tupleExpr
+}
+
 // Evaluate implements the Expr interface
 func (l *Literal) Evaluate(*ExpressionEnv) (EvalResult, error) {
 	return l.Val, nil
diff --git a/go/vt/vtgate/planbuilder/from.go b/go/vt/vtgate/planbuilder/from.go
index 0d33950996..3775bb1d49 100644
--- a/go/vt/vtgate/planbuilder/from.go
+++ b/go/vt/vtgate/planbuilder/from.go
@@ -261,8 +261,8 @@ func (pb *primitiveBuilder) buildTablePrimitive(tableExpr *sqlparser.AliasedTabl
 		// for keyspace id.
 		eroute = engine.NewSimpleRoute(engine.SelectEqualUnique, vschemaTable.Keyspace)
 		vindex, _ = vindexes.NewBinary("binary", nil)
-		eroute.Vindex, _ = vindex.(vindexes.SingleColumn)
-		eroute.Value = sqltypes.PlanValue{Value: sqltypes.MakeTrusted(sqltypes.VarBinary, vschemaTable.Pinned)}
+		eroute.Vindex = vindex
+		eroute.Values = []engine.RouteValue{sqltypes.PlanValue{Value: sqltypes.MakeTrusted(sqltypes.VarBinary, vschemaTable.Pinned)}}
 	}
 	eroute.TableName = sqlparser.String(vschemaTable.Name)
 	rb.eroute = eroute
diff --git a/go/vt/vtgate/planbuilder/querytree_transformers.go b/go/vt/vtgate/planbuilder/querytree_transformers.go
index 6d2a05166b..1f116e8272 100644
--- a/go/vt/vtgate/planbuilder/querytree_transformers.go
+++ b/go/vt/vtgate/planbuilder/querytree_transformers.go
@@ -18,6 +18,7 @@ package planbuilder
 
 import (
 	"sort"
+	"strconv"
 	"strings"
 
 	"vitess.io/vitess/go/mysql/collations"
@@ -376,7 +377,8 @@ func transformRoutePlan(ctx *planningContext, n *routeTree) (*routeGen4, error)
 	}
 
 	if n.selected != nil {
-		for _, predicate := range n.selected.predicates {
+		_, isMultiColumn := n.selected.foundVindex.(vindexes.MultiColumn)
+		for idx, predicate := range n.selected.predicates {
 			switch predicate := predicate.(type) {
 			case *sqlparser.ComparisonExpr:
 				if predicate.Operator == sqlparser.InOp {
@@ -388,6 +390,10 @@ func transformRoutePlan(ctx *planningContext, n *routeTree) (*routeGen4, error)
 								extractedSubquery.SetArgName(engine.ListVarName)
 							}
 						}
+						if isMultiColumn {
+							predicate.Right = sqlparser.ListArg(engine.ListVarName + strconv.Itoa(idx))
+							continue
+						}
 						predicate.Right = sqlparser.ListArg(engine.ListVarName)
 					}
 				}
@@ -427,15 +433,13 @@ func transformRoutePlan(ctx *planningContext, n *routeTree) (*routeGen4, error)
 		where = &sqlparser.Where{Expr: predicates, Type: sqlparser.WhereClause}
 	}
 
-	var singleColumn vindexes.SingleColumn
-	var value engine.RouteValue
+	var vindex vindexes.Vindex
+	var values []engine.RouteValue
 	if n.selectedVindex() != nil {
-		vdx, ok := n.selected.foundVindex.(vindexes.SingleColumn)
-		if !ok || len(n.selected.values) != 1 {
-			return nil, vterrors.New(vtrpcpb.Code_UNIMPLEMENTED, "unsupported: multi-column values")
+		vindex = n.selected.foundVindex
+		for _, value := range n.selected.values {
+			values = append(values, &evalengine.RouteValue{Expr: value})
 		}
-		singleColumn = vdx
-		value = &evalengine.RouteValue{Expr: n.selected.values[0]}
 	}
 
 	var expressions sqlparser.SelectExprs
@@ -471,8 +475,8 @@ func transformRoutePlan(ctx *planningContext, n *routeTree) (*routeGen4, error)
 			Opcode:              n.routeOpCode,
 			TableName:           strings.Join(tableNames, ", "),
 			Keyspace:            n.keyspace,
-			Vindex:              singleColumn,
-			Value:               value,
+			Vindex:              vindex,
+			Values:              values,
 			SysTableTableName:   n.SysTableTableName,
 			SysTableTableSchema: n.SysTableTableSchema,
 		},
diff --git a/go/vt/vtgate/planbuilder/route.go b/go/vt/vtgate/planbuilder/route.go
index 851b3a9318..a96168b37a 100644
--- a/go/vt/vtgate/planbuilder/route.go
+++ b/go/vt/vtgate/planbuilder/route.go
@@ -153,7 +153,7 @@ func (rb *route) ContainsTables() semantics.TableSet {
 // Wireup implements the logicalPlan interface
 func (rb *route) Wireup(plan logicalPlan, jt *jointab) error {
 	// Precaution: update ERoute.Values only if it's not set already.
-	if rb.eroute.Value == nil {
+	if rb.eroute.Values == nil {
 		// Resolve values stored in the logical plan.
 		switch vals := rb.condition.(type) {
 		case *sqlparser.ComparisonExpr:
@@ -161,7 +161,7 @@ func (rb *route) Wireup(plan logicalPlan, jt *jointab) error {
 			if err != nil {
 				return err
 			}
-			rb.eroute.Value = pv
+			rb.eroute.Values = []engine.RouteValue{pv}
 			vals.Right = sqlparser.ListArg(engine.ListVarName)
 		case nil:
 			// no-op.
@@ -170,7 +170,7 @@ func (rb *route) Wireup(plan logicalPlan, jt *jointab) error {
 			if err != nil {
 				return err
 			}
-			rb.eroute.Value = pv
+			rb.eroute.Values = []engine.RouteValue{pv}
 		}
 	}
 
diff --git a/go/vt/vtgate/planbuilder/routetree.go b/go/vt/vtgate/planbuilder/routetree.go
index 18f99cc497..d6e810f00b 100644
--- a/go/vt/vtgate/planbuilder/routetree.go
+++ b/go/vt/vtgate/planbuilder/routetree.go
@@ -81,8 +81,10 @@ type (
 
 	// vindexOption stores the information needed to know if we have all the information needed to use a vindex
 	vindexOption struct {
-		ready       bool
-		values      []evalengine.Expr
+		ready  bool
+		values []evalengine.Expr
+		// columns that we have seen so far. Used only for multi-column vindexes so that we can track how many columns part of the vindex we have seen
+		colsSeen    map[string]interface{}
 		valueExprs  []sqlparser.Expr
 		predicates  []sqlparser.Expr
 		opcode      engine.RouteOpcode
@@ -509,31 +511,130 @@ func (rp *routeTree) haveMatchingVindex(
 		if !ctx.semTable.DirectDeps(column).IsSolvedBy(v.tableID) {
 			continue
 		}
-		// Ignore MultiColumn vindexes for finding matching Vindex.
-		if _, isSingleCol := v.colVindex.Vindex.(vindexes.SingleColumn); !isSingleCol {
-			continue
-		}
+		switch v.colVindex.Vindex.(type) {
+		case vindexes.SingleColumn:
+			col := v.colVindex.Columns[0]
+			if column.Name.Equal(col) {
+				// single column vindex - just add the option
+				routeOpcode := opcode(v.colVindex)
+				vindex := vfunc(v.colVindex)
+				v.options = append(v.options, &vindexOption{
+					values:      []evalengine.Expr{value},
+					valueExprs:  []sqlparser.Expr{valueExpr},
+					predicates:  []sqlparser.Expr{node},
+					opcode:      routeOpcode,
+					foundVindex: vindex,
+					cost:        costFor(vindex, routeOpcode),
+					ready:       true,
+				})
+				newVindexFound = true
+			}
+		case vindexes.MultiColumn:
+			colLoweredName := ""
+			indexOfCol := -1
+			for idx, col := range v.colVindex.Columns {
+				if column.Name.Equal(col) {
+					colLoweredName = column.Name.Lowered()
+					indexOfCol = idx
+					break
+				}
+			}
+			if colLoweredName == "" {
+				break
+			}
 
-		col := v.colVindex.Columns[0]
-		if column.Name.Equal(col) {
-			// single column vindex - just add the option
-			routeOpcode := opcode(v.colVindex)
-			vindex := vfunc(v.colVindex)
-			v.options = append(v.options, &vindexOption{
-				values:      []evalengine.Expr{value},
-				valueExprs:  []sqlparser.Expr{valueExpr},
-				predicates:  []sqlparser.Expr{node},
-				opcode:      routeOpcode,
-				foundVindex: vindex,
-				cost:        costFor(vindex, routeOpcode),
-				ready:       true,
-			})
-			newVindexFound = true
+			var newOption []*vindexOption
+			for _, op := range v.options {
+				if op.ready {
+					continue
+				}
+				_, isPresent := op.colsSeen[colLoweredName]
+				if isPresent {
+					continue
+				}
+				option := copyOption(op)
+				optionReady := option.updateWithNewColumn(colLoweredName, valueExpr, indexOfCol, value, node, v.colVindex, opcode)
+				if optionReady {
+					newVindexFound = true
+				}
+				newOption = append(newOption, option)
+			}
+			v.options = append(v.options, newOption...)
+
+			// multi column vindex - just always add as new option for furince we do not have one already
+			option := createOption(v.colVindex, vfunc)
+			optionReady := option.updateWithNewColumn(colLoweredName, valueExpr, indexOfCol, value, node, v.colVindex, opcode)
+			if optionReady {
+				newVindexFound = true
+			}
+			v.options = append(v.options, option)
 		}
 	}
 	return newVindexFound
 }
 
+func createOption(
+	colVindex *vindexes.ColumnVindex,
+	vfunc func(*vindexes.ColumnVindex) vindexes.Vindex,
+) *vindexOption {
+	values := make([]evalengine.Expr, len(colVindex.Columns))
+	predicates := make([]sqlparser.Expr, len(colVindex.Columns))
+	vindex := vfunc(colVindex)
+
+	return &vindexOption{
+		values:      values,
+		predicates:  predicates,
+		colsSeen:    map[string]interface{}{},
+		foundVindex: vindex,
+	}
+}
+
+func copyOption(orig *vindexOption) *vindexOption {
+	colsSeen := make(map[string]interface{}, len(orig.colsSeen))
+	valueExprs := make([]sqlparser.Expr, len(orig.valueExprs))
+	values := make([]evalengine.Expr, len(orig.values))
+	predicates := make([]sqlparser.Expr, len(orig.predicates))
+
+	copy(values, orig.values)
+	copy(valueExprs, orig.valueExprs)
+	copy(predicates, orig.predicates)
+	for k, v := range orig.colsSeen {
+		colsSeen[k] = v
+	}
+	vo := &vindexOption{
+		values:      values,
+		colsSeen:    colsSeen,
+		valueExprs:  valueExprs,
+		predicates:  predicates,
+		opcode:      orig.opcode,
+		foundVindex: orig.foundVindex,
+		cost:        orig.cost,
+	}
+	return vo
+}
+
+func (option *vindexOption) updateWithNewColumn(
+	colLoweredName string,
+	valueExpr sqlparser.Expr,
+	indexOfCol int,
+	value evalengine.Expr,
+	node sqlparser.Expr,
+	colVindex *vindexes.ColumnVindex,
+	opcode func(*vindexes.ColumnVindex) engine.RouteOpcode,
+) bool {
+	option.colsSeen[colLoweredName] = true
+	option.valueExprs = append(option.valueExprs, valueExpr)
+	option.values[indexOfCol] = value
+	option.predicates[indexOfCol] = node
+	option.ready = len(option.colsSeen) == len(colVindex.Columns)
+	routeOpcode := opcode(colVindex)
+	if option.opcode < routeOpcode {
+		option.opcode = routeOpcode
+		option.cost = costFor(option.foundVindex, routeOpcode)
+	}
+	return option.ready
+}
+
 // pickBestAvailableVindex goes over the available vindexes for this route and picks the best one available.
 func (rp *routeTree) pickBestAvailableVindex() {
 	for _, v := range rp.vindexPreds {
diff --git a/go/vt/vtgate/vcursor_impl.go b/go/vt/vtgate/vcursor_impl.go
index f29ff7dbe0..9d9f018f1c 100644
--- a/go/vt/vtgate/vcursor_impl.go
+++ b/go/vt/vtgate/vcursor_impl.go
@@ -558,6 +558,10 @@ func (vc *vcursorImpl) ResolveDestinations(keyspace string, ids []*querypb.Value
 	return vc.resolver.ResolveDestinations(vc.ctx, keyspace, vc.tabletType, ids, destinations)
 }
 
+func (vc *vcursorImpl) ResolveDestinationsMultiCol(keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) {
+	return vc.resolver.ResolveDestinationsMultiCol(vc.ctx, keyspace, vc.tabletType, ids, destinations)
+}
+
 func (vc *vcursorImpl) Session() engine.SessionActions {
 	return vc
 }


Test patch:
diff --git a/go/test/endtoend/vtgate/gen4/gen4_test.go b/go/test/endtoend/vtgate/gen4/gen4_test.go
index 7170d3ce5a..93f4215446 100644
--- a/go/test/endtoend/vtgate/gen4/gen4_test.go
+++ b/go/test/endtoend/vtgate/gen4/gen4_test.go
@@ -255,3 +255,24 @@ func TestHashJoin(t *testing.T) {
 	defer utils.Exec(t, conn, `set workload = oltp`)
 	utils.AssertMatches(t, conn, `select /*vt+ ALLOW_HASH_JOIN */ t1.id from t1 x join t1 where x.col = t1.col and x.id <= 3 and t1.id >= 3`, `[[INT64(3)]]`)
 }
+
+func TestMultiColumnVindex(t *testing.T) {
+	ctx := context.Background()
+	conn, err := mysql.Connect(ctx, &vtParams)
+	require.NoError(t, err)
+	defer conn.Close()
+
+	defer utils.ExecAllowError(t, conn, `delete from user_region`)
+	utils.Exec(t, conn, `insert into user_region(id, cola, colb) values (1, 1, 2),(2, 30, 40),(3, 500, 600),(4, 30, 40),(5, 10000, 30000),(6, 422333, 40),(7, 30, 60)`)
+
+	for _, workload := range []string{"olap", "oltp"} {
+		t.Run(workload, func(t *testing.T) {
+			utils.Exec(t, conn, fmt.Sprintf(`set workload = %s`, workload))
+			utils.AssertMatches(t, conn, `select id from user_region where cola = 1 and colb = 2`, `[[INT64(1)]]`)
+			utils.AssertMatches(t, conn, `select id from user_region where cola in (30,422333) and colb = 40 order by id`, `[[INT64(2)] [INT64(4)] [INT64(6)]]`)
+			utils.AssertMatches(t, conn, `select id from user_region where cola in (30,422333) and colb in (40,60) order by id`, `[[INT64(2)] [INT64(4)] [INT64(6)] [INT64(7)]]`)
+			utils.AssertMatches(t, conn, `select id from user_region where cola in (30,422333) and colb in (40,60) and cola = 422333`, `[[INT64(6)]]`)
+			utils.AssertMatches(t, conn, `select id from user_region where cola in (30,422333) and colb in (40,60) and cola = 30 and colb = 60`, `[[INT64(7)]]`)
+		})
+	}
+}
diff --git a/go/test/endtoend/vtgate/gen4/main_test.go b/go/test/endtoend/vtgate/gen4/main_test.go
index 5273a30a00..92864252af 100644
--- a/go/test/endtoend/vtgate/gen4/main_test.go
+++ b/go/test/endtoend/vtgate/gen4/main_test.go
@@ -52,6 +52,13 @@ create table t3(
 	tcol2 varchar(50),
 	primary key(id)
 ) Engine=InnoDB;
+
+create table user_region(
+	id bigint,
+	cola bigint,
+	colb bigint,
+	primary key(id)
+) Engine=InnoDB;
 `
 	unshardedSchemaSQL = `create table u_a(
 	id bigint,
@@ -72,6 +79,12 @@ create table u_b(
   "vindexes": {
     "xxhash": {
       "type": "xxhash"
+    },
+    "regional_vdx": {
+	  "type": "region_experimental",
+	  "params": {
+		"region_bytes": "1"
+	  }
     }
   },
   "tables": {
@@ -110,7 +123,15 @@ create table u_b(
           "type": "VARCHAR"
         }
       ]
-    }
+    },
+    "user_region": {
+	  "column_vindexes": [
+	    {
+          "columns": ["cola","colb"],
+		  "name": "regional_vdx"
+		}
+      ]
+	}
   }
 }`
 
diff --git a/go/vt/vtgate/engine/fake_vcursor_test.go b/go/vt/vtgate/engine/fake_vcursor_test.go
index 77a3a3c1c2..a76aa2eaf4 100644
--- a/go/vt/vtgate/engine/fake_vcursor_test.go
+++ b/go/vt/vtgate/engine/fake_vcursor_test.go
@@ -260,6 +260,10 @@ func (t *noopVCursor) ResolveDestinations(keyspace string, ids []*querypb.Value,
 	panic("unimplemented")
 }
 
+func (t *noopVCursor) ResolveDestinationsMultiCol(keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) {
+	panic("unimplemented")
+}
+
 func (t *noopVCursor) SubmitOnlineDDL(onlineDDl *schema.OnlineDDL) error {
 	panic("unimplemented")
 }
@@ -506,6 +510,73 @@ func (f *loggingVCursor) ResolveDestinations(keyspace string, ids []*querypb.Val
 	return rss, values, nil
 }
 
+func (f *loggingVCursor) ResolveDestinationsMultiCol(keyspace string, ids [][]sqltypes.Value, destinations []key.Destination) ([]*srvtopo.ResolvedShard, [][][]sqltypes.Value, error) {
+	f.log = append(f.log, fmt.Sprintf("ResolveDestinationsMultiCol %v %v %v", keyspace, ids, key.DestinationsString(destinations)))
+	if f.shardErr != nil {
+		return nil, nil, f.shardErr
+	}
+
+	var rss []*srvtopo.ResolvedShard
+	var values [][][]sqltypes.Value
+	visited := make(map[string]int)
+	for i, destination := range destinations {
+		var shards []string
+
+		switch d := destination.(type) {
+		case key.DestinationAllShards:
+			shards = f.shards
+		case key.DestinationKeyRange:
+			shards = f.shardForKsid
+		case key.DestinationKeyspaceID:
+			if f.shardForKsid == nil || f.curShardForKsid >= len(f.shardForKsid) {
+				shards = []string{"-20"}
+			} else {
+				shards = []string{f.shardForKsid[f.curShardForKsid]}
+				f.curShardForKsid++
+			}
+		case key.DestinationKeyspaceIDs:
+			for _, ksid := range d {
+				if string(ksid) < "\x20" {
+					shards = append(shards, "-20")
+				} else {
+					shards = append(shards, "20-")
+				}
+			}
+		case key.DestinationAnyShard:
+			// Take the first shard.
+			shards = f.shards[:1]
+		case key.DestinationNone:
+			// Nothing to do here.
+		case key.DestinationShard:
+			shards = []string{destination.String()}
+		default:
+			return nil, nil, fmt.Errorf("unsupported destination: %v", destination)
+		}
+
+		for _, shard := range shards {
+			vi, ok := visited[shard]
+			if !ok {
+				vi = len(rss)
+				visited[shard] = vi
+				rss = append(rss, &srvtopo.ResolvedShard{
+					Target: &querypb.Target{
+						Keyspace:   keyspace,
+						Shard:      shard,
+						TabletType: f.resolvedTargetTabletType,
+					},
+				})
+				if ids != nil {
+					values = append(values, nil)
+				}
+			}
+			if ids != nil {
+				values[vi] = append(values[vi], ids[i])
+			}
+		}
+	}
+	return rss, values, nil
+}
+
 func (f *loggingVCursor) ExpectLog(t *testing.T, want []string) {
 	t.Helper()
 	if len(f.log) == 0 && len(want) == 0 {
diff --git a/go/vt/vtgate/engine/route_test.go b/go/vt/vtgate/engine/route_test.go
index fce887ca8a..8361ed63bd 100644
--- a/go/vt/vtgate/engine/route_test.go
+++ b/go/vt/vtgate/engine/route_test.go
@@ -18,6 +18,8 @@ package engine
 
 import (
 	"errors"
+	"fmt"
+	"strconv"
 	"testing"
 
 	"github.com/stretchr/testify/assert"
@@ -235,9 +237,9 @@ func TestSelectEqualUnique(t *testing.T) {
 	)
 	sel.Vindex = vindex.(vindexes.SingleColumn)
 
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.NewLiteralInt(1),
-	}
+	}}
 	vc := &loggingVCursor{
 		shards:  []string{"-20", "20-"},
 		results: []*sqltypes.Result{defaultSelectResult},
@@ -272,7 +274,7 @@ func TestSelectNone(t *testing.T) {
 		"dummy_select_field",
 	)
 	sel.Vindex = vindex.(vindexes.SingleColumn)
-	sel.Value = nil
+	sel.Values = nil
 
 	vc := &loggingVCursor{
 		shards:  []string{"-20", "20-"},
@@ -307,9 +309,9 @@ func TestSelectEqualUniqueScatter(t *testing.T) {
 		"dummy_select_field",
 	)
 	sel.Vindex = vindex.(vindexes.SingleColumn)
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.NewLiteralInt(1),
-	}
+	}}
 	vc := &loggingVCursor{
 		shards:       []string{"-20", "20-"},
 		shardForKsid: []string{"-20", "20-"},
@@ -349,9 +351,9 @@ func TestSelectEqual(t *testing.T) {
 		"dummy_select_field",
 	)
 	sel.Vindex = vindex.(vindexes.SingleColumn)
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.NewLiteralInt(1),
-	}
+	}}
 	vc := &loggingVCursor{
 		shards: []string{"-20", "20-"},
 		results: []*sqltypes.Result{
@@ -402,9 +404,9 @@ func TestSelectEqualNoRoute(t *testing.T) {
 		"dummy_select_field",
 	)
 	sel.Vindex = vindex.(vindexes.SingleColumn)
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.NewLiteralInt(1),
-	}
+	}}
 
 	vc := &loggingVCursor{shards: []string{"-20", "20-"}}
 	result, err := sel.TryExecute(vc, map[string]*querypb.BindVariable{}, false)
@@ -437,13 +439,13 @@ func TestSelectINUnique(t *testing.T) {
 		"dummy_select_field",
 	)
 	sel.Vindex = vindex.(vindexes.SingleColumn)
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.TupleExpr{
 			evalengine.NewLiteralInt(1),
 			evalengine.NewLiteralInt(2),
 			evalengine.NewLiteralInt(4),
 		},
-	}
+	}}
 	vc := &loggingVCursor{
 		shards:       []string{"-20", "20-"},
 		shardForKsid: []string{"-20", "-20", "20-"},
@@ -486,13 +488,13 @@ func TestSelectINNonUnique(t *testing.T) {
 		"dummy_select_field",
 	)
 	sel.Vindex = vindex.(vindexes.SingleColumn)
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.TupleExpr{
 			evalengine.NewLiteralInt(1),
 			evalengine.NewLiteralInt(2),
 			evalengine.NewLiteralInt(4),
 		},
-	}
+	}}
 
 	fields := sqltypes.MakeTestFields(
 		"fromc|toc",
@@ -549,13 +551,13 @@ func TestSelectMultiEqual(t *testing.T) {
 		"dummy_select_field",
 	)
 	sel.Vindex = vindex.(vindexes.SingleColumn)
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.TupleExpr{
 			evalengine.NewLiteralInt(1),
 			evalengine.NewLiteralInt(2),
 			evalengine.NewLiteralInt(4),
 		},
-	}
+	}}
 
 	vc := &loggingVCursor{
 		shards:       []string{"-20", "20-"},
@@ -600,9 +602,9 @@ func TestSelectLike(t *testing.T) {
 	)
 
 	sel.Vindex = vindex
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.NewLiteralString([]byte("a%"), collations.TypedCollation{}),
-	}
+	}}
 	// md5("a") = 0cc175b9c0f1b6a831c399e269772661
 	// keyspace id prefix for "a" is 0x0c
 	vc.shardForKsid = []string{"-0c80", "0c80-0d"}
@@ -630,9 +632,9 @@ func TestSelectLike(t *testing.T) {
 
 	vc.Rewind()
 
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.NewLiteralString([]byte("ab%"), collations.TypedCollation{}),
-	}
+	}}
 	// md5("b") = 92eb5ffee6ae2fec3ad71c777531578f
 	// keyspace id prefix for "ab" is 0x0c92
 	// adding one byte to the prefix just hit one shard
@@ -772,9 +774,9 @@ func TestRouteGetFields(t *testing.T) {
 		"dummy_select_field",
 	)
 	sel.Vindex = vindex.(vindexes.SingleColumn)
-	sel.Value = &evalengine.RouteValue{
+	sel.Values = []RouteValue{&evalengine.RouteValue{
 		Expr: evalengine.NewLiteralInt(1),
-	}
+	}}
 
 	vc := &loggingVCursor{shards: []string{"-20", "20-"}}
 	result, err := sel.TryExecute(vc, map[string]*querypb.BindVariable{}, true)
@@ -1367,3 +1369,249 @@ func TestExecFail(t *testing.T) {
 		vc.ExpectWarnings(t, []*querypb.QueryWarning{{Code: mysql.ERQueryInterrupted, Message: "query timeout -20 (errno 1317) (sqlstate HY000)"}})
 	})
 }
+
+func TestSelectEqualUniqueMultiColumnVindex(t *testing.T) {
+	vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"})
+	sel := NewRoute(
+		SelectEqualUnique,
+		&vindexes.Keyspace{
+			Name:    "ks",
+			Sharded: true,
+		},
+		"dummy_select",
+		"dummy_select_field",
+	)
+	sel.Vindex = vindex
+	sel.Values = []RouteValue{&evalengine.RouteValue{
+		Expr: evalengine.NewLiteralInt(1),
+	}, &evalengine.RouteValue{
+		Expr: evalengine.NewLiteralInt(2),
+	}}
+
+	vc := &loggingVCursor{
+		shards:  []string{"-20", "20-"},
+		results: []*sqltypes.Result{defaultSelectResult},
+	}
+	result, err := sel.TryExecute(vc, map[string]*querypb.BindVariable{}, false)
+	require.NoError(t, err)
+	vc.ExpectLog(t, []string{
+		`ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`,
+		`ExecuteMultiShard ks.-20: dummy_select {} false false`,
+	})
+	expectResult(t, "sel.Execute", result, defaultSelectResult)
+
+	vc.Rewind()
+	result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false)
+	require.NoError(t, err)
+	vc.ExpectLog(t, []string{
+		`ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f)`,
+		`StreamExecuteMulti dummy_select ks.-20: {} `,
+	})
+	expectResult(t, "sel.StreamExecute", result, defaultSelectResult)
+}
+
+func TestSelectINMultiColumnVindex(t *testing.T) {
+	vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"})
+	sel := NewRoute(
+		SelectIN,
+		&vindexes.Keyspace{
+			Name:    "ks",
+			Sharded: true,
+		},
+		"dummy_select",
+		"dummy_select_field",
+	)
+	sel.Vindex = vindex
+	sel.Values = []RouteValue{
+		&evalengine.RouteValue{
+			Expr: evalengine.NewTupleExpr(
+				evalengine.NewLiteralInt(1),
+				evalengine.NewLiteralInt(2),
+			),
+		},
+		&evalengine.RouteValue{
+			Expr: evalengine.NewTupleExpr(
+				evalengine.NewLiteralInt(3),
+				evalengine.NewLiteralInt(4),
+			),
+		},
+	}
+
+	vc := &loggingVCursor{
+		shards:       []string{"-20", "20-"},
+		shardForKsid: []string{"-20", "20-", "20-", "20-"},
+		results:      []*sqltypes.Result{defaultSelectResult},
+	}
+	result, err := sel.TryExecute(vc, map[string]*querypb.BindVariable{}, false)
+	require.NoError(t, err)
+	vc.ExpectLog(t, []string{
+		`ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)] [INT64(2) INT64(3)] [INT64(2) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe),DestinationKeyspaceID(024eb190c9a2fa169c),DestinationKeyspaceID(02d2fd8867d50d2dfe)`,
+		`ExecuteMultiShard ks.-20: dummy_select {__vals0: type:TUPLE values:{type:INT64 value:"1"} __vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: dummy_select {__vals0: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} __vals1: type:TUPLE values:{type:INT64 value:"4"} values:{type:INT64 value:"3"}} false false`,
+	})
+	expectResult(t, "sel.Execute", result, defaultSelectResult)
+
+	vc.Rewind()
+	result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false)
+	require.NoError(t, err)
+	vc.ExpectLog(t, []string{
+		`ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)] [INT64(2) INT64(3)] [INT64(2) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe),DestinationKeyspaceID(024eb190c9a2fa169c),DestinationKeyspaceID(02d2fd8867d50d2dfe)`,
+		`StreamExecuteMulti dummy_select ks.-20: {__vals0: type:TUPLE values:{type:INT64 value:"1"} __vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: {__vals0: type:TUPLE values:{type:INT64 value:"1"} values:{type:INT64 value:"2"} __vals1: type:TUPLE values:{type:INT64 value:"4"} values:{type:INT64 value:"3"}} `,
+	})
+	expectResult(t, "sel.StreamExecute", result, defaultSelectResult)
+}
+
+func TestSelectINMixedMultiColumnComparision(t *testing.T) {
+	vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"})
+	sel := NewRoute(
+		SelectIN,
+		&vindexes.Keyspace{
+			Name:    "ks",
+			Sharded: true,
+		},
+		"dummy_select",
+		"dummy_select_field",
+	)
+	sel.Vindex = vindex
+	sel.Values = []RouteValue{
+		&evalengine.RouteValue{
+			Expr: evalengine.NewLiteralInt(1),
+		},
+		&evalengine.RouteValue{
+			Expr: evalengine.NewTupleExpr(
+				evalengine.NewLiteralInt(3),
+				evalengine.NewLiteralInt(4),
+			),
+		},
+	}
+
+	vc := &loggingVCursor{
+		shards:       []string{"-20", "20-"},
+		shardForKsid: []string{"-20", "20-"},
+		results:      []*sqltypes.Result{defaultSelectResult},
+	}
+	result, err := sel.TryExecute(vc, map[string]*querypb.BindVariable{}, false)
+	require.NoError(t, err)
+	vc.ExpectLog(t, []string{
+		`ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe)`,
+		`ExecuteMultiShard ks.-20: dummy_select {__vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: dummy_select {__vals1: type:TUPLE values:{type:INT64 value:"4"}} false false`,
+	})
+	expectResult(t, "sel.Execute", result, defaultSelectResult)
+
+	vc.Rewind()
+	result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false)
+	require.NoError(t, err)
+	vc.ExpectLog(t, []string{
+		`ResolveDestinationsMultiCol ks [[INT64(1) INT64(3)] [INT64(1) INT64(4)]] Destinations:DestinationKeyspaceID(014eb190c9a2fa169c),DestinationKeyspaceID(01d2fd8867d50d2dfe)`,
+		`StreamExecuteMulti dummy_select ks.-20: {__vals1: type:TUPLE values:{type:INT64 value:"3"}} ks.20-: {__vals1: type:TUPLE values:{type:INT64 value:"4"}} `,
+	})
+	expectResult(t, "sel.StreamExecute", result, defaultSelectResult)
+}
+
+func TestSelectMultiEqualMultiCol(t *testing.T) {
+	vindex, _ := vindexes.NewRegionExperimental("", map[string]string{"region_bytes": "1"})
+	sel := NewRoute(
+		SelectMultiEqual,
+		&vindexes.Keyspace{Name: "ks", Sharded: true},
+		"dummy_select",
+		"dummy_select_field",
+	)
+	sel.Vindex = vindex
+	sel.Values = []RouteValue{
+		&evalengine.RouteValue{
+			Expr: evalengine.NewTupleExpr(
+				evalengine.NewLiteralInt(1),
+				evalengine.NewLiteralInt(3),
+			)},
+		&evalengine.RouteValue{
+			Expr: evalengine.NewTupleExpr(
+				evalengine.NewLiteralInt(2),
+				evalengine.NewLiteralInt(4),
+			),
+		},
+	}
+
+	vc := &loggingVCursor{
+		shards:       []string{"-20", "20-40", "40-"},
+		shardForKsid: []string{"-20", "40-"},
+		results:      []*sqltypes.Result{defaultSelectResult},
+	}
+	result, err := sel.TryExecute(vc, map[string]*querypb.BindVariable{}, false)
+	require.NoError(t, err)
+	vc.ExpectLog(t, []string{
+		`ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)] [INT64(3) INT64(4)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f),DestinationKeyspaceID(03d2fd8867d50d2dfe)`,
+		`ExecuteMultiShard ks.-20: dummy_select {} ks.40-: dummy_select {} false false`,
+	})
+	expectResult(t, "sel.Execute", result, defaultSelectResult)
+
+	vc.Rewind()
+	result, err = wrapStreamExecute(sel, vc, map[string]*querypb.BindVariable{}, false)
+	require.NoError(t, err)
+	vc.ExpectLog(t, []string{
+		`ResolveDestinationsMultiCol ks [[INT64(1) INT64(2)] [INT64(3) INT64(4)]] Destinations:DestinationKeyspaceID(0106e7ea22ce92708f),DestinationKeyspaceID(03d2fd8867d50d2dfe)`,
+		`StreamExecuteMulti dummy_select ks.-20: {} ks.40-: {} `,
+	})
+	expectResult(t, "sel.StreamExecute", result, defaultSelectResult)
+}
+
+func TestBuildRowColValues(t *testing.T) {
+	out := buildRowColValues([][]sqltypes.Value{
+		{sqltypes.NewInt64(1), sqltypes.NewInt64(10)},
+		{sqltypes.NewInt64(2), sqltypes.NewInt64(20)},
+	}, []sqltypes.Value{
+		sqltypes.NewInt64(3),
+		sqltypes.NewInt64(4),
+	})
+
+	require.Len(t, out, 4)
+	require.EqualValues(t, "[INT64(1) INT64(10) INT64(3)]", fmt.Sprintf("%s", out[0]))
+	require.EqualValues(t, "[INT64(1) INT64(10) INT64(4)]", fmt.Sprintf("%s", out[1]))
+	require.EqualValues(t, "[INT64(2) INT64(20) INT64(3)]", fmt.Sprintf("%s", out[2]))
+	require.EqualValues(t, "[INT64(2) INT64(20) INT64(4)]", fmt.Sprintf("%s", out[3]))
+}
+
+func TestBuildMultiColumnVindexValues(t *testing.T) {
+	testcases := []struct {
+		input  [][][]sqltypes.Value
+		output [][][]*querypb.Value
+	}{
+		{
+			input: [][][]sqltypes.Value{
+				{
+					{sqltypes.NewInt64(1), sqltypes.NewInt64(10)},
+					{sqltypes.NewInt64(2), sqltypes.NewInt64(20)},
+				}, {
+					{sqltypes.NewInt64(10), sqltypes.NewInt64(10)},
+					{sqltypes.NewInt64(20), sqltypes.NewInt64(20)},
+				},
+			},
+			output: [][][]*querypb.Value{
+				{
+					{sqltypes.ValueToProto(sqltypes.NewInt64(1)), sqltypes.ValueToProto(sqltypes.NewInt64(2))},
+					{sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))},
+				}, {
+					{sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))},
+					{sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))},
+				},
+			},
+		}, {
+			input: [][][]sqltypes.Value{{
+				{sqltypes.NewInt64(10), sqltypes.NewInt64(10), sqltypes.NewInt64(1)},
+				{sqltypes.NewInt64(20), sqltypes.NewInt64(20), sqltypes.NewInt64(1)},
+			},
+			},
+			output: [][][]*querypb.Value{{
+				{sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))},
+				{sqltypes.ValueToProto(sqltypes.NewInt64(10)), sqltypes.ValueToProto(sqltypes.NewInt64(20))},
+				{sqltypes.ValueToProto(sqltypes.NewInt64(1))},
+			},
+			},
+		},
+	}
+
+	for idx, testcase := range testcases {
+		t.Run(strconv.Itoa(idx), func(t *testing.T) {
+			out := buildMultiColumnVindexValues(testcase.input)
+			require.EqualValues(t, testcase.output, out)
+		})
+	}
+}
diff --git a/go/vt/vtgate/executor_framework_test.go b/go/vt/vtgate/executor_framework_test.go
index 582f4669c7..fda61fab3b 100644
--- a/go/vt/vtgate/executor_framework_test.go
+++ b/go/vt/vtgate/executor_framework_test.go
@@ -130,7 +130,13 @@ var executorVSchema = `
         		"to": "keyspace_id"
       		},
       		"owner": "t2_wo_lookup"
-    	}
+    	},
+		"regional_vdx": {
+			"type": "region_experimental",
+			"params": {
+				"region_bytes": "1"
+			}
+        }
 	},
 	"tables": {
 		"user": {
@@ -302,7 +308,15 @@ var executorVSchema = `
 				  	"name": "t2_lu_vdx"
 				}
             ]
-    	}
+    	},
+		"user_region": {
+			"column_vindexes": [
+				{
+					"columns": ["cola","colb"],
+					"name": "regional_vdx"
+				}
+			]
+		}
 	}
 }
 `
diff --git a/go/vt/vtgate/executor_select_test.go b/go/vt/vtgate/executor_select_test.go
index 8923e4fe89..e577b6025f 100644
--- a/go/vt/vtgate/executor_select_test.go
+++ b/go/vt/vtgate/executor_select_test.go
@@ -2698,3 +2698,235 @@ func TestGen4SelectStraightJoin(t *testing.T) {
 	utils.MustMatch(t, wantQueries, sbc1.Queries)
 	utils.MustMatch(t, wantWarnings, session.Warnings)
 }
+
+func TestGen4MultiColumnVindexEqual(t *testing.T) {
+	executor, sbc1, sbc2, _ := createExecutorEnv()
+	executor.normalize = true
+	*plannerVersion = "gen4"
+	defer func() {
+		// change it back to v3
+		*plannerVersion = "v3"
+	}()
+
+	session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
+	query := "select * from user_region where cola = 1 and colb = 2"
+	_, err := executor.Execute(context.Background(),
+		"TestGen4MultiColumnVindex",
+		session,
+		query, map[string]*querypb.BindVariable{},
+	)
+	require.NoError(t, err)
+	wantQueries := []*querypb.BoundQuery{
+		{
+			Sql: "select * from user_region where cola = :vtg1 and colb = :vtg2",
+			BindVariables: map[string]*querypb.BindVariable{
+				"vtg1": sqltypes.Int64BindVariable(1),
+				"vtg2": sqltypes.Int64BindVariable(2),
+			},
+		},
+	}
+	utils.MustMatch(t, wantQueries, sbc1.Queries)
+	require.Nil(t, sbc2.Queries)
+
+	sbc1.Queries = nil
+
+	query = "select * from user_region where cola = 17984 and colb = 1"
+	_, err = executor.Execute(context.Background(),
+		"TestGen4MultiColumnVindex",
+		session,
+		query, map[string]*querypb.BindVariable{},
+	)
+	require.NoError(t, err)
+	wantQueries = []*querypb.BoundQuery{
+		{
+			Sql: "select * from user_region where cola = :vtg1 and colb = :vtg2",
+			BindVariables: map[string]*querypb.BindVariable{
+				"vtg1": sqltypes.Int64BindVariable(17984),
+				"vtg2": sqltypes.Int64BindVariable(1),
+			},
+		},
+	}
+	utils.MustMatch(t, wantQueries, sbc2.Queries)
+	require.Nil(t, sbc1.Queries)
+}
+
+func TestGen4MultiColumnVindexIn(t *testing.T) {
+	executor, sbc1, sbc2, _ := createExecutorEnv()
+	executor.normalize = true
+	*plannerVersion = "gen4"
+	defer func() {
+		// change it back to v3
+		*plannerVersion = "v3"
+	}()
+
+	session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
+	query := "select * from user_region where cola IN (1,17984) and colb IN (2,3,4)"
+	_, err := executor.Execute(context.Background(),
+		"TestGen4MultiColumnVindex",
+		session,
+		query, map[string]*querypb.BindVariable{},
+	)
+	require.NoError(t, err)
+	bv1, _ := sqltypes.BuildBindVariable([]int64{1})
+	bv2, _ := sqltypes.BuildBindVariable([]int64{17984})
+	bvtg1, _ := sqltypes.BuildBindVariable([]int64{1, 17984})
+	bvtg2, _ := sqltypes.BuildBindVariable([]int64{2, 3, 4})
+	wantQueries := []*querypb.BoundQuery{
+		{
+			Sql: "select * from user_region where cola in ::__vals0 and colb in ::__vals1",
+			BindVariables: map[string]*querypb.BindVariable{
+				"__vals0": bv1,
+				"__vals1": bvtg2,
+				"vtg1":    bvtg1,
+				"vtg2":    bvtg2,
+			},
+		},
+	}
+	utils.MustMatch(t, wantQueries, sbc1.Queries)
+	wantQueries = []*querypb.BoundQuery{
+		{
+			Sql: "select * from user_region where cola in ::__vals0 and colb in ::__vals1",
+			BindVariables: map[string]*querypb.BindVariable{
+				"__vals0": bv2,
+				"__vals1": bvtg2,
+				"vtg1":    bvtg1,
+				"vtg2":    bvtg2,
+			},
+		},
+	}
+	utils.MustMatch(t, wantQueries, sbc2.Queries)
+}
+
+func TestGen4MultiColMixedColComparision(t *testing.T) {
+	executor, sbc1, sbc2, _ := createExecutorEnv()
+	executor.normalize = true
+	*plannerVersion = "gen4"
+	defer func() {
+		// change it back to v3
+		*plannerVersion = "v3"
+	}()
+
+	session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
+	query := "select * from user_region where colb = 2 and cola IN (1,17984)"
+	_, err := executor.Execute(context.Background(),
+		"TestGen4MultiColMixedColComparision",
+		session,
+		query, map[string]*querypb.BindVariable{},
+	)
+	require.NoError(t, err)
+	bvtg1 := sqltypes.Int64BindVariable(2)
+	bvtg2, _ := sqltypes.BuildBindVariable([]int64{1, 17984})
+	vals0sbc1, _ := sqltypes.BuildBindVariable([]int64{1})
+	vals0sbc2, _ := sqltypes.BuildBindVariable([]int64{17984})
+	wantQueries := []*querypb.BoundQuery{
+		{
+			Sql: "select * from user_region where colb = :vtg1 and cola in ::__vals0",
+			BindVariables: map[string]*querypb.BindVariable{
+				"__vals0": vals0sbc1,
+				"vtg1":    bvtg1,
+				"vtg2":    bvtg2,
+			},
+		},
+	}
+	utils.MustMatch(t, wantQueries, sbc1.Queries)
+	wantQueries = []*querypb.BoundQuery{
+		{
+			Sql: "select * from user_region where colb = :vtg1 and cola in ::__vals0",
+			BindVariables: map[string]*querypb.BindVariable{
+				"__vals0": vals0sbc2,
+				"vtg1":    bvtg1,
+				"vtg2":    bvtg2,
+			},
+		},
+	}
+	utils.MustMatch(t, wantQueries, sbc2.Queries)
+}
+
+func TestGen4MultiColBestVindexSel(t *testing.T) {
+	executor, sbc1, sbc2, _ := createExecutorEnv()
+	executor.normalize = true
+	*plannerVersion = "gen4"
+	defer func() {
+		// change it back to v3
+		*plannerVersion = "v3"
+	}()
+
+	session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
+	query := "select * from user_region where colb = 2 and cola IN (1,17984) and cola = 1"
+	_, err := executor.Execute(context.Background(),
+		"TestGen4MultiColBestVindexSel",
+		session,
+		query, map[string]*querypb.BindVariable{},
+	)
+	require.NoError(t, err)
+	bvtg2, _ := sqltypes.BuildBindVariable([]int64{1, 17984})
+	wantQueries := []*querypb.BoundQuery{
+		{
+			Sql: "select * from user_region where colb = :vtg1 and cola in ::vtg2 and cola = :vtg3",
+			BindVariables: map[string]*querypb.BindVariable{
+				"vtg1": sqltypes.Int64BindVariable(2),
+				"vtg2": bvtg2,
+				"vtg3": sqltypes.Int64BindVariable(1),
+			},
+		},
+	}
+	utils.MustMatch(t, wantQueries, sbc1.Queries)
+	require.Nil(t, sbc2.Queries)
+
+	// reset
+	sbc1.Queries = nil
+
+	query = "select * from user_region where colb in (10,20) and cola IN (1,17984) and cola = 1 and colb = 2"
+	_, err = executor.Execute(context.Background(),
+		"TestGen4MultiColBestVindexSel",
+		session,
+		query, map[string]*querypb.BindVariable{},
+	)
+	require.NoError(t, err)
+
+	bvtg1, _ := sqltypes.BuildBindVariable([]int64{10, 20})
+	wantQueries = []*querypb.BoundQuery{
+		{
+			Sql: "select * from user_region where colb in ::vtg1 and cola in ::vtg2 and cola = :vtg3 and colb = :vtg4",
+			BindVariables: map[string]*querypb.BindVariable{
+				"vtg1": bvtg1,
+				"vtg2": bvtg2,
+				"vtg3": sqltypes.Int64BindVariable(1),
+				"vtg4": sqltypes.Int64BindVariable(2),
+			},
+		},
+	}
+	utils.MustMatch(t, wantQueries, sbc1.Queries)
+	require.Nil(t, sbc2.Queries)
+}
+
+func TestGen4MultiColMultiEqual(t *testing.T) {
+	executor, sbc1, sbc2, _ := createExecutorEnv()
+	executor.normalize = true
+	*plannerVersion = "gen4"
+	defer func() {
+		// change it back to v3
+		*plannerVersion = "v3"
+	}()
+
+	session := NewSafeSession(&vtgatepb.Session{TargetString: "TestExecutor"})
+	query := "select * from user_region where (cola,colb) in ((17984,2),(17984,3))"
+	_, err := executor.Execute(context.Background(),
+		"TestGen4MultiColMultiEqual",
+		session,
+		query, map[string]*querypb.BindVariable{},
+	)
+	require.NoError(t, err)
+	wantQueries := []*querypb.BoundQuery{
+		{
+			Sql: "select * from user_region where (cola, colb) in ((:vtg1, :vtg2), (:vtg1, :vtg3))",
+			BindVariables: map[string]*querypb.BindVariable{
+				"vtg1": sqltypes.Int64BindVariable(17984),
+				"vtg2": sqltypes.Int64BindVariable(2),
+				"vtg3": sqltypes.Int64BindVariable(3),
+			},
+		},
+	}
+	require.Nil(t, sbc1.Queries)
+	utils.MustMatch(t, wantQueries, sbc2.Queries)
+}
diff --git a/go/vt/vtgate/executor_test.go b/go/vt/vtgate/executor_test.go
index 327dbb65a1..4590994620 100644
--- a/go/vt/vtgate/executor_test.go
+++ b/go/vt/vtgate/executor_test.go
@@ -860,6 +860,7 @@ func TestExecutorShow(t *testing.T) {
 			buildVarCharRow("TestExecutor", "music_user_map", "lookup_hash_unique", "from=music_id; table=music_user_map; to=user_id", "music"),
 			buildVarCharRow("TestExecutor", "name_lastname_keyspace_id_map", "lookup", "from=name,lastname; table=name_lastname_keyspace_id_map; to=keyspace_id", "user2"),
 			buildVarCharRow("TestExecutor", "name_user_map", "lookup_hash", "from=name; table=name_user_map; to=user_id", "user"),
+			buildVarCharRow("TestExecutor", "regional_vdx", "region_experimental", "region_bytes=1", ""),
 			buildVarCharRow("TestExecutor", "t1_lkp_vdx", "consistent_lookup_unique", "from=unq_col; table=t1_lkp_idx; to=keyspace_id", "t1"),
 			buildVarCharRow("TestExecutor", "t2_lu_vdx", "lookup_hash_unique", "from=lu_col; table=TestUnsharded.lu_idx; to=keyspace_id", "t2_wo_lookup"),
 			buildVarCharRow("TestExecutor", "t2_wo_lu_vdx", "lookup_unique", "from=wo_lu_col; table=TestUnsharded.wo_lu_idx; to=keyspace_id; write_only=true", "t2_wo_lookup"),
diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go
index b06cfd193d..9e77bb3042 100644
--- a/go/vt/vtgate/planbuilder/plan_test.go
+++ b/go/vt/vtgate/planbuilder/plan_test.go
@@ -666,7 +666,7 @@ func testFile(t *testing.T, filename, tempDir string, vschema *vschemaWrapper) {
 			testName := fmt.Sprintf("%d Gen4: %s", tcase.lineno, tcase.comments)
 			t.Run(testName, func(t *testing.T) {
 				if out != tcase.output2ndPlanner {
-					t.Errorf("Gen4 - %s:%d\nDiff:\n%s\n[%s] \n[%s]", filename, tcase.lineno, cmp.Diff(tcase.output2ndPlanner, out), tcase.output, out)
+					t.Errorf("Gen4 - %s:%d\nDiff:\n%s\n[%s] \n[%s]", filename, tcase.lineno, cmp.Diff(tcase.output2ndPlanner, out), tcase.output2ndPlanner, out)
 				}
 				if err != nil {
 					out = `"` + out + `"`
diff --git a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
index bba259a3ce..1bf36ef4c1 100644
--- a/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/aggr_cases.txt
@@ -34,7 +34,9 @@ Gen4 plan same as above
     "FieldQuery": "select count(*), col from `user` where 1 != 1",
     "Query": "select count(*), col from `user` where id = 1",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -51,7 +53,9 @@ Gen4 plan same as above
     "FieldQuery": "select count(*), col from `user` where 1 != 1",
     "Query": "select count(*), col from `user` where id = 1",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -826,7 +830,9 @@ Gen4 error: unsupported: '*' expression in cross-shard query
     "FieldQuery": "select id, count(*) as c from `user` where 1 != 1 group by id",
     "Query": "select id, count(*) as c from `user` group by id having id = 1 and c = 10",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -843,7 +849,9 @@ Gen4 error: unsupported: '*' expression in cross-shard query
     "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",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1975,7 +1983,9 @@ Gen4 plan same as above
     "FieldQuery": "select `user`.col1 as a from `user` where 1 != 1 group by a collate utf8_general_ci",
     "Query": "select `user`.col1 as a from `user` where `user`.id = 5 group by a collate utf8_general_ci",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1992,7 +2002,9 @@ Gen4 plan same as above
     "FieldQuery": "select `user`.col1 as a from `user` where 1 != 1 group by a collate utf8_general_ci",
     "Query": "select `user`.col1 as a from `user` where `user`.id = 5 group by a collate utf8_general_ci",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2746,7 +2758,9 @@ Gen4 plan same as above
         "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": "INT64(3)",
+        "Values": [
+          "INT64(3)"
+        ],
         "Vindex": "user_index"
       }
     ]
diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt
index 9f5c179263..6bb9e67db8 100644
--- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt
@@ -51,7 +51,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.id = 5",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -68,7 +70,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.id = 5",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -103,7 +107,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.id = 5 + 5",
     "Table": "`user`",
-    "Values": "INT64(10)",
+    "Values": [
+      "INT64(10)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -123,7 +129,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from music where 1 != 1",
     "Query": "select id from music where id = 5 and user_id = 4",
     "Table": "music",
-    "Values": 4,
+    "Values": [
+      4
+    ],
     "Vindex": "user_index"
   }
 }
@@ -140,7 +148,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from music where 1 != 1",
     "Query": "select id from music where id = 5 and user_id = 4",
     "Table": "music",
-    "Values": "INT64(4)",
+    "Values": [
+      "INT64(4)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -160,7 +170,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where costly = 'aa' and `name` = 'bb'",
     "Table": "`user`",
-    "Values": "bb",
+    "Values": [
+      "bb"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -177,7 +189,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where costly = 'aa' and `name` = 'bb'",
     "Table": "`user`",
-    "Values": "VARBINARY(\"bb\")",
+    "Values": [
+      "VARBINARY(\"bb\")"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -198,8 +212,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where costly in ('aa', 'bb') and `name` in ::__vals",
     "Table": "`user`",
     "Values": [
-      "aa",
-      "bb"
+      [
+        "aa",
+        "bb"
+      ]
     ],
     "Vindex": "name_user_map"
   }
@@ -217,7 +233,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where costly in ('aa', 'bb') and `name` in ::__vals",
     "Table": "`user`",
-    "Values": "(VARBINARY(\"aa\"), VARBINARY(\"bb\"))",
+    "Values": [
+      "(VARBINARY(\"aa\"), VARBINARY(\"bb\"))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -238,8 +256,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where (`name`, col) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
     "Values": [
-      "aa",
-      "cc"
+      [
+        "aa",
+        "cc"
+      ]
     ],
     "Vindex": "name_user_map"
   }
@@ -257,7 +277,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (`name`, col) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
-    "Values": "(VARBINARY(\"aa\"), VARBINARY(\"cc\"))",
+    "Values": [
+      "(VARBINARY(\"aa\"), VARBINARY(\"cc\"))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -278,8 +300,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where (col, `name`) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
     "Values": [
-      "bb",
-      "dd"
+      [
+        "bb",
+        "dd"
+      ]
     ],
     "Vindex": "name_user_map"
   }
@@ -297,7 +321,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (col, `name`) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
-    "Values": "(VARBINARY(\"bb\"), VARBINARY(\"dd\"))",
+    "Values": [
+      "(VARBINARY(\"bb\"), VARBINARY(\"dd\"))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -318,8 +344,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where (costly, `name`) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
     "Values": [
-      "bb",
-      "dd"
+      [
+        "bb",
+        "dd"
+      ]
     ],
     "Vindex": "name_user_map"
   }
@@ -337,7 +365,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (costly, `name`) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
-    "Values": "(VARBINARY(\"bb\"), VARBINARY(\"dd\"))",
+    "Values": [
+      "(VARBINARY(\"bb\"), VARBINARY(\"dd\"))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -358,8 +388,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where (`name`, costly) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
     "Values": [
-      "aa",
-      "cc"
+      [
+        "aa",
+        "cc"
+      ]
     ],
     "Vindex": "name_user_map"
   }
@@ -377,7 +409,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (`name`, costly) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
-    "Values": "(VARBINARY(\"aa\"), VARBINARY(\"cc\"))",
+    "Values": [
+      "(VARBINARY(\"aa\"), VARBINARY(\"cc\"))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -398,7 +432,9 @@ Gen4 plan same as above
     "Query": "select id from `user` where (col, costly) in (('aa', 'bb')) and (col, `name`) in (('cc', 'dd'))",
     "Table": "`user`",
     "Values": [
-      "dd"
+      [
+        "dd"
+      ]
     ],
     "Vindex": "name_user_map"
   }
@@ -416,7 +452,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (col, costly) in (('aa', 'bb')) and (col, `name`) in (('cc', 'dd'))",
     "Table": "`user`",
-    "Values": "(VARBINARY(\"dd\"))",
+    "Values": [
+      "(VARBINARY(\"dd\"))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -436,7 +474,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (col, `name`) in (('aa', 'bb')) and id = 5",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -453,7 +493,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (col, `name`) in (('aa', 'bb')) and id = 5",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -474,8 +516,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where (costly, `name`) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
     "Values": [
-      "bb",
-      "dd"
+      [
+        "bb",
+        "dd"
+      ]
     ],
     "Vindex": "name_user_map"
   }
@@ -493,7 +537,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (costly, `name`) in (('aa', 'bb'), ('cc', 'dd'))",
     "Table": "`user`",
-    "Values": "(VARBINARY(\"bb\"), VARBINARY(\"dd\"))",
+    "Values": [
+      "(VARBINARY(\"bb\"), VARBINARY(\"dd\"))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -514,8 +560,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where ((col1, `name`), col2) in ((('aa', 'bb'), 'cc'), (('dd', 'ee'), 'ff'))",
     "Table": "`user`",
     "Values": [
-      "bb",
-      "ee"
+      [
+        "bb",
+        "ee"
+      ]
     ],
     "Vindex": "name_user_map"
   }
@@ -533,7 +581,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where ((col1, `name`), col2) in ((('aa', 'bb'), 'cc'), (('dd', 'ee'), 'ff'))",
     "Table": "`user`",
-    "Values": "(VARBINARY(\"bb\"), VARBINARY(\"ee\"))",
+    "Values": [
+      "(VARBINARY(\"bb\"), VARBINARY(\"ee\"))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -554,8 +604,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where (`name`, (col1, col2)) in (('aa', ('bb', 'cc')), ('dd', ('ee', 'ff')))",
     "Table": "`user`",
     "Values": [
-      "aa",
-      "dd"
+      [
+        "aa",
+        "dd"
+      ]
     ],
     "Vindex": "name_user_map"
   }
@@ -573,7 +625,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (`name`, (col1, col2)) in (('aa', ('bb', 'cc')), ('dd', ('ee', 'ff')))",
     "Table": "`user`",
-    "Values": "(VARBINARY(\"aa\"), VARBINARY(\"dd\"))",
+    "Values": [
+      "(VARBINARY(\"aa\"), VARBINARY(\"dd\"))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -646,7 +700,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where (col1, `name`) in (('aa', 1 + 1))",
     "Table": "`user`",
-    "Values": "(INT64(2))",
+    "Values": [
+      "(INT64(2))"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -704,7 +760,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `name` = :a",
     "Table": "`user`",
-    "Values": ":a",
+    "Values": [
+      ":a"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -725,7 +783,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `name` = 18446744073709551615",
     "Table": "`user`",
-    "Values": 18446744073709551615,
+    "Values": [
+      18446744073709551615
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -760,7 +820,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `name` in ::__vals",
     "Table": "`user`",
-    "Values": "::list",
+    "Values": [
+      "::list"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -777,7 +839,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `name` in ::__vals",
     "Table": "`user`",
-    "Values": ":list",
+    "Values": [
+      ":list"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -797,7 +861,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.id from `user` join user_extra on `user`.id = user_extra.user_id where 1 != 1",
     "Query": "select user_extra.id from `user` join user_extra on `user`.id = user_extra.user_id where `user`.id = 5",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -814,7 +880,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.id from `user`, user_extra where 1 != 1",
     "Query": "select user_extra.id from `user`, user_extra where `user`.id = 5 and `user`.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -834,7 +902,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.id from `user` join user_extra on `user`.id = user_extra.user_id where 1 != 1",
     "Query": "select user_extra.id from `user` join user_extra on `user`.id = user_extra.user_id where user_extra.user_id = 5",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -851,7 +921,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.id from `user`, user_extra where 1 != 1",
     "Query": "select user_extra.id from `user`, user_extra where user_extra.user_id = 5 and `user`.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -871,7 +943,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.id from `user` left join user_extra on `user`.id = user_extra.user_id where 1 != 1",
     "Query": "select user_extra.id from `user` left join user_extra on `user`.id = user_extra.user_id where `user`.id = 5",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -888,7 +962,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.id from `user` left join user_extra on `user`.id = user_extra.user_id where 1 != 1",
     "Query": "select user_extra.id from `user` left join user_extra on `user`.id = user_extra.user_id where `user`.id = 5",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -908,7 +984,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.id from `user` left join user_extra on `user`.id = user_extra.user_id where 1 != 1",
     "Query": "select user_extra.id from `user` left join user_extra on `user`.id = user_extra.user_id where user_extra.user_id = 5",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -925,7 +1003,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.id from `user`, user_extra where 1 != 1",
     "Query": "select user_extra.id from `user`, user_extra where user_extra.user_id = 5 and `user`.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -954,7 +1034,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select `user`.col from `user` where `user`.id = 5",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       },
       {
@@ -994,7 +1076,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select `user`.col from `user` where `user`.id = 5",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -1036,7 +1120,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select `user`.col from `user` where `user`.id = 5",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       },
       {
@@ -1049,7 +1135,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.id from user_extra where 1 != 1",
         "Query": "select user_extra.id from user_extra where user_extra.col = :user_col and user_extra.user_id = 5",
         "Table": "user_extra",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1068,7 +1156,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.id from `user`, user_extra where 1 != 1",
     "Query": "select user_extra.id from `user`, user_extra where `user`.id = 5 and user_extra.user_id = 5 and `user`.col = user_extra.col",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1108,7 +1198,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.id from user_extra where 1 != 1",
         "Query": "select user_extra.id from user_extra where user_extra.col = :user_col and user_extra.user_id = :user_col",
         "Table": "user_extra",
-        "Values": ":user_col",
+        "Values": [
+          ":user_col"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1148,7 +1240,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.id from user_extra where 1 != 1",
         "Query": "select user_extra.id from user_extra where user_extra.col = :user_col and user_extra.user_id = :user_col",
         "Table": "user_extra",
-        "Values": ":user_col",
+        "Values": [
+          ":user_col"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1249,8 +1343,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where `user`.col = 5 and `user`.id in ::__vals",
     "Table": "`user`",
     "Values": [
-      1,
-      2
+      [
+        1,
+        2
+      ]
     ],
     "Vindex": "user_index"
   }
@@ -1268,7 +1364,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.col = 5 and `user`.id in ::__vals",
     "Table": "`user`",
-    "Values": "(INT64(1), INT64(2))",
+    "Values": [
+      "(INT64(1), INT64(2))"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1289,8 +1387,10 @@ Gen4 plan same as above
     "Query": "select id from `user` where `user`.col = case `user`.col when 'foo' then true else false end and `user`.id in ::__vals",
     "Table": "`user`",
     "Values": [
-      1,
-      2
+      [
+        1,
+        2
+      ]
     ],
     "Vindex": "user_index"
   }
@@ -1308,7 +1408,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.col = case `user`.col when 'foo' then true else false end and `user`.id in ::__vals",
     "Table": "`user`",
-    "Values": "(INT64(1), INT64(2))",
+    "Values": [
+      "(INT64(1), INT64(2))"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1328,7 +1430,9 @@ Gen4 plan same as above
     "FieldQuery": "select id or col as val from `user` where 1 != 1",
     "Query": "select id or col as val from `user` where `user`.col = 5 and `user`.id in (1, 2) and `user`.`name` = 'aa'",
     "Table": "`user`",
-    "Values": "aa",
+    "Values": [
+      "aa"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -1345,7 +1449,9 @@ Gen4 plan same as above
     "FieldQuery": "select id or col as val from `user` where 1 != 1",
     "Query": "select id or col as val from `user` where `user`.col = 5 and `user`.id in (1, 2) and `user`.`name` = 'aa'",
     "Table": "`user`",
-    "Values": "VARBINARY(\"aa\")",
+    "Values": [
+      "VARBINARY(\"aa\")"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -1365,7 +1471,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.col = false and `user`.id in (1, 2) and `user`.`name` = 'aa'",
     "Table": "`user`",
-    "Values": "aa",
+    "Values": [
+      "aa"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -1382,7 +1490,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.col = false and `user`.id in (1, 2) and `user`.`name` = 'aa'",
     "Table": "`user`",
-    "Values": "VARBINARY(\"aa\")",
+    "Values": [
+      "VARBINARY(\"aa\")"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -1402,7 +1512,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.col = 5 and `user`.id in (1, 2) and `user`.`name` = 'aa' and `user`.id = 1",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1419,7 +1531,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.col = 5 and `user`.id in (1, 2) and `user`.`name` = 'aa' and `user`.id = 1",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1439,7 +1553,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.id = 1 and `user`.`name` = 'aa' and `user`.id in (1, 2) and `user`.col = 5",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1456,7 +1572,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.id = 1 and `user`.`name` = 'aa' and `user`.id in (1, 2) and `user`.col = 5",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1553,7 +1671,9 @@ Gen4 plan same as above
         "FieldQuery": "select 1 from `user` where 1 != 1",
         "Query": "select 1 from `user` where `user`.id = :unsharded_id",
         "Table": "`user`",
-        "Values": ":unsharded_id",
+        "Values": [
+          ":unsharded_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1575,7 +1695,9 @@ Gen4 plan same as above
     "FieldQuery": "select col from `user` as route1 where 1 != 1",
     "Query": "select col from `user` as route1 where id = 1",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1592,7 +1714,9 @@ Gen4 plan same as above
     "FieldQuery": "select col from `user` as route1 where 1 != 1",
     "Query": "select col from `user` as route1 where id = 1",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1633,8 +1757,10 @@ Gen4 plan same as above
         "Query": "select u.m from `user` as u where u.id in ::__vals and u.id in (select m2 from `user` where `user`.id = u.id and `user`.col = :user_extra_col)",
         "Table": "`user`",
         "Values": [
-          ":user_extra_col",
-          1
+          [
+            ":user_extra_col",
+            1
+          ]
         ],
         "Vindex": "user_index"
       }
@@ -1675,7 +1801,9 @@ Gen4 plan same as above
         "FieldQuery": "select u.m from `user` as u where 1 != 1",
         "Query": "select u.m from `user` as u where u.id in (select m2 from `user` where `user`.id = u.id and `user`.col = :user_extra_col) and u.id in ::__vals",
         "Table": "`user`",
-        "Values": "(:user_extra_col, INT64(1))",
+        "Values": [
+          "(:user_extra_col, INT64(1))"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1718,8 +1846,10 @@ Gen4 plan same as above
         "Query": "select u.m from `user` as u where u.id in ::__vals and u.id in (select m2 from `user` where `user`.id = u.id)",
         "Table": "`user`",
         "Values": [
-          ":user_extra_col",
-          1
+          [
+            ":user_extra_col",
+            1
+          ]
         ],
         "Vindex": "user_index"
       }
@@ -1760,7 +1890,9 @@ Gen4 plan same as above
         "FieldQuery": "select u.m from `user` as u where 1 != 1",
         "Query": "select u.m from `user` as u where u.id in (select m2 from `user` where `user`.id = u.id) and u.id in ::__vals",
         "Table": "`user`",
-        "Values": "(:user_extra_col, INT64(1))",
+        "Values": [
+          "(:user_extra_col, INT64(1))"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1799,7 +1931,9 @@ Gen4 plan same as above
         "FieldQuery": "select u.m from `user` as u where 1 != 1",
         "Query": "select u.m from `user` as u where u.id = 5 and u.id in (select m2 from `user` where `user`.id = 5)",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1835,7 +1969,9 @@ Gen4 plan same as above
         "FieldQuery": "select u.m from `user` as u where 1 != 1",
         "Query": "select u.m from `user` as u where u.id in (select m2 from `user` where `user`.id = 5) and u.id = 5",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1878,8 +2014,10 @@ Gen4 plan same as above
         "Query": "select u.m from `user` as u where u.id in ::__vals and u.id in (select m2 from `user` where `user`.id = u.id and `user`.col = :user_extra_col and `user`.id in (select m3 from user_extra where user_extra.user_id = `user`.id))",
         "Table": "`user`",
         "Values": [
-          ":user_extra_col",
-          1
+          [
+            ":user_extra_col",
+            1
+          ]
         ],
         "Vindex": "user_index"
       }
@@ -1920,7 +2058,9 @@ Gen4 plan same as above
         "FieldQuery": "select u.m from `user` as u where 1 != 1",
         "Query": "select u.m from `user` as u where u.id in (select m2 from `user` where `user`.id = u.id and `user`.col = :user_extra_col and `user`.id in (select m3 from user_extra where user_extra.user_id = `user`.id)) and u.id in ::__vals",
         "Table": "`user`",
-        "Values": "(:user_extra_col, INT64(1))",
+        "Values": [
+          "(:user_extra_col, INT64(1))"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1961,7 +2101,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where id = 5 and `user`.col in (select user_extra.col from user_extra where user_extra.user_id = 5)",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1978,7 +2120,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where id = 5 and `user`.col in (select user_extra.col from user_extra where user_extra.user_id = 5)",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1998,7 +2142,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where id = 'aa' and `user`.col in (select user_extra.col from user_extra where user_extra.user_id = 'aa')",
     "Table": "`user`",
-    "Values": "aa",
+    "Values": [
+      "aa"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2015,7 +2161,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where id = 'aa' and `user`.col in (select user_extra.col from user_extra where user_extra.user_id = 'aa')",
     "Table": "`user`",
-    "Values": "VARBINARY(\"aa\")",
+    "Values": [
+      "VARBINARY(\"aa\")"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2035,7 +2183,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where id = :a and `user`.col in (select user_extra.col from user_extra where user_extra.user_id = :a)",
     "Table": "`user`",
-    "Values": ":a",
+    "Values": [
+      ":a"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2100,7 +2250,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where :__sq_has_values1 = 1 and id in ::__vals",
         "Table": "`user`",
-        "Values": "::__sq1",
+        "Values": [
+          "::__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -2138,7 +2290,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where :__sq_has_values1 = 1 and id in ::__vals",
         "Table": "`user`",
-        "Values": ":__sq1",
+        "Values": [
+          ":__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -2293,7 +2447,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = :__sq1",
         "Table": "`user`",
-        "Values": ":__sq1",
+        "Values": [
+          ":__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -2356,7 +2512,9 @@ Gen4 plan same as above
         "FieldQuery": "select id1 from `user` where 1 != 1",
         "Query": "select id1 from `user` where id = :__sq2",
         "Table": "`user`",
-        "Values": ":__sq2",
+        "Values": [
+          ":__sq2"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -2415,7 +2573,9 @@ Gen4 plan same as above
         "FieldQuery": "select id1 from `user` where 1 != 1",
         "Query": "select id1 from `user` where id = :__sq1",
         "Table": "`user`",
-        "Values": ":__sq1",
+        "Values": [
+          ":__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -2475,7 +2635,9 @@ Gen4 plan same as above
         "FieldQuery": "select col from `user` where 1 != 1",
         "Query": "select col from `user` where id = :__sq1",
         "Table": "`user`",
-        "Values": ":__sq1",
+        "Values": [
+          ":__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -2498,7 +2660,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.Id from `user` join user_extra on `user`.iD = user_extra.User_Id where 1 != 1",
     "Query": "select user_extra.Id from `user` join user_extra on `user`.iD = user_extra.User_Id where `user`.Id = 5",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2515,7 +2679,9 @@ Gen4 plan same as above
     "FieldQuery": "select user_extra.Id from `user`, user_extra where 1 != 1",
     "Query": "select user_extra.Id from `user`, user_extra where `user`.Id = 5 and `user`.iD = user_extra.User_Id",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2573,7 +2739,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from music where 1 != 1",
     "Query": "select id from music where id is null",
     "Table": "music",
-    "Values": null,
+    "Values": [
+      null
+    ],
     "Vindex": "music_user_map"
   }
 }
@@ -2590,7 +2758,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from music where 1 != 1",
     "Query": "select id from music where id is null",
     "Table": "music",
-    "Values": "NULL",
+    "Values": [
+      "NULL"
+    ],
     "Vindex": "music_user_map"
   }
 }
@@ -2667,7 +2837,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from music where 1 != 1",
     "Query": "select id from music where user_id = 4 and id in (null, 1, 2)",
     "Table": "music",
-    "Values": 4,
+    "Values": [
+      4
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2684,7 +2856,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from music where 1 != 1",
     "Query": "select id from music where user_id = 4 and id in (null, 1, 2)",
     "Table": "music",
-    "Values": "INT64(4)",
+    "Values": [
+      "INT64(4)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2750,7 +2924,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.col from user_extra where 1 != 1",
         "Query": "select user_extra.col from user_extra where user_extra.user_id = 42",
         "Table": "user_extra",
-        "Values": 42,
+        "Values": [
+          42
+        ],
         "Vindex": "user_index"
       },
       {
@@ -2771,7 +2947,9 @@ Gen4 plan same as above
             "FieldQuery": "select user_extra.col from user_extra where 1 != 1",
             "Query": "select user_extra.col from user_extra where user_extra.user_id = 411",
             "Table": "user_extra",
-            "Values": 411,
+            "Values": [
+              411
+            ],
             "Vindex": "user_index"
           },
           {
@@ -2784,7 +2962,9 @@ Gen4 plan same as above
             "FieldQuery": "select id from `user` where 1 != 1",
             "Query": "select id from `user` where :__sq_has_values1 = 1 and id in ::__vals and (:__sq_has_values2 = 0 or id not in ::__sq2)",
             "Table": "`user`",
-            "Values": "::__sq1",
+            "Values": [
+              "::__sq1"
+            ],
             "Vindex": "user_index"
           }
         ]
@@ -2813,7 +2993,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.col from user_extra where 1 != 1",
         "Query": "select user_extra.col from user_extra where user_extra.user_id = 411",
         "Table": "user_extra",
-        "Values": "INT64(411)",
+        "Values": [
+          "INT64(411)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -2834,7 +3016,9 @@ Gen4 plan same as above
             "FieldQuery": "select user_extra.col from user_extra where 1 != 1",
             "Query": "select user_extra.col from user_extra where user_extra.user_id = 42",
             "Table": "user_extra",
-            "Values": "INT64(42)",
+            "Values": [
+              "INT64(42)"
+            ],
             "Vindex": "user_index"
           },
           {
@@ -2847,7 +3031,9 @@ Gen4 plan same as above
             "FieldQuery": "select id from `user` where 1 != 1",
             "Query": "select id from `user` where (:__sq_has_values1 = 0 or id not in ::__sq1) and (:__sq_has_values2 = 1 and id in ::__vals)",
             "Table": "`user`",
-            "Values": ":__sq2",
+            "Values": [
+              ":__sq2"
+            ],
             "Vindex": "user_index"
           }
         ]
@@ -2871,7 +3057,9 @@ Gen4 plan same as above
     "FieldQuery": "select c2 from cfc_vindex_col where 1 != 1",
     "Query": "select c2 from cfc_vindex_col where c1 like 'A%'",
     "Table": "cfc_vindex_col",
-    "Values": "A%",
+    "Values": [
+      "A%"
+    ],
     "Vindex": "cfc"
   }
 }
@@ -2888,7 +3076,9 @@ Gen4 plan same as above
     "FieldQuery": "select c2 from cfc_vindex_col where 1 != 1",
     "Query": "select c2 from cfc_vindex_col where c1 like 'A%'",
     "Table": "cfc_vindex_col",
-    "Values": "VARBINARY(\"A%\")",
+    "Values": [
+      "VARBINARY(\"A%\")"
+    ],
     "Vindex": "cfc"
   }
 }
@@ -2907,7 +3097,9 @@ Gen4 plan same as above
     "FieldQuery": "select col from samecolvin where 1 != 1",
     "Query": "select col from samecolvin where col = :col",
     "Table": "samecolvin",
-    "Values": ":col",
+    "Values": [
+      ":col"
+    ],
     "Vindex": "vindex1"
   }
 }
@@ -2985,7 +3177,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where :__sq_has_values1 = 1 and id in ::__vals",
         "Table": "`user`",
-        "Values": "::__sq1",
+        "Values": [
+          "::__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -3023,7 +3217,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where :__sq_has_values1 = 1 and id in ::__vals",
         "Table": "`user`",
-        "Values": ":__sq1",
+        "Values": [
+          ":__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -3107,7 +3303,9 @@ Gen4 plan same as above
     "FieldQuery": "select u1.col from `user` as u1 where 1 != 1",
     "Query": "select u1.col from `user` as u1 where u1.id = 5 and u1.`name` in (select u2.`name` from `user` as u2 where u2.id = 5)",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -3124,7 +3322,9 @@ Gen4 plan same as above
     "FieldQuery": "select u1.col from `user` as u1 where 1 != 1",
     "Query": "select u1.col from `user` as u1 where u1.id = 5 and u1.`name` in (select u2.`name` from `user` as u2 where u2.id = 5)",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -3144,7 +3344,9 @@ Gen4 plan same as above
     "FieldQuery": "select u1.col from `user` as u1 where 1 != 1",
     "Query": "select u1.col from `user` as u1 where u1.id = 5 and exists (select u2.`name` from `user` as u2 where u2.id = 5)",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -3161,7 +3363,9 @@ Gen4 plan same as above
     "FieldQuery": "select u1.col from `user` as u1 where 1 != 1",
     "Query": "select u1.col from `user` as u1 where u1.id = 5 and exists (select u2.`name` from `user` as u2 where u2.id = 5)",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -3181,7 +3385,9 @@ Gen4 plan same as above
     "FieldQuery": "select u1.col from `user` as u1 where 1 != 1",
     "Query": "select u1.col from `user` as u1 where u1.id = 5 and not exists (select u2.`name` from `user` as u2 where u2.id = 5)",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -3198,7 +3404,9 @@ Gen4 plan same as above
     "FieldQuery": "select u1.col from `user` as u1 where 1 != 1",
     "Query": "select u1.col from `user` as u1 where u1.id = 5 and not exists (select u2.`name` from `user` as u2 where u2.id = 5)",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -3226,7 +3434,9 @@ Gen4 plan same as above
         "FieldQuery": "select u2.`name` from `user` as u2 where 1 != 1",
         "Query": "select u2.`name` from `user` as u2 where u2.id = 5",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       },
       {
@@ -3263,7 +3473,9 @@ Gen4 plan same as above
         "FieldQuery": "select u2.`name` from `user` as u2 where 1 != 1",
         "Query": "select u2.`name` from `user` as u2 where u2.id = 5",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -3304,7 +3516,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.col from user_extra where 1 != 1",
         "Query": "select user_extra.col from user_extra where user_extra.user_id = 4",
         "Table": "user_extra",
-        "Values": 4,
+        "Values": [
+          4
+        ],
         "Vindex": "user_index"
       },
       {
@@ -3317,7 +3531,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 5 and :__sq_has_values1 = 1 and id in ::__sq1 and id not in (select user_extra.col from user_extra where user_extra.user_id = 5)",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -3344,7 +3560,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.col from user_extra where 1 != 1",
         "Query": "select user_extra.col from user_extra where user_extra.user_id = 4",
         "Table": "user_extra",
-        "Values": "INT64(4)",
+        "Values": [
+          "INT64(4)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -3357,7 +3575,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 5 and id not in (select user_extra.col from user_extra where user_extra.user_id = 5) and (:__sq_has_values2 = 1 and id in ::__sq2)",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -3387,7 +3607,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.col from user_extra where 1 != 1",
         "Query": "select user_extra.col from user_extra where user_extra.user_id = 4",
         "Table": "user_extra",
-        "Values": 4,
+        "Values": [
+          4
+        ],
         "Vindex": "user_index"
       },
       {
@@ -3400,7 +3622,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 5 and id in (select user_extra.col from user_extra where user_extra.user_id = 5) and (:__sq_has_values1 = 0 or id not in ::__sq1)",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -3427,7 +3651,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.col from user_extra where 1 != 1",
         "Query": "select user_extra.col from user_extra where user_extra.user_id = 4",
         "Table": "user_extra",
-        "Values": "INT64(4)",
+        "Values": [
+          "INT64(4)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -3440,7 +3666,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 5 and (:__sq_has_values1 = 0 or id not in ::__sq1) and id in (select user_extra.col from user_extra where user_extra.user_id = 5)",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -3510,7 +3738,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from `user` where 1 != 1",
     "Query": "select id from `user` where `user`.id = `user`.col and `user`.col = 5",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -3643,7 +3873,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where `user`.id = :user_extra_col",
         "Table": "`user`",
-        "Values": ":user_extra_col",
+        "Values": [
+          ":user_extra_col"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -3815,7 +4047,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from multicolvin where 1 != 1",
     "Query": "select * from multicolvin where column_b = 1",
     "Table": "multicolvin",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "colb_colc_map"
   }
 }
@@ -3832,7 +4066,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from multicolvin where 1 != 1",
     "Query": "select * from multicolvin where column_b = 1",
     "Table": "multicolvin",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "colb_colc_map"
   }
 }
@@ -3852,7 +4088,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from multicolvin where 1 != 1",
     "Query": "select * from multicolvin where column_b = 1 and column_c = 2",
     "Table": "multicolvin",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "colb_colc_map"
   }
 }
@@ -3869,7 +4107,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from multicolvin where 1 != 1",
     "Query": "select * from multicolvin where column_b = 1 and column_c = 2",
     "Table": "multicolvin",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "colb_colc_map"
   }
 }
@@ -3889,7 +4129,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from multicolvin where 1 != 1",
     "Query": "select * from multicolvin where column_b = 1 and column_c = 2 and column_a = 3",
     "Table": "multicolvin",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "colb_colc_map"
   }
 }
@@ -3906,7 +4148,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from multicolvin where 1 != 1",
     "Query": "select * from multicolvin where column_b = 1 and column_c = 2 and column_a = 3",
     "Table": "multicolvin",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "colb_colc_map"
   }
 }
@@ -3926,7 +4170,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from multicolvin where 1 != 1",
     "Query": "select * from multicolvin where column_a = 3 and column_b = 1",
     "Table": "multicolvin",
-    "Values": 3,
+    "Values": [
+      3
+    ],
     "Vindex": "cola_map"
   }
 }
@@ -3943,12 +4189,14 @@ Gen4 plan same as above
     "FieldQuery": "select * from multicolvin where 1 != 1",
     "Query": "select * from multicolvin where column_a = 3 and column_b = 1",
     "Table": "multicolvin",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "colb_colc_map"
   }
 }
 
-# multi column vindexes are not allowed to be selected as vindex option. This will be Scatter
+# multi column vindex produces Equal plan in gen4 and Scatter in v3
 "select * from multicol_tbl where cola = 1 and colb = 2"
 {
   "QueryType": "SELECT",
@@ -3965,7 +4213,368 @@ Gen4 plan same as above
     "Table": "multicol_tbl"
   }
 }
-Gen4 plan same as above
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where cola = 1 and colb = 2",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectEqualUnique",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where cola = 1 and colb = 2",
+    "Table": "multicol_tbl",
+    "Values": [
+      "INT64(1)",
+      "INT64(2)"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
+
+# multi column vindex with different order places the vindex keys in correct order
+"select * from multicol_tbl where colb = 2 and cola = 1"
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where colb = 2 and cola = 1",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectScatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where colb = 2 and cola = 1",
+    "Table": "multicol_tbl"
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where colb = 2 and cola = 1",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectEqualUnique",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where colb = 2 and cola = 1",
+    "Table": "multicol_tbl",
+    "Values": [
+      "INT64(1)",
+      "INT64(2)"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
+
+# multi column vindex produces IN plan in gen4 and Scatter in v3
+"select * from multicol_tbl where cola in (1,2) and colb in (3,4)"
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where cola in (1,2) and colb in (3,4)",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectScatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where cola in (1, 2) and colb in (3, 4)",
+    "Table": "multicol_tbl"
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where cola in (1,2) and colb in (3,4)",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectIN",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where cola in ::__vals0 and colb in ::__vals1",
+    "Table": "multicol_tbl",
+    "Values": [
+      "(INT64(1), INT64(2))",
+      "(INT64(3), INT64(4))"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
+
+# multi column vindex with different order places the vindex keys in correct order in IN plan in gen4
+"select * from multicol_tbl where colb in (3,4) and cola in (1,2)"
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where colb in (3,4) and cola in (1,2)",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectScatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where colb in (3, 4) and cola in (1, 2)",
+    "Table": "multicol_tbl"
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where colb in (3,4) and cola in (1,2)",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectIN",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where colb in ::__vals1 and cola in ::__vals0",
+    "Table": "multicol_tbl",
+    "Values": [
+      "(INT64(1), INT64(2))",
+      "(INT64(3), INT64(4))"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
+
+# multi column vindex with different order with one IN predicate and one equality
+"select * from multicol_tbl where colb in (3,4) and cola = 1"
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where colb in (3,4) and cola = 1",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectScatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where colb in (3, 4) and cola = 1",
+    "Table": "multicol_tbl"
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where colb in (3,4) and cola = 1",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectIN",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where colb in ::__vals1 and cola = 1",
+    "Table": "multicol_tbl",
+    "Values": [
+      "INT64(1)",
+      "(INT64(3), INT64(4))"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
+
+# multi column vindex with both IN predicate and equality predicate
+"select * from multicol_tbl where cola in (1,10) and cola = 4 and colb in (5,6) and colb = 7"
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where cola in (1,10) and cola = 4 and colb in (5,6) and colb = 7",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectScatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where cola in (1, 10) and cola = 4 and colb in (5, 6) and colb = 7",
+    "Table": "multicol_tbl"
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where cola in (1,10) and cola = 4 and colb in (5,6) and colb = 7",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectEqualUnique",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where cola in (1, 10) and cola = 4 and colb in (5, 6) and colb = 7",
+    "Table": "multicol_tbl",
+    "Values": [
+      "INT64(4)",
+      "INT64(7)"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
+
+# multi column vindex with one column with equal followed by IN predicate, ordering matters for now
+"select * from multicol_tbl where cola = 4 and cola in (1,10) and colb in (5,6)"
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where cola = 4 and cola in (1,10) and colb in (5,6)",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectScatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where cola = 4 and cola in (1, 10) and colb in (5, 6)",
+    "Table": "multicol_tbl"
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where cola = 4 and cola in (1,10) and colb in (5,6)",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectIN",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where cola = 4 and cola in ::__vals0 and colb in ::__vals1",
+    "Table": "multicol_tbl",
+    "Values": [
+      "(INT64(1), INT64(10))",
+      "(INT64(5), INT64(6))"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
+
+# multi column vindex with one column with IN followed by equal predicate, ordering matters for now
+"select * from multicol_tbl where cola in (1,10) and cola = 4 and colb in (5,6)"
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where cola in (1,10) and cola = 4 and colb in (5,6)",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectScatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where cola in (1, 10) and cola = 4 and colb in (5, 6)",
+    "Table": "multicol_tbl"
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where cola in (1,10) and cola = 4 and colb in (5,6)",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectIN",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where cola in (1, 10) and cola = 4 and colb in ::__vals1",
+    "Table": "multicol_tbl",
+    "Values": [
+      "INT64(4)",
+      "(INT64(5), INT64(6))"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
+
+# multi column vindex with better plan selection
+"select * from multicol_tbl where colb in (1,2) and cola IN (3,4) and cola = 5 and colb = 6"
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where colb in (1,2) and cola IN (3,4) and cola = 5 and colb = 6",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectScatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where colb in (1, 2) and cola in (3, 4) and cola = 5 and colb = 6",
+    "Table": "multicol_tbl"
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where colb in (1,2) and cola IN (3,4) and cola = 5 and colb = 6",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectEqualUnique",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where colb in (1, 2) and cola in (3, 4) and cola = 5 and colb = 6",
+    "Table": "multicol_tbl",
+    "Values": [
+      "INT64(5)",
+      "INT64(6)"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
+
+# multi column vindex as tuple
+"select * from multicol_tbl where (cola,colb) in ((1,2),(3,4))"
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where (cola,colb) in ((1,2),(3,4))",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectScatter",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where (cola, colb) in ((1, 2), (3, 4))",
+    "Table": "multicol_tbl"
+  }
+}
+{
+  "QueryType": "SELECT",
+  "Original": "select * from multicol_tbl where (cola,colb) in ((1,2),(3,4))",
+  "Instructions": {
+    "OperatorType": "Route",
+    "Variant": "SelectMultiEqual",
+    "Keyspace": {
+      "Name": "user",
+      "Sharded": true
+    },
+    "FieldQuery": "select * from multicol_tbl where 1 != 1",
+    "Query": "select * from multicol_tbl where (cola, colb) in ((1, 2), (3, 4))",
+    "Table": "multicol_tbl",
+    "Values": [
+      "(INT64(1), INT64(3))",
+      "(INT64(2), INT64(4))"
+    ],
+    "Vindex": "multicolIdx"
+  }
+}
 
 # Multi-route unique vindex constraint (with hash join)
 "select /*vt+ ALLOW_HASH_JOIN */ user_extra.id from user join user_extra on user.col = user_extra.col where user.id = 5"
@@ -3991,7 +4600,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select /*vt+ ALLOW_HASH_JOIN */ `user`.col from `user` where `user`.id = 5",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       },
       {
@@ -4029,7 +4640,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select /*vt+ ALLOW_HASH_JOIN */ `user`.col from `user` where `user`.id = 5",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       },
       {
diff --git a/go/vt/vtgate/planbuilder/testdata/from_cases.txt b/go/vt/vtgate/planbuilder/testdata/from_cases.txt
index 105e673d3c..40b35765a4 100644
--- a/go/vt/vtgate/planbuilder/testdata/from_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/from_cases.txt
@@ -132,7 +132,9 @@ Gen4 error: Incorrect usage/placement of 'NEXT'
         "FieldQuery": "select 1 from `user` where 1 != 1",
         "Query": "select 1 from `user` where :__sq_has_values1 = 1 and id in ::__vals",
         "Table": "`user`",
-        "Values": "::__sq1",
+        "Values": [
+          "::__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -441,7 +443,9 @@ Gen4 plan same as above
         "FieldQuery": "select 1 from `user` where 1 != 1",
         "Query": "select 1 from `user` where `user`.id = :music_id",
         "Table": "`user`",
-        "Values": ":music_id",
+        "Values": [
+          ":music_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -481,7 +485,9 @@ Gen4 plan same as above
         "FieldQuery": "select 1 from `user` where 1 != 1",
         "Query": "select 1 from `user` where `user`.id = :music_id",
         "Table": "`user`",
-        "Values": ":music_id",
+        "Values": [
+          ":music_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1425,7 +1431,9 @@ Gen4 plan same as above
     "FieldQuery": "select `user`.col from `user` join user_extra on `user`.id = 5 and `user`.id = user_extra.user_id where 1 != 1",
     "Query": "select `user`.col from `user` join user_extra on `user`.id = 5 and `user`.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1442,7 +1450,9 @@ Gen4 plan same as above
     "FieldQuery": "select `user`.col from `user`, user_extra where 1 != 1",
     "Query": "select `user`.col from `user`, user_extra where `user`.id = 5 and `user`.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1546,7 +1556,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select `user`.col from `user` where `user`.id = 5",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       },
       {
@@ -1582,7 +1594,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select `user`.col from `user` where `user`.id = 5",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -1621,7 +1635,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select `user`.col from `user` where `user`.id = 5",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       },
       {
@@ -1657,7 +1673,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select `user`.col from `user` where `user`.id = 5",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -1748,7 +1766,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select `user`.col from `user` where `user`.id = :user_extra_col",
         "Table": "`user`",
-        "Values": ":user_extra_col",
+        "Values": [
+          ":user_extra_col"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1790,7 +1810,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col from `user` where 1 != 1",
         "Query": "select `user`.col from `user` where `user`.`name` = :user_extra_user_id",
         "Table": "`user`",
-        "Values": ":user_extra_user_id",
+        "Values": [
+          ":user_extra_user_id"
+        ],
         "Vindex": "name_user_map"
       }
     ]
@@ -1830,7 +1852,9 @@ Gen4 plan same as above
         "FieldQuery": "select 1 from user_extra where 1 != 1",
         "Query": "select 1 from user_extra where user_extra.user_id = :user_name",
         "Table": "user_extra",
-        "Values": ":user_name",
+        "Values": [
+          ":user_name"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1952,7 +1976,9 @@ Gen4 plan same as above
     "FieldQuery": "select ref.col from ref join (select aa from `user` where 1 != 1) as `user` where 1 != 1",
     "Query": "select ref.col from ref join (select aa from `user` where `user`.id = 1) as `user`",
     "Table": "`user`, ref",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1969,7 +1995,9 @@ Gen4 plan same as above
     "FieldQuery": "select ref.col from ref, (select aa from `user` where 1 != 1) as `user` where 1 != 1",
     "Query": "select ref.col from ref, (select aa from `user` where `user`.id = 1) as `user`",
     "Table": "`user`, ref",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2027,7 +2055,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from (select id, col from `user` where 1 != 1) as t where 1 != 1",
     "Query": "select id from (select id, col from `user` where id = 5) as t",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2044,7 +2074,9 @@ Gen4 plan same as above
     "FieldQuery": "select id from (select id, col from `user` where 1 != 1) as t where 1 != 1",
     "Query": "select id from (select id, col from `user` where id = 5) as t",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2064,7 +2096,9 @@ Gen4 plan same as above
     "FieldQuery": "select t.id from (select id from `user` where 1 != 1) as t join user_extra on t.id = user_extra.user_id where 1 != 1",
     "Query": "select t.id from (select id from `user` where id = 5) as t join user_extra on t.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2081,7 +2115,9 @@ Gen4 plan same as above
     "FieldQuery": "select t.id from (select id from `user` where 1 != 1) as t, user_extra where 1 != 1",
     "Query": "select t.id from (select id from `user` where id = 5) as t, user_extra where t.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2101,7 +2137,9 @@ Gen4 plan same as above
     "FieldQuery": "select t.id from (select `user`.id from `user` where 1 != 1) as t join user_extra on t.id = user_extra.user_id where 1 != 1",
     "Query": "select t.id from (select `user`.id from `user` where `user`.id = 5) as t join user_extra on t.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2118,7 +2156,9 @@ Gen4 plan same as above
     "FieldQuery": "select t.id from (select `user`.id from `user` where 1 != 1) as t, user_extra where 1 != 1",
     "Query": "select t.id from (select `user`.id from `user` where `user`.id = 5) as t, user_extra where t.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2158,7 +2198,9 @@ Gen4 error: Duplicate column name 'id'
     "FieldQuery": "select t.id from user_extra, (select id from `user` where 1 != 1) as t where 1 != 1",
     "Query": "select t.id from user_extra, (select id from `user` where id = 5) as t where t.id = user_extra.user_id",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2187,7 +2229,9 @@ Gen4 error: Duplicate column name 'id'
         "FieldQuery": "select t.id from (select id from `user` where 1 != 1) as t where 1 != 1",
         "Query": "select t.id from (select id from `user` where id = 5) as t",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       },
       {
@@ -2227,7 +2271,9 @@ Gen4 error: Duplicate column name 'id'
         "FieldQuery": "select t.id from (select id from `user` where 1 != 1) as t where 1 != 1",
         "Query": "select t.id from (select id from `user` where id = 5) as t",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -2260,7 +2306,9 @@ Gen4 error: Duplicate column name 'id'
     "FieldQuery": "select id from (select id, col from `user` as route1 where 1 != 1) as t where 1 != 1",
     "Query": "select id from (select id, col from `user` as route1 where id = 5) as t",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2277,7 +2325,9 @@ Gen4 error: Duplicate column name 'id'
     "FieldQuery": "select id from (select id, col from `user` as route1 where 1 != 1) as t where 1 != 1",
     "Query": "select id from (select id, col from `user` as route1 where id = 5) as t",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2316,7 +2366,9 @@ Gen4 error: symbol t.col not found
     "FieldQuery": "select id from (select id, col from `user` as route1 where 1 != 1) as t where 1 != 1",
     "Query": "select id from (select id, col from `user` as route1) as t where id = 5",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2333,7 +2385,9 @@ Gen4 error: symbol t.col not found
     "FieldQuery": "select id from (select id, col from `user` as route1 where 1 != 1) as t where 1 != 1",
     "Query": "select id from (select id, col from `user` as route1 where id = 5) as t",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2438,7 +2492,9 @@ Gen4 error: symbol id not found
     "FieldQuery": "select id from (select id from (select id from `user` where 1 != 1) as u where 1 != 1) as t where 1 != 1",
     "Query": "select id from (select id from (select id from `user`) as u) as t where id = 5",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2455,7 +2511,9 @@ Gen4 error: symbol id not found
     "FieldQuery": "select id from (select id from (select id from `user` where 1 != 1) as u where 1 != 1) as t where 1 != 1",
     "Query": "select id from (select id from (select id from `user` where id = 5) as u) as t",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2475,7 +2533,9 @@ Gen4 error: symbol id not found
     "FieldQuery": "select u.col, e.col from (select col from `user` where 1 != 1) as u join (select col from user_extra where 1 != 1) as e where 1 != 1",
     "Query": "select u.col, e.col from (select col from `user` where id = 5) as u join (select col from user_extra where user_id = 5) as e",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2492,7 +2552,9 @@ Gen4 error: symbol id not found
     "FieldQuery": "select u.col, e.col from (select col from `user` where 1 != 1) as u, (select col from user_extra where 1 != 1) as e where 1 != 1",
     "Query": "select u.col, e.col from (select col from `user` where id = 5) as u, (select col from user_extra where user_id = 5) as e",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2918,7 +2980,9 @@ Gen4 plan same as above
                 "FieldQuery": "select `user`.id, `user`.col1 from `user` where 1 != 1",
                 "Query": "select `user`.id, `user`.col1 from `user` where `user`.id = :ua_id",
                 "Table": "`user`",
-                "Values": ":ua_id",
+                "Values": [
+                  ":ua_id"
+                ],
                 "Vindex": "user_index"
               },
               {
@@ -3814,7 +3878,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.id from `user` where 1 != 1",
         "Query": "select `user`.id from `user` where `user`.id = :user_extra_id",
         "Table": "`user`",
-        "Values": ":user_extra_id",
+        "Values": [
+          ":user_extra_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -3856,7 +3922,9 @@ Gen4 plan same as above
         "FieldQuery": "select 1 from user_extra where 1 != 1",
         "Query": "select 1 from user_extra where user_extra.assembly_id = :user_id and user_extra.user_id = 2",
         "Table": "user_extra",
-        "Values": 2,
+        "Values": [
+          2
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -3885,7 +3953,9 @@ Gen4 plan same as above
         "FieldQuery": "select user_extra.assembly_id from user_extra where 1 != 1",
         "Query": "select user_extra.assembly_id from user_extra where user_extra.user_id = 2",
         "Table": "user_extra",
-        "Values": "INT64(2)",
+        "Values": [
+          "INT64(2)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -3898,7 +3968,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.id from `user` where 1 != 1",
         "Query": "select `user`.id from `user` where `user`.id = :user_extra_assembly_id",
         "Table": "`user`",
-        "Values": ":user_extra_assembly_id",
+        "Values": [
+          ":user_extra_assembly_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -4040,7 +4112,9 @@ Gen4 plan same as above
         "FieldQuery": "select 1 from music as m where 1 != 1",
         "Query": "select 1 from music as m where m.user_id = :ue_user_id",
         "Table": "music",
-        "Values": ":ue_user_id",
+        "Values": [
+          ":ue_user_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -4080,7 +4154,9 @@ Gen4 plan same as above
         "FieldQuery": "select 1 from `user` as u where 1 != 1",
         "Query": "select 1 from `user` as u where u.id = :ue_id",
         "Table": "`user`",
-        "Values": ":ue_id",
+        "Values": [
+          ":ue_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -4160,7 +4236,9 @@ Gen4 plan same as above
         "FieldQuery": "select u.id as uid from `user` as u where 1 != 1",
         "Query": "select u.id as uid from `user` as u where u.id = :ue_id",
         "Table": "`user`",
-        "Values": ":ue_id",
+        "Values": [
+          ":ue_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -4277,7 +4355,9 @@ Gen4 plan same as above
             "FieldQuery": "select `user`.id, `user`.col from `user` where 1 != 1",
             "Query": "select `user`.id, `user`.col from `user` where `user`.id = 5",
             "Table": "`user`",
-            "Values": "INT64(5)",
+            "Values": [
+              "INT64(5)"
+            ],
             "Vindex": "user_index"
           },
           {
@@ -4359,7 +4439,9 @@ Gen4 plan same as above
     "FieldQuery": "select u.a from (select id as b, `name` from `user` where 1 != 1) as u(a, n) where 1 != 1",
     "Query": "select u.a from (select id as b, `name` from `user` where `name` = 1) as u(a, n)",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -4380,7 +4462,9 @@ Gen4 plan same as above
     "FieldQuery": "select u.a from (select id as b, `name` from `user` where 1 != 1) as u(a, n) where 1 != 1",
     "Query": "select u.a from (select id as b, `name` from `user` where b = 1 and `name` = 1) as u(a, n)",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "name_user_map"
   }
 }
@@ -4490,7 +4574,9 @@ Gen4 plan same as above
             "FieldQuery": "select id from `user` where 1 != 1",
             "Query": "select id from `user` where col = :__sq1 and :__sq_has_values2 = 1 and id in ::__vals",
             "Table": "`user`",
-            "Values": "::__sq2",
+            "Values": [
+              "::__sq2"
+            ],
             "Vindex": "user_index"
           }
         ]
@@ -4555,7 +4641,9 @@ Gen4 plan same as above
             "FieldQuery": "select id from `user` where 1 != 1",
             "Query": "select id from `user` where :__sq_has_values1 = 1 and id in ::__vals and col = :__sq2",
             "Table": "`user`",
-            "Values": ":__sq1",
+            "Values": [
+              ":__sq1"
+            ],
             "Vindex": "user_index"
           }
         ]
diff --git a/go/vt/vtgate/planbuilder/testdata/large_cases.txt b/go/vt/vtgate/planbuilder/testdata/large_cases.txt
index b5c8be8d96..1a3974a70a 100644
--- a/go/vt/vtgate/planbuilder/testdata/large_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/large_cases.txt
@@ -40,7 +40,9 @@
             "FieldQuery": "select user_extra.user_id from user_extra where 1 != 1",
             "Query": "select user_extra.user_id from user_extra where user_extra.user_id = :user_id",
             "Table": "user_extra",
-            "Values": ":user_id",
+            "Values": [
+              ":user_id"
+            ],
             "Vindex": "user_index"
           },
           {
@@ -58,7 +60,9 @@
                 "FieldQuery": "select 1 from user_metadata where 1 != 1",
                 "Query": "select 1 from user_metadata where user_metadata.user_id = :user_extra_user_id",
                 "Table": "user_metadata",
-                "Values": ":user_extra_user_id",
+                "Values": [
+                  ":user_extra_user_id"
+                ],
                 "Vindex": "user_index"
               },
               {
@@ -157,7 +161,9 @@
                                     "FieldQuery": "select 1 from music_extra where 1 != 1",
                                     "Query": "select 1 from music_extra where music_extra.music_id = :music_id",
                                     "Table": "music_extra",
-                                    "Values": ":music_id",
+                                    "Values": [
+                                      ":music_id"
+                                    ],
                                     "Vindex": "music_user_map"
                                   }
                                 ]
diff --git a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt
index 07469024e3..c1055cd05c 100644
--- a/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/memory_sort_cases.txt
@@ -395,7 +395,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
             "FieldQuery": "select `user`.col1 as a, `user`.col2 as b, `user`.id from `user` where 1 != 1",
             "Query": "select `user`.col1 as a, `user`.col2 as b, `user`.id from `user` where `user`.id = 1",
             "Table": "`user`",
-            "Values": 1,
+            "Values": [
+              1
+            ],
             "Vindex": "user_index"
           },
           {
@@ -408,7 +410,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
             "FieldQuery": "select music.col3 as c, weight_string(music.col3) from music where 1 != 1",
             "Query": "select music.col3 as c, weight_string(music.col3) from music where music.id = :user_id",
             "Table": "music",
-            "Values": ":user_id",
+            "Values": [
+              ":user_id"
+            ],
             "Vindex": "music_user_map"
           }
         ]
@@ -445,7 +449,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
             "FieldQuery": "select `user`.id, `user`.col1 as a, `user`.col2 as b from `user` where 1 != 1",
             "Query": "select `user`.id, `user`.col1 as a, `user`.col2 as b from `user` where `user`.id = 1",
             "Table": "`user`",
-            "Values": "INT64(1)",
+            "Values": [
+              "INT64(1)"
+            ],
             "Vindex": "user_index"
           },
           {
@@ -458,7 +464,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
             "FieldQuery": "select music.col3 as c, weight_string(music.col3) from music where 1 != 1",
             "Query": "select music.col3 as c, weight_string(music.col3) from music where music.id = :user_id",
             "Table": "music",
-            "Values": ":user_id",
+            "Values": [
+              ":user_id"
+            ],
             "Vindex": "music_user_map"
           }
         ]
@@ -497,7 +505,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
             "FieldQuery": "select `user`.col1 as a, `user`.col2, weight_string(`user`.col1), weight_string(`user`.col2), `user`.id from `user` where 1 != 1",
             "Query": "select `user`.col1 as a, `user`.col2, weight_string(`user`.col1), weight_string(`user`.col2), `user`.id from `user` where `user`.id = 1",
             "Table": "`user`",
-            "Values": 1,
+            "Values": [
+              1
+            ],
             "Vindex": "user_index"
           },
           {
@@ -510,7 +520,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
             "FieldQuery": "select music.col3, weight_string(music.col3) from music where 1 != 1",
             "Query": "select music.col3, weight_string(music.col3) from music where music.id = :user_id",
             "Table": "music",
-            "Values": ":user_id",
+            "Values": [
+              ":user_id"
+            ],
             "Vindex": "music_user_map"
           }
         ]
@@ -547,7 +559,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
             "FieldQuery": "select `user`.id, `user`.col1 as a, `user`.col2, weight_string(`user`.col1), weight_string(`user`.col2) from `user` where 1 != 1",
             "Query": "select `user`.id, `user`.col1 as a, `user`.col2, weight_string(`user`.col1), weight_string(`user`.col2) from `user` where `user`.id = 1",
             "Table": "`user`",
-            "Values": "INT64(1)",
+            "Values": [
+              "INT64(1)"
+            ],
             "Vindex": "user_index"
           },
           {
@@ -560,7 +574,9 @@ Gen4 error: Expression of SELECT list is not in GROUP BY clause and contains non
             "FieldQuery": "select music.col3, weight_string(music.col3) from music where 1 != 1",
             "Query": "select music.col3, weight_string(music.col3) from music where music.id = :user_id",
             "Table": "music",
-            "Values": ":user_id",
+            "Values": [
+              ":user_id"
+            ],
             "Vindex": "music_user_map"
           }
         ]
diff --git a/go/vt/vtgate/planbuilder/testdata/oltp_cases.txt b/go/vt/vtgate/planbuilder/testdata/oltp_cases.txt
index 7510afbd53..de1ec57611 100644
--- a/go/vt/vtgate/planbuilder/testdata/oltp_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/oltp_cases.txt
@@ -13,7 +13,9 @@
     "FieldQuery": "select c from sbtest34 where 1 != 1",
     "Query": "select c from sbtest34 where id = 15",
     "Table": "sbtest34",
-    "Values": 15,
+    "Values": [
+      15
+    ],
     "Vindex": "hash"
   }
 }
@@ -30,7 +32,9 @@
     "FieldQuery": "select c from sbtest34 where 1 != 1",
     "Query": "select c from sbtest34 where id = 15",
     "Table": "sbtest34",
-    "Values": "INT64(15)",
+    "Values": [
+      "INT64(15)"
+    ],
     "Vindex": "hash"
   }
 }
diff --git a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
index 056734427f..43126792db 100644
--- a/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/postprocess_cases.txt
@@ -213,7 +213,9 @@ Gen4 error: Column 'col1' in field list is ambiguous
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` having :__sq_has_values1 = 1 and id in ::__vals",
         "Table": "`user`",
-        "Values": "::__sq1",
+        "Values": [
+          "::__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -251,7 +253,9 @@ Gen4 error: Column 'col1' in field list is ambiguous
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where :__sq_has_values1 = 1 and id in ::__vals",
         "Table": "`user`",
-        "Values": ":__sq1",
+        "Values": [
+          ":__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -273,7 +277,9 @@ Gen4 error: Column 'col1' in field list is ambiguous
     "FieldQuery": "select col from `user` where 1 != 1",
     "Query": "select col from `user` where id = 5 order by aa asc",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -290,7 +296,9 @@ Gen4 error: Column 'col1' in field list is ambiguous
     "FieldQuery": "select col from `user` where 1 != 1",
     "Query": "select col from `user` where id = 5 order by aa asc",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -310,7 +318,9 @@ Gen4 error: Column 'col1' in field list is ambiguous
     "FieldQuery": "select col from `user` where 1 != 1",
     "Query": "select col from `user` where id = 1 order by 1 asc",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -327,7 +337,9 @@ Gen4 error: Column 'col1' in field list is ambiguous
     "FieldQuery": "select col from `user` where 1 != 1",
     "Query": "select col from `user` where id = 1 order by col asc",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -667,7 +679,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where 1 != 1",
         "Query": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where `user`.id = 1 order by null",
         "Table": "`user`",
-        "Values": 1,
+        "Values": [
+          1
+        ],
         "Vindex": "user_index"
       },
       {
@@ -680,7 +694,9 @@ Gen4 plan same as above
         "FieldQuery": "select music.col3 from music where 1 != 1",
         "Query": "select music.col3 from music where music.id = :user_id order by null",
         "Table": "music",
-        "Values": ":user_id",
+        "Values": [
+          ":user_id"
+        ],
         "Vindex": "music_user_map"
       }
     ]
@@ -709,7 +725,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.id, `user`.col1 as a, `user`.col2 from `user` where 1 != 1",
         "Query": "select `user`.id, `user`.col1 as a, `user`.col2 from `user` where `user`.id = 1 order by null",
         "Table": "`user`",
-        "Values": "INT64(1)",
+        "Values": [
+          "INT64(1)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -722,7 +740,9 @@ Gen4 plan same as above
         "FieldQuery": "select music.col3 from music where 1 != 1",
         "Query": "select music.col3 from music where music.id = :user_id order by null",
         "Table": "music",
-        "Values": ":user_id",
+        "Values": [
+          ":user_id"
+        ],
         "Vindex": "music_user_map"
       }
     ]
@@ -753,7 +773,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where 1 != 1",
         "Query": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where `user`.id = 1 order by a asc",
         "Table": "`user`",
-        "Values": 1,
+        "Values": [
+          1
+        ],
         "Vindex": "user_index"
       },
       {
@@ -766,7 +788,9 @@ Gen4 plan same as above
         "FieldQuery": "select music.col3 from music where 1 != 1",
         "Query": "select music.col3 from music where music.id = :user_id",
         "Table": "music",
-        "Values": ":user_id",
+        "Values": [
+          ":user_id"
+        ],
         "Vindex": "music_user_map"
       }
     ]
@@ -796,7 +820,9 @@ Gen4 plan same as above
         "OrderBy": "(1|3) ASC",
         "Query": "select `user`.id, `user`.col1 as a, `user`.col2, weight_string(`user`.col1) from `user` where `user`.id = 1 order by a asc",
         "Table": "`user`",
-        "Values": "INT64(1)",
+        "Values": [
+          "INT64(1)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -809,7 +835,9 @@ Gen4 plan same as above
         "FieldQuery": "select music.col3 from music where 1 != 1",
         "Query": "select music.col3 from music where music.id = :user_id",
         "Table": "music",
-        "Values": ":user_id",
+        "Values": [
+          ":user_id"
+        ],
         "Vindex": "music_user_map"
       }
     ]
@@ -840,7 +868,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where 1 != 1",
         "Query": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where `user`.id = 1 order by a asc",
         "Table": "`user`",
-        "Values": 1,
+        "Values": [
+          1
+        ],
         "Vindex": "user_index"
       },
       {
@@ -853,7 +883,9 @@ Gen4 plan same as above
         "FieldQuery": "select music.col3 from music where 1 != 1",
         "Query": "select music.col3 from music where music.id = :user_id",
         "Table": "music",
-        "Values": ":user_id",
+        "Values": [
+          ":user_id"
+        ],
         "Vindex": "music_user_map"
       }
     ]
@@ -883,7 +915,9 @@ Gen4 plan same as above
         "OrderBy": "(1|3) ASC",
         "Query": "select `user`.id, `user`.col1 as a, `user`.col2, weight_string(`user`.col1) from `user` where `user`.id = 1 order by a asc",
         "Table": "`user`",
-        "Values": "INT64(1)",
+        "Values": [
+          "INT64(1)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -896,7 +930,9 @@ Gen4 plan same as above
         "FieldQuery": "select music.col3 from music where 1 != 1",
         "Query": "select music.col3 from music where music.id = :user_id",
         "Table": "music",
-        "Values": ":user_id",
+        "Values": [
+          ":user_id"
+        ],
         "Vindex": "music_user_map"
       }
     ]
@@ -986,7 +1022,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where 1 != 1",
         "Query": "select `user`.col1 as a, `user`.col2, `user`.id from `user` where `user`.id = 1 order by RAND()",
         "Table": "`user`",
-        "Values": 1,
+        "Values": [
+          1
+        ],
         "Vindex": "user_index"
       },
       {
@@ -999,7 +1037,9 @@ Gen4 plan same as above
         "FieldQuery": "select music.col3 from music where 1 != 1",
         "Query": "select music.col3 from music where music.id = :user_id order by RAND()",
         "Table": "music",
-        "Values": ":user_id",
+        "Values": [
+          ":user_id"
+        ],
         "Vindex": "music_user_map"
       }
     ]
@@ -1028,7 +1068,9 @@ Gen4 plan same as above
         "FieldQuery": "select `user`.id, `user`.col1 as a, `user`.col2 from `user` where 1 != 1",
         "Query": "select `user`.id, `user`.col1 as a, `user`.col2 from `user` where `user`.id = 1 order by RAND()",
         "Table": "`user`",
-        "Values": "INT64(1)",
+        "Values": [
+          "INT64(1)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -1041,7 +1083,9 @@ Gen4 plan same as above
         "FieldQuery": "select music.col3 from music where 1 != 1",
         "Query": "select music.col3 from music where music.id = :user_id order by RAND()",
         "Table": "music",
-        "Values": ":user_id",
+        "Values": [
+          ":user_id"
+        ],
         "Vindex": "music_user_map"
       }
     ]
@@ -1103,7 +1147,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by col asc",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1120,7 +1166,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by col asc",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1140,7 +1188,9 @@ Gen4 plan same as above
     "FieldQuery": "select `user`.* from `user` where 1 != 1",
     "Query": "select `user`.* from `user` where id = 5 order by `user`.col asc",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1157,7 +1207,9 @@ Gen4 plan same as above
     "FieldQuery": "select `user`.* from `user` where 1 != 1",
     "Query": "select `user`.* from `user` where id = 5 order by `user`.col asc",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1177,7 +1229,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by `user`.col asc",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1194,7 +1248,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by `user`.col asc",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1326,7 +1382,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by `user`.col collate utf8_general_ci asc",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1343,7 +1401,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by `user`.col collate utf8_general_ci asc",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1363,7 +1423,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by -col1 asc",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1380,7 +1442,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by -col1 asc",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1400,7 +1464,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by concat(col, col1) collate utf8_general_ci desc",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1417,7 +1483,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by concat(col, col1) collate utf8_general_ci desc",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1437,7 +1505,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by id + col collate utf8_general_ci desc",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1454,7 +1524,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 order by id + col collate utf8_general_ci desc",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1474,7 +1546,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` as u join (select user_id from user_extra where 1 != 1) as eu on u.id = eu.user_id where 1 != 1",
     "Query": "select * from `user` as u join (select user_id from user_extra where user_id = 5) as eu on u.id = eu.user_id where u.id = 5 order by eu.user_id asc",
     "Table": "`user`, user_extra",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1491,7 +1565,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` as u, (select user_id from user_extra where 1 != 1) as eu where 1 != 1",
     "Query": "select * from `user` as u, (select user_id from user_extra where user_id = 5) as eu where u.id = 5 and u.id = eu.user_id order by eu.user_id asc",
     "Table": "`user`, user_extra",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1511,7 +1587,9 @@ Gen4 plan same as above
     "FieldQuery": "select col from `user` as route1 where 1 != 1",
     "Query": "select col from `user` as route1 where id = 1 order by col asc",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1528,7 +1606,9 @@ Gen4 plan same as above
     "FieldQuery": "select col from `user` as route1 where 1 != 1",
     "Query": "select col from `user` as route1 where id = 1 order by col asc",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1548,7 +1628,9 @@ Gen4 plan same as above
     "FieldQuery": "select col1 from `user` where 1 != 1",
     "Query": "select col1 from `user` where id = 1 limit 1",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1565,7 +1647,9 @@ Gen4 plan same as above
     "FieldQuery": "select col1 from `user` where 1 != 1",
     "Query": "select col1 from `user` where id = 1 limit 1",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
diff --git a/go/vt/vtgate/planbuilder/testdata/rails_cases.txt b/go/vt/vtgate/planbuilder/testdata/rails_cases.txt
index 2704be90ef..6414bca70b 100644
--- a/go/vt/vtgate/planbuilder/testdata/rails_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/rails_cases.txt
@@ -60,7 +60,9 @@
                     "FieldQuery": "select book6s_order2s.order2_id from book6s_order2s where 1 != 1",
                     "Query": "select book6s_order2s.order2_id from book6s_order2s where book6s_order2s.book6_id = :book6s_id",
                     "Table": "book6s_order2s",
-                    "Values": ":book6s_id",
+                    "Values": [
+                      ":book6s_id"
+                    ],
                     "Vindex": "binary_md5"
                   }
                 ]
@@ -88,7 +90,9 @@
             "FieldQuery": "select 1 from customer2s where 1 != 1",
             "Query": "select 1 from customer2s where customer2s.id = :order2s_customer2_id",
             "Table": "customer2s",
-            "Values": ":order2s_customer2_id",
+            "Values": [
+              ":order2s_customer2_id"
+            ],
             "Vindex": "binary_md5"
           }
         ]
@@ -103,7 +107,9 @@
         "FieldQuery": "select 1 from supplier5s where 1 != 1",
         "Query": "select 1 from supplier5s where supplier5s.id = :book6s_supplier5_id",
         "Table": "supplier5s",
-        "Values": ":book6s_supplier5_id",
+        "Values": [
+          ":book6s_supplier5_id"
+        ],
         "Vindex": "binary_md5"
       }
     ]
@@ -174,7 +180,9 @@
                 "FieldQuery": "select 1 from book6s_order2s where 1 != 1",
                 "Query": "select 1 from book6s_order2s where book6s_order2s.book6_id = :book6s_id and book6s_order2s.order2_id = :order2s_id",
                 "Table": "book6s_order2s",
-                "Values": ":book6s_id",
+                "Values": [
+                  ":book6s_id"
+                ],
                 "Vindex": "binary_md5"
               }
             ]
@@ -189,7 +197,9 @@
             "FieldQuery": "select 1 from supplier5s where 1 != 1",
             "Query": "select 1 from supplier5s where supplier5s.id = :book6s_supplier5_id",
             "Table": "supplier5s",
-            "Values": ":book6s_supplier5_id",
+            "Values": [
+              ":book6s_supplier5_id"
+            ],
             "Vindex": "binary_md5"
           }
         ]
@@ -260,7 +270,9 @@
                     "FieldQuery": "select book6s_order2s.order2_id from book6s_order2s where 1 != 1",
                     "Query": "select /*vt+ ALLOW_HASH_JOIN */ book6s_order2s.order2_id from book6s_order2s where book6s_order2s.book6_id = :book6s_id",
                     "Table": "book6s_order2s",
-                    "Values": ":book6s_id",
+                    "Values": [
+                      ":book6s_id"
+                    ],
                     "Vindex": "binary_md5"
                   }
                 ]
@@ -288,7 +300,9 @@
             "FieldQuery": "select 1 from customer2s where 1 != 1",
             "Query": "select /*vt+ ALLOW_HASH_JOIN */ 1 from customer2s where customer2s.id = :order2s_customer2_id",
             "Table": "customer2s",
-            "Values": ":order2s_customer2_id",
+            "Values": [
+              ":order2s_customer2_id"
+            ],
             "Vindex": "binary_md5"
           }
         ]
@@ -303,7 +317,9 @@
         "FieldQuery": "select 1 from supplier5s where 1 != 1",
         "Query": "select /*vt+ ALLOW_HASH_JOIN */ 1 from supplier5s where supplier5s.id = :book6s_supplier5_id",
         "Table": "supplier5s",
-        "Values": ":book6s_supplier5_id",
+        "Values": [
+          ":book6s_supplier5_id"
+        ],
         "Vindex": "binary_md5"
       }
     ]
@@ -370,7 +386,9 @@
                 "FieldQuery": "select book6s_order2s.order2_id from book6s_order2s where 1 != 1",
                 "Query": "select /*vt+ ALLOW_HASH_JOIN */ book6s_order2s.order2_id from book6s_order2s where book6s_order2s.book6_id = :book6s_id",
                 "Table": "book6s_order2s",
-                "Values": ":book6s_id",
+                "Values": [
+                  ":book6s_id"
+                ],
                 "Vindex": "binary_md5"
               }
             ]
diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.txt b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
index e3884db648..841847137a 100644
--- a/go/vt/vtgate/planbuilder/testdata/select_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/select_cases.txt
@@ -625,7 +625,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from pin_test where 1 != 1",
     "Query": "select * from pin_test",
     "Table": "pin_test",
-    "Values": "\ufffd",
+    "Values": [
+      "\ufffd"
+    ],
     "Vindex": "binary"
   }
 }
@@ -642,7 +644,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from pin_test where 1 != 1",
     "Query": "select * from pin_test",
     "Table": "pin_test",
-    "Values": "VARBINARY(\"\\x80\")",
+    "Values": [
+      "VARBINARY(\"\\x80\")"
+    ],
     "Vindex": "binary"
   }
 }
@@ -1040,7 +1044,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where `name` = 'abc' and id = 4 limit 5",
     "Table": "`user`",
-    "Values": 4,
+    "Values": [
+      4
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1057,7 +1063,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where `name` = 'abc' and id = 4 limit 5",
     "Table": "`user`",
-    "Values": "INT64(4)",
+    "Values": [
+      "INT64(4)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1077,7 +1085,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 4 and `name` = 'abc' limit 5",
     "Table": "`user`",
-    "Values": 4,
+    "Values": [
+      4
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1094,7 +1104,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 4 and `name` = 'abc' limit 5",
     "Table": "`user`",
-    "Values": "INT64(4)",
+    "Values": [
+      "INT64(4)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1114,7 +1126,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 4 and `name` = 'abc' limit 5",
     "Table": "`user`",
-    "Values": 4,
+    "Values": [
+      4
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1131,7 +1145,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 4 and `name` = 'abc' limit 5",
     "Table": "`user`",
-    "Values": "INT64(4)",
+    "Values": [
+      "INT64(4)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1151,7 +1167,9 @@ Gen4 plan same as above
     "FieldQuery": "select user0_.col as col0_ from `user` as user0_ where 1 != 1",
     "Query": "select user0_.col as col0_ from `user` as user0_ where id = 1 order by user0_.col desc limit 2",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1168,7 +1186,9 @@ Gen4 plan same as above
     "FieldQuery": "select user0_.col as col0_ from `user` as user0_ where 1 != 1",
     "Query": "select user0_.col as col0_ from `user` as user0_ where id = 1 order by user0_.col desc limit 2",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1188,7 +1208,9 @@ Gen4 plan same as above
     "FieldQuery": "select user0_.col as col0_ from `user` as user0_ where 1 != 1",
     "Query": "select user0_.col as col0_ from `user` as user0_ where id = 1 order by col0_ desc limit 3",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1205,7 +1227,9 @@ Gen4 plan same as above
     "FieldQuery": "select user0_.col as col0_ from `user` as user0_ where 1 != 1",
     "Query": "select user0_.col as col0_ from `user` as user0_ where id = 1 order by col0_ desc limit 3",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1225,7 +1249,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 1 and `name` = true limit 5",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1242,7 +1268,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 1 and `name` = true limit 5",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1262,7 +1290,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 1 and `name` limit 5",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1279,7 +1309,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 1 and `name` limit 5",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1299,7 +1331,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 and `name` = true limit 5",
     "Table": "`user`",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1316,7 +1350,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from `user` where 1 != 1",
     "Query": "select * from `user` where id = 5 and `name` = true limit 5",
     "Table": "`user`",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1541,7 +1577,9 @@ Gen4 error: symbol t.col not found
     "FieldQuery": "select * from music where 1 != 1 union select * from `user` where 1 != 1",
     "Query": "select * from music where user_id = 1 union select * from `user` where id = 1",
     "Table": "music",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1558,7 +1596,9 @@ Gen4 error: symbol t.col not found
     "FieldQuery": "select * from music where 1 != 1 union select * from `user` where 1 != 1",
     "Query": "select * from music where user_id = 1 union select * from `user` where id = 1",
     "Table": "music",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1578,7 +1618,9 @@ Gen4 error: symbol t.col not found
     "FieldQuery": "select *, :__lastInsertId as `last_insert_id()` from music where 1 != 1 union select * from `user` where 1 != 1",
     "Query": "select *, :__lastInsertId as `last_insert_id()` from music where user_id = 1 union select * from `user` where id = 1",
     "Table": "music",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1595,7 +1637,9 @@ Gen4 error: symbol t.col not found
     "FieldQuery": "select *, :__lastInsertId as `last_insert_id()` from music where 1 != 1 union select * from `user` where 1 != 1",
     "Query": "select *, :__lastInsertId as `last_insert_id()` from music where user_id = 1 union select * from `user` where id = 1",
     "Table": "music",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1839,7 +1883,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from music where 1 != 1",
     "Query": "select * from music where user_id = 1",
     "Table": "music",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1856,7 +1902,9 @@ Gen4 plan same as above
     "FieldQuery": "select * from music where 1 != 1",
     "Query": "select * from music where user_id = 1",
     "Table": "music",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -1970,7 +2018,9 @@ Gen4 plan same as above
         "FieldQuery": "select * from music where 1 != 1",
         "Query": "select * from music where user_id = 1 limit 2",
         "Table": "music",
-        "Values": 1,
+        "Values": [
+          1
+        ],
         "Vindex": "user_index"
       },
       {
@@ -1983,7 +2033,9 @@ Gen4 plan same as above
         "FieldQuery": "select count(*) from music where 1 != 1",
         "Query": "select count(*) from music where user_id = 1",
         "Table": "music",
-        "Values": 1,
+        "Values": [
+          1
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -2005,7 +2057,9 @@ Gen4 plan same as above
         "FieldQuery": "select * from music where 1 != 1",
         "Query": "select * from music where user_id = 1 limit 2",
         "Table": "music",
-        "Values": "INT64(1)",
+        "Values": [
+          "INT64(1)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -2018,7 +2072,9 @@ Gen4 plan same as above
         "FieldQuery": "select count(*) from music where 1 != 1",
         "Query": "select count(*) from music where user_id = 1",
         "Table": "music",
-        "Values": "INT64(1)",
+        "Values": [
+          "INT64(1)"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -2216,7 +2272,9 @@ Gen4 plan same as above
     "FieldQuery": "select (select u.id from `user` as u where 1 != 1), a.id from `user` as a where 1 != 1",
     "Query": "select (select u.id from `user` as u where u.id = 1), a.id from `user` as a where a.id = 1",
     "Table": "`user`",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2233,7 +2291,9 @@ Gen4 plan same as above
     "FieldQuery": "select (select u.id from `user` as u where 1 != 1), a.id from `user` as a where 1 != 1",
     "Query": "select (select u.id from `user` as u where u.id = 1), a.id from `user` as a where a.id = 1",
     "Table": "`user`",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "user_index"
   }
 }
@@ -2784,7 +2844,9 @@ Gen4 plan same as above
         "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": "INT64(3)",
+        "Values": [
+          "INT64(3)"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -2827,7 +2889,9 @@ Gen4 plan same as above
         "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": "INT64(3)",
+        "Values": [
+          "INT64(3)"
+        ],
         "Vindex": "user_index"
       }
     ]
diff --git a/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt b/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt
index d0e93ebb33..556efbfb99 100644
--- a/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/systemtables_cases.txt
@@ -924,7 +924,9 @@ Gen4 plan same as above
         "FieldQuery": "select 1 from `user` where 1 != 1",
         "Query": "select 1 from `user` where `user`.id = :x_id",
         "Table": "`user`",
-        "Values": ":x_id",
+        "Values": [
+          ":x_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -964,7 +966,9 @@ Gen4 plan same as above
         "FieldQuery": "select 1 from `user` where 1 != 1",
         "Query": "select 1 from `user` where `user`.id = :x_id",
         "Table": "`user`",
-        "Values": ":x_id",
+        "Values": [
+          ":x_id"
+        ],
         "Vindex": "user_index"
       }
     ]
diff --git a/go/vt/vtgate/planbuilder/testdata/tpcc_cases.txt b/go/vt/vtgate/planbuilder/testdata/tpcc_cases.txt
index 853485f767..b204702857 100644
--- a/go/vt/vtgate/planbuilder/testdata/tpcc_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/tpcc_cases.txt
@@ -13,7 +13,9 @@
     "FieldQuery": "select c_discount, c_last, c_credit, w_tax from customer1 as c join warehouse1 as w on c_w_id = w_id where 1 != 1",
     "Query": "select c_discount, c_last, c_credit, w_tax from customer1 as c join warehouse1 as w on c_w_id = w_id where w_id = 1 and c_d_id = 15 and c_id = 10",
     "Table": "customer1, warehouse1",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "hash"
   }
 }
@@ -30,7 +32,9 @@
     "FieldQuery": "select c_discount, c_last, c_credit, w_tax from customer1 as c, warehouse1 as w where 1 != 1",
     "Query": "select c_discount, c_last, c_credit, w_tax from customer1 as c, warehouse1 as w where c_d_id = 15 and c_id = 10 and w_id = 1 and c_w_id = w_id",
     "Table": "customer1, warehouse1",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -50,7 +54,9 @@
     "FieldQuery": "select d_next_o_id, d_tax from district1 where 1 != 1",
     "Query": "select d_next_o_id, d_tax from district1 where d_w_id = 15 and d_id = 95 for update",
     "Table": "district1",
-    "Values": 15,
+    "Values": [
+      15
+    ],
     "Vindex": "hash"
   }
 }
@@ -67,7 +73,9 @@
     "FieldQuery": "select d_next_o_id, d_tax from district1 where 1 != 1",
     "Query": "select d_next_o_id, d_tax from district1 where d_w_id = 15 and d_id = 95 for update",
     "Table": "district1",
-    "Values": "INT64(15)",
+    "Values": [
+      "INT64(15)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -151,7 +159,9 @@ Gen4 plan same as above
     "FieldQuery": "select i_price, i_name, i_data from item1 where 1 != 1",
     "Query": "select i_price, i_name, i_data from item1 where i_id = 9654",
     "Table": "item1",
-    "Values": 9654,
+    "Values": [
+      9654
+    ],
     "Vindex": "hash"
   }
 }
@@ -168,7 +178,9 @@ Gen4 plan same as above
     "FieldQuery": "select i_price, i_name, i_data from item1 where 1 != 1",
     "Query": "select i_price, i_name, i_data from item1 where i_id = 9654",
     "Table": "item1",
-    "Values": "INT64(9654)",
+    "Values": [
+      "INT64(9654)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -188,7 +200,9 @@ Gen4 plan same as above
     "FieldQuery": "select s_quantity, s_data, s_dist_01 as s_dist from stock1 where 1 != 1",
     "Query": "select s_quantity, s_data, s_dist_01 as s_dist from stock1 where s_i_id = 2198 and s_w_id = 89 for update",
     "Table": "stock1",
-    "Values": 89,
+    "Values": [
+      89
+    ],
     "Vindex": "hash"
   }
 }
@@ -205,7 +219,9 @@ Gen4 plan same as above
     "FieldQuery": "select s_quantity, s_data, s_dist_01 as s_dist from stock1 where 1 != 1",
     "Query": "select s_quantity, s_data, s_dist_01 as s_dist from stock1 where s_i_id = 2198 and s_w_id = 89 for update",
     "Table": "stock1",
-    "Values": "INT64(89)",
+    "Values": [
+      "INT64(89)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -293,7 +309,9 @@ Gen4 plan same as above
     "FieldQuery": "select w_street_1, w_street_2, w_city, w_state, w_zip, w_name from warehouse1 where 1 != 1",
     "Query": "select w_street_1, w_street_2, w_city, w_state, w_zip, w_name from warehouse1 where w_id = 998",
     "Table": "warehouse1",
-    "Values": 998,
+    "Values": [
+      998
+    ],
     "Vindex": "hash"
   }
 }
@@ -310,7 +328,9 @@ Gen4 plan same as above
     "FieldQuery": "select w_street_1, w_street_2, w_city, w_state, w_zip, w_name from warehouse1 where 1 != 1",
     "Query": "select w_street_1, w_street_2, w_city, w_state, w_zip, w_name from warehouse1 where w_id = 998",
     "Table": "warehouse1",
-    "Values": "INT64(998)",
+    "Values": [
+      "INT64(998)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -354,7 +374,9 @@ Gen4 plan same as above
     "FieldQuery": "select d_street_1, d_street_2, d_city, d_state, d_zip, d_name from district1 where 1 != 1",
     "Query": "select d_street_1, d_street_2, d_city, d_state, d_zip, d_name from district1 where d_w_id = 896 and d_id = 9",
     "Table": "district1",
-    "Values": 896,
+    "Values": [
+      896
+    ],
     "Vindex": "hash"
   }
 }
@@ -371,7 +393,9 @@ Gen4 plan same as above
     "FieldQuery": "select d_street_1, d_street_2, d_city, d_state, d_zip, d_name from district1 where 1 != 1",
     "Query": "select d_street_1, d_street_2, d_city, d_state, d_zip, d_name from district1 where d_w_id = 896 and d_id = 9",
     "Table": "district1",
-    "Values": "INT64(896)",
+    "Values": [
+      "INT64(896)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -391,7 +415,9 @@ Gen4 plan same as above
     "FieldQuery": "select count(c_id) as namecnt from customer1 where 1 != 1",
     "Query": "select count(c_id) as namecnt from customer1 where c_w_id = 5 and c_d_id = 1 and c_last = 'last'",
     "Table": "customer1",
-    "Values": 5,
+    "Values": [
+      5
+    ],
     "Vindex": "hash"
   }
 }
@@ -408,7 +434,9 @@ Gen4 plan same as above
     "FieldQuery": "select count(c_id) as namecnt from customer1 where 1 != 1",
     "Query": "select count(c_id) as namecnt from customer1 where c_w_id = 5 and c_d_id = 1 and c_last = 'last'",
     "Table": "customer1",
-    "Values": "INT64(5)",
+    "Values": [
+      "INT64(5)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -428,7 +456,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_id from customer1 where 1 != 1",
     "Query": "select c_id from customer1 where c_w_id = 8 and c_d_id = 5 and c_last = 'item_last' order by c_first asc",
     "Table": "customer1",
-    "Values": 8,
+    "Values": [
+      8
+    ],
     "Vindex": "hash"
   }
 }
@@ -445,7 +475,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_id from customer1 where 1 != 1",
     "Query": "select c_id from customer1 where c_w_id = 8 and c_d_id = 5 and c_last = 'item_last' order by c_first asc",
     "Table": "customer1",
-    "Values": "INT64(8)",
+    "Values": [
+      "INT64(8)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -465,7 +497,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_first, c_middle, c_last, c_street_1, c_street_2, c_city, c_state, c_zip, c_phone, c_credit, c_credit_lim, c_discount, c_balance, c_ytd_payment, c_since from customer1 where 1 != 1",
     "Query": "select c_first, c_middle, c_last, c_street_1, c_street_2, c_city, c_state, c_zip, c_phone, c_credit, c_credit_lim, c_discount, c_balance, c_ytd_payment, c_since from customer1 where c_w_id = 8965 and c_d_id = 1 and c_id = 9 for update",
     "Table": "customer1",
-    "Values": 8965,
+    "Values": [
+      8965
+    ],
     "Vindex": "hash"
   }
 }
@@ -482,7 +516,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_first, c_middle, c_last, c_street_1, c_street_2, c_city, c_state, c_zip, c_phone, c_credit, c_credit_lim, c_discount, c_balance, c_ytd_payment, c_since from customer1 where 1 != 1",
     "Query": "select c_first, c_middle, c_last, c_street_1, c_street_2, c_city, c_state, c_zip, c_phone, c_credit, c_credit_lim, c_discount, c_balance, c_ytd_payment, c_since from customer1 where c_w_id = 8965 and c_d_id = 1 and c_id = 9 for update",
     "Table": "customer1",
-    "Values": "INT64(8965)",
+    "Values": [
+      "INT64(8965)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -502,7 +538,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_data from customer1 where 1 != 1",
     "Query": "select c_data from customer1 where c_w_id = 32 and c_d_id = 68 and c_id = 5",
     "Table": "customer1",
-    "Values": 32,
+    "Values": [
+      32
+    ],
     "Vindex": "hash"
   }
 }
@@ -519,7 +557,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_data from customer1 where 1 != 1",
     "Query": "select c_data from customer1 where c_w_id = 32 and c_d_id = 68 and c_id = 5",
     "Table": "customer1",
-    "Values": "INT64(32)",
+    "Values": [
+      "INT64(32)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -607,7 +647,9 @@ Gen4 plan same as above
     "FieldQuery": "select count(c_id) as namecnt from customer1 where 1 != 1",
     "Query": "select count(c_id) as namecnt from customer1 where c_w_id = 870 and c_d_id = 780 and c_last = 'last'",
     "Table": "customer1",
-    "Values": 870,
+    "Values": [
+      870
+    ],
     "Vindex": "hash"
   }
 }
@@ -624,7 +666,9 @@ Gen4 plan same as above
     "FieldQuery": "select count(c_id) as namecnt from customer1 where 1 != 1",
     "Query": "select count(c_id) as namecnt from customer1 where c_w_id = 870 and c_d_id = 780 and c_last = 'last'",
     "Table": "customer1",
-    "Values": "INT64(870)",
+    "Values": [
+      "INT64(870)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -644,7 +688,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_balance, c_first, c_middle, c_id from customer1 where 1 != 1",
     "Query": "select c_balance, c_first, c_middle, c_id from customer1 where c_w_id = 840 and c_d_id = 1 and c_last = 'test' order by c_first asc",
     "Table": "customer1",
-    "Values": 840,
+    "Values": [
+      840
+    ],
     "Vindex": "hash"
   }
 }
@@ -661,7 +707,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_balance, c_first, c_middle, c_id from customer1 where 1 != 1",
     "Query": "select c_balance, c_first, c_middle, c_id from customer1 where c_w_id = 840 and c_d_id = 1 and c_last = 'test' order by c_first asc",
     "Table": "customer1",
-    "Values": "INT64(840)",
+    "Values": [
+      "INT64(840)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -681,7 +729,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_balance, c_first, c_middle, c_last from customer1 where 1 != 1",
     "Query": "select c_balance, c_first, c_middle, c_last from customer1 where c_w_id = 15 and c_d_id = 5169 and c_id = 1",
     "Table": "customer1",
-    "Values": 15,
+    "Values": [
+      15
+    ],
     "Vindex": "hash"
   }
 }
@@ -698,7 +748,9 @@ Gen4 plan same as above
     "FieldQuery": "select c_balance, c_first, c_middle, c_last from customer1 where 1 != 1",
     "Query": "select c_balance, c_first, c_middle, c_last from customer1 where c_w_id = 15 and c_d_id = 5169 and c_id = 1",
     "Table": "customer1",
-    "Values": "INT64(15)",
+    "Values": [
+      "INT64(15)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -718,7 +770,9 @@ Gen4 plan same as above
     "FieldQuery": "select o_id, o_carrier_id, o_entry_d from orders1 where 1 != 1",
     "Query": "select o_id, o_carrier_id, o_entry_d from orders1 where o_w_id = 9894 and o_d_id = 3 and o_c_id = 159 order by o_id desc",
     "Table": "orders1",
-    "Values": 9894,
+    "Values": [
+      9894
+    ],
     "Vindex": "hash"
   }
 }
@@ -735,7 +789,9 @@ Gen4 plan same as above
     "FieldQuery": "select o_id, o_carrier_id, o_entry_d from orders1 where 1 != 1",
     "Query": "select o_id, o_carrier_id, o_entry_d from orders1 where o_w_id = 9894 and o_d_id = 3 and o_c_id = 159 order by o_id desc",
     "Table": "orders1",
-    "Values": "INT64(9894)",
+    "Values": [
+      "INT64(9894)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -755,7 +811,9 @@ Gen4 plan same as above
     "FieldQuery": "select ol_i_id, ol_supply_w_id, ol_quantity, ol_amount, ol_delivery_d from order_line1 where 1 != 1",
     "Query": "select ol_i_id, ol_supply_w_id, ol_quantity, ol_amount, ol_delivery_d from order_line1 where ol_w_id = 92 and ol_d_id = 5 and ol_o_id = 1",
     "Table": "order_line1",
-    "Values": 92,
+    "Values": [
+      92
+    ],
     "Vindex": "hash"
   }
 }
@@ -772,7 +830,9 @@ Gen4 plan same as above
     "FieldQuery": "select ol_i_id, ol_supply_w_id, ol_quantity, ol_amount, ol_delivery_d from order_line1 where 1 != 1",
     "Query": "select ol_i_id, ol_supply_w_id, ol_quantity, ol_amount, ol_delivery_d from order_line1 where ol_w_id = 92 and ol_d_id = 5 and ol_o_id = 1",
     "Table": "order_line1",
-    "Values": "INT64(92)",
+    "Values": [
+      "INT64(92)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -792,7 +852,9 @@ Gen4 plan same as above
     "FieldQuery": "select no_o_id from new_orders1 where 1 != 1",
     "Query": "select no_o_id from new_orders1 where no_d_id = 689 and no_w_id = 15 order by no_o_id asc limit 1 for update",
     "Table": "new_orders1",
-    "Values": 15,
+    "Values": [
+      15
+    ],
     "Vindex": "hash"
   }
 }
@@ -809,7 +871,9 @@ Gen4 plan same as above
     "FieldQuery": "select no_o_id from new_orders1 where 1 != 1",
     "Query": "select no_o_id from new_orders1 where no_d_id = 689 and no_w_id = 15 order by no_o_id asc limit 1 for update",
     "Table": "new_orders1",
-    "Values": "INT64(15)",
+    "Values": [
+      "INT64(15)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -853,7 +917,9 @@ Gen4 plan same as above
     "FieldQuery": "select o_c_id from orders1 where 1 != 1",
     "Query": "select o_c_id from orders1 where o_id = 6 and o_d_id = 1983 and o_w_id = 894605",
     "Table": "orders1",
-    "Values": 894605,
+    "Values": [
+      894605
+    ],
     "Vindex": "hash"
   }
 }
@@ -870,7 +936,9 @@ Gen4 plan same as above
     "FieldQuery": "select o_c_id from orders1 where 1 != 1",
     "Query": "select o_c_id from orders1 where o_id = 6 and o_d_id = 1983 and o_w_id = 894605",
     "Table": "orders1",
-    "Values": "INT64(894605)",
+    "Values": [
+      "INT64(894605)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -938,7 +1006,9 @@ Gen4 plan same as above
     "FieldQuery": "select SUM(ol_amount) as sm from order_line1 where 1 != 1",
     "Query": "select SUM(ol_amount) as sm from order_line1 where ol_o_id = 680 and ol_d_id = 201 and ol_w_id = 87",
     "Table": "order_line1",
-    "Values": 87,
+    "Values": [
+      87
+    ],
     "Vindex": "hash"
   }
 }
@@ -955,7 +1025,9 @@ Gen4 plan same as above
     "FieldQuery": "select SUM(ol_amount) as sm from order_line1 where 1 != 1",
     "Query": "select SUM(ol_amount) as sm from order_line1 where ol_o_id = 680 and ol_d_id = 201 and ol_w_id = 87",
     "Table": "order_line1",
-    "Values": "INT64(87)",
+    "Values": [
+      "INT64(87)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -999,7 +1071,9 @@ Gen4 plan same as above
     "FieldQuery": "select d_next_o_id from district1 where 1 != 1",
     "Query": "select d_next_o_id from district1 where d_id = 6 and d_w_id = 21",
     "Table": "district1",
-    "Values": 21,
+    "Values": [
+      21
+    ],
     "Vindex": "hash"
   }
 }
@@ -1016,7 +1090,9 @@ Gen4 plan same as above
     "FieldQuery": "select d_next_o_id from district1 where 1 != 1",
     "Query": "select d_next_o_id from district1 where d_id = 6 and d_w_id = 21",
     "Table": "district1",
-    "Values": "INT64(21)",
+    "Values": [
+      "INT64(21)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -1036,7 +1112,9 @@ Gen4 plan same as above
     "FieldQuery": "select COUNT(distinct s.s_i_id) from stock1 as s join order_line1 as ol on ol.ol_w_id = s.s_w_id and ol.ol_i_id = s.s_i_id where 1 != 1",
     "Query": "select COUNT(distinct s.s_i_id) from stock1 as s join order_line1 as ol on ol.ol_w_id = s.s_w_id and ol.ol_i_id = s.s_i_id where ol.ol_w_id = 12 and ol.ol_d_id = 1908 and ol.ol_o_id \u003c 30 and ol.ol_o_id \u003e= 15 and s.s_w_id = 12 and s.s_quantity \u003c 10",
     "Table": "stock1, order_line1",
-    "Values": 12,
+    "Values": [
+      12
+    ],
     "Vindex": "hash"
   }
 }
@@ -1053,7 +1131,9 @@ Gen4 plan same as above
     "FieldQuery": "select COUNT(distinct s.s_i_id) from stock1 as s, order_line1 as ol where 1 != 1",
     "Query": "select COUNT(distinct s.s_i_id) from stock1 as s, order_line1 as ol where s.s_w_id = 12 and s.s_quantity \u003c 10 and ol.ol_w_id = 12 and ol.ol_d_id = 1908 and ol.ol_o_id \u003c 30 and ol.ol_o_id \u003e= 15 and ol.ol_w_id = s.s_w_id and ol.ol_i_id = s.s_i_id",
     "Table": "order_line1, stock1",
-    "Values": "INT64(12)",
+    "Values": [
+      "INT64(12)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -1073,7 +1153,9 @@ Gen4 plan same as above
     "FieldQuery": "select ol_i_id from order_line1 where 1 != 1",
     "Query": "select distinct ol_i_id from order_line1 where ol_w_id = 1 and ol_d_id = 156 and ol_o_id \u003c 500 and ol_o_id \u003e= 56",
     "Table": "order_line1",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "hash"
   }
 }
@@ -1090,7 +1172,9 @@ Gen4 plan same as above
     "FieldQuery": "select ol_i_id from order_line1 where 1 != 1",
     "Query": "select distinct ol_i_id from order_line1 where ol_w_id = 1 and ol_d_id = 156 and ol_o_id \u003c 500 and ol_o_id \u003e= 56",
     "Table": "order_line1",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -1110,7 +1194,9 @@ Gen4 plan same as above
     "FieldQuery": "select count(*) from stock1 where 1 != 1",
     "Query": "select count(*) from stock1 where s_w_id = 1 and s_i_id = 8 and s_quantity \u003c 1000",
     "Table": "stock1",
-    "Values": 1,
+    "Values": [
+      1
+    ],
     "Vindex": "hash"
   }
 }
@@ -1127,7 +1213,9 @@ Gen4 plan same as above
     "FieldQuery": "select count(*) from stock1 where 1 != 1",
     "Query": "select count(*) from stock1 where s_w_id = 1 and s_i_id = 8 and s_quantity \u003c 1000",
     "Table": "stock1",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "hash"
   }
 }
@@ -1173,7 +1261,9 @@ Gen4 plan same as above
             "FieldQuery": "select 1 from (select o_c_id, o_w_id, o_d_id, count(distinct o_w_id), o_id from orders1 where 1 != 1 group by o_c_id, o_d_id, o_w_id) as t where 1 != 1",
             "Query": "select 1 from (select o_c_id, o_w_id, o_d_id, count(distinct o_w_id), o_id from orders1 where o_w_id = 1 and o_id \u003e 2100 and o_id \u003c 11153 group by o_c_id, o_d_id, o_w_id having count(distinct o_id) \u003e 1 limit 1) as t where t.o_w_id = :o_o_w_id and t.o_d_id = :o_o_d_id and t.o_c_id = :o_o_c_id",
             "Table": "orders1",
-            "Values": 1,
+            "Values": [
+              1
+            ],
             "Vindex": "hash"
           }
         ]
@@ -1194,7 +1284,9 @@ Gen4 plan same as above
     "FieldQuery": "select o.o_id, o.o_d_id from orders1 as o, (select o_c_id, o_w_id, o_d_id, count(distinct o_w_id), o_id from orders1 where 1 != 1) as t where 1 != 1",
     "Query": "select o.o_id, o.o_d_id from orders1 as o, (select o_c_id, o_w_id, o_d_id, count(distinct o_w_id), o_id from orders1 where o_w_id = 1 and o_id \u003e 2100 and o_id \u003c 11153) as t where t.o_w_id = o.o_w_id and t.o_d_id = o.o_d_id and t.o_c_id = o.o_c_id limit 1",
     "Table": "orders1",
-    "Values": "INT64(1)",
+    "Values": [
+      "INT64(1)"
+    ],
     "Vindex": "hash"
   }
 }
diff --git a/go/vt/vtgate/planbuilder/testdata/tpch_cases.txt b/go/vt/vtgate/planbuilder/testdata/tpch_cases.txt
index befead9ddd..c992b6eda1 100644
--- a/go/vt/vtgate/planbuilder/testdata/tpch_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/tpch_cases.txt
@@ -177,7 +177,9 @@ Gen4 error: unsupported: in scatter query: complex aggregate expression
             "FieldQuery": "select total_revenue from revenue0 where 1 != 1",
             "Query": "select total_revenue from revenue0 where supplier_no = :s_suppkey and total_revenue = :__sq1",
             "Table": "revenue0",
-            "Values": ":s_suppkey",
+            "Values": [
+              ":s_suppkey"
+            ],
             "Vindex": "hash"
           }
         ]
@@ -304,7 +306,9 @@ Gen4 error: unsupported: in scatter query: complex aggregate expression
                         "FieldQuery": "select p_brand, p_type, p_size, weight_string(p_brand), weight_string(p_type), weight_string(p_size) from part where 1 != 1",
                         "Query": "select p_brand, p_type, p_size, weight_string(p_brand), weight_string(p_type), weight_string(p_size) from part where p_brand != 'Brand#45' and p_type not like 'MEDIUM POLISHED%' and p_size in (49, 14, 23, 45, 19, 3, 36, 9) and p_partkey = :ps_partkey",
                         "Table": "part",
-                        "Values": ":ps_partkey",
+                        "Values": [
+                          ":ps_partkey"
+                        ],
                         "Vindex": "hash"
                       }
                     ]
@@ -384,7 +388,9 @@ Gen4 error: unsupported: cross-shard correlated subquery
                     "OrderBy": "(3|6) DESC, (2|5) ASC",
                     "Query": "select o_custkey, o_orderkey, o_orderdate, o_totalprice, weight_string(o_orderkey), weight_string(o_orderdate), weight_string(o_totalprice) from orders where :__sq_has_values1 = 1 and o_orderkey in ::__vals order by o_totalprice desc, o_orderdate asc",
                     "Table": "orders",
-                    "Values": ":__sq1",
+                    "Values": [
+                      ":__sq1"
+                    ],
                     "Vindex": "hash"
                   },
                   {
@@ -397,7 +403,9 @@ Gen4 error: unsupported: cross-shard correlated subquery
                     "FieldQuery": "select c_name, c_custkey, weight_string(c_name), weight_string(c_custkey) from customer where 1 != 1",
                     "Query": "select c_name, c_custkey, weight_string(c_name), weight_string(c_custkey) from customer where c_custkey = :o_custkey",
                     "Table": "customer",
-                    "Values": ":o_custkey",
+                    "Values": [
+                      ":o_custkey"
+                    ],
                     "Vindex": "hash"
                   }
                 ]
@@ -412,7 +420,9 @@ Gen4 error: unsupported: cross-shard correlated subquery
                 "FieldQuery": "select sum(l_quantity) from lineitem where 1 != 1",
                 "Query": "select sum(l_quantity) from lineitem where l_orderkey = :o_orderkey",
                 "Table": "lineitem",
-                "Values": ":o_orderkey",
+                "Values": [
+                  ":o_orderkey"
+                ],
                 "Vindex": "lineitem_map"
               }
             ]
diff --git a/go/vt/vtgate/planbuilder/testdata/union_cases.txt b/go/vt/vtgate/planbuilder/testdata/union_cases.txt
index 37dd70e0b4..9625730f27 100644
--- a/go/vt/vtgate/planbuilder/testdata/union_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/union_cases.txt
@@ -94,7 +94,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 1",
         "Table": "`user`",
-        "Values": 1,
+        "Values": [
+          1
+        ],
         "Vindex": "user_index"
       },
       {
@@ -107,7 +109,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 5",
         "Table": "`user`",
-        "Values": 5,
+        "Values": [
+          5
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -129,7 +133,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 1",
         "Table": "`user`",
-        "Values": "INT64(1)",
+        "Values": [
+          "INT64(1)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -142,7 +148,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 5",
         "Table": "`user`",
-        "Values": "INT64(5)",
+        "Values": [
+          "INT64(5)"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -326,7 +334,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1 union select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 1 union select id from `user` where id = 1",
         "Table": "`user`",
-        "Values": 1,
+        "Values": [
+          1
+        ],
         "Vindex": "user_index"
       },
       {
@@ -359,7 +369,9 @@ Gen4 plan same as above
         "FieldQuery": "select id from `user` where 1 != 1 union select id from `user` where 1 != 1",
         "Query": "select id from `user` where id = 1 union select id from `user` where id = 1",
         "Table": "`user`",
-        "Values": "INT64(1)",
+        "Values": [
+          "INT64(1)"
+        ],
         "Vindex": "user_index"
       },
       {
@@ -761,7 +773,9 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
             "FieldQuery": "select * from music where 1 != 1",
             "Query": "select * from music where id = 1",
             "Table": "music",
-            "Values": 1,
+            "Values": [
+              1
+            ],
             "Vindex": "music_user_map"
           },
           {
@@ -774,7 +788,9 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
             "FieldQuery": "select * from `user` where 1 != 1",
             "Query": "select * from `user` where id = 1",
             "Table": "`user`",
-            "Values": 1,
+            "Values": [
+              1
+            ],
             "Vindex": "user_index"
           }
         ]
@@ -801,7 +817,9 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
             "FieldQuery": "select * from music where 1 != 1",
             "Query": "select distinct * from music where id = 1",
             "Table": "music",
-            "Values": "INT64(1)",
+            "Values": [
+              "INT64(1)"
+            ],
             "Vindex": "music_user_map"
           },
           {
@@ -814,7 +832,9 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
             "FieldQuery": "select * from `user` where 1 != 1",
             "Query": "select distinct * from `user` where id = 1",
             "Table": "`user`",
-            "Values": "INT64(1)",
+            "Values": [
+              "INT64(1)"
+            ],
             "Vindex": "user_index"
           }
         ]
@@ -844,7 +864,9 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
             "FieldQuery": "select 1 from music where 1 != 1",
             "Query": "select 1 from music where id = 1",
             "Table": "music",
-            "Values": 1,
+            "Values": [
+              1
+            ],
             "Vindex": "music_user_map"
           },
           {
@@ -857,7 +879,9 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
             "FieldQuery": "select 1 from music where 1 != 1",
             "Query": "select 1 from music where id = 2",
             "Table": "music",
-            "Values": 2,
+            "Values": [
+              2
+            ],
             "Vindex": "music_user_map"
           }
         ]
@@ -884,7 +908,9 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
             "FieldQuery": "select 1 from music where 1 != 1",
             "Query": "select distinct 1 from music where id = 1",
             "Table": "music",
-            "Values": "INT64(1)",
+            "Values": [
+              "INT64(1)"
+            ],
             "Vindex": "music_user_map"
           },
           {
@@ -897,7 +923,9 @@ Gen4 error: nesting of unions at the right-hand side is not yet supported
             "FieldQuery": "select 1 from music where 1 != 1",
             "Query": "select distinct 1 from music where id = 2",
             "Table": "music",
-            "Values": "INT64(2)",
+            "Values": [
+              "INT64(2)"
+            ],
             "Vindex": "music_user_map"
           }
         ]
diff --git a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
index b6a8d78394..9caafc33da 100644
--- a/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/unsupported_cases.txt
@@ -545,7 +545,9 @@ Gen4 error: expr cannot be converted, not supported: *sqlparser.FuncExpr
     "FieldQuery": "select 1 from `user` where 1 != 1",
     "Query": "select 1 from `user` having count(id) = 10 and `name` = 'a'",
     "Table": "`user`",
-    "Values": "a",
+    "Values": [
+      "a"
+    ],
     "Vindex": "name_user_map"
   }
 }
diff --git a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt
index d3e3e6b02c..63417b3d8e 100644
--- a/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt
+++ b/go/vt/vtgate/planbuilder/testdata/wireup_cases.txt
@@ -71,7 +71,9 @@
         "FieldQuery": "select u.id as uid from `user` as u where 1 != 1",
         "Query": "select u.id as uid from `user` as u where u.id = :e_id",
         "Table": "`user`",
-        "Values": ":e_id",
+        "Values": [
+          ":e_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -151,7 +153,9 @@
         "FieldQuery": "select u.id as uid from `user` as u where 1 != 1",
         "Query": "select u.id as uid from `user` as u where u.id = :e_id",
         "Table": "`user`",
-        "Values": ":e_id",
+        "Values": [
+          ":e_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -578,7 +582,9 @@
             "FieldQuery": "select 1 from `user` as u3 where 1 != 1",
             "Query": "select 1 from `user` as u3 where u3.id = :u1_col",
             "Table": "`user`",
-            "Values": ":u1_col",
+            "Values": [
+              ":u1_col"
+            ],
             "Vindex": "user_index"
           }
         ]
@@ -669,7 +675,9 @@
                 "FieldQuery": "select 1 from `user` as u3 where 1 != 1",
                 "Query": "select 1 from `user` as u3 where u3.id = :u1_col",
                 "Table": "`user`",
-                "Values": ":u1_col",
+                "Values": [
+                  ":u1_col"
+                ],
                 "Vindex": "user_index"
               }
             ]
@@ -720,7 +728,9 @@
             "FieldQuery": "select 1 from `user` as u2 where 1 != 1",
             "Query": "select 1 from `user` as u2 where u2.id = :u1_col",
             "Table": "`user`",
-            "Values": ":u1_col",
+            "Values": [
+              ":u1_col"
+            ],
             "Vindex": "user_index"
           },
           {
@@ -733,7 +743,9 @@
             "FieldQuery": "select 1 from `user` as u3 where 1 != 1",
             "Query": "select 1 from `user` as u3 where u3.id = :u1_col",
             "Table": "`user`",
-            "Values": ":u1_col",
+            "Values": [
+              ":u1_col"
+            ],
             "Vindex": "user_index"
           }
         ]
@@ -785,7 +797,9 @@
             "FieldQuery": "select 1 from `user` as u2 where 1 != 1",
             "Query": "select 1 from `user` as u2 where u2.id = :u1_col",
             "Table": "`user`",
-            "Values": ":u1_col",
+            "Values": [
+              ":u1_col"
+            ],
             "Vindex": "user_index"
           }
         ]
@@ -800,7 +814,9 @@
         "FieldQuery": "select 1 from `user` as u3 where 1 != 1",
         "Query": "select 1 from `user` as u3 where u3.id = :u1_col",
         "Table": "`user`",
-        "Values": ":u1_col",
+        "Values": [
+          ":u1_col"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -880,7 +896,9 @@
         "FieldQuery": "select `weird``name`.a from `weird``name` where 1 != 1",
         "Query": "select `weird``name`.a from `weird``name` where `weird``name`.`a``b*c` = :unsharded_id",
         "Table": "`weird``name`",
-        "Values": ":unsharded_id",
+        "Values": [
+          ":unsharded_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -960,7 +978,9 @@
         "FieldQuery": "select 1 from `weird``name` where 1 != 1",
         "Query": "select 1 from `weird``name` where `weird``name`.`a``b*c` = :unsharded_id",
         "Table": "`weird``name`",
-        "Values": ":unsharded_id",
+        "Values": [
+          ":unsharded_id"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1119,7 +1139,9 @@
         "FieldQuery": "select 1 from `user` where 1 != 1",
         "Query": "select 1 from `user` where :__sq_has_values1 = 1 and id in ::__vals",
         "Table": "`user`",
-        "Values": "::__sq1",
+        "Values": [
+          "::__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]
@@ -1186,7 +1208,9 @@
         "FieldQuery": "select 1 from `user` where 1 != 1",
         "Query": "select 1 from `user` where :__sq_has_values1 = 1 and id in ::__vals",
         "Table": "`user`",
-        "Values": ":__sq1",
+        "Values": [
+          ":__sq1"
+        ],
         "Vindex": "user_index"
       }
     ]