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