1 // Copyright (c) 2011-present, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
6 #include "table/mock_table.h"
8 #include "db/dbformat.h"
9 #include "env/composite_env_wrapper.h"
10 #include "file/random_access_file_reader.h"
11 #include "port/port.h"
12 #include "rocksdb/table_properties.h"
13 #include "table/get_context.h"
14 #include "util/coding.h"
16 namespace ROCKSDB_NAMESPACE
{
19 KVVector
MakeMockFile(std::initializer_list
<KVPair
> l
) { return KVVector(l
); }
21 void SortKVVector(KVVector
* kv_vector
, const Comparator
* ucmp
) {
22 InternalKeyComparator
icmp(ucmp
);
23 std::sort(kv_vector
->begin(), kv_vector
->end(),
24 [icmp
](KVPair a
, KVPair b
) -> bool {
25 return icmp
.Compare(a
.first
, b
.first
) < 0;
29 class MockTableReader
: public TableReader
{
31 explicit MockTableReader(const KVVector
& table
) : table_(table
) {}
33 InternalIterator
* NewIterator(const ReadOptions
&,
34 const SliceTransform
* prefix_extractor
,
35 Arena
* arena
, bool skip_filters
,
36 TableReaderCaller caller
,
37 size_t compaction_readahead_size
= 0,
38 bool allow_unprepared_value
= false) override
;
40 Status
Get(const ReadOptions
& readOptions
, const Slice
& key
,
41 GetContext
* get_context
, const SliceTransform
* prefix_extractor
,
42 bool skip_filters
= false) override
;
44 uint64_t ApproximateOffsetOf(const Slice
& /*key*/,
45 TableReaderCaller
/*caller*/) override
{
49 uint64_t ApproximateSize(const Slice
& /*start*/, const Slice
& /*end*/,
50 TableReaderCaller
/*caller*/) override
{
54 size_t ApproximateMemoryUsage() const override
{ return 0; }
56 void SetupForCompaction() override
{}
58 std::shared_ptr
<const TableProperties
> GetTableProperties() const override
;
63 const KVVector
& table_
;
66 class MockTableIterator
: public InternalIterator
{
68 explicit MockTableIterator(const KVVector
& table
) : table_(table
) {
72 bool Valid() const override
{ return itr_
!= table_
.end(); }
74 void SeekToFirst() override
{ itr_
= table_
.begin(); }
76 void SeekToLast() override
{
81 void Seek(const Slice
& target
) override
{
82 KVPair
target_pair(target
.ToString(), "");
83 InternalKeyComparator
icmp(BytewiseComparator());
84 itr_
= std::lower_bound(table_
.begin(), table_
.end(), target_pair
,
85 [icmp
](KVPair a
, KVPair b
) -> bool {
86 return icmp
.Compare(a
.first
, b
.first
) < 0;
90 void SeekForPrev(const Slice
& target
) override
{
91 KVPair
target_pair(target
.ToString(), "");
92 InternalKeyComparator
icmp(BytewiseComparator());
93 itr_
= std::upper_bound(table_
.begin(), table_
.end(), target_pair
,
94 [icmp
](KVPair a
, KVPair b
) -> bool {
95 return icmp
.Compare(a
.first
, b
.first
) < 0;
100 void Next() override
{ ++itr_
; }
102 void Prev() override
{
103 if (itr_
== table_
.begin()) {
110 Slice
key() const override
{ return Slice(itr_
->first
); }
112 Slice
value() const override
{ return Slice(itr_
->second
); }
114 Status
status() const override
{ return Status::OK(); }
117 const KVVector
& table_
;
118 KVVector::const_iterator itr_
;
121 class MockTableBuilder
: public TableBuilder
{
123 MockTableBuilder(uint32_t id
, MockTableFileSystem
* file_system
,
124 MockTableFactory::MockCorruptionMode corrupt_mode
=
125 MockTableFactory::kCorruptNone
)
126 : id_(id
), file_system_(file_system
), corrupt_mode_(corrupt_mode
) {
127 table_
= MakeMockFile({});
130 // REQUIRES: Either Finish() or Abandon() has been called.
131 ~MockTableBuilder() {}
133 // Add key,value to the table being constructed.
134 // REQUIRES: key is after any previously added key according to comparator.
135 // REQUIRES: Finish(), Abandon() have not been called
136 void Add(const Slice
& key
, const Slice
& value
) override
{
137 if (corrupt_mode_
== MockTableFactory::kCorruptValue
) {
139 table_
.push_back({key
.ToString(), value
.ToString() + " "});
140 corrupt_mode_
= MockTableFactory::kCorruptNone
;
141 } else if (corrupt_mode_
== MockTableFactory::kCorruptKey
) {
142 table_
.push_back({key
.ToString() + " ", value
.ToString()});
143 corrupt_mode_
= MockTableFactory::kCorruptNone
;
144 } else if (corrupt_mode_
== MockTableFactory::kCorruptReorderKey
) {
145 if (prev_key_
.empty()) {
146 prev_key_
= key
.ToString();
147 prev_value_
= value
.ToString();
149 table_
.push_back({key
.ToString(), value
.ToString()});
150 table_
.push_back({prev_key_
, prev_value_
});
151 corrupt_mode_
= MockTableFactory::kCorruptNone
;
154 table_
.push_back({key
.ToString(), value
.ToString()});
158 // Return non-ok iff some error has been detected.
159 Status
status() const override
{ return Status::OK(); }
161 // Return non-ok iff some error happens during IO.
162 IOStatus
io_status() const override
{ return IOStatus::OK(); }
164 Status
Finish() override
{
165 MutexLock
lock_guard(&file_system_
->mutex
);
166 file_system_
->files
.insert({id_
, table_
});
170 void Abandon() override
{}
172 uint64_t NumEntries() const override
{ return table_
.size(); }
174 uint64_t FileSize() const override
{ return table_
.size(); }
176 TableProperties
GetTableProperties() const override
{
177 return TableProperties();
181 std::string
GetFileChecksum() const override
{ return kUnknownFileChecksum
; }
182 // Get file checksum function name
183 const char* GetFileChecksumFuncName() const override
{
184 return kUnknownFileChecksumFuncName
;
189 std::string prev_key_
;
190 std::string prev_value_
;
191 MockTableFileSystem
* file_system_
;
196 InternalIterator
* MockTableReader::NewIterator(
197 const ReadOptions
&, const SliceTransform
* /* prefix_extractor */,
198 Arena
* /*arena*/, bool /*skip_filters*/, TableReaderCaller
/*caller*/,
199 size_t /*compaction_readahead_size*/, bool /* allow_unprepared_value */) {
200 return new MockTableIterator(table_
);
203 Status
MockTableReader::Get(const ReadOptions
&, const Slice
& key
,
204 GetContext
* get_context
,
205 const SliceTransform
* /*prefix_extractor*/,
206 bool /*skip_filters*/) {
207 std::unique_ptr
<MockTableIterator
> iter(new MockTableIterator(table_
));
208 for (iter
->Seek(key
); iter
->Valid(); iter
->Next()) {
209 ParsedInternalKey parsed_key
;
211 ParseInternalKey(iter
->key(), &parsed_key
, true /* log_err_key */);
212 if (!pik_status
.ok()) {
216 bool dont_care
__attribute__((__unused__
));
217 if (!get_context
->SaveValue(parsed_key
, iter
->value(), &dont_care
)) {
224 std::shared_ptr
<const TableProperties
> MockTableReader::GetTableProperties()
226 return std::shared_ptr
<const TableProperties
>(new TableProperties());
229 MockTableFactory::MockTableFactory()
230 : next_id_(1), corrupt_mode_(MockTableFactory::kCorruptNone
) {}
232 Status
MockTableFactory::NewTableReader(
233 const ReadOptions
& /*ro*/,
234 const TableReaderOptions
& /*table_reader_options*/,
235 std::unique_ptr
<RandomAccessFileReader
>&& file
, uint64_t /*file_size*/,
236 std::unique_ptr
<TableReader
>* table_reader
,
237 bool /*prefetch_index_and_filter_in_cache*/) const {
238 uint32_t id
= GetIDFromFile(file
.get());
240 MutexLock
lock_guard(&file_system_
.mutex
);
242 auto it
= file_system_
.files
.find(id
);
243 if (it
== file_system_
.files
.end()) {
244 return Status::IOError("Mock file not found");
247 table_reader
->reset(new MockTableReader(it
->second
));
252 TableBuilder
* MockTableFactory::NewTableBuilder(
253 const TableBuilderOptions
& /*table_builder_options*/,
254 uint32_t /*column_family_id*/, WritableFileWriter
* file
) const {
255 uint32_t id
= GetAndWriteNextID(file
);
257 return new MockTableBuilder(id
, &file_system_
, corrupt_mode_
);
260 Status
MockTableFactory::CreateMockTable(Env
* env
, const std::string
& fname
,
261 KVVector file_contents
) {
262 std::unique_ptr
<WritableFile
> file
;
263 auto s
= env
->NewWritableFile(fname
, &file
, EnvOptions());
268 WritableFileWriter
file_writer(NewLegacyWritableFileWrapper(std::move(file
)),
269 fname
, EnvOptions());
271 uint32_t id
= GetAndWriteNextID(&file_writer
);
272 file_system_
.files
.insert({id
, std::move(file_contents
)});
276 uint32_t MockTableFactory::GetAndWriteNextID(WritableFileWriter
* file
) const {
277 uint32_t next_id
= next_id_
.fetch_add(1);
279 EncodeFixed32(buf
, next_id
);
280 file
->Append(Slice(buf
, 4));
284 uint32_t MockTableFactory::GetIDFromFile(RandomAccessFileReader
* file
) const {
287 file
->Read(IOOptions(), 0, 4, &result
, buf
, nullptr);
288 assert(result
.size() == 4);
289 return DecodeFixed32(buf
);
292 void MockTableFactory::AssertSingleFile(const KVVector
& file_contents
) {
293 ASSERT_EQ(file_system_
.files
.size(), 1U);
294 ASSERT_EQ(file_contents
, file_system_
.files
.begin()->second
);
297 void MockTableFactory::AssertLatestFile(const KVVector
& file_contents
) {
298 ASSERT_GE(file_system_
.files
.size(), 1U);
299 auto latest
= file_system_
.files
.end();
302 if (file_contents
!= latest
->second
) {
303 std::cout
<< "Wrong content! Content of latest file:" << std::endl
;
304 for (const auto& kv
: latest
->second
) {
305 ParsedInternalKey ikey
;
306 std::string key
, value
;
307 std::tie(key
, value
) = kv
;
308 ASSERT_OK(ParseInternalKey(Slice(key
), &ikey
, true /* log_err_key */));
309 std::cout
<< ikey
.DebugString(true, false) << " -> " << value
317 } // namespace ROCKSDB_NAMESPACE