From f0d282f5f83c73759d210f3ead2b7f09da97dd55 Mon Sep 17 00:00:00 2001 From: Marty Schoch Date: Mon, 26 Oct 2015 16:14:29 -0400 Subject: [PATCH] add test case for seeing prefix iterators outside of range similar to #256 except for prefix iterators includes fix for boltdb and gtreap which had incorrect behavior --- index/store/boltdb/iterator.go | 10 ++++- index/store/boltdb/store_test.go | 6 +++ index/store/goleveldb/store_test.go | 6 +++ index/store/gtreap/iterator.go | 19 +++++++- index/store/gtreap/store_test.go | 6 +++ index/store/metrics/store_test.go | 6 +++ index/store/test/iterator.go | 68 +++++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 2 deletions(-) diff --git a/index/store/boltdb/iterator.go b/index/store/boltdb/iterator.go index 63bde9f5..84f54534 100644 --- a/index/store/boltdb/iterator.go +++ b/index/store/boltdb/iterator.go @@ -39,9 +39,17 @@ func (i *Iterator) updateValid() { } func (i *Iterator) Seek(k []byte) { - if bytes.Compare(k, i.start) < 0 { + if i.start != nil && bytes.Compare(k, i.start) < 0 { k = i.start } + if i.prefix != nil && !bytes.HasPrefix(k, i.prefix) { + if bytes.Compare(k, i.prefix) < 0 { + k = i.prefix + } else { + i.valid = false + return + } + } i.key, i.val = i.cursor.Seek(k) i.updateValid() } diff --git a/index/store/boltdb/store_test.go b/index/store/boltdb/store_test.go index 4abde11b..f0dc9da2 100644 --- a/index/store/boltdb/store_test.go +++ b/index/store/boltdb/store_test.go @@ -66,6 +66,12 @@ func TestBoltDBPrefixIterator(t *testing.T) { test.CommonTestPrefixIterator(t, s) } +func TestBoltDBPrefixIteratorSeek(t *testing.T) { + s := open(t, nil) + defer cleanup(t, s) + test.CommonTestPrefixIteratorSeek(t, s) +} + func TestBoltDBRangeIterator(t *testing.T) { s := open(t, nil) defer cleanup(t, s) diff --git a/index/store/goleveldb/store_test.go b/index/store/goleveldb/store_test.go index 6fceee78..c7984345 100644 --- a/index/store/goleveldb/store_test.go +++ b/index/store/goleveldb/store_test.go @@ -69,6 +69,12 @@ func TestGoLevelDBPrefixIterator(t *testing.T) { test.CommonTestPrefixIterator(t, s) } +func TestGoLevelDBPrefixIteratorSeek(t *testing.T) { + s := open(t, nil) + defer cleanup(t, s) + test.CommonTestPrefixIteratorSeek(t, s) +} + func TestGoLevelDBRangeIterator(t *testing.T) { s := open(t, nil) defer cleanup(t, s) diff --git a/index/store/gtreap/iterator.go b/index/store/gtreap/iterator.go index bc56eb34..9e596af0 100644 --- a/index/store/gtreap/iterator.go +++ b/index/store/gtreap/iterator.go @@ -36,9 +36,26 @@ type Iterator struct { } func (w *Iterator) Seek(k []byte) { - if bytes.Compare(k, w.start) < 0 { + if w.start != nil && bytes.Compare(k, w.start) < 0 { k = w.start } + if w.prefix != nil && !bytes.HasPrefix(k, w.prefix) { + if bytes.Compare(k, w.prefix) < 0 { + k = w.prefix + } else { + var end []byte + for i := len(w.prefix) - 1; i >= 0; i-- { + c := w.prefix[i] + if c < 0xff { + end = make([]byte, i+1) + copy(end, w.prefix) + end[i] = c + 1 + break + } + } + k = end + } + } w.restart(&Item{k: k}) } diff --git a/index/store/gtreap/store_test.go b/index/store/gtreap/store_test.go index b7686ee6..c42502c1 100644 --- a/index/store/gtreap/store_test.go +++ b/index/store/gtreap/store_test.go @@ -64,6 +64,12 @@ func TestGTreapPrefixIterator(t *testing.T) { test.CommonTestPrefixIterator(t, s) } +func TestGTreapPrefixIteratorSeek(t *testing.T) { + s := open(t, nil) + defer cleanup(t, s) + test.CommonTestPrefixIteratorSeek(t, s) +} + func TestGTreapRangeIterator(t *testing.T) { s := open(t, nil) defer cleanup(t, s) diff --git a/index/store/metrics/store_test.go b/index/store/metrics/store_test.go index f51d6d61..91667e8c 100644 --- a/index/store/metrics/store_test.go +++ b/index/store/metrics/store_test.go @@ -53,6 +53,12 @@ func TestMetricsPrefixIterator(t *testing.T) { test.CommonTestPrefixIterator(t, s) } +func TestMetricsPrefixIteratorSeek(t *testing.T) { + s := open(t, nil) + defer cleanup(t, s) + test.CommonTestPrefixIteratorSeek(t, s) +} + func TestMetricsRangeIterator(t *testing.T) { s := open(t, nil) defer cleanup(t, s) diff --git a/index/store/test/iterator.go b/index/store/test/iterator.go index 0d7d8992..be147edd 100644 --- a/index/store/test/iterator.go +++ b/index/store/test/iterator.go @@ -131,6 +131,74 @@ func CommonTestPrefixIterator(t *testing.T, s store.KVStore) { } } +func CommonTestPrefixIteratorSeek(t *testing.T, s store.KVStore) { + + data := []testRow{ + {[]byte("a"), []byte("val")}, + {[]byte("b1"), []byte("val")}, + {[]byte("b2"), []byte("val")}, + {[]byte("b3"), []byte("val")}, + {[]byte("c"), []byte("val")}, + } + + err := batchWriteRows(s, data) + if err != nil { + t.Fatal(err) + } + + // open a reader + reader, err := s.Reader() + if err != nil { + t.Fatal(err) + } + + // get an iterator on a central subset of the data + iter := reader.PrefixIterator([]byte("b")) + + // check that all keys have prefix + found := []string{} + for ; iter.Valid(); iter.Next() { + found = append(found, string(iter.Key())) + } + for _, f := range found { + if !strings.HasPrefix(f, "b") { + t.Errorf("got key '%s' that doesn't have correct prefix") + } + } + if len(found) != 3 { + t.Errorf("expected 3 keys with prefix, got %d", len(found)) + } + + // now try to seek before the prefix and repeat + found = []string{} + for iter.Seek([]byte("a")); iter.Valid(); iter.Next() { + found = append(found, string(iter.Key())) + } + for _, f := range found { + if !strings.HasPrefix(f, "b") { + t.Errorf("got key '%s' that doesn't have correct prefix") + } + } + if len(found) != 3 { + t.Errorf("expected 3 keys with prefix, got %d", len(found)) + } + + // now try to seek after the prefix and repeat + found = []string{} + for iter.Seek([]byte("c")); iter.Valid(); iter.Next() { + found = append(found, string(iter.Key())) + } + for _, f := range found { + if !strings.HasPrefix(f, "b") { + t.Errorf("got key '%s' that doesn't have correct prefix") + } + } + if len(found) != 0 { + t.Errorf("expected 0 keys with prefix, got %d", len(found)) + } + +} + func CommonTestRangeIterator(t *testing.T, s store.KVStore) { data := []testRow{