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