// Copyright (c) 2012 Facebook. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef LEVELDB_UTIL_LDB_H_ #define LEVELDB_UTIL_LDB_H_ #include #include #include #include #include #include #include "leveldb/db.h" #include "leveldb/env.h" #include "leveldb/options.h" #include "leveldb/iterator.h" #include "leveldb/slice.h" #include "db/version_set.h" #include "util/logging.h" namespace leveldb { class LDBCommandExecuteResult { public: enum State { EXEC_NOT_STARTED = 0, EXEC_SUCCEED = 1, EXEC_FAILED = 2, }; LDBCommandExecuteResult() { state_ = EXEC_NOT_STARTED; message_ = ""; } LDBCommandExecuteResult(State state, std::string& msg) { state_ = state; message_ = msg; } std::string ToString() { std::string ret; switch (state_) { case EXEC_SUCCEED: break; case EXEC_FAILED: ret.append("Failed: "); break; case EXEC_NOT_STARTED: ret.append("Not started: "); } if (!message_.empty()) { ret.append(message_); } return ret; } void Reset() { state_ = EXEC_NOT_STARTED; message_ = ""; } bool IsSucceed() { return state_ == EXEC_SUCCEED; } bool IsNotStarted() { return state_ == EXEC_NOT_STARTED; } bool IsFailed() { return state_ == EXEC_FAILED; } static LDBCommandExecuteResult SUCCEED(std::string msg) { return LDBCommandExecuteResult(EXEC_SUCCEED, msg); } static LDBCommandExecuteResult FAILED(std::string msg) { return LDBCommandExecuteResult(EXEC_FAILED, msg); } private: State state_; std::string message_; bool operator==(const LDBCommandExecuteResult&); bool operator!=(const LDBCommandExecuteResult&); }; class LDBCommand { public: /* Constructor */ LDBCommand(std::string& db_name, std::vector& args) : db_path_(db_name), db_(NULL) { parse_open_args(args); } LDBCommand(std::vector& args) : db_path_(""), db_(NULL) { parse_open_args(args); } virtual leveldb::Options PrepareOptionsForOpenDB(); virtual bool NoDBOpen() { return false; } virtual ~LDBCommand() { if (db_ != NULL) { delete db_; db_ = NULL; } } /* Print the help message */ static void Help(std::string& ret) { ret.append("--db=DB_PATH ["); ret.append(LDBCommand::BLOOM_ARG); ret.append("] ["); ret.append(LDBCommand::COMPRESSION_TYPE_ARG); ret.append("] ["); ret.append(LDBCommand::BLOCK_SIZE); ret.append("] ["); ret.append(LDBCommand::AUTO_COMPACTION); ret.append("] ["); ret.append(LDBCommand::WRITE_BUFFER_SIZE_ARG); ret.append("] ["); ret.append(LDBCommand::FILE_SIZE_ARG); ret.append("] "); } /* Run the command, and return the execute result. */ void Run() { if (!exec_state_.IsNotStarted()) { return; } if (db_ == NULL && !NoDBOpen()) { OpenDB(); if (!exec_state_.IsNotStarted()) { return; } } DoCommand(); if (exec_state_.IsNotStarted()) { exec_state_ = LDBCommandExecuteResult::SUCCEED(""); } if (db_ != NULL) { CloseDB (); } } virtual void DoCommand() = 0; LDBCommandExecuteResult GetExecuteState() { return exec_state_; } void ClearPreviousRunState() { exec_state_.Reset(); } static std::string HexToString(const std::string& str) { std::string parsed; for (unsigned int i = 0; i < str.length();) { int c; sscanf(str.c_str() + i, "%2X", &c); parsed.push_back(c); i += 2; } return parsed; } static std::string StringToHex(const std::string& str) { std::string result; char buf[10]; for (size_t i = 0; i < str.length(); i++) { snprintf(buf, 10, "%02X", (unsigned char)str[i]); result += buf; } return result; } static const char* DELIM; static bool ParseKeyValue(const std::string& line, std::string* key, std::string* value, bool hex) { size_t pos = line.find(DELIM); if (pos != std::string::npos) { (*key) = line.substr(0, pos); (*value) = line.substr(pos + strlen(DELIM)); if (hex) { (*key) = HexToString(*key); (*value) = HexToString(*value); } return true; } else { return false; } } static std::string PrintKeyValue(const std::string& key, const std::string& value, bool hex) { std::string result; result.append(hex ? StringToHex(key) : key); result.append(DELIM); result.append(hex ? StringToHex(value) : value); return result; } protected: void OpenDB() { leveldb::Options opt = PrepareOptionsForOpenDB(); if (!exec_state_.IsNotStarted()) { return; } // Open the DB. leveldb::Status st = leveldb::DB::Open(opt, db_path_, &db_); if (!st.ok()) { std::string msg = st.ToString(); exec_state_ = LDBCommandExecuteResult::FAILED(msg); } } void CloseDB () { if (db_ != NULL) { delete db_; db_ = NULL; } } static const char* FROM_ARG; static const char* END_ARG; static const char* HEX_ARG; LDBCommandExecuteResult exec_state_; std::string db_path_; leveldb::DB* db_; private: static const char* BLOOM_ARG; static const char* COMPRESSION_TYPE_ARG; static const char* BLOCK_SIZE; static const char* AUTO_COMPACTION; static const char* WRITE_BUFFER_SIZE_ARG; static const char* FILE_SIZE_ARG; std::vector open_args_; void parse_open_args(std::vector& args); }; class Compactor: public LDBCommand { public: Compactor(std::string& db_name, std::vector& args); virtual ~Compactor() {} static void Help(std::string& ret); virtual void DoCommand(); private: bool null_from_; std::string from_; bool null_to_; std::string to_; bool hex_; }; class DBDumper: public LDBCommand { public: DBDumper(std::string& db_name, std::vector& args); virtual ~DBDumper() {} static void Help(std::string& ret); virtual void DoCommand(); private: bool null_from_; std::string from_; bool null_to_; std::string to_; int max_keys_; bool count_only_; bool print_stats_; bool hex_; bool hex_output_; static const char* MAX_KEYS_ARG; static const char* COUNT_ONLY_ARG; static const char* STATS_ARG; static const char* HEX_OUTPUT_ARG; }; class DBLoader: public LDBCommand { public: DBLoader(std::string& db_name, std::vector& args); virtual ~DBLoader() {} static void Help(std::string& ret); virtual void DoCommand(); virtual leveldb::Options PrepareOptionsForOpenDB(); private: bool hex_input_; bool create_if_missing_; bool disable_wal_; static const char* HEX_INPUT_ARG; static const char* CREATE_IF_MISSING_ARG; static const char* DISABLE_WAL_ARG; }; class DBQuerier: public LDBCommand { public: DBQuerier(std::string& db_name, std::vector& args); virtual ~DBQuerier() {} static void Help(std::string& ret); virtual void DoCommand(); private: bool hex_; static const char* HEX_ARG; static const char* HELP_CMD; static const char* GET_CMD; static const char* PUT_CMD; static const char* DELETE_CMD; }; class ReduceDBLevels : public LDBCommand { public: ReduceDBLevels (std::string& db_name, std::vector& args); ~ReduceDBLevels() {} virtual leveldb::Options PrepareOptionsForOpenDB(); virtual void DoCommand(); virtual bool NoDBOpen() { return true; } static void Help(std::string& msg); static std::vector PrepareArgs(int new_levels, bool print_old_level = false); private: int old_levels_; int new_levels_; bool print_old_levels_; static const char* NEW_LEVLES_ARG; static const char* PRINT_OLD_LEVELS_ARG; Status GetOldNumOfLevels(leveldb::Options& opt, int* levels); }; class WALDumper : public LDBCommand { public: WALDumper (std::vector& args); ~WALDumper() {} virtual bool NoDBOpen() { return true; } static void Help(std::string& ret); virtual void DoCommand(); private: bool print_header_; std::string wal_file_; static const char* WAL_FILE_ARG; }; } #endif