diff --git a/include/rocksdb/ldb_tool.h b/include/rocksdb/ldb_tool.h index 22ea7734f6..e6d992d576 100644 --- a/include/rocksdb/ldb_tool.h +++ b/include/rocksdb/ldb_tool.h @@ -27,15 +27,15 @@ struct LDBOptions { // Default: Slice::ToString() std::shared_ptr key_formatter; - std::string print_help_header = "ldb - RocksDB Tool"; + std::string print_help_header = "beezcli - Speedb Tool"; }; class LDBTool { public: - void Run( - int argc, char** argv, Options db_options = Options(), - const LDBOptions& ldb_options = LDBOptions(), - const std::vector* column_families = nullptr); + void Run(int argc, char** argv, Options db_options = Options(), + const LDBOptions& ldb_options = LDBOptions(), + const std::vector* column_families = nullptr, + bool exit_with_retcode = true); }; } // namespace ROCKSDB_NAMESPACE diff --git a/include/rocksdb/utilities/ldb_cmd.h b/include/rocksdb/utilities/ldb_cmd.h index 0076381925..0e4f53d351 100644 --- a/include/rocksdb/utilities/ldb_cmd.h +++ b/include/rocksdb/utilities/ldb_cmd.h @@ -73,6 +73,7 @@ class LDBCommand { static const std::string ARG_PREPOPULATE_BLOB_CACHE; static const std::string ARG_DECODE_BLOB_INDEX; static const std::string ARG_DUMP_UNCOMPRESSED_BLOBS; + static const std::string ARG_INTERACTIVE; struct ParsedParams { std::string cmd; @@ -191,6 +192,9 @@ class LDBCommand { bool create_if_missing_; + // If true will not print values for dump, idump, scan + bool is_no_value_; + /** * Map of options passed on the command-line. */ @@ -207,6 +211,9 @@ class LDBCommand { /** Shared pointer to underlying environment if applicable **/ std::shared_ptr env_guard_; + /** ttl value for dbwithttl::open **/ + int32_t ttl_; + bool ParseKeyValue(const std::string& line, std::string* key, std::string* value, bool is_key_hex, bool is_value_hex); diff --git a/monitoring/histogram.h b/monitoring/histogram.h index 41af014422..daf0c994d2 100644 --- a/monitoring/histogram.h +++ b/monitoring/histogram.h @@ -106,6 +106,7 @@ class Histogram { virtual const char* Name() const = 0; virtual uint64_t min() const = 0; virtual uint64_t max() const = 0; + virtual uint64_t sum() const = 0; virtual uint64_t num() const = 0; virtual double Median() const = 0; virtual double Percentile(double p) const = 0; @@ -132,6 +133,7 @@ class HistogramImpl : public Histogram { virtual uint64_t min() const override { return stats_.min(); } virtual uint64_t max() const override { return stats_.max(); } virtual uint64_t num() const override { return stats_.num(); } + virtual uint64_t sum() const override { return stats_.sum(); } virtual double Median() const override; virtual double Percentile(double p) const override; virtual double Average() const override; diff --git a/monitoring/histogram_windowing.h b/monitoring/histogram_windowing.h index f8da07b366..b2a9e25800 100644 --- a/monitoring/histogram_windowing.h +++ b/monitoring/histogram_windowing.h @@ -38,6 +38,7 @@ class HistogramWindowingImpl : public Histogram virtual uint64_t min() const override { return stats_.min(); } virtual uint64_t max() const override { return stats_.max(); } virtual uint64_t num() const override { return stats_.num(); } + virtual uint64_t sum() const override { return stats_.sum(); } virtual double Median() const override; virtual double Percentile(double p) const override; virtual double Average() const override; diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 147282474e..90e76e26ee 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,11 +1,12 @@ set(CORE_TOOLS sst_dump.cc - ldb.cc) + ldb.cc + beezcli.cc) foreach(src ${CORE_TOOLS}) get_filename_component(exename ${src} NAME_WE) add_executable(${exename}${ARTIFACT_SUFFIX} ${src}) - target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${ROCKSDB_LIB}) + target_link_libraries(${exename}${ARTIFACT_SUFFIX} ${ROCKSDB_LIB} readline) list(APPEND core_tool_deps ${exename}) endforeach() diff --git a/tools/beezcli.cc b/tools/beezcli.cc new file mode 100644 index 0000000000..7a9606d591 --- /dev/null +++ b/tools/beezcli.cc @@ -0,0 +1,124 @@ +// Copyright (C) 2023 Speedb Ltd. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +// without this flag make format will force stdio to be after readline +// which may cause compilation error on clang +// clang-format off +#include +// clang-format on +#include +#include +#include + +#include +#include +#include + +#include "rocksdb/ldb_tool.h" + +void SignalHandler(int sigint) { + std::cout << std::endl << "Ciao" << std::endl; + exit(0); +} +void ToArgv(std::string const& input, std::vector& temp) { + std::istringstream buffer(input); + std::copy(std::istream_iterator(buffer), + std::istream_iterator(), std::back_inserter(temp)); +} +int main(int argc, char** argv) { + signal(SIGINT, &SignalHandler); + ROCKSDB_NAMESPACE::LDBTool tool; + std::string prompt = "beezcli> "; + const char* const short_opts = "dis\0"; + const option long_opts[] = {{"db", required_argument, 0, 'd'}, + {"interactive", no_argument, nullptr, 'i'}, + {"secondary_path", required_argument, 0, 's'}, + {0, 0, 0, 0}}; + int opt; + std::string db_path = ""; + std::string secondary_path = ""; + bool i = false; + bool d = false; + bool s [[maybe_unused]] = false; + opterr = 0; + opt = getopt_long(argc, argv, short_opts, long_opts, nullptr); + while (opt != -1) { + switch (opt) { + case 'd': + db_path = std::string(optarg); + std::cout << db_path << std::endl; + d = true; + break; + case 'i': + i = true; + break; + case 's': + secondary_path = std::string(optarg); + s = true; + break; + } + opt = getopt_long(argc, argv, short_opts, long_opts, nullptr); + } + char* line; + if (i && !d) { + std::cerr << "interactive flag provided without --db" << std::endl; + return EINVAL; + } + while (i && d && (line = readline(prompt.c_str())) && line) { + if (line[0] != '\0') add_history(line); + std::string input(line); + free(line); + line = nullptr; + if (input == "help") { + char** help = new char*[2]; + help[0] = argv[0]; + help[1] = const_cast("--help"); + tool.Run(2, help, ROCKSDB_NAMESPACE::Options(), + ROCKSDB_NAMESPACE::LDBOptions(), nullptr, false); + continue; + } + if (input == "quit" || input == "exit") { + SignalHandler(0); + } + if (!input.empty()) { + if (!s) { + std::vector vec; + ToArgv(std::string(argv[0]) + " " + input + " --db=" + db_path, vec); + std::vector cstrings{}; + for (const auto& string : vec) { + cstrings.push_back(const_cast(string.c_str())); + } + tool.Run(cstrings.size(), cstrings.data(), ROCKSDB_NAMESPACE::Options(), + ROCKSDB_NAMESPACE::LDBOptions(), nullptr, false); + } else { + std::vector vec; + ToArgv(std::string(argv[0]) + " " + input + " --db=" + db_path + + " --secondary_path=" + secondary_path, + vec); + std::vector cstrings{}; + for (const auto& string : vec) { + cstrings.push_back(const_cast(string.c_str())); + } + tool.Run(cstrings.size(), cstrings.data(), ROCKSDB_NAMESPACE::Options(), + ROCKSDB_NAMESPACE::LDBOptions(), nullptr, false); + } + } + } + if (line == nullptr && i && d) { + SignalHandler(0); + } + tool.Run(argc, argv); + return 0; +} diff --git a/tools/ldb_cmd.cc b/tools/ldb_cmd.cc index a3d61778b5..edaba4552a 100644 --- a/tools/ldb_cmd.cc +++ b/tools/ldb_cmd.cc @@ -107,6 +107,7 @@ const std::string LDBCommand::ARG_PREPOPULATE_BLOB_CACHE = const std::string LDBCommand::ARG_DECODE_BLOB_INDEX = "decode_blob_index"; const std::string LDBCommand::ARG_DUMP_UNCOMPRESSED_BLOBS = "dump_uncompressed_blobs"; +const std::string LDBCommand::ARG_INTERACTIVE = "interactive"; const char* LDBCommand::DELIM = " ==> "; @@ -213,6 +214,9 @@ LDBCommand* LDBCommand::SelectCommand(const ParsedParams& parsed_params) { } else if (parsed_params.cmd == BatchPutCommand::Name()) { return new BatchPutCommand(parsed_params.cmd_params, parsed_params.option_map, parsed_params.flags); + } else if (parsed_params.cmd == MultiGetCommand::Name()) { + return new MultiGetCommand(parsed_params.cmd_params, + parsed_params.option_map, parsed_params.flags); } else if (parsed_params.cmd == ScanCommand::Name()) { return new ScanCommand(parsed_params.cmd_params, parsed_params.option_map, parsed_params.flags); @@ -384,7 +388,8 @@ LDBCommand::LDBCommand(const std::map& options, create_if_missing_(false), option_map_(options), flags_(flags), - valid_cmd_line_options_(valid_cmd_line_options) { + valid_cmd_line_options_(valid_cmd_line_options), + ttl_(-1) { auto itr = options.find(ARG_DB); if (itr != options.end()) { db_path_ = itr->second; @@ -415,7 +420,9 @@ LDBCommand::LDBCommand(const std::map& options, is_key_hex_ = IsKeyHex(options, flags); is_value_hex_ = IsValueHex(options, flags); - is_db_ttl_ = IsFlagPresent(flags, ARG_TTL); + ParseIntOption(option_map_, ARG_TTL, ttl_, exec_state_); + is_db_ttl_ = ((ttl_ != -1) || IsFlagPresent(flags, ARG_TTL)); + is_no_value_ = IsFlagPresent(flags, ARG_NO_VALUE); timestamp_ = IsFlagPresent(flags, ARG_TIMESTAMP); try_load_options_ = IsTryLoadOptions(options, flags); force_consistency_checks_ = @@ -440,19 +447,28 @@ void LDBCommand::OpenDB() { Status st; std::vector handles_opened; if (is_db_ttl_) { - // ldb doesn't yet support TTL DB with multiple column families - if (!column_family_name_.empty() || !column_families_.empty()) { - exec_state_ = LDBCommandExecuteResult::Failed( - "ldb doesn't support TTL DB with multiple column families"); - } if (!secondary_path_.empty()) { exec_state_ = LDBCommandExecuteResult::Failed( "Open as secondary is not supported for TTL DB yet."); } + std::vector ttls; + for (size_t i = 0; i < column_families_.size(); ++i) { + ttls.push_back(ttl_); + } if (is_read_only_) { - st = DBWithTTL::Open(options_, db_path_, &db_ttl_, 0, true); + if (!column_families_.empty()) { + st = DBWithTTL::Open(options_, db_path_, column_families_, + &handles_opened, &db_ttl_, ttls, true); + } else { + st = DBWithTTL::Open(options_, db_path_, &db_ttl_, ttl_, true); + } } else { - st = DBWithTTL::Open(options_, db_path_, &db_ttl_); + if (!column_families_.empty()) { + st = DBWithTTL::Open(options_, db_path_, column_families_, + &handles_opened, &db_ttl_, ttls); + } else { + st = DBWithTTL::Open(options_, db_path_, &db_ttl_, ttl_); + } } db_ = db_ttl_; } else { @@ -500,7 +516,6 @@ void LDBCommand::OpenDB() { } } else { // We successfully opened DB in single column family mode. - assert(column_families_.empty()); if (column_family_name_ != kDefaultColumnFamilyName) { exec_state_ = LDBCommandExecuteResult::Failed( "Non-existing column family " + column_family_name_); @@ -1087,6 +1102,7 @@ std::string LDBCommand::HelpRangeCmdArgs() { str_stream << " "; str_stream << "[--" << ARG_FROM << "] "; str_stream << "[--" << ARG_TO << "] "; + str_stream << "[--" << ARG_TTL << "[=]] "; return str_stream.str(); } @@ -1118,8 +1134,7 @@ bool LDBCommand::IsTryLoadOptions( // to false. TODO: TTL_DB may need to fix that, otherwise it's unable to open // DB which has incompatible setting with default options. bool default_val = (options.find(ARG_DB) != options.end()) && - !IsFlagPresent(flags, ARG_CREATE_IF_MISSING) && - !IsFlagPresent(flags, ARG_TTL); + !IsFlagPresent(flags, ARG_CREATE_IF_MISSING); return ParseBooleanOption(options, ARG_TRY_LOAD_OPTIONS, default_val); } @@ -1764,11 +1779,12 @@ InternalDumpCommand::InternalDumpCommand( const std::vector& /*params*/, const std::map& options, const std::vector& flags) - : LDBCommand(options, flags, true, - BuildCmdLineOptions( - {ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, ARG_FROM, ARG_TO, - ARG_MAX_KEYS, ARG_COUNT_ONLY, ARG_COUNT_DELIM, ARG_STATS, - ARG_INPUT_KEY_HEX, ARG_DECODE_BLOB_INDEX})), + : LDBCommand( + options, flags, true, + BuildCmdLineOptions( + {ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, ARG_NO_VALUE, ARG_FROM, + ARG_TO, ARG_MAX_KEYS, ARG_COUNT_ONLY, ARG_COUNT_DELIM, ARG_STATS, + ARG_INPUT_KEY_HEX, ARG_DECODE_BLOB_INDEX, ARG_TTL})), has_from_(false), has_to_(false), max_keys_(-1), @@ -1814,9 +1830,11 @@ void InternalDumpCommand::Help(std::string& ret) { ret.append(" [--" + ARG_INPUT_KEY_HEX + "]"); ret.append(" [--" + ARG_MAX_KEYS + "=]"); ret.append(" [--" + ARG_COUNT_ONLY + "]"); + ret.append(" [--" + ARG_NO_VALUE + "]"); ret.append(" [--" + ARG_COUNT_DELIM + "=]"); ret.append(" [--" + ARG_STATS + "]"); ret.append(" [--" + ARG_DECODE_BLOB_INDEX + "]"); + ret.append(" [--" + ARG_TTL + "[=]]"); ret.append("\n"); } @@ -1825,7 +1843,8 @@ void InternalDumpCommand::DoCommand() { assert(GetExecuteState().IsFailed()); return; } - + HistogramImpl vsize; + HistogramImpl ksize; if (print_stats_) { std::string stats; if (db_->GetProperty(GetCfHandle(), "rocksdb.stats", &stats)) { @@ -1884,9 +1903,38 @@ void InternalDumpCommand::DoCommand() { if (!count_only_ && !count_delim_) { std::string key = ikey.DebugString(is_key_hex_); Slice value(key_version.value); + std::string valuestr = value.ToString(is_value_hex_); + if (print_stats_) { + ksize.Add(key.size()); + vsize.Add(valuestr.size()); + } + // support value with ts + if (is_db_ttl_) { + // keep in mind it might in some scenarios strip the value if opened a + // non ttl db with ttl. The sanity check is unable to test if the value + // stripped is ok or not. do not open a regular db with the ttl flag + st = DBWithTTLImpl::SanityCheckTimestamp(valuestr); + if (!st.ok()) { + fprintf(stderr, "%s => error striping ts, error: %s \n", key.c_str(), + st.ToString().c_str()); + continue; + } + // keep in mind it might in some scenarios strip the value if opened a + // non ttl db with ttl. + st = DBWithTTLImpl::StripTS(&valuestr); + if (!st.ok()) { + fprintf(stderr, "%s => error striping ts, error: %s \n", key.c_str(), + st.ToString().c_str()); + continue; + } + } if (!decode_blob_index_ || value_type != kTypeBlobIndex) { - fprintf(stdout, "%s => %s\n", key.c_str(), - value.ToString(is_value_hex_).c_str()); + if (is_no_value_) { + fprintf(stdout, "%s\n", key.c_str()); + } else { + fprintf(stdout, "%s => %s\n", key.c_str(), valuestr.c_str()); + } + } else { BlobIndex blob_index; @@ -1894,8 +1942,12 @@ void InternalDumpCommand::DoCommand() { if (!s.ok()) { fprintf(stderr, "%s => error decoding blob index =>\n", key.c_str()); } else { - fprintf(stdout, "%s => %s\n", key.c_str(), - blob_index.DebugString(is_value_hex_).c_str()); + if (is_no_value_) { + fprintf(stdout, "%s\n", key.c_str()); + } else { + fprintf(stdout, "%s => %s\n", key.c_str(), + blob_index.DebugString(is_value_hex_).c_str()); + } } } } @@ -1909,6 +1961,16 @@ void InternalDumpCommand::DoCommand() { } else { fprintf(stdout, "Internal keys in range: %lld\n", count); } + if (count_only_ || print_stats_) { + fprintf(stdout, "\nKey size distribution: \n"); + fprintf(stdout, "\nSum of keys' sizes in range: %" PRIu64 "\n", + ksize.sum()); + fprintf(stdout, "%s\n", ksize.ToString().c_str()); + fprintf(stdout, "Value size distribution: \n"); + fprintf(stdout, "\nSum of values' sizes in range: %" PRIu64 "\n", + vsize.sum()); + fprintf(stdout, "%s\n", vsize.ToString().c_str()); + } } const std::string DBDumperCommand::ARG_COUNT_ONLY = "count_only"; @@ -1920,13 +1982,13 @@ DBDumperCommand::DBDumperCommand( const std::vector& /*params*/, const std::map& options, const std::vector& flags) - : LDBCommand( - options, flags, true, - BuildCmdLineOptions( - {ARG_TTL, ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, ARG_FROM, ARG_TO, - ARG_MAX_KEYS, ARG_COUNT_ONLY, ARG_COUNT_DELIM, ARG_STATS, - ARG_TTL_START, ARG_TTL_END, ARG_TTL_BUCKET, ARG_TIMESTAMP, - ARG_PATH, ARG_DECODE_BLOB_INDEX, ARG_DUMP_UNCOMPRESSED_BLOBS})), + : LDBCommand(options, flags, true, + BuildCmdLineOptions( + {ARG_TTL, ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX, + ARG_NO_VALUE, ARG_FROM, ARG_TO, ARG_MAX_KEYS, + ARG_COUNT_ONLY, ARG_COUNT_DELIM, ARG_STATS, ARG_TTL_START, + ARG_TTL_END, ARG_TTL_BUCKET, ARG_TIMESTAMP, ARG_PATH, + ARG_DECODE_BLOB_INDEX, ARG_DUMP_UNCOMPRESSED_BLOBS})), null_from_(true), null_to_(true), max_keys_(-1), @@ -1998,7 +2060,7 @@ void DBDumperCommand::Help(std::string& ret) { ret.append(" "); ret.append(DBDumperCommand::Name()); ret.append(HelpRangeCmdArgs()); - ret.append(" [--" + ARG_TTL + "]"); + ret.append(" [--" + ARG_TTL + "[=]]"); ret.append(" [--" + ARG_MAX_KEYS + "=]"); ret.append(" [--" + ARG_TIMESTAMP + "]"); ret.append(" [--" + ARG_COUNT_ONLY + "]"); @@ -2136,7 +2198,7 @@ void DBDumperCommand::DoDumpCommand() { } HistogramImpl vsize_hist; - + HistogramImpl ksize_hist; for (; iter->Valid(); iter->Next()) { int rawtime = 0; // If end marker was specified, we stop before it @@ -2181,18 +2243,25 @@ void DBDumperCommand::DoDumpCommand() { } - if (count_only_) { + if (count_only_ || print_stats_) { vsize_hist.Add(iter->value().size()); + ksize_hist.Add(iter->key().size()); } if (!count_only_ && !count_delim_) { if (is_db_ttl_ && timestamp_) { fprintf(stdout, "%s ", TimeToHumanString(rawtime).c_str()); } - std::string str = - PrintKeyValue(iter->key().ToString(), iter->value().ToString(), - is_key_hex_, is_value_hex_); - fprintf(stdout, "%s\n", str.c_str()); + if (is_no_value_) { + std::string str = is_key_hex_ ? StringToHex(iter->key().ToString()) + : iter->key().ToString(); + fprintf(stdout, "%s\n", str.c_str()); + } else { + std::string str = + PrintKeyValue(iter->key().ToString(), iter->value().ToString(), + is_key_hex_, is_value_hex_); + fprintf(stdout, "%s\n", str.c_str()); + } } } @@ -2206,8 +2275,14 @@ void DBDumperCommand::DoDumpCommand() { fprintf(stdout, "Keys in range: %" PRIu64 "\n", count); } - if (count_only_) { + if (count_only_ || print_stats_) { + fprintf(stdout, "\nKey size distribution: \n"); + fprintf(stdout, "\nSum of keys' sizes in range: %" PRIu64 "\n", + ksize_hist.sum()); + fprintf(stdout, "%s\n", ksize_hist.ToString().c_str()); fprintf(stdout, "Value size distribution: \n"); + fprintf(stdout, "\nSum of values' sizes in range: %" PRIu64 "\n", + vsize_hist.sum()); fprintf(stdout, "%s\n", vsize_hist.ToString().c_str()); } // Clean up @@ -2769,7 +2844,7 @@ void GetCommand::Help(std::string& ret) { ret.append(" "); ret.append(GetCommand::Name()); ret.append(" "); - ret.append(" [--" + ARG_TTL + "]"); + ret.append(" [--" + ARG_TTL + "[=]]"); ret.append("\n"); } @@ -2921,6 +2996,55 @@ void BatchPutCommand::OverrideBaseOptions() { options_.create_if_missing = create_if_missing_; } +// ---------------------------------------------------------------------------- +MultiGetCommand::MultiGetCommand( + const std::vector& params, + const std::map& options, + const std::vector& flags) + : LDBCommand( + options, flags, false, + BuildCmdLineOptions({ARG_TTL, ARG_HEX, ARG_KEY_HEX, ARG_VALUE_HEX})) { + if (params.size() < 1) { + exec_state_ = LDBCommandExecuteResult::Failed( + "At least one must be specified multiget."); + } + keys_ = params; +} +void MultiGetCommand::Help(std::string& ret) { + ret.append(" "); + ret.append(MultiGetCommand::Name()); + ret.append(" [] [..]"); + ret.append(" [--" + ARG_TTL + "[=]]"); + ret.append("\n"); +} + +void MultiGetCommand::DoCommand() { + if (!db_) { + assert(GetExecuteState().IsFailed()); + return; + } + + Status st; + std::vector statuses; + std::vector values; + ReadOptions ropts; + std::vector keys; + for (const auto& key : keys_) { + keys.push_back(key); + } + statuses = db_->MultiGet(ropts, keys, &values); + for (size_t i = 0; i < statuses.size(); ++i) { + if (statuses[i].ok()) { + fprintf(stdout, "%s\n", + PrintKeyValue(keys[i].ToString().c_str(), values[i], is_key_hex_, + is_value_hex_) + .c_str()); + } else { + fprintf(stderr, "Cannot get: %s, error: %s\n", keys[i].ToString().c_str(), + statuses[i].ToString().c_str()); + } + } +} // ---------------------------------------------------------------------------- ScanCommand::ScanCommand(const std::vector& /*params*/, @@ -2980,7 +3104,6 @@ void ScanCommand::Help(std::string& ret) { ret.append(" "); ret.append(ScanCommand::Name()); ret.append(HelpRangeCmdArgs()); - ret.append(" [--" + ARG_TTL + "]"); ret.append(" [--" + ARG_TIMESTAMP + "]"); ret.append(" [--" + ARG_MAX_KEYS + "=q] "); ret.append(" [--" + ARG_TTL_START + "=:- is inclusive]"); @@ -3256,7 +3379,7 @@ DBQuerierCommand::DBQuerierCommand( void DBQuerierCommand::Help(std::string& ret) { ret.append(" "); ret.append(DBQuerierCommand::Name()); - ret.append(" [--" + ARG_TTL + "]"); + ret.append(" [--" + ARG_TTL + "[=]]"); ret.append("\n"); ret.append(" Starts a REPL shell. Type help for list of available " "commands."); diff --git a/tools/ldb_cmd_impl.h b/tools/ldb_cmd_impl.h index 17848f2cd4..b1ec07ec7e 100644 --- a/tools/ldb_cmd_impl.h +++ b/tools/ldb_cmd_impl.h @@ -441,6 +441,25 @@ class BatchPutCommand : public LDBCommand { std::vector> key_values_; }; +class MultiGetCommand : public LDBCommand { + public: + static std::string Name() { return "multiget"; } + + MultiGetCommand(const std::vector& params, + const std::map& options, + const std::vector& flags); + + void DoCommand() override; + + static void Help(std::string& ret); + + private: + /** + * The keys to be fetched. + */ + std::vector keys_; +}; + class ScanCommand : public LDBCommand { public: static std::string Name() { return "scan"; } diff --git a/tools/ldb_test.py b/tools/ldb_test.py index c69c5ca732..d711e94353 100644 --- a/tools/ldb_test.py +++ b/tools/ldb_test.py @@ -165,6 +165,12 @@ def testStringBatchPut(self): self.assertRunFAIL("batchput k1") self.assertRunFAIL("batchput k1 v1 k2") + def testMultiGet(self): + print("Running testMultiGet...") + self.assertRunOK("batchput x1 y1 x2 y2 --create_if_missing", "OK") + self.assertRunOK("multiget x1 x2", "x1 ==> y1\nx2 ==> y2") + self.assertRunFAIL("multiget x2 x3") + def testBlobBatchPut(self): print("Running testBlobBatchPut...") diff --git a/tools/ldb_tool.cc b/tools/ldb_tool.cc index b1a19ab058..8223303178 100644 --- a/tools/ldb_tool.cc +++ b/tools/ldb_tool.cc @@ -24,6 +24,9 @@ void LDBCommandRunner::PrintHelp(const LDBOptions& ldb_options, "= when necessary\n"); ret.append("\n"); ret.append("commands can optionally specify\n"); + ret.append(" --" + LDBCommand::ARG_INTERACTIVE + + " to enter interactive interface"); + ret.append("\n"); ret.append(" --" + LDBCommand::ARG_ENV_URI + "= or --" + LDBCommand::ARG_FS_URI + "= if necessary"); ret.append("\n"); @@ -48,9 +51,14 @@ void LDBCommandRunner::PrintHelp(const LDBOptions& ldb_options, " --" + LDBCommand::ARG_CF_NAME + "= : name of the column family to operate on. default: default " "column family\n"); - ret.append(" --" + LDBCommand::ARG_TTL + - " with 'put','get','scan','dump','query','batchput'" - " : DB supports ttl and value is internally timestamp-suffixed\n"); + ret.append( + " --" + LDBCommand::ARG_TTL + + " with 'put','get','scan','dump','query','batchput','multiget','compact'" + " : DB supports ttl and value is internally timestamp-suffixed\n" + " Make sure to use --" + + LDBCommand::ARG_TTL + + " only for db created with ttl otherwise you may lead to a data " + "corruption\n"); ret.append(" --" + LDBCommand::ARG_TRY_LOAD_OPTIONS + " : Try to load option file from DB. Default to true if " + LDBCommand::ARG_DB + @@ -93,6 +101,7 @@ void LDBCommandRunner::PrintHelp(const LDBOptions& ldb_options, PutCommand::Help(ret); GetCommand::Help(ret); BatchPutCommand::Help(ret); + MultiGetCommand::Help(ret); ScanCommand::Help(ret); DeleteCommand::Help(ret); DeleteRangeCommand::Help(ret); @@ -174,10 +183,13 @@ int LDBCommandRunner::RunCommand( void LDBTool::Run(int argc, char** argv, Options options, const LDBOptions& ldb_options, - const std::vector* column_families) { + const std::vector* column_families, + bool exit_with_retcode) { int error_code = LDBCommandRunner::RunCommand(argc, argv, options, ldb_options, column_families); - exit(error_code); + if (exit_with_retcode) { + exit(error_code); + } } } // namespace ROCKSDB_NAMESPACE