From 231d4b15fc88a9547e12d616f9c936875366f47b Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sat, 2 Mar 2024 15:27:29 +0300 Subject: [PATCH] Add loadable dump format to vitastor-kv (dump) --- src/kv_cli.cpp | 34 ++++++++++++++------------ src/str_util.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ src/str_util.h | 2 ++ 3 files changed, 83 insertions(+), 15 deletions(-) diff --git a/src/kv_cli.cpp b/src/kv_cli.cpp index ab2e3718..92f9fc67 100644 --- a/src/kv_cli.cpp +++ b/src/kv_cli.cpp @@ -118,8 +118,9 @@ void kv_cli_t::run(const json11::Json::object & cfg) finished = true; } }); - interactive = true; - printf("> "); + interactive = isatty(0); + if (interactive) + printf("> "); } catch (std::exception & e) { @@ -236,7 +237,7 @@ void kv_cli_t::handle_cmd(const std::string & cmd, std::function cb) if (res < 0) fprintf(stderr, "Error opening index: %s (code %d)\n", strerror(-res), res); else - printf("Index opened. Current size: %lu bytes\n", db->get_size()); + fprintf(interactive ? stdout : stderr, "Index opened. Current size: %lu bytes\n", db->get_size()); cb(); }); } @@ -272,15 +273,15 @@ void kv_cli_t::handle_cmd(const std::string & cmd, std::function cb) } else if (opname == "get" || opname == "set" || opname == "del") { + std::string key = scan_escaped(cmd, pos); if (opname == "get" || opname == "del") { - if (pos == std::string::npos) + if (key == "") { fprintf(stderr, "Usage: %s \n", opname.c_str()); cb(); return; } - auto key = trim(cmd.substr(pos+1)); if (opname == "get") { db->get(key, [this, cb](int res, const std::string & value) @@ -302,34 +303,33 @@ void kv_cli_t::handle_cmd(const std::string & cmd, std::function cb) if (res < 0) fprintf(stderr, "Error: %s (code %d)\n", strerror(-res), res); else - printf("OK\n"); + fprintf(interactive ? stdout : stderr, "OK\n"); cb(); }); } } else { - auto pos2 = cmd.find_first_of(" \t", pos+1); - if (pos2 == std::string::npos) + if (key == "" || pos >= cmd.size()) { fprintf(stderr, "Usage: set \n"); cb(); return; } - auto key = trim(cmd.substr(pos+1, pos2-pos-1)); - auto value = trim(cmd.substr(pos2+1)); + auto value = trim(cmd.substr(pos)); db->set(key, value, [this, cb](int res) { if (res < 0) fprintf(stderr, "Error: %s (code %d)\n", strerror(-res), res); else - printf("OK\n"); + fprintf(interactive ? stdout : stderr, "OK\n"); cb(); }); } } - else if (opname == "list") + else if (opname == "list" || opname == "dump") { + bool dump = opname == "dump"; std::string start, end; if (pos != std::string::npos) { @@ -358,7 +358,10 @@ void kv_cli_t::handle_cmd(const std::string & cmd, std::function cb) } else { - printf("%s = %s\n", key.c_str(), value.c_str()); + if (dump) + printf("set %s %s\n", auto_addslashes(key).c_str(), value.c_str()); + else + printf("%s = %s\n", key.c_str(), value.c_str()); db->list_next(handle, NULL); } }); @@ -367,7 +370,7 @@ void kv_cli_t::handle_cmd(const std::string & cmd, std::function cb) { db->close([=]() { - printf("Index closed\n"); + fprintf(interactive ? stdout : stderr, "Index closed\n"); cb(); }); } @@ -382,7 +385,8 @@ void kv_cli_t::handle_cmd(const std::string & cmd, std::function cb) stderr, "Unknown operation: %s. Supported operations:\n" "open [block_size]\n" "config \n" - "get \nset \ndel \nlist [ [end]]\n" + "get \nset \ndel \n" + "list [ [end]]\ndump [ [end]]\n" "close\nquit\n", opname.c_str() ); cb(); diff --git a/src/str_util.cpp b/src/str_util.cpp index d55217af..ee3bbb5c 100644 --- a/src/str_util.cpp +++ b/src/str_util.cpp @@ -348,3 +348,65 @@ std::vector explode(const std::string & sep, const std::string & va } return res; } + +// extract possibly double-quoted part of string with escape characters +std::string scan_escaped(const std::string & cmd, size_t & pos) +{ + std::string key; + auto pos2 = cmd.find_first_not_of(" \t\r\n", pos); + if (pos2 == std::string::npos) + { + pos = cmd.size(); + return ""; + } + pos = pos2; + if (cmd[pos] != '"') + { + pos2 = cmd.find_first_of(" \t\r\n", pos); + pos2 = pos2 == std::string::npos ? cmd.size() : pos2; + key = cmd.substr(pos, pos2-pos); + pos2 = cmd.find_first_not_of(" \t\r\n", pos2); + pos = pos2 == std::string::npos ? cmd.size() : pos2; + return key; + } + pos++; + while (pos < cmd.size()) + { + auto pos2 = cmd.find_first_of("\\\"", pos); + pos2 = pos2 == std::string::npos ? cmd.size() : pos2; + if (pos2 > pos) + key += cmd.substr(pos, pos2-pos); + pos = pos2; + if (pos >= cmd.size()) + break; + if (cmd[pos] == '"') + { + pos++; + break; + } + if (cmd[pos] == '\\') + { + if (pos < cmd.size()-1) + key += cmd[++pos]; + pos++; + } + } + return key; +} + +std::string auto_addslashes(const std::string & str) +{ + auto pos = str.find_first_of("\\\""); + if (pos == std::string::npos) + return str; + std::string res = "\""+str.substr(0, pos)+"\\"+str[pos]; + while (pos < str.size()-1) + { + auto pos2 = str.find_first_of("\\\"", pos+1); + if (pos2 == std::string::npos) + return res + str.substr(pos+1) + "\""; + res += str.substr(pos, pos2-pos)+"\\"+str[pos2]; + pos = pos2; + } + return res+"\""; +} diff --git a/src/str_util.h b/src/str_util.h index f0c3c5d8..23c0d8dd 100644 --- a/src/str_util.h +++ b/src/str_util.h @@ -22,3 +22,5 @@ std::string str_repeat(const std::string & str, int times); size_t utf8_length(const std::string & s); size_t utf8_length(const char *s); std::vector explode(const std::string & sep, const std::string & value, bool trim); +std::string scan_escaped(const std::string & cmd, size_t & pos); +std::string auto_addslashes(const std::string & str);