1 // Copyright (c) 2011 The LevelDB Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. See the AUTHORS file for names of contributors.
5 #include "leveldb/table.h"
9 #include "db/dbformat.h"
10 #include "db/memtable.h"
11 #include "db/write_batch_internal.h"
12 #include "leveldb/db.h"
13 #include "leveldb/env.h"
14 #include "leveldb/iterator.h"
15 #include "leveldb/table_builder.h"
16 #include "table/block.h"
17 #include "table/block_builder.h"
18 #include "table/format.h"
19 #include "util/random.h"
20 #include "util/testharness.h"
21 #include "util/testutil.h"
25 // Return reverse of "key".
26 // Used to test non-lexicographic comparators.
27 static std::string
Reverse(const Slice
& key
) {
28 std::string
str(key
.ToString());
30 for (std::string::reverse_iterator rit
= str
.rbegin();
31 rit
!= str
.rend(); ++rit
) {
38 class ReverseKeyComparator
: public Comparator
{
40 virtual const char* Name() const {
41 return "leveldb.ReverseBytewiseComparator";
44 virtual int Compare(const Slice
& a
, const Slice
& b
) const {
45 return BytewiseComparator()->Compare(Reverse(a
), Reverse(b
));
48 virtual void FindShortestSeparator(
50 const Slice
& limit
) const {
51 std::string s
= Reverse(*start
);
52 std::string l
= Reverse(limit
);
53 BytewiseComparator()->FindShortestSeparator(&s
, l
);
57 virtual void FindShortSuccessor(std::string
* key
) const {
58 std::string s
= Reverse(*key
);
59 BytewiseComparator()->FindShortSuccessor(&s
);
64 static ReverseKeyComparator reverse_key_comparator
;
66 static void Increment(const Comparator
* cmp
, std::string
* key
) {
67 if (cmp
== BytewiseComparator()) {
70 assert(cmp
== &reverse_key_comparator
);
71 std::string rev
= Reverse(*key
);
77 // An STL comparator that uses a Comparator
80 const Comparator
* cmp
;
82 STLLessThan() : cmp(BytewiseComparator()) { }
83 STLLessThan(const Comparator
* c
) : cmp(c
) { }
84 bool operator()(const std::string
& a
, const std::string
& b
) const {
85 return cmp
->Compare(Slice(a
), Slice(b
)) < 0;
90 class StringSink
: public WritableFile
{
94 const std::string
& contents() const { return contents_
; }
96 virtual Status
Close() { return Status::OK(); }
97 virtual Status
Flush() { return Status::OK(); }
98 virtual Status
Sync() { return Status::OK(); }
100 virtual Status
Append(const Slice
& data
) {
101 contents_
.append(data
.data(), data
.size());
106 std::string contents_
;
110 class StringSource
: public RandomAccessFile
{
112 StringSource(const Slice
& contents
)
113 : contents_(contents
.data(), contents
.size()) {
116 virtual ~StringSource() { }
118 uint64_t Size() const { return contents_
.size(); }
120 virtual Status
Read(uint64_t offset
, size_t n
, Slice
* result
,
121 char* scratch
) const {
122 if (offset
> contents_
.size()) {
123 return Status::InvalidArgument("invalid Read offset");
125 if (offset
+ n
> contents_
.size()) {
126 n
= contents_
.size() - offset
;
128 memcpy(scratch
, &contents_
[offset
], n
);
129 *result
= Slice(scratch
, n
);
134 std::string contents_
;
137 typedef std::map
<std::string
, std::string
, STLLessThan
> KVMap
;
139 // Helper class for tests to unify the interface between
140 // BlockBuilder/TableBuilder and Block/Table.
143 explicit Constructor(const Comparator
* cmp
) : data_(STLLessThan(cmp
)) { }
144 virtual ~Constructor() { }
146 void Add(const std::string
& key
, const Slice
& value
) {
147 data_
[key
] = value
.ToString();
150 // Finish constructing the data structure with all the keys that have
151 // been added so far. Returns the keys in sorted order in "*keys"
152 // and stores the key/value pairs in "*kvmap"
153 void Finish(const Options
& options
,
154 std::vector
<std::string
>* keys
,
158 for (KVMap::const_iterator it
= data_
.begin();
161 keys
->push_back(it
->first
);
164 Status s
= FinishImpl(options
, *kvmap
);
165 ASSERT_TRUE(s
.ok()) << s
.ToString();
168 // Construct the data structure from the data in "data"
169 virtual Status
FinishImpl(const Options
& options
, const KVMap
& data
) = 0;
171 virtual Iterator
* NewIterator() const = 0;
173 virtual const KVMap
& data() { return data_
; }
175 virtual DB
* db() const { return NULL
; } // Overridden in DBConstructor
181 class BlockConstructor
: public Constructor
{
183 explicit BlockConstructor(const Comparator
* cmp
)
187 ~BlockConstructor() {
190 virtual Status
FinishImpl(const Options
& options
, const KVMap
& data
) {
193 BlockBuilder
builder(&options
);
195 for (KVMap::const_iterator it
= data
.begin();
198 builder
.Add(it
->first
, it
->second
);
201 data_
= builder
.Finish().ToString();
202 BlockContents contents
;
203 contents
.data
= data_
;
204 contents
.cachable
= false;
205 contents
.heap_allocated
= false;
206 block_
= new Block(contents
);
209 virtual Iterator
* NewIterator() const {
210 return block_
->NewIterator(comparator_
);
214 const Comparator
* comparator_
;
221 class TableConstructor
: public Constructor
{
223 TableConstructor(const Comparator
* cmp
)
225 source_(NULL
), table_(NULL
) {
227 ~TableConstructor() {
230 virtual Status
FinishImpl(const Options
& options
, const KVMap
& data
) {
233 TableBuilder
builder(options
, &sink
);
235 for (KVMap::const_iterator it
= data
.begin();
238 builder
.Add(it
->first
, it
->second
);
239 ASSERT_TRUE(builder
.status().ok());
241 Status s
= builder
.Finish();
242 ASSERT_TRUE(s
.ok()) << s
.ToString();
244 ASSERT_EQ(sink
.contents().size(), builder
.FileSize());
247 source_
= new StringSource(sink
.contents());
248 Options table_options
;
249 table_options
.comparator
= options
.comparator
;
250 return Table::Open(table_options
, source_
, sink
.contents().size(), &table_
);
253 virtual Iterator
* NewIterator() const {
254 return table_
->NewIterator(ReadOptions());
257 uint64_t ApproximateOffsetOf(const Slice
& key
) const {
258 return table_
->ApproximateOffsetOf(key
);
269 StringSource
* source_
;
275 // A helper class that converts internal format keys into user keys
276 class KeyConvertingIterator
: public Iterator
{
278 explicit KeyConvertingIterator(Iterator
* iter
) : iter_(iter
) { }
279 virtual ~KeyConvertingIterator() { delete iter_
; }
280 virtual bool Valid() const { return iter_
->Valid(); }
281 virtual void Seek(const Slice
& target
) {
282 ParsedInternalKey
ikey(target
, kMaxSequenceNumber
, kTypeValue
);
284 AppendInternalKey(&encoded
, ikey
);
285 iter_
->Seek(encoded
);
287 virtual void SeekToFirst() { iter_
->SeekToFirst(); }
288 virtual void SeekToLast() { iter_
->SeekToLast(); }
289 virtual void Next() { iter_
->Next(); }
290 virtual void Prev() { iter_
->Prev(); }
292 virtual Slice
key() const {
294 ParsedInternalKey key
;
295 if (!ParseInternalKey(iter_
->key(), &key
)) {
296 status_
= Status::Corruption("malformed internal key");
297 return Slice("corrupted key");
302 virtual Slice
value() const { return iter_
->value(); }
303 virtual Status
status() const {
304 return status_
.ok() ? iter_
->status() : status_
;
308 mutable Status status_
;
311 // No copying allowed
312 KeyConvertingIterator(const KeyConvertingIterator
&);
313 void operator=(const KeyConvertingIterator
&);
316 class MemTableConstructor
: public Constructor
{
318 explicit MemTableConstructor(const Comparator
* cmp
)
320 internal_comparator_(cmp
) {
321 memtable_
= new MemTable(internal_comparator_
);
324 ~MemTableConstructor() {
327 virtual Status
FinishImpl(const Options
& options
, const KVMap
& data
) {
329 memtable_
= new MemTable(internal_comparator_
);
332 for (KVMap::const_iterator it
= data
.begin();
335 memtable_
->Add(seq
, kTypeValue
, it
->first
, it
->second
);
340 virtual Iterator
* NewIterator() const {
341 return new KeyConvertingIterator(memtable_
->NewIterator());
345 InternalKeyComparator internal_comparator_
;
349 class DBConstructor
: public Constructor
{
351 explicit DBConstructor(const Comparator
* cmp
)
360 virtual Status
FinishImpl(const Options
& options
, const KVMap
& data
) {
364 for (KVMap::const_iterator it
= data
.begin();
368 batch
.Put(it
->first
, it
->second
);
369 ASSERT_TRUE(db_
->Write(WriteOptions(), &batch
).ok());
373 virtual Iterator
* NewIterator() const {
374 return db_
->NewIterator(ReadOptions());
377 virtual DB
* db() const { return db_
; }
381 std::string name
= test::TmpDir() + "/table_testdb";
384 options
.comparator
= comparator_
;
385 Status status
= DestroyDB(name
, options
);
386 ASSERT_TRUE(status
.ok()) << status
.ToString();
388 options
.create_if_missing
= true;
389 options
.error_if_exists
= true;
390 options
.write_buffer_size
= 10000; // Something small to force merging
391 status
= DB::Open(options
, name
, &db_
);
392 ASSERT_TRUE(status
.ok()) << status
.ToString();
395 const Comparator
* comparator_
;
408 bool reverse_compare
;
409 int restart_interval
;
412 static const TestArgs kTestArgList
[] = {
413 { TABLE_TEST
, false, 16 },
414 { TABLE_TEST
, false, 1 },
415 { TABLE_TEST
, false, 1024 },
416 { TABLE_TEST
, true, 16 },
417 { TABLE_TEST
, true, 1 },
418 { TABLE_TEST
, true, 1024 },
420 { BLOCK_TEST
, false, 16 },
421 { BLOCK_TEST
, false, 1 },
422 { BLOCK_TEST
, false, 1024 },
423 { BLOCK_TEST
, true, 16 },
424 { BLOCK_TEST
, true, 1 },
425 { BLOCK_TEST
, true, 1024 },
427 // Restart interval does not matter for memtables
428 { MEMTABLE_TEST
, false, 16 },
429 { MEMTABLE_TEST
, true, 16 },
431 // Do not bother with restart interval variations for DB
432 { DB_TEST
, false, 16 },
433 { DB_TEST
, true, 16 },
435 static const int kNumTestArgs
= sizeof(kTestArgList
) / sizeof(kTestArgList
[0]);
439 Harness() : constructor_(NULL
) { }
441 void Init(const TestArgs
& args
) {
444 options_
= Options();
446 options_
.block_restart_interval
= args
.restart_interval
;
447 // Use shorter block size for tests to exercise block boundary
449 options_
.block_size
= 256;
450 if (args
.reverse_compare
) {
451 options_
.comparator
= &reverse_key_comparator
;
455 constructor_
= new TableConstructor(options_
.comparator
);
458 constructor_
= new BlockConstructor(options_
.comparator
);
461 constructor_
= new MemTableConstructor(options_
.comparator
);
464 constructor_
= new DBConstructor(options_
.comparator
);
473 void Add(const std::string
& key
, const std::string
& value
) {
474 constructor_
->Add(key
, value
);
477 void Test(Random
* rnd
) {
478 std::vector
<std::string
> keys
;
480 constructor_
->Finish(options_
, &keys
, &data
);
482 TestForwardScan(keys
, data
);
483 TestBackwardScan(keys
, data
);
484 TestRandomAccess(rnd
, keys
, data
);
487 void TestForwardScan(const std::vector
<std::string
>& keys
,
489 Iterator
* iter
= constructor_
->NewIterator();
490 ASSERT_TRUE(!iter
->Valid());
492 for (KVMap::const_iterator model_iter
= data
.begin();
493 model_iter
!= data
.end();
495 ASSERT_EQ(ToString(data
, model_iter
), ToString(iter
));
498 ASSERT_TRUE(!iter
->Valid());
502 void TestBackwardScan(const std::vector
<std::string
>& keys
,
504 Iterator
* iter
= constructor_
->NewIterator();
505 ASSERT_TRUE(!iter
->Valid());
507 for (KVMap::const_reverse_iterator model_iter
= data
.rbegin();
508 model_iter
!= data
.rend();
510 ASSERT_EQ(ToString(data
, model_iter
), ToString(iter
));
513 ASSERT_TRUE(!iter
->Valid());
517 void TestRandomAccess(Random
* rnd
,
518 const std::vector
<std::string
>& keys
,
520 static const bool kVerbose
= false;
521 Iterator
* iter
= constructor_
->NewIterator();
522 ASSERT_TRUE(!iter
->Valid());
523 KVMap::const_iterator model_iter
= data
.begin();
524 if (kVerbose
) fprintf(stderr
, "---\n");
525 for (int i
= 0; i
< 200; i
++) {
526 const int toss
= rnd
->Uniform(5);
530 if (kVerbose
) fprintf(stderr
, "Next\n");
533 ASSERT_EQ(ToString(data
, model_iter
), ToString(iter
));
539 if (kVerbose
) fprintf(stderr
, "SeekToFirst\n");
541 model_iter
= data
.begin();
542 ASSERT_EQ(ToString(data
, model_iter
), ToString(iter
));
547 std::string key
= PickRandomKey(rnd
, keys
);
548 model_iter
= data
.lower_bound(key
);
549 if (kVerbose
) fprintf(stderr
, "Seek '%s'\n",
550 EscapeString(key
).c_str());
551 iter
->Seek(Slice(key
));
552 ASSERT_EQ(ToString(data
, model_iter
), ToString(iter
));
558 if (kVerbose
) fprintf(stderr
, "Prev\n");
560 if (model_iter
== data
.begin()) {
561 model_iter
= data
.end(); // Wrap around to invalid value
565 ASSERT_EQ(ToString(data
, model_iter
), ToString(iter
));
571 if (kVerbose
) fprintf(stderr
, "SeekToLast\n");
574 model_iter
= data
.end();
576 std::string last
= data
.rbegin()->first
;
577 model_iter
= data
.lower_bound(last
);
579 ASSERT_EQ(ToString(data
, model_iter
), ToString(iter
));
587 std::string
ToString(const KVMap
& data
, const KVMap::const_iterator
& it
) {
588 if (it
== data
.end()) {
591 return "'" + it
->first
+ "->" + it
->second
+ "'";
595 std::string
ToString(const KVMap
& data
,
596 const KVMap::const_reverse_iterator
& it
) {
597 if (it
== data
.rend()) {
600 return "'" + it
->first
+ "->" + it
->second
+ "'";
604 std::string
ToString(const Iterator
* it
) {
608 return "'" + it
->key().ToString() + "->" + it
->value().ToString() + "'";
612 std::string
PickRandomKey(Random
* rnd
, const std::vector
<std::string
>& keys
) {
616 const int index
= rnd
->Uniform(keys
.size());
617 std::string result
= keys
[index
];
618 switch (rnd
->Uniform(3)) {
620 // Return an existing key
623 // Attempt to return something smaller than an existing key
624 if (result
.size() > 0 && result
[result
.size()-1] > '\0') {
625 result
[result
.size()-1]--;
630 // Return something larger than an existing key
631 Increment(options_
.comparator
, &result
);
639 // Returns NULL if not running against a DB
640 DB
* db() const { return constructor_
->db(); }
644 Constructor
* constructor_
;
647 // Test empty table/block.
648 TEST(Harness
, Empty
) {
649 for (int i
= 0; i
< kNumTestArgs
; i
++) {
650 Init(kTestArgList
[i
]);
651 Random
rnd(test::RandomSeed() + 1);
656 // Special test for a block with no restart entries. The C++ leveldb
657 // code never generates such blocks, but the Java version of leveldb
659 TEST(Harness
, ZeroRestartPointsInBlock
) {
660 char data
[sizeof(uint32_t)];
661 memset(data
, 0, sizeof(data
));
662 BlockContents contents
;
663 contents
.data
= Slice(data
, sizeof(data
));
664 contents
.cachable
= false;
665 contents
.heap_allocated
= false;
666 Block
block(contents
);
667 Iterator
* iter
= block
.NewIterator(BytewiseComparator());
669 ASSERT_TRUE(!iter
->Valid());
671 ASSERT_TRUE(!iter
->Valid());
673 ASSERT_TRUE(!iter
->Valid());
677 // Test the empty key
678 TEST(Harness
, SimpleEmptyKey
) {
679 for (int i
= 0; i
< kNumTestArgs
; i
++) {
680 Init(kTestArgList
[i
]);
681 Random
rnd(test::RandomSeed() + 1);
687 TEST(Harness
, SimpleSingle
) {
688 for (int i
= 0; i
< kNumTestArgs
; i
++) {
689 Init(kTestArgList
[i
]);
690 Random
rnd(test::RandomSeed() + 2);
696 TEST(Harness
, SimpleMulti
) {
697 for (int i
= 0; i
< kNumTestArgs
; i
++) {
698 Init(kTestArgList
[i
]);
699 Random
rnd(test::RandomSeed() + 3);
707 TEST(Harness
, SimpleSpecialKey
) {
708 for (int i
= 0; i
< kNumTestArgs
; i
++) {
709 Init(kTestArgList
[i
]);
710 Random
rnd(test::RandomSeed() + 4);
711 Add("\xff\xff", "v3");
716 TEST(Harness
, Randomized
) {
717 for (int i
= 0; i
< kNumTestArgs
; i
++) {
718 Init(kTestArgList
[i
]);
719 Random
rnd(test::RandomSeed() + 5);
720 for (int num_entries
= 0; num_entries
< 2000;
721 num_entries
+= (num_entries
< 50 ? 1 : 200)) {
722 if ((num_entries
% 10) == 0) {
723 fprintf(stderr
, "case %d of %d: num_entries = %d\n",
724 (i
+ 1), int(kNumTestArgs
), num_entries
);
726 for (int e
= 0; e
< num_entries
; e
++) {
728 Add(test::RandomKey(&rnd
, rnd
.Skewed(4)),
729 test::RandomString(&rnd
, rnd
.Skewed(5), &v
).ToString());
736 TEST(Harness
, RandomizedLongDB
) {
737 Random
rnd(test::RandomSeed());
738 TestArgs args
= { DB_TEST
, false, 16 };
740 int num_entries
= 100000;
741 for (int e
= 0; e
< num_entries
; e
++) {
743 Add(test::RandomKey(&rnd
, rnd
.Skewed(4)),
744 test::RandomString(&rnd
, rnd
.Skewed(5), &v
).ToString());
748 // We must have created enough data to force merging
750 for (int level
= 0; level
< config::kNumLevels
; level
++) {
753 snprintf(name
, sizeof(name
), "leveldb.num-files-at-level%d", level
);
754 ASSERT_TRUE(db()->GetProperty(name
, &value
));
755 files
+= atoi(value
.c_str());
760 class MemTableTest
{ };
762 TEST(MemTableTest
, Simple
) {
763 InternalKeyComparator
cmp(BytewiseComparator());
764 MemTable
* memtable
= new MemTable(cmp
);
767 WriteBatchInternal::SetSequence(&batch
, 100);
768 batch
.Put(std::string("k1"), std::string("v1"));
769 batch
.Put(std::string("k2"), std::string("v2"));
770 batch
.Put(std::string("k3"), std::string("v3"));
771 batch
.Put(std::string("largekey"), std::string("vlarge"));
772 ASSERT_TRUE(WriteBatchInternal::InsertInto(&batch
, memtable
).ok());
774 Iterator
* iter
= memtable
->NewIterator();
776 while (iter
->Valid()) {
777 fprintf(stderr
, "key: '%s' -> '%s'\n",
778 iter
->key().ToString().c_str(),
779 iter
->value().ToString().c_str());
787 static bool Between(uint64_t val
, uint64_t low
, uint64_t high
) {
788 bool result
= (val
>= low
) && (val
<= high
);
790 fprintf(stderr
, "Value %llu is not in range [%llu, %llu]\n",
791 (unsigned long long)(val
),
792 (unsigned long long)(low
),
793 (unsigned long long)(high
));
800 TEST(TableTest
, ApproximateOffsetOfPlain
) {
801 TableConstructor
c(BytewiseComparator());
802 c
.Add("k01", "hello");
803 c
.Add("k02", "hello2");
804 c
.Add("k03", std::string(10000, 'x'));
805 c
.Add("k04", std::string(200000, 'x'));
806 c
.Add("k05", std::string(300000, 'x'));
807 c
.Add("k06", "hello3");
808 c
.Add("k07", std::string(100000, 'x'));
809 std::vector
<std::string
> keys
;
812 options
.block_size
= 1024;
813 options
.compression
= kNoCompression
;
814 c
.Finish(options
, &keys
, &kvmap
);
816 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("abc"), 0, 0));
817 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k01"), 0, 0));
818 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k01a"), 0, 0));
819 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k02"), 0, 0));
820 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k03"), 0, 0));
821 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k04"), 10000, 11000));
822 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k04a"), 210000, 211000));
823 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k05"), 210000, 211000));
824 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k06"), 510000, 511000));
825 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k07"), 510000, 511000));
826 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("xyz"), 610000, 612000));
830 static bool SnappyCompressionSupported() {
832 Slice in
= "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
833 return port::Snappy_Compress(in
.data(), in
.size(), &out
);
836 TEST(TableTest
, ApproximateOffsetOfCompressed
) {
837 if (!SnappyCompressionSupported()) {
838 fprintf(stderr
, "skipping compression tests\n");
843 TableConstructor
c(BytewiseComparator());
845 c
.Add("k01", "hello");
846 c
.Add("k02", test::CompressibleString(&rnd
, 0.25, 10000, &tmp
));
847 c
.Add("k03", "hello3");
848 c
.Add("k04", test::CompressibleString(&rnd
, 0.25, 10000, &tmp
));
849 std::vector
<std::string
> keys
;
852 options
.block_size
= 1024;
853 options
.compression
= kSnappyCompression
;
854 c
.Finish(options
, &keys
, &kvmap
);
856 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("abc"), 0, 0));
857 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k01"), 0, 0));
858 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k02"), 0, 0));
859 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k03"), 2000, 3000));
860 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("k04"), 2000, 3000));
861 ASSERT_TRUE(Between(c
.ApproximateOffsetOf("xyz"), 4000, 6000));
864 } // namespace leveldb
866 int main(int argc
, char** argv
) {
867 return leveldb::test::RunAllTests();