]>
Commit | Line | Data |
---|---|---|
7c673cae | 1 | // Copyright (c) 2011-present, Facebook, Inc. All rights reserved. |
11fdf7f2 TL |
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). | |
7c673cae FG |
5 | |
6 | #include "table/mock_table.h" | |
7 | ||
8 | #include "db/dbformat.h" | |
f67539c2 TL |
9 | #include "env/composite_env_wrapper.h" |
10 | #include "file/random_access_file_reader.h" | |
7c673cae FG |
11 | #include "port/port.h" |
12 | #include "rocksdb/table_properties.h" | |
13 | #include "table/get_context.h" | |
14 | #include "util/coding.h" | |
7c673cae | 15 | |
f67539c2 | 16 | namespace ROCKSDB_NAMESPACE { |
7c673cae FG |
17 | namespace mock { |
18 | ||
20effc67 | 19 | KVVector MakeMockFile(std::initializer_list<KVPair> l) { return KVVector(l); } |
7c673cae | 20 | |
20effc67 TL |
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; | |
26 | }); | |
27 | } | |
7c673cae | 28 | |
20effc67 TL |
29 | class MockTableReader : public TableReader { |
30 | public: | |
31 | explicit MockTableReader(const KVVector& table) : table_(table) {} | |
7c673cae | 32 | |
20effc67 TL |
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; | |
39 | ||
40 | Status Get(const ReadOptions& readOptions, const Slice& key, | |
41 | GetContext* get_context, const SliceTransform* prefix_extractor, | |
42 | bool skip_filters = false) override; | |
43 | ||
44 | uint64_t ApproximateOffsetOf(const Slice& /*key*/, | |
45 | TableReaderCaller /*caller*/) override { | |
46 | return 0; | |
47 | } | |
48 | ||
49 | uint64_t ApproximateSize(const Slice& /*start*/, const Slice& /*end*/, | |
50 | TableReaderCaller /*caller*/) override { | |
51 | return 0; | |
52 | } | |
53 | ||
54 | size_t ApproximateMemoryUsage() const override { return 0; } | |
55 | ||
56 | void SetupForCompaction() override {} | |
57 | ||
58 | std::shared_ptr<const TableProperties> GetTableProperties() const override; | |
59 | ||
60 | ~MockTableReader() {} | |
61 | ||
62 | private: | |
63 | const KVVector& table_; | |
64 | }; | |
65 | ||
66 | class MockTableIterator : public InternalIterator { | |
67 | public: | |
68 | explicit MockTableIterator(const KVVector& table) : table_(table) { | |
69 | itr_ = table_.end(); | |
70 | } | |
71 | ||
72 | bool Valid() const override { return itr_ != table_.end(); } | |
73 | ||
74 | void SeekToFirst() override { itr_ = table_.begin(); } | |
75 | ||
76 | void SeekToLast() override { | |
77 | itr_ = table_.end(); | |
78 | --itr_; | |
79 | } | |
80 | ||
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; | |
87 | }); | |
88 | } | |
89 | ||
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; | |
96 | }); | |
97 | Prev(); | |
98 | } | |
99 | ||
100 | void Next() override { ++itr_; } | |
101 | ||
102 | void Prev() override { | |
103 | if (itr_ == table_.begin()) { | |
104 | itr_ = table_.end(); | |
105 | } else { | |
106 | --itr_; | |
107 | } | |
108 | } | |
109 | ||
110 | Slice key() const override { return Slice(itr_->first); } | |
111 | ||
112 | Slice value() const override { return Slice(itr_->second); } | |
113 | ||
114 | Status status() const override { return Status::OK(); } | |
115 | ||
116 | private: | |
117 | const KVVector& table_; | |
118 | KVVector::const_iterator itr_; | |
119 | }; | |
120 | ||
121 | class MockTableBuilder : public TableBuilder { | |
122 | public: | |
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({}); | |
128 | } | |
129 | ||
130 | // REQUIRES: Either Finish() or Abandon() has been called. | |
131 | ~MockTableBuilder() {} | |
132 | ||
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) { | |
138 | // Corrupt the value | |
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(); | |
148 | } else { | |
149 | table_.push_back({key.ToString(), value.ToString()}); | |
150 | table_.push_back({prev_key_, prev_value_}); | |
151 | corrupt_mode_ = MockTableFactory::kCorruptNone; | |
152 | } | |
153 | } else { | |
154 | table_.push_back({key.ToString(), value.ToString()}); | |
155 | } | |
156 | } | |
157 | ||
158 | // Return non-ok iff some error has been detected. | |
159 | Status status() const override { return Status::OK(); } | |
160 | ||
161 | // Return non-ok iff some error happens during IO. | |
162 | IOStatus io_status() const override { return IOStatus::OK(); } | |
163 | ||
164 | Status Finish() override { | |
165 | MutexLock lock_guard(&file_system_->mutex); | |
166 | file_system_->files.insert({id_, table_}); | |
167 | return Status::OK(); | |
168 | } | |
169 | ||
170 | void Abandon() override {} | |
171 | ||
172 | uint64_t NumEntries() const override { return table_.size(); } | |
173 | ||
174 | uint64_t FileSize() const override { return table_.size(); } | |
175 | ||
176 | TableProperties GetTableProperties() const override { | |
177 | return TableProperties(); | |
178 | } | |
179 | ||
180 | // Get file checksum | |
181 | std::string GetFileChecksum() const override { return kUnknownFileChecksum; } | |
182 | // Get file checksum function name | |
183 | const char* GetFileChecksumFuncName() const override { | |
184 | return kUnknownFileChecksumFuncName; | |
185 | } | |
186 | ||
187 | private: | |
188 | uint32_t id_; | |
189 | std::string prev_key_; | |
190 | std::string prev_value_; | |
191 | MockTableFileSystem* file_system_; | |
192 | int corrupt_mode_; | |
193 | KVVector table_; | |
194 | }; | |
7c673cae | 195 | |
11fdf7f2 TL |
196 | InternalIterator* MockTableReader::NewIterator( |
197 | const ReadOptions&, const SliceTransform* /* prefix_extractor */, | |
f67539c2 | 198 | Arena* /*arena*/, bool /*skip_filters*/, TableReaderCaller /*caller*/, |
20effc67 | 199 | size_t /*compaction_readahead_size*/, bool /* allow_unprepared_value */) { |
7c673cae FG |
200 | return new MockTableIterator(table_); |
201 | } | |
202 | ||
203 | Status MockTableReader::Get(const ReadOptions&, const Slice& key, | |
11fdf7f2 TL |
204 | GetContext* get_context, |
205 | const SliceTransform* /*prefix_extractor*/, | |
206 | bool /*skip_filters*/) { | |
7c673cae FG |
207 | std::unique_ptr<MockTableIterator> iter(new MockTableIterator(table_)); |
208 | for (iter->Seek(key); iter->Valid(); iter->Next()) { | |
209 | ParsedInternalKey parsed_key; | |
20effc67 TL |
210 | Status pik_status = |
211 | ParseInternalKey(iter->key(), &parsed_key, true /* log_err_key */); | |
212 | if (!pik_status.ok()) { | |
213 | return pik_status; | |
7c673cae FG |
214 | } |
215 | ||
11fdf7f2 TL |
216 | bool dont_care __attribute__((__unused__)); |
217 | if (!get_context->SaveValue(parsed_key, iter->value(), &dont_care)) { | |
7c673cae FG |
218 | break; |
219 | } | |
220 | } | |
221 | return Status::OK(); | |
222 | } | |
223 | ||
224 | std::shared_ptr<const TableProperties> MockTableReader::GetTableProperties() | |
225 | const { | |
226 | return std::shared_ptr<const TableProperties>(new TableProperties()); | |
227 | } | |
228 | ||
20effc67 TL |
229 | MockTableFactory::MockTableFactory() |
230 | : next_id_(1), corrupt_mode_(MockTableFactory::kCorruptNone) {} | |
7c673cae FG |
231 | |
232 | Status MockTableFactory::NewTableReader( | |
20effc67 | 233 | const ReadOptions& /*ro*/, |
11fdf7f2 | 234 | const TableReaderOptions& /*table_reader_options*/, |
494da23a TL |
235 | std::unique_ptr<RandomAccessFileReader>&& file, uint64_t /*file_size*/, |
236 | std::unique_ptr<TableReader>* table_reader, | |
11fdf7f2 | 237 | bool /*prefetch_index_and_filter_in_cache*/) const { |
7c673cae FG |
238 | uint32_t id = GetIDFromFile(file.get()); |
239 | ||
240 | MutexLock lock_guard(&file_system_.mutex); | |
241 | ||
242 | auto it = file_system_.files.find(id); | |
243 | if (it == file_system_.files.end()) { | |
244 | return Status::IOError("Mock file not found"); | |
245 | } | |
246 | ||
247 | table_reader->reset(new MockTableReader(it->second)); | |
248 | ||
249 | return Status::OK(); | |
250 | } | |
251 | ||
252 | TableBuilder* MockTableFactory::NewTableBuilder( | |
11fdf7f2 TL |
253 | const TableBuilderOptions& /*table_builder_options*/, |
254 | uint32_t /*column_family_id*/, WritableFileWriter* file) const { | |
7c673cae FG |
255 | uint32_t id = GetAndWriteNextID(file); |
256 | ||
20effc67 | 257 | return new MockTableBuilder(id, &file_system_, corrupt_mode_); |
7c673cae FG |
258 | } |
259 | ||
260 | Status MockTableFactory::CreateMockTable(Env* env, const std::string& fname, | |
20effc67 | 261 | KVVector file_contents) { |
7c673cae FG |
262 | std::unique_ptr<WritableFile> file; |
263 | auto s = env->NewWritableFile(fname, &file, EnvOptions()); | |
264 | if (!s.ok()) { | |
265 | return s; | |
266 | } | |
267 | ||
f67539c2 TL |
268 | WritableFileWriter file_writer(NewLegacyWritableFileWrapper(std::move(file)), |
269 | fname, EnvOptions()); | |
7c673cae FG |
270 | |
271 | uint32_t id = GetAndWriteNextID(&file_writer); | |
272 | file_system_.files.insert({id, std::move(file_contents)}); | |
273 | return Status::OK(); | |
274 | } | |
275 | ||
276 | uint32_t MockTableFactory::GetAndWriteNextID(WritableFileWriter* file) const { | |
277 | uint32_t next_id = next_id_.fetch_add(1); | |
278 | char buf[4]; | |
279 | EncodeFixed32(buf, next_id); | |
280 | file->Append(Slice(buf, 4)); | |
281 | return next_id; | |
282 | } | |
283 | ||
284 | uint32_t MockTableFactory::GetIDFromFile(RandomAccessFileReader* file) const { | |
285 | char buf[4]; | |
286 | Slice result; | |
20effc67 | 287 | file->Read(IOOptions(), 0, 4, &result, buf, nullptr); |
7c673cae FG |
288 | assert(result.size() == 4); |
289 | return DecodeFixed32(buf); | |
290 | } | |
291 | ||
20effc67 | 292 | void MockTableFactory::AssertSingleFile(const KVVector& file_contents) { |
7c673cae FG |
293 | ASSERT_EQ(file_system_.files.size(), 1U); |
294 | ASSERT_EQ(file_contents, file_system_.files.begin()->second); | |
295 | } | |
296 | ||
20effc67 | 297 | void MockTableFactory::AssertLatestFile(const KVVector& file_contents) { |
7c673cae FG |
298 | ASSERT_GE(file_system_.files.size(), 1U); |
299 | auto latest = file_system_.files.end(); | |
300 | --latest; | |
301 | ||
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; | |
20effc67 TL |
308 | ASSERT_OK(ParseInternalKey(Slice(key), &ikey, true /* log_err_key */)); |
309 | std::cout << ikey.DebugString(true, false) << " -> " << value | |
310 | << std::endl; | |
7c673cae | 311 | } |
11fdf7f2 | 312 | FAIL(); |
7c673cae FG |
313 | } |
314 | } | |
315 | ||
316 | } // namespace mock | |
f67539c2 | 317 | } // namespace ROCKSDB_NAMESPACE |