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 }