diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 1f3953573a6..639817e0544 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -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 } @@ -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 ( diff --git a/go/vt/vtgate/planbuilder/routetree.go b/go/vt/vtgate/planbuilder/routetree.go index 0f085fb7414..b9d06be4cbb 100644 --- a/go/vt/vtgate/planbuilder/routetree.go +++ b/go/vt/vtgate/planbuilder/routetree.go @@ -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, @@ -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 diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt index ae778ca4498..71558ff669e 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt @@ -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 diff --git a/go/vt/vtgate/planbuilder/testdata/schema_test.json b/go/vt/vtgate/planbuilder/testdata/schema_test.json index 356fa36e974..b7e1fcdc0f3 100644 --- a/go/vt/vtgate/planbuilder/testdata/schema_test.json +++ b/go/vt/vtgate/planbuilder/testdata/schema_test.json @@ -52,7 +52,7 @@ "owner": "overlap_vindex" }, "name_user_map": { - "type": "multi", + "type": "name_lkp_test", "owner": "user" }, "email_user_map": { @@ -81,6 +81,9 @@ }, "cfc": { "type": "cfc" + }, + "multicolIdx": { + "type": "multiCol_test" } }, "tables": { @@ -278,6 +281,14 @@ "type": "VARCHAR" } ] + }, + "multicol_tbl": { + "column_vindexes": [ + { + "columns": ["cola", "colb"], + "name": "multicolIdx" + } + ] } } },