Skip to content

Commit 01dcf57

Browse files
committed
base: make comparer tolerate empty keys
When synthetic prefix is used, we can trim the synthetic prefix from a given seek key and compare it with the "bare" key in a table. After trimming the key prefix can become "empty" (with just the terminator left). This change extends the comparer test suite to check that we can remove leading bytes from prefixes and adjusts the testkeys and crdb comparers. Fixes #3906
1 parent d73ab80 commit 01dcf57

File tree

3 files changed

+16
-22
lines changed

3 files changed

+16
-22
lines changed

internal/base/comparer.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ import (
1818
// Compare returns -1, 0, or +1 depending on whether a is 'less than', 'equal
1919
// to' or 'greater than' b.
2020
//
21-
// Both a and b must be valid keys.
21+
// Both a and b must be valid keys. Note that because of synthetic prefix
22+
// functionality, the Compare function can be called on a key (either from the
23+
// database or passed as an argument for an iterator operation) after the
24+
// synthetic prefix has been removed. In general, this implies that removing any
25+
// leading bytes from a prefix must yield another valid prefix.
2226
//
2327
// A key a is less than b if a's prefix is byte-wise less than b's prefix, or if
2428
// the prefixes are equal and a's suffix is less than b's suffix (according to
@@ -433,6 +437,14 @@ func CheckComparer(c *Comparer, prefixes [][]byte, suffixes [][]byte) error {
433437
return errors.Errorf("CompareSuffixes is inconsistent")
434438
}
435439

440+
n := len(prefixes)
441+
// Removing leading bytes from prefixes must yield valid prefixes.
442+
for i := 0; i < n; i++ {
443+
for j := 1; j < len(prefixes[i]); j++ {
444+
prefixes = append(prefixes, prefixes[i][j:])
445+
}
446+
}
447+
436448
// Check the split function.
437449
for _, p := range prefixes {
438450
for _, s := range suffixes {

internal/crdbtest/crdbtest.go

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -178,26 +178,22 @@ func DecodeTimestamp(mvccKey []byte) ([]byte, []byte, uint64, uint32) {
178178

179179
// Split implements base.Split for CockroachDB keys.
180180
func Split(key []byte) int {
181-
// TODO(radu): Pebble sometimes passes empty "keys" and we have to tolerate
182-
// them until we fix that.
183181
if len(key) == 0 {
184182
return 0
185183
}
186184

187185
// Last byte is the version length + 1 when there is a version, else it is
188186
// 0.
189187
versionLen := int(key[len(key)-1])
190-
if versionLen >= len(key) {
191-
panic(errors.AssertionFailedf("empty key"))
188+
if versionLen > len(key) {
189+
panic(errors.AssertionFailedf("invalid version length"))
192190
}
193191
return len(key) - versionLen
194192
}
195193

196194
// Compare compares cockroach keys, including the version (which could be MVCC
197195
// timestamps).
198196
func Compare(a, b []byte) int {
199-
// TODO(radu): Pebble sometimes passes empty "keys" and we have to tolerate
200-
// them until we fix that.
201197
if len(a) == 0 || len(b) == 0 {
202198
return cmp.Compare(len(a), len(b))
203199
}
@@ -208,11 +204,6 @@ func Compare(a, b []byte) int {
208204
// SplitMVCCKey instead of doing this.
209205
aEnd := len(a) - 1
210206
bEnd := len(b) - 1
211-
if aEnd < 0 || bEnd < 0 {
212-
// This should never happen unless there is some sort of corruption of
213-
// the keys.
214-
panic(errors.AssertionFailedf("malformed key: %x, %x", a, b))
215-
}
216207

217208
// Compute the index of the separator between the key and the version. If the
218209
// separator is found to be at -1 for both keys, then we are comparing bare

internal/testkeys/testkeys.go

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,6 @@ var Comparer = &base.Comparer{
115115
// value is smaller.
116116
func compare(a, b []byte) int {
117117
ai, bi := split(a), split(b)
118-
if ai == 0 && len(a) > 0 {
119-
panic(fmt.Sprintf("Compare called with bare suffix %s", a))
120-
}
121-
if bi == 0 && len(b) > 0 {
122-
panic(fmt.Sprintf("Compare called with bare suffix %s", b))
123-
}
124118
if v := bytes.Compare(a[:ai], b[:bi]); v != 0 {
125119
return v
126120
}
@@ -129,10 +123,7 @@ func compare(a, b []byte) int {
129123

130124
func split(a []byte) int {
131125
i := bytes.LastIndexByte(a, suffixDelim)
132-
if i == 0 {
133-
panic(fmt.Sprintf("Split called on bare suffix %q", a))
134-
}
135-
if i > 0 {
126+
if i >= 0 {
136127
return i
137128
}
138129
return len(a)

0 commit comments

Comments
 (0)