@@ -180,39 +180,31 @@ func EncodeTimestamp(key []byte, walltime uint64, logical uint32) []byte {
180180 return key
181181}
182182
183- // DecodeEngineKey decodes an Engine key (the key that gets stored in Pebble).
184- // Returns:
185- //
186- // - roachKey: key prefix without the separator byte; corresponds to
187- // MVCCKey.Key / EngineKey.Key (roachpb.Key type).
188- //
189- // - untypedVersion: when the suffix does not correspond to a timestamp,
190- // untypedVersion is set to the suffix without the length
191- // byte; corresponds to EngineKey.Version.
192- //
193- // - wallTime, logicalTime: timestamp for the key, or 0 if the key does not
194- // correspond to a timestamp.
195- func DecodeEngineKey (
196- engineKey []byte ,
197- ) (roachKey []byte , untypedVersion []byte , wallTime uint64 , logicalTime uint32 ) {
198- tsLen := int (engineKey [len (engineKey )- 1 ])
199- tsStart := len (engineKey ) - tsLen
200- if tsStart < 0 {
201- if invariants .Enabled {
202- panic ("invalid length byte" )
203- }
204- return nil , nil , 0 , 0
183+ // DecodeEngineKey decodes the given bytes as an EngineKey.
184+ func DecodeEngineKey (b []byte ) (roachKey , version []byte , ok bool ) {
185+ if len (b ) == 0 {
186+ return nil , nil , false
205187 }
206- roachKeyEnd := tsStart - 1
207- if roachKeyEnd < 0 {
208- roachKeyEnd = 0
209- } else if invariants .Enabled && engineKey [roachKeyEnd ] != 0 {
210- panic ("invalid separator byte" )
188+ // Last byte is the version length + 1 when there is a version,
189+ // else it is 0.
190+ versionLen := int (b [len (b )- 1 ])
191+ if versionLen == 1 {
192+ // The key encodes an empty version, which is not valid.
193+ return nil , nil , false
211194 }
212-
213- roachKey = engineKey [:roachKeyEnd ]
214- untypedVersion , wallTime , logicalTime = DecodeSuffix (engineKey [tsStart :])
215- return roachKey , untypedVersion , wallTime , logicalTime
195+ // keyPartEnd points to the sentinel byte.
196+ keyPartEnd := len (b ) - 1 - versionLen
197+ if keyPartEnd < 0 || b [keyPartEnd ] != 0x00 {
198+ return nil , nil , false
199+ }
200+ // roachKey excludes the sentinel byte.
201+ roachKey = b [:keyPartEnd ]
202+ if versionLen > 0 {
203+ // version consists of the bytes after the sentinel and before the
204+ // length.
205+ version = b [keyPartEnd + 1 : len (b )- 1 ]
206+ }
207+ return roachKey , version , true
216208}
217209
218210// DecodeSuffix decodes the suffix of a key. If the suffix is a timestamp,
@@ -606,9 +598,9 @@ func (kw *cockroachKeyWriter) ComparePrev(key []byte) colblk.KeyComparison {
606598 lp := kw .roachKeys .UnsafeGet (kw .roachKeys .Rows () - 1 )
607599 cmpv .CommonPrefixLen = int32 (crbytes .CommonPrefix (lp , key [:cmpv .PrefixLen - 1 ]))
608600 if cmpv .CommonPrefixLen == cmpv .PrefixLen - 1 {
609- // Adjust CommonPrefixLen to include the sentinel byte,
601+ // Adjust CommonPrefixLen to include the sentinel byte.
610602 cmpv .CommonPrefixLen = cmpv .PrefixLen
611- cmpv .UserKeyComparison = int32 (CompareRangeSuffixes (key [cmpv .PrefixLen :], kw .prevSuffix ))
603+ cmpv .UserKeyComparison = int32 (ComparePointSuffixes (key [cmpv .PrefixLen :], kw .prevSuffix ))
612604 return cmpv
613605 }
614606 // The keys have different MVCC prefixes. We haven't determined which is
@@ -628,28 +620,48 @@ func (kw *cockroachKeyWriter) ComparePrev(key []byte) colblk.KeyComparison {
628620func (kw * cockroachKeyWriter ) WriteKey (
629621 row int , key []byte , keyPrefixLen , keyPrefixLenSharedWithPrev int32 ,
630622) {
623+ if len (key ) == 0 {
624+ panic (errors .AssertionFailedf ("empty key" ))
625+ }
626+ // Last byte is the version length + 1 when there is a version,
627+ // else it is 0.
628+ versionLen := int (key [len (key )- 1 ])
629+ if (len (key )- versionLen ) != int (keyPrefixLen ) || key [keyPrefixLen - 1 ] != 0x00 {
630+ panic (errors .AssertionFailedf ("invalid %d-byte key with %d-byte prefix (%q)" ,
631+ len (key ), keyPrefixLen , key ))
632+ }
631633 // TODO(jackson): Avoid copying the previous suffix.
632- // TODO(jackson): Use keyPrefixLen to speed up decoding.
633- roachKey , untypedVersion , wallTime , logicalTime := DecodeEngineKey (key )
634634 kw .prevSuffix = append (kw .prevSuffix [:0 ], key [keyPrefixLen :]... )
635+
635636 // When the roach key is the same, keyPrefixLenSharedWithPrev includes the
636637 // separator byte.
637- kw .roachKeys .Put (roachKey , min (int (keyPrefixLenSharedWithPrev ), len (roachKey )))
638- kw .wallTimes .Set (row , wallTime )
639- // The w.logicalTimes builder was initialized with InitWithDefault, so if we
640- // don't set a value, the column value is implicitly zero. We only need to
641- // Set anything for non-zero values.
642- if logicalTime > 0 {
643- kw .logicalTimes .Set (row , uint64 (logicalTime ))
644- }
645- kw .untypedVersions .Put (untypedVersion )
646- if wallTime != 0 || logicalTime != 0 {
638+ kw .roachKeys .Put (key [:keyPrefixLen - 1 ], min (int (keyPrefixLenSharedWithPrev ), int (keyPrefixLen )- 1 ))
639+
640+ // NB: The w.logicalTimes builder was initialized with InitWithDefault, so
641+ // if we don't set a value, the column value is implicitly zero. We only
642+ // need to Set anything for non-zero values.
643+ var wallTime uint64
644+ var untypedVersion []byte
645+ switch versionLen {
646+ case 0 :
647+ // No-op.
648+ kw .suffixTypes |= hasNonMVCCSuffixes
649+ case 9 :
647650 kw .suffixTypes |= hasMVCCSuffixes
648- } else if len (untypedVersion ) == 0 {
651+ wallTime = binary .BigEndian .Uint64 (key [keyPrefixLen : keyPrefixLen + 8 ])
652+ case 13 , 14 :
653+ kw .suffixTypes |= hasMVCCSuffixes
654+ wallTime = binary .BigEndian .Uint64 (key [keyPrefixLen : keyPrefixLen + 8 ])
655+ kw .logicalTimes .Set (row , uint64 (binary .BigEndian .Uint32 (key [keyPrefixLen + 8 :keyPrefixLen + 12 ])))
656+ // NOTE: byte 13 used to store the timestamp's synthetic bit, but this is no
657+ // longer consulted and can be ignored during decoding.
658+ default :
659+ // Not a MVCC timestamp.
649660 kw .suffixTypes |= hasEmptySuffixes
650- } else {
651- kw .suffixTypes |= hasNonMVCCSuffixes
661+ untypedVersion = key [keyPrefixLen :]
652662 }
663+ kw .wallTimes .Set (row , wallTime )
664+ kw .untypedVersions .Put (untypedVersion )
653665}
654666
655667func (kw * cockroachKeyWriter ) MaterializeKey (dst []byte , i int ) []byte {
@@ -744,23 +756,46 @@ func (ks *cockroachKeySeeker) init(d *colblk.DataBlockDecoder) {
744756
745757// IsLowerBound is part of the KeySeeker interface.
746758func (ks * cockroachKeySeeker ) IsLowerBound (k []byte , syntheticSuffix []byte ) bool {
747- roachKey , untypedVersion , wallTime , logicalTime := DecodeEngineKey (k )
748- if v := Compare (ks .roachKeys .UnsafeFirstSlice (), roachKey ); v != 0 {
759+ roachKey , version , ok := DecodeEngineKey (k )
760+ if ! ok {
761+ panic (errors .AssertionFailedf ("invalid key %q" , k ))
762+ }
763+ if v := bytes .Compare (ks .roachKeys .UnsafeFirstSlice (), roachKey ); v != 0 {
749764 return v > 0
750765 }
766+ // If there's a synthetic suffix, we ignore the block's suffix columns and
767+ // compare the key's suffix to the synthetic suffix.
751768 if len (syntheticSuffix ) > 0 {
752- return Compare (syntheticSuffix , k [len (roachKey )+ 1 :]) >= 0
769+ return ComparePointSuffixes (syntheticSuffix , k [len (roachKey )+ 1 :]) >= 0
753770 }
754- if len (untypedVersion ) > 0 {
771+ var wallTime uint64
772+ var logicalTime uint32
773+ switch len (version ) {
774+ case engineKeyNoVersion :
775+ case engineKeyVersionWallTimeLen :
776+ wallTime = binary .BigEndian .Uint64 (version [:8 ])
777+ case engineKeyVersionWallAndLogicalTimeLen , engineKeyVersionWallLogicalAndSyntheticTimeLen :
778+ wallTime = binary .BigEndian .Uint64 (version [:8 ])
779+ logicalTime = binary .BigEndian .Uint32 (version [8 :12 ])
780+ default :
781+ // The provided key `k` is not a MVCC key. Assert that the first key in
782+ // the block is also not an MVCC key. If it were, that would mean there
783+ // exists both a MVCC key and a non-MVCC key with the same prefix.
784+ //
785+ // TODO(jackson): Double check that we'll never produce index separators
786+ // that are invalid version lengths.
755787 if invariants .Enabled && ks .mvccWallTimes .At (0 ) != 0 {
756788 panic ("comparing timestamp with untyped suffix" )
757789 }
758- return Compare (ks .untypedVersions .At (0 ), untypedVersion ) >= 0
790+ return ComparePointSuffixes (ks .untypedVersions .At (0 ), version ) >= 0
759791 }
792+
793+ // NB: The sign comparison is inverted because suffixes are sorted such that
794+ // the largest timestamps are "smaller" in the lexicographical ordering.
760795 if v := cmp .Compare (ks .mvccWallTimes .At (0 ), wallTime ); v != 0 {
761- return v > 0
796+ return v < 0
762797 }
763- return cmp .Compare (uint32 (ks .mvccLogical .At (0 )), logicalTime ) > = 0
798+ return cmp .Compare (uint32 (ks .mvccLogical .At (0 )), logicalTime ) < = 0
764799}
765800
766801// SeekGE is part of the KeySeeker interface.
0 commit comments