Skip to content

Commit

Permalink
Merge pull request #9060 from planetscale/gen4-singleColVindex-multic…
Browse files Browse the repository at this point in the history
…olumns

Gen4: Handle single column vindex correctly with multi column
  • Loading branch information
harshit-gangal authored Oct 22, 2021
2 parents e8cc6a1 + 0e30dbf commit 1298678
Show file tree
Hide file tree
Showing 4 changed files with 205 additions and 71 deletions.
62 changes: 45 additions & 17 deletions go/vt/vtgate/planbuilder/plan_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,31 +90,31 @@ func newLookupIndex(name string, _ map[string]string) (vindexes.Vindex, error) {

var _ vindexes.Lookup = (*lookupIndex)(nil)

// multiIndex satisfies Lookup, NonUnique.
type multiIndex struct{ name string }

func (v *multiIndex) String() string { return v.name }
func (*multiIndex) Cost() int { return 3 }
func (*multiIndex) IsUnique() bool { return false }
func (*multiIndex) NeedsVCursor() bool { return false }
func (*multiIndex) Verify(vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) {
// nameLkpIndex satisfies Lookup, NonUnique.
type nameLkpIndex struct{ name string }

func (v *nameLkpIndex) String() string { return v.name }
func (*nameLkpIndex) Cost() int { return 3 }
func (*nameLkpIndex) IsUnique() bool { return false }
func (*nameLkpIndex) NeedsVCursor() bool { return false }
func (*nameLkpIndex) Verify(vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) {
return []bool{}, nil
}
func (*multiIndex) Map(cursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) {
func (*nameLkpIndex) Map(cursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) {
return nil, nil
}
func (*multiIndex) Create(vindexes.VCursor, [][]sqltypes.Value, [][]byte, bool) error { return nil }
func (*multiIndex) Delete(vindexes.VCursor, [][]sqltypes.Value, []byte) error { return nil }
func (*multiIndex) Update(vindexes.VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error {
func (*nameLkpIndex) Create(vindexes.VCursor, [][]sqltypes.Value, [][]byte, bool) error { return nil }
func (*nameLkpIndex) Delete(vindexes.VCursor, [][]sqltypes.Value, []byte) error { return nil }
func (*nameLkpIndex) Update(vindexes.VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error {
return nil
}

func newMultiIndex(name string, _ map[string]string) (vindexes.Vindex, error) {
return &multiIndex{name: name}, nil
func newNameLkpIndex(name string, _ map[string]string) (vindexes.Vindex, error) {
return &nameLkpIndex{name: name}, nil
}

var _ vindexes.Vindex = (*multiIndex)(nil)
var _ vindexes.Lookup = (*multiIndex)(nil)
var _ vindexes.Vindex = (*nameLkpIndex)(nil)
var _ vindexes.Lookup = (*nameLkpIndex)(nil)

// costlyIndex satisfies Lookup, NonUnique.
type costlyIndex struct{ name string }
Expand Down Expand Up @@ -142,11 +142,39 @@ func newCostlyIndex(name string, _ map[string]string) (vindexes.Vindex, error) {
var _ vindexes.Vindex = (*costlyIndex)(nil)
var _ vindexes.Lookup = (*costlyIndex)(nil)

// multiColIndex satisfies multi column vindex.
type multiColIndex struct {
name string
}

func newMultiColIndex(name string, _ map[string]string) (vindexes.Vindex, error) {
return &multiColIndex{name: name}, nil
}

var _ vindexes.MultiColumn = (*multiColIndex)(nil)

func (m *multiColIndex) String() string { return m.name }

func (m *multiColIndex) Cost() int { return 1 }

func (m *multiColIndex) IsUnique() bool { return true }

func (m *multiColIndex) NeedsVCursor() bool { return false }

func (m *multiColIndex) Map(vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) {
return nil, nil
}

func (m *multiColIndex) Verify(vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) {
return []bool{}, nil
}

func init() {
vindexes.Register("hash_test", newHashIndex)
vindexes.Register("lookup_test", newLookupIndex)
vindexes.Register("multi", newMultiIndex)
vindexes.Register("name_lkp_test", newNameLkpIndex)
vindexes.Register("costly", newCostlyIndex)
vindexes.Register("multiCol_test", newMultiColIndex)
}

const (
Expand Down
72 changes: 19 additions & 53 deletions go/vt/vtgate/planbuilder/routetree.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,15 +480,6 @@ func (rp *routeTree) hasVindex(column *sqlparser.ColName) bool {
return false
}

func allNotNil(s []sqlparser.Expr) bool {
for _, expr := range s {
if expr == nil {
return false
}
}
return true
}

func (rp *routeTree) haveMatchingVindex(
ctx *planningContext,
node sqlparser.Expr,
Expand All @@ -504,51 +495,26 @@ func (rp *routeTree) haveMatchingVindex(
if !ctx.semTable.DirectDeps(column).IsSolvedBy(v.tableID) {
continue
}
cols := len(v.colVindex.Columns)
for idx, col := range v.colVindex.Columns {
if column.Name.Equal(col) {
if cols == 1 {
// single column vindex - just add the option
routeOpcode := opcode(v.colVindex)
vindex := vfunc(v.colVindex)
v.options = append(v.options, &vindexOption{
values: []sqltypes.PlanValue{value},
valueExprs: []sqlparser.Expr{valueExpr},
predicates: []sqlparser.Expr{node},
opcode: routeOpcode,
foundVindex: vindex,
cost: costFor(vindex, routeOpcode),
ready: true,
})
newVindexFound = true
} else {
// let's first see if we can improve any of the existing options
for _, option := range v.options {
if option.predicates[idx] == nil {
option.values[idx] = value
option.predicates[idx] = node
option.valueExprs[idx] = valueExpr
}
if allNotNil(option.predicates) {
option.opcode = opcode(v.colVindex)
option.foundVindex = vfunc(v.colVindex)
option.cost = costFor(option.foundVindex, option.opcode)
option.ready = true
newVindexFound = true
}
}
// Ignore MultiColumn vindexes for finding matching Vindex.
if _, isSingleCol := v.colVindex.Vindex.(vindexes.SingleColumn); !isSingleCol {
continue
}

newOption := &vindexOption{
values: make([]sqltypes.PlanValue, cols),
valueExprs: make([]sqlparser.Expr, cols),
predicates: make([]sqlparser.Expr, cols),
}
newOption.values[idx] = value
newOption.predicates[idx] = node
newOption.valueExprs[idx] = valueExpr
v.options = append(v.options, newOption)
}
}
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: []sqltypes.PlanValue{value},
valueExprs: []sqlparser.Expr{valueExpr},
predicates: []sqlparser.Expr{node},
opcode: routeOpcode,
foundVindex: vindex,
cost: costFor(vindex, routeOpcode),
ready: true,
})
newVindexFound = true
}
}
return newVindexFound
Expand Down
129 changes: 129 additions & 0 deletions go/vt/vtgate/planbuilder/testdata/filter_cases.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3323,3 +3323,132 @@ Gen4 plan same as above
]
}
}

# should use colb_colc_map as first column of the vindex is present in predicate
"select * from multicolvin where column_b = 1"
{
"QueryType": "SELECT",
"Original": "select * from multicolvin where column_b = 1",
"Instructions": {
"OperatorType": "Route",
"Variant": "SelectEqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select * from multicolvin where 1 != 1",
"Query": "select * from multicolvin where column_b = 1",
"Table": "multicolvin",
"Values": [
1
],
"Vindex": "colb_colc_map"
}
}
Gen4 plan same as above

# should only use first column of the vindex colb_colc_map
"select * from multicolvin where column_b = 1 and column_c = 2"
{
"QueryType": "SELECT",
"Original": "select * from multicolvin where column_b = 1 and column_c = 2",
"Instructions": {
"OperatorType": "Route",
"Variant": "SelectEqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select * from multicolvin where 1 != 1",
"Query": "select * from multicolvin where column_b = 1 and column_c = 2",
"Table": "multicolvin",
"Values": [
1
],
"Vindex": "colb_colc_map"
}
}
Gen4 plan same as above

# uses vindex colb_colc_map
"select * from multicolvin where column_b = 1 and column_c = 2 and column_a = 3"
{
"QueryType": "SELECT",
"Original": "select * from multicolvin where column_b = 1 and column_c = 2 and column_a = 3",
"Instructions": {
"OperatorType": "Route",
"Variant": "SelectEqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"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
],
"Vindex": "colb_colc_map"
}
}
Gen4 plan same as above

# v3 takes cola_map, gen4 takes colb_colc_map, may be based on map key ordering
"select * from multicolvin where column_a = 3 and column_b = 1"
{
"QueryType": "SELECT",
"Original": "select * from multicolvin where column_a = 3 and column_b = 1",
"Instructions": {
"OperatorType": "Route",
"Variant": "SelectEqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select * from multicolvin where 1 != 1",
"Query": "select * from multicolvin where column_a = 3 and column_b = 1",
"Table": "multicolvin",
"Values": [
3
],
"Vindex": "cola_map"
}
}
{
"QueryType": "SELECT",
"Original": "select * from multicolvin where column_a = 3 and column_b = 1",
"Instructions": {
"OperatorType": "Route",
"Variant": "SelectEqualUnique",
"Keyspace": {
"Name": "user",
"Sharded": true
},
"FieldQuery": "select * from multicolvin where 1 != 1",
"Query": "select * from multicolvin where column_a = 3 and column_b = 1",
"Table": "multicolvin",
"Values": [
1
],
"Vindex": "colb_colc_map"
}
}

# multi column vindexes are not allowed to be selected as vindex option. This will be Scatter
"select * from multicol_tbl where cola = 1 and colb = 2"
{
"QueryType": "SELECT",
"Original": "select * from multicol_tbl where cola = 1 and colb = 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 cola = 1 and colb = 2",
"Table": "multicol_tbl"
}
}
Gen4 plan same as above
13 changes: 12 additions & 1 deletion go/vt/vtgate/planbuilder/testdata/schema_test.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"owner": "overlap_vindex"
},
"name_user_map": {
"type": "multi",
"type": "name_lkp_test",
"owner": "user"
},
"email_user_map": {
Expand Down Expand Up @@ -81,6 +81,9 @@
},
"cfc": {
"type": "cfc"
},
"multicolIdx": {
"type": "multiCol_test"
}
},
"tables": {
Expand Down Expand Up @@ -278,6 +281,14 @@
"type": "VARCHAR"
}
]
},
"multicol_tbl": {
"column_vindexes": [
{
"columns": ["cola", "colb"],
"name": "multicolIdx"
}
]
}
}
},
Expand Down

0 comments on commit 1298678

Please sign in to comment.