]>
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 | #ifndef ROCKSDB_LITE | |
7 | ||
8 | #include "rocksdb/utilities/ldb_cmd.h" | |
20effc67 | 9 | |
f67539c2 TL |
10 | #include "db/version_edit.h" |
11 | #include "db/version_set.h" | |
12 | #include "env/composite_env_wrapper.h" | |
13 | #include "file/filename.h" | |
14 | #include "port/stack_trace.h" | |
15 | #include "rocksdb/file_checksum.h" | |
16 | #include "test_util/sync_point.h" | |
17 | #include "test_util/testharness.h" | |
18 | #include "test_util/testutil.h" | |
19 | #include "util/file_checksum_helper.h" | |
20effc67 | 20 | #include "util/random.h" |
7c673cae FG |
21 | |
22 | using std::string; | |
23 | using std::vector; | |
24 | using std::map; | |
25 | ||
f67539c2 | 26 | namespace ROCKSDB_NAMESPACE { |
494da23a | 27 | |
f67539c2 TL |
28 | class LdbCmdTest : public testing::Test { |
29 | public: | |
30 | LdbCmdTest() : testing::Test() {} | |
31 | ||
32 | Env* TryLoadCustomOrDefaultEnv() { | |
33 | const char* test_env_uri = getenv("TEST_ENV_URI"); | |
34 | if (!test_env_uri) { | |
35 | return Env::Default(); | |
36 | } | |
37 | Env* env = Env::Default(); | |
38 | Env::LoadEnv(test_env_uri, &env, &env_guard_); | |
39 | return env; | |
40 | } | |
41 | ||
42 | private: | |
43 | std::shared_ptr<Env> env_guard_; | |
44 | }; | |
7c673cae | 45 | |
20effc67 TL |
46 | TEST_F(LdbCmdTest, HelpAndVersion) { |
47 | Options o; | |
48 | o.env = TryLoadCustomOrDefaultEnv(); | |
49 | LDBOptions lo; | |
50 | static const char* help[] = {"./ldb", "--help"}; | |
51 | ASSERT_EQ(0, LDBCommandRunner::RunCommand(2, help, o, lo, nullptr)); | |
52 | static const char* version[] = {"./ldb", "--version"}; | |
53 | ASSERT_EQ(0, LDBCommandRunner::RunCommand(2, version, o, lo, nullptr)); | |
54 | static const char* bad[] = {"./ldb", "--not_an_option"}; | |
55 | ASSERT_NE(0, LDBCommandRunner::RunCommand(2, bad, o, lo, nullptr)); | |
56 | } | |
57 | ||
7c673cae FG |
58 | TEST_F(LdbCmdTest, HexToString) { |
59 | // map input to expected outputs. | |
60 | // odd number of "hex" half bytes doesn't make sense | |
61 | map<string, vector<int>> inputMap = { | |
62 | {"0x07", {7}}, {"0x5050", {80, 80}}, {"0xFF", {-1}}, | |
63 | {"0x1234", {18, 52}}, {"0xaaAbAC", {-86, -85, -84}}, {"0x1203", {18, 3}}, | |
64 | }; | |
65 | ||
66 | for (const auto& inPair : inputMap) { | |
f67539c2 | 67 | auto actual = ROCKSDB_NAMESPACE::LDBCommand::HexToString(inPair.first); |
7c673cae FG |
68 | auto expected = inPair.second; |
69 | for (unsigned int i = 0; i < actual.length(); i++) { | |
70 | EXPECT_EQ(expected[i], static_cast<int>((signed char) actual[i])); | |
71 | } | |
f67539c2 | 72 | auto reverse = ROCKSDB_NAMESPACE::LDBCommand::StringToHex(actual); |
7c673cae FG |
73 | EXPECT_STRCASEEQ(inPair.first.c_str(), reverse.c_str()); |
74 | } | |
75 | } | |
76 | ||
77 | TEST_F(LdbCmdTest, HexToStringBadInputs) { | |
78 | const vector<string> badInputs = { | |
79 | "0xZZ", "123", "0xx5", "0x111G", "0x123", "Ox12", "0xT", "0x1Q1", | |
80 | }; | |
20effc67 | 81 | for (const auto& badInput : badInputs) { |
7c673cae | 82 | try { |
f67539c2 | 83 | ROCKSDB_NAMESPACE::LDBCommand::HexToString(badInput); |
7c673cae FG |
84 | std::cerr << "Should fail on bad hex value: " << badInput << "\n"; |
85 | FAIL(); | |
86 | } catch (...) { | |
87 | } | |
88 | } | |
89 | } | |
90 | ||
494da23a | 91 | TEST_F(LdbCmdTest, MemEnv) { |
f67539c2 TL |
92 | Env* base_env = TryLoadCustomOrDefaultEnv(); |
93 | std::unique_ptr<Env> env(NewMemEnv(base_env)); | |
494da23a TL |
94 | Options opts; |
95 | opts.env = env.get(); | |
96 | opts.create_if_missing = true; | |
97 | ||
98 | DB* db = nullptr; | |
20effc67 | 99 | std::string dbname = test::PerThreadDBPath(env.get(), "ldb_cmd_test"); |
494da23a TL |
100 | ASSERT_OK(DB::Open(opts, dbname, &db)); |
101 | ||
102 | WriteOptions wopts; | |
103 | for (int i = 0; i < 100; i++) { | |
104 | char buf[16]; | |
105 | snprintf(buf, sizeof(buf), "%08d", i); | |
106 | ASSERT_OK(db->Put(wopts, buf, buf)); | |
107 | } | |
108 | FlushOptions fopts; | |
109 | fopts.wait = true; | |
110 | ASSERT_OK(db->Flush(fopts)); | |
111 | ||
112 | delete db; | |
113 | ||
114 | char arg1[] = "./ldb"; | |
115 | char arg2[1024]; | |
116 | snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str()); | |
117 | char arg3[] = "dump_live_files"; | |
118 | char* argv[] = {arg1, arg2, arg3}; | |
119 | ||
f67539c2 TL |
120 | ASSERT_EQ(0, |
121 | LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr)); | |
122 | } | |
123 | ||
124 | class FileChecksumTestHelper { | |
125 | private: | |
126 | Options options_; | |
127 | DB* db_; | |
128 | std::string dbname_; | |
129 | ||
130 | Status VerifyChecksum(LiveFileMetaData& file_meta) { | |
131 | std::string cur_checksum; | |
132 | std::string checksum_func_name; | |
133 | ||
134 | Status s; | |
135 | EnvOptions soptions; | |
136 | std::unique_ptr<SequentialFile> file_reader; | |
137 | std::string file_path = dbname_ + "/" + file_meta.name; | |
138 | s = options_.env->NewSequentialFile(file_path, &file_reader, soptions); | |
139 | if (!s.ok()) { | |
140 | return s; | |
141 | } | |
142 | std::unique_ptr<char[]> scratch(new char[2048]); | |
f67539c2 | 143 | Slice result; |
20effc67 TL |
144 | FileChecksumGenFactory* file_checksum_gen_factory = |
145 | options_.file_checksum_gen_factory.get(); | |
146 | if (file_checksum_gen_factory == nullptr) { | |
f67539c2 TL |
147 | cur_checksum = kUnknownFileChecksum; |
148 | checksum_func_name = kUnknownFileChecksumFuncName; | |
149 | } else { | |
20effc67 TL |
150 | FileChecksumGenContext gen_context; |
151 | gen_context.file_name = file_meta.name; | |
152 | std::unique_ptr<FileChecksumGenerator> file_checksum_gen = | |
153 | file_checksum_gen_factory->CreateFileChecksumGenerator(gen_context); | |
154 | checksum_func_name = file_checksum_gen->Name(); | |
f67539c2 TL |
155 | s = file_reader->Read(2048, &result, scratch.get()); |
156 | if (!s.ok()) { | |
157 | return s; | |
158 | } | |
159 | while (result.size() != 0) { | |
20effc67 | 160 | file_checksum_gen->Update(scratch.get(), result.size()); |
f67539c2 TL |
161 | s = file_reader->Read(2048, &result, scratch.get()); |
162 | if (!s.ok()) { | |
163 | return s; | |
164 | } | |
165 | } | |
20effc67 TL |
166 | file_checksum_gen->Finalize(); |
167 | cur_checksum = file_checksum_gen->GetChecksum(); | |
f67539c2 TL |
168 | } |
169 | ||
170 | std::string stored_checksum = file_meta.file_checksum; | |
171 | std::string stored_checksum_func_name = file_meta.file_checksum_func_name; | |
172 | if ((cur_checksum != stored_checksum) || | |
173 | (checksum_func_name != stored_checksum_func_name)) { | |
174 | return Status::Corruption( | |
175 | "Checksum does not match! The file: " + file_meta.name + | |
176 | ", checksum name: " + stored_checksum_func_name + " and checksum " + | |
177 | stored_checksum + ". However, expected checksum name: " + | |
178 | checksum_func_name + " and checksum " + cur_checksum); | |
179 | } | |
180 | return Status::OK(); | |
181 | } | |
182 | ||
183 | public: | |
184 | FileChecksumTestHelper(Options& options, DB* db, std::string db_name) | |
185 | : options_(options), db_(db), dbname_(db_name) {} | |
186 | ~FileChecksumTestHelper() {} | |
187 | ||
188 | // Verify the checksum information in Manifest. | |
189 | Status VerifyChecksumInManifest( | |
190 | const std::vector<LiveFileMetaData>& live_files) { | |
191 | // Step 1: verify if the dbname_ is correct | |
192 | if (dbname_[dbname_.length() - 1] != '/') { | |
193 | dbname_.append("/"); | |
194 | } | |
195 | ||
196 | // Step 2, get the the checksum information by recovering the VersionSet | |
197 | // from Manifest. | |
198 | std::unique_ptr<FileChecksumList> checksum_list(NewFileChecksumList()); | |
199 | EnvOptions sopt; | |
200 | std::shared_ptr<Cache> tc(NewLRUCache(options_.max_open_files - 10, | |
201 | options_.table_cache_numshardbits)); | |
202 | options_.db_paths.emplace_back(dbname_, 0); | |
203 | options_.num_levels = 64; | |
204 | WriteController wc(options_.delayed_write_rate); | |
205 | WriteBufferManager wb(options_.db_write_buffer_size); | |
206 | ImmutableDBOptions immutable_db_options(options_); | |
207 | VersionSet versions(dbname_, &immutable_db_options, sopt, tc.get(), &wb, | |
20effc67 | 208 | &wc, nullptr, nullptr); |
f67539c2 TL |
209 | std::vector<std::string> cf_name_list; |
210 | Status s; | |
211 | s = versions.ListColumnFamilies(&cf_name_list, dbname_, | |
20effc67 | 212 | immutable_db_options.fs.get()); |
f67539c2 TL |
213 | if (s.ok()) { |
214 | std::vector<ColumnFamilyDescriptor> cf_list; | |
215 | for (const auto& name : cf_name_list) { | |
216 | fprintf(stdout, "cf_name: %s", name.c_str()); | |
217 | cf_list.emplace_back(name, ColumnFamilyOptions(options_)); | |
218 | } | |
219 | s = versions.Recover(cf_list, true); | |
220 | } | |
221 | if (s.ok()) { | |
222 | s = versions.GetLiveFilesChecksumInfo(checksum_list.get()); | |
223 | } | |
224 | if (!s.ok()) { | |
225 | return s; | |
226 | } | |
227 | ||
228 | // Step 3 verify the checksum | |
229 | if (live_files.size() != checksum_list->size()) { | |
230 | return Status::Corruption("The number of files does not match!"); | |
231 | } | |
232 | for (size_t i = 0; i < live_files.size(); i++) { | |
233 | std::string stored_checksum = ""; | |
234 | std::string stored_func_name = ""; | |
235 | s = checksum_list->SearchOneFileChecksum( | |
236 | live_files[i].file_number, &stored_checksum, &stored_func_name); | |
237 | if (s.IsNotFound()) { | |
238 | return s; | |
239 | } | |
240 | if (live_files[i].file_checksum != stored_checksum || | |
241 | live_files[i].file_checksum_func_name != stored_func_name) { | |
242 | return Status::Corruption( | |
243 | "Checksum does not match! The file: " + | |
244 | ToString(live_files[i].file_number) + | |
245 | ". In Manifest, checksum name: " + stored_func_name + | |
246 | " and checksum " + stored_checksum + | |
247 | ". However, expected checksum name: " + | |
248 | live_files[i].file_checksum_func_name + " and checksum " + | |
249 | live_files[i].file_checksum); | |
250 | } | |
251 | } | |
252 | return Status::OK(); | |
253 | } | |
254 | ||
255 | // Verify the checksum of each file by recalculting the checksum and | |
256 | // comparing it with the one being generated when a SST file is created. | |
257 | Status VerifyEachFileChecksum() { | |
258 | assert(db_ != nullptr); | |
259 | std::vector<LiveFileMetaData> live_files; | |
260 | db_->GetLiveFilesMetaData(&live_files); | |
261 | for (auto a_file : live_files) { | |
262 | Status cs = VerifyChecksum(a_file); | |
263 | if (!cs.ok()) { | |
264 | return cs; | |
265 | } | |
266 | } | |
267 | return Status::OK(); | |
268 | } | |
269 | }; | |
270 | ||
271 | TEST_F(LdbCmdTest, DumpFileChecksumNoChecksum) { | |
272 | Env* base_env = TryLoadCustomOrDefaultEnv(); | |
273 | std::unique_ptr<Env> env(NewMemEnv(base_env)); | |
274 | Options opts; | |
275 | opts.env = env.get(); | |
276 | opts.create_if_missing = true; | |
f67539c2 TL |
277 | |
278 | DB* db = nullptr; | |
20effc67 | 279 | std::string dbname = test::PerThreadDBPath(env.get(), "ldb_cmd_test"); |
f67539c2 TL |
280 | ASSERT_OK(DB::Open(opts, dbname, &db)); |
281 | ||
282 | WriteOptions wopts; | |
283 | FlushOptions fopts; | |
284 | fopts.wait = true; | |
285 | Random rnd(test::RandomSeed()); | |
286 | for (int i = 0; i < 200; i++) { | |
287 | char buf[16]; | |
288 | snprintf(buf, sizeof(buf), "%08d", i); | |
20effc67 | 289 | std::string v = rnd.RandomString(100); |
f67539c2 TL |
290 | ASSERT_OK(db->Put(wopts, buf, v)); |
291 | } | |
292 | ASSERT_OK(db->Flush(fopts)); | |
293 | for (int i = 100; i < 300; i++) { | |
294 | char buf[16]; | |
295 | snprintf(buf, sizeof(buf), "%08d", i); | |
20effc67 | 296 | std::string v = rnd.RandomString(100); |
f67539c2 TL |
297 | ASSERT_OK(db->Put(wopts, buf, v)); |
298 | } | |
299 | ASSERT_OK(db->Flush(fopts)); | |
300 | for (int i = 200; i < 400; i++) { | |
301 | char buf[16]; | |
302 | snprintf(buf, sizeof(buf), "%08d", i); | |
20effc67 | 303 | std::string v = rnd.RandomString(100); |
f67539c2 TL |
304 | ASSERT_OK(db->Put(wopts, buf, v)); |
305 | } | |
306 | ASSERT_OK(db->Flush(fopts)); | |
307 | for (int i = 300; i < 400; i++) { | |
308 | char buf[16]; | |
309 | snprintf(buf, sizeof(buf), "%08d", i); | |
20effc67 | 310 | std::string v = rnd.RandomString(100); |
f67539c2 TL |
311 | ASSERT_OK(db->Put(wopts, buf, v)); |
312 | } | |
313 | ASSERT_OK(db->Flush(fopts)); | |
314 | ||
315 | char arg1[] = "./ldb"; | |
316 | char arg2[1024]; | |
317 | snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str()); | |
318 | char arg3[] = "file_checksum_dump"; | |
319 | char* argv[] = {arg1, arg2, arg3}; | |
320 | ||
321 | ASSERT_EQ(0, | |
322 | LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr)); | |
323 | ||
324 | // Verify each sst file checksum value and checksum name | |
325 | FileChecksumTestHelper fct_helper(opts, db, dbname); | |
326 | ASSERT_OK(fct_helper.VerifyEachFileChecksum()); | |
327 | ||
328 | // Manually trigger compaction | |
329 | char b_buf[16]; | |
330 | snprintf(b_buf, sizeof(b_buf), "%08d", 0); | |
331 | char e_buf[16]; | |
332 | snprintf(e_buf, sizeof(e_buf), "%08d", 399); | |
333 | Slice begin(b_buf); | |
334 | Slice end(e_buf); | |
335 | CompactRangeOptions options; | |
336 | ASSERT_OK(db->CompactRange(options, &begin, &end)); | |
337 | // Verify each sst file checksum after compaction | |
338 | FileChecksumTestHelper fct_helper_ac(opts, db, dbname); | |
339 | ASSERT_OK(fct_helper_ac.VerifyEachFileChecksum()); | |
340 | ||
341 | ASSERT_EQ(0, | |
342 | LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr)); | |
343 | ||
344 | // Verify the checksum information in memory is the same as that in Manifest; | |
345 | std::vector<LiveFileMetaData> live_files; | |
346 | db->GetLiveFilesMetaData(&live_files); | |
347 | delete db; | |
348 | ASSERT_OK(fct_helper_ac.VerifyChecksumInManifest(live_files)); | |
349 | } | |
350 | ||
351 | TEST_F(LdbCmdTest, DumpFileChecksumCRC32) { | |
352 | Env* base_env = TryLoadCustomOrDefaultEnv(); | |
353 | std::unique_ptr<Env> env(NewMemEnv(base_env)); | |
354 | Options opts; | |
355 | opts.env = env.get(); | |
356 | opts.create_if_missing = true; | |
20effc67 | 357 | opts.file_checksum_gen_factory = GetFileChecksumGenCrc32cFactory(); |
f67539c2 TL |
358 | |
359 | DB* db = nullptr; | |
20effc67 | 360 | std::string dbname = test::PerThreadDBPath(env.get(), "ldb_cmd_test"); |
f67539c2 TL |
361 | ASSERT_OK(DB::Open(opts, dbname, &db)); |
362 | ||
363 | WriteOptions wopts; | |
364 | FlushOptions fopts; | |
365 | fopts.wait = true; | |
366 | Random rnd(test::RandomSeed()); | |
367 | for (int i = 0; i < 100; i++) { | |
368 | char buf[16]; | |
369 | snprintf(buf, sizeof(buf), "%08d", i); | |
20effc67 | 370 | std::string v = rnd.RandomString(100); |
f67539c2 TL |
371 | ASSERT_OK(db->Put(wopts, buf, v)); |
372 | } | |
373 | ASSERT_OK(db->Flush(fopts)); | |
374 | for (int i = 50; i < 150; i++) { | |
375 | char buf[16]; | |
376 | snprintf(buf, sizeof(buf), "%08d", i); | |
20effc67 | 377 | std::string v = rnd.RandomString(100); |
f67539c2 TL |
378 | ASSERT_OK(db->Put(wopts, buf, v)); |
379 | } | |
380 | ASSERT_OK(db->Flush(fopts)); | |
381 | for (int i = 100; i < 200; i++) { | |
382 | char buf[16]; | |
383 | snprintf(buf, sizeof(buf), "%08d", i); | |
20effc67 | 384 | std::string v = rnd.RandomString(100); |
f67539c2 TL |
385 | ASSERT_OK(db->Put(wopts, buf, v)); |
386 | } | |
387 | ASSERT_OK(db->Flush(fopts)); | |
388 | for (int i = 150; i < 250; i++) { | |
389 | char buf[16]; | |
390 | snprintf(buf, sizeof(buf), "%08d", i); | |
20effc67 | 391 | std::string v = rnd.RandomString(100); |
f67539c2 TL |
392 | ASSERT_OK(db->Put(wopts, buf, v)); |
393 | } | |
394 | ASSERT_OK(db->Flush(fopts)); | |
395 | ||
396 | char arg1[] = "./ldb"; | |
397 | char arg2[1024]; | |
398 | snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str()); | |
399 | char arg3[] = "file_checksum_dump"; | |
400 | char* argv[] = {arg1, arg2, arg3}; | |
401 | ||
402 | ASSERT_EQ(0, | |
403 | LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr)); | |
404 | ||
405 | // Verify each sst file checksum value and checksum name | |
406 | FileChecksumTestHelper fct_helper(opts, db, dbname); | |
407 | ASSERT_OK(fct_helper.VerifyEachFileChecksum()); | |
408 | ||
409 | // Manually trigger compaction | |
410 | char b_buf[16]; | |
411 | snprintf(b_buf, sizeof(b_buf), "%08d", 0); | |
412 | char e_buf[16]; | |
413 | snprintf(e_buf, sizeof(e_buf), "%08d", 249); | |
414 | Slice begin(b_buf); | |
415 | Slice end(e_buf); | |
416 | CompactRangeOptions options; | |
417 | ASSERT_OK(db->CompactRange(options, &begin, &end)); | |
418 | // Verify each sst file checksum after compaction | |
419 | FileChecksumTestHelper fct_helper_ac(opts, db, dbname); | |
420 | ASSERT_OK(fct_helper_ac.VerifyEachFileChecksum()); | |
421 | ||
422 | ASSERT_EQ(0, | |
423 | LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr)); | |
424 | ||
425 | // Verify the checksum information in memory is the same as that in Manifest; | |
426 | std::vector<LiveFileMetaData> live_files; | |
427 | db->GetLiveFilesMetaData(&live_files); | |
428 | delete db; | |
429 | ASSERT_OK(fct_helper_ac.VerifyChecksumInManifest(live_files)); | |
494da23a TL |
430 | } |
431 | ||
432 | TEST_F(LdbCmdTest, OptionParsing) { | |
433 | // test parsing flags | |
f67539c2 TL |
434 | Options opts; |
435 | opts.env = TryLoadCustomOrDefaultEnv(); | |
494da23a TL |
436 | { |
437 | std::vector<std::string> args; | |
438 | args.push_back("scan"); | |
439 | args.push_back("--ttl"); | |
440 | args.push_back("--timestamp"); | |
f67539c2 TL |
441 | LDBCommand* command = ROCKSDB_NAMESPACE::LDBCommand::InitFromCmdLineArgs( |
442 | args, opts, LDBOptions(), nullptr); | |
494da23a TL |
443 | const std::vector<std::string> flags = command->TEST_GetFlags(); |
444 | EXPECT_EQ(flags.size(), 2); | |
445 | EXPECT_EQ(flags[0], "ttl"); | |
446 | EXPECT_EQ(flags[1], "timestamp"); | |
447 | delete command; | |
448 | } | |
449 | // test parsing options which contains equal sign in the option value | |
450 | { | |
451 | std::vector<std::string> args; | |
452 | args.push_back("scan"); | |
453 | args.push_back("--db=/dev/shm/ldbtest/"); | |
454 | args.push_back( | |
455 | "--from='abcd/efg/hijk/lmn/" | |
456 | "opq:__rst.uvw.xyz?a=3+4+bcd+efghi&jk=lm_no&pq=rst-0&uv=wx-8&yz=a&bcd_" | |
457 | "ef=gh.ijk'"); | |
f67539c2 TL |
458 | LDBCommand* command = ROCKSDB_NAMESPACE::LDBCommand::InitFromCmdLineArgs( |
459 | args, opts, LDBOptions(), nullptr); | |
494da23a TL |
460 | const std::map<std::string, std::string> option_map = |
461 | command->TEST_GetOptionMap(); | |
462 | EXPECT_EQ(option_map.at("db"), "/dev/shm/ldbtest/"); | |
463 | EXPECT_EQ(option_map.at("from"), | |
464 | "'abcd/efg/hijk/lmn/" | |
465 | "opq:__rst.uvw.xyz?a=3+4+bcd+efghi&jk=lm_no&pq=rst-0&uv=wx-8&yz=" | |
466 | "a&bcd_ef=gh.ijk'"); | |
467 | delete command; | |
468 | } | |
469 | } | |
470 | ||
f67539c2 TL |
471 | TEST_F(LdbCmdTest, ListFileTombstone) { |
472 | Env* base_env = TryLoadCustomOrDefaultEnv(); | |
473 | std::unique_ptr<Env> env(NewMemEnv(base_env)); | |
474 | Options opts; | |
475 | opts.env = env.get(); | |
476 | opts.create_if_missing = true; | |
477 | ||
478 | DB* db = nullptr; | |
20effc67 | 479 | std::string dbname = test::PerThreadDBPath(env.get(), "ldb_cmd_test"); |
f67539c2 TL |
480 | ASSERT_OK(DB::Open(opts, dbname, &db)); |
481 | ||
482 | WriteOptions wopts; | |
483 | ASSERT_OK(db->Put(wopts, "foo", "1")); | |
484 | ASSERT_OK(db->Put(wopts, "bar", "2")); | |
485 | ||
486 | FlushOptions fopts; | |
487 | fopts.wait = true; | |
488 | ASSERT_OK(db->Flush(fopts)); | |
489 | ||
490 | ASSERT_OK(db->DeleteRange(wopts, db->DefaultColumnFamily(), "foo", "foo2")); | |
491 | ASSERT_OK(db->DeleteRange(wopts, db->DefaultColumnFamily(), "bar", "foo2")); | |
492 | ASSERT_OK(db->Flush(fopts)); | |
493 | ||
494 | delete db; | |
495 | ||
496 | { | |
497 | char arg1[] = "./ldb"; | |
498 | char arg2[1024]; | |
499 | snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str()); | |
500 | char arg3[] = "list_file_range_deletes"; | |
501 | char* argv[] = {arg1, arg2, arg3}; | |
502 | ||
503 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
504 | "ListFileRangeDeletesCommand::DoCommand:BeforePrint", [&](void* arg) { | |
505 | std::string* out_str = reinterpret_cast<std::string*>(arg); | |
506 | ||
507 | // Count number of tombstones printed | |
508 | int num_tb = 0; | |
509 | const std::string kFingerprintStr = "start: "; | |
510 | auto offset = out_str->find(kFingerprintStr); | |
511 | while (offset != std::string::npos) { | |
512 | num_tb++; | |
513 | offset = | |
514 | out_str->find(kFingerprintStr, offset + kFingerprintStr.size()); | |
515 | } | |
516 | EXPECT_EQ(2, num_tb); | |
517 | }); | |
518 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
519 | ||
520 | ASSERT_EQ( | |
521 | 0, LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr)); | |
522 | ||
523 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks(); | |
524 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); | |
525 | } | |
526 | ||
527 | // Test the case of limiting tombstones | |
528 | { | |
529 | char arg1[] = "./ldb"; | |
530 | char arg2[1024]; | |
531 | snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str()); | |
532 | char arg3[] = "list_file_range_deletes"; | |
533 | char arg4[] = "--max_keys=1"; | |
534 | char* argv[] = {arg1, arg2, arg3, arg4}; | |
535 | ||
536 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->SetCallBack( | |
537 | "ListFileRangeDeletesCommand::DoCommand:BeforePrint", [&](void* arg) { | |
538 | std::string* out_str = reinterpret_cast<std::string*>(arg); | |
539 | ||
540 | // Count number of tombstones printed | |
541 | int num_tb = 0; | |
542 | const std::string kFingerprintStr = "start: "; | |
543 | auto offset = out_str->find(kFingerprintStr); | |
544 | while (offset != std::string::npos) { | |
545 | num_tb++; | |
546 | offset = | |
547 | out_str->find(kFingerprintStr, offset + kFingerprintStr.size()); | |
548 | } | |
549 | EXPECT_EQ(1, num_tb); | |
550 | }); | |
551 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->EnableProcessing(); | |
552 | ||
553 | ASSERT_EQ( | |
554 | 0, LDBCommandRunner::RunCommand(4, argv, opts, LDBOptions(), nullptr)); | |
555 | ||
556 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->ClearAllCallBacks(); | |
557 | ROCKSDB_NAMESPACE::SyncPoint::GetInstance()->DisableProcessing(); | |
558 | } | |
559 | } | |
20effc67 TL |
560 | |
561 | TEST_F(LdbCmdTest, DisableConsistencyChecks) { | |
562 | Env* base_env = TryLoadCustomOrDefaultEnv(); | |
563 | std::unique_ptr<Env> env(NewMemEnv(base_env)); | |
564 | Options opts; | |
565 | opts.env = env.get(); | |
566 | opts.create_if_missing = true; | |
567 | ||
568 | std::string dbname = test::PerThreadDBPath(env.get(), "ldb_cmd_test"); | |
569 | ||
570 | { | |
571 | DB* db = nullptr; | |
572 | ASSERT_OK(DB::Open(opts, dbname, &db)); | |
573 | ||
574 | WriteOptions wopts; | |
575 | FlushOptions fopts; | |
576 | fopts.wait = true; | |
577 | ||
578 | ASSERT_OK(db->Put(wopts, "foo1", "1")); | |
579 | ASSERT_OK(db->Put(wopts, "bar1", "2")); | |
580 | ASSERT_OK(db->Flush(fopts)); | |
581 | ||
582 | ASSERT_OK(db->Put(wopts, "foo2", "3")); | |
583 | ASSERT_OK(db->Put(wopts, "bar2", "4")); | |
584 | ASSERT_OK(db->Flush(fopts)); | |
585 | ||
586 | delete db; | |
587 | } | |
588 | ||
589 | { | |
590 | char arg1[] = "./ldb"; | |
591 | char arg2[1024]; | |
592 | snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str()); | |
593 | char arg3[] = "checkconsistency"; | |
594 | char* argv[] = {arg1, arg2, arg3}; | |
595 | ||
596 | SyncPoint::GetInstance()->SetCallBack( | |
597 | "Version::PrepareApply:forced_check", [&](void* arg) { | |
598 | bool* forced = reinterpret_cast<bool*>(arg); | |
599 | ASSERT_TRUE(*forced); | |
600 | }); | |
601 | SyncPoint::GetInstance()->EnableProcessing(); | |
602 | ||
603 | ASSERT_EQ( | |
604 | 0, LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr)); | |
605 | ||
606 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
607 | SyncPoint::GetInstance()->DisableProcessing(); | |
608 | } | |
609 | { | |
610 | char arg1[] = "./ldb"; | |
611 | char arg2[1024]; | |
612 | snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str()); | |
613 | char arg3[] = "scan"; | |
614 | char* argv[] = {arg1, arg2, arg3}; | |
615 | ||
616 | SyncPoint::GetInstance()->SetCallBack( | |
617 | "Version::PrepareApply:forced_check", [&](void* arg) { | |
618 | bool* forced = reinterpret_cast<bool*>(arg); | |
619 | ASSERT_TRUE(*forced); | |
620 | }); | |
621 | SyncPoint::GetInstance()->EnableProcessing(); | |
622 | ||
623 | ASSERT_EQ( | |
624 | 0, LDBCommandRunner::RunCommand(3, argv, opts, LDBOptions(), nullptr)); | |
625 | ||
626 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
627 | SyncPoint::GetInstance()->DisableProcessing(); | |
628 | } | |
629 | { | |
630 | char arg1[] = "./ldb"; | |
631 | char arg2[1024]; | |
632 | snprintf(arg2, sizeof(arg2), "--db=%s", dbname.c_str()); | |
633 | char arg3[] = "scan"; | |
634 | char arg4[] = "--disable_consistency_checks"; | |
635 | char* argv[] = {arg1, arg2, arg3, arg4}; | |
636 | ||
637 | SyncPoint::GetInstance()->SetCallBack( | |
638 | "ColumnFamilyData::ColumnFamilyData", [&](void* arg) { | |
639 | ColumnFamilyOptions* cfo = | |
640 | reinterpret_cast<ColumnFamilyOptions*>(arg); | |
641 | ASSERT_FALSE(cfo->force_consistency_checks); | |
642 | }); | |
643 | SyncPoint::GetInstance()->EnableProcessing(); | |
644 | ||
645 | ASSERT_EQ( | |
646 | 0, LDBCommandRunner::RunCommand(4, argv, opts, LDBOptions(), nullptr)); | |
647 | ||
648 | SyncPoint::GetInstance()->ClearAllCallBacks(); | |
649 | SyncPoint::GetInstance()->DisableProcessing(); | |
650 | } | |
651 | } | |
652 | ||
653 | TEST_F(LdbCmdTest, TestBadDbPath) { | |
654 | Env* base_env = TryLoadCustomOrDefaultEnv(); | |
655 | std::unique_ptr<Env> env(NewMemEnv(base_env)); | |
656 | Options opts; | |
657 | opts.env = env.get(); | |
658 | opts.create_if_missing = true; | |
659 | ||
660 | std::string dbname = test::PerThreadDBPath(env.get(), "ldb_cmd_test"); | |
661 | char arg1[] = "./ldb"; | |
662 | char arg2[1024]; | |
663 | snprintf(arg2, sizeof(arg2), "--db=%s/.no_such_dir", dbname.c_str()); | |
664 | char arg3[1024]; | |
665 | snprintf(arg3, sizeof(arg3), "create_column_family"); | |
666 | char arg4[] = "bad cf"; | |
667 | char* argv[] = {arg1, arg2, arg3, arg4}; | |
668 | ||
669 | ASSERT_EQ(1, | |
670 | LDBCommandRunner::RunCommand(4, argv, opts, LDBOptions(), nullptr)); | |
671 | snprintf(arg3, sizeof(arg3), "drop_column_family"); | |
672 | ASSERT_EQ(1, | |
673 | LDBCommandRunner::RunCommand(4, argv, opts, LDBOptions(), nullptr)); | |
674 | } | |
f67539c2 TL |
675 | } // namespace ROCKSDB_NAMESPACE |
676 | ||
677 | #ifdef ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS | |
678 | extern "C" { | |
679 | void RegisterCustomObjects(int argc, char** argv); | |
680 | } | |
681 | #else | |
682 | void RegisterCustomObjects(int /*argc*/, char** /*argv*/) {} | |
683 | #endif // !ROCKSDB_UNITTESTS_WITH_CUSTOM_OBJECTS_FROM_STATIC_LIBS | |
494da23a | 684 | |
7c673cae | 685 | int main(int argc, char** argv) { |
f67539c2 | 686 | ROCKSDB_NAMESPACE::port::InstallStackTraceHandler(); |
7c673cae | 687 | ::testing::InitGoogleTest(&argc, argv); |
f67539c2 | 688 | RegisterCustomObjects(argc, argv); |
7c673cae FG |
689 | return RUN_ALL_TESTS(); |
690 | } | |
691 | #else | |
692 | #include <stdio.h> | |
693 | ||
11fdf7f2 | 694 | int main(int /*argc*/, char** /*argv*/) { |
7c673cae FG |
695 | fprintf(stderr, "SKIPPED as LDBCommand is not supported in ROCKSDB_LITE\n"); |
696 | return 0; | |
697 | } | |
698 | ||
699 | #endif // ROCKSDB_LITE |