From 30c34666b3dff9a2c0d5499a95f7ada68706e2a8 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Fri, 22 Oct 2021 12:39:30 +0530 Subject: [PATCH 1/5] handle cases for single column vindex with multi columns to define uniqueness in lookup table Signed-off-by: Harshit Gangal --- go/vt/vtgate/planbuilder/routetree.go | 17 ++- .../planbuilder/testdata/filter_cases.txt | 106 ++++++++++++++++++ 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/go/vt/vtgate/planbuilder/routetree.go b/go/vt/vtgate/planbuilder/routetree.go index 0f085fb7414..2fb64161e3b 100644 --- a/go/vt/vtgate/planbuilder/routetree.go +++ b/go/vt/vtgate/planbuilder/routetree.go @@ -504,10 +504,15 @@ 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 { + cols := v.colVindex.Columns + if _, isSingleCol := v.colVindex.Vindex.(vindexes.SingleColumn); isSingleCol { + cols = v.colVindex.Columns[:1] + } + + numCols := len(cols) + for idx, col := range cols { if column.Name.Equal(col) { - if cols == 1 { + if numCols == 1 { // single column vindex - just add the option routeOpcode := opcode(v.colVindex) vindex := vfunc(v.colVindex) @@ -539,9 +544,9 @@ func (rp *routeTree) haveMatchingVindex( } newOption := &vindexOption{ - values: make([]sqltypes.PlanValue, cols), - valueExprs: make([]sqlparser.Expr, cols), - predicates: make([]sqlparser.Expr, cols), + values: make([]sqltypes.PlanValue, numCols), + valueExprs: make([]sqlparser.Expr, numCols), + predicates: make([]sqlparser.Expr, numCols), } newOption.values[idx] = value newOption.predicates[idx] = node diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt index ae778ca4498..ca8717b9a3b 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt @@ -3323,3 +3323,109 @@ Gen4 plan same as above ] } } + +"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 + +"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 + +"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 + +"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" + } +} From b4b409797ecc7f7c0c08b42d4c56699289a8dc8a Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Fri, 22 Oct 2021 13:05:51 +0530 Subject: [PATCH 2/5] removed support for multi-column vindex selection Signed-off-by: Harshit Gangal --- go/vt/vtgate/planbuilder/routetree.go | 75 +++++++-------------------- 1 file changed, 18 insertions(+), 57 deletions(-) diff --git a/go/vt/vtgate/planbuilder/routetree.go b/go/vt/vtgate/planbuilder/routetree.go index 2fb64161e3b..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,56 +495,26 @@ func (rp *routeTree) haveMatchingVindex( if !ctx.semTable.DirectDeps(column).IsSolvedBy(v.tableID) { continue } - cols := v.colVindex.Columns - if _, isSingleCol := v.colVindex.Vindex.(vindexes.SingleColumn); isSingleCol { - cols = v.colVindex.Columns[:1] + // Ignore MultiColumn vindexes for finding matching Vindex. + if _, isSingleCol := v.colVindex.Vindex.(vindexes.SingleColumn); !isSingleCol { + continue } - numCols := len(cols) - for idx, col := range cols { - if column.Name.Equal(col) { - if numCols == 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 - } - } - - newOption := &vindexOption{ - values: make([]sqltypes.PlanValue, numCols), - valueExprs: make([]sqlparser.Expr, numCols), - predicates: make([]sqlparser.Expr, numCols), - } - 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 From e3b359651efcbdf4a82b5d88bb31b117da58e6fe Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Fri, 22 Oct 2021 11:13:02 +0530 Subject: [PATCH 3/5] renamed multiIndex to nameLkpIndex Signed-off-by: Harshit Gangal --- go/vt/vtgate/planbuilder/plan_test.go | 34 +++++++++---------- .../planbuilder/testdata/schema_test.json | 2 +- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 1f3953573a6..0b434632ba0 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 } @@ -145,7 +145,7 @@ var _ vindexes.Lookup = (*costlyIndex)(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) } diff --git a/go/vt/vtgate/planbuilder/testdata/schema_test.json b/go/vt/vtgate/planbuilder/testdata/schema_test.json index 356fa36e974..5f960d97c5f 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": { From 5a5d726ebab52ff54980c7f829c5947dcb03e0c4 Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Fri, 22 Oct 2021 11:52:57 +0530 Subject: [PATCH 4/5] added multi column vindex to plan test Signed-off-by: Harshit Gangal --- go/vt/vtgate/planbuilder/plan_test.go | 32 +++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index 0b434632ba0..ee7efe16be9 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -142,11 +142,43 @@ 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 { + panic("implement me") +} + +func (m *multiColIndex) NeedsVCursor() bool { + panic("implement me") +} + +func (m *multiColIndex) Map(vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { + panic("implement me") +} + +func (m *multiColIndex) Verify(vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { + panic("implement me") +} + func init() { vindexes.Register("hash_test", newHashIndex) vindexes.Register("lookup_test", newLookupIndex) vindexes.Register("name_lkp_test", newNameLkpIndex) vindexes.Register("costly", newCostlyIndex) + vindexes.Register("multiCol", newMultiColIndex) } const ( From 0e30dbfc7dd2e524745eb2d7df8f4bc295abebbd Mon Sep 17 00:00:00 2001 From: Harshit Gangal Date: Fri, 22 Oct 2021 13:21:51 +0530 Subject: [PATCH 5/5] added multi column vindex to plan tests Signed-off-by: Harshit Gangal --- go/vt/vtgate/planbuilder/plan_test.go | 14 ++++------- .../planbuilder/testdata/filter_cases.txt | 23 +++++++++++++++++++ .../planbuilder/testdata/schema_test.json | 11 +++++++++ 3 files changed, 39 insertions(+), 9 deletions(-) diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index ee7efe16be9..639817e0544 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -157,20 +157,16 @@ func (m *multiColIndex) String() string { return m.name } func (m *multiColIndex) Cost() int { return 1 } -func (m *multiColIndex) IsUnique() bool { - panic("implement me") -} +func (m *multiColIndex) IsUnique() bool { return true } -func (m *multiColIndex) NeedsVCursor() bool { - panic("implement me") -} +func (m *multiColIndex) NeedsVCursor() bool { return false } func (m *multiColIndex) Map(vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { - panic("implement me") + return nil, nil } func (m *multiColIndex) Verify(vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { - panic("implement me") + return []bool{}, nil } func init() { @@ -178,7 +174,7 @@ func init() { vindexes.Register("lookup_test", newLookupIndex) vindexes.Register("name_lkp_test", newNameLkpIndex) vindexes.Register("costly", newCostlyIndex) - vindexes.Register("multiCol", newMultiColIndex) + vindexes.Register("multiCol_test", newMultiColIndex) } const ( diff --git a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt index ca8717b9a3b..71558ff669e 100644 --- a/go/vt/vtgate/planbuilder/testdata/filter_cases.txt +++ b/go/vt/vtgate/planbuilder/testdata/filter_cases.txt @@ -3324,6 +3324,7 @@ 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", @@ -3346,6 +3347,7 @@ Gen4 plan same as above } 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", @@ -3368,6 +3370,7 @@ Gen4 plan same as above } 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", @@ -3390,6 +3393,7 @@ Gen4 plan same as above } 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", @@ -3429,3 +3433,22 @@ Gen4 plan same as above "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 5f960d97c5f..b7e1fcdc0f3 100644 --- a/go/vt/vtgate/planbuilder/testdata/schema_test.json +++ b/go/vt/vtgate/planbuilder/testdata/schema_test.json @@ -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" + } + ] } } },