mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
Merge branch 'protobuf-host-build' of github.com:yandex/ClickHouse into protobuf-host-build
This commit is contained in:
commit
1238ee7d2e
@ -1,14 +1,14 @@
|
||||
[![ClickHouse — open source distributed column-oriented DBMS](https://github.com/ClickHouse/ClickHouse/raw/master/website/images/logo-400x240.png)](https://clickhouse.com)
|
||||
|
||||
ClickHouse® is an open-source column-oriented database management system that allows generating analytical data reports in real time.
|
||||
ClickHouse® is an open-source column-oriented database management system that allows generating analytical data reports in real-time.
|
||||
|
||||
## Useful Links
|
||||
|
||||
* [Official website](https://clickhouse.com/) has quick high-level overview of ClickHouse on main page.
|
||||
* [Tutorial](https://clickhouse.com/docs/en/getting_started/tutorial/) shows how to set up and query small ClickHouse cluster.
|
||||
* [Official website](https://clickhouse.com/) has a quick high-level overview of ClickHouse on the main page.
|
||||
* [Tutorial](https://clickhouse.com/docs/en/getting_started/tutorial/) shows how to set up and query a small ClickHouse cluster.
|
||||
* [Documentation](https://clickhouse.com/docs/en/) provides more in-depth information.
|
||||
* [YouTube channel](https://www.youtube.com/c/ClickHouseDB) has a lot of content about ClickHouse in video format.
|
||||
* [Slack](https://join.slack.com/t/clickhousedb/shared_invite/zt-rxm3rdrk-lIUmhLC3V8WTaL0TGxsOmg) and [Telegram](https://telegram.me/clickhouse_en) allow to chat with ClickHouse users in real-time.
|
||||
* [Slack](https://join.slack.com/t/clickhousedb/shared_invite/zt-rxm3rdrk-lIUmhLC3V8WTaL0TGxsOmg) and [Telegram](https://telegram.me/clickhouse_en) allow chatting with ClickHouse users in real-time.
|
||||
* [Blog](https://clickhouse.com/blog/en/) contains various ClickHouse-related articles, as well as announcements and reports about events.
|
||||
* [Code Browser](https://clickhouse.com/codebrowser/html_report/ClickHouse/index.html) with syntax highlight and navigation.
|
||||
* [Contacts](https://clickhouse.com/company/#contact) can help to get your questions answered if there are any.
|
||||
|
@ -53,12 +53,7 @@ endif ()
|
||||
if (NOT OPENSSL_FOUND AND NOT MISSING_INTERNAL_SSL_LIBRARY)
|
||||
set (USE_INTERNAL_SSL_LIBRARY 1)
|
||||
set (OPENSSL_ROOT_DIR "${ClickHouse_SOURCE_DIR}/contrib/boringssl")
|
||||
|
||||
if (ARCH_AMD64)
|
||||
set (OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/include")
|
||||
elseif (ARCH_AARCH64)
|
||||
set (OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/include")
|
||||
endif ()
|
||||
set (OPENSSL_INCLUDE_DIR "${OPENSSL_ROOT_DIR}/include")
|
||||
set (OPENSSL_CRYPTO_LIBRARY crypto)
|
||||
set (OPENSSL_SSL_LIBRARY ssl)
|
||||
set (OPENSSL_FOUND 1)
|
||||
|
@ -5,7 +5,7 @@ set (DEFAULT_LIBS "-nodefaultlibs")
|
||||
|
||||
# We need builtins from Clang's RT even without libcxx - for ubsan+int128.
|
||||
# See https://bugs.llvm.org/show_bug.cgi?id=16404
|
||||
if (COMPILER_CLANG AND NOT (CMAKE_CROSSCOMPILING AND ARCH_AARCH64))
|
||||
if (COMPILER_CLANG AND NOT CMAKE_CROSSCOMPILING)
|
||||
execute_process (COMMAND ${CMAKE_CXX_COMPILER} --print-libgcc-file-name --rtlib=compiler-rt OUTPUT_VARIABLE BUILTINS_LIBRARY OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
else ()
|
||||
set (BUILTINS_LIBRARY "-lgcc")
|
||||
|
32
cmake/linux/toolchain-ppc64le.cmake
Normal file
32
cmake/linux/toolchain-ppc64le.cmake
Normal file
@ -0,0 +1,32 @@
|
||||
set (CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
|
||||
|
||||
set (CMAKE_SYSTEM_NAME "Linux")
|
||||
set (CMAKE_SYSTEM_PROCESSOR "ppc64le")
|
||||
set (CMAKE_C_COMPILER_TARGET "ppc64le-linux-gnu")
|
||||
set (CMAKE_CXX_COMPILER_TARGET "ppc64le-linux-gnu")
|
||||
set (CMAKE_ASM_COMPILER_TARGET "ppc64le-linux-gnu")
|
||||
|
||||
set (TOOLCHAIN_PATH "${CMAKE_CURRENT_LIST_DIR}/../../contrib/sysroot/linux-powerpc64le")
|
||||
|
||||
set (CMAKE_SYSROOT "${TOOLCHAIN_PATH}/powerpc64le-linux-gnu/libc")
|
||||
|
||||
find_program (LLVM_AR_PATH NAMES "llvm-ar" "llvm-ar-13" "llvm-ar-12" "llvm-ar-11" "llvm-ar-10" "llvm-ar-9" "llvm-ar-8")
|
||||
find_program (LLVM_RANLIB_PATH NAMES "llvm-ranlib" "llvm-ranlib-13" "llvm-ranlib-12" "llvm-ranlib-11" "llvm-ranlib-10" "llvm-ranlib-9")
|
||||
|
||||
set (CMAKE_AR "${LLVM_AR_PATH}" CACHE FILEPATH "" FORCE)
|
||||
set (CMAKE_RANLIB "${LLVM_RANLIB_PATH}" CACHE FILEPATH "" FORCE)
|
||||
|
||||
set (CMAKE_C_FLAGS_INIT "${CMAKE_C_FLAGS} --gcc-toolchain=${TOOLCHAIN_PATH}")
|
||||
set (CMAKE_CXX_FLAGS_INIT "${CMAKE_CXX_FLAGS} --gcc-toolchain=${TOOLCHAIN_PATH}")
|
||||
set (CMAKE_ASM_FLAGS_INIT "${CMAKE_ASM_FLAGS} --gcc-toolchain=${TOOLCHAIN_PATH}")
|
||||
|
||||
set (LINKER_NAME "ld.lld" CACHE STRING "" FORCE)
|
||||
|
||||
set (CMAKE_EXE_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
set (CMAKE_SHARED_LINKER_FLAGS_INIT "-fuse-ld=lld")
|
||||
|
||||
set (HAS_PRE_1970_EXITCODE "0" CACHE STRING "Result from TRY_RUN" FORCE)
|
||||
set (HAS_PRE_1970_EXITCODE__TRYRUN_OUTPUT "" CACHE STRING "Output from TRY_RUN" FORCE)
|
||||
|
||||
set (HAS_POST_2038_EXITCODE "0" CACHE STRING "Result from TRY_RUN" FORCE)
|
||||
set (HAS_POST_2038_EXITCODE__TRYRUN_OUTPUT "" CACHE STRING "Output from TRY_RUN" FORCE)
|
@ -21,29 +21,26 @@ if (CMAKE_CROSSCOMPILING)
|
||||
if (OS_DARWIN)
|
||||
# FIXME: broken dependencies
|
||||
set (ENABLE_GRPC OFF CACHE INTERNAL "") # no protobuf -> no grpc
|
||||
|
||||
set (ENABLE_ICU OFF CACHE INTERNAL "")
|
||||
set (ENABLE_FASTOPS OFF CACHE INTERNAL "")
|
||||
elseif (OS_LINUX OR OS_ANDROID)
|
||||
if (ARCH_AARCH64)
|
||||
# FIXME: broken dependencies
|
||||
set (ENABLE_GRPC OFF CACHE INTERNAL "")
|
||||
set (ENABLE_MYSQL OFF CACHE INTERNAL "")
|
||||
set (USE_SENTRY OFF CACHE INTERNAL "")
|
||||
elseif (ARCH_PPC64LE)
|
||||
set (ENABLE_GRPC OFF CACHE INTERNAL "")
|
||||
set (USE_SENTRY OFF CACHE INTERNAL "")
|
||||
endif ()
|
||||
elseif (OS_FREEBSD)
|
||||
# FIXME: broken dependencies
|
||||
set (ENABLE_PROTOBUF OFF CACHE INTERNAL "")
|
||||
set (ENABLE_GRPC OFF CACHE INTERNAL "")
|
||||
|
||||
set (ENABLE_ORC OFF CACHE INTERNAL "") # no protobuf -> no parquet -> no orc
|
||||
|
||||
set (ENABLE_EMBEDDED_COMPILER OFF CACHE INTERNAL "")
|
||||
else ()
|
||||
message (FATAL_ERROR "Trying to cross-compile to unsupported system: ${CMAKE_SYSTEM_NAME}!")
|
||||
endif ()
|
||||
|
||||
# Don't know why but CXX_STANDARD doesn't work for cross-compilation
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17")
|
||||
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++20")
|
||||
|
||||
message (STATUS "Cross-compiling for target: ${CMAKE_CXX_COMPILE_TARGET}")
|
||||
endif ()
|
||||
|
2
contrib/CMakeLists.txt
vendored
2
contrib/CMakeLists.txt
vendored
@ -278,7 +278,7 @@ if (USE_FASTOPS)
|
||||
endif()
|
||||
|
||||
if (USE_AMQPCPP OR USE_CASSANDRA)
|
||||
add_subdirectory (libuv)
|
||||
add_subdirectory (libuv-cmake)
|
||||
endif()
|
||||
if (USE_AMQPCPP)
|
||||
add_subdirectory (amqpcpp-cmake)
|
||||
|
2
contrib/boost
vendored
2
contrib/boost
vendored
@ -1 +1 @@
|
||||
Subproject commit 66d17f060c4867aeea99fa2a20cfdae89ae2a2ec
|
||||
Subproject commit 311cfd498966d4f77742703d605d9c2e7b4cc6a8
|
@ -81,7 +81,7 @@
|
||||
/* #undef JEMALLOC_HAVE_ISSETUGID */
|
||||
|
||||
/* Defined if pthread_atfork(3) is available. */
|
||||
#define JEMALLOC_HAVE_PTHREAD_ATFORK
|
||||
/* #undef JEMALLOC_HAVE_PTHREAD_ATFORK */
|
||||
|
||||
/* Defined if pthread_setname_np(3) is available. */
|
||||
#define JEMALLOC_HAVE_PTHREAD_SETNAME_NP
|
||||
@ -284,7 +284,7 @@
|
||||
#define JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS
|
||||
|
||||
/* Defined if madvise(2) is available but MADV_FREE is not (x86 Linux only). */
|
||||
/* #undef JEMALLOC_DEFINE_MADVISE_FREE */
|
||||
#define JEMALLOC_DEFINE_MADVISE_FREE
|
||||
|
||||
/*
|
||||
* Defined if MADV_DO[NT]DUMP is supported as an argument to madvise.
|
||||
|
2
contrib/libuv
vendored
2
contrib/libuv
vendored
@ -1 +1 @@
|
||||
Subproject commit e2e9b7e9f978ce8a1367b5fe781d97d1ce9f94ab
|
||||
Subproject commit 95081e7c16c9857babe6d4e2bc1c779198ea89ae
|
160
contrib/libuv-cmake/CMakeLists.txt
Normal file
160
contrib/libuv-cmake/CMakeLists.txt
Normal file
@ -0,0 +1,160 @@
|
||||
# This file is a modified version of contrib/libuv/CMakeLists.txt
|
||||
|
||||
include(CMakeDependentOption)
|
||||
|
||||
set (SOURCE_DIR "${CMAKE_SOURCE_DIR}/contrib/libuv")
|
||||
set (BINARY_DIR "${CMAKE_BINARY_DIR}/contrib/libuv")
|
||||
|
||||
|
||||
if(CMAKE_C_COMPILER_ID MATCHES "AppleClang|Clang|GNU")
|
||||
list(APPEND uv_cflags -fvisibility=hidden --std=gnu89)
|
||||
list(APPEND uv_cflags -Wall -Wextra -Wstrict-prototypes)
|
||||
list(APPEND uv_cflags -Wno-unused-parameter)
|
||||
endif()
|
||||
|
||||
set(uv_sources
|
||||
src/fs-poll.c
|
||||
src/idna.c
|
||||
src/inet.c
|
||||
src/random.c
|
||||
src/strscpy.c
|
||||
src/threadpool.c
|
||||
src/timer.c
|
||||
src/uv-common.c
|
||||
src/uv-data-getter-setters.c
|
||||
src/version.c
|
||||
src/unix/async.c
|
||||
src/unix/core.c
|
||||
src/unix/dl.c
|
||||
src/unix/fs.c
|
||||
src/unix/getaddrinfo.c
|
||||
src/unix/getnameinfo.c
|
||||
src/unix/loop-watcher.c
|
||||
src/unix/loop.c
|
||||
src/unix/pipe.c
|
||||
src/unix/poll.c
|
||||
src/unix/process.c
|
||||
src/unix/random-devurandom.c
|
||||
src/unix/signal.c
|
||||
src/unix/stream.c
|
||||
src/unix/tcp.c
|
||||
src/unix/thread.c
|
||||
src/unix/tty.c
|
||||
src/unix/udp.c)
|
||||
|
||||
if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "Android|Linux|OS/390")
|
||||
list(APPEND uv_sources src/unix/proctitle.c)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD")
|
||||
list(APPEND uv_sources src/unix/freebsd.c)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|NetBSD|OpenBSD")
|
||||
list(APPEND uv_sources src/unix/posix-hrtime.c src/unix/bsd-proctitle.c)
|
||||
endif()
|
||||
|
||||
if(APPLE OR CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|NetBSD|OpenBSD")
|
||||
list(APPEND uv_sources src/unix/bsd-ifaddrs.c src/unix/kqueue.c)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
|
||||
list(APPEND uv_sources src/unix/random-getrandom.c)
|
||||
endif()
|
||||
|
||||
if(APPLE OR CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
list(APPEND uv_sources src/unix/random-getentropy.c)
|
||||
endif()
|
||||
|
||||
if(APPLE)
|
||||
list(APPEND uv_defines _DARWIN_UNLIMITED_SELECT=1 _DARWIN_USE_64_BIT_INODE=1)
|
||||
list(APPEND uv_sources
|
||||
src/unix/darwin-proctitle.c
|
||||
src/unix/darwin.c
|
||||
src/unix/fsevents.c)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112)
|
||||
list(APPEND uv_libraries dl rt)
|
||||
list(APPEND uv_sources
|
||||
src/unix/linux-core.c
|
||||
src/unix/linux-inotify.c
|
||||
src/unix/linux-syscalls.c
|
||||
src/unix/procfs-exepath.c
|
||||
src/unix/random-getrandom.c
|
||||
src/unix/random-sysctl-linux.c
|
||||
src/unix/sysinfo-loadavg.c)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "NetBSD")
|
||||
list(APPEND uv_sources src/unix/netbsd.c)
|
||||
list(APPEND uv_libraries kvm)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "OpenBSD")
|
||||
list(APPEND uv_sources src/unix/openbsd.c)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "OS/390")
|
||||
list(APPEND uv_defines PATH_MAX=255)
|
||||
list(APPEND uv_defines _AE_BIMODAL)
|
||||
list(APPEND uv_defines _ALL_SOURCE)
|
||||
list(APPEND uv_defines _LARGE_TIME_API)
|
||||
list(APPEND uv_defines _OPEN_MSGQ_EXT)
|
||||
list(APPEND uv_defines _OPEN_SYS_FILE_EXT)
|
||||
list(APPEND uv_defines _OPEN_SYS_IF_EXT)
|
||||
list(APPEND uv_defines _OPEN_SYS_SOCK_EXT3)
|
||||
list(APPEND uv_defines _OPEN_SYS_SOCK_IPV6)
|
||||
list(APPEND uv_defines _UNIX03_SOURCE)
|
||||
list(APPEND uv_defines _UNIX03_THREADS)
|
||||
list(APPEND uv_defines _UNIX03_WITHDRAWN)
|
||||
list(APPEND uv_defines _XOPEN_SOURCE_EXTENDED)
|
||||
list(APPEND uv_sources
|
||||
src/unix/pthread-fixes.c
|
||||
src/unix/pthread-barrier.c
|
||||
src/unix/os390.c
|
||||
src/unix/os390-syscalls.c)
|
||||
endif()
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "SunOS")
|
||||
list(APPEND uv_defines __EXTENSIONS__ _XOPEN_SOURCE=500)
|
||||
list(APPEND uv_libraries kstat nsl sendfile socket)
|
||||
list(APPEND uv_sources src/unix/no-proctitle.c src/unix/sunos.c)
|
||||
endif()
|
||||
|
||||
set(uv_sources_tmp "")
|
||||
foreach(file ${uv_sources})
|
||||
list(APPEND uv_sources_tmp "${SOURCE_DIR}/${file}")
|
||||
endforeach(file)
|
||||
set(uv_sources "${uv_sources_tmp}")
|
||||
|
||||
list(APPEND uv_defines CLICKHOUSE_GLIBC_COMPATIBILITY)
|
||||
|
||||
add_library(uv ${uv_sources})
|
||||
target_compile_definitions(uv
|
||||
INTERFACE USING_UV_SHARED=1
|
||||
PRIVATE ${uv_defines} BUILDING_UV_SHARED=1)
|
||||
target_compile_options(uv PRIVATE ${uv_cflags})
|
||||
target_include_directories(uv PUBLIC ${SOURCE_DIR}/include PRIVATE ${SOURCE_DIR}/src)
|
||||
target_link_libraries(uv ${uv_libraries})
|
||||
|
||||
add_library(uv_a STATIC ${uv_sources})
|
||||
target_compile_definitions(uv_a PRIVATE ${uv_defines})
|
||||
target_compile_options(uv_a PRIVATE ${uv_cflags})
|
||||
target_include_directories(uv_a PUBLIC ${SOURCE_DIR}/include PRIVATE ${SOURCE_DIR}/src)
|
||||
target_link_libraries(uv_a ${uv_libraries})
|
||||
|
||||
if(UNIX)
|
||||
# Now for some gibbering horrors from beyond the stars...
|
||||
foreach(x ${uv_libraries})
|
||||
set(LIBS "${LIBS} -l${x}")
|
||||
endforeach(x)
|
||||
file(STRINGS ${SOURCE_DIR}/configure.ac configure_ac REGEX ^AC_INIT)
|
||||
string(REGEX MATCH [0-9]+[.][0-9]+[.][0-9]+ PACKAGE_VERSION "${configure_ac}")
|
||||
string(REGEX MATCH ^[0-9]+ UV_VERSION_MAJOR "${PACKAGE_VERSION}")
|
||||
# The version in the filename is mirroring the behaviour of autotools.
|
||||
set_target_properties(uv PROPERTIES VERSION ${UV_VERSION_MAJOR}.0.0
|
||||
SOVERSION ${UV_VERSION_MAJOR})
|
||||
endif()
|
||||
|
2
contrib/s2geometry
vendored
2
contrib/s2geometry
vendored
@ -1 +1 @@
|
||||
Subproject commit 20ea540d81f4575a3fc0aea585aac611bcd03ede
|
||||
Subproject commit 38b7a290f927cc372218c2094602b83e35b18c05
|
2
contrib/sysroot
vendored
2
contrib/sysroot
vendored
@ -1 +1 @@
|
||||
Subproject commit 611d3315e9e369a338de4ffa128eb87b4fb87dec
|
||||
Subproject commit 002415524b5d14124bb8a61a3ce7ac65774f5479
|
@ -61,6 +61,7 @@ def parse_env_variables(build_type, compiler, sanitizer, package_type, image_typ
|
||||
DARWIN_ARM_SUFFIX = "-darwin-aarch64"
|
||||
ARM_SUFFIX = "-aarch64"
|
||||
FREEBSD_SUFFIX = "-freebsd"
|
||||
PPC_SUFFIX = '-ppc64le'
|
||||
|
||||
result = []
|
||||
cmake_flags = ['$CMAKE_FLAGS', '-DADD_GDB_INDEX_FOR_GOLD=1']
|
||||
@ -69,8 +70,9 @@ def parse_env_variables(build_type, compiler, sanitizer, package_type, image_typ
|
||||
is_cross_darwin = compiler.endswith(DARWIN_SUFFIX)
|
||||
is_cross_darwin_arm = compiler.endswith(DARWIN_ARM_SUFFIX)
|
||||
is_cross_arm = compiler.endswith(ARM_SUFFIX)
|
||||
is_cross_ppc = compiler.endswith(PPC_SUFFIX)
|
||||
is_cross_freebsd = compiler.endswith(FREEBSD_SUFFIX)
|
||||
is_cross_compile = is_cross_darwin or is_cross_darwin_arm or is_cross_arm or is_cross_freebsd
|
||||
is_cross_compile = is_cross_darwin or is_cross_darwin_arm or is_cross_arm or is_cross_freebsd or is_cross_ppc
|
||||
|
||||
# Explicitly use LLD with Clang by default.
|
||||
# Don't force linker for cross-compilation.
|
||||
@ -97,6 +99,9 @@ def parse_env_variables(build_type, compiler, sanitizer, package_type, image_typ
|
||||
elif is_cross_freebsd:
|
||||
cc = compiler[:-len(FREEBSD_SUFFIX)]
|
||||
cmake_flags.append("-DCMAKE_TOOLCHAIN_FILE=/build/cmake/freebsd/toolchain-x86_64.cmake")
|
||||
elif is_cross_ppc:
|
||||
cc = compiler[:-len(PPC_SUFFIX)]
|
||||
cmake_flags.append("-DCMAKE_TOOLCHAIN_FILE=/build/cmake/linux/toolchain-ppc64le.cmake")
|
||||
else:
|
||||
cc = compiler
|
||||
|
||||
@ -205,7 +210,7 @@ if __name__ == "__main__":
|
||||
parser.add_argument("--build-type", choices=("debug", ""), default="")
|
||||
parser.add_argument("--compiler", choices=("clang-11", "clang-11-darwin", "clang-11-darwin-aarch64", "clang-11-aarch64",
|
||||
"clang-12", "clang-12-darwin", "clang-12-darwin-aarch64", "clang-12-aarch64",
|
||||
"clang-13", "clang-13-darwin", "clang-13-darwin-aarch64", "clang-13-aarch64",
|
||||
"clang-13", "clang-13-darwin", "clang-13-darwin-aarch64", "clang-13-aarch64", "clang-13-ppc64le",
|
||||
"clang-11-freebsd", "clang-12-freebsd", "clang-13-freebsd", "gcc-11"), default="clang-13")
|
||||
parser.add_argument("--sanitizer", choices=("address", "thread", "memory", "undefined", ""), default="")
|
||||
parser.add_argument("--unbundled", action="store_true")
|
||||
|
@ -71,42 +71,42 @@ def prepare_for_hung_check(drop_databases):
|
||||
# FIXME this function should not exist, but...
|
||||
|
||||
# ThreadFuzzer significantly slows down server and causes false-positive hung check failures
|
||||
call("clickhouse client -q 'SYSTEM STOP THREAD FUZZER'", shell=True, stderr=STDOUT)
|
||||
call("clickhouse client -q 'SYSTEM STOP THREAD FUZZER'", shell=True, stderr=STDOUT, timeout=30)
|
||||
|
||||
# We attach gdb to clickhouse-server before running tests
|
||||
# to print stacktraces of all crashes even if clickhouse cannot print it for some reason.
|
||||
# However, it obstruct checking for hung queries.
|
||||
logging.info("Will terminate gdb (if any)")
|
||||
call("kill -TERM $(pidof gdb)", shell=True, stderr=STDOUT)
|
||||
call("kill -TERM $(pidof gdb)", shell=True, stderr=STDOUT, timeout=30)
|
||||
|
||||
# Some tests set too low memory limit for default user and forget to reset in back.
|
||||
# It may cause SYSTEM queries to fail, let's disable memory limit.
|
||||
call("clickhouse client --max_memory_usage_for_user=0 -q 'SELECT 1 FORMAT Null'", shell=True, stderr=STDOUT)
|
||||
call("clickhouse client --max_memory_usage_for_user=0 -q 'SELECT 1 FORMAT Null'", shell=True, stderr=STDOUT, timeout=30)
|
||||
|
||||
# Some tests execute SYSTEM STOP MERGES or similar queries.
|
||||
# It may cause some ALTERs to hang.
|
||||
# Possibly we should fix tests and forbid to use such queries without specifying table.
|
||||
call("clickhouse client -q 'SYSTEM START MERGES'", shell=True, stderr=STDOUT)
|
||||
call("clickhouse client -q 'SYSTEM START DISTRIBUTED SENDS'", shell=True, stderr=STDOUT)
|
||||
call("clickhouse client -q 'SYSTEM START TTL MERGES'", shell=True, stderr=STDOUT)
|
||||
call("clickhouse client -q 'SYSTEM START MOVES'", shell=True, stderr=STDOUT)
|
||||
call("clickhouse client -q 'SYSTEM START FETCHES'", shell=True, stderr=STDOUT)
|
||||
call("clickhouse client -q 'SYSTEM START REPLICATED SENDS'", shell=True, stderr=STDOUT)
|
||||
call("clickhouse client -q 'SYSTEM START REPLICATION QUEUES'", shell=True, stderr=STDOUT)
|
||||
call("clickhouse client -q 'SYSTEM START MERGES'", shell=True, stderr=STDOUT, timeout=30)
|
||||
call("clickhouse client -q 'SYSTEM START DISTRIBUTED SENDS'", shell=True, stderr=STDOUT, timeout=30)
|
||||
call("clickhouse client -q 'SYSTEM START TTL MERGES'", shell=True, stderr=STDOUT, timeout=30)
|
||||
call("clickhouse client -q 'SYSTEM START MOVES'", shell=True, stderr=STDOUT, timeout=30)
|
||||
call("clickhouse client -q 'SYSTEM START FETCHES'", shell=True, stderr=STDOUT, timeout=30)
|
||||
call("clickhouse client -q 'SYSTEM START REPLICATED SENDS'", shell=True, stderr=STDOUT, timeout=30)
|
||||
call("clickhouse client -q 'SYSTEM START REPLICATION QUEUES'", shell=True, stderr=STDOUT, timeout=30)
|
||||
|
||||
# Issue #21004, live views are experimental, so let's just suppress it
|
||||
call("""clickhouse client -q "KILL QUERY WHERE upper(query) LIKE 'WATCH %'" """, shell=True, stderr=STDOUT)
|
||||
call("""clickhouse client -q "KILL QUERY WHERE upper(query) LIKE 'WATCH %'" """, shell=True, stderr=STDOUT, timeout=30)
|
||||
|
||||
# Kill other queries which known to be slow
|
||||
# It's query from 01232_preparing_sets_race_condition_long, it may take up to 1000 seconds in slow builds
|
||||
call("""clickhouse client -q "KILL QUERY WHERE query LIKE 'insert into tableB select %'" """, shell=True, stderr=STDOUT)
|
||||
call("""clickhouse client -q "KILL QUERY WHERE query LIKE 'insert into tableB select %'" """, shell=True, stderr=STDOUT, timeout=30)
|
||||
# Long query from 00084_external_agregation
|
||||
call("""clickhouse client -q "KILL QUERY WHERE query LIKE 'SELECT URL, uniq(SearchPhrase) AS u FROM test.hits GROUP BY URL ORDER BY u %'" """, shell=True, stderr=STDOUT)
|
||||
call("""clickhouse client -q "KILL QUERY WHERE query LIKE 'SELECT URL, uniq(SearchPhrase) AS u FROM test.hits GROUP BY URL ORDER BY u %'" """, shell=True, stderr=STDOUT, timeout=30)
|
||||
|
||||
if drop_databases:
|
||||
# Here we try to drop all databases in async mode. If some queries really hung, than drop will hung too.
|
||||
# Otherwise we will get rid of queries which wait for background pool. It can take a long time on slow builds (more than 900 seconds).
|
||||
databases = check_output('clickhouse client -q "SHOW DATABASES"', shell=True).decode('utf-8').strip().split()
|
||||
databases = check_output('clickhouse client -q "SHOW DATABASES"', shell=True, timeout=30).decode('utf-8').strip().split()
|
||||
for db in databases:
|
||||
if db == "system":
|
||||
continue
|
||||
@ -117,13 +117,13 @@ def prepare_for_hung_check(drop_databases):
|
||||
# Wait for last queries to finish if any, not longer than 300 seconds
|
||||
call("""clickhouse client -q "select sleepEachRow((
|
||||
select maxOrDefault(300 - elapsed) + 1 from system.processes where query not like '%from system.processes%' and elapsed < 300
|
||||
) / 300) from numbers(300) format Null" """, shell=True, stderr=STDOUT)
|
||||
) / 300) from numbers(300) format Null" """, shell=True, stderr=STDOUT, timeout=330)
|
||||
|
||||
# Even if all clickhouse-test processes are finished, there are probably some sh scripts,
|
||||
# which still run some new queries. Let's ignore them.
|
||||
try:
|
||||
query = """clickhouse client -q "SELECT count() FROM system.processes where where elapsed > 300" """
|
||||
output = check_output(query, shell=True, stderr=STDOUT).decode('utf-8').strip()
|
||||
output = check_output(query, shell=True, stderr=STDOUT, timeout=30).decode('utf-8').strip()
|
||||
if int(output) == 0:
|
||||
return False
|
||||
except:
|
||||
@ -176,6 +176,7 @@ if __name__ == "__main__":
|
||||
if res != 0 and have_long_running_queries:
|
||||
logging.info("Hung check failed with exit code {}".format(res))
|
||||
hung_check_status = "Hung check failed\tFAIL\n"
|
||||
open(os.path.join(args.output_folder, "test_results.tsv"), 'w+').write(hung_check_status)
|
||||
with open(os.path.join(args.output_folder, "test_results.tsv"), 'w+') as results:
|
||||
results.write(hung_check_status)
|
||||
|
||||
logging.info("Stress test finished")
|
||||
|
@ -688,7 +688,7 @@ Tags:
|
||||
- `policy_name_N` — Policy name. Policy names must be unique.
|
||||
- `volume_name_N` — Volume name. Volume names must be unique.
|
||||
- `disk` — a disk within a volume.
|
||||
- `max_data_part_size_bytes` — the maximum size of a part that can be stored on any of the volume’s disks.
|
||||
- `max_data_part_size_bytes` — the maximum size of a part that can be stored on any of the volume’s disks. If the a size of a merged part estimated to be bigger than `max_data_part_size_bytes` then this part will be written to a next volume. Basically this feature allows to keep new/small parts on a hot (SSD) volume and move them to a cold (HDD) volume when they reach large size. Do not use this setting if your policy has only one volume.
|
||||
- `move_factor` — when the amount of available space gets lower than this factor, data automatically start to move on the next volume if any (by default, 0.1).
|
||||
- `prefer_not_to_merge` — Disables merging of data parts on this volume. When this setting is enabled, merging data on this volume is not allowed. This allows controlling how ClickHouse works with slow disks.
|
||||
|
||||
|
@ -10,6 +10,9 @@ Columns:
|
||||
- `type` ([String](../../sql-reference/data-types/string.md)) — Index type.
|
||||
- `expr` ([String](../../sql-reference/data-types/string.md)) — Expression for the index calculation.
|
||||
- `granularity` ([UInt64](../../sql-reference/data-types/int-uint.md)) — The number of granules in the block.
|
||||
- `data_compressed_bytes` ([UInt64](../../sql-reference/data-types/int-uint.md)) — The size of compressed data, in bytes.
|
||||
- `data_uncompressed_bytes` ([UInt64](../../sql-reference/data-types/int-uint.md)) — The size of decompressed data, in bytes.
|
||||
- `marks_bytes` ([UInt64](../../sql-reference/data-types/int-uint.md)) — The size of marks, in bytes.
|
||||
|
||||
**Example**
|
||||
|
||||
@ -26,6 +29,9 @@ name: clicks_idx
|
||||
type: minmax
|
||||
expr: clicks
|
||||
granularity: 1
|
||||
data_compressed_bytes: 58
|
||||
data_uncompressed_bytes: 6
|
||||
marks: 48
|
||||
|
||||
Row 2:
|
||||
──────
|
||||
@ -35,4 +41,7 @@ name: contacts_null_idx
|
||||
type: minmax
|
||||
expr: assumeNotNull(contacts_null)
|
||||
granularity: 1
|
||||
data_compressed_bytes: 58
|
||||
data_uncompressed_bytes: 6
|
||||
marks: 48
|
||||
```
|
||||
|
@ -38,6 +38,12 @@ Columns:
|
||||
|
||||
- `marks_bytes` ([UInt64](../../sql-reference/data-types/int-uint.md)) – The size of the file with marks.
|
||||
|
||||
- `secondary_indices_compressed_bytes` ([UInt64](../../sql-reference/data-types/int-uint.md)) – Total size of compressed data for secondary indices in the data part. All the auxiliary files (for example, files with marks) are not included.
|
||||
|
||||
- `secondary_indices_uncompressed_bytes` ([UInt64](../../sql-reference/data-types/int-uint.md)) – Total size of uncompressed data for secondary indices in the data part. All the auxiliary files (for example, files with marks) are not included.
|
||||
|
||||
- `secondary_indices_marks_bytes` ([UInt64](../../sql-reference/data-types/int-uint.md)) – The size of the file with marks for secondary indices.
|
||||
|
||||
- `modification_time` ([DateTime](../../sql-reference/data-types/datetime.md)) – The time the directory with the data part was modified. This usually corresponds to the time of data part creation.
|
||||
|
||||
- `remove_time` ([DateTime](../../sql-reference/data-types/datetime.md)) – The time when the data part became inactive.
|
||||
@ -119,6 +125,9 @@ rows: 6
|
||||
bytes_on_disk: 310
|
||||
data_compressed_bytes: 157
|
||||
data_uncompressed_bytes: 91
|
||||
secondary_indices_compressed_bytes: 58
|
||||
secondary_indices_uncompressed_bytes: 6
|
||||
secondary_indices_marks_bytes: 48
|
||||
marks_bytes: 144
|
||||
modification_time: 2020-06-18 13:01:49
|
||||
remove_time: 1970-01-01 00:00:00
|
||||
|
@ -141,20 +141,49 @@ This is a relatively fast non-cryptographic hash function of average quality for
|
||||
Calculates a 64-bit hash code from any type of integer.
|
||||
It works faster than intHash32. Average quality.
|
||||
|
||||
## SHA1 {#sha1}
|
||||
## SHA1, SHA224, SHA256, SHA512 {#sha}
|
||||
|
||||
## SHA224 {#sha224}
|
||||
Calculates SHA-1, SHA-224, SHA-256, SHA-512 hash from a string and returns the resulting set of bytes as [FixedString](../data-types/fixedstring.md).
|
||||
|
||||
## SHA256 {#sha256}
|
||||
**Syntax**
|
||||
|
||||
## SHA384 {#sha384}
|
||||
``` sql
|
||||
SHA1('s')
|
||||
...
|
||||
SHA512('s')
|
||||
```
|
||||
|
||||
## SHA512 {#sha512}
|
||||
|
||||
Calculates SHA-1, SHA-224, SHA-256, SHA-384 or SHA-512 from a string and returns the resulting set of bytes as FixedString(20), FixedString(28), FixedString(32), FixedString(48) or FixedString(64).
|
||||
The function works fairly slowly (SHA-1 processes about 5 million short strings per second per processor core, while SHA-224 and SHA-256 process about 2.2 million).
|
||||
We recommend using this function only in cases when you need a specific hash function and you can’t select it.
|
||||
Even in these cases, we recommend applying the function offline and pre-calculating values when inserting them into the table, instead of applying it in SELECTS.
|
||||
Even in these cases, we recommend applying the function offline and pre-calculating values when inserting them into the table, instead of applying it in `SELECT` queries.
|
||||
|
||||
**Arguments**
|
||||
|
||||
- `s` — Input string for SHA hash calculation. [String](../data-types/string.md).
|
||||
|
||||
**Returned value**
|
||||
|
||||
- SHA hash as a hex-unencoded FixedString. SHA-1 returns as FixedString(20), SHA-224 as FixedString(28), SHA-256 — FixedString(32), SHA-512 — FixedString(64).
|
||||
|
||||
Type: [FixedString](../data-types/fixedstring.md).
|
||||
|
||||
**Example**
|
||||
|
||||
Use the [hex](../functions/encoding-functions.md#hex) function to represent the result as a hex-encoded string.
|
||||
|
||||
Query:
|
||||
|
||||
``` sql
|
||||
SELECT hex(SHA1('abc'));
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
``` text
|
||||
┌─hex(SHA1('abc'))─────────────────────────┐
|
||||
│ A9993E364706816ABA3E25717850C26C9CD0D89D │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## URLHash(url\[, N\]) {#urlhashurl-n}
|
||||
|
||||
|
@ -10,7 +10,7 @@ A set of queries that allow changing the table structure.
|
||||
Syntax:
|
||||
|
||||
``` sql
|
||||
ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN ...
|
||||
ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|RENAME|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN ...
|
||||
```
|
||||
|
||||
In the query, specify a list of one or more comma-separated actions.
|
||||
@ -25,6 +25,7 @@ The following actions are supported:
|
||||
- [COMMENT COLUMN](#alter_comment-column) — Adds a text comment to the column.
|
||||
- [MODIFY COLUMN](#alter_modify-column) — Changes column’s type, default expression and TTL.
|
||||
- [MODIFY COLUMN REMOVE](#modify-remove) — Removes one of the column properties.
|
||||
- [MATERIALIZE COLUMN](#materialize-column) — Materializes the column in the parts where the column is missing.
|
||||
|
||||
These actions are described in detail below.
|
||||
|
||||
@ -193,6 +194,39 @@ ALTER TABLE table_with_ttl MODIFY COLUMN column_ttl REMOVE TTL;
|
||||
|
||||
- [REMOVE TTL](ttl.md).
|
||||
|
||||
## MATERIALIZE COLUMN {#materialize-column}
|
||||
|
||||
Materializes the column in the parts where the column is missing. This is useful in case of creating a new column with complicated `DEFAULT` or `MATERIALIZED` expression. Calculation of the column directly on `SELECT` query can cause bigger request execution time, so it is reasonable to use `MATERIALIZE COLUMN` for such columns. To perform same manipulation for existing column, use `FINAL` modifier.
|
||||
|
||||
Syntax:
|
||||
|
||||
```sql
|
||||
ALTER TABLE table MATERIALIZE COLUMN col [FINAL];
|
||||
```
|
||||
|
||||
**Example**
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS tmp;
|
||||
SET mutations_sync = 2;
|
||||
CREATE TABLE tmp (x Int64) ENGINE = MergeTree() ORDER BY tuple() PARTITION BY tuple();
|
||||
INSERT INTO tmp SELECT * FROM system.numbers LIMIT 10;
|
||||
ALTER TABLE tmp ADD COLUMN s String MATERIALIZED toString(x);
|
||||
SELECT groupArray(x), groupArray(s) FROM tmp;
|
||||
```
|
||||
|
||||
**Result:**
|
||||
|
||||
```sql
|
||||
┌─groupArray(x)─────────┬─groupArray(s)─────────────────────────────┐
|
||||
│ [0,1,2,3,4,5,6,7,8,9] │ ['0','1','2','3','4','5','6','7','8','9'] │
|
||||
└───────────────────────┴───────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**See Also**
|
||||
|
||||
- [MATERIALIZED](../../statements/create/table.md#materialized).
|
||||
|
||||
## Limitations {#alter-query-limitations}
|
||||
|
||||
The `ALTER` query lets you create and delete separate elements (columns) in nested data structures, but not whole nested data structures. To add a nested data structure, you can add columns with a name like `name.nested_name` and the type `Array(T)`. A nested data structure is equivalent to multiple array columns with a name that has the same prefix before the dot.
|
||||
|
@ -50,14 +50,13 @@ When creating a materialized view with `TO [db].[table]`, you must not use `POPU
|
||||
A materialized view is implemented as follows: when inserting data to the table specified in `SELECT`, part of the inserted data is converted by this `SELECT` query, and the result is inserted in the view.
|
||||
|
||||
!!! important "Important"
|
||||
Materialized views in ClickHouse use **column names** instead of column order during insertion into destination table. If some column names are not present in `SELECT`'s result ClickHouse will use a default value, even if column is not `Nullable`. A safe practice would be to add aliases for every column when using Materialized views.
|
||||
Materialized views in ClickHouse use **column names** instead of column order during insertion into destination table. If some column names are not present in the `SELECT` query result, ClickHouse uses a default value, even if the column is not [Nullable](../../data-types/nullable.md). A safe practice would be to add aliases for every column when using Materialized views.
|
||||
|
||||
!!! important "Important"
|
||||
Materialized views in ClickHouse are implemented more like insert triggers. If there’s some aggregation in the view query, it’s applied only to the batch of freshly inserted data. Any changes to existing data of source table (like update, delete, drop partition, etc.) does not change the materialized view.
|
||||
|
||||
If you specify `POPULATE`, the existing table data is inserted in the view when creating it, as if making a `CREATE TABLE ... AS SELECT ...` . Otherwise, the query contains only the data inserted in the table after creating the view. We **do not recommend** using POPULATE, since data inserted in the table during the view creation will not be inserted in it.
|
||||
If you specify `POPULATE`, the existing table data is inserted into the view when creating it, as if making a `CREATE TABLE ... AS SELECT ...` . Otherwise, the query contains only the data inserted in the table after creating the view. We **do not recommend** using `POPULATE`, since data inserted in the table during the view creation will not be inserted in it.
|
||||
|
||||
A `SELECT` query can contain `DISTINCT`, `GROUP BY`, `ORDER BY`, `LIMIT`… Note that the corresponding conversions are performed independently on each block of inserted data. For example, if `GROUP BY` is set, data is aggregated during insertion, but only within a single packet of inserted data. The data won’t be further aggregated. The exception is when using an `ENGINE` that independently performs data aggregation, such as `SummingMergeTree`.
|
||||
A `SELECT` query can contain `DISTINCT`, `GROUP BY`, `ORDER BY`, `LIMIT`. Note that the corresponding conversions are performed independently on each block of inserted data. For example, if `GROUP BY` is set, data is aggregated during insertion, but only within a single packet of inserted data. The data won’t be further aggregated. The exception is when using an `ENGINE` that independently performs data aggregation, such as `SummingMergeTree`.
|
||||
|
||||
The execution of [ALTER](../../../sql-reference/statements/alter/view.md) queries on materialized views has limitations, so they might be inconvenient. If the materialized view uses the construction `TO [db.]name`, you can `DETACH` the view, run `ALTER` for the target table, and then `ATTACH` the previously detached (`DETACH`) view.
|
||||
|
||||
|
@ -668,7 +668,7 @@ TTL d + INTERVAL 1 MONTH GROUP BY k1, k2 SET x = max(x), y = min(y);
|
||||
- `policy_name_N` — название политики. Названия политик должны быть уникальны.
|
||||
- `volume_name_N` — название тома. Названия томов должны быть уникальны.
|
||||
- `disk` — диск, находящийся внутри тома.
|
||||
- `max_data_part_size_bytes` — максимальный размер куска данных, который может находится на любом из дисков этого тома.
|
||||
- `max_data_part_size_bytes` — максимальный размер куска данных, который может находится на любом из дисков этого тома. Если в результате слияния размер куска ожидается больше, чем max_data_part_size_bytes, то этот кусок будет записан в следующий том. В основном эта функция позволяет хранить новые / мелкие куски на горячем (SSD) томе и перемещать их на холодный (HDD) том, когда они достигают большого размера. Не используйте этот параметр, если политика имеет только один том.
|
||||
- `move_factor` — доля доступного свободного места на томе, если места становится меньше, то данные начнут перемещение на следующий том, если он есть (по умолчанию 0.1).
|
||||
- `prefer_not_to_merge` — Отключает слияние кусков данных, хранящихся на данном томе. Если данная настройка включена, то слияние данных, хранящихся на данном томе, не допускается. Это позволяет контролировать работу ClickHouse с медленными дисками.
|
||||
|
||||
|
@ -141,20 +141,49 @@ SELECT groupBitXor(cityHash64(*)) FROM table
|
||||
Вычисляет 64-битный хэш-код от целого числа любого типа.
|
||||
Работает быстрее, чем intHash32. Качество среднее.
|
||||
|
||||
## SHA1 {#sha1}
|
||||
## SHA1, SHA224, SHA256, SHA512 {#sha}
|
||||
|
||||
## SHA224 {#sha224}
|
||||
Вычисляет SHA-1, SHA-224, SHA-256, SHA-512 хеш строки и возвращает полученный набор байт в виде [FixedString](../data-types/fixedstring.md).
|
||||
|
||||
## SHA256 {#sha256}
|
||||
**Синтаксис**
|
||||
|
||||
## SHA384 {#sha384}
|
||||
``` sql
|
||||
SHA1('s')
|
||||
...
|
||||
SHA512('s')
|
||||
```
|
||||
|
||||
## SHA512 {#sha512}
|
||||
Функция работает достаточно медленно (SHA-1 — примерно 5 миллионов коротких строк в секунду на одном процессорном ядре, SHA-224 и SHA-256 — примерно 2.2 миллионов).
|
||||
Рекомендуется использовать эти функции лишь в тех случаях, когда вам нужна конкретная хеш-функция и вы не можете её выбрать.
|
||||
Даже в этих случаях рекомендуется применять функцию офлайн — заранее вычисляя значения при вставке в таблицу, вместо того чтобы применять её при выполнении `SELECT`.
|
||||
|
||||
Вычисляет SHA-1, SHA-224, SHA-256 от строки и возвращает полученный набор байт в виде FixedString(20), FixedString(28), FixedString(32), FixedLength(48) или FixedString(64).
|
||||
Функция работает достаточно медленно (SHA-1 - примерно 5 миллионов коротких строк в секунду на одном процессорном ядре, SHA-224 и SHA-256 - примерно 2.2 миллионов).
|
||||
Рекомендуется использовать эти функции лишь в тех случаях, когда вам нужна конкретная хэш-функция и вы не можете её выбрать.
|
||||
Даже в этих случаях, рекомендуется применять функцию оффлайн - заранее вычисляя значения при вставке в таблицу, вместо того, чтобы применять её при SELECT-ах.
|
||||
**Параметры**
|
||||
|
||||
- `s` — входная строка для вычисления хеша SHA. [String](../data-types/string.md).
|
||||
|
||||
**Возвращаемое значение**
|
||||
|
||||
- Хеш SHA в виде шестнадцатеричной некодированной строки FixedString. SHA-1 хеш как FixedString(20), SHA-224 как FixedString(28), SHA-256 — FixedString(32), SHA-512 — FixedString(64).
|
||||
|
||||
Тип: [FixedString](../data-types/fixedstring.md).
|
||||
|
||||
**Пример**
|
||||
|
||||
Используйте функцию [hex](../functions/encoding-functions.md#hex) для представления результата в виде строки с шестнадцатеричной кодировкой.
|
||||
|
||||
Запрос:
|
||||
|
||||
``` sql
|
||||
SELECT hex(SHA1('abc'));
|
||||
```
|
||||
|
||||
Результат:
|
||||
|
||||
``` text
|
||||
┌─hex(SHA1('abc'))─────────────────────────┐
|
||||
│ A9993E364706816ABA3E25717850C26C9CD0D89D │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## URLHash(url\[, N\]) {#urlhashurl-n}
|
||||
|
||||
|
@ -10,7 +10,7 @@ toc_title: "Манипуляции со столбцами"
|
||||
Синтаксис:
|
||||
|
||||
``` sql
|
||||
ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN ...
|
||||
ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|RENAME|CLEAR|COMMENT|MODIFY|MATERIALIZE COLUMN ...
|
||||
```
|
||||
|
||||
В запросе можно указать сразу несколько действий над одной таблицей через запятую.
|
||||
@ -20,11 +20,12 @@ ALTER TABLE [db].name [ON CLUSTER cluster] ADD|DROP|CLEAR|COMMENT|MODIFY COLUMN
|
||||
|
||||
- [ADD COLUMN](#alter_add-column) — добавляет столбец в таблицу;
|
||||
- [DROP COLUMN](#alter_drop-column) — удаляет столбец;
|
||||
- [RENAME COLUMN](#alter_rename-column) — переименовывает существующий столбец.
|
||||
- [RENAME COLUMN](#alter_rename-column) — переименовывает существующий столбец;
|
||||
- [CLEAR COLUMN](#alter_clear-column) — сбрасывает все значения в столбце для заданной партиции;
|
||||
- [COMMENT COLUMN](#alter_comment-column) — добавляет комментарий к столбцу;
|
||||
- [MODIFY COLUMN](#alter_modify-column) — изменяет тип столбца, выражение для значения по умолчанию и TTL.
|
||||
- [MODIFY COLUMN REMOVE](#modify-remove) — удаляет какое-либо из свойств столбца.
|
||||
- [MODIFY COLUMN](#alter_modify-column) — изменяет тип столбца, выражение для значения по умолчанию и TTL;
|
||||
- [MODIFY COLUMN REMOVE](#modify-remove) — удаляет какое-либо из свойств столбца;
|
||||
- [MATERIALIZE COLUMN](#materialize-column) — делает столбец материализованным (`MATERIALIZED`) в кусках, в которых отсутствуют значения.
|
||||
|
||||
Подробное описание для каждого действия приведено ниже.
|
||||
|
||||
@ -193,6 +194,35 @@ ALTER TABLE table_with_ttl MODIFY COLUMN column_ttl REMOVE TTL;
|
||||
|
||||
- [REMOVE TTL](ttl.md).
|
||||
|
||||
## MATERIALIZE COLUMN {#materialize-column}
|
||||
|
||||
Материализует столбец таблицы в кусках, в которых отсутствуют значения. Используется, если необходимо создать новый столбец со сложным материализованным выражением или выражением для заполнения по умолчанию (`DEFAULT`), потому как вычисление такого столбца прямо во время выполнения запроса `SELECT` оказывается ощутимо затратным. Чтобы совершить ту же операцию для существующего столбца, используйте модификатор `FINAL`.
|
||||
|
||||
Синтаксис:
|
||||
|
||||
```sql
|
||||
ALTER TABLE table MATERIALIZE COLUMN col [FINAL];
|
||||
```
|
||||
|
||||
**Пример**
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS tmp;
|
||||
SET mutations_sync = 2;
|
||||
CREATE TABLE tmp (x Int64) ENGINE = MergeTree() ORDER BY tuple() PARTITION BY tuple();
|
||||
INSERT INTO tmp SELECT * FROM system.numbers LIMIT 10;
|
||||
ALTER TABLE tmp ADD COLUMN s String MATERIALIZED toString(x);
|
||||
SELECT groupArray(x), groupArray(s) FROM tmp;
|
||||
```
|
||||
|
||||
**Результат:**
|
||||
|
||||
```sql
|
||||
┌─groupArray(x)─────────┬─groupArray(s)─────────────────────────────┐
|
||||
│ [0,1,2,3,4,5,6,7,8,9] │ ['0','1','2','3','4','5','6','7','8','9'] │
|
||||
└───────────────────────┴───────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Ограничения запроса ALTER {#ogranicheniia-zaprosa-alter}
|
||||
|
||||
Запрос `ALTER` позволяет создавать и удалять отдельные элементы (столбцы) вложенных структур данных, но не вложенные структуры данных целиком. Для добавления вложенной структуры данных, вы можете добавить столбцы с именем вида `name.nested_name` и типом `Array(T)` - вложенная структура данных полностью эквивалентна нескольким столбцам-массивам с именем, имеющим одинаковый префикс до точки.
|
||||
|
@ -48,9 +48,12 @@ CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]na
|
||||
Материализованное представление устроено следующим образом: при вставке данных в таблицу, указанную в SELECT-е, кусок вставляемых данных преобразуется этим запросом SELECT, и полученный результат вставляется в представление.
|
||||
|
||||
!!! important "Важно"
|
||||
|
||||
Материализованные представления в ClickHouse используют **имена столбцов** вместо порядка следования столбцов при вставке в целевую таблицу. Если в результатах запроса `SELECT` некоторые имена столбцов отсутствуют, то ClickHouse использует значение по умолчанию, даже если столбец не является [Nullable](../../data-types/nullable.md). Безопасной практикой при использовании материализованных представлений считается добавление псевдонимов для каждого столбца.
|
||||
|
||||
Материализованные представления в ClickHouse больше похожи на `after insert` триггеры. Если в запросе материализованного представления есть агрегирование, оно применяется только к вставляемому блоку записей. Любые изменения существующих данных исходной таблицы (например обновление, удаление, удаление раздела и т.д.) не изменяют материализованное представление.
|
||||
|
||||
Если указано `POPULATE`, то при создании представления, в него будут вставлены имеющиеся данные таблицы, как если бы был сделан запрос `CREATE TABLE ... AS SELECT ...` . Иначе, представление будет содержать только данные, вставляемые в таблицу после создания представления. Не рекомендуется использовать POPULATE, так как вставляемые в таблицу данные во время создания представления, не попадут в него.
|
||||
Если указано `POPULATE`, то при создании представления в него будут добавлены данные, уже содержащиеся в исходной таблице, как если бы был сделан запрос `CREATE TABLE ... AS SELECT ...` . Если `POPULATE` не указано, представление будет содержать только данные, добавленные в таблицу после создания представления. Использовать `POPULATE` не рекомендуется, так как в представление не попадут данные, добавляемые в таблицу во время создания представления.
|
||||
|
||||
Запрос `SELECT` может содержать `DISTINCT`, `GROUP BY`, `ORDER BY`, `LIMIT`… Следует иметь ввиду, что соответствующие преобразования будут выполняться независимо, на каждый блок вставляемых данных. Например, при наличии `GROUP BY`, данные будут агрегироваться при вставке, но только в рамках одной пачки вставляемых данных. Далее, данные не будут доагрегированы. Исключение - использование ENGINE, производящего агрегацию данных самостоятельно, например, `SummingMergeTree`.
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include "Client.h"
|
||||
#include "Core/Protocol.h"
|
||||
|
||||
#include <base/argsToConfig.h>
|
||||
#include <base/find_symbols.h>
|
||||
@ -377,6 +378,9 @@ std::vector<String> Client::loadWarningMessages()
|
||||
case Protocol::Server::EndOfStream:
|
||||
return messages;
|
||||
|
||||
case Protocol::Server::ProfileEvents:
|
||||
continue;
|
||||
|
||||
default:
|
||||
throw Exception(ErrorCodes::UNKNOWN_PACKET_FROM_SERVER, "Unknown packet {} from server {}",
|
||||
packet.type, connection->getDescription());
|
||||
|
@ -9,6 +9,10 @@
|
||||
#include <base/LocalDate.h>
|
||||
#include <base/LineReader.h>
|
||||
#include <base/scope_guard_safe.h>
|
||||
#include "Columns/ColumnString.h"
|
||||
#include "Columns/ColumnsNumber.h"
|
||||
#include "Core/Block.h"
|
||||
#include "Core/Protocol.h"
|
||||
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
# include <Common/config_version.h>
|
||||
@ -72,6 +76,12 @@ namespace ErrorCodes
|
||||
|
||||
}
|
||||
|
||||
namespace ProfileEvents
|
||||
{
|
||||
extern const Event UserTimeMicroseconds;
|
||||
extern const Event SystemTimeMicroseconds;
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
@ -611,6 +621,10 @@ bool ClientBase::receiveAndProcessPacket(ASTPtr parsed_query, bool cancelled)
|
||||
onEndOfStream();
|
||||
return false;
|
||||
|
||||
case Protocol::Server::ProfileEvents:
|
||||
onProfileEvents(packet.block);
|
||||
return true;
|
||||
|
||||
default:
|
||||
throw Exception(
|
||||
ErrorCodes::UNKNOWN_PACKET_FROM_SERVER, "Unknown packet {} from server {}", packet.type, connection->getDescription());
|
||||
@ -651,6 +665,45 @@ void ClientBase::onEndOfStream()
|
||||
}
|
||||
|
||||
|
||||
void ClientBase::onProfileEvents(Block & block)
|
||||
{
|
||||
const auto rows = block.rows();
|
||||
if (rows == 0)
|
||||
return;
|
||||
const auto & array_thread_id = typeid_cast<const ColumnUInt64 &>(*block.getByName("thread_id").column).getData();
|
||||
const auto & names = typeid_cast<const ColumnString &>(*block.getByName("name").column);
|
||||
const auto & host_names = typeid_cast<const ColumnString &>(*block.getByName("host_name").column);
|
||||
const auto & array_values = typeid_cast<const ColumnUInt64 &>(*block.getByName("value").column).getData();
|
||||
|
||||
const auto * user_time_name = ProfileEvents::getName(ProfileEvents::UserTimeMicroseconds);
|
||||
const auto * system_time_name = ProfileEvents::getName(ProfileEvents::SystemTimeMicroseconds);
|
||||
|
||||
HostToThreadTimesMap thread_times;
|
||||
for (size_t i = 0; i < rows; ++i)
|
||||
{
|
||||
auto thread_id = array_thread_id[i];
|
||||
auto host_name = host_names.getDataAt(i).toString();
|
||||
if (thread_id != 0)
|
||||
progress_indication.addThreadIdToList(host_name, thread_id);
|
||||
auto event_name = names.getDataAt(i);
|
||||
auto value = array_values[i];
|
||||
if (event_name == user_time_name)
|
||||
{
|
||||
thread_times[host_name][thread_id].user_ms = value;
|
||||
}
|
||||
else if (event_name == system_time_name)
|
||||
{
|
||||
thread_times[host_name][thread_id].system_ms = value;
|
||||
}
|
||||
else if (event_name == MemoryTracker::USAGE_EVENT_NAME)
|
||||
{
|
||||
thread_times[host_name][thread_id].memory_usage = value;
|
||||
}
|
||||
}
|
||||
progress_indication.updateThreadEventData(thread_times);
|
||||
}
|
||||
|
||||
|
||||
/// Flush all buffers.
|
||||
void ClientBase::resetOutput()
|
||||
{
|
||||
|
@ -114,6 +114,7 @@ private:
|
||||
void onReceiveExceptionFromServer(std::unique_ptr<Exception> && e);
|
||||
void onProfileInfo(const BlockStreamProfileInfo & profile_info);
|
||||
void onEndOfStream();
|
||||
void onProfileEvents(Block & block);
|
||||
|
||||
void sendData(Block & sample, const ColumnsDescription & columns_description, ASTPtr parsed_query);
|
||||
void sendDataFrom(ReadBuffer & buf, Block & sample,
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <memory>
|
||||
#include <Poco/Net/NetException.h>
|
||||
#include <Core/Defines.h>
|
||||
#include <Core/Settings.h>
|
||||
@ -21,6 +22,7 @@
|
||||
#include <Common/StringUtils/StringUtils.h>
|
||||
#include <Common/OpenSSLHelpers.h>
|
||||
#include <Common/randomSeed.h>
|
||||
#include "Core/Block.h"
|
||||
#include <Interpreters/ClientInfo.h>
|
||||
#include <Compression/CompressionFactory.h>
|
||||
#include <Processors/Pipe.h>
|
||||
@ -535,6 +537,7 @@ void Connection::sendQuery(
|
||||
maybe_compressed_out.reset();
|
||||
block_in.reset();
|
||||
block_logs_in.reset();
|
||||
block_profile_events_in.reset();
|
||||
block_out.reset();
|
||||
|
||||
/// Send empty block which means end of data.
|
||||
@ -870,6 +873,10 @@ Packet Connection::receivePacket()
|
||||
case Protocol::Server::ReadTaskRequest:
|
||||
return res;
|
||||
|
||||
case Protocol::Server::ProfileEvents:
|
||||
res.block = receiveProfileEvents();
|
||||
return res;
|
||||
|
||||
default:
|
||||
/// In unknown state, disconnect - to not leave unsynchronised connection.
|
||||
disconnect();
|
||||
@ -923,6 +930,13 @@ Block Connection::receiveDataImpl(NativeReader & reader)
|
||||
}
|
||||
|
||||
|
||||
Block Connection::receiveProfileEvents()
|
||||
{
|
||||
initBlockProfileEventsInput();
|
||||
return receiveDataImpl(*block_profile_events_in);
|
||||
}
|
||||
|
||||
|
||||
void Connection::initInputBuffers()
|
||||
{
|
||||
|
||||
@ -956,6 +970,15 @@ void Connection::initBlockLogsInput()
|
||||
}
|
||||
|
||||
|
||||
void Connection::initBlockProfileEventsInput()
|
||||
{
|
||||
if (!block_profile_events_in)
|
||||
{
|
||||
block_profile_events_in = std::make_unique<NativeReader>(*in, server_revision);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Connection::setDescription()
|
||||
{
|
||||
auto resolved_address = getResolvedAddress();
|
||||
|
@ -206,6 +206,7 @@ private:
|
||||
std::shared_ptr<ReadBuffer> maybe_compressed_in;
|
||||
std::unique_ptr<NativeReader> block_in;
|
||||
std::unique_ptr<NativeReader> block_logs_in;
|
||||
std::unique_ptr<NativeReader> block_profile_events_in;
|
||||
|
||||
/// Where to write data for INSERT.
|
||||
std::shared_ptr<WriteBuffer> maybe_compressed_out;
|
||||
@ -249,6 +250,7 @@ private:
|
||||
Block receiveData();
|
||||
Block receiveLogData();
|
||||
Block receiveDataImpl(NativeReader & reader);
|
||||
Block receiveProfileEvents();
|
||||
|
||||
std::vector<String> receiveMultistringMessage(UInt64 msg_type) const;
|
||||
std::unique_ptr<Exception> receiveException() const;
|
||||
@ -258,6 +260,7 @@ private:
|
||||
void initInputBuffers();
|
||||
void initBlockInput();
|
||||
void initBlockLogsInput();
|
||||
void initBlockProfileEventsInput();
|
||||
|
||||
[[noreturn]] void throwUnexpectedPacket(UInt64 packet_type, const char * expected) const;
|
||||
};
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include "Core/Protocol.h"
|
||||
#if defined(OS_LINUX)
|
||||
|
||||
#include <Client/HedgedConnections.h>
|
||||
@ -412,6 +413,7 @@ Packet HedgedConnections::receivePacketFromReplica(const ReplicaLocation & repli
|
||||
case Protocol::Server::Totals:
|
||||
case Protocol::Server::Extremes:
|
||||
case Protocol::Server::Log:
|
||||
case Protocol::Server::ProfileEvents:
|
||||
replica_with_last_received_packet = replica_location;
|
||||
break;
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
#include <Processors/Executors/PushingPipelineExecutor.h>
|
||||
#include <Processors/Executors/PushingAsyncPipelineExecutor.h>
|
||||
#include <Storages/IStorage.h>
|
||||
#include "Core/Protocol.h"
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -328,6 +329,7 @@ Packet LocalConnection::receivePacket()
|
||||
case Protocol::Server::Extremes: [[fallthrough]];
|
||||
case Protocol::Server::Log: [[fallthrough]];
|
||||
case Protocol::Server::Data:
|
||||
case Protocol::Server::ProfileEvents:
|
||||
{
|
||||
if (state->block && state->block.value())
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include <IO/ConnectionTimeouts.h>
|
||||
#include <IO/Operators.h>
|
||||
#include <Common/thread_local_rng.h>
|
||||
#include "Core/Protocol.h"
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -320,6 +321,7 @@ Packet MultiplexedConnections::receivePacketUnlocked(AsyncCallback async_callbac
|
||||
case Protocol::Server::Totals:
|
||||
case Protocol::Server::Extremes:
|
||||
case Protocol::Server::Log:
|
||||
case Protocol::Server::ProfileEvents:
|
||||
break;
|
||||
|
||||
case Protocol::Server::EndOfStream:
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Common/typeid_cast.h>
|
||||
#include <Common/Macros.h>
|
||||
#include "Core/Protocol.h"
|
||||
#include <IO/Operators.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <TableFunctions/TableFunctionFactory.h>
|
||||
@ -162,6 +163,8 @@ void Suggest::fetch(IServerConnection & connection, const ConnectionTimeouts & t
|
||||
continue;
|
||||
case Protocol::Server::Log:
|
||||
continue;
|
||||
case Protocol::Server::ProfileEvents:
|
||||
continue;
|
||||
|
||||
case Protocol::Server::Exception:
|
||||
packet.exception->rethrow();
|
||||
|
@ -41,6 +41,12 @@ namespace CurrentMetrics
|
||||
values[metric].store(value, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/// Get value of specified metric.
|
||||
inline Value get(Metric metric)
|
||||
{
|
||||
return values[metric].load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
/// Add value for specified metric. You must subtract value later; or see class Increment below.
|
||||
inline void add(Metric metric, Value value = 1)
|
||||
{
|
||||
|
@ -91,6 +91,24 @@ std::shared_ptr<InternalTextLogsQueue> CurrentThread::getInternalTextLogsQueue()
|
||||
return current_thread->getInternalTextLogsQueue();
|
||||
}
|
||||
|
||||
void CurrentThread::attachInternalProfileEventsQueue(const InternalProfileEventsQueuePtr & queue)
|
||||
{
|
||||
if (unlikely(!current_thread))
|
||||
return;
|
||||
current_thread->attachInternalProfileEventsQueue(queue);
|
||||
}
|
||||
|
||||
InternalProfileEventsQueuePtr CurrentThread::getInternalProfileEventsQueue()
|
||||
{
|
||||
if (unlikely(!current_thread))
|
||||
return nullptr;
|
||||
|
||||
if (current_thread->getCurrentState() == ThreadStatus::ThreadState::Died)
|
||||
return nullptr;
|
||||
|
||||
return current_thread->getInternalProfileEventsQueue();
|
||||
}
|
||||
|
||||
ThreadGroupStatusPtr CurrentThread::getGroup()
|
||||
{
|
||||
if (unlikely(!current_thread))
|
||||
|
@ -46,6 +46,9 @@ public:
|
||||
LogsLevel client_logs_level);
|
||||
static std::shared_ptr<InternalTextLogsQueue> getInternalTextLogsQueue();
|
||||
|
||||
static void attachInternalProfileEventsQueue(const InternalProfileEventsQueuePtr & queue);
|
||||
static InternalProfileEventsQueuePtr getInternalProfileEventsQueue();
|
||||
|
||||
static void setFatalErrorCallback(std::function<void()> callback);
|
||||
|
||||
/// Makes system calls to update ProfileEvents that contain info from rusage and taskstats
|
||||
|
@ -588,6 +588,7 @@
|
||||
M(618, LZ4_DECODER_FAILED) \
|
||||
M(619, POSTGRESQL_REPLICATION_INTERNAL_ERROR) \
|
||||
M(620, QUERY_NOT_ALLOWED) \
|
||||
M(621, CANNOT_NORMALIZE_STRING) \
|
||||
\
|
||||
M(999, KEEPER_EXCEPTION) \
|
||||
M(1000, POCO_EXCEPTION) \
|
||||
|
@ -64,6 +64,9 @@ private:
|
||||
void setOrRaiseProfilerLimit(Int64 value);
|
||||
|
||||
public:
|
||||
|
||||
static constexpr auto USAGE_EVENT_NAME = "MemoryTrackerUsage";
|
||||
|
||||
explicit MemoryTracker(VariableContext level_ = VariableContext::Thread);
|
||||
explicit MemoryTracker(MemoryTracker * parent_, VariableContext level_ = VariableContext::Thread);
|
||||
|
||||
@ -143,6 +146,11 @@ public:
|
||||
metric.store(metric_, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
CurrentMetrics::Metric getMetric()
|
||||
{
|
||||
return metric.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void setDescription(const char * description)
|
||||
{
|
||||
description_ptr.store(description, std::memory_order_relaxed);
|
||||
|
@ -1,10 +1,41 @@
|
||||
#include "ProgressIndication.h"
|
||||
#include <cstddef>
|
||||
#include <numeric>
|
||||
#include <cmath>
|
||||
#include <IO/WriteBufferFromFileDescriptor.h>
|
||||
#include <base/types.h>
|
||||
#include <Common/TerminalSize.h>
|
||||
#include <Common/UnicodeBar.h>
|
||||
#include <Databases/DatabaseMemory.h>
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr UInt64 ZERO = 0;
|
||||
|
||||
UInt64 calculateNewCoresNumber(DB::ThreadIdToTimeMap const & prev, DB::ThreadIdToTimeMap const& next)
|
||||
{
|
||||
if (next.find(ZERO) == next.end())
|
||||
return ZERO;
|
||||
auto accumulated = std::accumulate(next.cbegin(), next.cend(), ZERO,
|
||||
[&prev](UInt64 acc, auto const & elem)
|
||||
{
|
||||
if (elem.first == ZERO)
|
||||
return acc;
|
||||
auto thread_time = elem.second.time();
|
||||
auto it = prev.find(elem.first);
|
||||
if (it != prev.end())
|
||||
thread_time -= it->second.time();
|
||||
return acc + thread_time;
|
||||
});
|
||||
|
||||
auto elapsed = next.at(ZERO).time() - (prev.contains(ZERO) ? prev.at(ZERO).time() : ZERO);
|
||||
if (elapsed == ZERO)
|
||||
return ZERO;
|
||||
return (accumulated + elapsed - 1) / elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
@ -29,6 +60,8 @@ void ProgressIndication::resetProgress()
|
||||
show_progress_bar = false;
|
||||
written_progress_chars = 0;
|
||||
write_progress_on_update = false;
|
||||
host_active_cores.clear();
|
||||
thread_data.clear();
|
||||
}
|
||||
|
||||
void ProgressIndication::setFileProgressCallback(ContextMutablePtr context, bool write_progress_on_update_)
|
||||
@ -43,6 +76,56 @@ void ProgressIndication::setFileProgressCallback(ContextMutablePtr context, bool
|
||||
});
|
||||
}
|
||||
|
||||
void ProgressIndication::addThreadIdToList(String const & host, UInt64 thread_id)
|
||||
{
|
||||
auto & thread_to_times = thread_data[host];
|
||||
if (thread_to_times.contains(thread_id))
|
||||
return;
|
||||
thread_to_times[thread_id] = {};
|
||||
}
|
||||
|
||||
void ProgressIndication::updateThreadEventData(HostToThreadTimesMap & new_thread_data)
|
||||
{
|
||||
for (auto & new_host_map : new_thread_data)
|
||||
{
|
||||
auto & host_map = thread_data[new_host_map.first];
|
||||
auto new_cores = calculateNewCoresNumber(host_map, new_host_map.second);
|
||||
host_active_cores[new_host_map.first] = new_cores;
|
||||
host_map = std::move(new_host_map.second);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ProgressIndication::getUsedThreadsCount() const
|
||||
{
|
||||
return std::accumulate(thread_data.cbegin(), thread_data.cend(), 0,
|
||||
[] (size_t acc, auto const & threads)
|
||||
{
|
||||
return acc + threads.second.size();
|
||||
});
|
||||
}
|
||||
|
||||
UInt64 ProgressIndication::getApproximateCoresNumber() const
|
||||
{
|
||||
return std::accumulate(host_active_cores.cbegin(), host_active_cores.cend(), ZERO,
|
||||
[](UInt64 acc, auto const & elem)
|
||||
{
|
||||
return acc + elem.second;
|
||||
});
|
||||
}
|
||||
|
||||
UInt64 ProgressIndication::getMemoryUsage() const
|
||||
{
|
||||
return std::accumulate(thread_data.cbegin(), thread_data.cend(), ZERO,
|
||||
[](UInt64 acc, auto const & host_data)
|
||||
{
|
||||
return acc + std::accumulate(host_data.second.cbegin(), host_data.second.cend(), ZERO,
|
||||
[](UInt64 memory, auto const & data)
|
||||
{
|
||||
return memory + data.second.memory_usage;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void ProgressIndication::writeFinalProgress()
|
||||
{
|
||||
if (progress.read_rows < 1000)
|
||||
@ -148,6 +231,23 @@ void ProgressIndication::writeProgress()
|
||||
message << ' ' << (99 * current_count / max_count) << '%';
|
||||
}
|
||||
|
||||
// If approximate cores number is known, display it.
|
||||
auto cores_number = getApproximateCoresNumber();
|
||||
if (cores_number != 0)
|
||||
{
|
||||
// Calculated cores number may be not accurate
|
||||
// so it's better to print min(threads, cores).
|
||||
UInt64 threads_number = getUsedThreadsCount();
|
||||
message << " Running " << threads_number << " threads on "
|
||||
<< std::min(cores_number, threads_number) << " cores";
|
||||
|
||||
auto memory_usage = getMemoryUsage();
|
||||
if (memory_usage != 0)
|
||||
message << " with " << formatReadableSizeWithDecimalSuffix(memory_usage) << " RAM used.";
|
||||
else
|
||||
message << ".";
|
||||
}
|
||||
|
||||
message << CLEAR_TO_END_OF_LINE;
|
||||
++increment;
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <IO/Progress.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <base/types.h>
|
||||
#include <Common/Stopwatch.h>
|
||||
|
||||
|
||||
@ -11,6 +14,18 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
struct ThreadEventData
|
||||
{
|
||||
UInt64 time() const noexcept { return user_ms + system_ms; }
|
||||
|
||||
UInt64 user_ms = 0;
|
||||
UInt64 system_ms = 0;
|
||||
UInt64 memory_usage = 0;
|
||||
};
|
||||
|
||||
using ThreadIdToTimeMap = std::unordered_map<UInt64, ThreadEventData>;
|
||||
using HostToThreadTimesMap = std::unordered_map<String, ThreadIdToTimeMap>;
|
||||
|
||||
class ProgressIndication
|
||||
{
|
||||
public:
|
||||
@ -41,7 +56,18 @@ public:
|
||||
/// How much seconds passed since query execution start.
|
||||
double elapsedSeconds() const { return watch.elapsedSeconds(); }
|
||||
|
||||
void addThreadIdToList(String const & host, UInt64 thread_id);
|
||||
|
||||
void updateThreadEventData(HostToThreadTimesMap & new_thread_data);
|
||||
|
||||
private:
|
||||
|
||||
size_t getUsedThreadsCount() const;
|
||||
|
||||
UInt64 getApproximateCoresNumber() const;
|
||||
|
||||
UInt64 getMemoryUsage() const;
|
||||
|
||||
/// This flag controls whether to show the progress bar. We start showing it after
|
||||
/// the query has been executing for 0.5 seconds, and is still less than half complete.
|
||||
bool show_progress_bar = false;
|
||||
@ -58,6 +84,9 @@ private:
|
||||
Stopwatch watch;
|
||||
|
||||
bool write_progress_on_update = false;
|
||||
|
||||
std::unordered_map<String, UInt64> host_active_cores;
|
||||
HostToThreadTimesMap thread_data;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <base/getPageSize.h>
|
||||
|
||||
#include <csignal>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -140,6 +141,12 @@ ThreadStatus::~ThreadStatus()
|
||||
/// We've already allocated a little bit more than the limit and cannot track it in the thread memory tracker or its parent.
|
||||
}
|
||||
|
||||
if (thread_group)
|
||||
{
|
||||
std::lock_guard guard(thread_group->mutex);
|
||||
thread_group->threads.erase(this);
|
||||
}
|
||||
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
/// It may cause segfault if query_context was destroyed, but was not detached
|
||||
auto query_context_ptr = query_context.lock();
|
||||
@ -196,6 +203,17 @@ void ThreadStatus::attachInternalTextLogsQueue(const InternalTextLogsQueuePtr &
|
||||
thread_group->client_logs_level = client_logs_level;
|
||||
}
|
||||
|
||||
void ThreadStatus::attachInternalProfileEventsQueue(const InternalProfileEventsQueuePtr & profile_queue)
|
||||
{
|
||||
profile_queue_ptr = profile_queue;
|
||||
|
||||
if (!thread_group)
|
||||
return;
|
||||
|
||||
std::lock_guard lock(thread_group->mutex);
|
||||
thread_group->profile_queue_ptr = profile_queue;
|
||||
}
|
||||
|
||||
void ThreadStatus::setFatalErrorCallback(std::function<void()> callback)
|
||||
{
|
||||
fatal_error_callback = std::move(callback);
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <Common/OpenTelemetryTraceContext.h>
|
||||
#include <Common/ProfileEvents.h>
|
||||
#include <base/StringRef.h>
|
||||
#include <Common/ConcurrentBoundedQueue.h>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
@ -15,6 +16,7 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
namespace Poco
|
||||
@ -42,6 +44,10 @@ class QueryViewsLog;
|
||||
using InternalTextLogsQueuePtr = std::shared_ptr<InternalTextLogsQueue>;
|
||||
using InternalTextLogsQueueWeakPtr = std::weak_ptr<InternalTextLogsQueue>;
|
||||
|
||||
using InternalProfileEventsQueue = ConcurrentBoundedQueue<Block>;
|
||||
using InternalProfileEventsQueuePtr = std::shared_ptr<InternalProfileEventsQueue>;
|
||||
using InternalProfileEventsQueueWeakPtr = std::weak_ptr<InternalProfileEventsQueue>;
|
||||
using ThreadStatusPtr = ThreadStatus *;
|
||||
|
||||
/** Thread group is a collection of threads dedicated to single task
|
||||
* (query or other process like background merge).
|
||||
@ -63,9 +69,11 @@ public:
|
||||
ContextWeakPtr global_context;
|
||||
|
||||
InternalTextLogsQueueWeakPtr logs_queue_ptr;
|
||||
InternalProfileEventsQueueWeakPtr profile_queue_ptr;
|
||||
std::function<void()> fatal_error_callback;
|
||||
|
||||
std::vector<UInt64> thread_ids;
|
||||
std::unordered_set<ThreadStatusPtr> threads;
|
||||
|
||||
/// The first thread created this thread group
|
||||
UInt64 master_thread_id = 0;
|
||||
@ -132,6 +140,8 @@ protected:
|
||||
/// A logs queue used by TCPHandler to pass logs to a client
|
||||
InternalTextLogsQueueWeakPtr logs_queue_ptr;
|
||||
|
||||
InternalProfileEventsQueueWeakPtr profile_queue_ptr;
|
||||
|
||||
bool performance_counters_finalized = false;
|
||||
UInt64 query_start_time_nanoseconds = 0;
|
||||
UInt64 query_start_time_microseconds = 0;
|
||||
@ -206,6 +216,13 @@ public:
|
||||
void attachInternalTextLogsQueue(const InternalTextLogsQueuePtr & logs_queue,
|
||||
LogsLevel client_logs_level);
|
||||
|
||||
InternalProfileEventsQueuePtr getInternalProfileEventsQueue() const
|
||||
{
|
||||
return thread_state == Died ? nullptr : profile_queue_ptr.lock();
|
||||
}
|
||||
|
||||
void attachInternalProfileEventsQueue(const InternalProfileEventsQueuePtr & profile_queue);
|
||||
|
||||
/// Callback that is used to trigger sending fatal error messages to client.
|
||||
void setFatalErrorCallback(std::function<void()> callback);
|
||||
void onFatalError();
|
||||
|
@ -125,10 +125,6 @@ nuraft::ptr<nuraft::buffer> KeeperStateMachine::commit(const uint64_t log_idx, n
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_TEST(log, "Commit request for session {} with type {}, log id {}{}",
|
||||
request_for_session.session_id, toString(request_for_session.request->getOpNum()), log_idx,
|
||||
request_for_session.request->getPath().empty() ? "" : ", path " + request_for_session.request->getPath());
|
||||
|
||||
std::lock_guard lock(storage_and_responses_lock);
|
||||
KeeperStorage::ResponsesForSessions responses_for_sessions = storage->processRequest(request_for_session.request, request_for_session.session_id, log_idx);
|
||||
for (auto & response_for_session : responses_for_sessions)
|
||||
|
@ -105,12 +105,16 @@ namespace MySQLReplication
|
||||
if (query.starts_with("BEGIN") || query.starts_with("COMMIT"))
|
||||
{
|
||||
typ = QUERY_EVENT_MULTI_TXN_FLAG;
|
||||
if (!query.starts_with("COMMIT"))
|
||||
transaction_complete = false;
|
||||
}
|
||||
else if (query.starts_with("XA"))
|
||||
{
|
||||
if (query.starts_with("XA ROLLBACK"))
|
||||
throw ReplicationError("ParseQueryEvent: Unsupported query event:" + query, ErrorCodes::LOGICAL_ERROR);
|
||||
typ = QUERY_EVENT_XA;
|
||||
if (!query.starts_with("XA COMMIT"))
|
||||
transaction_complete = false;
|
||||
}
|
||||
else if (query.starts_with("SAVEPOINT"))
|
||||
{
|
||||
@ -711,9 +715,26 @@ namespace MySQLReplication
|
||||
{
|
||||
switch (event->header.type)
|
||||
{
|
||||
case FORMAT_DESCRIPTION_EVENT:
|
||||
case QUERY_EVENT:
|
||||
case FORMAT_DESCRIPTION_EVENT: {
|
||||
binlog_pos = event->header.log_pos;
|
||||
break;
|
||||
}
|
||||
case QUERY_EVENT: {
|
||||
auto query = std::static_pointer_cast<QueryEvent>(event);
|
||||
if (query->transaction_complete && pending_gtid)
|
||||
{
|
||||
gtid_sets.update(*pending_gtid);
|
||||
pending_gtid.reset();
|
||||
}
|
||||
binlog_pos = event->header.log_pos;
|
||||
break;
|
||||
}
|
||||
case XID_EVENT: {
|
||||
if (pending_gtid)
|
||||
{
|
||||
gtid_sets.update(*pending_gtid);
|
||||
pending_gtid.reset();
|
||||
}
|
||||
binlog_pos = event->header.log_pos;
|
||||
break;
|
||||
}
|
||||
@ -724,9 +745,11 @@ namespace MySQLReplication
|
||||
break;
|
||||
}
|
||||
case GTID_EVENT: {
|
||||
if (pending_gtid)
|
||||
gtid_sets.update(*pending_gtid);
|
||||
auto gtid_event = std::static_pointer_cast<GTIDEvent>(event);
|
||||
binlog_pos = event->header.log_pos;
|
||||
gtid_sets.update(gtid_event->gtid);
|
||||
pending_gtid = gtid_event->gtid;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -792,6 +815,7 @@ namespace MySQLReplication
|
||||
{
|
||||
event = std::make_shared<QueryEvent>(std::move(event_header));
|
||||
event->parseEvent(event_payload);
|
||||
position.update(event);
|
||||
|
||||
auto query = std::static_pointer_cast<QueryEvent>(event);
|
||||
switch (query->typ)
|
||||
@ -803,7 +827,7 @@ namespace MySQLReplication
|
||||
break;
|
||||
}
|
||||
default:
|
||||
position.update(event);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -383,6 +383,7 @@ namespace MySQLReplication
|
||||
String schema;
|
||||
String query;
|
||||
QueryType typ = QUERY_EVENT_DDL;
|
||||
bool transaction_complete = true;
|
||||
|
||||
QueryEvent(EventHeader && header_)
|
||||
: EventBase(std::move(header_)), thread_id(0), exec_time(0), schema_len(0), error_code(0), status_len(0)
|
||||
@ -536,6 +537,9 @@ namespace MySQLReplication
|
||||
void update(BinlogEventPtr event);
|
||||
void update(UInt64 binlog_pos_, const String & binlog_name_, const String & gtid_sets_);
|
||||
void dump(WriteBuffer & out) const;
|
||||
|
||||
private:
|
||||
std::optional<GTID> pending_gtid;
|
||||
};
|
||||
|
||||
class IFlavor : public MySQLProtocol::IMySQLReadPacket
|
||||
|
@ -80,7 +80,8 @@ namespace Protocol
|
||||
ReadTaskRequest = 13, /// String (UUID) describes a request for which next task is needed
|
||||
/// This is such an inverted logic, where server sends requests
|
||||
/// And client returns back response
|
||||
MAX = ReadTaskRequest,
|
||||
ProfileEvents = 14, /// Packet with profile events from server.
|
||||
MAX = ProfileEvents,
|
||||
};
|
||||
|
||||
/// NOTE: If the type of packet argument would be Enum, the comparison packet >= 0 && packet < 10
|
||||
@ -103,7 +104,8 @@ namespace Protocol
|
||||
"Log",
|
||||
"TableColumns",
|
||||
"PartUUIDs",
|
||||
"ReadTaskRequest"
|
||||
"ReadTaskRequest",
|
||||
"ProfileEvents",
|
||||
};
|
||||
return packet <= MAX
|
||||
? data[packet]
|
||||
|
@ -36,6 +36,8 @@
|
||||
|
||||
#define DBMS_MIN_PROTOCOL_VERSION_WITH_DISTRIBUTED_DEPTH 54448
|
||||
|
||||
#define DBMS_MIN_PROTOCOL_VERSION_WITH_PROFILE_EVENTS 54450
|
||||
|
||||
/// Version of ClickHouse TCP protocol.
|
||||
///
|
||||
/// Should be incremented manually on protocol changes.
|
||||
@ -43,6 +45,6 @@
|
||||
/// NOTE: DBMS_TCP_PROTOCOL_VERSION has nothing common with VERSION_REVISION,
|
||||
/// later is just a number for server version (one number instead of commit SHA)
|
||||
/// for simplicity (sometimes it may be more convenient in some use cases).
|
||||
#define DBMS_TCP_PROTOCOL_VERSION 54449
|
||||
#define DBMS_TCP_PROTOCOL_VERSION 54450
|
||||
|
||||
#define DBMS_MIN_PROTOCOL_VERSION_WITH_INITIAL_QUERY_START_TIME 54449
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <Core/BackgroundSchedulePool.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Common/Exception.h>
|
||||
#include "Core/Protocol.h"
|
||||
#include <base/logger_useful.h>
|
||||
|
||||
namespace CurrentMetrics
|
||||
@ -81,6 +82,7 @@ void ConnectionCollector::drainConnections(IConnections & connections) noexcept
|
||||
{
|
||||
case Protocol::Server::EndOfStream:
|
||||
case Protocol::Server::Log:
|
||||
case Protocol::Server::ProfileEvents:
|
||||
break;
|
||||
|
||||
case Protocol::Server::Exception:
|
||||
|
@ -1,9 +1,12 @@
|
||||
#include <Common/ConcurrentBoundedQueue.h>
|
||||
|
||||
#include <DataStreams/ConnectionCollector.h>
|
||||
#include <DataStreams/RemoteQueryExecutor.h>
|
||||
#include <DataStreams/RemoteQueryExecutorReadContext.h>
|
||||
|
||||
#include <Columns/ColumnConst.h>
|
||||
#include <Common/CurrentThread.h>
|
||||
#include "Core/Protocol.h"
|
||||
#include <Processors/Pipe.h>
|
||||
#include <Processors/Sources/SourceFromSingleChunk.h>
|
||||
#include <Storages/IStorage.h>
|
||||
@ -390,6 +393,12 @@ std::optional<Block> RemoteQueryExecutor::processPacket(Packet packet)
|
||||
log_queue->pushBlock(std::move(packet.block));
|
||||
break;
|
||||
|
||||
case Protocol::Server::ProfileEvents:
|
||||
/// Pass profile events from remote server to client
|
||||
if (auto profile_queue = CurrentThread::getInternalProfileEventsQueue())
|
||||
profile_queue->emplace(std::move(packet.block));
|
||||
break;
|
||||
|
||||
default:
|
||||
got_unknown_packet_from_replica = true;
|
||||
throw Exception(ErrorCodes::UNKNOWN_PACKET_FROM_SERVER, "Unknown packet {} from one of the following replicas: {}",
|
||||
|
@ -25,14 +25,14 @@ ColumnPtr ExecutableFunctionJoinGet<or_null>::executeImpl(const ColumnsWithTypeA
|
||||
auto key = arguments[i];
|
||||
keys.emplace_back(std::move(key));
|
||||
}
|
||||
return storage_join->joinGet(keys, result_columns).column;
|
||||
return storage_join->joinGet(keys, result_columns, getContext()).column;
|
||||
}
|
||||
|
||||
template <bool or_null>
|
||||
ExecutableFunctionPtr FunctionJoinGet<or_null>::prepare(const ColumnsWithTypeAndName &) const
|
||||
{
|
||||
Block result_columns {{return_type->createColumn(), return_type, attr_name}};
|
||||
return std::make_unique<ExecutableFunctionJoinGet<or_null>>(table_lock, storage_join, result_columns);
|
||||
return std::make_unique<ExecutableFunctionJoinGet<or_null>>(getContext(), table_lock, storage_join, result_columns);
|
||||
}
|
||||
|
||||
static std::pair<std::shared_ptr<StorageJoin>, String>
|
||||
@ -89,7 +89,7 @@ FunctionBasePtr JoinGetOverloadResolver<or_null>::buildImpl(const ColumnsWithTyp
|
||||
auto return_type = storage_join->joinGetCheckAndGetReturnType(data_types, attr_name, or_null);
|
||||
auto table_lock = storage_join->lockForShare(getContext()->getInitialQueryId(), getContext()->getSettingsRef().lock_acquire_timeout);
|
||||
|
||||
return std::make_unique<FunctionJoinGet<or_null>>(table_lock, storage_join, attr_name, argument_types, return_type);
|
||||
return std::make_unique<FunctionJoinGet<or_null>>(getContext(), table_lock, storage_join, attr_name, argument_types, return_type);
|
||||
}
|
||||
|
||||
void registerFunctionJoinGet(FunctionFactory & factory)
|
||||
|
@ -14,13 +14,15 @@ class StorageJoin;
|
||||
using StorageJoinPtr = std::shared_ptr<StorageJoin>;
|
||||
|
||||
template <bool or_null>
|
||||
class ExecutableFunctionJoinGet final : public IExecutableFunction
|
||||
class ExecutableFunctionJoinGet final : public IExecutableFunction, WithContext
|
||||
{
|
||||
public:
|
||||
ExecutableFunctionJoinGet(TableLockHolder table_lock_,
|
||||
ExecutableFunctionJoinGet(ContextPtr context_,
|
||||
TableLockHolder table_lock_,
|
||||
StorageJoinPtr storage_join_,
|
||||
const DB::Block & result_columns_)
|
||||
: table_lock(std::move(table_lock_))
|
||||
: WithContext(context_)
|
||||
, table_lock(std::move(table_lock_))
|
||||
, storage_join(std::move(storage_join_))
|
||||
, result_columns(result_columns_)
|
||||
{}
|
||||
@ -42,15 +44,17 @@ private:
|
||||
};
|
||||
|
||||
template <bool or_null>
|
||||
class FunctionJoinGet final : public IFunctionBase
|
||||
class FunctionJoinGet final : public IFunctionBase, WithContext
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = or_null ? "joinGetOrNull" : "joinGet";
|
||||
|
||||
FunctionJoinGet(TableLockHolder table_lock_,
|
||||
FunctionJoinGet(ContextPtr context_,
|
||||
TableLockHolder table_lock_,
|
||||
StorageJoinPtr storage_join_, String attr_name_,
|
||||
DataTypes argument_types_, DataTypePtr return_type_)
|
||||
: table_lock(std::move(table_lock_))
|
||||
: WithContext(context_)
|
||||
, table_lock(std::move(table_lock_))
|
||||
, storage_join(storage_join_)
|
||||
, attr_name(std::move(attr_name_))
|
||||
, argument_types(std::move(argument_types_))
|
||||
|
182
src/Functions/normalizeString.cpp
Normal file
182
src/Functions/normalizeString.cpp
Normal file
@ -0,0 +1,182 @@
|
||||
#if !defined(ARCADIA_BUILD)
|
||||
# include "config_core.h"
|
||||
#endif
|
||||
|
||||
#if USE_ICU
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/FunctionStringToString.h>
|
||||
#include <unicode/rep.h>
|
||||
#include <unicode/unistr.h>
|
||||
#include <unicode/unorm2.h>
|
||||
#include <unicode/ustring.h>
|
||||
#include <unicode/utypes.h>
|
||||
#include <base/logger_useful.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Parsers/IAST_fwd.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int CANNOT_NORMALIZE_STRING;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Expansion factors are specified for UTF-32, since icu uses UTF-32 for normalization
|
||||
// Maximum expansion factors for different normalization forms
|
||||
// https://unicode.org/faq/normalization.html#12
|
||||
|
||||
struct NormalizeNFCImpl
|
||||
{
|
||||
static constexpr auto name = "normalizeUTF8NFC";
|
||||
|
||||
static constexpr auto expansionFactor = 3;
|
||||
|
||||
static const UNormalizer2 *getNormalizer(UErrorCode *err)
|
||||
{
|
||||
return unorm2_getNFCInstance(err);
|
||||
}
|
||||
};
|
||||
|
||||
struct NormalizeNFDImpl
|
||||
{
|
||||
static constexpr auto name = "normalizeUTF8NFD";
|
||||
|
||||
static constexpr auto expansionFactor = 4;
|
||||
|
||||
static const UNormalizer2 *getNormalizer(UErrorCode *err)
|
||||
{
|
||||
return unorm2_getNFDInstance(err);
|
||||
}
|
||||
};
|
||||
|
||||
struct NormalizeNFKCImpl
|
||||
{
|
||||
static constexpr auto name = "normalizeUTF8NFKC";
|
||||
|
||||
static constexpr auto expansionFactor = 18;
|
||||
|
||||
static const UNormalizer2 *getNormalizer(UErrorCode *err)
|
||||
{
|
||||
return unorm2_getNFKCInstance(err);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
struct NormalizeNFKDImpl
|
||||
{
|
||||
static constexpr auto name = "normalizeUTF8NFKD";
|
||||
|
||||
static constexpr auto expansionFactor = 18;
|
||||
|
||||
static const UNormalizer2 *getNormalizer(UErrorCode *err)
|
||||
{
|
||||
return unorm2_getNFKDInstance(err);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename NormalizeImpl>
|
||||
struct NormalizeUTF8Impl
|
||||
{
|
||||
|
||||
static void vector(const ColumnString::Chars & data,
|
||||
const ColumnString::Offsets & offsets,
|
||||
ColumnString::Chars & res_data,
|
||||
ColumnString::Offsets & res_offsets)
|
||||
{
|
||||
UErrorCode err = U_ZERO_ERROR;
|
||||
|
||||
const UNormalizer2 *normalizer = NormalizeImpl::getNormalizer(&err);
|
||||
if (U_FAILURE(err))
|
||||
throw Exception(ErrorCodes::CANNOT_NORMALIZE_STRING, "Normalization failed (getNormalizer): {}", u_errorName(err));
|
||||
|
||||
size_t size = offsets.size();
|
||||
res_offsets.resize(size);
|
||||
|
||||
ColumnString::Offset current_from_offset = 0;
|
||||
ColumnString::Offset current_to_offset = 0;
|
||||
|
||||
icu::UnicodeString to_string;
|
||||
|
||||
PODArray<UChar> from_uchars;
|
||||
PODArray<UChar> to_uchars;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
size_t from_size = offsets[i] - current_from_offset - 1;
|
||||
|
||||
from_uchars.resize(from_size + 1);
|
||||
int32_t from_code_points;
|
||||
u_strFromUTF8(
|
||||
from_uchars.data(),
|
||||
from_uchars.size(),
|
||||
&from_code_points,
|
||||
reinterpret_cast<const char*>(&data[current_from_offset]),
|
||||
from_size,
|
||||
&err);
|
||||
if (U_FAILURE(err))
|
||||
throw Exception(ErrorCodes::CANNOT_NORMALIZE_STRING, "Normalization failed (strFromUTF8): {}", u_errorName(err));
|
||||
|
||||
to_uchars.resize(from_code_points * NormalizeImpl::expansionFactor + 1);
|
||||
|
||||
int32_t to_code_points = unorm2_normalize(
|
||||
normalizer,
|
||||
from_uchars.data(),
|
||||
from_code_points,
|
||||
to_uchars.data(),
|
||||
to_uchars.size(),
|
||||
&err);
|
||||
if (U_FAILURE(err))
|
||||
throw Exception(ErrorCodes::CANNOT_NORMALIZE_STRING, "Normalization failed (normalize): {}", u_errorName(err));
|
||||
|
||||
size_t max_to_size = current_to_offset + 4 * to_code_points + 1;
|
||||
if (res_data.size() < max_to_size)
|
||||
res_data.resize(max_to_size);
|
||||
|
||||
int32_t to_size;
|
||||
u_strToUTF8(
|
||||
reinterpret_cast<char*>(&res_data[current_to_offset]),
|
||||
res_data.size() - current_to_offset,
|
||||
&to_size,
|
||||
to_uchars.data(),
|
||||
to_code_points,
|
||||
&err);
|
||||
if (U_FAILURE(err))
|
||||
throw Exception(ErrorCodes::CANNOT_NORMALIZE_STRING, "Normalization failed (strToUTF8): {}", u_errorName(err));
|
||||
|
||||
current_to_offset += to_size;
|
||||
res_data[current_to_offset] = 0;
|
||||
++current_to_offset;
|
||||
res_offsets[i] = current_to_offset;
|
||||
|
||||
current_from_offset = offsets[i];
|
||||
}
|
||||
}
|
||||
|
||||
[[noreturn]] static void vectorFixed(const ColumnString::Chars &, size_t, ColumnString::Chars &)
|
||||
{
|
||||
throw Exception("Cannot apply function normalizeUTF8 to fixed string.", ErrorCodes::ILLEGAL_COLUMN);
|
||||
}
|
||||
};
|
||||
|
||||
using FunctionNormalizeUTF8NFC = FunctionStringToString<NormalizeUTF8Impl<NormalizeNFCImpl>, NormalizeNFCImpl>;
|
||||
using FunctionNormalizeUTF8NFD = FunctionStringToString<NormalizeUTF8Impl<NormalizeNFDImpl>, NormalizeNFDImpl>;
|
||||
using FunctionNormalizeUTF8NFKC = FunctionStringToString<NormalizeUTF8Impl<NormalizeNFKCImpl>, NormalizeNFKCImpl>;
|
||||
using FunctionNormalizeUTF8NFKD = FunctionStringToString<NormalizeUTF8Impl<NormalizeNFKDImpl>, NormalizeNFKDImpl>;
|
||||
}
|
||||
|
||||
void registerFunctionNormalizeUTF8(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionNormalizeUTF8NFC>();
|
||||
factory.registerFunction<FunctionNormalizeUTF8NFD>();
|
||||
factory.registerFunction<FunctionNormalizeUTF8NFKC>();
|
||||
factory.registerFunction<FunctionNormalizeUTF8NFKD>();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -52,6 +52,10 @@ void registerFunctionSynonyms(FunctionFactory &);
|
||||
void registerFunctionLemmatize(FunctionFactory &);
|
||||
#endif
|
||||
|
||||
#if USE_ICU
|
||||
void registerFunctionNormalizeUTF8(FunctionFactory &);
|
||||
#endif
|
||||
|
||||
void registerFunctionsString(FunctionFactory & factory)
|
||||
{
|
||||
registerFunctionRepeat(factory);
|
||||
@ -97,6 +101,10 @@ void registerFunctionsString(FunctionFactory & factory)
|
||||
registerFunctionSynonyms(factory);
|
||||
registerFunctionLemmatize(factory);
|
||||
#endif
|
||||
|
||||
#if USE_ICU
|
||||
registerFunctionNormalizeUTF8(factory);
|
||||
#endif
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -107,10 +107,7 @@ ASTPtr tryExchangeFunctions(const ASTFunction & func)
|
||||
|| !supported.find(lower_name)->second.count(child_func->name))
|
||||
return {};
|
||||
|
||||
/// Cannot rewrite function with alias cause alias could become undefined
|
||||
if (!func.tryGetAlias().empty() || !child_func->tryGetAlias().empty())
|
||||
return {};
|
||||
|
||||
auto original_alias = func.tryGetAlias();
|
||||
const auto & child_func_args = child_func->arguments->children;
|
||||
const auto * first_literal = child_func_args[0]->as<ASTLiteral>();
|
||||
const auto * second_literal = child_func_args[1]->as<ASTLiteral>();
|
||||
@ -132,7 +129,12 @@ ASTPtr tryExchangeFunctions(const ASTFunction & func)
|
||||
optimized_ast = exchangeExtractSecondArgument(new_name, *child_func);
|
||||
}
|
||||
|
||||
return optimized_ast;
|
||||
if (optimized_ast)
|
||||
{
|
||||
optimized_ast->setAlias(original_alias);
|
||||
return optimized_ast;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -938,7 +938,7 @@ JoinPtr SelectQueryExpressionAnalyzer::makeTableJoin(
|
||||
if (auto storage = analyzed_join->getStorageJoin())
|
||||
{
|
||||
std::tie(left_convert_actions, right_convert_actions) = analyzed_join->createConvertingActions(left_columns, {});
|
||||
return storage->getJoinLocked(analyzed_join);
|
||||
return storage->getJoinLocked(analyzed_join, getContext());
|
||||
}
|
||||
|
||||
joined_plan = buildJoinedPlan(getContext(), join_element, *analyzed_join, query_options);
|
||||
|
@ -744,7 +744,7 @@ bool HashJoin::addJoinedBlock(const Block & source_block, bool check_limits)
|
||||
size_t total_rows = 0;
|
||||
size_t total_bytes = 0;
|
||||
{
|
||||
if (storage_join_lock.mutex())
|
||||
if (storage_join_lock)
|
||||
throw DB::Exception("addJoinedBlock called when HashJoin locked to prevent updates",
|
||||
ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include <Common/ColumnsHashing.h>
|
||||
#include <Common/HashTable/HashMap.h>
|
||||
#include <Common/HashTable/FixedHashMap.h>
|
||||
#include <Common/RWLock.h>
|
||||
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnFixedString.h>
|
||||
@ -334,9 +335,9 @@ public:
|
||||
|
||||
/// We keep correspondence between used_flags and hash table internal buffer.
|
||||
/// Hash table cannot be modified during HashJoin lifetime and must be protected with lock.
|
||||
void setLock(std::shared_mutex & rwlock)
|
||||
void setLock(RWLockImpl::LockHolder rwlock_holder)
|
||||
{
|
||||
storage_join_lock = std::shared_lock<std::shared_mutex>(rwlock);
|
||||
storage_join_lock = rwlock_holder;
|
||||
}
|
||||
|
||||
void reuseJoinedData(const HashJoin & join);
|
||||
@ -391,7 +392,7 @@ private:
|
||||
|
||||
/// Should be set via setLock to protect hash table from modification from StorageJoin
|
||||
/// If set HashJoin instance is not available for modification (addJoinedBlock)
|
||||
std::shared_lock<std::shared_mutex> storage_join_lock;
|
||||
RWLockImpl::LockHolder storage_join_lock = nullptr;
|
||||
|
||||
void dataMapInit(MapsVariant &);
|
||||
|
||||
|
@ -6,7 +6,7 @@
|
||||
#include <Common/UTF8Helpers.h>
|
||||
|
||||
#if defined(__SSE2__)
|
||||
#include <immintrin.h>
|
||||
#include <emmintrin.h>
|
||||
|
||||
#if defined(__SSE4_2__)
|
||||
#include <nmmintrin.h>
|
||||
|
@ -474,5 +474,14 @@ ContextMutablePtr Session::makeQueryContextImpl(const ClientInfo * client_info_t
|
||||
return query_context;
|
||||
}
|
||||
|
||||
|
||||
void Session::releaseSessionID()
|
||||
{
|
||||
if (!named_session)
|
||||
return;
|
||||
named_session->release();
|
||||
named_session = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -68,6 +68,9 @@ public:
|
||||
ContextMutablePtr makeQueryContext(const ClientInfo & query_client_info) const;
|
||||
ContextMutablePtr makeQueryContext(ClientInfo && query_client_info) const;
|
||||
|
||||
/// Releases the currently used session ID so it becomes available for reuse by another session.
|
||||
void releaseSessionID();
|
||||
|
||||
private:
|
||||
std::shared_ptr<SessionLog> getSessionLog() const;
|
||||
ContextMutablePtr makeQueryContextImpl(const ClientInfo * client_info_to_copy, ClientInfo * client_info_to_move) const;
|
||||
|
@ -1,3 +1,4 @@
|
||||
#include <mutex>
|
||||
#include <Common/ThreadStatus.h>
|
||||
|
||||
#include <Processors/Transforms/buildPushingToViewsChain.h>
|
||||
@ -123,10 +124,12 @@ void ThreadStatus::setupState(const ThreadGroupStatusPtr & thread_group_)
|
||||
|
||||
/// NOTE: thread may be attached multiple times if it is reused from a thread pool.
|
||||
thread_group->thread_ids.emplace_back(thread_id);
|
||||
thread_group->threads.insert(this);
|
||||
|
||||
logs_queue_ptr = thread_group->logs_queue_ptr;
|
||||
fatal_error_callback = thread_group->fatal_error_callback;
|
||||
query_context = thread_group->query_context;
|
||||
profile_queue_ptr = thread_group->profile_queue_ptr;
|
||||
|
||||
if (global_context.expired())
|
||||
global_context = thread_group->global_context;
|
||||
@ -397,6 +400,10 @@ void ThreadStatus::detachQuery(bool exit_if_already_detached, bool thread_exits)
|
||||
finalizePerformanceCounters();
|
||||
|
||||
/// Detach from thread group
|
||||
{
|
||||
std::lock_guard guard(thread_group->mutex);
|
||||
thread_group->threads.erase(this);
|
||||
}
|
||||
performance_counters.setParent(&ProfileEvents::global_counters);
|
||||
memory_tracker.reset();
|
||||
|
||||
|
@ -14,7 +14,6 @@
|
||||
#include <Interpreters/RewriteCountVariantsVisitor.h>
|
||||
#include <Interpreters/MonotonicityCheckVisitor.h>
|
||||
#include <Interpreters/ConvertStringsToEnumVisitor.h>
|
||||
#include <Interpreters/PredicateExpressionsOptimizer.h>
|
||||
#include <Interpreters/RewriteFunctionToSubcolumnVisitor.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Interpreters/ExternalDictionariesLoader.h>
|
||||
@ -710,9 +709,6 @@ void TreeOptimizer::apply(ASTPtr & query, TreeRewriterResult & result,
|
||||
if (settings.optimize_arithmetic_operations_in_aggregate_functions)
|
||||
optimizeAggregationFunctions(query);
|
||||
|
||||
/// Push the predicate expression down to the subqueries.
|
||||
result.rewrite_subqueries = PredicateExpressionsOptimizer(context, tables_with_columns, settings).optimize(*select_query);
|
||||
|
||||
/// GROUP BY injective function elimination.
|
||||
optimizeGroupBy(select_query, result.source_columns_set, context);
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include <Interpreters/getTableExpressions.h>
|
||||
#include <Interpreters/TreeOptimizer.h>
|
||||
#include <Interpreters/replaceAliasColumnsInQuery.h>
|
||||
#include <Interpreters/PredicateExpressionsOptimizer.h>
|
||||
|
||||
#include <Parsers/ASTExpressionList.h>
|
||||
#include <Parsers/ASTFunction.h>
|
||||
@ -1036,7 +1037,12 @@ TreeRewriterResultPtr TreeRewriter::analyzeSelect(
|
||||
if (settings.legacy_column_name_of_tuple_literal)
|
||||
markTupleLiteralsAsLegacy(query);
|
||||
|
||||
TreeOptimizer::apply(query, result, tables_with_columns, getContext());
|
||||
/// Push the predicate expression down to subqueries. The optimization should be applied to both initial and secondary queries.
|
||||
result.rewrite_subqueries = PredicateExpressionsOptimizer(getContext(), tables_with_columns, settings).optimize(*select_query);
|
||||
|
||||
/// Only apply AST optimization for initial queries.
|
||||
if (getContext()->getClientInfo().query_kind == ClientInfo::QueryKind::INITIAL_QUERY)
|
||||
TreeOptimizer::apply(query, result, tables_with_columns, getContext());
|
||||
|
||||
/// array_join_alias_to_name, array_join_result_to_source.
|
||||
getArrayJoinedColumns(query, result, select_query, result.source_columns, source_columns_set);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
QueryIdHolder::QueryIdHolder(const String & query_id_, const MergeTreeData & data_) : query_id(query_id_), data(data_)
|
||||
{
|
||||
}
|
||||
|
@ -2,13 +2,16 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
class MergeTreeData;
|
||||
|
||||
/// Holds the current query id and do something meaningful in destructor.
|
||||
/// Currently it's used for cleaning query id in the MergeTreeData query set.
|
||||
struct QueryIdHolder
|
||||
struct QueryIdHolder : private boost::noncopyable
|
||||
{
|
||||
QueryIdHolder(const std::string & query_id_, const MergeTreeData & data_);
|
||||
|
||||
|
@ -945,7 +945,7 @@ void ReadFromMergeTree::initializePipeline(QueryPipelineBuilder & pipeline, cons
|
||||
ProfileEvents::increment(ProfileEvents::SelectedRanges, result.selected_ranges);
|
||||
ProfileEvents::increment(ProfileEvents::SelectedMarks, result.selected_marks);
|
||||
|
||||
auto query_id_holder = MergeTreeDataSelectExecutor::checkLimits(data, result.parts_with_ranges, context);
|
||||
auto query_id_holder = MergeTreeDataSelectExecutor::checkLimits(data, result, context);
|
||||
|
||||
if (result.parts_with_ranges.empty())
|
||||
{
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "GRPCServer.h"
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#if USE_GRPC
|
||||
|
||||
#include <Columns/ColumnString.h>
|
||||
@ -585,6 +587,7 @@ namespace
|
||||
void finishQuery();
|
||||
void onException(const Exception & exception);
|
||||
void onFatalError();
|
||||
void releaseQueryIDAndSessionID();
|
||||
void close();
|
||||
|
||||
void readQueryInfo();
|
||||
@ -1176,6 +1179,7 @@ namespace
|
||||
addProgressToResult();
|
||||
query_scope->logPeakMemoryUsage();
|
||||
addLogsToResult();
|
||||
releaseQueryIDAndSessionID();
|
||||
sendResult();
|
||||
close();
|
||||
|
||||
@ -1206,6 +1210,8 @@ namespace
|
||||
LOG_WARNING(log, "Couldn't send logs to client");
|
||||
}
|
||||
|
||||
releaseQueryIDAndSessionID();
|
||||
|
||||
try
|
||||
{
|
||||
sendException(exception);
|
||||
@ -1225,7 +1231,7 @@ namespace
|
||||
{
|
||||
try
|
||||
{
|
||||
finalize = true;
|
||||
result.mutable_exception()->set_name("FatalError");
|
||||
addLogsToResult();
|
||||
sendResult();
|
||||
}
|
||||
@ -1235,6 +1241,17 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void Call::releaseQueryIDAndSessionID()
|
||||
{
|
||||
/// releaseQueryIDAndSessionID() should be called before sending the final result to the client
|
||||
/// because the client may decide to send another query with the same query ID or session ID
|
||||
/// immediately after it receives our final result, and it's prohibited to have
|
||||
/// two queries executed at the same time with the same query ID or session ID.
|
||||
io.process_list_entry.reset();
|
||||
if (session)
|
||||
session->releaseSessionID();
|
||||
}
|
||||
|
||||
void Call::close()
|
||||
{
|
||||
responder.reset();
|
||||
|
@ -1,4 +1,11 @@
|
||||
#include <algorithm>
|
||||
#include <iomanip>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <string.h>
|
||||
#include <base/types.h>
|
||||
#include <base/scope_guard.h>
|
||||
#include <Poco/Net/NetException.h>
|
||||
#include <Poco/Util/LayeredConfiguration.h>
|
||||
@ -30,6 +37,7 @@
|
||||
#include <Access/Credentials.h>
|
||||
#include <Storages/ColumnDefault.h>
|
||||
#include <DataTypes/DataTypeLowCardinality.h>
|
||||
#include <DataTypes/DataTypeEnum.h>
|
||||
#include <Compression/CompressionFactory.h>
|
||||
#include <base/logger_useful.h>
|
||||
#include <Common/CurrentMetrics.h>
|
||||
@ -238,6 +246,11 @@ void TCPHandler::runImpl()
|
||||
sendLogs();
|
||||
});
|
||||
}
|
||||
if (client_tcp_protocol_version >= DBMS_MIN_PROTOCOL_VERSION_WITH_PROFILE_EVENTS)
|
||||
{
|
||||
state.profile_queue = std::make_shared<InternalProfileEventsQueue>(std::numeric_limits<int>::max());
|
||||
CurrentThread::attachInternalProfileEventsQueue(state.profile_queue);
|
||||
}
|
||||
|
||||
query_context->setExternalTablesInitializer([this] (ContextPtr context)
|
||||
{
|
||||
@ -665,6 +678,7 @@ void TCPHandler::processOrdinaryQueryWithProcessors()
|
||||
/// Some time passed and there is a progress.
|
||||
after_send_progress.restart();
|
||||
sendProgress();
|
||||
sendProfileEvents();
|
||||
}
|
||||
|
||||
sendLogs();
|
||||
@ -690,6 +704,7 @@ void TCPHandler::processOrdinaryQueryWithProcessors()
|
||||
sendProfileInfo(executor.getProfileInfo());
|
||||
sendProgress();
|
||||
sendLogs();
|
||||
sendProfileEvents();
|
||||
}
|
||||
|
||||
if (state.is_connection_closed)
|
||||
@ -812,6 +827,173 @@ void TCPHandler::sendExtremes(const Block & extremes)
|
||||
}
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
using namespace ProfileEvents;
|
||||
|
||||
enum ProfileEventTypes : int8_t
|
||||
{
|
||||
INCREMENT = 1,
|
||||
GAUGE = 2,
|
||||
};
|
||||
|
||||
constexpr size_t NAME_COLUMN_INDEX = 4;
|
||||
constexpr size_t VALUE_COLUMN_INDEX = 5;
|
||||
|
||||
struct ProfileEventsSnapshot
|
||||
{
|
||||
UInt64 thread_id;
|
||||
ProfileEvents::Counters counters;
|
||||
Int64 memory_usage;
|
||||
time_t current_time;
|
||||
};
|
||||
|
||||
/*
|
||||
* Add records about provided non-zero ProfileEvents::Counters.
|
||||
*/
|
||||
void dumpProfileEvents(
|
||||
ProfileEventsSnapshot const & snapshot,
|
||||
MutableColumns & columns,
|
||||
String const & host_name)
|
||||
{
|
||||
size_t rows = 0;
|
||||
auto & name_column = columns[NAME_COLUMN_INDEX];
|
||||
auto & value_column = columns[VALUE_COLUMN_INDEX];
|
||||
for (ProfileEvents::Event event = 0; event < ProfileEvents::Counters::num_counters; ++event)
|
||||
{
|
||||
UInt64 value = snapshot.counters[event].load(std::memory_order_relaxed);
|
||||
|
||||
if (value == 0)
|
||||
continue;
|
||||
|
||||
const char * desc = ProfileEvents::getName(event);
|
||||
name_column->insertData(desc, strlen(desc));
|
||||
value_column->insert(value);
|
||||
rows++;
|
||||
}
|
||||
|
||||
// Fill the rest of the columns with data
|
||||
for (size_t row = 0; row < rows; ++row)
|
||||
{
|
||||
size_t i = 0;
|
||||
columns[i++]->insertData(host_name.data(), host_name.size());
|
||||
columns[i++]->insert(UInt64(snapshot.current_time));
|
||||
columns[i++]->insert(UInt64{snapshot.thread_id});
|
||||
columns[i++]->insert(ProfileEventTypes::INCREMENT);
|
||||
}
|
||||
}
|
||||
|
||||
void dumpMemoryTracker(
|
||||
ProfileEventsSnapshot const & snapshot,
|
||||
MutableColumns & columns,
|
||||
String const & host_name)
|
||||
{
|
||||
{
|
||||
size_t i = 0;
|
||||
columns[i++]->insertData(host_name.data(), host_name.size());
|
||||
columns[i++]->insert(UInt64(snapshot.current_time));
|
||||
columns[i++]->insert(UInt64{snapshot.thread_id});
|
||||
columns[i++]->insert(ProfileEventTypes::GAUGE);
|
||||
|
||||
columns[i++]->insertData(MemoryTracker::USAGE_EVENT_NAME, strlen(MemoryTracker::USAGE_EVENT_NAME));
|
||||
columns[i++]->insert(snapshot.memory_usage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void TCPHandler::sendProfileEvents()
|
||||
{
|
||||
if (client_tcp_protocol_version < DBMS_MIN_PROTOCOL_VERSION_WITH_PROFILE_EVENTS)
|
||||
return;
|
||||
|
||||
auto profile_event_type = std::make_shared<DataTypeEnum8>(
|
||||
DataTypeEnum8::Values
|
||||
{
|
||||
{ "increment", static_cast<Int8>(INCREMENT)},
|
||||
{ "gauge", static_cast<Int8>(GAUGE)},
|
||||
});
|
||||
|
||||
NamesAndTypesList column_names_and_types = {
|
||||
{ "host_name", std::make_shared<DataTypeString>() },
|
||||
{ "current_time", std::make_shared<DataTypeDateTime>() },
|
||||
{ "thread_id", std::make_shared<DataTypeUInt64>() },
|
||||
{ "type", profile_event_type },
|
||||
{ "name", std::make_shared<DataTypeString>() },
|
||||
{ "value", std::make_shared<DataTypeUInt64>() },
|
||||
};
|
||||
|
||||
ColumnsWithTypeAndName temp_columns;
|
||||
for (auto const & name_and_type : column_names_and_types)
|
||||
temp_columns.emplace_back(name_and_type.type, name_and_type.name);
|
||||
|
||||
Block block(std::move(temp_columns));
|
||||
|
||||
MutableColumns columns = block.mutateColumns();
|
||||
auto thread_group = CurrentThread::getGroup();
|
||||
auto const current_thread_id = CurrentThread::get().thread_id;
|
||||
std::vector<ProfileEventsSnapshot> snapshots;
|
||||
ProfileEventsSnapshot group_snapshot;
|
||||
{
|
||||
std::lock_guard guard(thread_group->mutex);
|
||||
snapshots.reserve(thread_group->threads.size());
|
||||
for (auto * thread : thread_group->threads)
|
||||
{
|
||||
auto const thread_id = thread->thread_id;
|
||||
if (thread_id == current_thread_id)
|
||||
continue;
|
||||
auto current_time = time(nullptr);
|
||||
auto counters = thread->performance_counters.getPartiallyAtomicSnapshot();
|
||||
auto memory_usage = thread->memory_tracker.get();
|
||||
snapshots.push_back(ProfileEventsSnapshot{
|
||||
thread_id,
|
||||
std::move(counters),
|
||||
memory_usage,
|
||||
current_time
|
||||
});
|
||||
}
|
||||
|
||||
group_snapshot.thread_id = 0;
|
||||
group_snapshot.current_time = time(nullptr);
|
||||
group_snapshot.memory_usage = thread_group->memory_tracker.get();
|
||||
group_snapshot.counters = thread_group->performance_counters.getPartiallyAtomicSnapshot();
|
||||
}
|
||||
|
||||
for (auto & snapshot : snapshots)
|
||||
{
|
||||
dumpProfileEvents(snapshot, columns, server_display_name);
|
||||
dumpMemoryTracker(snapshot, columns, server_display_name);
|
||||
}
|
||||
dumpProfileEvents(group_snapshot, columns, server_display_name);
|
||||
dumpMemoryTracker(group_snapshot, columns, server_display_name);
|
||||
|
||||
MutableColumns logs_columns;
|
||||
Block curr_block;
|
||||
size_t rows = 0;
|
||||
|
||||
for (; state.profile_queue->tryPop(curr_block); ++rows)
|
||||
{
|
||||
auto curr_columns = curr_block.getColumns();
|
||||
for (size_t j = 0; j < curr_columns.size(); ++j)
|
||||
columns[j]->insertRangeFrom(*curr_columns[j], 0, curr_columns[j]->size());
|
||||
}
|
||||
|
||||
bool empty = columns[0]->empty();
|
||||
if (!empty)
|
||||
{
|
||||
block.setColumns(std::move(columns));
|
||||
|
||||
initProfileEventsBlockOutput(block);
|
||||
|
||||
writeVarUInt(Protocol::Server::ProfileEvents, *out);
|
||||
writeStringBinary("", *out);
|
||||
|
||||
state.profile_events_block_out->write(block);
|
||||
out->next();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool TCPHandler::receiveProxyHeader()
|
||||
{
|
||||
if (in->eof())
|
||||
@ -1453,6 +1635,20 @@ void TCPHandler::initLogsBlockOutput(const Block & block)
|
||||
}
|
||||
|
||||
|
||||
void TCPHandler::initProfileEventsBlockOutput(const Block & block)
|
||||
{
|
||||
if (!state.profile_events_block_out)
|
||||
{
|
||||
const Settings & query_settings = query_context->getSettingsRef();
|
||||
state.profile_events_block_out = std::make_unique<NativeWriter>(
|
||||
*out,
|
||||
client_tcp_protocol_version,
|
||||
block.cloneEmpty(),
|
||||
!query_settings.low_cardinality_allow_in_native_format);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool TCPHandler::isQueryCancelled()
|
||||
{
|
||||
if (state.is_cancelled || state.sent_all_data)
|
||||
|
@ -48,6 +48,9 @@ struct QueryState
|
||||
InternalTextLogsQueuePtr logs_queue;
|
||||
std::unique_ptr<NativeWriter> logs_block_out;
|
||||
|
||||
InternalProfileEventsQueuePtr profile_queue;
|
||||
std::unique_ptr<NativeWriter> profile_events_block_out;
|
||||
|
||||
/// From where to read data for INSERT.
|
||||
std::shared_ptr<ReadBuffer> maybe_compressed_in;
|
||||
std::unique_ptr<NativeReader> block_in;
|
||||
@ -228,11 +231,13 @@ private:
|
||||
void sendProfileInfo(const BlockStreamProfileInfo & info);
|
||||
void sendTotals(const Block & totals);
|
||||
void sendExtremes(const Block & extremes);
|
||||
void sendProfileEvents();
|
||||
|
||||
/// Creates state.block_in/block_out for blocks read/write, depending on whether compression is enabled.
|
||||
void initBlockInput();
|
||||
void initBlockOutput(const Block & block);
|
||||
void initLogsBlockOutput(const Block & block);
|
||||
void initProfileEventsBlockOutput(const Block & block);
|
||||
|
||||
bool isQueryCancelled();
|
||||
|
||||
|
@ -87,6 +87,8 @@ struct ColumnSize
|
||||
}
|
||||
};
|
||||
|
||||
using IndexSize = ColumnSize;
|
||||
|
||||
/** Storage. Describes the table. Responsible for
|
||||
* - storage of the table data;
|
||||
* - the definition in which files (or not in files) the data is stored;
|
||||
@ -163,6 +165,11 @@ public:
|
||||
using ColumnSizeByName = std::unordered_map<std::string, ColumnSize>;
|
||||
virtual ColumnSizeByName getColumnSizes() const { return {}; }
|
||||
|
||||
/// Optional size information of each secondary index.
|
||||
/// Valid only for MergeTree family.
|
||||
using IndexSizeByName = std::unordered_map<std::string, IndexSize>;
|
||||
virtual IndexSizeByName getSecondaryIndexSizes() const { return {}; }
|
||||
|
||||
/// Get mutable version (snapshot) of storage metadata. Metadata object is
|
||||
/// multiversion, so it can be concurrently changed, but returned copy can be
|
||||
/// used without any locks.
|
||||
@ -219,6 +226,7 @@ private:
|
||||
/// without locks.
|
||||
MultiVersionStorageMetadataPtr metadata;
|
||||
|
||||
protected:
|
||||
RWLockImpl::LockHolder tryLockTimed(
|
||||
const RWLock & rwlock, RWLockImpl::Type type, const String & query_id, const std::chrono::milliseconds & acquire_timeout) const;
|
||||
|
||||
|
@ -439,9 +439,13 @@ void IMergeTreeDataPart::removeIfNeeded()
|
||||
if (file_name.empty())
|
||||
throw Exception("relative_path " + relative_path + " of part " + name + " is invalid or not set", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
if (!startsWith(file_name, "tmp"))
|
||||
if (!startsWith(file_name, "tmp") && !endsWith(file_name, ".tmp_proj"))
|
||||
{
|
||||
LOG_ERROR(storage.log, "~DataPart() should remove part {} but its name doesn't start with tmp. Too suspicious, keeping the part.", path);
|
||||
LOG_ERROR(
|
||||
storage.log,
|
||||
"~DataPart() should remove part {} but its name doesn't start with \"tmp\" or end with \".tmp_proj\". Too "
|
||||
"suspicious, keeping the part.",
|
||||
path);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -584,7 +588,7 @@ void IMergeTreeDataPart::loadColumnsChecksumsIndexes(bool require_columns_checks
|
||||
loadColumns(require_columns_checksums);
|
||||
loadChecksums(require_columns_checksums);
|
||||
loadIndexGranularity();
|
||||
calculateColumnsSizesOnDisk();
|
||||
calculateColumnsAndSecondaryIndicesSizesOnDisk();
|
||||
loadIndex(); /// Must be called after loadIndexGranularity as it uses the value of `index_granularity`
|
||||
loadRowsCount(); /// Must be called after loadIndexGranularity() as it uses the value of `index_granularity`.
|
||||
loadPartitionAndMinMaxIndex();
|
||||
@ -1420,6 +1424,11 @@ void IMergeTreeDataPart::checkConsistency(bool /* require_part_metadata */) cons
|
||||
throw Exception("Method 'checkConsistency' is not implemented for part with type " + getType().toString(), ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
void IMergeTreeDataPart::calculateColumnsAndSecondaryIndicesSizesOnDisk()
|
||||
{
|
||||
calculateColumnsSizesOnDisk();
|
||||
calculateSecondaryIndicesSizesOnDisk();
|
||||
}
|
||||
|
||||
void IMergeTreeDataPart::calculateColumnsSizesOnDisk()
|
||||
{
|
||||
@ -1429,6 +1438,41 @@ void IMergeTreeDataPart::calculateColumnsSizesOnDisk()
|
||||
calculateEachColumnSizes(columns_sizes, total_columns_size);
|
||||
}
|
||||
|
||||
void IMergeTreeDataPart::calculateSecondaryIndicesSizesOnDisk()
|
||||
{
|
||||
if (checksums.empty())
|
||||
throw Exception("Cannot calculate secondary indexes sizes when columns or checksums are not initialized", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
auto secondary_indices_descriptions = storage.getInMemoryMetadataPtr()->secondary_indices;
|
||||
|
||||
for (auto & index_description : secondary_indices_descriptions)
|
||||
{
|
||||
ColumnSize index_size;
|
||||
|
||||
auto index_ptr = MergeTreeIndexFactory::instance().get(index_description);
|
||||
auto index_name = index_ptr->getFileName();
|
||||
auto index_name_escaped = escapeForFileName(index_name);
|
||||
|
||||
auto index_file_name = index_name_escaped + index_ptr->getSerializedFileExtension();
|
||||
auto index_marks_file_name = index_name_escaped + index_granularity_info.marks_file_extension;
|
||||
|
||||
/// If part does not contain index
|
||||
auto bin_checksum = checksums.files.find(index_file_name);
|
||||
if (bin_checksum != checksums.files.end())
|
||||
{
|
||||
index_size.data_compressed = bin_checksum->second.file_size;
|
||||
index_size.data_uncompressed = bin_checksum->second.uncompressed_size;
|
||||
}
|
||||
|
||||
auto mrk_checksum = checksums.files.find(index_marks_file_name);
|
||||
if (mrk_checksum != checksums.files.end())
|
||||
index_size.marks = mrk_checksum->second.file_size;
|
||||
|
||||
total_secondary_indices_size.add(index_size);
|
||||
secondary_index_sizes[index_description.name] = index_size;
|
||||
}
|
||||
}
|
||||
|
||||
ColumnSize IMergeTreeDataPart::getColumnSize(const String & column_name, const IDataType & /* type */) const
|
||||
{
|
||||
/// For some types of parts columns_size maybe not calculated
|
||||
@ -1439,6 +1483,15 @@ ColumnSize IMergeTreeDataPart::getColumnSize(const String & column_name, const I
|
||||
return ColumnSize{};
|
||||
}
|
||||
|
||||
IndexSize IMergeTreeDataPart::getSecondaryIndexSize(const String & secondary_index_name) const
|
||||
{
|
||||
auto it = secondary_index_sizes.find(secondary_index_name);
|
||||
if (it != secondary_index_sizes.end())
|
||||
return it->second;
|
||||
|
||||
return ColumnSize{};
|
||||
}
|
||||
|
||||
void IMergeTreeDataPart::accumulateColumnSizes(ColumnToSize & column_to_size) const
|
||||
{
|
||||
for (const auto & [column_name, size] : columns_sizes)
|
||||
|
@ -55,6 +55,8 @@ public:
|
||||
using ColumnSizeByName = std::unordered_map<std::string, ColumnSize>;
|
||||
using NameToNumber = std::unordered_map<std::string, size_t>;
|
||||
|
||||
using IndexSizeByName = std::unordered_map<std::string, ColumnSize>;
|
||||
|
||||
using Type = MergeTreeDataPartType;
|
||||
|
||||
|
||||
@ -101,9 +103,16 @@ public:
|
||||
/// Otherwise return information about column size on disk.
|
||||
ColumnSize getColumnSize(const String & column_name, const IDataType & /* type */) const;
|
||||
|
||||
/// NOTE: Returns zeros if secondary indexes are not found in checksums.
|
||||
/// Otherwise return information about secondary index size on disk.
|
||||
IndexSize getSecondaryIndexSize(const String & secondary_index_name) const;
|
||||
|
||||
/// Return information about column size on disk for all columns in part
|
||||
ColumnSize getTotalColumnsSize() const { return total_columns_size; }
|
||||
|
||||
/// Return information about secondary indexes size on disk for all indexes in part
|
||||
IndexSize getTotalSeconaryIndicesSize() const { return total_secondary_indices_size; }
|
||||
|
||||
virtual String getFileNameForColumn(const NameAndTypePair & column) const = 0;
|
||||
|
||||
virtual ~IMergeTreeDataPart();
|
||||
@ -175,6 +184,7 @@ public:
|
||||
|
||||
/// A directory path (relative to storage's path) where part data is actually stored
|
||||
/// Examples: 'detached/tmp_fetch_<name>', 'tmp_<name>', '<name>'
|
||||
/// NOTE: Cannot have trailing slash.
|
||||
mutable String relative_path;
|
||||
MergeTreeIndexGranularityInfo index_granularity_info;
|
||||
|
||||
@ -341,7 +351,9 @@ public:
|
||||
|
||||
/// Calculate the total size of the entire directory with all the files
|
||||
static UInt64 calculateTotalSizeOnDisk(const DiskPtr & disk_, const String & from);
|
||||
void calculateColumnsSizesOnDisk();
|
||||
|
||||
/// Calculate column and secondary indices sizes on disk.
|
||||
void calculateColumnsAndSecondaryIndicesSizesOnDisk();
|
||||
|
||||
String getRelativePathForPrefix(const String & prefix) const;
|
||||
|
||||
@ -396,6 +408,10 @@ protected:
|
||||
/// Size for each column, calculated once in calcuateColumnSizesOnDisk
|
||||
ColumnSizeByName columns_sizes;
|
||||
|
||||
ColumnSize total_secondary_indices_size;
|
||||
|
||||
IndexSizeByName secondary_index_sizes;
|
||||
|
||||
/// Total size on disk, not only columns. May not contain size of
|
||||
/// checksums.txt and columns.txt. 0 - if not counted;
|
||||
UInt64 bytes_on_disk{0};
|
||||
@ -450,6 +466,10 @@ private:
|
||||
|
||||
void loadPartitionAndMinMaxIndex();
|
||||
|
||||
void calculateColumnsSizesOnDisk();
|
||||
|
||||
void calculateSecondaryIndicesSizesOnDisk();
|
||||
|
||||
/// Load default compression codec from file default_compression_codec.txt
|
||||
/// if it not exists tries to deduce codec from compressed column without
|
||||
/// any specifial compression.
|
||||
|
@ -89,7 +89,10 @@ static void extractMergingAndGatheringColumns(
|
||||
|
||||
bool MergeTask::ExecuteAndFinalizeHorizontalPart::prepare()
|
||||
{
|
||||
const String local_tmp_prefix = global_ctx->parent_part ? ctx->prefix : "tmp_merge_";
|
||||
// projection parts have different prefix and suffix compared to normal parts.
|
||||
// E.g. `proj_a.proj` for a normal projection merge and `proj_a.tmp_proj` for a projection materialization merge.
|
||||
const String local_tmp_prefix = global_ctx->parent_part ? "" : "tmp_merge_";
|
||||
const String local_tmp_suffix = global_ctx->parent_part ? ctx->suffix : "";
|
||||
|
||||
if (global_ctx->merges_blocker->isCancelled())
|
||||
throw Exception("Cancelled merging parts", ErrorCodes::ABORTED);
|
||||
@ -114,7 +117,8 @@ bool MergeTask::ExecuteAndFinalizeHorizontalPart::prepare()
|
||||
}
|
||||
|
||||
ctx->disk = global_ctx->space_reservation->getDisk();
|
||||
auto local_new_part_tmp_path = global_ctx->data->relative_data_path + local_tmp_prefix + global_ctx->future_part->name + (global_ctx->parent_part ? ".proj" : "") + "/";
|
||||
auto local_new_part_relative_tmp_path_name = local_tmp_prefix + global_ctx->future_part->name + local_tmp_suffix;
|
||||
auto local_new_part_tmp_path = global_ctx->data->relative_data_path + local_new_part_relative_tmp_path_name + "/";
|
||||
if (ctx->disk->exists(local_new_part_tmp_path))
|
||||
throw Exception("Directory " + fullPath(ctx->disk, local_new_part_tmp_path) + " already exists", ErrorCodes::DIRECTORY_ALREADY_EXISTS);
|
||||
|
||||
@ -138,7 +142,7 @@ bool MergeTask::ExecuteAndFinalizeHorizontalPart::prepare()
|
||||
global_ctx->future_part->type,
|
||||
global_ctx->future_part->part_info,
|
||||
local_single_disk_volume,
|
||||
local_tmp_prefix + global_ctx->future_part->name + (global_ctx->parent_part ? ".proj" : ""),
|
||||
local_new_part_relative_tmp_path_name,
|
||||
global_ctx->parent_part);
|
||||
|
||||
global_ctx->new_data_part->uuid = global_ctx->future_part->uuid;
|
||||
@ -526,7 +530,9 @@ bool MergeTask::MergeProjectionsStage::mergeMinMaxIndexAndPrepareProjections() c
|
||||
auto projection_future_part = std::make_shared<FutureMergedMutatedPart>();
|
||||
projection_future_part->assign(std::move(projection_parts));
|
||||
projection_future_part->name = projection.name;
|
||||
projection_future_part->path = global_ctx->future_part->path + "/" + projection.name + ".proj/";
|
||||
// TODO (ab): path in future_part is only for merge process introspection, which is not available for merges of projection parts.
|
||||
// Let's comment this out to avoid code inconsistency and add it back after we implement projection merge introspection.
|
||||
// projection_future_part->path = global_ctx->future_part->path + "/" + projection.name + ".proj/";
|
||||
projection_future_part->part_info = {"all", 0, 0, 0};
|
||||
|
||||
MergeTreeData::MergingParams projection_merging_params;
|
||||
@ -553,7 +559,7 @@ bool MergeTask::MergeProjectionsStage::mergeMinMaxIndexAndPrepareProjections() c
|
||||
global_ctx->deduplicate_by_columns,
|
||||
projection_merging_params,
|
||||
global_ctx->new_data_part.get(),
|
||||
"", // empty string for projection
|
||||
".proj",
|
||||
global_ctx->data,
|
||||
global_ctx->merges_blocker,
|
||||
global_ctx->ttl_merges_blocker));
|
||||
|
@ -58,7 +58,7 @@ public:
|
||||
Names deduplicate_by_columns_,
|
||||
MergeTreeData::MergingParams merging_params_,
|
||||
const IMergeTreeDataPart * parent_part_,
|
||||
String prefix_,
|
||||
String suffix_,
|
||||
MergeTreeData * data_,
|
||||
ActionBlocker * merges_blocker_,
|
||||
ActionBlocker * ttl_merges_blocker_)
|
||||
@ -83,7 +83,7 @@ public:
|
||||
|
||||
auto prepare_stage_ctx = std::make_shared<ExecuteAndFinalizeHorizontalPartRuntimeContext>();
|
||||
|
||||
prepare_stage_ctx->prefix = std::move(prefix_);
|
||||
prepare_stage_ctx->suffix = std::move(suffix_);
|
||||
prepare_stage_ctx->merging_params = std::move(merging_params_);
|
||||
|
||||
(*stages.begin())->setRuntimeContext(std::move(prepare_stage_ctx), global_ctx);
|
||||
@ -170,7 +170,7 @@ private:
|
||||
struct ExecuteAndFinalizeHorizontalPartRuntimeContext : public IStageRuntimeContext //-V730
|
||||
{
|
||||
/// Dependencies
|
||||
String prefix;
|
||||
String suffix;
|
||||
MergeTreeData::MergingParams merging_params{};
|
||||
|
||||
DiskPtr tmp_disk{nullptr};
|
||||
|
@ -1167,7 +1167,7 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks)
|
||||
}
|
||||
}
|
||||
|
||||
calculateColumnSizesImpl();
|
||||
calculateColumnAndSecondaryIndexSizesImpl();
|
||||
|
||||
|
||||
LOG_DEBUG(log, "Loaded data parts ({} items)", data_parts_indexes.size());
|
||||
@ -2352,7 +2352,7 @@ bool MergeTreeData::renameTempPartAndReplace(
|
||||
{
|
||||
covered_part->remove_time.store(current_time, std::memory_order_relaxed);
|
||||
modifyPartState(covered_part, DataPartState::Outdated);
|
||||
removePartContributionToColumnSizes(covered_part);
|
||||
removePartContributionToColumnAndSecondaryIndexSizes(covered_part);
|
||||
reduce_bytes += covered_part->getBytesOnDisk();
|
||||
reduce_rows += covered_part->rows_count;
|
||||
++reduce_parts;
|
||||
@ -2361,7 +2361,7 @@ bool MergeTreeData::renameTempPartAndReplace(
|
||||
decreaseDataVolume(reduce_bytes, reduce_rows, reduce_parts);
|
||||
|
||||
modifyPartState(part_it, DataPartState::Committed);
|
||||
addPartContributionToColumnSizes(part);
|
||||
addPartContributionToColumnAndSecondaryIndexSizes(part);
|
||||
addPartContributionToDataVolume(part);
|
||||
}
|
||||
|
||||
@ -2404,7 +2404,7 @@ void MergeTreeData::removePartsFromWorkingSet(const MergeTreeData::DataPartsVect
|
||||
{
|
||||
if (part->getState() == IMergeTreeDataPart::State::Committed)
|
||||
{
|
||||
removePartContributionToColumnSizes(part);
|
||||
removePartContributionToColumnAndSecondaryIndexSizes(part);
|
||||
removePartContributionToDataVolume(part);
|
||||
}
|
||||
|
||||
@ -2542,7 +2542,7 @@ restore_covered)
|
||||
if (part->getState() == DataPartState::Committed)
|
||||
{
|
||||
removePartContributionToDataVolume(part);
|
||||
removePartContributionToColumnSizes(part);
|
||||
removePartContributionToColumnAndSecondaryIndexSizes(part);
|
||||
}
|
||||
modifyPartState(it_part, DataPartState::Deleting);
|
||||
|
||||
@ -2590,7 +2590,7 @@ restore_covered)
|
||||
|
||||
if ((*it)->getState() != DataPartState::Committed)
|
||||
{
|
||||
addPartContributionToColumnSizes(*it);
|
||||
addPartContributionToColumnAndSecondaryIndexSizes(*it);
|
||||
addPartContributionToDataVolume(*it);
|
||||
modifyPartState(it, DataPartState::Committed); // iterator is not invalidated here
|
||||
}
|
||||
@ -2621,7 +2621,7 @@ restore_covered)
|
||||
|
||||
if ((*it)->getState() != DataPartState::Committed)
|
||||
{
|
||||
addPartContributionToColumnSizes(*it);
|
||||
addPartContributionToColumnAndSecondaryIndexSizes(*it);
|
||||
addPartContributionToDataVolume(*it);
|
||||
modifyPartState(it, DataPartState::Committed);
|
||||
}
|
||||
@ -2973,17 +2973,17 @@ static void loadPartAndFixMetadataImpl(MergeTreeData::MutableDataPartPtr part)
|
||||
part->modification_time = disk->getLastModified(full_part_path).epochTime();
|
||||
}
|
||||
|
||||
void MergeTreeData::calculateColumnSizesImpl()
|
||||
void MergeTreeData::calculateColumnAndSecondaryIndexSizesImpl()
|
||||
{
|
||||
column_sizes.clear();
|
||||
|
||||
/// Take into account only committed parts
|
||||
auto committed_parts_range = getDataPartsStateRange(DataPartState::Committed);
|
||||
for (const auto & part : committed_parts_range)
|
||||
addPartContributionToColumnSizes(part);
|
||||
addPartContributionToColumnAndSecondaryIndexSizes(part);
|
||||
}
|
||||
|
||||
void MergeTreeData::addPartContributionToColumnSizes(const DataPartPtr & part)
|
||||
void MergeTreeData::addPartContributionToColumnAndSecondaryIndexSizes(const DataPartPtr & part)
|
||||
{
|
||||
for (const auto & column : part->getColumns())
|
||||
{
|
||||
@ -2991,9 +2991,17 @@ void MergeTreeData::addPartContributionToColumnSizes(const DataPartPtr & part)
|
||||
ColumnSize part_column_size = part->getColumnSize(column.name, *column.type);
|
||||
total_column_size.add(part_column_size);
|
||||
}
|
||||
|
||||
auto indexes_descriptions = getInMemoryMetadataPtr()->secondary_indices;
|
||||
for (const auto & index : indexes_descriptions)
|
||||
{
|
||||
IndexSize & total_secondary_index_size = secondary_index_sizes[index.name];
|
||||
IndexSize part_index_size = part->getSecondaryIndexSize(index.name);
|
||||
total_secondary_index_size.add(part_index_size);
|
||||
}
|
||||
}
|
||||
|
||||
void MergeTreeData::removePartContributionToColumnSizes(const DataPartPtr & part)
|
||||
void MergeTreeData::removePartContributionToColumnAndSecondaryIndexSizes(const DataPartPtr & part)
|
||||
{
|
||||
for (const auto & column : part->getColumns())
|
||||
{
|
||||
@ -3013,6 +3021,26 @@ void MergeTreeData::removePartContributionToColumnSizes(const DataPartPtr & part
|
||||
log_subtract(total_column_size.data_uncompressed, part_column_size.data_uncompressed, ".data_uncompressed");
|
||||
log_subtract(total_column_size.marks, part_column_size.marks, ".marks");
|
||||
}
|
||||
|
||||
auto indexes_descriptions = getInMemoryMetadataPtr()->secondary_indices;
|
||||
for (const auto & index : indexes_descriptions)
|
||||
{
|
||||
IndexSize & total_secondary_index_size = secondary_index_sizes[index.name];
|
||||
IndexSize part_secondary_index_size = part->getSecondaryIndexSize(index.name);
|
||||
|
||||
auto log_subtract = [&](size_t & from, size_t value, const char * field)
|
||||
{
|
||||
if (value > from)
|
||||
LOG_ERROR(log, "Possibly incorrect index size subtraction: {} - {} = {}, index: {}, field: {}",
|
||||
from, value, from - value, index.name, field);
|
||||
|
||||
from -= value;
|
||||
};
|
||||
|
||||
log_subtract(total_secondary_index_size.data_compressed, part_secondary_index_size.data_compressed, ".data_compressed");
|
||||
log_subtract(total_secondary_index_size.data_uncompressed, part_secondary_index_size.data_uncompressed, ".data_uncompressed");
|
||||
log_subtract(total_secondary_index_size.marks, part_secondary_index_size.marks, ".marks");
|
||||
}
|
||||
}
|
||||
|
||||
void MergeTreeData::checkAlterPartitionIsPossible(
|
||||
@ -4043,7 +4071,7 @@ MergeTreeData::DataPartsVector MergeTreeData::Transaction::commit(MergeTreeData:
|
||||
reduce_rows += covered_part->rows_count;
|
||||
|
||||
data.modifyPartState(covered_part, DataPartState::Outdated);
|
||||
data.removePartContributionToColumnSizes(covered_part);
|
||||
data.removePartContributionToColumnAndSecondaryIndexSizes(covered_part);
|
||||
}
|
||||
reduce_parts += covered_parts.size();
|
||||
|
||||
@ -4052,7 +4080,7 @@ MergeTreeData::DataPartsVector MergeTreeData::Transaction::commit(MergeTreeData:
|
||||
++add_parts;
|
||||
|
||||
data.modifyPartState(part, DataPartState::Committed);
|
||||
data.addPartContributionToColumnSizes(part);
|
||||
data.addPartContributionToColumnAndSecondaryIndexSizes(part);
|
||||
}
|
||||
}
|
||||
data.decreaseDataVolume(reduce_bytes, reduce_rows, reduce_parts);
|
||||
@ -5321,26 +5349,33 @@ void MergeTreeData::setDataVolume(size_t bytes, size_t rows, size_t parts)
|
||||
total_active_size_parts.store(parts, std::memory_order_release);
|
||||
}
|
||||
|
||||
void MergeTreeData::insertQueryIdOrThrow(const String & query_id, size_t max_queries) const
|
||||
bool MergeTreeData::insertQueryIdOrThrow(const String & query_id, size_t max_queries) const
|
||||
{
|
||||
std::lock_guard lock(query_id_set_mutex);
|
||||
return insertQueryIdOrThrowNoLock(query_id, max_queries, lock);
|
||||
}
|
||||
|
||||
bool MergeTreeData::insertQueryIdOrThrowNoLock(const String & query_id, size_t max_queries, const std::lock_guard<std::mutex> &) const
|
||||
{
|
||||
if (query_id_set.find(query_id) != query_id_set.end())
|
||||
return;
|
||||
return false;
|
||||
if (query_id_set.size() >= max_queries)
|
||||
throw Exception(
|
||||
ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES, "Too many simultaneous queries for table {}. Maximum is: {}", log_name, max_queries);
|
||||
query_id_set.insert(query_id);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MergeTreeData::removeQueryId(const String & query_id) const
|
||||
{
|
||||
std::lock_guard lock(query_id_set_mutex);
|
||||
removeQueryIdNoLock(query_id, lock);
|
||||
}
|
||||
|
||||
void MergeTreeData::removeQueryIdNoLock(const String & query_id, const std::lock_guard<std::mutex> &) const
|
||||
{
|
||||
if (query_id_set.find(query_id) == query_id_set.end())
|
||||
{
|
||||
/// Do not throw exception, because this method is used in destructor.
|
||||
LOG_WARNING(log, "We have query_id removed but it's not recorded. This is a bug");
|
||||
assert(false);
|
||||
}
|
||||
else
|
||||
query_id_set.erase(query_id);
|
||||
}
|
||||
|
@ -654,6 +654,12 @@ public:
|
||||
return column_sizes;
|
||||
}
|
||||
|
||||
IndexSizeByName getSecondaryIndexSizes() const override
|
||||
{
|
||||
auto lock = lockParts();
|
||||
return secondary_index_sizes;
|
||||
}
|
||||
|
||||
/// For ATTACH/DETACH/DROP PARTITION.
|
||||
String getPartitionIDFromQuery(const ASTPtr & ast, ContextPtr context) const;
|
||||
std::unordered_set<String> getPartitionIDsFromQuery(const ASTs & asts, ContextPtr context) const;
|
||||
@ -794,11 +800,16 @@ public:
|
||||
/// section from config.xml.
|
||||
CompressionCodecPtr getCompressionCodecForPart(size_t part_size_compressed, const IMergeTreeDataPart::TTLInfos & ttl_infos, time_t current_time) const;
|
||||
|
||||
std::lock_guard<std::mutex> getQueryIdSetLock() const { return std::lock_guard<std::mutex>(query_id_set_mutex); }
|
||||
|
||||
/// Record current query id where querying the table. Throw if there are already `max_queries` queries accessing the same table.
|
||||
void insertQueryIdOrThrow(const String & query_id, size_t max_queries) const;
|
||||
/// Returns false if the `query_id` already exists in the running set, otherwise return true.
|
||||
bool insertQueryIdOrThrow(const String & query_id, size_t max_queries) const;
|
||||
bool insertQueryIdOrThrowNoLock(const String & query_id, size_t max_queries, const std::lock_guard<std::mutex> &) const;
|
||||
|
||||
/// Remove current query id after query finished.
|
||||
void removeQueryId(const String & query_id) const;
|
||||
void removeQueryIdNoLock(const String & query_id, const std::lock_guard<std::mutex> &) const;
|
||||
|
||||
/// Return the partition expression types as a Tuple type. Return DataTypeUInt8 if partition expression is empty.
|
||||
DataTypePtr getPartitionValueType() const;
|
||||
@ -873,6 +884,9 @@ protected:
|
||||
/// Current column sizes in compressed and uncompressed form.
|
||||
ColumnSizeByName column_sizes;
|
||||
|
||||
/// Current secondary index sizes in compressed and uncompressed form.
|
||||
IndexSizeByName secondary_index_sizes;
|
||||
|
||||
/// Engine-specific methods
|
||||
BrokenPartCallback broken_part_callback;
|
||||
|
||||
@ -1005,11 +1019,12 @@ protected:
|
||||
|
||||
void checkStoragePolicy(const StoragePolicyPtr & new_storage_policy) const;
|
||||
|
||||
/// Calculates column sizes in compressed form for the current state of data_parts. Call with data_parts mutex locked.
|
||||
void calculateColumnSizesImpl();
|
||||
/// Adds or subtracts the contribution of the part to compressed column sizes.
|
||||
void addPartContributionToColumnSizes(const DataPartPtr & part);
|
||||
void removePartContributionToColumnSizes(const DataPartPtr & part);
|
||||
/// Calculates column and secondary indexes sizes in compressed form for the current state of data_parts. Call with data_parts mutex locked.
|
||||
void calculateColumnAndSecondaryIndexSizesImpl();
|
||||
|
||||
/// Adds or subtracts the contribution of the part to compressed column and secondary indexes sizes.
|
||||
void addPartContributionToColumnAndSecondaryIndexSizes(const DataPartPtr & part);
|
||||
void removePartContributionToColumnAndSecondaryIndexSizes(const DataPartPtr & part);
|
||||
|
||||
/// If there is no part in the partition with ID `partition_id`, returns empty ptr. Should be called under the lock.
|
||||
DataPartPtr getAnyPartInPartition(const String & partition_id, DataPartsLock & data_parts_lock) const;
|
||||
|
@ -428,7 +428,7 @@ MergeTaskPtr MergeTreeDataMergerMutator::mergePartsToTemporaryPart(
|
||||
const Names & deduplicate_by_columns,
|
||||
const MergeTreeData::MergingParams & merging_params,
|
||||
const IMergeTreeDataPart * parent_part,
|
||||
const String & prefix)
|
||||
const String & suffix)
|
||||
{
|
||||
return std::make_shared<MergeTask>(
|
||||
future_part,
|
||||
@ -442,7 +442,7 @@ MergeTaskPtr MergeTreeDataMergerMutator::mergePartsToTemporaryPart(
|
||||
deduplicate_by_columns,
|
||||
merging_params,
|
||||
parent_part,
|
||||
prefix,
|
||||
suffix,
|
||||
&data,
|
||||
&merges_blocker,
|
||||
&ttl_merges_blocker);
|
||||
|
@ -108,7 +108,7 @@ public:
|
||||
const Names & deduplicate_by_columns,
|
||||
const MergeTreeData::MergingParams & merging_params,
|
||||
const IMergeTreeDataPart * parent_part = nullptr,
|
||||
const String & prefix = "");
|
||||
const String & suffix = "");
|
||||
|
||||
/// Mutate a single data part with the specified commands. Will create and return a temporary part.
|
||||
MutateTaskPtr mutatePartToTemporaryPart(
|
||||
|
@ -173,9 +173,7 @@ QueryPlanPtr MergeTreeDataSelectExecutor::read(
|
||||
auto projection_plan = std::make_unique<QueryPlan>();
|
||||
if (query_info.projection->desc->is_minmax_count_projection)
|
||||
{
|
||||
Pipe pipe(std::make_shared<SourceFromSingleChunk>(
|
||||
query_info.minmax_count_projection_block.cloneEmpty(),
|
||||
Chunk(query_info.minmax_count_projection_block.getColumns(), query_info.minmax_count_projection_block.rows())));
|
||||
Pipe pipe(std::make_shared<SourceFromSingleChunk>(query_info.minmax_count_projection_block));
|
||||
auto read_from_pipe = std::make_unique<ReadFromPreparedSource>(std::move(pipe));
|
||||
projection_plan->addStep(std::move(read_from_pipe));
|
||||
}
|
||||
@ -993,47 +991,48 @@ RangesInDataParts MergeTreeDataSelectExecutor::filterPartsByPrimaryKeyAndSkipInd
|
||||
|
||||
std::shared_ptr<QueryIdHolder> MergeTreeDataSelectExecutor::checkLimits(
|
||||
const MergeTreeData & data,
|
||||
const RangesInDataParts & parts_with_ranges,
|
||||
const ReadFromMergeTree::AnalysisResult & result,
|
||||
const ContextPtr & context)
|
||||
{
|
||||
const auto & settings = context->getSettingsRef();
|
||||
// Check limitations. query_id is used as the quota RAII's resource key.
|
||||
String query_id;
|
||||
const auto data_settings = data.getSettings();
|
||||
auto max_partitions_to_read
|
||||
= settings.max_partitions_to_read.changed ? settings.max_partitions_to_read : data_settings->max_partitions_to_read;
|
||||
if (max_partitions_to_read > 0)
|
||||
{
|
||||
const auto data_settings = data.getSettings();
|
||||
auto max_partitions_to_read
|
||||
= settings.max_partitions_to_read.changed ? settings.max_partitions_to_read : data_settings->max_partitions_to_read;
|
||||
if (max_partitions_to_read > 0)
|
||||
{
|
||||
std::set<String> partitions;
|
||||
for (const auto & part_with_ranges : parts_with_ranges)
|
||||
partitions.insert(part_with_ranges.data_part->info.partition_id);
|
||||
if (partitions.size() > size_t(max_partitions_to_read))
|
||||
throw Exception(
|
||||
ErrorCodes::TOO_MANY_PARTITIONS,
|
||||
"Too many partitions to read. Current {}, max {}",
|
||||
partitions.size(),
|
||||
max_partitions_to_read);
|
||||
}
|
||||
std::set<String> partitions;
|
||||
for (const auto & part_with_ranges : result.parts_with_ranges)
|
||||
partitions.insert(part_with_ranges.data_part->info.partition_id);
|
||||
if (partitions.size() > size_t(max_partitions_to_read))
|
||||
throw Exception(
|
||||
ErrorCodes::TOO_MANY_PARTITIONS,
|
||||
"Too many partitions to read. Current {}, max {}",
|
||||
partitions.size(),
|
||||
max_partitions_to_read);
|
||||
}
|
||||
|
||||
if (data_settings->max_concurrent_queries > 0 && data_settings->min_marks_to_honor_max_concurrent_queries > 0)
|
||||
if (data_settings->max_concurrent_queries > 0 && data_settings->min_marks_to_honor_max_concurrent_queries > 0
|
||||
&& result.selected_marks >= data_settings->min_marks_to_honor_max_concurrent_queries)
|
||||
{
|
||||
auto query_id = context->getCurrentQueryId();
|
||||
if (!query_id.empty())
|
||||
{
|
||||
size_t sum_marks = 0;
|
||||
for (const auto & part : parts_with_ranges)
|
||||
sum_marks += part.getMarksCount();
|
||||
|
||||
if (sum_marks >= data_settings->min_marks_to_honor_max_concurrent_queries)
|
||||
auto lock = data.getQueryIdSetLock();
|
||||
if (data.insertQueryIdOrThrowNoLock(query_id, data_settings->max_concurrent_queries, lock))
|
||||
{
|
||||
query_id = context->getCurrentQueryId();
|
||||
if (!query_id.empty())
|
||||
data.insertQueryIdOrThrow(query_id, data_settings->max_concurrent_queries);
|
||||
try
|
||||
{
|
||||
return std::make_shared<QueryIdHolder>(query_id, data);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
/// If we fail to construct the holder, remove query_id explicitly to avoid leak.
|
||||
data.removeQueryIdNoLock(query_id, lock);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!query_id.empty())
|
||||
return std::make_shared<QueryIdHolder>(query_id, data);
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -197,7 +197,7 @@ public:
|
||||
/// Also, return QueryIdHolder. If not null, we should keep it until query finishes.
|
||||
static std::shared_ptr<QueryIdHolder> checkLimits(
|
||||
const MergeTreeData & data,
|
||||
const RangesInDataParts & parts_with_ranges,
|
||||
const ReadFromMergeTree::AnalysisResult & result,
|
||||
const ContextPtr & context);
|
||||
};
|
||||
|
||||
|
@ -575,7 +575,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataWriter::writeTempProjectionPart(
|
||||
return writeProjectionPartImpl(
|
||||
part_name,
|
||||
part_type,
|
||||
"tmp_insert_" + part_name + ".proj" /* relative_path */,
|
||||
part_name + ".tmp_proj" /* relative_path */,
|
||||
true /* is_temp */,
|
||||
parent_part,
|
||||
data,
|
||||
|
@ -60,11 +60,16 @@ struct MergeTreePartInfo
|
||||
/// True if contains rhs (this part is obtained by merging rhs with some other parts or mutating rhs)
|
||||
bool contains(const MergeTreePartInfo & rhs) const
|
||||
{
|
||||
/// Containing part may have equal level iff block numbers are equal (unless level is MAX_LEVEL)
|
||||
/// (e.g. all_0_5_2 does not contain all_0_4_2, but all_0_5_3 or all_0_4_2_9 do)
|
||||
bool strictly_contains_block_range = (min_block == rhs.min_block && max_block == rhs.max_block) || level > rhs.level
|
||||
|| level == MAX_LEVEL || level == LEGACY_MAX_LEVEL;
|
||||
return partition_id == rhs.partition_id /// Parts for different partitions are not merged
|
||||
&& min_block <= rhs.min_block
|
||||
&& max_block >= rhs.max_block
|
||||
&& level >= rhs.level
|
||||
&& mutation >= rhs.mutation;
|
||||
&& mutation >= rhs.mutation
|
||||
&& strictly_contains_block_range;
|
||||
}
|
||||
|
||||
/// Return part mutation version, if part wasn't mutated return zero
|
||||
|
@ -87,7 +87,8 @@ void MergedBlockOutputStream::writeSuffixAndFinalizePart(
|
||||
new_part->checksums = checksums;
|
||||
new_part->setBytesOnDisk(checksums.getTotalSizeOnDisk());
|
||||
new_part->index_granularity = writer->getIndexGranularity();
|
||||
new_part->calculateColumnsSizesOnDisk();
|
||||
new_part->calculateColumnsAndSecondaryIndicesSizesOnDisk();
|
||||
|
||||
if (default_codec != nullptr)
|
||||
new_part->default_codec = default_codec;
|
||||
new_part->storage.lockSharedData(*new_part);
|
||||
|
@ -475,7 +475,7 @@ void finalizeMutatedPart(
|
||||
new_data_part->setBytesOnDisk(
|
||||
MergeTreeData::DataPart::calculateTotalSizeOnDisk(new_data_part->volume->getDisk(), new_data_part->getFullRelativePath()));
|
||||
new_data_part->default_codec = codec;
|
||||
new_data_part->calculateColumnsSizesOnDisk();
|
||||
new_data_part->calculateColumnsAndSecondaryIndicesSizesOnDisk();
|
||||
new_data_part->storage.lockSharedData(*new_data_part);
|
||||
}
|
||||
|
||||
@ -654,7 +654,7 @@ public:
|
||||
{},
|
||||
projection_merging_params,
|
||||
ctx->new_data_part.get(),
|
||||
"tmp_merge_");
|
||||
".tmp_proj");
|
||||
|
||||
next_level_parts.push_back(executeHere(tmp_part_merge_task));
|
||||
|
||||
@ -832,8 +832,8 @@ bool PartMergerWriter::mutateOriginalPartAndPrepareProjections()
|
||||
auto projection_block = projection_squash.add({});
|
||||
if (projection_block)
|
||||
{
|
||||
projection_parts[projection.name].emplace_back(
|
||||
MergeTreeDataWriter::writeTempProjectionPart(*ctx->data, ctx->log, projection_block, projection, ctx->new_data_part.get(), ++block_num));
|
||||
projection_parts[projection.name].emplace_back(MergeTreeDataWriter::writeTempProjectionPart(
|
||||
*ctx->data, ctx->log, projection_block, projection, ctx->new_data_part.get(), ++block_num));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1082,7 +1082,7 @@ private:
|
||||
|
||||
if (!ctx->disk->isDirectory(it->path()))
|
||||
ctx->disk->createHardLink(it->path(), destination);
|
||||
else if (!startsWith("tmp_", it->name())) // ignore projection tmp merge dir
|
||||
else if (!endsWith(".tmp_proj", it->name())) // ignore projection tmp merge dir
|
||||
{
|
||||
// it's a projection part directory
|
||||
ctx->disk->createDirectories(destination);
|
||||
|
@ -102,7 +102,7 @@ IMergeTreeDataPart::Checksums checkDataPart(
|
||||
/// It also calculates checksum of projections.
|
||||
auto checksum_file = [&](const String & file_path, const String & file_name)
|
||||
{
|
||||
if (disk->isDirectory(file_path) && endsWith(file_name, ".proj") && !startsWith(file_name, "tmp_")) // ignore projection tmp merge dir
|
||||
if (disk->isDirectory(file_path) && endsWith(file_name, ".proj"))
|
||||
{
|
||||
auto projection_name = file_name.substr(0, file_name.size() - sizeof(".proj") + 1);
|
||||
auto pit = data_part->getProjectionParts().find(projection_name);
|
||||
@ -124,7 +124,8 @@ IMergeTreeDataPart::Checksums checkDataPart(
|
||||
auto file_buf = disk->readFile(proj_path);
|
||||
HashingReadBuffer hashing_buf(*file_buf);
|
||||
hashing_buf.ignoreAll();
|
||||
projection_checksums_data.files[MergeTreeDataPartCompact::DATA_FILE_NAME_WITH_EXTENSION] = IMergeTreeDataPart::Checksums::Checksum(hashing_buf.count(), hashing_buf.getHash());
|
||||
projection_checksums_data.files[MergeTreeDataPartCompact::DATA_FILE_NAME_WITH_EXTENSION]
|
||||
= IMergeTreeDataPart::Checksums::Checksum(hashing_buf.count(), hashing_buf.getHash());
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -140,7 +141,8 @@ IMergeTreeDataPart::Checksums checkDataPart(
|
||||
[&](const ISerialization::SubstreamPath & substream_path)
|
||||
{
|
||||
String projection_file_name = ISerialization::getFileNameForStream(projection_column, substream_path) + ".bin";
|
||||
checksums_data.files[projection_file_name] = checksum_compressed_file(disk, projection_path + projection_file_name);
|
||||
checksums_data.files[projection_file_name]
|
||||
= checksum_compressed_file(disk, projection_path + projection_file_name);
|
||||
},
|
||||
{});
|
||||
}
|
||||
|
@ -89,9 +89,6 @@ ProjectionDescription::getProjectionFromAST(const ASTPtr & definition_ast, const
|
||||
if (projection_definition->name.empty())
|
||||
throw Exception("Projection must have name in definition.", ErrorCodes::INCORRECT_QUERY);
|
||||
|
||||
if (startsWith(projection_definition->name, "tmp_"))
|
||||
throw Exception("Projection's name cannot start with 'tmp_'", ErrorCodes::INCORRECT_QUERY);
|
||||
|
||||
if (!projection_definition->query)
|
||||
throw Exception("QUERY is required for projection", ErrorCodes::INCORRECT_QUERY);
|
||||
|
||||
@ -220,13 +217,13 @@ void ProjectionDescription::recalculateWithNewColumns(const ColumnsDescription &
|
||||
Block ProjectionDescription::calculate(const Block & block, ContextPtr context) const
|
||||
{
|
||||
auto builder = InterpreterSelectQuery(
|
||||
query_ast,
|
||||
context,
|
||||
Pipe(std::make_shared<SourceFromSingleChunk>(block, Chunk(block.getColumns(), block.rows()))),
|
||||
SelectQueryOptions{
|
||||
type == ProjectionDescription::Type::Normal ? QueryProcessingStage::FetchColumns
|
||||
: QueryProcessingStage::WithMergeableState})
|
||||
.buildQueryPipeline();
|
||||
query_ast,
|
||||
context,
|
||||
Pipe(std::make_shared<SourceFromSingleChunk>(block, Chunk(block.getColumns(), block.rows()))),
|
||||
SelectQueryOptions{
|
||||
type == ProjectionDescription::Type::Normal ? QueryProcessingStage::FetchColumns
|
||||
: QueryProcessingStage::WithMergeableState})
|
||||
.buildQueryPipeline();
|
||||
builder.resize(1);
|
||||
builder.addTransform(std::make_shared<SquashingChunksTransform>(builder.getHeader(), block.rows(), 0));
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
#include <Storages/StorageJoin.h>
|
||||
#include <Storages/StorageFactory.h>
|
||||
#include <Storages/StorageSet.h>
|
||||
#include <Storages/TableLockHolder.h>
|
||||
#include <Interpreters/HashJoin.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <Parsers/ASTCreateQuery.h>
|
||||
#include <Parsers/ASTIdentifier.h>
|
||||
#include <Core/ColumnNumbers.h>
|
||||
#include <DataTypes/NestedUtils.h>
|
||||
#include <Disks/IDisk.h>
|
||||
#include <Interpreters/joinDispatch.h>
|
||||
#include <Interpreters/MutationsInterpreter.h>
|
||||
#include <Interpreters/TableJoin.h>
|
||||
@ -68,17 +68,24 @@ StorageJoin::StorageJoin(
|
||||
restore();
|
||||
}
|
||||
|
||||
RWLockImpl::LockHolder StorageJoin::tryLockTimedWithContext(const RWLock & lock, RWLockImpl::Type type, ContextPtr context) const
|
||||
{
|
||||
const String query_id = context ? context->getInitialQueryId() : RWLockImpl::NO_QUERY;
|
||||
const std::chrono::milliseconds acquire_timeout
|
||||
= context ? context->getSettingsRef().lock_acquire_timeout : std::chrono::seconds(DBMS_DEFAULT_LOCK_ACQUIRE_TIMEOUT_SEC);
|
||||
return tryLockTimed(lock, type, query_id, acquire_timeout);
|
||||
}
|
||||
|
||||
SinkToStoragePtr StorageJoin::write(const ASTPtr & query, const StorageMetadataPtr & metadata_snapshot, ContextPtr context)
|
||||
{
|
||||
std::lock_guard mutate_lock(mutate_mutex);
|
||||
return StorageSetOrJoinBase::write(query, metadata_snapshot, context);
|
||||
}
|
||||
|
||||
void StorageJoin::truncate(
|
||||
const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, ContextPtr, TableExclusiveLockHolder&)
|
||||
void StorageJoin::truncate(const ASTPtr &, const StorageMetadataPtr & metadata_snapshot, ContextPtr context, TableExclusiveLockHolder &)
|
||||
{
|
||||
std::lock_guard mutate_lock(mutate_mutex);
|
||||
std::unique_lock<std::shared_mutex> lock(rwlock);
|
||||
TableLockHolder holder = tryLockTimedWithContext(rwlock, RWLockImpl::Write, context);
|
||||
|
||||
disk->removeRecursive(path);
|
||||
disk->createDirectories(path);
|
||||
@ -128,7 +135,7 @@ void StorageJoin::mutate(const MutationCommands & commands, ContextPtr context)
|
||||
}
|
||||
|
||||
/// Now acquire exclusive lock and modify storage.
|
||||
std::unique_lock<std::shared_mutex> lock(rwlock);
|
||||
TableLockHolder holder = tryLockTimedWithContext(rwlock, RWLockImpl::Write, context);
|
||||
|
||||
join = std::move(new_data);
|
||||
increment = 1;
|
||||
@ -152,7 +159,7 @@ void StorageJoin::mutate(const MutationCommands & commands, ContextPtr context)
|
||||
}
|
||||
}
|
||||
|
||||
HashJoinPtr StorageJoin::getJoinLocked(std::shared_ptr<TableJoin> analyzed_join) const
|
||||
HashJoinPtr StorageJoin::getJoinLocked(std::shared_ptr<TableJoin> analyzed_join, ContextPtr context) const
|
||||
{
|
||||
auto metadata_snapshot = getInMemoryMetadataPtr();
|
||||
if (!analyzed_join->sameStrictnessAndKind(strictness, kind))
|
||||
@ -171,34 +178,36 @@ HashJoinPtr StorageJoin::getJoinLocked(std::shared_ptr<TableJoin> analyzed_join)
|
||||
analyzed_join->setRightKeys(key_names);
|
||||
|
||||
HashJoinPtr join_clone = std::make_shared<HashJoin>(analyzed_join, metadata_snapshot->getSampleBlock().sortColumns());
|
||||
join_clone->setLock(rwlock);
|
||||
|
||||
RWLockImpl::LockHolder holder = tryLockTimedWithContext(rwlock, RWLockImpl::Read, context);
|
||||
join_clone->setLock(holder);
|
||||
join_clone->reuseJoinedData(*join);
|
||||
|
||||
return join_clone;
|
||||
}
|
||||
|
||||
|
||||
void StorageJoin::insertBlock(const Block & block)
|
||||
void StorageJoin::insertBlock(const Block & block, ContextPtr context)
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lock(rwlock);
|
||||
TableLockHolder holder = tryLockTimedWithContext(rwlock, RWLockImpl::Write, context);
|
||||
join->addJoinedBlock(block, true);
|
||||
}
|
||||
|
||||
size_t StorageJoin::getSize() const
|
||||
size_t StorageJoin::getSize(ContextPtr context) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(rwlock);
|
||||
TableLockHolder holder = tryLockTimedWithContext(rwlock, RWLockImpl::Read, context);
|
||||
return join->getTotalRowCount();
|
||||
}
|
||||
|
||||
std::optional<UInt64> StorageJoin::totalRows(const Settings &) const
|
||||
std::optional<UInt64> StorageJoin::totalRows(const Settings &settings) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(rwlock);
|
||||
TableLockHolder holder = tryLockTimed(rwlock, RWLockImpl::Read, RWLockImpl::NO_QUERY, settings.lock_acquire_timeout);
|
||||
return join->getTotalRowCount();
|
||||
}
|
||||
|
||||
std::optional<UInt64> StorageJoin::totalBytes(const Settings &) const
|
||||
std::optional<UInt64> StorageJoin::totalBytes(const Settings &settings) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(rwlock);
|
||||
TableLockHolder holder = tryLockTimed(rwlock, RWLockImpl::Read, RWLockImpl::NO_QUERY, settings.lock_acquire_timeout);
|
||||
return join->getTotalByteCount();
|
||||
}
|
||||
|
||||
@ -207,9 +216,9 @@ DataTypePtr StorageJoin::joinGetCheckAndGetReturnType(const DataTypes & data_typ
|
||||
return join->joinGetCheckAndGetReturnType(data_types, column_name, or_null);
|
||||
}
|
||||
|
||||
ColumnWithTypeAndName StorageJoin::joinGet(const Block & block, const Block & block_with_columns_to_add) const
|
||||
ColumnWithTypeAndName StorageJoin::joinGet(const Block & block, const Block & block_with_columns_to_add, ContextPtr context) const
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lock(rwlock);
|
||||
TableLockHolder holder = tryLockTimedWithContext(rwlock, RWLockImpl::Read, context);
|
||||
return join->joinGet(block, block_with_columns_to_add);
|
||||
}
|
||||
|
||||
@ -370,10 +379,10 @@ size_t rawSize(const StringRef & t)
|
||||
class JoinSource : public SourceWithProgress
|
||||
{
|
||||
public:
|
||||
JoinSource(HashJoinPtr join_, std::shared_mutex & rwlock, UInt64 max_block_size_, Block sample_block_)
|
||||
JoinSource(HashJoinPtr join_, TableLockHolder lock_holder_, UInt64 max_block_size_, Block sample_block_)
|
||||
: SourceWithProgress(sample_block_)
|
||||
, join(join_)
|
||||
, lock(rwlock)
|
||||
, lock_holder(lock_holder_)
|
||||
, max_block_size(max_block_size_)
|
||||
, sample_block(std::move(sample_block_))
|
||||
{
|
||||
@ -421,7 +430,7 @@ protected:
|
||||
|
||||
private:
|
||||
HashJoinPtr join;
|
||||
std::shared_lock<std::shared_mutex> lock;
|
||||
TableLockHolder lock_holder;
|
||||
|
||||
UInt64 max_block_size;
|
||||
Block sample_block;
|
||||
@ -571,7 +580,7 @@ Pipe StorageJoin::read(
|
||||
const Names & column_names,
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
SelectQueryInfo & /*query_info*/,
|
||||
ContextPtr /*context*/,
|
||||
ContextPtr context,
|
||||
QueryProcessingStage::Enum /*processed_stage*/,
|
||||
size_t max_block_size,
|
||||
unsigned /*num_streams*/)
|
||||
@ -579,7 +588,8 @@ Pipe StorageJoin::read(
|
||||
metadata_snapshot->check(column_names, getVirtuals(), getStorageID());
|
||||
|
||||
Block source_sample_block = metadata_snapshot->getSampleBlockForColumns(column_names, getVirtuals(), getStorageID());
|
||||
return Pipe(std::make_shared<JoinSource>(join, rwlock, max_block_size, source_sample_block));
|
||||
RWLockImpl::LockHolder holder = tryLockTimedWithContext(rwlock, RWLockImpl::Read, context);
|
||||
return Pipe(std::make_shared<JoinSource>(join, std::move(holder), max_block_size, source_sample_block));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,7 +2,9 @@
|
||||
|
||||
#include <base/shared_ptr_helper.h>
|
||||
|
||||
#include <Common/RWLock.h>
|
||||
#include <Storages/StorageSet.h>
|
||||
#include <Storages/TableLockHolder.h>
|
||||
#include <Storages/JoinSettings.h>
|
||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||
|
||||
@ -35,7 +37,7 @@ public:
|
||||
|
||||
/// Return instance of HashJoin holding lock that protects from insertions to StorageJoin.
|
||||
/// HashJoin relies on structure of hash table that's why we need to return it with locked mutex.
|
||||
HashJoinPtr getJoinLocked(std::shared_ptr<TableJoin> analyzed_join) const;
|
||||
HashJoinPtr getJoinLocked(std::shared_ptr<TableJoin> analyzed_join, ContextPtr context) const;
|
||||
|
||||
/// Get result type for function "joinGet(OrNull)"
|
||||
DataTypePtr joinGetCheckAndGetReturnType(const DataTypes & data_types, const String & column_name, bool or_null) const;
|
||||
@ -43,7 +45,7 @@ public:
|
||||
/// Execute function "joinGet(OrNull)" on data block.
|
||||
/// Takes rwlock for read to prevent parallel StorageJoin updates during processing data block
|
||||
/// (but not during processing whole query, it's safe for joinGet that doesn't involve `used_flags` from HashJoin)
|
||||
ColumnWithTypeAndName joinGet(const Block & block, const Block & block_with_columns_to_add) const;
|
||||
ColumnWithTypeAndName joinGet(const Block & block, const Block & block_with_columns_to_add, ContextPtr context) const;
|
||||
|
||||
SinkToStoragePtr write(const ASTPtr & query, const StorageMetadataPtr & metadata_snapshot, ContextPtr context) override;
|
||||
|
||||
@ -73,12 +75,13 @@ private:
|
||||
|
||||
/// Protect state for concurrent use in insertFromBlock and joinBlock.
|
||||
/// Lock is stored in HashJoin instance during query and blocks concurrent insertions.
|
||||
mutable std::shared_mutex rwlock;
|
||||
mutable RWLock rwlock = RWLockImpl::create();
|
||||
mutable std::mutex mutate_mutex;
|
||||
|
||||
void insertBlock(const Block & block) override;
|
||||
void insertBlock(const Block & block, ContextPtr context) override;
|
||||
void finishInsert() override {}
|
||||
size_t getSize() const override;
|
||||
size_t getSize(ContextPtr context) const override;
|
||||
RWLockImpl::LockHolder tryLockTimedWithContext(const RWLock & lock, RWLockImpl::Type type, ContextPtr context) const;
|
||||
|
||||
protected:
|
||||
StorageJoin(
|
||||
|
@ -34,11 +34,11 @@ namespace ErrorCodes
|
||||
}
|
||||
|
||||
|
||||
class SetOrJoinSink : public SinkToStorage
|
||||
class SetOrJoinSink : public SinkToStorage, WithContext
|
||||
{
|
||||
public:
|
||||
SetOrJoinSink(
|
||||
StorageSetOrJoinBase & table_, const StorageMetadataPtr & metadata_snapshot_,
|
||||
ContextPtr ctx, StorageSetOrJoinBase & table_, const StorageMetadataPtr & metadata_snapshot_,
|
||||
const String & backup_path_, const String & backup_tmp_path_,
|
||||
const String & backup_file_name_, bool persistent_);
|
||||
|
||||
@ -60,6 +60,7 @@ private:
|
||||
|
||||
|
||||
SetOrJoinSink::SetOrJoinSink(
|
||||
ContextPtr ctx,
|
||||
StorageSetOrJoinBase & table_,
|
||||
const StorageMetadataPtr & metadata_snapshot_,
|
||||
const String & backup_path_,
|
||||
@ -67,6 +68,7 @@ SetOrJoinSink::SetOrJoinSink(
|
||||
const String & backup_file_name_,
|
||||
bool persistent_)
|
||||
: SinkToStorage(metadata_snapshot_->getSampleBlock())
|
||||
, WithContext(ctx)
|
||||
, table(table_)
|
||||
, metadata_snapshot(metadata_snapshot_)
|
||||
, backup_path(backup_path_)
|
||||
@ -84,7 +86,7 @@ void SetOrJoinSink::consume(Chunk chunk)
|
||||
/// Sort columns in the block. This is necessary, since Set and Join count on the same column order in different blocks.
|
||||
Block sorted_block = getHeader().cloneWithColumns(chunk.detachColumns()).sortColumns();
|
||||
|
||||
table.insertBlock(sorted_block);
|
||||
table.insertBlock(sorted_block, getContext());
|
||||
if (persistent)
|
||||
backup_stream.write(sorted_block);
|
||||
}
|
||||
@ -104,10 +106,11 @@ void SetOrJoinSink::onFinish()
|
||||
}
|
||||
|
||||
|
||||
SinkToStoragePtr StorageSetOrJoinBase::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, ContextPtr /*context*/)
|
||||
SinkToStoragePtr StorageSetOrJoinBase::write(const ASTPtr & /*query*/, const StorageMetadataPtr & metadata_snapshot, ContextPtr context)
|
||||
{
|
||||
UInt64 id = ++increment;
|
||||
return std::make_shared<SetOrJoinSink>(*this, metadata_snapshot, path, fs::path(path) / "tmp/", toString(id) + ".bin", persistent);
|
||||
return std::make_shared<SetOrJoinSink>(
|
||||
context, *this, metadata_snapshot, path, fs::path(path) / "tmp/", toString(id) + ".bin", persistent);
|
||||
}
|
||||
|
||||
|
||||
@ -155,10 +158,10 @@ StorageSet::StorageSet(
|
||||
}
|
||||
|
||||
|
||||
void StorageSet::insertBlock(const Block & block) { set->insertFromBlock(block.getColumnsWithTypeAndName()); }
|
||||
void StorageSet::insertBlock(const Block & block, ContextPtr) { set->insertFromBlock(block.getColumnsWithTypeAndName()); }
|
||||
void StorageSet::finishInsert() { set->finishInsert(); }
|
||||
|
||||
size_t StorageSet::getSize() const { return set->getTotalRowCount(); }
|
||||
size_t StorageSet::getSize(ContextPtr) const { return set->getTotalRowCount(); }
|
||||
std::optional<UInt64> StorageSet::totalRows(const Settings &) const { return set->getTotalRowCount(); }
|
||||
std::optional<UInt64> StorageSet::totalBytes(const Settings &) const { return set->getTotalByteCount(); }
|
||||
|
||||
@ -210,6 +213,7 @@ void StorageSetOrJoinBase::restore()
|
||||
|
||||
void StorageSetOrJoinBase::restoreFromFile(const String & file_path)
|
||||
{
|
||||
ContextPtr ctx = nullptr;
|
||||
auto backup_buf = disk->readFile(file_path);
|
||||
CompressedReadBuffer compressed_backup_buf(*backup_buf);
|
||||
NativeReader backup_stream(compressed_backup_buf, 0);
|
||||
@ -218,14 +222,14 @@ void StorageSetOrJoinBase::restoreFromFile(const String & file_path)
|
||||
while (Block block = backup_stream.read())
|
||||
{
|
||||
info.update(block);
|
||||
insertBlock(block);
|
||||
insertBlock(block, ctx);
|
||||
}
|
||||
|
||||
finishInsert();
|
||||
|
||||
/// TODO Add speed, compressed bytes, data volume in memory, compression ratio ... Generalize all statistics logging in project.
|
||||
LOG_INFO(&Poco::Logger::get("StorageSetOrJoinBase"), "Loaded from backup file {}. {} rows, {}. State has {} unique rows.",
|
||||
file_path, info.rows, ReadableSize(info.bytes), getSize());
|
||||
file_path, info.rows, ReadableSize(info.bytes), getSize(ctx));
|
||||
}
|
||||
|
||||
|
||||
|
@ -51,10 +51,10 @@ private:
|
||||
void restoreFromFile(const String & file_path);
|
||||
|
||||
/// Insert the block into the state.
|
||||
virtual void insertBlock(const Block & block) = 0;
|
||||
virtual void insertBlock(const Block & block, ContextPtr context) = 0;
|
||||
/// Call after all blocks were inserted.
|
||||
virtual void finishInsert() = 0;
|
||||
virtual size_t getSize() const = 0;
|
||||
virtual size_t getSize(ContextPtr context) const = 0;
|
||||
};
|
||||
|
||||
|
||||
@ -81,9 +81,9 @@ public:
|
||||
private:
|
||||
SetPtr set;
|
||||
|
||||
void insertBlock(const Block & block) override;
|
||||
void insertBlock(const Block & block, ContextPtr) override;
|
||||
void finishInsert() override;
|
||||
size_t getSize() const override;
|
||||
size_t getSize(ContextPtr) const override;
|
||||
|
||||
protected:
|
||||
StorageSet(
|
||||
|
@ -25,6 +25,9 @@ StorageSystemDataSkippingIndices::StorageSystemDataSkippingIndices(const Storage
|
||||
{ "type", std::make_shared<DataTypeString>() },
|
||||
{ "expr", std::make_shared<DataTypeString>() },
|
||||
{ "granularity", std::make_shared<DataTypeUInt64>() },
|
||||
{ "data_compressed_bytes", std::make_shared<DataTypeUInt64>() },
|
||||
{ "data_uncompressed_bytes", std::make_shared<DataTypeUInt64>() },
|
||||
{ "marks", std::make_shared<DataTypeUInt64>()}
|
||||
}));
|
||||
setInMemoryMetadata(storage_metadata);
|
||||
}
|
||||
@ -97,6 +100,7 @@ protected:
|
||||
continue;
|
||||
const auto indices = metadata_snapshot->getSecondaryIndices();
|
||||
|
||||
auto secondary_index_sizes = table->getSecondaryIndexSizes();
|
||||
for (const auto & index : indices)
|
||||
{
|
||||
++rows_count;
|
||||
@ -127,6 +131,21 @@ protected:
|
||||
// 'granularity' column
|
||||
if (column_mask[src_index++])
|
||||
res_columns[res_index++]->insert(index.granularity);
|
||||
|
||||
auto & secondary_index_size = secondary_index_sizes[index.name];
|
||||
|
||||
// 'compressed bytes' column
|
||||
if (column_mask[src_index++])
|
||||
res_columns[res_index++]->insert(secondary_index_size.data_compressed);
|
||||
|
||||
// 'uncompressed bytes' column
|
||||
|
||||
if (column_mask[src_index++])
|
||||
res_columns[res_index++]->insert(secondary_index_size.data_uncompressed);
|
||||
|
||||
/// 'marks' column
|
||||
if (column_mask[src_index++])
|
||||
res_columns[res_index++]->insert(secondary_index_size.marks);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,9 @@ StorageSystemParts::StorageSystemParts(const StorageID & table_id_)
|
||||
{"data_compressed_bytes", std::make_shared<DataTypeUInt64>()},
|
||||
{"data_uncompressed_bytes", std::make_shared<DataTypeUInt64>()},
|
||||
{"marks_bytes", std::make_shared<DataTypeUInt64>()},
|
||||
{"secondary_indices_compressed_bytes", std::make_shared<DataTypeUInt64>()},
|
||||
{"secondary_indices_uncompressed_bytes", std::make_shared<DataTypeUInt64>()},
|
||||
{"secondary_indices_marks_bytes", std::make_shared<DataTypeUInt64>()},
|
||||
{"modification_time", std::make_shared<DataTypeDateTime>()},
|
||||
{"remove_time", std::make_shared<DataTypeDateTime>()},
|
||||
{"refcount", std::make_shared<DataTypeUInt32>()},
|
||||
@ -98,6 +101,7 @@ void StorageSystemParts::processNextStorage(
|
||||
auto part_state = all_parts_state[part_number];
|
||||
|
||||
ColumnSize columns_size = part->getTotalColumnsSize();
|
||||
ColumnSize secondary_indexes_size = part->getTotalSeconaryIndicesSize();
|
||||
|
||||
size_t src_index = 0, res_index = 0;
|
||||
if (columns_mask[src_index++])
|
||||
@ -126,6 +130,12 @@ void StorageSystemParts::processNextStorage(
|
||||
columns[res_index++]->insert(columns_size.data_uncompressed);
|
||||
if (columns_mask[src_index++])
|
||||
columns[res_index++]->insert(columns_size.marks);
|
||||
if (columns_mask[src_index++])
|
||||
columns[res_index++]->insert(secondary_indexes_size.data_compressed);
|
||||
if (columns_mask[src_index++])
|
||||
columns[res_index++]->insert(secondary_indexes_size.data_uncompressed);
|
||||
if (columns_mask[src_index++])
|
||||
columns[res_index++]->insert(secondary_indexes_size.marks);
|
||||
if (columns_mask[src_index++])
|
||||
columns[res_index++]->insert(static_cast<UInt64>(part->modification_time));
|
||||
|
||||
|
@ -162,6 +162,16 @@
|
||||
"splitted": "unsplitted",
|
||||
"tidy": "disable",
|
||||
"with_coverage": false
|
||||
},
|
||||
{
|
||||
"compiler": "clang-13-ppc64le",
|
||||
"build-type": "",
|
||||
"sanitizer": "",
|
||||
"package-type": "binary",
|
||||
"bundled": "bundled",
|
||||
"splitted": "unsplitted",
|
||||
"tidy": "disable",
|
||||
"with_coverage": false
|
||||
}
|
||||
],
|
||||
"tests_config": {
|
||||
|
@ -89,10 +89,13 @@ def make_clickhouse_client(base_args):
|
||||
# hence we should use 'system'.
|
||||
database='system',
|
||||
settings=get_additional_client_options_dict(base_args))
|
||||
|
||||
def clickhouse_execute_one(base_args, *args, **kwargs):
|
||||
return make_clickhouse_client(base_args).execute_one(*args, **kwargs)
|
||||
|
||||
def clickhouse_execute(base_args, *args, **kwargs):
|
||||
return make_clickhouse_client(base_args).execute(*args, **kwargs)
|
||||
|
||||
def clickhouse_execute_pandas(base_args, *args, **kwargs):
|
||||
return make_clickhouse_client(base_args).execute_pandas(*args, **kwargs)
|
||||
|
||||
@ -109,6 +112,7 @@ def stop_tests():
|
||||
global restarted_tests
|
||||
|
||||
with stop_tests_triggered_lock:
|
||||
print("Stopping tests")
|
||||
if not stop_tests_triggered.is_set():
|
||||
stop_tests_triggered.set()
|
||||
|
||||
@ -875,7 +879,7 @@ def run_tests_array(all_tests_with_params):
|
||||
|
||||
while True:
|
||||
if is_concurrent:
|
||||
case = queue.get()
|
||||
case = queue.get(timeout=args.timeout * 1.1)
|
||||
if not case:
|
||||
break
|
||||
else:
|
||||
@ -1076,10 +1080,10 @@ def do_run_tests(jobs, test_suite: TestSuite, parallel):
|
||||
pool.map_async(run_tests_array, parallel_tests_array)
|
||||
|
||||
for suit in test_suite.parallel_tests:
|
||||
queue.put(suit)
|
||||
queue.put(suit, timeout=args.timeout * 1.1)
|
||||
|
||||
for _ in range(jobs):
|
||||
queue.put(None)
|
||||
queue.put(None, timeout=args.timeout * 1.1)
|
||||
|
||||
queue.close()
|
||||
|
||||
|
@ -211,7 +211,7 @@ def test_errors_handling():
|
||||
assert "Table default.t already exists" in e.display_text
|
||||
|
||||
def test_authentication():
|
||||
query("CREATE USER john IDENTIFIED BY 'qwe123'")
|
||||
query("CREATE USER OR REPLACE john IDENTIFIED BY 'qwe123'")
|
||||
assert query("SELECT currentUser()", user_name="john", password="qwe123") == "john\n"
|
||||
|
||||
def test_logs():
|
||||
|
@ -980,3 +980,33 @@ def mysql_settings_test(clickhouse_node, mysql_node, service_name):
|
||||
clickhouse_node.query("DROP DATABASE test_database")
|
||||
mysql_node.query("DROP DATABASE test_database")
|
||||
|
||||
def materialized_mysql_large_transaction(clickhouse_node, mysql_node, service_name):
|
||||
mysql_node.query("DROP DATABASE IF EXISTS largetransaction")
|
||||
clickhouse_node.query("DROP DATABASE IF EXISTS largetransaction")
|
||||
mysql_node.query("CREATE DATABASE largetransaction")
|
||||
|
||||
mysql_node.query("CREATE TABLE largetransaction.test_table ("
|
||||
"`key` INT NOT NULL PRIMARY KEY AUTO_INCREMENT, "
|
||||
"`value` INT NOT NULL) ENGINE = InnoDB;")
|
||||
num_rows = 200000
|
||||
rows_per_insert = 5000
|
||||
values = ",".join(["(1)" for _ in range(rows_per_insert)])
|
||||
for i in range(num_rows//rows_per_insert):
|
||||
mysql_node.query(f"INSERT INTO largetransaction.test_table (`value`) VALUES {values};")
|
||||
|
||||
|
||||
clickhouse_node.query("CREATE DATABASE largetransaction ENGINE = MaterializedMySQL('{}:3306', 'largetransaction', 'root', 'clickhouse')".format(service_name))
|
||||
check_query(clickhouse_node, "SELECT COUNT() FROM largetransaction.test_table", f"{num_rows}\n")
|
||||
|
||||
mysql_node.query("UPDATE largetransaction.test_table SET value = 2;")
|
||||
|
||||
# Attempt to restart clickhouse after it has started processing
|
||||
# the transaction, but before it has completed it.
|
||||
while int(clickhouse_node.query("SELECT COUNT() FROM largetransaction.test_table WHERE value = 2")) == 0:
|
||||
time.sleep(0.2)
|
||||
clickhouse_node.restart_clickhouse()
|
||||
|
||||
check_query(clickhouse_node, "SELECT COUNT() FROM largetransaction.test_table WHERE value = 2", f"{num_rows}\n")
|
||||
|
||||
clickhouse_node.query("DROP DATABASE largetransaction")
|
||||
mysql_node.query("DROP DATABASE largetransaction")
|
||||
|
@ -237,3 +237,8 @@ def test_materialize_with_enum(started_cluster, started_mysql_8_0, started_mysql
|
||||
def test_mysql_settings(started_cluster, started_mysql_8_0, started_mysql_5_7, clickhouse_node):
|
||||
materialize_with_ddl.mysql_settings_test(clickhouse_node, started_mysql_5_7, "mysql57")
|
||||
materialize_with_ddl.mysql_settings_test(clickhouse_node, started_mysql_8_0, "mysql80")
|
||||
|
||||
@pytest.mark.parametrize(('clickhouse_node'), [pytest.param(node_db_ordinary, id="ordinary"), pytest.param(node_db_atomic, id="atomic")])
|
||||
def test_large_transaction(started_cluster, started_mysql_8_0, started_mysql_5_7, clickhouse_node):
|
||||
materialize_with_ddl.materialized_mysql_large_transaction(clickhouse_node, started_mysql_8_0, "mysql80")
|
||||
materialize_with_ddl.materialized_mysql_large_transaction(clickhouse_node, started_mysql_5_7, "mysql57")
|
||||
|
15
tests/performance/normalize_utf8.xml
Normal file
15
tests/performance/normalize_utf8.xml
Normal file
@ -0,0 +1,15 @@
|
||||
<test>
|
||||
<preconditions>
|
||||
<table_exists>hits_10m_single</table_exists>
|
||||
</preconditions>
|
||||
|
||||
<create_query>CREATE TABLE strings (words String) ENGINE Memory</create_query>
|
||||
<fill_query>INSERT INTO strings SELECT SearchPhrase FROM hits_10m_single WHERE length(SearchPhrase) > 0</fill_query>
|
||||
|
||||
<query>SELECT normalizeUTF8NFC(words) FROM strings FORMAT Null</query>
|
||||
<query>SELECT normalizeUTF8NFD(words) FROM strings FORMAT Null</query>
|
||||
<query>SELECT normalizeUTF8NFKC(words) FROM strings FORMAT Null</query>
|
||||
<query>SELECT normalizeUTF8NFKD(words) FROM strings FORMAT Null</query>
|
||||
|
||||
<drop_query>DROP TABLE IF EXISTS strings</drop_query>
|
||||
</test>
|
@ -7,9 +7,10 @@ CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
${CLICKHOUSE_CURL} --max-time 1 -sS "${CLICKHOUSE_URL}&query_id=cancel_http_readonly_queries_on_client_close&cancel_http_readonly_queries_on_client_close=1&query=SELECT+count()+FROM+system.numbers" 2>&1 | grep -cF 'curl: (28)'
|
||||
|
||||
while true
|
||||
do
|
||||
i=0 retries=300
|
||||
while [[ $i -lt $retries ]]; do
|
||||
${CLICKHOUSE_CURL} -sS --data "SELECT count() FROM system.processes WHERE query_id = 'cancel_http_readonly_queries_on_client_close'" "${CLICKHOUSE_URL}" | grep '0' && break
|
||||
((++i))
|
||||
sleep 0.2
|
||||
done
|
||||
|
||||
|
@ -79,7 +79,7 @@ timeout $TIMEOUT bash -c thread5 2> /dev/null &
|
||||
wait
|
||||
check_replication_consistency "alter_table" "count(), sum(a), sum(b), round(sum(c))"
|
||||
|
||||
$CLICKHOUSE_CLIENT -n -q "DROP TABLE alter_table0;" &
|
||||
$CLICKHOUSE_CLIENT -n -q "DROP TABLE alter_table1;" &
|
||||
$CLICKHOUSE_CLIENT -n -q "DROP TABLE alter_table0;" 2> >(grep -F -v 'is already started to be removing by another replica right now') &
|
||||
$CLICKHOUSE_CLIENT -n -q "DROP TABLE alter_table1;" 2> >(grep -F -v 'is already started to be removing by another replica right now') &
|
||||
|
||||
wait
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user