vitess.io/vitess@v0.16.2/go/vt/vtgate/vindexes/region_experimental.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package vindexes 18 19 import ( 20 "bytes" 21 "context" 22 "encoding/binary" 23 "fmt" 24 25 "vitess.io/vitess/go/vt/vtgate/evalengine" 26 27 "vitess.io/vitess/go/sqltypes" 28 "vitess.io/vitess/go/vt/key" 29 ) 30 31 var ( 32 _ MultiColumn = (*RegionExperimental)(nil) 33 ) 34 35 func init() { 36 Register("region_experimental", NewRegionExperimental) 37 } 38 39 // RegionExperimental is a multi-column unique vindex. The first column is prefixed 40 // to the hash of the second column to produce the keyspace id. 41 // RegionExperimental can be used for geo-partitioning because the first column can denote a region, 42 // and its value will dictate the shard for that region. 43 type RegionExperimental struct { 44 name string 45 regionBytes int 46 } 47 48 // NewRegionExperimental creates a RegionExperimental vindex. 49 // The supplied map requires all the fields of "consistent_lookup_unique". 50 // Additionally, it requires a region_bytes argument whose value can be "1", or "2". 51 func NewRegionExperimental(name string, m map[string]string) (Vindex, error) { 52 rbs, ok := m["region_bytes"] 53 if !ok { 54 return nil, fmt.Errorf("region_experimental missing region_bytes param") 55 } 56 var rb int 57 switch rbs { 58 case "1": 59 rb = 1 60 case "2": 61 rb = 2 62 default: 63 return nil, fmt.Errorf("region_bits must be 1 or 2: %v", rbs) 64 } 65 return &RegionExperimental{ 66 name: name, 67 regionBytes: rb, 68 }, nil 69 } 70 71 // String returns the name of the vindex. 72 func (ge *RegionExperimental) String() string { 73 return ge.name 74 } 75 76 // Cost returns the cost of this index as 1. 77 func (ge *RegionExperimental) Cost() int { 78 return 1 79 } 80 81 // IsUnique returns true since the Vindex is unique. 82 func (ge *RegionExperimental) IsUnique() bool { 83 return true 84 } 85 86 // NeedsVCursor satisfies the Vindex interface. 87 func (ge *RegionExperimental) NeedsVCursor() bool { 88 return false 89 } 90 91 // Map satisfies MultiColumn. 92 func (ge *RegionExperimental) Map(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { 93 destinations := make([]key.Destination, 0, len(rowsColValues)) 94 for _, row := range rowsColValues { 95 if len(row) > 2 { 96 destinations = append(destinations, key.DestinationNone{}) 97 continue 98 } 99 // Compute region prefix. 100 rn, err := evalengine.ToUint64(row[0]) 101 if err != nil { 102 destinations = append(destinations, key.DestinationNone{}) 103 continue 104 } 105 r := make([]byte, 2, 2+8) 106 binary.BigEndian.PutUint16(r, uint16(rn)) 107 108 // Concatenate and add to destinations. 109 if ge.regionBytes == 1 { 110 r = r[1:] 111 } 112 113 dest := r 114 if len(row) == 2 { 115 // Compute hash. 116 hn, err := evalengine.ToUint64(row[1]) 117 if err != nil { 118 destinations = append(destinations, key.DestinationNone{}) 119 continue 120 } 121 h := vhash(hn) 122 dest = append(dest, h...) 123 destinations = append(destinations, key.DestinationKeyspaceID(dest)) 124 } else { 125 destinations = append(destinations, NewKeyRangeFromPrefix(dest)) 126 } 127 } 128 return destinations, nil 129 } 130 131 // Verify satisfies MultiColumn. 132 func (ge *RegionExperimental) Verify(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte) ([]bool, error) { 133 result := make([]bool, len(rowsColValues)) 134 destinations, _ := ge.Map(ctx, vcursor, rowsColValues) 135 for i, dest := range destinations { 136 destksid, ok := dest.(key.DestinationKeyspaceID) 137 if !ok { 138 continue 139 } 140 result[i] = bytes.Equal([]byte(destksid), ksids[i]) 141 } 142 return result, nil 143 } 144 145 func (ge *RegionExperimental) PartialVindex() bool { 146 return true 147 }