vitess.io/vitess@v0.16.2/go/vt/vtgate/vindexes/lookup_hash.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 "context" 21 "encoding/json" 22 "fmt" 23 24 "vitess.io/vitess/go/vt/vtgate/evalengine" 25 26 "vitess.io/vitess/go/sqltypes" 27 "vitess.io/vitess/go/vt/key" 28 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 29 vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" 30 ) 31 32 var ( 33 _ SingleColumn = (*LookupHash)(nil) 34 _ Lookup = (*LookupHash)(nil) 35 _ LookupPlanable = (*LookupHash)(nil) 36 _ SingleColumn = (*LookupHashUnique)(nil) 37 _ Lookup = (*LookupHashUnique)(nil) 38 _ LookupPlanable = (*LookupHashUnique)(nil) 39 ) 40 41 func init() { 42 Register("lookup_hash", NewLookupHash) 43 Register("lookup_hash_unique", NewLookupHashUnique) 44 } 45 46 //==================================================================== 47 48 // LookupHash defines a vindex that uses a lookup table. 49 // The table is expected to define the id column as unique. It's 50 // NonUnique and a Lookup. 51 // Warning: This Vindex is being deprecated in favor of Lookup 52 type LookupHash struct { 53 name string 54 writeOnly bool 55 lkp lookupInternal 56 } 57 58 // NewLookupHash creates a LookupHash vindex. 59 // The supplied map has the following required fields: 60 // 61 // table: name of the backing table. It can be qualified by the keyspace. 62 // from: list of columns in the table that have the 'from' values of the lookup vindex. 63 // to: The 'to' column name of the table. 64 // 65 // The following fields are optional: 66 // 67 // autocommit: setting this to "true" will cause inserts to upsert and deletes to be ignored. 68 // write_only: in this mode, Map functions return the full keyrange causing a full scatter. 69 func NewLookupHash(name string, m map[string]string) (Vindex, error) { 70 lh := &LookupHash{name: name} 71 72 cc, err := parseCommonConfig(m) 73 if err != nil { 74 return nil, err 75 } 76 lh.writeOnly, err = boolFromMap(m, "write_only") 77 if err != nil { 78 return nil, err 79 } 80 81 // if autocommit is on for non-unique lookup, upsert should also be on. 82 upsert := cc.autocommit || cc.multiShardAutocommit 83 if err := lh.lkp.Init(m, cc.autocommit, upsert, cc.multiShardAutocommit); err != nil { 84 return nil, err 85 } 86 return lh, nil 87 } 88 89 // String returns the name of the vindex. 90 func (lh *LookupHash) String() string { 91 return lh.name 92 } 93 94 // Cost returns the cost of this vindex as 20. 95 func (lh *LookupHash) Cost() int { 96 return 20 97 } 98 99 // IsUnique returns false since the Vindex is not unique. 100 func (lh *LookupHash) IsUnique() bool { 101 return false 102 } 103 104 // NeedsVCursor satisfies the Vindex interface. 105 func (lh *LookupHash) NeedsVCursor() bool { 106 return true 107 } 108 109 // Map can map ids to key.Destination objects. 110 func (lh *LookupHash) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 111 out := make([]key.Destination, 0, len(ids)) 112 if lh.writeOnly { 113 for range ids { 114 out = append(out, key.DestinationKeyRange{KeyRange: &topodatapb.KeyRange{}}) 115 } 116 return out, nil 117 } 118 119 // if ignore_nulls is set and the query is about single null value, then fallback to all shards 120 if len(ids) == 1 && ids[0].IsNull() && lh.lkp.IgnoreNulls { 121 for range ids { 122 out = append(out, key.DestinationKeyRange{KeyRange: &topodatapb.KeyRange{}}) 123 } 124 return out, nil 125 } 126 127 results, err := lh.lkp.Lookup(ctx, vcursor, ids, vtgatepb.CommitOrder_NORMAL) 128 if err != nil { 129 return nil, err 130 } 131 return lh.MapResult(ids, results) 132 } 133 134 // MapResult implements the LookupPlanable interface 135 func (lh *LookupHash) MapResult(ids []sqltypes.Value, results []*sqltypes.Result) ([]key.Destination, error) { 136 out := make([]key.Destination, 0, len(ids)) 137 if lh.writeOnly { 138 for range ids { 139 out = append(out, key.DestinationKeyRange{KeyRange: &topodatapb.KeyRange{}}) 140 } 141 return out, nil 142 } 143 144 for _, result := range results { 145 if len(result.Rows) == 0 { 146 out = append(out, key.DestinationNone{}) 147 continue 148 } 149 ksids := make([][]byte, 0, len(result.Rows)) 150 for _, row := range result.Rows { 151 num, err := evalengine.ToUint64(row[0]) 152 if err != nil { 153 // A failure to convert is equivalent to not being 154 // able to map. 155 continue 156 } 157 ksids = append(ksids, vhash(num)) 158 } 159 out = append(out, key.DestinationKeyspaceIDs(ksids)) 160 } 161 return out, nil 162 } 163 164 // Query implements the LookupPlanable interface 165 func (lh *LookupHash) Query() (selQuery string, arguments []string) { 166 return lh.lkp.query() 167 } 168 169 // AllowBatch implements the LookupPlanable interface 170 func (lh *LookupHash) AllowBatch() bool { 171 return lh.lkp.BatchLookup 172 } 173 174 func (lh *LookupHash) AutoCommitEnabled() bool { 175 return lh.lkp.Autocommit 176 } 177 178 // GetCommitOrder implements the LookupPlanable interface 179 func (lh *LookupHash) GetCommitOrder() vtgatepb.CommitOrder { 180 return vtgatepb.CommitOrder_NORMAL 181 } 182 183 // Verify returns true if ids maps to ksids. 184 func (lh *LookupHash) Verify(ctx context.Context, vcursor VCursor, ids []sqltypes.Value, ksids [][]byte) ([]bool, error) { 185 if lh.writeOnly { 186 out := make([]bool, len(ids)) 187 for i := range ids { 188 out[i] = true 189 } 190 return out, nil 191 } 192 193 values, err := unhashList(ksids) 194 if err != nil { 195 return nil, fmt.Errorf("lookup.Verify.vunhash: %v", err) 196 } 197 return lh.lkp.Verify(ctx, vcursor, ids, values) 198 } 199 200 // Create reserves the id by inserting it into the vindex table. 201 func (lh *LookupHash) Create(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte, ignoreMode bool) error { 202 values, err := unhashList(ksids) 203 if err != nil { 204 return fmt.Errorf("lookup.Create.vunhash: %v", err) 205 } 206 return lh.lkp.Create(ctx, vcursor, rowsColValues, values, ignoreMode) 207 } 208 209 // Update updates the entry in the vindex table. 210 func (lh *LookupHash) Update(ctx context.Context, vcursor VCursor, oldValues []sqltypes.Value, ksid []byte, newValues []sqltypes.Value) error { 211 v, err := vunhash(ksid) 212 if err != nil { 213 return fmt.Errorf("lookup.Update.vunhash: %v", err) 214 } 215 return lh.lkp.Update(ctx, vcursor, oldValues, ksid, sqltypes.NewUint64(v), newValues) 216 } 217 218 // Delete deletes the entry from the vindex table. 219 func (lh *LookupHash) Delete(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksid []byte) error { 220 v, err := vunhash(ksid) 221 if err != nil { 222 return fmt.Errorf("lookup.Delete.vunhash: %v", err) 223 } 224 return lh.lkp.Delete(ctx, vcursor, rowsColValues, sqltypes.NewUint64(v), vtgatepb.CommitOrder_NORMAL) 225 } 226 227 // MarshalJSON returns a JSON representation of LookupHash. 228 func (lh *LookupHash) MarshalJSON() ([]byte, error) { 229 return json.Marshal(lh.lkp) 230 } 231 232 // unhashList unhashes a list of keyspace ids into []sqltypes.Value. 233 func unhashList(ksids [][]byte) ([]sqltypes.Value, error) { 234 values := make([]sqltypes.Value, 0, len(ksids)) 235 for _, ksid := range ksids { 236 v, err := vunhash(ksid) 237 if err != nil { 238 return nil, err 239 } 240 values = append(values, sqltypes.NewUint64(v)) 241 } 242 return values, nil 243 } 244 245 //==================================================================== 246 247 // LookupHashUnique defines a vindex that uses a lookup table. 248 // The table is expected to define the id column as unique. It's 249 // Unique and a Lookup. 250 // Warning: This Vindex is being depcreated in favor of LookupUnique 251 type LookupHashUnique struct { 252 name string 253 writeOnly bool 254 lkp lookupInternal 255 } 256 257 var _ LookupPlanable = (*LookupHashUnique)(nil) 258 259 // NewLookupHashUnique creates a LookupHashUnique vindex. 260 // The supplied map has the following required fields: 261 // 262 // table: name of the backing table. It can be qualified by the keyspace. 263 // from: list of columns in the table that have the 'from' values of the lookup vindex. 264 // to: The 'to' column name of the table. 265 // 266 // The following fields are optional: 267 // 268 // autocommit: setting this to "true" will cause deletes to be ignored. 269 // write_only: in this mode, Map functions return the full keyrange causing a full scatter. 270 func NewLookupHashUnique(name string, m map[string]string) (Vindex, error) { 271 lhu := &LookupHashUnique{name: name} 272 273 cc, err := parseCommonConfig(m) 274 if err != nil { 275 return nil, err 276 } 277 lhu.writeOnly, err = boolFromMap(m, "write_only") 278 if err != nil { 279 return nil, err 280 } 281 282 // Don't allow upserts for unique vindexes. 283 if err := lhu.lkp.Init(m, cc.autocommit, false /* upsert */, cc.multiShardAutocommit); err != nil { 284 return nil, err 285 } 286 return lhu, nil 287 } 288 289 // String returns the name of the vindex. 290 func (lhu *LookupHashUnique) String() string { 291 return lhu.name 292 } 293 294 // Cost returns the cost of this vindex as 10. 295 func (lhu *LookupHashUnique) Cost() int { 296 return 10 297 } 298 299 // IsUnique returns true since the Vindex is unique. 300 func (lhu *LookupHashUnique) IsUnique() bool { 301 return true 302 } 303 304 // NeedsVCursor satisfies the Vindex interface. 305 func (lhu *LookupHashUnique) NeedsVCursor() bool { 306 return true 307 } 308 309 // Map can map ids to key.Destination objects. 310 func (lhu *LookupHashUnique) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 311 if lhu.writeOnly { 312 out := make([]key.Destination, 0, len(ids)) 313 for range ids { 314 out = append(out, key.DestinationKeyRange{KeyRange: &topodatapb.KeyRange{}}) 315 } 316 return out, nil 317 } 318 319 results, err := lhu.lkp.Lookup(ctx, vcursor, ids, vtgatepb.CommitOrder_NORMAL) 320 if err != nil { 321 return nil, err 322 } 323 return lhu.MapResult(ids, results) 324 } 325 326 func (lhu *LookupHashUnique) MapResult(ids []sqltypes.Value, results []*sqltypes.Result) ([]key.Destination, error) { 327 out := make([]key.Destination, 0, len(ids)) 328 if lhu.writeOnly { 329 for range ids { 330 out = append(out, key.DestinationKeyRange{KeyRange: &topodatapb.KeyRange{}}) 331 } 332 return out, nil 333 } 334 for i, result := range results { 335 switch len(result.Rows) { 336 case 0: 337 out = append(out, key.DestinationNone{}) 338 case 1: 339 num, err := evalengine.ToUint64(result.Rows[0][0]) 340 if err != nil { 341 out = append(out, key.DestinationNone{}) 342 continue 343 } 344 out = append(out, key.DestinationKeyspaceID(vhash(num))) 345 default: 346 return nil, fmt.Errorf("LookupHash.Map: unexpected multiple results from vindex %s: %v", lhu.lkp.Table, ids[i]) 347 } 348 } 349 return out, nil 350 } 351 352 // Verify returns true if ids maps to ksids. 353 func (lhu *LookupHashUnique) Verify(ctx context.Context, vcursor VCursor, ids []sqltypes.Value, ksids [][]byte) ([]bool, error) { 354 if lhu.writeOnly { 355 out := make([]bool, len(ids)) 356 for i := range ids { 357 out[i] = true 358 } 359 return out, nil 360 } 361 362 values, err := unhashList(ksids) 363 if err != nil { 364 return nil, fmt.Errorf("lookup.Verify.vunhash: %v", err) 365 } 366 return lhu.lkp.Verify(ctx, vcursor, ids, values) 367 } 368 369 // Create reserves the id by inserting it into the vindex table. 370 func (lhu *LookupHashUnique) Create(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte, ignoreMode bool) error { 371 values, err := unhashList(ksids) 372 if err != nil { 373 return fmt.Errorf("lookup.Create.vunhash: %v", err) 374 } 375 return lhu.lkp.Create(ctx, vcursor, rowsColValues, values, ignoreMode) 376 } 377 378 // Delete deletes the entry from the vindex table. 379 func (lhu *LookupHashUnique) Delete(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksid []byte) error { 380 v, err := vunhash(ksid) 381 if err != nil { 382 return fmt.Errorf("lookup.Delete.vunhash: %v", err) 383 } 384 return lhu.lkp.Delete(ctx, vcursor, rowsColValues, sqltypes.NewUint64(v), vtgatepb.CommitOrder_NORMAL) 385 } 386 387 // Update updates the entry in the vindex table. 388 func (lhu *LookupHashUnique) Update(ctx context.Context, vcursor VCursor, oldValues []sqltypes.Value, ksid []byte, newValues []sqltypes.Value) error { 389 v, err := vunhash(ksid) 390 if err != nil { 391 return fmt.Errorf("lookup.Update.vunhash: %v", err) 392 } 393 return lhu.lkp.Update(ctx, vcursor, oldValues, ksid, sqltypes.NewUint64(v), newValues) 394 } 395 396 // MarshalJSON returns a JSON representation of LookupHashUnique. 397 func (lhu *LookupHashUnique) MarshalJSON() ([]byte, error) { 398 return json.Marshal(lhu.lkp) 399 } 400 401 // IsBackfilling implements the LookupBackfill interface 402 func (lhu *LookupHashUnique) IsBackfilling() bool { 403 return lhu.writeOnly 404 } 405 406 func (lhu *LookupHashUnique) AllowBatch() bool { 407 return lhu.lkp.BatchLookup 408 } 409 410 func (lhu *LookupHashUnique) AutoCommitEnabled() bool { 411 return lhu.lkp.Autocommit 412 } 413 414 func (lhu *LookupHashUnique) Query() (selQuery string, arguments []string) { 415 return lhu.lkp.query() 416 } 417 418 // GetCommitOrder implements the LookupPlanable interface 419 func (lhu *LookupHashUnique) GetCommitOrder() vtgatepb.CommitOrder { 420 return vtgatepb.CommitOrder_NORMAL 421 }