Merge remote-tracking branch 'upstream/master' into METR-23466

This commit is contained in:
proller 2016-12-07 22:08:09 +03:00
commit 76dc51aae5
105 changed files with 4061 additions and 2865 deletions

View File

@ -8,7 +8,13 @@ insert_final_newline = true
# Matches multiple files with brace expansion notation
# Set default charset
[*.{c,cpp,cxx,h,hpp,hxx,py}]
[*.{c,cpp,cxx,h,hpp,hxx,py,cmake}]
charset = utf-8
indent_style = tab
indent_size = 4
trim_trailing_whitespace = true
[CMakeLists.txt]
charset = utf-8
indent_style = tab
indent_size = 4

View File

@ -1,108 +1,123 @@
project (ClickHouse)
cmake_minimum_required(VERSION 2.6)
cmake_minimum_required (VERSION 2.6)
if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# require at least gcc 5
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5)
message(FATAL_ERROR "GCC version must be at least 5! For example, if GCC 5 is available under gcc-5, g++-5 names, do the following: export CC=gcc-5 CXX=g++-5; rm -rf CMakeCache.txt CMakeFiles; and re run cmake or ./release.")
endif()
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
# Require at least gcc 5
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5 AND NOT CMAKE_VERSION VERSION_LESS 2.8.9)
message (FATAL_ERROR "GCC version must be at least 5! For example, if GCC 5 is available under gcc-5, g++-5 names, do the following: export CC=gcc-5 CXX=g++-5; rm -rf CMakeCache.txt CMakeFiles; and re run cmake or ./release.")
endif ()
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang")
# require at least clang 3.8
# Require at least clang 3.8
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.8)
message(FATAL_ERROR "Clang version must be at least 3.8!")
endif()
else()
message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang 3.8+ and GCC 5+.")
endif()
message (FATAL_ERROR "Clang version must be at least 3.8!")
endif ()
else ()
message (WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang 3.8+ and GCC 5+.")
endif ()
if (APPLE)
set(APPLE_EXTRA_CXX_FLAG "-Dexp10=__exp10") # Also needed for libc++
endif()
set (APPLE_EXTRA_CXX_FLAG "-Dexp10=__exp10") # Also needed for libc++
endif ()
# отключаем варнинг о том, что в каждой директории должен быть CMakeLists.txt
cmake_policy(SET CMP0014 OLD)
cmake_policy (SET CMP0014 OLD) # Ignore warning about CMakeLists.txt in each directory
cmake_policy (SET CMP0012 NEW) # Don't dereference TRUE and FALSE
cmake_policy(SET CMP0012 NEW) # Don't dereference TRUE and FALSE
IF(NOT CMAKE_BUILD_TYPE)
message(STATUS "CMAKE_BUILD_TYPE is not set, set to default = RELWITHDEBINFO")
SET(CMAKE_BUILD_TYPE "RELWITHDEBINFO")
ENDIF()
MESSAGE( STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE} )
if (NOT CMAKE_BUILD_TYPE)
message (STATUS "CMAKE_BUILD_TYPE is not set, set to default = RELWITHDEBINFO")
set (CMAKE_BUILD_TYPE "RELWITHDEBINFO")
endif ()
message (STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE} )
# ASan - build type with address sanitizer
# UBSan - build type with undefined behaviour sanitizer
# TSan is not supported due to false positive errors in libstdc++ and necessity to rebuild libstdc++ with TSan
set(CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Debug;Release;MinSizeRel;ASan;UBSan" CACHE STRING "" FORCE)
set (CMAKE_CONFIGURATION_TYPES "RelWithDebInfo;Debug;Release;MinSizeRel;ASan;UBSan" CACHE STRING "" FORCE)
IF (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)")
SET(AARCH64 1)
ENDIF()
if (CMAKE_SYSTEM_PROCESSOR MATCHES "^(aarch64.*|AARCH64.*)")
set (AARCH64 1)
endif ()
IF (NOT AARCH64)
SET(MACHINE_FLAGS "-msse4 -mpopcnt")
ENDIF()
if (NOT AARCH64)
set (MACHINE_FLAGS "-msse4 -mpopcnt")
endif ()
SET(COMMON_WARNING_FLAGS "-Wall -Werror")
SET(CXX_WARNING_FLAGS "-Wnon-virtual-dtor -Wold-style-cast")
set (COMMON_WARNING_FLAGS "-Wall -Werror")
set (CXX_WARNING_FLAGS "-Wnon-virtual-dtor -Wold-style-cast")
set (ENABLE_CXX11_ABI FALSE CACHE BOOL "Enables C++11 ABI")
set (TEST_COVERAGE FALSE CACHE BOOL "Enables flags for test coverage")
set (ENABLE_TESTS TRUE CACHE BOOL "Enables tests")
set (USE_STATIC_LIBRARIES TRUE CACHE BOOL "Set to FALSE to use shared libraries")
if (NOT $ENV{USE_STATIC_LIBRARIES})
set (USE_STATIC_LIBRARIES FALSE)
endif ()
set (GLIBC_COMPATIBILITY FALSE CACHE BOOL "Set to TRUE to enable compatibility with older glibc libraries")
if ($ENV{GLIBC_COMPATIBILITY})
set (GLIBC_COMPATIBILITY TRUE)
endif()
endif ()
set (ENABLE_MONGODB TRUE CACHE BOOL "Set to TRUE to enable MongoDB support as source for external dictionaries")
if (NOT $ENV{ENABLE_MONGODB})
set (ENABLE_MONGODB FALSE)
endif ()
set (ENABLE_LIBTCMALLOC TRUE CACHE BOOL "Set to TRUE to enable libtcmalloc.")
if (NOT $ENV{ENABLE_LIBTCMALLOC})
set (ENABLE_LIBTCMALLOC FALSE)
endif ()
set (DEBUG_LIBTCMALLOC FALSE CACHE BOOL "Set to TRUE to use debug version of libtcmalloc.")
if ($ENV{DEBUG_LIBTCMALLOC})
set (ENABLE_LIBTCMALLOC TRUE)
endif ()
if (GLIBC_COMPATIBILITY)
SET(GLIBC_COMPATIBILITY_COMPILE_FLAGS "-include ${ClickHouse_SOURCE_DIR}/libs/libcommon/include/common/glibc_compatibility.h")
SET(GLIBC_COMPATIBILITY_LINK_FLAGS "-Wl,--wrap=memcpy")
endif()
set (GLIBC_COMPATIBILITY_COMPILE_FLAGS "-include ${ClickHouse_SOURCE_DIR}/libs/libcommon/include/common/glibc_compatibility.h")
set (GLIBC_COMPATIBILITY_LINK_FLAGS "-Wl,--wrap=memcpy")
endif ()
if (DISABLE_CXX11_ABI)
SET(CXX11_ABI "-D_GLIBCXX_USE_CXX11_ABI=0")
endif()
if (NOT ENABLE_CXX11_ABI)
set (CXX11_ABI "-D_GLIBCXX_USE_CXX11_ABI=0")
endif ()
SET(CMAKE_BUILD_COLOR_MAKEFILE ON)
SET(CMAKE_CXX_FLAGS "-std=gnu++1y ${APPLE_EXTRA_CXX_FLAG} -fno-omit-frame-pointer ${COMMON_WARNING_FLAGS} ${CXX_WARNING_FLAGS} ${MACHINE_FLAGS} ${GLIBC_COMPATIBILITY_COMPILE_FLAGS} ${CXX11_ABI}")
SET(CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
SET(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g")
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -ggdb3 -fno-inline")
set (CMAKE_BUILD_COLOR_MAKEFILE ON)
set (CMAKE_CXX_FLAGS "-std=gnu++1y ${APPLE_EXTRA_CXX_FLAG} -fno-omit-frame-pointer ${COMMON_WARNING_FLAGS} ${CXX_WARNING_FLAGS} ${MACHINE_FLAGS} ${GLIBC_COMPATIBILITY_COMPILE_FLAGS} ${CXX11_ABI}")
set (CMAKE_CXX_FLAGS_RELEASE "-O3 -DNDEBUG")
set (CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g")
set (CMAKE_CXX_FLAGS_DEBUG "-O0 -g3 -ggdb3 -fno-inline")
SET(CMAKE_C_FLAGS "-fno-omit-frame-pointer ${COMMON_WARNING_FLAGS} ${MACHINE_FLAGS} ${GLIBC_COMPATIBILITY_COMPILE_FLAGS} ${CXX11_ABI}")
SET(CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
SET(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g")
SET(CMAKE_C_FLAGS_DEBUG "-O0 -g3 -ggdb3 -fno-inline")
set (CMAKE_C_FLAGS "-fno-omit-frame-pointer ${COMMON_WARNING_FLAGS} ${MACHINE_FLAGS} ${GLIBC_COMPATIBILITY_COMPILE_FLAGS} ${CXX11_ABI}")
set (CMAKE_C_FLAGS_RELEASE "-O3 -DNDEBUG")
set (CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g")
set (CMAKE_C_FLAGS_DEBUG "-O0 -g3 -ggdb3 -fno-inline")
if (NOT APPLE)
SET(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ ${GLIBC_COMPATIBILITY_LINK_FLAGS} ${CXX11_ABI}")
endif()
set (CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++ ${GLIBC_COMPATIBILITY_LINK_FLAGS} ${CXX11_ABI}")
endif ()
# -fuse-ld=gold - fix linkage for gcc-5.4, gcc-6.1
# see more in http://stackoverflow.com/questions/37603238/fsanitize-not-using-gold-linker-in-gcc-6-1
SET(CMAKE_CXX_FLAGS_ASAN "-O3 -g -fsanitize=address -fno-omit-frame-pointer -fuse-ld=gold ${CXX11_ABI}")
SET(CMAKE_CXX_FLAGS_UBSAN "-O3 -g -fsanitize=undefined -fno-omit-frame-pointer ${CXX11_ABI}")
SET(CMAKE_C_FLAGS_ASAN "-O3 -g -fsanitize=address -fno-omit-frame-pointer -fuse-ld=gold ${CXX11_ABI}")
SET(CMAKE_C_FLAGS_UBSAN "-O3 -g -fsanitize=undefined -fno-omit-frame-pointer ${CXX11_ABI}")
# See more in http://stackoverflow.com/questions/37603238/fsanitize-not-using-gold-linker-in-gcc-6-1
set (CMAKE_CXX_FLAGS_ASAN "-O3 -g -fsanitize=address -fno-omit-frame-pointer -fuse-ld=gold ${CXX11_ABI}")
set (CMAKE_CXX_FLAGS_UBSAN "-O3 -g -fsanitize=undefined -fno-omit-frame-pointer ${CXX11_ABI}")
set (CMAKE_C_FLAGS_ASAN "-O3 -g -fsanitize=address -fno-omit-frame-pointer -fuse-ld=gold ${CXX11_ABI}")
set (CMAKE_C_FLAGS_UBSAN "-O3 -g -fsanitize=undefined -fno-omit-frame-pointer ${CXX11_ABI}")
# Флаги для test coverage
IF (TEST_COVERAGE)
SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g -fprofile-arcs -ftest-coverage -fPIC -DIS_DEBUG ${CXX11_ABI}")
ENDIF(TEST_COVERAGE)
# Flags for test coverage
if (TEST_COVERAGE)
set (CMAKE_CXX_FLAGS_DEBUG "-O0 -g -fprofile-arcs -ftest-coverage -fPIC -DIS_DEBUG ${CXX11_ABI}")
endif (TEST_COVERAGE)
# Собирать тесты?
IF (NOT DEFINED TESTS)
MESSAGE(STATUS "Tests are enabled")
SET(TESTS YES)
ENDIF()
# Run tests with "make check"
if (ENABLE_TESTS)
message (STATUS "Tests are enabled")
include (cmake/add_check.cmake)
endif (ENABLE_TESTS)
# тесты запускать с помощью "make check"
IF(TESTS)
INCLUDE(add.test.cmake)
ENDIF(TESTS)
# Префикс для установки
SET(CMAKE_INSTALL_PREFIX /usr)
# Installation prefix
set (CMAKE_INSTALL_PREFIX /usr)
include_directories (${ClickHouse_SOURCE_DIR}/contrib/libcityhash/include/)
include_directories (${ClickHouse_SOURCE_DIR}/contrib/liblz4/include/)
@ -140,61 +155,30 @@ include_directories (${ClickHouse_SOURCE_DIR}/libs/libzkutil/include/)
include_directories (${ClickHouse_SOURCE_DIR}/dbms/include)
include_directories (/usr/local/include/glib-2.0/)
include_directories (/usr/local/lib/glib-2.0/include/)
include_directories (/usr/include/glib-2.0/)
include_directories (/usr/lib64/glib-2.0/include/)
IF (AARCH64)
include_directories (/usr/lib/aarch64-linux-gnu/glib-2.0/include/)
ELSE()
include_directories (/usr/lib/x86_64-linux-gnu/glib-2.0/include/)
ENDIF()
include_directories (/usr/local/include/)
link_directories (/usr/local/lib)
# External libraries
# 1. openssl
include_directories ("/usr/local/opt/openssl/include")
set (OPENSSL_HINTS "/usr/local/opt/openssl/lib")
find_library (LIBSSL libssl.a HINTS ${OPENSSL_HINTS})
find_library (LIBCRYPTO libcrypto.a HINTS ${OPENSSL_HINTS})
set (OPENSSL_LIBS ${LIBSSL} ${LIBCRYPTO})
# 2. icu4c
include_directories ("/usr/local/opt/icu4c/include")
set (ICU_HINTS "/usr/local/opt/icu4c/lib")
find_library (ICUI18N libicui18n.a HINTS ${ICU_HINTS})
find_library (ICUUC libicuuc.a HINTS ${ICU_HINTS})
find_library (ICUDATA libicudata.a HINTS ${ICU_HINTS})
set (ICU_LIBS ${ICUI18N} ${ICUUC} ${ICUDATA})
# 3. boost
set (BOOST_HINTS "/usr/local/opt/boost/lib")
find_library (BOOST_PROGRAM_OPTIONS_LIB libboost_program_options.a HINTS ${BOOST_HINTS})
find_library (BOOST_SYSTEM_LIB libboost_system.a HINTS ${BOOST_HINTS})
find_library (BOOST_FILESYSTEM_LIB libboost_filesystem.a HINTS ${BOOST_HINTS})
find_library (BOOST_REGEX_LIB libboost_regex.a HINTS ${BOOST_HINTS})
find_library (BOOST_THREAD_LIB libboost_thread.a HINTS ${BOOST_HINTS})
# 4. ltdl
set (LTDL_HINTS "/usr/local/opt/libtool/lib")
find_library (LTDL_LIB libltdl.a HINTS ${LTDL_HINTS})
include (cmake/find_openssl.cmake)
include (cmake/find_icu4c.cmake)
include (cmake/find_boost.cmake)
include (cmake/find_libtool.cmake)
include (cmake/find_libmysqlclient.cmake)
include (cmake/find_rt.cmake)
# Directory for Yandex specific files
SET(CLICKHOUSE_PRIVATE_DIR ${ClickHouse_SOURCE_DIR}/private/)
set (CLICKHOUSE_PRIVATE_DIR ${ClickHouse_SOURCE_DIR}/private/)
add_subdirectory (contrib)
add_subdirectory (libs)
add_subdirectory (utils)
add_subdirectory (dbms)
IF (EXISTS ${CLICKHOUSE_PRIVATE_DIR})
if (EXISTS ${CLICKHOUSE_PRIVATE_DIR})
add_subdirectory (private)
ENDIF()
endif ()
message(STATUS "C_FLAGS: =${CMAKE_C_FLAGS}")
message(STATUS "CXX_FLAGS:=${CMAKE_CXX_FLAGS}")
message (STATUS "C_FLAGS: =${CMAKE_C_FLAGS}")
message (STATUS "CXX_FLAGS:=${CMAKE_CXX_FLAGS}")
include (cmake/print_include_directories.cmake)

View File

@ -1,6 +1,6 @@
==How to increase maxfiles on MacOS X==
## How to increase maxfiles on Mac OS X
To increase maxfiles on MacOS create the following file:
To increase maxfiles on MacOS, create the following file:
(Note: you'll need to use sudo)
@ -29,11 +29,11 @@ To increase maxfiles on MacOS create the following file:
</plist>
```
execute the following command:
Execute the following command:
```
sudo chown root:wheel /Library/LaunchDaemons/limit.maxfiles.plist
```
Reboot.
To check if it's working, you can use 'ulimit -n' command
To check if it's working, you can use `ulimit -n` command.

View File

@ -1,15 +0,0 @@
# добавляем вывод программы при ошибке теста
enable_testing()
if (CMAKE_CONFIGURATION_TYPES)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
--force-new-ctest-process --output-on-failure
--build-config "$<CONFIGURATION>")
else()
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
--force-new-ctest-process --output-on-failure)
endif()
macro (add_check target)
add_test(NAME test_${target} COMMAND ${target} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_dependencies(check ${target})
endmacro (add_check)

15
cmake/add_check.cmake Normal file
View File

@ -0,0 +1,15 @@
# Adding test output on failure
enable_testing ()
if (CMAKE_CONFIGURATION_TYPES)
add_custom_target (check COMMAND ${CMAKE_CTEST_COMMAND}
--force-new-ctest-process --output-on-failure
--build-config "$<CONFIGURATION>")
else ()
add_custom_target (check COMMAND ${CMAKE_CTEST_COMMAND}
--force-new-ctest-process --output-on-failure)
endif ()
macro (add_check target)
add_test (NAME test_${target} COMMAND ${target} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
add_dependencies (check ${target})
endmacro (add_check)

20
cmake/find_boost.cmake Normal file
View File

@ -0,0 +1,20 @@
set (Boost_USE_STATIC_LIBS ${USE_STATIC_LIBRARIES})
set (BOOST_ROOT "/usr/local")
find_package (Boost 1.57 COMPONENTS program_options system filesystem regex thread)
if (NOT Boost_FOUND)
# Try to find manually.
set (BOOST_HINTS "")
if (USE_STATIC_LIBRARIES)
find_library (Boost_PROGRAM_OPTIONS_LIBRARY libboost_program_options.a HINTS ${BOOST_HINTS})
find_library (Boost_SYSTEM_LIBRARY libboost_system.a HINTS ${BOOST_HINTS})
find_library (Boost_FILESYSTEM_LIBRARY libboost_filesystem.a HINTS ${BOOST_HINTS})
find_library (Boost_REGEX_LIBRARY libboost_regex.a HINTS ${BOOST_HINTS})
find_library (Boost_THREAD_LIBRARY libboost_thread.a HINTS ${BOOST_HINTS})
else ()
find_library (Boost_PROGRAM_OPTIONS_LIBRARY boost_program_options HINTS ${BOOST_HINTS})
find_library (Boost_SYSTEM_LIBRARY boost_system HINTS ${BOOST_HINTS})
find_library (Boost_FILESYSTEM_LIBRARY boost_filesystem HINTS ${BOOST_HINTS})
find_library (Boost_REGEX_LIBRARY boost_regex HINTS ${BOOST_HINTS})
find_library (Boost_THREAD_LIBRARY boost_thread HINTS ${BOOST_HINTS})
endif ()
endif ()

39
cmake/find_glib.cmake Normal file
View File

@ -0,0 +1,39 @@
set (GLIB_HINTS "/usr/local/opt/glib/lib")
set (GLIB_INCLUDE_HINTS
"/usr/local/include/glib-2.0/"
"/usr/local/lib/glib-2.0/include/"
"/usr/include/glib-2.0/"
"/usr/lib64/glib-2.0/include/")
if (AARCH64)
set (GLIB_INCLUDE_HINTS ${GLIB_INCLUDE_HINTS} "/usr/lib/aarch64-linux-gnu/glib-2.0/include/")
else ()
set (GLIB_INCLUDE_HINTS ${GLIB_INCLUDE_HINTS} "/usr/lib/x86_64-linux-gnu/glib-2.0/include/")
endif ()
if (USE_STATIC_LIBRARIES)
find_library (GLIB_LIB libglib-2.0.a HINTS ${GLIB_HINTS})
else ()
find_library (GLIB_LIB glib-2.0 HINTS ${GLIB_HINTS})
endif ()
find_path (GLIB_INCLUDE_DIR NAMES glib.h HINTS ${GLIB_INCLUDE_HINTS})
find_path (GLIB_CONFIG_INCLUDE_DIR NAMES glibconfig.h HINTS ${GLIB_INCLUDE_HINTS})
include_directories (${GLIB_INCLUDE_DIR})
include_directories (${GLIB_CONFIG_INCLUDE_DIR})
if (APPLE)
set (INTL_HINTS "/usr/local/opt/gettext/lib")
if (USE_STATIC_LIBRARIES)
find_library (INTL_LIB libintl.a HINTS ${INTL_HINTS})
else ()
find_library (INTL_LIB intl HINTS ${INTL_HINTS})
endif ()
set (ICONV_HINTS "/usr/local/opt/libiconv/lib")
if (USE_STATIC_LIBRARIES)
find_library (ICONV_LIB libiconv.a HINTS ${ICONV_HINTS})
else ()
find_library (ICONV_LIB iconv HINTS ${ICONV_HINTS})
endif ()
find_library (CORE_FOUNDATION_LIB CoreFoundation)
find_library (CARBON_LIB Carbon)
set (GLIB_LIBS ${GLIB_LIB} ${INTL_LIB} ${ICONV_LIB} ${CORE_FOUNDATION_LIB} ${CARBON_LIB})
else (APPLE)
set (GLIB_LIBS ${GLIB_LIB})
endif (APPLE)

12
cmake/find_icu4c.cmake Normal file
View File

@ -0,0 +1,12 @@
include_directories ("/usr/local/opt/icu4c/include")
set (ICU_HINTS "/usr/local/opt/icu4c/lib")
if (USE_STATIC_LIBRARIES)
find_library (ICUI18N libicui18n.a HINTS ${ICU_HINTS})
find_library (ICUUC libicuuc.a HINTS ${ICU_HINTS})
find_library (ICUDATA libicudata.a HINTS ${ICU_HINTS})
else ()
find_library (ICUI18N icui18n HINTS ${ICU_HINTS})
find_library (ICUUC icuuc HINTS ${ICU_HINTS})
find_library (ICUDATA icudata HINTS ${ICU_HINTS})
endif ()
set (ICU_LIBS ${ICUI18N} ${ICUUC} ${ICUDATA})

View File

@ -0,0 +1,9 @@
set (MYSQL_HINTS "/usr/local/opt/mysql/lib")
set (MYSQL_INCLUDE_HINTS "/usr/local/opt/mysql/include")
if (USE_STATIC_LIBRARIES)
find_library (STATIC_MYSQLCLIENT_LIB libmysqlclient.a HINTS ${MYSQL_HINTS})
else ()
find_library (MYSQLCLIENT_LIB mysqlclient HINTS ${MYSQL_HINTS})
endif ()
find_path (MYSQL_INCLUDE_DIR NAMES mysql.h PATH_SUFFIXES mysql HINTS ${MYSQL_INCLUDE_HINTS})
include_directories (${MYSQL_INCLUDE_DIR})

6
cmake/find_libtool.cmake Normal file
View File

@ -0,0 +1,6 @@
set (LTDL_HINTS "/usr/local/opt/libtool/lib")
if (USE_STATIC_LIBRARIES)
find_library (LTDL_LIB libltdl.a HINTS ${LTDL_HINTS})
else ()
find_library (LTDL_LIB ltdl HINTS ${LTDL_HINTS})
endif ()

21
cmake/find_openssl.cmake Normal file
View File

@ -0,0 +1,21 @@
set (OPENSSL_USE_STATIC_LIBS ${USE_STATIC_LIBRARIES})
if (APPLE)
set (OPENSSL_ROOT_DIR "/usr/local/opt/openssl")
endif ()
find_package (OpenSSL)
if (OPENSSL_FOUND)
include_directories (${OPENSSL_INCLUDE_DIR})
endif ()
if (NOT OPENSSL_FOUND)
# Try to find manually.
include_directories ("/usr/local/opt/openssl/include")
set (OPENSSL_HINTS "/usr/local/opt/openssl/lib")
if (USE_STATIC_LIBRARIES)
find_library (OPENSSL_SSL_LIBRARY libssl.a HINTS ${OPENSSL_HINTS})
find_library (OPENSSL_CRYPTO_LIBRARY libcrypto.a HINTS ${OPENSSL_HINTS})
else ()
find_library (OPENSSL_SSL_LIBRARY ssl HINTS ${OPENSSL_HINTS})
find_library (OPENSSL_CRYPTO_LIBRARY crypto HINTS ${OPENSSL_HINTS})
endif ()
set (OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY})
endif ()

16
cmake/find_rt.cmake Normal file
View File

@ -0,0 +1,16 @@
if (APPLE)
set (RT_LIBRARIES "apple_rt")
else ()
if (USE_STATIC_LIBRARIES)
set (RT_LIBRARIES "librt.a")
else ()
set (RT_LIBRARIES "rt")
endif ()
endif ()
function (target_link_rt_by_force TARGET)
if (NOT APPLE)
set (FLAGS "-Wl,-no-as-needed -lrt -Wl,-as-needed")
set_property (TARGET ${TARGET} APPEND PROPERTY LINK_FLAGS "${FLAGS}")
endif ()
endfunction ()

View File

@ -0,0 +1,6 @@
get_property (dirs DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY INCLUDE_DIRECTORIES)
file (WRITE ${CMAKE_CURRENT_BINARY_DIR}/include_directories.txt "")
foreach (dir ${dirs})
string (REPLACE "${ClickHouse_SOURCE_DIR}" "." dir "${dir}")
file (APPEND ${CMAKE_CURRENT_BINARY_DIR}/include_directories.txt "-I ${dir} ")
endforeach ()

View File

@ -1,4 +1,4 @@
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-old-style-cast")
set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-old-style-cast")
add_subdirectory (libcityhash)
add_subdirectory (liblz4)
@ -10,10 +10,10 @@ add_subdirectory (libpoco)
add_subdirectory (libre2)
add_subdirectory (libzookeeper)
if (NOT DEFINED $ENV{DISABLE_LIBTCMALLOC})
if (ENABLE_LIBTCMALLOC)
add_subdirectory (libtcmalloc)
endif()
endif ()
IF (NOT AARCH64)
if (NOT AARCH64)
add_subdirectory (libcpuid)
ENDIF()
endif ()

View File

@ -1,8 +1,8 @@
include_directories (${CMAKE_CURRENT_BINARY_DIR})
IF (NOT AARCH64) # Не используется. Портировать не сложно.
SET(SOURCES_ONLY_ON_X86_64 src/metrohash128crc.cpp)
ENDIF()
if (NOT AARCH64) # Not used. Pretty easy to port.
set (SOURCES_ONLY_ON_X86_64 src/metrohash128crc.cpp)
endif ()
add_library(metrohash
src/metrohash.h

View File

@ -5,8 +5,8 @@ cmake_minimum_required(VERSION 2.8.0)
# POCO_UNBUNDLED
# POCO_NO_LOCALE
#
# ENABLE_{COMPONENT}
# ENABLE_TESTS
# POCO_ENABLE_{COMPONENT}
# POCO_ENABLE_TESTS
project(Poco)
@ -68,30 +68,30 @@ set(CMAKE_DEBUG_POSTFIX "d" CACHE STRING "Set debug library postfix" FORCE)
include(PocoMacros)
# Allow enabling and disabling components
option(ENABLE_XML "Enable the XML" ON)
option(ENABLE_JSON "Enable the JSON" ON)
option(ENABLE_MONGODB "Enable MongoDB" ON)
option(ENABLE_PDF "Enable PDF" OFF)
option(ENABLE_UTIL "Enable Util" ON)
option(ENABLE_NET "Enable Net" ON)
option(ENABLE_NETSSL "Enable NetSSL" ON)
option(ENABLE_NETSSL_WIN "Enable NetSSL Windows" OFF)
option(ENABLE_CRYPTO "Enable Crypto" ON)
option(ENABLE_DATA "Enable Data" ON)
option(ENABLE_DATA_SQLITE "Enable Data SQlite" OFF)
option(ENABLE_DATA_MYSQL "Enable Data MySQL" OFF)
option(ENABLE_DATA_ODBC "Enable Data ODBC" ON)
option(ENABLE_SEVENZIP "Enable SevenZip" OFF)
option(ENABLE_ZIP "Enable Zip" ON)
option(ENABLE_APACHECONNECTOR "Enable ApacheConnector" OFF)
option(ENABLE_CPPPARSER "Enable C++ parser" OFF)
option(ENABLE_POCODOC "Enable Poco Documentation Generator" OFF)
option(ENABLE_PAGECOMPILER "Enable PageCompiler" ON)
option(ENABLE_PAGECOMPILER_FILE2PAGE "Enable File2Page" ON)
option(POCO_ENABLE_XML "Enable the XML" ON)
option(POCO_ENABLE_JSON "Enable the JSON" ON)
option(POCO_ENABLE_MONGODB "Enable MongoDB" ON)
option(POCO_ENABLE_PDF "Enable PDF" OFF)
option(POCO_ENABLE_UTIL "Enable Util" ON)
option(POCO_ENABLE_NET "Enable Net" ON)
option(POCO_ENABLE_NETSSL "Enable NetSSL" ON)
option(POCO_ENABLE_NETSSL_WIN "Enable NetSSL Windows" OFF)
option(POCO_ENABLE_CRYPTO "Enable Crypto" ON)
option(POCO_ENABLE_DATA "Enable Data" ON)
option(POCO_ENABLE_DATA_SQLITE "Enable Data SQlite" OFF)
option(POCO_ENABLE_DATA_MYSQL "Enable Data MySQL" OFF)
option(POCO_ENABLE_DATA_ODBC "Enable Data ODBC" ON)
option(POCO_ENABLE_SEVENZIP "Enable SevenZip" OFF)
option(POCO_ENABLE_ZIP "Enable Zip" ON)
option(POCO_ENABLE_APACHECONNECTOR "Enable ApacheConnector" OFF)
option(POCO_ENABLE_CPPPARSER "Enable C++ parser" OFF)
option(POCO_ENABLE_POCODOC "Enable Poco Documentation Generator" OFF)
option(POCO_ENABLE_PAGECOMPILER "Enable PageCompiler" ON)
option(POCO_ENABLE_PAGECOMPILER_FILE2PAGE "Enable File2Page" ON)
option(FORCE_OPENSSL "Force usage of OpenSSL even under windows" OFF)
option(ENABLE_TESTS
option(POCO_ENABLE_TESTS
"Set to OFF|ON (default is OFF) to control build of POCO tests & samples" OFF)
option(POCO_STATIC
@ -101,16 +101,16 @@ option(POCO_UNBUNDLED
"Set to OFF|ON (default is OFF) to control linking dependencies as external" OFF)
# Uncomment from next two lines to force statitc or dynamic library, default is autodetection
if(POCO_STATIC)
if (POCO_STATIC)
add_definitions( -DPOCO_STATIC -DPOCO_NO_AUTOMATIC_LIBS)
set( LIB_MODE STATIC )
message(STATUS "Building static libraries")
else(POCO_STATIC)
else (POCO_STATIC)
set( LIB_MODE SHARED )
message(STATUS "Building dynamic libraries")
endif(POCO_STATIC)
endif (POCO_STATIC)
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
include(CTest)
enable_testing()
message(STATUS "Building with unittests & samples")
@ -172,78 +172,78 @@ endif (${CMAKE_CXX_COMPILER_ID} MATCHES "SunPro")
# iOS
if (IOS)
add_definitions( -DPOCO_HAVE_IPv6 -DPOCO_NO_FPENVIRONMENT -DPOCO_NO_STAT64 -DPOCO_NO_SHAREDLIBS -DPOCO_NO_NET_IFTYPES )
endif(IOS)
endif (IOS)
#Android
if (ANDROID)
add_definitions( -DPOCO_ANDROID -DPOCO_NO_FPENVIRONMENT -DPOCO_NO_WSTRING -DPOCO_NO_SHAREDMEMORY )
endif(ANDROID)
endif (ANDROID)
# Collect the built libraries and include dirs, the will be used to create the PocoConfig.cmake file
set(Poco_COMPONENTS "")
set (Poco_COMPONENTS "")
if (ENABLE_TESTS)
add_subdirectory(CppUnit)
if (POCO_ENABLE_TESTS)
add_subdirectory (CppUnit)
endif ()
add_subdirectory(Foundation)
if(ENABLE_XML)
add_subdirectory(XML)
list(APPEND Poco_COMPONENTS "XML")
endif()
if(ENABLE_JSON)
add_subdirectory(JSON)
list(APPEND Poco_COMPONENTS "JSON")
endif()
if(ENABLE_MONGODB)
add_subdirectory(MongoDB)
list(APPEND Poco_COMPONENTS "MongoDB")
endif()
if(ENABLE_PDF)
add_subdirectory(PDF)
list(APPEND Poco_COMPONENTS "PDF")
endif()
if(ENABLE_UTIL)
add_subdirectory(Util)
list(APPEND Poco_COMPONENTS "Util")
endif()
if(ENABLE_NET)
add_subdirectory(Net)
list(APPEND Poco_COMPONENTS "Net")
add_subdirectory (Foundation)
if (POCO_ENABLE_XML)
add_subdirectory (XML)
list (APPEND Poco_COMPONENTS "XML")
endif ()
if (POCO_ENABLE_JSON)
add_subdirectory (JSON)
list (APPEND Poco_COMPONENTS "JSON")
endif ()
if (POCO_ENABLE_MONGODB)
add_subdirectory (MongoDB)
list (APPEND Poco_COMPONENTS "MongoDB")
endif ()
if (POCO_ENABLE_PDF)
add_subdirectory (PDF)
list (APPEND Poco_COMPONENTS "PDF")
endif()
if (POCO_ENABLE_UTIL)
add_subdirectory (Util)
list (APPEND Poco_COMPONENTS "Util")
endif ()
if (POCO_ENABLE_NET)
add_subdirectory (Net)
list (APPEND Poco_COMPONENTS "Net")
endif ()
#NetSSL
if(WIN32 AND ENABLE_NETSSL_WIN)
if(WIN32 AND POCO_ENABLE_NETSSL_WIN)
add_subdirectory(NetSSL_Win)
list(APPEND Poco_COMPONENTS "NetSSL_Win")
endif(WIN32 AND ENABLE_NETSSL_WIN)
endif(WIN32 AND POCO_ENABLE_NETSSL_WIN)
find_package(OpenSSL)
if(OPENSSL_FOUND)
include_directories("${OPENSSL_INCLUDE_DIR}")
if(ENABLE_NETSSL)
if(POCO_ENABLE_NETSSL)
add_subdirectory(NetSSL_OpenSSL)
list(APPEND Poco_COMPONENTS "NetSSL_OpenSSL")
endif()
if(ENABLE_CRYPTO)
if(POCO_ENABLE_CRYPTO)
add_subdirectory(Crypto)
list(APPEND Poco_COMPONENTS "Crypto")
endif()
endif(OPENSSL_FOUND)
if(ENABLE_DATA)
if(POCO_ENABLE_DATA)
add_subdirectory(Data)
list(APPEND Poco_COMPONENTS "Data")
endif()
if(ENABLE_SEVENZIP)
if(POCO_ENABLE_SEVENZIP)
add_subdirectory(SevenZip)
list(APPEND Poco_COMPONENTS "SevenZip")
endif()
if(ENABLE_ZIP)
if(POCO_ENABLE_ZIP)
add_subdirectory(Zip)
list(APPEND Poco_COMPONENTS "Zip")
endif()
@ -252,28 +252,28 @@ find_package(APR)
find_package(Apache2)
if(APRUTIL_FOUND AND APACHE_FOUND)
include_directories( "${APACHE_INCLUDE_DIR}" "${APRUTIL_INCLUDE_DIR}" )
if(ENABLE_APACHECONNECTOR)
if(POCO_ENABLE_APACHECONNECTOR)
add_subdirectory(ApacheConnector)
list(APPEND Poco_COMPONENTS "ApacheConnector")
endif()
endif(APRUTIL_FOUND AND APACHE_FOUND)
if(ENABLE_CPPPARSER)
if(POCO_ENABLE_CPPPARSER)
add_subdirectory(CppParser)
list(APPEND Poco_COMPONENTS "CppParser")
endif()
if(ENABLE_POCODOC)
if(POCO_ENABLE_POCODOC)
add_subdirectory(PocoDoc)
list(APPEND Poco_COMPONENTS "PocoDoc")
endif()
if(ENABLE_PAGECOMPILER)
if(POCO_ENABLE_PAGECOMPILER)
add_subdirectory(PageCompiler)
list(APPEND Poco_COMPONENTS "PageCompiler")
endif()
if(ENABLE_PAGECOMPILER_FILE2PAGE)
if(POCO_ENABLE_PAGECOMPILER_FILE2PAGE)
add_subdirectory(PageCompiler/File2Page)
list(APPEND Poco_COMPONENTS "File2Page")
endif()

View File

@ -21,7 +21,7 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoFoundation ${OPENSSL_LIBRARIES} )
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(samples)
add_subdirectory(testsuite)
endif ()

View File

@ -28,7 +28,7 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoFoundation)
if(ENABLE_DATA_MYSQL)
if(POCO_ENABLE_DATA_MYSQL)
find_package(MySQL)
if(MYSQL_FOUND)
include_directories("${MYSQL_INCLUDE_DIR}")
@ -37,9 +37,9 @@ if(ENABLE_DATA_MYSQL)
else()
message(STATUS "MySQL Support Disabled - no MySQL library")
endif(MYSQL_FOUND)
endif(ENABLE_DATA_MYSQL)
endif(POCO_ENABLE_DATA_MYSQL)
if(ENABLE_DATA_ODBC)
if(POCO_ENABLE_DATA_ODBC)
find_package(ODBC)
if(WIN32 AND NOT WINCE)
set(ODBC_LIBRARIES "odbc32" "odbccp32")
@ -54,9 +54,9 @@ if(ENABLE_DATA_ODBC)
message(STATUS "ODBC Support Disabled - no ODBC runtime")
endif()
endif(WIN32 AND NOT WINCE)
endif(ENABLE_DATA_ODBC)
endif(POCO_ENABLE_DATA_ODBC)
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(samples)
add_subdirectory(testsuite)
endif ()

View File

@ -21,6 +21,6 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoFoundation PocoData ${MYSQL_LIB})
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(testsuite)
endif ()

View File

@ -21,6 +21,6 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoFoundation PocoData ${ODBC_LIBRARIES})
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(testsuite)
endif ()

View File

@ -114,7 +114,7 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" ${SYSLIBS})
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory( samples )
add_subdirectory( testsuite )
endif ()

View File

@ -19,7 +19,7 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoFoundation)
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(samples)
add_subdirectory(testsuite)
endif ()

View File

@ -19,7 +19,7 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoNet PocoFoundation)
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(samples)
add_subdirectory(testsuite)
endif ()

View File

@ -30,7 +30,7 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoFoundation ${SYSLIBS})
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(samples)
add_subdirectory(testsuite)
endif ()

View File

@ -19,7 +19,7 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoCrypto PocoNet PocoUtil PocoFoundation ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY} )
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(samples)
add_subdirectory(testsuite)
endif ()

View File

@ -24,18 +24,18 @@ set_target_properties( "${LIBNAME}"
)
target_link_libraries( "${LIBNAME}" PocoFoundation)
if (ENABLE_XML)
if (POCO_ENABLE_XML)
target_link_libraries( "${LIBNAME}" PocoXML)
else ()
add_definitions( -DPOCO_UTIL_NO_XMLCONFIGURATION )
endif()
if (ENABLE_JSON)
if (POCO_ENABLE_JSON)
target_link_libraries( "${LIBNAME}" PocoJSON)
else ()
add_definitions( -DPOCO_UTIL_NO_JSONCONFIGURATION )
endif()
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(samples)
add_subdirectory(testsuite)
endif ()

View File

@ -42,7 +42,7 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" ${SYSLIBS} PocoFoundation)
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(samples)
add_subdirectory(testsuite)
endif ()

View File

@ -19,7 +19,7 @@ set_target_properties( "${LIBNAME}"
target_link_libraries( "${LIBNAME}" PocoUtil PocoXML PocoFoundation)
if (ENABLE_TESTS)
if (POCO_ENABLE_TESTS)
add_subdirectory(samples)
add_subdirectory(testsuite)
endif ()

View File

@ -1,59 +1,56 @@
SET(re2_headers
./re2/tostring.cc
./re2/dfa.cc
./re2/prefilter.cc
./re2/compile.cc
./re2/regexp.cc
./re2/onepass.cc
./re2/prefilter_tree.cc
./re2/set.cc
./re2/filtered_re2.cc
./re2/perl_groups.cc
./re2/parse.cc
./re2/nfa.cc
./re2/bitstate.cc
./re2/simplify.cc
./re2/unicode_groups.cc
./re2/mimics_pcre.cc
./re2/re2.cc
./re2/prog.cc
./re2/unicode_casefold.cc
./util/test.cc
./util/strutil.cc
./util/stringpiece.cc
./util/hash.cc
./util/arena.cc
./util/benchmark.cc
./util/valgrind.cc
./util/pcre.cc
./util/stringprintf.cc
./util/rune.cc
./util/random.cc
./util/thread.cc
set (re2_headers
./re2/tostring.cc
./re2/dfa.cc
./re2/prefilter.cc
./re2/compile.cc
./re2/regexp.cc
./re2/onepass.cc
./re2/prefilter_tree.cc
./re2/set.cc
./re2/filtered_re2.cc
./re2/perl_groups.cc
./re2/parse.cc
./re2/nfa.cc
./re2/bitstate.cc
./re2/simplify.cc
./re2/unicode_groups.cc
./re2/mimics_pcre.cc
./re2/re2.cc
./re2/prog.cc
./re2/unicode_casefold.cc
./util/test.cc
./util/strutil.cc
./util/stringpiece.cc
./util/hash.cc
./util/arena.cc
./util/benchmark.cc
./util/valgrind.cc
./util/pcre.cc
./util/stringprintf.cc
./util/rune.cc
./util/random.cc
./util/thread.cc
)
# Смысл в том, чтобы собрать две версии библиотеки - потокобезопасную (re2) и непотокобезопасную (re2_st).
# Библиотека re2, при выполнении регекспа, изменяет некоторое состояние - создаёт временные DFA.
# Для того, чтобы один объект-регексп можно было использовать одновременно из разных потоков, она использует RWLock.
# При этом, даже если использовать в разных потоках разные объекты re2 (созданные из одинакового регекспа),
# то, не смотря на отсутствие блокировок, RWLock "вхолостую" всё-равно очень существенно тормозит.
# Решение: собрать непотокобезопасную версию библиотеки и использовать разные объекты re2 в разных потоках.
add_definitions( -DNDEBUG )
# Building re2 which is thread-safe and re2_st which is not.
# re2 changes its state during matching of regular expression, e.g. creates temporary DFA.
# It uses RWLock to process the same regular expression object from different threads.
# In order to avoid redundant locks in some cases, we use not thread-safe version of the library (re2_st).
add_definitions (-DNDEBUG)
add_library (re2 ${re2_headers})
add_library (re2_st ${re2_headers})
set_target_properties(re2_st PROPERTIES COMPILE_DEFINITIONS "NO_THREADS;re2=re2_st")
set_target_properties (re2_st PROPERTIES COMPILE_DEFINITIONS "NO_THREADS;re2=re2_st")
message ("Creating headers for re2_st library.")
file (MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/re2_st)
foreach (FILENAME filtered_re2.h re2.h set.h stringpiece.h variadic_function.h)
file (READ ${CMAKE_CURRENT_SOURCE_DIR}/re2/${FILENAME} CONTENT)
string(REGEX REPLACE "using re2::RE2;" "" CONTENT "${CONTENT}")
string(REGEX REPLACE "namespace re2" "namespace re2_st" CONTENT "${CONTENT}")
string(REGEX REPLACE "re2::" "re2_st::" CONTENT "${CONTENT}")
string(REGEX REPLACE "\"re2/" "\"re2_st/" CONTENT "${CONTENT}")
string(REGEX REPLACE "(.\\*?_H)" "\\1_ST" CONTENT "${CONTENT}")
string (REGEX REPLACE "using re2::RE2;" "" CONTENT "${CONTENT}")
string (REGEX REPLACE "namespace re2" "namespace re2_st" CONTENT "${CONTENT}")
string (REGEX REPLACE "re2::" "re2_st::" CONTENT "${CONTENT}")
string (REGEX REPLACE "\"re2/" "\"re2_st/" CONTENT "${CONTENT}")
string (REGEX REPLACE "(.\\*?_H)" "\\1_ST" CONTENT "${CONTENT}")
file (WRITE ${CMAKE_CURRENT_BINARY_DIR}/re2_st/${FILENAME} "${CONTENT}")
endforeach ()

View File

@ -10,6 +10,8 @@ add_definitions(
include_directories (include src)
message(STATUS "Building: tcmalloc_minimal_internal")
add_library (tcmalloc_minimal_internal
./src/malloc_hook.cc
./src/base/spinlock_internal.cc

View File

@ -23,7 +23,7 @@ PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:$PATH"
# Опция -mcx16 для того, чтобы выбиралось больше заголовочных файлов (с запасом).
for src_file in $(clang -M -xc++ -std=gnu++1y -Wall -Werror -msse4 -mcx16 -mpopcnt -O3 -g -fPIC \
$(cat "$SOURCE_PATH/CMakeLists.txt" | grep include_directories | grep -v ClickHouse_BINARY_DIR | sed -e "s!\${ClickHouse_SOURCE_DIR}!$SOURCE_PATH!; s!include_directories (!-I !; s!)!!;" | tr '\n' ' ') \
$(cat "$SOURCE_PATH/build/include_directories.txt") \
"$SOURCE_PATH/dbms/include/DB/Interpreters/SpecializedAggregator.h" |
tr -d '\\' |
grep -v '.o:' |

View File

@ -1,49 +1,32 @@
include_directories(include)
include_directories(/usr/include/mysql)
include_directories (include)
include_directories (/usr/include/mysql)
add_subdirectory (src)
option(ENABLE_MONGODB "Set to TRUE to enable MongoDB support as source for external dictionaries" True)
set (DISABLE_MONGODB FALSE CACHE BOOL "Set to TRUE to disable MongoDB support as source for external dictionaries")
if (ENABLE_MONGODB)
set (DISABLE_MONGODB FALSE)
else()
set (DISABLE_MONGODB TRUE)
endif()
set (LINK_MONGOCLIENT libmongoclient.a ${OPENSSL_LIBRARIES} ${Boost_THREAD_LIBRARY})
add_definitions(-D ENABLE_MONGODB)
endif ()
if ($ENV{DISABLE_MONGODB})
set (DISABLE_MONGODB TRUE)
endif()
if (DISABLE_MONGODB)
add_definitions(-D DISABLE_MONGODB)
else()
set (LINK_MONGOCLIENT libmongoclient.a ${OPENSSL_LIBS} ${BOOST_THREAD_LIB})
endif()
if ($ENV{DISABLE_LIBTCMALLOC})
if (NOT ENABLE_LIBTCMALLOC)
add_definitions(-D NO_TCMALLOC)
endif()
endif ()
if (APPLE)
set (AIO_CPP_FILES "")
set (AIO_H_FILES "")
set (APPLE_ICONV_LIB iconv)
else()
set (AIO_H_FILES include/DB/Common/AIO.h
include/DB/IO/WriteBufferAIO.h
include/DB/IO/ReadBufferAIO.h
)
include/DB/IO/ReadBufferAIO.h)
set (AIO_CPP_FILES
src/IO/ReadBufferAIO.cpp
src/IO/WriteBufferAIO.cpp
)
src/IO/WriteBufferAIO.cpp)
set (APPLE_ICONV_LIB "")
endif()
add_library(string_utils
add_library (string_utils
include/DB/Common/StringUtils.h
src/Common/StringUtils.cpp)
@ -298,6 +281,7 @@ add_library (dbms
include/DB/DataStreams/SquashingTransform.h
include/DB/DataStreams/SquashingBlockInputStream.h
include/DB/DataStreams/SquashingBlockOutputStream.h
include/DB/DataStreams/ColumnGathererStream.h
include/DB/DataTypes/IDataType.h
include/DB/DataTypes/IDataTypeDummy.h
include/DB/DataTypes/DataTypeSet.h
@ -813,6 +797,7 @@ add_library (dbms
src/DataStreams/SquashingTransform.cpp
src/DataStreams/SquashingBlockInputStream.cpp
src/DataStreams/SquashingBlockOutputStream.cpp
src/DataStreams/ColumnGathererStream.cpp
src/DataTypes/DataTypeString.cpp
src/DataTypes/DataTypeFixedString.cpp
@ -983,9 +968,8 @@ add_library (dbms
)
if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
# Не генерируем отладочную информацию для файлов с большим количеством инстанцирований шаблонов
# - для более быстрой линковки и меньшего размера бинарника.
SET_SOURCE_FILES_PROPERTIES(
# Won't generate debug info for files with heavy template instantiation to achieve faster linking and lower size.
set_source_files_properties(
src/Functions/FunctionsArithmetic.cpp
src/Functions/FunctionsArray.cpp
src/Functions/FunctionsCoding.cpp
@ -1017,11 +1001,11 @@ if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug")
src/Dictionaries/ComplexKeyHashedDictionary.cpp
src/Dictionaries/ComplexKeyCacheDictionary.cpp
PROPERTIES COMPILE_FLAGS -g0)
endif()
endif ()
IF (NOT AARCH64)
SET(LINK_LIBRARIES_ONLY_ON_X86_64 cpuid)
ENDIF()
if (NOT AARCH64)
set (LINK_LIBRARIES_ONLY_ON_X86_64 cpuid)
endif ()
target_link_libraries(dbms
common
@ -1033,10 +1017,10 @@ target_link_libraries(dbms
double-conversion
${LINK_LIBRARIES_ONLY_ON_X86_64}
re2 re2_st
libcrypto.a
${BOOST_SYSTEM_LIB}
${OPENSSL_CRYPTO_LIBRARY}
${Boost_SYSTEM_LIBRARY}
${LINK_MONGOCLIENT}
${BOOST_REGEX_LIB}
${Boost_REGEX_LIBRARY}
PocoData
PocoDataODBC
${APPLE_ICONV_LIB}

View File

@ -35,9 +35,9 @@ struct SortColumnDescription
using SortDescription = std::vector<SortColumnDescription>;
/** Курсор, позволяющий сравнивать соответствующие строки в разных блоках.
* Курсор двигается по одному блоку.
* Для использования в priority queue.
/** Cursor allows to compare rows in different blocks (and parts).
* Cursor moves inside single block.
* It is used in priority queue.
*/
struct SortCursorImpl
{
@ -48,14 +48,17 @@ struct SortCursorImpl
size_t pos = 0;
size_t rows = 0;
/** Порядок (что сравнивается), если сравниваемые столбцы равны.
* Даёт возможность предпочитать строки из нужного курсора.
/** Determines order if comparing columns are equal.
* Order is determined by number of cursor.
*
* Cursor number (always?) equals to number of merging part.
* Therefore this field can be used to determine part number of current row (see ColumnGathererStream).
*/
size_t order;
using NeedCollationFlags = std::vector<UInt8>;
/** Нужно ли использовать Collator для сортировки столбца */
/** Should we use Collator to sort a column? */
NeedCollationFlags need_collation;
/** Есть ли хотя бы один столбец с Collator. */

View File

@ -24,8 +24,8 @@ class CollapsingSortedBlockInputStream : public MergingSortedBlockInputStream
{
public:
CollapsingSortedBlockInputStream(BlockInputStreams inputs_, const SortDescription & description_,
const String & sign_column_, size_t max_block_size_)
: MergingSortedBlockInputStream(inputs_, description_, max_block_size_),
const String & sign_column_, size_t max_block_size_, MergedRowSources * out_row_sources_ = nullptr)
: MergingSortedBlockInputStream(inputs_, description_, max_block_size_, 0, out_row_sources_),
sign_column(sign_column_)
{
}
@ -77,6 +77,12 @@ private:
size_t blocks_written = 0;
/// Fields specific for VERTICAL merge algorithm
size_t current_pos = 0; /// Global row number of current key
size_t first_negative_pos = 0; /// Global row number of first_negative
size_t last_positive_pos = 0; /// Global row number of last_positive
size_t last_negative_pos = 0; /// Global row number of last_negative
/** Делаем поддержку двух разных курсоров - с Collation и без.
* Шаблоны используем вместо полиморфных SortCursor'ов и вызовов виртуальных функций.
*/

View File

@ -0,0 +1,104 @@
#pragma once
#include <DB/DataStreams/IProfilingBlockInputStream.h>
#include <DB/Storages/IStorage.h>
#include <DB/Common/PODArray.h>
namespace DB
{
/// Tiny struct, stores number of a Part from which current row was fetched, and insertion flag.
struct RowSourcePart
{
RowSourcePart() = default;
RowSourcePart(size_t source_num, bool flag = false)
{
static_assert(sizeof(*this) == 1, "Size of RowSourcePart is too big due to compiler settings");
setSourceNum(source_num);
setSkipFlag(flag);
}
/// is equal to getSourceNum() if flag is false
size_t getData() const { return data; }
size_t getSourceNum()const { return data & MASK_NUMBER; }
/// In CollapsingMergeTree case flag means "skip this rows"
bool getSkipFlag() const { return (data & MASK_FLAG) != 0; }
void setSourceNum(size_t source_num)
{
data = (data & MASK_FLAG) | (static_cast<UInt8>(source_num) & MASK_NUMBER);
}
void setSkipFlag(bool flag)
{
data = flag ? data | MASK_FLAG : data & ~MASK_FLAG;
}
static constexpr size_t MAX_PARTS = 0x7F;
static constexpr UInt8 MASK_NUMBER = 0x7F;
static constexpr UInt8 MASK_FLAG = 0x80;
private:
UInt8 data;
};
using MergedRowSources = PODArray<RowSourcePart>;
/** Gather single stream from multiple streams according to streams mask.
* Stream mask maps row number to index of source stream.
* Streams should conatin exactly one column.
*/
class ColumnGathererStream : public IProfilingBlockInputStream
{
public:
ColumnGathererStream(const BlockInputStreams & source_streams, const String & column_name_,
const MergedRowSources & pos_to_source_idx_, size_t block_size_ = DEFAULT_BLOCK_SIZE);
String getName() const override { return "ColumnGatherer"; }
String getID() const override;
Block readImpl() override;
private:
String name;
ColumnWithTypeAndName column;
const MergedRowSources & pos_to_source_idx;
/// Cache required fileds
struct Source
{
const IColumn * column;
size_t pos;
size_t size;
Block block;
Source(Block && block_, const String & name) : block(std::move(block_))
{
update(name);
}
void update(const String & name)
{
column = block.getByName(name).column.get();
size = block.rows();
pos = 0;
}
};
std::vector<Source> sources;
size_t pos_global = 0;
size_t block_size;
Logger * log = &Logger::get("ColumnGathererStream");
};
}

View File

@ -9,6 +9,7 @@
#include <DB/Core/SortDescription.h>
#include <DB/DataStreams/IProfilingBlockInputStream.h>
#include <DB/DataStreams/ColumnGathererStream.h>
namespace DB
@ -56,10 +57,12 @@ inline void intrusive_ptr_release(detail::SharedBlock * ptr)
class MergingSortedBlockInputStream : public IProfilingBlockInputStream
{
public:
/// limit - если не 0, то можно выдать только первые limit строк в сортированном порядке.
MergingSortedBlockInputStream(BlockInputStreams inputs_, const SortDescription & description_, size_t max_block_size_, size_t limit_ = 0)
/// limit - if isn't 0, then we can produce only first limit rows in sorted order.
/// out_row_sources - if isn't nullptr, then at the end of execution it should contain part numbers of each readed row (and needed flag)
MergingSortedBlockInputStream(BlockInputStreams & inputs_, const SortDescription & description_,
size_t max_block_size_, size_t limit_ = 0, MergedRowSources * out_row_sources_ = nullptr)
: description(description_), max_block_size(max_block_size_), limit(limit_),
source_blocks(inputs_.size()), cursors(inputs_.size())
source_blocks(inputs_.size()), cursors(inputs_.size()), out_row_sources(out_row_sources_)
{
children.insert(children.end(), inputs_.begin(), inputs_.end());
}
@ -158,6 +161,10 @@ protected:
using QueueWithCollation = std::priority_queue<SortCursorWithCollation>;
QueueWithCollation queue_with_collation;
/// Used in Vertical merge algorithm to gather non-PK columns (on next step)
/// If it is not nullptr then it should be populated during execution
MergedRowSources * out_row_sources = nullptr;
/// Эти методы используются в Collapsing/Summing/Aggregating... SortedBlockInputStream-ах.

View File

@ -8,7 +8,7 @@
#include <DB/Dictionaries/ExecutableDictionarySource.h>
#include <DB/Dictionaries/HTTPDictionarySource.h>
#ifndef DISABLE_MONGODB
#ifdef ENABLE_MONGODB
#include <DB/Dictionaries/MongoDBDictionarySource.h>
#endif
@ -118,7 +118,7 @@ public:
}
else if ("mongodb" == source_type)
{
#ifndef DISABLE_MONGODB
#ifdef ENABLE_MONGODB
return std::make_unique<MongoDBDictionarySource>(dict_struct, config, config_prefix + ".mongodb", sample_block);
#else
throw Exception{

File diff suppressed because it is too large Load Diff

View File

@ -13,7 +13,6 @@
#include <DB/Columns/ColumnArray.h>
#include <DB/Columns/ColumnFixedString.h>
#include <DB/Columns/ColumnConst.h>
#include <DB/Functions/FunctionsArray.h>
#include <DB/Functions/IFunction.h>
#include <ext/range.hpp>
@ -932,61 +931,13 @@ public:
static FunctionPtr create(const Context & context) { return std::make_shared<FunctionReverse>(); }
/// Получить имя функции.
String getName() const override
{
return name;
}
String getName() const override;
/// Получить тип результата по типам аргументов. Если функция неприменима для данных аргументов - кинуть исключение.
DataTypePtr getReturnType(const DataTypes & arguments) const override
{
if (arguments.size() != 1)
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
+ toString(arguments.size()) + ", should be 1.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
if (!typeid_cast<const DataTypeString *>(&*arguments[0]) && !typeid_cast<const DataTypeFixedString *>(&*arguments[0])
&& !typeid_cast<const DataTypeArray *>(&*arguments[0]))
throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return arguments[0]->clone();
}
DataTypePtr getReturnType(const DataTypes & arguments) const override;
/// Выполнить функцию над блоком.
void execute(Block & block, const ColumnNumbers & arguments, size_t result) override
{
const ColumnPtr column = block.getByPosition(arguments[0]).column;
if (const ColumnString * col = typeid_cast<const ColumnString *>(column.get()))
{
std::shared_ptr<ColumnString> col_res = std::make_shared<ColumnString>();
block.getByPosition(result).column = col_res;
ReverseImpl::vector(col->getChars(), col->getOffsets(),
col_res->getChars(), col_res->getOffsets());
}
else if (const ColumnFixedString * col = typeid_cast<const ColumnFixedString *>(column.get()))
{
auto col_res = std::make_shared<ColumnFixedString>(col->getN());
block.getByPosition(result).column = col_res;
ReverseImpl::vector_fixed(col->getChars(), col->getN(),
col_res->getChars());
}
else if (const ColumnConstString * col = typeid_cast<const ColumnConstString *>(column.get()))
{
String res;
ReverseImpl::constant(col->getData(), res);
auto col_res = std::make_shared<ColumnConstString>(col->size(), res);
block.getByPosition(result).column = col_res;
}
else if (typeid_cast<const ColumnArray *>(column.get()) || typeid_cast<const ColumnConstArray *>(column.get()))
{
FunctionArrayReverse().execute(block, arguments, result);
}
else
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN);
}
void execute(Block & block, const ColumnNumbers & arguments, size_t result) override;
};
@ -1566,108 +1517,12 @@ public:
static constexpr auto name = "appendTrailingCharIfAbsent";
static FunctionPtr create(const Context & context) { return std::make_shared<FunctionAppendTrailingCharIfAbsent>(); }
String getName() const override
{
return name;
}
String getName() const override;
private:
DataTypePtr getReturnType(const DataTypes & arguments) const override
{
if (arguments.size() != 2)
throw Exception{
"Number of arguments for function " + getName() + " doesn't match: passed "
+ toString(arguments.size()) + ", should be 2.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH
};
DataTypePtr getReturnType(const DataTypes & arguments) const override;
if (!typeid_cast<const DataTypeString *>(arguments[0].get()))
throw Exception{
"Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT
};
if (!typeid_cast<const DataTypeString *>(arguments[1].get()))
throw Exception{
"Illegal type " + arguments[1]->getName() + " of argument of function " + getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT
};
return std::make_shared<DataTypeString>();
}
void execute(Block & block, const ColumnNumbers & arguments, const size_t result) override
{
const auto & column = block.getByPosition(arguments[0]).column;
const auto & column_char = block.getByPosition(arguments[1]).column;
if (!typeid_cast<const ColumnConstString *>(column_char.get()))
throw Exception{
"Second argument of function " + getName() + " must be a constant string",
ErrorCodes::ILLEGAL_COLUMN
};
const auto & trailing_char_str = static_cast<const ColumnConstString &>(*column_char).getData();
if (trailing_char_str.size() != 1)
throw Exception{
"Second argument of function " + getName() + " must be a one-character string",
ErrorCodes::BAD_ARGUMENTS
};
if (const auto col = typeid_cast<const ColumnString *>(&*column))
{
auto col_res = std::make_shared<ColumnString>();
block.getByPosition(result).column = col_res;
const auto & src_data = col->getChars();
const auto & src_offsets = col->getOffsets();
auto & dst_data = col_res->getChars();
auto & dst_offsets = col_res->getOffsets();
const auto size = src_offsets.size();
dst_data.resize(src_data.size() + size);
dst_offsets.resize(size);
ColumnString::Offset_t src_offset{};
ColumnString::Offset_t dst_offset{};
for (const auto i : ext::range(0, size))
{
const auto src_length = src_offsets[i] - src_offset;
memcpySmallAllowReadWriteOverflow15(&dst_data[dst_offset], &src_data[src_offset], src_length);
src_offset = src_offsets[i];
dst_offset += src_length;
if (src_length > 1 && dst_data[dst_offset - 2] != trailing_char_str.front())
{
dst_data[dst_offset - 1] = trailing_char_str.front();
dst_data[dst_offset] = 0;
++dst_offset;
}
dst_offsets[i] = dst_offset;
}
dst_data.resize_assume_reserved(dst_offset);
}
else if (const auto col = typeid_cast<const ColumnConstString *>(&*column))
{
const auto & in_data = col->getData();
block.getByPosition(result).column = std::make_shared<ColumnConstString>(
col->size(),
in_data.size() == 0 ? in_data :
in_data.back() == trailing_char_str.front() ? in_data : in_data + trailing_char_str);
}
else
throw Exception{
"Illegal column " + block.getByPosition(arguments[0]).column->getName()
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN
};
}
void execute(Block & block, const ColumnNumbers & arguments, const size_t result) override;
};

View File

@ -1048,7 +1048,7 @@ struct ReplaceStringImpl
/// Определим, к какому индексу оно относится.
while (i < size && begin + n * (i + 1) <= match)
{
res_offsets[i] = res_offset + ((begin + n * (i + 1)) - pos);
res_offsets[i] = res_offset + ((begin + n * (i + 1)) - pos) + 1;
++i;
}
res_offset += (match - pos);
@ -1061,7 +1061,7 @@ struct ReplaceStringImpl
bool can_finish_current_string = false;
/// Проверяем, что вхождение не переходит через границы строк.
if (match + needle.size() < begin + n * (i + 1))
if (match + needle.size() - 1 < begin + n * (i + 1))
{
res_data.resize(res_data.size() + replacement.size());
memcpy(&res_data[res_offset], replacement.data(), replacement.size());
@ -1085,6 +1085,10 @@ struct ReplaceStringImpl
pos = begin + n * (i + 1);
}
}
if (i < size) {
res_offsets[i] = res_offset + ((begin + n * (i + 1)) - pos) + 1;
}
}
static void constant(const std::string & data, const std::string & needle, const std::string & replacement,

View File

@ -7,31 +7,14 @@ namespace DB
{
/** Select parts to merge based on its level.
*
* Select first range of parts at least min_parts_to_merge length with minimum level.
*
* If enough time has passed, lower min_parts_to_merge.
* And if no ranges of consecutive parts with same level, and much time has passed,
* allow to select parts of different level.
* This is done to allow further merging when table is not updated.
* Select first range of parts of parts_to_merge length with minimum level.
*/
class LevelMergeSelector : public IMergeSelector
{
public:
struct Settings
{
size_t min_parts_to_merge = 8;
size_t max_parts_to_merge = 100;
/** min_parts_to_merge will be lowered by 1 after that time.
* It will be lowered by 2 after that time * 2^1,
* It will be lowered by 3 after that time * 2^2,
* and so on, exponentially.
*/
time_t lower_base_after = 300;
/// If there are too much parts, merge any parts of same level, or if no such ranges, then totally ignore levels.
size_t fallback_after_num_parts = 50;
size_t parts_to_merge = 10;
};
LevelMergeSelector(const Settings & settings) : settings(settings) {}

View File

@ -32,10 +32,21 @@ struct MergeInfo
UInt64 total_size_bytes_compressed{};
UInt64 total_size_marks{};
std::atomic<UInt64> bytes_read_uncompressed{};
std::atomic<UInt64> rows_read{};
std::atomic<UInt64> bytes_written_uncompressed{};
/// Updated only for Horizontal algorithm
std::atomic<UInt64> rows_read{};
std::atomic<UInt64> rows_written{};
/// Updated only for Vertical algorithm
/// mutually exclusive with rows_read and rows_written, updated either rows_written either columns_written
std::atomic<UInt64> columns_written{};
/// Updated in both cases
/// Number of rows for which primary key columns have been written
std::atomic<UInt64> rows_with_key_columns_read{};
std::atomic<UInt64> rows_with_key_columns_written{};
MergeInfo(const std::string & database, const std::string & table, const std::string & result_part_name)
: database{database}, table{table}, result_part_name{result_part_name}
@ -52,9 +63,12 @@ struct MergeInfo
total_size_bytes_compressed(other.total_size_bytes_compressed),
total_size_marks(other.total_size_marks),
bytes_read_uncompressed(other.bytes_read_uncompressed.load(std::memory_order_relaxed)),
rows_read(other.rows_read.load(std::memory_order_relaxed)),
bytes_written_uncompressed(other.bytes_written_uncompressed.load(std::memory_order_relaxed)),
rows_written(other.rows_written.load(std::memory_order_relaxed))
rows_read(other.rows_read.load(std::memory_order_relaxed)),
rows_written(other.rows_written.load(std::memory_order_relaxed)),
columns_written(other.columns_written.load(std::memory_order_relaxed)),
rows_with_key_columns_read(other.rows_with_key_columns_read.load(std::memory_order_relaxed)),
rows_with_key_columns_written(other.rows_with_key_columns_written.load(std::memory_order_relaxed))
{
}
};

View File

@ -24,7 +24,7 @@ public:
const MarkRanges & mark_ranges_, bool use_uncompressed_cache_,
ExpressionActionsPtr prewhere_actions_, String prewhere_column_, bool check_columns,
size_t min_bytes_to_use_direct_io_, size_t max_read_buffer_size_,
bool save_marks_in_cache_);
bool save_marks_in_cache_, bool quiet = false);
~MergeTreeBlockInputStream() override;

View File

@ -55,11 +55,11 @@ namespace ErrorCodes
* Структура файлов:
* / min-date _ max-date _ min-id _ max-id _ level / - директория с куском.
* Внутри директории с куском:
* checksums.txt - список файлов с их размерами и контрольными суммами.
* columns.txt - список столбцов с их типами.
* checksums.txt - содержит список файлов с их размерами и контрольными суммами.
* columns.txt - содержит список столбцов с их типами.
* primary.idx - индексный файл.
* Column.bin - данные столбца
* Column.mrk - засечки, указывающие, откуда начинать чтение, чтобы пропустить n * k строк.
* [Column].bin - данные столбца
* [Column].mrk - засечки, указывающие, откуда начинать чтение, чтобы пропустить n * k строк.
*
* Имеется несколько режимов работы, определяющих, что делать при мердже:
* - Ordinary - ничего дополнительно не делать;

View File

@ -9,6 +9,7 @@ namespace DB
{
class MergeListEntry;
class MergeProgressCallback;
struct ReshardingJob;
@ -125,6 +126,19 @@ public:
bool isCancelled() const { return cancelled > 0; }
public:
enum class MergeAlgorithm
{
Horizontal, /// per-row merge of all columns
Vertical /// per-row merge of PK columns, per-column gather for non-PK columns
};
private:
MergeAlgorithm chooseMergeAlgorithm(const MergeTreeData & data, const MergeTreeData::DataPartsVector & parts,
size_t rows_upper_bound, MergedRowSources & rows_sources_to_alloc) const;
private:
MergeTreeData & data;
const BackgroundProcessingPool & pool;

View File

@ -46,6 +46,8 @@ struct MergeTreeDataPartChecksums
void addFile(const String & file_name, size_t file_size, uint128 file_hash);
void add(MergeTreeDataPartChecksums && rhs_checksums);
/// Проверяет, что множество столбцов и их контрольные суммы совпадают. Если нет - бросает исключение.
/// Если have_uncompressed, для сжатых файлов сравнивает чексуммы разжатых данных. Иначе сравнивает только чексуммы файлов.
void checkEqual(const MergeTreeDataPartChecksums & rhs, bool have_uncompressed) const;

View File

@ -62,6 +62,7 @@ private:
std::unique_ptr<CompressedReadBufferFromFile> non_cached_buffer;
std::string path_prefix;
size_t max_mark_range;
bool is_empty = false;
Stream(
const String & path_prefix_, UncompressedCache * uncompressed_cache,
@ -69,9 +70,14 @@ private:
const MarkRanges & all_mark_ranges, size_t aio_threshold, size_t max_read_buffer_size,
const ReadBufferFromFileBase::ProfileCallback & profile_callback, clockid_t clock_type);
static std::unique_ptr<Stream> createEmptyPtr();
void loadMarks(MarkCache * cache, bool save_in_cache);
void seekToMark(size_t index);
private:
Stream() = default;
};
using FileStreams = std::map<std::string, std::unique_ptr<Stream>>;

View File

@ -16,13 +16,18 @@ struct MergeTreeSettings
{
/** Merge settings. */
/// Maximum in total size of parts to merge, when there are maximum (minimum) free threads in background pool.
/// Maximum in total size of parts to merge, when there are maximum (minimum) free threads in background pool (or entries in replication queue).
size_t max_bytes_to_merge_at_max_space_in_pool = 100ULL * 1024 * 1024 * 1024;
size_t max_bytes_to_merge_at_min_space_in_pool = 1024 * 1024;
/// How many tasks of merging parts are allowed simultaneously in ReplicatedMergeTree queue.
size_t max_replicated_merges_in_queue = 16;
/// When there is less than specified number of free entries in pool (or replicated queue),
/// start to lower maximum size of merge to process (or to put in queue).
/// This is to allow small merges to process - not filling the pool with long running merges.
size_t number_of_free_entries_in_pool_to_lower_max_size_of_merge = 8;
/// How many seconds to keep obsolete parts.
time_t old_parts_lifetime = 8 * 60;
@ -90,6 +95,9 @@ struct MergeTreeSettings
/// Minimal absolute delay to close, stop serving requests and not return Ok during status check.
size_t min_absolute_delay_to_close = 0;
/// Enable usage of Vertical merge algorithm.
size_t enable_vertical_merge_algorithm = 0;
void loadFromConfig(const String & config_elem, Poco::Util::AbstractConfiguration & config)
{
@ -124,6 +132,7 @@ struct MergeTreeSettings
SET_SIZE_T(min_relative_delay_to_yield_leadership);
SET_SIZE_T(min_relative_delay_to_close);
SET_SIZE_T(min_absolute_delay_to_close);
SET_SIZE_T(enable_vertical_merge_algorithm);
#undef SET_SIZE_T
#undef SET_DOUBLE

View File

@ -11,6 +11,8 @@
#include <DB/DataTypes/DataTypeArray.h>
#include <DB/DataStreams/IBlockOutputStream.h>
#include <DB/Columns/ColumnArray.h>
namespace DB
{
@ -33,6 +35,7 @@ public:
{
}
protected:
using OffsetColumns = std::set<std::string>;
@ -94,7 +97,8 @@ protected:
using ColumnStreams = std::map<String, std::unique_ptr<ColumnStream>>;
void addStream(const String & path, const String & name, const IDataType & type, size_t estimated_size = 0, size_t level = 0, String filename = "")
void addStream(const String & path, const String & name, const IDataType & type, size_t estimated_size = 0,
size_t level = 0, String filename = "", bool skip_offsets = false)
{
String escaped_column_name;
if (filename.size())
@ -104,6 +108,8 @@ protected:
/// Для массивов используются отдельные потоки для размеров.
if (const DataTypeArray * type_arr = typeid_cast<const DataTypeArray *>(&type))
{
if (!skip_offsets)
{
String size_name = DataTypeNested::extractNestedTableName(name)
+ ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level);
@ -118,10 +124,12 @@ protected:
compression_method,
estimated_size,
aio_threshold);
}
addStream(path, name, *type_arr->getNestedType(), estimated_size, level + 1);
}
else
{
column_streams[name] = std::make_unique<ColumnStream>(
escaped_column_name,
path + escaped_column_name + ".bin",
@ -131,15 +139,18 @@ protected:
estimated_size,
aio_threshold);
}
}
/// Записать данные одного столбца.
void writeData(const String & name, const IDataType & type, const IColumn & column, OffsetColumns & offset_columns, size_t level = 0)
void writeData(const String & name, const IDataType & type, const IColumn & column, OffsetColumns & offset_columns,
size_t level = 0, bool skip_offsets = false)
{
size_t size = column.size();
/// Для массивов требуется сначала сериализовать размеры, а потом значения.
if (const DataTypeArray * type_arr = typeid_cast<const DataTypeArray *>(&type))
const DataTypeArray * type_arr = typeid_cast<const DataTypeArray *>(&type);
if (!skip_offsets && type_arr)
{
String size_name = DataTypeNested::extractNestedTableName(name)
+ ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level);
@ -306,11 +317,16 @@ public:
throw Exception("Method writeSuffix is not supported by MergedBlockOutputStream", ErrorCodes::NOT_IMPLEMENTED);
}
MergeTreeData::DataPart::Checksums writeSuffixAndGetChecksums()
MergeTreeData::DataPart::Checksums writeSuffixAndGetChecksums(
const NamesAndTypesList & total_column_list,
MergeTreeData::DataPart::Checksums * additional_column_checksums = nullptr)
{
/// Заканчиваем запись и достаем чексуммы.
MergeTreeData::DataPart::Checksums checksums;
if (additional_column_checksums)
checksums = std::move(*additional_column_checksums);
if (storage.merging_params.mode != MergeTreeData::MergingParams::Unsorted)
{
index_stream->next();
@ -319,10 +335,10 @@ public:
index_stream = nullptr;
}
for (ColumnStreams::iterator it = column_streams.begin(); it != column_streams.end(); ++it)
for (auto & column_stream : column_streams)
{
it->second->finalize();
it->second->addToChecksums(checksums);
column_stream.second->finalize();
column_stream.second->addToChecksums(checksums);
}
column_streams.clear();
@ -338,7 +354,7 @@ public:
{
/// Записываем файл с описанием столбцов.
WriteBufferFromFile out(part_path + "columns.txt", 4096);
columns_list.writeText(out);
total_column_list.writeText(out);
}
{
@ -350,6 +366,11 @@ public:
return checksums;
}
MergeTreeData::DataPart::Checksums writeSuffixAndGetChecksums()
{
return writeSuffixAndGetChecksums(columns_list, nullptr);
}
MergeTreeData::DataPart::Index & getIndex()
{
return index_columns;
@ -488,12 +509,13 @@ private:
class MergedColumnOnlyOutputStream : public IMergedBlockOutputStream
{
public:
MergedColumnOnlyOutputStream(MergeTreeData & storage_, String part_path_, bool sync_, CompressionMethod compression_method)
MergedColumnOnlyOutputStream(MergeTreeData & storage_, String part_path_, bool sync_, CompressionMethod compression_method,
bool skip_offsets_ = false)
: IMergedBlockOutputStream(
storage_, storage_.context.getSettings().min_compress_block_size,
storage_.context.getSettings().max_compress_block_size, compression_method,
storage_.context.getSettings().min_bytes_to_use_direct_io),
part_path(part_path_), sync(sync_)
part_path(part_path_), sync(sync_), skip_offsets(skip_offsets_)
{
}
@ -505,7 +527,7 @@ public:
for (size_t i = 0; i < block.columns(); ++i)
{
addStream(part_path, block.getByPosition(i).name,
*block.getByPosition(i).type, 0, 0, block.getByPosition(i).name);
*block.getByPosition(i).type, 0, 0, block.getByPosition(i).name, skip_offsets);
}
initialized = true;
}
@ -516,7 +538,7 @@ public:
for (size_t i = 0; i < block.columns(); ++i)
{
const ColumnWithTypeAndName & column = block.getByPosition(i);
writeData(column.name, *column.type, *column.column, offset_columns);
writeData(column.name, *column.type, *column.column, offset_columns, 0, skip_offsets);
}
size_t written_for_last_mark = (storage.index_granularity - index_offset + rows) % storage.index_granularity;
@ -537,8 +559,7 @@ public:
column_stream.second->finalize();
if (sync)
column_stream.second->sync();
std::string column = escapeForFileName(column_stream.first);
column_stream.second->addToChecksums(checksums, column);
column_stream.second->addToChecksums(checksums);
}
column_streams.clear();
@ -552,6 +573,7 @@ private:
bool initialized = false;
bool sync;
bool skip_offsets;
};
}

View File

@ -1,30 +1,45 @@
find_library(READLINE_LIB
NAMES libreadline.a libreadline.so
HINTS "/usr/local/opt/readline/lib")
set (READLINE_HINTS "/usr/local/opt/readline/lib")
if (USE_STATIC_LIBRARIES)
find_library (READLINE_LIB NAMES libreadline.a HINTS ${READLINE_HINTS})
else ()
find_library (READLINE_LIB NAMES readline HINTS ${READLINE_HINTS})
endif ()
if (USE_STATIC_LIBRARIES)
find_library (EDIT_LIB NAMES libedit.a)
else ()
find_library (EDIT_LIB NAMES edit)
endif ()
if (USE_STATIC_LIBRARIES)
find_library (CURSES_LIB NAMES libcurses.a)
else ()
find_library (CURSES_LIB NAMES curses)
endif ()
if (USE_STATIC_LIBRARIES)
find_library (TERMCAP_LIB NAMES libtermcap.a termcap)
else ()
find_library (TERMCAP_LIB NAMES termcap)
endif ()
find_library(LIBEDIT_LIB
NAMES libedit.a libedit.so)
if (READLINE_LIB)
include_directories ("/usr/local/opt/readline/include")
add_definitions (-D USE_READLINE)
set (LINE_EDITING_LIBS ${READLINE_LIB} ${TERMCAP_LIB})
message (STATUS "Using line editing libraries: ${LINE_EDITING_LIBS}")
elseif (EDIT_LIB)
add_definitions (-D USE_LIBEDIT)
set (LINE_EDITING_LIBS ${EDIT_LIB} ${CURSES_LIB} ${TERMCAP_LIB})
message (STATUS "Using line editing libraries: ${LINE_EDITING_LIBS}")
else ()
message (STATUS "Not using any library for line editing.")
endif ()
if(READLINE_LIB)
include_directories("/usr/local/opt/readline/include")
add_definitions(-D USE_READLINE)
set(LINE_EDITING_LIBS ${READLINE_LIB} libtermcap.a)
message(STATUS "Using line editing libraries: ${LINE_EDITING_LIBS}")
elseif(LIBEDIT_LIB)
add_definitions(-D USE_LIBEDIT)
set(LINE_EDITING_LIBS ${LIBEDIT_LIB} libcurses.a libtermcap.a)
message(STATUS "Using line editing libraries: ${LINE_EDITING_LIBS}")
else()
message(STATUS "Not using any library for line editing.")
endif()
add_library (clickhouse-client Client.cpp)
target_link_libraries (clickhouse-client dbms ${LINE_EDITING_LIBS} ${Boost_PROGRAM_OPTIONS_LIBRARY})
install (FILES config.xml DESTINATION /etc/clickhouse-client COMPONENT clickhouse-client)
add_library(clickhouse-client Client.cpp)
target_link_libraries (clickhouse-client dbms ${LINE_EDITING_LIBS} ${BOOST_PROGRAM_OPTIONS_LIB})
INSTALL(FILES config.xml DESTINATION /etc/clickhouse-client COMPONENT clickhouse-client)
add_library (clickhouse-benchmark Benchmark.cpp)
target_link_libraries (clickhouse-benchmark dbms ${Boost_PROGRAM_OPTIONS_LIBRARY})
add_library(clickhouse-benchmark Benchmark.cpp)
target_link_libraries (clickhouse-benchmark dbms ${BOOST_PROGRAM_OPTIONS_LIB})
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -32,7 +32,7 @@ add_executable (simple_cache simple_cache.cpp)
target_link_libraries (simple_cache common)
add_executable (compact_array compact_array.cpp)
target_link_libraries (compact_array dbms ${BOOST_FILESYSTEM_LIB})
target_link_libraries (compact_array dbms ${Boost_FILESYSTEM_LIBRARY})
add_executable (radix_sort radix_sort.cpp)
target_link_libraries (radix_sort dbms)

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -55,6 +55,13 @@ void CollapsingSortedBlockInputStream::insertRows(ColumnPlainPtrs & merged_colum
++merged_rows;
for (size_t i = 0; i < num_columns; ++i)
merged_columns[i]->insertFrom(*last_negative.columns[i], last_negative.row_num);
if (out_row_sources)
{
/// true flag value means "skip row"
out_row_sources->data()[last_positive_pos].setSkipFlag(false);
out_row_sources->data()[last_negative_pos].setSkipFlag(false);
}
}
return;
}
@ -64,6 +71,9 @@ void CollapsingSortedBlockInputStream::insertRows(ColumnPlainPtrs & merged_colum
++merged_rows;
for (size_t i = 0; i < num_columns; ++i)
merged_columns[i]->insertFrom(*first_negative.columns[i], first_negative.row_num);
if (out_row_sources)
out_row_sources->data()[first_negative_pos].setSkipFlag(false);
}
if (count_positive >= count_negative)
@ -71,6 +81,9 @@ void CollapsingSortedBlockInputStream::insertRows(ColumnPlainPtrs & merged_colum
++merged_rows;
for (size_t i = 0; i < num_columns; ++i)
merged_columns[i]->insertFrom(*last_positive.columns[i], last_positive.row_num);
if (out_row_sources)
out_row_sources->data()[last_positive_pos].setSkipFlag(false);
}
if (!(count_positive == count_negative || count_positive + 1 == count_negative || count_positive == count_negative + 1))
@ -123,7 +136,7 @@ void CollapsingSortedBlockInputStream::merge(ColumnPlainPtrs & merged_columns, s
size_t merged_rows = 0;
/// Вынимаем строки в нужном порядке и кладём в merged_block, пока строк не больше max_block_size
while (!queue.empty())
for (; !queue.empty(); ++current_pos)
{
TSortCursor current = queue.top();
@ -149,6 +162,10 @@ void CollapsingSortedBlockInputStream::merge(ColumnPlainPtrs & merged_columns, s
queue.pop();
/// Initially, skip all rows. On insert, unskip "corner" rows.
if (out_row_sources)
out_row_sources->emplace_back(current.impl->order, true);
if (key_differs)
{
/// Запишем данные для предыдущего первичного ключа.
@ -166,13 +183,21 @@ void CollapsingSortedBlockInputStream::merge(ColumnPlainPtrs & merged_columns, s
last_is_positive = true;
setRowRef(last_positive, current);
last_positive_pos = current_pos;
}
else if (sign == -1)
{
if (!count_negative)
{
setRowRef(first_negative, current);
first_negative_pos = current_pos;
}
if (!blocks_written && !merged_rows)
{
setRowRef(last_negative, current);
last_negative_pos = current_pos;
}
++count_negative;
last_is_positive = false;

View File

@ -0,0 +1,111 @@
#include <DB/DataStreams/ColumnGathererStream.h>
namespace DB
{
namespace ErrorCodes
{
extern const int LOGICAL_ERROR;
extern const int INCOMPATIBLE_COLUMNS;
extern const int INCORRECT_NUMBER_OF_COLUMNS;
extern const int EMPTY_DATA_PASSED;
extern const int RECEIVED_EMPTY_DATA;
}
ColumnGathererStream::ColumnGathererStream(const BlockInputStreams & source_streams, const String & column_name_,
const MergedRowSources & pos_to_source_idx_, size_t block_size_)
: name(column_name_), pos_to_source_idx(pos_to_source_idx_), block_size(block_size_)
{
if (source_streams.empty())
throw Exception("There are no streams to gather", ErrorCodes::EMPTY_DATA_PASSED);
children.assign(source_streams.begin(), source_streams.end());
sources.reserve(children.size());
for (size_t i = 0; i < children.size(); i++)
{
sources.emplace_back(children[i]->read(), name);
Block & block = sources.back().block;
/// Sometimes MergeTreeReader injects additional column with partitioning key
if (block.columns() > 2 || !block.has(name))
throw Exception("Block should have 1 or 2 columns and contain column with requested name", ErrorCodes::INCORRECT_NUMBER_OF_COLUMNS);
if (i == 0)
{
column.name = name;
column.type = block.getByName(name).type->clone();
column.column = column.type->createColumn();
}
if (block.getByName(name).column->getName() != column.column->getName())
throw Exception("Column types don't match", ErrorCodes::INCOMPATIBLE_COLUMNS);
}
}
String ColumnGathererStream::getID() const
{
std::stringstream res;
res << getName() << "(";
for (size_t i = 0; i < children.size(); i++)
res << (i == 0 ? "" : ", " ) << children[i]->getID();
res << ")";
return res.str();
}
Block ColumnGathererStream::readImpl()
{
if (children.size() == 1)
return children[0]->read();
if (pos_global >= pos_to_source_idx.size())
return Block();
Block block_res{column.cloneEmpty()};
IColumn & column_res = *block_res.unsafeGetByPosition(0).column;
size_t pos_finish = std::min(pos_global + block_size, pos_to_source_idx.size());
column_res.reserve(pos_finish - pos_global);
for (size_t pos = pos_global; pos < pos_finish; ++pos)
{
auto source_id = pos_to_source_idx[pos].getSourceNum();
bool skip = pos_to_source_idx[pos].getSkipFlag();
Source & source = sources[source_id];
if (source.pos >= source.size) /// Fetch new block
{
try
{
source.block = children[source_id]->read();
source.update(name);
}
catch (Exception & e)
{
e.addMessage("Cannot fetch required block. Stream " + children[source_id]->getID() + ", part " + toString(source_id));
throw;
}
if (0 == source.size)
{
throw Exception("Fetched block is empty. Stream " + children[source_id]->getID() + ", part " + toString(source_id),
ErrorCodes::RECEIVED_EMPTY_DATA);
}
}
if (!skip)
column_res.insertFrom(*source.column, source.pos); //TODO: vectorize
++source.pos;
}
pos_global = pos_finish;
return block_res;
}
}

View File

@ -195,13 +195,10 @@ void MergingSortedBlockInputStream::merge(Block & merged_block, ColumnPlainPtrs
return;
}
size_t source_num = 0;
size_t size = cursors.size();
for (; source_num < size; ++source_num)
if (&cursors[source_num] == current.impl)
break;
/// Actually, current.impl->order stores source number (i.e. cursors[current.impl->order] == current.impl)
size_t source_num = current.impl->order;
if (source_num == size)
if (source_num >= cursors.size())
throw Exception("Logical error in MergingSortedBlockInputStream", ErrorCodes::LOGICAL_ERROR);
for (size_t i = 0; i < num_columns; ++i)
@ -224,6 +221,9 @@ void MergingSortedBlockInputStream::merge(Block & merged_block, ColumnPlainPtrs
finished = true;
}
if (out_row_sources)
out_row_sources->resize_fill(out_row_sources->size() + merged_rows, RowSourcePart(source_num));
// std::cerr << "fetching next block\n";
total_merged_rows += merged_rows;
@ -236,6 +236,12 @@ void MergingSortedBlockInputStream::merge(Block & merged_block, ColumnPlainPtrs
for (size_t i = 0; i < num_columns; ++i)
merged_columns[i]->insertFrom(*current->all_columns[i], current->pos);
if (out_row_sources)
{
/// Actually, current.impl->order stores source number (i.e. cursors[current.impl->order] == current.impl)
out_row_sources->emplace_back(current.impl->order);
}
if (!current->isLast())
{
// std::cerr << "moving to next row\n";

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,173 @@
#include <DB/Functions/FunctionsArray.h>
#include <DB/Functions/FunctionFactory.h>
#include <DB/Functions/FunctionsString.h>
namespace DB
{
String FunctionReverse::getName() const
{
return name;
}
DataTypePtr FunctionReverse::getReturnType(const DataTypes & arguments) const
{
if (arguments.size() != 1)
throw Exception("Number of arguments for function " + getName() + " doesn't match: passed "
+ toString(arguments.size()) + ", should be 1.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH);
if (!typeid_cast<const DataTypeString *>(&*arguments[0]) && !typeid_cast<const DataTypeFixedString *>(&*arguments[0])
&& !typeid_cast<const DataTypeArray *>(&*arguments[0]))
throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
return arguments[0]->clone();
}
void FunctionReverse::execute(Block & block, const ColumnNumbers & arguments, size_t result)
{
const ColumnPtr column = block.getByPosition(arguments[0]).column;
if (const ColumnString * col = typeid_cast<const ColumnString *>(column.get()))
{
std::shared_ptr<ColumnString> col_res = std::make_shared<ColumnString>();
block.getByPosition(result).column = col_res;
ReverseImpl::vector(col->getChars(), col->getOffsets(),
col_res->getChars(), col_res->getOffsets());
}
else if (const ColumnFixedString * col = typeid_cast<const ColumnFixedString *>(column.get()))
{
auto col_res = std::make_shared<ColumnFixedString>(col->getN());
block.getByPosition(result).column = col_res;
ReverseImpl::vector_fixed(col->getChars(), col->getN(),
col_res->getChars());
}
else if (const ColumnConstString * col = typeid_cast<const ColumnConstString *>(column.get()))
{
String res;
ReverseImpl::constant(col->getData(), res);
auto col_res = std::make_shared<ColumnConstString>(col->size(), res);
block.getByPosition(result).column = col_res;
}
else if (typeid_cast<const ColumnArray *>(column.get()) || typeid_cast<const ColumnConstArray *>(column.get()))
{
FunctionArrayReverse().execute(block, arguments, result);
}
else
throw Exception("Illegal column " + block.getByPosition(arguments[0]).column->getName()
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN);
}
String FunctionAppendTrailingCharIfAbsent::getName() const
{
return name;
}
DataTypePtr FunctionAppendTrailingCharIfAbsent::getReturnType(const DataTypes & arguments) const
{
if (arguments.size() != 2)
throw Exception{
"Number of arguments for function " + getName() + " doesn't match: passed "
+ toString(arguments.size()) + ", should be 2.",
ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH
};
if (!typeid_cast<const DataTypeString *>(arguments[0].get()))
throw Exception{
"Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT
};
if (!typeid_cast<const DataTypeString *>(arguments[1].get()))
throw Exception{
"Illegal type " + arguments[1]->getName() + " of argument of function " + getName(),
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT
};
return std::make_shared<DataTypeString>();
}
void FunctionAppendTrailingCharIfAbsent::execute(Block & block, const ColumnNumbers & arguments, const size_t result)
{
const auto & column = block.getByPosition(arguments[0]).column;
const auto & column_char = block.getByPosition(arguments[1]).column;
if (!typeid_cast<const ColumnConstString *>(column_char.get()))
throw Exception{
"Second argument of function " + getName() + " must be a constant string",
ErrorCodes::ILLEGAL_COLUMN
};
const auto & trailing_char_str = static_cast<const ColumnConstString &>(*column_char).getData();
if (trailing_char_str.size() != 1)
throw Exception{
"Second argument of function " + getName() + " must be a one-character string",
ErrorCodes::BAD_ARGUMENTS
};
if (const auto col = typeid_cast<const ColumnString *>(&*column))
{
auto col_res = std::make_shared<ColumnString>();
block.getByPosition(result).column = col_res;
const auto & src_data = col->getChars();
const auto & src_offsets = col->getOffsets();
auto & dst_data = col_res->getChars();
auto & dst_offsets = col_res->getOffsets();
const auto size = src_offsets.size();
dst_data.resize(src_data.size() + size);
dst_offsets.resize(size);
ColumnString::Offset_t src_offset{};
ColumnString::Offset_t dst_offset{};
for (const auto i : ext::range(0, size))
{
const auto src_length = src_offsets[i] - src_offset;
memcpySmallAllowReadWriteOverflow15(&dst_data[dst_offset], &src_data[src_offset], src_length);
src_offset = src_offsets[i];
dst_offset += src_length;
if (src_length > 1 && dst_data[dst_offset - 2] != trailing_char_str.front())
{
dst_data[dst_offset - 1] = trailing_char_str.front();
dst_data[dst_offset] = 0;
++dst_offset;
}
dst_offsets[i] = dst_offset;
}
dst_data.resize_assume_reserved(dst_offset);
}
else if (const auto col = typeid_cast<const ColumnConstString *>(&*column))
{
const auto & in_data = col->getData();
block.getByPosition(result).column = std::make_shared<ColumnConstString>(
col->size(),
in_data.size() == 0 ? in_data :
in_data.back() == trailing_char_str.front() ? in_data : in_data + trailing_char_str);
}
else
throw Exception{
"Illegal column " + block.getByPosition(arguments[0]).column->getName()
+ " of argument of function " + getName(),
ErrorCodes::ILLEGAL_COLUMN
};
}
void registerFunctionsString(FunctionFactory & factory)
{
factory.registerFunction<FunctionEmpty>();

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -65,8 +65,8 @@ target_link_libraries (io_operators dbms)
if (NOT APPLE)
add_executable(write_buffer_aio write_buffer_aio.cpp)
target_link_libraries (write_buffer_aio dbms ${BOOST_FILESYSTEM_LIB})
target_link_libraries (write_buffer_aio dbms ${Boost_FILESYSTEM_LIBRARY})
add_executable(read_buffer_aio read_buffer_aio.cpp)
target_link_libraries (read_buffer_aio dbms ${BOOST_FILESYSTEM_LIB})
endif()
target_link_libraries (read_buffer_aio dbms ${Boost_FILESYSTEM_LIBRARY})
endif ()

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -189,6 +189,7 @@ void Compiler::compile(
" -shared -fPIC -fvisibility=hidden -fno-implement-inlines"
" -isystem /usr/share/clickhouse/headers/usr/local/include/"
" -isystem /usr/share/clickhouse/headers/usr/include/"
" -isystem /usr/share/clickhouse/headers/usr/include/mysql/"
" -isystem /usr/share/clickhouse/headers/usr/include/c++/*/"
" -isystem /usr/share/clickhouse/headers/usr/include/x86_64-linux-gnu/"
" -isystem /usr/share/clickhouse/headers/usr/include/x86_64-linux-gnu/c++/*/"

View File

@ -51,4 +51,4 @@ target_link_libraries (in_join_subqueries_preprocessor dbms)
add_check(in_join_subqueries_preprocessor)
add_executable (users users.cpp)
target_link_libraries (users dbms ${BOOST_FILESYSTEM_LIB})
target_link_libraries (users dbms ${Boost_FILESYSTEM_LIBRARY})

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -42,30 +42,11 @@ struct Estimator
void selectWithinPartition(
const LevelMergeSelector::PartsInPartition & parts,
const size_t max_total_size_to_merge,
const time_t current_min_part_age,
Estimator & estimator,
const LevelMergeSelector::Settings & settings)
{
size_t parts_size = parts.size();
if (parts_size <= 1)
return;
/// Will lower 'min_parts_to_merge' if all parts are old enough.
/// NOTE It is called base, because it is a base of logarithm, that determines merge tree depth.
double actual_base = settings.min_parts_to_merge;
if (current_min_part_age > settings.lower_base_after)
{
actual_base -= log2(current_min_part_age - settings.lower_base_after);
if (actual_base < 2)
actual_base = 2;
}
if (parts.size() > settings.fallback_after_num_parts)
actual_base = 2;
/// Not enough parts to merge.
if (parts.size() < actual_base)
if (parts_size < settings.parts_to_merge)
return;
/// To easily calculate sum size in any range.
@ -79,79 +60,43 @@ void selectWithinPartition(
prefix_sums[i + 1] = prefix_sum;
}
size_t max_level = 0;
size_t prev_level = -1;
bool has_range_of_same_level = false;
/// Use "corrected" level. It will be non-decreasing while traversing parts right to left.
/// This is done for compatibility with another algorithms.
size_t corrected_level_at_left = 0;
size_t corrected_level_at_right = 0;
for (const auto & part : parts)
size_t range_end = parts_size;
size_t range_begin = range_end - settings.parts_to_merge;
for (size_t i = range_begin; i < range_end; ++i)
if (corrected_level_at_left < parts[i].level)
corrected_level_at_left = parts[i].level;
while (true)
{
if (part.level > max_level)
max_level = part.level;
if (corrected_level_at_left < parts[range_begin].level)
corrected_level_at_left = parts[range_begin].level;
if (part.level == prev_level)
has_range_of_same_level = true;
if (corrected_level_at_right < parts[range_end - 1].level)
corrected_level_at_right = parts[range_end - 1].level;
prev_level = part.level;
/// Leftmost range of same corrected level.
if (corrected_level_at_left == corrected_level_at_right
&& (range_begin == 0 || parts[range_begin - 1].level > corrected_level_at_left))
{
size_t range_size = prefix_sums[range_end] - prefix_sums[range_begin];
if (range_size <= max_total_size_to_merge)
estimator.consider(parts.begin() + range_begin, parts.begin() + range_end, range_size);
break; /// Minumum level is enough.
}
/// If no ranges of same level - then nothing to merge
/// except case when parts are old (much time has passed) and 'base' was lowered to minimum.
if (!has_range_of_same_level && actual_base > 2)
return;
/// For each level, try to select range of parts with that level.
for (size_t level = 0; level <= max_level; ++level)
{
bool in_range = false;
size_t range_begin = 0;
size_t range_end = 0;
for (size_t i = 0; i <= parts_size; ++i)
{
/// But if !has_range_of_same_level - it is allowed to select parts with any different levels.
if (i < parts_size && (parts[i].level == level || !has_range_of_same_level))
{
if (!in_range)
{
in_range = true;
range_begin = i;
}
}
else
{
if (in_range)
{
in_range = false;
range_end = i;
size_t range_length = range_end - range_begin;
/// Length of range is enough.
if (range_length >= actual_base)
{
/// If length of range is larger than 'max_parts_to_merge' - split it to subranges of almost equal lengths.
/// For example, if 'max_parts_to_merge' == 100 and 'range_length' = 101, split it to subranges of lengths 50 and 51.
size_t num_subranges = (range_length + settings.max_parts_to_merge - 1) / settings.max_parts_to_merge;
for (size_t subrange_index = 0; subrange_index < num_subranges; ++subrange_index)
{
size_t subrange_begin = range_begin + subrange_index * range_length / num_subranges;
size_t subrange_end = range_begin + (subrange_index + 1) * range_length / num_subranges;
size_t size_of_subrange = prefix_sums[subrange_end] - prefix_sums[subrange_begin];
/// Don't consider this range if its size is too large.
if (!max_total_size_to_merge || size_of_subrange <= max_total_size_to_merge)
estimator.consider(parts.begin() + subrange_begin, parts.begin() + subrange_end, size_of_subrange);
}
}
}
}
}
/// If we don't care of levels, first iteration was enough.
if (!has_range_of_same_level)
if (range_begin == 0)
break;
--range_begin;
--range_end;
}
}
@ -162,16 +107,10 @@ LevelMergeSelector::PartsInPartition LevelMergeSelector::select(
const Partitions & partitions,
const size_t max_total_size_to_merge)
{
time_t min_age = -1;
for (const auto & partition : partitions)
for (const auto & part : partition)
if (min_age == -1 || part.age < min_age)
min_age = part.age;
Estimator estimator;
for (const auto & partition : partitions)
selectWithinPartition(partition, max_total_size_to_merge, min_age, estimator, settings);
selectWithinPartition(partition, max_total_size_to_merge, estimator, settings);
return estimator.getBest();
}

View File

@ -21,7 +21,7 @@ MergeTreeBlockInputStream::MergeTreeBlockInputStream(const String & path_, ///
const MarkRanges & mark_ranges_, bool use_uncompressed_cache_,
ExpressionActionsPtr prewhere_actions_, String prewhere_column_, bool check_columns,
size_t min_bytes_to_use_direct_io_, size_t max_read_buffer_size_,
bool save_marks_in_cache_)
bool save_marks_in_cache_, bool quiet)
:
path(path_), block_size(block_size_),
storage(storage_), owned_data_part(owned_data_part_),
@ -97,6 +97,7 @@ MergeTreeBlockInputStream::MergeTreeBlockInputStream(const String & path_, ///
total_rows += range.end - range.begin;
total_rows *= storage.index_granularity;
if (!quiet)
LOG_TRACE(log, "Reading " << all_mark_ranges.size() << " ranges from part " << owned_data_part->name
<< ", approx. " << total_rows
<< (all_mark_ranges.size() > 1

View File

@ -16,11 +16,13 @@
#include <DB/DataStreams/AggregatingSortedBlockInputStream.h>
#include <DB/DataStreams/MaterializingBlockInputStream.h>
#include <DB/DataStreams/ConcatBlockInputStream.h>
#include <DB/DataStreams/ColumnGathererStream.h>
#include <DB/Storages/MergeTree/BackgroundProcessingPool.h>
#include <DB/Common/Increment.h>
#include <DB/Common/interpolate.h>
#include <cmath>
#include <numeric>
namespace ProfileEvents
@ -42,6 +44,10 @@ namespace ErrorCodes
extern const int ABORTED;
}
using MergeAlgorithm = MergeTreeDataMerger::MergeAlgorithm;
namespace
{
@ -95,10 +101,16 @@ size_t MergeTreeDataMerger::getMaxPartsSizeForMerge(size_t pool_size, size_t poo
if (pool_used > pool_size)
throw Exception("Logical error: invalid arguments passed to getMaxPartsSizeForMerge: pool_used > pool_size", ErrorCodes::LOGICAL_ERROR);
size_t max_size = interpolateExponential(
size_t free_entries = pool_size - pool_used;
size_t max_size = 0;
if (free_entries >= data.settings.number_of_free_entries_in_pool_to_lower_max_size_of_merge)
max_size = data.settings.max_bytes_to_merge_at_max_space_in_pool;
else
max_size = interpolateExponential(
data.settings.max_bytes_to_merge_at_min_space_in_pool,
data.settings.max_bytes_to_merge_at_max_space_in_pool,
static_cast<double>(pool_size - pool_used) / pool_size);
static_cast<double>(free_entries) / data.settings.number_of_free_entries_in_pool_to_lower_max_size_of_merge);
return std::min(max_size, static_cast<size_t>(DiskSpaceMonitor::getUnreservedFreeSpace(data.full_path) / DISK_USAGE_COEFFICIENT_TO_SELECT));
}
@ -283,7 +295,159 @@ MergeTreeData::DataPartsVector MergeTreeDataMerger::selectAllPartsFromPartition(
}
/// parts должны быть отсортированы.
/// PK columns are sorted and merged, ordinary columns are gathered using info from merge step
static void extractMergingAndGatheringColumns(const NamesAndTypesList & all_columns, ExpressionActionsPtr primary_key_expressions,
const MergeTreeData::MergingParams & merging_params,
NamesAndTypesList & gathering_columns, Names & gathering_column_names,
NamesAndTypesList & merging_columns, Names & merging_column_names
)
{
Names key_columns_dup = primary_key_expressions->getRequiredColumns();
std::set<String> key_columns(key_columns_dup.cbegin(), key_columns_dup.cend());
/// Force sign column for Collapsing mode
if (merging_params.mode == MergeTreeData::MergingParams::Collapsing)
key_columns.emplace(merging_params.sign_column);
/// TODO: also force "summing" and "aggregating" columns to make Horizontal merge only for such columns
for (auto & column : all_columns)
{
auto it = std::find(key_columns.cbegin(), key_columns.cend(), column.name);
if (key_columns.end() == it)
{
gathering_columns.emplace_back(column);
gathering_column_names.emplace_back(column.name);
}
else
{
merging_columns.emplace_back(column);
merging_column_names.emplace_back(column.name);
}
}
}
/* Allow to compute more accurate progress statistics */
class ColumnSizeEstimator
{
MergeTreeData::DataPart::ColumnToSize map;
public:
/// Stores approximate size of columns in bytes
/// Exact values are not required since it used for relative values estimation (progress).
size_t sum_total = 0;
size_t sum_index_columns = 0;
size_t sum_ordinary_columns = 0;
ColumnSizeEstimator(const MergeTreeData::DataPart::ColumnToSize & map_, const Names & key_columns, const Names & ordinary_columns)
: map(map_)
{
for (const auto & name : key_columns)
if (!map.count(name)) map[name] = 0;
for (const auto & name : ordinary_columns)
if (!map.count(name)) map[name] = 0;
for (const auto & name : key_columns)
sum_index_columns += map.at(name);
for (const auto & name : ordinary_columns)
sum_ordinary_columns += map.at(name);
sum_total = std::max(1UL, sum_index_columns + sum_ordinary_columns);
}
/// Approximate size of num_rows column elements if column contains num_total_rows elements
Float64 columnSize(const String & column, size_t num_rows, size_t num_total_rows) const
{
return static_cast<Float64>(map.at(column)) / num_total_rows * num_rows;
}
/// Relative size of num_rows column elements (in comparison with overall size of all columns) if column contains num_total_rows elements
Float64 columnProgress(const String & column, size_t num_rows, size_t num_total_rows) const
{
return columnSize(column, num_rows, num_total_rows) / sum_total;
}
/// Like columnSize, but takes into account only PK columns
Float64 keyColumnsSize(size_t num_rows, size_t num_total_rows) const
{
return static_cast<Float64>(sum_index_columns) / num_total_rows * num_rows;
}
/// Like columnProgress, but takes into account only PK columns
Float64 keyColumnsProgress(size_t num_rows, size_t num_total_rows) const
{
return keyColumnsSize(num_rows, num_total_rows) / sum_total;
}
};
class MergeProgressCallback : public ProgressCallback
{
public:
MergeProgressCallback(MergeList::Entry & merge_entry_) : merge_entry(merge_entry_) {}
MergeProgressCallback(MergeList::Entry & merge_entry_, MergeTreeDataMerger::MergeAlgorithm merge_alg_, size_t num_total_rows,
const ColumnSizeEstimator & column_sizes)
: merge_entry(merge_entry_), merge_alg(merge_alg_)
{
if (merge_alg == MergeAlgorithm::Horizontal)
average_elem_progress = 1.0 / num_total_rows;
else
average_elem_progress = column_sizes.keyColumnsProgress(1, num_total_rows);
}
MergeList::Entry & merge_entry;
const MergeAlgorithm merge_alg{MergeAlgorithm::Vertical};
Float64 average_elem_progress;
void operator() (const Progress & value)
{
ProfileEvents::increment(ProfileEvents::MergedUncompressedBytes, value.bytes);
merge_entry->bytes_read_uncompressed += value.bytes;
merge_entry->rows_with_key_columns_read += value.rows;
if (merge_alg == MergeAlgorithm::Horizontal)
{
ProfileEvents::increment(ProfileEvents::MergedRows, value.rows);
merge_entry->rows_read += value.rows;
merge_entry->progress = average_elem_progress * merge_entry->rows_read;
}
else
{
merge_entry->progress = average_elem_progress * merge_entry->rows_with_key_columns_read;
}
};
};
class MergeProgressCallbackVerticalStep : public MergeProgressCallback
{
public:
MergeProgressCallbackVerticalStep(MergeList::Entry & merge_entry_, size_t num_total_rows_exact,
const ColumnSizeEstimator & column_sizes, const String & column_name)
: MergeProgressCallback(merge_entry_), initial_progress(merge_entry->progress)
{
average_elem_progress = column_sizes.columnProgress(column_name, 1, num_total_rows_exact);
}
Float64 initial_progress;
/// NOTE: not thread safe (to be copyable). It is OK in current single thread use case
size_t rows_read_internal{0};
void operator() (const Progress & value)
{
merge_entry->bytes_read_uncompressed += value.bytes;
ProfileEvents::increment(ProfileEvents::MergedUncompressedBytes, value.bytes);
rows_read_internal += value.rows;
Float64 local_progress = average_elem_progress * rows_read_internal;
merge_entry->progress = initial_progress + local_progress;
};
};
/// parts should be sorted.
MergeTreeData::MutableDataPartPtr MergeTreeDataMerger::mergePartsToTemporaryPart(
MergeTreeData::DataPartsVector & parts, const String & merged_name, MergeList::Entry & merge_entry,
size_t aio_threshold, time_t time_of_merge, DiskSpaceMonitor::Reservation * disk_reservation)
@ -308,55 +472,62 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMerger::mergePartsToTemporaryPart
}
MergeTreeData::DataPart::ColumnToSize merged_column_to_size;
if (aio_threshold > 0)
{
for (const MergeTreeData::DataPartPtr & part : parts)
part->accumulateColumnSizes(merged_column_to_size);
}
Names column_names = data.getColumnNamesList();
NamesAndTypesList column_names_and_types = data.getColumnsList();
Names all_column_names = data.getColumnNamesList();
NamesAndTypesList all_columns = data.getColumnsList();
SortDescription sort_desc = data.getSortDescription();
NamesAndTypesList gathering_columns, merging_columns;
Names gathering_column_names, merging_column_names;
extractMergingAndGatheringColumns(all_columns, data.getPrimaryExpression(), data.merging_params,
gathering_columns, gathering_column_names, merging_columns, merging_column_names);
MergeTreeData::MutableDataPartPtr new_data_part = std::make_shared<MergeTreeData::DataPart>(data);
ActiveDataPartSet::parsePartName(merged_name, *new_data_part);
new_data_part->name = "tmp_" + merged_name;
new_data_part->is_temp = true;
size_t sum_input_rows_upper_bound = merge_entry->total_size_marks * data.index_granularity;
MergedRowSources merged_rows_sources;
MergedRowSources * merged_rows_sources_ptr = &merged_rows_sources;
MergeAlgorithm merge_alg = chooseMergeAlgorithm(data, parts, sum_input_rows_upper_bound, merged_rows_sources);
LOG_DEBUG(log, "Selected MergeAlgorithm: " << ((merge_alg == MergeAlgorithm::Vertical) ? "Vertical" : "Horizontal"));
if (merge_alg != MergeAlgorithm::Vertical)
{
merged_rows_sources_ptr = nullptr;
merging_columns = all_columns;
merging_column_names = all_column_names;
gathering_columns.clear();
gathering_column_names.clear();
}
ColumnSizeEstimator column_sizes(merged_column_to_size, merging_column_names, gathering_column_names);
/** Читаем из всех кусков, сливаем и пишем в новый.
* Попутно вычисляем выражение для сортировки.
*/
BlockInputStreams src_streams;
size_t sum_rows_approx = 0;
const auto rows_total = merge_entry->total_size_marks * data.index_granularity;
for (size_t i = 0; i < parts.size(); ++i)
{
MarkRanges ranges{{0, parts[i]->size}};
String part_path = data.getFullPath() + parts[i]->name + '/';
auto input = std::make_unique<MergeTreeBlockInputStream>(
part_path, DEFAULT_MERGE_BLOCK_SIZE, column_names, data,
parts[i], ranges, false, nullptr, "", true, aio_threshold, DBMS_DEFAULT_BUFFER_SIZE, false);
part_path, DEFAULT_MERGE_BLOCK_SIZE, merging_column_names, data, parts[i],
MarkRanges(1, MarkRange(0, parts[i]->size)), false, nullptr, "", true, aio_threshold, DBMS_DEFAULT_BUFFER_SIZE, false);
input->setProgressCallback([&merge_entry, rows_total] (const Progress & value)
{
const auto new_rows_read = merge_entry->rows_read += value.rows;
merge_entry->progress = static_cast<Float64>(new_rows_read) / rows_total;
merge_entry->bytes_read_uncompressed += value.bytes;
ProfileEvents::increment(ProfileEvents::MergedRows, value.rows);
ProfileEvents::increment(ProfileEvents::MergedUncompressedBytes, value.bytes);
});
input->setProgressCallback(MergeProgressCallback{merge_entry, merge_alg, sum_input_rows_upper_bound, column_sizes});
if (data.merging_params.mode != MergeTreeData::MergingParams::Unsorted)
src_streams.emplace_back(std::make_shared<MaterializingBlockInputStream>(
std::make_shared<ExpressionBlockInputStream>(BlockInputStreamPtr(std::move(input)), data.getPrimaryExpression())));
else
src_streams.emplace_back(std::move(input));
sum_rows_approx += parts[i]->size * data.index_granularity;
}
/// Порядок потоков важен: при совпадении ключа элементы идут в порядке номера потока-источника.
@ -368,32 +539,32 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMerger::mergePartsToTemporaryPart
{
case MergeTreeData::MergingParams::Ordinary:
merged_stream = std::make_unique<MergingSortedBlockInputStream>(
src_streams, data.getSortDescription(), DEFAULT_MERGE_BLOCK_SIZE);
src_streams, sort_desc, DEFAULT_MERGE_BLOCK_SIZE, 0, merged_rows_sources_ptr);
break;
case MergeTreeData::MergingParams::Collapsing:
merged_stream = std::make_unique<CollapsingSortedBlockInputStream>(
src_streams, data.getSortDescription(), data.merging_params.sign_column, DEFAULT_MERGE_BLOCK_SIZE);
src_streams, sort_desc, data.merging_params.sign_column, DEFAULT_MERGE_BLOCK_SIZE, merged_rows_sources_ptr);
break;
case MergeTreeData::MergingParams::Summing:
merged_stream = std::make_unique<SummingSortedBlockInputStream>(
src_streams, data.getSortDescription(), data.merging_params.columns_to_sum, DEFAULT_MERGE_BLOCK_SIZE);
src_streams, sort_desc, data.merging_params.columns_to_sum, DEFAULT_MERGE_BLOCK_SIZE);
break;
case MergeTreeData::MergingParams::Aggregating:
merged_stream = std::make_unique<AggregatingSortedBlockInputStream>(
src_streams, data.getSortDescription(), DEFAULT_MERGE_BLOCK_SIZE);
src_streams, sort_desc, DEFAULT_MERGE_BLOCK_SIZE);
break;
case MergeTreeData::MergingParams::Replacing:
merged_stream = std::make_unique<ReplacingSortedBlockInputStream>(
src_streams, data.getSortDescription(), data.merging_params.version_column, DEFAULT_MERGE_BLOCK_SIZE);
src_streams, sort_desc, data.merging_params.version_column, DEFAULT_MERGE_BLOCK_SIZE);
break;
case MergeTreeData::MergingParams::Graphite:
merged_stream = std::make_unique<GraphiteRollupSortedBlockInputStream>(
src_streams, data.getSortDescription(), DEFAULT_MERGE_BLOCK_SIZE,
src_streams, sort_desc, DEFAULT_MERGE_BLOCK_SIZE,
data.merging_params.graphite_params, time_of_merge);
break;
@ -412,7 +583,7 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMerger::mergePartsToTemporaryPart
static_cast<double>(merge_entry->total_size_bytes_compressed) / data.getTotalActiveSizeInBytes());
MergedBlockOutputStream to{
data, new_part_tmp_path, column_names_and_types, compression_method, merged_column_to_size, aio_threshold};
data, new_part_tmp_path, merging_columns, compression_method, merged_column_to_size, aio_threshold};
merged_stream->readPrefix();
to.writePrefix();
@ -426,19 +597,94 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMerger::mergePartsToTemporaryPart
rows_written += block.rows();
to.write(block);
if (merge_alg == MergeAlgorithm::Horizontal)
merge_entry->rows_written = merged_stream->getProfileInfo().rows;
merge_entry->rows_with_key_columns_written = merged_stream->getProfileInfo().rows;
merge_entry->bytes_written_uncompressed = merged_stream->getProfileInfo().bytes;
if (disk_reservation)
disk_reservation->update(static_cast<size_t>((1 - std::min(1., 1. * rows_written / sum_rows_approx)) * initial_reservation));
/// This update is unactual for VERTICAL algorithm sicne it requires more accurate per-column updates
/// Reservation updates is not performed yet, during the merge it may lead to higher free space requirements
if (disk_reservation && merge_alg == MergeAlgorithm::Horizontal)
{
Float64 relative_rows_written = std::min(1., 1. * rows_written / sum_input_rows_upper_bound);
disk_reservation->update(static_cast<size_t>((1. - relative_rows_written) * initial_reservation));
}
}
if (isCancelled())
throw Exception("Cancelled merging parts", ErrorCodes::ABORTED);
MergeTreeData::DataPart::Checksums checksums_ordinary_columns;
/// Gather ordinary columns
if (merge_alg == MergeAlgorithm::Vertical)
{
size_t sum_input_rows_exact = merge_entry->rows_with_key_columns_read;
merge_entry->columns_written = merging_column_names.size();
merge_entry->progress = column_sizes.keyColumnsProgress(sum_input_rows_exact, sum_input_rows_exact);
BlockInputStreams column_part_streams(parts.size());
NameSet offset_columns_written;
auto it_name_and_type = gathering_columns.cbegin();
for (size_t column_num = 0; column_num < gathering_column_names.size(); ++column_num, it_name_and_type++)
{
const String & column_name = it_name_and_type->name;
const DataTypePtr & column_type = it_name_and_type->type;
const String offset_column_name = DataTypeNested::extractNestedTableName(column_name);
Names column_name_(1, column_name);
NamesAndTypesList column_name_and_type_(1, *it_name_and_type);
Float64 progress_before = merge_entry->progress;
bool offset_written = offset_columns_written.count(offset_column_name);
LOG_TRACE(log, "Gathering column " << column_name << " " << column_type->getName());
for (size_t part_num = 0; part_num < parts.size(); ++part_num)
{
String part_path = data.getFullPath() + parts[part_num]->name + '/';
/// TODO: test perfomance with more accurate settings
auto column_part_stream = std::make_shared<MergeTreeBlockInputStream>(
part_path, DEFAULT_MERGE_BLOCK_SIZE, column_name_, data, parts[part_num],
MarkRanges(1, MarkRange(0, parts[part_num]->size)), false, nullptr, "", true, aio_threshold, DBMS_DEFAULT_BUFFER_SIZE,
false, true);
column_part_stream->setProgressCallback(
MergeProgressCallbackVerticalStep{merge_entry, sum_input_rows_exact, column_sizes, column_name});
column_part_streams[part_num] = std::move(column_part_stream);
}
ColumnGathererStream column_gathered_stream(column_part_streams, column_name, merged_rows_sources, DEFAULT_BLOCK_SIZE);
MergedColumnOnlyOutputStream column_to(data, new_part_tmp_path, true, compression_method, offset_written);
column_to.writePrefix();
while ((block = column_gathered_stream.read()))
{
column_to.write(block);
}
/// NOTE: nested column contains duplicates checksums (and files)
checksums_ordinary_columns.add(column_to.writeSuffixAndGetChecksums());
if (typeid_cast<const DataTypeArray *>(column_type.get()))
offset_columns_written.emplace(offset_column_name);
merge_entry->columns_written = merging_column_names.size() + column_num;
merge_entry->bytes_written_uncompressed += column_gathered_stream.getProfileInfo().bytes;
merge_entry->progress = progress_before + column_sizes.columnProgress(column_name, sum_input_rows_exact, sum_input_rows_exact);
if (isCancelled())
throw Exception("Cancelled merging parts", ErrorCodes::ABORTED);
}
}
merged_stream->readSuffix();
new_data_part->columns = column_names_and_types;
new_data_part->columns = all_columns;
if (merge_alg != MergeAlgorithm::Vertical)
new_data_part->checksums = to.writeSuffixAndGetChecksums();
else
new_data_part->checksums = to.writeSuffixAndGetChecksums(all_columns, &checksums_ordinary_columns);
new_data_part->index.swap(to.getIndex());
/// Для удобства, даже CollapsingSortedBlockInputStream не может выдать ноль строк.
@ -454,6 +700,43 @@ MergeTreeData::MutableDataPartPtr MergeTreeDataMerger::mergePartsToTemporaryPart
}
MergeTreeDataMerger::MergeAlgorithm MergeTreeDataMerger::chooseMergeAlgorithm(
const MergeTreeData & data, const MergeTreeData::DataPartsVector & parts,
size_t sum_rows_upper_bound, MergedRowSources & rows_sources_to_alloc) const
{
if (data.context.getMergeTreeSettings().enable_vertical_merge_algorithm == 0)
return MergeAlgorithm::Horizontal;
bool is_supported_storage =
data.merging_params.mode == MergeTreeData::MergingParams::Ordinary ||
data.merging_params.mode == MergeTreeData::MergingParams::Collapsing;
bool enough_ordinary_cols = data.getColumnNamesList().size() > data.getSortDescription().size();
bool enough_total_rows = sum_rows_upper_bound >= DEFAULT_MERGE_BLOCK_SIZE;
bool no_parts_overflow = parts.size() <= RowSourcePart::MAX_PARTS;
auto merge_alg = (is_supported_storage && enough_total_rows && enough_ordinary_cols && no_parts_overflow) ?
MergeAlgorithm::Vertical : MergeAlgorithm::Horizontal;
if (merge_alg == MergeAlgorithm::Vertical)
{
try
{
rows_sources_to_alloc.reserve(sum_rows_upper_bound);
}
catch (...)
{
/// Not enough memory for VERTICAL merge algorithm, make sense for very large tables
merge_alg = MergeAlgorithm::Horizontal;
}
}
return merge_alg;
}
MergeTreeData::DataPartPtr MergeTreeDataMerger::renameMergedTemporaryPart(
MergeTreeData::DataPartsVector & parts,
MergeTreeData::MutableDataPartPtr & new_data_part,

View File

@ -222,6 +222,14 @@ void MergeTreeDataPartChecksums::addFile(const String & file_name, size_t file_s
files[file_name] = Checksum(file_size, file_hash);
}
void MergeTreeDataPartChecksums::add(MergeTreeDataPartChecksums && rhs_checksums)
{
for (auto & checksum : rhs_checksums.files)
files[std::move(checksum.first)] = std::move(checksum.second);
rhs_checksums.files.clear();
}
/// Контрольная сумма от множества контрольных сумм .bin файлов.
void MergeTreeDataPartChecksums::summaryDataChecksum(SipHash & hash) const
{

View File

@ -215,6 +215,13 @@ MergeTreeReader::Stream::Stream(
}
}
std::unique_ptr<MergeTreeReader::Stream> MergeTreeReader::Stream::createEmptyPtr()
{
std::unique_ptr<Stream> res(new Stream);
res->is_empty = true;
return res;
}
void MergeTreeReader::Stream::loadMarks(MarkCache * cache, bool save_in_cache)
{
@ -285,26 +292,39 @@ void MergeTreeReader::addStream(const String & name, const IDataType & type, con
{
String escaped_column_name = escapeForFileName(name);
/** Если файла с данными нет - то не будем пытаться открыть его.
* Это нужно, чтобы можно было добавлять новые столбцы к структуре таблицы без создания файлов для старых кусков.
const DataTypeArray * type_arr = typeid_cast<const DataTypeArray *>(&type);
bool data_file_exists = Poco::File(path + escaped_column_name + ".bin").exists();
bool is_column_of_nested_type = type_arr && level == 0 && DataTypeNested::extractNestedTableName(name) != name;
/** If data file is missing then we will not try to open it.
* It is necessary since it allows to add new column to structure of the table without creating new files for old parts.
* But we should try to load offset data for array columns of Nested subtable (their data will be filled by default value).
*/
if (!Poco::File(path + escaped_column_name + ".bin").exists())
if (!data_file_exists && !is_column_of_nested_type)
return;
/// Для массивов используются отдельные потоки для размеров.
if (const DataTypeArray * type_arr = typeid_cast<const DataTypeArray *>(&type))
if (type_arr)
{
String size_name = DataTypeNested::extractNestedTableName(name)
+ ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level);
String escaped_size_name = escapeForFileName(DataTypeNested::extractNestedTableName(name))
+ ARRAY_SIZES_COLUMN_NAME_SUFFIX + toString(level);
String size_path = path + escaped_size_name + ".bin";
/// We don't have neither offsets neither data -> skipping, default values will be filled after
if (!data_file_exists && !Poco::File(size_path).exists())
return;
if (!streams.count(size_name))
streams.emplace(size_name, std::make_unique<Stream>(
path + escaped_size_name, uncompressed_cache, mark_cache, save_marks_in_cache,
all_mark_ranges, aio_threshold, max_read_buffer_size, profile_callback, clock_type));
if (data_file_exists)
addStream(name, *type_arr->getNestedType(), all_mark_ranges, profile_callback, clock_type, level + 1);
else
streams.emplace(name, Stream::createEmptyPtr());
}
else
streams.emplace(name, std::make_unique<Stream>(
@ -344,10 +364,11 @@ void MergeTreeReader::readData(const String & name, const IDataType & type, ICol
required_internal_size - array.getData().size(),
level + 1);
size_t read_internal_size = array.getData().size();
/** Исправление для ошибочно записанных пустых файлов с данными массива.
* Такое бывает после ALTER с добавлением новых столбцов во вложенную структуру данных.
*/
size_t read_internal_size = array.getData().size();
if (required_internal_size != read_internal_size)
{
if (read_internal_size != 0)
@ -369,6 +390,11 @@ void MergeTreeReader::readData(const String & name, const IDataType & type, ICol
else
{
Stream & stream = *streams[name];
/// It means that data column of array column will be empty, and it will be replaced by const data column
if (stream.is_empty)
return;
double & avg_value_size_hint = avg_value_size_hints[name];
stream.seekToMark(from_mark);
type.deserializeBinary(column, *stream.data_buffer, max_rows_to_read, avg_value_size_hint);

View File

@ -59,8 +59,12 @@ struct Estimator
* It must be lower, and thus we decide, what range is better to merge.
*
* The integral is lower iff the following formula is lower:
*
* sum_size / (count - 1)
*
* But we have some tunes to prefer longer ranges.
*/
return (sum_size + sum_size_fixed_cost * count) / (count - 1);
return (sum_size + sum_size_fixed_cost * count) / (count - 1.9);
}
double min_score = 0;
@ -124,7 +128,7 @@ bool allow(
// std::cerr << "combined_ratio: " << combined_ratio << "\n";
double lowered_base = interpolateLinear(settings.base, 1.0, combined_ratio);
double lowered_base = interpolateLinear(settings.base, 2.0, combined_ratio);
// std::cerr << "------- lowered_base: " << lowered_base << "\n";

View File

@ -691,7 +691,7 @@ BlockInputStreams StorageLog::read(
max_block_size,
column_names,
*this,
0, std::numeric_limits<size_t>::max(),
0, marksCount() ? std::numeric_limits<size_t>::max() : 0,
settings.max_read_buffer_size));
}
else

View File

@ -24,7 +24,10 @@ StorageSystemMerges::StorageSystemMerges(const std::string & name)
{ "bytes_read_uncompressed", std::make_shared<DataTypeUInt64>() },
{ "rows_read", std::make_shared<DataTypeUInt64>() },
{ "bytes_written_uncompressed", std::make_shared<DataTypeUInt64>() },
{ "rows_written", std::make_shared<DataTypeUInt64>() }
{ "rows_written", std::make_shared<DataTypeUInt64>() },
{ "columns_written", std::make_shared<DataTypeUInt64>() },
{ "rows_with_key_columns_read", std::make_shared<DataTypeUInt64>() },
{ "rows_with_key_columns_written", std::make_shared<DataTypeUInt64>() }
}
{
}
@ -58,13 +61,16 @@ BlockInputStreams StorageSystemMerges::read(
ColumnWithTypeAndName col_rows_read{std::make_shared<ColumnUInt64>(), std::make_shared<DataTypeUInt64>(), "rows_read"};
ColumnWithTypeAndName col_bytes_written_uncompressed{std::make_shared<ColumnUInt64>(), std::make_shared<DataTypeUInt64>(), "bytes_written_uncompressed"};
ColumnWithTypeAndName col_rows_written{std::make_shared<ColumnUInt64>(), std::make_shared<DataTypeUInt64>(), "rows_written"};
ColumnWithTypeAndName col_columns_written{std::make_shared<ColumnUInt64>(), std::make_shared<DataTypeUInt64>(), "columns_written"};
ColumnWithTypeAndName col_rows_with_key_columns_read{std::make_shared<ColumnUInt64>(), std::make_shared<DataTypeUInt64>(), "rows_with_key_columns_read"};
ColumnWithTypeAndName col_rows_with_key_columns_written{std::make_shared<ColumnUInt64>(), std::make_shared<DataTypeUInt64>(), "rows_with_key_columns_written"};
for (const auto & merge : context.getMergeList().get())
{
col_database.column->insert(merge.database);
col_table.column->insert(merge.table);
col_elapsed.column->insert(merge.watch.elapsedSeconds());
col_progress.column->insert(merge.progress);
col_progress.column->insert(std::min(1., merge.progress)); /// little cheat
col_num_parts.column->insert(merge.num_parts);
col_result_part_name.column->insert(merge.result_part_name);
col_total_size_bytes_compressed.column->insert(merge.total_size_bytes_compressed);
@ -73,6 +79,9 @@ BlockInputStreams StorageSystemMerges::read(
col_rows_read.column->insert(merge.rows_read.load(std::memory_order_relaxed));
col_bytes_written_uncompressed.column->insert(merge.bytes_written_uncompressed.load(std::memory_order_relaxed));
col_rows_written.column->insert(merge.rows_written.load(std::memory_order_relaxed));
col_columns_written.column->insert(merge.columns_written.load(std::memory_order_relaxed));
col_rows_with_key_columns_read.column->insert(merge.rows_with_key_columns_read.load(std::memory_order_relaxed));
col_rows_with_key_columns_written.column->insert(merge.rows_with_key_columns_written.load(std::memory_order_relaxed));
}
Block block{
@ -87,7 +96,10 @@ BlockInputStreams StorageSystemMerges::read(
col_bytes_read_uncompressed,
col_rows_read,
col_bytes_written_uncompressed,
col_rows_written
col_rows_written,
col_columns_written,
col_rows_with_key_columns_read,
col_rows_with_key_columns_written
};
return BlockInputStreams{1, std::make_shared<OneBlockInputStream>(block)};

View File

@ -33,3 +33,5 @@ target_link_libraries (merge_selector dbms)
add_executable (merge_selector2 merge_selector2.cpp)
target_link_libraries (merge_selector2 dbms)
add_executable (row_source_bitwise_compatibility row_source_bitwise_test.cpp)

View File

@ -4,6 +4,7 @@
#include <DB/IO/Operators.h>
#include <DB/Storages/MergeTree/SimpleMergeSelector.h>
#include <DB/Storages/MergeTree/LevelMergeSelector.h>
#include <DB/Common/formatReadable.h>
/** This program tests merge-selecting algorithm.
@ -21,13 +22,11 @@ int main(int argc, char ** argv)
IMergeSelector::Partitions partitions(1);
IMergeSelector::PartsInPartition & parts = partitions.back();
SimpleMergeSelector::Settings settings;
SimpleMergeSelector selector(settings);
/* SimpleMergeSelector::Settings settings;
SimpleMergeSelector selector(settings);*/
/* LevelMergeSelector::Settings settings;
settings.min_parts_to_merge = 8;
settings.max_parts_to_merge = 16;
LevelMergeSelector selector(settings);*/
LevelMergeSelector::Settings settings;
LevelMergeSelector selector(settings);
ReadBufferFromFileDescriptor in(STDIN_FILENO);
@ -41,18 +40,19 @@ int main(int argc, char ** argv)
IMergeSelector::Part part;
in >> part.size >> "\t" >> part.age >> "\t" >> part.level >> "\t" >> part_names.back() >> "\n";
part.data = part_names.back().data();
part.level = 0;
// part.level = 0;
parts.emplace_back(part);
sum_parts_size += part.size;
}
size_t total_size_merged = 0;
size_t sum_size_written = sum_parts_size;
size_t num_merges = 1;
size_t age_passed = 0;
while (parts.size() > 1)
{
IMergeSelector::PartsInPartition selected_parts = selector.select(partitions, 0 /*100ULL * 1024 * 1024 * 1024*/);
IMergeSelector::PartsInPartition selected_parts = selector.select(partitions, 100ULL * 1024 * 1024 * 1024);
if (selected_parts.empty())
{
@ -109,6 +109,8 @@ int main(int argc, char ** argv)
std::cout << '\n';
sum_size_written += sum_merged_size;
total_size_merged += sum_merged_size;
++num_merges;
double time_to_merge = sum_merged_size / (1048576 * 10.0);
@ -117,7 +119,9 @@ int main(int argc, char ** argv)
for (auto & part : parts)
part.age += time_to_merge;
std::cout << "Time passed: " << age_passed << ", num parts: " << parts.size() << '\n';
std::cout << "Time passed: " << age_passed << ", num parts: " << parts.size()
<< ", merged " << selected_parts.size() << " parts, " << formatReadableSizeWithBinarySuffix(sum_merged_size)
<< ", total written: " << formatReadableSizeWithBinarySuffix(total_size_merged) << '\n';
}
std::cout << std::fixed << std::setprecision(2)

View File

@ -0,0 +1,34 @@
#include <cstdlib>
#include <DB/DataStreams/ColumnGathererStream.h>
using DB::RowSourcePart;
static void check(const RowSourcePart & s, size_t num, bool flag)
{
if ((s.getSourceNum() != num || s.getSkipFlag() != flag) || (!flag && s.getData() != num))
{
printf("FAIL");
std::exit(-1);
}
}
int main(int, char **)
{
check(RowSourcePart(0, false), 0, false);
check(RowSourcePart(0, true), 0, true);
check(RowSourcePart(1, false), 1, false);
check(RowSourcePart(1, true), 1, true);
check(RowSourcePart(RowSourcePart::MAX_PARTS, false), RowSourcePart::MAX_PARTS, false);
check(RowSourcePart(RowSourcePart::MAX_PARTS, true), RowSourcePart::MAX_PARTS, true);
RowSourcePart p{80, false};
check(p, 80, false);
p.setSkipFlag(true);
check(p, 80, true);
p.setSkipFlag(false);
check(p, 80, false);
p.setSourceNum(RowSourcePart::MAX_PARTS);
check(p, RowSourcePart::MAX_PARTS, false);
printf("PASSED");
return 0;
}

View File

@ -0,0 +1,9 @@
[0] [1] ['1']
[0,0] [1,2] ['1','12']
[0,0,0] [1,2,3] ['1','12','123']
[0]
[0,0]
[0,0,0]
[0]
[0,0]
[0,0,0]

View File

@ -0,0 +1,17 @@
DROP TABLE IF EXISTS test.alter;
CREATE TABLE test.alter (d Date, k UInt64, i32 Int32, n Nested(ui8 UInt8, s String)) ENGINE=MergeTree(d, k, 8192);
INSERT INTO test.alter VALUES ('2015-01-01', 3, 30, [1,2,3], ['1','12','123']);
INSERT INTO test.alter VALUES ('2015-01-01', 2, 20, [1,2], ['1','12']);
INSERT INTO test.alter VALUES ('2015-01-01', 1, 10, [1], ['1']);
ALTER TABLE test.alter ADD COLUMN `n.i8` Array(Int8) AFTER i32;
SELECT `n.i8`, `n.ui8`, `n.s` FROM test.alter ORDER BY k;
SELECT `n.i8` FROM test.alter ORDER BY k;
OPTIMIZE TABLE test.alter;
SELECT `n.i8` FROM test.alter ORDER BY k;
DROP TABLE IF EXISTS test.alter;

View File

@ -0,0 +1,4 @@
bao ba*
bar bar
boa b*a
foo f**

View File

@ -0,0 +1,15 @@
DROP TABLE IF EXISTS test.replaceall;
CREATE TABLE test.replaceall (str FixedString(3)) ENGINE = Memory;
INSERT INTO test.replaceall VALUES ('foo');
INSERT INTO test.replaceall VALUES ('boa');
INSERT INTO test.replaceall VALUES ('bar');
INSERT INTO test.replaceall VALUES ('bao');
SELECT
str,
replaceAll(str, 'o', '*') AS replaced
FROM test.replaceall
ORDER BY str ASC;
DROP TABLE test.replaceall;

View File

@ -111,7 +111,7 @@ This is rarely used but enabled by default.
If you don't need it, you could set variable and skip installation step:
```
export DISABLE_MONGODB=1
export ENABLE_MONGODB=0
```
Otherwise:

View File

@ -24,7 +24,7 @@ brew install boost --cc=gcc-6
## Install required libraries
```
brew install icu4c mysql openssl unixodbc glib libtool gettext
brew install icu4c mysql openssl unixodbc glib libtool gettext homebrew/dupes/libiconv homebrew/dupes/zlib
```
## Install optional libraries
@ -54,7 +54,7 @@ export CXX=g++-6
## Disable MongoDB binding
```
export DISABLE_MONGODB=1
export ENABLE_MONGODB=0
```
## Detect number of threads
@ -72,3 +72,7 @@ cmake ..
make -j $THREADS
cd ..
```
# Caveats
If you intend to run clickhouse-server, make sure to increase system's maxfiles variable. See [MacOS.md](https://github.com/yandex/ClickHouse/blob/master/MacOS.md) for more details.

View File

@ -194,8 +194,7 @@
QueryProcessingStage::Enum stage = QueryProcessingStage::Complete); /// До какой стадии выполнять SELECT запрос.
```
4. Комментарии следует писать на русском или английском языке.
Если вы хорошо умеете выразить мысль на английском, то лучше писать на английском языке.
4. Комментарии следует писать только на английском языке.
5. При написании библиотеки, разместите подробный комментарий о том, что это такое, в самом главном заголовочном файле.

View File

@ -33,7 +33,7 @@ https://github.com/yandex/ClickHouse/blob/master/doc/build.md
Install required packages. After that let's run the following command from directory with source code of ClickHouse:
```
~/ClickHouse$ DISABLE_MONGODB=1 ./release --standalone
~/ClickHouse$ ENABLE_MONGODB=0 ./release --standalone
```
The build successfully completed:

View File

@ -33,7 +33,7 @@ https://github.com/yandex/ClickHouse/blob/master/doc/build.md
Установим необходимые пакеты. После этого выполним следующую команду из директории с исходными кодами ClickHouse:
```
~/ClickHouse$ DISABLE_MONGODB=1 ./release --standalone
~/ClickHouse$ ENABLE_MONGODB=0 ./release --standalone
```
Сборка успешно завершена:

View File

@ -441,7 +441,7 @@ By default, access is allowed from everywhere for the default user without a pas
===Installing from source===
Build following the instructions in <a href="https://github.com/yandex/ClickHouse/blob/master/doc/build.md">build.md</a>
To build, follow the instructions in <a href="https://github.com/yandex/ClickHouse/blob/master/doc/build.md">build.md</a> (for Linux) or in <a href="https://github.com/yandex/ClickHouse/blob/master/doc/build_osx.md">build_osx.md</a> (for Mac OS X).
You can compile packages and install them. You can also use programs without installing packages.
@ -4607,73 +4607,73 @@ Casting to FixedString(N) works only for String and FixedString(N).
==Functions for working with dates and times==
===toYear===
- Converts a date or date with time to a UInt16 number containing the year number (AD).
Converts a date or date with time to a UInt16 number containing the year number (AD).
===toMonth===
- Converts a date or date with time to a UInt8 number containing the month number (1-12).
Converts a date or date with time to a UInt8 number containing the month number (1-12).
===toDayOfMonth===
- Converts a date or date with time to a UInt8 number containing the number of the day of the month (1-31).
Converts a date or date with time to a UInt8 number containing the number of the day of the month (1-31).
===toDayOfWeek===
- Converts a date or date with time to a UInt8 number containing the number of the day of the week (Monday is 1, and Sunday is 7).
Converts a date or date with time to a UInt8 number containing the number of the day of the week (Monday is 1, and Sunday is 7).
===toHour===
- Converts a date with time to a UInt8 number containing the number of the hour in 24-hour time (0-23).
Converts a date with time to a UInt8 number containing the number of the hour in 24-hour time (0-23).
This function assumes that if clocks are moved ahead, it is by one hour and occurs at 2 a.m., and if clocks are moved back, it is by one hour and occurs at 3 a.m. (which is not always true - even in Moscow the clocks were once changed at a different time).
===toMinute===
- Converts a date with time to a UInt8 number containing the number of the minute of the hour (0-59).
Converts a date with time to a UInt8 number containing the number of the minute of the hour (0-59).
===toSecond===
- Converts a date with time to a UInt8 number containing the number of the second in the minute (0-59).
Converts a date with time to a UInt8 number containing the number of the second in the minute (0-59).
Leap seconds are not accounted for.
===toMonday===
- Rounds down a date or date with time to the nearest Monday.
Rounds down a date or date with time to the nearest Monday.
Returns the date.
===toStartOfMonth===
- Rounds down a date or date with time to the first day of the month.
Rounds down a date or date with time to the first day of the month.
Returns the date.
===toStartOfQuarter===
- Rounds down a date or date with time to the first day of the quarter.
Rounds down a date or date with time to the first day of the quarter.
The first day of the quarter is either 1 January, 1 April, 1 July, or 1 October. Returns the date.
===toStartOfYear===
- Rounds down a date or date with time to the first day of the year.
Rounds down a date or date with time to the first day of the year.
Returns the date.
===toStartOfMinute===
- Rounds down a date with time to the start of the minute.
Rounds down a date with time to the start of the minute.
===toStartOfHour===
- Rounds down a date with time to the start of the hour.
Rounds down a date with time to the start of the hour.
===toTime===
- Converts a date with time to the date of the start of the Unix Epoch, while preserving the time.
Converts a date with time to the date of the start of the Unix Epoch, while preserving the time.
===toRelativeYearNum===
- Converts a date with time or date to the number of the year, starting from a certain fixed point in the past.
Converts a date with time or date to the number of the year, starting from a certain fixed point in the past.
===toRelativeMonthNum===
- Converts a date with time or date to the number of the month, starting from a certain fixed point in the past.
Converts a date with time or date to the number of the month, starting from a certain fixed point in the past.
===toRelativeWeekNum===
- Converts a date with time or date to the number of the week, starting from a certain fixed point in the past.
Converts a date with time or date to the number of the week, starting from a certain fixed point in the past.
===toRelativeDayNum===
- Converts a date with time or date to the number of the day, starting from a certain fixed point in the past.
Converts a date with time or date to the number of the day, starting from a certain fixed point in the past.
===toRelativeHourNum===
- Converts a date with time or date to the number of the hour, starting from a certain fixed point in the past.
Converts a date with time or date to the number of the hour, starting from a certain fixed point in the past.
===toRelativeMinuteNum===
- Converts a date with time or date to the number of the minute, starting from a certain fixed point in the past.
Converts a date with time or date to the number of the minute, starting from a certain fixed point in the past.
===toRelativeSecondNum===
- Converts a date with time or date to the number of the second, starting from a certain fixed point in the past.
Converts a date with time or date to the number of the second, starting from a certain fixed point in the past.
===now===
Accepts zero arguments and returns the current time at one of the moments of request execution.
@ -4688,11 +4688,11 @@ Accepts zero arguments and returns yesterday&#39;s date at one of the moments of
The same as &#39;today() - 1&#39;.
===timeSlot===
- Rounds the time to the half hour.
Rounds the time to the half hour.
This function is specific to Yandex.Metrica, since half an hour is the minimum amount of time for breaking a session into two sessions if a counter shows a single user&#39;s consecutive pageviews that differ in time by strictly more than this amount. This means that tuples (the counter number, user ID, and time slot) can be used to search for pageviews that are included in the corresponding session.
===timeSlots(StartTime, Duration)===
- For a time interval starting at &#39;StartTime&#39; and continuing for &#39;Duration&#39; seconds, it returns an array of moments in time, consisting of points from this interval rounded down to the half hour.
For a time interval starting at &#39;StartTime&#39; and continuing for &#39;Duration&#39; seconds, it returns an array of moments in time, consisting of points from this interval rounded down to the half hour.
For example, %%timeSlots(toDateTime(&#39;2012-01-01 12:20:00&#39;), toUInt32(600)) = [toDateTime(&#39;2012-01-01 12:00:00&#39;), toDateTime(&#39;2012-01-01 12:30:00&#39;)]%%.
This is necessary for searching for pageviews in the corresponding session.
@ -4700,52 +4700,52 @@ This is necessary for searching for pageviews in the corresponding session.
==Functions for working with strings==
===empty===
- Returns 1 for an empty string or 0 for a non-empty string.
Returns 1 for an empty string or 0 for a non-empty string.
The result type is UInt8.
A string is considered non-empty if it contains at least one byte, even if this is a space or a null byte.
The function also works for arrays.
===notEmpty===
- Returns 0 for an empty string or 1 for a non-empty string.
Returns 0 for an empty string or 1 for a non-empty string.
The result type is UInt8.
The function also works for arrays.
===length===
- Returns the length of a string in bytes (not in characters, and not in code points).
Returns the length of a string in bytes (not in characters, and not in code points).
The result type is UInt64.
The function also works for arrays.
===lengthUTF8===
- Returns the length of a string in Unicode code points (not in characters), assuming that the string contains a set of bytes that make up UTF-8 encoded text. If this assumption is not met, it returns some result (it doesn&#39;t throw an exception).
Returns the length of a string in Unicode code points (not in characters), assuming that the string contains a set of bytes that make up UTF-8 encoded text. If this assumption is not met, it returns some result (it doesn&#39;t throw an exception).
The result type is UInt64.
===lower===
- Converts ASCII Latin symbols in a string to lowercase.
Converts ASCII Latin symbols in a string to lowercase.
===upper===
- Converts ASCII Latin symbols in a string to uppercase.
Converts ASCII Latin symbols in a string to uppercase.
===lowerUTF8===
- Converts a string to lowercase, assuming the string contains a set of bytes that make up a UTF-8 encoded text. It doesn&#39;t detect the language. So for Turkish the result might not be exactly correct.
Converts a string to lowercase, assuming the string contains a set of bytes that make up a UTF-8 encoded text. It doesn&#39;t detect the language. So for Turkish the result might not be exactly correct.
If length of UTF-8 sequence is different for upper and lower case of code point, then result for that code point could be incorrect.
If value contains invalid UTF-8, the behavior is unspecified.
===upperUTF8===
- Converts a string to uppercase, assuming the string contains a set of bytes that make up a UTF-8 encoded text. It doesn&#39;t detect the language. So for Turkish the result might not be exactly correct.
Converts a string to uppercase, assuming the string contains a set of bytes that make up a UTF-8 encoded text. It doesn&#39;t detect the language. So for Turkish the result might not be exactly correct.
If length of UTF-8 sequence is different for upper and lower case of code point, then result for that code point could be incorrect.
If value contains invalid UTF-8, the behavior is unspecified.
===reverse===
- Reverses the string (as a sequence of bytes).
Reverses the string (as a sequence of bytes).
===reverseUTF8===
- Reverses a sequence of Unicode code points, assuming that the string contains a set of bytes representing a UTF-8 text. Otherwise, it does something else (it doesn&#39;t throw an exception).
Reverses a sequence of Unicode code points, assuming that the string contains a set of bytes representing a UTF-8 text. Otherwise, it does something else (it doesn&#39;t throw an exception).
===concat(s1, s2, ...)===
- Concatenates strings from the function arguments, without a separator.
Concatenates strings from the function arguments, without a separator.
===substring(s, offset, length)===
- Returns a substring starting with the byte from the &#39;offset&#39; index that is &#39;length&#39; bytes long. Character indexing starts from one (as in standard SQL). The &#39;offset&#39; and &#39;length&#39; arguments must be constants.
Returns a substring starting with the byte from the &#39;offset&#39; index that is &#39;length&#39; bytes long. Character indexing starts from one (as in standard SQL). The &#39;offset&#39; and &#39;length&#39; arguments must be constants.
===substringUTF8(s, offset, length)===
The same as &#39;substring&#39;, but for Unicode code points. Works under the assumption that the string contains a set of bytes representing a UTF-8 encoded text. If this assumption is not met, it returns some result (it doesn&#39;t throw an exception).
@ -4753,6 +4753,8 @@ The same as &#39;substring&#39;, but for Unicode code points. Works under the as
===appendTrailingCharIfAbsent(s, c)===
If the %%s%% string is non-empty and does not contain the %%c%% character at the end, it appends the %%c%% character to the end.
===convertCharset(s, from, to)===
Returns a string with the data %%s%% (encoded as %%from%% charset) that was converted to the %%to%% charset.
==Functions for searching strings==
@ -4760,14 +4762,16 @@ The search is case-sensitive in all these functions.
The search substring or regular expression must be a constant in all these functions.
===position(haystack, needle)===
- Search for the &#39;needle&#39; substring in the &#39;haystack&#39; string.
Searches for the &#39;needle&#39; substring in the &#39;haystack&#39; string.
Returns the position (in bytes) of the found substring, starting from 1, or returns 0 if the substring was not found.
There's also positionCaseInsensitive function.
===positionUTF8(haystack, needle)===
The same as &#39;position&#39;, but the position is returned in Unicode code points. Works under the assumption that the string contains a set of bytes representing a UTF-8 encoded text. If this assumption is not met, it returns some result (it doesn&#39;t throw an exception).
There's also positionCaseInsensitiveUTF8 function.
===match(haystack, pattern)===
- Checks whether the string matches the &#39;pattern&#39; regular expression.
Checks whether the string matches the &#39;pattern&#39; regular expression.
The regular expression is re2.
Returns 0 if it doesn&#39;t match, or 1 if it matches.
@ -4784,7 +4788,7 @@ Extracts a fragment of a string using a regular expression. If &#39;haystack&#39
Extracts all the fragments of a string using a regular expression. If &#39;haystack&#39; doesn&#39;t match the &#39;pattern&#39; regex, an empty string is returned. Returns an array of strings consisting of all matches to the regex. In general, the behavior is the same as the &#39;extract&#39; function (it takes the first subpattern, or the entire expression if there isn&#39;t a subpattern).
===like(haystack, pattern), haystack LIKE pattern operator===
- Checks whether a string matches a simple regular expression. The regular expression can contain the metasymbols %%%%% and %%_%%.
Checks whether a string matches a simple regular expression. The regular expression can contain the metasymbols %%%%% and %%_%%.
%%%%% indicates any quantity of any bytes (including zero characters).
%%_%% indicates any one byte.
@ -4866,17 +4870,17 @@ SELECT replaceRegexpAll(&#39;Hello, World!&#39;, &#39;^&#39;, &#39;here: &#39;)
==Functions for working with arrays==
===empty===
- Returns 1 for an empty array, or 0 for a non-empty array.
Returns 1 for an empty array, or 0 for a non-empty array.
The result type is UInt8.
The function also works for strings.
===notEmpty===
- Returns 0 for an empty array, or 1 for a non-empty array.
Returns 0 for an empty array, or 1 for a non-empty array.
The result type is UInt8.
The function also works for strings.
===length===
- Returns the number of items in the array.
Returns the number of items in the array.
The result type is UInt64.
The function also works for strings.
@ -4887,17 +4891,20 @@ The function also works for strings.
===emptyArrayString===
Accepts zero arguments and returns an empty array of the appropriate type.
===emptyArrayToSingle===
Accepts an empty array as argument and returns an array of one element equal to the default value.
===range(N)===
- Returns an array of numbers from 0 to N-1.
Returns an array of numbers from 0 to N-1.
Just in case, an exception is thrown if arrays with a total length of more than 100,000,000 elements are created in a data block.
===array(x1, ...), [x1, ...] operator===
- Creates an array from the function arguments.
Creates an array from the function arguments.
The arguments must be constants and have types that have the smallest common type. At least one argument must be passed, because otherwise it isn&#39;t clear which type of array to create. That is, you can&#39;t use this function to create an empty array (to do that, use the &#39;emptyArray*&#39; function described above).
Returns an &#39;Array(T)&#39; type result, where &#39;T&#39; is the smallest common type out of the passed arguments.
===arrayElement(arr, n), arr[n] operator===
- Get the element with the index &#39;n&#39; from the array &#39;arr&#39;.
Get the element with the index &#39;n&#39; from the array &#39;arr&#39;.
&#39;n&#39; should be any integer type.
Indexes in an array begin from one.
Negative indexes are supported - in this case, it selects the corresponding element numbered from the end. For example, &#39;arr[-1]&#39; is the last item in the array.
@ -4907,18 +4914,18 @@ If the index goes beyond the array bounds:
- otherwise, a default value is returned (0 for numbers, an empty string for strings, etc.).
===has(arr, elem)===
- Checking whether the &#39;arr&#39; array has the &#39;elem&#39; element.
Checks whether the &#39;arr&#39; array has the &#39;elem&#39; element.
Returns 0 if the the element is not in the array, or 1 if it is.
&#39;elem&#39; must be a constant.
===indexOf(arr, x)===
- Returns the index of the &#39;x&#39; element (starting from 1) if it is in the array, or 0 if it is not.
Returns the index of the &#39;x&#39; element (starting from 1) if it is in the array, or 0 if it is not.
===countEqual(arr, x)===
- Returns the number of elements in the array equal to &#39;x&#39;. Equivalent to <span class="inline-example">arrayCount(elem -> elem = x, arr)</span>.
Returns the number of elements in the array equal to &#39;x&#39;. Equivalent to <span class="inline-example">arrayCount(elem -> elem = x, arr)</span>.
===arrayEnumerate(arr)===
- Returns the array %%[1, 2, 3, ..., length(arr)]%%
Returns the array %%[1, 2, 3, ..., length(arr)]%%
This function is normally used together with ARRAY JOIN. It allows counting something just once for each array after applying ARRAY JOIN. Example:
@ -4955,7 +4962,7 @@ WHERE (CounterID = 160656) AND notEmpty(GoalsReached)
This function can also be used in higher-order functions. For example, you can use it to get array indexes for elements that match a condition.
===arrayEnumerateUniq(arr, ...)===
- Returns an array the same size as the source array, indicating for each element what its position is among elements with the same value.
Returns an array the same size as the source array, indicating for each element what its position is among elements with the same value.
For example: %%arrayEnumerateUniq([10, 20, 10, 30]) = [1, 1, 2, 1]%%.
This function is useful when using ARRAY JOIN and aggregation of array elements. Example:
@ -5003,7 +5010,7 @@ SELECT arrayEnumerateUniq([1, 1, 1, 2, 2, 2], [1, 1, 2, 1, 1, 2]) AS res
This is necessary when using ARRAY JOIN with a nested data structure and further aggregation across multiple elements in this structure.
===arrayJoin(arr)===
- A special function. See the section &quot;arrayJoin function&quot;.
A special function. See the section &quot;arrayJoin function&quot;.
==Higher-order functions==
@ -5068,20 +5075,20 @@ Returns the index of the first element in the &#39;arr1&#39; array for which &#3
==Functions for splitting and merging strings and arrays==
===splitByChar(separator, s)===
- Splits a string into substrings, using &#39;separator&#39; as the separator.
Splits a string into substrings, using &#39;separator&#39; as the separator.
&#39;separator&#39; must be a string constant consisting of exactly one character.
Returns an array of selected substrings. Empty substrings may be selected if the separator occurs at the beginning or end of the string, or if there are multiple consecutive separators.
===splitByString(separator, s)===
- The same as above, but it uses a string of multiple characters as the separator. The string must be non-empty.
The same as above, but it uses a string of multiple characters as the separator. The string must be non-empty.
===arrayStringConcat(arr[, separator])===
- Concatenates strings from the array elements, using &#39;separator&#39; as the separator.
Concatenates strings from the array elements, using &#39;separator&#39; as the separator.
&#39;separator&#39; is a string constant, an optional parameter. By default it is an empty string.
Returns a string.
===alphaTokens(s)===
- Selects substrings of consecutive bytes from the range a-z and A-Z.
Selects substrings of consecutive bytes from the range a-z and A-Z.
Returns an array of selected substrings.
@ -5163,19 +5170,19 @@ URLPathHierarchy(&#39;https://example.com/browse/CONV-6788&#39;) =
If the URL doesn&#39;t have anything similar, the URL remains unchanged.
<h4>cutWWW</h4>
- Removes no more than one &#39;www.&#39; from the beginning of the URL&#39;s domain, if present.
Removes no more than one &#39;www.&#39; from the beginning of the URL&#39;s domain, if present.
<h4>cutQueryString</h4>
- Removes the query-string. The question mark is also removed.
Removes the query-string. The question mark is also removed.
<h4>cutFragment</h4>
- Removes the fragment identifier. The number sign is also removed.
Removes the fragment identifier. The number sign is also removed.
<h4>cutQueryStringAndFragment</h4>
- Removes the query-string and fragment identifier. The question mark and number sign are also removed.
Removes the query-string and fragment identifier. The question mark and number sign are also removed.
<h4>cutURLParameter(URL, name)</h4>
- Removes the URL parameter named &#39;name&#39;, if present. This function works under the assumption that the parameter name is encoded in the URL exactly the same way as in the passed argument.
Removes the URL parameter named &#39;name&#39;, if present. This function works under the assumption that the parameter name is encoded in the URL exactly the same way as in the passed argument.
==Functions for working with IP addresses==
@ -5288,11 +5295,11 @@ If an argument is passed, it can be any type, and its value is not used for anyt
The only purpose of this argument is to prevent common subexpression elimination, so that two different instances of the same function return different columns with different random numbers.
===rand===
- Returns a pseudo-random UInt32 number, evenly distributed among all UInt32-type numbers.
Returns a pseudo-random UInt32 number, evenly distributed among all UInt32-type numbers.
Uses a linear congruential generator.
===rand64===
- Returns a pseudo-random UInt64 number, evenly distributed among all UInt64-type numbers.
Returns a pseudo-random UInt64 number, evenly distributed among all UInt64-type numbers.
Uses a linear congruential generator.
@ -5301,45 +5308,45 @@ Uses a linear congruential generator.
Hash functions can be used for deterministic pseudo-random shuffling of elements.
===halfMD5===
- Calculates the MD5 from a string. Then it takes the first 8 bytes of the hash and interprets them as UInt64 in big endian.
Calculates the MD5 from a string. Then it takes the first 8 bytes of the hash and interprets them as UInt64 in big endian.
Accepts a String-type argument. Returns UInt64.
This function works fairly slowly (5 million short strings per second per processor core).
If you don&#39;t need MD5 in particular, use the &#39;sipHash64&#39; function instead.
===MD5===
- Calculates the MD5 from a string and returns the resulting set of bytes as FixedString(16).
Calculates the MD5 from a string and returns the resulting set of bytes as FixedString(16).
If you don&#39;t need MD5 in particular, but you need a decent cryptographic 128-bit hash, use the &#39;sipHash128&#39; function instead.
If you need the same result as gives 'md5sum' utility, write %%lower(hex(MD5(s)))%%.
===sipHash64===
- Calculates SipHash from a string.
Calculates SipHash from a string.
Accepts a String-type argument. Returns UInt64.
SipHash is a cryptographic hash function. It works at least three times faster than MD5. For more information, see <a href="https://131002.net/siphash/">https://131002.net/siphash/</a>
===sipHash128===
- Calculates SipHash from a string.
Calculates SipHash from a string.
Accepts a String-type argument. Returns FixedString(16).
Differs from sipHash64 in that the final xor-folding state is only done up to 128 bytes.
===cityHash64===
- Calculates CityHash64 from a string or a similar hash function for any number of any type of arguments.
Calculates CityHash64 from a string or a similar hash function for any number of any type of arguments.
For String-type arguments, CityHash is used. This is a fast non-cryptographic hash function for strings with decent quality.
For other types of arguments, a decent implementation-specific fast non-cryptographic hash function is used.
If multiple arguments are passed, the function is calculated using the same rules and chain combinations using the CityHash combinator.
For example, you can compute the checksum of an entire table with accuracy up to the row order: %%SELECT sum(cityHash64(*)) FROM table%%.
===intHash32===
- Calculates a 32-bit hash code from any type of integer.
Calculates a 32-bit hash code from any type of integer.
This is a relatively fast non-cryptographic hash function of average quality for numbers.
===intHash64===
- Calculates a 64-bit hash code from any type of integer.
Calculates a 64-bit hash code from any type of integer.
It works faster than intHash32. Average quality.
===SHA1===
===SHA224===
===SHA256===
- Calculates SHA-1, SHA-224, or SHA-256 from a string and returns the resulting set of bytes as FixedString(20), FixedString(28), or FixedString(32).
Calculates SHA-1, SHA-224, or SHA-256 from a string and returns the resulting set of bytes as FixedString(20), FixedString(28), or FixedString(32).
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&#39;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.
@ -5714,12 +5721,12 @@ See the section &quot;IN operators&quot;.
===tuple(x, y, ...), operator (x, y, ...)===
- A function that allows grouping multiple columns.
A function that allows grouping multiple columns.
For columns with the types T1, T2, ..., it returns a Tuple(T1, T2, ...) type tuple containing these columns. There is no cost to execute the function.
Tuples are normally used as intermediate values for an argument of IN operators, or for creating a list of formal parameters of lambda functions. Tuples can&#39;t be written to a table.
===tupleElement(tuple, n), operator x.N===
- A function that allows getting columns from a tuple.
A function that allows getting columns from a tuple.
&#39;N&#39; is the column index, starting from 1. &#39;N&#39; must be a constant. &#39;N&#39; must be a strict postive integer no greater than the size of the tuple.
There is no cost to execute the function.
@ -5727,24 +5734,24 @@ There is no cost to execute the function.
==Other functions==
===hostName()===
- Returns a string with the name of the host that this function was performed on. For distributed processing, this is the name of the remote server host, if the function is performed on a remote server.
Returns a string with the name of the host that this function was performed on. For distributed processing, this is the name of the remote server host, if the function is performed on a remote server.
===visibleWidth(x)===
- Calculates the approximate width when outputting values to the console in text format (tab-separated). This function is used by the system for implementing Pretty formats.
Calculates the approximate width when outputting values to the console in text format (tab-separated). This function is used by the system for implementing Pretty formats.
===toTypeName(x)===
- Gets the type name. Returns a string containing the type name of the passed argument.
Gets the type name. Returns a string containing the type name of the passed argument.
===blockSize()===
- Gets the size of the block.
Gets the size of the block.
In ClickHouse, queries are always run on blocks (sets of column parts). This function allows getting the size of the block that you called it for.
===materialize(x)===
- Turns a constant into a full column containing just one value.
Turns a constant into a full column containing just one value.
In ClickHouse, full columns and constants are represented differently in memory. Functions work differently for constant arguments and normal arguments (different code is executed), although the result is almost always the same. This function is for debugging this behavior.
===ignore(...)===
- A function that accepts any arguments and always returns 0.
A function that accepts any arguments and always returns 0.
However, the argument is still calculated. This can be used for benchmarks.
===sleep(seconds)===
@ -5891,6 +5898,78 @@ LIMIT 10
└────────────────┴─────────┘
%%
===formatReadableSize(x)===
Gets a size (number of bytes). Returns a string that contains rounded size with the suffix (KiB, MiB etc.).
Example:
%%
SELECT
arrayJoin([1, 1024, 1024*1024, 192851925]) AS filesize_bytes,
formatReadableSize(filesize_bytes) AS filesize
┌─filesize_bytes─┬─filesize───┐
│ 1 │ 1.00 B │
│ 1024 │ 1.00 KiB │
│ 1048576 │ 1.00 MiB │
│ 192851925 │ 183.92 MiB │
└────────────────┴────────────┘
%%
===least(a, b)===
Returns the least element of a and b.
===greatest(a, b)===
Returns the greatest element of a and b.
===uptime()===
Returns server's uptime in seconds.
===version()===
Returns server's version as a string.
===rowNumberInAllBlocks()===
Returns an incremental row number within all blocks that were processed by this function.
===runningDifference(x)===
Calculates the difference between consecutive values in the data block.
Result of the function depends on the order of the data in the blocks.
It works only inside of the each processed block of data. Data splitting in the blocks is not explicitly controlled by the user.
If you specify ORDER BY in subquery and call runningDifference outside of it, you could get an expected result.
Example:
%%
SELECT
EventID,
EventTime,
runningDifference(EventTime) AS delta
FROM
(
SELECT
EventID,
EventTime
FROM events
WHERE EventDate = '2016-11-24'
ORDER BY EventTime ASC
LIMIT 5
)
┌─EventID─┬───────────EventTime─┬─delta─┐
│ 1106 │ 2016-11-24 00:00:04 │ 0 │
│ 1107 │ 2016-11-24 00:00:05 │ 1 │
│ 1108 │ 2016-11-24 00:00:05 │ 0 │
│ 1109 │ 2016-11-24 00:00:09 │ 4 │
│ 1110 │ 2016-11-24 00:00:10 │ 1 │
└─────────┴─────────────────────┴───────┘
%%
==arrayJoin function==

View File

@ -450,7 +450,7 @@ ClickHouse содержит настройки ограничения досту
===Установка из исходников===
Для сборки воспользуйтесь инструкцией: <a href="https://github.com/yandex/ClickHouse/blob/master/doc/build.md">build.md</a>
Для сборки воспользуйтесь инструкцией <a href="https://github.com/yandex/ClickHouse/blob/master/doc/build.md">build.md</a> (для Linux) или <a href="https://github.com/yandex/ClickHouse/blob/master/doc/build_osx.md">build_osx.md</a> (для Mac OS X).
Вы можете собрать пакеты и установить их.
Также вы можете использовать программы без установки пакетов.
@ -611,8 +611,8 @@ Date: Fri, 16 Nov 2012 19:21:50 GMT
1
</pre>
Как видно, curl немного неудобен тем, что надо URL-эскейпить пробелы.
wget сам всё эскейпит, но его не рекомендуется использовать, так как он плохо работает по HTTP 1.1 при использовании keep-alive и Transfer-Encoding: chunked.
Как видно, curl немного неудобен тем, что надо экранировать пробелы в URL.
wget экранирует самостоятельно, но его не рекомендуется использовать, так как он плохо работает по HTTP 1.1 при использовании keep-alive и Transfer-Encoding: chunked.
<pre class="terminal">
$ echo 'SELECT 1' | curl 'http://localhost:8123/' --data-binary @-
@ -908,7 +908,7 @@ cat file.csv | clickhouse-client --database=test --query="INSERT INTO test FORMA
== Программа clickhouse-local ==
Программа clickhouse-local позоляет выполнять быструю обработку локальных файлов, хранящих таблицы, не прибегая к развертыванию и настройке clickhouse-server ...
Программа clickhouse-local позволяет выполнять быструю обработку локальных файлов, хранящих таблицы, не прибегая к развертыванию и настройке clickhouse-server ...
==Синтаксис==
@ -943,7 +943,7 @@ cat file.csv | clickhouse-client --database=test --query="INSERT INTO test FORMA
===Идентификаторы===
Идентификаторы (имена столбцов, функций, типов данных) могут быть квотированными или не квотированными.
Не квотированные идентификаторы начинаются на букву латинского алфавита или подчёркивание; продолжаются на букву латинского алфавита или подчёркивание или цифру. Короче говоря, должны соответствовать регекспу %%^[a-zA-Z_][0-9a-zA-Z_]*$%%. Примеры: %%x%%, %%_1%%, %%X_y__Z123_%%.
Не квотированные идентификаторы начинаются на букву латинского алфавита или подчёркивание; продолжаются на букву латинского алфавита или подчёркивание или цифру. Короче говоря, должны соответствовать регулярному выражению %%^[a-zA-Z_][0-9a-zA-Z_]*$%%. Примеры: %%x%%, %%_1%%, %%X_y__Z123_%%.
Квотированные идентификаторы расположены в обратных кавычках %%`id`%% (также, как в MySQL), и могут обозначать произвольный (непустой) набор байт. При этом, внутри записи такого идентификатора, символы (например, символ обратной кавычки) могут экранироваться с помощью обратного слеша. Правила экранирования такие же, как в строковых литералах (см. ниже).
Рекомендуется использовать идентификаторы, которые не нужно квотировать.
@ -955,8 +955,8 @@ cat file.csv | clickhouse-client --database=test --query="INSERT INTO test FORMA
Числовой литерал пытается распарситься:
- сначала как 64-битное число без знака, с помощью функции strtoull;
- если не получилось - то как 64-битное число со знаком, с помощью функци strtoll;
- если не получилось - то как число с плавающей запятой, с помощью функци strtod;
- если не получилось - то как 64-битное число со знаком, с помощью функции strtoll;
- если не получилось - то как число с плавающей запятой, с помощью функции strtod;
- иначе - ошибка.
Соответствующее значение будет иметь тип минимального размера, который вмещает значение.
@ -1598,7 +1598,7 @@ ORDER BY PageViews DESC LIMIT 1000%%
Выборка с указанием относительного коэффициента является "согласованной": если рассмотреть все возможные данные, которые могли бы быть в таблице, то выборка (при использовании одного выражения сэмплирования, указанного при создании таблицы), с одинаковым коэффициентом, выбирает всегда одно и то же подмножество этих всевозможных данных. То есть, выборка из разных таблиц, на разных серверах, в разное время, делается одинаковым образом.
Например, выборка по идентификаторам посетителей, выберет из разных таблиц строки с одинаковым подножеством всех возможных идентификаторов посетителей. Это позволяет использовать выборку в подзапросах в секции IN, а также при ручном сопоставлении результатов разных запросов с выборками.
Например, выборка по идентификаторам посетителей, выберет из разных таблиц строки с одинаковым подмножеством всех возможных идентификаторов посетителей. Это позволяет использовать выборку в подзапросах в секции IN, а также при ручном сопоставлении результатов разных запросов с выборками.
<h4>Секция ARRAY JOIN</h4>
@ -2064,7 +2064,7 @@ WHERE и HAVING отличаются тем, что WHERE выполняется
Секция ORDER BY содержит список выражений, к каждому из которых также может быть приписано DESC или ASC (направление сортировки). Если ничего не приписано - это аналогично приписыванию ASC. ASC - сортировка по возрастанию, DESC - сортировка по убыванию. Обозначение направления сортировки действует на одно выражение, а не на весь список. Пример: %%ORDER BY Visits DESC, SearchPhrase%%
Для сортировки по значениям типа String есть возможность указать collation (сравнение). Пример: %%ORDER BY SearchPhrase COLLATE 'tr'%% - для сортировки по поисковой фразе, по возрастанию, с учётом турецкого алфавита, регистронезависимо, при допущении, что строки в кодировке UTF-8. COLLATE может быть указан или не указан для каждого выражения в ORDER BY независимо. Если есть ASC или DESC, то COLLATE указывается после них. При использовании COLLATE сортировка всегда регистронезависимо.
Для сортировки по значениям типа String есть возможность указать collation (сравнение). Пример: %%ORDER BY SearchPhrase COLLATE 'tr'%% - для сортировки по поисковой фразе, по возрастанию, с учётом турецкого алфавита, регистронезависимо, при допущении, что строки в кодировке UTF-8. COLLATE может быть указан или не указан для каждого выражения в ORDER BY независимо. Если есть ASC или DESC, то COLLATE указывается после них. При использовании COLLATE сортировка всегда регистронезависима.
Рекомендуется использовать COLLATE только для окончательной сортировки небольшого количества строк, так как производительность сортировки с указанием COLLATE меньше, чем обычной сортировки по байтам.
@ -2428,14 +2428,14 @@ curl -F 'passwd=@passwd.tsv;' 'http://localhost:8123/?query=SELECT+shell,+count(
%%Merge(hits, '^WatchLog')%%
- данные будут читаться из таблиц в базе hits, имена которых соответствуют регекспу '^WatchLog'.
- данные будут читаться из таблиц в базе hits, имена которых соответствуют регулярному выражению '^WatchLog'.
Вместо имени базы данных может использоваться константное выражение, возвращающее строку. Например, %%currentDatabase()%%.
Регулярные выражения - re2 (как PCRE, но без особых извратов), регистрозависимые.
Смотрите замечание об экранировании в регулярных выражениях в разделе "match".
При выборе таблиц для чтения, сама Merge-таблица не будет выбрана, даже если попадает под регексп - чтобы не возникло циклов.
При выборе таблиц для чтения, сама Merge-таблица не будет выбрана, даже если попадает под регулярное выражение - чтобы не возникло циклов.
Впрочем, вы можете создать две Merge-таблицы, которые будут пытаться бесконечно читать данные друг-друга. Этого делать не нужно.
Типичный способ использования движка Merge - возможность работы с большим количеством таблиц типа TinyLog, как с одной.
@ -2567,6 +2567,7 @@ calcs - имя кластера в конфигурационном файле
Если после INSERT-а в Distributed таблицу, сервер перестал существовать или был грубо перезапущен (например, в следствие аппаратного сбоя), то записанные данные могут быть потеряны. Если в директории таблицы обнаружен повреждённый кусок данных, то он переносится в поддиректорию broken и больше не используется.
При выставлении опции max_parallel_replicas выполнение запроса распараллеливается по всем репликам внутри одного шарда. Подробнее смотрите раздел "Настройки, max_parallel_replicas".
==MergeTree==
@ -2643,12 +2644,12 @@ CollapsingMergeTree принимает дополнительный параме
Здесь Sign - столбец, содержащий -1 для "старых" значений и 1 для "новых" значений.
При слиянии, для каждой группы идущих подряд одинаковых значений первичного ключа (столбцов, по которым сортируются данные), остаётся не более одной строки со значением столбца sign_column = -1 ("отрицательной строки") и не более одиной строки со значением столбца sign_column = 1 ("положительной строки"). То есть - производится схлопывание записей из лога изменений.
При слиянии, для каждой группы идущих подряд одинаковых значений первичного ключа (столбцов, по которым сортируются данные), остаётся не более одной строки со значением столбца sign_column = -1 ("отрицательной строки") и не более одной строки со значением столбца sign_column = 1 ("положительной строки"). То есть - производится схлопывание записей из лога изменений.
Если количество положительных и отрицательных строк совпадает - то пишет первую отрицательную и последнюю положительную строку.
Если положительных на 1 больше, чем отрицательных - то пишет только последнюю положительную строку.
Если отрицательных на 1 больше, чем положительных - то пишет только первую отрицательную строку.
Иначе - логическая ошибка, и ни одна из таких строк не пишется. (Логическая ошибка может возникать, если случано один кусок лога был вставлен более одного раза. Поэтому, об ошибке всего лишь пишется в лог сервера, и слияние продолжает работать.)
Иначе - логическая ошибка, и ни одна из таких строк не пишется. (Логическая ошибка может возникать, если случайно один кусок лога был вставлен более одного раза. Поэтому, об ошибке всего лишь пишется в лог сервера, и слияние продолжает работать.)
Как видно, от схлопывания не должны меняться результаты расчётов статистик.
Изменения постепенно схлопываются так что в конце-концов, для почти каждого объекта, остаются лишь его последние значения.
@ -2868,7 +2869,7 @@ min_bytes, max_bytes - условие на количество байт в бу
Данные, вставляемые в таблицу Buffer, попадают в подчинённую таблицу в порядке, возможно отличающимся от порядка вставки, и блоками, возможно отличающимися от вставленных блоков. В связи с этим, трудно корректно использовать таблицу типа Buffer для записи в CollapsingMergeTree. Чтобы избежать проблемы, можно выставить num_layers в 1.
Если таблица назначения является реплицируемой, то при записи в таблицу Buffer будут потеряны некоторые ожидаемые свойства реплицируемых таблиц. Из-за произвольного изменения порядка строк и размеров блоков данных, перестаёт работать дедубликация данных, в результате чего исчезает возможность надёжной exactly once записи в реплицируемые таблицы.
Если таблица назначения является реплицируемой, то при записи в таблицу Buffer будут потеряны некоторые ожидаемые свойства реплицируемых таблиц. Из-за произвольного изменения порядка строк и размеров блоков данных, перестаёт работать дедупликация данных, в результате чего исчезает возможность надёжной exactly once записи в реплицируемые таблицы.
В связи с этими недостатками, таблицы типа Buffer можно рекомендовать к применению лишь в очень редких случаях.
@ -2928,7 +2929,7 @@ min_bytes, max_bytes - условие на количество байт в бу
Каждый блок данных записывается атомарно. Запрос INSERT разбивается на блоки данных размером до max_insert_block_size = 1048576 строк. То есть, если в запросе INSERT менее 1048576 строк, то он делается атомарно.
Блоки данных дедублицируются. При многократной записи одного и того же блока данных (блоков данных одинакового размера, содержащих одни и те же строчки в одном и том же порядке), блок будет записан только один раз. Это сделано для того, чтобы в случае сбоя в сети, когда клиентское приложение не может понять, были ли данные записаны в БД, можно было просто повторить запрос INSERT. При этом не имеет значения, на какую реплику будут отправлены INSERT-ы с одинаковыми данными. То есть, обеспечивается идемпотентность INSERT-ов. Это работает только для последних 100 вставленных в таблицу блоков.
Блоки данных дедуплицируются. При многократной записи одного и того же блока данных (блоков данных одинакового размера, содержащих одни и те же строчки в одном и том же порядке), блок будет записан только один раз. Это сделано для того, чтобы в случае сбоя в сети, когда клиентское приложение не может понять, были ли данные записаны в БД, можно было просто повторить запрос INSERT. При этом не имеет значения, на какую реплику будут отправлены INSERT-ы с одинаковыми данными. То есть, обеспечивается идемпотентность INSERT-ов. Это работает только для последних 100 вставленных в таблицу блоков.
При репликации, по сети передаются только исходные вставляемые данные. Дальнейшие преобразования данных (слияния) координируются и делаются на всех репликах одинаковым образом. За счёт этого минимизируется использование сети, и благодаря этому, репликация хорошо работает при расположении реплик в разных датацентрах. (Стоит заметить, что дублирование данных в разных датацентрах, по сути, является основной задачей репликации).
@ -3044,7 +3045,7 @@ min_bytes, max_bytes - условие на количество байт в бу
===Восстановление в случае потери или повреждения метаданных на ZooKeeper кластере===
Если вы продолбали ZooKeeper, то вы можете сохранить данные, переместив их в нереплицируемую таблицу, как описано в пункте выше.
Если данные в ZooKeeper оказались утеряны или повреждены, то вы можете сохранить данные, переместив их в нереплицируемую таблицу, как описано в пункте выше.
==Перешардирование==
@ -3141,7 +3142,7 @@ TO ...
==system.one==
Таблица содержит одну строку с одним столбцом dummy типа UInt8, содержащим значени 0.
Таблица содержит одну строку с одним столбцом dummy типа UInt8, содержащим значение 0.
Эта таблица используется, если в SELECT запросе не указана секция FROM.
То есть, это - аналог таблицы DUAL, которую можно найти в других СУБД.
@ -3619,7 +3620,7 @@ localhost
Целые числа пишутся в десятичной форме. Числа могут содержать лишний символ "+" в начале (игнорируется при парсинге, а при форматировании не пишется). Неотрицательные числа не могут содержать знак отрицания. При чтении допустим парсинг пустой строки, как числа ноль, или (для знаковых типов) строки, состоящей из одного минуса, как числа ноль. Числа, не помещающиеся в соответствующий тип данных, могут парсится, как некоторое другое число, без сообщения об ошибке.
Числа с плавающей запятой пишутся в десятичной форме. При этом, десятичный разделитель - точка. Поддерживается экспоненциальная запись, а также inf, +inf, -inf, nan. Запись числа с плавающей запятой может начинаться или заканчиваться на десятичную точку.
При форматрировании, возможна потеря точности чисел с плавающей запятой.
При форматировании возможна потеря точности чисел с плавающей запятой.
При парсинге, допустимо чтение не обязательно наиболее близкого к десятичной записи машинно-представимого числа.
Даты выводятся в формате YYYY-MM-DD, парсятся в том же формате, но с любыми символами в качестве разделителей.
@ -3879,7 +3880,7 @@ Extremes:
}
%%
JSON совместим с JavaScript. Для этого, дополнительно эскейпятся некоторые символы: символ прямого слеша %%/%% экранируется в виде %%\/%%; альтернативные переводы строк %%U+2028%%, %%U+2029%%, на которых ломаются некоторые браузеры, экранируются в виде <span class="inline-example">\u<i>XXXX</i></span>-последовательностей. Эскейпятся ASCII control characters: backspace, form feed, line feed, carriage return, horizontal tab в виде %%\b%%, %%\f%%, %%\n%%, %%\r%%, %%\t%% соответственно, а также остальные байты из диапазона 00-1F с помощью <span class="inline-example">\u<i>XXXX</i></span>-последовательностей. Невалидные UTF-8 последовательности заменяются на replacement character %%<25>%% и, таким образом, выводимый текст будет состоять из валидных UTF-8 последовательностей. Числа типа UInt64 и Int64, для совместимости с JavaScript, по умолчанию выводятся в двойных кавычках, чтобы они выводились без кавычек можно установить конфигурационный параметр output_format_json_quote_64bit_integers равным 0.
JSON совместим с JavaScript. Для этого, дополнительно экранируются некоторые символы: символ прямого слеша %%/%% экранируется в виде %%\/%%; альтернативные переводы строк %%U+2028%%, %%U+2029%%, на которых ломаются некоторые браузеры, экранируются в виде <span class="inline-example">\u<i>XXXX</i></span>-последовательностей. Экранируются ASCII control characters: backspace, form feed, line feed, carriage return, horizontal tab в виде %%\b%%, %%\f%%, %%\n%%, %%\r%%, %%\t%% соответственно, а также остальные байты из диапазона 00-1F с помощью <span class="inline-example">\u<i>XXXX</i></span>-последовательностей. Невалидные UTF-8 последовательности заменяются на replacement character %%<25>%% и, таким образом, выводимый текст будет состоять из валидных UTF-8 последовательностей. Числа типа UInt64 и Int64, для совместимости с JavaScript, по умолчанию выводятся в двойных кавычках, чтобы они выводились без кавычек можно установить конфигурационный параметр output_format_json_quote_64bit_integers равным 0.
%%rows%% - общее количество выведенных строчек.
%%rows_before_limit_at_least%% - не менее скольких строчек получилось бы, если бы не было LIMIT-а. Выводится только если запрос содержит LIMIT.
@ -4424,7 +4425,7 @@ END
Функции могут быть по-разному реализованы для константных и не константных аргументов (выполняется разный код). Но результат работы для константы и полноценного столбца, содержащего только одно такое же значение, должен совпадать.
===Иммутабельность===
===Неизменяемость===
Функции не могут поменять значения своих аргументов - любые изменения возвращаются в качестве результата. Соответственно, от порядка записи функций в запросе, результат вычислений отдельных функций не зависит.
@ -4564,9 +4565,9 @@ END
===greater, оператор &gt;===
===lessOrEquals, оператор &lt;====
===lessOrEquals, оператор &lt;&#61;===
===greaterOrEquals, оператор &gt;====
===greaterOrEquals, оператор &gt;&#61;===
==Логические функции==
@ -4647,7 +4648,7 @@ SELECT
===reinterpretAsString===
Функция принимает число или дату или дату-с-временем и возвращает строку, содержающую байты, представляющие соответствующее значение в host order (little endian). При этом, отбрасываются нулевые байты с конца. Например, значение 255 типа UInt32 будет строкой длины 1 байт.
Функция принимает число или дату или дату-с-временем и возвращает строку, содержащую байты, представляющие соответствующее значение в host order (little endian). При этом, отбрасываются нулевые байты с конца. Например, значение 255 типа UInt32 будет строкой длины 1 байт.
===CAST(x, t)===
@ -4692,79 +4693,79 @@ SELECT
===toYear===
- переводит дату или дату-с-временем в число типа UInt16, содержащее номер года (AD).
Переводит дату или дату-с-временем в число типа UInt16, содержащее номер года (AD).
===toMonth===
- переводит дату или дату-с-временем в число типа UInt8, содержащее номер месяца (1-12).
Переводит дату или дату-с-временем в число типа UInt8, содержащее номер месяца (1-12).
===toDayOfMonth===
- переводит дату или дату-с-временем в число типа UInt8, содержащее номер дня в месяце (1-31).
Переводит дату или дату-с-временем в число типа UInt8, содержащее номер дня в месяце (1-31).
===toDayOfWeek===
- переводит дату или дату-с-временем в число типа UInt8, содержащее номер дня в неделе (понедельник - 1, воскресенье - 7).
Переводит дату или дату-с-временем в число типа UInt8, содержащее номер дня в неделе (понедельник - 1, воскресенье - 7).
===toHour===
- переводит дату-с-временем в число типа UInt8, содержащее номер часа в сутках (0-23).
Переводит дату-с-временем в число типа UInt8, содержащее номер часа в сутках (0-23).
Функция исходит из допущения, что перевод стрелок вперёд, если осуществляется, то на час, в два часа ночи, а перевод стрелок назад, если осуществляется, то на час, в три часа ночи (что, в общем, не верно - даже в Москве два раза перевод стрелок был осуществлён в другое время).
===toMinute===
- переводит дату-с-временем в число типа UInt8, содержащее номер минуты в часе (0-59).
Переводит дату-с-временем в число типа UInt8, содержащее номер минуты в часе (0-59).
===toSecond===
- переводит дату-с-временем в число типа UInt8, содержащее номер секунды в минуте (0-59).
Переводит дату-с-временем в число типа UInt8, содержащее номер секунды в минуте (0-59).
Секунды координации не учитываются.
===toMonday===
- округляет дату или дату-с-временем вниз до ближайшего понедельника.
Округляет дату или дату-с-временем вниз до ближайшего понедельника.
Возвращается дата.
===toStartOfMonth===
- округляет дату или дату-с-временем вниз до первого дня месяца.
Округляет дату или дату-с-временем вниз до первого дня месяца.
Возвращается дата.
===toStartOfQuarter===
- округляет дату или дату-с-временем вниз до первого дня квартала.
Округляет дату или дату-с-временем вниз до первого дня квартала.
Первый день квартала - это одно из 1 января, 1 апреля, 1 июля, 1 октября.
Возвращается дата.
===toStartOfYear===
- округляет дату или дату-с-временем вниз до первого дня года.
Округляет дату или дату-с-временем вниз до первого дня года.
Возвращается дата.
===toStartOfMinute===
- округляет дату-с-временем вниз до начала минуты.
Округляет дату-с-временем вниз до начала минуты.
===toStartOfFiveMinute===
- округляет дату-с-временем вниз до начала пятиминутного интервала.
Округляет дату-с-временем вниз до начала пятиминутного интервала.
Замечание: если вам нужно округлить дату-с-временем до какого-либо другого количества секунд, минут или часов, вы можете перевести её в число с помощью функции %%toUInt32%%, затем округлить число с помощью функции %%intDiv%% и умножения, а затем перевести обратно, с помощью функции %%toDateTime%%.
===toStartOfHour===
- округляет дату-с-временем вниз до начала часа.
Округляет дату-с-временем вниз до начала часа.
===toTime===
- переводит дату-с-временем на дату начала unix-эпохи, сохраняя при этом время.
Переводит дату-с-временем на дату начала unix-эпохи, сохраняя при этом время.
===toRelativeYearNum===
- переводит дату-с-временем или дату в номер года, начиная с некоторого фиксированного момента в прошлом.
Переводит дату-с-временем или дату в номер года, начиная с некоторого фиксированного момента в прошлом.
===toRelativeMonthNum===
- переводит дату-с-временем или дату в номер месяца, начиная с некоторого фиксированного момента в прошлом.
Переводит дату-с-временем или дату в номер месяца, начиная с некоторого фиксированного момента в прошлом.
===toRelativeWeekNum===
- переводит дату-с-временем или дату в номер недели, начиная с некоторого фиксированного момента в прошлом.
Переводит дату-с-временем или дату в номер недели, начиная с некоторого фиксированного момента в прошлом.
===toRelativeDayNum===
- переводит дату-с-временем или дату в номер дня, начиная с некоторого фиксированного момента в прошлом.
Переводит дату-с-временем или дату в номер дня, начиная с некоторого фиксированного момента в прошлом.
===toRelativeHourNum===
- переводит дату-с-временем в номер часа, начиная с некоторого фиксированного момента в прошлом.
Переводит дату-с-временем в номер часа, начиная с некоторого фиксированного момента в прошлом.
===toRelativeMinuteNum===
- переводит дату-с-временем в номер минуты, начиная с некоторого фиксированного момента в прошлом.
Переводит дату-с-временем в номер минуты, начиная с некоторого фиксированного момента в прошлом.
===toRelativeSecondNum===
- переводит дату-с-временем в номер секунды, начиная с некоторого фиксированного момента в прошлом.
Переводит дату-с-временем в номер секунды, начиная с некоторого фиксированного момента в прошлом.
===now===
Принимает ноль аргументов и возвращает текущее время на один из моментов выполнения запроса.
@ -4779,11 +4780,11 @@ SELECT
Делает то же самое, что today() - 1.
===timeSlot===
- округляет время до получаса.
Округляет время до получаса.
Эта функция является специфичной для Яндекс.Метрики, так как пол часа - минимальное время, для которого, если соседние по времени хиты одного посетителя на одном счётчике отстоят друг от друга строго более, чем на это время, визит может быть разбит на два визита. То есть, кортежи (номер счётчика, идентификатор посетителя, тайм-слот) могут использоваться для поиска хитов, входящий в соответствующий визит.
===timeSlots(StartTime, Duration)===
- для интервала времени, начинающегося в StartTime и продолжающегося Duration секунд, возвращает массив моментов времени, состоящий из округлений вниз до получаса точек из этого интервала.
Для интервала времени, начинающегося в StartTime и продолжающегося Duration секунд, возвращает массив моментов времени, состоящий из округлений вниз до получаса точек из этого интервала.
Например, %%timeSlots(toDateTime('2012-01-01 12:20:00'), toUInt32(600)) = [toDateTime('2012-01-01 12:00:00'), toDateTime('2012-01-01 12:30:00')]%%.
Это нужно для поиска хитов, входящих в соответствующий визит.
@ -4791,54 +4792,54 @@ SELECT
==Функции для работы со строками==
===empty===
- возвращает 1 для пустой строки, и 0 для непустой строки.
Возвращает 1 для пустой строки, и 0 для непустой строки.
Тип результата - UInt8.
Строка считается непустой, если содержит хотя бы один байт, пусть даже это пробел или нулевой байт.
Функция также работает для массивов.
===notEmpty===
- возвращает 0 для пустой строки, и 1 для непустой строки.
Возвращает 0 для пустой строки, и 1 для непустой строки.
Тип результата - UInt8.
Функция также работает для массивов.
===length===
- возвращает длину строки в байтах (не символах, не кодовых точках).
Возвращает длину строки в байтах (не символах, не кодовых точках).
Тип результата - UInt64.
Функция также работает для массивов.
===lengthUTF8===
- возвращает длину строки в кодовых точках Unicode (не символах), при допущении, что строка содержит набор байт, являющийся текстом в кодировке UTF-8. Если допущение не выполнено - то возвращает какой-нибудь результат (не кидает исключение).
Возвращает длину строки в кодовых точках Unicode (не символах), при допущении, что строка содержит набор байт, являющийся текстом в кодировке UTF-8. Если допущение не выполнено - то возвращает какой-нибудь результат (не кидает исключение).
Тип результата - UInt64.
===lower===
- переводит ASCII-символы латиницы в строке в нижний регистр.
Переводит ASCII-символы латиницы в строке в нижний регистр.
===upper===
- переводит ASCII-символы латиницы в строке в верхний регистр.
Переводит ASCII-символы латиницы в строке в верхний регистр.
===lowerUTF8===
- переводит строку в нижний регистр, при допущении, что строка содержит набор байт, представляющий текст в кодировке UTF-8.
Переводит строку в нижний регистр, при допущении, что строка содержит набор байт, представляющий текст в кодировке UTF-8.
Не учитывает язык. То есть, для турецкого языка, результат может быть не совсем верным.
Если длина UTF-8 последовательности байт различна для верхнего и нижнего регистра кодовой точки, то для этой кодовой точки, результат работы может быть некорректным.
Если строка содержит набор байт, не являющийся UTF-8, то поведение не определено.
===upperUTF8===
- переводит строку в верхний регистр, при допущении, что строка содержит набор байт, представляющий текст в кодировке UTF-8.
Переводит строку в верхний регистр, при допущении, что строка содержит набор байт, представляющий текст в кодировке UTF-8.
Не учитывает язык. То есть, для турецкого языка, результат может быть не совсем верным.
Если длина UTF-8 последовательности байт различна для верхнего и нижнего регистра кодовой точки, то для этой кодовой точки, результат работы может быть некорректным.
Если строка содержит набор байт, не являющийся UTF-8, то поведение не определено.
===reverse===
- разворачивает строку (как последовательность байт).
Разворачивает строку (как последовательность байт).
===reverseUTF8===
- разворачивает последовательность кодовых точек Unicode, при допущении, что строка содержит набор байт, представляющий текст в кодировке UTF-8. Иначе - что-то делает (не кидает исключение).
Разворачивает последовательность кодовых точек Unicode, при допущении, что строка содержит набор байт, представляющий текст в кодировке UTF-8. Иначе - что-то делает (не кидает исключение).
===concat(s1, s2, ...)===
- склеивает строки, перечисленные в аргументах, без разделителей.
Склеивает строки, перечисленные в аргументах, без разделителей.
===substring(s, offset, length)===
- возвращает подстроку, начиная с байта по индексу offset, длины length байт. Индексация символов - начиная с единицы (как в стандартном SQL). Аргументы offset и length должны быть константами.
Возвращает подстроку, начиная с байта по индексу offset, длины length байт. Индексация символов - начиная с единицы (как в стандартном SQL). Аргументы offset и length должны быть константами.
===substringUTF8(s, offset, length)===
Так же, как substring, но для кодовых точек Unicode. Работает при допущении, что строка содержит набор байт, представляющий текст в кодировке UTF-8. Если допущение не выполнено - то возвращает какой-нибудь результат (не кидает исключение).
@ -4846,6 +4847,8 @@ SELECT
===appendTrailingCharIfAbsent(s, c)===
Если строка %%s%% непустая и не содержит символ %%c%% на конце, то добавляет символ %%c%% в конец.
===convertCharset(s, from, to)===
Возвращает сконвертированную из кодировки from в кодировку to строку s.
==Функции поиска в строках==
@ -4853,14 +4856,16 @@ SELECT
Во всех функциях, подстрока для поиска или регулярное выражение, должно быть константой.
===position(haystack, needle)===
- поиск подстроки needle в строке haystack.
Поиск подстроки needle в строке haystack.
Возвращает позицию (в байтах) найденной подстроки, начиная с 1, или 0, если подстрока не найдена.
Есть также функция positionCaseInsensitive.
===positionUTF8(haystack, needle)===
Так же, как position, но позиция возвращается в кодовых точках Unicode. Работает при допущении, что строка содержит набор байт, представляющий текст в кодировке UTF-8. Если допущение не выполнено - то возвращает какой-нибудь результат (не кидает исключение).
Есть также функция positionCaseInsensitiveUTF8.
===match(haystack, pattern)===
- проверка строки на соответствие регулярному выражению pattern. Регулярное выражение re2.
Проверка строки на соответствие регулярному выражению pattern. Регулярное выражение re2.
Возвращает 0 (если не соответствует) или 1 (если соответствует).
Обратите внимание, что для экранирования в регулярном выражении, используется символ %%\%% (обратный слеш). Этот же символ используется для экранирования в строковых литералах. Поэтому, чтобы экранировать символ в регулярном выражении, необходимо написать в строковом литерале %%\\%% (два обратных слеша).
@ -4875,7 +4880,7 @@ SELECT
Извлечение всех фрагментов строки по регулярному выражению. Если haystack не соответствует регулярному выражению pattern, то возвращается пустая строка. Возвращается массив строк, состоящий из всех соответствий регулярному выражению. В остальном, поведение аналогично функции extract (по прежнему, вынимается первый subpattern, или всё выражение, если subpattern-а нет).
===like(haystack, pattern), оператор haystack LIKE pattern===
- проверка строки на соответствие простому регулярному выражению.
Проверка строки на соответствие простому регулярному выражению.
Регулярное выражение может содержать метасимволы %%%%% и %%_%%.
%%%%% обозначает любое количество любых байт (в том числе, нулевое количество символов).
%%_%% обозначает один любой байт.
@ -4959,17 +4964,17 @@ SELECT replaceRegexpAll('Hello, World!', '^', 'here: ') AS res
==Функции по работе с массивами==
===empty===
- возвращает 1 для пустого массива, и 0 для непустого массива.
Возвращает 1 для пустого массива, и 0 для непустого массива.
Тип результата - UInt8.
Функция также работает для строк.
===notEmpty===
- возвращает 0 для пустого массива, и 1 для непустого массива.
Возвращает 0 для пустого массива, и 1 для непустого массива.
Тип результата - UInt8.
Функция также работает для строк.
===length===
- возвращает количество элементов в массиве.
Возвращает количество элементов в массиве.
Тип результата - UInt64.
Функция также работает для строк.
@ -4980,17 +4985,20 @@ SELECT replaceRegexpAll('Hello, World!', '^', 'here: ') AS res
===emptyArrayString===
Принимает ноль аргументов и возвращает пустой массив соответствующего типа.
===emptyArrayToSingle===
Принимает пустой массив и возвращает массив из одного элемента, равного значению по умолчанию.
===range(N)===
- возвращает массив чисел от 0 до N-1.
Возвращает массив чисел от 0 до N-1.
На всякий случай, если на блок данных, создаются массивы суммарной длины больше 100 000 000 элементов, то кидается исключение.
===array(x1, ...), оператор [x1, ...]===
- создаёт массив из аргументов функции.
Создаёт массив из аргументов функции.
Аргументы должны быть константами и иметь типы, для которых есть наименьший общий тип. Должен быть передан хотя бы один аргумент, так как иначе непонятно, какого типа создавать массив. То есть, с помощью этой функции невозможно создать пустой массив (для этого используйте функции emptyArray*, описанные выше).
Возвращает результат типа Array(T), где T - наименьший общий тип от переданных аргументов.
===arrayElement(arr, n), оператор arr[n]===
- достать элемент с индексом n из массива arr.
Достаёт элемент с индексом n из массива arr.
n должен быть любым целочисленным типом.
Индексы в массиве начинаются с единицы.
Поддерживаются отрицательные индексы - в этом случае, будет выбран соответствующий по номеру элемент с конца. Например, arr[-1] - последний элемент массива.
@ -5000,18 +5008,18 @@ n должен быть любым целочисленным типом.
- иначе, возвращается некоторое значение по умолчанию (0 для чисел, пустая строка для строк и т. п.).
===has(arr, elem)===
- проверка наличия элемента elem в массиве arr.
Проверяет наличие элемента elem в массиве arr.
Возвращает 0, если элемента в массиве нет, или 1, если есть.
elem должен быть константой.
===indexOf(arr, x)===
- возвращает индекс элемента x (начиная с 1), если он есть в массиве, или 0, если его нет.
Возвращает индекс элемента x (начиная с 1), если он есть в массиве, или 0, если его нет.
===countEqual(arr, x)===
- возвращает количество элементов массива, равных x. Эквивалентно <span class="inline-example">arrayCount(elem -> elem = x, arr)</span>.
Возвращает количество элементов массива, равных x. Эквивалентно <span class="inline-example">arrayCount(elem -> elem = x, arr)</span>.
===arrayEnumerate(arr)===
- возаращает массив %%[1, 2, 3, ..., length(arr)]%%
Возвращает массив %%[1, 2, 3, ..., length(arr)]%%
Эта функция обычно используется совместно с ARRAY JOIN. Она позволяет, после применения ARRAY JOIN, посчитать что-либо только один раз для каждого массива. Пример:
@ -5048,7 +5056,7 @@ WHERE (CounterID = 160656) AND notEmpty(GoalsReached)
Также эта функция может быть использована в функциях высшего порядка. Например, с её помощью можно достать индексы массива для элементов, удовлетворяющих некоторому условию.
===arrayEnumerateUniq(arr, ...)===
- возаращает массив, такого же размера, как исходный, где для каждого элемента указано, какой он по счету среди элементов с таким же значением.
Возвращает массив, такого же размера, как исходный, где для каждого элемента указано, какой он по счету среди элементов с таким же значением.
Например: %%arrayEnumerateUniq([10, 20, 10, 30]) = [1, 1, 2, 1]%%.
Эта функция полезна при использовании ARRAY JOIN и агрегации по элементам массива. Пример:
@ -5096,7 +5104,7 @@ SELECT arrayEnumerateUniq([1, 1, 1, 2, 2, 2], [1, 1, 2, 1, 1, 2]) AS res
Это нужно при использовании ARRAY JOIN с вложенной структурой данных и затем агрегации по нескольким элементам этой структуры.
===arrayJoin(arr)===
- особенная функция. Смотрите раздел "Функция arrayJoin".
Особенная функция. Смотрите раздел "Функция arrayJoin".
==Функции высшего порядка==
@ -5161,20 +5169,20 @@ SELECT
==Функции разбиения и слияния строк и массивов==
===splitByChar(separator, s)===
- разбивает строку на подстроки, используя в качестве разделителя separator.
Разбивает строку на подстроки, используя в качестве разделителя separator.
separator должен быть константной строкой из ровно одного символа.
Возвращается массив выделенных подстрок. Могут выделяться пустые подстроки, если разделитель идёт в начале или в конце строки, или если идёт более одного разделителя подряд.
===splitByString(separator, s)===
- то же самое, но использует строку из нескольких символов в качестве разделителя. Строка должна быть непустой.
То же самое, но использует строку из нескольких символов в качестве разделителя. Строка должна быть непустой.
===arrayStringConcat(arr[, separator])===
- склеивает строки, перечисленные в массиве, с разделителем separator.
Склеивает строки, перечисленные в массиве, с разделителем separator.
separator - необязательный параметр, константная строка, по умолчанию равен пустой строке.
Возвращается строка.
===alphaTokens(s)===
- выделяет подстроки из подряд идущих байт из диапазонов a-z и A-Z.
Выделяет подстроки из подряд идущих байт из диапазонов a-z и A-Z.
Возвращается массив выделенных подстрок.
@ -5187,59 +5195,59 @@ separator - необязательный параметр, константна
Если в URL-е нет ничего похожего, то возвращается пустая строка.
<h4>protocol</h4>
- выделить протокол. Примеры: http, ftp, mailto, magnet...
Возвращает протокол. Примеры: http, ftp, mailto, magnet...
<h4>domain</h4>
- выделить домен.
Возвращает домен.
<h4>domainWithoutWWW</h4>
- выделить домен, удалив не более одного 'www.' с начала, если есть.
Возвращает домен, удалив не более одного 'www.' с начала, если есть.
<h4>topLevelDomain</h4>
- выделить домен верхнего уровня. Пример: .ru.
Возвращает домен верхнего уровня. Пример: .ru.
<h4>firstSignificantSubdomain</h4>
- выделить "первый существенный поддомен". Это понятие является нестандартным и специфично для Яндекс.Метрики.
Возвращает "первый существенный поддомен". Это понятие является нестандартным и специфично для Яндекс.Метрики.
Первый существенный поддомен - это домен второго уровня, если он не равен одному из com, net, org, co, или домен третьего уровня, иначе.
Например, firstSignificantSubdomain('https://news.yandex.ru/') = 'yandex', firstSignificantSubdomain('https://news.yandex.com.tr/') = 'yandex'.
Список "несущественных" доменов второго уровня и другие детали реализации могут изменяться в будущем.
<h4>cutToFirstSignificantSubdomain</h4>
- выделяет часть домена, включающую поддомены верхнего уровня до "первого существенного поддомена" (см. выше).
Возвращает часть домена, включающую поддомены верхнего уровня до "первого существенного поддомена" (см. выше).
Например, cutToFirstSignificantSubdomain('https://news.yandex.com.tr/') = 'yandex.com.tr'.
<h4>path</h4>
- выделить путь. Пример: /top/news.html
Путь не включает в себя query-string.
Возвращает путь. Пример: /top/news.html
Путь не включает в себя query string.
<h4>pathFull</h4>
- то же самое, но включая query-string и fragment. Пример: /top/news.html?page=2#comments
То же самое, но включая query string и fragment. Пример: /top/news.html?page=2#comments
<h4>queryString</h4>
- выделить query-string. Пример: page=1&amp;lr=213.
Возвращает query-string. Пример: page=1&amp;lr=213.
query-string не включает в себя начальный знак вопроса, а также # и всё, что после #.
<h4>fragment</h4>
- выделить fragment identifier.
Возвращает fragment identifier.
fragment не включает в себя начальный символ решётки.
<h4>queryStringAndFragment</h4>
- выделить query-string и fragment identifier. Пример: страница=1#29390.
Возвращает query string и fragment identifier. Пример: страница=1#29390.
<h4>extractURLParameter(URL, name)</h4>
- достать значение параметра name в URL, если такой есть; или пустую строку, иначе; если параметров с таким именем много - вернуть первый попавшийся. Функция работает при допущении, что имя параметра закодировано в URL в точности таким же образом, что и в переданном аргументе.
Возвращает значение параметра name в URL, если такой есть; или пустую строку, иначе; если параметров с таким именем много - вернуть первый попавшийся. Функция работает при допущении, что имя параметра закодировано в URL в точности таким же образом, что и в переданном аргументе.
<h4>extractURLParameters(URL)</h4>
- получить массив строк вида name=value, соответствующих параметрам URL. Значения никак не декодируются.
Возвращает массив строк вида name=value, соответствующих параметрам URL. Значения никак не декодируются.
<h4>extractURLParameterNames(URL)</h4>
- получить массив строк вида name, соответствующих именам параметров URL. Значения никак не декодируются.
Возвращает массив строк вида name, соответствующих именам параметров URL. Значения никак не декодируются.
<h4>URLHierarchy(URL)</h4>
- получить массив, содержащий URL, обрезанный с конца по символам %%/%%, %%?%% в пути и query-string. Подряд идущие символы-разделители считаются за один. Резка производится в позиции после всех подряд идущих символов-разделителей. Пример:
Возвращает массив, содержащий URL, обрезанный с конца по символам %%/%%, %%?%% в пути и query-string. Подряд идущие символы-разделители считаются за один. Резка производится в позиции после всех подряд идущих символов-разделителей. Пример:
<h4>URLPathHierarchy(URL)</h4>
- то же самое, но без протокола и хоста в результате. Элемент / (корень) не включается. Пример:
То же самое, но без протокола и хоста в результате. Элемент / (корень) не включается. Пример:
Функция используется для реализации древовидных отчётов по URL в Яндекс.Метрике.
@ -5256,19 +5264,19 @@ URLPathHierarchy('https://example.com/browse/CONV-6788') =
Если в URL-е нет ничего похожего, то URL остаётся без изменений.
<h4>cutWWW</h4>
- удалить не более одного 'www.' с начала домена URL-а, если есть.
Удаляет не более одного 'www.' с начала домена URL-а, если есть.
<h4>cutQueryString</h4>
- удалить query-string. Знак вопроса тоже удаляется.
Удаляет query string. Знак вопроса тоже удаляется.
<h4>cutFragment</h4>
- удалить fragment identifier. Символ решётки тоже удаляется.
Удаляет fragment identifier. Символ решётки тоже удаляется.
<h4>cutQueryStringAndFragment</h4>
- удалить query-string и fragment identifier. Знак вопроса и символ решётки тоже удаляются.
Удаляет query string и fragment identifier. Знак вопроса и символ решётки тоже удаляются.
<h4>cutURLParameter(URL, name)</h4>
- удалить параметр URL с именем name, если такой есть. Функция работает при допущении, что имя параметра закодировано в URL в точности таким же образом, что и в переданном аргументе.
Удаляет параметр URL с именем name, если такой есть. Функция работает при допущении, что имя параметра закодировано в URL в точности таким же образом, что и в переданном аргументе.
==Функции для работы с IP-адресами==
@ -5381,11 +5389,11 @@ HEX может быть в любом регистре.
Этот аргумент нужен только для того, чтобы предотвратить склейку одинаковых выражений - чтобы две разные записи одной функции возвращали разные столбцы, с разными случайными числами.
===rand===
- возвращает псевдослучайное число типа UInt32, равномерно распределённое среди всех чисел типа UInt32.
Возвращает псевдослучайное число типа UInt32, равномерно распределённое среди всех чисел типа UInt32.
Используется linear congruential generator.
===rand64===
- возвращает псевдослучайное число типа UInt64, равномерно распределённое среди всех чисел типа UInt64.
Возвращает псевдослучайное число типа UInt64, равномерно распределённое среди всех чисел типа UInt64.
Используется linear congruential generator.
@ -5394,46 +5402,46 @@ HEX может быть в любом регистре.
Функции хэширования могут использоваться для детерминированного псевдослучайного разбрасывания элементов.
===halfMD5===
- вычисляет MD5 от строки. Затем берёт первые 8 байт от хэша и интерпретирует их как UInt64 в big endian.
Вычисляет MD5 от строки. Затем берёт первые 8 байт от хэша и интерпретирует их как UInt64 в big endian.
Принимает аргумент типа String. Возвращает UInt64.
Функция работает достаточно медленно (5 миллионов коротких строк в секунду на одном процессорном ядре).
Если вам не нужен конкретно MD5, то используйте вместо этого функцию sipHash64.
===MD5===
- вычисляет MD5 от строки и возвращает полученный набор байт в виде FixedString(16).
Вычисляет MD5 от строки и возвращает полученный набор байт в виде FixedString(16).
Если вам не нужен конкретно MD5, а нужен неплохой криптографический 128-битный хэш, то используйте вместо этого функцию sipHash128.
Если вы хотите получить такой же результат, как выдаёт утилита md5sum, напишите %%lower(hex(MD5(s)))%%.
===sipHash64===
- вычисляет SipHash от строки.
Вычисляет SipHash от строки.
Принимает аргумент типа String. Возвращает UInt64.
SipHash - криптографическая хэш-функция. Работает быстрее чем MD5 не менее чем в 3 раза.
Подробнее смотрите по ссылке: <a href="https://131002.net/siphash/">https://131002.net/siphash/</a>
===sipHash128===
- вычисляет SipHash от строки.
Вычисляет SipHash от строки.
Принимает аргумент типа String. Возвращает FixedString(16).
Отличается от sipHash64 тем, что финальный xor-folding состояния делается только до 128 бит.
===cityHash64===
- вычисляет CityHash64 от строки или похожую хэш-функцию для произвольного количества аргументов произвольного типа.
Вычисляет CityHash64 от строки или похожую хэш-функцию для произвольного количества аргументов произвольного типа.
Если аргумент имеет тип String, то используется CityHash. Это быстрая некриптографическая хэш-функция неплохого качества для строк.
Если аргумент имеет другой тип, то используется implementation specific быстрая некриптографическая хэш-функция неплохого качества.
Если передано несколько аргументов, то функция вычисляется по тем же правилам, с помощью комбинации по цепочке с использованием комбинатора из CityHash.
Например, так вы можете вычислить чексумму всей таблицы с точностью до порядка строк: %%SELECT sum(cityHash64(*)) FROM table%%.
===intHash32===
- вычисляет 32-битный хэш-код от целого числа любого типа.
Вычисляет 32-битный хэш-код от целого числа любого типа.
Это сравнительно быстрая некриптографическая хэш-функция среднего качества для чисел.
===intHash64===
- вычисляет 64-битный хэш-код от целого числа любого типа.
Вычисляет 64-битный хэш-код от целого числа любого типа.
Работает быстрее, чем intHash32. Качество среднее.
===SHA1===
===SHA224===
===SHA256===
- вычисляет SHA-1, SHA-224, SHA-256 от строки и возвращает полученный набор байт в виде FixedString(20), FixedString(28), FixedString(32).
Вычисляет SHA-1, SHA-224, SHA-256 от строки и возвращает полученный набор байт в виде FixedString(20), FixedString(28), FixedString(32).
Функция работает достаточно медленно (SHA-1 - примерно 5 миллионов коротких строк в секунду на одном процессорном ядре, SHA-224 и SHA-256 - примерно 2.2 миллионов).
Рекомендуется использовать эти функции лишь в тех случаях, когда вам нужна конкретная хэш-функция и вы не можете её выбрать.
Даже в этих случаях, рекомендуется применять функцию оффлайн - заранее вычисляя значения при вставке в таблицу, вместо того, чтобы применять её при SELECT-ах.
@ -5447,10 +5455,10 @@ URLHash(s, N) - вычислить хэш от строки до N-го уров
==Функции кодирования==
===hex===
Принимает строку, число, дату или дату-с-временем. Возвращает строку, содержащую шестнадцатеричное представление аргумента. Используются заглавные буквы A-F. Не используются префиксы %%0x%% и суффиксы %%h%%. Для строк просто все байты кодируются в виде двух шестнадцатиричных цифр. Числа выводятся в big endian ("человеческом") формате. Для чисел вырезаются старшие нули, но только по целым байтам. Например, %%hex(1) = '01'%%. Даты кодируются как число дней с начала unix-эпохи. Даты-с-временем кодируются как число секунд с начала unix-эпохи.
Принимает строку, число, дату или дату-с-временем. Возвращает строку, содержащую шестнадцатеричное представление аргумента. Используются заглавные буквы A-F. Не используются префиксы %%0x%% и суффиксы %%h%%. Для строк просто все байты кодируются в виде двух шестнадцатеричных цифр. Числа выводятся в big endian ("человеческом") формате. Для чисел вырезаются старшие нули, но только по целым байтам. Например, %%hex(1) = '01'%%. Даты кодируются как число дней с начала unix-эпохи. Даты-с-временем кодируются как число секунд с начала unix-эпохи.
===unhex(str)===
Принимает строку, содержащую произвольное количество шестнадцатиричных цифр, и возвращает строку, содержащую соответствующие байты. Поддерживаются как строчные, так и заглавные буквы A-F. Число шестнадцатиричных цифр не обязано быть чётным. Если оно нечётное - последняя цифра интерпретируется как младшая половинка байта 00-0F. Если строка-аргумент содержит что-либо кроме шестнадцатиричных цифр, то будет возвращён какой-либо implementation-defined результат (не кидается исключение).
Принимает строку, содержащую произвольное количество шестнадцатеричных цифр, и возвращает строку, содержащую соответствующие байты. Поддерживаются как строчные, так и заглавные буквы A-F. Число шестнадцатеричных цифр не обязано быть чётным. Если оно нечётное - последняя цифра интерпретируется как младшая половинка байта 00-0F. Если строка-аргумент содержит что-либо кроме шестнадцатеричных цифр, то будет возвращён какой-либо implementation-defined результат (не кидается исключение).
Если вы хотите преобразовать результат в число, то вы можете использовать функции reverse и reinterpretAs<i>Type</i>.
===bitmaskToList(num)===
@ -5463,7 +5471,7 @@ URLHash(s, N) - вычислить хэш от строки до N-го уров
==Функции округления==
===floor(x[, N])===
Вернуть наибольшее круглое число, которое меньше или равно, чем x.
Возвращает наибольшее круглое число, которое меньше или равно, чем x.
Круглым называется число, кратное <span class="inline-example">1 / 10<sup>N</sup></span> или ближайшее к нему число соответствующего типа данных, если <span class="inline-example">1 / 10<sup>N</sup></span> не представимо точно.
N - целочисленная константа, не обязательный параметр. По умолчанию - ноль, что означает - округлять до целого числа.
N может быть отрицательным.
@ -5473,11 +5481,11 @@ x - любой числовой тип. Результат - число того
В случае переполнения при округлении (например, %%floor(-128, -1)%%), возвращается implementation specific результат.
===ceil(x[, N])===
Вернуть наименьшее круглое число, которое больше или равно, чем x.
Возвращает наименьшее круглое число, которое больше или равно, чем x.
В остальном, аналогично функции floor, см. выше.
===round(x[, N])===
Вернуть ближайшее к num круглое число, которое может быть меньше или больше или равно x.
Возвращает ближайшее к num круглое число, которое может быть меньше или больше или равно x.
Если x находится посередине от ближайших круглых чисел, то возвращается какое-либо одно из них (implementation specific).
Число -0. может считаться или не считаться круглым (implementation specific).
В остальном, аналогично функциям floor и ceil, см. выше.
@ -5801,7 +5809,7 @@ id должен иметь тип UInt64.
===visitParamExtractString(params, name)===
Распарсить строку в двойных кавычках. Значение разэскейпливается. Если разэскейпить не удалось, то возвращается пустая строка. Примеры:
Распарсить строку в двойных кавычках. У значения убирается экранирование. Если убрать экранированные символы не удалось, то возвращается пустая строка. Примеры:
%%visitParamExtractString('{"abc":"\\n\\u0000"}', 'abc') = '\n\0'%%
%%visitParamExtractString('{"abc":"\\u263a"}', 'abc') = '☺'%%
%%visitParamExtractString('{"abc":"\\u263"}', 'abc') = ''%%
@ -5817,12 +5825,12 @@ id должен иметь тип UInt64.
===tuple(x, y, ...), оператор (x, y, ...)===
- функция, позволяющая сгруппировать несколько столбцов.
Функция, позволяющая сгруппировать несколько столбцов.
Для столбцов, имеющих типы T1, T2, ... возвращает кортеж типа Tuple(T1, T2, ...), содержащий эти столбцы. Выполнение функции ничего не стоит.
Кортежи обычно используются как промежуточное значение в качестве аргумента операторов IN, или для создания списка формальных параметров лямбда-функций. Кортежи не могут быть записаны в таблицу.
===tupleElement(tuple, n), оператор x.N===
- функция, позволяющая достать столбец из кортежа.
Функция, позволяющая достать столбец из кортежа.
N - индекс столбца начиная с 1. N должно быть константой. N должно быть целым строго положительным числом не большим размера кортежа.
Выполнение функции ничего не стоит.
@ -5830,25 +5838,25 @@ N - индекс столбца начиная с 1. N должно быть к
==Прочие функции==
===hostName()===
- возвращает строку - имя хоста, на котором эта функция была выполнена. При распределённой обработке запроса, это будет имя хоста удалённого сервера, если функция выполняется на удалённом сервере.
Возвращает строку - имя хоста, на котором эта функция была выполнена. При распределённой обработке запроса, это будет имя хоста удалённого сервера, если функция выполняется на удалённом сервере.
===visibleWidth(x)===
- вычисляет приблизительную ширину при выводе значения в текстовом (tab-separated) виде на консоль.
Вычисляет приблизительную ширину при выводе значения в текстовом (tab-separated) виде на консоль.
Функция используется системой для реализации Pretty форматов.
===toTypeName(x)===
- получить имя типа. Возвращает строку, содержащую имя типа переданного аргумента.
Возвращает строку, содержащую имя типа переданного аргумента.
===blockSize()===
- получить размер блока.
Получить размер блока.
В ClickHouse выполнение запроса всегда идёт по блокам (наборам кусочков столбцов). Функция позволяет получить размер блока, для которого её вызвали.
===materialize(x)===
- превратить константу в полноценный столбец, содержащий только одно значение.
Превращает константу в полноценный столбец, содержащий только одно значение.
В ClickHouse полноценные столбцы и константы представлены в памяти по-разному. Функции по-разному работают для аргументов-констант и обычных аргументов (выполняется разный код), хотя результат почти всегда должен быть одинаковым. Эта функция предназначена для отладки такого поведения.
===ignore(...)===
- функция, принимающая любые аргументы, и всегда возвращающая 0.
Принимает любые аргументы, всегда возвращает 0.
При этом, аргумент всё равно вычисляется. Это может использоваться для бенчмарков.
===sleep(seconds)===
@ -5871,7 +5879,7 @@ N - индекс столбца начиная с 1. N должно быть к
Принимает константные строки - имя базы данных, имя таблицы и название столбца. Возвращает константное выражение типа UInt8, равное 1,
если есть столбец, иначе 0.
Функция кидает исключение, если таблица не существует.
Для элементов вложенной структуры данных функция проверяет сушествование столбца. Для самой же вложенной структуры данных функция возвращает 0.
Для элементов вложенной структуры данных функция проверяет существование столбца. Для самой же вложенной структуры данных функция возвращает 0.
===bar===
Позволяет построить unicode-art диаграмму.
@ -5994,6 +6002,79 @@ LIMIT 10
└────────────────┴─────────┘
%%
===formatReadableSize(x)===
Принимает размер (число байт). Возвращает округленный размер с суффиксом (KiB, MiB и т.д.) в виде строки.
Пример:
%%
SELECT
arrayJoin([1, 1024, 1024*1024, 192851925]) AS filesize_bytes,
formatReadableSize(filesize_bytes) AS filesize
┌─filesize_bytes─┬─filesize───┐
│ 1 │ 1.00 B │
│ 1024 │ 1.00 KiB │
│ 1048576 │ 1.00 MiB │
│ 192851925 │ 183.92 MiB │
└────────────────┴────────────┘
%%
===least(a, b)===
Возвращает наименьшее значение из a и b.
===greatest(a, b)===
Возвращает наибольшее значение из a и b.
===uptime()===
Возвращает аптайм сервера в секундах.
===version()===
Возвращает версию сервера в виде строки.
===rowNumberInAllBlocks()===
Возвращает порядковый номер строки в блоке данных. Функция учитывает только задействованные блоки данных.
===runningDifference(x)===
Считает разницу между последовательными значениями строк в блоке данных.
Возвращает 0 для первой строки и разницу с предыдущей строкой для каждой последующей строки.
Результат функции зависит от затронутых блоков данных и порядка данных в блоке.
Если сделать подзапрос с ORDER BY и вызывать функцию извне подзапроса, можно будет получить ожидаемый результат.
Пример:
%%
SELECT
EventID,
EventTime,
runningDifference(EventTime) AS delta
FROM
(
SELECT
EventID,
EventTime
FROM events
WHERE EventDate = '2016-11-24'
ORDER BY EventTime ASC
LIMIT 5
)
┌─EventID─┬───────────EventTime─┬─delta─┐
│ 1106 │ 2016-11-24 00:00:04 │ 0 │
│ 1107 │ 2016-11-24 00:00:05 │ 1 │
│ 1108 │ 2016-11-24 00:00:05 │ 0 │
│ 1109 │ 2016-11-24 00:00:09 │ 4 │
│ 1110 │ 2016-11-24 00:00:10 │ 1 │
└─────────┴─────────────────────┴───────┘
%%
==Функция arrayJoin==
@ -6418,7 +6499,7 @@ regions_hierarchy*.txt: TabSeparated (без заголовка), столбцы
regions_names_*.txt: TabSeparated (без заголовка), столбцы:
- идентификатор региона (UInt32);
- имя региона (String) - не может содержать табы или переводы строк, даже заэскейпленные.
- имя региона (String) - не может содержать табы или переводы строк, даже экранированные.
Для хранения в оперативке используется плоский массив. Поэтому, идентификаторы не должны быть больше миллиона.
@ -6630,7 +6711,7 @@ id рекламодателя дата начала действия скид
dictGetT('dict_name', 'attr_name', id, date)
Функция достаёт значение для данного id и для диапазона дат, в который входит переданная дата. Если не найден id или для найденного id не найден диапазон, то возвращается значение по-умолчанию для словаря.
Функция достаёт значение для данного id и для диапазона дат, в который входит переданная дата. Если не найден id или для найденного id не найден диапазон, то возвращается значение по умолчанию для словаря.
Если есть перекрывающиеся диапазоны, то можно использовать любой подходящий.
@ -6910,6 +6991,13 @@ dictGetT('dict_name', 'attr_name', id, date)
Если равно 1 - сэмплирование по умолчанию не делается.
==max_parallel_replicas==
Максимальное количество используемых реплик каждого шарда при выполнении запроса.
Для консистентности (чтобы получить разные части одного и того же разбиения), эта опция работает только при заданном ключе сэмплирования.
Отставание реплик не контролируется.
==compile==
Включить компиляцию запросов. По умолчанию - 0 (выключено).
@ -6919,7 +7007,7 @@ dictGetT('dict_name', 'attr_name', id, date)
==min_count_to_compile==
После скольки раз, когда скомпилированный кусок кода мог пригодиться, выполнить его компиляцию. по умолчанию, 3.
После скольких раз, когда скомпилированный кусок кода мог пригодиться, выполнить его компиляцию. По умолчанию - 3.
В случае, если значение равно нулю, то компиляция выполняется синхронно, и запрос будет ждать окончания процесса компиляции перед продолжением выполнения. Это можно использовать для тестирования, иначе используйте значения, начиная с 1. Как правило, компиляция занимает по времени около 5-10 секунд.
В случае, если значение равно 1 или больше, компиляция выполняется асинхронно, в отдельном потоке. При готовности результата, он сразу же будет использован, в том числе, уже выполняющимися в данный момент запросами.
@ -7225,10 +7313,10 @@ SET profile = 'web'
Каждый элемент списка имеет одну из следующих форм:
&lt;ip&gt; IP-адрес или маска подсети. Например, 222.111.222.3 или 10.0.0.1/8 или 2a02:6b8::3 или 2a02:6b8::3/64.
&lt;host&gt; Имя хоста. Например: example01. Для проверки делается DNS-запрос, и все полученные адреса сравниваются с адресом клиента.
&lt;host_regexp&gt; Регексп для имён хостов. Например, ^example\d\d-\d\d-\d\.yandex\.ru$
Для проверки, для адреса клиента делается DNS PTR-запрос и к результату применяется регексп.
&lt;host_regexp&gt; Регулярное выражение для имён хостов. Например, ^example\d\d-\d\d-\d\.yandex\.ru$
Для проверки, для адреса клиента делается DNS PTR-запрос и к результату применяется регулярное выражение.
Потом для результата PTR-запроса делается снова DNS-запрос, и все полученные адреса сравниваются с адресом клиента.
Настоятельно рекомендуется, чтобы регексп заканчивался на \.yandex\.ru$.
Настоятельно рекомендуется, чтобы регулярное выражение заканчивалось на \.yandex\.ru$.
Если вы устанавливаете ClickHouse самостоятельно, укажите здесь:
&lt;networks&gt;

View File

@ -59,37 +59,21 @@ add_library (common
${REVISIONFILE}
)
# TESTIRT-3687 DISABLE_LIBTCMALLOC - when testing for memory leaks, disable libtcmalloc
IF($ENV{DISABLE_LIBTCMALLOC})
message(STATUS "Disabling libtcmalloc for valgrind better analysis")
ELSE($ENV{DISABLE_LIBTCMALLOC})
IF($ENV{DEBUG_LIBTCMALLOC})
message(STATUS "Link libtcmalloc_minimal_debug for testing")
SET(MALLOC_LIBRARIES libtcmalloc_minimal_debug.a)
ELSE($ENV{DEBUG_LIBTCMALLOC})
message(STATUS "Link libtcmalloc_minimal")
SET(MALLOC_LIBRARIES tcmalloc_minimal_internal)
ENDIF($ENV{DEBUG_LIBTCMALLOC})
ENDIF($ENV{DISABLE_LIBTCMALLOC})
if (APPLE)
SET(RT_LIBRARIES "apple_rt")
else()
SET(RT_LIBRARIES "librt.a")
endif()
set (GLIB_HINTS "/usr/local/opt/glib/lib")
find_library (GLIB_LIB libglib-2.0.a HINTS ${GLIB_HINTS})
if (APPLE)
set (INTL_HINTS "/usr/local/opt/gettext/lib")
find_library (INTL_LIB libintl.a HINTS ${INTL_HINTS})
find_library (CORE_FOUNDATION_LIB CoreFoundation)
find_library (CARBON_LIB Carbon)
set (GLIB_LIBS ${GLIB_LIB} ${INTL_LIB} libiconv.a ${CORE_FOUNDATION_LIB} ${CARBON_LIB})
# When testing for memory leaks, disable libtcmalloc.
if (ENABLE_LIBTCMALLOC)
if (DEBUG_LIBTCMALLOC)
message (STATUS "Link libtcmalloc_minimal_debug for testing")
set (MALLOC_LIBRARIES libtcmalloc_minimal_debug.a)
else ()
message (STATUS "Link libtcmalloc_minimal")
set (MALLOC_LIBRARIES tcmalloc_minimal_internal)
endif ()
else ()
set (GLIB_LIBS ${GLIB_LIB})
message (STATUS "Disabling libtcmalloc for valgrind better analysis")
endif ()
include (${ClickHouse_SOURCE_DIR}/cmake/find_glib.cmake)
target_link_libraries (
common
pocoext
@ -99,6 +83,6 @@ target_link_libraries (
${ICU_LIBS}
${RT_LIBRARIES})
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (src/tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -5,6 +5,10 @@ function tag_filter {
# Get last revision number
function get_revision {
BASEDIR=`dirname "$0"`
CURDIR=`pwd`
cd ${BASEDIR}
git fetch --tags
git tag | tag_filter | tail -1 | sed 's/^v1\.1\.\(.*\)-testing$/\1/'
cd ${CURDIR}
}

View File

@ -8,18 +8,12 @@ add_executable (multi_version multi_version.cpp)
add_executable (json_test json_test.cpp)
add_executable (strong_typedef strong_typedef.cpp)
if (APPLE)
set (RT_LIBRARIES "")
else()
set (RT_LIBRARIES "rt")
endif()
target_link_libraries (date_lut_init common ${ICU_LIBS} dl)
target_link_libraries (date_lut2 common ${ICU_LIBS} dl)
target_link_libraries (date_lut3 common ${ICU_LIBS} dl)
target_link_libraries (date_lut4 common ${ICU_LIBS} dl)
target_link_libraries (multi_version dbms ${BOOST_SYSTEM_LIB} ${RT_LIBRARIES})
target_link_libraries (json_test dbms ${BOOST_SYSTEM_LIB} ${RT_LIBRARIES})
target_link_libraries (multi_version dbms ${Boost_SYSTEM_LIBRARY} ${RT_LIBRARIES})
target_link_libraries (json_test dbms ${Boost_SYSTEM_LIBRARY} ${RT_LIBRARIES})
target_link_libraries (strong_typedef common)
add_check (json_test)

View File

@ -26,20 +26,27 @@ add_library (mysqlxx
add_dependencies (mysqlxx common)
set (MYSQL_HINTS "/usr/local/opt/mysql/lib")
find_library (MYSQLCLIENT_LIB libmysqlclient.a HINTS ${MYSQL_HINTS})
set (OUR_MYSQLCLIENT_LIB ${CMAKE_CURRENT_BINARY_DIR}/libmysqlclient.a)
set (Z_HINTS "/usr/local/opt/zlib/lib")
if (USE_STATIC_LIBRARIES)
find_library (Z_LIB libz.a HINTS ${Z_HINTS})
else ()
find_library (Z_LIB z HINTS ${Z_HINTS})
endif ()
target_link_libraries (mysqlxx common ${OUR_MYSQLCLIENT_LIB} ${OPENSSL_LIBS} libz.a dl)
add_custom_command(
OUTPUT ${OUR_MYSQLCLIENT_LIB}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/patch.sh ${MYSQLCLIENT_LIB} ${OUR_MYSQLCLIENT_LIB}
if (USE_STATIC_LIBRARIES)
set (MYSQLCLIENT_LIB ${CMAKE_CURRENT_BINARY_DIR}/libmysqlclient.a)
target_link_libraries (mysqlxx common ${MYSQLCLIENT_LIB} ${OPENSSL_LIBRARIES} ${Z_LIB} dl)
add_custom_command (
OUTPUT ${MYSQLCLIENT_LIB}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/patch.sh ${STATIC_MYSQLCLIENT_LIB} ${MYSQLCLIENT_LIB}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Patching mysqlclient library.")
add_custom_target(our_mysql_client DEPENDS ${OUR_MYSQLCLIENT_LIB})
add_dependencies(mysqlxx our_mysql_client)
add_custom_target (our_mysql_client DEPENDS ${MYSQLCLIENT_LIB})
add_dependencies (mysqlxx our_mysql_client)
endif ()
if (TESTS)
target_link_libraries (mysqlxx common ${MYSQLCLIENT_LIB} ${OPENSSL_LIBRARIES} ${Z_LIB} dl)
if (ENABLE_TESTS)
add_subdirectory (src/tests)
endif (TESTS)
endif (ENABLE_TESTS)

View File

@ -1,7 +1,7 @@
#pragma once
#include <sstream>
#include <mysql/mysql.h>
#include <mysql.h>
#include <Poco/Exception.h>

View File

@ -3,7 +3,7 @@
#include <list>
#include <memory>
#include <mysql/mysqld_error.h>
#include <mysqld_error.h>
#include <Poco/Util/Application.h>
#include <Poco/Util/LayeredConfiguration.h>

View File

@ -1,7 +1,7 @@
#pragma once
#include <string>
#include <mysql/mysql.h>
#include <mysql.h>
#include <Poco/Types.h>
#include <common/LocalDate.h>

View File

@ -3,10 +3,6 @@ include_directories (${CMAKE_CURRENT_BINARY_DIR})
add_executable (mysqlxx_test mysqlxx_test.cpp)
add_executable (failover failover.cpp)
if (APPLE)
SET(RT_LIBRARIES "")
else()
SET(RT_LIBRARIES "rt")
endif()
target_link_libraries (mysqlxx_test mysqlxx dbms ${RT_LIBRARIES})
target_link_libraries (failover mysqlxx PocoUtil PocoFoundation)
target_link_rt_by_force (failover)

View File

@ -1,3 +1,3 @@
IF(TESTS)
if (ENABLE_TESTS)
add_subdirectory (tests)
ENDIF(TESTS)
endif (ENABLE_TESTS)

View File

@ -51,7 +51,7 @@ make_control "$CONTROL" "$DAEMONS"
gen_changelog "$REVISION" "$CHDATE" "$AUTHOR" "$CHLOG" "$DAEMONS"
# Build (only binary packages).
debuild -e DAEMONS="${DAEMONS}" -e DISABLE_MONGODB -e GLIBC_COMPATIBILITY -e CC -e CXX -b ${DEBUILD_NOSIGN_OPTIONS}
debuild -e DAEMONS="${DAEMONS}" -e ENABLE_MONGODB -e GLIBC_COMPATIBILITY -e CC -e CXX -b ${DEBUILD_NOSIGN_OPTIONS}
if [[ $STANDALONE != 'yes' ]]
then

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