Merge branch 'master' of github.com:clickhouse/ClickHouse

This commit is contained in:
Ivan Blinkov 2020-01-20 15:05:58 +03:00
commit 8146e5a1c4
108 changed files with 2111 additions and 1049 deletions

3
.gitmodules vendored
View File

@ -134,6 +134,9 @@
[submodule "contrib/libc-headers"] [submodule "contrib/libc-headers"]
path = contrib/libc-headers path = contrib/libc-headers
url = https://github.com/ClickHouse-Extras/libc-headers.git url = https://github.com/ClickHouse-Extras/libc-headers.git
[submodule "contrib/replxx"]
path = contrib/replxx
url = https://github.com/AmokHuginnsson/replxx.git
[submodule "contrib/ryu"] [submodule "contrib/ryu"]
path = contrib/ryu path = contrib/ryu
url = https://github.com/ClickHouse-Extras/ryu.git url = https://github.com/ClickHouse-Extras/ryu.git

View File

@ -328,7 +328,6 @@ include (cmake/find/xxhash.cmake)
include (cmake/find/sparsehash.cmake) include (cmake/find/sparsehash.cmake)
include (cmake/find/rt.cmake) include (cmake/find/rt.cmake)
include (cmake/find/execinfo.cmake) include (cmake/find/execinfo.cmake)
include (cmake/find/readline_edit.cmake)
include (cmake/find/re2.cmake) include (cmake/find/re2.cmake)
include (cmake/find/libgsasl.cmake) include (cmake/find/libgsasl.cmake)
include (cmake/find/rdkafka.cmake) include (cmake/find/rdkafka.cmake)
@ -353,6 +352,7 @@ include (cmake/find/simdjson.cmake)
include (cmake/find/rapidjson.cmake) include (cmake/find/rapidjson.cmake)
include (cmake/find/fastops.cmake) include (cmake/find/fastops.cmake)
include (cmake/find/orc.cmake) include (cmake/find/orc.cmake)
include (cmake/find/replxx.cmake)
find_contrib_lib(cityhash) find_contrib_lib(cityhash)
find_contrib_lib(farmhash) find_contrib_lib(farmhash)

View File

@ -11,7 +11,6 @@ if (ENABLE_BASE64)
if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/base64") if (NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/base64")
message (WARNING "submodule contrib/base64 is missing. to fix try run: \n git submodule update --init --recursive") message (WARNING "submodule contrib/base64 is missing. to fix try run: \n git submodule update --init --recursive")
else() else()
set (BASE64_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/base64/include)
set (BASE64_LIBRARY base64) set (BASE64_LIBRARY base64)
set (USE_BASE64 1) set (USE_BASE64 1)
endif() endif()

View File

@ -1,60 +0,0 @@
include (CMakePushCheckState)
cmake_push_check_state ()
option (ENABLE_READLINE "Enable readline" ${ENABLE_LIBRARIES})
if (ENABLE_READLINE)
set (READLINE_PATHS "/usr/local/opt/readline/lib")
# First try find custom lib for macos users (default lib without history support)
find_library (READLINE_LIB NAMES readline PATHS ${READLINE_PATHS} NO_DEFAULT_PATH)
if (NOT READLINE_LIB)
find_library (READLINE_LIB NAMES readline PATHS ${READLINE_PATHS})
endif ()
list(APPEND CMAKE_FIND_LIBRARY_SUFFIXES .so.2)
find_library (EDIT_LIB NAMES edit)
set(READLINE_INCLUDE_PATHS "/usr/local/opt/readline/include")
if (READLINE_LIB AND TERMCAP_LIBRARY)
find_path (READLINE_INCLUDE_DIR NAMES readline/readline.h PATHS ${READLINE_INCLUDE_PATHS} NO_DEFAULT_PATH)
if (NOT READLINE_INCLUDE_DIR)
find_path (READLINE_INCLUDE_DIR NAMES readline/readline.h PATHS ${READLINE_INCLUDE_PATHS})
endif ()
if (READLINE_INCLUDE_DIR AND READLINE_LIB)
set (USE_READLINE 1)
set (LINE_EDITING_LIBS ${READLINE_LIB} ${TERMCAP_LIBRARY})
message (STATUS "Using line editing libraries (readline): ${READLINE_INCLUDE_DIR} : ${LINE_EDITING_LIBS}")
endif ()
elseif (EDIT_LIB AND TERMCAP_LIBRARY)
find_library (CURSES_LIB NAMES curses)
find_path (READLINE_INCLUDE_DIR NAMES editline/readline.h PATHS ${READLINE_INCLUDE_PATHS})
if (CURSES_LIB AND READLINE_INCLUDE_DIR)
set (USE_LIBEDIT 1)
set (LINE_EDITING_LIBS ${EDIT_LIB} ${CURSES_LIB} ${TERMCAP_LIBRARY})
message (STATUS "Using line editing libraries (edit): ${READLINE_INCLUDE_DIR} : ${LINE_EDITING_LIBS}")
endif ()
endif ()
endif ()
if (LINE_EDITING_LIBS AND READLINE_INCLUDE_DIR)
include (CheckCXXSourceRuns)
set (CMAKE_REQUIRED_LIBRARIES ${CMAKE_REQUIRED_LIBRARIES} ${LINE_EDITING_LIBS})
set (CMAKE_REQUIRED_INCLUDES ${CMAKE_REQUIRED_INCLUDES} ${READLINE_INCLUDE_DIR})
check_cxx_source_runs ("
#include <stdio.h>
#include <readline/readline.h>
#include <readline/history.h>
int main() {
add_history(NULL);
append_history(1,NULL);
return 0;
}
" HAVE_READLINE_HISTORY)
else ()
message (STATUS "Not using any library for line editing.")
endif ()
cmake_pop_check_state ()

40
cmake/find/replxx.cmake Normal file
View File

@ -0,0 +1,40 @@
option (ENABLE_REPLXX "Enable replxx support" ${NOT_UNBUNDLED})
if (ENABLE_REPLXX)
option (USE_INTERNAL_REPLXX "Use internal replxx library" ${NOT_UNBUNDLED})
if (USE_INTERNAL_REPLXX AND NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/replxx/README.md")
message (WARNING "submodule contrib/replxx is missing. to fix try run: \n git submodule update --init --recursive")
set (USE_INTERNAL_REPLXX 0)
endif ()
if (NOT USE_INTERNAL_REPLXX)
find_library(LIBRARY_REPLXX NAMES replxx replxx-static)
find_path(INCLUDE_REPLXX replxx.hxx)
add_library(replxx UNKNOWN IMPORTED)
set_property(TARGET replxx PROPERTY IMPORTED_LOCATION ${LIBRARY_REPLXX})
target_include_directories(replxx PUBLIC ${INCLUDE_REPLXX})
set(CMAKE_REQUIRED_LIBRARIES replxx)
check_cxx_source_compiles(
"
#include <replxx.hxx>
int main() {
replxx::Replxx rx;
}
"
EXTERNAL_REPLXX_WORKS
)
if (NOT EXTERNAL_REPLXX_WORKS)
message (FATAL_ERROR "replxx is unusable: ${LIBRARY_REPLXX} ${INCLUDE_REPLXX}")
endif ()
endif ()
set(USE_REPLXX 1)
message (STATUS "Using replxx")
else ()
set(USE_REPLXX 0)
endif ()

View File

@ -48,7 +48,6 @@ if (SANITIZE)
set (ENABLE_EMBEDDED_COMPILER 0 CACHE BOOL "") set (ENABLE_EMBEDDED_COMPILER 0 CACHE BOOL "")
set (USE_INTERNAL_CAPNP_LIBRARY 0 CACHE BOOL "") set (USE_INTERNAL_CAPNP_LIBRARY 0 CACHE BOOL "")
set (USE_SIMDJSON 0 CACHE BOOL "") set (USE_SIMDJSON 0 CACHE BOOL "")
set (ENABLE_READLINE 0 CACHE BOOL "")
set (ENABLE_ORC 0 CACHE BOOL "") set (ENABLE_ORC 0 CACHE BOOL "")
set (ENABLE_PARQUET 0 CACHE BOOL "") set (ENABLE_PARQUET 0 CACHE BOOL "")
set (USE_CAPNP 0 CACHE BOOL "") set (USE_CAPNP 0 CACHE BOOL "")

View File

@ -15,7 +15,6 @@ if (CMAKE_CROSSCOMPILING)
set (USE_SNAPPY OFF CACHE INTERNAL "") set (USE_SNAPPY OFF CACHE INTERNAL "")
set (ENABLE_PROTOBUF OFF CACHE INTERNAL "") set (ENABLE_PROTOBUF OFF CACHE INTERNAL "")
set (ENABLE_PARQUET OFF CACHE INTERNAL "") set (ENABLE_PARQUET OFF CACHE INTERNAL "")
set (ENABLE_READLINE OFF CACHE INTERNAL "")
set (ENABLE_ICU OFF CACHE INTERNAL "") set (ENABLE_ICU OFF CACHE INTERNAL "")
set (ENABLE_FASTOPS OFF CACHE INTERNAL "") set (ENABLE_FASTOPS OFF CACHE INTERNAL "")
elseif (OS_LINUX) elseif (OS_LINUX)

View File

@ -331,3 +331,7 @@ endif()
if (USE_FASTOPS) if (USE_FASTOPS)
add_subdirectory (fastops-cmake) add_subdirectory (fastops-cmake)
endif() endif()
if (USE_INTERNAL_REPLXX)
add_subdirectory (replxx-cmake)
endif()

View File

@ -74,7 +74,6 @@ file(GLOB S3_UNIFIED_SRC
) )
set(S3_INCLUDES set(S3_INCLUDES
"${CMAKE_CURRENT_SOURCE_DIR}/include/"
"${AWS_COMMON_LIBRARY_DIR}/include/" "${AWS_COMMON_LIBRARY_DIR}/include/"
"${AWS_EVENT_STREAM_LIBRARY_DIR}/include/" "${AWS_EVENT_STREAM_LIBRARY_DIR}/include/"
"${AWS_S3_LIBRARY_DIR}/include/" "${AWS_S3_LIBRARY_DIR}/include/"
@ -96,7 +95,7 @@ target_compile_definitions(aws_s3 PUBLIC -DENABLE_CURL_CLIENT)
target_compile_definitions(aws_s3 PUBLIC "AWS_SDK_VERSION_MAJOR=1") target_compile_definitions(aws_s3 PUBLIC "AWS_SDK_VERSION_MAJOR=1")
target_compile_definitions(aws_s3 PUBLIC "AWS_SDK_VERSION_MINOR=7") target_compile_definitions(aws_s3 PUBLIC "AWS_SDK_VERSION_MINOR=7")
target_compile_definitions(aws_s3 PUBLIC "AWS_SDK_VERSION_PATCH=231") target_compile_definitions(aws_s3 PUBLIC "AWS_SDK_VERSION_PATCH=231")
target_include_directories(aws_s3 PUBLIC ${S3_INCLUDES} "${CMAKE_BINARY_DIR}/install") target_include_directories(aws_s3 PUBLIC ${S3_INCLUDES})
if (OPENSSL_FOUND) if (OPENSSL_FOUND)
target_compile_definitions(aws_s3 PUBLIC -DENABLE_OPENSSL_ENCRYPTION) target_compile_definitions(aws_s3 PUBLIC -DENABLE_OPENSSL_ENCRYPTION)

1
contrib/replxx vendored Submodule

@ -0,0 +1 @@
Subproject commit 37582f0bb8c52513c6c6b76797c02d852d701dad

View File

@ -0,0 +1,18 @@
set (LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/replxx")
set(SRCS
${LIBRARY_DIR}/src/conversion.cxx
${LIBRARY_DIR}/src/escape.cxx
${LIBRARY_DIR}/src/history.cxx
${LIBRARY_DIR}/src/io.cxx
${LIBRARY_DIR}/src/prompt.cxx
${LIBRARY_DIR}/src/replxx.cxx
${LIBRARY_DIR}/src/replxx_impl.cxx
${LIBRARY_DIR}/src/util.cxx
${LIBRARY_DIR}/src/wcwidth.cpp
${LIBRARY_DIR}/src/ConvertUTF.cpp
)
add_library(replxx ${SRCS})
target_include_directories(replxx PUBLIC ${LIBRARY_DIR}/include)
target_compile_options(replxx PUBLIC -Wno-documentation)

View File

@ -142,10 +142,10 @@ elseif (COMPILER_GCC)
add_cxx_compile_options(-Wmaybe-uninitialized) add_cxx_compile_options(-Wmaybe-uninitialized)
# Warn when the indentation of the code does not reflect the block structure # Warn when the indentation of the code does not reflect the block structure
add_cxx_compile_options(-Wmisleading-indentation) add_cxx_compile_options(-Wmisleading-indentation)
# Warn if a global function is defined without a previous declaration # Warn if a global function is defined without a previous declaration - disabled because of build times
# add_cxx_compile_options(-Wmissing-declarations) # add_cxx_compile_options(-Wmissing-declarations)
# Warn if a user-supplied include directory does not exist # Warn if a user-supplied include directory does not exist
# add_cxx_compile_options(-Wmissing-include-dirs) add_cxx_compile_options(-Wmissing-include-dirs)
# Obvious # Obvious
add_cxx_compile_options(-Wnon-virtual-dtor) add_cxx_compile_options(-Wnon-virtual-dtor)
# Obvious # Obvious
@ -563,7 +563,7 @@ if (USE_JEMALLOC)
endif() endif()
endif () endif ()
dbms_target_include_directories (PUBLIC ${DBMS_INCLUDE_DIR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src/Formats/include) dbms_target_include_directories (PUBLIC ${DBMS_INCLUDE_DIR})
target_include_directories (clickhouse_common_io PUBLIC ${DBMS_INCLUDE_DIR}) target_include_directories (clickhouse_common_io PUBLIC ${DBMS_INCLUDE_DIR})
target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${DOUBLE_CONVERSION_INCLUDE_DIR}) target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${DOUBLE_CONVERSION_INCLUDE_DIR})

View File

@ -418,7 +418,7 @@ private:
std::cerr << percent << "%\t\t"; std::cerr << percent << "%\t\t";
for (const auto & info : infos) for (const auto & info : infos)
{ {
std::cerr << info->sampler.quantileInterpolated(percent / 100.0) << " sec." << "\t"; std::cerr << info->sampler.quantileNearest(percent / 100.0) << " sec." << "\t";
} }
std::cerr << "\n"; std::cerr << "\n";
}; };
@ -453,7 +453,7 @@ private:
auto print_percentile = [&json_out](Stats & info, auto percent, bool with_comma = true) auto print_percentile = [&json_out](Stats & info, auto percent, bool with_comma = true)
{ {
json_out << "\"" << percent << "\"" << ": " << info.sampler.quantileInterpolated(percent / 100.0) << (with_comma ? ",\n" : "\n"); json_out << "\"" << percent << "\"" << ": " << info.sampler.quantileNearest(percent / 100.0) << (with_comma ? ",\n" : "\n");
}; };
json_out << "{\n"; json_out << "{\n";

View File

@ -1,14 +1,10 @@
set(CLICKHOUSE_CLIENT_SOURCES set(CLICKHOUSE_CLIENT_SOURCES
${CMAKE_CURRENT_SOURCE_DIR}/Client.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Client.cpp
${CMAKE_CURRENT_SOURCE_DIR}/ConnectionParameters.cpp ${CMAKE_CURRENT_SOURCE_DIR}/ConnectionParameters.cpp
${CMAKE_CURRENT_SOURCE_DIR}/Suggest.cpp
) )
set(CLICKHOUSE_CLIENT_LINK PRIVATE clickhouse_common_config clickhouse_functions clickhouse_aggregate_functions clickhouse_common_io clickhouse_parsers string_utils ${LINE_EDITING_LIBS} ${Boost_PROGRAM_OPTIONS_LIBRARY}) set(CLICKHOUSE_CLIENT_LINK PRIVATE clickhouse_common_config clickhouse_functions clickhouse_aggregate_functions clickhouse_common_io clickhouse_parsers string_utils ${LINE_EDITING_LIBS} ${Boost_PROGRAM_OPTIONS_LIBRARY})
set(CLICKHOUSE_CLIENT_INCLUDE PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include)
if (READLINE_INCLUDE_DIR)
set(CLICKHOUSE_CLIENT_INCLUDE ${CLICKHOUSE_CLIENT_INCLUDE} SYSTEM PRIVATE ${READLINE_INCLUDE_DIR})
endif ()
include(CheckSymbolExists) include(CheckSymbolExists)
check_symbol_exists(readpassphrase readpassphrase.h HAVE_READPASSPHRASE) check_symbol_exists(readpassphrase readpassphrase.h HAVE_READPASSPHRASE)

View File

@ -1,7 +1,7 @@
#include "TestHint.h" #include "TestHint.h"
#include "ConnectionParameters.h" #include "ConnectionParameters.h"
#include "Suggest.h"
#include <port/unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h> #include <fcntl.h>
#include <signal.h> #include <signal.h>
@ -18,8 +18,9 @@
#include <Poco/String.h> #include <Poco/String.h>
#include <Poco/File.h> #include <Poco/File.h>
#include <Poco/Util/Application.h> #include <Poco/Util/Application.h>
#include <common/readline_use.h>
#include <common/find_symbols.h> #include <common/find_symbols.h>
#include <common/config_common.h>
#include <common/LineReader.h>
#include <Common/ClickHouseRevision.h> #include <Common/ClickHouseRevision.h>
#include <Common/Stopwatch.h> #include <Common/Stopwatch.h>
#include <Common/Exception.h> #include <Common/Exception.h>
@ -69,10 +70,6 @@
#include <common/argsToConfig.h> #include <common/argsToConfig.h>
#include <Common/TerminalSize.h> #include <Common/TerminalSize.h>
#if USE_READLINE
#include "Suggest.h"
#endif
#ifndef __clang__ #ifndef __clang__
#pragma GCC optimize("-fno-var-tracking-assignments") #pragma GCC optimize("-fno-var-tracking-assignments")
#endif #endif
@ -89,39 +86,6 @@
#define DISABLE_LINE_WRAPPING "\033[?7l" #define DISABLE_LINE_WRAPPING "\033[?7l"
#define ENABLE_LINE_WRAPPING "\033[?7h" #define ENABLE_LINE_WRAPPING "\033[?7h"
#if USE_READLINE && RL_VERSION_MAJOR >= 7
#define BRACK_PASTE_PREF "\033[200~"
#define BRACK_PASTE_SUFF "\033[201~"
#define BRACK_PASTE_LAST '~'
#define BRACK_PASTE_SLEN 6
/// This handler bypasses some unused macro/event checkings.
static int clickhouse_rl_bracketed_paste_begin(int /* count */, int /* key */)
{
std::string buf;
buf.reserve(128);
RL_SETSTATE(RL_STATE_MOREINPUT);
SCOPE_EXIT(RL_UNSETSTATE(RL_STATE_MOREINPUT));
int c;
while ((c = rl_read_key()) >= 0)
{
if (c == '\r')
c = '\n';
buf.push_back(c);
if (buf.size() >= BRACK_PASTE_SLEN && c == BRACK_PASTE_LAST && buf.substr(buf.size() - BRACK_PASTE_SLEN) == BRACK_PASTE_SUFF)
{
buf.resize(buf.size() - BRACK_PASTE_SLEN);
break;
}
}
return static_cast<size_t>(rl_insert_text(buf.c_str())) == buf.size() ? 0 : 1;
}
#endif
namespace DB namespace DB
{ {
@ -136,7 +100,6 @@ namespace ErrorCodes
extern const int UNEXPECTED_PACKET_FROM_SERVER; extern const int UNEXPECTED_PACKET_FROM_SERVER;
extern const int CLIENT_OUTPUT_FORMAT_SPECIFIED; extern const int CLIENT_OUTPUT_FORMAT_SPECIFIED;
extern const int CANNOT_SET_SIGNAL_HANDLER; extern const int CANNOT_SET_SIGNAL_HANDLER;
extern const int CANNOT_READLINE;
extern const int SYSTEM_ERROR; extern const int SYSTEM_ERROR;
extern const int INVALID_USAGE_OF_INPUT; extern const int INVALID_USAGE_OF_INPUT;
} }
@ -157,7 +120,7 @@ private:
"учшеж", "йгшеж", "дщпщгеж", "учшеж", "йгшеж", "дщпщгеж",
"q", "й", "\\q", "\\Q", "\\й", "\\Й", ":q", "Жй" "q", "й", "\\q", "\\Q", "\\й", "\\Й", ":q", "Жй"
}; };
bool is_interactive = true; /// Use either readline interface or batch mode. bool is_interactive = true; /// Use either interactive line editing interface or batch mode.
bool need_render_progress = true; /// Render query execution progress. bool need_render_progress = true; /// Render query execution progress.
bool echo_queries = false; /// Print queries before execution in batch mode. bool echo_queries = false; /// Print queries before execution in batch mode.
bool ignore_error = false; /// In case of errors, don't print error message, continue to next query. Only applicable for non-interactive mode. bool ignore_error = false; /// In case of errors, don't print error message, continue to next query. Only applicable for non-interactive mode.
@ -514,26 +477,10 @@ private:
if (print_time_to_stderr) if (print_time_to_stderr)
throw Exception("time option could be specified only in non-interactive mode", ErrorCodes::BAD_ARGUMENTS); throw Exception("time option could be specified only in non-interactive mode", ErrorCodes::BAD_ARGUMENTS);
#if USE_READLINE if (server_revision >= Suggest::MIN_SERVER_REVISION && !config().getBool("disable_suggestion", false))
SCOPE_EXIT({ Suggest::instance().finalize(); });
if (server_revision >= Suggest::MIN_SERVER_REVISION
&& !config().getBool("disable_suggestion", false))
{
/// Load suggestion data from the server. /// Load suggestion data from the server.
Suggest::instance().load(connection_parameters, config().getInt("suggestion_limit")); Suggest::instance().load(connection_parameters, config().getInt("suggestion_limit"));
/// Added '.' to the default list. Because it is used to separate database and table.
rl_basic_word_break_characters = " \t\n\r\"\\'`@$><=;|&{(.";
/// Not append whitespace after single suggestion. Because whitespace after function name is meaningless.
rl_completion_append_character = '\0';
rl_completion_entry_function = Suggest::generator;
}
else
/// Turn tab completion off.
rl_bind_key('\t', rl_insert);
#endif
/// Load command history if present. /// Load command history if present.
if (config().has("history_file")) if (config().has("history_file"))
history_file = config().getString("history_file"); history_file = config().getString("history_file");
@ -546,70 +493,45 @@ private:
history_file = home_path + "/.clickhouse-client-history"; history_file = home_path + "/.clickhouse-client-history";
} }
if (!history_file.empty()) if (!history_file.empty() && !Poco::File(history_file).exists())
{
if (Poco::File(history_file).exists())
{
#if USE_READLINE
int res = read_history(history_file.c_str());
if (res)
std::cerr << "Cannot read history from file " + history_file + ": "+ errnoToString(ErrorCodes::CANNOT_READ_HISTORY);
#endif
}
else /// Create history file.
Poco::File(history_file).createFile(); Poco::File(history_file).createFile();
LineReader lr(&Suggest::instance(), history_file, '\\', config().has("multiline") ? ';' : 0);
do
{
auto input = lr.readLine(prompt(), ":-] ");
if (input.empty())
break;
try
{
if (!process(input))
break;
}
catch (const Exception & e)
{
actual_client_error = e.code();
if (!actual_client_error || actual_client_error != expected_client_error)
{
std::cerr << std::endl
<< "Exception on client:" << std::endl
<< "Code: " << e.code() << ". " << e.displayText() << std::endl;
if (config().getBool("stacktrace", false))
std::cerr << "Stack trace:" << std::endl << e.getStackTraceString() << std::endl;
std::cerr << std::endl;
} }
#if USE_READLINE /// Client-side exception during query execution can result in the loss of
/// Install Ctrl+C signal handler that will be used in interactive mode. /// sync in the connection protocol.
/// So we reconnect and allow to enter the next query.
if (rl_initialize()) connect();
throw Exception("Cannot initialize readline", ErrorCodes::CANNOT_READLINE);
#if RL_VERSION_MAJOR >= 7
/// Enable bracketed-paste-mode only when multiquery is enabled and multiline is
/// disabled, so that we are able to paste and execute multiline queries in a whole
/// instead of erroring out, while be less intrusive.
if (config().has("multiquery") && !config().has("multiline"))
{
/// When bracketed paste mode is set, pasted text is bracketed with control sequences so
/// that the program can differentiate pasted text from typed-in text. This helps
/// clickhouse-client so that without -m flag, one can still paste multiline queries, and
/// possibly get better pasting performance. See https://cirw.in/blog/bracketed-paste for
/// more details.
rl_variable_bind("enable-bracketed-paste", "on");
/// Use our bracketed paste handler to get better user experience. See comments above.
rl_bind_keyseq(BRACK_PASTE_PREF, clickhouse_rl_bracketed_paste_begin);
} }
#endif
auto clear_prompt_or_exit = [](int)
{
/// This is signal safe.
ssize_t res = write(STDOUT_FILENO, "\n", 1);
/// Allow to quit client while query is in progress by pressing Ctrl+C twice.
/// (First press to Ctrl+C will try to cancel query by InterruptListener).
if (res == 1 && rl_line_buffer[0] && !RL_ISSTATE(RL_STATE_DONE))
{
rl_replace_line("", 0);
if (rl_forced_update_display())
_exit(0);
} }
else while (true);
{
/// A little dirty, but we struggle to find better way to correctly
/// force readline to exit after returning from the signal handler.
_exit(0);
}
};
if (signal(SIGINT, clear_prompt_or_exit) == SIG_ERR)
throwFromErrno("Cannot set signal handler.", ErrorCodes::CANNOT_SET_SIGNAL_HANDLER);
#endif
loop();
if (isNewYearMode()) if (isNewYearMode())
std::cout << "Happy new year." << std::endl; std::cout << "Happy new year." << std::endl;
@ -621,17 +543,6 @@ private:
} }
else else
{ {
/// This is intended for testing purposes.
if (config().getBool("always_load_suggestion_data", false))
{
#if USE_READLINE
SCOPE_EXIT({ Suggest::instance().finalize(); });
Suggest::instance().load(connection_parameters, config().getInt("suggestion_limit"));
#else
throw Exception("Command line suggestions cannot work without readline", ErrorCodes::BAD_ARGUMENTS);
#endif
}
query_id = config().getString("query_id", ""); query_id = config().getString("query_id", "");
nonInteractive(); nonInteractive();
@ -706,111 +617,11 @@ private:
} }
/// Check if multi-line query is inserted from the paste buffer.
/// Allows delaying the start of query execution until the entirety of query is inserted.
static bool hasDataInSTDIN()
{
timeval timeout = { 0, 0 };
fd_set fds;
FD_ZERO(&fds);
FD_SET(STDIN_FILENO, &fds);
return select(1, &fds, nullptr, nullptr, &timeout) == 1;
}
inline const String prompt() const inline const String prompt() const
{ {
return boost::replace_all_copy(prompt_by_server_display_name, "{database}", config().getString("database", "default")); return boost::replace_all_copy(prompt_by_server_display_name, "{database}", config().getString("database", "default"));
} }
void loop()
{
String input;
String prev_input;
while (char * line_ = readline(input.empty() ? prompt().c_str() : ":-] "))
{
String line = line_;
free(line_);
size_t ws = line.size();
while (ws > 0 && isWhitespaceASCII(line[ws - 1]))
--ws;
if (ws == 0 || line.empty())
continue;
bool ends_with_semicolon = line[ws - 1] == ';';
bool ends_with_backslash = line[ws - 1] == '\\';
has_vertical_output_suffix = (ws >= 2) && (line[ws - 2] == '\\') && (line[ws - 1] == 'G');
if (ends_with_backslash)
line = line.substr(0, ws - 1);
input += line;
if (!ends_with_backslash && (ends_with_semicolon || has_vertical_output_suffix || (!config().has("multiline") && !hasDataInSTDIN())))
{
// TODO: should we do sensitive data masking on client too? History file can be source of secret leaks.
if (input != prev_input)
{
/// Replace line breaks with spaces to prevent the following problem.
/// Every line of multi-line query is saved to history file as a separate line.
/// If the user restarts the client then after pressing the "up" button
/// every line of the query will be displayed separately.
std::string logged_query = input;
if (config().has("multiline"))
std::replace(logged_query.begin(), logged_query.end(), '\n', ' ');
add_history(logged_query.c_str());
#if USE_READLINE && HAVE_READLINE_HISTORY
if (!history_file.empty() && append_history(1, history_file.c_str()))
std::cerr << "Cannot append history to file " + history_file + ": " + errnoToString(ErrorCodes::CANNOT_APPEND_HISTORY);
#endif
prev_input = input;
}
if (has_vertical_output_suffix)
input = input.substr(0, input.length() - 2);
try
{
if (!process(input))
break;
}
catch (const Exception & e)
{
actual_client_error = e.code();
if (!actual_client_error || actual_client_error != expected_client_error)
{
std::cerr << std::endl
<< "Exception on client:" << std::endl
<< "Code: " << e.code() << ". " << e.displayText() << std::endl;
if (config().getBool("stacktrace", false))
std::cerr << "Stack trace:" << std::endl
<< e.getStackTraceString() << std::endl;
std::cerr << std::endl;
}
/// Client-side exception during query execution can result in the loss of
/// sync in the connection protocol.
/// So we reconnect and allow to enter the next query.
connect();
}
input = "";
}
else
{
input += '\n';
}
}
}
void nonInteractive() void nonInteractive()
{ {
@ -2001,13 +1812,6 @@ public:
server_logs_file = options["server_logs_file"].as<std::string>(); server_logs_file = options["server_logs_file"].as<std::string>();
if (options.count("disable_suggestion")) if (options.count("disable_suggestion"))
config().setBool("disable_suggestion", true); config().setBool("disable_suggestion", true);
if (options.count("always_load_suggestion_data"))
{
if (options.count("disable_suggestion"))
throw Exception("Command line parameters disable_suggestion (-A) and always_load_suggestion_data cannot be specified simultaneously",
ErrorCodes::BAD_ARGUMENTS);
config().setBool("always_load_suggestion_data", true);
}
if (options.count("suggestion_limit")) if (options.count("suggestion_limit"))
config().setInt("suggestion_limit", options["suggestion_limit"].as<int>()); config().setInt("suggestion_limit", options["suggestion_limit"].as<int>());

View File

@ -0,0 +1,144 @@
#include "Suggest.h"
#include <Columns/ColumnString.h>
#include <Common/typeid_cast.h>
namespace DB
{
void Suggest::load(const ConnectionParameters & connection_parameters, size_t suggestion_limit)
{
loading_thread = std::thread([connection_parameters, suggestion_limit, this]
{
try
{
Connection connection(
connection_parameters.host,
connection_parameters.port,
connection_parameters.default_database,
connection_parameters.user,
connection_parameters.password,
"client",
connection_parameters.compression,
connection_parameters.security);
loadImpl(connection, connection_parameters.timeouts, suggestion_limit);
}
catch (...)
{
std::cerr << "Cannot load data for command line suggestions: " << getCurrentExceptionMessage(false, true) << "\n";
}
/// Note that keyword suggestions are available even if we cannot load data from server.
std::sort(words.begin(), words.end());
ready = true;
});
}
Suggest::Suggest()
{
/// Keywords may be not up to date with ClickHouse parser.
words = {"CREATE", "DATABASE", "IF", "NOT", "EXISTS", "TEMPORARY", "TABLE", "ON", "CLUSTER", "DEFAULT",
"MATERIALIZED", "ALIAS", "ENGINE", "AS", "VIEW", "POPULATE", "SETTINGS", "ATTACH", "DETACH", "DROP",
"RENAME", "TO", "ALTER", "ADD", "MODIFY", "CLEAR", "COLUMN", "AFTER", "COPY", "PROJECT",
"PRIMARY", "KEY", "CHECK", "PARTITION", "PART", "FREEZE", "FETCH", "FROM", "SHOW", "INTO",
"OUTFILE", "FORMAT", "TABLES", "DATABASES", "LIKE", "PROCESSLIST", "CASE", "WHEN", "THEN", "ELSE",
"END", "DESCRIBE", "DESC", "USE", "SET", "OPTIMIZE", "FINAL", "DEDUPLICATE", "INSERT", "VALUES",
"SELECT", "DISTINCT", "SAMPLE", "ARRAY", "JOIN", "GLOBAL", "LOCAL", "ANY", "ALL", "INNER",
"LEFT", "RIGHT", "FULL", "OUTER", "CROSS", "USING", "PREWHERE", "WHERE", "GROUP", "BY",
"WITH", "TOTALS", "HAVING", "ORDER", "COLLATE", "LIMIT", "UNION", "AND", "OR", "ASC",
"IN", "KILL", "QUERY", "SYNC", "ASYNC", "TEST", "BETWEEN", "TRUNCATE"};
}
void Suggest::loadImpl(Connection & connection, const ConnectionTimeouts & timeouts, size_t suggestion_limit)
{
std::stringstream query;
query << "SELECT DISTINCT arrayJoin(extractAll(name, '[\\\\w_]{2,}')) AS res FROM ("
"SELECT name FROM system.functions"
" UNION ALL "
"SELECT name FROM system.table_engines"
" UNION ALL "
"SELECT name FROM system.formats"
" UNION ALL "
"SELECT name FROM system.table_functions"
" UNION ALL "
"SELECT name FROM system.data_type_families"
" UNION ALL "
"SELECT name FROM system.settings"
" UNION ALL "
"SELECT cluster FROM system.clusters"
" UNION ALL "
"SELECT concat(func.name, comb.name) FROM system.functions AS func CROSS JOIN system.aggregate_function_combinators AS comb WHERE is_aggregate";
/// The user may disable loading of databases, tables, columns by setting suggestion_limit to zero.
if (suggestion_limit > 0)
{
String limit_str = toString(suggestion_limit);
query <<
" UNION ALL "
"SELECT name FROM system.databases LIMIT " << limit_str
<< " UNION ALL "
"SELECT DISTINCT name FROM system.tables LIMIT " << limit_str
<< " UNION ALL "
"SELECT DISTINCT name FROM system.columns LIMIT " << limit_str;
}
query << ") WHERE notEmpty(res)";
fetch(connection, timeouts, query.str());
}
void Suggest::fetch(Connection & connection, const ConnectionTimeouts & timeouts, const std::string & query)
{
connection.sendQuery(timeouts, query);
while (true)
{
Packet packet = connection.receivePacket();
switch (packet.type)
{
case Protocol::Server::Data:
fillWordsFromBlock(packet.block);
continue;
case Protocol::Server::Progress:
continue;
case Protocol::Server::ProfileInfo:
continue;
case Protocol::Server::Totals:
continue;
case Protocol::Server::Extremes:
continue;
case Protocol::Server::Log:
continue;
case Protocol::Server::Exception:
packet.exception->rethrow();
return;
case Protocol::Server::EndOfStream:
return;
default:
throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
}
}
}
void Suggest::fillWordsFromBlock(const Block & block)
{
if (!block)
return;
if (block.columns() != 1)
throw Exception("Wrong number of columns received for query to read words for suggestion", ErrorCodes::LOGICAL_ERROR);
const ColumnString & column = typeid_cast<const ColumnString &>(*block.getByPosition(0).column);
size_t rows = block.rows();
for (size_t i = 0; i < rows; ++i)
words.emplace_back(column.getDataAt(i).toString());
}
}

View File

@ -2,18 +2,9 @@
#include "ConnectionParameters.h" #include "ConnectionParameters.h"
#include <string>
#include <sstream>
#include <string.h>
#include <vector>
#include <algorithm>
#include <common/readline_use.h>
#include <Common/typeid_cast.h>
#include <Columns/ColumnString.h>
#include <Client/Connection.h> #include <Client/Connection.h>
#include <IO/ConnectionTimeouts.h> #include <IO/ConnectionTimeouts.h>
#include <common/LineReader.h>
namespace DB namespace DB
@ -24,141 +15,8 @@ namespace ErrorCodes
extern const int UNKNOWN_PACKET_FROM_SERVER; extern const int UNKNOWN_PACKET_FROM_SERVER;
} }
class Suggest : private boost::noncopyable class Suggest : public LineReader::Suggest, boost::noncopyable
{ {
private:
/// The vector will be filled with completion words from the server and sorted.
using Words = std::vector<std::string>;
/// Keywords may be not up to date with ClickHouse parser.
Words words
{
"CREATE", "DATABASE", "IF", "NOT", "EXISTS", "TEMPORARY", "TABLE", "ON", "CLUSTER", "DEFAULT", "MATERIALIZED", "ALIAS", "ENGINE",
"AS", "VIEW", "POPULATE", "SETTINGS", "ATTACH", "DETACH", "DROP", "RENAME", "TO", "ALTER", "ADD", "MODIFY", "CLEAR", "COLUMN", "AFTER",
"COPY", "PROJECT", "PRIMARY", "KEY", "CHECK", "PARTITION", "PART", "FREEZE", "FETCH", "FROM", "SHOW", "INTO", "OUTFILE", "FORMAT", "TABLES",
"DATABASES", "LIKE", "PROCESSLIST", "CASE", "WHEN", "THEN", "ELSE", "END", "DESCRIBE", "DESC", "USE", "SET", "OPTIMIZE", "FINAL", "DEDUPLICATE",
"INSERT", "VALUES", "SELECT", "DISTINCT", "SAMPLE", "ARRAY", "JOIN", "GLOBAL", "LOCAL", "ANY", "ALL", "INNER", "LEFT", "RIGHT", "FULL", "OUTER",
"CROSS", "USING", "PREWHERE", "WHERE", "GROUP", "BY", "WITH", "TOTALS", "HAVING", "ORDER", "COLLATE", "LIMIT", "UNION", "AND", "OR", "ASC", "IN",
"KILL", "QUERY", "SYNC", "ASYNC", "TEST", "BETWEEN", "TRUNCATE"
};
/// Words are fetched asynchronously.
std::thread loading_thread;
std::atomic<bool> ready{false};
/// Points to current word to suggest.
Words::const_iterator pos;
/// Points after the last possible match.
Words::const_iterator end;
/// Set iterators to the matched range of words if any.
void findRange(const char * prefix, size_t prefix_length)
{
std::string prefix_str(prefix);
std::tie(pos, end) = std::equal_range(words.begin(), words.end(), prefix_str,
[prefix_length](const std::string & s, const std::string & prefix_searched) { return strncmp(s.c_str(), prefix_searched.c_str(), prefix_length) < 0; });
}
/// Iterates through matched range.
char * nextMatch()
{
if (pos >= end)
return nullptr;
/// readline will free memory by itself.
char * word = strdup(pos->c_str());
++pos;
return word;
}
void loadImpl(Connection & connection, const ConnectionTimeouts & timeouts, size_t suggestion_limit)
{
std::stringstream query;
query << "SELECT DISTINCT arrayJoin(extractAll(name, '[\\\\w_]{2,}')) AS res FROM ("
"SELECT name FROM system.functions"
" UNION ALL "
"SELECT name FROM system.table_engines"
" UNION ALL "
"SELECT name FROM system.formats"
" UNION ALL "
"SELECT name FROM system.table_functions"
" UNION ALL "
"SELECT name FROM system.data_type_families"
" UNION ALL "
"SELECT name FROM system.settings"
" UNION ALL "
"SELECT concat(func.name, comb.name) FROM system.functions AS func CROSS JOIN system.aggregate_function_combinators AS comb WHERE is_aggregate";
/// The user may disable loading of databases, tables, columns by setting suggestion_limit to zero.
if (suggestion_limit > 0)
{
String limit_str = toString(suggestion_limit);
query <<
" UNION ALL "
"SELECT name FROM system.databases LIMIT " << limit_str
<< " UNION ALL "
"SELECT DISTINCT name FROM system.tables LIMIT " << limit_str
<< " UNION ALL "
"SELECT DISTINCT name FROM system.columns LIMIT " << limit_str;
}
query << ") WHERE notEmpty(res)";
fetch(connection, timeouts, query.str());
}
void fetch(Connection & connection, const ConnectionTimeouts & timeouts, const std::string & query)
{
connection.sendQuery(timeouts, query);
while (true)
{
Packet packet = connection.receivePacket();
switch (packet.type)
{
case Protocol::Server::Data:
fillWordsFromBlock(packet.block);
continue;
case Protocol::Server::Progress:
continue;
case Protocol::Server::ProfileInfo:
continue;
case Protocol::Server::Totals:
continue;
case Protocol::Server::Extremes:
continue;
case Protocol::Server::Log:
continue;
case Protocol::Server::Exception:
packet.exception->rethrow();
return;
case Protocol::Server::EndOfStream:
return;
default:
throw Exception("Unknown packet from server", ErrorCodes::UNKNOWN_PACKET_FROM_SERVER);
}
}
}
void fillWordsFromBlock(const Block & block)
{
if (!block)
return;
if (block.columns() != 1)
throw Exception("Wrong number of columns received for query to read words for suggestion", ErrorCodes::LOGICAL_ERROR);
const ColumnString & column = typeid_cast<const ColumnString &>(*block.getByPosition(0).column);
size_t rows = block.rows();
for (size_t i = 0; i < rows; ++i)
words.emplace_back(column.getDataAt(i).toString());
}
public: public:
static Suggest & instance() static Suggest & instance()
{ {
@ -166,64 +24,25 @@ public:
return instance; return instance;
} }
/// More old server versions cannot execute the query above. void load(const ConnectionParameters & connection_parameters, size_t suggestion_limit);
/// Older server versions cannot execute the query above.
static constexpr int MIN_SERVER_REVISION = 54406; static constexpr int MIN_SERVER_REVISION = 54406;
void load(const ConnectionParameters & connection_parameters, size_t suggestion_limit) private:
{ Suggest();
loading_thread = std::thread([connection_parameters, suggestion_limit, this] ~Suggest()
{
try
{
Connection connection(
connection_parameters.host,
connection_parameters.port,
connection_parameters.default_database,
connection_parameters.user,
connection_parameters.password,
"client",
connection_parameters.compression,
connection_parameters.security);
loadImpl(connection, connection_parameters.timeouts, suggestion_limit);
}
catch (...)
{
std::cerr << "Cannot load data for command line suggestions: " << getCurrentExceptionMessage(false, true) << "\n";
}
/// Note that keyword suggestions are available even if we cannot load data from server.
std::sort(words.begin(), words.end());
ready = true;
});
}
void finalize()
{ {
if (loading_thread.joinable()) if (loading_thread.joinable())
loading_thread.join(); loading_thread.join();
} }
/// A function for readline. void loadImpl(Connection & connection, const ConnectionTimeouts & timeouts, size_t suggestion_limit);
static char * generator(const char * text, int state) void fetch(Connection & connection, const ConnectionTimeouts & timeouts, const std::string & query);
{ void fillWordsFromBlock(const Block & block);
Suggest & suggest = Suggest::instance();
if (!suggest.ready)
return nullptr;
if (state == 0)
suggest.findRange(text, strlen(text));
/// Do not append whitespace after word. For unknown reason, rl_completion_append_character = '\0' does not work. /// Words are fetched asynchronously.
rl_completion_suppress_append = 1; std::thread loading_thread;
return suggest.nextMatch();
}
~Suggest()
{
finalize();
}
}; };
} }

View File

@ -10,4 +10,4 @@ set_target_properties(readpassphrase
PROPERTIES LINKER_LANGUAGE C PROPERTIES LINKER_LANGUAGE C
) )
# . to allow #include <readpassphrase.h> # . to allow #include <readpassphrase.h>
target_include_directories(readpassphrase PUBLIC . ${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CURRENT_BINARY_DIR}/../include) target_include_directories(readpassphrase PUBLIC . ${CMAKE_CURRENT_BINARY_DIR}/include)

View File

@ -15,20 +15,24 @@ set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE PUBLIC ${ClickHouse_SOURCE_DIR}/libs/libdaemo
if (USE_POCO_SQLODBC) if (USE_POCO_SQLODBC)
set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_SQLODBC_LIBRARY}) set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_SQLODBC_LIBRARY})
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_SQLODBC_INCLUDE_DIR}) # Wouldnt work anyway because of the way list variable got expanded in `target_include_directories`
# set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_SQLODBC_INCLUDE_DIR})
endif () endif ()
if (Poco_SQL_FOUND) if (Poco_SQL_FOUND)
set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_SQL_LIBRARY}) set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_SQL_LIBRARY})
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_SQL_INCLUDE_DIR}) # Wouldnt work anyway because of the way list variable got expanded in `target_include_directories`
# set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_SQL_INCLUDE_DIR})
endif () endif ()
if (USE_POCO_DATAODBC) if (USE_POCO_DATAODBC)
set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_DataODBC_LIBRARY}) set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_DataODBC_LIBRARY})
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_DataODBC_INCLUDE_DIR}) # Wouldnt work anyway because of the way list variable got expanded in `target_include_directories`
# set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${ODBC_INCLUDE_DIRS} ${Poco_DataODBC_INCLUDE_DIR})
endif() endif()
if (Poco_Data_FOUND) if (Poco_Data_FOUND)
set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_Data_LIBRARY}) set(CLICKHOUSE_ODBC_BRIDGE_LINK ${CLICKHOUSE_ODBC_BRIDGE_LINK} PRIVATE ${Poco_Data_LIBRARY})
set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_Data_INCLUDE_DIR}) # Wouldnt work anyway because of the way list variable got expanded in `target_include_directories`
# set(CLICKHOUSE_ODBC_BRIDGE_INCLUDE ${CLICKHOUSE_ODBC_BRIDGE_INCLUDE} SYSTEM PRIVATE ${Poco_Data_INCLUDE_DIR})
endif () endif ()
clickhouse_program_add_library(odbc-bridge) clickhouse_program_add_library(odbc-bridge)

View File

@ -61,6 +61,10 @@ void InterserverIOHTTPHandler::processQuery(Poco::Net::HTTPServerRequest & reque
ReadBufferFromIStream body(request.stream()); ReadBufferFromIStream body(request.stream());
auto endpoint = server.context().getInterserverIOHandler().getEndpoint(endpoint_name); auto endpoint = server.context().getInterserverIOHandler().getEndpoint(endpoint_name);
/// Locked for read while query processing
std::shared_lock lock(endpoint->rwlock);
if (endpoint->blocker.isCancelled())
throw Exception("Transferring part to replica was cancelled", ErrorCodes::ABORTED);
if (compress) if (compress)
{ {

View File

@ -79,21 +79,19 @@ void MySQLHandler::run()
if (!connection_context.mysql.max_packet_size) if (!connection_context.mysql.max_packet_size)
connection_context.mysql.max_packet_size = MAX_PACKET_LENGTH; connection_context.mysql.max_packet_size = MAX_PACKET_LENGTH;
/* LOG_TRACE(log, "Capabilities: " << handshake_response.capability_flags LOG_TRACE(log, "Capabilities: " << handshake_response.capability_flags
<< "\nmax_packet_size: " << ", max_packet_size: "
<< handshake_response.max_packet_size << handshake_response.max_packet_size
<< "\ncharacter_set: " << ", character_set: "
<< handshake_response.character_set << static_cast<int>(handshake_response.character_set)
<< "\nuser: " << ", user: "
<< handshake_response.username << handshake_response.username
<< "\nauth_response length: " << ", auth_response length: "
<< handshake_response.auth_response.length() << handshake_response.auth_response.length()
<< "\nauth_response: " << ", database: "
<< handshake_response.auth_response
<< "\ndatabase: "
<< handshake_response.database << handshake_response.database
<< "\nauth_plugin_name: " << ", auth_plugin_name: "
<< handshake_response.auth_plugin_name);*/ << handshake_response.auth_plugin_name);
client_capability_flags = handshake_response.capability_flags; client_capability_flags = handshake_response.capability_flags;
if (!(client_capability_flags & CLIENT_PROTOCOL_41)) if (!(client_capability_flags & CLIENT_PROTOCOL_41))

View File

@ -34,7 +34,7 @@ MySQLHandlerFactory::MySQLHandlerFactory(IServer & server_)
} }
catch (...) catch (...)
{ {
LOG_INFO(log, "Failed to create SSL context. SSL will be disabled. Error: " << getCurrentExceptionMessage(false)); LOG_TRACE(log, "Failed to create SSL context. SSL will be disabled. Error: " << getCurrentExceptionMessage(false));
ssl_enabled = false; ssl_enabled = false;
} }
#endif #endif
@ -47,7 +47,7 @@ MySQLHandlerFactory::MySQLHandlerFactory(IServer & server_)
} }
catch (...) catch (...)
{ {
LOG_WARNING(log, "Failed to read RSA keys. Error: " << getCurrentExceptionMessage(false)); LOG_TRACE(log, "Failed to read RSA key pair from server certificate. Error: " << getCurrentExceptionMessage(false));
generateRSAKeys(); generateRSAKeys();
} }
#endif #endif
@ -104,7 +104,7 @@ void MySQLHandlerFactory::readRSAKeys()
void MySQLHandlerFactory::generateRSAKeys() void MySQLHandlerFactory::generateRSAKeys()
{ {
LOG_INFO(log, "Generating new RSA key."); LOG_TRACE(log, "Generating new RSA key pair.");
public_key.reset(RSA_new()); public_key.reset(RSA_new());
if (!public_key) if (!public_key)
throw Exception("Failed to allocate RSA key. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR); throw Exception("Failed to allocate RSA key. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);

View File

@ -436,8 +436,10 @@ int Server::main(const std::vector<std::string> & /*args*/)
main_config_zk_changed_event, main_config_zk_changed_event,
[&](ConfigurationPtr config) [&](ConfigurationPtr config)
{ {
setTextLog(global_context->getTextLog()); // FIXME logging-related things need synchronization -- see the 'Logger * log' saved
buildLoggers(*config, logger()); // in a lot of places. For now, disable updating log configuration without server restart.
//setTextLog(global_context->getTextLog());
//buildLoggers(*config, logger());
global_context->setClustersConfig(config); global_context->setClustersConfig(config);
global_context->setMacros(std::make_unique<Macros>(*config, "macros")); global_context->setMacros(std::make_unique<Macros>(*config, "macros"));
@ -862,6 +864,9 @@ int Server::main(const std::vector<std::string> & /*args*/)
for (auto & server : servers) for (auto & server : servers)
server->start(); server->start();
setTextLog(global_context->getTextLog());
buildLoggers(config(), logger());
main_config_reloader->start(); main_config_reloader->start();
users_config_reloader->start(); users_config_reloader->start();
if (dns_cache_updater) if (dns_cache_updater)

View File

@ -387,7 +387,6 @@ namespace ErrorCodes
extern const int PTHREAD_ERROR = 411; extern const int PTHREAD_ERROR = 411;
extern const int NETLINK_ERROR = 412; extern const int NETLINK_ERROR = 412;
extern const int CANNOT_SET_SIGNAL_HANDLER = 413; extern const int CANNOT_SET_SIGNAL_HANDLER = 413;
extern const int CANNOT_READLINE = 414;
extern const int ALL_REPLICAS_LOST = 415; extern const int ALL_REPLICAS_LOST = 415;
extern const int REPLICA_STATUS_CHANGED = 416; extern const int REPLICA_STATUS_CHANGED = 416;
extern const int EXPECTED_ALL_OR_ANY = 417; extern const int EXPECTED_ALL_OR_ANY = 417;

View File

@ -1030,6 +1030,7 @@ public:
LOG_TRACE(log, "Authentication method match."); LOG_TRACE(log, "Authentication method match.");
} }
bool sent_public_key = false;
if (auth_response == "\1") if (auth_response == "\1")
{ {
LOG_TRACE(log, "Client requests public key."); LOG_TRACE(log, "Client requests public key.");
@ -1050,6 +1051,7 @@ public:
AuthMoreData data(pem); AuthMoreData data(pem);
packet_sender->sendPacket(data, true); packet_sender->sendPacket(data, true);
sent_public_key = true;
AuthSwitchResponse response; AuthSwitchResponse response;
packet_sender->receivePacket(response); packet_sender->receivePacket(response);
@ -1069,13 +1071,15 @@ public:
*/ */
if (!is_secure_connection && !auth_response->empty() && auth_response != String("\0", 1)) if (!is_secure_connection && !auth_response->empty() && auth_response != String("\0", 1))
{ {
LOG_TRACE(log, "Received nonempty password"); LOG_TRACE(log, "Received nonempty password.");
auto ciphertext = reinterpret_cast<unsigned char *>(auth_response->data()); auto ciphertext = reinterpret_cast<unsigned char *>(auth_response->data());
unsigned char plaintext[RSA_size(&private_key)]; unsigned char plaintext[RSA_size(&private_key)];
int plaintext_size = RSA_private_decrypt(auth_response->size(), ciphertext, plaintext, &private_key, RSA_PKCS1_OAEP_PADDING); int plaintext_size = RSA_private_decrypt(auth_response->size(), ciphertext, plaintext, &private_key, RSA_PKCS1_OAEP_PADDING);
if (plaintext_size == -1) if (plaintext_size == -1)
{ {
if (!sent_public_key)
LOG_WARNING(log, "Client could have encrypted password with different public key since it didn't request it from server.");
throw Exception("Failed to decrypt auth data. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR); throw Exception("Failed to decrypt auth data. Error: " + getOpenSSLErrors(), ErrorCodes::OPENSSL_ERROR);
} }

View File

@ -10,8 +10,6 @@ namespace DB
{ {
namespace ErrorCodes namespace ErrorCodes
{ {
extern const int UNKNOWN_ELEMENT_IN_CONFIG;
extern const int EXCESSIVE_ELEMENT_IN_CONFIG;
extern const int FILE_DOESNT_EXIST; extern const int FILE_DOESNT_EXIST;
extern const int FILE_ALREADY_EXISTS; extern const int FILE_ALREADY_EXISTS;
extern const int DIRECTORY_DOESNT_EXIST; extern const int DIRECTORY_DOESNT_EXIST;

View File

@ -1,19 +1,24 @@
#pragma once #pragma once
#include <Disks/IDisk.h> #include <Disks/IDisk.h>
#include <IO/ReadBuffer.h>
#include <IO/WriteBuffer.h>
#include <mutex> #include <mutex>
#include <memory>
#include <unordered_map> #include <unordered_map>
namespace DB namespace DB
{ {
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
}
class ReadBuffer;
class WriteBuffer;
/** Implementation of Disk intended only for testing purposes.
* All filesystem objects are stored in memory and lost on server restart.
*
* NOTE Work in progress. Currently the interface is not viable enough to support MergeTree or even StripeLog tables.
* Please delete this interface if it will not be finished after 2020-06-18.
*/
class DiskMemory : public IDisk class DiskMemory : public IDisk
{ {
public: public:

View File

@ -32,7 +32,6 @@ if (OPENSSL_CRYPTO_LIBRARY)
target_link_libraries(clickhouse_functions PUBLIC ${OPENSSL_CRYPTO_LIBRARY}) target_link_libraries(clickhouse_functions PUBLIC ${OPENSSL_CRYPTO_LIBRARY})
endif() endif()
target_include_directories(clickhouse_functions PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/include)
target_include_directories(clickhouse_functions SYSTEM PRIVATE ${DIVIDE_INCLUDE_DIR} ${METROHASH_INCLUDE_DIR} ${SPARSEHASH_INCLUDE_DIR}) target_include_directories(clickhouse_functions SYSTEM PRIVATE ${DIVIDE_INCLUDE_DIR} ${METROHASH_INCLUDE_DIR} ${SPARSEHASH_INCLUDE_DIR})
if (CONSISTENT_HASHING_INCLUDE_DIR) if (CONSISTENT_HASHING_INCLUDE_DIR)

View File

@ -282,28 +282,31 @@ template <
typename Op, template <typename, size_t> typename OperationApplierImpl, size_t N = 10> typename Op, template <typename, size_t> typename OperationApplierImpl, size_t N = 10>
struct OperationApplier struct OperationApplier
{ {
template <typename Columns, typename ResultColumn> template <typename Columns, typename ResultData>
static void apply(Columns & in, ResultColumn & result) static void apply(Columns & in, ResultData & result_data, bool use_result_data_as_input = false)
{ {
while (in.size() > 1) if (!use_result_data_as_input)
{ doBatchedApply<false>(in, result_data);
doBatchedApply(in, result->getData()); while (in.size() > 0)
in.push_back(result.get()); doBatchedApply<true>(in, result_data);
}
} }
template <typename Columns, typename ResultData> template <bool CarryResult, typename Columns, typename ResultData>
static void NO_INLINE doBatchedApply(Columns & in, ResultData & result_data) static void NO_INLINE doBatchedApply(Columns & in, ResultData & result_data)
{ {
if (N > in.size()) if (N > in.size())
{ {
OperationApplier<Op, OperationApplierImpl, N - 1>::doBatchedApply(in, result_data); OperationApplier<Op, OperationApplierImpl, N - 1>
::template doBatchedApply<CarryResult>(in, result_data);
return; return;
} }
const OperationApplierImpl<Op, N> operationApplierImpl(in); const OperationApplierImpl<Op, N> operationApplierImpl(in);
size_t i = 0; size_t i = 0;
for (auto & res : result_data) for (auto & res : result_data)
if constexpr (CarryResult)
res = Op::apply(res, operationApplierImpl.apply(i++));
else
res = operationApplierImpl.apply(i++); res = operationApplierImpl.apply(i++);
in.erase(in.end() - N, in.end()); in.erase(in.end() - N, in.end());
@ -312,9 +315,9 @@ struct OperationApplier
template < template <
typename Op, template <typename, size_t> typename OperationApplierImpl> typename Op, template <typename, size_t> typename OperationApplierImpl>
struct OperationApplier<Op, OperationApplierImpl, 1> struct OperationApplier<Op, OperationApplierImpl, 0>
{ {
template <typename Columns, typename Result> template <bool, typename Columns, typename Result>
static void NO_INLINE doBatchedApply(Columns &, Result &) static void NO_INLINE doBatchedApply(Columns &, Result &)
{ {
throw Exception( throw Exception(
@ -332,7 +335,7 @@ static void executeForTernaryLogicImpl(ColumnRawPtrs arguments, ColumnWithTypeAn
const bool has_consts = extractConstColumnsTernary<Op>(arguments, const_3v_value); const bool has_consts = extractConstColumnsTernary<Op>(arguments, const_3v_value);
/// If the constant value uniquely determines the result, return it. /// If the constant value uniquely determines the result, return it.
if (has_consts && (arguments.empty() || (Op::isSaturable() && Op::isSaturatedValue(const_3v_value)))) if (has_consts && (arguments.empty() || Op::isSaturatedValue(const_3v_value)))
{ {
result_info.column = ColumnConst::create( result_info.column = ColumnConst::create(
convertFromTernaryData(UInt8Container({const_3v_value}), result_info.type->isNullable()), convertFromTernaryData(UInt8Container({const_3v_value}), result_info.type->isNullable()),
@ -341,16 +344,10 @@ static void executeForTernaryLogicImpl(ColumnRawPtrs arguments, ColumnWithTypeAn
return; return;
} }
const auto result_column = ColumnUInt8::create(input_rows_count); const auto result_column = has_consts ?
MutableColumnPtr const_column_holder; ColumnUInt8::create(input_rows_count, const_3v_value) : ColumnUInt8::create(input_rows_count);
if (has_consts)
{
const_column_holder =
convertFromTernaryData(UInt8Container(input_rows_count, const_3v_value), const_3v_value == Ternary::Null);
arguments.push_back(const_column_holder.get());
}
OperationApplier<Op, AssociativeGenericApplierImpl>::apply(arguments, result_column); OperationApplier<Op, AssociativeGenericApplierImpl>::apply(arguments, result_column->getData(), has_consts);
result_info.column = convertFromTernaryData(result_column->getData(), result_info.type->isNullable()); result_info.column = convertFromTernaryData(result_column->getData(), result_info.type->isNullable());
} }
@ -425,19 +422,8 @@ static void basicExecuteImpl(ColumnRawPtrs arguments, ColumnWithTypeAndName & re
if (has_consts && Op::apply(const_val, 0) == 0 && Op::apply(const_val, 1) == 1) if (has_consts && Op::apply(const_val, 0) == 0 && Op::apply(const_val, 1) == 1)
has_consts = false; has_consts = false;
UInt8ColumnPtrs uint8_args; auto col_res = has_consts ?
ColumnUInt8::create(input_rows_count, const_val) : ColumnUInt8::create(input_rows_count);
auto col_res = ColumnUInt8::create();
UInt8Container & vec_res = col_res->getData();
if (has_consts)
{
vec_res.assign(input_rows_count, const_val);
uint8_args.push_back(col_res.get());
}
else
{
vec_res.resize(input_rows_count);
}
/// FastPath detection goes in here /// FastPath detection goes in here
if (arguments.size() == (has_consts ? 1 : 2)) if (arguments.size() == (has_consts ? 1 : 2))
@ -452,7 +438,8 @@ static void basicExecuteImpl(ColumnRawPtrs arguments, ColumnWithTypeAndName & re
} }
/// Convert all columns to UInt8 /// Convert all columns to UInt8
Columns converted_columns; UInt8ColumnPtrs uint8_args;
Columns converted_columns_holder;
for (const IColumn * column : arguments) for (const IColumn * column : arguments)
{ {
if (auto uint8_column = checkAndGetColumn<ColumnUInt8>(column)) if (auto uint8_column = checkAndGetColumn<ColumnUInt8>(column))
@ -462,15 +449,11 @@ static void basicExecuteImpl(ColumnRawPtrs arguments, ColumnWithTypeAndName & re
auto converted_column = ColumnUInt8::create(input_rows_count); auto converted_column = ColumnUInt8::create(input_rows_count);
convertColumnToUInt8(column, converted_column->getData()); convertColumnToUInt8(column, converted_column->getData());
uint8_args.push_back(converted_column.get()); uint8_args.push_back(converted_column.get());
converted_columns.emplace_back(std::move(converted_column)); converted_columns_holder.emplace_back(std::move(converted_column));
} }
} }
OperationApplier<Op, AssociativeApplierImpl>::apply(uint8_args, col_res); OperationApplier<Op, AssociativeApplierImpl>::apply(uint8_args, col_res->getData(), has_consts);
/// This is possible if there is exactly one non-constant among the arguments, and it is of type UInt8.
if (uint8_args[0] != col_res.get())
vec_res.assign(uint8_args[0]->getData());
result_info.column = std::move(col_res); result_info.column = std::move(col_res);
} }

View File

@ -83,12 +83,7 @@ struct XorImpl
static inline constexpr bool isSaturable() { return false; } static inline constexpr bool isSaturable() { return false; }
static inline constexpr bool isSaturatedValue(bool) { return false; } static inline constexpr bool isSaturatedValue(bool) { return false; }
/** Considering that CH uses UInt8 for representation of boolean values this function static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return !!a != !!b; }
* returns 255 as "true" but the current implementation of logical functions suggests that
* any nonzero value is "true" as well. Also the current code provides no guarantee
* for "true" to be represented with the value of 1.
*/
static inline constexpr ResultType apply(UInt8 a, UInt8 b) { return (a != b) ? Ternary::True : Ternary::False; }
static inline constexpr bool specialImplementationForNulls() { return false; } static inline constexpr bool specialImplementationForNulls() { return false; }
#if USE_EMBEDDED_COMPILER #if USE_EMBEDDED_COMPILER

View File

@ -2,7 +2,6 @@ include(${ClickHouse_SOURCE_DIR}/cmake/dbms_glob_sources.cmake)
add_headers_and_sources(clickhouse_functions_url .) add_headers_and_sources(clickhouse_functions_url .)
add_library(clickhouse_functions_url ${clickhouse_functions_url_sources} ${clickhouse_functions_url_headers}) add_library(clickhouse_functions_url ${clickhouse_functions_url_sources} ${clickhouse_functions_url_headers})
target_link_libraries(clickhouse_functions_url PRIVATE dbms) target_link_libraries(clickhouse_functions_url PRIVATE dbms)
target_include_directories(clickhouse_functions_url PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/../include) # ${CMAKE_CURRENT_BINARY_DIR}/include
if (CMAKE_BUILD_TYPE_UC STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE_UC STREQUAL "RELWITHDEBINFO" OR CMAKE_BUILD_TYPE_UC STREQUAL "MINSIZEREL") 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. # Won't generate debug info for files with heavy template instantiation to achieve faster linking and lower size.

View File

@ -14,6 +14,7 @@ namespace ErrorCodes
{ {
extern const int SIZES_OF_ARRAYS_DOESNT_MATCH; extern const int SIZES_OF_ARRAYS_DOESNT_MATCH;
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
extern const int ILLEGAL_COLUMN;
} }
/// arrayZip(['a', 'b', 'c'], ['d', 'e', 'f']) = [('a', 'd'), ('b', 'e'), ('c', 'f')] /// arrayZip(['a', 'b', 'c'], ['d', 'e', 'f']) = [('a', 'd'), ('b', 'e'), ('c', 'f')]
@ -44,9 +45,8 @@ public:
const DataTypeArray * array_type = checkAndGetDataType<DataTypeArray>(arguments[index].type.get()); const DataTypeArray * array_type = checkAndGetDataType<DataTypeArray>(arguments[index].type.get());
if (!array_type) if (!array_type)
throw Exception( throw Exception("Argument " + toString(index + 1) + " of function " + getName()
"Argument " + toString(index + 1) + " of function must be array. Found " + arguments[0].type->getName() + " instead.", + " must be array. Found " + arguments[0].type->getName() + " instead.", ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
arguments_types.emplace_back(array_type->getNestedType()); arguments_types.emplace_back(array_type->getNestedType());
} }
@ -56,26 +56,37 @@ public:
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
{ {
auto first_argument = block.getByPosition(arguments[0]); size_t num_arguments = arguments.size();
const auto & first_array_column = checkAndGetColumn<ColumnArray>(first_argument.column.get());
Columns res_tuple_columns(arguments.size()); ColumnPtr first_array_column;
res_tuple_columns[0] = first_array_column->getDataPtr(); Columns tuple_columns(num_arguments);
for (size_t index = 1; index < arguments.size(); ++index) for (size_t i = 0; i < num_arguments; ++i)
{ {
const auto & argument_type_and_column = block.getByPosition(arguments[index]); /// Constant columns cannot be inside tuple. It's only possible to have constant tuple as a whole.
const auto & argument_array_column = checkAndGetColumn<ColumnArray>(argument_type_and_column.column.get()); ColumnPtr holder = block.getByPosition(arguments[i]).column->convertToFullColumnIfConst();
if (!first_array_column->hasEqualOffsets(*argument_array_column)) const ColumnArray * column_array = checkAndGetColumn<ColumnArray>(holder.get());
throw Exception("The argument 1 and argument " + toString(index + 1) + " of function have different array sizes",
if (!column_array)
throw Exception("Argument " + toString(i + 1) + " of function " + getName() + " must be array."
" Found column " + holder->getName() + " instead.", ErrorCodes::ILLEGAL_COLUMN);
if (i == 0)
{
first_array_column = holder;
}
else if (!column_array->hasEqualOffsets(static_cast<const ColumnArray &>(*first_array_column)))
{
throw Exception("The argument 1 and argument " + toString(i + 1) + " of function " + getName() + " have different array sizes",
ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH); ErrorCodes::SIZES_OF_ARRAYS_DOESNT_MATCH);
}
res_tuple_columns[index] = argument_array_column->getDataPtr(); tuple_columns[i] = column_array->getDataPtr();
} }
block.getByPosition(result).column = ColumnArray::create( block.getByPosition(result).column = ColumnArray::create(
ColumnTuple::create(res_tuple_columns), first_array_column->getOffsetsPtr()); ColumnTuple::create(tuple_columns), static_cast<const ColumnArray &>(*first_array_column).getOffsetsPtr());
} }
}; };

View File

@ -0,0 +1,50 @@
#include <ext/bit_cast.h>
#include <Functions/FunctionFactory.h>
#include <Functions/FunctionUnaryArithmetic.h>
namespace DB
{
template <typename A>
struct BitCountImpl
{
using ResultType = UInt8;
static inline ResultType apply(A a)
{
/// We count bits in the value representation in memory. For example, we support floats.
/// We need to avoid sign-extension when converting signed numbers to larger type. So, uint8_t(-1) has 8 bits.
if constexpr (std::is_same_v<A, UInt64> || std::is_same_v<A, Int64>)
return __builtin_popcountll(a);
if constexpr (std::is_same_v<A, UInt32> || std::is_same_v<A, Int32> || std::is_unsigned_v<A>)
return __builtin_popcount(a);
else
return __builtin_popcountll(ext::bit_cast<unsigned long long>(a));
}
#if USE_EMBEDDED_COMPILER
static constexpr bool compilable = false;
#endif
};
struct NameBitCount { static constexpr auto name = "bitCount"; };
using FunctionBitCount = FunctionUnaryArithmetic<BitCountImpl, NameBitCount, false /* is injective */>;
/// The function has no ranges of monotonicity.
template <> struct FunctionUnaryArithmeticMonotonicity<NameBitCount>
{
static bool has() { return false; }
static IFunction::Monotonicity get(const Field &, const Field &)
{
return {};
}
};
void registerFunctionBitCount(FunctionFactory & factory)
{
factory.registerFunction<FunctionBitCount>();
}
}

View File

@ -0,0 +1,73 @@
#include <Functions/IFunctionImpl.h>
#include <Functions/FunctionHelpers.h>
#include <Functions/FunctionFactory.h>
#include <DataTypes/DataTypesNumber.h>
#include <DataTypes/getLeastSupertype.h>
#include <Core/ColumnNumbers.h>
namespace DB
{
/// ifNotFinite(x, y) is equivalent to isFinite(x) ? x : y.
class FunctionIfNotFinite : public IFunction
{
public:
static constexpr auto name = "ifNotFinite";
FunctionIfNotFinite(const Context & context_) : context(context_) {}
static FunctionPtr create(const Context & context)
{
return std::make_shared<FunctionIfNotFinite>(context);
}
std::string getName() const override
{
return name;
}
size_t getNumberOfArguments() const override { return 2; }
bool useDefaultImplementationForNulls() const override { return false; }
bool useDefaultImplementationForConstants() const override { return true; }
ColumnNumbers getArgumentsThatDontImplyNullableReturnType(size_t /*number_of_arguments*/) const override { return {0}; }
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
{
auto is_finite_type = FunctionFactory::instance().get("isFinite", context)->build({arguments[0]})->getReturnType();
auto if_type = FunctionFactory::instance().get("if", context)->build({{nullptr, is_finite_type, ""}, arguments[0], arguments[1]})->getReturnType();
return if_type;
}
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
{
Block temp_block = block;
auto is_finite = FunctionFactory::instance().get("isFinite", context)->build(
{temp_block.getByPosition(arguments[0])});
size_t is_finite_pos = temp_block.columns();
temp_block.insert({nullptr, is_finite->getReturnType(), ""});
auto func_if = FunctionFactory::instance().get("if", context)->build(
{temp_block.getByPosition(is_finite_pos), temp_block.getByPosition(arguments[0]), temp_block.getByPosition(arguments[1])});
is_finite->execute(temp_block, {arguments[0]}, is_finite_pos, input_rows_count);
func_if->execute(temp_block, {is_finite_pos, arguments[0], arguments[1]}, result, input_rows_count);
block.getByPosition(result).column = std::move(temp_block.getByPosition(result).column);
}
private:
const Context & context;
};
void registerFunctionIfNotFinite(FunctionFactory & factory)
{
factory.registerFunction<FunctionIfNotFinite>();
}
}

View File

@ -20,6 +20,7 @@ void registerFunctionBitShiftLeft(FunctionFactory & factory);
void registerFunctionBitShiftRight(FunctionFactory & factory); void registerFunctionBitShiftRight(FunctionFactory & factory);
void registerFunctionBitRotateLeft(FunctionFactory & factory); void registerFunctionBitRotateLeft(FunctionFactory & factory);
void registerFunctionBitRotateRight(FunctionFactory & factory); void registerFunctionBitRotateRight(FunctionFactory & factory);
void registerFunctionBitCount(FunctionFactory & factory);
void registerFunctionLeast(FunctionFactory & factory); void registerFunctionLeast(FunctionFactory & factory);
void registerFunctionGreatest(FunctionFactory & factory); void registerFunctionGreatest(FunctionFactory & factory);
void registerFunctionBitTest(FunctionFactory & factory); void registerFunctionBitTest(FunctionFactory & factory);
@ -58,6 +59,7 @@ void registerFunctionsArithmetic(FunctionFactory & factory)
registerFunctionBitShiftRight(factory); registerFunctionBitShiftRight(factory);
registerFunctionBitRotateLeft(factory); registerFunctionBitRotateLeft(factory);
registerFunctionBitRotateRight(factory); registerFunctionBitRotateRight(factory);
registerFunctionBitCount(factory);
registerFunctionLeast(factory); registerFunctionLeast(factory);
registerFunctionGreatest(factory); registerFunctionGreatest(factory);
registerFunctionBitTest(factory); registerFunctionBitTest(factory);

View File

@ -36,6 +36,7 @@ void registerFunctionHasColumnInTable(FunctionFactory &);
void registerFunctionIsFinite(FunctionFactory &); void registerFunctionIsFinite(FunctionFactory &);
void registerFunctionIsInfinite(FunctionFactory &); void registerFunctionIsInfinite(FunctionFactory &);
void registerFunctionIsNaN(FunctionFactory &); void registerFunctionIsNaN(FunctionFactory &);
void registerFunctionIfNotFinite(FunctionFactory &);
void registerFunctionThrowIf(FunctionFactory &); void registerFunctionThrowIf(FunctionFactory &);
void registerFunctionVersion(FunctionFactory &); void registerFunctionVersion(FunctionFactory &);
void registerFunctionUptime(FunctionFactory &); void registerFunctionUptime(FunctionFactory &);
@ -93,6 +94,7 @@ void registerFunctionsMiscellaneous(FunctionFactory & factory)
registerFunctionIsFinite(factory); registerFunctionIsFinite(factory);
registerFunctionIsInfinite(factory); registerFunctionIsInfinite(factory);
registerFunctionIsNaN(factory); registerFunctionIsNaN(factory);
registerFunctionIfNotFinite(factory);
registerFunctionThrowIf(factory); registerFunctionThrowIf(factory);
registerFunctionVersion(factory); registerFunctionVersion(factory);
registerFunctionUptime(factory); registerFunctionUptime(factory);

View File

@ -1,5 +1,6 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <cstring>
#include <ryu/ryu.h> #include <ryu/ryu.h>

View File

@ -11,6 +11,7 @@
#include <map> #include <map>
#include <atomic> #include <atomic>
#include <utility> #include <utility>
#include <shared_mutex>
#include <Poco/Net/HTMLForm.h> #include <Poco/Net/HTMLForm.h>
namespace Poco { namespace Net { class HTTPServerResponse; } } namespace Poco { namespace Net { class HTTPServerResponse; } }
@ -24,42 +25,6 @@ namespace ErrorCodes
extern const int NO_SUCH_INTERSERVER_IO_ENDPOINT; extern const int NO_SUCH_INTERSERVER_IO_ENDPOINT;
} }
/** Location of the service.
*/
struct InterserverIOEndpointLocation
{
public:
InterserverIOEndpointLocation(const std::string & name_, const std::string & host_, UInt16 port_)
: name(name_), host(host_), port(port_)
{
}
/// Creates a location based on its serialized representation.
InterserverIOEndpointLocation(const std::string & serialized_location)
{
ReadBufferFromString buf(serialized_location);
readBinary(name, buf);
readBinary(host, buf);
readBinary(port, buf);
assertEOF(buf);
}
/// Serializes the location.
std::string toString() const
{
WriteBufferFromOwnString buf;
writeBinary(name, buf);
writeBinary(host, buf);
writeBinary(port, buf);
return buf.str();
}
public:
std::string name;
std::string host;
UInt16 port;
};
/** Query processor from other servers. /** Query processor from other servers.
*/ */
class InterserverIOEndpoint class InterserverIOEndpoint
@ -71,6 +36,7 @@ public:
/// You need to stop the data transfer if blocker is activated. /// You need to stop the data transfer if blocker is activated.
ActionBlocker blocker; ActionBlocker blocker;
std::shared_mutex rwlock;
}; };
using InterserverIOEndpointPtr = std::shared_ptr<InterserverIOEndpoint>; using InterserverIOEndpointPtr = std::shared_ptr<InterserverIOEndpoint>;
@ -90,11 +56,10 @@ public:
throw Exception("Duplicate interserver IO endpoint: " + name, ErrorCodes::DUPLICATE_INTERSERVER_IO_ENDPOINT); throw Exception("Duplicate interserver IO endpoint: " + name, ErrorCodes::DUPLICATE_INTERSERVER_IO_ENDPOINT);
} }
void removeEndpoint(const String & name) bool removeEndpointIfExists(const String & name)
{ {
std::lock_guard lock(mutex); std::lock_guard lock(mutex);
if (!endpoint_map.erase(name)) return endpoint_map.erase(name);
throw Exception("No interserver IO endpoint named " + name, ErrorCodes::NO_SUCH_INTERSERVER_IO_ENDPOINT);
} }
InterserverIOEndpointPtr getEndpoint(const String & name) InterserverIOEndpointPtr getEndpoint(const String & name)
@ -115,41 +80,4 @@ private:
std::mutex mutex; std::mutex mutex;
}; };
/// In the constructor calls `addEndpoint`, in the destructor - `removeEndpoint`.
class InterserverIOEndpointHolder
{
public:
InterserverIOEndpointHolder(const String & name_, InterserverIOEndpointPtr endpoint_, InterserverIOHandler & handler_)
: name(name_), endpoint(std::move(endpoint_)), handler(handler_)
{
handler.addEndpoint(name, endpoint);
}
InterserverIOEndpointPtr getEndpoint()
{
return endpoint;
}
~InterserverIOEndpointHolder()
try
{
handler.removeEndpoint(name);
/// After destroying the object, `endpoint` can still live, since its ownership is acquired during the processing of the request,
/// see InterserverIOHTTPHandler.cpp
}
catch (...)
{
tryLogCurrentException("~InterserverIOEndpointHolder");
}
ActionBlocker & getBlocker() { return endpoint->blocker; }
private:
String name;
InterserverIOEndpointPtr endpoint;
InterserverIOHandler & handler;
};
using InterserverIOEndpointHolderPtr = std::shared_ptr<InterserverIOEndpointHolder>;
} }

View File

@ -11,7 +11,6 @@ namespace DB
class IStorage; class IStorage;
using StoragePtr = std::shared_ptr<IStorage>; using StoragePtr = std::shared_ptr<IStorage>;
using StorageWeakPtr = std::weak_ptr<IStorage>;
using Tables = std::map<String, StoragePtr>; using Tables = std::map<String, StoragePtr>;
} }

View File

@ -1,8 +1,6 @@
#include <Storages/MergeTree/DataPartsExchange.h> #include <Storages/MergeTree/DataPartsExchange.h>
#include <Storages/IStorage.h>
#include <Common/CurrentMetrics.h> #include <Common/CurrentMetrics.h>
#include <Common/NetException.h> #include <Common/NetException.h>
#include <Common/typeid_cast.h>
#include <IO/HTTPCommon.h> #include <IO/HTTPCommon.h>
#include <Poco/File.h> #include <Poco/File.h>
#include <ext/scope_guard.h> #include <ext/scope_guard.h>
@ -53,9 +51,6 @@ std::string Service::getId(const std::string & node_id) const
void Service::processQuery(const Poco::Net::HTMLForm & params, ReadBuffer & /*body*/, WriteBuffer & out, Poco::Net::HTTPServerResponse & response) void Service::processQuery(const Poco::Net::HTMLForm & params, ReadBuffer & /*body*/, WriteBuffer & out, Poco::Net::HTTPServerResponse & response)
{ {
if (blocker.isCancelled())
throw Exception("Transferring part to replica was cancelled", ErrorCodes::ABORTED);
String client_protocol_version = params.get("client_protocol_version", REPLICATION_PROTOCOL_VERSION_WITHOUT_PARTS_SIZE); String client_protocol_version = params.get("client_protocol_version", REPLICATION_PROTOCOL_VERSION_WITHOUT_PARTS_SIZE);
@ -88,15 +83,11 @@ void Service::processQuery(const Poco::Net::HTMLForm & params, ReadBuffer & /*bo
++data.current_table_sends; ++data.current_table_sends;
SCOPE_EXIT({--data.current_table_sends;}); SCOPE_EXIT({--data.current_table_sends;});
StoragePtr owned_storage = storage.lock();
if (!owned_storage)
throw Exception("The table was already dropped", ErrorCodes::UNKNOWN_TABLE);
LOG_TRACE(log, "Sending part " << part_name); LOG_TRACE(log, "Sending part " << part_name);
try try
{ {
auto storage_lock = owned_storage->lockStructureForShare(false, RWLockImpl::NO_QUERY); auto storage_lock = data.lockStructureForShare(false, RWLockImpl::NO_QUERY);
MergeTreeData::DataPartPtr part = findPart(part_name); MergeTreeData::DataPartPtr part = findPart(part_name);

View File

@ -20,8 +20,8 @@ namespace DataPartsExchange
class Service final : public InterserverIOEndpoint class Service final : public InterserverIOEndpoint
{ {
public: public:
Service(MergeTreeData & data_, StoragePtr & storage_) : data(data_), Service(MergeTreeData & data_)
storage(storage_), log(&Logger::get(data.getLogName() + " (Replicated PartsService)")) {} : data(data_), log(&Logger::get(data.getLogName() + " (Replicated PartsService)")) {}
Service(const Service &) = delete; Service(const Service &) = delete;
Service & operator=(const Service &) = delete; Service & operator=(const Service &) = delete;
@ -33,8 +33,9 @@ private:
MergeTreeData::DataPartPtr findPart(const String & name); MergeTreeData::DataPartPtr findPart(const String & name);
private: private:
/// StorageReplicatedMergeTree::shutdown() waits for all parts exchange handlers to finish,
/// so Service will never access dangling reference to storage
MergeTreeData & data; MergeTreeData & data;
StorageWeakPtr storage;
Logger * log; Logger * log;
}; };

View File

@ -633,6 +633,10 @@ void MergeTreeData::setTTLExpressions(const ColumnsDescription::ColumnTTLs & new
if (new_ttl_table_ast) if (new_ttl_table_ast)
{ {
std::vector<TTLEntry> update_move_ttl_entries;
ASTPtr update_ttl_table_ast = nullptr;
TTLEntry update_ttl_table_entry;
bool seen_delete_ttl = false; bool seen_delete_ttl = false;
for (auto ttl_element_ptr : new_ttl_table_ast->children) for (auto ttl_element_ptr : new_ttl_table_ast->children)
{ {
@ -647,8 +651,8 @@ void MergeTreeData::setTTLExpressions(const ColumnsDescription::ColumnTTLs & new
auto new_ttl_table_entry = create_ttl_entry(ttl_element.children[0]); auto new_ttl_table_entry = create_ttl_entry(ttl_element.children[0]);
if (!only_check) if (!only_check)
{ {
ttl_table_ast = ttl_element.children[0]; update_ttl_table_ast = ttl_element.children[0];
ttl_table_entry = new_ttl_table_entry; update_ttl_table_entry = new_ttl_table_entry;
} }
seen_delete_ttl = true; seen_delete_ttl = true;
@ -670,11 +674,18 @@ void MergeTreeData::setTTLExpressions(const ColumnsDescription::ColumnTTLs & new
throw Exception(message, ErrorCodes::BAD_TTL_EXPRESSION); throw Exception(message, ErrorCodes::BAD_TTL_EXPRESSION);
} }
if (!only_check)
update_move_ttl_entries.emplace_back(std::move(new_ttl_entry));
}
}
if (!only_check) if (!only_check)
{ {
move_ttl_entries.emplace_back(std::move(new_ttl_entry)); ttl_table_entry = update_ttl_table_entry;
} ttl_table_ast = update_ttl_table_ast;
}
auto move_ttl_entries_lock = std::lock_guard<std::mutex>(move_ttl_entries_mutex);
move_ttl_entries = update_move_ttl_entries;
} }
} }
} }
@ -3293,7 +3304,7 @@ ReservationPtr MergeTreeData::tryReserveSpacePreferringTTLRules(UInt64 expected_
ReservationPtr reservation; ReservationPtr reservation;
auto ttl_entry = selectTTLEntryForTTLInfos(ttl_infos, time_of_move); auto ttl_entry = selectTTLEntryForTTLInfos(ttl_infos, time_of_move);
if (ttl_entry != nullptr) if (ttl_entry)
{ {
SpacePtr destination_ptr = ttl_entry->getDestination(storage_policy); SpacePtr destination_ptr = ttl_entry->getDestination(storage_policy);
if (!destination_ptr) if (!destination_ptr)
@ -3352,27 +3363,28 @@ bool MergeTreeData::TTLEntry::isPartInDestination(const StoragePolicyPtr & polic
return false; return false;
} }
const MergeTreeData::TTLEntry * MergeTreeData::selectTTLEntryForTTLInfos( std::optional<MergeTreeData::TTLEntry> MergeTreeData::selectTTLEntryForTTLInfos(
const MergeTreeDataPart::TTLInfos & ttl_infos, const MergeTreeDataPart::TTLInfos & ttl_infos,
time_t time_of_move) const time_t time_of_move) const
{ {
const MergeTreeData::TTLEntry * result = nullptr;
/// Prefer TTL rule which went into action last.
time_t max_max_ttl = 0; time_t max_max_ttl = 0;
std::vector<DB::MergeTreeData::TTLEntry>::const_iterator best_entry_it;
for (const auto & ttl_entry : move_ttl_entries) auto lock = std::lock_guard(move_ttl_entries_mutex);
for (auto ttl_entry_it = move_ttl_entries.begin(); ttl_entry_it != move_ttl_entries.end(); ++ttl_entry_it)
{ {
auto ttl_info_it = ttl_infos.moves_ttl.find(ttl_entry.result_column); auto ttl_info_it = ttl_infos.moves_ttl.find(ttl_entry_it->result_column);
/// Prefer TTL rule which went into action last.
if (ttl_info_it != ttl_infos.moves_ttl.end() if (ttl_info_it != ttl_infos.moves_ttl.end()
&& ttl_info_it->second.max <= time_of_move && ttl_info_it->second.max <= time_of_move
&& max_max_ttl <= ttl_info_it->second.max) && max_max_ttl <= ttl_info_it->second.max)
{ {
result = &ttl_entry; best_entry_it = ttl_entry_it;
max_max_ttl = ttl_info_it->second.max; max_max_ttl = ttl_info_it->second.max;
} }
} }
return result; return max_max_ttl ? *best_entry_it : std::optional<MergeTreeData::TTLEntry>();
} }
MergeTreeData::DataParts MergeTreeData::getDataParts(const DataPartStates & affordable_states) const MergeTreeData::DataParts MergeTreeData::getDataParts(const DataPartStates & affordable_states) const
@ -3773,7 +3785,15 @@ bool MergeTreeData::selectPartsAndMove()
bool MergeTreeData::areBackgroundMovesNeeded() const bool MergeTreeData::areBackgroundMovesNeeded() const
{ {
return storage_policy->getVolumes().size() > 1; auto policy = storage_policy;
if (policy->getVolumes().size() > 1)
return true;
if (policy->getVolumes().size() == 1 && policy->getVolumes()[0]->disks.size() > 1 && move_ttl_entries.size() > 0)
return true;
return false;
} }
bool MergeTreeData::movePartsToSpace(const DataPartsVector & parts, SpacePtr space) bool MergeTreeData::movePartsToSpace(const DataPartsVector & parts, SpacePtr space)

View File

@ -737,12 +737,17 @@ public:
bool isPartInDestination(const StoragePolicyPtr & policy, const MergeTreeDataPart & part) const; bool isPartInDestination(const StoragePolicyPtr & policy, const MergeTreeDataPart & part) const;
}; };
const TTLEntry * selectTTLEntryForTTLInfos(const MergeTreeDataPart::TTLInfos & ttl_infos, time_t time_of_move) const; std::optional<TTLEntry> selectTTLEntryForTTLInfos(const MergeTreeDataPart::TTLInfos & ttl_infos, time_t time_of_move) const;
using TTLEntriesByName = std::unordered_map<String, TTLEntry>; using TTLEntriesByName = std::unordered_map<String, TTLEntry>;
TTLEntriesByName column_ttl_entries_by_name; TTLEntriesByName column_ttl_entries_by_name;
TTLEntry ttl_table_entry; TTLEntry ttl_table_entry;
/// This mutex is required for background move operations which do not obtain global locks.
mutable std::mutex move_ttl_entries_mutex;
/// Vector rw operations have to be done under "move_ttl_entries_mutex".
std::vector<TTLEntry> move_ttl_entries; std::vector<TTLEntry> move_ttl_entries;
String sampling_expr_column_name; String sampling_expr_column_name;

View File

@ -127,14 +127,14 @@ bool MergeTreePartsMover::selectPartsForMove(
if (!can_move(part, &reason)) if (!can_move(part, &reason))
continue; continue;
const MergeTreeData::TTLEntry * ttl_entry_ptr = part->storage.selectTTLEntryForTTLInfos(part->ttl_infos, time_of_move); auto ttl_entry = part->storage.selectTTLEntryForTTLInfos(part->ttl_infos, time_of_move);
auto to_insert = need_to_move.find(part->disk); auto to_insert = need_to_move.find(part->disk);
ReservationPtr reservation; ReservationPtr reservation;
if (ttl_entry_ptr) if (ttl_entry)
{ {
auto destination = ttl_entry_ptr->getDestination(policy); auto destination = ttl_entry->getDestination(policy);
if (destination && !ttl_entry_ptr->isPartInDestination(policy, *part)) if (destination && !ttl_entry->isPartInDestination(policy, *part))
reservation = part->storage.tryReserveSpace(part->bytes_on_disk, ttl_entry_ptr->getDestination(policy)); reservation = part->storage.tryReserveSpace(part->bytes_on_disk, ttl_entry->getDestination(policy));
} }
if (reservation) /// Found reservation by TTL rule. if (reservation) /// Found reservation by TTL rule.
@ -221,7 +221,9 @@ void MergeTreePartsMover::swapClonedPart(const MergeTreeData::DataPartPtr & clon
return; return;
} }
cloned_part->renameTo(active_part->name); /// Don't remove new directory but throw an error because it may contain part which is currently in use.
cloned_part->renameTo(active_part->name, false);
/// TODO what happen if server goes down here? /// TODO what happen if server goes down here?
data->swapActivePart(cloned_part); data->swapActivePart(cloned_part);

View File

@ -99,8 +99,8 @@ void ReplicatedMergeTreeAlterThread::run()
/// Temporarily cancel parts sending /// Temporarily cancel parts sending
ActionLock data_parts_exchange_blocker; ActionLock data_parts_exchange_blocker;
if (storage.data_parts_exchange_endpoint_holder) if (storage.data_parts_exchange_endpoint)
data_parts_exchange_blocker = storage.data_parts_exchange_endpoint_holder->getBlocker().cancel(); data_parts_exchange_blocker = storage.data_parts_exchange_endpoint->blocker.cancel();
/// Temporarily cancel part fetches /// Temporarily cancel part fetches
auto fetches_blocker = storage.fetcher.blocker.cancel(); auto fetches_blocker = storage.fetcher.blocker.cancel();

View File

@ -2931,10 +2931,8 @@ void StorageReplicatedMergeTree::startup()
getStorageID().getFullTableName() + " (ReplicatedMergeTreeQueue)", getStorageID().getFullTableName() + " (ReplicatedMergeTreeQueue)",
getDataParts()); getDataParts());
StoragePtr ptr = shared_from_this(); data_parts_exchange_endpoint = std::make_shared<DataPartsExchange::Service>(*this);
InterserverIOEndpointPtr data_parts_exchange_endpoint = std::make_shared<DataPartsExchange::Service>(*this, ptr); global_context.getInterserverIOHandler().addEndpoint(data_parts_exchange_endpoint->getId(replica_path), data_parts_exchange_endpoint);
data_parts_exchange_endpoint_holder = std::make_shared<InterserverIOEndpointHolder>(
data_parts_exchange_endpoint->getId(replica_path), data_parts_exchange_endpoint, global_context.getInterserverIOHandler());
queue_task_handle = global_context.getBackgroundPool().addTask([this] { return queueTask(); }); queue_task_handle = global_context.getBackgroundPool().addTask([this] { return queueTask(); });
if (areBackgroundMovesNeeded()) if (areBackgroundMovesNeeded())
@ -2966,11 +2964,15 @@ void StorageReplicatedMergeTree::shutdown()
global_context.getBackgroundMovePool().removeTask(move_parts_task_handle); global_context.getBackgroundMovePool().removeTask(move_parts_task_handle);
move_parts_task_handle.reset(); move_parts_task_handle.reset();
if (data_parts_exchange_endpoint_holder) if (data_parts_exchange_endpoint)
{ {
data_parts_exchange_endpoint_holder->getBlocker().cancelForever(); global_context.getInterserverIOHandler().removeEndpointIfExists(data_parts_exchange_endpoint->getId(replica_path));
data_parts_exchange_endpoint_holder = nullptr; /// Ask all parts exchange handlers to finish asap. New ones will fail to start
data_parts_exchange_endpoint->blocker.cancelForever();
/// Wait for all of them
std::unique_lock lock(data_parts_exchange_endpoint->rwlock);
} }
data_parts_exchange_endpoint.reset();
} }
@ -5423,7 +5425,7 @@ ActionLock StorageReplicatedMergeTree::getActionLock(StorageActionBlockType acti
return fetcher.blocker.cancel(); return fetcher.blocker.cancel();
if (action_type == ActionLocks::PartsSend) if (action_type == ActionLocks::PartsSend)
return data_parts_exchange_endpoint_holder ? data_parts_exchange_endpoint_holder->getBlocker().cancel() : ActionLock(); return data_parts_exchange_endpoint ? data_parts_exchange_endpoint->blocker.cancel() : ActionLock();
if (action_type == ActionLocks::ReplicationQueue) if (action_type == ActionLocks::ReplicationQueue)
return queue.actions_blocker.cancel(); return queue.actions_blocker.cancel();

View File

@ -231,7 +231,7 @@ private:
std::atomic<bool> is_leader {false}; std::atomic<bool> is_leader {false};
zkutil::LeaderElectionPtr leader_election; zkutil::LeaderElectionPtr leader_election;
InterserverIOEndpointHolderPtr data_parts_exchange_endpoint_holder; InterserverIOEndpointPtr data_parts_exchange_endpoint;
MergeTreeDataSelectExecutor reader; MergeTreeDataSelectExecutor reader;
MergeTreeDataWriter writer; MergeTreeDataWriter writer;

View File

@ -36,7 +36,6 @@ const char * auto_config_build[]
"USE_INTERNAL_MEMCPY", "@USE_INTERNAL_MEMCPY@", "USE_INTERNAL_MEMCPY", "@USE_INTERNAL_MEMCPY@",
"USE_GLIBC_COMPATIBILITY", "@GLIBC_COMPATIBILITY@", "USE_GLIBC_COMPATIBILITY", "@GLIBC_COMPATIBILITY@",
"USE_JEMALLOC", "@USE_JEMALLOC@", "USE_JEMALLOC", "@USE_JEMALLOC@",
"USE_TCMALLOC", "@USE_TCMALLOC@",
"USE_MIMALLOC", "@USE_MIMALLOC@", "USE_MIMALLOC", "@USE_MIMALLOC@",
"USE_UNWIND", "@USE_UNWIND@", "USE_UNWIND", "@USE_UNWIND@",
"USE_ICU", "@USE_ICU@", "USE_ICU", "@USE_ICU@",
@ -62,6 +61,7 @@ const char * auto_config_build[]
"USE_HYPERSCAN", "@USE_HYPERSCAN@", "USE_HYPERSCAN", "@USE_HYPERSCAN@",
"USE_SIMDJSON", "@USE_SIMDJSON@", "USE_SIMDJSON", "@USE_SIMDJSON@",
"USE_POCO_REDIS", "@USE_POCO_REDIS@", "USE_POCO_REDIS", "@USE_POCO_REDIS@",
"USE_REPLXX", "@USE_REPLXX@",
nullptr, nullptr nullptr, nullptr
}; };

View File

@ -67,5 +67,5 @@ sudo -u clickhouse UBSAN_OPTIONS='print_stacktrace=1' ./clickhouse-ubsan server
# How to use Memory Sanitizer # How to use Memory Sanitizer
``` ```
CC=clang-8 CXX=clang++-8 cmake -D ENABLE_HDFS=0 -D ENABLE_CAPNP=0 -D ENABLE_RDKAFKA=0 -D ENABLE_ICU=0 -D ENABLE_POCO_MONGODB=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_POCO_ODBC=0 -D ENABLE_ODBC=0 -D ENABLE_MYSQL=0 -D ENABLE_EMBEDDED_COMPILER=0 -D USE_INTERNAL_CAPNP_LIBRARY=0 -D USE_SIMDJSON=0 -D ENABLE_READLINE=0 -D SANITIZE=memory .. CC=clang-8 CXX=clang++-8 cmake -D ENABLE_HDFS=0 -D ENABLE_CAPNP=0 -D ENABLE_RDKAFKA=0 -D ENABLE_ICU=0 -D ENABLE_POCO_MONGODB=0 -D ENABLE_POCO_NETSSL=0 -D ENABLE_POCO_ODBC=0 -D ENABLE_ODBC=0 -D ENABLE_MYSQL=0 -D ENABLE_EMBEDDED_COMPILER=0 -D USE_INTERNAL_CAPNP_LIBRARY=0 -D USE_SIMDJSON=0 -D SANITIZE=memory ..
``` ```

View File

@ -501,13 +501,17 @@ def test_moves_after_merges_work(started_cluster, name, engine, positive):
node1.query("DROP TABLE IF EXISTS {}".format(name)) node1.query("DROP TABLE IF EXISTS {}".format(name))
@pytest.mark.parametrize("name,engine,positive", [ @pytest.mark.parametrize("name,engine,positive,bar", [
("mt_test_moves_after_merges_do_not_work","MergeTree()",0), ("mt_test_moves_after_alter_do_not_work","MergeTree()",0,"DELETE"),
("replicated_mt_test_moves_after_merges_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_merges_do_not_work', '1')",0), ("replicated_mt_test_moves_after_alter_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_alter_do_not_work', '1')",0,"DELETE"),
("mt_test_moves_after_merges_work","MergeTree()",1), ("mt_test_moves_after_alter_work","MergeTree()",1,"DELETE"),
("replicated_mt_test_moves_after_merges_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_merges_work', '1')",1), ("replicated_mt_test_moves_after_alter_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_alter_work', '1')",1,"DELETE"),
("mt_test_moves_after_alter_do_not_work","MergeTree()",0,"TO DISK 'external'"),
("replicated_mt_test_moves_after_alter_do_not_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_alter_do_not_work', '1')",0,"TO DISK 'external'"),
("mt_test_moves_after_alter_work","MergeTree()",1,"TO DISK 'external'"),
("replicated_mt_test_moves_after_alter_work","ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_alter_work', '1')",1,"TO DISK 'external'"),
]) ])
def test_ttls_do_not_work_after_alter(started_cluster, name, engine, positive): def test_ttls_do_not_work_after_alter(started_cluster, name, engine, positive, bar):
try: try:
node1.query(""" node1.query("""
CREATE TABLE {name} ( CREATE TABLE {name} (
@ -523,8 +527,8 @@ def test_ttls_do_not_work_after_alter(started_cluster, name, engine, positive):
node1.query(""" node1.query("""
ALTER TABLE {name} ALTER TABLE {name}
MODIFY TTL MODIFY TTL
d1 + INTERVAL 15 MINUTE d1 + INTERVAL 15 MINUTE {bar}
""".format(name=name)) # That shall disable TTL. """.format(name=name, bar=bar)) # That shall disable TTL.
data = [] # 10MB in total data = [] # 10MB in total
for i in range(10): for i in range(10):
@ -620,3 +624,142 @@ limitations under the License."""
finally: finally:
node1.query("DROP TABLE IF EXISTS {name}".format(name=name)) node1.query("DROP TABLE IF EXISTS {name}".format(name=name))
@pytest.mark.parametrize("name,engine", [
("concurrently_altering_ttl_mt","MergeTree()"),
("concurrently_altering_ttl_replicated_mt","ReplicatedMergeTree('/clickhouse/concurrently_altering_ttl_replicated_mt', '1')",),
])
def test_concurrent_alter_with_ttl_move(started_cluster, name, engine):
try:
node1.query("""
CREATE TABLE {name} (
EventDate Date,
number UInt64
) ENGINE = {engine}
ORDER BY tuple()
PARTITION BY toYYYYMM(EventDate)
SETTINGS storage_policy='jbods_with_external'
""".format(name=name, engine=engine))
values = list({ random.randint(1, 1000000) for _ in range(0, 1000) })
def insert(num):
for i in range(num):
day = random.randint(11, 30)
value = values.pop()
month = '0' + str(random.choice([3, 4]))
node1.query("INSERT INTO {} VALUES(toDate('2019-{m}-{d}'), {v})".format(name, m=month, d=day, v=value))
def alter_move(num):
def produce_alter_move(node, name):
move_type = random.choice(["PART", "PARTITION"])
if move_type == "PART":
for _ in range(10):
try:
parts = node1.query("SELECT name from system.parts where table = '{}' and active = 1".format(name)).strip().split('\n')
break
except QueryRuntimeException:
pass
else:
raise Exception("Cannot select from system.parts")
move_part = random.choice(["'" + part + "'" for part in parts])
else:
move_part = random.choice([201903, 201904])
move_disk = random.choice(["DISK", "VOLUME"])
if move_disk == "DISK":
move_volume = random.choice(["'external'", "'jbod1'", "'jbod2'"])
else:
move_volume = random.choice(["'main'", "'external'"])
try:
node1.query("ALTER TABLE {} MOVE {mt} {mp} TO {md} {mv}".format(
name, mt=move_type, mp=move_part, md=move_disk, mv=move_volume))
except QueryRuntimeException as ex:
pass
for i in range(num):
produce_alter_move(node1, name)
def alter_update(num):
for i in range(num):
node1.query("ALTER TABLE {} UPDATE number = number + 1 WHERE 1".format(name))
def alter_modify_ttl(num):
for i in range(num):
ttls = []
for j in range(random.randint(1, 10)):
what = random.choice(["TO VOLUME 'main'", "TO VOLUME 'external'", "TO DISK 'jbod1'", "TO DISK 'jbod2'", "TO DISK 'external'"])
when = "now()+{}".format(random.randint(-1, 5))
ttls.append("{} {}".format(when, what))
node1.query("ALTER TABLE {} MODIFY TTL {}".format(name, ", ".join(ttls)))
def optimize_table(num):
for i in range(num):
node1.query("OPTIMIZE TABLE {} FINAL".format(name))
p = Pool(15)
tasks = []
for i in range(5):
tasks.append(p.apply_async(insert, (100,)))
tasks.append(p.apply_async(alter_move, (100,)))
tasks.append(p.apply_async(alter_update, (100,)))
tasks.append(p.apply_async(alter_modify_ttl, (100,)))
tasks.append(p.apply_async(optimize_table, (100,)))
for task in tasks:
task.get(timeout=60)
assert node1.query("SELECT 1") == "1\n"
assert node1.query("SELECT COUNT() FROM {}".format(name)) == "500\n"
finally:
node1.query("DROP TABLE IF EXISTS {name}".format(name=name))
@pytest.mark.parametrize("name,positive", [
("test_double_move_while_select_negative", 0),
("test_double_move_while_select_positive", 1),
])
def test_double_move_while_select(started_cluster, name, positive):
try:
node1.query("""
CREATE TABLE {name} (
n Int64,
s String
) ENGINE = MergeTree
ORDER BY tuple()
PARTITION BY n
SETTINGS storage_policy='small_jbod_with_external'
""".format(name=name))
node1.query("INSERT INTO {name} VALUES (1, '{string}')".format(name=name, string=get_random_string(10 * 1024 * 1024)))
parts = node1.query("SELECT name FROM system.parts WHERE table = '{name}' AND active = 1".format(name=name)).splitlines()
assert len(parts) == 1
node1.query("ALTER TABLE {name} MOVE PART '{part}' TO DISK 'external'".format(name=name, part=parts[0]))
def long_select():
if positive:
node1.query("SELECT sleep(3), sleep(2), sleep(1), n FROM {name}".format(name=name))
thread = threading.Thread(target=long_select)
thread.start()
node1.query("ALTER TABLE {name} MOVE PART '{part}' TO DISK 'jbod1'".format(name=name, part=parts[0]))
# Fill jbod1 to force ClickHouse to make move of partition 1 to external.
node1.query("INSERT INTO {name} VALUES (2, '{string}')".format(name=name, string=get_random_string(9 * 1024 * 1024)))
node1.query("INSERT INTO {name} VALUES (3, '{string}')".format(name=name, string=get_random_string(9 * 1024 * 1024)))
node1.query("INSERT INTO {name} VALUES (4, '{string}')".format(name=name, string=get_random_string(9 * 1024 * 1024)))
# If SELECT locked old part on external, move shall fail.
assert node1.query("SELECT disk_name FROM system.parts WHERE table = '{name}' AND active = 1 AND name = '{part}'"
.format(name=name, part=parts[0])).splitlines() == ["jbod1" if positive else "external"]
thread.join()
assert node1.query("SELECT n FROM {name} ORDER BY n".format(name=name)).splitlines() == ["1", "2", "3", "4"]
finally:
node1.query("DROP TABLE IF EXISTS {name}".format(name=name))

View File

@ -0,0 +1,27 @@
<test>
<type>once</type>
<stop_conditions>
<any_of>
<!-- This is only for infinite running query. -->
<average_speed_not_changing_for_ms>2000</average_speed_not_changing_for_ms>
<total_time_ms>10000</total_time_ms>
</any_of>
</stop_conditions>
<substitutions>
<substitution>
<name>expr</name>
<values>
<value>number</value>
<value>toUInt32(number)</value>
<value>toUInt16(number)</value>
<value>toUInt8(number)</value>
<value>toInt32(number)</value>
<value>toFloat64(number)</value>
</values>
</substitution>
</substitutions>
<query>SELECT bitCount({expr}) FROM system.numbers</query>
</test>

View File

@ -0,0 +1,56 @@
-- Test simple logic over smaller batch of columns
SELECT
-- x1, x2, x3, x4,
xor(x1, x2, x3, x4) AS xor1,
xor(xor(x1, x2), xor(x3, x4)) AS xor2,
or(x1, x2, x3, x4) AS or1,
or(x1 or x2, x3 or x4) AS or2,
and(x1, x2, x3, x4) AS and1,
and(x1 and x2, x3 and x4) AS and2
FROM (
SELECT
toUInt8(number % 2) AS x1,
toUInt8(number / 2 % 2) AS x2,
toUInt8(number / 4 % 2) AS x3,
toUInt8(number / 8 % 2) AS x4
FROM numbers(16)
)
WHERE
xor1 != xor2 OR (and1 != and2 OR or1 != or2)
;
-- Test simple logic over multiple batches of columns (currently batch spans over 10 columns)
SELECT
-- x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11,
xor(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS xor1,
xor(x1, xor(xor(xor(x2, x3), xor(x4, x5)), xor(xor(x6, x7), xor(x8, xor(x9, xor(x10, x11)))))) AS xor2,
or(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS or1,
or(x1, or(or(or(x2, x3), or(x4, x5)), or(or(x6, x7), or(x8, or(x9, or(x10, x11)))))) AS or2,
and(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS and1,
and(x1, and((x2 and x3) and (x4 and x5), (x6 and x7) and (x8 and (x9 and (x10 and x11))))) AS and2
FROM (
SELECT
toUInt8(number % 2) AS x1,
toUInt8(number / 2 % 2) AS x2,
toUInt8(number / 4 % 2) AS x3,
toUInt8(number / 8 % 2) AS x4,
toUInt8(number / 16 % 2) AS x5,
toUInt8(number / 32 % 2) AS x6,
toUInt8(number / 64 % 2) AS x7,
toUInt8(number / 128 % 2) AS x8,
toUInt8(number / 256 % 2) AS x9,
toUInt8(number / 512 % 2) AS x10,
toUInt8(number / 1024 % 2) AS x11
FROM numbers(2048)
)
WHERE
xor1 != xor2 OR (and1 != and2 OR or1 != or2)
;
SELECT 'OK';

View File

@ -0,0 +1,59 @@
-- Tests codepath for ternary logic
SELECT
-- x1, x2, x3, x4,
xor(x1, x2, x3, x4) AS xor1,
xor(xor(x1, x2), xor(x3, x4)) AS xor2,
or(x1, x2, x3, x4) AS or1,
or(x1 or x2, x3 or x4) AS or2,
and(x1, x2, x3, x4) AS and1,
and(x1 and x2, x3 and x4) AS and2
FROM (
SELECT
nullIf(toUInt8(number % 3), 2) AS x1,
nullIf(toUInt8(number / 3 % 3), 2) AS x2,
nullIf(toUInt8(number / 9 % 3), 2) AS x3,
nullIf(toUInt8(number / 27 % 3), 2) AS x4
FROM numbers(81)
)
WHERE
(xor1 != xor2 OR (xor1 is NULL) != (xor2 is NULL)) OR
(or1 != or2 OR (or1 is NULL) != (or2 is NULL) OR (and1 != and2 OR (and1 is NULL) != (and2 is NULL)))
;
-- Test ternary logic over multiple batches of columns (currently batch spans over 10 columns)
SELECT
-- x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11,
xor(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS xor1,
xor(x1, xor(xor(xor(x2, x3), xor(x4, x5)), xor(xor(x6, x7), xor(x8, xor(x9, xor(x10, x11)))))) AS xor2,
or(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS or1,
or(x1, or(or(or(x2, x3), or(x4, x5)), or(or(x6, x7), or(x8, or(x9, or(x10, x11)))))) AS or2,
and(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11) AS and1,
and(x1, and((x2 and x3) and (x4 and x5), (x6 and x7) and (x8 and (x9 and (x10 and x11))))) AS and2
FROM (
SELECT
nullIf(toUInt8(number % 3), 2) AS x1,
nullIf(toUInt8(number / 3 % 3), 2) AS x2,
nullIf(toUInt8(number / 9 % 3), 2) AS x3,
nullIf(toUInt8(number / 27 % 3), 2) AS x4,
nullIf(toUInt8(number / 81 % 3), 2) AS x5,
nullIf(toUInt8(number / 243 % 3), 2) AS x6,
nullIf(toUInt8(number / 729 % 3), 2) AS x7,
nullIf(toUInt8(number / 2187 % 3), 2) AS x8,
nullIf(toUInt8(number / 6561 % 3), 2) AS x9,
nullIf(toUInt8(number / 19683 % 3), 2) AS x10,
nullIf(toUInt8(number / 59049 % 3), 2) AS x11
FROM numbers(177147)
)
WHERE
(xor1 != xor2 OR (xor1 is NULL) != (xor2 is NULL)) OR
(or1 != or2 OR (or1 is NULL) != (or2 is NULL) OR (and1 != and2 OR (and1 is NULL) != (and2 is NULL)))
;
SELECT 'OK';

View File

@ -1,2 +1 @@
default merge_ab x UInt8 0 0 0 0 0 0 0 default merge_ab x UInt8 0 0 0 0 0 0 0
default as_kafka x UInt8 0 0 0 0 0 0 0

View File

@ -1,26 +1,13 @@
DROP TABLE IF EXISTS merge_a; DROP TABLE IF EXISTS merge_a;
DROP TABLE IF EXISTS merge_b; DROP TABLE IF EXISTS merge_b;
DROP TABLE IF EXISTS merge_ab; DROP TABLE IF EXISTS merge_ab;
DROP TABLE IF EXISTS kafka;
DROP TABLE IF EXISTS as_kafka;
CREATE TABLE merge_a (x UInt8) ENGINE = StripeLog; CREATE TABLE merge_a (x UInt8) ENGINE = StripeLog;
CREATE TABLE merge_b (x UInt8) ENGINE = StripeLog; CREATE TABLE merge_b (x UInt8) ENGINE = StripeLog;
CREATE TABLE merge_ab AS merge(currentDatabase(), '^merge_[ab]$'); CREATE TABLE merge_ab AS merge(currentDatabase(), '^merge_[ab]$');
CREATE TABLE kafka (x UInt8)
ENGINE = Kafka
SETTINGS kafka_broker_list = 'kafka',
kafka_topic_list = 'topic',
kafka_group_name = 'group',
kafka_format = 'CSV';
CREATE TABLE as_kafka AS kafka ENGINE = Memory;
SELECT * FROM system.columns WHERE database = currentDatabase() AND table = 'merge_ab'; SELECT * FROM system.columns WHERE database = currentDatabase() AND table = 'merge_ab';
SELECT * FROM system.columns WHERE database = currentDatabase() AND table = 'as_kafka';
DROP TABLE merge_a; DROP TABLE merge_a;
DROP TABLE merge_b; DROP TABLE merge_b;
DROP TABLE merge_ab; DROP TABLE merge_ab;
DROP TABLE kafka;
DROP TABLE as_kafka;

View File

@ -7,8 +7,8 @@ CLICKHOUSE_CLIENT=`echo ${CLICKHOUSE_CLIENT} | sed 's/'"--send_logs_level=${CLIC
$CLICKHOUSE_CLIENT --query="DROP TABLE IF EXISTS check;" $CLICKHOUSE_CLIENT --query="DROP TABLE IF EXISTS check;"
$CLICKHOUSE_CLIENT --query="CREATE TABLE check (x UInt64, y UInt64 DEFAULT throwIf(x > 10000000)) ENGINE = Memory;" $CLICKHOUSE_CLIENT --query="CREATE TABLE check (x UInt64, y UInt64 DEFAULT throwIf(x > 1500000)) ENGINE = Memory;"
seq 1 11000000 | $CLICKHOUSE_CLIENT --query="INSERT INTO check(x) FORMAT TSV" 2>&1 | grep -q "Value passed to 'throwIf' function is non zero." && echo 'OK' || echo 'FAIL' ||: seq 1 2000000 | $CLICKHOUSE_CLIENT --query="INSERT INTO check(x) FORMAT TSV" 2>&1 | grep -q "Value passed to 'throwIf' function is non zero." && echo 'OK' || echo 'FAIL' ||:
$CLICKHOUSE_CLIENT --query="DROP TABLE check;" $CLICKHOUSE_CLIENT --query="DROP TABLE check;"

View File

@ -0,0 +1,24 @@
[(0,'hello'),(1,'world')]
[(0,'hello'),(1,'world')]
[(0,'hello'),(1,'world')]
[(0,'hello'),(1,'world')]
[(0,'0'),(0,'world')]
[(0,'1'),(1,'world')]
[(0,'2'),(2,'world')]
[(0,'3'),(3,'world')]
[(0,'4'),(4,'world')]
[(0,'5'),(5,'world')]
[(0,'6'),(6,'world')]
[(0,'7'),(7,'world')]
[(0,'8'),(8,'world')]
[(0,'9'),(9,'world')]
[(1,[]),(0,[]),(0,[])]
[(1,[]),(1,[]),(1,[])]
[(1,[]),(2,[]),(4,[])]
[(1,[]),(3,[]),(9,[])]
[(1,[]),(4,[]),(16,[])]
[(1,[]),(5,[]),(25,[])]
[(1,[]),(6,[]),(36,[])]
[(1,[]),(7,[]),(49,[])]
[(1,[]),(8,[]),(64,[])]
[(1,[]),(9,[]),(81,[])]

View File

@ -0,0 +1,7 @@
SELECT arrayZip([0, 1], ['hello', 'world']);
SELECT arrayZip(materialize([0, 1]), ['hello', 'world']);
SELECT arrayZip([0, 1], materialize(['hello', 'world']));
SELECT arrayZip(materialize([0, 1]), materialize(['hello', 'world']));
SELECT arrayZip([0, number], [toString(number), 'world']) FROM numbers(10);
SELECT arrayZip([1, number, number * number], [[], [], []]) FROM numbers(10);

View File

@ -0,0 +1,17 @@
111
1
0.5
0.33
0.25
0.2
0.17
0.14
0.12
0.11
1
-1
2
2
\N
-42
1

View File

@ -0,0 +1,11 @@
SELECT ifNotFinite(round(1 / number, 2), 111) FROM numbers(10);
SELECT ifNotFinite(1, 2);
SELECT ifNotFinite(-1.0, 2);
SELECT ifNotFinite(nan, 2);
SELECT ifNotFinite(-1 / 0, 2);
SELECT ifNotFinite(log(0), NULL);
SELECT ifNotFinite(sqrt(-1), -42);
SELECT ifNotFinite(1234567890123456789, -1234567890123456789); -- { serverError 386 }
SELECT ifNotFinite(NULL, 1);

View File

@ -0,0 +1,21 @@
0
1
1
2
1
2
2
3
1
2
4
0
1
8
64
32
16
8
1 10 000000000000F03F
-1 11 000000000000F0BF
inf 11 000000000000F07F

View File

@ -0,0 +1,13 @@
SELECT bitCount(number) FROM numbers(10);
SELECT avg(bitCount(number)) FROM numbers(256);
SELECT bitCount(0);
SELECT bitCount(1);
SELECT bitCount(-1);
SELECT bitCount(toInt64(-1));
SELECT bitCount(toInt32(-1));
SELECT bitCount(toInt16(-1));
SELECT bitCount(toInt8(-1));
SELECT x, bitCount(x), hex(reinterpretAsString(x)) FROM VALUES ('x Float64', (1), (-1), (inf));

View File

@ -0,0 +1,9 @@
1
\N
\N
\N
\N
---
---
\N
\N

View File

@ -0,0 +1,42 @@
SELECT id
FROM
(
SELECT 1 AS id
UNION ALL
SELECT NULL
UNION ALL
SELECT NULL
)
ALL FULL OUTER JOIN
(
SELECT 1 AS id
UNION ALL
SELECT NULL
UNION ALL
SELECT NULL
) USING (id)
ORDER BY id;
SELECT '---';
SELECT *
FROM
(
SELECT NULL AS x
)
INNER JOIN
(
SELECT NULL AS x
) USING (x);
SELECT '---';
SELECT *
FROM
(
SELECT NULL AS x
)
FULL OUTER JOIN
(
SELECT NULL AS x
) USING (x);

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
((((((((((((((SELECT((((((((((((((((((((((((((((((((1))))))))))))))))))))))))))))))))))))))))))))))

View File

@ -176,7 +176,8 @@ def parse_env_variables(build_type, compiler, sanitizer, package_type, image_typ
result.append("ALIEN_PKGS='" + ' '.join(['--' + pkg for pkg in alien_pkgs]) + "'") result.append("ALIEN_PKGS='" + ' '.join(['--' + pkg for pkg in alien_pkgs]) + "'")
if unbundled: if unbundled:
cmake_flags.append('-DUNBUNDLED=1 -DENABLE_MYSQL=0 -DENABLE_POCO_ODBC=0 -DENABLE_ODBC=0') # TODO: fix build with ENABLE_RDKAFKA
cmake_flags.append('-DUNBUNDLED=1 -DENABLE_MYSQL=0 -DENABLE_POCO_ODBC=0 -DENABLE_ODBC=0 -DENABLE_READLINE=0 -DENABLE_RDKAFKA=0')
if split_binary: if split_binary:
cmake_flags.append('-DUSE_STATIC_LIBRARIES=0 -DSPLIT_SHARED_LIBRARIES=1 -DCLICKHOUSE_SPLIT_BINARY=1') cmake_flags.append('-DUSE_STATIC_LIBRARIES=0 -DSPLIT_SHARED_LIBRARIES=1 -DCLICKHOUSE_SPLIT_BINARY=1')

View File

@ -58,12 +58,6 @@ $ export CC=gcc-9
$ export CXX=g++-9 $ export CXX=g++-9
``` ```
## Install Required Libraries from Packages
```bash
$ sudo apt-get install libreadline-dev
```
## Checkout ClickHouse Sources ## Checkout ClickHouse Sources
```bash ```bash

View File

@ -11,7 +11,7 @@ $ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/inst
## Install Required Compilers, Tools, and Libraries ## Install Required Compilers, Tools, and Libraries
```bash ```bash
$ brew install cmake ninja libtool gettext readline $ brew install cmake ninja libtool gettext
``` ```
## Checkout ClickHouse Sources ## Checkout ClickHouse Sources

View File

@ -4,6 +4,10 @@ Building of ClickHouse is supported on Linux, FreeBSD and Mac OS X.
If you use Windows, you need to create a virtual machine with Ubuntu. To start working with a virtual machine please install VirtualBox. You can download Ubuntu from the website: https://www.ubuntu.com/#download. Please create a virtual machine from the downloaded image (you should reserve at least 4GB of RAM for it). To run a command line terminal in Ubuntu, please locate a program containing the word "terminal" in its name (gnome-terminal, konsole etc.) or just press Ctrl+Alt+T. If you use Windows, you need to create a virtual machine with Ubuntu. To start working with a virtual machine please install VirtualBox. You can download Ubuntu from the website: https://www.ubuntu.com/#download. Please create a virtual machine from the downloaded image (you should reserve at least 4GB of RAM for it). To run a command line terminal in Ubuntu, please locate a program containing the word "terminal" in its name (gnome-terminal, konsole etc.) or just press Ctrl+Alt+T.
# If you use 32-bit system
ClickHouse cannot work or build on 32-bit system. You should acquire access to 64-bit system and you can continue reading.
# Creating a repository on GitHub # Creating a repository on GitHub
@ -96,15 +100,7 @@ Next, check the version of CMake: `cmake --version`. If it is below 3.3, you sho
# Optional External Libraries # Optional External Libraries
ClickHouse uses several external libraries for building. Most of them do not need to be installed separately as they are built together with ClickHouse from the sources located in the submodules. You can check the list in `contrib`. ClickHouse uses several external libraries for building. All of them do not need to be installed separately as they are built together with ClickHouse from the sources located in the submodules. You can check the list in `contrib`.
There is one library that is not built from sources but is supplied by the system: Readline, and thus is recommended to be installed.
Ubuntu: `sudo apt install libreadline-dev`
Mac OS X: `brew install readline`
However, these libraries are optional and ClickHouse can well be built without them. ICU is used for support of `COLLATE` in `ORDER BY` (i.e. for sorting in turkish alphabet). Readline is used for more convenient command input in clickhouse-client.
# C++ Compiler # C++ Compiler

View File

@ -21,11 +21,11 @@ If you use Oracle through the ODBC driver as a source of external dictionaries,
NLS_LANG=RUSSIAN_RUSSIA.UTF8 NLS_LANG=RUSSIAN_RUSSIA.UTF8
``` ```
## How to export data from ClickHouse to the file? ## How Do I Export Data from ClickHouse to a File? {#how-to-export-to-file}
### Using INTO OUTFILE Clause ### Using INTO OUTFILE Clause
Add [INTO OUTFILE](../query_language/select/#into-outfile-clause) clause to your query. Add an [INTO OUTFILE](../query_language/select/#into-outfile-clause) clause to your query.
For example: For example:
@ -41,11 +41,11 @@ For example:
SELECT * FROM table INTO OUTFILE 'file' FORMAT CSV SELECT * FROM table INTO OUTFILE 'file' FORMAT CSV
``` ```
### Using File-engine Table ### Using a File-Engine Table
See [File](../operations/table_engines/file.md). See [File](../operations/table_engines/file.md).
### Using Command-line Redirection ### Using Command-Line Redirection
```sql ```sql
$ clickhouse-client --query "SELECT * from table" > result.txt $ clickhouse-client --query "SELECT * from table" > result.txt

View File

@ -53,7 +53,7 @@ Only a single query is run, so everything after the semicolon is ignored.
You can specify `\G` instead of or after the semicolon. This indicates Vertical format. In this format, each value is printed on a separate line, which is convenient for wide tables. This unusual feature was added for compatibility with the MySQL CLI. You can specify `\G` instead of or after the semicolon. This indicates Vertical format. In this format, each value is printed on a separate line, which is convenient for wide tables. This unusual feature was added for compatibility with the MySQL CLI.
The command line is based on 'readline' (and 'history' or 'libedit', or without a library, depending on the build). In other words, it uses the familiar keyboard shortcuts and keeps a history. The command line is based on 'replxx' (similar to 'readline'). In other words, it uses the familiar keyboard shortcuts and keeps a history.
The history is written to `~/.clickhouse-client-history`. The history is written to `~/.clickhouse-client-history`.
By default, the format used is PrettyCompact. You can change the format in the FORMAT clause of the query, or by specifying `\G` at the end of the query, using the `--format` or `--vertical` argument in the command line, or using the client configuration file. By default, the format used is PrettyCompact. You can change the format in the FORMAT clause of the query, or by specifying `\G` at the end of the query, using the `--format` or `--vertical` argument in the command line, or using the client configuration file.

View File

@ -968,7 +968,7 @@ To exchange data with Hadoop, you can use [HDFS table engine](../operations/tabl
## ORC {#data-format-orc} ## ORC {#data-format-orc}
[Apache ORC](https://orc.apache.org/) is a columnar storage format widespread in the Hadoop ecosystem. ClickHouse supports only read operations for this format. [Apache ORC](https://orc.apache.org/) is a columnar storage format widespread in the Hadoop ecosystem. You can only insert data in this format to ClickHouse.
### Data Types Matching ### Data Types Matching
@ -991,18 +991,18 @@ The table below shows supported data types and how they match ClickHouse [data t
| `STRING`, `BINARY` | [String](../data_types/string.md) | | `STRING`, `BINARY` | [String](../data_types/string.md) |
| `DECIMAL` | [Decimal](../data_types/decimal.md) | | `DECIMAL` | [Decimal](../data_types/decimal.md) |
ClickHouse supports configurable precision of `Decimal` type. The `INSERT` query treats the ORC `DECIMAL` type as the ClickHouse `Decimal128` type. ClickHouse supports configurable precision of the `Decimal` type. The `INSERT` query treats the ORC `DECIMAL` type as the ClickHouse `Decimal128` type.
Unsupported ORC data types: `DATE32`, `TIME32`, `FIXED_SIZE_BINARY`, `JSON`, `UUID`, `ENUM`. Unsupported ORC data types: `DATE32`, `TIME32`, `FIXED_SIZE_BINARY`, `JSON`, `UUID`, `ENUM`.
Data types of a ClickHouse table columns can differ from the corresponding fields of the ORC data inserted. When inserting data, ClickHouse interprets data types according to the table above and then [cast](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) the data to that data type which is set for the ClickHouse table column. The data types of ClickHouse table columns don't have to match the corresponding ORC data fields. When inserting data, ClickHouse interprets data types according to the table above and then [casts](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) the data to the data type set for the ClickHouse table column.
### Inserting Data ### Inserting Data
You can insert Parquet data from a file into ClickHouse table by the following command: You can insert ORC data from a file into ClickHouse table by the following command:
```bash ```bash
$ cat {filename} | clickhouse-client --query="INSERT INTO {some_table} FORMAT ORC" $ cat filename.orc | clickhouse-client --query="INSERT INTO some_table FORMAT ORC"
``` ```
To exchange data with Hadoop, you can use [HDFS table engine](../operations/table_engines/hdfs.md). To exchange data with Hadoop, you can use [HDFS table engine](../operations/table_engines/hdfs.md).

View File

@ -172,7 +172,7 @@ $ echo 'SELECT number FROM numbers LIMIT 10' | curl 'http://localhost:8123/?data
By default, the database that is registered in the server settings is used as the default database. By default, this is the database called 'default'. Alternatively, you can always specify the database using a dot before the table name. By default, the database that is registered in the server settings is used as the default database. By default, this is the database called 'default'. Alternatively, you can always specify the database using a dot before the table name.
The username and password can be indicated in one of two ways: The username and password can be indicated in one of three ways:
1. Using HTTP Basic Authentication. Example: 1. Using HTTP Basic Authentication. Example:
@ -186,6 +186,12 @@ $ echo 'SELECT 1' | curl 'http://user:password@localhost:8123/' -d @-
$ echo 'SELECT 1' | curl 'http://localhost:8123/?user=user&password=password' -d @- $ echo 'SELECT 1' | curl 'http://localhost:8123/?user=user&password=password' -d @-
``` ```
3. Using X-ClickHouse-User and X-ClickHouse-Key headers. Example:
```bash
$ echo 'SELECT 1' | curl -H 'X-ClickHouse-User: user' -H 'X-ClickHouse-Key: password' 'http://localhost:8123/' -d @-
```
If the user name is not specified, the `default` name is used. If the password is not specified, the empty password is used. If the user name is not specified, the `default` name is used. If the password is not specified, the empty password is used.
You can also use the URL parameters to specify any settings for processing a single query, or entire profiles of settings. Example:http://localhost:8123/?profile=web&max_rows_to_read=1000000000&query=SELECT+1 You can also use the URL parameters to specify any settings for processing a single query, or entire profiles of settings. Example:http://localhost:8123/?profile=web&max_rows_to_read=1000000000&query=SELECT+1

View File

@ -0,0 +1,35 @@
# MySQL interface {#mysql_interface}
ClickHouse supports MySQL wire protocol. It can be enabled by [mysql_port](../operations/server_settings/settings.md#server_settings-mysql_port) setting in configuration file:
```xml
<mysql_port>9004</mysql_port>
```
Example of connecting using command-line tool mysql:
```bash
$ mysql --protocol tcp -u default -P 9004
```
Output if connection succeeded:
```text
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 20.2.1.1-ClickHouse
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
```
For compatibility with all MySQL clients, it is recommended to specify user password with [double SHA1](../operations/settings/settings_users.md#password_double_sha1_hex) in configuration file.
If user password is specified using [SHA256](../operations/settings/settings_users.md#password_sha256_hex), some clients won't be able to authenticate (mysqljs and old versions of command-line tool mysql).
Restrictions:
- prepared queries are not supported
- some data types are sent as strings

View File

@ -1,16 +1,14 @@
# How To Test Your Hardware With ClickHouse # How To Test Your Hardware With ClickHouse
Draft.
With this instruction you can run basic ClickHouse performance test on any server without installation of ClickHouse packages. With this instruction you can run basic ClickHouse performance test on any server without installation of ClickHouse packages.
1. Go to "commits" page: https://github.com/ClickHouse/ClickHouse/commits/master \1. Go to "commits" page: [https://github.com/ClickHouse/ClickHouse/commits/master](https://github.com/ClickHouse/ClickHouse/commits/master)
2. Click on the first green check mark or red cross with green "ClickHouse Build Check" and click on the "Details" link near "ClickHouse Build Check". \2. Click on the first green check mark or red cross with green "ClickHouse Build Check" and click on the "Details" link near "ClickHouse Build Check".
3. Copy the link to "clickhouse" binary for amd64 or aarch64. \3. Copy the link to "clickhouse" binary for amd64 or aarch64.
4. ssh to the server and download it with wget: \4. ssh to the server and download it with wget:
``` ```
# For amd64: # For amd64:
wget https://clickhouse-builds.s3.yandex.net/0/00ba767f5d2a929394ea3be193b1f79074a1c4bc/1578163263_binary/clickhouse wget https://clickhouse-builds.s3.yandex.net/0/00ba767f5d2a929394ea3be193b1f79074a1c4bc/1578163263_binary/clickhouse
@ -20,7 +18,7 @@ wget https://clickhouse-builds.s3.yandex.net/0/00ba767f5d2a929394ea3be193b1f7907
chmod a+x clickhouse chmod a+x clickhouse
``` ```
5. Download configs: \5. Download configs:
``` ```
wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/programs/server/config.xml wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/programs/server/config.xml
wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/programs/server/users.xml wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/programs/server/users.xml
@ -29,16 +27,19 @@ wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/program
wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/programs/server/config.d/log_to_console.xml -O config.d/log_to_console.xml wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/programs/server/config.d/log_to_console.xml -O config.d/log_to_console.xml
``` ```
6. Download benchmark files: \6. Download benchmark files:
``` ```
wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/benchmark/clickhouse/benchmark-new.sh wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/benchmark/clickhouse/benchmark-new.sh
chmod a+x benchmark-new.sh chmod a+x benchmark-new.sh
wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/benchmark/clickhouse/queries.sql wget https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/dbms/benchmark/clickhouse/queries.sql
``` ```
7. Download test data: \7. Download test data:
According to the instruction: According to the instruction:
https://clickhouse.yandex/docs/en/getting_started/example_datasets/metrica/
[https://clickhouse.yandex/docs/en/getting_started/example_datasets/metrica/](https://clickhouse.yandex/docs/en/getting_started/example_datasets/metrica/)
("hits" table containing 100 million rows) ("hits" table containing 100 million rows)
``` ```
@ -47,26 +48,29 @@ tar xvf hits_100m_obfuscated_v1.tar.xz -C .
mv hits_100m_obfuscated_v1/* . mv hits_100m_obfuscated_v1/* .
``` ```
8. Run the server: \8. Run the server:
``` ```
./clickhouse server ./clickhouse server
``` ```
9. Check the data: \9. Check the data:
ssh to the server in another terminal ssh to the server in another terminal
``` ```
./clickhouse client --query "SELECT count() FROM hits_100m_obfuscated" ./clickhouse client --query "SELECT count() FROM hits_100m_obfuscated"
100000000 100000000
``` ```
10. Edit the benchmark-new.sh, change "clickhouse-client" to "./clickhouse client" and add "--max_memory_usage 100000000000" parameter. \10. Edit the benchmark-new.sh, change "clickhouse-client" to "./clickhouse client" and add "--max_memory_usage 100000000000" parameter.
``` ```
mcedit benchmark-new.sh mcedit benchmark-new.sh
``` ```
11. Run the benchmark: \11. Run the benchmark:
``` ```
./benchmark-new.sh hits_100m_obfuscated ./benchmark-new.sh hits_100m_obfuscated
``` ```
12. Send the numbers and the info about your hardware configuration to clickhouse-feedback@yandex-team.com \12. Send the numbers and the info about your hardware configuration to clickhouse-feedback@yandex-team.com
All the results are published here: [https://clickhouse.yandex/benchmark_hardware.html](https://clickhouse.yandex/benchmark_hardware.html)

View File

@ -709,6 +709,20 @@ Positive integer.
<tcp_port_secure>9440</tcp_port_secure> <tcp_port_secure>9440</tcp_port_secure>
``` ```
## mysql_port {#server_settings-mysql_port}
Port for communicating with clients over MySQL protocol.
**Possible values**
Positive integer.
Example
```xml
<mysql_port>9004</mysql_port>
```
## tmp_path ## tmp_path
Path to temporary data for processing large queries. Path to temporary data for processing large queries.

View File

@ -33,12 +33,14 @@ Structure of the `users` section:
### user_name/password ### user_name/password
Password could be specified in plaintext or in SHA256 (hex format). Password can be specified in plaintext or in SHA256 (hex format).
- To assign a password in plaintext (**not recommended**), place it in a `password` element. - To assign a password in plaintext (**not recommended**), place it in a `password` element.
For example, `<password>qwerty</password>`. The password can be left blank. For example, `<password>qwerty</password>`. The password can be left blank.
<a id="password_sha256_hex"></a>
- To assign a password using its SHA256 hash, place it in a `password_sha256_hex` element. - To assign a password using its SHA256 hash, place it in a `password_sha256_hex` element.
For example, `<password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>`. For example, `<password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>`.
@ -51,6 +53,19 @@ Password could be specified in plaintext or in SHA256 (hex format).
The first line of the result is the password. The second line is the corresponding SHA256 hash. The first line of the result is the password. The second line is the corresponding SHA256 hash.
<a id="password_double_sha1_hex"></a>
- For compatibility with MySQL clients, password can be specified in double SHA1 hash. Place it in `password_double_sha1_hex` element.
For example, `<password_double_sha1_hex>08b4a0f1de6ad37da17359e592c8d74788a83eb0</password_double_sha1_hex>`.
Example of how to generate a password from shell:
```
PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | openssl dgst -sha1 -binary | openssl dgst -sha1
```
The first line of the result is the password. The second line is the corresponding double SHA1 hash.
### user_name/networks ### user_name/networks

View File

@ -78,6 +78,91 @@ round(3.55, 1) = 3.6
round(3.65, 1) = 3.6 round(3.65, 1) = 3.6
``` ```
**See Also**
- [roundBankers](#roundbankers)
## roundBankers {#roundbankers}
Rounds a number to a specified decimal position.
- If rounding number is a half between two numbers, the function uses banker's rounding.
Banker's rounding is a method of rounding fractional numbers. When the rounding number is a half between two numbers, it is rounded to the nearest even number. E.g. 3.5 rounds up to 4, 2.5 rounds down to 2.
- In other cases function rounds numbers to the nearest integer.
Using banker's rounding, you can reduce the effect of rounding numbers on the result of summing or subtracting these numbers.
For example, sum numbers 1.5, 2.5, 3.5, 4.5 with different rounding:
- No rounding: 1.5 + 2.5 + 3.5 + 4.5 = 12.
- Banker's rounding: 2 + 2 + 4 + 4 = 12.
- Rounding to the nearest integer: 2 + 3 + 4 + 5 = 14.
**Syntax**
```sql
roundBankers(expression [, decimal_places])
```
**Parameters**
- `expression` — A number to be rounded. Can be any [expression](../syntax.md#syntax-expressions) returning the numeric [data type](../../data_types/index.md#data_types).
- `decimal-places` — Decimal places. An integer number.
- `decimal-places > 0` — The function rounds the number to the given position right of the decimal point. E.g. `roundBankers(3.55, 1) = 3.6`.
- `decimal-places < 0` — The function rounds the number to the given position left of the decimal point. E.g. `roundBankers(24.55, -1) = 20`.
- `decimal-places = 0` — The function rounds the number to integer. In this case the argument can be omitted. E.g. `roundBankers(2.5) = 2`.
**Returned value**
A value rounded by banker's rounding method.
### Examples
**Example of use**
Query:
```sql
SELECT number / 2 AS x, roundBankers(x, 0) AS b fROM system.numbers limit 10
```
Result:
```text
┌───x─┬─b─┐
│ 0 │ 0 │
│ 0.5 │ 0 │
│ 1 │ 1 │
│ 1.5 │ 2 │
│ 2 │ 2 │
│ 2.5 │ 2 │
│ 3 │ 3 │
│ 3.5 │ 4 │
│ 4 │ 4 │
│ 4.5 │ 4 │
└─────┴───┘
```
**Examples of Banker's rounding**
```text
roundBankers(0.4) = 0
roundBankers(-3.5) = -4
roundBankers(4.5) = 4
roundBankers(3.55, 1) = 3.6
roundBankers(3.65, 1) = 3.6
roundBankers(10.35, 1) = 10.4
roundBankers(10.755, 2) = 11,76
```
**See Also**
- [round](#rounding_functions-round)
## roundToExp2(num) ## roundToExp2(num)
Accepts a number. If the number is less than one, it returns 0. Otherwise, it rounds the number down to the nearest (whole non-negative) degree of two. Accepts a number. If the number is less than one, it returns 0. Otherwise, it rounds the number down to the nearest (whole non-negative) degree of two.

View File

@ -53,7 +53,7 @@ cat file.csv | clickhouse-client --database=test --query="INSERT INTO test FORMA
شما میتوانید از `\G` به جای سیمی کالن یا بعد از سیمی کالن استفاده کنید. این علامت، فرمت Vertical را نشان می دهد. در این فرمت، هر مقدار در یک خط جدا چاپ می شود که برای جداول عریض مناسب است. این ویژگی غیرمعمول برای سازگاری با MySQL CLI اضافه شد. شما میتوانید از `\G` به جای سیمی کالن یا بعد از سیمی کالن استفاده کنید. این علامت، فرمت Vertical را نشان می دهد. در این فرمت، هر مقدار در یک خط جدا چاپ می شود که برای جداول عریض مناسب است. این ویژگی غیرمعمول برای سازگاری با MySQL CLI اضافه شد.
command line برا پایه 'readline' (و 'history' یا 'libedit'، یه بدون کتابخانه بسته به build) می باشد. به عبارت دیگر، این محیط از shortcut های آشنا استفاده می کند و history دستورات را نگه می دار. history ها در فایل ~/.clickhouse-client-history نوشته می شوند. command line برا پایه 'replxx' می باشد. به عبارت دیگر، این محیط از shortcut های آشنا استفاده می کند و history دستورات را نگه می دار. history ها در فایل ~/.clickhouse-client-history نوشته می شوند.
به صورت پیش فرض فرمت خروجی PrettyCompact می باشد. شما میتوانید از طریق دستور FORMAT در یک query، یا با مشخص کردن `\G` در انتهای query، استفاده از آرگومان های `--format` یا `--vertical` یا از کانفیگ فایل کلاینت، فرمت خروجی را مشخص کنید. به صورت پیش فرض فرمت خروجی PrettyCompact می باشد. شما میتوانید از طریق دستور FORMAT در یک query، یا با مشخص کردن `\G` در انتهای query، استفاده از آرگومان های `--format` یا `--vertical` یا از کانفیگ فایل کلاینت، فرمت خروجی را مشخص کنید.

View File

@ -5,6 +5,10 @@
Если вы используете Windows, вам потребуется создать виртуальную машину с Ubuntu. Для работы с виртуальной машиной, установите VirtualBox. Скачать Ubuntu можно на сайте: https://www.ubuntu.com/#download Создайте виртуальную машину из полученного образа. Выделите для неё не менее 4 GB оперативной памяти. Для запуска терминала в Ubuntu, найдите в меню программу со словом terminal (gnome-terminal, konsole или что-то в этом роде) или нажмите Ctrl+Alt+T. Если вы используете Windows, вам потребуется создать виртуальную машину с Ubuntu. Для работы с виртуальной машиной, установите VirtualBox. Скачать Ubuntu можно на сайте: https://www.ubuntu.com/#download Создайте виртуальную машину из полученного образа. Выделите для неё не менее 4 GB оперативной памяти. Для запуска терминала в Ubuntu, найдите в меню программу со словом terminal (gnome-terminal, konsole или что-то в этом роде) или нажмите Ctrl+Alt+T.
# Если вы используете 32-битную систему
ClickHouse не работает и не собирается на 32-битных системах. Получите доступ к 64-битной системе и продолжайте.
# Создание репозитория на GitHub # Создание репозитория на GitHub
@ -96,15 +100,7 @@ brew install cmake ninja
# Необязательные внешние библиотеки # Необязательные внешние библиотеки
ClickHouse использует для сборки некоторое количество внешних библиотек. Большинство из них не требуется отдельно устанавливать, так как они собираются вместе с ClickHouse, из исходников, которые расположены в submodules. Посмотреть набор этих библиотек можно в директории contrib. ClickHouse использует для сборки некоторое количество внешних библиотек. Но ни одну из них не требуется отдельно устанавливать, так как они собираются вместе с ClickHouse, из исходников, которые расположены в submodules. Посмотреть набор этих библиотек можно в директории contrib.
Одна библиотека не собирается из исходников, а используется из системы: Readline, и её рекомендуется установить.
Ubuntu: `sudo apt install libreadline-dev`
Mac OS X: `brew install readline`
Впрочем, эти библиотеки не обязательны для работы и ClickHouse может быть собран без них. ICU используется для поддержки `COLLATE` в `ORDER BY` (например, для сортировки с учётом турецкого алфавита). Readline используется для более удобного набора команд в интерактивном режиме в clickhouse-client.
# Компилятор C++ # Компилятор C++

View File

@ -16,9 +16,11 @@
Изначально делал [Андрей Чулков](https://github.com/achulkov2), ВШЭ, теперь доделывает [Ольга Хвостикова](https://github.com/stavrolia), но сроки немного сдвинуты из-за задачи 25.9. Будем надеятся на реализацию к концу ноября. Впрочем, [Андрей Чулков](https://github.com/achulkov2) скоро сможет помочь её доделать. Изначально делал [Андрей Чулков](https://github.com/achulkov2), ВШЭ, теперь доделывает [Ольга Хвостикова](https://github.com/stavrolia), но сроки немного сдвинуты из-за задачи 25.9. Будем надеятся на реализацию к концу ноября. Впрочем, [Андрей Чулков](https://github.com/achulkov2) скоро сможет помочь её доделать.
Upd. Доделывать будет другой человек. Хотелось бы в Q1, но приоритет не высокий.
### 1.2. Wait-free каталог баз данных. ### 1.2. Wait-free каталог баз данных.
Делает [Александр Токмаков](https://github.com/tavplubix), первый рабочий вариант в декабре 2019. Нужно для DataLens и Яндекс.Метрики. Q1. Делает [Александр Токмаков](https://github.com/tavplubix), первый рабочий вариант в декабре 2019. Нужно для DataLens и Яндекс.Метрики.
Манипуляции с каталогом баз данных: запросы CREATE TABLE, DROP TABLE, RENAME TABLE и DATABASE, требуют синхронизации с помощью блокировок. Эта синхронизация становится весьма сложной, так как на неё полагается много внутренних структур данных. Манипуляции с каталогом баз данных: запросы CREATE TABLE, DROP TABLE, RENAME TABLE и DATABASE, требуют синхронизации с помощью блокировок. Эта синхронизация становится весьма сложной, так как на неё полагается много внутренних структур данных.
@ -26,7 +28,7 @@
### 1.3. Неблокирующие ALTER. ### 1.3. Неблокирующие ALTER.
И полностью immutable куски. Делает [Александр Сапин](https://github.com/alesapin). Готов приступить к задаче в конце ноября 2019. Нужно для Яндекс.Метрики. Q1. И полностью immutable куски. Делает [Александр Сапин](https://github.com/alesapin). Готов приступить к задаче в конце ноября 2019. Нужно для Яндекс.Метрики.
### 1.4. Нетранзитивные ALTER столбцов. ### 1.4. Нетранзитивные ALTER столбцов.
@ -38,6 +40,8 @@
### 1.6. Полиморфные куски данных. ### 1.6. Полиморфные куски данных.
Компактные куски - Q1, куски в оперативке Q1/Q2.
Делает [Антон Попов](https://github.com/CurtizJ), первый рабочий вариант в декабре. Пререквизит чтобы снизить сложность мелких INSERT, что в свою очередь нужно для 1.12, иначе задача 1.12 не сможет нормально работать. Особенно нужно для Яндекс.Облака. Делает [Антон Попов](https://github.com/CurtizJ), первый рабочий вариант в декабре. Пререквизит чтобы снизить сложность мелких INSERT, что в свою очередь нужно для 1.12, иначе задача 1.12 не сможет нормально работать. Особенно нужно для Яндекс.Облака.
Данные в таблицах типа MergeTree в ClickHouse хранятся в виде набора независимых "кусков". Внутри куска, каждый столбец, а также индекс, хранится в отдельных файлах. Это сделано для возможности быстрых манипуляций со столбцами (пример - запрос ALTER DROP COLUMN). При вставке данных (INSERT), создаётся новый кусок. Для таблиц с большим количеством столбцов, запросы INSERT с маленьким количеством строк являются неэффективными, так как требуют создания большого количества файлов в файловой системе. Это является врождённой особенностью ClickHouse - одной из первой проблем, с которыми сталкиваются пользователи. Пользователям приходится буферизовывать данные и собирать их в более крупные пачки перед вставкой в ClickHouse. Данные в таблицах типа MergeTree в ClickHouse хранятся в виде набора независимых "кусков". Внутри куска, каждый столбец, а также индекс, хранится в отдельных файлах. Это сделано для возможности быстрых манипуляций со столбцами (пример - запрос ALTER DROP COLUMN). При вставке данных (INSERT), создаётся новый кусок. Для таблиц с большим количеством столбцов, запросы INSERT с маленьким количеством строк являются неэффективными, так как требуют создания большого количества файлов в файловой системе. Это является врождённой особенностью ClickHouse - одной из первой проблем, с которыми сталкиваются пользователи. Пользователям приходится буферизовывать данные и собирать их в более крупные пачки перед вставкой в ClickHouse.
@ -54,6 +58,8 @@
Делает [Владимир Чеботарёв](https://github.com/excitoon), Altinity. Декабрь 2019. Делает [Владимир Чеботарёв](https://github.com/excitoon), Altinity. Декабрь 2019.
Q1. Закоммичено, но есть технический долг, который исправляется сейчас.
### 1.9. Использование TTL для прореживания данных. ### 1.9. Использование TTL для прореживания данных.
Будет делать Сорокин Николай, ВШЭ и Яндекс. Будет делать Сорокин Николай, ВШЭ и Яндекс.
@ -86,6 +92,8 @@
### 1.11. Виртуальная файловая система. ### 1.11. Виртуальная файловая система.
Q2.
Нужно для Яндекс.Облака. Делает Александр, Яндекс.Облако, а также Олег Ершов, ВШЭ и Яндекс. Нужно для Яндекс.Облака. Делает Александр, Яндекс.Облако, а также Олег Ершов, ВШЭ и Яндекс.
ClickHouse использует для хранения данных локальную файловую систему. Существует сценарий работы, в котором размещение старых (архивных) данных было бы выгодно на удалённой файловой системе. Если файловая система POSIX совместимая, то это не составляет проблем: ClickHouse успешно работает с Ceph, GlusterFS, MooseFS. Также востребованным является сценарий использования S3 (из-за доступности в облаке) или HDFS (для интеграции с Hadoop). Но эти файловые системы не являются POSIX совместимыми. Хотя для них существуют FUSE драйверы, но скорость работы сильно страдает и поддержка неполная. ClickHouse использует для хранения данных локальную файловую систему. Существует сценарий работы, в котором размещение старых (архивных) данных было бы выгодно на удалённой файловой системе. Если файловая система POSIX совместимая, то это не составляет проблем: ClickHouse успешно работает с Ceph, GlusterFS, MooseFS. Также востребованным является сценарий использования S3 (из-за доступности в облаке) или HDFS (для интеграции с Hadoop). Но эти файловые системы не являются POSIX совместимыми. Хотя для них существуют FUSE драйверы, но скорость работы сильно страдает и поддержка неполная.
@ -94,6 +102,8 @@ ClickHouse использует небольшое подмножество фу
### 1.12. Экспериментальная реализация VFS поверх S3 и HDFS. ### 1.12. Экспериментальная реализация VFS поверх S3 и HDFS.
Q2.
Нужно для Яндекс.Облака. Требует 1.11. Желательно 1.6 и 1.18. Нужно для Яндекс.Облака. Требует 1.11. Желательно 1.6 и 1.18.
Делает Александр, Яндекс.Облако (сначала часть для S3), а также Олег Ершов, ВШЭ и Яндекс. Делает Александр, Яндекс.Облако (сначала часть для S3), а также Олег Ершов, ВШЭ и Яндекс.
@ -103,8 +113,10 @@ ClickHouse использует небольшое подмножество фу
### 1.14. Не писать столбцы, полностью состоящие из нулей. ### 1.14. Не писать столбцы, полностью состоящие из нулей.
Антон Попов. Q1/Q2.
В очереди. Простая задача, является небольшим пререквизитом для потенциальной поддержки полуструктурированных данных. В очереди. Простая задача, является небольшим пререквизитом для потенциальной поддержки полуструктурированных данных.
### 1.15. Возможность иметь разный первичный ключ в разных кусках. ### 1.15. Возможность иметь разный первичный ключ в разных кусках.
Сложная задача, только после 1.3. Сложная задача, только после 1.3.
@ -130,6 +142,9 @@ ClickHouse использует небольшое подмножество фу
Делает [Николай Кочетов](https://github.com/KochetovNicolai). Финальная стадия разработки. Включение по-умолчанию в конце декабря 2019. Удаление старого кода в начале 2020. Делает [Николай Кочетов](https://github.com/KochetovNicolai). Финальная стадия разработки. Включение по-умолчанию в конце декабря 2019. Удаление старого кода в начале 2020.
Upd. На данный момент исправляются проблемы с регрессиями производительности в отдельных случаях. Кажется, что все проблемы исправлены.
Включение по-умолчанию в Q1, но остаётся вторая часть задачи по корректному выделению async части.
### 2.2. Инфраструктура событий/метрик/ограничений/квот/трассировки. ### 2.2. Инфраструктура событий/метрик/ограничений/квот/трассировки.
В очереди. https://gist.github.com/alexey-milovidov/d62d73222d83b9319dc519cbb13aeff6 В очереди. https://gist.github.com/alexey-milovidov/d62d73222d83b9319dc519cbb13aeff6
@ -152,11 +167,11 @@ ClickHouse использует небольшое подмножество фу
### 2.7. Нормализация Context. ### 2.7. Нормализация Context.
В очереди. В очереди. Нужно для YQL.
### 2.8. Декларативный парсер запросов. ### 2.8. Декларативный парсер запросов.
Низкий приоритет. Задачу хочет сделать [Иван Лежанкин](https://github.com/abyss7) в свободное время, но пока ничего нет. Средний приоритет. Нужно для YQL.
### 2.9. Логгировние в format-стиле. ### 2.9. Логгировние в format-стиле.
@ -193,7 +208,7 @@ ClickHouse использует небольшое подмножество фу
### 3.1. Перенос документации по функциям в код. ### 3.1. Перенос документации по функциям в код.
Требует 2.12 и 2.13. Требует 2.12 и 2.13. Хотим в Q1/Q2, средний приоритет.
### 3.2. Перенос однородных частей документации в код. ### 3.2. Перенос однородных частей документации в код.
@ -201,7 +216,7 @@ ClickHouse использует небольшое подмножество фу
### 3.3. Исправить катастрофически отвратительно неприемлемый поиск по документации. ### 3.3. Исправить катастрофически отвратительно неприемлемый поиск по документации.
[Иван Блинков](https://github.com/blinkov/) - очень хороший человек. Сам сайт документации основан на технологиях, не удовлетворяющих требованиям задачи, и эти технологии трудно исправить. [Иван Блинков](https://github.com/blinkov/) - очень хороший человек. Сам сайт документации основан на технологиях, не удовлетворяющих требованиям задачи, и эти технологии трудно исправить. Задачу будет делать первый встретившийся нам frontend разработчик, которого мы сможем заставить это сделать.
### 3.4. + Добавить японский язык в документацию. ### 3.4. + Добавить японский язык в документацию.
@ -212,7 +227,7 @@ ClickHouse использует небольшое подмножество фу
### 4.1. Уменьшение числа потоков при распределённых запросах. ### 4.1. Уменьшение числа потоков при распределённых запросах.
[Никита Лапков](https://github.com/laplab), весна 2020. [Никита Лапков](https://github.com/laplab), весна 2020. Upd. Есть прототип.
### 4.2. Спекулятивное выполнение запросов на нескольких репликах. ### 4.2. Спекулятивное выполнение запросов на нескольких репликах.
@ -231,7 +246,7 @@ ClickHouse использует небольшое подмножество фу
### 4.4. Ограничение сетевой полосы при репликации. ### 4.4. Ограничение сетевой полосы при репликации.
Дмитрий Григорьев, ВШЭ. Дмитрий Григорьев, ВШЭ. Нужно для Метрики.
### 4.5. Возможность продолжить передачу куска данных при репликации после сбоя. ### 4.5. Возможность продолжить передачу куска данных при репликации после сбоя.
@ -251,19 +266,20 @@ ClickHouse использует небольшое подмножество фу
### 5.1. Разделение задач на более мелкие куски в clickhouse-copier. ### 5.1. Разделение задач на более мелкие куски в clickhouse-copier.
Нужно для Метрики, в очереди, но исполнитель не назначен, есть шанс успеть в 2019. Q1. Нужно для Метрики, в очереди. Никита Михайлов.
### 5.2. Автонастройка лимита на оперативку и размера кэшей. ### 5.2. Автонастройка лимита на оперативку и размера кэшей.
### 5.3. Встроенная ручка для Prometheus и, возможно, Solomon. ### 5.3. + Встроенная ручка для Prometheus.
Простая задача. https://github.com/Vdimir Сделано. https://github.com/Vdimir
### 5.4. Opt-in сообщать в клиенте, если вышла новая версия. ### 5.4. Opt-in сообщать в клиенте, если вышла новая версия.
### 5.5. LTS релизы. ### 5.5. + LTS релизы.
Требует 7.5. Задачу хочет Метрика, Облако, БК, Маркет и Altinity. Первой LTS версией уже стала версия 19.14. Требует 7.5. Задачу хочет Метрика, Облако, БК, Маркет и Altinity. Первой LTS версией уже стала версия 19.14.
Метрика, БК, Маркет уже используют более свежие версии чем LTS.
## 6. Инструментирование. ## 6. Инструментирование.
@ -271,15 +287,15 @@ ClickHouse использует небольшое подмножество фу
### 6.1. Исправления сэмплирующего профайлера запросов. ### 6.1. Исправления сэмплирующего профайлера запросов.
Михаил Филимонов, Altinity. Ноябрь 2019. Сделано. Михаил Филимонов, Altinity. Ноябрь 2019. Сделано.
Осталось ещё проверить работоспособность профайлера в первом потоке (что важно для INSERT). Осталось ещё проверить работоспособность профайлера в первом потоке (что важно для INSERT). Иван Лежанкин. Q1.
### 6.2. Добавление memory profiler. ### 6.2. Добавление memory profiler.
Сравнительно простая задача, но только для опытных разработчиков. Нужна всем. Сравнительно простая задача, но только для опытных разработчиков. Нужна всем. Иван Лежанкин. Q1.
### 6.3. Учёт оперативки total расширить не только на запросы. ### 6.3. Учёт оперативки total расширить не только на запросы.
Исправление долгоживущей проблемы с дрифтом учёта оперативки. Нужна для Метрики и БК. Исправление долгоживущей проблемы с дрифтом учёта оперативки. Нужна для Метрики и БК. Иван Лежанкин. Q1.
### 6.4. Поддержка perf events как метрик запроса. ### 6.4. Поддержка perf events как метрик запроса.
@ -291,11 +307,11 @@ ClickHouse использует небольшое подмножество фу
Требует 2.2. Требует 2.2.
### 6.6. Стек трейс для любых исключений. ### 6.6. + Стек трейс для любых исключений.
Сейчас есть стек трейс для почти всех, но не всех исключений. Требует 7.4. Сейчас есть стек трейс для почти всех, но не всех исключений. Требует 7.4.
### 6.7. Таблица system.stack_trace. ### 6.7. + Таблица system.stack_trace.
Сравнительно простая задача, но только для опытных разработчиков. Сравнительно простая задача, но только для опытных разработчиков.
@ -327,7 +343,7 @@ ClickHouse использует небольшое подмножество фу
Сейчас включено только при сборке с clang, но продакшен сборка использует gcc. Сейчас включено только при сборке с clang, но продакшен сборка использует gcc.
Требует 7.2 и, возможно, 7.1 (только в случае новой версии ICU). Требует 7.2 и, возможно, 7.1 (только в случае новой версии ICU).
### 7.5. Начать публиковать LTS релизы. ### 7.5. + Начать публиковать LTS релизы.
[Александр Сапин](https://github.com/alesapin). [Александр Сапин](https://github.com/alesapin).
@ -364,11 +380,17 @@ UBSan включен в функциональных тестах, но не в
При сборке с clang, -Weverything уже включено. Но в gcc есть уникальные warning-и, отсутствующие в clang. При сборке с clang, -Weverything уже включено. Но в gcc есть уникальные warning-и, отсутствующие в clang.
Сделал Wolf Kreuzerkrieg. Сделал Wolf Kreuzerkrieg.
### 7.14. Альтернатива для readline и libedit. ### 7.14. + Альтернатива для readline и libedit.
Тагир Кускаров, ВШЭ. Посмотрим на https://github.com/AmokHuginnsson/replxx Подключение replxx вместо readline сделал Иван Лежанкин.
Для ввода запросов в интерактивном режиме в клиенте командной строки clickhouse-client используется библиотека readline или libedit. ### 7.14.1. Улучшение возможностей интерактивного режима clickhouse-client.
Тагир Кускаров, ВШЭ.
Upd. В рамках данной задачи добавляем подстветку синтаксиса и исправление проблем со вставкой больших запросов.
Для ввода запросов в интерактивном режиме в клиенте командной строки clickhouse-client использовалась библиотека readline или libedit.
Библиотеки readline и libedit обладает следующими недостатками: Библиотеки readline и libedit обладает следующими недостатками:
- (исправлено в новых версиях readline) Очень низкая производительность вставки больших кусков текста. Вставка каждого следующего символа имеет сложность O(n = количество предыдущих символов) и при вставке 1 МБ текста, скорость падает до десятков байт в секунду. - (исправлено в новых версиях readline) Очень низкая производительность вставки больших кусков текста. Вставка каждого следующего символа имеет сложность O(n = количество предыдущих символов) и при вставке 1 МБ текста, скорость падает до десятков байт в секунду.
@ -407,11 +429,13 @@ UBSan включен в функциональных тестах, но не в
Уже давно собираются универсальные tgz пакеты, но по нелепой случайности из них исчез install скрипт. Уже давно собираются универсальные tgz пакеты, но по нелепой случайности из них исчез install скрипт.
[Александр Сапин](https://github.com/alesapin). Может делегировать эту задачу кому угодно. [Александр Сапин](https://github.com/alesapin). Может делегировать эту задачу кому угодно.
Upd. Сделано всё кроме инструкции на сайте. Для этого требуется создать директории testing/stable/prestable на repo.yandex.ru. Внезапно оказалось, что человек, отвечающий за это, в отпуске, и он не отвечает на вопрос, кто его заместитель. Q1.
### 7.18.1. Доделать бинарники под Mac. ### 7.18.1. Доделать бинарники под Mac.
Уже есть автосборка бинарников под Mac на каждый коммит и PR, но с недостатками. Уже есть автосборка бинарников под Mac на каждый коммит и PR, но с недостатками.
[Иван Лежанкин](https://github.com/abyss7). Требует 7.1, 7.2. Рекомендуется 7.14. Сейчас не хватает по крайней мере SSL и ICU. Нужно для Яндекс.Облака. [Иван Лежанкин](https://github.com/abyss7). Требует 7.1, 7.2. Рекомендуется 7.14. Сейчас не хватает по крайней мере SSL и ICU. Нужно для Яндекс.Облака.
Upd. Сделано SSL. Ориентируемся в Q1, но приоритет средний и может потеряться.
### 7.18. Поместить ссылку на собранные бинарники под Mac на сайт. ### 7.18. Поместить ссылку на собранные бинарники под Mac на сайт.
@ -480,6 +504,8 @@ Fuzzing тестирование - это тестирование случай
Также можно сделать функции с детерминированным генератором случайных чисел (аргументом передаётся seed) для воспроизводимости тестовых кейсов. Также можно сделать функции с детерминированным генератором случайных чисел (аргументом передаётся seed) для воспроизводимости тестовых кейсов.
Upd. Сергей Штыков сделал функцию `randomPrintableASCII`.
### 7.24. Fuzzing лексера и парсера запросов; кодеков и форматов. ### 7.24. Fuzzing лексера и парсера запросов; кодеков и форматов.
Андрей Некрашевич, ВШЭ. Андрей Некрашевич, ВШЭ.
@ -496,6 +522,9 @@ Fuzzing тестирование - это тестирование случай
Затем, возможно, [Иван Лежанкин](https://github.com/abyss7). Но сейчас приостановлено, так как Максим из YT должен исправить регрессию производительности в анализе индекса. Затем, возможно, [Иван Лежанкин](https://github.com/abyss7). Но сейчас приостановлено, так как Максим из YT должен исправить регрессию производительности в анализе индекса.
Максим из YT сказал, что сделает это после нового года. Максим из YT сказал, что сделает это после нового года.
Максим из YT сказал, что "мы планируем в январе добиться".
Нужно для CHYT и YQL.
### 7.26. Побайтовая идентичность репозитория с Аркадией. ### 7.26. Побайтовая идентичность репозитория с Аркадией.
@ -529,11 +558,11 @@ Fuzzing тестирование - это тестирование случай
### 7.33. Выкладывать патч релизы в репозиторий автоматически. ### 7.33. Выкладывать патч релизы в репозиторий автоматически.
[Александр Сапин](https://github.com/alesapin). Может делегировать эту задачу кому угодно. В очереди. Иван Лежанкин.
### 7.34. Бэкпортировать bugfix автоматически. ### 7.34. Бэкпортировать bugfix автоматически.
[Александр Сапин](https://github.com/alesapin). Может делегировать эту задачу кому угодно. В очереди. Иван Лежанкин.
### 7.35. Начальные правила для авто-merge. ### 7.35. Начальные правила для авто-merge.
@ -564,6 +593,8 @@ Altinity.
[Ольга Хвостикова](https://github.com/stavrolia). [Ольга Хвостикова](https://github.com/stavrolia).
Уменьшение числа stream-ов сделано, а вот правильная поддержка диапазонов - нет. Будем надеяться на Q1/Q2.
### 8.4. Унификация File, HDFS, S3 под URL. ### 8.4. Унификация File, HDFS, S3 под URL.
### 8.5. + Аутентификация в S3. ### 8.5. + Аутентификация в S3.
@ -579,7 +610,7 @@ Altinity.
В ядрах 2.6 отсутствует один системный вызов, который библиотека hdfs3 использует без необходимости. В ядрах 2.6 отсутствует один системный вызов, который библиотека hdfs3 использует без необходимости.
Сделал Amos Bird. Сделал Amos Bird.
### 8.8. Поддержка виртуальных столбцов с именем файла и путём. ### 8.8. + Поддержка виртуальных столбцов с именем файла и путём.
[Ольга Хвостикова](https://github.com/stavrolia). [Ольга Хвостикова](https://github.com/stavrolia).
@ -611,10 +642,26 @@ Altinity.
### 8.16. Поддержка формата Avro. ### 8.16. Поддержка формата Avro.
Павел Круглов, ВШЭ и Яндекс. Andrew Onyshchuk. Есть pull request. Q1.
Формат Apache Avro является компактным структурированным построчным бинарным форматом данных с внешней схемой. Этот формат часто используется совместно с Kafka и поддержка его в качестве одного из форматов ввода-вывода в ClickHouse является востребованной пользователями. Формат Apache Avro является компактным структурированным построчным бинарным форматом данных с внешней схемой. Этот формат часто используется совместно с Kafka и поддержка его в качестве одного из форматов ввода-вывода в ClickHouse является востребованной пользователями.
### 8.16.1. Поддержка формата JSONEachRow, засунутого в массив.
Павел Круглов, ВШЭ и Яндекс.
### 8.16.2. Поддержка формата Thrift.
Павел Круглов, ВШЭ и Яндекс.
### 8.16.3. Поддержка формата MsgPack.
Павел Круглов, ВШЭ и Яндекс.
### 8.16.4. Формат Regexp.
Павел Круглов, ВШЭ и Яндекс.
### 8.17. ClickHouse как MySQL реплика. ### 8.17. ClickHouse как MySQL реплика.
Ильяс Адюгамов, ВШЭ. Ильяс Адюгамов, ВШЭ.
@ -642,6 +689,7 @@ Maxim Fedotov, Wargaming + Yuri Baranov, Яндекс.
### 8.21. Поддержка произвольного количества языков для имён регионов. ### 8.21. Поддержка произвольного количества языков для имён регионов.
Нужно для БК. Декабрь 2019. Нужно для БК. Декабрь 2019.
В декабре для БК сделан минимальный вариант этой задачи.
### 8.22. Поддержка синтаксиса для переменных в стиле MySQL. ### 8.22. Поддержка синтаксиса для переменных в стиле MySQL.
@ -689,7 +737,7 @@ ClickHouse предоставляет возможность обратитьс
### 10.4. Словарь из YDB (KikiMR). ### 10.4. Словарь из YDB (KikiMR).
Нужно для Метрики, а делать будет таинственный незнакомец из команды KikiMR (под вопросом). Нужно для Метрики, а делать будет таинственный незнакомец из команды KikiMR (под вопросом). Таинственный незнакомец не подтверждает, что он будет делать эту задачу.
### 10.5. Закрытие соединений и уменьшение числа соединений для MySQL и ODBC. ### 10.5. Закрытие соединений и уменьшение числа соединений для MySQL и ODBC.
@ -707,15 +755,15 @@ ClickHouse предоставляет возможность обратитьс
### 10.9. Уменьшение блокировок для cache словарей за счёт одновременных запросов одного и того же. ### 10.9. Уменьшение блокировок для cache словарей за счёт одновременных запросов одного и того же.
Нужно для БК, но мотивация задачи находится под вопросом, так как есть рабочее предположение о том, что данная задача не устраняет причину проблемы. Никита Михайлов. Q1. Нужно для БК, но мотивация задачи находится под вопросом, так как есть рабочее предположение о том, что данная задача не устраняет причину проблемы.
### 10.10. Возможность использования старых значений из cache словаря пока они перезапрашиваются. ### 10.10. Возможность использования старых значений из cache словаря пока они перезапрашиваются.
Нужно для БК и Метрики. Никита Михайлов. Q1. Нужно для БК и Метрики.
### 10.11. Возможность исключительно асинхронных запросов в cache словарях. ### 10.11. Возможность исключительно асинхронных запросов в cache словарях.
Нужно для БК и Метрики. Требует 10.10. Никита Михайлов. Q1. Нужно для БК и Метрики. Требует 10.10.
### 10.12. Layout direct для словарей. ### 10.12. Layout direct для словарей.
@ -731,7 +779,7 @@ ClickHouse предоставляет возможность обратитьс
### 10.16. Словари на локальном SSD. ### 10.16. Словари на локальном SSD.
Никита Васильев, ВШЭ и Яндекс. Никита Васильев, ВШЭ и Яндекс. Есть pull request.
Реализовать в ClickHouse специализированный движок таблиц, подходящий для быстрых key-value запросов и оптимизированный для расположения данных на SSD. Это может быть: реализация на основе RocksDB; сериализованные RowBinary данные с индексом в оперативке; секретная очень эффективная структура данных, о которой я расскажу. Реализовать в ClickHouse специализированный движок таблиц, подходящий для быстрых key-value запросов и оптимизированный для расположения данных на SSD. Это может быть: реализация на основе RocksDB; сериализованные RowBinary данные с индексом в оперативке; секретная очень эффективная структура данных, о которой я расскажу.
@ -778,7 +826,7 @@ ClickHouse предоставляет возможность обратитьс
### 11.10. Преднастроенные HTTP handlers для запросов. ### 11.10. Преднастроенные HTTP handlers для запросов.
zhang2014 zhang2014, есть pull request.
Возможность описать в конфигурационном файле handler (путь в URL) для HTTP запросов к серверу, которому соответствует некоторый параметризованный запрос. Пользователь может вызвать этот обработчик и не должен передавать SQL запрос. Возможность описать в конфигурационном файле handler (путь в URL) для HTTP запросов к серверу, которому соответствует некоторый параметризованный запрос. Пользователь может вызвать этот обработчик и не должен передавать SQL запрос.
@ -787,15 +835,18 @@ zhang2014
### 12.1. Role Based Access Control. ### 12.1. Role Based Access Control.
[Виталий Баранов](https://github.com/vitlibar). Финальная стадия разработки, рабочая версия в декабре 2019. [Виталий Баранов](https://github.com/vitlibar). Финальная стадия разработки, рабочая версия в начале февраля 2019.
Q1. Сейчас сделаны все интерфейсы в коде и запросы, но не сделаны варианты хранения прав кроме прототипа.
### 12.2. Управление пользователями и правами доступа с помощью SQL запросов. ### 12.2. Управление пользователями и правами доступа с помощью SQL запросов.
[Виталий Баранов](https://github.com/vitlibar). Финальная стадия разработки, рабочая версия в декабре 2019. [Виталий Баранов](https://github.com/vitlibar). Финальная стадия разработки, рабочая версия в декабре 2019.
Q1.
### 12.3. Подключение справочника пользователей и прав доступа из LDAP. ### 12.3. Подключение справочника пользователей и прав доступа из LDAP.
[Виталий Баранов](https://github.com/vitlibar). Требует 12.1. [Виталий Баранов](https://github.com/vitlibar). Требует 12.1.
Q1/Q2.
### 12.4. Подключение IDM системы Яндекса как справочника пользователей и прав доступа. ### 12.4. Подключение IDM системы Яндекса как справочника пользователей и прав доступа.
@ -823,6 +874,7 @@ zhang2014
### 13.3. Пулы ресурсов. ### 13.3. Пулы ресурсов.
Требует 13.2 или сможем сделать более неудобную реализацию раньше. Требует 13.2 или сможем сделать более неудобную реализацию раньше.
Обсуждается вариант неудобной реализации. Пока средний приоритет, целимся на Q1/Q2.
## 14. Диалект SQL. ## 14. Диалект SQL.
@ -830,9 +882,12 @@ zhang2014
### 14.1. Исправление семантики CAST для Nullable. ### 14.1. Исправление семантики CAST для Nullable.
Нужно для DataLens. А также для внедрения в BI инструмент Looker. Нужно для DataLens. А также для внедрения в BI инструмент Looker.
Павел Потёмкин, ВШЭ.
### 14.2. Поддержка WITH для подзапросов. ### 14.2. Поддержка WITH для подзапросов.
Павел Потёмкин, ВШЭ.
### 14.3. Поддержка подстановок для множеств в правой части IN. ### 14.3. Поддержка подстановок для множеств в правой части IN.
### 14.4. Поддержка подстановок для идентификаторов (имён) в SQL запросе. ### 14.4. Поддержка подстановок для идентификаторов (имён) в SQL запросе.
@ -845,8 +900,12 @@ zhang2014
### 14.6. Глобальный scope для WITH. ### 14.6. Глобальный scope для WITH.
Павел Потёмкин, ВШЭ.
### 14.7. Nullable для WITH ROLLUP, WITH CUBE, WITH TOTALS. ### 14.7. Nullable для WITH ROLLUP, WITH CUBE, WITH TOTALS.
Павел Потёмкин, ВШЭ.
Простая задача. Простая задача.
### 14.8. Модификаторы DISTINCT, ORDER BY для агрегатных функций. ### 14.8. Модификаторы DISTINCT, ORDER BY для агрегатных функций.
@ -893,18 +952,23 @@ zhang2014.
### 14.18. UNION DISTINCT и возможность включить его по-умолчанию. ### 14.18. UNION DISTINCT и возможность включить его по-умолчанию.
Павел Потёмкин, ВШЭ.
Для BI систем. Для BI систем.
### 14.19. Совместимость парсера типов данных с SQL. ### 14.19. Совместимость парсера типов данных с SQL.
Павел Потёмкин, ВШЭ.
Для BI систем. Для BI систем.
### 14.20. Позиционные аргументы для GROUP BY и ORDER BY. ### 14.20. Позиционные аргументы для GROUP BY и ORDER BY.
Павел Потёмкин, ВШЭ.
Тривиально и используется многими системами, но не входит в стандарт SQL. Тривиально и используется многими системами, но не входит в стандарт SQL.
### 14.21. Приведение типов для IN (подзапрос) и для JOIN. ### 14.21. Приведение типов для IN (подзапрос) и для JOIN.
Павел Потёмкин, ВШЭ.
## 15. Улучшение поддержки JOIN. ## 15. Улучшение поддержки JOIN.
@ -912,6 +976,15 @@ zhang2014.
Артём Зуйков. Сейчас merge JOIN включается вручную опцией и всегда замедляет запросы. Хотим, чтобы он замедлял запросы только когда это неизбежно. Артём Зуйков. Сейчас merge JOIN включается вручную опцией и всегда замедляет запросы. Хотим, чтобы он замедлял запросы только когда это неизбежно.
Кстати, смысл merge JOIN появляется только совместно с 15.2 и 15.3. Кстати, смысл merge JOIN появляется только совместно с 15.2 и 15.3.
Q1.
### 15.1.1. Алгоритм two-level merge JOIN.
Александр Кузьменков. В очереди.
### 15.1.2. Тестирование реализации JOIN в Greenplum.
В очереди.
### 15.2. Прокидывание условий в OUTER JOIN. ### 15.2. Прокидывание условий в OUTER JOIN.
@ -934,7 +1007,7 @@ zhang2014.
## 16. Типы данных и функции. ## 16. Типы данных и функции.
### 16.1. DateTime64. ### 16.1. + DateTime64.
Василий Немков, Altinity, декабрь 2019. Василий Немков, Altinity, декабрь 2019.
@ -964,6 +1037,8 @@ ClickHouse не является geospatial СУБД. Тем не менее, в
Похожая, но более сложная задача, которую ClickHouse пока не умеет решать - определение полигона среди множества полигонов, в которые попадают точки. Для примера: определение района города по географическим координатам. Для решения этой задачи нужно будет реализовать поддержку словарей с полигонами, в которых данные проиндексированы для быстрого поиска. Похожая, но более сложная задача, которую ClickHouse пока не умеет решать - определение полигона среди множества полигонов, в которые попадают точки. Для примера: определение района города по географическим координатам. Для решения этой задачи нужно будет реализовать поддержку словарей с полигонами, в которых данные проиндексированы для быстрого поиска.
Upd. Андрей сделал прототип интерфейса и реализацию-заглушку внутри него.
### 17.2. GIS типы данных и операции. ### 17.2. GIS типы данных и операции.
Алексей Коряков, Алексей Илюхов, ВШЭ, Яндекс.Карты. Алексей Коряков, Алексей Илюхов, ВШЭ, Яндекс.Карты.
@ -1070,13 +1145,15 @@ Hold. Полезно для заказчиков внутри Яндекса, н
Начинал Олег Ершов, доделывает Никита Михайлов, помогает [Александр Кузьменков](https://github.com/akuzm). Готово. Начинал Олег Ершов, доделывает Никита Михайлов, помогает [Александр Кузьменков](https://github.com/akuzm). Готово.
### 21.1.1. Избавление от лишнего копирование при параллельном парсинге форматов, если возможен mmap файла целиком.
### 21.2. Параллельное форматирование форматов. ### 21.2. Параллельное форматирование форматов.
После 21.1, предположительно Никита Михайлов. Задача сильно проще чем 21.1. После 21.1, предположительно Никита Михайлов. Задача сильно проще чем 21.1.
### 21.3. Исправление низкой производительности анализа индекса в случае большого множества в секции IN. ### 21.3. Исправление низкой производительности анализа индекса в случае большого множества в секции IN.
Нужно всем (Zen, БК, DataLens...) Пока ещё не выбран исполнитель. Нужно всем (Zen, БК, DataLens, TestEnv...). Антон Попов, Q1/Q2.
### 21.4. Использование ORDER BY ключа для оптимизации GROUP BY и DISTINCT. ### 21.4. Использование ORDER BY ключа для оптимизации GROUP BY и DISTINCT.
@ -1091,12 +1168,14 @@ Hold. Полезно для заказчиков внутри Яндекса, н
### 21.5. Распараллеливание INSERT при INSERT SELECT, если это необходимо. ### 21.5. Распараллеливание INSERT при INSERT SELECT, если это необходимо.
[Vxider](https://github.com/Vxider), ICT [Vxider](https://github.com/Vxider), ICT
Есть pull request.
### 21.6. Уменьшение числа потоков для SELECT в случае тривиального INSERT SELECT. ### 21.6. Уменьшение числа потоков для SELECT в случае тривиального INSERT SELECT.
### 21.7. Кэш результатов запросов. ### 21.7. Кэш результатов запросов.
[Achimbab](https://github.com/achimbab). [Achimbab](https://github.com/achimbab).
Есть pull request.
### 21.8. Взаимная интеграция аллокатора и кэша. ### 21.8. Взаимная интеграция аллокатора и кэша.
@ -1131,6 +1210,8 @@ Amos Bird.
- Вынесение любых функций наружу any, anyLast. - Вынесение любых функций наружу any, anyLast.
- При GROUP BY по transform или if по строкам, замена строк на Enum. - При GROUP BY по transform или if по строкам, замена строк на Enum.
Сделана замена цепочек if на multiIf, но внезапно оказалось, что это является не оптимизацией, а наоборот.
### 21.12. Алгебраические оптимизации запросов. ### 21.12. Алгебраические оптимизации запросов.
Руслан Камалов, Михаил Малафеев, Виктор Гришанин, ВШЭ Руслан Камалов, Михаил Малафеев, Виктор Гришанин, ВШЭ
@ -1200,9 +1281,9 @@ Constraints позволяют задать выражение, истиннос
В ByteDance есть готовая реализация, но они её боятся из-за, возможно, низкого качества кода. В ByteDance есть готовая реализация, но они её боятся из-за, возможно, низкого качества кода.
### 21.21. Чтение больших файлов с помощью mmap. ### 21.21. + Чтение больших файлов с помощью mmap.
Тривиально, почти всё готово. Сделан вариант, но достаточно топорный. Без тестирования в продакшене включать по-умолчанию нельзя.
### 21.22. Userspace page cache. ### 21.22. Userspace page cache.
@ -1211,6 +1292,7 @@ Constraints позволяют задать выражение, истиннос
### 21.23. Ускорение работы с вторичными индексами. ### 21.23. Ускорение работы с вторичными индексами.
zhang2014. zhang2014.
Есть pull request.
## 22. Долги и недоделанные возможности. ## 22. Долги и недоделанные возможности.
@ -1219,9 +1301,9 @@ zhang2014.
Нужно для Яндекс.Облака. Сделал Алексей Миловидов. Нужно для Яндекс.Облака. Сделал Алексей Миловидов.
### 22.2. Убрать возможность изменить настройки в native протоколе в случае readonly. ### 22.2. + Убрать возможность изменить настройки в native протоколе в случае readonly.
Алексей Миловидов или [Виталий Баранов](https://github.com/vitlibar). N.Vartolomei.
### 22.3. Защита от абсурдно заданных пользователем кодеков. ### 22.3. Защита от абсурдно заданных пользователем кодеков.
@ -1229,25 +1311,27 @@ zhang2014.
### 22.4. Исправление оставшихся deadlocks в табличных RWLock-ах. ### 22.4. Исправление оставшихся deadlocks в табличных RWLock-ах.
Александр Казаков. Нужно для Яндекс.Метрики и Datalens. Александр Казаков. Нужно для Яндекс.Метрики и Datalens. Задача постепенно тащится и исправлениями в соседних местах стала менее актуальна.
В Q1 будет сделана или отменена с учётом 1.2. и 1.3.
### 22.5. Исправление редких срабатываний TSan в stress тестах в CI. ### 22.5. + Исправление редких срабатываний TSan в stress тестах в CI.
Александр Казаков. Александр Казаков.
### 22.6. Изменение только DEFAULT в ALTER TABLE может поменять тип столбца. ### 22.6. Изменение только DEFAULT в ALTER TABLE может поменять тип столбца.
### 22.7. Row-Level Security не работает в случае наличия в запросе IN подзапросов. ### 22.7. + Row-Level Security не работает в случае наличия в запросе IN подзапросов.
[Виталий Баранов](https://github.com/vitlibar). Нужно для Метрики. Нужно для Метрики. Иван Лежанкин.
### 22.8. Исправить десериализацию параметров для параметризованных запросов. ### 22.8. + Исправить десериализацию параметров для параметризованных запросов.
Хотел исправить Василий Немков, Altinity, но есть маленькие затруднения, наверное переделает Алексей Миловидов. Хотел исправить Василий Немков, Altinity, но есть маленькие затруднения, наверное переделает Алексей Миловидов.
### 22.9. Разобраться с десериализацией массивов со значениями по-умолчанию в Protobuf формате в случае protobuf 3. ### 22.9. Разобраться с десериализацией массивов со значениями по-умолчанию в Protobuf формате в случае protobuf 3.
[Виталий Баранов](https://github.com/vitlibar). Возможно, это - фундаментальная проблема и следует её только документировать. [Виталий Баранов](https://github.com/vitlibar). Возможно, это - фундаментальная проблема и следует её только документировать.
Кажется, отменяем, но пока ещё не всё ясно.
### 22.10. Исправление дрифта при отслеживании потребления памяти запросами. ### 22.10. Исправление дрифта при отслеживании потребления памяти запросами.
@ -1278,11 +1362,10 @@ zhang2014.
Altinity. Altinity.
### 22.16. Исправление низкой производительности кодека DoubleDelta. ### 22.16. + Исправление низкой производительности кодека DoubleDelta.
Василий Немков, Altinity - в процессе. Василий Немков, Altinity - в процессе.
Можно считать, что сделано, хотя отсутствие SIMD оптимизаций для variable length кодеков - это ужасно.
Мы считаем важным, что код в ClickHouse содержит разумные оптимизации, основанные на анализе производительности. Но иногда бывают досадные исключения.
### 22.17. Консистентно работающий POPULATE для MaterializedView. ### 22.17. Консистентно работающий POPULATE для MaterializedView.
@ -1331,14 +1414,14 @@ https://github.com/ClickHouse/ClickHouse/issues/2655
Altinity. Altinity.
### 22.29. Уязвимость DDL для словарей executable. ### 22.29. + Уязвимость DDL для словарей executable.
[Александр Сапин](https://github.com/alesapin) [Александр Сапин](https://github.com/alesapin)
## 23. Default Festival. ## 23. Default Festival.
### 23.1. Включение minimalistic_part_header в ZooKeeper. ### 23.1. + Включение minimalistic_part_header в ZooKeeper.
Сильно уменьшает объём данных в ZooKeeper. Уже год в продакшене в Яндекс.Метрике. Сильно уменьшает объём данных в ZooKeeper. Уже год в продакшене в Яндекс.Метрике.
Алексей Миловидов, ноябрь 2019. Алексей Миловидов, ноябрь 2019.
@ -1367,13 +1450,13 @@ Altinity.
Просто аккуратно включить. Просто аккуратно включить.
### 23.8. Включение оптимизации VALUES. ### 23.8. + Включение оптимизации VALUES.
Просто аккуратно включить. Просто аккуратно включить.
### 23.9. Включение Processors. ### 23.9. Включение Processors.
[Николай Кочетов](https://github.com/KochetovNicolai). Q1. [Николай Кочетов](https://github.com/KochetovNicolai).
### 23.10. Включение mlock бинарника. ### 23.10. Включение mlock бинарника.
@ -1578,12 +1661,16 @@ ucasFL, ICT.
Алгоритмы min-hash и sim-hash позволяют вычислить для текста несколько хэш-значений таких, что при небольшом изменении текста, по крайней мере один из хэшей не меняется. Вычисления можно реализовать на n-грамах и словарных шинглах. Предлагается добавить поддержку этих алгоритмов в виде функций в ClickHouse и изучить их применимость для задачи нечёткого поиска полудубликатов. Алгоритмы min-hash и sim-hash позволяют вычислить для текста несколько хэш-значений таких, что при небольшом изменении текста, по крайней мере один из хэшей не меняется. Вычисления можно реализовать на n-грамах и словарных шинглах. Предлагается добавить поддержку этих алгоритмов в виде функций в ClickHouse и изучить их применимость для задачи нечёткого поиска полудубликатов.
Есть pull request, есть что доделывать.
### 24.28. Другой sketch для квантилей. ### 24.28. Другой sketch для квантилей.
Похоже на quantileTiming, но с логарифмическими корзинами. Похоже на quantileTiming, но с логарифмическими корзинами. См. DDSketch.
### 24.29. Поддержка Arrow Flight. ### 24.29. Поддержка Arrow Flight.
Жанна Зосимова, ВШЭ.
### 24.30. ClickHouse как графовая СУБД. ### 24.30. ClickHouse как графовая СУБД.
Amos Bird, но его решение слишком громоздкое и пока не open-source. Amos Bird, но его решение слишком громоздкое и пока не open-source.
@ -1613,7 +1700,7 @@ Amos Bird, но его решение слишком громоздкое и п
### 25.2. Вычитка и выкладка статьи про обфускацию данных на английском. ### 25.2. Вычитка и выкладка статьи про обфускацию данных на английском.
Эми, Александр Казаков, Алексей Миловидов, ноябрь 2019. Эми, Александр Казаков, Алексей Миловидов, Q1.
Готово к выкладке. Готово к выкладке.
### 25.3. Подготовка статьи "Секреты оптимизации производительности ClickHouse". ### 25.3. Подготовка статьи "Секреты оптимизации производительности ClickHouse".
@ -1640,9 +1727,9 @@ Amos Bird, но его решение слишком громоздкое и п
### 25.9. Подготовка докладчиков: khvostikao, ilezhankin, nikitamikhailov, akuzm и другие. ### 25.9. Подготовка докладчиков: khvostikao, ilezhankin, nikitamikhailov, akuzm и другие.
[Ольга Хвостикова](https://github.com/stavrolia), [Иван Лежанкин](https://github.com/abyss7), Никита Михайлов, [Александр Кузьменков](https://github.com/akuzm). [Ольга Хвостикова](https://github.com/stavrolia), [Иван Лежанкин](https://github.com/abyss7), Никита Михайлов, [Александр Кузьменков](https://github.com/akuzm), Артём Зуйков.
Уже готовые докладчики: Алексей Миловидов, [Николай Кочетов](https://github.com/KochetovNicolai), [Александр Сапин](https://github.com/alesapin). Уже готовые докладчики: Алексей Миловидов, [Николай Кочетов](https://github.com/KochetovNicolai), [Александр Сапин](https://github.com/alesapin).
Получаем минимум 7 докладчиков в 2020 году. Получаем минимум 8 докладчиков в 2020 году.
### 25.10. Митапы в России и Беларуси: Москва x2 + митап для разработчиков или хакатон, Санкт-Петербург, Минск, Нижний Новгород, Екатеринбург, Новосибирск и/или Академгородок, Иннополис или Казань. ### 25.10. Митапы в России и Беларуси: Москва x2 + митап для разработчиков или хакатон, Санкт-Петербург, Минск, Нижний Новгород, Екатеринбург, Новосибирск и/или Академгородок, Иннополис или Казань.
@ -1650,7 +1737,7 @@ Amos Bird, но его решение слишком громоздкое и п
### 25.11. Митапы зарубежные: восток США (Нью Йорк, возможно Raleigh), возможно северо-запад (Сиэтл), Китай (Пекин снова, возможно митап для разработчиков или хакатон), Лондон. ### 25.11. Митапы зарубежные: восток США (Нью Йорк, возможно Raleigh), возможно северо-запад (Сиэтл), Китай (Пекин снова, возможно митап для разработчиков или хакатон), Лондон.
[Иван Блинков](https://github.com/blinkov/) - организация [Иван Блинков](https://github.com/blinkov/) - организация. Две штуки в США запланированы.
### 25.12. Статья "научная" - про устройство хранения данных и индексов или whitepaper по архитектуре. Есть вариант подать на VLDB. ### 25.12. Статья "научная" - про устройство хранения данных и индексов или whitepaper по архитектуре. Есть вариант подать на VLDB.

View File

@ -21,4 +21,37 @@
NLS_LANG=RUSSIAN_RUSSIA.UTF8 NLS_LANG=RUSSIAN_RUSSIA.UTF8
``` ```
## Как экспортировать данные из ClickHouse в файл? {#how-to-export-to-file}
### Секция INTO OUTFILE
Добавьте секцию [INTO OUTFILE](../query_language/select/#into-outfile-clause) к своему запросу.
Например:
```sql
SELECT * FROM table INTO OUTFILE 'file'
```
По умолчанию, для выдачи данных ClickHouse использует формат [TabSeparated](../interfaces/formats.md#tabseparated). Чтобы выбрать [формат данных](../interfaces/formats.md), используйте [секцию FORMAT](../query_language/select/#format-clause).
Например:
```sql
SELECT * FROM table INTO OUTFILE 'file' FORMAT CSV
```
### Таблица с движком File
Смотрите [File](../operations/table_engines/file.md).
### Перенаправление в командой строке
```sql
$ clickhouse-client --query "SELECT * from table" > result.txt
```
Смотрите [clickhouse-client](../interfaces/cli.md).
[Оригинальная статья ](https://clickhouse.yandex/docs/en/faq/general/) <!--hide--> [Оригинальная статья ](https://clickhouse.yandex/docs/en/faq/general/) <!--hide-->

View File

@ -28,6 +28,7 @@ ClickHouse может принимать (`INSERT`) и отдавать (`SELECT
| [PrettySpace](#prettyspace) | ✗ | ✔ | | [PrettySpace](#prettyspace) | ✗ | ✔ |
| [Protobuf](#protobuf) | ✔ | ✔ | | [Protobuf](#protobuf) | ✔ | ✔ |
| [Parquet](#data-format-parquet) | ✔ | ✔ | | [Parquet](#data-format-parquet) | ✔ | ✔ |
| [ORC](#data-format-orc) | ✔ | ✗ |
| [RowBinary](#rowbinary) | ✔ | ✔ | | [RowBinary](#rowbinary) | ✔ | ✔ |
| [RowBinaryWithNamesAndTypes](#rowbinarywithnamesandtypes) | ✔ | ✔ | | [RowBinaryWithNamesAndTypes](#rowbinarywithnamesandtypes) | ✔ | ✔ |
| [Native](#native) | ✔ | ✔ | | [Native](#native) | ✔ | ✔ |
@ -941,7 +942,7 @@ ClickHouse поддерживает настраиваемую точность
Типы данных столбцов в ClickHouse могут отличаться от типов данных соответствующих полей файла в формате Parquet. При вставке данных, ClickHouse интерпретирует типы данных в соответствии с таблицей выше, а затем [приводит](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) данные к тому типу, который установлен для столбца таблицы. Типы данных столбцов в ClickHouse могут отличаться от типов данных соответствующих полей файла в формате Parquet. При вставке данных, ClickHouse интерпретирует типы данных в соответствии с таблицей выше, а затем [приводит](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) данные к тому типу, который установлен для столбца таблицы.
### Inserting and Selecting Data ### Вставка и выборка данных
Чтобы вставить в ClickHouse данные из файла в формате Parquet, выполните команду следующего вида: Чтобы вставить в ClickHouse данные из файла в формате Parquet, выполните команду следующего вида:
@ -955,7 +956,49 @@ $ cat {filename} | clickhouse-client --query="INSERT INTO {some_table} FORMAT Pa
$ clickhouse-client --query="SELECT * FROM {some_table} FORMAT Parquet" > {some_file.pq} $ clickhouse-client --query="SELECT * FROM {some_table} FORMAT Parquet" > {some_file.pq}
``` ```
Для обмена данными с экосистемой Hadoop можно использовать движки таблиц [HDFS](../operations/table_engines/hdfs.md) и `URL`. Для обмена данными с экосистемой Hadoop можно использовать движки таблиц [HDFS](../operations/table_engines/hdfs.md).
## ORC {#data-format-orc}
[Apache ORC](https://orc.apache.org/) - это column-oriented формат данных, распространённый в экосистеме Hadoop. Вы можете только вставлять данные этого формата в ClickHouse.
### Соответствие типов данных
Таблица показывает поддержанные типы данных и их соответствие [типам данных](../data_types/index.md) ClickHouse для запросов `INSERT`.
| Тип данных ORC (`INSERT`) | Тип данных ClickHouse |
| -------------------- | ------------------ |
| `UINT8`, `BOOL` | [UInt8](../data_types/int_uint.md) |
| `INT8` | [Int8](../data_types/int_uint.md) |
| `UINT16` | [UInt16](../data_types/int_uint.md) |
| `INT16` | [Int16](../data_types/int_uint.md) |
| `UINT32` | [UInt32](../data_types/int_uint.md) |
| `INT32` | [Int32](../data_types/int_uint.md) |
| `UINT64` | [UInt64](../data_types/int_uint.md) |
| `INT64` | [Int64](../data_types/int_uint.md) |
| `FLOAT`, `HALF_FLOAT` | [Float32](../data_types/float.md) |
| `DOUBLE` | [Float64](../data_types/float.md) |
| `DATE32` | [Date](../data_types/date.md) |
| `DATE64`, `TIMESTAMP` | [DateTime](../data_types/datetime.md) |
| `STRING`, `BINARY` | [String](../data_types/string.md) |
| `DECIMAL` | [Decimal](../data_types/decimal.md) |
ClickHouse поддерживает настраиваемую точность для формата `Decimal`. При обработке запроса `INSERT`, ClickHouse обрабатывает тип данных Parquet `DECIMAL` как `Decimal128`.
Неподдержанные типы данных ORC: `DATE32`, `TIME32`, `FIXED_SIZE_BINARY`, `JSON`, `UUID`, `ENUM`.
Типы данных столбцов в таблицах ClickHouse могут отличаться от типов данных для соответствующих полей ORC. При вставке данных, ClickHouse интерпретирует типы данных ORC согласно таблице соответствия, а затем [приводит](../query_language/functions/type_conversion_functions/#type_conversion_function-cast) данные к типу, установленному для столбца таблицы ClickHouse.
### Вставка данных
Данные ORC можно вставить в таблицу ClickHouse командой:
```bash
$ cat filename.orc | clickhouse-client --query="INSERT INTO some_table FORMAT ORC"
```
Для обмена данных с Hadoop можно использовать [движок таблиц HDFS](../operations/table_engines/hdfs.md).
## Схема формата {#formatschema} ## Схема формата {#formatschema}

View File

@ -173,7 +173,7 @@ $ echo 'SELECT number FROM numbers LIMIT 10' | curl 'http://localhost:8123/?data
По умолчанию используется БД, которая прописана в настройках сервера, как БД по умолчанию. По умолчанию, это - БД default. Также вы всегда можете указать БД через точку перед именем таблицы. По умолчанию используется БД, которая прописана в настройках сервера, как БД по умолчанию. По умолчанию, это - БД default. Также вы всегда можете указать БД через точку перед именем таблицы.
Имя пользователя и пароль могут быть указаны в одном из двух вариантов: Имя пользователя и пароль могут быть указаны в одном из трёх вариантов:
1. С использованием HTTP Basic Authentication. Пример: 1. С использованием HTTP Basic Authentication. Пример:
@ -187,6 +187,12 @@ $ echo 'SELECT 1' | curl 'http://user:password@localhost:8123/' -d @-
$ echo 'SELECT 1' | curl 'http://localhost:8123/?user=user&password=password' -d @- $ echo 'SELECT 1' | curl 'http://localhost:8123/?user=user&password=password' -d @-
``` ```
3. С использованием заголовков X-ClickHouse-User и X-ClickHouse-Key. Пример:
```bash
$ echo 'SELECT 1' | curl -H 'X-ClickHouse-User: user' -H 'X-ClickHouse-Key: password' 'http://localhost:8123/' -d @-
```
Если пользователь не задан,то используется `default`. Если пароль не задан, то используется пустой пароль. Если пользователь не задан,то используется `default`. Если пароль не задан, то используется пустой пароль.
Также в параметрах URL вы можете указать любые настройки, которые будут использованы для обработки одного запроса, или целые профили настроек. Пример:http://localhost:8123/?profile=web&max_rows_to_read=1000000000&query=SELECT+1 Также в параметрах URL вы можете указать любые настройки, которые будут использованы для обработки одного запроса, или целые профили настроек. Пример:http://localhost:8123/?profile=web&max_rows_to_read=1000000000&query=SELECT+1

View File

@ -0,0 +1,35 @@
# MySQL-интерфейс {#mysql_interface}
ClickHouse поддерживает взаимодействие по протоколу MySQL. Данная функция включается настройкой [mysql_port](../operations/server_settings/settings.md#server_settings-mysql_port) в конфигурационном файле:
```xml
<mysql_port>9004</mysql_port>
```
Пример подключения с помощью стандартного клиента mysql:
```bash
$ mysql --protocol tcp -u default -P 9004
```
Вывод в случае успешного подключения:
```text
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 4
Server version: 20.2.1.1-ClickHouse
Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
```
Для совместимости со всеми клиентами рекомендуется задавать пароль пользователя в конфигурационном файле с помощью двойного хэша [SHA1](../operations/settings/settings_users.md#password_double_sha1_hex).
В случае указания пароля с помощью [SHA256](../operations/settings/settings_users.md#password_sha256_hex) некоторые клиенты не смогут пройти аутентификацию (mysqljs и старые версии стандартного клиента mysql).
Ограничения:
- не поддерживаются подготовленные запросы
- некоторые типы данных отправляются как строки

View File

@ -661,6 +661,16 @@ TCP порт для защищённого обмена данными с кли
<tcp_port_secure>9440</tcp_port_secure> <tcp_port_secure>9440</tcp_port_secure>
``` ```
## mysql_port {#server_settings-mysql_port}
Порт для взаимодействия с клиентами по протоколу MySQL.
Пример
```xml
<mysql_port>9004</mysql_port>
```
## tmp_path ## tmp_path
Путь ко временным данным для обработки больших запросов. Путь ко временным данным для обработки больших запросов.

View File

@ -39,6 +39,8 @@
Например, `<password>qwerty</password>`. Пароль можно оставить пустым. Например, `<password>qwerty</password>`. Пароль можно оставить пустым.
<a id="password_sha256_hex"></a>
- Чтобы назначить пароль в виде SHA256, поместите хэш в элемент `password_sha256_hex`. - Чтобы назначить пароль в виде SHA256, поместите хэш в элемент `password_sha256_hex`.
Например, `<password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>`. Например, `<password_sha256_hex>65e84be33532fb784c48129675f9eff3a682b27168c0ea744b2cf58ee02337c5</password_sha256_hex>`.
@ -51,6 +53,20 @@
Первая строка результата — пароль. Вторая строка — соответствующий ему хэш SHA256. Первая строка результата — пароль. Вторая строка — соответствующий ему хэш SHA256.
<a id="password_double_sha1_hex"></a>
- Для совместимости с клиентами MySQL, пароль можно задать с помощью двойного хэша SHA1, поместив его в элемент `password_double_sha1_hex`.
Например, `<password_double_sha1_hex>08b4a0f1de6ad37da17359e592c8d74788a83eb0</password_double_sha1_hex>`.
Пример создания пароля в командной строке:
```
PASSWORD=$(base64 < /dev/urandom | head -c8); echo "$PASSWORD"; echo -n "$PASSWORD" | openssl dgst -sha1 -binary | openssl dgst -sha1
```
Первая строка результата — пароль. Вторая строка — соответствующий ему двойной хэш SHA1.
### user_name/networks ### user_name/networks
Список сетей, из которых пользователь может подключиться к серверу ClickHouse. Список сетей, из которых пользователь может подключиться к серверу ClickHouse.

View File

@ -75,6 +75,91 @@ round(3.55, 1) = 3.6
round(3.65, 1) = 3.6 round(3.65, 1) = 3.6
``` ```
**См. также**
- [roundBankers](#roundbankers)
## roundBankers {#roundbankers}
Округляет число до указанного десятичного разряда.
- Если округляемое число равноудалено от соседних чисел, то используется банковское округление.
Банковское округление (англ. banker's rounding) — метод округления дробных чисел. Если округляемое число равноудалёно от соседних чисел, то оно округляется до ближайшего чётного числа. К примеру, 3,5 округляется до 4, а 2,5 до 2.
- В других случаях функция округляет к ближайшему целому.
Банковское округление позволяет уменьшить влияние округления чисел на результат суммирования или вычитания этих чисел.
Пример суммирования чисел 1.5, 2.5, 3.5 и 4.5 с различным округлением:
- Без округления: 1.5 + 2.5 + 3.5 + 4.5 = 12.
- Банковское округление: 2 + 2 + 4 + 4 = 12.
- Округление до ближайшего целого: 2 + 3 + 4 + 5 = 14.
**Синтаксис**
```sql
roundBankers(expression [, decimal_places])
```
**Параметры**
- `expression` — Число для округления. Может быть любым [выражением](../syntax.md#syntax-expressions), возвращающим числовой [тип данных](../../data_types/index.md#data_types).
- `decimal-places` — Десятичный разряд. Целое число.
- `decimal-places > 0` — Функция округляет значение выражения до ближайшего чётного числа на соответствующей позиции справа от запятой. Например, `roundBankers(3.55, 1) = 3.6`.
- `decimal-places < 0` — Функция округляет значение выражения до ближайшего чётного числа на соответствующей позиции слева от запятой. Например, `roundBankers(24.55, -1) = 20`.
- `decimal-places = 0` — Функция округляет значение до целого. В этом случае аргумент можно не передавать. Например, `roundBankers(2.5) = 2`.
**Возвращаемое значение**
Округлённое значение по методу банковского округления.
**Пример использования**
Запрос:
```sql
SELECT number / 2 AS x, roundBankers(x, 0) AS b fROM system.numbers limit 10
```
Результат:
```text
┌───x─┬─b─┐
│ 0 │ 0 │
│ 0.5 │ 0 │
│ 1 │ 1 │
│ 1.5 │ 2 │
│ 2 │ 2 │
│ 2.5 │ 2 │
│ 3 │ 3 │
│ 3.5 │ 4 │
│ 4 │ 4 │
│ 4.5 │ 4 │
└─────┴───┘
```
**Примеры банковского округления**
```text
roundBankers(0.4) = 0
roundBankers(-3.5) = -4
roundBankers(4.5) = 4
roundBankers(3.55, 1) = 3.6
roundBankers(3.65, 1) = 3.6
roundBankers(10.35, 1) = 10.4
roundBankers(10.755, 2) = 11,76
```
**См. также**
- [round](#rounding_functions-round)
## roundToExp2(num) ## roundToExp2(num)
Принимает число. Если число меньше единицы - возвращает 0. Иначе округляет число вниз до ближайшей (целой неотрицательной) степени двух. Принимает число. Если число меньше единицы - возвращает 0. Иначе округляет число вниз до ближайшей (целой неотрицательной) степени двух.

View File

@ -1147,7 +1147,7 @@ SELECT CounterID, 2 AS table, sum(Sign) AS c
Запросы - части `UNION ALL` нельзя заключить в скобки. `ORDER BY` и `LIMIT` применяются к отдельным запросам, а не к общему результату. Если вам нужно применить какое-либо преобразование к общему результату, то вы можете разместить все запросы с `UNION ALL` в подзапросе в секции `FROM`. Запросы - части `UNION ALL` нельзя заключить в скобки. `ORDER BY` и `LIMIT` применяются к отдельным запросам, а не к общему результату. Если вам нужно применить какое-либо преобразование к общему результату, то вы можете разместить все запросы с `UNION ALL` в подзапросе в секции `FROM`.
### Секция INTO OUTFILE ### Секция INTO OUTFILE {#into-outfile-clause}
При указании `INTO OUTFILE filename` (где filename - строковый литерал), результат запроса будет сохранён в файл filename. При указании `INTO OUTFILE filename` (где filename - строковый литерал), результат запроса будет сохранён в файл filename.
В отличие от MySQL, файл создаётся на стороне клиента. Если файл с таким именем уже существует, это приведёт к ошибке. В отличие от MySQL, файл создаётся на стороне клиента. Если файл с таким именем уже существует, это приведёт к ошибке.
@ -1155,7 +1155,7 @@ SELECT CounterID, 2 AS table, sum(Sign) AS c
Формат вывода по умолчанию - TabSeparated, как и в не интерактивном режиме клиента командной строки. Формат вывода по умолчанию - TabSeparated, как и в не интерактивном режиме клиента командной строки.
### Секция FORMAT ### Секция FORMAT {#format-clause}
При указании FORMAT format вы можете получить данные в любом указанном формате. При указании FORMAT format вы можете получить данные в любом указанном формате.
Это может использоваться для удобства или для создания дампов. Это может использоваться для удобства или для создания дампов.

View File

@ -25,6 +25,7 @@ nav:
- 'Command-Line Client': 'interfaces/cli.md' - 'Command-Line Client': 'interfaces/cli.md'
- 'Native Interface (TCP)': 'interfaces/tcp.md' - 'Native Interface (TCP)': 'interfaces/tcp.md'
- 'HTTP Interface': 'interfaces/http.md' - 'HTTP Interface': 'interfaces/http.md'
- 'MySQL Interface': 'interfaces/mysql.md'
- 'Input and Output Formats': 'interfaces/formats.md' - 'Input and Output Formats': 'interfaces/formats.md'
- 'JDBC Driver': 'interfaces/jdbc.md' - 'JDBC Driver': 'interfaces/jdbc.md'
- 'ODBC Driver': 'interfaces/odbc.md' - 'ODBC Driver': 'interfaces/odbc.md'

View File

@ -26,6 +26,7 @@ nav:
- 'Клиент командной строки': 'interfaces/cli.md' - 'Клиент командной строки': 'interfaces/cli.md'
- 'Нативный интерфейс (TCP)': 'interfaces/tcp.md' - 'Нативный интерфейс (TCP)': 'interfaces/tcp.md'
- 'HTTP-интерфейс': 'interfaces/http.md' - 'HTTP-интерфейс': 'interfaces/http.md'
- 'MySQL-интерфейс': 'interfaces/mysql.md'
- 'Форматы входных и выходных данных': 'interfaces/formats.md' - 'Форматы входных и выходных данных': 'interfaces/formats.md'
- 'JDBC-драйвер': 'interfaces/jdbc.md' - 'JDBC-драйвер': 'interfaces/jdbc.md'
- 'ODBC-драйвер': 'interfaces/odbc.md' - 'ODBC-драйвер': 'interfaces/odbc.md'

View File

@ -59,12 +59,6 @@ export CC=gcc-9
export CXX=g++-9 export CXX=g++-9
``` ```
## 安装所需的工具依赖库
```bash
sudo apt-get install libreadline-dev
```
## 拉取 ClickHouse 源码 ## 拉取 ClickHouse 源码
```bash ```bash

View File

@ -12,7 +12,7 @@ ClickHouse 支持在 Mac OS X 10.12 版本中编译。若您在用更早的操
## 安装编译器,工具库 ## 安装编译器,工具库
```bash ```bash
brew install cmake ninja gcc icu4c mariadb-connector-c openssl libtool gettext readline brew install cmake ninja gcc icu4c mariadb-connector-c openssl libtool gettext
``` ```
## 拉取 ClickHouse 源码 ## 拉取 ClickHouse 源码

View File

@ -105,14 +105,6 @@ brew install cmake ninja
ClickHouse使用多个外部库进行构建。大多数外部库不需要单独安装而是和ClickHouse一起在子模块中构建。可以查看`contrib`中罗列的清单。 ClickHouse使用多个外部库进行构建。大多数外部库不需要单独安装而是和ClickHouse一起在子模块中构建。可以查看`contrib`中罗列的清单。
有一些库不是由源构建的而是由系统提供例如Readline也建议安装。
Ubuntu: `sudo apt install libreadline-dev`
Mac OS X: `brew install readline`
但是这些库本身都是可选的ClickHouse即便没有它们也可以构建。ICU用于支持`ORDER BY`中的`COLLATE`(例如,对土耳其字母进行排序)。Readline用于在clickhouse-client中更便捷的指令输入。
# C++ 编译器 # C++ 编译器

View File

@ -45,7 +45,7 @@ cat file.csv | clickhouse-client --database=test --query="INSERT INTO test FORMA
您可以指定 `\G` 来替代分号或者在分号后面,这表示 `Vertical` 的格式。在这种格式下,每一个值都会打印在不同的行中,这种方式对于宽表来说很方便。这个不常见的特性是为了兼容 MySQL 命令而加的。 您可以指定 `\G` 来替代分号或者在分号后面,这表示 `Vertical` 的格式。在这种格式下,每一个值都会打印在不同的行中,这种方式对于宽表来说很方便。这个不常见的特性是为了兼容 MySQL 命令而加的。
命令行客户端是基于 `readline` 库(`history` 库或者 `libedit` 库, 或不基于其他库, 这取决于客户端是如何编译的)。换句话说,它可以使用我们熟悉的快捷键方式来操作以及保留历史命令。 命令行客户端是基于 `replxx`。换句话说,它可以使用我们熟悉的快捷键方式来操作以及保留历史命令。
历史命令会写入在 `~/.clickhouse-client-history` 中。 历史命令会写入在 `~/.clickhouse-client-history` 中。
默认情况下,输出的格式是 `PrettyCompact`。您可以通过 FORMAT 设置根据不同查询来修改格式,或者通过在查询末尾指定 `\G` 字符,或通过在命令行中使用 `--format` or `--vertical` 参数,或使用客户端的配置文件。 默认情况下,输出的格式是 `PrettyCompact`。您可以通过 FORMAT 设置根据不同查询来修改格式,或者通过在查询末尾指定 `\G` 字符,或通过在命令行中使用 `--format` or `--vertical` 参数,或使用客户端的配置文件。

View File

@ -11,46 +11,48 @@ if (DEFINED APPLE_HAVE_CLOCK_GETTIME)
endif () endif ()
add_library (common add_library (common
src/argsToConfig.cpp
src/coverage.cpp
src/DateLUT.cpp src/DateLUT.cpp
src/DateLUTImpl.cpp src/DateLUTImpl.cpp
src/preciseExp10.c
src/shift10.cpp
src/mremap.cpp
src/JSON.cpp
src/getMemoryAmount.cpp
src/demangle.cpp src/demangle.cpp
src/setTerminalEcho.cpp src/getMemoryAmount.cpp
src/getThreadNumber.cpp src/getThreadNumber.cpp
src/sleep.cpp src/JSON.cpp
src/argsToConfig.cpp src/LineReader.cpp
src/mremap.cpp
src/phdr_cache.cpp src/phdr_cache.cpp
src/coverage.cpp src/preciseExp10.c
src/setTerminalEcho.cpp
src/shift10.cpp
src/sleep.cpp
include/common/SimpleCache.h include/common/constexpr_helpers.h
include/common/Types.h include/common/coverage.h
include/common/DayNum.h
include/common/DateLUT.h include/common/DateLUT.h
include/common/DateLUTImpl.h include/common/DateLUTImpl.h
include/common/DayNum.h
include/common/demangle.h
include/common/ErrorHandlers.h
include/common/find_symbols.h
include/common/getMemoryAmount.h
include/common/getThreadNumber.h
include/common/JSON.h
include/common/likely.h
include/common/LineReader.h
include/common/LocalDate.h include/common/LocalDate.h
include/common/LocalDateTime.h include/common/LocalDateTime.h
include/common/ErrorHandlers.h
include/common/preciseExp10.h
include/common/shift10.h
include/common/mremap.h
include/common/likely.h
include/common/logger_useful.h include/common/logger_useful.h
include/common/strong_typedef.h include/common/mremap.h
include/common/JSON.h
include/common/getMemoryAmount.h
include/common/demangle.h
include/common/setTerminalEcho.h
include/common/find_symbols.h
include/common/constexpr_helpers.h
include/common/getThreadNumber.h
include/common/sleep.h
include/common/SimpleCache.h
include/common/phdr_cache.h include/common/phdr_cache.h
include/common/coverage.h include/common/preciseExp10.h
include/common/setTerminalEcho.h
include/common/shift10.h
include/common/SimpleCache.h
include/common/SimpleCache.h
include/common/sleep.h
include/common/strong_typedef.h
include/common/Types.h
include/ext/bit_cast.h include/ext/bit_cast.h
include/ext/chrono_io.h include/ext/chrono_io.h
@ -90,6 +92,10 @@ if(CCTZ_LIBRARY)
target_link_libraries(common PRIVATE ${CCTZ_LIBRARY}) target_link_libraries(common PRIVATE ${CCTZ_LIBRARY})
endif() endif()
if (USE_REPLXX)
target_link_libraries(common PRIVATE replxx)
endif ()
target_link_libraries (common target_link_libraries (common
PUBLIC PUBLIC
${Poco_Util_LIBRARY} ${Poco_Util_LIBRARY}

View File

@ -0,0 +1,57 @@
#pragma once
#include <common/Types.h>
#include <atomic>
#include <vector>
class LineReader
{
public:
class Suggest
{
protected:
using Words = std::vector<std::string>;
using WordsRange = std::pair<Words::const_iterator, Words::const_iterator>;
Words words;
std::atomic<bool> ready{false};
public:
/// Get iterators for the matched range of words if any.
WordsRange getCompletions(const String & prefix, size_t prefix_length) const;
};
LineReader(const Suggest * suggest, const String & history_file_path, char extender, char delimiter = 0); /// if delimiter != 0, then it's multiline mode
~LineReader();
/// Reads the whole line until delimiter (in multiline mode) or until the last line without extender.
/// If resulting line is empty, it means the user interrupted the input.
/// Non-empty line is appended to history - without duplication.
/// Typical delimiter is ';' (semicolon) and typical extender is '\' (backslash).
String readLine(const String & first_prompt, const String & second_prompt);
private:
enum InputStatus
{
ABORT = 0,
RESET_LINE,
INPUT_LINE,
};
String input;
String prev_line;
const String history_file_path;
const char extender;
const char delimiter;
InputStatus readOneLine(const String & prompt);
void addToHistory(const String & line);
/// Since CMake doesn't impose restrictions on includes between unrelated targets
/// it's possible that we include this file without USE_REPLXX defined.
#ifdef __clang__
[[maybe_unused]]
#endif
void * impl;
};

Some files were not shown because too many files have changed in this diff Show More