github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/row/helper.go (about) 1 // Copyright 2018 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 "sort" 15 16 "github.com/cockroachdb/cockroach/pkg/keys" 17 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 18 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 19 "github.com/cockroachdb/cockroach/pkg/util" 20 "github.com/cockroachdb/cockroach/pkg/util/encoding" 21 ) 22 23 // rowHelper has the common methods for table row manipulations. 24 type rowHelper struct { 25 Codec keys.SQLCodec 26 27 TableDesc *sqlbase.ImmutableTableDescriptor 28 // Secondary indexes. 29 Indexes []sqlbase.IndexDescriptor 30 indexEntries []sqlbase.IndexEntry 31 32 // Computed during initialization for pretty-printing. 33 primIndexValDirs []encoding.Direction 34 secIndexValDirs [][]encoding.Direction 35 36 // Computed and cached. 37 primaryIndexKeyPrefix []byte 38 primaryIndexCols map[sqlbase.ColumnID]struct{} 39 sortedColumnFamilies map[sqlbase.FamilyID][]sqlbase.ColumnID 40 } 41 42 func newRowHelper( 43 codec keys.SQLCodec, desc *sqlbase.ImmutableTableDescriptor, indexes []sqlbase.IndexDescriptor, 44 ) rowHelper { 45 rh := rowHelper{Codec: codec, TableDesc: desc, Indexes: indexes} 46 47 // Pre-compute the encoding directions of the index key values for 48 // pretty-printing in traces. 49 rh.primIndexValDirs = sqlbase.IndexKeyValDirs(&rh.TableDesc.PrimaryIndex) 50 51 rh.secIndexValDirs = make([][]encoding.Direction, len(rh.Indexes)) 52 for i := range rh.Indexes { 53 rh.secIndexValDirs[i] = sqlbase.IndexKeyValDirs(&rh.Indexes[i]) 54 } 55 56 return rh 57 } 58 59 // encodeIndexes encodes the primary and secondary index keys. The 60 // secondaryIndexEntries are only valid until the next call to encodeIndexes or 61 // encodeSecondaryIndexes. includeEmpty details whether the results should 62 // include empty secondary index k/v pairs. 63 func (rh *rowHelper) encodeIndexes( 64 colIDtoRowIndex map[sqlbase.ColumnID]int, 65 values []tree.Datum, 66 ignoreIndexes util.FastIntSet, 67 includeEmpty bool, 68 ) (primaryIndexKey []byte, secondaryIndexEntries []sqlbase.IndexEntry, err error) { 69 primaryIndexKey, err = rh.encodePrimaryIndex(colIDtoRowIndex, values) 70 if err != nil { 71 return nil, nil, err 72 } 73 secondaryIndexEntries, err = rh.encodeSecondaryIndexes(colIDtoRowIndex, values, ignoreIndexes, includeEmpty) 74 if err != nil { 75 return nil, nil, err 76 } 77 return primaryIndexKey, secondaryIndexEntries, nil 78 } 79 80 // encodePrimaryIndex encodes the primary index key. 81 func (rh *rowHelper) encodePrimaryIndex( 82 colIDtoRowIndex map[sqlbase.ColumnID]int, values []tree.Datum, 83 ) (primaryIndexKey []byte, err error) { 84 if rh.primaryIndexKeyPrefix == nil { 85 rh.primaryIndexKeyPrefix = sqlbase.MakeIndexKeyPrefix(rh.Codec, rh.TableDesc.TableDesc(), 86 rh.TableDesc.PrimaryIndex.ID) 87 } 88 primaryIndexKey, _, err = sqlbase.EncodeIndexKey( 89 rh.TableDesc.TableDesc(), &rh.TableDesc.PrimaryIndex, colIDtoRowIndex, values, rh.primaryIndexKeyPrefix) 90 return primaryIndexKey, err 91 } 92 93 // encodeSecondaryIndexes encodes the secondary index keys based on a row's 94 // values. 95 // 96 // The secondaryIndexEntries are only valid until the next call to encodeIndexes 97 // or encodeSecondaryIndexes, when they are overwritten. 98 // 99 // This function will not encode index entries for any index with an ID in 100 // ignoreIndexes. 101 // 102 // includeEmpty details whether the results should include empty secondary index 103 // k/v pairs. 104 func (rh *rowHelper) encodeSecondaryIndexes( 105 colIDtoRowIndex map[sqlbase.ColumnID]int, 106 values []tree.Datum, 107 ignoreIndexes util.FastIntSet, 108 includeEmpty bool, 109 ) (secondaryIndexEntries []sqlbase.IndexEntry, err error) { 110 if cap(rh.indexEntries) < len(rh.Indexes) { 111 rh.indexEntries = make([]sqlbase.IndexEntry, 0, len(rh.Indexes)) 112 } 113 114 rh.indexEntries = rh.indexEntries[:0] 115 116 for i := range rh.Indexes { 117 index := &rh.Indexes[i] 118 if !ignoreIndexes.Contains(int(index.ID)) { 119 entries, err := sqlbase.EncodeSecondaryIndex(rh.Codec, rh.TableDesc.TableDesc(), index, colIDtoRowIndex, values, includeEmpty) 120 if err != nil { 121 return nil, err 122 } 123 rh.indexEntries = append(rh.indexEntries, entries...) 124 } 125 } 126 127 return rh.indexEntries, nil 128 } 129 130 // skipColumnInPK returns true if the value at column colID does not need 131 // to be encoded because it is already part of the primary key. Composite 132 // datums are considered too, so a composite datum in a PK will return false. 133 // TODO(dan): This logic is common and being moved into TableDescriptor (see 134 // #6233). Once it is, use the shared one. 135 func (rh *rowHelper) skipColumnInPK( 136 colID sqlbase.ColumnID, family sqlbase.FamilyID, value tree.Datum, 137 ) (bool, error) { 138 if rh.primaryIndexCols == nil { 139 rh.primaryIndexCols = make(map[sqlbase.ColumnID]struct{}) 140 for _, colID := range rh.TableDesc.PrimaryIndex.ColumnIDs { 141 rh.primaryIndexCols[colID] = struct{}{} 142 } 143 } 144 if _, ok := rh.primaryIndexCols[colID]; !ok { 145 return false, nil 146 } 147 if cdatum, ok := value.(tree.CompositeDatum); ok { 148 // Composite columns are encoded in both the key and the value. 149 return !cdatum.IsComposite(), nil 150 } 151 // Skip primary key columns as their values are encoded in the key of 152 // each family. Family 0 is guaranteed to exist and acts as a 153 // sentinel. 154 return true, nil 155 } 156 157 func (rh *rowHelper) sortedColumnFamily(famID sqlbase.FamilyID) ([]sqlbase.ColumnID, bool) { 158 if rh.sortedColumnFamilies == nil { 159 rh.sortedColumnFamilies = make(map[sqlbase.FamilyID][]sqlbase.ColumnID, len(rh.TableDesc.Families)) 160 for i := range rh.TableDesc.Families { 161 family := &rh.TableDesc.Families[i] 162 colIDs := append([]sqlbase.ColumnID(nil), family.ColumnIDs...) 163 sort.Sort(sqlbase.ColumnIDs(colIDs)) 164 rh.sortedColumnFamilies[family.ID] = colIDs 165 } 166 } 167 colIDs, ok := rh.sortedColumnFamilies[famID] 168 return colIDs, ok 169 }