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