Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[12.0] Gen4: Handle single column vindex correctly with multi column #9061

Merged
merged 5 commits into from
Oct 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -442,15 +442,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 @@ -466,51 +457,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 @@ -3268,3 +3268,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