diff --git a/.gitmodules b/.gitmodules index 8333e5544bc..f6f2c652004 100644 --- a/.gitmodules +++ b/.gitmodules @@ -134,6 +134,9 @@ [submodule "contrib/libc-headers"] path = contrib/libc-headers url = https://github.com/ClickHouse-Extras/libc-headers.git +[submodule "contrib/replxx"] + path = contrib/replxx + url = https://github.com/AmokHuginnsson/replxx.git [submodule "contrib/ryu"] path = contrib/ryu url = https://github.com/ClickHouse-Extras/ryu.git diff --git a/CMakeLists.txt b/CMakeLists.txt index cd32288ec9c..d37cdfc3af8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -328,7 +328,6 @@ include (cmake/find/xxhash.cmake) include (cmake/find/sparsehash.cmake) include (cmake/find/rt.cmake) include (cmake/find/execinfo.cmake) -include (cmake/find/readline_edit.cmake) include (cmake/find/re2.cmake) include (cmake/find/libgsasl.cmake) include (cmake/find/rdkafka.cmake) @@ -353,6 +352,7 @@ include (cmake/find/simdjson.cmake) include (cmake/find/rapidjson.cmake) include (cmake/find/fastops.cmake) include (cmake/find/orc.cmake) +include (cmake/find/replxx.cmake) find_contrib_lib(cityhash) find_contrib_lib(farmhash) diff --git a/cmake/find/base64.cmake b/cmake/find/base64.cmake index 022a78478ea..f72397597d7 100644 --- a/cmake/find/base64.cmake +++ b/cmake/find/base64.cmake @@ -11,7 +11,6 @@ if (ENABLE_BASE64) if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/base64") message (WARNING "submodule contrib/base64 is missing. to fix try run: \n git submodule update --init --recursive") else() - set (BASE64_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/base64/include) set (BASE64_LIBRARY base64) set (USE_BASE64 1) endif() diff --git a/cmake/find/readline_edit.cmake b/cmake/find/readline_edit.cmake deleted file mode 100644 index 96518a66887..00000000000 --- a/cmake/find/readline_edit.cmake +++ /dev/null @@ -1,60 +0,0 @@ -include (CMakePushCheckState) -cmake_push_check_state () - -option (ENABLE_READLINE "Enable readline" ${ENABLE_LIBRARIES}) -if (ENABLE_READLINE) - -set (READLINE_PATHS "/usr/local/opt/readline/lib") -# First try find custom lib for macos users (default lib without history support) -find_library (READLINE_LIB NAMES readline PATHS ${READLINE_PATHS} NO_DEFAULT_PATH) -if (NOT READLINE_LIB) - find_library (READLINE_LIB NAMES readline PATHS ${READLINE_PATHS}) -endif () - -list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .so.2) - -find_library (EDIT_LIB NAMES edit) - -set(READLINE_INCLUDE_PATHS "/usr/local/opt/readline/include") -if (READLINE_LIB AND TERMCAP_LIBRARY) - find_path (READLINE_INCLUDE_DIR NAMES readline/readline.h PATHS ${READLINE_INCLUDE_PATHS} NO_DEFAULT_PATH) - if (NOT READLINE_INCLUDE_DIR) - find_path (READLINE_INCLUDE_DIR NAMES readline/readline.h PATHS ${READLINE_INCLUDE_PATHS}) - endif () - if (READLINE_INCLUDE_DIR AND READLINE_LIB) - set (USE_READLINE 1) - set (LINE_EDITING_LIBS ${READLINE_LIB} ${TERMCAP_LIBRARY}) - message (STATUS "Using line editing libraries (readline): ${READLINE_INCLUDE_DIR} : ${LINE_EDITING_LIBS}") - endif () -elseif (EDIT_LIB AND TERMCAP_LIBRARY) - find_library (CURSES_LIB NAMES curses) - find_path (READLINE_INCLUDE_DIR NAMES editline/readline.h PATHS ${READLINE_INCLUDE_PATHS}) - if (CURSES_LIB AND READLINE_INCLUDE_DIR) - set (USE_LIBEDIT 1) - set (LINE_EDITING_LIBS ${EDIT_LIB} ${CURSES_LIB} ${TERMCAP_LIBRARY}) - message (STATUS "Using line editing libraries (edit): ${READLINE_INCLUDE_DIR} : ${LINE_EDITING_LIBS}") - endif () -endif () - -endif () - -if (LINE_EDITING_LIBS AND READLINE_INCLUDE_DIR) - include (CheckCXXSourceRuns) - - set (CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${LINE_EDITING_LIBS}) - set (CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${READLINE_INCLUDE_DIR}) - check_cxx_source_runs (" - #include - #include - #include - int main() { - add_history(NULL); - append_history(1,NULL); - return 0; - } - " HAVE_READLINE_HISTORY) -else () - message (STATUS "Not using any library for line editing.") -endif () - -cmake_pop_check_state () diff --git a/cmake/find/replxx.cmake b/cmake/find/replxx.cmake new file mode 100644 index 00000000000..3a0e5917b04 --- /dev/null +++ b/cmake/find/replxx.cmake @@ -0,0 +1,40 @@ +option (ENABLE_REPLXX "Enable replxx support" ${NOT_UNBUNDLED}) + +if (ENABLE_REPLXX) + option (USE_INTERNAL_REPLXX "Use internal replxx library" ${NOT_UNBUNDLED}) + + if (USE_INTERNAL_REPLXX AND NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/replxx/README.md") + message (WARNING "submodule contrib/replxx is missing. to fix try run: \n git submodule update --init --recursive") + set (USE_INTERNAL_REPLXX 0) + endif () + + if (NOT USE_INTERNAL_REPLXX) + find_library(LIBRARY_REPLXX NAMES replxx replxx-static) + find_path(INCLUDE_REPLXX replxx.hxx) + + add_library(replxx UNKNOWN IMPORTED) + set_property(TARGET replxx PROPERTY IMPORTED_LOCATION ${LIBRARY_REPLXX}) + target_include_directories(replxx PUBLIC ${INCLUDE_REPLXX}) + + set(CMAKE_REQUIRED_LIBRARIES replxx) + check_cxx_source_compiles( + " + #include + int main() { + replxx::Replxx rx; + } + " + EXTERNAL_REPLXX_WORKS + ) + + if (NOT EXTERNAL_REPLXX_WORKS) + message (FATAL_ERROR "replxx is unusable: ${LIBRARY_REPLXX} ${INCLUDE_REPLXX}") + endif () + endif () + + set(USE_REPLXX 1) + + message (STATUS "Using replxx") +else () + set(USE_REPLXX 0) +endif () diff --git a/cmake/sanitize.cmake b/cmake/sanitize.cmake index cb099ade7f5..8a3dd7f7634 100644 --- a/cmake/sanitize.cmake +++ b/cmake/sanitize.cmake @@ -48,7 +48,6 @@ if (SANITIZE) set (ENABLE_EMBEDDED_COMPILER 0 CACHE BOOL "") set (USE_INTERNAL_CAPNP_LIBRARY 0 CACHE BOOL "") set (USE_SIMDJSON 0 CACHE BOOL "") - set (ENABLE_READLINE 0 CACHE BOOL "") set (ENABLE_ORC 0 CACHE BOOL "") set (ENABLE_PARQUET 0 CACHE BOOL "") set (USE_CAPNP 0 CACHE BOOL "") diff --git a/cmake/target.cmake b/cmake/target.cmake index 9233af62dcf..1f40e28e76b 100644 --- a/cmake/target.cmake +++ b/cmake/target.cmake @@ -15,7 +15,6 @@ if (CMAKE_CROSSCOMPILING) set (USE_SNAPPY OFF CACHE INTERNAL "") set (ENABLE_PROTOBUF OFF CACHE INTERNAL "") set (ENABLE_PARQUET OFF CACHE INTERNAL "") - set (ENABLE_READLINE OFF CACHE INTERNAL "") set (ENABLE_ICU OFF CACHE INTERNAL "") set (ENABLE_FASTOPS OFF CACHE INTERNAL "") elseif (OS_LINUX) diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index 53ad9a0c138..f81d616cddd 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -331,3 +331,7 @@ endif() if (USE_FASTOPS) add_subdirectory (fastops-cmake) endif() + +if (USE_INTERNAL_REPLXX) + add_subdirectory (replxx-cmake) +endif() diff --git a/contrib/aws-s3-cmake/CMakeLists.txt b/contrib/aws-s3-cmake/CMakeLists.txt index e86ac0cb5a6..ef9107e0d4f 100644 --- a/contrib/aws-s3-cmake/CMakeLists.txt +++ b/contrib/aws-s3-cmake/CMakeLists.txt @@ -74,7 +74,6 @@ file(GLOB S3_UNIFIED_SRC ) set(S3_INCLUDES - "${CMAKE_CURRENT_SOURCE_DIR}/include/" "${AWS_COMMON_LIBRARY_DIR}/include/" "${AWS_EVENT_STREAM_LIBRARY_DIR}/include/" "${AWS_S3_LIBRARY_DIR}/include/" @@ -96,7 +95,7 @@ target_compile_definitions(aws_s3 PUBLIC -DENABLE_CURL_CLIENT) target_compile_definitions(aws_s3 PUBLIC "AWS_SDK_VERSION_MAJOR=1") target_compile_definitions(aws_s3 PUBLIC "AWS_SDK_VERSION_MINOR=7") target_compile_definitions(aws_s3 PUBLIC "AWS_SDK_VERSION_PATCH=231") -target_include_directories(aws_s3 PUBLIC ${S3_INCLUDES} "${CMAKE_BINARY_DIR}/install") +target_include_directories(aws_s3 PUBLIC ${S3_INCLUDES}) if (OPENSSL_FOUND) target_compile_definitions(aws_s3 PUBLIC -DENABLE_OPENSSL_ENCRYPTION) diff --git a/contrib/replxx b/contrib/replxx new file mode 160000 index 00000000000..37582f0bb8c --- /dev/null +++ b/contrib/replxx @@ -0,0 +1 @@ +Subproject commit 37582f0bb8c52513c6c6b76797c02d852d701dad diff --git a/contrib/replxx-cmake/CMakeLists.txt b/contrib/replxx-cmake/CMakeLists.txt new file mode 100644 index 00000000000..1b27fd53070 --- /dev/null +++ b/contrib/replxx-cmake/CMakeLists.txt @@ -0,0 +1,18 @@ +set (LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/replxx") + +set(SRCS + ${LIBRARY_DIR}/src/conversion.cxx + ${LIBRARY_DIR}/src/escape.cxx + ${LIBRARY_DIR}/src/history.cxx + ${LIBRARY_DIR}/src/io.cxx + ${LIBRARY_DIR}/src/prompt.cxx + ${LIBRARY_DIR}/src/replxx.cxx + ${LIBRARY_DIR}/src/replxx_impl.cxx + ${LIBRARY_DIR}/src/util.cxx + ${LIBRARY_DIR}/src/wcwidth.cpp + ${LIBRARY_DIR}/src/ConvertUTF.cpp +) + +add_library(replxx ${SRCS}) +target_include_directories(replxx PUBLIC ${LIBRARY_DIR}/include) +target_compile_options(replxx PUBLIC -Wno-documentation) diff --git a/dbms/CMakeLists.txt b/dbms/CMakeLists.txt index d7072371487..087dd3d18ab 100644 --- a/dbms/CMakeLists.txt +++ b/dbms/CMakeLists.txt @@ -142,10 +142,10 @@ elseif (COMPILER_GCC) add_cxx_compile_options(-Wmaybe-uninitialized) # Warn when the indentation of the code does not reflect the block structure add_cxx_compile_options(-Wmisleading-indentation) - # Warn if a global function is defined without a previous declaration + # Warn if a global function is defined without a previous declaration - disabled because of build times # add_cxx_compile_options(-Wmissing-declarations) # Warn if a user-supplied include directory does not exist - # add_cxx_compile_options(-Wmissing-include-dirs) + add_cxx_compile_options(-Wmissing-include-dirs) # Obvious add_cxx_compile_options(-Wnon-virtual-dtor) # Obvious @@ -563,7 +563,7 @@ if (USE_JEMALLOC) endif() endif () -dbms_target_include_directories (PUBLIC ${DBMS_INCLUDE_DIR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src/Formats/include) +dbms_target_include_directories (PUBLIC ${DBMS_INCLUDE_DIR}) target_include_directories (clickhouse_common_io PUBLIC ${DBMS_INCLUDE_DIR}) target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${DOUBLE_CONVERSION_INCLUDE_DIR}) diff --git a/dbms/programs/benchmark/Benchmark.cpp b/dbms/programs/benchmark/Benchmark.cpp index 1c98ecd1333..19c434e8952 100644 --- a/dbms/programs/benchmark/Benchmark.cpp +++ b/dbms/programs/benchmark/Benchmark.cpp @@ -418,7 +418,7 @@ private: std::cerr << percent << "%\t\t"; for (const auto & info : infos) { - std::cerr << info->sampler.quantileInterpolated(percent / 100.0) << " sec." << "\t"; + std::cerr << info->sampler.quantileNearest(percent / 100.0) << " sec." << "\t"; } std::cerr << "\n"; }; @@ -453,7 +453,7 @@ private: auto print_percentile = [&json_out](Stats & info, auto percent, bool with_comma = true) { - json_out << "\"" << percent << "\"" << ": " << info.sampler.quantileInterpolated(percent / 100.0) << (with_comma ? ",\n" : "\n"); + json_out << "\"" << percent << "\"" << ": " << info.sampler.quantileNearest(percent / 100.0) << (with_comma ? ",\n" : "\n"); }; json_out << "{\n"; diff --git a/dbms/programs/client/CMakeLists.txt b/dbms/programs/client/CMakeLists.txt index dc5cf787adf..d4c157ac3b0 100644 --- a/dbms/programs/client/CMakeLists.txt +++ b/dbms/programs/client/CMakeLists.txt @@ -1,14 +1,10 @@ set(CLICKHOUSE_CLIENT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ConnectionParameters.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Suggest.cpp ) set(CLICKHOUSE_CLIENT_LINK PRIVATE clickhouse_common_config clickhouse_functions clickhouse_aggregate_functions clickhouse_common_io clickhouse_parsers string_utils ${LINE_EDITING_LIBS} ${Boost_PROGRAM_OPTIONS_LIBRARY}) -set(CLICKHOUSE_CLIENT_INCLUDE PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) - -if (READLINE_INCLUDE_DIR) - set(CLICKHOUSE_CLIENT_INCLUDE ${CLICKHOUSE_CLIENT_INCLUDE} SYSTEM PRIVATE ${READLINE_INCLUDE_DIR}) -endif () include(CheckSymbolExists) check_symbol_exists(readpassphrase readpassphrase.h HAVE_READPASSPHRASE) diff --git a/dbms/programs/client/Client.cpp b/dbms/programs/client/Client.cpp index b37acab77ea..172899d59bf 100644 --- a/dbms/programs/client/Client.cpp +++ b/dbms/programs/client/Client.cpp @@ -1,7 +1,7 @@ #include "TestHint.h" #include "ConnectionParameters.h" +#include "Suggest.h" -#include #include #include #include @@ -18,8 +18,9 @@ #include #include #include -#include #include +#include +#include #include #include #include @@ -69,10 +70,6 @@ #include #include -#if USE_READLINE -#include "Suggest.h" -#endif - #ifndef __clang__ #pragma GCC optimize("-fno-var-tracking-assignments") #endif @@ -89,39 +86,6 @@ #define DISABLE_LINE_WRAPPING "\033[?7l" #define ENABLE_LINE_WRAPPING "\033[?7h" -#if USE_READLINE && RL_VERSION_MAJOR >= 7 - -#define BRACK_PASTE_PREF "\033[200~" -#define BRACK_PASTE_SUFF "\033[201~" - -#define BRACK_PASTE_LAST '~' -#define BRACK_PASTE_SLEN 6 - -/// This handler bypasses some unused macro/event checkings. -static int clickhouse_rl_bracketed_paste_begin(int /* count */, int /* key */) -{ - std::string buf; - buf.reserve(128); - - RL_SETSTATE(RL_STATE_MOREINPUT); - SCOPE_EXIT(RL_UNSETSTATE(RL_STATE_MOREINPUT)); - int c; - while ((c = rl_read_key()) >= 0) - { - if (c == '\r') - c = '\n'; - buf.push_back(c); - if (buf.size() >= BRACK_PASTE_SLEN && c == BRACK_PASTE_LAST && buf.substr(buf.size() - BRACK_PASTE_SLEN) == BRACK_PASTE_SUFF) - { - buf.resize(buf.size() - BRACK_PASTE_SLEN); - break; - } - } - return static_cast(rl_insert_text(buf.c_str())) == buf.size() ? 0 : 1; -} - -#endif - namespace DB { @@ -136,7 +100,6 @@ namespace ErrorCodes extern const int UNEXPECTED_PACKET_FROM_SERVER; extern const int CLIENT_OUTPUT_FORMAT_SPECIFIED; extern const int CANNOT_SET_SIGNAL_HANDLER; - extern const int CANNOT_READLINE; extern const int SYSTEM_ERROR; extern const int INVALID_USAGE_OF_INPUT; } @@ -157,7 +120,7 @@ private: "учшеж", "йгшеж", "дщпщгеж", "q", "й", "\\q", "\\Q", "\\й", "\\Й", ":q", "Жй" }; - bool is_interactive = true; /// Use either readline interface or batch mode. + bool is_interactive = true; /// Use either interactive line editing interface or batch mode. bool need_render_progress = true; /// Render query execution progress. bool echo_queries = false; /// Print queries before execution in batch mode. bool ignore_error = false; /// In case of errors, don't print error message, continue to next query. Only applicable for non-interactive mode. @@ -514,26 +477,10 @@ private: if (print_time_to_stderr) throw Exception("time option could be specified only in non-interactive mode", ErrorCodes::BAD_ARGUMENTS); -#if USE_READLINE - SCOPE_EXIT({ Suggest::instance().finalize(); }); - if (server_revision >= Suggest::MIN_SERVER_REVISION - && !config().getBool("disable_suggestion", false)) - { + if (server_revision >= Suggest::MIN_SERVER_REVISION && !config().getBool("disable_suggestion", false)) /// Load suggestion data from the server. Suggest::instance().load(connection_parameters, config().getInt("suggestion_limit")); - /// Added '.' to the default list. Because it is used to separate database and table. - rl_basic_word_break_characters = " \t\n\r\"\\'`@$><=;|&{(."; - - /// Not append whitespace after single suggestion. Because whitespace after function name is meaningless. - rl_completion_append_character = '\0'; - - rl_completion_entry_function = Suggest::generator; - } - else - /// Turn tab completion off. - rl_bind_key('\t', rl_insert); -#endif /// Load command history if present. if (config().has("history_file")) history_file = config().getString("history_file"); @@ -546,70 +493,45 @@ private: history_file = home_path + "/.clickhouse-client-history"; } - if (!history_file.empty()) + if (!history_file.empty() && !Poco::File(history_file).exists()) + Poco::File(history_file).createFile(); + + LineReader lr(&Suggest::instance(), history_file, '\\', config().has("multiline") ? ';' : 0); + + do { - if (Poco::File(history_file).exists()) + auto input = lr.readLine(prompt(), ":-] "); + if (input.empty()) + break; + + try { -#if USE_READLINE - int res = read_history(history_file.c_str()); - if (res) - std::cerr << "Cannot read history from file " + history_file + ": "+ errnoToString(ErrorCodes::CANNOT_READ_HISTORY); -#endif + if (!process(input)) + break; + } + catch (const Exception & e) + { + actual_client_error = e.code(); + if (!actual_client_error || actual_client_error != expected_client_error) + { + std::cerr << std::endl + << "Exception on client:" << std::endl + << "Code: " << e.code() << ". " << e.displayText() << std::endl; + + if (config().getBool("stacktrace", false)) + std::cerr << "Stack trace:" << std::endl << e.getStackTraceString() << std::endl; + + std::cerr << std::endl; + + } + + /// Client-side exception during query execution can result in the loss of + /// sync in the connection protocol. + /// So we reconnect and allow to enter the next query. + connect(); } - else /// Create history file. - Poco::File(history_file).createFile(); } - -#if USE_READLINE - /// Install Ctrl+C signal handler that will be used in interactive mode. - - if (rl_initialize()) - throw Exception("Cannot initialize readline", ErrorCodes::CANNOT_READLINE); - -#if RL_VERSION_MAJOR >= 7 - /// Enable bracketed-paste-mode only when multiquery is enabled and multiline is - /// disabled, so that we are able to paste and execute multiline queries in a whole - /// instead of erroring out, while be less intrusive. - if (config().has("multiquery") && !config().has("multiline")) - { - /// When bracketed paste mode is set, pasted text is bracketed with control sequences so - /// that the program can differentiate pasted text from typed-in text. This helps - /// clickhouse-client so that without -m flag, one can still paste multiline queries, and - /// possibly get better pasting performance. See https://cirw.in/blog/bracketed-paste for - /// more details. - rl_variable_bind("enable-bracketed-paste", "on"); - - /// Use our bracketed paste handler to get better user experience. See comments above. - rl_bind_keyseq(BRACK_PASTE_PREF, clickhouse_rl_bracketed_paste_begin); - } -#endif - - auto clear_prompt_or_exit = [](int) - { - /// This is signal safe. - ssize_t res = write(STDOUT_FILENO, "\n", 1); - - /// Allow to quit client while query is in progress by pressing Ctrl+C twice. - /// (First press to Ctrl+C will try to cancel query by InterruptListener). - if (res == 1 && rl_line_buffer[0] && !RL_ISSTATE(RL_STATE_DONE)) - { - rl_replace_line("", 0); - if (rl_forced_update_display()) - _exit(0); - } - else - { - /// A little dirty, but we struggle to find better way to correctly - /// force readline to exit after returning from the signal handler. - _exit(0); - } - }; - - if (signal(SIGINT, clear_prompt_or_exit) == SIG_ERR) - throwFromErrno("Cannot set signal handler.", ErrorCodes::CANNOT_SET_SIGNAL_HANDLER); -#endif - - loop(); + while (true); if (isNewYearMode()) std::cout << "Happy new year." << std::endl; @@ -621,17 +543,6 @@ private: } else { - /// This is intended for testing purposes. - if (config().getBool("always_load_suggestion_data", false)) - { -#if USE_READLINE - SCOPE_EXIT({ Suggest::instance().finalize(); }); - Suggest::instance().load(connection_parameters, config().getInt("suggestion_limit")); -#else - throw Exception("Command line suggestions cannot work without readline", ErrorCodes::BAD_ARGUMENTS); -#endif - } - query_id = config().getString("query_id", ""); nonInteractive(); @@ -706,111 +617,11 @@ private: } - /// Check if multi-line query is inserted from the paste buffer. - /// Allows delaying the start of query execution until the entirety of query is inserted. - static bool hasDataInSTDIN() - { - timeval timeout = { 0, 0 }; - fd_set fds; - FD_ZERO(&fds); - FD_SET(STDIN_FILENO, &fds); - return select(1, &fds, nullptr, nullptr, &timeout) == 1; - } - inline const String prompt() const { return boost::replace_all_copy(prompt_by_server_display_name, "{database}", config().getString("database", "default")); } - void loop() - { - String input; - String prev_input; - - while (char * line_ = readline(input.empty() ? prompt().c_str() : ":-] ")) - { - String line = line_; - free(line_); - - size_t ws = line.size(); - while (ws > 0 && isWhitespaceASCII(line[ws - 1])) - --ws; - - if (ws == 0 || line.empty()) - continue; - - bool ends_with_semicolon = line[ws - 1] == ';'; - bool ends_with_backslash = line[ws - 1] == '\\'; - - has_vertical_output_suffix = (ws >= 2) && (line[ws - 2] == '\\') && (line[ws - 1] == 'G'); - - if (ends_with_backslash) - line = line.substr(0, ws - 1); - - input += line; - - if (!ends_with_backslash && (ends_with_semicolon || has_vertical_output_suffix || (!config().has("multiline") && !hasDataInSTDIN()))) - { - // TODO: should we do sensitive data masking on client too? History file can be source of secret leaks. - if (input != prev_input) - { - /// Replace line breaks with spaces to prevent the following problem. - /// Every line of multi-line query is saved to history file as a separate line. - /// If the user restarts the client then after pressing the "up" button - /// every line of the query will be displayed separately. - std::string logged_query = input; - if (config().has("multiline")) - std::replace(logged_query.begin(), logged_query.end(), '\n', ' '); - add_history(logged_query.c_str()); - -#if USE_READLINE && HAVE_READLINE_HISTORY - if (!history_file.empty() && append_history(1, history_file.c_str())) - std::cerr << "Cannot append history to file " + history_file + ": " + errnoToString(ErrorCodes::CANNOT_APPEND_HISTORY); -#endif - - prev_input = input; - } - - if (has_vertical_output_suffix) - input = input.substr(0, input.length() - 2); - - try - { - if (!process(input)) - break; - } - catch (const Exception & e) - { - actual_client_error = e.code(); - if (!actual_client_error || actual_client_error != expected_client_error) - { - std::cerr << std::endl - << "Exception on client:" << std::endl - << "Code: " << e.code() << ". " << e.displayText() << std::endl; - - if (config().getBool("stacktrace", false)) - std::cerr << "Stack trace:" << std::endl - << e.getStackTraceString() << std::endl; - - std::cerr << std::endl; - - } - - /// Client-side exception during query execution can result in the loss of - /// sync in the connection protocol. - /// So we reconnect and allow to enter the next query. - connect(); - } - - input = ""; - } - else - { - input += '\n'; - } - } - } - void nonInteractive() { @@ -2001,13 +1812,6 @@ public: server_logs_file = options["server_logs_file"].as(); if (options.count("disable_suggestion")) config().setBool("disable_suggestion", true); - if (options.count("always_load_suggestion_data")) - { - if (options.count("disable_suggestion")) - throw Exception("Command line parameters disable_suggestion (-A) and always_load_suggestion_data cannot be specified simultaneously", - ErrorCodes::BAD_ARGUMENTS); - config().setBool("always_load_suggestion_data", true); - } if (options.count("suggestion_limit")) config().setInt("suggestion_limit", options["suggestion_limit"].as()); diff --git a/dbms/programs/client/Suggest.cpp b/dbms/programs/client/Suggest.cpp new file mode 100644 index 00000000000..65487e07a93 --- /dev/null +++ b/dbms/programs/client/Suggest.cpp @@ -0,0 +1,144 @@ +#include "Suggest.h" + +#include +#include + +namespace DB +{ + +void Suggest::load(const ConnectionParameters & connection_parameters, size_t suggestion_limit) +{ + loading_thread = std::thread([connection_parameters, suggestion_limit, this] + { + try + { + Connection connection( + connection_parameters.host, + connection_parameters.port, + connection_parameters.default_database, + connection_parameters.user, + connection_parameters.password, + "client", + connection_parameters.compression, + connection_parameters.security); + + loadImpl(connection, connection_parameters.timeouts, suggestion_limit); + } + catch (...) + { + std::cerr << "Cannot load data for command line suggestions: " << getCurrentExceptionMessage(false, true) << "\n"; + } + + /// Note that keyword suggestions are available even if we cannot load data from server. + + std::sort(words.begin(), words.end()); + ready = true; + }); +} + +Suggest::Suggest() +{ + /// Keywords may be not up to date with ClickHouse parser. + words = {"CREATE", "DATABASE", "IF", "NOT", "EXISTS", "TEMPORARY", "TABLE", "ON", "CLUSTER", "DEFAULT", + "MATERIALIZED", "ALIAS", "ENGINE", "AS", "VIEW", "POPULATE", "SETTINGS", "ATTACH", "DETACH", "DROP", + "RENAME", "TO", "ALTER", "ADD", "MODIFY", "CLEAR", "COLUMN", "AFTER", "COPY", "PROJECT", + "PRIMARY", "KEY", "CHECK", "PARTITION", "PART", "FREEZE", "FETCH", "FROM", "SHOW", "INTO", + "OUTFILE", "FORMAT", "TABLES", "DATABASES", "LIKE", "PROCESSLIST", "CASE", "WHEN", "THEN", "ELSE", + "END", "DESCRIBE", "DESC", "USE", "SET", "OPTIMIZE", "FINAL", "DEDUPLICATE", "INSERT", "VALUES", + "SELECT", "DISTINCT", "SAMPLE", "ARRAY", "JOIN", "GLOBAL", "LOCAL", "ANY", "ALL", "INNER", + "LEFT", "RIGHT", "FULL", "OUTER", "CROSS", "USING", "PREWHERE", "WHERE", "GROUP", "BY", + "WITH", "TOTALS", "HAVING", "ORDER", "COLLATE", "LIMIT", "UNION", "AND", "OR", "ASC", + "IN", "KILL", "QUERY", "SYNC", "ASYNC", "TEST", "BETWEEN", "TRUNCATE"}; +} + +void Suggest::loadImpl(Connection & connection, const ConnectionTimeouts & timeouts, size_t suggestion_limit) +{ + std::stringstream query; + query << "SELECT DISTINCT arrayJoin(extractAll(name, '[\\\\w_]{2,}')) AS res FROM (" + "SELECT name FROM system.functions" + " UNION ALL " + "SELECT name FROM system.table_engines" + " UNION ALL " + "SELECT name FROM system.formats" + " UNION ALL " + "SELECT name FROM system.table_functions" + " UNION ALL " + "SELECT name FROM system.data_type_families" + " UNION ALL " + "SELECT name FROM system.settings" + " UNION ALL " + "SELECT cluster FROM system.clusters" + " UNION ALL " + "SELECT concat(func.name, comb.name) FROM system.functions AS func CROSS JOIN system.aggregate_function_combinators AS comb WHERE is_aggregate"; + + /// The user may disable loading of databases, tables, columns by setting suggestion_limit to zero. + if (suggestion_limit > 0) + { + String limit_str = toString(suggestion_limit); + query << + " UNION ALL " + "SELECT name FROM system.databases LIMIT " << limit_str + << " UNION ALL " + "SELECT DISTINCT name FROM system.tables LIMIT " << limit_str + << " UNION ALL " + "SELECT DISTINCT name FROM system.columns LIMIT " << limit_str; + } + + query << ") WHERE notEmpty(res)"; + + fetch(connection, timeouts, query.str()); +} + +void Suggest::fetch(Connection & connection, const ConnectionTimeouts & timeouts, const std::string & query) +{ + connection.sendQuery(timeouts, query); + + while (true) + { + Packet packet = connection.receivePacket(); + switch (packet.type) + { + case Protocol::Server::Data: + fillWordsFromBlock(packet.block); + continue; + + case Protocol::Server::Progress: + continue; + case Protocol::Server::ProfileInfo: + continue; + case Protocol::Server::Totals: + continue; + case Protocol::Server::Extremes: + continue; + case Protocol::Server::Log: + continue; + + case Protocol::Server::Exception: + packet.exception->rethrow(); + return; + + case Protocol::Server::EndOfStream: + return; + + default: + throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER); + } + } +} + +void Suggest::fillWordsFromBlock(const Block & block) +{ + if (!block) + return; + + if (block.columns() != 1) + throw Exception("Wrong number of columns received for query to read words for suggestion", ErrorCodes::LOGICAL_ERROR); + + const ColumnString & column = typeid_cast(*block.getByPosition(0).column); + + size_t rows = block.rows(); + for (size_t i = 0; i < rows; ++i) + words.emplace_back(column.getDataAt(i).toString()); +} + +} diff --git a/dbms/programs/client/Suggest.h b/dbms/programs/client/Suggest.h index 78cc8d94db0..bd4f239ddc7 100644 --- a/dbms/programs/client/Suggest.h +++ b/dbms/programs/client/Suggest.h @@ -2,18 +2,9 @@ #include "ConnectionParameters.h" -#include -#include -#include -#include -#include - -#include - -#include -#include #include #include +#include namespace DB @@ -24,141 +15,8 @@ namespace ErrorCodes extern const int UNKNOWN_PACKET_FROM_SERVER; } -class Suggest : private boost::noncopyable +class Suggest : public LineReader::Suggest, boost::noncopyable { -private: - /// The vector will be filled with completion words from the server and sorted. - using Words = std::vector; - - /// Keywords may be not up to date with ClickHouse parser. - Words words - { - "CREATE", "DATABASE", "IF", "NOT", "EXISTS", "TEMPORARY", "TABLE", "ON", "CLUSTER", "DEFAULT", "MATERIALIZED", "ALIAS", "ENGINE", - "AS", "VIEW", "POPULATE", "SETTINGS", "ATTACH", "DETACH", "DROP", "RENAME", "TO", "ALTER", "ADD", "MODIFY", "CLEAR", "COLUMN", "AFTER", - "COPY", "PROJECT", "PRIMARY", "KEY", "CHECK", "PARTITION", "PART", "FREEZE", "FETCH", "FROM", "SHOW", "INTO", "OUTFILE", "FORMAT", "TABLES", - "DATABASES", "LIKE", "PROCESSLIST", "CASE", "WHEN", "THEN", "ELSE", "END", "DESCRIBE", "DESC", "USE", "SET", "OPTIMIZE", "FINAL", "DEDUPLICATE", - "INSERT", "VALUES", "SELECT", "DISTINCT", "SAMPLE", "ARRAY", "JOIN", "GLOBAL", "LOCAL", "ANY", "ALL", "INNER", "LEFT", "RIGHT", "FULL", "OUTER", - "CROSS", "USING", "PREWHERE", "WHERE", "GROUP", "BY", "WITH", "TOTALS", "HAVING", "ORDER", "COLLATE", "LIMIT", "UNION", "AND", "OR", "ASC", "IN", - "KILL", "QUERY", "SYNC", "ASYNC", "TEST", "BETWEEN", "TRUNCATE" - }; - - /// Words are fetched asynchronously. - std::thread loading_thread; - std::atomic ready{false}; - - /// Points to current word to suggest. - Words::const_iterator pos; - /// Points after the last possible match. - Words::const_iterator end; - - /// Set iterators to the matched range of words if any. - void findRange(const char * prefix, size_t prefix_length) - { - std::string prefix_str(prefix); - std::tie(pos, end) = std::equal_range(words.begin(), words.end(), prefix_str, - [prefix_length](const std::string & s, const std::string & prefix_searched) { return strncmp(s.c_str(), prefix_searched.c_str(), prefix_length) < 0; }); - } - - /// Iterates through matched range. - char * nextMatch() - { - if (pos >= end) - return nullptr; - - /// readline will free memory by itself. - char * word = strdup(pos->c_str()); - ++pos; - return word; - } - - void loadImpl(Connection & connection, const ConnectionTimeouts & timeouts, size_t suggestion_limit) - { - std::stringstream query; - query << "SELECT DISTINCT arrayJoin(extractAll(name, '[\\\\w_]{2,}')) AS res FROM (" - "SELECT name FROM system.functions" - " UNION ALL " - "SELECT name FROM system.table_engines" - " UNION ALL " - "SELECT name FROM system.formats" - " UNION ALL " - "SELECT name FROM system.table_functions" - " UNION ALL " - "SELECT name FROM system.data_type_families" - " UNION ALL " - "SELECT name FROM system.settings" - " UNION ALL " - "SELECT concat(func.name, comb.name) FROM system.functions AS func CROSS JOIN system.aggregate_function_combinators AS comb WHERE is_aggregate"; - - /// The user may disable loading of databases, tables, columns by setting suggestion_limit to zero. - if (suggestion_limit > 0) - { - String limit_str = toString(suggestion_limit); - query << - " UNION ALL " - "SELECT name FROM system.databases LIMIT " << limit_str - << " UNION ALL " - "SELECT DISTINCT name FROM system.tables LIMIT " << limit_str - << " UNION ALL " - "SELECT DISTINCT name FROM system.columns LIMIT " << limit_str; - } - - query << ") WHERE notEmpty(res)"; - - fetch(connection, timeouts, query.str()); - } - - void fetch(Connection & connection, const ConnectionTimeouts & timeouts, const std::string & query) - { - connection.sendQuery(timeouts, query); - - while (true) - { - Packet packet = connection.receivePacket(); - switch (packet.type) - { - case Protocol::Server::Data: - fillWordsFromBlock(packet.block); - continue; - - case Protocol::Server::Progress: - continue; - case Protocol::Server::ProfileInfo: - continue; - case Protocol::Server::Totals: - continue; - case Protocol::Server::Extremes: - continue; - case Protocol::Server::Log: - continue; - - case Protocol::Server::Exception: - packet.exception->rethrow(); - return; - - case Protocol::Server::EndOfStream: - return; - - default: - throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER); - } - } - } - - void fillWordsFromBlock(const Block & block) - { - if (!block) - return; - - if (block.columns() != 1) - throw Exception("Wrong number of columns received for query to read words for suggestion", ErrorCodes::LOGICAL_ERROR); - - const ColumnString & column = typeid_cast(*block.getByPosition(0).column); - - size_t rows = block.rows(); - for (size_t i = 0; i < rows; ++i) - words.emplace_back(column.getDataAt(i).toString()); - } - public: static Suggest & instance() { @@ -166,64 +24,25 @@ public: return instance; } - /// More old server versions cannot execute the query above. + void load(const ConnectionParameters & connection_parameters, size_t suggestion_limit); + + /// Older server versions cannot execute the query above. static constexpr int MIN_SERVER_REVISION = 54406; - void load(const ConnectionParameters & connection_parameters, size_t suggestion_limit) - { - loading_thread = std::thread([connection_parameters, suggestion_limit, this] - { - try - { - Connection connection( - connection_parameters.host, - connection_parameters.port, - connection_parameters.default_database, - connection_parameters.user, - connection_parameters.password, - "client", - connection_parameters.compression, - connection_parameters.security); - - loadImpl(connection, connection_parameters.timeouts, suggestion_limit); - } - catch (...) - { - std::cerr << "Cannot load data for command line suggestions: " << getCurrentExceptionMessage(false, true) << "\n"; - } - - /// Note that keyword suggestions are available even if we cannot load data from server. - - std::sort(words.begin(), words.end()); - ready = true; - }); - } - - void finalize() +private: + Suggest(); + ~Suggest() { if (loading_thread.joinable()) loading_thread.join(); } - /// A function for readline. - static char * generator(const char * text, int state) - { - Suggest & suggest = Suggest::instance(); - if (!suggest.ready) - return nullptr; - if (state == 0) - suggest.findRange(text, strlen(text)); + void loadImpl(Connection & connection, const ConnectionTimeouts & timeouts, size_t suggestion_limit); + void fetch(Connection & connection, const ConnectionTimeouts & timeouts, const std::string & query); + void fillWordsFromBlock(const Block & block); - /// Do not append whitespace after word. For unknown reason, rl_completion_append_character = '\0' does not work. - rl_completion_suppress_append = 1; - - return suggest.nextMatch(); - } - - ~Suggest() - { - finalize(); - } + /// Words are fetched asynchronously. + std::thread loading_thread; }; } diff --git a/dbms/programs/client/readpassphrase/CMakeLists.txt b/dbms/programs/client/readpassphrase/CMakeLists.txt index 94ed9f54bdb..a10b54c377d 100644 --- a/dbms/programs/client/readpassphrase/CMakeLists.txt +++ b/dbms/programs/client/readpassphrase/CMakeLists.txt @@ -10,4 +10,4 @@ set_target_properties(readpassphrase PROPERTIES LINKER_LANGUAGE C ) # . to allow #include -target_include_directories(readpassphrase PUBLIC . ${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/../include) +target_include_directories(readpassphrase PUBLIC . ${CMAKE_CURRENT_BINARY_DIR}/include) diff --git a/dbms/programs/odbc-bridge/CMakeLists.txt b/dbms/programs/odbc-bridge/CMakeLists.txt index d03ff257562..350663eb9ef 100644 --- a/dbms/programs/odbc-bridge/CMakeLists.txt +++ b/dbms/programs/odbc-bridge/CMakeLists.txt @@ -15,20 +15,24 @@ set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE PUBLIC ${ClickHouse_SOURCE_DIR}/libs/libdaemo if (USE_POCO_SQLODBC) set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_SQLODBC_LIBRARY}) - set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_SQLODBC_INCLUDE_DIR}) + # Wouldnt work anyway because of the way list variable got expanded in `target_include_directories` + # set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_SQLODBC_INCLUDE_DIR}) endif () if (Poco_SQL_FOUND) set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_SQL_LIBRARY}) - set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_SQL_INCLUDE_DIR}) + # Wouldnt work anyway because of the way list variable got expanded in `target_include_directories` + # set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_SQL_INCLUDE_DIR}) endif () if (USE_POCO_DATAODBC) set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_DataODBC_LIBRARY}) - set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_DataODBC_INCLUDE_DIR}) + # Wouldnt work anyway because of the way list variable got expanded in `target_include_directories` + # set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_DataODBC_INCLUDE_DIR}) endif() if (Poco_Data_FOUND) set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_Data_LIBRARY}) - set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_Data_INCLUDE_DIR}) + # Wouldnt work anyway because of the way list variable got expanded in `target_include_directories` + # set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_Data_INCLUDE_DIR}) endif () clickhouse_program_add_library(odbc-bridge) diff --git a/dbms/programs/server/InterserverIOHTTPHandler.cpp b/dbms/programs/server/InterserverIOHTTPHandler.cpp index 5302302bb5b..407d3c41a9b 100644 --- a/dbms/programs/server/InterserverIOHTTPHandler.cpp +++ b/dbms/programs/server/InterserverIOHTTPHandler.cpp @@ -61,6 +61,10 @@ void InterserverIOHTTPHandler::processQuery(Poco::Net::HTTPServerRequest & reque ReadBufferFromIStream body(request.stream()); auto endpoint = server.context().getInterserverIOHandler().getEndpoint(endpoint_name); + /// Locked for read while query processing + std::shared_lock lock(endpoint->rwlock); + if (endpoint->blocker.isCancelled()) + throw Exception("Transferring part to replica was cancelled", ErrorCodes::ABORTED); if (compress) { diff --git a/dbms/programs/server/MySQLHandler.cpp b/dbms/programs/server/MySQLHandler.cpp index a147ccafba0..64a78702bf0 100644 --- a/dbms/programs/server/MySQLHandler.cpp +++ b/dbms/programs/server/MySQLHandler.cpp @@ -79,21 +79,19 @@ void MySQLHandler::run() if (!connection_context.mysql.max_packet_size) connection_context.mysql.max_packet_size = MAX_PACKET_LENGTH; -/* LOG_TRACE(log, "Capabilities: " << handshake_response.capability_flags - << "\nmax_packet_size: " + LOG_TRACE(log, "Capabilities: " << handshake_response.capability_flags + << ", max_packet_size: " << handshake_response.max_packet_size - << "\ncharacter_set: " - << handshake_response.character_set - << "\nuser: " + << ", character_set: " + << static_cast(handshake_response.character_set) + << ", user: " << handshake_response.username - << "\nauth_response length: " + << ", auth_response length: " << handshake_response.auth_response.length() - << "\nauth_response: " - << handshake_response.auth_response - << "\ndatabase: " + << ", database: " << handshake_response.database - << "\nauth_plugin_name: " - << handshake_response.auth_plugin_name);*/ + << ", auth_plugin_name: " + << handshake_response.auth_plugin_name); client_capability_flags = handshake_response.capability_flags; if (!(client_capability_flags & CLIENT_PROTOCOL_41)) diff --git a/dbms/programs/server/MySQLHandlerFactory.cpp b/dbms/programs/server/MySQLHandlerFactory.cpp index 987efbfa347..576ef4d5170 100644 --- a/dbms/programs/server/MySQLHandlerFactory.cpp +++ b/dbms/programs/server/MySQLHandlerFactory.cpp @@ -34,7 +34,7 @@ MySQLHandlerFactory::MySQLHandlerFactory(IServer & server_) } catch (...) { - LOG_INFO(log, "Failed to create SSL context. SSL will be disabled. Error: " << getCurrentExceptionMessage(false)); + LOG_TRACE(log, "Failed to create SSL context. SSL will be disabled. Error: " << getCurrentExceptionMessage(false)); ssl_enabled = false; } #endif @@ -47,7 +47,7 @@ MySQLHandlerFactory::MySQLHandlerFactory(IServer & server_) } catch (...) { - LOG_WARNING(log, "Failed to read RSA keys. Error: " << getCurrentExceptionMessage(false)); + LOG_TRACE(log, "Failed to read RSA key pair from server certificate. Error: " << getCurrentExceptionMessage(false)); generateRSAKeys(); } #endif @@ -104,7 +104,7 @@ void MySQLHandlerFactory::readRSAKeys() void MySQLHandlerFactory::generateRSAKeys() { - LOG_INFO(log, "Generating new RSA key."); + LOG_TRACE(log, "Generating new RSA key pair."); public_key.reset(RSA_new()); if (!public_key) throw Exception("Failed to allocate RSA key. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR); diff --git a/dbms/programs/server/Server.cpp b/dbms/programs/server/Server.cpp index 35168a4b606..3ff943d519e 100644 --- a/dbms/programs/server/Server.cpp +++ b/dbms/programs/server/Server.cpp @@ -436,8 +436,10 @@ int Server::main(const std::vector & /*args*/) main_config_zk_changed_event, [&](ConfigurationPtr config) { - setTextLog(global_context->getTextLog()); - buildLoggers(*config, logger()); + // FIXME logging-related things need synchronization -- see the 'Logger * log' saved + // in a lot of places. For now, disable updating log configuration without server restart. + //setTextLog(global_context->getTextLog()); + //buildLoggers(*config, logger()); global_context->setClustersConfig(config); global_context->setMacros(std::make_unique(*config, "macros")); @@ -862,6 +864,9 @@ int Server::main(const std::vector & /*args*/) for (auto & server : servers) server->start(); + setTextLog(global_context->getTextLog()); + buildLoggers(config(), logger()); + main_config_reloader->start(); users_config_reloader->start(); if (dns_cache_updater) diff --git a/dbms/src/Common/ErrorCodes.cpp b/dbms/src/Common/ErrorCodes.cpp index fd72f8c7ea7..adf40ed2951 100644 --- a/dbms/src/Common/ErrorCodes.cpp +++ b/dbms/src/Common/ErrorCodes.cpp @@ -387,7 +387,6 @@ namespace ErrorCodes extern const int PTHREAD_ERROR = 411; extern const int NETLINK_ERROR = 412; extern const int CANNOT_SET_SIGNAL_HANDLER = 413; - extern const int CANNOT_READLINE = 414; extern const int ALL_REPLICAS_LOST = 415; extern const int REPLICA_STATUS_CHANGED = 416; extern const int EXPECTED_ALL_OR_ANY = 417; diff --git a/dbms/src/Core/MySQLProtocol.h b/dbms/src/Core/MySQLProtocol.h index 9b2a2cba249..2afeff341ab 100644 --- a/dbms/src/Core/MySQLProtocol.h +++ b/dbms/src/Core/MySQLProtocol.h @@ -1030,6 +1030,7 @@ public: LOG_TRACE(log, "Authentication method match."); } + bool sent_public_key = false; if (auth_response == "\1") { LOG_TRACE(log, "Client requests public key."); @@ -1050,6 +1051,7 @@ public: AuthMoreData data(pem); packet_sender->sendPacket(data, true); + sent_public_key = true; AuthSwitchResponse response; packet_sender->receivePacket(response); @@ -1069,13 +1071,15 @@ public: */ if (!is_secure_connection && !auth_response->empty() && auth_response != String("\0", 1)) { - LOG_TRACE(log, "Received nonempty password"); + LOG_TRACE(log, "Received nonempty password."); auto ciphertext = reinterpret_cast(auth_response->data()); unsigned char plaintext[RSA_size(&private_key)]; int plaintext_size = RSA_private_decrypt(auth_response->size(), ciphertext, plaintext, &private_key, RSA_PKCS1_OAEP_PADDING); if (plaintext_size == -1) { + if (!sent_public_key) + LOG_WARNING(log, "Client could have encrypted password with different public key since it didn't request it from server."); throw Exception("Failed to decrypt auth data. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR); } diff --git a/dbms/src/Disks/DiskMemory.cpp b/dbms/src/Disks/DiskMemory.cpp index d491cf3bea0..54c140df5af 100644 --- a/dbms/src/Disks/DiskMemory.cpp +++ b/dbms/src/Disks/DiskMemory.cpp @@ -10,8 +10,6 @@ namespace DB { namespace ErrorCodes { - extern const int UNKNOWN_ELEMENT_IN_CONFIG; - extern const int EXCESSIVE_ELEMENT_IN_CONFIG; extern const int FILE_DOESNT_EXIST; extern const int FILE_ALREADY_EXISTS; extern const int DIRECTORY_DOESNT_EXIST; diff --git a/dbms/src/Disks/DiskMemory.h b/dbms/src/Disks/DiskMemory.h index 0fc7a5846d0..b537e29d646 100644 --- a/dbms/src/Disks/DiskMemory.h +++ b/dbms/src/Disks/DiskMemory.h @@ -1,19 +1,24 @@ #pragma once #include -#include -#include #include +#include #include namespace DB { -namespace ErrorCodes -{ - extern const int LOGICAL_ERROR; -} +class ReadBuffer; +class WriteBuffer; + + +/** Implementation of Disk intended only for testing purposes. + * All filesystem objects are stored in memory and lost on server restart. + * + * NOTE Work in progress. Currently the interface is not viable enough to support MergeTree or even StripeLog tables. + * Please delete this interface if it will not be finished after 2020-06-18. + */ class DiskMemory : public IDisk { public: diff --git a/dbms/src/Functions/CMakeLists.txt b/dbms/src/Functions/CMakeLists.txt index 9eed1061349..f2a27974cbb 100644 --- a/dbms/src/Functions/CMakeLists.txt +++ b/dbms/src/Functions/CMakeLists.txt @@ -32,7 +32,6 @@ if (OPENSSL_CRYPTO_LIBRARY) target_link_libraries(clickhouse_functions PUBLIC ${OPENSSL_CRYPTO_LIBRARY}) endif() -target_include_directories(clickhouse_functions PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include) target_include_directories(clickhouse_functions SYSTEM PRIVATE ${DIVIDE_INCLUDE_DIR} ${METROHASH_INCLUDE_DIR} ${SPARSEHASH_INCLUDE_DIR}) if (CONSISTENT_HASHING_INCLUDE_DIR) diff --git a/dbms/src/Functions/FunctionsLogical.cpp b/dbms/src/Functions/FunctionsLogical.cpp index 75c602df088..b5c64ce9602 100644 --- a/dbms/src/Functions/FunctionsLogical.cpp +++ b/dbms/src/Functions/FunctionsLogical.cpp @@ -282,29 +282,32 @@ template < typename Op, template typename OperationApplierImpl, size_t N = 10> struct OperationApplier { - template - static void apply(Columns & in, ResultColumn & result) + template + static void apply(Columns & in, ResultData & result_data, bool use_result_data_as_input = false) { - while (in.size() > 1) - { - doBatchedApply(in, result->getData()); - in.push_back(result.get()); - } + if (!use_result_data_as_input) + doBatchedApply(in, result_data); + while (in.size() > 0) + doBatchedApply(in, result_data); } - template + template static void NO_INLINE doBatchedApply(Columns & in, ResultData & result_data) { if (N > in.size()) { - OperationApplier::doBatchedApply(in, result_data); + OperationApplier + ::template doBatchedApply(in, result_data); return; } const OperationApplierImpl operationApplierImpl(in); size_t i = 0; for (auto & res : result_data) - res = operationApplierImpl.apply(i++); + if constexpr (CarryResult) + res = Op::apply(res, operationApplierImpl.apply(i++)); + else + res = operationApplierImpl.apply(i++); in.erase(in.end() - N, in.end()); } @@ -312,9 +315,9 @@ struct OperationApplier template < typename Op, template typename OperationApplierImpl> -struct OperationApplier +struct OperationApplier { - template + template static void NO_INLINE doBatchedApply(Columns &, Result &) { throw Exception( @@ -332,7 +335,7 @@ static void executeForTernaryLogicImpl(ColumnRawPtrs arguments, ColumnWithTypeAn const bool has_consts = extractConstColumnsTernary(arguments, const_3v_value); /// If the constant value uniquely determines the result, return it. - if (has_consts && (arguments.empty() || (Op::isSaturable() && Op::isSaturatedValue(const_3v_value)))) + if (has_consts && (arguments.empty() || Op::isSaturatedValue(const_3v_value))) { result_info.column = ColumnConst::create( convertFromTernaryData(UInt8Container({const_3v_value}), result_info.type->isNullable()), @@ -341,16 +344,10 @@ static void executeForTernaryLogicImpl(ColumnRawPtrs arguments, ColumnWithTypeAn return; } - const auto result_column = ColumnUInt8::create(input_rows_count); - MutableColumnPtr const_column_holder; - if (has_consts) - { - const_column_holder = - convertFromTernaryData(UInt8Container(input_rows_count, const_3v_value), const_3v_value == Ternary::Null); - arguments.push_back(const_column_holder.get()); - } + const auto result_column = has_consts ? + ColumnUInt8::create(input_rows_count, const_3v_value) : ColumnUInt8::create(input_rows_count); - OperationApplier::apply(arguments, result_column); + OperationApplier::apply(arguments, result_column->getData(), has_consts); result_info.column = convertFromTernaryData(result_column->getData(), result_info.type->isNullable()); } @@ -425,19 +422,8 @@ static void basicExecuteImpl(ColumnRawPtrs arguments, ColumnWithTypeAndName & re if (has_consts && Op::apply(const_val, 0) == 0 && Op::apply(const_val, 1) == 1) has_consts = false; - UInt8ColumnPtrs uint8_args; - - auto col_res = ColumnUInt8::create(); - UInt8Container & vec_res = col_res->getData(); - if (has_consts) - { - vec_res.assign(input_rows_count, const_val); - uint8_args.push_back(col_res.get()); - } - else - { - vec_res.resize(input_rows_count); - } + auto col_res = has_consts ? + ColumnUInt8::create(input_rows_count, const_val) : ColumnUInt8::create(input_rows_count); /// FastPath detection goes in here if (arguments.size() == (has_consts ? 1 : 2)) @@ -452,7 +438,8 @@ static void basicExecuteImpl(ColumnRawPtrs arguments, ColumnWithTypeAndName & re } /// Convert all columns to UInt8 - Columns converted_columns; + UInt8ColumnPtrs uint8_args; + Columns converted_columns_holder; for (const IColumn * column : arguments) { if (auto uint8_column = checkAndGetColumn(column)) @@ -462,15 +449,11 @@ static void basicExecuteImpl(ColumnRawPtrs arguments, ColumnWithTypeAndName & re auto converted_column = ColumnUInt8::create(input_rows_count); convertColumnToUInt8(column, converted_column->getData()); uint8_args.push_back(converted_column.get()); - converted_columns.emplace_back(std::move(converted_column)); + converted_columns_holder.emplace_back(std::move(converted_column)); } } - OperationApplier::apply(uint8_args, col_res); - - /// This is possible if there is exactly one non-constant among the arguments, and it is of type UInt8. - if (uint8_args[0] != col_res.get()) - vec_res.assign(uint8_args[0]->getData()); + OperationApplier::apply(uint8_args, col_res->getData(), has_consts); result_info.column = std::move(col_res); } diff --git a/dbms/src/Functions/FunctionsLogical.h b/dbms/src/Functions/FunctionsLogical.h index e671c7184d8..814c9319f52 100644 --- a/dbms/src/Functions/FunctionsLogical.h +++ b/dbms/src/Functions/FunctionsLogical.h @@ -83,12 +83,7 @@ struct XorImpl static inline constexpr bool isSaturable() { return false; } static inline constexpr bool isSaturatedValue(bool) { return false; } - /** Considering that CH uses UInt8 for representation of boolean values this function - * returns 255 as "true" but the current implementation of logical functions suggests that - * any nonzero value is "true" as well. Also the current code provides no guarantee - * for "true" to be represented with the value of 1. - */ - static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return (a != b) ? Ternary::True : Ternary::False; } + static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return !!a != !!b; } static inline constexpr bool specialImplementationForNulls() { return false; } #if USE_EMBEDDED_COMPILER diff --git a/dbms/src/Functions/URL/CMakeLists.txt b/dbms/src/Functions/URL/CMakeLists.txt index 92bdbd8fcab..fabfccae230 100644 --- a/dbms/src/Functions/URL/CMakeLists.txt +++ b/dbms/src/Functions/URL/CMakeLists.txt @@ -2,7 +2,6 @@ include(${ClickHouse_SOURCE_DIR}/cmake/dbms_glob_sources.cmake) add_headers_and_sources(clickhouse_functions_url .) add_library(clickhouse_functions_url ${clickhouse_functions_url_sources} ${clickhouse_functions_url_headers}) target_link_libraries(clickhouse_functions_url PRIVATE dbms) -target_include_directories(clickhouse_functions_url PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../include) # ${CMAKE_CURRENT_BINARY_DIR}/include if (CMAKE_BUILD_TYPE_UC STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE_UC STREQUAL "RELWITHDEBINFO" OR CMAKE_BUILD_TYPE_UC STREQUAL "MINSIZEREL") # Won't generate debug info for files with heavy template instantiation to achieve faster linking and lower size. diff --git a/dbms/src/Functions/array/arrayZip.cpp b/dbms/src/Functions/array/arrayZip.cpp index 20fca29bae8..b191a055468 100644 --- a/dbms/src/Functions/array/arrayZip.cpp +++ b/dbms/src/Functions/array/arrayZip.cpp @@ -14,6 +14,7 @@ namespace ErrorCodes { extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int ILLEGAL_COLUMN; } /// arrayZip(['a', 'b', 'c'], ['d', 'e', 'f']) = [('a', 'd'), ('b', 'e'), ('c', 'f')] @@ -44,9 +45,8 @@ public: const DataTypeArray * array_type = checkAndGetDataType(arguments[index].type.get()); if (!array_type) - throw Exception( - "Argument " + toString(index + 1) + " of function must be array. Found " + arguments[0].type->getName() + " instead.", - ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + throw Exception("Argument " + toString(index + 1) + " of function " + getName() + + " must be array. Found " + arguments[0].type->getName() + " instead.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); arguments_types.emplace_back(array_type->getNestedType()); } @@ -56,26 +56,37 @@ public: void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override { - auto first_argument = block.getByPosition(arguments[0]); - const auto & first_array_column = checkAndGetColumn(first_argument.column.get()); + size_t num_arguments = arguments.size(); - Columns res_tuple_columns(arguments.size()); - res_tuple_columns[0] = first_array_column->getDataPtr(); + ColumnPtr first_array_column; + Columns tuple_columns(num_arguments); - for (size_t index = 1; index < arguments.size(); ++index) + for (size_t i = 0; i < num_arguments; ++i) { - const auto & argument_type_and_column = block.getByPosition(arguments[index]); - const auto & argument_array_column = checkAndGetColumn(argument_type_and_column.column.get()); + /// Constant columns cannot be inside tuple. It's only possible to have constant tuple as a whole. + ColumnPtr holder = block.getByPosition(arguments[i]).column->convertToFullColumnIfConst(); - if (!first_array_column->hasEqualOffsets(*argument_array_column)) - throw Exception("The argument 1 and argument " + toString(index + 1) + " of function have different array sizes", - ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH); + const ColumnArray * column_array = checkAndGetColumn(holder.get()); - res_tuple_columns[index] = argument_array_column->getDataPtr(); + if (!column_array) + throw Exception("Argument " + toString(i + 1) + " of function " + getName() + " must be array." + " Found column " + holder->getName() + " instead.", ErrorCodes::ILLEGAL_COLUMN); + + if (i == 0) + { + first_array_column = holder; + } + else if (!column_array->hasEqualOffsets(static_cast(*first_array_column))) + { + throw Exception("The argument 1 and argument " + toString(i + 1) + " of function " + getName() + " have different array sizes", + ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH); + } + + tuple_columns[i] = column_array->getDataPtr(); } block.getByPosition(result).column = ColumnArray::create( - ColumnTuple::create(res_tuple_columns), first_array_column->getOffsetsPtr()); + ColumnTuple::create(tuple_columns), static_cast(*first_array_column).getOffsetsPtr()); } }; diff --git a/dbms/src/Functions/bitCount.cpp b/dbms/src/Functions/bitCount.cpp new file mode 100644 index 00000000000..433a5ed9bee --- /dev/null +++ b/dbms/src/Functions/bitCount.cpp @@ -0,0 +1,50 @@ +#include +#include +#include + + +namespace DB +{ + +template +struct BitCountImpl +{ + using ResultType = UInt8; + + static inline ResultType apply(A a) + { + /// We count bits in the value representation in memory. For example, we support floats. + /// We need to avoid sign-extension when converting signed numbers to larger type. So, uint8_t(-1) has 8 bits. + + if constexpr (std::is_same_v || std::is_same_v) + return __builtin_popcountll(a); + if constexpr (std::is_same_v || std::is_same_v || std::is_unsigned_v) + return __builtin_popcount(a); + else + return __builtin_popcountll(ext::bit_cast(a)); + } + +#if USE_EMBEDDED_COMPILER + static constexpr bool compilable = false; +#endif +}; + +struct NameBitCount { static constexpr auto name = "bitCount"; }; +using FunctionBitCount = FunctionUnaryArithmetic; + +/// The function has no ranges of monotonicity. +template <> struct FunctionUnaryArithmeticMonotonicity +{ + static bool has() { return false; } + static IFunction::Monotonicity get(const Field &, const Field &) + { + return {}; + } +}; + +void registerFunctionBitCount(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} diff --git a/dbms/src/Functions/ifNotFinite.cpp b/dbms/src/Functions/ifNotFinite.cpp new file mode 100644 index 00000000000..605eeddf515 --- /dev/null +++ b/dbms/src/Functions/ifNotFinite.cpp @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +/// ifNotFinite(x, y) is equivalent to isFinite(x) ? x : y. +class FunctionIfNotFinite : public IFunction +{ +public: + static constexpr auto name = "ifNotFinite"; + + FunctionIfNotFinite(const Context & context_) : context(context_) {} + + static FunctionPtr create(const Context & context) + { + return std::make_shared(context); + } + + std::string getName() const override + { + return name; + } + + size_t getNumberOfArguments() const override { return 2; } + bool useDefaultImplementationForNulls() const override { return false; } + bool useDefaultImplementationForConstants() const override { return true; } + ColumnNumbers getArgumentsThatDontImplyNullableReturnType(size_t /*number_of_arguments*/) const override { return {0}; } + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + auto is_finite_type = FunctionFactory::instance().get("isFinite", context)->build({arguments[0]})->getReturnType(); + auto if_type = FunctionFactory::instance().get("if", context)->build({{nullptr, is_finite_type, ""}, arguments[0], arguments[1]})->getReturnType(); + return if_type; + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + Block temp_block = block; + + auto is_finite = FunctionFactory::instance().get("isFinite", context)->build( + {temp_block.getByPosition(arguments[0])}); + + size_t is_finite_pos = temp_block.columns(); + temp_block.insert({nullptr, is_finite->getReturnType(), ""}); + + auto func_if = FunctionFactory::instance().get("if", context)->build( + {temp_block.getByPosition(is_finite_pos), temp_block.getByPosition(arguments[0]), temp_block.getByPosition(arguments[1])}); + + is_finite->execute(temp_block, {arguments[0]}, is_finite_pos, input_rows_count); + + func_if->execute(temp_block, {is_finite_pos, arguments[0], arguments[1]}, result, input_rows_count); + + block.getByPosition(result).column = std::move(temp_block.getByPosition(result).column); + } + +private: + const Context & context; +}; + + +void registerFunctionIfNotFinite(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + diff --git a/dbms/src/Functions/registerFunctionsArithmetic.cpp b/dbms/src/Functions/registerFunctionsArithmetic.cpp index eb68fc32fa1..88350b4fac7 100644 --- a/dbms/src/Functions/registerFunctionsArithmetic.cpp +++ b/dbms/src/Functions/registerFunctionsArithmetic.cpp @@ -20,6 +20,7 @@ void registerFunctionBitShiftLeft(FunctionFactory & factory); void registerFunctionBitShiftRight(FunctionFactory & factory); void registerFunctionBitRotateLeft(FunctionFactory & factory); void registerFunctionBitRotateRight(FunctionFactory & factory); +void registerFunctionBitCount(FunctionFactory & factory); void registerFunctionLeast(FunctionFactory & factory); void registerFunctionGreatest(FunctionFactory & factory); void registerFunctionBitTest(FunctionFactory & factory); @@ -58,6 +59,7 @@ void registerFunctionsArithmetic(FunctionFactory & factory) registerFunctionBitShiftRight(factory); registerFunctionBitRotateLeft(factory); registerFunctionBitRotateRight(factory); + registerFunctionBitCount(factory); registerFunctionLeast(factory); registerFunctionGreatest(factory); registerFunctionBitTest(factory); diff --git a/dbms/src/Functions/registerFunctionsMiscellaneous.cpp b/dbms/src/Functions/registerFunctionsMiscellaneous.cpp index ef93d4554d5..23d95f8d5b1 100644 --- a/dbms/src/Functions/registerFunctionsMiscellaneous.cpp +++ b/dbms/src/Functions/registerFunctionsMiscellaneous.cpp @@ -36,6 +36,7 @@ void registerFunctionHasColumnInTable(FunctionFactory &); void registerFunctionIsFinite(FunctionFactory &); void registerFunctionIsInfinite(FunctionFactory &); void registerFunctionIsNaN(FunctionFactory &); +void registerFunctionIfNotFinite(FunctionFactory &); void registerFunctionThrowIf(FunctionFactory &); void registerFunctionVersion(FunctionFactory &); void registerFunctionUptime(FunctionFactory &); @@ -93,6 +94,7 @@ void registerFunctionsMiscellaneous(FunctionFactory & factory) registerFunctionIsFinite(factory); registerFunctionIsInfinite(factory); registerFunctionIsNaN(factory); + registerFunctionIfNotFinite(factory); registerFunctionThrowIf(factory); registerFunctionVersion(factory); registerFunctionUptime(factory); diff --git a/dbms/src/IO/tests/ryu_test.cpp b/dbms/src/IO/tests/ryu_test.cpp index 0866f6afd3d..d8c385f2d0b 100644 --- a/dbms/src/IO/tests/ryu_test.cpp +++ b/dbms/src/IO/tests/ryu_test.cpp @@ -1,5 +1,6 @@ #include #include +#include #include diff --git a/dbms/src/Interpreters/InterserverIOHandler.h b/dbms/src/Interpreters/InterserverIOHandler.h index 7cef5df9866..4651c8cb978 100644 --- a/dbms/src/Interpreters/InterserverIOHandler.h +++ b/dbms/src/Interpreters/InterserverIOHandler.h @@ -11,6 +11,7 @@ #include #include #include +#include #include namespace Poco { namespace Net { class HTTPServerResponse; } } @@ -24,42 +25,6 @@ namespace ErrorCodes extern const int NO_SUCH_INTERSERVER_IO_ENDPOINT; } -/** Location of the service. - */ -struct InterserverIOEndpointLocation -{ -public: - InterserverIOEndpointLocation(const std::string & name_, const std::string & host_, UInt16 port_) - : name(name_), host(host_), port(port_) - { - } - - /// Creates a location based on its serialized representation. - InterserverIOEndpointLocation(const std::string & serialized_location) - { - ReadBufferFromString buf(serialized_location); - readBinary(name, buf); - readBinary(host, buf); - readBinary(port, buf); - assertEOF(buf); - } - - /// Serializes the location. - std::string toString() const - { - WriteBufferFromOwnString buf; - writeBinary(name, buf); - writeBinary(host, buf); - writeBinary(port, buf); - return buf.str(); - } - -public: - std::string name; - std::string host; - UInt16 port; -}; - /** Query processor from other servers. */ class InterserverIOEndpoint @@ -71,6 +36,7 @@ public: /// You need to stop the data transfer if blocker is activated. ActionBlocker blocker; + std::shared_mutex rwlock; }; using InterserverIOEndpointPtr = std::shared_ptr; @@ -90,11 +56,10 @@ public: throw Exception("Duplicate interserver IO endpoint: " + name, ErrorCodes::DUPLICATE_INTERSERVER_IO_ENDPOINT); } - void removeEndpoint(const String & name) + bool removeEndpointIfExists(const String & name) { std::lock_guard lock(mutex); - if (!endpoint_map.erase(name)) - throw Exception("No interserver IO endpoint named " + name, ErrorCodes::NO_SUCH_INTERSERVER_IO_ENDPOINT); + return endpoint_map.erase(name); } InterserverIOEndpointPtr getEndpoint(const String & name) @@ -115,41 +80,4 @@ private: std::mutex mutex; }; -/// In the constructor calls `addEndpoint`, in the destructor - `removeEndpoint`. -class InterserverIOEndpointHolder -{ -public: - InterserverIOEndpointHolder(const String & name_, InterserverIOEndpointPtr endpoint_, InterserverIOHandler & handler_) - : name(name_), endpoint(std::move(endpoint_)), handler(handler_) - { - handler.addEndpoint(name, endpoint); - } - - InterserverIOEndpointPtr getEndpoint() - { - return endpoint; - } - - ~InterserverIOEndpointHolder() - try - { - handler.removeEndpoint(name); - /// After destroying the object, `endpoint` can still live, since its ownership is acquired during the processing of the request, - /// see InterserverIOHTTPHandler.cpp - } - catch (...) - { - tryLogCurrentException("~InterserverIOEndpointHolder"); - } - - ActionBlocker & getBlocker() { return endpoint->blocker; } - -private: - String name; - InterserverIOEndpointPtr endpoint; - InterserverIOHandler & handler; -}; - -using InterserverIOEndpointHolderPtr = std::shared_ptr; - } diff --git a/dbms/src/Storages/IStorage_fwd.h b/dbms/src/Storages/IStorage_fwd.h index e80fa2a0eb6..4983a734d21 100644 --- a/dbms/src/Storages/IStorage_fwd.h +++ b/dbms/src/Storages/IStorage_fwd.h @@ -11,7 +11,6 @@ namespace DB class IStorage; using StoragePtr = std::shared_ptr; -using StorageWeakPtr = std::weak_ptr; using Tables = std::map; } diff --git a/dbms/src/Storages/MergeTree/DataPartsExchange.cpp b/dbms/src/Storages/MergeTree/DataPartsExchange.cpp index 12137b4f023..e459de6fa58 100644 --- a/dbms/src/Storages/MergeTree/DataPartsExchange.cpp +++ b/dbms/src/Storages/MergeTree/DataPartsExchange.cpp @@ -1,8 +1,6 @@ #include -#include #include #include -#include #include #include #include @@ -53,9 +51,6 @@ std::string Service::getId(const std::string & node_id) const void Service::processQuery(const Poco::Net::HTMLForm & params, ReadBuffer & /*body*/, WriteBuffer & out, Poco::Net::HTTPServerResponse & response) { - if (blocker.isCancelled()) - throw Exception("Transferring part to replica was cancelled", ErrorCodes::ABORTED); - String client_protocol_version = params.get("client_protocol_version", REPLICATION_PROTOCOL_VERSION_WITHOUT_PARTS_SIZE); @@ -88,15 +83,11 @@ void Service::processQuery(const Poco::Net::HTMLForm & params, ReadBuffer & /*bo ++data.current_table_sends; SCOPE_EXIT({--data.current_table_sends;}); - StoragePtr owned_storage = storage.lock(); - if (!owned_storage) - throw Exception("The table was already dropped", ErrorCodes::UNKNOWN_TABLE); - LOG_TRACE(log, "Sending part " << part_name); try { - auto storage_lock = owned_storage->lockStructureForShare(false, RWLockImpl::NO_QUERY); + auto storage_lock = data.lockStructureForShare(false, RWLockImpl::NO_QUERY); MergeTreeData::DataPartPtr part = findPart(part_name); diff --git a/dbms/src/Storages/MergeTree/DataPartsExchange.h b/dbms/src/Storages/MergeTree/DataPartsExchange.h index 00d46870866..c0e8c0d2331 100644 --- a/dbms/src/Storages/MergeTree/DataPartsExchange.h +++ b/dbms/src/Storages/MergeTree/DataPartsExchange.h @@ -20,8 +20,8 @@ namespace DataPartsExchange class Service final : public InterserverIOEndpoint { public: - Service(MergeTreeData & data_, StoragePtr & storage_) : data(data_), - storage(storage_), log(&Logger::get(data.getLogName() + " (Replicated PartsService)")) {} + Service(MergeTreeData & data_) + : data(data_), log(&Logger::get(data.getLogName() + " (Replicated PartsService)")) {} Service(const Service &) = delete; Service & operator=(const Service &) = delete; @@ -33,8 +33,9 @@ private: MergeTreeData::DataPartPtr findPart(const String & name); private: + /// StorageReplicatedMergeTree::shutdown() waits for all parts exchange handlers to finish, + /// so Service will never access dangling reference to storage MergeTreeData & data; - StorageWeakPtr storage; Logger * log; }; diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.cpp b/dbms/src/Storages/MergeTree/MergeTreeData.cpp index a8b67840f10..210d412d86f 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreeData.cpp @@ -633,6 +633,10 @@ void MergeTreeData::setTTLExpressions(const ColumnsDescription::ColumnTTLs & new if (new_ttl_table_ast) { + std::vector update_move_ttl_entries; + ASTPtr update_ttl_table_ast = nullptr; + TTLEntry update_ttl_table_entry; + bool seen_delete_ttl = false; for (auto ttl_element_ptr : new_ttl_table_ast->children) { @@ -647,8 +651,8 @@ void MergeTreeData::setTTLExpressions(const ColumnsDescription::ColumnTTLs & new auto new_ttl_table_entry = create_ttl_entry(ttl_element.children[0]); if (!only_check) { - ttl_table_ast = ttl_element.children[0]; - ttl_table_entry = new_ttl_table_entry; + update_ttl_table_ast = ttl_element.children[0]; + update_ttl_table_entry = new_ttl_table_entry; } seen_delete_ttl = true; @@ -671,11 +675,18 @@ void MergeTreeData::setTTLExpressions(const ColumnsDescription::ColumnTTLs & new } if (!only_check) - { - move_ttl_entries.emplace_back(std::move(new_ttl_entry)); - } + update_move_ttl_entries.emplace_back(std::move(new_ttl_entry)); } } + + if (!only_check) + { + ttl_table_entry = update_ttl_table_entry; + ttl_table_ast = update_ttl_table_ast; + + auto move_ttl_entries_lock = std::lock_guard(move_ttl_entries_mutex); + move_ttl_entries = update_move_ttl_entries; + } } } @@ -3293,7 +3304,7 @@ ReservationPtr MergeTreeData::tryReserveSpacePreferringTTLRules(UInt64 expected_ ReservationPtr reservation; auto ttl_entry = selectTTLEntryForTTLInfos(ttl_infos, time_of_move); - if (ttl_entry != nullptr) + if (ttl_entry) { SpacePtr destination_ptr = ttl_entry->getDestination(storage_policy); if (!destination_ptr) @@ -3352,27 +3363,28 @@ bool MergeTreeData::TTLEntry::isPartInDestination(const StoragePolicyPtr & polic return false; } -const MergeTreeData::TTLEntry * MergeTreeData::selectTTLEntryForTTLInfos( +std::optional MergeTreeData::selectTTLEntryForTTLInfos( const MergeTreeDataPart::TTLInfos & ttl_infos, time_t time_of_move) const { - const MergeTreeData::TTLEntry * result = nullptr; - /// Prefer TTL rule which went into action last. time_t max_max_ttl = 0; + std::vector::const_iterator best_entry_it; - for (const auto & ttl_entry : move_ttl_entries) + auto lock = std::lock_guard(move_ttl_entries_mutex); + for (auto ttl_entry_it = move_ttl_entries.begin(); ttl_entry_it != move_ttl_entries.end(); ++ttl_entry_it) { - auto ttl_info_it = ttl_infos.moves_ttl.find(ttl_entry.result_column); + auto ttl_info_it = ttl_infos.moves_ttl.find(ttl_entry_it->result_column); + /// Prefer TTL rule which went into action last. if (ttl_info_it != ttl_infos.moves_ttl.end() && ttl_info_it->second.max <= time_of_move && max_max_ttl <= ttl_info_it->second.max) { - result = &ttl_entry; + best_entry_it = ttl_entry_it; max_max_ttl = ttl_info_it->second.max; } } - return result; + return max_max_ttl ? *best_entry_it : std::optional(); } MergeTreeData::DataParts MergeTreeData::getDataParts(const DataPartStates & affordable_states) const @@ -3773,7 +3785,15 @@ bool MergeTreeData::selectPartsAndMove() bool MergeTreeData::areBackgroundMovesNeeded() const { - return storage_policy->getVolumes().size() > 1; + auto policy = storage_policy; + + if (policy->getVolumes().size() > 1) + return true; + + if (policy->getVolumes().size() == 1 && policy->getVolumes()[0]->disks.size() > 1 && move_ttl_entries.size() > 0) + return true; + + return false; } bool MergeTreeData::movePartsToSpace(const DataPartsVector & parts, SpacePtr space) diff --git a/dbms/src/Storages/MergeTree/MergeTreeData.h b/dbms/src/Storages/MergeTree/MergeTreeData.h index ca2730a8aef..3c051829a61 100644 --- a/dbms/src/Storages/MergeTree/MergeTreeData.h +++ b/dbms/src/Storages/MergeTree/MergeTreeData.h @@ -737,12 +737,17 @@ public: bool isPartInDestination(const StoragePolicyPtr & policy, const MergeTreeDataPart & part) const; }; - const TTLEntry * selectTTLEntryForTTLInfos(const MergeTreeDataPart::TTLInfos & ttl_infos, time_t time_of_move) const; + std::optional selectTTLEntryForTTLInfos(const MergeTreeDataPart::TTLInfos & ttl_infos, time_t time_of_move) const; using TTLEntriesByName = std::unordered_map; TTLEntriesByName column_ttl_entries_by_name; TTLEntry ttl_table_entry; + + /// This mutex is required for background move operations which do not obtain global locks. + mutable std::mutex move_ttl_entries_mutex; + + /// Vector rw operations have to be done under "move_ttl_entries_mutex". std::vector move_ttl_entries; String sampling_expr_column_name; diff --git a/dbms/src/Storages/MergeTree/MergeTreePartsMover.cpp b/dbms/src/Storages/MergeTree/MergeTreePartsMover.cpp index 551a0de1338..2085f7b4fec 100644 --- a/dbms/src/Storages/MergeTree/MergeTreePartsMover.cpp +++ b/dbms/src/Storages/MergeTree/MergeTreePartsMover.cpp @@ -127,14 +127,14 @@ bool MergeTreePartsMover::selectPartsForMove( if (!can_move(part, &reason)) continue; - const MergeTreeData::TTLEntry * ttl_entry_ptr = part->storage.selectTTLEntryForTTLInfos(part->ttl_infos, time_of_move); + auto ttl_entry = part->storage.selectTTLEntryForTTLInfos(part->ttl_infos, time_of_move); auto to_insert = need_to_move.find(part->disk); ReservationPtr reservation; - if (ttl_entry_ptr) + if (ttl_entry) { - auto destination = ttl_entry_ptr->getDestination(policy); - if (destination && !ttl_entry_ptr->isPartInDestination(policy, *part)) - reservation = part->storage.tryReserveSpace(part->bytes_on_disk, ttl_entry_ptr->getDestination(policy)); + auto destination = ttl_entry->getDestination(policy); + if (destination && !ttl_entry->isPartInDestination(policy, *part)) + reservation = part->storage.tryReserveSpace(part->bytes_on_disk, ttl_entry->getDestination(policy)); } if (reservation) /// Found reservation by TTL rule. @@ -221,7 +221,9 @@ void MergeTreePartsMover::swapClonedPart(const MergeTreeData::DataPartPtr & clon return; } - cloned_part->renameTo(active_part->name); + /// Don't remove new directory but throw an error because it may contain part which is currently in use. + cloned_part->renameTo(active_part->name, false); + /// TODO what happen if server goes down here? data->swapActivePart(cloned_part); diff --git a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeAlterThread.cpp b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeAlterThread.cpp index cae520781ec..d1a201df1be 100644 --- a/dbms/src/Storages/MergeTree/ReplicatedMergeTreeAlterThread.cpp +++ b/dbms/src/Storages/MergeTree/ReplicatedMergeTreeAlterThread.cpp @@ -99,8 +99,8 @@ void ReplicatedMergeTreeAlterThread::run() /// Temporarily cancel parts sending ActionLock data_parts_exchange_blocker; - if (storage.data_parts_exchange_endpoint_holder) - data_parts_exchange_blocker = storage.data_parts_exchange_endpoint_holder->getBlocker().cancel(); + if (storage.data_parts_exchange_endpoint) + data_parts_exchange_blocker = storage.data_parts_exchange_endpoint->blocker.cancel(); /// Temporarily cancel part fetches auto fetches_blocker = storage.fetcher.blocker.cancel(); diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.cpp b/dbms/src/Storages/StorageReplicatedMergeTree.cpp index cc73c7610b9..2fdd7daa684 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.cpp +++ b/dbms/src/Storages/StorageReplicatedMergeTree.cpp @@ -2931,10 +2931,8 @@ void StorageReplicatedMergeTree::startup() getStorageID().getFullTableName() + " (ReplicatedMergeTreeQueue)", getDataParts()); - StoragePtr ptr = shared_from_this(); - InterserverIOEndpointPtr data_parts_exchange_endpoint = std::make_shared(*this, ptr); - data_parts_exchange_endpoint_holder = std::make_shared( - data_parts_exchange_endpoint->getId(replica_path), data_parts_exchange_endpoint, global_context.getInterserverIOHandler()); + data_parts_exchange_endpoint = std::make_shared(*this); + global_context.getInterserverIOHandler().addEndpoint(data_parts_exchange_endpoint->getId(replica_path), data_parts_exchange_endpoint); queue_task_handle = global_context.getBackgroundPool().addTask([this] { return queueTask(); }); if (areBackgroundMovesNeeded()) @@ -2966,11 +2964,15 @@ void StorageReplicatedMergeTree::shutdown() global_context.getBackgroundMovePool().removeTask(move_parts_task_handle); move_parts_task_handle.reset(); - if (data_parts_exchange_endpoint_holder) + if (data_parts_exchange_endpoint) { - data_parts_exchange_endpoint_holder->getBlocker().cancelForever(); - data_parts_exchange_endpoint_holder = nullptr; + global_context.getInterserverIOHandler().removeEndpointIfExists(data_parts_exchange_endpoint->getId(replica_path)); + /// Ask all parts exchange handlers to finish asap. New ones will fail to start + data_parts_exchange_endpoint->blocker.cancelForever(); + /// Wait for all of them + std::unique_lock lock(data_parts_exchange_endpoint->rwlock); } + data_parts_exchange_endpoint.reset(); } @@ -5423,7 +5425,7 @@ ActionLock StorageReplicatedMergeTree::getActionLock(StorageActionBlockType acti return fetcher.blocker.cancel(); if (action_type == ActionLocks::PartsSend) - return data_parts_exchange_endpoint_holder ? data_parts_exchange_endpoint_holder->getBlocker().cancel() : ActionLock(); + return data_parts_exchange_endpoint ? data_parts_exchange_endpoint->blocker.cancel() : ActionLock(); if (action_type == ActionLocks::ReplicationQueue) return queue.actions_blocker.cancel(); diff --git a/dbms/src/Storages/StorageReplicatedMergeTree.h b/dbms/src/Storages/StorageReplicatedMergeTree.h index 0330ba084e4..0fff99b00f3 100644 --- a/dbms/src/Storages/StorageReplicatedMergeTree.h +++ b/dbms/src/Storages/StorageReplicatedMergeTree.h @@ -231,7 +231,7 @@ private: std::atomic is_leader {false}; zkutil::LeaderElectionPtr leader_election; - InterserverIOEndpointHolderPtr data_parts_exchange_endpoint_holder; + InterserverIOEndpointPtr data_parts_exchange_endpoint; MergeTreeDataSelectExecutor reader; MergeTreeDataWriter writer; diff --git a/dbms/src/Storages/System/StorageSystemBuildOptions.generated.cpp.in b/dbms/src/Storages/System/StorageSystemBuildOptions.generated.cpp.in index 25e7086c1a6..550ead28996 100644 --- a/dbms/src/Storages/System/StorageSystemBuildOptions.generated.cpp.in +++ b/dbms/src/Storages/System/StorageSystemBuildOptions.generated.cpp.in @@ -36,7 +36,6 @@ const char * auto_config_build[] "USE_INTERNAL_MEMCPY", "@USE_INTERNAL_MEMCPY@", "USE_GLIBC_COMPATIBILITY", "@GLIBC_COMPATIBILITY@", "USE_JEMALLOC", "@USE_JEMALLOC@", - "USE_TCMALLOC", "@USE_TCMALLOC@", "USE_MIMALLOC", "@USE_MIMALLOC@", "USE_UNWIND", "@USE_UNWIND@", "USE_ICU", "@USE_ICU@", @@ -62,6 +61,7 @@ const char * auto_config_build[] "USE_HYPERSCAN", "@USE_HYPERSCAN@", "USE_SIMDJSON", "@USE_SIMDJSON@", "USE_POCO_REDIS", "@USE_POCO_REDIS@", + "USE_REPLXX", "@USE_REPLXX@", nullptr, nullptr }; diff --git a/dbms/tests/instructions/sanitizers.md b/dbms/tests/instructions/sanitizers.md index c0347a32cad..45e1304e2a1 100644 --- a/dbms/tests/instructions/sanitizers.md +++ b/dbms/tests/instructions/sanitizers.md @@ -67,5 +67,5 @@ sudo -u clickhouse UBSAN_OPTIONS='print_stacktrace=1' ./clickhouse-ubsan server # How to use Memory Sanitizer ``` -CC=clang-8 CXX=clang++-8 cmake -D ENABLE_HDFS=0 -D ENABLE_CAPNP=0 -D ENABLE_RDKAFKA=0 -D ENABLE_ICU=0 -D ENABLE_POCO_MONGODB=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_POCO_ODBC=0 -D ENABLE_ODBC=0 -D ENABLE_MYSQL=0 -D ENABLE_EMBEDDED_COMPILER=0 -D USE_INTERNAL_CAPNP_LIBRARY=0 -D USE_SIMDJSON=0 -D ENABLE_READLINE=0 -D SANITIZE=memory .. +CC=clang-8 CXX=clang++-8 cmake -D ENABLE_HDFS=0 -D ENABLE_CAPNP=0 -D ENABLE_RDKAFKA=0 -D ENABLE_ICU=0 -D ENABLE_POCO_MONGODB=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_POCO_ODBC=0 -D ENABLE_ODBC=0 -D ENABLE_MYSQL=0 -D ENABLE_EMBEDDED_COMPILER=0 -D USE_INTERNAL_CAPNP_LIBRARY=0 -D USE_SIMDJSON=0 -D SANITIZE=memory .. ``` diff --git a/dbms/tests/integration/test_ttl_move/test.py b/dbms/tests/integration/test_ttl_move/test.py index 071257d24ca..7fabdd85230 100644 --- a/dbms/tests/integration/test_ttl_move/test.py +++ b/dbms/tests/integration/test_ttl_move/test.py @@ -74,37 +74,37 @@ def test_rule_with_invalid_destination(started_cluster, name, engine, alter): {expression} SETTINGS storage_policy='{policy}' """.format(expression=x, name=name, engine=engine, policy=policy) - + if alter: node1.query(get_command(None, "small_jbod_with_external")) - + with pytest.raises(QueryRuntimeException): node1.query(get_command("TTL d1 TO DISK 'unknown'", "small_jbod_with_external")) - + node1.query("DROP TABLE IF EXISTS {}".format(name)) - + if alter: node1.query(get_command(None, "small_jbod_with_external")) - + with pytest.raises(QueryRuntimeException): node1.query(get_command("TTL d1 TO VOLUME 'unknown'", "small_jbod_with_external")) - + node1.query("DROP TABLE IF EXISTS {}".format(name)) - + if alter: node1.query(get_command(None, "only_jbod2")) - + with pytest.raises(QueryRuntimeException): node1.query(get_command("TTL d1 TO DISK 'jbod1'", "only_jbod2")) - + node1.query("DROP TABLE IF EXISTS {}".format(name)) - + if alter: node1.query(get_command(None, "only_jbod2")) - + with pytest.raises(QueryRuntimeException): node1.query(get_command("TTL d1 TO VOLUME 'external'", "only_jbod2")) - + finally: node1.query("DROP TABLE IF EXISTS {}".format(name)) @@ -501,13 +501,17 @@ def test_moves_after_merges_work(started_cluster, name, engine, positive): node1.query("DROP TABLE IF EXISTS {}".format(name)) -@pytest.mark.parametrize("name,engine,positive", [ - ("mt_test_moves_after_merges_do_not_work","MergeTree()",0), - ("replicated_mt_test_moves_after_merges_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_merges_do_not_work', '1')",0), - ("mt_test_moves_after_merges_work","MergeTree()",1), - ("replicated_mt_test_moves_after_merges_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_merges_work', '1')",1), +@pytest.mark.parametrize("name,engine,positive,bar", [ + ("mt_test_moves_after_alter_do_not_work","MergeTree()",0,"DELETE"), + ("replicated_mt_test_moves_after_alter_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_alter_do_not_work', '1')",0,"DELETE"), + ("mt_test_moves_after_alter_work","MergeTree()",1,"DELETE"), + ("replicated_mt_test_moves_after_alter_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_alter_work', '1')",1,"DELETE"), + ("mt_test_moves_after_alter_do_not_work","MergeTree()",0,"TO DISK 'external'"), + ("replicated_mt_test_moves_after_alter_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_alter_do_not_work', '1')",0,"TO DISK 'external'"), + ("mt_test_moves_after_alter_work","MergeTree()",1,"TO DISK 'external'"), + ("replicated_mt_test_moves_after_alter_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_alter_work', '1')",1,"TO DISK 'external'"), ]) -def test_ttls_do_not_work_after_alter(started_cluster, name, engine, positive): +def test_ttls_do_not_work_after_alter(started_cluster, name, engine, positive, bar): try: node1.query(""" CREATE TABLE {name} ( @@ -523,8 +527,8 @@ def test_ttls_do_not_work_after_alter(started_cluster, name, engine, positive): node1.query(""" ALTER TABLE {name} MODIFY TTL - d1 + INTERVAL 15 MINUTE - """.format(name=name)) # That shall disable TTL. + d1 + INTERVAL 15 MINUTE {bar} + """.format(name=name, bar=bar)) # That shall disable TTL. data = [] # 10MB in total for i in range(10): @@ -578,14 +582,14 @@ limitations under the License.""" ) ENGINE = {engine} ORDER BY tuple() PARTITION BY p1 - TTL d1 + INTERVAL 30 SECOND TO DISK 'jbod2', + TTL d1 + INTERVAL 30 SECOND TO DISK 'jbod2', d1 + INTERVAL 60 SECOND TO VOLUME 'external' SETTINGS storage_policy='jbods_with_external', merge_with_ttl_timeout=0 """.format(name=name, engine=engine)) node1.query(""" ALTER TABLE {name} MODIFY - TTL d1 + INTERVAL 0 SECOND TO DISK 'jbod2', + TTL d1 + INTERVAL 0 SECOND TO DISK 'jbod2', d1 + INTERVAL 5 SECOND TO VOLUME 'external', d1 + INTERVAL 10 SECOND DELETE """.format(name=name)) @@ -620,3 +624,142 @@ limitations under the License.""" finally: node1.query("DROP TABLE IF EXISTS {name}".format(name=name)) + + +@pytest.mark.parametrize("name,engine", [ + ("concurrently_altering_ttl_mt","MergeTree()"), + ("concurrently_altering_ttl_replicated_mt","ReplicatedMergeTree('/clickhouse/concurrently_altering_ttl_replicated_mt', '1')",), +]) +def test_concurrent_alter_with_ttl_move(started_cluster, name, engine): + try: + node1.query(""" + CREATE TABLE {name} ( + EventDate Date, + number UInt64 + ) ENGINE = {engine} + ORDER BY tuple() + PARTITION BY toYYYYMM(EventDate) + SETTINGS storage_policy='jbods_with_external' + """.format(name=name, engine=engine)) + + values = list({ random.randint(1, 1000000) for _ in range(0, 1000) }) + + def insert(num): + for i in range(num): + day = random.randint(11, 30) + value = values.pop() + month = '0' + str(random.choice([3, 4])) + node1.query("INSERT INTO {} VALUES(toDate('2019-{m}-{d}'), {v})".format(name, m=month, d=day, v=value)) + + def alter_move(num): + def produce_alter_move(node, name): + move_type = random.choice(["PART", "PARTITION"]) + if move_type == "PART": + for _ in range(10): + try: + parts = node1.query("SELECT name from system.parts where table = '{}' and active = 1".format(name)).strip().split('\n') + break + except QueryRuntimeException: + pass + else: + raise Exception("Cannot select from system.parts") + + move_part = random.choice(["'" + part + "'" for part in parts]) + else: + move_part = random.choice([201903, 201904]) + + move_disk = random.choice(["DISK", "VOLUME"]) + if move_disk == "DISK": + move_volume = random.choice(["'external'", "'jbod1'", "'jbod2'"]) + else: + move_volume = random.choice(["'main'", "'external'"]) + try: + node1.query("ALTER TABLE {} MOVE {mt} {mp} TO {md} {mv}".format( + name, mt=move_type, mp=move_part, md=move_disk, mv=move_volume)) + except QueryRuntimeException as ex: + pass + + for i in range(num): + produce_alter_move(node1, name) + + def alter_update(num): + for i in range(num): + node1.query("ALTER TABLE {} UPDATE number = number + 1 WHERE 1".format(name)) + + def alter_modify_ttl(num): + for i in range(num): + ttls = [] + for j in range(random.randint(1, 10)): + what = random.choice(["TO VOLUME 'main'", "TO VOLUME 'external'", "TO DISK 'jbod1'", "TO DISK 'jbod2'", "TO DISK 'external'"]) + when = "now()+{}".format(random.randint(-1, 5)) + ttls.append("{} {}".format(when, what)) + node1.query("ALTER TABLE {} MODIFY TTL {}".format(name, ", ".join(ttls))) + + def optimize_table(num): + for i in range(num): + node1.query("OPTIMIZE TABLE {} FINAL".format(name)) + + p = Pool(15) + tasks = [] + for i in range(5): + tasks.append(p.apply_async(insert, (100,))) + tasks.append(p.apply_async(alter_move, (100,))) + tasks.append(p.apply_async(alter_update, (100,))) + tasks.append(p.apply_async(alter_modify_ttl, (100,))) + tasks.append(p.apply_async(optimize_table, (100,))) + + for task in tasks: + task.get(timeout=60) + + assert node1.query("SELECT 1") == "1\n" + assert node1.query("SELECT COUNT() FROM {}".format(name)) == "500\n" + finally: + node1.query("DROP TABLE IF EXISTS {name}".format(name=name)) + +@pytest.mark.parametrize("name,positive", [ + ("test_double_move_while_select_negative", 0), + ("test_double_move_while_select_positive", 1), +]) +def test_double_move_while_select(started_cluster, name, positive): + try: + node1.query(""" + CREATE TABLE {name} ( + n Int64, + s String + ) ENGINE = MergeTree + ORDER BY tuple() + PARTITION BY n + SETTINGS storage_policy='small_jbod_with_external' + """.format(name=name)) + + node1.query("INSERT INTO {name} VALUES (1, '{string}')".format(name=name, string=get_random_string(10 * 1024 * 1024))) + + parts = node1.query("SELECT name FROM system.parts WHERE table = '{name}' AND active = 1".format(name=name)).splitlines() + assert len(parts) == 1 + + node1.query("ALTER TABLE {name} MOVE PART '{part}' TO DISK 'external'".format(name=name, part=parts[0])) + + def long_select(): + if positive: + node1.query("SELECT sleep(3), sleep(2), sleep(1), n FROM {name}".format(name=name)) + + thread = threading.Thread(target=long_select) + thread.start() + + node1.query("ALTER TABLE {name} MOVE PART '{part}' TO DISK 'jbod1'".format(name=name, part=parts[0])) + + # Fill jbod1 to force ClickHouse to make move of partition 1 to external. + node1.query("INSERT INTO {name} VALUES (2, '{string}')".format(name=name, string=get_random_string(9 * 1024 * 1024))) + node1.query("INSERT INTO {name} VALUES (3, '{string}')".format(name=name, string=get_random_string(9 * 1024 * 1024))) + node1.query("INSERT INTO {name} VALUES (4, '{string}')".format(name=name, string=get_random_string(9 * 1024 * 1024))) + + # If SELECT locked old part on external, move shall fail. + assert node1.query("SELECT disk_name FROM system.parts WHERE table = '{name}' AND active = 1 AND name = '{part}'" + .format(name=name, part=parts[0])).splitlines() == ["jbod1" if positive else "external"] + + thread.join() + + assert node1.query("SELECT n FROM {name} ORDER BY n".format(name=name)).splitlines() == ["1", "2", "3", "4"] + + finally: + node1.query("DROP TABLE IF EXISTS {name}".format(name=name)) diff --git a/dbms/tests/performance/bitCount.xml b/dbms/tests/performance/bitCount.xml new file mode 100644 index 00000000000..8936f700b51 --- /dev/null +++ b/dbms/tests/performance/bitCount.xml @@ -0,0 +1,27 @@ + + once + + + + + 2000 + 10000 + + + + + + expr + + number + toUInt32(number) + toUInt16(number) + toUInt8(number) + toInt32(number) + toFloat64(number) + + + + + SELECT bitCount({expr}) FROM system.numbers + diff --git a/dbms/tests/queries/0_stateless/00552_logical_functions_simple.reference b/dbms/tests/queries/0_stateless/00552_logical_functions_simple.reference new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00552_logical_functions_simple.reference @@ -0,0 +1 @@ +OK diff --git a/dbms/tests/queries/0_stateless/00552_logical_functions_simple.sql b/dbms/tests/queries/0_stateless/00552_logical_functions_simple.sql new file mode 100644 index 00000000000..2043199b9c2 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00552_logical_functions_simple.sql @@ -0,0 +1,56 @@ + +-- Test simple logic over smaller batch of columns +SELECT + -- x1, x2, x3, x4, + xor(x1, x2, x3, x4) AS xor1, + xor(xor(x1, x2), xor(x3, x4)) AS xor2, + + or(x1, x2, x3, x4) AS or1, + or(x1 or x2, x3 or x4) AS or2, + + and(x1, x2, x3, x4) AS and1, + and(x1 and x2, x3 and x4) AS and2 +FROM ( + SELECT + toUInt8(number % 2) AS x1, + toUInt8(number / 2 % 2) AS x2, + toUInt8(number / 4 % 2) AS x3, + toUInt8(number / 8 % 2) AS x4 + FROM numbers(16) +) +WHERE + xor1 != xor2 OR (and1 != and2 OR or1 != or2) +; + +-- Test simple logic over multiple batches of columns (currently batch spans over 10 columns) +SELECT + -- x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, + xor(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS xor1, + xor(x1, xor(xor(xor(x2, x3), xor(x4, x5)), xor(xor(x6, x7), xor(x8, xor(x9, xor(x10, x11)))))) AS xor2, + + or(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS or1, + or(x1, or(or(or(x2, x3), or(x4, x5)), or(or(x6, x7), or(x8, or(x9, or(x10, x11)))))) AS or2, + + and(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS and1, + and(x1, and((x2 and x3) and (x4 and x5), (x6 and x7) and (x8 and (x9 and (x10 and x11))))) AS and2 +FROM ( + SELECT + toUInt8(number % 2) AS x1, + toUInt8(number / 2 % 2) AS x2, + toUInt8(number / 4 % 2) AS x3, + toUInt8(number / 8 % 2) AS x4, + toUInt8(number / 16 % 2) AS x5, + toUInt8(number / 32 % 2) AS x6, + toUInt8(number / 64 % 2) AS x7, + toUInt8(number / 128 % 2) AS x8, + toUInt8(number / 256 % 2) AS x9, + toUInt8(number / 512 % 2) AS x10, + toUInt8(number / 1024 % 2) AS x11 + FROM numbers(2048) +) +WHERE + xor1 != xor2 OR (and1 != and2 OR or1 != or2) +; + + +SELECT 'OK'; diff --git a/dbms/tests/queries/0_stateless/00552_logical_functions_ternary.reference b/dbms/tests/queries/0_stateless/00552_logical_functions_ternary.reference new file mode 100644 index 00000000000..d86bac9de59 --- /dev/null +++ b/dbms/tests/queries/0_stateless/00552_logical_functions_ternary.reference @@ -0,0 +1 @@ +OK diff --git a/dbms/tests/queries/0_stateless/00552_logical_functions_ternary.sql b/dbms/tests/queries/0_stateless/00552_logical_functions_ternary.sql new file mode 100644 index 00000000000..b22e5c61f0f --- /dev/null +++ b/dbms/tests/queries/0_stateless/00552_logical_functions_ternary.sql @@ -0,0 +1,59 @@ + +-- Tests codepath for ternary logic +SELECT + -- x1, x2, x3, x4, + xor(x1, x2, x3, x4) AS xor1, + xor(xor(x1, x2), xor(x3, x4)) AS xor2, + + or(x1, x2, x3, x4) AS or1, + or(x1 or x2, x3 or x4) AS or2, + + and(x1, x2, x3, x4) AS and1, + and(x1 and x2, x3 and x4) AS and2 +FROM ( + SELECT + nullIf(toUInt8(number % 3), 2) AS x1, + nullIf(toUInt8(number / 3 % 3), 2) AS x2, + nullIf(toUInt8(number / 9 % 3), 2) AS x3, + nullIf(toUInt8(number / 27 % 3), 2) AS x4 + FROM numbers(81) +) +WHERE + (xor1 != xor2 OR (xor1 is NULL) != (xor2 is NULL)) OR + (or1 != or2 OR (or1 is NULL) != (or2 is NULL) OR (and1 != and2 OR (and1 is NULL) != (and2 is NULL))) +; + + +-- Test ternary logic over multiple batches of columns (currently batch spans over 10 columns) +SELECT + -- x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, + xor(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS xor1, + xor(x1, xor(xor(xor(x2, x3), xor(x4, x5)), xor(xor(x6, x7), xor(x8, xor(x9, xor(x10, x11)))))) AS xor2, + + or(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS or1, + or(x1, or(or(or(x2, x3), or(x4, x5)), or(or(x6, x7), or(x8, or(x9, or(x10, x11)))))) AS or2, + + and(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS and1, + and(x1, and((x2 and x3) and (x4 and x5), (x6 and x7) and (x8 and (x9 and (x10 and x11))))) AS and2 +FROM ( + SELECT + nullIf(toUInt8(number % 3), 2) AS x1, + nullIf(toUInt8(number / 3 % 3), 2) AS x2, + nullIf(toUInt8(number / 9 % 3), 2) AS x3, + nullIf(toUInt8(number / 27 % 3), 2) AS x4, + nullIf(toUInt8(number / 81 % 3), 2) AS x5, + nullIf(toUInt8(number / 243 % 3), 2) AS x6, + nullIf(toUInt8(number / 729 % 3), 2) AS x7, + nullIf(toUInt8(number / 2187 % 3), 2) AS x8, + nullIf(toUInt8(number / 6561 % 3), 2) AS x9, + nullIf(toUInt8(number / 19683 % 3), 2) AS x10, + nullIf(toUInt8(number / 59049 % 3), 2) AS x11 + FROM numbers(177147) +) +WHERE + (xor1 != xor2 OR (xor1 is NULL) != (xor2 is NULL)) OR + (or1 != or2 OR (or1 is NULL) != (or2 is NULL) OR (and1 != and2 OR (and1 is NULL) != (and2 is NULL))) +; + + +SELECT 'OK'; diff --git a/dbms/tests/queries/0_stateless/00981_no_virtual_columns.reference b/dbms/tests/queries/0_stateless/00981_no_virtual_columns.reference index c1df99e5f94..a7ec77dc030 100644 --- a/dbms/tests/queries/0_stateless/00981_no_virtual_columns.reference +++ b/dbms/tests/queries/0_stateless/00981_no_virtual_columns.reference @@ -1,2 +1 @@ default merge_ab x UInt8 0 0 0 0 0 0 0 -default as_kafka x UInt8 0 0 0 0 0 0 0 diff --git a/dbms/tests/queries/0_stateless/00981_no_virtual_columns.sql b/dbms/tests/queries/0_stateless/00981_no_virtual_columns.sql index 43c08b71b97..476377b4ddf 100644 --- a/dbms/tests/queries/0_stateless/00981_no_virtual_columns.sql +++ b/dbms/tests/queries/0_stateless/00981_no_virtual_columns.sql @@ -1,26 +1,13 @@ DROP TABLE IF EXISTS merge_a; DROP TABLE IF EXISTS merge_b; DROP TABLE IF EXISTS merge_ab; -DROP TABLE IF EXISTS kafka; -DROP TABLE IF EXISTS as_kafka; CREATE TABLE merge_a (x UInt8) ENGINE = StripeLog; CREATE TABLE merge_b (x UInt8) ENGINE = StripeLog; CREATE TABLE merge_ab AS merge(currentDatabase(), '^merge_[ab]$'); -CREATE TABLE kafka (x UInt8) - ENGINE = Kafka - SETTINGS kafka_broker_list = 'kafka', - kafka_topic_list = 'topic', - kafka_group_name = 'group', - kafka_format = 'CSV'; -CREATE TABLE as_kafka AS kafka ENGINE = Memory; - SELECT * FROM system.columns WHERE database = currentDatabase() AND table = 'merge_ab'; -SELECT * FROM system.columns WHERE database = currentDatabase() AND table = 'as_kafka'; DROP TABLE merge_a; DROP TABLE merge_b; DROP TABLE merge_ab; -DROP TABLE kafka; -DROP TABLE as_kafka; diff --git a/dbms/tests/queries/0_stateless/00995_exception_while_insert.sh b/dbms/tests/queries/0_stateless/00995_exception_while_insert.sh index 5b95174d1f8..0173d1aedae 100755 --- a/dbms/tests/queries/0_stateless/00995_exception_while_insert.sh +++ b/dbms/tests/queries/0_stateless/00995_exception_while_insert.sh @@ -7,8 +7,8 @@ CLICKHOUSE_CLIENT=`echo ${CLICKHOUSE_CLIENT} | sed 's/'"--send_logs_level=${CLIC $CLICKHOUSE_CLIENT --query="DROP TABLE IF EXISTS check;" -$CLICKHOUSE_CLIENT --query="CREATE TABLE check (x UInt64, y UInt64 DEFAULT throwIf(x > 10000000)) ENGINE = Memory;" +$CLICKHOUSE_CLIENT --query="CREATE TABLE check (x UInt64, y UInt64 DEFAULT throwIf(x > 1500000)) ENGINE = Memory;" -seq 1 11000000 | $CLICKHOUSE_CLIENT --query="INSERT INTO check(x) FORMAT TSV" 2>&1 | grep -q "Value passed to 'throwIf' function is non zero." && echo 'OK' || echo 'FAIL' ||: +seq 1 2000000 | $CLICKHOUSE_CLIENT --query="INSERT INTO check(x) FORMAT TSV" 2>&1 | grep -q "Value passed to 'throwIf' function is non zero." && echo 'OK' || echo 'FAIL' ||: $CLICKHOUSE_CLIENT --query="DROP TABLE check;" diff --git a/dbms/tests/queries/0_stateless/01065_array_zip_mixed_const.reference b/dbms/tests/queries/0_stateless/01065_array_zip_mixed_const.reference new file mode 100644 index 00000000000..dc683e2bf4d --- /dev/null +++ b/dbms/tests/queries/0_stateless/01065_array_zip_mixed_const.reference @@ -0,0 +1,24 @@ +[(0,'hello'),(1,'world')] +[(0,'hello'),(1,'world')] +[(0,'hello'),(1,'world')] +[(0,'hello'),(1,'world')] +[(0,'0'),(0,'world')] +[(0,'1'),(1,'world')] +[(0,'2'),(2,'world')] +[(0,'3'),(3,'world')] +[(0,'4'),(4,'world')] +[(0,'5'),(5,'world')] +[(0,'6'),(6,'world')] +[(0,'7'),(7,'world')] +[(0,'8'),(8,'world')] +[(0,'9'),(9,'world')] +[(1,[]),(0,[]),(0,[])] +[(1,[]),(1,[]),(1,[])] +[(1,[]),(2,[]),(4,[])] +[(1,[]),(3,[]),(9,[])] +[(1,[]),(4,[]),(16,[])] +[(1,[]),(5,[]),(25,[])] +[(1,[]),(6,[]),(36,[])] +[(1,[]),(7,[]),(49,[])] +[(1,[]),(8,[]),(64,[])] +[(1,[]),(9,[]),(81,[])] diff --git a/dbms/tests/queries/0_stateless/01065_array_zip_mixed_const.sql b/dbms/tests/queries/0_stateless/01065_array_zip_mixed_const.sql new file mode 100644 index 00000000000..0cd369739f4 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01065_array_zip_mixed_const.sql @@ -0,0 +1,7 @@ +SELECT arrayZip([0, 1], ['hello', 'world']); +SELECT arrayZip(materialize([0, 1]), ['hello', 'world']); +SELECT arrayZip([0, 1], materialize(['hello', 'world'])); +SELECT arrayZip(materialize([0, 1]), materialize(['hello', 'world'])); + +SELECT arrayZip([0, number], [toString(number), 'world']) FROM numbers(10); +SELECT arrayZip([1, number, number * number], [[], [], []]) FROM numbers(10); diff --git a/dbms/tests/queries/0_stateless/01065_if_not_finite.reference b/dbms/tests/queries/0_stateless/01065_if_not_finite.reference new file mode 100644 index 00000000000..cccd6c68047 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01065_if_not_finite.reference @@ -0,0 +1,17 @@ +111 +1 +0.5 +0.33 +0.25 +0.2 +0.17 +0.14 +0.12 +0.11 +1 +-1 +2 +2 +\N +-42 +1 diff --git a/dbms/tests/queries/0_stateless/01065_if_not_finite.sql b/dbms/tests/queries/0_stateless/01065_if_not_finite.sql new file mode 100644 index 00000000000..495932692ea --- /dev/null +++ b/dbms/tests/queries/0_stateless/01065_if_not_finite.sql @@ -0,0 +1,11 @@ +SELECT ifNotFinite(round(1 / number, 2), 111) FROM numbers(10); + +SELECT ifNotFinite(1, 2); +SELECT ifNotFinite(-1.0, 2); +SELECT ifNotFinite(nan, 2); +SELECT ifNotFinite(-1 / 0, 2); +SELECT ifNotFinite(log(0), NULL); +SELECT ifNotFinite(sqrt(-1), -42); +SELECT ifNotFinite(1234567890123456789, -1234567890123456789); -- { serverError 386 } + +SELECT ifNotFinite(NULL, 1); diff --git a/dbms/tests/queries/0_stateless/01066_bit_count.reference b/dbms/tests/queries/0_stateless/01066_bit_count.reference new file mode 100644 index 00000000000..4a3b084b4a2 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01066_bit_count.reference @@ -0,0 +1,21 @@ +0 +1 +1 +2 +1 +2 +2 +3 +1 +2 +4 +0 +1 +8 +64 +32 +16 +8 +1 10 000000000000F03F +-1 11 000000000000F0BF +inf 11 000000000000F07F diff --git a/dbms/tests/queries/0_stateless/01066_bit_count.sql b/dbms/tests/queries/0_stateless/01066_bit_count.sql new file mode 100644 index 00000000000..d50b2657542 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01066_bit_count.sql @@ -0,0 +1,13 @@ +SELECT bitCount(number) FROM numbers(10); +SELECT avg(bitCount(number)) FROM numbers(256); + +SELECT bitCount(0); +SELECT bitCount(1); +SELECT bitCount(-1); + +SELECT bitCount(toInt64(-1)); +SELECT bitCount(toInt32(-1)); +SELECT bitCount(toInt16(-1)); +SELECT bitCount(toInt8(-1)); + +SELECT x, bitCount(x), hex(reinterpretAsString(x)) FROM VALUES ('x Float64', (1), (-1), (inf)); diff --git a/dbms/tests/queries/0_stateless/01067_join_null.reference b/dbms/tests/queries/0_stateless/01067_join_null.reference new file mode 100644 index 00000000000..e06b673c8c5 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01067_join_null.reference @@ -0,0 +1,9 @@ +1 +\N +\N +\N +\N +--- +--- +\N +\N diff --git a/dbms/tests/queries/0_stateless/01067_join_null.sql b/dbms/tests/queries/0_stateless/01067_join_null.sql new file mode 100644 index 00000000000..60672312368 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01067_join_null.sql @@ -0,0 +1,42 @@ +SELECT id +FROM +( + SELECT 1 AS id + UNION ALL + SELECT NULL + UNION ALL + SELECT NULL +) +ALL FULL OUTER JOIN +( + SELECT 1 AS id + UNION ALL + SELECT NULL + UNION ALL + SELECT NULL +) USING (id) +ORDER BY id; + +SELECT '---'; + +SELECT * +FROM +( + SELECT NULL AS x +) +INNER JOIN +( + SELECT NULL AS x +) USING (x); + +SELECT '---'; + +SELECT * +FROM +( + SELECT NULL AS x +) +FULL OUTER JOIN +( + SELECT NULL AS x +) USING (x); diff --git a/dbms/tests/queries/0_stateless/01068_parens.reference b/dbms/tests/queries/0_stateless/01068_parens.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01068_parens.reference @@ -0,0 +1 @@ +1 diff --git a/dbms/tests/queries/0_stateless/01068_parens.sql b/dbms/tests/queries/0_stateless/01068_parens.sql new file mode 100644 index 00000000000..7cb4f097b15 --- /dev/null +++ b/dbms/tests/queries/0_stateless/01068_parens.sql @@ -0,0 +1 @@ +((((((((((((((SELECT((((((((((((((((((((((((((((((((1)))))))))))))))))))))))))))))))))))))))))))))) diff --git a/docker/packager/packager b/docker/packager/packager index 5e8ffbf1cb9..62767cae8f0 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -176,7 +176,8 @@ def parse_env_variables(build_type, compiler, sanitizer, package_type, image_typ result.append("ALIEN_PKGS='" + ' '.join(['--' + pkg for pkg in alien_pkgs]) + "'") if unbundled: - cmake_flags.append('-DUNBUNDLED=1 -DENABLE_MYSQL=0 -DENABLE_POCO_ODBC=0 -DENABLE_ODBC=0') + # TODO: fix build with ENABLE_RDKAFKA + cmake_flags.append('-DUNBUNDLED=1 -DENABLE_MYSQL=0 -DENABLE_POCO_ODBC=0 -DENABLE_ODBC=0 -DENABLE_READLINE=0 -DENABLE_RDKAFKA=0') if split_binary: cmake_flags.append('-DUSE_STATIC_LIBRARIES=0 -DSPLIT_SHARED_LIBRARIES=1 -DCLICKHOUSE_SPLIT_BINARY=1') diff --git a/docs/en/development/build.md b/docs/en/development/build.md index 2df8e7eeb25..1686f013559 100644 --- a/docs/en/development/build.md +++ b/docs/en/development/build.md @@ -58,12 +58,6 @@ $ export CC=gcc-9 $ export CXX=g++-9 ``` -## Install Required Libraries from Packages - -```bash -$ sudo apt-get install libreadline-dev -``` - ## Checkout ClickHouse Sources ```bash diff --git a/docs/en/development/build_osx.md b/docs/en/development/build_osx.md index 23fe52ddb64..ae08d3abb3f 100644 --- a/docs/en/development/build_osx.md +++ b/docs/en/development/build_osx.md @@ -11,7 +11,7 @@ $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/inst ## Install Required Compilers, Tools, and Libraries ```bash -$ brew install cmake ninja libtool gettext readline +$ brew install cmake ninja libtool gettext ``` ## Checkout ClickHouse Sources diff --git a/docs/en/development/developer_instruction.md b/docs/en/development/developer_instruction.md index 40eb60fc5da..591fc7c486c 100644 --- a/docs/en/development/developer_instruction.md +++ b/docs/en/development/developer_instruction.md @@ -4,6 +4,10 @@ Building of ClickHouse is supported on Linux, FreeBSD and Mac OS X. If you use Windows, you need to create a virtual machine with Ubuntu. To start working with a virtual machine please install VirtualBox. You can download Ubuntu from the website: https://www.ubuntu.com/#download. Please create a virtual machine from the downloaded image (you should reserve at least 4GB of RAM for it). To run a command line terminal in Ubuntu, please locate a program containing the word "terminal" in its name (gnome-terminal, konsole etc.) or just press Ctrl+Alt+T. +# If you use 32-bit system + +ClickHouse cannot work or build on 32-bit system. You should acquire access to 64-bit system and you can continue reading. + # Creating a repository on GitHub @@ -96,15 +100,7 @@ Next, check the version of CMake: `cmake --version`. If it is below 3.3, you sho # Optional External Libraries -ClickHouse uses several external libraries for building. Most of them do not need to be installed separately as they are built together with ClickHouse from the sources located in the submodules. You can check the list in `contrib`. - -There is one library that is not built from sources but is supplied by the system: Readline, and thus is recommended to be installed. - -Ubuntu: `sudo apt install libreadline-dev` - -Mac OS X: `brew install readline` - -However, these libraries are optional and ClickHouse can well be built without them. ICU is used for support of `COLLATE` in `ORDER BY` (i.e. for sorting in turkish alphabet). Readline is used for more convenient command input in clickhouse-client. +ClickHouse uses several external libraries for building. All of them do not need to be installed separately as they are built together with ClickHouse from the sources located in the submodules. You can check the list in `contrib`. # C++ Compiler diff --git a/docs/en/faq/general.md b/docs/en/faq/general.md index 3e6daf6ed9a..8afe995f724 100644 --- a/docs/en/faq/general.md +++ b/docs/en/faq/general.md @@ -21,11 +21,11 @@ If you use Oracle through the ODBC driver as a source of external dictionaries, NLS_LANG=RUSSIAN_RUSSIA.UTF8 ``` -## How to export data from ClickHouse to the file? +## How Do I Export Data from ClickHouse to a File? {#how-to-export-to-file} ### Using INTO OUTFILE Clause -Add [INTO OUTFILE](../query_language/select/#into-outfile-clause) clause to your query. +Add an [INTO OUTFILE](../query_language/select/#into-outfile-clause) clause to your query. For example: @@ -41,11 +41,11 @@ For example: SELECT * FROM table INTO OUTFILE 'file' FORMAT CSV ``` -### Using File-engine Table +### Using a File-Engine Table See [File](../operations/table_engines/file.md). -### Using Command-line Redirection +### Using Command-Line Redirection ```sql $ clickhouse-client --query "SELECT * from table" > result.txt diff --git a/docs/en/interfaces/cli.md b/docs/en/interfaces/cli.md index 86c7a104670..0fcc5e8899f 100644 --- a/docs/en/interfaces/cli.md +++ b/docs/en/interfaces/cli.md @@ -53,7 +53,7 @@ Only a single query is run, so everything after the semicolon is ignored. You can specify `\G` instead of or after the semicolon. This indicates Vertical format. In this format, each value is printed on a separate line, which is convenient for wide tables. This unusual feature was added for compatibility with the MySQL CLI. -The command line is based on 'readline' (and 'history' or 'libedit', or without a library, depending on the build). In other words, it uses the familiar keyboard shortcuts and keeps a history. +The command line is based on 'replxx' (similar to 'readline'). In other words, it uses the familiar keyboard shortcuts and keeps a history. The history is written to `~/.clickhouse-client-history`. By default, the format used is PrettyCompact. You can change the format in the FORMAT clause of the query, or by specifying `\G` at the end of the query, using the `--format` or `--vertical` argument in the command line, or using the client configuration file. diff --git a/docs/en/interfaces/formats.md b/docs/en/interfaces/formats.md index b37c9cdddb2..9992467047a 100644 --- a/docs/en/interfaces/formats.md +++ b/docs/en/interfaces/formats.md @@ -968,7 +968,7 @@ To exchange data with Hadoop, you can use [HDFS table engine](../operations/tabl ## ORC {#data-format-orc} -[Apache ORC](https://orc.apache.org/) is a columnar storage format widespread in the Hadoop ecosystem. ClickHouse supports only read operations for this format. +[Apache ORC](https://orc.apache.org/) is a columnar storage format widespread in the Hadoop ecosystem. You can only insert data in this format to ClickHouse. ### Data Types Matching @@ -991,18 +991,18 @@ The table below shows supported data types and how they match ClickHouse [data t | `STRING`, `BINARY` | [String](../data_types/string.md) | | `DECIMAL` | [Decimal](../data_types/decimal.md) | -ClickHouse supports configurable precision of `Decimal` type. The `INSERT` query treats the ORC `DECIMAL` type as the ClickHouse `Decimal128` type. +ClickHouse supports configurable precision of the `Decimal` type. The `INSERT` query treats the ORC `DECIMAL` type as the ClickHouse `Decimal128` type. Unsupported ORC data types: `DATE32`, `TIME32`, `FIXED_SIZE_BINARY`, `JSON`, `UUID`, `ENUM`. -Data types of a ClickHouse table columns can differ from the corresponding fields of the ORC data inserted. When inserting data, ClickHouse interprets data types according to the table above and then [cast](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) the data to that data type which is set for the ClickHouse table column. +The data types of ClickHouse table columns don't have to match the corresponding ORC data fields. When inserting data, ClickHouse interprets data types according to the table above and then [casts](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) the data to the data type set for the ClickHouse table column. ### Inserting Data -You can insert Parquet data from a file into ClickHouse table by the following command: +You can insert ORC data from a file into ClickHouse table by the following command: ```bash -$ cat {filename} | clickhouse-client --query="INSERT INTO {some_table} FORMAT ORC" +$ cat filename.orc | clickhouse-client --query="INSERT INTO some_table FORMAT ORC" ``` To exchange data with Hadoop, you can use [HDFS table engine](../operations/table_engines/hdfs.md). diff --git a/docs/en/interfaces/http.md b/docs/en/interfaces/http.md index 4383318f98f..f8f4b159a4e 100644 --- a/docs/en/interfaces/http.md +++ b/docs/en/interfaces/http.md @@ -172,7 +172,7 @@ $ echo 'SELECT number FROM numbers LIMIT 10' | curl 'http://localhost:8123/?data By default, the database that is registered in the server settings is used as the default database. By default, this is the database called 'default'. Alternatively, you can always specify the database using a dot before the table name. -The username and password can be indicated in one of two ways: +The username and password can be indicated in one of three ways: 1. Using HTTP Basic Authentication. Example: @@ -186,6 +186,12 @@ $ echo 'SELECT 1' | curl 'http://user:password@localhost:8123/' -d @- $ echo 'SELECT 1' | curl 'http://localhost:8123/?user=user&password=password' -d @- ``` +3. Using ‘X-ClickHouse-User’ and ‘X-ClickHouse-Key’ headers. Example: + +```bash +$ echo 'SELECT 1' | curl -H 'X-ClickHouse-User: user' -H 'X-ClickHouse-Key: password' 'http://localhost:8123/' -d @- +``` + If the user name is not specified, the `default` name is used. If the password is not specified, the empty password is used. You can also use the URL parameters to specify any settings for processing a single query, or entire profiles of settings. Example:http://localhost:8123/?profile=web&max_rows_to_read=1000000000&query=SELECT+1 diff --git a/docs/en/interfaces/mysql.md b/docs/en/interfaces/mysql.md new file mode 100644 index 00000000000..1c56eaffb82 --- /dev/null +++ b/docs/en/interfaces/mysql.md @@ -0,0 +1,35 @@ +# MySQL interface {#mysql_interface} + +ClickHouse supports MySQL wire protocol. It can be enabled by [mysql_port](../operations/server_settings/settings.md#server_settings-mysql_port) setting in configuration file: +```xml +9004 +``` + +Example of connecting using command-line tool mysql: +```bash +$ mysql --protocol tcp -u default -P 9004 +``` + +Output if connection succeeded: +```text +Welcome to the MySQL monitor. Commands end with ; or \g. +Your MySQL connection id is 4 +Server version: 20.2.1.1-ClickHouse + +Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + +Oracle is a registered trademark of Oracle Corporation and/or its +affiliates. Other names may be trademarks of their respective +owners. + +Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. + +mysql> +``` + +For compatibility with all MySQL clients, it is recommended to specify user password with [double SHA1](../operations/settings/settings_users.md#password_double_sha1_hex) in configuration file. +If user password is specified using [SHA256](../operations/settings/settings_users.md#password_sha256_hex), some clients won't be able to authenticate (mysqljs and old versions of command-line tool mysql). + +Restrictions: +- prepared queries are not supported +- some data types are sent as strings diff --git a/docs/en/operations/performance_test.md b/docs/en/operations/performance_test.md index a56490ac8ba..f5f249a75e8 100644 --- a/docs/en/operations/performance_test.md +++ b/docs/en/operations/performance_test.md @@ -1,16 +1,14 @@ # How To Test Your Hardware With ClickHouse -Draft. - With this instruction you can run basic ClickHouse performance test on any server without installation of ClickHouse packages. -1. Go to "commits" page: https://github.com/ClickHouse/ClickHouse/commits/master +\1. Go to "commits" page: [https://github.com/ClickHouse/ClickHouse/commits/master](https://github.com/ClickHouse/ClickHouse/commits/master) -2. Click on the first green check mark or red cross with green "ClickHouse Build Check" and click on the "Details" link near "ClickHouse Build Check". +\2. Click on the first green check mark or red cross with green "ClickHouse Build Check" and click on the "Details" link near "ClickHouse Build Check". -3. Copy the link to "clickhouse" binary for amd64 or aarch64. +\3. Copy the link to "clickhouse" binary for amd64 or aarch64. -4. ssh to the server and download it with wget: +\4. ssh to the server and download it with wget: ``` # For amd64: wget https://clickhouse-builds.s3.yandex.net/0/00ba767f5d2a929394ea3be193b1f79074a1c4bc/1578163263_binary/clickhouse @@ -20,7 +18,7 @@ wget https://clickhouse-builds.s3.yandex.net/0/00ba767f5d2a929394ea3be193b1f7907 chmod a+x clickhouse ``` -5. Download configs: +\5. Download configs: ``` wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/programs/server/config.xml wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/programs/server/users.xml @@ -29,16 +27,19 @@ wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/program wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/programs/server/config.d/log_to_console.xml -O config.d/log_to_console.xml ``` -6. Download benchmark files: +\6. Download benchmark files: ``` wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/benchmark/clickhouse/benchmark-new.sh chmod a+x benchmark-new.sh wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/benchmark/clickhouse/queries.sql ``` -7. Download test data: +\7. Download test data: + According to the instruction: -https://clickhouse.yandex/docs/en/getting_started/example_datasets/metrica/ + +[https://clickhouse.yandex/docs/en/getting_started/example_datasets/metrica/](https://clickhouse.yandex/docs/en/getting_started/example_datasets/metrica/) + ("hits" table containing 100 million rows) ``` @@ -47,26 +48,29 @@ tar xvf hits_100m_obfuscated_v1.tar.xz -C . mv hits_100m_obfuscated_v1/* . ``` -8. Run the server: +\8. Run the server: ``` ./clickhouse server ``` -9. Check the data: +\9. Check the data: + ssh to the server in another terminal ``` ./clickhouse client --query "SELECT count() FROM hits_100m_obfuscated" 100000000 ``` -10. Edit the benchmark-new.sh, change "clickhouse-client" to "./clickhouse client" and add "--max_memory_usage 100000000000" parameter. +\10. Edit the benchmark-new.sh, change "clickhouse-client" to "./clickhouse client" and add "--max_memory_usage 100000000000" parameter. ``` mcedit benchmark-new.sh ``` -11. Run the benchmark: +\11. Run the benchmark: ``` ./benchmark-new.sh hits_100m_obfuscated ``` -12. Send the numbers and the info about your hardware configuration to clickhouse-feedback@yandex-team.com +\12. Send the numbers and the info about your hardware configuration to clickhouse-feedback@yandex-team.com + +All the results are published here: [https://clickhouse.yandex/benchmark_hardware.html](https://clickhouse.yandex/benchmark_hardware.html) diff --git a/docs/en/operations/server_settings/settings.md b/docs/en/operations/server_settings/settings.md index 89bb7ef33ae..c13d53eabc7 100644 --- a/docs/en/operations/server_settings/settings.md +++ b/docs/en/operations/server_settings/settings.md @@ -709,6 +709,20 @@ Positive integer. 9440 ``` +## mysql_port {#server_settings-mysql_port} + +Port for communicating with clients over MySQL protocol. + +**Possible values** + +Positive integer. + +Example + +```xml +9004 +``` + ## tmp_path Path to temporary data for processing large queries. diff --git a/docs/en/operations/settings/settings_users.md b/docs/en/operations/settings/settings_users.md index 8937c59b667..5289bb19645 100644 --- a/docs/en/operations/settings/settings_users.md +++ b/docs/en/operations/settings/settings_users.md @@ -33,12 +33,14 @@ Structure of the `users` section: ### user_name/password -Password could be specified in plaintext or in SHA256 (hex format). +Password can be specified in plaintext or in SHA256 (hex format). - To assign a password in plaintext (**not recommended**), place it in a `password` element. For example, `qwerty`. The password can be left blank. + + - To assign a password using its SHA256 hash, place it in a `password_sha256_hex` element. For example, `65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5`. @@ -51,6 +53,19 @@ Password could be specified in plaintext or in SHA256 (hex format). The first line of the result is the password. The second line is the corresponding SHA256 hash. + + +- For compatibility with MySQL clients, password can be specified in double SHA1 hash. Place it in `password_double_sha1_hex` element. + + For example, `08b4a0f1de6ad37da17359e592c8d74788a83eb0`. + + Example of how to generate a password from shell: + + ``` + PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | openssl dgst -sha1 -binary | openssl dgst -sha1 + ``` + + The first line of the result is the password. The second line is the corresponding double SHA1 hash. ### user_name/networks diff --git a/docs/en/query_language/functions/rounding_functions.md b/docs/en/query_language/functions/rounding_functions.md index 3fe58a05c46..2640472f955 100644 --- a/docs/en/query_language/functions/rounding_functions.md +++ b/docs/en/query_language/functions/rounding_functions.md @@ -78,6 +78,91 @@ round(3.55, 1) = 3.6 round(3.65, 1) = 3.6 ``` +**See Also** + +- [roundBankers](#roundbankers) + +## roundBankers {#roundbankers} + +Rounds a number to a specified decimal position. + +- If rounding number is a half between two numbers, the function uses banker's rounding. + + Banker's rounding is a method of rounding fractional numbers. When the rounding number is a half between two numbers, it is rounded to the nearest even number. E.g. 3.5 rounds up to 4, 2.5 rounds down to 2. + +- In other cases function rounds numbers to the nearest integer. + +Using banker's rounding, you can reduce the effect of rounding numbers on the result of summing or subtracting these numbers. + +For example, sum numbers 1.5, 2.5, 3.5, 4.5 with different rounding: + +- No rounding: 1.5 + 2.5 + 3.5 + 4.5 = 12. +- Banker's rounding: 2 + 2 + 4 + 4 = 12. +- Rounding to the nearest integer: 2 + 3 + 4 + 5 = 14. + +**Syntax** + +```sql +roundBankers(expression [, decimal_places]) +``` + +**Parameters** + +- `expression` — A number to be rounded. Can be any [expression](../syntax.md#syntax-expressions) returning the numeric [data type](../../data_types/index.md#data_types). +- `decimal-places` — Decimal places. An integer number. + - `decimal-places > 0` — The function rounds the number to the given position right of the decimal point. E.g. `roundBankers(3.55, 1) = 3.6`. + - `decimal-places < 0` — The function rounds the number to the given position left of the decimal point. E.g. `roundBankers(24.55, -1) = 20`. + - `decimal-places = 0` — The function rounds the number to integer. In this case the argument can be omitted. E.g. `roundBankers(2.5) = 2`. + +**Returned value** + +A value rounded by banker's rounding method. + +### Examples + +**Example of use** + +Query: + +```sql + SELECT number / 2 AS x, roundBankers(x, 0) AS b fROM system.numbers limit 10 +``` + +Result: + +```text +┌───x─┬─b─┐ +│ 0 │ 0 │ +│ 0.5 │ 0 │ +│ 1 │ 1 │ +│ 1.5 │ 2 │ +│ 2 │ 2 │ +│ 2.5 │ 2 │ +│ 3 │ 3 │ +│ 3.5 │ 4 │ +│ 4 │ 4 │ +│ 4.5 │ 4 │ +└─────┴───┘ +``` + +**Examples of Banker's rounding** + +```text +roundBankers(0.4) = 0 +roundBankers(-3.5) = -4 +roundBankers(4.5) = 4 +roundBankers(3.55, 1) = 3.6 +roundBankers(3.65, 1) = 3.6 +roundBankers(10.35, 1) = 10.4 +roundBankers(10.755, 2) = 11,76 +``` + +**See Also** + +- [round](#rounding_functions-round) + + + ## roundToExp2(num) Accepts a number. If the number is less than one, it returns 0. Otherwise, it rounds the number down to the nearest (whole non-negative) degree of two. diff --git a/docs/fa/interfaces/cli.md b/docs/fa/interfaces/cli.md index e5f869e1c0d..4c366ac0104 100644 --- a/docs/fa/interfaces/cli.md +++ b/docs/fa/interfaces/cli.md @@ -53,7 +53,7 @@ cat file.csv | clickhouse-client --database=test --query="INSERT INTO test FORMA شما میتوانید از `\G` به جای سیمی کالن یا بعد از سیمی کالن استفاده کنید. این علامت، فرمت Vertical را نشان می دهد. در این فرمت، هر مقدار در یک خط جدا چاپ می شود که برای جداول عریض مناسب است. این ویژگی غیرمعمول برای سازگاری با MySQL CLI اضافه شد. -command line برا پایه 'readline' (و 'history' یا 'libedit'، یه بدون کتابخانه بسته به build) می باشد. به عبارت دیگر، این محیط از shortcut های آشنا استفاده می کند و history دستورات را نگه می دار. history ها در فایل ~/.clickhouse-client-history نوشته می شوند. +command line برا پایه 'replxx' می باشد. به عبارت دیگر، این محیط از shortcut های آشنا استفاده می کند و history دستورات را نگه می دار. history ها در فایل ~/.clickhouse-client-history نوشته می شوند. به صورت پیش فرض فرمت خروجی PrettyCompact می باشد. شما میتوانید از طریق دستور FORMAT در یک query، یا با مشخص کردن `\G` در انتهای query، استفاده از آرگومان های `--format` یا `--vertical` یا از کانفیگ فایل کلاینت، فرمت خروجی را مشخص کنید. diff --git a/docs/ru/development/developer_instruction.md b/docs/ru/development/developer_instruction.md index 61be36a7089..4686847041d 100644 --- a/docs/ru/development/developer_instruction.md +++ b/docs/ru/development/developer_instruction.md @@ -5,6 +5,10 @@ Если вы используете Windows, вам потребуется создать виртуальную машину с Ubuntu. Для работы с виртуальной машиной, установите VirtualBox. Скачать Ubuntu можно на сайте: https://www.ubuntu.com/#download Создайте виртуальную машину из полученного образа. Выделите для неё не менее 4 GB оперативной памяти. Для запуска терминала в Ubuntu, найдите в меню программу со словом terminal (gnome-terminal, konsole или что-то в этом роде) или нажмите Ctrl+Alt+T. +# Если вы используете 32-битную систему + +ClickHouse не работает и не собирается на 32-битных системах. Получите доступ к 64-битной системе и продолжайте. + # Создание репозитория на GitHub @@ -96,15 +100,7 @@ brew install cmake ninja # Необязательные внешние библиотеки -ClickHouse использует для сборки некоторое количество внешних библиотек. Большинство из них не требуется отдельно устанавливать, так как они собираются вместе с ClickHouse, из исходников, которые расположены в submodules. Посмотреть набор этих библиотек можно в директории contrib. - -Одна библиотека не собирается из исходников, а используется из системы: Readline, и её рекомендуется установить. - -Ubuntu: `sudo apt install libreadline-dev` - -Mac OS X: `brew install readline` - -Впрочем, эти библиотеки не обязательны для работы и ClickHouse может быть собран без них. ICU используется для поддержки `COLLATE` в `ORDER BY` (например, для сортировки с учётом турецкого алфавита). Readline используется для более удобного набора команд в интерактивном режиме в clickhouse-client. +ClickHouse использует для сборки некоторое количество внешних библиотек. Но ни одну из них не требуется отдельно устанавливать, так как они собираются вместе с ClickHouse, из исходников, которые расположены в submodules. Посмотреть набор этих библиотек можно в директории contrib. # Компилятор C++ diff --git a/docs/ru/extended_roadmap.md b/docs/ru/extended_roadmap.md index d5cbcf5f8c8..8448822282d 100644 --- a/docs/ru/extended_roadmap.md +++ b/docs/ru/extended_roadmap.md @@ -16,9 +16,11 @@ Изначально делал [Андрей Чулков](https://github.com/achulkov2), ВШЭ, теперь доделывает [Ольга Хвостикова](https://github.com/stavrolia), но сроки немного сдвинуты из-за задачи 25.9. Будем надеятся на реализацию к концу ноября. Впрочем, [Андрей Чулков](https://github.com/achulkov2) скоро сможет помочь её доделать. +Upd. Доделывать будет другой человек. Хотелось бы в Q1, но приоритет не высокий. + ### 1.2. Wait-free каталог баз данных. -Делает [Александр Токмаков](https://github.com/tavplubix), первый рабочий вариант в декабре 2019. Нужно для DataLens и Яндекс.Метрики. +Q1. Делает [Александр Токмаков](https://github.com/tavplubix), первый рабочий вариант в декабре 2019. Нужно для DataLens и Яндекс.Метрики. Манипуляции с каталогом баз данных: запросы CREATE TABLE, DROP TABLE, RENAME TABLE и DATABASE, требуют синхронизации с помощью блокировок. Эта синхронизация становится весьма сложной, так как на неё полагается много внутренних структур данных. @@ -26,7 +28,7 @@ ### 1.3. Неблокирующие ALTER. -И полностью immutable куски. Делает [Александр Сапин](https://github.com/alesapin). Готов приступить к задаче в конце ноября 2019. Нужно для Яндекс.Метрики. +Q1. И полностью immutable куски. Делает [Александр Сапин](https://github.com/alesapin). Готов приступить к задаче в конце ноября 2019. Нужно для Яндекс.Метрики. ### 1.4. Нетранзитивные ALTER столбцов. @@ -38,6 +40,8 @@ ### 1.6. Полиморфные куски данных. +Компактные куски - Q1, куски в оперативке Q1/Q2. + Делает [Антон Попов](https://github.com/CurtizJ), первый рабочий вариант в декабре. Пререквизит чтобы снизить сложность мелких INSERT, что в свою очередь нужно для 1.12, иначе задача 1.12 не сможет нормально работать. Особенно нужно для Яндекс.Облака. Данные в таблицах типа MergeTree в ClickHouse хранятся в виде набора независимых "кусков". Внутри куска, каждый столбец, а также индекс, хранится в отдельных файлах. Это сделано для возможности быстрых манипуляций со столбцами (пример - запрос ALTER DROP COLUMN). При вставке данных (INSERT), создаётся новый кусок. Для таблиц с большим количеством столбцов, запросы INSERT с маленьким количеством строк являются неэффективными, так как требуют создания большого количества файлов в файловой системе. Это является врождённой особенностью ClickHouse - одной из первой проблем, с которыми сталкиваются пользователи. Пользователям приходится буферизовывать данные и собирать их в более крупные пачки перед вставкой в ClickHouse. @@ -54,6 +58,8 @@ Делает [Владимир Чеботарёв](https://github.com/excitoon), Altinity. Декабрь 2019. +Q1. Закоммичено, но есть технический долг, который исправляется сейчас. + ### 1.9. Использование TTL для прореживания данных. Будет делать Сорокин Николай, ВШЭ и Яндекс. @@ -86,6 +92,8 @@ ### 1.11. Виртуальная файловая система. +Q2. + Нужно для Яндекс.Облака. Делает Александр, Яндекс.Облако, а также Олег Ершов, ВШЭ и Яндекс. ClickHouse использует для хранения данных локальную файловую систему. Существует сценарий работы, в котором размещение старых (архивных) данных было бы выгодно на удалённой файловой системе. Если файловая система POSIX совместимая, то это не составляет проблем: ClickHouse успешно работает с Ceph, GlusterFS, MooseFS. Также востребованным является сценарий использования S3 (из-за доступности в облаке) или HDFS (для интеграции с Hadoop). Но эти файловые системы не являются POSIX совместимыми. Хотя для них существуют FUSE драйверы, но скорость работы сильно страдает и поддержка неполная. @@ -94,6 +102,8 @@ ClickHouse использует небольшое подмножество фу ### 1.12. Экспериментальная реализация VFS поверх S3 и HDFS. +Q2. + Нужно для Яндекс.Облака. Требует 1.11. Желательно 1.6 и 1.18. Делает Александр, Яндекс.Облако (сначала часть для S3), а также Олег Ершов, ВШЭ и Яндекс. @@ -103,8 +113,10 @@ ClickHouse использует небольшое подмножество фу ### 1.14. Не писать столбцы, полностью состоящие из нулей. +Антон Попов. Q1/Q2. В очереди. Простая задача, является небольшим пререквизитом для потенциальной поддержки полуструктурированных данных. + ### 1.15. Возможность иметь разный первичный ключ в разных кусках. Сложная задача, только после 1.3. @@ -130,6 +142,9 @@ ClickHouse использует небольшое подмножество фу Делает [Николай Кочетов](https://github.com/KochetovNicolai). Финальная стадия разработки. Включение по-умолчанию в конце декабря 2019. Удаление старого кода в начале 2020. +Upd. На данный момент исправляются проблемы с регрессиями производительности в отдельных случаях. Кажется, что все проблемы исправлены. +Включение по-умолчанию в Q1, но остаётся вторая часть задачи по корректному выделению async части. + ### 2.2. Инфраструктура событий/метрик/ограничений/квот/трассировки. В очереди. https://gist.github.com/alexey-milovidov/d62d73222d83b9319dc519cbb13aeff6 @@ -152,11 +167,11 @@ ClickHouse использует небольшое подмножество фу ### 2.7. Нормализация Context. -В очереди. +В очереди. Нужно для YQL. ### 2.8. Декларативный парсер запросов. -Низкий приоритет. Задачу хочет сделать [Иван Лежанкин](https://github.com/abyss7) в свободное время, но пока ничего нет. +Средний приоритет. Нужно для YQL. ### 2.9. Логгировние в format-стиле. @@ -193,7 +208,7 @@ ClickHouse использует небольшое подмножество фу ### 3.1. Перенос документации по функциям в код. -Требует 2.12 и 2.13. +Требует 2.12 и 2.13. Хотим в Q1/Q2, средний приоритет. ### 3.2. Перенос однородных частей документации в код. @@ -201,7 +216,7 @@ ClickHouse использует небольшое подмножество фу ### 3.3. Исправить катастрофически отвратительно неприемлемый поиск по документации. -[Иван Блинков](https://github.com/blinkov/) - очень хороший человек. Сам сайт документации основан на технологиях, не удовлетворяющих требованиям задачи, и эти технологии трудно исправить. +[Иван Блинков](https://github.com/blinkov/) - очень хороший человек. Сам сайт документации основан на технологиях, не удовлетворяющих требованиям задачи, и эти технологии трудно исправить. Задачу будет делать первый встретившийся нам frontend разработчик, которого мы сможем заставить это сделать. ### 3.4. + Добавить японский язык в документацию. @@ -212,7 +227,7 @@ ClickHouse использует небольшое подмножество фу ### 4.1. Уменьшение числа потоков при распределённых запросах. -[Никита Лапков](https://github.com/laplab), весна 2020. +[Никита Лапков](https://github.com/laplab), весна 2020. Upd. Есть прототип. ### 4.2. Спекулятивное выполнение запросов на нескольких репликах. @@ -231,7 +246,7 @@ ClickHouse использует небольшое подмножество фу ### 4.4. Ограничение сетевой полосы при репликации. -Дмитрий Григорьев, ВШЭ. +Дмитрий Григорьев, ВШЭ. Нужно для Метрики. ### 4.5. Возможность продолжить передачу куска данных при репликации после сбоя. @@ -251,19 +266,20 @@ ClickHouse использует небольшое подмножество фу ### 5.1. Разделение задач на более мелкие куски в clickhouse-copier. -Нужно для Метрики, в очереди, но исполнитель не назначен, есть шанс успеть в 2019. +Q1. Нужно для Метрики, в очереди. Никита Михайлов. ### 5.2. Автонастройка лимита на оперативку и размера кэшей. -### 5.3. Встроенная ручка для Prometheus и, возможно, Solomon. +### 5.3. + Встроенная ручка для Prometheus. -Простая задача. https://github.com/Vdimir +Сделано. https://github.com/Vdimir ### 5.4. Opt-in сообщать в клиенте, если вышла новая версия. -### 5.5. LTS релизы. +### 5.5. + LTS релизы. Требует 7.5. Задачу хочет Метрика, Облако, БК, Маркет и Altinity. Первой LTS версией уже стала версия 19.14. +Метрика, БК, Маркет уже используют более свежие версии чем LTS. ## 6. Инструментирование. @@ -271,15 +287,15 @@ ClickHouse использует небольшое подмножество фу ### 6.1. Исправления сэмплирующего профайлера запросов. Михаил Филимонов, Altinity. Ноябрь 2019. Сделано. -Осталось ещё проверить работоспособность профайлера в первом потоке (что важно для INSERT). +Осталось ещё проверить работоспособность профайлера в первом потоке (что важно для INSERT). Иван Лежанкин. Q1. ### 6.2. Добавление memory profiler. -Сравнительно простая задача, но только для опытных разработчиков. Нужна всем. +Сравнительно простая задача, но только для опытных разработчиков. Нужна всем. Иван Лежанкин. Q1. ### 6.3. Учёт оперативки total расширить не только на запросы. -Исправление долгоживущей проблемы с дрифтом учёта оперативки. Нужна для Метрики и БК. +Исправление долгоживущей проблемы с дрифтом учёта оперативки. Нужна для Метрики и БК. Иван Лежанкин. Q1. ### 6.4. Поддержка perf events как метрик запроса. @@ -291,11 +307,11 @@ ClickHouse использует небольшое подмножество фу Требует 2.2. -### 6.6. Стек трейс для любых исключений. +### 6.6. + Стек трейс для любых исключений. Сейчас есть стек трейс для почти всех, но не всех исключений. Требует 7.4. -### 6.7. Таблица system.stack_trace. +### 6.7. + Таблица system.stack_trace. Сравнительно простая задача, но только для опытных разработчиков. @@ -327,7 +343,7 @@ ClickHouse использует небольшое подмножество фу Сейчас включено только при сборке с clang, но продакшен сборка использует gcc. Требует 7.2 и, возможно, 7.1 (только в случае новой версии ICU). -### 7.5. Начать публиковать LTS релизы. +### 7.5. + Начать публиковать LTS релизы. [Александр Сапин](https://github.com/alesapin). @@ -364,11 +380,17 @@ UBSan включен в функциональных тестах, но не в При сборке с clang, -Weverything уже включено. Но в gcc есть уникальные warning-и, отсутствующие в clang. Сделал Wolf Kreuzerkrieg. -### 7.14. Альтернатива для readline и libedit. +### 7.14. + Альтернатива для readline и libedit. -Тагир Кускаров, ВШЭ. Посмотрим на https://github.com/AmokHuginnsson/replxx +Подключение replxx вместо readline сделал Иван Лежанкин. -Для ввода запросов в интерактивном режиме в клиенте командной строки clickhouse-client используется библиотека readline или libedit. +### 7.14.1. Улучшение возможностей интерактивного режима clickhouse-client. + +Тагир Кускаров, ВШЭ. + +Upd. В рамках данной задачи добавляем подстветку синтаксиса и исправление проблем со вставкой больших запросов. + +Для ввода запросов в интерактивном режиме в клиенте командной строки clickhouse-client использовалась библиотека readline или libedit. Библиотеки readline и libedit обладает следующими недостатками: - (исправлено в новых версиях readline) Очень низкая производительность вставки больших кусков текста. Вставка каждого следующего символа имеет сложность O(n = количество предыдущих символов) и при вставке 1 МБ текста, скорость падает до десятков байт в секунду. @@ -407,11 +429,13 @@ UBSan включен в функциональных тестах, но не в Уже давно собираются универсальные tgz пакеты, но по нелепой случайности из них исчез install скрипт. [Александр Сапин](https://github.com/alesapin). Может делегировать эту задачу кому угодно. +Upd. Сделано всё кроме инструкции на сайте. Для этого требуется создать директории testing/stable/prestable на repo.yandex.ru. Внезапно оказалось, что человек, отвечающий за это, в отпуске, и он не отвечает на вопрос, кто его заместитель. Q1. ### 7.18.1. Доделать бинарники под Mac. Уже есть автосборка бинарников под Mac на каждый коммит и PR, но с недостатками. [Иван Лежанкин](https://github.com/abyss7). Требует 7.1, 7.2. Рекомендуется 7.14. Сейчас не хватает по крайней мере SSL и ICU. Нужно для Яндекс.Облака. +Upd. Сделано SSL. Ориентируемся в Q1, но приоритет средний и может потеряться. ### 7.18. Поместить ссылку на собранные бинарники под Mac на сайт. @@ -480,6 +504,8 @@ Fuzzing тестирование - это тестирование случай Также можно сделать функции с детерминированным генератором случайных чисел (аргументом передаётся seed) для воспроизводимости тестовых кейсов. +Upd. Сергей Штыков сделал функцию `randomPrintableASCII`. + ### 7.24. Fuzzing лексера и парсера запросов; кодеков и форматов. Андрей Некрашевич, ВШЭ. @@ -496,6 +522,9 @@ Fuzzing тестирование - это тестирование случай Затем, возможно, [Иван Лежанкин](https://github.com/abyss7). Но сейчас приостановлено, так как Максим из YT должен исправить регрессию производительности в анализе индекса. Максим из YT сказал, что сделает это после нового года. +Максим из YT сказал, что "мы планируем в январе добиться". + +Нужно для CHYT и YQL. ### 7.26. Побайтовая идентичность репозитория с Аркадией. @@ -529,11 +558,11 @@ Fuzzing тестирование - это тестирование случай ### 7.33. Выкладывать патч релизы в репозиторий автоматически. -[Александр Сапин](https://github.com/alesapin). Может делегировать эту задачу кому угодно. +В очереди. Иван Лежанкин. ### 7.34. Бэкпортировать bugfix автоматически. -[Александр Сапин](https://github.com/alesapin). Может делегировать эту задачу кому угодно. +В очереди. Иван Лежанкин. ### 7.35. Начальные правила для авто-merge. @@ -564,6 +593,8 @@ Altinity. [Ольга Хвостикова](https://github.com/stavrolia). +Уменьшение числа stream-ов сделано, а вот правильная поддержка диапазонов - нет. Будем надеяться на Q1/Q2. + ### 8.4. Унификация File, HDFS, S3 под URL. ### 8.5. + Аутентификация в S3. @@ -579,7 +610,7 @@ Altinity. В ядрах 2.6 отсутствует один системный вызов, который библиотека hdfs3 использует без необходимости. Сделал Amos Bird. -### 8.8. Поддержка виртуальных столбцов с именем файла и путём. +### 8.8. + Поддержка виртуальных столбцов с именем файла и путём. [Ольга Хвостикова](https://github.com/stavrolia). @@ -611,10 +642,26 @@ Altinity. ### 8.16. Поддержка формата Avro. -Павел Круглов, ВШЭ и Яндекс. +Andrew Onyshchuk. Есть pull request. Q1. Формат Apache Avro является компактным структурированным построчным бинарным форматом данных с внешней схемой. Этот формат часто используется совместно с Kafka и поддержка его в качестве одного из форматов ввода-вывода в ClickHouse является востребованной пользователями. +### 8.16.1. Поддержка формата JSONEachRow, засунутого в массив. + +Павел Круглов, ВШЭ и Яндекс. + +### 8.16.2. Поддержка формата Thrift. + +Павел Круглов, ВШЭ и Яндекс. + +### 8.16.3. Поддержка формата MsgPack. + +Павел Круглов, ВШЭ и Яндекс. + +### 8.16.4. Формат Regexp. + +Павел Круглов, ВШЭ и Яндекс. + ### 8.17. ClickHouse как MySQL реплика. Ильяс Адюгамов, ВШЭ. @@ -642,6 +689,7 @@ Maxim Fedotov, Wargaming + Yuri Baranov, Яндекс. ### 8.21. Поддержка произвольного количества языков для имён регионов. Нужно для БК. Декабрь 2019. +В декабре для БК сделан минимальный вариант этой задачи. ### 8.22. Поддержка синтаксиса для переменных в стиле MySQL. @@ -689,7 +737,7 @@ ClickHouse предоставляет возможность обратитьс ### 10.4. Словарь из YDB (KikiMR). -Нужно для Метрики, а делать будет таинственный незнакомец из команды KikiMR (под вопросом). +Нужно для Метрики, а делать будет таинственный незнакомец из команды KikiMR (под вопросом). Таинственный незнакомец не подтверждает, что он будет делать эту задачу. ### 10.5. Закрытие соединений и уменьшение числа соединений для MySQL и ODBC. @@ -707,15 +755,15 @@ ClickHouse предоставляет возможность обратитьс ### 10.9. Уменьшение блокировок для cache словарей за счёт одновременных запросов одного и того же. -Нужно для БК, но мотивация задачи находится под вопросом, так как есть рабочее предположение о том, что данная задача не устраняет причину проблемы. +Никита Михайлов. Q1. Нужно для БК, но мотивация задачи находится под вопросом, так как есть рабочее предположение о том, что данная задача не устраняет причину проблемы. ### 10.10. Возможность использования старых значений из cache словаря пока они перезапрашиваются. -Нужно для БК и Метрики. +Никита Михайлов. Q1. Нужно для БК и Метрики. ### 10.11. Возможность исключительно асинхронных запросов в cache словарях. -Нужно для БК и Метрики. Требует 10.10. +Никита Михайлов. Q1. Нужно для БК и Метрики. Требует 10.10. ### 10.12. Layout direct для словарей. @@ -731,7 +779,7 @@ ClickHouse предоставляет возможность обратитьс ### 10.16. Словари на локальном SSD. -Никита Васильев, ВШЭ и Яндекс. +Никита Васильев, ВШЭ и Яндекс. Есть pull request. Реализовать в ClickHouse специализированный движок таблиц, подходящий для быстрых key-value запросов и оптимизированный для расположения данных на SSD. Это может быть: реализация на основе RocksDB; сериализованные RowBinary данные с индексом в оперативке; секретная очень эффективная структура данных, о которой я расскажу. @@ -778,7 +826,7 @@ ClickHouse предоставляет возможность обратитьс ### 11.10. Преднастроенные HTTP handlers для запросов. -zhang2014 +zhang2014, есть pull request. Возможность описать в конфигурационном файле handler (путь в URL) для HTTP запросов к серверу, которому соответствует некоторый параметризованный запрос. Пользователь может вызвать этот обработчик и не должен передавать SQL запрос. @@ -787,15 +835,18 @@ zhang2014 ### 12.1. Role Based Access Control. -[Виталий Баранов](https://github.com/vitlibar). Финальная стадия разработки, рабочая версия в декабре 2019. +[Виталий Баранов](https://github.com/vitlibar). Финальная стадия разработки, рабочая версия в начале февраля 2019. +Q1. Сейчас сделаны все интерфейсы в коде и запросы, но не сделаны варианты хранения прав кроме прототипа. ### 12.2. Управление пользователями и правами доступа с помощью SQL запросов. [Виталий Баранов](https://github.com/vitlibar). Финальная стадия разработки, рабочая версия в декабре 2019. +Q1. ### 12.3. Подключение справочника пользователей и прав доступа из LDAP. [Виталий Баранов](https://github.com/vitlibar). Требует 12.1. +Q1/Q2. ### 12.4. Подключение IDM системы Яндекса как справочника пользователей и прав доступа. @@ -823,6 +874,7 @@ zhang2014 ### 13.3. Пулы ресурсов. Требует 13.2 или сможем сделать более неудобную реализацию раньше. +Обсуждается вариант неудобной реализации. Пока средний приоритет, целимся на Q1/Q2. ## 14. Диалект SQL. @@ -830,9 +882,12 @@ zhang2014 ### 14.1. Исправление семантики CAST для Nullable. Нужно для DataLens. А также для внедрения в BI инструмент Looker. +Павел Потёмкин, ВШЭ. ### 14.2. Поддержка WITH для подзапросов. +Павел Потёмкин, ВШЭ. + ### 14.3. Поддержка подстановок для множеств в правой части IN. ### 14.4. Поддержка подстановок для идентификаторов (имён) в SQL запросе. @@ -845,8 +900,12 @@ zhang2014 ### 14.6. Глобальный scope для WITH. +Павел Потёмкин, ВШЭ. + ### 14.7. Nullable для WITH ROLLUP, WITH CUBE, WITH TOTALS. +Павел Потёмкин, ВШЭ. + Простая задача. ### 14.8. Модификаторы DISTINCT, ORDER BY для агрегатных функций. @@ -893,18 +952,23 @@ zhang2014. ### 14.18. UNION DISTINCT и возможность включить его по-умолчанию. +Павел Потёмкин, ВШЭ. Для BI систем. ### 14.19. Совместимость парсера типов данных с SQL. +Павел Потёмкин, ВШЭ. Для BI систем. ### 14.20. Позиционные аргументы для GROUP BY и ORDER BY. +Павел Потёмкин, ВШЭ. Тривиально и используется многими системами, но не входит в стандарт SQL. ### 14.21. Приведение типов для IN (подзапрос) и для JOIN. +Павел Потёмкин, ВШЭ. + ## 15. Улучшение поддержки JOIN. @@ -912,6 +976,15 @@ zhang2014. Артём Зуйков. Сейчас merge JOIN включается вручную опцией и всегда замедляет запросы. Хотим, чтобы он замедлял запросы только когда это неизбежно. Кстати, смысл merge JOIN появляется только совместно с 15.2 и 15.3. +Q1. + +### 15.1.1. Алгоритм two-level merge JOIN. + +Александр Кузьменков. В очереди. + +### 15.1.2. Тестирование реализации JOIN в Greenplum. + +В очереди. ### 15.2. Прокидывание условий в OUTER JOIN. @@ -934,7 +1007,7 @@ zhang2014. ## 16. Типы данных и функции. -### 16.1. DateTime64. +### 16.1. + DateTime64. Василий Немков, Altinity, декабрь 2019. @@ -964,6 +1037,8 @@ ClickHouse не является geospatial СУБД. Тем не менее, в Похожая, но более сложная задача, которую ClickHouse пока не умеет решать - определение полигона среди множества полигонов, в которые попадают точки. Для примера: определение района города по географическим координатам. Для решения этой задачи нужно будет реализовать поддержку словарей с полигонами, в которых данные проиндексированы для быстрого поиска. +Upd. Андрей сделал прототип интерфейса и реализацию-заглушку внутри него. + ### 17.2. GIS типы данных и операции. Алексей Коряков, Алексей Илюхов, ВШЭ, Яндекс.Карты. @@ -1070,13 +1145,15 @@ Hold. Полезно для заказчиков внутри Яндекса, н Начинал Олег Ершов, доделывает Никита Михайлов, помогает [Александр Кузьменков](https://github.com/akuzm). Готово. +### 21.1.1. Избавление от лишнего копирование при параллельном парсинге форматов, если возможен mmap файла целиком. + ### 21.2. Параллельное форматирование форматов. После 21.1, предположительно Никита Михайлов. Задача сильно проще чем 21.1. ### 21.3. Исправление низкой производительности анализа индекса в случае большого множества в секции IN. -Нужно всем (Zen, БК, DataLens...) Пока ещё не выбран исполнитель. +Нужно всем (Zen, БК, DataLens, TestEnv...). Антон Попов, Q1/Q2. ### 21.4. Использование ORDER BY ключа для оптимизации GROUP BY и DISTINCT. @@ -1091,12 +1168,14 @@ Hold. Полезно для заказчиков внутри Яндекса, н ### 21.5. Распараллеливание INSERT при INSERT SELECT, если это необходимо. [Vxider](https://github.com/Vxider), ICT +Есть pull request. ### 21.6. Уменьшение числа потоков для SELECT в случае тривиального INSERT SELECT. ### 21.7. Кэш результатов запросов. [Achimbab](https://github.com/achimbab). +Есть pull request. ### 21.8. Взаимная интеграция аллокатора и кэша. @@ -1131,6 +1210,8 @@ Amos Bird. - Вынесение любых функций наружу any, anyLast. - При GROUP BY по transform или if по строкам, замена строк на Enum. +Сделана замена цепочек if на multiIf, но внезапно оказалось, что это является не оптимизацией, а наоборот. + ### 21.12. Алгебраические оптимизации запросов. Руслан Камалов, Михаил Малафеев, Виктор Гришанин, ВШЭ @@ -1200,9 +1281,9 @@ Constraints позволяют задать выражение, истиннос В ByteDance есть готовая реализация, но они её боятся из-за, возможно, низкого качества кода. -### 21.21. Чтение больших файлов с помощью mmap. +### 21.21. + Чтение больших файлов с помощью mmap. -Тривиально, почти всё готово. +Сделан вариант, но достаточно топорный. Без тестирования в продакшене включать по-умолчанию нельзя. ### 21.22. Userspace page cache. @@ -1211,6 +1292,7 @@ Constraints позволяют задать выражение, истиннос ### 21.23. Ускорение работы с вторичными индексами. zhang2014. +Есть pull request. ## 22. Долги и недоделанные возможности. @@ -1219,9 +1301,9 @@ zhang2014. Нужно для Яндекс.Облака. Сделал Алексей Миловидов. -### 22.2. Убрать возможность изменить настройки в native протоколе в случае readonly. +### 22.2. + Убрать возможность изменить настройки в native протоколе в случае readonly. -Алексей Миловидов или [Виталий Баранов](https://github.com/vitlibar). +N.Vartolomei. ### 22.3. Защита от абсурдно заданных пользователем кодеков. @@ -1229,25 +1311,27 @@ zhang2014. ### 22.4. Исправление оставшихся deadlocks в табличных RWLock-ах. -Александр Казаков. Нужно для Яндекс.Метрики и Datalens. +Александр Казаков. Нужно для Яндекс.Метрики и Datalens. Задача постепенно тащится и исправлениями в соседних местах стала менее актуальна. +В Q1 будет сделана или отменена с учётом 1.2. и 1.3. -### 22.5. Исправление редких срабатываний TSan в stress тестах в CI. +### 22.5. + Исправление редких срабатываний TSan в stress тестах в CI. Александр Казаков. ### 22.6. Изменение только DEFAULT в ALTER TABLE может поменять тип столбца. -### 22.7. Row-Level Security не работает в случае наличия в запросе IN подзапросов. +### 22.7. + Row-Level Security не работает в случае наличия в запросе IN подзапросов. -[Виталий Баранов](https://github.com/vitlibar). Нужно для Метрики. +Нужно для Метрики. Иван Лежанкин. -### 22.8. Исправить десериализацию параметров для параметризованных запросов. +### 22.8. + Исправить десериализацию параметров для параметризованных запросов. Хотел исправить Василий Немков, Altinity, но есть маленькие затруднения, наверное переделает Алексей Миловидов. ### 22.9. Разобраться с десериализацией массивов со значениями по-умолчанию в Protobuf формате в случае protobuf 3. [Виталий Баранов](https://github.com/vitlibar). Возможно, это - фундаментальная проблема и следует её только документировать. +Кажется, отменяем, но пока ещё не всё ясно. ### 22.10. Исправление дрифта при отслеживании потребления памяти запросами. @@ -1278,11 +1362,10 @@ zhang2014. Altinity. -### 22.16. Исправление низкой производительности кодека DoubleDelta. +### 22.16. + Исправление низкой производительности кодека DoubleDelta. Василий Немков, Altinity - в процессе. - -Мы считаем важным, что код в ClickHouse содержит разумные оптимизации, основанные на анализе производительности. Но иногда бывают досадные исключения. +Можно считать, что сделано, хотя отсутствие SIMD оптимизаций для variable length кодеков - это ужасно. ### 22.17. Консистентно работающий POPULATE для MaterializedView. @@ -1331,14 +1414,14 @@ https://github.com/ClickHouse/ClickHouse/issues/2655 Altinity. -### 22.29. Уязвимость DDL для словарей executable. +### 22.29. + Уязвимость DDL для словарей executable. [Александр Сапин](https://github.com/alesapin) ## 23. Default Festival. -### 23.1. Включение minimalistic_part_header в ZooKeeper. +### 23.1. + Включение minimalistic_part_header в ZooKeeper. Сильно уменьшает объём данных в ZooKeeper. Уже год в продакшене в Яндекс.Метрике. Алексей Миловидов, ноябрь 2019. @@ -1367,13 +1450,13 @@ Altinity. Просто аккуратно включить. -### 23.8. Включение оптимизации VALUES. +### 23.8. + Включение оптимизации VALUES. Просто аккуратно включить. ### 23.9. Включение Processors. -[Николай Кочетов](https://github.com/KochetovNicolai). +Q1. [Николай Кочетов](https://github.com/KochetovNicolai). ### 23.10. Включение mlock бинарника. @@ -1578,12 +1661,16 @@ ucasFL, ICT. Алгоритмы min-hash и sim-hash позволяют вычислить для текста несколько хэш-значений таких, что при небольшом изменении текста, по крайней мере один из хэшей не меняется. Вычисления можно реализовать на n-грамах и словарных шинглах. Предлагается добавить поддержку этих алгоритмов в виде функций в ClickHouse и изучить их применимость для задачи нечёткого поиска полудубликатов. +Есть pull request, есть что доделывать. + ### 24.28. Другой sketch для квантилей. -Похоже на quantileTiming, но с логарифмическими корзинами. +Похоже на quantileTiming, но с логарифмическими корзинами. См. DDSketch. ### 24.29. Поддержка Arrow Flight. +Жанна Зосимова, ВШЭ. + ### 24.30. ClickHouse как графовая СУБД. Amos Bird, но его решение слишком громоздкое и пока не open-source. @@ -1613,7 +1700,7 @@ Amos Bird, но его решение слишком громоздкое и п ### 25.2. Вычитка и выкладка статьи про обфускацию данных на английском. -Эми, Александр Казаков, Алексей Миловидов, ноябрь 2019. +Эми, Александр Казаков, Алексей Миловидов, Q1. Готово к выкладке. ### 25.3. Подготовка статьи "Секреты оптимизации производительности ClickHouse". @@ -1640,9 +1727,9 @@ Amos Bird, но его решение слишком громоздкое и п ### 25.9. Подготовка докладчиков: khvostikao, ilezhankin, nikitamikhailov, akuzm и другие. -[Ольга Хвостикова](https://github.com/stavrolia), [Иван Лежанкин](https://github.com/abyss7), Никита Михайлов, [Александр Кузьменков](https://github.com/akuzm). +[Ольга Хвостикова](https://github.com/stavrolia), [Иван Лежанкин](https://github.com/abyss7), Никита Михайлов, [Александр Кузьменков](https://github.com/akuzm), Артём Зуйков. Уже готовые докладчики: Алексей Миловидов, [Николай Кочетов](https://github.com/KochetovNicolai), [Александр Сапин](https://github.com/alesapin). -Получаем минимум 7 докладчиков в 2020 году. +Получаем минимум 8 докладчиков в 2020 году. ### 25.10. Митапы в России и Беларуси: Москва x2 + митап для разработчиков или хакатон, Санкт-Петербург, Минск, Нижний Новгород, Екатеринбург, Новосибирск и/или Академгородок, Иннополис или Казань. @@ -1650,7 +1737,7 @@ Amos Bird, но его решение слишком громоздкое и п ### 25.11. Митапы зарубежные: восток США (Нью Йорк, возможно Raleigh), возможно северо-запад (Сиэтл), Китай (Пекин снова, возможно митап для разработчиков или хакатон), Лондон. -[Иван Блинков](https://github.com/blinkov/) - организация +[Иван Блинков](https://github.com/blinkov/) - организация. Две штуки в США запланированы. ### 25.12. Статья "научная" - про устройство хранения данных и индексов или whitepaper по архитектуре. Есть вариант подать на VLDB. diff --git a/docs/ru/faq/general.md b/docs/ru/faq/general.md index 010926d2cf9..970069bc641 100644 --- a/docs/ru/faq/general.md +++ b/docs/ru/faq/general.md @@ -21,4 +21,37 @@ NLS_LANG=RUSSIAN_RUSSIA.UTF8 ``` +## Как экспортировать данные из ClickHouse в файл? {#how-to-export-to-file} + +### Секция INTO OUTFILE + +Добавьте секцию [INTO OUTFILE](../query_language/select/#into-outfile-clause) к своему запросу. + +Например: + +```sql +SELECT * FROM table INTO OUTFILE 'file' +``` + +По умолчанию, для выдачи данных ClickHouse использует формат [TabSeparated](../interfaces/formats.md#tabseparated). Чтобы выбрать [формат данных](../interfaces/formats.md), используйте [секцию FORMAT](../query_language/select/#format-clause). + +Например: + +```sql +SELECT * FROM table INTO OUTFILE 'file' FORMAT CSV +``` + +### Таблица с движком File + +Смотрите [File](../operations/table_engines/file.md). + +### Перенаправление в командой строке + +```sql +$ clickhouse-client --query "SELECT * from table" > result.txt +``` + +Смотрите [clickhouse-client](../interfaces/cli.md). + + [Оригинальная статья ](https://clickhouse.yandex/docs/en/faq/general/) diff --git a/docs/ru/interfaces/formats.md b/docs/ru/interfaces/formats.md index 36f7ae462c4..ad941b23345 100644 --- a/docs/ru/interfaces/formats.md +++ b/docs/ru/interfaces/formats.md @@ -28,6 +28,7 @@ ClickHouse может принимать (`INSERT`) и отдавать (`SELECT | [PrettySpace](#prettyspace) | ✗ | ✔ | | [Protobuf](#protobuf) | ✔ | ✔ | | [Parquet](#data-format-parquet) | ✔ | ✔ | +| [ORC](#data-format-orc) | ✔ | ✗ | | [RowBinary](#rowbinary) | ✔ | ✔ | | [RowBinaryWithNamesAndTypes](#rowbinarywithnamesandtypes) | ✔ | ✔ | | [Native](#native) | ✔ | ✔ | @@ -132,28 +133,28 @@ world `delimiter_1${column_1:serializeAs_1}delimiter_2${column_2:serializeAs_2} ... delimiter_N`, - где `delimiter_i` - разделители между значениями (символ `$` в разделителе экранируется как `$$`), - `column_i` - имена или номера столбцов, значения которых должны быть выведены или считаны (если имя не указано - столбец пропускается), + где `delimiter_i` - разделители между значениями (символ `$` в разделителе экранируется как `$$`), + `column_i` - имена или номера столбцов, значения которых должны быть выведены или считаны (если имя не указано - столбец пропускается), `serializeAs_i` - тип экранирования для значений соответствующего столбца. Поддерживаются следующие типы экранирования: - + - `CSV`, `JSON`, `XML` (как в одноимённых форматах) - `Escaped` (как в `TSV`) - `Quoted` (как в `Values`) - `Raw` (без экранирования, как в `TSVRaw`) - `None` (тип экранирования отсутствует, см. далее) - - Если для столбца не указан тип экранирования, используется `None`. `XML` и `Raw` поддерживаются только для вывода. - - Так, в форматной строке - + + Если для столбца не указан тип экранирования, используется `None`. `XML` и `Raw` поддерживаются только для вывода. + + Так, в форматной строке + `Search phrase: ${SearchPhrase:Quoted}, count: ${c:Escaped}, ad price: $$${price:JSON};` - + между разделителями `Search phrase: `, `, count: `, `, ad price: $` и `;` при выводе будут подставлены (при вводе - будут ожидаться) значения столбцов `SearchPhrase`, `c` и `price`, сериализованные как `Quoted`, `Escaped` и `JSON` соответственно, например: `Search phrase: 'bathroom interior design', count: 2166, ad price: $3;` - + Настройка `format_template_rows_between_delimiter` задаёт разделитель между строками, который выводится (или ожмдается при вводе) после каждой строки, кроме последней. По умолчанию `\n`. - + Настройка `format_template_resultset` задаёт путь к файлу, содержащему форматную строку для результата. Форматная строка для результата имеет синтаксис аналогичный форматной строке для строк таблицы и позволяет указать префикс, суффикс и способ вывода дополнительной информации. Вместо имён столбцов в ней указываются следующие имена подстановок: - `data` - строки с данными в формате `format_template_row`, разделённые `format_template_rows_between_delimiter`. Эта подстановка должна быть первой подстановкой в форматной строке. @@ -165,15 +166,15 @@ world - `time` - время выполнения запроса в секундах - `rows_read` - сколько строк было прочитано при выполнении запроса - `bytes_read` - сколько байт (несжатых) было прочитано при выполнении запроса - + У подстановок `data`, `totals`, `min` и `max` не должны быть указаны типы экранирования (или должен быть указан `None`). Остальные подстановки - это отдельные значения, для них может быть указан любой тип экранирования. Если строка `format_template_resultset` пустая, то по-умолчанию используется `${data}`. Из всех перечисленных подстановок форматная строка `format_template_resultset` для ввода может содержать только `data`. Также при вводе формат поддерживает пропуск значений столбцов и пропуск значений в префиксе и суффиксе (см. пример). - + Пример вывода: ```sql -SELECT SearchPhrase, count() AS c FROM test.hits GROUP BY SearchPhrase ORDER BY c DESC LIMIT 5 FORMAT Template SETTINGS +SELECT SearchPhrase, count() AS c FROM test.hits GROUP BY SearchPhrase ORDER BY c DESC LIMIT 5 FORMAT Template SETTINGS format_template_resultset = '/some/path/resultset.format', format_template_row = '/some/path/row.format', format_template_rows_between_delimiter = '\n ' ``` `/some/path/resultset.format`: @@ -225,7 +226,7 @@ Page views: 6, User id: 4324182021466249494, Useless field: world, Duration: 185 Total rows: 2 ``` ```sql -INSERT INTO UserActivity FORMAT Template SETTINGS +INSERT INTO UserActivity FORMAT Template SETTINGS format_template_resultset = '/some/path/resultset.format', format_template_row = '/some/path/row.format' ``` `/some/path/resultset.format`: @@ -238,7 +239,7 @@ Page views: ${PageViews:CSV}, User id: ${UserID:CSV}, Useless field: ${:CSV}, Du ``` `PageViews`, `UserID`, `Duration` и `Sign` внутри подстановок - имена столбцов в таблице, в которую вставляются данные. Значения после `Useless field` в строках и значение после `\nTotal rows: ` в суффиксе будут проигнорированы. Все разделители во входных данных должны строго соответствовать разделителям в форматных строках. - + ## TemplateIgnoreSpaces {#templateignorespaces} Подходит только для ввода. Отличается от формата `Template` тем, что пропускает пробельные символы между разделителями и значениями во входном потоке. Также в этом формате можно указать пустые подстановки с типом экранирования `None` (`${}` или `${:None}`), чтобы разбить разделители на несколько частей, пробелы между которыми должны игнорироваться. Такие подстановки используются только для пропуска пробелов. С помощью этого формата можно считывать `JSON`, если значения столбцов в нём всегда идут в одном порядке в каждой строке. Например, для вставки данных из примера вывода формата [JSON](#json) в таблицу со столбцами `phrase` и `cnt` можно использовать следующий запрос: @@ -941,7 +942,7 @@ ClickHouse поддерживает настраиваемую точность Типы данных столбцов в ClickHouse могут отличаться от типов данных соответствующих полей файла в формате Parquet. При вставке данных, ClickHouse интерпретирует типы данных в соответствии с таблицей выше, а затем [приводит](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) данные к тому типу, который установлен для столбца таблицы. -### Inserting and Selecting Data +### Вставка и выборка данных Чтобы вставить в ClickHouse данные из файла в формате Parquet, выполните команду следующего вида: @@ -955,7 +956,49 @@ $ cat {filename} | clickhouse-client --query="INSERT INTO {some_table} FORMAT Pa $ clickhouse-client --query="SELECT * FROM {some_table} FORMAT Parquet" > {some_file.pq} ``` -Для обмена данными с экосистемой Hadoop можно использовать движки таблиц [HDFS](../operations/table_engines/hdfs.md) и `URL`. +Для обмена данными с экосистемой Hadoop можно использовать движки таблиц [HDFS](../operations/table_engines/hdfs.md). + + +## ORC {#data-format-orc} + +[Apache ORC](https://orc.apache.org/) - это column-oriented формат данных, распространённый в экосистеме Hadoop. Вы можете только вставлять данные этого формата в ClickHouse. + +### Соответствие типов данных + +Таблица показывает поддержанные типы данных и их соответствие [типам данных](../data_types/index.md) ClickHouse для запросов `INSERT`. + +| Тип данных ORC (`INSERT`) | Тип данных ClickHouse | +| -------------------- | ------------------ | +| `UINT8`, `BOOL` | [UInt8](../data_types/int_uint.md) | +| `INT8` | [Int8](../data_types/int_uint.md) | +| `UINT16` | [UInt16](../data_types/int_uint.md) | +| `INT16` | [Int16](../data_types/int_uint.md) | +| `UINT32` | [UInt32](../data_types/int_uint.md) | +| `INT32` | [Int32](../data_types/int_uint.md) | +| `UINT64` | [UInt64](../data_types/int_uint.md) | +| `INT64` | [Int64](../data_types/int_uint.md) | +| `FLOAT`, `HALF_FLOAT` | [Float32](../data_types/float.md) | +| `DOUBLE` | [Float64](../data_types/float.md) | +| `DATE32` | [Date](../data_types/date.md) | +| `DATE64`, `TIMESTAMP` | [DateTime](../data_types/datetime.md) | +| `STRING`, `BINARY` | [String](../data_types/string.md) | +| `DECIMAL` | [Decimal](../data_types/decimal.md) | + +ClickHouse поддерживает настраиваемую точность для формата `Decimal`. При обработке запроса `INSERT`, ClickHouse обрабатывает тип данных Parquet `DECIMAL` как `Decimal128`. + +Неподдержанные типы данных ORC: `DATE32`, `TIME32`, `FIXED_SIZE_BINARY`, `JSON`, `UUID`, `ENUM`. + +Типы данных столбцов в таблицах ClickHouse могут отличаться от типов данных для соответствующих полей ORC. При вставке данных, ClickHouse интерпретирует типы данных ORC согласно таблице соответствия, а затем [приводит](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) данные к типу, установленному для столбца таблицы ClickHouse. + +### Вставка данных + +Данные ORC можно вставить в таблицу ClickHouse командой: + +```bash +$ cat filename.orc | clickhouse-client --query="INSERT INTO some_table FORMAT ORC" +``` + +Для обмена данных с Hadoop можно использовать [движок таблиц HDFS](../operations/table_engines/hdfs.md). ## Схема формата {#formatschema} diff --git a/docs/ru/interfaces/http.md b/docs/ru/interfaces/http.md index 7ae60ace82b..4779388cde7 100644 --- a/docs/ru/interfaces/http.md +++ b/docs/ru/interfaces/http.md @@ -173,7 +173,7 @@ $ echo 'SELECT number FROM numbers LIMIT 10' | curl 'http://localhost:8123/?data По умолчанию используется БД, которая прописана в настройках сервера, как БД по умолчанию. По умолчанию, это - БД default. Также вы всегда можете указать БД через точку перед именем таблицы. -Имя пользователя и пароль могут быть указаны в одном из двух вариантов: +Имя пользователя и пароль могут быть указаны в одном из трёх вариантов: 1. С использованием HTTP Basic Authentication. Пример: @@ -187,6 +187,12 @@ $ echo 'SELECT 1' | curl 'http://user:password@localhost:8123/' -d @- $ echo 'SELECT 1' | curl 'http://localhost:8123/?user=user&password=password' -d @- ``` +3. С использованием заголовков ‘X-ClickHouse-User’ и ‘X-ClickHouse-Key’. Пример: + +```bash +$ echo 'SELECT 1' | curl -H 'X-ClickHouse-User: user' -H 'X-ClickHouse-Key: password' 'http://localhost:8123/' -d @- +``` + Если пользователь не задан,то используется `default`. Если пароль не задан, то используется пустой пароль. Также в параметрах URL вы можете указать любые настройки, которые будут использованы для обработки одного запроса, или целые профили настроек. Пример:http://localhost:8123/?profile=web&max_rows_to_read=1000000000&query=SELECT+1 diff --git a/docs/ru/interfaces/mysql.md b/docs/ru/interfaces/mysql.md new file mode 100644 index 00000000000..8f01d5afb44 --- /dev/null +++ b/docs/ru/interfaces/mysql.md @@ -0,0 +1,35 @@ +# MySQL-интерфейс {#mysql_interface} + +ClickHouse поддерживает взаимодействие по протоколу MySQL. Данная функция включается настройкой [mysql_port](../operations/server_settings/settings.md#server_settings-mysql_port) в конфигурационном файле: +```xml +9004 +``` + +Пример подключения с помощью стандартного клиента mysql: +```bash +$ mysql --protocol tcp -u default -P 9004 +``` + +Вывод в случае успешного подключения: +```text +Welcome to the MySQL monitor. Commands end with ; or \g. +Your MySQL connection id is 4 +Server version: 20.2.1.1-ClickHouse + +Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. + +Oracle is a registered trademark of Oracle Corporation and/or its +affiliates. Other names may be trademarks of their respective +owners. + +Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. + +mysql> +``` + +Для совместимости со всеми клиентами рекомендуется задавать пароль пользователя в конфигурационном файле с помощью двойного хэша [SHA1](../operations/settings/settings_users.md#password_double_sha1_hex). +В случае указания пароля с помощью [SHA256](../operations/settings/settings_users.md#password_sha256_hex) некоторые клиенты не смогут пройти аутентификацию (mysqljs и старые версии стандартного клиента mysql). + +Ограничения: +- не поддерживаются подготовленные запросы +- некоторые типы данных отправляются как строки diff --git a/docs/ru/operations/server_settings/settings.md b/docs/ru/operations/server_settings/settings.md index 9072b2a6376..de0cf7d2a41 100644 --- a/docs/ru/operations/server_settings/settings.md +++ b/docs/ru/operations/server_settings/settings.md @@ -661,6 +661,16 @@ TCP порт для защищённого обмена данными с кли 9440 ``` +## mysql_port {#server_settings-mysql_port} + +Порт для взаимодействия с клиентами по протоколу MySQL. + +Пример + +```xml +9004 +``` + ## tmp_path Путь ко временным данным для обработки больших запросов. diff --git a/docs/ru/operations/settings/settings_users.md b/docs/ru/operations/settings/settings_users.md index dfa3e40c660..11f7d925671 100644 --- a/docs/ru/operations/settings/settings_users.md +++ b/docs/ru/operations/settings/settings_users.md @@ -39,6 +39,8 @@ Например, `qwerty`. Пароль можно оставить пустым. + + - Чтобы назначить пароль в виде SHA256, поместите хэш в элемент `password_sha256_hex`. Например, `65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5`. @@ -51,6 +53,20 @@ Первая строка результата — пароль. Вторая строка — соответствующий ему хэш SHA256. + + +- Для совместимости с клиентами MySQL, пароль можно задать с помощью двойного хэша SHA1, поместив его в элемент `password_double_sha1_hex`. + + Например, `08b4a0f1de6ad37da17359e592c8d74788a83eb0`. + + Пример создания пароля в командной строке: + + ``` + PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | openssl dgst -sha1 -binary | openssl dgst -sha1 + ``` + + Первая строка результата — пароль. Вторая строка — соответствующий ему двойной хэш SHA1. + ### user_name/networks Список сетей, из которых пользователь может подключиться к серверу ClickHouse. diff --git a/docs/ru/query_language/functions/rounding_functions.md b/docs/ru/query_language/functions/rounding_functions.md index 61bf6d94419..8f5eeca5dc8 100644 --- a/docs/ru/query_language/functions/rounding_functions.md +++ b/docs/ru/query_language/functions/rounding_functions.md @@ -75,6 +75,91 @@ round(3.55, 1) = 3.6 round(3.65, 1) = 3.6 ``` +**См. также** + +- [roundBankers](#roundbankers) + +## roundBankers {#roundbankers} + +Округляет число до указанного десятичного разряда. + +- Если округляемое число равноудалено от соседних чисел, то используется банковское округление. + + Банковское округление (англ. banker's rounding) — метод округления дробных чисел. Если округляемое число равноудалёно от соседних чисел, то оно округляется до ближайшего чётного числа. К примеру, 3,5 округляется до 4, а 2,5 до 2. + +- В других случаях функция округляет к ближайшему целому. + +Банковское округление позволяет уменьшить влияние округления чисел на результат суммирования или вычитания этих чисел. + +Пример суммирования чисел 1.5, 2.5, 3.5 и 4.5 с различным округлением: + +- Без округления: 1.5 + 2.5 + 3.5 + 4.5 = 12. +- Банковское округление: 2 + 2 + 4 + 4 = 12. +- Округление до ближайшего целого: 2 + 3 + 4 + 5 = 14. + + +**Синтаксис** + +```sql +roundBankers(expression [, decimal_places]) +``` + +**Параметры** + +- `expression` — Число для округления. Может быть любым [выражением](../syntax.md#syntax-expressions), возвращающим числовой [тип данных](../../data_types/index.md#data_types). +- `decimal-places` — Десятичный разряд. Целое число. + - `decimal-places > 0` — Функция округляет значение выражения до ближайшего чётного числа на соответствующей позиции справа от запятой. Например, `roundBankers(3.55, 1) = 3.6`. + - `decimal-places < 0` — Функция округляет значение выражения до ближайшего чётного числа на соответствующей позиции слева от запятой. Например, `roundBankers(24.55, -1) = 20`. + - `decimal-places = 0` — Функция округляет значение до целого. В этом случае аргумент можно не передавать. Например, `roundBankers(2.5) = 2`. + + +**Возвращаемое значение** + +Округлённое значение по методу банковского округления. + +**Пример использования** + +Запрос: + +```sql + SELECT number / 2 AS x, roundBankers(x, 0) AS b fROM system.numbers limit 10 +``` + +Результат: + +```text +┌───x─┬─b─┐ +│ 0 │ 0 │ +│ 0.5 │ 0 │ +│ 1 │ 1 │ +│ 1.5 │ 2 │ +│ 2 │ 2 │ +│ 2.5 │ 2 │ +│ 3 │ 3 │ +│ 3.5 │ 4 │ +│ 4 │ 4 │ +│ 4.5 │ 4 │ +└─────┴───┘ +``` + + +**Примеры банковского округления** + +```text +roundBankers(0.4) = 0 +roundBankers(-3.5) = -4 +roundBankers(4.5) = 4 +roundBankers(3.55, 1) = 3.6 +roundBankers(3.65, 1) = 3.6 +roundBankers(10.35, 1) = 10.4 +roundBankers(10.755, 2) = 11,76 +``` + +**См. также** + +- [round](#rounding_functions-round) + + ## roundToExp2(num) Принимает число. Если число меньше единицы - возвращает 0. Иначе округляет число вниз до ближайшей (целой неотрицательной) степени двух. diff --git a/docs/ru/query_language/select.md b/docs/ru/query_language/select.md index fb659e4355f..9bc5c7a4c04 100644 --- a/docs/ru/query_language/select.md +++ b/docs/ru/query_language/select.md @@ -1147,7 +1147,7 @@ SELECT CounterID, 2 AS table, sum(Sign) AS c Запросы - части `UNION ALL` нельзя заключить в скобки. `ORDER BY` и `LIMIT` применяются к отдельным запросам, а не к общему результату. Если вам нужно применить какое-либо преобразование к общему результату, то вы можете разместить все запросы с `UNION ALL` в подзапросе в секции `FROM`. -### Секция INTO OUTFILE +### Секция INTO OUTFILE {#into-outfile-clause} При указании `INTO OUTFILE filename` (где filename - строковый литерал), результат запроса будет сохранён в файл filename. В отличие от MySQL, файл создаётся на стороне клиента. Если файл с таким именем уже существует, это приведёт к ошибке. @@ -1155,7 +1155,7 @@ SELECT CounterID, 2 AS table, sum(Sign) AS c Формат вывода по умолчанию - TabSeparated, как и в не интерактивном режиме клиента командной строки. -### Секция FORMAT +### Секция FORMAT {#format-clause} При указании FORMAT format вы можете получить данные в любом указанном формате. Это может использоваться для удобства или для создания дампов. diff --git a/docs/toc_en.yml b/docs/toc_en.yml index 604aca5d18e..f5bbea39945 100644 --- a/docs/toc_en.yml +++ b/docs/toc_en.yml @@ -25,6 +25,7 @@ nav: - 'Command-Line Client': 'interfaces/cli.md' - 'Native Interface (TCP)': 'interfaces/tcp.md' - 'HTTP Interface': 'interfaces/http.md' + - 'MySQL Interface': 'interfaces/mysql.md' - 'Input and Output Formats': 'interfaces/formats.md' - 'JDBC Driver': 'interfaces/jdbc.md' - 'ODBC Driver': 'interfaces/odbc.md' diff --git a/docs/toc_ru.yml b/docs/toc_ru.yml index dc6a0d9227c..cd422773282 100644 --- a/docs/toc_ru.yml +++ b/docs/toc_ru.yml @@ -26,6 +26,7 @@ nav: - 'Клиент командной строки': 'interfaces/cli.md' - 'Нативный интерфейс (TCP)': 'interfaces/tcp.md' - 'HTTP-интерфейс': 'interfaces/http.md' + - 'MySQL-интерфейс': 'interfaces/mysql.md' - 'Форматы входных и выходных данных': 'interfaces/formats.md' - 'JDBC-драйвер': 'interfaces/jdbc.md' - 'ODBC-драйвер': 'interfaces/odbc.md' diff --git a/docs/zh/development/build.md b/docs/zh/development/build.md index 3a3cdfd1b12..5b3117ff39e 100644 --- a/docs/zh/development/build.md +++ b/docs/zh/development/build.md @@ -59,12 +59,6 @@ export CC=gcc-9 export CXX=g++-9 ``` -## 安装所需的工具依赖库 - -```bash -sudo apt-get install libreadline-dev -``` - ## 拉取 ClickHouse 源码 ```bash diff --git a/docs/zh/development/build_osx.md b/docs/zh/development/build_osx.md index 449586b17cc..50085fb9f78 100644 --- a/docs/zh/development/build_osx.md +++ b/docs/zh/development/build_osx.md @@ -12,7 +12,7 @@ ClickHouse 支持在 Mac OS X 10.12 版本中编译。若您在用更早的操 ## 安装编译器,工具库 ```bash -brew install cmake ninja gcc icu4c mariadb-connector-c openssl libtool gettext readline +brew install cmake ninja gcc icu4c mariadb-connector-c openssl libtool gettext ``` ## 拉取 ClickHouse 源码 diff --git a/docs/zh/development/developer_instruction.md b/docs/zh/development/developer_instruction.md index 3f257d5a58e..f78a9f422b9 100644 --- a/docs/zh/development/developer_instruction.md +++ b/docs/zh/development/developer_instruction.md @@ -105,14 +105,6 @@ brew install cmake ninja ClickHouse使用多个外部库进行构建。大多数外部库不需要单独安装,而是和ClickHouse一起在子模块中构建。可以查看`contrib`中罗列的清单。 -有一些库不是由源构建的,而是由系统提供,例如:Readline,也建议安装。 - -Ubuntu: `sudo apt install libreadline-dev` - -Mac OS X: `brew install readline` - -但是,这些库本身都是可选的,ClickHouse即便没有它们也可以构建。ICU用于支持`ORDER BY`中的`COLLATE`(例如,对土耳其字母进行排序)。Readline用于在clickhouse-client中更便捷的指令输入。 - # C++ 编译器 diff --git a/docs/zh/interfaces/cli.md b/docs/zh/interfaces/cli.md index bc9be960c48..8fd3ea7cf18 100644 --- a/docs/zh/interfaces/cli.md +++ b/docs/zh/interfaces/cli.md @@ -45,7 +45,7 @@ cat file.csv | clickhouse-client --database=test --query="INSERT INTO test FORMA 您可以指定 `\G` 来替代分号或者在分号后面,这表示 `Vertical` 的格式。在这种格式下,每一个值都会打印在不同的行中,这种方式对于宽表来说很方便。这个不常见的特性是为了兼容 MySQL 命令而加的。 -命令行客户端是基于 `readline` 库(`history` 库或者 `libedit` 库, 或不基于其他库, 这取决于客户端是如何编译的)。换句话说,它可以使用我们熟悉的快捷键方式来操作以及保留历史命令。 +命令行客户端是基于 `replxx`。换句话说,它可以使用我们熟悉的快捷键方式来操作以及保留历史命令。 历史命令会写入在 `~/.clickhouse-client-history` 中。 默认情况下,输出的格式是 `PrettyCompact`。您可以通过 FORMAT 设置根据不同查询来修改格式,或者通过在查询末尾指定 `\G` 字符,或通过在命令行中使用 `--format` or `--vertical` 参数,或使用客户端的配置文件。 diff --git a/libs/libcommon/CMakeLists.txt b/libs/libcommon/CMakeLists.txt index 878fcf5585d..312fcc48b13 100644 --- a/libs/libcommon/CMakeLists.txt +++ b/libs/libcommon/CMakeLists.txt @@ -11,46 +11,48 @@ if (DEFINED APPLE_HAVE_CLOCK_GETTIME) endif () add_library (common + src/argsToConfig.cpp + src/coverage.cpp src/DateLUT.cpp src/DateLUTImpl.cpp - src/preciseExp10.c - src/shift10.cpp - src/mremap.cpp - src/JSON.cpp - src/getMemoryAmount.cpp src/demangle.cpp - src/setTerminalEcho.cpp + src/getMemoryAmount.cpp src/getThreadNumber.cpp - src/sleep.cpp - src/argsToConfig.cpp + src/JSON.cpp + src/LineReader.cpp + src/mremap.cpp src/phdr_cache.cpp - src/coverage.cpp + src/preciseExp10.c + src/setTerminalEcho.cpp + src/shift10.cpp + src/sleep.cpp - include/common/SimpleCache.h - include/common/Types.h - include/common/DayNum.h + include/common/constexpr_helpers.h + include/common/coverage.h include/common/DateLUT.h include/common/DateLUTImpl.h + include/common/DayNum.h + include/common/demangle.h + include/common/ErrorHandlers.h + include/common/find_symbols.h + include/common/getMemoryAmount.h + include/common/getThreadNumber.h + include/common/JSON.h + include/common/likely.h + include/common/LineReader.h include/common/LocalDate.h include/common/LocalDateTime.h - include/common/ErrorHandlers.h - include/common/preciseExp10.h - include/common/shift10.h - include/common/mremap.h - include/common/likely.h include/common/logger_useful.h - include/common/strong_typedef.h - include/common/JSON.h - include/common/getMemoryAmount.h - include/common/demangle.h - include/common/setTerminalEcho.h - include/common/find_symbols.h - include/common/constexpr_helpers.h - include/common/getThreadNumber.h - include/common/sleep.h - include/common/SimpleCache.h + include/common/mremap.h include/common/phdr_cache.h - include/common/coverage.h + include/common/preciseExp10.h + include/common/setTerminalEcho.h + include/common/shift10.h + include/common/SimpleCache.h + include/common/SimpleCache.h + include/common/sleep.h + include/common/strong_typedef.h + include/common/Types.h include/ext/bit_cast.h include/ext/chrono_io.h @@ -90,6 +92,10 @@ if(CCTZ_LIBRARY) target_link_libraries(common PRIVATE ${CCTZ_LIBRARY}) endif() +if (USE_REPLXX) + target_link_libraries(common PRIVATE replxx) +endif () + target_link_libraries (common PUBLIC ${Poco_Util_LIBRARY} diff --git a/libs/libcommon/include/common/LineReader.h b/libs/libcommon/include/common/LineReader.h new file mode 100644 index 00000000000..120ff76dac6 --- /dev/null +++ b/libs/libcommon/include/common/LineReader.h @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include +#include + +class LineReader +{ +public: + class Suggest + { + protected: + using Words = std::vector; + using WordsRange = std::pair; + + Words words; + std::atomic ready{false}; + + public: + /// Get iterators for the matched range of words if any. + WordsRange getCompletions(const String & prefix, size_t prefix_length) const; + }; + + LineReader(const Suggest * suggest, const String & history_file_path, char extender, char delimiter = 0); /// if delimiter != 0, then it's multiline mode + ~LineReader(); + + /// Reads the whole line until delimiter (in multiline mode) or until the last line without extender. + /// If resulting line is empty, it means the user interrupted the input. + /// Non-empty line is appended to history - without duplication. + /// Typical delimiter is ';' (semicolon) and typical extender is '\' (backslash). + String readLine(const String & first_prompt, const String & second_prompt); + +private: + enum InputStatus + { + ABORT = 0, + RESET_LINE, + INPUT_LINE, + }; + + String input; + String prev_line; + const String history_file_path; + const char extender; + const char delimiter; + + InputStatus readOneLine(const String & prompt); + void addToHistory(const String & line); + + /// Since CMake doesn't impose restrictions on includes between unrelated targets + /// it's possible that we include this file without USE_REPLXX defined. +#ifdef __clang__ + [[maybe_unused]] +#endif + void * impl; +}; diff --git a/libs/libcommon/include/common/Types.h b/libs/libcommon/include/common/Types.h index 5d933f218c1..f499fbad012 100644 --- a/libs/libcommon/include/common/Types.h +++ b/libs/libcommon/include/common/Types.h @@ -1,8 +1,10 @@ #pragma once + +#include #include #include +#include #include -#include using Int8 = int8_t; using Int16 = int16_t; @@ -14,6 +16,8 @@ using UInt16 = uint16_t; using UInt32 = uint32_t; using UInt64 = uint64_t; +using String = std::string; + /// The standard library type traits, such as std::is_arithmetic, with one exception /// (std::common_type), are "set in stone". Attempting to specialize them causes undefined behavior. /// So instead of using the std type_traits, we use our own version which allows extension. @@ -52,4 +56,3 @@ struct is_arithmetic template inline constexpr bool is_arithmetic_v = is_arithmetic::value; - diff --git a/libs/libcommon/include/common/config_common.h.in b/libs/libcommon/include/common/config_common.h.in index 810cf0b87f9..6cee84a5b32 100644 --- a/libs/libcommon/include/common/config_common.h.in +++ b/libs/libcommon/include/common/config_common.h.in @@ -2,10 +2,7 @@ // .h autogenerated by cmake ! -#cmakedefine01 USE_TCMALLOC #cmakedefine01 USE_JEMALLOC -#cmakedefine01 USE_READLINE -#cmakedefine01 USE_LIBEDIT -#cmakedefine01 HAVE_READLINE_HISTORY +#cmakedefine01 USE_REPLXX #cmakedefine01 UNBUNDLED #cmakedefine01 WITH_COVERAGE diff --git a/libs/libcommon/include/common/readline_use.h b/libs/libcommon/include/common/readline_use.h deleted file mode 100644 index 2d9c6d154c1..00000000000 --- a/libs/libcommon/include/common/readline_use.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#if __has_include() -#include -#endif - -/// Different line editing libraries can be used depending on the environment. -#if USE_READLINE - #include - #include -#elif USE_LIBEDIT - #include -#else - #include - #include - #include - inline char * readline(const char * prompt) - { - std::string s; - std::cout << prompt; - std::getline(std::cin, s); - - if (!std::cin.good()) - return nullptr; - return strdup(s.data()); - } - #define add_history(...) do {} while (0) - #define rl_bind_key(...) do {} while (0) -#endif diff --git a/libs/libcommon/src/LineReader.cpp b/libs/libcommon/src/LineReader.cpp new file mode 100644 index 00000000000..eca67698efe --- /dev/null +++ b/libs/libcommon/src/LineReader.cpp @@ -0,0 +1,165 @@ +#include +#include + +#if USE_REPLXX +# include +#endif + +#include +#include + +#include +#include + + +namespace +{ + +/// Trim ending whitespace inplace +void trim(String & s) +{ + s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) { return !std::isspace(ch); }).base(), s.end()); +} + +/// Check if multi-line query is inserted from the paste buffer. +/// Allows delaying the start of query execution until the entirety of query is inserted. +bool hasInputData() +{ + timeval timeout = {0, 0}; + fd_set fds; + FD_ZERO(&fds); + FD_SET(STDIN_FILENO, &fds); + return select(1, &fds, nullptr, nullptr, &timeout) == 1; +} + +constexpr char word_break_characters[] = " \t\n\r\"\\'`@$><=;|&{(."; + +} + +LineReader::Suggest::WordsRange LineReader::Suggest::getCompletions(const String & prefix, size_t prefix_length) const +{ + if (!ready) + return std::make_pair(words.end(), words.end()); + + std::string_view last_word; + + auto last_word_pos = prefix.find_last_of(word_break_characters); + if (std::string::npos == last_word_pos) + last_word = prefix; + else + last_word = std::string_view(prefix).substr(last_word_pos + 1, std::string::npos); + + /// last_word can be empty. + + return std::equal_range( + words.begin(), words.end(), last_word, [prefix_length](std::string_view s, std::string_view prefix_searched) + { + return strncmp(s.data(), prefix_searched.data(), prefix_length) < 0; + }); +} + +LineReader::LineReader(const Suggest * suggest, const String & history_file_path_, char extender_, char delimiter_) + : history_file_path(history_file_path_), extender(extender_), delimiter(delimiter_) +{ +#if USE_REPLXX + impl = new replxx::Replxx; + auto & rx = *(replxx::Replxx*)(impl); + + if (!history_file_path.empty()) + rx.history_load(history_file_path); + + auto callback = [suggest] (const String & context, size_t context_size) + { + auto range = suggest->getCompletions(context, context_size); + return replxx::Replxx::completions_t(range.first, range.second); + }; + + rx.set_completion_callback(callback); + rx.set_complete_on_empty(false); + rx.set_word_break_characters(word_break_characters); +#endif + /// FIXME: check extender != delimiter +} + +LineReader::~LineReader() +{ +#if USE_REPLXX + auto & rx = *(replxx::Replxx*)(impl); + if (!history_file_path.empty()) + rx.history_save(history_file_path); + delete (replxx::Replxx *)impl; +#endif +} + +String LineReader::readLine(const String & first_prompt, const String & second_prompt) +{ + String line; + bool is_multiline = false; + + while (auto status = readOneLine(is_multiline ? second_prompt : first_prompt)) + { + if (status == RESET_LINE) + { + line.clear(); + is_multiline = false; + continue; + } + + if (input.empty()) + continue; + + is_multiline = (input.back() == extender) || (delimiter && input.back() != delimiter) || hasInputData(); + + if (input.back() == extender) + { + input = input.substr(0, input.size() - 1); + trim(input); + if (input.empty()) + continue; + } + + line += (line.empty() ? "" : " ") + input; + + if (!is_multiline) + { + if (line != prev_line) + { + addToHistory(line); + prev_line = line; + } + + return line; + } + } + + return {}; +} + +LineReader::InputStatus LineReader::readOneLine(const String & prompt) +{ + input.clear(); + +#if USE_REPLXX + auto & rx = *(replxx::Replxx*)(impl); + const char* cinput = rx.input(prompt); + if (cinput == nullptr) + return (errno != EAGAIN) ? ABORT : RESET_LINE; + input = cinput; +#else + std::cout << prompt; + std::getline(std::cin, input); + if (!std::cin.good()) + return ABORT; +#endif + + trim(input); + return INPUT_LINE; +} + +void LineReader::addToHistory(const String & line) +{ +#if USE_REPLXX + auto & rx = *(replxx::Replxx*)(impl); + rx.history_add(line); +#endif +} diff --git a/utils/zookeeper-cli/CMakeLists.txt b/utils/zookeeper-cli/CMakeLists.txt index 550d0e855d8..7c14ed605fb 100644 --- a/utils/zookeeper-cli/CMakeLists.txt +++ b/utils/zookeeper-cli/CMakeLists.txt @@ -1,6 +1,3 @@ add_executable(clickhouse-zookeeper-cli zookeeper-cli.cpp) target_link_libraries(clickhouse-zookeeper-cli PRIVATE clickhouse_common_zookeeper ${Poco_Foundation_LIBRARY} ${LINE_EDITING_LIBS}) -if (READLINE_INCLUDE_DIR) - target_include_directories (clickhouse-zookeeper-cli SYSTEM PRIVATE ${READLINE_INCLUDE_DIR}) -endif () INSTALL(TARGETS clickhouse-zookeeper-cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT clickhouse-utils) diff --git a/utils/zookeeper-cli/zookeeper-cli.cpp b/utils/zookeeper-cli/zookeeper-cli.cpp index 6655358f105..5e36ffecdaa 100644 --- a/utils/zookeeper-cli/zookeeper-cli.cpp +++ b/utils/zookeeper-cli/zookeeper-cli.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -69,12 +69,13 @@ int main(int argc, char ** argv) Logger::root().setLevel("trace"); zkutil::ZooKeeper zk(argv[1]); + LineReader lr(nullptr, {}, '\\'); - while (char * line_ = readline(":3 ")) + do { - add_history(line_); - std::string line(line_); - free(line_); + const auto & line = lr.readLine(":3 ", ":3 "); + if (line.empty()) + break; try { @@ -211,6 +212,7 @@ int main(int argc, char ** argv) std::cerr << "KeeperException: " << e.displayText() << std::endl; } } + while (true); } catch (const Coordination::Exception & e) { diff --git a/website/benchmark.html b/website/benchmark.html index 5240cdeeb73..ad8397c1d5b 100644 --- a/website/benchmark.html +++ b/website/benchmark.html @@ -1755,7 +1755,7 @@ var runs = ["first (cold cache)", "second", "third"]; var current_runs = ['0', '1']; try { - var state = JSON.parse(window.location.hash.substring(1)); + var state = JSON.parse(decodeURIComponent(window.location.hash.substring(1))); current_data_size = state[0]; current_systems = state[1]; diff --git a/website/benchmark_hardware.html b/website/benchmark_hardware.html index 4da4d6e2488..307a2232183 100644 --- a/website/benchmark_hardware.html +++ b/website/benchmark_hardware.html @@ -441,7 +441,6 @@ var results = [ { "system": "Xeon Gold 6230, 2 sockets, 40 threads", - "data_size": 100000000, "time": "2020-01-01 00:00:00", "result": [ @@ -493,7 +492,6 @@ var results = { "system": "Yandex Cloud Cascade Lake, 64 vCPU (32 threads), 128 GB RAM, 400 GB SSD", - "data_size": 100000000, "time": "2020-01-11 00:00:00", "result": [ @@ -545,7 +543,6 @@ var results = { "system": "Yandex Cloud Cascade Lake, 64 vCPU (32 threads), 128 GB RAM, 4 TB SSD", - "data_size": 100000000, "time": "2020-01-13 00:00:00", "result": [ @@ -597,7 +594,6 @@ var results = { "system": "Yandex Cloud Cascade Lake, 4 vCPU (2 threads), 16 GB RAM, 30 GB SSD", - "data_size": 100000000, "time": "2020-01-13 00:00:00", "result": [ @@ -649,7 +645,6 @@ var results = { "system": "Yandex Cloud Broadwell, 4 vCPU (2 threads), 16 GB RAM, 30 GB SSD", - "data_size": 100000000, "time": "2020-01-14 00:00:00", "result": [ @@ -700,8 +695,7 @@ var results = }, { - "system": "Dell PowerEdge R6415 DX180 AMD EPYC™ 7551P 32-Core Naples (Zen), 128 GB RAM, 2x SSD 960 GB RAID 1", - "data_size": 100000000, + "system": "Dell PowerEdge R6415 DX180 AMD EPYC™ 7551P 32-Core Naples (Zen), 128 GB RAM, 2x SSD 960 GB RAID-1", "time": "2020-01-13 00:00:00", "result": [ @@ -752,8 +746,7 @@ var results = }, { - "system": "Dell PowerEdge R640 DX292 2x Xeon SP Gold 16-Core 2.10GHz, 196 GB RAM, 2x SSD 960 GB RAID 1", - "data_size": 100000000, + "system": "Dell PowerEdge R640 DX292 2x Xeon SP Gold 16-Core 2.10GHz, 196 GB RAM, 2x SSD 960 GB RAID-1", "time": "2020-01-13 00:00:00", "result": [ @@ -805,7 +798,6 @@ var results = { "system": "E5-2650 v2 @ 2.60GHz, 2 sockets, 16 threads, 8xHDD RAID-5", - "data_size": 100000000, "time": "2020-01-12 00:00:00", "result": [ @@ -857,7 +849,6 @@ var results = { "system": "Time4vps.eu VPS (KVM) Linux Ubuntu 4 Core (Skylake) 16GB RAM 160GB Disk", - "data_size": 100000000, "time": "2020-01-13 00:00:00", "result": [ @@ -909,7 +900,6 @@ var results = { "system": "Lenovo B580 Laptop (i5-3210M)", - "data_size": 100000000, "time": "2020-01-11 00:00:00", "result": [ @@ -961,7 +951,6 @@ var results = { "system": "Dell PowerEdge R730xd, 2 socket 10 cores E5-2640 v4, HW RAID5 3TBx12 SATA", - "data_size": 100000000, "time": "2020-01-14 00:00:00", "result": [ @@ -1013,7 +1002,6 @@ var results = { "system": "Yandex Managed ClickHouse, s3.3xlarge, Cascade Lake 32 vCPU, 128 GB RAM, 1 TB local SSD", - "data_size": 100000000, "time": "2020-01-14 00:00:00", "result": [ @@ -1065,7 +1053,6 @@ var results = { "system": "Yandex Managed ClickHouse, s3.3xlarge, Cascade Lake 32 vCPU, 128 GB RAM, 12.5 TB local HDD", - "data_size": 100000000, "time": "2020-01-14 00:00:00", "result": [ @@ -1117,7 +1104,6 @@ var results = { "system": "Dell R530, 128GB DDR4, 2x480 GB SATA SSD, Perc H730 RAID-1", - "data_size": 100000000, "time": "2020-01-14 00:00:00", "result": [ @@ -1169,7 +1155,6 @@ var results = { "system": "Dell R530, 128GB DDR4, 6x2TB SATA 3.5 HDD, Perc H730 RAID-10", - "data_size": 100000000, "time": "2020-01-14 00:00:00", "result": [ @@ -1221,7 +1206,6 @@ var results = { "system": "Xeon 2176G, 64GB RAM, 2xSSD 960GB (SAMSUNG MZQLB960HAJR-00007), ZFS RAID-1", - "data_size": 100000000, "time": "2020-01-14 00:00:00", "result": [ @@ -1273,7 +1257,6 @@ var results = { "system": "Azure DS3v2 4vcpu 14GB RAM 1TB Standard SSD", - "data_size": 100000000, "time": "2020-01-15 00:00:00", "result": [ @@ -1325,7 +1308,6 @@ var results = { "system": "Azure DS3v2 4vcpu 14GB RAM 1TB Premium SSD", - "data_size": 100000000, "time": "2020-01-15 00:00:00", "result": [ @@ -1377,7 +1359,6 @@ var results = { "system": "AWS i3.8xlarge 32vCPU 244GiB 4x1900 NVMe SSD", - "data_size": 100000000, "time": "2020-01-15 00:00:00", "result": [ @@ -1429,7 +1410,6 @@ var results = { "system": "AWS m5d.24xlarge 96vCPU 384GiB 4x900 NVMe SSD", - "data_size": 100000000, "time": "2020-01-15 00:00:00", "result": [ @@ -1481,7 +1461,6 @@ var results = { "system": "AWS i3en.24xlarge 96vCPU 768GiB 8x7500 NVMe SSD", - "data_size": 100000000, "time": "2020-01-15 00:00:00", "result": [ @@ -1530,19 +1509,216 @@ var results = [0.012, 0.006, 0.007] ] }, + + { + "system": "Huawei TaiShan 2280 v2 (AArch64) 64 core (2-die), one physical HDD", + "time": "2020-01-15 00:00:00", + "result": + [ +[0.356, 0.002, 0.002], +[0.333, 0.018, 0.017], +[0.608, 0.021, 0.021], +[1.885, 0.032, 0.032], +[0.598, 0.099, 0.097], +[2.884, 0.165, 0.167], +[0.356, 0.016, 0.014], +[0.349, 0.015, 0.015], +[0.981, 0.283, 0.296], +[0.783, 0.326, 0.328], +[0.580, 0.135, 0.136], +[0.511, 0.142, 0.142], +[1.060, 0.434, 0.438], +[1.069, 0.569, 0.566], +[1.116, 0.479, 0.479], +[0.825, 0.478, 0.486], +[1.899, 1.574, 1.590], +[1.260, 0.874, 0.849], +[5.456, 2.869, 2.903], +[0.418, 0.037, 0.034], +[19.336, 0.478, 0.494], +[22.442, 0.595, 0.595], +[45.958, 8.735, 1.363], +[41.321, 0.675, 0.706], +[6.074, 0.167, 0.159], +[0.925, 0.133, 0.133], +[1.151, 0.153, 0.152], +[19.627, 0.607, 0.622], +[16.496, 0.792, 0.787], +[1.770, 2.045, 1.242], +[4.827, 0.471, 0.466], +[7.695, 0.701, 0.647], +[5.246, 4.741, 4.676], +[20.496, 2.676, 2.628], +[20.338, 2.559, 2.557], +[1.696, 0.701, 0.724], +[0.665, 0.294, 0.302], +[0.402, 0.140, 0.137], +[0.366, 0.082, 0.086], +[0.867, 0.575, 0.552], +[0.334, 0.025, 0.025], +[0.333, 0.023, 0.022], +[0.340, 0.007, 0.007] + ] + }, + + { + "system": "AWS m5ad.24xlarge 96vCPU 384GiB 4x900 NVMe SSD, AMD EPYC 7000 series 2.5 GHz", + "time": "2020-01-17 00:00:00", + "result": + [ +[0.013, 0.002, 0.002], +[0.055, 0.020, 0.025], +[0.054, 0.027, 0.026], +[0.154, 0.035, 0.035], +[0.221, 0.117, 0.118], +[0.325, 0.171, 0.166], +[0.042, 0.021, 0.017], +[0.025, 0.017, 0.018], +[0.353, 0.253, 0.253], +[0.477, 0.610, 0.720], +[0.257, 0.154, 0.139], +[0.251, 0.130, 0.114], +[0.513, 0.293, 0.286], +[0.618, 0.360, 0.350], +[0.468, 0.336, 0.329], +[0.390, 0.333, 0.411], +[1.112, 0.936, 1.497], +[2.434, 1.350, 0.886], +[2.590, 2.069, 2.331], +[0.160, 0.048, 0.036], +[1.638, 0.334, 0.312], +[1.841, 0.423, 0.373], +[3.673, 1.122, 1.078], +[3.808, 0.912, 0.494], +[0.480, 0.112, 0.120], +[0.248, 0.107, 0.099], +[0.470, 0.118, 0.114], +[1.648, 0.544, 0.469], +[1.418, 0.583, 0.624], +[0.966, 1.231, 0.999], +[0.539, 0.311, 0.370], +[1.159, 0.712, 0.716], +[3.755, 2.772, 2.973], +[2.748, 2.033, 2.242], +[2.842, 2.150, 2.019], +[0.784, 0.616, 0.641], +[0.304, 0.273, 0.235], +[0.106, 0.086, 0.093], +[0.117, 0.073, 0.075], +[0.604, 0.453, 0.502], +[0.050, 0.036, 0.034], +[0.043, 0.023, 0.027], +[0.013, 0.008, 0.007] + ] + }, + + { + "system": "Lenovo Thinkpad X1 Carbon 6th Gen i7-8550U CPU @ 1.80GHz 4 threads, 16 GiB", + "time": "2020-01-18 00:00:00", + "result": + [ +[0.006, 0.002, 0.002], +[0.031, 0.019, 0.020], +[0.082, 0.078, 0.080], +[0.157, 0.093, 0.092], +[0.274, 0.214, 0.206], +[0.601, 0.513, 0.513], +[0.038, 0.045, 0.041], +[0.023, 0.018, 0.018], +[1.394, 1.378, 1.323], +[1.567, 1.496, 1.483], +[0.406, 0.328, 0.327], +[0.468, 0.414, 0.397], +[1.846, 1.753, 1.737], +[2.492, 2.423, 2.404], +[2.136, 2.064, 2.078], +[2.038, 1.971, 1.971], +[5.794, 5.679, 5.708], +[3.430, 3.498, 3.356], +[11.946, 11.738, 11.700], +[0.158, 0.105, 0.091], +[2.151, 1.551, 1.593], +[2.581, 1.990, 1.985], +[6.101, 5.390, 5.320], +[3.528, 2.341, 2.322], +[0.772, 0.699, 0.701], +[0.606, 0.583, 0.587], +[0.877, 0.723, 0.728], +[2.398, 1.916, 1.924], +[3.634, 3.272, 3.247], +[4.102, 4.082, 4.078], +[1.885, 1.784, 1.741], +[2.994, 2.691, 2.707], +[19.060, 18.852, 18.929], +[8.745, 8.476, 8.553], +[8.685, 8.406, 8.946], +[3.416, 3.426, 3.397], +[0.238, 0.234, 0.210], +[0.080, 0.071, 0.072], +[0.078, 0.066, 0.066], +[0.470, 0.407, 0.396], +[0.034, 0.030, 0.029], +[0.025, 0.021, 0.021], +[0.010, 0.007, 0.006] + ] + }, + + { + "system": "E5645 @ 2.40GHz, 2 sockets, 12 threads, 96 GiB, 14 x 2TB HDD RAID-10", + "time": "2020-01-18 00:00:00", + "result": + [ +[0.061, 0.003, 0.003], +[0.203, 0.026, 0.019], +[0.231, 0.056, 0.060], +[0.533, 0.080, 0.099], +[0.458, 0.202, 0.213], +[0.723, 0.468, 0.411], +[0.143, 0.034, 0.029], +[0.117, 0.025, 0.023], +[1.033, 0.810, 0.745], +[1.165, 0.916, 0.898], +[0.514, 0.249, 0.297], +[0.600, 0.343, 0.385], +[1.294, 1.156, 1.221], +[1.859, 1.459, 1.384], +[1.627, 1.349, 1.346], +[1.414, 1.269, 1.306], +[3.798, 3.774, 3.631], +[2.177, 2.054, 2.016], +[7.002, 6.187, 6.263], +[0.461, 0.081, 0.116], +[3.860, 1.296, 1.330], +[4.705, 1.587, 1.503], +[9.533, 3.887, 3.564], +[11.468, 1.932, 1.712], +[1.362, 0.451, 0.403], +[0.648, 0.374, 0.414], +[1.195, 0.437, 0.418], +[4.187, 1.686, 1.474], +[3.289, 2.146, 2.159], +[3.919, 4.242, 4.208], +[1.673, 1.084, 1.040], +[3.264, 1.496, 1.629], +[8.883, 8.965, 9.027], +[5.813, 5.225, 5.365], +[5.874, 5.376, 5.353], +[2.053, 1.910, 1.951], +[0.478, 0.324, 0.325], +[0.206, 0.132, 0.124], +[0.222, 0.105, 0.111], +[0.699, 0.599, 0.563], +[0.213, 0.041, 0.040], +[0.133, 0.032, 0.040], +[0.062, 0.010, 0.010] + ] + }, ];