vitess.io/vitess@v0.16.2/go/vt/vtgate/vindexes/lookup_unicodeloosemd5_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/binary" 22 "encoding/json" 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 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 30 vtgatepb "vitess.io/vitess/go/vt/proto/vtgate" 31 ) 32 33 var ( 34 _ SingleColumn = (*LookupUnicodeLooseMD5Hash)(nil) 35 _ Lookup = (*LookupUnicodeLooseMD5Hash)(nil) 36 _ LookupPlanable = (*LookupUnicodeLooseMD5Hash)(nil) 37 _ SingleColumn = (*LookupUnicodeLooseMD5HashUnique)(nil) 38 _ Lookup = (*LookupUnicodeLooseMD5HashUnique)(nil) 39 _ LookupPlanable = (*LookupUnicodeLooseMD5HashUnique)(nil) 40 ) 41 42 func init() { 43 Register("lookup_unicodeloosemd5_hash", NewLookupUnicodeLooseMD5Hash) 44 Register("lookup_unicodeloosemd5_hash_unique", NewLookupUnicodeLooseMD5HashUnique) 45 } 46 47 //==================================================================== 48 49 // LookupUnicodeLooseMD5Hash defines a vindex that uses a lookup table. 50 // The table is expected to define the id column as unique. It's 51 // NonUnique and a Lookup and stores the from value in a hashed form. 52 // Warning: This Vindex is being depcreated in favor of Lookup 53 type LookupUnicodeLooseMD5Hash struct { 54 name string 55 writeOnly bool 56 lkp lookupInternal 57 } 58 59 // NewLookupUnicodeLooseMD5Hash creates a LookupUnicodeLooseMD5Hash vindex. 60 // The supplied map has the following required fields: 61 // 62 // table: name of the backing table. It can be qualified by the keyspace. 63 // from: list of columns in the table that have the 'from' values of the lookup vindex. 64 // to: The 'to' column name of the table. 65 // 66 // The following fields are optional: 67 // 68 // autocommit: setting this to "true" will cause inserts to upsert and deletes to be ignored. 69 // write_only: in this mode, Map functions return the full keyrange causing a full scatter. 70 func NewLookupUnicodeLooseMD5Hash(name string, m map[string]string) (Vindex, error) { 71 lh := &LookupUnicodeLooseMD5Hash{name: name} 72 73 cc, err := parseCommonConfig(m) 74 if err != nil { 75 return nil, err 76 } 77 lh.writeOnly, err = boolFromMap(m, "write_only") 78 if err != nil { 79 return nil, err 80 } 81 82 // if autocommit is on for non-unique lookup, upsert should also be on. 83 if err := lh.lkp.Init(m, cc.autocommit, cc.autocommit || cc.multiShardAutocommit, 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 *LookupUnicodeLooseMD5Hash) String() string { 91 return lh.name 92 } 93 94 // Cost returns the cost of this vindex as 20. 95 func (lh *LookupUnicodeLooseMD5Hash) Cost() int { 96 return 20 97 } 98 99 // IsUnique returns false since the Vindex is not unique. 100 func (lh *LookupUnicodeLooseMD5Hash) IsUnique() bool { 101 return false 102 } 103 104 // NeedsVCursor satisfies the Vindex interface. 105 func (lh *LookupUnicodeLooseMD5Hash) NeedsVCursor() bool { 106 return true 107 } 108 109 // Map can map ids to key.Destination objects. 110 func (lh *LookupUnicodeLooseMD5Hash) 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 ids, err := convertIds(ids) 128 if err != nil { 129 return nil, err 130 } 131 results, err := lh.lkp.Lookup(ctx, vcursor, ids, vtgatepb.CommitOrder_NORMAL) 132 if err != nil { 133 return nil, err 134 } 135 for _, result := range results { 136 if len(result.Rows) == 0 { 137 out = append(out, key.DestinationNone{}) 138 continue 139 } 140 ksids := make([][]byte, 0, len(result.Rows)) 141 for _, row := range result.Rows { 142 num, err := evalengine.ToUint64(row[0]) 143 if err != nil { 144 // A failure to convert is equivalent to not being 145 // able to map. 146 continue 147 } 148 ksids = append(ksids, vhash(num)) 149 } 150 out = append(out, key.DestinationKeyspaceIDs(ksids)) 151 } 152 return out, nil 153 } 154 155 // MapResult implements the LookupPlanable interface 156 func (lh *LookupUnicodeLooseMD5Hash) MapResult(ids []sqltypes.Value, results []*sqltypes.Result) ([]key.Destination, error) { 157 out := make([]key.Destination, 0, len(ids)) 158 if lh.writeOnly { 159 for range ids { 160 out = append(out, key.DestinationKeyRange{KeyRange: &topodatapb.KeyRange{}}) 161 } 162 return out, nil 163 } 164 165 for _, result := range results { 166 if len(result.Rows) == 0 { 167 out = append(out, key.DestinationNone{}) 168 continue 169 } 170 ksids := make([][]byte, 0, len(result.Rows)) 171 for _, row := range result.Rows { 172 num, err := evalengine.ToUint64(row[0]) 173 if err != nil { 174 // A failure to convert is equivalent to not being 175 // able to map. 176 continue 177 } 178 ksids = append(ksids, vhash(num)) 179 } 180 out = append(out, key.DestinationKeyspaceIDs(ksids)) 181 } 182 return out, nil 183 } 184 185 // Query implements the LookupPlanable interface 186 func (lh *LookupUnicodeLooseMD5Hash) Query() (selQuery string, arguments []string) { 187 return lh.lkp.query() 188 } 189 190 // AllowBatch implements the LookupPlanable interface 191 func (lh *LookupUnicodeLooseMD5Hash) AllowBatch() bool { 192 return lh.lkp.BatchLookup 193 } 194 195 func (lh *LookupUnicodeLooseMD5Hash) AutoCommitEnabled() bool { 196 return lh.lkp.Autocommit 197 } 198 199 // GetCommitOrder implements the LookupPlanable interface 200 func (lh *LookupUnicodeLooseMD5Hash) GetCommitOrder() vtgatepb.CommitOrder { 201 return vtgatepb.CommitOrder_NORMAL 202 } 203 204 // Verify returns true if ids maps to ksids. 205 func (lh *LookupUnicodeLooseMD5Hash) Verify(ctx context.Context, vcursor VCursor, ids []sqltypes.Value, ksids [][]byte) ([]bool, error) { 206 if lh.writeOnly { 207 out := make([]bool, len(ids)) 208 for i := range ids { 209 out[i] = true 210 } 211 return out, nil 212 } 213 214 values, err := unhashList(ksids) 215 if err != nil { 216 return nil, fmt.Errorf("lookup.Verify.vunhash: %v", err) 217 } 218 ids, err = convertIds(ids) 219 if err != nil { 220 return nil, fmt.Errorf("lookup.Verify.vunhash: %v", err) 221 } 222 return lh.lkp.Verify(ctx, vcursor, ids, values) 223 } 224 225 // Create reserves the id by inserting it into the vindex table. 226 func (lh *LookupUnicodeLooseMD5Hash) Create(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte, ignoreMode bool) error { 227 values, err := unhashList(ksids) 228 if err != nil { 229 return fmt.Errorf("lookup.Create.vunhash: %v", err) 230 } 231 rowsColValues, err = convertRows(rowsColValues) 232 if err != nil { 233 return fmt.Errorf("lookup.Create.convert: %v", err) 234 } 235 return lh.lkp.Create(ctx, vcursor, rowsColValues, values, ignoreMode) 236 } 237 238 // Update updates the entry in the vindex table. 239 func (lh *LookupUnicodeLooseMD5Hash) Update(ctx context.Context, vcursor VCursor, oldValues []sqltypes.Value, ksid []byte, newValues []sqltypes.Value) error { 240 v, err := vunhash(ksid) 241 if err != nil { 242 return fmt.Errorf("lookup.Update.vunhash: %v", err) 243 } 244 newValues, err = convertIds(newValues) 245 if err != nil { 246 return fmt.Errorf("lookup.Update.convert: %v", err) 247 } 248 oldValues, err = convertIds(oldValues) 249 if err != nil { 250 return fmt.Errorf("lookup.Update.convert: %v", err) 251 } 252 return lh.lkp.Update(ctx, vcursor, oldValues, ksid, sqltypes.NewUint64(v), newValues) 253 } 254 255 // Delete deletes the entry from the vindex table. 256 func (lh *LookupUnicodeLooseMD5Hash) Delete(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksid []byte) error { 257 v, err := vunhash(ksid) 258 if err != nil { 259 return fmt.Errorf("lookup.Delete.vunhash: %v", err) 260 } 261 rowsColValues, err = convertRows(rowsColValues) 262 if err != nil { 263 return fmt.Errorf("lookup.Delete.convert: %v", err) 264 } 265 return lh.lkp.Delete(ctx, vcursor, rowsColValues, sqltypes.NewUint64(v), vtgatepb.CommitOrder_NORMAL) 266 } 267 268 // MarshalJSON returns a JSON representation of LookupHash. 269 func (lh *LookupUnicodeLooseMD5Hash) MarshalJSON() ([]byte, error) { 270 return json.Marshal(lh.lkp) 271 } 272 273 //==================================================================== 274 275 // LookupUnicodeLooseMD5HashUnique defines a vindex that uses a lookup table. 276 // The table is expected to define the id column as unique. It's 277 // Unique and a Lookup and will store the from value in a hashed format. 278 // Warning: This Vindex is being depcreated in favor of LookupUnique 279 type LookupUnicodeLooseMD5HashUnique struct { 280 name string 281 writeOnly bool 282 lkp lookupInternal 283 } 284 285 // NewLookupUnicodeLooseMD5HashUnique creates a LookupUnicodeLooseMD5HashUnique vindex. 286 // The supplied map has the following required fields: 287 // 288 // table: name of the backing table. It can be qualified by the keyspace. 289 // from: list of columns in the table that have the 'from' values of the lookup vindex. 290 // to: The 'to' column name of the table. 291 // 292 // The following fields are optional: 293 // 294 // autocommit: setting this to "true" will cause deletes to be ignored. 295 // write_only: in this mode, Map functions return the full keyrange causing a full scatter. 296 func NewLookupUnicodeLooseMD5HashUnique(name string, m map[string]string) (Vindex, error) { 297 lhu := &LookupUnicodeLooseMD5HashUnique{name: name} 298 299 cc, err := parseCommonConfig(m) 300 if err != nil { 301 return nil, err 302 } 303 lhu.writeOnly, err = boolFromMap(m, "write_only") 304 if err != nil { 305 return nil, err 306 } 307 308 // Don't allow upserts for unique vindexes. 309 if err := lhu.lkp.Init(m, cc.autocommit, false, cc.multiShardAutocommit); err != nil { 310 return nil, err 311 } 312 return lhu, nil 313 } 314 315 // String returns the name of the vindex. 316 func (lhu *LookupUnicodeLooseMD5HashUnique) String() string { 317 return lhu.name 318 } 319 320 // Cost returns the cost of this vindex as 10. 321 func (lhu *LookupUnicodeLooseMD5HashUnique) Cost() int { 322 return 10 323 } 324 325 // IsUnique returns true since the Vindex is unique. 326 func (lhu *LookupUnicodeLooseMD5HashUnique) IsUnique() bool { 327 return true 328 } 329 330 // NeedsVCursor satisfies the Vindex interface. 331 func (lhu *LookupUnicodeLooseMD5HashUnique) NeedsVCursor() bool { 332 return true 333 } 334 335 // Map can map ids to key.Destination objects. 336 func (lhu *LookupUnicodeLooseMD5HashUnique) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 337 out := make([]key.Destination, 0, len(ids)) 338 if lhu.writeOnly { 339 for range ids { 340 out = append(out, key.DestinationKeyRange{KeyRange: &topodatapb.KeyRange{}}) 341 } 342 return out, nil 343 } 344 345 ids, err := convertIds(ids) 346 if err != nil { 347 return nil, err 348 } 349 results, err := lhu.lkp.Lookup(ctx, vcursor, ids, vtgatepb.CommitOrder_NORMAL) 350 if err != nil { 351 return nil, err 352 } 353 for i, result := range results { 354 switch len(result.Rows) { 355 case 0: 356 out = append(out, key.DestinationNone{}) 357 case 1: 358 num, err := evalengine.ToUint64(result.Rows[0][0]) 359 if err != nil { 360 out = append(out, key.DestinationNone{}) 361 continue 362 } 363 out = append(out, key.DestinationKeyspaceID(vhash(num))) 364 default: 365 return nil, fmt.Errorf("LookupUnicodeLooseMD5HashUnique.Map: unexpected multiple results from vindex %s: %v", lhu.lkp.Table, ids[i]) 366 } 367 } 368 return out, nil 369 } 370 371 // MapResult implements the LookupPlanable interface 372 func (lhu *LookupUnicodeLooseMD5HashUnique) MapResult(ids []sqltypes.Value, results []*sqltypes.Result) ([]key.Destination, error) { 373 out := make([]key.Destination, 0, len(ids)) 374 if lhu.writeOnly { 375 for range ids { 376 out = append(out, key.DestinationKeyRange{KeyRange: &topodatapb.KeyRange{}}) 377 } 378 return out, nil 379 } 380 381 for i, result := range results { 382 switch len(result.Rows) { 383 case 0: 384 out = append(out, key.DestinationNone{}) 385 case 1: 386 num, err := evalengine.ToUint64(result.Rows[0][0]) 387 if err != nil { 388 out = append(out, key.DestinationNone{}) 389 continue 390 } 391 out = append(out, key.DestinationKeyspaceID(vhash(num))) 392 default: 393 return nil, fmt.Errorf("LookupUnicodeLooseMD5HashUnique.Map: unexpected multiple results from vindex %s: %v", lhu.lkp.Table, ids[i]) 394 } 395 } 396 return out, nil 397 } 398 399 // Query implements the LookupPlanable interface 400 func (lhu *LookupUnicodeLooseMD5HashUnique) Query() (selQuery string, arguments []string) { 401 return lhu.lkp.query() 402 } 403 404 // AllowBatch implements the LookupPlanable interface 405 func (lhu *LookupUnicodeLooseMD5HashUnique) AllowBatch() bool { 406 return lhu.lkp.BatchLookup 407 } 408 409 func (lhu *LookupUnicodeLooseMD5HashUnique) AutoCommitEnabled() bool { 410 return lhu.lkp.Autocommit 411 } 412 413 // GetCommitOrder implements the LookupPlanable interface 414 func (lhu *LookupUnicodeLooseMD5HashUnique) GetCommitOrder() vtgatepb.CommitOrder { 415 return vtgatepb.CommitOrder_NORMAL 416 } 417 418 // Verify returns true if ids maps to ksids. 419 func (lhu *LookupUnicodeLooseMD5HashUnique) Verify(ctx context.Context, vcursor VCursor, ids []sqltypes.Value, ksids [][]byte) ([]bool, error) { 420 if lhu.writeOnly { 421 out := make([]bool, len(ids)) 422 for i := range ids { 423 out[i] = true 424 } 425 return out, nil 426 } 427 428 values, err := unhashList(ksids) 429 if err != nil { 430 return nil, fmt.Errorf("lookup.Verify.vunhash: %v", err) 431 } 432 ids, err = convertIds(ids) 433 if err != nil { 434 return nil, fmt.Errorf("lookup.Verify.vunhash: %v", err) 435 } 436 return lhu.lkp.Verify(ctx, vcursor, ids, values) 437 } 438 439 // Create reserves the id by inserting it into the vindex table. 440 func (lhu *LookupUnicodeLooseMD5HashUnique) Create(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksids [][]byte, ignoreMode bool) error { 441 values, err := unhashList(ksids) 442 if err != nil { 443 return fmt.Errorf("lookup.Create.vunhash: %v", err) 444 } 445 rowsColValues, err = convertRows(rowsColValues) 446 if err != nil { 447 return fmt.Errorf("lookup.Create.convert: %v", err) 448 } 449 return lhu.lkp.Create(ctx, vcursor, rowsColValues, values, ignoreMode) 450 } 451 452 // Delete deletes the entry from the vindex table. 453 func (lhu *LookupUnicodeLooseMD5HashUnique) Delete(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value, ksid []byte) error { 454 v, err := vunhash(ksid) 455 if err != nil { 456 return fmt.Errorf("lookup.Delete.vunhash: %v", err) 457 } 458 rowsColValues, err = convertRows(rowsColValues) 459 if err != nil { 460 return fmt.Errorf("lookup.Delete.convert: %v", err) 461 } 462 return lhu.lkp.Delete(ctx, vcursor, rowsColValues, sqltypes.NewUint64(v), vtgatepb.CommitOrder_NORMAL) 463 } 464 465 // Update updates the entry in the vindex table. 466 func (lhu *LookupUnicodeLooseMD5HashUnique) Update(ctx context.Context, vcursor VCursor, oldValues []sqltypes.Value, ksid []byte, newValues []sqltypes.Value) error { 467 v, err := vunhash(ksid) 468 if err != nil { 469 return fmt.Errorf("lookup.Update.vunhash: %v", err) 470 } 471 newValues, err = convertIds(newValues) 472 if err != nil { 473 return fmt.Errorf("lookup.Update.convert: %v", err) 474 } 475 oldValues, err = convertIds(oldValues) 476 if err != nil { 477 return fmt.Errorf("lookup.Update.convert: %v", err) 478 } 479 return lhu.lkp.Update(ctx, vcursor, oldValues, ksid, sqltypes.NewUint64(v), newValues) 480 } 481 482 // MarshalJSON returns a JSON representation of LookupHashUnique. 483 func (lhu *LookupUnicodeLooseMD5HashUnique) MarshalJSON() ([]byte, error) { 484 return json.Marshal(lhu.lkp) 485 } 486 487 // IsBackfilling implements the LookupBackfill interface 488 func (lhu *LookupUnicodeLooseMD5HashUnique) IsBackfilling() bool { 489 return lhu.writeOnly 490 } 491 492 func unicodeHashValue(value sqltypes.Value) (sqltypes.Value, error) { 493 hash, err := unicodeHash(vMD5Hash, value) 494 if err != nil { 495 return sqltypes.NULL, err 496 } 497 498 return sqltypes.NewUint64(binary.BigEndian.Uint64(hash[:8])), nil 499 } 500 501 func convertIds(ids []sqltypes.Value) ([]sqltypes.Value, error) { 502 converted := make([]sqltypes.Value, 0, len(ids)) 503 for _, id := range ids { 504 idVal, err := unicodeHashValue(id) 505 if err != nil { 506 return nil, err 507 } 508 converted = append(converted, idVal) 509 } 510 return converted, nil 511 } 512 513 func convertRows(rows [][]sqltypes.Value) ([][]sqltypes.Value, error) { 514 converted := make([][]sqltypes.Value, 0, len(rows)) 515 for _, row := range rows { 516 row, err := convertIds(row) 517 if err != nil { 518 return nil, err 519 } 520 converted = append(converted, row) 521 } 522 return converted, nil 523 }