diff --git a/go/vt/vtgate/planbuilder/operators/dml_planning.go b/go/vt/vtgate/planbuilder/operators/dml_planning.go index 4fc3965e112..5a875b78f36 100644 --- a/go/vt/vtgate/planbuilder/operators/dml_planning.go +++ b/go/vt/vtgate/planbuilder/operators/dml_planning.go @@ -45,9 +45,9 @@ func getVindexInformation( var vindexesAndPredicates []*VindexPlusPredicates for _, colVindex := range table.Ordered { - if lu, isLu := colVindex.Vindex.(vindexes.LookupBackfill); isLu && lu.IsBackfilling() { - // Checking if the Vindex is currently backfilling or not, if it isn't we can read from the vindex table - // and we will be able to do a delete equal. Otherwise, we continue to look for next best vindex. + // Checking if the Vindex is currently backfilling or not, if it isn't we can read from the vindex table, + // and we will be able to do a delete equal. Otherwise, we continue to look for next best vindex. + if colVindex.IsBackfilling() { continue } diff --git a/go/vt/vtgate/planbuilder/operators/route.go b/go/vt/vtgate/planbuilder/operators/route.go index 46d9515b707..1c1772220f0 100644 --- a/go/vt/vtgate/planbuilder/operators/route.go +++ b/go/vt/vtgate/planbuilder/operators/route.go @@ -25,7 +25,6 @@ import ( "vitess.io/vitess/go/vt/vtgate/evalengine" "vitess.io/vitess/go/vt/vtgate/planbuilder/operators/ops" "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" - "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" ) @@ -795,6 +794,12 @@ func createRouteFromVSchemaTable( } for _, columnVindex := range vschemaTable.ColumnVindexes { + // Checking if the Vindex is currently backfilling or not, if it isn't we can read from the vindex table + // Otherwise, we ignore this vindex for selection. + if columnVindex.IsBackfilling() { + continue + } + plan.VindexPreds = append(plan.VindexPreds, &VindexPlusPredicates{ColVindex: columnVindex, TableID: solves}) } diff --git a/go/vt/vtgate/planbuilder/plan_test.go b/go/vt/vtgate/planbuilder/plan_test.go index e492abfae5a..283b8874b0d 100644 --- a/go/vt/vtgate/planbuilder/plan_test.go +++ b/go/vt/vtgate/planbuilder/plan_test.go @@ -18,7 +18,6 @@ package planbuilder import ( "bytes" - "context" "encoding/json" "fmt" "math/rand" @@ -31,185 +30,22 @@ import ( "github.com/nsf/jsondiff" "github.com/stretchr/testify/require" - "vitess.io/vitess/go/vt/servenv" - - vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" - - "vitess.io/vitess/go/test/utils" - vschemapb "vitess.io/vitess/go/vt/proto/vschema" - "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" - "vitess.io/vitess/go/mysql/collations" - "vitess.io/vitess/go/vt/vtgate/semantics" - - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/key" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" + vschemapb "vitess.io/vitess/go/vt/proto/vschema" + "vitess.io/vitess/go/vt/servenv" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo/topoproto" + "vitess.io/vitess/go/vt/vterrors" "vitess.io/vitess/go/vt/vtgate/engine" + "vitess.io/vitess/go/vt/vtgate/planbuilder/plancontext" + "vitess.io/vitess/go/vt/vtgate/semantics" "vitess.io/vitess/go/vt/vtgate/vindexes" - - topodatapb "vitess.io/vitess/go/vt/proto/topodata" ) -// hashIndex is a functional, unique Vindex. -type hashIndex struct{ name string } - -func (v *hashIndex) String() string { return v.name } -func (*hashIndex) Cost() int { return 1 } -func (*hashIndex) IsUnique() bool { return true } -func (*hashIndex) NeedsVCursor() bool { return false } -func (*hashIndex) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { - return []bool{}, nil -} -func (*hashIndex) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { - return nil, nil -} - -func newHashIndex(name string, _ map[string]string) (vindexes.Vindex, error) { - return &hashIndex{name: name}, nil -} - -// lookupIndex is a unique Vindex, and satisfies Lookup. -type lookupIndex struct{ name string } - -func (v *lookupIndex) String() string { return v.name } -func (*lookupIndex) Cost() int { return 2 } -func (*lookupIndex) IsUnique() bool { return true } -func (*lookupIndex) NeedsVCursor() bool { return false } -func (*lookupIndex) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { - return []bool{}, nil -} -func (*lookupIndex) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { - return nil, nil -} -func (*lookupIndex) Create(context.Context, vindexes.VCursor, [][]sqltypes.Value, [][]byte, bool) error { - return nil -} -func (*lookupIndex) Delete(context.Context, vindexes.VCursor, [][]sqltypes.Value, []byte) error { - return nil -} -func (*lookupIndex) Update(context.Context, vindexes.VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { - return nil -} - -func newLookupIndex(name string, _ map[string]string) (vindexes.Vindex, error) { - return &lookupIndex{name: name}, nil -} - -var _ vindexes.Lookup = (*lookupIndex)(nil) - -// 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) AllowBatch() bool { return true } -func (*nameLkpIndex) AutoCommitEnabled() bool { return false } -func (*nameLkpIndex) GetCommitOrder() vtgatepb.CommitOrder { return vtgatepb.CommitOrder_NORMAL } -func (*nameLkpIndex) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { - return []bool{}, nil -} -func (*nameLkpIndex) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { - return nil, nil -} -func (*nameLkpIndex) Create(context.Context, vindexes.VCursor, [][]sqltypes.Value, [][]byte, bool) error { - return nil -} -func (*nameLkpIndex) Delete(context.Context, vindexes.VCursor, [][]sqltypes.Value, []byte) error { - return nil -} -func (*nameLkpIndex) Update(context.Context, vindexes.VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { - return nil -} -func (v *nameLkpIndex) Query() (string, []string) { - return "select name, keyspace_id from name_user_vdx where name in ::name", []string{"name"} -} -func (*nameLkpIndex) MapResult([]sqltypes.Value, []*sqltypes.Result) ([]key.Destination, error) { - return nil, nil -} - -func newNameLkpIndex(name string, _ map[string]string) (vindexes.Vindex, error) { - return &nameLkpIndex{name: name}, nil -} - -var _ vindexes.Vindex = (*nameLkpIndex)(nil) -var _ vindexes.Lookup = (*nameLkpIndex)(nil) -var _ vindexes.LookupPlanable = (*nameLkpIndex)(nil) - -// costlyIndex satisfies Lookup, NonUnique. -type costlyIndex struct{ name string } - -func (v *costlyIndex) String() string { return v.name } -func (*costlyIndex) Cost() int { return 10 } -func (*costlyIndex) IsUnique() bool { return false } -func (*costlyIndex) NeedsVCursor() bool { return false } -func (*costlyIndex) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { - return []bool{}, nil -} -func (*costlyIndex) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { - return nil, nil -} -func (*costlyIndex) Create(context.Context, vindexes.VCursor, [][]sqltypes.Value, [][]byte, bool) error { - return nil -} -func (*costlyIndex) Delete(context.Context, vindexes.VCursor, [][]sqltypes.Value, []byte) error { - return nil -} -func (*costlyIndex) Update(context.Context, vindexes.VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { - return nil -} - -func newCostlyIndex(name string, _ map[string]string) (vindexes.Vindex, error) { - return &costlyIndex{name: name}, nil -} - -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(ctx context.Context, vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { - return nil, nil -} - -func (m *multiColIndex) Verify(ctx context.Context, vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { - return []bool{}, nil -} - -func (m *multiColIndex) PartialVindex() bool { - return true -} - -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_test", newMultiColIndex) -} - func makeTestOutput(t *testing.T) string { testOutputTempDir := utils.MakeTestOutput(t, "testdata", "plan_test") diff --git a/go/vt/vtgate/planbuilder/plan_test_vindex.go b/go/vt/vtgate/planbuilder/plan_test_vindex.go new file mode 100644 index 00000000000..432ef7b8479 --- /dev/null +++ b/go/vt/vtgate/planbuilder/plan_test_vindex.go @@ -0,0 +1,222 @@ +/* +Copyright 2023 The Vitess Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package planbuilder + +import ( + "context" + "strconv" + + "vitess.io/vitess/go/sqltypes" + "vitess.io/vitess/go/vt/key" + vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" + "vitess.io/vitess/go/vt/vtgate/vindexes" +) + +// hashIndex is a functional, unique Vindex. +type hashIndex struct{ name string } + +func (v *hashIndex) String() string { return v.name } +func (*hashIndex) Cost() int { return 1 } +func (*hashIndex) IsUnique() bool { return true } +func (*hashIndex) NeedsVCursor() bool { return false } +func (*hashIndex) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { + return []bool{}, nil +} +func (*hashIndex) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { + return nil, nil +} +func newHashIndex(name string, _ map[string]string) (vindexes.Vindex, error) { + return &hashIndex{name: name}, nil +} + +// lookupIndex is a unique Vindex, and satisfies Lookup. +type lookupIndex struct{ name string } + +func (v *lookupIndex) String() string { return v.name } +func (*lookupIndex) Cost() int { return 2 } +func (*lookupIndex) IsUnique() bool { return true } +func (*lookupIndex) NeedsVCursor() bool { return false } +func (*lookupIndex) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { + return []bool{}, nil +} +func (*lookupIndex) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { + return nil, nil +} +func (*lookupIndex) Create(context.Context, vindexes.VCursor, [][]sqltypes.Value, [][]byte, bool) error { + return nil +} +func (*lookupIndex) Delete(context.Context, vindexes.VCursor, [][]sqltypes.Value, []byte) error { + return nil +} +func (*lookupIndex) Update(context.Context, vindexes.VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { + return nil +} +func newLookupIndex(name string, _ map[string]string) (vindexes.Vindex, error) { + return &lookupIndex{name: name}, nil +} + +var _ vindexes.Lookup = (*lookupIndex)(nil) + +// 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) AllowBatch() bool { return true } +func (*nameLkpIndex) AutoCommitEnabled() bool { return false } +func (*nameLkpIndex) GetCommitOrder() vtgatepb.CommitOrder { return vtgatepb.CommitOrder_NORMAL } +func (*nameLkpIndex) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { + return []bool{}, nil +} +func (*nameLkpIndex) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { + return nil, nil +} +func (*nameLkpIndex) Create(context.Context, vindexes.VCursor, [][]sqltypes.Value, [][]byte, bool) error { + return nil +} +func (*nameLkpIndex) Delete(context.Context, vindexes.VCursor, [][]sqltypes.Value, []byte) error { + return nil +} +func (*nameLkpIndex) Update(context.Context, vindexes.VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { + return nil +} +func (*nameLkpIndex) Query() (string, []string) { + return "select name, keyspace_id from name_user_vdx where name in ::name", []string{"name"} +} +func (*nameLkpIndex) MapResult([]sqltypes.Value, []*sqltypes.Result) ([]key.Destination, error) { + return nil, nil +} +func newNameLkpIndex(name string, _ map[string]string) (vindexes.Vindex, error) { + return &nameLkpIndex{name: name}, nil +} + +var _ vindexes.Vindex = (*nameLkpIndex)(nil) +var _ vindexes.Lookup = (*nameLkpIndex)(nil) +var _ vindexes.LookupPlanable = (*nameLkpIndex)(nil) + +// costlyIndex satisfies Lookup, NonUnique. +type costlyIndex struct{ name string } + +func (v *costlyIndex) String() string { return v.name } +func (*costlyIndex) Cost() int { return 10 } +func (*costlyIndex) IsUnique() bool { return false } +func (*costlyIndex) NeedsVCursor() bool { return false } +func (*costlyIndex) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { + return []bool{}, nil +} +func (*costlyIndex) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { + return nil, nil +} +func (*costlyIndex) Create(context.Context, vindexes.VCursor, [][]sqltypes.Value, [][]byte, bool) error { + return nil +} +func (*costlyIndex) Delete(context.Context, vindexes.VCursor, [][]sqltypes.Value, []byte) error { + return nil +} +func (*costlyIndex) Update(context.Context, vindexes.VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { + return nil +} +func newCostlyIndex(name string, _ map[string]string) (vindexes.Vindex, error) { + return &costlyIndex{name: name}, nil +} + +var _ vindexes.Vindex = (*costlyIndex)(nil) +var _ vindexes.Lookup = (*costlyIndex)(nil) + +// multiColIndex satisfies multi column vindex. +type multiColIndex struct{ name string } + +func (m *multiColIndex) String() string { return m.name } +func (*multiColIndex) Cost() int { return 1 } +func (*multiColIndex) IsUnique() bool { return true } +func (*multiColIndex) NeedsVCursor() bool { return false } +func (*multiColIndex) Map(ctx context.Context, vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { + return nil, nil +} +func (*multiColIndex) Verify(ctx context.Context, vcursor vindexes.VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { + return []bool{}, nil +} +func (*multiColIndex) PartialVindex() bool { return true } +func newMultiColIndex(name string, _ map[string]string) (vindexes.Vindex, error) { + return &multiColIndex{name: name}, nil +} + +var _ vindexes.MultiColumn = (*multiColIndex)(nil) + +// unqLkpVdxBackfill satisfies Lookup, Unique. +type unqLkpVdxBackfill struct { + name string + inBackfill bool + cost int +} + +func (u *unqLkpVdxBackfill) String() string { return u.name } +func (u *unqLkpVdxBackfill) Cost() int { return u.cost } +func (*unqLkpVdxBackfill) IsUnique() bool { return false } +func (*unqLkpVdxBackfill) NeedsVCursor() bool { return false } +func (*unqLkpVdxBackfill) AllowBatch() bool { return true } +func (*unqLkpVdxBackfill) AutoCommitEnabled() bool { return false } +func (*unqLkpVdxBackfill) GetCommitOrder() vtgatepb.CommitOrder { return vtgatepb.CommitOrder_NORMAL } +func (*unqLkpVdxBackfill) Verify(context.Context, vindexes.VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { + return []bool{}, nil +} +func (*unqLkpVdxBackfill) Map(ctx context.Context, vcursor vindexes.VCursor, ids []sqltypes.Value) ([]key.Destination, error) { + return nil, nil +} +func (*unqLkpVdxBackfill) Create(context.Context, vindexes.VCursor, [][]sqltypes.Value, [][]byte, bool) error { + return nil +} +func (*unqLkpVdxBackfill) Delete(context.Context, vindexes.VCursor, [][]sqltypes.Value, []byte) error { + return nil +} +func (*unqLkpVdxBackfill) Update(context.Context, vindexes.VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { + return nil +} +func (*unqLkpVdxBackfill) Query() (string, []string) { + return "select unq_key, keyspace_id from unq_lkp_idx where unq_key in ::unq_key", []string{"unq_key"} +} +func (*unqLkpVdxBackfill) MapResult([]sqltypes.Value, []*sqltypes.Result) ([]key.Destination, error) { + return nil, nil +} +func (u *unqLkpVdxBackfill) IsBackfilling() bool { return u.inBackfill } + +func newUnqLkpVdxBackfill(name string, m map[string]string) (vindexes.Vindex, error) { + vdx := &unqLkpVdxBackfill{name: name} + if val, ok := m["write_only"]; ok { + vdx.inBackfill = val == "true" + } + if val, ok := m["cost"]; ok { + vdx.cost, _ = strconv.Atoi(val) + } + return vdx, nil +} + +var _ vindexes.Vindex = (*unqLkpVdxBackfill)(nil) +var _ vindexes.Lookup = (*unqLkpVdxBackfill)(nil) +var _ vindexes.LookupPlanable = (*unqLkpVdxBackfill)(nil) +var _ vindexes.LookupBackfill = (*unqLkpVdxBackfill)(nil) + +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_test", newMultiColIndex) + vindexes.Register("unq_lkp_test", newUnqLkpVdxBackfill) +} diff --git a/go/vt/vtgate/planbuilder/testdata/select_cases.json b/go/vt/vtgate/planbuilder/testdata/select_cases.json index 467421f7258..57e48630c0e 100644 --- a/go/vt/vtgate/planbuilder/testdata/select_cases.json +++ b/go/vt/vtgate/planbuilder/testdata/select_cases.json @@ -8105,5 +8105,256 @@ "user.user" ] } + }, + { + "comment": "pick email as vindex lookup", + "query": "select * from customer where email = 'a@mail.com'", + "v3-plan": { + "QueryType": "SELECT", + "Original": "select * from customer where email = 'a@mail.com'", + "Instructions": { + "OperatorType": "Route", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from customer where 1 != 1", + "Query": "select * from customer where email = 'a@mail.com'", + "Table": "customer", + "Values": [ + "VARCHAR(\"a@mail.com\")" + ], + "Vindex": "unq_lkp_vdx" + } + }, + "gen4-plan": { + "QueryType": "SELECT", + "Original": "select * from customer where email = 'a@mail.com'", + "Instructions": { + "OperatorType": "VindexLookup", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Values": [ + "VARCHAR(\"a@mail.com\")" + ], + "Vindex": "unq_lkp_vdx", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select unq_key, keyspace_id from unq_lkp_idx where 1 != 1", + "Query": "select unq_key, keyspace_id from unq_lkp_idx where unq_key in ::__vals", + "Table": "unq_lkp_idx", + "Values": [ + ":unq_key" + ], + "Vindex": "shard_index" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from customer where 1 != 1", + "Query": "select * from customer where email = 'a@mail.com'", + "Table": "customer" + } + ] + }, + "TablesUsed": [ + "user.customer" + ] + } + }, + { + "comment": "phone is in backfill vindex - not selected for vindex lookup", + "query": "select * from customer where phone = 123456", + "v3-plan": { + "QueryType": "SELECT", + "Original": "select * from customer where phone = 123456", + "Instructions": { + "OperatorType": "Route", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from customer where 1 != 1", + "Query": "select * from customer where phone = 123456", + "Table": "customer", + "Values": [ + "INT64(123456)" + ], + "Vindex": "unq_lkp_bf_vdx" + } + }, + "gen4-plan": { + "QueryType": "SELECT", + "Original": "select * from customer where phone = 123456", + "Instructions": { + "OperatorType": "Route", + "Variant": "Scatter", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from customer where 1 != 1", + "Query": "select * from customer where phone = 123456", + "Table": "customer" + }, + "TablesUsed": [ + "user.customer" + ] + } + }, + { + "comment": "email vindex is costly than phone vindex - but phone vindex is backfiling hence ignored", + "query": "select * from customer where email = 'a@mail.com' and phone = 123456", + "v3-plan": { + "QueryType": "SELECT", + "Original": "select * from customer where email = 'a@mail.com' and phone = 123456", + "Instructions": { + "OperatorType": "Route", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from customer where 1 != 1", + "Query": "select * from customer where email = 'a@mail.com' and phone = 123456", + "Table": "customer", + "Values": [ + "INT64(123456)" + ], + "Vindex": "unq_lkp_bf_vdx" + } + }, + "gen4-plan": { + "QueryType": "SELECT", + "Original": "select * from customer where email = 'a@mail.com' and phone = 123456", + "Instructions": { + "OperatorType": "VindexLookup", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Values": [ + "VARCHAR(\"a@mail.com\")" + ], + "Vindex": "unq_lkp_vdx", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select unq_key, keyspace_id from unq_lkp_idx where 1 != 1", + "Query": "select unq_key, keyspace_id from unq_lkp_idx where unq_key in ::__vals", + "Table": "unq_lkp_idx", + "Values": [ + ":unq_key" + ], + "Vindex": "shard_index" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from customer where 1 != 1", + "Query": "select * from customer where email = 'a@mail.com' and phone = 123456", + "Table": "customer" + } + ] + }, + "TablesUsed": [ + "user.customer" + ] + } + }, + { + "comment": "predicate order changed: email vindex is costly than phone vindex - but phone vindex is backfiling hence ignored", + "query": "select * from customer where phone = 123456 and email = 'a@mail.com'", + "v3-plan": { + "QueryType": "SELECT", + "Original": "select * from customer where phone = 123456 and email = 'a@mail.com'", + "Instructions": { + "OperatorType": "Route", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from customer where 1 != 1", + "Query": "select * from customer where phone = 123456 and email = 'a@mail.com'", + "Table": "customer", + "Values": [ + "INT64(123456)" + ], + "Vindex": "unq_lkp_bf_vdx" + } + }, + "gen4-plan": { + "QueryType": "SELECT", + "Original": "select * from customer where phone = 123456 and email = 'a@mail.com'", + "Instructions": { + "OperatorType": "VindexLookup", + "Variant": "Equal", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "Values": [ + "VARCHAR(\"a@mail.com\")" + ], + "Vindex": "unq_lkp_vdx", + "Inputs": [ + { + "OperatorType": "Route", + "Variant": "IN", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select unq_key, keyspace_id from unq_lkp_idx where 1 != 1", + "Query": "select unq_key, keyspace_id from unq_lkp_idx where unq_key in ::__vals", + "Table": "unq_lkp_idx", + "Values": [ + ":unq_key" + ], + "Vindex": "shard_index" + }, + { + "OperatorType": "Route", + "Variant": "ByDestination", + "Keyspace": { + "Name": "user", + "Sharded": true + }, + "FieldQuery": "select * from customer where 1 != 1", + "Query": "select * from customer where phone = 123456 and email = 'a@mail.com'", + "Table": "customer" + } + ] + }, + "TablesUsed": [ + "user.customer" + ] + } } ] diff --git a/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json b/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json index 8c38997f06e..85e5051ce2a 100644 --- a/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json +++ b/go/vt/vtgate/planbuilder/testdata/vschemas/schema.json @@ -124,6 +124,30 @@ "name_muticoltbl_map": { "type": "name_lkp_test", "owner": "multicol_tbl" + }, + "shard_index": { + "type": "xxhash" + }, + "unq_lkp_bf_vdx": { + "type": "unq_lkp_test", + "owner": "customer", + "params": { + "table": "unq_lkp_idx", + "from": " ", + "to": "keyspace_id", + "cost": "100", + "write_only": "true" + } + }, + "unq_lkp_vdx": { + "type": "unq_lkp_test", + "owner": "customer", + "params": { + "table": "unq_lkp_idx", + "from": "unq_key", + "to": "keyspace_id", + "cost": "300" + } } }, "tables": { @@ -376,6 +400,30 @@ "name": "user_index" } ] + }, + "customer": { + "column_vindexes": [ + { + "column": "id", + "name": "shard_index" + }, + { + "column": "email", + "name": "unq_lkp_vdx" + }, + { + "column": "phone", + "name": "unq_lkp_bf_vdx" + } + ] + }, + "unq_lkp_idx": { + "column_vindexes": [ + { + "column": "unq_key", + "name": "shard_index" + } + ] } } }, diff --git a/go/vt/vtgate/vindexes/vschema.go b/go/vt/vtgate/vindexes/vschema.go index 773a5d8466a..149103d66d2 100644 --- a/go/vt/vtgate/vindexes/vschema.go +++ b/go/vt/vtgate/vindexes/vschema.go @@ -24,17 +24,15 @@ import ( "sort" "strings" - "vitess.io/vitess/go/sqlescape" - vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" - "vitess.io/vitess/go/vt/vterrors" - "vitess.io/vitess/go/json2" + "vitess.io/vitess/go/sqlescape" "vitess.io/vitess/go/sqltypes" - "vitess.io/vitess/go/vt/sqlparser" - querypb "vitess.io/vitess/go/vt/proto/query" topodatapb "vitess.io/vitess/go/vt/proto/topodata" vschemapb "vitess.io/vitess/go/vt/proto/vschema" + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" + "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" ) // TabletTypeSuffix maps the tablet type to its suffix string. @@ -128,6 +126,7 @@ type ColumnVindex struct { isUnique bool cost int partial bool + backfill bool } // IsUnique is used to tell whether the ColumnVindex @@ -148,6 +147,11 @@ func (c *ColumnVindex) IsPartialVindex() bool { return c.partial } +// IsBackfilling returns true if the vindex is in the process of backfilling the rows. +func (c *ColumnVindex) IsBackfilling() bool { + return c.backfill +} + // Column describes a column. type Column struct { Name sqlparser.IdentifierCI `json:"name"` @@ -558,6 +562,10 @@ func buildTables(ks *vschemapb.Keyspace, vschema *VSchema, ksvschema *KeyspaceSc columns = append(columns, sqlparser.NewIdentifierCI(indCol)) } } + backfill := false + if lkpBackfill, ok := vindex.(LookupBackfill); ok { + backfill = lkpBackfill.IsBackfilling() + } columnVindex := &ColumnVindex{ Columns: columns, Type: vindexInfo.Type, @@ -566,6 +574,7 @@ func buildTables(ks *vschemapb.Keyspace, vschema *VSchema, ksvschema *KeyspaceSc Vindex: vindex, isUnique: vindex.IsUnique(), cost: vindex.Cost(), + backfill: backfill, } if i == 0 { // Perform Primary vindex check. @@ -618,13 +627,14 @@ func buildTables(ks *vschemapb.Keyspace, vschema *VSchema, ksvschema *KeyspaceSc columnSubset := columns[:i] cost++ columnVindex = &ColumnVindex{ - Columns: columnSubset, - Type: vindexInfo.Type, - Name: ind.Name, - Owned: owned, - Vindex: vindex, - cost: cost, - partial: true, + Columns: columnSubset, + Type: vindexInfo.Type, + Name: ind.Name, + Owned: owned, + Vindex: vindex, + cost: cost, + partial: true, + backfill: backfill, } t.ColumnVindexes = append(t.ColumnVindexes, columnVindex) }