diff --git a/.gitmodules b/.gitmodules index 7f5d1307a6e..c05da0c9ff9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -157,6 +157,14 @@ [submodule "contrib/openldap"] path = contrib/openldap url = https://github.com/openldap/openldap.git +[submodule "contrib/cassandra"] + path = contrib/cassandra + url = https://github.com/ClickHouse-Extras/cpp-driver.git + branch = clickhouse +[submodule "contrib/libuv"] + path = contrib/libuv + url = https://github.com/ClickHouse-Extras/libuv.git + branch = clickhouse [submodule "contrib/fmtlib"] path = contrib/fmtlib url = https://github.com/fmtlib/fmt.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 53dfd1df1cb..4683bf8dec1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -327,20 +327,16 @@ message (STATUS "Building for: ${CMAKE_SYSTEM} ${CMAKE_SYSTEM_PROCESSOR} ${CMAKE include (GNUInstallDirs) include (cmake/contrib_finder.cmake) -include (cmake/lib_name.cmake) find_contrib_lib(double-conversion) # Must be before parquet include (cmake/find/ssl.cmake) include (cmake/find/ldap.cmake) # after ssl include (cmake/find/icu.cmake) -include (cmake/find/boost.cmake) include (cmake/find/zlib.cmake) include (cmake/find/zstd.cmake) include (cmake/find/ltdl.cmake) # for odbc include (cmake/find/termcap.cmake) # openssl, zlib before poco -include (cmake/find/lz4.cmake) -include (cmake/find/xxhash.cmake) include (cmake/find/sparsehash.cmake) include (cmake/find/re2.cmake) include (cmake/find/libgsasl.cmake) @@ -358,17 +354,16 @@ include (cmake/find/hdfs3.cmake) # uses protobuf include (cmake/find/s3.cmake) include (cmake/find/base64.cmake) include (cmake/find/parquet.cmake) -include (cmake/find/hyperscan.cmake) include (cmake/find/simdjson.cmake) include (cmake/find/rapidjson.cmake) include (cmake/find/fastops.cmake) include (cmake/find/orc.cmake) include (cmake/find/avro.cmake) include (cmake/find/msgpack.cmake) +include (cmake/find/cassandra.cmake) find_contrib_lib(cityhash) find_contrib_lib(farmhash) -find_contrib_lib(metrohash) find_contrib_lib(btrie) if (ENABLE_TESTS) diff --git a/README.md b/README.md index 955f9d1a5d1..2008e5d2750 100644 --- a/README.md +++ b/README.md @@ -10,10 +10,12 @@ ClickHouse is an open-source column-oriented database management system that all * [YouTube channel](https://www.youtube.com/c/ClickHouseDB) has a lot of content about ClickHouse in video format. * [Slack](https://join.slack.com/t/clickhousedb/shared_invite/zt-d2zxkf9e-XyxDa_ucfPxzuH4SJIm~Ng) and [Telegram](https://telegram.me/clickhouse_en) allow to chat with ClickHouse users in real-time. * [Blog](https://clickhouse.yandex/blog/en/) contains various ClickHouse-related articles, as well as announces and reports about events. +* [Yandex.Messenger channel](https://yandex.ru/chat/#/join/20e380d9-c7be-4123-ab06-e95fb946975e) shares announcements and useful links in Russian. * [Contacts](https://clickhouse.tech/#contacts) can help to get your questions answered if there are any. * You can also [fill this form](https://clickhouse.tech/#meet) to meet Yandex ClickHouse team in person. ## Upcoming Events +* [ClickHouse Online Meetup (in Russian)](https://events.yandex.ru/events/click-house-onlajn-vs-18-06-2020) on June 18, 2020. * [ClickHouse Workshop in Novosibirsk](https://2020.codefest.ru/lecture/1628) on TBD date. * [Yandex C++ Open-Source Sprints in Moscow](https://events.yandex.ru/events/otkrytyj-kod-v-yandek-28-03-2020) on TBD date. diff --git a/base/common/CMakeLists.txt b/base/common/CMakeLists.txt index 9b827cdb468..074f73b158b 100644 --- a/base/common/CMakeLists.txt +++ b/base/common/CMakeLists.txt @@ -16,6 +16,7 @@ set (SRCS shift10.cpp sleep.cpp terminalColors.cpp + errnoToString.cpp ) if (ENABLE_REPLXX) @@ -43,10 +44,6 @@ endif() target_include_directories(common PUBLIC .. ${CMAKE_CURRENT_BINARY_DIR}/..) -if (NOT USE_INTERNAL_BOOST_LIBRARY) - target_include_directories (common SYSTEM BEFORE PUBLIC ${Boost_INCLUDE_DIRS}) -endif () - # Allow explicit fallback to readline if (NOT ENABLE_REPLXX AND ENABLE_READLINE) message (STATUS "Attempt to fallback to readline explicitly") @@ -72,7 +69,8 @@ endif () target_link_libraries (common PUBLIC ${CITYHASH_LIBRARIES} - ${Boost_SYSTEM_LIBRARY} + boost::headers_only + boost::system FastMemcpy Poco::Net Poco::Net::SSL diff --git a/base/common/LineReader.cpp b/base/common/LineReader.cpp index 0d06e5ef225..dd2e09b0393 100644 --- a/base/common/LineReader.cpp +++ b/base/common/LineReader.cpp @@ -67,8 +67,8 @@ LineReader::Suggest::WordsRange LineReader::Suggest::getCompletions(const String }); } -LineReader::LineReader(const String & history_file_path_, char extender_, char delimiter_) - : history_file_path(history_file_path_), extender(extender_), delimiter(delimiter_) +LineReader::LineReader(const String & history_file_path_, bool multiline_, Patterns extenders_, Patterns delimiters_) + : history_file_path(history_file_path_), multiline(multiline_), extenders(std::move(extenders_)), delimiters(std::move(delimiters_)) { /// FIXME: check extender != delimiter } @@ -76,38 +76,60 @@ LineReader::LineReader(const String & history_file_path_, char extender_, char d String LineReader::readLine(const String & first_prompt, const String & second_prompt) { String line; - bool is_multiline = false; + bool need_next_line = false; - while (auto status = readOneLine(is_multiline ? second_prompt : first_prompt)) + while (auto status = readOneLine(need_next_line ? second_prompt : first_prompt)) { if (status == RESET_LINE) { line.clear(); - is_multiline = false; + need_next_line = false; continue; } if (input.empty()) { - if (!line.empty() && !delimiter && !hasInputData()) + if (!line.empty() && !multiline && !hasInputData()) break; else continue; } - is_multiline = (input.back() == extender) || (delimiter && input.back() != delimiter) || hasInputData(); - - if (input.back() == extender) +#if !defined(ARCADIA_BUILD) /// C++20 + const char * has_extender = nullptr; + for (const auto * extender : extenders) { - input = input.substr(0, input.size() - 1); + if (input.ends_with(extender)) + { + has_extender = extender; + break; + } + } + + const char * has_delimiter = nullptr; + for (const auto * delimiter : delimiters) + { + if (input.ends_with(delimiter)) + { + has_delimiter = delimiter; + break; + } + } + + need_next_line = has_extender || (multiline && !has_delimiter) || hasInputData(); + + if (has_extender) + { + input.resize(input.size() - strlen(has_extender)); trim(input); if (input.empty()) continue; } +#endif line += (line.empty() ? "" : " ") + input; - if (!is_multiline) + if (!need_next_line) break; } diff --git a/base/common/LineReader.h b/base/common/LineReader.h index 3e64bc858ad..f31459078ab 100644 --- a/base/common/LineReader.h +++ b/base/common/LineReader.h @@ -21,7 +21,9 @@ public: WordsRange getCompletions(const String & prefix, size_t prefix_length) const; }; - LineReader(const String & history_file_path, char extender, char delimiter = 0); /// if delimiter != 0, then it's multiline mode + using Patterns = std::vector; + + LineReader(const String & history_file_path, bool multiline, Patterns extenders, Patterns delimiters); virtual ~LineReader() {} /// Reads the whole line until delimiter (in multiline mode) or until the last line without extender. @@ -51,8 +53,10 @@ protected: String input; private: - const char extender; - const char delimiter; + bool multiline; + + Patterns extenders; + Patterns delimiters; String prev_line; diff --git a/base/common/ReadlineLineReader.cpp b/base/common/ReadlineLineReader.cpp index ee9a37d2168..d52ac0e9769 100644 --- a/base/common/ReadlineLineReader.cpp +++ b/base/common/ReadlineLineReader.cpp @@ -56,8 +56,9 @@ static char * generate(const char * text, int state) return nextMatch(); }; -ReadlineLineReader::ReadlineLineReader(const Suggest & suggest_, const String & history_file_path_, char extender_, char delimiter_) - : LineReader(history_file_path_, extender_, delimiter_) +ReadlineLineReader::ReadlineLineReader( + const Suggest & suggest_, const String & history_file_path_, bool multiline_, Patterns extenders_, Patterns delimiters_) + : LineReader(history_file_path_, multiline_, std::move(extenders_), std::move(delimiters_)) { suggest = &suggest_; diff --git a/base/common/ReadlineLineReader.h b/base/common/ReadlineLineReader.h index 395ae56c724..95bd23b4634 100644 --- a/base/common/ReadlineLineReader.h +++ b/base/common/ReadlineLineReader.h @@ -8,7 +8,7 @@ class ReadlineLineReader : public LineReader { public: - ReadlineLineReader(const Suggest & suggest, const String & history_file_path, char extender, char delimiter = 0); + ReadlineLineReader(const Suggest & suggest, const String & history_file_path, bool multiline, Patterns extenders_, Patterns delimiters_); ~ReadlineLineReader() override; void enableBracketedPaste() override; diff --git a/base/common/ReplxxLineReader.cpp b/base/common/ReplxxLineReader.cpp index 52c42235f1b..251170ab5c1 100644 --- a/base/common/ReplxxLineReader.cpp +++ b/base/common/ReplxxLineReader.cpp @@ -1,9 +1,11 @@ #include +#include #include #include #include #include +#include namespace { @@ -16,14 +18,42 @@ void trim(String & s) } -ReplxxLineReader::ReplxxLineReader(const Suggest & suggest, const String & history_file_path_, char extender_, char delimiter_) - : LineReader(history_file_path_, extender_, delimiter_) +ReplxxLineReader::ReplxxLineReader( + const Suggest & suggest, + const String & history_file_path_, + bool multiline_, + Patterns extenders_, + Patterns delimiters_, + replxx::Replxx::highlighter_callback_t highlighter_) + : LineReader(history_file_path_, multiline_, std::move(extenders_), std::move(delimiters_)), highlighter(std::move(highlighter_)) { using namespace std::placeholders; using Replxx = replxx::Replxx; if (!history_file_path.empty()) - rx.history_load(history_file_path); + { + history_file_fd = open(history_file_path.c_str(), O_RDWR); + if (history_file_fd < 0) + { + rx.print("Open of history file failed: %s\n", errnoToString(errno).c_str()); + } + else + { + if (flock(history_file_fd, LOCK_SH)) + { + rx.print("Shared lock of history file failed: %s\n", errnoToString(errno).c_str()); + } + else + { + rx.history_load(history_file_path); + + if (flock(history_file_fd, LOCK_UN)) + { + rx.print("Unlock of history file failed: %s\n", errnoToString(errno).c_str()); + } + } + } + } auto callback = [&suggest] (const String & context, size_t context_size) { @@ -35,6 +65,9 @@ ReplxxLineReader::ReplxxLineReader(const Suggest & suggest, const String & histo rx.set_complete_on_empty(false); rx.set_word_break_characters(word_break_characters); + if (highlighter) + rx.set_highlighter_callback(highlighter); + /// By default C-p/C-n binded to COMPLETE_NEXT/COMPLETE_PREV, /// bind C-p/C-n to history-previous/history-next like readline. rx.bind_key(Replxx::KEY::control('N'), [this](char32_t code) { return rx.invoke(Replxx::ACTION::HISTORY_NEXT, code); }); @@ -48,8 +81,8 @@ ReplxxLineReader::ReplxxLineReader(const Suggest & suggest, const String & histo ReplxxLineReader::~ReplxxLineReader() { - if (!history_file_path.empty()) - rx.history_save(history_file_path); + if (close(history_file_fd)) + rx.print("Close of history file failed: %s\n", strerror(errno)); } LineReader::InputStatus ReplxxLineReader::readOneLine(const String & prompt) @@ -67,7 +100,20 @@ LineReader::InputStatus ReplxxLineReader::readOneLine(const String & prompt) void ReplxxLineReader::addToHistory(const String & line) { + // locking history file to prevent from inconsistent concurrent changes + bool locked = false; + if (flock(history_file_fd, LOCK_EX)) + rx.print("Lock of history file failed: %s\n", strerror(errno)); + else + locked = true; + rx.history_add(line); + + // flush changes to the disk + rx.history_save(history_file_path); + + if (locked && 0 != flock(history_file_fd, LOCK_UN)) + rx.print("Unlock of history file failed: %s\n", strerror(errno)); } void ReplxxLineReader::enableBracketedPaste() diff --git a/base/common/ReplxxLineReader.h b/base/common/ReplxxLineReader.h index e7821f54ad3..1fbfd53457b 100644 --- a/base/common/ReplxxLineReader.h +++ b/base/common/ReplxxLineReader.h @@ -4,10 +4,17 @@ #include + class ReplxxLineReader : public LineReader { public: - ReplxxLineReader(const Suggest & suggest, const String & history_file_path, char extender, char delimiter = 0); + ReplxxLineReader( + const Suggest & suggest, + const String & history_file_path, + bool multiline, + Patterns extenders_, + Patterns delimiters_, + replxx::Replxx::highlighter_callback_t highlighter_); ~ReplxxLineReader() override; void enableBracketedPaste() override; @@ -17,4 +24,8 @@ private: void addToHistory(const String & line) override; replxx::Replxx rx; + replxx::Replxx::highlighter_callback_t highlighter; + + // used to call flock() to synchronize multiple clients using same history file + int history_file_fd = -1; }; diff --git a/base/common/errnoToString.cpp b/base/common/errnoToString.cpp new file mode 100644 index 00000000000..cdadba2c615 --- /dev/null +++ b/base/common/errnoToString.cpp @@ -0,0 +1,29 @@ +#include "errnoToString.h" + +#include + + +std::string errnoToString(int code, int the_errno) +{ + const size_t buf_size = 128; + char buf[buf_size]; +#ifndef _GNU_SOURCE + int rc = strerror_r(the_errno, buf, buf_size); +#ifdef __APPLE__ + if (rc != 0 && rc != EINVAL) +#else + if (rc != 0) +#endif + { + std::string tmp = std::to_string(code); + const char * code_str = tmp.c_str(); + const char * unknown_message = "Unknown error "; + strcpy(buf, unknown_message); + strcpy(buf + strlen(unknown_message), code_str); + } + return fmt::format("errno: {}, strerror: {}", the_errno, buf); +#else + (void)code; + return fmt::format("errno: {}, strerror: {}", the_errno, strerror_r(the_errno, buf, sizeof(buf))); +#endif +} diff --git a/base/common/errnoToString.h b/base/common/errnoToString.h new file mode 100644 index 00000000000..fd5f81ec2c7 --- /dev/null +++ b/base/common/errnoToString.h @@ -0,0 +1,6 @@ +#pragma once + +#include +#include + +std::string errnoToString(int code, int the_errno = errno); diff --git a/base/common/strong_typedef.h b/base/common/strong_typedef.h index a46eb415e15..d9850a25c37 100644 --- a/base/common/strong_typedef.h +++ b/base/common/strong_typedef.h @@ -1,6 +1,8 @@ #pragma once +#include #include +#include template struct StrongTypedef diff --git a/base/common/ya.make b/base/common/ya.make index 6e45b0193c5..d40b1f5abfd 100644 --- a/base/common/ya.make +++ b/base/common/ya.make @@ -47,6 +47,7 @@ SRCS( shift10.cpp sleep.cpp terminalColors.cpp + errnoToString.cpp ) END() diff --git a/base/mysqlxx/CMakeLists.txt b/base/mysqlxx/CMakeLists.txt index 702e0197ffb..7d35c1bd31d 100644 --- a/base/mysqlxx/CMakeLists.txt +++ b/base/mysqlxx/CMakeLists.txt @@ -32,10 +32,18 @@ else () endif () endif () -target_link_libraries(mysqlxx PUBLIC common PRIVATE ${MYSQLCLIENT_LIBRARIES} PUBLIC ${Boost_SYSTEM_LIBRARY} PRIVATE ${ZLIB_LIBRARIES}) +target_link_libraries (mysqlxx + PUBLIC + common + PRIVATE + ${MYSQLCLIENT_LIBRARIES} + ${ZLIB_LIBRARIES} +) + if(OPENSSL_LIBRARIES) target_link_libraries(mysqlxx PRIVATE ${OPENSSL_LIBRARIES}) endif() + target_link_libraries(mysqlxx PRIVATE ${PLATFORM_LIBRARIES}) if (NOT USE_INTERNAL_MYSQL_LIBRARY AND OPENSSL_INCLUDE_DIR) diff --git a/cmake/Modules/Findmetrohash.cmake b/cmake/Modules/Findmetrohash.cmake deleted file mode 100644 index c51665795bd..00000000000 --- a/cmake/Modules/Findmetrohash.cmake +++ /dev/null @@ -1,44 +0,0 @@ -# - Try to find metrohash headers and libraries. -# -# Usage of this module as follows: -# -# find_package(metrohash) -# -# Variables used by this module, they can change the default behaviour and need -# to be set before calling find_package: -# -# METROHASH_ROOT_DIR Set this variable to the root installation of -# metrohash if the module has problems finding -# the proper installation path. -# -# Variables defined by this module: -# -# METROHASH_FOUND System has metrohash libs/headers -# METROHASH_LIBRARIES The metrohash library/libraries -# METROHASH_INCLUDE_DIR The location of metrohash headers - -find_path(METROHASH_ROOT_DIR - NAMES include/metrohash.h -) - -find_library(METROHASH_LIBRARIES - NAMES metrohash - PATHS ${METROHASH_ROOT_DIR}/lib ${METROHASH_LIBRARIES_PATHS} -) - -find_path(METROHASH_INCLUDE_DIR - NAMES metrohash.h - PATHS ${METROHASH_ROOT_DIR}/include PATH_SUFFIXES metrohash ${METROHASH_INCLUDE_PATHS} -) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(metrohash DEFAULT_MSG - METROHASH_LIBRARIES - METROHASH_INCLUDE_DIR -) - -mark_as_advanced( - METROHASH_ROOT_DIR - METROHASH_LIBRARIES - METROHASH_INCLUDE_DIR -) diff --git a/cmake/find/boost.cmake b/cmake/find/boost.cmake deleted file mode 100644 index ec10a34d839..00000000000 --- a/cmake/find/boost.cmake +++ /dev/null @@ -1,52 +0,0 @@ -option (USE_INTERNAL_BOOST_LIBRARY "Set to FALSE to use system boost library instead of bundled" ${NOT_UNBUNDLED}) - -# Test random file existing in all package variants -if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/boost/libs/system/src/error_code.cpp") - if(USE_INTERNAL_BOOST_LIBRARY) - message(WARNING "submodules in contrib/boost is missing. to fix try run: \n git submodule update --init --recursive") - endif() - set (USE_INTERNAL_BOOST_LIBRARY 0) - set (MISSING_INTERNAL_BOOST_LIBRARY 1) -endif () - -if (NOT USE_INTERNAL_BOOST_LIBRARY) - set (Boost_USE_STATIC_LIBS ${USE_STATIC_LIBRARIES}) - set (BOOST_ROOT "/usr/local") - find_package (Boost 1.60 COMPONENTS program_options system filesystem thread regex) - # incomplete, no include search, who use it? - if (NOT Boost_FOUND) - # # Try to find manually. - # set (BOOST_PATHS "") - # find_library (Boost_PROGRAM_OPTIONS_LIBRARY boost_program_options PATHS ${BOOST_PATHS}) - # find_library (Boost_SYSTEM_LIBRARY boost_system PATHS ${BOOST_PATHS}) - # find_library (Boost_FILESYSTEM_LIBRARY boost_filesystem PATHS ${BOOST_PATHS}) - # maybe found but incorrect version. - set (Boost_INCLUDE_DIRS "") - set (Boost_SYSTEM_LIBRARY "") - endif () -endif () - -if (NOT Boost_SYSTEM_LIBRARY AND NOT MISSING_INTERNAL_BOOST_LIBRARY) - set (USE_INTERNAL_BOOST_LIBRARY 1) - set (Boost_SYSTEM_LIBRARY boost_system_internal) - set (Boost_PROGRAM_OPTIONS_LIBRARY boost_program_options_internal) - set (Boost_FILESYSTEM_LIBRARY boost_filesystem_internal ${Boost_SYSTEM_LIBRARY}) - set (Boost_IOSTREAMS_LIBRARY boost_iostreams_internal) - set (Boost_REGEX_LIBRARY boost_regex_internal) - - set (Boost_INCLUDE_DIRS) - - set (BOOST_ROOT "${ClickHouse_SOURCE_DIR}/contrib/boost") - - # For boost from github: - file (GLOB Boost_INCLUDE_DIRS_ "${ClickHouse_SOURCE_DIR}/contrib/boost/libs/*/include") - list (APPEND Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS_}) - # numeric has additional level - file (GLOB Boost_INCLUDE_DIRS_ "${ClickHouse_SOURCE_DIR}/contrib/boost/libs/numeric/*/include") - list (APPEND Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIRS_}) - - # For packaged version: - list (APPEND Boost_INCLUDE_DIRS "${ClickHouse_SOURCE_DIR}/contrib/boost") -endif () - -message (STATUS "Using Boost: ${Boost_INCLUDE_DIRS} : ${Boost_PROGRAM_OPTIONS_LIBRARY},${Boost_SYSTEM_LIBRARY},${Boost_FILESYSTEM_LIBRARY},${Boost_IOSTREAMS_LIBRARY},${Boost_REGEX_LIBRARY}") diff --git a/cmake/find/cassandra.cmake b/cmake/find/cassandra.cmake new file mode 100644 index 00000000000..f41e0f645f4 --- /dev/null +++ b/cmake/find/cassandra.cmake @@ -0,0 +1,26 @@ +option(ENABLE_CASSANDRA "Enable Cassandra" ${ENABLE_LIBRARIES}) + +if (ENABLE_CASSANDRA) + if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/libuv") + message (ERROR "submodule contrib/libuv is missing. to fix try run: \n git submodule update --init --recursive") + elseif (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/cassandra") + message (ERROR "submodule contrib/cassandra is missing. to fix try run: \n git submodule update --init --recursive") + else() + set (LIBUV_ROOT_DIR "${ClickHouse_SOURCE_DIR}/contrib/libuv") + set (CASSANDRA_INCLUDE_DIR + "${ClickHouse_SOURCE_DIR}/contrib/cassandra/include/") + if (USE_STATIC_LIBRARIES) + set (LIBUV_LIBRARY uv_a) + set (CASSANDRA_LIBRARY cassandra_static) + else() + set (LIBUV_LIBRARY uv) + set (CASSANDRA_LIBRARY cassandra) + endif() + set (USE_CASSANDRA 1) + set (CASS_ROOT_DIR "${ClickHouse_SOURCE_DIR}/contrib/cassandra") + + endif() +endif() + +message (STATUS "Using cassandra=${USE_CASSANDRA}: ${CASSANDRA_INCLUDE_DIR} : ${CASSANDRA_LIBRARY}") +message (STATUS "Using libuv: ${LIBUV_ROOT_DIR} : ${LIBUV_LIBRARY}") diff --git a/cmake/find/hyperscan.cmake b/cmake/find/hyperscan.cmake deleted file mode 100644 index 039981fce81..00000000000 --- a/cmake/find/hyperscan.cmake +++ /dev/null @@ -1,33 +0,0 @@ -if (HAVE_SSSE3) - option (ENABLE_HYPERSCAN "Enable hyperscan" ${ENABLE_LIBRARIES}) -endif () - -if (ENABLE_HYPERSCAN) - -option (USE_INTERNAL_HYPERSCAN_LIBRARY "Set to FALSE to use system hyperscan instead of the bundled" ${NOT_UNBUNDLED}) - -if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/hyperscan/CMakeLists.txt") - if (USE_INTERNAL_HYPERSCAN_LIBRARY) - message (WARNING "submodule contrib/hyperscan is missing. to fix try run: \n git submodule update --init --recursive") - endif () - set (MISSING_INTERNAL_HYPERSCAN_LIBRARY 1) - set (USE_INTERNAL_HYPERSCAN_LIBRARY 0) -endif () - -if (NOT USE_INTERNAL_HYPERSCAN_LIBRARY) - find_library (HYPERSCAN_LIBRARY hs) - find_path (HYPERSCAN_INCLUDE_DIR NAMES hs/hs.h hs.h PATHS ${HYPERSCAN_INCLUDE_PATHS}) -endif () - -if (HYPERSCAN_LIBRARY AND HYPERSCAN_INCLUDE_DIR) - set (USE_HYPERSCAN 1) -elseif (NOT MISSING_INTERNAL_HYPERSCAN_LIBRARY) - set (HYPERSCAN_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/hyperscan/src) - set (HYPERSCAN_LIBRARY hs) - set (USE_HYPERSCAN 1) - set (USE_INTERNAL_HYPERSCAN_LIBRARY 1) -endif() - -message (STATUS "Using hyperscan=${USE_HYPERSCAN}: ${HYPERSCAN_INCLUDE_DIR} : ${HYPERSCAN_LIBRARY}") - -endif () diff --git a/cmake/find/lz4.cmake b/cmake/find/lz4.cmake deleted file mode 100644 index 5f5e058b53d..00000000000 --- a/cmake/find/lz4.cmake +++ /dev/null @@ -1,23 +0,0 @@ -option (USE_INTERNAL_LZ4_LIBRARY "Set to FALSE to use system lz4 library instead of bundled" ${NOT_UNBUNDLED}) - -if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/lz4/lib/lz4.h") - if (USE_INTERNAL_LZ4_LIBRARY) - message (WARNING "submodule contrib/lz4 is missing. to fix try run: \n git submodule update --init --recursive") - set (USE_INTERNAL_LZ4_LIBRARY 0) - endif () - set (MISSING_INTERNAL_LZ4_LIBRARY 1) -endif () - -if (NOT USE_INTERNAL_LZ4_LIBRARY) - find_library (LZ4_LIBRARY lz4) - find_path (LZ4_INCLUDE_DIR NAMES lz4.h PATHS ${LZ4_INCLUDE_PATHS}) -endif () - -if (LZ4_LIBRARY AND LZ4_INCLUDE_DIR) -elseif (NOT MISSING_INTERNAL_LZ4_LIBRARY) - set (LZ4_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/lz4/lib) - set (USE_INTERNAL_LZ4_LIBRARY 1) - set (LZ4_LIBRARY lz4) -endif () - -message (STATUS "Using lz4: ${LZ4_INCLUDE_DIR} : ${LZ4_LIBRARY}") diff --git a/cmake/find/parquet.cmake b/cmake/find/parquet.cmake index 4c91286dae0..d4f62b87d29 100644 --- a/cmake/find/parquet.cmake +++ b/cmake/find/parquet.cmake @@ -63,7 +63,7 @@ elseif(NOT MISSING_INTERNAL_PARQUET_LIBRARY AND NOT OS_FREEBSD) set(ARROW_LIBRARY arrow_shared) set(PARQUET_LIBRARY parquet_shared) if(USE_INTERNAL_PARQUET_LIBRARY_NATIVE_CMAKE) - list(APPEND PARQUET_LIBRARY ${Boost_REGEX_LIBRARY}) + list(APPEND PARQUET_LIBRARY boost::regex) endif() set(THRIFT_LIBRARY thrift) endif() diff --git a/cmake/find/xxhash.cmake b/cmake/find/xxhash.cmake deleted file mode 100644 index 8af871e8fd5..00000000000 --- a/cmake/find/xxhash.cmake +++ /dev/null @@ -1,22 +0,0 @@ -option (USE_INTERNAL_XXHASH_LIBRARY "Set to FALSE to use system xxHash library instead of bundled" ${NOT_UNBUNDLED}) - -if (USE_INTERNAL_XXHASH_LIBRARY AND NOT USE_INTERNAL_LZ4_LIBRARY) - message (WARNING "can not use internal xxhash without internal lz4") - set (USE_INTERNAL_XXHASH_LIBRARY 0) -endif () - -if (USE_INTERNAL_XXHASH_LIBRARY) - set (XXHASH_LIBRARY lz4) - set (XXHASH_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/lz4/lib) -else () - find_library (XXHASH_LIBRARY xxhash) - find_path (XXHASH_INCLUDE_DIR NAMES xxhash.h PATHS ${XXHASH_INCLUDE_PATHS}) -endif () - -if (XXHASH_LIBRARY AND XXHASH_INCLUDE_DIR) - set (USE_XXHASH 1) -else () - set (USE_XXHASH 0) -endif () - -message (STATUS "Using xxhash=${USE_XXHASH}: ${XXHASH_INCLUDE_DIR} : ${XXHASH_LIBRARY}") diff --git a/cmake/lib_name.cmake b/cmake/lib_name.cmake deleted file mode 100644 index f18b2e52576..00000000000 --- a/cmake/lib_name.cmake +++ /dev/null @@ -1,4 +0,0 @@ -set(DIVIDE_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/libdivide) -set(DBMS_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/src ${ClickHouse_BINARY_DIR}/src) -set(DOUBLE_CONVERSION_CONTRIB_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/double-conversion) -set(METROHASH_CONTRIB_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/libmetrohash/src) diff --git a/cmake/print_include_directories.cmake b/cmake/print_include_directories.cmake index 62ebd434320..cc2098cb397 100644 --- a/cmake/print_include_directories.cmake +++ b/cmake/print_include_directories.cmake @@ -21,11 +21,6 @@ if (TARGET double-conversion) list(APPEND dirs ${dirs1}) endif () -if (TARGET ${Boost_PROGRAM_OPTIONS_LIBRARY}) - get_property (dirs1 TARGET ${Boost_PROGRAM_OPTIONS_LIBRARY} PROPERTY INCLUDE_DIRECTORIES) - list(APPEND dirs ${dirs1}) -endif () - list(REMOVE_DUPLICATES dirs) file (WRITE ${CMAKE_CURRENT_BINARY_DIR}/include_directories.txt "") foreach (dir ${dirs}) diff --git a/contrib/CMakeLists.txt b/contrib/CMakeLists.txt index d122188ad0b..b8029124712 100644 --- a/contrib/CMakeLists.txt +++ b/contrib/CMakeLists.txt @@ -16,13 +16,18 @@ set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w") set_property(DIRECTORY PROPERTY EXCLUDE_FROM_ALL 1) +add_subdirectory (boost-cmake) add_subdirectory (cctz-cmake) add_subdirectory (consistent-hashing-sumbur) add_subdirectory (consistent-hashing) add_subdirectory (croaring) add_subdirectory (FastMemcpy) +add_subdirectory (hyperscan-cmake) add_subdirectory (jemalloc-cmake) add_subdirectory (libcpuid-cmake) +add_subdirectory (libdivide) +add_subdirectory (libmetrohash) +add_subdirectory (lz4-cmake) add_subdirectory (murmurhash) add_subdirectory (replxx-cmake) add_subdirectory (ryu-cmake) @@ -33,14 +38,6 @@ add_subdirectory (poco-cmake) # TODO: refactor the contrib libraries below this comment. -if (USE_INTERNAL_BOOST_LIBRARY) - add_subdirectory (boost-cmake) -endif () - -if (USE_INTERNAL_LZ4_LIBRARY) - add_subdirectory (lz4-cmake) -endif () - if (USE_INTERNAL_ZSTD_LIBRARY) add_subdirectory (zstd-cmake) endif () @@ -63,10 +60,6 @@ if (USE_INTERNAL_FARMHASH_LIBRARY) add_subdirectory (libfarmhash) endif () -if (USE_INTERNAL_METROHASH_LIBRARY) - add_subdirectory (libmetrohash) -endif () - if (USE_INTERNAL_BTRIE_LIBRARY) add_subdirectory (libbtrie) endif () @@ -294,18 +287,6 @@ if (USE_BASE64) add_subdirectory (base64-cmake) endif() -if (USE_INTERNAL_HYPERSCAN_LIBRARY) - # The library is large - avoid bloat. - if (USE_STATIC_LIBRARIES) - add_subdirectory (hyperscan) - target_compile_options (hs PRIVATE -g0) - else () - set(BUILD_SHARED_LIBS 1 CACHE INTERNAL "") - add_subdirectory (hyperscan) - target_compile_options (hs_shared PRIVATE -g0) - endif () -endif() - if (USE_SIMDJSON) add_subdirectory (simdjson-cmake) endif() @@ -314,4 +295,10 @@ if (USE_FASTOPS) add_subdirectory (fastops-cmake) endif() +if (USE_CASSANDRA) + add_subdirectory (libuv) + add_subdirectory (cassandra) +endif() + add_subdirectory (fmtlib-cmake) + diff --git a/contrib/arrow-cmake/CMakeLists.txt b/contrib/arrow-cmake/CMakeLists.txt index 46c6b0e3918..afcdae68e77 100644 --- a/contrib/arrow-cmake/CMakeLists.txt +++ b/contrib/arrow-cmake/CMakeLists.txt @@ -47,7 +47,8 @@ set(thriftcpp_threads_SOURCES ) add_library(${THRIFT_LIBRARY} ${thriftcpp_SOURCES} ${thriftcpp_threads_SOURCES}) set_target_properties(${THRIFT_LIBRARY} PROPERTIES CXX_STANDARD 14) # REMOVE after https://github.com/apache/thrift/pull/1641 -target_include_directories(${THRIFT_LIBRARY} SYSTEM PUBLIC ${ClickHouse_SOURCE_DIR}/contrib/thrift/lib/cpp/src PRIVATE ${Boost_INCLUDE_DIRS}) +target_include_directories(${THRIFT_LIBRARY} SYSTEM PUBLIC ${ClickHouse_SOURCE_DIR}/contrib/thrift/lib/cpp/src) +target_link_libraries (${THRIFT_LIBRARY} PRIVATE boost::headers_only) # === orc @@ -146,7 +147,7 @@ add_custom_target(metadata_fbs DEPENDS ${FBS_OUTPUT_FILES}) add_dependencies(metadata_fbs flatc) # arrow-cmake cmake file calling orc cmake subroutine which detects certain compiler features. -# Apple Clang compiler failed to compile this code without specifying c++11 standard. +# Apple Clang compiler failed to compile this code without specifying c++11 standard. # As result these compiler features detected as absent. In result it failed to compile orc itself. # In orc makefile there is code that sets flags, but arrow-cmake ignores these flags. if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") @@ -286,10 +287,6 @@ set(ARROW_SRCS ${ARROW_SRCS} ${LIBRARY_DIR}/compute/kernels/util_internal.cc ) -if (LZ4_INCLUDE_DIR AND LZ4_LIBRARY) - set(ARROW_WITH_LZ4 1) -endif () - if (SNAPPY_INCLUDE_DIR AND SNAPPY_LIBRARY) set(ARROW_WITH_SNAPPY 1) endif () @@ -302,10 +299,8 @@ if (ZSTD_INCLUDE_DIR AND ZSTD_LIBRARY) set(ARROW_WITH_ZSTD 1) endif () -if (ARROW_WITH_LZ4) - add_definitions(-DARROW_WITH_LZ4) - SET(ARROW_SRCS ${LIBRARY_DIR}/util/compression_lz4.cc ${ARROW_SRCS}) -endif () +add_definitions(-DARROW_WITH_LZ4) +SET(ARROW_SRCS ${LIBRARY_DIR}/util/compression_lz4.cc ${ARROW_SRCS}) if (ARROW_WITH_SNAPPY) add_definitions(-DARROW_WITH_SNAPPY) @@ -328,18 +323,15 @@ add_library(${ARROW_LIBRARY} ${ARROW_SRCS}) # Arrow dependencies add_dependencies(${ARROW_LIBRARY} ${FLATBUFFERS_LIBRARY} metadata_fbs) -target_link_libraries(${ARROW_LIBRARY} PRIVATE boost_system_internal boost_filesystem_internal boost_regex_internal) -target_link_libraries(${ARROW_LIBRARY} PRIVATE ${FLATBUFFERS_LIBRARY}) +target_link_libraries(${ARROW_LIBRARY} PRIVATE ${FLATBUFFERS_LIBRARY} boost::filesystem) if (USE_INTERNAL_PROTOBUF_LIBRARY) add_dependencies(${ARROW_LIBRARY} protoc) endif () -target_include_directories(${ARROW_LIBRARY} SYSTEM PUBLIC ${ClickHouse_SOURCE_DIR}/contrib/arrow/cpp/src PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cpp/src ${Boost_INCLUDE_DIRS}) +target_include_directories(${ARROW_LIBRARY} SYSTEM PUBLIC ${ClickHouse_SOURCE_DIR}/contrib/arrow/cpp/src PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cpp/src) target_link_libraries(${ARROW_LIBRARY} PRIVATE ${DOUBLE_CONVERSION_LIBRARIES} ${Protobuf_LIBRARY}) -if (ARROW_WITH_LZ4) - target_link_libraries(${ARROW_LIBRARY} PRIVATE ${LZ4_LIBRARY}) -endif () +target_link_libraries(${ARROW_LIBRARY} PRIVATE lz4) if (ARROW_WITH_SNAPPY) target_link_libraries(${ARROW_LIBRARY} PRIVATE ${SNAPPY_LIBRARY}) endif () @@ -396,8 +388,7 @@ list(APPEND PARQUET_SRCS add_library(${PARQUET_LIBRARY} ${PARQUET_SRCS}) target_include_directories(${PARQUET_LIBRARY} SYSTEM PUBLIC ${ClickHouse_SOURCE_DIR}/contrib/arrow/cpp/src ${CMAKE_CURRENT_SOURCE_DIR}/cpp/src) include(${ClickHouse_SOURCE_DIR}/contrib/thrift/build/cmake/ConfigureChecks.cmake) # makes config.h -target_link_libraries(${PARQUET_LIBRARY} PUBLIC ${ARROW_LIBRARY} PRIVATE ${THRIFT_LIBRARY} ${Boost_REGEX_LIBRARY}) -target_include_directories(${PARQUET_LIBRARY} PRIVATE ${Boost_INCLUDE_DIRS}) +target_link_libraries(${PARQUET_LIBRARY} PUBLIC ${ARROW_LIBRARY} PRIVATE ${THRIFT_LIBRARY} boost::headers_only boost::regex) if (SANITIZE STREQUAL "undefined") target_compile_options(${PARQUET_LIBRARY} PRIVATE -fno-sanitize=undefined) diff --git a/contrib/avro-cmake/CMakeLists.txt b/contrib/avro-cmake/CMakeLists.txt index f544b3c50cd..052a19ee804 100644 --- a/contrib/avro-cmake/CMakeLists.txt +++ b/contrib/avro-cmake/CMakeLists.txt @@ -45,13 +45,12 @@ set_target_properties (avrocpp PROPERTIES VERSION ${AVRO_VERSION_MAJOR}.${AVRO_V target_include_directories(avrocpp SYSTEM PUBLIC ${AVROCPP_INCLUDE_DIR}) -target_include_directories(avrocpp SYSTEM PUBLIC ${Boost_INCLUDE_DIRS}) -target_link_libraries (avrocpp ${Boost_IOSTREAMS_LIBRARY}) +target_link_libraries (avrocpp PRIVATE boost::headers_only boost::iostreams) if (SNAPPY_INCLUDE_DIR AND SNAPPY_LIBRARY) target_compile_definitions (avrocpp PUBLIC SNAPPY_CODEC_AVAILABLE) target_include_directories (avrocpp PRIVATE ${SNAPPY_INCLUDE_DIR}) - target_link_libraries (avrocpp ${SNAPPY_LIBRARY}) + target_link_libraries (avrocpp PRIVATE ${SNAPPY_LIBRARY}) endif () if (COMPILER_GCC) @@ -67,4 +66,4 @@ ADD_CUSTOM_TARGET(avro_symlink_headers ALL COMMAND ${CMAKE_COMMAND} -E make_directory ${AVROCPP_ROOT_DIR}/include COMMAND ${CMAKE_COMMAND} -E create_symlink ${AVROCPP_ROOT_DIR}/api ${AVROCPP_ROOT_DIR}/include/avro ) -add_dependencies(avrocpp avro_symlink_headers) \ No newline at end of file +add_dependencies(avrocpp avro_symlink_headers) diff --git a/contrib/aws b/contrib/aws index fb5c604525f..17e10c0fc77 160000 --- a/contrib/aws +++ b/contrib/aws @@ -1 +1 @@ -Subproject commit fb5c604525f5151d75a856462653e7e38b559b79 +Subproject commit 17e10c0fc77f22afe890fa6d1b283760e5edaa56 diff --git a/contrib/boost-cmake/CMakeLists.txt b/contrib/boost-cmake/CMakeLists.txt index 582cc84a552..e92fe4b7159 100644 --- a/contrib/boost-cmake/CMakeLists.txt +++ b/contrib/boost-cmake/CMakeLists.txt @@ -1,45 +1,133 @@ -# Supported contrib/boost source variants: -# 1. Default - Minimized vrsion from release archive : https://github.com/ClickHouse-Extras/boost -# 2. Release archive unpacked to contrib/boost -# 3. Full boost https://github.com/boostorg/boost +option (USE_INTERNAL_BOOST_LIBRARY "Use internal Boost library" ${NOT_UNBUNDLED}) -# if boostorg/boost connected as submodule: Update all boost internal submodules to tag: -# git submodule foreach "git fetch --all && git checkout boost-1.66.0 || true" +if (USE_INTERNAL_BOOST_LIBRARY) + set (LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/boost) -# -# Important boost patch: 094c18b -# + # filesystem -include(${ClickHouse_SOURCE_DIR}/cmake/dbms_glob_sources.cmake) + set (SRCS_FILESYSTEM + ${LIBRARY_DIR}/libs/filesystem/src/codecvt_error_category.cpp + ${LIBRARY_DIR}/libs/filesystem/src/operations.cpp + ${LIBRARY_DIR}/libs/filesystem/src/path_traits.cpp + ${LIBRARY_DIR}/libs/filesystem/src/path.cpp + ${LIBRARY_DIR}/libs/filesystem/src/portability.cpp + ${LIBRARY_DIR}/libs/filesystem/src/unique_path.cpp + ${LIBRARY_DIR}/libs/filesystem/src/utf8_codecvt_facet.cpp + ${LIBRARY_DIR}/libs/filesystem/src/windows_file_codecvt.cpp + ) -set(LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/boost) + add_library (_boost_filesystem ${SRCS_FILESYSTEM}) + add_library (boost::filesystem ALIAS _boost_filesystem) + target_include_directories (_boost_filesystem SYSTEM BEFORE PUBLIC ${LIBRARY_DIR}) -if(NOT MSVC) - add_definitions(-Wno-unused-variable -Wno-deprecated-declarations) -endif() + # headers-only -macro(add_boost_lib lib_name) - add_headers_and_sources(boost_${lib_name} ${LIBRARY_DIR}/libs/${lib_name}/src) - add_library(boost_${lib_name}_internal ${boost_${lib_name}_sources}) - target_include_directories(boost_${lib_name}_internal SYSTEM BEFORE PUBLIC ${Boost_INCLUDE_DIRS}) - target_compile_definitions(boost_${lib_name}_internal PUBLIC BOOST_SYSTEM_NO_DEPRECATED) -endmacro() + add_library (_boost_headers_only INTERFACE) + add_library (boost::headers_only ALIAS _boost_headers_only) + target_include_directories (_boost_headers_only SYSTEM BEFORE INTERFACE ${LIBRARY_DIR}) -add_boost_lib(system) + # iostreams -add_boost_lib(program_options) + set (SRCS_IOSTREAMS + ${LIBRARY_DIR}/libs/iostreams/src/file_descriptor.cpp + ${LIBRARY_DIR}/libs/iostreams/src/gzip.cpp + ${LIBRARY_DIR}/libs/iostreams/src/mapped_file.cpp + ${LIBRARY_DIR}/libs/iostreams/src/zlib.cpp + ) -add_boost_lib(filesystem) -target_link_libraries(boost_filesystem_internal PRIVATE boost_system_internal) + add_library (_boost_iostreams ${SRCS_IOSTREAMS}) + add_library (boost::iostreams ALIAS _boost_iostreams) + target_include_directories (_boost_iostreams PRIVATE ${LIBRARY_DIR}) + target_link_libraries (_boost_iostreams PRIVATE zlib) -#add_boost_lib(random) + # program_options -if (USE_INTERNAL_PARQUET_LIBRARY) - add_boost_lib(regex) -endif() + set (SRCS_PROGRAM_OPTIONS + ${LIBRARY_DIR}/libs/program_options/src/cmdline.cpp + ${LIBRARY_DIR}/libs/program_options/src/config_file.cpp + ${LIBRARY_DIR}/libs/program_options/src/convert.cpp + ${LIBRARY_DIR}/libs/program_options/src/options_description.cpp + ${LIBRARY_DIR}/libs/program_options/src/parsers.cpp + ${LIBRARY_DIR}/libs/program_options/src/positional_options.cpp + ${LIBRARY_DIR}/libs/program_options/src/split.cpp + ${LIBRARY_DIR}/libs/program_options/src/utf8_codecvt_facet.cpp + ${LIBRARY_DIR}/libs/program_options/src/value_semantic.cpp + ${LIBRARY_DIR}/libs/program_options/src/variables_map.cpp + ${LIBRARY_DIR}/libs/program_options/src/winmain.cpp + ) -if (USE_INTERNAL_AVRO_LIBRARY) - add_boost_lib(iostreams) - target_link_libraries(boost_iostreams_internal PUBLIC ${ZLIB_LIBRARIES}) - target_include_directories(boost_iostreams_internal SYSTEM BEFORE PRIVATE ${ZLIB_INCLUDE_DIR}) -endif() + add_library (_boost_program_options ${SRCS_PROGRAM_OPTIONS}) + add_library (boost::program_options ALIAS _boost_program_options) + target_include_directories (_boost_program_options SYSTEM BEFORE PUBLIC ${LIBRARY_DIR}) + + # regex + + set (SRCS_REGEX + ${LIBRARY_DIR}/libs/regex/src/c_regex_traits.cpp + ${LIBRARY_DIR}/libs/regex/src/cpp_regex_traits.cpp + ${LIBRARY_DIR}/libs/regex/src/cregex.cpp + ${LIBRARY_DIR}/libs/regex/src/fileiter.cpp + ${LIBRARY_DIR}/libs/regex/src/icu.cpp + ${LIBRARY_DIR}/libs/regex/src/instances.cpp + ${LIBRARY_DIR}/libs/regex/src/internals.hpp + ${LIBRARY_DIR}/libs/regex/src/posix_api.cpp + ${LIBRARY_DIR}/libs/regex/src/regex_debug.cpp + ${LIBRARY_DIR}/libs/regex/src/regex_raw_buffer.cpp + ${LIBRARY_DIR}/libs/regex/src/regex_traits_defaults.cpp + ${LIBRARY_DIR}/libs/regex/src/regex.cpp + ${LIBRARY_DIR}/libs/regex/src/static_mutex.cpp + ${LIBRARY_DIR}/libs/regex/src/usinstances.cpp + ${LIBRARY_DIR}/libs/regex/src/w32_regex_traits.cpp + ${LIBRARY_DIR}/libs/regex/src/wc_regex_traits.cpp + ${LIBRARY_DIR}/libs/regex/src/wide_posix_api.cpp + ${LIBRARY_DIR}/libs/regex/src/winstances.cpp + ) + + add_library (_boost_regex ${SRCS_REGEX}) + add_library (boost::regex ALIAS _boost_regex) + target_include_directories (_boost_regex PRIVATE ${LIBRARY_DIR}) + + # system + + set (SRCS_SYSTEM + ${LIBRARY_DIR}/libs/system/src/error_code.cpp + ) + + add_library (_boost_system ${SRCS_SYSTEM}) + add_library (boost::system ALIAS _boost_system) + target_include_directories (_boost_system PRIVATE ${LIBRARY_DIR}) +else () + # 1.70 like in contrib/boost + # 1.67 on CI + set(BOOST_VERSION 1.67) + + find_package(Boost ${BOOST_VERSION} COMPONENTS + system + filesystem + iostreams + program_options + regex + REQUIRED) + + add_library (_boost_headers_only INTERFACE) + add_library (boost::headers_only ALIAS _boost_headers_only) + target_include_directories (_boost_headers_only SYSTEM BEFORE INTERFACE ${Boost_INCLUDE_DIR}) + + add_library (_boost_filesystem INTERFACE) + add_library (_boost_iostreams INTERFACE) + add_library (_boost_program_options INTERFACE) + add_library (_boost_regex INTERFACE) + add_library (_boost_system INTERFACE) + + target_link_libraries (_boost_filesystem INTERFACE ${Boost_FILESYSTEM_LIBRARY}) + target_link_libraries (_boost_iostreams INTERFACE ${Boost_IOSTREAMS_LIBRARY}) + target_link_libraries (_boost_program_options INTERFACE ${Boost_PROGRAM_OPTIONS_LIBRARY}) + target_link_libraries (_boost_regex INTERFACE ${Boost_REGEX_LIBRARY}) + target_link_libraries (_boost_system INTERFACE ${Boost_SYSTEM_LIBRARY}) + + add_library (boost::filesystem ALIAS _boost_filesystem) + add_library (boost::iostreams ALIAS _boost_iostreams) + add_library (boost::program_options ALIAS _boost_program_options) + add_library (boost::regex ALIAS _boost_regex) + add_library (boost::system ALIAS _boost_system) +endif () diff --git a/contrib/cassandra b/contrib/cassandra new file mode 160000 index 00000000000..a49b4e0e269 --- /dev/null +++ b/contrib/cassandra @@ -0,0 +1 @@ +Subproject commit a49b4e0e2696a4b8ef286a5b9538d1cbe8490509 diff --git a/contrib/cppkafka-cmake/CMakeLists.txt b/contrib/cppkafka-cmake/CMakeLists.txt index 2725eaf7a77..9f512974948 100644 --- a/contrib/cppkafka-cmake/CMakeLists.txt +++ b/contrib/cppkafka-cmake/CMakeLists.txt @@ -1,31 +1,33 @@ -set(CPPKAFKA_DIR ${ClickHouse_SOURCE_DIR}/contrib/cppkafka) +set(LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/cppkafka) set(SRCS - ${CPPKAFKA_DIR}/src/configuration.cpp - ${CPPKAFKA_DIR}/src/topic_configuration.cpp - ${CPPKAFKA_DIR}/src/configuration_option.cpp - ${CPPKAFKA_DIR}/src/exceptions.cpp - ${CPPKAFKA_DIR}/src/topic.cpp - ${CPPKAFKA_DIR}/src/buffer.cpp - ${CPPKAFKA_DIR}/src/queue.cpp - ${CPPKAFKA_DIR}/src/message.cpp - ${CPPKAFKA_DIR}/src/message_timestamp.cpp - ${CPPKAFKA_DIR}/src/message_internal.cpp - ${CPPKAFKA_DIR}/src/topic_partition.cpp - ${CPPKAFKA_DIR}/src/topic_partition_list.cpp - ${CPPKAFKA_DIR}/src/metadata.cpp - ${CPPKAFKA_DIR}/src/group_information.cpp - ${CPPKAFKA_DIR}/src/error.cpp - ${CPPKAFKA_DIR}/src/event.cpp - - ${CPPKAFKA_DIR}/src/kafka_handle_base.cpp - ${CPPKAFKA_DIR}/src/producer.cpp - ${CPPKAFKA_DIR}/src/consumer.cpp + ${LIBRARY_DIR}/src/buffer.cpp + ${LIBRARY_DIR}/src/configuration_option.cpp + ${LIBRARY_DIR}/src/configuration.cpp + ${LIBRARY_DIR}/src/consumer.cpp + ${LIBRARY_DIR}/src/error.cpp + ${LIBRARY_DIR}/src/event.cpp + ${LIBRARY_DIR}/src/exceptions.cpp + ${LIBRARY_DIR}/src/group_information.cpp + ${LIBRARY_DIR}/src/kafka_handle_base.cpp + ${LIBRARY_DIR}/src/message_internal.cpp + ${LIBRARY_DIR}/src/message_timestamp.cpp + ${LIBRARY_DIR}/src/message.cpp + ${LIBRARY_DIR}/src/metadata.cpp + ${LIBRARY_DIR}/src/producer.cpp + ${LIBRARY_DIR}/src/queue.cpp + ${LIBRARY_DIR}/src/topic_configuration.cpp + ${LIBRARY_DIR}/src/topic_partition_list.cpp + ${LIBRARY_DIR}/src/topic_partition.cpp + ${LIBRARY_DIR}/src/topic.cpp ) add_library(cppkafka ${SRCS}) -target_link_libraries(cppkafka PRIVATE ${RDKAFKA_LIBRARY}) -target_include_directories(cppkafka PRIVATE ${CPPKAFKA_DIR}/include/cppkafka) -target_include_directories(cppkafka PRIVATE ${Boost_INCLUDE_DIRS}) -target_include_directories(cppkafka SYSTEM PUBLIC ${CPPKAFKA_DIR}/include) +target_link_libraries(cppkafka + PRIVATE + ${RDKAFKA_LIBRARY} + boost::headers_only +) +target_include_directories(cppkafka PRIVATE ${LIBRARY_DIR}/include/cppkafka) +target_include_directories(cppkafka SYSTEM BEFORE PUBLIC ${LIBRARY_DIR}/include) diff --git a/contrib/hyperscan-cmake/CMakeLists.txt b/contrib/hyperscan-cmake/CMakeLists.txt new file mode 100644 index 00000000000..1f30bfccbe8 --- /dev/null +++ b/contrib/hyperscan-cmake/CMakeLists.txt @@ -0,0 +1,250 @@ +option (ENABLE_HYPERSCAN "Enable hyperscan library" ${ENABLE_LIBRARIES}) + +if (NOT HAVE_SSSE3) + set (ENABLE_HYPERSCAN OFF) +endif () + +if (ENABLE_HYPERSCAN) + option (USE_INTERNAL_HYPERSCAN_LIBRARY "Use internal hyperscan library" ${NOT_UNBUNDLED}) + + if (USE_INTERNAL_HYPERSCAN_LIBRARY) + set (LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/hyperscan) + + set (SRCS + ${LIBRARY_DIR}/src/alloc.c + ${LIBRARY_DIR}/src/compiler/asserts.cpp + ${LIBRARY_DIR}/src/compiler/compiler.cpp + ${LIBRARY_DIR}/src/compiler/error.cpp + ${LIBRARY_DIR}/src/crc32.c + ${LIBRARY_DIR}/src/database.c + ${LIBRARY_DIR}/src/fdr/engine_description.cpp + ${LIBRARY_DIR}/src/fdr/fdr_compile_util.cpp + ${LIBRARY_DIR}/src/fdr/fdr_compile.cpp + ${LIBRARY_DIR}/src/fdr/fdr_confirm_compile.cpp + ${LIBRARY_DIR}/src/fdr/fdr_engine_description.cpp + ${LIBRARY_DIR}/src/fdr/fdr.c + ${LIBRARY_DIR}/src/fdr/flood_compile.cpp + ${LIBRARY_DIR}/src/fdr/teddy_compile.cpp + ${LIBRARY_DIR}/src/fdr/teddy_engine_description.cpp + ${LIBRARY_DIR}/src/fdr/teddy.c + ${LIBRARY_DIR}/src/grey.cpp + ${LIBRARY_DIR}/src/hs_valid_platform.c + ${LIBRARY_DIR}/src/hs_version.c + ${LIBRARY_DIR}/src/hs.cpp + ${LIBRARY_DIR}/src/hwlm/hwlm_build.cpp + ${LIBRARY_DIR}/src/hwlm/hwlm_literal.cpp + ${LIBRARY_DIR}/src/hwlm/hwlm.c + ${LIBRARY_DIR}/src/hwlm/noodle_build.cpp + ${LIBRARY_DIR}/src/hwlm/noodle_engine.c + ${LIBRARY_DIR}/src/nfa/accel_dfa_build_strat.cpp + ${LIBRARY_DIR}/src/nfa/accel.c + ${LIBRARY_DIR}/src/nfa/accelcompile.cpp + ${LIBRARY_DIR}/src/nfa/castle.c + ${LIBRARY_DIR}/src/nfa/castlecompile.cpp + ${LIBRARY_DIR}/src/nfa/dfa_build_strat.cpp + ${LIBRARY_DIR}/src/nfa/dfa_min.cpp + ${LIBRARY_DIR}/src/nfa/gough.c + ${LIBRARY_DIR}/src/nfa/goughcompile_accel.cpp + ${LIBRARY_DIR}/src/nfa/goughcompile_reg.cpp + ${LIBRARY_DIR}/src/nfa/goughcompile.cpp + ${LIBRARY_DIR}/src/nfa/lbr.c + ${LIBRARY_DIR}/src/nfa/limex_64.c + ${LIBRARY_DIR}/src/nfa/limex_accel.c + ${LIBRARY_DIR}/src/nfa/limex_compile.cpp + ${LIBRARY_DIR}/src/nfa/limex_native.c + ${LIBRARY_DIR}/src/nfa/limex_simd128.c + ${LIBRARY_DIR}/src/nfa/limex_simd256.c + ${LIBRARY_DIR}/src/nfa/limex_simd384.c + ${LIBRARY_DIR}/src/nfa/limex_simd512.c + ${LIBRARY_DIR}/src/nfa/mcclellan.c + ${LIBRARY_DIR}/src/nfa/mcclellancompile_util.cpp + ${LIBRARY_DIR}/src/nfa/mcclellancompile.cpp + ${LIBRARY_DIR}/src/nfa/mcsheng_compile.cpp + ${LIBRARY_DIR}/src/nfa/mcsheng_data.c + ${LIBRARY_DIR}/src/nfa/mcsheng.c + ${LIBRARY_DIR}/src/nfa/mpv.c + ${LIBRARY_DIR}/src/nfa/mpvcompile.cpp + ${LIBRARY_DIR}/src/nfa/nfa_api_dispatch.c + ${LIBRARY_DIR}/src/nfa/nfa_build_util.cpp + ${LIBRARY_DIR}/src/nfa/rdfa_graph.cpp + ${LIBRARY_DIR}/src/nfa/rdfa_merge.cpp + ${LIBRARY_DIR}/src/nfa/rdfa.cpp + ${LIBRARY_DIR}/src/nfa/repeat.c + ${LIBRARY_DIR}/src/nfa/repeatcompile.cpp + ${LIBRARY_DIR}/src/nfa/sheng.c + ${LIBRARY_DIR}/src/nfa/shengcompile.cpp + ${LIBRARY_DIR}/src/nfa/shufti.c + ${LIBRARY_DIR}/src/nfa/shufticompile.cpp + ${LIBRARY_DIR}/src/nfa/tamarama.c + ${LIBRARY_DIR}/src/nfa/tamaramacompile.cpp + ${LIBRARY_DIR}/src/nfa/truffle.c + ${LIBRARY_DIR}/src/nfa/trufflecompile.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_anchored_acyclic.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_anchored_dots.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_asserts.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_builder.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_calc_components.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_cyclic_redundancy.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_depth.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_dominators.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_edge_redundancy.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_equivalence.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_execute.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_expr_info.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_extparam.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_fixed_width.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_fuzzy.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_haig.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_holder.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_is_equal.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_lbr.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_limex_accel.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_limex.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_literal_analysis.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_literal_component.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_literal_decorated.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_mcclellan.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_misc_opt.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_netflow.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_prefilter.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_prune.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_puff.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_redundancy.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_region_redundancy.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_region.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_repeat.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_reports.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_restructuring.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_revacc.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_sep.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_small_literal_set.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_som_add_redundancy.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_som_util.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_som.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_split.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_squash.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_stop.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_uncalc_components.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_utf8.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_util.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_vacuous.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_violet.cpp + ${LIBRARY_DIR}/src/nfagraph/ng_width.cpp + ${LIBRARY_DIR}/src/nfagraph/ng.cpp + ${LIBRARY_DIR}/src/parser/AsciiComponentClass.cpp + ${LIBRARY_DIR}/src/parser/buildstate.cpp + ${LIBRARY_DIR}/src/parser/check_refs.cpp + ${LIBRARY_DIR}/src/parser/Component.cpp + ${LIBRARY_DIR}/src/parser/ComponentAlternation.cpp + ${LIBRARY_DIR}/src/parser/ComponentAssertion.cpp + ${LIBRARY_DIR}/src/parser/ComponentAtomicGroup.cpp + ${LIBRARY_DIR}/src/parser/ComponentBackReference.cpp + ${LIBRARY_DIR}/src/parser/ComponentBoundary.cpp + ${LIBRARY_DIR}/src/parser/ComponentByte.cpp + ${LIBRARY_DIR}/src/parser/ComponentClass.cpp + ${LIBRARY_DIR}/src/parser/ComponentCondReference.cpp + ${LIBRARY_DIR}/src/parser/ComponentEmpty.cpp + ${LIBRARY_DIR}/src/parser/ComponentEUS.cpp + ${LIBRARY_DIR}/src/parser/ComponentRepeat.cpp + ${LIBRARY_DIR}/src/parser/ComponentSequence.cpp + ${LIBRARY_DIR}/src/parser/ComponentVisitor.cpp + ${LIBRARY_DIR}/src/parser/ComponentWordBoundary.cpp + ${LIBRARY_DIR}/src/parser/ConstComponentVisitor.cpp + ${LIBRARY_DIR}/src/parser/control_verbs.cpp + ${LIBRARY_DIR}/src/parser/logical_combination.cpp + ${LIBRARY_DIR}/src/parser/parse_error.cpp + ${LIBRARY_DIR}/src/parser/parser_util.cpp + ${LIBRARY_DIR}/src/parser/Parser.cpp + ${LIBRARY_DIR}/src/parser/prefilter.cpp + ${LIBRARY_DIR}/src/parser/shortcut_literal.cpp + ${LIBRARY_DIR}/src/parser/ucp_table.cpp + ${LIBRARY_DIR}/src/parser/unsupported.cpp + ${LIBRARY_DIR}/src/parser/utf8_validate.cpp + ${LIBRARY_DIR}/src/parser/Utf8ComponentClass.cpp + ${LIBRARY_DIR}/src/rose/block.c + ${LIBRARY_DIR}/src/rose/catchup.c + ${LIBRARY_DIR}/src/rose/init.c + ${LIBRARY_DIR}/src/rose/match.c + ${LIBRARY_DIR}/src/rose/program_runtime.c + ${LIBRARY_DIR}/src/rose/rose_build_add_mask.cpp + ${LIBRARY_DIR}/src/rose/rose_build_add.cpp + ${LIBRARY_DIR}/src/rose/rose_build_anchored.cpp + ${LIBRARY_DIR}/src/rose/rose_build_bytecode.cpp + ${LIBRARY_DIR}/src/rose/rose_build_castle.cpp + ${LIBRARY_DIR}/src/rose/rose_build_compile.cpp + ${LIBRARY_DIR}/src/rose/rose_build_convert.cpp + ${LIBRARY_DIR}/src/rose/rose_build_dedupe.cpp + ${LIBRARY_DIR}/src/rose/rose_build_engine_blob.cpp + ${LIBRARY_DIR}/src/rose/rose_build_exclusive.cpp + ${LIBRARY_DIR}/src/rose/rose_build_groups.cpp + ${LIBRARY_DIR}/src/rose/rose_build_infix.cpp + ${LIBRARY_DIR}/src/rose/rose_build_instructions.cpp + ${LIBRARY_DIR}/src/rose/rose_build_lit_accel.cpp + ${LIBRARY_DIR}/src/rose/rose_build_long_lit.cpp + ${LIBRARY_DIR}/src/rose/rose_build_lookaround.cpp + ${LIBRARY_DIR}/src/rose/rose_build_matchers.cpp + ${LIBRARY_DIR}/src/rose/rose_build_merge.cpp + ${LIBRARY_DIR}/src/rose/rose_build_misc.cpp + ${LIBRARY_DIR}/src/rose/rose_build_program.cpp + ${LIBRARY_DIR}/src/rose/rose_build_role_aliasing.cpp + ${LIBRARY_DIR}/src/rose/rose_build_scatter.cpp + ${LIBRARY_DIR}/src/rose/rose_build_width.cpp + ${LIBRARY_DIR}/src/rose/rose_in_util.cpp + ${LIBRARY_DIR}/src/rose/stream.c + ${LIBRARY_DIR}/src/runtime.c + ${LIBRARY_DIR}/src/scratch.c + ${LIBRARY_DIR}/src/smallwrite/smallwrite_build.cpp + ${LIBRARY_DIR}/src/som/slot_manager.cpp + ${LIBRARY_DIR}/src/som/som_runtime.c + ${LIBRARY_DIR}/src/som/som_stream.c + ${LIBRARY_DIR}/src/stream_compress.c + ${LIBRARY_DIR}/src/util/alloc.cpp + ${LIBRARY_DIR}/src/util/charreach.cpp + ${LIBRARY_DIR}/src/util/clique.cpp + ${LIBRARY_DIR}/src/util/compile_context.cpp + ${LIBRARY_DIR}/src/util/compile_error.cpp + ${LIBRARY_DIR}/src/util/cpuid_flags.c + ${LIBRARY_DIR}/src/util/depth.cpp + ${LIBRARY_DIR}/src/util/fatbit_build.cpp + ${LIBRARY_DIR}/src/util/multibit_build.cpp + ${LIBRARY_DIR}/src/util/multibit.c + ${LIBRARY_DIR}/src/util/report_manager.cpp + ${LIBRARY_DIR}/src/util/simd_utils.c + ${LIBRARY_DIR}/src/util/state_compress.c + ${LIBRARY_DIR}/src/util/target_info.cpp + ${LIBRARY_DIR}/src/util/ue2string.cpp + ) + + add_library (hyperscan ${SRCS}) + + target_compile_definitions (hyperscan PUBLIC USE_HYPERSCAN=1) + target_compile_options (hyperscan + PRIVATE -g0 -march=corei7 # library has too much debug information + ) + target_include_directories (hyperscan + PRIVATE + common + ${LIBRARY_DIR}/include + ) + target_include_directories (hyperscan SYSTEM PUBLIC ${LIBRARY_DIR}/src) + if (ARCH_AMD64) + target_include_directories (hyperscan PRIVATE x86_64) + endif () + target_link_libraries (hyperscan PRIVATE boost::headers_only) + else () + find_library (LIBRARY_HYPERSCAN hs) + find_path (INCLUDE_HYPERSCAN NAMES hs.h HINTS /usr/include/hs) # Ubuntu puts headers in this folder + + add_library (hyperscan UNKNOWN IMPORTED GLOBAL) + set_target_properties (hyperscan PROPERTIES IMPORTED_LOCATION ${LIBRARY_HYPERSCAN}) + set_target_properties (hyperscan PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${INCLUDE_HYPERSCAN}) + set_property(TARGET hyperscan APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS USE_HYPERSCAN=1) + endif () + + message (STATUS "Using hyperscan") +else () + add_library (hyperscan INTERFACE) + target_compile_definitions (hyperscan INTERFACE USE_HYPERSCAN=0) + + message (STATUS "Not using hyperscan") +endif () diff --git a/contrib/hyperscan-cmake/common/hs_version.h b/contrib/hyperscan-cmake/common/hs_version.h new file mode 100644 index 00000000000..f6fa8cb209f --- /dev/null +++ b/contrib/hyperscan-cmake/common/hs_version.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015, Intel Corporation + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of Intel Corporation nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef HS_VERSION_H_C6428FAF8E3713 +#define HS_VERSION_H_C6428FAF8E3713 + +/** + * A version string to identify this release of Hyperscan. + */ +#define HS_VERSION_STRING "5.1.1 2000-01-01" + +#define HS_VERSION_32BIT ((5 << 24) | (1 << 16) | (1 << 8) | 0) + +#endif /* HS_VERSION_H_C6428FAF8E3713 */ + diff --git a/contrib/hyperscan-cmake/x86_64/config.h b/contrib/hyperscan-cmake/x86_64/config.h new file mode 100644 index 00000000000..4786e3f4e21 --- /dev/null +++ b/contrib/hyperscan-cmake/x86_64/config.h @@ -0,0 +1,106 @@ +/* used by cmake */ + +#ifndef CONFIG_H_ +#define CONFIG_H_ + +/* "Define if the build is 32 bit" */ +/* #undef ARCH_32_BIT */ + +/* "Define if the build is 64 bit" */ +#define ARCH_64_BIT + +/* "Define if building for IA32" */ +/* #undef ARCH_IA32 */ + +/* "Define if building for EM64T" */ +#define ARCH_X86_64 + +/* internal build, switch on dump support. */ +/* #undef DUMP_SUPPORT */ + +/* Define if building "fat" runtime. */ +/* #undef FAT_RUNTIME */ + +/* Define if building AVX-512 in the fat runtime. */ +/* #undef BUILD_AVX512 */ + +/* Define to 1 if `backtrace' works. */ +#define HAVE_BACKTRACE + +/* C compiler has __builtin_assume_aligned */ +#define HAVE_CC_BUILTIN_ASSUME_ALIGNED + +/* C++ compiler has __builtin_assume_aligned */ +#define HAVE_CXX_BUILTIN_ASSUME_ALIGNED + +/* C++ compiler has x86intrin.h */ +#define HAVE_CXX_X86INTRIN_H + +/* C compiler has x86intrin.h */ +#define HAVE_C_X86INTRIN_H + +/* C++ compiler has intrin.h */ +/* #undef HAVE_CXX_INTRIN_H */ + +/* C compiler has intrin.h */ +/* #undef HAVE_C_INTRIN_H */ + +/* Define to 1 if you have the declaration of `pthread_setaffinity_np', and to + 0 if you don't. */ +/* #undef HAVE_DECL_PTHREAD_SETAFFINITY_NP */ + +/* #undef HAVE_PTHREAD_NP_H */ + +/* Define to 1 if you have the `malloc_info' function. */ +/* #undef HAVE_MALLOC_INFO */ + +/* Define to 1 if you have the `memmem' function. */ +/* #undef HAVE_MEMMEM */ + +/* Define to 1 if you have a working `mmap' system call. */ +#define HAVE_MMAP + +/* Define to 1 if `posix_memalign' works. */ +#define HAVE_POSIX_MEMALIGN + +/* Define to 1 if you have the `setrlimit' function. */ +#define HAVE_SETRLIMIT + +/* Define to 1 if you have the `shmget' function. */ +/* #undef HAVE_SHMGET */ + +/* Define to 1 if you have the `sigaction' function. */ +#define HAVE_SIGACTION + +/* Define to 1 if you have the `sigaltstack' function. */ +#define HAVE_SIGALTSTACK + +/* Define if the sqlite3_open_v2 call is available */ +/* #undef HAVE_SQLITE3_OPEN_V2 */ + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H + +/* Define to 1 if you have the `_aligned_malloc' function. */ +/* #undef HAVE__ALIGNED_MALLOC */ + +/* Define if compiler has __builtin_constant_p */ +#define HAVE__BUILTIN_CONSTANT_P + +/* Optimize, inline critical functions */ +#define HS_OPTIMIZE + +#define HS_VERSION +#define HS_MAJOR_VERSION +#define HS_MINOR_VERSION +#define HS_PATCH_VERSION + +#define BUILD_DATE + +/* define if this is a release build. */ +#define RELEASE_BUILD + +/* define if reverse_graph requires patch for boost 1.62.0 */ +/* #undef BOOST_REVGRAPH_PATCH */ + +#endif /* CONFIG_H_ */ diff --git a/contrib/libdivide/CMakeLists.txt b/contrib/libdivide/CMakeLists.txt new file mode 100644 index 00000000000..57e9f254db5 --- /dev/null +++ b/contrib/libdivide/CMakeLists.txt @@ -0,0 +1,2 @@ +add_library (libdivide INTERFACE) +target_include_directories (libdivide SYSTEM BEFORE INTERFACE .) diff --git a/contrib/libhdfs3-cmake/CMakeLists.txt b/contrib/libhdfs3-cmake/CMakeLists.txt index e68f0bacf46..4c71770f5b6 100644 --- a/contrib/libhdfs3-cmake/CMakeLists.txt +++ b/contrib/libhdfs3-cmake/CMakeLists.txt @@ -209,9 +209,8 @@ endif() target_link_libraries(hdfs3 PRIVATE ${LIBXML2_LIBRARY}) # inherit from parent cmake -target_include_directories(hdfs3 PRIVATE ${Boost_INCLUDE_DIRS}) target_include_directories(hdfs3 PRIVATE ${Protobuf_INCLUDE_DIR}) -target_link_libraries(hdfs3 PRIVATE ${Protobuf_LIBRARY}) +target_link_libraries(hdfs3 PRIVATE ${Protobuf_LIBRARY} boost::headers_only) if(OPENSSL_INCLUDE_DIR AND OPENSSL_LIBRARIES) target_include_directories(hdfs3 PRIVATE ${OPENSSL_INCLUDE_DIR}) target_link_libraries(hdfs3 PRIVATE ${OPENSSL_LIBRARIES}) diff --git a/contrib/libmetrohash/CMakeLists.txt b/contrib/libmetrohash/CMakeLists.txt index d71a5432715..9304cb3644c 100644 --- a/contrib/libmetrohash/CMakeLists.txt +++ b/contrib/libmetrohash/CMakeLists.txt @@ -1,13 +1,10 @@ -if (HAVE_SSE42) # Not used. Pretty easy to port. - set (SOURCES_SSE42_ONLY src/metrohash128crc.cpp src/metrohash128crc.h) -endif () - -add_library(metrohash - src/metrohash.h - src/testvector.h - +set (SRCS src/metrohash64.cpp src/metrohash128.cpp - ${SOURCES_SSE42_ONLY}) +) +if (HAVE_SSE42) # Not used. Pretty easy to port. + list (APPEND SRCS src/metrohash128crc.cpp) +endif () -target_include_directories(metrohash PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/src) +add_library(metrohash ${SRCS}) +target_include_directories(metrohash PUBLIC src) diff --git a/contrib/librdkafka-cmake/CMakeLists.txt b/contrib/librdkafka-cmake/CMakeLists.txt index 93ef9d2357b..b8dcb0a9340 100644 --- a/contrib/librdkafka-cmake/CMakeLists.txt +++ b/contrib/librdkafka-cmake/CMakeLists.txt @@ -82,7 +82,7 @@ target_compile_options(rdkafka PRIVATE -fno-sanitize=undefined) target_include_directories(rdkafka SYSTEM PUBLIC include) target_include_directories(rdkafka SYSTEM PUBLIC ${RDKAFKA_SOURCE_DIR}) # Because weird logic with "include_next" is used. target_include_directories(rdkafka SYSTEM PRIVATE ${ZSTD_INCLUDE_DIR}/common) # Because wrong path to "zstd_errors.h" is used. -target_link_libraries(rdkafka PRIVATE ${ZLIB_LIBRARIES} ${ZSTD_LIBRARY} ${LZ4_LIBRARY} ${LIBGSASL_LIBRARY}) +target_link_libraries(rdkafka PRIVATE lz4 ${ZLIB_LIBRARIES} ${ZSTD_LIBRARY} ${LIBGSASL_LIBRARY}) if(OPENSSL_SSL_LIBRARY AND OPENSSL_CRYPTO_LIBRARY) target_link_libraries(rdkafka PRIVATE ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY}) endif() diff --git a/contrib/libuv b/contrib/libuv new file mode 160000 index 00000000000..84438304f41 --- /dev/null +++ b/contrib/libuv @@ -0,0 +1 @@ +Subproject commit 84438304f41d8ea6670ee5409f4d6c63ca784f28 diff --git a/contrib/lz4-cmake/CMakeLists.txt b/contrib/lz4-cmake/CMakeLists.txt index 856389395ca..b8121976213 100644 --- a/contrib/lz4-cmake/CMakeLists.txt +++ b/contrib/lz4-cmake/CMakeLists.txt @@ -1,17 +1,28 @@ -SET(LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/lz4/lib) +option (USE_INTERNAL_LZ4_LIBRARY "Use internal lz4 library" ${NOT_UNBUNDLED}) -add_library (lz4 - ${LIBRARY_DIR}/lz4.c - ${LIBRARY_DIR}/lz4hc.c - ${LIBRARY_DIR}/lz4frame.c - ${LIBRARY_DIR}/lz4frame.h - ${LIBRARY_DIR}/xxhash.c - ${LIBRARY_DIR}/xxhash.h +if (USE_INTERNAL_LZ4_LIBRARY) + set (LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/lz4) - ${LIBRARY_DIR}/lz4.h - ${LIBRARY_DIR}/lz4hc.h) + set (SRCS + ${LIBRARY_DIR}/lib/lz4.c + ${LIBRARY_DIR}/lib/lz4hc.c + ${LIBRARY_DIR}/lib/lz4frame.c + ${LIBRARY_DIR}/lib/xxhash.c + ) -target_compile_definitions(lz4 PUBLIC LZ4_DISABLE_DEPRECATE_WARNINGS=1) -target_compile_options(lz4 PRIVATE -fno-sanitize=undefined) + add_library (lz4 ${SRCS}) -target_include_directories(lz4 PUBLIC ${LIBRARY_DIR}) + target_compile_definitions (lz4 PUBLIC LZ4_DISABLE_DEPRECATE_WARNINGS=1 USE_XXHASH=1) + if (SANITIZE STREQUAL "undefined") + target_compile_options (lz4 PRIVATE -fno-sanitize=undefined) + endif () + target_include_directories(lz4 PUBLIC ${LIBRARY_DIR}/lib) +else () + find_library (LIBRARY_LZ4 lz4) + find_path (INCLUDE_LZ4 lz4.h) + + add_library (lz4 UNKNOWN IMPORTED) + set_property (TARGET lz4 PROPERTY IMPORTED_LOCATION ${LIBRARY_LZ4}) + set_property (TARGET lz4 PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${INCLUDE_LZ4}) + set_property (TARGET lz4 APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS USE_XXHASH=0) +endif () diff --git a/contrib/replxx b/contrib/replxx index f1332626639..2d37daaad24 160000 --- a/contrib/replxx +++ b/contrib/replxx @@ -1 +1 @@ -Subproject commit f1332626639d6492eaf170758642da14fbbda7bf +Subproject commit 2d37daaad24be71e76514a36b0a47120be2f9086 diff --git a/docker/client/Dockerfile b/docker/client/Dockerfile index 5ca7e508d56..493cdaac543 100644 --- a/docker/client/Dockerfile +++ b/docker/client/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:18.04 -ARG repository="deb http://repo.yandex.ru/clickhouse/deb/stable/ main/" +ARG repository="deb https://repo.clickhouse.tech/deb/stable/ main/" ARG version=20.5.1.* RUN apt-get update \ diff --git a/docker/packager/binary/build.sh b/docker/packager/binary/build.sh index 0d1bdc2a88a..4b566ef2158 100755 --- a/docker/packager/binary/build.sh +++ b/docker/packager/binary/build.sh @@ -18,8 +18,7 @@ ccache --zero-stats ||: ln -s /usr/lib/x86_64-linux-gnu/libOpenCL.so.1.0.0 /usr/lib/libOpenCL.so ||: rm -f CMakeCache.txt cmake .. -LA -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DSANITIZE=$SANITIZER $CMAKE_FLAGS -ninja -ccache --show-stats ||: +ninja clickhouse-bundle mv ./programs/clickhouse* /output mv ./src/unit_tests_dbms /output find . -name '*.so' -print -exec mv '{}' /output \; @@ -47,3 +46,4 @@ then rm -r /output/* mv "$COMBINED_OUTPUT.tgz" /output fi +ccache --show-stats ||: diff --git a/docker/packager/deb/Dockerfile b/docker/packager/deb/Dockerfile index 786e6620eac..8a67ff24692 100644 --- a/docker/packager/deb/Dockerfile +++ b/docker/packager/deb/Dockerfile @@ -54,6 +54,8 @@ RUN apt-get --allow-unauthenticated update -y \ libboost-system-dev \ libboost-filesystem-dev \ libboost-thread-dev \ + libboost-iostreams-dev \ + libboost-regex-dev \ zlib1g-dev \ liblz4-dev \ libdouble-conversion-dev \ @@ -82,8 +84,8 @@ RUN apt-get --allow-unauthenticated update -y \ libcctz-dev \ libldap2-dev \ libsasl2-dev \ - heimdal-multidev - + heimdal-multidev \ + libhyperscan-dev # This symlink required by gcc to find lld compiler diff --git a/docker/packager/packager b/docker/packager/packager index 8a5bdda60e8..ccb01a4df92 100755 --- a/docker/packager/packager +++ b/docker/packager/packager @@ -120,6 +120,7 @@ def parse_env_variables(build_type, compiler, sanitizer, package_type, image_typ result.append("CCACHE_BASEDIR=/build") result.append("CCACHE_NOHASHDIR=true") result.append("CCACHE_COMPILERCHECK=content") + result.append("CCACHE_MAXSIZE=15G") # result.append("CCACHE_UMASK=777") if distcc_hosts: diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index 93f192c3f3c..3a16a1fd158 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:18.04 -ARG repository="deb http://repo.yandex.ru/clickhouse/deb/stable/ main/" +ARG repository="deb https://repo.clickhouse.tech/deb/stable/ main/" ARG version=20.5.1.* ARG gosu_ver=1.10 diff --git a/docker/server/entrypoint.sh b/docker/server/entrypoint.sh index 2af8a377b92..8fc9c670b06 100644 --- a/docker/server/entrypoint.sh +++ b/docker/server/entrypoint.sh @@ -94,7 +94,7 @@ if [ -n "$(ls /docker-entrypoint-initdb.d/)" ] || [ -n "$CLICKHOUSE_DB" ]; then # check if clickhouse is ready to accept connections # will try to send ping clickhouse via http_port (max 12 retries, with 1 sec delay) - if ! wget --spider --quiet --tries=12 --waitretry=1 --retry-connrefused "http://localhost:$HTTP_PORT/ping" ; then + if ! wget --spider --quiet --prefer-family=IPv6 --tries=12 --waitretry=1 --retry-connrefused "http://localhost:$HTTP_PORT/ping" ; then echo >&2 'ClickHouse init process failed.' exit 1 fi @@ -110,7 +110,7 @@ if [ -n "$(ls /docker-entrypoint-initdb.d/)" ] || [ -n "$CLICKHOUSE_DB" ]; then # create default database, if defined if [ -n "$CLICKHOUSE_DB" ]; then echo "$0: create database '$CLICKHOUSE_DB'" - "${clickhouseclient[@]}" "CREATE DATABASE IF NOT EXISTS $CLICKHOUSE_DB"; + "${clickhouseclient[@]}" -q "CREATE DATABASE IF NOT EXISTS $CLICKHOUSE_DB"; fi for f in /docker-entrypoint-initdb.d/*; do diff --git a/docker/test/Dockerfile b/docker/test/Dockerfile index 1dd756ed7c2..6673d32c2e2 100644 --- a/docker/test/Dockerfile +++ b/docker/test/Dockerfile @@ -1,6 +1,6 @@ FROM ubuntu:18.04 -ARG repository="deb http://repo.yandex.ru/clickhouse/deb/stable/ main/" +ARG repository="deb https://repo.clickhouse.tech/deb/stable/ main/" ARG version=20.5.1.* RUN apt-get update && \ diff --git a/docker/test/integration/compose/docker_compose_cassandra.yml b/docker/test/integration/compose/docker_compose_cassandra.yml new file mode 100644 index 00000000000..6567a352027 --- /dev/null +++ b/docker/test/integration/compose/docker_compose_cassandra.yml @@ -0,0 +1,7 @@ +version: '2.3' +services: + cassandra1: + image: cassandra + restart: always + ports: + - 9043:9042 diff --git a/docker/test/integration/compose/docker_compose_minio.yml b/docker/test/integration/compose/docker_compose_minio.yml index c52c45b9d69..eefbe4abff5 100644 --- a/docker/test/integration/compose/docker_compose_minio.yml +++ b/docker/test/integration/compose/docker_compose_minio.yml @@ -43,7 +43,10 @@ services: # Empty container to run proxy resolver. resolver: - image: python:3 + build: + context: ../../../docker/test/integration/ + dockerfile: resolver/Dockerfile + network: host ports: - "4083:8080" tty: true diff --git a/docker/test/integration/resolver/Dockerfile b/docker/test/integration/resolver/Dockerfile new file mode 100644 index 00000000000..37118b7a555 --- /dev/null +++ b/docker/test/integration/resolver/Dockerfile @@ -0,0 +1,4 @@ +# Helper docker container to run python bottle apps + +FROM python:3 +RUN python -m pip install bottle \ No newline at end of file diff --git a/docker/test/performance-comparison/compare.sh b/docker/test/performance-comparison/compare.sh index 209b36f59af..a2760907cb3 100755 --- a/docker/test/performance-comparison/compare.sh +++ b/docker/test/performance-comparison/compare.sh @@ -104,13 +104,12 @@ function run_tests # allows the tests to pass even when we add new functions and tests for # them, that are not supported in the old revision. test_prefix=left/performance - elif [ "$PR_TO_TEST" != "" ] && [ "$PR_TO_TEST" != "0" ] - then + else # For PRs, use newer test files so we can test these changes. test_prefix=right/performance - # If some tests were changed in the PR, we may want to run only these - # ones. The list of changed tests in changed-test.txt is prepared in + # If only the perf tests were changed in the PR, we will run only these + # tests. The list of changed tests in changed-test.txt is prepared in # entrypoint.sh from git diffs, because it has the cloned repo. Used # to use rsync for that but it was really ugly and not always correct # (e.g. when the reference SHA is really old and has some other @@ -199,12 +198,14 @@ function get_profiles clickhouse-client --port 9001 --query "select * from system.trace_log format TSVWithNamesAndTypes" > left-trace-log.tsv ||: & clickhouse-client --port 9001 --query "select arrayJoin(trace) addr, concat(splitByChar('/', addressToLine(addr))[-1], '#', demangle(addressToSymbol(addr)) ) name from system.trace_log group by addr format TSVWithNamesAndTypes" > left-addresses.tsv ||: & clickhouse-client --port 9001 --query "select * from system.metric_log format TSVWithNamesAndTypes" > left-metric-log.tsv ||: & + clickhouse-client --port 9001 --query "select * from system.asynchronous_metric_log format TSVWithNamesAndTypes" > left-async-metric-log.tsv ||: & clickhouse-client --port 9002 --query "select * from system.query_log where type = 2 format TSVWithNamesAndTypes" > right-query-log.tsv ||: & clickhouse-client --port 9002 --query "select * from system.query_thread_log format TSVWithNamesAndTypes" > right-query-thread-log.tsv ||: & clickhouse-client --port 9002 --query "select * from system.trace_log format TSVWithNamesAndTypes" > right-trace-log.tsv ||: & clickhouse-client --port 9002 --query "select arrayJoin(trace) addr, concat(splitByChar('/', addressToLine(addr))[-1], '#', demangle(addressToSymbol(addr)) ) name from system.trace_log group by addr format TSVWithNamesAndTypes" > right-addresses.tsv ||: & clickhouse-client --port 9002 --query "select * from system.metric_log format TSVWithNamesAndTypes" > right-metric-log.tsv ||: & + clickhouse-client --port 9002 --query "select * from system.asynchronous_metric_log format TSVWithNamesAndTypes" > right-async-metric-log.tsv ||: & wait @@ -348,9 +349,11 @@ create table query_metric_stats engine File(TSVWithNamesAndTypes, create table queries engine File(TSVWithNamesAndTypes, 'report/queries.tsv') as select -- FIXME Comparison mode doesn't make sense for queries that complete - -- immediately, so for now we pretend they don't exist. We don't want to - -- remove them altogether because we want to be able to detect regressions, - -- but the right way to do this is not yet clear. + -- immediately (on the same order of time as noise). We compute average + -- run time between old and new version, and if it is below a threshold, + -- we just skip the query. If there is a significant regression, the + -- average will be above threshold, we'll process it normally and will + -- detect the regression. (left + right) / 2 < 0.02 as short, not short and abs(diff) > report_threshold and abs(diff) > stat_threshold as changed_fail, @@ -410,11 +413,11 @@ create table all_query_runs_json engine File(JSON, 'report/all-query-runs.json') ; create table changed_perf_tsv engine File(TSV, 'report/changed-perf.tsv') as - select left, right, diff, stat_threshold, changed_fail, test, query_display_name + select left, right, diff, stat_threshold, changed_fail, test, query_index, query_display_name from queries where changed_show order by abs(diff) desc; create table unstable_queries_tsv engine File(TSV, 'report/unstable-queries.tsv') as - select left, right, diff, stat_threshold, unstable_fail, test, query_display_name + select left, right, diff, stat_threshold, unstable_fail, test, query_index, query_display_name from queries where unstable_show order by stat_threshold desc; create table queries_for_flamegraph engine File(TSVWithNamesAndTypes, @@ -422,9 +425,39 @@ create table queries_for_flamegraph engine File(TSVWithNamesAndTypes, select test, query_index from queries where unstable_show or changed_show ; -create table unstable_tests_tsv engine File(TSV, 'report/bad-tests.tsv') as - select test, sum(unstable_fail) u, sum(changed_fail) c, u + c s from queries - group by test having s > 0 order by s desc; +create table test_time_changes_tsv engine File(TSV, 'report/test-time-changes.tsv') as + select test, queries, average_time_change from ( + select test, count(*) queries, + sum(left) as left, sum(right) as right, + (right - left) / right average_time_change + from queries + group by test + order by abs(average_time_change) desc + ) + ; + +create table unstable_tests_tsv engine File(TSV, 'report/unstable-tests.tsv') as + select test, sum(unstable_show) total_unstable, sum(changed_show) total_changed + from queries + group by test + order by total_unstable + total_changed desc + ; + +create table test_perf_changes_tsv engine File(TSV, 'report/test-perf-changes.tsv') as + select test, + queries, + coalesce(total_unstable, 0) total_unstable, + coalesce(total_changed, 0) total_changed, + total_unstable + total_changed total_bad, + coalesce(toString(floor(average_time_change, 3)), '??') average_time_change_str + from test_time_changes_tsv + full join unstable_tests_tsv + using test + where (abs(average_time_change) > 0.05 and queries > 5) + or (total_bad > 0) + order by total_bad desc, average_time_change desc + settings join_use_nulls = 1 + ; create table query_time engine Memory as select * from file('analyze/client-times.tsv', TSV, @@ -465,8 +498,8 @@ create table all_tests_tsv engine File(TSV, 'report/all-queries.tsv') as select changed_fail, unstable_fail, left, right, diff, floor(left > right ? left / right : right / left, 3), - stat_threshold, test, query_display_name - from queries order by test, query_display_name; + stat_threshold, test, query_index, query_display_name + from queries order by test, query_index; -- new report for all queries with all metrics (no page yet) create table all_query_metrics_tsv engine File(TSV, 'report/all-query-metrics.tsv') as @@ -583,7 +616,7 @@ create table metric_devation engine File(TSVWithNamesAndTypes, union all select * from unstable_run_traces union all select * from unstable_run_metrics_2) mm group by test, query_index, metric - having d > 0.5 + having d > 0.5 and q[3] > 5 ) metrics left join query_display_names using (test, query_index) order by test, query_index, d desc diff --git a/docker/test/performance-comparison/config/config.d/perf-comparison-tweaks-config.xml b/docker/test/performance-comparison/config/config.d/perf-comparison-tweaks-config.xml index e41ab8eb75d..5dcc3c51eca 100644 --- a/docker/test/performance-comparison/config/config.d/perf-comparison-tweaks-config.xml +++ b/docker/test/performance-comparison/config/config.d/perf-comparison-tweaks-config.xml @@ -19,6 +19,5 @@ 1000 - 0 1000000000 diff --git a/docker/test/performance-comparison/config/users.d/perf-comparison-tweaks-users.xml b/docker/test/performance-comparison/config/users.d/perf-comparison-tweaks-users.xml index ce1416ac9dc..6e3e3df5d39 100644 --- a/docker/test/performance-comparison/config/users.d/perf-comparison-tweaks-users.xml +++ b/docker/test/performance-comparison/config/users.d/perf-comparison-tweaks-users.xml @@ -5,6 +5,7 @@ 0 1 1 + 1 diff --git a/docker/test/performance-comparison/entrypoint.sh b/docker/test/performance-comparison/entrypoint.sh index ef62c8981e9..fb3c80ba8a1 100755 --- a/docker/test/performance-comparison/entrypoint.sh +++ b/docker/test/performance-comparison/entrypoint.sh @@ -83,10 +83,17 @@ if [ "$REF_PR" == "" ]; then echo Reference PR is not specified ; exit 1 ; fi if [ "$PR_TO_TEST" != "0" ] then - # Prepare the list of tests changed in the PR for use by compare.sh. Compare to - # merge base, because master might be far in the future and have unrelated test - # changes. - git -C ch diff --name-only "$SHA_TO_TEST" "$(git -C ch merge-base "$SHA_TO_TEST" master)" -- tests/performance | tee changed-tests.txt + # If the PR only changes the tests and nothing else, prepare a list of these + # tests for use by compare.sh. Compare to merge base, because master might be + # far in the future and have unrelated test changes. + base=$(git -C ch merge-base "$SHA_TO_TEST" master) + git -C ch diff --name-only "$base" "$SHA_TO_TEST" | tee changed-tests.txt + if grep -vq '^tests/performance' changed-tests.txt + then + # Have some other changes besides the tests, so truncate the test list, + # meaning, run all tests. + : > changed-tests.txt + fi fi # Set python output encoding so that we can print queries with Russian letters. @@ -124,5 +131,5 @@ done dmesg -T > dmesg.log -7z a /output/output.7z ./*.{log,tsv,html,txt,rep,svg} {right,left}/{performance,db/preprocessed_configs,scripts} report analyze +7z a '-x!*/tmp' /output/output.7z ./*.{log,tsv,html,txt,rep,svg,columns} {right,left}/{performance,db/preprocessed_configs,scripts} report analyze benchmark cp compare.log /output diff --git a/docker/test/performance-comparison/perf.py b/docker/test/performance-comparison/perf.py index ac506d046b1..308d4760b48 100755 --- a/docker/test/performance-comparison/perf.py +++ b/docker/test/performance-comparison/perf.py @@ -100,11 +100,20 @@ for c in connections: report_stage_end('drop1') -# Apply settings +# Apply settings. +# If there are errors, report them and continue -- maybe a new test uses a setting +# that is not in master, but the queries can still run. If we have multiple +# settings and one of them throws an exception, all previous settings for this +# connection will be reset, because the driver reconnects on error (not +# configurable). So the end result is uncertain, but hopefully we'll be able to +# run at least some queries. settings = root.findall('settings/*') for c in connections: for s in settings: - c.execute("set {} = '{}'".format(s.tag, s.text)) + try: + c.execute("set {} = '{}'".format(s.tag, s.text)) + except: + print(traceback.format_exc(), file=sys.stderr) report_stage_end('settings') diff --git a/docker/test/performance-comparison/report.py b/docker/test/performance-comparison/report.py index 866e78da098..9db37932aea 100755 --- a/docker/test/performance-comparison/report.py +++ b/docker/test/performance-comparison/report.py @@ -207,7 +207,8 @@ if args.report == 'main': 'p < 0.001 threshold', # 3 # Failed # 4 'Test', # 5 - 'Query', # 6 + '#', # 6 + 'Query', # 7 ] print(tableHeader(columns)) @@ -248,7 +249,8 @@ if args.report == 'main': 'p < 0.001 threshold', #3 # Failed #4 'Test', #5 - 'Query' #6 + '#', #6 + 'Query' #7 ] print(tableStart('Unstable queries')) @@ -272,9 +274,9 @@ if args.report == 'main': skipped_tests_rows = tsvRows('analyze/skipped-tests.tsv') printSimpleTable('Skipped tests', ['Test', 'Reason'], skipped_tests_rows) - printSimpleTable('Tests with most unstable queries', - ['Test', 'Unstable', 'Changed perf', 'Total not OK'], - tsvRows('report/bad-tests.tsv')) + printSimpleTable('Test performance changes', + ['Test', 'Queries', 'Unstable', 'Changed perf', 'Total not OK', 'Avg relative time diff'], + tsvRows('report/test-perf-changes.tsv')) def print_test_times(): global slow_average_tests @@ -357,7 +359,7 @@ if args.report == 'main': error_tests += slow_average_tests if error_tests: status = 'failure' - message_array.append(str(error_tests) + ' errors') + message_array.insert(0, str(error_tests) + ' errors') if message_array: message = ', '.join(message_array) @@ -391,7 +393,8 @@ elif args.report == 'all-queries': 'Times speedup / slowdown', #5 'p < 0.001 threshold', #6 'Test', #7 - 'Query', #8 + '#', #8 + 'Query', #9 ] print(tableStart('All query times')) diff --git a/docker/test/stateful/Dockerfile b/docker/test/stateful/Dockerfile index d751a2532bc..3aff49bf5a1 100644 --- a/docker/test/stateful/Dockerfile +++ b/docker/test/stateful/Dockerfile @@ -24,6 +24,8 @@ CMD dpkg -i package_folder/clickhouse-common-static_*.deb; \ ln -s /usr/share/clickhouse-test/config/listen.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/part_log.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/text_log.xml /etc/clickhouse-server/config.d/; \ + ln -s /usr/share/clickhouse-test/config/metric_log.xml /etc/clickhouse-server/config.d/; \ + ln -s /usr/share/clickhouse-test/config/log_queries.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/readonly.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/ints_dictionary.xml /etc/clickhouse-server/; \ ln -s /usr/share/clickhouse-test/config/strings_dictionary.xml /etc/clickhouse-server/; \ diff --git a/docker/test/stateful_with_coverage/run.sh b/docker/test/stateful_with_coverage/run.sh index 5530aadb4ca..b946f5b187d 100755 --- a/docker/test/stateful_with_coverage/run.sh +++ b/docker/test/stateful_with_coverage/run.sh @@ -59,7 +59,9 @@ ln -s /usr/share/clickhouse-test/config/zookeeper.xml /etc/clickhouse-server/con ln -s /usr/share/clickhouse-test/config/listen.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/part_log.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/text_log.xml /etc/clickhouse-server/config.d/; \ + ln -s /usr/share/clickhouse-test/config/metric_log.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/query_masking_rules.xml /etc/clickhouse-server/config.d/; \ + ln -s /usr/share/clickhouse-test/config/log_queries.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/readonly.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/ints_dictionary.xml /etc/clickhouse-server/; \ ln -s /usr/share/clickhouse-test/config/strings_dictionary.xml /etc/clickhouse-server/; \ diff --git a/docker/test/stateless/Dockerfile b/docker/test/stateless/Dockerfile index 8fbaffe88bc..41a53f8a3f5 100644 --- a/docker/test/stateless/Dockerfile +++ b/docker/test/stateless/Dockerfile @@ -62,7 +62,9 @@ CMD dpkg -i package_folder/clickhouse-common-static_*.deb; \ ln -s /usr/share/clickhouse-test/config/listen.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/part_log.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/text_log.xml /etc/clickhouse-server/config.d/; \ + ln -s /usr/share/clickhouse-test/config/metric_log.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/query_masking_rules.xml /etc/clickhouse-server/config.d/; \ + ln -s /usr/share/clickhouse-test/config/log_queries.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/readonly.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/access_management.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/ints_dictionary.xml /etc/clickhouse-server/; \ diff --git a/docker/test/stateless_with_coverage/run.sh b/docker/test/stateless_with_coverage/run.sh index 12ed7a25b75..185dc95c783 100755 --- a/docker/test/stateless_with_coverage/run.sh +++ b/docker/test/stateless_with_coverage/run.sh @@ -50,7 +50,9 @@ ln -s /usr/share/clickhouse-test/config/zookeeper.xml /etc/clickhouse-server/con ln -s /usr/share/clickhouse-test/config/listen.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/part_log.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/text_log.xml /etc/clickhouse-server/config.d/; \ + ln -s /usr/share/clickhouse-test/config/metric_log.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/share/clickhouse-test/config/query_masking_rules.xml /etc/clickhouse-server/config.d/; \ + ln -s /usr/share/clickhouse-test/config/log_queries.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/readonly.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/access_management.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/ints_dictionary.xml /etc/clickhouse-server/; \ diff --git a/docker/test/stress/Dockerfile b/docker/test/stress/Dockerfile index 66f5135a4a4..a5aa3bbf004 100644 --- a/docker/test/stress/Dockerfile +++ b/docker/test/stress/Dockerfile @@ -31,6 +31,7 @@ CMD dpkg -i package_folder/clickhouse-common-static_*.deb; \ dpkg -i package_folder/clickhouse-server_*.deb; \ dpkg -i package_folder/clickhouse-client_*.deb; \ dpkg -i package_folder/clickhouse-test_*.deb; \ + ln -s /usr/share/clickhouse-test/config/log_queries.xml /etc/clickhouse-server/users.d/; \ ln -s /usr/share/clickhouse-test/config/part_log.xml /etc/clickhouse-server/config.d/; \ ln -s /usr/lib/llvm-9/bin/llvm-symbolizer /usr/bin/llvm-symbolizer; \ echo "TSAN_OPTIONS='halt_on_error=1 history_size=7 ignore_noninstrumented_modules=1 verbosity=1'" >> /etc/environment; \ diff --git a/docs/en/development/build.md b/docs/en/development/build.md index b9b22c737ac..842e565b132 100644 --- a/docs/en/development/build.md +++ b/docs/en/development/build.md @@ -5,9 +5,13 @@ toc_title: How to Build ClickHouse on Linux # How to Build ClickHouse for Development {#how-to-build-clickhouse-for-development} -The following tutorial is based on the Ubuntu Linux system. -With appropriate changes, it should also work on any other Linux distribution. -Supported platforms: x86\_64 and AArch64. Support for Power9 is experimental. +The following tutorial is based on the Ubuntu Linux system. With appropriate changes, it should also work on any other Linux distribution. + +Supported platforms: + +- x86\_64 +- AArch64 +- Power9 (experimental) ## Install Git, CMake, Python and Ninja {#install-git-cmake-python-and-ninja} @@ -21,8 +25,18 @@ Or cmake3 instead of cmake on older systems. There are several ways to do this. +### Install from Repository {#install-from-repository} + +On Ubuntu 19.10 or newer: +``` +$ sudo apt-get update +$ sudo apt-get install gcc-9 g++-9 +``` + ### Install from a PPA Package {#install-from-a-ppa-package} +On older Ubuntu: + ``` bash $ sudo apt-get install software-properties-common $ sudo apt-add-repository ppa:ubuntu-toolchain-r/test @@ -32,7 +46,7 @@ $ sudo apt-get install gcc-9 g++-9 ### Install from Sources {#install-from-sources} -Look at [utils/ci/build-gcc-from-sources.sh](https://github.com/ClickHouse/ClickHouse/blob/master/utils/ci/build-gcc-from-sources.sh) +See [utils/ci/build-gcc-from-sources.sh](https://github.com/ClickHouse/ClickHouse/blob/master/utils/ci/build-gcc-from-sources.sh) ## Use GCC 9 for Builds {#use-gcc-9-for-builds} @@ -61,7 +75,6 @@ $ mkdir build $ cd build $ cmake .. $ ninja -$ cd .. ``` To create an executable, run `ninja clickhouse`. diff --git a/docs/en/development/developer-instruction.md b/docs/en/development/developer-instruction.md index 3a6774037c1..3776c9b513f 100644 --- a/docs/en/development/developer-instruction.md +++ b/docs/en/development/developer-instruction.md @@ -137,7 +137,7 @@ Official Yandex builds currently use GCC because it generates machine code of sl To install GCC on Ubuntu run: `sudo apt install gcc g++` -Check the version of gcc: `gcc --version`. If it is below 9, then follow the instruction here: https://clickhouse.tech/docs/en/development/build/\#install-gcc-9. +Check the version of gcc: `gcc --version`. If it is below 9, then follow the instruction here: https://clickhouse.tech/docs/en/development/build/#install-gcc-9. Mac OS X build is supported only for Clang. Just run `brew install llvm` @@ -245,7 +245,7 @@ The Code Style Guide: https://clickhouse.tech/docs/en/development/style/ Writing tests: https://clickhouse.tech/docs/en/development/tests/ -List of tasks: https://github.com/ClickHouse/ClickHouse/blob/master/testsructions/easy\_tasks\_sorted\_en.md +List of tasks: https://github.com/ClickHouse/ClickHouse/contribute ## Test Data {#test-data} diff --git a/docs/en/engines/table-engines/index.md b/docs/en/engines/table-engines/index.md index ac23120b9cd..ee28bfda905 100644 --- a/docs/en/engines/table-engines/index.md +++ b/docs/en/engines/table-engines/index.md @@ -60,7 +60,7 @@ Engines in the family: - [Distributed](special/distributed.md#distributed) - [MaterializedView](special/materializedview.md#materializedview) - [Dictionary](special/dictionary.md#dictionary) -- [Merge](special/merge.md#merge +- [Merge](special/merge.md#merge) - [File](special/file.md#file) - [Null](special/null.md#null) - [Set](special/set.md#set) diff --git a/docs/en/engines/table-engines/integrations/kafka.md b/docs/en/engines/table-engines/integrations/kafka.md index d7824443c1d..97d5333b0ad 100644 --- a/docs/en/engines/table-engines/integrations/kafka.md +++ b/docs/en/engines/table-engines/integrations/kafka.md @@ -174,5 +174,6 @@ For a list of possible configuration options, see the [librdkafka configuration **See Also** - [Virtual columns](../index.md#table_engines-virtual_columns) +- [background_schedule_pool_size](../../../operations/settings/settings.md#background_schedule_pool_size) [Original article](https://clickhouse.tech/docs/en/operations/table_engines/kafka/) diff --git a/docs/en/engines/table-engines/mergetree-family/mergetree.md b/docs/en/engines/table-engines/mergetree-family/mergetree.md index 20710941a67..33d12293172 100644 --- a/docs/en/engines/table-engines/mergetree-family/mergetree.md +++ b/docs/en/engines/table-engines/mergetree-family/mergetree.md @@ -41,8 +41,8 @@ CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1, INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2 ) ENGINE = MergeTree() +ORDER BY expr [PARTITION BY expr] -[ORDER BY expr] [PRIMARY KEY expr] [SAMPLE BY expr] [TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...] @@ -58,23 +58,27 @@ For a description of parameters, see the [CREATE query description](../../../sql - `ENGINE` — Name and parameters of the engine. `ENGINE = MergeTree()`. The `MergeTree` engine does not have parameters. -- `PARTITION BY` — The [partitioning key](custom-partitioning-key.md). +- `ORDER BY` — The sorting key. + + A tuple of column names or arbitrary expressions. Example: `ORDER BY (CounterID, EventDate)`. + + ClickHouse uses the sorting key as a primary key if the primary key is not defined obviously by the `PRIMARY KEY` clause. + + Use the `ORDER BY tuple()` syntax, if you don't need sorting. See [Selecting the Primary Key](#selecting-the-primary-key). + +- `PARTITION BY` — The [partitioning key](custom-partitioning-key.md). Optional. For partitioning by month, use the `toYYYYMM(date_column)` expression, where `date_column` is a column with a date of the type [Date](../../../sql-reference/data-types/date.md). The partition names here have the `"YYYYMM"` format. -- `ORDER BY` — The sorting key. - - A tuple of columns or arbitrary expressions. Example: `ORDER BY (CounterID, EventDate)`. - -- `PRIMARY KEY` — The primary key if it [differs from the sorting key](#choosing-a-primary-key-that-differs-from-the-sorting-key). +- `PRIMARY KEY` — The primary key if it [differs from the sorting key](#choosing-a-primary-key-that-differs-from-the-sorting-key). Optional. By default the primary key is the same as the sorting key (which is specified by the `ORDER BY` clause). Thus in most cases it is unnecessary to specify a separate `PRIMARY KEY` clause. -- `SAMPLE BY` — An expression for sampling. +- `SAMPLE BY` — An expression for sampling. Optional. If a sampling expression is used, the primary key must contain it. Example: `SAMPLE BY intHash32(UserID) ORDER BY (CounterID, EventDate, intHash32(UserID))`. -- `TTL` — A list of rules specifying storage duration of rows and defining logic of automatic parts movement [between disks and volumes](#table_engine-mergetree-multiple-volumes). +- `TTL` — A list of rules specifying storage duration of rows and defining logic of automatic parts movement [between disks and volumes](#table_engine-mergetree-multiple-volumes). Optional. Expression must have one `Date` or `DateTime` column as a result. Example: `TTL date + INTERVAL 1 DAY` @@ -83,7 +87,7 @@ For a description of parameters, see the [CREATE query description](../../../sql For more details, see [TTL for columns and tables](#table_engine-mergetree-ttl) -- `SETTINGS` — Additional parameters that control the behavior of the `MergeTree`: +- `SETTINGS` — Additional parameters that control the behavior of the `MergeTree` (optional): - `index_granularity` — Maximum number of data rows between the marks of an index. Default value: 8192. See [Data Storage](#mergetree-data-storage). - `index_granularity_bytes` — Maximum size of data granules in bytes. Default value: 10Mb. To restrict the granule size only by number of rows, set to 0 (not recommended). See [Data Storage](#mergetree-data-storage). @@ -198,6 +202,10 @@ The number of columns in the primary key is not explicitly limited. Depending on A long primary key will negatively affect the insert performance and memory consumption, but extra columns in the primary key do not affect ClickHouse performance during `SELECT` queries. +You can create a table without a primary key using the `ORDER BY tuple()` syntax. In this case, ClickHouse stores data in the order of inserting. If you want to save data order when inserting data by `INSERT ... SELECT` queries, set [max_insert_threads = 1](../../../operations/settings/settings.md#settings-max-insert-threads). + +To select data in the initial order, use [single-threaded](../../../operations/settings/settings.md#settings-max_threads) `SELECT` queries. + ### Choosing a Primary Key that Differs from the Sorting Key {#choosing-a-primary-key-that-differs-from-the-sorting-key} It is possible to specify a primary key (an expression with values that are written in the index file for each mark) that is different from the sorting key (an expression for sorting the rows in data parts). In this case the primary key expression tuple must be a prefix of the sorting key expression tuple. @@ -332,8 +340,8 @@ The `set` index can be used with all functions. Function subsets for other index |------------------------------------------------------------------------------------------------------------|-------------|--------|-------------|-------------|---------------| | [equals (=, ==)](../../../sql-reference/functions/comparison-functions.md#function-equals) | ✔ | ✔ | ✔ | ✔ | ✔ | | [notEquals(!=, \<\>)](../../../sql-reference/functions/comparison-functions.md#function-notequals) | ✔ | ✔ | ✔ | ✔ | ✔ | -| [like](../../../sql-reference/functions/string-search-functions.md#function-like) | ✔ | ✔ | ✔ | ✗ | ✗ | -| [notLike](../../../sql-reference/functions/string-search-functions.md#function-notlike) | ✔ | ✔ | ✔ | ✗ | ✗ | +| [like](../../../sql-reference/functions/string-search-functions.md#function-like) | ✔ | ✔ | ✔ | ✔ | ✔ | +| [notLike](../../../sql-reference/functions/string-search-functions.md#function-notlike) | ✔ | ✔ | ✗ | ✗ | ✗ | | [startsWith](../../../sql-reference/functions/string-functions.md#startswith) | ✔ | ✔ | ✔ | ✔ | ✗ | | [endsWith](../../../sql-reference/functions/string-functions.md#endswith) | ✗ | ✗ | ✔ | ✔ | ✗ | | [multiSearchAny](../../../sql-reference/functions/string-search-functions.md#function-multisearchany) | ✗ | ✗ | ✔ | ✗ | ✗ | @@ -349,7 +357,8 @@ The `set` index can be used with all functions. Function subsets for other index Functions with a constant argument that is less than ngram size can’t be used by `ngrambf_v1` for query optimization. -Bloom filters can have false positive matches, so the `ngrambf_v1`, `tokenbf_v1`, and `bloom_filter` indexes can’t be used for optimizing queries where the result of a function is expected to be false, for example: +!!! note "Note" + Bloom filters can have false positive matches, so the `ngrambf_v1`, `tokenbf_v1`, and `bloom_filter` indexes can’t be used for optimizing queries where the result of a function is expected to be false, for example: - Can be optimized: - `s LIKE '%test%'` @@ -623,6 +632,8 @@ SETTINGS storage_policy = 'moving_from_ssd_to_hdd' The `default` storage policy implies using only one volume, which consists of only one disk given in ``. Once a table is created, its storage policy cannot be changed. +The number of threads performing background moves of data parts can be changed by [background_move_pool_size](../../../operations/settings/settings.md#background_move_pool_size) setting. + ### Details {#details} In the case of `MergeTree` tables, data is getting to disk in different ways: diff --git a/docs/en/engines/table-engines/mergetree-family/replication.md b/docs/en/engines/table-engines/mergetree-family/replication.md index d813f76d127..aa4e2e87d95 100644 --- a/docs/en/engines/table-engines/mergetree-family/replication.md +++ b/docs/en/engines/table-engines/mergetree-family/replication.md @@ -63,7 +63,7 @@ For each `INSERT` query, approximately ten entries are added to ZooKeeper throug For very large clusters, you can use different ZooKeeper clusters for different shards. However, this hasn’t proven necessary on the Yandex.Metrica cluster (approximately 300 servers). -Replication is asynchronous and multi-master. `INSERT` queries (as well as `ALTER`) can be sent to any available server. Data is inserted on the server where the query is run, and then it is copied to the other servers. Because it is asynchronous, recently inserted data appears on the other replicas with some latency. If part of the replicas are not available, the data is written when they become available. If a replica is available, the latency is the amount of time it takes to transfer the block of compressed data over the network. +Replication is asynchronous and multi-master. `INSERT` queries (as well as `ALTER`) can be sent to any available server. Data is inserted on the server where the query is run, and then it is copied to the other servers. Because it is asynchronous, recently inserted data appears on the other replicas with some latency. If part of the replicas are not available, the data is written when they become available. If a replica is available, the latency is the amount of time it takes to transfer the block of compressed data over the network. The number of threads performing background tasks for replicated tables can be set by [background_schedule_pool_size](../../../operations/settings/settings.md#background_schedule_pool_size) setting. By default, an INSERT query waits for confirmation of writing the data from only one replica. If the data was successfully written to only one replica and the server with this replica ceases to exist, the stored data will be lost. To enable getting confirmation of data writes from multiple replicas, use the `insert_quorum` option. @@ -215,4 +215,8 @@ After this, you can launch the server, create a `MergeTree` table, move the data If the data in ZooKeeper was lost or damaged, you can save data by moving it to an unreplicated table as described above. +**See also** + +- [background_schedule_pool_size](../../../operations/settings/settings.md#background_schedule_pool_size) + [Original article](https://clickhouse.tech/docs/en/operations/table_engines/replication/) diff --git a/docs/en/engines/table-engines/special/buffer.md b/docs/en/engines/table-engines/special/buffer.md index 12143044f21..bf6c08f8f6c 100644 --- a/docs/en/engines/table-engines/special/buffer.md +++ b/docs/en/engines/table-engines/special/buffer.md @@ -3,7 +3,7 @@ toc_priority: 45 toc_title: Buffer --- -# Buffer {#buffer} +# Buffer Table Engine {#buffer} Buffers the data to write in RAM, periodically flushing it to another table. During the read operation, data is read from the buffer and the other table simultaneously. @@ -34,9 +34,9 @@ Example: CREATE TABLE merge.hits_buffer AS merge.hits ENGINE = Buffer(merge, hits, 16, 10, 100, 10000, 1000000, 10000000, 100000000) ``` -Creating a ‘merge.hits\_buffer’ table with the same structure as ‘merge.hits’ and using the Buffer engine. When writing to this table, data is buffered in RAM and later written to the ‘merge.hits’ table. 16 buffers are created. The data in each of them is flushed if either 100 seconds have passed, or one million rows have been written, or 100 MB of data have been written; or if simultaneously 10 seconds have passed and 10,000 rows and 10 MB of data have been written. For example, if just one row has been written, after 100 seconds it will be flushed, no matter what. But if many rows have been written, the data will be flushed sooner. +Creating a `merge.hits_buffer` table with the same structure as `merge.hits` and using the Buffer engine. When writing to this table, data is buffered in RAM and later written to the ‘merge.hits’ table. 16 buffers are created. The data in each of them is flushed if either 100 seconds have passed, or one million rows have been written, or 100 MB of data have been written; or if simultaneously 10 seconds have passed and 10,000 rows and 10 MB of data have been written. For example, if just one row has been written, after 100 seconds it will be flushed, no matter what. But if many rows have been written, the data will be flushed sooner. -When the server is stopped, with DROP TABLE or DETACH TABLE, buffer data is also flushed to the destination table. +When the server is stopped, with `DROP TABLE` or `DETACH TABLE`, buffer data is also flushed to the destination table. You can set empty strings in single quotation marks for the database and table name. This indicates the absence of a destination table. In this case, when the data flush conditions are reached, the buffer is simply cleared. This may be useful for keeping a window of data in memory. @@ -52,11 +52,11 @@ If you need to run ALTER for a subordinate table and the Buffer table, we recomm If the server is restarted abnormally, the data in the buffer is lost. -FINAL and SAMPLE do not work correctly for Buffer tables. These conditions are passed to the destination table, but are not used for processing data in the buffer. If these features are required we recommend only using the Buffer table for writing, while reading from the destination table. +`FINAL` and `SAMPLE` do not work correctly for Buffer tables. These conditions are passed to the destination table, but are not used for processing data in the buffer. If these features are required we recommend only using the Buffer table for writing, while reading from the destination table. When adding data to a Buffer, one of the buffers is locked. This causes delays if a read operation is simultaneously being performed from the table. -Data that is inserted to a Buffer table may end up in the subordinate table in a different order and in different blocks. Because of this, a Buffer table is difficult to use for writing to a CollapsingMergeTree correctly. To avoid problems, you can set ‘num\_layers’ to 1. +Data that is inserted to a Buffer table may end up in the subordinate table in a different order and in different blocks. Because of this, a Buffer table is difficult to use for writing to a CollapsingMergeTree correctly. To avoid problems, you can set `num_layers` to 1. If the destination table is replicated, some expected characteristics of replicated tables are lost when writing to a Buffer table. The random changes to the order of rows and sizes of data parts cause data deduplication to quit working, which means it is not possible to have a reliable ‘exactly once’ write to replicated tables. diff --git a/docs/en/engines/table-engines/special/dictionary.md b/docs/en/engines/table-engines/special/dictionary.md index 31806b54ff3..086ad53fab5 100644 --- a/docs/en/engines/table-engines/special/dictionary.md +++ b/docs/en/engines/table-engines/special/dictionary.md @@ -3,15 +3,17 @@ toc_priority: 35 toc_title: Dictionary --- -# Dictionary {#dictionary} +# Dictionary Table Engine {#dictionary} The `Dictionary` engine displays the [dictionary](../../../sql-reference/dictionaries/external-dictionaries/external-dicts.md) data as a ClickHouse table. +## Example + As an example, consider a dictionary of `products` with the following configuration: ``` xml - + products @@ -36,7 +38,7 @@ As an example, consider a dictionary of `products` with the following configurat - + ``` diff --git a/docs/en/engines/table-engines/special/distributed.md b/docs/en/engines/table-engines/special/distributed.md index 98ca70134b5..11245bbf262 100644 --- a/docs/en/engines/table-engines/special/distributed.md +++ b/docs/en/engines/table-engines/special/distributed.md @@ -3,14 +3,14 @@ toc_priority: 33 toc_title: Distributed --- -# Distributed {#distributed} +# Distributed Table Engine {#distributed} -**Tables with Distributed engine do not store any data by themself**, but allow distributed query processing on multiple servers. +Tables with Distributed engine do not store any data by their own, but allow distributed query processing on multiple servers. Reading is automatically parallelized. During a read, the table indexes on remote servers are used, if there are any. The Distributed engine accepts parameters: -- the cluster name in the server’s config file +- the cluster name in the server's config file - the name of a remote database @@ -31,13 +31,13 @@ Example: Distributed(logs, default, hits[, sharding_key[, policy_name]]) ``` -Data will be read from all servers in the ‘logs’ cluster, from the default.hits table located on every server in the cluster. +Data will be read from all servers in the `logs` cluster, from the default.hits table located on every server in the cluster. Data is not only read but is partially processed on the remote servers (to the extent that this is possible). For example, for a query with GROUP BY, data will be aggregated on remote servers, and the intermediate states of aggregate functions will be sent to the requestor server. Then data will be further aggregated. Instead of the database name, you can use a constant expression that returns a string. For example: currentDatabase(). -logs – The cluster name in the server’s config file. +logs – The cluster name in the server's config file. Clusters are set like this: @@ -75,15 +75,15 @@ Clusters are set like this: ``` -Here a cluster is defined with the name ‘logs’ that consists of two shards, each of which contains two replicas. +Here a cluster is defined with the name `logs` that consists of two shards, each of which contains two replicas. Shards refer to the servers that contain different parts of the data (in order to read all the data, you must access all the shards). Replicas are duplicating servers (in order to read all the data, you can access the data on any one of the replicas). Cluster names must not contain dots. The parameters `host`, `port`, and optionally `user`, `password`, `secure`, `compression` are specified for each server: -- `host` – The address of the remote server. You can use either the domain or the IPv4 or IPv6 address. If you specify the domain, the server makes a DNS request when it starts, and the result is stored as long as the server is running. If the DNS request fails, the server doesn’t start. If you change the DNS record, restart the server. -- `port` – The TCP port for messenger activity (‘tcp\_port’ in the config, usually set to 9000). Do not confuse it with http\_port. +- `host` – The address of the remote server. You can use either the domain or the IPv4 or IPv6 address. If you specify the domain, the server makes a DNS request when it starts, and the result is stored as long as the server is running. If the DNS request fails, the server doesn't start. If you change the DNS record, restart the server. +- `port` – The TCP port for messenger activity (`tcp_port` in the config, usually set to 9000). Do not confuse it with http\_port. - `user` – Name of the user for connecting to a remote server. Default value: default. This user must have access to connect to the specified server. Access is configured in the users.xml file. For more information, see the section [Access rights](../../../operations/access-rights.md). - `password` – The password for connecting to a remote server (not masked). Default value: empty string. - `secure` - Use ssl for connection, usually you also should define `port` = 9440. Server should listen on `9440` and have correct certificates. @@ -97,44 +97,44 @@ You can specify just one of the shards (in this case, query processing should be You can specify as many clusters as you wish in the configuration. -To view your clusters, use the ‘system.clusters’ table. +To view your clusters, use the `system.clusters` table. -The Distributed engine allows working with a cluster like a local server. However, the cluster is inextensible: you must write its configuration in the server config file (even better, for all the cluster’s servers). +The Distributed engine allows working with a cluster like a local server. However, the cluster is inextensible: you must write its configuration in the server config file (even better, for all the cluster's servers). -The Distributed engine requires writing clusters to the config file. Clusters from the config file are updated on the fly, without restarting the server. If you need to send a query to an unknown set of shards and replicas each time, you don’t need to create a Distributed table – use the ‘remote’ table function instead. See the section [Table functions](../../../sql-reference/table-functions/index.md). +The Distributed engine requires writing clusters to the config file. Clusters from the config file are updated on the fly, without restarting the server. If you need to send a query to an unknown set of shards and replicas each time, you don't need to create a Distributed table – use the `remote` table function instead. See the section [Table functions](../../../sql-reference/table-functions/index.md). There are two methods for writing data to a cluster: -First, you can define which servers to write which data to and perform the write directly on each shard. In other words, perform INSERT in the tables that the distributed table “looks at”. This is the most flexible solution as you can use any sharding scheme, which could be non-trivial due to the requirements of the subject area. This is also the most optimal solution since data can be written to different shards completely independently. +First, you can define which servers to write which data to and perform the write directly on each shard. In other words, perform INSERT in the tables that the distributed table "looks at". This is the most flexible solution as you can use any sharding scheme, which could be non-trivial due to the requirements of the subject area. This is also the most optimal solution since data can be written to different shards completely independently. -Second, you can perform INSERT in a Distributed table. In this case, the table will distribute the inserted data across the servers itself. In order to write to a Distributed table, it must have a sharding key set (the last parameter). In addition, if there is only one shard, the write operation works without specifying the sharding key, since it doesn’t mean anything in this case. +Second, you can perform INSERT in a Distributed table. In this case, the table will distribute the inserted data across the servers itself. In order to write to a Distributed table, it must have a sharding key set (the last parameter). In addition, if there is only one shard, the write operation works without specifying the sharding key, since it doesn't mean anything in this case. Each shard can have a weight defined in the config file. By default, the weight is equal to one. Data is distributed across shards in the amount proportional to the shard weight. For example, if there are two shards and the first has a weight of 9 while the second has a weight of 10, the first will be sent 9 / 19 parts of the rows, and the second will be sent 10 / 19. -Each shard can have the ‘internal\_replication’ parameter defined in the config file. +Each shard can have the `internal_replication` parameter defined in the config file. -If this parameter is set to ‘true’, the write operation selects the first healthy replica and writes data to it. Use this alternative if the Distributed table “looks at” replicated tables. In other words, if the table where data will be written is going to replicate them itself. +If this parameter is set to `true`, the write operation selects the first healthy replica and writes data to it. Use this alternative if the Distributed table "looks at" replicated tables. In other words, if the table where data will be written is going to replicate them itself. -If it is set to ‘false’ (the default), data is written to all replicas. In essence, this means that the Distributed table replicates data itself. This is worse than using replicated tables, because the consistency of replicas is not checked, and over time they will contain slightly different data. +If it is set to `false` (the default), data is written to all replicas. In essence, this means that the Distributed table replicates data itself. This is worse than using replicated tables, because the consistency of replicas is not checked, and over time they will contain slightly different data. -To select the shard that a row of data is sent to, the sharding expression is analyzed, and its remainder is taken from dividing it by the total weight of the shards. The row is sent to the shard that corresponds to the half-interval of the remainders from ‘prev\_weight’ to ‘prev\_weights + weight’, where ‘prev\_weights’ is the total weight of the shards with the smallest number, and ‘weight’ is the weight of this shard. For example, if there are two shards, and the first has a weight of 9 while the second has a weight of 10, the row will be sent to the first shard for the remainders from the range \[0, 9), and to the second for the remainders from the range \[9, 19). +To select the shard that a row of data is sent to, the sharding expression is analyzed, and its remainder is taken from dividing it by the total weight of the shards. The row is sent to the shard that corresponds to the half-interval of the remainders from `prev_weight` to `prev_weights + weight`, where `prev_weights` is the total weight of the shards with the smallest number, and `weight` is the weight of this shard. For example, if there are two shards, and the first has a weight of 9 while the second has a weight of 10, the row will be sent to the first shard for the remainders from the range \[0, 9), and to the second for the remainders from the range \[9, 19). -The sharding expression can be any expression from constants and table columns that returns an integer. For example, you can use the expression ‘rand()’ for random distribution of data, or ‘UserID’ for distribution by the remainder from dividing the user’s ID (then the data of a single user will reside on a single shard, which simplifies running IN and JOIN by users). If one of the columns is not distributed evenly enough, you can wrap it in a hash function: intHash64(UserID). +The sharding expression can be any expression from constants and table columns that returns an integer. For example, you can use the expression `rand()` for random distribution of data, or `UserID` for distribution by the remainder from dividing the user's ID (then the data of a single user will reside on a single shard, which simplifies running IN and JOIN by users). If one of the columns is not distributed evenly enough, you can wrap it in a hash function: intHash64(UserID). -A simple reminder from the division is a limited solution for sharding and isn’t always appropriate. It works for medium and large volumes of data (dozens of servers), but not for very large volumes of data (hundreds of servers or more). In the latter case, use the sharding scheme required by the subject area, rather than using entries in Distributed tables. +A simple reminder from the division is a limited solution for sharding and isn't always appropriate. It works for medium and large volumes of data (dozens of servers), but not for very large volumes of data (hundreds of servers or more). In the latter case, use the sharding scheme required by the subject area, rather than using entries in Distributed tables. -SELECT queries are sent to all the shards and work regardless of how data is distributed across the shards (they can be distributed completely randomly). When you add a new shard, you don’t have to transfer the old data to it. You can write new data with a heavier weight – the data will be distributed slightly unevenly, but queries will work correctly and efficiently. +SELECT queries are sent to all the shards and work regardless of how data is distributed across the shards (they can be distributed completely randomly). When you add a new shard, you don't have to transfer the old data to it. You can write new data with a heavier weight – the data will be distributed slightly unevenly, but queries will work correctly and efficiently. You should be concerned about the sharding scheme in the following cases: - Queries are used that require joining data (IN or JOIN) by a specific key. If data is sharded by this key, you can use local IN or JOIN instead of GLOBAL IN or GLOBAL JOIN, which is much more efficient. -- A large number of servers is used (hundreds or more) with a large number of small queries (queries of individual clients - websites, advertisers, or partners). In order for the small queries to not affect the entire cluster, it makes sense to locate data for a single client on a single shard. Alternatively, as we’ve done in Yandex.Metrica, you can set up bi-level sharding: divide the entire cluster into “layers”, where a layer may consist of multiple shards. Data for a single client is located on a single layer, but shards can be added to a layer as necessary, and data is randomly distributed within them. Distributed tables are created for each layer, and a single shared distributed table is created for global queries. +- A large number of servers is used (hundreds or more) with a large number of small queries (queries of individual clients - websites, advertisers, or partners). In order for the small queries to not affect the entire cluster, it makes sense to locate data for a single client on a single shard. Alternatively, as we've done in Yandex.Metrica, you can set up bi-level sharding: divide the entire cluster into "layers", where a layer may consist of multiple shards. Data for a single client is located on a single layer, but shards can be added to a layer as necessary, and data is randomly distributed within them. Distributed tables are created for each layer, and a single shared distributed table is created for global queries. -Data is written asynchronously. When inserted in the table, the data block is just written to the local file system. The data is sent to the remote servers in the background as soon as possible. The period for sending data is managed by the [distributed\_directory\_monitor\_sleep\_time\_ms](../../../operations/settings/settings.md#distributed_directory_monitor_sleep_time_ms) and [distributed\_directory\_monitor\_max\_sleep\_time\_ms](../../../operations/settings/settings.md#distributed_directory_monitor_max_sleep_time_ms) settings. The `Distributed` engine sends each file with inserted data separately, but you can enable batch sending of files with the [distributed\_directory\_monitor\_batch\_inserts](../../../operations/settings/settings.md#distributed_directory_monitor_batch_inserts) setting. This setting improves cluster performance by better utilizing local server and network resources. You should check whether data is sent successfully by checking the list of files (data waiting to be sent) in the table directory: `/var/lib/clickhouse/data/database/table/`. +Data is written asynchronously. When inserted in the table, the data block is just written to the local file system. The data is sent to the remote servers in the background as soon as possible. The period for sending data is managed by the [distributed\_directory\_monitor\_sleep\_time\_ms](../../../operations/settings/settings.md#distributed_directory_monitor_sleep_time_ms) and [distributed\_directory\_monitor\_max\_sleep\_time\_ms](../../../operations/settings/settings.md#distributed_directory_monitor_max_sleep_time_ms) settings. The `Distributed` engine sends each file with inserted data separately, but you can enable batch sending of files with the [distributed\_directory\_monitor\_batch\_inserts](../../../operations/settings/settings.md#distributed_directory_monitor_batch_inserts) setting. This setting improves cluster performance by better utilizing local server and network resources. You should check whether data is sent successfully by checking the list of files (data waiting to be sent) in the table directory: `/var/lib/clickhouse/data/database/table/`. The number of threads performing background tasks can be set by [background_distributed_schedule_pool_size](../../../operations/settings/settings.md#background_distributed_schedule_pool_size) setting. -If the server ceased to exist or had a rough restart (for example, after a device failure) after an INSERT to a Distributed table, the inserted data might be lost. If a damaged data part is detected in the table directory, it is transferred to the ‘broken’ subdirectory and no longer used. +If the server ceased to exist or had a rough restart (for example, after a device failure) after an INSERT to a Distributed table, the inserted data might be lost. If a damaged data part is detected in the table directory, it is transferred to the `broken` subdirectory and no longer used. -When the max\_parallel\_replicas option is enabled, query processing is parallelized across all replicas within a single shard. For more information, see the section [max\_parallel\_replicas](../../../operations/settings/settings.md#settings-max_parallel_replicas). +When the `max_parallel_replicas` option is enabled, query processing is parallelized across all replicas within a single shard. For more information, see the section [max\_parallel\_replicas](../../../operations/settings/settings.md#settings-max_parallel_replicas). ## Virtual Columns {#virtual-columns} @@ -146,5 +146,6 @@ When the max\_parallel\_replicas option is enabled, query processing is parallel **See Also** - [Virtual columns](index.md#table_engines-virtual_columns) +- [background_distributed_schedule_pool_size](../../../operations/settings/settings.md#background_distributed_schedule_pool_size) [Original article](https://clickhouse.tech/docs/en/operations/table_engines/distributed/) diff --git a/docs/en/engines/table-engines/special/external-data.md b/docs/en/engines/table-engines/special/external-data.md index de487aef154..a2f6c076196 100644 --- a/docs/en/engines/table-engines/special/external-data.md +++ b/docs/en/engines/table-engines/special/external-data.md @@ -1,11 +1,11 @@ --- -toc_priority: 34 -toc_title: External data +toc_priority: 45 +toc_title: External Data --- # External Data for Query Processing {#external-data-for-query-processing} -ClickHouse allows sending a server the data that is needed for processing a query, together with a SELECT query. This data is put in a temporary table (see the section “Temporary tables”) and can be used in the query (for example, in IN operators). +ClickHouse allows sending a server the data that is needed for processing a query, together with a `SELECT` query. This data is put in a temporary table (see the section “Temporary tables”) and can be used in the query (for example, in `IN` operators). For example, if you have a text file with important user identifiers, you can upload it to the server along with a query that uses filtration by this list. @@ -46,7 +46,7 @@ $ cat /etc/passwd | sed 's/:/\t/g' | clickhouse-client --query="SELECT shell, co /bin/sync 1 ``` -When using the HTTP interface, external data is passed in the multipart/form-data format. Each table is transmitted as a separate file. The table name is taken from the file name. The ‘query\_string’ is passed the parameters ‘name\_format’, ‘name\_types’, and ‘name\_structure’, where ‘name’ is the name of the table that these parameters correspond to. The meaning of the parameters is the same as when using the command-line client. +When using the HTTP interface, external data is passed in the multipart/form-data format. Each table is transmitted as a separate file. The table name is taken from the file name. The `query_string` is passed the parameters `name_format`, `name_types`, and `name_structure`, where `name` is the name of the table that these parameters correspond to. The meaning of the parameters is the same as when using the command-line client. Example: diff --git a/docs/en/engines/table-engines/special/file.md b/docs/en/engines/table-engines/special/file.md index 28470e526b2..afccf0a4552 100644 --- a/docs/en/engines/table-engines/special/file.md +++ b/docs/en/engines/table-engines/special/file.md @@ -3,12 +3,11 @@ toc_priority: 37 toc_title: File --- -# File {#table_engines-file} +# File Table Engine {#table_engines-file} -The File table engine keeps the data in a file in one of the supported [file -formats](../../../interfaces/formats.md#formats) (TabSeparated, Native, etc.). +The File table engine keeps the data in a file in one of the supported [file formats](../../../interfaces/formats.md#formats) (`TabSeparated`, `Native`, etc.). -Usage examples: +Usage scenarios: - Data export from ClickHouse to file. - Convert data from one format to another. @@ -34,7 +33,7 @@ You may manually create this subfolder and file in server filesystem and then [A !!! warning "Warning" Be careful with this functionality, because ClickHouse does not keep track of external changes to such files. The result of simultaneous writes via ClickHouse and outside of ClickHouse is undefined. -**Example:** +## Example **1.** Set up the `file_engine_table` table: diff --git a/docs/en/engines/table-engines/special/generate.md b/docs/en/engines/table-engines/special/generate.md index 396b039f8df..aa12092367c 100644 --- a/docs/en/engines/table-engines/special/generate.md +++ b/docs/en/engines/table-engines/special/generate.md @@ -3,7 +3,7 @@ toc_priority: 46 toc_title: GenerateRandom --- -# Generaterandom {#table_engines-generate} +# GenerateRandom Table Engine {#table_engines-generate} The GenerateRandom table engine produces random data for given table schema. @@ -25,7 +25,7 @@ Generate table engine supports only `SELECT` queries. It supports all [DataTypes](../../../sql-reference/data-types/index.md) that can be stored in a table except `LowCardinality` and `AggregateFunction`. -**Example:** +## Example **1.** Set up the `generate_engine_table` table: diff --git a/docs/en/engines/table-engines/special/join.md b/docs/en/engines/table-engines/special/join.md index d0e685f9c48..0f88f0a56e7 100644 --- a/docs/en/engines/table-engines/special/join.md +++ b/docs/en/engines/table-engines/special/join.md @@ -3,9 +3,12 @@ toc_priority: 40 toc_title: Join --- -# Join {#join} +# Join Table Engine {#join} -Prepared data structure for using in [JOIN](../../../sql-reference/statements/select/join.md#select-join) operations. +Optional prepared data structure for usage in [JOIN](../../../sql-reference/statements/select/join.md#select-join) operations. + +!!! note "Note" + This is not an article about the [JOIN clause](../../../sql-reference/statements/select/join.md#select-join) itself. ## Creating a Table {#creating-a-table} diff --git a/docs/en/engines/table-engines/special/materializedview.md b/docs/en/engines/table-engines/special/materializedview.md index b0d99bc67d9..53ebf9641af 100644 --- a/docs/en/engines/table-engines/special/materializedview.md +++ b/docs/en/engines/table-engines/special/materializedview.md @@ -3,8 +3,8 @@ toc_priority: 43 toc_title: MaterializedView --- -# Materializedview {#materializedview} +# MaterializedView Table Engine {#materializedview} -Used for implementing materialized views (for more information, see [CREATE TABLE](../../../sql-reference/statements/create.md)). For storing data, it uses a different engine that was specified when creating the view. When reading from a table, it just uses this engine. +Used for implementing materialized views (for more information, see [CREATE TABLE](../../../sql-reference/statements/create.md)). For storing data, it uses a different engine that was specified when creating the view. When reading from a table, it just uses that engine. [Original article](https://clickhouse.tech/docs/en/operations/table_engines/materializedview/) diff --git a/docs/en/engines/table-engines/special/memory.md b/docs/en/engines/table-engines/special/memory.md index abdb07658d4..a6c833ebdba 100644 --- a/docs/en/engines/table-engines/special/memory.md +++ b/docs/en/engines/table-engines/special/memory.md @@ -3,15 +3,16 @@ toc_priority: 44 toc_title: Memory --- -# Memory {#memory} +# Memory Table Engine {#memory} The Memory engine stores data in RAM, in uncompressed form. Data is stored in exactly the same form as it is received when read. In other words, reading from this table is completely free. Concurrent data access is synchronized. Locks are short: read and write operations don’t block each other. Indexes are not supported. Reading is parallelized. + Maximal productivity (over 10 GB/sec) is reached on simple queries, because there is no reading from the disk, decompressing, or deserializing data. (We should note that in many cases, the productivity of the MergeTree engine is almost as high.) When restarting a server, data disappears from the table and the table becomes empty. Normally, using this table engine is not justified. However, it can be used for tests, and for tasks where maximum speed is required on a relatively small number of rows (up to approximately 100,000,000). -The Memory engine is used by the system for temporary tables with external query data (see the section “External data for processing a query”), and for implementing GLOBAL IN (see the section “IN operators”). +The Memory engine is used by the system for temporary tables with external query data (see the section “External data for processing a query”), and for implementing `GLOBAL IN` (see the section “IN operators”). [Original article](https://clickhouse.tech/docs/en/operations/table_engines/memory/) diff --git a/docs/en/engines/table-engines/special/merge.md b/docs/en/engines/table-engines/special/merge.md index a683fcdbc9e..5dca7f8602d 100644 --- a/docs/en/engines/table-engines/special/merge.md +++ b/docs/en/engines/table-engines/special/merge.md @@ -3,13 +3,17 @@ toc_priority: 36 toc_title: Merge --- -# Merge {#merge} +# Merge Table Engine {#merge} The `Merge` engine (not to be confused with `MergeTree`) does not store data itself, but allows reading from any number of other tables simultaneously. + Reading is automatically parallelized. Writing to a table is not supported. When reading, the indexes of tables that are actually being read are used, if they exist. + The `Merge` engine accepts parameters: the database name and a regular expression for tables. -Example: +## Examples + +Example 1: ``` sql Merge(hits, '^WatchLog') diff --git a/docs/en/engines/table-engines/special/null.md b/docs/en/engines/table-engines/special/null.md index 73c5a2b1ea6..5f9a2ac679b 100644 --- a/docs/en/engines/table-engines/special/null.md +++ b/docs/en/engines/table-engines/special/null.md @@ -3,10 +3,11 @@ toc_priority: 38 toc_title: 'Null' --- -# Null {#null} +# Null Table Engine {#null} -When writing to a Null table, data is ignored. When reading from a Null table, the response is empty. +When writing to a `Null` table, data is ignored. When reading from a `Null` table, the response is empty. -However, you can create a materialized view on a Null table. So the data written to the table will end up in the view. +!!! info "Hint" + However, you can create a materialized view on a `Null` table. So the data written to the table will end up affecting the view, but original raw data will still be discarded. [Original article](https://clickhouse.tech/docs/en/operations/table_engines/null/) diff --git a/docs/en/engines/table-engines/special/set.md b/docs/en/engines/table-engines/special/set.md index fb0c5952ae4..65fd7376532 100644 --- a/docs/en/engines/table-engines/special/set.md +++ b/docs/en/engines/table-engines/special/set.md @@ -3,14 +3,14 @@ toc_priority: 39 toc_title: Set --- -# Set {#set} +# Set Table Engine {#set} -A data set that is always in RAM. It is intended for use on the right side of the IN operator (see the section “IN operators”). +A data set that is always in RAM. It is intended for use on the right side of the `IN` operator (see the section “IN operators”). -You can use INSERT to insert data in the table. New elements will be added to the data set, while duplicates will be ignored. -But you can’t perform SELECT from the table. The only way to retrieve data is by using it in the right half of the IN operator. +You can use `INSERT` to insert data in the table. New elements will be added to the data set, while duplicates will be ignored. +But you can’t perform `SELECT` from the table. The only way to retrieve data is by using it in the right half of the `IN` operator. -Data is always located in RAM. For INSERT, the blocks of inserted data are also written to the directory of tables on the disk. When starting the server, this data is loaded to RAM. In other words, after restarting, the data remains in place. +Data is always located in RAM. For `INSERT`, the blocks of inserted data are also written to the directory of tables on the disk. When starting the server, this data is loaded to RAM. In other words, after restarting, the data remains in place. For a rough server restart, the block of data on the disk might be lost or damaged. In the latter case, you may need to manually delete the file with damaged data. diff --git a/docs/en/engines/table-engines/special/url.md b/docs/en/engines/table-engines/special/url.md index d560487c788..4fa1a50df38 100644 --- a/docs/en/engines/table-engines/special/url.md +++ b/docs/en/engines/table-engines/special/url.md @@ -3,12 +3,13 @@ toc_priority: 41 toc_title: URL --- -# URL(URL, Format) {#table_engines-url} +# URL Table Engine {#table_engines-url} -Manages data on a remote HTTP/HTTPS server. This engine is similar -to the [File](file.md) engine. +Queries data to/from a remote HTTP/HTTPS server. This engine is similar to the [File](file.md) engine. -## Using the Engine in the ClickHouse Server {#using-the-engine-in-the-clickhouse-server} +Syntax: `URL(URL, Format)` + +## Usage {#using-the-engine-in-the-clickhouse-server} The `format` must be one that ClickHouse can use in `SELECT` queries and, if necessary, in `INSERTs`. For the full list of supported formats, see @@ -24,7 +25,7 @@ respectively. For processing `POST` requests, the remote server must support You can limit the maximum number of HTTP GET redirect hops using the [max\_http\_get\_redirects](../../../operations/settings/settings.md#setting-max_http_get_redirects) setting. -**Example:** +## Example **1.** Create a `url_engine_table` table on the server : diff --git a/docs/en/engines/table-engines/special/view.md b/docs/en/engines/table-engines/special/view.md index f5d74795dec..c4f95adfa6d 100644 --- a/docs/en/engines/table-engines/special/view.md +++ b/docs/en/engines/table-engines/special/view.md @@ -3,7 +3,7 @@ toc_priority: 42 toc_title: View --- -# View {#table_engines-view} +# View Table Engine {#table_engines-view} Used for implementing views (for more information, see the `CREATE VIEW query`). It does not store data, but only stores the specified `SELECT` query. When reading from a table, it runs this query (and deletes all unnecessary columns from the query). diff --git a/docs/en/getting-started/install.md b/docs/en/getting-started/install.md index e917b8ef58c..7c8ae631e1a 100644 --- a/docs/en/getting-started/install.md +++ b/docs/en/getting-started/install.md @@ -64,7 +64,7 @@ You can also download and install packages manually from [here](https://repo.cli It is recommended to use official pre-compiled `tgz` archives for all Linux distributions, where installation of `deb` or `rpm` packages is not possible. -The required version can be downloaded with `curl` or `wget` from repository https://repo.yandex.ru/clickhouse/tgz/. +The required version can be downloaded with `curl` or `wget` from repository https://repo.clickhouse.tech/tgz/. After that downloaded archives should be unpacked and installed with installation scripts. Example for the latest version: ``` bash diff --git a/docs/en/interfaces/odbc.md b/docs/en/interfaces/odbc.md index 4fd7cd23964..42ae4cf5b53 100644 --- a/docs/en/interfaces/odbc.md +++ b/docs/en/interfaces/odbc.md @@ -5,6 +5,6 @@ toc_title: ODBC Driver # ODBC Driver {#odbc-driver} -- [Official driver](https://github.com/ClickHouse/clickhouse-odbc). +- [Official driver](https://github.com/ClickHouse/clickhouse-odbc) [Original article](https://clickhouse.tech/docs/en/interfaces/odbc/) diff --git a/docs/en/introduction/adopters.md b/docs/en/introduction/adopters.md index 4daadf32be6..081f963f74f 100644 --- a/docs/en/introduction/adopters.md +++ b/docs/en/introduction/adopters.md @@ -57,7 +57,7 @@ toc_title: Adopters | [S7 Airlines](https://www.s7.ru){.favicon} | Airlines | Metrics, Logging | — | — | [Talk in Russian, March 2019](https://www.youtube.com/watch?v=nwG68klRpPg&t=15s) | | [SEMrush](https://www.semrush.com/){.favicon} | Marketing | Main product | — | — | [Slides in Russian, August 2018](https://github.com/ClickHouse/clickhouse-presentations/blob/master/meetup17/5_semrush.pdf) | | [scireum GmbH](https://www.scireum.de/){.favicon} | e-Commerce | Main product | — | — | [Talk in German, February 2020](https://www.youtube.com/watch?v=7QWAn5RbyR4) | -| [Sentry](https://sentry.io/){.favicon} | Software developer | Backend for product | — | — | [Blog Post in English, May 2019](https://blog.sentry.io/2019/05/16/introducing-snuba-sentrys-new-search-infrastructure) | +| [Sentry](https://sentry.io/){.favicon} | Software Development | Main product | — | — | [Blog Post in English, May 2019](https://blog.sentry.io/2019/05/16/introducing-snuba-sentrys-new-search-infrastructure) | | [SGK](http://www.sgk.gov.tr/wps/portal/sgk/tr){.favicon} | Goverment Social Security | Analytics | — | — | [Slides in English, November 2019](https://github.com/ClickHouse/clickhouse-presentations/blob/master/meetup35/ClickHouse%20Meetup-Ramazan%20POLAT.pdf) | | [seo.do](https://seo.do/){.favicon} | Analytics | Main product | — | — | [Slides in English, November 2019](https://github.com/ClickHouse/clickhouse-presentations/blob/master/meetup35/CH%20Presentation-%20Metehan%20Çetinkaya.pdf) | | [Sina](http://english.sina.com/index.html){.favicon} | News | — | — | — | [Slides in Chinese, October 2018](https://github.com/ClickHouse/clickhouse-presentations/blob/master/meetup19/6.%20ClickHouse最佳实践%20高鹏_新浪.pdf) | diff --git a/docs/en/operations/backup.md b/docs/en/operations/backup.md index 423f7d1ef33..72316284e3b 100644 --- a/docs/en/operations/backup.md +++ b/docs/en/operations/backup.md @@ -31,6 +31,7 @@ For smaller volumes of data, a simple `INSERT INTO ... SELECT ...` to remote tab ## Manipulations with Parts {#manipulations-with-parts} ClickHouse allows using the `ALTER TABLE ... FREEZE PARTITION ...` query to create a local copy of table partitions. This is implemented using hardlinks to the `/var/lib/clickhouse/shadow/` folder, so it usually does not consume extra disk space for old data. The created copies of files are not handled by ClickHouse server, so you can just leave them there: you will have a simple backup that doesn’t require any additional external system, but it will still be prone to hardware issues. For this reason, it’s better to remotely copy them to another location and then remove the local copies. Distributed filesystems and object stores are still a good options for this, but normal attached file servers with a large enough capacity might work as well (in this case the transfer will occur via the network filesystem or maybe [rsync](https://en.wikipedia.org/wiki/Rsync)). +Data can be restored from backup using the `ALTER TABLE ... ATTACH PARTITION ...` For more information about queries related to partition manipulations, see the [ALTER documentation](../sql-reference/statements/alter.md#alter_manipulations-with-partitions). diff --git a/docs/en/operations/configuration-files.md b/docs/en/operations/configuration-files.md index 30ea1f2e562..f574240ea39 100644 --- a/docs/en/operations/configuration-files.md +++ b/docs/en/operations/configuration-files.md @@ -7,26 +7,33 @@ toc_title: Configuration Files ClickHouse supports multi-file configuration management. The main server configuration file is `/etc/clickhouse-server/config.xml`. Other files must be in the `/etc/clickhouse-server/config.d` directory. -!!! note "Note" - All the configuration files should be in XML format. Also, they should have the same root element, usually ``. +All the configuration files should be in XML format. Also, they should have the same root element, usually ``. -Some settings specified in the main configuration file can be overridden in other configuration files. The `replace` or `remove` attributes can be specified for the elements of these configuration files. +## Override -If neither is specified, it combines the contents of elements recursively, replacing values of duplicate children. +Some settings specified in the main configuration file can be overridden in other configuration files: -If `replace` is specified, it replaces the entire element with the specified one. +- The `replace` or `remove` attributes can be specified for the elements of these configuration files. +- If neither is specified, it combines the contents of elements recursively, replacing values of duplicate children. +- If `replace` is specified, it replaces the entire element with the specified one. +- If `remove` is specified, it deletes the element. -If `remove` is specified, it deletes the element. +## Substitution -The config can also define “substitutions”. If an element has the `incl` attribute, the corresponding substitution from the file will be used as the value. By default, the path to the file with substitutions is `/etc/metrika.xml`. This can be changed in the [include\_from](server-configuration-parameters/settings.md#server_configuration_parameters-include_from) element in the server config. The substitution values are specified in `/yandex/substitution_name` elements in this file. If a substitution specified in `incl` does not exist, it is recorded in the log. To prevent ClickHouse from logging missing substitutions, specify the `optional="true"` attribute (for example, settings for [macros](server-configuration-parameters/settings.md)). +The config can also define "substitutions". If an element has the `incl` attribute, the corresponding substitution from the file will be used as the value. By default, the path to the file with substitutions is `/etc/metrika.xml`. This can be changed in the [include\_from](server-configuration-parameters/settings.md#server_configuration_parameters-include_from) element in the server config. The substitution values are specified in `/yandex/substitution_name` elements in this file. If a substitution specified in `incl` does not exist, it is recorded in the log. To prevent ClickHouse from logging missing substitutions, specify the `optional="true"` attribute (for example, settings for [macros](server-configuration-parameters/settings.md)). Substitutions can also be performed from ZooKeeper. To do this, specify the attribute `from_zk = "/path/to/node"`. The element value is replaced with the contents of the node at `/path/to/node` in ZooKeeper. You can also put an entire XML subtree on the ZooKeeper node and it will be fully inserted into the source element. +## User Settings + The `config.xml` file can specify a separate config with user settings, profiles, and quotas. The relative path to this config is set in the `users_config` element. By default, it is `users.xml`. If `users_config` is omitted, the user settings, profiles, and quotas are specified directly in `config.xml`. Users configuration can be splitted into separate files similar to `config.xml` and `config.d/`. Directory name is defined as `users_config` setting without `.xml` postfix concatenated with `.d`. Directory `users.d` is used by default, as `users_config` defaults to `users.xml`. + +## Example + For example, you can have separate config file for each user like this: ``` bash @@ -48,8 +55,10 @@ $ cat /etc/clickhouse-server/users.d/alice.xml ``` +## Implementation Details + For each config file, the server also generates `file-preprocessed.xml` files when starting. These files contain all the completed substitutions and overrides, and they are intended for informational use. If ZooKeeper substitutions were used in the config files but ZooKeeper is not available on the server start, the server loads the configuration from the preprocessed file. The server tracks changes in config files, as well as files and ZooKeeper nodes that were used when performing substitutions and overrides, and reloads the settings for users and clusters on the fly. This means that you can modify the cluster, users, and their settings without restarting the server. -[Original article](https://clickhouse.tech/docs/en/operations/configuration_files/) +[Original article](https://clickhouse.tech/docs/en/operations/configuration-files/) diff --git a/docs/en/operations/server-configuration-parameters/settings.md b/docs/en/operations/server-configuration-parameters/settings.md index 5961c701283..b90b432da6c 100644 --- a/docs/en/operations/server-configuration-parameters/settings.md +++ b/docs/en/operations/server-configuration-parameters/settings.md @@ -892,6 +892,9 @@ The update is performed asynchronously, in a separate system thread. **Default value**: 15. +**See also** + +- [background_schedule_pool_size](../settings/settings.md#background_schedule_pool_size) ## access_control_path {#access_control_path} diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index c0e095230da..f29866d4980 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -1336,7 +1336,7 @@ Type: URL Default value: Empty -## background\_pool\_size {#background_pool_size} +## background_pool_size {#background_pool_size} Sets the number of threads performing background operations in table engines (for example, merges in [MergeTree engine](../../engines/table-engines/mergetree-family/index.md) tables). This setting is applied from `default` profile at ClickHouse server start and can’t be changed in a user session. By adjusting this setting, you manage CPU and disk load. Smaller pool size utilizes less CPU and disk resources, but background processes advance slower which might eventually impact query performance. @@ -1348,6 +1348,46 @@ Possible values: Default value: 16. +## background_buffer_flush_schedule_pool_size {#background_buffer_flush_schedule_pool_size} + +Sets the number of threads performing background flush in [Buffer](../../engines/table-engines/special/buffer.md)-engine tables. This setting is applied at ClickHouse server start and can't be changed in a user session. + +Possible values: + +- Any positive integer. + +Default value: 16. + +## background_move_pool_size {#background_move_pool_size} + +Sets the number of threads performing background moves of data parts for [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md#table_engine-mergetree-multiple-volumes)-engine tables. This setting is applied at ClickHouse server start and can’t be changed in a user session. + +Possible values: + +- Any positive integer. + +Default value: 8. + +## background_schedule_pool_size {#background_schedule_pool_size} + +Sets the number of threads performing background tasks for [replicated](../../engines/table-engines/mergetree-family/replication.md) tables, [Kafka](../../engines/table-engines/integrations/kafka.md) streaming, [DNS cache updates](../server-configuration-parameters/settings.md#server-settings-dns-cache-update-period). This setting is applied at ClickHouse server start and can’t be changed in a user session. + +Possible values: + +- Any positive integer. + +Default value: 16. + +## background_distributed_schedule_pool_size {#background_distributed_schedule_pool_size} + +Sets the number of threads performing background tasks for [distributed](../../engines/table-engines/special/distributed.md) sends. This setting is applied at ClickHouse server start and can’t be changed in a user session. + +Possible values: + +- Any positive integer. + +Default value: 16. + ## low_cardinality_max_dictionary_size {#low_cardinality_max_dictionary_size} Sets a maximum size in rows of a shared global dictionary for the [LowCardinality](../../sql-reference/data-types/lowcardinality.md) data type that can be written to a storage file system. This setting prevents issues with RAM in case of unlimited dictionary growth. All the data that can't be encoded due to maximum dictionary size limitation ClickHouse writes in an ordinary method. @@ -1406,5 +1446,4 @@ Possible values: Default value: 0. - [Original article](https://clickhouse.tech/docs/en/operations/settings/settings/) diff --git a/docs/en/operations/system-tables.md b/docs/en/operations/system-tables.md index f364d4e3068..28f448b632c 100644 --- a/docs/en/operations/system-tables.md +++ b/docs/en/operations/system-tables.md @@ -18,9 +18,11 @@ System tables: - Available only for reading data. - Can't be dropped or altered, but can be detached. -Most of system tables store their data in RAM. ClickHouse server creates such system tables at the start. +Most of system tables store their data in RAM. A ClickHouse server creates such system tables at the start. -The [metric_log](#system_tables-metric_log), [query_log](#system_tables-query_log), [query_thread_log](#system_tables-query_thread_log), [trace_log](#system_tables-trace_log) system tables store data in a storage filesystem. You can alter them or remove from a disk manually. If you remove one of that tables from a disk, the ClickHouse server creates the table again at the time of the next recording. A storage period for these tables is not limited, and ClickHouse server doesn't delete their data automatically. You need to organize removing of outdated logs by yourself. For example, you can use [TTL](../sql-reference/statements/alter.md#manipulations-with-table-ttl) settings for removing outdated log records. +Unlike other system tables, the system tables [metric_log](#system_tables-metric_log), [query_log](#system_tables-query_log), [query_thread_log](#system_tables-query_thread_log), [trace_log](#system_tables-trace_log) are served by [MergeTree](../engines/table-engines/mergetree-family/mergetree.md) table engine and store their data in a storage filesystem. If you remove a table from a filesystem, the ClickHouse server creates the empty one again at the time of the next data writing. If system table schema changed in a new release, then ClickHouse renames the current table and creates a new one. + +By default, table growth is unlimited. To control a size of a table, you can use [TTL](../sql-reference/statements/alter.md#manipulations-with-table-ttl) settings for removing outdated log records. Also you can use the partitioning feature of `MergeTree`-engine tables. ### Sources of System Metrics {#system-tables-sources-of-system-metrics} @@ -81,6 +83,10 @@ SELECT * FROM system.asynchronous_metrics LIMIT 10 - [system.events](#system_tables-events) — Contains a number of events that have occurred. - [system.metric\_log](#system_tables-metric_log) — Contains a history of metrics values from tables `system.metrics` и `system.events`. +## system.asynchronous_metric_log {#system-tables-async-log} + +Contains the historical values for `system.asynchronous_log` (see [system.asynchronous_metrics](#system_tables-asynchronous_metrics)) + ## system.clusters {#system-clusters} Contains information about clusters available in the config file and the servers in them. @@ -176,6 +182,41 @@ This table contains a single String column called ‘name’ – the name of a d Each database that the server knows about has a corresponding entry in the table. This system table is used for implementing the `SHOW DATABASES` query. +## system.data_type_families {#system_tables-data_type_families} + +Contains information about supported [data types](../sql-reference/data-types/). + +Columns: + +- `name` ([String](../sql-reference/data-types/string.md)) — Data type name. +- `case_insensitive` ([UInt8](../sql-reference/data-types/int-uint.md)) — Property that shows whether you can use a data type name in a query in case insensitive manner or not. For example, `Date` and `date` are both valid. +- `alias_to` ([String](../sql-reference/data-types/string.md)) — Data type name for which `name` is an alias. + +**Example** + +``` sql +SELECT * FROM system.data_type_families WHERE alias_to = 'String' +``` + +``` text +┌─name───────┬─case_insensitive─┬─alias_to─┐ +│ LONGBLOB │ 1 │ String │ +│ LONGTEXT │ 1 │ String │ +│ TINYTEXT │ 1 │ String │ +│ TEXT │ 1 │ String │ +│ VARCHAR │ 1 │ String │ +│ MEDIUMBLOB │ 1 │ String │ +│ BLOB │ 1 │ String │ +│ TINYBLOB │ 1 │ String │ +│ CHAR │ 1 │ String │ +│ MEDIUMTEXT │ 1 │ String │ +└────────────┴──────────────────┴──────────┘ +``` + +**See Also** + +- [Syntax](../sql-reference/syntax.md) — Information about supported syntax. + ## system.detached\_parts {#system_tables-detached_parts} Contains information about detached parts of [MergeTree](../engines/table-engines/mergetree-family/mergetree.md) tables. The `reason` column specifies why the part was detached. For user-detached parts, the reason is empty. Such parts can be attached with [ALTER TABLE ATTACH PARTITION\|PART](../sql-reference/statements/alter.md#alter_attach-partition) command. For the description of other columns, see [system.parts](#system_tables-parts). If part name is invalid, values of some columns may be `NULL`. Such parts can be deleted with [ALTER TABLE DROP DETACHED PART](../sql-reference/statements/alter.md#alter_drop-detached). @@ -601,9 +642,9 @@ You can change settings of queries logging in the [query_log](server-configurati You can disable queries logging by setting [log_queries = 0](settings/settings.md#settings-log-queries). We don't recommend to turn off logging because information in this table is important for solving issues. -The flushing period of logs is set in `flush_interval_milliseconds` parameter of the [query_log](server-configuration-parameters/settings.md#server_configuration_parameters-query-log) server settings section. To force flushing logs, use the [SYSTEM FLUSH LOGS](../sql-reference/statements/system.md#query_language-system-flush_logs) query. +The flushing period of data is set in `flush_interval_milliseconds` parameter of the [query_log](server-configuration-parameters/settings.md#server_configuration_parameters-query-log) server settings section. To force flushing, use the [SYSTEM FLUSH LOGS](../sql-reference/statements/system.md#query_language-system-flush_logs) query. -ClickHouse doesn't delete logs from the table automatically. See [Introduction](#system-tables-introduction) for more details. +ClickHouse doesn't delete data from the table automatically. See [Introduction](#system-tables-introduction) for more details. The `system.query_log` table registers two kinds of queries: @@ -731,68 +772,117 @@ Settings.Values: ['0','random','1','10000000000'] ## system.query_thread_log {#system_tables-query_thread_log} -The table contains information about each query execution thread. +Contains information about threads which execute queries, for example, thread name, thread start time, duration of query processing. -ClickHouse creates this table only if the [query\_thread\_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log) server parameter is specified. This parameter sets the logging rules, such as the logging interval or the name of the table the queries will be logged in. +To start logging: -To enable query logging, set the [log\_query\_threads](settings/settings.md#settings-log-query-threads) parameter to 1. For details, see the [Settings](settings/settings.md) section. +1. Configure parameters in the [query_thread_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log) section. +2. Set [log_query_threads](settings/settings.md#settings-log-query-threads) to 1. + +The flushing period of data is set in `flush_interval_milliseconds` parameter of the [query_thread_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log) server settings section. To force flushing, use the [SYSTEM FLUSH LOGS](../sql-reference/statements/system.md#query_language-system-flush_logs) query. + +ClickHouse doesn't delete data from the table automatically. See [Introduction](#system-tables-introduction) for more details. Columns: -- `event_date` (Date) — the date when the thread has finished execution of the query. -- `event_time` (DateTime) — the date and time when the thread has finished execution of the query. -- `query_start_time` (DateTime) — Start time of query execution. -- `query_duration_ms` (UInt64) — Duration of query execution. -- `read_rows` (UInt64) — Number of read rows. -- `read_bytes` (UInt64) — Number of read bytes. -- `written_rows` (UInt64) — For `INSERT` queries, the number of written rows. For other queries, the column value is 0. -- `written_bytes` (UInt64) — For `INSERT` queries, the number of written bytes. For other queries, the column value is 0. -- `memory_usage` (Int64) — The difference between the amount of allocated and freed memory in context of this thread. -- `peak_memory_usage` (Int64) — The maximum difference between the amount of allocated and freed memory in context of this thread. -- `thread_name` (String) — Name of the thread. -- `thread_number` (UInt32) — Internal thread ID. -- `os_thread_id` (Int32) — OS thread ID. -- `master_thread_id` (UInt64) — OS initial ID of initial thread. -- `query` (String) — Query string. -- `is_initial_query` (UInt8) — Query type. Possible values: +- `event_date` ([Date](../sql-reference/data-types/date.md)) — The date when the thread has finished execution of the query. +- `event_time` ([DateTime](../sql-reference/data-types/datetime.md)) — The date and time when the thread has finished execution of the query. +- `query_start_time` ([DateTime](../sql-reference/data-types/datetime.md)) — Start time of query execution. +- `query_duration_ms` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — Duration of query execution. +- `read_rows` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — Number of read rows. +- `read_bytes` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — Number of read bytes. +- `written_rows` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — For `INSERT` queries, the number of written rows. For other queries, the column value is 0. +- `written_bytes` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — For `INSERT` queries, the number of written bytes. For other queries, the column value is 0. +- `memory_usage` ([Int64](../sql-reference/data-types/int-uint.md)) — The difference between the amount of allocated and freed memory in context of this thread. +- `peak_memory_usage` ([Int64](../sql-reference/data-types/int-uint.md)) — The maximum difference between the amount of allocated and freed memory in context of this thread. +- `thread_name` ([String](../sql-reference/data-types/string.md)) — Name of the thread. +- `thread_number` ([UInt32](../sql-reference/data-types/int-uint.md)) — Internal thread ID. +- `thread_id` ([Int32](../sql-reference/data-types/int-uint.md)) — thread ID. +- `master_thread_id` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — OS initial ID of initial thread. +- `query` ([String](../sql-reference/data-types/string.md)) — Query string. +- `is_initial_query` ([UInt8](../sql-reference/data-types/int-uint.md#uint-ranges)) — Query type. Possible values: - 1 — Query was initiated by the client. - 0 — Query was initiated by another query for distributed query execution. -- `user` (String) — Name of the user who initiated the current query. -- `query_id` (String) — ID of the query. -- `address` (IPv6) — IP address that was used to make the query. -- `port` (UInt16) — The client port that was used to make the query. -- `initial_user` (String) — Name of the user who ran the initial query (for distributed query execution). -- `initial_query_id` (String) — ID of the initial query (for distributed query execution). -- `initial_address` (IPv6) — IP address that the parent query was launched from. -- `initial_port` (UInt16) — The client port that was used to make the parent query. -- `interface` (UInt8) — Interface that the query was initiated from. Possible values: +- `user` ([String](../sql-reference/data-types/string.md)) — Name of the user who initiated the current query. +- `query_id` ([String](../sql-reference/data-types/string.md)) — ID of the query. +- `address` ([IPv6](../sql-reference/data-types/domains/ipv6.md)) — IP address that was used to make the query. +- `port` ([UInt16](../sql-reference/data-types/int-uint.md#uint-ranges)) — The client port that was used to make the query. +- `initial_user` ([String](../sql-reference/data-types/string.md)) — Name of the user who ran the initial query (for distributed query execution). +- `initial_query_id` ([String](../sql-reference/data-types/string.md)) — ID of the initial query (for distributed query execution). +- `initial_address` ([IPv6](../sql-reference/data-types/domains/ipv6.md)) — IP address that the parent query was launched from. +- `initial_port` ([UInt16](../sql-reference/data-types/int-uint.md#uint-ranges)) — The client port that was used to make the parent query. +- `interface` ([UInt8](../sql-reference/data-types/int-uint.md#uint-ranges)) — Interface that the query was initiated from. Possible values: - 1 — TCP. - 2 — HTTP. -- `os_user` (String) — OS’s username who runs [clickhouse-client](../interfaces/cli.md). -- `client_hostname` (String) — Hostname of the client machine where the [clickhouse-client](../interfaces/cli.md) or another TCP client is run. -- `client_name` (String) — The [clickhouse-client](../interfaces/cli.md) or another TCP client name. -- `client_revision` (UInt32) — Revision of the [clickhouse-client](../interfaces/cli.md) or another TCP client. -- `client_version_major` (UInt32) — Major version of the [clickhouse-client](../interfaces/cli.md) or another TCP client. -- `client_version_minor` (UInt32) — Minor version of the [clickhouse-client](../interfaces/cli.md) or another TCP client. -- `client_version_patch` (UInt32) — Patch component of the [clickhouse-client](../interfaces/cli.md) or another TCP client version. -- `http_method` (UInt8) — HTTP method that initiated the query. Possible values: +- `os_user` ([String](../sql-reference/data-types/string.md)) — OS’s username who runs [clickhouse-client](../interfaces/cli.md). +- `client_hostname` ([String](../sql-reference/data-types/string.md)) — Hostname of the client machine where the [clickhouse-client](../interfaces/cli.md) or another TCP client is run. +- `client_name` ([String](../sql-reference/data-types/string.md)) — The [clickhouse-client](../interfaces/cli.md) or another TCP client name. +- `client_revision` ([UInt32](../sql-reference/data-types/int-uint.md)) — Revision of the [clickhouse-client](../interfaces/cli.md) or another TCP client. +- `client_version_major` ([UInt32](../sql-reference/data-types/int-uint.md)) — Major version of the [clickhouse-client](../interfaces/cli.md) or another TCP client. +- `client_version_minor` ([UInt32](../sql-reference/data-types/int-uint.md)) — Minor version of the [clickhouse-client](../interfaces/cli.md) or another TCP client. +- `client_version_patch` ([UInt32](../sql-reference/data-types/int-uint.md)) — Patch component of the [clickhouse-client](../interfaces/cli.md) or another TCP client version. +- `http_method` ([UInt8](../sql-reference/data-types/int-uint.md#uint-ranges)) — HTTP method that initiated the query. Possible values: - 0 — The query was launched from the TCP interface. - 1 — `GET` method was used. - 2 — `POST` method was used. -- `http_user_agent` (String) — The `UserAgent` header passed in the HTTP request. -- `quota_key` (String) — The “quota key” specified in the [quotas](quotas.md) setting (see `keyed`). -- `revision` (UInt32) — ClickHouse revision. -- `ProfileEvents.Names` (Array(String)) — Counters that measure different metrics for this thread. The description of them could be found in the table [system.events](#system_tables-events) -- `ProfileEvents.Values` (Array(UInt64)) — Values of metrics for this thread that are listed in the `ProfileEvents.Names` column. +- `http_user_agent` ([String](../sql-reference/data-types/string.md)) — The `UserAgent` header passed in the HTTP request. +- `quota_key` ([String](../sql-reference/data-types/string.md)) — The “quota key” specified in the [quotas](quotas.md) setting (see `keyed`). +- `revision` ([UInt32](../sql-reference/data-types/int-uint.md)) — ClickHouse revision. +- `ProfileEvents.Names` ([Array(String)](../sql-reference/data-types/array.md)) — Counters that measure different metrics for this thread. The description of them could be found in the table [system.events](#system_tables-events). +- `ProfileEvents.Values` ([Array(UInt64)](../sql-reference/data-types/array.md)) — Values of metrics for this thread that are listed in the `ProfileEvents.Names` column. -By default, logs are added to the table at intervals of 7.5 seconds. You can set this interval in the [query\_thread\_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log) server setting (see the `flush_interval_milliseconds` parameter). To flush the logs forcibly from the memory buffer into the table, use the `SYSTEM FLUSH LOGS` query. +**Example** -When the table is deleted manually, it will be automatically created on the fly. Note that all the previous logs will be deleted. +``` sql + SELECT * FROM system.query_thread_log LIMIT 1 FORMAT Vertical +``` -!!! note "Note" - The storage period for logs is unlimited. Logs aren’t automatically deleted from the table. You need to organize the removal of outdated logs yourself. +``` text +Row 1: +────── +event_date: 2020-05-13 +event_time: 2020-05-13 14:02:28 +query_start_time: 2020-05-13 14:02:28 +query_duration_ms: 0 +read_rows: 1 +read_bytes: 1 +written_rows: 0 +written_bytes: 0 +memory_usage: 0 +peak_memory_usage: 0 +thread_name: QueryPipelineEx +thread_id: 28952 +master_thread_id: 28924 +query: SELECT 1 +is_initial_query: 1 +user: default +query_id: 5e834082-6f6d-4e34-b47b-cd1934f4002a +address: ::ffff:127.0.0.1 +port: 57720 +initial_user: default +initial_query_id: 5e834082-6f6d-4e34-b47b-cd1934f4002a +initial_address: ::ffff:127.0.0.1 +initial_port: 57720 +interface: 1 +os_user: bayonet +client_hostname: clickhouse.ru-central1.internal +client_name: ClickHouse client +client_revision: 54434 +client_version_major: 20 +client_version_minor: 4 +client_version_patch: 1 +http_method: 0 +http_user_agent: +quota_key: +revision: 54434 +ProfileEvents.Names: ['ContextLock','RealTimeMicroseconds','UserTimeMicroseconds','OSCPUWaitMicroseconds','OSCPUVirtualTimeMicroseconds'] +ProfileEvents.Values: [1,97,81,5,81] +... +``` -You can specify an arbitrary partitioning key for the `system.query_thread_log` table in the [query\_thread\_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log) server setting (see the `partition_by` parameter). +**See Also** + +- [system.query_log](#system_tables-query_log) — Description of the `query_log` system table which contains common information about queries execution. ## system.trace\_log {#system_tables-trace_log} diff --git a/docs/en/operations/utilities/clickhouse-copier.md b/docs/en/operations/utilities/clickhouse-copier.md index ab3b49523d2..d450f5753e4 100644 --- a/docs/en/operations/utilities/clickhouse-copier.md +++ b/docs/en/operations/utilities/clickhouse-copier.md @@ -29,7 +29,7 @@ To reduce network traffic, we recommend running `clickhouse-copier` on the same The utility should be run manually: ``` bash -$ clickhouse-copier copier --daemon --config zookeeper.xml --task-path /task/path --base-dir /path/to/dir +$ clickhouse-copier --daemon --config zookeeper.xml --task-path /task/path --base-dir /path/to/dir ``` Parameters: diff --git a/docs/en/sql-reference/aggregate-functions/parametric-functions.md b/docs/en/sql-reference/aggregate-functions/parametric-functions.md index 3dec141d736..a7908c1e740 100644 --- a/docs/en/sql-reference/aggregate-functions/parametric-functions.md +++ b/docs/en/sql-reference/aggregate-functions/parametric-functions.md @@ -316,7 +316,7 @@ Result: The function takes as arguments a set of conditions from 1 to 32 arguments of type `UInt8` that indicate whether a certain condition was met for the event. Any condition can be specified as an argument (as in [WHERE](../../sql-reference/statements/select/where.md#select-where)). -The conditions, except the first, apply in pairs: the result of the second will be true if the first and second are true, of the third if the first and fird are true, etc. +The conditions, except the first, apply in pairs: the result of the second will be true if the first and second are true, of the third if the first and third are true, etc. **Syntax** diff --git a/docs/en/sql-reference/aggregate-functions/reference.md b/docs/en/sql-reference/aggregate-functions/reference.md index 4c505a46fd1..baba1ce904e 100644 --- a/docs/en/sql-reference/aggregate-functions/reference.md +++ b/docs/en/sql-reference/aggregate-functions/reference.md @@ -1805,7 +1805,7 @@ For more information see [parameters](#agg_functions-stochasticlinearregression- stochasticLogisticRegression(1.0, 1.0, 10, 'SGD') ``` -1. Fitting +**1.** Fitting @@ -1813,7 +1813,7 @@ stochasticLogisticRegression(1.0, 1.0, 10, 'SGD') Predicted labels have to be in \[-1, 1\]. -1. Predicting +**2.** Predicting diff --git a/docs/en/sql-reference/data-types/aggregatefunction.md b/docs/en/sql-reference/data-types/aggregatefunction.md index f214db20ea7..ce810f9b2a9 100644 --- a/docs/en/sql-reference/data-types/aggregatefunction.md +++ b/docs/en/sql-reference/data-types/aggregatefunction.md @@ -28,7 +28,7 @@ CREATE TABLE t ) ENGINE = ... ``` -[uniq](../../sql-reference/aggregate-functions/reference.md#agg_function-uniq), anyIf ([any](../../sql-reference/aggregate-functions/reference.md#agg_function-any)+[If](../../sql-reference/aggregate-functions/combinators.md#agg-functions-combinator-if)) and [quantiles](../../sql-reference/aggregate-functions/reference.md) are the aggregate functions supported in ClickHouse. +[uniq](../../sql-reference/aggregate-functions/reference.md#agg_function-uniq), anyIf ([any](../../sql-reference/aggregate-functions/reference.md#agg_function-any)+[If](../../sql-reference/aggregate-functions/combinators.md#agg-functions-combinator-if)) and [quantiles](../../sql-reference/aggregate-functions/reference.md#quantiles) are the aggregate functions supported in ClickHouse. ## Usage {#usage} diff --git a/docs/en/sql-reference/data-types/index.md b/docs/en/sql-reference/data-types/index.md index da507b81acd..63ec877b703 100644 --- a/docs/en/sql-reference/data-types/index.md +++ b/docs/en/sql-reference/data-types/index.md @@ -10,4 +10,6 @@ ClickHouse can store various kinds of data in table cells. This section describes the supported data types and special considerations for using and/or implementing them if any. +You can check whether data type name is case-sensitive in the [system.data_type_families](../../operations/system-tables.md#system_tables-data_type_families) table. + [Original article](https://clickhouse.tech/docs/en/data_types/) diff --git a/docs/en/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md b/docs/en/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md index b79a34f401a..71b719ce996 100644 --- a/docs/en/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md +++ b/docs/en/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md @@ -625,4 +625,43 @@ Setting fields: - `storage_type` – The structure of internal Redis storage using for work with keys. `simple` is for simple sources and for hashed single key sources, `hash_map` is for hashed sources with two keys. Ranged sources and cache sources with complex key are unsupported. May be omitted, default value is `simple`. - `db_index` – The specific numeric index of Redis logical database. May be omitted, default value is 0. +### Cassandra {#dicts-external_dicts_dict_sources-cassandra} + +Example of settings: + +```xml + + + localhost + 9042 + username + qwerty123 + database_name + table_name + 1 + 1 + One + "SomeColumn" = 42 + 8 + + +``` + +Setting fields: +- `host` – The Cassandra host or comma-separated list of hosts. +- `port` – The port on the Cassandra servers. If not specified, default port is used. +- `user` – Name of the Cassandra user. +- `password` – Password of the Cassandra user. +- `keyspace` – Name of the keyspace (database). +- `column_family` – Name of the column family (table). +- `allow_filering` – Flag to allow or not potentially expensive conditions on clustering key columns. Default value is 1. +- `partition_key_prefix` – Number of partition key columns in primary key of the Cassandra table. + Required for compose key dictionaries. Order of key columns in the dictionary definition must be the same as in Cassandra. + Default value is 1 (the first key column is a partition key and other key columns are clustering key). +- `consistency` – Consistency level. Possible values: `One`, `Two`, `Three`, + `All`, `EachQuorum`, `Quorum`, `LocalQuorum`, `LocalOne`, `Serial`, `LocalSerial`. Default is `One`. +- `where` – Optional selection criteria. +- `max_threads` – The maximum number of threads to use for loading data from multiple partitions in compose key dictionaries. + + [Original article](https://clickhouse.tech/docs/en/query_language/dicts/external_dicts_dict_sources/) diff --git a/docs/en/sql-reference/functions/comparison-functions.md b/docs/en/sql-reference/functions/comparison-functions.md index f03dc04a21a..0b6d8b6e36e 100644 --- a/docs/en/sql-reference/functions/comparison-functions.md +++ b/docs/en/sql-reference/functions/comparison-functions.md @@ -22,7 +22,7 @@ Strings are compared by bytes. A shorter string is smaller than all strings that ## equals, a = b and a == b operator {#function-equals} -## notEquals, a ! operator= b and a \<\> b {#function-notequals} +## notEquals, a != b and a \<\> b operator {#function-notequals} ## less, \< operator {#function-less} diff --git a/docs/en/sql-reference/functions/geo.md b/docs/en/sql-reference/functions/geo.md index 58c501e7e1c..942b951c7c8 100644 --- a/docs/en/sql-reference/functions/geo.md +++ b/docs/en/sql-reference/functions/geo.md @@ -7,7 +7,7 @@ toc_title: Working with geographical coordinates ## greatCircleDistance {#greatcircledistance} -Calculate the distance between two points on the Earth’s surface using [the great-circle formula](https://en.wikipedia.org/wiki/Great-circle_distance). +Calculates the distance between two points on the Earth’s surface using [the great-circle formula](https://en.wikipedia.org/wiki/Great-circle_distance). ``` sql greatCircleDistance(lon1Deg, lat1Deg, lon2Deg, lat2Deg) @@ -40,6 +40,37 @@ SELECT greatCircleDistance(55.755831, 37.617673, -55.755831, -37.617673) └───────────────────────────────────────────────────────────────────┘ ``` +## greatCircleAngle {#greatcircleangle} + +Calculates the central angle between two points on the Earth’s surface using [the great-circle formula](https://en.wikipedia.org/wiki/Great-circle_distance). + +``` sql +greatCircleAngle(lon1Deg, lat1Deg, lon2Deg, lat2Deg) +``` + +**Input parameters** + +- `lon1Deg` — Longitude of the first point in degrees. +- `lat1Deg` — Latitude of the first point in degrees. +- `lon2Deg` — Longitude of the second point in degrees. +- `lat2Deg` — Latitude of the second point in degrees. + +**Returned value** + +The central angle between two points in degrees. + +**Example** + +``` sql +SELECT greatCircleAngle(0, 0, 45, 0) AS arc +``` + +``` text +┌─arc─┐ +│ 45 │ +└─────┘ +``` + ## pointInEllipses {#pointinellipses} Checks whether the point belongs to at least one of the ellipses. diff --git a/docs/en/sql-reference/functions/random-functions.md b/docs/en/sql-reference/functions/random-functions.md index 788ece6371b..05fb982138c 100644 --- a/docs/en/sql-reference/functions/random-functions.md +++ b/docs/en/sql-reference/functions/random-functions.md @@ -60,4 +60,43 @@ Result: └────────────┴────────────┴──────────────┴────────────────┴─────────────────┴──────────────────────┘ ``` +# Random functions for working with strings {#random-functions-for-working-with-strings} + +## randomString {#random-string} + +## randomFixedString {#random-fixed-string} + +## randomPrintableASCII {#random-printable-ascii} + +## randomStringUTF8 {#random-string-utf8} + +## fuzzBits {#fuzzbits} + +**Syntax** + +``` sql +fuzzBits([s], [prob]) +``` +Inverts bits of `s`, each with probability `prob`. + +**Parameters** +- `s` - `String` or `FixedString` +- `prob` - constant `Float32/64` + +**Returned value** +Fuzzed string with same as s type. + +**Example** + +``` sql +SELECT fuzzBits(materialize('abacaba'), 0.1) +FROM numbers(3) +``` +``` text +┌─fuzzBits(materialize('abacaba'), 0.1)─┐ +│ abaaaja │ +│ a*cjab+ │ +│ aeca2A │ +└───────────────────────────────────────┘ + [Original article](https://clickhouse.tech/docs/en/query_language/functions/random_functions/) diff --git a/docs/en/sql-reference/statements/misc.md b/docs/en/sql-reference/statements/misc.md index 18cbf1a90e8..bd978908588 100644 --- a/docs/en/sql-reference/statements/misc.md +++ b/docs/en/sql-reference/statements/misc.md @@ -271,7 +271,7 @@ Renames one or more tables. RENAME TABLE [db11.]name11 TO [db12.]name12, [db21.]name21 TO [db22.]name22, ... [ON CLUSTER cluster] ``` -All tables are renamed under global locking. Renaming tables is a light operation. If you indicated another database after TO, the table will be moved to this database. However, the directories with databases must reside in the same file system (otherwise, an error is returned). +Renaming tables is a light operation. If you indicated another database after `TO`, the table will be moved to this database. However, the directories with databases must reside in the same file system (otherwise, an error is returned). If you rename multiple tables in one query, this is a non-atomic operation, it may be partially executed, queries in other sessions may receive the error `Table ... doesn't exist ..`. ## SET {#query-set} diff --git a/docs/en/sql-reference/statements/select/index.md b/docs/en/sql-reference/statements/select/index.md index 8224bf1e798..a2dbeaded95 100644 --- a/docs/en/sql-reference/statements/select/index.md +++ b/docs/en/sql-reference/statements/select/index.md @@ -1,12 +1,15 @@ --- toc_priority: 33 toc_folder_title: SELECT -toc_title: Queries Syntax +toc_title: Overview +title: SELECT Query --- -# SELECT Queries Syntax {#select-queries-syntax} +# SELECT Query {#select-queries-syntax} -`SELECT` performs data retrieval. +`SELECT` queries perform data retrieval. By default, the requested data is returned to the client, while in conjunction with [INSERT INTO](../../../sql-reference/statements/insert-into.md) it can be forwarded to a different table. + +## Syntax ``` sql [WITH expr_list|(subquery)] diff --git a/docs/en/sql-reference/statements/system.md b/docs/en/sql-reference/statements/system.md index 9544998334f..e4823686c68 100644 --- a/docs/en/sql-reference/statements/system.md +++ b/docs/en/sql-reference/statements/system.md @@ -5,10 +5,13 @@ toc_title: SYSTEM # SYSTEM Queries {#query-language-system} +- [RELOAD EMBEDDED DICTIONARIES](#query_language-system-reload-emdedded-dictionaries) - [RELOAD DICTIONARIES](#query_language-system-reload-dictionaries) - [RELOAD DICTIONARY](#query_language-system-reload-dictionary) - [DROP DNS CACHE](#query_language-system-drop-dns-cache) - [DROP MARK CACHE](#query_language-system-drop-mark-cache) +- [DROP UNCOMPRESSED CACHE](#query_language-system-drop-uncompressed-cache) +- [DROP COMPILED EXPRESSION CACHE](#query_language-system-drop-compiled-expression-cache) - [FLUSH LOGS](#query_language-system-flush_logs) - [RELOAD CONFIG](#query_language-system-reload-config) - [SHUTDOWN](#query_language-system-shutdown) @@ -18,7 +21,25 @@ toc_title: SYSTEM - [START DISTRIBUTED SENDS](#query_language-system-start-distributed-sends) - [STOP MERGES](#query_language-system-stop-merges) - [START MERGES](#query_language-system-start-merges) +- [STOP TTL MERGES](#query_language-stop-ttl-merges) +- [START TTL MERGES](#query_language-start-ttl-merges) +- [STOP MOVES](#query_language-stop-moves) +- [START MOVES](#query_language-start-moves) +- [STOP FETCHES](#query_language-system-stop-fetches) +- [START FETCHES](#query_language-system-start-fetches) +- [STOP REPLICATED SENDS](#query_language-system-start-replicated-sends) +- [START REPLICATED SENDS](#query_language-system-start-replicated-sends) +- [STOP REPLICATION QUEUES](#query_language-system-stop-replication-queues) +- [START REPLICATION QUEUES](#query_language-system-start-replication-queues) +- [SYNC REPLICA](#query_language-system-sync-replica) +- [RESTART REPLICA](#query_language-system-restart-replica) +- [RESTART REPLICAS](#query_language-system-restart-replicas) +## RELOAD EMBEDDED DICTIONARIES] {#query_language-system-reload-emdedded-dictionaries} +Reload all [Internal dictionaries](../dictionaries/internal-dicts.md). +By default, internal dictionaries are disabled. +Always returns `Ok.` regardless of the result of the internal dictionary update. + ## RELOAD DICTIONARIES {#query_language-system-reload-dictionaries} Reloads all dictionaries that have been successfully loaded before. @@ -45,6 +66,16 @@ For more convenient (automatic) cache management, see disable\_internal\_dns\_ca Resets the mark cache. Used in development of ClickHouse and performance tests. +## DROP UNCOMPRESSED CACHE {#query_language-system-drop-uncompressed-cache} + +Reset the uncompressed data cache. Used in development of ClickHouse and performance tests. +For manage uncompressed data cache parameters use following server level settings [uncompressed_cache_size](../../operations/server-configuration-parameters/settings.md#server-settings-uncompressed_cache_size) and query/user/profile level settings [use_uncompressed_cache](../../operations/settings/settings.md#setting-use_uncompressed_cache) + + +## DROP COMPILED EXPRESSION CACHE {#query_language-system-drop-compiled-expression-cache} +Reset the compiled expression cache. Used in development of ClickHouse and performance tests. +Complied expression cache used when query/user/profile enable option [compile](../../operations/settings/settings.md#compile) + ## FLUSH LOGS {#query_language-system-flush_logs} Flushes buffers of log messages to system tables (e.g. system.query\_log). Allows you to not wait 7.5 seconds when debugging. @@ -89,6 +120,10 @@ Enables background data distribution when inserting data into distributed tables SYSTEM START DISTRIBUTED SENDS [db.] ``` +## Managing MergeTree Tables {#query-language-system-mergetree} + +ClickHouse can manage background processes in [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md) tables. + ### STOP MERGES {#query_language-system-stop-merges} Provides possibility to stop background merges for tables in the MergeTree family: @@ -108,4 +143,110 @@ Provides possibility to start background merges for tables in the MergeTree fami SYSTEM START MERGES [[db.]merge_tree_family_table_name] ``` +### STOP TTL MERGES {#query_language-stop-ttl-merges} + +Provides possibility to stop background delete old data according to [TTL expression](../../engines/table-engines/mergetree-family/mergetree.md#table_engine-mergetree-ttl) for tables in the MergeTree family: +Return `Ok.` even table doesn't exists or table have not MergeTree engine. Return error when database doesn't exists: + +``` sql +SYSTEM STOP TTL MERGES [[db.]merge_tree_family_table_name] +``` + +### START TTL MERGES {#query_language-start-ttl-merges} + +Provides possibility to start background delete old data according to [TTL expression](../../engines/table-engines/mergetree-family/mergetree.md#table_engine-mergetree-ttl) for tables in the MergeTree family: +Return `Ok.` even table doesn't exists. Return error when database doesn't exists: + +``` sql +SYSTEM START TTL MERGES [[db.]merge_tree_family_table_name] +``` + +### STOP MOVES {#query_language-stop-moves} + +Provides possibility to stop background move data according to [TTL table expression with TO VOLUME or TO DISK clause](../../engines/table-engines/mergetree-family/mergetree.md#mergetree-table-ttl) for tables in the MergeTree family: +Return `Ok.` even table doesn't exists. Return error when database doesn't exists: + +``` sql +SYSTEM STOP MOVES [[db.]merge_tree_family_table_name] +``` + +### START MOVES {#query_language-start-moves} + +Provides possibility to start background move data according to [TTL table expression with TO VOLUME and TO DISK clause](../../engines/table-engines/mergetree-family/mergetree.md#mergetree-table-ttl) for tables in the MergeTree family: +Return `Ok.` even table doesn't exists. Return error when database doesn't exists: + +``` sql +SYSTEM STOP MOVES [[db.]merge_tree_family_table_name] +``` + +## Managing ReplicatedMergeTree Tables {#query-language-system-replicated} + +ClickHouse can manage background replication related processes in [ReplicatedMergeTree](../../engines/table-engines/mergetree-family/replacingmergetree.md) tables. + +### STOP FETCHES {#query_language-system-stop-fetches} +Provides possibility to stop background fetches for inserted parts for tables in the `ReplicatedMergeTree` family: +Always returns `Ok.` regardless of the table engine and even table or database doesn't exists. + +``` sql +SYSTEM STOP FETCHES [[db.]replicated_merge_tree_family_table_name] +``` + +### START FETCHES {#query_language-system-start-fetches} +Provides possibility to start background fetches for inserted parts for tables in the `ReplicatedMergeTree` family: +Always returns `Ok.` regardless of the table engine and even table or database doesn't exists. + +``` sql +SYSTEM START FETCHES [[db.]replicated_merge_tree_family_table_name] +``` + +### STOP REPLICATED SENDS {#query_language-system-start-replicated-sends} +Provides possibility to stop background sends to other replicas in cluster for new inserted parts for tables in the `ReplicatedMergeTree` family: + +``` sql +SYSTEM STOP REPLICATED SENDS [[db.]replicated_merge_tree_family_table_name] +``` + +### START REPLICATED SENDS {#query_language-system-start-replicated-sends} +Provides possibility to start background sends to other replicas in cluster for new inserted parts for tables in the `ReplicatedMergeTree` family: + +``` sql +SYSTEM START REPLICATED SENDS [[db.]replicated_merge_tree_family_table_name] +``` + +### STOP REPLICATION QUEUES {#query_language-system-stop-replication-queues} +Provides possibility to stop background fetch tasks from replication queues which stored in Zookeeper for tables in the `ReplicatedMergeTree` family. Possible background tasks types - merges, fetches, mutation, DDL statements with ON CLUSTER clause: + +``` sql +SYSTEM STOP REPLICATION QUEUES [[db.]replicated_merge_tree_family_table_name] +``` + +### START REPLICATION QUEUES {#query_language-system-start-replication-queues} +Provides possibility to start background fetch tasks from replication queues which stored in Zookeeper for tables in the `ReplicatedMergeTree` family. Possible background tasks types - merges, fetches, mutation, DDL statements with ON CLUSTER clause: + +``` sql +SYSTEM START REPLICATION QUEUES [[db.]replicated_merge_tree_family_table_name] +``` + +### SYNC REPLICA {#query_language-system-sync-replica} +Wait until a `ReplicatedMergeTree` table will be synced with other replicas in a cluster. Will run until `receive_timeout` if fetches currently disabled for the table. + +``` sql +SYSTEM SYNC REPLICA [db.]replicated_merge_tree_family_table_name +``` + +### RESTART REPLICA {#query_language-system-restart-replica} +Provides possibility to reinitialize Zookeeper sessions state for `ReplicatedMergeTree` table, will compare current state with Zookeeper as source of true and add tasks to Zookeeper queue if needed +Initialization replication quene based on ZooKeeper date happens in the same way as `ATTACH TABLE` statement. For a short time the table will be unavailable for any operations. + +``` sql +SYSTEM RESTART REPLICA [db.]replicated_merge_tree_family_table_name +``` + +### RESTART REPLICAS {#query_language-system-restart-replicas} +Provides possibility to reinitialize Zookeeper sessions state for all `ReplicatedMergeTree` tables, will compare current state with Zookeeper as source of true and add tasks to Zookeeper queue if needed + +``` sql +SYSTEM RESTART QUEUES [db.]replicated_merge_tree_family_table_name +``` + [Original article](https://clickhouse.tech/docs/en/query_language/system/) diff --git a/docs/en/sql-reference/syntax.md b/docs/en/sql-reference/syntax.md index 70db90b38be..cca01114681 100644 --- a/docs/en/sql-reference/syntax.md +++ b/docs/en/sql-reference/syntax.md @@ -40,7 +40,7 @@ Keywords are case-insensitive when they correspond to: - SQL standard. For example, `SELECT`, `select` and `SeLeCt` are all valid. - Implementation in some popular DBMS (MySQL or Postgres). For example, `DateTime` is the same as `datetime`. -Whether data type name is case-sensitive can be checked in the `system.data_type_families` table. +You can check whether a data type name is case-sensitive in the [system.data_type_families](../operations/system-tables.md#system_tables-data_type_families) table. In contrast to standard SQL, all other keywords (including functions names) are **case-sensitive**. diff --git a/docs/es/development/developer-instruction.md b/docs/es/development/developer-instruction.md index 9c56abe33a1..8f21e851b89 100644 --- a/docs/es/development/developer-instruction.md +++ b/docs/es/development/developer-instruction.md @@ -141,7 +141,7 @@ Las compilaciones oficiales de Yandex actualmente usan GCC porque genera código Para instalar GCC en Ubuntu, ejecute: `sudo apt install gcc g++` -Compruebe la versión de gcc: `gcc --version`. Si está por debajo de 9, siga las instrucciones aquí: https://clickhouse .tech/docs/en/development/build/\#install-gcc-9. +Compruebe la versión de gcc: `gcc --version`. Si está por debajo de 9, siga las instrucciones aquí: https://clickhouse.tech/docs/es/development/build/#install-gcc-9. La compilación de Mac OS X solo es compatible con Clang. Sólo tiene que ejecutar `brew install llvm` @@ -249,7 +249,7 @@ La Guía de estilo de código: https://clickhouse.tech/docs/en/development/style Pruebas de escritura: https://clickhouse.tech/docs/en/development/tests/ -Lista de tareas: https://github.com/ClickHouse/ClickHouse/blob/master/testsructions/easy\_tasks\_sorted\_en.md +Lista de tareas: https://github.com/ClickHouse/ClickHouse/contribute # Datos de prueba {#test-data} diff --git a/docs/es/getting-started/install.md b/docs/es/getting-started/install.md index 83f5fff8af2..89b5735f192 100644 --- a/docs/es/getting-started/install.md +++ b/docs/es/getting-started/install.md @@ -66,7 +66,7 @@ También puede descargar e instalar paquetes manualmente desde [aqui](https://re Se recomienda utilizar pre-compilado oficial `tgz` para todas las distribuciones de Linux, donde la instalación de `deb` o `rpm` paquetes no es posible. -La versión requerida se puede descargar con `curl` o `wget` desde el repositorio https://repo.yandex.ru/clickhouse/tgz/. +La versión requerida se puede descargar con `curl` o `wget` desde el repositorio https://repo.clickhouse.tech/tgz/. Después de eso, los archivos descargados deben desempaquetarse e instalarse con scripts de instalación. Ejemplo para la última versión: ``` bash diff --git a/docs/fa/development/developer-instruction.md b/docs/fa/development/developer-instruction.md index cbc4734cec8..287960aff04 100644 --- a/docs/fa/development/developer-instruction.md +++ b/docs/fa/development/developer-instruction.md @@ -143,7 +143,7 @@ toc_title: "\u062F\u0633\u062A\u0648\u0631\u0627\u0644\u0639\u0645\u0644 \u062A\ برای نصب شورای همکاری خلیج فارس در اوبونتو اجرای: `sudo apt install gcc g++` -بررسی نسخه شورای همکاری خلیج فارس: `gcc --version`. اگر زیر است 9, سپس دستورالعمل اینجا را دنبال کنید: https://clickhouse.فناوری / اسناد / ارتباطات / توسعه/ساختن / \#نصب شورای همکاری خلیج فارس-9. +بررسی نسخه شورای همکاری خلیج فارس: `gcc --version`. اگر زیر است 9, سپس دستورالعمل اینجا را دنبال کنید: https://clickhouse.tech/docs/fa/development/build/#install-gcc-9. سیستم عامل مک ایکس ساخت فقط برای صدای جرنگ جرنگ پشتیبانی می شود. فقط فرار کن `brew install llvm` @@ -251,7 +251,7 @@ KDevelop و QTCreator دیگر از جایگزین های بسیار خوبی ا تست نوشتن: https://clickhouse.فناوری / اسناد/توسعه/تست/ -فهرست تکلیفها: https://github.com/ClickHouse/ClickHouse/blob/master/testsructions/easy\_tasks\_sorted\_en.md +فهرست تکلیفها: https://github.com/ClickHouse/ClickHouse/contribute # داده های تست {#test-data} diff --git a/docs/fa/getting-started/install.md b/docs/fa/getting-started/install.md index ec0b67944d9..d3e39e97c80 100644 --- a/docs/fa/getting-started/install.md +++ b/docs/fa/getting-started/install.md @@ -67,7 +67,7 @@ sudo yum install clickhouse-server clickhouse-client توصیه می شود به استفاده از رسمی از پیش وارد شده `tgz` بایگانی برای همه توزیع های لینوکس, که نصب و راه اندازی `deb` یا `rpm` بسته امکان پذیر نیست. -نسخه مورد نیاز را می توان با دانلود `curl` یا `wget` از مخزن https://repo.yandex.ru/clickhouse/tgz/. +نسخه مورد نیاز را می توان با دانلود `curl` یا `wget` از مخزن https://repo.clickhouse.tech/tgz/. پس از که دانلود بایگانی باید غیر بستهای و نصب شده با اسکریپت نصب و راه اندازی. به عنوان مثال برای جدیدترین نسخه: ``` bash diff --git a/docs/fr/development/developer-instruction.md b/docs/fr/development/developer-instruction.md index 414cfc1d339..a20066fa3f7 100644 --- a/docs/fr/development/developer-instruction.md +++ b/docs/fr/development/developer-instruction.md @@ -141,7 +141,7 @@ Les builds officiels de Yandex utilisent actuellement GCC car ils génèrent du Pour installer GCC sur Ubuntu Exécutez: `sudo apt install gcc g++` -Vérifiez la version de gcc: `gcc --version`. Si elle est inférieure à 9, suivez les instructions ici: https://clickhouse.tech/docs/fr/développement/construction/\#install-gcc-9. +Vérifiez la version de gcc: `gcc --version`. Si elle est inférieure à 9, suivez les instructions ici: https://clickhouse.tech/docs/fr/development/build/#install-gcc-9. Mac OS X build est pris en charge uniquement pour Clang. Il suffit d'exécuter `brew install llvm` @@ -249,7 +249,7 @@ Le code Style Guide: https://clickhouse.tech/docs/fr/développement/style/ Rédaction de tests: https://clickhouse.tech/docs/fr/développement/tests/ -Liste des tâches: https://github.com/ClickHouse/ClickHouse/blob/master/testsructions/easy\_tasks\_sorted\_en.md +Liste des tâches: https://github.com/ClickHouse/ClickHouse/contribute # Des Données De Test {#test-data} diff --git a/docs/fr/getting-started/install.md b/docs/fr/getting-started/install.md index 770c4cf8e42..3659369fb4a 100644 --- a/docs/fr/getting-started/install.md +++ b/docs/fr/getting-started/install.md @@ -66,7 +66,7 @@ Vous pouvez également télécharger et installer des paquets manuellement à pa Il est recommandé d'utiliser officiel pré-compilé `tgz` archives pour toutes les distributions Linux, où l'installation de `deb` ou `rpm` les emballages n'est pas possible. -La version requise peut être téléchargée avec `curl` ou `wget` depuis le référentiel https://repo.yandex.ru/clickhouse/tgz/. +La version requise peut être téléchargée avec `curl` ou `wget` depuis le référentiel https://repo.clickhouse.tech/tgz/. Après cela, les archives téléchargées doivent être décompressées et installées avec des scripts d'installation. Exemple pour la dernière version: ``` bash diff --git a/docs/ja/development/developer-instruction.md b/docs/ja/development/developer-instruction.md index d65b25bd98c..6441e77185f 100644 --- a/docs/ja/development/developer-instruction.md +++ b/docs/ja/development/developer-instruction.md @@ -141,7 +141,7 @@ ClickHouseのビルドには、バージョン9以降のGCCとClangバージョ UBUNTUにGCCをインストールするには: `sudo apt install gcc g++` -Gccのバージョンを確認する: `gcc --version`. の場合は下記9その指示に従う。https://clickhouse.tech/docs/en/development/build/\#install-gcc-9. +Gccのバージョンを確認する: `gcc --version`. の場合は下記9その指示に従う。https://clickhouse.tech/docs/ja/development/build/#install-gcc-9. Mac OS XのビルドはClangでのみサポートされています。 ちょうど実行 `brew install llvm` @@ -249,7 +249,7 @@ KDevelopとQTCreatorは、ClickHouseを開発するためのIDEの他の優れ 筆記試験:https://clickhouse.tech/docs/en/development/tests/ -タスクのリスト:https://github.com/ClickHouse/ClickHouse/blob/master/testsructions/easy\_tasks\_sorted\_en.md +タスクのリスト:https://github.com/ClickHouse/ClickHouse/contribute # テストデータ {#test-data} diff --git a/docs/ja/getting-started/install.md b/docs/ja/getting-started/install.md index 9710fdae6a7..66867652811 100644 --- a/docs/ja/getting-started/install.md +++ b/docs/ja/getting-started/install.md @@ -66,7 +66,7 @@ sudo yum install clickhouse-server clickhouse-client 公式の事前コンパイルを使用することをお勧めします `tgz` のインストール `deb` または `rpm` パッケージはできません。 -必要なバージョンは次のとおりです `curl` または `wget` リポジトリからhttps://repo.yandex.ru/clickhouse/tgz/. +必要なバージョンは次のとおりです `curl` または `wget` リポジトリからhttps://repo.clickhouse.tech/tgz/. その後、アーカイブをダウンロードは開梱と設置と設置のためのイントロダクションです。 最新バージョンの例: ``` bash diff --git a/docs/ru/development/developer-instruction.md b/docs/ru/development/developer-instruction.md index 11ac3a73f6e..775b156dff5 100644 --- a/docs/ru/development/developer-instruction.md +++ b/docs/ru/development/developer-instruction.md @@ -135,7 +135,7 @@ ClickHouse использует для сборки некоторое коли Для установки GCC под Ubuntu, выполните: `sudo apt install gcc g++`. -Проверьте версию gcc: `gcc --version`. Если версия меньше 9, то следуйте инструкции: https://clickhouse.tech/docs/en/development/build/\#install-gcc-9 +Проверьте версию gcc: `gcc --version`. Если версия меньше 9, то следуйте инструкции: https://clickhouse.tech/docs/ru/development/build/#install-gcc-9. Сборка под Mac OS X поддерживается только для компилятора Clang. Чтобы установить его выполните `brew install llvm` @@ -244,7 +244,7 @@ Mac OS X: Разработка тестов: https://clickhouse.tech/docs/ru/development/tests/ -Список задач: https://github.com/ClickHouse/ClickHouse/blob/master/tests/instructions/easy\_tasks\_sorted\_ru.md +Список задач: https://github.com/ClickHouse/ClickHouse/contribute # Тестовые данные {#testovye-dannye} diff --git a/docs/ru/engines/table-engines/integrations/kafka.md b/docs/ru/engines/table-engines/integrations/kafka.md index e2940ccfb56..8a79c8556b8 100644 --- a/docs/ru/engines/table-engines/integrations/kafka.md +++ b/docs/ru/engines/table-engines/integrations/kafka.md @@ -165,5 +165,6 @@ Kafka(kafka_broker_list, kafka_topic_list, kafka_group_name, kafka_format **Смотрите также** - [Виртуальные столбцы](index.md#table_engines-virtual_columns) +- [background_schedule_pool_size](../../../operations/settings/settings.md#background_schedule_pool_size) [Оригинальная статья](https://clickhouse.tech/docs/ru/operations/table_engines/kafka/) diff --git a/docs/ru/engines/table-engines/mergetree-family/mergetree.md b/docs/ru/engines/table-engines/mergetree-family/mergetree.md index fe7b7627c01..f9192015188 100644 --- a/docs/ru/engines/table-engines/mergetree-family/mergetree.md +++ b/docs/ru/engines/table-engines/mergetree-family/mergetree.md @@ -589,6 +589,8 @@ SETTINGS storage_policy = 'moving_from_ssd_to_hdd' По умолчанию используется политика хранения `default` в которой есть один том и один диск, указанный в ``. В данный момент менять политику хранения после создания таблицы нельзя. +Количество потоков для фоновых перемещений кусков между дисками можно изменить с помощью настройки [background_move_pool_size](../../../operations/settings/settings.md#background_move_pool_size) + ### Особенности работы {#osobennosti-raboty} В таблицах `MergeTree` данные попадают на диск несколькими способами: diff --git a/docs/ru/engines/table-engines/mergetree-family/replication.md b/docs/ru/engines/table-engines/mergetree-family/replication.md index 51379c5a8cf..54f390c293e 100644 --- a/docs/ru/engines/table-engines/mergetree-family/replication.md +++ b/docs/ru/engines/table-engines/mergetree-family/replication.md @@ -58,7 +58,7 @@ ClickHouse хранит метаинформацию о репликах в [Apa Для очень больших кластеров, можно использовать разные кластеры ZooKeeper для разных шардов. Впрочем, на кластере Яндекс.Метрики (примерно 300 серверов) такой необходимости не возникает. -Репликация асинхронная, мульти-мастер. Запросы `INSERT` и `ALTER` можно направлять на любой доступный сервер. Данные вставятся на сервер, где выполнен запрос, а затем скопируются на остальные серверы. В связи с асинхронностью, только что вставленные данные появляются на остальных репликах с небольшой задержкой. Если часть реплик недоступна, данные на них запишутся тогда, когда они станут доступны. Если реплика доступна, то задержка составляет столько времени, сколько требуется для передачи блока сжатых данных по сети. +Репликация асинхронная, мульти-мастер. Запросы `INSERT` и `ALTER` можно направлять на любой доступный сервер. Данные вставятся на сервер, где выполнен запрос, а затем скопируются на остальные серверы. В связи с асинхронностью, только что вставленные данные появляются на остальных репликах с небольшой задержкой. Если часть реплик недоступна, данные на них запишутся тогда, когда они станут доступны. Если реплика доступна, то задержка составляет столько времени, сколько требуется для передачи блока сжатых данных по сети. Количество потоков для выполнения фоновых задач можно задать с помощью настройки [background_schedule_pool_size](../../../operations/settings/settings.md#background_schedule_pool_size). По умолчанию, запрос INSERT ждёт подтверждения записи только от одной реплики. Если данные были успешно записаны только на одну реплику, и сервер с этой репликой перестал существовать, то записанные данные будут потеряны. Вы можете включить подтверждение записи от нескольких реплик, используя настройку `insert_quorum`. diff --git a/docs/ru/engines/table-engines/special/distributed.md b/docs/ru/engines/table-engines/special/distributed.md index cd92332b07e..68753a7c217 100644 --- a/docs/ru/engines/table-engines/special/distributed.md +++ b/docs/ru/engines/table-engines/special/distributed.md @@ -107,7 +107,7 @@ logs - имя кластера в конфигурационном файле с - используются запросы, требующие соединение данных (IN, JOIN) по определённому ключу - тогда если данные шардированы по этому ключу, то можно использовать локальные IN, JOIN вместо GLOBAL IN, GLOBAL JOIN, что кардинально более эффективно. - используется большое количество серверов (сотни и больше) и большое количество маленьких запросов (запросы отдельных клиентов - сайтов, рекламодателей, партнёров) - тогда, для того, чтобы маленькие запросы не затрагивали весь кластер, имеет смысл располагать данные одного клиента на одном шарде, или (вариант, который используется в Яндекс.Метрике) сделать двухуровневое шардирование: разбить весь кластер на «слои», где слой может состоять из нескольких шардов; данные для одного клиента располагаются на одном слое, но в один слой можно по мере необходимости добавлять шарды, в рамках которых данные распределены произвольным образом; создаются распределённые таблицы на каждый слой и одна общая распределённая таблица для глобальных запросов. -Запись данных осуществляется полностью асинхронно. При вставке в таблицу, блок данных сначала записывается в файловую систему. Затем, в фоновом режиме отправляются на удалённые серверы при первой возможности. Период отправки регулируется настройками [distributed\_directory\_monitor\_sleep\_time\_ms](../../../operations/settings/settings.md#distributed_directory_monitor_sleep_time_ms) и [distributed\_directory\_monitor\_max\_sleep\_time\_ms](../../../operations/settings/settings.md#distributed_directory_monitor_max_sleep_time_ms). Движок таблиц `Distributed` отправляет каждый файл со вставленными данными отдельно, но можно включить пакетную отправку данных настройкой [distributed\_directory\_monitor\_batch\_inserts](../../../operations/settings/settings.md#distributed_directory_monitor_batch_inserts). Эта настройка улучшает производительность кластера за счет более оптимального использования ресурсов сервера-отправителя и сети. Необходимо проверять, что данные отправлены успешно, для этого проверьте список файлов (данных, ожидающих отправки) в каталоге таблицы `/var/lib/clickhouse/data/database/table/`. +Запись данных осуществляется полностью асинхронно. При вставке в таблицу, блок данных сначала записывается в файловую систему. Затем, в фоновом режиме отправляются на удалённые серверы при первой возможности. Период отправки регулируется настройками [distributed\_directory\_monitor\_sleep\_time\_ms](../../../operations/settings/settings.md#distributed_directory_monitor_sleep_time_ms) и [distributed\_directory\_monitor\_max\_sleep\_time\_ms](../../../operations/settings/settings.md#distributed_directory_monitor_max_sleep_time_ms). Движок таблиц `Distributed` отправляет каждый файл со вставленными данными отдельно, но можно включить пакетную отправку данных настройкой [distributed\_directory\_monitor\_batch\_inserts](../../../operations/settings/settings.md#distributed_directory_monitor_batch_inserts). Эта настройка улучшает производительность кластера за счет более оптимального использования ресурсов сервера-отправителя и сети. Необходимо проверять, что данные отправлены успешно, для этого проверьте список файлов (данных, ожидающих отправки) в каталоге таблицы `/var/lib/clickhouse/data/database/table/`. Количество потоков для выполнения фоновых задач можно задать с помощью настройки [background_distributed_schedule_pool_size](../../../operations/settings/settings.md#background_distributed_schedule_pool_size). Если после INSERT-а в Distributed таблицу, сервер перестал существовать или был грубо перезапущен (например, в следствие аппаратного сбоя), то записанные данные могут быть потеряны. Если в директории таблицы обнаружен повреждённый кусок данных, то он переносится в поддиректорию broken и больше не используется. diff --git a/docs/ru/getting-started/install.md b/docs/ru/getting-started/install.md index 04712328844..19943f182d8 100644 --- a/docs/ru/getting-started/install.md +++ b/docs/ru/getting-started/install.md @@ -34,8 +34,8 @@ $ grep -q sse4_2 /proc/cpuinfo && echo "SSE 4.2 supported" || echo "SSE 4.2 not ``` bash sudo yum install yum-utils -sudo rpm --import https://repo.yandex.ru/clickhouse/CLICKHOUSE-KEY.GPG -sudo yum-config-manager --add-repo https://repo.yandex.ru/clickhouse/rpm/stable/x86_64 +sudo rpm --import https://repo.clickhouse.tech/CLICKHOUSE-KEY.GPG +sudo yum-config-manager --add-repo https://repo.clickhouse.tech/rpm/stable/x86_64 ``` Для использования наиболее свежих версий нужно заменить `stable` на `testing` (рекомендуется для тестовых окружений). Также иногда доступен `prestable`. @@ -46,21 +46,21 @@ sudo yum-config-manager --add-repo https://repo.yandex.ru/clickhouse/rpm/stable/ sudo yum install clickhouse-server clickhouse-client ``` -Также есть возможность установить пакеты вручную, скачав отсюда: https://repo.yandex.ru/clickhouse/rpm/stable/x86\_64. +Также есть возможность установить пакеты вручную, скачав отсюда: https://repo.clickhouse.tech/rpm/stable/x86\_64. ### Из Tgz архивов {#from-tgz-archives} Команда ClickHouse в Яндексе рекомендует использовать предкомпилированные бинарники из `tgz` архивов для всех дистрибутивов, где невозможна установка `deb` и `rpm` пакетов. -Интересующую версию архивов можно скачать вручную с помощью `curl` или `wget` из репозитория https://repo.yandex.ru/clickhouse/tgz/. +Интересующую версию архивов можно скачать вручную с помощью `curl` или `wget` из репозитория https://repo.clickhouse.tech/tgz/. После этого архивы нужно распаковать и воспользоваться скриптами установки. Пример установки самой свежей версии: ``` bash export LATEST_VERSION=`curl https://api.github.com/repos/ClickHouse/ClickHouse/tags 2>/dev/null | grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | head -n 1` -curl -O https://repo.yandex.ru/clickhouse/tgz/clickhouse-common-static-$LATEST_VERSION.tgz -curl -O https://repo.yandex.ru/clickhouse/tgz/clickhouse-common-static-dbg-$LATEST_VERSION.tgz -curl -O https://repo.yandex.ru/clickhouse/tgz/clickhouse-server-$LATEST_VERSION.tgz -curl -O https://repo.yandex.ru/clickhouse/tgz/clickhouse-client-$LATEST_VERSION.tgz +curl -O https://repo.clickhouse.tech/tgz/clickhouse-common-static-$LATEST_VERSION.tgz +curl -O https://repo.clickhouse.tech/tgz/clickhouse-common-static-dbg-$LATEST_VERSION.tgz +curl -O https://repo.clickhouse.tech/tgz/clickhouse-server-$LATEST_VERSION.tgz +curl -O https://repo.clickhouse.tech/tgz/clickhouse-client-$LATEST_VERSION.tgz tar -xzvf clickhouse-common-static-$LATEST_VERSION.tgz sudo clickhouse-common-static-$LATEST_VERSION/install/doinst.sh diff --git a/docs/ru/interfaces/formats.md b/docs/ru/interfaces/formats.md index 75e87a34f88..822f5543f9b 100644 --- a/docs/ru/interfaces/formats.md +++ b/docs/ru/interfaces/formats.md @@ -525,7 +525,7 @@ CREATE TABLE IF NOT EXISTS example_table - Если `input_format_defaults_for_omitted_fields = 1`, то значение по умолчанию для `x` равно `0`, а значение по умолчанию `a` равно `x * 2`. !!! note "Предупреждение" - Если `insert_sample_with_metadata = 1`, то при обработке запросов ClickHouse потребляет больше вычислительных ресурсов, чем если `insert_sample_with_metadata = 0`. + Если `input_format_defaults_for_omitted_fields = 1`, то при обработке запросов ClickHouse потребляет больше вычислительных ресурсов, чем если `input_format_defaults_for_omitted_fields = 0`. ### Выборка данных {#vyborka-dannykh} diff --git a/docs/ru/operations/server-configuration-parameters/settings.md b/docs/ru/operations/server-configuration-parameters/settings.md index ccde7a945ac..e3c1629a46a 100644 --- a/docs/ru/operations/server-configuration-parameters/settings.md +++ b/docs/ru/operations/server-configuration-parameters/settings.md @@ -843,6 +843,10 @@ ClickHouse использует ZooKeeper для хранения метадан **Значение по умолчанию**: 15. +**Смотрите также** + +- [background_schedule_pool_size](../settings/settings.md#background_schedule_pool_size) + ## access_control_path {#access_control_path} Путь к каталогу, где сервер ClickHouse хранит конфигурации пользователей и ролей, созданные командами SQL. diff --git a/docs/ru/operations/settings/settings.md b/docs/ru/operations/settings/settings.md index 4dd43e9607b..5e34affcaac 100644 --- a/docs/ru/operations/settings/settings.md +++ b/docs/ru/operations/settings/settings.md @@ -1181,4 +1181,44 @@ Default value: 0. Значение по умолчанию: 16. +## background_buffer_flush_schedule_pool_size {#background_buffer_flush_schedule_pool_size} + +Задает количество потоков для выполнения фонового сброса данных в таблицах с движком [Buffer](../../engines/table-engines/special/buffer.md). Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 16. + +## background_move_pool_size {#background_move_pool_size} + +Задает количество потоков для фоновых перемещений кусков между дисками. Работает для таблиц с движком [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md#table_engine-mergetree-multiple-volumes). Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 8. + +## background_schedule_pool_size {#background_schedule_pool_size} + +Задает количество потоков для выполнения фоновых задач. Работает для [реплицируемых](../../engines/table-engines/mergetree-family/replication.md) таблиц, стримов в [Kafka](../../engines/table-engines/integrations/kafka.md) и обновления IP адресов у записей во внутреннем [DNS кеше](../server-configuration-parameters/settings.md#server-settings-dns-cache-update-period). Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 16. + +## background_distributed_schedule_pool_size {#background_distributed_schedule_pool_size} + +Задает количество потоков для выполнения фоновых задач. Работает для таблиц с движком [Distributed](../../engines/table-engines/special/distributed.md). Настройка применяется при запуске сервера ClickHouse и не может быть изменена в пользовательском сеансе. + +Допустимые значения: + +- Положительное целое число. + +Значение по умолчанию: 16. + [Оригинальная статья](https://clickhouse.tech/docs/ru/operations/settings/settings/) diff --git a/docs/ru/operations/system-tables.md b/docs/ru/operations/system-tables.md index 8954a484560..6e57e7a63f3 100644 --- a/docs/ru/operations/system-tables.md +++ b/docs/ru/operations/system-tables.md @@ -132,6 +132,41 @@ SELECT * FROM system.contributors WHERE name='Olga Khvostikova' Для каждой базы данных, о которой знает сервер, будет присутствовать соответствующая запись в таблице. Эта системная таблица используется для реализации запроса `SHOW DATABASES`. +## system.data_type_families {#system_tables-data_type_families} + +Содержит информацию о поддерживаемых [типах данных](../sql-reference/data-types/). + +Столбцы: + +- `name` ([String](../sql-reference/data-types/string.md)) — имя типа данных. +- `case_insensitive` ([UInt8](../sql-reference/data-types/int-uint.md)) — свойство, которое показывает, зависит ли имя типа данных в запросе от регистра. Например, допустимы и `Date`, и `date`. +- `alias_to` ([String](../sql-reference/data-types/string.md)) — тип данных, для которого `name` является алиасом. + +**Пример** + +``` sql +SELECT * FROM system.data_type_families WHERE alias_to = 'String' +``` + +``` text +┌─name───────┬─case_insensitive─┬─alias_to─┐ +│ LONGBLOB │ 1 │ String │ +│ LONGTEXT │ 1 │ String │ +│ TINYTEXT │ 1 │ String │ +│ TEXT │ 1 │ String │ +│ VARCHAR │ 1 │ String │ +│ MEDIUMBLOB │ 1 │ String │ +│ BLOB │ 1 │ String │ +│ TINYBLOB │ 1 │ String │ +│ CHAR │ 1 │ String │ +│ MEDIUMTEXT │ 1 │ String │ +└────────────┴──────────────────┴──────────┘ +``` + +**See Also** + +- [Синтаксис](../sql-reference/syntax.md) — поддерживаемый SQL синтаксис. + ## system.detached\_parts {#system_tables-detached_parts} Содержит информацию об отсоединённых кусках таблиц семейства [MergeTree](../engines/table-engines/mergetree-family/mergetree.md). Столбец `reason` содержит причину, по которой кусок был отсоединён. Для кусов, отсоединённых пользователем, `reason` содержит пустую строку. @@ -558,15 +593,9 @@ CurrentMetric_ReplicatedChecks: 0 Можно отключить логгирование настройкой [log_queries = 0](settings/settings.md#settings-log-queries). По-возможности, не отключайте логгирование, поскольку информация из таблицы важна при решении проблем. -Период сброса логов в таблицу задаётся параметром `flush_interval_milliseconds` в конфигурационной секции [query_log](server-configuration-parameters/settings.md#server_configuration_parameters-query-log). Чтобы принудительно записать логи из буффера памяти в таблицу, используйте запрос [SYSTEM FLUSH LOGS](../sql-reference/statements/system.md#query_language-system-flush_logs). +Период сброса данных в таблицу задаётся параметром `flush_interval_milliseconds` в конфигурационной секции [query_log](server-configuration-parameters/settings.md#server_configuration_parameters-query-log). Чтобы принудительно записать логи из буффера памяти в таблицу, используйте запрос [SYSTEM FLUSH LOGS](../sql-reference/statements/system.md#query_language-system-flush_logs). -ClickHouse не удаляет логи из таблица автоматически. Смотрите [Введение](#system-tables-introduction). - -Можно указать произвольный ключ партиционирования для таблицы `system.query_log` в конфигурации [query\_log](server-configuration-parameters/settings.md#server_configuration_parameters-query-log) (параметр `partition_by`). - - - -Если таблицу удалить вручную, она создается заново автоматически «на лету». При этом все логи на момент удаления таблицы будут убраны. +ClickHouse не удаляет данные из таблица автоматически. Смотрите [Введение](#system-tables-introduction). Таблица `system.query_log` содержит информацию о двух видах запросов: @@ -694,71 +723,116 @@ Settings.Values: ['0','random','1','10000000000'] ## system.query_thread_log {#system_tables-query_thread_log} -Содержит информацию о каждом потоке выполняемых запросов. +Содержит информацию о потоках, которые выполняют запросы, например, имя потока, время его запуска, продолжительность обработки запроса. -ClickHouse создаёт таблицу только в том случае, когда установлен конфигурационный параметр сервера [query\_thread\_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log). Параметр задаёт правила ведения лога, такие как интервал логирования или имя таблицы, в которую будут логгироваться запросы. +Чтобы начать логирование: -Чтобы включить логирование, задайте значение параметра [log\_query\_threads](settings/settings.md#settings-log-query-threads) равным 1. Подробности смотрите в разделе [Настройки](settings/settings.md#settings). +1. Настройте параметры [query_thread_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log) в конфигурации сервера. +2. Установите значение [log_query_threads](settings/settings.md#settings-log-query-threads) равным 1. + +Интервал сброса данных в таблицу задаётся параметром `flush_interval_milliseconds` в разделе настроек сервера [query_thread_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log). Чтобы принудительно записать логи из буфера памяти в таблицу, используйте запрос [SYSTEM FLUSH LOGS](../sql-reference/statements/system.md#query_language-system-flush_logs). + +ClickHouse не удаляет данные из таблицы автоматически. Подробности в разделе [Введение](#system-tables-introduction). Столбцы: -- `event_date` (Date) — дата завершения выполнения запроса потоком. -- `event_time` (DateTime) — дата и время завершения выполнения запроса потоком. -- `query_start_time` (DateTime) — время начала обработки запроса. -- `query_duration_ms` (UInt64) — длительность обработки запроса в миллисекундах. -- `read_rows` (UInt64) — количество прочитанных строк. -- `read_bytes` (UInt64) — количество прочитанных байтов. -- `written_rows` (UInt64) — количество записанных строк для запросов `INSERT`. Для других запросов, значение столбца 0. -- `written_bytes` (UInt64) — объём записанных данных в байтах для запросов `INSERT`. Для других запросов, значение столбца 0. -- `memory_usage` (Int64) — разница между выделенной и освобождённой памятью в контексте потока. -- `peak_memory_usage` (Int64) — максимальная разница между выделенной и освобождённой памятью в контексте потока. -- `thread_name` (String) — Имя потока. -- `thread_id` (UInt64) — tid (ID потока операционной системы). -- `master_thread_id` (UInt64) — tid (ID потока операционной системы) главного потока. -- `query` (String) — текст запроса. -- `is_initial_query` (UInt8) — вид запроса. Возможные значения: +- `event_date` ([Date](../sql-reference/data-types/date.md)) — дата завершения выполнения запроса потоком. +- `event_time` ([DateTime](../sql-reference/data-types/datetime.md)) — дата и время завершения выполнения запроса потоком. +- `query_start_time` ([DateTime](../sql-reference/data-types/datetime.md)) — время начала обработки запроса. +- `query_duration_ms` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — длительность обработки запроса в миллисекундах. +- `read_rows` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — количество прочитанных строк. +- `read_bytes` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — количество прочитанных байтов. +- `written_rows` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — количество записанных строк для запросов `INSERT`. Для других запросов, значение столбца 0. +- `written_bytes` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — объём записанных данных в байтах для запросов `INSERT`. Для других запросов, значение столбца 0. +- `memory_usage` ([Int64](../sql-reference/data-types/int-uint.md)) — разница между выделенной и освобождённой памятью в контексте потока. +- `peak_memory_usage` ([Int64](../sql-reference/data-types/int-uint.md)) — максимальная разница между выделенной и освобождённой памятью в контексте потока. +- `thread_name` ([String](../sql-reference/data-types/string.md)) — Имя потока. +- `thread_id` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — tid (ID потока операционной системы). +- `master_thread_id` ([UInt64](../sql-reference/data-types/int-uint.md#uint-ranges)) — tid (ID потока операционной системы) главного потока. +- `query` ([String](../sql-reference/data-types/string.md)) — текст запроса. +- `is_initial_query` ([UInt8](../sql-reference/data-types/int-uint.md#uint-ranges)) — вид запроса. Возможные значения: - 1 — запрос был инициирован клиентом. - 0 — запрос был инициирован другим запросом при распределенном запросе. -- `user` (String) — пользователь, запустивший текущий запрос. -- `query_id` (String) — ID запроса. -- `address` (IPv6) — IP адрес, с которого пришел запрос. -- `port` (UInt16) — порт, с которого пришел запрос. -- `initial_user` (String) — пользователь, запустивший первоначальный запрос (для распределенных запросов). -- `initial_query_id` (String) — ID родительского запроса. -- `initial_address` (IPv6) — IP адрес, с которого пришел родительский запрос. -- `initial_port` (UInt16) — порт, пришел родительский запрос. -- `interface` (UInt8) — интерфейс, с которого ушёл запрос. Возможные значения: +- `user` ([String](../sql-reference/data-types/string.md)) — пользователь, запустивший текущий запрос. +- `query_id` ([String](../sql-reference/data-types/string.md)) — ID запроса. +- `address` ([IPv6](../sql-reference/data-types/domains/ipv6.md)) — IP адрес, с которого пришел запрос. +- `port` ([UInt16](../sql-reference/data-types/int-uint.md#uint-ranges)) — порт, с которого пришел запрос. +- `initial_user` ([String](../sql-reference/data-types/string.md)) — пользователь, запустивший первоначальный запрос (для распределенных запросов). +- `initial_query_id` ([String](../sql-reference/data-types/string.md)) — ID родительского запроса. +- `initial_address` ([IPv6](../sql-reference/data-types/domains/ipv6.md)) — IP адрес, с которого пришел родительский запрос. +- `initial_port` ([UInt16](../sql-reference/data-types/int-uint.md#uint-ranges)) — порт, пришел родительский запрос. +- `interface` ([UInt8](../sql-reference/data-types/int-uint.md#uint-ranges)) — интерфейс, с которого ушёл запрос. Возможные значения: - 1 — TCP. - 2 — HTTP. -- `os_user` (String) — имя пользователя в OS, который запустил [clickhouse-client](../interfaces/cli.md). -- `client_hostname` (String) — hostname клиентской машины, с которой присоединился [clickhouse-client](../interfaces/cli.md) или другой TCP клиент. -- `client_name` (String) — [clickhouse-client](../interfaces/cli.md) или другой TCP клиент. -- `client_revision` (UInt32) — ревизия [clickhouse-client](../interfaces/cli.md) или другого TCP клиента. -- `client_version_major` (UInt32) — старшая версия [clickhouse-client](../interfaces/cli.md) или другого TCP клиента. -- `client_version_minor` (UInt32) — младшая версия [clickhouse-client](../interfaces/cli.md) или другого TCP клиента. -- `client_version_patch` (UInt32) — патч [clickhouse-client](../interfaces/cli.md) или другого TCP клиента. -- `http_method` (UInt8) — HTTP метод, инициировавший запрос. Возможные значения: +- `os_user` ([String](../sql-reference/data-types/string.md)) — имя пользователя в OS, который запустил [clickhouse-client](../interfaces/cli.md). +- `client_hostname` ([String](../sql-reference/data-types/string.md)) — hostname клиентской машины, с которой присоединился [clickhouse-client](../interfaces/cli.md) или другой TCP клиент. +- `client_name` ([String](../sql-reference/data-types/string.md)) — [clickhouse-client](../interfaces/cli.md) или другой TCP клиент. +- `client_revision` ([UInt32](../sql-reference/data-types/int-uint.md)) — ревизия [clickhouse-client](../interfaces/cli.md) или другого TCP клиента. +- `client_version_major` ([UInt32](../sql-reference/data-types/int-uint.md)) — старшая версия [clickhouse-client](../interfaces/cli.md) или другого TCP клиента. +- `client_version_minor` ([UInt32](../sql-reference/data-types/int-uint.md)) — младшая версия [clickhouse-client](../interfaces/cli.md) или другого TCP клиента. +- `client_version_patch` ([UInt32](../sql-reference/data-types/int-uint.md)) — патч [clickhouse-client](../interfaces/cli.md) или другого TCP клиента. +- `http_method` ([UInt8](../sql-reference/data-types/int-uint.md#uint-ranges)) — HTTP метод, инициировавший запрос. Возможные значения: - 0 — запрос запущен с интерфейса TCP. - 1 — `GET`. - 2 — `POST`. -- `http_user_agent` (String) — HTTP заголовок `UserAgent`. -- `quota_key` (String) — «ключ квоты» из настроек [квот](quotas.md) (см. `keyed`). -- `revision` (UInt32) — ревизия ClickHouse. -- `ProfileEvents.Names` (Array(String)) — Счетчики для изменения различных метрик для данного потока. Описание метрик можно получить из таблицы [system.events](#system_tables-events)(\#system\_tables-events -- `ProfileEvents.Values` (Array(UInt64)) — метрики для данного потока, перечисленные в столбце `ProfileEvents.Names`. +- `http_user_agent` ([String](../sql-reference/data-types/string.md)) — HTTP заголовок `UserAgent`. +- `quota_key` ([String](../sql-reference/data-types/string.md)) — «ключ квоты» из настроек [квот](quotas.md) (см. `keyed`). +- `revision` ([UInt32](../sql-reference/data-types/int-uint.md)) — ревизия ClickHouse. +- `ProfileEvents.Names` ([Array(String)](../sql-reference/data-types/array.md)) — Счетчики для изменения различных метрик для данного потока. Описание метрик можно получить из таблицы [system.events](#system_tables-events). +- `ProfileEvents.Values` ([Array(UInt64)](../sql-reference/data-types/array.md)) — метрики для данного потока, перечисленные в столбце `ProfileEvents.Names`. -По умолчанию, строки добавляются в таблицу логирования с интервалом в 7,5 секунд. Можно задать интервал в конфигурационном параметре сервера [query\_thread\_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log) (смотрите параметр `flush_interval_milliseconds`). Чтобы принудительно записать логи из буффера памяти в таблицу, используйте запрос `SYSTEM FLUSH LOGS`. +**Пример** -Если таблицу удалить вручную, она пересоздастся автоматически «на лету». При этом все логи на момент удаления таблицы будут удалены. +``` sql + SELECT * FROM system.query_thread_log LIMIT 1 FORMAT Vertical +``` -!!! note "Примечание" - Срок хранения логов не ограничен. Логи не удаляются из таблицы автоматически. Вам необходимо самостоятельно организовать удаление устаревших логов. +``` text +Row 1: +────── +event_date: 2020-05-13 +event_time: 2020-05-13 14:02:28 +query_start_time: 2020-05-13 14:02:28 +query_duration_ms: 0 +read_rows: 1 +read_bytes: 1 +written_rows: 0 +written_bytes: 0 +memory_usage: 0 +peak_memory_usage: 0 +thread_name: QueryPipelineEx +thread_id: 28952 +master_thread_id: 28924 +query: SELECT 1 +is_initial_query: 1 +user: default +query_id: 5e834082-6f6d-4e34-b47b-cd1934f4002a +address: ::ffff:127.0.0.1 +port: 57720 +initial_user: default +initial_query_id: 5e834082-6f6d-4e34-b47b-cd1934f4002a +initial_address: ::ffff:127.0.0.1 +initial_port: 57720 +interface: 1 +os_user: bayonet +client_hostname: clickhouse.ru-central1.internal +client_name: ClickHouse client +client_revision: 54434 +client_version_major: 20 +client_version_minor: 4 +client_version_patch: 1 +http_method: 0 +http_user_agent: +quota_key: +revision: 54434 +ProfileEvents.Names: ['ContextLock','RealTimeMicroseconds','UserTimeMicroseconds','OSCPUWaitMicroseconds','OSCPUVirtualTimeMicroseconds'] +ProfileEvents.Values: [1,97,81,5,81] +... +``` -Можно указать произвольный ключ партиционирования для таблицы `system.query_log` в конфигурации [query\_thread\_log](server-configuration-parameters/settings.md#server_configuration_parameters-query_thread_log) (параметр `partition_by`). +**Смотрите также** -## system.query_thread_log {#system_tables-query_thread_log} - -Содержит информацию о каждом потоке исполнения запроса. +- [system.query_log](#system_tables-query_log) — описание системной таблицы `query_log`, которая содержит общую информацию о выполненных запросах. ## system.trace\_log {#system_tables-trace_log} diff --git a/docs/ru/sql-reference/data-types/index.md b/docs/ru/sql-reference/data-types/index.md index 8b4f28aad3e..65bce13a1a4 100644 --- a/docs/ru/sql-reference/data-types/index.md +++ b/docs/ru/sql-reference/data-types/index.md @@ -8,5 +8,7 @@ toc_title: "\u0412\u0432\u0435\u0434\u0435\u043D\u0438\u0435" ClickHouse может сохранять в ячейках таблиц данные различных типов. +Зависимость имен типов данных от регистра можно проверить в системной таблице [system.data_type_families](../../operations/system-tables.md#system_tables-data_type_families). + Раздел содержит описания поддерживаемых типов данных и специфику их использования и/или реализации, если таковые имеются. [Оригинальная статья](https://clickhouse.tech/docs/ru/data_types/) diff --git a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-hierarchical.md b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-hierarchical.md index 3534fc4e48a..1f09eb28d2e 100644 --- a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-hierarchical.md +++ b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-hierarchical.md @@ -32,7 +32,7 @@ ClickHouse поддерживает иерархические словари с ClickHouse поддерживает свойство [hierarchical](external-dicts-dict-structure.md#hierarchical-dict-attr) для атрибутов [внешнего словаря](index.md). Это свойство позволяет конфигурировать словари, подобные описанному выше. -С помощью функции [dictGetHierarchy](../../../sql-reference/dictionaries/external-dictionaries/external-dicts-dict-hierarchical.md#dictgethierarchy) можно получить цепочку предков элемента. +С помощью функции [dictGetHierarchy](../../../sql-reference/functions/ext-dict-functions.md#dictgethierarchy) можно получить цепочку предков элемента. Структура словаря для нашего примера может выглядеть следующим образом: diff --git a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-layout.md b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-layout.md index 9256fab5e0c..368da949dc8 100644 --- a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-layout.md +++ b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-layout.md @@ -2,7 +2,7 @@ Словари можно размещать в памяти множеством способов. -Рекомендуем [flat](#flat), [hashed](#hashed) и [complex\_key\_hashed](#complex-key-hashed). Скорость обработки словарей при этом максимальна. +Рекомендуем [flat](#flat), [hashed](#dicts-external_dicts_dict_layout-hashed) и [complex\_key\_hashed](#complex-key-hashed). Скорость обработки словарей при этом максимальна. Размещение с кэшированием не рекомендуется использовать из-за потенциально низкой производительности и сложностей в подборе оптимальных параметров. Читайте об этом подробнее в разделе «[cache](#cache)». @@ -34,7 +34,7 @@ ``` -Соответствущий [DDL-запрос](../../../sql-reference/statements/create.md#create-dictionary-query): +Соответствущий [DDL-запрос](../../statements/create.md#create-dictionary-query): ``` sql CREATE DICTIONARY (...) @@ -46,7 +46,7 @@ LAYOUT(LAYOUT_TYPE(param value)) -- layout settings ## Способы размещения словарей в памяти {#sposoby-razmeshcheniia-slovarei-v-pamiati} - [flat](#flat) -- [hashed](#hashed) +- [hashed](#dicts-external_dicts_dict_layout-hashed) - [sparse\_hashed](#dicts-external_dicts_dict_layout-sparse_hashed) - [cache](#cache) - [direct](#direct) @@ -80,7 +80,7 @@ LAYOUT(LAYOUT_TYPE(param value)) -- layout settings LAYOUT(FLAT()) ``` -### hashed {#hashed} +### hashed {#dicts-external_dicts_dict_layout-hashed} Словарь полностью хранится в оперативной памяти в виде хэш-таблиц. Словарь может содержать произвольное количество элементов с произвольными идентификаторами. На практике, количество ключей может достигать десятков миллионов элементов. diff --git a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md index 4190e8e1015..e5b20f3960c 100644 --- a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md +++ b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md @@ -19,7 +19,7 @@ ``` -Аналогичный [DDL-запрос](../../../sql-reference/statements/create.md#create-dictionary-query): +Аналогичный [DDL-запрос](../../statements/create.md#create-dictionary-query): ``` sql CREATE DICTIONARY dict_name (...) @@ -150,7 +150,7 @@ SOURCE(HTTP( )) ``` -Чтобы ClickHouse смог обратиться к HTTPS-ресурсу, необходимо [настроить openSSL](../../../sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md) в конфигурации сервера. +Чтобы ClickHouse смог обратиться к HTTPS-ресурсу, необходимо [настроить openSSL](../../../operations/server-configuration-parameters/settings.md#server_configuration_parameters-openssl) в конфигурации сервера. Поля настройки: @@ -531,7 +531,7 @@ SOURCE(CLICKHOUSE( Поля настройки: -- `host` — хост ClickHouse. Если host локальный, то запрос выполняется без сетевого взаимодействия. Чтобы повысить отказоустойчивость решения, можно создать таблицу типа [Distributed](../../../sql-reference/dictionaries/external-dictionaries/external-dicts-dict-sources.md) и прописать её в дальнейших настройках. +- `host` — хост ClickHouse. Если host локальный, то запрос выполняется без сетевого взаимодействия. Чтобы повысить отказоустойчивость решения, можно создать таблицу типа [Distributed](../../../engines/table-engines/special/distributed.md) и прописать её в дальнейших настройках. - `port` — порт сервера ClickHouse. - `user` — имя пользователя ClickHouse. - `password` — пароль пользователя ClickHouse. diff --git a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-structure.md b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-structure.md index 27702959eac..4c3b4eb22e4 100644 --- a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-structure.md +++ b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict-structure.md @@ -154,7 +154,7 @@ CREATE DICTIONARY somename ( | Тег | Описание | Обязательный | |------------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------| | `name` | Имя столбца. | Да | -| `type` | Тип данных ClickHouse.
ClickHouse пытается привести значение из словаря к заданному типу данных. Например, в случае MySQL, в таблице-источнике поле может быть `TEXT`, `VARCHAR`, `BLOB`, но загружено может быть как `String`. [Nullable](../../../sql-reference/dictionaries/external-dictionaries/external-dicts-dict-structure.md) не поддерживается. | Да | +| `type` | Тип данных ClickHouse.
ClickHouse пытается привести значение из словаря к заданному типу данных. Например, в случае MySQL, в таблице-источнике поле может быть `TEXT`, `VARCHAR`, `BLOB`, но загружено может быть как `String`. [Nullable](../../../sql-reference/data-types/nullable.md) не поддерживается. | Да | | `null_value` | Значение по умолчанию для несуществующего элемента.
В примере это пустая строка. Нельзя указать значение `NULL`. | Да | | `expression` | [Выражение](../../syntax.md#syntax-expressions), которое ClickHouse выполняет со значением.
Выражением может быть имя столбца в удаленной SQL базе. Таким образом, вы можете использовать его для создания псевдонима удаленного столбца.

Значение по умолчанию: нет выражения. | Нет | | `hierarchical` | Если `true`, то атрибут содержит ключ предка для текущего элемента. Смотрите [Иерархические словари](external-dicts-dict-hierarchical.md).

Default value: `false`. | No | @@ -162,6 +162,6 @@ CREATE DICTIONARY somename ( ## Смотрите также {#smotrite-takzhe} -- [Функции для работы с внешними словарями](../../../sql-reference/dictionaries/external-dictionaries/external-dicts-dict-structure.md). +- [Функции для работы с внешними словарями](../../../sql-reference/functions/ext-dict-functions.md). [Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/dicts/external_dicts_dict_structure/) diff --git a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict.md b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict.md index 9eb6c8d8d86..a7d3394864b 100644 --- a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict.md +++ b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts-dict.md @@ -24,7 +24,7 @@ XML-конфигурация словаря имеет следующую стр ``` -Соответствующий [DDL-запрос](../../../sql-reference/statements/create.md#create-dictionary-query) имеет следующий вид: +Соответствующий [DDL-запрос](../../statements/create.md#create-dictionary-query) имеет следующий вид: ``` sql CREATE DICTIONARY dict_name diff --git a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts.md b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts.md index 7442a5dd3be..80f717dfe93 100644 --- a/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts.md +++ b/docs/ru/sql-reference/dictionaries/external-dictionaries/external-dicts.md @@ -5,11 +5,11 @@ ClickHouse: - Полностью или частично хранит словари в оперативной памяти. - Периодически обновляет их и динамически подгружает отсутствующие значения. -- Позволяет создавать внешние словари с помощью xml-файлов или [DDL-запросов](../../../sql-reference/statements/create.md#create-dictionary-query). +- Позволяет создавать внешние словари с помощью xml-файлов или [DDL-запросов](../../statements/create.md#create-dictionary-query). -Конфигурация внешних словарей может находится в одном или нескольких xml-файлах. Путь к конфигурации указывается в параметре [dictionaries\_config](../../../sql-reference/dictionaries/external-dictionaries/external-dicts.md). +Конфигурация внешних словарей может находится в одном или нескольких xml-файлах. Путь к конфигурации указывается в параметре [dictionaries\_config](../../../operations/server-configuration-parameters/settings.md#server_configuration_parameters-dictionaries_config). -Словари могут загружаться при старте сервера или при первом использовании, в зависимости от настройки [dictionaries\_lazy\_load](../../../sql-reference/dictionaries/external-dictionaries/external-dicts.md). +Словари могут загружаться при старте сервера или при первом использовании, в зависимости от настройки [dictionaries\_lazy\_load](../../../operations/server-configuration-parameters/settings.md#server_configuration_parameters-dictionaries_lazy_load). Системная таблица [system.dictionaries](../../../operations/system-tables.md#system_tables-dictionaries) содержит информацию о словарях, сконфигурированных на сервере. Для каждого словаря там можно найти: @@ -41,10 +41,10 @@ ClickHouse: В одном файле можно [сконфигурировать](external-dicts-dict.md) произвольное количество словарей. -Если вы создаёте внешние словари [DDL-запросами](../../../sql-reference/statements/create.md#create-dictionary-query), то не задавайте конфигурацию словаря в конфигурации сервера. +Если вы создаёте внешние словари [DDL-запросами](../../statements/create.md#create-dictionary-query), то не задавайте конфигурацию словаря в конфигурации сервера. !!! attention "Внимание" - Можно преобразовывать значения по небольшому словарю, описав его в запросе `SELECT` (см. функцию [transform](../../../sql-reference/dictionaries/external-dictionaries/external-dicts.md)). Эта функциональность не связана с внешними словарями. + Можно преобразовывать значения по небольшому словарю, описав его в запросе `SELECT` (см. функцию [transform](../../../sql-reference/functions/other-functions.md)). Эта функциональность не связана с внешними словарями. ## Смотрите также {#ext-dicts-see-also} @@ -53,6 +53,6 @@ ClickHouse: - [Обновление словарей](external-dicts-dict-lifetime.md) - [Источники внешних словарей](external-dicts-dict-sources.md) - [Ключ и поля словаря](external-dicts-dict-structure.md) -- [Функции для работы с внешними словарями](../../../sql-reference/dictionaries/external-dictionaries/external-dicts.md#ext_dict_functions) +- [Функции для работы с внешними словарями](../../../sql-reference/functions/ext-dict-functions.md) [Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/dicts/external_dicts/) diff --git a/docs/ru/sql-reference/functions/geo.md b/docs/ru/sql-reference/functions/geo.md index e747d719938..45c30b3c2cd 100644 --- a/docs/ru/sql-reference/functions/geo.md +++ b/docs/ru/sql-reference/functions/geo.md @@ -40,7 +40,7 @@ SELECT greatCircleDistance(55.755831, 37.617673, -55.755831, -37.617673) Вычисляет угловое расстояние на сфере по [формуле большого круга](https://en.wikipedia.org/wiki/Great-circle_distance). ``` sql -greatCircleDistance(lon1Deg, lat1Deg, lon2Deg, lat2Deg) +greatCircleAngle(lon1Deg, lat1Deg, lon2Deg, lat2Deg) ``` **Входные параметры** diff --git a/docs/ru/sql-reference/functions/type-conversion-functions.md b/docs/ru/sql-reference/functions/type-conversion-functions.md index 57cc76b797e..41ded78055c 100644 --- a/docs/ru/sql-reference/functions/type-conversion-functions.md +++ b/docs/ru/sql-reference/functions/type-conversion-functions.md @@ -358,7 +358,7 @@ SELECT toTypeName(CAST(x, 'Nullable(UInt16)')) FROM t_null ## toInterval(Year\|Quarter\|Month\|Week\|Day\|Hour\|Minute\|Second) {#function-tointerval} -Приводит аргумент из числового типа данных к типу данных [IntervalType](../../sql-reference/functions/type-conversion-functions.md). +Приводит аргумент из числового типа данных к типу данных [IntervalType](../../sql-reference/data-types/special-data-types/interval.md). **Синтаксис** diff --git a/docs/ru/sql-reference/index.md b/docs/ru/sql-reference/index.md index a13e3774b86..ea611e75995 100644 --- a/docs/ru/sql-reference/index.md +++ b/docs/ru/sql-reference/index.md @@ -10,7 +10,7 @@ toc_title: hidden - [SELECT](statements/select/index.md) - [INSERT INTO](statements/insert-into.md) - [CREATE](statements/create.md) -- [ALTER](statements/alter.md) +- [ALTER](statements/alter.md#query_language_queries_alter) - [Прочие виды запросов](statements/misc.md) [Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/) diff --git a/docs/ru/sql-reference/statements/misc.md b/docs/ru/sql-reference/statements/misc.md index 97d2ce5818e..77f9570ae47 100644 --- a/docs/ru/sql-reference/statements/misc.md +++ b/docs/ru/sql-reference/statements/misc.md @@ -256,7 +256,7 @@ OPTIMIZE TABLE [db.]name [ON CLUSTER cluster] [PARTITION partition | PARTITION I RENAME TABLE [db11.]name11 TO [db12.]name12, [db21.]name21 TO [db22.]name22, ... [ON CLUSTER cluster] ``` -Все таблицы переименовываются под глобальной блокировкой. Переименовывание таблицы является лёгкой операцией. Если вы указали после TO другую базу данных, то таблица будет перенесена в эту базу данных. При этом, директории с базами данных должны быть расположены в одной файловой системе (иначе возвращается ошибка). +Переименовывание таблицы является лёгкой операцией. Если вы указали после `TO` другую базу данных, то таблица будет перенесена в эту базу данных. При этом, директории с базами данных должны быть расположены в одной файловой системе (иначе возвращается ошибка). В случае переименования нескольких таблиц в одном запросе — это неатомарная операция, может выполнится частично, запросы в других сессиях могут получить ошибку `Table ... doesn't exist...`. ## SET {#query-set} diff --git a/docs/ru/sql-reference/statements/system.md b/docs/ru/sql-reference/statements/system.md index b058739c894..1b66fa039d9 100644 --- a/docs/ru/sql-reference/statements/system.md +++ b/docs/ru/sql-reference/statements/system.md @@ -1,9 +1,12 @@ # Запросы SYSTEM {#query-language-system} +- [RELOAD EMBEDDED DICTIONARIES](#query_language-system-reload-emdedded-dictionaries) - [RELOAD DICTIONARIES](#query_language-system-reload-dictionaries) - [RELOAD DICTIONARY](#query_language-system-reload-dictionary) - [DROP DNS CACHE](#query_language-system-drop-dns-cache) - [DROP MARK CACHE](#query_language-system-drop-mark-cache) +- [DROP UNCOMPRESSED CACHE](#query_language-system-drop-uncompressed-cache) +- [DROP COMPILED EXPRESSION CACHE](#query_language-system-drop-compiled-expression-cache) - [FLUSH LOGS](#query_language-system-flush_logs) - [RELOAD CONFIG](#query_language-system-reload-config) - [SHUTDOWN](#query_language-system-shutdown) @@ -13,7 +16,25 @@ - [START DISTRIBUTED SENDS](#query_language-system-start-distributed-sends) - [STOP MERGES](#query_language-system-stop-merges) - [START MERGES](#query_language-system-start-merges) +- [STOP TTL MERGES](#query_language-stop-ttl-merges) +- [START TTL MERGES](#query_language-start-ttl-merges) +- [STOP MOVES](#query_language-stop-moves) +- [START MOVES](#query_language-start-moves) +- [STOP FETCHES](#query_language-system-stop-fetches) +- [START FETCHES](#query_language-system-start-fetches) +- [STOP REPLICATED SENDS](#query_language-system-start-replicated-sends) +- [START REPLICATED SENDS](#query_language-system-start-replicated-sends) +- [STOP REPLICATION QUEUES](#query_language-system-stop-replication-queues) +- [START REPLICATION QUEUES](#query_language-system-start-replication-queues) +- [SYNC REPLICA](#query_language-system-sync-replica) +- [RESTART REPLICA](#query_language-system-restart-replica) +- [RESTART REPLICAS](#query_language-system-restart-replicas) +## RELOAD EMBEDDED DICTIONARIES] {#query_language-system-reload-emdedded-dictionaries} +Перегружет все [Встроенные словари](../dictionaries/internal-dicts.md). +По умолчанию встроенные словари выключены. +Всегда возвращает `Ok.`, вне зависимости от результата обновления встроенных словарей. + ## RELOAD DICTIONARIES {#query_language-system-reload-dictionaries} Перегружает все словари, которые были успешно загружены до этого. @@ -40,6 +61,16 @@ SELECT name, status FROM system.dictionaries; Сбрасывает кеш «засечек» (`mark cache`). Используется при разработке ClickHouse и тестах производительности. +## DROP UNCOMPRESSED CACHE {#query_language-system-drop-uncompressed-cache} + +Сбрасывает кеш не сжатых данных. Используется при разработке ClickHouse и тестах производительности. +Для управления кешем не сжатых данных используйте следующие настройки уровня сервера [uncompressed_cache_size](../../operations/server-configuration-parameters/settings.md#server-settings-uncompressed_cache_size) и настройки уровня запрос/пользователь/профиль [use_uncompressed_cache](../../operations/settings/settings.md#setting-use_uncompressed_cache) + + +## DROP COMPILED EXPRESSION CACHE {#query_language-system-drop-compiled-expression-cache} +Сбрасывает кеш скомпилированных выражений. Используется при разработке ClickHouse и тестах производительности. +Компилированные выражения используются когда включена настройка уровня запрос/пользователь/профиль [compile](../../operations/settings/settings.md#compile) + ## FLUSH LOGS {#query_language-system-flush_logs} Записывает буферы логов в системные таблицы (например system.query\_log). Позволяет не ждать 7.5 секунд при отладке. @@ -84,6 +115,10 @@ SYSTEM FLUSH DISTRIBUTED [db.] SYSTEM START DISTRIBUTED SENDS [db.] ``` +## Managing MergeTree Tables {#query-language-system-mergetree} + +ClickHouse может управлять фоновыми процессами в [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md) таблицах. + ### STOP MERGES {#query_language-system-stop-merges} Позволяет остановить фоновые мержи для таблиц семейства MergeTree: @@ -103,4 +138,110 @@ SYSTEM STOP MERGES [[db.]merge_tree_family_table_name] SYSTEM START MERGES [[db.]merge_tree_family_table_name] ``` +### STOP TTL MERGES {#query_language-stop-ttl-merges} + +Позволяет остановить фоновые процессы удаления старых данных основанные на [выражениях TTL](../../engines/table-engines/mergetree-family/mergetree.md#table_engine-mergetree-ttl) для таблиц семейства MergeTree: +Возвращает `Ok.` даже если указана несуществующая таблица или таблица имеет тип отличный от MergeTree. Возвращает ошибку если указана не существующая база данных: + +``` sql +SYSTEM STOP TTL MERGES [[db.]merge_tree_family_table_name] +``` + +### START TTL MERGES {#query_language-start-ttl-merges} + +Запускает фоновые процессы удаления старых данных основанные на [выражениях TTL](../../engines/table-engines/mergetree-family/mergetree.md#table_engine-mergetree-ttl) для таблиц семейства MergeTree: +Возвращает `Ok.` даже если указана несуществующая таблица или таблица имеет тип отличный от MergeTree. Возвращает ошибку если указана не существующая база данных: + +``` sql +SYSTEM START TTL MERGES [[db.]merge_tree_family_table_name] +``` + +### STOP MOVES {#query_language-stop-moves} + +Позволяет остановить фоновые процессы переноса данных основанные [табличных выражениях TTL с использованием TO VOLUME или TO DISK](../../engines/table-engines/mergetree-family/mergetree.md#mergetree-table-ttl) for tables in the MergeTree family: +Возвращает `Ok.` даже если указана несуществующая таблица или таблица имеет тип отличный от MergeTree. Возвращает ошибку если указана не существующая база данных: + +``` sql +SYSTEM STOP MOVES [[db.]merge_tree_family_table_name] +``` + +### START MOVES {#query_language-start-moves} + +Запускает фоновые процессы переноса данных основанные [табличных выражениях TTL с использованием TO VOLUME или TO DISK](../../engines/table-engines/mergetree-family/mergetree.md#mergetree-table-ttl) for tables in the MergeTree family: +Возвращает `Ok.` даже если указана несуществующая таблица или таблица имеет тип отличный от MergeTree. Возвращает ошибку если указана не существующая база данных: + +``` sql +SYSTEM STOP MOVES [[db.]merge_tree_family_table_name] +``` + +## Managing ReplicatedMergeTree Tables {#query-language-system-replicated} + +ClickHouse может управлять фоновыми процессами связанными c репликацией в таблицах семейства [ReplicatedMergeTree](../../engines/table-engines/mergetree-family/replacingmergetree.md). + +### STOP FETCHES {#query_language-system-stop-fetches} +Позволяет остановить фоновые процессы синхронизации новыми вставленными кусками данных с другими репликами в кластере для таблиц семейства `ReplicatedMergeTree`: +Всегда возвращает `Ok.` вне зависимости от типа таблицы и даже если таблица или база данных не существет. + +``` sql +SYSTEM STOP FETCHES [[db.]replicated_merge_tree_family_table_name] +``` + +### START FETCHES {#query_language-system-start-fetches} +Позволяет запустить фоновые процессы синхронизации новыми вставленными кусками данных с другими репликами в кластере для таблиц семейства `ReplicatedMergeTree`: +Всегда возвращает `Ok.` вне зависимости от типа таблицы и даже если таблица или база данных не существет. + +``` sql +SYSTEM START FETCHES [[db.]replicated_merge_tree_family_table_name] +``` + +### STOP REPLICATED SENDS {#query_language-system-start-replicated-sends} +Позволяет остановить фоновые процессы отсылки новых вставленных кусков данных другим репликам в кластере для таблиц семейства `ReplicatedMergeTree`: + +``` sql +SYSTEM STOP REPLICATED SENDS [[db.]replicated_merge_tree_family_table_name] +``` + +### START REPLICATED SENDS {#query_language-system-start-replicated-sends} +Позволяет запустить фоновые процессы отсылки новых вставленных кусков данных другим репликам в кластере для таблиц семейства `ReplicatedMergeTree`: + +``` sql +SYSTEM START REPLICATED SENDS [[db.]replicated_merge_tree_family_table_name] +``` + +### STOP REPLICATION QUEUES {#query_language-system-stop-replication-queues} +Останавливает фоновые процессы разбора заданий из очереди репликации которая хранится в Zookeeper для таблиц семейства `ReplicatedMergeTree`. Возможные типы заданий - merges, fetches, mutation, DDL запросы с ON CLUSTER: + +``` sql +SYSTEM STOP REPLICATION QUEUES [[db.]replicated_merge_tree_family_table_name] +``` + +### START REPLICATION QUEUES {#query_language-system-start-replication-queues} +Запускает фоновые процессы разбора заданий из очереди репликации которая хранится в Zookeeper для таблиц семейства `ReplicatedMergeTree`. Возможные типы заданий - merges, fetches, mutation, DDL запросы с ON CLUSTER: + +``` sql +SYSTEM START REPLICATION QUEUES [[db.]replicated_merge_tree_family_table_name] +``` + +### SYNC REPLICA {#query_language-system-sync-replica} +Ждет когда таблица семейства `ReplicatedMergeTree` будет синхронизирована с другими репликами в кластере, будет работать до достижения `receive_timeout`, если синхронизация для таблицы отключена в настоящий момент времени: + +``` sql +SYSTEM SYNC REPLICA [db.]replicated_merge_tree_family_table_name +``` + +### RESTART REPLICA {#query_language-system-restart-replica} +Реинициализация состояния Zookeeper сессий для таблицы семейства `ReplicatedMergeTree`, сравнивает текущее состояние с тем что хранится в Zookeeper как источник правды и добавляет задачи Zookeeper очередь если необходимо +Инициализация очереди репликации на основе данных ZooKeeper, происходит так же как при attach table. На короткое время таблица станет недоступной для любых операций. + +``` sql +SYSTEM RESTART QUEUES [db.]replicated_merge_tree_family_table_name +``` + +### RESTART REPLICAS {#query_language-system-restart-replicas} +Реинициализация состояния Zookeeper сессий для всех `ReplicatedMergeTree` таблиц, сравнивает текущее состояние с тем что хранится в Zookeeper как источник правды и добавляет задачи Zookeeper очередь если необходимо + +``` sql +SYSTEM RESTART QUEUES [db.]replicated_merge_tree_family_table_name +``` + [Оригинальная статья](https://clickhouse.tech/docs/ru/query_language/system/) diff --git a/docs/ru/sql-reference/syntax.md b/docs/ru/sql-reference/syntax.md index 24ab2be8a16..41a9c012b71 100644 --- a/docs/ru/sql-reference/syntax.md +++ b/docs/ru/sql-reference/syntax.md @@ -33,7 +33,7 @@ INSERT INTO t VALUES (1, 'Hello, world'), (2, 'abc'), (3, 'def') - Стандарту SQL. Например, применение любого из вариантов `SELECT`, `select` или `SeLeCt` не вызовет ошибки. - Реализации в некоторых популярных DBMS (MySQL или Postgres). Например, `DateTime` и `datetime`. -Зависимость от регистра для имён типов данных можно проверить в таблице `system.data_type_families`. +Зависимость от регистра для имён типов данных можно проверить в таблице [system.data_type_families](../operations/system-tables.md#system_tables-data_type_families). В отличие от стандарта SQL, все остальные ключевые слова, включая названия функций зависят от регистра. diff --git a/docs/tools/requirements.txt b/docs/tools/requirements.txt index d9ea19ff389..570dcf0aaf2 100644 --- a/docs/tools/requirements.txt +++ b/docs/tools/requirements.txt @@ -2,7 +2,7 @@ Babel==2.8.0 backports-abc==0.5 backports.functools-lru-cache==1.6.1 beautifulsoup4==4.9.1 -certifi==2020.4.5.1 +certifi==2020.4.5.2 chardet==3.0.4 click==7.1.2 closure==20191111 @@ -13,7 +13,7 @@ idna==2.9 Jinja2==2.11.2 jinja2-highlight==0.6.1 jsmin==2.2.2 -livereload==2.6.1 +livereload==2.6.2 Markdown==3.2.1 MarkupSafe==1.1.1 mkdocs==1.1.2 @@ -22,7 +22,7 @@ mkdocs-macros-plugin==0.4.9 nltk==3.5 nose==1.3.7 protobuf==3.12.2 -numpy==1.18.4 +numpy==1.18.5 Pygments==2.5.2 pymdown-extensions==7.1 python-slugify==1.2.6 diff --git a/docs/tools/test.py b/docs/tools/test.py index 63b84885d9f..5c0cf4b799d 100755 --- a/docs/tools/test.py +++ b/docs/tools/test.py @@ -92,9 +92,11 @@ def test_single_page(input_path, lang): logging.warning('Found %d duplicate anchor points' % duplicate_anchor_points) if links_to_nowhere: - logging.warning(f'Found {links_to_nowhere} links to nowhere in {lang}') if lang == 'en': # TODO: check all languages again + logging.error(f'Found {links_to_nowhere} links to nowhere in {lang}') sys.exit(1) + else: + logging.warning(f'Found {links_to_nowhere} links to nowhere in {lang}') if len(anchor_points) <= 10: logging.error('Html parsing is probably broken') diff --git a/docs/tools/translate/requirements.txt b/docs/tools/translate/requirements.txt index 3c212ee8bc2..0c9d44a346e 100644 --- a/docs/tools/translate/requirements.txt +++ b/docs/tools/translate/requirements.txt @@ -1,5 +1,5 @@ Babel==2.8.0 -certifi==2020.4.5.1 +certifi==2020.4.5.2 chardet==3.0.4 googletrans==2.4.0 idna==2.9 diff --git a/docs/tr/development/developer-instruction.md b/docs/tr/development/developer-instruction.md index a65c6666288..0ca5f9cdd63 100644 --- a/docs/tr/development/developer-instruction.md +++ b/docs/tr/development/developer-instruction.md @@ -141,7 +141,7 @@ Resmi Yandex şu anda GCC'Yİ kullanıyor çünkü biraz daha iyi performansa sa Ubuntu run GCC yüklemek için: `sudo apt install gcc g++` -Gcc sürümünü kontrol edin: `gcc --version`. 9'un altındaysa, buradaki talimatları izleyin: https://clickhouse.tech / docs/TR/development / build / \#ınstall-gcc-9. +Gcc sürümünü kontrol edin: `gcc --version`. 9'un altındaysa, buradaki talimatları izleyin: https://clickhouse.tech/docs/tr/development/build/#install-gcc-9. Mac OS X build sadece Clang için desteklenir. Sadece koş `brew install llvm` @@ -249,7 +249,7 @@ Kod stili Kılavuzu: https://clickhouse.tech / doscs / TR / development / style/ Yazma testleri: https://clickhouse.teknoloji / doscs / TR / geliştirme / testler/ -Görevlerin listesi: https://github.com/ClickHouse/ClickHouse/blob/master/testsructions/easy\_tasks\_sorted\_en.md +Görevlerin listesi: https://github.com/ClickHouse/ClickHouse/contribute # Test Verileri {#test-data} diff --git a/docs/tr/getting-started/install.md b/docs/tr/getting-started/install.md index 3bf319430bd..361f59b3a10 100644 --- a/docs/tr/getting-started/install.md +++ b/docs/tr/getting-started/install.md @@ -66,7 +66,7 @@ Ayrıca paketleri manuel olarak indirebilir ve yükleyebilirsiniz [burada](https Resmi önceden derlenmiş kullanılması tavsiye edilir `tgz` Arch ,iv ,es for tüm Linux dağıtım installationları, kurulumu `deb` veya `rpm` paketler mümkün değildir. -Gerekli sürümü ile indirilebilir `curl` veya `wget` depo fromdan https://repo.yandex.ru/clickhouse/tgz/. +Gerekli sürümü ile indirilebilir `curl` veya `wget` depo fromdan https://repo.clickhouse.tech/tgz/. Bundan sonra indirilen arşivler açılmalı ve kurulum komut dosyaları ile kurulmalıdır. En son sürüm için örnek: ``` bash diff --git a/docs/zh/development/developer-instruction.md b/docs/zh/development/developer-instruction.md index 6911a0e4dc9..b40e6db3af1 100644 --- a/docs/zh/development/developer-instruction.md +++ b/docs/zh/development/developer-instruction.md @@ -129,7 +129,7 @@ Yandex官方当前使用GCC构建ClickHouse,因为它生成的机器代码性 在Ubuntu上安装GCC,请执行:`sudo apt install gcc g++` -请使用`gcc --version`查看gcc的版本。如果gcc版本低于9,请参考此处的指示:https://clickhouse.tech/docs/en/development/build/\#install-gcc-9 。 +请使用`gcc --version`查看gcc的版本。如果gcc版本低于9,请参考此处的指示:https://clickhouse.tech/docs/zh/development/build/#an-zhuang-gcc-9 。 在Mac OS X上安装GCC,请执行:`brew install gcc` @@ -234,7 +234,7 @@ ClickHouse的架构描述可以在此处查看:https://clickhouse.tech/docs/en 编写测试用例:https://clickhouse.tech/docs/en/development/tests/ -任务列表:https://github.com/ClickHouse/ClickHouse/blob/master/tests/instructions/easy\_tasks\_sorted\_en.md +任务列表:https://github.com/ClickHouse/ClickHouse/contribute # 测试数据 {#ce-shi-shu-ju} diff --git a/docs/zh/engines/database-engines/lazy.md b/docs/zh/engines/database-engines/lazy.md index c0a08e37559..700eb4b3b25 100644 --- a/docs/zh/engines/database-engines/lazy.md +++ b/docs/zh/engines/database-engines/lazy.md @@ -1,15 +1,13 @@ --- -machine_translated: true -machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd toc_priority: 31 toc_title: "\u61D2\u60F0" --- -# 懒惰 {#lazy} +# 延时引擎Lazy {#lazy} -仅将表保留在RAM中 `expiration_time_in_seconds` 上次访问后几秒钟。 只能与\*日志表一起使用。 +在距最近一次访问间隔`expiration_time_in_seconds`时间段内,将表保存在内存中,仅适用于 \*Log引擎表 -它针对存储许多小\*日志表进行了优化,访问之间存在较长的时间间隔。 +由于针对这类表的访问间隔较长,对保存大量小的 \*Log引擎表进行了优化, ## 创建数据库 {#creating-a-database} diff --git a/docs/zh/engines/table-engines/mergetree-family/versionedcollapsingmergetree.md b/docs/zh/engines/table-engines/mergetree-family/versionedcollapsingmergetree.md index 52f3c708126..19caae5e1a1 100644 --- a/docs/zh/engines/table-engines/mergetree-family/versionedcollapsingmergetree.md +++ b/docs/zh/engines/table-engines/mergetree-family/versionedcollapsingmergetree.md @@ -5,7 +5,7 @@ toc_priority: 37 toc_title: "\u7248\u672C\u96C6\u5408\u5728\u65B0\u6811" --- -# 版本集合在新树 {#versionedcollapsingmergetree} +# 版本折叠合并树 {#versionedcollapsingmergetree} 这个引擎: diff --git a/docs/zh/getting-started/install.md b/docs/zh/getting-started/install.md index 32eb7fa0a82..bbc79c3bf78 100644 --- a/docs/zh/getting-started/install.md +++ b/docs/zh/getting-started/install.md @@ -34,8 +34,8 @@ Yandex ClickHouse团队建议使用官方预编译的`rpm`软件包,用于Cent ``` bash sudo yum install yum-utils -sudo rpm --import https://repo.yandex.ru/clickhouse/CLICKHOUSE-KEY.GPG -sudo yum-config-manager --add-repo https://repo.yandex.ru/clickhouse/rpm/stable/x86_64 +sudo rpm --import https://repo.clickhouse.tech/CLICKHOUSE-KEY.GPG +sudo yum-config-manager --add-repo https://repo.clickhouse.tech/rpm/stable/x86_64 ``` 如果您想使用最新版本,请将`stable`替换为`testing`(建议您在测试环境中使用)。 @@ -46,7 +46,7 @@ sudo yum-config-manager --add-repo https://repo.yandex.ru/clickhouse/rpm/stable/ sudo yum install clickhouse-server clickhouse-client ``` -您也可以从此处手动下载和安装软件包:https://repo.yandex.ru/clickhouse/rpm/stable/x86_64。 +您也可以从此处手动下载和安装软件包:https://repo.clickhouse.tech/rpm/stable/x86_64。 ### 来自Docker {#from-docker-image} diff --git a/docs/zh/index.md b/docs/zh/index.md index 522affa6250..926c4ce2fdf 100644 --- a/docs/zh/index.md +++ b/docs/zh/index.md @@ -4,7 +4,7 @@ ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS) 在传统的行式数据库系统中,数据按如下顺序存储: -| 行 | 小心点 | JavaEnable | 标题 | GoodEvent | 活动时间 | +| row | watchID | JavaEnable | title | GoodEvent | EventTime | |-----|-------------|------------|------------|-----------|---------------------| | \#0 | 89354350662 | 1 | 投资者关系 | 1 | 2016-05-18 05:19:20 | | \#1 | 90329509958 | 0 | 联系我们 | 1 | 2016-05-18 08:10:20 | @@ -18,23 +18,23 @@ ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS) 在列式数据库系统中,数据按如下的顺序存储: -| 行: | \#0 | \#1 | \#2 | \#N | +| row: | \#0 | \#1 | \#2 | \#N | |-------------|---------------------|---------------------|---------------------|-----| -| 小心点: | 89354350662 | 90329509958 | 89953706054 | … | +| watchID: | 89354350662 | 90329509958 | 89953706054 | … | | JavaEnable: | 1 | 0 | 1 | … | -| 标题: | 投资者关系 | 联系我们 | 任务 | … | +| title: | 投资者关系 | 联系我们 | 任务 | … | | GoodEvent: | 1 | 1 | 1 | … | -| 活动时间: | 2016-05-18 05:19:20 | 2016-05-18 08:10:20 | 2016-05-18 07:38:00 | … | +| EventTime: | 2016-05-18 05:19:20 | 2016-05-18 08:10:20 | 2016-05-18 07:38:00 | … | -该示例中只展示了数据在列式数据库中数据的排列顺序。 +该示例中只展示了数据在列式数据库中数据的排列方式。 对于存储而言,列式数据库总是将同一列的数据存储在一起,不同列的数据也总是分开存储。 常见的列式数据库有: Vertica、 Paraccel (Actian Matrix,Amazon Redshift)、 Sybase IQ、 Exasol、 Infobright、 InfiniDB、 MonetDB (VectorWise, Actian Vector)、 LucidDB、 SAP HANA、 Google Dremel、 Google PowerDrill、 Druid、 kdb+。 {: .灰色 } -不同的存储方式适合不同的场景,这里的查询场景包括: 进行了哪些查询,多久查询一次以及各类查询的比例; 每种查询读取多少数据————行、列和字节;读取数据和写入数据之间的关系;使用的数据集大小以及如何使用本地的数据集;是否使用事务,以及它们是如何进行隔离的;数据的复制机制与数据的完整性要求;每种类型的查询要求的延迟与吞吐量等等。 +不同的数据存储方式适用不同的业务场景,数据访问的场景包括:进行了何种查询、多久查询一次以及各类查询的比例; 每种查询读取多少数据————行、列和字节;读取数据和写入数据之间的关系;使用的数据集大小以及如何使用本地的数据集;是否使用事务,以及它们是如何进行隔离的;数据的复制机制与数据的完整性要求;每种类型的查询要求的延迟与吞吐量等等。 -系统负载越高,根据使用场景进行定制化就越重要,并且定制将会变的越精细。没有一个系统同样适用于明显不同的场景。如果系统适用于广泛的场景,在负载高的情况下,所有的场景可以会被公平但低效处理,或者高效处理一小部分场景。 +系统负载越高,依据使用场景进行定制化就越重要,并且定制将会变的越精细。没有一个系统能够同时适用所有明显不同的业务场景。如果系统适用于广泛的场景,在负载高的情况下,要兼顾所有的场景,那么将不得不做出选择。是要平衡还是要效率? ## OLAP场景的关键特征 {#olapchang-jing-de-guan-jian-te-zheng} @@ -52,7 +52,7 @@ ClickHouse是一个用于联机分析(OLAP)的列式数据库管理系统(DBMS) - 每一个查询除了一个大表外都很小 - 查询结果明显小于源数据,换句话说,数据被过滤或聚合后能够被盛放在单台服务器的内存中 -很容易可以看出,OLAP场景与其他流行场景(例如,OLTP或K/V)有很大的不同, 因此想要使用OLTP或Key-Value数据库去高效的处理分析查询是没有意义的,例如,使用OLAP数据库去处理分析请求通常要优于使用MongoDB或Redis去处理分析请求。 +很容易可以看出,OLAP场景与其他通常业务场景(例如,OLTP或K/V)有很大的不同, 因此想要使用OLTP或Key-Value数据库去高效的处理分析查询场景,并不是非常完美的适用方案。例如,使用OLAP数据库去处理分析请求通常要优于使用MongoDB或Redis去处理分析请求。 ## 列式数据库更适合OLAP场景的原因 {#lie-shi-shu-ju-ku-geng-gua-he-olapchang-jing-de-yuan-yin} diff --git a/docs/zh/operations/configuration-files.md b/docs/zh/operations/configuration-files.md index 16a903b6528..aa0da86e8d0 100644 --- a/docs/zh/operations/configuration-files.md +++ b/docs/zh/operations/configuration-files.md @@ -1,27 +1,31 @@ # 配置文件 {#configuration_files} -主服务器配置文件是 `config.xml`. 它驻留在 `/etc/clickhouse-server/` 目录。 +ClickHouse支持多配置文件管理。主配置文件是`/etc/clickhouse-server/config.xml`。其余文件须在目录`/etc/clickhouse-server/config.d`。 -单个设置可以在复盖 `*.xml` 和 `*.conf` 在文件 `conf.d` 和 `config.d` 配置文件旁边的目录。 +!!! 注意: + 所有配置文件必须是XML格式。此外,配置文件须有相同的跟元素,通常是``。 -该 `replace` 或 `remove` 可以为这些配置文件的元素指定属性。 +主配置文件中的一些配置可以通过`replace`或`remove`属性被配置文件覆盖。 -如果两者都未指定,则递归组合元素的内容,替换重复子项的值。 +如果两者都未指定,则递归组合配置的内容,替换重复子项的值。 -如果 `replace` 如果指定,则将整个元素替换为指定的元素。 +如果指定`replace`属性,则将整个元素替换为指定的元素。 -如果 `remove` 如果指定,则删除该元素。 +如果指定`remove`属性,则删除该元素。 -The config can also define «substitutions». If an element has the `incl` 属性时,从文件中的相应替换将被用作该值。 默认情况下,具有替换的文件的路径为 `/etc/metrika.xml`. 这可以在改变 [包括\_从](server-configuration-parameters/settings.md#server_configuration_parameters-include_from) 服务器配置中的元素。 替换值在指定 `/yandex/substitution_name` 这个文件中的元素。 如果在指定的替换 `incl` 不存在,则将其记录在日志中。 要防止ClickHouse记录丢失的替换,请指定 `optional="true"` 属性(例如,设置 [宏](#macros) server\_settings/settings.md))。 +此外,配置文件还可指定"substitutions"。如果一个元素有`incl`属性,则文件中的相应替换值将被使用。默认情况下,具有替换的文件的路径为`/etc/metrika.xml`。这可以在服务配置中的[include\_from](server-configuration-parameters/settings.md#server_configuration_parameters-include_from)元素中被修改。替换值在这个文件的`/yandex/substitution_name`元素中被指定。如果`incl`中指定的替换值不存在,则将其记录在日志中。为防止ClickHouse记录丢失的替换,请指定`optional="true"`属性(例如,[宏](server-configuration-parameters/settings.md)设置)。 -替换也可以从ZooKeeper执行。 为此,请指定属性 `from_zk = "/path/to/node"`. 元素值被替换为节点的内容 `/path/to/node` 在动物园管理员。 您还可以将整个XML子树放在ZooKeeper节点上,并将其完全插入到源元素中。 +替换也可以从ZooKeeper执行。为此,请指定属性`from_zk = "/path/to/node"`。元素值被替换为ZooKeeper节点`/path/to/node`的内容。您还可以将整个XML子树放在ZooKeeper节点上,并将其完全插入到源元素中。 -该 `config.xml` 文件可以指定具有用户设置、配置文件和配额的单独配置。 这个配置的相对路径在 ‘users\_config’ 元素。 默认情况下,它是 `users.xml`. 如果 `users_config` 被省略,用户设置,配置文件和配额直接在指定 `config.xml`. +`config.xml` 文件可以指定单独的配置文件用于配置用户设置、配置文件及配额。可在`users_config`元素中指定其配置文件相对路径。其默认值是`users.xml`。如果`users_config`被省略,用户设置,配置文件和配额则直接在`config.xml`中指定。 -此外, `users_config` 可以从文件中复盖 `users_config.d` 目录(例如, `users.d`)和替换。 例如,您可以为每个用户提供单独的配置文件,如下所示: +用户配置可以分为如`config.xml`和`config.d/`等形式的单独配置文件。目录名称为配置`user_config`的值,去掉`.xml`后缀并与添加`.d`。由于`users_config`配置默认值为`users.xml`,所以目录名默认使用`users.d`。例如,您可以为每个用户有单独的配置文件,如下所示: + +``` bash +$ cat /etc/clickhouse-server/users.d/alice.xml +``` ``` xml -$ cat /etc/clickhouse-server/users.d/alice.xml @@ -36,7 +40,7 @@ $ cat /etc/clickhouse-server/users.d/alice.xml ``` -对于每个配置文件,服务器还会生成 `file-preprocessed.xml` 启动时的文件。 这些文件包含所有已完成的替换和复盖,并且它们旨在提供信息。 如果zookeeper替换在配置文件中使用,但ZooKeeper在服务器启动时不可用,则服务器将从预处理的文件中加载配置。 +对于每个配置文件,服务器还会在启动时生成 `file-preprocessed.xml` 文件。这些文件包含所有已完成的替换和复盖,并且它们旨在提供信息。如果zookeeper替换在配置文件中使用,但ZooKeeper在服务器启动时不可用,则服务器将从预处理的文件中加载配置。 服务器跟踪配置文件中的更改,以及执行替换和复盖时使用的文件和ZooKeeper节点,并动态重新加载用户和集群的设置。 这意味着您可以在不重新启动服务器的情况下修改群集、用户及其设置。 diff --git a/docs/zh/operations/utilities/clickhouse-benchmark.md b/docs/zh/operations/utilities/clickhouse-benchmark.md index d1e83cb9789..1c255f621c0 100644 --- a/docs/zh/operations/utilities/clickhouse-benchmark.md +++ b/docs/zh/operations/utilities/clickhouse-benchmark.md @@ -1,11 +1,9 @@ --- -machine_translated: true -machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd toc_priority: 61 -toc_title: "\uFF82\u6697\uFF6A\uFF82\u6C3E\u73AF\u50AC\uFF82\u56E3" +toc_title: "性能测试" --- -# ツ暗ェツ氾环催ツ団 {#clickhouse-benchmark} +# 性能测试 {#clickhouse-benchmark} 连接到ClickHouse服务器并重复发送指定的查询。 @@ -21,7 +19,7 @@ $ echo "single query" | clickhouse-benchmark [keys] $ clickhouse-benchmark [keys] <<< "single query" ``` -如果要发送一组查询,请创建一个文本文件,并将每个查询放在此文件中的单个字符串上。 例如: +如果要发送一组查询,请创建一个文本文件,并将每个查询的字符串放在此文件中。 例如: ``` sql SELECT * FROM system.numbers LIMIT 10000000 @@ -34,15 +32,15 @@ SELECT 1 clickhouse-benchmark [keys] < queries_file ``` -## 键 {#clickhouse-benchmark-keys} +## keys参数 {#clickhouse-benchmark-keys} - `-c N`, `--concurrency=N` — Number of queries that `clickhouse-benchmark` 同时发送。 默认值:1。 - `-d N`, `--delay=N` — Interval in seconds between intermediate reports (set 0 to disable reports). Default value: 1. -- `-h WORD`, `--host=WORD` — Server host. Default value: `localhost`. 为 [比较模式](#clickhouse-benchmark-comparison-mode) 您可以使用多个 `-h` 钥匙 +- `-h WORD`, `--host=WORD` — Server host. Default value: `localhost`. 为 [比较模式](#clickhouse-benchmark-comparison-mode) 您可以使用多个 `-h` 参数 - `-p N`, `--port=N` — Server port. Default value: 9000. For the [比较模式](#clickhouse-benchmark-comparison-mode) 您可以使用多个 `-p` 钥匙 -- `-i N`, `--iterations=N` — Total number of queries. Default value: 0. -- `-r`, `--randomize` — Random order of queries execution if there is more then one input query. -- `-s`, `--secure` — Using TLS connection. +- `-i N`, `--iterations=N` — 查询的总次数. Default value: 0. +- `-r`, `--randomize` — 有多个查询时,以随机顺序执行. +- `-s`, `--secure` — 使用TLS安全连接. - `-t N`, `--timelimit=N` — Time limit in seconds. `clickhouse-benchmark` 达到指定的时间限制时停止发送查询。 默认值:0(禁用时间限制)。 - `--confidence=N` — Level of confidence for T-test. Possible values: 0 (80%), 1 (90%), 2 (95%), 3 (98%), 4 (99%), 5 (99.5%). Default value: 5. In the [比较模式](#clickhouse-benchmark-comparison-mode) `clickhouse-benchmark` 执行 [独立双样本学生的t测试](https://en.wikipedia.org/wiki/Student%27s_t-test#Independent_two-sample_t-test) 测试以确定两个分布是否与所选置信水平没有不同。 - `--cumulative` — Printing cumulative data instead of data per interval. @@ -51,14 +49,14 @@ clickhouse-benchmark [keys] < queries_file - `--user=USERNAME` — ClickHouse user name. Default value: `default`. - `--password=PSWD` — ClickHouse user password. Default value: empty string. - `--stacktrace` — Stack traces output. When the key is set, `clickhouse-bencmark` 输出异常的堆栈跟踪。 -- `--stage=WORD` — Query processing stage at server. ClickHouse stops query processing and returns answer to `clickhouse-benchmark` 在指定的阶段。 可能的值: `complete`, `fetch_columns`, `with_mergeable_state`. 默认值: `complete`. +- `--stage=WORD` — 查询请求的服务端处理状态. 在特定阶段Clickhouse会停止查询处理,并返回结果给`clickhouse-benchmark`。 可能的值: `complete`, `fetch_columns`, `with_mergeable_state`. 默认值: `complete`. - `--help` — Shows the help message. -如果你想申请一些 [设置](../../operations/settings/index.md) 对于查询,请将它们作为键传递 `--= SETTING_VALUE`. 例如, `--max_memory_usage=1048576`. +如果你想在查询时应用上述的部分参数 [设置](../../operations/settings/index.md) ,请将它们作为键传递 `--= SETTING_VALUE`. 例如, `--max_memory_usage=1048576`. ## 输出 {#clickhouse-benchmark-output} -默认情况下, `clickhouse-benchmark` 每个报表 `--delay` 间隔。 +默认情况下, `clickhouse-benchmark` 按照 `--delay` 参数间隔输出结果。 报告示例: @@ -83,27 +81,27 @@ localhost:9000, queries 10, QPS: 6.772, RPS: 67904487.440, MiB/s: 518.070, resul 99.990% 0.150 sec. ``` -在报告中,您可以找到: +在结果报告中,您可以找到: -- 在查询的数量 `Queries executed:` 场。 +- 查询数量:参见`Queries executed:`字段。 -- 状态字符串包含(按顺序): +- 状态码(按顺序给出): - - ClickHouse服务器的端点。 + - ClickHouse服务器的连接信息。 - 已处理的查询数。 - - QPS:QPS:在指定的时间段内每秒执行多少个查询服务器 `--delay` 争论。 - - RPS:在指定的时间段内,服务器每秒读取多少行 `--delay` 争论。 - - MiB/s:在指定的时间段内每秒读取多少mebibytes服务器 `--delay` 争论。 - - 结果RPS:在指定的时间段内,服务器每秒放置到查询结果的行数 `--delay` 争论。 - - 结果MiB/s.在指定的时间段内,服务器每秒将多少mebibytes放置到查询结果中 `--delay` 争论。 + - QPS:服务端每秒处理的查询数量 + - RPS:服务器每秒读取多少行 + - MiB/s:服务器每秒读取多少字节的数据 + - 结果RPS:服务端每秒生成多少行的结果集数据 + - 结果MiB/s.服务端每秒生成多少字节的结果集数据 -- 查询执行时间的百分位数。 +- 查询执行时间的百分比。 -## 比较模式 {#clickhouse-benchmark-comparison-mode} +## 对比模式 {#clickhouse-benchmark-comparison-mode} `clickhouse-benchmark` 可以比较两个正在运行的ClickHouse服务器的性能。 -要使用比较模式,请通过以下两对指定两个服务器的端点 `--host`, `--port` 钥匙 键在参数列表中的位置匹配在一起,第一 `--host` 与第一匹配 `--port` 等等。 `clickhouse-benchmark` 建立到两个服务器的连接,然后发送查询。 每个查询寻址到随机选择的服务器。 每个服务器的结果分别显示。 +要使用对比模式,分别为每个服务器配置各自的`--host`, `--port`参数。`clickhouse-benchmark` 会根据设置的参数建立到各个Server的连接并发送请求。每个查询请求会随机发送到某个服务器。输出结果会按服务器分组输出 ## 示例 {#clickhouse-benchmark-example} diff --git a/docs/zh/operations/utilities/clickhouse-copier.md b/docs/zh/operations/utilities/clickhouse-copier.md index a5364bcaa71..3dc29fe16fa 100644 --- a/docs/zh/operations/utilities/clickhouse-copier.md +++ b/docs/zh/operations/utilities/clickhouse-copier.md @@ -24,7 +24,7 @@ 该实用程序应手动运行: ``` bash -clickhouse-copier copier --daemon --config zookeeper.xml --task-path /task/path --base-dir /path/to/dir +clickhouse-copier --daemon --config zookeeper.xml --task-path /task/path --base-dir /path/to/dir ``` 参数: diff --git a/docs/zh/sql-reference/aggregate-functions/combinators.md b/docs/zh/sql-reference/aggregate-functions/combinators.md index c5c3e8a9577..c458097a5fb 100644 --- a/docs/zh/sql-reference/aggregate-functions/combinators.md +++ b/docs/zh/sql-reference/aggregate-functions/combinators.md @@ -1,51 +1,49 @@ --- -machine_translated: true -machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd toc_priority: 37 -toc_title: "\u7EC4\u5408\u5668" +toc_title: 聚合函数组合器 --- # 聚合函数组合器 {#aggregate_functions_combinators} 聚合函数的名称可以附加一个后缀。 这改变了聚合函数的工作方式。 -## -如果 {#agg-functions-combinator-if} +## -If {#agg-functions-combinator-if} -The suffix -If can be appended to the name of any aggregate function. In this case, the aggregate function accepts an extra argument – a condition (Uint8 type). The aggregate function processes only the rows that trigger the condition. If the condition was not triggered even once, it returns a default value (usually zeros or empty strings). +-If可以加到任何聚合函数之后。加了-If之后聚合函数需要接受一个额外的参数,一个条件(Uint8类型),如果条件满足,那聚合函数处理当前的行数据,如果不满足,那返回默认值(通常是0或者空字符串)。 -例: `sumIf(column, cond)`, `countIf(cond)`, `avgIf(x, cond)`, `quantilesTimingIf(level1, level2)(x, cond)`, `argMinIf(arg, val, cond)` 等等。 +例: `sumIf(column, cond)`, `countIf(cond)`, `avgIf(x, cond)`, `quantilesTimingIf(level1, level2)(x, cond)`, `argMinIf(arg, val, cond)` 等等。 -使用条件聚合函数,您可以一次计算多个条件的聚合,而无需使用子查询和 `JOIN`例如,在Yandex的。Metrica,条件聚合函数用于实现段比较功能。 +使用条件聚合函数,您可以一次计算多个条件的聚合,而无需使用子查询和 `JOIN`例如,在Yandex.Metrica,条件聚合函数用于实现段比较功能。 -## -阵列 {#agg-functions-combinator-array} +## -Array {#agg-functions-combinator-array} -Array后缀可以附加到任何聚合函数。 在这种情况下,聚合函数采用的参数 ‘Array(T)’ 类型(数组)而不是 ‘T’ 类型参数。 如果聚合函数接受多个参数,则它必须是长度相等的数组。 在处理数组时,聚合函数的工作方式与所有数组元素的原始聚合函数类似。 -示例1: `sumArray(arr)` -总计所有的所有元素 ‘arr’ 阵列。 在这个例子中,它可以更简单地编写: `sum(arraySum(arr))`. +示例1: `sumArray(arr)` -总计所有的所有元素 ‘arr’ 阵列。在这个例子中,它可以更简单地编写: `sum(arraySum(arr))`. -示例2: `uniqArray(arr)` – Counts the number of unique elements in all ‘arr’ 阵列。 这可以做一个更简单的方法: `uniq(arrayJoin(arr))`,但它并不总是可以添加 ‘arrayJoin’ 到查询。 +示例2: `uniqArray(arr)` – 计算‘arr’中唯一元素的个数。这可以是一个更简单的方法: `uniq(arrayJoin(arr))`,但它并不总是可以添加 ‘arrayJoin’ 到查询。 --如果和-阵列可以组合。 然而, ‘Array’ 必须先来,然后 ‘If’. 例: `uniqArrayIf(arr, cond)`, `quantilesTimingArrayIf(level1, level2)(arr, cond)`. 由于这个顺序,该 ‘cond’ 参数不会是数组。 +如果和-If组合,‘Array’ 必须先来,然后 ‘If’. 例: `uniqArrayIf(arr, cond)`, `quantilesTimingArrayIf(level1, level2)(arr, cond)`。由于这个顺序,该 ‘cond’ 参数不会是数组。 -## -州 {#agg-functions-combinator-state} +## -State {#agg-functions-combinator-state} -如果应用此combinator,则聚合函数不会返回结果值(例如唯一值的数量 [uniq](reference.md#agg_function-uniq) 函数),但聚合的中间状态(用于 `uniq`,这是用于计算唯一值的数量的散列表)。 这是一个 `AggregateFunction(...)` 可用于进一步处理或存储在表中以完成聚合。 +如果应用此combinator,则聚合函数不会返回结果值(例如唯一值的数量 [uniq](reference.md#agg_function-uniq) 函数),但是返回聚合的中间状态(对于 `uniq`,返回的是计算唯一值的数量的哈希表)。 这是一个 `AggregateFunction(...)` 可用于进一步处理或存储在表中以完成稍后的聚合。 要使用这些状态,请使用: - [AggregatingMergeTree](../../engines/table-engines/mergetree-family/aggregatingmergetree.md) 表引擎。 -- [最后聚会](../../sql-reference/functions/other-functions.md#function-finalizeaggregation) 功能。 -- [跑累积](../../sql-reference/functions/other-functions.md#function-runningaccumulate) 功能。 -- [-合并](#aggregate_functions_combinators-merge) combinator +- [finalizeAggregation](../../sql-reference/functions/other-functions.md#function-finalizeaggregation) 功能。 +- [runningAccumulate](../../sql-reference/functions/other-functions.md#function-runningaccumulate) 功能。 +- [-Merge](#aggregate_functions_combinators-merge) combinator - [-MergeState](#aggregate_functions_combinators-mergestate) combinator -## -合并 {#aggregate_functions_combinators-merge} +## -Merge {#aggregate_functions_combinators-merge} 如果应用此组合器,则聚合函数将中间聚合状态作为参数,组合状态以完成聚合,并返回结果值。 ## -MergeState {#aggregate_functions_combinators-mergestate} -以与-Merge combinator相同的方式合并中间聚合状态。 但是,它不会返回结果值,而是返回中间聚合状态,类似于-State combinator。 +以与-Merge 相同的方式合并中间聚合状态。 但是,它不会返回结果值,而是返回中间聚合状态,类似于-State。 ## -ForEach {#agg-functions-combinator-foreach} @@ -55,7 +53,7 @@ The suffix -If can be appended to the name of any aggregate function. In this ca 更改聚合函数的行为。 -如果聚合函数没有输入值,则使用此combinator,它返回其返回数据类型的默认值。 适用于可以采用空输入数据的聚合函数。 +如果聚合函数没有输入值,则使用此组合器它返回其返回数据类型的默认值。 适用于可以采用空输入数据的聚合函数。 `-OrDefault` 可与其他组合器一起使用。 @@ -67,7 +65,7 @@ The suffix -If can be appended to the name of any aggregate function. In this ca **参数** -- `x` — Aggregate function parameters. +- `x` — 聚合函数参数。 **返回值** @@ -174,7 +172,7 @@ FROM └────────────────────────────────┘ ``` -## -重新采样 {#agg-functions-combinator-resample} +## -Resample {#agg-functions-combinator-resample} 允许您将数据划分为组,然后单独聚合这些组中的数据。 通过将一列中的值拆分为间隔来创建组。 @@ -184,19 +182,19 @@ FROM **参数** -- `start` — Starting value of the whole required interval for `resampling_key` 值。 -- `stop` — Ending value of the whole required interval for `resampling_key` 值。 整个时间间隔不包括 `stop` 价值 `[start, stop)`. -- `step` — Step for separating the whole interval into subintervals. The `aggFunction` 在每个子区间上独立执行。 -- `resampling_key` — Column whose values are used for separating data into intervals. +- `start` — `resampling_key` 开始值。 +- `stop` — `resampling_key` 结束边界。 区间内部不包含 `stop` 值,即 `[start, stop)`. +- `step` — 分组的步长。 The `aggFunction` 在每个子区间上独立执行。 +- `resampling_key` — 取样列,被用来分组. - `aggFunction_params` — `aggFunction` 参数。 **返回值** -- 阵列 `aggFunction` 每个子区间的结果。 +- `aggFunction` 每个子区间的结果,结果为数组。 **示例** -考虑一下 `people` 具有以下数据的表: +考虑一下 `people` 表具有以下数据的表结构: ``` text ┌─name───┬─age─┬─wage─┐ @@ -209,9 +207,9 @@ FROM └────────┴─────┴──────┘ ``` -让我们得到的人的名字,他们的年龄在于的时间间隔 `[30,60)` 和 `[60,75)`. 由于我们使用整数表示的年龄,我们得到的年龄 `[30, 59]` 和 `[60,74]` 间隔。 +让我们得到的人的名字,他们的年龄在于的时间间隔 `[30,60)` 和 `[60,75)`。 由于我们使用整数表示的年龄,我们得到的年龄 `[30, 59]` 和 `[60,74]` 间隔。 -要在数组中聚合名称,我们使用 [groupArray](reference.md#agg_function-grouparray) 聚合函数。 这需要一个参数。 在我们的例子中,它是 `name` 列。 该 `groupArrayResample` 函数应该使用 `age` 按年龄聚合名称的列。 要定义所需的时间间隔,我们通过 `30, 75, 30` 参数到 `groupArrayResample` 功能。 +要在数组中聚合名称,我们使用 [groupArray](reference.md#agg_function-grouparray) 聚合函数。 这需要一个参数。 在我们的例子中,它是 `name` 列。 `groupArrayResample` 函数应该使用 `age` 按年龄聚合名称, 要定义所需的时间间隔,我们传入 `30, 75, 30` 参数给 `groupArrayResample` 函数。 ``` sql SELECT groupArrayResample(30, 75, 30)(name, age) FROM people @@ -225,7 +223,7 @@ SELECT groupArrayResample(30, 75, 30)(name, age) FROM people 考虑结果。 -`Jonh` 是因为他太年轻了 其他人按照指定的年龄间隔进行分配。 +`Jonh` 没有被选中,因为他太年轻了。 其他人按照指定的年龄间隔进行分配。 现在让我们计算指定年龄间隔内的总人数和平均工资。 diff --git a/docs/zh/sql-reference/aggregate-functions/index.md b/docs/zh/sql-reference/aggregate-functions/index.md index 06666c49d03..57d8e362d99 100644 --- a/docs/zh/sql-reference/aggregate-functions/index.md +++ b/docs/zh/sql-reference/aggregate-functions/index.md @@ -1,9 +1,6 @@ --- -machine_translated: true -machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd -toc_folder_title: "\u805A\u5408\u51FD\u6570" toc_priority: 33 -toc_title: "\u5BFC\u8A00" +toc_title: 简介 --- # 聚合函数 {#aggregate-functions} diff --git a/docs/zh/sql-reference/aggregate-functions/parametric-functions.md b/docs/zh/sql-reference/aggregate-functions/parametric-functions.md index c8f942013ea..69572086549 100644 --- a/docs/zh/sql-reference/aggregate-functions/parametric-functions.md +++ b/docs/zh/sql-reference/aggregate-functions/parametric-functions.md @@ -1,15 +1,13 @@ --- -machine_translated: true -machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd toc_priority: 38 -toc_title: "\u53C2\u6570" +toc_title: 参数聚合函数 --- # 参数聚合函数 {#aggregate_functions_parametric} -Some aggregate functions can accept not only argument columns (used for compression), but a set of parameters – constants for initialization. The syntax is two pairs of brackets instead of one. The first is for parameters, and the second is for arguments. +一些聚合函数不仅可以接受参数列(用于压缩),也可以接收常量的初始化参数。这种语法是接受两个括号的参数,第一个数初始化参数,第二个是入参。 -## 直方图 {#histogram} +## histogram {#histogram} 计算自适应直方图。 它不能保证精确的结果。 @@ -21,20 +19,21 @@ histogram(number_of_bins)(values) **参数** -`number_of_bins` — Upper limit for the number of bins in the histogram. The function automatically calculates the number of bins. It tries to reach the specified number of bins, but if it fails, it uses fewer bins. -`values` — [表达式](../syntax.md#syntax-expressions) 导致输入值。 +`number_of_bins` — 直方图bin个数,这个函数会自动计算bin的数量,而且会尽量使用指定值,如果无法做到,那就使用更小的bin个数。 + +`values` — [表达式](../syntax.md#syntax-expressions) 输入值。 **返回值** -- [阵列](../../sql-reference/data-types/array.md) 的 [元组](../../sql-reference/data-types/tuple.md) 下面的格式: +- [Array](../../sql-reference/data-types/array.md) 的 [Tuples](../../sql-reference/data-types/tuple.md) 如下: ``` [(lower_1, upper_1, height_1), ... (lower_N, upper_N, height_N)] ``` - - `lower` — Lower bound of the bin. - - `upper` — Upper bound of the bin. - - `height` — Calculated height of the bin. + - `lower` — bin的下边界。 + - `upper` — bin的上边界。 + - `height` — bin的计算权重。 **示例** @@ -53,7 +52,7 @@ FROM ( └─────────────────────────────────────────────────────────────────────────┘ ``` -您可以使用 [酒吧](../../sql-reference/functions/other-functions.md#function-bar) 功能,例如: +您可以使用 [bar](../../sql-reference/functions/other-functions.md#function-bar) 功能,例如: ``` sql WITH histogram(5)(rand() % 100) AS hist @@ -93,11 +92,11 @@ sequenceMatch(pattern)(timestamp, cond1, cond2, ...) **参数** -- `pattern` — Pattern string. See [模式语法](#sequence-function-pattern-syntax). +- `pattern` — 模式字符串。 参考 [模式语法](#sequence-function-pattern-syntax). -- `timestamp` — Column considered to contain time data. Typical data types are `Date` 和 `DateTime`. 您还可以使用任何支持的 [UInt](../../sql-reference/data-types/int-uint.md) 数据类型。 +- `timestamp` — 包含时间的列。典型的时间类型是: `Date` 和 `DateTime`。您还可以使用任何支持的 [UInt](../../sql-reference/data-types/int-uint.md) 数据类型。 -- `cond1`, `cond2` — Conditions that describe the chain of events. Data type: `UInt8`. 最多可以传递32个条件参数。 该函数只考虑这些条件中描述的事件。 如果序列包含未在条件中描述的数据,则函数将跳过这些数据。 +- `cond1`, `cond2` — 事件链的约束条件。 数据类型是: `UInt8`。 最多可以传递32个条件参数。 该函数只考虑这些条件中描述的事件。 如果序列包含未在条件中描述的数据,则函数将跳过这些数据。 **返回值** @@ -109,11 +108,11 @@ sequenceMatch(pattern)(timestamp, cond1, cond2, ...) **模式语法** -- `(?N)` — Matches the condition argument at position `N`. 条件在编号 `[1, 32]` 范围。 例如, `(?1)` 匹配传递给 `cond1` 参数。 +- `(?N)` — 在位置`N`匹配条件参数。 条件在编号 `[1, 32]` 范围。 例如, `(?1)` 匹配传递给 `cond1` 参数。 -- `.*` — Matches any number of events. You don't need conditional arguments to match this element of the pattern. +- `.*` — 匹配任何事件的数字。 不需要条件参数来匹配这个模式。 -- `(?t operator value)` — Sets the time in seconds that should separate two events. For example, pattern `(?1)(?t>1800)(?2)` 匹配彼此发生超过1800秒的事件。 这些事件之间可以存在任意数量的任何事件。 您可以使用 `>=`, `>`, `<`, `<=` 运营商。 +- `(?t operator value)` — 分开两个事件的时间。 例如: `(?1)(?t>1800)(?2)` 匹配彼此发生超过1800秒的事件。 这些事件之间可以存在任意数量的任何事件。 您可以使用 `>=`, `>`, `<`, `<=` 运算符。 **例** @@ -169,7 +168,7 @@ SELECT sequenceMatch('(?1)(?2)')(time, number = 1, number = 2, number = 4) FROM ## sequenceCount(pattern)(time, cond1, cond2, …) {#function-sequencecount} -计数与模式匹配的事件链的数量。 该函数搜索不重叠的事件链。 当前链匹配后,它开始搜索下一个链。 +计算与模式匹配的事件链的数量。该函数搜索不重叠的事件链。当前链匹配后,它开始搜索下一个链。 !!! warning "警告" 在同一秒钟发生的事件可能以未定义的顺序排列在序列中,影响结果。 @@ -180,11 +179,11 @@ sequenceCount(pattern)(timestamp, cond1, cond2, ...) **参数** -- `pattern` — Pattern string. See [模式语法](#sequence-function-pattern-syntax). +- `pattern` — 模式字符串。 参考:[模式语法](#sequence-function-pattern-syntax). -- `timestamp` — Column considered to contain time data. Typical data types are `Date` 和 `DateTime`. 您还可以使用任何支持的 [UInt](../../sql-reference/data-types/int-uint.md) 数据类型。 +- `timestamp` — 包含时间的列。典型的时间类型是: `Date` 和 `DateTime`。您还可以使用任何支持的 [UInt](../../sql-reference/data-types/int-uint.md) 数据类型。 -- `cond1`, `cond2` — Conditions that describe the chain of events. Data type: `UInt8`. 最多可以传递32个条件参数。 该函数只考虑这些条件中描述的事件。 如果序列包含未在条件中描述的数据,则函数将跳过这些数据。 +- `cond1`, `cond2` — 事件链的约束条件。 数据类型是: `UInt8`。 最多可以传递32个条件参数。该函数只考虑这些条件中描述的事件。 如果序列包含未在条件中描述的数据,则函数将跳过这些数据。 **返回值** @@ -227,9 +226,9 @@ SELECT sequenceCount('(?1).*(?2)')(time, number = 1, number = 2) FROM t 搜索滑动时间窗中的事件链,并计算从链中发生的最大事件数。 -该函数根据算法工作: +该函数采用如下算法: -- 该函数搜索触发链中的第一个条件并将事件计数器设置为1的数据。 这是滑动窗口启动的时刻。 +- 该函数搜索触发链中的第一个条件并将事件计数器设置为1。 这是滑动窗口启动的时刻。 - 如果来自链的事件在窗口内顺序发生,则计数器将递增。 如果事件序列中断,则计数器不会增加。 @@ -243,11 +242,11 @@ windowFunnel(window, [mode])(timestamp, cond1, cond2, ..., condN) **参数** -- `window` — Length of the sliding window in seconds. -- `mode` -这是一个可选的参数。 - - `'strict'` -当 `'strict'` 设置时,windowFunnel()仅对唯一值应用条件。 -- `timestamp` — Name of the column containing the timestamp. Data types supported: [日期](../../sql-reference/data-types/date.md), [日期时间](../../sql-reference/data-types/datetime.md#data_type-datetime) 和其他无符号整数类型(请注意,即使时间戳支持 `UInt64` 类型,它的值不能超过Int64最大值,即2^63-1)。 -- `cond` — Conditions or data describing the chain of events. [UInt8](../../sql-reference/data-types/int-uint.md). +- `window` — 滑动窗户的大小,单位是秒。 +- `mode` - 这是一个可选的参数。 + - `'strict'` - 当 `'strict'` 设置时,windowFunnel()仅对唯一值应用匹配条件。 +- `timestamp` — 包含时间的列。 数据类型支持: [日期](../../sql-reference/data-types/date.md), [日期时间](../../sql-reference/data-types/datetime.md#data_type-datetime) 和其他无符号整数类型(请注意,即使时间戳支持 `UInt64` 类型,它的值不能超过Int64最大值,即2^63-1)。 +- `cond` — 事件链的约束条件。 [UInt8](../../sql-reference/data-types/int-uint.md) 类型。 **返回值** @@ -284,7 +283,7 @@ windowFunnel(window, [mode])(timestamp, cond1, cond2, ..., condN) └────────────┴─────────┴─────────────────────┴─────────┴─────────┘ ``` -了解用户有多远 `user_id` 可以在2019的1-2月期间通过链条。 +了解用户`user_id` 可以在2019的1-2月期间通过链条多远。 查询: @@ -313,12 +312,12 @@ ORDER BY level ASC └───────┴───┘ ``` -## 保留 {#retention} +## Retention {#retention} -该函数将一组条件作为参数,类型为1到32个参数 `UInt8` 表示事件是否满足特定条件。 +该函数将一组条件作为参数,类型为1到32个 `UInt8` 类型的参数,用来表示事件是否满足特定条件。 任何条件都可以指定为参数(如 [WHERE](../../sql-reference/statements/select/where.md#select-where)). -除了第一个以外,条件成对适用:如果第一个和第二个是真的,第二个结果将是真的,如果第一个和fird是真的,第三个结果将是真的,等等。 +除了第一个以外,条件成对适用:如果第一个和第二个是真的,第二个结果将是真的,如果第一个和第三个是真的,第三个结果将是真的,等等。 **语法** @@ -328,22 +327,22 @@ retention(cond1, cond2, ..., cond32); **参数** -- `cond` — an expression that returns a `UInt8` 结果(1或0)。 +- `cond` — 返回 `UInt8` 结果(1或0)的表达式。 **返回值** 数组为1或0。 -- 1 — condition was met for the event. -- 0 — condition wasn't met for the event. +- 1 — 条件满足。 +- 0 — 条件不满足。 类型: `UInt8`. **示例** -让我们考虑计算的一个例子 `retention` 功能,以确定网站流量。 +让我们考虑使用 `retention` 功能的一个例子 ,以确定网站流量。 -**1.** Сreate a table to illustrate an example. +**1.** 举例说明,先创建一张表。 ``` sql CREATE TABLE retention_test(date Date, uid Int32) ENGINE = Memory; @@ -402,7 +401,7 @@ SELECT * FROM retention_test └────────────┴─────┘ ``` -**2.** 按唯一ID对用户进行分组 `uid` 使用 `retention` 功能。 +**2.** 按唯一ID `uid` 对用户进行分组,使用 `retention` 功能。 查询: @@ -466,7 +465,7 @@ FROM └────┴────┴────┘ ``` -哪里: +条件: - `r1`-2020-01-01期间访问该网站的独立访问者数量( `cond1` 条件)。 - `r2`-在2020-01-01和2020-01-02之间的特定时间段内访问该网站的唯一访问者的数量 (`cond1` 和 `cond2` 条件)。 @@ -474,9 +473,9 @@ FROM ## uniqUpTo(N)(x) {#uniquptonx} -Calculates the number of different argument values ​​if it is less than or equal to N. If the number of different argument values is greater than N, it returns N + 1. +计算小于或者等于N的不同参数的个数。如果结果大于N,那返回N+1。 -建议使用小Ns,高达10。 N的最大值为100。 +建议使用较小的Ns,比如:10。N的最大值为100。 对于聚合函数的状态,它使用的内存量等于1+N\*一个字节值的大小。 对于字符串,它存储8个字节的非加密哈希。 也就是说,计算是近似的字符串。 @@ -488,12 +487,12 @@ Calculates the number of different argument values ​​if it is less than or e 用法示例: ``` text -Problem: Generate a report that shows only keywords that produced at least 5 unique users. -Solution: Write in the GROUP BY query SearchPhrase HAVING uniqUpTo(4)(UserID) >= 5 +问题:产出一个不少于五个唯一用户的关键字报告 +解决方案: 写group by查询语句 HAVING uniqUpTo(4)(UserID) >= 5 ``` +## sumMapFiltered(keys\_to\_keep)(keys, values) {#summapfilteredkeys-to-keepkeys-values} + +和 [sumMap](reference.md#agg_functions-summap) 基本一致, 除了一个键数组作为参数传递。这在使用高基数key时尤其有用。 + [原始文章](https://clickhouse.tech/docs/en/query_language/agg_functions/parametric_functions/) - -## sumMapFiltered(keys\_to\_keep)(键值) {#summapfilteredkeys-to-keepkeys-values} - -同样的行为 [sumMap](reference.md#agg_functions-summap) 除了一个键数组作为参数传递。 这在使用高基数密钥时尤其有用。 diff --git a/docs/zh/sql-reference/aggregate-functions/reference.md b/docs/zh/sql-reference/aggregate-functions/reference.md index 53510900536..7d5ecda7bb4 100644 --- a/docs/zh/sql-reference/aggregate-functions/reference.md +++ b/docs/zh/sql-reference/aggregate-functions/reference.md @@ -1,13 +1,11 @@ --- -machine_translated: true -machine_translated_rev: 72537a2d527c63c07aa5d2361a8829f3895cf2bd toc_priority: 36 -toc_title: "\u53C2\u8003\u8D44\u6599" +toc_title: 聚合函数 --- # 聚合函数引用 {#aggregate-functions-reference} -## 计数 {#agg_function-count} +## count {#agg_function-count} 计数行数或非空值。 @@ -73,7 +71,7 @@ SELECT count(DISTINCT num) FROM t 这个例子表明 `count(DISTINCT num)` 由执行 `uniqExact` 根据功能 `count_distinct_implementation` 设定值。 -## 任何(x) {#agg_function-any} +## any(x) {#agg_function-any} 选择第一个遇到的值。 查询可以以任何顺序执行,甚至每次都以不同的顺序执行,因此此函数的结果是不确定的。 @@ -115,7 +113,7 @@ FROM ontime 选择遇到的最后一个值。 其结果是一样不确定的 `any` 功能。 -## 集团比特 {#groupbitand} +## groupBitAnd {#groupbitand} 按位应用 `AND` 对于一系列的数字。 @@ -337,7 +335,7 @@ SELECT argMin(user, salary) FROM salary 总计 ‘value’ 数组根据在指定的键 ‘key’ 阵列。 传递键和值数组的元组与传递两个键和值数组是同义的。 元素的数量 ‘key’ 和 ‘value’ 总计的每一行必须相同。 -Returns a tuple of two arrays: keys in sorted order, and values ​​summed for the corresponding keys. +返回两个数组的一个二元组: key是排好序的,value是对应key的求和。 示例: @@ -374,7 +372,7 @@ GROUP BY timeslot ## skewPop {#skewpop} -计算 [歪斜](https://en.wikipedia.org/wiki/Skewness) 的序列。 +计算的序列[偏度](https://en.wikipedia.org/wiki/Skewness)。 ``` sql skewPop(expr) @@ -386,7 +384,7 @@ skewPop(expr) **返回值** -The skewness of the given distribution. Type — [Float64](../../sql-reference/data-types/float.md) +给定序列的偏度。类型 — [Float64](../../sql-reference/data-types/float.md) **示例** @@ -410,7 +408,7 @@ skewSamp(expr) **返回值** -The skewness of the given distribution. Type — [Float64](../../sql-reference/data-types/float.md). 如果 `n <= 1` (`n` 是样本的大小),则该函数返回 `nan`. +给定序列的偏度。 类型 — [Float64](../../sql-reference/data-types/float.md). 如果 `n <= 1` (`n` 是样本的大小),则该函数返回 `nan`. **示例** @@ -432,7 +430,7 @@ kurtPop(expr) **返回值** -The kurtosis of the given distribution. Type — [Float64](../../sql-reference/data-types/float.md) +给定序列的峰度。 类型 — [Float64](../../sql-reference/data-types/float.md) **示例** @@ -456,7 +454,7 @@ kurtSamp(expr) **返回值** -The kurtosis of the given distribution. Type — [Float64](../../sql-reference/data-types/float.md). 如果 `n <= 1` (`n` 是样本的大小),则该函数返回 `nan`. +给定序列的峰度。类型 — [Float64](../../sql-reference/data-types/float.md). 如果 `n <= 1` (`n` 是样本的大小),则该函数返回 `nan`. **示例** @@ -533,7 +531,7 @@ FROM ( 只适用于数字。 结果总是Float64。 -## 平均加权 {#avgweighted} +## avgWeighted {#avgweighted} 计算 [加权算术平均值](https://en.wikipedia.org/wiki/Weighted_arithmetic_mean). @@ -545,10 +543,10 @@ avgWeighted(x, weight) **参数** -- `x` — Values. [整数](../data-types/int-uint.md) 或 [浮点](../data-types/float.md). -- `weight` — Weights of the values. [整数](../data-types/int-uint.md) 或 [浮点](../data-types/float.md). +- `x` — 值。 [整数](../data-types/int-uint.md) 或 [浮点](../data-types/float.md). +- `weight` — 值的加权。 [整数](../data-types/int-uint.md) 或 [浮点](../data-types/float.md). -类型 `x` 和 `weight` 一定是一样的 +`x` 和 `weight` 的类型一定是一样的 **返回值** @@ -590,7 +588,7 @@ uniq(x[, ...]) - A [UInt64](../../sql-reference/data-types/int-uint.md)-键入号码。 -**实施细节** +**实现细节** 功能: @@ -598,7 +596,7 @@ uniq(x[, ...]) - 使用自适应采样算法。 对于计算状态,该函数使用最多65536个元素哈希值的样本。 - This algorithm is very accurate and very efficient on the CPU. When the query contains several of these functions, using `uniq` is almost as fast as using other aggregate functions. + 这个算法是非常精确的,并且对于CPU来说非常高效。如果查询包含一些这样的函数,那和其他聚合函数相比 `uniq` 将是几乎一样快。 - 确定性地提供结果(它不依赖于查询处理顺序)。 @@ -629,17 +627,17 @@ uniqCombined(HLL_precision)(x[, ...]) **返回值** -- 一个数字 [UInt64](../../sql-reference/data-types/int-uint.md)-键入号码。 +- 一个[UInt64](../../sql-reference/data-types/int-uint.md)类型的数字。 -**实施细节** +**实现细节** 功能: - 计算散列(64位散列 `String` 否则32位)对于聚合中的所有参数,然后在计算中使用它。 -- 使用三种算法的组合:数组、哈希表和HyperLogLog与error错表。 +- 使用三种算法的组合:数组、哈希表和包含错误修正表的HyperLogLog。 - For a small number of distinct elements, an array is used. When the set size is larger, a hash table is used. For a larger number of elements, HyperLogLog is used, which will occupy a fixed amount of memory. + 少量的不同的值,使用数组。 值再多一些,使用哈希表。对于大量的数据来说,使用HyperLogLog,HyperLogLog占用一个固定的内存空间。 - 确定性地提供结果(它不依赖于查询处理顺序)。 @@ -650,7 +648,7 @@ uniqCombined(HLL_precision)(x[, ...]) - 消耗少几倍的内存。 - 计算精度高出几倍。 -- 通常具有略低的性能。 在某些情况下, `uniqCombined` 可以表现得比 `uniq`,例如,使用通过网络传输大量聚合状态的分布式查询。 +- 通常具有略低的性能。 在某些情况下, `uniqCombined` 可以表现得比 `uniq` 好,例如,使用通过网络传输大量聚合状态的分布式查询。 **另请参阅** @@ -679,7 +677,7 @@ uniqHLL12(x[, ...]) - A [UInt64](../../sql-reference/data-types/int-uint.md)-键入号码。 -**实施细节** +**实现细节** 功能: @@ -707,9 +705,9 @@ uniqHLL12(x[, ...]) uniqExact(x[, ...]) ``` -使用 `uniqExact` 功能,如果你绝对需要一个确切的结果。 否则使用 [uniq](#agg_function-uniq) 功能。 +如果你绝对需要一个确切的结果,使用 `uniqExact` 功能。 否则使用 [uniq](#agg_function-uniq) 功能。 -该 `uniqExact` 功能使用更多的内存比 `uniq`,因为状态的大小随着不同值的数量的增加而无界增长。 +`uniqExact` 比 `uniq` 使用更多的内存,因为状态的大小随着不同值的数量的增加而无界增长。 **参数** @@ -721,7 +719,7 @@ uniqExact(x[, ...]) - [uniqCombined](#agg_function-uniqcombined) - [uniqHLL12](#agg_function-uniqhll12) -## 群交(x),群交(max\_size)(x) {#agg_function-grouparray} +## groupArray(x), groupArray(max\_size)(x) {#agg_function-grouparray} 创建参数值的数组。 值可以按任何(不确定)顺序添加到数组中。 @@ -748,10 +746,10 @@ groupArrayInsertAt(default_x, size)(x, pos); **参数** -- `x` — Value to be inserted. [表达式](../syntax.md#syntax-expressions) 导致的一个 [支持的数据类型](../../sql-reference/data-types/index.md). -- `pos` — Position at which the specified element `x` 将被插入。 数组中的索引编号从零开始。 [UInt32](../../sql-reference/data-types/int-uint.md#uint-ranges). -- `default_x`— Default value for substituting in empty positions. Optional parameter. [表达式](../syntax.md#syntax-expressions) 导致为配置的数据类型 `x` 参数。 如果 `default_x` 未定义,则 [默认值](../../sql-reference/statements/create.md#create-default-values) 被使用。 -- `size`— Length of the resulting array. Optional parameter. When using this parameter, the default value `default_x` 必须指定。 [UInt32](../../sql-reference/data-types/int-uint.md#uint-ranges). +- `x` — 被插入的值。[表达式](../syntax.md#syntax-expressions) 导致的一个 [支持的数据类型](../../sql-reference/data-types/index.md). +- `pos` — `x` 将被插入的位置。 数组中的索引编号从零开始。 [UInt32](../../sql-reference/data-types/int-uint.md#uint-ranges). +- `default_x`— 如果代入值为空,则使用默认值。可选参数。[表达式](../syntax.md#syntax-expressions) 为 `x` 数据类型的数据。 如果 `default_x` 未定义,则 [默认值](../../sql-reference/statements/create.md#create-default-values) 被使用。 +- `size`— 结果数组的长度。可选参数。如果使用该参数,`default_x` 必须指定。 [UInt32](../../sql-reference/data-types/int-uint.md#uint-ranges). **返回值** @@ -803,7 +801,7 @@ SELECT groupArrayInsertAt('-', 5)(toString(number), number * 2) FROM numbers(5); └───────────────────────────────────────────────────────────────────┘ ``` -元件的多线程插入到一个位置。 +在一个位置多线程插入数据。 查询: @@ -832,8 +830,8 @@ groupArrayMovingSum(window_size)(numbers_for_summing) **参数** -- `numbers_for_summing` — [表达式](../syntax.md#syntax-expressions) 生成数值数据类型值。 -- `window_size` — Size of the calculation window. +- `numbers_for_summing` — [表达式](../syntax.md#syntax-expressions) 为数值数据类型值。 +- `window_size` — 窗口大小。 **返回值** @@ -906,13 +904,13 @@ groupArrayMovingAvg(window_size)(numbers_for_summing) **参数** - `numbers_for_summing` — [表达式](../syntax.md#syntax-expressions) 生成数值数据类型值。 -- `window_size` — Size of the calculation window. +- `window_size` — 窗口大小。 **返回值** - 与输入数据大小和类型相同的数组。 -该函数使用 [四舍五入到零](https://en.wikipedia.org/wiki/Rounding#Rounding_towards_zero). 它截断结果数据类型的小数位数。 +该函数使用 [四舍五入到零](https://en.wikipedia.org/wiki/Rounding#Rounding_towards_zero). 它截断无意义的小数位来保证结果的数据类型。 **示例** @@ -967,20 +965,20 @@ FROM t └───────────┴──────────────────────────────────┴───────────────────────┘ ``` -## 禄,赂麓ta脌麓,):脡,,拢脢,group媒group)galaxy s8碌胫脢)禄煤)酶脱脩) {#groupuniqarrayx-groupuniqarraymax-sizex} +## groupUniqArray(x), groupUniqArray(max\_size)(x) {#groupuniqarrayx-groupuniqarraymax-sizex} 从不同的参数值创建一个数组。 内存消耗是一样的 `uniqExact` 功能。 -第二个版本(与 `max_size` 参数)将结果数组的大小限制为 `max_size` 元素。 +第二个版本(`max_size` 参数)将结果数组的大小限制为 `max_size` 元素。 例如, `groupUniqArray(1)(x)` 相当于 `[any(x)]`. -## 分位数 {#quantile} +## quantile {#quantile} -计算近似值 [分位数](https://en.wikipedia.org/wiki/Quantile) 的数字数据序列。 +计算数字序列的近似[分位数](https://en.wikipedia.org/wiki/Quantile)。 -此功能适用 [油藏采样](https://en.wikipedia.org/wiki/Reservoir_sampling) 随着储存器大小高达8192和随机数发生器进行采样。 结果是非确定性的。 要获得精确的分位数,请使用 [quantileExact](#quantileexact) 功能。 +此功能适用 [水塘抽样(](https://en.wikipedia.org/wiki/Reservoir_sampling),使用储存器最大到8192和随机数发生器进行采样。 结果是非确定性的。 要获得精确的分位数,请使用 [quantileExact](#quantileexact) 功能。 -当使用多个 `quantile*` 在查询中具有不同级别的函数,内部状态不会被组合(即查询的工作效率低于它可以)。 在这种情况下,使用 [分位数](#quantiles) 功能。 +当在一个查询中使用多个不同层次的 `quantile*` 时,内部状态不会被组合(即查询的工作效率低于组合情况)。在这种情况下,使用[分位数](#quantiles)功能。 **语法** @@ -992,12 +990,12 @@ quantile(level)(expr) **参数** -- `level` — Level of quantile. Optional parameter. Constant floating-point number from 0 to 1. We recommend using a `level` 值的范围 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). -- `expr` — Expression over the column values resulting in numeric [数据类型](../../sql-reference/data-types/index.md#data_types), [日期](../../sql-reference/data-types/date.md) 或 [日期时间](../../sql-reference/data-types/datetime.md). +- `level` — 分位数层次。可选参数。 从0到1的一个float类型的常量。 我们推荐 `level` 值的范围为 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). +- `expr` — 求职表达式,类型为:数值[数据类型](../../sql-reference/data-types/index.md#data_types),[日期](../../sql-reference/data-types/date.md)数据类型或[时间](../../sql-reference/data-types/datetime.md)数据类型。 **返回值** -- 指定电平的近似分位数。 +- 指定层次的近似分位数。 类型: @@ -1037,13 +1035,13 @@ SELECT quantile(val) FROM t - [中位数](#median) - [分位数](#quantiles) -## 量化确定 {#quantiledeterministic} +## quantileDeterministic {#quantiledeterministic} -计算近似值 [分位数](https://en.wikipedia.org/wiki/Quantile) 的数字数据序列。 +计算数字序列的近似[分位数](https://en.wikipedia.org/wiki/Quantile)。 -此功能适用 [油藏采样](https://en.wikipedia.org/wiki/Reservoir_sampling) 与储层大小高达8192和采样的确定性算法。 结果是确定性的。 要获得精确的分位数,请使用 [quantileExact](#quantileexact) 功能。 +此功能适用 [水塘抽样(](https://en.wikipedia.org/wiki/Reservoir_sampling),使用储存器最大到8192和随机数发生器进行采样。 结果是非确定性的。 要获得精确的分位数,请使用 [quantileExact](#quantileexact) 功能。 -当使用多个 `quantile*` 在查询中具有不同级别的函数,内部状态不会被组合(即查询的工作效率低于它可以)。 在这种情况下,使用 [分位数](#quantiles) 功能。 +当在一个查询中使用多个不同层次的 `quantile*` 时,内部状态不会被组合(即查询的工作效率低于组合情况)。在这种情况下,使用[分位数](#quantiles)功能。 **语法** @@ -1055,13 +1053,13 @@ quantileDeterministic(level)(expr, determinator) **参数** -- `level` — Level of quantile. Optional parameter. Constant floating-point number from 0 to 1. We recommend using a `level` 值的范围 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). -- `expr` — Expression over the column values resulting in numeric [数据类型](../../sql-reference/data-types/index.md#data_types), [日期](../../sql-reference/data-types/date.md) 或 [日期时间](../../sql-reference/data-types/datetime.md). -- `determinator` — Number whose hash is used instead of a random number generator in the reservoir sampling algorithm to make the result of sampling deterministic. As a determinator you can use any deterministic positive number, for example, a user id or an event id. If the same determinator value occures too often, the function works incorrectly. +- `level` — 分位数层次。可选参数。 从0到1的一个float类型的常量。 我们推荐 `level` 值的范围为 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). +- `expr` — 求职表达式,类型为:数值[数据类型](../../sql-reference/data-types/index.md#data_types),[日期](../../sql-reference/data-types/date.md)数据类型或[时间](../../sql-reference/data-types/datetime.md)数据类型。 +- `determinator` — 一个数字,其hash被用来代替在水塘抽样中随机生成的数字,这样可以保证取样的确定性。你可以使用用户ID或者事件ID等任何正数,但是如果相同的 `determinator` 出现多次,那结果很可能不正确。 **返回值** -- 指定电平的近似分位数。 +- 指定层次的近似分位数。 类型: @@ -1103,11 +1101,11 @@ SELECT quantileDeterministic(val, 1) FROM t ## quantileExact {#quantileexact} -正是计算 [分位数](https://en.wikipedia.org/wiki/Quantile) 的数字数据序列。 +准确计算数字序列的[分位数](https://en.wikipedia.org/wiki/Quantile)。 -To get exact value, all the passed values ​​are combined into an array, which is then partially sorted. Therefore, the function consumes `O(n)` 内存,其中 `n` 是传递的多个值。 然而,对于少量的值,该函数是非常有效的。 +为了准确计算,所有输入的数据被合并为一个数组,并且部分的排序。因此该函数需要 `O(n)` 的内存,n为输入数据的个数。但是对于少量数据来说,该函数还是非常有效的。 -当使用多个 `quantile*` 在查询中具有不同级别的函数,内部状态不会被组合(即查询的工作效率低于它可以)。 在这种情况下,使用 [分位数](#quantiles) 功能。 +当在一个查询中使用多个不同层次的 `quantile*` 时,内部状态不会被组合(即查询的工作效率低于组合情况)。在这种情况下,使用[分位数](#quantiles)功能。 **语法** @@ -1119,12 +1117,12 @@ quantileExact(level)(expr) **参数** -- `level` — Level of quantile. Optional parameter. Constant floating-point number from 0 to 1. We recommend using a `level` 值的范围 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). -- `expr` — Expression over the column values resulting in numeric [数据类型](../../sql-reference/data-types/index.md#data_types), [日期](../../sql-reference/data-types/date.md) 或 [日期时间](../../sql-reference/data-types/datetime.md). +- `level` — 分位数层次。可选参数。 从0到1的一个float类型的常量。 我们推荐 `level` 值的范围为 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). +- `expr` — 求职表达式,类型为:数值[数据类型](../../sql-reference/data-types/index.md#data_types),[日期](../../sql-reference/data-types/date.md)数据类型或[时间](../../sql-reference/data-types/datetime.md)数据类型。 **返回值** -- 指定电平的分位数。 +- 指定层次的分位数。 类型: @@ -1153,13 +1151,13 @@ SELECT quantileExact(number) FROM numbers(10) - [中位数](#median) - [分位数](#quantiles) -## 分位数加权 {#quantileexactweighted} +## quantileExactWeighted {#quantileexactweighted} -正是计算 [分位数](https://en.wikipedia.org/wiki/Quantile) 数值数据序列,考虑到每个元素的权重。 +考虑到每个元素的权重,然后准确计算数值序列的[分位数](https://en.wikipedia.org/wiki/Quantile)。 -To get exact value, all the passed values ​​are combined into an array, which is then partially sorted. Each value is counted with its weight, as if it is present `weight` times. A hash table is used in the algorithm. Because of this, if the passed values ​​are frequently repeated, the function consumes less RAM than [quantileExact](#quantileexact). 您可以使用此功能,而不是 `quantileExact` 并指定重量1。 +为了准确计算,所有输入的数据被合并为一个数组,并且部分的排序。每个输入值需要根据 `weight` 计算求和。该算法使用哈希表。正因为如此,在数据重复较多的时候使用的内存是少于[quantileExact](#quantileexact)的。 您可以使用此函数代替 `quantileExact` 并指定重量1。 -当使用多个 `quantile*` 在查询中具有不同级别的函数,内部状态不会被组合(即查询的工作效率低于它可以)。 在这种情况下,使用 [分位数](#quantiles) 功能。 +当在一个查询中使用多个不同层次的 `quantile*` 时,内部状态不会被组合(即查询的工作效率低于组合情况)。在这种情况下,使用[分位数](#quantiles)功能。 **语法** @@ -1171,13 +1169,13 @@ quantileExactWeighted(level)(expr, weight) **参数** -- `level` — Level of quantile. Optional parameter. Constant floating-point number from 0 to 1. We recommend using a `level` 值的范围 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). -- `expr` — Expression over the column values resulting in numeric [数据类型](../../sql-reference/data-types/index.md#data_types), [日期](../../sql-reference/data-types/date.md) 或 [日期时间](../../sql-reference/data-types/datetime.md). -- `weight` — Column with weights of sequence members. Weight is a number of value occurrences. +- `level` — 分位数层次。可选参数。 从0到1的一个float类型的常量。 我们推荐 `level` 值的范围为 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). +- `expr` — 求职表达式,类型为:数值[数据类型](../../sql-reference/data-types/index.md#data_types),[日期](../../sql-reference/data-types/date.md)数据类型或[时间](../../sql-reference/data-types/datetime.md)数据类型。 +- `weight` — 权重序列。 权重是一个数据出现的数值。 **返回值** -- 指定电平的分位数。 +- 指定层次的分位数。 类型: @@ -1217,13 +1215,13 @@ SELECT quantileExactWeighted(n, val) FROM t - [中位数](#median) - [分位数](#quantiles) -## 分位定时 {#quantiletiming} +## quantileTiming {#quantiletiming} -随着确定的精度计算 [分位数](https://en.wikipedia.org/wiki/Quantile) 的数字数据序列。 +使用确定的精度计算数字数据序列的[分位数](https://en.wikipedia.org/wiki/Quantile)。 结果是确定性的(它不依赖于查询处理顺序)。 该函数针对描述加载网页时间或后端响应时间等分布的序列进行了优化。 -当使用多个 `quantile*` 在查询中具有不同级别的函数,内部状态不会被组合(即查询的工作效率低于它可以)。 在这种情况下,使用 [分位数](#quantiles) 功能。 +当在一个查询中使用多个不同层次的 `quantile*` 时,内部状态不会被组合(即查询的工作效率低于组合情况)。在这种情况下,使用[分位数](#quantiles)功能。 **语法** @@ -1235,12 +1233,12 @@ quantileTiming(level)(expr) **参数** -- `level` — Level of quantile. Optional parameter. Constant floating-point number from 0 to 1. We recommend using a `level` 值的范围 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). +- `level` — 分位数层次。可选参数。 从0到1的一个float类型的常量。 我们推荐 `level` 值的范围为 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). -- `expr` — [表达式](../syntax.md#syntax-expressions) 在一个列值返回 [浮动\*](../../sql-reference/data-types/float.md)-键入号码。 +- `expr` — [表达式](../syntax.md#syntax-expressions),返回 [浮动\*](../../sql-reference/data-types/float.md)类型数据。 - - If negative values are passed to the function, the behavior is undefined. - - If the value is greater than 30,000 (a page loading time of more than 30 seconds), it is assumed to be 30,000. + - 如果输入负值,那结果是不可预期的。 + - 如果输入值大于30000(页面加载时间大于30s),那我们假设为30000。 **精度** @@ -1252,16 +1250,16 @@ quantileTiming(level)(expr) 否则,计算结果将四舍五入到16毫秒的最接近倍数。 !!! note "注" - 对于计算页面加载时间分位数,此函数比 [分位数](#quantile). + 对于计算页面加载时间分位数,此函数比 [分位数](#quantile)更有效和准确。 **返回值** -- 指定电平的分位数。 +- 指定层次的分位数。 类型: `Float32`. !!! note "注" - 如果没有值传递给函数(当使用 `quantileTimingIf`), [阿南](../../sql-reference/data-types/float.md#data_type-float-nan-inf) 被返回。 这样做的目的是将这些案例与导致零的案例区分开来。 看 [按条款订购](../statements/select/order-by.md#select-order-by) 对于排序注意事项 `NaN` 值。 + 如果没有值传递给函数(当使用 `quantileTimingIf`), [NaN](../../sql-reference/data-types/float.md#data_type-float-nan-inf) 被返回。 这样做的目的是将这些案例与导致零的案例区分开来。 看 [ORDER BY clause](../statements/select/order-by.md#select-order-by) 对于 `NaN` 值排序注意事项。 **示例** @@ -1300,13 +1298,13 @@ SELECT quantileTiming(response_time) FROM t - [中位数](#median) - [分位数](#quantiles) -## 分位时间加权 {#quantiletimingweighted} +## quantileTimingWeighted {#quantiletimingweighted} -随着确定的精度计算 [分位数](https://en.wikipedia.org/wiki/Quantile) 根据每个序列成员的权重对数字数据序列进行处理。 +根据每个序列成员的权重,使用确定的精度计算数字序列的[分位数](https://en.wikipedia.org/wiki/Quantile)。 结果是确定性的(它不依赖于查询处理顺序)。 该函数针对描述加载网页时间或后端响应时间等分布的序列进行了优化。 -当使用多个 `quantile*` 在查询中具有不同级别的函数,内部状态不会被组合(即查询的工作效率低于它可以)。 在这种情况下,使用 [分位数](#quantiles) 功能。 +当在一个查询中使用多个不同层次的 `quantile*` 时,内部状态不会被组合(即查询的工作效率低于组合情况)。在这种情况下,使用[分位数](#quantiles)功能。 **语法** @@ -1318,14 +1316,14 @@ quantileTimingWeighted(level)(expr, weight) **参数** -- `level` — Level of quantile. Optional parameter. Constant floating-point number from 0 to 1. We recommend using a `level` 值的范围 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). +- `level` — 分位数层次。可选参数。 从0到1的一个float类型的常量。 我们推荐 `level` 值的范围为 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). -- `expr` — [表达式](../syntax.md#syntax-expressions) 在一个列值返回 [浮动\*](../../sql-reference/data-types/float.md)-键入号码。 +- `expr` — [表达式](../syntax.md#syntax-expressions),返回 [浮动\*](../../sql-reference/data-types/float.md)类型数据。 - - If negative values are passed to the function, the behavior is undefined. - - If the value is greater than 30,000 (a page loading time of more than 30 seconds), it is assumed to be 30,000. + - 如果输入负值,那结果是不可预期的。 + - 如果输入值大于30000(页面加载时间大于30s),那我们假设为30000。 -- `weight` — Column with weights of sequence elements. Weight is a number of value occurrences. +- `weight` — 权重序列。 权重是一个数据出现的数值。 **精度** @@ -1337,16 +1335,16 @@ quantileTimingWeighted(level)(expr, weight) 否则,计算结果将四舍五入到16毫秒的最接近倍数。 !!! note "注" - 对于计算页面加载时间分位数,此函数比 [分位数](#quantile). + 对于计算页面加载时间分位数,此函数比 [分位数](#quantile)更高效和准确。 **返回值** -- 指定电平的分位数。 +- 指定层次的分位数。 类型: `Float32`. !!! note "注" - 如果没有值传递给函数(当使用 `quantileTimingIf`), [阿南](../../sql-reference/data-types/float.md#data_type-float-nan-inf) 被返回。 这样做的目的是将这些案例与导致零的案例区分开来。 看 [按条款订购](../statements/select/order-by.md#select-order-by) 对于排序注意事项 `NaN` 值。 + 如果没有值传递给函数(当使用 `quantileTimingIf`), [NaN](../../sql-reference/data-types/float.md#data_type-float-nan-inf) 被返回。 这样做的目的是将这些案例与导致零的案例区分开来。看 [ORDER BY clause](../statements/select/order-by.md#select-order-by) 对于 `NaN` 值排序注意事项。 **示例** @@ -1384,13 +1382,13 @@ SELECT quantileTimingWeighted(response_time, weight) FROM t ## quantileTDigest {#quantiletdigest} -计算近似值 [分位数](https://en.wikipedia.org/wiki/Quantile) 使用的数字数据序列 [t-digest](https://github.com/tdunning/t-digest/blob/master/docs/t-digest-paper/histo.pdf) 算法。 +使用[t-digest](https://github.com/tdunning/t-digest/blob/master/docs/t-digest-paper/histo.pdf) 算法计算近似[分位数](https://en.wikipedia.org/wiki/Quantile)。 -最大误差为1%。 内存消耗 `log(n)`,哪里 `n` 是多个值。 结果取决于运行查询的顺序,并且是不确定的。 +最大误差为1%。 内存消耗 `log(n)`,这里 `n` 是值的个数。 结果取决于运行查询的顺序,并且是不确定的。 -该功能的性能低于性能 [分位数](#quantile) 或 [分位定时](#quantiletiming). 在状态大小与精度的比率方面,这个函数比 `quantile`. +该功能的性能低于性能 [分位数](#quantile) 或 [时间分位](#quantiletiming). 在状态大小与精度的比率方面,这个函数比 `quantile`更优秀。 -当使用多个 `quantile*` 在查询中具有不同级别的函数,内部状态不会被组合(即查询的工作效率低于它可以)。 在这种情况下,使用 [分位数](#quantiles) 功能。 +当在一个查询中使用多个不同层次的 `quantile*` 时,内部状态不会被组合(即查询的工作效率低于组合情况)。在这种情况下,使用[分位数](#quantiles)功能。 **语法** @@ -1402,12 +1400,12 @@ quantileTDigest(level)(expr) **参数** -- `level` — Level of quantile. Optional parameter. Constant floating-point number from 0 to 1. We recommend using a `level` 值的范围 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). -- `expr` — Expression over the column values resulting in numeric [数据类型](../../sql-reference/data-types/index.md#data_types), [日期](../../sql-reference/data-types/date.md) 或 [日期时间](../../sql-reference/data-types/datetime.md). +- `level` — 分位数层次。可选参数。 从0到1的一个float类型的常量。 我们推荐 `level` 值的范围为 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). +- `expr` — 求职表达式,类型为:数值[数据类型](../../sql-reference/data-types/index.md#data_types),[日期](../../sql-reference/data-types/date.md)数据类型或[时间](../../sql-reference/data-types/datetime.md)数据类型。 **回值** -- 指定电平的近似分位数。 +- 指定层次的分位数。 类型: @@ -1438,13 +1436,13 @@ SELECT quantileTDigest(number) FROM numbers(10) ## quantileTDigestWeighted {#quantiletdigestweighted} -计算近似值 [分位数](https://en.wikipedia.org/wiki/Quantile) 使用的数字数据序列 [t-digest](https://github.com/tdunning/t-digest/blob/master/docs/t-digest-paper/histo.pdf) 算法。 该函数考虑了每个序列成员的权重。 最大误差为1%。 内存消耗 `log(n)`,哪里 `n` 是多个值。 +使用[t-digest](https://github.com/tdunning/t-digest/blob/master/docs/t-digest-paper/histo.pdf) 算法计算近似[分位数](https://en.wikipedia.org/wiki/Quantile)。 该函数考虑了每个序列成员的权重。最大误差为1%。 内存消耗 `log(n)`,这里 `n` 是值的个数。 -该功能的性能低于性能 [分位数](#quantile) 或 [分位定时](#quantiletiming). 在状态大小与精度的比率方面,这个函数比 `quantile`. +该功能的性能低于性能 [分位数](#quantile) 或 [时间分位](#quantiletiming). 在状态大小与精度的比率方面,这个函数比 `quantile`更优秀。 结果取决于运行查询的顺序,并且是不确定的。 -当使用多个 `quantile*` 在查询中具有不同级别的函数,内部状态不会被组合(即查询的工作效率低于它可以)。 在这种情况下,使用 [分位数](#quantiles) 功能。 +当在一个查询中使用多个不同层次的 `quantile*` 时,内部状态不会被组合(即查询的工作效率低于组合情况)。在这种情况下,使用[分位数](#quantiles)功能 **语法** @@ -1456,13 +1454,13 @@ quantileTDigest(level)(expr) **参数** -- `level` — Level of quantile. Optional parameter. Constant floating-point number from 0 to 1. We recommend using a `level` 值的范围 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). -- `expr` — Expression over the column values resulting in numeric [数据类型](../../sql-reference/data-types/index.md#data_types), [日期](../../sql-reference/data-types/date.md) 或 [日期时间](../../sql-reference/data-types/datetime.md). -- `weight` — Column with weights of sequence elements. Weight is a number of value occurrences. +- `level` — 分位数层次。可选参数。 从0到1的一个float类型的常量。 我们推荐 `level` 值的范围为 `[0.01, 0.99]`. 默认值:0.5。 在 `level=0.5` 该函数计算 [中位数](https://en.wikipedia.org/wiki/Median). +- `expr` — 求职表达式,类型为:数值[数据类型](../../sql-reference/data-types/index.md#data_types),[日期](../../sql-reference/data-types/date.md)数据类型或[时间](../../sql-reference/data-types/datetime.md)数据类型。 +- `weight` — 权重序列。 权重是一个数据出现的数值。 **返回值** -- 指定电平的近似分位数。 +- 指定层次的分位数。 类型: @@ -1491,20 +1489,20 @@ SELECT quantileTDigestWeighted(number, 1) FROM numbers(10) - [中位数](#median) - [分位数](#quantiles) -## 中位数 {#median} +## median {#median} -该 `median*` 函数是相应的别名 `quantile*` 功能。 它们计算数字数据样本的中位数。 +`median*` 函数是 `quantile*` 函数的别名。 它们计算数字数据样本的中位数。 -功能: +函数: -- `median` — Alias for [分位数](#quantile). -- `medianDeterministic` — Alias for [量化确定](#quantiledeterministic). -- `medianExact` — Alias for [quantileExact](#quantileexact). -- `medianExactWeighted` — Alias for [分位数加权](#quantileexactweighted). -- `medianTiming` — Alias for [分位定时](#quantiletiming). -- `medianTimingWeighted` — Alias for [分位时间加权](#quantiletimingweighted). -- `medianTDigest` — Alias for [quantileTDigest](#quantiletdigest). -- `medianTDigestWeighted` — Alias for [quantileTDigestWeighted](#quantiletdigestweighted). +- `median` — [quantile](#quantile)别名。 +- `medianDeterministic` — [quantileDeterministic](#quantiledeterministic)别名。 +- `medianExact` — [quantileExact](#quantileexact)别名。 +- `medianExactWeighted` — [quantileExactWeighted](#quantileexactweighted)别名。 +- `medianTiming` — [quantileTiming](#quantiletiming)别名。 +- `medianTimingWeighted` — [quantileTimingWeighted](#quantiletimingweighted)别名。 +- `medianTDigest` — [quantileTDigest](#quantiletdigest)别名。 +- `medianTDigestWeighted` — [quantileTDigestWeighted](#quantiletdigestweighted)别名。 **示例** @@ -1535,11 +1533,11 @@ SELECT medianDeterministic(val, 1) FROM t ## quantiles(level1, level2, …)(x) {#quantiles} -所有分位数函数也具有相应的分位数函数: `quantiles`, `quantilesDeterministic`, `quantilesTiming`, `quantilesTimingWeighted`, `quantilesExact`, `quantilesExactWeighted`, `quantilesTDigest`. 这些函数在一遍中计算所列电平的所有分位数,并返回结果值的数组。 +所有分位数函数也有相应的函数: `quantiles`, `quantilesDeterministic`, `quantilesTiming`, `quantilesTimingWeighted`, `quantilesExact`, `quantilesExactWeighted`, `quantilesTDigest`。这些函数一次计算所列层次的所有分位数,并返回结果值的数组。 ## varSamp(x) {#varsampx} -计算金额 `Σ((x - x̅)^2) / (n - 1)`,哪里 `n` 是样本大小和 `x̅`是平均值 `x`. +计算 `Σ((x - x̅)^2) / (n - 1)`,这里 `n` 是样本大小, `x̅`是`x`的平均值。 它表示随机变量的方差的无偏估计,如果传递的值形成其样本。 @@ -1550,23 +1548,23 @@ SELECT medianDeterministic(val, 1) FROM t ## varPop(x) {#varpopx} -计算金额 `Σ((x - x̅)^2) / n`,哪里 `n` 是样本大小和 `x̅`是平均值 `x`. +计算 `Σ((x - x̅)^2) / n`,这里 `n` 是样本大小, `x̅`是`x`的平均值。 -换句话说,分散为一组值。 返回 `Float64`. +换句话说,计算一组数据的离差。 返回 `Float64`。 !!! note "注" 该函数使用数值不稳定的算法。 如果你需要 [数值稳定性](https://en.wikipedia.org/wiki/Numerical_stability) 在计算中,使用 `varPopStable` 功能。 它的工作速度较慢,但提供较低的计算错误。 ## stddevSamp(x) {#stddevsampx} -结果等于平方根 `varSamp(x)`. +结果等于平方根 `varSamp(x)`。 !!! note "注" 该函数使用数值不稳定的算法。 如果你需要 [数值稳定性](https://en.wikipedia.org/wiki/Numerical_stability) 在计算中,使用 `stddevSampStable` 功能。 它的工作速度较慢,但提供较低的计算错误。 ## stddevPop(x) {#stddevpopx} -结果等于平方根 `varPop(x)`. +结果等于平方根 `varPop(x)`。 !!! note "注" 该函数使用数值不稳定的算法。 如果你需要 [数值稳定性](https://en.wikipedia.org/wiki/Numerical_stability) 在计算中,使用 `stddevPopStable` 功能。 它的工作速度较慢,但提供较低的计算错误。 @@ -1575,15 +1573,15 @@ SELECT medianDeterministic(val, 1) FROM t 返回指定列中近似最常见值的数组。 生成的数组按值的近似频率降序排序(而不是值本身)。 -实现了 [过滤节省空间](http://www.l2f.inesc-id.pt/~fmmb/wiki/uploads/Work/misnis.ref0a.pdf) 基于reduce-and-combine算法的TopK分析算法 [并行节省空间](https://arxiv.org/pdf/1401.0702.pdf). +实现了[过滤节省空间](http://www.l2f.inesc-id.pt/~fmmb/wiki/uploads/Work/misnis.ref0a.pdf)算法, 使用基于reduce-and-combine的算法,借鉴[并行节省空间](https://arxiv.org/pdf/1401.0702.pdf). ``` sql topK(N)(column) ``` -此函数不提供保证的结果。 在某些情况下,可能会发生错误,并且可能会返回不是最常见值的常见值。 +此函数不提供保证的结果。 在某些情况下,可能会发生错误,并且可能会返回不是最高频的值。 -我们建议使用 `N < 10` 值;性能降低了大 `N` 值。 的最大值 `N = 65536`. +我们建议使用 `N < 10` 值,`N` 值越大,性能越低。最大值 `N = 65536`。 **参数** @@ -1593,11 +1591,11 @@ topK(N)(column) **参数** -- ' x ' – The value to calculate frequency. +- ' x ' – 计算的频率值。 **示例** -就拿 [时间](../../getting-started/example-datasets/ontime.md) 数据集,并选择在三个最频繁出现的值 `AirlineID` 列。 +就拿 [OnTime](../../getting-started/example-datasets/ontime.md) 数据集来说,选择`AirlineID` 列中出现最频繁的三个。 ``` sql SELECT topK(3)(AirlineID) AS res @@ -1612,7 +1610,7 @@ FROM ontime ## topKWeighted {#topkweighted} -类似于 `topK` 但需要一个整数类型的附加参数 - `weight`. 每个价值都被记入 `weight` 次频率计算。 +类似于 `topK` 但需要一个整数类型的附加参数 - `weight`. 每个输入都被记入 `weight` 次频率计算。 **语法** @@ -1622,12 +1620,12 @@ topKWeighted(N)(x, weight) **参数** -- `N` — The number of elements to return. +- `N` — 返回值个数。 **参数** -- `x` – The value. -- `weight` — The weight. [UInt8](../../sql-reference/data-types/int-uint.md). +- `x` – 输入值。 +- `weight` — 权重。 [UInt8](../../sql-reference/data-types/int-uint.md)类型。 **返回值** @@ -1651,36 +1649,36 @@ SELECT topKWeighted(10)(number, number) FROM numbers(1000) ## covarSamp(x,y) {#covarsampx-y} -计算的值 `Σ((x - x̅)(y - y̅)) / (n - 1)`. +计算 `Σ((x - x̅)(y - y̅)) / (n - 1)`。 -返回Float64。 当 `n <= 1`, returns +∞. +返回Float64。 当 `n <= 1`, returns +∞。 !!! note "注" 该函数使用数值不稳定的算法。 如果你需要 [数值稳定性](https://en.wikipedia.org/wiki/Numerical_stability) 在计算中,使用 `covarSampStable` 功能。 它的工作速度较慢,但提供较低的计算错误。 ## covarPop(x,y) {#covarpopx-y} -计算的值 `Σ((x - x̅)(y - y̅)) / n`. +计算 `Σ((x - x̅)(y - y̅)) / n`。 !!! note "注" 该函数使用数值不稳定的算法。 如果你需要 [数值稳定性](https://en.wikipedia.org/wiki/Numerical_stability) 在计算中,使用 `covarPopStable` 功能。 它的工作速度较慢,但提供了较低的计算错误。 ## corr(x,y) {#corrx-y} -计算Pearson相关系数: `Σ((x - x̅)(y - y̅)) / sqrt(Σ((x - x̅)^2) * Σ((y - y̅)^2))`. +计算Pearson相关系数: `Σ((x - x̅)(y - y̅)) / sqrt(Σ((x - x̅)^2) * Σ((y - y̅)^2))`。 !!! note "注" 该函数使用数值不稳定的算法。 如果你需要 [数值稳定性](https://en.wikipedia.org/wiki/Numerical_stability) 在计算中,使用 `corrStable` 功能。 它的工作速度较慢,但提供较低的计算错误。 ## categoricalInformationValue {#categoricalinformationvalue} -计算的值 `(P(tag = 1) - P(tag = 0))(log(P(tag = 1)) - log(P(tag = 0)))` 对于每个类别。 +对于每个类别计算 `(P(tag = 1) - P(tag = 0))(log(P(tag = 1)) - log(P(tag = 0)))` 。 ``` sql categoricalInformationValue(category1, category2, ..., tag) ``` -结果指示离散(分类)要素如何使用 `[category1, category2, ...]` 有助于预测的价值的学习模型 `tag`. +结果指示离散(分类)要素如何使用 `[category1, category2, ...]` 有助于使用学习模型预测`tag`的值。 ## simpleLinearRegression {#simplelinearregression} @@ -1692,12 +1690,12 @@ simpleLinearRegression(x, y) 参数: -- `x` — Column with dependent variable values. -- `y` — Column with explanatory variable values. +- `x` — x轴。 +- `y` — y轴。 返回值: -常量 `(a, b)` 结果行的 `y = a*x + b`. +符合`y = a*x + b`的常量 `(a, b)` 。 **例** @@ -1721,9 +1719,9 @@ SELECT arrayReduce('simpleLinearRegression', [0, 1, 2, 3], [3, 4, 5, 6]) └───────────────────────────────────────────────────────────────────┘ ``` -## 随机指标线上回归 {#agg_functions-stochasticlinearregression} +## stochasticLinearRegression {#agg_functions-stochasticlinearregression} -该函数实现随机线性回归。 它支持自定义参数的学习率,L2正则化系数,迷你批量大小,并具有更新权重的方法很少 ([亚当](https://en.wikipedia.org/wiki/Stochastic_gradient_descent#Adam) (默认使用), [简单SGD](https://en.wikipedia.org/wiki/Stochastic_gradient_descent), [动量](https://en.wikipedia.org/wiki/Stochastic_gradient_descent#Momentum), [Nesterov](https://mipt.ru/upload/medialibrary/d7e/41-91.pdf)). +该函数实现随机线性回归。 它支持自定义参数的学习率、L2正则化系数、微批,并且具有少量更新权重的方法([Adam](https://en.wikipedia.org/wiki/Stochastic_gradient_descent#Adam) (默认), [simple SGD](https://en.wikipedia.org/wiki/Stochastic_gradient_descent), [Momentum](https://en.wikipedia.org/wiki/Stochastic_gradient_descent#Momentum), [Nesterov](https://mipt.ru/upload/medialibrary/d7e/41-91.pdf))。 ### 参数 {#agg_functions-stochasticlinearregression-parameters} @@ -1738,14 +1736,14 @@ stochasticLinearRegression(1.0, 1.0, 10, 'SGD') 3. `mini-batch size` 设置元素的数量,这些元素将被计算和求和以执行梯度下降的一个步骤。 纯随机下降使用一个元素,但是具有小批量(约10个元素)使梯度步骤更稳定。 默认值为 `15`. 4. `method for updating weights` 他们是: `Adam` (默认情况下), `SGD`, `Momentum`, `Nesterov`. `Momentum` 和 `Nesterov` 需要更多的计算和内存,但是它们恰好在收敛速度和随机梯度方法的稳定性方面是有用的。 -### 用途 {#agg_functions-stochasticlinearregression-usage} +### 用法 {#agg_functions-stochasticlinearregression-usage} `stochasticLinearRegression` 用于两个步骤:拟合模型和预测新数据。 为了拟合模型并保存其状态以供以后使用,我们使用 `-State` combinator,它基本上保存了状态(模型权重等)。 为了预测我们使用函数 [evalMLMethod](../functions/machine-learning-functions.md#machine_learning_methods-evalmlmethod),这需要一个状态作为参数以及特征来预测。 -**1.** 适合 +**1.** 安装 可以使用这种查询。 @@ -1807,28 +1805,28 @@ evalMLMethod(model, param1, param2) FROM test_data stochasticLogisticRegression(1.0, 1.0, 10, 'SGD') ``` -1. 适合 +**1.** 安装 - See the `Fitting` section in the [stochasticLinearRegression](#stochasticlinearregression-usage-fitting) description. + 参考stochasticLinearRegression相关文档 - Predicted labels have to be in \[-1, 1\]. + 预测标签的取值范围为[-1, 1] -1. 预测 +**2.** 预测 - Using saved state we can predict probability of object having label `1`. + 使用已经保存的state我们可以预测标签为 `1` 的对象的概率。 ``` sql WITH (SELECT state FROM your_model) AS model SELECT evalMLMethod(model, param1, param2) FROM test_data ``` - The query will return a column of probabilities. Note that first argument of `evalMLMethod` is `AggregateFunctionState` object, next are columns of features. + 查询结果返回一个列的概率。注意 `evalMLMethod` 的第一个参数是 `AggregateFunctionState` 对象,接下来的参数是列的特性。 - We can also set a bound of probability, which assigns elements to different labels. + 我们也可以设置概率的范围, 这样需要给元素指定不同的标签。 ``` sql SELECT ans < 1.1 AND ans > 0.5 FROM @@ -1836,14 +1834,14 @@ stochasticLogisticRegression(1.0, 1.0, 10, 'SGD') evalMLMethod(model, param1, param2) AS ans FROM test_data) ``` - Then the result will be labels. + 结果是标签。 - `test_data` is a table like `train_data` but may not contain target value. + `test_data` 是一个像 `train_data` 一样的表,但是不包含目标值。 **另请参阅** - [随机指标线上回归](#agg_functions-stochasticlinearregression) -- [线性回归和逻辑回归之间的差异。](https://stackoverflow.com/questions/12146914/what-is-the-difference-between-linear-regression-and-logistic-regression) +- [线性回归和逻辑回归之间的差异](https://stackoverflow.com/questions/12146914/what-is-the-difference-between-linear-regression-and-logistic-regression) ## groupBitmapAnd {#groupbitmapand} diff --git a/docs/zh/sql-reference/data-types/lowcardinality.md b/docs/zh/sql-reference/data-types/lowcardinality.md new file mode 100644 index 00000000000..b8985691f0f --- /dev/null +++ b/docs/zh/sql-reference/data-types/lowcardinality.md @@ -0,0 +1,59 @@ +--- +toc_priority: 51 +toc_title: 低基数类型 +--- + +# 低基数类型 {#lowcardinality-data-type} + +把其它数据类型转变为字典编码类型。 + +## 语法 {#lowcardinality-syntax} + +```sql +LowCardinality(data_type) +``` + +**参数** + +- `data_type` — [String](string.md), [FixedString](fixedstring.md), [Date](date.md), [DateTime](datetime.md),包括数字类型,但是[Decimal](decimal.md)除外。对一些数据类型来说,`LowCardinality` 并不高效,详查[allow_suspicious_low_cardinality_types](../../operations/settings/settings.md#allow_suspicious_low_cardinality_types)设置描述。 + +## 描述 {#lowcardinality-dscr} + +`LowCardinality` 是一种改变数据存储和数据处理方法的概念。 ClickHouse会把 `LowCardinality` 所在的列进行[dictionary coding](https://en.wikipedia.org/wiki/Dictionary_coder)。对很多应用来说,处理字典编码的数据可以显著的增加[SELECT](../statements/select/index.md)查询速度。 + +使用 `LowCarditality` 数据类型的效率依赖于数据的多样性。如果一个字典包含少于10000个不同的值,那么ClickHouse可以进行更高效的数据存储和处理。反之如果字典多于10000,效率会表现的更差。 + +当使用字符类型的时候,可以考虑使用 `LowCardinality` 代替[Enum](enum.md)。 `LowCardinality` 通常更加灵活和高效。 + +## 例子 + +创建一个 `LowCardinality` 类型的列: + +```sql +CREATE TABLE lc_t +( + `id` UInt16, + `strings` LowCardinality(String) +) +ENGINE = MergeTree() +ORDER BY id +``` + +## 相关的设置和函数 + +设置: + +- [low_cardinality_max_dictionary_size](../../operations/settings/settings.md#low_cardinality_max_dictionary_size) +- [low_cardinality_use_single_dictionary_for_part](../../operations/settings/settings.md#low_cardinality_use_single_dictionary_for_part) +- [low_cardinality_allow_in_native_format](../../operations/settings/settings.md#low_cardinality_allow_in_native_format) +- [allow_suspicious_low_cardinality_types](../../operations/settings/settings.md#allow_suspicious_low_cardinality_types) + +函数: + +- [toLowCardinality](../functions/type-conversion-functions.md#tolowcardinality) + +## 参考 + +- [高效低基数类型](https://www.altinity.com/blog/2019/3/27/low-cardinality). +- [使用低基数类型减少ClickHouse的存储成本 – 来自Instana工程师的分享](https://www.instana.com/blog/reducing-clickhouse-storage-cost-with-the-low-cardinality-type-lessons-from-an-instana-engineer/). +- [字符优化 (俄语视频分享)](https://youtu.be/rqf-ILRgBdY?list=PL0Z2YDlm0b3iwXCpEFiOOYmwXzVmjJfEt). [英语分享](https://github.com/yandex/clickhouse-presentations/raw/master/meetup19/string_optimization.pdf). \ No newline at end of file diff --git a/docs/zh/sql-reference/functions/arithmetic-functions.md b/docs/zh/sql-reference/functions/arithmetic-functions.md index 1c2ed3fccfc..b7cfa87ef94 100644 --- a/docs/zh/sql-reference/functions/arithmetic-functions.md +++ b/docs/zh/sql-reference/functions/arithmetic-functions.md @@ -1,3 +1,8 @@ +--- +toc_priority: 35 +toc_title: 算术函数 +--- + # 算术函数 {#suan-zhu-han-shu} 对于所有算术函数,结果类型为结果适合的最小数字类型(如果存在这样的类型)。最小数字类型是根据数字的位数,是否有符号以及是否是浮点类型而同时进行的。如果没有足够的位,则采用最高位类型。 diff --git a/docs/zh/sql-reference/functions/comparison-functions.md b/docs/zh/sql-reference/functions/comparison-functions.md index a3da3a3047a..8b5d72e64c2 100644 --- a/docs/zh/sql-reference/functions/comparison-functions.md +++ b/docs/zh/sql-reference/functions/comparison-functions.md @@ -1,3 +1,8 @@ +--- +toc_priority: 36 +toc_title: 比较函数 +--- + # 比较函数 {#bi-jiao-han-shu} 比较函数始终返回0或1(UInt8)。 @@ -15,18 +20,16 @@ 字符串按字节进行比较。较短的字符串小于以其开头并且至少包含一个字符的所有字符串。 -注意。直到1.1.54134版本,有符号和无符号数字的比较方式与C++相同。换句话说,在SELECT 9223372036854775807 &gt; -1 等情况下,您可能会得到错误的结果。 此行为在版本1.1.54134中已更改,现在在数学上是正确的。 +## 等于,a=b和a==b 运算符 {#equals-a-b-and-a-b-operator} -## 等于,a=b和a==b运算符 {#equals-a-b-and-a-b-operator} +## 不等于,a!=b和a<>b 运算符 {#notequals-a-operator-b-and-a-b} -## notEquals,a! 运算符=b和a `<>` b {#notequals-a-operator-b-and-a-b} +## 少, < 运算符 {#less-operator} -## 少, `< operator` {#less-operator} +## 大于, > 运算符 {#greater-operator} -## 更大, `> operator` {#greater-operator} +## 小于等于, <= 运算符 {#lessorequals-operator} -## 出租等级, `<= operator` {#lessorequals-operator} - -## 伟大的等级, `>= operator` {#greaterorequals-operator} +## 大于等于, >= 运算符 {#greaterorequals-operator} [来源文章](https://clickhouse.tech/docs/en/query_language/functions/comparison_functions/) diff --git a/docs/zh/sql-reference/functions/date-time-functions.md b/docs/zh/sql-reference/functions/date-time-functions.md index 3fbe272ebe8..a418379d4ec 100644 --- a/docs/zh/sql-reference/functions/date-time-functions.md +++ b/docs/zh/sql-reference/functions/date-time-functions.md @@ -22,11 +22,11 @@ SELECT 将Date或DateTime转换为指定的时区。 -## 玩一年 {#toyear} +## toYear {#toyear} 将Date或DateTime转换为包含年份编号(AD)的UInt16类型的数字。 -## 到四分钟 {#toquarter} +## toQuarter {#toquarter} 将Date或DateTime转换为包含季度编号的UInt8类型的数字。 diff --git a/docs/zh/sql-reference/functions/index.md b/docs/zh/sql-reference/functions/index.md index 8d178592e92..52954c95cff 100644 --- a/docs/zh/sql-reference/functions/index.md +++ b/docs/zh/sql-reference/functions/index.md @@ -1,3 +1,9 @@ +--- +toc_folder_title: 函数 +toc_priority: 32 +toc_title: 简介 +--- + # 函数 {#han-shu} ClickHouse中至少存在两种类型的函数 - 常规函数(它们称之为«函数»)和聚合函数。 常规函数的工作就像分别为每一行执行一次函数计算一样(对于每一行,函数的结果不依赖于其他行)。 聚合函数则从各行累积一组值(即函数的结果以来整个结果集)。 diff --git a/docs/zh/sql-reference/functions/logical-functions.md b/docs/zh/sql-reference/functions/logical-functions.md index b14f1bb5d37..cc168dbb1ed 100644 --- a/docs/zh/sql-reference/functions/logical-functions.md +++ b/docs/zh/sql-reference/functions/logical-functions.md @@ -1,15 +1,20 @@ +--- +toc_priority: 37 +toc_title: 逻辑函数 +--- + # 逻辑函数 {#luo-ji-han-shu} 逻辑函数可以接受任何数字类型的参数,并返回UInt8类型的0或1。 当向函数传递零时,函数将判定为«false»,否则,任何其他非零的值都将被判定为«true»。 -## 和,和运营商 {#and-and-operator} +## 和,`AND` 运算符 {#and-and-operator} -## 或,或运营商 {#or-or-operator} +## 或,`OR` 运算符 {#or-or-operator} -## 不是,不是运营商 {#not-not-operator} +## 非,`NOT` 运算符 {#not-not-operator} -## 异或 {#xor} +## 异或,`XOR` 运算符 {#xor} [来源文章](https://clickhouse.tech/docs/en/query_language/functions/logical_functions/) diff --git a/docs/zh/sql-reference/functions/type-conversion-functions.md b/docs/zh/sql-reference/functions/type-conversion-functions.md index 011b2951e74..6e3ed9afb78 100644 --- a/docs/zh/sql-reference/functions/type-conversion-functions.md +++ b/docs/zh/sql-reference/functions/type-conversion-functions.md @@ -1,16 +1,230 @@ +--- +toc_priority: 38 +toc_title: 类型转换函数 +--- + # 类型转换函数 {#lei-xing-zhuan-huan-han-shu} -## toUInt8,toUInt16,toUInt32,toUInt64 {#touint8-touint16-touint32-touint64} +## 数值类型转换常见的问题 {#numeric-conversion-issues} -## toInt8,toInt16,toInt32,toInt64 {#toint8-toint16-toint32-toint64} +当你把一个值从一个类型转换为另外一个类型的时候,你需要注意的是这是一个不安全的操作,可能导致数据的丢失。数据丢失一般发生在你将一个大的数据类型转换为小的数据类型的时候,或者你把两个不同的数据类型相互转换的时候。 -## toFloat32,toFloat64 {#tofloat32-tofloat64} +ClickHouse和[C++](https://en.cppreference.com/w/cpp/language/implicit_conversion)有相同的类型转换行为。 -## 今天,今天 {#todate-todatetime} +## toInt(8\|16\|32\|64) {#touint8-touint16-touint32-touint64} -## toUInt8OrZero,toUInt16OrZero,toUInt32OrZero,toUInt64OrZero,toInt8OrZero,toInt16OrZero,toInt32OrZero,toInt64OrZero,toFloat32OrZero,toFloat64OrZero,toDateOrZero,toDateTimeOrZero {#touint8orzero-touint16orzero-touint32orzero-touint64orzero-toint8orzero-toint16orzero-toint32orzero-toint64orzero-tofloat32orzero-tofloat64orzero-todateorzero-todatetimeorzero} +转换一个输入值为[Int](../../sql-reference/data-types/int-uint.md)类型。这个函数包括: + +- `toInt8(expr)` — 结果为`Int8`数据类型。 +- `toInt16(expr)` — 结果为`Int16`数据类型。 +- `toInt32(expr)` — 结果为`Int32`数据类型。 +- `toInt64(expr)` — 结果为`Int64`数据类型。 + +**参数** + +- `expr` — [表达式](../syntax.md#syntax-expressions)返回一个数字或者代表数值类型的字符串。不支持二进制、八进制、十六进制的数字形式,有效数字之前的0也会被忽略。 + +**返回值** + +整形在`Int8`, `Int16`, `Int32`,或者 `Int64` 的数据类型。 + +函数使用[rounding towards zero](https://en.wikipedia.org/wiki/Rounding#Rounding_towards_zero)原则,这意味着会截断丢弃小数部分的数值。 + +[NaN and Inf](../../sql-reference/data-types/float.md#data_type-float-nan-inf)转换是不确定的。具体使用的时候,请参考[数值类型转换常见的问题](#numeric-conversion-issues)。 + +**例子** + +``` sql +SELECT toInt64(nan), toInt32(32), toInt16('16'), toInt8(8.8) +``` + +``` text +┌─────────toInt64(nan)─┬─toInt32(32)─┬─toInt16('16')─┬─toInt8(8.8)─┐ +│ -9223372036854775808 │ 32 │ 16 │ 8 │ +└──────────────────────┴─────────────┴───────────────┴─────────────┘ +``` + +## toInt(8\|16\|32\|64)OrZero {#toint8163264orzero} + +这个函数需要一个字符类型的入参,然后尝试把它转为`Int (8 | 16 | 32 | 64)`,如果转换失败直接返回0。 + +**例子** + +``` sql +select toInt64OrZero('123123'), toInt8OrZero('123qwe123') +``` + +``` text +┌─toInt64OrZero('123123')─┬─toInt8OrZero('123qwe123')─┐ +│ 123123 │ 0 │ +└─────────────────────────┴───────────────────────────┘ +``` +## toInt(8\|16\|32\|64)OrNull {#toint8163264ornull} + +这个函数需要一个字符类型的入参,然后尝试把它转为`Int (8 | 16 | 32 | 64)`,如果转换失败直接返回`NULL`。 + +**例子** + +``` sql +select toInt64OrNull('123123'), toInt8OrNull('123qwe123') +``` + +``` text +┌─toInt64OrNull('123123')─┬─toInt8OrNull('123qwe123')─┐ +│ 123123 │ ᴺᵁᴸᴸ │ +└─────────────────────────┴───────────────────────────┘ +``` + +## toUInt(8\|16\|32\|64) {#touint8163264} + +转换一个输入值到[UInt](../../sql-reference/data-types/int-uint.md)类型。 这个函数包括: + +- `toUInt8(expr)` — 结果为`UInt8`数据类型。 +- `toUInt16(expr)` — 结果为`UInt16`数据类型。 +- `toUInt32(expr)` — 结果为`UInt32`数据类型。 +- `toUInt64(expr)` — 结果为`UInt64`数据类型。 + +**参数** + +- `expr` — [表达式](../syntax.md#syntax-expressions)返回一个数字或者代表数值类型的字符串。不支持二进制、八进制、十六进制的数字形式,有效数字之前的0也会被忽略。 + +**返回值** + +整形在`UInt8`, `UInt16`, `UInt32`,或者 `UInt64` 的数据类型。 + +函数使用[rounding towards zero](https://en.wikipedia.org/wiki/Rounding#Rounding_towards_zero)原则,这意味着会截断丢弃小数部分的数值。 + +对于负数和[NaN and Inf](../../sql-reference/data-types/float.md#data_type-float-nan-inf)来说转换的结果是不确定的。如果你传入一个负数,比如:`'-32'`,ClickHouse会抛出异常。具体使用的时候,请参考[数值类型转换常见的问题](#numeric-conversion-issues)。 + +**例子** + +``` sql +SELECT toUInt64(nan), toUInt32(-32), toUInt16('16'), toUInt8(8.8) +``` + +``` text +┌───────toUInt64(nan)─┬─toUInt32(-32)─┬─toUInt16('16')─┬─toUInt8(8.8)─┐ +│ 9223372036854775808 │ 4294967264 │ 16 │ 8 │ +└─────────────────────┴───────────────┴────────────────┴──────────────┘ +``` + +## toUInt(8\|16\|32\|64)OrZero {#touint8163264orzero} + +## toUInt(8\|16\|32\|64)OrNull {#touint8163264ornull} + +## toFloat(32\|64) {#tofloat3264} + +## toFloat(32\|64)OrZero {#tofloat3264orzero} + +## toFloat(32\|64)OrNull {#tofloat3264ornull} + +## toDate {#todate} + +## toDateOrZero {#todateorzero} + +## toDateOrNull {#todateornull} + +## toDateTime {#todatetime} + +## toDateTimeOrZero {#todatetimeorzero} + +## toDateTimeOrNull {#todatetimeornull} + +## toDecimal(32\|64\|128) {#todecimal3264128} + +转换 `value` 到[Decimal](../../sql-reference/data-types/decimal.md)类型的值,其中精度为`S`。`value`可以是一个数字或者一个字符串。`S` 指定小数位的精度。 + +- `toDecimal32(value, S)` +- `toDecimal64(value, S)` +- `toDecimal128(value, S)` + +## toDecimal(32\|64\|128)OrNull {#todecimal3264128ornull} + +转换一个输入的字符到[Nullable(Decimal(P,S))](../../sql-reference/data-types/decimal.md)类型的数据。这个函数包括: + +- `toDecimal32OrNull(expr, S)` — 结果为`Nullable(Decimal32(S))`数据类型。 +- `toDecimal64OrNull(expr, S)` — 结果为`Nullable(Decimal64(S))`数据类型。 +- `toDecimal128OrNull(expr, S)` — 结果为`Nullable(Decimal128(S))`数据类型。 + +如果在解析输入值发生错误的时候你希望得到一个`NULL`值而不是抛出异常,你可以使用该函数。 + +**参数** + +- `expr` — [表达式](../syntax.md#syntax-expressions)返回一个[String](../../sql-reference/data-types/string.md)类型的数据。 ClickHouse倾向于文本类型的表示带小数类型的数值,比如`'1.111'`。 +- `S` — 小数位的精度。 + +**返回值** + +`Nullable(Decimal(P,S))`类型的数据,包括: + +- 如果有的话,小数位`S`。 +- 如果解析错误或者输入的数字的小数位多于`S`,那结果为`NULL`。 + +**例子** + +``` sql +SELECT toDecimal32OrNull(toString(-1.111), 5) AS val, toTypeName(val) +``` + +``` text +┌──────val─┬─toTypeName(toDecimal32OrNull(toString(-1.111), 5))─┐ +│ -1.11100 │ Nullable(Decimal(9, 5)) │ +└──────────┴────────────────────────────────────────────────────┘ +``` + +``` sql +SELECT toDecimal32OrNull(toString(-1.111), 2) AS val, toTypeName(val) +``` + +``` text +┌──val─┬─toTypeName(toDecimal32OrNull(toString(-1.111), 2))─┐ +│ ᴺᵁᴸᴸ │ Nullable(Decimal(9, 2)) │ +└──────┴────────────────────────────────────────────────────┘ +``` + +## toDecimal(32\|64\|128)OrZero {#todecimal3264128orzero} + +转换输入值为[Decimal(P,S)](../../sql-reference/data-types/decimal.md)类型数据。这个函数包括: + +- `toDecimal32OrZero( expr, S)` — 结果为`Decimal32(S)` 数据类型。 +- `toDecimal64OrZero( expr, S)` — 结果为`Decimal64(S)` 数据类型。 +- `toDecimal128OrZero( expr, S)` — 结果为`Decimal128(S)` 数据类型。 + +当解析错误的时候,你不需要抛出异常而希望得到`0`值,你可以使用该函数。 + +**参数** + +- `expr` — [表达式](../syntax.md#syntax-expressions)返回一个[String](../../sql-reference/data-types/string.md)类型的数据。 ClickHouse倾向于文本类型的表示带小数类型的数值,比如`'1.111'`。 +- `S` — 小数位的精度。 + +**返回值** + +A value in the `Nullable(Decimal(P,S))` data type. The value contains: + +- 如果有的话,小数位`S`。 +- 如果解析错误或者输入的数字的小数位多于`S`,那结果为小数位精度为`S`的`0`。 +**例子** + +``` sql +SELECT toDecimal32OrZero(toString(-1.111), 5) AS val, toTypeName(val) +``` + +``` text +┌──────val─┬─toTypeName(toDecimal32OrZero(toString(-1.111), 5))─┐ +│ -1.11100 │ Decimal(9, 5) │ +└──────────┴────────────────────────────────────────────────────┘ +``` + +``` sql +SELECT toDecimal32OrZero(toString(-1.111), 2) AS val, toTypeName(val) +``` + +``` text +┌──val─┬─toTypeName(toDecimal32OrZero(toString(-1.111), 2))─┐ +│ 0.00 │ Decimal(9, 2) │ +└──────┴────────────────────────────────────────────────────┘ +``` -## toUInt8OrNull,toUInt16OrNull,toUInt32OrNull,toUInt64OrNull,toInt8OrNull,toInt16OrNull,toInt32OrNull,toInt64OrNull,toFloat32OrNull,toFloat64OrNull,toDateOrNull,toDateTimeOrNull {#touint8ornull-touint16ornull-touint32ornull-touint64ornull-toint8ornull-toint16ornull-toint32ornull-toint64ornull-tofloat32ornull-tofloat64ornull-todateornull-todatetimeornull} ## toString {#tostring} @@ -47,10 +261,6 @@ SELECT 另请参阅`toUnixTimestamp`函数。 -## toDecimal32(value,S),toDecimal64(value,S),toDecimal128(value,S) {#todecimal32value-s-todecimal64value-s-todecimal128value-s} - -将`value`转换为精度为`S`的[十进制](../../sql-reference/functions/type-conversion-functions.md)。`value`可以是数字或字符串。`S`参数为指定的小数位数。 - ## toFixedString(s,N) {#tofixedstrings-n} 将String类型的参数转换为FixedString(N)类型的值(具有固定长度N的字符串)。N必须是一个常量。 @@ -78,17 +288,19 @@ SELECT toFixedString('foo\0bar', 8) AS s, toStringCutToZero(s) AS s_cut │ foo\0bar\0 │ foo │ └────────────┴───────┘ -## reinterpretAsUInt8,reinterpretAsUInt16,reinterpretAsUInt32,reinterpretAsUInt64 {#reinterpretasuint8-reinterpretasuint16-reinterpretasuint32-reinterpretasuint64} +## reinterpretAsUInt(8\|16\|32\|64) {#reinterpretasuint8163264} -## reinterpretAsInt8,reinterpretAsInt16,reinterpretAsInt32,reinterpretAsInt64 {#reinterpretasint8-reinterpretasint16-reinterpretasint32-reinterpretasint64} +## reinterpretAsInt(8\|16\|32\|64) {#reinterpretasint8163264} -## reinterpretAsFloat32,reinterpretAsFloat64 {#reinterpretasfloat32-reinterpretasfloat64} +## reinterpretAsFloat(32\|64) {#reinterpretasfloat3264} -## 重新解释日期,重新解释日期时间 {#reinterpretasdate-reinterpretasdatetime} +## reinterpretAsDate {#reinterpretasdate} + +## reinterpretAsDateTime {#reinterpretasdatetime} 这些函数接受一个字符串,并将放在字符串开头的字节解释为主机顺序中的数字(little endian)。如果字符串不够长,则函数就像使用必要数量的空字节填充字符串一样。如果字符串比需要的长,则忽略额外的字节。Date被解释为Unix时间戳的天数,DateTime被解释为Unix时间戳。 -## 重新解释字符串 {#reinterpretasstring} +## reinterpretAsString {#reinterpretasstring} 此函数接受数字、Date或DateTime,并返回一个字符串,其中包含表示主机顺序(小端)的相应值的字节。从末尾删除空字节。例如,UInt32类型值255是一个字节长的字符串。 @@ -96,7 +308,7 @@ SELECT toFixedString('foo\0bar', 8) AS s, toStringCutToZero(s) AS s_cut 此函数接受数字、Date或DateTime,并返回包含表示主机顺序(小端)的相应值的字节的FixedString。从末尾删除空字节。例如,UInt32类型值255是一个长度为一个字节的FixedString。 -## 演员(x,t) {#type_conversion_function-cast} +## CAST(x, T) {#type_conversion_function-cast} 将’x’转换为’t’数据类型。还支持语法CAST(x AS t) @@ -133,10 +345,32 @@ SELECT │ Nullable(UInt16) │ └─────────────────────────────────────────┘ -## 每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每天每 {#function-tointerval} +## toInterval(Year\|Quarter\|Month\|Week\|Day\|Hour\|Minute\|Second) {#function-tointerval} -将数字类型参数转换为Interval类型(时间区间)。 -Interval类型实际上是非常有用的,您可以使用此类型的数据直接与Date或DateTime执行算术运算。同时,ClickHouse为Interval类型数据的声明提供了更方便的语法。例如: +把一个数值类型的值转换为[Interval](../../sql-reference/data-types/special-data-types/interval.md)类型的数据。 + +**语法** + +``` sql +toIntervalSecond(number) +toIntervalMinute(number) +toIntervalHour(number) +toIntervalDay(number) +toIntervalWeek(number) +toIntervalMonth(number) +toIntervalQuarter(number) +toIntervalYear(number) +``` + +**参数** + +- `number` — 正整数,持续的时间。 + +**返回值** + +- 时间的`Interval`值。 + +**例子** ``` sql WITH @@ -148,22 +382,257 @@ SELECT date + interval_to_week ``` - ┌─plus(date, interval_week)─┬─plus(date, interval_to_week)─┐ - │ 2019-01-08 │ 2019-01-08 │ - └───────────────────────────┴──────────────────────────────┘ +``` text +┌─plus(date, interval_week)─┬─plus(date, interval_to_week)─┐ +│ 2019-01-08 │ 2019-01-08 │ +└───────────────────────────┴──────────────────────────────┘ +``` -## parsedatetimebestefort {#type_conversion_functions-parsedatetimebesteffort} +## parseDateTimeBestEffort {#parsedatetimebesteffort} -将数字类型参数解析为Date或DateTime类型。 -与toDate和toDateTime不同,parseDateTimeBestEffort可以进行更复杂的日期格式。 -有关详细信息,请参阅链接:[复杂日期格式](https://xkcd.com/1179/)。 +把[String](../../sql-reference/data-types/string.md)类型的时间日期转换为[DateTime](../../sql-reference/data-types/datetime.md#data_type-datetime)数据类型。 -## parsedatetimebestefortornull {#parsedatetimebesteffortornull} +该函数可以解析[ISO 8601](https://en.wikipedia.org/wiki/ISO_8601),[RFC 1123 - 5.2.14 RFC-822 Date and Time Specification](https://tools.ietf.org/html/rfc1123#page-55)或者ClickHouse的一些别的时间日期格式。 -与[parsedatetimebestefort](#type_conversion_functions-parsedatetimebesteffort)相同,但它遇到无法处理的日期格式时返回null。 +**语法** -## parsedatetimebestefortorzero {#parsedatetimebesteffortorzero} +``` sql +parseDateTimeBestEffort(time_string [, time_zone]); +``` -与[parsedatetimebestefort](#type_conversion_functions-parsedatetimebesteffort)相同,但它遇到无法处理的日期格式时返回零Date或零DateTime。 +**参数** + +- `time_string` — 字符类型的时间和日期。 +- `time_zone` — 字符类型的时区。 + +**非标准格式的支持** + +- 9位或者10位的数字时间,[unix timestamp](https://en.wikipedia.org/wiki/Unix_time). +- 时间和日期组成的字符串: `YYYYMMDDhhmmss`, `DD/MM/YYYY hh:mm:ss`, `DD-MM-YY hh:mm`, `YYYY-MM-DD hh:mm:ss`等。 +- 只有日期的字符串: `YYYY`, `YYYYMM`, `YYYY*MM`, `DD/MM/YYYY`, `DD-MM-YY` 等。 +- 只有天和时间: `DD`, `DD hh`, `DD hh:mm`。这种情况下 `YYYY-MM` 默认为 `2000-01`。 +- 包含时间日期以及时区信息: `YYYY-MM-DD hh:mm:ss ±h:mm`等。例如: `2020-12-12 17:36:00 -5:00`。 + +对于所有的格式来说,这个函数通过全称或者第一个三个字符的月份名称来解析月份,比如:`24/DEC/18`, `24-Dec-18`, `01-September-2018`。 + +**返回值** + +- `DateTime`类型数据。 + +**例子** + +查询: + +``` sql +SELECT parseDateTimeBestEffort('12/12/2020 12:12:57') +AS parseDateTimeBestEffort; +``` + +结果: + +``` text +┌─parseDateTimeBestEffort─┐ +│ 2020-12-12 12:12:57 │ +└─────────────────────────┘ +``` + +查询: + +``` sql +SELECT parseDateTimeBestEffort('Sat, 18 Aug 2018 07:22:16 GMT', 'Europe/Moscow') +AS parseDateTimeBestEffort +``` + +结果: + +``` text +┌─parseDateTimeBestEffort─┐ +│ 2018-08-18 10:22:16 │ +└─────────────────────────┘ +``` + +查询: + +``` sql +SELECT parseDateTimeBestEffort('1284101485') +AS parseDateTimeBestEffort +``` + +结果: + +``` text +┌─parseDateTimeBestEffort─┐ +│ 2015-07-07 12:04:41 │ +└─────────────────────────┘ +``` + +查询: + +``` sql +SELECT parseDateTimeBestEffort('2018-12-12 10:12:12') +AS parseDateTimeBestEffort +``` + +结果: + +``` text +┌─parseDateTimeBestEffort─┐ +│ 2018-12-12 10:12:12 │ +└─────────────────────────┘ +``` + +查询: + +``` sql +SELECT parseDateTimeBestEffort('10 20:19') +``` + +结果: + +``` text +┌─parseDateTimeBestEffort('10 20:19')─┐ +│ 2000-01-10 20:19:00 │ +└─────────────────────────────────────┘ +``` + +**除此之外** + +- [ISO 8601 announcement by @xkcd](https://xkcd.com/1179/) +- [RFC 1123](https://tools.ietf.org/html/rfc1123) +- [toDate](#todate) +- [toDateTime](#todatetime) + +## parseDateTimeBestEffortOrNull {#parsedatetimebesteffortornull} + +这个函数和[parseDateTimeBestEffort](#parsedatetimebesteffort)基本一致,除了无法解析返回结果为`NULL`。 + +## parseDateTimeBestEffortOrZero {#parsedatetimebesteffortorzero} + +这个函数和[parseDateTimeBestEffort](#parsedatetimebesteffort)基本一致,除了无法解析返回结果为`0`。 + +## toLowCardinality {#tolowcardinality} + +把输入值转换为[LowCardianlity](../data-types/lowcardinality.md)的相同类型的数据。 + +如果要把`LowCardinality`类型的数据转换为其他类型,使用[CAST](#type_conversion_function-cast)函数。比如:`CAST(x as String)`。 + +**语法** + +```sql +toLowCardinality(expr) +``` + +**参数** + +- `expr` — [表达式](../syntax.md#syntax-expressions)为[支持的数据类型](../data-types/index.md#data_types)的一种。 + + +**返回值** + +- `expr`的结果。 + +类型: `LowCardinality(expr_result_type)` + +**例子** + +查询: + +```sql +SELECT toLowCardinality('1') +``` + +结果: + +```text +┌─toLowCardinality('1')─┐ +│ 1 │ +└───────────────────────┘ +``` + + +## toUnixTimestamp64Milli +## toUnixTimestamp64Micro +## toUnixTimestamp64Nano + +把一个`DateTime64`类型的数据转换为`Int64`类型的数据,结果包含固定亚秒的精度。输入的值是变大还是变低依赖于输入的精度。需要注意的是输出的值是一个UTC的时间戳, 不是同一个时区的`DateTime64`值。 + +**语法** + +``` sql +toUnixTimestamp64Milli(value) +``` + +**参数** + +- `value` — 任何精度的DateTime64类型的数据。 + +**返回值** + +- `value` `Int64`类型数据。 + +**例子** + +查询: + +``` sql +WITH toDateTime64('2019-09-16 19:20:12.345678910', 6) AS dt64 +SELECT toUnixTimestamp64Milli(dt64) +``` + +结果: + +``` text +┌─toUnixTimestamp64Milli(dt64)─┐ +│ 1568650812345 │ +└──────────────────────────────┘ +``` + +``` sql +WITH toDateTime64('2019-09-16 19:20:12.345678910', 6) AS dt64 +SELECT toUnixTimestamp64Nano(dt64) +``` + +结果: + +``` text +┌─toUnixTimestamp64Nano(dt64)─┐ +│ 1568650812345678000 │ +└─────────────────────────────┘ +``` + +## fromUnixTimestamp64Milli +## fromUnixTimestamp64Micro +## fromUnixTimestamp64Nano + +把`Int64`类型的数据转换为`DateTime64`类型的数据,结果包含固定的亚秒精度和可选的时区。 输入的值是变大还是变低依赖于输入的精度。需要注意的是输入的值是一个UTC的时间戳, 不是一个包含时区的时间戳。 + + +**语法** + +``` sql +fromUnixTimestamp64Milli(value [, ti]) +``` + +**参数** + +- `value` — `Int64`类型的数据,可以是任意精度。 +- `timezone` — `String`类型的时区 + +**返回值** + +- `value` DateTime64`类型的数据。 + +**例子** + +``` sql +WITH CAST(1234567891011, 'Int64') AS i64 +SELECT fromUnixTimestamp64Milli(i64, 'UTC') +``` + +``` text +┌─fromUnixTimestamp64Milli(i64, 'UTC')─┐ +│ 2009-02-13 23:31:31.011 │ +└──────────────────────────────────────┘ +``` [来源文章](https://clickhouse.tech/docs/en/query_language/functions/type_conversion_functions/) diff --git a/programs/CMakeLists.txt b/programs/CMakeLists.txt index baf8270d1bf..b36a2ff8194 100644 --- a/programs/CMakeLists.txt +++ b/programs/CMakeLists.txt @@ -207,7 +207,7 @@ if (TARGET clickhouse-server AND TARGET copy-headers) endif () if (ENABLE_TESTS AND USE_GTEST) - set (CLICKHOUSE_ALL_TESTS_TARGETS local_date_time_comparison unit_tests_libcommon unit_tests_dbms hashing_write_buffer hashing_read_buffer in_join_subqueries_preprocessor) - add_custom_target (clickhouse-tests ALL DEPENDS ${CLICKHOUSE_ALL_TESTS_TARGETS}) + set (CLICKHOUSE_UNIT_TESTS_TARGETS unit_tests_libcommon unit_tests_dbms) + add_custom_target (clickhouse-tests ALL DEPENDS ${CLICKHOUSE_UNIT_TESTS_TARGETS}) add_dependencies(clickhouse-bundle clickhouse-tests) endif() diff --git a/programs/benchmark/Benchmark.cpp b/programs/benchmark/Benchmark.cpp index 1b2867940ea..bb814f474e3 100644 --- a/programs/benchmark/Benchmark.cpp +++ b/programs/benchmark/Benchmark.cpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -469,7 +470,7 @@ private: const auto & info = infos[i]; json_out << double_quote << connections[i]->getDescription() << ": {\n"; - json_out << double_quote << "statistics: {\n"; + json_out << double_quote << "statistics" << ": {\n"; print_key_value("QPS", info->queries / info->work_time); print_key_value("RPS", info->read_rows / info->work_time); @@ -479,7 +480,7 @@ private: print_key_value("num_queries", info->queries.load(), false); json_out << "},\n"; - json_out << double_quote << "query_time_percentiles: {\n"; + json_out << double_quote << "query_time_percentiles" << ": {\n"; for (int percent = 0; percent <= 90; percent += 10) print_percentile(*info, percent); @@ -539,7 +540,7 @@ int mainEntryClickHouseBenchmark(int argc, char ** argv) ("password", value()->default_value(""), "") ("database", value()->default_value("default"), "") ("stacktrace", "print stack traces of exceptions") - ("confidence", value()->default_value(5), "set the level of confidence for T-test [0=80%, 1=90%, 2=95%, 3=98%, 4=99%, 5=99.5%(default)") + ("confidence", value()->default_value(5), "set the level of confidence for T-test [0=80%, 1=90%, 2=95%, 3=98%, 4=99%, 5=99.5%(default)") ("query_id", value()->default_value(""), "") ; @@ -550,6 +551,8 @@ int mainEntryClickHouseBenchmark(int argc, char ** argv) boost::program_options::store(boost::program_options::parse_command_line(argc, argv, desc), options); boost::program_options::notify(options); + clearPasswordFromCommandLine(argc, argv); + if (options.count("help")) { std::cout << "Usage: " << argv[0] << " [options] < queries.txt\n"; diff --git a/programs/benchmark/CMakeLists.txt b/programs/benchmark/CMakeLists.txt index be999aafe80..3fa8deb6bd9 100644 --- a/programs/benchmark/CMakeLists.txt +++ b/programs/benchmark/CMakeLists.txt @@ -1,5 +1,12 @@ -set(CLICKHOUSE_BENCHMARK_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Benchmark.cpp) -set(CLICKHOUSE_BENCHMARK_LINK PRIVATE dbms clickhouse_aggregate_functions clickhouse_common_config ${Boost_PROGRAM_OPTIONS_LIBRARY}) +set (CLICKHOUSE_BENCHMARK_SOURCES Benchmark.cpp) + +set (CLICKHOUSE_BENCHMARK_LINK + PRIVATE + boost::program_options + clickhouse_aggregate_functions + clickhouse_common_config + dbms +) clickhouse_program_add(benchmark) diff --git a/programs/client/CMakeLists.txt b/programs/client/CMakeLists.txt index e273123afe0..6ded6a94f3a 100644 --- a/programs/client/CMakeLists.txt +++ b/programs/client/CMakeLists.txt @@ -1,10 +1,19 @@ -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_SOURCES + Client.cpp + ConnectionParameters.cpp + Suggest.cpp ) -set(CLICKHOUSE_CLIENT_LINK PRIVATE clickhouse_common_config clickhouse_functions clickhouse_aggregate_functions clickhouse_common_io clickhouse_parsers string_utils ${Boost_PROGRAM_OPTIONS_LIBRARY}) +set (CLICKHOUSE_CLIENT_LINK + PRIVATE + boost::program_options + clickhouse_aggregate_functions + clickhouse_common_config + clickhouse_common_io + clickhouse_functions + clickhouse_parsers + string_utils +) # Always use internal readpassphrase add_subdirectory(readpassphrase) diff --git a/programs/client/Client.cpp b/programs/client/Client.cpp index 930e55a06f8..63467c1129d 100644 --- a/programs/client/Client.cpp +++ b/programs/client/Client.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -75,6 +76,7 @@ #include #include #include +#include #if !defined(ARCADIA_BUILD) # include @@ -357,6 +359,78 @@ private: return false; } +#if USE_REPLXX + static void highlight(const String & query, std::vector & colors) + { + using namespace replxx; + + static const std::unordered_map token_to_color = + { + { TokenType::Whitespace, Replxx::Color::DEFAULT }, + { TokenType::Comment, Replxx::Color::GRAY }, + { TokenType::BareWord, Replxx::Color::DEFAULT }, + { TokenType::Number, Replxx::Color::GREEN }, + { TokenType::StringLiteral, Replxx::Color::CYAN }, + { TokenType::QuotedIdentifier, Replxx::Color::MAGENTA }, + { TokenType::OpeningRoundBracket, Replxx::Color::BROWN }, + { TokenType::ClosingRoundBracket, Replxx::Color::BROWN }, + { TokenType::OpeningSquareBracket, Replxx::Color::BROWN }, + { TokenType::ClosingSquareBracket, Replxx::Color::BROWN }, + { TokenType::OpeningCurlyBrace, Replxx::Color::INTENSE }, + { TokenType::ClosingCurlyBrace, Replxx::Color::INTENSE }, + + { TokenType::Comma, Replxx::Color::INTENSE }, + { TokenType::Semicolon, Replxx::Color::INTENSE }, + { TokenType::Dot, Replxx::Color::INTENSE }, + { TokenType::Asterisk, Replxx::Color::INTENSE }, + { TokenType::Plus, Replxx::Color::INTENSE }, + { TokenType::Minus, Replxx::Color::INTENSE }, + { TokenType::Slash, Replxx::Color::INTENSE }, + { TokenType::Percent, Replxx::Color::INTENSE }, + { TokenType::Arrow, Replxx::Color::INTENSE }, + { TokenType::QuestionMark, Replxx::Color::INTENSE }, + { TokenType::Colon, Replxx::Color::INTENSE }, + { TokenType::Equals, Replxx::Color::INTENSE }, + { TokenType::NotEquals, Replxx::Color::INTENSE }, + { TokenType::Less, Replxx::Color::INTENSE }, + { TokenType::Greater, Replxx::Color::INTENSE }, + { TokenType::LessOrEquals, Replxx::Color::INTENSE }, + { TokenType::GreaterOrEquals, Replxx::Color::INTENSE }, + { TokenType::Concatenation, Replxx::Color::INTENSE }, + { TokenType::At, Replxx::Color::INTENSE }, + + { TokenType::EndOfStream, Replxx::Color::DEFAULT }, + + { TokenType::Error, Replxx::Color::RED }, + { TokenType::ErrorMultilineCommentIsNotClosed, Replxx::Color::RED }, + { TokenType::ErrorSingleQuoteIsNotClosed, Replxx::Color::RED }, + { TokenType::ErrorDoubleQuoteIsNotClosed, Replxx::Color::RED }, + { TokenType::ErrorSinglePipeMark, Replxx::Color::RED }, + { TokenType::ErrorWrongNumber, Replxx::Color::RED }, + { TokenType::ErrorMaxQuerySizeExceeded, Replxx::Color::RED } + }; + + const Replxx::Color unknown_token_color = Replxx::Color::RED; + + Lexer lexer(query.data(), query.data() + query.size()); + size_t pos = 0; + + for (Token token = lexer.nextToken(); !token.isEnd(); token = lexer.nextToken()) + { + size_t utf8_len = UTF8::countCodePoints(reinterpret_cast(token.begin), token.size()); + for (size_t code_point_index = 0; code_point_index < utf8_len; ++code_point_index) + { + if (token_to_color.find(token.type) != token_to_color.end()) + colors[pos + code_point_index] = token_to_color.at(token.type); + else + colors[pos + code_point_index] = unknown_token_color; + } + + pos += utf8_len; + } + } +#endif + int mainImpl() { UseSSL use_ssl; @@ -498,12 +572,26 @@ private: if (!history_file.empty() && !Poco::File(history_file).exists()) Poco::File(history_file).createFile(); + LineReader::Patterns query_extenders = {"\\"}; + LineReader::Patterns query_delimiters = {";", "\\G"}; + #if USE_REPLXX - ReplxxLineReader lr(Suggest::instance(), history_file, '\\', config().has("multiline") ? ';' : 0); + replxx::Replxx::highlighter_callback_t highlight_callback{}; + if (config().getBool("highlight")) + highlight_callback = highlight; + + ReplxxLineReader lr( + Suggest::instance(), + history_file, + config().has("multiline"), + query_extenders, + query_delimiters, + highlight_callback); + #elif defined(USE_READLINE) && USE_READLINE - ReadlineLineReader lr(Suggest::instance(), history_file, '\\', config().has("multiline") ? ';' : 0); + ReadlineLineReader lr(Suggest::instance(), history_file, config().has("multiline"), query_extenders, query_delimiters); #else - LineReader lr(history_file, '\\', config().has("multiline") ? ';' : 0); + LineReader lr(history_file, config().has("multiline"), query_extenders, query_delimiters); #endif /// Enable bracketed-paste-mode only when multiquery is enabled and multiline is @@ -1178,7 +1266,7 @@ private: break; } - if (!receiveAndProcessPacket()) + if (!receiveAndProcessPacket(cancelled)) break; } @@ -1189,14 +1277,16 @@ private: /// Receive a part of the result, or progress info or an exception and process it. /// Returns true if one should continue receiving packets. - bool receiveAndProcessPacket() + /// Output of result is suppressed if query was cancelled. + bool receiveAndProcessPacket(bool cancelled) { Packet packet = connection->receivePacket(); switch (packet.type) { case Protocol::Server::Data: - onData(packet.block); + if (!cancelled) + onData(packet.block); return true; case Protocol::Server::Progress: @@ -1208,11 +1298,13 @@ private: return true; case Protocol::Server::Totals: - onTotals(packet.block); + if (!cancelled) + onTotals(packet.block); return true; case Protocol::Server::Extremes: - onExtremes(packet.block); + if (!cancelled) + onExtremes(packet.block); return true; case Protocol::Server::Exception: @@ -1304,7 +1396,7 @@ private: while (packet_type && *packet_type == Protocol::Server::Log) { - receiveAndProcessPacket(); + receiveAndProcessPacket(false); packet_type = connection->checkPacket(); } } @@ -1759,6 +1851,7 @@ public: ("echo", "in batch mode, print query before execution") ("max_client_network_bandwidth", po::value(), "the maximum speed of data exchange over the network for the client in bytes per second.") ("compression", po::value(), "enable or disable compression") + ("highlight", po::value()->default_value(true), "enable or disable basic syntax highlight in interactive command line") ("log-level", po::value(), "client log level") ("server_logs_file", po::value(), "put server logs into specified file") ; @@ -1828,7 +1921,11 @@ public: std::string text = e.displayText(); std::cerr << "Code: " << e.code() << ". " << text << std::endl; std::cerr << "Table №" << i << std::endl << std::endl; - exit(e.code()); + /// Avoid the case when error exit code can possibly overflow to normal (zero). + auto exit_code = e.code() % 256; + if (exit_code == 0) + exit_code = 255; + exit(exit_code); } } @@ -1905,9 +2002,12 @@ public: config().setBool("disable_suggestion", true); if (options.count("suggestion_limit")) config().setInt("suggestion_limit", options["suggestion_limit"].as()); + if (options.count("highlight")) + config().setBool("highlight", options["highlight"].as()); argsToConfig(common_arguments, config(), 100); + clearPasswordFromCommandLine(argc, argv); } }; diff --git a/programs/client/Suggest.cpp b/programs/client/Suggest.cpp index 8fffbec4fab..4ac5e735fd5 100644 --- a/programs/client/Suggest.cpp +++ b/programs/client/Suggest.cpp @@ -114,6 +114,8 @@ void Suggest::loadImpl(Connection & connection, const ConnectionTimeouts & timeo << " UNION ALL " "SELECT DISTINCT name FROM system.tables LIMIT " << limit_str << " UNION ALL " + "SELECT DISTINCT name FROM system.dictionaries LIMIT " << limit_str + << " UNION ALL " "SELECT DISTINCT name FROM system.columns LIMIT " << limit_str; } diff --git a/programs/compressor/CMakeLists.txt b/programs/compressor/CMakeLists.txt index c009bb55f76..ff642a32fd4 100644 --- a/programs/compressor/CMakeLists.txt +++ b/programs/compressor/CMakeLists.txt @@ -1,7 +1,12 @@ # Also in utils -set(CLICKHOUSE_COMPRESSOR_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Compressor.cpp) -set(CLICKHOUSE_COMPRESSOR_LINK PRIVATE dbms clickhouse_parsers ${Boost_PROGRAM_OPTIONS_LIBRARY}) -#set(CLICKHOUSE_COMPRESSOR_INCLUDE SYSTEM PRIVATE ...) +set (CLICKHOUSE_COMPRESSOR_SOURCES Compressor.cpp) + +set (CLICKHOUSE_COMPRESSOR_LINK + PRIVATE + boost::program_options + clickhouse_parsers + dbms +) clickhouse_program_add(compressor) diff --git a/programs/copier/ClusterCopier.cpp b/programs/copier/ClusterCopier.cpp index 5254d2a97ac..7fa0f663295 100644 --- a/programs/copier/ClusterCopier.cpp +++ b/programs/copier/ClusterCopier.cpp @@ -25,7 +25,7 @@ void ClusterCopier::init() task_description_watch_callback = [this] (const Coordination::WatchResponse & response) { - if (response.error != Coordination::ZOK) + if (response.error != Coordination::Error::ZOK) return; UInt64 version = ++task_description_version; LOG_DEBUG(log, "Task description should be updated, local version {}", version); @@ -206,11 +206,11 @@ void ClusterCopier::uploadTaskDescription(const std::string & task_path, const s zookeeper->createAncestors(local_task_description_path); auto code = zookeeper->tryCreate(local_task_description_path, task_config_str, zkutil::CreateMode::Persistent); - if (code && force) + if (code != Coordination::Error::ZOK && force) zookeeper->createOrUpdate(local_task_description_path, task_config_str, zkutil::CreateMode::Persistent); LOG_DEBUG(log, "Task description {} uploaded to {} with result {} ({})", - ((code && !force) ? "not " : ""), local_task_description_path, code, zookeeper->error2string(code)); + ((code != Coordination::Error::ZOK && !force) ? "not " : ""), local_task_description_path, code, Coordination::errorMessage(code)); } void ClusterCopier::reloadTaskDescription() @@ -220,10 +220,10 @@ void ClusterCopier::reloadTaskDescription() String task_config_str; Coordination::Stat stat{}; - int code; + Coordination::Error code; zookeeper->tryGetWatch(task_description_path, task_config_str, &stat, task_description_watch_callback, &code); - if (code) + if (code != Coordination::Error::ZOK) throw Exception("Can't get description node " + task_description_path, ErrorCodes::BAD_ARGUMENTS); LOG_DEBUG(log, "Loading description, zxid={}", task_description_current_stat.czxid); @@ -376,10 +376,10 @@ zkutil::EphemeralNodeHolder::Ptr ClusterCopier::createTaskWorkerNodeAndWaitIfNee Coordination::Responses responses; auto code = zookeeper->tryMulti(ops, responses); - if (code == Coordination::ZOK || code == Coordination::ZNODEEXISTS) + if (code == Coordination::Error::ZOK || code == Coordination::Error::ZNODEEXISTS) return std::make_shared(current_worker_path, *zookeeper, false, false, description); - if (code == Coordination::ZBADVERSION) + if (code == Coordination::Error::ZBADVERSION) { ++num_bad_version_errors; @@ -545,7 +545,7 @@ TaskStatus ClusterCopier::tryMoveAllPiecesToDestinationTable(const TaskTable & t } catch (const Coordination::Exception & e) { - if (e.code == Coordination::ZNODEEXISTS) + if (e.code == Coordination::Error::ZNODEEXISTS) { LOG_DEBUG(log, "Someone is already moving pieces {}", current_partition_attach_is_active); return TaskStatus::Active; @@ -745,7 +745,7 @@ bool ClusterCopier::tryDropPartitionPiece( } catch (const Coordination::Exception & e) { - if (e.code == Coordination::ZNODEEXISTS) + if (e.code == Coordination::Error::ZNODEEXISTS) { LOG_DEBUG(log, "Partition {} piece {} is cleaning now by somebody, sleep", task_partition.name, toString(current_piece_number)); std::this_thread::sleep_for(default_sleep_time); @@ -778,7 +778,7 @@ bool ClusterCopier::tryDropPartitionPiece( } catch (const Coordination::Exception & e) { - if (e.code == Coordination::ZNODEEXISTS) + if (e.code == Coordination::Error::ZNODEEXISTS) { LOG_DEBUG(log, "Partition {} is being filled now by somebody, sleep", task_partition.name); return false; @@ -795,7 +795,7 @@ bool ClusterCopier::tryDropPartitionPiece( /// Remove all status nodes { Strings children; - if (zookeeper->tryGetChildren(current_shards_path, children) == Coordination::ZOK) + if (zookeeper->tryGetChildren(current_shards_path, children) == Coordination::Error::ZOK) for (const auto & child : children) { zookeeper->removeRecursive(current_shards_path + "/" + child); @@ -845,7 +845,7 @@ bool ClusterCopier::tryDropPartitionPiece( } LOG_INFO(log, "Partition {} piece {} was dropped on cluster {}", task_partition.name, toString(current_piece_number), task_table.cluster_push_name); - if (zookeeper->tryCreate(current_shards_path, host_id, zkutil::CreateMode::Persistent) == Coordination::ZNODEEXISTS) + if (zookeeper->tryCreate(current_shards_path, host_id, zkutil::CreateMode::Persistent) == Coordination::Error::ZNODEEXISTS) zookeeper->set(current_shards_path, host_id); } @@ -1233,7 +1233,7 @@ TaskStatus ClusterCopier::processPartitionPieceTaskImpl( } catch (const Coordination::Exception & e) { - if (e.code == Coordination::ZNODEEXISTS) + if (e.code == Coordination::Error::ZNODEEXISTS) { LOG_DEBUG(log, "Someone is already processing {}", current_task_piece_is_active_path); return TaskStatus::Active; @@ -1271,9 +1271,9 @@ TaskStatus ClusterCopier::processPartitionPieceTaskImpl( { String state_finished = TaskStateWithOwner::getData(TaskState::Finished, host_id); auto res = zookeeper->tryCreate(current_task_piece_status_path, state_finished, zkutil::CreateMode::Persistent); - if (res == Coordination::ZNODEEXISTS) + if (res == Coordination::Error::ZNODEEXISTS) LOG_DEBUG(log, "Partition {} piece {} is absent on current replica of a shard. But other replicas have already marked it as done.", task_partition.name, current_piece_number); - if (res == Coordination::ZOK) + if (res == Coordination::Error::ZOK) LOG_DEBUG(log, "Partition {} piece {} is absent on current replica of a shard. Will mark it as done. Other replicas will do the same.", task_partition.name, current_piece_number); return TaskStatus::Finished; } @@ -1429,7 +1429,7 @@ TaskStatus ClusterCopier::processPartitionPieceTaskImpl( { Coordination::ExistsResponse status = future_is_dirty_checker.get(); - if (status.error != Coordination::ZNONODE) + if (status.error != Coordination::Error::ZNONODE) { LogicalClock dirt_discovery_epoch (status.stat.mzxid); if (dirt_discovery_epoch == clean_state_clock.discovery_zxid) diff --git a/programs/copier/ZooKeeperStaff.h b/programs/copier/ZooKeeperStaff.h index edd0d9e43d2..66036ae2f27 100644 --- a/programs/copier/ZooKeeperStaff.h +++ b/programs/copier/ZooKeeperStaff.h @@ -178,7 +178,7 @@ public: [stale = stale] (const Coordination::WatchResponse & rsp) { auto logger = &Poco::Logger::get("ClusterCopier"); - if (rsp.error == Coordination::ZOK) + if (rsp.error == Coordination::Error::ZOK) { switch (rsp.type) { diff --git a/programs/extract-from-config/CMakeLists.txt b/programs/extract-from-config/CMakeLists.txt index b82cbb966ae..ff2d7937117 100644 --- a/programs/extract-from-config/CMakeLists.txt +++ b/programs/extract-from-config/CMakeLists.txt @@ -1,5 +1,11 @@ -set(CLICKHOUSE_EXTRACT_FROM_CONFIG_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/ExtractFromConfig.cpp) -set(CLICKHOUSE_EXTRACT_FROM_CONFIG_LINK PRIVATE clickhouse_common_config clickhouse_common_io clickhouse_common_zookeeper ${Boost_PROGRAM_OPTIONS_LIBRARY}) -#set(CLICKHOUSE_EXTRACT_FROM_CONFIG_INCLUDE SYSTEM PRIVATE ...) +set (CLICKHOUSE_EXTRACT_FROM_CONFIG_SOURCES ExtractFromConfig.cpp) + +set (CLICKHOUSE_EXTRACT_FROM_CONFIG_LINK + PRIVATE + boost::program_options + clickhouse_common_config + clickhouse_common_io + clickhouse_common_zookeeper +) clickhouse_program_add(extract-from-config) diff --git a/programs/format/CMakeLists.txt b/programs/format/CMakeLists.txt index aac72d641e6..ab06708cd3a 100644 --- a/programs/format/CMakeLists.txt +++ b/programs/format/CMakeLists.txt @@ -1,5 +1,11 @@ -set(CLICKHOUSE_FORMAT_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Format.cpp) -set(CLICKHOUSE_FORMAT_LINK PRIVATE dbms clickhouse_common_io clickhouse_parsers ${Boost_PROGRAM_OPTIONS_LIBRARY}) -#set(CLICKHOUSE_FORMAT_INCLUDE SYSTEM PRIVATE ...) +set (CLICKHOUSE_FORMAT_SOURCES Format.cpp) + +set (CLICKHOUSE_FORMAT_LINK + PRIVATE + boost::program_options + clickhouse_common_io + clickhouse_parsers + dbms +) clickhouse_program_add(format) diff --git a/programs/local/CMakeLists.txt b/programs/local/CMakeLists.txt index d066fd53277..b61f0ea33b7 100644 --- a/programs/local/CMakeLists.txt +++ b/programs/local/CMakeLists.txt @@ -1,6 +1,17 @@ -set(CLICKHOUSE_LOCAL_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/LocalServer.cpp) -set(CLICKHOUSE_LOCAL_LINK PRIVATE clickhouse_storages_system clickhouse_dictionaries clickhouse_common_config clickhouse_common_io clickhouse_functions clickhouse_aggregate_functions clickhouse_parsers clickhouse_table_functions ${Boost_PROGRAM_OPTIONS_LIBRARY}) -#set(CLICKHOUSE_LOCAL_INCLUDE SYSTEM PRIVATE ...) +set (CLICKHOUSE_LOCAL_SOURCES LocalServer.cpp) + +set (CLICKHOUSE_LOCAL_LINK + PRIVATE + boost::program_options + clickhouse_aggregate_functions + clickhouse_common_config + clickhouse_common_io + clickhouse_dictionaries + clickhouse_functions + clickhouse_parsers + clickhouse_storages_system + clickhouse_table_functions +) clickhouse_program_add(local) diff --git a/programs/obfuscator/CMakeLists.txt b/programs/obfuscator/CMakeLists.txt index 19dba2be95c..d1179b3718c 100644 --- a/programs/obfuscator/CMakeLists.txt +++ b/programs/obfuscator/CMakeLists.txt @@ -1,5 +1,9 @@ -set(CLICKHOUSE_OBFUSCATOR_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/Obfuscator.cpp) -set(CLICKHOUSE_OBFUSCATOR_LINK PRIVATE dbms ${Boost_PROGRAM_OPTIONS_LIBRARY}) -#set(CLICKHOUSE_OBFUSCATOR_INCLUDE SYSTEM PRIVATE ...) +set (CLICKHOUSE_OBFUSCATOR_SOURCES Obfuscator.cpp) + +set (CLICKHOUSE_OBFUSCATOR_LINK + PRIVATE + boost::program_options + dbms +) clickhouse_program_add(obfuscator) diff --git a/programs/server/Server.cpp b/programs/server/Server.cpp index e587e134075..9734bafe30e 100644 --- a/programs/server/Server.cpp +++ b/programs/server/Server.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -125,6 +126,7 @@ namespace ErrorCodes extern const int FAILED_TO_GETPWUID; extern const int MISMATCHING_USERS_FOR_PROCESS_AND_DATA; extern const int NETWORK_ERROR; + extern const int UNKNOWN_ELEMENT_IN_CONFIG; } @@ -210,6 +212,52 @@ void Server::defineOptions(Poco::Util::OptionSet & options) BaseDaemon::defineOptions(options); } + +/// Check that there is no user-level settings at the top level in config. +/// This is a common source of mistake (user don't know where to write user-level setting). +void checkForUserSettingsAtTopLevel(const Poco::Util::AbstractConfiguration & config, const std::string & path) +{ + if (config.getBool("skip_check_for_incorrect_settings", false)) + return; + + Settings settings; + for (const auto & setting : settings) + { + std::string name = setting.getName().toString(); + if (config.has(name)) + { + throw Exception(fmt::format("A setting '{}' appeared at top level in config {}." + " But it is user-level setting that should be located in users.xml inside section for specific profile." + " You can add it to if you want to change default value of this setting." + " You can also disable the check - specify 1" + " in the main configuration file.", + name, path), + ErrorCodes::UNKNOWN_ELEMENT_IN_CONFIG); + } + } +} + +void checkForUsersNotInMainConfig( + const Poco::Util::AbstractConfiguration & config, + const std::string & config_path, + const std::string & users_config_path, + Poco::Logger * log) +{ + if (config.getBool("skip_check_for_incorrect_settings", false)) + return; + + if (config.has("users") || config.has("profiles") || config.has("quotas")) + { + /// We cannot throw exception here, because we have support for obsolete 'conf.d' directory + /// (that does not correspond to config.d or users.d) but substitute configuration to both of them. + + LOG_ERROR(log, "The , and elements should be located in users config file: {} not in main config {}." + " Also note that you should place configuration changes to the appropriate *.d directory like 'users.d'.", + users_config_path, config_path); + } +} + + int Server::main(const std::vector & /*args*/) { Poco::Logger * log = &logger(); @@ -269,6 +317,8 @@ int Server::main(const std::vector & /*args*/) config().add(loaded_config.configuration.duplicate(), PRIO_DEFAULT, false); } + checkForUserSettingsAtTopLevel(config(), config_path); + const auto memory_amount = getMemoryAmount(); #if defined(OS_LINUX) @@ -473,13 +523,16 @@ int Server::main(const std::vector & /*args*/) SensitiveDataMasker::setInstance(std::make_unique(config(), "query_masking_rules")); } - auto main_config_reloader = std::make_unique(config_path, + auto main_config_reloader = std::make_unique( + config_path, include_from_path, config().getString("path", ""), std::move(main_config_zk_node_cache), main_config_zk_changed_event, [&](ConfigurationPtr config) { + checkForUserSettingsAtTopLevel(*config, config_path); + // 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()); @@ -508,12 +561,21 @@ int Server::main(const std::vector & /*args*/) if (Poco::File(config_dir + users_config_path).exists()) users_config_path = config_dir + users_config_path; } - auto users_config_reloader = std::make_unique(users_config_path, + + if (users_config_path != config_path) + checkForUsersNotInMainConfig(config(), config_path, users_config_path, log); + + auto users_config_reloader = std::make_unique( + users_config_path, include_from_path, config().getString("path", ""), zkutil::ZooKeeperNodeCache([&] { return global_context->getZooKeeper(); }), std::make_shared(), - [&](ConfigurationPtr config) { global_context->setUsersConfig(config); }, + [&](ConfigurationPtr config) + { + global_context->setUsersConfig(config); + checkForUserSettingsAtTopLevel(*config, users_config_path); + }, /* already_loaded = */ false); /// Reload config in SYSTEM RELOAD CONFIG query. @@ -542,7 +604,8 @@ int Server::main(const std::vector & /*args*/) if (uncompressed_cache_size > max_cache_size) { uncompressed_cache_size = max_cache_size; - LOG_INFO(log, "Uncompressed cache size was lowered to {} because the system has low amount of memory", formatReadableSizeWithBinarySuffix(uncompressed_cache_size)); + LOG_INFO(log, "Uncompressed cache size was lowered to {} because the system has low amount of memory", + formatReadableSizeWithBinarySuffix(uncompressed_cache_size)); } global_context->setUncompressedCache(uncompressed_cache_size); @@ -557,7 +620,8 @@ int Server::main(const std::vector & /*args*/) if (mark_cache_size > max_cache_size) { mark_cache_size = max_cache_size; - LOG_INFO(log, "Mark cache size was lowered to {} because the system has low amount of memory", formatReadableSizeWithBinarySuffix(uncompressed_cache_size)); + LOG_INFO(log, "Mark cache size was lowered to {} because the system has low amount of memory", + formatReadableSizeWithBinarySuffix(mark_cache_size)); } global_context->setMarkCache(mark_cache_size); diff --git a/programs/server/config.xml b/programs/server/config.xml index 21605edeb36..0ceba85593a 100644 --- a/programs/server/config.xml +++ b/programs/server/config.xml @@ -1,6 +1,9 @@ @@ -478,6 +481,20 @@ 1000 + + + system + asynchronous_metric_log
+ + 60000 +
+ diff --git a/programs/server/users.d/access_management.xml b/programs/server/users.d/access_management.xml new file mode 100644 index 00000000000..7e799cb7b10 --- /dev/null +++ b/programs/server/users.d/access_management.xml @@ -0,0 +1,7 @@ + + + + 1 + + + diff --git a/src/Access/AllowedClientHosts.cpp b/src/Access/AllowedClientHosts.cpp index 82372fd8b14..1cee8a2f782 100644 --- a/src/Access/AllowedClientHosts.cpp +++ b/src/Access/AllowedClientHosts.cpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace DB @@ -44,66 +45,22 @@ namespace return IPSubnet(toIPv6(subnet.getPrefix()), subnet.getMask()); } - - /// Helper function for isAddressOfHost(). - bool isAddressOfHostImpl(const IPAddress & address, const String & host) - { - IPAddress addr_v6 = toIPv6(address); - - /// Resolve by hand, because Poco don't use AI_ALL flag but we need it. - addrinfo * ai_begin = nullptr; - SCOPE_EXIT( - { - if (ai_begin) - freeaddrinfo(ai_begin); - }); - - addrinfo hints; - memset(&hints, 0, sizeof(hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_flags |= AI_V4MAPPED | AI_ALL; - - int err = getaddrinfo(host.c_str(), nullptr, &hints, &ai_begin); - if (err) - throw Exception("Cannot getaddrinfo(" + host + "): " + gai_strerror(err), ErrorCodes::DNS_ERROR); - - for (const addrinfo * ai = ai_begin; ai; ai = ai->ai_next) - { - if (ai->ai_addrlen && ai->ai_addr) - { - if (ai->ai_family == AF_INET) - { - const auto & sin = *reinterpret_cast(ai->ai_addr); - if (addr_v6 == toIPv6(IPAddress(&sin.sin_addr, sizeof(sin.sin_addr)))) - { - return true; - } - } - else if (ai->ai_family == AF_INET6) - { - const auto & sin = *reinterpret_cast(ai->ai_addr); - if (addr_v6 == IPAddress(&sin.sin6_addr, sizeof(sin.sin6_addr), sin.sin6_scope_id)) - { - return true; - } - } - } - } - - return false; - } - - auto & getIsAddressOfHostCache() - { - static SimpleCache cache; - return cache; - } - /// Whether a specified address is one of the addresses of a specified host. bool isAddressOfHost(const IPAddress & address, const String & host) { - /// We need to cache DNS requests. - return getIsAddressOfHostCache()(address, host); + IPAddress addr_v6 = toIPv6(address); + + auto host_addresses = DNSResolver::instance().resolveHostAll(host); + + for (const auto & addr : host_addresses) + { + if (addr.family() == IPAddress::Family::IPv4 && addr_v6 == toIPv6(addr)) + return true; + else if (addr.family() == IPAddress::Family::IPv6 && addr_v6 == addr) + return true; + } + + return false; } /// Helper function for isAddressOfLocalhost(). @@ -147,16 +104,10 @@ namespace return boost::range::find(local_addresses, toIPv6(address)) != local_addresses.end(); } - /// Helper function for getHostByAddress(). - String getHostByAddressImpl(const IPAddress & address) + /// Returns the host name by its address. + String getHostByAddress(const IPAddress & address) { - Poco::Net::SocketAddress sock_addr(address, 0); - - /// Resolve by hand, because Poco library doesn't have such functionality. - char host[1024]; - int err = getnameinfo(sock_addr.addr(), sock_addr.length(), host, sizeof(host), nullptr, 0, NI_NAMEREQD); - if (err) - throw Exception("Cannot getnameinfo(" + address.toString() + "): " + gai_strerror(err), ErrorCodes::DNS_ERROR); + String host = DNSResolver::instance().reverseResolve(address); /// Check that PTR record is resolved back to client address if (!isAddressOfHost(address, host)) @@ -165,19 +116,6 @@ namespace return host; } - auto & getHostByAddressCache() - { - static SimpleCache cache; - return cache; - } - - /// Returns the host name by its address. - String getHostByAddress(const IPAddress & address) - { - /// We need to cache DNS requests. - return getHostByAddressCache()(address); - } - void parseLikePatternIfIPSubnet(const String & pattern, IPSubnet & subnet, IPAddress::Family address_family) { @@ -376,10 +314,4 @@ bool AllowedClientHosts::contains(const IPAddress & client_address) const return false; } -void AllowedClientHosts::dropDNSCaches() -{ - getIsAddressOfHostCache().drop(); - getHostByAddressCache().drop(); -} - } diff --git a/src/Access/AllowedClientHosts.h b/src/Access/AllowedClientHosts.h index 4f4d54ce1ac..2baafb2e04a 100644 --- a/src/Access/AllowedClientHosts.h +++ b/src/Access/AllowedClientHosts.h @@ -114,8 +114,6 @@ public: friend bool operator ==(const AllowedClientHosts & lhs, const AllowedClientHosts & rhs); friend bool operator !=(const AllowedClientHosts & lhs, const AllowedClientHosts & rhs) { return !(lhs == rhs); } - static void dropDNSCaches(); - private: std::vector addresses; std::vector subnets; diff --git a/src/Access/IAccessStorage.cpp b/src/Access/IAccessStorage.cpp index 8e4314ec7c5..a7af61c7712 100644 --- a/src/Access/IAccessStorage.cpp +++ b/src/Access/IAccessStorage.cpp @@ -12,10 +12,10 @@ namespace DB { namespace ErrorCodes { - extern const int BAD_CAST; extern const int ACCESS_ENTITY_ALREADY_EXISTS; extern const int ACCESS_ENTITY_NOT_FOUND; extern const int ACCESS_STORAGE_READONLY; + extern const int LOGICAL_ERROR; } @@ -403,7 +403,7 @@ void IAccessStorage::throwBadCast(const UUID & id, EntityType type, const String { throw Exception( "ID {" + toString(id) + "}: " + outputEntityTypeAndName(type, name) + " expected to be of type " + toString(required_type), - ErrorCodes::BAD_CAST); + ErrorCodes::LOGICAL_ERROR); } diff --git a/src/AggregateFunctions/AggregateFunctionCount.h b/src/AggregateFunctions/AggregateFunctionCount.h index 092ffc6b6cf..e54f014f7a4 100644 --- a/src/AggregateFunctions/AggregateFunctionCount.h +++ b/src/AggregateFunctions/AggregateFunctionCount.h @@ -67,6 +67,12 @@ public: { data(place).count = new_count; } + + /// The function returns non-Nullable type even when wrapped with Null combinator. + bool returnDefaultWhenOnlyNull() const override + { + return true; + } }; diff --git a/src/AggregateFunctions/AggregateFunctionFactory.cpp b/src/AggregateFunctions/AggregateFunctionFactory.cpp index aeb4fb6db96..3982c48700b 100644 --- a/src/AggregateFunctions/AggregateFunctionFactory.cpp +++ b/src/AggregateFunctions/AggregateFunctionFactory.cpp @@ -63,14 +63,15 @@ AggregateFunctionPtr AggregateFunctionFactory::get( { auto type_without_low_cardinality = convertLowCardinalityTypesToNested(argument_types); - /// If one of types is Nullable, we apply aggregate function combinator "Null". + /// If one of the types is Nullable, we apply aggregate function combinator "Null". if (std::any_of(type_without_low_cardinality.begin(), type_without_low_cardinality.end(), [](const auto & type) { return type->isNullable(); })) { AggregateFunctionCombinatorPtr combinator = AggregateFunctionCombinatorFactory::instance().tryFindSuffix("Null"); if (!combinator) - throw Exception("Logical error: cannot find aggregate function combinator to apply a function to Nullable arguments.", ErrorCodes::LOGICAL_ERROR); + throw Exception("Logical error: cannot find aggregate function combinator to apply a function to Nullable arguments.", + ErrorCodes::LOGICAL_ERROR); DataTypes nested_types = combinator->transformArguments(type_without_low_cardinality); Array nested_parameters = combinator->transformParameters(parameters); @@ -132,9 +133,10 @@ AggregateFunctionPtr AggregateFunctionFactory::getImpl( auto hints = this->getHints(name); if (!hints.empty()) - throw Exception("Unknown aggregate function " + name + ". Maybe you meant: " + toString(hints), ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION); + throw Exception(fmt::format("Unknown aggregate function {}. Maybe you meant: {}", name, toString(hints)), + ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION); else - throw Exception("Unknown aggregate function " + name, ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION); + throw Exception(fmt::format("Unknown aggregate function {}", name), ErrorCodes::UNKNOWN_AGGREGATE_FUNCTION); } diff --git a/src/AggregateFunctions/AggregateFunctionMLMethod.h b/src/AggregateFunctions/AggregateFunctionMLMethod.h index ce4ef98e0cf..a11ca9032a5 100644 --- a/src/AggregateFunctions/AggregateFunctionMLMethod.h +++ b/src/AggregateFunctions/AggregateFunctionMLMethod.h @@ -15,7 +15,6 @@ namespace ErrorCodes { extern const int LOGICAL_ERROR; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; - extern const int BAD_CAST; } /** @@ -381,7 +380,7 @@ public: auto * column = typeid_cast(&to); if (!column) throw Exception("Cast of column of predictions is incorrect. getReturnTypeToPredict must return same value as it is casted to", - ErrorCodes::BAD_CAST); + ErrorCodes::LOGICAL_ERROR); this->data(place).predict(column->getData(), block, offset, limit, arguments, context); } diff --git a/src/AggregateFunctions/AggregateFunctionNull.cpp b/src/AggregateFunctions/AggregateFunctionNull.cpp index 60712636562..993cb93c991 100644 --- a/src/AggregateFunctions/AggregateFunctionNull.cpp +++ b/src/AggregateFunctions/AggregateFunctionNull.cpp @@ -33,6 +33,11 @@ public: AggregateFunctionPtr transformAggregateFunction( const AggregateFunctionPtr & nested_function, const DataTypes & arguments, const Array & params) const override { + /// Special case for 'count' function. It could be called with Nullable arguments + /// - that means - count number of calls, when all arguments are not NULL. + if (nested_function && nested_function->getName() == "count") + return std::make_shared(arguments[0], params); + bool has_nullable_types = false; bool has_null_types = false; for (const auto & arg_type : arguments) @@ -49,35 +54,47 @@ public: } if (!has_nullable_types) - throw Exception("Aggregate function combinator 'Null' requires at least one argument to be Nullable", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); - - if (nested_function) - if (auto adapter = nested_function->getOwnNullAdapter(nested_function, arguments, params)) - return adapter; - - /// Special case for 'count' function. It could be called with Nullable arguments - /// - that means - count number of calls, when all arguments are not NULL. - if (nested_function && nested_function->getName() == "count") - return std::make_shared(arguments[0], params); + throw Exception("Aggregate function combinator 'Null' requires at least one argument to be Nullable", + ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); if (has_null_types) return std::make_shared(arguments, params); - bool return_type_is_nullable = nested_function->getReturnType()->canBeInsideNullable(); + assert(nested_function); + + if (auto adapter = nested_function->getOwnNullAdapter(nested_function, arguments, params)) + return adapter; + + bool return_type_is_nullable = !nested_function->returnDefaultWhenOnlyNull() && nested_function->getReturnType()->canBeInsideNullable(); + bool serialize_flag = return_type_is_nullable || nested_function->returnDefaultWhenOnlyNull(); if (arguments.size() == 1) { if (return_type_is_nullable) - return std::make_shared>(nested_function, arguments, params); + { + return std::make_shared>(nested_function, arguments, params); + } else - return std::make_shared>(nested_function, arguments, params); + { + if (serialize_flag) + return std::make_shared>(nested_function, arguments, params); + else + return std::make_shared>(nested_function, arguments, params); + } } else { if (return_type_is_nullable) - return std::make_shared>(nested_function, arguments, params); + { + return std::make_shared>(nested_function, arguments, params); + } else - return std::make_shared>(nested_function, arguments, params); + { + if (serialize_flag) + return std::make_shared>(nested_function, arguments, params); + else + return std::make_shared>(nested_function, arguments, params); + } } } }; diff --git a/src/AggregateFunctions/AggregateFunctionNull.h b/src/AggregateFunctions/AggregateFunctionNull.h index 55d610207f1..d6f0079232c 100644 --- a/src/AggregateFunctions/AggregateFunctionNull.h +++ b/src/AggregateFunctions/AggregateFunctionNull.h @@ -28,7 +28,10 @@ namespace ErrorCodes /// If all rows had NULL, the behaviour is determined by "result_is_nullable" template parameter. /// true - return NULL; false - return value from empty aggregation state of nested function. -template +/// When serialize_flag is set to true, the flag about presense of values is serialized +/// regardless to the "result_is_nullable" even if it's unneeded - for protocol compatibility. + +template class AggregateFunctionNullBase : public IAggregateFunctionHelper { protected: @@ -129,7 +132,7 @@ public: void serialize(ConstAggregateDataPtr place, WriteBuffer & buf) const override { bool flag = getFlag(place); - if constexpr (result_is_nullable) + if constexpr (serialize_flag) writeBinary(flag, buf); if (flag) nested_function->serialize(nestedPlace(place), buf); @@ -138,7 +141,7 @@ public: void deserialize(AggregateDataPtr place, ReadBuffer & buf, Arena * arena) const override { bool flag = 1; - if constexpr (result_is_nullable) + if constexpr (serialize_flag) readBinary(flag, buf); if (flag) { @@ -183,12 +186,15 @@ public: /** There are two cases: for single argument and variadic. * Code for single argument is much more efficient. */ -template -class AggregateFunctionNullUnary final : public AggregateFunctionNullBase> +template +class AggregateFunctionNullUnary final + : public AggregateFunctionNullBase> { public: AggregateFunctionNullUnary(AggregateFunctionPtr nested_function_, const DataTypes & arguments, const Array & params) - : AggregateFunctionNullBase>(std::move(nested_function_), arguments, params) + : AggregateFunctionNullBase>(std::move(nested_function_), arguments, params) { } @@ -218,12 +224,15 @@ public: }; -template -class AggregateFunctionNullVariadic final : public AggregateFunctionNullBase> +template +class AggregateFunctionNullVariadic final + : public AggregateFunctionNullBase> { public: AggregateFunctionNullVariadic(AggregateFunctionPtr nested_function_, const DataTypes & arguments, const Array & params) - : AggregateFunctionNullBase>(std::move(nested_function_), arguments, params), + : AggregateFunctionNullBase>(std::move(nested_function_), arguments, params), number_of_arguments(arguments.size()) { if (number_of_arguments == 1) @@ -263,11 +272,6 @@ public: this->nested_function->add(this->nestedPlace(place), nested_columns, row_num, arena); } - bool allocatesMemoryInArena() const override - { - return this->nested_function->allocatesMemoryInArena(); - } - private: enum { MAX_ARGS = 8 }; size_t number_of_arguments = 0; diff --git a/src/AggregateFunctions/AggregateFunctionQuantile.cpp b/src/AggregateFunctions/AggregateFunctionQuantile.cpp index 993b25d6a6e..816fabad222 100644 --- a/src/AggregateFunctions/AggregateFunctionQuantile.cpp +++ b/src/AggregateFunctions/AggregateFunctionQuantile.cpp @@ -70,17 +70,17 @@ AggregateFunctionPtr createAggregateFunctionQuantile(const std::string & name, c WhichDataType which(argument_type); #define DISPATCH(TYPE) \ - if (which.idx == TypeIndex::TYPE) return std::make_shared>(argument_type, params); + if (which.idx == TypeIndex::TYPE) return std::make_shared>(argument_types, params); FOR_NUMERIC_TYPES(DISPATCH) #undef DISPATCH - if (which.idx == TypeIndex::Date) return std::make_shared>(argument_type, params); - if (which.idx == TypeIndex::DateTime) return std::make_shared>(argument_type, params); + if (which.idx == TypeIndex::Date) return std::make_shared>(argument_types, params); + if (which.idx == TypeIndex::DateTime) return std::make_shared>(argument_types, params); if constexpr (supportDecimal()) { - if (which.idx == TypeIndex::Decimal32) return std::make_shared>(argument_type, params); - if (which.idx == TypeIndex::Decimal64) return std::make_shared>(argument_type, params); - if (which.idx == TypeIndex::Decimal128) return std::make_shared>(argument_type, params); + if (which.idx == TypeIndex::Decimal32) return std::make_shared>(argument_types, params); + if (which.idx == TypeIndex::Decimal64) return std::make_shared>(argument_types, params); + if (which.idx == TypeIndex::Decimal128) return std::make_shared>(argument_types, params); } throw Exception("Illegal type " + argument_type->getName() + " of argument for aggregate function " + name, diff --git a/src/AggregateFunctions/AggregateFunctionQuantile.h b/src/AggregateFunctions/AggregateFunctionQuantile.h index cc90a22da81..7bdfc13295c 100644 --- a/src/AggregateFunctions/AggregateFunctionQuantile.h +++ b/src/AggregateFunctions/AggregateFunctionQuantile.h @@ -78,8 +78,8 @@ private: DataTypePtr & argument_type; public: - AggregateFunctionQuantile(const DataTypePtr & argument_type_, const Array & params) - : IAggregateFunctionDataHelper>({argument_type_}, params) + AggregateFunctionQuantile(const DataTypes & argument_types_, const Array & params) + : IAggregateFunctionDataHelper>(argument_types_, params) , levels(params, returns_many), level(levels.levels[0]), argument_type(this->argument_types[0]) { if (!returns_many && levels.size() > 1) diff --git a/src/AggregateFunctions/AggregateFunctionUniq.h b/src/AggregateFunctions/AggregateFunctionUniq.h index 334e809ebe7..1588611b8a2 100644 --- a/src/AggregateFunctions/AggregateFunctionUniq.h +++ b/src/AggregateFunctions/AggregateFunctionUniq.h @@ -244,6 +244,12 @@ public: { assert_cast(to).getData().push_back(this->data(place).set.size()); } + + /// The function returns non-Nullable type even when wrapped with Null combinator. + bool returnDefaultWhenOnlyNull() const override + { + return true; + } }; @@ -298,6 +304,12 @@ public: { assert_cast(to).getData().push_back(this->data(place).set.size()); } + + /// The function returns non-Nullable type even when wrapped with Null combinator. + bool returnDefaultWhenOnlyNull() const override + { + return true; + } }; } diff --git a/src/AggregateFunctions/AggregateFunctionWindowFunnel.h b/src/AggregateFunctions/AggregateFunctionWindowFunnel.h index 726656d1ca8..b5704203ade 100644 --- a/src/AggregateFunctions/AggregateFunctionWindowFunnel.h +++ b/src/AggregateFunctions/AggregateFunctionWindowFunnel.h @@ -240,9 +240,10 @@ public: return std::make_shared(); } - AggregateFunctionPtr getOwnNullAdapter(const AggregateFunctionPtr & nested_function, const DataTypes & arguments, const Array & params) const override + AggregateFunctionPtr getOwnNullAdapter( + const AggregateFunctionPtr & nested_function, const DataTypes & arguments, const Array & params) const override { - return std::make_shared>(nested_function, arguments, params); + return std::make_shared>(nested_function, arguments, params); } void add(AggregateDataPtr place, const IColumn ** columns, const size_t row_num, Arena *) const override diff --git a/src/AggregateFunctions/IAggregateFunction.h b/src/AggregateFunctions/IAggregateFunction.h index 1870eee07b8..439a5e07c2e 100644 --- a/src/AggregateFunctions/IAggregateFunction.h +++ b/src/AggregateFunctions/IAggregateFunction.h @@ -150,6 +150,8 @@ public: virtual void addBatchSinglePlaceNotNull( size_t batch_size, AggregateDataPtr place, const IColumn ** columns, const UInt8 * null_map, Arena * arena) const = 0; + virtual void addBatchSinglePlaceFromInterval(size_t batch_begin, size_t batch_end, AggregateDataPtr place, const IColumn ** columns, Arena * arena) const = 0; + /** In addition to addBatch, this method collects multiple rows of arguments into array "places" * as long as they are between offsets[i-1] and offsets[i]. This is used for arrayReduce and * -Array combinator. It might also be used generally to break data dependency when array @@ -169,6 +171,12 @@ public: return nullptr; } + /** When the function is wrapped with Null combinator, + * should we return Nullable type with NULL when no values were aggregated + * or we should return non-Nullable type with default value (example: count, countDistinct). + */ + virtual bool returnDefaultWhenOnlyNull() const { return false; } + const DataTypes & getArgumentTypes() const { return argument_types; } const Array & getParameters() const { return parameters; } @@ -214,6 +222,12 @@ public: static_cast(this)->add(place, columns, i, arena); } + void addBatchSinglePlaceFromInterval(size_t batch_begin, size_t batch_end, AggregateDataPtr place, const IColumn ** columns, Arena * arena) const override + { + for (size_t i = batch_begin; i < batch_end; ++i) + static_cast(this)->add(place, columns, i, arena); + } + void addBatchArray( size_t batch_size, AggregateDataPtr * places, size_t place_offset, const IColumn ** columns, const UInt64 * offsets, Arena * arena) const override diff --git a/src/AggregateFunctions/parseAggregateFunctionParameters.cpp b/src/AggregateFunctions/parseAggregateFunctionParameters.cpp index 2a6b9e3b499..27772c143e8 100644 --- a/src/AggregateFunctions/parseAggregateFunctionParameters.cpp +++ b/src/AggregateFunctions/parseAggregateFunctionParameters.cpp @@ -27,8 +27,12 @@ Array getAggregateFunctionParametersArray(const ASTPtr & expression_list, const const auto * literal = parameters[i]->as(); if (!literal) { - throw Exception("Parameters to aggregate functions must be literals" + (error_context.empty() ? "" : " (in " + error_context +")"), - ErrorCodes::PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS); + throw Exception( + ErrorCodes::PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS, + "Parameters to aggregate functions must be literals. " + "Got parameter '{}'{}", + parameters[i]->formatForErrorMessage(), + (error_context.empty() ? "" : " (in " + error_context +")")); } params_row[i] = literal->value; diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 13e8aac6906..fe223373cf3 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -161,12 +161,12 @@ add_object_library(clickhouse_processors_merges_algorithms Processors/Merges/Alg if (MAKE_STATIC_LIBRARIES OR NOT SPLIT_SHARED_LIBRARIES) add_library (dbms STATIC ${dbms_headers} ${dbms_sources}) - target_link_libraries (dbms PRIVATE jemalloc) + target_link_libraries (dbms PRIVATE jemalloc libdivide) set (all_modules dbms) else() add_library (dbms SHARED ${dbms_headers} ${dbms_sources}) target_link_libraries (dbms PUBLIC ${all_modules}) - target_link_libraries (clickhouse_interpreters PRIVATE jemalloc) + target_link_libraries (clickhouse_interpreters PRIVATE jemalloc libdivide) list (APPEND all_modules dbms) # force all split libs to be linked set (CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--no-as-needed") @@ -184,6 +184,9 @@ macro (dbms_target_link_libraries) endforeach () endmacro () +dbms_target_include_directories (PUBLIC ${ClickHouse_SOURCE_DIR}/src ${ClickHouse_BINARY_DIR}/src) +target_include_directories (clickhouse_common_io PUBLIC ${ClickHouse_SOURCE_DIR}/src ${ClickHouse_BINARY_DIR}/src) + if (USE_EMBEDDED_COMPILER) dbms_target_link_libraries (PRIVATE ${REQUIRED_LLVM_LIBRARIES}) dbms_target_include_directories (SYSTEM BEFORE PUBLIC ${LLVM_INCLUDE_DIRS}) @@ -240,8 +243,8 @@ target_link_libraries(clickhouse_common_io ${EXECINFO_LIBRARIES} cpuid PUBLIC - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${Boost_SYSTEM_LIBRARY} + boost::program_options + boost::system ${CITYHASH_LIBRARIES} ${ZLIB_LIBRARIES} pcg_random @@ -264,18 +267,18 @@ endif() dbms_target_link_libraries ( PRIVATE ${BTRIE_LIBRARIES} - ${Boost_PROGRAM_OPTIONS_LIBRARY} - ${Boost_FILESYSTEM_LIBRARY} - ${LZ4_LIBRARY} - clickhouse_parsers + boost::filesystem + boost::program_options clickhouse_common_config clickhouse_common_zookeeper clickhouse_dictionaries_embedded + clickhouse_parsers + lz4 Poco::JSON string_utils PUBLIC - ${Boost_SYSTEM_LIBRARY} ${MYSQLXX_LIBRARY} + boost::system clickhouse_common_io ) @@ -284,10 +287,6 @@ dbms_target_include_directories(PUBLIC ${CMAKE_CURRENT_BINARY_DIR}/Core/include) dbms_target_include_directories(SYSTEM BEFORE PUBLIC ${PDQSORT_INCLUDE_DIR}) -if (NOT USE_INTERNAL_LZ4_LIBRARY AND LZ4_INCLUDE_DIR) - dbms_target_include_directories(SYSTEM BEFORE PRIVATE ${LZ4_INCLUDE_DIR}) -endif () - if (ZSTD_LIBRARY) dbms_target_link_libraries(PRIVATE ${ZSTD_LIBRARY}) if (NOT USE_INTERNAL_ZSTD_LIBRARY AND ZSTD_INCLUDE_DIR) @@ -295,10 +294,6 @@ if (ZSTD_LIBRARY) endif () endif() -if (NOT USE_INTERNAL_BOOST_LIBRARY) - target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${Boost_INCLUDE_DIRS}) -endif () - if (USE_ICU) dbms_target_link_libraries (PRIVATE ${ICU_LIBRARIES}) dbms_target_include_directories (SYSTEM PRIVATE ${ICU_INCLUDE_DIRS}) @@ -329,8 +324,6 @@ if (USE_LDAP) dbms_target_include_directories (SYSTEM BEFORE PRIVATE ${OPENLDAP_INCLUDE_DIR}) dbms_target_link_libraries (PRIVATE ${OPENLDAP_LIBRARIES}) endif () - -dbms_target_include_directories (SYSTEM BEFORE PRIVATE ${DIVIDE_INCLUDE_DIR}) dbms_target_include_directories (SYSTEM BEFORE PRIVATE ${SPARSEHASH_INCLUDE_DIR}) if (USE_PROTOBUF) @@ -359,8 +352,10 @@ if (USE_OPENCL) target_include_directories (clickhouse_common_io SYSTEM BEFORE PRIVATE ${OpenCL_INCLUDE_DIRS}) endif () -dbms_target_include_directories (PUBLIC ${DBMS_INCLUDE_DIR}) -target_include_directories (clickhouse_common_io PUBLIC ${DBMS_INCLUDE_DIR}) +if (USE_CASSANDRA) + dbms_target_link_libraries(PUBLIC ${CASSANDRA_LIBRARY}) + dbms_target_include_directories (SYSTEM BEFORE PUBLIC ${CASS_INCLUDE_DIR}) +endif() target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${DOUBLE_CONVERSION_INCLUDE_DIR}) diff --git a/src/Columns/ColumnAggregateFunction.cpp b/src/Columns/ColumnAggregateFunction.cpp index 3b1f99bc5be..d4021b45f0e 100644 --- a/src/Columns/ColumnAggregateFunction.cpp +++ b/src/Columns/ColumnAggregateFunction.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -27,6 +28,51 @@ namespace ErrorCodes } +static std::string getTypeString(const AggregateFunctionPtr & func) +{ + WriteBufferFromOwnString stream; + stream << "AggregateFunction(" << func->getName(); + const auto & parameters = func->getParameters(); + const auto & argument_types = func->getArgumentTypes(); + + if (!parameters.empty()) + { + stream << '('; + for (size_t i = 0; i < parameters.size(); ++i) + { + if (i) + stream << ", "; + stream << applyVisitor(FieldVisitorToString(), parameters[i]); + } + stream << ')'; + } + + for (const auto & argument_type : argument_types) + stream << ", " << argument_type->getName(); + + stream << ')'; + return stream.str(); +} + + +ColumnAggregateFunction::ColumnAggregateFunction(const AggregateFunctionPtr & func_) + : func(func_), type_string(getTypeString(func)) +{ +} + +ColumnAggregateFunction::ColumnAggregateFunction(const AggregateFunctionPtr & func_, const ConstArenas & arenas_) + : foreign_arenas(arenas_), func(func_), type_string(getTypeString(func)) +{ + +} + +void ColumnAggregateFunction::set(const AggregateFunctionPtr & func_) +{ + func = func_; + type_string = getTypeString(func); +} + + ColumnAggregateFunction::~ColumnAggregateFunction() { if (!func->hasTrivialDestructor() && !src) @@ -59,7 +105,7 @@ MutableColumnPtr ColumnAggregateFunction::convertToValues(MutableColumnPtr colum * Due to the presence of WITH TOTALS, during aggregation the states of this aggregate function will be stored * in the ColumnAggregateFunction column of type * AggregateFunction(quantileTimingState(0.5), UInt64). - * Then, in `TotalsHavingBlockInputStream`, it will be called `convertToValues` method, + * Then, in `TotalsHavingTransform`, it will be called `convertToValues` method, * to get the "ready" values. * But it just converts a column of type * `AggregateFunction(quantileTimingState(0.5), UInt64)` @@ -336,15 +382,10 @@ MutableColumnPtr ColumnAggregateFunction::cloneEmpty() const return create(func); } -String ColumnAggregateFunction::getTypeString() const -{ - return DataTypeAggregateFunction(func, func->getArgumentTypes(), func->getParameters()).getName(); -} - Field ColumnAggregateFunction::operator[](size_t n) const { Field field = AggregateFunctionStateData(); - field.get().name = getTypeString(); + field.get().name = type_string; { WriteBufferFromString buffer(field.get().data); func->serialize(data[n], buffer); @@ -355,7 +396,7 @@ Field ColumnAggregateFunction::operator[](size_t n) const void ColumnAggregateFunction::get(size_t n, Field & res) const { res = AggregateFunctionStateData(); - res.get().name = getTypeString(); + res.get().name = type_string; { WriteBufferFromString buffer(res.get().data); func->serialize(data[n], buffer); @@ -425,8 +466,6 @@ static void pushBackAndCreateState(ColumnAggregateFunction::Container & data, Ar void ColumnAggregateFunction::insert(const Field & x) { - String type_string = getTypeString(); - if (x.getType() != Field::Types::AggregateFunctionState) throw Exception(String("Inserting field of type ") + x.getTypeName() + " into ColumnAggregateFunction. " "Expected " + Field::Types::toString(Field::Types::AggregateFunctionState), ErrorCodes::LOGICAL_ERROR); @@ -564,7 +603,7 @@ void ColumnAggregateFunction::getExtremes(Field & min, Field & max) const AggregateDataPtr place = place_buffer.data(); AggregateFunctionStateData serialized; - serialized.name = getTypeString(); + serialized.name = type_string; func->create(place); try diff --git a/src/Columns/ColumnAggregateFunction.h b/src/Columns/ColumnAggregateFunction.h index 40f73665ebe..a9b3c38a2e0 100644 --- a/src/Columns/ColumnAggregateFunction.h +++ b/src/Columns/ColumnAggregateFunction.h @@ -74,6 +74,9 @@ private: /// Array of pointers to aggregation states, that are placed in arenas. Container data; + /// Name of the type to distinguish different aggregation states. + String type_string; + ColumnAggregateFunction() {} /// Create a new column that has another column as a source. @@ -84,29 +87,17 @@ private: /// but ownership of different elements cannot be mixed by different columns. void ensureOwnership(); - ColumnAggregateFunction(const AggregateFunctionPtr & func_) - : func(func_) - { - } + ColumnAggregateFunction(const AggregateFunctionPtr & func_); ColumnAggregateFunction(const AggregateFunctionPtr & func_, - const ConstArenas & arenas_) - : foreign_arenas(arenas_), func(func_) - { - } - + const ConstArenas & arenas_); ColumnAggregateFunction(const ColumnAggregateFunction & src_); - String getTypeString() const; - public: ~ColumnAggregateFunction() override; - void set(const AggregateFunctionPtr & func_) - { - func = func_; - } + void set(const AggregateFunctionPtr & func_); AggregateFunctionPtr getAggregateFunction() { return func; } AggregateFunctionPtr getAggregateFunction() const { return func; } @@ -121,6 +112,7 @@ public: std::string getName() const override { return "AggregateFunction(" + func->getName() + ")"; } const char * getFamilyName() const override { return "AggregateFunction"; } + TypeIndex getDataType() const override { return TypeIndex::AggregateFunction; } MutableColumnPtr predictValues(Block & block, const ColumnNumbers & arguments, const Context & context) const; diff --git a/src/Columns/ColumnArray.h b/src/Columns/ColumnArray.h index 55935a91cde..a20165826bb 100644 --- a/src/Columns/ColumnArray.h +++ b/src/Columns/ColumnArray.h @@ -52,6 +52,7 @@ public: std::string getName() const override; const char * getFamilyName() const override { return "Array"; } + TypeIndex getDataType() const override { return TypeIndex::Array; } MutableColumnPtr cloneResized(size_t size) const override; size_t size() const override; Field operator[](size_t n) const override; diff --git a/src/Columns/ColumnConst.h b/src/Columns/ColumnConst.h index 5fc96b14be8..02dfcc5b620 100644 --- a/src/Columns/ColumnConst.h +++ b/src/Columns/ColumnConst.h @@ -50,6 +50,11 @@ public: return "Const"; } + TypeIndex getDataType() const override + { + return data->getDataType(); + } + MutableColumnPtr cloneResized(size_t new_size) const override { return ColumnConst::create(data, new_size); diff --git a/src/Columns/ColumnDecimal.cpp b/src/Columns/ColumnDecimal.cpp index 1c238cc6458..3e6fb833b56 100644 --- a/src/Columns/ColumnDecimal.cpp +++ b/src/Columns/ColumnDecimal.cpp @@ -333,17 +333,6 @@ void ColumnDecimal::getExtremes(Field & min, Field & max) const max = NearestFieldType(cur_max, scale); } -TypeIndex columnDecimalDataType(const IColumn * column) -{ - if (checkColumn>(column)) - return TypeIndex::Decimal32; - else if (checkColumn>(column)) - return TypeIndex::Decimal64; - else if (checkColumn>(column)) - return TypeIndex::Decimal128; - return TypeIndex::Nothing; -} - template class ColumnDecimal; template class ColumnDecimal; template class ColumnDecimal; diff --git a/src/Columns/ColumnDecimal.h b/src/Columns/ColumnDecimal.h index 16c6a47c30a..37d85b05d4c 100644 --- a/src/Columns/ColumnDecimal.h +++ b/src/Columns/ColumnDecimal.h @@ -81,6 +81,7 @@ private: public: const char * getFamilyName() const override { return TypeName::get(); } + TypeIndex getDataType() const override { return TypeId::value; } bool isNumeric() const override { return false; } bool canBeInsideNullable() const override { return true; } @@ -197,6 +198,4 @@ ColumnPtr ColumnDecimal::indexImpl(const PaddedPODArray & indexes, size return res; } -TypeIndex columnDecimalDataType(const IColumn * column); - } diff --git a/src/Columns/ColumnFixedString.h b/src/Columns/ColumnFixedString.h index 996a1f99ef1..6b7f1ecf793 100644 --- a/src/Columns/ColumnFixedString.h +++ b/src/Columns/ColumnFixedString.h @@ -43,6 +43,7 @@ private: public: std::string getName() const override { return "FixedString(" + std::to_string(n) + ")"; } const char * getFamilyName() const override { return "FixedString"; } + TypeIndex getDataType() const override { return TypeIndex::FixedString; } MutableColumnPtr cloneResized(size_t size) const override; diff --git a/src/Columns/ColumnFunction.h b/src/Columns/ColumnFunction.h index 31cb8708a6e..267f3c7285a 100644 --- a/src/Columns/ColumnFunction.h +++ b/src/Columns/ColumnFunction.h @@ -29,6 +29,7 @@ private: public: const char * getFamilyName() const override { return "Function"; } + TypeIndex getDataType() const override { return TypeIndex::Function; } MutableColumnPtr cloneResized(size_t size) const override; diff --git a/src/Columns/ColumnLowCardinality.h b/src/Columns/ColumnLowCardinality.h index 905d15f8167..1e6319a2cb1 100644 --- a/src/Columns/ColumnLowCardinality.h +++ b/src/Columns/ColumnLowCardinality.h @@ -39,6 +39,7 @@ public: std::string getName() const override { return "ColumnLowCardinality"; } const char * getFamilyName() const override { return "ColumnLowCardinality"; } + TypeIndex getDataType() const override { return TypeIndex::LowCardinality; } ColumnPtr convertToFullColumn() const { return getDictionary().getNestedColumn()->index(getIndexes(), 0); } ColumnPtr convertToFullColumnIfLowCardinality() const override { return convertToFullColumn(); } diff --git a/src/Columns/ColumnNothing.h b/src/Columns/ColumnNothing.h index 691143e2c15..c2738bb4cdc 100644 --- a/src/Columns/ColumnNothing.h +++ b/src/Columns/ColumnNothing.h @@ -21,6 +21,7 @@ private: public: const char * getFamilyName() const override { return "Nothing"; } MutableColumnPtr cloneDummy(size_t s_) const override { return ColumnNothing::create(s_); } + TypeIndex getDataType() const override { return TypeIndex::Nothing; } bool canBeInsideNullable() const override { return true; } diff --git a/src/Columns/ColumnNullable.h b/src/Columns/ColumnNullable.h index 2cd8ff9f40f..a8f226ed37d 100644 --- a/src/Columns/ColumnNullable.h +++ b/src/Columns/ColumnNullable.h @@ -45,6 +45,7 @@ public: const char * getFamilyName() const override { return "Nullable"; } std::string getName() const override { return "Nullable(" + nested_column->getName() + ")"; } + TypeIndex getDataType() const override { return TypeIndex::Nullable; } MutableColumnPtr cloneResized(size_t size) const override; size_t size() const override { return nested_column->size(); } bool isNullAt(size_t n) const override { return assert_cast(*null_map).getData()[n] != 0;} diff --git a/src/Columns/ColumnSet.h b/src/Columns/ColumnSet.h index b30ba86fafe..316f8196e5a 100644 --- a/src/Columns/ColumnSet.h +++ b/src/Columns/ColumnSet.h @@ -25,6 +25,7 @@ private: public: const char * getFamilyName() const override { return "Set"; } + TypeIndex getDataType() const override { return TypeIndex::Set; } MutableColumnPtr cloneDummy(size_t s_) const override { return ColumnSet::create(s_, data); } ConstSetPtr getData() const { return data; } diff --git a/src/Columns/ColumnString.h b/src/Columns/ColumnString.h index a0b3d259b67..f067bce47bc 100644 --- a/src/Columns/ColumnString.h +++ b/src/Columns/ColumnString.h @@ -56,6 +56,7 @@ private: public: const char * getFamilyName() const override { return "String"; } + TypeIndex getDataType() const override { return TypeIndex::String; } size_t size() const override { diff --git a/src/Columns/ColumnTuple.h b/src/Columns/ColumnTuple.h index 69b18e2fc0f..33c48a0cdd1 100644 --- a/src/Columns/ColumnTuple.h +++ b/src/Columns/ColumnTuple.h @@ -40,6 +40,7 @@ public: std::string getName() const override; const char * getFamilyName() const override { return "Tuple"; } + TypeIndex getDataType() const override { return TypeIndex::Tuple; } MutableColumnPtr cloneEmpty() const override; MutableColumnPtr cloneResized(size_t size) const override; diff --git a/src/Columns/ColumnVector.cpp b/src/Columns/ColumnVector.cpp index 95efd0dedad..50f1dba4fdb 100644 --- a/src/Columns/ColumnVector.cpp +++ b/src/Columns/ColumnVector.cpp @@ -289,13 +289,6 @@ void ColumnVector::updatePermutation(bool reverse, size_t limit, int nan_dire equal_range = std::move(new_ranges); } - -template -const char * ColumnVector::getFamilyName() const -{ - return TypeName::get(); -} - template MutableColumnPtr ColumnVector::cloneResized(size_t size) const { @@ -517,33 +510,6 @@ void ColumnVector::getExtremes(Field & min, Field & max) const max = NearestFieldType(cur_max); } -TypeIndex columnVectorDataType(const IColumn * column) -{ - if (checkColumn>(column)) - return TypeIndex::UInt8; - else if (checkColumn>(column)) - return TypeIndex::UInt16; - else if (checkColumn>(column)) - return TypeIndex::UInt32; - else if (checkColumn>(column)) - return TypeIndex::UInt64; - else if (checkColumn>(column)) - return TypeIndex::Int8; - else if (checkColumn>(column)) - return TypeIndex::Int16; - else if (checkColumn>(column)) - return TypeIndex::Int32; - else if (checkColumn>(column)) - return TypeIndex::Int64; - else if (checkColumn>(column)) - return TypeIndex::Int128; - else if (checkColumn>(column)) - return TypeIndex::Float32; - else if (checkColumn>(column)) - return TypeIndex::Float64; - return TypeIndex::Nothing; -} - /// Explicit template instantiations - to avoid code bloat in headers. template class ColumnVector; template class ColumnVector; diff --git a/src/Columns/ColumnVector.h b/src/Columns/ColumnVector.h index 5e934b42df0..b9b14f4b2a1 100644 --- a/src/Columns/ColumnVector.h +++ b/src/Columns/ColumnVector.h @@ -199,7 +199,8 @@ public: data.reserve(n); } - const char * getFamilyName() const override; + const char * getFamilyName() const override { return TypeName::get(); } + TypeIndex getDataType() const override { return TypeId::value; } MutableColumnPtr cloneResized(size_t size) const override; @@ -320,6 +321,4 @@ ColumnPtr ColumnVector::indexImpl(const PaddedPODArray & indexes, size_ return res; } -TypeIndex columnVectorDataType(const IColumn * column); - } diff --git a/src/Columns/IColumn.h b/src/Columns/IColumn.h index 1d92ed1c3ab..c227ec97e3a 100644 --- a/src/Columns/IColumn.h +++ b/src/Columns/IColumn.h @@ -51,6 +51,9 @@ public: /// Name of a Column kind, without parameters (example: FixedString, Array). virtual const char * getFamilyName() const = 0; + /// Type of data that column contains. It's an underlying type: UInt16 for Date, UInt32 for DateTime, so on. + virtual TypeIndex getDataType() const = 0; + /** If column isn't constant, returns itself. * If column is constant, transforms constant to full column (if column type allows such transform) and return it. */ diff --git a/src/Columns/IColumnUnique.h b/src/Columns/IColumnUnique.h index af5d9878a3b..693ed18b87e 100644 --- a/src/Columns/IColumnUnique.h +++ b/src/Columns/IColumnUnique.h @@ -66,6 +66,7 @@ public: virtual UInt128 getHash() const = 0; const char * getFamilyName() const override { return "ColumnUnique"; } + TypeIndex getDataType() const override { return getNestedColumn()->getDataType(); } void insert(const Field &) override { diff --git a/src/Common/Arena.h b/src/Common/Arena.h index f1d42e53345..d203a92d4a3 100644 --- a/src/Common/Arena.h +++ b/src/Common/Arena.h @@ -150,7 +150,7 @@ public: return res; } - /// Get peice of memory with alignment + /// Get piece of memory with alignment char * alignedAlloc(size_t size, size_t alignment) { do diff --git a/src/Common/Config/CMakeLists.txt b/src/Common/Config/CMakeLists.txt index 44e74fb30b5..a7914fb17ec 100644 --- a/src/Common/Config/CMakeLists.txt +++ b/src/Common/Config/CMakeLists.txt @@ -7,12 +7,11 @@ set (SRCS add_library(clickhouse_common_config ${SRCS}) -target_include_directories(clickhouse_common_config PUBLIC ${DBMS_INCLUDE_DIR}) target_link_libraries(clickhouse_common_config PUBLIC + clickhouse_common_zookeeper common Poco::XML PRIVATE - clickhouse_common_zookeeper string_utils ) diff --git a/src/Common/DNSResolver.cpp b/src/Common/DNSResolver.cpp index c5b4fd0c585..2f530f2f2de 100644 --- a/src/Common/DNSResolver.cpp +++ b/src/Common/DNSResolver.cpp @@ -4,20 +4,32 @@ #include #include #include +#include #include #include #include -#include -#include #include #include #include +#include namespace ProfileEvents { extern Event DNSError; } +namespace std +{ +template<> struct hash +{ + size_t operator()(const Poco::Net::IPAddress & address) const noexcept + { + std::string_view addr(static_cast(address.addr()), address.length()); + std::hash hash_impl; + return hash_impl(addr); + } +}; +} namespace DB { @@ -25,6 +37,7 @@ namespace DB namespace ErrorCodes { extern const int BAD_ARGUMENTS; + extern const int DNS_ERROR; } @@ -76,16 +89,48 @@ static void splitHostAndPort(const std::string & host_and_port, std::string & ou } } -static Poco::Net::IPAddress resolveIPAddressImpl(const std::string & host) +static DNSResolver::IPAddresses resolveIPAddressImpl(const std::string & host) { + Poco::Net::IPAddress ip; + /// NOTE: Poco::Net::DNS::resolveOne(host) doesn't work for IP addresses like 127.0.0.2 - /// Therefore we use SocketAddress constructor with dummy port to resolve IP - return Poco::Net::SocketAddress(host, 0U).host(); + if (Poco::Net::IPAddress::tryParse(host, ip)) + return DNSResolver::IPAddresses(1, ip); + + /// Family: AF_UNSPEC + /// AI_ALL is required for checking if client is allowed to connect from an address + auto flags = Poco::Net::DNS::DNS_HINT_AI_V4MAPPED | Poco::Net::DNS::DNS_HINT_AI_ALL; + /// Do not resolve IPv6 (or IPv4) if no local IPv6 (or IPv4) addresses are configured. + /// It should not affect client address checking, since client cannot connect from IPv6 address + /// if server has no IPv6 addresses. + flags |= Poco::Net::DNS::DNS_HINT_AI_ADDRCONFIG; +#if defined(ARCADIA_BUILD) + auto addresses = Poco::Net::DNS::hostByName(host, &Poco::Net::DNS::DEFAULT_DNS_TIMEOUT, flags).addresses(); +#else + auto addresses = Poco::Net::DNS::hostByName(host, flags).addresses(); +#endif + if (addresses.empty()) + throw Exception("Not found address of host: " + host, ErrorCodes::DNS_ERROR); + + return addresses; +} + +static String reverseResolveImpl(const Poco::Net::IPAddress & address) +{ + Poco::Net::SocketAddress sock_addr(address, 0); + + /// Resolve by hand, because Poco::Net::DNS::hostByAddress(...) does getaddrinfo(...) after getnameinfo(...) + char host[1024]; + int err = getnameinfo(sock_addr.addr(), sock_addr.length(), host, sizeof(host), nullptr, 0, NI_NAMEREQD); + if (err) + throw Exception("Cannot getnameinfo(" + address.toString() + "): " + gai_strerror(err), ErrorCodes::DNS_ERROR); + return host; } struct DNSResolver::Impl { SimpleCache cache_host; + SimpleCache cache_address; std::mutex drop_mutex; std::mutex update_mutex; @@ -95,18 +140,25 @@ struct DNSResolver::Impl /// Store hosts, which was asked to resolve from last update of DNS cache. NameSet new_hosts; + std::unordered_set new_addresses; /// Store all hosts, which was whenever asked to resolve NameSet known_hosts; + std::unordered_set known_addresses; /// If disabled, will not make cache lookups, will resolve addresses manually on each call std::atomic disable_cache{false}; }; -DNSResolver::DNSResolver() : impl(std::make_unique()) {} +DNSResolver::DNSResolver() : impl(std::make_unique()), log(&Poco::Logger::get("DNSResolver")) {} Poco::Net::IPAddress DNSResolver::resolveHost(const std::string & host) +{ + return resolveHostAll(host).front(); +} + +DNSResolver::IPAddresses DNSResolver::resolveHostAll(const std::string & host) { if (impl->disable_cache) return resolveIPAddressImpl(host); @@ -125,7 +177,7 @@ Poco::Net::SocketAddress DNSResolver::resolveAddress(const std::string & host_an splitHostAndPort(host_and_port, host, port); addToNewHosts(host); - return Poco::Net::SocketAddress(impl->cache_host(host), port); + return Poco::Net::SocketAddress(impl->cache_host(host).front(), port); } Poco::Net::SocketAddress DNSResolver::resolveAddress(const std::string & host, UInt16 port) @@ -134,17 +186,29 @@ Poco::Net::SocketAddress DNSResolver::resolveAddress(const std::string & host, U return Poco::Net::SocketAddress(host, port); addToNewHosts(host); - return Poco::Net::SocketAddress(impl->cache_host(host), port); + return Poco::Net::SocketAddress(impl->cache_host(host).front(), port); +} + +String DNSResolver::reverseResolve(const Poco::Net::IPAddress & address) +{ + if (impl->disable_cache) + return reverseResolveImpl(address); + + addToNewAddresses(address); + return impl->cache_address(address); } void DNSResolver::dropCache() { impl->cache_host.drop(); + impl->cache_address.drop(); std::scoped_lock lock(impl->update_mutex, impl->drop_mutex); impl->known_hosts.clear(); + impl->known_addresses.clear(); impl->new_hosts.clear(); + impl->new_addresses.clear(); impl->host_name.reset(); } @@ -166,34 +230,27 @@ String DNSResolver::getHostName() return *impl->host_name; } -bool DNSResolver::updateCache() +static const String & cacheElemToString(const String & str) { return str; } +static String cacheElemToString(const Poco::Net::IPAddress & addr) { return addr.toString(); } + +template +bool DNSResolver::updateCacheImpl(UpdateF && update_func, ElemsT && elems, const String & log_msg) { - { - std::lock_guard lock(impl->drop_mutex); - for (const auto & host : impl->new_hosts) - impl->known_hosts.insert(host); - impl->new_hosts.clear(); - - impl->host_name.emplace(Poco::Net::DNS::hostName()); - } - - std::lock_guard lock(impl->update_mutex); - bool updated = false; - String lost_hosts; - for (const auto & host : impl->known_hosts) + String lost_elems; + for (const auto & elem : elems) { try { - updated |= updateHost(host); + updated |= (this->*update_func)(elem); } catch (const Poco::Net::NetException &) { ProfileEvents::increment(ProfileEvents::DNSError); - if (!lost_hosts.empty()) - lost_hosts += ", "; - lost_hosts += host; + if (!lost_elems.empty()) + lost_elems += ", "; + lost_elems += cacheElemToString(elem); } catch (...) { @@ -201,12 +258,41 @@ bool DNSResolver::updateCache() } } - if (!lost_hosts.empty()) - LOG_INFO(&Poco::Logger::get("DNSResolver"), "Cached hosts not found: {}", lost_hosts); + if (!lost_elems.empty()) + LOG_INFO(log, log_msg, lost_elems); return updated; } +bool DNSResolver::updateCache() +{ + LOG_DEBUG(log, "Updating DNS cache"); + + { + std::lock_guard lock(impl->drop_mutex); + + for (const auto & host : impl->new_hosts) + impl->known_hosts.insert(host); + impl->new_hosts.clear(); + + for (const auto & address : impl->new_addresses) + impl->known_addresses.insert(address); + impl->new_addresses.clear(); + + impl->host_name.emplace(Poco::Net::DNS::hostName()); + } + + /// FIXME Updating may take a long time becouse we cannot manage timeouts of getaddrinfo(...) and getnameinfo(...). + /// DROP DNS CACHE will wait on update_mutex (possibly while holding drop_mutex) + std::lock_guard lock(impl->update_mutex); + + bool hosts_updated = updateCacheImpl(&DNSResolver::updateHost, impl->known_hosts, "Cached hosts not found: {}"); + updateCacheImpl(&DNSResolver::updateAddress, impl->known_addresses, "Cached addresses not found: {}"); + + LOG_DEBUG(log, "Updated DNS cache"); + return hosts_updated; +} + bool DNSResolver::updateHost(const String & host) { /// Usage of updateHost implies that host is already in cache and there is no extra computations @@ -215,12 +301,25 @@ bool DNSResolver::updateHost(const String & host) return old_value != impl->cache_host(host); } +bool DNSResolver::updateAddress(const Poco::Net::IPAddress & address) +{ + auto old_value = impl->cache_address(address); + impl->cache_address.update(address); + return old_value == impl->cache_address(address); +} + void DNSResolver::addToNewHosts(const String & host) { std::lock_guard lock(impl->drop_mutex); impl->new_hosts.insert(host); } +void DNSResolver::addToNewAddresses(const Poco::Net::IPAddress & address) +{ + std::lock_guard lock(impl->drop_mutex); + impl->new_addresses.insert(address); +} + DNSResolver::~DNSResolver() = default; DNSResolver & DNSResolver::instance() diff --git a/src/Common/DNSResolver.h b/src/Common/DNSResolver.h index 7dfbe49ab77..7dbc2852d43 100644 --- a/src/Common/DNSResolver.h +++ b/src/Common/DNSResolver.h @@ -5,6 +5,7 @@ #include #include #include +#include namespace DB @@ -16,18 +17,26 @@ namespace DB class DNSResolver : private boost::noncopyable { public: + typedef std::vector IPAddresses; + static DNSResolver & instance(); DNSResolver(const DNSResolver &) = delete; - /// Accepts host names like 'example.com' or '127.0.0.1' or '::1' and resolve its IP + /// Accepts host names like 'example.com' or '127.0.0.1' or '::1' and resolves its IP Poco::Net::IPAddress resolveHost(const std::string & host); - /// Accepts host names like 'example.com:port' or '127.0.0.1:port' or '[::1]:port' and resolve its IP and port + /// Accepts host names like 'example.com' or '127.0.0.1' or '::1' and resolves all its IPs + IPAddresses resolveHostAll(const std::string & host); + + /// Accepts host names like 'example.com:port' or '127.0.0.1:port' or '[::1]:port' and resolves its IP and port Poco::Net::SocketAddress resolveAddress(const std::string & host_and_port); Poco::Net::SocketAddress resolveAddress(const std::string & host, UInt16 port); + /// Accepts host IP and resolves its host name + String reverseResolve(const Poco::Net::IPAddress & address); + /// Get this server host name String getHostName(); @@ -44,16 +53,21 @@ public: ~DNSResolver(); private: + template + bool updateCacheImpl(UpdateF && update_func, ElemsT && elems, const String & log_msg); DNSResolver(); struct Impl; std::unique_ptr impl; + Poco::Logger * log; - /// Returns true if IP of host has been changed. + /// Updates cached value and returns true it has been changed. bool updateHost(const String & host); + bool updateAddress(const Poco::Net::IPAddress & address); void addToNewHosts(const String & host); + void addToNewAddresses(const Poco::Net::IPAddress & address); }; } diff --git a/src/Common/Elf.cpp b/src/Common/Elf.cpp index 11d454abd71..ee32586d95e 100644 --- a/src/Common/Elf.cpp +++ b/src/Common/Elf.cpp @@ -54,6 +54,18 @@ Elf::Elf(const std::string & path) throw Exception("The ELF is truncated (section names string table points after end of file)", ErrorCodes::CANNOT_PARSE_ELF); section_names = reinterpret_cast(mapped + section_names_offset); + + /// Get program headers + + ElfOff program_header_offset = header->e_phoff; + uint16_t program_header_num_entries = header->e_phnum; + + if (!program_header_offset + || !program_header_num_entries + || program_header_offset + program_header_num_entries * sizeof(ElfPhdr) > elf_size) + throw Exception("The ELF is truncated (program header points after end of file)", ErrorCodes::CANNOT_PARSE_ELF); + + program_headers = reinterpret_cast(mapped + program_header_offset); } @@ -104,6 +116,40 @@ std::optional Elf::findSectionByName(const char * name) const } +String Elf::getBuildID() const +{ + for (size_t idx = 0; idx < header->e_phnum; ++idx) + { + const ElfPhdr & phdr = program_headers[idx]; + + if (phdr.p_type == PT_NOTE) + return getBuildID(mapped + phdr.p_offset, phdr.p_filesz); + } + return {}; +} + + +String Elf::getBuildID(const char * nhdr_pos, size_t size) +{ + const char * nhdr_end = nhdr_pos + size; + + while (nhdr_pos < nhdr_end) + { + const ElfNhdr & nhdr = *reinterpret_cast(nhdr_pos); + + nhdr_pos += sizeof(ElfNhdr) + nhdr.n_namesz; + if (nhdr.n_type == NT_GNU_BUILD_ID) + { + const char * build_id = nhdr_pos; + return {build_id, nhdr.n_descsz}; + } + nhdr_pos += nhdr.n_descsz; + } + + return {}; +} + + const char * Elf::Section::name() const { if (!elf.section_names) diff --git a/src/Common/Elf.h b/src/Common/Elf.h index f3aafc8e5a9..632d7e6f0b1 100644 --- a/src/Common/Elf.h +++ b/src/Common/Elf.h @@ -17,6 +17,7 @@ using ElfEhdr = ElfW(Ehdr); using ElfOff = ElfW(Off); using ElfPhdr = ElfW(Phdr); using ElfShdr = ElfW(Shdr); +using ElfNhdr = ElfW(Nhdr); using ElfSym = ElfW(Sym); @@ -53,12 +54,18 @@ public: const char * end() const { return mapped + elf_size; } size_t size() const { return elf_size; } + /// Obtain build id from PT_NOTES section of program headers. Return empty string if does not exist. + /// The string is returned in binary. Note that "readelf -n ./clickhouse-server" prints it in hex. + String getBuildID() const; + static String getBuildID(const char * nhdr_pos, size_t size); + private: MMapReadBufferFromFile in; size_t elf_size; const char * mapped; const ElfEhdr * header; const ElfShdr * section_headers; + const ElfPhdr * program_headers; const char * section_names = nullptr; }; diff --git a/src/Common/ErrorCodes.cpp b/src/Common/ErrorCodes.cpp index cb4c591041c..694f0979f63 100644 --- a/src/Common/ErrorCodes.cpp +++ b/src/Common/ErrorCodes.cpp @@ -341,7 +341,6 @@ namespace ErrorCodes extern const int OUTPUT_IS_NOT_SORTED = 365; extern const int SIZES_OF_NESTED_COLUMNS_ARE_INCONSISTENT = 366; extern const int TOO_MANY_FETCHES = 367; - extern const int BAD_CAST = 368; extern const int ALL_REPLICAS_ARE_STALE = 369; extern const int DATA_TYPE_CANNOT_BE_USED_IN_TABLES = 370; extern const int INCONSISTENT_CLUSTER_DEFINITION = 371; @@ -398,7 +397,6 @@ namespace ErrorCodes extern const int CANNOT_GETTIMEOFDAY = 423; extern const int CANNOT_LINK = 424; extern const int SYSTEM_ERROR = 425; - extern const int NULL_POINTER_DEREFERENCE = 426; extern const int CANNOT_COMPILE_REGEXP = 427; extern const int UNKNOWN_LOG_LEVEL = 428; extern const int FAILED_TO_GETPWUID = 429; @@ -458,7 +456,6 @@ namespace ErrorCodes extern const int TOO_MANY_REDIRECTS = 483; extern const int INTERNAL_REDIS_ERROR = 484; extern const int SCALAR_ALREADY_EXISTS = 485; - extern const int UNKNOWN_SCALAR = 486; extern const int CANNOT_GET_CREATE_DICTIONARY_QUERY = 487; extern const int UNKNOWN_DICTIONARY = 488; extern const int INCORRECT_DICTIONARY_DEFINITION = 489; @@ -498,6 +495,7 @@ namespace ErrorCodes extern const int ALTER_OF_COLUMN_IS_FORBIDDEN = 524; extern const int INCORRECT_DISK_INDEX = 525; extern const int UNKNOWN_VOLUME_TYPE = 526; + extern const int CASSANDRA_INTERNAL_ERROR = 527; extern const int KEEPER_EXCEPTION = 999; extern const int POCO_EXCEPTION = 1000; diff --git a/src/Common/Exception.cpp b/src/Common/Exception.cpp index b0c897127c6..f2470ea0406 100644 --- a/src/Common/Exception.cpp +++ b/src/Common/Exception.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -85,31 +86,6 @@ std::string Exception::getStackTraceString() const } -std::string errnoToString(int code, int the_errno) -{ - const size_t buf_size = 128; - char buf[buf_size]; -#ifndef _GNU_SOURCE - int rc = strerror_r(the_errno, buf, buf_size); -#ifdef __APPLE__ - if (rc != 0 && rc != EINVAL) -#else - if (rc != 0) -#endif - { - std::string tmp = std::to_string(code); - const char * code_str = tmp.c_str(); - const char * unknown_message = "Unknown error "; - strcpy(buf, unknown_message); - strcpy(buf + strlen(unknown_message), code_str); - } - return "errno: " + toString(the_errno) + ", strerror: " + std::string(buf); -#else - (void)code; - return "errno: " + toString(the_errno) + ", strerror: " + std::string(strerror_r(the_errno, buf, sizeof(buf))); -#endif -} - void throwFromErrno(const std::string & s, int code, int the_errno) { throw ErrnoException(s + ", " + errnoToString(code, the_errno), code, the_errno); diff --git a/src/Common/Exception.h b/src/Common/Exception.h index de63f35f463..763b90048bb 100644 --- a/src/Common/Exception.h +++ b/src/Common/Exception.h @@ -8,6 +8,8 @@ #include +#include + namespace Poco { class Logger; } @@ -20,8 +22,14 @@ public: Exception() = default; Exception(const std::string & msg, int code); - enum CreateFromPocoTag { CreateFromPoco }; - enum CreateFromSTDTag { CreateFromSTD }; + // Format message with fmt::format, like the logging functions. + template + Exception(int code, Fmt&&... fmt) + : Exception(fmt::format(std::forward(fmt)...), code) + {} + + struct CreateFromPocoTag {}; + struct CreateFromSTDTag {}; Exception(CreateFromPocoTag, const Poco::Exception & exc); Exception(CreateFromSTDTag, const std::exception & exc); @@ -73,7 +81,6 @@ private: using Exceptions = std::vector; -std::string errnoToString(int code, int the_errno = errno); [[noreturn]] void throwFromErrno(const std::string & s, int code, int the_errno = errno); /// Useful to produce some extra information about available space and inodes on device [[noreturn]] void throwFromErrnoWithPath(const std::string & s, const std::string & path, int code, diff --git a/src/Common/OptimizedRegularExpression.cpp b/src/Common/OptimizedRegularExpression.cpp index 8c4aa00f171..1464923e6ab 100644 --- a/src/Common/OptimizedRegularExpression.cpp +++ b/src/Common/OptimizedRegularExpression.cpp @@ -38,6 +38,7 @@ void OptimizedRegularExpressionImpl::analyze( required_substring_is_prefix = false; required_substring.clear(); bool has_alternative_on_depth_0 = false; + bool has_case_insensitive_flag = false; /// Substring with a position. using Substring = std::pair; @@ -65,7 +66,17 @@ void OptimizedRegularExpressionImpl::analyze( switch (*pos) { - case '|': case '(': case ')': case '^': case '$': case '.': case '[': case '?': case '*': case '+': case '{': + case '|': + case '(': + case ')': + case '^': + case '$': + case '.': + case '[': + case '?': + case '*': + case '+': + case '{': if (depth == 0 && !in_curly_braces && !in_square_braces) { if (last_substring->first.empty()) @@ -110,6 +121,28 @@ void OptimizedRegularExpressionImpl::analyze( trivial_substrings.resize(trivial_substrings.size() + 1); last_substring = &trivial_substrings.back(); } + + /// Check for case-insensitive flag. + if (pos + 1 < end && pos[1] == '?') + { + for (size_t offset = 2; pos + offset < end; ++offset) + { + if (pos[offset] == '-' /// it means flag negation + /// various possible flags, actually only imsU are supported by re2 + || (pos[offset] >= 'a' && pos[offset] <= 'z') + || (pos[offset] >= 'A' && pos[offset] <= 'Z')) + { + if (pos[offset] == 'i') + { + /// Actually it can be negated case-insensitive flag. But we don't care. + has_case_insensitive_flag = true; + break; + } + } + else + break; + } + } } ++pos; break; @@ -209,7 +242,7 @@ void OptimizedRegularExpressionImpl::analyze( if (!is_trivial) { - if (!has_alternative_on_depth_0) + if (!has_alternative_on_depth_0 && !has_case_insensitive_flag) { /// We choose the non-alternative substring of the maximum length for first search. diff --git a/src/Common/PipeFDs.cpp b/src/Common/PipeFDs.cpp index 1f57234534f..d91917c23a4 100644 --- a/src/Common/PipeFDs.cpp +++ b/src/Common/PipeFDs.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include diff --git a/src/Common/ProfileEvents.cpp b/src/Common/ProfileEvents.cpp index a75339a644d..8393ea85112 100644 --- a/src/Common/ProfileEvents.cpp +++ b/src/Common/ProfileEvents.cpp @@ -180,6 +180,25 @@ M(OSWriteBytes, "Number of bytes written to disks or block devices. Doesn't include bytes that are in page cache dirty pages. May not include data that was written by OS asynchronously.") \ M(OSReadChars, "Number of bytes read from filesystem, including page cache.") \ M(OSWriteChars, "Number of bytes written to filesystem, including page cache.") \ + \ + M(PerfCpuCycles, "Total cycles. Be wary of what happens during CPU frequency scaling.") \ + M(PerfInstructions, "Retired instructions. Be careful, these can be affected by various issues, most notably hardware interrupt counts.") \ + M(PerfCacheReferences, "Cache accesses. Usually this indicates Last Level Cache accesses but this may vary depending on your CPU. This may include prefetches and coherency messages; again this depends on the design of your CPU.") \ + M(PerfCacheMisses, "Cache misses. Usually this indicates Last Level Cache misses; this is intended to be used in con‐junction with the PERFCOUNTHWCACHEREFERENCES event to calculate cache miss rates.") \ + M(PerfBranchInstructions, "Retired branch instructions. Prior to Linux 2.6.35, this used the wrong event on AMD processors.") \ + M(PerfBranchMisses, "Mispredicted branch instructions.") \ + M(PerfBusCycles, "Bus cycles, which can be different from total cycles.") \ + M(PerfStalledCyclesFrontend, "Stalled cycles during issue.") \ + M(PerfStalledCyclesBackend, "Stalled cycles during retirement.") \ + M(PerfRefCpuCycles, "Total cycles; not affected by CPU frequency scaling.") \ + \ + M(PerfCpuClock, "The CPU clock, a high-resolution per-CPU timer") \ + M(PerfTaskClock, "A clock count specific to the task that is running") \ + M(PerfContextSwitches, "Number of context switches") \ + M(PerfCpuMigrations, "Number of times the process has migrated to a new CPU") \ + M(PerfAlignmentFaults, "Number of alignment faults. These happen when unaligned memory accesses happen; the kernel can handle these but it reduces performance. This happens only on some architectures (never on x86).") \ + M(PerfEmulationFaults, "Number of emulation faults. The kernel sometimes traps on unimplemented instructions and emulates them for user space. This can negatively impact performance.") \ + \ M(CreatedHTTPConnections, "Total amount of created HTTP connections (closed or opened).") \ \ M(CannotWriteToWriteBufferDiscard, "Number of stack traces dropped by query profiler or signal handler because pipe is full or cannot write to pipe.") \ diff --git a/src/Common/QueryProfiler.cpp b/src/Common/QueryProfiler.cpp index a8b7d51a260..c4c7d21314d 100644 --- a/src/Common/QueryProfiler.cpp +++ b/src/Common/QueryProfiler.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #include diff --git a/src/Common/ShellCommand.cpp b/src/Common/ShellCommand.cpp index 1b97ed5689c..53ab2301a0a 100644 --- a/src/Common/ShellCommand.cpp +++ b/src/Common/ShellCommand.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include diff --git a/src/Common/StatusFile.cpp b/src/Common/StatusFile.cpp index 758f500e9d2..d228fdb42b6 100644 --- a/src/Common/StatusFile.cpp +++ b/src/Common/StatusFile.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include diff --git a/src/Common/StringUtils/CMakeLists.txt b/src/Common/StringUtils/CMakeLists.txt index c63e0f260ba..bd1282a08d5 100644 --- a/src/Common/StringUtils/CMakeLists.txt +++ b/src/Common/StringUtils/CMakeLists.txt @@ -6,4 +6,3 @@ include(${ClickHouse_SOURCE_DIR}/cmake/dbms_glob_sources.cmake) add_headers_and_sources(clickhouse_common_stringutils .) add_library(string_utils ${clickhouse_common_stringutils_headers} ${clickhouse_common_stringutils_sources}) -target_include_directories (string_utils PRIVATE ${DBMS_INCLUDE_DIR}) diff --git a/src/Common/SymbolIndex.cpp b/src/Common/SymbolIndex.cpp index 482c6fd0bad..54789695dd1 100644 --- a/src/Common/SymbolIndex.cpp +++ b/src/Common/SymbolIndex.cpp @@ -196,6 +196,20 @@ void collectSymbolsFromProgramHeaders(dl_phdr_info * info, } +String getBuildIDFromProgramHeaders(dl_phdr_info * info) +{ + for (size_t header_index = 0; header_index < info->dlpi_phnum; ++header_index) + { + const ElfPhdr & phdr = info->dlpi_phdr[header_index]; + if (phdr.p_type != PT_NOTE) + continue; + + return Elf::getBuildID(reinterpret_cast(info->dlpi_addr + phdr.p_vaddr), phdr.p_memsz); + } + return {}; +} + + void collectSymbolsFromELFSymbolTable( dl_phdr_info * info, const Elf & elf, @@ -283,8 +297,31 @@ void collectSymbolsFromELF(dl_phdr_info * info, object_name = std::filesystem::exists(debug_info_path) ? debug_info_path : canonical_path; + /// But we have to compare Build ID to check that debug info corresponds to the same executable. + String our_build_id = getBuildIDFromProgramHeaders(info); + SymbolIndex::Object object; object.elf = std::make_unique(object_name); + + String file_build_id = object.elf->getBuildID(); + + if (our_build_id != file_build_id) + { + /// If debug info doesn't correspond to our binary, fallback to the info in our binary. + if (object_name != canonical_path) + { + object_name = canonical_path; + object.elf = std::make_unique(object_name); + + /// But it can still be outdated, for example, if executable file was deleted from filesystem and replaced by another file. + file_build_id = object.elf->getBuildID(); + if (our_build_id != file_build_id) + return; + } + else + return; + } + object.address_begin = reinterpret_cast(info->dlpi_addr); object.address_end = reinterpret_cast(info->dlpi_addr + object.elf->size()); object.name = object_name; diff --git a/src/Common/ThreadProfileEvents.cpp b/src/Common/ThreadProfileEvents.cpp index 42452bf590b..fdc27f7efa3 100644 --- a/src/Common/ThreadProfileEvents.cpp +++ b/src/Common/ThreadProfileEvents.cpp @@ -4,9 +4,22 @@ #include "TaskStatsInfoGetter.h" #include "ProcfsMetricsProvider.h" +#include "hasLinuxCapability.h" +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace DB { @@ -104,6 +117,404 @@ void TasksStatsCounters::incrementProfileEvents(const ::taskstats & prev, const profile_events.increment(ProfileEvents::OSReadBytes, safeDiff(prev.read_bytes, curr.read_bytes)); profile_events.increment(ProfileEvents::OSWriteBytes, safeDiff(prev.write_bytes, curr.write_bytes)); } + +} + +#endif + +#if defined(__linux__) && !defined(ARCADIA_BUILD) + +namespace DB +{ + +thread_local PerfEventsCounters current_thread_counters; + +#define SOFTWARE_EVENT(PERF_NAME, LOCAL_NAME) \ + PerfEventInfo \ + { \ + .event_type = perf_type_id::PERF_TYPE_SOFTWARE, \ + .event_config = (PERF_NAME), \ + .profile_event = ProfileEvents::LOCAL_NAME, \ + .settings_name = #LOCAL_NAME \ + } + +#define HARDWARE_EVENT(PERF_NAME, LOCAL_NAME) \ + PerfEventInfo \ + { \ + .event_type = perf_type_id::PERF_TYPE_HARDWARE, \ + .event_config = (PERF_NAME), \ + .profile_event = ProfileEvents::LOCAL_NAME, \ + .settings_name = #LOCAL_NAME \ + } + +// descriptions' source: http://man7.org/linux/man-pages/man2/perf_event_open.2.html +static const PerfEventInfo raw_events_info[] = { + HARDWARE_EVENT(PERF_COUNT_HW_CPU_CYCLES, PerfCpuCycles), + HARDWARE_EVENT(PERF_COUNT_HW_INSTRUCTIONS, PerfInstructions), + HARDWARE_EVENT(PERF_COUNT_HW_CACHE_REFERENCES, PerfCacheReferences), + HARDWARE_EVENT(PERF_COUNT_HW_CACHE_MISSES, PerfCacheMisses), + HARDWARE_EVENT(PERF_COUNT_HW_BRANCH_INSTRUCTIONS, PerfBranchInstructions), + HARDWARE_EVENT(PERF_COUNT_HW_BRANCH_MISSES, PerfBranchMisses), + HARDWARE_EVENT(PERF_COUNT_HW_BUS_CYCLES, PerfBusCycles), + HARDWARE_EVENT(PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, PerfStalledCyclesFrontend), + HARDWARE_EVENT(PERF_COUNT_HW_STALLED_CYCLES_BACKEND, PerfStalledCyclesBackend), + HARDWARE_EVENT(PERF_COUNT_HW_REF_CPU_CYCLES, PerfRefCpuCycles), + // `cpu-clock` is a bit broken according to this: https://stackoverflow.com/a/56967896 + SOFTWARE_EVENT(PERF_COUNT_SW_CPU_CLOCK, PerfCpuClock), + SOFTWARE_EVENT(PERF_COUNT_SW_TASK_CLOCK, PerfTaskClock), + SOFTWARE_EVENT(PERF_COUNT_SW_CONTEXT_SWITCHES, PerfContextSwitches), + SOFTWARE_EVENT(PERF_COUNT_SW_CPU_MIGRATIONS, PerfCpuMigrations), + SOFTWARE_EVENT(PERF_COUNT_SW_ALIGNMENT_FAULTS, PerfAlignmentFaults), + SOFTWARE_EVENT(PERF_COUNT_SW_EMULATION_FAULTS, PerfEmulationFaults) +}; + +#undef HARDWARE_EVENT +#undef SOFTWARE_EVENT + +// A map of event name -> event index, to parse event list in settings. +static std::unordered_map populateEventMap() +{ + std::unordered_map name_to_index; + name_to_index.reserve(NUMBER_OF_RAW_EVENTS); + + for (size_t i = 0; i < NUMBER_OF_RAW_EVENTS; ++i) + { + name_to_index.emplace(raw_events_info[i].settings_name, i); + } + + return name_to_index; +} + +static const auto event_name_to_index = populateEventMap(); + +static int openPerfEvent(perf_event_attr *hw_event, pid_t pid, int cpu, int group_fd, UInt64 flags) +{ + return static_cast(syscall(SYS_perf_event_open, hw_event, pid, cpu, group_fd, flags)); +} + +static int openPerfEventDisabled(Int32 perf_event_paranoid, bool has_cap_sys_admin, UInt32 perf_event_type, UInt64 perf_event_config) +{ + perf_event_attr pe{}; + pe.type = perf_event_type; + pe.size = sizeof(struct perf_event_attr); + pe.config = perf_event_config; + // disable by default to add as little extra time as possible + pe.disabled = 1; + // can record kernel only when `perf_event_paranoid` <= 1 or have CAP_SYS_ADMIN + pe.exclude_kernel = perf_event_paranoid >= 2 && !has_cap_sys_admin; + pe.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING; + + return openPerfEvent(&pe, /* measure the calling thread */ 0, /* on any cpu */ -1, -1, 0); +} + +static void enablePerfEvent(int event_fd) +{ + if (ioctl(event_fd, PERF_EVENT_IOC_ENABLE, 0)) + { + LOG_WARNING(&Poco::Logger::get("PerfEvents"), + "Can't enable perf event with file descriptor {}: '{}' ({})", + event_fd, strerror(errno), errno); + } +} + +static void disablePerfEvent(int event_fd) +{ + if (ioctl(event_fd, PERF_EVENT_IOC_DISABLE, 0)) + { + LOG_WARNING(&Poco::Logger::get("PerfEvents"), + "Can't disable perf event with file descriptor {}: '{}' ({})", + event_fd, strerror(errno), errno); + } +} + +static void releasePerfEvent(int event_fd) +{ + if (close(event_fd)) + { + LOG_WARNING(&Poco::Logger::get("PerfEvents"), + "Can't close perf event file descriptor {}: {} ({})", + event_fd, strerror(errno), errno); + } +} + +static bool validatePerfEventDescriptor(int & fd) +{ + if (fcntl(fd, F_GETFL) != -1) + return true; + + if (errno == EBADF) + { + LOG_WARNING(&Poco::Logger::get("PerfEvents"), + "Event descriptor {} was closed from the outside; reopening", fd); + } + else + { + LOG_WARNING(&Poco::Logger::get("PerfEvents"), + "Error while checking availability of event descriptor {}: {} ({})", + fd, strerror(errno), errno); + + disablePerfEvent(fd); + releasePerfEvent(fd); + } + + fd = -1; + return false; +} + +bool PerfEventsCounters::processThreadLocalChanges(const std::string & needed_events_list) +{ + const auto valid_event_indices = eventIndicesFromString(needed_events_list); + + // find state changes (if there are any) + bool old_state[NUMBER_OF_RAW_EVENTS]; + for (size_t i = 0; i < NUMBER_OF_RAW_EVENTS; ++i) + old_state[i] = thread_events_descriptors_holder.descriptors[i] != -1; + + bool new_state[NUMBER_OF_RAW_EVENTS]; + std::fill_n(new_state, NUMBER_OF_RAW_EVENTS, false); + for (size_t opened_index : valid_event_indices) + new_state[opened_index] = true; + + std::vector events_to_open; + std::vector events_to_release; + for (size_t i = 0; i < NUMBER_OF_RAW_EVENTS; ++i) + { + bool old_one = old_state[i]; + bool new_one = new_state[i]; + + if (old_one == new_one) + { + if (old_one + && !validatePerfEventDescriptor( + thread_events_descriptors_holder.descriptors[i])) + { + events_to_open.push_back(i); + } + continue; + } + + if (new_one) + events_to_open.push_back(i); + else + events_to_release.push_back(i); + } + + // release unused descriptors + for (size_t i : events_to_release) + { + int & fd = thread_events_descriptors_holder.descriptors[i]; + disablePerfEvent(fd); + releasePerfEvent(fd); + fd = -1; + } + + if (events_to_open.empty()) + { + return true; + } + + // check permissions + // cat /proc/sys/kernel/perf_event_paranoid + // -1: Allow use of (almost) all events by all users + // >=0: Disallow raw tracepoint access by users without CAP_IOC_LOCK + // >=1: Disallow CPU event access by users without CAP_SYS_ADMIN + // >=2: Disallow kernel profiling by users without CAP_SYS_ADMIN + // >=3: Disallow all event access by users without CAP_SYS_ADMIN + Int32 perf_event_paranoid = 0; + std::ifstream paranoid_file("/proc/sys/kernel/perf_event_paranoid"); + paranoid_file >> perf_event_paranoid; + + bool has_cap_sys_admin = hasLinuxCapability(CAP_SYS_ADMIN); + if (perf_event_paranoid >= 3 && !has_cap_sys_admin) + { + LOG_WARNING(&Poco::Logger::get("PerfEvents"), + "Not enough permissions to record perf events: " + "perf_event_paranoid = {} and CAP_SYS_ADMIN = 0", + perf_event_paranoid); + return false; + } + + // Open descriptors for new events. + // Theoretically, we can run out of file descriptors. Threads go up to 10k, + // and there might be a dozen perf events per thread, so we're looking at + // 100k open files. In practice, this is not likely -- perf events are + // mostly used in performance tests or other kinds of testing, and the + // number of threads stays below hundred. + // We used to check the number of open files by enumerating /proc/self/fd, + // but listing all open files before opening more files is obviously + // quadratic, and quadraticity never ends well. + for (size_t i : events_to_open) + { + const PerfEventInfo & event_info = raw_events_info[i]; + int & fd = thread_events_descriptors_holder.descriptors[i]; + // disable by default to add as little extra time as possible + fd = openPerfEventDisabled(perf_event_paranoid, has_cap_sys_admin, event_info.event_type, event_info.event_config); + + if (fd == -1 && errno != ENOENT) + { + // ENOENT means that the event is not supported. Don't log it, because + // this is called for each thread and would be too verbose. Log other + // error codes because they might signify an error. + LOG_WARNING(&Poco::Logger::get("PerfEvents"), + "Failed to open perf event {} (event_type={}, event_config={}): " + "'{}' ({})", event_info.settings_name, event_info.event_type, + event_info.event_config, strerror(errno), errno); + } + } + + return true; +} + +// Parse comma-separated list of event names. Empty means all available +// events. +std::vector PerfEventsCounters::eventIndicesFromString(const std::string & events_list) +{ + std::vector result; + result.reserve(NUMBER_OF_RAW_EVENTS); + + if (events_list.empty()) + { + for (size_t i = 0; i < NUMBER_OF_RAW_EVENTS; ++i) + { + result.push_back(i); + } + return result; + } + + std::istringstream iss(events_list); + std::string event_name; + while (std::getline(iss, event_name, ',')) + { + // Allow spaces at the beginning of the token, so that you can write + // 'a, b'. + event_name.erase(0, event_name.find_first_not_of(' ')); + + auto entry = event_name_to_index.find(event_name); + if (entry != event_name_to_index.end()) + { + result.push_back(entry->second); + } + else + { + LOG_ERROR(&Poco::Logger::get("PerfEvents"), + "Unknown perf event name '{}' specified in settings", event_name); + } + } + + return result; +} + +void PerfEventsCounters::initializeProfileEvents(const std::string & events_list) +{ + if (!processThreadLocalChanges(events_list)) + return; + + for (int fd : thread_events_descriptors_holder.descriptors) + { + if (fd == -1) + continue; + + // We don't reset the event, because the time_running and time_enabled + // can't be reset anyway and we have to calculate deltas. + enablePerfEvent(fd); + } +} + +void PerfEventsCounters::finalizeProfileEvents(ProfileEvents::Counters & profile_events) +{ + // Disable all perf events. + for (auto fd : thread_events_descriptors_holder.descriptors) + { + if (fd == -1) + continue; + disablePerfEvent(fd); + } + + // Read the counter values. + PerfEventValue current_values[NUMBER_OF_RAW_EVENTS]; + for (size_t i = 0; i < NUMBER_OF_RAW_EVENTS; ++i) + { + int fd = thread_events_descriptors_holder.descriptors[i]; + if (fd == -1) + continue; + + constexpr ssize_t bytes_to_read = sizeof(current_values[0]); + const int bytes_read = read(fd, ¤t_values[i], bytes_to_read); + + if (bytes_read != bytes_to_read) + { + LOG_WARNING(&Poco::Logger::get("PerfEvents"), + "Can't read event value from file descriptor {}: '{}' ({})", + fd, strerror(errno), errno); + current_values[i] = {}; + } + } + + // actually process counters' values + for (size_t i = 0; i < NUMBER_OF_RAW_EVENTS; ++i) + { + int fd = thread_events_descriptors_holder.descriptors[i]; + if (fd == -1) + continue; + + const PerfEventInfo & info = raw_events_info[i]; + const PerfEventValue & previous_value = previous_values[i]; + const PerfEventValue & current_value = current_values[i]; + + // Account for counter multiplexing. time_running and time_enabled are + // not reset by PERF_EVENT_IOC_RESET, so we don't use it and calculate + // deltas from old values. + const UInt64 delta = (current_value.value - previous_value.value) + * (current_value.time_enabled - previous_value.time_enabled) + / std::max(1.f, + float(current_value.time_running - previous_value.time_running)); + + profile_events.increment(info.profile_event, delta); + } + + // Store current counter values for the next profiling period. + memcpy(previous_values, current_values, sizeof(current_values)); +} + +void PerfEventsCounters::closeEventDescriptors() +{ + thread_events_descriptors_holder.releaseResources(); +} + +PerfDescriptorsHolder::PerfDescriptorsHolder() +{ + for (int & descriptor : descriptors) + descriptor = -1; +} + +PerfDescriptorsHolder::~PerfDescriptorsHolder() +{ + releaseResources(); +} + +void PerfDescriptorsHolder::releaseResources() +{ + for (int & descriptor : descriptors) + { + if (descriptor == -1) + continue; + + disablePerfEvent(descriptor); + releasePerfEvent(descriptor); + descriptor = -1; + } +} + +} + +#else + +namespace DB +{ + +// Not on Linux or in Arcadia: the functionality is disabled. +PerfEventsCounters current_thread_counters; + } #endif diff --git a/src/Common/ThreadProfileEvents.h b/src/Common/ThreadProfileEvents.h index 038e04c4955..b6281234214 100644 --- a/src/Common/ThreadProfileEvents.h +++ b/src/Common/ThreadProfileEvents.h @@ -5,6 +5,7 @@ #include #include #include +#include #if defined(__linux__) @@ -34,6 +35,24 @@ namespace ProfileEvents extern const Event OSWriteChars; extern const Event OSReadBytes; extern const Event OSWriteBytes; + + extern const Event PerfCpuCycles; + extern const Event PerfInstructions; + extern const Event PerfCacheReferences; + extern const Event PerfCacheMisses; + extern const Event PerfBranchInstructions; + extern const Event PerfBranchMisses; + extern const Event PerfBusCycles; + extern const Event PerfStalledCyclesFrontend; + extern const Event PerfStalledCyclesBackend; + extern const Event PerfRefCpuCycles; + + extern const Event PerfCpuClock; + extern const Event PerfTaskClock; + extern const Event PerfContextSwitches; + extern const Event PerfCpuMigrations; + extern const Event PerfAlignmentFaults; + extern const Event PerfEmulationFaults; #endif } @@ -116,6 +135,78 @@ struct RUsageCounters } }; +// thread_local is disabled in Arcadia, so we have to use a dummy implementation +// there. +#if defined(__linux__) && !defined(ARCADIA_BUILD) + +struct PerfEventInfo +{ + // see perf_event.h/perf_type_id enum + int event_type; + // see configs in perf_event.h + int event_config; + ProfileEvents::Event profile_event; + std::string settings_name; +}; + +struct PerfEventValue +{ + UInt64 value = 0; + UInt64 time_enabled = 0; + UInt64 time_running = 0; +}; + +static constexpr size_t NUMBER_OF_RAW_EVENTS = 16; + +struct PerfDescriptorsHolder : boost::noncopyable +{ + int descriptors[NUMBER_OF_RAW_EVENTS]{}; + + PerfDescriptorsHolder(); + + ~PerfDescriptorsHolder(); + + void releaseResources(); +}; + +struct PerfEventsCounters +{ + PerfDescriptorsHolder thread_events_descriptors_holder; + + // time_enabled and time_running can't be reset, so we have to store the + // data from the previous profiling period and calculate deltas to them, + // to be able to properly account for counter multiplexing. + PerfEventValue previous_values[NUMBER_OF_RAW_EVENTS]{}; + + + void initializeProfileEvents(const std::string & events_list); + void finalizeProfileEvents(ProfileEvents::Counters & profile_events); + void closeEventDescriptors(); + bool processThreadLocalChanges(const std::string & needed_events_list); + + + static std::vector eventIndicesFromString(const std::string & events_list); +}; + +// Perf event creation is moderately heavy, so we create them once per thread and +// then reuse. +extern thread_local PerfEventsCounters current_thread_counters; + +#else + +// Not on Linux, or in Arcadia: the functionality is disabled. +struct PerfEventsCounters +{ + void initializeProfileEvents(const std::string & /* events_list */) {} + void finalizeProfileEvents(ProfileEvents::Counters & /* profile_events */) {} + void closeEventDescriptors() {} +}; + +// thread_local is disabled in Arcadia, so we are going to use a static dummy. +extern PerfEventsCounters current_thread_counters; + +#endif + #if defined(__linux__) class TasksStatsCounters diff --git a/src/Common/ThreadStatus.cpp b/src/Common/ThreadStatus.cpp index 682a4b0a412..ddb0b96df0e 100644 --- a/src/Common/ThreadStatus.cpp +++ b/src/Common/ThreadStatus.cpp @@ -1,6 +1,5 @@ #include -#include #include #include #include @@ -57,36 +56,6 @@ ThreadStatus::~ThreadStatus() current_thread = nullptr; } -void ThreadStatus::initPerformanceCounters() -{ - performance_counters_finalized = false; - - /// Clear stats from previous query if a new query is started - /// TODO: make separate query_thread_performance_counters and thread_performance_counters - performance_counters.resetCounters(); - memory_tracker.resetCounters(); - memory_tracker.setDescription("(for thread)"); - - query_start_time_nanoseconds = getCurrentTimeNanoseconds(); - query_start_time = time(nullptr); - ++queries_started; - - *last_rusage = RUsageCounters::current(query_start_time_nanoseconds); - if (!taskstats) - { - try - { - taskstats = TasksStatsCounters::create(thread_id); - } - catch (...) - { - tryLogCurrentException(log); - } - } - if (taskstats) - taskstats->reset(); -} - void ThreadStatus::updatePerformanceCounters() { try diff --git a/src/Common/ThreadStatus.h b/src/Common/ThreadStatus.h index 11cd7628a7d..d0952c3ab28 100644 --- a/src/Common/ThreadStatus.h +++ b/src/Common/ThreadStatus.h @@ -33,6 +33,7 @@ class QueryProfilerCpu; class QueryThreadLog; class TasksStatsCounters; struct RUsageCounters; +struct PerfEventsCounters; class TaskStatsInfoGetter; class InternalTextLogsQueue; using InternalTextLogsQueuePtr = std::shared_ptr; diff --git a/src/Common/UTF8Helpers.cpp b/src/Common/UTF8Helpers.cpp index 3cc29371b64..d8ba1bac5e9 100644 --- a/src/Common/UTF8Helpers.cpp +++ b/src/Common/UTF8Helpers.cpp @@ -89,7 +89,18 @@ static int wcwidth(wchar_t wc) } } -size_t computeWidth(const UInt8 * data, size_t size, size_t prefix) noexcept + +namespace +{ + +enum ComputeWidthMode +{ + Width, /// Calcualte and return visible width + BytesBeforLimit /// Calculate and return the maximum number of bytes when substring fits in visible width. +}; + +template +static size_t computeWidthImpl(const UInt8 * data, size_t size, size_t prefix, size_t limit) noexcept { UTF8Decoder decoder; size_t width = 0; @@ -132,16 +143,24 @@ size_t computeWidth(const UInt8 * data, size_t size, size_t prefix) noexcept ++i; } + /// Now i points to position in bytes after regular ASCII sequence + /// and if width > limit, then (width - limit) is the number of extra ASCII characters after width limit. + if (mode == BytesBeforLimit && width > limit) + return i - (width - limit); + switch (decoder.decode(data[i])) { case UTF8Decoder::REJECT: + { decoder.reset(); // invalid sequences seem to have zero width in modern terminals // tested in libvte-based, alacritty, urxvt and xterm i -= rollback; rollback = 0; break; + } case UTF8Decoder::ACCEPT: + { // there are special control characters that manipulate the terminal output. // (`0x08`, `0x09`, `0x0a`, `0x0b`, `0x0c`, `0x0d`, `0x1b`) // Since we don't touch the original column data, there is no easy way to escape them. @@ -149,12 +168,19 @@ size_t computeWidth(const UInt8 * data, size_t size, size_t prefix) noexcept // TODO: multiline support for '\n' // special treatment for '\t' + size_t next_width = width; if (decoder.codepoint == '\t') - width += 8 - (prefix + width) % 8; + next_width += 8 - (prefix + width) % 8; else - width += wcwidth(decoder.codepoint); + next_width += wcwidth(decoder.codepoint); + + if (mode == BytesBeforLimit && next_width > limit) + return i - rollback; + width = next_width; + rollback = 0; break; + } // continue if we meet other values here default: ++rollback; @@ -162,7 +188,21 @@ size_t computeWidth(const UInt8 * data, size_t size, size_t prefix) noexcept } // no need to handle trailing sequence as they have zero width - return width; -} + return (mode == BytesBeforLimit) ? size : width; +} + +} + + +size_t computeWidth(const UInt8 * data, size_t size, size_t prefix) noexcept +{ + return computeWidthImpl(data, size, prefix, 0); +} + +size_t computeBytesBeforeWidth(const UInt8 * data, size_t size, size_t prefix, size_t limit) noexcept +{ + return computeWidthImpl(data, size, prefix, limit); +} + } } diff --git a/src/Common/UTF8Helpers.h b/src/Common/UTF8Helpers.h index c1c73783870..926bb6a7f92 100644 --- a/src/Common/UTF8Helpers.h +++ b/src/Common/UTF8Helpers.h @@ -99,6 +99,19 @@ int queryConvert(const CharT * bytes, int length) /// and include `\t` to the nearest longer length with multiple of eight. size_t computeWidth(const UInt8 * data, size_t size, size_t prefix = 0) noexcept; + +/** Calculate the maximum number of bytes, so that substring of this size fits in 'limit' width. + * + * For example, we have string "x你好", it has 3 code points and visible width of 5 and byte size of 7. + + * Suppose we have limit = 3. + * Then we have to return 4 as maximum number of bytes + * and the truncated string will be "x你": two code points, visible width 3, byte size 4. + * + * The same result will be for limit 4, because the last character would not fit. + */ +size_t computeBytesBeforeWidth(const UInt8 * data, size_t size, size_t prefix, size_t limit) noexcept; + } diff --git a/src/Common/ZooKeeper/CMakeLists.txt b/src/Common/ZooKeeper/CMakeLists.txt index 90a75f1d9ec..ef32d9266c0 100644 --- a/src/Common/ZooKeeper/CMakeLists.txt +++ b/src/Common/ZooKeeper/CMakeLists.txt @@ -5,7 +5,6 @@ add_headers_and_sources(clickhouse_common_zookeeper .) add_library(clickhouse_common_zookeeper ${clickhouse_common_zookeeper_headers} ${clickhouse_common_zookeeper_sources}) target_link_libraries (clickhouse_common_zookeeper PUBLIC clickhouse_common_io common PRIVATE string_utils) -target_include_directories(clickhouse_common_zookeeper PUBLIC ${DBMS_INCLUDE_DIR}) if (ENABLE_TESTS) add_subdirectory (tests) diff --git a/src/Common/ZooKeeper/IKeeper.cpp b/src/Common/ZooKeeper/IKeeper.cpp index 5c27971038f..cb378ba1e13 100644 --- a/src/Common/ZooKeeper/IKeeper.cpp +++ b/src/Common/ZooKeeper/IKeeper.cpp @@ -23,7 +23,7 @@ namespace ProfileEvents namespace Coordination { -Exception::Exception(const std::string & msg, const int32_t code_, int) +Exception::Exception(const std::string & msg, const Error code_, int) : DB::Exception(msg, DB::ErrorCodes::KEEPER_EXCEPTION), code(code_) { if (Coordination::isUserError(code)) @@ -34,17 +34,17 @@ Exception::Exception(const std::string & msg, const int32_t code_, int) ProfileEvents::increment(ProfileEvents::ZooKeeperOtherExceptions); } -Exception::Exception(const std::string & msg, const int32_t code_) +Exception::Exception(const std::string & msg, const Error code_) : Exception(msg + " (" + errorMessage(code_) + ")", code_, 0) { } -Exception::Exception(const int32_t code_) +Exception::Exception(const Error code_) : Exception(errorMessage(code_), code_, 0) { } -Exception::Exception(const int32_t code_, const std::string & path) +Exception::Exception(const Error code_, const std::string & path) : Exception(std::string{errorMessage(code_)} + ", path: " + path, code_, 0) { } @@ -58,10 +58,10 @@ using namespace DB; static void addRootPath(String & path, const String & root_path) { if (path.empty()) - throw Exception("Path cannot be empty", ZBADARGUMENTS); + throw Exception("Path cannot be empty", Error::ZBADARGUMENTS); if (path[0] != '/') - throw Exception("Path must begin with /", ZBADARGUMENTS); + throw Exception("Path must begin with /", Error::ZBADARGUMENTS); if (root_path.empty()) return; @@ -78,64 +78,62 @@ static void removeRootPath(String & path, const String & root_path) return; if (path.size() <= root_path.size()) - throw Exception("Received path is not longer than root_path", ZDATAINCONSISTENCY); + throw Exception("Received path is not longer than root_path", Error::ZDATAINCONSISTENCY); path = path.substr(root_path.size()); } -const char * errorMessage(int32_t code) +const char * errorMessage(Error code) { switch (code) { - case ZOK: return "Ok"; - case ZSYSTEMERROR: return "System error"; - case ZRUNTIMEINCONSISTENCY: return "Run time inconsistency"; - case ZDATAINCONSISTENCY: return "Data inconsistency"; - case ZCONNECTIONLOSS: return "Connection loss"; - case ZMARSHALLINGERROR: return "Marshalling error"; - case ZUNIMPLEMENTED: return "Unimplemented"; - case ZOPERATIONTIMEOUT: return "Operation timeout"; - case ZBADARGUMENTS: return "Bad arguments"; - case ZINVALIDSTATE: return "Invalid zhandle state"; - case ZAPIERROR: return "API error"; - case ZNONODE: return "No node"; - case ZNOAUTH: return "Not authenticated"; - case ZBADVERSION: return "Bad version"; - case ZNOCHILDRENFOREPHEMERALS: return "No children for ephemerals"; - case ZNODEEXISTS: return "Node exists"; - case ZNOTEMPTY: return "Not empty"; - case ZSESSIONEXPIRED: return "Session expired"; - case ZINVALIDCALLBACK: return "Invalid callback"; - case ZINVALIDACL: return "Invalid ACL"; - case ZAUTHFAILED: return "Authentication failed"; - case ZCLOSING: return "ZooKeeper is closing"; - case ZNOTHING: return "(not error) no server responses to process"; - case ZSESSIONMOVED: return "Session moved to another server, so operation is ignored"; + case Error::ZOK: return "Ok"; + case Error::ZSYSTEMERROR: return "System error"; + case Error::ZRUNTIMEINCONSISTENCY: return "Run time inconsistency"; + case Error::ZDATAINCONSISTENCY: return "Data inconsistency"; + case Error::ZCONNECTIONLOSS: return "Connection loss"; + case Error::ZMARSHALLINGERROR: return "Marshalling error"; + case Error::ZUNIMPLEMENTED: return "Unimplemented"; + case Error::ZOPERATIONTIMEOUT: return "Operation timeout"; + case Error::ZBADARGUMENTS: return "Bad arguments"; + case Error::ZINVALIDSTATE: return "Invalid zhandle state"; + case Error::ZAPIERROR: return "API error"; + case Error::ZNONODE: return "No node"; + case Error::ZNOAUTH: return "Not authenticated"; + case Error::ZBADVERSION: return "Bad version"; + case Error::ZNOCHILDRENFOREPHEMERALS: return "No children for ephemerals"; + case Error::ZNODEEXISTS: return "Node exists"; + case Error::ZNOTEMPTY: return "Not empty"; + case Error::ZSESSIONEXPIRED: return "Session expired"; + case Error::ZINVALIDCALLBACK: return "Invalid callback"; + case Error::ZINVALIDACL: return "Invalid ACL"; + case Error::ZAUTHFAILED: return "Authentication failed"; + case Error::ZCLOSING: return "ZooKeeper is closing"; + case Error::ZNOTHING: return "(not error) no server responses to process"; + case Error::ZSESSIONMOVED: return "Session moved to another server, so operation is ignored"; } - if (code > 0) - return strerror(code); - return "unknown error"; + __builtin_unreachable(); } -bool isHardwareError(int32_t zk_return_code) +bool isHardwareError(Error zk_return_code) { - return zk_return_code == ZINVALIDSTATE - || zk_return_code == ZSESSIONEXPIRED - || zk_return_code == ZSESSIONMOVED - || zk_return_code == ZCONNECTIONLOSS - || zk_return_code == ZMARSHALLINGERROR - || zk_return_code == ZOPERATIONTIMEOUT; + return zk_return_code == Error::ZINVALIDSTATE + || zk_return_code == Error::ZSESSIONEXPIRED + || zk_return_code == Error::ZSESSIONMOVED + || zk_return_code == Error::ZCONNECTIONLOSS + || zk_return_code == Error::ZMARSHALLINGERROR + || zk_return_code == Error::ZOPERATIONTIMEOUT; } -bool isUserError(int32_t zk_return_code) +bool isUserError(Error zk_return_code) { - return zk_return_code == ZNONODE - || zk_return_code == ZBADVERSION - || zk_return_code == ZNOCHILDRENFOREPHEMERALS - || zk_return_code == ZNODEEXISTS - || zk_return_code == ZNOTEMPTY; + return zk_return_code == Error::ZNONODE + || zk_return_code == Error::ZBADVERSION + || zk_return_code == Error::ZNOCHILDRENFOREPHEMERALS + || zk_return_code == Error::ZNODEEXISTS + || zk_return_code == Error::ZNOTEMPTY; } diff --git a/src/Common/ZooKeeper/IKeeper.h b/src/Common/ZooKeeper/IKeeper.h index f415e0306e8..409c3838147 100644 --- a/src/Common/ZooKeeper/IKeeper.h +++ b/src/Common/ZooKeeper/IKeeper.h @@ -53,6 +53,57 @@ struct Stat int64_t pzxid; }; +enum class Error : int32_t +{ + ZOK = 0, + + /** System and server-side errors. + * This is never thrown by the server, it shouldn't be used other than + * to indicate a range. Specifically error codes greater than this + * value, but lesser than ZAPIERROR, are system errors. + */ + ZSYSTEMERROR = -1, + + ZRUNTIMEINCONSISTENCY = -2, /// A runtime inconsistency was found + ZDATAINCONSISTENCY = -3, /// A data inconsistency was found + ZCONNECTIONLOSS = -4, /// Connection to the server has been lost + ZMARSHALLINGERROR = -5, /// Error while marshalling or unmarshalling data + ZUNIMPLEMENTED = -6, /// Operation is unimplemented + ZOPERATIONTIMEOUT = -7, /// Operation timeout + ZBADARGUMENTS = -8, /// Invalid arguments + ZINVALIDSTATE = -9, /// Invliad zhandle state + + /** API errors. + * This is never thrown by the server, it shouldn't be used other than + * to indicate a range. Specifically error codes greater than this + * value are API errors. + */ + ZAPIERROR = -100, + + ZNONODE = -101, /// Node does not exist + ZNOAUTH = -102, /// Not authenticated + ZBADVERSION = -103, /// Version conflict + ZNOCHILDRENFOREPHEMERALS = -108, /// Ephemeral nodes may not have children + ZNODEEXISTS = -110, /// The node already exists + ZNOTEMPTY = -111, /// The node has children + ZSESSIONEXPIRED = -112, /// The session has been expired by the server + ZINVALIDCALLBACK = -113, /// Invalid callback specified + ZINVALIDACL = -114, /// Invalid ACL specified + ZAUTHFAILED = -115, /// Client authentication failed + ZCLOSING = -116, /// ZooKeeper is closing + ZNOTHING = -117, /// (not error) no server responses to process + ZSESSIONMOVED = -118 /// Session moved to another server, so operation is ignored +}; + +/// Network errors and similar. You should reinitialize ZooKeeper session in case of these errors +bool isHardwareError(Error code); + +/// Valid errors sent from the server about database state (like "no node"). Logical and authentication errors (like "bad arguments") are not here. +bool isUserError(Error code); + +const char * errorMessage(Error code); + + struct Request; using RequestPtr = std::shared_ptr; using Requests = std::vector; @@ -74,7 +125,7 @@ using ResponseCallback = std::function; struct Response { - int32_t error = 0; + Error error = Error::ZOK; Response() = default; Response(const Response &) = default; Response & operator=(const Response &) = default; @@ -225,56 +276,6 @@ using CheckCallback = std::function; using MultiCallback = std::function; -enum Error -{ - ZOK = 0, - - /** System and server-side errors. - * This is never thrown by the server, it shouldn't be used other than - * to indicate a range. Specifically error codes greater than this - * value, but lesser than ZAPIERROR, are system errors. - */ - ZSYSTEMERROR = -1, - - ZRUNTIMEINCONSISTENCY = -2, /// A runtime inconsistency was found - ZDATAINCONSISTENCY = -3, /// A data inconsistency was found - ZCONNECTIONLOSS = -4, /// Connection to the server has been lost - ZMARSHALLINGERROR = -5, /// Error while marshalling or unmarshalling data - ZUNIMPLEMENTED = -6, /// Operation is unimplemented - ZOPERATIONTIMEOUT = -7, /// Operation timeout - ZBADARGUMENTS = -8, /// Invalid arguments - ZINVALIDSTATE = -9, /// Invliad zhandle state - - /** API errors. - * This is never thrown by the server, it shouldn't be used other than - * to indicate a range. Specifically error codes greater than this - * value are API errors. - */ - ZAPIERROR = -100, - - ZNONODE = -101, /// Node does not exist - ZNOAUTH = -102, /// Not authenticated - ZBADVERSION = -103, /// Version conflict - ZNOCHILDRENFOREPHEMERALS = -108, /// Ephemeral nodes may not have children - ZNODEEXISTS = -110, /// The node already exists - ZNOTEMPTY = -111, /// The node has children - ZSESSIONEXPIRED = -112, /// The session has been expired by the server - ZINVALIDCALLBACK = -113, /// Invalid callback specified - ZINVALIDACL = -114, /// Invalid ACL specified - ZAUTHFAILED = -115, /// Client authentication failed - ZCLOSING = -116, /// ZooKeeper is closing - ZNOTHING = -117, /// (not error) no server responses to process - ZSESSIONMOVED = -118 /// Session moved to another server, so operation is ignored -}; - -/// Network errors and similar. You should reinitialize ZooKeeper session in case of these errors -bool isHardwareError(int32_t code); - -/// Valid errors sent from the server about database state (like "no node"). Logical and authentication errors (like "bad arguments") are not here. -bool isUserError(int32_t code); - -const char * errorMessage(int32_t code); - /// For watches. enum State { @@ -301,19 +302,19 @@ class Exception : public DB::Exception { private: /// Delegate constructor, used to minimize repetition; last parameter used for overload resolution. - Exception(const std::string & msg, const int32_t code_, int); + Exception(const std::string & msg, const Error code_, int); public: - explicit Exception(const int32_t code_); - Exception(const std::string & msg, const int32_t code_); - Exception(const int32_t code_, const std::string & path); + explicit Exception(const Error code_); + Exception(const std::string & msg, const Error code_); + Exception(const Error code_, const std::string & path); Exception(const Exception & exc); const char * name() const throw() override { return "Coordination::Exception"; } const char * className() const throw() override { return "Coordination::Exception"; } Exception * clone() const override { return new Exception(*this); } - const int32_t code; + const Error code; }; diff --git a/src/Common/ZooKeeper/Increment.h b/src/Common/ZooKeeper/Increment.h index fa5f550ca9b..883fc00442e 100644 --- a/src/Common/ZooKeeper/Increment.h +++ b/src/Common/ZooKeeper/Increment.h @@ -29,11 +29,11 @@ public: if (zookeeper->tryGet(path, result_str, &stat)) { result = std::stol(result_str) + 1; - success = zookeeper->trySet(path, std::to_string(result), stat.version) == Coordination::ZOK; + success = zookeeper->trySet(path, std::to_string(result), stat.version) == Coordination::Error::ZOK; } else { - success = zookeeper->tryCreate(path, std::to_string(result), zkutil::CreateMode::Persistent) == Coordination::ZOK; + success = zookeeper->tryCreate(path, std::to_string(result), zkutil::CreateMode::Persistent) == Coordination::Error::ZOK; } } while (!success); diff --git a/src/Common/ZooKeeper/KeeperException.h b/src/Common/ZooKeeper/KeeperException.h index 5fcca4cf3d2..6498aca809c 100644 --- a/src/Common/ZooKeeper/KeeperException.h +++ b/src/Common/ZooKeeper/KeeperException.h @@ -21,12 +21,12 @@ public: /// If it is user error throws KeeperMultiException else throws ordinary KeeperException /// If it is ZOK does nothing - static void check(int32_t code, const Coordination::Requests & requests, const Coordination::Responses & responses); + static void check(Coordination::Error code, const Coordination::Requests & requests, const Coordination::Responses & responses); - KeeperMultiException(int32_t code, const Coordination::Requests & requests, const Coordination::Responses & responses); + KeeperMultiException(Coordination::Error code, const Coordination::Requests & requests, const Coordination::Responses & responses); private: - static size_t getFailedOpIndex(int32_t code, const Coordination::Responses & responses); + static size_t getFailedOpIndex(Coordination::Error code, const Coordination::Responses & responses); }; } diff --git a/src/Common/ZooKeeper/LeaderElection.h b/src/Common/ZooKeeper/LeaderElection.h index e3b97e7f8ca..f8a4d56dc76 100644 --- a/src/Common/ZooKeeper/LeaderElection.h +++ b/src/Common/ZooKeeper/LeaderElection.h @@ -36,7 +36,12 @@ public: * It means that different participants of leader election have different identifiers * and existence of more than one ephemeral node with same identifier indicates an error. */ - LeaderElection(DB::BackgroundSchedulePool & pool_, const std::string & path_, ZooKeeper & zookeeper_, LeadershipHandler handler_, const std::string & identifier_ = "") + LeaderElection( + DB::BackgroundSchedulePool & pool_, + const std::string & path_, + ZooKeeper & zookeeper_, + LeadershipHandler handler_, + const std::string & identifier_) : pool(pool_), path(path_), zookeeper(zookeeper_), handler(handler_), identifier(identifier_) , log_name("LeaderElection (" + path + ")") , log(&Poco::Logger::get(log_name)) @@ -121,7 +126,7 @@ private: { DB::tryLogCurrentException(log); - if (e.code == Coordination::ZSESSIONEXPIRED) + if (e.code == Coordination::Error::ZSESSIONEXPIRED) return; } catch (...) diff --git a/src/Common/ZooKeeper/Lock.cpp b/src/Common/ZooKeeper/Lock.cpp index c781d8ba2bf..9c966cc576d 100644 --- a/src/Common/ZooKeeper/Lock.cpp +++ b/src/Common/ZooKeeper/Lock.cpp @@ -16,13 +16,13 @@ bool Lock::tryLock() else { std::string dummy; - int32_t code = zookeeper->tryCreate(lock_path, lock_message, zkutil::CreateMode::Ephemeral, dummy); + Coordination::Error code = zookeeper->tryCreate(lock_path, lock_message, zkutil::CreateMode::Ephemeral, dummy); - if (code == Coordination::ZNODEEXISTS) + if (code == Coordination::Error::ZNODEEXISTS) { locked.reset(); } - else if (code == Coordination::ZOK) + else if (code == Coordination::Error::ZOK) { locked = std::make_unique(zookeeper); } diff --git a/src/Common/ZooKeeper/TestKeeper.cpp b/src/Common/ZooKeeper/TestKeeper.cpp index 4f736e66aab..a734d218ff6 100644 --- a/src/Common/ZooKeeper/TestKeeper.cpp +++ b/src/Common/ZooKeeper/TestKeeper.cpp @@ -158,7 +158,7 @@ struct TestKeeperMultiRequest final : MultiRequest, TestKeeperRequest requests.push_back(std::make_shared(*concrete_request_check)); } else - throw Exception("Illegal command as part of multi ZooKeeper request", ZBADARGUMENTS); + throw Exception("Illegal command as part of multi ZooKeeper request", Error::ZBADARGUMENTS); } } @@ -338,7 +338,7 @@ ResponsePtr TestKeeperListRequest::process(TestKeeper::Container & container, in { auto path_prefix = path; if (path_prefix.empty()) - throw Exception("Logical error: path cannot be empty", ZSESSIONEXPIRED); + throw Exception("Logical error: path cannot be empty", Error::ZSESSIONEXPIRED); if (path_prefix.back() != '/') path_prefix += '/'; @@ -514,7 +514,7 @@ void TestKeeper::finalize() WatchResponse response; response.type = SESSION; response.state = EXPIRED_SESSION; - response.error = ZSESSIONEXPIRED; + response.error = Error::ZSESSIONEXPIRED; for (auto & callback : path_watch.second) { @@ -541,7 +541,7 @@ void TestKeeper::finalize() if (info.callback) { ResponsePtr response = info.request->createResponse(); - response->error = ZSESSIONEXPIRED; + response->error = Error::ZSESSIONEXPIRED; try { info.callback(*response); @@ -556,7 +556,7 @@ void TestKeeper::finalize() WatchResponse response; response.type = SESSION; response.state = EXPIRED_SESSION; - response.error = ZSESSIONEXPIRED; + response.error = Error::ZSESSIONEXPIRED; try { info.watch(response); @@ -587,10 +587,10 @@ void TestKeeper::pushRequest(RequestInfo && request) std::lock_guard lock(push_request_mutex); if (expired) - throw Exception("Session expired", ZSESSIONEXPIRED); + throw Exception("Session expired", Error::ZSESSIONEXPIRED); if (!requests_queue.tryPush(std::move(request), operation_timeout.totalMilliseconds())) - throw Exception("Cannot push request to queue within operation timeout", ZOPERATIONTIMEOUT); + throw Exception("Cannot push request to queue within operation timeout", Error::ZOPERATIONTIMEOUT); } catch (...) { diff --git a/src/Common/ZooKeeper/ZooKeeper.cpp b/src/Common/ZooKeeper/ZooKeeper.cpp index 115518e2bf9..169299483ce 100644 --- a/src/Common/ZooKeeper/ZooKeeper.cpp +++ b/src/Common/ZooKeeper/ZooKeeper.cpp @@ -38,9 +38,9 @@ const int CreateMode::PersistentSequential = 2; const int CreateMode::EphemeralSequential = 3; -static void check(int32_t code, const std::string & path) +static void check(Coordination::Error code, const std::string & path) { - if (code) + if (code != Coordination::Error::ZOK) throw KeeperException(code, path); } @@ -59,7 +59,7 @@ void ZooKeeper::init(const std::string & implementation_, const std::string & ho if (implementation == "zookeeper") { if (hosts.empty()) - throw KeeperException("No hosts passed to ZooKeeper constructor.", Coordination::ZBADARGUMENTS); + throw KeeperException("No hosts passed to ZooKeeper constructor.", Coordination::Error::ZBADARGUMENTS); std::vector hosts_strings; splitInto<','>(hosts_strings, hosts); @@ -84,7 +84,7 @@ void ZooKeeper::init(const std::string & implementation_, const std::string & ho } if (nodes.empty()) - throw KeeperException("Cannot use any of provided ZooKeeper nodes", Coordination::ZBADARGUMENTS); + throw KeeperException("Cannot use any of provided ZooKeeper nodes", Coordination::Error::ZBADARGUMENTS); impl = std::make_unique( nodes, @@ -112,7 +112,7 @@ void ZooKeeper::init(const std::string & implementation_, const std::string & ho } if (!chroot.empty() && !exists("/")) - throw KeeperException("Zookeeper root doesn't exist. You should create root node " + chroot + " before start.", Coordination::ZNONODE); + throw KeeperException("Zookeeper root doesn't exist. You should create root node " + chroot + " before start.", Coordination::Error::ZNONODE); } ZooKeeper::ZooKeeper(const std::string & hosts_, const std::string & identity_, int32_t session_timeout_ms_, @@ -164,7 +164,7 @@ struct ZooKeeperArgs implementation = config.getString(config_name + "." + key); } else - throw KeeperException(std::string("Unknown key ") + key + " in config file", Coordination::ZBADARGUMENTS); + throw KeeperException(std::string("Unknown key ") + key + " in config file", Coordination::Error::ZBADARGUMENTS); } /// Shuffle the hosts to distribute the load among ZooKeeper nodes. @@ -182,7 +182,7 @@ struct ZooKeeperArgs if (!chroot.empty()) { if (chroot.front() != '/') - throw KeeperException(std::string("Root path in config file should start with '/', but got ") + chroot, Coordination::ZBADARGUMENTS); + throw KeeperException(std::string("Root path in config file should start with '/', but got ") + chroot, Coordination::Error::ZBADARGUMENTS); if (chroot.back() == '/') chroot.pop_back(); } @@ -211,17 +211,17 @@ static Coordination::WatchCallback callbackForEvent(const EventPtr & watch) } -int32_t ZooKeeper::getChildrenImpl(const std::string & path, Strings & res, +Coordination::Error ZooKeeper::getChildrenImpl(const std::string & path, Strings & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback) { - int32_t code = 0; + Coordination::Error code = Coordination::Error::ZOK; Poco::Event event; auto callback = [&](const Coordination::ListResponse & response) { code = response.error; - if (!code) + if (code == Coordination::Error::ZOK) { res = response.names; if (stat) @@ -251,37 +251,37 @@ Strings ZooKeeper::getChildrenWatch( return res; } -int32_t ZooKeeper::tryGetChildren(const std::string & path, Strings & res, +Coordination::Error ZooKeeper::tryGetChildren(const std::string & path, Strings & res, Coordination::Stat * stat, const EventPtr & watch) { - int32_t code = getChildrenImpl(path, res, stat, callbackForEvent(watch)); + Coordination::Error code = getChildrenImpl(path, res, stat, callbackForEvent(watch)); - if (!(code == Coordination::ZOK || code == Coordination::ZNONODE)) + if (!(code == Coordination::Error::ZOK || code == Coordination::Error::ZNONODE)) throw KeeperException(code, path); return code; } -int32_t ZooKeeper::tryGetChildrenWatch(const std::string & path, Strings & res, +Coordination::Error ZooKeeper::tryGetChildrenWatch(const std::string & path, Strings & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback) { - int32_t code = getChildrenImpl(path, res, stat, watch_callback); + Coordination::Error code = getChildrenImpl(path, res, stat, watch_callback); - if (!(code == Coordination::ZOK || code == Coordination::ZNONODE)) + if (!(code == Coordination::Error::ZOK || code == Coordination::Error::ZNONODE)) throw KeeperException(code, path); return code; } -int32_t ZooKeeper::createImpl(const std::string & path, const std::string & data, int32_t mode, std::string & path_created) +Coordination::Error ZooKeeper::createImpl(const std::string & path, const std::string & data, int32_t mode, std::string & path_created) { - int32_t code = 0; + Coordination::Error code = Coordination::Error::ZOK; Poco::Event event; auto callback = [&](const Coordination::CreateResponse & response) { code = response.error; - if (!code) + if (code == Coordination::Error::ZOK) path_created = response.path_created; event.set(); }; @@ -298,20 +298,20 @@ std::string ZooKeeper::create(const std::string & path, const std::string & data return path_created; } -int32_t ZooKeeper::tryCreate(const std::string & path, const std::string & data, int32_t mode, std::string & path_created) +Coordination::Error ZooKeeper::tryCreate(const std::string & path, const std::string & data, int32_t mode, std::string & path_created) { - int32_t code = createImpl(path, data, mode, path_created); + Coordination::Error code = createImpl(path, data, mode, path_created); - if (!(code == Coordination::ZOK || - code == Coordination::ZNONODE || - code == Coordination::ZNODEEXISTS || - code == Coordination::ZNOCHILDRENFOREPHEMERALS)) + if (!(code == Coordination::Error::ZOK || + code == Coordination::Error::ZNONODE || + code == Coordination::Error::ZNODEEXISTS || + code == Coordination::Error::ZNOCHILDRENFOREPHEMERALS)) throw KeeperException(code, path); return code; } -int32_t ZooKeeper::tryCreate(const std::string & path, const std::string & data, int32_t mode) +Coordination::Error ZooKeeper::tryCreate(const std::string & path, const std::string & data, int32_t mode) { std::string path_created; return tryCreate(path, data, mode, path_created); @@ -320,9 +320,9 @@ int32_t ZooKeeper::tryCreate(const std::string & path, const std::string & data, void ZooKeeper::createIfNotExists(const std::string & path, const std::string & data) { std::string path_created; - int32_t code = createImpl(path, data, CreateMode::Persistent, path_created); + Coordination::Error code = createImpl(path, data, CreateMode::Persistent, path_created); - if (code == Coordination::ZOK || code == Coordination::ZNODEEXISTS) + if (code == Coordination::Error::ZOK || code == Coordination::Error::ZNODEEXISTS) return; else throw KeeperException(code, path); @@ -341,14 +341,14 @@ void ZooKeeper::createAncestors(const std::string & path) } } -int32_t ZooKeeper::removeImpl(const std::string & path, int32_t version) +Coordination::Error ZooKeeper::removeImpl(const std::string & path, int32_t version) { - int32_t code = 0; + Coordination::Error code = Coordination::Error::ZOK; Poco::Event event; auto callback = [&](const Coordination::RemoveResponse & response) { - if (response.error) + if (response.error != Coordination::Error::ZOK) code = response.error; event.set(); }; @@ -363,26 +363,26 @@ void ZooKeeper::remove(const std::string & path, int32_t version) check(tryRemove(path, version), path); } -int32_t ZooKeeper::tryRemove(const std::string & path, int32_t version) +Coordination::Error ZooKeeper::tryRemove(const std::string & path, int32_t version) { - int32_t code = removeImpl(path, version); - if (!(code == Coordination::ZOK || - code == Coordination::ZNONODE || - code == Coordination::ZBADVERSION || - code == Coordination::ZNOTEMPTY)) + Coordination::Error code = removeImpl(path, version); + if (!(code == Coordination::Error::ZOK || + code == Coordination::Error::ZNONODE || + code == Coordination::Error::ZBADVERSION || + code == Coordination::Error::ZNOTEMPTY)) throw KeeperException(code, path); return code; } -int32_t ZooKeeper::existsImpl(const std::string & path, Coordination::Stat * stat, Coordination::WatchCallback watch_callback) +Coordination::Error ZooKeeper::existsImpl(const std::string & path, Coordination::Stat * stat, Coordination::WatchCallback watch_callback) { - int32_t code = 0; + Coordination::Error code = Coordination::Error::ZOK; Poco::Event event; auto callback = [&](const Coordination::ExistsResponse & response) { code = response.error; - if (!code && stat) + if (code == Coordination::Error::ZOK && stat) *stat = response.stat; event.set(); }; @@ -399,22 +399,22 @@ bool ZooKeeper::exists(const std::string & path, Coordination::Stat * stat, cons bool ZooKeeper::existsWatch(const std::string & path, Coordination::Stat * stat, Coordination::WatchCallback watch_callback) { - int32_t code = existsImpl(path, stat, watch_callback); + Coordination::Error code = existsImpl(path, stat, watch_callback); - if (!(code == Coordination::ZOK || code == Coordination::ZNONODE)) + if (!(code == Coordination::Error::ZOK || code == Coordination::Error::ZNONODE)) throw KeeperException(code, path); - return code != Coordination::ZNONODE; + return code != Coordination::Error::ZNONODE; } -int32_t ZooKeeper::getImpl(const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback) +Coordination::Error ZooKeeper::getImpl(const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback) { - int32_t code = 0; + Coordination::Error code = Coordination::Error::ZOK; Poco::Event event; auto callback = [&](const Coordination::GetResponse & response) { code = response.error; - if (!code) + if (code == Coordination::Error::ZOK) { res = response.data; if (stat) @@ -431,7 +431,7 @@ int32_t ZooKeeper::getImpl(const std::string & path, std::string & res, Coordina std::string ZooKeeper::get(const std::string & path, Coordination::Stat * stat, const EventPtr & watch) { - int32_t code = 0; + Coordination::Error code = Coordination::Error::ZOK; std::string res; if (tryGet(path, res, stat, watch, &code)) return res; @@ -441,7 +441,7 @@ std::string ZooKeeper::get(const std::string & path, Coordination::Stat * stat, std::string ZooKeeper::getWatch(const std::string & path, Coordination::Stat * stat, Coordination::WatchCallback watch_callback) { - int32_t code = 0; + Coordination::Error code = Coordination::Error::ZOK; std::string res; if (tryGetWatch(path, res, stat, watch_callback, &code)) return res; @@ -449,34 +449,44 @@ std::string ZooKeeper::getWatch(const std::string & path, Coordination::Stat * s throw KeeperException("Can't get data for node " + path + ": node doesn't exist", code); } -bool ZooKeeper::tryGet(const std::string & path, std::string & res, Coordination::Stat * stat, const EventPtr & watch, int * return_code) +bool ZooKeeper::tryGet( + const std::string & path, + std::string & res, + Coordination::Stat * stat, + const EventPtr & watch, + Coordination::Error * return_code) { return tryGetWatch(path, res, stat, callbackForEvent(watch), return_code); } -bool ZooKeeper::tryGetWatch(const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback, int * return_code) +bool ZooKeeper::tryGetWatch( + const std::string & path, + std::string & res, + Coordination::Stat * stat, + Coordination::WatchCallback watch_callback, + Coordination::Error * return_code) { - int32_t code = getImpl(path, res, stat, watch_callback); + Coordination::Error code = getImpl(path, res, stat, watch_callback); - if (!(code == Coordination::ZOK || code == Coordination::ZNONODE)) + if (!(code == Coordination::Error::ZOK || code == Coordination::Error::ZNONODE)) throw KeeperException(code, path); if (return_code) *return_code = code; - return code == Coordination::ZOK; + return code == Coordination::Error::ZOK; } -int32_t ZooKeeper::setImpl(const std::string & path, const std::string & data, +Coordination::Error ZooKeeper::setImpl(const std::string & path, const std::string & data, int32_t version, Coordination::Stat * stat) { - int32_t code = 0; + Coordination::Error code = Coordination::Error::ZOK; Poco::Event event; auto callback = [&](const Coordination::SetResponse & response) { code = response.error; - if (!code && stat) + if (code == Coordination::Error::ZOK && stat) *stat = response.stat; event.set(); }; @@ -493,34 +503,34 @@ void ZooKeeper::set(const std::string & path, const std::string & data, int32_t void ZooKeeper::createOrUpdate(const std::string & path, const std::string & data, int32_t mode) { - int32_t code = trySet(path, data, -1); - if (code == Coordination::ZNONODE) + Coordination::Error code = trySet(path, data, -1); + if (code == Coordination::Error::ZNONODE) { create(path, data, mode); } - else if (code != Coordination::ZOK) + else if (code != Coordination::Error::ZOK) throw KeeperException(code, path); } -int32_t ZooKeeper::trySet(const std::string & path, const std::string & data, +Coordination::Error ZooKeeper::trySet(const std::string & path, const std::string & data, int32_t version, Coordination::Stat * stat) { - int32_t code = setImpl(path, data, version, stat); + Coordination::Error code = setImpl(path, data, version, stat); - if (!(code == Coordination::ZOK || - code == Coordination::ZNONODE || - code == Coordination::ZBADVERSION)) + if (!(code == Coordination::Error::ZOK || + code == Coordination::Error::ZNONODE || + code == Coordination::Error::ZBADVERSION)) throw KeeperException(code, path); return code; } -int32_t ZooKeeper::multiImpl(const Coordination::Requests & requests, Coordination::Responses & responses) +Coordination::Error ZooKeeper::multiImpl(const Coordination::Requests & requests, Coordination::Responses & responses) { if (requests.empty()) - return Coordination::ZOK; + return Coordination::Error::ZOK; - int32_t code = 0; + Coordination::Error code = Coordination::Error::ZOK; Poco::Event event; auto callback = [&](const Coordination::MultiResponse & response) @@ -538,15 +548,15 @@ int32_t ZooKeeper::multiImpl(const Coordination::Requests & requests, Coordinati Coordination::Responses ZooKeeper::multi(const Coordination::Requests & requests) { Coordination::Responses responses; - int32_t code = multiImpl(requests, responses); + Coordination::Error code = multiImpl(requests, responses); KeeperMultiException::check(code, requests, responses); return responses; } -int32_t ZooKeeper::tryMulti(const Coordination::Requests & requests, Coordination::Responses & responses) +Coordination::Error ZooKeeper::tryMulti(const Coordination::Requests & requests, Coordination::Responses & responses) { - int32_t code = multiImpl(requests, responses); - if (code && !Coordination::isUserError(code)) + Coordination::Error code = multiImpl(requests, responses); + if (code != Coordination::Error::ZOK && !Coordination::isUserError(code)) throw KeeperException(code); return code; } @@ -587,7 +597,7 @@ void ZooKeeper::removeChildrenRecursive(const std::string & path) void ZooKeeper::tryRemoveChildrenRecursive(const std::string & path) { Strings children; - if (tryGetChildren(path, children) != Coordination::ZOK) + if (tryGetChildren(path, children) != Coordination::Error::ZOK) return; while (!children.empty()) { @@ -609,7 +619,7 @@ void ZooKeeper::tryRemoveChildrenRecursive(const std::string & path) /// this means someone is concurrently removing these children and we will have /// to remove them one by one. Coordination::Responses responses; - if (tryMulti(ops, responses) != Coordination::ZOK) + if (tryMulti(ops, responses) != Coordination::Error::ZOK) for (const std::string & child : batch) tryRemove(child); } @@ -645,7 +655,7 @@ bool ZooKeeper::waitForDisappear(const std::string & path, const WaitCondition & auto callback = [state](const Coordination::ExistsResponse & response) { - state->code = response.error; + state->code = int32_t(response.error); if (state->code) state->event.set(); }; @@ -654,7 +664,7 @@ bool ZooKeeper::waitForDisappear(const std::string & path, const WaitCondition & { if (!state->code) { - state->code = response.error; + state->code = int32_t(response.error); if (!state->code) state->event_type = response.type; state->event.set(); @@ -670,11 +680,11 @@ bool ZooKeeper::waitForDisappear(const std::string & path, const WaitCondition & else if (!state->event.tryWait(1000)) continue; - if (state->code == Coordination::ZNONODE) + if (state->code == int32_t(Coordination::Error::ZNONODE)) return true; if (state->code) - throw KeeperException(state->code, path); + throw KeeperException(static_cast(state->code.load(std::memory_order_seq_cst)), path); if (state->event_type == Coordination::DELETED) return true; @@ -688,11 +698,6 @@ ZooKeeperPtr ZooKeeper::startNewSession() const } -std::string ZooKeeper::error2string(int32_t code) -{ - return Coordination::errorMessage(code); -} - bool ZooKeeper::expired() { return impl->isExpired(); @@ -712,7 +717,7 @@ std::future ZooKeeper::asyncCreate(const std::stri auto callback = [promise, path](const Coordination::CreateResponse & response) mutable { - if (response.error) + if (response.error != Coordination::Error::ZOK) promise->set_exception(std::make_exception_ptr(KeeperException(path, response.error))); else promise->set_value(response); @@ -730,7 +735,7 @@ std::future ZooKeeper::asyncGet(const std::string & p auto callback = [promise, path](const Coordination::GetResponse & response) mutable { - if (response.error) + if (response.error != Coordination::Error::ZOK) promise->set_exception(std::make_exception_ptr(KeeperException(path, response.error))); else promise->set_value(response); @@ -748,7 +753,7 @@ std::future ZooKeeper::asyncTryGet(const std::string auto callback = [promise, path](const Coordination::GetResponse & response) mutable { - if (response.error && response.error != Coordination::ZNONODE) + if (response.error != Coordination::Error::ZOK && response.error != Coordination::Error::ZNONODE) promise->set_exception(std::make_exception_ptr(KeeperException(path, response.error))); else promise->set_value(response); @@ -765,7 +770,7 @@ std::future ZooKeeper::asyncExists(const std::stri auto callback = [promise, path](const Coordination::ExistsResponse & response) mutable { - if (response.error && response.error != Coordination::ZNONODE) + if (response.error != Coordination::Error::ZOK && response.error != Coordination::Error::ZNONODE) promise->set_exception(std::make_exception_ptr(KeeperException(path, response.error))); else promise->set_value(response); @@ -782,7 +787,7 @@ std::future ZooKeeper::asyncSet(const std::string & p auto callback = [promise, path](const Coordination::SetResponse & response) mutable { - if (response.error) + if (response.error != Coordination::Error::ZOK) promise->set_exception(std::make_exception_ptr(KeeperException(path, response.error))); else promise->set_value(response); @@ -799,7 +804,7 @@ std::future ZooKeeper::asyncGetChildren(const std::s auto callback = [promise, path](const Coordination::ListResponse & response) mutable { - if (response.error) + if (response.error != Coordination::Error::ZOK) promise->set_exception(std::make_exception_ptr(KeeperException(path, response.error))); else promise->set_value(response); @@ -816,7 +821,7 @@ std::future ZooKeeper::asyncRemove(const std::stri auto callback = [promise, path](const Coordination::RemoveResponse & response) mutable { - if (response.error) + if (response.error != Coordination::Error::ZOK) promise->set_exception(std::make_exception_ptr(KeeperException(path, response.error))); else promise->set_value(response); @@ -833,8 +838,13 @@ std::future ZooKeeper::asyncTryRemove(const std::s auto callback = [promise, path](const Coordination::RemoveResponse & response) mutable { - if (response.error && response.error != Coordination::ZNONODE && response.error != Coordination::ZBADVERSION && response.error != Coordination::ZNOTEMPTY) + if (response.error != Coordination::Error::ZOK + && response.error != Coordination::Error::ZNONODE + && response.error != Coordination::Error::ZBADVERSION + && response.error != Coordination::Error::ZNOTEMPTY) + { promise->set_exception(std::make_exception_ptr(KeeperException(path, response.error))); + } else promise->set_value(response); }; @@ -864,7 +874,7 @@ std::future ZooKeeper::asyncMulti(const Coordinatio auto callback = [promise](const Coordination::MultiResponse & response) mutable { - if (response.error) + if (response.error != Coordination::Error::ZOK) promise->set_exception(std::make_exception_ptr(KeeperException(response.error))); else promise->set_value(response); @@ -874,7 +884,7 @@ std::future ZooKeeper::asyncMulti(const Coordinatio return future; } -int32_t ZooKeeper::tryMultiNoThrow(const Coordination::Requests & requests, Coordination::Responses & responses) +Coordination::Error ZooKeeper::tryMultiNoThrow(const Coordination::Requests & requests, Coordination::Responses & responses) { try { @@ -887,24 +897,24 @@ int32_t ZooKeeper::tryMultiNoThrow(const Coordination::Requests & requests, Coor } -size_t KeeperMultiException::getFailedOpIndex(int32_t exception_code, const Coordination::Responses & responses) +size_t KeeperMultiException::getFailedOpIndex(Coordination::Error exception_code, const Coordination::Responses & responses) { if (responses.empty()) throw DB::Exception("Responses for multi transaction is empty", DB::ErrorCodes::LOGICAL_ERROR); for (size_t index = 0, size = responses.size(); index < size; ++index) - if (responses[index]->error) + if (responses[index]->error != Coordination::Error::ZOK) return index; if (!Coordination::isUserError(exception_code)) - throw DB::Exception("There are no failed OPs because '" + ZooKeeper::error2string(exception_code) + "' is not valid response code for that", + throw DB::Exception("There are no failed OPs because '" + std::string(Coordination::errorMessage(exception_code)) + "' is not valid response code for that", DB::ErrorCodes::LOGICAL_ERROR); throw DB::Exception("There is no failed OpResult", DB::ErrorCodes::LOGICAL_ERROR); } -KeeperMultiException::KeeperMultiException(int32_t exception_code, const Coordination::Requests & requests_, const Coordination::Responses & responses_) +KeeperMultiException::KeeperMultiException(Coordination::Error exception_code, const Coordination::Requests & requests_, const Coordination::Responses & responses_) : KeeperException("Transaction failed", exception_code), requests(requests_), responses(responses_), failed_op_index(getFailedOpIndex(exception_code, responses)) { @@ -917,9 +927,10 @@ std::string KeeperMultiException::getPathForFirstFailedOp() const return requests[failed_op_index]->getPath(); } -void KeeperMultiException::check(int32_t exception_code, const Coordination::Requests & requests, const Coordination::Responses & responses) +void KeeperMultiException::check( + Coordination::Error exception_code, const Coordination::Requests & requests, const Coordination::Responses & responses) { - if (!exception_code) + if (exception_code == Coordination::Error::ZOK) return; if (Coordination::isUserError(exception_code)) diff --git a/src/Common/ZooKeeper/ZooKeeper.h b/src/Common/ZooKeeper/ZooKeeper.h index 3bf9ad3c100..416e40c2da4 100644 --- a/src/Common/ZooKeeper/ZooKeeper.h +++ b/src/Common/ZooKeeper/ZooKeeper.h @@ -99,8 +99,8 @@ public: /// * The parent is ephemeral. /// * The node already exists. /// In case of other errors throws an exception. - int32_t tryCreate(const std::string & path, const std::string & data, int32_t mode, std::string & path_created); - int32_t tryCreate(const std::string & path, const std::string & data, int32_t mode); + Coordination::Error tryCreate(const std::string & path, const std::string & data, int32_t mode, std::string & path_created); + Coordination::Error tryCreate(const std::string & path, const std::string & data, int32_t mode); /// Create a Persistent node. /// Does nothing if the node already exists. @@ -117,7 +117,7 @@ public: /// * The node doesn't exist /// * Versions don't match /// * The node has children. - int32_t tryRemove(const std::string & path, int32_t version = -1); + Coordination::Error tryRemove(const std::string & path, int32_t version = -1); bool exists(const std::string & path, Coordination::Stat * stat = nullptr, const EventPtr & watch = nullptr); bool existsWatch(const std::string & path, Coordination::Stat * stat, Coordination::WatchCallback watch_callback); @@ -127,9 +127,11 @@ public: /// Doesn't not throw in the following cases: /// * The node doesn't exist. Returns false in this case. - bool tryGet(const std::string & path, std::string & res, Coordination::Stat * stat = nullptr, const EventPtr & watch = nullptr, int * code = nullptr); + bool tryGet(const std::string & path, std::string & res, Coordination::Stat * stat = nullptr, const EventPtr & watch = nullptr, + Coordination::Error * code = nullptr); - bool tryGetWatch(const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback, int * code = nullptr); + bool tryGetWatch(const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback, + Coordination::Error * code = nullptr); void set(const std::string & path, const std::string & data, int32_t version = -1, Coordination::Stat * stat = nullptr); @@ -140,7 +142,7 @@ public: /// Doesn't not throw in the following cases: /// * The node doesn't exist. /// * Versions do not match. - int32_t trySet(const std::string & path, const std::string & data, + Coordination::Error trySet(const std::string & path, const std::string & data, int32_t version = -1, Coordination::Stat * stat = nullptr); Strings getChildren(const std::string & path, @@ -153,11 +155,11 @@ public: /// Doesn't not throw in the following cases: /// * The node doesn't exist. - int32_t tryGetChildren(const std::string & path, Strings & res, + Coordination::Error tryGetChildren(const std::string & path, Strings & res, Coordination::Stat * stat = nullptr, const EventPtr & watch = nullptr); - int32_t tryGetChildrenWatch(const std::string & path, Strings & res, + Coordination::Error tryGetChildrenWatch(const std::string & path, Strings & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback); @@ -166,9 +168,9 @@ public: Coordination::Responses multi(const Coordination::Requests & requests); /// Throws only if some operation has returned an "unexpected" error /// - an error that would cause the corresponding try- method to throw. - int32_t tryMulti(const Coordination::Requests & requests, Coordination::Responses & responses); + Coordination::Error tryMulti(const Coordination::Requests & requests, Coordination::Responses & responses); /// Throws nothing (even session expired errors) - int32_t tryMultiNoThrow(const Coordination::Requests & requests, Coordination::Responses & responses); + Coordination::Error tryMultiNoThrow(const Coordination::Requests & requests, Coordination::Responses & responses); Int64 getClientID(); @@ -238,8 +240,6 @@ public: /// Like the previous one but don't throw any exceptions on future.get() FutureMulti tryAsyncMulti(const Coordination::Requests & ops); - static std::string error2string(int32_t code); - private: friend class EphemeralNodeHolder; @@ -250,13 +250,15 @@ private: void tryRemoveChildrenRecursive(const std::string & path); /// The following methods don't throw exceptions but return error codes. - int32_t createImpl(const std::string & path, const std::string & data, int32_t mode, std::string & path_created); - int32_t removeImpl(const std::string & path, int32_t version); - int32_t getImpl(const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback); - int32_t setImpl(const std::string & path, const std::string & data, int32_t version, Coordination::Stat * stat); - int32_t getChildrenImpl(const std::string & path, Strings & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback); - int32_t multiImpl(const Coordination::Requests & requests, Coordination::Responses & responses); - int32_t existsImpl(const std::string & path, Coordination::Stat * stat_, Coordination::WatchCallback watch_callback); + Coordination::Error createImpl(const std::string & path, const std::string & data, int32_t mode, std::string & path_created); + Coordination::Error removeImpl(const std::string & path, int32_t version); + Coordination::Error getImpl( + const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback); + Coordination::Error setImpl(const std::string & path, const std::string & data, int32_t version, Coordination::Stat * stat); + Coordination::Error getChildrenImpl( + const std::string & path, Strings & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback); + Coordination::Error multiImpl(const Coordination::Requests & requests, Coordination::Responses & responses); + Coordination::Error existsImpl(const std::string & path, Coordination::Stat * stat_, Coordination::WatchCallback watch_callback); std::unique_ptr impl; diff --git a/src/Common/ZooKeeper/ZooKeeperHolder.cpp b/src/Common/ZooKeeper/ZooKeeperHolder.cpp index 41a36a51082..ea8a2017e37 100644 --- a/src/Common/ZooKeeper/ZooKeeperHolder.cpp +++ b/src/Common/ZooKeeper/ZooKeeperHolder.cpp @@ -5,7 +5,7 @@ namespace DB { namespace ErrorCodes { - extern const int NULL_POINTER_DEREFERENCE; + extern const int LOGICAL_ERROR; } } @@ -57,7 +57,7 @@ ZooKeeperHolder::UnstorableZookeeperHandler::UnstorableZookeeperHandler(ZooKeepe ZooKeeper * ZooKeeperHolder::UnstorableZookeeperHandler::operator->() { if (zk_ptr == nullptr) - throw DB::Exception(nullptr_exception_message, DB::ErrorCodes::NULL_POINTER_DEREFERENCE); + throw DB::Exception(nullptr_exception_message, DB::ErrorCodes::LOGICAL_ERROR); return zk_ptr.get(); } @@ -65,20 +65,20 @@ ZooKeeper * ZooKeeperHolder::UnstorableZookeeperHandler::operator->() const ZooKeeper * ZooKeeperHolder::UnstorableZookeeperHandler::operator->() const { if (zk_ptr == nullptr) - throw DB::Exception(nullptr_exception_message, DB::ErrorCodes::NULL_POINTER_DEREFERENCE); + throw DB::Exception(nullptr_exception_message, DB::ErrorCodes::LOGICAL_ERROR); return zk_ptr.get(); } ZooKeeper & ZooKeeperHolder::UnstorableZookeeperHandler::operator*() { if (zk_ptr == nullptr) - throw DB::Exception(nullptr_exception_message, DB::ErrorCodes::NULL_POINTER_DEREFERENCE); + throw DB::Exception(nullptr_exception_message, DB::ErrorCodes::LOGICAL_ERROR); return *zk_ptr; } const ZooKeeper & ZooKeeperHolder::UnstorableZookeeperHandler::operator*() const { if (zk_ptr == nullptr) - throw DB::Exception(nullptr_exception_message, DB::ErrorCodes::NULL_POINTER_DEREFERENCE); + throw DB::Exception(nullptr_exception_message, DB::ErrorCodes::LOGICAL_ERROR); return *zk_ptr; } diff --git a/src/Common/ZooKeeper/ZooKeeperImpl.cpp b/src/Common/ZooKeeper/ZooKeeperImpl.cpp index e6cab23d2ce..61a64f2c51f 100644 --- a/src/Common/ZooKeeper/ZooKeeperImpl.cpp +++ b/src/Common/ZooKeeper/ZooKeeperImpl.cpp @@ -52,13 +52,6 @@ namespace CurrentMetrics extern const Metric ZooKeeperWatch; } -namespace DB -{ - namespace ErrorCodes - { - extern const int SUPPORT_IS_DISABLED; - } -} /** ZooKeeper wire protocol. @@ -335,6 +328,13 @@ static void read(int32_t & x, ReadBuffer & in) x = __builtin_bswap32(x); } +static void read(Error & x, ReadBuffer & in) +{ + int32_t code; + read(code, in); + x = Error(code); +} + static void read(bool & x, ReadBuffer & in) { readBinary(x, in); @@ -353,10 +353,10 @@ static void read(String & s, ReadBuffer & in) } if (size < 0) - throw Exception("Negative size while reading string from ZooKeeper", ZMARSHALLINGERROR); + throw Exception("Negative size while reading string from ZooKeeper", Error::ZMARSHALLINGERROR); if (size > MAX_STRING_OR_ARRAY_SIZE) - throw Exception("Too large string size while reading from ZooKeeper", ZMARSHALLINGERROR); + throw Exception("Too large string size while reading from ZooKeeper", Error::ZMARSHALLINGERROR); s.resize(size); in.read(s.data(), size); @@ -367,7 +367,7 @@ template void read(std::array & s, ReadBuffer & in) int32_t size = 0; read(size, in); if (size != N) - throw Exception("Unexpected array size while reading from ZooKeeper", ZMARSHALLINGERROR); + throw Exception("Unexpected array size while reading from ZooKeeper", Error::ZMARSHALLINGERROR); in.read(s.data(), N); } @@ -391,9 +391,9 @@ template void read(std::vector & arr, ReadBuffer & in) int32_t size = 0; read(size, in); if (size < 0) - throw Exception("Negative size while reading array from ZooKeeper", ZMARSHALLINGERROR); + throw Exception("Negative size while reading array from ZooKeeper", Error::ZMARSHALLINGERROR); if (size > MAX_STRING_OR_ARRAY_SIZE) - throw Exception("Too large array size while reading from ZooKeeper", ZMARSHALLINGERROR); + throw Exception("Too large array size while reading from ZooKeeper", Error::ZMARSHALLINGERROR); arr.resize(size); for (auto & elem : arr) read(elem, in); @@ -489,7 +489,7 @@ struct ZooKeeperCloseResponse final : ZooKeeperResponse { void readImpl(ReadBuffer &) override { - throw Exception("Received response for close request", ZRUNTIMEINCONSISTENCY); + throw Exception("Received response for close request", Error::ZRUNTIMEINCONSISTENCY); } }; @@ -650,12 +650,12 @@ struct ZooKeeperErrorResponse final : ErrorResponse, ZooKeeperResponse { void readImpl(ReadBuffer & in) override { - int32_t read_error; + Coordination::Error read_error; Coordination::read(read_error, in); if (read_error != error) - throw Exception("Error code in ErrorResponse (" + toString(read_error) + ") doesn't match error code in header (" + toString(error) + ")", - ZMARSHALLINGERROR); + throw Exception(fmt::format("Error code in ErrorResponse ({}) doesn't match error code in header ({})", read_error, error), + Error::ZMARSHALLINGERROR); } }; @@ -691,7 +691,7 @@ struct ZooKeeperMultiRequest final : MultiRequest, ZooKeeperRequest requests.push_back(std::make_shared(*concrete_request_check)); } else - throw Exception("Illegal command as part of multi ZooKeeper request", ZBADARGUMENTS); + throw Exception("Illegal command as part of multi ZooKeeper request", Error::ZBADARGUMENTS); } } @@ -739,14 +739,14 @@ struct ZooKeeperMultiResponse final : MultiResponse, ZooKeeperResponse { ZooKeeper::OpNum op_num; bool done; - int32_t op_error; + Error op_error; Coordination::read(op_num, in); Coordination::read(done, in); Coordination::read(op_error, in); if (done) - throw Exception("Not enough results received for multi transaction", ZMARSHALLINGERROR); + throw Exception("Not enough results received for multi transaction", Error::ZMARSHALLINGERROR); /// op_num == -1 is special for multi transaction. /// For unknown reason, error code is duplicated in header and in response body. @@ -754,18 +754,18 @@ struct ZooKeeperMultiResponse final : MultiResponse, ZooKeeperResponse if (op_num == -1) response = std::make_shared(); - if (op_error) + if (op_error != Error::ZOK) { response->error = op_error; /// Set error for whole transaction. /// If some operations fail, ZK send global error as zero and then send details about each operation. /// It will set error code for first failed operation and it will set special "runtime inconsistency" code for other operations. - if (!error && op_error != ZRUNTIMEINCONSISTENCY) + if (error == Error::ZOK && op_error != Error::ZRUNTIMEINCONSISTENCY) error = op_error; } - if (!op_error || op_num == -1) + if (op_error == Error::ZOK || op_num == -1) dynamic_cast(*response).readImpl(in); } @@ -780,11 +780,11 @@ struct ZooKeeperMultiResponse final : MultiResponse, ZooKeeperResponse Coordination::read(error_read, in); if (!done) - throw Exception("Too many results received for multi transaction", ZMARSHALLINGERROR); + throw Exception("Too many results received for multi transaction", Error::ZMARSHALLINGERROR); if (op_num != -1) - throw Exception("Unexpected op_num received at the end of results for multi transaction", ZMARSHALLINGERROR); + throw Exception("Unexpected op_num received at the end of results for multi transaction", Error::ZMARSHALLINGERROR); if (error_read != -1) - throw Exception("Unexpected error value received at the end of results for multi transaction", ZMARSHALLINGERROR); + throw Exception("Unexpected error value received at the end of results for multi transaction", Error::ZMARSHALLINGERROR); } } }; @@ -883,7 +883,7 @@ void ZooKeeper::connect( Poco::Timespan connection_timeout) { if (nodes.empty()) - throw Exception("No nodes passed to ZooKeeper constructor", ZBADARGUMENTS); + throw Exception("No nodes passed to ZooKeeper constructor", Error::ZBADARGUMENTS); static constexpr size_t num_tries = 3; bool connected = false; @@ -901,7 +901,8 @@ void ZooKeeper::connect( #if USE_SSL socket = Poco::Net::SecureStreamSocket(); #else - throw Exception{"Communication with ZooKeeper over SSL is disabled because poco library was built without NetSSL support.", ErrorCodes::SUPPORT_IS_DISABLED}; + throw Poco::Exception( + "Communication with ZooKeeper over SSL is disabled because poco library was built without NetSSL support."); #endif } else @@ -970,7 +971,7 @@ void ZooKeeper::connect( } message << fail_reasons.str() << "\n"; - throw Exception(message.str(), ZCONNECTIONLOSS); + throw Exception(message.str(), Error::ZCONNECTIONLOSS); } } @@ -1005,11 +1006,11 @@ void ZooKeeper::receiveHandshake() read(handshake_length); if (handshake_length != 36) - throw Exception("Unexpected handshake length received: " + toString(handshake_length), ZMARSHALLINGERROR); + throw Exception("Unexpected handshake length received: " + toString(handshake_length), Error::ZMARSHALLINGERROR); read(protocol_version_read); if (protocol_version_read != protocol_version) - throw Exception("Unexpected protocol version: " + toString(protocol_version_read), ZMARSHALLINGERROR); + throw Exception("Unexpected protocol version: " + toString(protocol_version_read), Error::ZMARSHALLINGERROR); read(timeout); if (timeout != session_timeout.totalMilliseconds()) @@ -1032,7 +1033,7 @@ void ZooKeeper::sendAuth(const String & scheme, const String & data) int32_t length; XID read_xid; int64_t zxid; - int32_t err; + Error err; read(length); size_t count_before_event = in->count(); @@ -1042,16 +1043,16 @@ void ZooKeeper::sendAuth(const String & scheme, const String & data) if (read_xid != auth_xid) throw Exception("Unexpected event received in reply to auth request: " + toString(read_xid), - ZMARSHALLINGERROR); + Error::ZMARSHALLINGERROR); int32_t actual_length = in->count() - count_before_event; if (length != actual_length) throw Exception("Response length doesn't match. Expected: " + toString(length) + ", actual: " + toString(actual_length), - ZMARSHALLINGERROR); + Error::ZMARSHALLINGERROR); - if (err) - throw Exception("Error received in reply to auth request. Code: " + toString(err) + ". Message: " + String(errorMessage(err)), - ZMARSHALLINGERROR); + if (err != Error::ZOK) + throw Exception("Error received in reply to auth request. Code: " + toString(int32_t(err)) + ". Message: " + String(errorMessage(err)), + Error::ZMARSHALLINGERROR); } @@ -1154,7 +1155,7 @@ void ZooKeeper::receiveThread() earliest_operation = operations.begin()->second; auto earliest_operation_deadline = earliest_operation->time + std::chrono::microseconds(operation_timeout.totalMicroseconds()); if (now > earliest_operation_deadline) - throw Exception("Operation timeout (deadline already expired) for path: " + earliest_operation->request->getPath(), ZOPERATIONTIMEOUT); + throw Exception("Operation timeout (deadline already expired) for path: " + earliest_operation->request->getPath(), Error::ZOPERATIONTIMEOUT); max_wait = std::chrono::duration_cast(earliest_operation_deadline - now).count(); } } @@ -1170,10 +1171,10 @@ void ZooKeeper::receiveThread() else { if (earliest_operation) - throw Exception("Operation timeout (no response) for path: " + earliest_operation->request->getPath(), ZOPERATIONTIMEOUT); + throw Exception("Operation timeout (no response) for path: " + earliest_operation->request->getPath(), Error::ZOPERATIONTIMEOUT); waited += max_wait; if (waited >= session_timeout.totalMicroseconds()) - throw Exception("Nothing is received in session timeout", ZOPERATIONTIMEOUT); + throw Exception("Nothing is received in session timeout", Error::ZOPERATIONTIMEOUT); } @@ -1193,7 +1194,7 @@ void ZooKeeper::receiveEvent() int32_t length; XID xid; int64_t zxid; - int32_t err; + Error err; read(length); size_t count_before_event = in->count(); @@ -1206,8 +1207,8 @@ void ZooKeeper::receiveEvent() if (xid == ping_xid) { - if (err) - throw Exception("Received error in heartbeat response: " + String(errorMessage(err)), ZRUNTIMEINCONSISTENCY); + if (err != Error::ZOK) + throw Exception("Received error in heartbeat response: " + String(errorMessage(err)), Error::ZRUNTIMEINCONSISTENCY); response = std::make_shared(); } @@ -1252,7 +1253,7 @@ void ZooKeeper::receiveEvent() auto it = operations.find(xid); if (it == operations.end()) - throw Exception("Received response for unknown xid", ZRUNTIMEINCONSISTENCY); + throw Exception("Received response for unknown xid", Error::ZRUNTIMEINCONSISTENCY); /// After this point, we must invoke callback, that we've grabbed from 'operations'. /// Invariant: all callbacks are invoked either in case of success or in case of error. @@ -1272,7 +1273,7 @@ void ZooKeeper::receiveEvent() if (!response) response = request_info.request->makeResponse(); - if (err) + if (err != Error::ZOK) response->error = err; else { @@ -1282,7 +1283,7 @@ void ZooKeeper::receiveEvent() int32_t actual_length = in->count() - count_before_event; if (length != actual_length) - throw Exception("Response length doesn't match. Expected: " + toString(length) + ", actual: " + toString(actual_length), ZMARSHALLINGERROR); + throw Exception("Response length doesn't match. Expected: " + toString(length) + ", actual: " + toString(actual_length), Error::ZMARSHALLINGERROR); } catch (...) { @@ -1294,7 +1295,7 @@ void ZooKeeper::receiveEvent() /// In case we cannot read the response, we should indicate it as the error of that type /// when the user cannot assume whether the request was processed or not. - response->error = ZCONNECTIONLOSS; + response->error = Error::ZCONNECTIONLOSS; if (request_info.callback) request_info.callback(*response); @@ -1361,8 +1362,8 @@ void ZooKeeper::finalize(bool error_send, bool error_receive) ResponsePtr response = request_info.request->makeResponse(); response->error = request_info.request->probably_sent - ? ZCONNECTIONLOSS - : ZSESSIONEXPIRED; + ? Error::ZCONNECTIONLOSS + : Error::ZSESSIONEXPIRED; if (request_info.callback) { @@ -1390,7 +1391,7 @@ void ZooKeeper::finalize(bool error_send, bool error_receive) WatchResponse response; response.type = SESSION; response.state = EXPIRED_SESSION; - response.error = ZSESSIONEXPIRED; + response.error = Error::ZSESSIONEXPIRED; for (auto & callback : path_watches.second) { @@ -1421,7 +1422,7 @@ void ZooKeeper::finalize(bool error_send, bool error_receive) ResponsePtr response = info.request->makeResponse(); if (response) { - response->error = ZSESSIONEXPIRED; + response->error = Error::ZSESSIONEXPIRED; try { info.callback(*response); @@ -1437,7 +1438,7 @@ void ZooKeeper::finalize(bool error_send, bool error_receive) WatchResponse response; response.type = SESSION; response.state = EXPIRED_SESSION; - response.error = ZSESSIONEXPIRED; + response.error = Error::ZSESSIONEXPIRED; try { info.watch(response); @@ -1466,9 +1467,9 @@ void ZooKeeper::pushRequest(RequestInfo && info) { info.request->xid = next_xid.fetch_add(1); if (info.request->xid == close_xid) - throw Exception("xid equal to close_xid", ZSESSIONEXPIRED); + throw Exception("xid equal to close_xid", Error::ZSESSIONEXPIRED); if (info.request->xid < 0) - throw Exception("XID overflow", ZSESSIONEXPIRED); + throw Exception("XID overflow", Error::ZSESSIONEXPIRED); } /// We must serialize 'pushRequest' and 'finalize' (from sendThread, receiveThread) calls @@ -1478,10 +1479,10 @@ void ZooKeeper::pushRequest(RequestInfo && info) std::lock_guard lock(push_request_mutex); if (expired) - throw Exception("Session expired", ZSESSIONEXPIRED); + throw Exception("Session expired", Error::ZSESSIONEXPIRED); if (!requests_queue.tryPush(std::move(info), operation_timeout.totalMilliseconds())) - throw Exception("Cannot push request to queue within operation timeout", ZOPERATIONTIMEOUT); + throw Exception("Cannot push request to queue within operation timeout", Error::ZOPERATIONTIMEOUT); } catch (...) { @@ -1651,7 +1652,7 @@ void ZooKeeper::close() request_info.request = std::make_shared(std::move(request)); if (!requests_queue.tryPush(std::move(request_info), operation_timeout.totalMilliseconds())) - throw Exception("Cannot push close request to queue within operation timeout", ZOPERATIONTIMEOUT); + throw Exception("Cannot push close request to queue within operation timeout", Error::ZOPERATIONTIMEOUT); ProfileEvents::increment(ProfileEvents::ZooKeeperClose); } diff --git a/src/Common/ZooKeeper/tests/gtest_zkutil_test_multi_exception.cpp b/src/Common/ZooKeeper/tests/gtest_zkutil_test_multi_exception.cpp index cd4c6e0a159..8440b4fe7c9 100644 --- a/src/Common/ZooKeeper/tests/gtest_zkutil_test_multi_exception.cpp +++ b/src/Common/ZooKeeper/tests/gtest_zkutil_test_multi_exception.cpp @@ -86,7 +86,7 @@ TEST(zkutil, MultiAsync) ops.clear(); auto res = fut.get(); - ASSERT_EQ(res.error, Coordination::ZOK); + ASSERT_EQ(res.error, Coordination::Error::ZOK); ASSERT_EQ(res.responses.size(), 2); } @@ -126,15 +126,15 @@ TEST(zkutil, MultiAsync) /// The test is quite heavy. It is normal if session is expired during this test. /// If we don't check that, the test will be flacky. - if (res.error != Coordination::ZSESSIONEXPIRED && res.error != Coordination::ZCONNECTIONLOSS) + if (res.error != Coordination::Error::ZSESSIONEXPIRED && res.error != Coordination::Error::ZCONNECTIONLOSS) { - ASSERT_EQ(res.error, Coordination::ZNODEEXISTS); + ASSERT_EQ(res.error, Coordination::Error::ZNODEEXISTS); ASSERT_EQ(res.responses.size(), 2); } } catch (const Coordination::Exception & e) { - if (e.code != Coordination::ZSESSIONEXPIRED && e.code != Coordination::ZCONNECTIONLOSS) + if (e.code != Coordination::Error::ZSESSIONEXPIRED && e.code != Coordination::Error::ZCONNECTIONLOSS) throw; } } diff --git a/src/Common/ZooKeeper/tests/zkutil_expiration_test.cpp b/src/Common/ZooKeeper/tests/zkutil_expiration_test.cpp index d245428db8e..e09c72a4d6c 100644 --- a/src/Common/ZooKeeper/tests/zkutil_expiration_test.cpp +++ b/src/Common/ZooKeeper/tests/zkutil_expiration_test.cpp @@ -39,12 +39,12 @@ int main(int argc, char ** argv) ops.emplace_back(zkutil::makeRemoveRequest("/test/zk_expiration_test", -1)); Coordination::Responses responses; - int32_t code = zk.tryMultiNoThrow(ops, responses); + Coordination::Error code = zk.tryMultiNoThrow(ops, responses); - std::cout << time(nullptr) - time0 << "s: " << zkutil::ZooKeeper::error2string(code) << std::endl; + std::cout << time(nullptr) - time0 << "s: " << Coordination::errorMessage(code) << std::endl; try { - if (code) + if (code != Coordination::Error::ZOK) std::cout << "Path: " << zkutil::KeeperMultiException(code, ops, responses).getPathForFirstFailedOp() << std::endl; } catch (...) diff --git a/src/Common/ZooKeeper/tests/zkutil_test_commands_new_lib.cpp b/src/Common/ZooKeeper/tests/zkutil_test_commands_new_lib.cpp index d9d3402fa32..89659fa5e46 100644 --- a/src/Common/ZooKeeper/tests/zkutil_test_commands_new_lib.cpp +++ b/src/Common/ZooKeeper/tests/zkutil_test_commands_new_lib.cpp @@ -49,8 +49,8 @@ try zk.create("/test", "old", false, false, {}, [&](const CreateResponse & response) { - if (response.error) - std::cerr << "Error (create) " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Error (create): " << errorMessage(response.error) << '\n'; else std::cerr << "Created path: " << response.path_created << '\n'; @@ -64,8 +64,8 @@ try zk.get("/test", [&](const GetResponse & response) { - if (response.error) - std::cerr << "Error (get) " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Error (get): " << errorMessage(response.error) << '\n'; else std::cerr << "Value: " << response.data << '\n'; @@ -73,8 +73,8 @@ try }, [](const WatchResponse & response) { - if (response.error) - std::cerr << "Watch (get) on /test, Error " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Watch (get) on /test, Error: " << errorMessage(response.error) << '\n'; else std::cerr << "Watch (get) on /test, path: " << response.path << ", type: " << response.type << '\n'; }); @@ -86,8 +86,8 @@ try zk.set("/test", "new", -1, [&](const SetResponse & response) { - if (response.error) - std::cerr << "Error (set) " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Error (set): " << errorMessage(response.error) << '\n'; else std::cerr << "Set\n"; @@ -101,8 +101,8 @@ try zk.list("/", [&](const ListResponse & response) { - if (response.error) - std::cerr << "Error (list) " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Error (list): " << errorMessage(response.error) << '\n'; else { std::cerr << "Children:\n"; @@ -114,8 +114,8 @@ try }, [](const WatchResponse & response) { - if (response.error) - std::cerr << "Watch (list) on /, Error " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Watch (list) on /, Error: " << errorMessage(response.error) << '\n'; else std::cerr << "Watch (list) on /, path: " << response.path << ", type: " << response.type << '\n'; }); @@ -127,8 +127,8 @@ try zk.exists("/test", [&](const ExistsResponse & response) { - if (response.error) - std::cerr << "Error (exists) " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Error (exists): " << errorMessage(response.error) << '\n'; else std::cerr << "Exists\n"; @@ -136,8 +136,8 @@ try }, [](const WatchResponse & response) { - if (response.error) - std::cerr << "Watch (exists) on /test, Error " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Watch (exists) on /test, Error: " << errorMessage(response.error) << '\n'; else std::cerr << "Watch (exists) on /test, path: " << response.path << ", type: " << response.type << '\n'; }); @@ -148,8 +148,8 @@ try zk.remove("/test", -1, [&](const RemoveResponse & response) { - if (response.error) - std::cerr << "Error (remove) " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Error (remove): " << errorMessage(response.error) << '\n'; else std::cerr << "Removed\n"; @@ -184,13 +184,13 @@ try zk.multi(ops, [&](const MultiResponse & response) { - if (response.error) - std::cerr << "Error (multi) " << response.error << ": " << errorMessage(response.error) << '\n'; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Error (multi): " << errorMessage(response.error) << '\n'; else { for (const auto & elem : response.responses) - if (elem->error) - std::cerr << "Error (elem) " << elem->error << ": " << errorMessage(elem->error) << '\n'; + if (elem->error != Coordination::Error::ZOK) + std::cerr << "Error (elem): " << errorMessage(elem->error) << '\n'; std::cerr << "Created path: " << dynamic_cast(*response.responses[0]).path_created << '\n'; } diff --git a/src/Common/ZooKeeper/tests/zookeeper_impl.cpp b/src/Common/ZooKeeper/tests/zookeeper_impl.cpp index 74ba63514f2..3c2e52c93f2 100644 --- a/src/Common/ZooKeeper/tests/zookeeper_impl.cpp +++ b/src/Common/ZooKeeper/tests/zookeeper_impl.cpp @@ -9,8 +9,8 @@ try zookeeper.create("/test", "hello", false, false, {}, [](const Coordination::CreateResponse & response) { - if (response.error) - std::cerr << "Error " << response.error << ": " << Coordination::errorMessage(response.error) << "\n"; + if (response.error != Coordination::Error::ZOK) + std::cerr << "Error: " << Coordination::errorMessage(response.error) << "\n"; else std::cerr << "Path created: " << response.path_created << "\n"; }); diff --git a/src/Common/assert_cast.h b/src/Common/assert_cast.h index 7f9a19805bb..b70068b8e81 100644 --- a/src/Common/assert_cast.h +++ b/src/Common/assert_cast.h @@ -13,7 +13,7 @@ namespace DB { namespace ErrorCodes { - extern const int BAD_CAST; + extern const int LOGICAL_ERROR; } } @@ -41,11 +41,11 @@ To assert_cast(From && from) } catch (const std::exception & e) { - throw DB::Exception(e.what(), DB::ErrorCodes::BAD_CAST); + throw DB::Exception(e.what(), DB::ErrorCodes::LOGICAL_ERROR); } throw DB::Exception("Bad cast from type " + demangle(typeid(from).name()) + " to " + demangle(typeid(To).name()), - DB::ErrorCodes::BAD_CAST); + DB::ErrorCodes::LOGICAL_ERROR); #else return static_cast(from); #endif diff --git a/src/Common/clearPasswordFromCommandLine.cpp b/src/Common/clearPasswordFromCommandLine.cpp new file mode 100644 index 00000000000..0ff56e25c3f --- /dev/null +++ b/src/Common/clearPasswordFromCommandLine.cpp @@ -0,0 +1,18 @@ +#include +#include "clearPasswordFromCommandLine.h" + +void clearPasswordFromCommandLine(int argc, char ** argv) +{ + for (int arg = 1; arg < argc; ++arg) + { + if (arg + 1 < argc && 0 == strcmp(argv[arg], "--password")) + { + ++arg; + memset(argv[arg], 0, strlen(argv[arg])); + } + else if (0 == strncmp(argv[arg], "--password=", strlen("--password="))) + { + memset(argv[arg] + strlen("--password="), 0, strlen(argv[arg]) - strlen("--password=")); + } + } +} diff --git a/src/Common/clearPasswordFromCommandLine.h b/src/Common/clearPasswordFromCommandLine.h new file mode 100644 index 00000000000..cf90fea1dc8 --- /dev/null +++ b/src/Common/clearPasswordFromCommandLine.h @@ -0,0 +1,6 @@ +#pragma once + +/** If there are --password=... or --password ... arguments in command line, replace their values with zero bytes. + * This is needed to prevent password exposure in 'ps' and similar tools. + */ +void clearPasswordFromCommandLine(int argc, char ** argv); diff --git a/src/Common/config.h.in b/src/Common/config.h.in index 237ca81ff9e..e6bc46256e0 100644 --- a/src/Common/config.h.in +++ b/src/Common/config.h.in @@ -9,5 +9,6 @@ #cmakedefine01 USE_BROTLI #cmakedefine01 USE_UNWIND #cmakedefine01 USE_OPENCL +#cmakedefine01 USE_CASSANDRA #cmakedefine01 USE_GRPC #cmakedefine01 CLICKHOUSE_SPLIT_BINARY diff --git a/src/Common/tests/CMakeLists.txt b/src/Common/tests/CMakeLists.txt index b68e71c0b43..2653ab30c29 100644 --- a/src/Common/tests/CMakeLists.txt +++ b/src/Common/tests/CMakeLists.txt @@ -26,7 +26,6 @@ add_executable (int_hashes_perf int_hashes_perf.cpp) target_link_libraries (int_hashes_perf PRIVATE clickhouse_common_io) add_executable (simple_cache simple_cache.cpp) -target_include_directories (simple_cache PRIVATE ${DBMS_INCLUDE_DIR}) target_link_libraries (simple_cache PRIVATE common) add_executable (compact_array compact_array.cpp) diff --git a/src/Common/typeid_cast.h b/src/Common/typeid_cast.h index 29ad2e520c0..f28271fb53b 100644 --- a/src/Common/typeid_cast.h +++ b/src/Common/typeid_cast.h @@ -15,7 +15,7 @@ namespace DB { namespace ErrorCodes { - extern const int BAD_CAST; + extern const int LOGICAL_ERROR; } } @@ -34,11 +34,11 @@ std::enable_if_t, To> typeid_cast(From & from) } catch (const std::exception & e) { - throw DB::Exception(e.what(), DB::ErrorCodes::BAD_CAST); + throw DB::Exception(e.what(), DB::ErrorCodes::LOGICAL_ERROR); } throw DB::Exception("Bad cast from type " + demangle(typeid(from).name()) + " to " + demangle(typeid(To).name()), - DB::ErrorCodes::BAD_CAST); + DB::ErrorCodes::LOGICAL_ERROR); } @@ -54,7 +54,7 @@ std::enable_if_t, To> typeid_cast(From * from) } catch (const std::exception & e) { - throw DB::Exception(e.what(), DB::ErrorCodes::BAD_CAST); + throw DB::Exception(e.what(), DB::ErrorCodes::LOGICAL_ERROR); } } @@ -71,6 +71,6 @@ std::enable_if_t, To> typeid_cast(const std::shared_ptr } catch (const std::exception & e) { - throw DB::Exception(e.what(), DB::ErrorCodes::BAD_CAST); + throw DB::Exception(e.what(), DB::ErrorCodes::LOGICAL_ERROR); } } diff --git a/src/Common/ya.make b/src/Common/ya.make index 83a419212bd..327089ff31d 100644 --- a/src/Common/ya.make +++ b/src/Common/ya.make @@ -30,6 +30,7 @@ SRCS( Config/configReadClient.cpp Config/ConfigReloader.cpp createHardLink.cpp + clearPasswordFromCommandLine.cpp CurrentMetrics.cpp CurrentThread.cpp DNSResolver.cpp diff --git a/src/Compression/CompressionCodecDelta.cpp b/src/Compression/CompressionCodecDelta.cpp index 2369e2ca232..6c7cf92a41d 100644 --- a/src/Compression/CompressionCodecDelta.cpp +++ b/src/Compression/CompressionCodecDelta.cpp @@ -166,6 +166,9 @@ void registerCodecDelta(CompressionCodecFactory & factory) const auto children = arguments->children; const auto * literal = children[0]->as(); + if (!literal) + throw Exception("Delta codec argument must be integer", ErrorCodes::ILLEGAL_CODEC_PARAMETER); + size_t user_bytes_size = literal->value.safeGet(); if (user_bytes_size != 1 && user_bytes_size != 2 && user_bytes_size != 4 && user_bytes_size != 8) throw Exception("Delta value for delta codec can be 1, 2, 4 or 8, given " + toString(user_bytes_size), ErrorCodes::ILLEGAL_CODEC_PARAMETER); diff --git a/src/Compression/CompressionCodecDoubleDelta.cpp b/src/Compression/CompressionCodecDoubleDelta.cpp index 95fa51d1bd0..19f2dc11e85 100644 --- a/src/Compression/CompressionCodecDoubleDelta.cpp +++ b/src/Compression/CompressionCodecDoubleDelta.cpp @@ -166,6 +166,7 @@ UInt32 compressDataForType(const char * source, UInt32 source_size, char * dest) throw Exception("Cannot compress, data size " + toString(source_size) + " is not aligned to " + toString(sizeof(ValueType)), ErrorCodes::CANNOT_COMPRESS); const char * source_end = source + source_size; + const char * dest_start = dest; const UInt32 items_count = source_size / sizeof(ValueType); unalignedStore(dest, items_count); @@ -229,7 +230,7 @@ UInt32 compressDataForType(const char * source, UInt32 source_size, char * dest) writer.flush(); - return sizeof(items_count) + sizeof(prev_value) + sizeof(prev_delta) + writer.count() / 8; + return (dest - dest_start) + (writer.count() + 7) / 8; } template @@ -237,7 +238,6 @@ void decompressDataForType(const char * source, UInt32 source_size, char * dest) { static_assert(is_unsigned_v, "ValueType must be unsigned."); using UnsignedDeltaType = ValueType; - using SignedDeltaType = typename std::make_signed::type; const char * source_end = source + source_size; @@ -286,12 +286,13 @@ void decompressDataForType(const char * source, UInt32 source_size, char * dest) if (write_spec.data_bits != 0) { const UInt8 sign = reader.readBit(); - SignedDeltaType signed_dd = static_cast(reader.readBits(write_spec.data_bits - 1) + 1); + double_delta = reader.readBits(write_spec.data_bits - 1) + 1; if (sign) { - signed_dd *= -1; + /// It's well defined for unsigned data types. + /// In constrast, it's undefined to do negation of the most negative signed number due to overflow. + double_delta = -double_delta; } - double_delta = static_cast(signed_dd); } const UnsignedDeltaType delta = double_delta + prev_delta; diff --git a/src/Compression/CompressionCodecGorilla.cpp b/src/Compression/CompressionCodecGorilla.cpp index 5782da791a1..7ba128cfe4e 100644 --- a/src/Compression/CompressionCodecGorilla.cpp +++ b/src/Compression/CompressionCodecGorilla.cpp @@ -90,6 +90,7 @@ UInt32 compressDataForType(const char * source, UInt32 source_size, char * dest, if (source_size % sizeof(T) != 0) throw Exception("Cannot compress, data size " + toString(source_size) + " is not aligned to " + toString(sizeof(T)), ErrorCodes::CANNOT_COMPRESS); const char * source_end = source + source_size; + const char * dest_start = dest; const char * dest_end = dest + dest_size; const UInt32 items_count = source_size / sizeof(T); @@ -145,7 +146,7 @@ UInt32 compressDataForType(const char * source, UInt32 source_size, char * dest, writer.flush(); - return sizeof(items_count) + sizeof(prev_value) + writer.count() / 8; + return (dest - dest_start) + (writer.count() + 7) / 8; } template diff --git a/src/Compression/CompressionCodecLZ4.cpp b/src/Compression/CompressionCodecLZ4.cpp index cf8f8e976ea..32c3958e65e 100644 --- a/src/Compression/CompressionCodecLZ4.cpp +++ b/src/Compression/CompressionCodecLZ4.cpp @@ -19,6 +19,7 @@ namespace ErrorCodes { extern const int CANNOT_COMPRESS; extern const int ILLEGAL_SYNTAX_FOR_CODEC_TYPE; +extern const int ILLEGAL_CODEC_PARAMETER; } @@ -84,6 +85,9 @@ void registerCodecLZ4HC(CompressionCodecFactory & factory) const auto children = arguments->children; const auto * literal = children[0]->as(); + if (!literal) + throw Exception("LZ4HC codec argument must be integer", ErrorCodes::ILLEGAL_CODEC_PARAMETER); + level = literal->value.safeGet(); } diff --git a/src/Compression/CompressionCodecZSTD.cpp b/src/Compression/CompressionCodecZSTD.cpp index a9dc5de59ad..f1030d87ddd 100644 --- a/src/Compression/CompressionCodecZSTD.cpp +++ b/src/Compression/CompressionCodecZSTD.cpp @@ -74,6 +74,9 @@ void registerCodecZSTD(CompressionCodecFactory & factory) const auto children = arguments->children; const auto * literal = children[0]->as(); + if (!literal) + throw Exception("ZSTD codec argument must be integer", ErrorCodes::ILLEGAL_CODEC_PARAMETER); + level = literal->value.safeGet(); if (level > ZSTD_maxCLevel()) throw Exception("ZSTD codec can't have level more that " + toString(ZSTD_maxCLevel()) + ", given " + toString(level), ErrorCodes::ILLEGAL_CODEC_PARAMETER); diff --git a/src/Compression/ICompressionCodec.cpp b/src/Compression/ICompressionCodec.cpp index 64e6051b8d5..3c7766ba508 100644 --- a/src/Compression/ICompressionCodec.cpp +++ b/src/Compression/ICompressionCodec.cpp @@ -21,6 +21,8 @@ namespace ErrorCodes UInt32 ICompressionCodec::compress(const char * source, UInt32 source_size, char * dest) const { + assert(source != nullptr && dest != nullptr); + dest[0] = getMethodByte(); UInt8 header_size = getHeaderSize(); /// Write data from header_size @@ -33,8 +35,9 @@ UInt32 ICompressionCodec::compress(const char * source, UInt32 source_size, char UInt32 ICompressionCodec::decompress(const char * source, UInt32 source_size, char * dest) const { - UInt8 header_size = getHeaderSize(); + assert(source != nullptr && dest != nullptr); + UInt8 header_size = getHeaderSize(); if (source_size < header_size) throw Exception("Can't decompress data: the compressed data size (" + toString(source_size) + ", this should include header size) is less than the header size (" + toString(header_size) + ")", ErrorCodes::CORRUPTED_DATA); diff --git a/src/Compression/tests/gtest_compressionCodec.cpp b/src/Compression/tests/gtest_compressionCodec.cpp index 36fc7eba8f8..dc33fc50252 100644 --- a/src/Compression/tests/gtest_compressionCodec.cpp +++ b/src/Compression/tests/gtest_compressionCodec.cpp @@ -220,7 +220,7 @@ template if (l_size != r_size) { - result = ::testing::AssertionFailure() << "size mismatch expected: " << l_size << " got:" << r_size; + result = ::testing::AssertionFailure() << "size mismatch, expected: " << l_size << " got:" << r_size; } if (l_size == 0 || r_size == 0) { @@ -403,11 +403,6 @@ CodecTestSequence generateSeq(Generator gen, const char* gen_name, B Begin = 0, { const T v = gen(static_cast(i)); -// if constexpr (debug_log_items) -// { -// std::cerr << "#" << i << " " << type_name() << "(" << sizeof(T) << " bytes) : " << v << std::endl; -// } - unalignedStore(write_pos, v); write_pos += sizeof(v); } @@ -483,6 +478,7 @@ void testTranscoding(Timer & timer, ICompressionCodec & codec, const CodecTestSe timer.start(); + assert(source_data.data() != nullptr); // Codec assumes that source buffer is not null. const UInt32 encoded_size = codec.compress(source_data.data(), source_data.size(), encoded.data()); timer.report("encoding"); @@ -751,7 +747,11 @@ private: auto RandomishGenerator = [](auto i) { - return static_cast(sin(static_cast(i * i)) * i); + using T = decltype(i); + double sin_value = sin(static_cast(i * i)) * i; + if (sin_value < std::numeric_limits::lowest() || sin_value > std::numeric_limits::max()) + return T{}; + return T(sin_value); }; auto MinMaxGenerator = []() @@ -796,7 +796,8 @@ std::vector generatePyramidOfSequences(const size_t sequences std::vector sequences; sequences.reserve(sequences_count); - sequences.push_back(makeSeq()); // sequence of size 0 + // Don't test against sequence of size 0, since it causes a nullptr source buffer as codec input and produces an error. + // sequences.push_back(makeSeq()); // sequence of size 0 for (size_t i = 1; i < sequences_count; ++i) { std::string name = generator_name + std::string(" from 0 to ") + std::to_string(i); diff --git a/src/Core/Settings.h b/src/Core/Settings.h index c3c8eedbb12..cd9de5abec3 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -28,6 +28,7 @@ class IColumn; /** Settings of query execution. + * These settings go to users.xml. */ struct Settings : public SettingsCollection { @@ -45,7 +46,7 @@ struct Settings : public SettingsCollection * A setting is "IMPORTANT" if it affects the results of queries and can't be ignored by older versions. */ -#define LIST_OF_SETTINGS(M) \ +#define COMMON_SETTINGS(M) \ M(SettingUInt64, min_compress_block_size, 65536, "The actual size of the block to compress, if the uncompressed data less than max_compress_block_size is no less than this value and no less than the volume of data for one mark.", 0) \ M(SettingUInt64, max_compress_block_size, 1048576, "The maximum size of blocks of uncompressed data before compressing for writing to a table.", 0) \ M(SettingUInt64, max_block_size, DEFAULT_BLOCK_SIZE, "Maximum block size for reading", 0) \ @@ -184,39 +185,10 @@ struct Settings : public SettingsCollection \ M(SettingString, count_distinct_implementation, "uniqExact", "What aggregate function to use for implementation of count(DISTINCT ...)", 0) \ \ - M(SettingBool, output_format_enable_streaming, false, "Enable streaming in output formats that support it.", 0) \ - M(SettingBool, output_format_write_statistics, true, "Write statistics about read rows, bytes, time elapsed in suitable output formats.", 0) \ - \ M(SettingBool, add_http_cors_header, false, "Write add http CORS header.", 0) \ \ M(SettingUInt64, max_http_get_redirects, 0, "Max number of http GET redirects hops allowed. Make sure additional security measures are in place to prevent a malicious server to redirect your requests to unexpected services.", 0) \ \ - M(SettingBool, input_format_skip_unknown_fields, false, "Skip columns with unknown names from input data (it works for JSONEachRow, CSVWithNames, TSVWithNames and TSKV formats).", 0) \ - M(SettingBool, input_format_with_names_use_header, true, "For TSVWithNames and CSVWithNames input formats this controls whether format parser is to assume that column data appear in the input exactly as they are specified in the header.", 0) \ - M(SettingBool, input_format_import_nested_json, false, "Map nested JSON data to nested tables (it works for JSONEachRow format).", 0) \ - M(SettingBool, input_format_defaults_for_omitted_fields, true, "For input data calculate default expressions for omitted fields (it works for JSONEachRow, CSV and TSV formats).", IMPORTANT) \ - M(SettingBool, input_format_tsv_empty_as_default, false, "Treat empty fields in TSV input as default values.", 0) \ - M(SettingBool, input_format_null_as_default, false, "For text input formats initialize null fields with default values if data type of this field is not nullable", 0) \ - \ - M(SettingBool, input_format_values_interpret_expressions, true, "For Values format: if the field could not be parsed by streaming parser, run SQL parser and try to interpret it as SQL expression.", 0) \ - M(SettingBool, input_format_values_deduce_templates_of_expressions, true, "For Values format: if the field could not be parsed by streaming parser, run SQL parser, deduce template of the SQL expression, try to parse all rows using template and then interpret expression for all rows.", 0) \ - M(SettingBool, input_format_values_accurate_types_of_literals, true, "For Values format: when parsing and interpreting expressions using template, check actual type of literal to avoid possible overflow and precision issues.", 0) \ - M(SettingURI, format_avro_schema_registry_url, {}, "For AvroConfluent format: Confluent Schema Registry URL.", 0) \ - \ - M(SettingBool, output_format_json_quote_64bit_integers, true, "Controls quoting of 64-bit integers in JSON output format.", 0) \ - \ - M(SettingBool, output_format_json_quote_denormals, false, "Enables '+nan', '-nan', '+inf', '-inf' outputs in JSON output format.", 0) \ - \ - M(SettingBool, output_format_json_escape_forward_slashes, true, "Controls escaping forward slashes for string outputs in JSON output format. This is intended for compatibility with JavaScript. Don't confuse with backslashes that are always escaped.", 0) \ - \ - M(SettingUInt64, output_format_pretty_max_rows, 10000, "Rows limit for Pretty formats.", 0) \ - M(SettingUInt64, output_format_pretty_max_column_pad_width, 250, "Maximum width to pad all values in a column in Pretty formats.", 0) \ - M(SettingBool, output_format_pretty_color, true, "Use ANSI escape sequences to paint colors in Pretty formats", 0) \ - M(SettingUInt64, output_format_parquet_row_group_size, 1000000, "Row group size in rows.", 0) \ - M(SettingString, output_format_avro_codec, "", "Compression codec used for output. Possible values: 'null', 'deflate', 'snappy'.", 0) \ - M(SettingUInt64, output_format_avro_sync_interval, 16 * 1024, "Sync interval in bytes.", 0) \ - M(SettingBool, output_format_tsv_crlf_end_of_line, false, "If it is set true, end of line in TSV format will be \\r\\n instead of \\n.", 0) \ - \ M(SettingBool, use_client_time_zone, false, "Use client timezone for interpreting DateTime string values, instead of adopting server timezone.", 0) \ \ M(SettingBool, send_progress_in_http_headers, false, "Send progress notifications using X-ClickHouse-Progress headers. Some clients do not support high amount of HTTP headers (Python requests in particular), so it is disabled by default.", 0) \ @@ -225,9 +197,6 @@ struct Settings : public SettingsCollection \ M(SettingBool, fsync_metadata, 1, "Do fsync after changing metadata for tables and databases (.sql files). Could be disabled in case of poor latency on server with high load of DDL queries and high load of disk subsystem.", 0) \ \ - M(SettingUInt64, input_format_allow_errors_num, 0, "Maximum absolute amount of errors while reading text formats (like CSV, TSV). In case of error, if at least absolute or relative amount of errors is lower than corresponding value, will skip until next line and continue.", 0) \ - M(SettingFloat, input_format_allow_errors_ratio, 0, "Maximum relative amount of errors while reading text formats (like CSV, TSV). In case of error, if at least absolute or relative amount of errors is lower than corresponding value, will skip until next line and continue.", 0) \ - \ M(SettingBool, join_use_nulls, 0, "Use NULLs for non-joined rows of outer JOINs for types that can be inside Nullable. If false, use default value of corresponding columns data type.", IMPORTANT) \ \ M(SettingJoinStrictness, join_default_strictness, JoinStrictness::ALL, "Set default strictness in JOIN query. Possible values: empty string, 'ANY', 'ALL'. If empty, query without strictness will throw exception.", 0) \ @@ -245,23 +214,6 @@ struct Settings : public SettingsCollection M(SettingMilliseconds, stream_flush_interval_ms, 7500, "Timeout for flushing data from streaming storages.", 0) \ M(SettingMilliseconds, stream_poll_timeout_ms, 500, "Timeout for polling data from/to streaming storages.", 0) \ \ - M(SettingString, format_schema, "", "Schema identifier (used by schema-based formats)", 0) \ - M(SettingString, format_template_resultset, "", "Path to file which contains format string for result set (for Template format)", 0) \ - M(SettingString, format_template_row, "", "Path to file which contains format string for rows (for Template format)", 0) \ - M(SettingString, format_template_rows_between_delimiter, "\n", "Delimiter between rows (for Template format)", 0) \ - \ - M(SettingString, format_custom_escaping_rule, "Escaped", "Field escaping rule (for CustomSeparated format)", 0) \ - M(SettingString, format_custom_field_delimiter, "\t", "Delimiter between fields (for CustomSeparated format)", 0) \ - M(SettingString, format_custom_row_before_delimiter, "", "Delimiter before field of the first column (for CustomSeparated format)", 0) \ - M(SettingString, format_custom_row_after_delimiter, "\n", "Delimiter after field of the last column (for CustomSeparated format)", 0) \ - M(SettingString, format_custom_row_between_delimiter, "", "Delimiter between rows (for CustomSeparated format)", 0) \ - M(SettingString, format_custom_result_before_delimiter, "", "Prefix before result set (for CustomSeparated format)", 0) \ - M(SettingString, format_custom_result_after_delimiter, "", "Suffix after result set (for CustomSeparated format)", 0) \ - \ - M(SettingString, format_regexp, "", "Regular expression (for Regexp format)", 0) \ - M(SettingString, format_regexp_escaping_rule, "Escaped", "Field escaping rule (for Regexp format)", 0) \ - M(SettingBool, format_regexp_skip_unmatched, false, "Skip lines unmatched by regular expression (for Regexp format", 0) \ - \ M(SettingBool, insert_allow_materialized_columns, 0, "If setting is enabled, Allow materialized columns in INSERT.", 0) \ M(SettingSeconds, http_connection_timeout, DEFAULT_HTTP_READ_BUFFER_CONNECTION_TIMEOUT, "HTTP connection timeout.", 0) \ M(SettingSeconds, http_send_timeout, DEFAULT_HTTP_READ_BUFFER_TIMEOUT, "HTTP send timeout", 0) \ @@ -275,6 +227,8 @@ struct Settings : public SettingsCollection M(SettingUInt64, odbc_max_field_size, 1024, "Max size of filed can be read from ODBC dictionary. Long strings are truncated.", 0) \ M(SettingUInt64, query_profiler_real_time_period_ns, 1000000000, "Period for real clock timer of query profiler (in nanoseconds). Set 0 value to turn off the real clock query profiler. Recommended value is at least 10000000 (100 times a second) for single queries or 1000000000 (once a second) for cluster-wide profiling.", 0) \ M(SettingUInt64, query_profiler_cpu_time_period_ns, 1000000000, "Period for CPU clock timer of query profiler (in nanoseconds). Set 0 value to turn off the CPU clock query profiler. Recommended value is at least 10000000 (100 times a second) for single queries or 1000000000 (once a second) for cluster-wide profiling.", 0) \ + M(SettingBool, metrics_perf_events_enabled, false, "If enabled, some of the perf events will be measured throughout queries' execution.", 0) \ + M(SettingString, metrics_perf_events_list, "", "Comma separated list of perf metrics that will be measured throughout queries' execution. Empty means all events. See PerfEventInfo in sources for the available events.", 0) \ \ \ /** Limits during query execution are part of the settings. \ @@ -357,13 +311,7 @@ struct Settings : public SettingsCollection M(SettingUInt64, max_network_bytes, 0, "The maximum number of bytes (compressed) to receive or transmit over the network for execution of the query.", 0) \ M(SettingUInt64, max_network_bandwidth_for_user, 0, "The maximum speed of data exchange over the network in bytes per second for all concurrently running user queries. Zero means unlimited.", 0)\ M(SettingUInt64, max_network_bandwidth_for_all_users, 0, "The maximum speed of data exchange over the network in bytes per second for all concurrently running queries. Zero means unlimited.", 0) \ - M(SettingChar, format_csv_delimiter, ',', "The character to be considered as a delimiter in CSV data. If setting with a string, a string has to have a length of 1.", 0) \ - M(SettingBool, format_csv_allow_single_quotes, 1, "If it is set to true, allow strings in single quotes.", 0) \ - M(SettingBool, format_csv_allow_double_quotes, 1, "If it is set to true, allow strings in double quotes.", 0) \ - M(SettingBool, output_format_csv_crlf_end_of_line, false, "If it is set true, end of line in CSV format will be \\r\\n instead of \\n.", 0) \ - M(SettingBool, input_format_csv_unquoted_null_literal_as_null, false, "Consider unquoted NULL literal as \\N", 0) \ \ - M(SettingDateTimeInputFormat, date_time_input_format, FormatSettings::DateTimeInputFormat::Basic, "Method to read DateTime from text input formats. Possible values: 'basic' and 'best_effort'.", 0) \ M(SettingBool, log_profile_events, true, "Log query performance statistics into the query_log and query_thread_log.", 0) \ M(SettingBool, log_query_settings, true, "Log query settings into the query_log.", 0) \ M(SettingBool, log_query_threads, true, "Log query threads into system.query_thread_log table. This setting have effect only when 'log_queries' is true.", 0) \ @@ -384,6 +332,7 @@ struct Settings : public SettingsCollection M(SettingBool, enable_debug_queries, false, "Enables debug queries such as AST.", 0) \ M(SettingBool, enable_unaligned_array_join, false, "Allow ARRAY JOIN with multiple arrays that have different sizes. When this settings is enabled, arrays will be resized to the longest one.", 0) \ M(SettingBool, optimize_read_in_order, true, "Enable ORDER BY optimization for reading data in corresponding order in MergeTree tables.", 0) \ + M(SettingBool, optimize_aggregation_in_order, false, "Enable GROUP BY optimization for aggregating data in corresponding order in MergeTree tables.", 0) \ M(SettingBool, low_cardinality_allow_in_native_format, true, "Use LowCardinality type in Native format. Otherwise, convert LowCardinality columns to ordinary for select query, and convert ordinary columns to required LowCardinality for insert query.", 0) \ M(SettingBool, cancel_http_readonly_queries_on_client_close, false, "Cancel HTTP readonly queries when a client closes the connection without waiting for response.", 0) \ M(SettingBool, external_table_functions_use_nulls, true, "If it is set to true, external table functions will implicitly use Nullable type if needed. Otherwise NULLs will be substituted with default values. Currently supported only by 'mysql' and 'odbc' table functions.", 0) \ @@ -410,6 +359,7 @@ struct Settings : public SettingsCollection M(SettingBool, enable_scalar_subquery_optimization, true, "If it is set to true, prevent scalar subqueries from (de)serializing large scalar values and possibly avoid running the same subquery more than once.", 0) \ M(SettingBool, optimize_trivial_count_query, true, "Process trivial 'SELECT count() FROM table' query from metadata.", 0) \ M(SettingUInt64, mutations_sync, 0, "Wait for synchronous execution of ALTER TABLE UPDATE/DELETE queries (mutations). 0 - execute asynchronously. 1 - wait current server. 2 - wait all replicas if they exist.", 0) \ + M(SettingBool, optimize_arithmetic_operations_in_aggregate_functions, true, "Move arithmetic operations out of aggregation functions", 0) \ M(SettingBool, optimize_if_chain_to_miltiif, false, "Replace if(cond1, then1, if(cond2, ...)) chains to multiIf. Currently it's not beneficial for numeric types.", 0) \ M(SettingBool, allow_experimental_alter_materialized_view_structure, false, "Allow atomic alter on Materialized views. Work in progress.", 0) \ M(SettingBool, enable_early_constant_folding, true, "Enable query optimization where we analyze function and subqueries results and rewrite query if there're constants there", 0) \ @@ -439,7 +389,70 @@ struct Settings : public SettingsCollection M(SettingUInt64, mark_cache_min_lifetime, 0, "Obsolete setting, does nothing. Will be removed after 2020-05-31", 0) \ M(SettingBool, partial_merge_join, false, "Obsolete. Use join_algorithm='prefer_partial_merge' instead.", 0) \ M(SettingUInt64, max_memory_usage_for_all_queries, 0, "Obsolete. Will be removed after 2020-10-20", 0) \ - M(SettingBool, experimental_use_processors, true, "Obsolete setting, does nothing. Will be removed after 2020-11-29.", 0) \ + \ + M(SettingBool, experimental_use_processors, true, "Obsolete setting, does nothing. Will be removed after 2020-11-29.", 0) + +#define FORMAT_FACTORY_SETTINGS(M) \ + M(SettingChar, format_csv_delimiter, ',', "The character to be considered as a delimiter in CSV data. If setting with a string, a string has to have a length of 1.", 0) \ + M(SettingBool, format_csv_allow_single_quotes, 1, "If it is set to true, allow strings in single quotes.", 0) \ + M(SettingBool, format_csv_allow_double_quotes, 1, "If it is set to true, allow strings in double quotes.", 0) \ + M(SettingBool, output_format_csv_crlf_end_of_line, false, "If it is set true, end of line in CSV format will be \\r\\n instead of \\n.", 0) \ + M(SettingBool, input_format_csv_unquoted_null_literal_as_null, false, "Consider unquoted NULL literal as \\N", 0) \ + M(SettingBool, input_format_skip_unknown_fields, false, "Skip columns with unknown names from input data (it works for JSONEachRow, CSVWithNames, TSVWithNames and TSKV formats).", 0) \ + M(SettingBool, input_format_with_names_use_header, true, "For TSVWithNames and CSVWithNames input formats this controls whether format parser is to assume that column data appear in the input exactly as they are specified in the header.", 0) \ + M(SettingBool, input_format_import_nested_json, false, "Map nested JSON data to nested tables (it works for JSONEachRow format).", 0) \ + M(SettingBool, input_format_defaults_for_omitted_fields, true, "For input data calculate default expressions for omitted fields (it works for JSONEachRow, CSV and TSV formats).", IMPORTANT) \ + M(SettingBool, input_format_tsv_empty_as_default, false, "Treat empty fields in TSV input as default values.", 0) \ + M(SettingBool, input_format_null_as_default, false, "For text input formats initialize null fields with default values if data type of this field is not nullable", 0) \ + \ + M(SettingDateTimeInputFormat, date_time_input_format, FormatSettings::DateTimeInputFormat::Basic, "Method to read DateTime from text input formats. Possible values: 'basic' and 'best_effort'.", 0) \ + \ + M(SettingBool, input_format_values_interpret_expressions, true, "For Values format: if the field could not be parsed by streaming parser, run SQL parser and try to interpret it as SQL expression.", 0) \ + M(SettingBool, input_format_values_deduce_templates_of_expressions, true, "For Values format: if the field could not be parsed by streaming parser, run SQL parser, deduce template of the SQL expression, try to parse all rows using template and then interpret expression for all rows.", 0) \ + M(SettingBool, input_format_values_accurate_types_of_literals, true, "For Values format: when parsing and interpreting expressions using template, check actual type of literal to avoid possible overflow and precision issues.", 0) \ + M(SettingURI, format_avro_schema_registry_url, {}, "For AvroConfluent format: Confluent Schema Registry URL.", 0) \ + \ + M(SettingBool, output_format_json_quote_64bit_integers, true, "Controls quoting of 64-bit integers in JSON output format.", 0) \ + \ + M(SettingBool, output_format_json_quote_denormals, false, "Enables '+nan', '-nan', '+inf', '-inf' outputs in JSON output format.", 0) \ + \ + M(SettingBool, output_format_json_escape_forward_slashes, true, "Controls escaping forward slashes for string outputs in JSON output format. This is intended for compatibility with JavaScript. Don't confuse with backslashes that are always escaped.", 0) \ + \ + M(SettingUInt64, output_format_pretty_max_rows, 10000, "Rows limit for Pretty formats.", 0) \ + M(SettingUInt64, output_format_pretty_max_column_pad_width, 250, "Maximum width to pad all values in a column in Pretty formats.", 0) \ + M(SettingUInt64, output_format_pretty_max_value_width, 10000, "Maximum width of value to display in Pretty formats. If greater - it will be cut.", 0) \ + M(SettingBool, output_format_pretty_color, true, "Use ANSI escape sequences to paint colors in Pretty formats", 0) \ + M(SettingUInt64, output_format_parquet_row_group_size, 1000000, "Row group size in rows.", 0) \ + M(SettingString, output_format_avro_codec, "", "Compression codec used for output. Possible values: 'null', 'deflate', 'snappy'.", 0) \ + M(SettingUInt64, output_format_avro_sync_interval, 16 * 1024, "Sync interval in bytes.", 0) \ + M(SettingBool, output_format_tsv_crlf_end_of_line, false, "If it is set true, end of line in TSV format will be \\r\\n instead of \\n.", 0) \ + \ + M(SettingUInt64, input_format_allow_errors_num, 0, "Maximum absolute amount of errors while reading text formats (like CSV, TSV). In case of error, if at least absolute or relative amount of errors is lower than corresponding value, will skip until next line and continue.", 0) \ + M(SettingFloat, input_format_allow_errors_ratio, 0, "Maximum relative amount of errors while reading text formats (like CSV, TSV). In case of error, if at least absolute or relative amount of errors is lower than corresponding value, will skip until next line and continue.", 0) \ + \ + M(SettingString, format_schema, "", "Schema identifier (used by schema-based formats)", 0) \ + M(SettingString, format_template_resultset, "", "Path to file which contains format string for result set (for Template format)", 0) \ + M(SettingString, format_template_row, "", "Path to file which contains format string for rows (for Template format)", 0) \ + M(SettingString, format_template_rows_between_delimiter, "\n", "Delimiter between rows (for Template format)", 0) \ + \ + M(SettingString, format_custom_escaping_rule, "Escaped", "Field escaping rule (for CustomSeparated format)", 0) \ + M(SettingString, format_custom_field_delimiter, "\t", "Delimiter between fields (for CustomSeparated format)", 0) \ + M(SettingString, format_custom_row_before_delimiter, "", "Delimiter before field of the first column (for CustomSeparated format)", 0) \ + M(SettingString, format_custom_row_after_delimiter, "\n", "Delimiter after field of the last column (for CustomSeparated format)", 0) \ + M(SettingString, format_custom_row_between_delimiter, "", "Delimiter between rows (for CustomSeparated format)", 0) \ + M(SettingString, format_custom_result_before_delimiter, "", "Prefix before result set (for CustomSeparated format)", 0) \ + M(SettingString, format_custom_result_after_delimiter, "", "Suffix after result set (for CustomSeparated format)", 0) \ + \ + M(SettingString, format_regexp, "", "Regular expression (for Regexp format)", 0) \ + M(SettingString, format_regexp_escaping_rule, "Escaped", "Field escaping rule (for Regexp format)", 0) \ + M(SettingBool, format_regexp_skip_unmatched, false, "Skip lines unmatched by regular expression (for Regexp format", 0) \ + \ + M(SettingBool, output_format_enable_streaming, false, "Enable streaming in output formats that support it.", 0) \ + M(SettingBool, output_format_write_statistics, true, "Write statistics about read rows, bytes, time elapsed in suitable output formats.", 0) + + #define LIST_OF_SETTINGS(M) \ + COMMON_SETTINGS(M) \ + FORMAT_FACTORY_SETTINGS(M) DECLARE_SETTINGS_COLLECTION(LIST_OF_SETTINGS) diff --git a/src/Core/SettingsCollectionImpl.h b/src/Core/SettingsCollectionImpl.h index 8210b04e2da..877567a7caf 100644 --- a/src/Core/SettingsCollectionImpl.h +++ b/src/Core/SettingsCollectionImpl.h @@ -7,6 +7,8 @@ */ #include +#include + namespace DB { @@ -91,7 +93,16 @@ Field SettingsCollection::const_reference::getValue() const template Field SettingsCollection::valueToCorrespondingType(size_t index, const Field & value) { - return members()[index].value_to_corresponding_type(value); + try + { + return members()[index].value_to_corresponding_type(value); + } + catch (Exception & e) + { + e.addMessage(fmt::format("in attempt to set the value of setting to {}", + applyVisitor(FieldVisitorToString(), value))); + throw; + } } diff --git a/src/Core/SortCursor.h b/src/Core/SortCursor.h index edf507f8a1d..4c90cc723bf 100644 --- a/src/Core/SortCursor.h +++ b/src/Core/SortCursor.h @@ -63,7 +63,7 @@ struct SortCursorImpl for (auto & column_desc : desc) { if (!column_desc.column_name.empty()) - throw Exception("SortDesctiption should contain column position if SortCursor was used without header.", + throw Exception("SortDescription should contain column position if SortCursor was used without header.", ErrorCodes::LOGICAL_ERROR); } reset(columns, {}); diff --git a/src/Core/SortDescription.h b/src/Core/SortDescription.h index 6cc957cac55..86e4bb573ed 100644 --- a/src/Core/SortDescription.h +++ b/src/Core/SortDescription.h @@ -59,6 +59,13 @@ struct SortColumnDescription { return !(*this == other); } + + std::string dump() const + { + std::stringstream ss; + ss << column_name << ":" << column_number << ":dir " << direction << "nulls " << nulls_direction; + return ss.str(); + } }; /// Description of the sorting rule for several columns. diff --git a/src/DataStreams/AddingConstColumnBlockInputStream.h b/src/DataStreams/AddingConstColumnBlockInputStream.h deleted file mode 100644 index de51317211d..00000000000 --- a/src/DataStreams/AddingConstColumnBlockInputStream.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include - -namespace DB -{ - -/** Adds a materialized const column to the block with a specified value. - */ -template -class AddingConstColumnBlockInputStream : public IBlockInputStream -{ -public: - AddingConstColumnBlockInputStream( - BlockInputStreamPtr input_, - DataTypePtr data_type_, - T value_, - String column_name_) - : data_type(data_type_), value(value_), column_name(column_name_) - { - children.push_back(input_); - } - - String getName() const override { return "AddingConstColumn"; } - - Block getHeader() const override - { - Block res = children.back()->getHeader(); - res.insert({data_type->createColumn(), data_type, column_name}); - return res; - } - -protected: - Block readImpl() override - { - Block res = children.back()->read(); - if (!res) - return res; - - res.insert({data_type->createColumnConst(res.rows(), value)->convertToFullColumnIfConst(), data_type, column_name}); - return res; - } - -private: - DataTypePtr data_type; - T value; - String column_name; -}; - -} diff --git a/src/DataStreams/AggregatingBlockInputStream.cpp b/src/DataStreams/AggregatingBlockInputStream.cpp deleted file mode 100644 index 150f794ca59..00000000000 --- a/src/DataStreams/AggregatingBlockInputStream.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include - -#include -#include -#include -#include - - -namespace ProfileEvents -{ - extern const Event ExternalAggregationMerge; -} - -namespace DB -{ - -Block AggregatingBlockInputStream::getHeader() const -{ - return aggregator.getHeader(final); -} - - -Block AggregatingBlockInputStream::readImpl() -{ - if (!executed) - { - executed = true; - AggregatedDataVariantsPtr data_variants = std::make_shared(); - - Aggregator::CancellationHook hook = [&]() { return this->isCancelled(); }; - aggregator.setCancellationHook(hook); - - aggregator.execute(children.back(), *data_variants); - - if (!aggregator.hasTemporaryFiles()) - { - ManyAggregatedDataVariants many_data { data_variants }; - impl = aggregator.mergeAndConvertToBlocks(many_data, final, 1); - } - else - { - /** If there are temporary files with partially-aggregated data on the disk, - * then read and merge them, spending the minimum amount of memory. - */ - - ProfileEvents::increment(ProfileEvents::ExternalAggregationMerge); - - if (!isCancelled()) - { - /// Flush data in the RAM to disk also. It's easier than merging on-disk and RAM data. - if (data_variants->size()) // NOLINT - aggregator.writeToTemporaryFile(*data_variants); - } - - const auto & files = aggregator.getTemporaryFiles(); - BlockInputStreams input_streams; - for (const auto & file : files.files) - { - temporary_inputs.emplace_back(std::make_unique(file->path())); - input_streams.emplace_back(temporary_inputs.back()->block_in); - } - - LOG_TRACE(log, "Will merge {} temporary files of size {} compressed, {} uncompressed.", files.files.size(), ReadableSize(files.sum_size_compressed), ReadableSize(files.sum_size_uncompressed)); - - impl = std::make_unique(input_streams, params, final, 1, 1); - } - } - - if (isCancelledOrThrowIfKilled() || !impl) - return {}; - - return impl->read(); -} - -} diff --git a/src/DataStreams/AggregatingBlockInputStream.h b/src/DataStreams/AggregatingBlockInputStream.h deleted file mode 100644 index 009a9704e4e..00000000000 --- a/src/DataStreams/AggregatingBlockInputStream.h +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - - -namespace DB -{ - - -/** Aggregates the stream of blocks using the specified key columns and aggregate functions. - * Columns with aggregate functions adds to the end of the block. - * If final = false, the aggregate functions are not finalized, that is, they are not replaced by their value, but contain an intermediate state of calculations. - * This is necessary so that aggregation can continue (for example, by combining streams of partially aggregated data). - */ -class AggregatingBlockInputStream : public IBlockInputStream -{ -public: - /** keys are taken from the GROUP BY part of the query - * Aggregate functions are searched everywhere in the expression. - * Columns corresponding to keys and arguments of aggregate functions must already be computed. - */ - AggregatingBlockInputStream(const BlockInputStreamPtr & input, const Aggregator::Params & params_, bool final_) - : params(params_), aggregator(params), final(final_) - { - children.push_back(input); - } - - String getName() const override { return "Aggregating"; } - - Block getHeader() const override; - -protected: - Block readImpl() override; - - Aggregator::Params params; - Aggregator aggregator; - bool final; - - bool executed = false; - - std::vector> temporary_inputs; - - /** From here we will get the completed blocks after the aggregation. */ - std::unique_ptr impl; - - Poco::Logger * log = &Poco::Logger::get("AggregatingBlockInputStream"); -}; - -} diff --git a/src/DataStreams/BlocksBlockInputStream.h b/src/DataStreams/BlocksSource.h similarity index 100% rename from src/DataStreams/BlocksBlockInputStream.h rename to src/DataStreams/BlocksSource.h diff --git a/src/DataStreams/CheckConstraintsBlockOutputStream.cpp b/src/DataStreams/CheckConstraintsBlockOutputStream.cpp index 878ab0c4e37..8e075e5bf08 100644 --- a/src/DataStreams/CheckConstraintsBlockOutputStream.cpp +++ b/src/DataStreams/CheckConstraintsBlockOutputStream.cpp @@ -1,4 +1,3 @@ -#include #include #include #include diff --git a/src/DataStreams/CollapsingFinalBlockInputStream.cpp b/src/DataStreams/CollapsingFinalBlockInputStream.cpp deleted file mode 100644 index 29b9a5d9ab6..00000000000 --- a/src/DataStreams/CollapsingFinalBlockInputStream.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include -#include - -/// Maximum number of messages about incorrect data in the log. -#define MAX_ERROR_MESSAGES 10 - - -namespace DB -{ - -CollapsingFinalBlockInputStream::~CollapsingFinalBlockInputStream() -{ - queue = {}; - for (auto & block : output_blocks) - delete block; -} - -void CollapsingFinalBlockInputStream::reportBadCounts() -{ - /// With inconsistent data, this is an unavoidable error that can not be easily fixed by admins. Therefore Warning. - LOG_WARNING(log, "Incorrect data: number of rows with sign = 1 ({}) differs with number of rows with sign = -1 ({}) by more than one", count_positive, count_negative); -} - -void CollapsingFinalBlockInputStream::reportBadSign(Int8 sign) -{ - LOG_ERROR(log, "Invalid sign: {}", static_cast(sign)); -} - -void CollapsingFinalBlockInputStream::fetchNextBlock(size_t input_index) -{ - BlockInputStreamPtr stream = children[input_index]; - Block block = stream->read(); - if (!block) - return; - MergingBlockPtr merging_block(new MergingBlock(block, input_index, description, sign_column_name, &output_blocks)); - ++blocks_fetched; - queue.push(Cursor(merging_block)); -} - -void CollapsingFinalBlockInputStream::commitCurrent() -{ - if (count_positive || count_negative) - { - if (count_positive >= count_negative && last_is_positive) - { - last_positive.addToFilter(); - } - - if (!(count_positive == count_negative || count_positive + 1 == count_negative || count_positive == count_negative + 1)) - { - if (count_incorrect_data < MAX_ERROR_MESSAGES) - reportBadCounts(); - ++count_incorrect_data; - } - - last_positive = Cursor(); - previous = Cursor(); - } - - count_negative = 0; - count_positive = 0; -} - -Block CollapsingFinalBlockInputStream::readImpl() -{ - if (first) - { - for (size_t i = 0; i < children.size(); ++i) - fetchNextBlock(i); - - first = false; - } - - /// We will create blocks for the answer until we get a non-empty block. - while (true) - { - while (!queue.empty() && output_blocks.empty()) - { - Cursor current = queue.top(); - queue.pop(); - - bool has_next = !queue.empty(); - Cursor next = has_next ? queue.top() : Cursor(); - - /// We will advance in the current block, not using the queue, as long as possible. - while (true) - { - if (!current.equal(previous)) - { - commitCurrent(); - previous = current; - } - - Int8 sign = current.getSign(); - if (sign == 1) - { - last_positive = current; - last_is_positive = true; - ++count_positive; - } - else if (sign == -1) - { - last_is_positive = false; - ++count_negative; - } - else - reportBadSign(sign); - - if (current.isLast()) - { - fetchNextBlock(current.block->stream_index); - - /// All streams are over. We'll process the last key. - if (!has_next) - commitCurrent(); - - break; - } - else - { - current.next(); - - if (has_next && !(next < current)) - { - queue.push(current); - break; - } - } - } - } - - /// End of the stream. - if (output_blocks.empty()) - { - if (blocks_fetched != blocks_output) - LOG_ERROR(log, "Logical error: CollapsingFinalBlockInputStream has output {} blocks instead of {}", blocks_output, blocks_fetched); - - return Block(); - } - - MergingBlock * merging_block = output_blocks.back(); - Block block = merging_block->block; - - for (size_t i = 0; i < block.columns(); ++i) - block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->filter(merging_block->filter, -1); - - output_blocks.pop_back(); - delete merging_block; - - ++blocks_output; - - if (block) - return block; - } -} - -} diff --git a/src/DataStreams/CollapsingFinalBlockInputStream.h b/src/DataStreams/CollapsingFinalBlockInputStream.h deleted file mode 100644 index d090c53ddf9..00000000000 --- a/src/DataStreams/CollapsingFinalBlockInputStream.h +++ /dev/null @@ -1,211 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace DB -{ -namespace ErrorCodes -{ - extern const int BAD_TYPE_OF_FIELD; -} - - -/// Collapses the same rows with the opposite sign roughly like CollapsingSortedBlockInputStream. -/// Outputs the rows in random order (the input streams must still be ordered). -/// Outputs only rows with a positive sign. -class CollapsingFinalBlockInputStream : public IBlockInputStream -{ -public: - CollapsingFinalBlockInputStream( - const BlockInputStreams & inputs, - const SortDescription & description_, - const String & sign_column_name_) - : description(description_), sign_column_name(sign_column_name_) - { - children.insert(children.end(), inputs.begin(), inputs.end()); - } - - ~CollapsingFinalBlockInputStream() override; - - String getName() const override { return "CollapsingFinal"; } - - bool isSortedOutput() const override { return true; } - const SortDescription & getSortDescription() const override { return description; } - - Block getHeader() const override { return children.at(0)->getHeader(); } - - - struct MergingBlock; - using BlockPlainPtrs = std::vector; - - struct MergingBlock : boost::noncopyable - { - MergingBlock(const Block & block_, - size_t stream_index_, - const SortDescription & desc, - const String & sign_column_name_, - BlockPlainPtrs * output_blocks_) - : block(block_), stream_index(stream_index_), output_blocks(output_blocks_) - { - sort_columns.resize(desc.size()); - for (size_t i = 0; i < desc.size(); ++i) - { - size_t column_number = !desc[i].column_name.empty() - ? block.getPositionByName(desc[i].column_name) - : desc[i].column_number; - - sort_columns[i] = block.safeGetByPosition(column_number).column.get(); - } - - const IColumn * sign_icolumn = block.getByName(sign_column_name_).column.get(); - - sign_column = typeid_cast(sign_icolumn); - - if (!sign_column) - throw Exception("Sign column must have type Int8", ErrorCodes::BAD_TYPE_OF_FIELD); - - rows = sign_column->size(); - /// Filled entirely with zeros. Then `1` are set in the positions of the rows to be left. - filter.resize_fill(rows); - } - - Block block; - - /// Rows with the same key will be sorted in ascending order of stream_index. - size_t stream_index; - size_t rows; - - /// Which rows should be left. Filled when the threads merge. - IColumn::Filter filter; - - /// Point to `block`. - ColumnRawPtrs sort_columns; - const ColumnInt8 * sign_column; - - /// When it reaches zero, the block can be outputted in response. - int refcount = 0; - - /// Where to put the block when it is ready to be outputted in response. - BlockPlainPtrs * output_blocks; - }; - -private: - Block readImpl() override; - - /// When deleting the last block reference, adds a block to `output_blocks`. - using MergingBlockPtr = boost::intrusive_ptr; - - struct Cursor - { - MergingBlockPtr block; - size_t pos = 0; - - Cursor() {} - explicit Cursor(const MergingBlockPtr & block_, size_t pos_ = 0) : block(block_), pos(pos_) {} - - bool operator< (const Cursor & rhs) const - { - for (size_t i = 0; i < block->sort_columns.size(); ++i) - { - int res = block->sort_columns[i]->compareAt(pos, rhs.pos, *(rhs.block->sort_columns[i]), 1); - if (res > 0) - return true; - if (res < 0) - return false; - } - - return block->stream_index > rhs.block->stream_index; - } - - /// Not consistent with operator< : does not consider order. - bool equal(const Cursor & rhs) const - { - if (!block || !rhs.block) - return false; - - for (size_t i = 0; i < block->sort_columns.size(); ++i) - { - int res = block->sort_columns[i]->compareAt(pos, rhs.pos, *(rhs.block->sort_columns[i]), 1); - if (res != 0) - return false; - } - - return true; - } - - Int8 getSign() - { - return block->sign_column->getData()[pos]; - } - - /// Indicates that this row should be outputted in response. - void addToFilter() - { - block->filter[pos] = 1; - } - - bool isLast() - { - return pos + 1 == block->rows; - } - - void next() - { - ++pos; - } - }; - - using Queue = std::priority_queue; - - const SortDescription description; - String sign_column_name; - - Poco::Logger * log = &Poco::Logger::get("CollapsingFinalBlockInputStream"); - - bool first = true; - - BlockPlainPtrs output_blocks; - - Queue queue; - - Cursor previous; /// The current primary key. - Cursor last_positive; /// The last positive row for the current primary key. - - size_t count_positive = 0; /// The number of positive rows for the current primary key. - size_t count_negative = 0; /// The number of negative rows for the current primary key. - bool last_is_positive = false; /// true if the last row for the current primary key is positive. - - size_t count_incorrect_data = 0; /// To prevent too many error messages from writing to the log. - - /// Count the number of blocks fetched and outputted. - size_t blocks_fetched = 0; - size_t blocks_output = 0; - - void fetchNextBlock(size_t input_index); - void commitCurrent(); - - void reportBadCounts(); - void reportBadSign(Int8 sign); -}; - - -inline void intrusive_ptr_add_ref(CollapsingFinalBlockInputStream::MergingBlock * ptr) -{ - ++ptr->refcount; -} - -inline void intrusive_ptr_release(CollapsingFinalBlockInputStream::MergingBlock * ptr) -{ - if (0 == --ptr->refcount) - ptr->output_blocks->push_back(ptr); -} - -} diff --git a/src/DataStreams/ConcatBlockInputStream.h b/src/DataStreams/ConcatBlockInputStream.h deleted file mode 100644 index e2ab60a1509..00000000000 --- a/src/DataStreams/ConcatBlockInputStream.h +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -#include - - -namespace DB -{ - - -/** Combines several sources into one. - * Unlike UnionBlockInputStream, it does this sequentially. - * Blocks of different sources are not interleaved with each other. - */ -class ConcatBlockInputStream : public IBlockInputStream -{ -public: - ConcatBlockInputStream(BlockInputStreams inputs_) - { - children.insert(children.end(), inputs_.begin(), inputs_.end()); - current_stream = children.begin(); - } - - String getName() const override { return "Concat"; } - - Block getHeader() const override { return children.at(0)->getHeader(); } - - /// We call readSuffix prematurely by ourself. Suppress default behaviour. - void readSuffix() override {} - -protected: - Block readImpl() override - { - Block res; - - while (current_stream != children.end()) - { - res = (*current_stream)->read(); - - if (res) - break; - else - { - (*current_stream)->readSuffix(); - ++current_stream; - } - } - - return res; - } - -private: - BlockInputStreams::iterator current_stream; -}; - -} diff --git a/src/DataStreams/ConvertColumnLowCardinalityToFullBlockInputStream.h b/src/DataStreams/ConvertColumnLowCardinalityToFullBlockInputStream.h deleted file mode 100644 index 4e0eb22ff80..00000000000 --- a/src/DataStreams/ConvertColumnLowCardinalityToFullBlockInputStream.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -namespace DB -{ - - -/** Combines several sources into one. - * Unlike UnionBlockInputStream, it does this sequentially. - * Blocks of different sources are not interleaved with each other. - */ -class ConvertColumnLowCardinalityToFullBlockInputStream : public IBlockInputStream -{ -public: - explicit ConvertColumnLowCardinalityToFullBlockInputStream(const BlockInputStreamPtr & input) - { - children.push_back(input); - } - - String getName() const override { return "ConvertColumnLowCardinalityToFull"; } - - Block getHeader() const override { return convert(children.at(0)->getHeader()); } - -protected: - Block readImpl() override { return convert(children.back()->read()); } - -private: - Block convert(Block && block) const - { - for (auto & column : block) - { - if (auto * column_const = typeid_cast(column.column.get())) - column.column = column_const->removeLowCardinality(); - else - column.column = column.column->convertToFullColumnIfLowCardinality(); - - if (auto * low_cardinality_type = typeid_cast(column.type.get())) - column.type = low_cardinality_type->getDictionaryType(); - } - - return std::move(block); - } -}; - -} diff --git a/src/DataStreams/CubeBlockInputStream.cpp b/src/DataStreams/CubeBlockInputStream.cpp deleted file mode 100644 index 50a6c0a970b..00000000000 --- a/src/DataStreams/CubeBlockInputStream.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int TOO_MANY_COLUMNS; -} - -CubeBlockInputStream::CubeBlockInputStream( - const BlockInputStreamPtr & input_, const Aggregator::Params & params_) : aggregator(params_), - keys(params_.keys) -{ - if (keys.size() > 30) - throw Exception("Too many columns for cube", ErrorCodes::TOO_MANY_COLUMNS); - - children.push_back(input_); - Aggregator::CancellationHook hook = [this]() { return this->isCancelled(); }; - aggregator.setCancellationHook(hook); -} - - -Block CubeBlockInputStream::getHeader() const -{ - Block res = children.at(0)->getHeader(); - finalizeBlock(res); - return res; -} - - -Block CubeBlockInputStream::readImpl() -{ - /** After reading all blocks from input stream, - * we will calculate all subsets of columns on next iterations of readImpl - * by zeroing columns at positions, where bits are zero in current bitmask. - */ - - if (!is_data_read) - { - BlocksList source_blocks; - while (auto block = children[0]->read()) - source_blocks.push_back(block); - - if (source_blocks.empty()) - return {}; - - is_data_read = true; - mask = (1 << keys.size()) - 1; - - if (source_blocks.size() > 1) - source_block = aggregator.mergeBlocks(source_blocks, false); - else - source_block = std::move(source_blocks.front()); - - zero_block = source_block.cloneEmpty(); - for (auto key : keys) - { - auto & current = zero_block.getByPosition(key); - current.column = current.column->cloneResized(source_block.rows()); - } - - auto finalized = source_block; - finalizeBlock(finalized); - return finalized; - } - - if (!mask) - return {}; - - --mask; - auto cube_block = source_block; - - for (size_t i = 0; i < keys.size(); ++i) - { - if (!((mask >> i) & 1)) - { - size_t pos = keys.size() - i - 1; - auto & current = cube_block.getByPosition(keys[pos]); - current.column = zero_block.getByPosition(keys[pos]).column; - } - } - - BlocksList cube_blocks = { cube_block }; - Block finalized = aggregator.mergeBlocks(cube_blocks, true); - return finalized; -} -} diff --git a/src/DataStreams/CubeBlockInputStream.h b/src/DataStreams/CubeBlockInputStream.h deleted file mode 100644 index 7e62950e8ee..00000000000 --- a/src/DataStreams/CubeBlockInputStream.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include -#include - - -namespace DB -{ - -class ExpressionActions; - - -/** Takes blocks after grouping, with non-finalized aggregate functions. - * Calculates all subsets of columns and aggreagetes over them. - */ -class CubeBlockInputStream : public IBlockInputStream -{ -private: - using ExpressionActionsPtr = std::shared_ptr; - using AggregateColumns = std::vector; -public: - CubeBlockInputStream( - const BlockInputStreamPtr & input_, const Aggregator::Params & params_); - - String getName() const override { return "Cube"; } - - Block getHeader() const override; - -protected: - Block readImpl() override; - -private: - Aggregator aggregator; - ColumnNumbers keys; - UInt32 mask = 0; - Block source_block; - Block zero_block; - bool is_data_read = false; -}; - -} diff --git a/src/DataStreams/DistinctBlockInputStream.cpp b/src/DataStreams/DistinctBlockInputStream.cpp deleted file mode 100644 index b1f9756d55e..00000000000 --- a/src/DataStreams/DistinctBlockInputStream.cpp +++ /dev/null @@ -1,122 +0,0 @@ -#include - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int SET_SIZE_LIMIT_EXCEEDED; -} - -DistinctBlockInputStream::DistinctBlockInputStream(const BlockInputStreamPtr & input, const SizeLimits & set_size_limits_, UInt64 limit_hint_, const Names & columns_) - : columns_names(columns_) - , limit_hint(limit_hint_) - , set_size_limits(set_size_limits_) -{ - children.push_back(input); -} - -Block DistinctBlockInputStream::readImpl() -{ - /// Execute until end of stream or until - /// a block with some new records will be gotten. - while (true) - { - if (no_more_rows) - return Block(); - - /// Stop reading if we already reach the limit. - if (limit_hint && data.getTotalRowCount() >= limit_hint) - return Block(); - - Block block = children[0]->read(); - if (!block) - return Block(); - - const ColumnRawPtrs column_ptrs(getKeyColumns(block)); - if (column_ptrs.empty()) - { - /// Only constants. We need to return single row. - no_more_rows = true; - for (auto & elem : block) - elem.column = elem.column->cut(0, 1); - return block; - } - - if (data.empty()) - data.init(SetVariants::chooseMethod(column_ptrs, key_sizes)); - - const size_t old_set_size = data.getTotalRowCount(); - const size_t rows = block.rows(); - IColumn::Filter filter(rows); - - switch (data.type) - { - case SetVariants::Type::EMPTY: - break; - #define M(NAME) \ - case SetVariants::Type::NAME: \ - buildFilter(*data.NAME, column_ptrs, filter, rows, data); \ - break; - APPLY_FOR_SET_VARIANTS(M) - #undef M - } - - /// Just go to the next block if there isn't any new record in the current one. - if (data.getTotalRowCount() == old_set_size) - continue; - - if (!set_size_limits.check(data.getTotalRowCount(), data.getTotalByteCount(), "DISTINCT", ErrorCodes::SET_SIZE_LIMIT_EXCEEDED)) - return {}; - - for (auto & elem : block) - elem.column = elem.column->filter(filter, -1); - - return block; - } -} - - -template -void DistinctBlockInputStream::buildFilter( - Method & method, - const ColumnRawPtrs & columns, - IColumn::Filter & filter, - size_t rows, - SetVariants & variants) const -{ - typename Method::State state(columns, key_sizes, nullptr); - - for (size_t i = 0; i < rows; ++i) - { - auto emplace_result = state.emplaceKey(method.data, i, variants.string_pool); - - /// Emit the record if there is no such key in the current set yet. - /// Skip it otherwise. - filter[i] = emplace_result.isInserted(); - } -} - - -ColumnRawPtrs DistinctBlockInputStream::getKeyColumns(const Block & block) const -{ - size_t columns = columns_names.empty() ? block.columns() : columns_names.size(); - - ColumnRawPtrs column_ptrs; - column_ptrs.reserve(columns); - - for (size_t i = 0; i < columns; ++i) - { - const auto & column = columns_names.empty() - ? block.safeGetByPosition(i).column - : block.getByName(columns_names[i]).column; - - /// Ignore all constant columns. - if (!isColumnConst(*column)) - column_ptrs.emplace_back(column.get()); - } - - return column_ptrs; -} - -} diff --git a/src/DataStreams/DistinctBlockInputStream.h b/src/DataStreams/DistinctBlockInputStream.h deleted file mode 100644 index 4df0bf46070..00000000000 --- a/src/DataStreams/DistinctBlockInputStream.h +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include -#include - -namespace DB -{ - -/** This class is intended for implementation of SELECT DISTINCT clause and - * leaves only unique rows in the stream. - * - * To optimize the SELECT DISTINCT ... LIMIT clause we can - * set limit_hint to non zero value. So we stop emitting new rows after - * count of already emitted rows will reach the limit_hint. - */ -class DistinctBlockInputStream : public IBlockInputStream -{ -public: - /// Empty columns_ means all collumns. - DistinctBlockInputStream(const BlockInputStreamPtr & input, const SizeLimits & set_size_limits_, UInt64 limit_hint_, const Names & columns_); - - String getName() const override { return "Distinct"; } - - Block getHeader() const override { return children.at(0)->getHeader(); } - -protected: - Block readImpl() override; - -private: - ColumnRawPtrs getKeyColumns(const Block & block) const; - - template - void buildFilter( - Method & method, - const ColumnRawPtrs & key_columns, - IColumn::Filter & filter, - size_t rows, - SetVariants & variants) const; - - - Names columns_names; - SetVariants data; - Sizes key_sizes; - UInt64 limit_hint; - - bool no_more_rows = false; - - /// Restrictions on the maximum size of the output data. - SizeLimits set_size_limits; -}; - -} diff --git a/src/DataStreams/DistinctSortedBlockInputStream.h b/src/DataStreams/DistinctSortedBlockInputStream.h index e3458967f72..1fb8c011f6e 100644 --- a/src/DataStreams/DistinctSortedBlockInputStream.h +++ b/src/DataStreams/DistinctSortedBlockInputStream.h @@ -21,7 +21,7 @@ namespace DB class DistinctSortedBlockInputStream : public IBlockInputStream { public: - /// Empty columns_ means all collumns. + /// Empty columns_ means all columns. DistinctSortedBlockInputStream(const BlockInputStreamPtr & input, const SizeLimits & set_size_limits_, UInt64 limit_hint_, const Names & columns); String getName() const override { return "DistinctSorted"; } diff --git a/src/DataStreams/FillingBlockInputStream.cpp b/src/DataStreams/FillingBlockInputStream.cpp deleted file mode 100644 index ec026d56ad0..00000000000 --- a/src/DataStreams/FillingBlockInputStream.cpp +++ /dev/null @@ -1,186 +0,0 @@ -#include -#include -#include - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int INVALID_WITH_FILL_EXPRESSION; -} - -FillingBlockInputStream::FillingBlockInputStream( - const BlockInputStreamPtr & input, const SortDescription & sort_description_) - : sort_description(sort_description_), filling_row(sort_description_), next_row(sort_description_) -{ - children.push_back(input); - header = children.at(0)->getHeader(); - - std::vector is_fill_column(header.columns()); - for (const auto & elem : sort_description) - is_fill_column[header.getPositionByName(elem.column_name)] = true; - - auto try_convert_fields = [](FillColumnDescription & descr, const DataTypePtr & type) - { - auto max_type = Field::Types::Null; - WhichDataType which(type); - DataTypePtr to_type; - if (isInteger(type) || which.isDateOrDateTime()) - { - max_type = Field::Types::Int64; - to_type = std::make_shared(); - } - else if (which.isFloat()) - { - max_type = Field::Types::Float64; - to_type = std::make_shared(); - } - - if (descr.fill_from.getType() > max_type || descr.fill_to.getType() > max_type - || descr.fill_step.getType() > max_type) - return false; - descr.fill_from = convertFieldToType(descr.fill_from, *to_type); - descr.fill_to = convertFieldToType(descr.fill_to, *to_type); - descr.fill_step = convertFieldToType(descr.fill_step, *to_type); - - return true; - }; - - for (size_t i = 0; i < header.columns(); ++i) - { - if (is_fill_column[i]) - { - size_t pos = fill_column_positions.size(); - auto & descr = filling_row.getFillDescription(pos); - auto type = header.getByPosition(i).type; - if (!try_convert_fields(descr, type)) - throw Exception("Incompatible types of WITH FILL expression values with column type " - + type->getName(), ErrorCodes::INVALID_WITH_FILL_EXPRESSION); - - if (type->isValueRepresentedByUnsignedInteger() && - ((!descr.fill_from.isNull() && less(descr.fill_from, Field{0}, 1)) || - (!descr.fill_to.isNull() && less(descr.fill_to, Field{0}, 1)))) - { - throw Exception("WITH FILL bound values cannot be negative for unsigned type " - + type->getName(), ErrorCodes::INVALID_WITH_FILL_EXPRESSION); - } - - fill_column_positions.push_back(i); - } - else - other_column_positions.push_back(i); - } -} - - -Block FillingBlockInputStream::readImpl() -{ - Columns old_fill_columns; - Columns old_other_columns; - MutableColumns res_fill_columns; - MutableColumns res_other_columns; - - auto init_columns_by_positions = [](const Block & block, Columns & columns, - MutableColumns & mutable_columns, const Positions & positions) - { - for (size_t pos : positions) - { - auto column = block.getByPosition(pos).column; - columns.push_back(column); - mutable_columns.push_back(column->cloneEmpty()->assumeMutable()); - } - }; - - auto block = children.back()->read(); - if (!block) - { - init_columns_by_positions(header, old_fill_columns, res_fill_columns, fill_column_positions); - init_columns_by_positions(header, old_other_columns, res_other_columns, other_column_positions); - - bool should_insert_first = next_row < filling_row; - - bool generated = false; - for (size_t i = 0; i < filling_row.size(); ++i) - next_row[i] = filling_row.getFillDescription(i).fill_to; - - if (should_insert_first && filling_row < next_row) - insertFromFillingRow(res_fill_columns, res_other_columns, filling_row); - - while (filling_row.next(next_row)) - { - generated = true; - insertFromFillingRow(res_fill_columns, res_other_columns, filling_row); - } - - if (generated) - return createResultBlock(res_fill_columns, res_other_columns); - - return block; - } - - size_t rows = block.rows(); - init_columns_by_positions(block, old_fill_columns, res_fill_columns, fill_column_positions); - init_columns_by_positions(block, old_other_columns, res_other_columns, other_column_positions); - - if (first) - { - for (size_t i = 0; i < filling_row.size(); ++i) - { - auto current_value = (*old_fill_columns[i])[0]; - const auto & fill_from = filling_row.getFillDescription(i).fill_from; - if (!fill_from.isNull() && !equals(current_value, fill_from)) - { - filling_row.initFromDefaults(i); - if (less(fill_from, current_value, filling_row.getDirection(i))) - insertFromFillingRow(res_fill_columns, res_other_columns, filling_row); - break; - } - filling_row[i] = current_value; - } - first = false; - } - - for (size_t row_ind = 0; row_ind < rows; ++row_ind) - { - bool should_insert_first = next_row < filling_row; - - for (size_t i = 0; i < filling_row.size(); ++i) - { - auto current_value = (*old_fill_columns[i])[row_ind]; - const auto & fill_to = filling_row.getFillDescription(i).fill_to; - - if (fill_to.isNull() || less(current_value, fill_to, filling_row.getDirection(i))) - next_row[i] = current_value; - else - next_row[i] = fill_to; - } - - /// A case, when at previous step row was initialized from defaults 'fill_from' values - /// and probably we need to insert it to block. - if (should_insert_first && filling_row < next_row) - insertFromFillingRow(res_fill_columns, res_other_columns, filling_row); - - /// Insert generated filling row to block, while it is less than current row in block. - while (filling_row.next(next_row)) - insertFromFillingRow(res_fill_columns, res_other_columns, filling_row); - - copyRowFromColumns(res_fill_columns, old_fill_columns, row_ind); - copyRowFromColumns(res_other_columns, old_other_columns, row_ind); - } - - return createResultBlock(res_fill_columns, res_other_columns); -} - -Block FillingBlockInputStream::createResultBlock(MutableColumns & fill_columns, MutableColumns & other_columns) const -{ - MutableColumns result_columns(header.columns()); - for (size_t i = 0; i < fill_columns.size(); ++i) - result_columns[fill_column_positions[i]] = std::move(fill_columns[i]); - for (size_t i = 0; i < other_columns.size(); ++i) - result_columns[other_column_positions[i]] = std::move(other_columns[i]); - - return header.cloneWithColumns(std::move(result_columns)); -} - -} diff --git a/src/DataStreams/FillingBlockInputStream.h b/src/DataStreams/FillingBlockInputStream.h deleted file mode 100644 index b84a1d5485f..00000000000 --- a/src/DataStreams/FillingBlockInputStream.h +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -#include -#include - -namespace DB -{ - -/** Implements modifier WITH FILL of ORDER BY clause. - * It fills gaps in data stream by rows with missing values in columns with set WITH FILL and deafults in other columns. - * Optionally FROM, TO and STEP values can be specified. - */ -class FillingBlockInputStream : public IBlockInputStream -{ -public: - FillingBlockInputStream(const BlockInputStreamPtr & input, const SortDescription & sort_description_); - - String getName() const override { return "Filling"; } - - Block getHeader() const override { return header; } - -protected: - Block readImpl() override; - -private: - Block createResultBlock(MutableColumns & fill_columns, MutableColumns & other_columns) const; - - const SortDescription sort_description; /// Contains only rows with WITH FILL. - FillingRow filling_row; /// Current row, which is used to fill gaps. - FillingRow next_row; /// Row to which we need to generate filling rows. - Block header; - - using Positions = std::vector; - Positions fill_column_positions; - Positions other_column_positions; - bool first = true; -}; - -} diff --git a/src/DataStreams/FilterColumnsBlockInputStream.cpp b/src/DataStreams/FilterColumnsBlockInputStream.cpp deleted file mode 100644 index fa7bb916fe7..00000000000 --- a/src/DataStreams/FilterColumnsBlockInputStream.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include - -namespace DB -{ - -Block FilterColumnsBlockInputStream::getHeader() const -{ - Block block = children.back()->getHeader(); - Block filtered; - - for (const auto & it : columns_to_save) - if (throw_if_column_not_found || block.has(it)) - filtered.insert(std::move(block.getByName(it))); - - return filtered; -} - -Block FilterColumnsBlockInputStream::readImpl() -{ - Block block = children.back()->read(); - - if (!block) - return block; - - Block filtered; - - for (const auto & it : columns_to_save) - if (throw_if_column_not_found || block.has(it)) - filtered.insert(std::move(block.getByName(it))); - - return filtered; -} - -} diff --git a/src/DataStreams/FilterColumnsBlockInputStream.h b/src/DataStreams/FilterColumnsBlockInputStream.h deleted file mode 100644 index 4416287195d..00000000000 --- a/src/DataStreams/FilterColumnsBlockInputStream.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include -#include - -namespace DB -{ - -/// Removes columns other than columns_to_save_ from block, -/// and reorders columns as in columns_to_save_. -/// Functionality is similar to ExpressionBlockInputStream with ExpressionActions containing PROJECT action. -class FilterColumnsBlockInputStream : public IBlockInputStream -{ -public: - FilterColumnsBlockInputStream( - const BlockInputStreamPtr & input, const Names & columns_to_save_, bool throw_if_column_not_found_) - : columns_to_save(columns_to_save_), throw_if_column_not_found(throw_if_column_not_found_) - { - children.push_back(input); - } - - String getName() const override - { - return "FilterColumns"; - } - - Block getHeader() const override; - -protected: - Block readImpl() override; - -private: - Names columns_to_save; - bool throw_if_column_not_found; -}; - -} diff --git a/src/DataStreams/FinishSortingBlockInputStream.cpp b/src/DataStreams/FinishSortingBlockInputStream.cpp deleted file mode 100644 index 2288e676abe..00000000000 --- a/src/DataStreams/FinishSortingBlockInputStream.cpp +++ /dev/null @@ -1,164 +0,0 @@ -#include -#include -#include - - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int LOGICAL_ERROR; -} - -static bool isPrefix(const SortDescription & pref_descr, const SortDescription & descr) -{ - if (pref_descr.size() > descr.size()) - return false; - - for (size_t i = 0; i < pref_descr.size(); ++i) - if (pref_descr[i] != descr[i]) - return false; - - return true; -} - -FinishSortingBlockInputStream::FinishSortingBlockInputStream( - const BlockInputStreamPtr & input, const SortDescription & description_sorted_, - const SortDescription & description_to_sort_, - size_t max_merged_block_size_, UInt64 limit_) - : description_sorted(description_sorted_), description_to_sort(description_to_sort_), - max_merged_block_size(max_merged_block_size_), limit(limit_) -{ - if (!isPrefix(description_sorted, description_to_sort)) - throw Exception("Can`t finish sorting. SortDescription of already sorted stream is not prefix of " - "SortDescription needed to sort", ErrorCodes::LOGICAL_ERROR); - - children.push_back(input); - header = children.at(0)->getHeader(); - removeConstantsFromSortDescription(header, description_to_sort); -} - - -struct Less -{ - const ColumnsWithSortDescriptions & left_columns; - const ColumnsWithSortDescriptions & right_columns; - - Less(const ColumnsWithSortDescriptions & left_columns_, const ColumnsWithSortDescriptions & right_columns_) : - left_columns(left_columns_), right_columns(right_columns_) {} - - bool operator() (size_t a, size_t b) const - { - for (auto it = left_columns.begin(), jt = right_columns.begin(); it != left_columns.end(); ++it, ++jt) - { - int res = it->description.direction * it->column->compareAt(a, b, *jt->column, it->description.nulls_direction); - if (res < 0) - return true; - else if (res > 0) - return false; - } - return false; - } -}; - - -Block FinishSortingBlockInputStream::readImpl() -{ - if (limit && total_rows_processed >= limit) - return {}; - - Block res; - if (impl) - res = impl->read(); - - /// If res block is empty, we have finished sorting previous chunk of blocks. - if (!res) - { - if (end_of_stream) - return {}; - - blocks.clear(); - if (tail_block) - blocks.push_back(std::move(tail_block)); - - while (true) - { - Block block = children.back()->read(); - - /// End of input stream, but we can`t return immediately, we need to merge already read blocks. - /// Check it later, when get end of stream from impl. - if (!block) - { - end_of_stream = true; - break; - } - - // If there were only const columns in sort description, then there is no need to sort. - // Return the blocks as is. - if (description_to_sort.empty()) - return block; - - if (block.rows() == 0) - continue; - - - removeConstantsFromBlock(block); - - /// Find the position of last already read key in current block. - if (!blocks.empty()) - { - const Block & last_block = blocks.back(); - auto last_columns = getColumnsWithSortDescription(last_block, description_sorted); - auto current_columns = getColumnsWithSortDescription(block, description_sorted); - - Less less(last_columns, current_columns); - - size_t size = block.rows(); - IColumn::Permutation perm(size); - for (size_t i = 0; i < size; ++i) - perm[i] = i; - - auto * it = std::upper_bound(perm.begin(), perm.end(), last_block.rows() - 1, less); - - /// We need to save tail of block, because next block may starts with the same key as in tail - /// and we should sort these rows in one chunk. - if (it != perm.end()) - { - size_t tail_pos = it - perm.begin(); - Block head_block = block.cloneEmpty(); - tail_block = block.cloneEmpty(); - - for (size_t i = 0; i < block.columns(); ++i) - { - head_block.getByPosition(i).column = block.getByPosition(i).column->cut(0, tail_pos); - tail_block.getByPosition(i).column = block.getByPosition(i).column->cut(tail_pos, block.rows() - tail_pos); - } - - if (head_block.rows()) - blocks.push_back(head_block); - - break; - } - } - - /// If we reach here, that means that current block is first in chunk - /// or it all consists of rows with the same key as tail of a previous block. - blocks.push_back(block); - } - - if (!blocks.empty()) - { - impl = std::make_unique(blocks, description_to_sort, max_merged_block_size, limit); - res = impl->read(); - } - } - - if (res) - enrichBlockWithConstants(res, header); - - total_rows_processed += res.rows(); - - return res; -} -} diff --git a/src/DataStreams/FinishSortingBlockInputStream.h b/src/DataStreams/FinishSortingBlockInputStream.h deleted file mode 100644 index c73dd2d4b85..00000000000 --- a/src/DataStreams/FinishSortingBlockInputStream.h +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include -#include -#include - - -namespace DB -{ - -/** Takes stream already sorted by `x` and finishes sorting it by (`x`, `y`). - * During sorting only blocks with rows that equal by `x` saved in RAM. - * */ -class FinishSortingBlockInputStream : public IBlockInputStream -{ -public: - /// limit - if not 0, allowed to return just first 'limit' rows in sorted order. - FinishSortingBlockInputStream(const BlockInputStreamPtr & input, const SortDescription & description_sorted_, - const SortDescription & description_to_sort_, - size_t max_merged_block_size_, UInt64 limit_); - - String getName() const override { return "FinishSorting"; } - - bool isSortedOutput() const override { return true; } - const SortDescription & getSortDescription() const override { return description_to_sort; } - - Block getHeader() const override { return header; } - -protected: - Block readImpl() override; - -private: - SortDescription description_sorted; - SortDescription description_to_sort; - size_t max_merged_block_size; - UInt64 limit; - - Block tail_block; - Blocks blocks; - - std::unique_ptr impl; - - /// Before operation, will remove constant columns from blocks. And after, place constant columns back. - /// to avoid excessive virtual function calls - /// Save original block structure here. - Block header; - - bool end_of_stream = false; - size_t total_rows_processed = 0; -}; -} diff --git a/src/DataStreams/LimitByBlockInputStream.cpp b/src/DataStreams/LimitByBlockInputStream.cpp deleted file mode 100644 index 74973dfd618..00000000000 --- a/src/DataStreams/LimitByBlockInputStream.cpp +++ /dev/null @@ -1,82 +0,0 @@ -#include -#include -#include - - -namespace DB -{ - -LimitByBlockInputStream::LimitByBlockInputStream(const BlockInputStreamPtr & input, - size_t group_length_, size_t group_offset_, const Names & columns) - : columns_names(columns) - , group_length(group_length_) - , group_offset(group_offset_) -{ - children.push_back(input); -} - -Block LimitByBlockInputStream::readImpl() -{ - /// Execute until end of stream or until - /// a block with some new records will be gotten. - while (true) - { - Block block = children[0]->read(); - if (!block) - return Block(); - - const ColumnRawPtrs column_ptrs(getKeyColumns(block)); - const size_t rows = block.rows(); - IColumn::Filter filter(rows); - size_t inserted_count = 0; - - for (size_t i = 0; i < rows; ++i) - { - UInt128 key; - SipHash hash; - - for (const auto & column : column_ptrs) - column->updateHashWithValue(i, hash); - - hash.get128(key.low, key.high); - - auto count = keys_counts[key]++; - if (count >= group_offset && count < group_length + group_offset) - { - inserted_count++; - filter[i] = 1; - } - else - filter[i] = 0; - } - - /// Just go to the next block if there isn't any new records in the current one. - if (!inserted_count) - continue; - - size_t all_columns = block.columns(); - for (size_t i = 0; i < all_columns; ++i) - block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->filter(filter, inserted_count); - - return block; - } -} - -ColumnRawPtrs LimitByBlockInputStream::getKeyColumns(Block & block) const -{ - ColumnRawPtrs column_ptrs; - column_ptrs.reserve(columns_names.size()); - - for (const auto & name : columns_names) - { - auto & column = block.getByName(name).column; - - /// Ignore all constant columns. - if (!isColumnConst(*column)) - column_ptrs.emplace_back(column.get()); - } - - return column_ptrs; -} - -} diff --git a/src/DataStreams/LimitByBlockInputStream.h b/src/DataStreams/LimitByBlockInputStream.h deleted file mode 100644 index 8fca64be606..00000000000 --- a/src/DataStreams/LimitByBlockInputStream.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include - -#include -#include - - -namespace DB -{ - -/** Implements LIMIT BY clause witch can be used to obtain a "top N by subgroup". - * - * For example, if you have table T like this (Num: 1 1 3 3 3 4 4 5 7 7 7 7), - * the query SELECT Num FROM T LIMIT 2 BY Num - * will give you the following result: (Num: 1 1 3 3 4 4 5 7 7). - */ -class LimitByBlockInputStream : public IBlockInputStream -{ -public: - LimitByBlockInputStream(const BlockInputStreamPtr & input, size_t group_length_, size_t group_offset_, const Names & columns); - - String getName() const override { return "LimitBy"; } - - Block getHeader() const override { return children.at(0)->getHeader(); } - -protected: - Block readImpl() override; - -private: - ColumnRawPtrs getKeyColumns(Block & block) const; - -private: - using MapHashed = HashMap; - - const Names columns_names; - const size_t group_length; - const size_t group_offset; - MapHashed keys_counts; -}; - -} diff --git a/src/DataStreams/MergeSortingBlockInputStream.cpp b/src/DataStreams/MergeSortingBlockInputStream.cpp deleted file mode 100644 index 5e1fbe599b7..00000000000 --- a/src/DataStreams/MergeSortingBlockInputStream.cpp +++ /dev/null @@ -1,277 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace ProfileEvents -{ - extern const Event ExternalSortWritePart; - extern const Event ExternalSortMerge; -} - -namespace DB -{ -namespace ErrorCodes -{ - extern const int NOT_ENOUGH_SPACE; -} - -MergeSortingBlockInputStream::MergeSortingBlockInputStream( - const BlockInputStreamPtr & input, SortDescription & description_, - size_t max_merged_block_size_, UInt64 limit_, size_t max_bytes_before_remerge_, - size_t max_bytes_before_external_sort_, VolumePtr tmp_volume_, const String & codec_, size_t min_free_disk_space_) - : description(description_), max_merged_block_size(max_merged_block_size_), limit(limit_), - max_bytes_before_remerge(max_bytes_before_remerge_), - max_bytes_before_external_sort(max_bytes_before_external_sort_), tmp_volume(tmp_volume_), - codec(codec_), - min_free_disk_space(min_free_disk_space_) -{ - children.push_back(input); - header = children.at(0)->getHeader(); - header_without_constants = header; - removeConstantsFromBlock(header_without_constants); - removeConstantsFromSortDescription(header, description); -} - - -Block MergeSortingBlockInputStream::readImpl() -{ - /** Algorithm: - * - read to memory blocks from source stream; - * - if too many of them and if external sorting is enabled, - * - merge all blocks to sorted stream and write it to temporary file; - * - at the end, merge all sorted streams from temporary files and also from rest of blocks in memory. - */ - - /// If has not read source blocks. - if (!impl) - { - while (Block block = children.back()->read()) - { - /// If there were only const columns in sort description, then there is no need to sort. - /// Return the blocks as is. - if (description.empty()) - return block; - - removeConstantsFromBlock(block); - - blocks.push_back(block); - sum_rows_in_blocks += block.rows(); - sum_bytes_in_blocks += block.allocatedBytes(); - - /** If significant amount of data was accumulated, perform preliminary merging step. - */ - if (blocks.size() > 1 - && limit - && limit * 2 < sum_rows_in_blocks /// 2 is just a guess. - && remerge_is_useful - && max_bytes_before_remerge - && sum_bytes_in_blocks > max_bytes_before_remerge) - { - remerge(); - } - - /** If too many of them and if external sorting is enabled, - * will merge blocks that we have in memory at this moment and write merged stream to temporary (compressed) file. - * NOTE. It's possible to check free space in filesystem. - */ - if (max_bytes_before_external_sort && sum_bytes_in_blocks > max_bytes_before_external_sort) - { - size_t size = sum_bytes_in_blocks + min_free_disk_space; - auto reservation = tmp_volume->reserve(size); - if (!reservation) - throw Exception("Not enough space for external sort in temporary storage", ErrorCodes::NOT_ENOUGH_SPACE); - - const std::string tmp_path(reservation->getDisk()->getPath()); - temporary_files.emplace_back(createTemporaryFile(tmp_path)); - - const std::string & path = temporary_files.back()->path(); - MergeSortingBlocksBlockInputStream block_in(blocks, description, max_merged_block_size, limit); - - LOG_INFO(log, "Sorting and writing part of data into temporary file {}", path); - ProfileEvents::increment(ProfileEvents::ExternalSortWritePart); - TemporaryFileStream::write(path, header_without_constants, block_in, &is_cancelled, codec); /// NOTE. Possibly limit disk usage. - LOG_INFO(log, "Done writing part of data into temporary file {}", path); - - blocks.clear(); - sum_bytes_in_blocks = 0; - sum_rows_in_blocks = 0; - } - } - - if ((blocks.empty() && temporary_files.empty()) || isCancelledOrThrowIfKilled()) - return Block(); - - if (temporary_files.empty()) - { - impl = std::make_unique(blocks, description, max_merged_block_size, limit); - } - else - { - /// If there was temporary files. - ProfileEvents::increment(ProfileEvents::ExternalSortMerge); - - LOG_INFO(log, "There are {} temporary sorted parts to merge.", temporary_files.size()); - - /// Create sorted streams to merge. - for (const auto & file : temporary_files) - { - temporary_inputs.emplace_back(std::make_unique(file->path(), header_without_constants)); - inputs_to_merge.emplace_back(temporary_inputs.back()->block_in); - } - - /// Rest of blocks in memory. - if (!blocks.empty()) - inputs_to_merge.emplace_back(std::make_shared(blocks, description, max_merged_block_size, limit)); - - /// Will merge that sorted streams. - impl = std::make_unique(inputs_to_merge, description, max_merged_block_size, limit); - } - } - - Block res = impl->read(); - if (res) - enrichBlockWithConstants(res, header); - return res; -} - - -MergeSortingBlocksBlockInputStream::MergeSortingBlocksBlockInputStream( - Blocks & blocks_, const SortDescription & description_, size_t max_merged_block_size_, UInt64 limit_) - : blocks(blocks_), header(blocks.at(0).cloneEmpty()), description(description_), max_merged_block_size(max_merged_block_size_), limit(limit_) -{ - Blocks nonempty_blocks; - for (const auto & block : blocks) - { - if (block.rows() == 0) - continue; - - nonempty_blocks.push_back(block); - cursors.emplace_back(block, description); - has_collation |= cursors.back().has_collation; - } - - blocks.swap(nonempty_blocks); - - if (has_collation) - queue_with_collation = SortingHeap(cursors); - else if (description.size() > 1) - queue_without_collation = SortingHeap(cursors); - else - queue_simple = SortingHeap(cursors); -} - - -Block MergeSortingBlocksBlockInputStream::readImpl() -{ - if (blocks.empty()) - return Block(); - - if (blocks.size() == 1) - { - Block res = blocks[0]; - blocks.clear(); - return res; - } - - if (has_collation) - return mergeImpl(queue_with_collation); - else if (description.size() > 1) - return mergeImpl(queue_without_collation); - else - return mergeImpl(queue_simple); -} - - -template -Block MergeSortingBlocksBlockInputStream::mergeImpl(TSortingHeap & queue) -{ - size_t num_columns = header.columns(); - MutableColumns merged_columns = header.cloneEmptyColumns(); - - /// Reserve - if (queue.isValid() && !blocks.empty()) - { - /// The expected size of output block is the same as input block - size_t size_to_reserve = blocks[0].rows(); - for (auto & column : merged_columns) - column->reserve(size_to_reserve); - } - - /// TODO: Optimization when a single block left. - - /// Take rows from queue in right order and push to 'merged'. - size_t merged_rows = 0; - while (queue.isValid()) - { - auto current = queue.current(); - - /// Append a row from queue. - for (size_t i = 0; i < num_columns; ++i) - merged_columns[i]->insertFrom(*current->all_columns[i], current->pos); - - ++total_merged_rows; - ++merged_rows; - - /// We don't need more rows because of limit has reached. - if (limit && total_merged_rows == limit) - { - blocks.clear(); - break; - } - - queue.next(); - - /// It's enough for current output block but we will continue. - if (merged_rows == max_merged_block_size) - break; - } - - if (!queue.isValid()) - blocks.clear(); - - if (merged_rows == 0) - return {}; - - return header.cloneWithColumns(std::move(merged_columns)); -} - - -void MergeSortingBlockInputStream::remerge() -{ - LOG_DEBUG(log, "Re-merging intermediate ORDER BY data ({} blocks with {} rows) to save memory consumption", blocks.size(), sum_rows_in_blocks); - - /// NOTE Maybe concat all blocks and partial sort will be faster than merge? - MergeSortingBlocksBlockInputStream merger(blocks, description, max_merged_block_size, limit); - - Blocks new_blocks; - size_t new_sum_rows_in_blocks = 0; - size_t new_sum_bytes_in_blocks = 0; - - merger.readPrefix(); - while (Block block = merger.read()) - { - new_sum_rows_in_blocks += block.rows(); - new_sum_bytes_in_blocks += block.allocatedBytes(); - new_blocks.emplace_back(std::move(block)); - } - merger.readSuffix(); - - LOG_DEBUG(log, "Memory usage is lowered from {} to {}", ReadableSize(sum_bytes_in_blocks), ReadableSize(new_sum_bytes_in_blocks)); - - /// If the memory consumption was not lowered enough - we will not perform remerge anymore. 2 is a guess. - if (new_sum_bytes_in_blocks * 2 > sum_bytes_in_blocks) - remerge_is_useful = false; - - blocks = std::move(new_blocks); - sum_rows_in_blocks = new_sum_rows_in_blocks; - sum_bytes_in_blocks = new_sum_bytes_in_blocks; -} -} diff --git a/src/DataStreams/MergeSortingBlockInputStream.h b/src/DataStreams/MergeSortingBlockInputStream.h deleted file mode 100644 index c21c548ff24..00000000000 --- a/src/DataStreams/MergeSortingBlockInputStream.h +++ /dev/null @@ -1,131 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#include -#include - -#include -#include - - -namespace DB -{ - -struct TemporaryFileStream; - -class IVolume; -using VolumePtr = std::shared_ptr; - -namespace ErrorCodes -{ -} -/** Merges stream of sorted each-separately blocks to sorted as-a-whole stream of blocks. - * If data to sort is too much, could use external sorting, with temporary files. - */ - -/** Part of implementation. Merging array of ready (already read from somewhere) blocks. - * Returns result of merge as stream of blocks, not more than 'max_merged_block_size' rows in each. - */ -class MergeSortingBlocksBlockInputStream : public IBlockInputStream -{ -public: - /// limit - if not 0, allowed to return just first 'limit' rows in sorted order. - MergeSortingBlocksBlockInputStream(Blocks & blocks_, const SortDescription & description_, - size_t max_merged_block_size_, UInt64 limit_ = 0); - - String getName() const override { return "MergeSortingBlocks"; } - - bool isSortedOutput() const override { return true; } - const SortDescription & getSortDescription() const override { return description; } - - Block getHeader() const override { return header; } - -protected: - Block readImpl() override; - -private: - Blocks & blocks; - Block header; - SortDescription description; - size_t max_merged_block_size; - UInt64 limit; - size_t total_merged_rows = 0; - - SortCursorImpls cursors; - - bool has_collation = false; - - SortingHeap queue_without_collation; - SortingHeap queue_simple; - SortingHeap queue_with_collation; - - /** Two different cursors are supported - with and without Collation. - * Templates are used (instead of virtual functions in SortCursor) for zero-overhead. - */ - template - Block mergeImpl(TSortingHeap & queue); -}; - - -class MergeSortingBlockInputStream : public IBlockInputStream -{ -public: - /// limit - if not 0, allowed to return just first 'limit' rows in sorted order. - MergeSortingBlockInputStream(const BlockInputStreamPtr & input, SortDescription & description_, - size_t max_merged_block_size_, UInt64 limit_, - size_t max_bytes_before_remerge_, - size_t max_bytes_before_external_sort_, VolumePtr tmp_volume_, - const String & codec_, - size_t min_free_disk_space_); - - String getName() const override { return "MergeSorting"; } - - bool isSortedOutput() const override { return true; } - const SortDescription & getSortDescription() const override { return description; } - - Block getHeader() const override { return header; } - -protected: - Block readImpl() override; - -private: - SortDescription description; - size_t max_merged_block_size; - UInt64 limit; - - size_t max_bytes_before_remerge; - size_t max_bytes_before_external_sort; - VolumePtr tmp_volume; - String codec; - size_t min_free_disk_space; - - Poco::Logger * log = &Poco::Logger::get("MergeSortingBlockInputStream"); - - Blocks blocks; - size_t sum_rows_in_blocks = 0; - size_t sum_bytes_in_blocks = 0; - std::unique_ptr impl; - - /// Before operation, will remove constant columns from blocks. And after, place constant columns back. - /// (to avoid excessive virtual function calls and because constants cannot be serialized in Native format for temporary files) - /// Save original block structure here. - Block header; - Block header_without_constants; - - /// Everything below is for external sorting. - std::vector> temporary_files; - std::vector> temporary_inputs; - - BlockInputStreams inputs_to_merge; - - /// Merge all accumulated blocks to keep no more than limit rows. - void remerge(); - /// If remerge doesn't save memory at least several times, mark it as useless and don't do it anymore. - bool remerge_is_useful = true; -}; -} diff --git a/src/DataStreams/MergingAggregatedBlockInputStream.cpp b/src/DataStreams/MergingAggregatedBlockInputStream.cpp deleted file mode 100644 index 6e0b5986e1b..00000000000 --- a/src/DataStreams/MergingAggregatedBlockInputStream.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include - -#include - - -namespace DB -{ - -Block MergingAggregatedBlockInputStream::getHeader() const -{ - return aggregator.getHeader(final); -} - - -Block MergingAggregatedBlockInputStream::readImpl() -{ - if (!executed) - { - executed = true; - AggregatedDataVariants data_variants; - - Aggregator::CancellationHook hook = [&]() { return this->isCancelled(); }; - aggregator.setCancellationHook(hook); - - aggregator.mergeStream(children.back(), data_variants, max_threads); - blocks = aggregator.convertToBlocks(data_variants, final, max_threads); - it = blocks.begin(); - } - - Block res; - if (isCancelledOrThrowIfKilled() || it == blocks.end()) - return res; - - res = std::move(*it); - ++it; - - return res; -} - - -} diff --git a/src/DataStreams/MergingAggregatedBlockInputStream.h b/src/DataStreams/MergingAggregatedBlockInputStream.h deleted file mode 100644 index e717d2b335d..00000000000 --- a/src/DataStreams/MergingAggregatedBlockInputStream.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include -#include - - -namespace DB -{ - - -/** A pre-aggregate stream of blocks in which each block is already aggregated. - * Aggregate functions in blocks should not be finalized so that their states can be merged. - */ -class MergingAggregatedBlockInputStream : public IBlockInputStream -{ -public: - MergingAggregatedBlockInputStream(const BlockInputStreamPtr & input, const Aggregator::Params & params, bool final_, size_t max_threads_) - : aggregator(params), final(final_), max_threads(max_threads_) - { - children.push_back(input); - } - - String getName() const override { return "MergingAggregated"; } - - Block getHeader() const override; - -protected: - Block readImpl() override; - -private: - Aggregator aggregator; - bool final; - size_t max_threads; - - bool executed = false; - BlocksList blocks; - BlocksList::iterator it; -}; - -} diff --git a/src/DataStreams/MergingAggregatedMemoryEfficientBlockInputStream.cpp b/src/DataStreams/MergingAggregatedMemoryEfficientBlockInputStream.cpp deleted file mode 100644 index 56dcbda0487..00000000000 --- a/src/DataStreams/MergingAggregatedMemoryEfficientBlockInputStream.cpp +++ /dev/null @@ -1,626 +0,0 @@ -#include -#include -#include -#include -#include - - -namespace CurrentMetrics -{ - extern const Metric QueryThread; -} - - -namespace DB -{ -namespace ErrorCodes -{ - extern const int LOGICAL_ERROR; -} - - -/** Scheme of operation: - * - * We have to output blocks in specific order: by bucket number: - * - * o o o o ... o - * 0 1 2 3 255 - * - * Each block is the result of merge of blocks with same bucket number from several sources: - * - * src1 o o ... - * | | - * src2 o o - * - * | | - * v v - * - * result o o - * 0 1 - * - * (we must merge 0th block from src1 with 0th block from src2 to form 0th result block and so on) - * - * We may read (request over network) blocks from different sources in parallel. - * It is done by getNextBlocksToMerge method. Number of threads is 'reading_threads'. - * - * Also, we may do merges for different buckets in parallel. - * For example, we may - * merge 1th block from src1 with 1th block from src2 in one thread - * and merge 2nd block from src1 with 2nd block from src2 in other thread. - * Number of threads is 'merging_threads' - * And we must keep only 'merging_threads' buckets of blocks in memory simultaneously, - * because our goal is to limit memory usage: not to keep all result in memory, but return it in streaming form. - * - * So, we return result sequentially, but perform calculations of resulting blocks in parallel. - * (calculation - is doing merge of source blocks for same buckets) - * - * Example: - * - * src1 . . o o . . . - * | | - * src2 o o - * - * | | - * v v - * - * result . . o o . . . - * - * In this picture, we do only two merges in parallel. - * When a merge is done, method 'getNextBlocksToMerge' is called to get blocks from sources for next bucket. - * Then next merge is performed. - * - * Main ('readImpl') method is waiting for merged blocks for next bucket and returns it. - */ - - -MergingAggregatedMemoryEfficientBlockInputStream::MergingAggregatedMemoryEfficientBlockInputStream( - BlockInputStreams inputs_, const Aggregator::Params & params, bool final_, size_t reading_threads_, size_t merging_threads_) - : aggregator(params), final(final_), - reading_threads(std::min(reading_threads_, inputs_.size())), merging_threads(merging_threads_), - inputs(inputs_.begin(), inputs_.end()) -{ - children = inputs_; - - /** Create threads that will request and read data from remote servers. - */ - if (reading_threads > 1) - reading_pool = std::make_unique(reading_threads); - - /** Create threads. Each of them will pull next set of blocks to merge in a loop, - * then merge them and place result in a queue (in fact, ordered map), from where we will read ready result blocks. - */ - if (merging_threads > 1) - parallel_merge_data = std::make_unique(merging_threads); -} - - -Block MergingAggregatedMemoryEfficientBlockInputStream::getHeader() const -{ - return aggregator.getHeader(final); -} - - -void MergingAggregatedMemoryEfficientBlockInputStream::readPrefix() -{ - start(); -} - - -void MergingAggregatedMemoryEfficientBlockInputStream::readSuffix() -{ - if (!all_read && !isCancelled()) - throw Exception("readSuffix called before all data is read", ErrorCodes::LOGICAL_ERROR); - - finalize(); - - for (auto & child : children) - child->readSuffix(); -} - - -void MergingAggregatedMemoryEfficientBlockInputStream::cancel(bool kill) -{ - if (kill) - is_killed = true; - - bool old_val = false; - if (!is_cancelled.compare_exchange_strong(old_val, true)) - return; - - if (parallel_merge_data) - { - { - std::unique_lock lock(parallel_merge_data->merged_blocks_mutex); - parallel_merge_data->finish = true; - } - parallel_merge_data->merged_blocks_changed.notify_one(); /// readImpl method must stop waiting and exit. - parallel_merge_data->have_space.notify_all(); /// Merging threads must stop waiting and exit. - } - - for (auto & input : inputs) - { - try - { - input.stream->cancel(kill); - } - catch (...) - { - /** If failed to ask to stop processing one or more sources. - * (example: connection reset during distributed query execution) - * - then don't care. - */ - LOG_ERROR(log, "Exception while cancelling {}", input.stream->getName()); - } - } -} - - -void MergingAggregatedMemoryEfficientBlockInputStream::start() -{ - if (started) - return; - - started = true; - - /// If child is RemoteBlockInputStream, then child->readPrefix() will send query to remote server, initiating calculations. - - if (reading_threads == 1) - { - for (auto & child : children) - child->readPrefix(); - } - else - { - size_t num_children = children.size(); - try - { - for (size_t i = 0; i < num_children; ++i) - { - auto & child = children[i]; - - auto thread_group = CurrentThread::getGroup(); - reading_pool->scheduleOrThrowOnError([&child, thread_group] - { - setThreadName("MergeAggReadThr"); - if (thread_group) - CurrentThread::attachToIfDetached(thread_group); - CurrentMetrics::Increment metric_increment{CurrentMetrics::QueryThread}; - child->readPrefix(); - }); - } - } - catch (...) - { - reading_pool->wait(); - throw; - } - reading_pool->wait(); - } - - if (merging_threads > 1) - { - auto & pool = parallel_merge_data->pool; - - /** Create threads that will receive and merge blocks. - */ - - for (size_t i = 0; i < merging_threads; ++i) - pool.scheduleOrThrowOnError([this, thread_group = CurrentThread::getGroup()]() { mergeThread(thread_group); }); - } -} - - -Block MergingAggregatedMemoryEfficientBlockInputStream::readImpl() -{ - start(); - - if (!parallel_merge_data) - { - if (BlocksToMerge blocks_to_merge = getNextBlocksToMerge()) - return aggregator.mergeBlocks(*blocks_to_merge, final); - return {}; - } - else - { - Block res; - - while (true) - { - std::unique_lock lock(parallel_merge_data->merged_blocks_mutex); - - parallel_merge_data->merged_blocks_changed.wait(lock, [this] - { - return parallel_merge_data->finish /// Requested to finish early. - || parallel_merge_data->exception /// An error in merging thread. - || parallel_merge_data->exhausted /// No more data in sources. - || !parallel_merge_data->merged_blocks.empty(); /// Have another merged block. - }); - - if (parallel_merge_data->exception) - std::rethrow_exception(parallel_merge_data->exception); - - if (parallel_merge_data->finish) - break; - - bool have_merged_block_or_merging_in_progress = !parallel_merge_data->merged_blocks.empty(); - - if (parallel_merge_data->exhausted && !have_merged_block_or_merging_in_progress) - break; - - if (have_merged_block_or_merging_in_progress) - { - auto it = parallel_merge_data->merged_blocks.begin(); - - if (it->second) - { - res.swap(it->second); - parallel_merge_data->merged_blocks.erase(it); - - lock.unlock(); - parallel_merge_data->have_space.notify_one(); /// We consumed block. Merging thread may merge next block for us. - break; - } - } - } - - if (!res) - all_read = true; - - return res; - } -} - - -MergingAggregatedMemoryEfficientBlockInputStream::~MergingAggregatedMemoryEfficientBlockInputStream() -{ - try - { - if (!all_read) - cancel(false); - - finalize(); - } - catch (...) - { - tryLogCurrentException(__PRETTY_FUNCTION__); - } -} - - -void MergingAggregatedMemoryEfficientBlockInputStream::finalize() -{ - if (!started) - return; - - LOG_TRACE(log, "Waiting for threads to finish"); - - if (parallel_merge_data) - parallel_merge_data->pool.wait(); - - LOG_TRACE(log, "Waited for threads to finish"); -} - - -void MergingAggregatedMemoryEfficientBlockInputStream::mergeThread(ThreadGroupStatusPtr thread_group) -{ - CurrentMetrics::Increment metric_increment{CurrentMetrics::QueryThread}; - - try - { - if (thread_group) - CurrentThread::attachToIfDetached(thread_group); - setThreadName("MergeAggMergThr"); - - while (!parallel_merge_data->finish) - { - /** Receiving next blocks is processing by one thread pool, and merge is in another. - * This is quite complex interaction. - * Each time: - * - 'reading_threads' will read one next block from each source; - * - group of blocks for merge is created from them; - * - one of 'merging_threads' will do merge this group of blocks; - */ - BlocksToMerge blocks_to_merge; - int output_order = -1; - - /** Synchronously: - * - fetch next blocks from sources, - * wait for space in 'merged_blocks' - * and reserve a place in 'merged_blocks' to do merge of them; - * - or, if no next blocks, set 'exhausted' flag. - */ - { - std::lock_guard lock_next_blocks(parallel_merge_data->get_next_blocks_mutex); - - if (parallel_merge_data->exhausted || parallel_merge_data->finish) - break; - - blocks_to_merge = getNextBlocksToMerge(); - - if (!blocks_to_merge || blocks_to_merge->empty()) - { - { - std::unique_lock lock_merged_blocks(parallel_merge_data->merged_blocks_mutex); - parallel_merge_data->exhausted = true; - } - - /// No new blocks has been read from sources. (But maybe, in another mergeThread, some previous block is still prepared.) - parallel_merge_data->merged_blocks_changed.notify_one(); - break; - } - - output_order = blocks_to_merge->front().info.is_overflows - ? NUM_BUCKETS /// "Overflow" blocks returned by 'getNextBlocksToMerge' after all other blocks. - : blocks_to_merge->front().info.bucket_num; - - { - std::unique_lock lock_merged_blocks(parallel_merge_data->merged_blocks_mutex); - - parallel_merge_data->have_space.wait(lock_merged_blocks, [this] - { - return parallel_merge_data->merged_blocks.size() < merging_threads - || parallel_merge_data->finish; - }); - - if (parallel_merge_data->finish) - break; - - /** Place empty block. It is promise to do merge and fill it. - * Main thread knows, that there will be result for 'output_order' place. - * Main thread must return results exactly in 'output_order', so that is important. - */ - parallel_merge_data->merged_blocks[output_order]; //-V607 - } - } - - /// At this point, several merge threads may work in parallel. - Block res = aggregator.mergeBlocks(*blocks_to_merge, final); - - { - std::lock_guard lock(parallel_merge_data->merged_blocks_mutex); - - if (parallel_merge_data->finish) - break; - - parallel_merge_data->merged_blocks[output_order] = res; - } - - /// Notify that we have another merged block. - parallel_merge_data->merged_blocks_changed.notify_one(); - } - } - catch (...) - { - { - std::lock_guard lock(parallel_merge_data->merged_blocks_mutex); - parallel_merge_data->exception = std::current_exception(); - parallel_merge_data->finish = true; - } - - parallel_merge_data->merged_blocks_changed.notify_one(); - parallel_merge_data->have_space.notify_all(); - } -} - - -MergingAggregatedMemoryEfficientBlockInputStream::BlocksToMerge MergingAggregatedMemoryEfficientBlockInputStream::getNextBlocksToMerge() -{ - /** There are several input sources. - * From each of them, data may be received in one of following forms: - * - * 1. Block with specified 'bucket_num'. - * It means, that on remote server, data was partitioned by buckets. - * And data for each 'bucket_num' from different servers may be merged independently. - * Because data in different buckets will contain different aggregation keys. - * Data for different 'bucket_num's will be received in increasing order of 'bucket_num'. - * - * 2. Block without specified 'bucket_num'. - * It means, that on remote server, data was not partitioned by buckets. - * If all servers will send non-partitioned data, we may just merge it. - * But if some other servers will send partitioned data, - * then we must first partition non-partitioned data, and then merge data in each partition. - * - * 3. Blocks with 'is_overflows' = true. - * It is additional data, that was not passed 'max_rows_to_group_by' threshold. - * It must be merged together independently of ordinary data. - */ - ++current_bucket_num; - - /// Read from source next block with bucket number not greater than 'current_bucket_num'. - - auto need_that_input = [this] (Input & input) - { - return !input.is_exhausted - && input.block.info.bucket_num < current_bucket_num; - }; - - auto read_from_input = [this] (Input & input) - { - /// If block with 'overflows' (not ordinary data) will be received, then remember that block and repeat. - while (true) - { -// std::cerr << "reading block\n"; - Block block = input.stream->read(); - - if (!block) - { -// std::cerr << "input is exhausted\n"; - input.is_exhausted = true; - break; - } - - if (block.info.bucket_num != -1) - { - /// One of partitioned blocks for two-level data. -// std::cerr << "block for bucket " << block.info.bucket_num << "\n"; - - has_two_level = true; - input.block = block; - } - else if (block.info.is_overflows) - { -// std::cerr << "block for overflows\n"; - - has_overflows = true; - input.overflow_block = block; - - continue; - } - else - { - /// Block for non-partitioned (single-level) data. -// std::cerr << "block without bucket\n"; - - input.block = block; - } - - break; - } - }; - - if (reading_threads == 1) - { - for (auto & input : inputs) - if (need_that_input(input)) - read_from_input(input); - } - else - { - try - { - for (auto & input : inputs) - { - if (need_that_input(input)) - { - auto thread_group = CurrentThread::getGroup(); - reading_pool->scheduleOrThrowOnError([&input, &read_from_input, thread_group] - { - setThreadName("MergeAggReadThr"); - if (thread_group) - CurrentThread::attachToIfDetached(thread_group); - CurrentMetrics::Increment metric_increment{CurrentMetrics::QueryThread}; - read_from_input(input); - }); - } - } - } - catch (...) - { - reading_pool->wait(); - throw; - } - reading_pool->wait(); - } - - while (true) - { - if (current_bucket_num >= NUM_BUCKETS) - { - /// All ordinary data was processed. Maybe, there are also 'overflows'-blocks. -// std::cerr << "at end\n"; - - if (has_overflows) - { -// std::cerr << "merging overflows\n"; - - has_overflows = false; - BlocksToMerge blocks_to_merge = std::make_unique(); - - for (auto & input : inputs) - if (input.overflow_block) - blocks_to_merge->emplace_back(std::move(input.overflow_block)); - - return blocks_to_merge; - } - else - return {}; - } - else if (has_two_level) - { - /** Having two-level (partitioned) data. - * Will process by bucket numbers in increasing order. - * Find minimum bucket number, for which there is data - * - this will be data for merge. - */ -// std::cerr << "has two level\n"; - - int min_bucket_num = NUM_BUCKETS; - - for (auto & input : inputs) - { - /// Blocks for already partitioned (two-level) data. - if (input.block.info.bucket_num != -1 && input.block.info.bucket_num < min_bucket_num) - min_bucket_num = input.block.info.bucket_num; - - /// Not yet partitioned (splitted to buckets) block. Will partition it and place result to 'splitted_blocks'. - if (input.block.info.bucket_num == -1 && input.block && input.splitted_blocks.empty()) - { - LOG_TRACE(&Poco::Logger::get("MergingAggregatedMemoryEfficient"), "Having block without bucket: will split."); - - input.splitted_blocks = aggregator.convertBlockToTwoLevel(input.block); - input.block = Block(); - } - - /// Blocks we got by splitting non-partitioned blocks. - if (!input.splitted_blocks.empty()) - { - for (const auto & block : input.splitted_blocks) - { - if (block && block.info.bucket_num < min_bucket_num) - { - min_bucket_num = block.info.bucket_num; - break; - } - } - } - } - - current_bucket_num = min_bucket_num; - -// std::cerr << "current_bucket_num = " << current_bucket_num << "\n"; - - /// No more blocks with ordinary data. - if (current_bucket_num == NUM_BUCKETS) - continue; - - /// Collect all blocks for 'current_bucket_num' to do merge. - BlocksToMerge blocks_to_merge = std::make_unique(); - - for (auto & input : inputs) - { - if (input.block.info.bucket_num == current_bucket_num) - { -// std::cerr << "having block for current_bucket_num\n"; - - blocks_to_merge->emplace_back(std::move(input.block)); - input.block = Block(); - } - else if (!input.splitted_blocks.empty() && input.splitted_blocks[min_bucket_num]) - { -// std::cerr << "having splitted data for bucket\n"; - - blocks_to_merge->emplace_back(std::move(input.splitted_blocks[min_bucket_num])); - input.splitted_blocks[min_bucket_num] = Block(); - } - } - - return blocks_to_merge; - } - else - { - /// There are only non-partitioned (single-level) data. Just merge them. -// std::cerr << "don't have two level\n"; - - BlocksToMerge blocks_to_merge = std::make_unique(); - - for (auto & input : inputs) - if (input.block) - blocks_to_merge->emplace_back(std::move(input.block)); - - current_bucket_num = NUM_BUCKETS; - return blocks_to_merge; - } - } -} - -} diff --git a/src/DataStreams/MergingAggregatedMemoryEfficientBlockInputStream.h b/src/DataStreams/MergingAggregatedMemoryEfficientBlockInputStream.h deleted file mode 100644 index f071ac42ee5..00000000000 --- a/src/DataStreams/MergingAggregatedMemoryEfficientBlockInputStream.h +++ /dev/null @@ -1,158 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - - -namespace DB -{ - - -/** Pre-aggregates block streams, holding in RAM only one or more (up to merging_threads) blocks from each source. - * This saves RAM in case of using two-level aggregation, where in each source there will be up to 256 blocks with parts of the result. - * - * Aggregate functions in blocks should not be finalized so that their states can be combined. - * - * Used to solve two tasks: - * - * 1. External aggregation with data flush to disk. - * Partially aggregated data (previously divided into 256 buckets) is flushed to some number of files on the disk. - * We need to read them and merge them by buckets - keeping only a few buckets from each file in RAM simultaneously. - * - * 2. Merge aggregation results for distributed query processing. - * Partially aggregated data arrives from different servers, which can be splitted down or not, into 256 buckets, - * and these buckets are passed to us by the network from each server in sequence, one by one. - * You should also read and merge by the buckets. - * - * The essence of the work: - * - * There are a number of sources. They give out blocks with partially aggregated data. - * Each source can return one of the following block sequences: - * 1. "unsplitted" block with bucket_num = -1; - * 2. "splitted" (two_level) blocks with bucket_num from 0 to 255; - * In both cases, there may also be a block of "overflows" with bucket_num = -1 and is_overflows = true; - * - * We start from the convention that splitted blocks are always passed in the order of bucket_num. - * That is, if a < b, then the bucket_num = a block goes before bucket_num = b. - * This is needed for a memory-efficient merge - * - so that you do not need to read the blocks up front, but go all the way up by bucket_num. - * - * In this case, not all bucket_num from the range of 0..255 can be present. - * The overflow block can be presented in any order relative to other blocks (but it can be only one). - * - * It is necessary to combine these sequences of blocks and return the result as a sequence with the same properties. - * That is, at the output, if there are "splitted" blocks in the sequence, then they should go in the order of bucket_num. - * - * The merge can be performed using several (merging_threads) threads. - * For this, receiving of a set of blocks for the next bucket_num should be done sequentially, - * and then, when we have several received sets, they can be merged in parallel. - * - * When you receive next blocks from different sources, - * data from sources can also be read in several threads (reading_threads) - * for optimal performance in the presence of a fast network or disks (from where these blocks are read). - */ -class MergingAggregatedMemoryEfficientBlockInputStream final : public IBlockInputStream -{ -public: - MergingAggregatedMemoryEfficientBlockInputStream( - BlockInputStreams inputs_, const Aggregator::Params & params, bool final_, - size_t reading_threads_, size_t merging_threads_); - - ~MergingAggregatedMemoryEfficientBlockInputStream() override; - - String getName() const override { return "MergingAggregatedMemoryEfficient"; } - - /// Sends the request (initiates calculations) earlier than `read`. - void readPrefix() override; - - /// Called either after everything is read, or after cancel. - void readSuffix() override; - - /** Different from the default implementation by trying to stop all sources, - * skipping failed by execution. - */ - void cancel(bool kill) override; - - Block getHeader() const override; - -protected: - Block readImpl() override; - -private: - static constexpr int NUM_BUCKETS = 256; - - Aggregator aggregator; - bool final; - size_t reading_threads; - size_t merging_threads; - - bool started = false; - bool all_read = false; - std::atomic has_two_level {false}; - std::atomic has_overflows {false}; - int current_bucket_num = -1; - - Poco::Logger * log = &Poco::Logger::get("MergingAggregatedMemoryEfficientBlockInputStream"); - - - struct Input - { - BlockInputStreamPtr stream; - Block block; - Block overflow_block; - std::vector splitted_blocks; - bool is_exhausted = false; - - Input(BlockInputStreamPtr & stream_) : stream(stream_) {} - }; - - std::vector inputs; - - using BlocksToMerge = std::unique_ptr; - - void start(); - - /// Get blocks that you can merge. This allows you to merge them in parallel in separate threads. - BlocksToMerge getNextBlocksToMerge(); - - std::unique_ptr reading_pool; - - /// For a parallel merge. - - struct ParallelMergeData - { - ThreadPool pool; - - /// Now one of the merging threads receives next blocks for the merge. This operation must be done sequentially. - std::mutex get_next_blocks_mutex; - - std::atomic exhausted {false}; /// No more source data. - std::atomic finish {false}; /// Need to terminate early. - - std::exception_ptr exception; - /// It is necessary to give out blocks in the order of the key (bucket_num). - /// If the value is an empty block, you need to wait for its merge. - /// (This means the promise that there will be data here, which is important because the data should be given out - /// in the order of the key - bucket_num) - std::map merged_blocks; - std::mutex merged_blocks_mutex; - /// An event that is used by merging threads to tell the main thread that the new block is ready. - std::condition_variable merged_blocks_changed; - /// An event by which the main thread is telling merging threads that it is possible to process the next group of blocks. - std::condition_variable have_space; - - explicit ParallelMergeData(size_t max_threads) : pool(max_threads) {} - }; - - std::unique_ptr parallel_merge_data; - - void mergeThread(ThreadGroupStatusPtr thread_group); - - void finalize(); -}; - -} diff --git a/src/DataStreams/ParallelAggregatingBlockInputStream.cpp b/src/DataStreams/ParallelAggregatingBlockInputStream.cpp deleted file mode 100644 index 611059c1443..00000000000 --- a/src/DataStreams/ParallelAggregatingBlockInputStream.cpp +++ /dev/null @@ -1,199 +0,0 @@ -#include -#include -#include -#include -#include - - -namespace ProfileEvents -{ - extern const Event ExternalAggregationMerge; -} - - -namespace DB -{ - - -ParallelAggregatingBlockInputStream::ParallelAggregatingBlockInputStream( - const BlockInputStreams & inputs, const BlockInputStreamPtr & additional_input_at_end, - const Aggregator::Params & params_, bool final_, size_t max_threads_, size_t temporary_data_merge_threads_) - : params(params_), aggregator(params), - final(final_), max_threads(std::min(inputs.size(), max_threads_)), temporary_data_merge_threads(temporary_data_merge_threads_), - keys_size(params.keys_size), aggregates_size(params.aggregates_size), - handler(*this), processor(inputs, additional_input_at_end, max_threads, handler) -{ - children = inputs; - if (additional_input_at_end) - children.push_back(additional_input_at_end); -} - - -Block ParallelAggregatingBlockInputStream::getHeader() const -{ - return aggregator.getHeader(final); -} - - -void ParallelAggregatingBlockInputStream::cancel(bool kill) -{ - if (kill) - is_killed = true; - bool old_val = false; - if (!is_cancelled.compare_exchange_strong(old_val, true, std::memory_order_seq_cst, std::memory_order_relaxed)) - return; - - if (!executed) - processor.cancel(kill); -} - - -Block ParallelAggregatingBlockInputStream::readImpl() -{ - if (!executed) - { - Aggregator::CancellationHook hook = [&]() { return this->isCancelled(); }; - aggregator.setCancellationHook(hook); - - execute(); - - if (isCancelledOrThrowIfKilled()) - return {}; - - if (!aggregator.hasTemporaryFiles()) - { - /** If all partially-aggregated data is in RAM, then merge them in parallel, also in RAM. - */ - impl = aggregator.mergeAndConvertToBlocks(many_data, final, max_threads); - } - else - { - /** If there are temporary files with partially-aggregated data on the disk, - * then read and merge them, spending the minimum amount of memory. - */ - - ProfileEvents::increment(ProfileEvents::ExternalAggregationMerge); - - const auto & files = aggregator.getTemporaryFiles(); - BlockInputStreams input_streams; - for (const auto & file : files.files) - { - temporary_inputs.emplace_back(std::make_unique(file->path())); - input_streams.emplace_back(temporary_inputs.back()->block_in); - } - - LOG_TRACE(log, "Will merge {} temporary files of size {} compressed, {} uncompressed.", files.files.size(), ReadableSize(files.sum_size_compressed), ReadableSize(files.sum_size_uncompressed)); - - impl = std::make_unique( - input_streams, params, final, temporary_data_merge_threads, temporary_data_merge_threads); - } - - executed = true; - } - - Block res; - if (isCancelledOrThrowIfKilled() || !impl) - return res; - - return impl->read(); -} - - -void ParallelAggregatingBlockInputStream::Handler::onBlock(Block & block, size_t thread_num) -{ - parent.aggregator.executeOnBlock(block, *parent.many_data[thread_num], - parent.threads_data[thread_num].key_columns, parent.threads_data[thread_num].aggregate_columns, parent.no_more_keys); - - parent.threads_data[thread_num].src_rows += block.rows(); - parent.threads_data[thread_num].src_bytes += block.bytes(); -} - -void ParallelAggregatingBlockInputStream::Handler::onFinishThread(size_t thread_num) -{ - if (!parent.isCancelled() && parent.aggregator.hasTemporaryFiles()) - { - /// Flush data in the RAM to disk. So it's easier to unite them later. - auto & data = *parent.many_data[thread_num]; - - if (data.isConvertibleToTwoLevel()) - data.convertToTwoLevel(); - - if (!data.empty()) - parent.aggregator.writeToTemporaryFile(data); - } -} - -void ParallelAggregatingBlockInputStream::Handler::onFinish() -{ - if (!parent.isCancelled() && parent.aggregator.hasTemporaryFiles()) - { - /// It may happen that some data has not yet been flushed, - /// because at the time of `onFinishThread` call, no data has been flushed to disk, and then some were. - for (auto & data : parent.many_data) - { - if (data->isConvertibleToTwoLevel()) - data->convertToTwoLevel(); - - if (!data->empty()) - parent.aggregator.writeToTemporaryFile(*data); - } - } -} - -void ParallelAggregatingBlockInputStream::Handler::onException(std::exception_ptr & exception, size_t thread_num) -{ - parent.exceptions[thread_num] = exception; - parent.cancel(false); -} - - -void ParallelAggregatingBlockInputStream::execute() -{ - many_data.resize(max_threads); - exceptions.resize(max_threads); - - for (size_t i = 0; i < max_threads; ++i) - threads_data.emplace_back(keys_size, aggregates_size); - - LOG_TRACE(log, "Aggregating"); - - Stopwatch watch; - - for (auto & elem : many_data) - elem = std::make_shared(); - - processor.process(); - processor.wait(); - - rethrowFirstException(exceptions); - - if (isCancelledOrThrowIfKilled()) - return; - - double elapsed_seconds = watch.elapsedSeconds(); - - size_t total_src_rows = 0; - size_t total_src_bytes = 0; - for (size_t i = 0; i < max_threads; ++i) - { - size_t rows = many_data[i]->size(); - LOG_TRACE(log, "Aggregated. {} to {} rows (from {}) in {} sec. ({} rows/sec., {}/sec.)", - threads_data[i].src_rows, rows, ReadableSize(threads_data[i].src_bytes), - elapsed_seconds, threads_data[i].src_rows / elapsed_seconds, - ReadableSize(threads_data[i].src_bytes / elapsed_seconds)); - - total_src_rows += threads_data[i].src_rows; - total_src_bytes += threads_data[i].src_bytes; - } - LOG_TRACE(log, "Total aggregated. {} rows (from {}) in {} sec. ({} rows/sec., {}/sec.)", - total_src_rows, ReadableSize(total_src_bytes), elapsed_seconds, - total_src_rows / elapsed_seconds, ReadableSize(total_src_bytes / elapsed_seconds)); - - /// If there was no data, and we aggregate without keys, we must return single row with the result of empty aggregation. - /// To do this, we pass a block with zero rows to aggregate. - if (total_src_rows == 0 && params.keys_size == 0 && !params.empty_result_for_aggregation_by_empty_set) - aggregator.executeOnBlock(children.at(0)->getHeader(), *many_data[0], - threads_data[0].key_columns, threads_data[0].aggregate_columns, no_more_keys); -} - -} diff --git a/src/DataStreams/ParallelAggregatingBlockInputStream.h b/src/DataStreams/ParallelAggregatingBlockInputStream.h deleted file mode 100644 index 4b0a2e806fa..00000000000 --- a/src/DataStreams/ParallelAggregatingBlockInputStream.h +++ /dev/null @@ -1,112 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include -#include - - -namespace DB -{ - - -/** Aggregates several sources in parallel. - * Makes aggregation of blocks from different sources independently in different threads, then combines the results. - * If final == false, aggregate functions are not finalized, that is, they are not replaced by their value, but contain an intermediate state of calculations. - * This is necessary so that aggregation can continue (for example, by combining streams of partially aggregated data). - */ -class ParallelAggregatingBlockInputStream : public IBlockInputStream -{ -public: - /** Columns from key_names and arguments of aggregate functions must already be computed. - */ - ParallelAggregatingBlockInputStream( - const BlockInputStreams & inputs, const BlockInputStreamPtr & additional_input_at_end, - const Aggregator::Params & params_, bool final_, size_t max_threads_, size_t temporary_data_merge_threads_); - - String getName() const override { return "ParallelAggregating"; } - - void cancel(bool kill) override; - - Block getHeader() const override; - -protected: - /// Do nothing that preparation to execution of the query be done in parallel, in ParallelInputsProcessor. - void readPrefix() override - { - } - - Block readImpl() override; - -private: - Aggregator::Params params; - Aggregator aggregator; - bool final; - size_t max_threads; - size_t temporary_data_merge_threads; - - size_t keys_size; - size_t aggregates_size; - - /** Used if there is a limit on the maximum number of rows in the aggregation, - * and if group_by_overflow_mode == ANY. - * In this case, new keys are not added to the set, but aggregation is performed only by - * keys that have already been added into the set. - */ - bool no_more_keys = false; - - std::atomic executed {false}; - std::vector> temporary_inputs; - - Poco::Logger * log = &Poco::Logger::get("ParallelAggregatingBlockInputStream"); - - - ManyAggregatedDataVariants many_data; - Exceptions exceptions; - - struct ThreadData - { - size_t src_rows = 0; - size_t src_bytes = 0; - - ColumnRawPtrs key_columns; - Aggregator::AggregateColumns aggregate_columns; - - ThreadData(size_t keys_size_, size_t aggregates_size_) - { - key_columns.resize(keys_size_); - aggregate_columns.resize(aggregates_size_); - } - }; - - std::vector threads_data; - - - struct Handler - { - Handler(ParallelAggregatingBlockInputStream & parent_) - : parent(parent_) {} - - void onBlock(Block & block, size_t thread_num); - void onFinishThread(size_t thread_num); - void onFinish(); - void onException(std::exception_ptr & exception, size_t thread_num); - - ParallelAggregatingBlockInputStream & parent; - }; - - Handler handler; - ParallelInputsProcessor processor; - - - void execute(); - - - /** From here we get the finished blocks after the aggregation. - */ - std::unique_ptr impl; -}; - -} diff --git a/src/DataStreams/PartialSortingBlockInputStream.cpp b/src/DataStreams/PartialSortingBlockInputStream.cpp deleted file mode 100644 index 3307f0352d7..00000000000 --- a/src/DataStreams/PartialSortingBlockInputStream.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include - -#include - - -namespace DB -{ - - -Block PartialSortingBlockInputStream::readImpl() -{ - Block res = children.back()->read(); - sortBlock(res, description, limit); - return res; -} - - -} diff --git a/src/DataStreams/PartialSortingBlockInputStream.h b/src/DataStreams/PartialSortingBlockInputStream.h deleted file mode 100644 index 0c98b98385b..00000000000 --- a/src/DataStreams/PartialSortingBlockInputStream.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include - -#include - - -namespace DB -{ - -/** Sorts each block individually by the values of the specified columns. - * At the moment, not very optimal algorithm is used. - */ -class PartialSortingBlockInputStream : public IBlockInputStream -{ -public: - /// limit - if not 0, then you can sort each block not completely, but only `limit` first rows by order. - PartialSortingBlockInputStream(const BlockInputStreamPtr & input_, SortDescription & description_, UInt64 limit_ = 0) - : description(description_), limit(limit_) - { - children.push_back(input_); - } - - String getName() const override { return "PartialSorting"; } - Block getHeader() const override { return children.at(0)->getHeader(); } - -protected: - Block readImpl() override; - -private: - SortDescription description; - UInt64 limit; -}; - -} diff --git a/src/DataStreams/PushingToViewsBlockOutputStream.cpp b/src/DataStreams/PushingToViewsBlockOutputStream.cpp index a4f0b58adeb..2c4792e184e 100644 --- a/src/DataStreams/PushingToViewsBlockOutputStream.cpp +++ b/src/DataStreams/PushingToViewsBlockOutputStream.cpp @@ -153,7 +153,7 @@ void PushingToViewsBlockOutputStream::write(const Block & block) const Settings & settings = context.getSettingsRef(); if (settings.parallel_view_processing && views.size() > 1) { - // Push to views concurrently if enabled, and more than one view is attached + // Push to views concurrently if enabled and more than one view is attached ThreadPool pool(std::min(size_t(settings.max_threads), views.size())); for (size_t view_num = 0; view_num < views.size(); ++view_num) { @@ -208,6 +208,45 @@ void PushingToViewsBlockOutputStream::writeSuffix() std::exception_ptr first_exception; + const Settings & settings = context.getSettingsRef(); + bool parallel_processing = false; + + /// Run writeSuffix() for views in separate thread pool. + /// In could have been done in PushingToViewsBlockOutputStream::process, however + /// it is not good if insert into main table fail but into view succeed. + if (settings.parallel_view_processing && views.size() > 1) + { + parallel_processing = true; + + // Push to views concurrently if enabled and more than one view is attached + ThreadPool pool(std::min(size_t(settings.max_threads), views.size())); + auto thread_group = CurrentThread::getGroup(); + + for (auto & view : views) + { + if (view.exception) + continue; + + pool.scheduleOrThrowOnError([thread_group, &view] + { + setThreadName("PushingToViews"); + if (thread_group) + CurrentThread::attachToIfDetached(thread_group); + + try + { + view.out->writeSuffix(); + } + catch (...) + { + view.exception = std::current_exception(); + } + }); + } + // Wait for concurrent view processing + pool.wait(); + } + for (auto & view : views) { if (view.exception) @@ -218,6 +257,9 @@ void PushingToViewsBlockOutputStream::writeSuffix() continue; } + if (parallel_processing) + continue; + try { view.out->writeSuffix(); diff --git a/src/DataStreams/RemoteBlockInputStream.cpp b/src/DataStreams/RemoteBlockInputStream.cpp index d5e77bb0759..c7c5ce2d00a 100644 --- a/src/DataStreams/RemoteBlockInputStream.cpp +++ b/src/DataStreams/RemoteBlockInputStream.cpp @@ -1,103 +1,46 @@ #include -#include -#include -#include -#include #include -#include -#include -#include -#include -#include -#include - -#include -#include - namespace DB { -namespace ErrorCodes -{ - extern const int UNKNOWN_PACKET_FROM_SERVER; -} - - RemoteBlockInputStream::RemoteBlockInputStream( Connection & connection, const String & query_, const Block & header_, const Context & context_, const Settings * settings, const ThrottlerPtr & throttler, const Scalars & scalars_, const Tables & external_tables_, QueryProcessingStage::Enum stage_) - : header(header_), query(query_), context(context_), scalars(scalars_), external_tables(external_tables_), stage(stage_) + : query_executor(connection, query_, header_, context_, settings, throttler, scalars_, external_tables_, stage_) { - if (settings) - context.setSettings(*settings); - - create_multiplexed_connections = [this, &connection, throttler]() - { - return std::make_unique(connection, context.getSettingsRef(), throttler); - }; + init(); } RemoteBlockInputStream::RemoteBlockInputStream( std::vector && connections, const String & query_, const Block & header_, const Context & context_, const Settings * settings, const ThrottlerPtr & throttler, const Scalars & scalars_, const Tables & external_tables_, QueryProcessingStage::Enum stage_) - : header(header_), query(query_), context(context_), scalars(scalars_), external_tables(external_tables_), stage(stage_) + : query_executor(std::move(connections), query_, header_, context_, settings, throttler, scalars_, external_tables_, stage_) { - if (settings) - context.setSettings(*settings); - - create_multiplexed_connections = [this, connections, throttler]() mutable - { - return std::make_unique( - std::move(connections), context.getSettingsRef(), throttler); - }; + init(); } RemoteBlockInputStream::RemoteBlockInputStream( const ConnectionPoolWithFailoverPtr & pool, const String & query_, const Block & header_, const Context & context_, const Settings * settings, const ThrottlerPtr & throttler, const Scalars & scalars_, const Tables & external_tables_, QueryProcessingStage::Enum stage_) - : header(header_), query(query_), context(context_), scalars(scalars_), external_tables(external_tables_), stage(stage_) + : query_executor(pool, query_, header_, context_, settings, throttler, scalars_, external_tables_, stage_) { - if (settings) - context.setSettings(*settings); - - create_multiplexed_connections = [this, pool, throttler]() - { - const Settings & current_settings = context.getSettingsRef(); - auto timeouts = ConnectionTimeouts::getTCPTimeoutsWithFailover(current_settings); - std::vector connections; - if (main_table) - { - auto try_results = pool->getManyChecked(timeouts, ¤t_settings, pool_mode, main_table.getQualifiedName()); - connections.reserve(try_results.size()); - for (auto & try_result : try_results) - connections.emplace_back(std::move(try_result.entry)); - } - else - connections = pool->getMany(timeouts, ¤t_settings, pool_mode); - - return std::make_unique( - std::move(connections), current_settings, throttler); - }; + init(); } -RemoteBlockInputStream::~RemoteBlockInputStream() +void RemoteBlockInputStream::init() { - /** If interrupted in the middle of the loop of communication with replicas, then interrupt - * all connections, then read and skip the remaining packets to make sure - * these connections did not remain hanging in the out-of-sync state. - */ - if (established || isQueryPending()) - multiplexed_connections->disconnect(); + query_executor.setProgressCallback([this](const Progress & progress) { progressImpl(progress); }); + query_executor.setProfileInfoCallback([this](const BlockStreamProfileInfo & info_) { info.setFrom(info_, true); }); + query_executor.setLogger(log); } void RemoteBlockInputStream::readPrefix() { - if (!sent_query) - sendQuery(); + query_executor.sendQuery(); } void RemoteBlockInputStream::cancel(bool kill) @@ -109,280 +52,22 @@ void RemoteBlockInputStream::cancel(bool kill) if (!is_cancelled.compare_exchange_strong(old_val, true, std::memory_order_seq_cst, std::memory_order_relaxed)) return; - { - std::lock_guard lock(external_tables_mutex); - - /// Stop sending external data. - for (auto & vec : external_tables_data) - for (auto & elem : vec) - elem->is_cancelled = true; - } - - if (!isQueryPending() || hasThrownException()) - return; - - tryCancel("Cancelling query"); + query_executor.cancel(); } -void RemoteBlockInputStream::sendScalars() -{ - multiplexed_connections->sendScalarsData(scalars); -} - -void RemoteBlockInputStream::sendExternalTables() -{ - size_t count = multiplexed_connections->size(); - - { - std::lock_guard lock(external_tables_mutex); - - external_tables_data.reserve(count); - - for (size_t i = 0; i < count; ++i) - { - ExternalTablesData res; - for (const auto & table : external_tables) - { - StoragePtr cur = table.second; - QueryProcessingStage::Enum read_from_table_stage = cur->getQueryProcessingStage(context); - - Pipes pipes; - - pipes = cur->read(cur->getColumns().getNamesOfPhysical(), {}, context, - read_from_table_stage, DEFAULT_BLOCK_SIZE, 1); - - auto data = std::make_unique(); - data->table_name = table.first; - - if (pipes.empty()) - data->pipe = std::make_unique(std::make_shared(cur->getSampleBlock(), Chunk())); - else if (pipes.size() == 1) - data->pipe = std::make_unique(std::move(pipes.front())); - else - { - auto concat = std::make_shared(pipes.front().getHeader(), pipes.size()); - data->pipe = std::make_unique(std::move(pipes), std::move(concat)); - } - - res.emplace_back(std::move(data)); - } - external_tables_data.push_back(std::move(res)); - } - } - - multiplexed_connections->sendExternalTablesData(external_tables_data); -} - - -/** If we receive a block with slightly different column types, or with excessive columns, - * we will adapt it to expected structure. - */ -static Block adaptBlockStructure(const Block & block, const Block & header) -{ - /// Special case when reader doesn't care about result structure. Deprecated and used only in Benchmark, PerformanceTest. - if (!header) - return block; - - Block res; - res.info = block.info; - - for (const auto & elem : header) - { - ColumnPtr column; - - if (elem.column && isColumnConst(*elem.column)) - { - /// We expect constant column in block. - /// If block is not empty, then get value for constant from it, - /// because it may be different for remote server for functions like version(), uptime(), ... - if (block.rows() > 0 && block.has(elem.name)) - { - /// Const column is passed as materialized. Get first value from it. - /// - /// TODO: check that column contains the same value. - /// TODO: serialize const columns. - auto col = block.getByName(elem.name); - col.column = block.getByName(elem.name).column->cut(0, 1); - - column = castColumn(col, elem.type); - - if (!isColumnConst(*column)) - column = ColumnConst::create(column, block.rows()); - else - /// It is not possible now. Just in case we support const columns serialization. - column = column->cloneResized(block.rows()); - } - else - column = elem.column->cloneResized(block.rows()); - } - else - column = castColumn(block.getByName(elem.name), elem.type); - - res.insert({column, elem.type, elem.name}); - } - return res; -} - - Block RemoteBlockInputStream::readImpl() { - if (!sent_query) - { - sendQuery(); + auto block = query_executor.read(); - if (context.getSettingsRef().skip_unavailable_shards && (0 == multiplexed_connections->size())) - return {}; - } + if (isCancelledOrThrowIfKilled()) + return Block(); - while (true) - { - if (isCancelledOrThrowIfKilled()) - return Block(); - - Packet packet = multiplexed_connections->receivePacket(); - - switch (packet.type) - { - case Protocol::Server::Data: - /// If the block is not empty and is not a header block - if (packet.block && (packet.block.rows() > 0)) - return adaptBlockStructure(packet.block, header); - break; /// If the block is empty - we will receive other packets before EndOfStream. - - case Protocol::Server::Exception: - got_exception_from_replica = true; - packet.exception->rethrow(); - break; - - case Protocol::Server::EndOfStream: - if (!multiplexed_connections->hasActiveConnections()) - { - finished = true; - return Block(); - } - break; - - case Protocol::Server::Progress: - /** We use the progress from a remote server. - * We also include in ProcessList, - * and we use it to check - * constraints (for example, the minimum speed of query execution) - * and quotas (for example, the number of lines to read). - */ - progressImpl(packet.progress); - break; - - case Protocol::Server::ProfileInfo: - /// Use own (client-side) info about read bytes, it is more correct info than server-side one. - info.setFrom(packet.profile_info, true); - break; - - case Protocol::Server::Totals: - totals = packet.block; - break; - - case Protocol::Server::Extremes: - extremes = packet.block; - break; - - case Protocol::Server::Log: - /// Pass logs from remote server to client - if (auto log_queue = CurrentThread::getInternalTextLogsQueue()) - log_queue->pushBlock(std::move(packet.block)); - break; - - default: - got_unknown_packet_from_replica = true; - throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER); - } - } + return block; } void RemoteBlockInputStream::readSuffixImpl() { - /** If one of: - * - nothing started to do; - * - received all packets before EndOfStream; - * - received exception from one replica; - * - received an unknown packet from one replica; - * then you do not need to read anything. - */ - if (!isQueryPending() || hasThrownException()) - return; - - /** If you have not read all the data yet, but they are no longer needed. - * This may be due to the fact that the data is sufficient (for example, when using LIMIT). - */ - - /// Send the request to abort the execution of the request, if not already sent. - tryCancel("Cancelling query because enough data has been read"); - - /// Get the remaining packets so that there is no out of sync in the connections to the replicas. - Packet packet = multiplexed_connections->drain(); - switch (packet.type) - { - case Protocol::Server::EndOfStream: - finished = true; - break; - - case Protocol::Server::Exception: - got_exception_from_replica = true; - packet.exception->rethrow(); - break; - - default: - got_unknown_packet_from_replica = true; - throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER); - } -} - -void RemoteBlockInputStream::sendQuery() -{ - multiplexed_connections = create_multiplexed_connections(); - - const auto& settings = context.getSettingsRef(); - if (settings.skip_unavailable_shards && 0 == multiplexed_connections->size()) - return; - - established = true; - - auto timeouts = ConnectionTimeouts::getTCPTimeoutsWithFailover(settings); - ClientInfo modified_client_info = context.getClientInfo(); - modified_client_info.query_kind = ClientInfo::QueryKind::SECONDARY_QUERY; - - multiplexed_connections->sendQuery(timeouts, query, query_id, stage, modified_client_info, true); - - established = false; - sent_query = true; - - if (settings.enable_scalar_subquery_optimization) - sendScalars(); - sendExternalTables(); -} - -void RemoteBlockInputStream::tryCancel(const char * reason) -{ - { - std::lock_guard guard(was_cancelled_mutex); - - if (was_cancelled) - return; - - was_cancelled = true; - multiplexed_connections->sendCancel(); - } - - LOG_TRACE(log, "({}) {}", multiplexed_connections->dumpAddresses(), reason); -} - -bool RemoteBlockInputStream::isQueryPending() const -{ - return sent_query && !finished; -} - -bool RemoteBlockInputStream::hasThrownException() const -{ - return got_exception_from_replica || got_unknown_packet_from_replica; + query_executor.finish(); } } diff --git a/src/DataStreams/RemoteBlockInputStream.h b/src/DataStreams/RemoteBlockInputStream.h index f6bac4155da..628feb0ab80 100644 --- a/src/DataStreams/RemoteBlockInputStream.h +++ b/src/DataStreams/RemoteBlockInputStream.h @@ -11,6 +11,7 @@ #include #include +#include namespace DB { @@ -44,114 +45,38 @@ public: const ThrottlerPtr & throttler = nullptr, const Scalars & scalars_ = Scalars(), const Tables & external_tables_ = Tables(), QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete); - ~RemoteBlockInputStream() override; - /// Set the query_id. For now, used by performance test to later find the query - /// in the server query_log. Must be called before sending the query to the - /// server. - void setQueryId(const std::string& query_id_) { assert(!sent_query); query_id = query_id_; } + /// in the server query_log. Must be called before sending the query to the server. + void setQueryId(const std::string & query_id) { query_executor.setQueryId(query_id); } /// Specify how we allocate connections on a shard. - void setPoolMode(PoolMode pool_mode_) { pool_mode = pool_mode_; } + void setPoolMode(PoolMode pool_mode) { query_executor.setPoolMode(pool_mode); } - void setMainTable(StorageID main_table_) { main_table = std::move(main_table_); } + void setMainTable(StorageID main_table_) { query_executor.setMainTable(std::move(main_table_)); } /// Sends query (initiates calculation) before read() void readPrefix() override; - /** Prevent default progress notification because progress' callback is - called by its own - */ + /// Prevent default progress notification because progress' callback is called by its own. void progress(const Progress & /*value*/) override {} void cancel(bool kill) override; String getName() const override { return "Remote"; } - Block getHeader() const override { return header; } + Block getHeader() const override { return query_executor.getHeader(); } + Block getTotals() override { return query_executor.getTotals(); } + Block getExtremes() override { return query_executor.getExtremes(); } protected: - /// Send all scalars to remote servers - void sendScalars(); - - /// Send all temporary tables to remote servers - void sendExternalTables(); - Block readImpl() override; - void readSuffixImpl() override; - /// Returns true if query was sent - bool isQueryPending() const; - - /// Returns true if exception was thrown - bool hasThrownException() const; - private: - void sendQuery(); - - Block receiveBlock(); - - /// If wasn't sent yet, send request to cancell all connections to replicas - void tryCancel(const char * reason); - -private: - Block header; - - std::function()> create_multiplexed_connections; - - std::unique_ptr multiplexed_connections; - - const String query; - String query_id = ""; - Context context; - - /// Scalars needed to be sent to remote servers - Scalars scalars; - /// Temporary tables needed to be sent to remote servers - Tables external_tables; - QueryProcessingStage::Enum stage; - - /// Streams for reading from temporary tables and following sending of data - /// to remote servers for GLOBAL-subqueries - std::vector external_tables_data; - std::mutex external_tables_mutex; - - /// Connections to replicas are established, but no queries are sent yet - std::atomic established { false }; - - /// Query is sent (used before getting first block) - std::atomic sent_query { false }; - - /** All data from all replicas are received, before EndOfStream packet. - * To prevent desynchronization, if not all data is read before object - * destruction, it's required to send cancel query request to replicas and - * read all packets before EndOfStream - */ - std::atomic finished { false }; - - /** Cancel query request was sent to all replicas because data is not needed anymore - * This behaviour may occur when: - * - data size is already satisfactory (when using LIMIT, for example) - * - an exception was thrown from client side - */ - bool was_cancelled { false }; - std::mutex was_cancelled_mutex; - - /** An exception from replica was received. No need in receiving more packets or - * requesting to cancel query execution - */ - std::atomic got_exception_from_replica { false }; - - /** Unkown packet was received from replica. No need in receiving more packets or - * requesting to cancel query execution - */ - std::atomic got_unknown_packet_from_replica { false }; - - PoolMode pool_mode = PoolMode::GET_MANY; - StorageID main_table = StorageID::createEmpty(); - + RemoteQueryExecutor query_executor; Poco::Logger * log = &Poco::Logger::get("RemoteBlockInputStream"); + + void init(); }; } diff --git a/src/DataStreams/RemoteQueryExecutor.cpp b/src/DataStreams/RemoteQueryExecutor.cpp new file mode 100644 index 00000000000..cf3b2c4abcd --- /dev/null +++ b/src/DataStreams/RemoteQueryExecutor.cpp @@ -0,0 +1,378 @@ +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int UNKNOWN_PACKET_FROM_SERVER; +} + +RemoteQueryExecutor::RemoteQueryExecutor( + Connection & connection, + const String & query_, const Block & header_, const Context & context_, const Settings * settings, + ThrottlerPtr throttler, const Scalars & scalars_, const Tables & external_tables_, QueryProcessingStage::Enum stage_) + : header(header_), query(query_), context(context_) + , scalars(scalars_), external_tables(external_tables_), stage(stage_) +{ + if (settings) + context.setSettings(*settings); + + create_multiplexed_connections = [this, &connection, throttler]() + { + return std::make_unique(connection, context.getSettingsRef(), throttler); + }; +} + +RemoteQueryExecutor::RemoteQueryExecutor( + std::vector && connections, + const String & query_, const Block & header_, const Context & context_, const Settings * settings, + const ThrottlerPtr & throttler, const Scalars & scalars_, const Tables & external_tables_, QueryProcessingStage::Enum stage_) + : header(header_), query(query_), context(context_) + , scalars(scalars_), external_tables(external_tables_), stage(stage_) +{ + if (settings) + context.setSettings(*settings); + + create_multiplexed_connections = [this, connections, throttler]() mutable + { + return std::make_unique( + std::move(connections), context.getSettingsRef(), throttler); + }; +} + +RemoteQueryExecutor::RemoteQueryExecutor( + const ConnectionPoolWithFailoverPtr & pool, + const String & query_, const Block & header_, const Context & context_, const Settings * settings, + const ThrottlerPtr & throttler, const Scalars & scalars_, const Tables & external_tables_, QueryProcessingStage::Enum stage_) + : header(header_), query(query_), context(context_) + , scalars(scalars_), external_tables(external_tables_), stage(stage_) +{ + if (settings) + context.setSettings(*settings); + + create_multiplexed_connections = [this, pool, throttler]() + { + const Settings & current_settings = context.getSettingsRef(); + auto timeouts = ConnectionTimeouts::getTCPTimeoutsWithFailover(current_settings); + std::vector connections; + if (main_table) + { + auto try_results = pool->getManyChecked(timeouts, ¤t_settings, pool_mode, main_table.getQualifiedName()); + connections.reserve(try_results.size()); + for (auto & try_result : try_results) + connections.emplace_back(std::move(try_result.entry)); + } + else + connections = pool->getMany(timeouts, ¤t_settings, pool_mode); + + return std::make_unique( + std::move(connections), current_settings, throttler); + }; +} + +RemoteQueryExecutor::~RemoteQueryExecutor() +{ + /** If interrupted in the middle of the loop of communication with replicas, then interrupt + * all connections, then read and skip the remaining packets to make sure + * these connections did not remain hanging in the out-of-sync state. + */ + if (established || isQueryPending()) + multiplexed_connections->disconnect(); +} + +/** If we receive a block with slightly different column types, or with excessive columns, + * we will adapt it to expected structure. + */ +static Block adaptBlockStructure(const Block & block, const Block & header) +{ + /// Special case when reader doesn't care about result structure. Deprecated and used only in Benchmark, PerformanceTest. + if (!header) + return block; + + Block res; + res.info = block.info; + + for (const auto & elem : header) + { + ColumnPtr column; + + if (elem.column && isColumnConst(*elem.column)) + { + /// We expect constant column in block. + /// If block is not empty, then get value for constant from it, + /// because it may be different for remote server for functions like version(), uptime(), ... + if (block.rows() > 0 && block.has(elem.name)) + { + /// Const column is passed as materialized. Get first value from it. + /// + /// TODO: check that column contains the same value. + /// TODO: serialize const columns. + auto col = block.getByName(elem.name); + col.column = block.getByName(elem.name).column->cut(0, 1); + + column = castColumn(col, elem.type); + + if (!isColumnConst(*column)) + column = ColumnConst::create(column, block.rows()); + else + /// It is not possible now. Just in case we support const columns serialization. + column = column->cloneResized(block.rows()); + } + else + column = elem.column->cloneResized(block.rows()); + } + else + column = castColumn(block.getByName(elem.name), elem.type); + + res.insert({column, elem.type, elem.name}); + } + return res; +} + +void RemoteQueryExecutor::sendQuery() +{ + if (sent_query) + return; + + multiplexed_connections = create_multiplexed_connections(); + + const auto& settings = context.getSettingsRef(); + if (settings.skip_unavailable_shards && 0 == multiplexed_connections->size()) + return; + + established = true; + + auto timeouts = ConnectionTimeouts::getTCPTimeoutsWithFailover(settings); + ClientInfo modified_client_info = context.getClientInfo(); + modified_client_info.query_kind = ClientInfo::QueryKind::SECONDARY_QUERY; + + multiplexed_connections->sendQuery(timeouts, query, query_id, stage, modified_client_info, true); + + established = false; + sent_query = true; + + if (settings.enable_scalar_subquery_optimization) + sendScalars(); + sendExternalTables(); +} + +Block RemoteQueryExecutor::read() +{ + if (!sent_query) + { + sendQuery(); + + if (context.getSettingsRef().skip_unavailable_shards && (0 == multiplexed_connections->size())) + return {}; + } + + while (true) + { + if (was_cancelled) + return Block(); + + Packet packet = multiplexed_connections->receivePacket(); + + switch (packet.type) + { + case Protocol::Server::Data: + /// If the block is not empty and is not a header block + if (packet.block && (packet.block.rows() > 0)) + return adaptBlockStructure(packet.block, header); + break; /// If the block is empty - we will receive other packets before EndOfStream. + + case Protocol::Server::Exception: + got_exception_from_replica = true; + packet.exception->rethrow(); + break; + + case Protocol::Server::EndOfStream: + if (!multiplexed_connections->hasActiveConnections()) + { + finished = true; + return Block(); + } + break; + + case Protocol::Server::Progress: + /** We use the progress from a remote server. + * We also include in ProcessList, + * and we use it to check + * constraints (for example, the minimum speed of query execution) + * and quotas (for example, the number of lines to read). + */ + if (progress_callback) + progress_callback(packet.progress); + break; + + case Protocol::Server::ProfileInfo: + /// Use own (client-side) info about read bytes, it is more correct info than server-side one. + if (profile_info_callback) + profile_info_callback(packet.profile_info); + break; + + case Protocol::Server::Totals: + totals = packet.block; + break; + + case Protocol::Server::Extremes: + extremes = packet.block; + break; + + case Protocol::Server::Log: + /// Pass logs from remote server to client + if (auto log_queue = CurrentThread::getInternalTextLogsQueue()) + log_queue->pushBlock(std::move(packet.block)); + break; + + default: + got_unknown_packet_from_replica = true; + throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER); + } + } +} + +void RemoteQueryExecutor::finish() +{ + /** If one of: + * - nothing started to do; + * - received all packets before EndOfStream; + * - received exception from one replica; + * - received an unknown packet from one replica; + * then you do not need to read anything. + */ + if (!isQueryPending() || hasThrownException()) + return; + + /** If you have not read all the data yet, but they are no longer needed. + * This may be due to the fact that the data is sufficient (for example, when using LIMIT). + */ + + /// Send the request to abort the execution of the request, if not already sent. + tryCancel("Cancelling query because enough data has been read"); + + /// Get the remaining packets so that there is no out of sync in the connections to the replicas. + Packet packet = multiplexed_connections->drain(); + switch (packet.type) + { + case Protocol::Server::EndOfStream: + finished = true; + break; + + case Protocol::Server::Exception: + got_exception_from_replica = true; + packet.exception->rethrow(); + break; + + default: + got_unknown_packet_from_replica = true; + throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER); + } +} + +void RemoteQueryExecutor::cancel() +{ + { + std::lock_guard lock(external_tables_mutex); + + /// Stop sending external data. + for (auto & vec : external_tables_data) + for (auto & elem : vec) + elem->is_cancelled = true; + } + + if (!isQueryPending() || hasThrownException()) + return; + + tryCancel("Cancelling query"); +} + +void RemoteQueryExecutor::sendScalars() +{ + multiplexed_connections->sendScalarsData(scalars); +} + +void RemoteQueryExecutor::sendExternalTables() +{ + size_t count = multiplexed_connections->size(); + + { + std::lock_guard lock(external_tables_mutex); + + external_tables_data.reserve(count); + + for (size_t i = 0; i < count; ++i) + { + ExternalTablesData res; + for (const auto & table : external_tables) + { + StoragePtr cur = table.second; + QueryProcessingStage::Enum read_from_table_stage = cur->getQueryProcessingStage(context); + + Pipes pipes; + + pipes = cur->read(cur->getColumns().getNamesOfPhysical(), {}, context, + read_from_table_stage, DEFAULT_BLOCK_SIZE, 1); + + auto data = std::make_unique(); + data->table_name = table.first; + + if (pipes.empty()) + data->pipe = std::make_unique(std::make_shared(cur->getSampleBlock(), Chunk())); + else if (pipes.size() == 1) + data->pipe = std::make_unique(std::move(pipes.front())); + else + { + auto concat = std::make_shared(pipes.front().getHeader(), pipes.size()); + data->pipe = std::make_unique(std::move(pipes), std::move(concat)); + } + + res.emplace_back(std::move(data)); + } + external_tables_data.push_back(std::move(res)); + } + } + + multiplexed_connections->sendExternalTablesData(external_tables_data); +} + +void RemoteQueryExecutor::tryCancel(const char * reason) +{ + { + /// Flag was_cancelled is atomic because it is checked in read(). + std::lock_guard guard(was_cancelled_mutex); + + if (was_cancelled) + return; + + was_cancelled = true; + multiplexed_connections->sendCancel(); + } + + if (log) + LOG_TRACE(log, "({}) {}", multiplexed_connections->dumpAddresses(), reason); +} + +bool RemoteQueryExecutor::isQueryPending() const +{ + return sent_query && !finished; +} + +bool RemoteQueryExecutor::hasThrownException() const +{ + return got_exception_from_replica || got_unknown_packet_from_replica; +} + +} diff --git a/src/DataStreams/RemoteQueryExecutor.h b/src/DataStreams/RemoteQueryExecutor.h new file mode 100644 index 00000000000..0db0e0218be --- /dev/null +++ b/src/DataStreams/RemoteQueryExecutor.h @@ -0,0 +1,164 @@ +#pragma once + +#include +#include +#include + +namespace DB +{ + +class Throttler; +using ThrottlerPtr = std::shared_ptr; + +struct Progress; +using ProgressCallback = std::function; + +struct BlockStreamProfileInfo; +using ProfileInfoCallback = std::function; + +/// This class allows one to launch queries on remote replicas of one shard and get results +class RemoteQueryExecutor +{ +public: + /// Takes already set connection. + /// If `settings` is nullptr, settings will be taken from context. + RemoteQueryExecutor( + Connection & connection, + const String & query_, const Block & header_, const Context & context_, const Settings * settings = nullptr, + ThrottlerPtr throttler_ = nullptr, const Scalars & scalars_ = Scalars(), const Tables & external_tables_ = Tables(), + QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete); + + /// Accepts several connections already taken from pool. + /// If `settings` is nullptr, settings will be taken from context. + RemoteQueryExecutor( + std::vector && connections, + const String & query_, const Block & header_, const Context & context_, const Settings * settings = nullptr, + const ThrottlerPtr & throttler = nullptr, const Scalars & scalars_ = Scalars(), const Tables & external_tables_ = Tables(), + QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete); + + /// Takes a pool and gets one or several connections from it. + /// If `settings` is nullptr, settings will be taken from context. + RemoteQueryExecutor( + const ConnectionPoolWithFailoverPtr & pool, + const String & query_, const Block & header_, const Context & context_, const Settings * settings = nullptr, + const ThrottlerPtr & throttler = nullptr, const Scalars & scalars_ = Scalars(), const Tables & external_tables_ = Tables(), + QueryProcessingStage::Enum stage_ = QueryProcessingStage::Complete); + + ~RemoteQueryExecutor(); + + /// Create connection and send query, external tables and scalars. + void sendQuery(); + + /// Read next block of data. Returns empty block if query is finished. + Block read(); + + /// Receive all remain packets and finish query. + /// It should be cancelled after read returned empty block. + void finish(); + + /// Cancel query execution. Sends Cancel packet and ignore others. + /// This method may be called from separate thread. + void cancel(); + + /// Get totals and extremes if any. + Block getTotals() { return std::move(totals); } + Block getExtremes() { return std::move(extremes); } + + /// Set callback for progress. It will be called on Progress packet. + void setProgressCallback(ProgressCallback callback) { progress_callback = std::move(callback); } + + /// Set callback for profile info. It will be called on ProfileInfo packet. + void setProfileInfoCallback(ProfileInfoCallback callback) { profile_info_callback = std::move(callback); } + + /// Set the query_id. For now, used by performance test to later find the query + /// in the server query_log. Must be called before sending the query to the server. + void setQueryId(const std::string& query_id_) { assert(!sent_query); query_id = query_id_; } + + /// Specify how we allocate connections on a shard. + void setPoolMode(PoolMode pool_mode_) { pool_mode = pool_mode_; } + + void setMainTable(StorageID main_table_) { main_table = std::move(main_table_); } + + void setLogger(Poco::Logger * logger) { log = logger; } + + const Block & getHeader() const { return header; } + +private: + Block header; + Block totals; + Block extremes; + + std::function()> create_multiplexed_connections; + std::unique_ptr multiplexed_connections; + + const String query; + String query_id = ""; + Context context; + + ProgressCallback progress_callback; + ProfileInfoCallback profile_info_callback; + + /// Scalars needed to be sent to remote servers + Scalars scalars; + /// Temporary tables needed to be sent to remote servers + Tables external_tables; + QueryProcessingStage::Enum stage; + + /// Streams for reading from temporary tables and following sending of data + /// to remote servers for GLOBAL-subqueries + std::vector external_tables_data; + std::mutex external_tables_mutex; + + /// Connections to replicas are established, but no queries are sent yet + std::atomic established { false }; + + /// Query is sent (used before getting first block) + std::atomic sent_query { false }; + + /** All data from all replicas are received, before EndOfStream packet. + * To prevent desynchronization, if not all data is read before object + * destruction, it's required to send cancel query request to replicas and + * read all packets before EndOfStream + */ + std::atomic finished { false }; + + /** Cancel query request was sent to all replicas because data is not needed anymore + * This behaviour may occur when: + * - data size is already satisfactory (when using LIMIT, for example) + * - an exception was thrown from client side + */ + std::atomic was_cancelled { false }; + std::mutex was_cancelled_mutex; + + /** An exception from replica was received. No need in receiving more packets or + * requesting to cancel query execution + */ + std::atomic got_exception_from_replica { false }; + + /** Unknown packet was received from replica. No need in receiving more packets or + * requesting to cancel query execution + */ + std::atomic got_unknown_packet_from_replica { false }; + + PoolMode pool_mode = PoolMode::GET_MANY; + StorageID main_table = StorageID::createEmpty(); + + Poco::Logger * log = nullptr; + + /// Send all scalars to remote servers + void sendScalars(); + + /// Send all temporary tables to remote servers + void sendExternalTables(); + + /// If wasn't sent yet, send request to cancel all connections to replicas + void tryCancel(const char * reason); + + /// Returns true if query was sent + bool isQueryPending() const; + + /// Returns true if exception was thrown + bool hasThrownException() const; +}; + +} diff --git a/src/DataStreams/ReverseBlockInputStream.cpp b/src/DataStreams/ReverseBlockInputStream.cpp deleted file mode 100644 index 2dfa147c68f..00000000000 --- a/src/DataStreams/ReverseBlockInputStream.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "ReverseBlockInputStream.h" - -#include - -namespace DB -{ - ReverseBlockInputStream::ReverseBlockInputStream(const BlockInputStreamPtr & input) - { - children.push_back(input); - } - - String ReverseBlockInputStream::getName() const - { - return "Reverse"; - } - - Block ReverseBlockInputStream::getHeader() const - { - return children.at(0)->getHeader(); - } - - Block ReverseBlockInputStream::readImpl() - { - auto result_block = children.back()->read(); - - if (!result_block) - { - return Block(); - } - - IColumn::Permutation permutation; - - size_t rows_size = result_block.rows(); - for (size_t i = 0; i < rows_size; ++i) - permutation.emplace_back(rows_size - 1 - i); - - for (auto & block : result_block) - block.column = block.column->permute(permutation, 0); - - return result_block; - } -} diff --git a/src/DataStreams/ReverseBlockInputStream.h b/src/DataStreams/ReverseBlockInputStream.h deleted file mode 100644 index 0bb369ed5a1..00000000000 --- a/src/DataStreams/ReverseBlockInputStream.h +++ /dev/null @@ -1,21 +0,0 @@ -#pragma once - -#include - -namespace DB -{ - -/// Reverses an order of rows in every block in a data stream. -class ReverseBlockInputStream : public IBlockInputStream -{ -public: - ReverseBlockInputStream(const BlockInputStreamPtr & input); - - String getName() const override; - Block getHeader() const override; - -protected: - Block readImpl() override; -}; - -} diff --git a/src/DataStreams/RollupBlockInputStream.cpp b/src/DataStreams/RollupBlockInputStream.cpp deleted file mode 100644 index a913dc727fa..00000000000 --- a/src/DataStreams/RollupBlockInputStream.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include -#include -#include -#include - -namespace DB -{ - -RollupBlockInputStream::RollupBlockInputStream( - const BlockInputStreamPtr & input_, const Aggregator::Params & params_) : aggregator(params_), - keys(params_.keys) -{ - children.push_back(input_); - Aggregator::CancellationHook hook = [this]() { return this->isCancelled(); }; - aggregator.setCancellationHook(hook); -} - - -Block RollupBlockInputStream::getHeader() const -{ - Block res = children.at(0)->getHeader(); - finalizeBlock(res); - return res; -} - - -Block RollupBlockInputStream::readImpl() -{ - /** After reading a block from input stream, - * we will subsequently roll it up on next iterations of 'readImpl' - * by zeroing out every column one-by-one and re-merging a block. - */ - - if (!is_data_read) - { - BlocksList source_blocks; - while (auto block = children[0]->read()) - source_blocks.push_back(block); - - if (source_blocks.empty()) - return {}; - - is_data_read = true; - if (source_blocks.size() > 1) - rollup_block = aggregator.mergeBlocks(source_blocks, false); - else - rollup_block = std::move(source_blocks.front()); - - current_key = keys.size() - 1; - - auto finalized = rollup_block; - finalizeBlock(finalized); - return finalized; - } - - if (current_key < 0) - return {}; - - auto & current = rollup_block.getByPosition(keys[current_key]); - current.column = current.column->cloneEmpty()->cloneResized(rollup_block.rows()); - --current_key; - - BlocksList rollup_blocks = { rollup_block }; - rollup_block = aggregator.mergeBlocks(rollup_blocks, false); - - auto finalized = rollup_block; - finalizeBlock(finalized); - return finalized; -} -} diff --git a/src/DataStreams/RollupBlockInputStream.h b/src/DataStreams/RollupBlockInputStream.h deleted file mode 100644 index dabf1e392a3..00000000000 --- a/src/DataStreams/RollupBlockInputStream.h +++ /dev/null @@ -1,41 +0,0 @@ -#pragma once - -#include -#include -#include - - -namespace DB -{ - -class ExpressionActions; - - -/** Takes blocks after grouping, with non-finalized aggregate functions. - * Calculates subtotals and grand totals values for a set of columns. - */ -class RollupBlockInputStream : public IBlockInputStream -{ -private: - using ExpressionActionsPtr = std::shared_ptr; - using AggregateColumns = std::vector; -public: - RollupBlockInputStream( - const BlockInputStreamPtr & input_, const Aggregator::Params & params_); - - String getName() const override { return "Rollup"; } - - Block getHeader() const override; - -protected: - Block readImpl() override; - -private: - Aggregator aggregator; - ColumnNumbers keys; - ssize_t current_key = -1; - Block rollup_block; - bool is_data_read = false; -}; - -} diff --git a/src/DataStreams/TotalsHavingBlockInputStream.cpp b/src/DataStreams/TotalsHavingBlockInputStream.cpp deleted file mode 100644 index f8a7742c0d0..00000000000 --- a/src/DataStreams/TotalsHavingBlockInputStream.cpp +++ /dev/null @@ -1,181 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -namespace DB -{ - - -TotalsHavingBlockInputStream::TotalsHavingBlockInputStream( - const BlockInputStreamPtr & input_, - bool overflow_row_, const ExpressionActionsPtr & expression_, - const std::string & filter_column_, TotalsMode totals_mode_, double auto_include_threshold_, bool final_) - : overflow_row(overflow_row_), - expression(expression_), filter_column_name(filter_column_), totals_mode(totals_mode_), - auto_include_threshold(auto_include_threshold_), final(final_) -{ - children.push_back(input_); - - /// Initialize current totals with initial state. - - Block source_header = children.at(0)->getHeader(); - - current_totals.reserve(source_header.columns()); - for (const auto & elem : source_header) - { - // Create a column with default value - MutableColumnPtr new_column = elem.type->createColumn(); - elem.type->insertDefaultInto(*new_column); - current_totals.emplace_back(std::move(new_column)); - } -} - - -Block TotalsHavingBlockInputStream::getTotals() -{ - if (!totals) - { - /** If totals_mode == AFTER_HAVING_AUTO, you need to decide whether to add aggregates to TOTALS for strings, - * not passed max_rows_to_group_by. - */ - if (overflow_aggregates) - { - if (totals_mode == TotalsMode::BEFORE_HAVING - || totals_mode == TotalsMode::AFTER_HAVING_INCLUSIVE - || (totals_mode == TotalsMode::AFTER_HAVING_AUTO - && static_cast(passed_keys) / total_keys >= auto_include_threshold)) - addToTotals(overflow_aggregates, nullptr); - } - - totals = children.at(0)->getHeader().cloneWithColumns(std::move(current_totals)); - finalizeBlock(totals); - } - - if (totals && expression) - expression->execute(totals); - - return totals; -} - - -Block TotalsHavingBlockInputStream::getHeader() const -{ - Block res = children.at(0)->getHeader(); - if (final) - finalizeBlock(res); - if (expression) - expression->execute(res); - return res; -} - - -Block TotalsHavingBlockInputStream::readImpl() -{ - Block finalized; - Block block; - - while (true) - { - block = children[0]->read(); - - /// Block with values not included in `max_rows_to_group_by`. We'll postpone it. - if (overflow_row && block && block.info.is_overflows) - { - overflow_aggregates = block; - continue; - } - - if (!block) - return finalized; - - finalized = block; - if (final) - finalizeBlock(finalized); - - total_keys += finalized.rows(); - - if (filter_column_name.empty()) - { - addToTotals(block, nullptr); - } - else - { - /// Compute the expression in HAVING. - expression->execute(finalized); - - size_t filter_column_pos = finalized.getPositionByName(filter_column_name); - ColumnPtr filter_column_ptr = finalized.safeGetByPosition(filter_column_pos).column->convertToFullColumnIfConst(); - - FilterDescription filter_description(*filter_column_ptr); - - /// Add values to `totals` (if it was not already done). - if (totals_mode == TotalsMode::BEFORE_HAVING) - addToTotals(block, nullptr); - else - addToTotals(block, filter_description.data); - - /// Filter the block by expression in HAVING. - size_t columns = finalized.columns(); - - for (size_t i = 0; i < columns; ++i) - { - ColumnWithTypeAndName & current_column = finalized.safeGetByPosition(i); - current_column.column = current_column.column->filter(*filter_description.data, -1); - if (current_column.column->empty()) - { - finalized.clear(); - break; - } - } - } - - if (!finalized) - continue; - - passed_keys += finalized.rows(); - return finalized; - } -} - - -void TotalsHavingBlockInputStream::addToTotals(const Block & source_block, const IColumn::Filter * filter) -{ - for (size_t i = 0, num_columns = source_block.columns(); i < num_columns; ++i) - { - const auto * source_column = typeid_cast( - source_block.getByPosition(i).column.get()); - if (!source_column) - { - continue; - } - - auto & totals_column = assert_cast(*current_totals[i]); - assert(totals_column.size() == 1); - - /// Accumulate all aggregate states from a column of a source block into - /// the corresponding totals column. - const auto & vec = source_column->getData(); - size_t size = vec.size(); - - if (filter) - { - for (size_t j = 0; j < size; ++j) - if ((*filter)[j]) - totals_column.insertMergeFrom(vec[j]); - } - else - { - for (size_t j = 0; j < size; ++j) - totals_column.insertMergeFrom(vec[j]); - } - } -} - -} diff --git a/src/DataStreams/TotalsHavingBlockInputStream.h b/src/DataStreams/TotalsHavingBlockInputStream.h deleted file mode 100644 index 0114f62d77c..00000000000 --- a/src/DataStreams/TotalsHavingBlockInputStream.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include - - -namespace DB -{ - -class Arena; -using ArenaPtr = std::shared_ptr; - -class ExpressionActions; -enum class TotalsMode; - -/** Takes blocks after grouping, with non-finalized aggregate functions. - * Calculates total values according to totals_mode. - * If necessary, evaluates the expression from HAVING and filters rows. Returns the finalized and filtered blocks. - */ -class TotalsHavingBlockInputStream : public IBlockInputStream -{ -private: - using ExpressionActionsPtr = std::shared_ptr; - -public: - /// expression may be nullptr - TotalsHavingBlockInputStream( - const BlockInputStreamPtr & input_, - bool overflow_row_, const ExpressionActionsPtr & expression_, - const std::string & filter_column_, TotalsMode totals_mode_, double auto_include_threshold_, bool final_); - - String getName() const override { return "TotalsHaving"; } - - Block getTotals() override; - - Block getHeader() const override; - -protected: - Block readImpl() override; - -private: - bool overflow_row; - ExpressionActionsPtr expression; - String filter_column_name; - TotalsMode totals_mode; - double auto_include_threshold; - bool final; - size_t passed_keys = 0; - size_t total_keys = 0; - - /** Here are the values that did not pass max_rows_to_group_by. - * They are added or not added to the current_totals, depending on the totals_mode. - */ - Block overflow_aggregates; - - /// Here, total values are accumulated. After the work is finished, they will be placed in IBlockInputStream::totals. - MutableColumns current_totals; - - /// If filter == nullptr - add all rows. Otherwise, only the rows that pass the filter (HAVING). - void addToTotals(const Block & block, const IColumn::Filter * filter); -}; - -} diff --git a/src/DataStreams/narrowBlockInputStreams.cpp b/src/DataStreams/narrowBlockInputStreams.cpp index 370528d727c..8464adb5bb8 100644 --- a/src/DataStreams/narrowBlockInputStreams.cpp +++ b/src/DataStreams/narrowBlockInputStreams.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include "narrowBlockInputStreams.h" @@ -24,26 +23,6 @@ namespace } } -BlockInputStreams narrowBlockInputStreams(BlockInputStreams & inputs, size_t width) -{ - size_t size = inputs.size(); - if (size <= width) - return inputs; - - std::vector partitions(width); - - auto distribution = getDistribution(size, width); - - for (size_t i = 0; i < size; ++i) - partitions[distribution[i]].push_back(inputs[i]); - - BlockInputStreams res(width); - for (size_t i = 0; i < width; ++i) - res[i] = std::make_shared(partitions[i]); - - return res; -} - Pipes narrowPipes(Pipes pipes, size_t width) { size_t size = pipes.size(); diff --git a/src/DataStreams/narrowBlockInputStreams.h b/src/DataStreams/narrowBlockInputStreams.h index 3011f85b720..0477d72b3de 100644 --- a/src/DataStreams/narrowBlockInputStreams.h +++ b/src/DataStreams/narrowBlockInputStreams.h @@ -16,7 +16,6 @@ using Pipes = std::vector; * Trying to glue the sources with each other uniformly randomly. * (to avoid overweighting if the distribution of the amount of data in different sources is subject to some pattern) */ -BlockInputStreams narrowBlockInputStreams(BlockInputStreams & inputs, size_t width); Pipes narrowPipes(Pipes pipes, size_t width); } diff --git a/src/DataStreams/processConstants.cpp b/src/DataStreams/processConstants.cpp deleted file mode 100644 index b2eb2b723a6..00000000000 --- a/src/DataStreams/processConstants.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include - -namespace DB -{ - -void removeConstantsFromBlock(Block & block) -{ - size_t columns = block.columns(); - size_t i = 0; - while (i < columns) - { - if (block.getByPosition(i).column && isColumnConst(*block.getByPosition(i).column)) - { - block.erase(i); - --columns; - } - else - ++i; - } -} - - -void removeConstantsFromSortDescription(const Block & header, SortDescription & description) -{ - /// Note: This code is not correct if column description contains column numbers instead of column names. - /// Hopefully, everywhere where it is used, column description contains names. - description.erase(std::remove_if(description.begin(), description.end(), - [&](const SortColumnDescription & elem) - { - const auto & column = !elem.column_name.empty() ? header.getByName(elem.column_name) - : header.safeGetByPosition(elem.column_number); - return column.column && isColumnConst(*column.column); - }), description.end()); -} - - -void enrichBlockWithConstants(Block & block, const Block & header) -{ - size_t rows = block.rows(); - size_t columns = header.columns(); - - for (size_t i = 0; i < columns; ++i) - { - const auto & col_type_name = header.getByPosition(i); - if (col_type_name.column && isColumnConst(*col_type_name.column)) - block.insert(i, {col_type_name.column->cloneResized(rows), col_type_name.type, col_type_name.name}); - } -} -} diff --git a/src/DataStreams/processConstants.h b/src/DataStreams/processConstants.h deleted file mode 100644 index 6fedab75c76..00000000000 --- a/src/DataStreams/processConstants.h +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include -#include - - -namespace DB -{ -/** Functions for manipulate constants for sorting. - * See MergeSortingBlocksBlockInputStream and FinishSortingBlockInputStream for details. -*/ - -/** Remove constant columns from block. - */ -void removeConstantsFromBlock(Block & block); - -void removeConstantsFromSortDescription(const Block & header, SortDescription & description); - -/** Add into block, whose constant columns was removed by previous function, - * constant columns from header (which must have structure as before removal of constants from block). - */ -void enrichBlockWithConstants(Block & block, const Block & header); -} diff --git a/src/DataStreams/tests/CMakeLists.txt b/src/DataStreams/tests/CMakeLists.txt index 3f6e154927f..14db417b71c 100644 --- a/src/DataStreams/tests/CMakeLists.txt +++ b/src/DataStreams/tests/CMakeLists.txt @@ -1,16 +1,4 @@ set(SRCS) -add_executable (expression_stream expression_stream.cpp ${SRCS}) -target_link_libraries (expression_stream PRIVATE dbms clickhouse_storages_system clickhouse_parsers) - -add_executable (filter_stream filter_stream.cpp ${SRCS}) -target_link_libraries (filter_stream PRIVATE dbms clickhouse_storages_system clickhouse_parsers clickhouse_common_io) - -add_executable (union_stream2 union_stream2.cpp ${SRCS}) -target_link_libraries (union_stream2 PRIVATE dbms) - -add_executable (collapsing_sorted_stream collapsing_sorted_stream.cpp ${SRCS}) -target_link_libraries (collapsing_sorted_stream PRIVATE dbms) - add_executable (finish_sorting_stream finish_sorting_stream.cpp ${SRCS}) target_link_libraries (finish_sorting_stream PRIVATE dbms) diff --git a/src/DataStreams/tests/collapsing_sorted_stream.cpp b/src/DataStreams/tests/collapsing_sorted_stream.cpp deleted file mode 100644 index e6d2167578b..00000000000 --- a/src/DataStreams/tests/collapsing_sorted_stream.cpp +++ /dev/null @@ -1,84 +0,0 @@ -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -#include - - -int main(int, char **) -try -{ - using namespace DB; - - Poco::AutoPtr channel = new Poco::ConsoleChannel(std::cerr); - Poco::Logger::root().setChannel(channel); - Poco::Logger::root().setLevel("trace"); - - Block block1; - - { - ColumnWithTypeAndName column1; - column1.name = "Sign"; - column1.type = std::make_shared(); - column1.column = ColumnInt8::create({static_cast(1), static_cast(-1)}); - block1.insert(column1); - - ColumnWithTypeAndName column2; - column2.name = "CounterID"; - column2.type = std::make_shared(); - column2.column = ColumnUInt32::create({static_cast(123), static_cast(123)}); - block1.insert(column2); - } - - Block block2; - - { - ColumnWithTypeAndName column1; - column1.name = "Sign"; - column1.type = std::make_shared(); - column1.column = ColumnInt8::create({static_cast(1), static_cast(1)}); - block2.insert(column1); - - ColumnWithTypeAndName column2; - column2.name = "CounterID"; - column2.type = std::make_shared(); - column2.column = ColumnUInt32::create({static_cast(123), static_cast(456)}); - block2.insert(column2); - } - - BlockInputStreams inputs; - inputs.push_back(std::make_shared(block1)); - inputs.push_back(std::make_shared(block2)); - - SortDescription descr; - SortColumnDescription col_descr("CounterID", 1, 1); - descr.push_back(col_descr); - - //CollapsingSortedBlockInputStream collapsed(inputs, descr, "Sign", 1048576); - CollapsingFinalBlockInputStream collapsed(inputs, descr, "Sign"); - - SharedContextHolder shared_context = Context::createShared(); - Context context = Context::createGlobal(shared_context.get()); - context.makeGlobalContext(); - WriteBufferFromFileDescriptor out_buf(STDERR_FILENO); - BlockOutputStreamPtr output = context.getOutputFormat("TabSeparated", out_buf, block1); - - copyData(collapsed, *output); - - return 0; -} - -catch (const DB::Exception & e) -{ - std::cerr << e.what() << ", " << e.displayText() << std::endl; - throw; -} diff --git a/src/DataStreams/tests/expression_stream.cpp b/src/DataStreams/tests/expression_stream.cpp deleted file mode 100644 index 84b35cc2d3d..00000000000 --- a/src/DataStreams/tests/expression_stream.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include -#include - - -int main(int argc, char ** argv) -try -{ - using namespace DB; - - size_t n = argc == 2 ? parse(argv[1]) : 10ULL; - - std::string input = "SELECT number, number / 3, number * number"; - - ParserSelectQuery parser; - ASTPtr ast = parseQuery(parser, input.data(), input.data() + input.size(), "", 0, 0); - - SharedContextHolder shared_context = Context::createShared(); - Context context = Context::createGlobal(shared_context.get()); - context.makeGlobalContext(); - - NamesAndTypesList source_columns = {{"number", std::make_shared()}}; - auto syntax_result = SyntaxAnalyzer(context).analyze(ast, source_columns); - SelectQueryExpressionAnalyzer analyzer(ast, syntax_result, context); - ExpressionActionsChain chain(context); - analyzer.appendSelect(chain, false); - analyzer.appendProjectResult(chain); - chain.finalize(); - ExpressionActionsPtr expression = chain.getLastActions(); - - StoragePtr table = StorageSystemNumbers::create(StorageID("test", "numbers"), false); - - Names column_names; - column_names.push_back("number"); - - QueryProcessingStage::Enum stage = table->getQueryProcessingStage(context); - - BlockInputStreamPtr in; - in = std::make_shared(std::move(table->read(column_names, {}, context, stage, 8192, 1)[0])); - in = std::make_shared(in, expression); - in = std::make_shared(in, 10, std::max(static_cast(0), static_cast(n) - 10)); - - WriteBufferFromOStream out1(std::cout); - BlockOutputStreamPtr out = FormatFactory::instance().getOutput("TabSeparated", out1, expression->getSampleBlock(), context); - - { - Stopwatch stopwatch; - stopwatch.start(); - - copyData(*in, *out); - - stopwatch.stop(); - std::cout << std::fixed << std::setprecision(2) - << "Elapsed " << stopwatch.elapsedSeconds() << " sec." - << ", " << n / stopwatch.elapsedSeconds() << " rows/sec." - << std::endl; - } - - return 0; -} -catch (const DB::Exception & e) -{ - std::cerr << e.what() << ", " << e.displayText() << std::endl; - throw; -} - diff --git a/src/DataStreams/tests/filter_stream.cpp b/src/DataStreams/tests/filter_stream.cpp deleted file mode 100644 index 8c481e1f258..00000000000 --- a/src/DataStreams/tests/filter_stream.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - - -int main(int argc, char ** argv) -try -{ - using namespace DB; - - size_t n = argc == 2 ? parse(argv[1]) : 10ULL; - - std::string input = "SELECT number, number % 3 == 1"; - - ParserSelectQuery parser; - ASTPtr ast = parseQuery(parser, input.data(), input.data() + input.size(), "", 0, 0); - - formatAST(*ast, std::cerr); - std::cerr << std::endl; - - SharedContextHolder shared_context = Context::createShared(); - Context context = Context::createGlobal(shared_context.get()); - context.makeGlobalContext(); - - NamesAndTypesList source_columns = {{"number", std::make_shared()}}; - auto syntax_result = SyntaxAnalyzer(context).analyze(ast, source_columns); - SelectQueryExpressionAnalyzer analyzer(ast, syntax_result, context); - ExpressionActionsChain chain(context); - analyzer.appendSelect(chain, false); - analyzer.appendProjectResult(chain); - chain.finalize(); - ExpressionActionsPtr expression = chain.getLastActions(); - - StoragePtr table = StorageSystemNumbers::create(StorageID("test", "numbers"), false); - - Names column_names; - column_names.push_back("number"); - - QueryProcessingStage::Enum stage = table->getQueryProcessingStage(context); - - BlockInputStreamPtr in = std::make_shared(std::move(table->read(column_names, {}, context, stage, 8192, 1)[0])); - in = std::make_shared(in, expression, "equals(modulo(number, 3), 1)"); - in = std::make_shared(in, 10, std::max(static_cast(0), static_cast(n) - 10)); - - WriteBufferFromOStream ob(std::cout); - BlockOutputStreamPtr out = FormatFactory::instance().getOutput("TabSeparated", ob, expression->getSampleBlock(), context); - - { - Stopwatch stopwatch; - stopwatch.start(); - - copyData(*in, *out); - - stopwatch.stop(); - std::cout << std::fixed << std::setprecision(2) - << "Elapsed " << stopwatch.elapsedSeconds() << " sec." - << ", " << n / stopwatch.elapsedSeconds() << " rows/sec." - << std::endl; - } - - return 0; -} -catch (const DB::Exception & e) -{ - std::cerr << e.what() << ", " << e.displayText() << std::endl; - throw; -} diff --git a/src/DataStreams/tests/finish_sorting_stream.cpp b/src/DataStreams/tests/finish_sorting_stream.cpp index d10ae3fbb63..ea151e01293 100644 --- a/src/DataStreams/tests/finish_sorting_stream.cpp +++ b/src/DataStreams/tests/finish_sorting_stream.cpp @@ -6,11 +6,13 @@ #include #include -#include -#include -#include - #include +#include +#include +#include +#include +#include +#include using namespace DB; @@ -33,7 +35,11 @@ int main(int argc, char ** argv) size_t m = argc >= 2 ? std::stol(argv[1]) : 2; size_t n = argc >= 3 ? std::stol(argv[2]) : 10; - Blocks blocks; + SortDescription sort_descr; + sort_descr.emplace_back("col1", 1, 1); + Block block_header; + BlocksList blocks; + for (size_t t = 0; t < m; ++t) { Block block; @@ -53,28 +59,37 @@ int main(int argc, char ** argv) column.column = std::move(col); block.insert(column); } - blocks.push_back(block); + + if (!block_header) + block_header = block.cloneEmpty(); + + sortBlock(block, sort_descr); + blocks.emplace_back(std::move(block)); } - SortDescription sort_descr; - sort_descr.emplace_back("col1", 1, 1); + auto blocks_stream = std::make_shared(std::move(blocks)); + Pipe source(std::make_shared(std::move(blocks_stream))); + QueryPipeline pipeline; + pipeline.init(std::move(source)); - for (auto & block : blocks) - sortBlock(block, sort_descr); - - BlockInputStreamPtr stream = std::make_shared(blocks, sort_descr, n); + pipeline.addPipe({std::make_shared(pipeline.getHeader(), sort_descr, n, 0, 0, 0, nullptr, 0)}); SortDescription sort_descr_final; sort_descr_final.emplace_back("col1", 1, 1); sort_descr_final.emplace_back("col2", 1, 1); - stream = std::make_shared(stream, sort_descr, sort_descr_final, n, 0); + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, sort_descr, sort_descr_final, n, 0); + }); + + auto stream = std::make_shared(std::move(pipeline)); { Stopwatch stopwatch; stopwatch.start(); - Block res_block = blocks[0].cloneEmpty(); + Block res_block = block_header; while (Block block = stream->read()) { diff --git a/src/DataStreams/tests/union_stream2.cpp b/src/DataStreams/tests/union_stream2.cpp deleted file mode 100644 index 5b84d89a435..00000000000 --- a/src/DataStreams/tests/union_stream2.cpp +++ /dev/null @@ -1,66 +0,0 @@ -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include - -#include - -#include -#include -#include - - -using namespace DB; - -int main(int, char **) -try -{ - SharedContextHolder shared_context = Context::createShared(); - Context context = Context::createGlobal(shared_context.get()); - context.makeGlobalContext(); - Settings settings = context.getSettings(); - - context.setPath("./"); - - loadMetadata(context); - - Names column_names; - column_names.push_back("WatchID"); - - StoragePtr table = DatabaseCatalog::instance().getTable({"default", "hits6"}, context); - - QueryProcessingStage::Enum stage = table->getQueryProcessingStage(context); - auto pipes = table->read(column_names, {}, context, stage, settings.max_block_size, settings.max_threads); - - BlockInputStreams streams(pipes.size()); - - for (size_t i = 0, size = streams.size(); i < size; ++i) - streams[i] = std::make_shared(std::make_shared(std::move(pipes[i]))); - - BlockInputStreamPtr stream = std::make_shared(streams, nullptr, settings.max_threads); - stream = std::make_shared(stream, 10, 0); - - WriteBufferFromFileDescriptor wb(STDERR_FILENO); - Block sample = table->getSampleBlock(); - BlockOutputStreamPtr out = context.getOutputFormat("TabSeparated", wb, sample); - - copyData(*stream, *out); - - return 0; -} -catch (const Exception & e) -{ - std::cerr << e.what() << ", " << e.displayText() << std::endl - << std::endl - << "Stack trace:" << std::endl - << e.getStackTraceString(); - return 1; -} diff --git a/src/DataStreams/ya.make b/src/DataStreams/ya.make index 14e1c319394..4c391cf839a 100644 --- a/src/DataStreams/ya.make +++ b/src/DataStreams/ya.make @@ -9,56 +9,40 @@ NO_COMPILER_WARNINGS() SRCS( AddingDefaultBlockOutputStream.cpp AddingDefaultsBlockInputStream.cpp - AggregatingBlockInputStream.cpp AsynchronousBlockInputStream.cpp BlockIO.cpp BlockStreamProfileInfo.cpp CheckConstraintsBlockOutputStream.cpp CheckSortedBlockInputStream.cpp - CollapsingFinalBlockInputStream.cpp ColumnGathererStream.cpp ConvertingBlockInputStream.cpp copyData.cpp CountingBlockOutputStream.cpp CreatingSetsBlockInputStream.cpp - CubeBlockInputStream.cpp - DistinctBlockInputStream.cpp DistinctSortedBlockInputStream.cpp ExecutionSpeedLimits.cpp ExpressionBlockInputStream.cpp - FillingBlockInputStream.cpp FilterBlockInputStream.cpp - FilterColumnsBlockInputStream.cpp finalizeBlock.cpp - FinishSortingBlockInputStream.cpp IBlockInputStream.cpp InputStreamFromASTInsertQuery.cpp InternalTextLogsRowOutputStream.cpp LimitBlockInputStream.cpp - LimitByBlockInputStream.cpp materializeBlock.cpp MaterializingBlockInputStream.cpp - MergeSortingBlockInputStream.cpp - MergingAggregatedBlockInputStream.cpp - MergingAggregatedMemoryEfficientBlockInputStream.cpp MergingSortedBlockInputStream.cpp narrowBlockInputStreams.cpp NativeBlockInputStream.cpp NativeBlockOutputStream.cpp - ParallelAggregatingBlockInputStream.cpp ParallelParsingBlockInputStream.cpp - PartialSortingBlockInputStream.cpp - processConstants.cpp PushingToViewsBlockOutputStream.cpp RemoteBlockInputStream.cpp RemoteBlockOutputStream.cpp - ReverseBlockInputStream.cpp - RollupBlockInputStream.cpp + RemoteQueryExecutor.cpp SizeLimits.cpp SquashingBlockInputStream.cpp SquashingBlockOutputStream.cpp SquashingTransform.cpp - TotalsHavingBlockInputStream.cpp TTLBlockInputStream.cpp ) diff --git a/src/DataTypes/DataTypeAggregateFunction.cpp b/src/DataTypes/DataTypeAggregateFunction.cpp index 3fb380eac0f..59811b1cd55 100644 --- a/src/DataTypes/DataTypeAggregateFunction.cpp +++ b/src/DataTypes/DataTypeAggregateFunction.cpp @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -36,25 +38,25 @@ namespace ErrorCodes std::string DataTypeAggregateFunction::doGetName() const { - std::stringstream stream; + WriteBufferFromOwnString stream; stream << "AggregateFunction(" << function->getName(); if (!parameters.empty()) { - stream << "("; + stream << '('; for (size_t i = 0; i < parameters.size(); ++i) { if (i) stream << ", "; stream << applyVisitor(DB::FieldVisitorToString(), parameters[i]); } - stream << ")"; + stream << ')'; } for (const auto & argument_type : argument_types) stream << ", " << argument_type->getName(); - stream << ")"; + stream << ')'; return stream.str(); } @@ -362,8 +364,11 @@ static DataTypePtr create(const ASTPtr & arguments) { const auto * literal = parameters[i]->as(); if (!literal) - throw Exception("Parameters to aggregate functions must be literals", - ErrorCodes::PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS); + throw Exception( + ErrorCodes::PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS, + "Parameters to aggregate functions must be literals. " + "Got parameter '{}' for function '{}'", + parameters[i]->formatForErrorMessage(), function_name); params_row[i] = literal->value; } diff --git a/src/DataTypes/DataTypeCustomSimpleAggregateFunction.cpp b/src/DataTypes/DataTypeCustomSimpleAggregateFunction.cpp index bf22845a5f6..2ddce184cce 100644 --- a/src/DataTypes/DataTypeCustomSimpleAggregateFunction.cpp +++ b/src/DataTypes/DataTypeCustomSimpleAggregateFunction.cpp @@ -82,8 +82,11 @@ static std::pair create(const ASTPtr & argum { const ASTLiteral * lit = parameters[i]->as(); if (!lit) - throw Exception("Parameters to aggregate functions must be literals", - ErrorCodes::PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS); + throw Exception( + ErrorCodes::PARAMETERS_TO_AGGREGATE_FUNCTIONS_MUST_BE_LITERALS, + "Parameters to aggregate functions must be literals. " + "Got parameter '{}' for function '{}'", + parameters[i]->formatForErrorMessage(), function_name); params_row[i] = lit->value; } diff --git a/src/DataTypes/DataTypeDateTime64.h b/src/DataTypes/DataTypeDateTime64.h index b575e9d81c1..249da255eb0 100644 --- a/src/DataTypes/DataTypeDateTime64.h +++ b/src/DataTypes/DataTypeDateTime64.h @@ -45,6 +45,8 @@ public: void deserializeProtobuf(IColumn & column, ProtobufReader & protobuf, bool allow_add_row, bool & row_added) const override; bool equals(const IDataType & rhs) const override; + + bool canBePromoted() const override { return false; } }; /** Tansform-type wrapper for DateTime64, applies given Transform to DateTime64 value or only to a whole part of it. diff --git a/src/DataTypes/DataTypeString.cpp b/src/DataTypes/DataTypeString.cpp index 4a9a6e9ab78..b3dfb874328 100644 --- a/src/DataTypes/DataTypeString.cpp +++ b/src/DataTypes/DataTypeString.cpp @@ -15,6 +15,9 @@ #include #include +#include +#include + #include #include #include @@ -27,6 +30,14 @@ namespace DB { + +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int UNEXPECTED_AST_STRUCTURE; +} + + void DataTypeString::serializeBinary(const Field & field, WriteBuffer & ostr) const { const String & s = get(field); @@ -366,12 +377,25 @@ bool DataTypeString::equals(const IDataType & rhs) const return typeid(rhs) == typeid(*this); } +static DataTypePtr create(const ASTPtr & arguments) +{ + if (arguments) + { + if (arguments->children.size() > 1) + throw Exception("String data type family mustnt have more than one argument - size in characters", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + const auto * argument = arguments->children[0]->as(); + if (!argument || argument->value.getType() != Field::Types::UInt64 || argument->value.get() == 0) + throw Exception("FixedString data type family may have only a number (positive integer) as its argument", ErrorCodes::UNEXPECTED_AST_STRUCTURE); + } + + return std::make_shared(); +} + void registerDataTypeString(DataTypeFactory & factory) { - auto creator = static_cast([] { return DataTypePtr(std::make_shared()); }); - - factory.registerSimpleDataType("String", creator); + factory.registerDataType("String", create); /// These synonyms are added for compatibility. @@ -392,5 +416,4 @@ void registerDataTypeString(DataTypeFactory & factory) factory.registerAlias("LONGBLOB", "String", DataTypeFactory::CaseInsensitive); factory.registerAlias("BYTEA", "String", DataTypeFactory::CaseInsensitive); /// PostgreSQL } - } diff --git a/src/DataTypes/DataTypeUUID.cpp b/src/DataTypes/DataTypeUUID.cpp index 87e306cf477..94a043eb472 100644 --- a/src/DataTypes/DataTypeUUID.cpp +++ b/src/DataTypes/DataTypeUUID.cpp @@ -16,13 +16,18 @@ void DataTypeUUID::serializeText(const IColumn & column, size_t row_num, WriteBu writeText(UUID(assert_cast(column).getData()[row_num]), ostr); } -void DataTypeUUID::deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings &) const +void DataTypeUUID::deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const { UUID x; readText(x, istr); assert_cast(column).getData().push_back(x); } +void DataTypeUUID::deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings & settings) const +{ + deserializeText(column, istr, settings); +} + void DataTypeUUID::serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const { serializeText(column, row_num, ostr, settings); diff --git a/src/DataTypes/DataTypeUUID.h b/src/DataTypes/DataTypeUUID.h index 75e634bc625..e9f1d22325b 100644 --- a/src/DataTypes/DataTypeUUID.h +++ b/src/DataTypes/DataTypeUUID.h @@ -17,6 +17,7 @@ public: bool equals(const IDataType & rhs) const override; void serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override; + void deserializeText(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override; void serializeTextEscaped(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override; void deserializeTextEscaped(IColumn & column, ReadBuffer & istr, const FormatSettings &) const override; void serializeTextQuoted(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings &) const override; @@ -30,6 +31,8 @@ public: bool canBeUsedInBitOperations() const override { return true; } bool canBeInsideNullable() const override { return true; } + + bool canBePromoted() const override { return false; } }; } diff --git a/src/DataTypes/DataTypesNumber.cpp b/src/DataTypes/DataTypesNumber.cpp index 18b819c4aa6..7cc443d4067 100644 --- a/src/DataTypes/DataTypesNumber.cpp +++ b/src/DataTypes/DataTypesNumber.cpp @@ -2,9 +2,38 @@ #include +#include +#include + + namespace DB { +namespace ErrorCodes +{ + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; +} + +template +static DataTypePtr createNumericDataType(const ASTPtr & arguments) +{ + if (arguments) + { + if (std::is_integral_v) + { + if (arguments->children.size() > 1) + throw Exception(String(TypeName::get()) + " data type family must not have more than one argument - display width", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + } + else + { + if (arguments->children.size() > 2) + throw Exception(String(TypeName::get()) + " data type family must not have more than two arguments - total number of digits and number of digits following the decimal point", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + } + } + return std::make_shared>(); +} + + void registerDataTypeNumbers(DataTypeFactory & factory) { factory.registerSimpleDataType("UInt8", [] { return DataTypePtr(std::make_shared()); }); @@ -12,13 +41,12 @@ void registerDataTypeNumbers(DataTypeFactory & factory) factory.registerSimpleDataType("UInt32", [] { return DataTypePtr(std::make_shared()); }); factory.registerSimpleDataType("UInt64", [] { return DataTypePtr(std::make_shared()); }); - factory.registerSimpleDataType("Int8", [] { return DataTypePtr(std::make_shared()); }); - factory.registerSimpleDataType("Int16", [] { return DataTypePtr(std::make_shared()); }); - factory.registerSimpleDataType("Int32", [] { return DataTypePtr(std::make_shared()); }); - factory.registerSimpleDataType("Int64", [] { return DataTypePtr(std::make_shared()); }); - - factory.registerSimpleDataType("Float32", [] { return DataTypePtr(std::make_shared()); }); - factory.registerSimpleDataType("Float64", [] { return DataTypePtr(std::make_shared()); }); + factory.registerDataType("Int8", createNumericDataType); + factory.registerDataType("Int16", createNumericDataType); + factory.registerDataType("Int32", createNumericDataType); + factory.registerDataType("Int64", createNumericDataType); + factory.registerDataType("Float32", createNumericDataType); + factory.registerDataType("Float64", createNumericDataType); /// These synonyms are added for compatibility. diff --git a/src/DataTypes/tests/gtest_data_type_get_common_type.cpp b/src/DataTypes/tests/gtest_data_type_get_common_type.cpp index fd511bfbbb4..bd13de79ef6 100644 --- a/src/DataTypes/tests/gtest_data_type_get_common_type.cpp +++ b/src/DataTypes/tests/gtest_data_type_get_common_type.cpp @@ -14,11 +14,6 @@ static bool operator==(const IDataType & left, const IDataType & right) return left.equals(right); } -std::ostream & operator<<(std::ostream & ostr, const IDataType & dt) -{ - return ostr << dt.getName(); -} - } using namespace DB; diff --git a/src/Databases/DatabaseMemory.cpp b/src/Databases/DatabaseMemory.cpp index 52b1f889943..cd559172197 100644 --- a/src/Databases/DatabaseMemory.cpp +++ b/src/Databases/DatabaseMemory.cpp @@ -68,7 +68,7 @@ ASTPtr DatabaseMemory::getCreateTableQueryImpl(const String & table_name, const { std::lock_guard lock{mutex}; auto it = create_queries.find(table_name); - if (it == create_queries.end()) + if (it == create_queries.end() || !it->second) { if (throw_on_error) throw Exception("There is no metadata of table " + table_name + " in database " + database_name, ErrorCodes::UNKNOWN_TABLE); diff --git a/src/Databases/DatabaseOnDisk.cpp b/src/Databases/DatabaseOnDisk.cpp index 1b542c7a1ff..364c9d50c48 100644 --- a/src/Databases/DatabaseOnDisk.cpp +++ b/src/Databases/DatabaseOnDisk.cpp @@ -122,7 +122,12 @@ String getObjectDefinitionFromCreateQuery(const ASTPtr & query) return statement_stream.str(); } -DatabaseOnDisk::DatabaseOnDisk(const String & name, const String & metadata_path_, const String & data_path_, const String & logger, const Context & context) +DatabaseOnDisk::DatabaseOnDisk( + const String & name, + const String & metadata_path_, + const String & data_path_, + const String & logger, + const Context & context) : DatabaseWithOwnTablesBase(name, logger, context) , metadata_path(metadata_path_) , data_path(data_path_) @@ -154,7 +159,6 @@ void DatabaseOnDisk::createTable( /// A race condition would be possible if a table with the same name is simultaneously created using CREATE and using ATTACH. /// But there is protection from it - see using DDLGuard in InterpreterCreateQuery. - if (isDictionaryExist(table_name)) throw Exception("Dictionary " + backQuote(getDatabaseName()) + "." + backQuote(table_name) + " already exists.", ErrorCodes::DICTIONARY_ALREADY_EXISTS); @@ -294,7 +298,7 @@ void DatabaseOnDisk::renameTable( { attachTable(table_name, table, table_data_relative_path); /// Better diagnostics. - throw Exception{Exception::CreateFromPoco, e}; + throw Exception{Exception::CreateFromPocoTag{}, e}; } /// Now table data are moved to new database, so we must add metadata and attach table to new database diff --git a/src/Databases/DatabaseOrdinary.cpp b/src/Databases/DatabaseOrdinary.cpp index 09f71f5af4a..b31752ad1b3 100644 --- a/src/Databases/DatabaseOrdinary.cpp +++ b/src/Databases/DatabaseOrdinary.cpp @@ -246,7 +246,7 @@ void DatabaseOrdinary::alterTable( auto & ast_create_query = ast->as(); ASTPtr new_columns = InterpreterCreateQuery::formatColumns(metadata.columns); - ASTPtr new_indices = InterpreterCreateQuery::formatIndices(metadata.indices); + ASTPtr new_indices = InterpreterCreateQuery::formatIndices(metadata.secondary_indices); ASTPtr new_constraints = InterpreterCreateQuery::formatConstraints(metadata.constraints); ast_create_query.columns_list->replace(ast_create_query.columns_list->columns, new_columns); diff --git a/src/Databases/IDatabase.h b/src/Databases/IDatabase.h index 3d108cfd91c..b9a7a907f73 100644 --- a/src/Databases/IDatabase.h +++ b/src/Databases/IDatabase.h @@ -40,6 +40,10 @@ public: virtual bool isValid() const = 0; virtual const String & name() const = 0; + + /// This method can return nullptr if it's Lazy database + /// (a database with support for lazy tables loading + /// - it maintains a list of tables but tables are loaded lazily). virtual const StoragePtr & table() const = 0; virtual ~IDatabaseTablesIterator() = default; diff --git a/src/Dictionaries/CMakeLists.txt b/src/Dictionaries/CMakeLists.txt index 4471b093add..0eb3c5f44d6 100644 --- a/src/Dictionaries/CMakeLists.txt +++ b/src/Dictionaries/CMakeLists.txt @@ -21,6 +21,10 @@ target_link_libraries(clickhouse_dictionaries string_utils ) +if(USE_CASSANDRA) + target_include_directories(clickhouse_dictionaries SYSTEM PRIVATE ${CASSANDRA_INCLUDE_DIR}) +endif() + add_subdirectory(Embedded) target_include_directories(clickhouse_dictionaries SYSTEM PRIVATE ${SPARSEHASH_INCLUDE_DIR}) diff --git a/src/Dictionaries/CassandraBlockInputStream.cpp b/src/Dictionaries/CassandraBlockInputStream.cpp new file mode 100644 index 00000000000..4f6a62a0eea --- /dev/null +++ b/src/Dictionaries/CassandraBlockInputStream.cpp @@ -0,0 +1,274 @@ +#if !defined(ARCADIA_BUILD) +#include +#endif + +#if USE_CASSANDRA + +#include +#include +#include +#include +#include +#include +#include "CassandraBlockInputStream.h" + + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int TYPE_MISMATCH; +} + +CassandraBlockInputStream::CassandraBlockInputStream( + const CassSessionShared & session_, + const String & query_str, + const Block & sample_block, + size_t max_block_size_) + : session(session_) + , statement(query_str.c_str(), /*parameters count*/ 0) + , max_block_size(max_block_size_) + , has_more_pages(cass_true) +{ + description.init(sample_block); + cassandraCheck(cass_statement_set_paging_size(statement, max_block_size)); +} + +void CassandraBlockInputStream::insertValue(IColumn & column, ValueType type, const CassValue * cass_value) +{ + switch (type) + { + case ValueType::vtUInt8: + { + cass_int8_t value; + cass_value_get_int8(cass_value, &value); + assert_cast(column).insertValue(static_cast(value)); + break; + } + case ValueType::vtUInt16: + { + cass_int16_t value; + cass_value_get_int16(cass_value, &value); + assert_cast(column).insertValue(static_cast(value)); + break; + } + case ValueType::vtUInt32: + { + cass_int32_t value; + cass_value_get_int32(cass_value, &value); + assert_cast(column).insertValue(static_cast(value)); + break; + } + case ValueType::vtUInt64: + { + cass_int64_t value; + cass_value_get_int64(cass_value, &value); + assert_cast(column).insertValue(static_cast(value)); + break; + } + case ValueType::vtInt8: + { + cass_int8_t value; + cass_value_get_int8(cass_value, &value); + assert_cast(column).insertValue(value); + break; + } + case ValueType::vtInt16: + { + cass_int16_t value; + cass_value_get_int16(cass_value, &value); + assert_cast(column).insertValue(value); + break; + } + case ValueType::vtInt32: + { + cass_int32_t value; + cass_value_get_int32(cass_value, &value); + assert_cast(column).insertValue(value); + break; + } + case ValueType::vtInt64: + { + cass_int64_t value; + cass_value_get_int64(cass_value, &value); + assert_cast(column).insertValue(value); + break; + } + case ValueType::vtFloat32: + { + cass_float_t value; + cass_value_get_float(cass_value, &value); + assert_cast(column).insertValue(value); + break; + } + case ValueType::vtFloat64: + { + cass_double_t value; + cass_value_get_double(cass_value, &value); + assert_cast(column).insertValue(value); + break; + } + case ValueType::vtString: + { + const char * value = nullptr; + size_t value_length; + cass_value_get_string(cass_value, &value, &value_length); + assert_cast(column).insertData(value, value_length); + break; + } + case ValueType::vtDate: + { + cass_uint32_t value; + cass_value_get_uint32(cass_value, &value); + assert_cast(column).insertValue(static_cast(value)); + break; + } + case ValueType::vtDateTime: + { + cass_int64_t value; + cass_value_get_int64(cass_value, &value); + assert_cast(column).insertValue(static_cast(value / 1000)); + break; + } + case ValueType::vtUUID: + { + CassUuid value; + cass_value_get_uuid(cass_value, &value); + std::array uuid_str; + cass_uuid_string(value, uuid_str.data()); + assert_cast(column).insert(parse(uuid_str.data(), uuid_str.size())); + break; + } + } +} + +void CassandraBlockInputStream::readPrefix() +{ + result_future = cass_session_execute(*session, statement); +} + +Block CassandraBlockInputStream::readImpl() +{ + if (!has_more_pages) + return {}; + + MutableColumns columns = description.sample_block.cloneEmptyColumns(); + + cassandraWaitAndCheck(result_future); + CassResultPtr result = cass_future_get_result(result_future); + + assert(cass_result_column_count(result) == columns.size()); + + assertTypes(result); + + has_more_pages = cass_result_has_more_pages(result); + if (has_more_pages) + { + cassandraCheck(cass_statement_set_paging_state(statement, result)); + result_future = cass_session_execute(*session, statement); + } + + CassIteratorPtr rows_iter = cass_iterator_from_result(result); /// Points to rows[-1] + while (cass_iterator_next(rows_iter)) + { + const CassRow * row = cass_iterator_get_row(rows_iter); + for (size_t col_idx = 0; col_idx < columns.size(); ++col_idx) + { + const CassValue * val = cass_row_get_column(row, col_idx); + if (cass_value_is_null(val)) + columns[col_idx]->insertDefault(); + else if (description.types[col_idx].second) + { + ColumnNullable & column_nullable = assert_cast(*columns[col_idx]); + insertValue(column_nullable.getNestedColumn(), description.types[col_idx].first, val); + column_nullable.getNullMapData().emplace_back(0); + } + else + insertValue(*columns[col_idx], description.types[col_idx].first, val); + } + } + + assert(cass_result_row_count(result) == columns.front()->size()); + + return description.sample_block.cloneWithColumns(std::move(columns)); +} + +void CassandraBlockInputStream::assertTypes(const CassResultPtr & result) +{ + if (!assert_types) + return; + + size_t column_count = cass_result_column_count(result); + for (size_t i = 0; i < column_count; ++i) + { + CassValueType expected = CASS_VALUE_TYPE_UNKNOWN; + String expected_text; + + /// Cassandra does not support unsigned integers (cass_uint32_t is for Date) + switch (description.types[i].first) + { + case ExternalResultDescription::ValueType::vtInt8: + case ExternalResultDescription::ValueType::vtUInt8: + expected = CASS_VALUE_TYPE_TINY_INT; + expected_text = "tinyint"; + break; + case ExternalResultDescription::ValueType::vtInt16: + case ExternalResultDescription::ValueType::vtUInt16: + expected = CASS_VALUE_TYPE_SMALL_INT; + expected_text = "smallint"; + break; + case ExternalResultDescription::ValueType::vtUInt32: + case ExternalResultDescription::ValueType::vtInt32: + expected = CASS_VALUE_TYPE_INT; + expected_text = "int"; + break; + case ExternalResultDescription::ValueType::vtInt64: + case ExternalResultDescription::ValueType::vtUInt64: + expected = CASS_VALUE_TYPE_BIGINT; + expected_text = "bigint"; + break; + case ExternalResultDescription::ValueType::vtFloat32: + expected = CASS_VALUE_TYPE_FLOAT; + expected_text = "float"; + break; + case ExternalResultDescription::ValueType::vtFloat64: + expected = CASS_VALUE_TYPE_DOUBLE; + expected_text = "double"; + break; + case ExternalResultDescription::ValueType::vtString: + expected = CASS_VALUE_TYPE_TEXT; + expected_text = "text, ascii or varchar"; + break; + case ExternalResultDescription::ValueType::vtDate: + expected = CASS_VALUE_TYPE_DATE; + expected_text = "date"; + break; + case ExternalResultDescription::ValueType::vtDateTime: + expected = CASS_VALUE_TYPE_TIMESTAMP; + expected_text = "timestamp"; + break; + case ExternalResultDescription::ValueType::vtUUID: + expected = CASS_VALUE_TYPE_UUID; + expected_text = "uuid"; + break; + } + + CassValueType got = cass_result_column_type(result, i); + + if (got != expected) + { + if (expected == CASS_VALUE_TYPE_TEXT && (got == CASS_VALUE_TYPE_ASCII || got == CASS_VALUE_TYPE_VARCHAR)) + continue; + + const auto & column_name = description.sample_block.getColumnsWithTypeAndName()[i].name; + throw Exception("Type mismatch for column " + column_name + ": expected Cassandra type " + expected_text, + ErrorCodes::TYPE_MISMATCH); + } + } + + assert_types = false; +} + +} +#endif diff --git a/src/Dictionaries/CassandraBlockInputStream.h b/src/Dictionaries/CassandraBlockInputStream.h new file mode 100644 index 00000000000..3b0e583e3ad --- /dev/null +++ b/src/Dictionaries/CassandraBlockInputStream.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#if USE_CASSANDRA +#include +#include +#include + + +namespace DB +{ + +class CassandraBlockInputStream final : public IBlockInputStream +{ +public: + CassandraBlockInputStream( + const CassSessionShared & session_, + const String & query_str, + const Block & sample_block, + size_t max_block_size); + + String getName() const override { return "Cassandra"; } + + Block getHeader() const override { return description.sample_block.cloneEmpty(); } + + void readPrefix() override; + +private: + using ValueType = ExternalResultDescription::ValueType; + + Block readImpl() override; + static void insertValue(IColumn & column, ValueType type, const CassValue * cass_value); + void assertTypes(const CassResultPtr & result); + + CassSessionShared session; + CassStatementPtr statement; + CassFuturePtr result_future; + const size_t max_block_size; + ExternalResultDescription description; + cass_bool_t has_more_pages; + bool assert_types = true; +}; + +} + +#endif diff --git a/src/Dictionaries/CassandraDictionarySource.cpp b/src/Dictionaries/CassandraDictionarySource.cpp new file mode 100644 index 00000000000..c41f528db91 --- /dev/null +++ b/src/Dictionaries/CassandraDictionarySource.cpp @@ -0,0 +1,211 @@ +#include "CassandraDictionarySource.h" +#include "DictionarySourceFactory.h" +#include "DictionaryStructure.h" + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int SUPPORT_IS_DISABLED; + extern const int NOT_IMPLEMENTED; +} + +void registerDictionarySourceCassandra(DictionarySourceFactory & factory) +{ + auto create_table_source = [=]([[maybe_unused]] const DictionaryStructure & dict_struct, + [[maybe_unused]] const Poco::Util::AbstractConfiguration & config, + [[maybe_unused]] const std::string & config_prefix, + [[maybe_unused]] Block & sample_block, + const Context & /* context */, + bool /*check_config*/) -> DictionarySourcePtr + { +#if USE_CASSANDRA + setupCassandraDriverLibraryLogging(CASS_LOG_INFO); + return std::make_unique(dict_struct, config, config_prefix + ".cassandra", sample_block); +#else + throw Exception{"Dictionary source of type `cassandra` is disabled because ClickHouse was built without cassandra support.", + ErrorCodes::SUPPORT_IS_DISABLED}; +#endif + }; + factory.registerSource("cassandra", create_table_source); +} + +} + +#if USE_CASSANDRA + +#include +#include +#include "CassandraBlockInputStream.h" +#include +#include + +namespace DB +{ +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; + extern const int INVALID_CONFIG_PARAMETER; +} + +CassandraSettings::CassandraSettings( + const Poco::Util::AbstractConfiguration & config, + const String & config_prefix) + : host(config.getString(config_prefix + ".host")) + , port(config.getUInt(config_prefix + ".port", 0)) + , user(config.getString(config_prefix + ".user", "")) + , password(config.getString(config_prefix + ".password", "")) + , db(config.getString(config_prefix + ".keyspace")) + , table(config.getString(config_prefix + ".column_family")) + , allow_filtering(config.getBool(config_prefix + ".allow_filtering", false)) + , partition_key_prefix(config.getUInt(config_prefix + ".partition_key_prefix", 1)) + , max_threads(config.getUInt(config_prefix + ".max_threads", 8)) + , where(config.getString(config_prefix + ".where", "")) +{ + setConsistency(config.getString(config_prefix + ".consistency", "One")); +} + +void CassandraSettings::setConsistency(const String & config_str) +{ + if (config_str == "One") + consistency = CASS_CONSISTENCY_ONE; + else if (config_str == "Two") + consistency = CASS_CONSISTENCY_TWO; + else if (config_str == "Three") + consistency = CASS_CONSISTENCY_THREE; + else if (config_str == "All") + consistency = CASS_CONSISTENCY_ALL; + else if (config_str == "EachQuorum") + consistency = CASS_CONSISTENCY_EACH_QUORUM; + else if (config_str == "Quorum") + consistency = CASS_CONSISTENCY_QUORUM; + else if (config_str == "LocalQuorum") + consistency = CASS_CONSISTENCY_LOCAL_QUORUM; + else if (config_str == "LocalOne") + consistency = CASS_CONSISTENCY_LOCAL_ONE; + else if (config_str == "Serial") + consistency = CASS_CONSISTENCY_SERIAL; + else if (config_str == "LocalSerial") + consistency = CASS_CONSISTENCY_LOCAL_SERIAL; + else /// CASS_CONSISTENCY_ANY is only valid for writes + throw Exception("Unsupported consistency level: " + config_str, ErrorCodes::INVALID_CONFIG_PARAMETER); +} + +static const size_t max_block_size = 8192; + +CassandraDictionarySource::CassandraDictionarySource( + const DictionaryStructure & dict_struct_, + const CassandraSettings & settings_, + const Block & sample_block_) + : log(&Poco::Logger::get("CassandraDictionarySource")) + , dict_struct(dict_struct_) + , settings(settings_) + , sample_block(sample_block_) + , query_builder(dict_struct, settings.db, settings.table, settings.where, IdentifierQuotingStyle::DoubleQuotes) +{ + cassandraCheck(cass_cluster_set_contact_points(cluster, settings.host.c_str())); + if (settings.port) + cassandraCheck(cass_cluster_set_port(cluster, settings.port)); + cass_cluster_set_credentials(cluster, settings.user.c_str(), settings.password.c_str()); + cassandraCheck(cass_cluster_set_consistency(cluster, settings.consistency)); +} + +CassandraDictionarySource::CassandraDictionarySource( + const DictionaryStructure & dict_struct_, + const Poco::Util::AbstractConfiguration & config, + const String & config_prefix, + Block & sample_block_) + : CassandraDictionarySource( + dict_struct_, + CassandraSettings(config, config_prefix), + sample_block_) +{ +} + +void CassandraDictionarySource::maybeAllowFiltering(String & query) const +{ + if (!settings.allow_filtering) + return; + query.pop_back(); /// remove semicolon + query += " ALLOW FILTERING;"; +} + +BlockInputStreamPtr CassandraDictionarySource::loadAll() +{ + String query = query_builder.composeLoadAllQuery(); + maybeAllowFiltering(query); + LOG_INFO(log, "Loading all using query: {}", query); + return std::make_shared(getSession(), query, sample_block, max_block_size); +} + +std::string CassandraDictionarySource::toString() const +{ + return "Cassandra: " + settings.db + '.' + settings.table; +} + +BlockInputStreamPtr CassandraDictionarySource::loadIds(const std::vector & ids) +{ + String query = query_builder.composeLoadIdsQuery(ids); + maybeAllowFiltering(query); + LOG_INFO(log, "Loading ids using query: {}", query); + return std::make_shared(getSession(), query, sample_block, max_block_size); +} + +BlockInputStreamPtr CassandraDictionarySource::loadKeys(const Columns & key_columns, const std::vector & requested_rows) +{ + if (requested_rows.empty()) + throw Exception("No rows requested", ErrorCodes::LOGICAL_ERROR); + + /// TODO is there a better way to load data by complex keys? + std::unordered_map> partitions; + for (const auto & row : requested_rows) + { + SipHash partition_key; + for (size_t i = 0; i < settings.partition_key_prefix; ++i) + key_columns[i]->updateHashWithValue(row, partition_key); + partitions[partition_key.get64()].push_back(row); + } + + BlockInputStreams streams; + for (const auto & partition : partitions) + { + String query = query_builder.composeLoadKeysQuery(key_columns, partition.second, ExternalQueryBuilder::CASSANDRA_SEPARATE_PARTITION_KEY, settings.partition_key_prefix); + maybeAllowFiltering(query); + LOG_INFO(log, "Loading keys for partition hash {} using query: {}", partition.first, query); + streams.push_back(std::make_shared(getSession(), query, sample_block, max_block_size)); + } + + if (streams.size() == 1) + return streams.front(); + + return std::make_shared(streams, nullptr, settings.max_threads); +} + +BlockInputStreamPtr CassandraDictionarySource::loadUpdatedAll() +{ + throw Exception("Method loadUpdatedAll is unsupported for CassandraDictionarySource", ErrorCodes::NOT_IMPLEMENTED); +} + +CassSessionShared CassandraDictionarySource::getSession() +{ + /// Reuse connection if exists, create new one if not + auto session = maybe_session.lock(); + if (session) + return session; + + std::lock_guard lock(connect_mutex); + session = maybe_session.lock(); + if (session) + return session; + + session = std::make_shared(); + CassFuturePtr future = cass_session_connect(*session, cluster); + cassandraWaitAndCheck(future); + maybe_session = session; + return session; +} + +} + +#endif diff --git a/src/Dictionaries/CassandraDictionarySource.h b/src/Dictionaries/CassandraDictionarySource.h new file mode 100644 index 00000000000..c0a4e774d23 --- /dev/null +++ b/src/Dictionaries/CassandraDictionarySource.h @@ -0,0 +1,89 @@ +#pragma once + +#include + +#if USE_CASSANDRA + +#include "DictionaryStructure.h" +#include "IDictionarySource.h" +#include "ExternalQueryBuilder.h" +#include +#include +#include + +namespace DB +{ + +struct CassandraSettings +{ + String host; + UInt16 port; + String user; + String password; + String db; + String table; + + CassConsistency consistency; + bool allow_filtering; + /// TODO get information about key from the driver + size_t partition_key_prefix; + size_t max_threads; + String where; + + CassandraSettings(const Poco::Util::AbstractConfiguration & config, const String & config_prefix); + + void setConsistency(const String & config_str); +}; + +class CassandraDictionarySource final : public IDictionarySource +{ +public: + CassandraDictionarySource( + const DictionaryStructure & dict_struct, + const CassandraSettings & settings_, + const Block & sample_block); + + CassandraDictionarySource( + const DictionaryStructure & dict_struct, + const Poco::Util::AbstractConfiguration & config, + const String & config_prefix, + Block & sample_block); + + BlockInputStreamPtr loadAll() override; + + bool supportsSelectiveLoad() const override { return true; } + + bool isModified() const override { return true; } + + bool hasUpdateField() const override { return false; } + + DictionarySourcePtr clone() const override + { + return std::make_unique(dict_struct, settings, sample_block); + } + + BlockInputStreamPtr loadIds(const std::vector & ids) override; + + BlockInputStreamPtr loadKeys(const Columns & key_columns, const std::vector & requested_rows) override; + + BlockInputStreamPtr loadUpdatedAll() override; + + String toString() const override; + +private: + void maybeAllowFiltering(String & query) const; + CassSessionShared getSession(); + + Poco::Logger * log; + const DictionaryStructure dict_struct; + const CassandraSettings settings; + Block sample_block; + ExternalQueryBuilder query_builder; + + std::mutex connect_mutex; + CassClusterPtr cluster; + CassSessionWeak maybe_session; +}; +} + +#endif diff --git a/src/Dictionaries/CassandraHelpers.cpp b/src/Dictionaries/CassandraHelpers.cpp new file mode 100644 index 00000000000..6de80a455c7 --- /dev/null +++ b/src/Dictionaries/CassandraHelpers.cpp @@ -0,0 +1,68 @@ +#include + +#if USE_CASSANDRA +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ +extern const int CASSANDRA_INTERNAL_ERROR; +} + +void cassandraCheck(CassError code) +{ + if (code != CASS_OK) + throw Exception("Cassandra driver error " + std::to_string(code) + ": " + cass_error_desc(code), + ErrorCodes::CASSANDRA_INTERNAL_ERROR); +} + + +void cassandraWaitAndCheck(CassFuturePtr & future) +{ + auto code = cass_future_error_code(future); /// Waits if not ready + if (code == CASS_OK) + return; + + /// `future` owns `message` and will free it on destruction + const char * message; + size_t message_len; + cass_future_error_message(future, &message, & message_len); + std::string full_message = "Cassandra driver error " + std::to_string(code) + ": " + cass_error_desc(code) + ": " + message; + throw Exception(full_message, ErrorCodes::CASSANDRA_INTERNAL_ERROR); +} + +static std::once_flag setup_logging_flag; + +void setupCassandraDriverLibraryLogging(CassLogLevel level) +{ + std::call_once(setup_logging_flag, [level]() + { + Poco::Logger * logger = &Poco::Logger::get("CassandraDriverLibrary"); + cass_log_set_level(level); + if (level != CASS_LOG_DISABLED) + cass_log_set_callback(cassandraLogCallback, logger); + }); +} + +void cassandraLogCallback(const CassLogMessage * message, void * data) +{ + Poco::Logger * logger = static_cast(data); + if (message->severity == CASS_LOG_CRITICAL || message->severity == CASS_LOG_ERROR) + LOG_ERROR(logger, message->message); + else if (message->severity == CASS_LOG_WARN) + LOG_WARNING(logger, message->message); + else if (message->severity == CASS_LOG_INFO) + LOG_INFO(logger, message->message); + else if (message->severity == CASS_LOG_DEBUG) + LOG_DEBUG(logger, message->message); + else if (message->severity == CASS_LOG_TRACE) + LOG_TRACE(logger, message->message); +} + +} + +#endif diff --git a/src/Dictionaries/CassandraHelpers.h b/src/Dictionaries/CassandraHelpers.h new file mode 100644 index 00000000000..8a00e372c96 --- /dev/null +++ b/src/Dictionaries/CassandraHelpers.h @@ -0,0 +1,84 @@ +#pragma once + +#if !defined(ARCADIA_BUILD) +#include +#endif + +#if USE_CASSANDRA +#include // Y_IGNORE +#include +#include + +namespace DB +{ + +namespace Cassandra +{ + +template +CassT * defaultCtor() { return nullptr; } + +/// RAII wrapper for raw pointers to objects from cassandra driver library +template> +class ObjectHolder +{ + CassT * ptr = nullptr; +public: + template + ObjectHolder(Args &&... args) : ptr(Ctor(std::forward(args)...)) {} + ObjectHolder(CassT * ptr_) : ptr(ptr_) {} + + ObjectHolder(const ObjectHolder &) = delete; + ObjectHolder & operator = (const ObjectHolder &) = delete; + + ObjectHolder(ObjectHolder && rhs) noexcept : ptr(rhs.ptr) { rhs.ptr = nullptr; } + ObjectHolder & operator = (ObjectHolder && rhs) noexcept + { + if (ptr) + Dtor(ptr); + ptr = rhs.ptr; + rhs.ptr = nullptr; + return *this; + } + + ~ObjectHolder() + { + if (ptr) + Dtor(ptr); + } + + /// For implicit conversion when passing object to driver library functions + operator CassT * () { return ptr; } + operator const CassT * () const { return ptr; } +}; + +} + +/// These object are created on pointer construction +using CassClusterPtr = Cassandra::ObjectHolder; +using CassStatementPtr = Cassandra::ObjectHolder; +using CassSessionPtr = Cassandra::ObjectHolder; + +/// Share connections between streams. Executing statements in one session object is thread-safe +using CassSessionShared = std::shared_ptr; +using CassSessionWeak = std::weak_ptr; + +/// The following objects are created inside Cassandra driver library, +/// but must be freed by user code +using CassFuturePtr = Cassandra::ObjectHolder; +using CassResultPtr = Cassandra::ObjectHolder; +using CassIteratorPtr = Cassandra::ObjectHolder; + +/// Checks return code, throws exception on error +void cassandraCheck(CassError code); +void cassandraWaitAndCheck(CassFuturePtr & future); + +/// By default driver library prints logs to stderr. +/// It should be redirected (or, at least, disabled) before calling other functions from the library. +void setupCassandraDriverLibraryLogging(CassLogLevel level); + +void cassandraLogCallback(const CassLogMessage * message, void * data); + +} + +#endif diff --git a/src/Dictionaries/ExternalQueryBuilder.cpp b/src/Dictionaries/ExternalQueryBuilder.cpp index 529fb3d60fa..e64f04d28f2 100644 --- a/src/Dictionaries/ExternalQueryBuilder.cpp +++ b/src/Dictionaries/ExternalQueryBuilder.cpp @@ -63,6 +63,13 @@ void ExternalQueryBuilder::writeQuoted(const std::string & s, WriteBuffer & out) std::string ExternalQueryBuilder::composeLoadAllQuery() const { WriteBufferFromOwnString out; + composeLoadAllQuery(out); + writeChar(';', out); + return out.str(); +} + +void ExternalQueryBuilder::composeLoadAllQuery(WriteBuffer & out) const +{ writeString("SELECT ", out); if (dict_struct.id) @@ -149,24 +156,26 @@ std::string ExternalQueryBuilder::composeLoadAllQuery() const writeString(" WHERE ", out); writeString(where, out); } - - writeChar(';', out); - - return out.str(); } std::string ExternalQueryBuilder::composeUpdateQuery(const std::string & update_field, const std::string & time_point) const { - std::string out = composeLoadAllQuery(); - std::string update_query; + WriteBufferFromOwnString out; + composeLoadAllQuery(out); if (!where.empty()) - update_query = " AND " + update_field + " >= '" + time_point + "'"; + writeString(" AND ", out); else - update_query = " WHERE " + update_field + " >= '" + time_point + "'"; + writeString(" WHERE ", out); - return out.insert(out.size() - 1, update_query); /// This is done to insert "update_query" before "out"'s semicolon + writeQuoted(update_field, out); + writeString(" >= '", out); + writeString(time_point, out); + writeChar('\'', out); + + writeChar(';', out); + return out.str(); } @@ -241,7 +250,7 @@ std::string ExternalQueryBuilder::composeLoadIdsQuery(const std::vector std::string -ExternalQueryBuilder::composeLoadKeysQuery(const Columns & key_columns, const std::vector & requested_rows, LoadKeysMethod method) +ExternalQueryBuilder::composeLoadKeysQuery(const Columns & key_columns, const std::vector & requested_rows, LoadKeysMethod method, size_t partition_key_prefix) { if (!dict_struct.key) throw Exception{"Composite key required for method", ErrorCodes::UNSUPPORTED_METHOD}; @@ -284,9 +293,13 @@ ExternalQueryBuilder::composeLoadKeysQuery(const Columns & key_columns, const st if (!where.empty()) { - writeString("(", out); + if (method != CASSANDRA_SEPARATE_PARTITION_KEY) + writeString("(", out); writeString(where, out); - writeString(") AND (", out); + if (method != CASSANDRA_SEPARATE_PARTITION_KEY) + writeString(") AND (", out); + else + writeString(" AND ", out); } if (method == AND_OR_CHAIN) @@ -298,28 +311,33 @@ ExternalQueryBuilder::composeLoadKeysQuery(const Columns & key_columns, const st writeString(" OR ", out); first = false; - composeKeyCondition(key_columns, row, out); + + writeString("(", out); + composeKeyCondition(key_columns, row, out, 0, key_columns.size()); + writeString(")", out); } } - else /* if (method == IN_WITH_TUPLES) */ + else if (method == IN_WITH_TUPLES) { - writeString(composeKeyTupleDefinition(), out); - writeString(" IN (", out); - - first = true; - for (const auto row : requested_rows) - { - if (!first) - writeString(", ", out); - - first = false; - composeKeyTuple(key_columns, row, out); - } - - writeString(")", out); + composeInWithTuples(key_columns, requested_rows, out, 0, key_columns.size()); + } + else /* if (method == CASSANDRA_SEPARATE_PARTITION_KEY) */ + { + /// CQL does not allow using OR conditions + /// and does not allow using multi-column IN expressions with partition key columns. + /// So we have to use multiple queries with conditions like + /// (partition_key_1 = val1 AND partition_key_2 = val2 ...) AND (clustering_key_1, ...) IN ((val3, ...), ...) + /// for each partition key. + /// `partition_key_prefix` is a number of columns from partition key. + /// All `requested_rows` must have the same values of partition key. + composeKeyCondition(key_columns, requested_rows.at(0), out, 0, partition_key_prefix); + if (partition_key_prefix && partition_key_prefix < key_columns.size()) + writeString(" AND ", out); + if (partition_key_prefix < key_columns.size()) + composeInWithTuples(key_columns, requested_rows, out, partition_key_prefix, key_columns.size()); } - if (!where.empty()) + if (!where.empty() && method != CASSANDRA_SEPARATE_PARTITION_KEY) { writeString(")", out); } @@ -330,13 +348,11 @@ ExternalQueryBuilder::composeLoadKeysQuery(const Columns & key_columns, const st } -void ExternalQueryBuilder::composeKeyCondition(const Columns & key_columns, const size_t row, WriteBuffer & out) const +void ExternalQueryBuilder::composeKeyCondition(const Columns & key_columns, const size_t row, WriteBuffer & out, + size_t beg, size_t end) const { - writeString("(", out); - - const auto keys_size = key_columns.size(); auto first = true; - for (const auto i : ext::range(0, keys_size)) + for (const auto i : ext::range(beg, end)) { if (!first) writeString(" AND ", out); @@ -346,45 +362,60 @@ void ExternalQueryBuilder::composeKeyCondition(const Columns & key_columns, cons const auto & key_description = (*dict_struct.key)[i]; /// key_i=value_i - writeString(key_description.name, out); + writeQuoted(key_description.name, out); writeString("=", out); key_description.type->serializeAsTextQuoted(*key_columns[i], row, out, format_settings); } +} + + +void ExternalQueryBuilder::composeInWithTuples(const Columns & key_columns, const std::vector & requested_rows, + WriteBuffer & out, size_t beg, size_t end) +{ + composeKeyTupleDefinition(out, beg, end); + writeString(" IN (", out); + + bool first = true; + for (const auto row : requested_rows) + { + if (!first) + writeString(", ", out); + + first = false; + composeKeyTuple(key_columns, row, out, beg, end); + } writeString(")", out); } -std::string ExternalQueryBuilder::composeKeyTupleDefinition() const +void ExternalQueryBuilder::composeKeyTupleDefinition(WriteBuffer & out, size_t beg, size_t end) const { if (!dict_struct.key) throw Exception{"Composite key required for method", ErrorCodes::UNSUPPORTED_METHOD}; - std::string result{"("}; + writeChar('(', out); auto first = true; - for (const auto & key : *dict_struct.key) + for (const auto i : ext::range(beg, end)) { if (!first) - result += ", "; + writeString(", ", out); first = false; - result += key.name; + writeQuoted((*dict_struct.key)[i].name, out); } - result += ")"; - - return result; + writeChar(')', out); } -void ExternalQueryBuilder::composeKeyTuple(const Columns & key_columns, const size_t row, WriteBuffer & out) const +void ExternalQueryBuilder::composeKeyTuple(const Columns & key_columns, const size_t row, WriteBuffer & out, size_t beg, size_t end) const { writeString("(", out); - const auto keys_size = key_columns.size(); auto first = true; - for (const auto i : ext::range(0, keys_size)) + for (const auto i : ext::range(beg, end)) { if (!first) writeString(", ", out); diff --git a/src/Dictionaries/ExternalQueryBuilder.h b/src/Dictionaries/ExternalQueryBuilder.h index 93e10f2d6b0..3011efbc895 100644 --- a/src/Dictionaries/ExternalQueryBuilder.h +++ b/src/Dictionaries/ExternalQueryBuilder.h @@ -42,30 +42,39 @@ struct ExternalQueryBuilder std::string composeLoadIdsQuery(const std::vector & ids); /** Generate a query to load data by set of composite keys. - * There are two methods of specification of composite keys in WHERE: + * There are three methods of specification of composite keys in WHERE: * 1. (x = c11 AND y = c12) OR (x = c21 AND y = c22) ... * 2. (x, y) IN ((c11, c12), (c21, c22), ...) + * 3. (x = c1 AND (y, z) IN ((c2, c3), ...)) */ enum LoadKeysMethod { AND_OR_CHAIN, IN_WITH_TUPLES, + CASSANDRA_SEPARATE_PARTITION_KEY, }; - std::string composeLoadKeysQuery(const Columns & key_columns, const std::vector & requested_rows, LoadKeysMethod method); + std::string composeLoadKeysQuery(const Columns & key_columns, const std::vector & requested_rows, LoadKeysMethod method, size_t partition_key_prefix = 0); private: const FormatSettings format_settings; + void composeLoadAllQuery(WriteBuffer & out) const; + + /// In the following methods `beg` and `end` specifies which columns to write in expression + /// Expression in form (x = c1 AND y = c2 ...) - void composeKeyCondition(const Columns & key_columns, const size_t row, WriteBuffer & out) const; + void composeKeyCondition(const Columns & key_columns, const size_t row, WriteBuffer & out, size_t beg, size_t end) const; + + /// Expression in form (x, y, ...) IN ((c1, c2, ...), ...) + void composeInWithTuples(const Columns & key_columns, const std::vector & requested_rows, WriteBuffer & out, size_t beg, size_t end); /// Expression in form (x, y, ...) - std::string composeKeyTupleDefinition() const; + void composeKeyTupleDefinition(WriteBuffer & out, size_t beg, size_t end) const; /// Expression in form (c1, c2, ...) - void composeKeyTuple(const Columns & key_columns, const size_t row, WriteBuffer & out) const; + void composeKeyTuple(const Columns & key_columns, const size_t row, WriteBuffer & out, size_t beg, size_t end) const; /// Write string with specified quoting style. void writeQuoted(const std::string & s, WriteBuffer & out) const; diff --git a/src/Dictionaries/registerDictionaries.cpp b/src/Dictionaries/registerDictionaries.cpp index ad6adbc86fb..8b2c984df6a 100644 --- a/src/Dictionaries/registerDictionaries.cpp +++ b/src/Dictionaries/registerDictionaries.cpp @@ -13,6 +13,7 @@ void registerDictionaries() registerDictionarySourceClickHouse(source_factory); registerDictionarySourceMongoDB(source_factory); registerDictionarySourceRedis(source_factory); + registerDictionarySourceCassandra(source_factory); registerDictionarySourceXDBC(source_factory); registerDictionarySourceJDBC(source_factory); registerDictionarySourceExecutable(source_factory); diff --git a/src/Dictionaries/registerDictionaries.h b/src/Dictionaries/registerDictionaries.h index a3a4a175d41..37ba51a9ae3 100644 --- a/src/Dictionaries/registerDictionaries.h +++ b/src/Dictionaries/registerDictionaries.h @@ -9,6 +9,7 @@ void registerDictionarySourceFile(DictionarySourceFactory & source_factory); void registerDictionarySourceMysql(DictionarySourceFactory & source_factory); void registerDictionarySourceClickHouse(DictionarySourceFactory & source_factory); void registerDictionarySourceMongoDB(DictionarySourceFactory & source_factory); +void registerDictionarySourceCassandra(DictionarySourceFactory & source_factory); void registerDictionarySourceRedis(DictionarySourceFactory & source_factory); void registerDictionarySourceXDBC(DictionarySourceFactory & source_factory); void registerDictionarySourceJDBC(DictionarySourceFactory & source_factory); diff --git a/src/Dictionaries/ya.make b/src/Dictionaries/ya.make index 12983b9527a..3de623a9a8b 100644 --- a/src/Dictionaries/ya.make +++ b/src/Dictionaries/ya.make @@ -17,6 +17,9 @@ SRCS( CacheDictionary_generate1.cpp CacheDictionary_generate2.cpp CacheDictionary_generate3.cpp + CassandraBlockInputStream.cpp + CassandraDictionarySource.cpp + CassandraHelpers.cpp ClickHouseDictionarySource.cpp ComplexKeyCacheDictionary.cpp ComplexKeyCacheDictionary_createAttributeWithType.cpp diff --git a/src/Formats/FormatFactory.cpp b/src/Formats/FormatFactory.cpp index 669baace2f5..9182c728600 100644 --- a/src/Formats/FormatFactory.cpp +++ b/src/Formats/FormatFactory.cpp @@ -101,6 +101,7 @@ static FormatSettings getOutputFormatSetting(const Settings & settings, const Co format_settings.csv.crlf_end_of_line = settings.output_format_csv_crlf_end_of_line; format_settings.pretty.max_rows = settings.output_format_pretty_max_rows; format_settings.pretty.max_column_pad_width = settings.output_format_pretty_max_column_pad_width; + format_settings.pretty.max_value_width = settings.output_format_pretty_max_value_width; format_settings.pretty.color = settings.output_format_pretty_color; format_settings.template_settings.resultset_format = settings.format_template_resultset; format_settings.template_settings.row_format = settings.format_template_row; diff --git a/src/Formats/FormatSettings.h b/src/Formats/FormatSettings.h index 5382d0e0b9d..26195c9ae0a 100644 --- a/src/Formats/FormatSettings.h +++ b/src/Formats/FormatSettings.h @@ -42,6 +42,7 @@ struct FormatSettings { UInt64 max_rows = 10000; UInt64 max_column_pad_width = 250; + UInt64 max_value_width = 10000; bool color = true; }; diff --git a/src/Functions/CMakeLists.txt b/src/Functions/CMakeLists.txt index 069a63aa9e1..6e5f984f435 100644 --- a/src/Functions/CMakeLists.txt +++ b/src/Functions/CMakeLists.txt @@ -10,28 +10,29 @@ add_library(clickhouse_functions ${clickhouse_functions_sources}) target_link_libraries(clickhouse_functions PUBLIC - clickhouse_dictionaries - clickhouse_dictionaries_embedded - dbms - consistent-hashing - consistent-hashing-sumbur + ${BASE64_LIBRARY} ${CITYHASH_LIBRARIES} ${FARMHASH_LIBRARIES} - ${METROHASH_LIBRARIES} - murmurhash - ${BASE64_LIBRARY} ${FASTOPS_LIBRARY} + clickhouse_dictionaries + clickhouse_dictionaries_embedded + consistent-hashing + consistent-hashing-sumbur + dbms + metrohash + murmurhash PRIVATE ${ZLIB_LIBRARIES} - ${Boost_FILESYSTEM_LIBRARY} + boost::filesystem + libdivide ) if (OPENSSL_CRYPTO_LIBRARY) target_link_libraries(clickhouse_functions PUBLIC ${OPENSSL_CRYPTO_LIBRARY}) endif() -target_include_directories(clickhouse_functions SYSTEM PRIVATE ${DIVIDE_INCLUDE_DIR} ${METROHASH_INCLUDE_DIR} ${SPARSEHASH_INCLUDE_DIR}) +target_include_directories(clickhouse_functions SYSTEM PRIVATE ${SPARSEHASH_INCLUDE_DIR}) 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. @@ -60,20 +61,14 @@ if(USE_BASE64) target_include_directories(clickhouse_functions SYSTEM PRIVATE ${BASE64_INCLUDE_DIR}) endif() -if(USE_XXHASH) - target_link_libraries(clickhouse_functions PRIVATE ${XXHASH_LIBRARY}) - target_include_directories(clickhouse_functions SYSTEM PRIVATE ${XXHASH_INCLUDE_DIR}) -endif() +target_link_libraries(clickhouse_functions PRIVATE lz4) if (USE_H3) target_link_libraries(clickhouse_functions PRIVATE ${H3_LIBRARY}) target_include_directories(clickhouse_functions SYSTEM PRIVATE ${H3_INCLUDE_DIR}) endif() -if(USE_HYPERSCAN) - target_link_libraries(clickhouse_functions PRIVATE ${HYPERSCAN_LIBRARY}) - target_include_directories(clickhouse_functions SYSTEM PRIVATE ${HYPERSCAN_INCLUDE_DIR}) -endif() +target_link_libraries(clickhouse_functions PRIVATE hyperscan) if(USE_SIMDJSON) target_link_libraries(clickhouse_functions PRIVATE ${SIMDJSON_LIBRARY}) diff --git a/src/Functions/FunctionsComparison.h b/src/Functions/FunctionsComparison.h index 642995974b5..91525f84c14 100644 --- a/src/Functions/FunctionsComparison.h +++ b/src/Functions/FunctionsComparison.h @@ -22,6 +22,7 @@ #include #include +#include #include #include @@ -51,7 +52,6 @@ namespace DB namespace ErrorCodes { - extern const int TOO_LARGE_STRING_SIZE; extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int LOGICAL_ERROR; @@ -812,94 +812,51 @@ private: } } - bool executeDateOrDateTimeOrEnumOrUUIDWithConstString( + bool executeWithConstString( Block & block, size_t result, const IColumn * col_left_untyped, const IColumn * col_right_untyped, - const DataTypePtr & left_type, const DataTypePtr & right_type, bool left_is_num, size_t input_rows_count) + const DataTypePtr & left_type, const DataTypePtr & right_type, size_t input_rows_count) { - /// This is no longer very special case - comparing dates, datetimes, and enumerations with a string constant. - const IColumn * column_string_untyped = !left_is_num ? col_left_untyped : col_right_untyped; - const IColumn * column_number = left_is_num ? col_left_untyped : col_right_untyped; - const IDataType * number_type = left_is_num ? left_type.get() : right_type.get(); + /// To compare something with const string, we cast constant to appropriate type and compare as usual. + /// It is ok to throw exception if value is not convertible. + /// We should deal with possible overflows, e.g. toUInt8(1) = '257' should return false. - WhichDataType which(number_type); + const ColumnConst * left_const = checkAndGetColumnConstStringOrFixedString(col_left_untyped); + const ColumnConst * right_const = checkAndGetColumnConstStringOrFixedString(col_right_untyped); - const bool legal_types = which.isDateOrDateTime() || which.isEnum() || which.isUUID(); - - const auto column_string = checkAndGetColumnConst(column_string_untyped); - if (!column_string || !legal_types) + if (!left_const && !right_const) return false; - StringRef string_value = column_string->getDataAt(0); + const IDataType * type_string = left_const ? left_type.get() : right_type.get(); + const DataTypePtr & type_to_compare = !left_const ? left_type : right_type; - if (which.isDate()) + Field string_value = left_const ? left_const->getField() : right_const->getField(); + Field converted = convertFieldToType(string_value, *type_to_compare, type_string); + + /// If not possible to convert, comparison with =, <, >, <=, >= yields to false and comparison with != yields to true. + if (converted.isNull()) { - DayNum date; - ReadBufferFromMemory in(string_value.data, string_value.size); - readDateText(date, in); - if (!in.eof()) - throw Exception("String is too long for Date: " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE); - - ColumnPtr parsed_const_date_holder = DataTypeDate().createColumnConst(input_rows_count, date); - const ColumnConst * parsed_const_date = assert_cast(parsed_const_date_holder.get()); - executeNumLeftType(block, result, - left_is_num ? col_left_untyped : parsed_const_date, - left_is_num ? parsed_const_date : col_right_untyped); + block.getByPosition(result).column = DataTypeUInt8().createColumnConst(input_rows_count, + std::is_same_v, NotEqualsOp>); } - else if (which.isDateTime()) + else { - time_t date_time; - ReadBufferFromMemory in(string_value.data, string_value.size); - readDateTimeText(date_time, in, dynamic_cast(*number_type).getTimeZone()); - if (!in.eof()) - throw Exception("String is too long for DateTime: " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE); + auto column_converted = type_to_compare->createColumnConst(input_rows_count, converted); - ColumnPtr parsed_const_date_time_holder = DataTypeDateTime().createColumnConst(input_rows_count, UInt64(date_time)); - const ColumnConst * parsed_const_date_time = assert_cast(parsed_const_date_time_holder.get()); - executeNumLeftType(block, result, - left_is_num ? col_left_untyped : parsed_const_date_time, - left_is_num ? parsed_const_date_time : col_right_untyped); + Block tmp_block + { + { left_const ? column_converted : col_left_untyped->getPtr(), type_to_compare, "" }, + { !left_const ? column_converted : col_right_untyped->getPtr(), type_to_compare, "" }, + block.getByPosition(result) + }; + + executeImpl(tmp_block, {0, 1}, 2, input_rows_count); + + block.getByPosition(result).column = std::move(tmp_block.getByPosition(2).column); } - else if (which.isUUID()) - { - UUID uuid; - ReadBufferFromMemory in(string_value.data, string_value.size); - readText(uuid, in); - if (!in.eof()) - throw Exception("String is too long for UUID: " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE); - - ColumnPtr parsed_const_uuid_holder = DataTypeUUID().createColumnConst(input_rows_count, uuid); - const ColumnConst * parsed_const_uuid = assert_cast(parsed_const_uuid_holder.get()); - executeNumLeftType(block, result, - left_is_num ? col_left_untyped : parsed_const_uuid, - left_is_num ? parsed_const_uuid : col_right_untyped); - } - - else if (which.isEnum8()) - executeEnumWithConstString(block, result, column_number, column_string, - number_type, left_is_num, input_rows_count); - else if (which.isEnum16()) - executeEnumWithConstString(block, result, column_number, column_string, - number_type, left_is_num, input_rows_count); return true; } - /// Comparison between DataTypeEnum and string constant containing the name of an enum element - template - void executeEnumWithConstString( - Block & block, const size_t result, const IColumn * column_number, const ColumnConst * column_string, - const IDataType * type_untyped, const bool left_is_num, size_t input_rows_count) - { - const auto type = static_cast(type_untyped); - - const Field x = castToNearestFieldType(type->getValue(column_string->getValue())); - const auto enum_col = type->createColumnConst(input_rows_count, x); - - executeNumLeftType(block, result, - left_is_num ? column_number : enum_col.get(), - left_is_num ? enum_col.get() : column_number); - } - void executeTuple(Block & block, size_t result, const ColumnWithTypeAndName & c0, const ColumnWithTypeAndName & c1, size_t input_rows_count) { @@ -1124,17 +1081,11 @@ public: bool has_date = left.isDate() || right.isDate(); if (!((both_represented_by_number && !has_date) /// Do not allow compare date and number. - || (left.isStringOrFixedString() && right.isStringOrFixedString()) + || (left.isStringOrFixedString() || right.isStringOrFixedString()) /// Everything can be compared with string by conversion. /// You can compare the date, datetime, or datatime64 and an enumeration with a constant string. - || (left.isString() && right.isDateOrDateTime()) - || (left.isDateOrDateTime() && right.isString()) || (left.isDateOrDateTime() && right.isDateOrDateTime() && left.idx == right.idx) /// only date vs date, or datetime vs datetime || (left.isUUID() && right.isUUID()) - || (left.isUUID() && right.isString()) - || (left.isString() && right.isUUID()) || (left.isEnum() && right.isEnum() && arguments[0]->getName() == arguments[1]->getName()) /// only equivalent enum type values can be compared against - || (left.isEnum() && right.isString()) - || (left.isString() && right.isEnum()) || (left_tuple && right_tuple && left_tuple->getElements().size() == right_tuple->getElements().size()) || (arguments[0]->equals(*arguments[1])))) { @@ -1151,7 +1102,8 @@ public: if (left_tuple && right_tuple) { - auto adaptor = FunctionOverloadResolverAdaptor(std::make_unique(FunctionComparison::create(context))); + auto adaptor = FunctionOverloadResolverAdaptor(std::make_unique( + FunctionComparison::create(context))); size_t size = left_tuple->getElements().size(); for (size_t i = 0; i < size; ++i) @@ -1201,6 +1153,9 @@ public: const bool left_is_num = col_left_untyped->isNumeric(); const bool right_is_num = col_right_untyped->isNumeric(); + const bool left_is_string = isStringOrFixedString(which_left); + const bool right_is_string = isStringOrFixedString(which_right); + bool date_and_datetime = (left_type != right_type) && which_left.isDateOrDateTime() && which_right.isDateOrDateTime(); @@ -1226,64 +1181,14 @@ public: { executeTuple(block, result, col_with_type_and_name_left, col_with_type_and_name_right, input_rows_count); } - else if (which_left.idx != which_right.idx - && (which_left.isDateTime64() || which_right.isDateTime64()) - && (which_left.isStringOrFixedString() || which_right.isStringOrFixedString())) + else if (left_is_string && right_is_string && executeString(block, result, col_left_untyped, col_right_untyped)) + { + } + else if (executeWithConstString( + block, result, col_left_untyped, col_right_untyped, + left_type, right_type, + input_rows_count)) { - /** Special case of comparing DateTime64 against a string. - * - * Can't be moved to executeDateOrDateTimeOrEnumOrUUIDWithConstString() - * since DateTime64 is basically a Decimal, but we do similar things, except type inference. - * Outline: - * - Extract string content - * - Parse it as a ColumnDateTime64 value (same type as DateTime64, means same precision) - * - Fabricate a column with type and name - * - Compare left and right comlumns as DateTime64 columns. - */ - - const size_t datetime64_col_index = which_left.isDateTime64() ? 0 : 1; - const size_t string_col_index = which_left.isStringOrFixedString() ? 0 : 1; - - const auto & datetime64_col_with_type_and_name = block.getByPosition(arguments[datetime64_col_index]); - const auto & string_col_with_type_and_name = block.getByPosition(arguments[string_col_index]); - - if (!isColumnConst(*string_col_with_type_and_name.column)) - throw Exception(getName() + ", illegal column type of argument #" + std::to_string(string_col_index) - + " '" + string_col_with_type_and_name.name + "'" - " expected const String or const FixedString," - " got " + string_col_with_type_and_name.type->getName(), - ErrorCodes::ILLEGAL_COLUMN); - - if (datetime64_col_with_type_and_name.column->size() == 0 || string_col_with_type_and_name.column->size() == 0) - { - // For some reason, when both left and right columns are empty (dry run while building a header block) - // executeDecimal() fills result column with bogus value. - block.getByPosition(result).column = ColumnUInt8::create(); - return; - } - - auto parsed_tmp_column_holder = datetime64_col_with_type_and_name.type->createColumn(); - - { - const StringRef string_value = string_col_with_type_and_name.column->getDataAt(0); - ReadBufferFromMemory in(string_value.data, string_value.size); - datetime64_col_with_type_and_name.type->deserializeAsWholeText(*parsed_tmp_column_holder, in, FormatSettings{}); - - if (!in.eof()) - throw Exception(getName() + ": String is too long for " + datetime64_col_with_type_and_name.type->getName() + " : " + string_value.toString(), ErrorCodes::TOO_LARGE_STRING_SIZE); - } - - // It is necessary to wrap tmp column in ColumnConst to avoid overflow when comparing. - // (non-const columns are expected to have same number of rows as every other column in block). - const ColumnWithTypeAndName parsed_tmp_col_with_type_and_name{ - ColumnConst::create(std::move(parsed_tmp_column_holder), 1), - datetime64_col_with_type_and_name.type, - string_col_with_type_and_name.name}; - - executeDecimal(block, result, - which_left.isDateTime64() ? datetime64_col_with_type_and_name : parsed_tmp_col_with_type_and_name, - which_right.isDateTime64() ? datetime64_col_with_type_and_name : parsed_tmp_col_with_type_and_name); - } else if (isColumnedAsDecimal(left_type) || isColumnedAsDecimal(right_type)) { @@ -1294,19 +1199,10 @@ public: executeDecimal(block, result, col_with_type_and_name_left, col_with_type_and_name_right); } - else if (!left_is_num && !right_is_num && executeString(block, result, col_left_untyped, col_right_untyped)) - { - } else if (left_type->equals(*right_type)) { executeGenericIdenticalTypes(block, result, col_left_untyped, col_right_untyped); } - else if (executeDateOrDateTimeOrEnumOrUUIDWithConstString( - block, result, col_left_untyped, col_right_untyped, - left_type, right_type, - left_is_num, input_rows_count)) - { - } else { executeGeneric(block, result, col_with_type_and_name_left, col_with_type_and_name_right); diff --git a/src/Functions/FunctionsConversion.h b/src/Functions/FunctionsConversion.h index 879b885cf66..83417a3229b 100644 --- a/src/Functions/FunctionsConversion.h +++ b/src/Functions/FunctionsConversion.h @@ -722,10 +722,10 @@ struct ConvertThroughParsing parsed = ToDataType::tryReadText(vec_to[i], read_buffer, ToDataType::maxPrecision(), vec_to.getScale()); else parsed = tryParseImpl(vec_to[i], read_buffer, local_time_zone); - - parsed = parsed && isAllRead(read_buffer); } + parsed = parsed && isAllRead(read_buffer); + if (!parsed) vec_to[i] = 0; diff --git a/src/Functions/GeoHash.cpp b/src/Functions/GeoHash.cpp index c6ac9939070..e59cf160ce1 100644 --- a/src/Functions/GeoHash.cpp +++ b/src/Functions/GeoHash.cpp @@ -260,10 +260,10 @@ void geohashDecode(const char * encoded_string, size_t encoded_len, Float64 * lo *latitude = decodeCoordinate(lat_encoded, LAT_MIN, LAT_MAX, singleCoordBitsPrecision(precision, LATITUDE)); } -GeohashesInBoxPreparedArgs geohashesInBoxPrepare(const Float64 longitude_min, - const Float64 latitude_min, - const Float64 longitude_max, - const Float64 latitude_max, +GeohashesInBoxPreparedArgs geohashesInBoxPrepare(Float64 longitude_min, + Float64 latitude_min, + Float64 longitude_max, + Float64 latitude_max, uint8_t precision) { precision = geohashPrecision(precision); @@ -273,6 +273,11 @@ GeohashesInBoxPreparedArgs geohashesInBoxPrepare(const Float64 longitude_min, return {}; } + longitude_min = std::max(longitude_min, LON_MIN); + longitude_max = std::min(longitude_max, LON_MAX); + latitude_min = std::max(latitude_min, LAT_MIN); + latitude_max = std::min(latitude_max, LAT_MAX); + const auto lon_step = getSpan(precision, LONGITUDE); const auto lat_step = getSpan(precision, LATITUDE); diff --git a/src/Functions/PolygonUtils.h b/src/Functions/PolygonUtils.h index 25c2da9915c..6e1b03a47bd 100644 --- a/src/Functions/PolygonUtils.h +++ b/src/Functions/PolygonUtils.h @@ -358,6 +358,9 @@ bool PointInPolygonWithGrid::contains(CoordinateType x, Coordina if (has_empty_bound) return false; + if (std::isnan(x) || std::isnan(y)) + return false; + CoordinateType float_row = (y + y_shift) * y_scale; CoordinateType float_col = (x + x_shift) * x_scale; diff --git a/src/Functions/URL/CMakeLists.txt b/src/Functions/URL/CMakeLists.txt index fabfccae230..21f0adb6594 100644 --- a/src/Functions/URL/CMakeLists.txt +++ b/src/Functions/URL/CMakeLists.txt @@ -9,10 +9,7 @@ if (CMAKE_BUILD_TYPE_UC STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE_UC STREQUAL "RELW endif () # TODO: move Functions/Regexps.h to some lib and use here -if(USE_HYPERSCAN) - target_link_libraries(clickhouse_functions_url PRIVATE ${HYPERSCAN_LIBRARY}) - target_include_directories(clickhouse_functions_url SYSTEM PRIVATE ${HYPERSCAN_INCLUDE_DIR}) -endif() +target_link_libraries(clickhouse_functions_url PRIVATE hyperscan) if (USE_GPERF) # Only for regenerate diff --git a/src/Functions/URL/FunctionsURL.h b/src/Functions/URL/FunctionsURL.h index fa5e9246488..297b62ca256 100644 --- a/src/Functions/URL/FunctionsURL.h +++ b/src/Functions/URL/FunctionsURL.h @@ -21,6 +21,7 @@ namespace DB * queryString * fragment * queryStringAndFragment + * netloc * * Functions, removing parts from URL. * If URL has nothing like, then it is returned unchanged. diff --git a/src/Functions/URL/netloc.cpp b/src/Functions/URL/netloc.cpp new file mode 100644 index 00000000000..d8858c3364a --- /dev/null +++ b/src/Functions/URL/netloc.cpp @@ -0,0 +1,17 @@ +#include +#include +#include "netloc.h" + +namespace DB +{ + +struct NameNetloc { static constexpr auto name = "netloc"; }; +using FunctionNetloc = FunctionStringToString, NameNetloc>; + +void registerFunctionNetloc(FunctionFactory & factory) +{ + factory.registerFunction(); +} + +} + diff --git a/src/Functions/URL/netloc.h b/src/Functions/URL/netloc.h new file mode 100644 index 00000000000..443ef7f9003 --- /dev/null +++ b/src/Functions/URL/netloc.h @@ -0,0 +1,129 @@ +#pragma once + +#include "FunctionsURL.h" +#include +#include "protocol.h" +#include +#include + + +namespace DB +{ + +struct ExtractNetloc +{ + /// We use the same as domain function + static size_t getReserveLengthForElement() { return 15; } + + static inline StringRef getNetworkLocation(const char * data, size_t size) + { + Pos pos = data; + Pos end = data + size; + + if (*pos == '/' && *(pos + 1) == '/') + { + pos += 2; + } + else + { + Pos scheme_end = data + std::min(size, 16UL); + for (++pos; pos < scheme_end; ++pos) + { + if (!isAlphaNumericASCII(*pos)) + { + switch (*pos) + { + case '.': + case '-': + case '+': + break; + case ' ': /// restricted symbols + case '\t': + case '<': + case '>': + case '%': + case '{': + case '}': + case '|': + case '\\': + case '^': + case '~': + case '[': + case ']': + case ';': + case '=': + case '&': + return StringRef{}; + default: + goto exloop; + } + } + } +exloop: if ((scheme_end - pos) > 2 && *pos == ':' && *(pos + 1) == '/' && *(pos + 2) == '/') + pos += 3; + else + pos = data; + } + + bool has_identification = false; + Pos question_mark_pos = end; + Pos slash_pos = end; + auto start_of_host = pos; + for (; pos < end; ++pos) + { + switch (*pos) + { + case '/': + if (has_identification) + return StringRef(start_of_host, pos - start_of_host); + else + slash_pos = pos; + break; + case '?': + if (has_identification) + return StringRef(start_of_host, pos - start_of_host); + else + question_mark_pos = pos; + break; + case '#': + return StringRef(start_of_host, pos - start_of_host); + case '@': /// foo:bar@example.ru + has_identification = true; + break; + case ' ': /// restricted symbols in whole URL + case '\t': + case '<': + case '>': + case '%': + case '{': + case '}': + case '|': + case '\\': + case '^': + case '~': + case '[': + case ']': + case ';': + case '=': + case '&': + return StringRef(start_of_host, std::min(std::min(pos - 1, question_mark_pos), slash_pos) - start_of_host); + } + } + + if (has_identification) + return StringRef(start_of_host, pos - start_of_host); + else + return StringRef(start_of_host, std::min(std::min(pos, question_mark_pos), slash_pos) - start_of_host); + } + + static void execute(Pos data, size_t size, Pos & res_data, size_t & res_size) + { + StringRef host = getNetworkLocation(data, size); + + res_data = host.data; + res_size = host.size; + } +}; + +} + diff --git a/src/Functions/URL/registerFunctionsURL.cpp b/src/Functions/URL/registerFunctionsURL.cpp index 9ba5261f728..f3906c2723e 100644 --- a/src/Functions/URL/registerFunctionsURL.cpp +++ b/src/Functions/URL/registerFunctionsURL.cpp @@ -26,6 +26,7 @@ void registerFunctionCutFragment(FunctionFactory & factory); void registerFunctionCutQueryStringAndFragment(FunctionFactory & factory); void registerFunctionCutURLParameter(FunctionFactory & factory); void registerFunctionDecodeURLComponent(FunctionFactory & factory); +void registerFunctionNetloc(FunctionFactory & factory); void registerFunctionsURL(FunctionFactory & factory) { @@ -52,6 +53,7 @@ void registerFunctionsURL(FunctionFactory & factory) registerFunctionCutQueryStringAndFragment(factory); registerFunctionCutURLParameter(factory); registerFunctionDecodeURLComponent(factory); + registerFunctionNetloc(factory); } } diff --git a/src/Functions/bitBoolMaskAnd.cpp b/src/Functions/bitBoolMaskAnd.cpp index 2c55e39506c..561caf316b2 100644 --- a/src/Functions/bitBoolMaskAnd.cpp +++ b/src/Functions/bitBoolMaskAnd.cpp @@ -7,7 +7,7 @@ namespace DB { namespace ErrorCodes { - extern const int BAD_CAST; + extern const int BAD_ARGUMENTS; } /// Working with UInt8: last bit = can be true, previous = can be false (Like src/Storages/MergeTree/BoolMask.h). @@ -23,8 +23,10 @@ namespace DB template static inline Result apply(A left, B right) { + // Should be a logical error, but this function is callable from SQL. + // Need to investigate this. if constexpr (!std::is_same_v || !std::is_same_v) - throw DB::Exception("It's a bug! Only UInt8 type is supported by __bitBoolMaskAnd.", ErrorCodes::BAD_CAST); + throw DB::Exception("It's a bug! Only UInt8 type is supported by __bitBoolMaskAnd.", ErrorCodes::BAD_ARGUMENTS); return static_cast( ((static_cast(left) & static_cast(right)) & 1) | ((((static_cast(left) >> 1) | (static_cast(right) >> 1)) & 1) << 1)); diff --git a/src/Functions/bitBoolMaskOr.cpp b/src/Functions/bitBoolMaskOr.cpp index 0b439165fca..a23be509f1a 100644 --- a/src/Functions/bitBoolMaskOr.cpp +++ b/src/Functions/bitBoolMaskOr.cpp @@ -7,7 +7,7 @@ namespace DB { namespace ErrorCodes { - extern const int BAD_CAST; + extern const int BAD_ARGUMENTS; } /// Working with UInt8: last bit = can be true, previous = can be false (Like src/Storages/MergeTree/BoolMask.h). @@ -24,7 +24,9 @@ namespace DB static inline Result apply(A left, B right) { if constexpr (!std::is_same_v || !std::is_same_v) - throw DB::Exception("It's a bug! Only UInt8 type is supported by __bitBoolMaskOr.", ErrorCodes::BAD_CAST); + // Should be a logical error, but this function is callable from SQL. + // Need to investigate this. + throw DB::Exception("It's a bug! Only UInt8 type is supported by __bitBoolMaskOr.", ErrorCodes::BAD_ARGUMENTS); return static_cast( ((static_cast(left) | static_cast(right)) & 1) | ((((static_cast(left) >> 1) & (static_cast(right) >> 1)) & 1) << 1)); diff --git a/src/Functions/bitSwapLastTwo.cpp b/src/Functions/bitSwapLastTwo.cpp index d6fa9a39ec3..9d942494258 100644 --- a/src/Functions/bitSwapLastTwo.cpp +++ b/src/Functions/bitSwapLastTwo.cpp @@ -7,7 +7,7 @@ namespace DB namespace ErrorCodes { extern const int LOGICAL_ERROR; - extern const int BAD_CAST; + extern const int BAD_ARGUMENTS; } /// Working with UInt8: last bit = can be true, previous = can be false (Like src/Storages/MergeTree/BoolMask.h). @@ -21,7 +21,9 @@ namespace DB static inline ResultType NO_SANITIZE_UNDEFINED apply(A a) { if constexpr (!std::is_same_v) - throw DB::Exception("It's a bug! Only UInt8 type is supported by __bitSwapLastTwo.", ErrorCodes::BAD_CAST); + // Should be a logical error, but this function is callable from SQL. + // Need to investigate this. + throw DB::Exception("It's a bug! Only UInt8 type is supported by __bitSwapLastTwo.", ErrorCodes::BAD_ARGUMENTS); return static_cast( ((static_cast(a) & 1) << 1) | ((static_cast(a) >> 1) & 1)); } diff --git a/src/Functions/bitWrapperFunc.cpp b/src/Functions/bitWrapperFunc.cpp index 9f7276fbf98..d2d4b45781b 100644 --- a/src/Functions/bitWrapperFunc.cpp +++ b/src/Functions/bitWrapperFunc.cpp @@ -6,7 +6,7 @@ namespace DB { namespace ErrorCodes { - extern const int BAD_CAST; + extern const int BAD_ARGUMENTS; } /// Working with UInt8: last bit = can be true, previous = can be false (Like src/Storages/MergeTree/BoolMask.h). @@ -20,8 +20,10 @@ namespace DB static inline ResultType NO_SANITIZE_UNDEFINED apply(A a) { + // Should be a logical error, but this function is callable from SQL. + // Need to investigate this. if constexpr (!is_integral_v) - throw DB::Exception("It's a bug! Only integer types are supported by __bitWrapperFunc.", ErrorCodes::BAD_CAST); + throw DB::Exception("It's a bug! Only integer types are supported by __bitWrapperFunc.", ErrorCodes::BAD_ARGUMENTS); return a == 0 ? static_cast(0b10) : static_cast(0b1); } diff --git a/src/Functions/config_functions.h.in b/src/Functions/config_functions.h.in index 46664caaa3f..eb96c13c355 100644 --- a/src/Functions/config_functions.h.in +++ b/src/Functions/config_functions.h.in @@ -3,8 +3,6 @@ // .h autogenerated by cmake! #cmakedefine01 USE_BASE64 -#cmakedefine01 USE_XXHASH -#cmakedefine01 USE_HYPERSCAN #cmakedefine01 USE_SIMDJSON #cmakedefine01 USE_RAPIDJSON #cmakedefine01 USE_H3 diff --git a/src/Functions/extractAllGroups.cpp b/src/Functions/extractAllGroups.cpp deleted file mode 100644 index a79efe86356..00000000000 --- a/src/Functions/extractAllGroups.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int ARGUMENT_OUT_OF_BOUND; - extern const int BAD_ARGUMENTS; -} - - -/** Match all groups of given input string with given re, return array of arrays of matches. - * - * SELECT extractAllGroups('abc=111, def=222, ghi=333', '("[^"]+"|\\w+)=("[^"]+"|\\w+)') - * should produce: - * [['abc', '111'], ['def', '222'], ['ghi', '333']] - */ -class FunctionExtractAllGroups : public IFunction -{ -public: - static constexpr auto name = "extractAllGroups"; - static FunctionPtr create(const Context &) { return std::make_shared(); } - - String getName() const override { return name; } - - size_t getNumberOfArguments() const override { return 2; } - - bool useDefaultImplementationForConstants() const override { return false; } - ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; } - - DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override - { - FunctionArgumentDescriptors args{ - {"haystack", isStringOrFixedString, nullptr, "const String or const FixedString"}, - {"needle", isStringOrFixedString, isColumnConst, "const String or const FixedString"}, - }; - validateFunctionArgumentTypes(*this, arguments, args); - - /// Two-dimensional array of strings, each `row` of top array represents matching groups. - return std::make_shared(std::make_shared(std::make_shared())); - } - - void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override - { - const ColumnPtr column_haystack = block.getByPosition(arguments[0]).column; - const ColumnPtr column_needle = block.getByPosition(arguments[1]).column; - - const auto needle = typeid_cast(*column_needle).getValue(); - - if (needle.empty()) - throw Exception(getName() + " length of 'needle' argument must be greater than 0.", ErrorCodes::ARGUMENT_OUT_OF_BOUND); - - const auto regexp = Regexps::get(needle); - const auto & re2 = regexp->getRE2(); - - if (!re2) - throw Exception("There is no groups in regexp: " + needle, ErrorCodes::BAD_ARGUMENTS); - - const size_t groups_count = re2->NumberOfCapturingGroups(); - - if (!groups_count) - throw Exception("There is no groups in regexp: " + needle, ErrorCodes::BAD_ARGUMENTS); - - // Including 0-group, which is the whole regexp. - PODArrayWithStackMemory matched_groups(groups_count + 1); - - ColumnArray::ColumnOffsets::MutablePtr root_offsets_col = ColumnArray::ColumnOffsets::create(); - ColumnArray::ColumnOffsets::MutablePtr nested_offsets_col = ColumnArray::ColumnOffsets::create(); - ColumnString::MutablePtr data_col = ColumnString::create(); - - auto & root_offsets_data = root_offsets_col->getData(); - auto & nested_offsets_data = nested_offsets_col->getData(); - - root_offsets_data.resize(input_rows_count); - ColumnArray::Offset current_root_offset = 0; - ColumnArray::Offset current_nested_offset = 0; - - for (size_t i = 0; i < input_rows_count; ++i) - { - StringRef current_row = column_haystack->getDataAt(i); - - // Extract all non-intersecting matches from haystack except group #0. - const auto * pos = current_row.data; - const auto * end = pos + current_row.size; - while (pos < end - && re2->Match(re2_st::StringPiece(pos, end - pos), - 0, end - pos, re2_st::RE2::UNANCHORED, matched_groups.data(), matched_groups.size())) - { - // 1 is to exclude group #0 which is whole re match. - for (size_t group = 1; group <= groups_count; ++group) - data_col->insertData(matched_groups[group].data(), matched_groups[group].size()); - - pos = matched_groups[0].data() + matched_groups[0].size(); - - current_nested_offset += groups_count; - nested_offsets_data.push_back(current_nested_offset); - - ++current_root_offset; - } - - root_offsets_data[i] = current_root_offset; - } - ColumnArray::MutablePtr nested_array_col = ColumnArray::create(std::move(data_col), std::move(nested_offsets_col)); - ColumnArray::MutablePtr root_array_col = ColumnArray::create(std::move(nested_array_col), std::move(root_offsets_col)); - block.getByPosition(result).column = std::move(root_array_col); - } -}; - -void registerFunctionExtractAllGroups(FunctionFactory & factory) -{ - factory.registerFunction(); -} - -} diff --git a/src/Functions/extractAllGroups.h b/src/Functions/extractAllGroups.h new file mode 100644 index 00000000000..8216a528b2c --- /dev/null +++ b/src/Functions/extractAllGroups.h @@ -0,0 +1,237 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int BAD_ARGUMENTS; +} + + +enum class ExtractAllGroupsResultKind +{ + VERTICAL, + HORIZONTAL +}; + + +/** Match all groups of given input string with given re, return array of arrays of matches. + * + * Depending on `Impl::Kind`, result is either grouped by grop id (Horizontal) or in order of appearance (Vertical): + * + * SELECT extractAllGroupsVertical('abc=111, def=222, ghi=333', '("[^"]+"|\\w+)=("[^"]+"|\\w+)') + * => + * [['abc', '111'], ['def', '222'], ['ghi', '333']] + * + * SELECT extractAllGroupsHorizontal('abc=111, def=222, ghi=333', '("[^"]+"|\\w+)=("[^"]+"|\\w+)') + * => + * [['abc', 'def', 'ghi'], ['111', '222', '333'] +*/ +template +class FunctionExtractAllGroups : public IFunction +{ +public: + static constexpr auto Kind = Impl::Kind; + static constexpr auto name = Impl::Name; + + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override { return name; } + + size_t getNumberOfArguments() const override { return 2; } + + bool useDefaultImplementationForConstants() const override { return true; } + ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; } + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + FunctionArgumentDescriptors args{ + {"haystack", isStringOrFixedString, nullptr, "const String or const FixedString"}, + {"needle", isStringOrFixedString, isColumnConst, "const String or const FixedString"}, + }; + validateFunctionArgumentTypes(*this, arguments, args); + + /// Two-dimensional array of strings, each `row` of top array represents matching groups. + return std::make_shared(std::make_shared(std::make_shared())); + } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + static const auto MAX_GROUPS_COUNT = 128; + + const ColumnPtr column_haystack = block.getByPosition(arguments[0]).column; + const ColumnPtr column_needle = block.getByPosition(arguments[1]).column; + + const auto needle = typeid_cast(*column_needle).getValue(); + + if (needle.empty()) + throw Exception("Length of 'needle' argument must be greater than 0.", ErrorCodes::BAD_ARGUMENTS); + + using StringPiece = typename Regexps::Regexp::StringPieceType; + const auto & regexp = Regexps::get(needle)->getRE2(); + + if (!regexp) + throw Exception("There are no groups in regexp: " + needle, ErrorCodes::BAD_ARGUMENTS); + + const size_t groups_count = regexp->NumberOfCapturingGroups(); + + if (!groups_count) + throw Exception("There are no groups in regexp: " + needle, ErrorCodes::BAD_ARGUMENTS); + + if (groups_count > MAX_GROUPS_COUNT - 1) + throw Exception("Too many groups in regexp: " + std::to_string(groups_count) + + ", max: " + std::to_string(MAX_GROUPS_COUNT - 1), + ErrorCodes::BAD_ARGUMENTS); + + // Including 0-group, which is the whole regexp. + PODArrayWithStackMemory matched_groups(groups_count + 1); + + ColumnArray::ColumnOffsets::MutablePtr root_offsets_col = ColumnArray::ColumnOffsets::create(); + ColumnArray::ColumnOffsets::MutablePtr nested_offsets_col = ColumnArray::ColumnOffsets::create(); + ColumnString::MutablePtr data_col = ColumnString::create(); + + auto & root_offsets_data = root_offsets_col->getData(); + auto & nested_offsets_data = nested_offsets_col->getData(); + + ColumnArray::Offset current_root_offset = 0; + ColumnArray::Offset current_nested_offset = 0; + + if constexpr (Kind == ExtractAllGroupsResultKind::VERTICAL) + { + root_offsets_data.resize(input_rows_count); + for (size_t i = 0; i < input_rows_count; ++i) + { + StringRef current_row = column_haystack->getDataAt(i); + + // Extract all non-intersecting matches from haystack except group #0. + const auto * pos = current_row.data; + const auto * end = pos + current_row.size; + while (pos < end + && regexp->Match({pos, static_cast(end - pos)}, + 0, end - pos, regexp->UNANCHORED, matched_groups.data(), matched_groups.size())) + { + // 1 is to exclude group #0 which is whole re match. + for (size_t group = 1; group <= groups_count; ++group) + data_col->insertData(matched_groups[group].data(), matched_groups[group].size()); + + pos = matched_groups[0].data() + matched_groups[0].size(); + + current_nested_offset += groups_count; + nested_offsets_data.push_back(current_nested_offset); + + ++current_root_offset; + } + + root_offsets_data[i] = current_root_offset; + } + } + else + { + std::vector all_matches; + // number of times RE matched on each row of haystack column. + std::vector number_of_matches_per_row; + + // we expect RE to match multiple times on each row, `* 8` is arbitrary to reduce number of re-allocations. + all_matches.reserve(input_rows_count * groups_count * 8); + number_of_matches_per_row.reserve(input_rows_count); + + for (size_t i = 0; i < input_rows_count; ++i) + { + size_t matches_per_row = 0; + + const auto & current_row = column_haystack->getDataAt(i); + + // Extract all non-intersecting matches from haystack except group #0. + const auto * pos = current_row.data; + const auto * end = pos + current_row.size; + while (pos < end + && regexp->Match({pos, static_cast(end - pos)}, + 0, end - pos, regexp->UNANCHORED, matched_groups.data(), matched_groups.size())) + { + // 1 is to exclude group #0 which is whole re match. + for (size_t group = 1; group <= groups_count; ++group) + all_matches.push_back(matched_groups[group]); + + pos = matched_groups[0].data() + matched_groups[0].size(); + + ++matches_per_row; + } + + number_of_matches_per_row.push_back(matches_per_row); + } + + { + size_t total_matched_groups_string_len = 0; + for (const auto & m : all_matches) + total_matched_groups_string_len += m.length(); + + data_col->reserve(total_matched_groups_string_len); + } + + nested_offsets_col->reserve(matched_groups.size()); + root_offsets_col->reserve(groups_count); + + // Re-arrange `all_matches` from: + // [ + // "ROW 0: 1st group 1st match", + // "ROW 0: 2nd group 1st match", + // ..., + // "ROW 0: 1st group 2nd match", + // "ROW 0: 2nd group 2nd match", + // ..., + // "ROW 1: 1st group 1st match", + // ... + // ] + // + // into column of 2D arrays: + // [ + // /* all matchig groups from ROW 0 of haystack column */ + // ["ROW 0: 1st group 1st match", "ROW 0: 1st group 2nd match", ...], + // ["ROW 0: 2nd group 1st match", "ROW 0: 2nd group 2nd match", ...], + // ... + // ], + // [ + // /* all matchig groups from row 1 of haystack column */ + // ["ROW 1: 1st group 1st match", ...], + // ... + // ] + + size_t row_offset = 0; + for (const auto matches_per_row : number_of_matches_per_row) + { + const size_t next_row_offset = row_offset + matches_per_row * groups_count; + for (size_t group_id = 0; group_id < groups_count; ++group_id) + { + for (size_t i = row_offset + group_id; i < next_row_offset && i < all_matches.size(); i += groups_count) + { + const auto & match = all_matches[i]; + data_col->insertData(match.begin(), match.length()); + } + nested_offsets_col->insertValue(data_col->size()); + } + root_offsets_col->insertValue(nested_offsets_col->size()); + row_offset = next_row_offset; + } + } + + ColumnArray::MutablePtr nested_array_col = ColumnArray::create(std::move(data_col), std::move(nested_offsets_col)); + ColumnArray::MutablePtr root_array_col = ColumnArray::create(std::move(nested_array_col), std::move(root_offsets_col)); + block.getByPosition(result).column = std::move(root_array_col); + } +}; + +} diff --git a/src/Functions/extractAllGroupsHorizontal.cpp b/src/Functions/extractAllGroupsHorizontal.cpp new file mode 100644 index 00000000000..fba7483ba03 --- /dev/null +++ b/src/Functions/extractAllGroupsHorizontal.cpp @@ -0,0 +1,23 @@ +#include +#include + +namespace +{ + +struct HorizontalImpl +{ + static constexpr auto Kind = DB::ExtractAllGroupsResultKind::HORIZONTAL; + static constexpr auto Name = "extractAllGroupsHorizontal"; +}; + +} + +namespace DB +{ + +void registerFunctionExtractAllGroupsHorizontal(FunctionFactory & factory) +{ + factory.registerFunction>(); +} + +} diff --git a/src/Functions/extractAllGroupsVertical.cpp b/src/Functions/extractAllGroupsVertical.cpp new file mode 100644 index 00000000000..9cbd148b016 --- /dev/null +++ b/src/Functions/extractAllGroupsVertical.cpp @@ -0,0 +1,24 @@ +#include +#include + +namespace +{ + +struct VerticalImpl +{ + static constexpr auto Kind = DB::ExtractAllGroupsResultKind::VERTICAL; + static constexpr auto Name = "extractAllGroupsVertical"; +}; + +} + +namespace DB +{ + +void registerFunctionExtractAllGroupsVertical(FunctionFactory & factory) +{ + factory.registerFunction>(); + factory.registerAlias("extractAllGroups", VerticalImpl::Name, FunctionFactory::CaseInsensitive); +} + +} diff --git a/src/Functions/extractGroups.cpp b/src/Functions/extractGroups.cpp index 882147ef664..f24abd2d0ff 100644 --- a/src/Functions/extractGroups.cpp +++ b/src/Functions/extractGroups.cpp @@ -17,7 +17,6 @@ namespace DB namespace ErrorCodes { - extern const int ARGUMENT_OUT_OF_BOUND; extern const int BAD_ARGUMENTS; } @@ -49,7 +48,6 @@ public: }; validateFunctionArgumentTypes(*this, arguments, args); - /// Two-dimensional array of strings, each `row` of top array represents matching groups. return std::make_shared(std::make_shared()); } @@ -61,7 +59,7 @@ public: const auto needle = typeid_cast(*column_needle).getValue(); if (needle.empty()) - throw Exception(getName() + " length of 'needle' argument must be greater than 0.", ErrorCodes::ARGUMENT_OUT_OF_BOUND); + throw Exception(getName() + " length of 'needle' argument must be greater than 0.", ErrorCodes::BAD_ARGUMENTS); const auto regexp = Regexps::get(needle); const auto & re2 = regexp->getRE2(); diff --git a/src/Functions/fuzzBits.cpp b/src/Functions/fuzzBits.cpp new file mode 100644 index 00000000000..8acee9c7bf0 --- /dev/null +++ b/src/Functions/fuzzBits.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace DB +{ +namespace ErrorCodes +{ + extern const int ILLEGAL_TYPE_OF_ARGUMENT; + extern const int ILLEGAL_COLUMN; + extern const int DECIMAL_OVERFLOW; + extern const int ARGUMENT_OUT_OF_BOUND; +} + + +namespace +{ + inline UInt8 getXorMask(UInt64 rand, double prob) + { + UInt8 res = 0; + for (int i = 0; i < 8; ++i) + { + UInt8 rand8 = rand; + rand >>= 8; + res <<= 1; + res |= (rand8 < prob * (1u << 8)); + } + return res; + } + void fuzzBits(const char8_t * ptr_in, char8_t * ptr_out, size_t len, double prob) + { + pcg64_fast rng(randomSeed()); // TODO It is inefficient. We should use SIMD PRNG instead. + + for (size_t i = 0; i < len; ++i) + { + UInt64 rand = rng(); + auto mask = getXorMask(rand, prob); + ptr_out[i] = ptr_in[i] ^ mask; + } + } +} + + +class FunctionFuzzBits : public IFunction +{ +public: + static constexpr auto name = "fuzzBits"; + + static FunctionPtr create(const Context &) { return std::make_shared(); } + + String getName() const override { return name; } + + bool isVariadic() const override { return false; } + + size_t getNumberOfArguments() const override { return 2; } + + ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1}; } // indexing from 0 + + DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override + { + if (!isStringOrFixedString(arguments[0].type)) + throw Exception( + "First argument of function " + getName() + " must be String or FixedString", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + if (!arguments[1].column || !isFloat(arguments[1].type)) + throw Exception("Second argument of function " + getName() + " must be constant float", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT); + + return arguments[0].type; + } + + bool isDeterministic() const override { return false; } + bool isDeterministicInScopeOfQuery() const override { return false; } + + void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override + { + auto col_in_untyped = block.getByPosition(arguments[0]).column; + const double inverse_probability = assert_cast(*block.getByPosition(arguments[1]).column).getValue(); + + if (inverse_probability < 0.0 || 1.0 < inverse_probability) + { + throw Exception("Second argument of function " + getName() + " must be from `0.0` to `1.0`", ErrorCodes::ARGUMENT_OUT_OF_BOUND); + } + + if (const ColumnConst * col_in_untyped_const = checkAndGetColumnConstStringOrFixedString(col_in_untyped.get())) + { + col_in_untyped = col_in_untyped_const->getDataColumnPtr(); + } + + if (const ColumnString * col_in = checkAndGetColumn(col_in_untyped.get())) + { + auto col_to = ColumnString::create(); + ColumnString::Chars & chars_to = col_to->getChars(); + ColumnString::Offsets & offsets_to = col_to->getOffsets(); + + chars_to.resize(col_in->getChars().size()); + // TODO: Maybe we can share `col_in->getOffsets()` to `offsets_to.resize` like clever pointers? They are same + offsets_to.resize(input_rows_count); + + const auto * ptr_in = col_in->getChars().data(); + auto * ptr_to = chars_to.data(); + fuzzBits(ptr_in, ptr_to, chars_to.size(), inverse_probability); + + for (size_t i = 0; i < input_rows_count; ++i) + { + offsets_to[i] = col_in->getOffsets()[i]; + ptr_to[offsets_to[i] - 1] = 0; + } + + block.getByPosition(result).column = std::move(col_to); + } + else if (const ColumnFixedString * col_in_fixed = checkAndGetColumn(col_in_untyped.get())) + { + const auto n = col_in_fixed->getN(); + auto col_to = ColumnFixedString::create(n); + ColumnFixedString::Chars & chars_to = col_to->getChars(); + + size_t total_size; + if (common::mulOverflow(input_rows_count, n, total_size)) + throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW); + + chars_to.resize(total_size); + + const auto * ptr_in = col_in_fixed->getChars().data(); + auto * ptr_to = chars_to.data(); + fuzzBits(ptr_in, ptr_to, chars_to.size(), inverse_probability); + + block.getByPosition(result).column = std::move(col_to); + } + else + { + throw Exception( + "Illegal column " + block.getByPosition(arguments[0]).column->getName() + " of argument of function " + getName(), + ErrorCodes::ILLEGAL_COLUMN); + } + } +}; + +void registerFunctionFuzzBits(FunctionFactory & factory) +{ + factory.registerFunction(); +} +} diff --git a/src/Functions/geohashesInBox.cpp b/src/Functions/geohashesInBox.cpp index 289e94b1c45..6bf0e5a82cd 100644 --- a/src/Functions/geohashesInBox.cpp +++ b/src/Functions/geohashesInBox.cpp @@ -120,7 +120,7 @@ public: // Actually write geohashes into preallocated buffer. geohashesInBox(prepared_args, out); - for (UInt8 i = 1; i <= prepared_args.items_count ; ++i) + for (UInt64 i = 1; i <= prepared_args.items_count ; ++i) { res_strings_offsets.push_back(starting_offset + (prepared_args.precision + 1) * i); } diff --git a/src/Functions/registerFunctionsRandom.cpp b/src/Functions/registerFunctionsRandom.cpp index 3638474c4fe..6a2cb82dc4c 100644 --- a/src/Functions/registerFunctionsRandom.cpp +++ b/src/Functions/registerFunctionsRandom.cpp @@ -10,6 +10,7 @@ void registerFunctionRandomPrintableASCII(FunctionFactory & factory); void registerFunctionRandomString(FunctionFactory & factory); void registerFunctionRandomFixedString(FunctionFactory & factory); void registerFunctionRandomStringUTF8(FunctionFactory & factory); +void registerFunctionFuzzBits(FunctionFactory & factory); void registerFunctionsRandom(FunctionFactory & factory) { @@ -21,6 +22,7 @@ void registerFunctionsRandom(FunctionFactory & factory) registerFunctionRandomString(factory); registerFunctionRandomFixedString(factory); registerFunctionRandomStringUTF8(factory); + registerFunctionFuzzBits(factory); } } diff --git a/src/Functions/registerFunctionsStringRegexp.cpp b/src/Functions/registerFunctionsStringRegexp.cpp index 350f7bd5d00..2a0a3c0ea1f 100644 --- a/src/Functions/registerFunctionsStringRegexp.cpp +++ b/src/Functions/registerFunctionsStringRegexp.cpp @@ -18,7 +18,8 @@ void registerFunctionMultiFuzzyMatchAny(FunctionFactory &); void registerFunctionMultiFuzzyMatchAnyIndex(FunctionFactory &); void registerFunctionMultiFuzzyMatchAllIndices(FunctionFactory &); void registerFunctionExtractGroups(FunctionFactory &); -void registerFunctionExtractAllGroups(FunctionFactory &); +void registerFunctionExtractAllGroupsVertical(FunctionFactory &); +void registerFunctionExtractAllGroupsHorizontal(FunctionFactory &); void registerFunctionsStringRegexp(FunctionFactory & factory) { @@ -37,7 +38,8 @@ void registerFunctionsStringRegexp(FunctionFactory & factory) registerFunctionMultiFuzzyMatchAnyIndex(factory); registerFunctionMultiFuzzyMatchAllIndices(factory); registerFunctionExtractGroups(factory); - registerFunctionExtractAllGroups(factory); + registerFunctionExtractAllGroupsVertical(factory); + registerFunctionExtractAllGroupsHorizontal(factory); } } diff --git a/src/Functions/ya.make b/src/Functions/ya.make index 70c42dd5af7..61fa5e64854 100644 --- a/src/Functions/ya.make +++ b/src/Functions/ya.make @@ -6,7 +6,7 @@ CFLAGS( ) ADDINCL( - library/consistent_hashing + library/cpp/consistent_hashing contrib/libs/farmhash contrib/libs/hyperscan/src contrib/libs/icu/common @@ -26,7 +26,7 @@ PEERDIR( contrib/libs/metrohash contrib/libs/rapidjson contrib/libs/xxhash - library/consistent_hashing + library/cpp/consistent_hashing ) # "Arcadia" build is slightly deficient. It lacks many libraries that we need. @@ -144,7 +144,8 @@ SRCS( exp10.cpp exp2.cpp exp.cpp - extractAllGroups.cpp + extractAllGroupsHorizontal.cpp + extractAllGroupsVertical.cpp extract.cpp extractGroups.cpp extractTimeZoneFromFunctionArguments.cpp @@ -172,6 +173,7 @@ SRCS( FunctionsRound.cpp FunctionsStringArray.cpp FunctionsStringSimilarity.cpp + fuzzBits.cpp GatherUtils/concat.cpp GatherUtils/createArraySink.cpp GatherUtils/createArraySource.cpp @@ -422,6 +424,7 @@ SRCS( URL/extractURLParameters.cpp URL/firstSignificantSubdomain.cpp URL/fragment.cpp + URL/netloc.cpp URL/path.cpp URL/pathFull.cpp URL/port.cpp diff --git a/src/Functions/ya.make.in b/src/Functions/ya.make.in index 42ae1c6dde8..2f01b20ca5f 100644 --- a/src/Functions/ya.make.in +++ b/src/Functions/ya.make.in @@ -5,7 +5,7 @@ CFLAGS( ) ADDINCL( - library/consistent_hashing + library/cpp/consistent_hashing contrib/libs/farmhash contrib/libs/hyperscan/src contrib/libs/icu/common @@ -25,7 +25,7 @@ PEERDIR( contrib/libs/metrohash contrib/libs/rapidjson contrib/libs/xxhash - library/consistent_hashing + library/cpp/consistent_hashing ) # "Arcadia" build is slightly deficient. It lacks many libraries that we need. diff --git a/src/IO/ReadBufferAIO.cpp b/src/IO/ReadBufferAIO.cpp index ffe8183f005..8b01b67c0c0 100644 --- a/src/IO/ReadBufferAIO.cpp +++ b/src/IO/ReadBufferAIO.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -95,11 +96,8 @@ bool ReadBufferAIO::nextImpl() if (profile_callback) watch.emplace(clock_type); - if (!is_aio) - { + if (!is_pending_read) synchronousRead(); - is_aio = true; - } else receive(); @@ -215,7 +213,9 @@ void ReadBufferAIO::synchronousRead() void ReadBufferAIO::receive() { if (!waitForAIOCompletion()) - return; + { + throw Exception("Trying to receive data from AIO, but nothing was queued. It's a bug", ErrorCodes::LOGICAL_ERROR); + } finalize(); } @@ -224,8 +224,6 @@ void ReadBufferAIO::skip() if (!waitForAIOCompletion()) return; - is_aio = false; - /// @todo I presume this assignment is redundant since waitForAIOCompletion() performs a similar one // bytes_read = future_bytes_read.get(); if ((bytes_read < 0) || (static_cast(bytes_read) < region_left_padding)) @@ -274,6 +272,9 @@ void ReadBufferAIO::prepare() region_aligned_size = region_aligned_end - region_aligned_begin; buffer_begin = fill_buffer.internalBuffer().begin(); + + /// Unpoison because msan doesn't instrument linux AIO + __msan_unpoison(buffer_begin, fill_buffer.internalBuffer().size()); } void ReadBufferAIO::finalize() diff --git a/src/IO/ReadBufferAIO.h b/src/IO/ReadBufferAIO.h index 77274c47073..5b2cf247a45 100644 --- a/src/IO/ReadBufferAIO.h +++ b/src/IO/ReadBufferAIO.h @@ -100,8 +100,6 @@ private: bool is_eof = false; /// At least one read request was sent. bool is_started = false; - /// Is the operation asynchronous? - bool is_aio = false; /// Did the asynchronous operation fail? bool aio_failed = false; diff --git a/src/IO/S3/PocoHTTPClient.cpp b/src/IO/S3/PocoHTTPClient.cpp index b8de483a5a8..0dfa80ca107 100644 --- a/src/IO/S3/PocoHTTPClient.cpp +++ b/src/IO/S3/PocoHTTPClient.cpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include #include @@ -149,8 +151,7 @@ void PocoHTTPClient::MakeRequestInternal( response->SetClientErrorMessage(error_message); } else - /// TODO: Do not copy whole stream. - Poco::StreamCopier::copyStream(response_body_stream, response->GetResponseBody()); + response->GetResponseStream().SetUnderlyingStream(std::make_shared(session, response_body_stream)); break; } diff --git a/src/IO/S3/PocoHTTPClientFactory.cpp b/src/IO/S3/PocoHTTPClientFactory.cpp index 033ad4af37c..e4b86593ec1 100644 --- a/src/IO/S3/PocoHTTPClientFactory.cpp +++ b/src/IO/S3/PocoHTTPClientFactory.cpp @@ -21,10 +21,12 @@ std::shared_ptr PocoHTTPClientFactory::CreateHttpRequest } std::shared_ptr PocoHTTPClientFactory::CreateHttpRequest( - const Aws::Http::URI & uri, Aws::Http::HttpMethod method, const Aws::IOStreamFactory & streamFactory) const + const Aws::Http::URI & uri, Aws::Http::HttpMethod method, const Aws::IOStreamFactory &) const { auto request = Aws::MakeShared("PocoHTTPClientFactory", uri, method); - request->SetResponseStreamFactory(streamFactory); + + /// Don't create default response stream. Actual response stream will be set later in PocoHTTPClient. + request->SetResponseStreamFactory(null_factory); return request; } diff --git a/src/IO/S3/PocoHTTPClientFactory.h b/src/IO/S3/PocoHTTPClientFactory.h index ac586289113..4e555f05502 100644 --- a/src/IO/S3/PocoHTTPClientFactory.h +++ b/src/IO/S3/PocoHTTPClientFactory.h @@ -4,22 +4,25 @@ namespace Aws::Http { - class HttpClient; - class HttpRequest; +class HttpClient; +class HttpRequest; } namespace DB::S3 { - class PocoHTTPClientFactory : public Aws::Http::HttpClientFactory { public: ~PocoHTTPClientFactory() override = default; - [[nodiscard]] std::shared_ptr CreateHttpClient(const Aws::Client::ClientConfiguration & clientConfiguration) const override; + [[nodiscard]] std::shared_ptr + CreateHttpClient(const Aws::Client::ClientConfiguration & clientConfiguration) const override; [[nodiscard]] std::shared_ptr CreateHttpRequest(const Aws::String & uri, Aws::Http::HttpMethod method, const Aws::IOStreamFactory & streamFactory) const override; [[nodiscard]] std::shared_ptr CreateHttpRequest(const Aws::Http::URI & uri, Aws::Http::HttpMethod method, const Aws::IOStreamFactory & streamFactory) const override; + +private: + const Aws::IOStreamFactory null_factory = []() { return nullptr; }; }; } diff --git a/src/IO/S3/PocoHTTPResponseStream.cpp b/src/IO/S3/PocoHTTPResponseStream.cpp new file mode 100644 index 00000000000..0a198268f2e --- /dev/null +++ b/src/IO/S3/PocoHTTPResponseStream.cpp @@ -0,0 +1,12 @@ +#include "PocoHTTPResponseStream.h" + +#include + +namespace DB::S3 +{ +PocoHTTPResponseStream::PocoHTTPResponseStream(std::shared_ptr session_, std::istream & response_stream_) + : Aws::IStream(response_stream_.rdbuf()), session(std::move(session_)) +{ +} + +} diff --git a/src/IO/S3/PocoHTTPResponseStream.h b/src/IO/S3/PocoHTTPResponseStream.h new file mode 100644 index 00000000000..8167ddc4346 --- /dev/null +++ b/src/IO/S3/PocoHTTPResponseStream.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace DB::S3 +{ +/** + * Wrapper of IStream to store response stream and corresponding HTTP session. + */ +class PocoHTTPResponseStream : public Aws::IStream +{ +public: + PocoHTTPResponseStream(std::shared_ptr session_, std::istream & response_stream_); + +private: + /// Poco HTTP session is holder of response stream. + std::shared_ptr session; +}; + +} diff --git a/src/IO/S3Common.cpp b/src/IO/S3Common.cpp index 1539b3c7025..2c75a137222 100644 --- a/src/IO/S3Common.cpp +++ b/src/IO/S3Common.cpp @@ -4,6 +4,7 @@ # include # include +# include # include # include @@ -60,6 +61,47 @@ public: private: Poco::Logger * log = &Poco::Logger::get("AWSClient"); }; + +class S3AuthSigner : public Aws::Client::AWSAuthV4Signer +{ +public: + S3AuthSigner( + const Aws::Client::ClientConfiguration & client_configuration, + const Aws::Auth::AWSCredentials & credentials, + const DB::HeaderCollection & headers_) + : Aws::Client::AWSAuthV4Signer( + std::make_shared(credentials), + "s3", + client_configuration.region, + Aws::Client::AWSAuthV4Signer::PayloadSigningPolicy::Never, + false) + , headers(headers_) + { + } + + bool SignRequest(Aws::Http::HttpRequest & request, const char * region, bool sign_body) const override + { + auto result = Aws::Client::AWSAuthV4Signer::SignRequest(request, region, sign_body); + for (const auto & header : headers) + request.SetHeaderValue(header.name, header.value); + return result; + } + + bool PresignRequest( + Aws::Http::HttpRequest & request, + const char * region, + const char * serviceName, + long long expiration_time_sec) const override // NOLINT + { + auto result = Aws::Client::AWSAuthV4Signer::PresignRequest(request, region, serviceName, expiration_time_sec); + for (const auto & header : headers) + request.SetHeaderValue(header.name, header.value); + return result; + } + +private: + const DB::HeaderCollection headers; +}; } namespace DB @@ -139,6 +181,25 @@ namespace S3 ); } + std::shared_ptr ClientFactory::create( // NOLINT + const String & endpoint, + bool is_virtual_hosted_style, + const String & access_key_id, + const String & secret_access_key, + HeaderCollection headers) + { + Aws::Client::ClientConfiguration cfg; + if (!endpoint.empty()) + cfg.endpointOverride = endpoint; + + Aws::Auth::AWSCredentials credentials(access_key_id, secret_access_key); + return std::make_shared( + std::make_shared(cfg, std::move(credentials), std::move(headers)), + std::move(cfg), // Client configuration. + is_virtual_hosted_style || cfg.endpointOverride.empty() // Use virtual addressing only if endpoint is not specified. + ); + } + URI::URI(const Poco::URI & uri_) { /// Case when bucket name represented in domain name of S3 URL. diff --git a/src/IO/S3Common.h b/src/IO/S3Common.h index 84795a4b39a..7f8cba66aad 100644 --- a/src/IO/S3Common.h +++ b/src/IO/S3Common.h @@ -5,7 +5,7 @@ #if USE_AWS_S3 #include -#include +#include #include namespace Aws::S3 @@ -13,6 +13,12 @@ namespace Aws::S3 class S3Client; } +namespace DB +{ + struct HttpHeader; + using HeaderCollection = std::vector; +} + namespace DB::S3 { @@ -34,6 +40,14 @@ public: bool is_virtual_hosted_style, const String & access_key_id, const String & secret_access_key); + + std::shared_ptr create( + const String & endpoint, + bool is_virtual_hosted_style, + const String & access_key_id, + const String & secret_access_key, + HeaderCollection headers); + private: ClientFactory(); diff --git a/src/IO/tests/gtest_s3_uri.cpp b/src/IO/tests/gtest_s3_uri.cpp index 814caeaffd3..aec6c779394 100644 --- a/src/IO/tests/gtest_s3_uri.cpp +++ b/src/IO/tests/gtest_s3_uri.cpp @@ -20,18 +20,42 @@ TEST(S3UriTest, validPatterns) ASSERT_EQ("https://s3.yandexcloud.net", uri.endpoint); ASSERT_EQ("jokserfn", uri.bucket); ASSERT_EQ("data", uri.key); + ASSERT_EQ(true, uri.is_virtual_hosted_style); } { S3::URI uri(Poco::URI("https://storage.yandexcloud.net/jokserfn/data")); ASSERT_EQ("https://storage.yandexcloud.net", uri.endpoint); ASSERT_EQ("jokserfn", uri.bucket); ASSERT_EQ("data", uri.key); + ASSERT_EQ(false, uri.is_virtual_hosted_style); + } + { + S3::URI uri(Poco::URI("https://bucketname.s3.us-east-2.amazonaws.com/data")); + ASSERT_EQ("https://s3.us-east-2.amazonaws.com", uri.endpoint); + ASSERT_EQ("bucketname", uri.bucket); + ASSERT_EQ("data", uri.key); + ASSERT_EQ(true, uri.is_virtual_hosted_style); } { S3::URI uri(Poco::URI("https://s3.us-east-2.amazonaws.com/bucketname/data")); ASSERT_EQ("https://s3.us-east-2.amazonaws.com", uri.endpoint); ASSERT_EQ("bucketname", uri.bucket); ASSERT_EQ("data", uri.key); + ASSERT_EQ(false, uri.is_virtual_hosted_style); + } + { + S3::URI uri(Poco::URI("https://bucketname.s3-us-east-2.amazonaws.com/data")); + ASSERT_EQ("https://s3-us-east-2.amazonaws.com", uri.endpoint); + ASSERT_EQ("bucketname", uri.bucket); + ASSERT_EQ("data", uri.key); + ASSERT_EQ(true, uri.is_virtual_hosted_style); + } + { + S3::URI uri(Poco::URI("https://s3-us-east-2.amazonaws.com/bucketname/data")); + ASSERT_EQ("https://s3-us-east-2.amazonaws.com", uri.endpoint); + ASSERT_EQ("bucketname", uri.bucket); + ASSERT_EQ("data", uri.key); + ASSERT_EQ(false, uri.is_virtual_hosted_style); } } diff --git a/src/Interpreters/ActionLocksManager.cpp b/src/Interpreters/ActionLocksManager.cpp index e8887f44a45..76e5228bec3 100644 --- a/src/Interpreters/ActionLocksManager.cpp +++ b/src/Interpreters/ActionLocksManager.cpp @@ -29,7 +29,8 @@ inline void forEachTable(F && f, const Context & context) { for (auto & elem : DatabaseCatalog::instance().getDatabases()) for (auto iterator = elem.second->getTablesIterator(context); iterator->isValid(); iterator->next()) - f(iterator->table()); + if (auto table = iterator->table()) + f(table); } diff --git a/src/Interpreters/ActionsVisitor.cpp b/src/Interpreters/ActionsVisitor.cpp index b5d9f30573e..512319375d5 100644 --- a/src/Interpreters/ActionsVisitor.cpp +++ b/src/Interpreters/ActionsVisitor.cpp @@ -512,7 +512,8 @@ void ActionsMatcher::visit(const ASTFunction & node, const ASTPtr & ast, Data & if (data.only_consts) arguments_present = false; else - throw Exception("Unknown identifier: " + child_column_name, ErrorCodes::UNKNOWN_IDENTIFIER); + throw Exception("Unknown identifier: " + child_column_name + " there are columns: " + data.getSampleBlock().dumpNames(), + ErrorCodes::UNKNOWN_IDENTIFIER); } } } diff --git a/src/Interpreters/Aggregator.cpp b/src/Interpreters/Aggregator.cpp index 96a9b1fc1df..538a24fa997 100644 --- a/src/Interpreters/Aggregator.cpp +++ b/src/Interpreters/Aggregator.cpp @@ -530,63 +530,33 @@ void NO_INLINE Aggregator::executeWithoutKeyImpl( } -bool Aggregator::executeOnBlock(const Block & block, AggregatedDataVariants & result, - ColumnRawPtrs & key_columns, AggregateColumns & aggregate_columns, bool & no_more_keys) +void NO_INLINE Aggregator::executeOnIntervalWithoutKeyImpl( + AggregatedDataWithoutKey & res, + size_t row_begin, + size_t row_end, + AggregateFunctionInstruction * aggregate_instructions, + Arena * arena) { - UInt64 num_rows = block.rows(); - return executeOnBlock(block.getColumns(), num_rows, result, key_columns, aggregate_columns, no_more_keys); + /// Adding values + for (AggregateFunctionInstruction * inst = aggregate_instructions; inst->that; ++inst) + { + if (inst->offsets) + inst->batch_that->addBatchSinglePlaceFromInterval(inst->offsets[row_begin], inst->offsets[row_end - 1], res + inst->state_offset, inst->batch_arguments, arena); + else + inst->batch_that->addBatchSinglePlaceFromInterval(row_begin, row_end, res + inst->state_offset, inst->batch_arguments, arena); + } } -bool Aggregator::executeOnBlock(Columns columns, UInt64 num_rows, AggregatedDataVariants & result, - ColumnRawPtrs & key_columns, AggregateColumns & aggregate_columns, bool & no_more_keys) + +void Aggregator::prepareAggregateInstructions(Columns columns, AggregateColumns & aggregate_columns, Columns & materialized_columns, + AggregateFunctionInstructions & aggregate_functions_instructions, NestedColumnsHolder & nested_columns_holder) { - if (isCancelled()) - return true; - - /// `result` will destroy the states of aggregate functions in the destructor - result.aggregator = this; - - /// How to perform the aggregation? - if (result.empty()) - { - result.init(method_chosen); - result.keys_size = params.keys_size; - result.key_sizes = key_sizes; - LOG_TRACE(log, "Aggregation method: {}", result.getMethodName()); - } - - if (isCancelled()) - return true; - for (size_t i = 0; i < params.aggregates_size; ++i) aggregate_columns[i].resize(params.aggregates[i].arguments.size()); - /** Constant columns are not supported directly during aggregation. - * To make them work anyway, we materialize them. - */ - Columns materialized_columns; - - /// Remember the columns we will work with - for (size_t i = 0; i < params.keys_size; ++i) - { - materialized_columns.push_back(columns.at(params.keys[i])->convertToFullColumnIfConst()); - key_columns[i] = materialized_columns.back().get(); - - if (!result.isLowCardinality()) - { - auto column_no_lc = recursiveRemoveLowCardinality(key_columns[i]->getPtr()); - if (column_no_lc.get() != key_columns[i]) - { - materialized_columns.emplace_back(std::move(column_no_lc)); - key_columns[i] = materialized_columns.back().get(); - } - } - } - - AggregateFunctionInstructions aggregate_functions_instructions(params.aggregates_size + 1); + aggregate_functions_instructions.resize(params.aggregates_size + 1); aggregate_functions_instructions[params.aggregates_size].that = nullptr; - std::vector> nested_columns_holder; for (size_t i = 0; i < params.aggregates_size; ++i) { for (size_t j = 0; j < aggregate_columns[i].size(); ++j) @@ -627,6 +597,62 @@ bool Aggregator::executeOnBlock(Columns columns, UInt64 num_rows, AggregatedData aggregate_functions_instructions[i].batch_that = that; } +} + + +bool Aggregator::executeOnBlock(const Block & block, AggregatedDataVariants & result, + ColumnRawPtrs & key_columns, AggregateColumns & aggregate_columns, bool & no_more_keys) +{ + UInt64 num_rows = block.rows(); + return executeOnBlock(block.getColumns(), num_rows, result, key_columns, aggregate_columns, no_more_keys); +} + + +bool Aggregator::executeOnBlock(Columns columns, UInt64 num_rows, AggregatedDataVariants & result, + ColumnRawPtrs & key_columns, AggregateColumns & aggregate_columns, bool & no_more_keys) +{ + if (isCancelled()) + return true; + + /// `result` will destroy the states of aggregate functions in the destructor + result.aggregator = this; + + /// How to perform the aggregation? + if (result.empty()) + { + result.init(method_chosen); + result.keys_size = params.keys_size; + result.key_sizes = key_sizes; + LOG_TRACE(log, "Aggregation method: {}", result.getMethodName()); + } + + if (isCancelled()) + return true; + + /** Constant columns are not supported directly during aggregation. + * To make them work anyway, we materialize them. + */ + Columns materialized_columns; + + /// Remember the columns we will work with + for (size_t i = 0; i < params.keys_size; ++i) + { + materialized_columns.push_back(columns.at(params.keys[i])->convertToFullColumnIfConst()); + key_columns[i] = materialized_columns.back().get(); + + if (!result.isLowCardinality()) + { + auto column_no_lc = recursiveRemoveLowCardinality(key_columns[i]->getPtr()); + if (column_no_lc.get() != key_columns[i]) + { + materialized_columns.emplace_back(std::move(column_no_lc)); + key_columns[i] = materialized_columns.back().get(); + } + } + } + NestedColumnsHolder nested_columns_holder; + AggregateFunctionInstructions aggregate_functions_instructions; + prepareAggregateInstructions(columns, aggregate_columns, materialized_columns, aggregate_functions_instructions, nested_columns_holder); if (isCancelled()) return true; @@ -666,7 +692,8 @@ bool Aggregator::executeOnBlock(Columns columns, UInt64 num_rows, AggregatedData if (auto * memory_tracker = memory_tracker_child->getParent()) current_memory_usage = memory_tracker->get(); - auto result_size_bytes = current_memory_usage - memory_usage_before_aggregation; /// Here all the results in the sum are taken into account, from different threads. + /// Here all the results in the sum are taken into account, from different threads. + auto result_size_bytes = current_memory_usage - memory_usage_before_aggregation; bool worth_convert_to_two_level = (params.group_by_two_level_threshold && result_size >= params.group_by_two_level_threshold) @@ -972,6 +999,73 @@ void Aggregator::convertToBlockImpl( data.clearAndShrink(); } + +template +inline void Aggregator::insertAggregatesIntoColumns( + Mapped & mapped, + MutableColumns & final_aggregate_columns) const +{ + /** Final values of aggregate functions are inserted to columns. + * Then states of aggregate functions, that are not longer needed, are destroyed. + * + * We mark already destroyed states with "nullptr" in data, + * so they will not be destroyed in destructor of Aggregator + * (other values will be destroyed in destructor in case of exception). + * + * But it becomes tricky, because we have multiple aggregate states pointed by a single pointer in data. + * So, if exception is thrown in the middle of moving states for different aggregate functions, + * we have to catch exceptions and destroy all the states that are no longer needed, + * to keep the data in consistent state. + * + * It is also tricky, because there are aggregate functions with "-State" modifier. + * When we call "insertResultInto" for them, they insert a pointer to the state to ColumnAggregateFunction + * and ColumnAggregateFunction will take ownership of this state. + * So, for aggregate functions with "-State" modifier, the state must not be destroyed + * after it has been transferred to ColumnAggregateFunction. + * But we should mark that the data no longer owns these states. + */ + + size_t insert_i = 0; + std::exception_ptr exception; + + try + { + /// Insert final values of aggregate functions into columns. + for (; insert_i < params.aggregates_size; ++insert_i) + aggregate_functions[insert_i]->insertResultInto( + mapped + offsets_of_aggregate_states[insert_i], + *final_aggregate_columns[insert_i]); + } + catch (...) + { + exception = std::current_exception(); + } + + /** Destroy states that are no longer needed. This loop does not throw. + * + * Don't destroy states for "-State" aggregate functions, + * because the ownership of this state is transferred to ColumnAggregateFunction + * and ColumnAggregateFunction will take care. + * + * But it's only for states that has been transferred to ColumnAggregateFunction + * before exception has been thrown; + */ + for (size_t destroy_i = 0; destroy_i < params.aggregates_size; ++destroy_i) + { + /// If ownership was not transferred to ColumnAggregateFunction. + if (!(destroy_i < insert_i && aggregate_functions[destroy_i]->isState())) + aggregate_functions[destroy_i]->destroy( + mapped + offsets_of_aggregate_states[destroy_i]); + } + + /// Mark the cell as destroyed so it will not be destroyed in destructor. + mapped = nullptr; + + if (exception) + std::rethrow_exception(exception); +} + + template void NO_INLINE Aggregator::convertToBlockImplFinal( Method & method, @@ -984,25 +1078,15 @@ void NO_INLINE Aggregator::convertToBlockImplFinal( if (data.hasNullKeyData()) { key_columns[0]->insertDefault(); - - for (size_t i = 0; i < params.aggregates_size; ++i) - aggregate_functions[i]->insertResultInto( - data.getNullKeyData() + offsets_of_aggregate_states[i], - *final_aggregate_columns[i]); + insertAggregatesIntoColumns(data.getNullKeyData(), final_aggregate_columns); } } data.forEachValue([&](const auto & key, auto & mapped) { method.insertKeyIntoColumns(key, key_columns, key_sizes); - - for (size_t i = 0; i < params.aggregates_size; ++i) - aggregate_functions[i]->insertResultInto( - mapped + offsets_of_aggregate_states[i], - *final_aggregate_columns[i]); + insertAggregatesIntoColumns(mapped, final_aggregate_columns); }); - - destroyImpl(data); } template @@ -1020,6 +1104,8 @@ void NO_INLINE Aggregator::convertToBlockImplNotFinal( for (size_t i = 0; i < params.aggregates_size; ++i) aggregate_columns[i]->push_back(data.getNullKeyData() + offsets_of_aggregate_states[i]); + + data.getNullKeyData() = nullptr; } } @@ -1112,7 +1198,39 @@ Block Aggregator::prepareBlockAndFill( return res; } +void Aggregator::fillAggregateColumnsWithSingleKey( + AggregatedDataVariants & data_variants, + MutableColumns & final_aggregate_columns) +{ + AggregatedDataWithoutKey & data = data_variants.without_key; + for (size_t i = 0; i < params.aggregates_size; ++i) + { + ColumnAggregateFunction & column_aggregate_func = assert_cast(*final_aggregate_columns[i]); + for (auto & pool : data_variants.aggregates_pools) + { + column_aggregate_func.addArena(pool); + } + column_aggregate_func.getData().push_back(data + offsets_of_aggregate_states[i]); + } + data = nullptr; +} + +void Aggregator::createStatesAndFillKeyColumnsWithSingleKey( + AggregatedDataVariants & data_variants, + Columns & key_columns, + size_t key_row, + MutableColumns & final_key_columns) +{ + AggregateDataPtr place = data_variants.aggregates_pool->alignedAlloc(total_size_of_aggregate_states, align_aggregate_states); + createAggregateStates(place); + data_variants.without_key = place; + + for (size_t i = 0; i < params.keys_size; ++i) + { + final_key_columns[i]->insertFrom(*key_columns[i].get(), key_row); + } +} Block Aggregator::prepareBlockAndFillWithoutKey(AggregatedDataVariants & data_variants, bool final, bool is_overflows) const { @@ -1128,16 +1246,16 @@ Block Aggregator::prepareBlockAndFillWithoutKey(AggregatedDataVariants & data_va { AggregatedDataWithoutKey & data = data_variants.without_key; - for (size_t i = 0; i < params.aggregates_size; ++i) - { - if (!final_) - aggregate_columns[i]->push_back(data + offsets_of_aggregate_states[i]); - else - aggregate_functions[i]->insertResultInto(data + offsets_of_aggregate_states[i], *final_aggregate_columns[i]); - } - if (!final_) + { + for (size_t i = 0; i < params.aggregates_size; ++i) + aggregate_columns[i]->push_back(data + offsets_of_aggregate_states[i]); data = nullptr; + } + else + { + insertAggregatesIntoColumns(data, final_aggregate_columns); + } if (params.overflow_row) for (size_t i = 0; i < params.keys_size; ++i) @@ -2328,8 +2446,7 @@ void NO_INLINE Aggregator::destroyImpl(Table & table) const return; for (size_t i = 0; i < params.aggregates_size; ++i) - if (!aggregate_functions[i]->isState()) - aggregate_functions[i]->destroy(data + offsets_of_aggregate_states[i]); + aggregate_functions[i]->destroy(data + offsets_of_aggregate_states[i]); data = nullptr; }); @@ -2343,8 +2460,7 @@ void Aggregator::destroyWithoutKey(AggregatedDataVariants & result) const if (nullptr != res_data) { for (size_t i = 0; i < params.aggregates_size; ++i) - if (!aggregate_functions[i]->isState()) - aggregate_functions[i]->destroy(res_data + offsets_of_aggregate_states[i]); + aggregate_functions[i]->destroy(res_data + offsets_of_aggregate_states[i]); res_data = nullptr; } diff --git a/src/Interpreters/Aggregator.h b/src/Interpreters/Aggregator.h index abc1356787e..6d0eeee9014 100644 --- a/src/Interpreters/Aggregator.h +++ b/src/Interpreters/Aggregator.h @@ -829,7 +829,7 @@ using ManyAggregatedDataVariants = std::vector; using ManyAggregatedDataVariantsPtr = std::shared_ptr; /** How are "total" values calculated with WITH TOTALS? - * (For more details, see TotalsHavingBlockInputStream.) + * (For more details, see TotalsHavingTransform.) * * In the absence of group_by_overflow_mode = 'any', the data is aggregated as usual, but the states of the aggregate functions are not finalized. * Later, the aggregate function states for all rows (passed through HAVING) are merged into one - this will be TOTALS. @@ -1002,6 +1002,7 @@ protected: friend class MergingAndConvertingBlockInputStream; friend class ConvertingAggregatedToChunksTransform; friend class ConvertingAggregatedToChunksSource; + friend class AggregatingInOrderTransform; Params params; @@ -1033,12 +1034,13 @@ protected: }; using AggregateFunctionInstructions = std::vector; + using NestedColumnsHolder = std::vector>; Sizes offsets_of_aggregate_states; /// The offset to the n-th aggregate function in a row of aggregate functions. size_t total_size_of_aggregate_states = 0; /// The total size of the row from the aggregate functions. // add info to track alignment requirement - // If there are states whose alignmentment are v1, ..vn, align_aggregate_states will be max(v1, ... vn) + // If there are states whose alignment are v1, ..vn, align_aggregate_states will be max(v1, ... vn) size_t align_aggregate_states = 1; bool all_aggregates_has_trivial_destructor = false; @@ -1105,6 +1107,13 @@ protected: AggregateFunctionInstruction * aggregate_instructions, Arena * arena); + static void executeOnIntervalWithoutKeyImpl( + AggregatedDataWithoutKey & res, + size_t row_begin, + size_t row_end, + AggregateFunctionInstruction * aggregate_instructions, + Arena * arena); + template void writeToTemporaryFileImpl( AggregatedDataVariants & data_variants, @@ -1157,6 +1166,11 @@ protected: MutableColumns & final_aggregate_columns, bool final) const; + template + void insertAggregatesIntoColumns( + Mapped & mapped, + MutableColumns & final_aggregate_columns) const; + template void convertToBlockImplFinal( Method & method, @@ -1250,6 +1264,22 @@ protected: * - sets the variable no_more_keys to true. */ bool checkLimits(size_t result_size, bool & no_more_keys) const; + + void prepareAggregateInstructions( + Columns columns, + AggregateColumns & aggregate_columns, + Columns & materialized_columns, + AggregateFunctionInstructions & instructions, + NestedColumnsHolder & nested_columns_holder); + + void fillAggregateColumnsWithSingleKey( + AggregatedDataVariants & data_variants, + MutableColumns & final_aggregate_columns); + + void createStatesAndFillKeyColumnsWithSingleKey( + AggregatedDataVariants & data_variants, + Columns & key_columns, size_t key_row, + MutableColumns & final_key_columns); }; diff --git a/src/Interpreters/ArithmeticOperationsInAgrFuncOptimize.cpp b/src/Interpreters/ArithmeticOperationsInAgrFuncOptimize.cpp new file mode 100644 index 00000000000..665c2febd9d --- /dev/null +++ b/src/Interpreters/ArithmeticOperationsInAgrFuncOptimize.cpp @@ -0,0 +1,313 @@ +#include +#include +#include +#include +#include +#include + +namespace DB +{ + +namespace ErrorCodes +{ + extern const int LOGICAL_ERROR; + extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; + extern const int UNEXPECTED_AST_STRUCTURE; +} +namespace +{ + constexpr const char * min = "min"; + constexpr const char * max = "max"; + constexpr const char * mul = "multiply"; + constexpr const char * plus = "plus"; + constexpr const char * sum = "sum"; +} + +bool isConstantField(const Field & field) +{ + return field.getType() == Field::Types::Int64 || + field.getType() == Field::Types::UInt64 || + field.getType() == Field::Types::Int128 || + field.getType() == Field::Types::UInt128; +} + +bool onlyConstsInside(const ASTFunction * func_node) +{ + return !(func_node->arguments->children[0]->as()) && + (func_node->arguments->children.size() == 2 && + !(func_node->arguments->children[1]->as())); +} + +bool inappropriateNameInside(const ASTFunction * func_node, const char * inter_func_name) +{ + return (func_node->arguments->children[0]->as() && + inter_func_name != func_node->arguments->children[0]->as()->name) || + (func_node->arguments->children.size() == 2 && + func_node->arguments->children[1]->as() && + inter_func_name != func_node->arguments->children[1]->as()->name); +} + +bool isInappropriate(const ASTPtr & node, const char * inter_func_name) +{ + return !node->as() || inter_func_name != node->as()->name; +} + +ASTFunction * getInternalFunction(const ASTFunction * f_n) +{ + const auto * function_args = f_n->arguments->as(); + if (!function_args || function_args->children.size() != 1) + throw Exception("Wrong number of arguments for function" + f_n->name + "(" + toString(function_args->children.size()) + " instead of 1)", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + return f_n->arguments->children[0]->as(); +} + +ASTFunction * treeFiller(ASTFunction * old_tree, const ASTs & nodes_array, size_t size, const char * name) +{ + for (size_t i = 0; i < size; ++i) + { + old_tree->arguments->children = {}; + old_tree->arguments->children.push_back(nodes_array[i]); + + old_tree->arguments->children.push_back(makeASTFunction(name)); + old_tree = old_tree->arguments->children[1]->as(); + } + return old_tree; +} + +/// scalar values from the first level +std::pair tryGetConst(const char * name, const ASTs & arguments) +{ + ASTs const_num; + ASTs not_const; + + for (const auto & arg : arguments) + { + if (const auto * literal = arg->as()) + { + if (isConstantField(literal->value)) + const_num.push_back(arg); + else + not_const.push_back(arg); + } + else + not_const.push_back(arg); + } + + if ((name == plus || name == mul) && const_num.size() + not_const.size() != 2) + { + throw Exception("Wrong number of arguments for function 'plus' or 'multiply' (" + toString(const_num.size() + not_const.size()) + " instead of 2)", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + } + + return {const_num, not_const}; +} + +std::pair findAllConsts(const ASTFunction * func_node, const char * inter_func_name) +{ + if (!func_node->arguments) + return {}; + + if (onlyConstsInside(func_node)) + return tryGetConst(func_node->name.c_str(), func_node->arguments->children); + else if (inappropriateNameInside(func_node, inter_func_name)) + { + bool first_child_is_const = func_node->arguments->children[0]->as() && + isConstantField(func_node->arguments->children[0]->as()->value); + bool second_child_is_const = func_node->arguments->children.size() == 2 && + func_node->arguments->children[1]->as() && + isConstantField(func_node->arguments->children[1]->as()->value); + if (first_child_is_const) + return {{func_node->arguments->children[0]}, {func_node->arguments->children[1]}}; + else if (second_child_is_const) + return {{func_node->arguments->children[1]}, {func_node->arguments->children[0]}}; + + if (isInappropriate(func_node->arguments->children[0], inter_func_name) && isInappropriate(func_node->arguments->children[1], inter_func_name)) + return {{}, {func_node->arguments->children[0], func_node->arguments->children[1]}}; + else if (isInappropriate(func_node->arguments->children[0], inter_func_name)) + { + std::pair ans = findAllConsts(func_node->arguments->children[1]->as(), inter_func_name); + ans.second.push_back(func_node->arguments->children[0]); + return ans; + } + + std::pair ans = findAllConsts(func_node->arguments->children[0]->as(), inter_func_name); + ans.second.push_back(func_node->arguments->children[1]); + return ans; + } + + std::pair fl = tryGetConst(func_node->name.c_str(), func_node->arguments->children); + ASTs first_lvl_consts = fl.first; + ASTs first_lvl_not_consts = fl.second; + if (!first_lvl_not_consts[0]->as()) + return {first_lvl_consts, first_lvl_not_consts}; + + std::pair ans = findAllConsts(first_lvl_not_consts[0]->as(), inter_func_name); + ASTs all_consts = ans.first; + ASTs all_not_consts = ans.second; + + if (first_lvl_consts.size() == 1) + { + if (!first_lvl_not_consts[0]->as()) + all_not_consts.push_back(first_lvl_not_consts[0]); + + all_consts.push_back(first_lvl_consts[0]); + } + else if (first_lvl_consts.empty()) + { + /// if node is inappropriate to go into it, we just add this node to all_not_consts vector + bool first_node_inappropriate_to_go_into = isInappropriate(first_lvl_not_consts[0], inter_func_name); + bool second_node_inappropriate_to_go_into = first_lvl_not_consts.size() == 2 && + isInappropriate(first_lvl_not_consts[1], inter_func_name); + if (first_node_inappropriate_to_go_into) + all_not_consts.push_back(first_lvl_not_consts[0]); + + if (second_node_inappropriate_to_go_into) + all_not_consts.push_back(first_lvl_not_consts[1]); + } + else + throw Exception("did not expect that", ErrorCodes::UNEXPECTED_AST_STRUCTURE); + return {all_consts, all_not_consts}; +} + +/// rebuilds tree, all scalar values now outside the main func +void buildTree(ASTFunction * cur_node, const char * func_name, const char * intro_func, const std::pair & tree_comp) +{ + ASTs cons_val = tree_comp.first; + ASTs non_cons = tree_comp.second; + + cur_node->name = intro_func; + cur_node = treeFiller(cur_node, cons_val, cons_val.size(), intro_func); + cur_node->name = func_name; + + if (non_cons.size() == 1) + cur_node->arguments->children.push_back(non_cons[0]); + else + { + cur_node->arguments->children.push_back(makeASTFunction(intro_func)); + cur_node = cur_node->arguments->children[0]->as(); + cur_node = treeFiller(cur_node, non_cons, non_cons.size() - 2, intro_func); + cur_node->arguments->children = {non_cons[non_cons.size() - 2], non_cons[non_cons.size() - 1]}; + } +} + +void sumOptimize(ASTFunction * f_n) +{ + const auto * function_args = f_n->arguments->as(); + + if (!function_args || function_args->children.size() != 1) + throw Exception("Wrong number of arguments for function 'sum' (" + toString(function_args->children.size()) + " instead of 1)", + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + + ASTFunction * inter_node = getInternalFunction(f_n); + if (inter_node && inter_node->name == mul) + { + std::pair nodes = findAllConsts(f_n, mul); + + if (nodes.first.empty()) + return; + + buildTree(f_n, sum, mul, nodes); + } +} + +void minOptimize(ASTFunction * f_n) +{ + ASTFunction * inter_node = getInternalFunction(f_n); + if (inter_node && inter_node->name == mul) + { + int sign = 1; + std::pair nodes = findAllConsts(f_n, mul); + + if (nodes.first.empty()) + return; + + for (const auto & arg : nodes.first) + { + Int128 num = applyVisitor(FieldVisitorConvertToNumber(), arg->as()->value); + + /// if multiplication is negative, min function becomes max + + if ((arg->as()->value.getType() == Field::Types::Int64 || + arg->as()->value.getType() == Field::Types::Int128) && num < static_cast(0)) + sign *= -1; + } + + if (sign == -1) + buildTree(f_n, max, mul, nodes); + else + buildTree(f_n, min, mul, nodes); + } + else if (inter_node && inter_node->name == plus) + { + std::pair nodes = findAllConsts(f_n, plus); + buildTree(f_n, min, plus, nodes); + } +} + +void maxOptimize(ASTFunction * f_n) +{ + ASTFunction * inter_node = getInternalFunction(f_n); + if (inter_node && inter_node->name == mul) + { + int sign = 1; + std::pair nodes = findAllConsts(f_n, mul); + + if (nodes.first.empty()) + return; + + for (const auto & arg: nodes.first) + { + Int128 num = applyVisitor(FieldVisitorConvertToNumber(), arg->as()->value); + + /// if multiplication is negative, max function becomes min + if ((arg->as()->value.getType() == Field::Types::Int64 || + arg->as()->value.getType() == Field::Types::Int128) && num < static_cast(0)) + sign *= -1; + } + + if (sign == -1) + buildTree(f_n, min, mul, nodes); + else + buildTree(f_n, max, mul, nodes); + } + else if (inter_node && inter_node->name == plus) + { + std::pair nodes = findAllConsts(f_n, plus); + buildTree(f_n, max, plus, nodes); + } +} + +/// optimize for min, max, sum is ready, ToDo: groupBitAnd, groupBitOr, groupBitXor +void ArithmeticOperationsInAgrFuncMatcher::visit(ASTFunction * function_node, Data data) +{ + data = {}; + if (function_node->name == "sum") + sumOptimize(function_node); + else if (function_node->name == "min") + minOptimize(function_node); + else if (function_node->name == "max") + maxOptimize(function_node); +} + +void ArithmeticOperationsInAgrFuncMatcher::visit(const ASTPtr & current_ast, Data data) +{ + if (!current_ast) + return; + + if (auto * function_node = current_ast->as()) + visit(function_node, data); +} + +bool ArithmeticOperationsInAgrFuncMatcher::needChildVisit(const ASTPtr & node, const ASTPtr & child) +{ + if (!child) + throw Exception("AST item should not have nullptr in children", ErrorCodes::LOGICAL_ERROR); + + if (node->as() || node->as()) + return false; // NOLINT + + return true; +} + +} diff --git a/src/Interpreters/ArithmeticOperationsInAgrFuncOptimize.h b/src/Interpreters/ArithmeticOperationsInAgrFuncOptimize.h new file mode 100644 index 00000000000..46af1e272db --- /dev/null +++ b/src/Interpreters/ArithmeticOperationsInAgrFuncOptimize.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +namespace DB +{ + +/// It converts some arithmetic. Optimization due to the linearity property of some aggregate functions. +/// Function collects const and not const nodes and rebuilds old tree. +class ArithmeticOperationsInAgrFuncMatcher +{ +public: + struct Data {}; + + static void visit(const ASTPtr & ast, Data data); + static void visit(ASTFunction *, Data data); + static bool needChildVisit(const ASTPtr & node, const ASTPtr & child); + +}; +using ArithmeticOperationsInAgrFuncVisitor = InDepthNodeVisitor; +} diff --git a/src/Interpreters/AsynchronousMetricLog.cpp b/src/Interpreters/AsynchronousMetricLog.cpp new file mode 100644 index 00000000000..e4415773655 --- /dev/null +++ b/src/Interpreters/AsynchronousMetricLog.cpp @@ -0,0 +1,64 @@ +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + +Block AsynchronousMetricLogElement::createBlock() +{ + ColumnsWithTypeAndName columns; + + columns.emplace_back(std::make_shared(), "event_date"); + columns.emplace_back(std::make_shared(), "event_time"); + columns.emplace_back(std::make_shared(), "name"); + columns.emplace_back(std::make_shared(), "value"); + + return Block(columns); +} + + +void AsynchronousMetricLogElement::appendToBlock(MutableColumns & columns) const +{ + size_t column_idx = 0; + + columns[column_idx++]->insert(event_date); + columns[column_idx++]->insert(event_time); + columns[column_idx++]->insert(metric_name); + columns[column_idx++]->insert(value); +} + + +inline UInt64 time_in_milliseconds(std::chrono::time_point timepoint) +{ + return std::chrono::duration_cast(timepoint.time_since_epoch()).count(); +} + + +inline UInt64 time_in_seconds(std::chrono::time_point timepoint) +{ + return std::chrono::duration_cast(timepoint.time_since_epoch()).count(); +} + +void AsynchronousMetricLog::addValues(const AsynchronousMetricValues & values) +{ + AsynchronousMetricLogElement element; + + const auto now = std::chrono::system_clock::now(); + element.event_time = time_in_seconds(now); + element.event_date = DateLUT::instance().toDayNum(element.event_time); + + for (const auto & [key, value] : values) + { + element.metric_name = key; + element.value = value; + + add(element); + } +} + +} diff --git a/src/Interpreters/AsynchronousMetricLog.h b/src/Interpreters/AsynchronousMetricLog.h new file mode 100644 index 00000000000..b7d6aab95b6 --- /dev/null +++ b/src/Interpreters/AsynchronousMetricLog.h @@ -0,0 +1,41 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + + +namespace DB +{ + +typedef double AsynchronousMetricValue; +typedef std::unordered_map AsynchronousMetricValues; + +/** AsynchronousMetricLog is a log of metric values measured at regular time interval. + */ + +struct AsynchronousMetricLogElement +{ + UInt16 event_date; + time_t event_time; + std::string metric_name; + double value; + + static std::string name() { return "AsynchronousMetricLog"; } + static Block createBlock(); + void appendToBlock(MutableColumns & columns) const; +}; + +class AsynchronousMetricLog : public SystemLog +{ +public: + using SystemLog::SystemLog; + + void addValues(const AsynchronousMetricValues &); +}; + +} diff --git a/src/Interpreters/AsynchronousMetrics.cpp b/src/Interpreters/AsynchronousMetrics.cpp index a4e03d35d42..6cd8fafa2a8 100644 --- a/src/Interpreters/AsynchronousMetrics.cpp +++ b/src/Interpreters/AsynchronousMetrics.cpp @@ -1,4 +1,5 @@ #include +#include #include #include #include @@ -37,7 +38,7 @@ AsynchronousMetrics::~AsynchronousMetrics() try { { - std::lock_guard lock{wait_mutex}; + std::lock_guard lock{mutex}; quit = true; } @@ -51,17 +52,10 @@ AsynchronousMetrics::~AsynchronousMetrics() } -AsynchronousMetrics::Container AsynchronousMetrics::getValues() const +AsynchronousMetricValues AsynchronousMetrics::getValues() const { - std::lock_guard lock{container_mutex}; - return container; -} - - -void AsynchronousMetrics::set(const std::string & name, Value value) -{ - std::lock_guard lock{container_mutex}; - container[name] = value; + std::lock_guard lock{mutex}; + return values; } @@ -69,8 +63,6 @@ void AsynchronousMetrics::run() { setThreadName("AsyncMetrics"); - std::unique_lock lock{wait_mutex}; - /// Next minute + 30 seconds. To be distant with moment of transmission of metrics, see MetricsTransmitter. const auto get_next_minute = [] { @@ -89,6 +81,7 @@ void AsynchronousMetrics::run() tryLogCurrentException(__PRETTY_FUNCTION__); } + std::unique_lock lock{mutex}; if (wait_cond.wait_until(lock, get_next_minute(), [this] { return quit; })) break; } @@ -113,41 +106,43 @@ static void calculateMaxAndSum(Max & max, Sum & sum, T x) void AsynchronousMetrics::update() { + AsynchronousMetricValues new_values; + { if (auto mark_cache = context.getMarkCache()) { - set("MarkCacheBytes", mark_cache->weight()); - set("MarkCacheFiles", mark_cache->count()); + new_values["MarkCacheBytes"] = mark_cache->weight(); + new_values["MarkCacheFiles"] = mark_cache->count(); } } { if (auto uncompressed_cache = context.getUncompressedCache()) { - set("UncompressedCacheBytes", uncompressed_cache->weight()); - set("UncompressedCacheCells", uncompressed_cache->count()); + new_values["UncompressedCacheBytes"] = uncompressed_cache->weight(); + new_values["UncompressedCacheCells"] = uncompressed_cache->count(); } } #if USE_EMBEDDED_COMPILER { if (auto compiled_expression_cache = context.getCompiledExpressionCache()) - set("CompiledExpressionCacheCount", compiled_expression_cache->count()); + new_values["CompiledExpressionCacheCount"] = compiled_expression_cache->count(); } #endif - set("Uptime", context.getUptimeSeconds()); + new_values["Uptime"] = context.getUptimeSeconds(); /// Process memory usage according to OS #if defined(OS_LINUX) { MemoryStatisticsOS::Data data = memory_stat.get(); - set("MemoryVirtual", data.virt); - set("MemoryResident", data.resident); - set("MemoryShared", data.shared); - set("MemoryCode", data.code); - set("MemoryDataAndStack", data.data_and_stack); + new_values["MemoryVirtual"] = data.virt; + new_values["MemoryResident"] = data.resident; + new_values["MemoryShared"] = data.shared; + new_values["MemoryCode"] = data.code; + new_values["MemoryDataAndStack"] = data.data_and_stack; /// We must update the value of total_memory_tracker periodically. /// Otherwise it might be calculated incorrectly - it can include a "drift" of memory amount. @@ -185,6 +180,9 @@ void AsynchronousMetrics::update() { ++total_number_of_tables; const auto & table = iterator->table(); + if (!table) + continue; + StorageMergeTree * table_merge_tree = dynamic_cast(table.get()); StorageReplicatedMergeTree * table_replicated_merge_tree = dynamic_cast(table.get()); @@ -225,21 +223,21 @@ void AsynchronousMetrics::update() } } - set("ReplicasMaxQueueSize", max_queue_size); - set("ReplicasMaxInsertsInQueue", max_inserts_in_queue); - set("ReplicasMaxMergesInQueue", max_merges_in_queue); + new_values["ReplicasMaxQueueSize"] = max_queue_size; + new_values["ReplicasMaxInsertsInQueue"] = max_inserts_in_queue; + new_values["ReplicasMaxMergesInQueue"] = max_merges_in_queue; - set("ReplicasSumQueueSize", sum_queue_size); - set("ReplicasSumInsertsInQueue", sum_inserts_in_queue); - set("ReplicasSumMergesInQueue", sum_merges_in_queue); + new_values["ReplicasSumQueueSize"] = sum_queue_size; + new_values["ReplicasSumInsertsInQueue"] = sum_inserts_in_queue; + new_values["ReplicasSumMergesInQueue"] = sum_merges_in_queue; - set("ReplicasMaxAbsoluteDelay", max_absolute_delay); - set("ReplicasMaxRelativeDelay", max_relative_delay); + new_values["ReplicasMaxAbsoluteDelay"] = max_absolute_delay; + new_values["ReplicasMaxRelativeDelay"] = max_relative_delay; - set("MaxPartCountForPartition", max_part_count_for_partition); + new_values["MaxPartCountForPartition"] = max_part_count_for_partition; - set("NumberOfDatabases", number_of_databases); - set("NumberOfTables", total_number_of_tables); + new_values["NumberOfDatabases"] = number_of_databases; + new_values["NumberOfTables"] = total_number_of_tables; } #if USE_JEMALLOC && JEMALLOC_VERSION_MAJOR >= 4 @@ -262,7 +260,7 @@ void AsynchronousMetrics::update() TYPE value{}; \ size_t size = sizeof(value); \ mallctl("stats." NAME, &value, &size, nullptr, 0); \ - set("jemalloc." NAME, value); \ + new_values["jemalloc." NAME] = value; \ } while (false); FOR_EACH_METRIC(GET_METRIC) @@ -273,6 +271,16 @@ void AsynchronousMetrics::update() #endif /// Add more metrics as you wish. + + // Log the new metrics. + if (auto log = context.getAsynchronousMetricLog()) + { + log->addValues(new_values); + } + + // Finally, update the current metrics. + std::lock_guard lock(mutex); + values = new_values; } } diff --git a/src/Interpreters/AsynchronousMetrics.h b/src/Interpreters/AsynchronousMetrics.h index ce6c0aae552..6817f545c8f 100644 --- a/src/Interpreters/AsynchronousMetrics.h +++ b/src/Interpreters/AsynchronousMetrics.h @@ -14,6 +14,9 @@ namespace DB class Context; +typedef double AsynchronousMetricValue; +typedef std::unordered_map AsynchronousMetricValues; + /** Periodically (each minute, starting at 30 seconds offset) * calculates and updates some metrics, @@ -29,21 +32,17 @@ public: ~AsynchronousMetrics(); - using Value = double; - using Container = std::unordered_map; /// Returns copy of all values. - Container getValues() const; + AsynchronousMetricValues getValues() const; private: Context & context; - bool quit {false}; - std::mutex wait_mutex; + mutable std::mutex mutex; std::condition_variable wait_cond; - - Container container; - mutable std::mutex container_mutex; + bool quit {false}; + AsynchronousMetricValues values; #if defined(OS_LINUX) MemoryStatisticsOS memory_stat; @@ -53,8 +52,6 @@ private: void run(); void update(); - - void set(const std::string & name, Value value); }; } diff --git a/src/Interpreters/BloomFilter.cpp b/src/Interpreters/BloomFilter.cpp index 010f29d8449..7e4e25c2607 100644 --- a/src/Interpreters/BloomFilter.cpp +++ b/src/Interpreters/BloomFilter.cpp @@ -18,6 +18,11 @@ namespace ErrorCodes static constexpr UInt64 SEED_GEN_A = 845897321; static constexpr UInt64 SEED_GEN_B = 217728422; +BloomFilter::BloomFilter(const BloomFilterParameters & params) + : BloomFilter(params.filter_size, params.filter_hashes, params.seed) +{ +} + BloomFilter::BloomFilter(size_t size_, size_t hashes_, size_t seed_) : size(size_), hashes(hashes_), seed(seed_), words((size + sizeof(UnderType) - 1) / sizeof(UnderType)), filter(words, 0) {} diff --git a/src/Interpreters/BloomFilter.h b/src/Interpreters/BloomFilter.h index 46ab3b6e82d..941194c4a70 100644 --- a/src/Interpreters/BloomFilter.h +++ b/src/Interpreters/BloomFilter.h @@ -11,6 +11,15 @@ namespace DB { +struct BloomFilterParameters +{ + /// size of filter in bytes. + size_t filter_size; + /// number of used hash functions. + size_t filter_hashes; + /// random seed for hash functions generation. + size_t seed; +}; class BloomFilter { @@ -19,6 +28,7 @@ public: using UnderType = UInt64; using Container = std::vector; + BloomFilter(const BloomFilterParameters & params); /// size -- size of filter in bytes. /// hashes -- number of used hash functions. /// seed -- random seed for hash functions generation. diff --git a/src/Interpreters/Cluster.cpp b/src/Interpreters/Cluster.cpp index 7fad9af8960..c01d0188e5c 100644 --- a/src/Interpreters/Cluster.cpp +++ b/src/Interpreters/Cluster.cpp @@ -139,8 +139,11 @@ String Cluster::Address::toFullString(bool use_compact_format) const { if (use_compact_format) { - return ((shard_index == 0) ? "" : "shard" + std::to_string(shard_index)) - + ((replica_index == 0) ? "" : "_replica" + std::to_string(replica_index)); + if (shard_index == 0 || replica_index == 0) + // shard_num/replica_num like in system.clusters table + throw Exception("shard_num/replica_num cannot be zero", ErrorCodes::LOGICAL_ERROR); + + return "shard" + std::to_string(shard_index) + "_replica" + std::to_string(replica_index); } else { @@ -284,7 +287,7 @@ Cluster::Cluster(const Poco::Util::AbstractConfiguration & config, const Setting const auto & prefix = config_prefix + key; const auto weight = config.getInt(prefix + ".weight", default_weight); - addresses.emplace_back(config, prefix); + addresses.emplace_back(config, prefix, current_shard_num, 1); const auto & address = addresses.back(); ShardInfo info; diff --git a/src/Interpreters/ClusterProxy/SelectStreamFactory.cpp b/src/Interpreters/ClusterProxy/SelectStreamFactory.cpp index 5d41b0e87ce..bfa6fae0977 100644 --- a/src/Interpreters/ClusterProxy/SelectStreamFactory.cpp +++ b/src/Interpreters/ClusterProxy/SelectStreamFactory.cpp @@ -1,8 +1,6 @@ #include #include #include -#include -#include #include #include #include @@ -13,9 +11,8 @@ #include #include #include -#include -#include -#include +#include +#include namespace ProfileEvents { @@ -118,13 +115,13 @@ void SelectStreamFactory::createForShard( const SelectQueryInfo &, Pipes & res) { - bool force_add_agg_info = processed_stage == QueryProcessingStage::WithMergeableState; - bool add_totals_port = false; - bool add_extremes_port = false; + bool add_agg_info = processed_stage == QueryProcessingStage::WithMergeableState; + bool add_totals = false; + bool add_extremes = false; if (processed_stage == QueryProcessingStage::Complete) { - add_totals_port = query_ast->as().group_by_with_totals; - add_extremes_port = context.getSettingsRef().extremes; + add_totals = query_ast->as().group_by_with_totals; + add_extremes = context.getSettingsRef().extremes; } auto modified_query_ast = query_ast->clone(); @@ -140,20 +137,13 @@ void SelectStreamFactory::createForShard( auto emplace_remote_stream = [&]() { - auto stream = std::make_shared( + auto remote_query_executor = std::make_shared( shard_info.pool, modified_query, header, context, nullptr, throttler, scalars, external_tables, processed_stage); - stream->setPoolMode(PoolMode::GET_MANY); + remote_query_executor->setPoolMode(PoolMode::GET_MANY); if (!table_func_ptr) - stream->setMainTable(main_table); + remote_query_executor->setMainTable(main_table); - auto source = std::make_shared(std::move(stream), force_add_agg_info); - - if (add_totals_port) - source->addTotalsPort(); - if (add_extremes_port) - source->addExtremesPort(); - - res.emplace_back(std::move(source)); + res.emplace_back(createRemoteSourcePipe(remote_query_executor, add_agg_info, add_totals, add_extremes)); }; const auto & settings = context.getSettingsRef(); @@ -246,8 +236,8 @@ void SelectStreamFactory::createForShard( auto lazily_create_stream = [ pool = shard_info.pool, shard_num = shard_info.shard_num, modified_query, header = header, modified_query_ast, context, throttler, main_table = main_table, table_func_ptr = table_func_ptr, scalars = scalars, external_tables = external_tables, - stage = processed_stage, local_delay]() - -> BlockInputStreamPtr + stage = processed_stage, local_delay, add_agg_info, add_totals, add_extremes]() + -> Pipe { auto current_settings = context.getSettingsRef(); auto timeouts = ConnectionTimeouts::getTCPTimeoutsWithFailover( @@ -277,8 +267,7 @@ void SelectStreamFactory::createForShard( } if (try_results.empty() || local_delay < max_remote_delay) - return std::make_shared( - createLocalStream(modified_query_ast, header, context, stage)); + return createLocalStream(modified_query_ast, header, context, stage).getPipe(); else { std::vector connections; @@ -286,20 +275,14 @@ void SelectStreamFactory::createForShard( for (auto & try_result : try_results) connections.emplace_back(std::move(try_result.entry)); - return std::make_shared( + auto remote_query_executor = std::make_shared( std::move(connections), modified_query, header, context, nullptr, throttler, scalars, external_tables, stage); + + return createRemoteSourcePipe(remote_query_executor, add_agg_info, add_totals, add_extremes); } }; - auto lazy_stream = std::make_shared("LazyShardWithLocalReplica", header, lazily_create_stream); - auto source = std::make_shared(std::move(lazy_stream), force_add_agg_info); - - if (add_totals_port) - source->addTotalsPort(); - if (add_extremes_port) - source->addExtremesPort(); - - res.emplace_back(std::move(source)); + res.emplace_back(createDelayedPipe(header, lazily_create_stream)); } else emplace_remote_stream(); diff --git a/src/Interpreters/CollectJoinOnKeysVisitor.h b/src/Interpreters/CollectJoinOnKeysVisitor.h index 8a1836a97ac..c9b106d21dd 100644 --- a/src/Interpreters/CollectJoinOnKeysVisitor.h +++ b/src/Interpreters/CollectJoinOnKeysVisitor.h @@ -26,8 +26,8 @@ public: struct Data { TableJoin & analyzed_join; - const TableWithColumnNames & left_table; - const TableWithColumnNames & right_table; + const TableWithColumnNamesAndTypes & left_table; + const TableWithColumnNamesAndTypes & right_table; const Aliases & aliases; const bool is_asof{false}; ASTPtr asof_left_key{}; diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 5e2f4ecadab..cb780443e03 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -101,7 +102,6 @@ namespace ErrorCodes extern const int SESSION_NOT_FOUND; extern const int SESSION_IS_LOCKED; extern const int LOGICAL_ERROR; - extern const int UNKNOWN_SCALAR; extern const int AUTHENTICATION_FAILED; extern const int NOT_IMPLEMENTED; } @@ -166,6 +166,8 @@ public: if (!session.unique()) throw Exception("Session is locked by a concurrent client.", ErrorCodes::SESSION_IS_LOCKED); + session->context.client_info = context.client_info; + return session; } @@ -351,6 +353,7 @@ struct ContextShared String format_schema_path; /// Path to a directory that contains schema files used by input formats. ActionLocksManagerPtr action_locks_manager; /// Set of storages' action lockers std::optional system_logs; /// Used to log queries and operations on parts + std::optional storage_s3_settings; /// Settings of S3 storage RemoteHostFilter remote_host_filter; /// Allowed URL from config.xml @@ -821,7 +824,11 @@ const Block & Context::getScalar(const String & name) const { auto it = scalars.find(name); if (scalars.end() == it) - throw Exception("Scalar " + backQuoteIfNeed(name) + " doesn't exist (internal bug)", ErrorCodes::UNKNOWN_SCALAR); + { + // This should be a logical error, but it fails the sql_fuzz test too + // often, so 'bad arguments' for now. + throw Exception("Scalar " + backQuoteIfNeed(name) + " doesn't exist (internal bug)", ErrorCodes::BAD_ARGUMENTS); + } return it->second; } @@ -975,7 +982,16 @@ void Context::setSetting(const StringRef & name, const Field & value) void Context::applySettingChange(const SettingChange & change) { - setSetting(change.name, change.value); + try + { + setSetting(change.name, change.value); + } + catch (Exception & e) + { + e.addMessage(fmt::format("in attempt to set the value of setting '{}' to {}", + change.name, applyVisitor(FieldVisitorToString(), change.value))); + throw; + } } @@ -1671,6 +1687,17 @@ std::shared_ptr Context::getMetricLog() } +std::shared_ptr Context::getAsynchronousMetricLog() +{ + auto lock = getLock(); + + if (!shared->system_logs) + return {}; + + return shared->system_logs->asynchronous_metric_log; +} + + CompressionCodecPtr Context::chooseCompressionCodec(size_t part_size, double part_size_ratio) const { auto lock = getLock(); @@ -1764,6 +1791,11 @@ void Context::updateStorageConfiguration(const Poco::Util::AbstractConfiguration LOG_ERROR(shared->log, "An error has occured while reloading storage policies, storage policies were not applied: {}", e.message()); } } + + if (shared->storage_s3_settings) + { + shared->storage_s3_settings->loadFromConfig("s3", config); + } } @@ -1782,6 +1814,18 @@ const MergeTreeSettings & Context::getMergeTreeSettings() const return *shared->merge_tree_settings; } +const StorageS3Settings & Context::getStorageS3Settings() const +{ + auto lock = getLock(); + + if (!shared->storage_s3_settings) + { + const auto & config = getConfigRef(); + shared->storage_s3_settings.emplace().loadFromConfig("s3", config); + } + + return *shared->storage_s3_settings; +} void Context::checkCanBeDropped(const String & database, const String & table, const size_t & size, const size_t & max_size_to_drop) const { diff --git a/src/Interpreters/Context.h b/src/Interpreters/Context.h index 864468c0663..5a4e959229f 100644 --- a/src/Interpreters/Context.h +++ b/src/Interpreters/Context.h @@ -80,7 +80,9 @@ class PartLog; class TextLog; class TraceLog; class MetricLog; +class AsynchronousMetricLog; struct MergeTreeSettings; +class StorageS3Settings; class IDatabase; class DDLWorker; class ITableFunction; @@ -525,12 +527,14 @@ public: std::shared_ptr getTraceLog(); std::shared_ptr getTextLog(); std::shared_ptr getMetricLog(); + std::shared_ptr getAsynchronousMetricLog(); /// Returns an object used to log opertaions with parts if it possible. /// Provide table name to make required cheks. std::shared_ptr getPartLog(const String & part_database); const MergeTreeSettings & getMergeTreeSettings() const; + const StorageS3Settings & getStorageS3Settings() const; /// Prevents DROP TABLE if its size is greater than max_size (50GB by default, max_size=0 turn off this check) void setMaxTableSizeToDrop(size_t max_size); diff --git a/src/Interpreters/DDLWorker.cpp b/src/Interpreters/DDLWorker.cpp index dac51b21081..9f89fa9199b 100644 --- a/src/Interpreters/DDLWorker.cpp +++ b/src/Interpreters/DDLWorker.cpp @@ -422,7 +422,7 @@ void DDLWorker::processTasks() } catch (const Coordination::Exception & e) { - if (server_startup && e.code == Coordination::ZNONODE) + if (server_startup && e.code == Coordination::Error::ZNONODE) { LOG_WARNING(log, "ZooKeeper NONODE error during startup. Ignoring entry {} ({}) : {}", task.entry_name, task.entry.query, getCurrentExceptionMessage(true)); } @@ -603,15 +603,15 @@ void DDLWorker::processTask(DDLTask & task, const ZooKeeperPtr & zookeeper) auto code = zookeeper->tryCreate(active_node_path, "", zkutil::CreateMode::Ephemeral, dummy); - if (code == Coordination::ZOK || code == Coordination::ZNODEEXISTS) + if (code == Coordination::Error::ZOK || code == Coordination::Error::ZNODEEXISTS) { // Ok } - else if (code == Coordination::ZNONODE) + else if (code == Coordination::Error::ZNONODE) { /// There is no parent createStatusDirs(task.entry_path, zookeeper); - if (Coordination::ZOK != zookeeper->tryCreate(active_node_path, "", zkutil::CreateMode::Ephemeral, dummy)) + if (Coordination::Error::ZOK != zookeeper->tryCreate(active_node_path, "", zkutil::CreateMode::Ephemeral, dummy)) throw Coordination::Exception(code, active_node_path); } else @@ -915,8 +915,9 @@ void DDLWorker::createStatusDirs(const std::string & node_path, const ZooKeeperP ops.emplace_back(std::make_shared(std::move(request))); } Coordination::Responses responses; - int code = zookeeper->tryMulti(ops, responses); - if (code && code != Coordination::ZNODEEXISTS) + Coordination::Error code = zookeeper->tryMulti(ops, responses); + if (code != Coordination::Error::ZOK + && code != Coordination::Error::ZNODEEXISTS) throw Coordination::Exception(code); } @@ -1013,7 +1014,7 @@ void DDLWorker::runMainThread() } } } - else if (e.code == Coordination::ZNONODE) + else if (e.code == Coordination::Error::ZNONODE) { LOG_ERROR(log, "ZooKeeper error: {}", getCurrentExceptionMessage(true)); } @@ -1201,8 +1202,8 @@ private: static Strings getChildrenAllowNoNode(const std::shared_ptr & zookeeper, const String & node_path) { Strings res; - int code = zookeeper->tryGetChildren(node_path, res); - if (code && code != Coordination::ZNONODE) + Coordination::Error code = zookeeper->tryGetChildren(node_path, res); + if (code != Coordination::Error::ZOK && code != Coordination::Error::ZNONODE) throw Coordination::Exception(code, node_path); return res; } diff --git a/src/Interpreters/DatabaseAndTableWithAlias.h b/src/Interpreters/DatabaseAndTableWithAlias.h index adb0829a54e..d4a1a582fdc 100644 --- a/src/Interpreters/DatabaseAndTableWithAlias.h +++ b/src/Interpreters/DatabaseAndTableWithAlias.h @@ -45,34 +45,6 @@ struct DatabaseAndTableWithAlias } }; -struct TableWithColumnNames -{ - DatabaseAndTableWithAlias table; - Names columns; - Names hidden_columns; /// Not general columns like MATERIALIZED and ALIAS. They are omitted in * and t.* results. - - TableWithColumnNames(const DatabaseAndTableWithAlias & table_, const Names & columns_) - : table(table_) - , columns(columns_) - { - columns_set.insert(columns.begin(), columns.end()); - } - - TableWithColumnNames(const DatabaseAndTableWithAlias table_, Names && columns_, Names && hidden_columns_) - : table(table_) - , columns(columns_) - , hidden_columns(hidden_columns_) - { - columns_set.insert(columns.begin(), columns.end()); - columns_set.insert(hidden_columns.begin(), hidden_columns.end()); - } - - bool hasColumn(const String & name) const { return columns_set.count(name); } - -private: - NameSet columns_set; -}; - struct TableWithColumnNamesAndTypes { DatabaseAndTableWithAlias table; @@ -96,21 +68,6 @@ struct TableWithColumnNamesAndTypes names.insert(col.name); } - TableWithColumnNames removeTypes() const - { - Names out_columns; - out_columns.reserve(columns.size()); - for (auto & col : columns) - out_columns.push_back(col.name); - - Names out_hidden_columns; - out_hidden_columns.reserve(hidden_columns.size()); - for (auto & col : hidden_columns) - out_hidden_columns.push_back(col.name); - - return TableWithColumnNames(table, std::move(out_columns), std::move(out_hidden_columns)); - } - private: NameSet names; }; @@ -118,7 +75,6 @@ private: std::vector getDatabaseAndTables(const ASTSelectQuery & select_query, const String & current_database); std::optional getDatabaseAndTable(const ASTSelectQuery & select, size_t table_number); -using TablesWithColumnNames = std::vector; -using TablesWithColumnNamesAndTypes = std::vector; +using TablesWithColumns = std::vector; } diff --git a/src/Interpreters/DatabaseCatalog.cpp b/src/Interpreters/DatabaseCatalog.cpp index 4871d8d37aa..3171f84ec9c 100644 --- a/src/Interpreters/DatabaseCatalog.cpp +++ b/src/Interpreters/DatabaseCatalog.cpp @@ -25,7 +25,6 @@ namespace ErrorCodes extern const int DATABASE_NOT_EMPTY; extern const int DATABASE_ACCESS_DENIED; extern const int LOGICAL_ERROR; - extern const int NULL_POINTER_DEREFERENCE; } TemporaryTableHolder::TemporaryTableHolder(const Context & context_, @@ -385,38 +384,46 @@ void DatabaseCatalog::updateUUIDMapping(const UUID & uuid, DatabasePtr database, it->second = std::make_pair(std::move(database), std::move(table)); } +std::unique_ptr DatabaseCatalog::database_catalog; + DatabaseCatalog::DatabaseCatalog(Context * global_context_) : global_context(global_context_), log(&Poco::Logger::get("DatabaseCatalog")) { if (!global_context) - throw Exception("DatabaseCatalog is not initialized. It's a bug.", ErrorCodes::NULL_POINTER_DEREFERENCE); + throw Exception("DatabaseCatalog is not initialized. It's a bug.", ErrorCodes::LOGICAL_ERROR); } DatabaseCatalog & DatabaseCatalog::init(Context * global_context_) { - static DatabaseCatalog database_catalog(global_context_); - return database_catalog; + if (database_catalog) + { + throw Exception("Database catalog is initialized twice. This is a bug.", + ErrorCodes::LOGICAL_ERROR); + } + + database_catalog.reset(new DatabaseCatalog(global_context_)); + + return *database_catalog; } DatabaseCatalog & DatabaseCatalog::instance() { - return init(nullptr); + if (!database_catalog) + { + throw Exception("Database catalog is not initialized. This is a bug.", + ErrorCodes::LOGICAL_ERROR); + } + + return *database_catalog; } void DatabaseCatalog::shutdown() { - try + // The catalog might not be initialized yet by init(global_context). It can + // happen if some exception was thrown on first steps of startup. + if (database_catalog) { - instance().shutdownImpl(); - } - catch (const Exception & e) - { - /// If catalog was not initialized yet by init(global_context), instance() throws NULL_POINTER_DEREFERENCE. - /// It can happen if some exception was thrown on first steps of startup (e.g. command line arguments parsing). - /// Ignore it. - if (e.code() == ErrorCodes::NULL_POINTER_DEREFERENCE) - return; - throw; + database_catalog->shutdownImpl(); } } @@ -724,5 +731,3 @@ DDLGuard::~DDLGuard() } } - - diff --git a/src/Interpreters/DatabaseCatalog.h b/src/Interpreters/DatabaseCatalog.h index aefed0f372d..540568927cc 100644 --- a/src/Interpreters/DatabaseCatalog.h +++ b/src/Interpreters/DatabaseCatalog.h @@ -169,6 +169,11 @@ public: void enqueueDroppedTableCleanup(StorageID table_id, StoragePtr table, String dropped_metadata_path, bool ignore_delay = false); private: + // The global instance of database catalog. unique_ptr is to allow + // deferred initialization. Thought I'd use std::optional, but I can't + // make emplace(global_context_) compile with private constructor ¯\_(ツ)_/¯. + static std::unique_ptr database_catalog; + DatabaseCatalog(Context * global_context_); void assertDatabaseExistsUnlocked(const String & database_name) const; void assertDatabaseDoesntExistUnlocked(const String & database_name) const; diff --git a/src/Interpreters/ExpressionAnalyzer.cpp b/src/Interpreters/ExpressionAnalyzer.cpp index b4988f00699..ecfa011f1c8 100644 --- a/src/Interpreters/ExpressionAnalyzer.cpp +++ b/src/Interpreters/ExpressionAnalyzer.cpp @@ -113,7 +113,7 @@ bool sanitizeBlock(Block & block) return false; col.column = col.type->createColumn(); } - else if (isColumnConst(*col.column) && !col.column->empty()) + else if (!col.column->empty()) col.column = col.column->cloneEmpty(); } return true; @@ -726,7 +726,8 @@ bool SelectQueryExpressionAnalyzer::appendWhere(ExpressionActionsChain & chain, return true; } -bool SelectQueryExpressionAnalyzer::appendGroupBy(ExpressionActionsChain & chain, bool only_types) +bool SelectQueryExpressionAnalyzer::appendGroupBy(ExpressionActionsChain & chain, bool only_types, bool optimize_aggregation_in_order, + ManyExpressionActions & group_by_elements_actions) { const auto * select_query = getAggregatingQuery(); @@ -743,6 +744,20 @@ bool SelectQueryExpressionAnalyzer::appendGroupBy(ExpressionActionsChain & chain getRootActions(ast, only_types, step.actions); } + if (optimize_aggregation_in_order) + { + auto all_columns = sourceWithJoinedColumns(); + for (auto & child : asts) + { + group_by_elements_actions.emplace_back(std::make_shared(all_columns, context)); + getRootActions(child, only_types, group_by_elements_actions.back()); + } +// std::cerr << "group_by_elements_actions\n"; +// for (const auto & elem : group_by_elements_actions) { +// std::cerr << elem->dumpActions() << "\n"; +// } + } + return true; } @@ -834,8 +849,11 @@ bool SelectQueryExpressionAnalyzer::appendOrderBy(ExpressionActionsChain & chain order_by_elements_actions.emplace_back(std::make_shared(all_columns, context)); getRootActions(child, only_types, order_by_elements_actions.back()); } +// std::cerr << "order_by_elements_actions\n"; +// for (const auto & elem : order_by_elements_actions) { +// std::cerr << elem->dumpActions() << "\n"; +// } } - return true; } @@ -1115,7 +1133,12 @@ ExpressionAnalysisResult::ExpressionAnalysisResult( if (need_aggregate) { - query_analyzer.appendGroupBy(chain, only_types || !first_stage); + /// TODO correct conditions + optimize_aggregation_in_order = + context.getSettingsRef().optimize_aggregation_in_order + && storage && query.groupBy(); + + query_analyzer.appendGroupBy(chain, only_types || !first_stage, optimize_aggregation_in_order, group_by_elements_actions); query_analyzer.appendAggregateFunctionsArguments(chain, only_types || !first_stage); before_aggregation = chain.getLastActions(); @@ -1128,13 +1151,13 @@ ExpressionAnalysisResult::ExpressionAnalysisResult( } } - bool has_stream_with_non_joned_rows = (before_join && before_join->getTableJoinAlgo()->hasStreamWithNonJoinedRows()); + bool has_stream_with_non_joined_rows = (before_join && before_join->getTableJoinAlgo()->hasStreamWithNonJoinedRows()); optimize_read_in_order = settings.optimize_read_in_order && storage && query.orderBy() && !query_analyzer.hasAggregation() && !query.final() - && !has_stream_with_non_joned_rows; + && !has_stream_with_non_joined_rows; /// If there is aggregation, we execute expressions in SELECT and ORDER BY on the initiating server, otherwise on the source servers. query_analyzer.appendSelect(chain, only_types || (need_aggregate ? !second_stage : !first_stage)); diff --git a/src/Interpreters/ExpressionAnalyzer.h b/src/Interpreters/ExpressionAnalyzer.h index 1afb289430e..c69cb61162f 100644 --- a/src/Interpreters/ExpressionAnalyzer.h +++ b/src/Interpreters/ExpressionAnalyzer.h @@ -174,6 +174,7 @@ struct ExpressionAnalysisResult bool remove_where_filter = false; bool optimize_read_in_order = false; + bool optimize_aggregation_in_order = false; ExpressionActionsPtr before_join; /// including JOIN ExpressionActionsPtr before_where; @@ -195,6 +196,7 @@ struct ExpressionAnalysisResult ConstantFilterDescription where_constant_filter_description; /// Actions by every element of ORDER BY ManyExpressionActions order_by_elements_actions; + ManyExpressionActions group_by_elements_actions; ExpressionAnalysisResult() = default; @@ -206,7 +208,9 @@ struct ExpressionAnalysisResult const FilterInfoPtr & filter_info, const Block & source_header); + /// Filter for row-level security. bool hasFilter() const { return filter_info.get(); } + bool hasJoin() const { return before_join.get(); } bool hasPrewhere() const { return prewhere_info.get(); } bool hasWhere() const { return before_where.get(); } @@ -303,7 +307,7 @@ private: /// Columns in `additional_required_columns` will not be removed (they can be used for e.g. sampling or FINAL modifier). bool appendPrewhere(ExpressionActionsChain & chain, bool only_types, const Names & additional_required_columns); bool appendWhere(ExpressionActionsChain & chain, bool only_types); - bool appendGroupBy(ExpressionActionsChain & chain, bool only_types); + bool appendGroupBy(ExpressionActionsChain & chain, bool only_types, bool optimize_aggregation_in_order, ManyExpressionActions &); void appendAggregateFunctionsArguments(ExpressionActionsChain & chain, bool only_types); /// After aggregation: diff --git a/src/Interpreters/ExtractExpressionInfoVisitor.cpp b/src/Interpreters/ExtractExpressionInfoVisitor.cpp index f0ca33b6b8b..5f7754d315a 100644 --- a/src/Interpreters/ExtractExpressionInfoVisitor.cpp +++ b/src/Interpreters/ExtractExpressionInfoVisitor.cpp @@ -38,10 +38,10 @@ void ExpressionInfoMatcher::visit(const ASTIdentifier & identifier, const ASTPtr { for (size_t index = 0; index < data.tables.size(); ++index) { - const auto & columns = data.tables[index].columns; + const auto & table = data.tables[index]; // TODO: make sure no collision ever happens - if (std::find(columns.begin(), columns.end(), identifier.name) != columns.end()) + if (table.hasColumn(identifier.name)) { data.unique_reference_tables_pos.emplace(index); break; diff --git a/src/Interpreters/ExtractExpressionInfoVisitor.h b/src/Interpreters/ExtractExpressionInfoVisitor.h index 65d23057e52..a412704edcc 100644 --- a/src/Interpreters/ExtractExpressionInfoVisitor.h +++ b/src/Interpreters/ExtractExpressionInfoVisitor.h @@ -16,7 +16,7 @@ struct ExpressionInfoMatcher struct Data { const Context & context; - const std::vector & tables; + const TablesWithColumns & tables; bool is_array_join = false; bool is_stateful_function = false; diff --git a/src/Interpreters/HashJoin.cpp b/src/Interpreters/HashJoin.cpp index 5fd2239d104..d18649c4c17 100644 --- a/src/Interpreters/HashJoin.cpp +++ b/src/Interpreters/HashJoin.cpp @@ -35,6 +35,8 @@ namespace ErrorCodes { extern const int BAD_TYPE_OF_FIELD; extern const int NOT_IMPLEMENTED; + extern const int NO_SUCH_COLUMN_IN_TABLE; + extern const int INCOMPATIBLE_TYPE_OF_JOIN; extern const int UNSUPPORTED_JOIN_KEYS; extern const int LOGICAL_ERROR; extern const int SET_SIZE_LIMIT_EXCEEDED; @@ -105,7 +107,7 @@ static ColumnWithTypeAndName correctNullability(ColumnWithTypeAndName && column, { if (nullable) { - JoinCommon::convertColumnToNullable(column); + JoinCommon::convertColumnToNullable(column, true); if (column.type->isNullable() && !negative_null_map.empty()) { MutableColumnPtr mutable_column = IColumn::mutate(std::move(column.column)); @@ -1230,7 +1232,7 @@ DataTypePtr HashJoin::joinGetReturnType(const String & column_name, bool or_null std::shared_lock lock(data->rwlock); if (!sample_block_with_columns_to_add.has(column_name)) - throw Exception("StorageJoin doesn't contain column " + column_name, ErrorCodes::LOGICAL_ERROR); + throw Exception("StorageJoin doesn't contain column " + column_name, ErrorCodes::NO_SUCH_COLUMN_IN_TABLE); auto elem = sample_block_with_columns_to_add.getByName(column_name); if (or_null) elem.type = makeNullable(elem.type); @@ -1254,7 +1256,7 @@ void HashJoin::joinGet(Block & block, const String & column_name, bool or_null) std::shared_lock lock(data->rwlock); if (key_names_right.size() != 1) - throw Exception("joinGet only supports StorageJoin containing exactly one key", ErrorCodes::LOGICAL_ERROR); + throw Exception("joinGet only supports StorageJoin containing exactly one key", ErrorCodes::UNSUPPORTED_JOIN_KEYS); checkTypeOfKey(block, right_table_keys); @@ -1269,7 +1271,7 @@ void HashJoin::joinGet(Block & block, const String & column_name, bool or_null) joinGetImpl(block, {elem}, std::get(data->maps)); } else - throw Exception("joinGet only supports StorageJoin of type Left Any", ErrorCodes::LOGICAL_ERROR); + throw Exception("joinGet only supports StorageJoin of type Left Any", ErrorCodes::INCOMPATIBLE_TYPE_OF_JOIN); } diff --git a/src/Interpreters/IdentifierSemantic.cpp b/src/Interpreters/IdentifierSemantic.cpp index 26bb8e6261d..8f254b50400 100644 --- a/src/Interpreters/IdentifierSemantic.cpp +++ b/src/Interpreters/IdentifierSemantic.cpp @@ -125,12 +125,6 @@ std::optional IdentifierSemantic::chooseTable(const ASTIdentifier & iden return tryChooseTable(identifier, tables, ambiguous); } -std::optional IdentifierSemantic::chooseTable(const ASTIdentifier & identifier, const std::vector & tables, - bool ambiguous) -{ - return tryChooseTable(identifier, tables, ambiguous); -} - std::optional IdentifierSemantic::chooseTable(const ASTIdentifier & identifier, const std::vector & tables, bool ambiguous) { @@ -196,19 +190,14 @@ IdentifierSemantic::ColumnMatch IdentifierSemantic::canReferColumnToTable(const return ColumnMatch::NoMatch; } -IdentifierSemantic::ColumnMatch IdentifierSemantic::canReferColumnToTable(const ASTIdentifier & identifier, - const TableWithColumnNames & db_and_table) -{ - /// TODO: ColumnName match logic is disabled cause caller's code is not ready for it - return canReferColumnToTable(identifier, db_and_table.table); -} - IdentifierSemantic::ColumnMatch IdentifierSemantic::canReferColumnToTable(const ASTIdentifier & identifier, const TableWithColumnNamesAndTypes & db_and_table) { ColumnMatch match = canReferColumnToTable(identifier, db_and_table.table); +#if 0 if (match == ColumnMatch::NoMatch && identifier.isShort() && db_and_table.hasColumn(identifier.shortName())) match = ColumnMatch::ColumnName; +#endif return match; } diff --git a/src/Interpreters/IdentifierSemantic.h b/src/Interpreters/IdentifierSemantic.h index 81019f65b1f..7e84e10a26f 100644 --- a/src/Interpreters/IdentifierSemantic.h +++ b/src/Interpreters/IdentifierSemantic.h @@ -41,7 +41,6 @@ struct IdentifierSemantic static std::optional extractNestedName(const ASTIdentifier & identifier, const String & table_name); static ColumnMatch canReferColumnToTable(const ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table); - static ColumnMatch canReferColumnToTable(const ASTIdentifier & identifier, const TableWithColumnNames & db_and_table); static ColumnMatch canReferColumnToTable(const ASTIdentifier & identifier, const TableWithColumnNamesAndTypes & db_and_table); static void setColumnShortName(ASTIdentifier & identifier, const DatabaseAndTableWithAlias & db_and_table); @@ -53,8 +52,6 @@ struct IdentifierSemantic static std::optional getMembership(const ASTIdentifier & identifier); static std::optional chooseTable(const ASTIdentifier &, const std::vector & tables, bool allow_ambiguous = false); - static std::optional chooseTable(const ASTIdentifier &, const std::vector & tables, - bool allow_ambiguous = false); static std::optional chooseTable(const ASTIdentifier &, const std::vector & tables, bool allow_ambiguous = false); diff --git a/src/Interpreters/InterpreterCreateQuery.cpp b/src/Interpreters/InterpreterCreateQuery.cpp index 369849d7ea6..8a7b9a245e4 100644 --- a/src/Interpreters/InterpreterCreateQuery.cpp +++ b/src/Interpreters/InterpreterCreateQuery.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -252,8 +253,8 @@ ASTPtr InterpreterCreateQuery::formatIndices(const IndicesDescription & indices) { auto res = std::make_shared(); - for (const auto & index : indices.indices) - res->children.push_back(index->clone()); + for (const auto & index : indices) + res->children.push_back(index.definition_ast->clone()); return res; } @@ -399,8 +400,8 @@ InterpreterCreateQuery::TableProperties InterpreterCreateQuery::setProperties(AS if (create.columns_list->indices) for (const auto & index : create.columns_list->indices->children) - properties.indices.indices.push_back( - std::dynamic_pointer_cast(index->clone())); + properties.indices.push_back( + IndexDescription::getIndexFromAST(index->clone(), properties.columns, context)); properties.constraints = getConstraintsDescription(create.columns_list->constraints); } @@ -417,7 +418,7 @@ InterpreterCreateQuery::TableProperties InterpreterCreateQuery::setProperties(AS /// Secondary indices make sense only for MergeTree family of storage engines. /// We should not copy them for other storages. if (create.storage && endsWith(create.storage->engine->name, "MergeTree")) - properties.indices = as_storage->getIndices(); + properties.indices = as_storage->getSecondaryIndices(); properties.constraints = as_storage->getConstraints(); } @@ -689,6 +690,10 @@ bool InterpreterCreateQuery::doCreateTable(ASTCreateQuery & create, /// Then background task is created by "startup" method. And when destructor of a table object is called, background task is still active, /// and the task will use references to freed data. + /// Also note that "startup" method is exception-safe. If exception is thrown from "startup", + /// we can safely destroy the object without a call to "shutdown", because there is guarantee + /// that no background threads/similar resources remain after exception from "startup". + res->startup(); return true; } diff --git a/src/Interpreters/InterpreterCreateQuery.h b/src/Interpreters/InterpreterCreateQuery.h index 2ce98b7b2c2..cf27f68ad73 100644 --- a/src/Interpreters/InterpreterCreateQuery.h +++ b/src/Interpreters/InterpreterCreateQuery.h @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/src/Interpreters/InterpreterInsertQuery.cpp b/src/Interpreters/InterpreterInsertQuery.cpp index c4a8e3041ac..7deed262eda 100644 --- a/src/Interpreters/InterpreterInsertQuery.cpp +++ b/src/Interpreters/InterpreterInsertQuery.cpp @@ -209,10 +209,7 @@ BlockIO InterpreterInsertQuery::execute() if (table->supportsParallelInsert() && settings.max_insert_threads > 1) out_streams_size = std::min(size_t(settings.max_insert_threads), res.pipeline.getNumStreams()); - if (out_streams_size == 1) - res.pipeline.addPipe({std::make_shared(res.pipeline.getHeader(), res.pipeline.getNumStreams())}); - else - res.pipeline.resize(out_streams_size); + res.pipeline.resize(out_streams_size); } else if (query.watch) { diff --git a/src/Interpreters/InterpreterSelectQuery.cpp b/src/Interpreters/InterpreterSelectQuery.cpp index 4728c214db0..98cf36cc30b 100644 --- a/src/Interpreters/InterpreterSelectQuery.cpp +++ b/src/Interpreters/InterpreterSelectQuery.cpp @@ -1,9 +1,6 @@ #include -#include -#include #include #include -#include #include #include #include @@ -74,6 +71,8 @@ #include #include #include +#include +#include namespace DB @@ -95,7 +94,8 @@ namespace ErrorCodes } /// Assumes `storage` is set and the table filter (row-level security) is not empty. -String InterpreterSelectQuery::generateFilterActions(ExpressionActionsPtr & actions, const ASTPtr & row_policy_filter, const Names & prerequisite_columns) const +String InterpreterSelectQuery::generateFilterActions( + ExpressionActionsPtr & actions, const ASTPtr & row_policy_filter, const Names & prerequisite_columns) const { const auto & db_name = table_id.getDatabaseName(); const auto & table_name = table_id.getTableName(); @@ -475,8 +475,7 @@ Block InterpreterSelectQuery::getSampleBlockImpl() second_stage, options.only_analyze, filter_info, - source_header - ); + source_header); if (options.to_stage == QueryProcessingStage::Enum::FetchColumns) { @@ -604,6 +603,20 @@ static SortDescription getSortDescription(const ASTSelectQuery & query, const Co return order_descr; } +static SortDescription getSortDescriptionFromGroupBy(const ASTSelectQuery & query) +{ + SortDescription order_descr; + order_descr.reserve(query.groupBy()->children.size()); + + for (const auto & elem : query.groupBy()->children) + { + String name = elem->getColumnName(); + order_descr.emplace_back(name, 1, 1); + } + + return order_descr; +} + static UInt64 getLimitUIntValue(const ASTPtr & node, const Context & context, const std::string & expr) { const auto & [field, type] = evaluateConstantExpression(node, context); @@ -651,8 +664,8 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn { /** Streams of data. When the query is executed in parallel, we have several data streams. * If there is no GROUP BY, then perform all operations before ORDER BY and LIMIT in parallel, then - * if there is an ORDER BY, then glue the streams using UnionBlockInputStream, and then MergeSortingBlockInputStream, - * if not, then glue it using UnionBlockInputStream, + * if there is an ORDER BY, then glue the streams using ResizeProcessor, and then MergeSorting transforms, + * if not, then glue it using ResizeProcessor, * then apply LIMIT. * If there is GROUP BY, then we will perform all operations up to GROUP BY, inclusive, in parallel; * a parallel GROUP BY will glue streams into one, @@ -742,7 +755,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn if (!expressions.second_stage && !expressions.need_aggregate && !expressions.hasHaving()) { if (expressions.has_order_by) - executeOrder(pipeline, query_info.input_sorting_info); + executeOrder(pipeline, query_info.input_order_info); if (expressions.has_order_by && query.limitLength()) executeDistinct(pipeline, false, expressions.selected_columns); @@ -835,7 +848,11 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn executeWhere(pipeline, expressions.before_where, expressions.remove_where_filter); if (expressions.need_aggregate) - executeAggregation(pipeline, expressions.before_aggregation, aggregate_overflow_row, aggregate_final); + { + executeAggregation(pipeline, expressions.before_aggregation, aggregate_overflow_row, aggregate_final, query_info.input_order_info); + /// We need to reset input order info, so that executeOrder can't use it + query_info.input_order_info.reset(); + } else { executeExpression(pipeline, expressions.before_order_and_select); @@ -901,7 +918,7 @@ void InterpreterSelectQuery::executeImpl(QueryPipeline & pipeline, const BlockIn if (!expressions.first_stage && !expressions.need_aggregate && !(query.group_by_with_totals && !aggregate_final)) executeMergeSorted(pipeline); else /// Otherwise, just sort. - executeOrder(pipeline, query_info.input_sorting_info); + executeOrder(pipeline, query_info.input_order_info); } /** Optimization - if there are several sources and there is LIMIT, then first apply the preliminary LIMIT, @@ -961,28 +978,19 @@ void InterpreterSelectQuery::executeFetchColumns( const Settings & settings = context->getSettingsRef(); /// Optimization for trivial query like SELECT count() FROM table. - auto check_trivial_count_query = [&]() -> std::optional + bool optimize_trivial_count = + syntax_analyzer_result->optimize_trivial_count + && storage + && !filter_info + && processing_stage == QueryProcessingStage::FetchColumns + && query_analyzer->hasAggregation() + && (query_analyzer->aggregates().size() == 1) + && typeid_cast(query_analyzer->aggregates()[0].function.get()); + + if (optimize_trivial_count) { - if (!settings.optimize_trivial_count_query || !syntax_analyzer_result->maybe_optimize_trivial_count || !storage - || query.sampleSize() || query.sampleOffset() || query.final() || query.prewhere() || query.where() || query.groupBy() - || !query_analyzer->hasAggregation() || processing_stage != QueryProcessingStage::FetchColumns) - return {}; - - const AggregateDescriptions & aggregates = query_analyzer->aggregates(); - - if (aggregates.size() != 1) - return {}; - - const AggregateDescription & desc = aggregates[0]; - if (typeid_cast(desc.function.get())) - return desc; - - return {}; - }; - - if (auto desc = check_trivial_count_query()) - { - auto func = desc->function; + const auto & desc = query_analyzer->aggregates()[0]; + const auto & func = desc.function; std::optional num_rows = storage->totalRows(); if (num_rows) { @@ -1001,13 +1009,13 @@ void InterpreterSelectQuery::executeFetchColumns( column->insertFrom(place); auto header = analysis_result.before_aggregation->getSampleBlock(); - size_t arguments_size = desc->argument_names.size(); + size_t arguments_size = desc.argument_names.size(); DataTypes argument_types(arguments_size); for (size_t j = 0; j < arguments_size; ++j) - argument_types[j] = header.getByName(desc->argument_names[j]).type; + argument_types[j] = header.getByName(desc.argument_names[j]).type; Block block_with_count{ - {std::move(column), std::make_shared(func, argument_types, desc->parameters), desc->column_name}}; + {std::move(column), std::make_shared(func, argument_types, desc.parameters), desc.column_name}}; auto istream = std::make_shared(block_with_count); pipeline.init(Pipe(std::make_shared(istream))); @@ -1277,15 +1285,21 @@ void InterpreterSelectQuery::executeFetchColumns( query_info.prewhere_info = prewhere_info; /// Create optimizer with prepared actions. - /// Maybe we will need to calc input_sorting_info later, e.g. while reading from StorageMerge. - if (analysis_result.optimize_read_in_order) + /// Maybe we will need to calc input_order_info later, e.g. while reading from StorageMerge. + if (analysis_result.optimize_read_in_order || analysis_result.optimize_aggregation_in_order) { - query_info.order_by_optimizer = std::make_shared( - analysis_result.order_by_elements_actions, - getSortDescription(query, *context), - query_info.syntax_analyzer_result); + if (analysis_result.optimize_read_in_order) + query_info.order_optimizer = std::make_shared( + analysis_result.order_by_elements_actions, + getSortDescription(query, *context), + query_info.syntax_analyzer_result); + else + query_info.order_optimizer = std::make_shared( + analysis_result.group_by_elements_actions, + getSortDescriptionFromGroupBy(query), + query_info.syntax_analyzer_result); - query_info.input_sorting_info = query_info.order_by_optimizer->getInputOrder(storage); + query_info.input_order_info = query_info.order_optimizer->getInputOrder(storage); } Pipes pipes = storage->read(required_columns, query_info, *context, processing_stage, max_block_size, max_streams); @@ -1391,7 +1405,7 @@ void InterpreterSelectQuery::executeWhere(QueryPipeline & pipeline, const Expres } -void InterpreterSelectQuery::executeAggregation(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool overflow_row, bool final) +void InterpreterSelectQuery::executeAggregation(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool overflow_row, bool final, InputOrderInfoPtr group_by_info) { pipeline.addSimpleTransform([&](const Block & header) { @@ -1429,6 +1443,62 @@ void InterpreterSelectQuery::executeAggregation(QueryPipeline & pipeline, const /// Forget about current totals and extremes. They will be calculated again after aggregation if needed. pipeline.dropTotalsAndExtremes(); + if (group_by_info && settings.optimize_aggregation_in_order) + { + auto & query = getSelectQuery(); + SortDescription group_by_descr = getSortDescriptionFromGroupBy(query); + bool need_finish_sorting = (group_by_info->order_key_prefix_descr.size() < group_by_descr.size()); + + if (need_finish_sorting) + { + /// TOO SLOW + } + else + { + if (pipeline.getNumStreams() > 1) + { + auto many_data = std::make_shared(pipeline.getNumStreams()); + size_t counter = 0; + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, transform_params, group_by_descr, settings.max_block_size, many_data, counter++); + }); + + for (auto & column_description : group_by_descr) + { + if (!column_description.column_name.empty()) + { + column_description.column_number = pipeline.getHeader().getPositionByName(column_description.column_name); + column_description.column_name.clear(); + } + } + + auto transform = std::make_shared( + pipeline.getHeader(), + pipeline.getNumStreams(), + group_by_descr, + settings.max_block_size); + + pipeline.addPipe({ std::move(transform) }); + } + else + { + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, transform_params, group_by_descr, settings.max_block_size); + }); + } + + pipeline.addSimpleTransform([&](const Block & header) + { + return std::make_shared(header, transform_params); + }); + + pipeline.enableQuotaForCurrentStreams(); + return; + } + } + /// If there are several sources, then we perform parallel aggregation if (pipeline.getNumStreams() > 1) { @@ -1591,7 +1661,47 @@ void InterpreterSelectQuery::executeExpression(QueryPipeline & pipeline, const E } -void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputSortingInfoPtr input_sorting_info) +void InterpreterSelectQuery::executeOrderOptimized(QueryPipeline & pipeline, InputOrderInfoPtr input_sorting_info, UInt64 limit, SortDescription & output_order_descr) +{ + const Settings & settings = context->getSettingsRef(); + + bool need_finish_sorting = (input_sorting_info->order_key_prefix_descr.size() < output_order_descr.size()); + if (pipeline.getNumStreams() > 1) + { + UInt64 limit_for_merging = (need_finish_sorting ? 0 : limit); + auto transform = std::make_shared( + pipeline.getHeader(), + pipeline.getNumStreams(), + input_sorting_info->order_key_prefix_descr, + settings.max_block_size, limit_for_merging); + + pipeline.addPipe({ std::move(transform) }); + } + + pipeline.enableQuotaForCurrentStreams(); + + if (need_finish_sorting) + { + pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr + { + if (stream_type != QueryPipeline::StreamType::Main) + return nullptr; + + return std::make_shared(header, output_order_descr, limit); + }); + + /// NOTE limits are not applied to the size of temporary sets in FinishSortingTransform + + pipeline.addSimpleTransform([&](const Block & header) -> ProcessorPtr + { + return std::make_shared( + header, input_sorting_info->order_key_prefix_descr, + output_order_descr, settings.max_block_size, limit); + }); + } +} + +void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputOrderInfoPtr input_sorting_info) { auto & query = getSelectQuery(); SortDescription output_order_descr = getSortDescription(query, *context); @@ -1611,43 +1721,7 @@ void InterpreterSelectQuery::executeOrder(QueryPipeline & pipeline, InputSorting * and then merge them into one sorted stream. * At this stage we merge per-thread streams into one. */ - - bool need_finish_sorting = (input_sorting_info->order_key_prefix_descr.size() < output_order_descr.size()); - - if (pipeline.getNumStreams() > 1) - { - UInt64 limit_for_merging = (need_finish_sorting ? 0 : limit); - auto transform = std::make_shared( - pipeline.getHeader(), - pipeline.getNumStreams(), - input_sorting_info->order_key_prefix_descr, - settings.max_block_size, limit_for_merging); - - pipeline.addPipe({ std::move(transform) }); - } - - pipeline.enableQuotaForCurrentStreams(); - - if (need_finish_sorting) - { - pipeline.addSimpleTransform([&](const Block & header, QueryPipeline::StreamType stream_type) -> ProcessorPtr - { - if (stream_type != QueryPipeline::StreamType::Main) - return nullptr; - - return std::make_shared(header, output_order_descr, limit); - }); - - /// NOTE limits are not applied to the size of temporary sets in FinishSortingTransform - - pipeline.addSimpleTransform([&](const Block & header) -> ProcessorPtr - { - return std::make_shared( - header, input_sorting_info->order_key_prefix_descr, - output_order_descr, settings.max_block_size, limit); - }); - } - + executeOrderOptimized(pipeline, input_sorting_info, limit, output_order_descr); return; } @@ -1920,8 +1994,8 @@ void InterpreterSelectQuery::executeExtremes(QueryPipeline & pipeline) void InterpreterSelectQuery::executeSubqueriesInSetsAndJoins(QueryPipeline & pipeline, const SubqueriesForSets & subqueries_for_sets) { - if (query_info.input_sorting_info) - executeMergeSorted(pipeline, query_info.input_sorting_info->order_key_prefix_descr, 0); + if (query_info.input_order_info) + executeMergeSorted(pipeline, query_info.input_order_info->order_key_prefix_descr, 0); const Settings & settings = context->getSettingsRef(); diff --git a/src/Interpreters/InterpreterSelectQuery.h b/src/Interpreters/InterpreterSelectQuery.h index ca7fb4c72ba..c60451d5f4a 100644 --- a/src/Interpreters/InterpreterSelectQuery.h +++ b/src/Interpreters/InterpreterSelectQuery.h @@ -113,12 +113,13 @@ private: const Names & columns_to_remove_after_prewhere); void executeWhere(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool remove_filter); - void executeAggregation(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool overflow_row, bool final); + void executeAggregation(QueryPipeline & pipeline, const ExpressionActionsPtr & expression, bool overflow_row, bool final, InputOrderInfoPtr group_by_info); void executeMergeAggregated(QueryPipeline & pipeline, bool overflow_row, bool final); void executeTotalsAndHaving(QueryPipeline & pipeline, bool has_having, const ExpressionActionsPtr & expression, bool overflow_row, bool final); void executeHaving(QueryPipeline & pipeline, const ExpressionActionsPtr & expression); static void executeExpression(QueryPipeline & pipeline, const ExpressionActionsPtr & expression); - void executeOrder(QueryPipeline & pipeline, InputSortingInfoPtr sorting_info); + void executeOrder(QueryPipeline & pipeline, InputOrderInfoPtr sorting_info); + void executeOrderOptimized(QueryPipeline & pipeline, InputOrderInfoPtr sorting_info, UInt64 limit, SortDescription & output_order_descr); void executeWithFill(QueryPipeline & pipeline); void executeMergeSorted(QueryPipeline & pipeline); void executePreLimit(QueryPipeline & pipeline, bool do_not_skip_offset); @@ -131,7 +132,8 @@ private: void executeSubqueriesInSetsAndJoins(QueryPipeline & pipeline, const std::unordered_map & subqueries_for_sets); void executeMergeSorted(QueryPipeline & pipeline, const SortDescription & sort_description, UInt64 limit); - String generateFilterActions(ExpressionActionsPtr & actions, const ASTPtr & row_policy_filter, const Names & prerequisite_columns = {}) const; + String generateFilterActions( + ExpressionActionsPtr & actions, const ASTPtr & row_policy_filter, const Names & prerequisite_columns = {}) const; enum class Modificator { @@ -158,6 +160,7 @@ private: /// Is calculated in getSampleBlock. Is used later in readImpl. ExpressionAnalysisResult analysis_result; + /// For row-level security. FilterInfoPtr filter_info; QueryProcessingStage::Enum from_stage = QueryProcessingStage::FetchColumns; diff --git a/src/Interpreters/InterpreterShowCreateQuery.cpp b/src/Interpreters/InterpreterShowCreateQuery.cpp index 30005c7b169..b14baaafbb9 100644 --- a/src/Interpreters/InterpreterShowCreateQuery.cpp +++ b/src/Interpreters/InterpreterShowCreateQuery.cpp @@ -69,7 +69,7 @@ BlockInputStreamPtr InterpreterShowCreateQuery::executeImpl() create_query = DatabaseCatalog::instance().getDatabase(show_query->database)->getCreateDictionaryQuery(show_query->table); } - if (!create_query && show_query && show_query->temporary) + if (!create_query) throw Exception("Unable to show the create query of " + show_query->table + ". Maybe it was created by the system.", ErrorCodes::THERE_IS_NO_QUERY); if (!context.getSettingsRef().show_table_uuid_in_table_create_query_if_not_nil) diff --git a/src/Interpreters/InterpreterShowTablesQuery.cpp b/src/Interpreters/InterpreterShowTablesQuery.cpp index 3660a41c474..4b0d4c21ad1 100644 --- a/src/Interpreters/InterpreterShowTablesQuery.cpp +++ b/src/Interpreters/InterpreterShowTablesQuery.cpp @@ -33,6 +33,32 @@ String InterpreterShowTablesQuery::getRewrittenQuery() if (query.databases) return "SELECT name FROM system.databases"; + /// SHOW CLUSTER/CLUSTERS + if (query.clusters) + { + std::stringstream rewritten_query; + rewritten_query << "SELECT DISTINCT cluster FROM system.clusters"; + + if (!query.like.empty()) + { + rewritten_query << " WHERE cluster " << (query.not_like ? "NOT " : "") << "LIKE " << std::quoted(query.like, '\''); + } + + if (query.limit_length) + rewritten_query << " LIMIT " << query.limit_length; + + return rewritten_query.str(); + } + else if (query.cluster) + { + std::stringstream rewritten_query; + rewritten_query << "SELECT * FROM system.clusters"; + + rewritten_query << " WHERE cluster = " << std::quoted(query.cluster_str, '\''); + + return rewritten_query.str(); + } + if (query.temporary && !query.from.empty()) throw Exception("The `FROM` and `TEMPORARY` cannot be used together in `SHOW TABLES`", ErrorCodes::SYNTAX_ERROR); diff --git a/src/Interpreters/InterpreterSystemQuery.cpp b/src/Interpreters/InterpreterSystemQuery.cpp index fedda7cab50..f35bafbe25a 100644 --- a/src/Interpreters/InterpreterSystemQuery.cpp +++ b/src/Interpreters/InterpreterSystemQuery.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -146,12 +147,17 @@ void InterpreterSystemQuery::startStopAction(StorageActionBlockType action_type, { for (auto iterator = elem.second->getTablesIterator(context); iterator->isValid(); iterator->next()) { + StoragePtr table = iterator->table(); + if (!table) + continue; + if (!access->isGranted(log, getRequiredAccessType(action_type), elem.first, iterator->name())) continue; + if (start) - manager->remove(iterator->table(), action_type); + manager->remove(table, action_type); else - manager->add(iterator->table(), action_type); + manager->add(table, action_type); } } } @@ -199,7 +205,6 @@ BlockIO InterpreterSystemQuery::execute() case Type::DROP_DNS_CACHE: context.checkAccess(AccessType::SYSTEM_DROP_DNS_CACHE); DNSResolver::instance().dropCache(); - AllowedClientHosts::dropDNSCaches(); /// Reinitialize clusters to update their resolved_addresses system_context.reloadClusterConfig(); break; @@ -302,7 +307,8 @@ BlockIO InterpreterSystemQuery::execute() [&] () { if (auto query_thread_log = context.getQueryThreadLog()) query_thread_log->flush(); }, [&] () { if (auto trace_log = context.getTraceLog()) trace_log->flush(); }, [&] () { if (auto text_log = context.getTextLog()) text_log->flush(); }, - [&] () { if (auto metric_log = context.getMetricLog()) metric_log->flush(); } + [&] () { if (auto metric_log = context.getMetricLog()) metric_log->flush(); }, + [&] () { if (auto asynchronous_metric_log = context.getAsynchronousMetricLog()) asynchronous_metric_log->flush(); } ); break; case Type::STOP_LISTEN_QUERIES: @@ -371,8 +377,11 @@ void InterpreterSystemQuery::restartReplicas(Context & system_context) DatabasePtr & database = elem.second; for (auto iterator = database->getTablesIterator(context); iterator->isValid(); iterator->next()) { - if (dynamic_cast(iterator->table().get())) - replica_names.emplace_back(StorageID{database->getDatabaseName(), iterator->name()}); + if (auto table = iterator->table()) + { + if (dynamic_cast(table.get())) + replica_names.emplace_back(StorageID{database->getDatabaseName(), iterator->name()}); + } } } diff --git a/src/Interpreters/MergeJoin.cpp b/src/Interpreters/MergeJoin.cpp index b4d39c0738e..eb816a96e52 100644 --- a/src/Interpreters/MergeJoin.cpp +++ b/src/Interpreters/MergeJoin.cpp @@ -8,8 +8,13 @@ #include #include #include -#include #include +#include +#include +#include +#include +#include +#include namespace DB { @@ -341,15 +346,6 @@ void joinInequalsLeft(const Block & left_block, MutableColumns & left_columns, M column->insertManyDefaults(rows_to_add); } -Blocks blocksListToBlocks(const BlocksList & in_blocks) -{ - Blocks out_blocks; - out_blocks.reserve(in_blocks.size()); - for (const auto & block : in_blocks) - out_blocks.push_back(block); - return out_blocks; -} - } @@ -439,11 +435,17 @@ void MergeJoin::mergeInMemoryRightBlocks() if (right_blocks.empty()) return; - Blocks blocks_to_merge = blocksListToBlocks(right_blocks.blocks); + auto stream = std::make_shared(std::move(right_blocks.blocks)); + Pipe source(std::make_shared(std::move(stream))); right_blocks.clear(); + QueryPipeline pipeline; + pipeline.init(std::move(source)); + /// TODO: there should be no splitted keys by blocks for RIGHT|FULL JOIN - MergeSortingBlocksBlockInputStream sorted_input(blocks_to_merge, right_sort_description, max_rows_in_right_block); + pipeline.addPipe({std::make_shared(pipeline.getHeader(), right_sort_description, max_rows_in_right_block, 0, 0, 0, nullptr, 0)}); + + auto sorted_input = PipelineExecutingBlockInputStream(std::move(pipeline)); while (Block block = sorted_input.read()) { diff --git a/src/Interpreters/MutationsInterpreter.cpp b/src/Interpreters/MutationsInterpreter.cpp index 224c3cab02b..123de1b6e84 100644 --- a/src/Interpreters/MutationsInterpreter.cpp +++ b/src/Interpreters/MutationsInterpreter.cpp @@ -294,7 +294,7 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) const ColumnsDescription & columns_desc = storage->getColumns(); - const IndicesDescription & indices_desc = storage->getIndices(); + const IndicesDescription & indices_desc = storage->getSecondaryIndices(); NamesAndTypesList all_columns = columns_desc.getAllPhysical(); NameSet updated_columns; @@ -391,15 +391,15 @@ ASTPtr MutationsInterpreter::prepare(bool dry_run) else if (command.type == MutationCommand::MATERIALIZE_INDEX) { auto it = std::find_if( - std::cbegin(indices_desc.indices), std::end(indices_desc.indices), - [&](const std::shared_ptr & index) + std::cbegin(indices_desc), std::end(indices_desc), + [&](const IndexDescription & index) { - return index->name == command.index_name; + return index.name == command.index_name; }); - if (it == std::cend(indices_desc.indices)) + if (it == std::cend(indices_desc)) throw Exception("Unknown index: " + command.index_name, ErrorCodes::BAD_ARGUMENTS); - auto query = (*it)->expr->clone(); + auto query = (*it).expression_list_ast->clone(); auto syntax_result = SyntaxAnalyzer(context).analyze(query, all_columns); const auto required_columns = syntax_result->requiredSourceColumns(); for (const auto & column : required_columns) diff --git a/src/Interpreters/PredicateExpressionsOptimizer.cpp b/src/Interpreters/PredicateExpressionsOptimizer.cpp index b5d2c632135..fea0228e3fe 100644 --- a/src/Interpreters/PredicateExpressionsOptimizer.cpp +++ b/src/Interpreters/PredicateExpressionsOptimizer.cpp @@ -18,14 +18,17 @@ namespace ErrorCodes } PredicateExpressionsOptimizer::PredicateExpressionsOptimizer( - const Context & context_, const TablesWithColumnNames & tables_with_columns_, const Settings & settings_) - : context(context_), tables_with_columns(tables_with_columns_), settings(settings_) + const Context & context_, const TablesWithColumns & tables_with_columns_, const Settings & settings) + : enable_optimize_predicate_expression(settings.enable_optimize_predicate_expression) + , enable_optimize_predicate_expression_to_final_subquery(settings.enable_optimize_predicate_expression_to_final_subquery) + , context(context_) + , tables_with_columns(tables_with_columns_) { } bool PredicateExpressionsOptimizer::optimize(ASTSelectQuery & select_query) { - if (!settings.enable_optimize_predicate_expression) + if (!enable_optimize_predicate_expression) return false; if (select_query.having() && (!select_query.group_by_with_cube && !select_query.group_by_with_rollup && !select_query.group_by_with_totals)) @@ -133,7 +136,7 @@ bool PredicateExpressionsOptimizer::tryRewritePredicatesToTables(ASTs & tables_e break; /// Skip left and right table optimization is_rewrite_tables |= tryRewritePredicatesToTable(tables_element[table_pos], tables_predicates[table_pos], - tables_with_columns[table_pos].columns); + tables_with_columns[table_pos].columns.getNames()); if (table_element->table_join && isRight(table_element->table_join->as()->kind)) break; /// Skip left table optimization @@ -143,12 +146,12 @@ bool PredicateExpressionsOptimizer::tryRewritePredicatesToTables(ASTs & tables_e return is_rewrite_tables; } -bool PredicateExpressionsOptimizer::tryRewritePredicatesToTable(ASTPtr & table_element, const ASTs & table_predicates, const Names & table_column) const +bool PredicateExpressionsOptimizer::tryRewritePredicatesToTable(ASTPtr & table_element, const ASTs & table_predicates, Names && table_columns) const { if (!table_predicates.empty()) { - auto optimize_final = settings.enable_optimize_predicate_expression_to_final_subquery; - PredicateRewriteVisitor::Data data(context, table_predicates, table_column, optimize_final); + auto optimize_final = enable_optimize_predicate_expression_to_final_subquery; + PredicateRewriteVisitor::Data data(context, table_predicates, std::move(table_columns), optimize_final); PredicateRewriteVisitor(data).visit(table_element); return data.is_rewrite; diff --git a/src/Interpreters/PredicateExpressionsOptimizer.h b/src/Interpreters/PredicateExpressionsOptimizer.h index da6b98987a6..f555c68020e 100644 --- a/src/Interpreters/PredicateExpressionsOptimizer.h +++ b/src/Interpreters/PredicateExpressionsOptimizer.h @@ -18,34 +18,21 @@ struct Settings; class PredicateExpressionsOptimizer { public: - PredicateExpressionsOptimizer(const Context & context_, const TablesWithColumnNames & tables_with_columns_, const Settings & settings_); + PredicateExpressionsOptimizer(const Context & context_, const TablesWithColumns & tables_with_columns_, const Settings & settings_); bool optimize(ASTSelectQuery & select_query); private: - /// Extracts settings, mostly to show which are used and which are not. - struct ExtractedSettings - { - const bool enable_optimize_predicate_expression; - const bool enable_optimize_predicate_expression_to_final_subquery; - - template - ExtractedSettings(const T & settings_) - : enable_optimize_predicate_expression(settings_.enable_optimize_predicate_expression), - enable_optimize_predicate_expression_to_final_subquery(settings_.enable_optimize_predicate_expression_to_final_subquery) - {} - }; - + const bool enable_optimize_predicate_expression; + const bool enable_optimize_predicate_expression_to_final_subquery; const Context & context; - const std::vector & tables_with_columns; - - const ExtractedSettings settings; + const TablesWithColumns & tables_with_columns; std::vector extractTablesPredicates(const ASTPtr & where, const ASTPtr & prewhere); bool tryRewritePredicatesToTables(ASTs & tables_element, const std::vector & tables_predicates); - bool tryRewritePredicatesToTable(ASTPtr & table_element, const ASTs & table_predicates, const Names & table_column) const; + bool tryRewritePredicatesToTable(ASTPtr & table_element, const ASTs & table_predicates, Names && table_columns) const; bool tryMovePredicatesFromHavingToWhere(ASTSelectQuery & select_query); }; diff --git a/src/Interpreters/PredicateRewriteVisitor.cpp b/src/Interpreters/PredicateRewriteVisitor.cpp index a834e68172b..7fc45044a88 100644 --- a/src/Interpreters/PredicateRewriteVisitor.cpp +++ b/src/Interpreters/PredicateRewriteVisitor.cpp @@ -17,7 +17,7 @@ namespace DB { PredicateRewriteVisitorData::PredicateRewriteVisitorData( - const Context & context_, const ASTs & predicates_, const Names & column_names_, bool optimize_final_) + const Context & context_, const ASTs & predicates_, Names && column_names_, bool optimize_final_) : context(context_), predicates(predicates_), column_names(column_names_), optimize_final(optimize_final_) { } diff --git a/src/Interpreters/PredicateRewriteVisitor.h b/src/Interpreters/PredicateRewriteVisitor.h index cc1b6472a4c..fa25381f4b9 100644 --- a/src/Interpreters/PredicateRewriteVisitor.h +++ b/src/Interpreters/PredicateRewriteVisitor.h @@ -24,12 +24,12 @@ public: return true; } - PredicateRewriteVisitorData(const Context & context_, const ASTs & predicates_, const Names & column_names_, bool optimize_final_); + PredicateRewriteVisitorData(const Context & context_, const ASTs & predicates_, Names && column_names_, bool optimize_final_); private: const Context & context; const ASTs & predicates; - const Names & column_names; + const Names column_names; bool optimize_final; void visitFirstInternalSelect(ASTSelectQuery & select_query, ASTPtr &); diff --git a/src/Interpreters/RowRefs.cpp b/src/Interpreters/RowRefs.cpp index e10f8bb2ea7..879a0bcf88e 100644 --- a/src/Interpreters/RowRefs.cpp +++ b/src/Interpreters/RowRefs.cpp @@ -104,9 +104,7 @@ const RowRef * AsofRowRefs::findAsof(TypeIndex type, ASOF::Inequality inequality std::optional AsofRowRefs::getTypeSize(const IColumn * asof_column, size_t & size) { - TypeIndex idx = columnVectorDataType(asof_column); - if (idx == TypeIndex::Nothing) - idx = columnDecimalDataType(asof_column); + TypeIndex idx = asof_column->getDataType(); switch (idx) { diff --git a/src/Interpreters/SyntaxAnalyzer.cpp b/src/Interpreters/SyntaxAnalyzer.cpp index b5f86b87fdc..8f6d368e6ad 100644 --- a/src/Interpreters/SyntaxAnalyzer.cpp +++ b/src/Interpreters/SyntaxAnalyzer.cpp @@ -22,6 +22,7 @@ #include /// getSmallestColumn() #include #include +#include #include #include @@ -101,7 +102,7 @@ using CustomizeGlobalNotInVisitor = InDepthNodeVisitor & tables_with_columns) + const TablesWithColumns & tables_with_columns) { LogAST log; TranslateQualifiedNamesVisitor::Data visitor_data(source_columns_set, tables_with_columns); @@ -429,6 +430,16 @@ void optimizeIf(ASTPtr & query, Aliases & aliases, bool if_chain_to_miltiif) OptimizeIfChainsVisitor().visit(query); } +void optimizeArithmeticOperationsInAgr(ASTPtr & query, bool optimize_arithmetic_operations_in_agr_func) +{ + if (optimize_arithmetic_operations_in_agr_func) + { + /// Removing arithmetic operations from functions + ArithmeticOperationsInAgrFuncVisitor::Data data = {}; + ArithmeticOperationsInAgrFuncVisitor(data).visit(query); + } +} + void getArrayJoinedColumns(ASTPtr & query, SyntaxAnalyzerResult & result, const ASTSelectQuery * select_query, const NamesAndTypesList & source_columns, const NameSet & source_columns_set) { @@ -517,7 +528,7 @@ void setJoinStrictness(ASTSelectQuery & select_query, JoinStrictness join_defaul /// Find the columns that are obtained by JOIN. void collectJoinedColumns(TableJoin & analyzed_join, const ASTSelectQuery & select_query, - const std::vector & tables, const Aliases & aliases) + const TablesWithColumns & tables, const Aliases & aliases) { const ASTTablesInSelectQueryElement * node = select_query.join(); if (!node) @@ -587,7 +598,7 @@ void SyntaxAnalyzerResult::collectSourceColumns(bool add_special) /// Calculate which columns are required to execute the expression. /// Then, delete all other columns from the list of available columns. /// After execution, columns will only contain the list of columns needed to read from the table. -void SyntaxAnalyzerResult::collectUsedColumns(const ASTPtr & query) +void SyntaxAnalyzerResult::collectUsedColumns(const ASTPtr & query, bool is_select) { /// We calculate required_source_columns with source_columns modifications and swap them on exit required_source_columns = source_columns; @@ -637,12 +648,11 @@ void SyntaxAnalyzerResult::collectUsedColumns(const ASTPtr & query) required.insert(column_name_type.name); } - const auto * select_query = query->as(); - /// You need to read at least one column to find the number of rows. - if (select_query && required.empty()) + if (is_select && required.empty()) { - maybe_optimize_trivial_count = true; + optimize_trivial_count = true; + /// We will find a column with minimum . /// Because it is the column that is cheapest to read. struct ColumnSizeTuple @@ -651,12 +661,14 @@ void SyntaxAnalyzerResult::collectUsedColumns(const ASTPtr & query) size_t type_size; size_t uncompressed_size; String name; + bool operator<(const ColumnSizeTuple & that) const { return std::tie(compressed_size, type_size, uncompressed_size) < std::tie(that.compressed_size, that.type_size, that.uncompressed_size); } }; + std::vector columns; if (storage) { @@ -670,6 +682,7 @@ void SyntaxAnalyzerResult::collectUsedColumns(const ASTPtr & query) columns.emplace_back(ColumnSizeTuple{c->second.data_compressed, type_size, c->second.data_uncompressed, source_column.name}); } } + if (!columns.empty()) required.insert(std::min_element(columns.begin(), columns.end())->name); else @@ -749,6 +762,7 @@ void SyntaxAnalyzerResult::collectUsedColumns(const ASTPtr & query) required_source_columns.swap(source_columns); } + SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyzeSelect( ASTPtr & query, SyntaxAnalyzerResult && result, @@ -779,12 +793,6 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyzeSelect( if (remove_duplicates) renameDuplicatedColumns(select_query); - /// TODO: Remove unneeded conversion - std::vector tables_with_column_names; - tables_with_column_names.reserve(tables_with_columns.size()); - for (const auto & table : tables_with_columns) - tables_with_column_names.emplace_back(table.removeTypes()); - if (tables_with_columns.size() > 1) { result.analyzed_join->columns_from_joined_table = tables_with_columns[1].columns; @@ -792,7 +800,7 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyzeSelect( source_columns_set, tables_with_columns[1].table.getQualifiedNamePrefix()); } - translateQualifiedNames(query, *select_query, source_columns_set, tables_with_column_names); + translateQualifiedNames(query, *select_query, source_columns_set, tables_with_columns); /// Optimizes logical expressions. LogicalExpressionsOptimizer(select_query, settings.optimize_min_equality_disjunction_chain_length.value).perform(); @@ -811,8 +819,11 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyzeSelect( { optimizeIf(query, result.aliases, settings.optimize_if_chain_to_miltiif); + /// Move arithmetic operations out of aggregation functions + optimizeArithmeticOperationsInAgr(query, settings.optimize_arithmetic_operations_in_aggregate_functions); + /// Push the predicate expression down to the subqueries. - result.rewrite_subqueries = PredicateExpressionsOptimizer(context, tables_with_column_names, settings).optimize(*select_query); + result.rewrite_subqueries = PredicateExpressionsOptimizer(context, tables_with_columns, settings).optimize(*select_query); /// GROUP BY injective function elimination. optimizeGroupBy(select_query, source_columns_set, context); @@ -831,11 +842,18 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyzeSelect( setJoinStrictness(*select_query, settings.join_default_strictness, settings.any_join_distinct_right_table_keys, result.analyzed_join->table_join); - collectJoinedColumns(*result.analyzed_join, *select_query, tables_with_column_names, result.aliases); + collectJoinedColumns(*result.analyzed_join, *select_query, tables_with_columns, result.aliases); } result.aggregates = getAggregates(query, *select_query); - result.collectUsedColumns(query); + result.collectUsedColumns(query, true); + + if (result.optimize_trivial_count) + result.optimize_trivial_count = settings.optimize_trivial_count_query && + !select_query->where() && !select_query->prewhere() && !select_query->groupBy() && !select_query->having() && + !select_query->sampleSize() && !select_query->sampleOffset() && !select_query->final() && + (tables_with_columns.size() < 2 || isLeft(result.analyzed_join->kind())); + return std::make_shared(result); } @@ -869,7 +887,7 @@ SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyze(ASTPtr & query, const NamesAndTy else assertNoAggregates(query, "in wrong place"); - result.collectUsedColumns(query); + result.collectUsedColumns(query, false); return std::make_shared(result); } diff --git a/src/Interpreters/SyntaxAnalyzer.h b/src/Interpreters/SyntaxAnalyzer.h index abacb25ac4d..175c2db295a 100644 --- a/src/Interpreters/SyntaxAnalyzer.h +++ b/src/Interpreters/SyntaxAnalyzer.h @@ -46,11 +46,11 @@ struct SyntaxAnalyzerResult /// Predicate optimizer overrides the sub queries bool rewrite_subqueries = false; + bool optimize_trivial_count = false; + /// Results of scalar sub queries Scalars scalars; - bool maybe_optimize_trivial_count = false; - SyntaxAnalyzerResult(const NamesAndTypesList & source_columns_, ConstStoragePtr storage_ = {}, bool add_special = true) : storage(storage_) , source_columns(source_columns_) @@ -59,7 +59,7 @@ struct SyntaxAnalyzerResult } void collectSourceColumns(bool add_special); - void collectUsedColumns(const ASTPtr & query); + void collectUsedColumns(const ASTPtr & query, bool is_select); Names requiredSourceColumns() const { return required_source_columns.getNames(); } const Scalars & getScalars() const { return scalars; } }; diff --git a/src/Interpreters/SystemLog.cpp b/src/Interpreters/SystemLog.cpp index 9ce2f7a4d0e..d79edde7052 100644 --- a/src/Interpreters/SystemLog.cpp +++ b/src/Interpreters/SystemLog.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -75,6 +76,9 @@ SystemLogs::SystemLogs(Context & global_context, const Poco::Util::AbstractConfi trace_log = createSystemLog(global_context, "system", "trace_log", config, "trace_log"); text_log = createSystemLog(global_context, "system", "text_log", config, "text_log"); metric_log = createSystemLog(global_context, "system", "metric_log", config, "metric_log"); + asynchronous_metric_log = createSystemLog( + global_context, "system", "asynchronous_metric_log", config, + "asynchronous_metric_log"); if (query_log) logs.emplace_back(query_log.get()); @@ -88,6 +92,9 @@ SystemLogs::SystemLogs(Context & global_context, const Poco::Util::AbstractConfi logs.emplace_back(text_log.get()); if (metric_log) logs.emplace_back(metric_log.get()); + if (asynchronous_metric_log) + logs.emplace_back(asynchronous_metric_log.get()); + try { diff --git a/src/Interpreters/SystemLog.h b/src/Interpreters/SystemLog.h index dd2f815ce92..e49ce574478 100644 --- a/src/Interpreters/SystemLog.h +++ b/src/Interpreters/SystemLog.h @@ -69,6 +69,7 @@ class PartLog; class TextLog; class TraceLog; class MetricLog; +class AsynchronousMetricLog; class ISystemLog @@ -99,6 +100,8 @@ struct SystemLogs std::shared_ptr trace_log; /// Used to log traces from query profiler std::shared_ptr text_log; /// Used to log all text messages. std::shared_ptr metric_log; /// Used to log all metrics. + /// Metrics from system.asynchronous_metrics. + std::shared_ptr asynchronous_metric_log; std::vector logs; }; diff --git a/src/Interpreters/ThreadStatusExt.cpp b/src/Interpreters/ThreadStatusExt.cpp index bde64919bc6..04265734ce7 100644 --- a/src/Interpreters/ThreadStatusExt.cpp +++ b/src/Interpreters/ThreadStatusExt.cpp @@ -8,6 +8,7 @@ #include #include #include +#include #if defined(OS_LINUX) # include @@ -134,6 +135,54 @@ void ThreadStatus::attachQuery(const ThreadGroupStatusPtr & thread_group_, bool setupState(thread_group_); } +void ThreadStatus::initPerformanceCounters() +{ + performance_counters_finalized = false; + + /// Clear stats from previous query if a new query is started + /// TODO: make separate query_thread_performance_counters and thread_performance_counters + performance_counters.resetCounters(); + memory_tracker.resetCounters(); + memory_tracker.setDescription("(for thread)"); + + query_start_time_nanoseconds = getCurrentTimeNanoseconds(); + query_start_time = time(nullptr); + ++queries_started; + + *last_rusage = RUsageCounters::current(query_start_time_nanoseconds); + + if (query_context) + { + const Settings & settings = query_context->getSettingsRef(); + if (settings.metrics_perf_events_enabled) + { + try + { + current_thread_counters.initializeProfileEvents( + settings.metrics_perf_events_list); + } + catch (...) + { + tryLogCurrentException(__PRETTY_FUNCTION__); + } + } + } + + if (!taskstats) + { + try + { + taskstats = TasksStatsCounters::create(thread_id); + } + catch (...) + { + tryLogCurrentException(log); + } + } + if (taskstats) + taskstats->reset(); +} + void ThreadStatus::finalizePerformanceCounters() { if (performance_counters_finalized) @@ -142,6 +191,21 @@ void ThreadStatus::finalizePerformanceCounters() performance_counters_finalized = true; updatePerformanceCounters(); + bool close_perf_descriptors = true; + if (query_context) + close_perf_descriptors = !query_context->getSettingsRef().metrics_perf_events_enabled; + + try + { + current_thread_counters.finalizeProfileEvents(performance_counters); + if (close_perf_descriptors) + current_thread_counters.closeEventDescriptors(); + } + catch (...) + { + tryLogCurrentException(log); + } + try { if (global_context && query_context) diff --git a/src/Interpreters/TranslateQualifiedNamesVisitor.cpp b/src/Interpreters/TranslateQualifiedNamesVisitor.cpp index b03bb4b0de3..908eb2fd57c 100644 --- a/src/Interpreters/TranslateQualifiedNamesVisitor.cpp +++ b/src/Interpreters/TranslateQualifiedNamesVisitor.cpp @@ -26,6 +26,7 @@ namespace DB namespace ErrorCodes { extern const int UNKNOWN_IDENTIFIER; + extern const int UNSUPPORTED_JOIN_KEYS; extern const int LOGICAL_ERROR; } @@ -36,9 +37,10 @@ bool TranslateQualifiedNamesMatcher::Data::unknownColumn(size_t table_pos, const auto nested2 = IdentifierSemantic::extractNestedName(identifier, table.alias); const String & short_name = identifier.shortName(); - const Names & column_names = tables[table_pos].columns; - for (const auto & known_name : column_names) + const auto & columns = tables[table_pos].columns; + for (const auto & column : columns) { + const String & known_name = column.name; if (short_name == known_name) return false; if (nested1 && *nested1 == known_name) @@ -47,9 +49,10 @@ bool TranslateQualifiedNamesMatcher::Data::unknownColumn(size_t table_pos, const return false; } - const Names & hidden_names = tables[table_pos].hidden_columns; - for (const auto & known_name : hidden_names) + const auto & hidden_columns = tables[table_pos].hidden_columns; + for (const auto & column : hidden_columns) { + const String & known_name = column.name; if (short_name == known_name) return false; if (nested1 && *nested1 == known_name) @@ -58,7 +61,7 @@ bool TranslateQualifiedNamesMatcher::Data::unknownColumn(size_t table_pos, const return false; } - return !column_names.empty(); + return !columns.empty(); } bool TranslateQualifiedNamesMatcher::needChildVisit(ASTPtr & node, const ASTPtr & child) @@ -231,11 +234,11 @@ void TranslateQualifiedNamesMatcher::visit(ASTExpressionList & node, const ASTPt bool first_table = true; for (const auto & table : tables_with_columns) { - for (const auto & column_name : table.columns) + for (const auto & column : table.columns) { - if (first_table || !data.join_using_columns.count(column_name)) + if (first_table || !data.join_using_columns.count(column.name)) { - addIdentifier(node.children, table.table, column_name, AsteriskSemantic::getAliases(*asterisk)); + addIdentifier(node.children, table.table, column.name, AsteriskSemantic::getAliases(*asterisk)); } } @@ -247,11 +250,11 @@ void TranslateQualifiedNamesMatcher::visit(ASTExpressionList & node, const ASTPt bool first_table = true; for (const auto & table : tables_with_columns) { - for (const auto & column_name : table.columns) + for (const auto & column : table.columns) { - if (asterisk_pattern->isColumnMatching(column_name) && (first_table || !data.join_using_columns.count(column_name))) + if (asterisk_pattern->isColumnMatching(column.name) && (first_table || !data.join_using_columns.count(column.name))) { - addIdentifier(node.children, table.table, column_name, AsteriskSemantic::getAliases(*asterisk_pattern)); + addIdentifier(node.children, table.table, column.name, AsteriskSemantic::getAliases(*asterisk_pattern)); } } @@ -266,9 +269,9 @@ void TranslateQualifiedNamesMatcher::visit(ASTExpressionList & node, const ASTPt { if (ident_db_and_name.satisfies(table.table, true)) { - for (const auto & column_name : table.columns) + for (const auto & column : table.columns) { - addIdentifier(node.children, table.table, column_name, AsteriskSemantic::getAliases(*qualified_asterisk)); + addIdentifier(node.children, table.table, column.name, AsteriskSemantic::getAliases(*qualified_asterisk)); } break; } @@ -296,7 +299,8 @@ void TranslateQualifiedNamesMatcher::extractJoinUsingColumns(const ASTPtr ast, D { String alias = key->tryGetAlias(); if (alias.empty()) - throw Exception("Logical error: expected identifier or alias, got: " + key->getID(), ErrorCodes::LOGICAL_ERROR); + throw Exception("Wrong key in USING. Expected identifier or alias, got: " + key->getID(), + ErrorCodes::UNSUPPORTED_JOIN_KEYS); data.join_using_columns.insert(alias); } } diff --git a/src/Interpreters/TranslateQualifiedNamesVisitor.h b/src/Interpreters/TranslateQualifiedNamesVisitor.h index e8c320671bf..1ed4da57a93 100644 --- a/src/Interpreters/TranslateQualifiedNamesVisitor.h +++ b/src/Interpreters/TranslateQualifiedNamesVisitor.h @@ -25,11 +25,11 @@ public: struct Data { const NameSet source_columns; - const std::vector & tables; + const TablesWithColumns & tables; std::unordered_set join_using_columns; bool has_columns; - Data(const NameSet & source_columns_, const std::vector & tables_, bool has_columns_ = true) + Data(const NameSet & source_columns_, const TablesWithColumns & tables_, bool has_columns_ = true) : source_columns(source_columns_) , tables(tables_) , has_columns(has_columns_) diff --git a/src/Interpreters/convertFieldToType.cpp b/src/Interpreters/convertFieldToType.cpp index 3cb774596c0..d46573d0461 100644 --- a/src/Interpreters/convertFieldToType.cpp +++ b/src/Interpreters/convertFieldToType.cpp @@ -33,8 +33,6 @@ namespace ErrorCodes { extern const int ARGUMENT_OUT_OF_BOUND; extern const int TYPE_MISMATCH; - extern const int TOO_LARGE_STRING_SIZE; - extern const int CANNOT_CONVERT_TYPE; } @@ -124,42 +122,6 @@ static Field convertDecimalType(const Field & from, const To & type) } -DayNum stringToDate(const String & s) -{ - ReadBufferFromString in(s); - DayNum date{}; - - readDateText(date, in); - if (!in.eof()) - throw Exception("String is too long for Date: " + s, ErrorCodes::TOO_LARGE_STRING_SIZE); - - return date; -} - -UInt64 stringToDateTime(const String & s) -{ - ReadBufferFromString in(s); - time_t date_time{}; - - readDateTimeText(date_time, in); - if (!in.eof()) - throw Exception("String is too long for DateTime: " + s, ErrorCodes::TOO_LARGE_STRING_SIZE); - - return UInt64(date_time); -} - -DateTime64::NativeType stringToDateTime64(const String & s, UInt32 scale) -{ - ReadBufferFromString in(s); - DateTime64 datetime64 {0}; - - readDateTime64Text(datetime64, scale, in); - if (!in.eof()) - throw Exception("String is too long for DateTime64: " + s, ErrorCodes::TOO_LARGE_STRING_SIZE); - - return datetime64.value; -} - Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const IDataType * from_type_hint) { WhichDataType which_type(type); @@ -184,7 +146,7 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID { return static_cast(type).getTimeZone().fromDayNum(DayNum(src.get())); } - else if (type.isValueRepresentedByNumber()) + else if (type.isValueRepresentedByNumber() && src.getType() != Field::Types::String) { if (which_type.isUInt8()) return convertNumericType(src, type); if (which_type.isUInt16()) return convertNumericType(src, type); @@ -200,9 +162,6 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID if (const auto * ptype = typeid_cast *>(&type)) return convertDecimalType(src, *ptype); if (const auto * ptype = typeid_cast *>(&type)) return convertDecimalType(src, *ptype); - if (!which_type.isDateOrDateTime() && !which_type.isUUID() && !which_type.isEnum()) - throw Exception{"Cannot convert field to type " + type.getName(), ErrorCodes::CANNOT_CONVERT_TYPE}; - if (which_type.isEnum() && (src.getType() == Field::Types::UInt64 || src.getType() == Field::Types::Int64)) { /// Convert UInt64 or Int64 to Enum's value @@ -214,36 +173,20 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID /// We don't need any conversion UInt64 is under type of Date and DateTime return src; } - // TODO (vnemkov): extra cases for DateTime64: converting from integer, converting from Decimal - if (src.getType() == Field::Types::String) + if (which_type.isUUID() && src.getType() == Field::Types::UInt128) { - if (which_type.isDate()) - { - /// Convert 'YYYY-MM-DD' Strings to Date - return stringToDate(src.get()); - } - else if (which_type.isDateTime()) - { - /// Convert 'YYYY-MM-DD hh:mm:ss' Strings to DateTime - return stringToDateTime(src.get()); - } - else if (which_type.isDateTime64()) - { - const auto * date_time64 = typeid_cast(&type); - /// Convert 'YYYY-MM-DD hh:mm:ss.NNNNNNNNN' Strings to DateTime - return stringToDateTime64(src.get(), date_time64->getScale()); - } - else if (which_type.isUUID()) - { - return stringToUUID(src.get()); - } - else if (which_type.isEnum()) - { - /// Convert String to Enum's value - return dynamic_cast(type).castToValue(src); - } + /// Already in needed type. + return src; } + + if (which_type.isDateTime64() && src.getType() == Field::Types::Decimal64) + { + /// Already in needed type. + return src; + } + + /// TODO Conversion from integers to DateTime64 } else if (which_type.isStringOrFixedString()) { @@ -328,17 +271,37 @@ Field convertFieldToTypeImpl(const Field & src, const IDataType & type, const ID return src; } + /// Conversion from string by parsing. if (src.getType() == Field::Types::String) { - const auto col = type.createColumn(); - ReadBufferFromString buffer(src.get()); - type.deserializeAsTextEscaped(*col, buffer, FormatSettings{}); + /// Promote data type to avoid overflows. Note that overflows in the largest data type are still possible. + const IDataType * type_to_parse = &type; + DataTypePtr holder; - return (*col)[0]; + if (type.canBePromoted()) + { + holder = type.promoteNumericType(); + type_to_parse = holder.get(); + } + + const auto col = type_to_parse->createColumn(); + ReadBufferFromString in_buffer(src.get()); + try + { + type_to_parse->deserializeAsWholeText(*col, in_buffer, FormatSettings{}); + } + catch (Exception & e) + { + e.addMessage(fmt::format("while converting '{}' to {}", src.get(), type.getName())); + throw; + } + if (!in_buffer.eof()) + throw Exception(ErrorCodes::TYPE_MISMATCH, "Cannot convert string {} to type {}", src.get(), type.getName()); + + Field parsed = (*col)[0]; + return convertFieldToType(parsed, type, from_type_hint); } - - // TODO (nemkov): should we attempt to parse value using or `type.deserializeAsTextEscaped()` type.deserializeAsTextEscaped() ? throw Exception("Type mismatch in IN or VALUES section. Expected: " + type.getName() + ". Got: " + Field::Types::toString(src.getType()), ErrorCodes::TYPE_MISMATCH); } diff --git a/src/Interpreters/getTableExpressions.cpp b/src/Interpreters/getTableExpressions.cpp index 8467a98685d..6e3fd516e1c 100644 --- a/src/Interpreters/getTableExpressions.cpp +++ b/src/Interpreters/getTableExpressions.cpp @@ -115,10 +115,9 @@ NamesAndTypesList getColumnsFromTableExpression(const ASTTableExpression & table return getColumnsFromTableExpression(table_expression, context, materialized, aliases, virtuals); } -std::vector getDatabaseAndTablesWithColumns(const std::vector & table_expressions, - const Context & context) +TablesWithColumns getDatabaseAndTablesWithColumns(const std::vector & table_expressions, const Context & context) { - std::vector tables_with_columns; + TablesWithColumns tables_with_columns; if (!table_expressions.empty()) { @@ -146,15 +145,4 @@ std::vector getDatabaseAndTablesWithColumns(const return tables_with_columns; } -std::vector getDatabaseAndTablesWithColumnNames(const std::vector & table_expressions, - const Context & context) -{ - std::vector tables_with_columns = getDatabaseAndTablesWithColumns(table_expressions, context); - std::vector out; - out.reserve(tables_with_columns.size()); - for (auto & table : tables_with_columns) - out.emplace_back(table.removeTypes()); - return out; -} - } diff --git a/src/Interpreters/getTableExpressions.h b/src/Interpreters/getTableExpressions.h index 4e49a94bcd9..9254fb9d6a0 100644 --- a/src/Interpreters/getTableExpressions.h +++ b/src/Interpreters/getTableExpressions.h @@ -17,9 +17,6 @@ const ASTTableExpression * getTableExpression(const ASTSelectQuery & select, siz ASTPtr extractTableExpression(const ASTSelectQuery & select, size_t table_number); NamesAndTypesList getColumnsFromTableExpression(const ASTTableExpression & table_expression, const Context & context); -std::vector getDatabaseAndTablesWithColumns(const std::vector & table_expressions, - const Context & context); -std::vector getDatabaseAndTablesWithColumnNames(const std::vector & table_expressions, - const Context & context); +TablesWithColumns getDatabaseAndTablesWithColumns(const std::vector & table_expressions, const Context & context); } diff --git a/src/Interpreters/join_common.cpp b/src/Interpreters/join_common.cpp index e3ca9258892..6dd3a202d4d 100644 --- a/src/Interpreters/join_common.cpp +++ b/src/Interpreters/join_common.cpp @@ -16,8 +16,14 @@ namespace ErrorCodes namespace JoinCommon { -void convertColumnToNullable(ColumnWithTypeAndName & column) +void convertColumnToNullable(ColumnWithTypeAndName & column, bool low_card_nullability) { + if (low_card_nullability && column.type->lowCardinality()) + { + column.column = recursiveRemoveLowCardinality(column.column); + column.type = recursiveRemoveLowCardinality(column.type); + } + if (column.type->isNullable() || !column.type->canBeInsideNullable()) return; diff --git a/src/Interpreters/join_common.h b/src/Interpreters/join_common.h index b69a0a4a993..47fa082e700 100644 --- a/src/Interpreters/join_common.h +++ b/src/Interpreters/join_common.h @@ -13,7 +13,7 @@ using ColumnRawPtrs = std::vector; namespace JoinCommon { -void convertColumnToNullable(ColumnWithTypeAndName & column); +void convertColumnToNullable(ColumnWithTypeAndName & column, bool low_card_nullability = false); void convertColumnsToNullable(Block & block, size_t starting_pos = 0); void removeColumnNullability(ColumnWithTypeAndName & column); Columns materializeColumns(const Block & block, const Names & names); diff --git a/src/Interpreters/tests/CMakeLists.txt b/src/Interpreters/tests/CMakeLists.txt index 19d302d2b30..324a38b1a17 100644 --- a/src/Interpreters/tests/CMakeLists.txt +++ b/src/Interpreters/tests/CMakeLists.txt @@ -1,15 +1,3 @@ -add_executable (expression expression.cpp) -target_link_libraries (expression PRIVATE dbms clickhouse_parsers) - -add_executable (create_query create_query.cpp) -target_link_libraries (create_query PRIVATE dbms clickhouse_parsers) - -add_executable (select_query select_query.cpp) -target_link_libraries (select_query PRIVATE clickhouse_storages_system dbms clickhouse_common_io) - -add_executable (aggregate aggregate.cpp) -target_link_libraries (aggregate PRIVATE dbms) - add_executable (hash_map hash_map.cpp) target_include_directories (hash_map SYSTEM BEFORE PRIVATE ${SPARSEHASH_INCLUDE_DIR}) target_link_libraries (hash_map PRIVATE dbms) @@ -19,8 +7,7 @@ target_include_directories (hash_map_lookup SYSTEM BEFORE PRIVATE ${SPARSEHASH_I target_link_libraries (hash_map_lookup PRIVATE dbms) add_executable (hash_map3 hash_map3.cpp) -target_include_directories(hash_map3 SYSTEM BEFORE PRIVATE ${METROHASH_INCLUDE_DIR}) -target_link_libraries (hash_map3 PRIVATE dbms ${FARMHASH_LIBRARIES} ${METROHASH_LIBRARIES}) +target_link_libraries (hash_map3 PRIVATE dbms ${FARMHASH_LIBRARIES} metrohash) add_executable (hash_map_string hash_map_string.cpp) target_include_directories (hash_map_string SYSTEM BEFORE PRIVATE ${SPARSEHASH_INCLUDE_DIR}) @@ -30,8 +17,7 @@ add_executable (hash_map_string_2 hash_map_string_2.cpp) target_link_libraries (hash_map_string_2 PRIVATE dbms) add_executable (hash_map_string_3 hash_map_string_3.cpp) -target_include_directories(hash_map_string_3 SYSTEM BEFORE PRIVATE ${METROHASH_INCLUDE_DIR}) -target_link_libraries (hash_map_string_3 PRIVATE dbms ${FARMHASH_LIBRARIES} ${METROHASH_LIBRARIES}) +target_link_libraries (hash_map_string_3 PRIVATE dbms ${FARMHASH_LIBRARIES} metrohash) add_executable (hash_map_string_small hash_map_string_small.cpp) target_include_directories (hash_map_string_small SYSTEM BEFORE PRIVATE ${SPARSEHASH_INCLUDE_DIR}) diff --git a/src/Interpreters/tests/aggregate.cpp b/src/Interpreters/tests/aggregate.cpp deleted file mode 100644 index 9959bca7aac..00000000000 --- a/src/Interpreters/tests/aggregate.cpp +++ /dev/null @@ -1,105 +0,0 @@ -#include -#include - -#include -#include - -#include -#include - -#include - -#include - -#include - - -int main(int argc, char ** argv) -{ - using namespace DB; - - try - { - size_t n = argc == 2 ? std::stol(argv[1]) : 10; - - Block block; - - { - ColumnWithTypeAndName column; - column.name = "x"; - column.type = std::make_shared(); - auto col = ColumnInt16::create(); - auto & vec_x = col->getData(); - - vec_x.resize(n); - for (size_t i = 0; i < n; ++i) - vec_x[i] = i % 9; - - column.column = std::move(col); - block.insert(column); - } - - const char * strings[] = {"abc", "def", "abcd", "defg", "ac"}; - - { - ColumnWithTypeAndName column; - column.name = "s1"; - column.type = std::make_shared(); - auto col = ColumnString::create(); - - for (size_t i = 0; i < n; ++i) - col->insert(std::string(strings[i % 5])); - - column.column = std::move(col); - block.insert(column); - } - - { - ColumnWithTypeAndName column; - column.name = "s2"; - column.type = std::make_shared(); - auto col = ColumnString::create(); - - for (size_t i = 0; i < n; ++i) - col->insert(std::string(strings[i % 3])); - - column.column = std::move(col); - block.insert(column); - } - - BlockInputStreamPtr stream = std::make_shared(block); - AggregatedDataVariants aggregated_data_variants; - - AggregateFunctionFactory factory; - - AggregateDescriptions aggregate_descriptions(1); - - DataTypes empty_list_of_types; - aggregate_descriptions[0].function = factory.get("count", empty_list_of_types); - - Aggregator::Params params( - stream->getHeader(), {0, 1}, aggregate_descriptions, - false, 0, OverflowMode::THROW, 0, 0, 0, false, nullptr, 1, 0); - - Aggregator aggregator(params); - - { - Stopwatch stopwatch; - stopwatch.start(); - - aggregator.execute(stream, aggregated_data_variants); - - stopwatch.stop(); - std::cout << std::fixed << std::setprecision(2) - << "Elapsed " << stopwatch.elapsedSeconds() << " sec." - << ", " << n / stopwatch.elapsedSeconds() << " rows/sec." - << std::endl; - } - } - catch (const Exception & e) - { - std::cerr << e.displayText() << std::endl; - } - - return 0; -} diff --git a/src/Interpreters/tests/create_query.cpp b/src/Interpreters/tests/create_query.cpp deleted file mode 100644 index 82bb8db5d78..00000000000 --- a/src/Interpreters/tests/create_query.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include -#include - -#include -#include -#include - -#include - -#include -#include - - -using namespace DB; - -int main(int, char **) -try -{ - std::string input = "CREATE TABLE IF NOT EXISTS hits (\n" - "WatchID UInt64,\n" - "JavaEnable UInt8,\n" - "Title String,\n" - "EventTime DateTime,\n" - "CounterID UInt32,\n" - "ClientIP UInt32,\n" - "RegionID UInt32,\n" - "UniqID UInt64,\n" - "CounterClass UInt8,\n" - "OS UInt8,\n" - "UserAgent UInt8,\n" - "URL String,\n" - "Referer String,\n" - "ResolutionWidth UInt16,\n" - "ResolutionHeight UInt16,\n" - "ResolutionDepth UInt8,\n" - "FlashMajor UInt8,\n" - "FlashMinor UInt8,\n" - "FlashMinor2 String,\n" - "NetMajor UInt8,\n" - "NetMinor UInt8,\n" - "UserAgentMajor UInt16,\n" - "UserAgentMinor FixedString(2),\n" - "CookieEnable UInt8,\n" - "JavascriptEnable UInt8,\n" - "IsMobile UInt8,\n" - "MobilePhone UInt8,\n" - "MobilePhoneModel String,\n" - "Params String,\n" - "IPNetworkID UInt32,\n" - "TraficSourceID Int8,\n" - "SearchEngineID UInt16,\n" - "SearchPhrase String,\n" - "AdvEngineID UInt8,\n" - "IsArtifical UInt8,\n" - "WindowClientWidth UInt16,\n" - "WindowClientHeight UInt16,\n" - "ClientTimeZone Int16,\n" - "ClientEventTime DateTime,\n" - "SilverlightVersion1 UInt8,\n" - "SilverlightVersion2 UInt8,\n" - "SilverlightVersion3 UInt32,\n" - "SilverlightVersion4 UInt16,\n" - "PageCharset String,\n" - "CodeVersion UInt32,\n" - "IsLink UInt8,\n" - "IsDownload UInt8,\n" - "IsNotBounce UInt8,\n" - "FUniqID UInt64,\n" - "OriginalURL String,\n" - "HID UInt32,\n" - "IsOldCounter UInt8,\n" - "IsEvent UInt8,\n" - "IsParameter UInt8,\n" - "DontCountHits UInt8,\n" - "WithHash UInt8\n" - ") ENGINE = Log"; - - ParserCreateQuery parser; - ASTPtr ast = parseQuery(parser, input.data(), input.data() + input.size(), "", 0, 0); - - SharedContextHolder shared_context = Context::createShared(); - Context context = Context::createGlobal(shared_context.get()); - context.makeGlobalContext(); - - context.setPath("./"); - auto database = std::make_shared("test", "./metadata/test/", context); - DatabaseCatalog::instance().attachDatabase("test", database); - database->loadStoredObjects(context, false); - context.setCurrentDatabase("test"); - - InterpreterCreateQuery interpreter(ast, context); - interpreter.execute(); - - return 0; -} -catch (const Exception & e) -{ - std::cerr << e.what() << ", " << e.displayText() << std::endl - << std::endl - << "Stack trace:" << std::endl - << e.getStackTraceString(); - return 1; -} diff --git a/src/Interpreters/tests/expression.cpp b/src/Interpreters/tests/expression.cpp deleted file mode 100644 index 8327514b3d3..00000000000 --- a/src/Interpreters/tests/expression.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include -#include - -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - - -int main(int argc, char ** argv) -{ - using namespace DB; - - try - { - std::string input = "SELECT x, s1, s2, " - "/*" - "2 + x * 2, x * 2, x % 3 == 1, " - "s1 == 'abc', s1 == s2, s1 != 'abc', s1 != s2, " - "s1 < 'abc', s1 < s2, s1 > 'abc', s1 > s2, " - "s1 <= 'abc', s1 <= s2, s1 >= 'abc', s1 >= s2, " - "*/" - "s1 < s2 AND x % 3 < x % 5"; - - ParserSelectQuery parser; - ASTPtr ast = parseQuery(parser, input.data(), input.data() + input.size(), "", 0, 0); - - formatAST(*ast, std::cerr); - std::cerr << std::endl; - - SharedContextHolder shared_context = Context::createShared(); - Context context = Context::createGlobal(shared_context.get()); - context.makeGlobalContext(); - NamesAndTypesList columns - { - {"x", std::make_shared()}, - {"s1", std::make_shared()}, - {"s2", std::make_shared()} - }; - - auto syntax_result = SyntaxAnalyzer(context).analyze(ast, columns); - SelectQueryExpressionAnalyzer analyzer(ast, syntax_result, context); - ExpressionActionsChain chain(context); - analyzer.appendSelect(chain, false); - analyzer.appendProjectResult(chain); - chain.finalize(); - ExpressionActionsPtr expression = chain.getLastActions(); - - size_t n = argc == 2 ? std::stol(argv[1]) : 10; - - Block block; - - { - ColumnWithTypeAndName column; - column.name = "x"; - column.type = std::make_shared(); - auto col = ColumnInt16::create(); - auto & vec_x = col->getData(); - - vec_x.resize(n); - for (size_t i = 0; i < n; ++i) - vec_x[i] = i % 9; - - column.column = std::move(col); - block.insert(column); - } - - const char * strings[] = {"abc", "def", "abcd", "defg", "ac"}; - - { - ColumnWithTypeAndName column; - column.name = "s1"; - column.type = std::make_shared(); - auto col = ColumnString::create(); - - for (size_t i = 0; i < n; ++i) - col->insert(std::string(strings[i % 5])); - - column.column = std::move(col); - block.insert(column); - } - - { - ColumnWithTypeAndName column; - column.name = "s2"; - column.type = std::make_shared(); - auto col = ColumnString::create(); - - for (size_t i = 0; i < n; ++i) - col->insert(std::string(strings[i % 3])); - - column.column = std::move(col); - block.insert(column); - } - - { - Stopwatch stopwatch; - stopwatch.start(); - - expression->execute(block); - - stopwatch.stop(); - std::cout << std::fixed << std::setprecision(2) - << "Elapsed " << stopwatch.elapsedSeconds() << " sec." - << ", " << n / stopwatch.elapsedSeconds() << " rows/sec." - << std::endl; - } - - auto is = std::make_shared(block); - LimitBlockInputStream lis(is, 20, std::max(0, static_cast(n) - 20)); - WriteBufferFromOStream out_buf(std::cout); - BlockOutputStreamPtr out = FormatFactory::instance().getOutput("TabSeparated", out_buf, block, context); - - copyData(lis, *out); - } - catch (const Exception & e) - { - std::cerr << e.displayText() << std::endl; - } - - return 0; -} diff --git a/src/Interpreters/tests/select_query.cpp b/src/Interpreters/tests/select_query.cpp deleted file mode 100644 index fb364d28086..00000000000 --- a/src/Interpreters/tests/select_query.cpp +++ /dev/null @@ -1,61 +0,0 @@ -#include -#include - -#include - -#include - -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - - -using namespace DB; - -int main(int, char **) -try -{ - Poco::AutoPtr channel = new Poco::ConsoleChannel(std::cerr); - Poco::Logger::root().setChannel(channel); - Poco::Logger::root().setLevel("trace"); - - /// Pre-initialize the `DateLUT` so that the first initialization does not affect the measured execution speed. - DateLUT::instance(); - - SharedContextHolder shared_context = Context::createShared(); - Context context = Context::createGlobal(shared_context.get()); - context.makeGlobalContext(); - - context.setPath("./"); - - loadMetadata(context); - - DatabasePtr system = std::make_shared("system", "./metadata/system/", context); - DatabaseCatalog::instance().attachDatabase("system", system); - system->loadStoredObjects(context, false); - attachSystemTablesLocal(*DatabaseCatalog::instance().getSystemDatabase()); - context.setCurrentDatabase("default"); - - ReadBufferFromFileDescriptor in(STDIN_FILENO); - WriteBufferFromFileDescriptor out(STDOUT_FILENO); - - executeQuery(in, out, /* allow_into_outfile = */ false, context, {}); - - return 0; -} -catch (const Exception & e) -{ - std::cerr << e.what() << ", " << e.displayText() << std::endl - << std::endl - << "Stack trace:" << std::endl - << e.getStackTraceString(); - return 1; -} diff --git a/src/Interpreters/ya.make b/src/Interpreters/ya.make index b210a1c5b8c..29be5d3c216 100644 --- a/src/Interpreters/ya.make +++ b/src/Interpreters/ya.make @@ -19,6 +19,8 @@ SRCS( addMissingDefaults.cpp addTypeConversionToAST.cpp Aggregator.cpp + ArithmeticOperationsInAgrFuncOptimize.cpp + ArithmeticOperationsInAgrFuncOptimize.h ArrayJoinAction.cpp AsynchronousMetrics.cpp BloomFilter.cpp @@ -103,6 +105,7 @@ SRCS( MarkTableIdentifiersVisitor.cpp MergeJoin.cpp MetricLog.cpp + AsynchronousMetricLog.cpp MutationsInterpreter.cpp NullableUtils.cpp OptimizeIfChains.cpp diff --git a/src/Parsers/ASTColumnDeclaration.cpp b/src/Parsers/ASTColumnDeclaration.cpp index 15bf1d59574..73bfe9896cc 100644 --- a/src/Parsers/ASTColumnDeclaration.cpp +++ b/src/Parsers/ASTColumnDeclaration.cpp @@ -12,7 +12,9 @@ ASTPtr ASTColumnDeclaration::clone() const if (type) { - res->type = type; + // Type may be an ASTFunction (e.g. `create table t (a Decimal(9,0))`), + // so we have to clone it properly as well. + res->type = type->clone(); res->children.push_back(res->type); } diff --git a/src/Parsers/ASTCreateUserQuery.cpp b/src/Parsers/ASTCreateUserQuery.cpp index e5c1178285b..1d61303860a 100644 --- a/src/Parsers/ASTCreateUserQuery.cpp +++ b/src/Parsers/ASTCreateUserQuery.cpp @@ -65,7 +65,8 @@ namespace settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " IDENTIFIED WITH " << authentication_type_name << (settings.hilite ? IAST::hilite_none : ""); if (password) - settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " BY " << quoteString(*password); + settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " BY " << (settings.hilite ? IAST::hilite_none : "") + << quoteString(*password); } diff --git a/src/Parsers/ASTExplainQuery.h b/src/Parsers/ASTExplainQuery.h index d921ff427ae..d7a40a2eb85 100644 --- a/src/Parsers/ASTExplainQuery.h +++ b/src/Parsers/ASTExplainQuery.h @@ -23,7 +23,13 @@ public: String getID(char delim) const override { return "Explain" + (delim + toString(kind)); } ExplainKind getKind() const { return kind; } - ASTPtr clone() const override { return std::make_shared(*this); } + ASTPtr clone() const override + { + auto res = std::make_shared(*this); + res->children.clear(); + res->children.push_back(children[0]->clone()); + return res; + } protected: void formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const override diff --git a/src/Parsers/ASTShowTablesQuery.cpp b/src/Parsers/ASTShowTablesQuery.cpp index 82b773ea70a..25a638c77d4 100644 --- a/src/Parsers/ASTShowTablesQuery.cpp +++ b/src/Parsers/ASTShowTablesQuery.cpp @@ -2,7 +2,6 @@ #include #include - namespace DB { @@ -20,6 +19,24 @@ void ASTShowTablesQuery::formatQueryImpl(const FormatSettings & settings, Format { settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW DATABASES" << (settings.hilite ? hilite_none : ""); } + else if (clusters) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW CLUSTERS" << (settings.hilite ? hilite_none : ""); + if (!like.empty()) + settings.ostr << (settings.hilite ? hilite_keyword : "") << (not_like ? " NOT" : "") << " LIKE " << (settings.hilite ? hilite_none : "") + << std::quoted(like, '\''); + + if (limit_length) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << " LIMIT " << (settings.hilite ? hilite_none : ""); + limit_length->formatImpl(settings, state, frame); + } + } + else if (cluster) + { + settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW CLUSTER" << (settings.hilite ? hilite_none : ""); + settings.ostr << " " << backQuoteIfNeed(cluster_str); + } else { settings.ostr << (settings.hilite ? hilite_keyword : "") << "SHOW " << (temporary ? "TEMPORARY " : "") << diff --git a/src/Parsers/ASTShowTablesQuery.h b/src/Parsers/ASTShowTablesQuery.h index be0d73a3ac7..f14d6e7bd33 100644 --- a/src/Parsers/ASTShowTablesQuery.h +++ b/src/Parsers/ASTShowTablesQuery.h @@ -9,14 +9,17 @@ namespace DB { -/** Query SHOW TABLES or SHOW DATABASES +/** Query SHOW TABLES or SHOW DATABASES or SHOW CLUSTERS */ class ASTShowTablesQuery : public ASTQueryWithOutput { public: bool databases{false}; + bool clusters{false}; + bool cluster{false}; bool dictionaries{false}; bool temporary{false}; + String cluster_str; String from; String like; bool not_like{false}; diff --git a/src/Parsers/CMakeLists.txt b/src/Parsers/CMakeLists.txt index 6e3ab9decb7..b83fc20e818 100644 --- a/src/Parsers/CMakeLists.txt +++ b/src/Parsers/CMakeLists.txt @@ -2,7 +2,6 @@ include(${ClickHouse_SOURCE_DIR}/cmake/dbms_glob_sources.cmake) add_headers_and_sources(clickhouse_parsers .) add_library(clickhouse_parsers ${clickhouse_parsers_headers} ${clickhouse_parsers_sources}) target_link_libraries(clickhouse_parsers PUBLIC clickhouse_common_io) -target_include_directories(clickhouse_parsers PUBLIC ${DBMS_INCLUDE_DIR}) if (USE_DEBUG_HELPERS) set (INCLUDE_DEBUG_HELPERS "-I${ClickHouse_SOURCE_DIR}/base -include ${ClickHouse_SOURCE_DIR}/src/Parsers/iostream_debug_helpers.h") diff --git a/src/Parsers/IParserBase.cpp b/src/Parsers/IParserBase.cpp index 0aade8e36ac..0241250926d 100644 --- a/src/Parsers/IParserBase.cpp +++ b/src/Parsers/IParserBase.cpp @@ -4,11 +4,6 @@ namespace DB { -namespace ErrorCodes -{ -} - - bool IParserBase::parse(Pos & pos, ASTPtr & node, Expected & expected) { expected.add(pos, getName()); diff --git a/src/Parsers/ParserCreateQuery.cpp b/src/Parsers/ParserCreateQuery.cpp index 6d8e81edbea..c54033bd27d 100644 --- a/src/Parsers/ParserCreateQuery.cpp +++ b/src/Parsers/ParserCreateQuery.cpp @@ -489,15 +489,12 @@ bool ParserCreateLiveViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & e ParserToken s_dot(TokenType::Dot); ParserToken s_lparen(TokenType::OpeningRoundBracket); ParserToken s_rparen(TokenType::ClosingRoundBracket); - ParserStorage storage_p; - ParserIdentifier name_p; ParserTablePropertiesDeclarationList table_properties_p; ParserSelectWithUnionQuery select_p; ASTPtr table; ASTPtr to_table; ASTPtr columns_list; - ASTPtr storage; ASTPtr as_database; ASTPtr as_table; ASTPtr select; diff --git a/src/Parsers/ParserShowTablesQuery.cpp b/src/Parsers/ParserShowTablesQuery.cpp index 36caf24e623..ee50d23ffc8 100644 --- a/src/Parsers/ParserShowTablesQuery.cpp +++ b/src/Parsers/ParserShowTablesQuery.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -20,6 +21,8 @@ bool ParserShowTablesQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec ParserKeyword s_temporary("TEMPORARY"); ParserKeyword s_tables("TABLES"); ParserKeyword s_databases("DATABASES"); + ParserKeyword s_clusters("CLUSTERS"); + ParserKeyword s_cluster("CLUSTER"); ParserKeyword s_dictionaries("DICTIONARIES"); ParserKeyword s_from("FROM"); ParserKeyword s_in("IN"); @@ -43,6 +46,36 @@ bool ParserShowTablesQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec { query->databases = true; } + else if (s_clusters.ignore(pos)) + { + query->clusters = true; + + if (s_not.ignore(pos, expected)) + query->not_like = true; + + if (s_like.ignore(pos, expected)) + { + if (!like_p.parse(pos, like, expected)) + return false; + } + else if (query->not_like) + return false; + if (s_limit.ignore(pos, expected)) + { + if (!exp_elem.parse(pos, query->limit_length, expected)) + return false; + } + } + else if (s_cluster.ignore(pos)) + { + query->cluster = true; + + String cluster_str; + if (!parseIdentifierOrStringLiteral(pos, expected, cluster_str)) + return false; + + query->cluster_str = std::move(cluster_str); + } else { if (s_temporary.ignore(pos)) diff --git a/src/Parsers/ParserShowTablesQuery.h b/src/Parsers/ParserShowTablesQuery.h index 1bbd3cb4ef6..4fd11d8e2a0 100644 --- a/src/Parsers/ParserShowTablesQuery.h +++ b/src/Parsers/ParserShowTablesQuery.h @@ -14,7 +14,7 @@ namespace DB class ParserShowTablesQuery : public IParserBase { protected: - const char * getName() const override { return "SHOW [TEMPORARY] TABLES|DATABASES [[NOT] LIKE 'str'] [LIMIT expr]"; } + const char * getName() const override { return "SHOW [TEMPORARY] TABLES|DATABASES|CLUSTERS|CLUSTER 'name' [[NOT] LIKE 'str'] [LIMIT expr]"; } bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; }; diff --git a/src/Parsers/ParserSystemQuery.cpp b/src/Parsers/ParserSystemQuery.cpp index 720ca666023..70a2b339f28 100644 --- a/src/Parsers/ParserSystemQuery.cpp +++ b/src/Parsers/ParserSystemQuery.cpp @@ -59,11 +59,31 @@ bool ParserSystemQuery::parseImpl(IParser::Pos & pos, ASTPtr & node, Expected & case Type::RESTART_REPLICA: case Type::SYNC_REPLICA: - case Type::FLUSH_DISTRIBUTED: if (!parseDatabaseAndTableName(pos, expected, res->database, res->table)) return false; break; + case Type::STOP_DISTRIBUTED_SENDS: + case Type::START_DISTRIBUTED_SENDS: + case Type::FLUSH_DISTRIBUTED: + { + String cluster_str; + if (ParserKeyword{"ON"}.ignore(pos, expected)) + { + if (!ASTQueryWithOnCluster::parse(pos, cluster_str, expected)) + return false; + } + res->cluster = cluster_str; + if (!parseDatabaseAndTableName(pos, expected, res->database, res->table)) + { + /// FLUSH DISTRIBUTED requires table + /// START/STOP DISTRIBUTED SENDS does not requires table + if (res->type == Type::FLUSH_DISTRIBUTED) + return false; + } + break; + } + case Type::STOP_MERGES: case Type::START_MERGES: case Type::STOP_TTL_MERGES: @@ -76,8 +96,6 @@ bool ParserSystemQuery::parseImpl(IParser::Pos & pos, ASTPtr & node, Expected & case Type::START_REPLICATED_SENDS: case Type::STOP_REPLICATION_QUEUES: case Type::START_REPLICATION_QUEUES: - case Type::STOP_DISTRIBUTED_SENDS: - case Type::START_DISTRIBUTED_SENDS: parseDatabaseAndTableName(pos, expected, res->database, res->table); break; diff --git a/src/Processors/ConcatProcessor.cpp b/src/Processors/ConcatProcessor.cpp index 27338c7c879..f4648caf0f0 100644 --- a/src/Processors/ConcatProcessor.cpp +++ b/src/Processors/ConcatProcessor.cpp @@ -4,6 +4,11 @@ namespace DB { +ConcatProcessor::ConcatProcessor(const Block & header, size_t num_inputs) + : IProcessor(InputPorts(num_inputs, header), OutputPorts{header}), current_input(inputs.begin()) +{ +} + ConcatProcessor::Status ConcatProcessor::prepare() { auto & output = outputs.front(); diff --git a/src/Processors/ConcatProcessor.h b/src/Processors/ConcatProcessor.h index 4aa5099b38a..64f9712c69a 100644 --- a/src/Processors/ConcatProcessor.h +++ b/src/Processors/ConcatProcessor.h @@ -16,10 +16,7 @@ namespace DB class ConcatProcessor : public IProcessor { public: - ConcatProcessor(const Block & header, size_t num_inputs) - : IProcessor(InputPorts(num_inputs, header), OutputPorts{header}), current_input(inputs.begin()) - { - } + ConcatProcessor(const Block & header, size_t num_inputs); String getName() const override { return "Concat"; } diff --git a/src/Processors/Formats/Impl/AvroRowInputFormat.cpp b/src/Processors/Formats/Impl/AvroRowInputFormat.cpp index 8017667909b..364e3282f00 100644 --- a/src/Processors/Formats/Impl/AvroRowInputFormat.cpp +++ b/src/Processors/Formats/Impl/AvroRowInputFormat.cpp @@ -638,7 +638,7 @@ private: } catch (const Poco::Exception & e) { - throw Exception(Exception::CreateFromPoco, e); + throw Exception(Exception::CreateFromPocoTag{}, e); } catch (const avro::Exception & e) { diff --git a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp index 96b1ff5a511..59d3814adea 100644 --- a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.cpp @@ -30,14 +30,14 @@ PrettyBlockOutputFormat::PrettyBlockOutputFormat( /// Note that number of code points is just a rough approximation of visible string width. void PrettyBlockOutputFormat::calculateWidths( const Block & header, const Chunk & chunk, - WidthsPerColumn & widths, Widths & max_widths, Widths & name_widths) + WidthsPerColumn & widths, Widths & max_padded_widths, Widths & name_widths) { - size_t num_rows = chunk.getNumRows(); + size_t num_rows = std::min(chunk.getNumRows(), format_settings.pretty.max_rows); size_t num_columns = chunk.getNumColumns(); const auto & columns = chunk.getColumns(); widths.resize(num_columns); - max_widths.resize_fill(num_columns); + max_padded_widths.resize_fill(num_columns); name_widths.resize(num_columns); /// Calculate widths of all values. @@ -57,9 +57,21 @@ void PrettyBlockOutputFormat::calculateWidths( elem.type->serializeAsText(*column, j, out_serialize, format_settings); } - widths[i][j] = std::min(format_settings.pretty.max_column_pad_width, - UTF8::computeWidth(reinterpret_cast(serialized_value.data()), serialized_value.size(), prefix)); - max_widths[i] = std::max(max_widths[i], widths[i][j]); + /// Avoid calculating width of too long strings by limiting the size in bytes. + /// Note that it is just an estimation. 4 is the maximum size of Unicode code point in bytes in UTF-8. + /// But it's possible that the string is long in bytes but very short in visible size. + /// (e.g. non-printable characters, diacritics, combining characters) + if (format_settings.pretty.max_value_width) + { + size_t max_byte_size = format_settings.pretty.max_value_width * 4; + if (serialized_value.size() > max_byte_size) + serialized_value.resize(max_byte_size); + } + + widths[i][j] = UTF8::computeWidth(reinterpret_cast(serialized_value.data()), serialized_value.size(), prefix); + max_padded_widths[i] = std::max(max_padded_widths[i], + std::min(format_settings.pretty.max_column_pad_width, + std::min(format_settings.pretty.max_value_width, widths[i][j]))); } /// And also calculate widths for names of columns. @@ -67,9 +79,9 @@ void PrettyBlockOutputFormat::calculateWidths( // name string doesn't contain Tab, no need to pass `prefix` name_widths[i] = std::min(format_settings.pretty.max_column_pad_width, UTF8::computeWidth(reinterpret_cast(elem.name.data()), elem.name.size())); - max_widths[i] = std::max(max_widths[i], name_widths[i]); + max_padded_widths[i] = std::max(max_padded_widths[i], name_widths[i]); } - prefix += max_widths[i] + 3; + prefix += max_padded_widths[i] + 3; } } @@ -174,18 +186,20 @@ void PrettyBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) if (i != 0) writeString(middle_values_separator_s, out); - writeCString("│ ", out); + writeCString("│", out); for (size_t j = 0; j < num_columns; ++j) { if (j != 0) - writeCString(" │ ", out); + writeCString("│", out); const auto & type = *header.getByPosition(j).type; - writeValueWithPadding(*columns[j], type, i, widths[j].empty() ? max_widths[j] : widths[j][i], max_widths[j]); + writeValueWithPadding(*columns[j], type, i, + widths[j].empty() ? max_widths[j] : widths[j][i], + max_widths[j]); } - writeCString(" │\n", out); + writeCString("│\n", out); } writeString(bottom_separator_s, out); @@ -197,20 +211,42 @@ void PrettyBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) void PrettyBlockOutputFormat::writeValueWithPadding( const IColumn & column, const IDataType & type, size_t row_num, size_t value_width, size_t pad_to_width) { + String serialized_value = " "; + { + WriteBufferFromString out_serialize(serialized_value, WriteBufferFromString::AppendModeTag()); + type.serializeAsText(column, row_num, out_serialize, format_settings); + } + + if (value_width > format_settings.pretty.max_value_width) + { + serialized_value.resize(UTF8::computeBytesBeforeWidth( + reinterpret_cast(serialized_value.data()), serialized_value.size(), 0, 1 + format_settings.pretty.max_value_width)); + + if (format_settings.pretty.color) + serialized_value += "\033[31;1m⋯\033[0m"; + else + serialized_value += "⋯"; + + value_width = format_settings.pretty.max_value_width; + } + else + serialized_value += ' '; + auto write_padding = [&]() { - for (size_t k = 0; k < pad_to_width - value_width; ++k) - writeChar(' ', out); + if (pad_to_width > value_width) + for (size_t k = 0; k < pad_to_width - value_width; ++k) + writeChar(' ', out); }; if (type.shouldAlignRightInPrettyFormats()) { write_padding(); - type.serializeAsText(column, row_num, out, format_settings); + out.write(serialized_value.data(), serialized_value.size()); } else { - type.serializeAsText(column, row_num, out, format_settings); + out.write(serialized_value.data(), serialized_value.size()); write_padding(); } } diff --git a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.h b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.h index eae1c3e9eb1..a873e02c1e4 100644 --- a/src/Processors/Formats/Impl/PrettyBlockOutputFormat.h +++ b/src/Processors/Formats/Impl/PrettyBlockOutputFormat.h @@ -52,7 +52,7 @@ protected: void calculateWidths( const Block & header, const Chunk & chunk, - WidthsPerColumn & widths, Widths & max_widths, Widths & name_widths); + WidthsPerColumn & widths, Widths & max_padded_widths, Widths & name_widths); void writeValueWithPadding( const IColumn & column, const IDataType & type, size_t row_num, size_t value_width, size_t pad_to_width); diff --git a/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp b/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp index 4726332e554..e9040f672b4 100644 --- a/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/PrettyCompactBlockOutputFormat.cpp @@ -83,19 +83,19 @@ void PrettyCompactBlockOutputFormat::writeRow( { size_t num_columns = max_widths.size(); - writeCString("│ ", out); + writeCString("│", out); for (size_t j = 0; j < num_columns; ++j) { if (j != 0) - writeCString(" │ ", out); + writeCString("│", out); const auto & type = *header.getByPosition(j).type; const auto & cur_widths = widths[j].empty() ? max_widths[j] : widths[j][row_num]; writeValueWithPadding(*columns[j], type, row_num, cur_widths, max_widths[j]); } - writeCString(" │\n", out); + writeCString("│\n", out); } void PrettyCompactBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind) diff --git a/src/Processors/Formats/Impl/PrettySpaceBlockOutputFormat.cpp b/src/Processors/Formats/Impl/PrettySpaceBlockOutputFormat.cpp index ca9d1b674d4..6b42ea57e1f 100644 --- a/src/Processors/Formats/Impl/PrettySpaceBlockOutputFormat.cpp +++ b/src/Processors/Formats/Impl/PrettySpaceBlockOutputFormat.cpp @@ -34,6 +34,8 @@ void PrettySpaceBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind { if (i != 0) writeCString(" ", out); + else + writeChar(' ', out); const ColumnWithTypeAndName & col = header.getByPosition(i); @@ -67,7 +69,7 @@ void PrettySpaceBlockOutputFormat::write(const Chunk & chunk, PortKind port_kind for (size_t column = 0; column < num_columns; ++column) { if (column != 0) - writeCString(" ", out); + writeCString(" ", out); const auto & type = *header.getByPosition(column).type; auto & cur_width = widths[column].empty() ? max_widths[column] : widths[column][row]; diff --git a/src/Processors/IProcessor.h b/src/Processors/IProcessor.h index b7c230cb6de..a9bd73d8026 100644 --- a/src/Processors/IProcessor.h +++ b/src/Processors/IProcessor.h @@ -158,11 +158,11 @@ public: static std::string statusToName(Status status); - /** Method 'prepare' is responsible for all cheap ("instantenous": O(1) of data volume, no wait) calculations. + /** Method 'prepare' is responsible for all cheap ("instantaneous": O(1) of data volume, no wait) calculations. * * It may access input and output ports, * indicate the need for work by another processor by returning NeedData or PortFull, - * or indicate the absense of work by returning Finished or Unneeded, + * or indicate the absence of work by returning Finished or Unneeded, * it may pull data from input ports and push data to output ports. * * The method is not thread-safe and must be called from a single thread in one moment of time, diff --git a/src/Processors/QueryPipeline.cpp b/src/Processors/QueryPipeline.cpp index 92c91a81b8a..5b6109440d5 100644 --- a/src/Processors/QueryPipeline.cpp +++ b/src/Processors/QueryPipeline.cpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace DB { @@ -673,8 +674,10 @@ void QueryPipeline::initRowsBeforeLimit() { RowsBeforeLimitCounterPtr rows_before_limit_at_least; + /// TODO: add setRowsBeforeLimitCounter as virtual method to IProcessor. std::vector limits; std::vector sources; + std::vector remote_sources; std::unordered_set visited; @@ -705,6 +708,9 @@ void QueryPipeline::initRowsBeforeLimit() if (auto * source = typeid_cast(processor)) sources.emplace_back(source); + + if (auto * source = typeid_cast(processor)) + remote_sources.emplace_back(source); } else if (auto * sorting = typeid_cast(processor)) { @@ -735,7 +741,7 @@ void QueryPipeline::initRowsBeforeLimit() } } - if (!rows_before_limit_at_least && (!limits.empty() || !sources.empty())) + if (!rows_before_limit_at_least && (!limits.empty() || !sources.empty() || !remote_sources.empty())) { rows_before_limit_at_least = std::make_shared(); @@ -744,6 +750,9 @@ void QueryPipeline::initRowsBeforeLimit() for (auto & source : sources) source->setRowsBeforeLimitCounter(rows_before_limit_at_least); + + for (auto & source : remote_sources) + source->setRowsBeforeLimitCounter(rows_before_limit_at_least); } /// If there is a limit, then enable rows_before_limit_at_least diff --git a/src/Processors/RowsBeforeLimitCounter.h b/src/Processors/RowsBeforeLimitCounter.h index 36ea4a557a8..f5eb40ff84a 100644 --- a/src/Processors/RowsBeforeLimitCounter.h +++ b/src/Processors/RowsBeforeLimitCounter.h @@ -15,6 +15,12 @@ public: rows_before_limit.fetch_add(rows, std::memory_order_release); } + void set(uint64_t rows) + { + setAppliedLimit(); + rows_before_limit.store(rows, std::memory_order_release); + } + uint64_t get() const { return rows_before_limit.load(std::memory_order_acquire); } void setAppliedLimit() { has_applied_limit.store(true, std::memory_order_release); } diff --git a/src/Processors/Sources/DelayedSource.cpp b/src/Processors/Sources/DelayedSource.cpp new file mode 100644 index 00000000000..42a33d00196 --- /dev/null +++ b/src/Processors/Sources/DelayedSource.cpp @@ -0,0 +1,119 @@ +#include +#include "NullSource.h" + +namespace DB +{ + +DelayedSource::DelayedSource(const Block & header, Creator processors_creator) + : IProcessor({}, OutputPorts(3, header)) + , creator(std::move(processors_creator)) +{ +} + +IProcessor::Status DelayedSource::prepare() +{ + /// At first, wait for main input is needed and expand pipeline. + if (inputs.empty()) + { + auto & first_output = outputs.front(); + + /// If main port was finished before callback was called, stop execution. + if (first_output.isFinished()) + { + for (auto & output : outputs) + output.finish(); + + return Status::Finished; + } + + if (!first_output.isNeeded()) + return Status::PortFull; + + /// Call creator callback to get processors. + if (processors.empty()) + return Status::Ready; + + return Status::ExpandPipeline; + } + + /// Process ports in order: main, totals, extremes + auto output = outputs.begin(); + for (auto input = inputs.begin(); input != inputs.end(); ++input, ++output) + { + if (output->isFinished()) + { + input->close(); + continue; + } + + if (!output->isNeeded()) + return Status::PortFull; + + if (input->isFinished()) + { + output->finish(); + continue; + } + + input->setNeeded(); + if (!input->hasData()) + return Status::PortFull; + + output->pushData(input->pullData(true)); + return Status::PortFull; + } + + return Status::Finished; +} + +void DelayedSource::work() +{ + auto pipe = creator(); + + main_output = &pipe.getPort(); + totals_output = pipe.getTotalsPort(); + extremes_output = pipe.getExtremesPort(); + + processors = std::move(pipe).detachProcessors(); + + if (!totals_output) + { + processors.emplace_back(std::make_shared(main_output->getHeader())); + totals_output = &processors.back()->getOutputs().back(); + } + + if (!extremes_output) + { + processors.emplace_back(std::make_shared(main_output->getHeader())); + extremes_output = &processors.back()->getOutputs().back(); + } +} + +Processors DelayedSource::expandPipeline() +{ + /// Add new inputs. They must have the same header as output. + for (const auto & output : {main_output, totals_output, extremes_output}) + { + inputs.emplace_back(outputs.front().getHeader(), this); + /// Connect checks that header is same for ports. + connect(*output, inputs.back()); + inputs.back().setNeeded(); + } + + /// Executor will check that all processors are connected. + return std::move(processors); +} + +Pipe createDelayedPipe(const Block & header, DelayedSource::Creator processors_creator) +{ + auto source = std::make_shared(header, std::move(processors_creator)); + + Pipe pipe(&source->getPort(DelayedSource::Main)); + pipe.setTotalsPort(&source->getPort(DelayedSource::Totals)); + pipe.setExtremesPort(&source->getPort(DelayedSource::Extremes)); + + pipe.addProcessors({std::move(source)}); + return pipe; +} + +} diff --git a/src/Processors/Sources/DelayedSource.h b/src/Processors/Sources/DelayedSource.h new file mode 100644 index 00000000000..31ec1e054fe --- /dev/null +++ b/src/Processors/Sources/DelayedSource.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +namespace DB +{ + +/// DelayedSource delays pipeline calculation until it starts execution. +/// It accepts callback which creates a new pipe. +/// +/// First time when DelayedSource's main output port needs data, callback is called. +/// Then, DelayedSource expands pipeline: adds new inputs and connects pipe with it. +/// Then, DelayedSource just move data from inputs to outputs until finished. +/// +/// It main output port of DelayedSource is never needed, callback won't be called. +class DelayedSource : public IProcessor +{ +public: + using Creator = std::function; + + DelayedSource(const Block & header, Creator processors_creator); + String getName() const override { return "Delayed"; } + + Status prepare() override; + void work() override; + Processors expandPipeline() override; + + enum PortKind { Main = 0, Totals = 1, Extremes = 2 }; + OutputPort & getPort(PortKind kind) { return *std::next(outputs.begin(), kind); } + +private: + Creator creator; + Processors processors; + + /// Outputs from returned pipe. + OutputPort * main_output = nullptr; + OutputPort * totals_output = nullptr; + OutputPort * extremes_output = nullptr; +}; + +/// Creates pipe from DelayedSource. +Pipe createDelayedPipe(const Block & header, DelayedSource::Creator processors_creator); + +} diff --git a/src/Processors/Sources/RemoteSource.cpp b/src/Processors/Sources/RemoteSource.cpp new file mode 100644 index 00000000000..2f76e0c87d4 --- /dev/null +++ b/src/Processors/Sources/RemoteSource.cpp @@ -0,0 +1,132 @@ +#include +#include +#include +#include + +namespace DB +{ + +RemoteSource::RemoteSource(RemoteQueryExecutorPtr executor, bool add_aggregation_info_) + : SourceWithProgress(executor->getHeader(), false) + , add_aggregation_info(add_aggregation_info_), query_executor(std::move(executor)) +{ + /// Add AggregatedChunkInfo if we expect DataTypeAggregateFunction as a result. + const auto & sample = getPort().getHeader(); + for (auto & type : sample.getDataTypes()) + if (typeid_cast(type.get())) + add_aggregation_info = true; +} + +RemoteSource::~RemoteSource() = default; + +Chunk RemoteSource::generate() +{ + if (!was_query_sent) + { + /// Progress method will be called on Progress packet. + query_executor->setProgressCallback([this](const Progress & value) { progress(value); }); + + /// Get rows_before_limit result for remote query from ProfileInfo packet. + query_executor->setProfileInfoCallback([this](const BlockStreamProfileInfo & info) + { + if (rows_before_limit && info.hasAppliedLimit()) + rows_before_limit->set(info.getRowsBeforeLimit()); + }); + + query_executor->sendQuery(); + + was_query_sent = true; + } + + auto block = query_executor->read(); + + if (!block) + { + query_executor->finish(); + return {}; + } + + UInt64 num_rows = block.rows(); + Chunk chunk(block.getColumns(), num_rows); + + if (add_aggregation_info) + { + auto info = std::make_shared(); + info->bucket_num = block.info.bucket_num; + info->is_overflows = block.info.is_overflows; + chunk.setChunkInfo(std::move(info)); + } + + return chunk; +} + +void RemoteSource::onCancel() +{ + query_executor->cancel(); +} + + +RemoteTotalsSource::RemoteTotalsSource(RemoteQueryExecutorPtr executor) + : ISource(executor->getHeader()) + , query_executor(std::move(executor)) +{ +} + +RemoteTotalsSource::~RemoteTotalsSource() = default; + +Chunk RemoteTotalsSource::generate() +{ + if (auto block = query_executor->getTotals()) + { + UInt64 num_rows = block.rows(); + return Chunk(block.getColumns(), num_rows); + } + + return {}; +} + + +RemoteExtremesSource::RemoteExtremesSource(RemoteQueryExecutorPtr executor) + : ISource(executor->getHeader()) + , query_executor(std::move(executor)) +{ +} + +RemoteExtremesSource::~RemoteExtremesSource() = default; + +Chunk RemoteExtremesSource::generate() +{ + if (auto block = query_executor->getExtremes()) + { + UInt64 num_rows = block.rows(); + return Chunk(block.getColumns(), num_rows); + } + + return {}; +} + + +Pipe createRemoteSourcePipe( + RemoteQueryExecutorPtr query_executor, + bool add_aggregation_info, bool add_totals, bool add_extremes) +{ + Pipe pipe(std::make_shared(query_executor, add_aggregation_info)); + + if (add_totals) + { + auto totals_source = std::make_shared(query_executor); + pipe.setTotalsPort(&totals_source->getPort()); + pipe.addProcessors({std::move(totals_source)}); + } + + if (add_extremes) + { + auto extremes_source = std::make_shared(query_executor); + pipe.setExtremesPort(&extremes_source->getPort()); + pipe.addProcessors({std::move(extremes_source)}); + } + + return pipe; +} + +} diff --git a/src/Processors/Sources/RemoteSource.h b/src/Processors/Sources/RemoteSource.h new file mode 100644 index 00000000000..0b4405a0905 --- /dev/null +++ b/src/Processors/Sources/RemoteSource.h @@ -0,0 +1,82 @@ +#pragma once + +#include +#include +#include + +namespace DB +{ + +class RemoteQueryExecutor; +using RemoteQueryExecutorPtr = std::shared_ptr; + +/// Source from RemoteQueryExecutor. Executes remote query and returns query result chunks. +class RemoteSource : public SourceWithProgress +{ +public: + /// Flag add_aggregation_info tells if AggregatedChunkInfo should be added to result chunk. + /// AggregatedChunkInfo stores the bucket number used for two-level aggregation. + /// This flag should be typically enabled for queries with GROUP BY which are executed till WithMergeableState. + RemoteSource(RemoteQueryExecutorPtr executor, bool add_aggregation_info_); + ~RemoteSource() override; + + String getName() const override { return "Remote"; } + + void setRowsBeforeLimitCounter(RowsBeforeLimitCounterPtr counter) { rows_before_limit.swap(counter); } + + /// Stop reading from stream if output port is finished. + void onUpdatePorts() override + { + if (getPort().isFinished()) + cancel(); + } + +protected: + Chunk generate() override; + void onCancel() override; + +private: + bool was_query_sent = false; + bool add_aggregation_info = false; + RemoteQueryExecutorPtr query_executor; + RowsBeforeLimitCounterPtr rows_before_limit; +}; + +/// Totals source from RemoteQueryExecutor. +class RemoteTotalsSource : public ISource +{ +public: + explicit RemoteTotalsSource(RemoteQueryExecutorPtr executor); + ~RemoteTotalsSource() override; + + String getName() const override { return "RemoteTotals"; } + +protected: + Chunk generate() override; + +private: + RemoteQueryExecutorPtr query_executor; +}; + +/// Extremes source from RemoteQueryExecutor. +class RemoteExtremesSource : public ISource +{ +public: + explicit RemoteExtremesSource(RemoteQueryExecutorPtr executor); + ~RemoteExtremesSource() override; + + String getName() const override { return "RemoteExtremes"; } + +protected: + Chunk generate() override; + +private: + RemoteQueryExecutorPtr query_executor; +}; + +/// Create pipe with remote sources. +Pipe createRemoteSourcePipe( + RemoteQueryExecutorPtr query_executor, + bool add_aggregation_info, bool add_totals, bool add_extremes); + +} diff --git a/src/Processors/Sources/SourceWithProgress.cpp b/src/Processors/Sources/SourceWithProgress.cpp index 8d7a0a3d946..6488289d5ce 100644 --- a/src/Processors/Sources/SourceWithProgress.cpp +++ b/src/Processors/Sources/SourceWithProgress.cpp @@ -12,6 +12,11 @@ namespace ErrorCodes extern const int TOO_MANY_BYTES; } +SourceWithProgress::SourceWithProgress(Block header, bool enable_auto_progress) + : ISourceWithProgress(header), auto_progress(enable_auto_progress) +{ +} + void SourceWithProgress::work() { if (!limits.speed_limits.checkTimeLimit(total_stopwatch.elapsed(), limits.timeout_overflow_mode)) @@ -24,7 +29,7 @@ void SourceWithProgress::work() ISourceWithProgress::work(); - if (!was_progress_called && has_input) + if (auto_progress && !was_progress_called && has_input) progress({ current_chunk.chunk.getNumRows(), current_chunk.chunk.bytes() }); } } diff --git a/src/Processors/Sources/SourceWithProgress.h b/src/Processors/Sources/SourceWithProgress.h index 4778c50e49d..34810045143 100644 --- a/src/Processors/Sources/SourceWithProgress.h +++ b/src/Processors/Sources/SourceWithProgress.h @@ -44,6 +44,8 @@ class SourceWithProgress : public ISourceWithProgress { public: using ISourceWithProgress::ISourceWithProgress; + /// If enable_auto_progress flag is set, progress() will be automatically called on each generated chunk. + SourceWithProgress(Block header, bool enable_auto_progress); using LocalLimits = IBlockInputStream::LocalLimits; using LimitsMode = IBlockInputStream::LimitsMode; @@ -76,6 +78,9 @@ private: /// This flag checks if progress() was manually called at generate() call. /// If not, it will be called for chunk after generate() was finished. bool was_progress_called = false; + + /// If enabled, progress() will be automatically called on each generated chunk. + bool auto_progress = true; }; } diff --git a/src/Processors/Transforms/AggregatingInOrderTransform.cpp b/src/Processors/Transforms/AggregatingInOrderTransform.cpp new file mode 100644 index 00000000000..7edeff65ec8 --- /dev/null +++ b/src/Processors/Transforms/AggregatingInOrderTransform.cpp @@ -0,0 +1,244 @@ +#include +#include + +namespace DB +{ + +AggregatingInOrderTransform::AggregatingInOrderTransform( + Block header, AggregatingTransformParamsPtr params_, + const SortDescription & group_by_description_, size_t res_block_size_) + : AggregatingInOrderTransform(std::move(header), std::move(params_) + , group_by_description_, res_block_size_, std::make_unique(1), 0) +{ +} + +AggregatingInOrderTransform::AggregatingInOrderTransform( + Block header, AggregatingTransformParamsPtr params_, + const SortDescription & group_by_description_, size_t res_block_size_, + ManyAggregatedDataPtr many_data_, size_t current_variant) + : IProcessor({std::move(header)}, {params_->getCustomHeader(false)}) + , res_block_size(res_block_size_) + , params(std::move(params_)) + , group_by_description(group_by_description_) + , aggregate_columns(params->params.aggregates_size) + , many_data(std::move(many_data_)) + , variants(*many_data->variants[current_variant]) +{ + /// We won't finalize states in order to merge same states (generated due to multi-thread execution) in AggregatingSortedTransform + res_header = params->getCustomHeader(false); + + /// Replace column names to column position in description_sorted. + for (auto & column_description : group_by_description) + { + if (!column_description.column_name.empty()) + { + column_description.column_number = res_header.getPositionByName(column_description.column_name); + column_description.column_name.clear(); + } + } +} + +AggregatingInOrderTransform::~AggregatingInOrderTransform() = default; + +static bool less(const MutableColumns & lhs, const Columns & rhs, size_t i, size_t j, const SortDescription & descr) +{ + for (const auto & elem : descr) + { + size_t ind = elem.column_number; + int res = elem.direction * lhs[ind]->compareAt(i, j, *rhs[ind], elem.nulls_direction); + if (res < 0) + return true; + else if (res > 0) + return false; + } + return false; +} + + +void AggregatingInOrderTransform::consume(Chunk chunk) +{ + size_t rows = chunk.getNumRows(); + if (rows == 0) + return; + + if (!is_consume_started) + { + LOG_TRACE(log, "Aggregating in order"); + is_consume_started = true; + } + src_rows += rows; + src_bytes += chunk.bytes(); + + Columns materialized_columns; + Columns key_columns(params->params.keys_size); + for (size_t i = 0; i < params->params.keys_size; ++i) + { + materialized_columns.push_back(chunk.getColumns().at(params->params.keys[i])->convertToFullColumnIfConst()); + key_columns[i] = materialized_columns.back(); + } + + Aggregator::NestedColumnsHolder nested_columns_holder; + Aggregator::AggregateFunctionInstructions aggregate_function_instructions; + params->aggregator.prepareAggregateInstructions(chunk.getColumns(), aggregate_columns, materialized_columns, aggregate_function_instructions, nested_columns_holder); + + size_t key_end = 0; + size_t key_begin = 0; + /// If we don't have a block we create it and fill with first key + if (!cur_block_size) + { + res_key_columns.resize(params->params.keys_size); + res_aggregate_columns.resize(params->params.aggregates_size); + + for (size_t i = 0; i < params->params.keys_size; ++i) + { + res_key_columns[i] = res_header.safeGetByPosition(i).type->createColumn(); + } + for (size_t i = 0; i < params->params.aggregates_size; ++i) + { + res_aggregate_columns[i] = res_header.safeGetByPosition(i + params->params.keys_size).type->createColumn(); + } + params->aggregator.createStatesAndFillKeyColumnsWithSingleKey(variants, key_columns, key_begin, res_key_columns); + ++cur_block_size; + } + ssize_t mid = 0; + ssize_t high = 0; + ssize_t low = -1; + /// Will split block into segments with the same key + while (key_end != rows) + { + high = rows; + /// Find the first position of new (not current) key in current chunk + while (high - low > 1) + { + mid = (low + high) / 2; + if (!less(res_key_columns, key_columns, cur_block_size - 1, mid, group_by_description)) + low = mid; + else + high = mid; + } + key_end = high; + /// Add data to aggr. state if interval is not empty. Empty when haven't found current key in new block. + if (key_begin != key_end) + { + params->aggregator.executeOnIntervalWithoutKeyImpl(variants.without_key, key_begin, key_end, aggregate_function_instructions.data(), variants.aggregates_pool); + } + + low = key_begin = key_end; + /// We finalize last key aggregation state if a new key found. + if (key_begin != rows) + { + params->aggregator.fillAggregateColumnsWithSingleKey(variants, res_aggregate_columns); + /// If res_block_size is reached we have to stop consuming and generate the block. Save the extra rows into new chunk. + if (cur_block_size == res_block_size) + { + Columns source_columns = chunk.detachColumns(); + + for (auto & source_column : source_columns) + source_column = source_column->cut(key_begin, rows - key_begin); + + current_chunk = Chunk(source_columns, rows - key_begin); + block_end_reached = true; + need_generate = true; + cur_block_size = 0; + return; + } + + /// We create a new state for the new key and update res_key_columns + params->aggregator.createStatesAndFillKeyColumnsWithSingleKey(variants, key_columns, key_begin, res_key_columns); + ++cur_block_size; + } + } + block_end_reached = false; +} + + +void AggregatingInOrderTransform::work() +{ + if (is_consume_finished || need_generate) + { + generate(); + } + else + { + consume(std::move(current_chunk)); + } +} + + +IProcessor::Status AggregatingInOrderTransform::prepare() +{ + auto & output = outputs.front(); + auto & input = inputs.back(); + + /// Check can output. + if (output.isFinished()) + { + input.close(); + return Status::Finished; + } + + if (!output.canPush()) + { + input.setNotNeeded(); + return Status::PortFull; + } + + if (block_end_reached) + { + if (need_generate) + { + return Status::Ready; + } + else + { + output.push(std::move(to_push_chunk)); + return Status::Ready; + } + } + else + { + if (is_consume_finished) + { + output.push(std::move(to_push_chunk)); + output.finish(); + LOG_TRACE(log, "Aggregated. {} to {} rows (from {})", src_rows, res_rows, + formatReadableSizeWithBinarySuffix(src_bytes)); + return Status::Finished; + } + if (input.isFinished()) + { + is_consume_finished = true; + return Status::Ready; + } + } + if (!input.hasData()) + { + input.setNeeded(); + return Status::NeedData; + } + current_chunk = input.pull(!is_consume_finished); + return Status::Ready; +} + +void AggregatingInOrderTransform::generate() +{ + if (cur_block_size && is_consume_finished) + params->aggregator.fillAggregateColumnsWithSingleKey(variants, res_aggregate_columns); + + Block res = res_header.cloneEmpty(); + + for (size_t i = 0; i < res_key_columns.size(); ++i) + { + res.getByPosition(i).column = std::move(res_key_columns[i]); + } + for (size_t i = 0; i < res_aggregate_columns.size(); ++i) + { + res.getByPosition(i + res_key_columns.size()).column = std::move(res_aggregate_columns[i]); + } + to_push_chunk = convertToChunk(res); + res_rows += to_push_chunk.getNumRows(); + need_generate = false; +} + + +} diff --git a/src/Processors/Transforms/AggregatingInOrderTransform.h b/src/Processors/Transforms/AggregatingInOrderTransform.h new file mode 100644 index 00000000000..10793e885ce --- /dev/null +++ b/src/Processors/Transforms/AggregatingInOrderTransform.h @@ -0,0 +1,92 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace DB +{ + +class AggregatingInOrderTransform : public IProcessor +{ + +public: + AggregatingInOrderTransform(Block header, AggregatingTransformParamsPtr params, + const SortDescription & group_by_description, size_t res_block_size, + ManyAggregatedDataPtr many_data, size_t current_variant); + + AggregatingInOrderTransform(Block header, AggregatingTransformParamsPtr params, + const SortDescription & group_by_description, size_t res_block_size); + + ~AggregatingInOrderTransform() override; + + String getName() const override { return "AggregatingInOrderTransform"; } + + Status prepare() override; + + void work() override; + + void consume(Chunk chunk); + +private: + void generate(); + + size_t res_block_size; + size_t cur_block_size = 0; + + MutableColumns res_key_columns; + MutableColumns res_aggregate_columns; + + AggregatingTransformParamsPtr params; + SortDescription group_by_description; + + Aggregator::AggregateColumns aggregate_columns; + + ManyAggregatedDataPtr many_data; + AggregatedDataVariants & variants; + + UInt64 src_rows = 0; + UInt64 src_bytes = 0; + UInt64 res_rows = 0; + + bool need_generate = false; + bool block_end_reached = false; + bool is_consume_started = false; + bool is_consume_finished = false; + + Block res_header; + Chunk current_chunk; + Chunk to_push_chunk; + + Poco::Logger * log = &Poco::Logger::get("AggregatingInOrderTransform"); +}; + + +class FinalizingSimpleTransform : public ISimpleTransform +{ +public: + FinalizingSimpleTransform(Block header, AggregatingTransformParamsPtr params_) + : ISimpleTransform({std::move(header)}, {params_->getHeader()}, true) + , params(params_) {} + + void transform(Chunk & chunk) override + { + if (params->final) + finalizeChunk(chunk); + else if (!chunk.getChunkInfo()) + { + auto info = std::make_shared(); + chunk.setChunkInfo(std::move(info)); + } + } + + String getName() const override { return "FinalizingSimpleTransform"; } + +private: + AggregatingTransformParamsPtr params; +}; + + +} diff --git a/src/Processors/Transforms/AggregatingTransform.cpp b/src/Processors/Transforms/AggregatingTransform.cpp index d8eff53f567..c5be62e276a 100644 --- a/src/Processors/Transforms/AggregatingTransform.cpp +++ b/src/Processors/Transforms/AggregatingTransform.cpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -20,23 +19,23 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } +/// Convert block to chunk. +/// Adds additional info about aggregation. +Chunk convertToChunk(const Block & block) +{ + auto info = std::make_shared(); + info->bucket_num = block.info.bucket_num; + info->is_overflows = block.info.is_overflows; + + UInt64 num_rows = block.rows(); + Chunk chunk(block.getColumns(), num_rows); + chunk.setChunkInfo(std::move(info)); + + return chunk; +} + namespace { - /// Convert block to chunk. - /// Adds additional info about aggregation. - Chunk convertToChunk(const Block & block) - { - auto info = std::make_shared(); - info->bucket_num = block.info.bucket_num; - info->is_overflows = block.info.is_overflows; - - UInt64 num_rows = block.rows(); - Chunk chunk(block.getColumns(), num_rows); - chunk.setChunkInfo(std::move(info)); - - return chunk; - } - const AggregatedChunkInfo * getInfoFromChunk(const Chunk & chunk) { const auto & info = chunk.getChunkInfo(); diff --git a/src/Processors/Transforms/AggregatingTransform.h b/src/Processors/Transforms/AggregatingTransform.h index c2693579c67..235d01ebc77 100644 --- a/src/Processors/Transforms/AggregatingTransform.h +++ b/src/Processors/Transforms/AggregatingTransform.h @@ -28,6 +28,8 @@ struct AggregatingTransformParams : params(params_), aggregator(params), final(final_) {} Block getHeader() const { return aggregator.getHeader(final); } + + Block getCustomHeader(bool final_) const { return aggregator.getHeader(final_); } }; struct ManyAggregatedData @@ -117,4 +119,6 @@ private: void initGenerate(); }; +Chunk convertToChunk(const Block & block); + } diff --git a/src/Processors/Transforms/FinishSortingTransform.cpp b/src/Processors/Transforms/FinishSortingTransform.cpp index 4c904eb95a1..b58b008339d 100644 --- a/src/Processors/Transforms/FinishSortingTransform.cpp +++ b/src/Processors/Transforms/FinishSortingTransform.cpp @@ -112,7 +112,7 @@ void FinishSortingTransform::consume(Chunk chunk) } } - /// If we reach here, that means that current cunk is first in portion + /// If we reach here, that means that current chunk is first in portion /// or it all consists of rows with the same key as tail of a previous chunk. chunks.push_back(std::move(chunk)); } diff --git a/src/Processors/Transforms/MergingAggregatedMemoryEfficientTransform.h b/src/Processors/Transforms/MergingAggregatedMemoryEfficientTransform.h index bf23b67e204..1ed1979c0d4 100644 --- a/src/Processors/Transforms/MergingAggregatedMemoryEfficientTransform.h +++ b/src/Processors/Transforms/MergingAggregatedMemoryEfficientTransform.h @@ -8,6 +8,50 @@ namespace DB { +/** Pre-aggregates data from ports, holding in RAM only one or more (up to merging_threads) blocks from each source. + * This saves RAM in case of using two-level aggregation, where in each source there will be up to 256 blocks with parts of the result. + * + * Aggregate functions in blocks should not be finalized so that their states can be combined. + * + * Used to solve two tasks: + * + * 1. External aggregation with data flush to disk. + * Partially aggregated data (previously divided into 256 buckets) is flushed to some number of files on the disk. + * We need to read them and merge them by buckets - keeping only a few buckets from each file in RAM simultaneously. + * + * 2. Merge aggregation results for distributed query processing. + * Partially aggregated data arrives from different servers, which can be splitted down or not, into 256 buckets, + * and these buckets are passed to us by the network from each server in sequence, one by one. + * You should also read and merge by the buckets. + * + * The essence of the work: + * + * There are a number of sources. They give out blocks with partially aggregated data. + * Each source can return one of the following block sequences: + * 1. "unsplitted" block with bucket_num = -1; + * 2. "splitted" (two_level) blocks with bucket_num from 0 to 255; + * In both cases, there may also be a block of "overflows" with bucket_num = -1 and is_overflows = true; + * + * We start from the convention that splitted blocks are always passed in the order of bucket_num. + * That is, if a < b, then the bucket_num = a block goes before bucket_num = b. + * This is needed for a memory-efficient merge + * - so that you do not need to read the blocks up front, but go all the way up by bucket_num. + * + * In this case, not all bucket_num from the range of 0..255 can be present. + * The overflow block can be presented in any order relative to other blocks (but it can be only one). + * + * It is necessary to combine these sequences of blocks and return the result as a sequence with the same properties. + * That is, at the output, if there are "splitted" blocks in the sequence, then they should go in the order of bucket_num. + * + * The merge can be performed using several (merging_threads) threads. + * For this, receiving of a set of blocks for the next bucket_num should be done sequentially, + * and then, when we have several received sets, they can be merged in parallel. + * + * When you receive next blocks from different sources, + * data from sources can also be read in several threads (reading_threads) + * for optimal performance in the presence of a fast network or disks (from where these blocks are read). + */ + /// Has several inputs and single output. /// Read from inputs chunks with partially aggregated data, group them by bucket number /// and write data from single bucket as single chunk. diff --git a/src/Processors/Transforms/SortingTransform.cpp b/src/Processors/Transforms/SortingTransform.cpp index e56db6e3842..03c8e87ce7a 100644 --- a/src/Processors/Transforms/SortingTransform.cpp +++ b/src/Processors/Transforms/SortingTransform.cpp @@ -285,7 +285,9 @@ IProcessor::Status SortingTransform::prepareGenerate() if (output.isFinished()) { - inputs.front().close(); + for (auto & input : inputs) + input.close(); + return Status::Finished; } diff --git a/src/Processors/Transforms/TotalsHavingTransform.h b/src/Processors/Transforms/TotalsHavingTransform.h index b6069da66f3..f16b333ffd4 100644 --- a/src/Processors/Transforms/TotalsHavingTransform.h +++ b/src/Processors/Transforms/TotalsHavingTransform.h @@ -1,5 +1,6 @@ -#include +#pragma once +#include #include namespace DB diff --git a/src/Processors/tests/CMakeLists.txt b/src/Processors/tests/CMakeLists.txt index 4ddb6c68416..e69de29bb2d 100644 --- a/src/Processors/tests/CMakeLists.txt +++ b/src/Processors/tests/CMakeLists.txt @@ -1,15 +0,0 @@ -add_executable (processors_test processors_test.cpp) -add_executable (processors_test_chain processors_test_chain.cpp) -add_executable (processors_test_merge processors_test_merge.cpp) -add_executable (processors_test_merging_sorted_transform processors_test_merging_sorted_transform.cpp) -add_executable (processors_test_merge_sorting_transform processors_test_merge_sorting_transform.cpp) -add_executable (processors_test_expand_pipeline processors_test_expand_pipeline.cpp) -add_executable (processors_test_aggregation processors_test_aggregation.cpp) - -target_link_libraries (processors_test PRIVATE dbms) -target_link_libraries (processors_test_chain PRIVATE dbms) -target_link_libraries (processors_test_merge PRIVATE dbms) -target_link_libraries (processors_test_expand_pipeline PRIVATE dbms) -target_link_libraries (processors_test_merging_sorted_transform PRIVATE dbms) -target_link_libraries (processors_test_merge_sorting_transform PRIVATE dbms) -target_link_libraries (processors_test_aggregation PRIVATE dbms clickhouse_aggregate_functions) diff --git a/src/Processors/tests/processors_test.cpp b/src/Processors/tests/processors_test.cpp deleted file mode 100644 index 3c73223e59c..00000000000 --- a/src/Processors/tests/processors_test.cpp +++ /dev/null @@ -1,228 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - -using namespace DB; - - -class NumbersSource : public ISource -{ -public: - String getName() const override { return "Numbers"; } - - NumbersSource(UInt64 start_number, unsigned sleep_useconds_) - : ISource(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - current_number(start_number), sleep_useconds(sleep_useconds_) - { - } - -private: - UInt64 current_number = 0; - unsigned sleep_useconds; - - Chunk generate() override - { - usleep(sleep_useconds); - - MutableColumns columns; - columns.emplace_back(ColumnUInt64::create(1, current_number)); - ++current_number; - return Chunk(std::move(columns), 1); - } -}; - - -class SleepyNumbersSource : public IProcessor -{ -protected: - OutputPort & output; - -public: - String getName() const override { return "SleepyNumbers"; } - - SleepyNumbersSource(UInt64 start_number, unsigned sleep_useconds_) - : IProcessor({}, {Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})}) - , output(outputs.front()), current_number(start_number), sleep_useconds(sleep_useconds_) - { - } - - Status prepare() override - { - if (active) - return Status::Wait; - - if (output.isFinished()) - return Status::Finished; - - if (!output.canPush()) - return Status::PortFull; - - if (!current_chunk) - return Status::Async; - - output.push(std::move(current_chunk)); - return Status::Async; - } - - void schedule(EventCounter & watch) override - { - active = true; - pool.scheduleOrThrowOnError([&watch, this] - { - usleep(sleep_useconds); - current_chunk = generate(); - active = false; - watch.notify(); - }); - } - - OutputPort & getPort() { return output; } - -private: - ThreadPool pool{1, 1, 0}; - Chunk current_chunk; - std::atomic_bool active {false}; - - UInt64 current_number = 0; - unsigned sleep_useconds; - - Chunk generate() - { - MutableColumns columns; - columns.emplace_back(ColumnUInt64::create(1, current_number)); - ++current_number; - return Chunk(std::move(columns), 1); - } -}; - - -class PrintSink : public ISink -{ -public: - String getName() const override { return "Print"; } - - explicit PrintSink(String prefix_) - : ISink(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - prefix(std::move(prefix_)) - { - } - -private: - String prefix; - WriteBufferFromFileDescriptor out{STDOUT_FILENO}; - FormatSettings settings; - - void consume(Chunk chunk) override - { - size_t rows = chunk.getNumRows(); - size_t columns = chunk.getNumColumns(); - - for (size_t row_num = 0; row_num < rows; ++row_num) - { - writeString(prefix, out); - for (size_t column_num = 0; column_num < columns; ++column_num) - { - if (column_num != 0) - writeChar('\t', out); - getPort().getHeader().getByPosition(column_num).type->serializeAsText(*chunk.getColumns()[column_num], row_num, out, settings); - } - writeChar('\n', out); - } - - out.next(); - } -}; - - -int main(int, char **) -try -{ - auto source0 = std::make_shared(0, 300000); - auto header = source0->getPort().getHeader(); - auto limit0 = std::make_shared(header, 10, 0); - - connect(source0->getPort(), limit0->getInputPort()); - - auto queue = std::make_shared(header); - - connect(limit0->getOutputPort(), queue->getInputPort()); - - auto source1 = std::make_shared(100, 100000); - auto source2 = std::make_shared(1000, 200000); - - auto source3 = std::make_shared(10, 100000); - auto limit3 = std::make_shared(header, 5, 0); - - connect(source3->getPort(), limit3->getInputPort()); - - auto source4 = std::make_shared(10, 100000); - auto limit4 = std::make_shared(header, 5, 0); - - connect(source4->getPort(), limit4->getInputPort()); - - auto concat = std::make_shared(header, 2); - - connect(limit3->getOutputPort(), concat->getInputs().front()); - connect(limit4->getOutputPort(), concat->getInputs().back()); - - auto fork = std::make_shared(header, 2); - - connect(concat->getOutputPort(), fork->getInputPort()); - - auto print_after_concat = std::make_shared("---------- "); - - connect(fork->getOutputs().back(), print_after_concat->getPort()); - - auto resize = std::make_shared(header, 4, 1); - - auto input_it = resize->getInputs().begin(); - connect(queue->getOutputPort(), *(input_it++)); - connect(source1->getPort(), *(input_it++)); - connect(source2->getPort(), *(input_it++)); - connect(fork->getOutputs().front(), *(input_it++)); - - auto limit = std::make_shared(header, 100, 0); - - connect(resize->getOutputs().front(), limit->getInputPort()); - - auto sink = std::make_shared(""); - - connect(limit->getOutputPort(), sink->getPort()); - - WriteBufferFromOStream out(std::cout); - std::vector processors = {source0, source1, source2, source3, source4, limit0, limit3, limit4, limit, - queue, concat, fork, print_after_concat, resize, sink}; - printPipeline(processors, out); - - // ThreadPool pool(4, 4, 10); - PipelineExecutor executor(processors); - /// SequentialPipelineExecutor executor({sink}); - - executor.execute(1); - - return 0; -} -catch (...) -{ - std::cerr << getCurrentExceptionMessage(true) << '\n'; - throw; -} diff --git a/src/Processors/tests/processors_test_aggregation.cpp b/src/Processors/tests/processors_test_aggregation.cpp deleted file mode 100644 index 9b8bee67d52..00000000000 --- a/src/Processors/tests/processors_test_aggregation.cpp +++ /dev/null @@ -1,411 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -using namespace DB; - -namespace DB::ErrorCodes -{ - extern const int LOGICAL_ERROR; -} - -class NumbersSource : public ISource -{ -public: - String getName() const override { return "Numbers"; } - - NumbersSource(UInt64 start_number, UInt64 step_, UInt64 block_size_, unsigned sleep_useconds_) - : ISource(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - current_number(start_number), step(step_), block_size(block_size_), sleep_useconds(sleep_useconds_) - { - } - -private: - UInt64 current_number = 0; - UInt64 step; - UInt64 block_size; - unsigned sleep_useconds; - - Chunk generate() override - { - usleep(sleep_useconds); - - MutableColumns columns; - columns.emplace_back(ColumnUInt64::create()); - - for (UInt64 i = 0; i < block_size; ++i, current_number += step) - columns.back()->insert(Field(current_number)); - - return Chunk(std::move(columns), block_size); - } -}; - -class PrintSink : public ISink -{ -public: - String getName() const override { return "Print"; } - - PrintSink(String prefix_, Block header) - : ISink(std::move(header)), - prefix(std::move(prefix_)) - { - } - -private: - String prefix; - WriteBufferFromFileDescriptor out{STDOUT_FILENO}; - FormatSettings settings; - - void consume(Chunk chunk) override - { - size_t rows = chunk.getNumRows(); - size_t columns = chunk.getNumColumns(); - - for (size_t row_num = 0; row_num < rows; ++row_num) - { - writeString(prefix, out); - for (size_t column_num = 0; column_num < columns; ++column_num) - { - if (column_num != 0) - writeChar('\t', out); - getPort().getHeader().getByPosition(column_num).type->serializeAsText(*chunk.getColumns()[column_num], row_num, out, settings); - } - writeChar('\n', out); - } - - out.next(); - } -}; - -class CheckSink : public ISink -{ -public: - String getName() const override { return "Check"; } - - CheckSink(Block header, size_t num_rows) - : ISink(std::move(header)), read_rows(num_rows, false) - { - } - - void checkAllRead() - { - for (size_t i = 0; i < read_rows.size(); ++i) - { - if (!read_rows[i]) - { - throw Exception("Check Failed. Row " + toString(i) + " was not read.", ErrorCodes::LOGICAL_ERROR); - } - } - } - -private: - std::vector read_rows; - - void consume(Chunk chunk) override - { - size_t rows = chunk.getNumRows(); - size_t columns = chunk.getNumColumns(); - - for (size_t row_num = 0; row_num < rows; ++row_num) - { - std::vector values(columns); - for (size_t column_num = 0; column_num < columns; ++column_num) - { - values[column_num] = chunk.getColumns()[column_num]->getUInt(row_num); - } - - if (values.size() >= 2 && 3 * values[0] != values[1]) - throw Exception("Check Failed. Got (" + toString(values[0]) + ", " + toString(values[1]) + ") in result," - + "but " + toString(values[0]) + " * 3 != " + toString(values[1]), - ErrorCodes::LOGICAL_ERROR); - - if (values[0] >= read_rows.size()) - throw Exception("Check Failed. Got string with number " + toString(values[0]) + - " (max " + toString(read_rows.size()), ErrorCodes::LOGICAL_ERROR); - - if (read_rows[values[0]]) - throw Exception("Row " + toString(values[0]) + " was already read.", ErrorCodes::LOGICAL_ERROR); - - read_rows[values[0]] = true; - } - } -}; - -template -struct Measure -{ - template - static typename TimeT::rep execution(F&& func, Args&&... args) - { - auto start = std::chrono::steady_clock::now(); - std::forward(func)(std::forward(args)...); - auto duration = std::chrono::duration_cast< TimeT> - (std::chrono::steady_clock::now() - start); - return duration.count(); - } -}; - -int main(int, char **) -try -{ - ThreadStatus thread_status; - CurrentThread::initializeQuery(); - auto thread_group = CurrentThread::getGroup(); - - Poco::AutoPtr channel = new Poco::ConsoleChannel(std::cerr); - Poco::Logger::root().setChannel(channel); - Poco::Logger::root().setLevel("trace"); - - registerAggregateFunctions(); - auto & factory = AggregateFunctionFactory::instance(); - - auto cur_path = Poco::Path().absolute().toString(); - auto disk = std::make_shared("tmp", cur_path, 0); - auto tmp_volume = std::make_shared("tmp", std::vector{disk}, 0); - - auto execute_one_stream = [&](String msg, size_t num_threads, bool two_level, bool external) - { - std::cerr << '\n' << msg << "\n"; - - size_t num_rows = 1000000; - size_t block_size = 1000; - - auto source1 = std::make_shared(0, 1, block_size, 0); - auto source2 = std::make_shared(0, 1, block_size, 0); - auto source3 = std::make_shared(0, 1, block_size, 0); - - auto limit1 = std::make_shared(source1->getPort().getHeader(), num_rows, 0); - auto limit2 = std::make_shared(source2->getPort().getHeader(), num_rows, 0); - auto limit3 = std::make_shared(source3->getPort().getHeader(), num_rows, 0); - - auto resize = std::make_shared(source1->getPort().getHeader(), 3, 1); - - AggregateDescriptions aggregate_descriptions(1); - - DataTypes sum_types = { std::make_shared() }; - aggregate_descriptions[0].function = factory.get("sum", sum_types); - aggregate_descriptions[0].arguments = {0}; - - bool overflow_row = false; /// Without overflow row. - size_t max_rows_to_group_by = 0; /// All. - size_t group_by_two_level_threshold = two_level ? 10 : 0; - size_t group_by_two_level_threshold_bytes = two_level ? 128 : 0; - size_t max_bytes_before_external_group_by = external ? 10000000 : 0; - - Aggregator::Params params( - source1->getPort().getHeader(), - {0}, - aggregate_descriptions, - overflow_row, - max_rows_to_group_by, - OverflowMode::THROW, - group_by_two_level_threshold, - group_by_two_level_threshold_bytes, - max_bytes_before_external_group_by, - false, /// empty_result_for_aggregation_by_empty_set - tmp_volume, - 1, /// max_threads - 0 - ); - - auto agg_params = std::make_shared(params, /* final =*/ false); - auto merge_params = std::make_shared(params, /* final =*/ true); - auto aggregating = std::make_shared(source1->getPort().getHeader(), agg_params); - auto merging = std::make_shared(aggregating->getOutputs().front().getHeader(), merge_params, 4); - auto sink = std::make_shared(merging->getOutputPort().getHeader(), num_rows); - - connect(source1->getPort(), limit1->getInputPort()); - connect(source2->getPort(), limit2->getInputPort()); - connect(source3->getPort(), limit3->getInputPort()); - - auto it = resize->getInputs().begin(); - connect(limit1->getOutputPort(), *(it++)); - connect(limit2->getOutputPort(), *(it++)); - connect(limit3->getOutputPort(), *(it++)); - - connect(resize->getOutputs().front(), aggregating->getInputs().front()); - connect(aggregating->getOutputs().front(), merging->getInputPort()); - connect(merging->getOutputPort(), sink->getPort()); - - std::vector processors = {source1, source2, source3, - limit1, limit2, limit3, - resize, aggregating, merging, sink}; -// WriteBufferFromOStream out(std::cout); -// printPipeline(processors, out); - - PipelineExecutor executor(processors); - executor.execute(num_threads); - sink->checkAllRead(); - }; - - auto execute_mult_streams = [&](String msg, size_t num_threads, bool two_level, bool external) - { - std::cerr << '\n' << msg << "\n"; - - size_t num_rows = 1000000; - size_t block_size = 1000; - - auto source1 = std::make_shared(0, 1, block_size, 0); - auto source2 = std::make_shared(0, 1, block_size, 0); - auto source3 = std::make_shared(0, 1, block_size, 0); - - auto limit1 = std::make_shared(source1->getPort().getHeader(), num_rows, 0); - auto limit2 = std::make_shared(source2->getPort().getHeader(), num_rows, 0); - auto limit3 = std::make_shared(source3->getPort().getHeader(), num_rows, 0); - - AggregateDescriptions aggregate_descriptions(1); - - DataTypes sum_types = { std::make_shared() }; - aggregate_descriptions[0].function = factory.get("sum", sum_types); - aggregate_descriptions[0].arguments = {0}; - - bool overflow_row = false; /// Without overflow row. - size_t max_rows_to_group_by = 0; /// All. - size_t group_by_two_level_threshold = two_level ? 10 : 0; - size_t group_by_two_level_threshold_bytes = two_level ? 128 : 0; - size_t max_bytes_before_external_group_by = external ? 10000000 : 0; - - Aggregator::Params params( - source1->getPort().getHeader(), - {0}, - aggregate_descriptions, - overflow_row, - max_rows_to_group_by, - OverflowMode::THROW, - group_by_two_level_threshold, - group_by_two_level_threshold_bytes, - max_bytes_before_external_group_by, - false, /// empty_result_for_aggregation_by_empty_set - tmp_volume, - 1, /// max_threads - 0 - ); - - auto agg_params = std::make_shared(params, /* final =*/ false); - auto merge_params = std::make_shared(params, /* final =*/ true); - - ManyAggregatedDataPtr data = std::make_unique(3); - - auto aggregating1 = std::make_shared(source1->getPort().getHeader(), agg_params, data, 0, 4, 4); - auto aggregating2 = std::make_shared(source1->getPort().getHeader(), agg_params, data, 1, 4, 4); - auto aggregating3 = std::make_shared(source1->getPort().getHeader(), agg_params, data, 2, 4, 4); - - Processors merging_pipe = createMergingAggregatedMemoryEfficientPipe( - aggregating1->getOutputs().front().getHeader(), - merge_params, - 3, 2); - - auto sink = std::make_shared(merging_pipe.back()->getOutputs().back().getHeader(), num_rows); - - connect(source1->getPort(), limit1->getInputPort()); - connect(source2->getPort(), limit2->getInputPort()); - connect(source3->getPort(), limit3->getInputPort()); - - connect(limit1->getOutputPort(), aggregating1->getInputs().front()); - connect(limit2->getOutputPort(), aggregating2->getInputs().front()); - connect(limit3->getOutputPort(), aggregating3->getInputs().front()); - - auto it = merging_pipe.front()->getInputs().begin(); - connect(aggregating1->getOutputs().front(), *(it++)); - connect(aggregating2->getOutputs().front(), *(it++)); - connect(aggregating3->getOutputs().front(), *(it++)); - - connect(merging_pipe.back()->getOutputs().back(), sink->getPort()); - - std::vector processors = {source1, source2, source3, - limit1, limit2, limit3, - aggregating1, aggregating2, aggregating3, sink}; - - processors.insert(processors.end(), merging_pipe.begin(), merging_pipe.end()); -// WriteBufferFromOStream out(std::cout); -// printPipeline(processors, out); - - PipelineExecutor executor(processors); - executor.execute(num_threads); - sink->checkAllRead(); - }; - - std::vector messages; - std::vector times; - - auto exec = [&](auto func, String msg, size_t num_threads, bool two_level, bool external) - { - msg += ", two_level = " + toString(two_level) + ", external = " + toString(external); - Int64 time = 0; - - auto wrapper = [&]() - { - ThreadStatus cur_status; - - CurrentThread::attachToIfDetached(thread_group); - time = Measure<>::execution(func, msg, num_threads, two_level, external); - }; - - std::thread thread(wrapper); - thread.join(); - - messages.emplace_back(msg); - times.emplace_back(time); - }; - - size_t num_threads = 4; - - exec(execute_one_stream, "One stream, single thread", 1, false, false); - exec(execute_one_stream, "One stream, multiple threads", num_threads, false, false); - - exec(execute_mult_streams, "Multiple streams, single thread", 1, false, false); - exec(execute_mult_streams, "Multiple streams, multiple threads", num_threads, false, false); - - exec(execute_one_stream, "One stream, single thread", 1, true, false); - exec(execute_one_stream, "One stream, multiple threads", num_threads, true, false); - - exec(execute_mult_streams, "Multiple streams, single thread", 1, true, false); - exec(execute_mult_streams, "Multiple streams, multiple threads", num_threads, true, false); - - exec(execute_one_stream, "One stream, single thread", 1, true, true); - exec(execute_one_stream, "One stream, multiple threads", num_threads, true, true); - - exec(execute_mult_streams, "Multiple streams, single thread", 1, true, true); - exec(execute_mult_streams, "Multiple streams, multiple threads", num_threads, true, true); - - for (size_t i = 0; i < messages.size(); ++i) - std::cout << messages[i] << " time: " << times[i] << " ms.\n"; - - return 0; -} -catch (...) -{ - std::cerr << getCurrentExceptionMessage(true) << '\n'; - throw; -} diff --git a/src/Processors/tests/processors_test_chain.cpp b/src/Processors/tests/processors_test_chain.cpp deleted file mode 100644 index 0fbd52eef39..00000000000 --- a/src/Processors/tests/processors_test_chain.cpp +++ /dev/null @@ -1,165 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include - - -using namespace DB; - - -class NumbersSource : public ISource -{ -public: - String getName() const override { return "Numbers"; } - - NumbersSource(UInt64 start_number, unsigned sleep_useconds_) - : ISource(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - current_number(start_number), sleep_useconds(sleep_useconds_) - { - } - -private: - UInt64 current_number = 0; - unsigned sleep_useconds; - - Chunk generate() override - { - usleep(sleep_useconds); - - MutableColumns columns; - columns.emplace_back(ColumnUInt64::create(1, current_number)); - ++current_number; - return Chunk(std::move(columns), 1); - } -}; - -class SleepyTransform : public ISimpleTransform -{ -public: - explicit SleepyTransform(unsigned sleep_useconds_) - : ISimpleTransform( - Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }}), - Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }}), - /*skip_empty_chunks =*/ false) - , sleep_useconds(sleep_useconds_) {} - - String getName() const override { return "SleepyTransform"; } - -protected: - void transform(Chunk &) override - { - usleep(sleep_useconds); - } - -private: - unsigned sleep_useconds; -}; - -class PrintSink : public ISink -{ -public: - String getName() const override { return "Print"; } - - explicit PrintSink(String prefix_) - : ISink(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - prefix(std::move(prefix_)) - { - } - -private: - String prefix; - WriteBufferFromFileDescriptor out{STDOUT_FILENO}; - FormatSettings settings; - - void consume(Chunk chunk) override - { - size_t rows = chunk.getNumRows(); - size_t columns = chunk.getNumColumns(); - - for (size_t row_num = 0; row_num < rows; ++row_num) - { - writeString(prefix, out); - for (size_t column_num = 0; column_num < columns; ++column_num) - { - if (column_num != 0) - writeChar('\t', out); - getPort().getHeader().getByPosition(column_num).type->serializeAsText(*chunk.getColumns()[column_num], row_num, out, settings); - } - writeChar('\n', out); - } - - out.next(); - } -}; - -template -struct Measure -{ - template - static typename TimeT::rep execution(F&& func, Args&&... args) - { - auto start = std::chrono::steady_clock::now(); - std::forward(func)(std::forward(args)...); - auto duration = std::chrono::duration_cast< TimeT> - (std::chrono::steady_clock::now() - start); - return duration.count(); - } -}; - -int main(int, char **) -try -{ - auto execute_chain = [](size_t num_threads) - { - std::cerr << "---------------------\n"; - - auto source = std::make_shared(0, 100000); - auto transform1 = std::make_shared(100000); - auto transform2 = std::make_shared(100000); - auto transform3 = std::make_shared(100000); - auto limit = std::make_shared(source->getPort().getHeader(), 20, 0); - auto sink = std::make_shared(""); - - connect(source->getPort(), transform1->getInputPort()); - connect(transform1->getOutputPort(), transform2->getInputPort()); - connect(transform2->getOutputPort(), transform3->getInputPort()); - connect(transform3->getOutputPort(), limit->getInputPort()); - connect(limit->getOutputPort(), sink->getPort()); - - std::vector processors = {source, transform1, transform2, transform3, limit, sink}; -// WriteBufferFromOStream out(std::cout); -// printPipeline(processors, out); - - PipelineExecutor executor(processors); - executor.execute(num_threads); - }; - - auto time_single = Measure<>::execution(execute_chain, 1); - auto time_mt = Measure<>::execution(execute_chain, 4); - - std::cout << "Single Thread time: " << time_single << " ms.\n"; - std::cout << "Multiple Threads time: " << time_mt << " ms.\n"; - - return 0; -} -catch (...) -{ - std::cerr << getCurrentExceptionMessage(true) << '\n'; - throw; -} diff --git a/src/Processors/tests/processors_test_expand_pipeline.cpp b/src/Processors/tests/processors_test_expand_pipeline.cpp deleted file mode 100644 index 83ac2ed0168..00000000000 --- a/src/Processors/tests/processors_test_expand_pipeline.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include - - -#include -#include -#include - -#include - -#include -#include -#include - -using namespace DB; - -class PrintSink : public ISink -{ -public: - String getName() const override { return "Print"; } - - explicit PrintSink(String prefix_) - : ISink(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - prefix(std::move(prefix_)) - { - } - -private: - String prefix; - WriteBufferFromFileDescriptor out{STDOUT_FILENO}; - FormatSettings settings; - - void consume(Chunk chunk) override - { - size_t rows = chunk.getNumRows(); - size_t columns = chunk.getNumColumns(); - - for (size_t row_num = 0; row_num < rows; ++row_num) - { - writeString(prefix, out); - for (size_t column_num = 0; column_num < columns; ++column_num) - { - if (column_num != 0) - writeChar('\t', out); - getPort().getHeader().getByPosition(column_num).type->serializeAsText(*chunk.getColumns()[column_num], row_num, out, settings); - } - writeChar('\n', out); - } - - out.next(); - } -}; - - -class OneNumberSource : public ISource -{ -public: - String getName() const override { return "OneNumber"; } - - explicit OneNumberSource(UInt64 number_) - : ISource(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - number(number_) - { - } - -private: - UInt64 number; - bool done = false; - - Chunk generate() override - { - if (done) - return Chunk(); - - done = true; - - MutableColumns columns; - columns.emplace_back(ColumnUInt64::create(1, number)); - return Chunk(std::move(columns), 1); - } -}; - - -class ExpandingProcessor : public IProcessor -{ -public: - String getName() const override { return "Expanding"; } - ExpandingProcessor() - : IProcessor({Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})}, - {Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})}) - {} - - Status prepare() override - { - auto & main_input = inputs.front(); - auto & main_output = outputs.front(); - auto & additional_input = inputs.back(); - auto & additional_output = outputs.back(); - /// Check can output. - - - if (main_output.isFinished()) - { - main_input.close(); - additional_input.close(); - additional_output.finish(); - return Status::Finished; - } - - if (!main_output.canPush()) - { - main_input.setNotNeeded(); - additional_input.setNotNeeded(); - return Status::PortFull; - } - - if (chunk_from_add_inp && is_processed) - { - if (is_processed) - main_output.push(std::move(chunk_from_add_inp)); - else - return Status::Ready; - } - - if (expanded) - { - if (chunk_from_main_inp) - { - if (additional_output.isFinished()) - { - main_input.close(); - return Status::Finished; - } - - if (!additional_output.canPush()) - { - main_input.setNotNeeded(); - return Status::PortFull; - } - - additional_output.push(std::move(chunk_from_main_inp)); - main_input.close(); - } - - if (additional_input.isFinished()) - { - main_output.finish(); - return Status::Finished; - } - - additional_input.setNeeded(); - - if (!additional_input.hasData()) - return Status::NeedData; - - chunk_from_add_inp = additional_input.pull(); - is_processed = false; - return Status::Ready; - } - else - { - if (!chunk_from_main_inp) - { - - if (main_input.isFinished()) - { - main_output.finish(); - return Status::Finished; - } - - main_input.setNeeded(); - - if (!main_input.hasData()) - return Status::NeedData; - - chunk_from_main_inp = main_input.pull(); - main_input.close(); - } - - UInt64 val = chunk_from_main_inp.getColumns()[0]->getUInt(0); - if (val) - { - --val; - chunk_from_main_inp.setColumns(Columns{ColumnUInt64::create(1, val)}, 1); - return Status::ExpandPipeline; - } - - main_output.push(std::move(chunk_from_main_inp)); - main_output.finish(); - return Status::Finished; - } - } - - Processors expandPipeline() override - { - auto & main_input = inputs.front(); - auto & main_output = outputs.front(); - - Processors processors = {std::make_shared()}; - inputs.push_back({main_input.getHeader(), this}); - outputs.push_back({main_output.getHeader(), this}); - connect(outputs.back(), processors.back()->getInputs().front()); - connect(processors.back()->getOutputs().front(), inputs.back()); - inputs.back().setNeeded(); - - expanded = true; - return processors; - } - - void work() override - { - auto num_rows = chunk_from_add_inp.getNumRows(); - auto columns = chunk_from_add_inp.mutateColumns(); - columns.front()->insert(Field(num_rows)); - chunk_from_add_inp.setColumns(std::move(columns), num_rows + 1); - is_processed = true; - } - -private: - bool expanded = false; - Chunk chunk_from_main_inp; - Chunk chunk_from_add_inp; - bool is_processed = false; -}; - - -template -struct Measure -{ - template - static typename TimeT::rep execution(F&& func, Args&&... args) - { - auto start = std::chrono::steady_clock::now(); - std::forward(func)(std::forward(args)...); - auto duration = std::chrono::duration_cast< TimeT> - (std::chrono::steady_clock::now() - start); - return duration.count(); - } -}; - -int main(int, char **) -try -{ - auto execute = [](String msg, size_t num, size_t num_threads) - { - std::cerr << msg << "\n"; - - auto source = std::make_shared(num); - auto expanding = std::make_shared(); - auto sink = std::make_shared(""); - - connect(source->getPort(), expanding->getInputs().front()); - connect(expanding->getOutputs().front(), sink->getPort()); - - std::vector processors = {source, expanding, sink}; - - PipelineExecutor executor(processors); - executor.execute(num_threads); - - WriteBufferFromOStream out(std::cout); - printPipeline(executor.getProcessors(), out); - }; - - ThreadPool pool(4, 4, 10); - - auto time_single = Measure<>::execution(execute, "Single thread", 10, 1); - auto time_mt = Measure<>::execution(execute, "Multiple threads", 10, 4); - - std::cout << "Single Thread time: " << time_single << " ms.\n"; - std::cout << "Multiple Threads time:" << time_mt << " ms.\n"; - - return 0; -} -catch (...) -{ - std::cerr << getCurrentExceptionMessage(true) << '\n'; - throw; -} diff --git a/src/Processors/tests/processors_test_merge.cpp b/src/Processors/tests/processors_test_merge.cpp deleted file mode 100644 index 11b0bfd1365..00000000000 --- a/src/Processors/tests/processors_test_merge.cpp +++ /dev/null @@ -1,334 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include - - -using namespace DB; - - -class MergingSortedProcessor : public IProcessor -{ -public: - MergingSortedProcessor(const Block & header, size_t num_inputs) - : IProcessor(InputPorts(num_inputs, header), OutputPorts{header}) - , chunks(num_inputs), positions(num_inputs, 0), finished(num_inputs, false) - { - } - - String getName() const override { return "MergingSortedProcessor"; } - - Status prepare() override - { - auto & output = outputs.front(); - - /// Check can output. - - if (output.isFinished()) - { - for (auto & in : inputs) - in.close(); - - return Status::Finished; - } - - if (!output.isNeeded()) - { - for (auto & in : inputs) - in.setNotNeeded(); - - return Status::PortFull; - } - - if (output.hasData()) - return Status::PortFull; - - /// Push if has data. - if (res) - { - output.push(std::move(res)); - return Status::PortFull; - } - - /// Check for inputs we need. - bool all_inputs_finished = true; - bool all_inputs_has_data = true; - auto it = inputs.begin(); - for (size_t i = 0; it != inputs.end(); ++it, ++i) - { - auto & input = *it; - if (!finished[i]) - { - if (!input.isFinished()) - { - all_inputs_finished = false; - bool needed = positions[i] >= chunks[i].getNumRows(); - if (needed) - { - input.setNeeded(); - if (input.hasData()) - { - chunks[i] = input.pull(); - positions[i] = 0; - } - else - all_inputs_has_data = false; - } - else - input.setNotNeeded(); - } - else - finished[i] = true; - } - } - - if (all_inputs_finished) - { - output.finish(); - return Status::Finished; - } - - if (!all_inputs_has_data) - return Status::NeedData; - - return Status::Ready; - } - - void work() override - { - using Key = std::pair; - std::priority_queue, std::greater<>> queue; - for (size_t i = 0; i < chunks.size(); ++i) - { - if (finished[i]) - continue; - - if (positions[i] >= chunks[i].getNumRows()) - return; - - queue.push({chunks[i].getColumns()[0]->getUInt(positions[i]), i}); - } - - auto col = ColumnUInt64::create(); - - while (!queue.empty()) - { - size_t ps = queue.top().second; - queue.pop(); - - const auto & cur_col = chunks[ps].getColumns()[0]; - col->insertFrom(*cur_col, positions[ps]); - ++positions[ps]; - - if (positions[ps] == cur_col->size()) - break; - - queue.push({cur_col->getUInt(positions[ps]), ps}); - } - - UInt64 num_rows = col->size(); - res.setColumns(Columns({std::move(col)}), num_rows); - } - - OutputPort & getOutputPort() { return outputs.front(); } - -private: - Chunks chunks; - Chunk res; - std::vector positions; - std::vector finished; -}; - - -class NumbersSource : public ISource -{ -public: - String getName() const override { return "Numbers"; } - - NumbersSource(UInt64 start_number, UInt64 step_, unsigned sleep_useconds_) - : ISource(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - current_number(start_number), step(step_), sleep_useconds(sleep_useconds_) - { - } - -private: - UInt64 current_number = 0; - UInt64 step; - unsigned sleep_useconds; - - Chunk generate() override - { - usleep(sleep_useconds); - - MutableColumns columns; - columns.emplace_back(ColumnUInt64::create(1, current_number)); - current_number += step; - return Chunk(std::move(columns), 1); - } -}; - - -class SleepyTransform : public ISimpleTransform -{ -public: - explicit SleepyTransform(unsigned sleep_useconds_) - : ISimpleTransform( - Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }}), - Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }}), - false) - , sleep_useconds(sleep_useconds_) {} - - String getName() const override { return "SleepyTransform"; } - -protected: - void transform(Chunk &) override - { - usleep(sleep_useconds); - } - -private: - unsigned sleep_useconds; -}; - -class PrintSink : public ISink -{ -public: - String getName() const override { return "Print"; } - - explicit PrintSink(String prefix_) - : ISink(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - prefix(std::move(prefix_)) - { - } - -private: - String prefix; - WriteBufferFromFileDescriptor out{STDOUT_FILENO}; - FormatSettings settings; - - void consume(Chunk chunk) override - { - size_t rows = chunk.getNumRows(); - size_t columns = chunk.getNumColumns(); - - for (size_t row_num = 0; row_num < rows; ++row_num) - { - writeString(prefix, out); - for (size_t column_num = 0; column_num < columns; ++column_num) - { - if (column_num != 0) - writeChar('\t', out); - getPort().getHeader().getByPosition(column_num).type->serializeAsText(*chunk.getColumns()[column_num], row_num, out, settings); - } - writeChar('\n', out); - } - - out.next(); - } -}; - -template -struct Measure -{ - template - static typename TimeT::rep execution(F&& func, Args&&... args) - { - auto start = std::chrono::steady_clock::now(); - std::forward(func)(std::forward(args)...); - auto duration = std::chrono::duration_cast< TimeT> - (std::chrono::steady_clock::now() - start); - return duration.count(); - } -}; - -int main(int, char **) -try -{ - auto execute_chain = [](String msg, size_t start1, size_t start2, size_t start3, size_t num_threads) - { - std::cerr << msg << "\n"; - - auto source1 = std::make_shared(start1, 3, 100000); - auto source2 = std::make_shared(start2, 3, 100000); - auto source3 = std::make_shared(start3, 3, 100000); - - auto transform1 = std::make_shared(100000); - auto transform2 = std::make_shared(100000); - auto transform3 = std::make_shared(100000); - - auto limit1 = std::make_shared(source1->getPort().getHeader(), 20, 0); - auto limit2 = std::make_shared(source2->getPort().getHeader(), 20, 0); - auto limit3 = std::make_shared(source3->getPort().getHeader(), 20, 0); - - auto merge = std::make_shared(source1->getPort().getHeader(), 3); - auto limit_fin = std::make_shared(source1->getPort().getHeader(), 54, 0); - auto sink = std::make_shared(""); - - connect(source1->getPort(), transform1->getInputPort()); - connect(source2->getPort(), transform2->getInputPort()); - connect(source3->getPort(), transform3->getInputPort()); - - connect(transform1->getOutputPort(), limit1->getInputPort()); - connect(transform2->getOutputPort(), limit2->getInputPort()); - connect(transform3->getOutputPort(), limit3->getInputPort()); - - auto it = merge->getInputs().begin(); - connect(limit1->getOutputPort(), *(it++)); - connect(limit2->getOutputPort(), *(it++)); - connect(limit3->getOutputPort(), *(it++)); - - connect(merge->getOutputPort(), limit_fin->getInputPort()); - connect(limit_fin->getOutputPort(), sink->getPort()); - - std::vector processors = {source1, source2, source3, - transform1, transform2, transform3, - limit1, limit2, limit3, - merge, limit_fin, sink}; -// WriteBufferFromOStream out(std::cout); -// printPipeline(processors, out); - - PipelineExecutor executor(processors); - executor.execute(num_threads); - }; - - auto even_time_single = Measure<>::execution(execute_chain, "Even distribution single thread", 0, 1, 2, 1); - auto even_time_mt = Measure<>::execution(execute_chain, "Even distribution multiple threads", 0, 1, 2, 4); - - auto half_time_single = Measure<>::execution(execute_chain, "Half distribution single thread", 0, 31, 62, 1); - auto half_time_mt = Measure<>::execution(execute_chain, "Half distribution multiple threads", 0, 31, 62, 4); - - auto ordered_time_single = Measure<>::execution(execute_chain, "Ordered distribution single thread", 0, 61, 122, 1); - auto ordered_time_mt = Measure<>::execution(execute_chain, "Ordered distribution multiple threads", 0, 61, 122, 4); - - std::cout << "Single Thread [0:60:3] [1:60:3] [2:60:3] time: " << even_time_single << " ms.\n"; - std::cout << "Multiple Threads [0:60:3] [1:60:3] [2:60:3] time:" << even_time_mt << " ms.\n"; - - std::cout << "Single Thread [0:60:3] [31:90:3] [62:120:3] time: " << half_time_single << " ms.\n"; - std::cout << "Multiple Threads [0:60:3] [31:90:3] [62:120:3] time: " << half_time_mt << " ms.\n"; - - std::cout << "Single Thread [0:60:3] [61:120:3] [122:180:3] time: " << ordered_time_single << " ms.\n"; - std::cout << "Multiple Threads [0:60:3] [61:120:3] [122:180:3] time: " << ordered_time_mt << " ms.\n"; - - return 0; -} -catch (...) -{ - std::cerr << getCurrentExceptionMessage(true) << '\n'; - throw; -} diff --git a/src/Processors/tests/processors_test_merge_sorting_transform.cpp b/src/Processors/tests/processors_test_merge_sorting_transform.cpp deleted file mode 100644 index 5e6720f0167..00000000000 --- a/src/Processors/tests/processors_test_merge_sorting_transform.cpp +++ /dev/null @@ -1,250 +0,0 @@ -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include -#include - - -namespace DB -{ - -namespace ErrorCodes -{ - extern const int LOGICAL_ERROR; -} - -class NumbersSource : public ISource -{ -public: - String getName() const override { return "Numbers"; } - - NumbersSource(UInt64 count_, UInt64 block_size_, unsigned sleep_useconds_) - : ISource(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - count(count_), block_size(block_size_), sleep_useconds(sleep_useconds_) - { - } - -private: - UInt64 current_number = 0; - UInt64 count; - UInt64 block_size; - unsigned sleep_useconds; - - Chunk generate() override - { - if (current_number == count) - return {}; - - usleep(sleep_useconds); - - MutableColumns columns; - columns.emplace_back(ColumnUInt64::create()); - - UInt64 number = current_number++; - for (UInt64 i = 0; i < block_size; ++i, number += count) - columns.back()->insert(Field(number)); - - return Chunk(std::move(columns), block_size); - } -}; - -class CheckSortedSink : public ISink -{ -public: - String getName() const override { return "Print"; } - - CheckSortedSink() - : ISink(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})) - { - } - -private: - FormatSettings settings; - UInt64 current_number = 0; - - void consume(Chunk chunk) override - { - size_t rows = chunk.getNumRows(); - - UInt64 prev = current_number; - const auto & col = chunk.getColumns().at(0); - for (size_t row_num = 0; row_num < rows; ++row_num) - { - UInt64 val = col->getUInt(row_num); - if (val != current_number) - throw Exception("Invalid value. Expected " + toString(current_number) + ", got " + toString(val), - ErrorCodes::LOGICAL_ERROR); - - ++current_number; - } - - std::cout << "CheckSortedSink: " << prev << " - " << current_number << std::endl; - } -}; - -template -struct Measure -{ - template - static typename TimeT::rep execution(F&& func, Args&&... args) - { - auto start = std::chrono::steady_clock::now(); - std::forward(func)(std::forward(args)...); - auto duration = std::chrono::duration_cast< TimeT> - (std::chrono::steady_clock::now() - start); - return duration.count(); - } -}; - -} - - -using namespace DB; - -int main(int, char **) -try -{ - Poco::AutoPtr channel = new Poco::ConsoleChannel(std::cerr); - Poco::Logger::root().setChannel(channel); - Poco::Logger::root().setLevel("trace"); - - auto disk = std::make_shared("tmp", ".", 0); - auto tmp_volume = std::make_shared("tmp", std::vector{disk}, 0); - - auto execute_chain = [tmp_volume]( - String msg, - UInt64 source_block_size, - UInt64 blocks_count, - size_t max_merged_block_size, - UInt64 limit, - size_t max_bytes_before_remerge, - size_t max_bytes_before_external_sort, - size_t num_threads) - { - std::cerr << "------------------------\n"; - std::cerr << msg << "\n"; - - auto source = std::make_shared(blocks_count, source_block_size, 100); - SortDescription description = {{0, 1, 1}}; - auto transform = std::make_shared( - source->getPort().getHeader(), description, - max_merged_block_size, limit, - max_bytes_before_remerge, max_bytes_before_external_sort, - tmp_volume, 0); - auto sink = std::make_shared(); - - connect(source->getPort(), transform->getInputs().front()); - connect(transform->getOutputs().front(), sink->getPort()); - - std::vector processors = {source, transform, sink}; - PipelineExecutor executor(processors); - executor.execute(num_threads); - - WriteBufferFromOStream out(std::cout); - printPipeline(executor.getProcessors(), out); - }; - - std::map times; - - for (size_t num_threads : {1, 4}) - { - { - UInt64 source_block_size = 100; - UInt64 blocks_count = 10; - size_t max_merged_block_size = 100; - UInt64 limit = 0; - size_t max_bytes_before_remerge = 10000000; - size_t max_bytes_before_external_sort = 10000000; - std::string msg = num_threads > 1 ? "multiple threads" : "single thread"; - msg += ", " + toString(blocks_count) + " blocks per " + toString(source_block_size) + " numbers" + - ", no remerge and external sorts."; - - Int64 time = Measure<>::execution(execute_chain, msg, - source_block_size, - blocks_count, - max_merged_block_size, - limit, - max_bytes_before_remerge, - max_bytes_before_external_sort, - num_threads); - - times[msg] = time; - } - - { - UInt64 source_block_size = 1024; - UInt64 blocks_count = 10; - size_t max_merged_block_size = 1024; - UInt64 limit = 2048; - size_t max_bytes_before_remerge = sizeof(UInt64) * source_block_size * 4; - size_t max_bytes_before_external_sort = 10000000; - std::string msg = num_threads > 1 ? "multiple threads" : "single thread"; - msg += ", " + toString(blocks_count) + " blocks per " + toString(source_block_size) + " numbers" + - ", with remerge, no external sorts."; - - Int64 time = Measure<>::execution(execute_chain, msg, - source_block_size, - blocks_count, - max_merged_block_size, - limit, - max_bytes_before_remerge, - max_bytes_before_external_sort, - num_threads); - - times[msg] = time; - } - - { - UInt64 source_block_size = 1024; - UInt64 blocks_count = 10; - size_t max_merged_block_size = 1024; - UInt64 limit = 0; - size_t max_bytes_before_remerge = 0; - size_t max_bytes_before_external_sort = sizeof(UInt64) * source_block_size * 4; - std::string msg = num_threads > 1 ? "multiple threads" : "single thread"; - msg += ", " + toString(blocks_count) + " blocks per " + toString(source_block_size) + " numbers" + - ", no remerge, with external sorts."; - - Int64 time = Measure<>::execution(execute_chain, msg, - source_block_size, - blocks_count, - max_merged_block_size, - limit, - max_bytes_before_remerge, - max_bytes_before_external_sort, - num_threads); - - times[msg] = time; - } - } - - for (auto & item : times) - std::cout << item.first << ' ' << item.second << " ms.\n"; - - return 0; -} -catch (...) -{ - std::cerr << getCurrentExceptionMessage(true) << '\n'; - throw; -} diff --git a/src/Processors/tests/processors_test_merging_sorted_transform.cpp b/src/Processors/tests/processors_test_merging_sorted_transform.cpp deleted file mode 100644 index 477626d165d..00000000000 --- a/src/Processors/tests/processors_test_merging_sorted_transform.cpp +++ /dev/null @@ -1,207 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include - - -using namespace DB; - - -class NumbersSource : public ISource -{ -public: - String getName() const override { return "Numbers"; } - - NumbersSource(UInt64 start_number, UInt64 step_, UInt64 block_size_, unsigned sleep_useconds_) - : ISource(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - current_number(start_number), step(step_), block_size(block_size_), sleep_useconds(sleep_useconds_) - { - } - -private: - UInt64 current_number = 0; - UInt64 step; - UInt64 block_size; - unsigned sleep_useconds; - - Chunk generate() override - { - usleep(sleep_useconds); - - MutableColumns columns; - columns.emplace_back(ColumnUInt64::create()); - - for (UInt64 i = 0; i < block_size; ++i, current_number += step) - columns.back()->insert(Field(current_number)); - - return Chunk(std::move(columns), block_size); - } -}; - - -class SleepyTransform : public ISimpleTransform -{ -public: - explicit SleepyTransform(unsigned sleep_useconds_) - : ISimpleTransform( - Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }}), - Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }}), - false) - , sleep_useconds(sleep_useconds_) {} - - String getName() const override { return "SleepyTransform"; } - -protected: - void transform(Chunk &) override - { - usleep(sleep_useconds); - } - -private: - unsigned sleep_useconds; -}; - -class PrintSink : public ISink -{ -public: - String getName() const override { return "Print"; } - - explicit PrintSink(String prefix_) - : ISink(Block({ColumnWithTypeAndName{ ColumnUInt64::create(), std::make_shared(), "number" }})), - prefix(std::move(prefix_)) - { - } - -private: - String prefix; - WriteBufferFromFileDescriptor out{STDOUT_FILENO}; - FormatSettings settings; - - void consume(Chunk chunk) override - { - size_t rows = chunk.getNumRows(); - size_t columns = chunk.getNumColumns(); - - for (size_t row_num = 0; row_num < rows; ++row_num) - { - writeString(prefix, out); - for (size_t column_num = 0; column_num < columns; ++column_num) - { - if (column_num != 0) - writeChar('\t', out); - getPort().getHeader().getByPosition(column_num).type->serializeAsText(*chunk.getColumns()[column_num], row_num, out, settings); - } - writeChar('\n', out); - } - - out.next(); - } -}; - -template -struct Measure -{ - template - static typename TimeT::rep execution(F&& func, Args&&... args) - { - auto start = std::chrono::steady_clock::now(); - std::forward(func)(std::forward(args)...); - auto duration = std::chrono::duration_cast< TimeT> - (std::chrono::steady_clock::now() - start); - return duration.count(); - } -}; - -int main(int, char **) -try -{ - auto execute_chain = [](String msg, size_t start1, size_t start2, size_t start3, size_t num_threads) - { - std::cerr << msg << "\n"; - - auto source1 = std::make_shared(start1, 3, 2, 100000); - auto source2 = std::make_shared(start2, 3, 2, 100000); - auto source3 = std::make_shared(start3, 3, 2, 100000); - - auto transform1 = std::make_shared(100000); - auto transform2 = std::make_shared(100000); - auto transform3 = std::make_shared(100000); - - auto limit1 = std::make_shared(source1->getPort().getHeader(), 20, 0); - auto limit2 = std::make_shared(source2->getPort().getHeader(), 20, 0); - auto limit3 = std::make_shared(source3->getPort().getHeader(), 20, 0); - - SortDescription description = {{0, 1, 1}}; - auto merge = std::make_shared(source1->getPort().getHeader(), 3, description, 2); - auto limit_fin = std::make_shared(source1->getPort().getHeader(), 54, 0); - auto sink = std::make_shared(""); - - connect(source1->getPort(), transform1->getInputPort()); - connect(source2->getPort(), transform2->getInputPort()); - connect(source3->getPort(), transform3->getInputPort()); - - connect(transform1->getOutputPort(), limit1->getInputPort()); - connect(transform2->getOutputPort(), limit2->getInputPort()); - connect(transform3->getOutputPort(), limit3->getInputPort()); - - auto it = merge->getInputs().begin(); - connect(limit1->getOutputPort(), *(it++)); - connect(limit2->getOutputPort(), *(it++)); - connect(limit3->getOutputPort(), *(it++)); - - connect(merge->getOutputs().front(), limit_fin->getInputPort()); - connect(limit_fin->getOutputPort(), sink->getPort()); - - std::vector processors = {source1, source2, source3, - transform1, transform2, transform3, - limit1, limit2, limit3, - merge, limit_fin, sink}; -// WriteBufferFromOStream out(std::cout); -// printPipeline(processors, out); - - PipelineExecutor executor(processors); - executor.execute(num_threads); - }; - - auto even_time_single = Measure<>::execution(execute_chain, "Even distribution single thread", 0, 1, 2, 1); - auto even_time_mt = Measure<>::execution(execute_chain, "Even distribution multiple threads", 0, 1, 2, 4); - - auto half_time_single = Measure<>::execution(execute_chain, "Half distribution single thread", 0, 31, 62, 1); - auto half_time_mt = Measure<>::execution(execute_chain, "Half distribution multiple threads", 0, 31, 62, 4); - - auto ordered_time_single = Measure<>::execution(execute_chain, "Ordered distribution single thread", 0, 61, 122, 1); - auto ordered_time_mt = Measure<>::execution(execute_chain, "Ordered distribution multiple threads", 0, 61, 122, 4); - - std::cout << "Single Thread [0:60:3] [1:60:3] [2:60:3] time: " << even_time_single << " ms.\n"; - std::cout << "Multiple Threads [0:60:3] [1:60:3] [2:60:3] time:" << even_time_mt << " ms.\n"; - - std::cout << "Single Thread [0:60:3] [31:90:3] [62:120:3] time: " << half_time_single << " ms.\n"; - std::cout << "Multiple Threads [0:60:3] [31:90:3] [62:120:3] time: " << half_time_mt << " ms.\n"; - - std::cout << "Single Thread [0:60:3] [61:120:3] [122:180:3] time: " << ordered_time_single << " ms.\n"; - std::cout << "Multiple Threads [0:60:3] [61:120:3] [122:180:3] time: " << ordered_time_mt << " ms.\n"; - - return 0; -} -catch (...) -{ - std::cerr << getCurrentExceptionMessage(true) << '\n'; - throw; -} diff --git a/src/Processors/ya.make b/src/Processors/ya.make index 62320f1c147..ccc48047763 100644 --- a/src/Processors/ya.make +++ b/src/Processors/ya.make @@ -106,9 +106,11 @@ SRCS( Port.cpp QueryPipeline.cpp ResizeProcessor.cpp + Sources/DelayedSource.cpp Sources/SinkToOutputStream.cpp Sources/SourceFromInputStream.cpp Sources/SourceWithProgress.cpp + Sources/RemoteSource.cpp Transforms/AddingMissedTransform.cpp Transforms/AddingSelectorTransform.cpp Transforms/AggregatingTransform.cpp @@ -134,6 +136,7 @@ SRCS( Transforms/RollupTransform.cpp Transforms/SortingTransform.cpp Transforms/TotalsHavingTransform.cpp + Transforms/AggregatingInOrderTransform.cpp ) END() diff --git a/src/Server/HTTPHandler.cpp b/src/Server/HTTPHandler.cpp index 84d23f10a55..e866af2f49b 100644 --- a/src/Server/HTTPHandler.cpp +++ b/src/Server/HTTPHandler.cpp @@ -430,6 +430,10 @@ void HTTPHandler::processQuery( auto param_could_be_skipped = [&] (const String & name) { + /// Empty parameter appears when URL like ?&a=b or a=b&&c=d. Just skip them for user's convenience. + if (name.empty()) + return true; + if (reserved_param_names.count(name)) return true; diff --git a/src/Server/HTTPHandlerFactory.cpp b/src/Server/HTTPHandlerFactory.cpp index 2f00aa0aa72..3bcb29672e7 100644 --- a/src/Server/HTTPHandlerFactory.cpp +++ b/src/Server/HTTPHandlerFactory.cpp @@ -1,9 +1,6 @@ #include "HTTPHandlerFactory.h" -#include #include -#include -#include #include #include "HTTPHandler.h" diff --git a/src/Server/ReplicasStatusHandler.cpp b/src/Server/ReplicasStatusHandler.cpp index 55746409feb..57c97b0e4e0 100644 --- a/src/Server/ReplicasStatusHandler.cpp +++ b/src/Server/ReplicasStatusHandler.cpp @@ -47,6 +47,9 @@ void ReplicasStatusHandler::handleRequest(Poco::Net::HTTPServerRequest & request for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next()) { const auto & table = iterator->table(); + if (!table) + continue; + StorageReplicatedMergeTree * table_replicated = dynamic_cast(table.get()); if (!table_replicated) diff --git a/src/Server/TCPHandler.cpp b/src/Server/TCPHandler.cpp index 6e9275540e5..7e17604c4c7 100644 --- a/src/Server/TCPHandler.cpp +++ b/src/Server/TCPHandler.cpp @@ -304,17 +304,17 @@ void TCPHandler::runImpl() * We will try to send exception to the client in any case - see below. */ state.io.onException(); - exception.emplace(Exception::CreateFromPoco, e); + exception.emplace(Exception::CreateFromPocoTag{}, e); } catch (const Poco::Exception & e) { state.io.onException(); - exception.emplace(Exception::CreateFromPoco, e); + exception.emplace(Exception::CreateFromPocoTag{}, e); } catch (const std::exception & e) { state.io.onException(); - exception.emplace(Exception::CreateFromSTD, e); + exception.emplace(Exception::CreateFromSTDTag{}, e); } catch (...) { diff --git a/src/Storages/AlterCommands.cpp b/src/Storages/AlterCommands.cpp index 6ffaf0750d3..ce70af2bb6a 100644 --- a/src/Storages/AlterCommands.cpp +++ b/src/Storages/AlterCommands.cpp @@ -253,7 +253,7 @@ std::optional AlterCommand::parse(const ASTAlterCommand * command_ } -void AlterCommand::apply(StorageInMemoryMetadata & metadata) const +void AlterCommand::apply(StorageInMemoryMetadata & metadata, const Context & context) const { if (type == ADD_COLUMN) { @@ -332,11 +332,11 @@ void AlterCommand::apply(StorageInMemoryMetadata & metadata) const else if (type == ADD_INDEX) { if (std::any_of( - metadata.indices.indices.cbegin(), - metadata.indices.indices.cend(), - [this](const ASTPtr & index_ast) + metadata.secondary_indices.cbegin(), + metadata.secondary_indices.cend(), + [this](const auto & index) { - return index_ast->as().name == index_name; + return index.name == index_name; })) { if (if_not_exists) @@ -346,47 +346,47 @@ void AlterCommand::apply(StorageInMemoryMetadata & metadata) const ErrorCodes::ILLEGAL_COLUMN}; } - auto insert_it = metadata.indices.indices.end(); + auto insert_it = metadata.secondary_indices.end(); if (!after_index_name.empty()) { insert_it = std::find_if( - metadata.indices.indices.begin(), - metadata.indices.indices.end(), - [this](const ASTPtr & index_ast) + metadata.secondary_indices.begin(), + metadata.secondary_indices.end(), + [this](const auto & index) { - return index_ast->as().name == after_index_name; + return index.name == after_index_name; }); - if (insert_it == metadata.indices.indices.end()) + if (insert_it == metadata.secondary_indices.end()) throw Exception("Wrong index name. Cannot find index " + backQuote(after_index_name) + " to insert after.", ErrorCodes::BAD_ARGUMENTS); ++insert_it; } - metadata.indices.indices.emplace(insert_it, std::dynamic_pointer_cast(index_decl)); + metadata.secondary_indices.emplace(insert_it, IndexDescription::getIndexFromAST(index_decl, metadata.columns, context)); } else if (type == DROP_INDEX) { if (!partition && !clear) { auto erase_it = std::find_if( - metadata.indices.indices.begin(), - metadata.indices.indices.end(), - [this](const ASTPtr & index_ast) + metadata.secondary_indices.begin(), + metadata.secondary_indices.end(), + [this](const auto & index) { - return index_ast->as().name == index_name; + return index.name == index_name; }); - if (erase_it == metadata.indices.indices.end()) + if (erase_it == metadata.secondary_indices.end()) { if (if_exists) return; throw Exception("Wrong index name. Cannot find index " + backQuote(index_name) + " to drop.", ErrorCodes::BAD_ARGUMENTS); } - metadata.indices.indices.erase(erase_it); + metadata.secondary_indices.erase(erase_it); } } else if (type == ADD_CONSTRAINT) @@ -615,7 +615,7 @@ bool AlterCommand::isTTLAlter(const StorageInMemoryMetadata & metadata) const return ttl_changed; } -std::optional AlterCommand::tryConvertToMutationCommand(StorageInMemoryMetadata & metadata) const +std::optional AlterCommand::tryConvertToMutationCommand(StorageInMemoryMetadata & metadata, const Context & context) const { if (!isRequireMutationStage(metadata)) return {}; @@ -658,7 +658,7 @@ std::optional AlterCommand::tryConvertToMutationCommand(Storage } result.ast = ast->clone(); - apply(metadata); + apply(metadata, context); return result; } @@ -697,7 +697,7 @@ String alterTypeToString(const AlterCommand::Type type) __builtin_unreachable(); } -void AlterCommands::apply(StorageInMemoryMetadata & metadata) const +void AlterCommands::apply(StorageInMemoryMetadata & metadata, const Context & context) const { if (!prepared) throw DB::Exception("Alter commands is not prepared. Cannot apply. It's a bug", ErrorCodes::LOGICAL_ERROR); @@ -705,7 +705,7 @@ void AlterCommands::apply(StorageInMemoryMetadata & metadata) const auto metadata_copy = metadata; for (const AlterCommand & command : *this) if (!command.ignore) - command.apply(metadata_copy); + command.apply(metadata_copy, context); metadata = std::move(metadata_copy); } @@ -975,11 +975,11 @@ static MutationCommand createMaterializeTTLCommand() return command; } -MutationCommands AlterCommands::getMutationCommands(StorageInMemoryMetadata metadata, bool materialize_ttl) const +MutationCommands AlterCommands::getMutationCommands(StorageInMemoryMetadata metadata, bool materialize_ttl, const Context & context) const { MutationCommands result; for (const auto & alter_cmd : *this) - if (auto mutation_cmd = alter_cmd.tryConvertToMutationCommand(metadata); mutation_cmd) + if (auto mutation_cmd = alter_cmd.tryConvertToMutationCommand(metadata, context); mutation_cmd) result.push_back(*mutation_cmd); if (materialize_ttl) diff --git a/src/Storages/AlterCommands.h b/src/Storages/AlterCommands.h index 82090cb1aaf..82e438f6a45 100644 --- a/src/Storages/AlterCommands.h +++ b/src/Storages/AlterCommands.h @@ -102,7 +102,7 @@ struct AlterCommand static std::optional parse(const ASTAlterCommand * command, bool sanity_check_compression_codecs); - void apply(StorageInMemoryMetadata & metadata) const; + void apply(StorageInMemoryMetadata & metadata, const Context & context) const; /// Checks that alter query changes data. For MergeTree: /// * column files (data and marks) @@ -124,7 +124,7 @@ struct AlterCommand /// If possible, convert alter command to mutation command. In other case /// return empty optional. Some storages may execute mutations after /// metadata changes. - std::optional tryConvertToMutationCommand(StorageInMemoryMetadata & metadata) const; + std::optional tryConvertToMutationCommand(StorageInMemoryMetadata & metadata, const Context & context) const; }; /// Return string representation of AlterCommand::Type @@ -151,7 +151,7 @@ public: /// Apply all alter command in sequential order to storage metadata. /// Commands have to be prepared before apply. - void apply(StorageInMemoryMetadata & metadata) const; + void apply(StorageInMemoryMetadata & metadata, const Context & context) const; /// At least one command modify data on disk. bool isModifyingData(const StorageInMemoryMetadata & metadata) const; @@ -166,7 +166,7 @@ public: /// alter. If alter can be performed as pure metadata update, than result is /// empty. If some TTL changes happened than, depending on materialize_ttl /// additional mutation command (MATERIALIZE_TTL) will be returned. - MutationCommands getMutationCommands(StorageInMemoryMetadata metadata, bool materialize_ttl) const; + MutationCommands getMutationCommands(StorageInMemoryMetadata metadata, bool materialize_ttl, const Context & context) const; }; } diff --git a/src/Storages/Distributed/DirectoryMonitor.cpp b/src/Storages/Distributed/DirectoryMonitor.cpp index 0e84f68e5fe..a491cc411b1 100644 --- a/src/Storages/Distributed/DirectoryMonitor.cpp +++ b/src/Storages/Distributed/DirectoryMonitor.cpp @@ -8,8 +8,10 @@ #include #include #include +#include #include #include +#include #include #include #include @@ -108,11 +110,19 @@ StorageDistributedDirectoryMonitor::~StorageDistributedDirectoryMonitor() void StorageDistributedDirectoryMonitor::flushAllData() { - if (!quit) + if (quit) + return; + + CurrentMetrics::Increment metric_pending_files{CurrentMetrics::DistributedFilesToInsert, 0}; + std::unique_lock lock{mutex}; + + const auto & files = getFiles(metric_pending_files); + if (!files.empty()) { - CurrentMetrics::Increment metric_pending_files{CurrentMetrics::DistributedFilesToInsert, 0}; - std::unique_lock lock{mutex}; - processFiles(metric_pending_files); + processFiles(files, metric_pending_files); + + /// Update counters + getFiles(metric_pending_files); } } @@ -139,20 +149,31 @@ void StorageDistributedDirectoryMonitor::run() while (!quit) { do_sleep = true; + + const auto & files = getFiles(metric_pending_files); + if (files.empty()) + break; + if (!monitor_blocker.isCancelled()) { try { - do_sleep = !processFiles(metric_pending_files); + do_sleep = !processFiles(files, metric_pending_files); + + std::unique_lock metrics_lock(metrics_mutex); + last_exception = std::exception_ptr{}; } catch (...) { + std::unique_lock metrics_lock(metrics_mutex); + do_sleep = true; ++error_count; sleep_time = std::min( std::chrono::milliseconds{Int64(default_sleep_time.count() * std::exp2(error_count))}, max_sleep_time); tryLogCurrentException(getLoggerName().data()); + last_exception = std::current_exception(); } } else @@ -163,6 +184,8 @@ void StorageDistributedDirectoryMonitor::run() const auto now = std::chrono::system_clock::now(); if (now - last_decrease_time > decrease_error_count_period) { + std::unique_lock metrics_lock(metrics_mutex); + error_count /= 2; last_decrease_time = now; } @@ -171,6 +194,9 @@ void StorageDistributedDirectoryMonitor::run() break; } + /// Update counters + getFiles(metric_pending_files); + if (!quit && do_sleep) task_handle->scheduleAfter(sleep_time.count()); } @@ -226,9 +252,10 @@ ConnectionPoolPtr StorageDistributedDirectoryMonitor::createPool(const std::stri } -bool StorageDistributedDirectoryMonitor::processFiles(CurrentMetrics::Increment & metric_pending_files) +std::map StorageDistributedDirectoryMonitor::getFiles(CurrentMetrics::Increment & metric_pending_files) { std::map files; + size_t new_bytes_count = 0; Poco::DirectoryIterator end; for (Poco::DirectoryIterator it{path}; it != end; ++it) @@ -237,16 +264,26 @@ bool StorageDistributedDirectoryMonitor::processFiles(CurrentMetrics::Increment Poco::Path file_path{file_path_str}; if (!it->isDirectory() && startsWith(file_path.getExtension(), "bin")) + { files[parse(file_path.getBaseName())] = file_path_str; + new_bytes_count += Poco::File(file_path).getSize(); + } } /// Note: the value of this metric will be kept if this function will throw an exception. /// This is needed, because in case of exception, files still pending. metric_pending_files.changeTo(files.size()); - if (files.empty()) - return false; + { + std::unique_lock metrics_lock(metrics_mutex); + files_count = files.size(); + bytes_count = new_bytes_count; + } + return files; +} +bool StorageDistributedDirectoryMonitor::processFiles(const std::map & files, CurrentMetrics::Increment & metric_pending_files) +{ if (should_batch_inserts) { processFilesWithBatching(files, metric_pending_files); @@ -269,7 +306,6 @@ void StorageDistributedDirectoryMonitor::processFile(const std::string & file_pa { LOG_TRACE(log, "Started processing `{}`", file_path); auto timeouts = ConnectionTimeouts::getTCPTimeoutsWithFailover(storage.global_context->getSettingsRef()); - auto connection = pool->get(timeouts); try { @@ -280,8 +316,11 @@ void StorageDistributedDirectoryMonitor::processFile(const std::string & file_pa Settings insert_settings; std::string insert_query; ClientInfo client_info; + readHeader(in, insert_settings, insert_query, client_info, log); + auto connection = pool->get(timeouts, &insert_settings); + RemoteBlockOutputStream remote{*connection, timeouts, insert_query, insert_settings, client_info}; remote.writePrefix(); @@ -591,6 +630,20 @@ bool StorageDistributedDirectoryMonitor::scheduleAfter(size_t ms) return task_handle->scheduleAfter(ms, false); } +StorageDistributedDirectoryMonitor::Status StorageDistributedDirectoryMonitor::getStatus() const +{ + std::unique_lock metrics_lock(metrics_mutex); + + return Status{ + path, + last_exception, + error_count, + files_count, + bytes_count, + monitor_blocker.isCancelled(), + }; +} + void StorageDistributedDirectoryMonitor::processFilesWithBatching( const std::map & files, CurrentMetrics::Increment & metric_pending_files) @@ -732,7 +785,10 @@ void StorageDistributedDirectoryMonitor::updatePath(const std::string & new_path task_handle->deactivate(); - path = new_path; + { + std::unique_lock metrics_lock(metrics_mutex); + path = new_path; + } current_batch_file_path = path + "current_batch.txt"; task_handle->activateAndSchedule(); diff --git a/src/Storages/Distributed/DirectoryMonitor.h b/src/Storages/Distributed/DirectoryMonitor.h index 418cd430243..960d82f0716 100644 --- a/src/Storages/Distributed/DirectoryMonitor.h +++ b/src/Storages/Distributed/DirectoryMonitor.h @@ -1,7 +1,7 @@ #pragma once -#include #include +#include #include #include @@ -14,6 +14,10 @@ namespace CurrentMetrics { class Increment; } namespace DB { +class StorageDistributed; +class ActionBlocker; +class BackgroundSchedulePool; + /** Details of StorageDistributed. * This type is not designed for standalone use. */ @@ -37,9 +41,24 @@ public: /// For scheduling via DistributedBlockOutputStream bool scheduleAfter(size_t ms); + + /// system.distribution_queue interface + struct Status + { + std::string path; + std::exception_ptr last_exception; + size_t error_count; + size_t files_count; + size_t bytes_count; + bool is_blocked; + }; + Status getStatus() const; + private: void run(); - bool processFiles(CurrentMetrics::Increment & metric_pending_files); + + std::map getFiles(CurrentMetrics::Increment & metric_pending_files); + bool processFiles(const std::map & files, CurrentMetrics::Increment & metric_pending_files); void processFile(const std::string & file_path, CurrentMetrics::Increment & metric_pending_files); void processFilesWithBatching(const std::map & files, CurrentMetrics::Increment & metric_pending_files); @@ -61,7 +80,12 @@ private: struct BatchHeader; struct Batch; - size_t error_count{}; + mutable std::mutex metrics_mutex; + size_t error_count = 0; + size_t files_count = 0; + size_t bytes_count = 0; + std::exception_ptr last_exception; + const std::chrono::milliseconds default_sleep_time; std::chrono::milliseconds sleep_time; const std::chrono::milliseconds max_sleep_time; diff --git a/src/Storages/Distributed/DistributedBlockOutputStream.cpp b/src/Storages/Distributed/DistributedBlockOutputStream.cpp index 5516e85b143..4e28923ebfc 100644 --- a/src/Storages/Distributed/DistributedBlockOutputStream.cpp +++ b/src/Storages/Distributed/DistributedBlockOutputStream.cpp @@ -518,7 +518,7 @@ void DistributedBlockOutputStream::writeAsyncImpl(const Block & block, const siz } else { - if (shard_info.isLocal()) + if (shard_info.isLocal() && settings.prefer_localhost_replica) writeToLocal(block, shard_info.getLocalNodeCount()); std::vector dir_names; diff --git a/src/Storages/IStorage.cpp b/src/Storages/IStorage.cpp index 501b905817d..496aa55d071 100644 --- a/src/Storages/IStorage.cpp +++ b/src/Storages/IStorage.cpp @@ -37,9 +37,15 @@ const ColumnsDescription & IStorage::getColumns() const return columns; } -const IndicesDescription & IStorage::getIndices() const +const IndicesDescription & IStorage::getSecondaryIndices() const { - return indices; + return secondary_indices; +} + + +bool IStorage::hasSecondaryIndices() const +{ + return !secondary_indices.empty(); } const ConstraintsDescription & IStorage::getConstraints() const @@ -289,9 +295,9 @@ void IStorage::setColumns(ColumnsDescription columns_) columns = std::move(columns_); } -void IStorage::setIndices(IndicesDescription indices_) +void IStorage::setSecondaryIndices(IndicesDescription secondary_indices_) { - indices = std::move(indices_); + secondary_indices = std::move(secondary_indices_); } void IStorage::setConstraints(ConstraintsDescription constraints_) @@ -369,7 +375,7 @@ TableStructureWriteLockHolder IStorage::lockExclusively(const String & query_id, StorageInMemoryMetadata IStorage::getInMemoryMetadata() const { - return StorageInMemoryMetadata(getColumns(), getIndices(), getConstraints()); + return StorageInMemoryMetadata(getColumns(), getSecondaryIndices(), getConstraints()); } void IStorage::alter( @@ -380,7 +386,7 @@ void IStorage::alter( lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto table_id = getStorageID(); StorageInMemoryMetadata metadata = getInMemoryMetadata(); - params.apply(metadata); + params.apply(metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, metadata); setColumns(std::move(metadata.columns)); } @@ -588,4 +594,64 @@ bool IStorage::hasAnyMoveTTL() const return !table_ttl.move_ttl.empty(); } + +ColumnDependencies IStorage::getColumnDependencies(const NameSet & updated_columns) const +{ + if (updated_columns.empty()) + return {}; + + ColumnDependencies res; + + NameSet indices_columns; + NameSet required_ttl_columns; + NameSet updated_ttl_columns; + + auto add_dependent_columns = [&updated_columns](const auto & expression, auto & to_set) + { + auto requiered_columns = expression->getRequiredColumns(); + for (const auto & dependency : requiered_columns) + { + if (updated_columns.count(dependency)) + { + to_set.insert(requiered_columns.begin(), requiered_columns.end()); + return true; + } + } + + return false; + }; + + for (const auto & index : getSecondaryIndices()) + add_dependent_columns(index.expression, indices_columns); + + if (hasRowsTTL()) + { + if (add_dependent_columns(getRowsTTL().expression, required_ttl_columns)) + { + /// Filter all columns, if rows TTL expression have to be recalculated. + for (const auto & column : getColumns().getAllPhysical()) + updated_ttl_columns.insert(column.name); + } + } + + for (const auto & [name, entry] : getColumnTTLs()) + { + if (add_dependent_columns(entry.expression, required_ttl_columns)) + updated_ttl_columns.insert(name); + } + + for (const auto & entry : getMoveTTLs()) + add_dependent_columns(entry.expression, required_ttl_columns); + + for (const auto & column : indices_columns) + res.emplace(column, ColumnDependency::SKIP_INDEX); + for (const auto & column : required_ttl_columns) + res.emplace(column, ColumnDependency::TTL_EXPRESSION); + for (const auto & column : updated_ttl_columns) + res.emplace(column, ColumnDependency::TTL_TARGET); + + return res; + +} + } diff --git a/src/Storages/IStorage.h b/src/Storages/IStorage.h index 41d658aac98..a637c9c6881 100644 --- a/src/Storages/IStorage.h +++ b/src/Storages/IStorage.h @@ -141,9 +141,13 @@ public: virtual ColumnSizeByName getColumnSizes() const { return {}; } public: /// thread-unsafe part. lockStructure must be acquired - virtual const ColumnsDescription & getColumns() const; /// returns combined set of columns - virtual void setColumns(ColumnsDescription columns_); /// sets only real columns, possibly overwrites virtual ones. - const IndicesDescription & getIndices() const; + const ColumnsDescription & getColumns() const; /// returns combined set of columns + void setColumns(ColumnsDescription columns_); /// sets only real columns, possibly overwrites virtual ones. + + void setSecondaryIndices(IndicesDescription secondary_indices_); + const IndicesDescription & getSecondaryIndices() const; + /// Has at least one non primary index + bool hasSecondaryIndices() const; const ConstraintsDescription & getConstraints() const; void setConstraints(ConstraintsDescription constraints_); @@ -184,8 +188,7 @@ public: /// thread-unsafe part. lockStructure must be acquired /// By default return empty list of columns. virtual NamesAndTypesList getVirtuals() const; -protected: /// still thread-unsafe part. - void setIndices(IndicesDescription indices_); +protected: /// Returns whether the column is virtual - by default all columns are real. /// Initially reserved virtual column name may be shadowed by real column. @@ -197,7 +200,7 @@ private: mutable std::mutex id_mutex; ColumnsDescription columns; - IndicesDescription indices; + IndicesDescription secondary_indices; ConstraintsDescription constraints; StorageMetadataKeyField partition_key; @@ -506,10 +509,9 @@ public: /// Returns column names that need to be read for FINAL to work. Names getColumnsRequiredForFinal() const { return getColumnsRequiredForSortingKey(); } - /// Returns columns, which will be needed to calculate dependencies (skip /// indices, TTL expressions) if we update @updated_columns set of columns. - virtual ColumnDependencies getColumnDependencies(const NameSet & /* updated_columns */) const { return {}; } + ColumnDependencies getColumnDependencies(const NameSet & updated_columns) const; /// Returns storage policy if storage supports it. virtual StoragePolicyPtr getStoragePolicy() const { return {}; } diff --git a/src/Storages/IndicesDescription.cpp b/src/Storages/IndicesDescription.cpp index 2363e7924ba..f3afe8c03a2 100644 --- a/src/Storages/IndicesDescription.cpp +++ b/src/Storages/IndicesDescription.cpp @@ -1,56 +1,122 @@ +#include +#include #include #include #include #include #include +#include #include namespace DB { - -bool IndicesDescription::empty() const +namespace ErrorCodes { - return indices.empty(); + extern const int INCORRECT_QUERY; + extern const int LOGICAL_ERROR; +}; + + +IndexDescription IndexDescription::getIndexFromAST(const ASTPtr & definition_ast, const ColumnsDescription & columns, const Context & context) +{ + const auto * index_definition = definition_ast->as(); + if (!index_definition) + throw Exception("Cannot create skip index from non ASTIndexDeclaration AST", ErrorCodes::LOGICAL_ERROR); + + if (index_definition->name.empty()) + throw Exception("Skip index must have name in definition.", ErrorCodes::INCORRECT_QUERY); + + if (!index_definition->type) + throw Exception("TYPE is required for index", ErrorCodes::INCORRECT_QUERY); + + if (index_definition->type->parameters && !index_definition->type->parameters->children.empty()) + throw Exception("Index type cannot have parameters", ErrorCodes::INCORRECT_QUERY); + + IndexDescription result; + result.definition_ast = index_definition->clone(); + result.name = index_definition->name; + result.type = Poco::toLower(index_definition->type->name); + result.granularity = index_definition->granularity; + + ASTPtr expr_list = extractKeyExpressionList(index_definition->expr->clone()); + result.expression_list_ast = expr_list->clone(); + + auto syntax = SyntaxAnalyzer(context).analyze(expr_list, columns.getAllPhysical()); + result.expression = ExpressionAnalyzer(expr_list, syntax, context).getActions(true); + Block block_without_columns = result.expression->getSampleBlock(); + + for (size_t i = 0; i < block_without_columns.columns(); ++i) + { + const auto & column = block_without_columns.getByPosition(i); + result.column_names.emplace_back(column.name); + result.data_types.emplace_back(column.type); + result.sample_block.insert(ColumnWithTypeAndName(column.type->createColumn(), column.type, column.name)); + } + + const auto & definition_arguments = index_definition->type->arguments; + if (definition_arguments) + { + for (size_t i = 0; i < definition_arguments->children.size(); ++i) + { + const auto * argument = definition_arguments->children[i]->as(); + if (!argument) + throw Exception("Only literals can be skip index arguments", ErrorCodes::INCORRECT_QUERY); + result.arguments.emplace_back(argument->value); + } + } + + return result; } + bool IndicesDescription::has(const String & name) const { - return std::cend(indices) != std::find_if( - std::cbegin(indices), std::cend(indices), - [&name](const auto & index) - { - return index->name == name; - }); + for (const auto & index : *this) + if (index.name == name) + return true; + return false; } String IndicesDescription::toString() const { - if (indices.empty()) + if (empty()) return {}; ASTExpressionList list; - for (const auto & index : indices) - list.children.push_back(index); + for (const auto & index : *this) + list.children.push_back(index.definition_ast); return serializeAST(list, true); } -IndicesDescription IndicesDescription::parse(const String & str) -{ - if (str.empty()) - return {}; - IndicesDescription res; +IndicesDescription IndicesDescription::parse(const String & str, const ColumnsDescription & columns, const Context & context) +{ + IndicesDescription result; + if (str.empty()) + return result; + ParserIndexDeclarationList parser; ASTPtr list = parseQuery(parser, str, 0, DBMS_DEFAULT_MAX_PARSER_DEPTH); for (const auto & index : list->children) - res.indices.push_back(std::dynamic_pointer_cast(index)); + result.emplace_back(IndexDescription::getIndexFromAST(index, columns, context)); - return res; + return result; +} + +ExpressionActionsPtr IndicesDescription::getSingleExpressionForIndices(const ColumnsDescription & columns, const Context & context) const +{ + ASTPtr combined_expr_list = std::make_shared(); + for (const auto & index : *this) + for (const auto & index_expr : index.expression_list_ast->children) + combined_expr_list->children.push_back(index_expr->clone()); + + auto syntax_result = SyntaxAnalyzer(context).analyze(combined_expr_list, columns.getAllPhysical()); + return ExpressionAnalyzer(combined_expr_list, syntax_result, context).getActions(false); } } diff --git a/src/Storages/IndicesDescription.h b/src/Storages/IndicesDescription.h index 883caf6cdc2..16932dda6a2 100644 --- a/src/Storages/IndicesDescription.h +++ b/src/Storages/IndicesDescription.h @@ -4,24 +4,64 @@ #include #include +#include +#include +#include +#include namespace DB { -class ASTIndexDeclaration; -using IndicesASTs = std::vector>; - -struct IndicesDescription +/// Description of non-primary index for Storage +struct IndexDescription { - IndicesASTs indices; + /// Definition AST of index + ASTPtr definition_ast; - IndicesDescription() = default; + /// List of expressions for index calculation + ASTPtr expression_list_ast; - bool empty() const; + /// Index name + String name; + + /// Index type (minmax, set, bloom filter, etc.) + String type; + + /// Prepared expressions for index calculations + ExpressionActionsPtr expression; + + /// Index arguments, for example probability for bloom filter + FieldVector arguments; + + /// Names of index columns (not to be confused with required columns) + Names column_names; + + /// Data types of index columns + DataTypes data_types; + + /// Sample block with index columns. (NOTE: columns in block are empty, but + /// not nullptr) + Block sample_block; + + /// Index granularity, make sense for skip indices + size_t granularity; + + /// Parse index from definition AST + static IndexDescription getIndexFromAST(const ASTPtr & definition_ast, const ColumnsDescription & columns, const Context & context); +}; + +/// All secondary indices in storage +struct IndicesDescription : public std::vector +{ + /// Index with name exists bool has(const String & name) const; - + /// Convert description to string String toString() const; - static IndicesDescription parse(const String & str); + /// Parse description from string + static IndicesDescription parse(const String & str, const ColumnsDescription & columns, const Context & context); + + /// Return common expression for all stored indices + ExpressionActionsPtr getSingleExpressionForIndices(const ColumnsDescription & columns, const Context & context) const; }; } diff --git a/src/Storages/Kafka/KafkaBlockInputStream.cpp b/src/Storages/Kafka/KafkaBlockInputStream.cpp index 3e4533f8bb2..3edfcc7b9d2 100644 --- a/src/Storages/Kafka/KafkaBlockInputStream.cpp +++ b/src/Storages/Kafka/KafkaBlockInputStream.cpp @@ -13,7 +13,7 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } KafkaBlockInputStream::KafkaBlockInputStream( - StorageKafka & storage_, const Context & context_, const Names & columns, size_t max_block_size_, bool commit_in_suffix_) + StorageKafka & storage_, const std::shared_ptr & context_, const Names & columns, size_t max_block_size_, bool commit_in_suffix_) : storage(storage_) , context(context_) , column_names(columns) @@ -22,12 +22,6 @@ KafkaBlockInputStream::KafkaBlockInputStream( , non_virtual_header(storage.getSampleBlockNonMaterialized()) , virtual_header(storage.getSampleBlockForColumns({"_topic", "_key", "_offset", "_partition", "_timestamp","_timestamp_ms","_headers.name","_headers.value"})) { - context.setSetting("input_format_skip_unknown_fields", 1u); // Always skip unknown fields regardless of the context (JSON or TSKV) - context.setSetting("input_format_allow_errors_ratio", 0.); - context.setSetting("input_format_allow_errors_num", storage.skipBroken()); - - if (!storage.getSchemaName().empty()) - context.setSetting("format_schema", storage.getSchemaName()); } KafkaBlockInputStream::~KafkaBlockInputStream() @@ -48,7 +42,7 @@ Block KafkaBlockInputStream::getHeader() const void KafkaBlockInputStream::readPrefixImpl() { - auto timeout = std::chrono::milliseconds(context.getSettingsRef().kafka_max_wait_ms.totalMilliseconds()); + auto timeout = std::chrono::milliseconds(context->getSettingsRef().kafka_max_wait_ms.totalMilliseconds()); buffer = storage.popReadBuffer(timeout); if (!buffer) @@ -73,7 +67,7 @@ Block KafkaBlockInputStream::readImpl() MutableColumns virtual_columns = virtual_header.cloneEmptyColumns(); auto input_format = FormatFactory::instance().getInputFormat( - storage.getFormatName(), *buffer, non_virtual_header, context, max_block_size); + storage.getFormatName(), *buffer, non_virtual_header, *context, max_block_size); InputPort port(input_format->getPort().getHeader(), input_format.get()); connect(input_format->getPort(), port); diff --git a/src/Storages/Kafka/KafkaBlockInputStream.h b/src/Storages/Kafka/KafkaBlockInputStream.h index e3052122894..387f5088721 100644 --- a/src/Storages/Kafka/KafkaBlockInputStream.h +++ b/src/Storages/Kafka/KafkaBlockInputStream.h @@ -14,7 +14,7 @@ class KafkaBlockInputStream : public IBlockInputStream { public: KafkaBlockInputStream( - StorageKafka & storage_, const Context & context_, const Names & columns, size_t max_block_size_, bool commit_in_suffix = true); + StorageKafka & storage_, const std::shared_ptr & context_, const Names & columns, size_t max_block_size_, bool commit_in_suffix = true); ~KafkaBlockInputStream() override; String getName() const override { return storage.getName(); } @@ -29,7 +29,7 @@ public: private: StorageKafka & storage; - Context context; + const std::shared_ptr context; Names column_names; UInt64 max_block_size; diff --git a/src/Storages/Kafka/KafkaBlockOutputStream.cpp b/src/Storages/Kafka/KafkaBlockOutputStream.cpp index fe8aa207c93..17ef5aa104c 100644 --- a/src/Storages/Kafka/KafkaBlockOutputStream.cpp +++ b/src/Storages/Kafka/KafkaBlockOutputStream.cpp @@ -11,7 +11,7 @@ namespace ErrorCodes extern const int CANNOT_CREATE_IO_BUFFER; } -KafkaBlockOutputStream::KafkaBlockOutputStream(StorageKafka & storage_, const Context & context_) : storage(storage_), context(context_) +KafkaBlockOutputStream::KafkaBlockOutputStream(StorageKafka & storage_, const std::shared_ptr & context_) : storage(storage_), context(context_) { } @@ -26,7 +26,7 @@ void KafkaBlockOutputStream::writePrefix() if (!buffer) throw Exception("Failed to create Kafka producer!", ErrorCodes::CANNOT_CREATE_IO_BUFFER); - child = FormatFactory::instance().getOutput(storage.getFormatName(), *buffer, getHeader(), context, [this](const Columns & columns, size_t row){ buffer->countRow(columns, row); }); + child = FormatFactory::instance().getOutput(storage.getFormatName(), *buffer, getHeader(), *context, [this](const Columns & columns, size_t row){ buffer->countRow(columns, row); }); } void KafkaBlockOutputStream::write(const Block & block) diff --git a/src/Storages/Kafka/KafkaBlockOutputStream.h b/src/Storages/Kafka/KafkaBlockOutputStream.h index f3eb3dae0ba..7a973724f1b 100644 --- a/src/Storages/Kafka/KafkaBlockOutputStream.h +++ b/src/Storages/Kafka/KafkaBlockOutputStream.h @@ -10,7 +10,7 @@ namespace DB class KafkaBlockOutputStream : public IBlockOutputStream { public: - explicit KafkaBlockOutputStream(StorageKafka & storage_, const Context & context_); + explicit KafkaBlockOutputStream(StorageKafka & storage_, const std::shared_ptr & context_); Block getHeader() const override; @@ -22,7 +22,7 @@ public: private: StorageKafka & storage; - Context context; + const std::shared_ptr context; ProducerBufferPtr buffer; BlockOutputStreamPtr child; }; diff --git a/src/Storages/Kafka/KafkaSettings.h b/src/Storages/Kafka/KafkaSettings.h index 43984f81e05..e65522b3606 100644 --- a/src/Storages/Kafka/KafkaSettings.h +++ b/src/Storages/Kafka/KafkaSettings.h @@ -1,7 +1,7 @@ #pragma once #include - +#include namespace DB { @@ -15,18 +15,34 @@ struct KafkaSettings : public SettingsCollection { -#define LIST_OF_KAFKA_SETTINGS(M) \ +#define KAFKA_RELATED_SETTINGS(M) \ M(SettingString, kafka_broker_list, "", "A comma-separated list of brokers for Kafka engine.", 0) \ M(SettingString, kafka_topic_list, "", "A list of Kafka topics.", 0) \ - M(SettingString, kafka_group_name, "", "A group of Kafka consumers.", 0) \ - M(SettingString, kafka_client_id, "", "A client id of Kafka consumer.", 0) \ + M(SettingString, kafka_group_name, "", "Client group id string. All Kafka consumers sharing the same group.id belong to the same group.", 0) \ + M(SettingString, kafka_client_id, "", "Client identifier.", 0) \ + M(SettingUInt64, kafka_num_consumers, 1, "The number of consumers per table for Kafka engine.", 0) \ + M(SettingBool, kafka_commit_every_batch, false, "Commit every consumed and handled batch instead of a single commit after writing a whole block", 0) \ + /* default is stream_poll_timeout_ms */ \ + M(SettingMilliseconds, kafka_poll_timeout_ms, 0, "Timeout for single poll from Kafka.", 0) \ + /* default is min(max_block_size, kafka_max_block_size)*/ \ + M(SettingUInt64, kafka_poll_max_batch_size, 0, "Maximum amount of messages to be polled in a single Kafka poll.", 0) \ + /* default is = min_insert_block_size / kafka_num_consumers */ \ + M(SettingUInt64, kafka_max_block_size, 0, "Number of row collected by poll(s) for flushing data from Kafka.", 0) \ + /* default is stream_flush_interval_ms */ \ + M(SettingMilliseconds, kafka_flush_interval_ms, 0, "Timeout for flushing data from Kafka.", 0) \ + /* those are mapped to format factory settings */ \ M(SettingString, kafka_format, "", "The message format for Kafka engine.", 0) \ M(SettingChar, kafka_row_delimiter, '\0', "The character to be considered as a delimiter in Kafka message.", 0) \ M(SettingString, kafka_schema, "", "Schema identifier (used by schema-based formats) for Kafka engine", 0) \ - M(SettingUInt64, kafka_num_consumers, 1, "The number of consumers per table for Kafka engine.", 0) \ - M(SettingUInt64, kafka_max_block_size, 0, "The maximum batch size for poll.", 0) \ - M(SettingUInt64, kafka_skip_broken_messages, 0, "Skip at least this number of broken messages from Kafka topic per block", 0) \ - M(SettingUInt64, kafka_commit_every_batch, 0, "Commit every consumed and handled batch instead of a single commit after writing a whole block", 0) + M(SettingUInt64, kafka_skip_broken_messages, 0, "Skip at least this number of broken messages from Kafka topic per block", 0) + + /** TODO: */ + /* https://github.com/edenhill/librdkafka/blob/master/CONFIGURATION.md */ + /* https://github.com/edenhill/librdkafka/blob/v1.4.2/src/rdkafka_conf.c */ + +#define LIST_OF_KAFKA_SETTINGS(M) \ + KAFKA_RELATED_SETTINGS(M) \ + FORMAT_FACTORY_SETTINGS(M) DECLARE_SETTINGS_COLLECTION(LIST_OF_KAFKA_SETTINGS) diff --git a/src/Storages/Kafka/StorageKafka.cpp b/src/Storages/Kafka/StorageKafka.cpp index fc83fd84884..bb721417c5b 100644 --- a/src/Storages/Kafka/StorageKafka.cpp +++ b/src/Storages/Kafka/StorageKafka.cpp @@ -119,39 +119,74 @@ StorageKafka::StorageKafka( const StorageID & table_id_, Context & context_, const ColumnsDescription & columns_, - const String & brokers_, - const String & group_, - const String & client_id_, - const Names & topics_, - const String & format_name_, - char row_delimiter_, - const String & schema_name_, - size_t num_consumers_, - UInt64 max_block_size_, - size_t skip_broken_, - bool intermediate_commit_) + std::unique_ptr kafka_settings_) : IStorage(table_id_) , global_context(context_.getGlobalContext()) - , kafka_context(Context(global_context)) - , topics(global_context.getMacros()->expand(topics_)) - , brokers(global_context.getMacros()->expand(brokers_)) - , group(global_context.getMacros()->expand(group_)) - , client_id(client_id_.empty() ? getDefaultClientId(table_id_) : global_context.getMacros()->expand(client_id_)) - , format_name(global_context.getMacros()->expand(format_name_)) - , row_delimiter(row_delimiter_) - , schema_name(global_context.getMacros()->expand(schema_name_)) - , num_consumers(num_consumers_) - , max_block_size(max_block_size_) + , kafka_context(std::make_shared(global_context)) + , kafka_settings(std::move(kafka_settings_)) + , topics(parseTopics(global_context.getMacros()->expand(kafka_settings->kafka_topic_list.value))) + , brokers(global_context.getMacros()->expand(kafka_settings->kafka_broker_list.value)) + , group(global_context.getMacros()->expand(kafka_settings->kafka_group_name.value)) + , client_id(kafka_settings->kafka_client_id.value.empty() ? getDefaultClientId(table_id_) : global_context.getMacros()->expand(kafka_settings->kafka_client_id.value)) + , format_name(global_context.getMacros()->expand(kafka_settings->kafka_format.value)) + , row_delimiter(kafka_settings->kafka_row_delimiter.value) + , schema_name(global_context.getMacros()->expand(kafka_settings->kafka_schema.value)) + , num_consumers(kafka_settings->kafka_num_consumers.value) , log(&Poco::Logger::get("StorageKafka (" + table_id_.table_name + ")")) - , semaphore(0, num_consumers_) - , skip_broken(skip_broken_) - , intermediate_commit(intermediate_commit_) + , semaphore(0, num_consumers) + , intermediate_commit(kafka_settings->kafka_commit_every_batch.value) + , settings_adjustments(createSettingsAdjustments()) { - kafka_context.makeQueryContext(); - setColumns(columns_); task = global_context.getSchedulePool().createTask(log->name(), [this]{ threadFunc(); }); task->deactivate(); + + kafka_context->makeQueryContext(); + kafka_context->applySettingsChanges(settings_adjustments); +} + +SettingsChanges StorageKafka::createSettingsAdjustments() +{ + SettingsChanges result; + // Needed for backward compatibility + if (!kafka_settings->input_format_skip_unknown_fields.changed) + { + // Always skip unknown fields regardless of the context (JSON or TSKV) + kafka_settings->input_format_skip_unknown_fields = true; + } + + if (!kafka_settings->input_format_allow_errors_ratio.changed) + { + kafka_settings->input_format_allow_errors_ratio = 0.; + } + + if (!kafka_settings->input_format_allow_errors_num.changed) + { + kafka_settings->input_format_allow_errors_num = kafka_settings->kafka_skip_broken_messages.value; + } + + if (!schema_name.empty()) + result.emplace_back("format_schema", schema_name); + + for (auto & it : *kafka_settings) + { + if (it.isChanged() && it.getName().toString().rfind("kafka_",0) == std::string::npos) + { + result.emplace_back(it.getName().toString(), it.getValueAsString()); + } + } + return result; +} + +Names StorageKafka::parseTopics(String topic_list) +{ + Names result; + boost::split(result,topic_list,[](char c){ return c == ','; }); + for (String & topic : result) + { + boost::trim(topic); + } + return result; } String StorageKafka::getDefaultClientId(const StorageID & table_id_) @@ -176,6 +211,8 @@ Pipes StorageKafka::read( /// Always use all consumers at once, otherwise SELECT may not read messages from all partitions. Pipes pipes; pipes.reserve(num_created_consumers); + auto modified_context = std::make_shared(context); + modified_context->applySettingsChanges(settings_adjustments); // Claim as many consumers as requested, but don't block for (size_t i = 0; i < num_created_consumers; ++i) @@ -184,7 +221,7 @@ Pipes StorageKafka::read( /// TODO: probably that leads to awful performance. /// FIXME: seems that doesn't help with extra reading and committing unprocessed messages. /// TODO: rewrite KafkaBlockInputStream to KafkaSource. Now it is used in other place. - pipes.emplace_back(std::make_shared(std::make_shared(*this, context, column_names, 1))); + pipes.emplace_back(std::make_shared(std::make_shared(*this, modified_context, column_names, 1))); } LOG_DEBUG(log, "Starting reading {} streams", pipes.size()); @@ -194,9 +231,12 @@ Pipes StorageKafka::read( BlockOutputStreamPtr StorageKafka::write(const ASTPtr &, const Context & context) { + auto modified_context = std::make_shared(context); + modified_context->applySettingsChanges(settings_adjustments); + if (topics.size() > 1) throw Exception("Can't write to Kafka table with multiple topics!", ErrorCodes::NOT_IMPLEMENTED); - return std::make_shared(*this, context); + return std::make_shared(*this, modified_context); } @@ -268,13 +308,14 @@ ConsumerBufferPtr StorageKafka::popReadBuffer(std::chrono::milliseconds timeout) return buffer; } - ProducerBufferPtr StorageKafka::createWriteBuffer(const Block & header) { cppkafka::Configuration conf; conf.set("metadata.broker.list", brokers); conf.set("group.id", group); conf.set("client.id", client_id); + conf.set("client.software.name", VERSION_NAME); + conf.set("client.software.version", VERSION_DESCRIBE); // TODO: fill required settings updateConfiguration(conf); @@ -303,9 +344,16 @@ ConsumerBufferPtr StorageKafka::createReadBuffer(const size_t consumer_number) { conf.set("client.id", client_id); } - + conf.set("client.software.name", VERSION_NAME); + conf.set("client.software.version", VERSION_DESCRIBE); conf.set("auto.offset.reset", "smallest"); // If no offset stored for this group, read all messages from the start + // that allows to prevent fast draining of the librdkafka queue + // during building of single insert block. Improves performance + // significantly, but may lead to bigger memory consumption. + size_t default_queued_min_messages = 100000; // we don't want to decrease the default + conf.set("queued.min.messages", std::max(getMaxBlockSize(),default_queued_min_messages)); + updateConfiguration(conf); // those settings should not be changed by users. @@ -317,17 +365,32 @@ ConsumerBufferPtr StorageKafka::createReadBuffer(const size_t consumer_number) auto consumer = std::make_shared(conf); consumer->set_destroy_flags(RD_KAFKA_DESTROY_F_NO_CONSUMER_CLOSE); - // Limit the number of batched messages to allow early cancellations - const Settings & settings = global_context.getSettingsRef(); - size_t batch_size = max_block_size; - if (!batch_size) - batch_size = settings.max_block_size.value; - size_t poll_timeout = settings.stream_poll_timeout_ms.totalMilliseconds(); - /// NOTE: we pass |stream_cancelled| by reference here, so the buffers should not outlive the storage. - return std::make_shared(consumer, log, batch_size, poll_timeout, intermediate_commit, stream_cancelled, getTopics()); + return std::make_shared(consumer, log, getPollMaxBatchSize(), getPollTimeoutMillisecond(), intermediate_commit, stream_cancelled, topics); } +size_t StorageKafka::getMaxBlockSize() const +{ + return kafka_settings->kafka_max_block_size.changed + ? kafka_settings->kafka_max_block_size.value + : (global_context.getSettingsRef().max_insert_block_size.value / num_consumers); +} + +size_t StorageKafka::getPollMaxBatchSize() const +{ + size_t batch_size = kafka_settings->kafka_poll_max_batch_size.changed + ? kafka_settings->kafka_poll_max_batch_size.value + : global_context.getSettingsRef().max_block_size.value; + + return std::min(batch_size,getMaxBlockSize()); +} + +size_t StorageKafka::getPollTimeoutMillisecond() const +{ + return kafka_settings->kafka_poll_timeout_ms.changed + ? kafka_settings->kafka_poll_timeout_ms.totalMilliseconds() + : global_context.getSettingsRef().stream_poll_timeout_ms.totalMilliseconds(); +} void StorageKafka::updateConfiguration(cppkafka::Configuration & conf) { @@ -458,19 +521,17 @@ bool StorageKafka::streamToViews() auto insert = std::make_shared(); insert->table_id = table_id; - const Settings & settings = global_context.getSettingsRef(); - size_t block_size = max_block_size; - if (block_size == 0) - block_size = settings.max_block_size; + size_t block_size = getMaxBlockSize(); // Create a stream for each consumer and join them in a union stream // Only insert into dependent views and expect that input blocks contain virtual columns - InterpreterInsertQuery interpreter(insert, kafka_context, false, true, true); + InterpreterInsertQuery interpreter(insert, *kafka_context, false, true, true); auto block_io = interpreter.execute(); // Create a stream for each consumer and join them in a union stream BlockInputStreams streams; streams.reserve(num_created_consumers); + for (size_t i = 0; i < num_created_consumers; ++i) { auto stream @@ -479,7 +540,11 @@ bool StorageKafka::streamToViews() // Limit read batch to maximum block size to allow DDL IBlockInputStream::LocalLimits limits; - limits.speed_limits.max_execution_time = settings.stream_flush_interval_ms; + + limits.speed_limits.max_execution_time = kafka_settings->kafka_flush_interval_ms.changed + ? kafka_settings->kafka_flush_interval_ms + : global_context.getSettingsRef().stream_flush_interval_ms; + limits.timeout_overflow_mode = OverflowMode::BREAK; stream->setLimits(limits); } @@ -514,17 +579,61 @@ void registerStorageKafka(StorageFactory & factory) size_t args_count = engine_args.size(); bool has_settings = args.storage_def->settings; - KafkaSettings kafka_settings; + auto kafka_settings = std::make_unique(); if (has_settings) { - kafka_settings.loadFromQuery(*args.storage_def); + kafka_settings->loadFromQuery(*args.storage_def); } + // Check arguments and settings + #define CHECK_KAFKA_STORAGE_ARGUMENT(ARG_NUM, PAR_NAME, EVAL) \ + /* One of the four required arguments is not specified */ \ + if (args_count < (ARG_NUM) && (ARG_NUM) <= 4 && \ + !kafka_settings->PAR_NAME.changed) \ + { \ + throw Exception( \ + "Required parameter '" #PAR_NAME "' " \ + "for storage Kafka not specified", \ + ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); \ + } \ + if (args_count >= (ARG_NUM)) \ + { \ + /* The same argument is given in two places */ \ + if (has_settings && \ + kafka_settings->PAR_NAME.changed) \ + { \ + throw Exception( \ + "The argument №" #ARG_NUM " of storage Kafka " \ + "and the parameter '" #PAR_NAME "' " \ + "in SETTINGS cannot be specified at the same time", \ + ErrorCodes::BAD_ARGUMENTS); \ + } \ + /* move engine args to settings */ \ + else \ + { \ + if ((EVAL) == 1) \ + { \ + engine_args[(ARG_NUM)-1] = \ + evaluateConstantExpressionAsLiteral( \ + engine_args[(ARG_NUM)-1], \ + args.local_context); \ + } \ + if ((EVAL) == 2) \ + { \ + engine_args[(ARG_NUM)-1] = \ + evaluateConstantExpressionOrIdentifierAsLiteral( \ + engine_args[(ARG_NUM)-1], \ + args.local_context); \ + } \ + kafka_settings->PAR_NAME.set( \ + engine_args[(ARG_NUM)-1]->as().value);\ + } \ + } + /** Arguments of engine is following: * - Kafka broker list * - List of topics * - Group ID (may be a constaint expression with a string result) - * - Client ID * - Message format (string) * - Row delimiter * - Schema (optional, if the format supports it) @@ -534,209 +643,32 @@ void registerStorageKafka(StorageFactory & factory) * - Do intermediate commits when the batch consumed and handled */ - // Check arguments and settings - #define CHECK_KAFKA_STORAGE_ARGUMENT(ARG_NUM, PAR_NAME) \ - /* One of the four required arguments is not specified */ \ - if (args_count < (ARG_NUM) && (ARG_NUM) <= 4 && \ - !kafka_settings.PAR_NAME.changed) \ - { \ - throw Exception( \ - "Required parameter '" #PAR_NAME "' " \ - "for storage Kafka not specified", \ - ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); \ - } \ - /* The same argument is given in two places */ \ - if (has_settings && \ - kafka_settings.PAR_NAME.changed && \ - args_count >= (ARG_NUM)) \ - { \ - throw Exception( \ - "The argument №" #ARG_NUM " of storage Kafka " \ - "and the parameter '" #PAR_NAME "' " \ - "in SETTINGS cannot be specified at the same time", \ - ErrorCodes::BAD_ARGUMENTS); \ - } - - CHECK_KAFKA_STORAGE_ARGUMENT(1, kafka_broker_list) - CHECK_KAFKA_STORAGE_ARGUMENT(2, kafka_topic_list) - CHECK_KAFKA_STORAGE_ARGUMENT(3, kafka_group_name) - CHECK_KAFKA_STORAGE_ARGUMENT(4, kafka_format) - CHECK_KAFKA_STORAGE_ARGUMENT(5, kafka_row_delimiter) - CHECK_KAFKA_STORAGE_ARGUMENT(6, kafka_schema) - CHECK_KAFKA_STORAGE_ARGUMENT(7, kafka_num_consumers) - CHECK_KAFKA_STORAGE_ARGUMENT(8, kafka_max_block_size) - CHECK_KAFKA_STORAGE_ARGUMENT(9, kafka_skip_broken_messages) - CHECK_KAFKA_STORAGE_ARGUMENT(10, kafka_commit_every_batch) + /* 0 = raw, 1 = evaluateConstantExpressionAsLiteral, 2=evaluateConstantExpressionOrIdentifierAsLiteral */ + CHECK_KAFKA_STORAGE_ARGUMENT(1, kafka_broker_list, 0) + CHECK_KAFKA_STORAGE_ARGUMENT(2, kafka_topic_list, 1) + CHECK_KAFKA_STORAGE_ARGUMENT(3, kafka_group_name, 2) + CHECK_KAFKA_STORAGE_ARGUMENT(4, kafka_format, 2) + CHECK_KAFKA_STORAGE_ARGUMENT(5, kafka_row_delimiter, 2) + CHECK_KAFKA_STORAGE_ARGUMENT(6, kafka_schema, 2) + CHECK_KAFKA_STORAGE_ARGUMENT(7, kafka_num_consumers, 0) + CHECK_KAFKA_STORAGE_ARGUMENT(8, kafka_max_block_size, 0) + CHECK_KAFKA_STORAGE_ARGUMENT(9, kafka_skip_broken_messages, 0) + CHECK_KAFKA_STORAGE_ARGUMENT(10, kafka_commit_every_batch, 0) #undef CHECK_KAFKA_STORAGE_ARGUMENT - // Get and check broker list - String brokers = kafka_settings.kafka_broker_list; - if (args_count >= 1) + auto num_consumers = kafka_settings->kafka_num_consumers.value; + + if (num_consumers > 16) { - const auto * ast = engine_args[0]->as(); - if (ast && ast->value.getType() == Field::Types::String) - { - brokers = safeGet(ast->value); - } - else - { - throw Exception(String("Kafka broker list must be a string"), ErrorCodes::BAD_ARGUMENTS); - } + throw Exception("Number of consumers can not be bigger than 16", ErrorCodes::BAD_ARGUMENTS); + } + else if (num_consumers < 1) + { + throw Exception("Number of consumers can not be lower than 1", ErrorCodes::BAD_ARGUMENTS); } - // Get and check topic list - String topic_list = kafka_settings.kafka_topic_list.value; - if (args_count >= 2) - { - engine_args[1] = evaluateConstantExpressionAsLiteral(engine_args[1], args.local_context); - topic_list = engine_args[1]->as().value.safeGet(); - } - - Names topics; - boost::split(topics, topic_list , [](char c){ return c == ','; }); - for (String & topic : topics) - { - boost::trim(topic); - } - - // Get and check group name - String group = kafka_settings.kafka_group_name.value; - if (args_count >= 3) - { - engine_args[2] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[2], args.local_context); - group = engine_args[2]->as().value.safeGet(); - } - - // Get and check message format name - String format = kafka_settings.kafka_format.value; - if (args_count >= 4) - { - engine_args[3] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[3], args.local_context); - - const auto * ast = engine_args[3]->as(); - if (ast && ast->value.getType() == Field::Types::String) - { - format = safeGet(ast->value); - } - else - { - throw Exception("Format must be a string", ErrorCodes::BAD_ARGUMENTS); - } - } - - // Parse row delimiter (optional) - char row_delimiter = kafka_settings.kafka_row_delimiter; - if (args_count >= 5) - { - engine_args[4] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[4], args.local_context); - - const auto * ast = engine_args[4]->as(); - String arg; - if (ast && ast->value.getType() == Field::Types::String) - { - arg = safeGet(ast->value); - } - else - { - throw Exception("Row delimiter must be a char", ErrorCodes::BAD_ARGUMENTS); - } - if (arg.size() > 1) - { - throw Exception("Row delimiter must be a char", ErrorCodes::BAD_ARGUMENTS); - } - else if (arg.empty()) - { - row_delimiter = '\0'; - } - else - { - row_delimiter = arg[0]; - } - } - - // Parse format schema if supported (optional) - String schema = kafka_settings.kafka_schema.value; - if (args_count >= 6) - { - engine_args[5] = evaluateConstantExpressionOrIdentifierAsLiteral(engine_args[5], args.local_context); - - const auto * ast = engine_args[5]->as(); - if (ast && ast->value.getType() == Field::Types::String) - { - schema = safeGet(ast->value); - } - else - { - throw Exception("Format schema must be a string", ErrorCodes::BAD_ARGUMENTS); - } - } - - // Parse number of consumers (optional) - UInt64 num_consumers = kafka_settings.kafka_num_consumers; - if (args_count >= 7) - { - const auto * ast = engine_args[6]->as(); - if (ast && ast->value.getType() == Field::Types::UInt64) - { - num_consumers = safeGet(ast->value); - } - else - { - throw Exception("Number of consumers must be a positive integer", ErrorCodes::BAD_ARGUMENTS); - } - } - - // Parse max block size (optional) - UInt64 max_block_size = static_cast(kafka_settings.kafka_max_block_size); - if (args_count >= 8) - { - const auto * ast = engine_args[7]->as(); - if (ast && ast->value.getType() == Field::Types::UInt64) - { - max_block_size = static_cast(safeGet(ast->value)); - } - else - { - // TODO: no check if the integer is really positive - throw Exception("Maximum block size must be a positive integer", ErrorCodes::BAD_ARGUMENTS); - } - } - - size_t skip_broken = static_cast(kafka_settings.kafka_skip_broken_messages); - if (args_count >= 9) - { - const auto * ast = engine_args[8]->as(); - if (ast && ast->value.getType() == Field::Types::UInt64) - { - skip_broken = static_cast(safeGet(ast->value)); - } - else - { - throw Exception("Number of broken messages to skip must be a non-negative integer", ErrorCodes::BAD_ARGUMENTS); - } - } - - bool intermediate_commit = static_cast(kafka_settings.kafka_commit_every_batch); - if (args_count >= 10) - { - const auto * ast = engine_args[9]->as(); - if (ast && ast->value.getType() == Field::Types::UInt64) - { - intermediate_commit = static_cast(safeGet(ast->value)); - } - else - { - throw Exception("Flag for committing every batch must be 0 or 1", ErrorCodes::BAD_ARGUMENTS); - } - } - - // Get and check client id - String client_id = kafka_settings.kafka_client_id.value; - - return StorageKafka::create( - args.table_id, args.context, args.columns, - brokers, group, client_id, topics, format, row_delimiter, schema, num_consumers, max_block_size, skip_broken, intermediate_commit); + return StorageKafka::create(args.table_id, args.context, args.columns, std::move(kafka_settings)); }; factory.registerStorage("Kafka", creator_fn, StorageFactory::StorageFeatures{ .supports_settings = true, }); diff --git a/src/Storages/Kafka/StorageKafka.h b/src/Storages/Kafka/StorageKafka.h index 1ea7d6dcad7..be3f89687fe 100644 --- a/src/Storages/Kafka/StorageKafka.h +++ b/src/Storages/Kafka/StorageKafka.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -54,10 +55,7 @@ public: ProducerBufferPtr createWriteBuffer(const Block & header); - const auto & getTopics() const { return topics; } const auto & getFormatName() const { return format_name; } - const auto & getSchemaName() const { return schema_name; } - const auto & skipBroken() const { return skip_broken; } NamesAndTypesList getVirtuals() const override; protected: @@ -65,58 +63,53 @@ protected: const StorageID & table_id_, Context & context_, const ColumnsDescription & columns_, - const String & brokers_, - const String & group_, - const String & client_id_, - const Names & topics_, - const String & format_name_, - char row_delimiter_, - const String & schema_name_, - size_t num_consumers_, - UInt64 max_block_size_, - size_t skip_broken, - bool intermediate_commit_); + std::unique_ptr kafka_settings_); private: // Configuration and state - Context global_context; - Context kafka_context; - Names topics; + Context & global_context; + std::shared_ptr kafka_context; + std::unique_ptr kafka_settings; + const Names topics; const String brokers; const String group; const String client_id; const String format_name; - char row_delimiter; /// optional row delimiter for generating char delimited stream in order to make various input stream parsers happy. + const char row_delimiter; /// optional row delimiter for generating char delimited stream in order to make various input stream parsers happy. const String schema_name; - size_t num_consumers; /// total number of consumers - UInt64 max_block_size; /// maximum block size for insertion into this table + const size_t num_consumers; /// total number of consumers + Poco::Logger * log; + Poco::Semaphore semaphore; + const bool intermediate_commit; + const SettingsChanges settings_adjustments; /// Can differ from num_consumers in case of exception in startup() (or if startup() hasn't been called). /// In this case we still need to be able to shutdown() properly. size_t num_created_consumers = 0; /// number of actually created consumers. - Poco::Logger * log; - - // Consumer list - Poco::Semaphore semaphore; - std::mutex mutex; std::vector buffers; /// available buffers for Kafka consumers - size_t skip_broken; - - bool intermediate_commit; + std::mutex mutex; // Stream thread BackgroundSchedulePool::TaskHolder task; std::atomic stream_cancelled{false}; + SettingsChanges createSettingsAdjustments(); ConsumerBufferPtr createReadBuffer(const size_t consumer_number); // Update Kafka configuration with values from CH user configuration. - void updateConfiguration(cppkafka::Configuration & conf); + void updateConfiguration(cppkafka::Configuration & conf); void threadFunc(); + + size_t getPollMaxBatchSize() const; + size_t getMaxBlockSize() const; + size_t getPollTimeoutMillisecond() const; + + static Names parseTopics(String topic_list); static String getDefaultClientId(const StorageID & table_id_); + bool streamToViews(); bool checkDependencies(const StorageID & table_id); }; diff --git a/src/Storages/LiveView/StorageBlocks.h b/src/Storages/LiveView/StorageBlocks.h index a21a9374137..2a9d7766fd7 100644 --- a/src/Storages/LiveView/StorageBlocks.h +++ b/src/Storages/LiveView/StorageBlocks.h @@ -26,6 +26,11 @@ public: return std::make_shared(table_id, columns, std::move(pipes), to_stage); } std::string getName() const override { return "Blocks"; } + /// It is passed inside the query and solved at its level. + bool supportsPrewhere() const override { return true; } + bool supportsSampling() const override { return true; } + bool supportsFinal() const override { return true; } + QueryProcessingStage::Enum getQueryProcessingStage(const Context &, QueryProcessingStage::Enum /*to_stage*/, const ASTPtr &) const override { return to_stage; } Pipes read( diff --git a/src/Storages/LiveView/StorageLiveView.cpp b/src/Storages/LiveView/StorageLiveView.cpp index 3ee8c8409b8..8a04a9e49e4 100644 --- a/src/Storages/LiveView/StorageLiveView.cpp +++ b/src/Storages/LiveView/StorageLiveView.cpp @@ -19,7 +19,7 @@ limitations under the License. */ #include #include #include -#include +#include #include #include #include diff --git a/src/Storages/MergeTree/BackgroundProcessingPool.cpp b/src/Storages/MergeTree/BackgroundProcessingPool.cpp index 8f6d7c19549..ec062d3d138 100644 --- a/src/Storages/MergeTree/BackgroundProcessingPool.cpp +++ b/src/Storages/MergeTree/BackgroundProcessingPool.cpp @@ -16,30 +16,24 @@ namespace DB { -void BackgroundProcessingPoolTaskInfo::wake() +void BackgroundProcessingPoolTaskInfo::signalReadyToRun() { Poco::Timestamp current_time; - { std::unique_lock lock(pool.tasks_mutex); - /// This will ensure that iterator is valid. Must be done under the same mutex when the iterator is invalidated. + /// This check ensures that the iterator is valid. Must be performed under the same mutex as invalidation. if (removed) return; - auto next_time_to_execute = iterator->first; - auto this_task_handle = iterator->second; + /// If this task did nothing the previous time and still should sleep, then reschedule to cancel the sleep. + const auto & scheduled_time = iterator->first; + if (scheduled_time > current_time) + pool.rescheduleTask(iterator, current_time); - /// If this task was done nothing at previous time and it has to sleep, then cancel sleep time. - if (next_time_to_execute > current_time) - next_time_to_execute = current_time; - - pool.tasks.erase(iterator); - iterator = pool.tasks.emplace(next_time_to_execute, this_task_handle); + /// Note that if all threads are currently busy doing their work, this call will not wakeup any thread. + pool.wake_event.notify_one(); } - - /// Note that if all threads are currently do some work, this call will not wakeup any thread. - pool.wake_event.notify_one(); } @@ -56,7 +50,7 @@ BackgroundProcessingPool::BackgroundProcessingPool(int size_, threads.resize(size); for (auto & thread : threads) - thread = ThreadFromGlobalPool([this] { threadFunction(); }); + thread = ThreadFromGlobalPool([this] { workLoopFunc(); }); } @@ -65,16 +59,19 @@ BackgroundProcessingPool::TaskHandle BackgroundProcessingPool::createTask(const return std::make_shared(*this, task); } -void BackgroundProcessingPool::startTask(const TaskHandle & task) +void BackgroundProcessingPool::startTask(const TaskHandle & task, bool allow_execute_in_parallel) { Poco::Timestamp current_time; + task->allow_execute_in_parallel = allow_execute_in_parallel; + { std::unique_lock lock(tasks_mutex); task->iterator = tasks.emplace(current_time, task); + + wake_event.notify_all(); } - wake_event.notify_all(); } BackgroundProcessingPool::TaskHandle BackgroundProcessingPool::addTask(const Task & task) @@ -105,8 +102,12 @@ BackgroundProcessingPool::~BackgroundProcessingPool() { try { - shutdown = true; - wake_event.notify_all(); + { + std::lock_guard lock(tasks_mutex); + shutdown = true; + wake_event.notify_all(); + } + for (auto & thread : threads) thread.join(); } @@ -117,7 +118,7 @@ BackgroundProcessingPool::~BackgroundProcessingPool() } -void BackgroundProcessingPool::threadFunction() +void BackgroundProcessingPool::workLoopFunc() { setThreadName(thread_name); @@ -137,80 +138,82 @@ void BackgroundProcessingPool::threadFunction() } SCOPE_EXIT({ CurrentThread::detachQueryIfNotDetached(); }); - if (auto * memory_tracker = CurrentThread::getMemoryTracker()) + if (auto * const memory_tracker = CurrentThread::getMemoryTracker()) memory_tracker->setMetric(settings.memory_metric); pcg64 rng(randomSeed()); std::this_thread::sleep_for(std::chrono::duration(std::uniform_real_distribution(0, settings.thread_sleep_seconds_random_part)(rng))); - while (!shutdown) + Poco::Timestamp scheduled_task_start_time; + + while (true) { TaskResult task_result = TaskResult::ERROR; TaskHandle task; - try { - Poco::Timestamp min_time; + std::unique_lock lock(tasks_mutex); + while (!task && !shutdown) { - std::unique_lock lock(tasks_mutex); - - if (!tasks.empty()) + for (const auto & [time, handle] : tasks) { - for (const auto & time_handle : tasks) + if (!handle->removed + && (handle->allow_execute_in_parallel || handle->concurrent_executors == 0)) { - if (!time_handle.second->removed) - { - min_time = time_handle.first; - task = time_handle.second; - break; - } + task = handle; + scheduled_task_start_time = time; + ++task->concurrent_executors; + break; } } + + if (task) + { + Poco::Timestamp current_time; + + if (scheduled_task_start_time <= current_time) + continue; + + wake_event.wait_for(lock, + std::chrono::microseconds(scheduled_task_start_time - current_time + + std::uniform_int_distribution(0, settings.thread_sleep_seconds_random_part * 1000000)(rng))); + } + else + { + wake_event.wait_for(lock, + std::chrono::duration(settings.thread_sleep_seconds + + std::uniform_real_distribution(0, settings.thread_sleep_seconds_random_part)(rng))); + } } if (shutdown) break; + } - if (!task) - { - std::unique_lock lock(tasks_mutex); - wake_event.wait_for(lock, - std::chrono::duration(settings.thread_sleep_seconds - + std::uniform_real_distribution(0, settings.thread_sleep_seconds_random_part)(rng))); - continue; - } + std::shared_lock rlock(task->rwlock); - /// No tasks ready for execution. - Poco::Timestamp current_time; - if (min_time > current_time) - { - std::unique_lock lock(tasks_mutex); - wake_event.wait_for(lock, std::chrono::microseconds( - min_time - current_time + std::uniform_int_distribution(0, settings.thread_sleep_seconds_random_part * 1000000)(rng))); - } + if (task->removed) + continue; - std::shared_lock rlock(task->rwlock); - - if (task->removed) - continue; - - { - CurrentMetrics::Increment metric_increment{settings.tasks_metric}; - task_result = task->function(); - } + try + { + CurrentMetrics::Increment metric_increment{settings.tasks_metric}; + task_result = task->task_function(); } catch (...) { tryLogCurrentException(__PRETTY_FUNCTION__); } - if (shutdown) - break; - { std::unique_lock lock(tasks_mutex); + if (shutdown) + break; + + --task->concurrent_executors; + if (task->removed) continue; @@ -231,8 +234,7 @@ void BackgroundProcessingPool::threadFunction() else if (task_result == TaskResult::NOTHING_TO_DO) next_time_to_execute += 1000000 * settings.thread_sleep_seconds_if_nothing_to_do; - tasks.erase(task->iterator); - task->iterator = tasks.emplace(next_time_to_execute, task); + rescheduleTask(task->iterator, next_time_to_execute); } } } diff --git a/src/Storages/MergeTree/BackgroundProcessingPool.h b/src/Storages/MergeTree/BackgroundProcessingPool.h index 526cab0800e..8bed696ab2c 100644 --- a/src/Storages/MergeTree/BackgroundProcessingPool.h +++ b/src/Storages/MergeTree/BackgroundProcessingPool.h @@ -85,11 +85,13 @@ public: /// Create task and start it. TaskHandle addTask(const Task & task); + /// The following two methods are invoked by Storage*MergeTree at startup /// Create task but not start it. TaskHandle createTask(const Task & task); /// Start the task that was created but not started. Precondition: task was not started. - void startTask(const TaskHandle & task); + void startTask(const TaskHandle & task, bool allow_execute_in_parallel = true); + /// Invoked by Storage*MergeTree at shutdown void removeTask(const TaskHandle & task); ~BackgroundProcessingPool(); @@ -109,13 +111,20 @@ protected: Threads threads; - std::atomic shutdown {false}; + bool shutdown{false}; std::condition_variable wake_event; /// Thread group used for profiling purposes ThreadGroupStatusPtr thread_group; - void threadFunction(); + void workLoopFunc(); + + void rescheduleTask(Tasks::iterator & task_it, const Poco::Timestamp & new_scheduled_ts) + { + auto node_handle = tasks.extract(task_it); + node_handle.key() = new_scheduled_ts; + task_it = tasks.insert(std::move(node_handle)); + } private: PoolSettings settings; @@ -125,23 +134,29 @@ private: class BackgroundProcessingPoolTaskInfo { public: - /// Wake up any thread. - void wake(); + /// Signals random idle thread from the pool that this task is ready to be executed. + void signalReadyToRun(); BackgroundProcessingPoolTaskInfo(BackgroundProcessingPool & pool_, const BackgroundProcessingPool::Task & function_) - : pool(pool_), function(function_) {} + : pool(pool_), task_function(function_) {} protected: friend class BackgroundProcessingPool; BackgroundProcessingPool & pool; - BackgroundProcessingPool::Task function; + BackgroundProcessingPool::Task task_function; - /// Read lock is hold when task is executed. + /// Read lock is held while task is being executed. + /// Write lock is used for stopping BGProcPool std::shared_mutex rwlock; + + bool allow_execute_in_parallel = false; + size_t concurrent_executors = 0; + + /// Signals that this task must no longer be planned for execution and is about to be removed std::atomic removed {false}; - std::multimap>::iterator iterator; + BackgroundProcessingPool::Tasks::iterator iterator; /// For exponential backoff. size_t count_no_work_done = 0; diff --git a/src/Storages/MergeTree/DataPartsExchange.cpp b/src/Storages/MergeTree/DataPartsExchange.cpp index 69494ba0d85..acc3bf38461 100644 --- a/src/Storages/MergeTree/DataPartsExchange.cpp +++ b/src/Storages/MergeTree/DataPartsExchange.cpp @@ -86,9 +86,6 @@ void Service::processQuery(const Poco::Net::HTMLForm & params, ReadBuffer & /*bo try { - auto storage_lock = data.lockStructureForShare( - false, RWLockImpl::NO_QUERY, data.getSettings()->lock_acquire_timeout_for_background_operations); - MergeTreeData::DataPartPtr part = findPart(part_name); CurrentMetrics::Increment metric_increment{CurrentMetrics::ReplicatedSend}; diff --git a/src/Storages/MergeTree/EphemeralLockInZooKeeper.cpp b/src/Storages/MergeTree/EphemeralLockInZooKeeper.cpp index 762dbc7d5b6..6b00215fd26 100644 --- a/src/Storages/MergeTree/EphemeralLockInZooKeeper.cpp +++ b/src/Storages/MergeTree/EphemeralLockInZooKeeper.cpp @@ -104,13 +104,13 @@ EphemeralLocksInAllPartitions::EphemeralLocksInAllPartitions( lock_ops.push_back(zkutil::makeCheckRequest(block_numbers_path, partitions_stat.version)); Coordination::Responses lock_responses; - int rc = zookeeper.tryMulti(lock_ops, lock_responses); - if (rc == Coordination::ZBADVERSION) + Coordination::Error rc = zookeeper.tryMulti(lock_ops, lock_responses); + if (rc == Coordination::Error::ZBADVERSION) { LOG_TRACE(&Poco::Logger::get("EphemeralLocksInAllPartitions"), "Someone has inserted a block in a new partition while we were creating locks. Retry."); continue; } - else if (rc != Coordination::ZOK) + else if (rc != Coordination::Error::ZOK) throw Coordination::Exception(rc); for (size_t i = 0; i < partitions.size(); ++i) diff --git a/src/Storages/MergeTree/IMergeTreeDataPart.h b/src/Storages/MergeTree/IMergeTreeDataPart.h index d98457f7f4b..f5ca0fee070 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPart.h +++ b/src/Storages/MergeTree/IMergeTreeDataPart.h @@ -107,6 +107,7 @@ public: virtual ~IMergeTreeDataPart(); using ColumnToSize = std::map; + /// Populates columns_to_size map (compressed size). void accumulateColumnSizes(ColumnToSize & /* column_to_size */) const; Type getType() const { return part_type; } @@ -117,6 +118,7 @@ public: const NamesAndTypesList & getColumns() const { return columns; } + /// Throws an exception if part is not stored in on-disk format. void assertOnDisk() const; void remove() const; @@ -161,6 +163,8 @@ public: VolumePtr volume; + /// A directory path (relative to storage's path) where part data is actually stored + /// Examples: 'detached/tmp_fetch_', 'tmp_', '' mutable String relative_path; MergeTreeIndexGranularityInfo index_granularity_info; @@ -290,10 +294,21 @@ public: void setBytesOnDisk(UInt64 bytes_on_disk_) { bytes_on_disk = bytes_on_disk_; } size_t getFileSizeOrZero(const String & file_name) const; + + /// Returns path to part dir relatively to disk mount point String getFullRelativePath() const; + + /// Returns full path to part dir String getFullPath() const; - void renameTo(const String & new_relative_path, bool remove_new_dir_if_exists = false) const; + + /// Makes checks and move part to new directory + /// Changes only relative_dir_name, you need to update other metadata (name, is_temp) explicitly + void renameTo(const String & new_relative_path, bool remove_new_dir_if_exists = true) const; + + /// Moves a part to detached/ directory and adds prefix to its name void renameToDetached(const String & prefix) const; + + /// Makes clone of a part in detached/ directory via hard links void makeCloneInDetached(const String & prefix) const; /// Makes full clone of part in detached/ on another disk @@ -306,6 +321,7 @@ public: /// storage and pass it to this method. virtual bool hasColumnFiles(const String & /* column */, const IDataType & /* type */) const{ return false; } + /// Calculate the total size of the entire directory with all the files static UInt64 calculateTotalSizeOnDisk(const DiskPtr & disk_, const String & from); void calculateColumnsSizesOnDisk(); @@ -358,6 +374,7 @@ private: void loadPartitionAndMinMaxIndex(); + /// Generate unique path to detach part String getRelativePathForDetachedPart(const String & prefix) const; }; diff --git a/src/Storages/MergeTree/IMergeTreeDataPartWriter.cpp b/src/Storages/MergeTree/IMergeTreeDataPartWriter.cpp index 763f4fff879..cfda613d31d 100644 --- a/src/Storages/MergeTree/IMergeTreeDataPartWriter.cpp +++ b/src/Storages/MergeTree/IMergeTreeDataPartWriter.cpp @@ -173,9 +173,9 @@ void IMergeTreeDataPartWriter::initPrimaryIndex() void IMergeTreeDataPartWriter::initSkipIndices() { - for (const auto & index : skip_indices) + for (const auto & index_helper : skip_indices) { - String stream_name = index->getFileName(); + String stream_name = index_helper->getFileName(); skip_indices_streams.emplace_back( std::make_unique( stream_name, @@ -184,7 +184,7 @@ void IMergeTreeDataPartWriter::initSkipIndices() part_path + stream_name, marks_file_extension, default_codec, settings.max_compress_block_size, 0, settings.aio_threshold)); - skip_indices_aggregators.push_back(index->createIndexAggregator()); + skip_indices_aggregators.push_back(index_helper->createIndexAggregator()); skip_index_filling.push_back(0); } @@ -253,7 +253,7 @@ void IMergeTreeDataPartWriter::calculateAndSerializeSkipIndices( /// Filling and writing skip indices like in MergeTreeDataPartWriterWide::writeColumn for (size_t i = 0; i < skip_indices.size(); ++i) { - const auto index = skip_indices[i]; + const auto index_helper = skip_indices[i]; auto & stream = *skip_indices_streams[i]; size_t prev_pos = 0; skip_index_current_data_mark = skip_index_data_mark; @@ -269,7 +269,7 @@ void IMergeTreeDataPartWriter::calculateAndSerializeSkipIndices( limit = index_granularity.getMarkRows(skip_index_current_data_mark); if (skip_indices_aggregators[i]->empty()) { - skip_indices_aggregators[i] = index->createIndexAggregator(); + skip_indices_aggregators[i] = index_helper->createIndexAggregator(); skip_index_filling[i] = 0; if (stream.compressed.offset() >= settings.min_compress_block_size) @@ -294,7 +294,7 @@ void IMergeTreeDataPartWriter::calculateAndSerializeSkipIndices( ++skip_index_filling[i]; /// write index if it is filled - if (skip_index_filling[i] == index->granularity) + if (skip_index_filling[i] == index_helper->index.granularity) { skip_indices_aggregators[i]->getGranuleAndReset()->serializeBinary(stream.compressed); skip_index_filling[i] = 0; diff --git a/src/Storages/MergeTree/KeyCondition.cpp b/src/Storages/MergeTree/KeyCondition.cpp index ee381709dd4..dad73b6a003 100644 --- a/src/Storages/MergeTree/KeyCondition.cpp +++ b/src/Storages/MergeTree/KeyCondition.cpp @@ -849,7 +849,7 @@ bool KeyCondition::tryParseAtomFromAST(const ASTPtr & node, const Context & cont || const_value.getType() == Field::Types::Float64) { /// Zero in all types is represented in memory the same way as in UInt64. - out.function = const_value.get() + out.function = const_value.safeGet() ? RPNElement::ALWAYS_TRUE : RPNElement::ALWAYS_FALSE; diff --git a/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp b/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp index be3caf98ad4..b6376dd3779 100644 --- a/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp +++ b/src/Storages/MergeTree/MergeTreeBlockOutputStream.cpp @@ -28,7 +28,7 @@ void MergeTreeBlockOutputStream::write(const Block & block) /// Initiate async merge - it will be done if it's good time for merge and if there are space in 'background_pool'. if (storage.merging_mutating_task_handle) - storage.merging_mutating_task_handle->wake(); + storage.merging_mutating_task_handle->signalReadyToRun(); } } diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 168a218184d..023e67ec3de 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -153,7 +152,8 @@ MergeTreeData::MergeTreeData( if (metadata.sample_by_ast != nullptr) { - StorageMetadataKeyField candidate_sampling_key = StorageMetadataKeyField::getKeyFromAST(metadata.sample_by_ast, getColumns(), global_context); + StorageMetadataKeyField candidate_sampling_key = StorageMetadataKeyField::getKeyFromAST( + metadata.sample_by_ast, getColumns(), global_context); const auto & pk_sample_block = getPrimaryKey().sample_block; if (!pk_sample_block.has(candidate_sampling_key.column_names[0]) && !attach @@ -248,7 +248,7 @@ MergeTreeData::MergeTreeData( StorageInMemoryMetadata MergeTreeData::getInMemoryMetadata() const { - StorageInMemoryMetadata metadata(getColumns(), getIndices(), getConstraints()); + StorageInMemoryMetadata metadata(getColumns(), getSecondaryIndices(), getConstraints()); if (isPartitionKeyDefined()) metadata.partition_by_ast = getPartitionKeyAST()->clone(); @@ -422,57 +422,23 @@ void MergeTreeData::setProperties(const StorageInMemoryMetadata & metadata, bool new_sorting_key_data_types.push_back(new_sorting_key_sample.getByPosition(i).type); } - ASTPtr skip_indices_with_primary_key_expr_list = new_primary_key_expr_list->clone(); - ASTPtr skip_indices_expr_list = new_primary_key_expr_list->clone(); - ASTPtr skip_indices_with_sorting_key_expr_list = new_sorting_key_expr_list->clone(); - - MergeTreeIndices new_indices; - - if (!metadata.indices.indices.empty()) + if (!metadata.secondary_indices.empty()) { std::set indices_names; - for (const auto & index_ast : metadata.indices.indices) + for (const auto & index : metadata.secondary_indices) { - const auto & index_decl = std::dynamic_pointer_cast(index_ast); - new_indices.push_back( - MergeTreeIndexFactory::instance().get( - all_columns, - std::dynamic_pointer_cast(index_decl), - global_context, - attach)); + MergeTreeIndexFactory::instance().validate(index, attach); - if (indices_names.find(new_indices.back()->name) != indices_names.end()) + if (indices_names.find(index.name) != indices_names.end()) throw Exception( - "Index with name " + backQuote(new_indices.back()->name) + " already exsists", + "Index with name " + backQuote(index.name) + " already exsists", ErrorCodes::LOGICAL_ERROR); - ASTPtr expr_list = MergeTreeData::extractKeyExpressionList(index_decl->expr->clone()); - for (const auto & expr : expr_list->children) - { - skip_indices_with_primary_key_expr_list->children.push_back(expr->clone()); - skip_indices_with_sorting_key_expr_list->children.push_back(expr->clone()); - skip_indices_expr_list->children.push_back(expr->clone()); - } - - indices_names.insert(new_indices.back()->name); + indices_names.insert(index.name); } } - auto syntax_primary = SyntaxAnalyzer(global_context).analyze( - skip_indices_with_primary_key_expr_list, all_columns); - auto new_indices_with_primary_key_expr = ExpressionAnalyzer( - skip_indices_with_primary_key_expr_list, syntax_primary, global_context).getActions(false); - - auto syntax_indices = SyntaxAnalyzer(global_context).analyze( - skip_indices_with_primary_key_expr_list, all_columns); - auto new_indices_expr = ExpressionAnalyzer( - skip_indices_expr_list, syntax_indices, global_context).getActions(false); - - auto syntax_sorting = SyntaxAnalyzer(global_context).analyze( - skip_indices_with_sorting_key_expr_list, all_columns); - auto new_indices_with_sorting_key_expr = ExpressionAnalyzer( - skip_indices_with_sorting_key_expr_list, syntax_sorting, global_context).getActions(false); if (!only_check) { @@ -496,17 +462,42 @@ void MergeTreeData::setProperties(const StorageInMemoryMetadata & metadata, bool new_primary_key.data_types = std::move(new_primary_key_data_types); setPrimaryKey(new_primary_key); - setIndices(metadata.indices); - skip_indices = std::move(new_indices); + setSecondaryIndices(metadata.secondary_indices); setConstraints(metadata.constraints); - - skip_indices_expr = new_indices_expr; - primary_key_and_skip_indices_expr = new_indices_with_primary_key_expr; - sorting_key_and_skip_indices_expr = new_indices_with_sorting_key_expr; } } +namespace +{ + +ExpressionActionsPtr getCombinedIndicesExpression( + const StorageMetadataKeyField & key, + const IndicesDescription & indices, + const ColumnsDescription & columns, + const Context & context) +{ + ASTPtr combined_expr_list = key.expression_list_ast->clone(); + + for (const auto & index : indices) + for (const auto & index_expr : index.expression_list_ast->children) + combined_expr_list->children.push_back(index_expr->clone()); + + auto syntax_result = SyntaxAnalyzer(context).analyze(combined_expr_list, columns.getAllPhysical()); + return ExpressionAnalyzer(combined_expr_list, syntax_result, context).getActions(false); +} + +} + +ExpressionActionsPtr MergeTreeData::getPrimaryKeyAndSkipIndicesExpression() const +{ + return getCombinedIndicesExpression(getPrimaryKey(), getSecondaryIndices(), getColumns(), global_context); +} + +ExpressionActionsPtr MergeTreeData::getSortingKeyAndSkipIndicesExpression() const +{ + return getCombinedIndicesExpression(getSortingKey(), getSecondaryIndices(), getColumns(), global_context); +} ASTPtr MergeTreeData::extractKeyExpressionList(const ASTPtr & node) { @@ -1314,6 +1305,24 @@ void MergeTreeData::dropAllData() LOG_TRACE(log, "dropAllData: done."); } +void MergeTreeData::dropIfEmpty() +{ + LOG_TRACE(log, "dropIfEmpty"); + + auto lock = lockParts(); + + if (!data_parts_by_info.empty()) + return; + + for (const auto & [path, disk] : getRelativeDataPathsWithDisks()) + { + /// Non recursive, exception is thrown if there are more files. + disk->remove(path + "format_version.txt"); + disk->remove(path + "detached"); + disk->remove(path); + } +} + namespace { @@ -1374,8 +1383,8 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S { /// Check that needed transformations can be applied to the list of columns without considering type conversions. StorageInMemoryMetadata metadata = getInMemoryMetadata(); - commands.apply(metadata); - if (getIndices().empty() && !metadata.indices.empty() && + commands.apply(metadata, global_context); + if (getSecondaryIndices().empty() && !metadata.secondary_indices.empty() && !settings.allow_experimental_data_skipping_indices) throw Exception("You must set the setting `allow_experimental_data_skipping_indices` to 1 " \ "before using data skipping indices.", ErrorCodes::BAD_ARGUMENTS); @@ -1396,9 +1405,9 @@ void MergeTreeData::checkAlterIsPossible(const AlterCommands & commands, const S columns_alter_type_forbidden.insert(col); } - for (const auto & index : skip_indices) + for (const auto & index : getSecondaryIndices()) { - for (const String & col : index->expr->getRequiredColumns()) + for (const String & col : index.expression->getRequiredColumns()) columns_alter_type_forbidden.insert(col); } @@ -1875,7 +1884,7 @@ void MergeTreeData::removePartsFromWorkingSet(const MergeTreeData::DataPartsVect part->remove_time.store(remove_time, std::memory_order_relaxed); if (part->state != IMergeTreeDataPart::State::Outdated) - modifyPartState(part,IMergeTreeDataPart::State::Outdated); + modifyPartState(part, IMergeTreeDataPart::State::Outdated); } } @@ -3073,14 +3082,15 @@ bool MergeTreeData::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, con /// If there is a tuple on the left side of the IN operator, at least one item of the tuple /// must be part of the key (probably wrapped by a chain of some acceptable functions). const auto * left_in_operand_tuple = left_in_operand->as(); + const auto & index_wrapper_factory = MergeTreeIndexFactory::instance(); if (left_in_operand_tuple && left_in_operand_tuple->name == "tuple") { for (const auto & item : left_in_operand_tuple->arguments->children) { if (isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(item)) return true; - for (const auto & index : skip_indices) - if (index->mayBenefitFromIndexForIn(item)) + for (const auto & index : getSecondaryIndices()) + if (index_wrapper_factory.get(index)->mayBenefitFromIndexForIn(item)) return true; } /// The tuple itself may be part of the primary key, so check that as a last resort. @@ -3088,8 +3098,8 @@ bool MergeTreeData::mayBenefitFromIndexForIn(const ASTPtr & left_in_operand, con } else { - for (const auto & index : skip_indices) - if (index->mayBenefitFromIndexForIn(left_in_operand)) + for (const auto & index : getSecondaryIndices()) + if (index_wrapper_factory.get(index)->mayBenefitFromIndexForIn(left_in_operand)) return true; return isPrimaryOrMinMaxKeyColumnPossiblyWrappedInFunctions(left_in_operand); @@ -3476,64 +3486,6 @@ bool MergeTreeData::moveParts(CurrentlyMovingPartsTagger && moving_tagger) return true; } -ColumnDependencies MergeTreeData::getColumnDependencies(const NameSet & updated_columns) const -{ - if (updated_columns.empty()) - return {}; - - ColumnDependencies res; - - NameSet indices_columns; - NameSet required_ttl_columns; - NameSet updated_ttl_columns; - - auto add_dependent_columns = [&updated_columns](const auto & expression, auto & to_set) - { - auto requiered_columns = expression->getRequiredColumns(); - for (const auto & dependency : requiered_columns) - { - if (updated_columns.count(dependency)) - { - to_set.insert(requiered_columns.begin(), requiered_columns.end()); - return true; - } - } - - return false; - }; - - for (const auto & index : skip_indices) - add_dependent_columns(index->expr, indices_columns); - - if (hasRowsTTL()) - { - if (add_dependent_columns(getRowsTTL().expression, required_ttl_columns)) - { - /// Filter all columns, if rows TTL expression have to be recalculated. - for (const auto & column : getColumns().getAllPhysical()) - updated_ttl_columns.insert(column.name); - } - } - - for (const auto & [name, entry] : getColumnTTLs()) - { - if (add_dependent_columns(entry.expression, required_ttl_columns)) - updated_ttl_columns.insert(name); - } - - for (const auto & entry : getMoveTTLs()) - add_dependent_columns(entry.expression, required_ttl_columns); - - for (const auto & column : indices_columns) - res.emplace(column, ColumnDependency::SKIP_INDEX); - for (const auto & column : required_ttl_columns) - res.emplace(column, ColumnDependency::TTL_EXPRESSION); - for (const auto & column : updated_ttl_columns) - res.emplace(column, ColumnDependency::TTL_TARGET); - - return res; -} - bool MergeTreeData::canUsePolymorphicParts(const MergeTreeSettings & settings, String * out_reason) const { if (!canUseAdaptiveGranularity()) diff --git a/src/Storages/MergeTree/MergeTreeData.h b/src/Storages/MergeTree/MergeTreeData.h index d9bedd7c6db..217e5000cf6 100644 --- a/src/Storages/MergeTree/MergeTreeData.h +++ b/src/Storages/MergeTree/MergeTreeData.h @@ -336,8 +336,6 @@ public: /// See comments about methods below in IStorage interface StorageInMemoryMetadata getInMemoryMetadata() const override; - ColumnDependencies getColumnDependencies(const NameSet & updated_columns) const override; - StoragePolicyPtr getStoragePolicy() const override; bool supportsPrewhere() const override { return true; } @@ -487,6 +485,9 @@ public: /// Deletes the data directory and flushes the uncompressed blocks cache and the marks cache. void dropAllData(); + /// Drop data directories if they are empty. It is safe to call this method if table creation was unsuccessful. + void dropIfEmpty(); + /// Moves the entire data directory. /// Flushes the uncompressed blocks cache and the marks cache. /// Must be called with locked lockStructureForAlter(). @@ -518,7 +519,6 @@ public: */ static ASTPtr extractKeyExpressionList(const ASTPtr & node); - bool hasSkipIndices() const { return !skip_indices.empty(); } /// Check that the part is not broken and calculate the checksums for it if they are not present. MutableDataPartPtr loadPartAndFixMetadata(const VolumePtr & volume, const String & relative_path) const; @@ -644,12 +644,8 @@ public: Int64 minmax_idx_date_column_pos = -1; /// In a common case minmax index includes a date column. Int64 minmax_idx_time_column_pos = -1; /// In other cases, minmax index often includes a dateTime column. - /// Secondary (data skipping) indices for MergeTree - MergeTreeIndices skip_indices; - - ExpressionActionsPtr skip_indices_expr; - ExpressionActionsPtr primary_key_and_skip_indices_expr; - ExpressionActionsPtr sorting_key_and_skip_indices_expr; + ExpressionActionsPtr getPrimaryKeyAndSkipIndicesExpression() const; + ExpressionActionsPtr getSortingKeyAndSkipIndicesExpression() const; std::optional selectTTLEntryForTTLInfos(const IMergeTreeDataPart::TTLInfos & ttl_infos, time_t time_of_move) const; diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp index 1e8d4136308..d861173d8a0 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.cpp @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -411,7 +410,7 @@ MergeTreeData::DataPartsVector MergeTreeDataMergerMutator::selectAllPartsFromPar static void extractMergingAndGatheringColumns( const NamesAndTypesList & storage_columns, const ExpressionActionsPtr & sorting_key_expr, - const MergeTreeIndices & indexes, + const IndicesDescription & indexes, const MergeTreeData::MergingParams & merging_params, NamesAndTypesList & gathering_columns, Names & gathering_column_names, NamesAndTypesList & merging_columns, Names & merging_column_names) @@ -420,7 +419,7 @@ static void extractMergingAndGatheringColumns( std::set key_columns(sort_key_columns_vec.cbegin(), sort_key_columns_vec.cend()); for (const auto & index : indexes) { - Names index_columns_vec = index->getColumnsRequiredForIndexCalc(); + Names index_columns_vec = index.expression->getRequiredColumns(); std::copy(index_columns_vec.cbegin(), index_columns_vec.cend(), std::inserter(key_columns, key_columns.end())); } @@ -607,7 +606,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor NamesAndTypesList merging_columns; Names gathering_column_names, merging_column_names; extractMergingAndGatheringColumns( - storage_columns, data.getSortingKey().expression, data.skip_indices, + storage_columns, data.getSortingKey().expression, data.getSecondaryIndices(), data.merging_params, gathering_columns, gathering_column_names, merging_columns, merging_column_names); auto single_disk_volume = std::make_shared("volume_" + future_part.name, disk); @@ -792,16 +791,19 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mergePartsToTempor if (need_remove_expired_values) merged_stream = std::make_shared(merged_stream, data, new_data_part, time_of_merge, force_ttl); - if (data.hasSkipIndices()) + + if (data.hasSecondaryIndices()) { - merged_stream = std::make_shared(merged_stream, data.skip_indices_expr); + const auto & indices = data.getSecondaryIndices(); + merged_stream = std::make_shared(merged_stream, indices.getSingleExpressionForIndices(data.getColumns(), data.global_context)); merged_stream = std::make_shared(merged_stream); } + const auto & index_factory = MergeTreeIndexFactory::instance(); MergedBlockOutputStream to{ new_data_part, merging_columns, - data.skip_indices, + index_factory.getMany(data.getSecondaryIndices()), compression_codec, merged_column_to_size, data_settings->min_merge_bytes_to_use_direct_io, @@ -1073,7 +1075,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor /// All columns from part are changed and may be some more that were missing before in part if (isCompactPart(source_part) || source_part->getColumns().isSubsetOf(updated_header.getNamesAndTypesList())) { - auto part_indices = getIndicesForNewDataPart(data.skip_indices, for_file_renames); + auto part_indices = getIndicesForNewDataPart(data.getSecondaryIndices(), for_file_renames); mutateAllPartColumns( new_data_part, part_indices, @@ -1089,7 +1091,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMergerMutator::mutatePartToTempor else /// TODO: check that we modify only non-key columns in this case. { /// We will modify only some of the columns. Other columns and key values can be copied as-is. - auto indices_to_recalc = getIndicesToRecalculate(in, storage_from_source_part, updated_header.getNamesAndTypesList(), context); + auto indices_to_recalc = getIndicesToRecalculate(in, updated_header.getNamesAndTypesList(), context); NameSet files_to_skip = collectFilesToSkip(updated_header, indices_to_recalc, mrk_extension); NameToNameVector files_to_rename = collectFilesForRenames(source_part, for_file_renames, mrk_extension); @@ -1492,7 +1494,7 @@ NamesAndTypesList MergeTreeDataMergerMutator::getColumnsForNewDataPart( } MergeTreeIndices MergeTreeDataMergerMutator::getIndicesForNewDataPart( - const MergeTreeIndices & all_indices, + const IndicesDescription & all_indices, const MutationCommands & commands_for_removes) { NameSet removed_indices; @@ -1502,32 +1504,34 @@ MergeTreeIndices MergeTreeDataMergerMutator::getIndicesForNewDataPart( MergeTreeIndices new_indices; for (const auto & index : all_indices) - if (!removed_indices.count(index->name)) - new_indices.push_back(index); + if (!removed_indices.count(index.name)) + new_indices.push_back(MergeTreeIndexFactory::instance().get(index)); return new_indices; } std::set MergeTreeDataMergerMutator::getIndicesToRecalculate( BlockInputStreamPtr & input_stream, - StoragePtr storage_from_source_part, const NamesAndTypesList & updated_columns, const Context & context) const { /// Checks if columns used in skipping indexes modified. + const auto & index_factory = MergeTreeIndexFactory::instance(); std::set indices_to_recalc; ASTPtr indices_recalc_expr_list = std::make_shared(); for (const auto & col : updated_columns.getNames()) { - for (size_t i = 0; i < data.skip_indices.size(); ++i) + const auto & indices = data.getSecondaryIndices(); + for (size_t i = 0; i < indices.size(); ++i) { - const auto & index = data.skip_indices[i]; - const auto & index_cols = index->getColumnsRequiredForIndexCalc(); + const auto & index = indices[i]; + const auto & index_cols = index.expression->getRequiredColumns(); auto it = std::find(std::cbegin(index_cols), std::cend(index_cols), col); - if (it != std::cend(index_cols) && indices_to_recalc.insert(index).second) + + if (it != std::cend(index_cols) + && indices_to_recalc.insert(index_factory.get(index)).second) { - ASTPtr expr_list = MergeTreeData::extractKeyExpressionList( - storage_from_source_part->getIndices().indices[i]->expr->clone()); + ASTPtr expr_list = index.expression_list_ast->clone(); for (const auto & expr : expr_list->children) indices_recalc_expr_list->children.push_back(expr->clone()); } @@ -1581,9 +1585,9 @@ void MergeTreeDataMergerMutator::mutateAllPartColumns( if (mutating_stream == nullptr) throw Exception("Cannot mutate part columns with uninitialized mutations stream. It's a bug", ErrorCodes::LOGICAL_ERROR); - if (data.hasPrimaryKey() || data.hasSkipIndices()) + if (data.hasPrimaryKey() || data.hasSecondaryIndices()) mutating_stream = std::make_shared( - std::make_shared(mutating_stream, data.primary_key_and_skip_indices_expr)); + std::make_shared(mutating_stream, data.getPrimaryKeyAndSkipIndicesExpression())); if (need_remove_expired_values) mutating_stream = std::make_shared(mutating_stream, data, new_data_part, time_of_mutation, true); diff --git a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h index d26e84eb18a..7c2ee53fc1d 100644 --- a/src/Storages/MergeTree/MergeTreeDataMergerMutator.h +++ b/src/Storages/MergeTree/MergeTreeDataMergerMutator.h @@ -82,7 +82,6 @@ public: const AllowedMergingPredicate & can_merge, String * out_disable_reason = nullptr); - /** Select all the parts in the specified partition for merge, if possible. * final - choose to merge even a single part - that is, allow to merge one part "with itself". */ @@ -162,7 +161,7 @@ private: /// Get skip indices, that should exists in the resulting data part. static MergeTreeIndices getIndicesForNewDataPart( - const MergeTreeIndices & all_indices, + const IndicesDescription & all_indices, const MutationCommands & commands_for_removes); bool shouldExecuteTTL(const Names & columns, const MutationCommands & commands) const; @@ -171,7 +170,6 @@ private: /// wraps input stream into additional expression stream std::set getIndicesToRecalculate( BlockInputStreamPtr & input_stream, - StoragePtr storage_from_source_part, const NamesAndTypesList & updated_columns, const Context & context) const; diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index e4321b82166..c1fc8184206 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -39,9 +39,7 @@ namespace std } #endif -#include #include -#include #include #include #include @@ -548,11 +546,13 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( RangesInDataParts parts_with_ranges; std::vector> useful_indices; - for (const auto & index : data.skip_indices) + + for (const auto & index : data.getSecondaryIndices()) { - auto condition = index->createIndexCondition(query_info, context); + auto index_helper = MergeTreeIndexFactory::instance().get(index); + auto condition = index_helper->createIndexCondition(query_info, context); if (!condition->alwaysUnknownOrTrue()) - useful_indices.emplace_back(index, condition); + useful_indices.emplace_back(index_helper, condition); } /// Let's find what range to read from each part. @@ -637,9 +637,9 @@ Pipes MergeTreeDataSelectExecutor::readFromParts( reader_settings, result_projection); } - else if (settings.optimize_read_in_order && query_info.input_sorting_info) + else if ((settings.optimize_read_in_order || settings.optimize_aggregation_in_order) && query_info.input_order_info) { - size_t prefix_size = query_info.input_sorting_info->order_key_prefix_descr.size(); + size_t prefix_size = query_info.input_order_info->order_key_prefix_descr.size(); auto order_key_prefix_ast = data.getSortingKey().expression_list_ast->clone(); order_key_prefix_ast->children.resize(prefix_size); @@ -855,7 +855,8 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsWithOrder( ExpressionActionsPtr & out_projection) const { size_t sum_marks = 0; - const InputSortingInfoPtr & input_sorting_info = query_info.input_sorting_info; + const InputOrderInfoPtr & input_order_info = query_info.input_order_info; + size_t adaptive_parts = 0; std::vector sum_marks_in_parts(parts.size()); const auto data_settings = data.getSettings(); @@ -998,10 +999,9 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsWithOrder( } parts.emplace_back(part); } + ranges_to_get_from_part = split_ranges(ranges_to_get_from_part, input_order_info->direction); - ranges_to_get_from_part = split_ranges(ranges_to_get_from_part, input_sorting_info->direction); - - if (input_sorting_info->direction == 1) + if (input_order_info->direction == 1) { pipes.emplace_back(std::make_shared( data, part.data_part, max_block_size, settings.preferred_block_size_bytes, @@ -1024,9 +1024,9 @@ Pipes MergeTreeDataSelectExecutor::spreadMarkRangesAmongStreamsWithOrder( if (pipes.size() > 1) { SortDescription sort_description; - for (size_t j = 0; j < input_sorting_info->order_key_prefix_descr.size(); ++j) + for (size_t j = 0; j < input_order_info->order_key_prefix_descr.size(); ++j) sort_description.emplace_back(data.getSortingKey().column_names[j], - input_sorting_info->direction, 1); + input_order_info->direction, 1); /// Drop temporary columns, added by 'sorting_key_prefix_expr' out_projection = createProjection(pipes.back(), data); @@ -1377,18 +1377,20 @@ MarkRanges MergeTreeDataSelectExecutor::markRangesFromPKRange( } MarkRanges MergeTreeDataSelectExecutor::filterMarksUsingIndex( - MergeTreeIndexPtr index, + MergeTreeIndexPtr index_helper, MergeTreeIndexConditionPtr condition, MergeTreeData::DataPartPtr part, const MarkRanges & ranges, const Settings & settings) const { - if (!part->volume->getDisk()->exists(part->getFullRelativePath() + index->getFileName() + ".idx")) + if (!part->volume->getDisk()->exists(part->getFullRelativePath() + index_helper->getFileName() + ".idx")) { - LOG_DEBUG(log, "File for index {} does not exist. Skipping it.", backQuote(index->name)); + LOG_DEBUG(log, "File for index {} does not exist. Skipping it.", backQuote(index_helper->index.name)); return ranges; } + auto index_granularity = index_helper->index.granularity; + const size_t min_marks_for_seek = roundRowsOrBytesToMarks( settings.merge_tree_min_rows_for_seek, settings.merge_tree_min_bytes_for_seek, @@ -1399,10 +1401,10 @@ MarkRanges MergeTreeDataSelectExecutor::filterMarksUsingIndex( size_t marks_count = part->getMarksCount(); size_t final_mark = part->index_granularity.hasFinalMark(); - size_t index_marks_count = (marks_count - final_mark + index->granularity - 1) / index->granularity; + size_t index_marks_count = (marks_count - final_mark + index_granularity - 1) / index_granularity; MergeTreeIndexReader reader( - index, part, + index_helper, part, index_marks_count, ranges); @@ -1415,8 +1417,8 @@ MarkRanges MergeTreeDataSelectExecutor::filterMarksUsingIndex( for (const auto & range : ranges) { MarkRange index_range( - range.begin / index->granularity, - (range.end + index->granularity - 1) / index->granularity); + range.begin / index_granularity, + (range.end + index_granularity - 1) / index_granularity); if (last_index_mark != index_range.begin || !granule) reader.seek(index_range.begin); @@ -1427,8 +1429,8 @@ MarkRanges MergeTreeDataSelectExecutor::filterMarksUsingIndex( granule = reader.read(); MarkRange data_range( - std::max(range.begin, index_mark * index->granularity), - std::min(range.end, (index_mark + 1) * index->granularity)); + std::max(range.begin, index_mark * index_granularity), + std::min(range.end, (index_mark + 1) * index_granularity)); if (!condition->mayBeTrueOnGranule(granule)) { @@ -1445,7 +1447,7 @@ MarkRanges MergeTreeDataSelectExecutor::filterMarksUsingIndex( last_index_mark = index_range.end - 1; } - LOG_DEBUG(log, "Index {} has dropped {} granules.", backQuote(index->name), granules_dropped); + LOG_DEBUG(log, "Index {} has dropped {} granules.", backQuote(index_helper->index.name), granules_dropped); return res; } diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h index 942e111635b..85d69ead181 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.h @@ -101,7 +101,7 @@ private: const Settings & settings) const; MarkRanges filterMarksUsingIndex( - MergeTreeIndexPtr index, + MergeTreeIndexPtr index_helper, MergeTreeIndexConditionPtr condition, MergeTreeData::DataPartPtr part, const MarkRanges & ranges, diff --git a/src/Storages/MergeTree/MergeTreeDataWriter.cpp b/src/Storages/MergeTree/MergeTreeDataWriter.cpp index 4eedf99c837..52eace30657 100644 --- a/src/Storages/MergeTree/MergeTreeDataWriter.cpp +++ b/src/Storages/MergeTree/MergeTreeDataWriter.cpp @@ -263,8 +263,8 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPa new_data_part->volume->getDisk()->createDirectories(full_path); /// If we need to calculate some columns to sort. - if (data.hasSortingKey() || data.hasSkipIndices()) - data.sorting_key_and_skip_indices_expr->execute(block); + if (data.hasSortingKey() || data.hasSecondaryIndices()) + data.getSortingKeyAndSkipIndicesExpression()->execute(block); Names sort_columns = data.getSortingKeyColumns(); SortDescription sort_description; @@ -302,7 +302,8 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempPart(BlockWithPa /// either default lz4 or compression method with zero thresholds on absolute and relative part size. auto compression_codec = data.global_context.chooseCompressionCodec(0, 0); - MergedBlockOutputStream out(new_data_part, columns, data.skip_indices, compression_codec); + const auto & index_factory = MergeTreeIndexFactory::instance(); + MergedBlockOutputStream out(new_data_part, columns, index_factory.getMany(data.getSecondaryIndices()), compression_codec); out.writePrefix(); out.writeWithPermutation(block, perm_ptr); diff --git a/src/Storages/MergeTree/MergeTreeIndexBloomFilter.cpp b/src/Storages/MergeTree/MergeTreeIndexBloomFilter.cpp index e94eb46625e..e825bb97d6a 100644 --- a/src/Storages/MergeTree/MergeTreeIndexBloomFilter.cpp +++ b/src/Storages/MergeTree/MergeTreeIndexBloomFilter.cpp @@ -28,23 +28,25 @@ namespace ErrorCodes } MergeTreeIndexBloomFilter::MergeTreeIndexBloomFilter( - const String & name_, const ExpressionActionsPtr & expr_, const Names & columns_, const DataTypes & data_types_, const Block & header_, - size_t granularity_, size_t bits_per_row_, size_t hash_functions_) - : IMergeTreeIndex(name_, expr_, columns_, data_types_, header_, granularity_), bits_per_row(bits_per_row_), - hash_functions(hash_functions_) + const IndexDescription & index_, + size_t bits_per_row_, + size_t hash_functions_) + : IMergeTreeIndex(index_) + , bits_per_row(bits_per_row_) + , hash_functions(hash_functions_) { } MergeTreeIndexGranulePtr MergeTreeIndexBloomFilter::createIndexGranule() const { - return std::make_shared(bits_per_row, hash_functions, columns.size()); + return std::make_shared(bits_per_row, hash_functions, index.column_names.size()); } bool MergeTreeIndexBloomFilter::mayBenefitFromIndexForIn(const ASTPtr & node) const { const String & column_name = node->getColumnName(); - for (const auto & cname : columns) + for (const auto & cname : index.column_names) if (column_name == cname) return true; @@ -60,12 +62,12 @@ bool MergeTreeIndexBloomFilter::mayBenefitFromIndexForIn(const ASTPtr & node) co MergeTreeIndexAggregatorPtr MergeTreeIndexBloomFilter::createIndexAggregator() const { - return std::make_shared(bits_per_row, hash_functions, columns); + return std::make_shared(bits_per_row, hash_functions, index.column_names); } MergeTreeIndexConditionPtr MergeTreeIndexBloomFilter::createIndexCondition(const SelectQueryInfo & query_info, const Context & context) const { - return std::make_shared(query_info, context, header, hash_functions); + return std::make_shared(query_info, context, index.sample_block, hash_functions); } static void assertIndexColumnsType(const Block & header) @@ -87,52 +89,41 @@ static void assertIndexColumnsType(const Block & header) } } -std::unique_ptr bloomFilterIndexCreatorNew( - const NamesAndTypesList & columns, std::shared_ptr node, const Context & context, bool attach) +MergeTreeIndexPtr bloomFilterIndexCreatorNew( + const IndexDescription & index) { - if (node->name.empty()) - throw Exception("Index must have unique name.", ErrorCodes::INCORRECT_QUERY); - - ASTPtr expr_list = MergeTreeData::extractKeyExpressionList(node->expr->clone()); - - auto syntax = SyntaxAnalyzer(context).analyze(expr_list, columns); - auto index_expr = ExpressionAnalyzer(expr_list, syntax, context).getActions(false); - auto index_sample = ExpressionAnalyzer(expr_list, syntax, context).getActions(true)->getSampleBlock(); - - assertIndexColumnsType(index_sample); double max_conflict_probability = 0.025; - const auto & arguments = node->type->arguments; - if (arguments && arguments->children.size() > 1) + if (!index.arguments.empty()) { - if (!attach) /// This is for backward compatibility. - throw Exception("BloomFilter index cannot have more than one parameter.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); - - arguments->children = { arguments->children[0] }; - } - - - if (arguments && !arguments->children.empty()) - { - auto * argument = arguments->children[0]->as(); - - if (!argument || (argument->value.safeGet() < 0 || argument->value.safeGet() > 1)) - { - if (!attach || !argument) /// This is for backward compatibility. - throw Exception("The BloomFilter false positive must be a double number between 0 and 1.", ErrorCodes::BAD_ARGUMENTS); - - argument->value = Field(std::min(Float64(1), std::max(argument->value.safeGet(), Float64(0)))); - } - - max_conflict_probability = argument->value.safeGet(); + const auto & argument = index.arguments[0]; + max_conflict_probability = std::min(Float64(1), std::max(argument.safeGet(), Float64(0))); } const auto & bits_per_row_and_size_of_hash_functions = BloomFilterHash::calculationBestPractices(max_conflict_probability); - return std::make_unique( - node->name, std::move(index_expr), index_sample.getNames(), index_sample.getDataTypes(), index_sample, node->granularity, - bits_per_row_and_size_of_hash_functions.first, bits_per_row_and_size_of_hash_functions.second); + return std::make_shared( + index, bits_per_row_and_size_of_hash_functions.first, bits_per_row_and_size_of_hash_functions.second); +} + +void bloomFilterIndexValidatorNew(const IndexDescription & index, bool attach) +{ + assertIndexColumnsType(index.sample_block); + + if (index.arguments.size() > 1) + { + if (!attach) /// This is for backward compatibility. + throw Exception("BloomFilter index cannot have more than one parameter.", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + } + + if (!index.arguments.empty()) + { + const auto & argument = index.arguments[0]; + + if (!attach && (argument.getType() != Field::Types::Float64 || argument.get() < 0 || argument.get() > 1)) + throw Exception("The BloomFilter false positive must be a double number between 0 and 1.", ErrorCodes::BAD_ARGUMENTS); + } } } diff --git a/src/Storages/MergeTree/MergeTreeIndexBloomFilter.h b/src/Storages/MergeTree/MergeTreeIndexBloomFilter.h index 2b89b9bddfa..b0d9a295bcd 100644 --- a/src/Storages/MergeTree/MergeTreeIndexBloomFilter.h +++ b/src/Storages/MergeTree/MergeTreeIndexBloomFilter.h @@ -12,8 +12,9 @@ class MergeTreeIndexBloomFilter : public IMergeTreeIndex { public: MergeTreeIndexBloomFilter( - const String & name_, const ExpressionActionsPtr & expr_, const Names & columns_, const DataTypes & data_types_, - const Block & header_, size_t granularity_, size_t bits_per_row_, size_t hash_functions_); + const IndexDescription & index_, + size_t bits_per_row_, + size_t hash_functions_); MergeTreeIndexGranulePtr createIndexGranule() const override; diff --git a/src/Storages/MergeTree/MergeTreeIndexFullText.cpp b/src/Storages/MergeTree/MergeTreeIndexFullText.cpp index 61ea5987e76..8aa0c65e664 100644 --- a/src/Storages/MergeTree/MergeTreeIndexFullText.cpp +++ b/src/Storages/MergeTree/MergeTreeIndexFullText.cpp @@ -42,7 +42,7 @@ namespace ErrorCodes /// Adds all tokens from string to bloom filter. static void stringToBloomFilter( - const char * data, size_t size, const std::unique_ptr & token_extractor, BloomFilter & bloom_filter) + const char * data, size_t size, TokenExtractorPtr token_extractor, BloomFilter & bloom_filter) { size_t cur = 0; size_t token_start = 0; @@ -53,7 +53,7 @@ static void stringToBloomFilter( /// Adds all tokens from like pattern string to bloom filter. (Because like pattern can contain `\%` and `\_`.) static void likeStringToBloomFilter( - const String & data, const std::unique_ptr & token_extractor, BloomFilter & bloom_filter) + const String & data, TokenExtractorPtr token_extractor, BloomFilter & bloom_filter) { size_t cur = 0; String token; @@ -61,48 +61,67 @@ static void likeStringToBloomFilter( bloom_filter.add(token.c_str(), token.size()); } /// Unified condition for equals, startsWith and endsWith -bool MergeTreeConditionFullText::createFunctionEqualsCondition(RPNElement & out, const Field & value, const MergeTreeIndexFullText & idx) +bool MergeTreeConditionFullText::createFunctionEqualsCondition(RPNElement & out, const Field & value, const BloomFilterParameters & params, TokenExtractorPtr token_extractor) { out.function = RPNElement::FUNCTION_EQUALS; - out.bloom_filter = std::make_unique( - idx.bloom_filter_size, idx.bloom_filter_hashes, idx.seed); + out.bloom_filter = std::make_unique(params); const auto & str = value.get(); - stringToBloomFilter(str.c_str(), str.size(), idx.token_extractor_func, *out.bloom_filter); + stringToBloomFilter(str.c_str(), str.size(), token_extractor, *out.bloom_filter); return true; } -MergeTreeIndexGranuleFullText::MergeTreeIndexGranuleFullText(const MergeTreeIndexFullText & index_) - : index(index_) +MergeTreeIndexGranuleFullText::MergeTreeIndexGranuleFullText( + const String & index_name_, + size_t columns_number, + const BloomFilterParameters & params_) + : index_name(index_name_) + , params(params_) , bloom_filters( - index.columns.size(), BloomFilter(index.bloom_filter_size, index.bloom_filter_hashes, index.seed)) - , has_elems(false) {} + columns_number, BloomFilter(params)) + , has_elems(false) +{ +} void MergeTreeIndexGranuleFullText::serializeBinary(WriteBuffer & ostr) const { if (empty()) - throw Exception("Attempt to write empty minmax index " + backQuote(index.name), ErrorCodes::LOGICAL_ERROR); + throw Exception("Attempt to write empty fulltext index " + backQuote(index_name), ErrorCodes::LOGICAL_ERROR); for (const auto & bloom_filter : bloom_filters) - ostr.write(reinterpret_cast(bloom_filter.getFilter().data()), index.bloom_filter_size); + ostr.write(reinterpret_cast(bloom_filter.getFilter().data()), params.filter_size); } void MergeTreeIndexGranuleFullText::deserializeBinary(ReadBuffer & istr) { for (auto & bloom_filter : bloom_filters) { - istr.read(reinterpret_cast(bloom_filter.getFilter().data()), index.bloom_filter_size); + istr.read(reinterpret_cast( + bloom_filter.getFilter().data()), params.filter_size); } has_elems = true; } -MergeTreeIndexAggregatorFullText::MergeTreeIndexAggregatorFullText(const MergeTreeIndexFullText & index_) - : index(index_), granule(std::make_shared(index)) {} +MergeTreeIndexAggregatorFullText::MergeTreeIndexAggregatorFullText( + const Names & index_columns_, + const String & index_name_, + const BloomFilterParameters & params_, + TokenExtractorPtr token_extractor_) + : index_columns(index_columns_) + , index_name (index_name_) + , params(params_) + , token_extractor(token_extractor_) + , granule( + std::make_shared( + index_name, index_columns.size(), params)) +{ +} MergeTreeIndexGranulePtr MergeTreeIndexAggregatorFullText::getGranuleAndReset() { - auto new_granule = std::make_shared(index); + auto new_granule = std::make_shared( + index_name, index_columns.size(), params); new_granule.swap(granule); return new_granule; } @@ -116,13 +135,13 @@ void MergeTreeIndexAggregatorFullText::update(const Block & block, size_t * pos, size_t rows_read = std::min(limit, block.rows() - *pos); - for (size_t col = 0; col < index.columns.size(); ++col) + for (size_t col = 0; col < index_columns.size(); ++col) { - const auto & column = block.getByName(index.columns[col]).column; + const auto & column = block.getByName(index_columns[col]).column; for (size_t i = 0; i < rows_read; ++i) { auto ref = column->getDataAt(*pos + i); - stringToBloomFilter(ref.data, ref.size, index.token_extractor_func, granule->bloom_filters[col]); + stringToBloomFilter(ref.data, ref.size, token_extractor, granule->bloom_filters[col]); } } granule->has_elems = true; @@ -133,7 +152,14 @@ void MergeTreeIndexAggregatorFullText::update(const Block & block, size_t * pos, MergeTreeConditionFullText::MergeTreeConditionFullText( const SelectQueryInfo & query_info, const Context & context, - const MergeTreeIndexFullText & index_) : index(index_), prepared_sets(query_info.sets) + const Block & index_sample_block, + const BloomFilterParameters & params_, + TokenExtractorPtr token_extactor_) + : index_columns(index_sample_block.getNames()) + , index_data_types(index_sample_block.getNamesAndTypesList().getTypes()) + , params(params_) + , token_extractor(token_extactor_) + , prepared_sets(query_info.sets) { rpn = std::move( RPNBuilder( @@ -283,11 +309,11 @@ bool MergeTreeConditionFullText::mayBeTrueOnGranule(MergeTreeIndexGranulePtr idx bool MergeTreeConditionFullText::getKey(const ASTPtr & node, size_t & key_column_num) { - auto it = std::find(index.columns.begin(), index.columns.end(), node->getColumnName()); - if (it == index.columns.end()) + auto it = std::find(index_columns.begin(), index_columns.end(), node->getColumnName()); + if (it == index_columns.end()) return false; - key_column_num = static_cast(it - index.columns.begin()); + key_column_num = static_cast(it - index_columns.begin()); return true; } @@ -331,67 +357,63 @@ bool MergeTreeConditionFullText::atomFromAST( if (key_arg_pos == 1 && (func_name != "equals" || func_name != "notEquals")) return false; - else if (!index.token_extractor_func->supportLike() && (func_name == "like" || func_name == "notLike")) + else if (!token_extractor->supportLike() && (func_name == "like" || func_name == "notLike")) return false; if (func_name == "notEquals") { out.key_column = key_column_num; out.function = RPNElement::FUNCTION_NOT_EQUALS; - out.bloom_filter = std::make_unique( - index.bloom_filter_size, index.bloom_filter_hashes, index.seed); + out.bloom_filter = std::make_unique(params); const auto & str = const_value.get(); - stringToBloomFilter(str.c_str(), str.size(), index.token_extractor_func, *out.bloom_filter); + stringToBloomFilter(str.c_str(), str.size(), token_extractor, *out.bloom_filter); return true; } else if (func_name == "equals") { out.key_column = key_column_num; - return createFunctionEqualsCondition(out, const_value, index); + return createFunctionEqualsCondition(out, const_value, params, token_extractor); } else if (func_name == "like") { out.key_column = key_column_num; out.function = RPNElement::FUNCTION_EQUALS; - out.bloom_filter = std::make_unique( - index.bloom_filter_size, index.bloom_filter_hashes, index.seed); + out.bloom_filter = std::make_unique(params); const auto & str = const_value.get(); - likeStringToBloomFilter(str, index.token_extractor_func, *out.bloom_filter); + likeStringToBloomFilter(str, token_extractor, *out.bloom_filter); return true; } else if (func_name == "notLike") { out.key_column = key_column_num; out.function = RPNElement::FUNCTION_NOT_EQUALS; - out.bloom_filter = std::make_unique( - index.bloom_filter_size, index.bloom_filter_hashes, index.seed); + out.bloom_filter = std::make_unique(params); const auto & str = const_value.get(); - likeStringToBloomFilter(str, index.token_extractor_func, *out.bloom_filter); + likeStringToBloomFilter(str, token_extractor, *out.bloom_filter); return true; } else if (func_name == "hasToken") { out.key_column = key_column_num; out.function = RPNElement::FUNCTION_EQUALS; - out.bloom_filter = std::make_unique( - index.bloom_filter_size, index.bloom_filter_hashes, index.seed); + out.bloom_filter = std::make_unique(params); const auto & str = const_value.get(); - stringToBloomFilter(str.c_str(), str.size(), index.token_extractor_func, *out.bloom_filter); + stringToBloomFilter(str.c_str(), str.size(), token_extractor, *out.bloom_filter); return true; } else if (func_name == "startsWith") { out.key_column = key_column_num; - return createFunctionEqualsCondition(out, const_value, index); + return createFunctionEqualsCondition(out, const_value, params, token_extractor); } else if (func_name == "endsWith") { out.key_column = key_column_num; - return createFunctionEqualsCondition(out, const_value, index); + return createFunctionEqualsCondition(out, const_value, params, token_extractor); } else if (func_name == "multiSearchAny") { @@ -406,9 +428,9 @@ bool MergeTreeConditionFullText::atomFromAST( if (element.getType() != Field::Types::String) return false; - bloom_filters.back().emplace_back(index.bloom_filter_size, index.bloom_filter_hashes, index.seed); + bloom_filters.back().emplace_back(params); const auto & str = element.get(); - stringToBloomFilter(str.c_str(), str.size(), index.token_extractor_func, bloom_filters.back().back()); + stringToBloomFilter(str.c_str(), str.size(), token_extractor, bloom_filters.back().back()); } out.set_bloom_filters = std::move(bloom_filters); return true; @@ -467,7 +489,7 @@ bool MergeTreeConditionFullText::tryPrepareSetBloomFilter( if (getKey(tuple_elements[i], key)) { key_tuple_mapping.emplace_back(i, key); - data_types.push_back(index.data_types[key]); + data_types.push_back(index_data_types[key]); } } } @@ -477,7 +499,7 @@ bool MergeTreeConditionFullText::tryPrepareSetBloomFilter( if (getKey(left_arg, key)) { key_tuple_mapping.emplace_back(0, key); - data_types.push_back(index.data_types[key]); + data_types.push_back(index_data_types[key]); } } @@ -515,9 +537,9 @@ bool MergeTreeConditionFullText::tryPrepareSetBloomFilter( const auto & column = columns[tuple_idx]; for (size_t row = 0; row < prepared_set->getTotalRowCount(); ++row) { - bloom_filters.back().emplace_back(index.bloom_filter_size, index.bloom_filter_hashes, index.seed); + bloom_filters.back().emplace_back(params); auto ref = column->getDataAt(row); - stringToBloomFilter(ref.data, ref.size, index.token_extractor_func, bloom_filters.back().back()); + stringToBloomFilter(ref.data, ref.size, token_extractor, bloom_filters.back().back()); } } @@ -529,23 +551,23 @@ bool MergeTreeConditionFullText::tryPrepareSetBloomFilter( MergeTreeIndexGranulePtr MergeTreeIndexFullText::createIndexGranule() const { - return std::make_shared(*this); + return std::make_shared(index.name, index.column_names.size(), params); } MergeTreeIndexAggregatorPtr MergeTreeIndexFullText::createIndexAggregator() const { - return std::make_shared(*this); + return std::make_shared(index.column_names, index.name, params, token_extractor.get()); } MergeTreeIndexConditionPtr MergeTreeIndexFullText::createIndexCondition( const SelectQueryInfo & query, const Context & context) const { - return std::make_shared(query, context, *this); + return std::make_shared(query, context, index.sample_block, params, token_extractor.get()); }; bool MergeTreeIndexFullText::mayBenefitFromIndexForIn(const ASTPtr & node) const { - return std::find(std::cbegin(columns), std::cend(columns), node->getColumnName()) != std::cend(columns); + return std::find(std::cbegin(index.column_names), std::cend(index.column_names), node->getColumnName()) != std::cend(index.column_names); } @@ -746,80 +768,63 @@ bool SplitTokenExtractor::nextLike(const String & str, size_t * pos, String & to } -std::unique_ptr bloomFilterIndexCreator( - const NamesAndTypesList & new_columns, - std::shared_ptr node, - const Context & context, - bool /*attach*/) +MergeTreeIndexPtr bloomFilterIndexCreator( + const IndexDescription & index) { - if (node->name.empty()) - throw Exception("Index must have unique name", ErrorCodes::INCORRECT_QUERY); - - ASTPtr expr_list = MergeTreeData::extractKeyExpressionList(node->expr->clone()); - - auto syntax = SyntaxAnalyzer(context).analyze(expr_list, new_columns); - auto index_expr = ExpressionAnalyzer(expr_list, syntax, context).getActions(false); - - auto sample = ExpressionAnalyzer(expr_list, syntax, context) - .getActions(true)->getSampleBlock(); - - Names columns; - DataTypes data_types; - - for (size_t i = 0; i < expr_list->children.size(); ++i) + if (index.type == NgramTokenExtractor::getName()) { - const auto & column = sample.getByPosition(i); - - columns.emplace_back(column.name); - data_types.emplace_back(column.type); - - if (data_types.back()->getTypeId() != TypeIndex::String - && data_types.back()->getTypeId() != TypeIndex::FixedString) - throw Exception("Bloom filter index can be used only with `String` or `FixedString` column.", ErrorCodes::INCORRECT_QUERY); - } - - boost::algorithm::to_lower(node->type->name); - if (node->type->name == NgramTokenExtractor::getName()) - { - if (!node->type->arguments || node->type->arguments->children.size() != 4) - throw Exception("`ngrambf` index must have exactly 4 arguments.", ErrorCodes::INCORRECT_QUERY); - - size_t n = typeid_cast( - *node->type->arguments->children[0]).value.get(); - size_t bloom_filter_size = typeid_cast( - *node->type->arguments->children[1]).value.get(); - size_t bloom_filter_hashes = typeid_cast( - *node->type->arguments->children[2]).value.get(); - size_t seed = typeid_cast( - *node->type->arguments->children[3]).value.get(); + size_t n = index.arguments[0].get(); + BloomFilterParameters params + { + .filter_size = index.arguments[1].get(), + .filter_hashes = index.arguments[2].get(), + .seed = index.arguments[3].get(), + }; auto tokenizer = std::make_unique(n); - return std::make_unique( - node->name, std::move(index_expr), columns, data_types, sample, node->granularity, - bloom_filter_size, bloom_filter_hashes, seed, std::move(tokenizer)); + return std::make_shared(index, params, std::move(tokenizer)); } - else if (node->type->name == SplitTokenExtractor::getName()) + else if (index.type == SplitTokenExtractor::getName()) { - if (!node->type->arguments || node->type->arguments->children.size() != 3) - throw Exception("`tokenbf` index must have exactly 3 arguments.", ErrorCodes::INCORRECT_QUERY); - - size_t bloom_filter_size = typeid_cast( - *node->type->arguments->children[0]).value.get(); - size_t bloom_filter_hashes = typeid_cast( - *node->type->arguments->children[1]).value.get(); - size_t seed = typeid_cast( - *node->type->arguments->children[2]).value.get(); + BloomFilterParameters params + { + .filter_size = index.arguments[0].get(), + .filter_hashes = index.arguments[1].get(), + .seed = index.arguments[2].get(), + }; auto tokenizer = std::make_unique(); - return std::make_unique( - node->name, std::move(index_expr), columns, data_types, sample, node->granularity, - bloom_filter_size, bloom_filter_hashes, seed, std::move(tokenizer)); + return std::make_shared(index, params, std::move(tokenizer)); } else { - throw Exception("Unknown index type: " + backQuote(node->name), ErrorCodes::LOGICAL_ERROR); + throw Exception("Unknown index type: " + backQuote(index.name), ErrorCodes::LOGICAL_ERROR); + } +} + +void bloomFilterIndexValidator(const IndexDescription & index, bool /*attach*/) +{ + for (const auto & data_type : index.data_types) + { + if (data_type->getTypeId() != TypeIndex::String && data_type->getTypeId() != TypeIndex::FixedString) + throw Exception("Bloom filter index can be used only with `String` or `FixedString` column.", ErrorCodes::INCORRECT_QUERY); + } + + if (index.type == NgramTokenExtractor::getName()) + { + if (index.arguments.size() != 4) + throw Exception("`ngrambf` index must have exactly 4 arguments.", ErrorCodes::INCORRECT_QUERY); + } + else if (index.type == SplitTokenExtractor::getName()) + { + if (index.arguments.size() != 3) + throw Exception("`tokenbf` index must have exactly 3 arguments.", ErrorCodes::INCORRECT_QUERY); + } + else + { + throw Exception("Unknown index type: " + backQuote(index.name), ErrorCodes::LOGICAL_ERROR); } } diff --git a/src/Storages/MergeTree/MergeTreeIndexFullText.h b/src/Storages/MergeTree/MergeTreeIndexFullText.h index 1e7f2753a93..c3c1ff8de8b 100644 --- a/src/Storages/MergeTree/MergeTreeIndexFullText.h +++ b/src/Storages/MergeTree/MergeTreeIndexFullText.h @@ -10,13 +10,29 @@ namespace DB { -class MergeTreeIndexFullText; +/// Interface for string parsers. +struct ITokenExtractor +{ + virtual ~ITokenExtractor() = default; + /// Fast inplace implementation for regular use. + /// Gets string (data ptr and len) and start position for extracting next token (state of extractor). + /// Returns false if parsing is finished, otherwise returns true. + virtual bool next(const char * data, size_t len, size_t * pos, size_t * token_start, size_t * token_len) const = 0; + /// Special implementation for creating bloom filter for LIKE function. + /// It skips unescaped `%` and `_` and supports escaping symbols, but it is less lightweight. + virtual bool nextLike(const String & str, size_t * pos, String & out) const = 0; + virtual bool supportLike() const = 0; +}; + +using TokenExtractorPtr = const ITokenExtractor *; struct MergeTreeIndexGranuleFullText : public IMergeTreeIndexGranule { explicit MergeTreeIndexGranuleFullText( - const MergeTreeIndexFullText & index_); + const String & index_name_, + size_t columns_number, + const BloomFilterParameters & params_); ~MergeTreeIndexGranuleFullText() override = default; @@ -25,17 +41,22 @@ struct MergeTreeIndexGranuleFullText : public IMergeTreeIndexGranule bool empty() const override { return !has_elems; } - const MergeTreeIndexFullText & index; + String index_name; + BloomFilterParameters params; + std::vector bloom_filters; bool has_elems; }; using MergeTreeIndexGranuleFullTextPtr = std::shared_ptr; - struct MergeTreeIndexAggregatorFullText : IMergeTreeIndexAggregator { - explicit MergeTreeIndexAggregatorFullText(const MergeTreeIndexFullText & index); + explicit MergeTreeIndexAggregatorFullText( + const Names & index_columns_, + const String & index_name_, + const BloomFilterParameters & params_, + TokenExtractorPtr token_extractor_); ~MergeTreeIndexAggregatorFullText() override = default; @@ -44,7 +65,11 @@ struct MergeTreeIndexAggregatorFullText : IMergeTreeIndexAggregator void update(const Block & block, size_t * pos, size_t limit) override; - const MergeTreeIndexFullText & index; + Names index_columns; + String index_name; + BloomFilterParameters params; + TokenExtractorPtr token_extractor; + MergeTreeIndexGranuleFullTextPtr granule; }; @@ -55,7 +80,9 @@ public: MergeTreeConditionFullText( const SelectQueryInfo & query_info, const Context & context, - const MergeTreeIndexFullText & index_); + const Block & index_sample_block, + const BloomFilterParameters & params_, + TokenExtractorPtr token_extactor_); ~MergeTreeConditionFullText() override = default; @@ -115,30 +142,19 @@ private: bool getKey(const ASTPtr & node, size_t & key_column_num); bool tryPrepareSetBloomFilter(const ASTs & args, RPNElement & out); - static bool createFunctionEqualsCondition(RPNElement & out, const Field & value, const MergeTreeIndexFullText & idx); + static bool createFunctionEqualsCondition( + RPNElement & out, const Field & value, const BloomFilterParameters & params, TokenExtractorPtr token_extractor); - const MergeTreeIndexFullText & index; + Names index_columns; + DataTypes index_data_types; + BloomFilterParameters params; + TokenExtractorPtr token_extractor; RPN rpn; /// Sets from syntax analyzer. PreparedSets prepared_sets; }; -/// Interface for string parsers. -struct ITokenExtractor -{ - virtual ~ITokenExtractor() = default; - /// Fast inplace implementation for regular use. - /// Gets string (data ptr and len) and start position for extracting next token (state of extractor). - /// Returns false if parsing is finished, otherwise returns true. - virtual bool next(const char * data, size_t len, size_t * pos, size_t * token_start, size_t * token_len) const = 0; - /// Special implementation for creating bloom filter for LIKE function. - /// It skips unescaped `%` and `_` and supports escaping symbols, but it is less lightweight. - virtual bool nextLike(const String & str, size_t * pos, String & out) const = 0; - - virtual bool supportLike() const = 0; -}; - /// Parser extracting all ngrams from string. struct NgramTokenExtractor : public ITokenExtractor { @@ -170,21 +186,12 @@ class MergeTreeIndexFullText : public IMergeTreeIndex { public: MergeTreeIndexFullText( - String name_, - ExpressionActionsPtr expr_, - const Names & columns_, - const DataTypes & data_types_, - const Block & header_, - size_t granularity_, - size_t bloom_filter_size_, - size_t bloom_filter_hashes_, - size_t seed_, - std::unique_ptr && token_extractor_func_) - : IMergeTreeIndex(name_, expr_, columns_, data_types_, header_, granularity_) - , bloom_filter_size(bloom_filter_size_) - , bloom_filter_hashes(bloom_filter_hashes_) - , seed(seed_) - , token_extractor_func(std::move(token_extractor_func_)) {} + const IndexDescription & index_, + const BloomFilterParameters & params_, + std::unique_ptr && token_extractor_) + : IMergeTreeIndex(index_) + , params(params_) + , token_extractor(std::move(token_extractor_)) {} ~MergeTreeIndexFullText() override = default; @@ -196,14 +203,9 @@ public: bool mayBenefitFromIndexForIn(const ASTPtr & node) const override; - /// Bloom filter size in bytes. - size_t bloom_filter_size; - /// Number of bloom filter hash functions. - size_t bloom_filter_hashes; - /// Bloom filter seed. - size_t seed; + BloomFilterParameters params; /// Function for selecting next token. - std::unique_ptr token_extractor_func; + std::unique_ptr token_extractor; }; } diff --git a/src/Storages/MergeTree/MergeTreeIndexMinMax.cpp b/src/Storages/MergeTree/MergeTreeIndexMinMax.cpp index bbfd5776f72..44973a984a3 100644 --- a/src/Storages/MergeTree/MergeTreeIndexMinMax.cpp +++ b/src/Storages/MergeTree/MergeTreeIndexMinMax.cpp @@ -12,26 +12,31 @@ namespace DB namespace ErrorCodes { extern const int LOGICAL_ERROR; - extern const int INCORRECT_QUERY; } -MergeTreeIndexGranuleMinMax::MergeTreeIndexGranuleMinMax(const MergeTreeIndexMinMax & index_) - : index(index_) {} +MergeTreeIndexGranuleMinMax::MergeTreeIndexGranuleMinMax(const String & index_name_, const Block & index_sample_block_) + : index_name(index_name_) + , index_sample_block(index_sample_block_) +{} MergeTreeIndexGranuleMinMax::MergeTreeIndexGranuleMinMax( - const MergeTreeIndexMinMax & index_, std::vector && hyperrectangle_) - : index(index_), hyperrectangle(std::move(hyperrectangle_)) {} + const String & index_name_, + const Block & index_sample_block_, + std::vector && hyperrectangle_) + : index_name(index_name_) + , index_sample_block(index_sample_block_) + , hyperrectangle(std::move(hyperrectangle_)) {} void MergeTreeIndexGranuleMinMax::serializeBinary(WriteBuffer & ostr) const { if (empty()) throw Exception( - "Attempt to write empty minmax index " + backQuote(index.name), ErrorCodes::LOGICAL_ERROR); + "Attempt to write empty minmax index " + backQuote(index_name), ErrorCodes::LOGICAL_ERROR); - for (size_t i = 0; i < index.columns.size(); ++i) + for (size_t i = 0; i < index_sample_block.columns(); ++i) { - const DataTypePtr & type = index.data_types[i]; + const DataTypePtr & type = index_sample_block.getByPosition(i).type; if (!type->isNullable()) { type->serializeBinary(hyperrectangle[i].left, ostr); @@ -55,9 +60,9 @@ void MergeTreeIndexGranuleMinMax::deserializeBinary(ReadBuffer & istr) hyperrectangle.clear(); Field min_val; Field max_val; - for (size_t i = 0; i < index.columns.size(); ++i) + for (size_t i = 0; i < index_sample_block.columns(); ++i) { - const DataTypePtr & type = index.data_types[i]; + const DataTypePtr & type = index_sample_block.getByPosition(i).type; if (!type->isNullable()) { type->deserializeBinary(min_val, istr); @@ -82,13 +87,14 @@ void MergeTreeIndexGranuleMinMax::deserializeBinary(ReadBuffer & istr) } } - -MergeTreeIndexAggregatorMinMax::MergeTreeIndexAggregatorMinMax(const MergeTreeIndexMinMax & index_) - : index(index_) {} +MergeTreeIndexAggregatorMinMax::MergeTreeIndexAggregatorMinMax(const String & index_name_, const Block & index_sample_block_) + : index_name(index_name_) + , index_sample_block(index_sample_block_) +{} MergeTreeIndexGranulePtr MergeTreeIndexAggregatorMinMax::getGranuleAndReset() { - return std::make_shared(index, std::move(hyperrectangle)); + return std::make_shared(index_name, index_sample_block, std::move(hyperrectangle)); } void MergeTreeIndexAggregatorMinMax::update(const Block & block, size_t * pos, size_t limit) @@ -102,9 +108,10 @@ void MergeTreeIndexAggregatorMinMax::update(const Block & block, size_t * pos, s FieldRef field_min; FieldRef field_max; - for (size_t i = 0; i < index.columns.size(); ++i) + for (size_t i = 0; i < index_sample_block.columns(); ++i) { - const auto & column = block.getByName(index.columns[i]).column; + auto index_column_name = index_sample_block.getByPosition(i).name; + const auto & column = block.getByName(index_column_name).column; column->cut(*pos, rows_read)->getExtremes(field_min, field_max); if (hyperrectangle.size() <= i) @@ -123,10 +130,13 @@ void MergeTreeIndexAggregatorMinMax::update(const Block & block, size_t * pos, s MergeTreeIndexConditionMinMax::MergeTreeIndexConditionMinMax( - const SelectQueryInfo &query, - const Context &context, - const MergeTreeIndexMinMax &index_) - : index(index_), condition(query, context, index.columns, index.expr) {} + const IndexDescription & index, + const SelectQueryInfo & query, + const Context & context) + : index_data_types(index.data_types) + , condition(query, context, index.column_names, index.expression) +{ +} bool MergeTreeIndexConditionMinMax::alwaysUnknownOrTrue() const { @@ -143,33 +153,32 @@ bool MergeTreeIndexConditionMinMax::mayBeTrueOnGranule(MergeTreeIndexGranulePtr for (const auto & range : granule->hyperrectangle) if (range.left.isNull() || range.right.isNull()) return true; - return condition.checkInHyperrectangle(granule->hyperrectangle, index.data_types).can_be_true; + return condition.checkInHyperrectangle(granule->hyperrectangle, index_data_types).can_be_true; } MergeTreeIndexGranulePtr MergeTreeIndexMinMax::createIndexGranule() const { - return std::make_shared(*this); + return std::make_shared(index.name, index.sample_block); } MergeTreeIndexAggregatorPtr MergeTreeIndexMinMax::createIndexAggregator() const { - return std::make_shared(*this); + return std::make_shared(index.name, index.sample_block); } - MergeTreeIndexConditionPtr MergeTreeIndexMinMax::createIndexCondition( const SelectQueryInfo & query, const Context & context) const { - return std::make_shared(query, context, *this); + return std::make_shared(index, query, context); }; bool MergeTreeIndexMinMax::mayBenefitFromIndexForIn(const ASTPtr & node) const { const String column_name = node->getColumnName(); - for (const auto & cname : columns) + for (const auto & cname : index.column_names) if (column_name == cname) return true; @@ -180,38 +189,13 @@ bool MergeTreeIndexMinMax::mayBenefitFromIndexForIn(const ASTPtr & node) const return false; } -std::unique_ptr minmaxIndexCreator( - const NamesAndTypesList & new_columns, - std::shared_ptr node, - const Context & context, - bool /*attach*/) +MergeTreeIndexPtr minmaxIndexCreator( + const IndexDescription & index) { - if (node->name.empty()) - throw Exception("Index must have unique name", ErrorCodes::INCORRECT_QUERY); - - if (node->type->arguments) - throw Exception("Minmax index have not any arguments", ErrorCodes::INCORRECT_QUERY); - - ASTPtr expr_list = MergeTreeData::extractKeyExpressionList(node->expr->clone()); - auto syntax = SyntaxAnalyzer(context).analyze(expr_list, new_columns); - auto minmax_expr = ExpressionAnalyzer(expr_list, syntax, context).getActions(false); - - auto sample = ExpressionAnalyzer(expr_list, syntax, context) - .getActions(true)->getSampleBlock(); - - Names columns; - DataTypes data_types; - - for (size_t i = 0; i < expr_list->children.size(); ++i) - { - const auto & column = sample.getByPosition(i); - - columns.emplace_back(column.name); - data_types.emplace_back(column.type); - } - - return std::make_unique( - node->name, std::move(minmax_expr), columns, data_types, sample, node->granularity); + return std::make_shared(index); } +void minmaxIndexValidator(const IndexDescription & /* index */, bool /* attach */) +{ +} } diff --git a/src/Storages/MergeTree/MergeTreeIndexMinMax.h b/src/Storages/MergeTree/MergeTreeIndexMinMax.h index 835f69de2d9..3956b1d9f9a 100644 --- a/src/Storages/MergeTree/MergeTreeIndexMinMax.h +++ b/src/Storages/MergeTree/MergeTreeIndexMinMax.h @@ -10,13 +10,14 @@ namespace DB { -class MergeTreeIndexMinMax; - - struct MergeTreeIndexGranuleMinMax : public IMergeTreeIndexGranule { - explicit MergeTreeIndexGranuleMinMax(const MergeTreeIndexMinMax & index_); - MergeTreeIndexGranuleMinMax(const MergeTreeIndexMinMax & index_, std::vector && hyperrectangle_); + MergeTreeIndexGranuleMinMax(const String & index_name_, const Block & index_sample_block_); + MergeTreeIndexGranuleMinMax( + const String & index_name_, + const Block & index_sample_block_, + std::vector && hyperrectangle_); + ~MergeTreeIndexGranuleMinMax() override = default; void serializeBinary(WriteBuffer & ostr) const override; @@ -24,21 +25,23 @@ struct MergeTreeIndexGranuleMinMax : public IMergeTreeIndexGranule bool empty() const override { return hyperrectangle.empty(); } - const MergeTreeIndexMinMax & index; + String index_name; + Block index_sample_block; std::vector hyperrectangle; }; struct MergeTreeIndexAggregatorMinMax : IMergeTreeIndexAggregator { - explicit MergeTreeIndexAggregatorMinMax(const MergeTreeIndexMinMax & index); + MergeTreeIndexAggregatorMinMax(const String & index_name_, const Block & index_sample_block); ~MergeTreeIndexAggregatorMinMax() override = default; bool empty() const override { return hyperrectangle.empty(); } MergeTreeIndexGranulePtr getGranuleAndReset() override; void update(const Block & block, size_t * pos, size_t limit) override; - const MergeTreeIndexMinMax & index; + String index_name; + Block index_sample_block; std::vector hyperrectangle; }; @@ -47,9 +50,9 @@ class MergeTreeIndexConditionMinMax : public IMergeTreeIndexCondition { public: MergeTreeIndexConditionMinMax( + const IndexDescription & index, const SelectQueryInfo & query, - const Context & context, - const MergeTreeIndexMinMax & index); + const Context & context); bool alwaysUnknownOrTrue() const override; @@ -57,7 +60,7 @@ public: ~MergeTreeIndexConditionMinMax() override = default; private: - const MergeTreeIndexMinMax & index; + DataTypes index_data_types; KeyCondition condition; }; @@ -65,14 +68,9 @@ private: class MergeTreeIndexMinMax : public IMergeTreeIndex { public: - MergeTreeIndexMinMax( - String name_, - ExpressionActionsPtr expr_, - const Names & columns_, - const DataTypes & data_types_, - const Block & header_, - size_t granularity_) - : IMergeTreeIndex(name_, expr_, columns_, data_types_, header_, granularity_) {} + MergeTreeIndexMinMax(const IndexDescription & index_) + : IMergeTreeIndex(index_) + {} ~MergeTreeIndexMinMax() override = default; diff --git a/src/Storages/MergeTree/MergeTreeIndexSet.cpp b/src/Storages/MergeTree/MergeTreeIndexSet.cpp index ce2f6975e68..221be222fd2 100644 --- a/src/Storages/MergeTree/MergeTreeIndexSet.cpp +++ b/src/Storages/MergeTree/MergeTreeIndexSet.cpp @@ -22,24 +22,38 @@ namespace ErrorCodes static const Field UNKNOWN_FIELD(3u); -MergeTreeIndexGranuleSet::MergeTreeIndexGranuleSet(const MergeTreeIndexSet & index_) - : index(index_) - , block(index.header.cloneEmpty()) {} +MergeTreeIndexGranuleSet::MergeTreeIndexGranuleSet( + const String & index_name_, + const Block & index_sample_block_, + size_t max_rows_) + : index_name(index_name_) + , max_rows(max_rows_) + , index_sample_block(index_sample_block_) + , block(index_sample_block) +{ +} MergeTreeIndexGranuleSet::MergeTreeIndexGranuleSet( - const MergeTreeIndexSet & index_, MutableColumns && mutable_columns_) - : index(index_) - , block(index.header.cloneWithColumns(std::move(mutable_columns_))) {} + const String & index_name_, + const Block & index_sample_block_, + size_t max_rows_, + MutableColumns && mutable_columns_) + : index_name(index_name_) + , max_rows(max_rows_) + , index_sample_block(index_sample_block_) + , block(index_sample_block.cloneWithColumns(std::move(mutable_columns_))) +{ +} void MergeTreeIndexGranuleSet::serializeBinary(WriteBuffer & ostr) const { if (empty()) throw Exception( - "Attempt to write empty set index " + backQuote(index.name), ErrorCodes::LOGICAL_ERROR); + "Attempt to write empty set index " + backQuote(index_name), ErrorCodes::LOGICAL_ERROR); const auto & size_type = DataTypePtr(std::make_shared()); - if (index.max_rows && size() > index.max_rows) + if (max_rows != 0 && size() > max_rows) { size_type->serializeBinary(0, ostr); return; @@ -47,9 +61,9 @@ void MergeTreeIndexGranuleSet::serializeBinary(WriteBuffer & ostr) const size_type->serializeBinary(size(), ostr); - for (size_t i = 0; i < index.columns.size(); ++i) + for (size_t i = 0; i < index_sample_block.columns(); ++i) { - const auto & type = index.data_types[i]; + const auto & type = index_sample_block.getByPosition(i).type; IDataType::SerializeBinaryBulkSettings settings; settings.getter = [&ostr](IDataType::SubstreamPath) -> WriteBuffer * { return &ostr; }; @@ -75,9 +89,10 @@ void MergeTreeIndexGranuleSet::deserializeBinary(ReadBuffer & istr) if (rows_to_read == 0) return; - for (size_t i = 0; i < index.columns.size(); ++i) + for (size_t i = 0; i < index_sample_block.columns(); ++i) { - const auto & type = index.data_types[i]; + const auto & column = index_sample_block.getByPosition(i); + const auto & type = column.type; auto new_column = type->createColumn(); IDataType::DeserializeBinaryBulkSettings settings; @@ -88,18 +103,21 @@ void MergeTreeIndexGranuleSet::deserializeBinary(ReadBuffer & istr) type->deserializeBinaryBulkStatePrefix(settings, state); type->deserializeBinaryBulkWithMultipleStreams(*new_column, rows_to_read, settings, state); - block.insert(ColumnWithTypeAndName(new_column->getPtr(), type, index.columns[i])); + block.insert(ColumnWithTypeAndName(new_column->getPtr(), type, column.name)); } } -MergeTreeIndexAggregatorSet::MergeTreeIndexAggregatorSet(const MergeTreeIndexSet & index_) - : index(index_), columns(index.header.cloneEmptyColumns()) +MergeTreeIndexAggregatorSet::MergeTreeIndexAggregatorSet(const String & index_name_, const Block & index_sample_block_, size_t max_rows_) + : index_name(index_name_) + , max_rows(max_rows_) + , index_sample_block(index_sample_block_) + , columns(index_sample_block_.cloneEmptyColumns()) { ColumnRawPtrs column_ptrs; - column_ptrs.reserve(index.columns.size()); + column_ptrs.reserve(index_sample_block.columns()); Columns materialized_columns; - for (const auto & column : index.header.getColumns()) + for (const auto & column : index_sample_block.getColumns()) { materialized_columns.emplace_back(column->convertToFullColumnIfConst()->convertToFullColumnIfLowCardinality()); column_ptrs.emplace_back(materialized_columns.back().get()); @@ -107,7 +125,7 @@ MergeTreeIndexAggregatorSet::MergeTreeIndexAggregatorSet(const MergeTreeIndexSet data.init(ClearableSetVariants::chooseMethod(column_ptrs, key_sizes)); - columns = index.header.cloneEmptyColumns(); + columns = index_sample_block.cloneEmptyColumns(); } void MergeTreeIndexAggregatorSet::update(const Block & block, size_t * pos, size_t limit) @@ -119,16 +137,17 @@ void MergeTreeIndexAggregatorSet::update(const Block & block, size_t * pos, size size_t rows_read = std::min(limit, block.rows() - *pos); - if (index.max_rows && size() > index.max_rows) + if (max_rows && size() > max_rows) { *pos += rows_read; return; } ColumnRawPtrs index_column_ptrs; - index_column_ptrs.reserve(index.columns.size()); + index_column_ptrs.reserve(index_sample_block.columns()); Columns materialized_columns; - for (const auto & column_name : index.columns) + const Names index_columns = index_sample_block.getNames(); + for (const auto & column_name : index_columns) { materialized_columns.emplace_back( block.getByName(column_name).column->convertToFullColumnIfConst()->convertToFullColumnIfLowCardinality()); @@ -154,7 +173,7 @@ void MergeTreeIndexAggregatorSet::update(const Block & block, size_t * pos, size { for (size_t i = 0; i < columns.size(); ++i) { - auto filtered_column = block.getByName(index.columns[i]).column->filter(filter, block.rows()); + auto filtered_column = block.getByName(index_columns[i]).column->filter(filter, block.rows()); columns[i]->insertRangeFrom(*filtered_column, 0, filtered_column->size()); } } @@ -191,7 +210,7 @@ bool MergeTreeIndexAggregatorSet::buildFilter( MergeTreeIndexGranulePtr MergeTreeIndexAggregatorSet::getGranuleAndReset() { - auto granule = std::make_shared(index, std::move(columns)); + auto granule = std::make_shared(index_name, index_sample_block, max_rows, std::move(columns)); switch (data.type) { @@ -205,19 +224,23 @@ MergeTreeIndexGranulePtr MergeTreeIndexAggregatorSet::getGranuleAndReset() #undef M } - columns = index.header.cloneEmptyColumns(); + columns = index_sample_block.cloneEmptyColumns(); return granule; } MergeTreeIndexConditionSet::MergeTreeIndexConditionSet( - const SelectQueryInfo & query, - const Context & context, - const MergeTreeIndexSet &index_) - : index(index_) + const String & index_name_, + const Block & index_sample_block_, + size_t max_rows_, + const SelectQueryInfo & query, + const Context & context) + : index_name(index_name_) + , max_rows(max_rows_) + , index_sample_block(index_sample_block_) { - for (const auto & name : index.columns) + for (const auto & name : index_sample_block.getNames()) if (!key_columns.count(name)) key_columns.insert(name); @@ -243,7 +266,7 @@ MergeTreeIndexConditionSet::MergeTreeIndexConditionSet( traverseAST(expression_ast); auto syntax_analyzer_result = SyntaxAnalyzer(context).analyze( - expression_ast, index.header.getNamesAndTypesList()); + expression_ast, index_sample_block.getNamesAndTypesList()); actions = ExpressionAnalyzer(expression_ast, syntax_analyzer_result, context).getActions(true); } @@ -262,7 +285,7 @@ bool MergeTreeIndexConditionSet::mayBeTrueOnGranule(MergeTreeIndexGranulePtr idx throw Exception( "Set index condition got a granule with the wrong type.", ErrorCodes::LOGICAL_ERROR); - if (useless || granule->empty() || (index.max_rows && granule->size() > index.max_rows)) + if (useless || granule->empty() || (max_rows != 0 && granule->size() > max_rows)) return true; Block result = granule->block; @@ -435,18 +458,18 @@ bool MergeTreeIndexConditionSet::checkASTUseless(const ASTPtr & node, bool atomi MergeTreeIndexGranulePtr MergeTreeIndexSet::createIndexGranule() const { - return std::make_shared(*this); + return std::make_shared(index.name, index.sample_block, max_rows); } MergeTreeIndexAggregatorPtr MergeTreeIndexSet::createIndexAggregator() const { - return std::make_shared(*this); + return std::make_shared(index.name, index.sample_block, max_rows); } MergeTreeIndexConditionPtr MergeTreeIndexSet::createIndexCondition( const SelectQueryInfo & query, const Context & context) const { - return std::make_shared(query, context, *this); + return std::make_shared(index.name, index.sample_block, max_rows, query, context); }; bool MergeTreeIndexSet::mayBenefitFromIndexForIn(const ASTPtr &) const @@ -454,47 +477,18 @@ bool MergeTreeIndexSet::mayBenefitFromIndexForIn(const ASTPtr &) const return false; } - -std::unique_ptr setIndexCreator( - const NamesAndTypesList & new_columns, - std::shared_ptr node, - const Context & context, - bool /*attach*/) +MergeTreeIndexPtr setIndexCreator(const IndexDescription & index) { - if (node->name.empty()) - throw Exception("Index must have unique name", ErrorCodes::INCORRECT_QUERY); + size_t max_rows = index.arguments[0].get(); + return std::make_shared(index, max_rows); +} - size_t max_rows = 0; - if (!node->type->arguments || node->type->arguments->children.size() != 1) +void setIndexValidator(const IndexDescription & index, bool /*attach*/) +{ + if (index.arguments.size() != 1) throw Exception("Set index must have exactly one argument.", ErrorCodes::INCORRECT_QUERY); - else if (node->type->arguments->children.size() == 1) - max_rows = node->type->arguments->children[0]->as().value.get(); - - - ASTPtr expr_list = MergeTreeData::extractKeyExpressionList(node->expr->clone()); - auto syntax = SyntaxAnalyzer(context).analyze(expr_list, new_columns); - auto unique_expr = ExpressionAnalyzer(expr_list, syntax, context).getActions(false); - - auto sample = ExpressionAnalyzer(expr_list, syntax, context) - .getActions(true)->getSampleBlock(); - - Block header; - - Names columns; - DataTypes data_types; - - for (size_t i = 0; i < expr_list->children.size(); ++i) - { - const auto & column = sample.getByPosition(i); - - columns.emplace_back(column.name); - data_types.emplace_back(column.type); - - header.insert(ColumnWithTypeAndName(column.type->createColumn(), column.type, column.name)); - } - - return std::make_unique( - node->name, std::move(unique_expr), columns, data_types, header, node->granularity, max_rows); + else if (index.arguments[0].getType() != Field::Types::UInt64) + throw Exception("Set index argument must be positive integer.", ErrorCodes::INCORRECT_QUERY); } } diff --git a/src/Storages/MergeTree/MergeTreeIndexSet.h b/src/Storages/MergeTree/MergeTreeIndexSet.h index 5b0448d13be..d84991f5e85 100644 --- a/src/Storages/MergeTree/MergeTreeIndexSet.h +++ b/src/Storages/MergeTree/MergeTreeIndexSet.h @@ -16,8 +16,16 @@ class MergeTreeIndexSet; struct MergeTreeIndexGranuleSet : public IMergeTreeIndexGranule { - explicit MergeTreeIndexGranuleSet(const MergeTreeIndexSet & index_); - MergeTreeIndexGranuleSet(const MergeTreeIndexSet & index_, MutableColumns && columns_); + explicit MergeTreeIndexGranuleSet( + const String & index_name_, + const Block & index_sample_block_, + size_t max_rows_); + + MergeTreeIndexGranuleSet( + const String & index_name_, + const Block & index_sample_block_, + size_t max_rows_, + MutableColumns && columns_); void serializeBinary(WriteBuffer & ostr) const override; void deserializeBinary(ReadBuffer & istr) override; @@ -27,14 +35,20 @@ struct MergeTreeIndexGranuleSet : public IMergeTreeIndexGranule ~MergeTreeIndexGranuleSet() override = default; - const MergeTreeIndexSet & index; + String index_name; + size_t max_rows; + Block index_sample_block; Block block; }; struct MergeTreeIndexAggregatorSet : IMergeTreeIndexAggregator { - explicit MergeTreeIndexAggregatorSet(const MergeTreeIndexSet & index); + explicit MergeTreeIndexAggregatorSet( + const String & index_name_, + const Block & index_sample_block_, + size_t max_rows_); + ~MergeTreeIndexAggregatorSet() override = default; size_t size() const { return data.getTotalRowCount(); } @@ -55,7 +69,10 @@ private: size_t limit, ClearableSetVariants & variants) const; - const MergeTreeIndexSet & index; + String index_name; + size_t max_rows; + Block index_sample_block; + ClearableSetVariants data; Sizes key_sizes; MutableColumns columns; @@ -66,9 +83,11 @@ class MergeTreeIndexConditionSet : public IMergeTreeIndexCondition { public: MergeTreeIndexConditionSet( - const SelectQueryInfo & query, - const Context & context, - const MergeTreeIndexSet & index); + const String & index_name_, + const Block & index_sample_block_, + size_t max_rows_, + const SelectQueryInfo & query, + const Context & context); bool alwaysUnknownOrTrue() const override; @@ -82,7 +101,10 @@ private: bool checkASTUseless(const ASTPtr & node, bool atomic = false) const; - const MergeTreeIndexSet & index; + + String index_name; + size_t max_rows; + Block index_sample_block; bool useless; std::set key_columns; @@ -95,14 +117,11 @@ class MergeTreeIndexSet : public IMergeTreeIndex { public: MergeTreeIndexSet( - String name_, - ExpressionActionsPtr expr_, - const Names & columns_, - const DataTypes & data_types_, - const Block & header_, - size_t granularity_, + const IndexDescription & index_, size_t max_rows_) - : IMergeTreeIndex(std::move(name_), std::move(expr_), columns_, data_types_, header_, granularity_), max_rows(max_rows_) {} + : IMergeTreeIndex(index_) + , max_rows(max_rows_) + {} ~MergeTreeIndexSet() override = default; diff --git a/src/Storages/MergeTree/MergeTreeIndices.cpp b/src/Storages/MergeTree/MergeTreeIndices.cpp index 95e18a8394f..b0f5b4d92f5 100644 --- a/src/Storages/MergeTree/MergeTreeIndices.cpp +++ b/src/Storages/MergeTree/MergeTreeIndices.cpp @@ -18,31 +18,27 @@ namespace ErrorCodes extern const int INCORRECT_QUERY; } -void MergeTreeIndexFactory::registerIndex(const std::string & name, Creator creator) +void MergeTreeIndexFactory::registerCreator(const std::string & index_type, Creator creator) { - if (!indexes.emplace(name, std::move(creator)).second) - throw Exception("MergeTreeIndexFactory: the Index creator name '" + name + "' is not unique", + if (!creators.emplace(index_type, std::move(creator)).second) + throw Exception("MergeTreeIndexFactory: the Index creator name '" + index_type + "' is not unique", ErrorCodes::LOGICAL_ERROR); } - -std::unique_ptr MergeTreeIndexFactory::get( - const NamesAndTypesList & columns, - std::shared_ptr node, - const Context & context, - bool attach) const +void MergeTreeIndexFactory::registerValidator(const std::string & index_type, Validator validator) { - if (!node->type) - throw Exception("TYPE is required for index", ErrorCodes::INCORRECT_QUERY); + if (!validators.emplace(index_type, std::move(validator)).second) + throw Exception("MergeTreeIndexFactory: the Index validator name '" + index_type + "' is not unique", ErrorCodes::LOGICAL_ERROR); +} - if (node->type->parameters && !node->type->parameters->children.empty()) - throw Exception("Index type cannot have parameters", ErrorCodes::INCORRECT_QUERY); - boost::algorithm::to_lower(node->type->name); - auto it = indexes.find(node->type->name); - if (it == indexes.end()) +MergeTreeIndexPtr MergeTreeIndexFactory::get( + const IndexDescription & index) const +{ + auto it = creators.find(index.type); + if (it == creators.end()) throw Exception( - "Unknown Index type '" + node->type->name + "'. Available index types: " + - std::accumulate(indexes.cbegin(), indexes.cend(), std::string{}, + "Unknown Index type '" + index.type + "'. Available index types: " + + std::accumulate(creators.cbegin(), creators.cend(), std::string{}, [] (auto && left, const auto & right) -> std::string { if (left.empty()) @@ -52,16 +48,56 @@ std::unique_ptr MergeTreeIndexFactory::get( }), ErrorCodes::INCORRECT_QUERY); - return it->second(columns, node, context, attach); + return it->second(index); +} + + +MergeTreeIndices MergeTreeIndexFactory::getMany(const std::vector & indices) const +{ + MergeTreeIndices result; + for (const auto & index : indices) + result.emplace_back(get(index)); + return result; +} + +void MergeTreeIndexFactory::validate(const IndexDescription & index, bool attach) const +{ + auto it = validators.find(index.type); + if (it == validators.end()) + throw Exception( + "Unknown Index type '" + index.type + "'. Available index types: " + + std::accumulate( + validators.cbegin(), + validators.cend(), + std::string{}, + [](auto && left, const auto & right) -> std::string + { + if (left.empty()) + return right.first; + else + return left + ", " + right.first; + }), + ErrorCodes::INCORRECT_QUERY); + + it->second(index, attach); } MergeTreeIndexFactory::MergeTreeIndexFactory() { - registerIndex("minmax", minmaxIndexCreator); - registerIndex("set", setIndexCreator); - registerIndex("ngrambf_v1", bloomFilterIndexCreator); - registerIndex("tokenbf_v1", bloomFilterIndexCreator); - registerIndex("bloom_filter", bloomFilterIndexCreatorNew); + registerCreator("minmax", minmaxIndexCreator); + registerValidator("minmax", minmaxIndexValidator); + + registerCreator("set", setIndexCreator); + registerValidator("set", setIndexValidator); + + registerCreator("ngrambf_v1", bloomFilterIndexCreator); + registerValidator("ngrambf_v1", bloomFilterIndexValidator); + + registerCreator("tokenbf_v1", bloomFilterIndexCreator); + registerValidator("tokenbf_v1", bloomFilterIndexValidator); + + registerCreator("bloom_filter", bloomFilterIndexCreatorNew); + registerValidator("bloom_filter", bloomFilterIndexValidatorNew); } MergeTreeIndexFactory & MergeTreeIndexFactory::instance() diff --git a/src/Storages/MergeTree/MergeTreeIndices.h b/src/Storages/MergeTree/MergeTreeIndices.h index f133dae2472..28795ae46b5 100644 --- a/src/Storages/MergeTree/MergeTreeIndices.h +++ b/src/Storages/MergeTree/MergeTreeIndices.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -17,13 +18,6 @@ constexpr auto INDEX_FILE_PREFIX = "skp_idx_"; namespace DB { -class MergeTreeData; -class IMergeTreeIndex; - -using MergeTreeIndexPtr = std::shared_ptr; -using MutableMergeTreeIndexPtr = std::shared_ptr; - - /// Stores some info about a single block of data. struct IMergeTreeIndexGranule { @@ -71,60 +65,34 @@ public: using MergeTreeIndexConditionPtr = std::shared_ptr; -/// Structure for storing basic index info like columns, expression, arguments, ... -class IMergeTreeIndex +struct IMergeTreeIndex { -public: - IMergeTreeIndex( - String name_, - ExpressionActionsPtr expr_, - const Names & columns_, - const DataTypes & data_types_, - const Block & header_, - size_t granularity_) - : name(name_) - , expr(expr_) - , columns(columns_) - , data_types(data_types_) - , header(header_) - , granularity(granularity_) {} + IMergeTreeIndex(const IndexDescription & index_) + : index(index_) + { + } virtual ~IMergeTreeIndex() = default; /// gets filename without extension - String getFileName() const { return INDEX_FILE_PREFIX + name; } + String getFileName() const { return INDEX_FILE_PREFIX + index.name; } /// Checks whether the column is in data skipping index. virtual bool mayBenefitFromIndexForIn(const ASTPtr & node) const = 0; virtual MergeTreeIndexGranulePtr createIndexGranule() const = 0; + virtual MergeTreeIndexAggregatorPtr createIndexAggregator() const = 0; virtual MergeTreeIndexConditionPtr createIndexCondition( const SelectQueryInfo & query_info, const Context & context) const = 0; - Names getColumnsRequiredForIndexCalc() const { return expr->getRequiredColumns(); } + Names getColumnsRequiredForIndexCalc() const { return index.expression->getRequiredColumns(); } - /// Index name - String name; - - /// Index expression (x * y) - /// with columns arguments - ExpressionActionsPtr expr; - - /// Names of columns for index - Names columns; - - /// Data types of columns - DataTypes data_types; - - /// Block with columns and data_types - Block header; - - /// Skip index granularity - size_t granularity; + const IndexDescription & index; }; +using MergeTreeIndexPtr = std::shared_ptr; using MergeTreeIndices = std::vector; @@ -133,53 +101,39 @@ class MergeTreeIndexFactory : private boost::noncopyable public: static MergeTreeIndexFactory & instance(); - using Creator = std::function< - std::unique_ptr( - const NamesAndTypesList & columns, - std::shared_ptr node, - const Context & context, - bool attach)>; + using Creator = std::function; - std::unique_ptr get( - const NamesAndTypesList & columns, - std::shared_ptr node, - const Context & context, - bool attach) const; + using Validator = std::function; - void registerIndex(const std::string & name, Creator creator); + void validate(const IndexDescription & index, bool attach) const; - const auto & getAllIndexes() const { return indexes; } + MergeTreeIndexPtr get(const IndexDescription & index) const; + + MergeTreeIndices getMany(const std::vector & indices) const; + + void registerCreator(const std::string & index_type, Creator creator); + void registerValidator(const std::string & index_type, Validator validator); protected: MergeTreeIndexFactory(); private: - using Indexes = std::unordered_map; - Indexes indexes; + using Creators = std::unordered_map; + using Validators = std::unordered_map; + Creators creators; + Validators validators; }; -std::unique_ptr minmaxIndexCreator( - const NamesAndTypesList & columns, - std::shared_ptr node, - const Context & context, - bool attach); +MergeTreeIndexPtr minmaxIndexCreator(const IndexDescription & index); +void minmaxIndexValidator(const IndexDescription & index, bool attach); -std::unique_ptr setIndexCreator( - const NamesAndTypesList & columns, - std::shared_ptr node, - const Context & context, - bool attach); +MergeTreeIndexPtr setIndexCreator(const IndexDescription & index); +void setIndexValidator(const IndexDescription & index, bool attach); -std::unique_ptr bloomFilterIndexCreator( - const NamesAndTypesList & columns, - std::shared_ptr node, - const Context & context, - bool attach); +MergeTreeIndexPtr bloomFilterIndexCreator(const IndexDescription & index); +void bloomFilterIndexValidator(const IndexDescription & index, bool attach); -std::unique_ptr bloomFilterIndexCreatorNew( - const NamesAndTypesList & columns, - std::shared_ptr node, - const Context & context, - bool attach); +MergeTreeIndexPtr bloomFilterIndexCreatorNew(const IndexDescription & index); +void bloomFilterIndexValidatorNew(const IndexDescription & index, bool attach); } diff --git a/src/Storages/MergeTree/MergeTreeRangeReader.cpp b/src/Storages/MergeTree/MergeTreeRangeReader.cpp index f2b53fbb70a..667b0b2da96 100644 --- a/src/Storages/MergeTree/MergeTreeRangeReader.cpp +++ b/src/Storages/MergeTree/MergeTreeRangeReader.cpp @@ -905,7 +905,9 @@ void MergeTreeRangeReader::executePrewhereActionsAndFilterColumns(ReadResult & r if (prewhere->remove_prewhere_column) result.columns.erase(result.columns.begin() + prewhere_column_pos); else - result.columns[prewhere_column_pos] = DataTypeUInt8().createColumnConst(result.num_rows, 1u)->convertToFullColumnIfConst(); + result.columns[prewhere_column_pos] = + getSampleBlock().getByName(prewhere->prewhere_column_name).type-> + createColumnConst(result.num_rows, 1u)->convertToFullColumnIfConst(); } } /// Filter in WHERE instead diff --git a/src/Storages/MergeTree/MergedBlockOutputStream.cpp b/src/Storages/MergeTree/MergedBlockOutputStream.cpp index e064a4c734a..bce50918ac0 100644 --- a/src/Storages/MergeTree/MergedBlockOutputStream.cpp +++ b/src/Storages/MergeTree/MergedBlockOutputStream.cpp @@ -157,8 +157,8 @@ void MergedBlockOutputStream::writeImpl(const Block & block, const IColumn::Perm return; std::unordered_set skip_indexes_column_names_set; - for (const auto & index : storage.skip_indices) - std::copy(index->columns.cbegin(), index->columns.cend(), + for (const auto & index : storage.getSecondaryIndices()) + std::copy(index.column_names.cbegin(), index.column_names.cend(), std::inserter(skip_indexes_column_names_set, skip_indexes_column_names_set.end())); Names skip_indexes_column_names(skip_indexes_column_names_set.begin(), skip_indexes_column_names_set.end()); diff --git a/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp b/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp index 1a99636534b..b5eefbe3f0c 100644 --- a/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp +++ b/src/Storages/MergeTree/MergedColumnOnlyOutputStream.cpp @@ -39,7 +39,7 @@ void MergedColumnOnlyOutputStream::write(const Block & block) { std::unordered_set skip_indexes_column_names_set; for (const auto & index : writer->getSkipIndices()) - std::copy(index->columns.cbegin(), index->columns.cend(), + std::copy(index->index.column_names.cbegin(), index->index.column_names.cend(), std::inserter(skip_indexes_column_names_set, skip_indexes_column_names_set.end())); Names skip_indexes_column_names(skip_indexes_column_names_set.begin(), skip_indexes_column_names_set.end()); diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp index d8c1103809f..1bbc56d940d 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeBlockOutputStream.cpp @@ -85,7 +85,7 @@ void ReplicatedMergeTreeBlockOutputStream::checkQuorumPrecondition(zkutil::ZooKe */ auto quorum_status = quorum_status_future.get(); - if (quorum_status.error != Coordination::ZNONODE) + if (quorum_status.error != Coordination::Error::ZNONODE) throw Exception("Quorum for previous write has not been satisfied yet. Status: " + quorum_status.data, ErrorCodes::UNSATISFIED_QUORUM_FOR_PREVIOUS_WRITE); /// Both checks are implicitly made also later (otherwise there would be a race condition). @@ -93,7 +93,7 @@ void ReplicatedMergeTreeBlockOutputStream::checkQuorumPrecondition(zkutil::ZooKe auto is_active = is_active_future.get(); auto host = host_future.get(); - if (is_active.error == Coordination::ZNONODE || host.error == Coordination::ZNONODE) + if (is_active.error == Coordination::Error::ZNONODE || host.error == Coordination::Error::ZNONODE) throw Exception("Replica is not active right now", ErrorCodes::READONLY); quorum_info.is_active_node_value = is_active.data; @@ -198,7 +198,8 @@ void ReplicatedMergeTreeBlockOutputStream::writeExistingPart(MergeTreeData::Muta } -void ReplicatedMergeTreeBlockOutputStream::commitPart(zkutil::ZooKeeperPtr & zookeeper, MergeTreeData::MutableDataPartPtr & part, const String & block_id) +void ReplicatedMergeTreeBlockOutputStream::commitPart( + zkutil::ZooKeeperPtr & zookeeper, MergeTreeData::MutableDataPartPtr & part, const String & block_id) { storage.check(part->getColumns()); assertSessionIsNotExpired(zookeeper); @@ -299,9 +300,9 @@ void ReplicatedMergeTreeBlockOutputStream::commitPart(zkutil::ZooKeeperPtr & zoo storage.renameTempPartAndAdd(part, nullptr, &transaction); Coordination::Responses responses; - int32_t multi_code = zookeeper->tryMultiNoThrow(ops, responses); /// 1 RTT + Coordination::Error multi_code = zookeeper->tryMultiNoThrow(ops, responses); /// 1 RTT - if (multi_code == Coordination::ZOK) + if (multi_code == Coordination::Error::ZOK) { transaction.commit(); storage.merge_selecting_task->schedule(); @@ -309,8 +310,8 @@ void ReplicatedMergeTreeBlockOutputStream::commitPart(zkutil::ZooKeeperPtr & zoo /// Lock nodes have been already deleted, do not delete them in destructor block_number_lock->assumeUnlocked(); } - else if (multi_code == Coordination::ZCONNECTIONLOSS - || multi_code == Coordination::ZOPERATIONTIMEOUT) + else if (multi_code == Coordination::Error::ZCONNECTIONLOSS + || multi_code == Coordination::Error::ZOPERATIONTIMEOUT) { /** If the connection is lost, and we do not know if the changes were applied, we can not delete the local part * if the changes were applied, the inserted block appeared in `/blocks/`, and it can not be inserted again. @@ -326,7 +327,7 @@ void ReplicatedMergeTreeBlockOutputStream::commitPart(zkutil::ZooKeeperPtr & zoo { String failed_op_path = zkutil::KeeperMultiException(multi_code, ops, responses).getPathForFirstFailedOp(); - if (multi_code == Coordination::ZNODEEXISTS && deduplicate_block && failed_op_path == block_id_path) + if (multi_code == Coordination::Error::ZNODEEXISTS && deduplicate_block && failed_op_path == block_id_path) { /// Block with the same id have just appeared in table (or other replica), rollback thee insertion. LOG_INFO(log, "Block with ID {} already exists; ignoring it (removing part {})", block_id, part->name); @@ -336,7 +337,7 @@ void ReplicatedMergeTreeBlockOutputStream::commitPart(zkutil::ZooKeeperPtr & zoo last_block_is_duplicate = true; ProfileEvents::increment(ProfileEvents::DuplicatedInsertedBlocks); } - else if (multi_code == Coordination::ZNODEEXISTS && failed_op_path == quorum_info.status_path) + else if (multi_code == Coordination::Error::ZNODEEXISTS && failed_op_path == quorum_info.status_path) { transaction.rollback(); @@ -347,7 +348,7 @@ void ReplicatedMergeTreeBlockOutputStream::commitPart(zkutil::ZooKeeperPtr & zoo /// NOTE: We could be here if the node with the quorum existed, but was quickly removed. transaction.rollback(); throw Exception("Unexpected logical error while adding block " + toString(block_number) + " with ID '" + block_id + "': " - + zkutil::ZooKeeper::error2string(multi_code) + ", path " + failed_op_path, + + Coordination::errorMessage(multi_code) + ", path " + failed_op_path, ErrorCodes::UNEXPECTED_ZOOKEEPER_ERROR); } } @@ -355,13 +356,13 @@ void ReplicatedMergeTreeBlockOutputStream::commitPart(zkutil::ZooKeeperPtr & zoo { transaction.rollback(); throw Exception("Unrecoverable network error while adding block " + toString(block_number) + " with ID '" + block_id + "': " - + zkutil::ZooKeeper::error2string(multi_code), ErrorCodes::UNEXPECTED_ZOOKEEPER_ERROR); + + Coordination::errorMessage(multi_code), ErrorCodes::UNEXPECTED_ZOOKEEPER_ERROR); } else { transaction.rollback(); throw Exception("Unexpected ZooKeeper error while adding block " + toString(block_number) + " with ID '" + block_id + "': " - + zkutil::ZooKeeper::error2string(multi_code), ErrorCodes::UNEXPECTED_ZOOKEEPER_ERROR); + + Coordination::errorMessage(multi_code), ErrorCodes::UNEXPECTED_ZOOKEEPER_ERROR); } if (quorum) diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp index de91a5d5940..1bc132eaba4 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeCleanupThread.cpp @@ -40,7 +40,7 @@ void ReplicatedMergeTreeCleanupThread::run() { tryLogCurrentException(log, __PRETTY_FUNCTION__); - if (e.code == Coordination::ZSESSIONEXPIRED) + if (e.code == Coordination::Error::ZSESSIONEXPIRED) return; } catch (...) @@ -319,15 +319,15 @@ void ReplicatedMergeTreeCleanupThread::clearOldBlocks() for (auto & pair : try_remove_futures) { const String & path = pair.first; - int32_t rc = pair.second.get().error; - if (rc == Coordination::ZNOTEMPTY) + Coordination::Error rc = pair.second.get().error; + if (rc == Coordination::Error::ZNOTEMPTY) { /// Can happen if there are leftover block nodes with children created by previous server versions. zookeeper->removeRecursive(path); cached_block_stats.erase(first_outdated_block->node); } - else if (rc) - LOG_WARNING(log, "Error while deleting ZooKeeper path `{}`: {}, ignoring.", path, zkutil::ZooKeeper::error2string(rc)); + else if (rc != Coordination::Error::ZOK) + LOG_WARNING(log, "Error while deleting ZooKeeper path `{}`: {}, ignoring.", path, Coordination::errorMessage(rc)); else { /// Successfully removed blocks have to be removed from cache @@ -348,7 +348,7 @@ void ReplicatedMergeTreeCleanupThread::getBlocksSortedByTime(zkutil::ZooKeeper & Strings blocks; Coordination::Stat stat; - if (zookeeper.tryGetChildren(storage.zookeeper_path + "/blocks", blocks, &stat)) + if (Coordination::Error::ZOK != zookeeper.tryGetChildren(storage.zookeeper_path + "/blocks", blocks, &stat)) throw Exception(storage.zookeeper_path + "/blocks doesn't exist", ErrorCodes::NOT_FOUND_NODE); /// Seems like this code is obsolete, because we delete blocks from cache @@ -391,7 +391,7 @@ void ReplicatedMergeTreeCleanupThread::getBlocksSortedByTime(zkutil::ZooKeeper & for (auto & elem : exists_futures) { auto status = elem.second.get(); - if (status.error != Coordination::ZNONODE) + if (status.error != Coordination::Error::ZNONODE) { cached_block_stats.emplace(elem.first, status.stat.ctime); timed_blocks.emplace_back(elem.first, status.stat.ctime); diff --git a/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp b/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp index 8f99f315620..0d824fa2dd8 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreePartCheckThread.cpp @@ -368,7 +368,7 @@ void ReplicatedMergeTreePartCheckThread::run() { tryLogCurrentException(log, __PRETTY_FUNCTION__); - if (e.code == Coordination::ZSESSIONEXPIRED) + if (e.code == Coordination::Error::ZSESSIONEXPIRED) return; task->scheduleAfter(PART_CHECK_ERROR_SLEEP_MS); diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp index 4ea7ddda738..8a9dbceba04 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.cpp @@ -319,8 +319,8 @@ void ReplicatedMergeTreeQueue::updateTimesInZooKeeper( Coordination::Responses responses; auto code = zookeeper->tryMulti(ops, responses); - if (code) - LOG_ERROR(log, "Couldn't set value of nodes for insert times ({}/min_unprocessed_insert_time, max_processed_insert_time): {}", replica_path, zkutil::ZooKeeper::error2string(code) + ". This shouldn't happen often."); + if (code != Coordination::Error::ZOK) + LOG_ERROR(log, "Couldn't set value of nodes for insert times ({}/min_unprocessed_insert_time, max_processed_insert_time): {}. This shouldn't happen often.", replica_path, Coordination::errorMessage(code)); } } @@ -364,8 +364,8 @@ void ReplicatedMergeTreeQueue::removeProcessedEntry(zkutil::ZooKeeperPtr zookeep notifySubscribers(queue_size); auto code = zookeeper->tryRemove(replica_path + "/queue/" + entry->znode_name); - if (code) - LOG_ERROR(log, "Couldn't remove {}/queue/{}: {}. This shouldn't happen often.", replica_path, entry->znode_name, zkutil::ZooKeeper::error2string(code)); + if (code != Coordination::Error::ZOK) + LOG_ERROR(log, "Couldn't remove {}/queue/{}: {}. This shouldn't happen often.", replica_path, entry->znode_name, Coordination::errorMessage(code)); updateTimesInZooKeeper(zookeeper, min_unprocessed_insert_time_changed, max_processed_insert_time_changed); } @@ -419,7 +419,7 @@ bool ReplicatedMergeTreeQueue::removeFromVirtualParts(const MergeTreePartInfo & return virtual_parts.remove(part_info); } -void ReplicatedMergeTreeQueue::pullLogsToQueue(zkutil::ZooKeeperPtr zookeeper, Coordination::WatchCallback watch_callback) +int32_t ReplicatedMergeTreeQueue::pullLogsToQueue(zkutil::ZooKeeperPtr zookeeper, Coordination::WatchCallback watch_callback) { std::lock_guard lock(pull_logs_to_queue_mutex); if (pull_log_blocker.isCancelled()) @@ -428,6 +428,10 @@ void ReplicatedMergeTreeQueue::pullLogsToQueue(zkutil::ZooKeeperPtr zookeeper, C String index_str = zookeeper->get(replica_path + "/log_pointer"); UInt64 index; + /// The version of "/log" is modified when new entries to merge/mutate/drop appear. + Coordination::Stat stat; + zookeeper->get(zookeeper_path + "/log", &stat); + Strings log_entries = zookeeper->getChildrenWatch(zookeeper_path + "/log", nullptr, watch_callback); /// We update mutations after we have loaded the list of log entries, but before we insert them @@ -559,8 +563,10 @@ void ReplicatedMergeTreeQueue::pullLogsToQueue(zkutil::ZooKeeperPtr zookeeper, C } if (storage.queue_task_handle) - storage.queue_task_handle->wake(); + storage.queue_task_handle->signalReadyToRun(); } + + return stat.version; } @@ -641,7 +647,7 @@ void ReplicatedMergeTreeQueue::updateMutations(zkutil::ZooKeeperPtr zookeeper, C } if (some_active_mutations_were_killed) - storage.queue_task_handle->wake(); + storage.queue_task_handle->signalReadyToRun(); if (!entries_to_load.empty()) { @@ -720,7 +726,7 @@ ReplicatedMergeTreeMutationEntryPtr ReplicatedMergeTreeQueue::removeMutation( std::lock_guard lock(update_mutations_mutex); auto rc = zookeeper->tryRemove(zookeeper_path + "/mutations/" + mutation_id); - if (rc == Coordination::ZOK) + if (rc == Coordination::Error::ZOK) LOG_DEBUG(log, "Removed mutation {} from ZooKeeper.", mutation_id); ReplicatedMergeTreeMutationEntryPtr entry; @@ -754,7 +760,7 @@ ReplicatedMergeTreeMutationEntryPtr ReplicatedMergeTreeQueue::removeMutation( } if (mutation_was_active) - storage.queue_task_handle->wake(); + storage.queue_task_handle->signalReadyToRun(); return entry; } @@ -844,8 +850,8 @@ void ReplicatedMergeTreeQueue::removePartProducingOpsInRange( if ((*it)->currently_executing) to_wait.push_back(*it); auto code = zookeeper->tryRemove(replica_path + "/queue/" + (*it)->znode_name); - if (code) - LOG_INFO(log, "Couldn't remove {}: {}", replica_path + "/queue/" + (*it)->znode_name, zkutil::ZooKeeper::error2string(code)); + if (code != Coordination::Error::ZOK) + LOG_INFO(log, "Couldn't remove {}: {}", replica_path + "/queue/" + (*it)->znode_name, Coordination::errorMessage(code)); updateStateOnQueueEntryRemoval( *it, /* is_successful = */ false, @@ -1625,15 +1631,15 @@ ReplicatedMergeTreeMergePredicate::ReplicatedMergeTreeMergePredicate( for (auto & block : block_infos) { Coordination::GetResponse resp = block.contents_future.get(); - if (!resp.error && lock_holder_paths.count(resp.data)) + if (resp.error == Coordination::Error::ZOK && lock_holder_paths.count(resp.data)) committing_blocks[block.partition].insert(block.number); } } - queue_.pullLogsToQueue(zookeeper); + merges_version = queue_.pullLogsToQueue(zookeeper); Coordination::GetResponse quorum_status_response = quorum_status_future.get(); - if (!quorum_status_response.error) + if (quorum_status_response.error == Coordination::Error::ZOK) { ReplicatedMergeTreeQuorumEntry quorum_status; quorum_status.fromString(quorum_status_response.data); diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h index 4cbb86adb7b..e093e193381 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h +++ b/src/Storages/MergeTree/ReplicatedMergeTreeQueue.h @@ -271,8 +271,9 @@ public: * If watch_callback is not empty, will call it when new entries appear in the log. * If there were new entries, notifies storage.queue_task_handle. * Additionally loads mutations (so that the set of mutations is always more recent than the queue). + * Return the version of "logs" node (that is updated for every merge/mutation/... added to the log) */ - void pullLogsToQueue(zkutil::ZooKeeperPtr zookeeper, Coordination::WatchCallback watch_callback = {}); + int32_t pullLogsToQueue(zkutil::ZooKeeperPtr zookeeper, Coordination::WatchCallback watch_callback = {}); /// Load new mutation entries. If something new is loaded, schedule storage.merge_selecting_task. /// If watch_callback is not empty, will call it when new mutations appear in ZK. @@ -434,6 +435,9 @@ public: bool isMutationFinished(const ReplicatedMergeTreeMutationEntry & mutation) const; + /// The version of "log" node that is used to check that no new merges have appeared. + int32_t getVersion() const { return merges_version; } + private: const ReplicatedMergeTreeQueue & queue; @@ -445,6 +449,8 @@ private: /// Quorum state taken at some later time than prev_virtual_parts. String inprogress_quorum_part; + + int32_t merges_version = -1; }; diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeRestartingThread.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeRestartingThread.cpp index 93d652f2be0..0ec9b8069ac 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeRestartingThread.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeRestartingThread.cpp @@ -234,7 +234,7 @@ void ReplicatedMergeTreeRestartingThread::removeFailedQuorumParts() auto zookeeper = storage.getZooKeeper(); Strings failed_parts; - if (zookeeper->tryGetChildren(storage.zookeeper_path + "/quorum/failed_parts", failed_parts) != Coordination::ZOK) + if (zookeeper->tryGetChildren(storage.zookeeper_path + "/quorum/failed_parts", failed_parts) != Coordination::Error::ZOK) return; /// Firstly, remove parts from ZooKeeper @@ -294,12 +294,12 @@ void ReplicatedMergeTreeRestartingThread::activateReplica() { auto code = zookeeper->tryRemove(is_active_path, stat.version); - if (code == Coordination::ZBADVERSION) + if (code == Coordination::Error::ZBADVERSION) throw Exception("Another instance of replica " + storage.replica_path + " was created just now." " You shouldn't run multiple instances of same replica. You need to check configuration files.", ErrorCodes::REPLICA_IS_ALREADY_ACTIVE); - if (code && code != Coordination::ZNONODE) + if (code != Coordination::Error::ZOK && code != Coordination::Error::ZNONODE) throw Coordination::Exception(code, is_active_path); } @@ -314,7 +314,7 @@ void ReplicatedMergeTreeRestartingThread::activateReplica() } catch (const Coordination::Exception & e) { - if (e.code == Coordination::ZNODEEXISTS) + if (e.code == Coordination::Error::ZNODEEXISTS) throw Exception("Replica " + storage.replica_path + " appears to be already active. If you're sure it's not, " "try again in a minute or remove znode " + storage.replica_path + "/is_active manually", ErrorCodes::REPLICA_IS_ALREADY_ACTIVE); diff --git a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp index 3f9d039ffa1..2444affdbff 100644 --- a/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp +++ b/src/Storages/MergeTree/ReplicatedMergeTreeTableMetadata.cpp @@ -55,7 +55,7 @@ ReplicatedMergeTreeTableMetadata::ReplicatedMergeTreeTableMetadata(const MergeTr ttl_table = formattedAST(data.getTableTTLs().definition_ast); - skip_indices = data.getIndices().toString(); + skip_indices = data.getSecondaryIndices().toString(); if (data.canUseAdaptiveGranularity()) index_granularity_bytes = data_settings->index_granularity_bytes; else diff --git a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h index 37acf0f0160..4cecba1faff 100644 --- a/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h +++ b/src/Storages/MergeTree/StorageFromMergeTreeDataPart.h @@ -39,11 +39,6 @@ public: return part->storage.mayBenefitFromIndexForIn(left_in_operand, query_context); } - ColumnDependencies getColumnDependencies(const NameSet & updated_columns) const override - { - return part->storage.getColumnDependencies(updated_columns); - } - StorageInMemoryMetadata getInMemoryMetadata() const override { return part->storage.getInMemoryMetadata(); @@ -60,7 +55,8 @@ protected: , part(part_) { setColumns(part_->storage.getColumns()); - setIndices(part_->storage.getIndices()); + setSecondaryIndices(part_->storage.getSecondaryIndices()); + setPrimaryKey(part_->storage.getPrimaryKey()); setSortingKey(part_->storage.getSortingKey()); setColumnTTLs(part->storage.getColumnTTLs()); setTableTTLs(part->storage.getTableTTLs()); diff --git a/src/Storages/MergeTree/registerStorageMergeTree.cpp b/src/Storages/MergeTree/registerStorageMergeTree.cpp index 40cc8edca74..e08ea1739a5 100644 --- a/src/Storages/MergeTree/registerStorageMergeTree.cpp +++ b/src/Storages/MergeTree/registerStorageMergeTree.cpp @@ -205,121 +205,32 @@ static void setGraphitePatternsFromConfig(const Context & context, } -static String getMergeTreeVerboseHelp(bool is_extended_syntax) +static String getMergeTreeVerboseHelp(bool) { using namespace std::string_literals; String help = R"( -MergeTree is a family of storage engines. +Syntax for the MergeTree table engine: -MergeTrees are different in two ways: -- they may be replicated and non-replicated; -- they may do different actions on merge: nothing; sign collapse; sum; apply aggregete functions. +CREATE TABLE [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster] +( + name1 [type1] [DEFAULT|MATERIALIZED|ALIAS expr1] [TTL expr1], + name2 [type2] [DEFAULT|MATERIALIZED|ALIAS expr2] [TTL expr2], + ... + INDEX index_name1 expr1 TYPE type1(...) GRANULARITY value1, + INDEX index_name2 expr2 TYPE type2(...) GRANULARITY value2 +) ENGINE = MergeTree() +ORDER BY expr +[PARTITION BY expr] +[PRIMARY KEY expr] +[SAMPLE BY expr] +[TTL expr [DELETE|TO DISK 'xxx'|TO VOLUME 'xxx'], ...] +[SETTINGS name=value, ...] -So we have 14 combinations: - MergeTree, CollapsingMergeTree, SummingMergeTree, AggregatingMergeTree, ReplacingMergeTree, GraphiteMergeTree, VersionedCollapsingMergeTree - ReplicatedMergeTree, ReplicatedCollapsingMergeTree, ReplicatedSummingMergeTree, ReplicatedAggregatingMergeTree, ReplicatedReplacingMergeTree, ReplicatedGraphiteMergeTree, ReplicatedVersionedCollapsingMergeTree +See details in documentation: https://clickhouse.tech/docs/en/engines/table-engines/mergetree-family/mergetree/. Other engines of the family support different syntax, see details in the corresponding documentation topics. -In most of cases, you need MergeTree or ReplicatedMergeTree. - -For replicated merge trees, you need to supply a path in ZooKeeper and a replica name as the first two parameters. -Path in ZooKeeper is like '/clickhouse/tables/01/' where /clickhouse/tables/ is a common prefix and 01 is a shard name. -Replica name is like 'mtstat01-1' - it may be the hostname or any suitable string identifying replica. -You may use macro substitutions for these parameters. It's like ReplicatedMergeTree('/clickhouse/tables/{shard}/', '{replica}'... -Look at the section in server configuration file. -)"; - - if (!is_extended_syntax) - help += R"( -Next parameter (which is the first for unreplicated tables and the third for replicated tables) is the name of date column. -Date column must exist in the table and have type Date (not DateTime). -It is used for internal data partitioning and works like some kind of index. - -If your source data doesn't have a column of type Date, but has a DateTime column, you may add values for Date column while loading, - or you may INSERT your source data to a table of type Log and then transform it with INSERT INTO t SELECT toDate(time) AS date, * FROM ... -If your source data doesn't have any date or time, you may just pass any constant for a date column while loading. - -Next parameter is optional sampling expression. Sampling expression is used to implement SAMPLE clause in query for approximate query execution. -If you don't need approximate query execution, simply omit this parameter. -Sample expression must be one of the elements of the primary key tuple. For example, if your primary key is (CounterID, EventDate, intHash64(UserID)), your sampling expression might be intHash64(UserID). - -Next parameter is the primary key tuple. It's like (CounterID, EventDate, intHash64(UserID)) - a list of column names or functional expressions in round brackets. If your primary key has just one element, you may omit round brackets. - -Careful choice of the primary key is extremely important for processing short-time queries. - -Next parameter is index (primary key) granularity. Good value is 8192. You have no reasons to use any other value. -)"; - - help += R"( -For the Collapsing mode, the )" + (is_extended_syntax ? "only"s : "last"s) + R"( parameter is the name of a sign column - a special column that is used to 'collapse' rows with the same primary key while merging. - -For the Summing mode, the optional )" + (is_extended_syntax ? ""s : "last "s) + R"(parameter is a list of columns to sum while merging. This list is passed in round brackets, like (PageViews, Cost). -If this parameter is omitted, the storage will sum all numeric columns except columns participating in the primary key. - -For the Replacing mode, the optional )" + (is_extended_syntax ? ""s : "last "s) + R"(parameter is the name of a 'version' column. While merging, for all rows with the same primary key, only one row is selected: the last row, if the version column was not specified, or the last row with the maximum version value, if specified. - -For VersionedCollapsing mode, the )" + (is_extended_syntax ? ""s : "last "s) + R"(2 parameters are the name of a sign column and the name of a 'version' column. Version column must be in primary key. While merging, a pair of rows with the same primary key and different sign may collapse. -)"; - - if (is_extended_syntax) - help += R"( -You can specify a partitioning expression in the PARTITION BY clause. It is optional but highly recommended. -A common partitioning expression is some function of the event date column e.g. PARTITION BY toYYYYMM(EventDate) will partition the table by month. -Rows with different partition expression values are never merged together. That allows manipulating partitions with ALTER commands. -Also it acts as a kind of index. - -Sorting key is specified in the ORDER BY clause. It is mandatory for all MergeTree types. -It is like (CounterID, EventDate, intHash64(UserID)) - a list of column names or functional expressions -in round brackets. -If your sorting key has just one element, you may omit round brackets. - -By default primary key is equal to the sorting key. You can specify a primary key that is a prefix of the -sorting key in the PRIMARY KEY clause. - -Careful choice of the primary key is extremely important for processing short-time queries. - -Optional sampling expression can be specified in the SAMPLE BY clause. It is used to implement the SAMPLE clause in a SELECT query for approximate query execution. -Sampling expression must be one of the elements of the primary key tuple. For example, if your primary key is (CounterID, EventDate, intHash64(UserID)), your sampling expression might be intHash64(UserID). - -Engine settings can be specified in the SETTINGS clause. Full list is in the source code in the 'src/Storages/MergeTree/MergeTreeSettings.h' file. -E.g. you can specify the index (primary key) granularity with SETTINGS index_granularity = 8192. - -Examples: - -MergeTree PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate) SETTINGS index_granularity = 8192 - -MergeTree PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID), EventTime) SAMPLE BY intHash32(UserID) - -MergeTree PARTITION BY toYYYYMM(EventDate) ORDER BY (CounterID, EventDate, intHash32(UserID), EventTime) PRIMARY KEY (CounterID, EventDate) SAMPLE BY intHash32(UserID) - -CollapsingMergeTree(Sign) PARTITION BY StartDate SAMPLE BY intHash32(UserID) ORDER BY (CounterID, StartDate, intHash32(UserID), VisitID) - -SummingMergeTree PARTITION BY toMonday(EventDate) ORDER BY (OrderID, EventDate, BannerID, PhraseID, ContextType, RegionID, PageID, IsFlat, TypeID, ResourceNo) - -SummingMergeTree((Shows, Clicks, Cost, CostCur, ShowsSumPosition, ClicksSumPosition, SessionNum, SessionLen, SessionCost, GoalsNum, SessionDepth)) PARTITION BY toYYYYMM(EventDate) ORDER BY (OrderID, EventDate, BannerID, PhraseID, ContextType, RegionID, PageID, IsFlat, TypeID, ResourceNo) - -ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/hits', '{replica}') PARTITION BY EventDate ORDER BY (CounterID, EventDate, intHash32(UserID), EventTime) SAMPLE BY intHash32(UserID) -)"; - else - help += R"( -Examples: - -MergeTree(EventDate, (CounterID, EventDate), 8192) - -MergeTree(EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID), EventTime), 8192) - -CollapsingMergeTree(StartDate, intHash32(UserID), (CounterID, StartDate, intHash32(UserID), VisitID), 8192, Sign) - -SummingMergeTree(EventDate, (OrderID, EventDate, BannerID, PhraseID, ContextType, RegionID, PageID, IsFlat, TypeID, ResourceNo), 8192) - -SummingMergeTree(EventDate, (OrderID, EventDate, BannerID, PhraseID, ContextType, RegionID, PageID, IsFlat, TypeID, ResourceNo), 8192, (Shows, Clicks, Cost, CostCur, ShowsSumPosition, ClicksSumPosition, SessionNum, SessionLen, SessionCost, GoalsNum, SessionDepth)) - -ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/hits', '{replica}', EventDate, intHash32(UserID), (CounterID, EventDate, intHash32(UserID), EventTime), 8192) -)"; - - help += R"( -For further info please read the documentation: https://clickhouse.yandex/ +If you use the Replicated version of engines, see https://clickhouse.tech/docs/en/engines/table-engines/mergetree-family/replication/. )"; return help; @@ -607,7 +518,7 @@ static StoragePtr create(const StorageFactory::Arguments & args) if (args.query.columns_list && args.query.columns_list->indices) for (auto & index : args.query.columns_list->indices->children) - indices_description.indices.push_back(std::dynamic_pointer_cast(index)); + indices_description.push_back(IndexDescription::getIndexFromAST(index, args.columns, args.context)); storage_settings->loadFromQuery(*args.storage_def); diff --git a/src/Storages/ReadInOrderOptimizer.cpp b/src/Storages/ReadInOrderOptimizer.cpp index 5bbe5be9928..bfdbd7ef557 100644 --- a/src/Storages/ReadInOrderOptimizer.cpp +++ b/src/Storages/ReadInOrderOptimizer.cpp @@ -30,7 +30,7 @@ ReadInOrderOptimizer::ReadInOrderOptimizer( forbidden_columns.insert(elem.first); } -InputSortingInfoPtr ReadInOrderOptimizer::getInputOrder(const StoragePtr & storage) const +InputOrderInfoPtr ReadInOrderOptimizer::getInputOrder(const StoragePtr & storage) const { Names sorting_key_columns; if (const auto * merge_tree = dynamic_cast(storage.get())) @@ -122,7 +122,7 @@ InputSortingInfoPtr ReadInOrderOptimizer::getInputOrder(const StoragePtr & stora if (order_key_prefix_descr.empty()) return {}; - return std::make_shared(std::move(order_key_prefix_descr), read_direction); + return std::make_shared(std::move(order_key_prefix_descr), read_direction); } } diff --git a/src/Storages/ReadInOrderOptimizer.h b/src/Storages/ReadInOrderOptimizer.h index 8416d23a912..de858e8fd92 100644 --- a/src/Storages/ReadInOrderOptimizer.h +++ b/src/Storages/ReadInOrderOptimizer.h @@ -20,10 +20,10 @@ public: const SortDescription & required_sort_description, const SyntaxAnalyzerResultPtr & syntax_result); - InputSortingInfoPtr getInputOrder(const StoragePtr & storage) const; + InputOrderInfoPtr getInputOrder(const StoragePtr & storage) const; private: - /// Actions for every element of order expression to analyze functions for monotonicicy + /// Actions for every element of order expression to analyze functions for monotonicity ManyExpressionActions elements_actions; NameSet forbidden_columns; SortDescription required_sort_description; diff --git a/src/Storages/SelectQueryInfo.h b/src/Storages/SelectQueryInfo.h index 84cf3a32aa1..c4cd1035ea7 100644 --- a/src/Storages/SelectQueryInfo.h +++ b/src/Storages/SelectQueryInfo.h @@ -2,6 +2,7 @@ #include #include +#include #include namespace DB @@ -35,25 +36,25 @@ struct FilterInfo bool do_remove_column = false; }; -struct InputSortingInfo +struct InputOrderInfo { SortDescription order_key_prefix_descr; int direction; - InputSortingInfo(const SortDescription & order_key_prefix_descr_, int direction_) + InputOrderInfo(const SortDescription & order_key_prefix_descr_, int direction_) : order_key_prefix_descr(order_key_prefix_descr_), direction(direction_) {} - bool operator ==(const InputSortingInfo & other) const + bool operator ==(const InputOrderInfo & other) const { return order_key_prefix_descr == other.order_key_prefix_descr && direction == other.direction; } - bool operator !=(const InputSortingInfo & other) const { return !(*this == other); } + bool operator !=(const InputOrderInfo & other) const { return !(*this == other); } }; using PrewhereInfoPtr = std::shared_ptr; using FilterInfoPtr = std::shared_ptr; -using InputSortingInfoPtr = std::shared_ptr; +using InputOrderInfoPtr = std::shared_ptr; struct SyntaxAnalyzerResult; using SyntaxAnalyzerResultPtr = std::shared_ptr; @@ -61,6 +62,7 @@ using SyntaxAnalyzerResultPtr = std::shared_ptr; class ReadInOrderOptimizer; using ReadInOrderOptimizerPtr = std::shared_ptr; + /** Query along with some additional data, * that can be used during query processing * inside storage engines. @@ -73,9 +75,9 @@ struct SelectQueryInfo PrewhereInfoPtr prewhere_info; - ReadInOrderOptimizerPtr order_by_optimizer; + ReadInOrderOptimizerPtr order_optimizer; /// We can modify it while reading from storage - mutable InputSortingInfoPtr input_sorting_info; + mutable InputOrderInfoPtr input_order_info; /// Prepared sets are used for indices by storage engine. /// Example: x IN (1, 2, 3) diff --git a/src/Storages/StorageBuffer.cpp b/src/Storages/StorageBuffer.cpp index 720c7a2d670..2d8c3fd9a2f 100644 --- a/src/Storages/StorageBuffer.cpp +++ b/src/Storages/StorageBuffer.cpp @@ -171,8 +171,8 @@ Pipes StorageBuffer::read( if (dst_has_same_structure) { - if (query_info.order_by_optimizer) - query_info.input_sorting_info = query_info.order_by_optimizer->getInputOrder(destination); + if (query_info.order_optimizer) + query_info.input_order_info = query_info.order_optimizer->getInputOrder(destination); /// The destination table has the same structure of the requested columns and we can simply read blocks from there. pipes_from_dst = destination->read(column_names, query_info, context, processed_stage, max_block_size, num_streams); @@ -450,7 +450,6 @@ void StorageBuffer::startup() LOG_WARNING(log, "Storage {} is run with readonly settings, it will not be able to insert data. Set appropriate system_profile to fix this.", getName()); } - flush_handle = bg_pool.createTask(log->name() + "/Bg", [this]{ flushBack(); }); flush_handle->activateAndSchedule(); } @@ -777,7 +776,7 @@ void StorageBuffer::alter(const AlterCommands & params, const Context & context, optimize({} /*query*/, {} /*partition_id*/, false /*final*/, false /*deduplicate*/, context); StorageInMemoryMetadata metadata = getInMemoryMetadata(); - params.apply(metadata); + params.apply(metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, metadata); setColumns(std::move(metadata.columns)); } diff --git a/src/Storages/StorageDistributed.cpp b/src/Storages/StorageDistributed.cpp index 574d93df566..d80fee1e4dc 100644 --- a/src/Storages/StorageDistributed.cpp +++ b/src/Storages/StorageDistributed.cpp @@ -9,7 +9,6 @@ #include #include -#include #include #include #include @@ -561,7 +560,7 @@ void StorageDistributed::alter(const AlterCommands & params, const Context & con checkAlterIsPossible(params, context.getSettingsRef()); StorageInMemoryMetadata metadata = getInMemoryMetadata(); - params.apply(metadata); + params.apply(metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, metadata); setColumns(std::move(metadata.columns)); } @@ -649,13 +648,23 @@ StorageDistributedDirectoryMonitor& StorageDistributed::requireDirectoryMonitor( auto & node_data = cluster_nodes_data[key]; if (!node_data.directory_monitor) { - node_data.conneciton_pool = StorageDistributedDirectoryMonitor::createPool(name, *this); + node_data.connection_pool = StorageDistributedDirectoryMonitor::createPool(name, *this); node_data.directory_monitor = std::make_unique( - *this, path, node_data.conneciton_pool, monitors_blocker, global_context->getDistributedSchedulePool()); + *this, path, node_data.connection_pool, monitors_blocker, global_context->getDistributedSchedulePool()); } return *node_data.directory_monitor; } +std::vector StorageDistributed::getDirectoryMonitorsStatuses() const +{ + std::vector statuses; + std::lock_guard lock(cluster_nodes_mutex); + statuses.reserve(cluster_nodes_data.size()); + for (const auto & node : cluster_nodes_data) + statuses.push_back(node.second.directory_monitor->getStatus()); + return statuses; +} + size_t StorageDistributed::getShardCount() const { return getCluster()->getShardCount(); diff --git a/src/Storages/StorageDistributed.h b/src/Storages/StorageDistributed.h index a7e3a073af4..4067012c449 100644 --- a/src/Storages/StorageDistributed.h +++ b/src/Storages/StorageDistributed.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -17,7 +18,6 @@ namespace DB { class Context; -class StorageDistributedDirectoryMonitor; class VolumeJBOD; using VolumeJBODPtr = std::shared_ptr; @@ -107,6 +107,9 @@ public: void createDirectoryMonitors(const std::string & disk); /// ensure directory monitor thread and connectoin pool creation by disk and subdirectory name StorageDistributedDirectoryMonitor & requireDirectoryMonitor(const std::string & disk, const std::string & name); + /// Return list of metrics for all created monitors + /// (note that monitors are created lazily, i.e. until at least one INSERT executed) + std::vector getDirectoryMonitorsStatuses() const; void flushClusterNodesAllData(); @@ -181,13 +184,13 @@ protected: struct ClusterNodeData { std::unique_ptr directory_monitor; - ConnectionPoolPtr conneciton_pool; + ConnectionPoolPtr connection_pool; void flushAllData() const; void shutdownAndDropAllData() const; }; std::unordered_map cluster_nodes_data; - std::mutex cluster_nodes_mutex; + mutable std::mutex cluster_nodes_mutex; }; diff --git a/src/Storages/StorageHDFS.cpp b/src/Storages/StorageHDFS.cpp index 562ea5c9486..352e0a43f39 100644 --- a/src/Storages/StorageHDFS.cpp +++ b/src/Storages/StorageHDFS.cpp @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -28,6 +27,7 @@ #include #include + namespace DB { namespace ErrorCodes diff --git a/src/Storages/StorageInMemoryMetadata.cpp b/src/Storages/StorageInMemoryMetadata.cpp index 6713519151f..ee38637e118 100644 --- a/src/Storages/StorageInMemoryMetadata.cpp +++ b/src/Storages/StorageInMemoryMetadata.cpp @@ -1,28 +1,29 @@ #include +#include #include #include #include #include #include +#include namespace DB { - StorageInMemoryMetadata::StorageInMemoryMetadata( const ColumnsDescription & columns_, - const IndicesDescription & indices_, + const IndicesDescription & secondary_indices_, const ConstraintsDescription & constraints_) : columns(columns_) - , indices(indices_) + , secondary_indices(secondary_indices_) , constraints(constraints_) { } StorageInMemoryMetadata::StorageInMemoryMetadata(const StorageInMemoryMetadata & other) : columns(other.columns) - , indices(other.indices) + , secondary_indices(other.secondary_indices) , constraints(other.constraints) { if (other.partition_by_ast) @@ -47,7 +48,7 @@ StorageInMemoryMetadata & StorageInMemoryMetadata::operator=(const StorageInMemo return *this; columns = other.columns; - indices = other.indices; + secondary_indices = other.secondary_indices; constraints = other.constraints; if (other.partition_by_ast) @@ -88,30 +89,6 @@ StorageInMemoryMetadata & StorageInMemoryMetadata::operator=(const StorageInMemo return *this; } -namespace -{ - ASTPtr extractKeyExpressionList(const ASTPtr & node) - { - if (!node) - return std::make_shared(); - - const auto * expr_func = node->as(); - - if (expr_func && expr_func->name == "tuple") - { - /// Primary key is specified in tuple, extract its arguments. - return expr_func->arguments->clone(); - } - else - { - /// Primary key consists of one column. - auto res = std::make_shared(); - res->children.push_back(node); - return res; - } - } -} - StorageMetadataKeyField StorageMetadataKeyField::getKeyFromAST(const ASTPtr & definition_ast, const ColumnsDescription & columns, const Context & context) { StorageMetadataKeyField result; @@ -138,5 +115,4 @@ StorageMetadataKeyField StorageMetadataKeyField::getKeyFromAST(const ASTPtr & de return result; } - } diff --git a/src/Storages/StorageInMemoryMetadata.h b/src/Storages/StorageInMemoryMetadata.h index 1733cb4a308..39bc8fd2b31 100644 --- a/src/Storages/StorageInMemoryMetadata.h +++ b/src/Storages/StorageInMemoryMetadata.h @@ -3,6 +3,7 @@ #include #include #include +#include #include namespace DB @@ -17,7 +18,7 @@ struct StorageInMemoryMetadata /// defaults, comments, etc. All table engines have columns. ColumnsDescription columns; /// Table indices. Currently supported for MergeTree only. - IndicesDescription indices; + IndicesDescription secondary_indices; /// Table constraints. Currently supported for MergeTree only. ConstraintsDescription constraints; /// PARTITION BY expression. Currently supported for MergeTree only. @@ -38,7 +39,7 @@ struct StorageInMemoryMetadata StorageInMemoryMetadata(const StorageInMemoryMetadata & other); StorageInMemoryMetadata() = default; - StorageInMemoryMetadata(const ColumnsDescription & columns_, const IndicesDescription & indices_, const ConstraintsDescription & constraints_); + StorageInMemoryMetadata(const ColumnsDescription & columns_, const IndicesDescription & secondary_indices_, const ConstraintsDescription & constraints_); StorageInMemoryMetadata & operator=(const StorageInMemoryMetadata & other); }; diff --git a/src/Storages/StorageJoin.cpp b/src/Storages/StorageJoin.cpp index 9b17334e570..5cceefe907b 100644 --- a/src/Storages/StorageJoin.cpp +++ b/src/Storages/StorageJoin.cpp @@ -251,22 +251,26 @@ public: , max_block_size(max_block_size_) , sample_block(std::move(sample_block_)) { - columns.resize(sample_block.columns()); column_indices.resize(sample_block.columns()); - column_with_null.resize(sample_block.columns()); + + auto & saved_block = parent.getJoinedData()->sample_block; + for (size_t i = 0; i < sample_block.columns(); ++i) { auto & [_, type, name] = sample_block.getByPosition(i); if (parent.right_table_keys.has(name)) { key_pos = i; - column_with_null[i] = parent.right_table_keys.getByName(name).type->isNullable(); + const auto & column = parent.right_table_keys.getByName(name); + restored_block.insert(column); } else { - auto pos = parent.sample_block_with_columns_to_add.getPositionByName(name); + size_t pos = saved_block.getPositionByName(name); column_indices[i] = pos; - column_with_null[i] = !parent.sample_block_with_columns_to_add.getByPosition(pos).type->equals(*type); + + const auto & column = saved_block.getByPosition(pos); + restored_block.insert(column); } } } @@ -291,11 +295,10 @@ private: std::shared_lock lock; UInt64 max_block_size; Block sample_block; + Block restored_block; /// sample_block with parent column types ColumnNumbers column_indices; - std::vector column_with_null; std::optional key_pos; - MutableColumns columns; std::unique_ptr> position; /// type erasure @@ -303,23 +306,7 @@ private: template Chunk createChunk(const Maps & maps) { - for (size_t i = 0; i < sample_block.columns(); ++i) - { - const auto & src_col = sample_block.safeGetByPosition(i); - columns[i] = src_col.type->createColumn(); - if (column_with_null[i]) - { - if (key_pos == i) - { - // unwrap null key column - auto & nullable_col = assert_cast(*columns[i]); - columns[i] = nullable_col.getNestedColumnPtr()->assumeMutable(); - } - else - // wrap non key column with null - columns[i] = makeNullable(std::move(columns[i]))->assumeMutable(); - } - } + MutableColumns columns = restored_block.cloneEmpty().mutateColumns(); size_t rows_added = 0; @@ -327,7 +314,7 @@ private: { #define M(TYPE) \ case HashJoin::Type::TYPE: \ - rows_added = fillColumns(*maps.TYPE); \ + rows_added = fillColumns(*maps.TYPE, columns); \ break; APPLY_FOR_JOIN_VARIANTS_LIMITED(M) #undef M @@ -340,29 +327,27 @@ private: if (!rows_added) return {}; - Columns res_columns; - res_columns.reserve(columns.size()); - + /// Correct nullability for (size_t i = 0; i < columns.size(); ++i) - if (column_with_null[i]) - { - if (key_pos == i) - res_columns.emplace_back(makeNullable(std::move(columns[i]))); - else - { - const auto & nullable_col = assert_cast(*columns[i]); - res_columns.emplace_back(makeNullable(nullable_col.getNestedColumnPtr())); - } - } - else - res_columns.emplace_back(std::move(columns[i])); + { + bool src_nullable = restored_block.getByPosition(i).type->isNullable(); + bool dst_nullable = sample_block.getByPosition(i).type->isNullable(); - UInt64 num_rows = res_columns.at(0)->size(); - return Chunk(std::move(res_columns), num_rows); + if (src_nullable && !dst_nullable) + { + auto & nullable_column = assert_cast(*columns[i]); + columns[i] = nullable_column.getNestedColumnPtr()->assumeMutable(); + } + else if (!src_nullable && dst_nullable) + columns[i] = makeNullable(std::move(columns[i]))->assumeMutable(); + } + + UInt64 num_rows = columns.at(0)->size(); + return Chunk(std::move(columns), num_rows); } template - size_t fillColumns(const Map & map) + size_t fillColumns(const Map & map, MutableColumns & columns) { size_t rows_added = 0; diff --git a/src/Storages/StorageMaterializedView.cpp b/src/Storages/StorageMaterializedView.cpp index 2afa7f7d713..f7233d67eca 100644 --- a/src/Storages/StorageMaterializedView.cpp +++ b/src/Storages/StorageMaterializedView.cpp @@ -158,7 +158,7 @@ StorageMaterializedView::StorageMaterializedView( StorageInMemoryMetadata StorageMaterializedView::getInMemoryMetadata() const { - StorageInMemoryMetadata result(getColumns(), getIndices(), getConstraints()); + StorageInMemoryMetadata result(getColumns(), getSecondaryIndices(), getConstraints()); result.select = getSelectQuery(); return result; } @@ -180,8 +180,8 @@ Pipes StorageMaterializedView::read( auto lock = storage->lockStructureForShare( false, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - if (query_info.order_by_optimizer) - query_info.input_sorting_info = query_info.order_by_optimizer->getInputOrder(storage); + if (query_info.order_optimizer) + query_info.input_order_info = query_info.order_optimizer->getInputOrder(storage); Pipes pipes = storage->read(column_names, query_info, context, processed_stage, max_block_size, num_streams); @@ -257,7 +257,7 @@ void StorageMaterializedView::alter( lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); auto table_id = getStorageID(); StorageInMemoryMetadata metadata = getInMemoryMetadata(); - params.apply(metadata); + params.apply(metadata, context); /// start modify query if (context.getSettingsRef().allow_experimental_alter_materialized_view_structure) diff --git a/src/Storages/StorageMerge.cpp b/src/Storages/StorageMerge.cpp index 5bf3f8a3ce2..64d0f11f853 100644 --- a/src/Storages/StorageMerge.cpp +++ b/src/Storages/StorageMerge.cpp @@ -1,11 +1,6 @@ -#include #include -#include -#include #include -#include #include -#include #include #include #include @@ -79,7 +74,7 @@ StoragePtr StorageMerge::getFirstTable(F && predicate) const bool StorageMerge::isRemote() const { - auto first_remote_table = getFirstTable([](const StoragePtr & table) { return table->isRemote(); }); + auto first_remote_table = getFirstTable([](const StoragePtr & table) { return table && table->isRemote(); }); return first_remote_table != nullptr; } @@ -117,7 +112,7 @@ QueryProcessingStage::Enum StorageMerge::getQueryProcessingStage(const Context & while (iterator->isValid()) { const auto & table = iterator->table(); - if (table.get() != this) + if (table && table.get() != this) { ++selected_table_size; stage_in_source_tables = std::max(stage_in_source_tables, table->getQueryProcessingStage(context, to_stage, query_ptr)); @@ -177,12 +172,12 @@ Pipes StorageMerge::read( num_streams *= num_streams_multiplier; size_t remaining_streams = num_streams; - InputSortingInfoPtr input_sorting_info; - if (query_info.order_by_optimizer) + InputOrderInfoPtr input_sorting_info; + if (query_info.order_optimizer) { for (auto it = selected_tables.begin(); it != selected_tables.end(); ++it) { - auto current_info = query_info.order_by_optimizer->getInputOrder(std::get<0>(*it)); + auto current_info = query_info.order_optimizer->getInputOrder(std::get<0>(*it)); if (it == selected_tables.begin()) input_sorting_info = current_info; else if (!current_info || (input_sorting_info && *current_info != *input_sorting_info)) @@ -192,7 +187,7 @@ Pipes StorageMerge::read( break; } - query_info.input_sorting_info = input_sorting_info; + query_info.input_order_info = input_sorting_info; } for (const auto & table : selected_tables) @@ -316,7 +311,7 @@ StorageMerge::StorageListWithLocks StorageMerge::getSelectedTables(const String while (iterator->isValid()) { const auto & table = iterator->table(); - if (table.get() != this) + if (table && table.get() != this) selected_tables.emplace_back( table, table->lockStructureForShare(false, query_id, settings.lock_acquire_timeout), iterator->name()); @@ -338,6 +333,8 @@ StorageMerge::StorageListWithLocks StorageMerge::getSelectedTables( while (iterator->isValid()) { StoragePtr storage = iterator->table(); + if (!storage) + continue; if (query && query->as()->prewhere() && !storage->supportsPrewhere()) throw Exception("Storage " + storage->getName() + " doesn't support PREWHERE.", ErrorCodes::ILLEGAL_PREWHERE); @@ -394,7 +391,7 @@ void StorageMerge::alter( auto table_id = getStorageID(); StorageInMemoryMetadata storage_metadata = getInMemoryMetadata(); - params.apply(storage_metadata); + params.apply(storage_metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, storage_metadata); setColumns(storage_metadata.columns); } @@ -495,7 +492,7 @@ NamesAndTypesList StorageMerge::getVirtuals() const { NamesAndTypesList virtuals{{"_table", std::make_shared()}}; - auto first_table = getFirstTable([](auto &&) { return true; }); + auto first_table = getFirstTable([](auto && table) { return table; }); if (first_table) { auto table_virtuals = first_table->getVirtuals(); diff --git a/src/Storages/StorageMergeTree.cpp b/src/Storages/StorageMergeTree.cpp index c6af41cc163..15e662b27b5 100644 --- a/src/Storages/StorageMergeTree.cpp +++ b/src/Storages/StorageMergeTree.cpp @@ -95,16 +95,36 @@ void StorageMergeTree::startup() /// NOTE background task will also do the above cleanups periodically. time_after_previous_cleanup.restart(); - auto & merge_pool = global_context.getBackgroundPool(); - merging_mutating_task_handle = merge_pool.createTask([this] { return mergeMutateTask(); }); - /// Ensure that thread started only after assignment to 'merging_mutating_task_handle' is done. - merge_pool.startTask(merging_mutating_task_handle); - - if (areBackgroundMovesNeeded()) + try { - auto & move_pool = global_context.getBackgroundMovePool(); - moving_task_handle = move_pool.createTask([this] { return movePartsTask(); }); - move_pool.startTask(moving_task_handle); + auto & merge_pool = global_context.getBackgroundPool(); + merging_mutating_task_handle = merge_pool.createTask([this] { return mergeMutateTask(); }); + /// Ensure that thread started only after assignment to 'merging_mutating_task_handle' is done. + merge_pool.startTask(merging_mutating_task_handle); + + if (areBackgroundMovesNeeded()) + { + auto & move_pool = global_context.getBackgroundMovePool(); + moving_task_handle = move_pool.createTask([this] { return movePartsTask(); }); + move_pool.startTask(moving_task_handle); + } + } + catch (...) + { + /// Exception safety: failed "startup" does not require a call to "shutdown" from the caller. + /// And it should be able to safely destroy table after exception in "startup" method. + /// It means that failed "startup" must not create any background tasks that we will have to wait. + try + { + shutdown(); + } + catch (...) + { + std::terminate(); + } + + /// Note: after failed "startup", the table will be in a state that only allows to destroy the object. + throw; } } @@ -121,16 +141,6 @@ void StorageMergeTree::shutdown() mutation_wait_event.notify_all(); } - try - { - clearOldPartsFromFilesystem(true); - } - catch (...) - { - /// Example: the case of readonly filesystem, we have failure removing old parts. - /// Should not prevent table shutdown. - tryLogCurrentException(log); - } merger_mutator.merges_blocker.cancelForever(); parts_mover.moves_blocker.cancelForever(); @@ -140,6 +150,23 @@ void StorageMergeTree::shutdown() if (moving_task_handle) global_context.getBackgroundMovePool().removeTask(moving_task_handle); + + + try + { + /// We clear all old parts after stopping all background operations. + /// It's important, because background operations can produce temporary + /// parts which will remove themselves in their descrutors. If so, we + /// may have race condition between our remove call and background + /// process. + clearOldPartsFromFilesystem(true); + } + catch (...) + { + /// Example: the case of readonly filesystem, we have failure removing old parts. + /// Should not prevent table shutdown. + tryLogCurrentException(log); + } } @@ -230,8 +257,10 @@ void StorageMergeTree::alter( auto table_id = getStorageID(); StorageInMemoryMetadata metadata = getInMemoryMetadata(); - auto maybe_mutation_commands = commands.getMutationCommands(metadata, context.getSettingsRef().materialize_ttl_after_modify); - commands.apply(metadata); + auto maybe_mutation_commands = commands.getMutationCommands(metadata, context.getSettingsRef().materialize_ttl_after_modify, context); + String mutation_file_name; + Int64 mutation_version = -1; + commands.apply(metadata, context); /// This alter can be performed at metadata level only if (commands.isSettingsAlter()) @@ -244,24 +273,25 @@ void StorageMergeTree::alter( } else { - lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); + { + /// TODO (relax this lock and remove this action lock) + auto merges_block = getActionLock(ActionLocks::PartsMerge); + lockStructureExclusively(table_lock_holder, context.getCurrentQueryId(), context.getSettingsRef().lock_acquire_timeout); - changeSettings(metadata.settings_ast, table_lock_holder); - /// Reinitialize primary key because primary key column types might have changed. - setProperties(metadata); + changeSettings(metadata.settings_ast, table_lock_holder); + /// Reinitialize primary key because primary key column types might have changed. + setProperties(metadata); - setTTLExpressions(metadata.columns, metadata.ttl_for_table_ast); + setTTLExpressions(metadata.columns, metadata.ttl_for_table_ast); - DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, metadata); + DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, metadata); - String mutation_file_name; - Int64 mutation_version = -1; - if (!maybe_mutation_commands.empty()) - mutation_version = startMutation(maybe_mutation_commands, mutation_file_name); - - /// We release all locks except alter_intention_lock which allows - /// to execute alter queries sequentially - table_lock_holder.releaseAllExceptAlterIntention(); + if (!maybe_mutation_commands.empty()) + mutation_version = startMutation(maybe_mutation_commands, mutation_file_name); + /// We release all locks except alter_intention_lock which allows + /// to execute alter queries sequentially + table_lock_holder.releaseAllExceptAlterIntention(); + } /// Always execute required mutations synchronously, because alters /// should be executed in sequential order. @@ -365,6 +395,8 @@ public: entry.latest_fail_reason = exception_message; } } + + storage.currently_processing_in_background_condition.notify_all(); } }; @@ -385,7 +417,7 @@ Int64 StorageMergeTree::startMutation(const MutationCommands & commands, String current_mutations_by_version.emplace(version, insertion.first->second); LOG_INFO(log, "Added mutation: {}", mutation_file_name); - merging_mutating_task_handle->wake(); + merging_mutating_task_handle->signalReadyToRun(); return version; } @@ -518,7 +550,7 @@ CancellationCode StorageMergeTree::killMutation(const String & mutation_id) } /// Maybe there is another mutation that was blocked by the killed one. Try to execute it immediately. - merging_mutating_task_handle->wake(); + merging_mutating_task_handle->signalReadyToRun(); return CancellationCode::CancelSent; } @@ -566,7 +598,7 @@ bool StorageMergeTree::merge( std::optional merging_tagger; { - std::lock_guard lock(currently_processing_in_background_mutex); + std::unique_lock lock(currently_processing_in_background_mutex); auto can_merge = [this, &lock] (const DataPartPtr & left, const DataPartPtr & right, String *) -> bool { @@ -590,8 +622,33 @@ bool StorageMergeTree::merge( } else { - UInt64 disk_space = getStoragePolicy()->getMaxUnreservedFreeSpace(); - selected = merger_mutator.selectAllPartsToMergeWithinPartition(future_part, disk_space, can_merge, partition_id, final, out_disable_reason); + while (true) + { + UInt64 disk_space = getStoragePolicy()->getMaxUnreservedFreeSpace(); + selected = merger_mutator.selectAllPartsToMergeWithinPartition( + future_part, disk_space, can_merge, partition_id, final, out_disable_reason); + + /// If final - we will wait for currently processing merges to finish and continue. + /// TODO Respect query settings for timeout + if (final + && !selected + && !currently_merging_mutating_parts.empty() + && out_disable_reason + && out_disable_reason->empty()) + { + LOG_DEBUG(log, "Waiting for currently running merges ({} parts are merging right now) to perform OPTIMIZE FINAL", + currently_merging_mutating_parts.size()); + + if (std::cv_status::timeout == currently_processing_in_background_condition.wait_for( + lock, std::chrono::seconds(DBMS_DEFAULT_LOCK_ACQUIRE_TIMEOUT_SEC))) + { + *out_disable_reason = "Timeout while waiting for already running merges before running OPTIMIZE with FINAL"; + break; + } + } + else + break; + } } if (!selected) @@ -847,7 +904,7 @@ BackgroundProcessingPoolTaskResult StorageMergeTree::mergeMutateTask() Int64 StorageMergeTree::getCurrentMutationVersion( const DataPartPtr & part, - std::lock_guard & /* currently_processing_in_background_mutex_lock */) const + std::unique_lock & /* currently_processing_in_background_mutex_lock */) const { auto it = current_mutations_by_version.upper_bound(part->info.getDataVersion()); if (it == current_mutations_by_version.begin()) diff --git a/src/Storages/StorageMergeTree.h b/src/Storages/StorageMergeTree.h index 473177abf1e..c6c8f99a62a 100644 --- a/src/Storages/StorageMergeTree.h +++ b/src/Storages/StorageMergeTree.h @@ -95,6 +95,7 @@ private: /// Mutex for parts currently processing in background /// merging (also with TTL), mutating or moving. mutable std::mutex currently_processing_in_background_mutex; + mutable std::condition_variable currently_processing_in_background_condition; /// Parts that currently participate in merge or mutation. /// This set have to be used with `currently_processing_in_background_mutex`. @@ -133,7 +134,7 @@ private: Int64 getCurrentMutationVersion( const DataPartPtr & part, - std::lock_guard & /* currently_processing_in_background_mutex_lock */) const; + std::unique_lock & /* currently_processing_in_background_mutex_lock */) const; void clearOldMutations(bool truncate = false); diff --git a/src/Storages/StorageNull.cpp b/src/Storages/StorageNull.cpp index a0044723e07..c29562325fa 100644 --- a/src/Storages/StorageNull.cpp +++ b/src/Storages/StorageNull.cpp @@ -52,7 +52,7 @@ void StorageNull::alter( auto table_id = getStorageID(); StorageInMemoryMetadata metadata = getInMemoryMetadata(); - params.apply(metadata); + params.apply(metadata, context); DatabaseCatalog::instance().getDatabase(table_id.database_name)->alterTable(context, table_id, metadata); setColumns(std::move(metadata.columns)); } diff --git a/src/Storages/StorageReplicatedMergeTree.cpp b/src/Storages/StorageReplicatedMergeTree.cpp index 00565e777ae..5931bca17ea 100644 --- a/src/Storages/StorageReplicatedMergeTree.cpp +++ b/src/Storages/StorageReplicatedMergeTree.cpp @@ -194,15 +194,19 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( zookeeper_path = "/" + zookeeper_path; replica_path = zookeeper_path + "/replicas/" + replica_name; - queue_updating_task = global_context.getSchedulePool().createTask(getStorageID().getFullTableName() + " (StorageReplicatedMergeTree::queueUpdatingTask)", [this]{ queueUpdatingTask(); }); + queue_updating_task = global_context.getSchedulePool().createTask( + getStorageID().getFullTableName() + " (StorageReplicatedMergeTree::queueUpdatingTask)", [this]{ queueUpdatingTask(); }); - mutations_updating_task = global_context.getSchedulePool().createTask(getStorageID().getFullTableName() + " (StorageReplicatedMergeTree::mutationsUpdatingTask)", [this]{ mutationsUpdatingTask(); }); + mutations_updating_task = global_context.getSchedulePool().createTask( + getStorageID().getFullTableName() + " (StorageReplicatedMergeTree::mutationsUpdatingTask)", [this]{ mutationsUpdatingTask(); }); - merge_selecting_task = global_context.getSchedulePool().createTask(getStorageID().getFullTableName() + " (StorageReplicatedMergeTree::mergeSelectingTask)", [this] { mergeSelectingTask(); }); + merge_selecting_task = global_context.getSchedulePool().createTask( + getStorageID().getFullTableName() + " (StorageReplicatedMergeTree::mergeSelectingTask)", [this] { mergeSelectingTask(); }); /// Will be activated if we win leader election. merge_selecting_task->deactivate(); - mutations_finalizing_task = global_context.getSchedulePool().createTask(getStorageID().getFullTableName() + " (StorageReplicatedMergeTree::mutationsFinalizingTask)", [this] { mutationsFinalizingTask(); }); + mutations_finalizing_task = global_context.getSchedulePool().createTask( + getStorageID().getFullTableName() + " (StorageReplicatedMergeTree::mutationsFinalizingTask)", [this] { mutationsFinalizingTask(); }); if (global_context.hasZooKeeper()) current_zookeeper = global_context.getZooKeeper(); @@ -240,6 +244,7 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( { LOG_WARNING(log, "No metadata in ZooKeeper: table will be in readonly mode."); is_readonly = true; + has_metadata_in_zookeeper = false; return; } @@ -248,20 +253,30 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree( if (!getDataParts().empty()) throw Exception("Data directory for table already containing data parts - probably it was unclean DROP table or manual intervention. You must either clear directory by hand or use ATTACH TABLE instead of CREATE TABLE if you need to use that parts.", ErrorCodes::INCORRECT_DATA); - createTableIfNotExists(); + try + { + bool is_first_replica = createTableIfNotExists(); - /// We have to check granularity on other replicas. If it's fixed we - /// must create our new replica with fixed granularity and store this - /// information in /replica/metadata. - other_replicas_fixed_granularity = checkFixedGranualrityInZookeeper(); + /// We have to check granularity on other replicas. If it's fixed we + /// must create our new replica with fixed granularity and store this + /// information in /replica/metadata. + other_replicas_fixed_granularity = checkFixedGranualrityInZookeeper(); - checkTableStructure(zookeeper_path); + checkTableStructure(zookeeper_path); - Coordination::Stat metadata_stat; - current_zookeeper->get(zookeeper_path + "/metadata", &metadata_stat); - metadata_version = metadata_stat.version; + Coordination::Stat metadata_stat; + current_zookeeper->get(zookeeper_path + "/metadata", &metadata_stat); + metadata_version = metadata_stat.version; - createReplica(); + if (!is_first_replica) + createReplica(); + } + catch (...) + { + /// If replica was not created, rollback creation of data directory. + dropIfEmpty(); + throw; + } } else { @@ -403,46 +418,310 @@ void StorageReplicatedMergeTree::createNewZooKeeperNodes() } -void StorageReplicatedMergeTree::createTableIfNotExists() +bool StorageReplicatedMergeTree::createTableIfNotExists() +{ + auto zookeeper = getZooKeeper(); + zookeeper->createAncestors(zookeeper_path); + + for (size_t i = 0; i < 1000; ++i) + { + /// Invariant: "replicas" does not exist if there is no table or if there are leftovers from incompletely dropped table. + if (zookeeper->exists(zookeeper_path + "/replicas")) + { + LOG_DEBUG(log, "This table {} is already created, will add new replica", zookeeper_path); + return false; + } + + /// There are leftovers from incompletely dropped table. + if (zookeeper->exists(zookeeper_path + "/dropped")) + { + /// This condition may happen when the previous drop attempt was not completed + /// or when table is dropped by another replica right now. + /// This is Ok because another replica is definitely going to drop the table. + + LOG_WARNING(log, "Removing leftovers from table {} (this might take several minutes)", zookeeper_path); + + Strings children; + Coordination::Error code = zookeeper->tryGetChildren(zookeeper_path, children); + if (code == Coordination::Error::ZNONODE) + { + LOG_WARNING(log, "Table {} is already finished removing by another replica right now", replica_path); + } + else + { + for (const auto & child : children) + if (child != "dropped") + zookeeper->tryRemoveRecursive(zookeeper_path + "/" + child); + + Coordination::Requests ops; + Coordination::Responses responses; + ops.emplace_back(zkutil::makeRemoveRequest(zookeeper_path + "/dropped", -1)); + ops.emplace_back(zkutil::makeRemoveRequest(zookeeper_path, -1)); + code = zookeeper->tryMulti(ops, responses); + + if (code == Coordination::Error::ZNONODE) + { + LOG_WARNING(log, "Table {} is already finished removing by another replica right now", replica_path); + } + else if (code == Coordination::Error::ZNOTEMPTY) + { + throw Exception(fmt::format( + "The old table was not completely removed from ZooKeeper, {} still exists and may contain some garbage. But it should never happen according to the logic of operations (it's a bug).", zookeeper_path), ErrorCodes::LOGICAL_ERROR); + } + else if (code != Coordination::Error::ZOK) + { + /// It is still possible that ZooKeeper session is expired or server is killed in the middle of the delete operation. + zkutil::KeeperMultiException::check(code, ops, responses); + } + else + { + LOG_WARNING(log, "The leftovers from table {} was successfully removed from ZooKeeper", zookeeper_path); + } + } + } + + LOG_DEBUG(log, "Creating table {}", zookeeper_path); + + /// We write metadata of table so that the replicas can check table parameters with them. + String metadata = ReplicatedMergeTreeTableMetadata(*this).toString(); + + Coordination::Requests ops; + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path, "", zkutil::CreateMode::Persistent)); + + /// Check that the table is not being dropped right now. + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/dropped", "", zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeRemoveRequest(zookeeper_path + "/dropped", -1)); + + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/metadata", metadata, + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/columns", getColumns().toString(), + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/log", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/blocks", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/block_numbers", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/nonincrement_block_numbers", "", + zkutil::CreateMode::Persistent)); /// /nonincrement_block_numbers dir is unused, but is created nonetheless for backwards compatibility. + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/leader_election", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/temp", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/replicas", "last added replica: " + replica_name, + zkutil::CreateMode::Persistent)); + + /// And create first replica atomically. See also "createReplica" method that is used to create not the first replicas. + + ops.emplace_back(zkutil::makeCreateRequest(replica_path, "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/host", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/log_pointer", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/queue", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/parts", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/flags", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/is_lost", "0", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata", metadata, + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/columns", getColumns().toString(), + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata_version", std::to_string(metadata_version), + zkutil::CreateMode::Persistent)); + + Coordination::Responses responses; + auto code = zookeeper->tryMulti(ops, responses); + if (code == Coordination::Error::ZNODEEXISTS) + { + LOG_WARNING(log, "It looks like the table {} was created by another server at the same moment, will retry", zookeeper_path); + continue; + } + else if (code != Coordination::Error::ZOK) + { + zkutil::KeeperMultiException::check(code, ops, responses); + } + + return true; + } + + throw Exception("Cannot create table, because it is created concurrently every time or because of logical error", ErrorCodes::LOGICAL_ERROR); +} + +void StorageReplicatedMergeTree::createReplica() { auto zookeeper = getZooKeeper(); - if (zookeeper->exists(zookeeper_path)) - return; + LOG_DEBUG(log, "Creating replica {}", replica_path); - LOG_DEBUG(log, "Creating table {}", zookeeper_path); + Coordination::Error code; - zookeeper->createAncestors(zookeeper_path); + do + { + Coordination::Stat replicas_stat; + String replicas_value; - /// We write metadata of table so that the replicas can check table parameters with them. - String metadata = ReplicatedMergeTreeTableMetadata(*this).toString(); + if (!zookeeper->tryGet(zookeeper_path + "/replicas", replicas_value, &replicas_stat)) + throw Exception(fmt::format("Cannot create a replica of the table {}, because the last replica of the table was dropped right now", + zookeeper_path), ErrorCodes::ALL_REPLICAS_LOST); - Coordination::Requests ops; - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path, "", - zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/metadata", metadata, - zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/columns", getColumns().toString(), - zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/log", "", - zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/blocks", "", - zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/block_numbers", "", - zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/nonincrement_block_numbers", "", - zkutil::CreateMode::Persistent)); /// /nonincrement_block_numbers dir is unused, but is created nonetheless for backwards compatibility. - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/leader_election", "", - zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/temp", "", - zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/replicas", "", - zkutil::CreateMode::Persistent)); + /// It is not the first replica, we will mark it as "lost", to immediately repair (clone) from existing replica. + /// By the way, it's possible that the replica will be first, if all previous replicas were removed concurrently. + String is_lost_value = replicas_stat.numChildren ? "1" : "0"; - Coordination::Responses responses; - auto code = zookeeper->tryMulti(ops, responses); - if (code && code != Coordination::ZNODEEXISTS) - throw Coordination::Exception(code); + Coordination::Requests ops; + ops.emplace_back(zkutil::makeCreateRequest(replica_path, "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/host", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/log_pointer", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/queue", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/parts", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/flags", "", + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/is_lost", is_lost_value, + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata", ReplicatedMergeTreeTableMetadata(*this).toString(), + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/columns", getColumns().toString(), + zkutil::CreateMode::Persistent)); + ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata_version", std::to_string(metadata_version), + zkutil::CreateMode::Persistent)); + + /// Check version of /replicas to see if there are any replicas created at the same moment of time. + ops.emplace_back(zkutil::makeSetRequest(zookeeper_path + "/replicas", "last added replica: " + replica_name, replicas_stat.version)); + + Coordination::Responses responses; + code = zookeeper->tryMulti(ops, responses); + if (code == Coordination::Error::ZNODEEXISTS) + { + throw Exception("Replica " + replica_path + " already exists.", ErrorCodes::REPLICA_IS_ALREADY_EXIST); + } + else if (code == Coordination::Error::ZBADVERSION) + { + LOG_ERROR(log, "Retrying createReplica(), because some other replicas were created at the same time"); + } + else if (code == Coordination::Error::ZNONODE) + { + throw Exception("Table " + zookeeper_path + " was suddenly removed.", ErrorCodes::ALL_REPLICAS_LOST); + } + else + { + zkutil::KeeperMultiException::check(code, ops, responses); + } + } while (code == Coordination::Error::ZBADVERSION); +} + +void StorageReplicatedMergeTree::drop() +{ + /// There is also the case when user has configured ClickHouse to wrong ZooKeeper cluster, + /// in this case, has_metadata_in_zookeeper = false, and we also permit to drop the table. + + if (has_metadata_in_zookeeper) + { + auto zookeeper = tryGetZooKeeper(); + + /// If probably there is metadata in ZooKeeper, we don't allow to drop the table. + if (is_readonly || !zookeeper) + throw Exception("Can't drop readonly replicated table (need to drop data in ZooKeeper as well)", ErrorCodes::TABLE_IS_READ_ONLY); + + shutdown(); + + if (zookeeper->expired()) + throw Exception("Table was not dropped because ZooKeeper session has expired.", ErrorCodes::TABLE_WAS_NOT_DROPPED); + + LOG_INFO(log, "Removing replica {}", replica_path); + replica_is_active_node = nullptr; + /// It may left some garbage if replica_path subtree are concurently modified + zookeeper->tryRemoveRecursive(replica_path); + if (zookeeper->exists(replica_path)) + LOG_ERROR(log, "Replica was not completely removed from ZooKeeper, {} still exists and may contain some garbage.", replica_path); + + /// Check that `zookeeper_path` exists: it could have been deleted by another replica after execution of previous line. + Strings replicas; + if (Coordination::Error::ZOK == zookeeper->tryGetChildren(zookeeper_path + "/replicas", replicas) && replicas.empty()) + { + LOG_INFO(log, "{} is the last replica, will remove table", replica_path); + + /** At this moment, another replica can be created and we cannot remove the table. + * Try to remove /replicas node first. If we successfully removed it, + * it guarantees that we are the only replica that proceed to remove the table + * and no new replicas can be created after that moment (it requires the existence of /replicas node). + * and table cannot be recreated with new /replicas node on another servers while we are removing data, + * because table creation is executed in single transaction that will conflict with remaining nodes. + */ + + Coordination::Requests ops; + Coordination::Responses responses; + ops.emplace_back(zkutil::makeRemoveRequest(zookeeper_path + "/replicas", -1)); + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/dropped", "", zkutil::CreateMode::Persistent)); + Coordination::Error code = zookeeper->tryMulti(ops, responses); + + if (code == Coordination::Error::ZNONODE || code == Coordination::Error::ZNODEEXISTS) + { + LOG_WARNING(log, "Table {} is already started to be removing by another replica right now", replica_path); + } + else if (code == Coordination::Error::ZNOTEMPTY) + { + LOG_WARNING(log, "Another replica was suddenly created, will keep the table {}", replica_path); + } + else if (code != Coordination::Error::ZOK) + { + zkutil::KeeperMultiException::check(code, ops, responses); + } + else + { + LOG_INFO(log, "Removing table {} (this might take several minutes)", zookeeper_path); + + Strings children; + code = zookeeper->tryGetChildren(zookeeper_path, children); + if (code == Coordination::Error::ZNONODE) + { + LOG_WARNING(log, "Table {} is already finished removing by another replica right now", replica_path); + } + else + { + for (const auto & child : children) + if (child != "dropped") + zookeeper->tryRemoveRecursive(zookeeper_path + "/" + child); + + ops.clear(); + responses.clear(); + ops.emplace_back(zkutil::makeRemoveRequest(zookeeper_path + "/dropped", -1)); + ops.emplace_back(zkutil::makeRemoveRequest(zookeeper_path, -1)); + code = zookeeper->tryMulti(ops, responses); + + if (code == Coordination::Error::ZNONODE) + { + LOG_WARNING(log, "Table {} is already finished removing by another replica right now", replica_path); + } + else if (code == Coordination::Error::ZNOTEMPTY) + { + LOG_ERROR(log, "Table was not completely removed from ZooKeeper, {} still exists and may contain some garbage.", + zookeeper_path); + } + else if (code != Coordination::Error::ZOK) + { + /// It is still possible that ZooKeeper session is expired or server is killed in the middle of the delete operation. + zkutil::KeeperMultiException::check(code, ops, responses); + } + else + { + LOG_INFO(log, "Table {} was successfully removed from ZooKeeper", zookeeper_path); + } + } + } + } + } + + dropAllData(); } @@ -501,7 +780,7 @@ void StorageReplicatedMergeTree::setTableStructure(ColumnsDescription new_column } if (metadata_diff.skip_indices_changed) - metadata.indices = IndicesDescription::parse(metadata_diff.new_skip_indices); + metadata.secondary_indices = IndicesDescription::parse(metadata_diff.new_skip_indices, new_columns, global_context); if (metadata_diff.constraints_changed) metadata.constraints = ConstraintsDescription::parse(metadata_diff.new_constraints); @@ -542,48 +821,6 @@ static time_t tryGetPartCreateTime(zkutil::ZooKeeperPtr & zookeeper, const Strin } -void StorageReplicatedMergeTree::createReplica() -{ - auto zookeeper = getZooKeeper(); - - LOG_DEBUG(log, "Creating replica {}", replica_path); - - int32_t code; - - do - { - Coordination::Stat replicas_stat; - String last_added_replica = zookeeper->get(zookeeper_path + "/replicas", &replicas_stat); - - /// If it is not the first replica, we will mark it as "lost", to immediately repair (clone) from existing replica. - String is_lost_value = last_added_replica.empty() ? "0" : "1"; - - Coordination::Requests ops; - Coordination::Responses responses; - ops.emplace_back(zkutil::makeCreateRequest(replica_path, "", zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/host", "", zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/log_pointer", "", zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/queue", "", zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/parts", "", zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/flags", "", zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/is_lost", is_lost_value, zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata", ReplicatedMergeTreeTableMetadata(*this).toString(), zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/columns", getColumns().toString(), zkutil::CreateMode::Persistent)); - ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/metadata_version", std::to_string(metadata_version), zkutil::CreateMode::Persistent)); - /// Check version of /replicas to see if there are any replicas created at the same moment of time. - ops.emplace_back(zkutil::makeSetRequest(zookeeper_path + "/replicas", "last added replica: " + replica_name, replicas_stat.version)); - - code = zookeeper->tryMulti(ops, responses); - if (code == Coordination::Error::ZNODEEXISTS) - throw Exception("Replica " + replica_path + " already exists.", ErrorCodes::REPLICA_IS_ALREADY_EXIST); - else if (code == Coordination::Error::ZBADVERSION) - LOG_ERROR(log, "Retrying createReplica(), because some other replicas were created at the same time"); - else - zkutil::KeeperMultiException::check(code, ops, responses); - } while (code == Coordination::Error::ZBADVERSION); -} - - void StorageReplicatedMergeTree::checkParts(bool skip_sanity_checks) { auto zookeeper = getZooKeeper(); @@ -705,7 +942,7 @@ void StorageReplicatedMergeTree::checkParts(bool skip_sanity_checks) time_t part_create_time = 0; Coordination::ExistsResponse exists_resp = exists_futures[i].get(); - if (!exists_resp.error) + if (exists_resp.error == Coordination::Error::ZOK) { part_create_time = exists_resp.stat.ctime / 1000; removePartFromZooKeeper(part_name, ops, exists_resp.stat.numChildren > 0); @@ -876,7 +1113,7 @@ MergeTreeData::DataPartsVector StorageReplicatedMergeTree::checkPartChecksumsAnd size_t num_check_ops = 2 * absent_part_paths_on_replicas.size(); size_t failed_op_index = e.failed_op_index; - if (failed_op_index < num_check_ops && e.code == Coordination::ZNODEEXISTS) + if (failed_op_index < num_check_ops && e.code == Coordination::Error::ZNODEEXISTS) { LOG_INFO(log, "The part {} on a replica suddenly appeared, will recheck checksums", e.getPathForFirstFailedOp()); } @@ -1353,15 +1590,15 @@ bool StorageReplicatedMergeTree::executeFetch(LogEntry & entry) Coordination::Responses responses; auto code = zookeeper->tryMulti(ops, responses); - if (code == Coordination::ZOK) + if (code == Coordination::Error::ZOK) { LOG_DEBUG(log, "Marked quorum for part {} as failed.", entry.new_part_name); queue.removeFromVirtualParts(part_info); return true; } - else if (code == Coordination::ZBADVERSION || code == Coordination::ZNONODE || code == Coordination::ZNODEEXISTS) + else if (code == Coordination::Error::ZBADVERSION || code == Coordination::Error::ZNONODE || code == Coordination::Error::ZNODEEXISTS) { - LOG_DEBUG(log, "State was changed or isn't expected when trying to mark quorum for part {} as failed. Code: {}", entry.new_part_name, zkutil::ZooKeeper::error2string(code)); + LOG_DEBUG(log, "State was changed or isn't expected when trying to mark quorum for part {} as failed. Code: {}", entry.new_part_name, Coordination::errorMessage(code)); } else throw Coordination::Exception(code); @@ -1857,7 +2094,7 @@ void StorageReplicatedMergeTree::cloneReplica(const String & source_replica, Coo auto rc = zookeeper->tryMulti(ops, responses); - if (rc == Coordination::ZOK) + if (rc == Coordination::Error::ZOK) { break; } @@ -2025,7 +2262,7 @@ void StorageReplicatedMergeTree::queueUpdatingTask() { tryLogCurrentException(log, __PRETTY_FUNCTION__); - if (e.code == Coordination::ZSESSIONEXPIRED) + if (e.code == Coordination::Error::ZSESSIONEXPIRED) { restarting_thread.wakeup(); return; @@ -2051,7 +2288,7 @@ void StorageReplicatedMergeTree::mutationsUpdatingTask() { tryLogCurrentException(log, __PRETTY_FUNCTION__); - if (e.code == Coordination::ZSESSIONEXPIRED) + if (e.code == Coordination::Error::ZSESSIONEXPIRED) return; mutations_updating_task->scheduleAfter(QUEUE_UPDATE_ERROR_SLEEP_MS); @@ -2171,7 +2408,7 @@ void StorageReplicatedMergeTree::mergeSelectingTask() const bool deduplicate = false; /// TODO: read deduplicate option from table config const bool force_ttl = false; - bool success = false; + CreateMergeEntryResult create_result = CreateMergeEntryResult::Other; try { @@ -2191,7 +2428,11 @@ void StorageReplicatedMergeTree::mergeSelectingTask() size_t merges_and_mutations_sum = merges_and_mutations_queued.first + merges_and_mutations_queued.second; if (merges_and_mutations_sum >= storage_settings_ptr->max_replicated_merges_in_queue) { - LOG_TRACE(log, "Number of queued merges ({}) and part mutations ({}) is greater than max_replicated_merges_in_queue ({}), so won't select new parts to merge or mutate.", merges_and_mutations_queued.first, merges_and_mutations_queued.second, storage_settings_ptr->max_replicated_merges_in_queue); + LOG_TRACE(log, "Number of queued merges ({}) and part mutations ({})" + " is greater than max_replicated_merges_in_queue ({}), so won't select new parts to merge or mutate.", + merges_and_mutations_queued.first, + merges_and_mutations_queued.second, + storage_settings_ptr->max_replicated_merges_in_queue); } else { @@ -2203,10 +2444,10 @@ void StorageReplicatedMergeTree::mergeSelectingTask() if (max_source_parts_size_for_merge > 0 && merger_mutator.selectPartsToMerge(future_merged_part, false, max_source_parts_size_for_merge, merge_pred, nullptr)) { - success = createLogEntryToMergeParts(zookeeper, future_merged_part.parts, - future_merged_part.name, future_merged_part.type, deduplicate, force_ttl); + create_result = createLogEntryToMergeParts(zookeeper, future_merged_part.parts, + future_merged_part.name, future_merged_part.type, deduplicate, force_ttl, nullptr, merge_pred.getVersion()); } - /// If there are many mutations in queue it may happen, that we cannot enqueue enough merges to merge all new parts + /// If there are many mutations in queue, it may happen, that we cannot enqueue enough merges to merge all new parts else if (max_source_part_size_for_mutation > 0 && queue.countMutations() > 0 && merges_and_mutations_queued.second < storage_settings_ptr->max_replicated_mutations_in_queue) { @@ -2221,11 +2462,11 @@ void StorageReplicatedMergeTree::mergeSelectingTask() if (!desired_mutation_version) continue; - if (createLogEntryToMutatePart(*part, desired_mutation_version->first, desired_mutation_version->second)) - { - success = true; + create_result = createLogEntryToMutatePart(*part, + desired_mutation_version->first, desired_mutation_version->second, merge_pred.getVersion()); + + if (create_result == CreateMergeEntryResult::Ok) break; - } } } } @@ -2238,11 +2479,15 @@ void StorageReplicatedMergeTree::mergeSelectingTask() if (!is_leader) return; - if (!success) + if (create_result != CreateMergeEntryResult::Ok + && create_result != CreateMergeEntryResult::LogUpdated) + { merge_selecting_task->scheduleAfter(MERGE_SELECTING_SLEEP_MS); + } else + { merge_selecting_task->schedule(); - + } } @@ -2276,14 +2521,15 @@ void StorageReplicatedMergeTree::mutationsFinalizingTask() } -bool StorageReplicatedMergeTree::createLogEntryToMergeParts( +StorageReplicatedMergeTree::CreateMergeEntryResult StorageReplicatedMergeTree::createLogEntryToMergeParts( zkutil::ZooKeeperPtr & zookeeper, const DataPartsVector & parts, const String & merged_name, const MergeTreeDataPartType & merged_part_type, bool deduplicate, bool force_ttl, - ReplicatedMergeTreeLogEntryData * out_log_entry) + ReplicatedMergeTreeLogEntryData * out_log_entry, + int32_t log_version) { std::vector> exists_futures; exists_futures.reserve(parts.size()); @@ -2294,7 +2540,7 @@ bool StorageReplicatedMergeTree::createLogEntryToMergeParts( for (size_t i = 0; i < parts.size(); ++i) { /// If there is no information about part in ZK, we will not merge it. - if (exists_futures[i].get().error == Coordination::ZNONODE) + if (exists_futures[i].get().error == Coordination::Error::ZNONODE) { all_in_zk = false; @@ -2308,7 +2554,7 @@ bool StorageReplicatedMergeTree::createLogEntryToMergeParts( } if (!all_in_zk) - return false; + return CreateMergeEntryResult::MissingPart; ReplicatedMergeTreeLogEntryData entry; entry.type = LogEntry::MERGE_PARTS; @@ -2320,21 +2566,46 @@ bool StorageReplicatedMergeTree::createLogEntryToMergeParts( entry.create_time = time(nullptr); for (const auto & part : parts) - { entry.source_parts.push_back(part->name); - } - String path_created = zookeeper->create(zookeeper_path + "/log/log-", entry.toString(), zkutil::CreateMode::PersistentSequential); - entry.znode_name = path_created.substr(path_created.find_last_of('/') + 1); + Coordination::Requests ops; + Coordination::Responses responses; + + ops.emplace_back(zkutil::makeCreateRequest( + zookeeper_path + "/log/log-", entry.toString(), + zkutil::CreateMode::PersistentSequential)); + + ops.emplace_back(zkutil::makeSetRequest( + zookeeper_path + "/log", "", log_version)); /// Check and update version. + + Coordination::Error code = zookeeper->tryMulti(ops, responses); + + if (code == Coordination::Error::ZOK) + { + String path_created = dynamic_cast(*responses.front()).path_created; + entry.znode_name = path_created.substr(path_created.find_last_of('/') + 1); + + LOG_TRACE(log, "Created log entry {} for merge {}", path_created, merged_name); + } + else if (code == Coordination::Error::ZBADVERSION) + { + LOG_TRACE(log, "Log entry is not created for merge {} because log was updated", merged_name); + return CreateMergeEntryResult::LogUpdated; + } + else + { + zkutil::KeeperMultiException::check(code, ops, responses); + } if (out_log_entry) *out_log_entry = entry; - return true; + return CreateMergeEntryResult::Ok; } -bool StorageReplicatedMergeTree::createLogEntryToMutatePart(const IMergeTreeDataPart & part, Int64 mutation_version, int alter_version) +StorageReplicatedMergeTree::CreateMergeEntryResult StorageReplicatedMergeTree::createLogEntryToMutatePart( + const IMergeTreeDataPart & part, Int64 mutation_version, int32_t alter_version, int32_t log_version) { auto zookeeper = getZooKeeper(); @@ -2343,11 +2614,12 @@ bool StorageReplicatedMergeTree::createLogEntryToMutatePart(const IMergeTreeData { if (part.modification_time + MAX_AGE_OF_LOCAL_PART_THAT_WASNT_ADDED_TO_ZOOKEEPER < time(nullptr)) { - LOG_WARNING(log, "Part {} (that was selected for mutation) with age {} seconds exists locally but not in ZooKeeper. Won't mutate that part and will check it.", part.name, (time(nullptr) - part.modification_time)); + LOG_WARNING(log, "Part {} (that was selected for mutation) with age {} seconds exists locally but not in ZooKeeper." + " Won't mutate that part and will check it.", part.name, (time(nullptr) - part.modification_time)); enqueuePartForCheck(part.name); } - return false; + return CreateMergeEntryResult::MissingPart; } MergeTreePartInfo new_part_info = part.info; @@ -2363,8 +2635,28 @@ bool StorageReplicatedMergeTree::createLogEntryToMutatePart(const IMergeTreeData entry.create_time = time(nullptr); entry.alter_version = alter_version; - zookeeper->create(zookeeper_path + "/log/log-", entry.toString(), zkutil::CreateMode::PersistentSequential); - return true; + Coordination::Requests ops; + Coordination::Responses responses; + + ops.emplace_back(zkutil::makeCreateRequest( + zookeeper_path + "/log/log-", entry.toString(), + zkutil::CreateMode::PersistentSequential)); + + ops.emplace_back(zkutil::makeSetRequest( + zookeeper_path + "/log", "", log_version)); /// Check and update version. + + Coordination::Error code = zookeeper->tryMulti(ops, responses); + + if (code == Coordination::Error::ZBADVERSION) + { + LOG_TRACE(log, "Log entry is not created for mutation {} because log was updated", new_part_name); + return CreateMergeEntryResult::LogUpdated; + } + + zkutil::KeeperMultiException::check(code, ops, responses); + + LOG_TRACE(log, "Created log entry for mutation {}", new_part_name); + return CreateMergeEntryResult::Ok; } @@ -2640,16 +2932,16 @@ void StorageReplicatedMergeTree::updateQuorum(const String & part_name) ops.emplace_back(zkutil::makeSetRequest(quorum_last_part_path, new_added_parts, added_parts_stat.version)); auto code = zookeeper->tryMulti(ops, responses); - if (code == Coordination::ZOK) + if (code == Coordination::Error::ZOK) { break; } - else if (code == Coordination::ZNONODE) + else if (code == Coordination::Error::ZNONODE) { /// The quorum has already been achieved. break; } - else if (code == Coordination::ZBADVERSION) + else if (code == Coordination::Error::ZBADVERSION) { /// Node was updated meanwhile. We must re-read it and repeat all the actions. continue; @@ -2662,16 +2954,16 @@ void StorageReplicatedMergeTree::updateQuorum(const String & part_name) /// We update the node, registering there one more replica. auto code = zookeeper->trySet(quorum_status_path, quorum_entry.toString(), stat.version); - if (code == Coordination::ZOK) + if (code == Coordination::Error::ZOK) { break; } - else if (code == Coordination::ZNONODE) + else if (code == Coordination::Error::ZNONODE) { /// The quorum has already been achieved. break; } - else if (code == Coordination::ZBADVERSION) + else if (code == Coordination::Error::ZBADVERSION) { /// Node was updated meanwhile. We must re-read it and repeat all the actions. continue; @@ -2715,16 +3007,16 @@ void StorageReplicatedMergeTree::cleanLastPartNode(const String & partition_id) auto code = zookeeper->trySet(quorum_last_part_path, new_added_parts, added_parts_stat.version); - if (code == Coordination::ZOK) + if (code == Coordination::Error::ZOK) { break; } - else if (code == Coordination::ZNONODE) + else if (code == Coordination::Error::ZNONODE) { /// Node is deleted. It is impossible, but it is Ok. break; } - else if (code == Coordination::ZBADVERSION) + else if (code == Coordination::Error::ZBADVERSION) { /// Node was updated meanwhile. We must re-read it and repeat all the actions. continue; @@ -2907,45 +3199,60 @@ void StorageReplicatedMergeTree::startup() if (is_readonly) return; - queue.initialize( - zookeeper_path, replica_path, - getStorageID().getFullTableName() + " (ReplicatedMergeTreeQueue)", - getDataParts()); - - data_parts_exchange_endpoint = std::make_shared(*this); - global_context.getInterserverIOHandler().addEndpoint(data_parts_exchange_endpoint->getId(replica_path), data_parts_exchange_endpoint); - - /// In this thread replica will be activated. - restarting_thread.start(); - - /// Wait while restarting_thread initializes LeaderElection (and so on) or makes first attmept to do it - startup_event.wait(); - - /// If we don't separate create/start steps, race condition will happen - /// between the assignment of queue_task_handle and queueTask that use the queue_task_handle. + try { - auto lock = queue.lockQueue(); - auto & pool = global_context.getBackgroundPool(); - queue_task_handle = pool.createTask([this] { return queueTask(); }); - pool.startTask(queue_task_handle); - } + queue.initialize( + zookeeper_path, replica_path, + getStorageID().getFullTableName() + " (ReplicatedMergeTreeQueue)", + getDataParts()); - if (areBackgroundMovesNeeded()) - { - auto & pool = global_context.getBackgroundMovePool(); - move_parts_task_handle = pool.createTask([this] { return movePartsTask(); }); - pool.startTask(move_parts_task_handle); + data_parts_exchange_endpoint = std::make_shared(*this); + global_context.getInterserverIOHandler().addEndpoint(data_parts_exchange_endpoint->getId(replica_path), data_parts_exchange_endpoint); + + /// In this thread replica will be activated. + restarting_thread.start(); + + /// Wait while restarting_thread initializes LeaderElection (and so on) or makes first attmept to do it + startup_event.wait(); + + /// If we don't separate create/start steps, race condition will happen + /// between the assignment of queue_task_handle and queueTask that use the queue_task_handle. + { + auto lock = queue.lockQueue(); + auto & pool = global_context.getBackgroundPool(); + queue_task_handle = pool.createTask([this] { return queueTask(); }); + pool.startTask(queue_task_handle); + } + + if (areBackgroundMovesNeeded()) + { + auto & pool = global_context.getBackgroundMovePool(); + move_parts_task_handle = pool.createTask([this] { return movePartsTask(); }); + pool.startTask(move_parts_task_handle); + } + } + catch (...) + { + /// Exception safety: failed "startup" does not require a call to "shutdown" from the caller. + /// And it should be able to safely destroy table after exception in "startup" method. + /// It means that failed "startup" must not create any background tasks that we will have to wait. + try + { + shutdown(); + } + catch (...) + { + std::terminate(); + } + + /// Note: after failed "startup", the table will be in a state that only allows to destroy the object. + throw; } - need_shutdown.store(true); } void StorageReplicatedMergeTree::shutdown() { - if (!need_shutdown.load()) - return; - - clearOldPartsFromFilesystem(true); /// Cancel fetches, merges and mutations to force the queue_task to finish ASAP. fetcher.blocker.cancelForever(); merger_mutator.merges_blocker.cancelForever(); @@ -2981,7 +3288,12 @@ void StorageReplicatedMergeTree::shutdown() std::unique_lock lock(data_parts_exchange_endpoint->rwlock); } data_parts_exchange_endpoint.reset(); - need_shutdown.store(false); + + /// We clear all old parts after stopping all background operations. It's + /// important, because background operations can produce temporary parts + /// which will remove themselves in their descrutors. If so, we may have + /// race condition between our remove call and background process. + clearOldPartsFromFilesystem(true); } @@ -3126,7 +3438,8 @@ BlockOutputStreamPtr StorageReplicatedMergeTree::write(const ASTPtr & /*query*/, } -bool StorageReplicatedMergeTree::optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & query_context) +bool StorageReplicatedMergeTree::optimize( + const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & query_context) { assertNotReadonly(); @@ -3136,14 +3449,11 @@ bool StorageReplicatedMergeTree::optimize(const ASTPtr & query, const ASTPtr & p return true; } + constexpr size_t max_retries = 10; + std::vector merge_entries; { - /// We must select parts for merge under merge_selecting_mutex because other threads - /// (merge_selecting_thread or OPTIMIZE queries) could assign new merges. - std::lock_guard merge_selecting_lock(merge_selecting_mutex); - auto zookeeper = getZooKeeper(); - ReplicatedMergeTreeMergePredicate can_merge = queue.getMergePredicate(zookeeper); auto handle_noop = [&] (const String & message) { @@ -3167,52 +3477,94 @@ bool StorageReplicatedMergeTree::optimize(const ASTPtr & query, const ASTPtr & p for (const String & partition_id : partition_ids) { - FutureMergedMutatedPart future_merged_part; - bool selected = merger_mutator.selectAllPartsToMergeWithinPartition( - future_merged_part, disk_space, can_merge, partition_id, true, nullptr); - ReplicatedMergeTreeLogEntryData merge_entry; - if (selected && !createLogEntryToMergeParts(zookeeper, future_merged_part.parts, - future_merged_part.name, future_merged_part.type, deduplicate, force_ttl, &merge_entry)) - return handle_noop("Can't create merge queue node in ZooKeeper"); - if (merge_entry.type != ReplicatedMergeTreeLogEntryData::Type::EMPTY) + size_t try_no = 0; + for (; try_no < max_retries; ++try_no) + { + /// We must select parts for merge under merge_selecting_mutex because other threads + /// (merge_selecting_thread or OPTIMIZE queries) could assign new merges. + std::lock_guard merge_selecting_lock(merge_selecting_mutex); + ReplicatedMergeTreeMergePredicate can_merge = queue.getMergePredicate(zookeeper); + + FutureMergedMutatedPart future_merged_part; + bool selected = merger_mutator.selectAllPartsToMergeWithinPartition( + future_merged_part, disk_space, can_merge, partition_id, true, nullptr); + + if (!selected) + break; + + ReplicatedMergeTreeLogEntryData merge_entry; + CreateMergeEntryResult create_result = createLogEntryToMergeParts( + zookeeper, future_merged_part.parts, + future_merged_part.name, future_merged_part.type, deduplicate, force_ttl, + &merge_entry, can_merge.getVersion()); + + if (create_result == CreateMergeEntryResult::MissingPart) + return handle_noop("Can't create merge queue node in ZooKeeper, because some parts are missing"); + + if (create_result == CreateMergeEntryResult::LogUpdated) + continue; + merge_entries.push_back(std::move(merge_entry)); + break; + } + if (try_no == max_retries) + return handle_noop("Can't create merge queue node in ZooKeeper, because log was updated in every of " + + toString(max_retries) + " tries"); } } else { - FutureMergedMutatedPart future_merged_part; - String disable_reason; - bool selected = false; - if (!partition) - { - selected = merger_mutator.selectPartsToMerge( - future_merged_part, true, storage_settings_ptr->max_bytes_to_merge_at_max_space_in_pool, can_merge, &disable_reason); - } - else + size_t try_no = 0; + for (; try_no < max_retries; ++try_no) { + std::lock_guard merge_selecting_lock(merge_selecting_mutex); + ReplicatedMergeTreeMergePredicate can_merge = queue.getMergePredicate(zookeeper); - UInt64 disk_space = getStoragePolicy()->getMaxUnreservedFreeSpace(); - String partition_id = getPartitionIDFromQuery(partition, query_context); - selected = merger_mutator.selectAllPartsToMergeWithinPartition( - future_merged_part, disk_space, can_merge, partition_id, final, &disable_reason); - } + FutureMergedMutatedPart future_merged_part; + String disable_reason; + bool selected = false; + if (!partition) + { + selected = merger_mutator.selectPartsToMerge( + future_merged_part, true, storage_settings_ptr->max_bytes_to_merge_at_max_space_in_pool, can_merge, &disable_reason); + } + else + { - if (!selected) - { - std::stringstream message; - message << "Cannot select parts for optimization"; - if (!disable_reason.empty()) - message << ": " << disable_reason; - LOG_INFO(log, message.str()); - return handle_noop(message.str()); - } + UInt64 disk_space = getStoragePolicy()->getMaxUnreservedFreeSpace(); + String partition_id = getPartitionIDFromQuery(partition, query_context); + selected = merger_mutator.selectAllPartsToMergeWithinPartition( + future_merged_part, disk_space, can_merge, partition_id, final, &disable_reason); + } + + if (!selected) + { + std::stringstream message; + message << "Cannot select parts for optimization"; + if (!disable_reason.empty()) + message << ": " << disable_reason; + LOG_INFO(log, message.str()); + return handle_noop(message.str()); + } + + ReplicatedMergeTreeLogEntryData merge_entry; + CreateMergeEntryResult create_result = createLogEntryToMergeParts( + zookeeper, future_merged_part.parts, + future_merged_part.name, future_merged_part.type, deduplicate, force_ttl, + &merge_entry, can_merge.getVersion()); + + if (create_result == CreateMergeEntryResult::MissingPart) + return handle_noop("Can't create merge queue node in ZooKeeper, because some parts are missing"); + + if (create_result == CreateMergeEntryResult::LogUpdated) + continue; - ReplicatedMergeTreeLogEntryData merge_entry; - if (!createLogEntryToMergeParts(zookeeper, future_merged_part.parts, - future_merged_part.name, future_merged_part.type, deduplicate, force_ttl, &merge_entry)) - return handle_noop("Can't create merge queue node in ZooKeeper"); - if (merge_entry.type != ReplicatedMergeTreeLogEntryData::Type::EMPTY) merge_entries.push_back(std::move(merge_entry)); + break; + } + if (try_no == max_retries) + return handle_noop("Can't create merge queue node in ZooKeeper, because log was updated in every of " + + toString(max_retries) + " tries"); } } @@ -3243,7 +3595,8 @@ bool StorageReplicatedMergeTree::executeMetadataAlter(const StorageReplicatedMer zookeeper->multi(requests); { - /// TODO (relax this lock) + /// TODO (relax this lock and remove this action lock) + auto merges_block = getActionLock(ActionLocks::PartsMerge); auto table_lock = lockExclusively(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations); LOG_INFO(log, "Metadata changed in ZooKeeper. Applying changes locally."); @@ -3276,7 +3629,7 @@ void StorageReplicatedMergeTree::alter( /// We don't replicate storage_settings_ptr ALTER. It's local operation. /// Also we don't upgrade alter lock to table structure lock. StorageInMemoryMetadata metadata = getInMemoryMetadata(); - params.apply(metadata); + params.apply(metadata, query_context); changeSettings(metadata.settings_ast, table_lock_holder); @@ -3285,7 +3638,8 @@ void StorageReplicatedMergeTree::alter( return; } - auto ast_to_str = [](ASTPtr query) -> String { + auto ast_to_str = [](ASTPtr query) -> String + { if (!query) return ""; return queryToString(query); @@ -3310,7 +3664,7 @@ void StorageReplicatedMergeTree::alter( StorageInMemoryMetadata current_metadata = getInMemoryMetadata(); StorageInMemoryMetadata future_metadata = current_metadata; - params.apply(future_metadata); + params.apply(future_metadata, query_context); ReplicatedMergeTreeTableMetadata future_metadata_in_zk(*this); if (ast_to_str(future_metadata.order_by_ast) != ast_to_str(current_metadata.order_by_ast)) @@ -3319,8 +3673,8 @@ void StorageReplicatedMergeTree::alter( if (ast_to_str(future_metadata.ttl_for_table_ast) != ast_to_str(current_metadata.ttl_for_table_ast)) future_metadata_in_zk.ttl_table = serializeAST(*future_metadata.ttl_for_table_ast); - String new_indices_str = future_metadata.indices.toString(); - if (new_indices_str != current_metadata.indices.toString()) + String new_indices_str = future_metadata.secondary_indices.toString(); + if (new_indices_str != current_metadata.secondary_indices.toString()) future_metadata_in_zk.skip_indices = new_indices_str; String new_constraints_str = future_metadata.constraints.toString(); @@ -3335,7 +3689,6 @@ void StorageReplicatedMergeTree::alter( String new_columns_str = future_metadata.columns.toString(); ops.emplace_back(zkutil::makeSetRequest(zookeeper_path + "/columns", new_columns_str, -1)); - if (ast_to_str(current_metadata.settings_ast) != ast_to_str(future_metadata.settings_ast)) { lockStructureExclusively( @@ -3357,10 +3710,12 @@ void StorageReplicatedMergeTree::alter( alter_entry->alter_version = new_metadata_version; alter_entry->create_time = time(nullptr); - auto maybe_mutation_commands = params.getMutationCommands(current_metadata, query_context.getSettingsRef().materialize_ttl_after_modify); + auto maybe_mutation_commands = params.getMutationCommands( + current_metadata, query_context.getSettingsRef().materialize_ttl_after_modify, query_context); alter_entry->have_mutation = !maybe_mutation_commands.empty(); - ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/log/log-", alter_entry->toString(), zkutil::CreateMode::PersistentSequential)); + ops.emplace_back(zkutil::makeCreateRequest( + zookeeper_path + "/log/log-", alter_entry->toString(), zkutil::CreateMode::PersistentSequential)); std::optional lock_holder; @@ -3391,9 +3746,9 @@ void StorageReplicatedMergeTree::alter( } Coordination::Responses results; - int32_t rc = zookeeper->tryMulti(ops, results); + Coordination::Error rc = zookeeper->tryMulti(ops, results); - if (rc == Coordination::ZOK) + if (rc == Coordination::Error::ZOK) { if (alter_entry->have_mutation) { @@ -3413,9 +3768,9 @@ void StorageReplicatedMergeTree::alter( } break; } - else if (rc == Coordination::ZBADVERSION) + else if (rc == Coordination::Error::ZBADVERSION) { - if (results[0]->error) + if (results[0]->error != Coordination::Error::ZOK) throw Exception("Metadata on replica is not up to date with common metadata in Zookeeper. Cannot alter", ErrorCodes::CANNOT_ASSIGN_ALTER); continue; @@ -3678,48 +4033,25 @@ void StorageReplicatedMergeTree::checkPartitionCanBeDropped(const ASTPtr & parti } -void StorageReplicatedMergeTree::drop() -{ - { - auto zookeeper = tryGetZooKeeper(); - - if (is_readonly || !zookeeper) - throw Exception("Can't drop readonly replicated table (need to drop data in ZooKeeper as well)", ErrorCodes::TABLE_IS_READ_ONLY); - - shutdown(); - - if (zookeeper->expired()) - throw Exception("Table was not dropped because ZooKeeper session has expired.", ErrorCodes::TABLE_WAS_NOT_DROPPED); - - LOG_INFO(log, "Removing replica {}", replica_path); - replica_is_active_node = nullptr; - /// It may left some garbage if replica_path subtree are concurently modified - zookeeper->tryRemoveRecursive(replica_path); - if (zookeeper->exists(replica_path)) - LOG_ERROR(log, "Replica was not completely removed from ZooKeeper, {} still exists and may contain some garbage.", replica_path); - - /// Check that `zookeeper_path` exists: it could have been deleted by another replica after execution of previous line. - Strings replicas; - if (zookeeper->tryGetChildren(zookeeper_path + "/replicas", replicas) == Coordination::ZOK && replicas.empty()) - { - LOG_INFO(log, "Removing table {} (this might take several minutes)", zookeeper_path); - zookeeper->tryRemoveRecursive(zookeeper_path); - if (zookeeper->exists(zookeeper_path)) - LOG_ERROR(log, "Table was not completely removed from ZooKeeper, {} still exists and may contain some garbage.", zookeeper_path); - } - } - - dropAllData(); -} - - void StorageReplicatedMergeTree::rename(const String & new_path_to_table_data, const StorageID & new_table_id) { MergeTreeData::rename(new_path_to_table_data, new_table_id); /// Update table name in zookeeper - auto zookeeper = getZooKeeper(); - zookeeper->set(replica_path + "/host", getReplicatedMergeTreeAddress().toString()); + if (!is_readonly) + { + /// We don't do it for readonly tables, because it will be updated on next table startup. + /// It is also Ok to skip ZK error for the same reason. + try + { + auto zookeeper = getZooKeeper(); + zookeeper->set(replica_path + "/host", getReplicatedMergeTreeAddress().toString()); + } + catch (Coordination::Exception & e) + { + LOG_WARNING(log, "Cannot update the value of 'host' node (replica address) in ZooKeeper: {}", e.displayText()); + } + } /// TODO: You can update names of loggers. } @@ -3770,8 +4102,8 @@ StorageReplicatedMergeTree::allocateBlockNumber( ops.push_back(zkutil::makeSetRequest(block_numbers_path, "", -1)); Coordination::Responses responses; - int code = zookeeper->tryMulti(ops, responses); - if (code && code != Coordination::ZNODEEXISTS) + Coordination::Error code = zookeeper->tryMulti(ops, responses); + if (code != Coordination::Error::ZOK && code != Coordination::Error::ZNODEEXISTS) zkutil::KeeperMultiException::check(code, ops, responses); } @@ -3784,7 +4116,7 @@ StorageReplicatedMergeTree::allocateBlockNumber( } catch (const zkutil::KeeperMultiException & e) { - if (e.code == Coordination::ZNODEEXISTS && e.getPathForFirstFailedOp() == zookeeper_block_id_path) + if (e.code == Coordination::Error::ZNODEEXISTS && e.getPathForFirstFailedOp() == zookeeper_block_id_path) return {}; throw Exception("Cannot allocate block number in ZooKeeper: " + e.displayText(), ErrorCodes::KEEPER_EXCEPTION); @@ -3798,7 +4130,8 @@ StorageReplicatedMergeTree::allocateBlockNumber( } -Strings StorageReplicatedMergeTree::waitForAllReplicasToProcessLogEntry(const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active) +Strings StorageReplicatedMergeTree::waitForAllReplicasToProcessLogEntry( + const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active) { LOG_DEBUG(log, "Waiting for all replicas to process {}", entry.znode_name); @@ -3823,7 +4156,8 @@ Strings StorageReplicatedMergeTree::waitForAllReplicasToProcessLogEntry(const Re } -bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(const String & replica, const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active) +bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry( + const String & replica, const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active) { String entry_str = entry.toString(); String log_node_name; @@ -4473,9 +4807,9 @@ void StorageReplicatedMergeTree::mutate(const MutationCommands & commands, const mutations_path + "/", entry.toString(), zkutil::CreateMode::PersistentSequential)); Coordination::Responses responses; - int32_t rc = zookeeper->tryMulti(requests, responses); + Coordination::Error rc = zookeeper->tryMulti(requests, responses); - if (rc == Coordination::ZOK) + if (rc == Coordination::Error::ZOK) { const String & path_created = dynamic_cast(responses[1].get())->path_created; @@ -4483,7 +4817,7 @@ void StorageReplicatedMergeTree::mutate(const MutationCommands & commands, const LOG_TRACE(log, "Created mutation with ID {}", entry.znode_name); break; } - else if (rc == Coordination::ZBADVERSION) + else if (rc == Coordination::Error::ZBADVERSION) { LOG_TRACE(log, "Version conflict when trying to create a mutation node, retrying..."); continue; @@ -4675,7 +5009,7 @@ bool StorageReplicatedMergeTree::tryRemovePartsFromZooKeeperWithRetries(const St for (size_t i = 0; i < part_names.size(); ++i) { Coordination::ExistsResponse exists_resp = exists_futures[i].get(); - if (!exists_resp.error) + if (exists_resp.error == Coordination::Error::ZOK) { Coordination::Requests ops; removePartFromZooKeeper(part_names[i], ops, exists_resp.stat.numChildren > 0); @@ -4687,7 +5021,7 @@ bool StorageReplicatedMergeTree::tryRemovePartsFromZooKeeperWithRetries(const St { auto response = future.get(); - if (response.error == 0 || response.error == Coordination::ZNONODE) + if (response.error == Coordination::Error::ZOK || response.error == Coordination::Error::ZNONODE) continue; if (Coordination::isHardwareError(response.error)) @@ -4736,7 +5070,7 @@ void StorageReplicatedMergeTree::removePartsFromZooKeeper( for (size_t i = 0; i < part_names.size(); ++i) { Coordination::ExistsResponse exists_resp = exists_futures[i].get(); - if (!exists_resp.error) + if (exists_resp.error == Coordination::Error::ZOK) { Coordination::Requests ops; removePartFromZooKeeper(part_names[i], ops, exists_resp.stat.numChildren > 0); @@ -4765,9 +5099,9 @@ void StorageReplicatedMergeTree::removePartsFromZooKeeper( continue; auto response = future.get(); - if (response.error == Coordination::ZOK) + if (response.error == Coordination::Error::ZOK) continue; - else if (response.error == Coordination::ZNONODE) + else if (response.error == Coordination::Error::ZNONODE) { LOG_DEBUG(log, "There is no part {} in ZooKeeper, it was only in filesystem", part_names[i]); continue; @@ -4779,7 +5113,7 @@ void StorageReplicatedMergeTree::removePartsFromZooKeeper( continue; } else - LOG_WARNING(log, "Cannot remove part {} from ZooKeeper: {}", part_names[i], zkutil::ZooKeeper::error2string(response.error)); + LOG_WARNING(log, "Cannot remove part {} from ZooKeeper: {}", part_names[i], Coordination::errorMessage(response.error)); } } @@ -4788,7 +5122,7 @@ void StorageReplicatedMergeTree::clearBlocksInPartition( zkutil::ZooKeeper & zookeeper, const String & partition_id, Int64 min_block_num, Int64 max_block_num) { Strings blocks; - if (zookeeper.tryGetChildren(zookeeper_path + "/blocks", blocks)) + if (Coordination::Error::ZOK != zookeeper.tryGetChildren(zookeeper_path + "/blocks", blocks)) throw Exception(zookeeper_path + "/blocks doesn't exist", ErrorCodes::NOT_FOUND_NODE); String partition_prefix = partition_id + "_"; @@ -4808,7 +5142,7 @@ void StorageReplicatedMergeTree::clearBlocksInPartition( const String & path = pair.first; auto result = pair.second.get(); - if (result.error == Coordination::ZNONODE) + if (result.error == Coordination::Error::ZNONODE) continue; ReadBufferFromString buf(result.data); @@ -4821,14 +5155,14 @@ void StorageReplicatedMergeTree::clearBlocksInPartition( for (auto & pair : to_delete_futures) { const String & path = pair.first; - int32_t rc = pair.second.get().error; - if (rc == Coordination::ZNOTEMPTY) + Coordination::Error rc = pair.second.get().error; + if (rc == Coordination::Error::ZNOTEMPTY) { /// Can happen if there are leftover block nodes with children created by previous server versions. zookeeper.removeRecursive(path); } - else if (rc) - LOG_WARNING(log, "Error while deleting ZooKeeper path `{}`: {}, ignoring.", path, zkutil::ZooKeeper::error2string(rc)); + else if (rc != Coordination::Error::ZOK) + LOG_WARNING(log, "Error while deleting ZooKeeper path `{}`: {}, ignoring.", path, Coordination::errorMessage(rc)); } LOG_TRACE(log, "Deleted {} deduplication block IDs in partition ID {}", to_delete_futures.size(), partition_id); @@ -4968,6 +5302,7 @@ void StorageReplicatedMergeTree::replacePartitionFrom(const StoragePtr & source_ } } + ops.emplace_back(zkutil::makeSetRequest(zookeeper_path + "/log", "", -1)); /// Just update version ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/log/log-", entry.toString(), zkutil::CreateMode::PersistentSequential)); Transaction transaction(*this); @@ -5153,7 +5488,8 @@ void StorageReplicatedMergeTree::movePartitionToTable(const StoragePtr & dest_ta } } - ops.emplace_back(zkutil::makeCreateRequest(dest_table_storage->zookeeper_path + "/log/log-", entry.toString(), zkutil::CreateMode::PersistentSequential)); + ops.emplace_back(zkutil::makeCreateRequest(dest_table_storage->zookeeper_path + "/log/log-", + entry.toString(), zkutil::CreateMode::PersistentSequential)); { Transaction transaction(*dest_table_storage); @@ -5200,11 +5536,13 @@ void StorageReplicatedMergeTree::movePartitionToTable(const StoragePtr & dest_ta Coordination::Requests ops_dest; - ops_dest.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/log/log-", entry_delete.toString(), zkutil::CreateMode::PersistentSequential)); + ops_dest.emplace_back(zkutil::makeCreateRequest( + zookeeper_path + "/log/log-", entry_delete.toString(), zkutil::CreateMode::PersistentSequential)); + ops_dest.emplace_back(zkutil::makeSetRequest(zookeeper_path + "/log", "", -1)); /// Just update version op_results = zookeeper->multi(ops_dest); - log_znode_path = dynamic_cast(*op_results.back()).path_created; + log_znode_path = dynamic_cast(*op_results.front()).path_created; entry_delete.znode_name = log_znode_path.substr(log_znode_path.find_last_of('/') + 1); if (query_context.getSettingsRef().replication_alter_partitions_sync > 1) @@ -5307,7 +5645,7 @@ bool StorageReplicatedMergeTree::waitForShrinkingQueueSize(size_t queue_size, UI queue.pullLogsToQueue(getZooKeeper()); /// This is significant, because the execution of this task could be delayed at BackgroundPool. /// And we force it to be executed. - queue_task_handle->wake(); + queue_task_handle->signalReadyToRun(); Poco::Event target_size_event; auto callback = [&target_size_event, queue_size] (size_t new_queue_size) @@ -5360,7 +5698,12 @@ bool StorageReplicatedMergeTree::dropPartsInPartition( entry.detach = detach; entry.create_time = time(nullptr); - String log_znode_path = zookeeper.create(zookeeper_path + "/log/log-", entry.toString(), zkutil::CreateMode::PersistentSequential); + Coordination::Requests ops; + ops.emplace_back(zkutil::makeCreateRequest(zookeeper_path + "/log/log-", entry.toString(), zkutil::CreateMode::PersistentSequential)); + ops.emplace_back(zkutil::makeSetRequest(zookeeper_path + "/log", "", -1)); /// Just update version. + Coordination::Responses responses = zookeeper.multi(ops); + + String log_znode_path = dynamic_cast(*responses.front()).path_created; entry.znode_name = log_znode_path.substr(log_znode_path.find_last_of('/') + 1); return true; diff --git a/src/Storages/StorageReplicatedMergeTree.h b/src/Storages/StorageReplicatedMergeTree.h index f01e51bd769..52ce1aada08 100644 --- a/src/Storages/StorageReplicatedMergeTree.h +++ b/src/Storages/StorageReplicatedMergeTree.h @@ -210,6 +210,8 @@ private: /// If true, the table is offline and can not be written to it. std::atomic_bool is_readonly {false}; + /// If false - ZooKeeper is available, but there is no table metadata. It's safe to drop table in this case. + bool has_metadata_in_zookeeper = true; String zookeeper_path; String replica_name; @@ -288,14 +290,13 @@ private: /// True if replica was created for existing table with fixed granularity bool other_replicas_fixed_granularity = false; - std::atomic_bool need_shutdown{false}; - template void foreachCommittedParts(const Func & func) const; - /** Creates the minimum set of nodes in ZooKeeper. + /** Creates the minimum set of nodes in ZooKeeper and create first replica. + * Returns true if was created, false if exists. */ - void createTableIfNotExists(); + bool createTableIfNotExists(); /** Creates a replica in ZooKeeper and adds to the queue all that it takes to catch up with the rest of the replicas. */ @@ -426,16 +427,23 @@ private: * Call when merge_selecting_mutex is locked. * Returns false if any part is not in ZK. */ - bool createLogEntryToMergeParts( + enum class CreateMergeEntryResult { Ok, MissingPart, LogUpdated, Other }; + + CreateMergeEntryResult createLogEntryToMergeParts( zkutil::ZooKeeperPtr & zookeeper, const DataPartsVector & parts, const String & merged_name, const MergeTreeDataPartType & merged_part_type, bool deduplicate, bool force_ttl, - ReplicatedMergeTreeLogEntryData * out_log_entry = nullptr); + ReplicatedMergeTreeLogEntryData * out_log_entry, + int32_t log_version); - bool createLogEntryToMutatePart(const IMergeTreeDataPart & part, Int64 mutation_version, int alter_version); + CreateMergeEntryResult createLogEntryToMutatePart( + const IMergeTreeDataPart & part, + Int64 mutation_version, + int32_t alter_version, + int32_t log_version); /// Exchange parts. diff --git a/src/Storages/StorageS3.cpp b/src/Storages/StorageS3.cpp index e8fd89c4505..397d064ba15 100644 --- a/src/Storages/StorageS3.cpp +++ b/src/Storages/StorageS3.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -23,6 +24,7 @@ #include +#include #include #include @@ -33,6 +35,8 @@ #include #include +#include + namespace DB { @@ -200,18 +204,24 @@ StorageS3::StorageS3( , format_name(format_name_) , min_upload_part_size(min_upload_part_size_) , compression_method(compression_method_) - , client(S3::ClientFactory::instance().create(uri_.endpoint, uri_.is_virtual_hosted_style, access_key_id_, secret_access_key_)) { context_global.getRemoteHostFilter().checkURL(uri_.uri); setColumns(columns_); setConstraints(constraints_); + + auto settings = context_.getStorageS3Settings().getSettings(uri.endpoint); + Aws::Auth::AWSCredentials credentials(access_key_id_, secret_access_key_); + if (access_key_id_.empty()) + credentials = Aws::Auth::AWSCredentials(std::move(settings.access_key_id), std::move(settings.secret_access_key)); + + client = S3::ClientFactory::instance().create( + uri_.endpoint, uri_.is_virtual_hosted_style, access_key_id_, secret_access_key_, std::move(settings.headers)); } namespace { - -/* "Recursive" directory listing with matched paths as a result. + /* "Recursive" directory listing with matched paths as a result. * Have the same method in StorageFile. */ Strings listFilesWithRegexpMatching(Aws::S3::S3Client & client, const S3::URI & globbed_uri) @@ -241,11 +251,17 @@ Strings listFilesWithRegexpMatching(Aws::S3::S3Client & client, const S3::URI & outcome = client.ListObjectsV2(request); if (!outcome.IsSuccess()) { - throw Exception("Could not list objects in bucket " + quoteString(request.GetBucket()) - + " with prefix " + quoteString(request.GetPrefix()) - + ", page " + std::to_string(page) - + ", S3 exception " + outcome.GetError().GetExceptionName() + " " + outcome.GetError().GetMessage() - , ErrorCodes::S3_ERROR); + std::ostringstream message; + message << "Could not list objects in bucket " << quoteString(request.GetBucket()) + << " with prefix " << quoteString(request.GetPrefix()); + + if (page > 1) + message << ", page " << std::to_string(page); + + message << ", S3 exception: " + backQuote(outcome.GetError().GetExceptionName()) + << ", message: " + quoteString(outcome.GetError().GetMessage()); + + throw Exception(message.str(), ErrorCodes::S3_ERROR); } for (const auto & row : outcome.GetResult().GetContents()) diff --git a/src/Storages/StorageS3Settings.cpp b/src/Storages/StorageS3Settings.cpp new file mode 100644 index 00000000000..5b443de6b9a --- /dev/null +++ b/src/Storages/StorageS3Settings.cpp @@ -0,0 +1,57 @@ +#include + +#include +#include + + +namespace DB +{ +namespace ErrorCodes +{ + extern const int INVALID_CONFIG_PARAMETER; +} + +void StorageS3Settings::loadFromConfig(const String & config_elem, const Poco::Util::AbstractConfiguration & config) +{ + std::lock_guard lock(mutex); + settings.clear(); + if (!config.has(config_elem)) + return; + + Poco::Util::AbstractConfiguration::Keys config_keys; + config.keys(config_elem, config_keys); + + for (const String & key : config_keys) + { + auto endpoint = config.getString(config_elem + "." + key + ".endpoint"); + auto access_key_id = config.getString(config_elem + "." + key + ".access_key_id", ""); + auto secret_access_key = config.getString(config_elem + "." + key + ".secret_access_key", ""); + + HeaderCollection headers; + Poco::Util::AbstractConfiguration::Keys subconfig_keys; + config.keys(config_elem + "." + key, subconfig_keys); + for (const String & subkey : subconfig_keys) + { + if (subkey.starts_with("header")) + { + auto header_str = config.getString(config_elem + "." + key + "." + subkey); + auto delimiter = header_str.find(':'); + if (delimiter == String::npos) + throw Exception("Malformed s3 header value", ErrorCodes::INVALID_CONFIG_PARAMETER); + headers.emplace_back(HttpHeader{header_str.substr(0, delimiter), header_str.substr(delimiter + 1, String::npos)}); + } + } + + settings.emplace(endpoint, S3AuthSettings{std::move(access_key_id), std::move(secret_access_key), std::move(headers)}); + } +} + +S3AuthSettings StorageS3Settings::getSettings(const String & endpoint) const +{ + std::lock_guard lock(mutex); + if (auto setting = settings.find(endpoint); setting != settings.end()) + return setting->second; + return {}; +} + +} diff --git a/src/Storages/StorageS3Settings.h b/src/Storages/StorageS3Settings.h new file mode 100644 index 00000000000..ac31928a240 --- /dev/null +++ b/src/Storages/StorageS3Settings.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include +#include +#include + +namespace Poco::Util +{ +class AbstractConfiguration; +} + +namespace DB +{ + +struct HttpHeader +{ + const String name; + const String value; +}; + +using HeaderCollection = std::vector; + +struct S3AuthSettings +{ + const String access_key_id; + const String secret_access_key; + + const HeaderCollection headers; +}; + +/// Settings for the StorageS3. +class StorageS3Settings +{ +public: + StorageS3Settings() = default; + void loadFromConfig(const String & config_elem, const Poco::Util::AbstractConfiguration & config); + + S3AuthSettings getSettings(const String & endpoint) const; + +private: + mutable std::mutex mutex; + std::map settings; +}; + +} diff --git a/src/Storages/StorageView.cpp b/src/Storages/StorageView.cpp index c6b37a50aa9..97403a359c3 100644 --- a/src/Storages/StorageView.cpp +++ b/src/Storages/StorageView.cpp @@ -124,7 +124,7 @@ ASTPtr StorageView::getRuntimeViewQuery(ASTSelectQuery * outer_query, const Cont /// TODO: remove getTableExpressions and getTablesWithColumns { const auto & table_expressions = getTableExpressions(*outer_query); - const auto & tables_with_columns = getDatabaseAndTablesWithColumnNames(table_expressions, context); + const auto & tables_with_columns = getDatabaseAndTablesWithColumns(table_expressions, context); replaceTableNameWithSubquery(outer_query, runtime_view_query); if (context.getSettingsRef().joined_subquery_requires_alias && tables_with_columns.size() > 1) diff --git a/src/Storages/System/StorageSystemBuildOptions.generated.cpp.in b/src/Storages/System/StorageSystemBuildOptions.generated.cpp.in index 9f73c00d22b..97998e11ea5 100644 --- a/src/Storages/System/StorageSystemBuildOptions.generated.cpp.in +++ b/src/Storages/System/StorageSystemBuildOptions.generated.cpp.in @@ -44,14 +44,14 @@ const char * auto_config_build[] "USE_RDKAFKA", "@USE_RDKAFKA@", "USE_CAPNP", "@USE_CAPNP@", "USE_BASE64", "@USE_BASE64@", - "USE_XXHASH", "@USE_XXHASH@", + "USE_XXHASH", "@USE_INTERNAL_LZ4_LIBRARY@", "USE_HDFS", "@USE_HDFS@", "USE_SNAPPY", "@USE_SNAPPY@", "USE_PARQUET", "@USE_PARQUET@", "USE_PROTOBUF", "@USE_PROTOBUF@", "USE_BROTLI", "@USE_BROTLI@", "USE_SSL", "@USE_SSL@", - "USE_HYPERSCAN", "@USE_HYPERSCAN@", + "USE_HYPERSCAN", "@ENABLE_HYPERSCAN@", "USE_SIMDJSON", "@USE_SIMDJSON@", "USE_GRPC", "@USE_GRPC@", diff --git a/src/Storages/System/StorageSystemColumns.cpp b/src/Storages/System/StorageSystemColumns.cpp index 8eb8856512e..90e52ad373e 100644 --- a/src/Storages/System/StorageSystemColumns.cpp +++ b/src/Storages/System/StorageSystemColumns.cpp @@ -32,6 +32,7 @@ StorageSystemColumns::StorageSystemColumns(const std::string & name_) { "table", std::make_shared() }, { "name", std::make_shared() }, { "type", std::make_shared() }, + { "position", std::make_shared() }, { "default_kind", std::make_shared() }, { "default_expression", std::make_shared() }, { "data_compressed_bytes", std::make_shared() }, @@ -131,8 +132,10 @@ protected: bool check_access_for_columns = check_access_for_tables && !access->isGranted(AccessType::SHOW_COLUMNS, database_name, table_name); + size_t position = 0; for (const auto & column : columns) { + ++position; if (check_access_for_columns && !access->isGranted(AccessType::SHOW_COLUMNS, database_name, table_name, column.name)) continue; @@ -147,6 +150,8 @@ protected: res_columns[res_index++]->insert(column.name); if (columns_mask[src_index++]) res_columns[res_index++]->insert(column.type->getName()); + if (columns_mask[src_index++]) + res_columns[res_index++]->insert(position); if (column.default_desc.expression) { @@ -303,12 +308,15 @@ Pipes StorageSystemColumns::read( for (auto iterator = database->getTablesIterator(context); iterator->isValid(); iterator->next()) { - const String & table_name = iterator->name(); - storages.emplace(std::piecewise_construct, - std::forward_as_tuple(database_name, table_name), - std::forward_as_tuple(iterator->table())); - table_column_mut->insert(table_name); - ++offsets[i]; + if (const auto & table = iterator->table()) + { + const String & table_name = iterator->name(); + storages.emplace(std::piecewise_construct, + std::forward_as_tuple(database_name, table_name), + std::forward_as_tuple(table)); + table_column_mut->insert(table_name); + ++offsets[i]; + } } } diff --git a/src/Storages/System/StorageSystemDistributionQueue.cpp b/src/Storages/System/StorageSystemDistributionQueue.cpp new file mode 100644 index 00000000000..2459be0ba71 --- /dev/null +++ b/src/Storages/System/StorageSystemDistributionQueue.cpp @@ -0,0 +1,120 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace DB +{ + + +NamesAndTypesList StorageSystemDistributionQueue::getNamesAndTypes() +{ + return { + { "database", std::make_shared() }, + { "table", std::make_shared() }, + { "data_path", std::make_shared() }, + { "is_blocked", std::make_shared() }, + { "error_count", std::make_shared() }, + { "data_files", std::make_shared() }, + { "data_compressed_bytes", std::make_shared() }, + { "last_exception", std::make_shared() }, + }; +} + + +void StorageSystemDistributionQueue::fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo & query_info) const +{ + const auto access = context.getAccess(); + const bool check_access_for_databases = !access->isGranted(AccessType::SHOW_TABLES); + + std::map> tables; + for (const auto & db : DatabaseCatalog::instance().getDatabases()) + { + /// Lazy database can not contain distributed tables + if (db.second->getEngineName() == "Lazy") + continue; + + const bool check_access_for_tables = check_access_for_databases && !access->isGranted(AccessType::SHOW_TABLES, db.first); + + for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next()) + { + StoragePtr table = iterator->table(); + if (!table) + continue; + + if (!dynamic_cast(table.get())) + continue; + if (check_access_for_tables && !access->isGranted(AccessType::SHOW_TABLES, db.first, iterator->name())) + continue; + tables[db.first][iterator->name()] = table; + } + } + + + MutableColumnPtr col_database_mut = ColumnString::create(); + MutableColumnPtr col_table_mut = ColumnString::create(); + + for (auto & db : tables) + { + for (auto & table : db.second) + { + col_database_mut->insert(db.first); + col_table_mut->insert(table.first); + } + } + + ColumnPtr col_database_to_filter = std::move(col_database_mut); + ColumnPtr col_table_to_filter = std::move(col_table_mut); + + /// Determine what tables are needed by the conditions in the query. + { + Block filtered_block + { + { col_database_to_filter, std::make_shared(), "database" }, + { col_table_to_filter, std::make_shared(), "table" }, + }; + + VirtualColumnUtils::filterBlockWithQuery(query_info.query, filtered_block, context); + + if (!filtered_block.rows()) + return; + + col_database_to_filter = filtered_block.getByName("database").column; + col_table_to_filter = filtered_block.getByName("table").column; + } + + for (size_t i = 0, tables_size = col_database_to_filter->size(); i < tables_size; ++i) + { + String database = (*col_database_to_filter)[i].safeGet(); + String table = (*col_table_to_filter)[i].safeGet(); + + auto & distributed_table = dynamic_cast(*tables[database][table]); + + for (const auto & status : distributed_table.getDirectoryMonitorsStatuses()) + { + size_t col_num = 0; + res_columns[col_num++]->insert(database); + res_columns[col_num++]->insert(table); + res_columns[col_num++]->insert(status.path); + res_columns[col_num++]->insert(status.is_blocked); + res_columns[col_num++]->insert(status.error_count); + res_columns[col_num++]->insert(status.files_count); + res_columns[col_num++]->insert(status.bytes_count); + + if (status.last_exception) + res_columns[col_num++]->insert(getExceptionMessage(status.last_exception, false)); + else + res_columns[col_num++]->insertDefault(); + } + } +} + +} diff --git a/src/Storages/System/StorageSystemDistributionQueue.h b/src/Storages/System/StorageSystemDistributionQueue.h new file mode 100644 index 00000000000..88e7fa45cf5 --- /dev/null +++ b/src/Storages/System/StorageSystemDistributionQueue.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + + +namespace DB +{ + +class Context; + + +/** Implements the `distribution_queue` system table, which allows you to view the INSERT queues for the Distributed tables. + */ +class StorageSystemDistributionQueue final : public ext::shared_ptr_helper, public IStorageSystemOneBlock +{ + friend struct ext::shared_ptr_helper; +public: + std::string getName() const override { return "SystemDistributionQueue"; } + + static NamesAndTypesList getNamesAndTypes(); + +protected: + using IStorageSystemOneBlock::IStorageSystemOneBlock; + + void fillData(MutableColumns & res_columns, const Context & context, const SelectQueryInfo & query_info) const override; +}; + +} diff --git a/src/Storages/System/StorageSystemGraphite.cpp b/src/Storages/System/StorageSystemGraphite.cpp index bfa25a99838..ffa789a4751 100644 --- a/src/Storages/System/StorageSystemGraphite.cpp +++ b/src/Storages/System/StorageSystemGraphite.cpp @@ -39,6 +39,8 @@ static StorageSystemGraphite::Configs getConfigs(const Context & context) for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next()) { const auto & table = iterator->table(); + if (!table) + continue; const MergeTreeData * table_data = dynamic_cast(table.get()); if (!table_data) diff --git a/src/Storages/System/StorageSystemMutations.cpp b/src/Storages/System/StorageSystemMutations.cpp index 685565d82e1..04dc8f1024a 100644 --- a/src/Storages/System/StorageSystemMutations.cpp +++ b/src/Storages/System/StorageSystemMutations.cpp @@ -53,13 +53,17 @@ void StorageSystemMutations::fillData(MutableColumns & res_columns, const Contex for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next()) { - if (!dynamic_cast(iterator->table().get())) + const auto & table = iterator->table(); + if (!table) + continue; + + if (!dynamic_cast(table.get())) continue; if (check_access_for_tables && !access->isGranted(AccessType::SHOW_TABLES, db.first, iterator->name())) continue; - merge_tree_tables[db.first][iterator->name()] = iterator->table(); + merge_tree_tables[db.first][iterator->name()] = table; } } diff --git a/src/Storages/System/StorageSystemPartsBase.cpp b/src/Storages/System/StorageSystemPartsBase.cpp index 3b97cbb2d9b..925a5df889e 100644 --- a/src/Storages/System/StorageSystemPartsBase.cpp +++ b/src/Storages/System/StorageSystemPartsBase.cpp @@ -115,6 +115,9 @@ StoragesInfoStream::StoragesInfoStream(const SelectQueryInfo & query_info, const { String table_name = iterator->name(); StoragePtr storage = iterator->table(); + if (!storage) + continue; + String engine_name = storage->getName(); if (!dynamic_cast(storage.get())) diff --git a/src/Storages/System/StorageSystemPartsColumns.cpp b/src/Storages/System/StorageSystemPartsColumns.cpp index b8acdc5f995..479621fd47f 100644 --- a/src/Storages/System/StorageSystemPartsColumns.cpp +++ b/src/Storages/System/StorageSystemPartsColumns.cpp @@ -49,6 +49,7 @@ StorageSystemPartsColumns::StorageSystemPartsColumns(const std::string & name_) {"column", std::make_shared()}, {"type", std::make_shared()}, + {"column_position", std::make_shared()}, {"default_kind", std::make_shared()}, {"default_expression", std::make_shared()}, {"column_bytes_on_disk", std::make_shared()}, @@ -101,9 +102,10 @@ void StorageSystemPartsColumns::processNextStorage(MutableColumns & columns_, co using State = IMergeTreeDataPart::State; + size_t column_position = 0; for (const auto & column : part->getColumns()) - { + ++column_position; size_t j = 0; { WriteBufferFromOwnString out; @@ -143,6 +145,7 @@ void StorageSystemPartsColumns::processNextStorage(MutableColumns & columns_, co columns_[j++]->insert(column.name); columns_[j++]->insert(column.type->getName()); + columns_[j++]->insert(column_position); auto column_info_it = columns_info.find(column.name); if (column_info_it != columns_info.end()) diff --git a/src/Storages/System/StorageSystemReplicas.cpp b/src/Storages/System/StorageSystemReplicas.cpp index 251b45e44b6..c2cd3a1e4b1 100644 --- a/src/Storages/System/StorageSystemReplicas.cpp +++ b/src/Storages/System/StorageSystemReplicas.cpp @@ -78,11 +78,15 @@ Pipes StorageSystemReplicas::read( const bool check_access_for_tables = check_access_for_databases && !access->isGranted(AccessType::SHOW_TABLES, db.first); for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next()) { - if (!dynamic_cast(iterator->table().get())) + const auto & table = iterator->table(); + if (!table) + continue; + + if (!dynamic_cast(table.get())) continue; if (check_access_for_tables && !access->isGranted(AccessType::SHOW_TABLES, db.first, iterator->name())) continue; - replicated_tables[db.first][iterator->name()] = iterator->table(); + replicated_tables[db.first][iterator->name()] = table; } } diff --git a/src/Storages/System/StorageSystemReplicationQueue.cpp b/src/Storages/System/StorageSystemReplicationQueue.cpp index 2c188cf3734..f5e43abada0 100644 --- a/src/Storages/System/StorageSystemReplicationQueue.cpp +++ b/src/Storages/System/StorageSystemReplicationQueue.cpp @@ -62,11 +62,14 @@ void StorageSystemReplicationQueue::fillData(MutableColumns & res_columns, const for (auto iterator = db.second->getTablesIterator(context); iterator->isValid(); iterator->next()) { - if (!dynamic_cast(iterator->table().get())) + const auto & table = iterator->table(); + if (!table) + continue; + if (!dynamic_cast(table.get())) continue; if (check_access_for_tables && !access->isGranted(AccessType::SHOW_TABLES, db.first, iterator->name())) continue; - replicated_tables[db.first][iterator->name()] = iterator->table(); + replicated_tables[db.first][iterator->name()] = table; } } diff --git a/src/Storages/System/StorageSystemZooKeeper.cpp b/src/Storages/System/StorageSystemZooKeeper.cpp index c3f1d8a8505..17ab4ed4efb 100644 --- a/src/Storages/System/StorageSystemZooKeeper.cpp +++ b/src/Storages/System/StorageSystemZooKeeper.cpp @@ -112,8 +112,13 @@ void StorageSystemZooKeeper::fillData(MutableColumns & res_columns, const Contex zkutil::ZooKeeperPtr zookeeper = context.getZooKeeper(); + String path_corrected; + /// path should starts with '/', otherwise ZBADARGUMENTS will be thrown in + /// ZooKeeper::sendThread and the session will fail. + if (path[0] != '/') + path_corrected = '/'; + path_corrected += path; /// In all cases except the root, path must not end with a slash. - String path_corrected = path; if (path_corrected != "/" && path_corrected.back() == '/') path_corrected.resize(path_corrected.size() - 1); @@ -131,7 +136,7 @@ void StorageSystemZooKeeper::fillData(MutableColumns & res_columns, const Contex for (size_t i = 0, size = nodes.size(); i < size; ++i) { auto res = futures[i].get(); - if (res.error == Coordination::ZNONODE) + if (res.error == Coordination::Error::ZNONODE) continue; /// Node was deleted meanwhile. const Coordination::Stat & stat = res.stat; diff --git a/src/Storages/System/attachSystemTables.cpp b/src/Storages/System/attachSystemTables.cpp index 585eab2b4d8..2b52f0fe5cc 100644 --- a/src/Storages/System/attachSystemTables.cpp +++ b/src/Storages/System/attachSystemTables.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -124,6 +125,7 @@ void attachSystemTablesServer(IDatabase & system_database, bool has_zookeeper) system_database.attachTable("mutations", StorageSystemMutations::create("mutations")); system_database.attachTable("replicas", StorageSystemReplicas::create("replicas")); system_database.attachTable("replication_queue", StorageSystemReplicationQueue::create("replication_queue")); + system_database.attachTable("distribution_queue", StorageSystemDistributionQueue::create("distribution_queue")); system_database.attachTable("dictionaries", StorageSystemDictionaries::create("dictionaries")); system_database.attachTable("models", StorageSystemModels::create("models")); system_database.attachTable("clusters", StorageSystemClusters::create("clusters")); diff --git a/src/Storages/extractKeyExpressionList.cpp b/src/Storages/extractKeyExpressionList.cpp new file mode 100644 index 00000000000..9dd44fe3902 --- /dev/null +++ b/src/Storages/extractKeyExpressionList.cpp @@ -0,0 +1,27 @@ +#include +#include +#include + +namespace DB +{ + ASTPtr extractKeyExpressionList(const ASTPtr & node) + { + if (!node) + return std::make_shared(); + + const auto * expr_func = node->as(); + + if (expr_func && expr_func->name == "tuple") + { + /// Primary key is specified in tuple, extract its arguments. + return expr_func->arguments->clone(); + } + else + { + /// Primary key consists of one column. + auto res = std::make_shared(); + res->children.push_back(node); + return res; + } + } +} diff --git a/src/Storages/extractKeyExpressionList.h b/src/Storages/extractKeyExpressionList.h new file mode 100644 index 00000000000..aa250bbd255 --- /dev/null +++ b/src/Storages/extractKeyExpressionList.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace DB +{ + ASTPtr extractKeyExpressionList(const ASTPtr & node); +} diff --git a/src/Storages/tests/CMakeLists.txt b/src/Storages/tests/CMakeLists.txt index 80dd4c8419c..292f7603838 100644 --- a/src/Storages/tests/CMakeLists.txt +++ b/src/Storages/tests/CMakeLists.txt @@ -1,9 +1,3 @@ -add_executable (system_numbers system_numbers.cpp) -target_link_libraries (system_numbers PRIVATE dbms clickhouse_storages_system clickhouse_common_io) - -add_executable (storage_log storage_log.cpp) -target_link_libraries (storage_log PRIVATE dbms) - add_executable (part_name part_name.cpp) target_link_libraries (part_name PRIVATE dbms) @@ -23,7 +17,14 @@ add_executable (get_abandonable_lock_in_all_partitions get_abandonable_lock_in_a target_link_libraries (get_abandonable_lock_in_all_partitions PRIVATE dbms clickhouse_common_config clickhouse_common_zookeeper) add_executable (transform_part_zk_nodes transform_part_zk_nodes.cpp) -target_link_libraries (transform_part_zk_nodes PRIVATE dbms clickhouse_common_config clickhouse_common_zookeeper string_utils ${Boost_PROGRAM_OPTIONS_LIBRARY}) +target_link_libraries (transform_part_zk_nodes + PRIVATE + boost::program_options + clickhouse_common_config + clickhouse_common_zookeeper + dbms + string_utils +) if (ENABLE_FUZZING) add_executable (mergetree_checksum_fuzzer mergetree_checksum_fuzzer.cpp) diff --git a/src/Storages/tests/get_current_inserts_in_replicated.cpp b/src/Storages/tests/get_current_inserts_in_replicated.cpp index aba69684045..f1a87b5ca05 100644 --- a/src/Storages/tests/get_current_inserts_in_replicated.cpp +++ b/src/Storages/tests/get_current_inserts_in_replicated.cpp @@ -86,7 +86,7 @@ try for (BlockInfo & block : block_infos) { Coordination::GetResponse resp = block.contents_future.get(); - if (!resp.error && lock_holder_paths.count(resp.data)) + if (resp.error == Coordination::Error::ZOK && lock_holder_paths.count(resp.data)) { ++total_count; current_inserts[block.partition].insert(block.number); diff --git a/src/Storages/tests/gtest_transform_query_for_external_database.cpp b/src/Storages/tests/gtest_transform_query_for_external_database.cpp index bf86322a676..318d667d9b0 100644 --- a/src/Storages/tests/gtest_transform_query_for_external_database.cpp +++ b/src/Storages/tests/gtest_transform_query_for_external_database.cpp @@ -18,6 +18,8 @@ using namespace DB; /// NOTE How to do better? struct State { + State(const State&) = delete; + Context context; NamesAndTypesList columns{ {"column", std::make_shared()}, @@ -27,23 +29,24 @@ struct State {"create_time", std::make_shared()}, }; + static const State & instance() + { + static State state; + return state; + } + +private: explicit State() : context(getContext().context) { registerFunctions(); DatabasePtr database = std::make_shared("test", context); database->attachTable("table", StorageMemory::create(StorageID("test", "table"), ColumnsDescription{columns}, ConstraintsDescription{})); - context.makeGlobalContext(); DatabaseCatalog::instance().attachDatabase("test", database); context.setCurrentDatabase("test"); } }; -State getState() -{ - static State state; - return state; -} static void check(const std::string & query, const std::string & expected, const Context & context, const NamesAndTypesList & columns) { @@ -60,7 +63,7 @@ static void check(const std::string & query, const std::string & expected, const TEST(TransformQueryForExternalDatabase, InWithSingleElement) { - const State & state = getState(); + const State & state = State::instance(); check("SELECT column FROM test.table WHERE 1 IN (1)", R"(SELECT "column" FROM "test"."table" WHERE 1)", @@ -75,7 +78,7 @@ TEST(TransformQueryForExternalDatabase, InWithSingleElement) TEST(TransformQueryForExternalDatabase, Like) { - const State & state = getState(); + const State & state = State::instance(); check("SELECT column FROM test.table WHERE column LIKE '%hello%'", R"(SELECT "column" FROM "test"."table" WHERE "column" LIKE '%hello%')", @@ -87,7 +90,7 @@ TEST(TransformQueryForExternalDatabase, Like) TEST(TransformQueryForExternalDatabase, Substring) { - const State & state = getState(); + const State & state = State::instance(); check("SELECT column FROM test.table WHERE left(column, 10) = RIGHT(column, 10) AND SUBSTRING(column FROM 1 FOR 2) = 'Hello'", R"(SELECT "column" FROM "test"."table")", @@ -96,7 +99,7 @@ TEST(TransformQueryForExternalDatabase, Substring) TEST(TransformQueryForExternalDatabase, MultipleAndSubqueries) { - const State & state = getState(); + const State & state = State::instance(); check("SELECT column FROM test.table WHERE 1 = 1 AND toString(column) = '42' AND column = 42 AND left(column, 10) = RIGHT(column, 10) AND column IN (1, 42) AND SUBSTRING(column FROM 1 FOR 2) = 'Hello' AND column != 4", R"(SELECT "column" FROM "test"."table" WHERE 1 AND ("column" = 42) AND ("column" IN (1, 42)) AND ("column" != 4))", @@ -108,7 +111,7 @@ TEST(TransformQueryForExternalDatabase, MultipleAndSubqueries) TEST(TransformQueryForExternalDatabase, Issue7245) { - const State & state = getState(); + const State & state = State::instance(); check("select apply_id from test.table where apply_type = 2 and create_time > addDays(toDateTime('2019-01-01 01:02:03'),-7) and apply_status in (3,4)", R"(SELECT "apply_id", "apply_type", "apply_status", "create_time" FROM "test"."table" WHERE ("apply_type" = 2) AND ("create_time" > '2018-12-25 01:02:03') AND ("apply_status" IN (3, 4)))", diff --git a/src/Storages/tests/storage_log.cpp b/src/Storages/tests/storage_log.cpp deleted file mode 100644 index 1e1ab4c682f..00000000000 --- a/src/Storages/tests/storage_log.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -int main(int, char **) -try -{ - using namespace DB; - - const size_t rows = 10000000; - - /// create table with a pair of columns - - NamesAndTypesList names_and_types; - names_and_types.emplace_back("a", std::make_shared()); - names_and_types.emplace_back("b", std::make_shared()); - - SharedContextHolder shared_context = Context::createShared(); - auto context = Context::createGlobal(shared_context.get()); - context.makeGlobalContext(); - context.setPath("./"); - - DiskPtr disk = std::make_unique("default", "./", 0); - StoragePtr table = StorageLog::create(disk, "table/", StorageID("test", "test"), ColumnsDescription{names_and_types}, ConstraintsDescription{}, 1048576); - - table->startup(); - - /// write into it - { - Block block; - - { - ColumnWithTypeAndName column; - column.name = "a"; - column.type = table->getColumns().getPhysical("a").type; - auto col = column.type->createColumn(); - ColumnUInt64::Container & vec = typeid_cast(*col).getData(); - - vec.resize(rows); - for (size_t i = 0; i < rows; ++i) - vec[i] = i; - - column.column = std::move(col); - block.insert(column); - } - - { - ColumnWithTypeAndName column; - column.name = "b"; - column.type = table->getColumns().getPhysical("b").type; - auto col = column.type->createColumn(); - ColumnUInt8::Container & vec = typeid_cast(*col).getData(); - - vec.resize(rows); - for (size_t i = 0; i < rows; ++i) - vec[i] = i * 2; - - column.column = std::move(col); - block.insert(column); - } - - BlockOutputStreamPtr out = table->write({}, context); - out->write(block); - } - - /// read from it - { - Names column_names; - column_names.push_back("a"); - column_names.push_back("b"); - - QueryProcessingStage::Enum stage = table->getQueryProcessingStage(context); - - BlockInputStreamPtr in = std::make_shared(std::move(table->read(column_names, {}, context, stage, 8192, 1)[0])); - - Block sample; - { - ColumnWithTypeAndName col; - col.type = std::make_shared(); - sample.insert(std::move(col)); - } - { - ColumnWithTypeAndName col; - col.type = std::make_shared(); - sample.insert(std::move(col)); - } - - WriteBufferFromOStream out_buf(std::cout); - - LimitBlockInputStream in_limit(in, 10, 0); - BlockOutputStreamPtr output = FormatFactory::instance().getOutput("TabSeparated", out_buf, sample, context); - - copyData(in_limit, *output); - } - - return 0; -} -catch (const DB::Exception & e) -{ - std::cerr << e.what() << ", " << e.displayText() << std::endl; - return 1; -} diff --git a/src/Storages/tests/system_numbers.cpp b/src/Storages/tests/system_numbers.cpp deleted file mode 100644 index 6955c90b74e..00000000000 --- a/src/Storages/tests/system_numbers.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include - -#include -#include -#include -#include -#include -#include -#include -#include - - -int main(int, char **) -try -{ - using namespace DB; - - StoragePtr table = StorageSystemNumbers::create(StorageID("test", "numbers"), false); - - Names column_names; - column_names.push_back("number"); - - Block sample; - ColumnWithTypeAndName col; - col.type = std::make_shared(); - sample.insert(std::move(col)); - - WriteBufferFromOStream out_buf(std::cout); - - SharedContextHolder shared_context = Context::createShared(); - auto context = Context::createGlobal(shared_context.get()); - context.makeGlobalContext(); - QueryProcessingStage::Enum stage = table->getQueryProcessingStage(context); - - auto stream = std::make_shared(std::move(table->read(column_names, {}, context, stage, 10, 1)[0])); - LimitBlockInputStream input(stream, 10, 96); - BlockOutputStreamPtr out = FormatFactory::instance().getOutput("TabSeparated", out_buf, sample, context); - - copyData(input, *out); - - return 0; -} -catch (const DB::Exception & e) -{ - std::cerr << e.what() << ", " << e.displayText() << std::endl; - return 1; -} diff --git a/src/Storages/tests/test_alter_distributed.sql b/src/Storages/tests/test_alter_distributed.sql deleted file mode 100644 index 0578d340276..00000000000 --- a/src/Storages/tests/test_alter_distributed.sql +++ /dev/null @@ -1,28 +0,0 @@ -create database if not exists test; - -drop table if exists test.merge_distributed; -drop table if exists test.merge_distributed1; - -create table test.merge_distributed1 ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), tuple(CounterID, StartDate, intHash32(UserID), VisitID, ClickLogID), 8192, Sign); -insert into test.merge_distributed1 values (1, '2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); - -create table test.merge_distributed ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = Distributed(self, test, merge_distributed1); - -alter table test.merge_distributed1 add column dummy String after CounterID; -alter table test.merge_distributed add column dummy String after CounterID; - -describe table test.merge_distributed; -show create table test.merge_distributed; - -insert into test.merge_distributed1 values (1, 'Hello, Alter Table!','2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); -select CounterID, dummy from test.merge_distributed where dummy <> '' limit 10; - -alter table test.merge_distributed drop column dummy; - -describe table test.merge_distributed; -show create table test.merge_distributed; - ---error: should fall, because there is no `dummy1` column -alter table test.merge_distributed add column dummy1 String after CounterID; -select CounterID, dummy1 from test.merge_distributed where dummy1 <> '' limit 10; - diff --git a/src/Storages/tests/test_alter_merge.sql b/src/Storages/tests/test_alter_merge.sql deleted file mode 100644 index 252577ddc37..00000000000 --- a/src/Storages/tests/test_alter_merge.sql +++ /dev/null @@ -1,35 +0,0 @@ -create database if not exists test; - -drop table if exists test.merge; -drop table if exists test.merge1; -drop table if exists test.merge2; - -create table test.merge1 ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), tuple(CounterID, StartDate, intHash32(UserID), VisitID, ClickLogID), 8192, Sign); -insert into test.merge1 values (1, '2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); - -create table test.merge2 ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), tuple(CounterID, StartDate, intHash32(UserID), VisitID, ClickLogID), 8192, Sign); -insert into test.merge2 values (2, '2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); - -create table test.merge ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = Merge(test, 'merge\[0-9\]'); - -alter table test.merge1 add column dummy String after CounterID; -alter table test.merge2 add column dummy String after CounterID; -alter table test.merge add column dummy String after CounterID; - -describe table test.merge; -show create table test.merge; - -insert into test.merge1 values (1, 'Hello, Alter Table!','2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); - -select CounterID, dummy from test.merge where dummy <> '' limit 10; - - -alter table test.merge drop column dummy; - -describe table test.merge; -show create table test.merge; - ---error: must correctly fall into the alter -alter table test.merge add column dummy1 String after CounterID; -select CounterID, dummy1 from test.merge where dummy1 <> '' limit 10; - diff --git a/src/Storages/tests/test_alter_merge_tree.sql b/src/Storages/tests/test_alter_merge_tree.sql deleted file mode 100644 index 5ac361acc5c..00000000000 --- a/src/Storages/tests/test_alter_merge_tree.sql +++ /dev/null @@ -1,17 +0,0 @@ -create database if not exists test; - -drop table if exists test.merge_tree; - -create table test.merge_tree ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), tuple(CounterID, StartDate, intHash32(UserID), VisitID, ClickLogID), 8192, Sign); - -insert into test.merge_tree values (1, '2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3) -alter table test.merge_tree add column dummy String after CounterID; -describe table test.merge_tree; - -insert into test.merge_tree values (1, 'Hello, Alter Table!','2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3) - -select CounterID, dummy from test.merge_tree where dummy <> '' limit 10; - -alter table test.merge_tree drop column dummy; - -describe table test.merge_tree; diff --git a/src/Storages/tests/transform_part_zk_nodes.cpp b/src/Storages/tests/transform_part_zk_nodes.cpp index 2b4f6b7ff5c..35e44bb9446 100644 --- a/src/Storages/tests/transform_part_zk_nodes.cpp +++ b/src/Storages/tests/transform_part_zk_nodes.cpp @@ -76,7 +76,7 @@ try } catch (const Coordination::Exception & e) { - if (e.code == Coordination::ZNONODE) + if (e.code == Coordination::Error::ZNONODE) continue; throw; } diff --git a/src/Storages/ya.make b/src/Storages/ya.make index 8a36fad696f..18f62504e1f 100644 --- a/src/Storages/ya.make +++ b/src/Storages/ya.make @@ -121,6 +121,7 @@ SRCS( System/StorageSystemQuotasUsage.cpp System/StorageSystemReplicas.cpp System/StorageSystemReplicationQueue.cpp + System/StorageSystemDistributionQueue.cpp System/StorageSystemRoleGrants.cpp System/StorageSystemRoles.cpp System/StorageSystemRowPolicies.cpp @@ -164,6 +165,7 @@ SRCS( StorageMySQL.cpp StorageNull.cpp StorageReplicatedMergeTree.cpp + StorageS3Settings.cpp StorageSet.cpp StorageStripeLog.cpp StorageTinyLog.cpp @@ -173,6 +175,7 @@ SRCS( StorageXDBC.cpp transformQueryForExternalDatabase.cpp VirtualColumnUtils.cpp + extractKeyExpressionList.cpp TTLDescription.cpp ) diff --git a/src/TableFunctions/TableFunctionGenerateRandom.cpp b/src/TableFunctions/TableFunctionGenerateRandom.cpp index 3b3db1c2510..548db38515c 100644 --- a/src/TableFunctions/TableFunctionGenerateRandom.cpp +++ b/src/TableFunctions/TableFunctionGenerateRandom.cpp @@ -21,6 +21,7 @@ namespace DB namespace ErrorCodes { + extern const int BAD_ARGUMENTS; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int LOGICAL_ERROR; } @@ -44,6 +45,18 @@ StoragePtr TableFunctionGenerateRandom::executeImpl(const ASTPtr & ast_function, " structure, [random_seed, max_string_length, max_array_length].", ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); + // All the arguments must be literals. + for (const auto & arg : args) + { + if (!arg->as()) + { + throw Exception(fmt::format( + "All arguments of table function '{}' must be literals. " + "Got '{}' instead", getName(), arg->formatForErrorMessage()), + ErrorCodes::BAD_ARGUMENTS); + } + } + /// Parsing first argument as table structure and creating a sample block std::string structure = args[0]->as().value.safeGet(); diff --git a/src/TableFunctions/TableFunctionMerge.cpp b/src/TableFunctions/TableFunctionMerge.cpp index cd924270f7c..ee447a13174 100644 --- a/src/TableFunctions/TableFunctionMerge.cpp +++ b/src/TableFunctions/TableFunctionMerge.cpp @@ -34,7 +34,8 @@ static NamesAndTypesList chooseColumns(const String & source_database, const Str auto iterator = database->getTablesIterator(context, table_name_match); if (iterator->isValid()) - any_table = iterator->table(); + if (const auto & table = iterator->table()) + any_table = table; } if (!any_table) diff --git a/src/TableFunctions/TableFunctionValues.cpp b/src/TableFunctions/TableFunctionValues.cpp index 4e166b10d8f..5ecd978146c 100644 --- a/src/TableFunctions/TableFunctionValues.cpp +++ b/src/TableFunctions/TableFunctionValues.cpp @@ -25,6 +25,7 @@ namespace DB namespace ErrorCodes { + extern const int BAD_ARGUMENTS; extern const int LOGICAL_ERROR; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; } @@ -75,6 +76,13 @@ StoragePtr TableFunctionValues::executeImpl(const ASTPtr & ast_function, const C ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH); /// Parsing first argument as table structure and creating a sample block + if (!args[0]->as()) + { + throw Exception(fmt::format( + "The first argument of table function '{}' must be a literal. " + "Got '{}' instead", getName(), args[0]->formatForErrorMessage()), + ErrorCodes::BAD_ARGUMENTS); + } std::string structure = args[0]->as().value.safeGet(); ColumnsDescription columns = parseColumnsListFromString(structure, context); diff --git a/src/TableFunctions/parseColumnsListForTableFunction.cpp b/src/TableFunctions/parseColumnsListForTableFunction.cpp index 8eea3edf9bd..5221d96e086 100644 --- a/src/TableFunctions/parseColumnsListForTableFunction.cpp +++ b/src/TableFunctions/parseColumnsListForTableFunction.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -11,27 +12,20 @@ namespace DB namespace ErrorCodes { extern const int LOGICAL_ERROR; - extern const int SYNTAX_ERROR; } ColumnsDescription parseColumnsListFromString(const std::string & structure, const Context & context) { - Expected expected; - - Tokens tokens(structure.c_str(), structure.c_str() + structure.size()); - IParser::Pos token_iterator(tokens, context.getSettingsRef().max_parser_depth); - ParserColumnDeclarationList parser; - ASTPtr columns_list_raw; + const Settings & settings = context.getSettingsRef(); - if (!parser.parse(token_iterator, columns_list_raw, expected)) - throw Exception("Cannot parse columns declaration list.", ErrorCodes::SYNTAX_ERROR); + ASTPtr columns_list_raw = parseQuery(parser, structure, "columns declaration list", settings.max_query_size, settings.max_parser_depth); auto * columns_list = dynamic_cast(columns_list_raw.get()); if (!columns_list) throw Exception("Could not cast AST to ASTExpressionList", ErrorCodes::LOGICAL_ERROR); - return InterpreterCreateQuery::getColumnsDescription(*columns_list, context, !context.getSettingsRef().allow_suspicious_codecs); + return InterpreterCreateQuery::getColumnsDescription(*columns_list, context, !settings.allow_suspicious_codecs); } } diff --git a/tests/config/log_queries.xml b/tests/config/log_queries.xml new file mode 100644 index 00000000000..25261072ade --- /dev/null +++ b/tests/config/log_queries.xml @@ -0,0 +1,7 @@ + + + + 1 + + + diff --git a/tests/config/metric_log.xml b/tests/config/metric_log.xml new file mode 100644 index 00000000000..0ca9f162416 --- /dev/null +++ b/tests/config/metric_log.xml @@ -0,0 +1,8 @@ + + + system + metric_log
+ 7500 + 1000 +
+
diff --git a/tests/integration/helpers/cluster.py b/tests/integration/helpers/cluster.py index 90337ed53c5..1c1f758b291 100644 --- a/tests/integration/helpers/cluster.py +++ b/tests/integration/helpers/cluster.py @@ -19,6 +19,7 @@ import pprint import psycopg2 import pymongo import pymysql +import cassandra.cluster from dicttoxml import dicttoxml from kazoo.client import KazooClient from kazoo.exceptions import KazooException @@ -108,6 +109,7 @@ class ClickHouseCluster: self.base_zookeeper_cmd = None self.base_mysql_cmd = [] self.base_kafka_cmd = [] + self.base_cassandra_cmd = [] self.pre_zookeeper_commands = [] self.instances = {} self.with_zookeeper = False @@ -119,6 +121,7 @@ class ClickHouseCluster: self.with_mongo = False self.with_net_trics = False self.with_redis = False + self.with_cassandra = False self.with_minio = False self.minio_host = "minio1" @@ -147,7 +150,7 @@ class ClickHouseCluster: def add_instance(self, name, config_dir=None, main_configs=None, user_configs=None, macros=None, with_zookeeper=False, with_mysql=False, with_kafka=False, clickhouse_path_dir=None, with_odbc_drivers=False, with_postgres=False, with_hdfs=False, with_mongo=False, - with_redis=False, with_minio=False, + with_redis=False, with_minio=False, with_cassandra=False, hostname=None, env_variables=None, image="yandex/clickhouse-integration-test", stay_alive=False, ipv4_address=None, ipv6_address=None, with_installed_binary=False, tmpfs=None, zookeeper_docker_compose_path=None, zookeeper_use_tmpfs=True): @@ -169,7 +172,7 @@ class ClickHouseCluster: instance = ClickHouseInstance( self, self.base_dir, name, config_dir, main_configs or [], user_configs or [], macros or {}, with_zookeeper, - self.zookeeper_config_path, with_mysql, with_kafka, with_mongo, with_redis, with_minio, + self.zookeeper_config_path, with_mysql, with_kafka, with_mongo, with_redis, with_minio, with_cassandra, self.base_configs_dir, self.server_bin_path, self.odbc_bridge_bin_path, clickhouse_path_dir, with_odbc_drivers, hostname=hostname, env_variables=env_variables or {}, image=image, stay_alive=stay_alive, ipv4_address=ipv4_address, @@ -265,6 +268,12 @@ class ClickHouseCluster: self.project_name, '--file', p.join(DOCKER_COMPOSE_DIR, 'docker_compose_minio.yml')] cmds.append(self.base_minio_cmd) + if with_cassandra and not self.with_cassandra: + self.with_cassandra = True + self.base_cmd.extend(['--file', p.join(DOCKER_COMPOSE_DIR, 'docker_compose_cassandra.yml')]) + self.base_cassandra_cmd = ['docker-compose', '--project-directory', self.base_dir, '--project-name', + self.project_name, '--file', p.join(DOCKER_COMPOSE_DIR, 'docker_compose_cassandra.yml')] + return instance def get_instance_docker_id(self, instance_name): @@ -451,6 +460,18 @@ class ClickHouseCluster: logging.warning("Can't connect to SchemaRegistry: %s", str(ex)) time.sleep(1) + def wait_cassandra_to_start(self, timeout=30): + cass_client = cassandra.cluster.Cluster(["localhost"], port="9043") + start = time.time() + while time.time() - start < timeout: + try: + cass_client.connect() + logging.info("Connected to Cassandra") + return + except Exception as ex: + logging.warning("Can't connect to Cassandra: %s", str(ex)) + time.sleep(1) + def start(self, destroy_dirs=True): if self.is_up: return @@ -527,6 +548,10 @@ class ClickHouseCluster: logging.info("Trying to connect to Minio...") self.wait_minio_to_start() + if self.with_cassandra and self.base_cassandra_cmd: + subprocess_check_call(self.base_cassandra_cmd + ['up', '-d', '--force-recreate']) + self.wait_cassandra_to_start() + clickhouse_start_cmd = self.base_cmd + ['up', '-d', '--no-recreate'] logging.info("Trying to create ClickHouse instance by command %s", ' '.join(map(str, clickhouse_start_cmd))) subprocess_check_call(clickhouse_start_cmd) @@ -656,7 +681,7 @@ class ClickHouseInstance: def __init__( self, cluster, base_path, name, custom_config_dir, custom_main_configs, custom_user_configs, macros, - with_zookeeper, zookeeper_config_path, with_mysql, with_kafka, with_mongo, with_redis, with_minio, + with_zookeeper, zookeeper_config_path, with_mysql, with_kafka, with_mongo, with_redis, with_minio, with_cassandra, base_configs_dir, server_bin_path, odbc_bridge_bin_path, clickhouse_path_dir, with_odbc_drivers, hostname=None, env_variables=None, image="yandex/clickhouse-integration-test", @@ -686,6 +711,7 @@ class ClickHouseInstance: self.with_mongo = with_mongo self.with_redis = with_redis self.with_minio = with_minio + self.with_cassandra = with_cassandra self.path = p.join(self.cluster.instances_dir, name) self.docker_compose_path = p.join(self.path, 'docker_compose.yml') diff --git a/tests/integration/test_always_fetch_merged/test.py b/tests/integration/test_always_fetch_merged/test.py index 63ab6f6b5ea..79458633081 100644 --- a/tests/integration/test_always_fetch_merged/test.py +++ b/tests/integration/test_always_fetch_merged/test.py @@ -55,9 +55,13 @@ def test_replica_always_download(started_cluster): node1.query("SYSTEM START MERGES") - time.sleep(3) - - node1_parts = node1.query("SELECT COUNT() FROM system.parts WHERE table = 'test_table' and active=1").strip() - node2_parts = node2.query("SELECT COUNT() FROM system.parts WHERE table = 'test_table' and active=1").strip() - assert int(node1_parts) < 10 # something merged - assert int(node2_parts) < 10 + for i in range(30): + node1_parts = node1.query("SELECT COUNT() FROM system.parts WHERE table = 'test_table' and active=1").strip() + node2_parts = node2.query("SELECT COUNT() FROM system.parts WHERE table = 'test_table' and active=1").strip() + if int(node1_parts) < 10 and int(node2_parts) < 10: + break + else: + time.sleep(0.5) + else: + assert int(node1_parts) < 10 + assert int(node2_parts) < 10 diff --git a/tests/integration/test_cluster_copier/test.py b/tests/integration/test_cluster_copier/test.py index 440f0fc016b..9c2bcc22ef7 100644 --- a/tests/integration/test_cluster_copier/test.py +++ b/tests/integration/test_cluster_copier/test.py @@ -1,29 +1,27 @@ import os -import os.path as p import sys import time -import datetime +import kazoo import pytest -from contextlib import contextmanager import docker -from kazoo.client import KazooClient +import random +from contextlib import contextmanager +from helpers.cluster import ClickHouseCluster +from helpers.test_tools import TSV CURRENT_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0, os.path.dirname(CURRENT_TEST_DIR)) -from helpers.cluster import ClickHouseCluster -from helpers.test_tools import TSV -COPYING_FAIL_PROBABILITY = 0.33 -MOVING_FAIL_PROBABILITY = 0.33 -cluster = None +COPYING_FAIL_PROBABILITY = 0.2 +MOVING_FAIL_PROBABILITY = 0.2 +cluster = ClickHouseCluster(__file__) def check_all_hosts_sucesfully_executed(tsv_content, num_hosts): M = TSV.toMat(tsv_content) hosts = [(l[0], l[1]) for l in M] # (host, port) codes = [l[2] for l in M] - messages = [l[3] for l in M] assert len(hosts) == num_hosts and len(set(hosts)) == num_hosts, "\n" + tsv_content assert len(set(codes)) == 1, "\n" + tsv_content @@ -36,7 +34,7 @@ def ddl_check_query(instance, query, num_hosts=3): return contents -@pytest.fixture(scope="function") +@pytest.fixture(scope="module") def started_cluster(): global cluster try: @@ -51,8 +49,6 @@ def started_cluster(): } } - cluster = ClickHouseCluster(__file__) - for cluster_name, shards in clusters_schema.iteritems(): for shard_name, replicas in shards.iteritems(): for replica_name in replicas: @@ -66,7 +62,6 @@ def started_cluster(): yield cluster finally: - pass cluster.shutdown() @@ -222,6 +217,11 @@ def execute_task(task, cmd_options): zk = cluster.get_kazoo_client('zoo1') print "Use ZooKeeper server: {}:{}".format(zk.hosts[0][0], zk.hosts[0][1]) + try: + zk.delete("/clickhouse-copier", recursive=True) + except kazoo.exceptions.NoNodeError: + print "No node /clickhouse-copier. It is Ok in first test." + zk_task_path = task.zk_task_path zk.ensure_path(zk_task_path) zk.create(zk_task_path + "/description", task.copier_task_config) @@ -236,7 +236,10 @@ def execute_task(task, cmd_options): '--base-dir', '/var/log/clickhouse-server/copier'] cmd += cmd_options - for instance_name, instance in cluster.instances.iteritems(): + copiers = random.sample(cluster.instances.keys(), 3) + + for instance_name in copiers: + instance = cluster.instances[instance_name] container = instance.get_docker_handle() exec_id = docker_api.exec_create(container.id, cmd, stderr=True) docker_api.exec_start(exec_id, detach=True) @@ -245,12 +248,13 @@ def execute_task(task, cmd_options): print "Copier for {} ({}) has started".format(instance.name, instance.ip_address) # Wait for copiers stopping and check their return codes - for exec_id, instance in zip(copiers_exec_ids, cluster.instances.itervalues()): + for exec_id, instance_name in zip(copiers_exec_ids, copiers): + instance = cluster.instances[instance_name] while True: res = docker_api.exec_inspect(exec_id) if not res['Running']: break - time.sleep(1) + time.sleep(0.5) assert res['ExitCode'] == 0, "Instance: {} ({}). Info: {}".format(instance.name, instance.ip_address, repr(res)) @@ -307,12 +311,15 @@ def test_copy_with_recovering_after_move_faults(started_cluster, use_sample_offs else: execute_task(Task1(started_cluster), ['--move-fault-probability', str(MOVING_FAIL_PROBABILITY)]) +@pytest.mark.timeout(600) def test_copy_month_to_week_partition(started_cluster): execute_task(Task2(started_cluster), []) +@pytest.mark.timeout(600) def test_copy_month_to_week_partition_with_recovering(started_cluster): execute_task(Task2(started_cluster), ['--copy-fault-probability', str(COPYING_FAIL_PROBABILITY)]) +@pytest.mark.timeout(600) def test_copy_month_to_week_partition_with_recovering_after_move_faults(started_cluster): execute_task(Task2(started_cluster), ['--move-fault-probability', str(MOVING_FAIL_PROBABILITY)]) diff --git a/tests/integration/test_dictionaries_all_layouts_and_sources/external_sources.py b/tests/integration/test_dictionaries_all_layouts_and_sources/external_sources.py index 7f8a480704c..f6985e7de54 100644 --- a/tests/integration/test_dictionaries_all_layouts_and_sources/external_sources.py +++ b/tests/integration/test_dictionaries_all_layouts_and_sources/external_sources.py @@ -2,11 +2,13 @@ import warnings import pymysql.cursors import pymongo +import cassandra.cluster import redis import aerospike from tzlocal import get_localzone import datetime import os +import uuid class ExternalSource(object): @@ -405,6 +407,73 @@ class SourceHTTPS(SourceHTTPBase): def _get_schema(self): return "https" +class SourceCassandra(ExternalSource): + TYPE_MAPPING = { + 'UInt8': 'tinyint', + 'UInt16': 'smallint', + 'UInt32': 'int', + 'UInt64': 'bigint', + 'Int8': 'tinyint', + 'Int16': 'smallint', + 'Int32': 'int', + 'Int64': 'bigint', + 'UUID': 'uuid', + 'Date': 'date', + 'DateTime': 'timestamp', + 'String': 'text', + 'Float32': 'float', + 'Float64': 'double' + } + + def __init__(self, name, internal_hostname, internal_port, docker_hostname, docker_port, user, password): + ExternalSource.__init__(self, name, internal_hostname, internal_port, docker_hostname, docker_port, user, password) + self.structure = dict() + + def get_source_str(self, table_name): + return ''' + + {host} + {port} + test + {table} + 1 + "Int64_" < 1000000000000000000 + + '''.format( + host=self.docker_hostname, + port=self.docker_port, + table=table_name, + ) + + def prepare(self, structure, table_name, cluster): + self.client = cassandra.cluster.Cluster([self.internal_hostname], port=self.internal_port) + self.session = self.client.connect() + self.session.execute("create keyspace if not exists test with replication = {'class': 'SimpleStrategy', 'replication_factor' : 1};") + self.session.execute('drop table if exists test."{}"'.format(table_name)) + self.structure[table_name] = structure + columns = ['"' + col.name + '" ' + self.TYPE_MAPPING[col.field_type] for col in structure.get_all_fields()] + keys = ['"' + col.name + '"' for col in structure.keys] + query = 'create table test."{name}" ({columns}, primary key ({pk}));'.format( + name=table_name, columns=', '.join(columns), pk=', '.join(keys)) + self.session.execute(query) + self.prepared = True + + def get_value_to_insert(self, value, type): + if type == 'UUID': + return uuid.UUID(value) + elif type == 'DateTime': + local_datetime = datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S') + return get_localzone().localize(local_datetime) + return value + + def load_data(self, data, table_name): + names_and_types = [(field.name, field.field_type) for field in self.structure[table_name].get_all_fields()] + columns = ['"' + col[0] + '"' for col in names_and_types] + insert = 'insert into test."{table}" ({columns}) values ({args})'.format( + table=table_name, columns=','.join(columns), args=','.join(['%s']*len(columns))) + for row in data: + values = [self.get_value_to_insert(row.get_value_by_name(col[0]), col[1]) for col in names_and_types] + self.session.execute(insert, values) class SourceRedis(ExternalSource): def __init__( diff --git a/tests/integration/test_dictionaries_all_layouts_and_sources/test.py b/tests/integration/test_dictionaries_all_layouts_and_sources/test.py index 0aac0d27ff9..0a812ea2a8b 100644 --- a/tests/integration/test_dictionaries_all_layouts_and_sources/test.py +++ b/tests/integration/test_dictionaries_all_layouts_and_sources/test.py @@ -4,7 +4,7 @@ import os from helpers.cluster import ClickHouseCluster from dictionary import Field, Row, Dictionary, DictionaryStructure, Layout from external_sources import SourceMySQL, SourceClickHouse, SourceFile, SourceExecutableCache, SourceExecutableHashed -from external_sources import SourceMongo, SourceMongoURI, SourceHTTP, SourceHTTPS, SourceRedis +from external_sources import SourceMongo, SourceMongoURI, SourceHTTP, SourceHTTPS, SourceRedis, SourceCassandra import math SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) @@ -117,6 +117,7 @@ LAYOUTS = [ ] SOURCES = [ + SourceCassandra("Cassandra", "localhost", "9043", "cassandra1", "9042", "", ""), SourceMongo("MongoDB", "localhost", "27018", "mongo1", "27017", "root", "clickhouse"), SourceMongoURI("MongoDB_URI", "localhost", "27018", "mongo1", "27017", "root", "clickhouse"), SourceMySQL("MySQL", "localhost", "3308", "mysql1", "3306", "root", "clickhouse"), @@ -131,7 +132,7 @@ SOURCES = [ DICTIONARIES = [] -# Key-value dictionaries with onle one possible field for key +# Key-value dictionaries with only one possible field for key SOURCES_KV = [ SourceRedis("RedisSimple", "localhost", "6380", "redis1", "6379", "", "", storage_type="simple"), SourceRedis("RedisHash", "localhost", "6380", "redis1", "6379", "", "", storage_type="hash_map"), @@ -183,7 +184,7 @@ def setup_module(module): for fname in os.listdir(dict_configs_path): main_configs.append(os.path.join(dict_configs_path, fname)) cluster = ClickHouseCluster(__file__, base_configs_dir=os.path.join(SCRIPT_DIR, 'configs')) - node = cluster.add_instance('node', main_configs=main_configs, with_mysql=True, with_mongo=True, with_redis=True) + node = cluster.add_instance('node', main_configs=main_configs, with_mysql=True, with_mongo=True, with_redis=True, with_cassandra=True) cluster.add_instance('clickhouse1') diff --git a/tests/integration/test_distributed_format/configs/remote_servers.xml b/tests/integration/test_distributed_format/configs/remote_servers.xml index 7d8d64bb78b..5c86713bd78 100644 --- a/tests/integration/test_distributed_format/configs/remote_servers.xml +++ b/tests/integration/test_distributed_format/configs/remote_servers.xml @@ -8,5 +8,12 @@ + + + + not_existing + 9000 + +
diff --git a/tests/integration/test_distributed_format/test.py b/tests/integration/test_distributed_format/test.py index 211127f624a..291db89ae4c 100644 --- a/tests/integration/test_distributed_format/test.py +++ b/tests/integration/test_distributed_format/test.py @@ -11,6 +11,11 @@ from helpers.test_tools import assert_eq_with_retry cluster = ClickHouseCluster(__file__) node = cluster.add_instance('node', config_dir="configs", main_configs=['configs/remote_servers.xml']) +cluster_param = pytest.mark.parametrize("cluster", [ + ('test_cluster'), + ('test_cluster_2'), +]) + @pytest.fixture(scope="module") def started_cluster(): @@ -22,9 +27,9 @@ def started_cluster(): finally: cluster.shutdown() - -def test_single_file(started_cluster): - node.query("create table test.distr_1 (x UInt64, s String) engine = Distributed('test_cluster', database, table)") +@cluster_param +def test_single_file(started_cluster, cluster): + node.query("create table test.distr_1 (x UInt64, s String) engine = Distributed('{}', database, table)".format(cluster)) node.query("insert into test.distr_1 values (1, 'a'), (2, 'bb'), (3, 'ccc')", settings={"use_compact_format_in_distributed_parts_names": "1"}) query = "select * from file('/var/lib/clickhouse/data/test/distr_1/shard1_replica1/1.bin', 'Distributed')" @@ -41,8 +46,9 @@ def test_single_file(started_cluster): node.query("drop table test.distr_1") -def test_two_files(started_cluster): - node.query("create table test.distr_2 (x UInt64, s String) engine = Distributed('test_cluster', database, table)") +@cluster_param +def test_two_files(started_cluster, cluster): + node.query("create table test.distr_2 (x UInt64, s String) engine = Distributed('{}', database, table)".format(cluster)) node.query("insert into test.distr_2 values (0, '_'), (1, 'a')", settings={"use_compact_format_in_distributed_parts_names": "1"}) node.query("insert into test.distr_2 values (2, 'bb'), (3, 'ccc')", settings={"use_compact_format_in_distributed_parts_names": "1"}) @@ -60,8 +66,9 @@ def test_two_files(started_cluster): node.query("drop table test.distr_2") -def test_single_file_old(started_cluster): - node.query("create table test.distr_3 (x UInt64, s String) engine = Distributed('test_cluster', database, table)") +@cluster_param +def test_single_file_old(started_cluster, cluster): + node.query("create table test.distr_3 (x UInt64, s String) engine = Distributed('{}', database, table)".format(cluster)) node.query("insert into test.distr_3 values (1, 'a'), (2, 'bb'), (3, 'ccc')") query = "select * from file('/var/lib/clickhouse/data/test/distr_3/default@not_existing:9000/1.bin', 'Distributed')" diff --git a/tests/integration/test_host_ip_change/test.py b/tests/integration/test_host_ip_change/test.py index 1b6d4365ac9..ac35478277c 100644 --- a/tests/integration/test_host_ip_change/test.py +++ b/tests/integration/test_host_ip_change/test.py @@ -76,8 +76,6 @@ node3 = cluster.add_instance('node3', main_configs=['configs/listen_host.xml'], with_zookeeper=True, ipv6_address='2001:3984:3989::1:1113') node4 = cluster.add_instance('node4', main_configs=['configs/remote_servers.xml', 'configs/listen_host.xml', 'configs/dns_update_short.xml'], with_zookeeper=True, ipv6_address='2001:3984:3989::1:1114') -node5 = cluster.add_instance('node5', main_configs=['configs/listen_host.xml', 'configs/dns_update_short.xml'], - user_configs=['configs/users_with_hostname.xml'], ipv6_address='2001:3984:3989::1:1115') @pytest.fixture(scope="module") def cluster_with_dns_cache_update(): @@ -142,24 +140,39 @@ def test_dns_cache_update(cluster_with_dns_cache_update): assert TSV(node4.query("SELECT DISTINCT host_name, host_address FROM system.clusters WHERE cluster='lost_host_cluster'")) == TSV("lost_host\t127.0.0.1\n") assert TSV(node4.query("SELECT hostName()")) == TSV("node4") -def test_user_access_ip_change(cluster_with_dns_cache_update): - assert node3.query("SELECT * FROM remote('node5', 'system', 'one')") == "0\n" - assert node4.query("SELECT * FROM remote('node5', 'system', 'one')") == "0\n" +# Check SYSTEM DROP DNS CACHE on node5 and background cache update on node6 +node5 = cluster.add_instance('node5', main_configs=['configs/listen_host.xml', 'configs/dns_update_long.xml'], + user_configs=['configs/users_with_hostname.xml'], ipv6_address='2001:3984:3989::1:1115') +node6 = cluster.add_instance('node6', main_configs=['configs/listen_host.xml', 'configs/dns_update_short.xml'], + user_configs=['configs/users_with_hostname.xml'], ipv6_address='2001:3984:3989::1:1116') - set_hosts(node5, ['127.255.255.255 node3', '2001:3984:3989::1:8884 unknown_host']) +@pytest.mark.parametrize("node", [node5, node6]) +def test_user_access_ip_change(cluster_with_dns_cache_update, node): + node_name = node.name + node_num = node.name[-1] + # getaddrinfo(...) may hang for a log time without this options + node.exec_in_container(['bash', '-c', 'echo -e "options timeout:1\noptions attempts:2" >> /etc/resolv.conf'], privileged=True, user='root') - cluster.restart_instance_with_ip_change(node3, "2001:3984:3989::1:8883") - cluster.restart_instance_with_ip_change(node4, "2001:3984:3989::1:8884") + assert node3.query("SELECT * FROM remote('{}', 'system', 'one')".format(node_name)) == "0\n" + assert node4.query("SELECT * FROM remote('{}', 'system', 'one')".format(node_name)) == "0\n" + + set_hosts(node, ['127.255.255.255 node3', '2001:3984:3989::1:88{}4 unknown_host'.format(node_num)]) + + cluster.restart_instance_with_ip_change(node3, "2001:3984:3989::1:88{}3".format(node_num)) + cluster.restart_instance_with_ip_change(node4, "2001:3984:3989::1:88{}4".format(node_num)) with pytest.raises(QueryRuntimeException): - node3.query("SELECT * FROM remote('node5', 'system', 'one')") + node3.query("SELECT * FROM remote('{}', 'system', 'one')".format(node_name)) with pytest.raises(QueryRuntimeException): - node4.query("SELECT * FROM remote('node5', 'system', 'one')") + node4.query("SELECT * FROM remote('{}', 'system', 'one')".format(node_name)) # now wrong addresses are cached - set_hosts(node5, []) - # client is not allowed to connect, so execute it directly in container to send query from localhost - node5.exec_in_container(['bash', '-c', 'clickhouse client -q "SYSTEM DROP DNS CACHE"'], privileged=True, user='root') + set_hosts(node, []) + retry_count = 60 + if node_name == 'node5': + # client is not allowed to connect, so execute it directly in container to send query from localhost + node.exec_in_container(['bash', '-c', 'clickhouse client -q "SYSTEM DROP DNS CACHE"'], privileged=True, user='root') + retry_count = 1 - assert node3.query("SELECT * FROM remote('node5', 'system', 'one')") == "0\n" - assert node4.query("SELECT * FROM remote('node5', 'system', 'one')") == "0\n" + assert_eq_with_retry(node3, "SELECT * FROM remote('{}', 'system', 'one')".format(node_name), "0", retry_count=retry_count, sleep_time=1) + assert_eq_with_retry(node4, "SELECT * FROM remote('{}', 'system', 'one')".format(node_name), "0", retry_count=retry_count, sleep_time=1) diff --git a/tests/integration/test_insert_distributed_load_balancing/configs/remote_servers.xml b/tests/integration/test_insert_distributed_load_balancing/configs/remote_servers.xml index 61bc5af1f7d..bfcb1c0977b 100644 --- a/tests/integration/test_insert_distributed_load_balancing/configs/remote_servers.xml +++ b/tests/integration/test_insert_distributed_load_balancing/configs/remote_servers.xml @@ -1,6 +1,6 @@ - + true @@ -12,7 +12,21 @@ 9000 - + + + + + false + + n2 + 9000 + + + n1 + 9000 + + + diff --git a/tests/integration/test_insert_distributed_load_balancing/test.py b/tests/integration/test_insert_distributed_load_balancing/test.py index 99d74ddc8df..52ee3ba1c4a 100644 --- a/tests/integration/test_insert_distributed_load_balancing/test.py +++ b/tests/integration/test_insert_distributed_load_balancing/test.py @@ -11,6 +11,11 @@ cluster = ClickHouseCluster(__file__) n1 = cluster.add_instance('n1', main_configs=['configs/remote_servers.xml']) n2 = cluster.add_instance('n2', main_configs=['configs/remote_servers.xml']) +params = pytest.mark.parametrize('cluster,q', [ + ('internal_replication', 0), + ('no_internal_replication', 1), +]) + @pytest.fixture(scope='module', autouse=True) def start_cluster(): try: @@ -19,7 +24,7 @@ def start_cluster(): finally: cluster.shutdown() -def create_tables(**dist_settings): +def create_tables(cluster): n1.query('DROP TABLE IF EXISTS data') n2.query('DROP TABLE IF EXISTS data') n1.query('DROP TABLE IF EXISTS dist') @@ -29,39 +34,44 @@ def create_tables(**dist_settings): n1.query(""" CREATE TABLE dist AS data Engine=Distributed( - integration_test_cluster, + {cluster}, currentDatabase(), data, rand() ) - """, settings=dist_settings) + """.format(cluster=cluster)) -def insert_data(**settings): - create_tables(**settings) +def insert_data(cluster, **settings): + create_tables(cluster) n1.query('INSERT INTO dist SELECT * FROM numbers(10)', settings=settings) n1.query('SYSTEM FLUSH DISTRIBUTED dist') -def test_prefer_localhost_replica_1(): - insert_data() +@params +def test_prefer_localhost_replica_1(cluster, q): + insert_data(cluster) assert int(n1.query('SELECT count() FROM data')) == 10 - assert int(n2.query('SELECT count() FROM data')) == 0 + assert int(n2.query('SELECT count() FROM data')) == 10*q -def test_prefer_localhost_replica_1_load_balancing_in_order(): - insert_data(load_balancing='in_order') +@params +def test_prefer_localhost_replica_1_load_balancing_in_order(cluster, q): + insert_data(cluster, load_balancing='in_order') assert int(n1.query('SELECT count() FROM data')) == 10 - assert int(n2.query('SELECT count() FROM data')) == 0 + assert int(n2.query('SELECT count() FROM data')) == 10*q -def test_prefer_localhost_replica_0_load_balancing_nearest_hostname(): - insert_data(load_balancing='nearest_hostname', prefer_localhost_replica=0) +@params +def test_prefer_localhost_replica_0_load_balancing_nearest_hostname(cluster, q): + insert_data(cluster, load_balancing='nearest_hostname', prefer_localhost_replica=0) assert int(n1.query('SELECT count() FROM data')) == 10 - assert int(n2.query('SELECT count() FROM data')) == 0 + assert int(n2.query('SELECT count() FROM data')) == 10*q -def test_prefer_localhost_replica_0_load_balancing_in_order(): - insert_data(load_balancing='in_order', prefer_localhost_replica=0) - assert int(n1.query('SELECT count() FROM data')) == 0 +@params +def test_prefer_localhost_replica_0_load_balancing_in_order(cluster, q): + insert_data(cluster, load_balancing='in_order', prefer_localhost_replica=0) + assert int(n1.query('SELECT count() FROM data')) == 10*q assert int(n2.query('SELECT count() FROM data')) == 10 -def test_prefer_localhost_replica_0_load_balancing_in_order_sync(): - insert_data(load_balancing='in_order', prefer_localhost_replica=0, insert_distributed_sync=1) - assert int(n1.query('SELECT count() FROM data')) == 0 +@params +def test_prefer_localhost_replica_0_load_balancing_in_order_sync(cluster, q): + insert_data(cluster, load_balancing='in_order', prefer_localhost_replica=0, insert_distributed_sync=1) + assert int(n1.query('SELECT count() FROM data')) == 10*q assert int(n2.query('SELECT count() FROM data')) == 10 diff --git a/tests/integration/test_insert_into_distributed_sync_async/test.py b/tests/integration/test_insert_into_distributed_sync_async/test.py index 51f4b48a181..ebe6cba404c 100755 --- a/tests/integration/test_insert_into_distributed_sync_async/test.py +++ b/tests/integration/test_insert_into_distributed_sync_async/test.py @@ -86,7 +86,7 @@ def test_insertion_sync_fails_with_timeout(started_cluster): with pytest.raises(QueryRuntimeException): node1.query(''' SET insert_distributed_sync = 1, insert_distributed_timeout = 1; - INSERT INTO distributed_table SELECT today() as date, number as val FROM system.numbers''', timeout=5) + INSERT INTO distributed_table SELECT today() as date, number as val FROM system.numbers''') def test_insertion_without_sync_ignores_timeout(started_cluster): diff --git a/tests/integration/test_s3_with_proxy/proxy-resolver/entrypoint.sh b/tests/integration/test_s3_with_proxy/proxy-resolver/entrypoint.sh deleted file mode 100644 index e456be666a9..00000000000 --- a/tests/integration/test_s3_with_proxy/proxy-resolver/entrypoint.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash - -pip install bottle -python resolver.py diff --git a/tests/integration/test_s3_with_proxy/test.py b/tests/integration/test_s3_with_proxy/test.py index 11176b19f0c..dc326b719bf 100644 --- a/tests/integration/test_s3_with_proxy/test.py +++ b/tests/integration/test_s3_with_proxy/test.py @@ -14,9 +14,7 @@ def run_resolver(cluster): current_dir = os.path.dirname(__file__) cluster.copy_file_to_container(container_id, os.path.join(current_dir, "proxy-resolver", "resolver.py"), "resolver.py") - cluster.copy_file_to_container(container_id, os.path.join(current_dir, "proxy-resolver", "entrypoint.sh"), - "entrypoint.sh") - cluster.exec_in_container(container_id, ["/bin/bash", "entrypoint.sh"], detach=True) + cluster.exec_in_container(container_id, ["python", "resolver.py"], detach=True) @pytest.fixture(scope="module") diff --git a/tests/integration/test_storage_kafka/test.py b/tests/integration/test_storage_kafka/test.py index d2a2532bb9a..2a1b42f8e0e 100644 --- a/tests/integration/test_storage_kafka/test.py +++ b/tests/integration/test_storage_kafka/test.py @@ -233,6 +233,83 @@ def test_kafka_settings_new_syntax(kafka_cluster): members = describe_consumer_group('new') assert members[0]['client_id'] == u'instance test 1234' + +@pytest.mark.timeout(180) +def test_kafka_issue11308(kafka_cluster): + # Check that matview does respect Kafka SETTINGS + kafka_produce('issue11308', ['{"t": 123, "e": {"x": "woof"} }', '{"t": 123, "e": {"x": "woof"} }', '{"t": 124, "e": {"x": "test"} }']) + + instance.query(''' + CREATE TABLE test.persistent_kafka ( + time UInt64, + some_string String + ) + ENGINE = MergeTree() + ORDER BY time; + + CREATE TABLE test.kafka (t UInt64, `e.x` String) + ENGINE = Kafka + SETTINGS kafka_broker_list = 'kafka1:19092', + kafka_topic_list = 'issue11308', + kafka_group_name = 'issue11308', + kafka_format = 'JSONEachRow', + kafka_row_delimiter = '\\n', + kafka_flush_interval_ms=1000, + input_format_import_nested_json = 1; + + CREATE MATERIALIZED VIEW test.persistent_kafka_mv TO test.persistent_kafka AS + SELECT + `t` AS `time`, + `e.x` AS `some_string` + FROM test.kafka; + ''') + + time.sleep(9) + + result = instance.query('SELECT * FROM test.persistent_kafka ORDER BY time;') + + instance.query(''' + DROP TABLE test.persistent_kafka; + DROP TABLE test.persistent_kafka_mv; + ''') + + expected = '''\ +123 woof +123 woof +124 test +''' + assert TSV(result) == TSV(expected) + + +@pytest.mark.timeout(180) +def test_kafka_issue4116(kafka_cluster): + # Check that format_csv_delimiter parameter works now - as part of all available format settings. + kafka_produce('issue4116', ['1|foo', '2|bar', '42|answer','100|multi\n101|row\n103|message']) + + instance.query(''' + CREATE TABLE test.kafka (a UInt64, b String) + ENGINE = Kafka + SETTINGS kafka_broker_list = 'kafka1:19092', + kafka_topic_list = 'issue4116', + kafka_group_name = 'issue4116', + kafka_format = 'CSV', + kafka_row_delimiter = '\\n', + format_csv_delimiter = '|'; + ''') + + result = instance.query('SELECT * FROM test.kafka ORDER BY a;') + + expected = '''\ +1 foo +2 bar +42 answer +100 multi +101 row +103 message +''' + assert TSV(result) == TSV(expected) + + @pytest.mark.timeout(180) def test_kafka_consumer_hang(kafka_cluster): diff --git a/tests/integration/test_storage_s3/configs/defaultS3.xml b/tests/integration/test_storage_s3/configs/defaultS3.xml new file mode 100644 index 00000000000..26dc52f9e8f --- /dev/null +++ b/tests/integration/test_storage_s3/configs/defaultS3.xml @@ -0,0 +1,8 @@ + + + + http://resolver:8080 +
Authorization: Bearer TOKEN
+
+
+
diff --git a/tests/integration/test_storage_s3/s3_mock/mock_s3.py b/tests/integration/test_storage_s3/s3_mock/mock_s3.py new file mode 100644 index 00000000000..35b477d6b10 --- /dev/null +++ b/tests/integration/test_storage_s3/s3_mock/mock_s3.py @@ -0,0 +1,17 @@ +from bottle import abort, route, run, request + + +@route('/<_bucket>/<_path>') +def server(_bucket, _path): + for name in request.headers: + if name == 'Authorization' and request.headers[name] == u'Bearer TOKEN': + return '1, 2, 3' + abort(403) + + +@route('/') +def ping(): + return 'OK' + + +run(host='0.0.0.0', port=8080) diff --git a/tests/integration/test_storage_s3/test.py b/tests/integration/test_storage_s3/test.py index 9f124507e14..b25e5907e62 100644 --- a/tests/integration/test_storage_s3/test.py +++ b/tests/integration/test_storage_s3/test.py @@ -2,6 +2,7 @@ import json import logging import random import threading +import os import pytest @@ -9,7 +10,6 @@ from helpers.cluster import ClickHouseCluster, ClickHouseInstance import helpers.client - logging.getLogger().setLevel(logging.INFO) logging.getLogger().addHandler(logging.StreamHandler()) @@ -82,14 +82,16 @@ def get_nginx_access_logs(): def cluster(): try: cluster = ClickHouseCluster(__file__) - cluster.add_instance("restricted_dummy", main_configs=["configs/config_for_test_remote_host_filter.xml"], with_minio=True) - cluster.add_instance("dummy", with_minio=True) + cluster.add_instance("restricted_dummy", main_configs=["configs/config_for_test_remote_host_filter.xml"], + with_minio=True) + cluster.add_instance("dummy", with_minio=True, main_configs=["configs/defaultS3.xml"]) logging.info("Starting cluster...") cluster.start() logging.info("Cluster started") prepare_s3_bucket(cluster) logging.info("S3 bucket created") + run_s3_mock(cluster) yield cluster finally: @@ -199,14 +201,15 @@ def test_put_get_with_globs(cluster): for j in range(10): path = "{}_{}/{}.csv".format(i, random.choice(['a', 'b', 'c', 'd']), j) max_path = max(path, max_path) - values = "({},{},{})".format(i, j, i+j) + values = "({},{},{})".format(i, j, i + j) query = "insert into table function s3('http://{}:{}/{}/{}', 'CSV', '{}') values {}".format( cluster.minio_host, cluster.minio_port, bucket, path, table_format, values) run_query(instance, query) query = "select sum(column1), sum(column2), sum(column3), min(_file), max(_path) from s3('http://{}:{}/{}/*_{{a,b,c,d}}/%3f.csv', 'CSV', '{}')".format( cluster.minio_redirect_host, cluster.minio_redirect_port, bucket, table_format) - assert run_query(instance, query).splitlines() == ["450\t450\t900\t0.csv\t{bucket}/{max_path}".format(bucket=bucket, max_path=max_path)] + assert run_query(instance, query).splitlines() == [ + "450\t450\t900\t0.csv\t{bucket}/{max_path}".format(bucket=bucket, max_path=max_path)] # Test multipart put. @@ -307,3 +310,29 @@ def test_s3_glob_scheherazade(cluster): query = "select count(), sum(column1), sum(column2), sum(column3) from s3('http://{}:{}/{}/night_*/tale.csv', 'CSV', '{}')".format( cluster.minio_redirect_host, cluster.minio_redirect_port, bucket, table_format) assert run_query(instance, query).splitlines() == ["1001\t1001\t1001\t1001"] + + +def run_s3_mock(cluster): + logging.info("Starting s3 mock") + container_id = cluster.get_container_id('resolver') + current_dir = os.path.dirname(__file__) + cluster.copy_file_to_container(container_id, os.path.join(current_dir, "s3_mock", "mock_s3.py"), "mock_s3.py") + cluster.exec_in_container(container_id, ["python", "mock_s3.py"], detach=True) + logging.info("S3 mock started") + + +# Test get values in CSV format with default settings. +def test_get_csv_default(cluster): + ping_response = cluster.exec_in_container(cluster.get_container_id('resolver'), ["curl", "-s", "http://resolver:8080"]) + assert ping_response == 'OK', 'Expected "OK", but got "{}"'.format(ping_response) + + table_format = "column1 UInt32, column2 UInt32, column3 UInt32" + filename = "test.csv" + get_query = "select * from s3('http://resolver:8080/{bucket}/{file}', 'CSV', '{table_format}')".format( + bucket=cluster.minio_restricted_bucket, + file=filename, + table_format=table_format) + + instance = cluster.instances["dummy"] # type: ClickHouseInstance + result = run_query(instance, get_query) + assert result == '1\t2\t3\n' diff --git a/tests/integration/test_ttl_move/test.py b/tests/integration/test_ttl_move/test.py index ae2af34c35b..26bd36b8cb6 100644 --- a/tests/integration/test_ttl_move/test.py +++ b/tests/integration/test_ttl_move/test.py @@ -58,6 +58,13 @@ def get_used_disks_for_table(node, table_name, partition=None): ORDER BY modification_time """.format(name=table_name, suffix=suffix)).strip().split('\n') +def check_used_disks_with_retry(node, table_name, expected_disks, retries): + for _ in range(retries): + used_disks = get_used_disks_for_table(node, table_name) + if set(used_disks).issubset(expected_disks): + return True + time.sleep(0.5) + return False @pytest.mark.parametrize("name,engine,alter", [ ("mt_test_rule_with_invalid_destination","MergeTree()",0), @@ -651,12 +658,6 @@ def test_materialize_ttl_in_partition(started_cluster, name, engine): node1.query("DROP TABLE IF EXISTS {}".format(name)) -def start_thread(*args, **kwargs): - thread = threading.Thread(*args, **kwargs) - thread.start() - return thread - - @pytest.mark.parametrize("name,engine,positive", [ ("mt_test_alter_multiple_ttls_positive", "MergeTree()", True), ("mt_replicated_test_alter_multiple_ttls_positive", "ReplicatedMergeTree('/clickhouse/replicated_test_alter_multiple_ttls_positive', '1')", True), @@ -687,8 +688,6 @@ limitations under the License.""" """ now = time.time() try: - sleeps = { delay : start_thread(target=time.sleep, args=(delay,)) for delay in [16, 26] } - node1.query(""" CREATE TABLE {name} ( p1 Int64, @@ -706,7 +705,7 @@ limitations under the License.""" ALTER TABLE {name} MODIFY TTL d1 + INTERVAL 0 SECOND TO DISK 'jbod2', d1 + INTERVAL 14 SECOND TO VOLUME 'external', - d1 + INTERVAL 24 SECOND DELETE + d1 + INTERVAL 19 SECOND DELETE """.format(name=name)) for p in range(3): @@ -724,18 +723,33 @@ limitations under the License.""" assert node1.query("SELECT count() FROM {name}".format(name=name)).splitlines() == ["6"] - sleeps[16].join() + if positive: + expected_disks = {"external"} + else: + expected_disks = {"jbod1", "jbod2"} - used_disks = get_used_disks_for_table(node1, name) - assert set(used_disks) == {"external"} if positive else {"jbod1", "jbod2"} + check_used_disks_with_retry(node1, name, expected_disks, 50) assert node1.query("SELECT count() FROM {name}".format(name=name)).splitlines() == ["6"] - sleeps[26].join() + time.sleep(5) - node1.query("OPTIMIZE TABLE {name} FINAL".format(name=name)) + for i in range(50): + rows_count = int(node1.query("SELECT count() FROM {name}".format(name=name)).strip()) + if positive: + if rows_count == 0: + break + else: + if rows_count == 3: + break + node1.query("OPTIMIZE TABLE {name} FINAL".format(name=name)) + time.sleep(0.5) - assert node1.query("SELECT count() FROM {name}".format(name=name)).splitlines() == ["0"] if positive else ["3"] + + if positive: + assert rows_count == 0 + else: + assert rows_count == 3 finally: node1.query("DROP TABLE IF EXISTS {name}".format(name=name)) @@ -815,7 +829,11 @@ def test_concurrent_alter_with_ttl_move(started_cluster, name, engine): def optimize_table(num): for i in range(num): - node1.query("OPTIMIZE TABLE {} FINAL".format(name)) + try: # optimize may throw after concurrent alter + node1.query("OPTIMIZE TABLE {} FINAL".format(name), settings={'optimize_throw_if_noop': '1'}) + break + except: + pass p = Pool(15) tasks = [] @@ -886,3 +904,93 @@ def test_double_move_while_select(started_cluster, name, positive): finally: node1.query("DROP TABLE IF EXISTS {name}".format(name=name)) + + +@pytest.mark.parametrize("name,engine,positive", [ + ("mt_test_alter_with_merge_do_not_work","MergeTree()",0), + ("replicated_mt_test_alter_with_merge_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_alter_with_merge_do_not_work', '1')",0), + ("mt_test_alter_with_merge_work","MergeTree()",1), + ("replicated_mt_test_alter_with_merge_work","ReplicatedMergeTree('/clickhouse/replicated_test_alter_with_merge_work', '1')",1), +]) +def test_alter_with_merge_work(started_cluster, name, engine, positive): + """Copyright 2019, Altinity LTD +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.""" + """Check that TTL expressions are re-evaluated for + existing parts after ALTER command changes TTL expressions + and parts are merged. + """ + try: + node1.query(""" + CREATE TABLE {name} ( + s1 String, + d1 DateTime + ) ENGINE = {engine} + ORDER BY tuple() + TTL d1 + INTERVAL 3000 SECOND TO DISK 'jbod2', + d1 + INTERVAL 6000 SECOND TO VOLUME 'external' + SETTINGS storage_policy='jbods_with_external', merge_with_ttl_timeout=0 + """.format(name=name, engine=engine)) + + + def optimize_table(num): + for i in range(num): + try: # optimize may throw after concurrent alter + node1.query("OPTIMIZE TABLE {} FINAL".format(name), settings={'optimize_throw_if_noop': '1'}) + break + except: + pass + + for p in range(3): + data = [] # 6MB in total + now = time.time() + for i in range(2): + s1 = get_random_string(1024 * 1024) # 1MB + d1 = now - 1 if positive else now + 300 + data.append("('{}', toDateTime({}))".format(s1, d1)) + values = ",".join(data) + node1.query("INSERT INTO {name} (s1, d1) VALUES {values}".format(name=name, values=values)) + + used_disks = get_used_disks_for_table(node1, name) + assert set(used_disks) == {"jbod1", "jbod2"} + + node1.query("SELECT count() FROM {name}".format(name=name)).splitlines() == ["6"] + + node1.query(""" + ALTER TABLE {name} MODIFY + TTL d1 + INTERVAL 0 SECOND TO DISK 'jbod2', + d1 + INTERVAL 5 SECOND TO VOLUME 'external', + d1 + INTERVAL 10 SECOND DELETE + """.format(name=name)) + + optimize_table(20) + + assert node1.query("SELECT count() FROM system.parts WHERE table = '{name}' AND active = 1".format(name=name)) == "1\n" + + time.sleep(5) + + optimize_table(20) + + if positive: + assert check_used_disks_with_retry(node1, name, set(["external"]), 50) + else: + assert check_used_disks_with_retry(node1, name, set(["jbod1", "jbod2"]), 50) + + time.sleep(5) + + optimize_table(20) + + if positive: + assert node1.query("SELECT count() FROM {name}".format(name=name)) == "0\n" + else: + assert node1.query("SELECT count() FROM {name}".format(name=name)) == "6\n" + + finally: + node1.query("DROP TABLE IF EXISTS {name}".format(name=name)) diff --git a/tests/integration/test_ttl_replicated/test.py b/tests/integration/test_ttl_replicated/test.py index 29169ad3c0e..a458db07a23 100644 --- a/tests/integration/test_ttl_replicated/test.py +++ b/tests/integration/test_ttl_replicated/test.py @@ -78,13 +78,13 @@ def test_ttl_many_columns(started_cluster): time.sleep(1) # sleep to allow use ttl merge selector for second time node1.query("OPTIMIZE TABLE test_ttl_2 FINAL", timeout=5) - + node2.query("SYSTEM SYNC REPLICA test_ttl_2", timeout=5) expected = "1\t0\t0\t0\t0\n6\t7\t8\t9\t10\n" assert TSV(node1.query("SELECT id, a, _idx, _offset, _partition FROM test_ttl_2 ORDER BY id")) == TSV(expected) assert TSV(node2.query("SELECT id, a, _idx, _offset, _partition FROM test_ttl_2 ORDER BY id")) == TSV(expected) - + @pytest.mark.parametrize("delete_suffix", [ "", @@ -167,3 +167,67 @@ def test_ttl_double_delete_rule_returns_error(started_cluster): pass except: assert False + +@pytest.mark.parametrize("name,engine", [ + ("test_ttl_alter_delete", "MergeTree()"), + ("test_replicated_ttl_alter_delete", "ReplicatedMergeTree('/clickhouse/test_replicated_ttl_alter_delete', '1')"), +]) +def test_ttl_alter_delete(started_cluster, name, engine): + """Copyright 2019, Altinity LTD +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License.""" + """Check compatibility with old TTL delete expressions to make sure + that: + * alter modify of column's TTL delete expression works + * alter to add new columns works + * alter modify to add TTL delete expression to a a new column works + for a table that has TTL delete expression defined but + no explicit storage policy assigned. + """ + drop_table([node1], name) + + def optimize_with_retry(retry=20): + for i in range(retry): + try: + node1.query("OPTIMIZE TABLE {name} FINAL".format(name=name), settings={"optimize_throw_if_noop": "1"}) + break + except: + time.sleep(0.5) + node1.query( + """ + CREATE TABLE {name} ( + s1 String, + d1 DateTime + ) ENGINE = {engine} + ORDER BY tuple() + TTL d1 + INTERVAL 1 DAY DELETE + """.format(name=name, engine=engine)) + + node1.query("""ALTER TABLE {name} MODIFY COLUMN s1 String TTL d1 + INTERVAL 1 SECOND""".format(name=name)) + node1.query("""ALTER TABLE {name} ADD COLUMN b1 Int32""".format(name=name)) + + node1.query("""INSERT INTO {name} (s1, b1, d1) VALUES ('hello1', 1, toDateTime({time}))""".format(name=name, time=time.time())) + node1.query("""INSERT INTO {name} (s1, b1, d1) VALUES ('hello2', 2, toDateTime({time}))""".format(name=name, time=time.time() + 360)) + + time.sleep(1) + + optimize_with_retry() + r = node1.query("SELECT s1, b1 FROM {name} ORDER BY b1, s1".format(name=name)).splitlines() + assert r == ["\t1", "hello2\t2"] + + node1.query("""ALTER TABLE {name} MODIFY COLUMN b1 Int32 TTL d1""".format(name=name)) + node1.query("""INSERT INTO {name} (s1, b1, d1) VALUES ('hello3', 3, toDateTime({time}))""".format(name=name, time=time.time())) + + time.sleep(1) + + optimize_with_retry() + + r = node1.query("SELECT s1, b1 FROM {name} ORDER BY b1, s1".format(name=name)).splitlines() + assert r == ["\t0", "\t0", "hello2\t2"] diff --git a/tests/performance/agg_functions_min_max_any.xml b/tests/performance/agg_functions_min_max_any.xml index 9c16cb88970..f212ab227b8 100644 --- a/tests/performance/agg_functions_min_max_any.xml +++ b/tests/performance/agg_functions_min_max_any.xml @@ -1,95 +1,92 @@ - + - test.hits + hits_100m_single -select min(Title) from test.hits where Title != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(Title) from test.hits where Title != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(Title) from test.hits where Title != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(Title) from test.hits where Title != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(URL) from test.hits where URL != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(URL) from test.hits where URL != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(URL) from test.hits where URL != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(URL) from test.hits where URL != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(Referer) from test.hits where Referer != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(Referer) from test.hits where Referer != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(Referer) from test.hits where Referer != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(Referer) from test.hits where Referer != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(FlashMinor2) from test.hits where FlashMinor2 != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(FlashMinor2) from test.hits where FlashMinor2 != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(FlashMinor2) from test.hits where FlashMinor2 != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(FlashMinor2) from test.hits where FlashMinor2 != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(MobilePhoneModel) from test.hits where MobilePhoneModel != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(MobilePhoneModel) from test.hits where MobilePhoneModel != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(MobilePhoneModel) from test.hits where MobilePhoneModel != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(MobilePhoneModel) from test.hits where MobilePhoneModel != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(Params) from test.hits where Params != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(Params) from test.hits where Params != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(Params) from test.hits where Params != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(Params) from test.hits where Params != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(SearchPhrase) from test.hits where SearchPhrase != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(SearchPhrase) from test.hits where SearchPhrase != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(SearchPhrase) from test.hits where SearchPhrase != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(SearchPhrase) from test.hits where SearchPhrase != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(PageCharset) from test.hits where PageCharset != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(PageCharset) from test.hits where PageCharset != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(PageCharset) from test.hits where PageCharset != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(PageCharset) from test.hits where PageCharset != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(SocialNetwork) from test.hits where SocialNetwork != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(SocialNetwork) from test.hits where SocialNetwork != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(SocialNetwork) from test.hits where SocialNetwork != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(SocialNetwork) from test.hits where SocialNetwork != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(SocialAction) from test.hits where SocialAction != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(SocialAction) from test.hits where SocialAction != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(SocialAction) from test.hits where SocialAction != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(SocialAction) from test.hits where SocialAction != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(SocialSourcePage) from test.hits where SocialSourcePage != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(SocialSourcePage) from test.hits where SocialSourcePage != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(SocialSourcePage) from test.hits where SocialSourcePage != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(SocialSourcePage) from test.hits where SocialSourcePage != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(ParamOrderID) from test.hits where ParamOrderID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(ParamOrderID) from test.hits where ParamOrderID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(ParamOrderID) from test.hits where ParamOrderID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(ParamOrderID) from test.hits where ParamOrderID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(OpenstatServiceName) from test.hits where OpenstatServiceName != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(OpenstatServiceName) from test.hits where OpenstatServiceName != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(OpenstatServiceName) from test.hits where OpenstatServiceName != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(OpenstatServiceName) from test.hits where OpenstatServiceName != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(OpenstatCampaignID) from test.hits where OpenstatCampaignID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(OpenstatCampaignID) from test.hits where OpenstatCampaignID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(OpenstatCampaignID) from test.hits where OpenstatCampaignID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(OpenstatCampaignID) from test.hits where OpenstatCampaignID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(OpenstatAdID) from test.hits where OpenstatAdID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(OpenstatAdID) from test.hits where OpenstatAdID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(OpenstatAdID) from test.hits where OpenstatAdID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(OpenstatAdID) from test.hits where OpenstatAdID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(OpenstatSourceID) from test.hits where OpenstatSourceID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(OpenstatSourceID) from test.hits where OpenstatSourceID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(OpenstatSourceID) from test.hits where OpenstatSourceID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(OpenstatSourceID) from test.hits where OpenstatSourceID != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(UTMSource) from test.hits where UTMSource != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(UTMSource) from test.hits where UTMSource != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(UTMSource) from test.hits where UTMSource != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(UTMSource) from test.hits where UTMSource != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(UTMMedium) from test.hits where UTMMedium != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(UTMMedium) from test.hits where UTMMedium != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(UTMMedium) from test.hits where UTMMedium != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(UTMMedium) from test.hits where UTMMedium != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(UTMCampaign) from test.hits where UTMCampaign != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(UTMCampaign) from test.hits where UTMCampaign != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(UTMCampaign) from test.hits where UTMCampaign != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(UTMCampaign) from test.hits where UTMCampaign != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(UTMContent) from test.hits where UTMContent != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(UTMContent) from test.hits where UTMContent != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(UTMContent) from test.hits where UTMContent != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(UTMContent) from test.hits where UTMContent != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(UTMTerm) from test.hits where UTMTerm != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(UTMTerm) from test.hits where UTMTerm != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(UTMTerm) from test.hits where UTMTerm != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(UTMTerm) from test.hits where UTMTerm != '' group by intHash32(UserID) % 1000000 FORMAT Null -select min(FromTag) from test.hits where FromTag != '' group by intHash32(UserID) % 1000000 FORMAT Null -select max(FromTag) from test.hits where FromTag != '' group by intHash32(UserID) % 1000000 FORMAT Null -select any(FromTag) from test.hits where FromTag != '' group by intHash32(UserID) % 1000000 FORMAT Null -select anyHeavy(FromTag) from test.hits where FromTag != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(Title) from hits_100m_single where Title != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(Title) from hits_100m_single where Title != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(Title) from hits_100m_single where Title != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(Title) from hits_100m_single where Title != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(URL) from hits_100m_single where URL != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(URL) from hits_100m_single where URL != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(URL) from hits_100m_single where URL != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(URL) from hits_100m_single where URL != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(Referer) from hits_100m_single where Referer != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(Referer) from hits_100m_single where Referer != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(Referer) from hits_100m_single where Referer != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(Referer) from hits_100m_single where Referer != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(FlashMinor2) from hits_100m_single where FlashMinor2 != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(FlashMinor2) from hits_100m_single where FlashMinor2 != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(FlashMinor2) from hits_100m_single where FlashMinor2 != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(FlashMinor2) from hits_100m_single where FlashMinor2 != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(MobilePhoneModel) from hits_100m_single where MobilePhoneModel != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(MobilePhoneModel) from hits_100m_single where MobilePhoneModel != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(MobilePhoneModel) from hits_100m_single where MobilePhoneModel != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(MobilePhoneModel) from hits_100m_single where MobilePhoneModel != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(Params) from hits_100m_single where Params != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(Params) from hits_100m_single where Params != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(Params) from hits_100m_single where Params != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(Params) from hits_100m_single where Params != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(SearchPhrase) from hits_100m_single where SearchPhrase != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(SearchPhrase) from hits_100m_single where SearchPhrase != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(SearchPhrase) from hits_100m_single where SearchPhrase != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(SearchPhrase) from hits_100m_single where SearchPhrase != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(PageCharset) from hits_100m_single where PageCharset != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(PageCharset) from hits_100m_single where PageCharset != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(PageCharset) from hits_100m_single where PageCharset != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(PageCharset) from hits_100m_single where PageCharset != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(SocialNetwork) from hits_100m_single where SocialNetwork != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(SocialNetwork) from hits_100m_single where SocialNetwork != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(SocialNetwork) from hits_100m_single where SocialNetwork != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(SocialNetwork) from hits_100m_single where SocialNetwork != '' group by intHash32(UserID) % 1000000 FORMAT Null + +select min(SocialSourcePage) from hits_100m_single where SocialSourcePage != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(SocialSourcePage) from hits_100m_single where SocialSourcePage != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(SocialSourcePage) from hits_100m_single where SocialSourcePage != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(SocialSourcePage) from hits_100m_single where SocialSourcePage != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(ParamOrderID) from hits_100m_single where ParamOrderID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(ParamOrderID) from hits_100m_single where ParamOrderID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(ParamOrderID) from hits_100m_single where ParamOrderID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(ParamOrderID) from hits_100m_single where ParamOrderID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(OpenstatServiceName) from hits_100m_single where OpenstatServiceName != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(OpenstatServiceName) from hits_100m_single where OpenstatServiceName != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(OpenstatServiceName) from hits_100m_single where OpenstatServiceName != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(OpenstatServiceName) from hits_100m_single where OpenstatServiceName != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(OpenstatCampaignID) from hits_100m_single where OpenstatCampaignID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(OpenstatCampaignID) from hits_100m_single where OpenstatCampaignID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(OpenstatCampaignID) from hits_100m_single where OpenstatCampaignID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(OpenstatCampaignID) from hits_100m_single where OpenstatCampaignID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(OpenstatAdID) from hits_100m_single where OpenstatAdID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(OpenstatAdID) from hits_100m_single where OpenstatAdID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(OpenstatAdID) from hits_100m_single where OpenstatAdID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(OpenstatAdID) from hits_100m_single where OpenstatAdID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(OpenstatSourceID) from hits_100m_single where OpenstatSourceID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(OpenstatSourceID) from hits_100m_single where OpenstatSourceID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(OpenstatSourceID) from hits_100m_single where OpenstatSourceID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(OpenstatSourceID) from hits_100m_single where OpenstatSourceID != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(UTMSource) from hits_100m_single where UTMSource != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(UTMSource) from hits_100m_single where UTMSource != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(UTMSource) from hits_100m_single where UTMSource != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(UTMSource) from hits_100m_single where UTMSource != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(UTMMedium) from hits_100m_single where UTMMedium != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(UTMMedium) from hits_100m_single where UTMMedium != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(UTMMedium) from hits_100m_single where UTMMedium != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(UTMMedium) from hits_100m_single where UTMMedium != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(UTMCampaign) from hits_100m_single where UTMCampaign != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(UTMCampaign) from hits_100m_single where UTMCampaign != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(UTMCampaign) from hits_100m_single where UTMCampaign != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(UTMCampaign) from hits_100m_single where UTMCampaign != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(UTMContent) from hits_100m_single where UTMContent != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(UTMContent) from hits_100m_single where UTMContent != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(UTMContent) from hits_100m_single where UTMContent != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(UTMContent) from hits_100m_single where UTMContent != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(UTMTerm) from hits_100m_single where UTMTerm != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(UTMTerm) from hits_100m_single where UTMTerm != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(UTMTerm) from hits_100m_single where UTMTerm != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(UTMTerm) from hits_100m_single where UTMTerm != '' group by intHash32(UserID) % 1000000 FORMAT Null +select min(FromTag) from hits_100m_single where FromTag != '' group by intHash32(UserID) % 1000000 FORMAT Null +select max(FromTag) from hits_100m_single where FromTag != '' group by intHash32(UserID) % 1000000 FORMAT Null +select any(FromTag) from hits_100m_single where FromTag != '' group by intHash32(UserID) % 1000000 FORMAT Null +select anyHeavy(FromTag) from hits_100m_single where FromTag != '' group by intHash32(UserID) % 1000000 FORMAT Null diff --git a/tests/performance/aggregating_merge_tree.xml b/tests/performance/aggregating_merge_tree.xml new file mode 100644 index 00000000000..d658fd705bb --- /dev/null +++ b/tests/performance/aggregating_merge_tree.xml @@ -0,0 +1,30 @@ + + + CREATE TABLE test( + t UInt64, + q1 AggregateFunction(quantilesTiming(0.50, 0.75, 0.90, 0.99), Float64), + q2 AggregateFunction(quantilesTiming(0.50, 0.75, 0.90, 0.99), Float64), + q3 AggregateFunction(quantilesTiming(0.50, 0.75, 0.90, 0.99), Float64), + q4 AggregateFunction(quantilesTiming(0.50, 0.75, 0.90, 0.99), Float64), + q5 AggregateFunction(quantilesTiming(0.50, 0.75, 0.90, 0.99), Float64) + ) ENGINE=SummingMergeTree() + ORDER BY t + + + + INSERT INTO test + SELECT + number / 10 as t, + quantilesTimingState(0.50, 0.75, 0.90, 0.99)(number/1000) as q1, + quantilesTimingState(0.50, 0.75, 0.90, 0.99)(number/1000) as q2, + quantilesTimingState(0.50, 0.75, 0.90, 0.99)(number/1000) as q3, + quantilesTimingState(0.50, 0.75, 0.90, 0.99)(number/1000) as q4, + quantilesTimingState(0.50, 0.75, 0.90, 0.99)(number/1000) as q5 + FROM numbers(1000 * 1000) + GROUP BY t + + + OPTIMIZE TABLE test FINAL + + DROP TABLE test + diff --git a/tests/performance/aggregation_in_order.xml b/tests/performance/aggregation_in_order.xml new file mode 100644 index 00000000000..6e58865dab4 --- /dev/null +++ b/tests/performance/aggregation_in_order.xml @@ -0,0 +1,23 @@ + + + hits_10m_single + hits_100m_single + + + 1 + + + + table + + hits_10m_single + hits_100m_single + + + + + SELECT avg(length(URL)) as x from hits_100m_single GROUP BY CounterID FORMAT Null + SELECT avg(length(URL)) as x from {table} GROUP BY CounterID, EventDate FORMAT Null + SELECT avg(length(URL)) as x from hits_10m_single GROUP BY CounterID, EventDate, intHash32(UserID) FORMAT Null + + diff --git a/tests/performance/arithmetic_operations_in_aggr_func.xml b/tests/performance/arithmetic_operations_in_aggr_func.xml new file mode 100644 index 00000000000..c91033bf9e4 --- /dev/null +++ b/tests/performance/arithmetic_operations_in_aggr_func.xml @@ -0,0 +1,14 @@ + + SELECT max(-1 * (((-2 * (number * -3)) * -4) * -5)) FROM numbers(500000000) + + SELECT min(-1 * (((-2 * (number * -3)) * -4) * -5)) FROM numbers(500000000) + + SELECT sum(-1 * (((-2 * (number * -3)) * -4) * -5)) FROM numbers(500000000) + + SELECT min(-1 + (((-2 + (number + -3)) + -4) + -5)) FROM numbers(500000000) + + SELECT max(-1 + (((-2 + (number + -3)) + -4) + -5)) FROM numbers(500000000) + + SELECT max(((((number) * 10) * -2) * 3) * 2) + min(((((number) * 10) * -2) * 3) * 2) FROM numbers(500000000) + + diff --git a/tests/performance/array_element.xml b/tests/performance/array_element.xml index 456ce55cbe1..1f82b833380 100644 --- a/tests/performance/array_element.xml +++ b/tests/performance/array_element.xml @@ -1,8 +1,5 @@ - - - - SELECT count() FROM numbers(10000000) WHERE NOT ignore([[1], [2]][number % 2 + 2]) - SELECT count() FROM numbers(10000000) WHERE NOT ignore([[], [2]][number % 2 + 2]) - SELECT count() FROM numbers(10000000) WHERE NOT ignore([[], []][number % 2 + 2]) + SELECT count() FROM numbers(100000000) WHERE NOT ignore([[1], [2]][number % 2 + 2]) + SELECT count() FROM numbers(100000000) WHERE NOT ignore([[], [2]][number % 2 + 2]) + SELECT count() FROM numbers(100000000) WHERE NOT ignore([[], []][number % 2 + 2]) diff --git a/tests/performance/base64_hits.xml b/tests/performance/base64_hits.xml index b07212bd598..7e7ffc6bd84 100644 --- a/tests/performance/base64_hits.xml +++ b/tests/performance/base64_hits.xml @@ -1,10 +1,11 @@ - - hits_100m_single + hits_10m_single - + + 1 + @@ -18,7 +19,7 @@ - SELECT count() FROM hits_100m_single WHERE NOT ignore(base64Encode({string})) - SELECT count() FROM hits_100m_single WHERE base64Decode(base64Encode({string})) != {string} - SELECT count() FROM hits_100m_single WHERE tryBase64Decode(base64Encode({string})) != {string} + SELECT count() FROM hits_10m_single WHERE NOT ignore(base64Encode({string})) + SELECT count() FROM hits_10m_single WHERE base64Decode(base64Encode({string})) != {string} + SELECT count() FROM hits_10m_single WHERE tryBase64Decode(base64Encode({string})) != {string} diff --git a/tests/performance/basename.xml b/tests/performance/basename.xml index 7d040da5998..daa0e9605b3 100644 --- a/tests/performance/basename.xml +++ b/tests/performance/basename.xml @@ -1,12 +1,12 @@ - - - - test.hits + hits_10m_single -SELECT count() FROM test.hits WHERE NOT ignore(basename(URL)) -SELECT count() FROM test.hits WHERE NOT ignore(basename(Referer)) + + 1 + + SELECT count() FROM hits_10m_single WHERE NOT ignore(basename(URL)) + SELECT count() FROM hits_10m_single WHERE NOT ignore(basename(Referer)) diff --git a/tests/performance/bit_operations_fixed_string.xml b/tests/performance/bit_operations_fixed_string.xml index c08761ba8fc..56b6ff804f7 100644 --- a/tests/performance/bit_operations_fixed_string.xml +++ b/tests/performance/bit_operations_fixed_string.xml @@ -1,11 +1,13 @@ - - - test.hits + + 1 + + + SELECT count() FROM test.hits WHERE NOT ignore(bitAnd(toFixedString(ClientIP6, 16), IPv6StringToNum('ffff:ffff:ffff:0000:0000:0000:0000:0000'))) SELECT count() FROM test.hits WHERE NOT ignore(bitOr(toFixedString(ClientIP6, 16), IPv6StringToNum('ffff:ffff:ffff:0000:0000:0000:0000:0000'))) SELECT count() FROM test.hits WHERE NOT ignore(bitXor(toFixedString(ClientIP6, 16), IPv6StringToNum('ffff:ffff:ffff:0000:0000:0000:0000:0000'))) diff --git a/tests/performance/bit_operations_fixed_string_numbers.xml b/tests/performance/bit_operations_fixed_string_numbers.xml index e10e665ac81..5d3d9aa804d 100644 --- a/tests/performance/bit_operations_fixed_string_numbers.xml +++ b/tests/performance/bit_operations_fixed_string_numbers.xml @@ -1,6 +1,4 @@ - - - SELECT count() FROM numbers(10000000) WHERE NOT ignore(bitXor(reinterpretAsFixedString(number), reinterpretAsFixedString(number + 1))) - SELECT count() FROM numbers(10000000) WHERE NOT ignore(bitXor(reinterpretAsFixedString(number), reinterpretAsFixedString(0xabcd0123cdef4567))) + SELECT count() FROM numbers(100000000) WHERE NOT ignore(bitXor(reinterpretAsFixedString(number), reinterpretAsFixedString(number + 1))) + SELECT count() FROM numbers(100000000) WHERE NOT ignore(bitXor(reinterpretAsFixedString(number), reinterpretAsFixedString(0xabcd0123cdef4567))) diff --git a/tests/performance/codecs_int_insert.xml b/tests/performance/codecs_int_insert.xml index 0ad04f00f78..662df80ae70 100644 --- a/tests/performance/codecs_int_insert.xml +++ b/tests/performance/codecs_int_insert.xml @@ -33,7 +33,7 @@ num_rows - 10000000 + 20000000 diff --git a/tests/performance/cpu_synthetic.xml b/tests/performance/cpu_synthetic.xml index 1076e051207..2888f7bbbd6 100644 --- a/tests/performance/cpu_synthetic.xml +++ b/tests/performance/cpu_synthetic.xml @@ -1,7 +1,4 @@ - - - - + hits_100m_single hits_10m_single diff --git a/tests/performance/extract.xml b/tests/performance/extract.xml index 71dd8ce775d..b370152c7b2 100644 --- a/tests/performance/extract.xml +++ b/tests/performance/extract.xml @@ -6,5 +6,6 @@ SELECT count() FROM test.hits WHERE NOT ignore(extract(URL, '(\\w+=\\w+)')) SELECT count() FROM test.hits WHERE NOT ignore(extractAll(URL, '(\\w+=\\w+)')) SELECT count() FROM test.hits WHERE NOT ignore(extractGroups(URL, '(\\w+)=(\\w+)')) - SELECT count() FROM test.hits WHERE NOT ignore(extractAllGroups(URL, '(\\w+)=(\\w+)')) + SELECT count() FROM test.hits WHERE NOT ignore(extractAllGroupsVertical(URL, '(\\w+)=(\\w+)')) + SELECT count() FROM test.hits WHERE NOT ignore(extractAllGroupsHorizontal(URL, '(\\w+)=(\\w+)')) diff --git a/tests/performance/fuzz_bits.xml b/tests/performance/fuzz_bits.xml new file mode 100644 index 00000000000..2679977cb1d --- /dev/null +++ b/tests/performance/fuzz_bits.xml @@ -0,0 +1,14 @@ + + + + + SELECT count() FROM zeros(1000000) WHERE NOT ignore(fuzzBits(randomString(10), 0.1)) + SELECT count() FROM zeros(100000) WHERE NOT ignore(fuzzBits(randomString(100), 0.5)) + SELECT count() FROM zeros(10000) WHERE NOT ignore(fuzzBits(randomFixedString(1000), 0.1)) + SELECT count() FROM zeros(1000) WHERE NOT ignore(fuzzBits(randomFixedString(10000), 0.5)) + + SELECT count() FROM zeros(1000000) WHERE NOT ignore(fuzzBits(randomString(rand() % 10), 0.7)) + SELECT count() FROM zeros(100000) WHERE NOT ignore(fuzzBits(randomString(rand() % 100), 0.7)) + SELECT count() FROM zeros(10000) WHERE NOT ignore(fuzzBits(randomString(rand() % 1000), 0.7)) + + diff --git a/tests/performance/generate_table_function.xml b/tests/performance/generate_table_function.xml index 74d4ad4d014..f09753c963a 100644 --- a/tests/performance/generate_table_function.xml +++ b/tests/performance/generate_table_function.xml @@ -3,7 +3,7 @@ SELECT sum(NOT ignore(*)) FROM (SELECT * FROM generateRandom('ui64 UInt64, i64 Int64, ui32 UInt32, i32 Int32, ui16 UInt16, i16 Int16, ui8 UInt8, i8 Int8', 0, 10, 10) LIMIT 10000000); SELECT sum(NOT ignore(*)) FROM (SELECT * FROM generateRandom('i Enum8(\'hello\' = 1, \'world\' = 5)', 0, 10, 10) LIMIT 10000000); SELECT sum(NOT ignore(*)) FROM (SELECT * FROM generateRandom('i Array(Nullable(Enum8(\'hello\' = 1, \'world\' = 5)))', 0, 10, 10) LIMIT 10000000); - SELECT sum(NOT ignore(*)) FROM (SELECT * FROM generateRandom('i Nullable(Enum16(\'h\' = 1, \'w\' = 5 , \'o\' = -200)))', 0, 10, 10) LIMIT 10000000); + SELECT sum(NOT ignore(*)) FROM (SELECT * FROM generateRandom('i Nullable(Enum16(\'h\' = 1, \'w\' = 5 , \'o\' = -200))', 0, 10, 10) LIMIT 10000000); SELECT sum(NOT ignore(*)) FROM (SELECT * FROM generateRandom('d Date, dt DateTime, dtm DateTime(\'Europe/Moscow\')', 0, 10, 10) LIMIT 10000000); SELECT sum(NOT ignore(*)) FROM (SELECT * FROM generateRandom('dt64 DateTime64, dts64 DateTime64(6), dtms64 DateTime64(6 ,\'Europe/Moscow\')', 0, 10, 10) LIMIT 10000000); SELECT sum(NOT ignore(*)) FROM (SELECT * FROM generateRandom('f32 Float32, f64 Float64', 0, 10, 10) LIMIT 10000000); diff --git a/tests/performance/int_parsing.xml b/tests/performance/int_parsing.xml index a9258875b5e..3008ed06684 100644 --- a/tests/performance/int_parsing.xml +++ b/tests/performance/int_parsing.xml @@ -1,92 +1,76 @@ - - - - test.hits + hits_100m_single - SELECT count() FROM test.hits WHERE NOT ignore(toString(WatchID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(JavaEnable)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(GoodEvent)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(CounterID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ClientIP)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(RegionID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(UserID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(CounterClass)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(OS)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(UserAgent)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(Refresh)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IsRobot)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ResolutionWidth)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ResolutionHeight)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ResolutionDepth)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(FlashMajor)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(FlashMinor)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(NetMajor)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(NetMinor)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(UserAgentMajor)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(CookieEnable)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(JavascriptEnable)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IsMobile)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(MobilePhone)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IPNetworkID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(TraficSourceID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(SearchEngineID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(SearchPhrase)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(AdvEngineID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IsArtifical)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(WindowClientWidth)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(WindowClientHeight)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ClientTimeZone)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(SilverlightVersion1)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(SilverlightVersion2)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(SilverlightVersion3)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(SilverlightVersion4)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(CodeVersion)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IsLink)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IsDownload)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IsNotBounce)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(FUniqID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(HID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IsOldCounter)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IsEvent)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(IsParameter)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(DontCountHits)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(WithHash)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(Age)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(Sex)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(Income)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(Interests)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(Robotness)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(RemoteIP)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(WindowName)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(OpenerName)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(HistoryLength)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(HTTPError)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(SendTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(DNSTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ConnectTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ResponseStartTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ResponseEndTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(FetchTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(RedirectTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(DOMInteractiveTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(DOMContentLoadedTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(DOMCompleteTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(LoadEventStartTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(LoadEventEndTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(NSToDOMContentLoadedTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(FirstPaintTiming)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(RedirectCount)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(SocialSourceNetworkID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ParamPrice)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(ParamCurrencyID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(HasGCLID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(RefererHash)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(URLHash)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(CLID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(YCLID)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(RequestNum)) SETTINGS max_threads = 1 - SELECT count() FROM test.hits WHERE NOT ignore(toString(RequestTry)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(WatchID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(JavaEnable)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(GoodEvent)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(CounterID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ClientIP)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(RegionID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(UserID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(CounterClass)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(OS)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(UserAgent)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(Refresh)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ResolutionWidth)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ResolutionHeight)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ResolutionDepth)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(FlashMajor)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(FlashMinor)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(NetMajor)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(NetMinor)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(UserAgentMajor)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(CookieEnable)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(JavascriptEnable)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(IsMobile)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(MobilePhone)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(IPNetworkID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(TraficSourceID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(SearchEngineID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(SearchPhrase)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(AdvEngineID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(IsArtifical)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(WindowClientWidth)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(WindowClientHeight)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ClientTimeZone)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(SilverlightVersion1)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(SilverlightVersion2)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(SilverlightVersion3)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(SilverlightVersion4)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(CodeVersion)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(IsLink)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(IsDownload)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(IsNotBounce)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(FUniqID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(HID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(IsOldCounter)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(IsEvent)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(IsParameter)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(DontCountHits)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(WithHash)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(Age)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(Sex)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(Income)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(Interests)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(Robotness)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(RemoteIP)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(WindowName)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(OpenerName)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(HistoryLength)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(HTTPError)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(SendTiming)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(DNSTiming)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ConnectTiming)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ResponseStartTiming)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ResponseEndTiming)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(FetchTiming)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(SocialSourceNetworkID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ParamPrice)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(ParamCurrencyID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(HasGCLID)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(RefererHash)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(URLHash)) SETTINGS max_threads = 1 + SELECT count() FROM hits_100m_single WHERE NOT ignore(toString(CLID)) SETTINGS max_threads = 1 diff --git a/tests/performance/number_formatting_formats.xml b/tests/performance/number_formatting_formats.xml index e6b5a8344dc..c4a17b1f133 100644 --- a/tests/performance/number_formatting_formats.xml +++ b/tests/performance/number_formatting_formats.xml @@ -1,6 +1,4 @@ - CREATE TABLE IF NOT EXISTS table_{format} (x UInt64) ENGINE = File(`{format}`) - format @@ -13,22 +11,30 @@ JSONEachRow TSKV RowBinary - Native XML Parquet ODBCDriver2 - Null MySQLWire - - test.hits - + + + format_fast + + Native + Null + + + - INSERT INTO table_{format} SELECT number FROM numbers(10000000) + CREATE TABLE IF NOT EXISTS table_{format} (x UInt64) ENGINE = File(`{format}`) + CREATE TABLE IF NOT EXISTS table_{format_fast} (x UInt64) ENGINE = File(`{format}`) + + INSERT INTO table_{format} SELECT number FROM numbers(10000000) + INSERT INTO table_{format_fast} SELECT number FROM numbers(20000000) DROP TABLE IF EXISTS table_{format} - + DROP TABLE IF EXISTS table_{format_fast} diff --git a/tests/performance/parallel_mv.xml b/tests/performance/parallel_mv.xml new file mode 100644 index 00000000000..ef50d506708 --- /dev/null +++ b/tests/performance/parallel_mv.xml @@ -0,0 +1,24 @@ + + + 1 + + + create table main_table (number UInt64) engine = MergeTree order by tuple(); + create materialized view mv_1 engine = MergeTree order by tuple() as + select number, toString(number) from main_table where number % 13 != 0; + create materialized view mv_2 engine = MergeTree order by tuple() as + select number, toString(number) from main_table where number % 13 != 1; + create materialized view mv_3 engine = MergeTree order by tuple() as + select number, toString(number) from main_table where number % 13 != 3; + create materialized view mv_4 engine = MergeTree order by tuple() as + select number, toString(number) from main_table where number % 13 != 4; + + + insert into main_table select number from numbers(1000000) + + drop table if exists main_table; + drop table if exists mv_1; + drop table if exists mv_2; + drop table if exists mv_3; + drop table if exists mv_4; + diff --git a/tests/performance/point_in_polygon.xml b/tests/performance/point_in_polygon.xml index b15fecbbfb0..a1ef4891577 100644 --- a/tests/performance/point_in_polygon.xml +++ b/tests/performance/point_in_polygon.xml @@ -4,10 +4,10 @@ INSERT INTO polygons WITH number + 1 AS radius SELECT [arrayMap(x -> (cos(x / 90. * pi()) * radius, sin(x / 90. * pi()) * radius), range(180))] - FROM numbers(100000) + FROM numbers(1000000) - SELECT pointInPolygon((100, 100), polygon) FROM polygons + SELECT pointInPolygon((100, 100), polygon) FROM polygons FORMAT Null DROP TABLE IF EXISTS polygons diff --git a/tests/performance/set_hits.xml b/tests/performance/set_hits.xml index 8b9ae1da83b..f788bb43196 100644 --- a/tests/performance/set_hits.xml +++ b/tests/performance/set_hits.xml @@ -1,16 +1,16 @@ - + hits_10m_single hits_100m_single - - - SELECT count() FROM hits_100m_single WHERE UserID IN (SELECT UserID FROM hits_100m_single WHERE AdvEngineID != 0) + + SELECT count() FROM hits_100m_single WHERE UserID IN (SELECT UserID FROM hits_100m_single WHERE AdvEngineID != 0) SETTINGS max_threads = 1 SELECT count() FROM hits_10m_single WHERE UserID IN (SELECT UserID FROM hits_10m_single) SELECT count() FROM hits_10m_single WHERE SearchPhrase IN (SELECT SearchPhrase FROM hits_10m_single) - SELECT count() FROM hits_10m_single WHERE URL IN (SELECT URL FROM hits_10m_single WHERE AdvEngineID != 0) + SELECT count() FROM hits_100m_single WHERE URL IN (SELECT URL FROM hits_100m_single WHERE AdvEngineID != 0) SELECT count() FROM hits_10m_single WHERE URL IN (SELECT URL FROM hits_10m_single WHERE SearchEngineID != 0) - SELECT count() FROM hits_10m_single WHERE RegionID IN (SELECT RegionID FROM hits_10m_single) + SELECT count() FROM hits_100m_single WHERE RegionID IN (SELECT RegionID FROM hits_100m_single) diff --git a/tests/performance/set_index.xml b/tests/performance/set_index.xml index d9b263159b5..88dbf457cba 100644 --- a/tests/performance/set_index.xml +++ b/tests/performance/set_index.xml @@ -1,9 +1,7 @@ - CREATE TABLE test_in (`a` UInt32) ENGINE = MergeTree() ORDER BY a INSERT INTO test_in SELECT number FROM numbers(500000000) - SELECT count() FROM test_in WHERE a IN (SELECT rand(1) FROM numbers(100000)) SETTINGS max_rows_to_read = 1, read_overflow_mode = 'break' @@ -14,8 +12,13 @@ SELECT count() FROM test_in WHERE -toInt64(a) NOT IN (SELECT toInt64(rand(1)) FROM numbers(100000)) settings max_rows_to_read=1, read_overflow_mode='break' - - SELECT count() FROM numbers(1000) WHERE toString(number) IN ('41577', '83972', '51697', '50014', '37553', '93459', '87438', '95971', '83186', '74326', '67871', '50406', '83678', '29655', '18580', '83905', '61518', '29059', '56700', '82787', '98672', '30884', '81822', '39850', '80852', '57627', '91346', '64522', '17781', '49467', '41099', '41929', '85618', '91389', '68564', '91769', '81219', '52218', '37220', '97097', '2129', '9886', '52049', '34847', '25364', '36429', '76897', '71868', '58121', '71199', '84819', '69991', '34046', '64507', '34892', '24228', '36986', '28588', '51159', '53444', '80531', '9941', '20256', '48103', '32565', '62890', '5379', '60302', '46434', '3205', '18821', '31030', '19794', '71557', '71703', '15024', '14004', '82164', '95659', '40227', '83358', '24395', '9610', '19814', '48491', '66412', '16012', '71586', '42143', '51103', '24463', '89949', '35694', '39193', '63904', '40489', '77144', '94014', '84836', '9980', '46554', '43905', '25588', '25205', '72624', '10249', '35888', '98478', '99030', '26834', '31', '81499', '14847', '82997', '92357', '92893', '17426', '56630', '22252', '68119', '62710', '8740', '82144', '79916', '23391', '30192', '99271', '96435', '44237', '98327', '69481', '16691', '13643', '84554', '38571', '70926', '99283', '79000', '20926', '86495', '4834', '1222', '39486', '57697', '58002', '40790', '15623', '3999', '31515', '12694', '26143', '35951', '54085', '97534', '35329', '73535', '88715', '29572', '75799', '45166', '32066', '48023', '69523', '93150', '8740', '96790', '15534', '63252', '5142', '67045', '93992', '16663', '292', '63924', '6588', '12190', '31506', '69590', '35394', '55168', '65223', '79183', '32600', '69676', '28316', '72111', '53531', '15073', '41127', '73451', '24725', '61647', '65315', '41143', '26493', '95608', '34407', '76098', '53105', '83691', '48755', '35696', '62587', '81826', '3963', '45766', '82751', '12430', '97685', '29919', '78155', '71636', '50215', '89734', '9892', '47151', '54855', '3428', '9712', '52592', '2403', '79602', '81243', '79859', '57361', '82000', '42107', '28860', '99591', '28296', '57337', '64969', '32332', '25535', '30924', '21313', '32554', '17342', '87311', '19825', '24898', '61323', '83209', '79322', '79009', '50746', '33396', '62033', '16548', '17427', '24073', '34640', '52368', '4724', '80408', '40', '33787', '16666', '19665', '86751', '27264', '2241', '88134', '53566', '10589', '79711', '92823', '58972', '91767', '60885', '51659', '7867', '96849', '30360', '20914', '9584', '1250', '22871', '23282', '99312', '4683', '33429', '68361', '82614', '81440', '47863', '69790', '11968', '75210', '66854', '37002', '61142', '71514', '1588', '42336', '11069', '26291', '2261', '71056', '13492', '9133', '91216', '72207', '71586', '86535', '83898', '24392', '45384', '48545', '61972', '503', '80180', '35834', '97025', '70411', '55039', '35430', '27631', '82533', '96831', '74077', '42533', '14451', '26943', '53783', '69489', '71969', '8432', '37230', '61348', '19472', '59115', '9886', '50951', '57109', '7141', '1902', '84130', '4323', '55889', '47784', '2220', '75988', '66988', '63721', '8131', '95601', '95207', '2311', '26541', '50991', '6717', '2969', '71857', '51034', '65958', '94716', '90275', '21012', '46859', '7984', '31131', '46457', '69578', '44540', '7294', '80117', '9925', '60155', '90608', '82684', '32193', '87071', '28006', '87604', '24501', '79087', '2848', '29237', '11221', '81319', '40966', '87641', '35325', '78705', '88636', '78717', '62831', '56390', '99271', '43821', '14453', '17923', '62695', '77322', '21038', '67677', '41271', '4376', '65426', '46091', '19887', '97251', '55583', '58763', '3826', '35037', '73533', '64267', '82319', '9836', '42622', '96829', '16363', '10455', '49290', '99992', '98229', '66356', '59087', '73998', '25986', '4279', '56790', '69540', '588', '36620', '60358', '45056', '89297', '42740', '8323', '19245', '82417', '41431', '699', '11554', '73910', '44491', '56019', '68901', '45816', '68126', '89379', '23885', '13263', '56395', '73130', '19089', '23771', '10335', '48547', '16903', '6453', '33560', '89668', '38159', '43177', '90655', '49712', '62', '66920', '34180', '12150', '48564', '39538', '85026', '87195', '14928', '8956', '71157', '53287', '39161', '67583', '83309', '92054', '86977', '56188', '15229', '88170', '60894', '58497', '89254', '40082', '86890', '60161', '97291', '45878', '23368', '14577', '92870', '37017', '97356', '99426', '76061', '89186', '99751', '85153', '61580', '39360', '90107', '25603', '26798', '76224', '6469', '7912', '69838', '16404', '67497', '28965', '80836', '80365', '91249', '48713', '17113', '33090', '40793', '70450', '66689', '83698', '17802', '43869', '13355', '18959', '79411', '87930', '9265', '37504', '44876', '97234', '94149', '35040', '22049', '49248', '6535', '36080', '28346', '94437', '78319', '17961', '89056', '56161', '35810', '41632', '45494', '53351', '89729', '99510', '51584', '59688', '6193', '70809', '51093', '92589', '90247', '34910', '78235', '17362', '49423', '63324', '525', '37638', '72325', '89356', '15298', '59116', '17848', '65429', '27029', '84781', '70247', '8825', '35082', '70451', '22522', '58125', '91879', '90531', '2478', '463', '37902', '54405', '87267', '72688', '22803', '33134', '35177', '84551', '44974', '88375', '76407', '27774', '33849', '19915', '82014', '80434', '26380', '48777', '53811', '14838', '26829', '56441', '99869', '49574', '85476', '19723', '16907', '4018', '37338', '78510', '47912', '13030', '65277', '95716', '67363', '21393', '89887', '78842', '81650', '903', '17436', '30704', '49223', '27198', '25500', '52214', '54258', '70082', '53950', '49312', '43615', '99473', '94348', '53661', '96213', '96346', '62010', '38268', '32861', '75660', '10392', '89491', '68335', '29817', '88706', '24184', '36298', '43440', '21626', '26535', '44560', '46363', '12534', '99070', '95606', '33714', '73070', '8303', '29853', '23014', '99982', '4530', '14955', '45803', '50', '90750', '30394', '81276', '95563', '47314', '58520', '91299', '88944', '54402', '67405', '29253', '47079', '71734', '99728', '17652', '13307', '35556', '18962', '26780', '17771', '53712', '60055', '37628', '35830', '90739', '61151', '41309', '27652', '3051', '53167', '98417', '19382', '36833', '75085', '65374', '87732', '30352', '31776', '32765', '97565', '92199', '49050', '29503', '51024', '18834', '8515', '24069', '96216', '10777', '90680', '18974', '68884', '85305', '36007', '56707', '4212', '47352', '34426', '13185', '92939', '95782', '70577', '58080', '98279', '3906', '5065', '56896', '16382', '31273', '17117', '98602', '12786', '24086', '63970', '72756', '35798', '82367', '7356', '53398', '68503', '2962', '16425', '67334', '68461', '65439', '15620', '70906', '29649', '46461', '74602', '38012', '71714', '16825', '89480', '53386', '88532', '35104', '28556', '82120', '23155', '23347', '24797', '60061', '54962', '99427', '82248', '82447', '39968', '63727', '27431', '81511', '91168', '71425', '80740', '84127', '40717', '15503', '15419', '46594', '61263', '19212', '53175', '70724', '74445', '23034', '71818', '40246', '18886', '53066', '4880', '83701', '86107', '87862', '44751', '392', '73440', '90291', '93395', '20894', '38463', '32664', '55158', '20090', '50004', '79070', '98471', '85478', '96615', '68149', '78334', '97752', '73207', '71678', '91238', '96757', '82598', '194', '35797', '45120', '60782', '28721', '17676', '78066', '60957', '11826', '51563', '50516', '16485', '47053', '31738', '48923', '23554', '96850', '42033', '73701', '78607', '45979', '54571', '12415', '31693', '15356', '36902', '9126', '3767', '3295', '90402', '24005', '95350', '67033', '49137', '72606', '51899', '17522', '31957', '44641', '53982', '23767', '68257', '15766', '19995', '2107', '48788', '11765', '91055', '46576', '54651', '50381', '62827', '73636', '46606', '98753', '37631', '70441', '87916', '66983', '33870', '31125', '12904', '57040', '4874', '58632', '42037', '18782', '5998', '18974', '57949', '81010', '90407', '99874', '20462', '89949', '10952', '71454', '95130', '46115', '3518', '13384', '69039', '79482', '22076', '59782', '32042', '40930', '60243', '29298', '6790', '46985', '44398', '85631', '14380', '66179', '2629', '32126', '49833', '14118', '58492', '31493', '81172', '96638', '8745', '89663', '76842', '78633', '41373', '83721', '42886', '11123', '32739', '11051', '1303', '92314', '83324', '85600', '44276', '69064', '56125', '84650', '31028', '12628', '14502', '64764', '39405', '44855', '79046', '51716', '46824', '83389', '1941', '1257', '9280', '73176', '84729', '2579', '63366', '22606', '35541', '51096', '13447', '18355', '68037', '28436', '94116', '81070', '78355', '67897', '5296', '32742', '77645', '91853', '18767', '67949', '40963', '5792', '17278', '25597', '41884', '80829', '7099', '18645', '60295', '12082', '81800', '78415', '18082', '38789', '16295', '72377', '74949', '55583', '66853', '15402', '72977', '15123', '99434', '34999', '21687', '76049', '42987', '83748', '88256', '66688', '21766', '20304', '29271', '10069', '19822', '11792', '42526', '74143', '17289', '30253', '6367', '20888', '12975', '94073', '98639', '30134', '26320', '65507', '69002', '53120', '4550', '38893', '18954', '38283', '54863', '17698', '99670', '10521', '92467', '60994', '18052', '48673', '35811', '87282', '62706', '16061', '53112', '22652', '37780', '55662', '26331', '49410', '79074', '10623', '69577', '79613', '9491', '31229', '43922', '84231', '58409', '36386', '46875', '74431', '76735', '38776', '23350', '7314', '9079', '51519', '98544', '70216', '63380', '90381', '1295', '46901', '58225', '55339', '89918', '75522', '35431', '89460', '49552', '89302', '23068', '28493', '3042', '25194', '59520', '9810', '95706', '81297', '89638', '54794', '94527', '45262', '97932', '78685', '6947', '22818', '48700', '9153', '12289', '22011', '58825', '93854', '65438', '4509', '33741', '28208', '69061', '48578', '40247', '77725', '31837', '39003', '69363', '78113', '76398', '97262', '67795', + + SELECT toString(rand()) IN ('41577', '83972', '51697', '50014', '37553', '93459', '87438', '95971', '83186', '74326', '67871', '50406', '83678', '29655', '18580', '83905', '61518', '29059', '56700', '82787', '98672', '30884', '81822', '39850', '80852', '57627', '91346', '64522', '17781', '49467', '41099', '41929', '85618', '91389', '68564', '91769', '81219', '52218', '37220', '97097', '2129', '9886', '52049', '34847', '25364', '36429', '76897', '71868', '58121', '71199', '84819', '69991', '34046', '64507', '34892', '24228', '36986', '28588', '51159', '53444', '80531', '9941', '20256', '48103', '32565', '62890', '5379', '60302', '46434', '3205', '18821', '31030', '19794', '71557', '71703', '15024', '14004', '82164', '95659', '40227', '83358', '24395', '9610', '19814', '48491', '66412', '16012', '71586', '42143', '51103', '24463', '89949', '35694', '39193', '63904', '40489', '77144', '94014', '84836', '9980', '46554', '43905', '25588', '25205', '72624', '10249', '35888', '98478', '99030', '26834', '31', '81499', '14847', '82997', '92357', '92893', '17426', '56630', '22252', '68119', '62710', '8740', '82144', '79916', '23391', '30192', '99271', '96435', '44237', '98327', '69481', '16691', '13643', '84554', '38571', '70926', '99283', '79000', '20926', '86495', '4834', '1222', '39486', '57697', '58002', '40790', '15623', '3999', '31515', '12694', '26143', '35951', '54085', '97534', '35329', '73535', '88715', '29572', '75799', '45166', '32066', '48023', '69523', '93150', '8740', '96790', '15534', '63252', '5142', '67045', '93992', '16663', '292', '63924', '6588', '12190', '31506', '69590', '35394', '55168', '65223', '79183', '32600', '69676', '28316', '72111', '53531', '15073', '41127', '73451', '24725', '61647', '65315', '41143', '26493', '95608', '34407', '76098', '53105', '83691', '48755', '35696', '62587', '81826', '3963', '45766', '82751', '12430', '97685', '29919', '78155', '71636', '50215', '89734', '9892', '47151', '54855', '3428', '9712', '52592', '2403', '79602', '81243', '79859', '57361', '82000', '42107', '28860', '99591', '28296', '57337', '64969', '32332', '25535', '30924', '21313', '32554', '17342', '87311', '19825', '24898', '61323', '83209', '79322', '79009', '50746', '33396', '62033', '16548', '17427', '24073', '34640', '52368', '4724', '80408', '40', '33787', '16666', '19665', '86751', '27264', '2241', '88134', '53566', '10589', '79711', '92823', '58972', '91767', '60885', '51659', '7867', '96849', '30360', '20914', '9584', '1250', '22871', '23282', '99312', '4683', '33429', '68361', '82614', '81440', '47863', '69790', '11968', '75210', '66854', '37002', '61142', '71514', '1588', '42336', '11069', '26291', '2261', '71056', '13492', '9133', '91216', '72207', '71586', '86535', '83898', '24392', '45384', '48545', '61972', '503', '80180', '35834', '97025', '70411', '55039', '35430', '27631', '82533', '96831', '74077', '42533', '14451', '26943', '53783', '69489', '71969', '8432', '37230', '61348', '19472', '59115', '9886', '50951', '57109', '7141', '1902', '84130', '4323', '55889', '47784', '2220', '75988', '66988', '63721', '8131', '95601', '95207', '2311', '26541', '50991', '6717', '2969', '71857', '51034', '65958', '94716', '90275', '21012', '46859', '7984', '31131', '46457', '69578', '44540', '7294', '80117', '9925', '60155', '90608', '82684', '32193', '87071', '28006', '87604', '24501', '79087', '2848', '29237', '11221', '81319', '40966', '87641', '35325', '78705', '88636', '78717', '62831', '56390', '99271', '43821', '14453', '17923', '62695', '77322', '21038', '67677', '41271', '4376', '65426', '46091', '19887', '97251', '55583', '58763', '3826', '35037', '73533', '64267', '82319', '9836', '42622', '96829', '16363', '10455', '49290', '99992', '98229', '66356', '59087', '73998', '25986', '4279', '56790', '69540', '588', '36620', '60358', '45056', '89297', '42740', '8323', '19245', '82417', '41431', '699', '11554', '73910', '44491', '56019', '68901', '45816', '68126', '89379', '23885', '13263', '56395', '73130', '19089', '23771', '10335', '48547', '16903', '6453', '33560', '89668', '38159', '43177', '90655', '49712', '62', '66920', '34180', '12150', '48564', '39538', '85026', '87195', '14928', '8956', '71157', '53287', '39161', '67583', '83309', '92054', '86977', '56188', '15229', '88170', '60894', '58497', '89254', '40082', '86890', '60161', '97291', '45878', '23368', '14577', '92870', '37017', '97356', '99426', '76061', '89186', '99751', '85153', '61580', '39360', '90107', '25603', '26798', '76224', '6469', '7912', '69838', '16404', '67497', '28965', '80836', '80365', '91249', '48713', '17113', '33090', '40793', '70450', '66689', '83698', '17802', '43869', '13355', '18959', '79411', '87930', '9265', '37504', '44876', '97234', '94149', '35040', '22049', '49248', '6535', '36080', '28346', '94437', '78319', '17961', '89056', '56161', '35810', '41632', '45494', '53351', '89729', '99510', '51584', '59688', '6193', '70809', '51093', '92589', '90247', '34910', '78235', '17362', '49423', '63324', '525', '37638', '72325', '89356', '15298', '59116', '17848', '65429', '27029', '84781', '70247', '8825', '35082', '70451', '22522', '58125', '91879', '90531', '2478', '463', '37902', '54405', '87267', '72688', '22803', '33134', '35177', '84551', '44974', '88375', '76407', '27774', '33849', '19915', '82014', '80434', '26380', '48777', '53811', '14838', '26829', '56441', '99869', '49574', '85476', '19723', '16907', '4018', '37338', '78510', '47912', '13030', '65277', '95716', '67363', '21393', '89887', '78842', '81650', '903', '17436', '30704', '49223', '27198', '25500', '52214', '54258', '70082', '53950', '49312', '43615', '99473', '94348', '53661', '96213', '96346', '62010', '38268', '32861', '75660', '10392', '89491', '68335', '29817', '88706', '24184', '36298', '43440', '21626', '26535', '44560', '46363', '12534', '99070', '95606', '33714', '73070', '8303', '29853', '23014', '99982', '4530', '14955', '45803', '50', '90750', '30394', '81276', '95563', '47314', '58520', '91299', '88944', '54402', '67405', '29253', '47079', '71734', '99728', '17652', '13307', '35556', '18962', '26780', '17771', '53712', '60055', '37628', '35830', '90739', '61151', '41309', '27652', '3051', '53167', '98417', '19382', '36833', '75085', '65374', '87732', '30352', '31776', '32765', '97565', '92199', '49050', '29503', '51024', '18834', '8515', '24069', '96216', '10777', '90680', '18974', '68884', '85305', '36007', '56707', '4212', '47352', '34426', '13185', '92939', '95782', '70577', '58080', '98279', '3906', '5065', '56896', '16382', '31273', '17117', '98602', '12786', '24086', '63970', '72756', '35798', '82367', '7356', '53398', '68503', '2962', '16425', '67334', '68461', '65439', '15620', '70906', '29649', '46461', '74602', '38012', '71714', '16825', '89480', '53386', '88532', '35104', '28556', '82120', '23155', '23347', '24797', '60061', '54962', '99427', '82248', '82447', '39968', '63727', '27431', '81511', '91168', '71425', '80740', '84127', '40717', '15503', '15419', '46594', '61263', '19212', '53175', '70724', '74445', '23034', '71818', '40246', '18886', '53066', '4880', '83701', '86107', '87862', '44751', '392', '73440', '90291', '93395', '20894', '38463', '32664', '55158', '20090', '50004', '79070', '98471', '85478', '96615', '68149', '78334', '97752', '73207', '71678', '91238', '96757', '82598', '194', '35797', '45120', '60782', '28721', '17676', '78066', '60957', '11826', '51563', '50516', '16485', '47053', '31738', '48923', '23554', '96850', '42033', '73701', '78607', '45979', '54571', '12415', '31693', '15356', '36902', '9126', '3767', '3295', '90402', '24005', '95350', '67033', '49137', '72606', '51899', '17522', '31957', '44641', '53982', '23767', '68257', '15766', '19995', '2107', '48788', '11765', '91055', '46576', '54651', '50381', '62827', '73636', '46606', '98753', '37631', '70441', '87916', '66983', '33870', '31125', '12904', '57040', '4874', '58632', '42037', '18782', '5998', '18974', '57949', '81010', '90407', '99874', '20462', '89949', '10952', '71454', '95130', '46115', '3518', '13384', '69039', '79482', '22076', '59782', '32042', '40930', '60243', '29298', '6790', '46985', '44398', '85631', '14380', '66179', '2629', '32126', '49833', '14118', '58492', '31493', '81172', '96638', '8745', '89663', '76842', '78633', '41373', '83721', '42886', '11123', '32739', '11051', '1303', '92314', '83324', '85600', '44276', '69064', '56125', '84650', '31028', '12628', '14502', '64764', '39405', '44855', '79046', '51716', '46824', '83389', '1941', '1257', '9280', '73176', '84729', '2579', '63366', '22606', '35541', '51096', '13447', '18355', '68037', '28436', '94116', '81070', '78355', '67897', '5296', '32742', '77645', '91853', '18767', '67949', '40963', '5792', '17278', '25597', '41884', '80829', '7099', '18645', '60295', '12082', '81800', '78415', '18082', '38789', '16295', '72377', '74949', '55583', '66853', '15402', '72977', '15123', '99434', '34999', '21687', '76049', '42987', '83748', '88256', '66688', '21766', '20304', '29271', '10069', '19822', '11792', '42526', '74143', '17289', '30253', '6367', '20888', '12975', '94073', '98639', '30134', '26320', '65507', '69002', '53120', '4550', '38893', '18954', '38283', '54863', '17698', '99670', '10521', '92467', '60994', '18052', '48673', '35811', '87282', '62706', '16061', '53112', '22652', '37780', '55662', '26331', '49410', '79074', '10623', '69577', '79613', '9491', '31229', '43922', '84231', '58409', '36386', '46875', '74431', '76735', '38776', '23350', '7314', '9079', '51519', '98544', '70216', '63380', '90381', '1295', '46901', '58225', '55339', '89918', '75522', '35431', '89460', '49552', '89302', '23068', '28493', '3042', '25194', '59520', '9810', '95706', '81297', '89638', '54794', '94527', '45262', '97932', '78685', '6947', '22818', '48700', '9153', '12289', '22011', '58825', '93854', '65438', '4509', '33741', '28208', '69061', '48578', '40247', '77725', '31837', '39003', '69363', '78113', '76398', '97262', '67795', '68446', '58896', '60969', '19849', '6722', '91854', '49519', '13949', '67109', '48824', '31723', '75554', '69575', '94986', '75350', '18628', '15284', '41943', '15433', '52607', '41', '22340', '29528', '24059', '34145', '72517', '46316', '10667', '54510', '19882', '47764', '69124', '41963', '84350', '48420', '4646', '24958', '69020', '97121', '26178', '62664', '50622', '32554', '49655', '45398', '11267', '72222', '73363', '89554', '89046', '57384', '29259', '37888', '24850', '74353', '57343', '34762', '2900', '11393', '42154', '94306', '70552', '75265', '921', '26003', '64352', '89857', '83171', '58249', '48940', '53512', '66335', '44865', '68729', '19299', '58003', '39854', '99122', '3860', '80173', '52242', '90966', '53183', '71982', '82325', '87842', '15000', '55627', '71132', '6354', '42402', '91719', '91644', '94533', '74925', '66278', '66911', '85576', '40495', '70919', '71797', '87835', '29845', '71832', '3390', '7994', '33499', '70715', '54897', '82710', '63077', '78105', '24758', '89585', '84607', '46477', '78618', '10989', '39222', '98749', '51685', '94664', '31008', '32823', '89521', '72160', '26952', '4001', '21421', '5737', '74027', '88179', '45553', '83743', '19913', '49435', '65616', '82641', '5149', '76959', '40681', '73023', '2670', '30845', '18863', '35094', '88400', '80963', '9154', '16571', '64192', '59694', '41317', '59942', '58856', '99281', '67260', '66971', '22716', '76089', '58047', '67071', '53707', '462', '52518', '72277', '10681', '69', '98855', '12593', '88842', '67242', '73543', '37439', '18413', '67211', '93495', '45576', '70614', '27988', '53210', '18618', '21318', '68059', '25518', '55917', '56522', '16548', '2404', '93538', '61452', '66358', '3709', '23914', '92426', '81439', '38070', '28988', '29939', '2948', '85720', '45628', '51101', '89431', '86365', '17571', '50987', '83849', '11015', '83812', '66187', '26362', '66786', '22024', '93866', '36161', '90080', '64874', '37294', '83860', '73821', '80279', '36766', '73117', '44620', '84556', '42070', '90383', '27862', '20665', '67576', '34997', '57958', '80638', '84351', '63961', '1362', '14338', '80377', '24192', '41294', '57368', '51189', '27287', '45764', '86289', '65600', '708', '84090', '96005', '55676', '84855', '72385', '70018', '9336', '82701', '3710', '52083', '74045', '96454', '30956', '67369', '78941', '81810', '71906', '23194', '33042', '50794', '61256', '24449', '48639', '22916', '78303', '13666', '40762', '43942', '51075', '89783', '95786', '90462', '6181', '36482', '40675', '4970', '6388', '91849', '72579', '94983', '86084', '20140', '68427', '48123', '43122', '98066', '37560', '6927', '72803', '5546', '62259', '98439', '6457', '98568', '70499', '33022', '28226', '29675', '20917', '75365', '20900', '8190', '56736', '99153', '77779', '49333', '50293', '97650', '4067', '47278', '42761', '71875', '13966', '11223', '46783', '18059', '61355', '29638', '75681', '24466', '89634', '20759', '83252', '37780', '15931', '74893', '6703', '64524', '80656', '85990', '78427', '18411', '20696', '86432', '93176', '69889', '15072', '15180', '9935', '10467', '60248', '42430', '62590', '89596', '27743', '26398', '79912', '60048', '50943', '38870', '69383', '72261', '98059', '55242', '74905', '5667', '54321', '70415', '39903', '49711', '85318', '79979', '59262', '82321', '15263', '17416', '74554', '94733', '72112', '49872', '54849', '73883', '78250', '74935', '68559', '57564', '50541', '45730', '41595', '5588', '83723', '42891', '11898', '14348', '99732', '14481', '85233', '21277', '94508', '52551', '74187', '7634', '42912', '25100', '43536', '35798', '48190', '86477', '22680', '48148', '59501', '56563', '16802', '81496', '97568', '68657', '51462', '67953', '99660', '39002', '54170', '57190', '68086', '52700', '6487', '55709', '70418', '62629', '70420', '35695', '36152', '45360', '53503', '46623', '76000', '50648', '97876', '44815', '29163', '1356', '64123', '71388', '17658', '99084', '58727', '59437', '38773', '71254', '81286', '97545', '18786', '56834', '20346', '36401', '62316', '58082', '67959', '99876', '69895', '80099', '62747', '20517', '99777', '6472', '49189', '31321', '39992', '68073', '13378', '51806', '21776', '52060', '96983', '25754', '93709', '96627', '8644', '93726', '14002', '37716', '87620', '34507', '76339', '24491', '5849', '44110', '522', '66521', '12776', '44887', '80535', '14548', '75248', '671', '73071', '35715', '59474', '7061', '82243', '56170', '20179', '59717', '1725', '24634', '11270', '77023', '63840', '46608', '44667', '22422', '59771', '94768', '73033', '82905', '16463', '40971', '22204', '58366', '28721', '14907', '76468', '81872', '38418', '36989', '61439', '10610', '131', '44296', '35453', '10117', '75856', '94603', '99602', '68075', '35949', '13599', '50030', '69633', '55956', '85465', '16429', '86081', '11145', '6195', '82207', '90598', '92814', '23725', '83204', '80346', '71542', '46634', '15820', '54123', '45397', '15322', '61743', '9273', '71347', '6835', '64006', '91718', '43677', '32923', '21486', '17098', '61694', '43347', '40019', '4071', '52443', '42386', '56839', '83514', '27633', '40780', '51749', '92101', '62384', '92206', '56044', '66174', '11137', '73966', '78471', '30468', '31643', '33197', '6888', '8066', '86603', '74383', '6098', '54411', '98819', '89862', '88639', '94422', '89371', '80526', '91747', '91220', '64944', '76658', '42046', '58518', '27249', '6646', '3028', '1346', '33763', '9734', '31737', '65527', '5892', '60813', '3410', '35464', '43009', '98382', '70580', '93898', '56404', '32995', '62771', '71556', '40538', '55612', '45656', '10758', '20268', '33603', '38310', '14242', '74397', '10722', '71575', '22590', '49043', '91439', '9055', '23668', '9101', '5268', '64133', '77501', '64684', '11337', '47575', '50732', '88680', '93730', '46785', '17589', '3520', '57595', '71241', '34994', '8753', '36147', '88844', '41914', '11250', '94632', '71927', '4624', '86279', '7664', '2659', '94853', '65386', '30438', '86005', '92883', '84629', '59910', '44484', '1306', '8404', '56962', '29990', '38445', '96191', '73013', '66590', '40951', '24712', '18825', '37268', '87843', '18972', '12154', '7779', '52149', '76152', '65799', '86011', '35475', '78083', '88232', '91551', '65532', '93516', '73827', '24227', '44687', '55759', '83819', '45088', '10856', '60488', '39051', '14103', '76650', '81181', '46731', '737', '58788', '78945', '42096', '66731', '66740', '72273', '88969', '5655', '86590', '41096', '80038', '32430', '51877', '23970', '91900', '13082', '45880', '94367', '19739', '61998', '71665', '16083', '57035', '26916', '10166', '18834', '46798', '66881', '28444', '68840', '10459', '81087', '4728', '76224', '39257', '23470', '93524', '37345', '30074', '49856', '22022', '55279', '5159', '5193', '58030', '57539', '12514', '49759', '96222', '52597', '67192', '88187', '53614', '16084', '79915', '28212', '79334', '85283', '32306', '31058', '43113', '74707', '74869', '2213', '32134', '6379', '85426', '87098', '35984', '51105', '69287', '16803', '83337', '14913', '62531', '58098', '7914', '20105', '28850', '1384', '43173', '62983', '87113', '76066', '86320', '77684', '45191', '95225', '41503', '36713', '48404', '91228', '53865', '98981', '59161', '61237', '84561', '17455', '14379', '57789', '80895', '99260', '84595', '72942', '53220', '84448', '81332', '49437', '83086', '93414', '54519', '52288', '74772', '22460', '49324', '11168', '96071', '61985', '38284', '6405', '54698', '71727', '60093', '37340', '87884', '83403', '4542', '94949', '19636', '15855', '39105', '10424', '67418', '91022', '69254', '8481', '38411', '3832', '44354', '93548', '57172', '28481', '372', '81497', '52179', '41060', '72141', '41396', '65590', '70432', '82819', '93814', '26118', '84780', '88485', '70821', '8222', '83000', '47067', '38516', '33347', '47681', '48202', '60749', '52112', '7937', '28105', '11394', '45746', '43252', '34494', '2979', '69715', '42486', '82315', '71760', '97413', '66137', '94487', '7429', '74434', '22964', '55251', '3448', '53534', '2574', '9693', '96157', '2955', '4348', '19566', '56930', '83319', '31310', '53905', '1148', '41726', '22233', '76045', '37351', '10545', '17581', '28047', '30199', '4741', '58111', '33497', '67796', '67730', '31247', '43772', '29461', '45970', '73353', '22534', '53962', '32147', '71392', '62579', '66345', '58246', '33442', '9581', '29705', '14058', '86471', '76125', '59363', '94982', '74810', '89149', '20066', '3366', '3568', '25752', '80036', '64119', '27270', '40061', '91052', '69022', '9852', '77112', '83075', '43924', '61661', '56133', '96652', '57944', '72576', '82170', '79236', '55745', '15309', '88878', '72761', '37647', '67465', '12777', '97309', '93202', '41470', '8787', '64920', '48514', '18917', '35157', '59151', '4640', '5317', '38134', '76548', '82788', '9214', '58418', '73185', '90554', '10543', '47182', '62936', '91765', '89751', '68931', '48865', '64607', '7150', '77862', '14297', '14828', '33013', '91698', '67593', '98096', '16595', '51639', '86531', '24719', '1703', '78788', '43810', '38918', '95491', '99903', '82671', '8291', '68288', '31224', '39863', '4265', '77798', '7698', '33804', '92286', '4744', '37038', '44203', '98212', '17369', '77442', '62879', '4145', '96881', '15646', '36824', '19959', '45451', '76049', '54272', '97577', '95298', '81115', '30204', '82041', '8037', '10052', '8756', '76833', '82851', '24276', '75574', '36037', '78079', '92807', '29064', '90000', '84150', '17102', '75092', '49424', '35597', '4693', '82853', '42511', '16119', '23478', '65240', '55585', '91762', '71671', '46682', '72479', '97696', '24615', '12579', '30274', '48255', '2336', '90202', '5808', '45426', '76308', '74639', '31245', '99894', '89638', '6233', '33893', '71899', '85273', '89429', '29761', '50231', '57249', '99347', '22642', '66972', '86221', '47514', '88274', '10819', '73150', '53754', '13304', '20478', '38099', '619', '14669', '8011', '97657', '26569', '65430', '13467', '38180', '23675', '72350', '42257', '39875', '23529', '53407', '11833', '29599', '95621', '7727', '59527', '86846', '22860', '5358', '3730', '87555', '362', '95755', '54565', '29935', '68950', '52349', '98344', '86576', '7420', '12236', '15844', '48099', '97535', '97081', '50261', '31187', '60496', '24123', '24042', '6376', '6679', '99806', '20306', '60676', '36881', '77309', '5247', '96569', '53417', '73252', '64179', '35318', '75732', '65119', '32621', '40464', '22887', '96152', '65161', '83381', '8915', '68142', '7328', '85031', '15688', '72519', '93992', '86927', '75538', '38205', '50877', '70039', '97538', '94822', '52131', '49643', '85206', '1347', '14574', '88736', '53442', '49991', '64925', '72283', '82213', '60905', '36118', '62963', '16983', '79185', '15111', '26059', '17792', '98218', '33214', '1094', '41754', '77275', '65173', '13190', '91004', '90422', '44387', '92672', '98641', '54609', '83295', '37395', '70104', '32986', '72524', '82478', '5837', '83916', '52736', '57112', '55985', '42642', '42136', '89642', '35712', '49489', '19726', '65824', '24384', '48112', '15366', '99206', '68384', '51389', '529', '21475', '75749', '95182', '60110', '70571', '74174', '38105', '78107', '4101', '8982', '11215', '23987', '3303', '28706', '54629', '98000', '67510', '30036', '99140', '48896', '40971', '7735', '79984', '50134', '94928', '57023', '52880', '83067', '41940', '62994', '89213', '38593', '19283', '68206', '22234', '19245', '26266', '32403', '65889', '17022', '64280', '42797', '27161', '57675', '42313', '93606', '93082', '20659', '90824', '1226', '66266', '12503', '57104', '15247', '51160', '92398', '71967', '59476', '44465', '35765', '10787', '47737', '45792', '2292', '47599', '89612', '8162', '87622', '69410', '45727', '31158', '99791', '89544', '27214', '99588', '40516', '75616', '36505', '46079', '95448', '97999', '47462', '47799', '82729', '34038', '60789', '96938', '22682', '79062', '93307', '36038', '49016', '90983', '48219', '50889', '32517', '72219', '71229', '82643', '1195', '70543', '17', '22178', '23544', '72371', '1163', '28527', '7336', '39846', '31956', '80963', '41804', '59791', '41831', '1940', '52377', '79494', '12531', '81112', '44320', '18746', '5774', '63869', '4085', '59922', '12751', '99443', '13530', '23872', '36026', '83360', '32711', '92980', '11140', '99323', '57263', '98149', '29265', '25548', '65995', '4818', '15593', '8535', '37863', '12217', '14474', '66584', '89272', '86690', '58777', '39666', '44756', '18442', '52586', '98030', '40850', '38708', '49304', '68923', '65008', '84388', '83639', '29866', '63675', '26793', '49227', '82099', '24090', '57535', '24201', '65776', '74054', '89833', '62979', '26613', '5851', '99766', '63484', '66605', '37179', '90760', '59336', '58390', '93239', '84578', '11396', '93994', '73818', '23972', '37720', '72369', '25063', '32952', '71036', '76612', '31285', '34090', '19136', '53783', '66436', '61478', '96749', '43658', '7399', '31574', '67073', '40480', '20727', '70993', '65549', '30800', '21507', '53785', '89574', '86381', '56492', '62603', '44856', '68687', '63794', '70996', '7475', '84238', '71939', '86886', '94792', '15036', '36936', '95722', '17771', '67850', '33371', '49314', '40744', '5432', '81057', '41201', '75986', '22961', '15323', '1570', '18657', '95219', '19130', '53127', '15867', '81135', '73206', '76668', '36386', '48828', '31417', '56916', '70891', '60534', '95777', '10022', '94053', '2928', '56326', '16559', '79656', '6414', '81247', '78270', '55687', '19151', '61597', '99857', '81142', '27725', '53493', '12185', '1455', '48501', '59425', '20591', '24900', '66079', '84889', '32024', '18919', '2043', '7076', '71201', '88258', '86521', '93348', '26395', '39646', '44145', '33911', '46231', '67054', '39979', '11630', '23020', '76278', '88056', '11480', '4723', '78612', '70211', '60622', '84687', '59092', '65675', '38479', '64399', '64699', '95964', '42764', '69060', '28189', '4193', '95805', '75462', '17245', '59640', '94773', '84292', '53092', '98507', '61353', '32483', '53027', '48912', '87221', '47788', '59263', '65196', '35567', '17494', '64253', '50223', '7057', '87467', '62414', '2523', '50910', '72353', '78986', '78104', '47719', '29108', '12957', '5114', '64435', '66707', '37449', '70399', '45334', '71606', '55338', '55072', '58765', '12151', '22012', '16954', '87366', '14240', '98041', '72296', '47408', '56879', '99584', '63172', '92316', '28071', '29880', '19608', '13839', '87484', '56541', '88662', '87098', '72124', '78282', '27653', '38993', '31870', '67239', '99445', '7376', '78487', '98880', '12180', '86773', '67773', '15416', '58172', '13075', '67559', '97510', '29705', '86985', '57024', '11827', '31236', '91920', '26116', '94614', '14486', '46252', '78847', '43786', '70048', '96739', '35240', '39933', '58209', '27852', '65669', '47323', '58150', '84444', '44344', '95882', '41258', '31314', '69060', '19916', '6979', '19436', '45572', '16259', '74566', '6306', '24705', '53422', '593', '97031', '22308', '26875', '23042', '78035', '34229', '61976', '23175', '50072', '90896', '50810', '71730', '86468', '94807', '8218', '36032', '58628', '60560', '51206', '37943', '27987', '15014', '49905', '70018', '66799', '80851', '23594', '29982', '6438', '97381', '47715', '96294', '17985', '48545', '12672', '5250', '9988', '24601', '3736', '97815', '54363', '64703', '44167', '68376', '16595', '38073', '29630', '59630', '1858', '71823', '75580', '70083', '14493', '93821', '93394', '85369', '3818', '8435', '59988', '43966', '13961', '15855', '83332', '80312', '27299', '88840', '76964', '56173', '62794', '79389', '82642', '85843', '47116', '43064', '16061', '28905', '54415', '72832', '91252', '93488', '79457', '99336', '70744', '80432', '6487', '880', '87701', '154', '86574', '86677', '17892', '81488', '95260', '12515', '43189', '9211', '55403', '41417', '60046', '54785', '83655', '28274', '65745', '63062', '44549', '36391', '48051', '7328', '3572', '33226', '49177', '25123', '59065', '19691', '15109', '10172', '95578', '29497', '48152', '20276', '36270', '78866', '48309', '53209', '55475', '30073', '19717', '16004', '45692', '83430', '9291', '45935', '57030', '92613', '91656', '67697', '34915', '28156', '56594', '3273', '11194', '98270', '34370', '2621', '66679', '97451', '97717', '87923', '48310', '37725', '69743', '75103', '84956', '75163', '16069', '65304', '19397', '18071', '27273', '49823', '57595', '98324', '82174', '10293', '80943', '64184', '19472', '4198', '9410', '25927', '65961', '33155', '95168', '33692', '61712', '69877', '13308', '17415', '10022', '2491', '67310', '96140', '68050', '76272', '17143', '76805', '57176', '7539', '22690', '95483', '87592', '27221', '90821', '51154', '99828', '68998', '54581', '74222', '10269', '65057', '45467', '96089', '55058', '89779', '60837', '74122', '52886', '58055', '14880', '93208', '66652', '68830', '24121', '62407', '87257', '18802', '14925', '45423', '98624', '55195', '59072', '41414', '77840', '66075', '62705', '26549', '19063', '57552', '2507', '52069', '57620', '66688', '14833', '33700', '90666', '98052', '5367', '2268', '43093', '69063', '22030', '85564', '92258', '1847', '24446', '65835', '38660', '91899', '87732', '52396', '31952', '36000', '86944', '16109', '80729', '53757', '60226', '59103', '84187', '36674', '72823', '29884', '4654', '69139', '20440', '57413', '3651', '39639', '44564', '57492', '84159', '751', '99748', '9659', '72661', '39220', '99742', '74734', '75729', '38071', '69934', '73640', '65294', '54524', '64372', '37927', '17187', '7863', '12732', '40296', '36197', '15821', '76831', '4400', '71933', '4040', '22072', '33064', '25702', '13324', '91275', '27388', '97729', '14620', '45989', '80737', '17934', '4219', '3032', '43457', '31051', '24469', '67041', '29328', '75499', '80951', '88212', '92595', '49969', '24612', '58732', '2718', '3805', '50918', '99426', '8614', '35580', '93273', '989', '24385', '41185', '25687', '47146', '25227', '95839', '56355', '98536', '79824', '31725', '46447', '26690', '68418', '47783', '33725', '21729', '70797', '59038', '60376', '25087', '68332', '67950', '12411', '95918', '64736', '65336', '74947', '64605', '4106', '42712', '96640', '28492', '28648', '42429', '821', '24333', '69677', '38959', '23484', '92005', '29352', '29159', '52873', '99947', '21834', '85347', '93479', '28298', '55608', '3226', '69714', '80283', '6577', '18849', '44605', '75286', '28139', '26541', '12867', '57500', '86617', '33005', '57498', '60223', '74954', '51401', '55246', '5648', '16513', '40930', '43821', '32090', '66002', '65530', '76083', '6047', '6879', '94987', '80787', '11688', '77161', '92670', '6696', '400', '28572', '47234', '51375', '88518', '762', '92617', '54260', '7560', '60180', '43331', '64059', '27616', '75839', '21392', '47756', '46254', '19486', '88533', '30130', '93694', '8557', '66534', '94447', '16910', '6480', '77440', '24366', '6195', '48946', '28597', '44429', '50300', '73556', '40638', '98709', '94413', '15987', '43860', '64871', '93953', '34506', '7296', '31753', '30626', '77510', '39829', '25696', '39776', '69185', '36540', '65413', '31528', '43446', '73532', '49776', '30282', '30004', '26725', '15200', '33958', '90320', '71836', '48051', '31970', '5326', '96194', '69695', '60898', '60945', '18271', '50868', '61468', '23593', '68985', '20628', '58044', '8942', '34849', '7384', '50500', '62895', '78780', '48946', '65278', '4067', '973', '34761', '15512', '73739', '23138', '47322', '55568', '32259', '71816', '49277', '75218', '76104', '19579', '68312', '67904', '33886', '53888', '26421', '43859', '40291', '39068', '31711', '36542', '10195', '39781', '72352', '13188', '34113', '9428', '60443', '4987', '13783', '80744', '63483', '18266', '11961', '87167', '46987', '28480', '74214', '39191', '8146', '38090', '75727', '79245', '47720', '52547', '45321', '4972', '49701', '74354', '69672', '63455', '41902', '5667', '54166', '4962', '25873', '44509', '73332', '73383', '29438', '21455', '12320', '11997', '16921', '49379', '63027', '86175', '8110', '76149', '2520', '11256', '25863', '50518', '69001', '79113', '9447', '91840', '5242', '10998', '46496', '2448', '56058', '20970', '10517', '17783', '25723', '97137', '62840', '1264', '78691', '81020', '55335', '48524', '2088', '90413', '76651', '26855', '16177', '14954', '62914', '21344', '5708', '75560', '39311', '95865', '28783', '64902', '95657', '46276', '33426', '4799', '11588', '57513', '73689', '77677', '63011', '97795', '34954', '76866', '32043', '32697', '26643', '36890', '53476', '3011', '13963', '49551', '87671', '67761', '17488', '94770', '50599', '33272', '23091', '38079', '41177', '22395', '91656', '79679', '38687', '57384', '80118', '42507', '4098', '78949', '45669', '48802', '83915', '78292', '4369', '57657', '49146', '45192', '98491', '72457', '46331', '207', '81601', '7409', '70856', '91605', '70295', '9171', '72293', '32997', '78025', '16795', '73534', '68780', '21284', '31767', '94381', '86439', '12420', '53285', '99563', '60502', '67954', '55012', '99809', '5431', '69978', '99712', '14401', '79498', '4495', '3045', '528', '72542', '91604', '72725', '39378', '80378', '41996', '20138', '54545', '59730', '36951', '45157', '37964', '97690', '12184', '4944', '53803', '93605', '60851', '68938', '46285', '89663', '90309', '6907', '87239', '81791', '83292', '90013', '68927', '14725', '81840', '63836', '52068', '43830', '4794', '931', '59255', '8263', '99057', '94401', '69033', '7437', '20364', '92884', '28193', '43932', '37629', '59426', '18891', '8583', '79551', '87242', '1483', '6725', '65786', '16844', '12650', '99305', '42841', '9811', '18800', '39313', '51373', '31874', '84558', '27831', '48614', '48975', '55509', '83363', '31854', '64001', '94028', '76125', '79314', '24893', '81132', '9441', '86015', '28356', '40358', '10160', '23328', '7330', '76538', '37611', '89351', '84132', '97047', '26109', '95222', '35130', '75600', '88602', '15073', '87835', '71649', '28948', '81615', '37498', '28674', '59776', '44095', '65924', '64368', '94536', '12518', '61711', '55619', '82949', '4114', '21540', '70544', '28022', '79983', '28781', '7749', '97873', '4951', '50076', '47611', '99522', '56820', '38653', '49047', '36283', '83908', '72452', '85625', '10811', '36998', '44083', '34864', '44975', '39057', '4551', '68450', '24781', '1503', '9871', '46885', '11424', '21259', '54900', '97669', '85669', '6015', '2521', '37661', '14915', '57423', '91903', '94789', '32059', '64972', '4600', '61465', '27118', '79785', '13547', '49766', '38410', '68860', '63756', '23621', '64387', '46255', '63408', '11297', '41081', '56326', '58349', '98703', '72268', '73574', '32098', '42534', '91502', '38083', '11241', '56828', '12098', '25377', '37054', '56328', '30034', '26922', '68401', '93478', '63275', '62650', '81407', '773', '79499', '14970', '47217', '1187', '57428', '69980', '77764', '74791', '22107', '54363', '39247', '56028', '56982', '84244', '21464', '18716', '25533', '94589', '94768', '21537', '18436', '81135', '27654', '79713', '56630', '61571', '58453', '26758', '68450', '68449', '2994', '15347', '83954', '71823', '6428', '44210', '79597', '95144', '32871', '1991', '320', '77157', '63607', '31154', '48846', '71125', '61750', '59608', '33038', '35733', '68915', '94127', '50383', '64242', '49708', '57270', '65019', '8581', '12111', '18487', '50013', '58664', '22214', '19033', '33681', '44754', '28830', '10381', '52318', '34959', '20682', '55453', '53800', '65774', '99164', '72102', '36986', '44157', '56716', '7974', '81475', '25926', '39402', '33688', '99671', '95312', '42268', '26536', '14482', '67377', '57993', '89147', '15834', '64995', '4700', '18714', '30221', '39095', '32749', '69257', '55204', '30497', '31839', '63045', '30009', '62683', '31232', '77680', '93551', '63589', '6989', '77246', '42169', '46117', '73226', '37427', '1858', '83649', '37410', '86369', '4641', '74481', '66168', '48041', '22597', '14670', '27464', '57165', '20939', '36282', '76940', '73358', '50521', '69603', '8895', '81793', '57743', '81903', '64025', '91641', '25276', '34040', '62642', '64015', '57657', '84890', '73832', '782', '60160', '16998', '40023', '24590', '88613', '76640', '53091', '67600', '80183', '45674', '64464', '25163', '42384', '66972', '13953', '41966', '66048', '15135', '73745', '19466', '53657', '34619', '13462', '15905', '48257', '73297', '238', '93525', '80556', '5942', '5411', '66169', '9090', '95130', '74316', '57321', '48083', '62355', '68113', '15239', '36644', '80326', '65817', '54428', '61955', '58849', '77206', '16073', '98261', '92091', '39178', '35464', '85109', '85452', '21128', '25665', '81860', '44664', '24024', '56960', '95124', '39786', '18836', '11121', '44163', '81074', '79064', '46219', '94694', '44233', '81469', '24642', '15030', '21995', '13587', '40755', '6669', '81093', '74305', '1881', '55649', '37273', '80827', '98643', '46694', '59281', '79231', '42813', '84984', '7052', '98113', '17296', '84434', '31205', '46894', '71219', '74530', '44686', '70744', '91388', '20692', '96853', '73803', '15836', '18126', '49686', '4179', '47588', '87892', '65425', '68012', '97468', '92510', '99271', '58694', '11918', '37051', '18644', '57228', '14265', '57572', '57022', '52186', '30193', '93570', '87872', '5257', '26784', '6476', '61746', '68559', '1720', '26202', '16519', '27688', '10645', '87174', '60845', '73385', '82075', '6933', '98828', '56895', '17344', '84253', '36561', '51648', '24939', '63470', '31034', '95052', '51090', '51465', '87979', '68650', '30181', '29598', '19137', '43221', '81353', '90170', '96985', '61115', '17385', '92314', '80650', '55821', '17874', '84333', '93272', '48260', '87272', '22764', '59957', '51870', '85988', '39222', '77241', '62535', '28344', '6011', '80831', '64551', '46299', '75195', '71177', '8660', '58943', '57003', '3306', '74413', '74068', '15073', '89016', '93140', '13911', '57170', '19880', '41870', '9131', '57495', '73032', '86979', '60094', '87026', '30880', '4736', '86301', '92707', '21689', '83565', '71275', '47665', '65687', '71184', '89897', '32490', '97577', '38723', '79113', '37531', '97500', '94450', '15699', '58019', '84423', '27057', '56017', '97148', '47365', '30669', '33818', '80406', '99690', '33012', '95178', '46809', '48448', '79350', '9146', '99701', '98976', '71197', '44161', '75069', '36602', '79650', '97301', '12020', '56658', '25701', '46392', '78609', '63073', '69419', '57736', '20102', '42415', '79044', '20277', '56280', '47903', '94311', '25558', '40336', '91305', '90505', '66769', '64562', '83737', '62892', '10375', '71024', '19988', '56946', '76110', '21847', '43162', '50578', '46086', '54167', '61722', '53463', '63134', '69288', '12838', '14116', '71687', '50846', '59810', '24826', '84138', '82885', '91496', '98600', '82769', '40049', '4125', '50694', '1294', '2805', '29691', '82321', '76462', '85945', '115', '29188', '66918', '71340', '31585', '61638', '95472', '52978', '50622', '81990', '60955', '70519', '22270', '35610', '95871', '89222', '41038', '52546', '1163', '67943', '1793', '92010', '35755', '74509', '66665', '95759', '8568', '44299', '67822', '5806', '85839', '13895', '87675', '31357', '88014', '40026', '53050', '28951', '31992', '42495', '82892', '51567', '2869', '45808', '20238', '20781', '56098', '66307', '95701', '614', '60833', '3091', '81339', '24195', '65639', '85976', '28116', '66224', '51502', '73637', '13207', '88302', '36488', '65518', '98187', '26', '74367', '64706', '53943', '86760', '25783', '82112', '34958', '86621', '20848', '63459', '14049', '84943', '91873', '50238', '77773', '64109', '8602', '87934', '47583', '66053', '30287', '5507', '80312', '37464', '57457', '86200', '17806', '16522', '38843', '94334', '59958', '63864', '53427', '74506', '33980', '90449', '30842', '53616', '36738', '52', '13595', '53051', '13174', '60163', '71420', '73835', '67119', '79018', '42782', '45059', '952', '46360', '85879', '71552', '84741', '29746', '32577', '10041', '7208', '97528', '51256', '916', '55973', '17684', '99046', '38782', '58660', '97798', '66032', '48339', '51329', '12532', '97904', '95454', '42737', '62541', '96702', '82953', '94610', '26645', '86813', '25480', '99713', '26078', '23028', '93056', '21445', '73209', '89318', '69987', '34705', '30064', '17094', '51135', '54141', '26625', '1086', '13082', '30843', '98672', '56864', '42605', '5833', '60850', '69366', '27351', '16456', '92609', '48030', '54322', '69891', '46502', '34578', '77918', '63276', '75958', '42519', '60266', '85576', '4855', '14258', '67017', '10545', '35078', '53012', '71922', '85784', '73402', '74363', '58457', '94102', '23510', '51559', '39482', '87057', '9377', '10106', '82985', '33931', '16523', '6484', '97749', '83172', '53753', '27466', '23073', '96083', '67302', '57465', '21877', '18013', '99804', '32873', '43123', '72365', '53197', '80578', '69770', '97471', '86954', '67183', '98497', '78474', '28450', '63183', '98699', '42738', '61433', '3491', '27304', '49311', '94980', '92740', '43272', '86549', '11406', '79636', '85582', '38086', '657', '2354', '26567', '77450', '42086', '21600', '49011', '44059', '47872', '75761', '96577', '11642', '83471', '79616', '23749', '77082', '96876', '65302', '84027', '48955', '59887', '20657', '75090', '9058', '50347', '66088', '70745', '76342', '58026', '95568', '61504', '93473', '84590', '47089', '74717', '93090', '46334', '68273', '59500', '54345', '72608', '54048', '86156', '40296', '74046', '6813', '36369', '74543', '18305', '85236', '31316', '37061', '96893', '23112', '5529', '10166', '19037', '1467', '70810', '30932', '18410', '92837', '81324', '12268', '54705', '25207', '90366', '56528', '3392', '88747', '39951', '97957', '99404', '23685', '13533', '15640', '11434', '66516', '71025', '65770', '88000', '52232', '32360', '10787', '37438', '2264', '94460', '80214', '42288', '59062', '29010', '64093', '21225', '22297', '36935', '19202', '5925', '85373', '27414', '28991', '9191', '42273', '56587', '89719', '77191', '64334', '61542', '28763', '28978', '79184', '59815', '95200', '30246', '54022', '287', '91808', '66347', '50833', '15356', '78614', @@ -25,8 +28,11 @@ '37674', '44977', '54370', '97381', '60218', '2423', '99591', '69913', '26507', '19708', '6279', '58955', '20126', '1495', '57894', '7638', '38700', '77148', '36844', '7539', '91452', '6914', '74349', '66850', '49104', '6516', '58535', '20851', '27859', '32881', '72919', '28203', '32882', '2419', '77583', '63822', '37703', '66793', '65784', '62281', '55867', '70703', '89344', '1498', '33770', '87176', '95636', '64891', '90736', '95521', '10989', '5237', '99010', '21106', '11422', '1831', '67239', '52557', '36468', '71713', '39637', '49574', '50455', '14953', '96900', '70852', '96982', '4341', '44585', '95651', '79669', '29652', '87294', '74692', '16221', '768', '35380', '21352', '50907', '27259', '11718', '5017', '55964', '94137', '52347', '10595', '12968', '85602', '97965', '18836', '90511', '70960', '97336', '44575', '23791', '42195', '64776', '29363', '42379', '1805', '28919', '6772', '78143', '54797', '27362', '56149', '59048', '38567', '6339', '27787', '42167', '45990', '95532', '54839', '26572', '38496', '89797', '6634', '16468', '24898', '66814', '98126', '31762', '36133', '64539', '43167', '87022', '61295', '30364', '89249', '25756', '63570', '91484', '10564', '79648', '5756', '41376', '61897', '40388', '88927', '62891', '79708', '25495', '22204', '33892', '36871', '19879', '58646', '57061', '73100', '75831', '20029', '67462', '54675', '7766', '2409', '24506', '7877', '11720', '86252', '9897', '8080', '70684', '74497', '2242', '24604', '31969', '83999', '56635', '5283', '64971', '79152', '27470', '89042', '22835', '21476', '50292', '56081', '96342', '32763', '84487', '64856', '79152', '64656', '72169', '69971', '93094', '52804', '80917', '53152', '56016', '28496', '79110', '17133', '12581', '91742', '78929', '2676', '46700', '59528', '93808', '4535', '54035', '40161', '62796', '3598', '97088', '13599', '36337', '73395', '17494', '86275', '62058', '61937', '87747', '94883', '90677', '88544', '72553', '50210', '75481', '64378', '74464', '21659', '30970', '71989', '84846', '72289', '88716', '39143', '8487', '4912', '91013', '18623', '19122', '36507', '76438', '7516', '67970', '72350', '69873', '33635', '55983', '69008', '49545', '3134', '60056', '52509', '63304', '15560', '23651', '81090', '7027', '8317', '33060', '37295', '51961', '53037', '97431', '40512', '23536', '25168', '78455', '85613', '12304', '40733', '99890', '51238', '55439', '96201', '73559', '92533', '90173', '16721', '6078', '29854', '38894', '31117', '63040', '86795', '81786', '21149', '38998', '61811', '48622', '73019', '59296', '13576', '92559', '36300', '77294', '26794', '50912', '98380', '13176', '57746', '75286', '15330', '40921', '7337', '4664', '20384', '4674', '44516', '27633', '31950', '88210', '54536', '9839', '80137', '77491', '18434', '45152', '96942', '41005', '76103', '34825', '86869', '14772', '13384', '21051', '37348', '34434', '97210', '54960', '26598', '60981', '41889', '6446', '64492', '95310', '86236', '81885', '35684', '16539', '98476', '32028', '96470', '6318', '99576', '93935', '48609', '86090', '2476', '65576', '80636', '44817', '99646', '98963', '20486', '26261', '27334', '72946', '82023', '33506', '80193', '13762', '98133', '21134', '33268', '63477', '74609', '30454', '51477', '93391', '96805', '68653', '2714', '63642', '51520', '22972', '13305', '96058', '42336', '74461', '31597', '12050', '81712', '37977', '25718', '4834', '56608', '75731', '406', '28585', '63924', '23702', '29849', '16941', '91921', '65842', '76525', '68534', '50902', '17609', '23852', '53703', '31286', '58526', '9633', '87596', '10654', '2085', '52766', '22135', '76524', '32295', '90072', '70078', '77786', '93741', '87320', '70309', '44024', '95286', '12361', '29682', '59766', '26685', '90686', '81691', '49704', '23431', '53955', '39023', '47261', '1530', '58265', '80065', '95620', '90621', '63760', '90676', '81653', '36397', '20252', '81754', '20256', '67098', '7838', '49408', '88400', '87941', '84533', '6570', '22567', '18850', '55472', '40129', '48425', '23497', '39308', '34698', '53092', '89480', '47785', '57282', '25508', '19006', '50604', '86917', '9436', '88921', '3168', '70537', '3185', '34988', '5462', '69482', '45768', '91955', '56898', '15307', '99731', '89292', '19356', '20646', '66712', '7281', '12856', '31174', '19577', '8726', '62971', '33008', '37118', '59055', '84101', '68445', '91957', '47526', '15627', '79914', '20013', '26147', '80821', '56372', '74205', '28531', '25352', '51775', '93948', '55212', '17863', '91521', '74911', '88160', '2360', '98260', '18294', '62402', '84268', '9580', '42668', '1467', '40059', '5221', '4216', '9917', '35420', '16496', '34369', '50253', '95234', '95114', '84193', '28322', '37031', '81284', '88628', '36782', '42572', '73347', '66188', '43342', '77285', '16513', '89064', '63066', '72645', '67075', '48208', '18181', '77898', '65795', '53707', '39856', '92883', '92567', '49733', '30236', '10273', '53029', '69773', '78379', '72108', '47696', '97557', '95184', '14688', '29853', '62694', '70431', '88435', '58799', '21883', '99866', '69178', '55870', '14414', '85274', '27321', '55555', '613', '15067', '88217', '73655', '99548', '13631', '78789', '36690', '7952', '60830', '77438', '40059', '95602', '43097', '3429', '93731', '90537', '2932', '35702', '16125', '6652', '39632', '39349', '9910', '38103', '78608', '73565', '48556', '28978', '7128', '82326', '53980', '28059', '28212', '87101', '77752', '99170', '56753', '30484', '71470', '32607', '24674', '32687', '25098', '94712', '64024', '48239', '90408', '17316', '99243', '3656', '67402', '48009', '98427', '52800', '56024', '4417', '89747', '93338', '18758', '56411', '44810', '82456', '30808', '75470', '67115', '66876', '53906', '78403', '56059', '34383', '60056', '89136', '7237', '11129', '21351', '78662', '43606', '37454', '45465', '9292', '38099', '81699', '50195', '49368', '47503', '44605', '6523', '81478', '37910', '397', '20256', '6835', '2787', '80383', '4241', '65986', '83870', '21205', '10879', '26593', '44357', '72604', '56131', '43423', '80206', '26240', '87198', '99445', '53504', '10632', '2465', '31793', '89575', '64184', '39988', '60049', '87100', '37151', '61585', '82180', '52065', '72519', '72935', '3201', '5862', '20560', '95339', '21661', '17533', '17182', '71189', '91564', '57999', '35490', '94773', '95056', '51583', '59394', '10727', '8655', '48123', '10701', '25314', '20100', '6533', '46435', '43188', '23001', '23018', '76637', '32018', '36603', '18701', '9550', '61550', '47541', '36500', '67507', '81574', '95490', '69169', '32584', '30045', '64699', '83539', '89396', '42517', '61979', '41528', '8271', '88377', '61423', '1158', '89724', '70789', '14886', '64823', '56675', '97747', '23990', '58495', '82064', '17062', '90258', '86854', '93304', '12925', '49975', '45074', '87155', '72223', '67344', '42733', '42516', '40110', '15444', '88285', '39371', '23198', '61544', '90205', '6192', '15718', '19803', '92712', '20081', '31397', '5555', '70463', '19521', '80401', '74097', '32060', '26495', '20507', '40473', '1449', '57215', '46142', '39303', '50359', '35898', '46908', '90752', '7823', '27416', '73770', '98790', '17907', '29999', '76417', '49926', '76752', '21608', '26524', '88209', '6000', '88897', '19541', '41451', '59538', '56560', '1456', '67828', '82407', '45722', '93344', '54279', '78594', '38354', '93807', '10929', '91560', '60681', '70615', '32527', '10108', '48303', '63134', '28500', '18257', '57081', '24801', '99077', '52197', '15390', '52300', '57116', '417', '7503', '20054', '75315', '81359', '69091', '18853', '2465', '25600', '13522', '74575', '12661', '83071', '15191', '27543', '21730', '60853', '18961', '14773', '89185', '33694', '51143', '1449', '68831', '78062', '65173', '32697', '41674', '9429', '22156', '96022', '46305', '97534', '5685', '48870', '89988', '20686', '66705', '6865', '94250', '16872', '13178', '7420', '73531', '92723', '60620', '48843', '74207', '60016', '50943', '62699', '63507', '76537', '87066', '76922', '24711', '34809', '5021', '31293', '53854', '77607', '52322', '10934', '50284', '87804', '36730', '86946', '80749', '43325', '97958', '7362', '39582', '10042', '42053', '66236', '69931', '23463', '87996', '33563', '4468', '32905', '50815', '79478', '28658', '46018', '23186', '26080', '13494', '6237', '42762', '86440', '77407', '10426', '62902', '73251', '36861', '92357', '98754', '1839', '46391', '11420', '27132', '93028', '39609', '42015', '68218', '54228', '5456', '38705', '64307', '49483', '878', '54360', '54480', '66684', '55089', '4537', '82073', '72602', '96238', '56708', '58625', '32991', '74205', '72868', '79086', '64250', '56376', '10621', '76607', '47706', '72760', '70303', '60715', '14644', '44186', '36264', '29489', '14184', '62699', '30567', '16700', '31222', '15650', '1500', '22950', '54628', '41004', '96094', '70028', '74178', '65328', '26605', '63076', '75271', '79285', '8151', '42101', '56362', '25961', '87864', '972', '29510', '2747', '8877', '9780', '61052', '84105', '15573', '27475', '44570', '25334', '18517', '44237', '84094', '67524', '76761', '65678', '79284', '2462', '42631', '22696', '19223', '29728', '67742', '11883', '59027', '12377', '80538', '2165', '17377', '15030', '49838', '23920', '26025', '68179', '75894', '43783', '97106', '75558', '35528', '52081', '16951', '68855', '402', '21459', '97550', '16948', '5369', '4641', '2663', '15233', '79974', '71093', '15234', '42690', '22322', '54282', '95845', '90010', '40530', '88298', '41885', '7079', '6098', '72786', '36603', '77378', '48393', '45723', '41996', '96025', '89297', '75586', '8422', '24360', '170', '46036', '46725', '67944', '74029', '73069', '45371', '99916', '71085', '42608', '89904', '6393', '51274', '42729', '58924', '82497', '64143', '88622', '18818', '89041', '56090', '21369', '78224', '90450', '45488', '58830', '4133', '98062', '81113', '11285', '51457', '3183', '38800', '65278', '42169', '28602', '52648', '44683', '75647', '11778', '32151', '33528', '23773', '68268', '23367', '70964', '23548', '35575', '67570', '77681', '74158', '25374', '62714', '43100', '4977', '51678', '83460', '29755', '15890', '64626', '54044', '14793', '64339', '94008', '97126', '49202', '33889', '12601', '12275', '56123', '94557', '68226', '67200', '9374', '70687', '29211', '8039', '14598', '74548', '37433', '98991', '29933', '37203', '23973', '96482', '64774', '58350', '61781', '31824', '57193', '26476', '21814', '32297', '32627', '44277', '33876', '55468', '81715', '82505', '61462', '20324', '84293', '40116', '51087', '43594', '6854', '59077', '39841', '26023', '22777', '66859', '82460', '89515', '41712', '33711', '71875', '10685', '12655', '50138', '31063', '37040', '95819', '38919', '27391', '29833', '34350', '65646', '7697', '2688', '41146', '13241', '50305', '86568', '24487', '78741', '96370', '21015', '31719', '39750', '25014', '72415', '8486', '90668', '51143', '49488', '21057', '92803', '53528', '39550', '76039', '44185', '32404', '30217', '19796', '38084', '49161', '80140', '20241', '39357', '68908', '93083', '77231', '6952', '36322', '50790', '623', '29730', '13616', '57546', '17434', '93811', '35148', '81419', '40250', '40329', '89126', '72402', '16053', '27107', '28919', '16829', '96582', '65057', '28416', '30801', '77742', '27420', '73118', '89352', '54706', '23035', '88413', '64608', '61930', '15037', '47327', '59596', '18700', '57576', '63628', '56823', '60091', '68209', '21001', '14962', '72257', '83802', '33721', '86343', '11133', '65737', '68477', '90725', '86869', '98403', '47393', '25356', '61372', '8873', '19888', '48836', '66005', '23531', '72520', '26461', '78508', '28213', '96394', '22983', '37856', '71814', '27425', '72753', '27511', '65471', '38592', '3683', '24652', '64505', '92543', '53201', '40639', '99542', '53425', '35321', '47669', '14134', '47727', '48202', '71931', '32119', '50086', '50266', '67159', '89317', '81905', '30315', '49154', '8690', '69365', '56881', '46473', '64100', '38365', '59377', '65630', '54871', '52745', '91536', '16106', '70066', '62063', '84530', '88103', '33599', '51063', '87299', '41880', '25335', '51252', '42788', '13568', '1721', '62424', '83308', '36787', '91536', '92555', '27600', '24030', '12267', '66336', '30242', '7183', '67624', '28471', '48593', '79766', '31178', '47818', '94522', '88855', '45262', '43670', '18065', '25062', '44558', '37189', '69225', '35216', '42683', '26289', '72816', '31947', '65871', '45715', '59452', '22014', '56669', '60331', '33450', '60601', '95047', '30789', '90107', '81565', '32266', '3252', '5446', '58756', '55370', '34034', '81071', '2560', '39054', '39564', '15010', '5389', '60002', '53320', '49545', '48444', '31415', '39278', '79879', '30148', '10186', '60358', '29011', '14419', '95159', '94815', '55251', '90910', '80582', '92304', '11697', '60061', '38577', '84439', '76196', '34542', '50963', '36294', '11123', '59763', '29873', '47383', '12979', '22119', '21723', '64725', '48377', '77132', '9817', '79920', '47653', '60069', '12924', '53808', '55962', '66969', '13757', '60615', '10994', '9138', '34119', '58436', '64407', '75170', '73524', '51864', '94183', '86847', '15585', '57616', '96267', '5340', '52929', '49096', '50291', '5559', '32382', '84077', '6598', '87921', '59719', '31726', '44772', '63373', '75420', '66829', '47275', '98264', '61387', '94945', '44540', '50098', '13078', '44729', '95332', '63555', '30782', '63203', '15071', '60996', '72812', '17418', '80215', '37610', '30670', '44674', '74822', '15471', '25236', '16266', '76213', '35820', '19567', '8715', '72003', '90606', '1434', '53545', '88170', '75014', '62287', '35436', '38669', '12927', '83877', '38622', '28313', '82884', '73969', '38671', '10450', '24158', '22941', '73162', '86548', '42482', '95315', '92016', '96156', '44012', '35962', '6366', '3881', '74300', '26248', '30182', '19164', '67105', '66771', '52587', '69894', '61820', '16551', '50743', '10096', '69030', '24451', '89165', '23929', '96291', '30685', '64413', '19913', '9049', '71383', '61684', '45384', '45927', '81840', '49521', '89594', '30055', '83430', '14930', '60316', '86585', '99375', '80170', '14207', '19584', '20067', '82874', '30159', '46647', '6942', '66777', '32638', '55662', '75470', '77622', '26893', '96149', '14373', '33252', '50574', '7945', '20696', '56662', '94348', '3384', '20956', '89668', '99052', '65131', '56847', '17589', '16419', '2670', '10705', '59587', '92902', '92424', '48570', '11034', '69149', '35733', '17315', '84966', '69353', '69590', '52834', '32561', '6049', '50156', '71676', '76423', '32361', '61509', '8845', '75709', '35956', '21912', '31188', '59083', '43459', '38614', '92206', '55645', '38737', '34193', '6451', '94163', '24326', '49976', '71600', '58024', '67160', '4365', '38270', '59558', '80834', '60739', '54318', '19738', '42196', '43191', '13463', '88914', '99239', '66869', '75691', '33085', '4323', '7170', '46184', '41423', '89835', '46877', '20349', '14365', '32727', '35322', '841', '23597', '43370', '57527', '73250', '32553', '71489', '44617', '98323', '37672', '59549', '96023', '63176', '13524', '15621', '30448', '28136', '45549', '3513', '64153', '19839', '24219', '41987', '51083', '90268', '52052', '31430', '4727', '99409', '43595', '82374', '61251', '51470', '66562', '98724', '23529', '53895', '67562', '87573', '89964', '30821', '15733', '33062', '86963', '33450', '75338', '32570', '14453', '38080', '36335', '84226', '52790', '42883', '61156', '42789', '57846', '60096', '29946', '80178', '15882', '1971', '60722', '62458', '8754', '59991', '89321', '584', '70565', '36458', '21226', '23561', '9837', '39364', '23065', '30675', '9306', '40085', '52082', '89976', '73283', '77851', '36174', '54470', '63250', '72111', '70853', '26723', '42590', '91230', '47512', '13983', '70898', '70927', '40721', '30642', '41628', '90010', '27306', '1933', '43304', '44499', '87890', '22201', '89249', '63935', '48438', '58588', '1061', '70061', '63075', '9676', '65820', '82156', '82668', '111', '54350', '10328', '23466', '98936', '18285', '53919', '32422', '84859', '58387', '24022', '32423', '6010', '56417', '49452', '69999', '14885', '47102', '59577', '24999', '75984', '96464', '59088', '85987', '71442', '88789', '4753', '8229', '76883', '15284', '90610', '40507', '78882', '55575', '25315', '7214', '70602', '4796', '35767', '54657', '42153', '16050', '93607', '99249', '77236', '59949', '52871', '47837', '33534', '30023', '89137', '99938', '35824', '50775', '30282', '82798', '53312', '65277', '68375', '91445', '58166', '43344', '6589', '82515', '34632', '78588', '152', '67554', '15877', '74334', '32783', '45147', '39483', '92067', '59029', '38298', '55229', '28268', '85140', '33451', '15424', '46695', '23201', '83329', '28372', '19518', '89198', '33305', '43892', '470', '37662', '9407', '14376', '80310', '21459', '72381', '80414', '88305', '69073', '63101', '91054', '47190', '48595', '24696', '41426', '35133', '94399', '21790', '55040', '73279', '20809', '67805', '94115', '58633', '78053', '89444', '4112', '8', '34517', '22106', '85934', '86814', '53333', '93437', '85062', '32791', '72744', '99843', '51161', '22730', '34908', '82918', '92566', '22467', '41226', '98518', '29235', '94042', '84371', '79100', '25214', '7764', '59427', '47891', '61092', '23775', '13641', '30837', '77377', '43032', '38441', '29462', '20300', '19070', '20982', '73987', '87836', '68062', '6419', '51563', '40084', '85694', '86677', '47142', '27222', '17844', '19158', '45120', '88524', '74724', '73229', '42470', '38751', '1132', '28603', '61188', '55021', '88825', '58005', '62411', '8843', '94852', '93664', '39253', '27473', '247', '43824', '1804', '8905', '11509', '95659', '7811', '80691', '15779', '49794', '8991', '76099', '29223', '36060', '85399', '41369', '22885', '38473', '22376', '50446', '89578', '25818', '61333', '78787', '47605', '83654', '99068', '52120', '48367', '86381', '19803', '72600', '31998', '37755', '88031', '83969', '42319', '27974', '35780', '93662', '46808', '60529', '15491', '10447', '48829', '33886', '68333', '44855', '86554', '64794', '66376', '58222', '14021', '52043', '56375', '1300', '38105', '89159', '97456', '26800', '93124', '3673', '32279', '30658', '84475', '3708', '93952', '39245', '91980', '55333', '79440', '64407', '46559', '60759', '10688', '49872', '45810', '87405', '66932', '56530', '57751', '9619', '27361', '6356', '65848', '7524', '20273', '22362', '20504', '28042', '39475', '51677', '85733', '32426', '54558', '17222', '56485', '34928', '90917', '70528', '51732', '61014', '98420', '67265', '41383', '3883', '47642', '53324', '93679', '93088', '57534', '44449', '46779', '81482', '54279', '80135', '11216', '92545', '18426', '96005', '57801', '21898', '5104', '83467', '72015', '43783', '89674', '57468', '96686', '95167', '38507', '95187', '64923', '71214', '42834', '93219', '47342', '24476', '84834', '29080', '86533', '30687', '68400', '26933', '37396', '65169', '89767', '20642', '53843', '85167', '77306', '46723', '68501', '4243', '35044', '15950', '40388', '53630', '76125', '10816', '83285', '4120', '11402', '91344', '95169') - - SELECT count() FROM numbers(10000) WHERE (number, number) IN ((17258, 93148), (4508, 52749), (68660, 70017), (77797, 23528), (1136, 37393), (53237, 15379), (68370, 73211), (15782, 54962), (59432, 45415), (68396, 920), (96154, 21016), (12700, 26887), (88016, 43191), (68153, 51575), (91315, 40005), (18070, 73178), (86, 631), (77717, 20324), (3227, 76188), (74960, 43147), (77538, 19628), (82292, 6525), (24293, 12566), (85244, 96287), (93982, 1329), (38064, 54723), (83999, 45810), (71921, 53673), (88638, 9669), (1959, 39535), (82235, 95796), (27907, 90975), (42383, 91015), (9948, 91514), (81712, 47309), (400, 25808), (31791, 46948), (39740, 36098), (25943, 84598), (99598, 52939), (77134, 15845), (40313, 72174), (85017, 94036), (36595, 14303), (83961, 68078), (55792, 72759), (73574, 43606), (9853, 63560), (28580, 56721), (74804, 41025), (32095, 55657), (52881, 63416), (91368, 90310), (23922, 38883), (30592, 10758), (66448, 61183), (31880, 96697), (11362, 20633), (75331, 2015), (71129, 8785), (1115, 70955), (7886, 83698), (18961, 84556), (16677, 43028), (37347, 70220), (31699, 71244), (10578, 96159), (67600, 39041), (78791, 86687), (21545, 54174), (68774, 37637), (46132, 81768), (98413, 20605), (2960, 23665), (31507, 35719), (96209, 18368), (60558, 38035), (21952, 3264), (11834, 86458), (21651, 17650), (86276, 36087), (18818, 24849), (61951, 3390), (59637, 62545), (30346, 72253), (36281, 2992), (78340, 49872), (94326, 93723), (3416, 94405), (12272, 8741), (22600, 22095), (57636, 37106), (38702, 14889), (70238, 11276), (17325, 60648), (16492, 41271), (52100, 1304), (93416, 7795), (57209, 71008), (48010, 36078), (20384, 74420), (77440, 34439), (69224, 45099), (30374, 33884), (49038, 90140), (1154, 84725), (64926, 86985), (91746, 73472), (59757, 75755), (45860, 71557), (45833, 36526), (74618, 73598), (91360, 65168), (58029, 30793), (56332, 14973), (99943, 96877), (97454, 6450), (64502, 77301), (73182, 31853), (76809, 83964), (82916, 86188), (78736, 65427), (36495, 7422), (76196, 2804), (96117, 61093), (9177, 26099), (52942, 63007), (48578, 47876), (50638, 89903), (7113, 97316), (35301, 12750), (47807, 7254), (38217, 55418), (56970, 41687), (20527, 62886), (358, 14021), (64018, 18582), (91740, 21683), (81967, 53589), (45437, 38450), (45476, 67752), (76851, 72072), (7304, 60091), (40097, 12897), (39906, 29247), (84262, 58734), (30857, 43791), (56087, 78929), (20498, 45954), (48726, 500), (62723, 43763), (28368, 30756), (74048, 52403), (15045, 95926), (75542, 55384), (52543, 22525), (56001, 6935), (11431, 46745), (77731, 7310), (36718, 59909), (32235, 91254), (92417, 25917), (21782, 79277), (46378, 87536), (35324, 26075), (6310, 76915), (1551, 69473), (50642, 68865), (55190, 72934), (49780, 21873), (99466, 29686), (90761, 13179), (72959, 57033), (20020, 90200), (46186, 79105), (73871, 52382), (59559, 38801), (59916, 16082), (33610, 94966), (46001, 45225), (86679, 26469), (77245, 91929), (32887, 36623), (11179, 46898), (87881, 68087), (45438, 47991), (24950, 94525), (91664, 51656), (43914, 47805), (15736, 96156), (56346, 20283), (85053, 48931), (17790, 26179), (96195, 55728), (43765, 54807), (44988, 89269), (55911, 99411), (52446, 47397), (28346, 65442), (96669, 68226), (66194, 26848), (37276, 55864), (14116, 41583), (18058, 16317), (93136, 85318), (35616, 86252), (29222, 29969), (33386, 85372), (71094, 44238), (27733, 31838), (64626, 16692), (52904, 97899), (97619, 12663), (50165, 4688), (67557, 44053), (69184, 66269), (73164, 89705), (39822, 15169), (65499, 72808), (30068, 63697), (30154, 64235), (97016, 58716), (94366, 36592), (1592, 16261), (87985, 52102), (12554, 23652), (15909, 25292), (2527, 91531), (92139, 36031), (28986, 30032), (3038, 56314), (32239, 26707), (15973, 34901), (70246, 39680), (82529, 38132), (45827, 74783), (53665, 64111), (55218, 84170), (20466, 16130), (55734, 71203), (31438, 96906), (66338, 85858), (35988, 68511), (78391, 15191), (80747, 59213), (5357, 11546), (16822, 16607), (36607, 41106), (74949, 30739), (45726, 64887), (1524, 54847), (37371, 89195), (28726, 27788), (22600, 44777), (53999, 63625), (84304, 98338), (49260, 76480), (74564, 53907), (89867, 97096), (60157, 61299), (17165, 10146), (56334, 36268), (62114, 49222), (22715, 23620), (42830, 11539), (41091, 69151), (75471, 68364), (18681, 43249), (42738, 63219), (35474, 98454), (76815, 46024), (66310, 36521), (86095, 77013), (63693, 77319), (80731, 63031), (95478, 92387), (23787, 63724), (46299, 68994), (4800, 2460), (9663, 80639), (77231, 85814), (81615, 11311), (35638, 27340), (13598, 14322), (30657, 17238), (90957, 96846), (69962, 52140), (41681, 65962), (96836, 58177), (36190, 11623), (4231, 40500), (43049, 41949), (71177, 98492), (30193, 39750), (19744, 33204), (63358, 30210), (45638, 58918), (43641, 38741), (35598, 40932), (33238, 36236), (50835, 20968), (25099, 34071), (84986, 88456), (35333, 1529), (79771, 23985), (647, 61658), (9424, 11743), (77766, 31528), (77811, 86973), (76403, 74377), (55568, 79251), (68858, 20762), (68520, 66773), (93598, 89823), (8080, 82539), (87760, 52247), (25191, 16905), (17837, 8339), (85177, 59050), (51680, 77374), (3287, 43018), (43479, 62141), (34909, 46322), (11869, 5885), (96193, 58417), (101, 47460), (34937, 88582), (83216, 88388), (28571, 15292), (66683, 62613), (34478, 8924), (2680, 89973), (62438, 44460), (11724, 4791), (5383, 72888), (88206, 67586), (8124, 21690), (28779, 75789), (66791, 4757), (6176, 47760), (6403, 78084), (78122, 35446), (99494, 73608), (39691, 89098), (59182, 19484), (25389, 98963), (96487, 3692), (76222, 67381), (21199, 50358), (95998, 58137), (28777, 43913), (14176, 60117), (52257, 81703), (14604, 13438), (71301, 14401), (19758, 66914), (15506, 29873), (87205, 29449), (93295, 15930), (63651, 11287), (19785, 15966), (30795, 75112), (69462, 37655), (18793, 85764), (36240, 31236), (98153, 73724), (72491, 4223), (66930, 35048), (25686, 13269), (13940, 13259), (69163, 11235), (1183, 86961), (54323, 67315), (85044, 60872), (48875, 3683), (43052, 92861), (87574, 32969), (92552, 80564), (94832, 47682), (72011, 80994), (60182, 917), (97788, 34169), (66432, 47940), (87468, 80954), (35385, 68758), (50555, 63710), (55311, 44337), (87065, 26514), (84581, 98736), (23212, 56499), (75120, 72447), (56087, 38285), (58171, 45629), (28401, 44319), (70432, 27883), (18891, 14646), (26206, 49924), (79957, 44914), (56064, 27529), (99090, 29197), (49435, 340), (53525, 65601), (76998, 88349), (50416, 70860), (42506, 75290), (34024, 13295), (86663, 46523), (88814, 231), (57809, 21), (84914, 84771), (43042, 66892), (17288, 33908), (4934, 63195), (50590, 1516), (97843, 80208), (20091, 86717), (71566, 15929), (19531, 23634), (41646, 45549), (89226, 82902), (96683, 63386), (31072, 53788), (51135, 41099), (78912, 65609), (36094, 23603), (88403, 51455), (73795, 47066), (26448, 82852), (22829, 2894), (30041, 92548), (27733, 20608), (70180, 19892), (51650, 63440), (76328, 13666), (40514, 6677), (2786, 51059), (40809, 16499), (10857, 82541), (78221, 61067), (17982, 51969), (85369, 66965), (47153, 47149), (43965, 75796), (82725, 60767), (42407, 97249), (51475, 81224), (60957, 89414), (33065, 21663), (36601, 5290), (95842, 67301), (64630, 60398), (55212, 35638), (41750, 44235), (75260, 82400), (91291, 25843), (6477, 8311), (14919, 52306), (66220, 33180), (45736, 2313), (37450, 64444), (98614, 61344), (75007, 50946), (56701, 28117), (66632, 5174), (92323, 76613), (6796, 73695), (33696, 76280), (86876, 5614), (50863, 67993), (36068, 17049), (91912, 34271), (70706, 1904), (97798, 41117), (68154, 72483), (83862, 25578), (61643, 17204), (69974, 64232), (77926, 19637), (64901, 88988), (71424, 91703), (91655, 17147), (46872, 56530), (44189, 98087), (95939, 54420), (72651, 68785), (67624, 84875), (92587, 87663), (65275, 81256), (53798, 2506), (14702, 3638), (71291, 50452), (14909, 13903), (66965, 26606), (14127, 60345), (35306, 1738), (77234, 10468), (53521, 41218), (80681, 82583), (44227, 26521), (32263, 21482), (82270, 56963), (50580, 80567), (11593, 22346), (20074, 26867), (73126, 28667), (62996, 24317), (20295, 57163), (1506, 57668), (69567, 45236), (43366, 26001), (88052, 40181), (1599, 89349), (36789, 1579), (39895, 46673), (30381, 3206), (31723, 5625), (19252, 31317), (16932, 77149), (48794, 34409), (55986, 30328), (47551, 75088), (57363, 78365), (95221, 63385), (26449, 5733), (96588, 53077), (52980, 41140), (8187, 85947), (36723, 26520), (23579, 38909), (33350, 19275), (63930, 19357), (43536, 59941), (31117, 77322), (44638, 94812), (44730, 99097), (95108, 48170), (57813, 49503), (79959, 89436), (86980, 62031), (8275, 44009), (36666, 94645), (22064, 38882), (40471, 16939), (31156, 11337), (13101, 96977), (17906, 26835), (89861, 51405), (73369, 67946), (99141, 58572), (27131, 98703), (15900, 43412), (51768, 93125), (78579, 46689), (23029, 13895), (60870, 55830), (22553, 8236), (76449, 96207), (83766, 51024), (27630, 50614), (53484, 90104), (77626, 21944), (46755, 41583), (53616, 34240), (94159, 44415), (13914, 90059), (44387, 89012), (27499, 64579), (83415, 30809), (77558, 82619), (88880, 9814), (8466, 4424), (43598, 91921), (24695, 3349), (46295, 65208), (51256, 82461), (49126, 93012), (16186, 96585), (43284, 22655), (93130, 90393), (77495, 34372), (85509, 65856), (86662, 61906), (50988, 44393), (29828, 17737), (91651, 35308), (29796, 49716), (14019, 87751), (29688, 71207), (82845, 19100), (11989, 50132), (21158, 99905), (54732, 42547), (32314, 12851), (46405, 43794), (87849, 45643), (53524, 21212), (61925, 75491), (12498, 21937), (30185, 69475), (48421, 52487), (15112, 90935), (33187, 17801), (61704, 25514), (17889, 23917), (18758, 57197), (7693, 47232), (47905, 24618), (11494, 78950), (95662, 54561), (8075, 33909), (90427, 46065), (73962, 19821), (50691, 79400), (58218, 4881), (94106, 2509), (60633, 55169), (49600, 83054), (23339, 13270), (70262, 58946), (48417, 97266), (27629, 46905), (74465, 75514), (41687, 2564), (12814, 19492), (78899, 30168), (17745, 35206), (37972, 35296), (22288, 80001), + + SELECT (rand(), rand()) IN ((17258, 93148), (4508, 52749), (68660, 70017), (77797, 23528), (1136, 37393), (53237, 15379), (68370, 73211), (15782, 54962), (59432, 45415), (68396, 920), (96154, 21016), (12700, 26887), (88016, 43191), (68153, 51575), (91315, 40005), (18070, 73178), (86, 631), (77717, 20324), (3227, 76188), (74960, 43147), (77538, 19628), (82292, 6525), (24293, 12566), (85244, 96287), (93982, 1329), (38064, 54723), (83999, 45810), (71921, 53673), (88638, 9669), (1959, 39535), (82235, 95796), (27907, 90975), (42383, 91015), (9948, 91514), (81712, 47309), (400, 25808), (31791, 46948), (39740, 36098), (25943, 84598), (99598, 52939), (77134, 15845), (40313, 72174), (85017, 94036), (36595, 14303), (83961, 68078), (55792, 72759), (73574, 43606), (9853, 63560), (28580, 56721), (74804, 41025), (32095, 55657), (52881, 63416), (91368, 90310), (23922, 38883), (30592, 10758), (66448, 61183), (31880, 96697), (11362, 20633), (75331, 2015), (71129, 8785), (1115, 70955), (7886, 83698), (18961, 84556), (16677, 43028), (37347, 70220), (31699, 71244), (10578, 96159), (67600, 39041), (78791, 86687), (21545, 54174), (68774, 37637), (46132, 81768), (98413, 20605), (2960, 23665), (31507, 35719), (96209, 18368), (60558, 38035), (21952, 3264), (11834, 86458), (21651, 17650), (86276, 36087), (18818, 24849), (61951, 3390), (59637, 62545), (30346, 72253), (36281, 2992), (78340, 49872), (94326, 93723), (3416, 94405), (12272, 8741), (22600, 22095), (57636, 37106), (38702, 14889), (70238, 11276), (17325, 60648), (16492, 41271), (52100, 1304), (93416, 7795), (57209, 71008), (48010, 36078), (20384, 74420), (77440, 34439), (69224, 45099), (30374, 33884), (49038, 90140), (1154, 84725), (64926, 86985), (91746, 73472), (59757, 75755), (45860, 71557), (45833, 36526), (74618, 73598), (91360, 65168), (58029, 30793), (56332, 14973), (99943, 96877), (97454, 6450), (64502, 77301), (73182, 31853), (76809, 83964), (82916, 86188), (78736, 65427), (36495, 7422), (76196, 2804), (96117, 61093), (9177, 26099), (52942, 63007), (48578, 47876), (50638, 89903), (7113, 97316), (35301, 12750), (47807, 7254), (38217, 55418), (56970, 41687), (20527, 62886), (358, 14021), (64018, 18582), (91740, 21683), (81967, 53589), (45437, 38450), (45476, 67752), (76851, 72072), (7304, 60091), (40097, 12897), (39906, 29247), (84262, 58734), (30857, 43791), (56087, 78929), (20498, 45954), (48726, 500), (62723, 43763), (28368, 30756), (74048, 52403), (15045, 95926), (75542, 55384), (52543, 22525), (56001, 6935), (11431, 46745), (77731, 7310), (36718, 59909), (32235, 91254), (92417, 25917), (21782, 79277), (46378, 87536), (35324, 26075), (6310, 76915), (1551, 69473), (50642, 68865), (55190, 72934), (49780, 21873), (99466, 29686), (90761, 13179), (72959, 57033), (20020, 90200), (46186, 79105), (73871, 52382), (59559, 38801), (59916, 16082), (33610, 94966), (46001, 45225), (86679, 26469), (77245, 91929), (32887, 36623), (11179, 46898), (87881, 68087), (45438, 47991), (24950, 94525), (91664, 51656), (43914, 47805), (15736, 96156), (56346, 20283), (85053, 48931), (17790, 26179), (96195, 55728), (43765, 54807), (44988, 89269), (55911, 99411), (52446, 47397), (28346, 65442), (96669, 68226), (66194, 26848), (37276, 55864), (14116, 41583), (18058, 16317), (93136, 85318), (35616, 86252), (29222, 29969), (33386, 85372), (71094, 44238), (27733, 31838), (64626, 16692), (52904, 97899), (97619, 12663), (50165, 4688), (67557, 44053), (69184, 66269), (73164, 89705), (39822, 15169), (65499, 72808), (30068, 63697), (30154, 64235), (97016, 58716), (94366, 36592), (1592, 16261), (87985, 52102), (12554, 23652), (15909, 25292), (2527, 91531), (92139, 36031), (28986, 30032), (3038, 56314), (32239, 26707), (15973, 34901), (70246, 39680), (82529, 38132), (45827, 74783), (53665, 64111), (55218, 84170), (20466, 16130), (55734, 71203), (31438, 96906), (66338, 85858), (35988, 68511), (78391, 15191), (80747, 59213), (5357, 11546), (16822, 16607), (36607, 41106), (74949, 30739), (45726, 64887), (1524, 54847), (37371, 89195), (28726, 27788), (22600, 44777), (53999, 63625), (84304, 98338), (49260, 76480), (74564, 53907), (89867, 97096), (60157, 61299), (17165, 10146), (56334, 36268), (62114, 49222), (22715, 23620), (42830, 11539), (41091, 69151), (75471, 68364), (18681, 43249), (42738, 63219), (35474, 98454), (76815, 46024), (66310, 36521), (86095, 77013), (63693, 77319), (80731, 63031), (95478, 92387), (23787, 63724), (46299, 68994), (4800, 2460), (9663, 80639), (77231, 85814), (81615, 11311), (35638, 27340), (13598, 14322), (30657, 17238), (90957, 96846), (69962, 52140), (41681, 65962), (96836, 58177), (36190, 11623), (4231, 40500), (43049, 41949), (71177, 98492), (30193, 39750), (19744, 33204), (63358, 30210), (45638, 58918), (43641, 38741), (35598, 40932), (33238, 36236), (50835, 20968), (25099, 34071), (84986, 88456), (35333, 1529), (79771, 23985), (647, 61658), (9424, 11743), (77766, 31528), (77811, 86973), (76403, 74377), (55568, 79251), (68858, 20762), (68520, 66773), (93598, 89823), (8080, 82539), (87760, 52247), (25191, 16905), (17837, 8339), (85177, 59050), (51680, 77374), (3287, 43018), (43479, 62141), (34909, 46322), (11869, 5885), (96193, 58417), (101, 47460), (34937, 88582), (83216, 88388), (28571, 15292), (66683, 62613), (34478, 8924), (2680, 89973), (62438, 44460), (11724, 4791), (5383, 72888), (88206, 67586), (8124, 21690), (28779, 75789), (66791, 4757), (6176, 47760), (6403, 78084), (78122, 35446), (99494, 73608), (39691, 89098), (59182, 19484), (25389, 98963), (96487, 3692), (76222, 67381), (21199, 50358), (95998, 58137), (28777, 43913), (14176, 60117), (52257, 81703), (14604, 13438), (71301, 14401), (19758, 66914), (15506, 29873), (87205, 29449), (93295, 15930), (63651, 11287), (19785, 15966), (30795, 75112), (69462, 37655), (18793, 85764), (36240, 31236), (98153, 73724), (72491, 4223), (66930, 35048), (25686, 13269), (13940, 13259), (69163, 11235), (1183, 86961), (54323, 67315), (85044, 60872), (48875, 3683), (43052, 92861), (87574, 32969), (92552, 80564), (94832, 47682), (72011, 80994), (60182, 917), (97788, 34169), (66432, 47940), (87468, 80954), (35385, 68758), (50555, 63710), (55311, 44337), (87065, 26514), (84581, 98736), (23212, 56499), (75120, 72447), (56087, 38285), (58171, 45629), (28401, 44319), (70432, 27883), (18891, 14646), (26206, 49924), (79957, 44914), (56064, 27529), (99090, 29197), (49435, 340), (53525, 65601), (76998, 88349), (50416, 70860), (42506, 75290), (34024, 13295), (86663, 46523), (88814, 231), (57809, 21), (84914, 84771), (43042, 66892), (17288, 33908), (4934, 63195), (50590, 1516), (97843, 80208), (20091, 86717), (71566, 15929), (19531, 23634), (41646, 45549), (89226, 82902), (96683, 63386), (31072, 53788), (51135, 41099), (78912, 65609), (36094, 23603), (88403, 51455), (73795, 47066), (26448, 82852), (22829, 2894), (30041, 92548), (27733, 20608), (70180, 19892), (51650, 63440), (76328, 13666), (40514, 6677), (2786, 51059), (40809, 16499), (10857, 82541), (78221, 61067), (17982, 51969), (85369, 66965), (47153, 47149), (43965, 75796), (82725, 60767), (42407, 97249), (51475, 81224), (60957, 89414), (33065, 21663), (36601, 5290), (95842, 67301), (64630, 60398), (55212, 35638), (41750, 44235), (75260, 82400), (91291, 25843), (6477, 8311), (14919, 52306), (66220, 33180), (45736, 2313), (37450, 64444), (98614, 61344), (75007, 50946), (56701, 28117), (66632, 5174), (92323, 76613), (6796, 73695), (33696, 76280), (86876, 5614), (50863, 67993), (36068, 17049), (91912, 34271), (70706, 1904), (97798, 41117), (68154, 72483), (83862, 25578), (61643, 17204), (69974, 64232), (77926, 19637), (64901, 88988), (71424, 91703), (91655, 17147), (46872, 56530), (44189, 98087), (95939, 54420), (72651, 68785), (67624, 84875), (92587, 87663), (65275, 81256), (53798, 2506), (14702, 3638), (71291, 50452), (14909, 13903), (66965, 26606), (14127, 60345), (35306, 1738), (77234, 10468), (53521, 41218), (80681, 82583), (44227, 26521), (32263, 21482), (82270, 56963), (50580, 80567), (11593, 22346), (20074, 26867), (73126, 28667), (62996, 24317), (20295, 57163), (1506, 57668), (69567, 45236), (43366, 26001), (88052, 40181), (1599, 89349), (36789, 1579), (39895, 46673), (30381, 3206), (31723, 5625), (19252, 31317), (16932, 77149), (48794, 34409), (55986, 30328), (47551, 75088), (57363, 78365), (95221, 63385), (26449, 5733), (96588, 53077), (52980, 41140), (8187, 85947), (36723, 26520), (23579, 38909), (33350, 19275), (63930, 19357), (43536, 59941), (31117, 77322), (44638, 94812), (44730, 99097), (95108, 48170), (57813, 49503), (79959, 89436), (86980, 62031), (8275, 44009), (36666, 94645), (22064, 38882), (40471, 16939), (31156, 11337), (13101, 96977), (17906, 26835), (89861, 51405), (73369, 67946), (99141, 58572), (27131, 98703), (15900, 43412), (51768, 93125), (78579, 46689), (23029, 13895), (60870, 55830), (22553, 8236), (76449, 96207), (83766, 51024), (27630, 50614), (53484, 90104), (77626, 21944), (46755, 41583), (53616, 34240), (94159, 44415), (13914, 90059), (44387, 89012), (27499, 64579), (83415, 30809), (77558, 82619), (88880, 9814), (8466, 4424), (43598, 91921), (24695, 3349), (46295, 65208), (51256, 82461), (49126, 93012), (16186, 96585), (43284, 22655), (93130, 90393), (77495, 34372), (85509, 65856), (86662, 61906), (50988, 44393), (29828, 17737), (91651, 35308), (29796, 49716), (14019, 87751), (29688, 71207), (82845, 19100), (11989, 50132), (21158, 99905), (54732, 42547), (32314, 12851), (46405, 43794), (87849, 45643), (53524, 21212), (61925, 75491), (12498, 21937), (30185, 69475), (48421, 52487), (15112, 90935), (33187, 17801), (61704, 25514), (17889, 23917), (18758, 57197), (7693, 47232), (47905, 24618), (11494, 78950), (95662, 54561), (8075, 33909), (90427, 46065), (73962, 19821), (50691, 79400), (58218, 4881), (94106, 2509), (60633, 55169), (49600, 83054), (23339, 13270), (70262, 58946), (48417, 97266), (27629, 46905), (74465, 75514), (41687, 2564), (12814, 19492), (78899, 30168), (17745, 35206), (37972, 35296), (22288, 80001), (68026, 36558), (40187, 12234), (92380, 22866), (56488, 64402), (41404, 62562), (47802, 45287), (83302, 85215), (58999, 85776), (35158, 16804), (13416, 94146), (62953, 28243), (83290, 19103), (4564, 21789), (64468, 20927), (25582, 47206), (57810, 18693), (28938, 97986), (61704, 14838), (19214, 3232), (12911, 25438), (85802, 28837), (56506, 89458), (66392, 47773), (68190, 43841), (43044, 52214), (57886, 32830), (15943, 59771), (37081, 89294), (4032, 32960), (46931, 85790), (69656, 72737), (28217, 39872), (86170, 42776), (55116, 51495), (90485, 45274), (60773, 36788), (2193, 2636), (70222, 62086), (75720, 70712), (17549, 51460), (23609, 31515), (70254, 39825), (63762, 11061), (13107, 15394), (45916, 72130), (91558, 86662), (99524, 69106), (93073, 29881), (31724, 3007), (69051, 59452), (59701, 86760), (4967, 82028), (57404, 48226), (71829, 79910), (23714, 62439), (73881, 67618), (63269, 40085), (6164, 23415), (48156, 93907), (18627, 16570), (6676, 22991), (36916, 41488), (99079, 13264), (32533, 99243), (55505, 63339), (89564, 3290), (24886, 34916), (91310, 9343), (49779, 12740), (26320, 3406), (57661, 5702), (10765, 57881), (5518, 47638), (93148, 27438), (73451, 24477), (84075, 96822), (58883, 58883), (96812, 82388), (30659, 59654), (24498, 95808), (25591, 21834), (13090, 87704), (76495, 17249), (75975, 84318), (55459, 70426), (84256, 88604), (79438, 43104), (45331, 7495), (63619, 11123), (24772, 2601), (63343, 14138), (39957, 98339), (55595, 17823), (97676, 53933), (91867, 25023), (64677, 67859), (43737, 34315), (24800, 53968), (93157, 17507), (24264, 35273), (33889, 507), (10207, 40542), (40213, 57800), (38321, 74160), (42391, 7651), (80267, 94736), (52473, 79634), (17075, 2531), (8595, 75890), (31496, 50367), (16069, 79896), (70067, 200), (23420, 49517), (1628, 45646), (8916, 36794), (72294, 88976), (40603, 86008), (91871, 71098), (5447, 70998), (24152, 17561), (65046, 34951), (56950, 9292), (19244, 31385), (74693, 31813), (97343, 21572), (38834, 135), (79717, 62486), (38, 10308), (58035, 71344), (85802, 81079), (5943, 156), (38735, 38867), (3803, 99366), (15853, 19408), (62988, 62008), (8316, 44684), (17035, 71012), (48584, 2117), (75425, 37336), (2405, 50420), (43653, 28836), (12394, 69430), (54522, 4954), (33359, 148), (41018, 82851), (79995, 55417), (65008, 32342), (36547, 88185), (8131, 7054), (38980, 20146), (27976, 63039), (53119, 67009), (40043, 98393), (29333, 51980), (85818, 98405), (77956, 20099), (99747, 16916), (11597, 50181), (40961, 8262), (75103, 13912), (62339, 69155), (3869, 85481), (7053, 30956), (33563, 53272), (96178, 81751), (99365, 88728), (34447, 11164), (62856, 30939), (92486, 3357), (56605, 35330), (42180, 15137), (83946, 62984), (61869, 55711), (52880, 49871), (44588, 27387), (16332, 24496), (1781, 13508), (56674, 95773), (21328, 19628), (96455, 24155), (14302, 74435), (54053, 24590), (86642, 22177), (24089, 16186), (70281, 4601), (18552, 70708), (95442, 5895), (96714, 6293), (43803, 45857), (93257, 18497), (90032, 85086), (40566, 87233), (32674, 73822), (95599, 49334), (62745, 51898), (8245, 93882), (14093, 40977), (47215, 53001), (59737, 68452), (90937, 25354), (43805, 82571), (81953, 68572), (37298, 96262), (94899, 65066), (34772, 80762), (55469, 1186), (8734, 91665), (18622, 51150), (85200, 39575), (65381, 15979), (89734, 89656), (64712, 53691), (87187, 58256), (8476, 89694), (49935, 35239), (63730, 34982), (27687, 91571), (87543, 15350), (85208, 18781), (14783, 2574), (44699, 666), (56440, 87617), (32732, 49301), (76725, 3895), (10419, 90580), (34725, 69476), (14831, 81588), (93924, 38057), (38528, 99060), (57136, 44206), (74685, 99559), (43083, 87511), (43105, 35474), (35582, 17560), (5578, 98727), (78947, 53865), (32013, 95029), (61552, 42674), (52191, 49975), (71566, 16403), (78534, 16350), (18520, 80501), (29114, 46547), (11488, 5069), (89591, 82384), (13741, 42318), (74385, 58849), (49739, 63421), (83821, 6676), (51997, 93321), (36677, 81768), (37915, 73495), (47175, 6086), (39989, 83110), (6489, 48112), (88822, 20370), (12846, 13952), (28930, 20879), (25139, 84552), (76434, 2665), (55145, 31523), (21177, 18630), (81077, 96275), (61006, 30845), (77722, 62651), (61181, 72545), (93838, 84287), (59300, 19014), (75076, 97980), (76979, 1473), (48409, 13097), (51718, 5325), (36522, 72119), (60917, 18995), (61469, 42853), (34387, 37322), (38684, 28120), (64136, 8559), (15368, 99424), (97824, 7864), (33833, 72029), (7024, 9961), (49400, 66220), (63025, 97179), (6135, 98878), (19873, 8438), (3963, 35670), (65186, 89423), (26653, 65943), (83132, 67000), (82578, 35007), (42680, 60479), (71102, 98589), (74842, 94010), (22931, 33725), (46537, 42629), (75793, 48115), (21630, 92454), (97993, 81332), (25747, 31814), (91231, 65953), (91981, 12219), (64719, 16254), (60914, 8334), (15887, 96432), (42110, 28837), (7295, 83147), (50334, 7053), (3949, 33594), (1524, 98230), (17265, 98024), (75969, 36232), (89538, 5212), (13444, 55946), (69823, 81848), (32578, 74024), (52018, 98290), (59118, 40186), (61002, 16977), (69537, 44780), (92, 13937), (33715, 42663), (46347, 8312), (86196, 59301), (17128, 85014), (26429, 57682), (45888, 99588), (22750, 96110), (46809, 49251), (24521, 40071), (287, 22115), (11741, 36315), (22742, 17581), (35808, 3110), (98904, 30407), (4584, 13383), (28585, 69669), (94823, 29715), (9551, 36389), (77997, 45746), (49894, 55722), (23415, 69459), (58246, 85144), (74136, 18102), (97366, 85724), (34271, 51601), (47535, 70883), (59443, 90103), (45213, 45811), (62741, 86898), (17324, 50034), (62080, 25193), (89524, 4421), (13476, 51456), (69198, 56718), (58024, 22969), (65210, 67941), (32561, 44881), (62295, 67448), (66135, 95453), (9417, 20443), (82486, 23745), (19185, 99041), (40662, 91714), (3423, 58624), (4512, 74502), (67772, 98023), (69575, 75779), (69107, 62805), (517, 33801), (47406, 7581), (81108, 10546), (12976, 47001), (16742, 83811), (44593, 82124), (52731, 34642), (81725, 20555), (94126, 91919), (24800, 59302), (97253, 39249), (71692, 10769), (88721, 56321), (7019, 69771), (31464, 61774), (29597, 19263), (65557, 31875), (28653, 69636), (58074, 76848), (15906, 80620), (18259, 40193), (99991, 4769), (98935, 99269), (12123, 60124), (20787, 47346), (13526, 33592), (95370, 40350), (17479, 42884), (58368, 83218), (63290, 74406), (97030, 35102), (45298, 27660), (64593, 21262), (76268, 82641), (1107, 44044), (21427, 79959), (85180, 62412), (7359, 1318), (83618, 9762), (1425, 55804), (32874, 97943), (68191, 38742), (41715, 17902), (3771, 15032), (7848, 74950), (33881, 40904), (75295, 26151), (75775, 13760), (90262, 89822), (88169, 18679), (57506, 32356), (94983, 44281), (37385, 37432), (18248, 48162), (45573, 66278), (25277, 72788), (26977, 36778), (26254, 61758), (12860, 48026), (96819, 3339), (13134, 1173), (26822, 53374), (15989, 29698), (11258, 54515), (37866, 34928), (22996, 26577), (39952, 42732), (6754, 70595), (86245, 44669), (47044, 34170), (6789, 45220), (31706, 2090), (42582, 40023), (35147, 46591), (88210, 11307), (53644, 7680), (11280, 91075), (42961, 65122), (40066, 52185), (20050, 6154), (98440, 20393), (88992, 75432), (32386, 66731), (36952, 34149), (18453, 32715), (84413, 10378), (59440, 2374), (45354, 85009), (50382, 66510), (64428, 95401), (9336, 41760), (26317, 91416), (81941, 99504), (26600, 53522), (81069, 40236), (51126, 27911), (97144, 14243), (62738, 50287), (37372, 28962), (12053, 9090), (69492, 95524), (68141, 52931), (17276, 16487), (69227, 25949), (14143, 70193), (7077, 53032), (65463, 74082), (94997, 66496), (80443, 55832), (66796, 5970), (15852, 95662), (81559, 97272), (55851, 18977), (91142, 48976), (91143, 950), (79225, 31004), (61310, 20760), (74541, 90842), (80322, 11630), (84631, 544), (66785, 86591), (25650, 63252), (59635, 18586), (2964, 6741), (37091, 71148), (11984, 43077), (87505, 62049), (61925, 92290), (18808, 3937), (8300, 33268), (70850, 50661), (86024, 73730), (85161, 47116), (50193, 89155), (37773, 40845), (9251, 41688), (6940, 65399), (42479, 95630), (19401, 43102), (48069, 36040), (62760, 95013), (394, 2641), (32567, 29306), (13870, 58835), (98248, 47291), (49803, 4523), (40222, 12883), (53576, 73105), (88265, 23629), (67865, 67875), (33473, 27144), (80219, 53893), (74878, 47341), (78070, 84803), (30003, 5600), (41103, 6145), (83490, 81076), (55059, 66736), (45015, 10239), (79555, 85819), (81808, 34970), (19235, 85480), (91807, 52177), (40887, 87009), (5003, 2687), (64964, 88122), (765, 94893), (93573, 20504), (28854, 38438), (94244, 93475), (72996, 84801), (75427, 81692), (63161, 98637), (18814, 61343), (22863, 60110), (8949, 12694), (19675, 94313), (43857, 74073), (15737, 58218), (48895, 68474), (22220, 92926), (69055, 50282), (40532, 74934), (59062, 66405), (85784, 87704), (58494, 88222), (2260, 20401), (73112, 99666), (46739, 95433), (21179, 85119), (11545, 38801), (59993, 50866), (10086, 4709), (70560, 29611), (27095, 89017), (6896, 2279), (92506, 5013), (48600, 90491), (18782, 54638), (54337, 82734), (52054, 13481), (38297, 56559), (15998, 30591), (89789, 7522), (18149, 28725), (3532, 28625), (70934, 49617), (84599, 55664), (74229, 52269), (55431, 11893), (32807, 72543), (83882, 53025), (11490, 83442), (14844, 88612), (12526, 45953), (906, 2231), (68240, 95612), (18818, 31535), (57774, 91290), (67250, 67400), (77332, 23550), (42332, 57775), (28792, 11539), (19108, 34608), (12399, 38591), (7329, 10740), (84288, 50928), (29461, 17629), (63884, 88489), (47479, 61085), (75357, 57255), (60107, 94046), (32934, 66312), (28615, 42600), (55553, 85213), (57838, 91426), (9783, 11513), (73677, 28821), (75408, 75561), (22995, 59224), (74874, 54145), (18513, 75901), (46440, 69414), (36072, 22263), (60560, 73325), (69967, 93358), (75949, 98634), (3688, 57991), (43482, 94541), (40922, 31011), (57763, 74497), (93576, 96392), (83038, 80656), (47757, 87045), (14061, 53465), (65619, 33775), (11341, 6702), (6249, 87358), (15766, 85937), (13135, 93945), (24495, 95900), (80359, 1739), (15468, 73426), (49240, 44999), (82839, 90808), (87438, 75613), (348, 73144), (99523, 85853), (21557, 70210), (64933, 1672), (38154, 17477), (97136, 67363), (96491, 8038), (97981, 3434), (54372, 27038), (88480, 86675), (21028, 21083), (43197, 4440), (31702, 78290), (66631, 24438), (11482, 17922), (90351, 39503), (46186, 32439), (73828, 6640), (56916, 26029), (62840, 1815), (20281, 28488), (18211, 30043), (65211, 93012), (43614, 58012), (90322, 77343), (64293, 94525), (59489, 39760), (93219, 78440), (74613, 9732), (38085, 19191), (58029, 48186), (88762, 1764), (28627, 21993), (49975, 41225), (70486, 43480), (82764, 96425), (27218, 78327), (17844, 73333), (70463, 37629), (10500, 33826), (97343, 66575), (82833, 51210), (77353, 45073), (27163, 39728), (78076, 46691), (80302, 39342), (77142, 1319), (87403, 80110), (53805, 27786), (50558, 74264), (83146, 31358), (11567, 4438), (30041, 54287), (91731, 18496), (57591, 93894), (72534, 59009), (98064, 59148), (69626, 66615), (20951, 43949), (61960, 68060), (48892, 67918), (61321, 56222), (75424, 77260), (4916, 81929), (68892, 81531), (28096, 28548), (62016, 107), (8593, 12030), (66743, 36772), (60174, 15106), (52844, 1923), (34768, 22065), (88988, 62910), (79214, 2998), (25675, 31376), (69959, 3614), (43885, 31708), (12206, 46548), (69924, 19343), (12984, 38980), (58250, 69438), (2580, 48684), (38112, 37124), (21842, 43150), (59384, 21921), (19908, 46678), (73396, 79529), (8274, 1557), (36975, 65519), (81069, 18712), (13692, 9148), (60617, 84762), (75749, 66154), (80375, 24553), (4257, 47056), (76880, 7687), (40714, 43448), (79112, 74791), (33119, 72730), (17670, 89183), (51614, 3921), (21247, 39857), (86756, 67673), (32792, 70035), (5917, 7197), (1762, 23130), (6455, 63664), (32806, 3729), (60469, 20511), (12522, 15149), (98106, 79338), (84754, 11162), (52058, 17973), (28789, 1521), (32766, 36325), (78914, 40453), (70297, 71854), (9313, 45190), (54559, 66227), (22342, 43860), (44152, 84294), (36913, 93173), (88523, 36338), (82234, 71140), (8328, 22947), (73250, 88125), (74356, 16820), (94472, 37349), (23126, 87806), (40315, 88729), (19935, 19145), (93312, 65719), (8477, 33108), (86660, 69525), (75557, 66964), (60437, 57494), (94419, 42524), (95372, 72274), (49866, 85685), (96808, 39404), (62961, 72507), (38634, 70815), (91379, 42430), (66359, 98699), (24382, 4186), (4003, 77760), (87840, 75265), (57641, 68871), (9773, 15942), (5664, 51289), (47923, 31308), (58632, 82468), (14097, 71829), (1838, 97710), (70433, 11364), (82363, 97879), (25257, 20615), (18249, 6758), (98581, 13639), (3290, 72449), (74546, 79380), (97254, 44448), (80316, 31760), (40516, 94809), (14444, 88981), (9693, 10259), (83795, 95485), (70201, 81014), (66644, 16761), (35529, 82718), (75774, 73476), (80139, 3957), (34803, 80689), (92085, 46499), (97871, 8004), (67369, 11354), (43578, 81596), (94695, 44963), (93741, 41629), (16005, 96652), (37918, 69012), (89832, 56041), (51798, 32386), (89749, 27647), (76279, 7990), (31746, 1346), (40841, 20480), (99942, 24473), (78495, 99194), (13588, 57088), (22183, 42297), (82707, 34435), (45026, 12747), (8000, 93211), (40453, 13025), (44100, 39880), (83900, 56474), (87691, 42802), (82000, 63867), (76627, 84731), (112, 92774), (34749, 97737), (59262, 57169), (95571, 44144), (74310, 68970), (63232, 92744), (53698, 21610), (39969, 75475), (39942, 28713), (81230, 50140), (97953, 96528), (86144, 48041), (96677, 49677), (22051, 48183), (33436, 54784), (5553, 11920), (67057, 17115), (57736, 72309), (8086, 85329), (72623, 94949), (13096, 48992), (63153, 56337), (30462, 1036), (75000, 24048), (62635, 50128), (91480, 83131), (25929, 79809), (96237, 76974), (59645, 20603), (31850, 29754), (91070, 36568), (30191, 33785), (86776, 67259), (49073, 39179), (16121, 73834), (84217, 52951), (95866, 47244), (63326, 73460), (134, 91953), (48189, 86069), (42971, 3700), (28643, 10479), (80151, 7446), (78798, 2655), (39135, 69364), (80244, 24904), (22818, 74964), (26753, 82419), (16858, 5212), (79891, 11215), (785, 46103), (12559, 24617), (73601, 71490), (70342, 7099), (73330, 6665), (11903, 28194), (16375, 37746), (86132, 51788), (90345, 68366), (5464, 78338), (23569, 83141), (17904, 94046), (35868, 60017), (22591, 93373), (70584, 72116), (49331, 34312), (16180, 91286), (58494, 65441), (9336, 52671), (32523, 26734), (40205, 83549), (28810, 96876), (44703, 38944), (46981, 37157), (8582, 7529), (59718, 71700), (62545, 73716), (6531, 23200), (30528, 59720), (57152, 84660), (16232, 67946), (60446, 45983), (68737, 54959), (57795, 73107), (26930, 35938), (9844, 44760), (3716, 79020), (99126, 8264), (66120, 16151), (50616, 25765), (93340, 95875), (34103, 88003), (14879, 99758), (49188, 6087), (89858, 42861), (36730, 72076), (25069, 26403), (98183, 48108), (3229, 5367), (59306, 80078), (61144, 58598), (72600, 98765), (57701, 23177), (10176, 11553), (82964, 13697), (7788, 28538), (39943, 97491), (56261, 17781), (2458, 1892), (6679, 45554), (42171, 66222), (24420, 44115), (35852, 41965), (50196, 49555), (34718, 60734), (6932, 61638), (69472, 56723), (489, 97620), (41335, 90578), (1333, 92787), (97883, 64754), (14208, 22097), (75776, 5938), (67446, 61518), (58743, 45162), (34749, 81243), (71451, 91991), (27804, 41836), (45274, 8039), (17593, 24498), (8801, 38559), (87460, 7109), (50075, 18284), (84043, 82146), (62932, 25018), (89647, 56768), (59920, 80801), (56357, 35142), (97376, 58181), (70715, 91103), (90829, 78985), (29776, 13275), (30546, 42320), (99266, 35340), (21234, 61062), (39239, 10745), (45990, 715), (47047, 6619), (4270, 94575), (90009, 72203), (25629, 2691), (67926, 89112), (46990, 61101), (22355, 69536), (1977, 56723), (54681, 34041), (83819, 7024), (81235, 7093), (16659, 87135), (49384, 32135), (42204, 17362), (90585, 70374), (51255, 1), (31600, 70085), (90189, 95778), (57349, 87789), (83384, 93771), (20718, 15529), (10644, 53591), (84103, 62101), (91340, 48382), (82854, 84420), (12561, 53517), (64835, 45362), (54154, 75841), (46498, 31175), (75035, 49552), (9306, 53403), (68851, 49139), (13463, 42107), (2322, 36695), (55953, 12098), (60656, 80482), (78987, 19632), (31228, 18523), (98972, 80489), (32367, 98405), (25139, 5164), (5692, 60610), (36535, 70097), (80542, 74320), (87984, 46750), (98201, 41341), (35217, 46813), (81795, 69057), (83927, 41032), (60149, 26087), (39954, 48361), (64485, 61448), (87185, 14580), (74559, 93251), (88544, 83366), (74015, 15864), (78623, 69719), (16941, 80710), (16315, 58313), (47277, 59107), (16067, 66290), (63906, 59891), (20754, 67817), (44428, 10652), (95960, 99045), (52163, 26221), (65566, 22057), (26836, 38898), (57107, 79274), (39020, 74857), (53540, 84159), (76646, 44324), (27967, 40171), (28710, 56332), (84036, 28711), (68742, 57241), (40535, 34737), (62681, 85386), (30472, 58405), (85086, 33013), (67059, 47481), (30441, 55098), (97892, 71991), (90296, 42905), (22441, 18863), (19606, 77242), (11206, 58380), (23901, 49962), (84094, 33761), (64400, 28093), (64228, 94543), (71874, 20871), (25385, 73117), (63398, 20999), (77547, 51893), (80783, 65858), (39807, 80754), (10336, 90318), (7826, 55346), (30206, 10711), (94411, 67364), (33509, 14329), (65350, 17006), (65999, 55699), (82753, 61081), (38851, 11896), (15155, 48635), (19985, 75204), (37144, 5344), (26173, 39587), (61111, 30966), (16180, 22987), (60707, 43599), (30136, 74118), (7880, 43857), (97445, 30233), (62700, 24828), (90914, 89452), (64131, 56925), (25259, 39132), (47104, 43950), (93891, 21913), (84573, 91029), (8604, 79858), (33141, 25534), (12468, 90413), (97063, 76359), (80826, 26061), (64013, 99099), (82158, 38882), (25799, 7564), (25477, 69847), (73374, 58520), (48230, 9453), (91424, 72273), (64893, 11750), (46753, 48434), (15974, 94633), (14872, 27027), (14527, 21313), (25660, 64644), (54196, 15138), (6313, 10911), (36168, 47170), (45346, 76), (10305, 60286), (65283, 39977), (21804, 37972), (65389, 86954), (90674, 64458), (15838, 22392), (43540, 42503), (49584, 67828), (56711, 87887), (40075, 73696), (23832, 91552), (39002, 65562), (20451, 64664), (70783, 92171), (29319, 57694), (56217, 44247), (52856, 57873), (80560, 90902), (31068, 11280), (46996, 34739), (57527, 4554), (8410, 25816), (12269, 38319), (88054, 49939), (337, 13231), (56432, 68236), (74841, 21476), (96006, 15712), (87145, 91660), (58090, 55111), (10310, 79789), (5734, 79710), (98992, 69026), (77033, 5734), (43338, 42635), (23898, 28669), (62708, 81652), (41279, 51722), (93444, 26355), (62046, 52199), (71492, 58736), (7379, 62581), (8592, 71885), (75026, 40387), (46696, 3939), (9787, 88907), (86356, 363), (97479, 20358), (77363, 65553), (44036, 22178), (98279, 64612), (3615, 411), (77003, 93018), (41605, 88489), (55992, 83614), (19493, 21633), (34639, 97064), (94602, 89289), (45853, 26299), (84170, 73386), (9221, 51439), (41513, 68166), (37170, 17690), (82511, 59246), (96674, 27574), (99301, 45675), (42716, 41520), (56623, 49130), (84100, 76804), (73855, 97007), (73303, 26912), (37151, 23837), (49190, 97104), (23487, 45628), (87763, 46550), (65111, 92605), (80481, 8151), (83949, 18930), (81749, 27244), (37449, 3023), (28303, 51545), (96441, 93242), (22082, 43254), (35135, 68407), (37712, 48709), (5111, 26774), (15532, 74246), (93605, 83583), (21491, 66472), (38922, 53076), (55455, 54432), (955, 44063), (311, 91630), (53554, 4522), (29927, 65668), (7525, 16035), (44093, 76745), (21481, 78198), (76875, 5306), (56126, 76437), (96534, 16880), (85600, 68336), (4479, 81002), (80414, 11593), (8186, 61147), (5624, 32879), (79312, 20995), (40407, 41512), (91261, 66022), (93228, 75364), (21136, 40111), (92148, 60681), (42549, 7944), (60157, 15040), (63562, 88365), (69056, 72713), (78263, 89223), (3776, 33039), (30042, 59984), (64567, 20977), (24720, 39157), (63582, 75653), (45363, 20249), (58093, 53833), (27918, 93306), (25791, 92686), (15904, 862), (72093, 19257), (64125, 88986), (41717, 27989), (43165, 98675), (76840, 48170), (64508, 3535), (91964, 33435), (96686, 88673), (66648, 64594), (17927, 30539), (73615, 22800), (18580, 48077), (59803, 48202), (76805, 89886), (2744, 52965), (55596, 22519), (35358, 11629), (83029, 80047), (36120, 91930), (26066, 23035), (48857, 14268), (63020, 26197), (60623, 23252), (34911, 72754), (34808, 21593), (64067, 58963), (34509, 8739), (52686, 96405), (98282, 10463), (6495, 64680), (59016, 86968), (33928, 51222), (39609, 84992), (67603, 89875), (14723, 16144), (30751, 46856), (76874, 75024), (89584, 58806), (51278, 4113), (27187, 93483), (80039, 52159), (6132, 25127), (42358, 77498), (33838, 79064), (74147, 76851), (39752, 27366), (44888, 9809), (10887, 4135), (22303, 36417), (58690, 34613), (53998, 74014), (71567, 32438), (65110, 93406), (77365, 41299), (18044, 70636), (77346, 21236), (78408, 245), (57704, 34662), (75258, 64730), (96992, 15533), (56010, 60769), (69163, 4826), (88709, 20725), (33197, 69743), (97169, 83194), (75277, 53343), (14531, 64740), (19997, 4752), (74016, 55946), (55290, 63626), (32533, 32920), (32946, 74610), (12386, 33853), (34825, 35374), (28772, 32716), (17280, 42683), (54184, 34332), (29964, 16203), (65767, 61448), (29133, 35728), (6861, 14160), (65483, 40224), (78335, 76002), (3061, 40615), (11780, 87517), (46135, 73448), (10920, 72592), (15696, 28810), (44154, 64134), (59365, 27248), (76601, 39862), (68264, 30019), (48572, 54575), (59499, 85796), (35064, 23789), (57028, 83545), (33911, 8463), (21827, 67966), (15983, 69649), (13919, 20584), (82742, 67956), (75457, 45767), (55394, 62309), (6099, 67510), (58078, 9594), (24511, 83149), (24781, 79624), (39745, 777), (92023, 40085), (22889, 37179), (17919, 28607), (79865, 72682), (99829, 38190), (21273, 21278), (88299, 23433), (88887, 48163), (62993, 61567), (82107, 84224), (65049, 61245), (75113, 93564), (81562, 7874), (32314, 32313), (3979, 46996), (40558, 93278), (58758, 68163), (40502, 58941), (76961, 65762), (48032, 36117), (64712, 9137), (12092, 56665), (12315, 66581), (20954, 29083), (57317, 48290), (23534, 86828), (4869, 35950), (26993, 24840), (93007, 45049), (18009, 20350), (43053, 71248), (47320, 66119), (50898, 96627), (669, 40018), (89236, 44039), (47375, 63306), (61906, 6658), (2672, 84546), (59778, 72319), (14497, 71952), (42420, 87023), (96465, 46140), (32857, 22772), (4985, 35125), (61918, 28016), (90275, 24406), (49799, 10811), (74137, 63345), (26135, 86306), (92971, 65541), (40134, 95892), (38554, 46307), (48113, 16343), (63990, 66283), (17793, 49570), (21736, 79819), (13831, 27523), (8939, 93929), (96577, 4909), (38583, 32781), (13701, 24436), (43444, 56054), (17166, 32346), (57202, 26264), (82858, 75049), (46317, 95666), (54911, 68161), (3894, 38521), (26456, 30270), (65214, 35331), (41143, 13109), (85441, 48899), (93226, 25027), (77045, 81171), (30345, 79232), (71167, 40854), (58761, 56824), (89047, 85314), (31686, 81947), (74946, 60661), (49903, 13625), (76341, 69067), (46963, 88891), (97223, 5921), (52143, 9828), (17413, 42731), (30236, 93426), (14540, 17652), (52251, 97233), (41581, 30097), (28771, 46426), (36260, 45179), (4068, 16410), (3146, 95055), (5993, 88855), (46103, 30022), (26667, 18756), (54576, 13438), (12800, 11258), (80761, 44979), (59811, 76627), (77917, 87270), (46286, 28657), (30609, 86852), (15200, 28936), (86331, 34195), (98461, 55054), (91760, 62792), (91551, 70192), (96030, 78205), (8254, 27057), (600, 37830), (58635, 65506), (81661, 73708), (11225, 24255), (15830, 9029), (84384, 46190), (31344, 25765), (25670, 30716), (88507, 19484), (28207, 45941), (91874, 15786), (10094, 10934), (38013, 2179), (14558, 36415), (65079, 48850), (65486, 85046), (54958, 60275), (99800, 96623), (68895, 99829), (3708, 75830), (96368, 22631), (99411, 50094), (56888, 3883), (87288, 45604), (64512, 84543), (45565, 14170), (77114, 15132), (31800, 70333), (57775, 40548), (31788, 67511), (51929, 13684), (53736, 81543), (84251, 86303), (63823, 83258), (77539, 61381), (43570, 39418), (79859, 34773), (8595, 64524), (97242, 9283), (15530, 84591), (75535, 65546), (16516, 50162), (58815, 1815), (34897, 82920), (66215, 81262), (81487, 4902), (64039, 25703), (78006, 90468), (3081, 26910), (58159, 4777), (73715, 36375), (69189, 60971), (18169, 39587), (57960, 57668), (6582, 63707), (11155, 47930), (70829, 92266), (6294, 92305), (2188, 6419), (17141, 54972), (60240, 35276), (10788, 29414), (17464, 76377), (3994, 17227), (12039, 24992), (1340, 77467), (1212, 41758), (52186, 80763), (970, 78819), (92897, 68714), (6349, 77016), (22069, 77732), (78209, 72708), (71986, 56770), (8580, 87225), (97505, 63546), (67459, 39771), (50707, 57066), (68226, 54176), (65425, 27407), (57723, 19288), (56974, 90449), (55878, 1264), (46939, 79863), (34868, 4652), (39872, 78482), (92657, 20961), (99690, 28825), (33761, 52922), (73738, 64995), (92092, 3237), (2463, 45045), (43984, 69864), (60146, 5333), (58127, 79082), (84395, 73949), (50818, 68457), (48585, 47420), (60878, 67337), (16573, 30621), (46524, 14168), (87995, 44854), (73143, 77177), (33967, 37276), (95038, 17670), (69022, 16038), (58485, 90526), (1705, 1443), (97969, 40011), (14719, 42770), (8695, 27192), (47546, 51349), (75263, 24419), (25420, 66286), (39198, 41401), (77896, 85583), (28265, 76766), (88836, 48759), (47768, 39582), (65103, 3167), (92171, 85360), (1549, 79296), (71725, 16645), (87349, 29290), (66201, 61712), (43525, 70338), (99025, 63090), (3687, 79963), (63600, 92088), (2480, 1359), (31384, 63603), (29650, 24391), (8552, 82260), (16729, 29139), (26503, 4767), (88945, 19824), (66695, 95696), (84016, 35417), (71521, 22206), (88433, 55606), (66380, 81316), (30573, 36000), (85223, 20494), (99672, 82813), (65500, 78258), (55817, 98414), (43248, 53800), (62787, 21018), (48981, 36258), (41216, 98585), (18576, 18004), (27272, 72860), (76774, 87664), (26737, 11514), (24472, 42538), (5860, 81355), (29066, 10012), (75308, 28561), (23609, 10007), (10007, 19146), (15568, 1487), (80743, 85294), (11207, 90623), (61258, 63879), (34363, 59005), (74884, 2528), (26604, 52738), (33304, 1202), (20381, 18984), (81968, 92425), (4407, 84677), (2112, 79756), (46970, 4367), (36854, 23482), (88346, 75107), (10643, 31806), (21351, 5590), (69317, 53292), (76711, 10085), (70333, 90592), (88818, 822), (23927, 48141), (84710, 33870), (96932, 22686), (5783, 87468), (7785, 11585), (49497, 33764), (13506, 55969), (37840, 78455), (21532, 22292), (97306, 42065), (6579, 40749), (2593, 4995), (81985, 23611), (63888, 98317), (44975, 83777), (57688, 42688), (641, 45787), (7316, 1967), (43837, 18274), (89994, 32770), (4285, 50388), (84699, 41841), (19564, 20683), (76027, 62278), (26140, 11288), (39656, 79954), (16718, 17335), (11583, 21283), (55441, 32178), (6810, 87225), (27191, 54323), (53406, 31512), (48003, 80077), (78497, 29570), (39140, 66619), (12651, 44576), (1761, 88410), (47139, 20766), (39183, 69367), (80479, 23285), (1568, 78535), (18476, 35058), (93551, 81063), (12059, 60021), (23356, 26572), (79975, 35434), (82230, 67436), (20243, 92343), (47809, 10634), (69537, 60167), (3873, 77404), (1136, 27956), (17470, 24156), (35849, 19150), (74760, 37961), (36660, 44448), (36009, 96619), (87110, 84921), (16080, 60637), (36046, 17351), (96403, 99978), (11060, 68629), (36081, 23464), (4684, 11817), (50126, 82936), (55262, 54135), (53717, 66293), (58028, 28065), (92791, 99766), (46266, 77711), (61912, 65782), (38677, 41158), (4001, 46340), (70987, 12784), (14819, 42857), (78985, 99956), (79737, 42497), (55305, 7329), (64103, 24170), (49093, 22115), (2465, 97282), (29009, 15663), (80976, 86477), (16439, 56685), (53482, 15293), (5038, 5991), (67060, 84201), (54350, 38095), (67539, 68292), (26464, 64908), (92909, 12867), (83517, 26474), (76081, 85247), (23250, 66616), (20783, 34330), (43074, 10165), (93968, 70375), (83802, 70820), (19871, 63094), (35699, 36506), (23905, 2401), (27847, 31968), (76714, 44112), (62599, 32720), (10362, 81985), (35708, 2090), (13071, 39035), (71851, 59493), (62833, 48082), (77164, 22804), (6469, 43229), (92051, 3719), (51910, 77689), (91470, 63253), (57914, 57836), (98819, 97813), (35975, 488), (51431, 34061), (45414, 85971), (56563, 93517), (40789, 53103), (9242, 20814), (784, 22584), (8740, 56894), (28504, 378), (8287, 96930), (74232, 97496), (61565, 7904), (9779, 45122), (33767, 48471), (16766, 10722), (47764, 478), (14374, 30099), (89134, 19977), (60860, 93201), (71123, 29840), (57159, 34410), (82411, 99537), (11307, 3733), (70264, 43028), (30418, 19372), (46543, 31506), (33043, 98980), (21137, 63374), (85640, 36957), (6790, 60751), (78771, 43700), (33808, 38263), (27232, 35152), (39925, 5062), (3120, 65621), (39319, 6795), (77468, 94964), (10481, 43009), (24237, 2103), (16837, 55667), (43846, 2874), (78786, 66811), (92185, 62395), (26318, 87942), (6208, 80815), (66952, 71885), (51435, 25450), (21443, 69801), (92554, 81977), (58912, 82288), (59681, 46177), (60397, 65866), (72065, 13318), (2848, 73852), (7577, 83238), (209, 40659), (72103, 15266), (23365, 67286), (14600, 29269), (85541, 63289), (25427, 54812), (22967, 54965), (81525, 27126), (20473, 55455), (84067, 25794), (46798, 79332), (93825, 74677), (447, 5904), (65661, 92916), (54428, 76482), (1025, 34415), (63761, 30038), (93025, 15090), (98807, 93426), (57562, 59615), (84884, 30620), (75066, 71824), (51199, 37934), (95530, 15260), (513, 98278), (62995, 28267), (47535, 69776), (39266, 4696), (14742, 13225), (44268, 16548), (45976, 41680), (99638, 30285), (85609, 5578), (28156, 12884), (76188, 86573), (44639, 15480), (86789, 72636), (18702, 13337), (96638, 59398), (90988, 26909), (85069, 95193), (67262, 38337), (51694, 19659), (93341, 80988), (48733, 88460), (55630, 22866), (96203, 10316), (30644, 68318), (79292, 63136), (60185, 73681), (60474, 19078), (48721, 82811), (19713, 99527), (17537, 55235), (296, 8353), (58691, 72158), (66734, 92490), (87642, 7174), (78285, 35337), (19503, 14273), (10713, 64116), (85966, 98738), (56561, 99347), (14869, 89963), (95126, 30748), (89272, 79060), (69888, 7193), (32583, 74564), (95542, 52128), (78360, 42675), (86062, 68404), (38732, 21411), (92935, 45415), (99027, 83925), (73232, 37405), diff --git a/tests/performance/sum_map.xml b/tests/performance/sum_map.xml index 9cc03035cce..cb1a4cb5bc6 100644 --- a/tests/performance/sum_map.xml +++ b/tests/performance/sum_map.xml @@ -7,7 +7,6 @@ scale - 100000 1000000 diff --git a/tests/performance/website.xml b/tests/performance/website.xml index 0011d225d6c..66357352f3e 100644 --- a/tests/performance/website.xml +++ b/tests/performance/website.xml @@ -55,7 +55,7 @@ SELECT URL, count() AS c FROM {table} GROUP BY URL ORDER BY c DESC LIMIT 10 SELECT 1, URL, count() AS c FROM {table} GROUP BY 1, URL ORDER BY c DESC LIMIT 10 SELECT ClientIP AS x, x - 1, x - 2, x - 3, count() AS c FROM hits_100m_single GROUP BY x, x - 1, x - 2, x - 3 ORDER BY c DESC LIMIT 10 -SELECT URL, count() AS PageViews FROM {table} WHERE CounterID = 34 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT DontCountHits AND NOT Refresh AND notEmpty(URL) GROUP BY URL ORDER BY PageViews DESC LIMIT 10 +SELECT URL, count() AS PageViews FROM {table} WHERE CounterID = 34 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT DontCountHits AND NOT Refresh AND notEmpty(URL) GROUP BY URL ORDER BY PageViews DESC LIMIT 10 SETTINGS max_threads = 1 SELECT Title, count() AS PageViews FROM {table} WHERE CounterID = 34 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT DontCountHits AND NOT Refresh AND notEmpty(Title) GROUP BY Title ORDER BY PageViews DESC LIMIT 10 SELECT URL, count() AS PageViews FROM {table} WHERE CounterID = 34 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT Refresh AND IsLink AND NOT IsDownload GROUP BY URL ORDER BY PageViews DESC LIMIT 1000 SELECT TraficSourceID, SearchEngineID, AdvEngineID, ((SearchEngineID = 0 AND AdvEngineID = 0) ? Referer : '') AS Src, URL AS Dst, count() AS PageViews FROM {table} WHERE CounterID = 34 AND EventDate >= '2013-07-01' AND EventDate <= '2013-07-31' AND NOT Refresh GROUP BY TraficSourceID, SearchEngineID, AdvEngineID, Src, Dst ORDER BY PageViews DESC LIMIT 1000 diff --git a/tests/queries/0_stateless/00098_k_union_all.reference b/tests/queries/0_stateless/00098_k_union_all.reference index 1b21a751afc..a6921e228af 100644 --- a/tests/queries/0_stateless/00098_k_union_all.reference +++ b/tests/queries/0_stateless/00098_k_union_all.reference @@ -1,18 +1,18 @@ -1 + 1 -1 -1 + 1 + 1 -1 -1 + 1 + 1 -1 -1 + 1 + 1 -1 -1 + 1 + 1 -1 -1 + 1 + 1 -1 + 1 diff --git a/tests/queries/0_stateless/00305_http_and_readonly.reference b/tests/queries/0_stateless/00305_http_and_readonly.reference index f148cbbb93b..4d0cb6bd6bc 100644 --- a/tests/queries/0_stateless/00305_http_and_readonly.reference +++ b/tests/queries/0_stateless/00305_http_and_readonly.reference @@ -1,19 +1,19 @@ -name value changed + name value changed -max_rows_to_read 10000 1 -readonly 0 0 -name value changed + max_rows_to_read 10000 1 + readonly 0 0 + name value changed -max_rows_to_read 10000 1 -readonly 2 1 -name value changed + max_rows_to_read 10000 1 + readonly 2 1 + name value changed -max_rows_to_read 10000 1 -readonly 1 1 -name value changed + max_rows_to_read 10000 1 + readonly 1 1 + name value changed -max_rows_to_read 10000 1 -readonly 2 1 + max_rows_to_read 10000 1 + readonly 2 1 Ok Ok 0 diff --git a/tests/queries/0_stateless/00398_url_functions.reference b/tests/queries/0_stateless/00398_url_functions.reference index acb605597d3..c926240b4f7 100644 --- a/tests/queries/0_stateless/00398_url_functions.reference +++ b/tests/queries/0_stateless/00398_url_functions.reference @@ -16,6 +16,17 @@ www.example.com example.com example.com example.com +====NETLOC==== +paul@www.example.com:80 +127.0.0.1:443 +127.0.0.1:443 +example.ru +example.ru +paul:zozo@example.ru +paul:zozo@example.ru +www.example.com +www.example.com +example.com ====DOMAIN==== com diff --git a/tests/queries/0_stateless/00398_url_functions.sql b/tests/queries/0_stateless/00398_url_functions.sql index d301cac5b15..c689844d08d 100644 --- a/tests/queries/0_stateless/00398_url_functions.sql +++ b/tests/queries/0_stateless/00398_url_functions.sql @@ -18,6 +18,17 @@ SELECT domain('example.com') as Host; SELECT domainWithoutWWW('//paul@www.example.com') AS Host; SELECT domainWithoutWWW('http://paul@www.example.com:80/') AS Host; +SELECT '====NETLOC===='; +SELECT netloc('http://paul@www.example.com:80/') AS Netloc; +SELECT netloc('http://127.0.0.1:443/') AS Netloc; +SELECT netloc('http://127.0.0.1:443') AS Netloc; +SELECT netloc('svn+ssh://example.ru/?q=hello%20world') AS Netloc; +SELECT netloc('svn+ssh://example.ru/?q=hello%20world') AS Netloc; +SELECT netloc('svn+ssh://paul:zozo@example.ru/?q=hello%20world') AS Netloc; +SELECT netloc('svn+ssh://paul:zozo@example.ru/?q=hello%20world') AS Netloc; +SELECT netloc('//www.example.com') AS Netloc; +SELECT netloc('www.example.com') as Netloc; +SELECT netloc('example.com') as Netloc; SELECT '====DOMAIN===='; SELECT topLevelDomain('http://paul@www.example.com:80/') AS Domain; diff --git a/tests/queries/0_stateless/00405_pretty_formats.reference b/tests/queries/0_stateless/00405_pretty_formats.reference index 07dfd76339b..ef3184f2837 100644 --- a/tests/queries/0_stateless/00405_pretty_formats.reference +++ b/tests/queries/0_stateless/00405_pretty_formats.reference @@ -38,20 +38,20 @@ │ 8 │ 8 │ (8,'8') │ 2 │ │ 9 │ 9 │ (9,'9') │ ᴺᵁᴸᴸ │ └───────┴───────┴─────────┴─────────────────┘ -hello world tuple sometimes_nulls + hello world tuple sometimes_nulls - 0 0 (0,'0') ᴺᵁᴸᴸ - 1 1 (1,'1') 1 - 2 2 (2,'2') 2 - 3 3 (3,'3') ᴺᵁᴸᴸ - 4 4 (4,'4') 1 -hello world tuple sometimes_nulls + 0 0 (0,'0') ᴺᵁᴸᴸ + 1 1 (1,'1') 1 + 2 2 (2,'2') 2 + 3 3 (3,'3') ᴺᵁᴸᴸ + 4 4 (4,'4') 1 + hello world tuple sometimes_nulls - 5 5 (5,'5') 2 - 6 6 (6,'6') ᴺᵁᴸᴸ - 7 7 (7,'7') 1 - 8 8 (8,'8') 2 - 9 9 (9,'9') ᴺᵁᴸᴸ + 5 5 (5,'5') 2 + 6 6 (6,'6') ᴺᵁᴸᴸ + 7 7 (7,'7') 1 + 8 8 (8,'8') 2 + 9 9 (9,'9') ᴺᵁᴸᴸ ┌─hello─┬─world─┬─tuple───┬─sometimes_nulls─┐ │ 0 │ 0 │ (0,'0') │ ᴺᵁᴸᴸ │ │ 1 │ 1 │ (1,'1') │ 1 │ @@ -104,20 +104,20 @@ │ 8 │ 8 │ (8,'8') │ 2 │ │ 9 │ 9 │ (9,'9') │ ᴺᵁᴸᴸ │ └───────┴───────┴─────────┴─────────────────┘ -hello world tuple sometimes_nulls + hello world tuple sometimes_nulls - 0 0 (0,'0') ᴺᵁᴸᴸ - 1 1 (1,'1') 1 - 2 2 (2,'2') 2 - 3 3 (3,'3') ᴺᵁᴸᴸ - 4 4 (4,'4') 1 -hello world tuple sometimes_nulls + 0 0 (0,'0') ᴺᵁᴸᴸ + 1 1 (1,'1') 1 + 2 2 (2,'2') 2 + 3 3 (3,'3') ᴺᵁᴸᴸ + 4 4 (4,'4') 1 + hello world tuple sometimes_nulls - 5 5 (5,'5') 2 - 6 6 (6,'6') ᴺᵁᴸᴸ - 7 7 (7,'7') 1 - 8 8 (8,'8') 2 - 9 9 (9,'9') ᴺᵁᴸᴸ + 5 5 (5,'5') 2 + 6 6 (6,'6') ᴺᵁᴸᴸ + 7 7 (7,'7') 1 + 8 8 (8,'8') 2 + 9 9 (9,'9') ᴺᵁᴸᴸ ┏━━━━━━━┳━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━┓ ┃ hello ┃ world ┃ tuple  ┃ sometimes_nulls ┃ ┡━━━━━━━╇━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━┩ @@ -148,16 +148,16 @@ hello world tuple sometimes_nulls │ 5 │ 5 │ (5,'5') │ 2 │ └───────┴───────┴─────────┴─────────────────┘ Showed first 6. -hello world tuple sometimes_nulls + hello world tuple sometimes_nulls - 0 0 (0,'0') ᴺᵁᴸᴸ - 1 1 (1,'1') 1 - 2 2 (2,'2') 2 - 3 3 (3,'3') ᴺᵁᴸᴸ - 4 4 (4,'4') 1 -hello world tuple sometimes_nulls + 0 0 (0,'0') ᴺᵁᴸᴸ + 1 1 (1,'1') 1 + 2 2 (2,'2') 2 + 3 3 (3,'3') ᴺᵁᴸᴸ + 4 4 (4,'4') 1 + hello world tuple sometimes_nulls - 5 5 (5,'5') 2 + 5 5 (5,'5') 2 Showed first 6. ┌─hello─┬─world─┬─tuple───┬─sometimes_nulls─┐ @@ -199,15 +199,15 @@ Showed first 6. │ 5 │ 5 │ (5,'5') │ 2 │ └───────┴───────┴─────────┴─────────────────┘ Showed first 6. -hello world tuple sometimes_nulls + hello world tuple sometimes_nulls - 0 0 (0,'0') ᴺᵁᴸᴸ - 1 1 (1,'1') 1 - 2 2 (2,'2') 2 - 3 3 (3,'3') ᴺᵁᴸᴸ - 4 4 (4,'4') 1 -hello world tuple sometimes_nulls + 0 0 (0,'0') ᴺᵁᴸᴸ + 1 1 (1,'1') 1 + 2 2 (2,'2') 2 + 3 3 (3,'3') ᴺᵁᴸᴸ + 4 4 (4,'4') 1 + hello world tuple sometimes_nulls - 5 5 (5,'5') 2 + 5 5 (5,'5') 2 Showed first 6. diff --git a/tests/queries/0_stateless/00476_pretty_formats_and_widths.reference b/tests/queries/0_stateless/00476_pretty_formats_and_widths.reference index c97991ae0e9..eedaa4b5d8b 100644 --- a/tests/queries/0_stateless/00476_pretty_formats_and_widths.reference +++ b/tests/queries/0_stateless/00476_pretty_formats_and_widths.reference @@ -33,18 +33,18 @@ │ 100000000 │ 100000000 │ │ 1000000000 │ 1000000000 │ └────────────┴────────────┘ - x s + x s - 1 1 - 10 10 - 100 100 - 1000 1000 - 10000 10000 - 100000 100000 - 1000000 1000000 - 10000000 10000000 - 100000000 100000000 -1000000000 1000000000 + 1 1 + 10 10 + 100 100 + 1000 1000 + 10000 10000 + 100000 100000 + 1000000 1000000 + 10000000 10000000 + 100000000 100000000 + 1000000000 1000000000 ┌──────────x─┬─s──────────┐ │ 1 │ 1 │ │ 10 │ 10 │ diff --git a/tests/queries/0_stateless/00500_point_in_polygon_nan.reference b/tests/queries/0_stateless/00500_point_in_polygon_nan.reference new file mode 100644 index 00000000000..573541ac970 --- /dev/null +++ b/tests/queries/0_stateless/00500_point_in_polygon_nan.reference @@ -0,0 +1 @@ +0 diff --git a/tests/queries/0_stateless/00500_point_in_polygon_nan.sql b/tests/queries/0_stateless/00500_point_in_polygon_nan.sql new file mode 100644 index 00000000000..37ed8dbeded --- /dev/null +++ b/tests/queries/0_stateless/00500_point_in_polygon_nan.sql @@ -0,0 +1 @@ +SELECT pointInPolygon((nan, 10.000100135803223), [(39.83154, 21.41527), (2., 1000.0001220703125), (39.90033, 21.37195), (1.000100016593933, 10.000100135803223), (39.83051, 21.42553), (39.82898, 21.41382), (39.83043, 21.41432), (39.83154, 21.41527)]); diff --git a/tests/queries/0_stateless/00569_parse_date_time_best_effort.reference b/tests/queries/0_stateless/00569_parse_date_time_best_effort.reference index 8638c0b707f..bf11e1c1d71 100644 --- a/tests/queries/0_stateless/00569_parse_date_time_best_effort.reference +++ b/tests/queries/0_stateless/00569_parse_date_time_best_effort.reference @@ -1,106 +1,106 @@ -s a b + s a b -0 2000-01-01 00:00:00 2000-01-01 00:00:00 -0000 2000-01-01 00:00:00 2000-01-01 00:00:00 -00:00:00 2000-01-01 00:00:00 2000-01-01 00:00:00 -01:00:00 2000-01-01 01:00:00 2000-01-01 01:00:00 -02/01/17 010203 MSK 2017-01-01 22:02:03 2017-01-01 22:02:03 -02/01/17 010203 MSK+0100 2017-01-01 21:02:03 2017-01-01 21:02:03 -02/01/17 010203 UTC+0300 2017-01-01 22:02:03 2017-01-01 22:02:03 -02/01/17 010203Z 2017-01-02 01:02:03 2017-01-02 01:02:03 -02/01/1970 010203Z 1970-01-02 01:02:03 1970-01-02 01:02:03 -02/01/70 010203Z 1970-01-02 01:02:03 1970-01-02 01:02:03 -11 Feb 2018 06:40:50 +0300 2018-02-11 03:40:50 2018-02-11 03:40:50 -17 Apr 2 1:2:3 2000-04-17 01:02:03 2000-04-17 01:02:03 -19700102 01:00:00 1970-01-02 01:00:00 1970-01-02 01:00:00 -1970010201:00:00 2032-06-06 02:03:21 2032-06-06 02:03:21 -19700102010203 1970-01-02 01:02:03 1970-01-02 01:02:03 -19700102010203Z 1970-01-02 01:02:03 1970-01-02 01:02:03 -1970/01/02 010203Z 1970-01-02 01:02:03 1970-01-02 01:02:03 -20 2000-01-20 00:00:00 2000-01-20 00:00:00 -201 ᴺᵁᴸᴸ 0000-00-00 00:00:00 -20160101 2016-01-01 00:00:00 2016-01-01 00:00:00 -2016-01-01 2016-01-01 00:00:00 2016-01-01 00:00:00 -201601-01 2016-01-01 01:00:00 2016-01-01 01:00:00 -2016-01-01MSD 2015-12-31 20:00:00 2015-12-31 20:00:00 -2016-01-01 MSD 2015-12-31 20:00:00 2015-12-31 20:00:00 -201601-01 MSD 2016-01-01 04:00:00 2016-01-01 04:00:00 -2016-01-01UTC 2016-01-01 00:00:00 2016-01-01 00:00:00 -2016-01-01Z 2016-01-01 00:00:00 2016-01-01 00:00:00 -2017 2017-01-01 00:00:00 2017-01-01 00:00:00 -2017/01/00 2017-01-01 00:00:00 2017-01-01 00:00:00 -2017/01/00 MSD 2016-12-31 20:00:00 2016-12-31 20:00:00 -2017/01/00 MSD Jun 2017-05-31 20:00:00 2017-05-31 20:00:00 -2017/01/01 2017-01-01 00:00:00 2017-01-01 00:00:00 -201701 02 010203 UTC+0300 2017-01-01 22:02:03 2017-01-01 22:02:03 -2017-01-02 03:04:05 2017-01-02 03:04:05 2017-01-02 03:04:05 -2017-01-0203:04:05 ᴺᵁᴸᴸ 0000-00-00 00:00:00 -2017-01-02 03:04:05+0 2017-01-02 03:04:05 2017-01-02 03:04:05 -2017-01-02 03:04:05+00 2017-01-02 03:04:05 2017-01-02 03:04:05 -2017-01-02 03:04:05+0000 2017-01-02 03:04:05 2017-01-02 03:04:05 -2017-01-02 03:04:05 -0100 2017-01-02 04:04:05 2017-01-02 04:04:05 -2017-01-02 03:04:05+030 2017-01-02 02:34:05 2017-01-02 02:34:05 -2017-01-02 03:04:05+0300 2017-01-02 00:04:05 2017-01-02 00:04:05 -2017-01-02 03:04:05+1 2017-01-02 02:04:05 2017-01-02 02:04:05 -2017-01-02 03:04:05+300 2017-01-02 00:04:05 2017-01-02 00:04:05 -2017-01-02 03:04:05+900 2017-01-01 18:04:05 2017-01-01 18:04:05 -2017-01-02 03:04:05GMT 2017-01-02 03:04:05 2017-01-02 03:04:05 -2017-01-02 03:04:05 MSD 2017-01-01 23:04:05 2017-01-01 23:04:05 -2017-01-02 03:04:05 MSD Feb 2017-02-01 23:04:05 2017-02-01 23:04:05 -2017-01-02 03:04:05 MSD Jun 2017-06-01 23:04:05 2017-06-01 23:04:05 -2017-01-02 03:04:05 MSK 2017-01-02 00:04:05 2017-01-02 00:04:05 -2017-01-02T03:04:05 2017-01-02 03:04:05 2017-01-02 03:04:05 -2017-01-02T03:04:05+00 2017-01-02 03:04:05 2017-01-02 03:04:05 -2017-01-02T03:04:05 -0100 2017-01-02 04:04:05 2017-01-02 04:04:05 -2017-01-02T03:04:05-0100 2017-01-02 04:04:05 2017-01-02 04:04:05 -2017-01-02T03:04:05+0100 2017-01-02 02:04:05 2017-01-02 02:04:05 -2017-01-02T03:04:05Z 2017-01-02 03:04:05 2017-01-02 03:04:05 -2017-01 03:04:05 MSD Jun 2017-05-31 23:04:05 2017-05-31 23:04:05 -2017-01 03:04 MSD Jun 2017-05-31 23:04:00 2017-05-31 23:04:00 -2017/01/31 2017-01-31 00:00:00 2017-01-31 00:00:00 -2017/01/32 0000-00-00 00:00:00 0000-00-00 00:00:00 -2017-01 MSD Jun 2017-05-31 20:00:00 2017-05-31 20:00:00 -201701 MSD Jun 2017-05-31 20:00:00 2017-05-31 20:00:00 -2017 25 1:2:3 0000-00-00 00:00:00 0000-00-00 00:00:00 -2017 25 Apr 1:2:3 2017-04-01 01:02:03 2017-04-01 01:02:03 -2017 Apr 01 11:22:33 2017-04-01 11:22:33 2017-04-01 11:22:33 -2017 Apr 02 01/02/03 UTC+0300 ᴺᵁᴸᴸ 0000-00-00 00:00:00 -2017 Apr 02 010203 UTC+0300 2017-04-01 22:02:03 2017-04-01 22:02:03 -2017 Apr 02 01:2:3 UTC+0300 2017-04-01 22:02:03 2017-04-01 22:02:03 -2017 Apr 02 1:02:3 2017-04-02 01:02:03 2017-04-02 01:02:03 -2017 Apr 02 11:22:33 2017-04-02 11:22:33 2017-04-02 11:22:33 -2017 Apr 02 1:2:03 2017-04-02 01:02:03 2017-04-02 01:02:03 -2017 Apr 02 1:22:33 2017-04-02 01:22:33 2017-04-02 01:22:33 -2017 Apr 02 1:2:3 2017-04-02 01:02:03 2017-04-02 01:02:03 -2017 Apr 02 1:2:33 2017-04-02 01:02:33 2017-04-02 01:02:33 -2017 Apr 02 1:2:3 MSK 2017-04-01 22:02:03 2017-04-01 22:02:03 -2017 Apr 02 1:2:3 MSK 2017 2017-04-01 22:02:03 2017-04-01 22:02:03 -2017 Apr 02 1:2:3 MSK 2018 2017-04-01 22:02:03 2017-04-01 22:02:03 -2017 Apr 02 1:2:3 UTC+0000 2017-04-02 01:02:03 2017-04-02 01:02:03 -2017 Apr 02 1:2:3 UTC+0300 2017-04-01 22:02:03 2017-04-01 22:02:03 -2017 Apr 02 1:2:3 UTC+0400 2017-04-01 21:02:03 2017-04-01 21:02:03 -2017 Apr 2 1:2:3 2017-04-02 01:02:03 2017-04-02 01:02:03 -2017 Jan 02 010203 UTC+0300 2017-01-01 22:02:03 2017-01-01 22:02:03 -25 Apr 2017 01:02:03 2017-04-25 01:02:03 2017-04-25 01:02:03 -25 Apr 2017 1:2:3 2017-04-25 01:02:03 2017-04-25 01:02:03 -25 Jan 2017 1:2:3 2017-01-25 01:02:03 2017-01-25 01:02:03 -25 Jan 2017 1:2:3 MSK 2017-01-24 22:02:03 2017-01-24 22:02:03 -25 Jan 2017 1:2:3 PM 2017-01-25 13:02:03 2017-01-25 13:02:03 -25 Jan 2017 1:2:3Z 2017-01-25 01:02:03 2017-01-25 01:02:03 -25 Jan 2017 1:2:3 Z 2017-01-25 01:02:03 2017-01-25 01:02:03 -25 Jan 2017 1:2:3 Z +0300 2017-01-24 22:02:03 2017-01-24 22:02:03 -25 Jan 2017 1:2:3 Z+03:00 2017-01-24 22:02:03 2017-01-24 22:02:03 -25 Jan 2017 1:2:3 Z +0300 OM ᴺᵁᴸᴸ 0000-00-00 00:00:00 -25 Jan 2017 1:2:3 Z +03:00 PM 2017-01-25 10:02:03 2017-01-25 10:02:03 -25 Jan 2017 1:2:3 Z +0300 PM 2017-01-25 10:02:03 2017-01-25 10:02:03 -25 Jan 2017 1:2:3 Z+03:00 PM 2017-01-25 10:02:03 2017-01-25 10:02:03 -25 Jan 2017 1:2:3 Z +03:30 PM 2017-01-25 09:32:03 2017-01-25 09:32:03 -25 Jan 2017 1:2:3Z Mo ᴺᵁᴸᴸ 0000-00-00 00:00:00 -25 Jan 2017 1:2:3Z Mon 2017-01-25 01:02:03 2017-01-25 01:02:03 -25 Jan 2017 1:2:3Z Moo ᴺᵁᴸᴸ 0000-00-00 00:00:00 -25 Jan 2017 1:2:3 Z PM 2017-01-25 13:02:03 2017-01-25 13:02:03 -25 Jan 2017 1:2:3Z PM 2017-01-25 13:02:03 2017-01-25 13:02:03 -25 Jan 2017 1:2:3 Z PM +03:00 2017-01-25 10:02:03 2017-01-25 10:02:03 -Jun, 11 Feb 2018 06:40:50 +0300 2000-06-01 00:00:00 2000-06-01 00:00:00 -Sun 11 Feb 2018 06:40:50 +0300 2018-02-11 03:40:50 2018-02-11 03:40:50 -Sun, 11 Feb 2018 06:40:50 +0300 2018-02-11 03:40:50 2018-02-11 03:40:50 + 0 2000-01-01 00:00:00 2000-01-01 00:00:00 + 0000 2000-01-01 00:00:00 2000-01-01 00:00:00 + 00:00:00 2000-01-01 00:00:00 2000-01-01 00:00:00 + 01:00:00 2000-01-01 01:00:00 2000-01-01 01:00:00 + 02/01/17 010203 MSK 2017-01-01 22:02:03 2017-01-01 22:02:03 + 02/01/17 010203 MSK+0100 2017-01-01 21:02:03 2017-01-01 21:02:03 + 02/01/17 010203 UTC+0300 2017-01-01 22:02:03 2017-01-01 22:02:03 + 02/01/17 010203Z 2017-01-02 01:02:03 2017-01-02 01:02:03 + 02/01/1970 010203Z 1970-01-02 01:02:03 1970-01-02 01:02:03 + 02/01/70 010203Z 1970-01-02 01:02:03 1970-01-02 01:02:03 + 11 Feb 2018 06:40:50 +0300 2018-02-11 03:40:50 2018-02-11 03:40:50 + 17 Apr 2 1:2:3 2000-04-17 01:02:03 2000-04-17 01:02:03 + 19700102 01:00:00 1970-01-02 01:00:00 1970-01-02 01:00:00 + 1970010201:00:00 ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 19700102010203 1970-01-02 01:02:03 1970-01-02 01:02:03 + 19700102010203Z 1970-01-02 01:02:03 1970-01-02 01:02:03 + 1970/01/02 010203Z 1970-01-02 01:02:03 1970-01-02 01:02:03 + 20 2000-01-20 00:00:00 2000-01-20 00:00:00 + 201 ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 20160101 2016-01-01 00:00:00 2016-01-01 00:00:00 + 2016-01-01 2016-01-01 00:00:00 2016-01-01 00:00:00 + 201601-01 2016-01-01 01:00:00 2016-01-01 01:00:00 + 2016-01-01MSD 2015-12-31 20:00:00 2015-12-31 20:00:00 + 2016-01-01 MSD 2015-12-31 20:00:00 2015-12-31 20:00:00 + 201601-01 MSD 2016-01-01 04:00:00 2016-01-01 04:00:00 + 2016-01-01UTC 2016-01-01 00:00:00 2016-01-01 00:00:00 + 2016-01-01Z 2016-01-01 00:00:00 2016-01-01 00:00:00 + 2017 2017-01-01 00:00:00 2017-01-01 00:00:00 + 2017/01/00 2017-01-01 00:00:00 2017-01-01 00:00:00 + 2017/01/00 MSD 2016-12-31 20:00:00 2016-12-31 20:00:00 + 2017/01/00 MSD Jun 2017-05-31 20:00:00 2017-05-31 20:00:00 + 2017/01/01 2017-01-01 00:00:00 2017-01-01 00:00:00 + 201701 02 010203 UTC+0300 2017-01-01 22:02:03 2017-01-01 22:02:03 + 2017-01-02 03:04:05 2017-01-02 03:04:05 2017-01-02 03:04:05 + 2017-01-0203:04:05 ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 2017-01-02 03:04:05+0 2017-01-02 03:04:05 2017-01-02 03:04:05 + 2017-01-02 03:04:05+00 2017-01-02 03:04:05 2017-01-02 03:04:05 + 2017-01-02 03:04:05+0000 2017-01-02 03:04:05 2017-01-02 03:04:05 + 2017-01-02 03:04:05 -0100 2017-01-02 04:04:05 2017-01-02 04:04:05 + 2017-01-02 03:04:05+030 2017-01-02 02:34:05 2017-01-02 02:34:05 + 2017-01-02 03:04:05+0300 2017-01-02 00:04:05 2017-01-02 00:04:05 + 2017-01-02 03:04:05+1 2017-01-02 02:04:05 2017-01-02 02:04:05 + 2017-01-02 03:04:05+300 2017-01-02 00:04:05 2017-01-02 00:04:05 + 2017-01-02 03:04:05+900 2017-01-01 18:04:05 2017-01-01 18:04:05 + 2017-01-02 03:04:05GMT 2017-01-02 03:04:05 2017-01-02 03:04:05 + 2017-01-02 03:04:05 MSD 2017-01-01 23:04:05 2017-01-01 23:04:05 + 2017-01-02 03:04:05 MSD Feb 2017-02-01 23:04:05 2017-02-01 23:04:05 + 2017-01-02 03:04:05 MSD Jun 2017-06-01 23:04:05 2017-06-01 23:04:05 + 2017-01-02 03:04:05 MSK 2017-01-02 00:04:05 2017-01-02 00:04:05 + 2017-01-02T03:04:05 2017-01-02 03:04:05 2017-01-02 03:04:05 + 2017-01-02T03:04:05+00 2017-01-02 03:04:05 2017-01-02 03:04:05 + 2017-01-02T03:04:05 -0100 2017-01-02 04:04:05 2017-01-02 04:04:05 + 2017-01-02T03:04:05-0100 2017-01-02 04:04:05 2017-01-02 04:04:05 + 2017-01-02T03:04:05+0100 2017-01-02 02:04:05 2017-01-02 02:04:05 + 2017-01-02T03:04:05Z 2017-01-02 03:04:05 2017-01-02 03:04:05 + 2017-01 03:04:05 MSD Jun 2017-05-31 23:04:05 2017-05-31 23:04:05 + 2017-01 03:04 MSD Jun 2017-05-31 23:04:00 2017-05-31 23:04:00 + 2017/01/31 2017-01-31 00:00:00 2017-01-31 00:00:00 + 2017/01/32 0000-00-00 00:00:00 0000-00-00 00:00:00 + 2017-01 MSD Jun 2017-05-31 20:00:00 2017-05-31 20:00:00 + 201701 MSD Jun 2017-05-31 20:00:00 2017-05-31 20:00:00 + 2017 25 1:2:3 ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 2017 25 Apr 1:2:3 2017-04-01 01:02:03 2017-04-01 01:02:03 + 2017 Apr 01 11:22:33 2017-04-01 11:22:33 2017-04-01 11:22:33 + 2017 Apr 02 01/02/03 UTC+0300 ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 2017 Apr 02 010203 UTC+0300 2017-04-01 22:02:03 2017-04-01 22:02:03 + 2017 Apr 02 01:2:3 UTC+0300 2017-04-01 22:02:03 2017-04-01 22:02:03 + 2017 Apr 02 1:02:3 2017-04-02 01:02:03 2017-04-02 01:02:03 + 2017 Apr 02 11:22:33 2017-04-02 11:22:33 2017-04-02 11:22:33 + 2017 Apr 02 1:2:03 2017-04-02 01:02:03 2017-04-02 01:02:03 + 2017 Apr 02 1:22:33 2017-04-02 01:22:33 2017-04-02 01:22:33 + 2017 Apr 02 1:2:3 2017-04-02 01:02:03 2017-04-02 01:02:03 + 2017 Apr 02 1:2:33 2017-04-02 01:02:33 2017-04-02 01:02:33 + 2017 Apr 02 1:2:3 MSK 2017-04-01 22:02:03 2017-04-01 22:02:03 + 2017 Apr 02 1:2:3 MSK 2017 ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 2017 Apr 02 1:2:3 MSK 2018 ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 2017 Apr 02 1:2:3 UTC+0000 2017-04-02 01:02:03 2017-04-02 01:02:03 + 2017 Apr 02 1:2:3 UTC+0300 2017-04-01 22:02:03 2017-04-01 22:02:03 + 2017 Apr 02 1:2:3 UTC+0400 2017-04-01 21:02:03 2017-04-01 21:02:03 + 2017 Apr 2 1:2:3 2017-04-02 01:02:03 2017-04-02 01:02:03 + 2017 Jan 02 010203 UTC+0300 2017-01-01 22:02:03 2017-01-01 22:02:03 + 25 Apr 2017 01:02:03 2017-04-25 01:02:03 2017-04-25 01:02:03 + 25 Apr 2017 1:2:3 2017-04-25 01:02:03 2017-04-25 01:02:03 + 25 Jan 2017 1:2:3 2017-01-25 01:02:03 2017-01-25 01:02:03 + 25 Jan 2017 1:2:3 MSK 2017-01-24 22:02:03 2017-01-24 22:02:03 + 25 Jan 2017 1:2:3 PM 2017-01-25 13:02:03 2017-01-25 13:02:03 + 25 Jan 2017 1:2:3Z 2017-01-25 01:02:03 2017-01-25 01:02:03 + 25 Jan 2017 1:2:3 Z 2017-01-25 01:02:03 2017-01-25 01:02:03 + 25 Jan 2017 1:2:3 Z +0300 2017-01-24 22:02:03 2017-01-24 22:02:03 + 25 Jan 2017 1:2:3 Z+03:00 2017-01-24 22:02:03 2017-01-24 22:02:03 + 25 Jan 2017 1:2:3 Z +0300 OM ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 25 Jan 2017 1:2:3 Z +03:00 PM 2017-01-25 10:02:03 2017-01-25 10:02:03 + 25 Jan 2017 1:2:3 Z +0300 PM 2017-01-25 10:02:03 2017-01-25 10:02:03 + 25 Jan 2017 1:2:3 Z+03:00 PM 2017-01-25 10:02:03 2017-01-25 10:02:03 + 25 Jan 2017 1:2:3 Z +03:30 PM 2017-01-25 09:32:03 2017-01-25 09:32:03 + 25 Jan 2017 1:2:3Z Mo ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 25 Jan 2017 1:2:3Z Mon 2017-01-25 01:02:03 2017-01-25 01:02:03 + 25 Jan 2017 1:2:3Z Moo ᴺᵁᴸᴸ 0000-00-00 00:00:00 + 25 Jan 2017 1:2:3 Z PM 2017-01-25 13:02:03 2017-01-25 13:02:03 + 25 Jan 2017 1:2:3Z PM 2017-01-25 13:02:03 2017-01-25 13:02:03 + 25 Jan 2017 1:2:3 Z PM +03:00 2017-01-25 10:02:03 2017-01-25 10:02:03 + Jun, 11 Feb 2018 06:40:50 +0300 ᴺᵁᴸᴸ 0000-00-00 00:00:00 + Sun 11 Feb 2018 06:40:50 +0300 2018-02-11 03:40:50 2018-02-11 03:40:50 + Sun, 11 Feb 2018 06:40:50 +0300 2018-02-11 03:40:50 2018-02-11 03:40:50 diff --git a/tests/queries/0_stateless/00578_merge_table_shadow_virtual_column.sql b/tests/queries/0_stateless/00578_merge_table_shadow_virtual_column.sql index 3071e307517..e729bfdf188 100644 --- a/tests/queries/0_stateless/00578_merge_table_shadow_virtual_column.sql +++ b/tests/queries/0_stateless/00578_merge_table_shadow_virtual_column.sql @@ -4,7 +4,7 @@ DROP TABLE IF EXISTS numbers2; CREATE TABLE numbers1 ENGINE = Memory AS SELECT number as _table FROM numbers(1000); CREATE TABLE numbers2 ENGINE = Memory AS SELECT number as _table FROM numbers(1000); -SELECT count() FROM merge(currentDatabase(), '^numbers\\d+$') WHERE _table='numbers1'; -- { serverError 43 } +SELECT count() FROM merge(currentDatabase(), '^numbers\\d+$') WHERE _table='numbers1'; -- { serverError 53 } SELECT count() FROM merge(currentDatabase(), '^numbers\\d+$') WHERE _table=1; DROP TABLE numbers1; diff --git a/tests/queries/0_stateless/00700_decimal_compare.reference b/tests/queries/0_stateless/00700_decimal_compare.reference index 32f0b0a6dea..2325847045f 100644 --- a/tests/queries/0_stateless/00700_decimal_compare.reference +++ b/tests/queries/0_stateless/00700_decimal_compare.reference @@ -1,3 +1,5 @@ +0 +1 -42 -42 1 0 0 0 1 1 42 42 1 0 0 0 1 1 -42 -42.42000 0 0 1 1 0 1 diff --git a/tests/queries/0_stateless/00700_decimal_compare.sql b/tests/queries/0_stateless/00700_decimal_compare.sql index 24b4ce588e5..ae2f5790570 100644 --- a/tests/queries/0_stateless/00700_decimal_compare.sql +++ b/tests/queries/0_stateless/00700_decimal_compare.sql @@ -19,7 +19,7 @@ INSERT INTO decimal (a, b, c, d, e, f, g, h, i, j) VALUES (-42, -42, -42, -0.42, SELECT a > toFloat64(0) FROM decimal; -- { serverError 43 } SELECT g > toFloat32(0) FROM decimal; -- { serverError 43 } -SELECT a > '0.0' FROM decimal; -- { serverError 43 } +SELECT a > '0.0' FROM decimal ORDER BY a; SELECT a, b, a = b, a < b, a > b, a != b, a <= b, a >= b FROM decimal ORDER BY a; SELECT a, g, a = g, a < g, a > g, a != g, a <= g, a >= g FROM decimal ORDER BY a; diff --git a/tests/queries/0_stateless/00746_sql_fuzzy.sh b/tests/queries/0_stateless/00746_sql_fuzzy.sh index f417f0146c2..539d0659d36 100755 --- a/tests/queries/0_stateless/00746_sql_fuzzy.sh +++ b/tests/queries/0_stateless/00746_sql_fuzzy.sh @@ -13,7 +13,7 @@ $CLICKHOUSE_CLIENT -q "select name from system.table_functions format TSV;" > $S # if you want long run use: env SQL_FUZZY_RUNS=100000 clickhouse-test sql_fuzzy for SQL_FUZZY_RUN in $(seq ${SQL_FUZZY_RUNS:=10}); do - env SQL_FUZZY_RUN=$SQL_FUZZY_RUN $CURDIR/00746_sql_fuzzy.pl | $CLICKHOUSE_CLIENT --max_execution_time 10 -n --ignore-error >/dev/null 2>&1 + env SQL_FUZZY_RUN=$SQL_FUZZY_RUN $CURDIR/00746_sql_fuzzy.pl | $CLICKHOUSE_CLIENT --format Null --max_execution_time 10 -n --ignore-error >/dev/null 2>&1 if [[ `$CLICKHOUSE_CLIENT -q "SELECT 'Still alive'"` != 'Still alive' ]]; then break fi diff --git a/tests/queries/0_stateless/00754_alter_modify_order_by_replicated_zookeeper.sql b/tests/queries/0_stateless/00754_alter_modify_order_by_replicated_zookeeper.sql index 96740d63778..c94c0f3c55b 100644 --- a/tests/queries/0_stateless/00754_alter_modify_order_by_replicated_zookeeper.sql +++ b/tests/queries/0_stateless/00754_alter_modify_order_by_replicated_zookeeper.sql @@ -43,7 +43,7 @@ DETACH TABLE test.summing_r2; ALTER TABLE test.summing_r1 ADD COLUMN t UInt32 AFTER z, MODIFY ORDER BY (x, y, t * t) SETTINGS replication_alter_partitions_sync = 2; -- { serverError 341 } ATTACH TABLE test.summing_r2; -SELECT sleep(1) Format Null; +SYSTEM SYNC REPLICA test.summing_r2; SELECT '*** Check SHOW CREATE TABLE after offline ALTER ***'; SHOW CREATE TABLE test.summing_r2; diff --git a/tests/queries/0_stateless/00813_parse_date_time_best_effort_more.reference b/tests/queries/0_stateless/00813_parse_date_time_best_effort_more.reference index 921400838d0..3acae55e846 100644 --- a/tests/queries/0_stateless/00813_parse_date_time_best_effort_more.reference +++ b/tests/queries/0_stateless/00813_parse_date_time_best_effort_more.reference @@ -1,22 +1,22 @@ -s a b + s a b -24.12.2018 2018-12-24 00:00:00 2018-12-24 00:00:00 -24-12-2018 2018-12-24 00:00:00 2018-12-24 00:00:00 -24.12.18 2018-12-24 00:00:00 2018-12-24 00:00:00 -24-12-18 2018-12-24 00:00:00 2018-12-24 00:00:00 -24-Dec-18 2018-12-24 00:00:00 2018-12-24 00:00:00 -24/DEC/18 2018-12-24 00:00:00 2018-12-24 00:00:00 -24/DEC/2018 2018-12-24 00:00:00 2018-12-24 00:00:00 -01-OCT-2015 2015-10-01 00:00:00 2015-10-01 00:00:00 -24.12.2018 2018-12-24 00:00:00 2018-12-24 00:00:00 -24-12-2018 2018-12-24 00:00:00 2018-12-24 00:00:00 -24.12.18 2018-12-24 00:00:00 2018-12-24 00:00:00 -24-12-18 2018-12-24 00:00:00 2018-12-24 00:00:00 -24-Dec-18 2018-12-24 00:00:00 2018-12-24 00:00:00 -24/DEC/18 2018-12-24 00:00:00 2018-12-24 00:00:00 -24/DEC/2018 2018-12-24 00:00:00 2018-12-24 00:00:00 -01-OCT-2015 2015-10-01 00:00:00 2015-10-01 00:00:00 -24.12.18 010203 2018-12-24 01:02:03 2018-12-24 01:02:03 -24.12.18 01:02:03 2018-12-24 01:02:03 2018-12-24 01:02:03 -24.DEC.18T01:02:03.000+0300 2018-12-23 22:02:03 2018-12-23 22:02:03 -01-September-2018 11:22 2018-09-01 11:22:00 2018-09-01 11:22:00 + 24.12.2018 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24-12-2018 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24.12.18 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24-12-18 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24-Dec-18 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24/DEC/18 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24/DEC/2018 2018-12-24 00:00:00 2018-12-24 00:00:00 + 01-OCT-2015 2015-10-01 00:00:00 2015-10-01 00:00:00 + 24.12.2018 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24-12-2018 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24.12.18 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24-12-18 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24-Dec-18 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24/DEC/18 2018-12-24 00:00:00 2018-12-24 00:00:00 + 24/DEC/2018 2018-12-24 00:00:00 2018-12-24 00:00:00 + 01-OCT-2015 2015-10-01 00:00:00 2015-10-01 00:00:00 + 24.12.18 010203 2018-12-24 01:02:03 2018-12-24 01:02:03 + 24.12.18 01:02:03 2018-12-24 01:02:03 2018-12-24 01:02:03 + 24.DEC.18T01:02:03.000+0300 2018-12-23 22:02:03 2018-12-23 22:02:03 + 01-September-2018 11:22 2018-09-01 11:22:00 2018-09-01 11:22:00 diff --git a/tests/queries/0_stateless/00972_geohashesInBox.reference b/tests/queries/0_stateless/00972_geohashesInBox.reference index e6844fa8394..92dab3cb04e 100644 --- a/tests/queries/0_stateless/00972_geohashesInBox.reference +++ b/tests/queries/0_stateless/00972_geohashesInBox.reference @@ -37,4 +37,6 @@ zooming ['s7w1z0gs3y0z','s7w1z0gs3y1p','s7w1z0gs3y1r','s7w1z0gs3y1x','s7w1z0gs3y2b','s7w1z0gs3y2c','s7w1z0gs3y2f','s7w1z0gs3y2g','s7w1z0gs3y2u','s7w1z0gs3y2v','s7w1z0gs3y30','s7w1z0gs3y31','s7w1z0gs3y32','s7w1z0gs3y33','s7w1z0gs3y34','s7w1z0gs3y35','s7w1z0gs3y36','s7w1z0gs3y37','s7w1z0gs3y38','s7w1z0gs3y39','s7w1z0gs3y3d','s7w1z0gs3y3e','s7w1z0gs3y3h','s7w1z0gs3y3j','s7w1z0gs3y3k','s7w1z0gs3y3m','s7w1z0gs3y3s','s7w1z0gs3y3t'] ['s7w1z0gs3y0z','s7w1z0gs3y1p','s7w1z0gs3y1r','s7w1z0gs3y1x','s7w1z0gs3y2b','s7w1z0gs3y2c','s7w1z0gs3y2f','s7w1z0gs3y2g','s7w1z0gs3y2u','s7w1z0gs3y2v','s7w1z0gs3y30','s7w1z0gs3y31','s7w1z0gs3y32','s7w1z0gs3y33','s7w1z0gs3y34','s7w1z0gs3y35','s7w1z0gs3y36','s7w1z0gs3y37','s7w1z0gs3y38','s7w1z0gs3y39','s7w1z0gs3y3d','s7w1z0gs3y3e','s7w1z0gs3y3h','s7w1z0gs3y3j','s7w1z0gs3y3k','s7w1z0gs3y3m','s7w1z0gs3y3s','s7w1z0gs3y3t'] ['s7w1z0gs3y0z','s7w1z0gs3y1p','s7w1z0gs3y1r','s7w1z0gs3y1x','s7w1z0gs3y2b','s7w1z0gs3y2c','s7w1z0gs3y2f','s7w1z0gs3y2g','s7w1z0gs3y2u','s7w1z0gs3y2v','s7w1z0gs3y30','s7w1z0gs3y31','s7w1z0gs3y32','s7w1z0gs3y33','s7w1z0gs3y34','s7w1z0gs3y35','s7w1z0gs3y36','s7w1z0gs3y37','s7w1z0gs3y38','s7w1z0gs3y39','s7w1z0gs3y3d','s7w1z0gs3y3e','s7w1z0gs3y3h','s7w1z0gs3y3j','s7w1z0gs3y3k','s7w1z0gs3y3m','s7w1z0gs3y3s','s7w1z0gs3y3t'] +input values are clamped to -90..90, -180..180 range +32768 errors diff --git a/tests/queries/0_stateless/00972_geohashesInBox.sql b/tests/queries/0_stateless/00972_geohashesInBox.sql index f382bf234ac..d52a03b055e 100644 --- a/tests/queries/0_stateless/00972_geohashesInBox.sql +++ b/tests/queries/0_stateless/00972_geohashesInBox.sql @@ -5,41 +5,46 @@ -- except for the cases when JS-version produces result outside of given region, -- typically at wrap points: poles, 0-latitude and 0-longitude. -select 'center'; +SELECT 'center'; SELECT arraySort(geohashesInBox(-1.0, -1.0, 1.0, 1.0, 3)); SELECT arraySort(geohashesInBox(-0.1, -0.1, 0.1, 0.1, 5)); SELECT arraySort(geohashesInBox(-0.01, -0.01, 0.01, 0.01, 5)); -select 'north pole'; +SELECT 'north pole'; SELECT arraySort(geohashesInBox(-180.0, 89.0, -179.0, 90.0, 3)); SELECT arraySort(geohashesInBox(-1.0, 89.0, 0.0, 90.0, 3)); SELECT arraySort(geohashesInBox(0.0, 89.0, 1.0, 90.0, 3)); SELECT arraySort(geohashesInBox(179.0, 89.0, 180.0, 90.0, 3)); -select 'south pole'; +SELECT 'south pole'; SELECT arraySort(geohashesInBox(-180.0, -90.0, -179.0, -89.0, 3)); SELECT arraySort(geohashesInBox(-1.0, -90.0, 0.0, -89.0, 3)); SELECT arraySort(geohashesInBox(0.0, -90.0, 1.0, -89.0, 3)); SELECT arraySort(geohashesInBox(179.0, -90.0, 180.0, -89.0, 3)); -select 'wrap point around equator'; +SELECT 'wrap point around equator'; SELECT arraySort(geohashesInBox(179.0, -1.0, 180.0, 0.0, 3)); SELECT arraySort(geohashesInBox(179.0, 0.0, 180.0, 1.0, 3)); SELECT arraySort(geohashesInBox(-180.0, -1.0, -179.0, 0.0, 3)); SELECT arraySort(geohashesInBox(-180.0, 0.0, -179.0, 1.0, 3)); -select 'arbitrary values in all 4 quarters'; +SELECT 'arbitrary values in all 4 quarters'; SELECT arraySort(geohashesInBox(98.36, 7.88, 98.37, 7.89, 6)); SELECT arraySort(geohashesInBox(53.8, 27.6, 53.9, 27.7, 5)); SELECT arraySort(geohashesInBox(-49.26, -25.38, -49.25, -25.37, 6)); SELECT arraySort(geohashesInBox(23.11, -82.37, 23.12, -82.36, 6)); -select 'small range always produces array of length 1'; -SELECT lon/5 - 180 as lon1, lat/5 - 90 as lat1, lon1 as lon2, lat1 as lat2, geohashesInBox(lon1, lat1, lon2, lat2, 1) as g FROM (SELECT arrayJoin(range(360*5)) as lon, arrayJoin(range(180*5)) as lat) WHERE length(g) != 1; -SELECT lon/5 - 40 as lon1, lat/5 - 20 as lat1, lon1 as lon2, lat1 as lat2, geohashesInBox(lon1, lat1, lon2, lat2, 12) as g FROM (SELECT arrayJoin(range(80*5)) as lon, arrayJoin(range(10*5)) as lat) WHERE length(g) != 1; -SELECT lon/5 - 40 as lon1, lat/5 - 20 as lat1, lon1 + 0.0000000001 as lon2, lat1 + 0.0000000001 as lat2, geohashesInBox(lon1, lat1, lon2, lat2, 1) as g FROM (SELECT arrayJoin(range(80*5)) as lon, arrayJoin(range(10*5)) as lat) WHERE length(g) != 1; +SELECT 'small range always produces array of length 1'; +SELECT lon/5 - 180 AS lon1, lat/5 - 90 AS lat1, lon1 AS lon2, lat1 AS lat2, geohashesInBox(lon1, lat1, lon2, lat2, 1) AS g +FROM (SELECT arrayJoin(range(360*5)) AS lon, arrayJoin(range(180*5)) AS lat) WHERE length(g) != 1; -select 'zooming'; +SELECT lon/5 - 40 AS lon1, lat/5 - 20 AS lat1, lon1 AS lon2, lat1 AS lat2, geohashesInBox(lon1, lat1, lon2, lat2, 12) AS g +FROM (SELECT arrayJoin(range(80*5)) AS lon, arrayJoin(range(10*5)) AS lat) WHERE length(g) != 1; + +SELECT lon/5 - 40 AS lon1, lat/5 - 20 AS lat1, lon1 + 0.0000000001 AS lon2, lat1 + 0.0000000001 AS lat2, geohashesInBox(lon1, lat1, lon2, lat2, 1) AS g +FROM (SELECT arrayJoin(range(80*5)) AS lon, arrayJoin(range(10*5)) AS lat) WHERE length(g) != 1; + +SELECT 'zooming'; SELECT arraySort(geohashesInBox(20.0, 20.0, 21.0, 21.0, 2)); SELECT arraySort(geohashesInBox(20.0, 20.0, 21.0, 21.0, 3)); SELECT arraySort(geohashesInBox(20.0, 20.0, 21.0, 21.0, 4)); @@ -56,8 +61,12 @@ SELECT arraySort(geohashesInBox(20.0, 20.0, 20.000001, 20.000001, 12)); SELECT arraySort(geohashesInBox(20.0, 20.0, 20.000001, 20.000001, 13)); SELECT arraySort(geohashesInBox(20.0, 20.0, 20.000001, 20.000001, 14)); -select 'errors'; +SELECT 'input values are clamped to -90..90, -180..180 range'; +SELECT length(geohashesInBox(-inf, -inf, inf, inf, 3)); + +SELECT 'errors'; SELECT geohashesInBox(); -- { serverError 42 } -- not enough arguments SELECT geohashesInBox(1, 2, 3, 4, 5); -- { serverError 43 } -- wrong types of arguments SELECT geohashesInBox(toFloat32(1.0), 2.0, 3.0, 4.0, 5); -- { serverError 43 } -- all lats and longs should be of the same type SELECT geohashesInBox(24.48, 40.56, 24.785, 40.81, 12); -- { serverError 128 } -- to many elements in array + diff --git a/tests/queries/0_stateless/00973_live_view_select_prewhere.reference b/tests/queries/0_stateless/00973_live_view_select_prewhere.reference new file mode 100644 index 00000000000..3a6fe59ae6d --- /dev/null +++ b/tests/queries/0_stateless/00973_live_view_select_prewhere.reference @@ -0,0 +1,2 @@ +5 1 +10 2 diff --git a/tests/queries/0_stateless/00973_live_view_select_prewhere.sql b/tests/queries/0_stateless/00973_live_view_select_prewhere.sql new file mode 100644 index 00000000000..df3b7cb505a --- /dev/null +++ b/tests/queries/0_stateless/00973_live_view_select_prewhere.sql @@ -0,0 +1,26 @@ +SET allow_experimental_live_view = 1; + +DROP TABLE IF EXISTS lv; +DROP TABLE IF EXISTS lv2; +DROP TABLE IF EXISTS mt; + +CREATE TABLE mt (a Int32) Engine=MergeTree order by tuple(); +CREATE LIVE VIEW lv AS SELECT sum(a) AS sum_a FROM mt PREWHERE a > 1; +CREATE LIVE VIEW lv2 AS SELECT sum(number) AS sum_number FROM system.numbers PREWHERE number > 1; + +INSERT INTO mt VALUES (1),(2),(3); + +SELECT *,_version FROM lv; +SELECT *,_version FROM lv PREWHERE sum_a > 5; -- { serverError 182 } + +INSERT INTO mt VALUES (1),(2),(3); + +SELECT *,_version FROM lv; +SELECT *,_version FROM lv PREWHERE sum_a > 10; -- { serverError 182 } + +SELECT *,_version FROM lv2; -- { serverError 182 } +SELECT *,_version FROM lv2 PREWHERE sum_number > 10; -- { serverError 182 } + +DROP TABLE lv; +DROP TABLE lv2; +DROP TABLE mt; diff --git a/tests/queries/0_stateless/00981_no_virtual_columns.reference b/tests/queries/0_stateless/00981_no_virtual_columns.reference index a7ec77dc030..587be6b4c3f 100644 --- a/tests/queries/0_stateless/00981_no_virtual_columns.reference +++ b/tests/queries/0_stateless/00981_no_virtual_columns.reference @@ -1 +1 @@ -default merge_ab x UInt8 0 0 0 0 0 0 0 +x diff --git a/tests/queries/0_stateless/00981_no_virtual_columns.sql b/tests/queries/0_stateless/00981_no_virtual_columns.sql index 476377b4ddf..b3946154581 100644 --- a/tests/queries/0_stateless/00981_no_virtual_columns.sql +++ b/tests/queries/0_stateless/00981_no_virtual_columns.sql @@ -6,7 +6,7 @@ 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]$'); -SELECT * FROM system.columns WHERE database = currentDatabase() AND table = 'merge_ab'; +SELECT name FROM system.columns WHERE database = currentDatabase() AND table = 'merge_ab'; DROP TABLE merge_a; DROP TABLE merge_b; diff --git a/tests/queries/0_stateless/01079_parallel_alter_add_drop_column_zookeeper.sh b/tests/queries/0_stateless/01079_parallel_alter_add_drop_column_zookeeper.sh index 37ed463f59b..dbd53d6d0b7 100755 --- a/tests/queries/0_stateless/01079_parallel_alter_add_drop_column_zookeeper.sh +++ b/tests/queries/0_stateless/01079_parallel_alter_add_drop_column_zookeeper.sh @@ -97,7 +97,7 @@ done echo "Equal number of columns" # This alter will finish all previous, but replica 1 maybe still not up-to-date -while [[ $(timeout 30 $CLICKHOUSE_CLIENT --query "ALTER TABLE concurrent_alter_add_drop_1 MODIFY COLUMN value0 String SETTINGS replication_alter_partitions_sync=2" 2>&1) ]]; do +while [[ $(timeout 120 $CLICKHOUSE_CLIENT --query "ALTER TABLE concurrent_alter_add_drop_1 MODIFY COLUMN value0 String SETTINGS replication_alter_partitions_sync=2" 2>&1) ]]; do sleep 1 done diff --git a/tests/queries/0_stateless/01079_parallel_alter_detach_table_zookeeper.sh b/tests/queries/0_stateless/01079_parallel_alter_detach_table_zookeeper.sh index 114008ded26..90172d38cfb 100755 --- a/tests/queries/0_stateless/01079_parallel_alter_detach_table_zookeeper.sh +++ b/tests/queries/0_stateless/01079_parallel_alter_detach_table_zookeeper.sh @@ -96,8 +96,12 @@ done # This alter will finish all previous, but replica 1 maybe still not up-to-date -while [[ $(timeout 30 $CLICKHOUSE_CLIENT --query "ALTER TABLE concurrent_alter_detach_1 MODIFY COLUMN value1 String SETTINGS replication_alter_partitions_sync=2" 2>&1) ]]; do +while [[ $(timeout 120 $CLICKHOUSE_CLIENT --query "ALTER TABLE concurrent_alter_detach_1 MODIFY COLUMN value1 String SETTINGS replication_alter_partitions_sync=2" 2>&1) ]]; do sleep 1 + # just try to attach table if it failed for some reason in the code above + for i in `seq $REPLICAS`; do + $CLICKHOUSE_CLIENT --query "ATTACH TABLE concurrent_alter_detach_$i" 2> /dev/null + done done for i in `seq $REPLICAS`; do diff --git a/tests/queries/0_stateless/01079_parallel_alter_modify_zookeeper.sh b/tests/queries/0_stateless/01079_parallel_alter_modify_zookeeper.sh index bacc742d16a..05ef4a1a675 100755 --- a/tests/queries/0_stateless/01079_parallel_alter_modify_zookeeper.sh +++ b/tests/queries/0_stateless/01079_parallel_alter_modify_zookeeper.sh @@ -100,8 +100,14 @@ wait echo "Finishing alters" -# This alter will finish all previous, but replica 1 maybe still not up-to-date -while [[ $(timeout 30 $CLICKHOUSE_CLIENT --query "ALTER TABLE concurrent_alter_mt_1 MODIFY COLUMN value1 String SETTINGS replication_alter_partitions_sync=2" 2>&1) ]]; do +# This alter will finish all previous, but replica 1 maybe still not up-to-date. +# If query will throw something, than we will sleep 1 and retry. If timeout +# happened we will silently go out of loop and probably fail tests in the +# following for loop. +# +# 120 seconds is more than enough, but in rare cases for slow builds (debug, +# thread) it maybe necessary. +while [[ $(timeout 120 $CLICKHOUSE_CLIENT --query "ALTER TABLE concurrent_alter_mt_1 MODIFY COLUMN value1 String SETTINGS replication_alter_partitions_sync=2" 2>&1) ]]; do sleep 1 done diff --git a/tests/queries/0_stateless/01087_table_function_generate.sql b/tests/queries/0_stateless/01087_table_function_generate.sql index 96db6803a47..05f03a5a4e6 100644 --- a/tests/queries/0_stateless/01087_table_function_generate.sql +++ b/tests/queries/0_stateless/01087_table_function_generate.sql @@ -33,11 +33,11 @@ LIMIT 10; SELECT '-'; SELECT toTypeName(i)s -FROM generateRandom('i Nullable(Enum16(\'h\' = 1, \'w\' = 5 , \'o\' = -200)))') +FROM generateRandom('i Nullable(Enum16(\'h\' = 1, \'w\' = 5 , \'o\' = -200))') LIMIT 1; SELECT i -FROM generateRandom('i Nullable(Enum16(\'h\' = 1, \'w\' = 5 , \'o\' = -200)))', 1, 10, 10) +FROM generateRandom('i Nullable(Enum16(\'h\' = 1, \'w\' = 5 , \'o\' = -200))', 1, 10, 10) LIMIT 10; SELECT '-'; SELECT diff --git a/tests/queries/0_stateless/01098_temporary_and_external_tables.sh b/tests/queries/0_stateless/01098_temporary_and_external_tables.sh index c984f363c31..b671019ca35 100755 --- a/tests/queries/0_stateless/01098_temporary_and_external_tables.sh +++ b/tests/queries/0_stateless/01098_temporary_and_external_tables.sh @@ -3,7 +3,8 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) . $CURDIR/../shell_config.sh -url="https://${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT_HTTPS}/?session_id=test_01098" +url_without_session="https://${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT_HTTPS}/?" +url="${url_without_session}session_id=test_01098" ${CLICKHOUSE_CURL} -m 30 -sSk "$url" --data "CREATE TEMPORARY TABLE tmp_table AS SELECT number AS n FROM numbers(42)" > /dev/null; @@ -14,3 +15,11 @@ echo "SELECT * FROM $full_tmp_name" | ${CLICKHOUSE_CURL} -m 60 -sSgk $url -d @- echo -ne '0\n1\n' | ${CLICKHOUSE_CURL} -m 30 -sSkF 'file=@-' "$url&file_format=CSV&file_types=UInt64&query=SELECT+sum((number+GLOBAL+IN+(SELECT+number+AS+n+FROM+remote('127.0.0.2',+numbers(5))+WHERE+n+GLOBAL+IN+(SELECT+*+FROM+tmp_table)+AND+n+GLOBAL+NOT+IN+(SELECT+*+FROM+file)+))+AS+res),+sum(number*res)+FROM+remote('127.0.0.2',+numbers(10))"; +echo -ne '0\n1\n' | ${CLICKHOUSE_CURL} -m 30 -sSkF 'file=@-' "$url&file_format=CSV&file_types=UInt64&query=SELECT+sleepEachRow(3)+FROM+file" > /dev/null & +sleep 1 +full_tmp_names=`echo "SELECT $name_expr FROM system.tables WHERE database='_temporary_and_external_tables' FORMAT TSV" | ${CLICKHOUSE_CURL} -m 30 -sSgk $url_without_session -d @-` +for name in $full_tmp_names +do + ${CLICKHOUSE_CURL} -m 30 -sSk "${url_without_session}query=SHOW+CREATE+TABLE+$name" 1>/dev/null 2>/dev/null +done; +wait diff --git a/tests/queries/0_stateless/01108_restart_replicas_rename_deadlock.sh b/tests/queries/0_stateless/01108_restart_replicas_rename_deadlock.sh index 65b738aed8e..a2fce893672 100755 --- a/tests/queries/0_stateless/01108_restart_replicas_rename_deadlock.sh +++ b/tests/queries/0_stateless/01108_restart_replicas_rename_deadlock.sh @@ -61,13 +61,16 @@ timeout $TIMEOUT bash -c restart_thread_1 2> /dev/null & timeout $TIMEOUT bash -c restart_thread_2 2> /dev/null & wait -sleep 3 for i in `seq 4`; do $CLICKHOUSE_CLIENT -q "SYSTEM SYNC REPLICA replica_01108_$i" >/dev/null 2>&1 $CLICKHOUSE_CLIENT -q "SYSTEM SYNC REPLICA replica_01108_${i}_tmp" >/dev/null 2>&1 done +while [[ `$CLICKHOUSE_CLIENT -q "SELECT count() FROM system.processes WHERE query LIKE 'RENAME%'"` -gt 0 ]]; do + sleep 1 +done; + $CLICKHOUSE_CLIENT -q "SELECT replaceOne(name, '_tmp', '') FROM system.tables WHERE database = currentDatabase() AND match(name, '^replica_01108_')" $CLICKHOUSE_CLIENT -q "SELECT sum(n), count(n) FROM merge(currentDatabase(), '^replica_01108_') GROUP BY position(_table, 'tmp')" diff --git a/tests/queries/0_stateless/01140_select_from_storage_join_fix.reference b/tests/queries/0_stateless/01140_select_from_storage_join_fix.reference new file mode 100644 index 00000000000..101a270ad39 --- /dev/null +++ b/tests/queries/0_stateless/01140_select_from_storage_join_fix.reference @@ -0,0 +1,8 @@ +1 s 1 String String +2 s 2 String String +3 s 3 Nullable(String) String +4 s 4 String Nullable(String) +1 s 1 String String +2 s 2 String String +3 s 3 Nullable(String) String +4 s 4 String Nullable(String) diff --git a/tests/queries/0_stateless/01140_select_from_storage_join_fix.sql b/tests/queries/0_stateless/01140_select_from_storage_join_fix.sql new file mode 100644 index 00000000000..4e64c90f56d --- /dev/null +++ b/tests/queries/0_stateless/01140_select_from_storage_join_fix.sql @@ -0,0 +1,42 @@ +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; +DROP TABLE IF EXISTS t3; +DROP TABLE IF EXISTS t4; + +CREATE TABLE t1 (id String, name String, value UInt32) +ENGINE = Join(ANY, LEFT, id) +SETTINGS join_use_nulls = 1; + +CREATE TABLE t2 (id String, name String, value UInt32) +ENGINE = Join(ANY, LEFT, id) +SETTINGS join_use_nulls = 0; + +CREATE TABLE t3 (id Nullable(String), name String, value UInt32) +ENGINE = Join(ANY, LEFT, id) +SETTINGS join_use_nulls = 1; + +CREATE TABLE t4 (id String, name Nullable(String), value UInt32) +ENGINE = Join(ANY, LEFT, id) +SETTINGS join_use_nulls = 0; + +insert into t1 values('1', 's', 1); +insert into t2 values('2', 's', 2); +insert into t3 values('3', 's', 3); +insert into t4 values('4', 's', 4); + +select *, toTypeName(id), toTypeName(name) from t1; +select *, toTypeName(id), toTypeName(name) from t2; +select *, toTypeName(id), toTypeName(name) from t3; +select *, toTypeName(id), toTypeName(name) from t4; + +SET join_use_nulls = 1; + +select *, toTypeName(id), toTypeName(name) from t1; +select *, toTypeName(id), toTypeName(name) from t2; +select *, toTypeName(id), toTypeName(name) from t3; +select *, toTypeName(id), toTypeName(name) from t4; + +DROP TABLE t1; +DROP TABLE t2; +DROP TABLE t3; +DROP TABLE t4; diff --git a/tests/integration/test_s3_with_proxy/proxy-resolver/__init__.py b/tests/queries/0_stateless/01141_join_get_negative.reference similarity index 100% rename from tests/integration/test_s3_with_proxy/proxy-resolver/__init__.py rename to tests/queries/0_stateless/01141_join_get_negative.reference diff --git a/tests/queries/0_stateless/01141_join_get_negative.sql b/tests/queries/0_stateless/01141_join_get_negative.sql new file mode 100644 index 00000000000..e165d34e460 --- /dev/null +++ b/tests/queries/0_stateless/01141_join_get_negative.sql @@ -0,0 +1,11 @@ +DROP TABLE IF EXISTS t1; +DROP TABLE IF EXISTS t2; + +CREATE TABLE t1 (`s` String, `x` Array(UInt8), `k` UInt64) ENGINE = Join(ANY, LEFT, k); +CREATE TABLE t2 (`s` String, `x` Array(UInt8), `k` UInt64) ENGINE = Join(ANY, INNER, k); + +SELECT joinGet('t1', '', number) FROM numbers(2); -- { serverError 16 } +SELECT joinGet('t2', 's', number) FROM numbers(2); -- { serverError 264 } + +DROP TABLE t1; +DROP TABLE t2; diff --git a/tests/queries/0_stateless/01142_join_lc_and_nullable_in_key.reference b/tests/queries/0_stateless/01142_join_lc_and_nullable_in_key.reference new file mode 100644 index 00000000000..d1b29b46df6 --- /dev/null +++ b/tests/queries/0_stateless/01142_join_lc_and_nullable_in_key.reference @@ -0,0 +1,29 @@ +1 l \N Nullable(String) +2 \N Nullable(String) +1 l \N Nullable(String) +2 \N Nullable(String) +- +1 l \N Nullable(String) +0 \N Nullable(String) +0 \N Nullable(String) +1 l \N Nullable(String) +- +1 l \N Nullable(String) +0 \N Nullable(String) +0 \N Nullable(String) +1 l \N Nullable(String) +- +1 l \N Nullable(String) +2 \N Nullable(String) +1 l \N Nullable(String) +2 \N Nullable(String) +- +1 l \N Nullable(String) +\N \N Nullable(String) +1 l \N Nullable(String) +\N \N Nullable(String) +- +1 l \N Nullable(String) +\N \N Nullable(String) +1 l \N Nullable(String) +\N \N Nullable(String) diff --git a/tests/queries/0_stateless/01142_join_lc_and_nullable_in_key.sql b/tests/queries/0_stateless/01142_join_lc_and_nullable_in_key.sql new file mode 100644 index 00000000000..edaf2870e89 --- /dev/null +++ b/tests/queries/0_stateless/01142_join_lc_and_nullable_in_key.sql @@ -0,0 +1,50 @@ +DROP TABLE IF EXISTS t; +DROP TABLE IF EXISTS nr; + +CREATE TABLE t (`x` UInt32, `lc` LowCardinality(String)) ENGINE = Memory; +CREATE TABLE nr (`x` Nullable(UInt32), `lc` Nullable(String)) ENGINE = Memory; + +INSERT INTO t VALUES (1, 'l'); +INSERT INTO nr VALUES (2, NULL); + +SET join_use_nulls = 0; + +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l LEFT JOIN nr AS r USING (x) ORDER BY x; +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l RIGHT JOIN nr AS r USING (x) ORDER BY x; +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l FULL JOIN nr AS r USING (x) ORDER BY x; + +SELECT '-'; + +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l LEFT JOIN nr AS r USING (lc) ORDER BY x; +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l RIGHT JOIN nr AS r USING (lc) ORDER BY x; +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l FULL JOIN nr AS r USING (lc) ORDER BY x; + +SELECT '-'; + +SELECT x, lc, materialize(r.lc) y, toTypeName(y) FROM t AS l LEFT JOIN nr AS r USING (lc) ORDER BY x; +SELECT x, lc, materialize(r.lc) y, toTypeName(y) FROM t AS l RIGHT JOIN nr AS r USING (lc) ORDER BY x; +SELECT x, lc, materialize(r.lc) y, toTypeName(y) FROM t AS l FULL JOIN nr AS r USING (lc) ORDER BY x; + +SELECT '-'; + +SET join_use_nulls = 1; + +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l LEFT JOIN nr AS r USING (x) ORDER BY x; +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l RIGHT JOIN nr AS r USING (x) ORDER BY x; +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l FULL JOIN nr AS r USING (x) ORDER BY x; + +SELECT '-'; + +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l LEFT JOIN nr AS r USING (lc) ORDER BY x; +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l RIGHT JOIN nr AS r USING (lc) ORDER BY x; +SELECT x, lc, r.lc, toTypeName(r.lc) FROM t AS l FULL JOIN nr AS r USING (lc) ORDER BY x; + +SELECT '-'; + +SELECT x, lc, materialize(r.lc) y, toTypeName(y) FROM t AS l LEFT JOIN nr AS r USING (lc) ORDER BY x; +SELECT x, lc, materialize(r.lc) y, toTypeName(y) FROM t AS l RIGHT JOIN nr AS r USING (lc) ORDER BY x; +SELECT x, lc, materialize(r.lc) y, toTypeName(y) FROM t AS l FULL JOIN nr AS r USING (lc) ORDER BY x; + + +DROP TABLE t; +DROP TABLE nr; diff --git a/tests/queries/0_stateless/01143_trivial_count_with_join.reference b/tests/queries/0_stateless/01143_trivial_count_with_join.reference new file mode 100644 index 00000000000..9c3f6a570ce --- /dev/null +++ b/tests/queries/0_stateless/01143_trivial_count_with_join.reference @@ -0,0 +1,5 @@ +4 +4 +4 +4 +4 diff --git a/tests/queries/0_stateless/01143_trivial_count_with_join.sql b/tests/queries/0_stateless/01143_trivial_count_with_join.sql new file mode 100644 index 00000000000..d31750e37dc --- /dev/null +++ b/tests/queries/0_stateless/01143_trivial_count_with_join.sql @@ -0,0 +1,10 @@ +drop table if exists t; +create table t engine Memory as select * from numbers(2); + +select count(*) from t, numbers(2) r; +select count(*) from t cross join numbers(2) r; +select count() from t cross join numbers(2) r; +select count(t.number) from t cross join numbers(2) r; +select count(r.number) from t cross join numbers(2) r; + +drop table t; diff --git a/tests/queries/0_stateless/01194_http_query_id.reference b/tests/queries/0_stateless/01194_http_query_id.reference new file mode 100644 index 00000000000..b8626c4cff2 --- /dev/null +++ b/tests/queries/0_stateless/01194_http_query_id.reference @@ -0,0 +1 @@ +4 diff --git a/tests/queries/0_stateless/01194_http_query_id.sh b/tests/queries/0_stateless/01194_http_query_id.sh new file mode 100755 index 00000000000..381ae67f88f --- /dev/null +++ b/tests/queries/0_stateless/01194_http_query_id.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +url="http://${CLICKHOUSE_HOST}:${CLICKHOUSE_PORT_HTTP}/?session_id=test_01194" +rnd=$RANDOM + +${CLICKHOUSE_CURL} -sS "$url&query=SELECT+$rnd,1" > /dev/null +${CLICKHOUSE_CURL} -sS "$url&query=SELECT+$rnd,2" > /dev/null +${CLICKHOUSE_CURL} -sS "$url" --data "SELECT $rnd,3" > /dev/null +${CLICKHOUSE_CURL} -sS "$url" --data "SELECT $rnd,4" > /dev/null + +${CLICKHOUSE_CURL} -sS "$url" --data "SYSTEM FLUSH LOGS" + +${CLICKHOUSE_CURL} -sS "$url&query=SELECT+count(DISTINCT+query_id)+FROM+system.query_log+WHERE+query+LIKE+'SELECT+$rnd%25'" diff --git a/tests/queries/0_stateless/01246_extractAllGroups.sql b/tests/queries/0_stateless/01246_extractAllGroups.sql deleted file mode 100644 index ade52117d76..00000000000 --- a/tests/queries/0_stateless/01246_extractAllGroups.sql +++ /dev/null @@ -1,51 +0,0 @@ --- error cases -SELECT extractAllGroups(); --{serverError 42} not enough arguments -SELECT extractAllGroups('hello'); --{serverError 42} not enough arguments -SELECT extractAllGroups('hello', 123); --{serverError 43} invalid argument type -SELECT extractAllGroups(123, 'world'); --{serverError 43} invalid argument type -SELECT extractAllGroups('hello world', '((('); --{serverError 427} invalid re -SELECT extractAllGroups('hello world', materialize('\\w+')); --{serverError 44} non-const needle - -SELECT '0 groups, zero matches'; -SELECT extractAllGroups('hello world', '\\w+'); -- { serverError 36 } - -SELECT '1 group, multiple matches, String and FixedString'; -SELECT extractAllGroups('hello world', '(\\w+)'); -SELECT extractAllGroups('hello world', CAST('(\\w+)' as FixedString(5))); -SELECT extractAllGroups(CAST('hello world' AS FixedString(12)), '(\\w+)'); -SELECT extractAllGroups(CAST('hello world' AS FixedString(12)), CAST('(\\w+)' as FixedString(5))); -SELECT extractAllGroups(materialize(CAST('hello world' AS FixedString(12))), '(\\w+)'); -SELECT extractAllGroups(materialize(CAST('hello world' AS FixedString(12))), CAST('(\\w+)' as FixedString(5))); - -SELECT 'mutiple groups, multiple matches'; -SELECT extractAllGroups('abc=111, def=222, ghi=333 "jkl mno"="444 foo bar"', '("[^"]+"|\\w+)=("[^"]+"|\\w+)'); - -SELECT 'big match'; -SELECT - length(haystack), length(matches[1]), length(matches), arrayMap((x) -> length(x), arrayMap(x -> x[1], matches)) -FROM ( - SELECT - repeat('abcdefghijklmnopqrstuvwxyz', number * 10) AS haystack, - extractAllGroups(haystack, '(abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz)') AS matches - FROM numbers(3) -); - -SELECT 'lots of matches'; -SELECT - length(haystack), length(matches[1]), length(matches), arrayReduce('sum', arrayMap((x) -> length(x), arrayMap(x -> x[1], matches))) -FROM ( - SELECT - repeat('abcdefghijklmnopqrstuvwxyz', number * 10) AS haystack, - extractAllGroups(haystack, '(\\w)') AS matches - FROM numbers(3) -); - -SELECT 'lots of groups'; -SELECT - length(haystack), length(matches[1]), length(matches), arrayMap((x) -> length(x), arrayMap(x -> x[1], matches)) -FROM ( - SELECT - repeat('abcdefghijklmnopqrstuvwxyz', number * 10) AS haystack, - extractAllGroups(haystack, repeat('(\\w)', 100)) AS matches - FROM numbers(3) -); diff --git a/tests/queries/0_stateless/01246_extractAllGroupsHorizontal.reference b/tests/queries/0_stateless/01246_extractAllGroupsHorizontal.reference new file mode 100644 index 00000000000..13e717485d8 --- /dev/null +++ b/tests/queries/0_stateless/01246_extractAllGroupsHorizontal.reference @@ -0,0 +1,22 @@ +0 groups, zero matches +1 group, multiple matches, String and FixedString +[['hello','world']] +[['hello','world']] +[['hello','world']] +[['hello','world']] +[['hello','world']] +[['hello','world']] +mutiple groups, multiple matches +[['abc','def','ghi','"jkl mno"'],['111','222','333','"444 foo bar"']] +big match +0 1 0 [] +260 1 1 [156] +520 1 3 [156,156,156] +lots of matches +0 1 0 0 +260 1 260 260 +520 1 520 520 +lots of groups +0 100 0 [] +260 100 2 [1,1] +520 100 5 [1,1,1,1,1] diff --git a/tests/queries/0_stateless/01246_extractAllGroupsHorizontal.sql b/tests/queries/0_stateless/01246_extractAllGroupsHorizontal.sql new file mode 100644 index 00000000000..b7a71415a9d --- /dev/null +++ b/tests/queries/0_stateless/01246_extractAllGroupsHorizontal.sql @@ -0,0 +1,51 @@ +-- error cases +SELECT extractAllGroupsHorizontal(); --{serverError 42} not enough arguments +SELECT extractAllGroupsHorizontal('hello'); --{serverError 42} not enough arguments +SELECT extractAllGroupsHorizontal('hello', 123); --{serverError 43} invalid argument type +SELECT extractAllGroupsHorizontal(123, 'world'); --{serverError 43} invalid argument type +SELECT extractAllGroupsHorizontal('hello world', '((('); --{serverError 427} invalid re +SELECT extractAllGroupsHorizontal('hello world', materialize('\\w+')); --{serverError 44} non-cons needle + +SELECT '0 groups, zero matches'; +SELECT extractAllGroupsHorizontal('hello world', '\\w+'); -- { serverError 36 } + +SELECT '1 group, multiple matches, String and FixedString'; +SELECT extractAllGroupsHorizontal('hello world', '(\\w+)'); +SELECT extractAllGroupsHorizontal('hello world', CAST('(\\w+)' as FixedString(5))); +SELECT extractAllGroupsHorizontal(CAST('hello world' AS FixedString(12)), '(\\w+)'); +SELECT extractAllGroupsHorizontal(CAST('hello world' AS FixedString(12)), CAST('(\\w+)' as FixedString(5))); +SELECT extractAllGroupsHorizontal(materialize(CAST('hello world' AS FixedString(12))), '(\\w+)'); +SELECT extractAllGroupsHorizontal(materialize(CAST('hello world' AS FixedString(12))), CAST('(\\w+)' as FixedString(5))); + +SELECT 'mutiple groups, multiple matches'; +SELECT extractAllGroupsHorizontal('abc=111, def=222, ghi=333 "jkl mno"="444 foo bar"', '("[^"]+"|\\w+)=("[^"]+"|\\w+)'); + +SELECT 'big match'; +SELECT + length(haystack), length(matches), length(matches[1]), arrayMap((x) -> length(x), matches[1]) +FROM ( + SELECT + repeat('abcdefghijklmnopqrstuvwxyz', number * 10) AS haystack, + extractAllGroupsHorizontal(haystack, '(abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz)') AS matches + FROM numbers(3) +); + +SELECT 'lots of matches'; +SELECT + length(haystack), length(matches), length(matches[1]), arrayReduce('sum', arrayMap((x) -> length(x), matches[1])) +FROM ( + SELECT + repeat('abcdefghijklmnopqrstuvwxyz', number * 10) AS haystack, + extractAllGroupsHorizontal(haystack, '(\\w)') AS matches + FROM numbers(3) +); + +SELECT 'lots of groups'; +SELECT + length(haystack), length(matches), length(matches[1]), arrayMap((x) -> length(x), matches[1]) +FROM ( + SELECT + repeat('abcdefghijklmnopqrstuvwxyz', number * 10) AS haystack, + extractAllGroupsHorizontal(haystack, repeat('(\\w)', 100)) AS matches + FROM numbers(3) +); diff --git a/tests/queries/0_stateless/01246_extractAllGroups.reference b/tests/queries/0_stateless/01246_extractAllGroupsVertical.reference similarity index 100% rename from tests/queries/0_stateless/01246_extractAllGroups.reference rename to tests/queries/0_stateless/01246_extractAllGroupsVertical.reference diff --git a/tests/queries/0_stateless/01246_extractAllGroupsVertical.sql b/tests/queries/0_stateless/01246_extractAllGroupsVertical.sql new file mode 100644 index 00000000000..8edc3f3e741 --- /dev/null +++ b/tests/queries/0_stateless/01246_extractAllGroupsVertical.sql @@ -0,0 +1,51 @@ +-- error cases +SELECT extractAllGroupsVertical(); --{serverError 42} not enough arguments +SELECT extractAllGroupsVertical('hello'); --{serverError 42} not enough arguments +SELECT extractAllGroupsVertical('hello', 123); --{serverError 43} invalid argument type +SELECT extractAllGroupsVertical(123, 'world'); --{serverError 43} invalid argument type +SELECT extractAllGroupsVertical('hello world', '((('); --{serverError 427} invalid re +SELECT extractAllGroupsVertical('hello world', materialize('\\w+')); --{serverError 44} non-const needle + +SELECT '0 groups, zero matches'; +SELECT extractAllGroupsVertical('hello world', '\\w+'); -- { serverError 36 } + +SELECT '1 group, multiple matches, String and FixedString'; +SELECT extractAllGroupsVertical('hello world', '(\\w+)'); +SELECT extractAllGroupsVertical('hello world', CAST('(\\w+)' as FixedString(5))); +SELECT extractAllGroupsVertical(CAST('hello world' AS FixedString(12)), '(\\w+)'); +SELECT extractAllGroupsVertical(CAST('hello world' AS FixedString(12)), CAST('(\\w+)' as FixedString(5))); +SELECT extractAllGroupsVertical(materialize(CAST('hello world' AS FixedString(12))), '(\\w+)'); +SELECT extractAllGroupsVertical(materialize(CAST('hello world' AS FixedString(12))), CAST('(\\w+)' as FixedString(5))); + +SELECT 'mutiple groups, multiple matches'; +SELECT extractAllGroupsVertical('abc=111, def=222, ghi=333 "jkl mno"="444 foo bar"', '("[^"]+"|\\w+)=("[^"]+"|\\w+)'); + +SELECT 'big match'; +SELECT + length(haystack), length(matches[1]), length(matches), arrayMap((x) -> length(x), arrayMap(x -> x[1], matches)) +FROM ( + SELECT + repeat('abcdefghijklmnopqrstuvwxyz', number * 10) AS haystack, + extractAllGroupsVertical(haystack, '(abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz)') AS matches + FROM numbers(3) +); + +SELECT 'lots of matches'; +SELECT + length(haystack), length(matches[1]), length(matches), arrayReduce('sum', arrayMap((x) -> length(x), arrayMap(x -> x[1], matches))) +FROM ( + SELECT + repeat('abcdefghijklmnopqrstuvwxyz', number * 10) AS haystack, + extractAllGroupsVertical(haystack, '(\\w)') AS matches + FROM numbers(3) +); + +SELECT 'lots of groups'; +SELECT + length(haystack), length(matches[1]), length(matches), arrayMap((x) -> length(x), arrayMap(x -> x[1], matches)) +FROM ( + SELECT + repeat('abcdefghijklmnopqrstuvwxyz', number * 10) AS haystack, + extractAllGroupsVertical(haystack, repeat('(\\w)', 100)) AS matches + FROM numbers(3) +); diff --git a/tests/queries/0_stateless/01249_bad_arguments_for_bloom_filter.reference b/tests/queries/0_stateless/01249_bad_arguments_for_bloom_filter.reference index dfff8c7be00..70d176d9b7a 100644 --- a/tests/queries/0_stateless/01249_bad_arguments_for_bloom_filter.reference +++ b/tests/queries/0_stateless/01249_bad_arguments_for_bloom_filter.reference @@ -1,3 +1,3 @@ -CREATE TABLE default.bloom_filter_idx_good\n(\n `u64` UInt64,\n `i32` Int32,\n `f64` Float64,\n `d` Decimal(10, 2),\n `s` String,\n `e` Enum8(\'a\' = 1, \'b\' = 2, \'c\' = 3),\n `dt` Date,\n INDEX bloom_filter_a i32 TYPE bloom_filter(0.) GRANULARITY 1\n)\nENGINE = MergeTree()\nORDER BY u64\nSETTINGS index_granularity = 8192 -CREATE TABLE default.bloom_filter_idx_good\n(\n `u64` UInt64,\n `i32` Int32,\n `f64` Float64,\n `d` Decimal(10, 2),\n `s` String,\n `e` Enum8(\'a\' = 1, \'b\' = 2, \'c\' = 3),\n `dt` Date,\n INDEX bloom_filter_a i32 TYPE bloom_filter(0.) GRANULARITY 1\n)\nENGINE = MergeTree()\nORDER BY u64\nSETTINGS index_granularity = 8192 -CREATE TABLE default.bloom_filter_idx_good\n(\n `u64` UInt64,\n `i32` Int32,\n `f64` Float64,\n `d` Decimal(10, 2),\n `s` String,\n `e` Enum8(\'a\' = 1, \'b\' = 2, \'c\' = 3),\n `dt` Date,\n INDEX bloom_filter_a i32 TYPE bloom_filter(1.) GRANULARITY 1\n)\nENGINE = MergeTree()\nORDER BY u64\nSETTINGS index_granularity = 8192 +CREATE TABLE default.bloom_filter_idx_good\n(\n `u64` UInt64, \n `i32` Int32, \n `f64` Float64, \n `d` Decimal(10, 2), \n `s` String, \n `e` Enum8(\'a\' = 1, \'b\' = 2, \'c\' = 3), \n `dt` Date, \n INDEX bloom_filter_a i32 TYPE bloom_filter(0., 1.) GRANULARITY 1\n)\nENGINE = MergeTree()\nORDER BY u64\nSETTINGS index_granularity = 8192 +CREATE TABLE default.bloom_filter_idx_good\n(\n `u64` UInt64, \n `i32` Int32, \n `f64` Float64, \n `d` Decimal(10, 2), \n `s` String, \n `e` Enum8(\'a\' = 1, \'b\' = 2, \'c\' = 3), \n `dt` Date, \n INDEX bloom_filter_a i32 TYPE bloom_filter(-0.1) GRANULARITY 1\n)\nENGINE = MergeTree()\nORDER BY u64\nSETTINGS index_granularity = 8192 +CREATE TABLE default.bloom_filter_idx_good\n(\n `u64` UInt64, \n `i32` Int32, \n `f64` Float64, \n `d` Decimal(10, 2), \n `s` String, \n `e` Enum8(\'a\' = 1, \'b\' = 2, \'c\' = 3), \n `dt` Date, \n INDEX bloom_filter_a i32 TYPE bloom_filter(1.01) GRANULARITY 1\n)\nENGINE = MergeTree()\nORDER BY u64\nSETTINGS index_granularity = 8192 diff --git a/tests/queries/0_stateless/01268_DateTime64_in_WHERE.sql b/tests/queries/0_stateless/01268_DateTime64_in_WHERE.sql index c65bf668d71..4ffcf3be3c9 100644 --- a/tests/queries/0_stateless/01268_DateTime64_in_WHERE.sql +++ b/tests/queries/0_stateless/01268_DateTime64_in_WHERE.sql @@ -1,12 +1,12 @@ -- Error cases: -- non-const string column -WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT DT64 = materialize(S); -- {serverError 44} -WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT materialize(S) = toDateTime64(S, 3); -- {serverError 44} -WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT * WHERE DT64 = materialize(S); -- {serverError 44} -WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT * WHERE materialize(S) = DT64; -- {serverError 44} +WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT DT64 = materialize(S); -- {serverError 43} +WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT materialize(S) = toDateTime64(S, 3); -- {serverError 43} +WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT * WHERE DT64 = materialize(S); -- {serverError 43} +WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT * WHERE materialize(S) = DT64; -- {serverError 43} -SELECT * WHERE toDateTime64(123.345, 3) == 'ABCD'; -- {serverError 131} -- invalid DateTime64 string -SELECT * WHERE toDateTime64(123.345, 3) == '2020-02-05 14:34:12.33333333333333333333333333333333333333333333333333333333'; -- {serverError 131} -- invalid string length +SELECT * WHERE toDateTime64(123.345, 3) == 'ABCD'; -- {serverError 53} -- invalid DateTime64 string +SELECT * WHERE toDateTime64(123.345, 3) == '2020-02-05 14:34:12.33333333333333333333333333333333333333333333333333333333'; -- {serverError 53} -- invalid string length SELECT 'in SELECT'; WITH '2020-02-05 14:34:12.333' as S, toDateTime64(S, 3) as DT64 SELECT DT64 = S; diff --git a/tests/queries/0_stateless/01268_data_numeric_parameters.reference b/tests/queries/0_stateless/01268_data_numeric_parameters.reference new file mode 100644 index 00000000000..fd76fd4ef54 --- /dev/null +++ b/tests/queries/0_stateless/01268_data_numeric_parameters.reference @@ -0,0 +1,3 @@ +Int8 Int8 Int16 Int16 Int32 Int32 Int64 Int64 +Float32 Float32 Float32 Float64 Float64 Float64 +String String diff --git a/tests/queries/0_stateless/01268_data_numeric_parameters.sql b/tests/queries/0_stateless/01268_data_numeric_parameters.sql new file mode 100644 index 00000000000..eceba51e7f5 --- /dev/null +++ b/tests/queries/0_stateless/01268_data_numeric_parameters.sql @@ -0,0 +1,42 @@ +DROP TABLE IF EXISTS ints; +DROP TABLE IF EXISTS floats; +DROP TABLE IF EXISTS strings; + +CREATE TABLE ints ( + a TINYINT, + b TINYINT(8), + c SMALLINT, + d SMALLINT(16), + e INT, + f INT(32), + g BIGINT, + h BIGINT(64) +) engine=Memory; + +INSERT INTO ints VALUES (1, 8, 11, 16, 21, 32, 41, 64); + +SELECT toTypeName(a), toTypeName(b), toTypeName(c), toTypeName(d), toTypeName(e), toTypeName(f), toTypeName(g), toTypeName(h) FROM ints; + +CREATE TABLE floats ( + a FLOAT, + b FLOAT(12), + c FLOAT(15, 22), + d DOUBLE, + e DOUBLE(12), + f DOUBLE(4, 18) + +) engine=Memory; + +INSERT INTO floats VALUES (1.1, 1.2, 1.3, 41.1, 41.1, 42.1); + +SELECT toTypeName(a), toTypeName(b), toTypeName(c), toTypeName(d), toTypeName(e), toTypeName(f) FROM floats; + + +CREATE TABLE strings ( + a VARCHAR, + b VARCHAR(11) +) engine=Memory; + +INSERT INTO strings VALUES ('test', 'string'); + +SELECT toTypeName(a), toTypeName(b) FROM strings; diff --git a/tests/queries/0_stateless/01268_procfs_metrics.sh b/tests/queries/0_stateless/01268_procfs_metrics.sh index e258f7faafa..1367b68a61c 100755 --- a/tests/queries/0_stateless/01268_procfs_metrics.sh +++ b/tests/queries/0_stateless/01268_procfs_metrics.sh @@ -17,14 +17,16 @@ function read_numbers_func() function show_processes_func() { - sleep 0.1; - - # These two system metrics for the generating query above are guaranteed to be nonzero when ProcFS is mounted at /proc - $CLICKHOUSE_CLIENT -q " - SELECT count() > 0 FROM system.processes\ - WHERE has(ProfileEvents.Names, 'OSCPUVirtualTimeMicroseconds') AND has(ProfileEvents.Names, 'OSReadChars')\ - SETTINGS max_threads = 1 - "; + while true; do + sleep 0.1; + + # These two system metrics for the generating query above are guaranteed to be nonzero when ProcFS is mounted at /proc + $CLICKHOUSE_CLIENT -q " + SELECT count() > 0 FROM system.processes\ + WHERE has(ProfileEvents.Names, 'OSCPUVirtualTimeMicroseconds') AND has(ProfileEvents.Names, 'OSReadChars')\ + SETTINGS max_threads = 1 + " | grep '1' && break; + done } diff --git a/tests/queries/0_stateless/01271_optimize_arithmetic_operations_in_aggr_func.reference b/tests/queries/0_stateless/01271_optimize_arithmetic_operations_in_aggr_func.reference new file mode 100644 index 00000000000..2fe897e3819 --- /dev/null +++ b/tests/queries/0_stateless/01271_optimize_arithmetic_operations_in_aggr_func.reference @@ -0,0 +1,6 @@ +-150000044999994 +6931471.112452272 +24580677 +-150000044999994 +6931471.112452272 +24580677 diff --git a/tests/queries/0_stateless/01271_optimize_arithmetic_operations_in_aggr_func.sql b/tests/queries/0_stateless/01271_optimize_arithmetic_operations_in_aggr_func.sql new file mode 100644 index 00000000000..d0e8fa426cf --- /dev/null +++ b/tests/queries/0_stateless/01271_optimize_arithmetic_operations_in_aggr_func.sql @@ -0,0 +1,11 @@ +set optimize_arithmetic_operations_in_aggregate_functions = 1; + +SELECT sum(number * -3) + min(2 * number * -3) - max(-1 * -2 * number * -3) FROM numbers(10000000); +SELECT max(log(2) * number) FROM numbers(10000000); +SELECT round(max(log(2) * 3 * sin(0.3) * number * 4)) FROM numbers(10000000); + +set optimize_arithmetic_operations_in_aggregate_functions = 0; + +SELECT sum(number * -3) + min(2 * number * -3) - max(-1 * -2 * number * -3) FROM numbers(10000000); +SELECT max(log(2) * number) FROM numbers(10000000); +SELECT round(max(log(2) * 3 * sin(0.3) * number * 4)) FROM numbers(10000000); diff --git a/tests/queries/0_stateless/01275_extract_groups_check.sql b/tests/queries/0_stateless/01275_extract_groups_check.sql index 2dd236f2a3b..f8bc5943a78 100644 --- a/tests/queries/0_stateless/01275_extract_groups_check.sql +++ b/tests/queries/0_stateless/01275_extract_groups_check.sql @@ -1,5 +1,5 @@ -SELECT extractGroups('hello', ''); -- { serverError 69 } -SELECT extractAllGroups('hello', ''); -- { serverError 69 } +SELECT extractGroups('hello', ''); -- { serverError 36 } +SELECT extractAllGroups('hello', ''); -- { serverError 36 } SELECT extractGroups('hello', ' '); -- { serverError 36 } SELECT extractAllGroups('hello', ' '); -- { serverError 36 } diff --git a/tests/queries/0_stateless/01277_convert_field_to_type_logical_error.sql b/tests/queries/0_stateless/01277_convert_field_to_type_logical_error.sql index 05295575cf1..4712c124237 100644 --- a/tests/queries/0_stateless/01277_convert_field_to_type_logical_error.sql +++ b/tests/queries/0_stateless/01277_convert_field_to_type_logical_error.sql @@ -1 +1 @@ -SELECT -2487, globalNullIn(toIntervalMinute(-88074), 'qEkek..'), [-27.537293]; -- { serverError 70 } +SELECT -2487, globalNullIn(toIntervalMinute(-88074), 'qEkek..'), [-27.537293]; -- { serverError 53 } diff --git a/tests/queries/0_stateless/01280_ttl_where_group_by.sql b/tests/queries/0_stateless/01280_ttl_where_group_by.sh old mode 100644 new mode 100755 similarity index 51% rename from tests/queries/0_stateless/01280_ttl_where_group_by.sql rename to tests/queries/0_stateless/01280_ttl_where_group_by.sh index e61716cfe81..d0e4c5b3099 --- a/tests/queries/0_stateless/01280_ttl_where_group_by.sql +++ b/tests/queries/0_stateless/01280_ttl_where_group_by.sh @@ -1,5 +1,19 @@ -drop table if exists ttl_01280_1; +#!/usr/bin/env bash +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +$CLICKHOUSE_CLIENT --query "drop table if exists ttl_01280_1" + +function optimize() +{ + for i in {0..20}; do + $CLICKHOUSE_CLIENT --query "OPTIMIZE TABLE $1 FINAL SETTINGS optimize_throw_if_noop=1" 2>/dev/null && break + sleep 0.3 + done +} + +$CLICKHOUSE_CLIENT -n --query " create table ttl_01280_1 (a Int, b Int, x Int, y Int, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second delete where x % 10 == 0 and y > 5; insert into ttl_01280_1 values (1, 1, 0, 4, now() + 10); insert into ttl_01280_1 values (1, 1, 10, 6, now()); @@ -7,13 +21,15 @@ insert into ttl_01280_1 values (1, 2, 3, 7, now()); insert into ttl_01280_1 values (1, 3, 0, 5, now()); insert into ttl_01280_1 values (2, 1, 20, 1, now()); insert into ttl_01280_1 values (2, 1, 0, 1, now()); -insert into ttl_01280_1 values (3, 1, 0, 8, now()); -select sleep(1.1) format Null; -optimize table ttl_01280_1 final; -select a, b, x, y from ttl_01280_1; +insert into ttl_01280_1 values (3, 1, 0, 8, now());" -drop table if exists ttl_01280_2; +sleep 2 +optimize "ttl_01280_1" +$CLICKHOUSE_CLIENT --query "select a, b, x, y from ttl_01280_1" +$CLICKHOUSE_CLIENT --query "drop table if exists ttl_01280_2" + +$CLICKHOUSE_CLIENT -n --query " create table ttl_01280_2 (a Int, b Int, x Array(Int32), y Double, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by a, b set x = minForEach(x), y = sum(y), d = max(d); insert into ttl_01280_2 values (1, 1, array(0, 2, 3), 4, now() + 10); insert into ttl_01280_2 values (1, 1, array(5, 4, 3), 6, now()); @@ -23,13 +39,15 @@ insert into ttl_01280_2 values (1, 3, array(1, 1, 2, 1), 9, now()); insert into ttl_01280_2 values (1, 3, array(3, 2, 1, 0), 3, now()); insert into ttl_01280_2 values (2, 1, array(3, 3, 3), 7, now()); insert into ttl_01280_2 values (2, 1, array(11, 1, 0, 3), 1, now()); -insert into ttl_01280_2 values (3, 1, array(2, 4, 5), 8, now()); -select sleep(1.1) format Null; -optimize table ttl_01280_2 final; -select a, b, x, y from ttl_01280_2; +insert into ttl_01280_2 values (3, 1, array(2, 4, 5), 8, now());" -drop table if exists ttl_01280_3; +sleep 2 +optimize "ttl_01280_2" +$CLICKHOUSE_CLIENT --query "select a, b, x, y from ttl_01280_2" +$CLICKHOUSE_CLIENT --query "drop table if exists ttl_01280_3" + +$CLICKHOUSE_CLIENT -n --query " create table ttl_01280_3 (a Int, b Int, x Int64, y Int, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by a set x = argMax(x, d), y = argMax(y, d), d = max(d); insert into ttl_01280_3 values (1, 1, 0, 4, now() + 10); insert into ttl_01280_3 values (1, 1, 10, 6, now() + 1); @@ -39,49 +57,47 @@ insert into ttl_01280_3 values (2, 1, 20, 1, now()); insert into ttl_01280_3 values (2, 1, 0, 3, now() + 1); insert into ttl_01280_3 values (3, 1, 0, 3, now()); insert into ttl_01280_3 values (3, 2, 8, 2, now() + 1); -insert into ttl_01280_3 values (3, 5, 5, 8, now()); -select sleep(2.1) format Null; -optimize table ttl_01280_3 final; -select a, b, x, y from ttl_01280_3; +insert into ttl_01280_3 values (3, 5, 5, 8, now());" -drop table if exists ttl_01280_4; +sleep 2 +optimize "ttl_01280_3" +$CLICKHOUSE_CLIENT --query "select a, b, x, y from ttl_01280_3" +$CLICKHOUSE_CLIENT --query "drop table if exists ttl_01280_4" + +$CLICKHOUSE_CLIENT -n --query " create table ttl_01280_4 (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (toDate(d), -(a + b)) ttl d + interval 1 second group by toDate(d) set x = sum(x), y = max(y); insert into ttl_01280_4 values (1, 1, 0, 4, now() + 10); insert into ttl_01280_4 values (10, 2, 3, 3, now()); insert into ttl_01280_4 values (2, 10, 1, 7, now()); insert into ttl_01280_4 values (3, 3, 5, 2, now()); -insert into ttl_01280_4 values (1, 5, 4, 9, now()); -select sleep(1.1) format Null; -optimize table ttl_01280_4 final; -select a, b, x, y from ttl_01280_4; +insert into ttl_01280_4 values (1, 5, 4, 9, now())" -drop table if exists ttl_01280_5; +sleep 2 +optimize "ttl_01280_4" +$CLICKHOUSE_CLIENT --query "select a, b, x, y from ttl_01280_4" -create table ttl_01280_5 (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (toDate(d), a, -b) ttl d + interval 1 second group by toDate(d), a set x = sum(x); +$CLICKHOUSE_CLIENT --query "drop table if exists ttl_01280_5" + +$CLICKHOUSE_CLIENT -n --query "create table ttl_01280_5 (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (toDate(d), a, -b) ttl d + interval 1 second group by toDate(d), a set x = sum(x); insert into ttl_01280_5 values (1, 2, 3, 5, now()); insert into ttl_01280_5 values (2, 10, 1, 5, now()); insert into ttl_01280_5 values (2, 3, 5, 5, now()); -insert into ttl_01280_5 values (1, 5, 4, 5, now()); -select sleep(1.1) format Null; -optimize table ttl_01280_5 final; -select a, b, x, y from ttl_01280_5; +insert into ttl_01280_5 values (1, 5, 4, 5, now());" -drop table if exists ttl_01280_6; +sleep 2 +optimize "ttl_01280_5" +$CLICKHOUSE_CLIENT --query "select a, b, x, y from ttl_01280_5" +$CLICKHOUSE_CLIENT --query "drop table if exists ttl_01280_6" + +$CLICKHOUSE_CLIENT -n --query " create table ttl_01280_6 (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (toDate(d), a, -b) ttl d + interval 1 second group by toDate(d), a; insert into ttl_01280_6 values (1, 2, 3, 5, now()); insert into ttl_01280_6 values (2, 10, 3, 5, now()); insert into ttl_01280_6 values (2, 3, 3, 5, now()); -insert into ttl_01280_6 values (1, 5, 3, 5, now()); -select sleep(1.1) format Null; -optimize table ttl_01280_6 final; -select a, b, x, y from ttl_01280_6; +insert into ttl_01280_6 values (1, 5, 3, 5, now())" -create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by x set y = max(y); -- { serverError 450} -create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by b set y = max(y); -- { serverError 450} -create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by a, b, x set y = max(y); -- { serverError 450} -create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by a set b = min(b), y = max(y); -- { serverError 450} -create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by a, b set y = max(y), y = max(y); -- { serverError 450} -create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (toDate(d), a) ttl d + interval 1 second group by toDate(d), a set d = min(d), b = max(b); -- { serverError 450} -create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (d, -(a + b)) ttl d + interval 1 second group by d, -(a + b) set a = sum(a), b = min(b); -- { serverError 450} +sleep 2 +optimize "ttl_01280_6" +$CLICKHOUSE_CLIENT --query "select a, b, x, y from ttl_01280_6" diff --git a/tests/queries/0_stateless/01280_ttl_where_group_by_negative.reference b/tests/queries/0_stateless/01280_ttl_where_group_by_negative.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/01280_ttl_where_group_by_negative.sql b/tests/queries/0_stateless/01280_ttl_where_group_by_negative.sql new file mode 100644 index 00000000000..f2c26a3d495 --- /dev/null +++ b/tests/queries/0_stateless/01280_ttl_where_group_by_negative.sql @@ -0,0 +1,7 @@ +create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by x set y = max(y); -- { serverError 450} +create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by b set y = max(y); -- { serverError 450} +create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by a, b, x set y = max(y); -- { serverError 450} +create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by a set b = min(b), y = max(y); -- { serverError 450} +create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (a, b) ttl d + interval 1 second group by a, b set y = max(y), y = max(y); -- { serverError 450} +create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (toDate(d), a) ttl d + interval 1 second group by toDate(d), a set d = min(d), b = max(b); -- { serverError 450} +create table ttl_01280_error (a Int, b Int, x Int64, y Int64, d DateTime) engine = MergeTree order by (d, -(a + b)) ttl d + interval 1 second group by d, -(a + b) set a = sum(a), b = min(b); -- { serverError 450} diff --git a/tests/queries/0_stateless/01281_group_by_limit_memory_tracking.sh b/tests/queries/0_stateless/01281_group_by_limit_memory_tracking.sh index 5922b8d74d2..2115530a450 100755 --- a/tests/queries/0_stateless/01281_group_by_limit_memory_tracking.sh +++ b/tests/queries/0_stateless/01281_group_by_limit_memory_tracking.sh @@ -20,6 +20,14 @@ function execute_null() function execute_group_by() { + # Peak memory usage for the main query (with GROUP BY) is ~100MiB (with + # max_threads=2 as here). + # So set max_memory_usage_for_user to 150MiB and if the memory tracking + # accounting will be incorrect then the second query will fail + # + # Note that we also need one running query for the user (sleep(3)), since + # max_memory_usage_for_user is installed to 0 once there are no more + # queries for user. local opts=( --max_memory_usage_for_user=$((150<<20)) --max_threads=2 diff --git a/tests/queries/0_stateless/01281_failed_insert_select_queries_couner.reference b/tests/queries/0_stateless/01281_unsucceeded_insert_select_queries_counter.reference similarity index 100% rename from tests/queries/0_stateless/01281_failed_insert_select_queries_couner.reference rename to tests/queries/0_stateless/01281_unsucceeded_insert_select_queries_counter.reference diff --git a/tests/queries/0_stateless/01281_failed_insert_select_queries_couner.sql b/tests/queries/0_stateless/01281_unsucceeded_insert_select_queries_counter.sql similarity index 100% rename from tests/queries/0_stateless/01281_failed_insert_select_queries_couner.sql rename to tests/queries/0_stateless/01281_unsucceeded_insert_select_queries_counter.sql diff --git a/tests/queries/0_stateless/01284_fuzz_bits.reference b/tests/queries/0_stateless/01284_fuzz_bits.reference new file mode 100644 index 00000000000..d35f590ed3d --- /dev/null +++ b/tests/queries/0_stateless/01284_fuzz_bits.reference @@ -0,0 +1,5 @@ + +100 +String +FixedString(10) +1 diff --git a/tests/queries/0_stateless/01284_fuzz_bits.sql b/tests/queries/0_stateless/01284_fuzz_bits.sql new file mode 100644 index 00000000000..24da23787cb --- /dev/null +++ b/tests/queries/0_stateless/01284_fuzz_bits.sql @@ -0,0 +1,31 @@ +SELECT fuzzBits(toString('string'), 1); -- { serverError 43 } +SELECT fuzzBits('string', -1.0); -- { serverError 69 } +SELECT fuzzBits('', 0.3); +SELECT length(fuzzBits(randomString(100), 0.5)); +SELECT toTypeName(fuzzBits(randomString(100), 0.5)); +SELECT toTypeName(fuzzBits(toFixedString('abacaba', 10), 0.9)); + +SELECT + ( + 0.29 * 8 * 10000 < sum + AND sum < 0.31 * 8 * 10000 + ) AS res +FROM + ( + SELECT + arraySum( + id -> bitCount( + reinterpretAsUInt8( + substring( + fuzzBits( + arrayStringConcat(arrayMap(x -> toString('\0'), range(10000))), + 0.3 + ), + id + 1, + 1 + ) + ) + ), + range(10000) + ) as sum + ) diff --git a/tests/queries/0_stateless/01291_aggregation_in_order.reference b/tests/queries/0_stateless/01291_aggregation_in_order.reference new file mode 100644 index 00000000000..c072a8aed3e --- /dev/null +++ b/tests/queries/0_stateless/01291_aggregation_in_order.reference @@ -0,0 +1,41 @@ +1 1 +1 2 +1 3 +1 4 +1 5 +1 6 +2 1 +2 2 +2 3 +2 4 +1 +2 +1 1 101 1 +1 2 102 1 +1 3 103 1 +1 4 104 1 +1 5 104 1 +1 6 105 1 +2 1 213 2 +2 2 107 2 +2 3 108 2 +2 4 109 2 +1 619 1 +2 537 2 +1 619 1 +2 537 2 +2019-05-05 00:00:00 -45363190 +2019-05-05 00:00:00 -1249512288 +2019-05-05 00:00:00 345522721 +2019-05-05 00:00:00 486601715 +2019-05-05 00:00:00 1449669396 +2019-05-05 00:00:00 45 +2019-05-06 00:00:00 46 +2019-05-07 00:00:00 47 +2019-05-08 00:00:00 48 +2019-05-09 00:00:00 49 +2019-05-05 00:00:00 0 1900940608 +2019-05-06 00:00:00 1 1857737272 +2019-05-07 00:00:00 2 1996614413 +2019-05-08 00:00:00 3 1873725230 +2019-05-09 00:00:00 4 1831412253 diff --git a/tests/queries/0_stateless/01291_aggregation_in_order.sql b/tests/queries/0_stateless/01291_aggregation_in_order.sql new file mode 100644 index 00000000000..753075f2757 --- /dev/null +++ b/tests/queries/0_stateless/01291_aggregation_in_order.sql @@ -0,0 +1,33 @@ +DROP TABLE IF EXISTS pk_order; + +SET optimize_aggregation_in_order = 1; + +CREATE TABLE pk_order(a UInt64, b UInt64, c UInt64, d UInt64) ENGINE=MergeTree() ORDER BY (a, b); +INSERT INTO pk_order(a, b, c, d) VALUES (1, 1, 101, 1), (1, 2, 102, 1), (1, 3, 103, 1), (1, 4, 104, 1); +INSERT INTO pk_order(a, b, c, d) VALUES (1, 5, 104, 1), (1, 6, 105, 1), (2, 1, 106, 2), (2, 1, 107, 2); +INSERT INTO pk_order(a, b, c, d) VALUES (2, 2, 107, 2), (2, 3, 108, 2), (2, 4, 109, 2); + +-- Order after group by in order is determined + +SELECT a, b FROM pk_order GROUP BY a, b; +SELECT a FROM pk_order GROUP BY a; + +SELECT a, b, sum(c), avg(d) FROM pk_order GROUP BY a, b; +SELECT a, sum(c), avg(d) FROM pk_order GROUP BY a; +SELECT a, sum(c), avg(d) FROM pk_order GROUP BY -a; + +DROP TABLE IF EXISTS pk_order; + +CREATE TABLE pk_order (d DateTime, a Int32, b Int32) ENGINE = MergeTree ORDER BY (d, a) + PARTITION BY toDate(d) SETTINGS index_granularity=1; + +INSERT INTO pk_order + SELECT toDateTime('2019-05-05 00:00:00') + INTERVAL number % 10 DAY, number, intHash32(number) from numbers(100); + +set max_block_size = 1; + +SELECT d, max(b) FROM pk_order GROUP BY d, a LIMIT 5; +SELECT d, avg(a) FROM pk_order GROUP BY toString(d) LIMIT 5; +SELECT toStartOfHour(d) as d1, min(a), max(b) FROM pk_order GROUP BY d1 LIMIT 5; + +DROP TABLE pk_order; diff --git a/tests/queries/0_stateless/01292_optimize_data_skip_idx_order_by_expr.reference b/tests/queries/0_stateless/01292_optimize_data_skip_idx_order_by_expr.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/tests/queries/0_stateless/01292_optimize_data_skip_idx_order_by_expr.reference @@ -0,0 +1 @@ +1 diff --git a/tests/queries/0_stateless/01292_optimize_data_skip_idx_order_by_expr.sql b/tests/queries/0_stateless/01292_optimize_data_skip_idx_order_by_expr.sql new file mode 100644 index 00000000000..cf823be1f79 --- /dev/null +++ b/tests/queries/0_stateless/01292_optimize_data_skip_idx_order_by_expr.sql @@ -0,0 +1,14 @@ +drop table if exists data_01292; + +create table data_01292 ( + key Int, + index key_idx (key) type minmax granularity 1 +) Engine=MergeTree() ORDER BY (key+0); + +insert into data_01292 values (1); + +optimize table data_01292 final; + +select * from data_01292 where key > 0; + +drop table if exists data_01292; diff --git a/tests/queries/0_stateless/01292_quantile_array_bug.reference b/tests/queries/0_stateless/01292_quantile_array_bug.reference new file mode 100644 index 00000000000..36abe23e89c --- /dev/null +++ b/tests/queries/0_stateless/01292_quantile_array_bug.reference @@ -0,0 +1 @@ +[7] diff --git a/tests/queries/0_stateless/01292_quantile_array_bug.sql b/tests/queries/0_stateless/01292_quantile_array_bug.sql new file mode 100644 index 00000000000..ecb1028d569 --- /dev/null +++ b/tests/queries/0_stateless/01292_quantile_array_bug.sql @@ -0,0 +1 @@ +select quantilesExactWeightedArray(0.5)(range(10), range(10)) diff --git a/tests/queries/0_stateless/01293_client_interactive_vertical_multiline.reference b/tests/queries/0_stateless/01293_client_interactive_vertical_multiline.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/01293_client_interactive_vertical_multiline.sh b/tests/queries/0_stateless/01293_client_interactive_vertical_multiline.sh new file mode 100755 index 00000000000..4d4292472da --- /dev/null +++ b/tests/queries/0_stateless/01293_client_interactive_vertical_multiline.sh @@ -0,0 +1,88 @@ +#!/usr/bin/expect -f + +log_user 0 +set timeout 60 +spawn clickhouse-client +match_max 100000 + +expect ":) " + +send -- "SELECT 1\r" +expect "│ 1 │" +expect ":) " + +send -- "SELECT 1\\G\r" +expect "Row 1:" +expect "1: 1" +expect ":) " + +send -- "SELECT 1\\\r" +expect ":-] " +send -- ", 2\r" +expect "│ 1 │ 2 │" +expect ":) " + +send -- "SELECT 1\\\r" +expect ":-] " +send -- ", 2\\G\r" +expect "Row 1:" +expect "1: 1" +expect "2: 2" +expect ":) " + +send -- "" +expect eof + +set timeout 60 +spawn clickhouse-client --multiline +match_max 100000 + +expect ":) " + +send -- "SELECT 1;\r" +expect "│ 1 │" +expect ":) " + +send -- "SELECT 1\\G\r" +expect "Row 1:" +expect "1: 1" +expect ":) " + +send -- "SELECT 1; \r" +expect "│ 1 │" +expect ":) " + +send -- "SELECT 1\\G \r" +expect "Row 1:" +expect "1: 1" +expect ":) " + +send -- "SELECT 1\r" +expect ":-] " +send -- ";\r" +expect "│ 1 │" +expect ":) " + +send -- "SELECT 1\r" +expect ":-] " +send -- "\\G\r" +expect "Row 1:" +expect "1: 1" +expect ":) " + +send -- "SELECT 1\r" +expect ":-] " +send -- ", 2;\r" +expect "│ 1 │ 2 │" +expect ":) " + +send -- "SELECT 1\r" +expect ":-] " +send -- ", 2\\G\r" +expect "Row 1:" +expect "1: 1" +expect "2: 2" +expect ":) " + +send -- "" +expect eof diff --git a/tests/queries/0_stateless/01293_client_interactive_vertical_singleline.reference b/tests/queries/0_stateless/01293_client_interactive_vertical_singleline.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/01293_client_interactive_vertical_singleline.sh b/tests/queries/0_stateless/01293_client_interactive_vertical_singleline.sh new file mode 100755 index 00000000000..3b98caeff2e --- /dev/null +++ b/tests/queries/0_stateless/01293_client_interactive_vertical_singleline.sh @@ -0,0 +1,34 @@ +#!/usr/bin/expect -f + +log_user 0 +set timeout 60 +spawn clickhouse-client +match_max 100000 + +expect ":) " + +send -- "SELECT 1\r" +expect "│ 1 │" +expect ":) " + +send -- "SELECT 1\\G\r" +expect "Row 1:" +expect "1: 1" +expect ":) " + +send -- "SELECT 1\\\r" +expect ":-] " +send -- ", 2\r" +expect "│ 1 │ 2 │" +expect ":) " + +send -- "SELECT 1\\\r" +expect ":-] " +send -- ", 2\\G\r" +expect "Row 1:" +expect "1: 1" +expect "2: 2" +expect ":) " + +send -- "" +expect eof diff --git a/tests/queries/0_stateless/01293_external_sorting_limit_bug.reference b/tests/queries/0_stateless/01293_external_sorting_limit_bug.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/01293_external_sorting_limit_bug.sql b/tests/queries/0_stateless/01293_external_sorting_limit_bug.sql new file mode 100644 index 00000000000..570ec4b8c3c --- /dev/null +++ b/tests/queries/0_stateless/01293_external_sorting_limit_bug.sql @@ -0,0 +1 @@ +SELECT number FROM (SELECT number FROM system.numbers LIMIT 999990) ORDER BY number ASC LIMIT 100, 65535 SETTINGS max_bytes_before_external_sort = 1000000 format Null diff --git a/tests/queries/0_stateless/01293_optimize_final_force.reference b/tests/queries/0_stateless/01293_optimize_final_force.reference new file mode 100644 index 00000000000..b0b9422adf0 --- /dev/null +++ b/tests/queries/0_stateless/01293_optimize_final_force.reference @@ -0,0 +1,100 @@ +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 +55 0 diff --git a/tests/queries/0_stateless/01293_optimize_final_force.sh b/tests/queries/0_stateless/01293_optimize_final_force.sh new file mode 100755 index 00000000000..50cba1e7534 --- /dev/null +++ b/tests/queries/0_stateless/01293_optimize_final_force.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +for i in {1..100}; do $CLICKHOUSE_CLIENT --multiquery --query " +DROP TABLE IF EXISTS mt; +CREATE TABLE mt (x UInt8, k UInt8 DEFAULT 0) ENGINE = SummingMergeTree ORDER BY k; + +INSERT INTO mt (x) VALUES (1); +INSERT INTO mt (x) VALUES (2); +INSERT INTO mt (x) VALUES (3); +INSERT INTO mt (x) VALUES (4); +INSERT INTO mt (x) VALUES (5); +INSERT INTO mt (x) VALUES (6); +INSERT INTO mt (x) VALUES (7); +INSERT INTO mt (x) VALUES (8); +INSERT INTO mt (x) VALUES (9); +INSERT INTO mt (x) VALUES (10); + +OPTIMIZE TABLE mt FINAL; +SELECT * FROM mt; + +DROP TABLE mt; +"; done diff --git a/tests/queries/0_stateless/01293_pretty_max_value_width.reference b/tests/queries/0_stateless/01293_pretty_max_value_width.reference new file mode 100644 index 00000000000..9887169f7af --- /dev/null +++ b/tests/queries/0_stateless/01293_pretty_max_value_width.reference @@ -0,0 +1,114 @@ +┏━━━━━━━━┳━━━━━┓ +┃ x  ┃ y  ┃ +┡━━━━━━━━╇━━━━━┩ +│ привет │ мир │ +└────────┴─────┘ +┏━━━━━━━┳━━━━━┓ +┃ x  ┃ y  ┃ +┡━━━━━━━╇━━━━━┩ +│ приве⋯│ мир │ +└───────┴─────┘ +┌─x─────┬─y───┐ +│ приве⋯│ мир │ +└───────┴─────┘ + x y + + приве⋯ мир +┏━━━━━━━┳━━━━━━━┓ +┃ x  ┃ y  ┃ +┡━━━━━━━╇━━━━━━━┩ +│ приве⋯│ мир │ +├───────┼───────┤ +│ мир │ приве⋯│ +└───────┴───────┘ +┌─x─────┬─y─────┐ +│ приве⋯│ мир │ +│ мир │ приве⋯│ +└───────┴───────┘ + x y + + приве⋯ мир + мир приве⋯ +┏━━━━━━━━┳━━━━━┓ +┃ x  ┃ y  ┃ +┡━━━━━━━━╇━━━━━┩ +│ привет │ мир │ +└────────┴─────┘ +┌─x──────┬─y───┐ +│ привет │ мир │ +└────────┴─────┘ + x y + + привет мир +┏━━━━━━━━┳━━━━━━━━┓ +┃ x  ┃ y  ┃ +┡━━━━━━━━╇━━━━━━━━┩ +│ привет │ мир │ +├────────┼────────┤ +│ мир │ привет │ +└────────┴────────┘ +┌─x──────┬─y──────┐ +│ привет │ мир │ +│ мир │ привет │ +└────────┴────────┘ + x y + + привет мир + мир привет +┏━━━┳━━━┓ +┃ x ┃ y ┃ +┡━━━╇━━━┩ +│ п⋯│ м⋯│ +└───┴───┘ +┌─x─┬─y─┐ +│ п⋯│ м⋯│ +└───┴───┘ + x y + + п⋯ м⋯ +┏━━━┳━━━┓ +┃ x ┃ y ┃ +┡━━━╇━━━┩ +│ п⋯│ м⋯│ +├───┼───┤ +│ м⋯│ п⋯│ +└───┴───┘ +┌─x─┬─y─┐ +│ п⋯│ м⋯│ +│ м⋯│ п⋯│ +└───┴───┘ + x y + + п⋯ м⋯ + м⋯ п⋯ +┏━━━┳━━━┓ +┃ x ┃ y ┃ +┡━━━╇━━━┩ +│ ⋯ │ ⋯ │ +└───┴───┘ +┌─x─┬─y─┐ +│ ⋯ │ ⋯ │ +└───┴───┘ + x y + + ⋯ ⋯ +┏━━━┳━━━┓ +┃ x ┃ y ┃ +┡━━━╇━━━┩ +│ ⋯ │ ⋯ │ +├───┼───┤ +│ ⋯ │ ⋯ │ +└───┴───┘ +┌─x─┬─y─┐ +│ ⋯ │ ⋯ │ +│ ⋯ │ ⋯ │ +└───┴───┘ + x y + + ⋯ ⋯ + ⋯ ⋯ +┏━━━┳━━━┓ +┃ x ┃ y ┃ +┡━━━╇━━━┩ +│ ⋯ │ ⋯ │ +└───┴───┘ diff --git a/tests/queries/0_stateless/01293_pretty_max_value_width.sql b/tests/queries/0_stateless/01293_pretty_max_value_width.sql new file mode 100644 index 00000000000..992aec06f0a --- /dev/null +++ b/tests/queries/0_stateless/01293_pretty_max_value_width.sql @@ -0,0 +1,43 @@ +SELECT 'привет' AS x, 'мир' AS y FORMAT Pretty; + +SET output_format_pretty_max_value_width = 5; +SELECT 'привет' AS x, 'мир' AS y FORMAT Pretty; +SELECT 'привет' AS x, 'мир' AS y FORMAT PrettyCompact; +SELECT 'привет' AS x, 'мир' AS y FORMAT PrettySpace; + +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT Pretty; +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT PrettyCompact; +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT PrettySpace; + +SET output_format_pretty_max_value_width = 6; + +SELECT 'привет' AS x, 'мир' AS y FORMAT Pretty; +SELECT 'привет' AS x, 'мир' AS y FORMAT PrettyCompact; +SELECT 'привет' AS x, 'мир' AS y FORMAT PrettySpace; + +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT Pretty; +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT PrettyCompact; +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT PrettySpace; + +SET output_format_pretty_max_value_width = 1; + +SELECT 'привет' AS x, 'мир' AS y FORMAT Pretty; +SELECT 'привет' AS x, 'мир' AS y FORMAT PrettyCompact; +SELECT 'привет' AS x, 'мир' AS y FORMAT PrettySpace; + +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT Pretty; +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT PrettyCompact; +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT PrettySpace; + +SET output_format_pretty_max_value_width = 0; + +SELECT 'привет' AS x, 'мир' AS y FORMAT Pretty; +SELECT 'привет' AS x, 'мир' AS y FORMAT PrettyCompact; +SELECT 'привет' AS x, 'мир' AS y FORMAT PrettySpace; + +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT Pretty; +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT PrettyCompact; +SELECT * FROM VALUES('x String, y String', ('привет', 'мир'), ('мир', 'привет')) FORMAT PrettySpace; + +SET output_format_pretty_color = 0; +SELECT 'привет' AS x, 'мир' AS y FORMAT Pretty; diff --git a/tests/queries/0_stateless/01293_show_clusters.reference b/tests/queries/0_stateless/01293_show_clusters.reference new file mode 100644 index 00000000000..39e25143131 --- /dev/null +++ b/tests/queries/0_stateless/01293_show_clusters.reference @@ -0,0 +1,8 @@ +test_cluster_two_shards +test_cluster_two_shards_different_databases +test_cluster_two_shards_localhost +test_shard_localhost +test_shard_localhost_secure +test_unavailable_shard +test_cluster_two_shards +test_shard_localhost 1 1 1 localhost ::1 9000 1 default 0 0 diff --git a/tests/queries/0_stateless/01293_show_clusters.sql b/tests/queries/0_stateless/01293_show_clusters.sql new file mode 100644 index 00000000000..af450680dac --- /dev/null +++ b/tests/queries/0_stateless/01293_show_clusters.sql @@ -0,0 +1,3 @@ +show clusters; +show clusters like 'test%' limit 1; +show cluster 'test_shard_localhost'; diff --git a/tests/queries/0_stateless/01293_system_distribution_queue.reference b/tests/queries/0_stateless/01293_system_distribution_queue.reference new file mode 100644 index 00000000000..a2c1e5f2a7b --- /dev/null +++ b/tests/queries/0_stateless/01293_system_distribution_queue.reference @@ -0,0 +1,6 @@ +INSERT +1 0 1 1 +FLUSH +1 0 0 0 +UNBLOCK +0 0 0 0 diff --git a/tests/queries/0_stateless/01293_system_distribution_queue.sql b/tests/queries/0_stateless/01293_system_distribution_queue.sql new file mode 100644 index 00000000000..4c9c690af09 --- /dev/null +++ b/tests/queries/0_stateless/01293_system_distribution_queue.sql @@ -0,0 +1,26 @@ +drop table if exists null_01293; +drop table if exists dist_01293; + +create table null_01293 (key Int) engine=Null(); +create table dist_01293 as null_01293 engine=Distributed(test_cluster_two_shards, currentDatabase(), null_01293, key); + +-- no rows, since no active monitor +select * from system.distribution_queue; + +select 'INSERT'; +system stop distributed sends dist_01293; +insert into dist_01293 select * from numbers(10); +-- metrics updated only after distributed_directory_monitor_sleep_time_ms +set distributed_directory_monitor_sleep_time_ms=10; +-- 1 second should guarantee metrics update +-- XXX: but this is kind of quirk, way more better will be account this metrics without any delays. +select sleep(1) format Null; +select is_blocked, error_count, data_files, data_compressed_bytes>100 from system.distribution_queue; +system flush distributed dist_01293; + +select 'FLUSH'; +select is_blocked, error_count, data_files, data_compressed_bytes from system.distribution_queue; + +select 'UNBLOCK'; +system start distributed sends dist_01293; +select is_blocked, error_count, data_files, data_compressed_bytes from system.distribution_queue; diff --git a/tests/queries/0_stateless/01294_lazy_database_concurrent_recreate_reattach_and_show_tables.reference b/tests/queries/0_stateless/01294_lazy_database_concurrent_recreate_reattach_and_show_tables.reference new file mode 100644 index 00000000000..678f9a34e6f --- /dev/null +++ b/tests/queries/0_stateless/01294_lazy_database_concurrent_recreate_reattach_and_show_tables.reference @@ -0,0 +1 @@ +Test OK diff --git a/tests/queries/0_stateless/01294_lazy_database_concurrent_recreate_reattach_and_show_tables.sh b/tests/queries/0_stateless/01294_lazy_database_concurrent_recreate_reattach_and_show_tables.sh new file mode 100755 index 00000000000..cc0c7a52456 --- /dev/null +++ b/tests/queries/0_stateless/01294_lazy_database_concurrent_recreate_reattach_and_show_tables.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +export CURR_DATABASE="test_lazy_01294_concurrent_${CLICKHOUSE_DATABASE}" + + +function recreate_lazy_func1() +{ + $CLICKHOUSE_CLIENT -q " + CREATE TABLE $CURR_DATABASE.log (a UInt64, b UInt64) ENGINE = Log; + "; + + while true; do + $CLICKHOUSE_CLIENT -q " + DETACH TABLE $CURR_DATABASE.log; + "; + + $CLICKHOUSE_CLIENT -q " + ATTACH TABLE $CURR_DATABASE.log; + "; + done +} + +function recreate_lazy_func2() +{ + while true; do + $CLICKHOUSE_CLIENT -q " + CREATE TABLE $CURR_DATABASE.tlog (a UInt64, b UInt64) ENGINE = TinyLog; + "; + + $CLICKHOUSE_CLIENT -q " + DROP TABLE $CURR_DATABASE.tlog; + "; + done +} + +function recreate_lazy_func3() +{ + $CLICKHOUSE_CLIENT -q " + CREATE TABLE $CURR_DATABASE.slog (a UInt64, b UInt64) ENGINE = StripeLog; + "; + + while true; do + $CLICKHOUSE_CLIENT -q " + ATTACH TABLE $CURR_DATABASE.slog; + "; + + $CLICKHOUSE_CLIENT -q " + DETACH TABLE $CURR_DATABASE.slog; + "; + done +} + +function recreate_lazy_func4() +{ + while true; do + $CLICKHOUSE_CLIENT -q " + CREATE TABLE $CURR_DATABASE.tlog2 (a UInt64, b UInt64) ENGINE = TinyLog; + "; + + $CLICKHOUSE_CLIENT -q " + DROP TABLE $CURR_DATABASE.tlog2; + "; + done +} + +function test_func() +{ + while true; do + $CLICKHOUSE_CLIENT -q "SYSTEM STOP TTL MERGES"; + done +} + + +export -f recreate_lazy_func1; +export -f recreate_lazy_func2; +export -f recreate_lazy_func3; +export -f recreate_lazy_func4; +export -f test_func; + + +${CLICKHOUSE_CLIENT} -n -q " + DROP DATABASE IF EXISTS $CURR_DATABASE; + CREATE DATABASE $CURR_DATABASE ENGINE = Lazy(1); +" + + +TIMEOUT=30 + +timeout $TIMEOUT bash -c recreate_lazy_func1 2> /dev/null & +timeout $TIMEOUT bash -c recreate_lazy_func2 2> /dev/null & +timeout $TIMEOUT bash -c recreate_lazy_func3 2> /dev/null & +timeout $TIMEOUT bash -c recreate_lazy_func4 2> /dev/null & +timeout $TIMEOUT bash -c test_func 2> /dev/null & + +wait +sleep 1 + +${CLICKHOUSE_CLIENT} -n -q " + DROP TABLE IF EXISTS $CURR_DATABASE.log; + DROP TABLE IF EXISTS $CURR_DATABASE.slog; + DROP TABLE IF EXISTS $CURR_DATABASE.tlog; + DROP TABLE IF EXISTS $CURR_DATABASE.tlog2; +" + +$CLICKHOUSE_CLIENT -q "SYSTEM START TTL MERGES"; +echo "Test OK" diff --git a/tests/queries/0_stateless/01294_system_distributed_on_cluster.reference b/tests/queries/0_stateless/01294_system_distributed_on_cluster.reference new file mode 100644 index 00000000000..a8b5d159c9c --- /dev/null +++ b/tests/queries/0_stateless/01294_system_distributed_on_cluster.reference @@ -0,0 +1,3 @@ +localhost 9000 0 0 0 +localhost 9000 0 0 0 +localhost 9000 0 0 0 diff --git a/tests/queries/0_stateless/01294_system_distributed_on_cluster.sql b/tests/queries/0_stateless/01294_system_distributed_on_cluster.sql new file mode 100644 index 00000000000..d56bddba3c6 --- /dev/null +++ b/tests/queries/0_stateless/01294_system_distributed_on_cluster.sql @@ -0,0 +1,21 @@ +-- just a smoke test + +-- quirk for ON CLUSTER does not uses currentDatabase() +drop database if exists db_01294; +create database db_01294; + +drop table if exists db_01294.dist_01294; +create table db_01294.dist_01294 as system.one engine=Distributed(test_shard_localhost, system, one); +-- flush +system flush distributed db_01294.dist_01294; +system flush distributed on cluster test_shard_localhost db_01294.dist_01294; +-- stop +system stop distributed sends; +system stop distributed sends db_01294.dist_01294; +system stop distributed sends on cluster test_shard_localhost db_01294.dist_01294; +-- start +system start distributed sends; +system start distributed sends db_01294.dist_01294; +system start distributed sends on cluster test_shard_localhost db_01294.dist_01294; + +drop database db_01294; diff --git a/tests/queries/0_stateless/01295_aggregation_bug_11413.reference b/tests/queries/0_stateless/01295_aggregation_bug_11413.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/tests/queries/0_stateless/01295_aggregation_bug_11413.reference @@ -0,0 +1 @@ +1 diff --git a/tests/queries/0_stateless/01295_aggregation_bug_11413.sql b/tests/queries/0_stateless/01295_aggregation_bug_11413.sql new file mode 100644 index 00000000000..ec43be9eab3 --- /dev/null +++ b/tests/queries/0_stateless/01295_aggregation_bug_11413.sql @@ -0,0 +1 @@ +SELECT 1 FROM remote('127.0.0.{1,2}', numbers(99)) GROUP BY materialize(1) HAVING count() > 0 AND argMax(1, tuple(0)) diff --git a/tests/queries/0_stateless/01296_codecs_bad_arguments.reference b/tests/queries/0_stateless/01296_codecs_bad_arguments.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/tests/queries/0_stateless/01296_codecs_bad_arguments.reference @@ -0,0 +1 @@ +1 diff --git a/tests/queries/0_stateless/01296_codecs_bad_arguments.sql b/tests/queries/0_stateless/01296_codecs_bad_arguments.sql new file mode 100644 index 00000000000..d7eb53300ec --- /dev/null +++ b/tests/queries/0_stateless/01296_codecs_bad_arguments.sql @@ -0,0 +1,15 @@ +DROP TABLE IF EXISTS delta_table; +DROP TABLE IF EXISTS zstd_table; +DROP TABLE IF EXISTS lz4_table; + +CREATE TABLE delta_table (`id` UInt64 CODEC(Delta(tuple()))) ENGINE = MergeTree() ORDER BY tuple(); --{serverError 433} +CREATE TABLE zstd_table (`id` UInt64 CODEC(ZSTD(tuple()))) ENGINE = MergeTree() ORDER BY tuple(); --{serverError 433} +CREATE TABLE lz4_table (`id` UInt64 CODEC(LZ4HC(tuple()))) ENGINE = MergeTree() ORDER BY tuple(); --{serverError 433} + +CREATE TABLE lz4_table (`id` UInt64 CODEC(LZ4(tuple()))) ENGINE = MergeTree() ORDER BY tuple(); --{serverError 378} + +SELECT 1; + +DROP TABLE IF EXISTS delta_table; +DROP TABLE IF EXISTS zstd_table; +DROP TABLE IF EXISTS lz4_table; diff --git a/tests/queries/0_stateless/01296_pipeline_stuck.reference b/tests/queries/0_stateless/01296_pipeline_stuck.reference new file mode 100644 index 00000000000..ed8de641763 --- /dev/null +++ b/tests/queries/0_stateless/01296_pipeline_stuck.reference @@ -0,0 +1,13 @@ +1 +INSERT SELECT +1 +1 +INSERT SELECT max_threads +1 +1 +1 +INSERT SELECT max_insert_threads max_threads +1 +1 +1 +1 diff --git a/tests/queries/0_stateless/01296_pipeline_stuck.sql b/tests/queries/0_stateless/01296_pipeline_stuck.sql new file mode 100644 index 00000000000..eeb67362634 --- /dev/null +++ b/tests/queries/0_stateless/01296_pipeline_stuck.sql @@ -0,0 +1,18 @@ +drop table if exists data_01295; +create table data_01295 (key Int) Engine=AggregatingMergeTree() order by key; + +insert into data_01295 values (1); +select * from data_01295; + +select 'INSERT SELECT'; +insert into data_01295 select * from data_01295; -- no stuck for now +select * from data_01295; + +select 'INSERT SELECT max_threads'; +insert into data_01295 select * from data_01295 final settings max_threads=2; -- stuck with multiple threads +select * from data_01295; + +select 'INSERT SELECT max_insert_threads max_threads'; +set max_insert_threads=2; +insert into data_01295 select * from data_01295 final settings max_threads=2; -- no stuck for now +select * from data_01295; diff --git a/tests/queries/0_stateless/01297_alter_distributed.reference b/tests/queries/0_stateless/01297_alter_distributed.reference new file mode 100644 index 00000000000..bd269322884 --- /dev/null +++ b/tests/queries/0_stateless/01297_alter_distributed.reference @@ -0,0 +1,18 @@ +CounterID UInt32 +dummy String +StartDate Date +Sign Int8 +VisitID UInt64 +UserID UInt64 +StartTime DateTime +ClickLogID UInt64 +CREATE TABLE default.merge_distributed\n(\n `CounterID` UInt32, \n `dummy` String, \n `StartDate` Date, \n `Sign` Int8, \n `VisitID` UInt64, \n `UserID` UInt64, \n `StartTime` DateTime, \n `ClickLogID` UInt64\n)\nENGINE = Distributed(\'test_shard_localhost\', \'default\', \'merge_distributed1\') +1 Hello, Alter Table! +CounterID UInt32 +StartDate Date +Sign Int8 +VisitID UInt64 +UserID UInt64 +StartTime DateTime +ClickLogID UInt64 +CREATE TABLE default.merge_distributed\n(\n `CounterID` UInt32, \n `StartDate` Date, \n `Sign` Int8, \n `VisitID` UInt64, \n `UserID` UInt64, \n `StartTime` DateTime, \n `ClickLogID` UInt64\n)\nENGINE = Distributed(\'test_shard_localhost\', \'default\', \'merge_distributed1\') diff --git a/tests/queries/0_stateless/01297_alter_distributed.sql b/tests/queries/0_stateless/01297_alter_distributed.sql new file mode 100644 index 00000000000..d5359cc5ea8 --- /dev/null +++ b/tests/queries/0_stateless/01297_alter_distributed.sql @@ -0,0 +1,28 @@ +drop table if exists merge_distributed; +drop table if exists merge_distributed1; + +create table merge_distributed1 ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), tuple(CounterID, StartDate, intHash32(UserID), VisitID, ClickLogID), 8192, Sign); +insert into merge_distributed1 values (1, '2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); + +create table merge_distributed ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = Distributed(test_shard_localhost, currentDatabase(), merge_distributed1); + +alter table merge_distributed1 add column dummy String after CounterID; +alter table merge_distributed add column dummy String after CounterID; + +describe table merge_distributed; +show create table merge_distributed; + +insert into merge_distributed1 values (1, 'Hello, Alter Table!','2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); +select CounterID, dummy from merge_distributed where dummy <> '' limit 10; + +alter table merge_distributed drop column dummy; + +describe table merge_distributed; +show create table merge_distributed; + +--error: should fall, because there is no `dummy1` column +alter table merge_distributed add column dummy1 String after CounterID; +select CounterID, dummy1 from merge_distributed where dummy1 <> '' limit 10; -- { serverError 47 } + +drop table merge_distributed; +drop table merge_distributed1; diff --git a/tests/queries/0_stateless/01298_alter_merge.reference b/tests/queries/0_stateless/01298_alter_merge.reference new file mode 100644 index 00000000000..393c0a600ff --- /dev/null +++ b/tests/queries/0_stateless/01298_alter_merge.reference @@ -0,0 +1,17 @@ +CounterID UInt32 +dummy String +StartDate Date +Sign Int8 +VisitID UInt64 +UserID UInt64 +StartTime DateTime +ClickLogID UInt64 +CREATE TABLE default.merge\n(\n `CounterID` UInt32, \n `dummy` String, \n `StartDate` Date, \n `Sign` Int8, \n `VisitID` UInt64, \n `UserID` UInt64, \n `StartTime` DateTime, \n `ClickLogID` UInt64\n)\nENGINE = Merge(\'default\', \'merge\\\\[0-9\\\\]\') +CounterID UInt32 +StartDate Date +Sign Int8 +VisitID UInt64 +UserID UInt64 +StartTime DateTime +ClickLogID UInt64 +CREATE TABLE default.merge\n(\n `CounterID` UInt32, \n `StartDate` Date, \n `Sign` Int8, \n `VisitID` UInt64, \n `UserID` UInt64, \n `StartTime` DateTime, \n `ClickLogID` UInt64\n)\nENGINE = Merge(\'default\', \'merge\\\\[0-9\\\\]\') diff --git a/tests/queries/0_stateless/01298_alter_merge.sql b/tests/queries/0_stateless/01298_alter_merge.sql new file mode 100644 index 00000000000..86c89c38c8c --- /dev/null +++ b/tests/queries/0_stateless/01298_alter_merge.sql @@ -0,0 +1,36 @@ +drop table if exists merge; +drop table if exists merge1; +drop table if exists merge2; + +create table merge1 ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), tuple(CounterID, StartDate, intHash32(UserID), VisitID, ClickLogID), 8192, Sign); +insert into merge1 values (1, '2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); + +create table merge2 ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), tuple(CounterID, StartDate, intHash32(UserID), VisitID, ClickLogID), 8192, Sign); +insert into merge2 values (2, '2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); + +create table merge ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = Merge(currentDatabase(), 'merge\[0-9\]'); + +alter table merge1 add column dummy String after CounterID; +alter table merge2 add column dummy String after CounterID; +alter table merge add column dummy String after CounterID; + +describe table merge; +show create table merge; + +insert into merge1 values (1, 'Hello, Alter Table!','2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3); + +select CounterID, dummy from merge where dummy <> '' limit 10; + + +alter table merge drop column dummy; + +describe table merge; +show create table merge; + +--error: must correctly fall into the alter +alter table merge add column dummy1 String after CounterID; +select CounterID, dummy1 from merge where dummy1 <> '' limit 10; + +drop table merge; +drop table merge1; +drop table merge2; diff --git a/tests/queries/0_stateless/01299_alter_merge_tree.reference b/tests/queries/0_stateless/01299_alter_merge_tree.reference new file mode 100644 index 00000000000..d641115026b --- /dev/null +++ b/tests/queries/0_stateless/01299_alter_merge_tree.reference @@ -0,0 +1,16 @@ +CounterID UInt32 +dummy String +StartDate Date +Sign Int8 +VisitID UInt64 +UserID UInt64 +StartTime DateTime +ClickLogID UInt64 +1 Hello, Alter Table! +CounterID UInt32 +StartDate Date +Sign Int8 +VisitID UInt64 +UserID UInt64 +StartTime DateTime +ClickLogID UInt64 diff --git a/tests/queries/0_stateless/01299_alter_merge_tree.sql b/tests/queries/0_stateless/01299_alter_merge_tree.sql new file mode 100644 index 00000000000..87608e6d15a --- /dev/null +++ b/tests/queries/0_stateless/01299_alter_merge_tree.sql @@ -0,0 +1,17 @@ +drop table if exists merge_tree; + +create table merge_tree ( CounterID UInt32, StartDate Date, Sign Int8, VisitID UInt64, UserID UInt64, StartTime DateTime, ClickLogID UInt64) ENGINE = CollapsingMergeTree(StartDate, intHash32(UserID), tuple(CounterID, StartDate, intHash32(UserID), VisitID, ClickLogID), 8192, Sign); + +insert into merge_tree values (1, '2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3) +alter table merge_tree add column dummy String after CounterID; +describe table merge_tree; + +insert into merge_tree values (1, 'Hello, Alter Table!','2013-09-19', 1, 0, 2, '2013-09-19 12:43:06', 3) + +select CounterID, dummy from merge_tree where dummy <> '' limit 10; + +alter table merge_tree drop column dummy; + +describe table merge_tree; + +drop table merge_tree; diff --git a/tests/queries/0_stateless/01300_client_save_history_when_terminated.reference b/tests/queries/0_stateless/01300_client_save_history_when_terminated.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/01300_client_save_history_when_terminated.sh b/tests/queries/0_stateless/01300_client_save_history_when_terminated.sh new file mode 100755 index 00000000000..5ffcbbda883 --- /dev/null +++ b/tests/queries/0_stateless/01300_client_save_history_when_terminated.sh @@ -0,0 +1,35 @@ +#!/usr/bin/expect -f + +log_user 0 +set timeout 60 +match_max 100000 + +spawn clickhouse-client +expect ":) " + +# Make a query +send -- "SELECT 'for the history'\r" +expect "for the history" +expect ":) " + +# Kill the client to check if the history was saved +exec kill -9 [exp_pid] +close + +# Run client one more time and press "up" to see the last recorded query +spawn clickhouse-client +expect ":) " +send -- "\[A" +expect "SELECT 'for the history'" + +# Will check that Ctrl+C clears current line. +send -- "\3" +expect ":)" + +# Will check that second Ctrl+C invocation does not exit from client. +send -- "\3" +expect ":)" + +# But Ctrl+D does. +send -- "\4" +expect eof diff --git a/tests/queries/0_stateless/01301_aggregate_state_exception_memory_leak.reference b/tests/queries/0_stateless/01301_aggregate_state_exception_memory_leak.reference new file mode 100644 index 00000000000..b20e7415f52 --- /dev/null +++ b/tests/queries/0_stateless/01301_aggregate_state_exception_memory_leak.reference @@ -0,0 +1,2 @@ +Memory limit (for query) exceeded +Ok diff --git a/tests/queries/0_stateless/01301_aggregate_state_exception_memory_leak.sh b/tests/queries/0_stateless/01301_aggregate_state_exception_memory_leak.sh new file mode 100755 index 00000000000..633fa5ce315 --- /dev/null +++ b/tests/queries/0_stateless/01301_aggregate_state_exception_memory_leak.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +function test() +{ + for i in {1..1000}; do + $CLICKHOUSE_CLIENT --max_memory_usage 1G <<< "SELECT uniqExactState(number) FROM system.numbers_mt GROUP BY number % 10"; + done +} + +export -f test; + +# If the memory leak exists, it will lead to OOM fairly quickly. +timeout 30 bash -c test 2>&1 | grep -o -F 'Memory limit (for query) exceeded' | uniq +echo 'Ok' diff --git a/tests/queries/0_stateless/01302_aggregate_state_exception_memory_leak.reference b/tests/queries/0_stateless/01302_aggregate_state_exception_memory_leak.reference new file mode 100644 index 00000000000..7326d960397 --- /dev/null +++ b/tests/queries/0_stateless/01302_aggregate_state_exception_memory_leak.reference @@ -0,0 +1 @@ +Ok diff --git a/tests/queries/0_stateless/01302_aggregate_state_exception_memory_leak.sh b/tests/queries/0_stateless/01302_aggregate_state_exception_memory_leak.sh new file mode 100755 index 00000000000..cd2fec408ab --- /dev/null +++ b/tests/queries/0_stateless/01302_aggregate_state_exception_memory_leak.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +function test() +{ + for i in {1..250}; do + $CLICKHOUSE_CLIENT --query "SELECT groupArrayIfState(('Hello, world' AS s) || s || s || s || s || s || s || s || s || s, NOT throwIf(number > 10000000, 'Ok')) FROM system.numbers_mt GROUP BY number % 10"; + done +} + +export -f test; + +# If the memory leak exists, it will lead to OOM fairly quickly. +timeout 30 bash -c test 2>&1 | grep -o -F 'Ok' | uniq diff --git a/tests/queries/0_stateless/01304_direct_io.reference b/tests/queries/0_stateless/01304_direct_io.reference new file mode 100644 index 00000000000..ec7a223ddc2 --- /dev/null +++ b/tests/queries/0_stateless/01304_direct_io.reference @@ -0,0 +1 @@ +Loaded 1 queries. diff --git a/tests/queries/0_stateless/01304_direct_io.sh b/tests/queries/0_stateless/01304_direct_io.sh new file mode 100755 index 00000000000..32091acd5eb --- /dev/null +++ b/tests/queries/0_stateless/01304_direct_io.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +$CLICKHOUSE_CLIENT --multiquery --query " + DROP TABLE IF EXISTS bug; + CREATE TABLE bug (UserID UInt64, Date Date) ENGINE = MergeTree ORDER BY Date; + INSERT INTO bug SELECT rand64(), '2020-06-07' FROM numbers(50000000); + OPTIMIZE TABLE bug FINAL;" + +$CLICKHOUSE_BENCHMARK --database $CLICKHOUSE_DATABASE --iterations 10 --max_threads 100 --min_bytes_to_use_direct_io 1 <<< "SELECT sum(UserID) FROM bug PREWHERE NOT ignore(Date)" 1>/dev/null 2>$CLICKHOUSE_TMP/err +cat $CLICKHOUSE_TMP/err | grep Exception +cat $CLICKHOUSE_TMP/err | grep Loaded + +$CLICKHOUSE_CLIENT --multiquery --query " + DROP TABLE bug;" diff --git a/tests/queries/0_stateless/01305_array_join_prewhere_in_subquery.reference b/tests/queries/0_stateless/01305_array_join_prewhere_in_subquery.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/tests/queries/0_stateless/01305_array_join_prewhere_in_subquery.reference @@ -0,0 +1 @@ +1 diff --git a/tests/queries/0_stateless/01305_array_join_prewhere_in_subquery.sql b/tests/queries/0_stateless/01305_array_join_prewhere_in_subquery.sql new file mode 100644 index 00000000000..535dee5ebbe --- /dev/null +++ b/tests/queries/0_stateless/01305_array_join_prewhere_in_subquery.sql @@ -0,0 +1,5 @@ +drop table if exists h; +create table h (EventDate Date, CounterID UInt64, WatchID UInt64) engine = MergeTree order by (CounterID, EventDate); +insert into h values ('2020-06-10', 16671268, 1); +SELECT count() from h ARRAY JOIN [1] AS a PREWHERE WatchID IN (SELECT toUInt64(1)) WHERE (EventDate = '2020-06-10') AND (CounterID = 16671268); +drop table if exists h; diff --git a/tests/queries/0_stateless/01305_nullable-prewhere_bug.reference b/tests/queries/0_stateless/01305_nullable-prewhere_bug.reference new file mode 100644 index 00000000000..bd81ae06cec --- /dev/null +++ b/tests/queries/0_stateless/01305_nullable-prewhere_bug.reference @@ -0,0 +1 @@ +some_field_value 1 diff --git a/tests/queries/0_stateless/01305_nullable-prewhere_bug.sql b/tests/queries/0_stateless/01305_nullable-prewhere_bug.sql new file mode 100644 index 00000000000..35d3762660f --- /dev/null +++ b/tests/queries/0_stateless/01305_nullable-prewhere_bug.sql @@ -0,0 +1,5 @@ +drop table if exists data; +CREATE TABLE data (ts DateTime, field String, num_field Nullable(Float64)) ENGINE = MergeTree() PARTITION BY ts ORDER BY ts; +insert into data values(toDateTime('2020-05-14 02:08:00'),'some_field_value',7.); +SELECT field, countIf(num_field > 6.0) FROM data PREWHERE (num_field>6.0) GROUP BY field; +drop table if exists data; diff --git a/tests/queries/0_stateless/01305_replica_create_drop_zookeeper.reference b/tests/queries/0_stateless/01305_replica_create_drop_zookeeper.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/tests/queries/0_stateless/01305_replica_create_drop_zookeeper.sh b/tests/queries/0_stateless/01305_replica_create_drop_zookeeper.sh new file mode 100755 index 00000000000..03325d3da13 --- /dev/null +++ b/tests/queries/0_stateless/01305_replica_create_drop_zookeeper.sh @@ -0,0 +1,28 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +set -e + +function thread() +{ + while true; do + $CLICKHOUSE_CLIENT -n -q "DROP TABLE IF EXISTS test_table_$1; + CREATE TABLE test_table_$1 (a UInt8) ENGINE = ReplicatedMergeTree('/clickhouse/tables/alter_table', 'r_$1') ORDER BY tuple();" 2>&1 | + grep -vP '(^$)|(^Received exception from server)|(^\d+\. )|because the last replica of the table was dropped right now|is already started to be removing by another replica right now|is already finished removing by another replica right now|Removing leftovers from table|Another replica was suddenly created|was successfully removed from ZooKeeper|was created by another server at the same moment|was suddenly removed' + done +} + + +# https://stackoverflow.com/questions/9954794/execute-a-shell-function-with-timeout +export -f thread; + +TIMEOUT=10 + +timeout $TIMEOUT bash -c 'thread 1' & +timeout $TIMEOUT bash -c 'thread 2' & + +wait + +for i in {1,2}; do $CLICKHOUSE_CLIENT -q "DROP TABLE IF EXISTS test_table_$i"; done diff --git a/tests/queries/0_stateless/01306_benchmark_json.reference b/tests/queries/0_stateless/01306_benchmark_json.reference new file mode 100644 index 00000000000..d00491fd7e5 --- /dev/null +++ b/tests/queries/0_stateless/01306_benchmark_json.reference @@ -0,0 +1 @@ +1 diff --git a/tests/queries/0_stateless/01306_benchmark_json.sh b/tests/queries/0_stateless/01306_benchmark_json.sh new file mode 100755 index 00000000000..3b9a9b93180 --- /dev/null +++ b/tests/queries/0_stateless/01306_benchmark_json.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +set -e + +$CLICKHOUSE_BENCHMARK --iterations 10 --json ${CLICKHOUSE_TMP}/out.json <<< "SELECT 1" 2>/dev/null && cat ${CLICKHOUSE_TMP}/out.json | + $CLICKHOUSE_LOCAL --input-format JSONAsString --structure "s String" --query "SELECT isValidJSON(s) FROM table" diff --git a/tests/queries/0_stateless/01307_multiple_leaders.reference b/tests/queries/0_stateless/01307_multiple_leaders.reference new file mode 100644 index 00000000000..576441b288d --- /dev/null +++ b/tests/queries/0_stateless/01307_multiple_leaders.reference @@ -0,0 +1,2 @@ +2000 1999000 +2000 1999000 diff --git a/tests/queries/0_stateless/01307_multiple_leaders.sh b/tests/queries/0_stateless/01307_multiple_leaders.sh new file mode 100755 index 00000000000..0bf5e0b13bf --- /dev/null +++ b/tests/queries/0_stateless/01307_multiple_leaders.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +set -e + +$CLICKHOUSE_CLIENT -n --query " +DROP TABLE IF EXISTS r0; +DROP TABLE IF EXISTS r1; + +CREATE TABLE r0 (x UInt64) ENGINE = ReplicatedMergeTree('/test/table', 'r0') ORDER BY x SETTINGS min_bytes_for_wide_part = '10M'; +CREATE TABLE r1 (x UInt64) ENGINE = ReplicatedMergeTree('/test/table', 'r1') ORDER BY x SETTINGS min_bytes_for_wide_part = '10M'; +" + +function thread() +{ + REPLICA=$1 + ITERATIONS=$2 + + $CLICKHOUSE_CLIENT --max_block_size 1 --min_insert_block_size_rows 0 --min_insert_block_size_bytes 0 --query "INSERT INTO r$REPLICA SELECT number * 2 + $REPLICA FROM numbers($ITERATIONS)" +} + + +thread 0 1000 & +thread 1 1000 & + +wait + +$CLICKHOUSE_CLIENT -n --query " +SYSTEM SYNC REPLICA r0; +SYSTEM SYNC REPLICA r1; + +SELECT count(), sum(x) FROM r0; +SELECT count(), sum(x) FROM r1; + +DROP TABLE r0; +DROP TABLE r1; +" diff --git a/tests/queries/0_stateless/01308_row_policy_and_trivial_count_query.reference b/tests/queries/0_stateless/01308_row_policy_and_trivial_count_query.reference new file mode 100644 index 00000000000..61150aca43c --- /dev/null +++ b/tests/queries/0_stateless/01308_row_policy_and_trivial_count_query.reference @@ -0,0 +1,3 @@ +3 +2 +3 diff --git a/tests/queries/0_stateless/01308_row_policy_and_trivial_count_query.sql b/tests/queries/0_stateless/01308_row_policy_and_trivial_count_query.sql new file mode 100644 index 00000000000..c105885cb60 --- /dev/null +++ b/tests/queries/0_stateless/01308_row_policy_and_trivial_count_query.sql @@ -0,0 +1,12 @@ +DROP TABLE IF EXISTS t; + +CREATE TABLE t (x UInt8) ENGINE = MergeTree ORDER BY x; +INSERT INTO t VALUES (1), (2), (3); + +SELECT count() FROM t; +CREATE ROW POLICY filter ON t USING (x % 2 = 1) TO ALL; +SELECT count() FROM t; +DROP ROW POLICY filter ON t; +SELECT count() FROM t; + +DROP TABLE t; diff --git a/tests/queries/0_stateless/01310_enum_comparison.reference b/tests/queries/0_stateless/01310_enum_comparison.reference new file mode 100644 index 00000000000..b261da18d51 --- /dev/null +++ b/tests/queries/0_stateless/01310_enum_comparison.reference @@ -0,0 +1,2 @@ +1 +0 diff --git a/tests/queries/0_stateless/01310_enum_comparison.sql b/tests/queries/0_stateless/01310_enum_comparison.sql new file mode 100644 index 00000000000..26901a61b2b --- /dev/null +++ b/tests/queries/0_stateless/01310_enum_comparison.sql @@ -0,0 +1,6 @@ +CREATE TEMPORARY TABLE enum (x Enum('hello' = 1, 'world' = 2)); +INSERT INTO enum VALUES ('hello'); + +SELECT count() FROM enum WHERE x = 'hello'; +SELECT count() FROM enum WHERE x = 'world'; +SELECT count() FROM enum WHERE x = 'xyz'; -- { serverError 36 } diff --git a/tests/queries/0_stateless/01311_comparison_with_constant_string.reference b/tests/queries/0_stateless/01311_comparison_with_constant_string.reference new file mode 100644 index 00000000000..7865f42932d --- /dev/null +++ b/tests/queries/0_stateless/01311_comparison_with_constant_string.reference @@ -0,0 +1,32 @@ +0 +1 +0 +--- +1 +0 +1 +--- +1 +0 +0 +--- +0 +--- +1 +0 +--- +--- +0 +--- +1 +--- +0 +--- +0 +--- +0 +--- +0 +--- +1 +--- diff --git a/tests/queries/0_stateless/01311_comparison_with_constant_string.sql b/tests/queries/0_stateless/01311_comparison_with_constant_string.sql new file mode 100644 index 00000000000..d6641a50c45 --- /dev/null +++ b/tests/queries/0_stateless/01311_comparison_with_constant_string.sql @@ -0,0 +1,33 @@ +SELECT number = '1' FROM numbers(3); +SELECT '---'; +SELECT '1' != number FROM numbers(3); +SELECT '---'; +SELECT '1' > number FROM numbers(3); +SELECT '---'; +SELECT 1 = '257'; +SELECT '---'; +SELECT 1 IN (1.23, '1', 2); +SELECT 1 IN (1.23, '2', 2); +SELECT '---'; + +-- it should work but it doesn't. +SELECT 1 = '1.0'; -- { serverError 53 } +SELECT '---'; + +SELECT 1 = '257'; +SELECT '---'; +SELECT 1 != '257'; +SELECT '---'; +SELECT 1 < '257'; -- this is wrong for now +SELECT '---'; +SELECT 1 > '257'; +SELECT '---'; +SELECT 1 <= '257'; -- this is wrong for now +SELECT '---'; +SELECT 1 >= '257'; +SELECT '---'; + +SELECT toDateTime('2020-06-13 01:02:03') = '2020-06-13T01:02:03'; +SELECT '---'; + +SELECT 0 = ''; -- { serverError 32 } diff --git a/tests/queries/0_stateless/01312_case_insensitive_regexp.reference b/tests/queries/0_stateless/01312_case_insensitive_regexp.reference new file mode 100644 index 00000000000..c18b4e9b082 --- /dev/null +++ b/tests/queries/0_stateless/01312_case_insensitive_regexp.reference @@ -0,0 +1,8 @@ +1 +1 +1 +1 +1 +1 +1 +1 diff --git a/tests/queries/0_stateless/01312_case_insensitive_regexp.sql b/tests/queries/0_stateless/01312_case_insensitive_regexp.sql new file mode 100644 index 00000000000..ca13989599d --- /dev/null +++ b/tests/queries/0_stateless/01312_case_insensitive_regexp.sql @@ -0,0 +1,8 @@ +SELECT match('Too late', 'Too late'); +select match('Too late', '(?i)Too late'); +select match('Too late', '(?i)too late'); +select match('Too late', '(?i:too late)'); +select match('Too late', '(?i)to{2} late'); +select match('Too late', '(?i)to(?)o late'); +select match('Too late', '(?i)to+ late'); +select match('Too late', '(?i)to(?:o|o) late'); diff --git a/tests/queries/0_stateless/01312_skip_empty_params.reference b/tests/queries/0_stateless/01312_skip_empty_params.reference new file mode 100644 index 00000000000..e8183f05f5d --- /dev/null +++ b/tests/queries/0_stateless/01312_skip_empty_params.reference @@ -0,0 +1,3 @@ +1 +1 +1 diff --git a/tests/queries/0_stateless/01312_skip_empty_params.sh b/tests/queries/0_stateless/01312_skip_empty_params.sh new file mode 100755 index 00000000000..2e3541aee35 --- /dev/null +++ b/tests/queries/0_stateless/01312_skip_empty_params.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +set -e + +$CLICKHOUSE_CURL -sS "$CLICKHOUSE_URL&query=select%201&log_queries=1" +$CLICKHOUSE_CURL -sS "$CLICKHOUSE_URL&&query=select%201&log_queries=1" +$CLICKHOUSE_CURL -sS "$CLICKHOUSE_URL&query=select%201&&&log_queries=1" diff --git a/tests/queries/0_stateless/01313_parse_date_time_best_effort_null_zero.reference b/tests/queries/0_stateless/01313_parse_date_time_best_effort_null_zero.reference new file mode 100644 index 00000000000..90bb776ca10 --- /dev/null +++ b/tests/queries/0_stateless/01313_parse_date_time_best_effort_null_zero.reference @@ -0,0 +1,6 @@ +\N +0000-00-00 00:00:00 +\N +0000-00-00 00:00:00.000 +\N +0000-00-00 00:00:00 diff --git a/tests/queries/0_stateless/01313_parse_date_time_best_effort_null_zero.sql b/tests/queries/0_stateless/01313_parse_date_time_best_effort_null_zero.sql new file mode 100644 index 00000000000..69b66b46df7 --- /dev/null +++ b/tests/queries/0_stateless/01313_parse_date_time_best_effort_null_zero.sql @@ -0,0 +1,12 @@ +SELECT parseDateTimeBestEffort(''); -- { serverError 6 } +SELECT parseDateTimeBestEffortOrNull(''); +SELECT parseDateTimeBestEffortOrZero(''); + +SELECT parseDateTime64BestEffort(''); -- { serverError 6 } +SELECT parseDateTime64BestEffortOrNull(''); +SELECT parseDateTime64BestEffortOrZero(''); + +SET date_time_input_format = 'best_effort'; +SELECT toDateTime(''); -- { serverError 41 } +SELECT toDateTimeOrNull(''); +SELECT toDateTimeOrZero(''); diff --git a/tests/queries/0_stateless/01314_position_in_system_columns.reference b/tests/queries/0_stateless/01314_position_in_system_columns.reference new file mode 100644 index 00000000000..32e0ae5900e --- /dev/null +++ b/tests/queries/0_stateless/01314_position_in_system_columns.reference @@ -0,0 +1,6 @@ +x UInt8 1 +y String 2 +z Array(String) 3 +x UInt8 1 +y String 2 +z Array(String) 3 diff --git a/tests/queries/0_stateless/01314_position_in_system_columns.sql b/tests/queries/0_stateless/01314_position_in_system_columns.sql new file mode 100644 index 00000000000..7bb0f3b5a96 --- /dev/null +++ b/tests/queries/0_stateless/01314_position_in_system_columns.sql @@ -0,0 +1,8 @@ +DROP TABLE IF EXISTS test; +CREATE TABLE test (x UInt8, y String, z Array(String)) ENGINE = MergeTree ORDER BY tuple(); +INSERT INTO test (x) VALUES (1); + +SELECT name, type, position FROM system.columns WHERE database = currentDatabase() AND table = 'test'; +SELECT column, type, column_position FROM system.parts_columns WHERE database = currentDatabase() AND table = 'test'; + +DROP TABLE test; diff --git a/tests/queries/0_stateless/01315_count_distinct_return_not_nullable.reference b/tests/queries/0_stateless/01315_count_distinct_return_not_nullable.reference new file mode 100644 index 00000000000..f8b77704aa3 --- /dev/null +++ b/tests/queries/0_stateless/01315_count_distinct_return_not_nullable.reference @@ -0,0 +1,9 @@ +0 +0 +0 +5 +5 +5 +0 +\N +\N diff --git a/tests/queries/0_stateless/01315_count_distinct_return_not_nullable.sql b/tests/queries/0_stateless/01315_count_distinct_return_not_nullable.sql new file mode 100644 index 00000000000..2d9b5ef54aa --- /dev/null +++ b/tests/queries/0_stateless/01315_count_distinct_return_not_nullable.sql @@ -0,0 +1,12 @@ +SELECT uniq(number >= 10 ? number : NULL) FROM numbers(10); +SELECT uniqExact(number >= 10 ? number : NULL) FROM numbers(10); +SELECT count(DISTINCT number >= 10 ? number : NULL) FROM numbers(10); + +SELECT uniq(number >= 5 ? number : NULL) FROM numbers(10); +SELECT uniqExact(number >= 5 ? number : NULL) FROM numbers(10); +SELECT count(DISTINCT number >= 5 ? number : NULL) FROM numbers(10); + +SELECT count(NULL); +-- These two returns NULL for now, but we want to change them to return 0. +SELECT uniq(NULL); +SELECT count(DISTINCT NULL); diff --git a/tests/queries/0_stateless/01316_create_user_syntax_hilite.reference b/tests/queries/0_stateless/01316_create_user_syntax_hilite.reference new file mode 100644 index 00000000000..ed7daeb3609 --- /dev/null +++ b/tests/queries/0_stateless/01316_create_user_syntax_hilite.reference @@ -0,0 +1 @@ +CREATE USER user IDENTIFIED WITH plaintext_password BY 'hello' diff --git a/tests/queries/0_stateless/01316_create_user_syntax_hilite.sh b/tests/queries/0_stateless/01316_create_user_syntax_hilite.sh new file mode 100755 index 00000000000..1031a96363c --- /dev/null +++ b/tests/queries/0_stateless/01316_create_user_syntax_hilite.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +set -e + +$CLICKHOUSE_FORMAT --hilite <<< "CREATE USER user IDENTIFIED WITH PLAINTEXT_PASSWORD BY 'hello'" diff --git a/tests/queries/0_stateless/01317_no_password_in_command_line.reference b/tests/queries/0_stateless/01317_no_password_in_command_line.reference new file mode 100644 index 00000000000..aa47d0d46d4 --- /dev/null +++ b/tests/queries/0_stateless/01317_no_password_in_command_line.reference @@ -0,0 +1,2 @@ +0 +0 diff --git a/tests/queries/0_stateless/01317_no_password_in_command_line.sh b/tests/queries/0_stateless/01317_no_password_in_command_line.sh new file mode 100755 index 00000000000..1a3ae88616a --- /dev/null +++ b/tests/queries/0_stateless/01317_no_password_in_command_line.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +. $CURDIR/../shell_config.sh + +set -e + +$CLICKHOUSE_CLIENT --query "DROP USER IF EXISTS user" +$CLICKHOUSE_CLIENT --query "CREATE USER user IDENTIFIED WITH PLAINTEXT_PASSWORD BY 'hello'" + +# False positive result due to race condition with sleeps is Ok. + +$CLICKHOUSE_CLIENT --user user --password hello --query "SELECT sleep(1)" & +sleep 0.1 +ps auxw | grep -F -- '--password' | grep -F hello ||: +wait + +$CLICKHOUSE_CLIENT --user user --password=hello --query "SELECT sleep(1)" & +sleep 0.1 +ps auxw | grep -F -- '--password' | grep -F hello ||: +wait + +$CLICKHOUSE_CLIENT --query "DROP USER user" diff --git a/tests/queries/1_stateful/00004_top_counters.reference b/tests/queries/1_stateful/00004_top_counters.reference index cf2824e45b0..e2d584170c0 100644 --- a/tests/queries/1_stateful/00004_top_counters.reference +++ b/tests/queries/1_stateful/00004_top_counters.reference @@ -8,3 +8,13 @@ 59183 85379 33010362 77807 800784 77492 +1704509 523264 +732797 475698 +598875 337212 +792887 252197 +3807842 196036 +25703952 147211 +716829 90109 +59183 85379 +33010362 77807 +800784 77492 diff --git a/tests/queries/1_stateful/00004_top_counters.sql b/tests/queries/1_stateful/00004_top_counters.sql index 045f940da42..abdd5ac794a 100644 --- a/tests/queries/1_stateful/00004_top_counters.sql +++ b/tests/queries/1_stateful/00004_top_counters.sql @@ -1 +1,2 @@ -SELECT CounterID, count() AS c FROM test.hits GROUP BY CounterID ORDER BY c DESC LIMIT 10 +SELECT CounterID, count() AS c FROM test.hits GROUP BY CounterID ORDER BY c DESC LIMIT 10; +SELECT CounterID, count() AS c FROM test.hits GROUP BY CounterID ORDER BY c DESC LIMIT 10 SETTINGS optimize_aggregation_in_order = 1 diff --git a/tests/queries/1_stateful/00047_bar.reference b/tests/queries/1_stateful/00047_bar.reference index 61999ae73c9..c038f59946e 100644 --- a/tests/queries/1_stateful/00047_bar.reference +++ b/tests/queries/1_stateful/00047_bar.reference @@ -98,3 +98,103 @@ 7901143 10022 █▌ 194599 9997 █▌ 21052498 9780 █▍ +1704509 523264 ████████████████████████████████████████████████████████████████████████████████ +732797 475698 ████████████████████████████████████████████████████████████████████████▋ +598875 337212 ███████████████████████████████████████████████████▌ +792887 252197 ██████████████████████████████████████▌ +3807842 196036 █████████████████████████████▊ +25703952 147211 ██████████████████████▌ +716829 90109 █████████████▋ +59183 85379 █████████████ +33010362 77807 ███████████▊ +800784 77492 ███████████▋ +20810645 73213 ███████████▏ +25843850 68945 ██████████▌ +23447120 67570 ██████████▎ +14739804 64174 █████████▋ +32077710 60456 █████████▏ +22446879 58389 ████████▊ +170282 57017 ████████▋ +11482817 52345 ████████ +63469 52142 ███████▊ +29103473 47758 ███████▎ +10136747 44080 ██████▋ +27528801 43395 ██████▋ +10581377 43279 ██████▌ +9841201 40581 ██████▏ +20310963 37562 █████▋ +17337667 34301 █████▏ +28600281 32776 █████ +32046685 28788 ████▍ +10130880 26603 ████ +8676831 25733 ███▊ +53230 25595 ███▊ +20271226 25585 ███▊ +17420663 25496 ███▊ +631207 25270 ███▋ +633130 24744 ███▋ +14324015 23349 ███▌ +8537965 21270 ███▎ +11285298 20825 ███▏ +14937615 20788 ███▏ +185050 20785 ███▏ +16368233 19897 ███ +81602 19724 ███ +62896 19717 ███ +12967664 19402 ██▊ +15996597 18557 ██▋ +4379238 18370 ██▋ +90982 17443 ██▋ +18211045 17390 ██▋ +14625884 17302 ██▋ +12864910 17279 ██▋ +126096 16959 ██▌ +30296134 16849 ██▌ +26360482 16175 ██▍ +17788950 16017 ██▍ +5928716 15340 ██▎ +15469035 15171 ██▎ +29732125 15146 ██▎ +32946244 15104 ██▎ +20957241 14719 ██▎ +9495695 14584 ██▏ +29241146 14540 ██▏ +109805 14199 ██▏ +26905788 13972 ██▏ +212019 13930 ██▏ +171509 13792 ██ +23913162 13615 ██ +1861993 13509 ██ +125776 13308 ██ +11312316 13181 ██ +32667326 13181 ██ +28628973 12922 █▊ +122804 12520 █▊ +12322758 12352 █▊ +1301819 12283 █▊ +10769545 12183 █▋ +21566939 12170 █▋ +28905364 12158 █▋ +4250765 12049 █▋ +15009727 11818 █▋ +12761932 11733 █▋ +26995888 11658 █▋ +12759346 11514 █▋ +1507911 11452 █▋ +968488 11444 █▋ +15736172 11358 █▋ +54310 11193 █▋ +17027391 11047 █▋ +17439919 10936 █▋ +4480860 10747 █▋ +26738469 10738 █▋ +9986231 10656 █▋ +1539995 10655 █▋ +214556 10625 █▌ +219339 10522 █▌ +3266 10503 █▌ +30563429 10128 █▌ +1960469 10098 █▌ +7901143 10022 █▌ +194599 9997 █▌ +21052498 9780 █▍ diff --git a/tests/queries/1_stateful/00047_bar.sql b/tests/queries/1_stateful/00047_bar.sql index c7310763525..37c420b91ff 100644 --- a/tests/queries/1_stateful/00047_bar.sql +++ b/tests/queries/1_stateful/00047_bar.sql @@ -1 +1,2 @@ -SELECT CounterID, count() AS c, bar(c, 0, 523264) FROM test.hits GROUP BY CounterID ORDER BY c DESC, CounterID ASC LIMIT 100 +SELECT CounterID, count() AS c, bar(c, 0, 523264) FROM test.hits GROUP BY CounterID ORDER BY c DESC, CounterID ASC LIMIT 100; +SELECT CounterID, count() AS c, bar(c, 0, 523264) FROM test.hits GROUP BY CounterID ORDER BY c DESC, CounterID ASC LIMIT 100 SETTINGS optimize_aggregation_in_order = 1 diff --git a/tests/queries/1_stateful/00049_max_string_if.reference b/tests/queries/1_stateful/00049_max_string_if.reference index f87bc6d1fd2..6897a773c87 100644 --- a/tests/queries/1_stateful/00049_max_string_if.reference +++ b/tests/queries/1_stateful/00049_max_string_if.reference @@ -18,3 +18,23 @@ 11482817 52345 я скачать игры 63469 52142 яндекс марте рокус надписями я любимому у полосы фото минск 29103473 47758 +1704509 523264 نيك امريكي نيك افلام سكس جامد +732797 475698 نيك سكس سيحاق +598875 337212 سکس باصات +792887 252197 №2267 отзыв +3807842 196036 ярмаркетовара 200кг купить по неделю тебелье +25703952 147211 +716829 90109 яндекс повыш +59183 85379 франция машину угловы крузер из кофе +33010362 77807 ярмаркетовара 200кг купить по неделю тебелье +800784 77492 ярмаркур смерти теплицы из чего +20810645 73213 ярмаркетовара 200кг купить по неделю тебе перево метиков детский +25843850 68945 электросчет-фактура +23447120 67570 южная степанов +14739804 64174 штангал волк +32077710 60456 +22446879 58389 فیلم سكس امريكي نيك +170282 57017 ل افلام السكس +11482817 52345 я скачать игры +63469 52142 яндекс марте рокус надписями я любимому у полосы фото минск +29103473 47758 diff --git a/tests/queries/1_stateful/00049_max_string_if.sql b/tests/queries/1_stateful/00049_max_string_if.sql index af87123ef02..5c6d4274bab 100644 --- a/tests/queries/1_stateful/00049_max_string_if.sql +++ b/tests/queries/1_stateful/00049_max_string_if.sql @@ -1 +1,2 @@ -SELECT CounterID, count(), maxIf(SearchPhrase, notEmpty(SearchPhrase)) FROM test.hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20 +SELECT CounterID, count(), maxIf(SearchPhrase, notEmpty(SearchPhrase)) FROM test.hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20; +SELECT CounterID, count(), maxIf(SearchPhrase, notEmpty(SearchPhrase)) FROM test.hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20 SETTINGS optimize_aggregation_in_order = 1 diff --git a/tests/queries/1_stateful/00050_min_max.reference b/tests/queries/1_stateful/00050_min_max.reference index ab47fd7a69a..91473c4ea17 100644 --- a/tests/queries/1_stateful/00050_min_max.reference +++ b/tests/queries/1_stateful/00050_min_max.reference @@ -18,3 +18,23 @@ 11482817 4611708000353743073 9223337838355779113 63469 4611695097019173921 9223353530156141191 29103473 4611744585914335132 9223333530281362537 +1704509 4611700827100483880 9223360787015464643 +732797 4611701940806302259 9223355550934604746 +598875 4611701407242345792 9223362250391155632 +792887 4611699550286611812 9223290551912005343 +3807842 4611710821592843606 9223326163906184987 +25703952 4611709443519524003 9223353913449113943 +716829 4611852156092872082 9223361623076951140 +59183 4611730685242027332 9223354909338698162 +33010362 4611704682869732882 9223268545373999677 +800784 4611752907938305166 9223340418389788041 +20810645 4611712185532639162 9223218900001937412 +25843850 4611690025407720929 9223346023778617822 +23447120 4611796031755620254 9223329309291309758 +14739804 4611692230555590277 9223313509005166531 +32077710 4611884228437061959 9223352444952988904 +22446879 4611846229717089436 9223124373140579096 +170282 4611833225706935900 9223371583739401906 +11482817 4611708000353743073 9223337838355779113 +63469 4611695097019173921 9223353530156141191 +29103473 4611744585914335132 9223333530281362537 diff --git a/tests/queries/1_stateful/00050_min_max.sql b/tests/queries/1_stateful/00050_min_max.sql index 4c45f6fffa6..1ca93a5d620 100644 --- a/tests/queries/1_stateful/00050_min_max.sql +++ b/tests/queries/1_stateful/00050_min_max.sql @@ -1 +1,2 @@ -SELECT CounterID, min(WatchID), max(WatchID) FROM test.hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20 +SELECT CounterID, min(WatchID), max(WatchID) FROM test.hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20; +SELECT CounterID, min(WatchID), max(WatchID) FROM test.hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20 SETTINGS optimize_aggregation_in_order = 1 diff --git a/tests/queries/1_stateful/00051_min_max_array.reference b/tests/queries/1_stateful/00051_min_max_array.reference index a5f1b6cdfef..b5555954099 100644 --- a/tests/queries/1_stateful/00051_min_max_array.reference +++ b/tests/queries/1_stateful/00051_min_max_array.reference @@ -18,3 +18,23 @@ 11482817 52345 [] [] [] 63469 52142 [] [] [] 29103473 47758 [6185451] [] [6185451] +1704509 523264 [271264] [] [271264] +732797 475698 [] [] [] +598875 337212 [] [] [] +792887 252197 [2094893,2028343] [] [1272031] +3807842 196036 [1710269] [] [1134660] +25703952 147211 [] [] [] +716829 90109 [4186138] [] [1800405] +59183 85379 [] [] [] +33010362 77807 [] [] [] +800784 77492 [4002316] [] [1270480] +20810645 73213 [] [] [] +25843850 68945 [4028285] [] [4028285] +23447120 67570 [6503091,2762273] [] [2098132] +14739804 64174 [4180720] [] [664490] +32077710 60456 [] [] [] +22446879 58389 [] [] [] +170282 57017 [4166114] [] [34386,1240412,1248634,1616213,2928740,1458582] +11482817 52345 [] [] [] +63469 52142 [] [] [] +29103473 47758 [6185451] [] [6185451] diff --git a/tests/queries/1_stateful/00051_min_max_array.sql b/tests/queries/1_stateful/00051_min_max_array.sql index 1027586372d..adf44fb9c22 100644 --- a/tests/queries/1_stateful/00051_min_max_array.sql +++ b/tests/queries/1_stateful/00051_min_max_array.sql @@ -1 +1,2 @@ -SELECT CounterID, count(), max(GoalsReached), min(GoalsReached), minIf(GoalsReached, notEmpty(GoalsReached)) FROM test.hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20 +SELECT CounterID, count(), max(GoalsReached), min(GoalsReached), minIf(GoalsReached, notEmpty(GoalsReached)) FROM test.hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20; +SELECT CounterID, count(), max(GoalsReached), min(GoalsReached), minIf(GoalsReached, notEmpty(GoalsReached)) FROM test.hits GROUP BY CounterID ORDER BY count() DESC LIMIT 20 SETTINGS optimize_aggregation_in_order = 1 diff --git a/tests/queries/1_stateful/00087_where_0.sql b/tests/queries/1_stateful/00087_where_0.sql index c55617d2245..33c325e53b8 100644 --- a/tests/queries/1_stateful/00087_where_0.sql +++ b/tests/queries/1_stateful/00087_where_0.sql @@ -1,3 +1,5 @@ SET max_rows_to_read = 1000; SELECT CounterID, uniq(UserID) FROM test.hits WHERE 0 != 0 GROUP BY CounterID; +SELECT CounterID, uniq(UserID) FROM test.hits WHERE 0 != 0 GROUP BY CounterID SETTINGS optimize_aggregation_in_order = 1; SELECT CounterID, uniq(UserID) FROM test.hits WHERE 0 AND CounterID = 1704509 GROUP BY CounterID; +SELECT CounterID, uniq(UserID) FROM test.hits WHERE 0 AND CounterID = 1704509 GROUP BY CounterID SETTINGS optimize_aggregation_in_order = 1; diff --git a/tests/queries/1_stateful/00149_quantiles_timing_distributed.reference b/tests/queries/1_stateful/00149_quantiles_timing_distributed.reference index 8ac5f01c7cc..e31a1e90d87 100644 --- a/tests/queries/1_stateful/00149_quantiles_timing_distributed.reference +++ b/tests/queries/1_stateful/00149_quantiles_timing_distributed.reference @@ -1 +1,2 @@ 10726001768429413598 +10726001768429413598 diff --git a/tests/queries/1_stateful/00149_quantiles_timing_distributed.sql b/tests/queries/1_stateful/00149_quantiles_timing_distributed.sql index b195518e1e7..dc63cb5867f 100644 --- a/tests/queries/1_stateful/00149_quantiles_timing_distributed.sql +++ b/tests/queries/1_stateful/00149_quantiles_timing_distributed.sql @@ -1 +1,2 @@ SELECT sum(cityHash64(*)) FROM (SELECT CounterID, quantileTiming(0.5)(SendTiming), count() FROM remote('127.0.0.{1,2,3,4,5,6,7,8,9,10}', test.hits) WHERE SendTiming != -1 GROUP BY CounterID); +SELECT sum(cityHash64(*)) FROM (SELECT CounterID, quantileTiming(0.5)(SendTiming), count() FROM remote('127.0.0.{1,2,3,4,5,6,7,8,9,10}', test.hits) WHERE SendTiming != -1 GROUP BY CounterID) SETTINGS optimize_aggregation_in_order = 1; diff --git a/tests/queries/1_stateful/00150_quantiles_timing_precision.reference b/tests/queries/1_stateful/00150_quantiles_timing_precision.reference index 09aaf8449dc..79ef24af591 100644 --- a/tests/queries/1_stateful/00150_quantiles_timing_precision.reference +++ b/tests/queries/1_stateful/00150_quantiles_timing_precision.reference @@ -1 +1,2 @@ 4379238 1868 1879 5755 0.006 +4379238 1868 1879 5755 0.006 diff --git a/tests/queries/1_stateful/00150_quantiles_timing_precision.sql b/tests/queries/1_stateful/00150_quantiles_timing_precision.sql index 7d5b27fafd3..e858bcf34ff 100644 --- a/tests/queries/1_stateful/00150_quantiles_timing_precision.sql +++ b/tests/queries/1_stateful/00150_quantiles_timing_precision.sql @@ -1 +1,2 @@ SELECT CounterID, quantileTiming(0.5)(SendTiming) AS qt, least(30000, quantileExact(0.5)(SendTiming)) AS qe, count() AS c, round(abs(qt - qe) / greatest(qt, qe) AS diff, 3) AS rounded_diff FROM test.hits WHERE SendTiming != -1 GROUP BY CounterID HAVING diff != 0 ORDER BY diff DESC; +SELECT CounterID, quantileTiming(0.5)(SendTiming) AS qt, least(30000, quantileExact(0.5)(SendTiming)) AS qe, count() AS c, round(abs(qt - qe) / greatest(qt, qe) AS diff, 3) AS rounded_diff FROM test.hits WHERE SendTiming != -1 GROUP BY CounterID HAVING diff != 0 ORDER BY diff DESC SETTINGS optimize_aggregation_in_order = 1; diff --git a/utils/build/build_debian.sh b/utils/build/build_debian.sh index 0c194fe53db..4ae54b0d29f 100755 --- a/utils/build/build_debian.sh +++ b/utils/build/build_debian.sh @@ -8,7 +8,7 @@ # install compiler and libs sudo apt install -y git bash cmake ninja-build gcc-8 g++-8 libicu-dev libreadline-dev gperf # for -DUNBUNDLED=1 mode: -#sudo apt install -y libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libboost-thread-dev zlib1g-dev liblz4-dev libdouble-conversion-dev libzstd-dev libre2-dev librdkafka-dev libcapnp-dev libpoco-dev libgoogle-perftools-dev libunwind-dev googletest libcctz-dev +#sudo apt install -y libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-regex-dev libboost-iostreams-dev zlib1g-dev liblz4-dev libdouble-conversion-dev libzstd-dev libre2-dev librdkafka-dev libcapnp-dev libpoco-dev libgoogle-perftools-dev libunwind-dev googletest libcctz-dev # install testing only stuff if you want: sudo apt install -y expect python python-lxml python-termcolor python-requests curl perl sudo openssl netcat-openbsd telnet diff --git a/utils/build/build_debian_unbundled.sh b/utils/build/build_debian_unbundled.sh index a75239321be..f5f59ce7a13 100755 --- a/utils/build/build_debian_unbundled.sh +++ b/utils/build/build_debian_unbundled.sh @@ -22,5 +22,5 @@ env TEST_RUN=1 \ `# Use all possible contrib libs from system` \ `# psmisc - killall` \ `# gdb - symbol test in pbuilder` \ - EXTRAPACKAGES="psmisc libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-regex-dev zlib1g-dev liblz4-dev libdouble-conversion-dev libsparsehash-dev librdkafka-dev libpoco-dev unixodbc-dev libsparsehash-dev libgoogle-perftools-dev libzstd-dev libre2-dev libunwind-dev googletest libcctz-dev libcapnp-dev libjemalloc-dev libssl-dev libunwind-dev libgsasl7-dev libxml2-dev libbrotli-dev libhyperscan-dev rapidjson-dev $EXTRAPACKAGES" \ + EXTRAPACKAGES="psmisc libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-regex-dev libboost-iostreams-dev zlib1g-dev liblz4-dev libdouble-conversion-dev libsparsehash-dev librdkafka-dev libpoco-dev unixodbc-dev libsparsehash-dev libgoogle-perftools-dev libzstd-dev libre2-dev libunwind-dev googletest libcctz-dev libcapnp-dev libjemalloc-dev libssl-dev libunwind-dev libgsasl7-dev libxml2-dev libbrotli-dev libhyperscan-dev rapidjson-dev $EXTRAPACKAGES" \ pdebuild --configfile $ROOT_DIR/debian/.pbuilderrc $PDEBUILD_OPT diff --git a/utils/build/build_no_submodules.sh b/utils/build/build_no_submodules.sh index fae10ab3270..4bcbe0b2a17 100755 --- a/utils/build/build_no_submodules.sh +++ b/utils/build/build_no_submodules.sh @@ -18,5 +18,5 @@ unzip -ou ch.zip # TODO: USE_INTERNAL_DOUBLE_CONVERSION_LIBRARY : cmake test # Shared because /usr/bin/ld.gold: error: /usr/lib/x86_64-linux-gnu/libcrypto.a(err.o): multiple definition of 'ERR_remove_thread_state' CMAKE_FLAGS+="-DUSE_STATIC_LIBRARIES=0 -DUSE_INTERNAL_DOUBLE_CONVERSION_LIBRARY=0 $CMAKE_FLAGS" -EXTRAPACKAGES+="libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-regex-dev liblz4-dev libzstd-dev libpoco-dev libdouble-conversion-dev libcctz-dev libre2-dev libsparsehash-dev $EXTRAPACKAGES" +EXTRAPACKAGES+="libboost-program-options-dev libboost-system-dev libboost-filesystem-dev libboost-thread-dev libboost-iostreams-dev libboost-regex-dev liblz4-dev libzstd-dev libpoco-dev libdouble-conversion-dev libcctz-dev libre2-dev libsparsehash-dev $EXTRAPACKAGES" . $ROOT_DIR/ClickHouse-${BRANCH}/release diff --git a/utils/check-marks/CMakeLists.txt b/utils/check-marks/CMakeLists.txt index bfb200b8d28..2fc22a925b1 100644 --- a/utils/check-marks/CMakeLists.txt +++ b/utils/check-marks/CMakeLists.txt @@ -1,2 +1,2 @@ add_executable (check-marks main.cpp) -target_link_libraries(check-marks PRIVATE dbms ${Boost_PROGRAM_OPTIONS_LIBRARY}) +target_link_libraries(check-marks PRIVATE dbms boost::program_options) diff --git a/utils/ci/jobs/quick-build/run.sh b/utils/ci/jobs/quick-build/run.sh index 56f0950c717..10da06f7414 100755 --- a/utils/ci/jobs/quick-build/run.sh +++ b/utils/ci/jobs/quick-build/run.sh @@ -21,7 +21,7 @@ BUILD_TARGETS=clickhouse BUILD_TYPE=Debug ENABLE_EMBEDDED_COMPILER=0 -CMAKE_FLAGS="-D CMAKE_C_FLAGS_ADD=-g0 -D CMAKE_CXX_FLAGS_ADD=-g0 -D ENABLE_JEMALLOC=0 -D ENABLE_CAPNP=0 -D ENABLE_RDKAFKA=0 -D ENABLE_UNWIND=0 -D ENABLE_ICU=0 -D ENABLE_POCO_MONGODB=0 -D ENABLE_POCO_REDIS=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_ODBC=0 -D ENABLE_MYSQL=0 -D ENABLE_SSL=0 -D ENABLE_POCO_NETSSL=0" +CMAKE_FLAGS="-D CMAKE_C_FLAGS_ADD=-g0 -D CMAKE_CXX_FLAGS_ADD=-g0 -D ENABLE_JEMALLOC=0 -D ENABLE_CAPNP=0 -D ENABLE_RDKAFKA=0 -D ENABLE_UNWIND=0 -D ENABLE_ICU=0 -D ENABLE_POCO_MONGODB=0 -D ENABLE_POCO_REDIS=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_ODBC=0 -D ENABLE_MYSQL=0 -D ENABLE_SSL=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_CASSANDRA=0" [[ $(uname) == "FreeBSD" ]] && COMPILER_PACKAGE_VERSION=devel && export COMPILER_PATH=/usr/local/bin diff --git a/utils/compressor/CMakeLists.txt b/utils/compressor/CMakeLists.txt index df32330a137..43cde973846 100644 --- a/utils/compressor/CMakeLists.txt +++ b/utils/compressor/CMakeLists.txt @@ -1,2 +1,2 @@ add_executable (decompress_perf decompress_perf.cpp) -target_link_libraries(decompress_perf PRIVATE dbms ${LZ4_LIBRARY}) +target_link_libraries(decompress_perf PRIVATE dbms lz4) diff --git a/utils/convert-month-partitioned-parts/CMakeLists.txt b/utils/convert-month-partitioned-parts/CMakeLists.txt index abfd60a07a0..14853590c76 100644 --- a/utils/convert-month-partitioned-parts/CMakeLists.txt +++ b/utils/convert-month-partitioned-parts/CMakeLists.txt @@ -1,2 +1,2 @@ add_executable (convert-month-partitioned-parts main.cpp) -target_link_libraries(convert-month-partitioned-parts PRIVATE dbms clickhouse_parsers ${Boost_PROGRAM_OPTIONS_LIBRARY}) +target_link_libraries(convert-month-partitioned-parts PRIVATE dbms clickhouse_parsers boost::program_options) diff --git a/utils/test-data-generator/CMakeLists.txt b/utils/test-data-generator/CMakeLists.txt index 758c3cdc0ce..20c37854c0a 100644 --- a/utils/test-data-generator/CMakeLists.txt +++ b/utils/test-data-generator/CMakeLists.txt @@ -6,7 +6,7 @@ if (USE_PROTOBUF) protobuf_generate_cpp(ProtobufDelimitedMessagesSerializer_Srcs2 ProtobufDelimitedMessagesSerializer_Hdrs2 ${CMAKE_CURRENT_SOURCE_DIR}/../../tests/queries/0_stateless/00825_protobuf_format_syntax2.proto) add_executable (ProtobufDelimitedMessagesSerializer ProtobufDelimitedMessagesSerializer.cpp ${ProtobufDelimitedMessagesSerializer_Srcs} ${ProtobufDelimitedMessagesSerializer_Hdrs} ${ProtobufDelimitedMessagesSerializer_Srcs2} ${ProtobufDelimitedMessagesSerializer_Hdrs2}) target_include_directories (ProtobufDelimitedMessagesSerializer SYSTEM BEFORE PRIVATE ${Protobuf_INCLUDE_DIR} ${CMAKE_CURRENT_BINARY_DIR}) - target_link_libraries (ProtobufDelimitedMessagesSerializer PRIVATE ${Protobuf_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY}) + target_link_libraries (ProtobufDelimitedMessagesSerializer PRIVATE ${Protobuf_LIBRARY} boost::program_options) get_filename_component(ProtobufDelimitedMessagesSerializer_OutputDir "${CMAKE_CURRENT_LIST_DIR}/../../tests/queries/0_stateless" REALPATH) target_compile_definitions(ProtobufDelimitedMessagesSerializer PRIVATE OUTPUT_DIR="${ProtobufDelimitedMessagesSerializer_OutputDir}") endif () diff --git a/utils/wikistat-loader/CMakeLists.txt b/utils/wikistat-loader/CMakeLists.txt index 7f72cbb9f46..96567e73790 100644 --- a/utils/wikistat-loader/CMakeLists.txt +++ b/utils/wikistat-loader/CMakeLists.txt @@ -1,2 +1,2 @@ add_executable (wikistat-loader main.cpp ${SRCS}) -target_link_libraries (wikistat-loader PRIVATE clickhouse_common_io ${Boost_PROGRAM_OPTIONS_LIBRARY}) +target_link_libraries (wikistat-loader PRIVATE clickhouse_common_io boost::program_options) diff --git a/utils/zookeeper-adjust-block-numbers-to-parts/CMakeLists.txt b/utils/zookeeper-adjust-block-numbers-to-parts/CMakeLists.txt index 2fdd87a4412..08907e1c5b9 100644 --- a/utils/zookeeper-adjust-block-numbers-to-parts/CMakeLists.txt +++ b/utils/zookeeper-adjust-block-numbers-to-parts/CMakeLists.txt @@ -1,3 +1,3 @@ add_executable (zookeeper-adjust-block-numbers-to-parts main.cpp ${SRCS}) target_compile_options(zookeeper-adjust-block-numbers-to-parts PRIVATE -Wno-format) -target_link_libraries (zookeeper-adjust-block-numbers-to-parts PRIVATE dbms clickhouse_common_zookeeper ${Boost_PROGRAM_OPTIONS_LIBRARY}) +target_link_libraries (zookeeper-adjust-block-numbers-to-parts PRIVATE dbms clickhouse_common_zookeeper boost::program_options) diff --git a/utils/zookeeper-cli/zookeeper-cli.cpp b/utils/zookeeper-cli/zookeeper-cli.cpp index 6fd7b39ab68..17a8c9f0da8 100644 --- a/utils/zookeeper-cli/zookeeper-cli.cpp +++ b/utils/zookeeper-cli/zookeeper-cli.cpp @@ -70,7 +70,7 @@ int main(int argc, char ** argv) Poco::Logger::root().setLevel("trace"); zkutil::ZooKeeper zk(argv[1]); - LineReader lr({}, '\\'); + LineReader lr({}, false, {"\\"}, {}); do { diff --git a/utils/zookeeper-create-entry-to-download-part/CMakeLists.txt b/utils/zookeeper-create-entry-to-download-part/CMakeLists.txt index 34f2e608ef9..7fe7fb94fa4 100644 --- a/utils/zookeeper-create-entry-to-download-part/CMakeLists.txt +++ b/utils/zookeeper-create-entry-to-download-part/CMakeLists.txt @@ -1,2 +1,2 @@ add_executable (zookeeper-create-entry-to-download-part main.cpp ${SRCS}) -target_link_libraries (zookeeper-create-entry-to-download-part PRIVATE dbms clickhouse_common_zookeeper ${Boost_PROGRAM_OPTIONS_LIBRARY}) +target_link_libraries (zookeeper-create-entry-to-download-part PRIVATE dbms clickhouse_common_zookeeper boost::program_options) diff --git a/utils/zookeeper-dump-tree/CMakeLists.txt b/utils/zookeeper-dump-tree/CMakeLists.txt index d2947fa8932..9f5da351068 100644 --- a/utils/zookeeper-dump-tree/CMakeLists.txt +++ b/utils/zookeeper-dump-tree/CMakeLists.txt @@ -1,2 +1,2 @@ add_executable (zookeeper-dump-tree main.cpp ${SRCS}) -target_link_libraries(zookeeper-dump-tree PRIVATE clickhouse_common_zookeeper clickhouse_common_io ${Boost_PROGRAM_OPTIONS_LIBRARY}) +target_link_libraries(zookeeper-dump-tree PRIVATE clickhouse_common_zookeeper clickhouse_common_io boost::program_options) diff --git a/utils/zookeeper-dump-tree/main.cpp b/utils/zookeeper-dump-tree/main.cpp index d848ffdad3c..877a4a996b7 100644 --- a/utils/zookeeper-dump-tree/main.cpp +++ b/utils/zookeeper-dump-tree/main.cpp @@ -45,7 +45,7 @@ try } catch (const Coordination::Exception & e) { - if (e.code == Coordination::ZNONODE) + if (e.code == Coordination::Error::ZNONODE) continue; throw; } diff --git a/utils/zookeeper-remove-by-list/CMakeLists.txt b/utils/zookeeper-remove-by-list/CMakeLists.txt index ba112bab9cf..c31b1ec3388 100644 --- a/utils/zookeeper-remove-by-list/CMakeLists.txt +++ b/utils/zookeeper-remove-by-list/CMakeLists.txt @@ -1,2 +1,2 @@ add_executable (zookeeper-remove-by-list main.cpp ${SRCS}) -target_link_libraries(zookeeper-remove-by-list PRIVATE clickhouse_common_zookeeper ${Boost_PROGRAM_OPTIONS_LIBRARY}) +target_link_libraries(zookeeper-remove-by-list PRIVATE clickhouse_common_zookeeper boost::program_options) diff --git a/website/robots.txt b/website/robots.txt index fa3a68b6d69..2cecc12e311 100644 --- a/website/robots.txt +++ b/website/robots.txt @@ -1,14 +1,4 @@ User-agent: * -Disallow: /docs/en/single/ -Disallow: /docs/zh/single/ -Disallow: /docs/es/single/ -Disallow: /docs/fr/single/ -Disallow: /docs/ru/single/ -Disallow: /docs/ja/single/ -Disallow: /docs/fa/single/ -Disallow: /docs/v1* -Disallow: /docs/v2* -Disallow: /docs/v3* Disallow: /cdn-cgi/ Allow: / Host: https://clickhouse.tech diff --git a/website/templates/index/features.html b/website/templates/index/features.html index c659e0d9301..f266bbaabb7 100644 --- a/website/templates/index/features.html +++ b/website/templates/index/features.html @@ -14,6 +14,7 @@
  • Real-time query processing
  • Real-time data ingestion
  • On-disk locality of reference
  • +
  • Secondary data-skipping indexes
  • Data compression
  • @@ -24,12 +25,14 @@
  • High availability
  • Cross-datacenter replication
  • Local and distributed joins
  • +
  • Adaptive join algorithm
  • Pluggable external dimension tables
  • Arrays and nested data types
  • - {## TODO: ##} diff --git a/website/templates/index/quickstart.html b/website/templates/index/quickstart.html index 74449b4da3a..ca7544f3b51 100644 --- a/website/templates/index/quickstart.html +++ b/website/templates/index/quickstart.html @@ -44,7 +44,7 @@

       Tutorial    -   +    Documentation

    diff --git a/website/templates/index/rich.html b/website/templates/index/rich.html index 1f2b4957306..ec2620e0a86 100644 --- a/website/templates/index/rich.html +++ b/website/templates/index/rich.html @@ -3,14 +3,14 @@
    -

    Feature-rich

    +

    Feature-rich SQL database

    1

    User-friendly SQL dialect

    -

    ClickHouse features a user-friendly SQL query dialect with a number of built-in analytics capabilities. In addition to common functions that could be found in most DBMS, ClickHouse comes with a lot of domain-specific functions and features out of the box.

    +

    ClickHouse features a SQL query dialect with a number of built-in analytics capabilities. In addition to common functions that could be found in most DBMS, ClickHouse comes with a lot of domain-specific functions and features for OLAP scenarios out of the box.

    2
    @@ -23,13 +23,11 @@
    3

    Join distributed or co-located data

    ClickHouse provides various options for joining tables. Joins could be either cluster local, they can also access data stored in external systems. There's also an external dictionaries support that provides an alternative more simple syntax for accessing data from an outside source.

    -
    4

    Approximate query processing

    Users can control the trade-off between result accuracy and query execution time, which is handy when dealing with multiple terabytes or petabytes of data. ClickHouse also provides probabilistic data structures for fast and memory-efficient calculation of cardinalities and quantiles

    -
    diff --git a/website/templates/index/why.html b/website/templates/index/why.html index a2917258923..dc9071a2067 100644 --- a/website/templates/index/why.html +++ b/website/templates/index/why.html @@ -11,8 +11,7 @@

    Blazing fast

    -

    ClickHouse uses all available hardware to its full potential to process each query as fast as possible. Peak - processing performance for a single query stands at more than 2 terabytes per second (after decompression, only used columns). In distributed setup reads are automatically balanced among healthy replicas to avoid increasing latency.

    +

    ClickHouse uses all available hardware to its full potential to process each query as fast as possible. Peak processing performance for a single query stands at more than 2 terabytes per second (after decompression, only used columns). In distributed setup reads are automatically balanced among healthy replicas to avoid increasing latency.

    Fault tolerant @@ -28,7 +27,7 @@

    Easy to use

    -

    ClickHouse is simple and works out-of-the-box. It streamlines all your data processing: ingest all your structured data into the system and it becomes instantly available for building reports. SQL dialect allows expressing the desired result without involving any custom non-standard API that could be found in some DBMS.

    +

    ClickHouse is simple and works out-of-the-box. It streamlines all your data processing: ingest all your structured data into the system and it becomes instantly available for building reports. SQL dialect allows expressing the desired result without involving any custom non-standard API that could be found in some alternative systems.

    Highly reliable