From d0f05c6cc5bf05c6e670ab8b338433e4334898e2 Mon Sep 17 00:00:00 2001 From: Tonko Sabolcec Date: Mon, 27 May 2019 15:33:03 +0200 Subject: [PATCH] Create simple dump executable Summary: Implemented a simple dump executable which runs `RETURN dump()` on the database using Bolt protocol. The `dump()` function returns a list of openCypher queries needed for database reconstruction (note: in the future we should use more appropriate method that supports streaming of data). Reviewers: teon.banek, mferencevic Reviewed By: teon.banek Subscribers: pullbot, mferencevic Differential Revision: https://phabricator.memgraph.io/D2067 --- .../interpret/awesome_memgraph_functions.cpp | 10 +- tools/src/CMakeLists.txt | 4 + tools/src/mg_dump/main.cpp | 101 ++++++++++++++++++ 3 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 tools/src/mg_dump/main.cpp diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp index baf563f02..dede0b9b5 100644 --- a/src/query/interpret/awesome_memgraph_functions.cpp +++ b/src/query/interpret/awesome_memgraph_functions.cpp @@ -904,9 +904,13 @@ TypedValue Dump(TypedValue *args, int64_t nargs, const EvaluationContext &, if (nargs != 0) { throw QueryRuntimeException("'dump' does not expect any arguments."); } - std::ostringstream oss; - database::DumpToCypher(&oss, dba); - return oss.str(); + + std::vector queries; + database::CypherDumpGenerator dump(dba); + for (std::ostringstream oss; dump.NextQuery(&oss); oss.str("")) { + queries.emplace_back(oss.str()); + } + return queries; } #endif // MG_SINGLE_NODE diff --git a/tools/src/CMakeLists.txt b/tools/src/CMakeLists.txt index a1fc382ce..ebc136d4a 100644 --- a/tools/src/CMakeLists.txt +++ b/tools/src/CMakeLists.txt @@ -19,6 +19,10 @@ if (READLINE_FOUND) endif() target_link_libraries(mg_client ${CLIENT_LIBS}) +# Memgraph Dump Target +add_executable(mg_dump mg_dump/main.cpp) +target_link_libraries(mg_dump mg-communication mg-io) + # Strip the executable in release build. string(TOLOWER ${CMAKE_BUILD_TYPE} lower_build_type) diff --git a/tools/src/mg_dump/main.cpp b/tools/src/mg_dump/main.cpp new file mode 100644 index 000000000..173cd7b3f --- /dev/null +++ b/tools/src/mg_dump/main.cpp @@ -0,0 +1,101 @@ +#include +#include +#include + +#include +#include + +#include "communication/bolt/client.hpp" +#include "io/network/endpoint.hpp" +#include "io/network/utils.hpp" +#include "version.hpp" + +namespace fs = std::filesystem; + +const char *kUsage = + "Memgraph dump tool.\n" + "A simple tool for dumping Memgraph database's data as list of openCypher " + "queries.\n"; + +DEFINE_string(host, "127.0.0.1", + "Server address. It can be a DNS resolvable hostname."); +DEFINE_int32(port, 7687, "Server port"); +DEFINE_string(username, "", "Username for the database"); +DEFINE_string(password, "", "Password for the database"); +DEFINE_bool(use_ssl, true, "Use SSL when connecting to the server"); +DEFINE_string(output, "", "Output target file path"); + +DECLARE_int32(min_log_level); + +// NOLINTNEXTLINE(bugprone-exception-escape) +int main(int argc, char **argv) { + gflags::SetVersionString(version_string); + gflags::SetUsageMessage(kUsage); + gflags::ParseCommandLineFlags(&argc, &argv, true); + + // TODO(tsabolcec): `FLAGS_min_log_level` is here to silent logs from + // `communication::bolt::Client`. Remove this setting once we move to C + // mg-bolt library which doesn't use glog. + FLAGS_min_log_level = google::ERROR; + google::InitGoogleLogging(argv[0]); + + communication::Init(); + + io::network::Endpoint endpoint(io::network::ResolveHostname(FLAGS_host), + FLAGS_port); + communication::ClientContext context(FLAGS_use_ssl); + communication::bolt::Client client(&context); + + if (FLAGS_output.empty()) { + std::cerr << "Output file not specified. Please run mg_dump --help for " + "more instructions." + << std::endl; + return 1; + } + + try { + const std::string bolt_client_version = + fmt::format("mg_dump/{}", gflags::VersionString()); + client.Connect(endpoint, FLAGS_username, FLAGS_password, + bolt_client_version); + } catch (const communication::bolt::ClientFatalException &e) { + std::cerr << "Connection failed: " << e.what() << std::endl; + return 1; + } + + // TODO(tsabolcec): Consider using the standard output instead of a file. + // That way we could use pipe for importing data at the same time, eg. + // `mg_dump --server=S1 | mg_client --server=S2`. + std::ofstream out(FLAGS_output, std::fstream::out); + if (!out.is_open()) { + std::cerr << "Error: couldn't open file \"" << FLAGS_output + << "\" for writing" << std::endl; + return 1; + } + + try { + auto ret = client.Execute("RETURN DUMP()", {}); + if (ret.records.size() != 1 || ret.records[0].size() != 1) { + std::cerr << "Error: client received response in unexpected format" + << std::endl; + out.close(); + CHECK(fs::remove(FLAGS_output)) << "Failed to remove output target file " + << FLAGS_output; + return 1; + } + + const auto &list = ret.records[0][0]; + for (const auto &query : list.ValueList()) { + out << query.ValueString() << std::endl; + } + out.close(); + } catch (const communication::bolt::ClientFatalException &e) { + std::cerr << "Client received exception: " << e.what() << std::endl; + out.close(); + CHECK(fs::remove(FLAGS_output)) << "Failed to remove output target file " + << FLAGS_output; + return 1; + } + + return 0; +}