diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..aa1f831433ce9723030c2fb3cd14d95b242d4826 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,98 @@ +cmake_minimum_required(VERSION 3.0.0) + +project("vGraph" CXX) + +set(CMAKE_SKIP_RPATH TRUE) + +set(CMAKE_VERBOSE_MAKEFILE TRUE) + +if (!CMAKE_CXX_COMPILER) + message(FATAL_ERROR "No C++ compiler found") +endif() + +set(CMAKE_CXX_STANDARD 14) + +set(CMAKE_EXE_LINKER_FLAGS "-static-libstdc++") + +# Possible values are Debug, Release, RelWithDebInfo, MinSizeRel +set(CMAKE_BUILD_TYPE "RelWithDebInfo") + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "_build") +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "_build") +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "_build") + +# Set the project home dir +set(VGRAPH_HOME ${CMAKE_CURRENT_SOURCE_DIR}) + +# To include customized FindXXX.cmake modules +set(CMAKE_MODULE_PATH "${VGRAPH_HOME}/cmake" ${CMAKE_MODULE_PATH}) + +find_package(Boost) +find_package(OpenSSL) +find_package(Krb5 REQUIRED gssapi) +find_package(PCHSupport) + +add_compile_options(-Winvalid-pch) + +include_directories(SYSTEM ${VGRAPH_HOME}/third-party/_build/include) +include_directories(SYSTEM ${Boost_INCLUDE_DIRS}) +include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR}) +include_directories(SYSTEM ${KRB5_INCLUDE_DIRS}) +include_directories(AFTER common) +include_directories(AFTER interface) +include_directories(AFTER ${VGRAPH_HOME}) + +link_directories(third-party/_build/lib ${Boost_LIBRARY_DIRS} ${KRB5_LIBRARY_DIRS}) + +# All thrift libraries +set(THRIFT_LIBRARIES + thriftcpp2 + thrift + thriftprotocol + server + async + protocol + transport + concurrency + security + thriftfrozen2 + thrift-core +) + +# All compression libraries +set(COMPRESSION_LIBRARIES bz2 snappy zstd z) + +add_subdirectory(third-party) +add_subdirectory(interface) +add_subdirectory(common) +add_subdirectory(dataman) +add_subdirectory(client) +#add_subdirectory(storage) +add_subdirectory(server) +add_subdirectory(console) +add_subdirectory(consensus) + +add_dependencies(common third-party) +#add_dependencies(storage_engines common) + +add_custom_target( + clean-build + COMMAND ${CMAKE_MAKE_PROGRAM} clean + DEPENDS clean-interface clean-pch +) + +add_custom_target( + clean-all + COMMAND ${CMAKE_MAKE_PROGRAM} clean + DEPENDS clean-interface clean-pch clean-third-party +) + +add_custom_target( + distclean + COMMAND "find" "." "-name" "CMakeFiles" "|" "xargs" "rm" "-fr" + COMMAND "find" "." "-name" "CMakeCache.txt" "|" "xargs" "rm" "-f" + COMMAND "find" "." "-name" "cmake_install.cmake" "|" "xargs" "rm" "-f" + COMMAND "find" "." "-name" "Makefile" "|" "xargs" "rm" "-f" + DEPENDS clean-all +) + diff --git a/src/console/CMakeLists.txt b/src/console/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..93e24feb06a92c868af03e79f27db80487eb29ae --- /dev/null +++ b/src/console/CMakeLists.txt @@ -0,0 +1,46 @@ +add_library(console_obj OBJECT CliManager.cpp CmdProcessor.cpp) +add_dependencies(console_obj common) + +add_executable( + vgraph + GraphDbConsole.cpp + $<TARGET_OBJECTS:console_obj> + $<TARGET_OBJECTS:client_cpp_obj> + $<TARGET_OBJECTS:dataman_obj> + $<TARGET_OBJECTS:base_obj> + $<TARGET_OBJECTS:vgraph_thrift_obj> + $<TARGET_OBJECTS:time_obj> +) +target_link_libraries( + vgraph + thriftcpp2 + thrift + thrift-core + thriftprotocol + async + protocol + transport + concurrency + security + thriftfrozen2 + wangle + folly + boost_system + boost_context + ${OPENSSL_LIBRARIES} + ${KRB5_LIBRARIES} + event + bz2 + snappy + zstd + z + glog + gflags + resolv + double-conversion + dl + -pthread +) + +#add_subdirectory(test) + diff --git a/src/console/CliManager.cpp b/src/console/CliManager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..56dfadd5aa34d9b44c70ebac3ac780bdc7b17c99 --- /dev/null +++ b/src/console/CliManager.cpp @@ -0,0 +1,102 @@ +/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved + * + * This source code is licensed under Apache 2.0 License + * (found in the LICENSE.Apache file in the root directory) + */ + +#include "console/CliManager.h" +#include "client/cpp/GraphDbClient.h" + +DEFINE_string(prompt, "vGraph >", + "Default prompt for the command line interface"); + + +namespace vesoft { +namespace vgraph { + +const int32_t kMaxAuthInfoRetries = 3; +const int32_t kMaxUsernameLen = 16; +const int32_t kMaxPasswordLen = 24; +const int32_t kMaxCommandLineLen = 1024; + + +bool CliManager::connect(const std::string& addr, + uint16_t port, + const std::string& username, + const std::string& password) { + char user[kMaxUsernameLen + 1]; + char pass[kMaxPasswordLen + 1]; + + strncpy(user, username.c_str(), kMaxUsernameLen); + user[kMaxUsernameLen] = '\0'; + strncpy(pass, password.c_str(), kMaxPasswordLen); + pass[kMaxPasswordLen] = '\0'; + + // Make sure username is not empty + for (int32_t i = 0; i < kMaxAuthInfoRetries && !strlen(user); i++) { + // Need to interactively get the username + std::cout << "Username: "; + std::cin.getline(user, kMaxUsernameLen); + user[kMaxUsernameLen] = '\0'; + } + if (!strlen(user)) { + std::cout << "Authentication failed: " + "Need a valid username to authenticate\n\n"; + return false; + } + + // Make sure password is not empty + for (int32_t i = 0; i < kMaxAuthInfoRetries && !strlen(pass); i++) { + // Need to interactively get the password + std::cout << "Password: "; + std::cin.getline(pass, kMaxPasswordLen); + pass[kMaxPasswordLen] = '\0'; + } + if (!strlen(pass)) { + std::cout << "Authentication failed: " + "Need a valid password\n\n"; + return false; + } + + addr_ = addr; + port_ = port; + username_ = user; + + auto client = std::make_unique<GraphDbClient>(addr_, port_); + int32_t err = client->connect(user, pass); + if (!err) { + std::cerr << "\nWelcome to vGraph (Version 0.1)\n\n"; + cmdProcessor_ = std::make_unique<CmdProcessor>(std::move(client)); + return true; + } else { + // There is an error + std::cout << "Authentication failed: " << client->getErrorStr() << "\n"; + return false; + } +} + + +void CliManager::batch(const std::string& filename) { +} + + +void CliManager::loop() { + char cmd[kMaxCommandLineLen + 1]; + + while (true) { + // Print out prompt + std::cout << FLAGS_prompt << " "; + std::cin.getline(cmd, kMaxCommandLineLen); + cmd[kMaxCommandLineLen] = '\0'; + + auto trimmedCmd = folly::trimWhitespace(cmd); + if (!trimmedCmd.empty() && !cmdProcessor_->process(trimmedCmd)) { + break; + } + } + + std::cout << "\nBye!\n\n"; +} + +} // namespace vgraph +} // namespace vesoft diff --git a/src/console/CliManager.h b/src/console/CliManager.h new file mode 100644 index 0000000000000000000000000000000000000000..e1ccb35d9e4fd25345658321ea0c8ffd8456d890 --- /dev/null +++ b/src/console/CliManager.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved + * + * This source code is licensed under Apache 2.0 License + * (found in the LICENSE.Apache file in the root directory) + */ + +#ifndef CONSOLE_CLIMANAGER_H_ +#define CONSOLE_CLIMANAGER_H_ + +#include "base/Base.h" +#include "console/CmdProcessor.h" + +namespace vesoft { +namespace vgraph { + +class CliManager final { +public: + CliManager() = default; + ~CliManager() = default; + + bool connect(const std::string& addr, + uint16_t port, + const std::string& username, + const std::string& password); + + void batch(const std::string& filename); + + void loop(); + +private: + std::string addr_; + uint16_t port_; + std::string username_; + + std::unique_ptr<CmdProcessor> cmdProcessor_; +}; + +} // namespace vgraph +} // namespace vesoft +#endif // CONSOLE_CLIMANAGER_H_ diff --git a/src/console/CmdProcessor.cpp b/src/console/CmdProcessor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3ca66b1467fb20e698d43739ceae2b86f5464a7b --- /dev/null +++ b/src/console/CmdProcessor.cpp @@ -0,0 +1,266 @@ +/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved + * + * This source code is licensed under Apache 2.0 License + * (found in the LICENSE.Apache file in the root directory) + */ + +#include "console/CmdProcessor.h" +#include "time/Duration.h" +#include "dataman/RowSetReader.h" +#include "dataman/RowReader.h" + +namespace vesoft { +namespace vgraph { + +#define GET_VALUE_WIDTH(VT, FN, FMT) \ + VT val; \ + if (fieldIt->get ## FN (val) == ResultType::SUCCEEDED) { \ + int32_t len = folly::stringPrintf(FMT, val).size(); \ + if (widths[fieldIdx] < len) { \ + widths[fieldIdx] = len; \ + } \ + } + +std::vector<int16_t> CmdProcessor::calColumnWidths( + const RowSetReader* dataReader) const { + std::vector<int16_t> widths; + + // Check column names first + auto* schema = dataReader->schema(); + for (int i = 0; i < schema->getNumFields(0); i++) { + widths.emplace_back(strlen(schema->getFieldName(i, 0))); + } + + // TODO Then check data width + auto rowIt = dataReader->begin(); + while (bool(rowIt)) { + int32_t fieldIdx = 0; + auto fieldIt = rowIt->begin(); + while (bool(fieldIt)) { + switch (schema->getFieldType(fieldIdx, 0)->get_type()) { + case cpp2::SupportedType::BOOL: { + // Enough to hold "false" + if (widths[fieldIdx] < 5) { + widths[fieldIdx] = 5; + } + break; + } + case cpp2::SupportedType::INT: { + GET_VALUE_WIDTH(int64_t, Int, "%d") + break; + } + case cpp2::SupportedType::FLOAT: { + GET_VALUE_WIDTH(float, Float, "%f") + break; + } + case cpp2::SupportedType::DOUBLE: { + GET_VALUE_WIDTH(double, Double, "%f") + break; + } + case cpp2::SupportedType::STRING: { + folly::StringPiece val; + if (fieldIt->getString(val) == ResultType::SUCCEEDED) { + if (widths[fieldIdx] < val.size()) { + widths[fieldIdx] = val.size(); + } + } + break; + } + case cpp2::SupportedType::VID: { + // Enough to hold "0x{16 letters}" + if (widths[fieldIdx] < 18) { + widths[fieldIdx] = 18; + } + break; + } + default: { + } + } + ++fieldIt; + ++fieldIdx; + } + ++rowIt; + } + + return std::move(widths); +} +#undef GET_VALUE_WIDTH + + +int32_t CmdProcessor::printResult(const RowSetReader* dataReader) const { + std::vector<int16_t> widths = calColumnWidths(dataReader); + int32_t sum = 0; + for (auto w : widths) { + sum += w; + } + + std::string headerLine(sum + 3 * widths.size() + 1, '='); + std::string rowLine(sum + 3 * widths.size() + 1, '-'); + std::cout << headerLine << "\n|"; + + std::vector<std::string> formats = printHeader(dataReader, widths); + std::cout << headerLine << "\n"; + + int32_t numRows = printData(dataReader, rowLine, formats); + return numRows; +} + + +std::vector<std::string> CmdProcessor::printHeader( + const RowSetReader* dataReader, + const std::vector<int16_t>& widths) const { + std::vector<std::string> formats; + + auto* schema = dataReader->schema(); + for (int i = 0; i < schema->getNumFields(0); i++) { + std::string fmt = folly::stringPrintf(" %%%ds |", widths[i]); + std::cout << folly::stringPrintf(fmt.c_str(), schema->getFieldName(i, 0)); + switch (schema->getFieldType(i, 0)->get_type()) { + case cpp2::SupportedType::BOOL: { + formats.emplace_back(folly::stringPrintf(" %%%ds |", widths[i])); + break; + } + case cpp2::SupportedType::INT: { + formats.emplace_back(folly::stringPrintf(" %%%dld |", widths[i])); + break; + } + case cpp2::SupportedType::FLOAT: { + formats.emplace_back(folly::stringPrintf(" %%%df |", widths[i])); + break; + } + case cpp2::SupportedType::DOUBLE: { + formats.emplace_back(folly::stringPrintf(" %%%df |", widths[i])); + break; + } + case cpp2::SupportedType::STRING: { + formats.emplace_back(folly::stringPrintf(" %%%ds |", widths[i])); + break; + } + case cpp2::SupportedType::VID: { + formats.emplace_back(folly::stringPrintf(" %%%sX |", widths[i])); + break; + } + default: { + formats.emplace_back(" *ERROR* |"); + } + } + } + std::cout << "\n"; + + return std::move(formats); +} + + +#define PRINT_FIELD_VALUE(VT, FN, VAL) \ + VT val; \ + if (fIt->get ## FN (val) == ResultType::SUCCEEDED) { \ + std::cout << folly::stringPrintf(formats[fIdx].c_str(), (VAL)); \ + } else { \ + std::cout << " *BAD* |"; \ + } + +int32_t CmdProcessor::printData(const RowSetReader* dataReader, + const std::string& rowLine, + const std::vector<std::string>& formats) const { + int32_t numRows = 0; + + auto* schema = dataReader->schema(); + auto rowIt = dataReader->begin(); + while (bool(rowIt)) { + int32_t fIdx = 0; + auto fIt = rowIt->begin(); + std::cout << "|"; + while (bool(fIt)) { + switch (schema->getFieldType(fIdx, 0)->get_type()) { + case cpp2::SupportedType::BOOL: { + PRINT_FIELD_VALUE(bool, Bool, (val ? "true" : "false")) + break; + } + case cpp2::SupportedType::INT: { + PRINT_FIELD_VALUE(int64_t, Int, val) + break; + } + case cpp2::SupportedType::FLOAT: { + PRINT_FIELD_VALUE(float, Float, val) + break; + } + case cpp2::SupportedType::DOUBLE: { + PRINT_FIELD_VALUE(double, Double, val) + break; + } + case cpp2::SupportedType::STRING: { + PRINT_FIELD_VALUE(folly::StringPiece, String, val.toString().c_str()) + break; + } + case cpp2::SupportedType::VID: { + PRINT_FIELD_VALUE(int64_t, Vid, val) + break; + } + default: { + std::cout << " *ERROR* |"; + } + } + ++fIt; + ++fIdx; + } + std::cout << "\n" << rowLine << "\n"; + ++numRows; + ++rowIt; + } + + return numRows; +} +#undef PRINT_FIELD_VALUE + + +bool CmdProcessor::processClientCmd(folly::StringPiece cmd, + bool& readyToExit) { + if (cmd == "exit") { + readyToExit = true; + return true; + } else { + readyToExit = false; + } + + // TODO(sye) Check for all client commands + + return false; +} + + +void CmdProcessor::processServerCmd(folly::StringPiece cmd) { + time::Duration dur; + std::unique_ptr<RowSetReader> dataReader; + int32_t res = client_->execute(cmd, dataReader); + if (!res) { + // Succeeded + auto* schema = dataReader->schema(); + int32_t numRows = 0; + if (dataReader->schema()) { + // Only print when the schema is no empty + numRows = printResult(dataReader.get()); + } + std::cout << "Got " << numRows << " rows (Time spent: " + << client_->getServerLatency() << "/" + << dur.elapsedInMSec() << " ms)\n"; + } else { + std::cout << "[ERROR (" << res << ")] " + << client_->getErrorStr() << "\n"; + } +} + + +bool CmdProcessor::process(folly::StringPiece cmd) { + bool exit; + if (processClientCmd(cmd, exit)) { + return !exit; + } + + processServerCmd(cmd); + + return true; +} + +} // namespace vgraph +} // namespace vesoft + diff --git a/src/console/CmdProcessor.h b/src/console/CmdProcessor.h new file mode 100644 index 0000000000000000000000000000000000000000..88b5a1be8ed1af1fb461d30512d523837fb5752a --- /dev/null +++ b/src/console/CmdProcessor.h @@ -0,0 +1,50 @@ +/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved + * + * This source code is licensed under Apache 2.0 License + * (found in the LICENSE.Apache file in the root directory) + */ + +#ifndef CONSOLE_CMDPROCESSOR_H_ +#define CONSOLE_CMDPROCESSOR_H_ + +#include "base/Base.h" +#include "client/cpp/GraphDbClient.h" + +namespace vesoft { +namespace vgraph { + +class CmdProcessor final { +public: + explicit CmdProcessor(std::unique_ptr<GraphDbClient> client) + : client_(std::move(client)) {} + ~CmdProcessor() = default; + + // Process the given command + // + // The method returns false when it's ready to exit, otherwise + // the method returns true + bool process(folly::StringPiece cmd); + +private: + std::unique_ptr<GraphDbClient> client_; + + // The method returns true if the given command is a client command + // and has been processed. Otherwise, the method returns false + bool processClientCmd(folly::StringPiece cmd, bool& readyToExit); + + void processServerCmd(folly::StringPiece cmd); + + std::vector<int16_t> calColumnWidths(const RowSetReader* dataReader) const; + + int32_t printResult(const RowSetReader* dataReader) const; + std::vector<std::string> printHeader(const RowSetReader* dataReader, + const std::vector<int16_t>& widths) const; + // The method returns the total number of rows + int32_t printData(const RowSetReader* dataReader, + const std::string& rowLine, + const std::vector<std::string>& formats) const; +}; + +} // namespace vgraph +} // namespace vesoft +#endif // CONSOLE_CMDPROCESSOR_H_ diff --git a/src/console/GraphDbConsole.cpp b/src/console/GraphDbConsole.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2d5ab9ef9a56a07bab0872e5e6c84bb54779c2b2 --- /dev/null +++ b/src/console/GraphDbConsole.cpp @@ -0,0 +1,29 @@ +/* Copyright (c) 2018 - present, VE Software Inc. All rights reserved + * + * This source code is licensed under Apache 2.0 License + * (found in the LICENSE.Apache file in the root directory) + */ + +#include "base/Base.h" +#include "console/CliManager.h" + +DEFINE_string(addr, "127.0.0.1", "GraphDB daemon IP address"); +DEFINE_int32(port, 34500, "GraphDB daemon listening port"); +DEFINE_string(username, "", "Username used to authenticate"); +DEFINE_string(password, "", "Password used to authenticate"); + + +int main(int argc, char *argv[]) { + folly::init(&argc, &argv, true); + + using namespace vesoft::vgraph; + + CliManager cli; + if (!cli.connect(FLAGS_addr, FLAGS_port, FLAGS_username, FLAGS_password)) { + exit(1); + } + + cli.loop(); +} + +