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  }