github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/row/writer.go (about)

     1  // Copyright 2015 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package row
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/keys"
    17  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    20  	"github.com/cockroachdb/cockroach/pkg/util"
    21  	"github.com/cockroachdb/errors"
    22  )
    23  
    24  // This file contains common functions for the three writers, Inserter, Deleter
    25  // and Updater.
    26  
    27  type checkFKConstraints bool
    28  
    29  const (
    30  	// CheckFKs can be passed to row writers to check fk validity.
    31  	CheckFKs checkFKConstraints = true
    32  	// SkipFKs can be passed to row writer to skip fk validity checks.
    33  	SkipFKs checkFKConstraints = false
    34  )
    35  
    36  // ColIDtoRowIndexFromCols groups a slice of ColumnDescriptors by their ID
    37  // field, returning a map from ID to the index of the column in the input slice.
    38  // It assumes there are no duplicate descriptors in the input.
    39  func ColIDtoRowIndexFromCols(cols []sqlbase.ColumnDescriptor) map[sqlbase.ColumnID]int {
    40  	colIDtoRowIndex := make(map[sqlbase.ColumnID]int, len(cols))
    41  	for i := range cols {
    42  		colIDtoRowIndex[cols[i].ID] = i
    43  	}
    44  	return colIDtoRowIndex
    45  }
    46  
    47  // ColMapping returns a map from ordinals in the fromCols list to ordinals in
    48  // the toCols list. More precisely, for 0 <= i < fromCols:
    49  //
    50  //   result[i] = j such that fromCols[i].ID == toCols[j].ID, or
    51  //                -1 if the column is not part of toCols.
    52  func ColMapping(fromCols, toCols []sqlbase.ColumnDescriptor) []int {
    53  	// colMap is a map from ColumnID to ordinal into fromCols.
    54  	var colMap util.FastIntMap
    55  	for i := range fromCols {
    56  		colMap.Set(int(fromCols[i].ID), i)
    57  	}
    58  
    59  	result := make([]int, len(fromCols))
    60  	for i := range result {
    61  		// -1 value indicates that this column is not being returned.
    62  		result[i] = -1
    63  	}
    64  
    65  	// Set the appropriate index values for the returning columns.
    66  	for toOrd := range toCols {
    67  		if fromOrd, ok := colMap.Get(int(toCols[toOrd].ID)); ok {
    68  			result[fromOrd] = toOrd
    69  		}
    70  	}
    71  
    72  	return result
    73  }
    74  
    75  // prepareInsertOrUpdateBatch constructs a KV batch that inserts or
    76  // updates a row in KV.
    77  // - batch is the KV batch where commands should be appended.
    78  // - putFn is the functions that can append Put/CPut commands to the batch.
    79  //   (must be adapted depending on whether 'overwrite' is set)
    80  // - helper is the rowHelper that knows about the table being modified.
    81  // - primaryIndexKey is the PK prefix for the current row.
    82  // - fetchedCols is the list of schema columns that have been fetched
    83  //   in preparation for this update.
    84  // - values is the SQL-level row values that are being written.
    85  // - marshaledValues contains the pre-encoded KV-level row values.
    86  //   marshaledValues is only used when writing single column families.
    87  //   Regardless of whether there are single column families,
    88  //   pre-encoding must occur prior to calling this function to check whether
    89  //   the encoding is _possible_ (i.e. values fit in the column types, etc).
    90  // - valColIDMapping/marshaledColIDMapping is the mapping from column
    91  //   IDs into positions of the slices values or marshaledValues.
    92  // - kvKey and kvValues must be heap-allocated scratch buffers to write
    93  //   roachpb.Key and roachpb.Value values.
    94  // - rawValueBuf must be a scratch byte array. This must be reinitialized
    95  //   to an empty slice on each call but can be preserved at its current
    96  //   capacity to avoid allocations. The function returns the slice.
    97  // - overwrite must be set to true for UPDATE and UPSERT.
    98  // - traceKV is to be set to log the KV operations added to the batch.
    99  func prepareInsertOrUpdateBatch(
   100  	ctx context.Context,
   101  	batch putter,
   102  	helper *rowHelper,
   103  	primaryIndexKey []byte,
   104  	fetchedCols []sqlbase.ColumnDescriptor,
   105  	values []tree.Datum,
   106  	valColIDMapping map[sqlbase.ColumnID]int,
   107  	marshaledValues []roachpb.Value,
   108  	marshaledColIDMapping map[sqlbase.ColumnID]int,
   109  	kvKey *roachpb.Key,
   110  	kvValue *roachpb.Value,
   111  	rawValueBuf []byte,
   112  	putFn func(ctx context.Context, b putter, key *roachpb.Key, value *roachpb.Value, traceKV bool),
   113  	overwrite, traceKV bool,
   114  ) ([]byte, error) {
   115  	for i := range helper.TableDesc.Families {
   116  		family := &helper.TableDesc.Families[i]
   117  		update := false
   118  		for _, colID := range family.ColumnIDs {
   119  			if _, ok := marshaledColIDMapping[colID]; ok {
   120  				update = true
   121  				break
   122  			}
   123  		}
   124  		// We can have an empty family.ColumnIDs in the following case:
   125  		// * A table is created with the primary key not in family 0, and another column in family 0.
   126  		// * The column in family 0 is dropped, leaving the 0'th family empty.
   127  		// In this case, we must keep the empty 0'th column family in order to ensure that column family 0
   128  		// is always encoded as the sentinel k/v for a row.
   129  		if !update && len(family.ColumnIDs) != 0 {
   130  			continue
   131  		}
   132  
   133  		if i > 0 {
   134  			// HACK: MakeFamilyKey appends to its argument, so on every loop iteration
   135  			// after the first, trim primaryIndexKey so nothing gets overwritten.
   136  			// TODO(dan): Instead of this, use something like engine.ChunkAllocator.
   137  			primaryIndexKey = primaryIndexKey[:len(primaryIndexKey):len(primaryIndexKey)]
   138  		}
   139  
   140  		*kvKey = keys.MakeFamilyKey(primaryIndexKey, uint32(family.ID))
   141  		// We need to ensure that column family 0 contains extra metadata, like composite primary key values.
   142  		// Additionally, the decoders expect that column family 0 is encoded with a TUPLE value tag, so we
   143  		// don't want to use the untagged value encoding.
   144  		if len(family.ColumnIDs) == 1 && family.ColumnIDs[0] == family.DefaultColumnID && family.ID != 0 {
   145  			// Storage optimization to store DefaultColumnID directly as a value. Also
   146  			// backwards compatible with the original BaseFormatVersion.
   147  
   148  			idx, ok := marshaledColIDMapping[family.DefaultColumnID]
   149  			if !ok {
   150  				continue
   151  			}
   152  
   153  			if marshaledValues[idx].RawBytes == nil {
   154  				if overwrite {
   155  					// If the new family contains a NULL value, then we must
   156  					// delete any pre-existing row.
   157  					insertDelFn(ctx, batch, kvKey, traceKV)
   158  				}
   159  			} else {
   160  				// We only output non-NULL values. Non-existent column keys are
   161  				// considered NULL during scanning and the row sentinel ensures we know
   162  				// the row exists.
   163  				putFn(ctx, batch, kvKey, &marshaledValues[idx], traceKV)
   164  			}
   165  
   166  			continue
   167  		}
   168  
   169  		rawValueBuf = rawValueBuf[:0]
   170  
   171  		var lastColID sqlbase.ColumnID
   172  		familySortedColumnIDs, ok := helper.sortedColumnFamily(family.ID)
   173  		if !ok {
   174  			return nil, errors.AssertionFailedf("invalid family sorted column id map")
   175  		}
   176  		for _, colID := range familySortedColumnIDs {
   177  			idx, ok := valColIDMapping[colID]
   178  			if !ok || values[idx] == tree.DNull {
   179  				// Column not being updated or inserted.
   180  				continue
   181  			}
   182  
   183  			if skip, err := helper.skipColumnInPK(colID, family.ID, values[idx]); err != nil {
   184  				return nil, err
   185  			} else if skip {
   186  				continue
   187  			}
   188  
   189  			col := &fetchedCols[idx]
   190  			if lastColID > col.ID {
   191  				return nil, errors.AssertionFailedf("cannot write column id %d after %d", col.ID, lastColID)
   192  			}
   193  			colIDDiff := col.ID - lastColID
   194  			lastColID = col.ID
   195  			var err error
   196  			rawValueBuf, err = sqlbase.EncodeTableValue(rawValueBuf, colIDDiff, values[idx], nil)
   197  			if err != nil {
   198  				return nil, err
   199  			}
   200  		}
   201  
   202  		if family.ID != 0 && len(rawValueBuf) == 0 {
   203  			if overwrite {
   204  				// The family might have already existed but every column in it is being
   205  				// set to NULL, so delete it.
   206  				insertDelFn(ctx, batch, kvKey, traceKV)
   207  			}
   208  		} else {
   209  			// Copy the contents of rawValueBuf into the roachpb.Value. This is
   210  			// a deep copy so rawValueBuf can be re-used by other calls to the
   211  			// function.
   212  			kvValue.SetTuple(rawValueBuf)
   213  			putFn(ctx, batch, kvKey, kvValue, traceKV)
   214  		}
   215  
   216  		// Release reference to roachpb.Key.
   217  		*kvKey = nil
   218  		// Prevent future calls to prepareInsertOrUpdateBatch from mutating
   219  		// the RawBytes in the kvValue we just added to the batch. Remember
   220  		// that we share the kvValue reference across calls to this function.
   221  		*kvValue = roachpb.Value{}
   222  	}
   223  
   224  	return rawValueBuf, nil
   225  }