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

     1  // Copyright 2019 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  	"fmt"
    16  
    17  	"github.com/cockroachdb/cockroach/pkg/keys"
    18  	"github.com/cockroachdb/cockroach/pkg/kv"
    19  	"github.com/cockroachdb/cockroach/pkg/roachpb"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    21  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    22  	"github.com/cockroachdb/cockroach/pkg/util"
    23  	"github.com/cockroachdb/cockroach/pkg/util/log"
    24  	"github.com/cockroachdb/errors"
    25  )
    26  
    27  // Inserter abstracts the key/value operations for inserting table rows.
    28  type Inserter struct {
    29  	Helper                rowHelper
    30  	InsertCols            []sqlbase.ColumnDescriptor
    31  	InsertColIDtoRowIndex map[sqlbase.ColumnID]int
    32  	Fks                   fkExistenceCheckForInsert
    33  
    34  	// For allocation avoidance.
    35  	marshaled []roachpb.Value
    36  	key       roachpb.Key
    37  	valueBuf  []byte
    38  	value     roachpb.Value
    39  }
    40  
    41  // MakeInserter creates a Inserter for the given table.
    42  //
    43  // insertCols must contain every column in the primary key.
    44  func MakeInserter(
    45  	ctx context.Context,
    46  	txn *kv.Txn,
    47  	codec keys.SQLCodec,
    48  	tableDesc *sqlbase.ImmutableTableDescriptor,
    49  	insertCols []sqlbase.ColumnDescriptor,
    50  	checkFKs checkFKConstraints,
    51  	fkTables FkTableMetadata,
    52  	alloc *sqlbase.DatumAlloc,
    53  ) (Inserter, error) {
    54  	ri := Inserter{
    55  		Helper:                newRowHelper(codec, tableDesc, tableDesc.WritableIndexes()),
    56  		InsertCols:            insertCols,
    57  		InsertColIDtoRowIndex: ColIDtoRowIndexFromCols(insertCols),
    58  		marshaled:             make([]roachpb.Value, len(insertCols)),
    59  	}
    60  
    61  	for i, col := range tableDesc.PrimaryIndex.ColumnIDs {
    62  		if _, ok := ri.InsertColIDtoRowIndex[col]; !ok {
    63  			return Inserter{}, fmt.Errorf("missing %q primary key column", tableDesc.PrimaryIndex.ColumnNames[i])
    64  		}
    65  	}
    66  
    67  	if checkFKs == CheckFKs {
    68  		var err error
    69  		if ri.Fks, err = makeFkExistenceCheckHelperForInsert(ctx, txn, codec, tableDesc, fkTables,
    70  			ri.InsertColIDtoRowIndex, alloc); err != nil {
    71  			return ri, err
    72  		}
    73  	}
    74  	return ri, nil
    75  }
    76  
    77  // insertCPutFn is used by insertRow when conflicts (i.e. the key already exists)
    78  // should generate errors.
    79  func insertCPutFn(
    80  	ctx context.Context, b putter, key *roachpb.Key, value *roachpb.Value, traceKV bool,
    81  ) {
    82  	// TODO(dan): We want do this V(2) log everywhere in sql. Consider making a
    83  	// client.Batch wrapper instead of inlining it everywhere.
    84  	if traceKV {
    85  		log.VEventfDepth(ctx, 1, 2, "CPut %s -> %s", *key, value.PrettyPrint())
    86  	}
    87  	b.CPut(key, value, nil /* expValue */)
    88  }
    89  
    90  // insertPutFn is used by insertRow when conflicts should be ignored.
    91  func insertPutFn(
    92  	ctx context.Context, b putter, key *roachpb.Key, value *roachpb.Value, traceKV bool,
    93  ) {
    94  	if traceKV {
    95  		log.VEventfDepth(ctx, 1, 2, "Put %s -> %s", *key, value.PrettyPrint())
    96  	}
    97  	b.Put(key, value)
    98  }
    99  
   100  // insertDelFn is used by insertRow to delete existing rows.
   101  func insertDelFn(ctx context.Context, b putter, key *roachpb.Key, traceKV bool) {
   102  	if traceKV {
   103  		log.VEventfDepth(ctx, 1, 2, "Del %s", *key)
   104  	}
   105  	b.Del(key)
   106  }
   107  
   108  // insertPutFn is used by insertRow when conflicts should be ignored.
   109  func insertInvertedPutFn(
   110  	ctx context.Context, b putter, key *roachpb.Key, value *roachpb.Value, traceKV bool,
   111  ) {
   112  	if traceKV {
   113  		log.VEventfDepth(ctx, 1, 2, "InitPut %s -> %s", *key, value.PrettyPrint())
   114  	}
   115  	b.InitPut(key, value, false)
   116  }
   117  
   118  type putter interface {
   119  	CPut(key, value interface{}, expValue *roachpb.Value)
   120  	Put(key, value interface{})
   121  	InitPut(key, value interface{}, failOnTombstones bool)
   122  	Del(key ...interface{})
   123  }
   124  
   125  // InsertRow adds to the batch the kv operations necessary to insert a table row
   126  // with the given values.
   127  func (ri *Inserter) InsertRow(
   128  	ctx context.Context,
   129  	b putter,
   130  	values []tree.Datum,
   131  	ignoreIndexes util.FastIntSet,
   132  	overwrite bool,
   133  	checkFKs checkFKConstraints,
   134  	traceKV bool,
   135  ) error {
   136  	if len(values) != len(ri.InsertCols) {
   137  		return errors.Errorf("got %d values but expected %d", len(values), len(ri.InsertCols))
   138  	}
   139  
   140  	putFn := insertCPutFn
   141  	if overwrite {
   142  		putFn = insertPutFn
   143  	}
   144  
   145  	// Encode the values to the expected column type. This needs to
   146  	// happen before index encoding because certain datum types (i.e. tuple)
   147  	// cannot be used as index values.
   148  	for i, val := range values {
   149  		// Make sure the value can be written to the column before proceeding.
   150  		var err error
   151  		if ri.marshaled[i], err = sqlbase.MarshalColumnValue(&ri.InsertCols[i], val); err != nil {
   152  			return err
   153  		}
   154  	}
   155  
   156  	if ri.Fks.checker != nil && checkFKs == CheckFKs {
   157  		if err := ri.Fks.addAllIdxChecks(ctx, values, traceKV); err != nil {
   158  			return err
   159  		}
   160  		if err := ri.Fks.checker.runCheck(ctx, nil, values); err != nil {
   161  			return err
   162  		}
   163  	}
   164  
   165  	// We don't want to insert any empty k/v's, so set includeEmpty to false.
   166  	// Consider the following case:
   167  	// TABLE t (
   168  	//   x INT PRIMARY KEY, y INT, z INT, w INT,
   169  	//   INDEX (y) STORING (z, w),
   170  	//   FAMILY (x), FAMILY (y), FAMILY (z), FAMILY (w)
   171  	//)
   172  	// If we are to insert row (1, 2, 3, NULL), the k/v pair for
   173  	// index i that encodes column w would have an empty value,
   174  	// because w is null, and the sole resident of that family.
   175  	// We don't want to insert empty k/v's like this, so we
   176  	// set includeEmpty to false.
   177  	primaryIndexKey, secondaryIndexEntries, err := ri.Helper.encodeIndexes(
   178  		ri.InsertColIDtoRowIndex, values, ignoreIndexes, false /* includeEmpty */)
   179  	if err != nil {
   180  		return err
   181  	}
   182  
   183  	// Add the new values.
   184  	ri.valueBuf, err = prepareInsertOrUpdateBatch(ctx, b,
   185  		&ri.Helper, primaryIndexKey, ri.InsertCols,
   186  		values, ri.InsertColIDtoRowIndex,
   187  		ri.marshaled, ri.InsertColIDtoRowIndex,
   188  		&ri.key, &ri.value, ri.valueBuf, putFn, overwrite, traceKV)
   189  	if err != nil {
   190  		return err
   191  	}
   192  
   193  	putFn = insertInvertedPutFn
   194  	for i := range secondaryIndexEntries {
   195  		e := &secondaryIndexEntries[i]
   196  		putFn(ctx, b, &e.Key, &e.Value, traceKV)
   197  	}
   198  
   199  	return nil
   200  }