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  }