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