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  }