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 }