mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-10-07 00:50:56 +00:00
Merge branch 'master' of github.com:yandex/ClickHouse
This commit is contained in:
commit
178fe43f81
@ -1 +1,4 @@
|
|||||||
* Настройка `enable_optimize_predicate_expression` выключена по-умолчанию.
|
* Настройка `enable_optimize_predicate_expression` выключена по-умолчанию.
|
||||||
|
|
||||||
|
### Улучшения:
|
||||||
|
* Файлы *-preprocessed.xml записываются в директорию с данными (/var/lib/clickhouse/preprocessed_configs). Для /etc/clickhouse-server больше не нужен +w для пользователя clickhouse. Для удобства создан симлинк /var/lib/clickhouse/preprocessed_configs -> /etc/clickhouse-server/preprocessed
|
||||||
|
@ -236,7 +236,7 @@ include(GNUInstallDirs)
|
|||||||
|
|
||||||
include (cmake/find_ssl.cmake)
|
include (cmake/find_ssl.cmake)
|
||||||
include (cmake/lib_name.cmake)
|
include (cmake/lib_name.cmake)
|
||||||
include (cmake/find_icu4c.cmake)
|
include (cmake/find_icu.cmake)
|
||||||
include (cmake/find_boost.cmake)
|
include (cmake/find_boost.cmake)
|
||||||
include (cmake/find_zlib.cmake)
|
include (cmake/find_zlib.cmake)
|
||||||
include (cmake/find_zstd.cmake)
|
include (cmake/find_zstd.cmake)
|
||||||
|
@ -9,3 +9,4 @@ ClickHouse is an open-source column-oriented database management system that all
|
|||||||
* [Documentation](https://clickhouse.yandex/docs/en/) provides more in-depth information.
|
* [Documentation](https://clickhouse.yandex/docs/en/) provides more in-depth information.
|
||||||
* [Blog](https://clickhouse.yandex/blog/en/) contains various ClickHouse-related articles, as well as announces and reports about events.
|
* [Blog](https://clickhouse.yandex/blog/en/) contains various ClickHouse-related articles, as well as announces and reports about events.
|
||||||
* [Contacts](https://clickhouse.yandex/#contacts) can help to get your questions answered if there are any.
|
* [Contacts](https://clickhouse.yandex/#contacts) can help to get your questions answered if there are any.
|
||||||
|
* You can also [fill this form](https://forms.yandex.com/surveys/meet-yandex-clickhouse-team/) to meet Yandex ClickHouse team in person.
|
||||||
|
394
cmake/Modules/FindICU.cmake
Normal file
394
cmake/Modules/FindICU.cmake
Normal file
@ -0,0 +1,394 @@
|
|||||||
|
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||||
|
# file Copyright.txt or https://cmake.org/licensing for details.
|
||||||
|
|
||||||
|
#.rst:
|
||||||
|
# FindICU
|
||||||
|
# -------
|
||||||
|
#
|
||||||
|
# Find the International Components for Unicode (ICU) libraries and
|
||||||
|
# programs.
|
||||||
|
#
|
||||||
|
# This module supports multiple components.
|
||||||
|
# Components can include any of: ``data``, ``i18n``, ``io``, ``le``,
|
||||||
|
# ``lx``, ``test``, ``tu`` and ``uc``.
|
||||||
|
#
|
||||||
|
# Note that on Windows ``data`` is named ``dt`` and ``i18n`` is named
|
||||||
|
# ``in``; any of the names may be used, and the appropriate
|
||||||
|
# platform-specific library name will be automatically selected.
|
||||||
|
#
|
||||||
|
# This module reports information about the ICU installation in
|
||||||
|
# several variables. General variables::
|
||||||
|
#
|
||||||
|
# ICU_VERSION - ICU release version
|
||||||
|
# ICU_FOUND - true if the main programs and libraries were found
|
||||||
|
# ICU_LIBRARIES - component libraries to be linked
|
||||||
|
# ICU_INCLUDE_DIRS - the directories containing the ICU headers
|
||||||
|
#
|
||||||
|
# Imported targets::
|
||||||
|
#
|
||||||
|
# ICU::<C>
|
||||||
|
#
|
||||||
|
# Where ``<C>`` is the name of an ICU component, for example
|
||||||
|
# ``ICU::i18n``.
|
||||||
|
#
|
||||||
|
# ICU programs are reported in::
|
||||||
|
#
|
||||||
|
# ICU_GENCNVAL_EXECUTABLE - path to gencnval executable
|
||||||
|
# ICU_ICUINFO_EXECUTABLE - path to icuinfo executable
|
||||||
|
# ICU_GENBRK_EXECUTABLE - path to genbrk executable
|
||||||
|
# ICU_ICU-CONFIG_EXECUTABLE - path to icu-config executable
|
||||||
|
# ICU_GENRB_EXECUTABLE - path to genrb executable
|
||||||
|
# ICU_GENDICT_EXECUTABLE - path to gendict executable
|
||||||
|
# ICU_DERB_EXECUTABLE - path to derb executable
|
||||||
|
# ICU_PKGDATA_EXECUTABLE - path to pkgdata executable
|
||||||
|
# ICU_UCONV_EXECUTABLE - path to uconv executable
|
||||||
|
# ICU_GENCFU_EXECUTABLE - path to gencfu executable
|
||||||
|
# ICU_MAKECONV_EXECUTABLE - path to makeconv executable
|
||||||
|
# ICU_GENNORM2_EXECUTABLE - path to gennorm2 executable
|
||||||
|
# ICU_GENCCODE_EXECUTABLE - path to genccode executable
|
||||||
|
# ICU_GENSPREP_EXECUTABLE - path to gensprep executable
|
||||||
|
# ICU_ICUPKG_EXECUTABLE - path to icupkg executable
|
||||||
|
# ICU_GENCMN_EXECUTABLE - path to gencmn executable
|
||||||
|
#
|
||||||
|
# ICU component libraries are reported in::
|
||||||
|
#
|
||||||
|
# ICU_<C>_FOUND - ON if component was found
|
||||||
|
# ICU_<C>_LIBRARIES - libraries for component
|
||||||
|
#
|
||||||
|
# ICU datafiles are reported in::
|
||||||
|
#
|
||||||
|
# ICU_MAKEFILE_INC - Makefile.inc
|
||||||
|
# ICU_PKGDATA_INC - pkgdata.inc
|
||||||
|
#
|
||||||
|
# Note that ``<C>`` is the uppercased name of the component.
|
||||||
|
#
|
||||||
|
# This module reads hints about search results from::
|
||||||
|
#
|
||||||
|
# ICU_ROOT - the root of the ICU installation
|
||||||
|
#
|
||||||
|
# The environment variable ``ICU_ROOT`` may also be used; the
|
||||||
|
# ICU_ROOT variable takes precedence.
|
||||||
|
#
|
||||||
|
# The following cache variables may also be set::
|
||||||
|
#
|
||||||
|
# ICU_<P>_EXECUTABLE - the path to executable <P>
|
||||||
|
# ICU_INCLUDE_DIR - the directory containing the ICU headers
|
||||||
|
# ICU_<C>_LIBRARY - the library for component <C>
|
||||||
|
#
|
||||||
|
# .. note::
|
||||||
|
#
|
||||||
|
# In most cases none of the above variables will require setting,
|
||||||
|
# unless multiple ICU versions are available and a specific version
|
||||||
|
# is required.
|
||||||
|
#
|
||||||
|
# Other variables one may set to control this module are::
|
||||||
|
#
|
||||||
|
# ICU_DEBUG - Set to ON to enable debug output from FindICU.
|
||||||
|
|
||||||
|
# Written by Roger Leigh <rleigh@codelibre.net>
|
||||||
|
|
||||||
|
set(icu_programs
|
||||||
|
gencnval
|
||||||
|
icuinfo
|
||||||
|
genbrk
|
||||||
|
icu-config
|
||||||
|
genrb
|
||||||
|
gendict
|
||||||
|
derb
|
||||||
|
pkgdata
|
||||||
|
uconv
|
||||||
|
gencfu
|
||||||
|
makeconv
|
||||||
|
gennorm2
|
||||||
|
genccode
|
||||||
|
gensprep
|
||||||
|
icupkg
|
||||||
|
gencmn)
|
||||||
|
|
||||||
|
set(icu_data
|
||||||
|
Makefile.inc
|
||||||
|
pkgdata.inc)
|
||||||
|
|
||||||
|
# The ICU checks are contained in a function due to the large number
|
||||||
|
# of temporary variables needed.
|
||||||
|
function(_ICU_FIND)
|
||||||
|
# Set up search paths, taking compiler into account. Search ICU_ROOT,
|
||||||
|
# with ICU_ROOT in the environment as a fallback if unset.
|
||||||
|
if(ICU_ROOT)
|
||||||
|
list(APPEND icu_roots "${ICU_ROOT}")
|
||||||
|
else()
|
||||||
|
if(NOT "$ENV{ICU_ROOT}" STREQUAL "")
|
||||||
|
file(TO_CMAKE_PATH "$ENV{ICU_ROOT}" NATIVE_PATH)
|
||||||
|
list(APPEND icu_roots "${NATIVE_PATH}")
|
||||||
|
set(ICU_ROOT "${NATIVE_PATH}"
|
||||||
|
CACHE PATH "Location of the ICU installation" FORCE)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Find include directory
|
||||||
|
list(APPEND icu_include_suffixes "include")
|
||||||
|
find_path(ICU_INCLUDE_DIR
|
||||||
|
NAMES "unicode/utypes.h"
|
||||||
|
HINTS ${icu_roots}
|
||||||
|
PATH_SUFFIXES ${icu_include_suffixes}
|
||||||
|
DOC "ICU include directory")
|
||||||
|
set(ICU_INCLUDE_DIR "${ICU_INCLUDE_DIR}" PARENT_SCOPE)
|
||||||
|
|
||||||
|
# Get version
|
||||||
|
if(ICU_INCLUDE_DIR AND EXISTS "${ICU_INCLUDE_DIR}/unicode/uvernum.h")
|
||||||
|
file(STRINGS "${ICU_INCLUDE_DIR}/unicode/uvernum.h" icu_header_str
|
||||||
|
REGEX "^#define[\t ]+U_ICU_VERSION[\t ]+\".*\".*")
|
||||||
|
|
||||||
|
string(REGEX REPLACE "^#define[\t ]+U_ICU_VERSION[\t ]+\"([^ \\n]*)\".*"
|
||||||
|
"\\1" icu_version_string "${icu_header_str}")
|
||||||
|
set(ICU_VERSION "${icu_version_string}")
|
||||||
|
set(ICU_VERSION "${icu_version_string}" PARENT_SCOPE)
|
||||||
|
unset(icu_header_str)
|
||||||
|
unset(icu_version_string)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
|
||||||
|
# 64-bit binary directory
|
||||||
|
set(_bin64 "bin64")
|
||||||
|
# 64-bit library directory
|
||||||
|
set(_lib64 "lib64")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
|
||||||
|
# Find all ICU programs
|
||||||
|
list(APPEND icu_binary_suffixes "${_bin64}" "bin")
|
||||||
|
foreach(program ${icu_programs})
|
||||||
|
string(TOUPPER "${program}" program_upcase)
|
||||||
|
set(cache_var "ICU_${program_upcase}_EXECUTABLE")
|
||||||
|
set(program_var "ICU_${program_upcase}_EXECUTABLE")
|
||||||
|
find_program("${cache_var}" "${program}"
|
||||||
|
HINTS ${icu_roots}
|
||||||
|
PATH_SUFFIXES ${icu_binary_suffixes}
|
||||||
|
DOC "ICU ${program} executable")
|
||||||
|
mark_as_advanced(cache_var)
|
||||||
|
set("${program_var}" "${${cache_var}}" PARENT_SCOPE)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
# Find all ICU libraries
|
||||||
|
list(APPEND icu_library_suffixes "${_lib64}" "lib")
|
||||||
|
set(ICU_REQUIRED_LIBS_FOUND ON)
|
||||||
|
foreach(component ${ICU_FIND_COMPONENTS})
|
||||||
|
string(TOUPPER "${component}" component_upcase)
|
||||||
|
set(component_cache "ICU_${component_upcase}_LIBRARY")
|
||||||
|
set(component_cache_release "${component_cache}_RELEASE")
|
||||||
|
set(component_cache_debug "${component_cache}_DEBUG")
|
||||||
|
set(component_found "${component_upcase}_FOUND")
|
||||||
|
set(component_libnames "icu${component}")
|
||||||
|
set(component_debug_libnames "icu${component}d")
|
||||||
|
|
||||||
|
# Special case deliberate library naming mismatches between Unix
|
||||||
|
# and Windows builds
|
||||||
|
unset(component_libnames)
|
||||||
|
unset(component_debug_libnames)
|
||||||
|
list(APPEND component_libnames "icu${component}")
|
||||||
|
list(APPEND component_debug_libnames "icu${component}d")
|
||||||
|
if(component STREQUAL "data")
|
||||||
|
list(APPEND component_libnames "icudt")
|
||||||
|
# Note there is no debug variant at present
|
||||||
|
list(APPEND component_debug_libnames "icudtd")
|
||||||
|
endif()
|
||||||
|
if(component STREQUAL "dt")
|
||||||
|
list(APPEND component_libnames "icudata")
|
||||||
|
# Note there is no debug variant at present
|
||||||
|
list(APPEND component_debug_libnames "icudatad")
|
||||||
|
endif()
|
||||||
|
if(component STREQUAL "i18n")
|
||||||
|
list(APPEND component_libnames "icuin")
|
||||||
|
list(APPEND component_debug_libnames "icuind")
|
||||||
|
endif()
|
||||||
|
if(component STREQUAL "in")
|
||||||
|
list(APPEND component_libnames "icui18n")
|
||||||
|
list(APPEND component_debug_libnames "icui18nd")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
find_library("${component_cache_release}" ${component_libnames}
|
||||||
|
HINTS ${icu_roots}
|
||||||
|
PATH_SUFFIXES ${icu_library_suffixes}
|
||||||
|
DOC "ICU ${component} library (release)")
|
||||||
|
find_library("${component_cache_debug}" ${component_debug_libnames}
|
||||||
|
HINTS ${icu_roots}
|
||||||
|
PATH_SUFFIXES ${icu_library_suffixes}
|
||||||
|
DOC "ICU ${component} library (debug)")
|
||||||
|
include(SelectLibraryConfigurations)
|
||||||
|
select_library_configurations(ICU_${component_upcase})
|
||||||
|
mark_as_advanced("${component_cache_release}" "${component_cache_debug}")
|
||||||
|
if(${component_cache})
|
||||||
|
set("${component_found}" ON)
|
||||||
|
list(APPEND ICU_LIBRARY "${${component_cache}}")
|
||||||
|
endif()
|
||||||
|
mark_as_advanced("${component_found}")
|
||||||
|
set("${component_cache}" "${${component_cache}}" PARENT_SCOPE)
|
||||||
|
set("${component_found}" "${${component_found}}" PARENT_SCOPE)
|
||||||
|
if(${component_found})
|
||||||
|
if (ICU_FIND_REQUIRED_${component})
|
||||||
|
list(APPEND ICU_LIBS_FOUND "${component} (required)")
|
||||||
|
else()
|
||||||
|
list(APPEND ICU_LIBS_FOUND "${component} (optional)")
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
if (ICU_FIND_REQUIRED_${component})
|
||||||
|
set(ICU_REQUIRED_LIBS_FOUND OFF)
|
||||||
|
list(APPEND ICU_LIBS_NOTFOUND "${component} (required)")
|
||||||
|
else()
|
||||||
|
list(APPEND ICU_LIBS_NOTFOUND "${component} (optional)")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
|
set(_ICU_REQUIRED_LIBS_FOUND "${ICU_REQUIRED_LIBS_FOUND}" PARENT_SCOPE)
|
||||||
|
set(ICU_LIBRARY "${ICU_LIBRARY}" PARENT_SCOPE)
|
||||||
|
|
||||||
|
# Find all ICU data files
|
||||||
|
if(CMAKE_LIBRARY_ARCHITECTURE)
|
||||||
|
list(APPEND icu_data_suffixes
|
||||||
|
"${_lib64}/${CMAKE_LIBRARY_ARCHITECTURE}/icu/${ICU_VERSION}"
|
||||||
|
"lib/${CMAKE_LIBRARY_ARCHITECTURE}/icu/${ICU_VERSION}"
|
||||||
|
"${_lib64}/${CMAKE_LIBRARY_ARCHITECTURE}/icu"
|
||||||
|
"lib/${CMAKE_LIBRARY_ARCHITECTURE}/icu")
|
||||||
|
endif()
|
||||||
|
list(APPEND icu_data_suffixes
|
||||||
|
"${_lib64}/icu/${ICU_VERSION}"
|
||||||
|
"lib/icu/${ICU_VERSION}"
|
||||||
|
"${_lib64}/icu"
|
||||||
|
"lib/icu")
|
||||||
|
foreach(data ${icu_data})
|
||||||
|
string(TOUPPER "${data}" data_upcase)
|
||||||
|
string(REPLACE "." "_" data_upcase "${data_upcase}")
|
||||||
|
set(cache_var "ICU_${data_upcase}")
|
||||||
|
set(data_var "ICU_${data_upcase}")
|
||||||
|
find_file("${cache_var}" "${data}"
|
||||||
|
HINTS ${icu_roots}
|
||||||
|
PATH_SUFFIXES ${icu_data_suffixes}
|
||||||
|
DOC "ICU ${data} data file")
|
||||||
|
mark_as_advanced(cache_var)
|
||||||
|
set("${data_var}" "${${cache_var}}" PARENT_SCOPE)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
if(NOT ICU_FIND_QUIETLY)
|
||||||
|
if(ICU_LIBS_FOUND)
|
||||||
|
message(STATUS "Found the following ICU libraries:")
|
||||||
|
foreach(found ${ICU_LIBS_FOUND})
|
||||||
|
message(STATUS " ${found}")
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
if(ICU_LIBS_NOTFOUND)
|
||||||
|
message(STATUS "The following ICU libraries were not found:")
|
||||||
|
foreach(notfound ${ICU_LIBS_NOTFOUND})
|
||||||
|
message(STATUS " ${notfound}")
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ICU_DEBUG)
|
||||||
|
message(STATUS "--------FindICU.cmake search debug--------")
|
||||||
|
message(STATUS "ICU binary path search order: ${icu_roots}")
|
||||||
|
message(STATUS "ICU include path search order: ${icu_roots}")
|
||||||
|
message(STATUS "ICU library path search order: ${icu_roots}")
|
||||||
|
message(STATUS "----------------")
|
||||||
|
endif()
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
_ICU_FIND()
|
||||||
|
|
||||||
|
include(FindPackageHandleStandardArgs)
|
||||||
|
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ICU
|
||||||
|
FOUND_VAR ICU_FOUND
|
||||||
|
REQUIRED_VARS ICU_INCLUDE_DIR
|
||||||
|
ICU_LIBRARY
|
||||||
|
_ICU_REQUIRED_LIBS_FOUND
|
||||||
|
VERSION_VAR ICU_VERSION
|
||||||
|
FAIL_MESSAGE "Failed to find all ICU components")
|
||||||
|
|
||||||
|
unset(_ICU_REQUIRED_LIBS_FOUND)
|
||||||
|
|
||||||
|
if(ICU_FOUND)
|
||||||
|
set(ICU_INCLUDE_DIRS "${ICU_INCLUDE_DIR}")
|
||||||
|
set(ICU_LIBRARIES "${ICU_LIBRARY}")
|
||||||
|
foreach(_ICU_component ${ICU_FIND_COMPONENTS})
|
||||||
|
string(TOUPPER "${_ICU_component}" _ICU_component_upcase)
|
||||||
|
set(_ICU_component_cache "ICU_${_ICU_component_upcase}_LIBRARY")
|
||||||
|
set(_ICU_component_cache_release "ICU_${_ICU_component_upcase}_LIBRARY_RELEASE")
|
||||||
|
set(_ICU_component_cache_debug "ICU_${_ICU_component_upcase}_LIBRARY_DEBUG")
|
||||||
|
set(_ICU_component_lib "ICU_${_ICU_component_upcase}_LIBRARIES")
|
||||||
|
set(_ICU_component_found "${_ICU_component_upcase}_FOUND")
|
||||||
|
set(_ICU_imported_target "ICU::${_ICU_component}")
|
||||||
|
if(${_ICU_component_found})
|
||||||
|
set("${_ICU_component_lib}" "${${_ICU_component_cache}}")
|
||||||
|
if(NOT TARGET ${_ICU_imported_target})
|
||||||
|
add_library(${_ICU_imported_target} UNKNOWN IMPORTED)
|
||||||
|
if(ICU_INCLUDE_DIR)
|
||||||
|
set_target_properties(${_ICU_imported_target} PROPERTIES
|
||||||
|
INTERFACE_INCLUDE_DIRECTORIES "${ICU_INCLUDE_DIR}")
|
||||||
|
endif()
|
||||||
|
if(EXISTS "${${_ICU_component_cache}}")
|
||||||
|
set_target_properties(${_ICU_imported_target} PROPERTIES
|
||||||
|
IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
|
||||||
|
IMPORTED_LOCATION "${${_ICU_component_cache}}")
|
||||||
|
endif()
|
||||||
|
if(EXISTS "${${_ICU_component_cache_release}}")
|
||||||
|
set_property(TARGET ${_ICU_imported_target} APPEND PROPERTY
|
||||||
|
IMPORTED_CONFIGURATIONS RELEASE)
|
||||||
|
set_target_properties(${_ICU_imported_target} PROPERTIES
|
||||||
|
IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX"
|
||||||
|
IMPORTED_LOCATION_RELEASE "${${_ICU_component_cache_release}}")
|
||||||
|
endif()
|
||||||
|
if(EXISTS "${${_ICU_component_cache_debug}}")
|
||||||
|
set_property(TARGET ${_ICU_imported_target} APPEND PROPERTY
|
||||||
|
IMPORTED_CONFIGURATIONS DEBUG)
|
||||||
|
set_target_properties(${_ICU_imported_target} PROPERTIES
|
||||||
|
IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX"
|
||||||
|
IMPORTED_LOCATION_DEBUG "${${_ICU_component_cache_debug}}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
unset(_ICU_component_upcase)
|
||||||
|
unset(_ICU_component_cache)
|
||||||
|
unset(_ICU_component_lib)
|
||||||
|
unset(_ICU_component_found)
|
||||||
|
unset(_ICU_imported_target)
|
||||||
|
endforeach()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(ICU_DEBUG)
|
||||||
|
message(STATUS "--------FindICU.cmake results debug--------")
|
||||||
|
message(STATUS "ICU found: ${ICU_FOUND}")
|
||||||
|
message(STATUS "ICU_VERSION number: ${ICU_VERSION}")
|
||||||
|
message(STATUS "ICU_ROOT directory: ${ICU_ROOT}")
|
||||||
|
message(STATUS "ICU_INCLUDE_DIR directory: ${ICU_INCLUDE_DIR}")
|
||||||
|
message(STATUS "ICU_LIBRARIES: ${ICU_LIBRARIES}")
|
||||||
|
|
||||||
|
foreach(program IN LISTS icu_programs)
|
||||||
|
string(TOUPPER "${program}" program_upcase)
|
||||||
|
set(program_lib "ICU_${program_upcase}_EXECUTABLE")
|
||||||
|
message(STATUS "${program} program: ${${program_lib}}")
|
||||||
|
unset(program_upcase)
|
||||||
|
unset(program_lib)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
foreach(data IN LISTS icu_data)
|
||||||
|
string(TOUPPER "${data}" data_upcase)
|
||||||
|
string(REPLACE "." "_" data_upcase "${data_upcase}")
|
||||||
|
set(data_lib "ICU_${data_upcase}")
|
||||||
|
message(STATUS "${data} data: ${${data_lib}}")
|
||||||
|
unset(data_upcase)
|
||||||
|
unset(data_lib)
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
foreach(component IN LISTS ICU_FIND_COMPONENTS)
|
||||||
|
string(TOUPPER "${component}" component_upcase)
|
||||||
|
set(component_lib "ICU_${component_upcase}_LIBRARIES")
|
||||||
|
set(component_found "${component_upcase}_FOUND")
|
||||||
|
message(STATUS "${component} library found: ${${component_found}}")
|
||||||
|
message(STATUS "${component} library: ${${component_lib}}")
|
||||||
|
unset(component_upcase)
|
||||||
|
unset(component_lib)
|
||||||
|
unset(component_found)
|
||||||
|
endforeach()
|
||||||
|
message(STATUS "----------------")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
unset(icu_programs)
|
@ -1,10 +0,0 @@
|
|||||||
function(generate_function_register FUNCTION_AREA)
|
|
||||||
foreach(FUNCTION IN LISTS ARGN)
|
|
||||||
configure_file (registerFunction.h.in ${FUNCTIONS_GENERATED_DIR}register${FUNCTION}.h)
|
|
||||||
configure_file (registerFunction.cpp.in ${FUNCTIONS_GENERATED_DIR}register${FUNCTION}.cpp)
|
|
||||||
set(REGISTER_HEADERS "${REGISTER_HEADERS}#include \"register${FUNCTION}.h\"\n")
|
|
||||||
set(REGISTER_FUNCTIONS "${REGISTER_FUNCTIONS} register${FUNCTION}(factory);\n")
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
configure_file (registerFunctions_area.cpp.in ${FUNCTIONS_GENERATED_DIR}registerFunctions${FUNCTION_AREA}.cpp)
|
|
||||||
endfunction()
|
|
16
cmake/find_icu.cmake
Normal file
16
cmake/find_icu.cmake
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
option (ENABLE_ICU "Enable ICU" ON)
|
||||||
|
|
||||||
|
if (ENABLE_ICU)
|
||||||
|
find_package(ICU COMPONENTS data i18n uc) # TODO: remove Modules/FindICU.cmake after cmake 3.7
|
||||||
|
#set (ICU_LIBRARIES ${ICU_I18N_LIBRARY} ${ICU_UC_LIBRARY} ${ICU_DATA_LIBRARY} CACHE STRING "")
|
||||||
|
set (ICU_LIBRARIES ICU::i18n ICU::uc ICU::data CACHE STRING "")
|
||||||
|
if (ICU_FOUND)
|
||||||
|
set(USE_ICU 1)
|
||||||
|
endif ()
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (USE_ICU)
|
||||||
|
message (STATUS "Using icu=${USE_ICU}: ${ICU_INCLUDE_DIR} : ${ICU_LIBRARIES}")
|
||||||
|
else ()
|
||||||
|
message (STATUS "Build without ICU (support for collations and charset conversion functions will be disabled)")
|
||||||
|
endif ()
|
@ -1,21 +0,0 @@
|
|||||||
option (ENABLE_ICU "Enable ICU" ON)
|
|
||||||
|
|
||||||
if (ENABLE_ICU)
|
|
||||||
set (ICU_PATHS "/usr/local/opt/icu4c/lib")
|
|
||||||
set (ICU_INCLUDE_PATHS "/usr/local/opt/icu4c/include")
|
|
||||||
find_library (ICUI18N icui18n PATHS ${ICU_PATHS})
|
|
||||||
find_library (ICUUC icuuc PATHS ${ICU_PATHS})
|
|
||||||
find_library (ICUDATA icudata PATHS ${ICU_PATHS})
|
|
||||||
set (ICU_LIBS ${ICUI18N} ${ICUUC} ${ICUDATA})
|
|
||||||
|
|
||||||
find_path (ICU_INCLUDE_DIR NAMES unicode/unistr.h PATHS ${ICU_INCLUDE_PATHS})
|
|
||||||
if (ICU_INCLUDE_DIR AND ICU_LIBS)
|
|
||||||
set(USE_ICU 1)
|
|
||||||
endif ()
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (USE_ICU)
|
|
||||||
message (STATUS "Using icu=${USE_ICU}: ${ICU_INCLUDE_DIR} : ${ICU_LIBS}")
|
|
||||||
else ()
|
|
||||||
message (STATUS "Build without ICU (support for collations and charset conversion functions will be disabled)")
|
|
||||||
endif ()
|
|
@ -242,8 +242,7 @@ endif()
|
|||||||
target_link_libraries (dbms PRIVATE ${Poco_Foundation_LIBRARY})
|
target_link_libraries (dbms PRIVATE ${Poco_Foundation_LIBRARY})
|
||||||
|
|
||||||
if (USE_ICU)
|
if (USE_ICU)
|
||||||
target_link_libraries (dbms PRIVATE ${ICU_LIBS})
|
target_link_libraries (dbms PRIVATE ${ICU_LIBRARIES})
|
||||||
target_include_directories (dbms SYSTEM PRIVATE ${ICU_INCLUDE_DIR})
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (USE_CAPNP)
|
if (USE_CAPNP)
|
||||||
|
@ -48,7 +48,7 @@ else ()
|
|||||||
link_directories (${LLVM_LIBRARY_DIRS})
|
link_directories (${LLVM_LIBRARY_DIRS})
|
||||||
endif ()
|
endif ()
|
||||||
add_executable (clickhouse main.cpp)
|
add_executable (clickhouse main.cpp)
|
||||||
target_link_libraries (clickhouse PRIVATE clickhouse_common_io)
|
target_link_libraries (clickhouse PRIVATE clickhouse_common_io string_utils)
|
||||||
target_include_directories (clickhouse BEFORE PRIVATE ${COMMON_INCLUDE_DIR})
|
target_include_directories (clickhouse BEFORE PRIVATE ${COMMON_INCLUDE_DIR})
|
||||||
target_include_directories (clickhouse PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories (clickhouse PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
@ -26,18 +26,18 @@ static void setupLogging(const std::string & log_level)
|
|||||||
static std::string extractFromConfig(
|
static std::string extractFromConfig(
|
||||||
const std::string & config_path, const std::string & key, bool process_zk_includes, bool try_get = false)
|
const std::string & config_path, const std::string & key, bool process_zk_includes, bool try_get = false)
|
||||||
{
|
{
|
||||||
ConfigProcessor processor(config_path, /* throw_on_bad_incl = */ false, /* log_to_console = */ false);
|
DB::ConfigProcessor processor(config_path, /* throw_on_bad_incl = */ false, /* log_to_console = */ false);
|
||||||
bool has_zk_includes;
|
bool has_zk_includes;
|
||||||
XMLDocumentPtr config_xml = processor.processConfig(&has_zk_includes);
|
DB::XMLDocumentPtr config_xml = processor.processConfig(&has_zk_includes);
|
||||||
if (has_zk_includes && process_zk_includes)
|
if (has_zk_includes && process_zk_includes)
|
||||||
{
|
{
|
||||||
ConfigurationPtr bootstrap_configuration(new Poco::Util::XMLConfiguration(config_xml));
|
DB::ConfigurationPtr bootstrap_configuration(new Poco::Util::XMLConfiguration(config_xml));
|
||||||
zkutil::ZooKeeperPtr zookeeper = std::make_shared<zkutil::ZooKeeper>(
|
zkutil::ZooKeeperPtr zookeeper = std::make_shared<zkutil::ZooKeeper>(
|
||||||
*bootstrap_configuration, "zookeeper");
|
*bootstrap_configuration, "zookeeper");
|
||||||
zkutil::ZooKeeperNodeCache zk_node_cache([&] { return zookeeper; });
|
zkutil::ZooKeeperNodeCache zk_node_cache([&] { return zookeeper; });
|
||||||
config_xml = processor.processConfig(&has_zk_includes, &zk_node_cache);
|
config_xml = processor.processConfig(&has_zk_includes, &zk_node_cache);
|
||||||
}
|
}
|
||||||
ConfigurationPtr configuration(new Poco::Util::XMLConfiguration(config_xml));
|
DB::ConfigurationPtr configuration(new Poco::Util::XMLConfiguration(config_xml));
|
||||||
// do not throw exception if not found
|
// do not throw exception if not found
|
||||||
if (try_get)
|
if (try_get)
|
||||||
return configuration->getString(key, "");
|
return configuration->getString(key, "");
|
||||||
|
@ -115,9 +115,11 @@ try
|
|||||||
/// Load config files if exists
|
/// Load config files if exists
|
||||||
if (config().has("config-file") || Poco::File("config.xml").exists())
|
if (config().has("config-file") || Poco::File("config.xml").exists())
|
||||||
{
|
{
|
||||||
ConfigProcessor config_processor(config().getString("config-file", "config.xml"), false, true);
|
const auto config_path = config().getString("config-file", "config.xml");
|
||||||
|
ConfigProcessor config_processor(config_path, false, true);
|
||||||
|
config_processor.setConfigPath(Poco::Path(config_path).makeParent().toString());
|
||||||
auto loaded_config = config_processor.loadConfig();
|
auto loaded_config = config_processor.loadConfig();
|
||||||
config_processor.savePreprocessedConfig(loaded_config);
|
config_processor.savePreprocessedConfig(loaded_config, loaded_config.configuration->getString("path", DBMS_DEFAULT_PATH));
|
||||||
config().add(loaded_config.configuration.duplicate(), PRIO_DEFAULT, false);
|
config().add(loaded_config.configuration.duplicate(), PRIO_DEFAULT, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,7 +350,7 @@ void LocalServer::setupUsers()
|
|||||||
const auto users_config_path = config().getString("users_config", config().getString("config-file", "config.xml"));
|
const auto users_config_path = config().getString("users_config", config().getString("config-file", "config.xml"));
|
||||||
ConfigProcessor config_processor(users_config_path);
|
ConfigProcessor config_processor(users_config_path);
|
||||||
const auto loaded_config = config_processor.loadConfig();
|
const auto loaded_config = config_processor.loadConfig();
|
||||||
config_processor.savePreprocessedConfig(loaded_config);
|
config_processor.savePreprocessedConfig(loaded_config, config().getString("path", DBMS_DEFAULT_PATH));
|
||||||
users_config = loaded_config.configuration;
|
users_config = loaded_config.configuration;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -96,7 +96,7 @@ void Server::initialize(Poco::Util::Application & self)
|
|||||||
|
|
||||||
std::string Server::getDefaultCorePath() const
|
std::string Server::getDefaultCorePath() const
|
||||||
{
|
{
|
||||||
return getCanonicalPath(config().getString("path")) + "cores";
|
return getCanonicalPath(config().getString("path", DBMS_DEFAULT_PATH)) + "cores";
|
||||||
}
|
}
|
||||||
|
|
||||||
int Server::main(const std::vector<std::string> & /*args*/)
|
int Server::main(const std::vector<std::string> & /*args*/)
|
||||||
@ -129,7 +129,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
|
|||||||
ConfigProcessor config_processor(config_path);
|
ConfigProcessor config_processor(config_path);
|
||||||
loaded_config = config_processor.loadConfigWithZooKeeperIncludes(
|
loaded_config = config_processor.loadConfigWithZooKeeperIncludes(
|
||||||
main_config_zk_node_cache, /* fallback_to_preprocessed = */ true);
|
main_config_zk_node_cache, /* fallback_to_preprocessed = */ true);
|
||||||
config_processor.savePreprocessedConfig(loaded_config);
|
config_processor.savePreprocessedConfig(loaded_config, config().getString("path", DBMS_DEFAULT_PATH));
|
||||||
config().removeConfiguration(old_configuration.get());
|
config().removeConfiguration(old_configuration.get());
|
||||||
config().add(loaded_config.configuration.duplicate(), PRIO_DEFAULT, false);
|
config().add(loaded_config.configuration.duplicate(), PRIO_DEFAULT, false);
|
||||||
}
|
}
|
||||||
@ -160,7 +160,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
std::string path = getCanonicalPath(config().getString("path"));
|
std::string path = getCanonicalPath(config().getString("path", DBMS_DEFAULT_PATH));
|
||||||
std::string default_database = config().getString("default_database", "default");
|
std::string default_database = config().getString("default_database", "default");
|
||||||
|
|
||||||
global_context->setPath(path);
|
global_context->setPath(path);
|
||||||
@ -301,6 +301,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
|
|||||||
std::string include_from_path = config().getString("include_from", "/etc/metrika.xml");
|
std::string include_from_path = config().getString("include_from", "/etc/metrika.xml");
|
||||||
auto main_config_reloader = std::make_unique<ConfigReloader>(config_path,
|
auto main_config_reloader = std::make_unique<ConfigReloader>(config_path,
|
||||||
include_from_path,
|
include_from_path,
|
||||||
|
config().getString("path", ""),
|
||||||
std::move(main_config_zk_node_cache),
|
std::move(main_config_zk_node_cache),
|
||||||
[&](ConfigurationPtr config)
|
[&](ConfigurationPtr config)
|
||||||
{
|
{
|
||||||
@ -322,6 +323,7 @@ int Server::main(const std::vector<std::string> & /*args*/)
|
|||||||
}
|
}
|
||||||
auto users_config_reloader = std::make_unique<ConfigReloader>(users_config_path,
|
auto users_config_reloader = std::make_unique<ConfigReloader>(users_config_path,
|
||||||
include_from_path,
|
include_from_path,
|
||||||
|
config().getString("path", ""),
|
||||||
zkutil::ZooKeeperNodeCache([&] { return global_context->getZooKeeper(); }),
|
zkutil::ZooKeeperNodeCache([&] { return global_context->getZooKeeper(); }),
|
||||||
[&](ConfigurationPtr config) { global_context->setUsersConfig(config); },
|
[&](ConfigurationPtr config) { global_context->setUsersConfig(config); },
|
||||||
/* already_loaded = */ false);
|
/* already_loaded = */ false);
|
||||||
|
@ -4,3 +4,5 @@
|
|||||||
#add_library(clickhouse_client ${LINK_MODE} ${clickhouse_client_headers} ${clickhouse_client_sources})
|
#add_library(clickhouse_client ${LINK_MODE} ${clickhouse_client_headers} ${clickhouse_client_sources})
|
||||||
#target_link_libraries (clickhouse_client clickhouse_common_io ${Poco_Net_LIBRARY})
|
#target_link_libraries (clickhouse_client clickhouse_common_io ${Poco_Net_LIBRARY})
|
||||||
#target_include_directories (clickhouse_client PRIVATE ${DBMS_INCLUDE_DIR})
|
#target_include_directories (clickhouse_client PRIVATE ${DBMS_INCLUDE_DIR})
|
||||||
|
|
||||||
|
add_subdirectory(tests)
|
||||||
|
@ -27,7 +27,6 @@
|
|||||||
#include <Poco/Net/SecureStreamSocket.h>
|
#include <Poco/Net/SecureStreamSocket.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
namespace CurrentMetrics
|
namespace CurrentMetrics
|
||||||
{
|
{
|
||||||
extern const Metric SendExternalTables;
|
extern const Metric SendExternalTables;
|
||||||
|
2
dbms/src/Client/tests/CMakeLists.txt
Normal file
2
dbms/src/Client/tests/CMakeLists.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
add_executable(test-connect test_connect.cpp)
|
||||||
|
target_link_libraries (test-connect dbms)
|
59
dbms/src/Client/tests/test_connect.cpp
Normal file
59
dbms/src/Client/tests/test_connect.cpp
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <Poco/Net/StreamSocket.h>
|
||||||
|
#include <Common/Exception.h>
|
||||||
|
#include <IO/ReadHelpers.h>
|
||||||
|
|
||||||
|
|
||||||
|
/** In a loop it connects to the server and immediately breaks the connection.
|
||||||
|
* Using the SO_LINGER option, we ensure that the connection is terminated by sending a RST packet (not FIN).
|
||||||
|
* This behavior causes a bug in the TCPServer implementation in the Poco library.
|
||||||
|
*/
|
||||||
|
int main(int argc, char ** argv)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
for (size_t i = 0, num_iters = argc >= 2 ? DB::parse<size_t>(argv[1]) : 1; i < num_iters; ++i)
|
||||||
|
{
|
||||||
|
std::cerr << ".";
|
||||||
|
|
||||||
|
Poco::Net::SocketAddress address("localhost", 9000);
|
||||||
|
|
||||||
|
int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
|
||||||
|
|
||||||
|
if (fd < 0)
|
||||||
|
DB::throwFromErrno("Cannot create socket", 0);
|
||||||
|
|
||||||
|
linger linger_value;
|
||||||
|
linger_value.l_onoff = 1;
|
||||||
|
linger_value.l_linger = 0;
|
||||||
|
|
||||||
|
if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_value, sizeof(linger_value)))
|
||||||
|
DB::throwFromErrno("Cannot set linger", 0);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
int res = connect(fd, address.addr(), address.length());
|
||||||
|
|
||||||
|
if (res != 0 && errno != EINPROGRESS && errno != EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
close(fd);
|
||||||
|
DB::throwFromErrno("Cannot connect", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
catch (const Poco::Exception & e)
|
||||||
|
{
|
||||||
|
std::cerr << e.displayText() << "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "\n";
|
||||||
|
}
|
||||||
|
catch (const Poco::Exception & e)
|
||||||
|
{
|
||||||
|
std::cerr << e.displayText() << "\n";
|
||||||
|
}
|
@ -259,7 +259,7 @@ void ColumnLowCardinality::getPermutation(bool reverse, size_t limit, int nan_di
|
|||||||
if (limit == 0)
|
if (limit == 0)
|
||||||
limit = size();
|
limit = size();
|
||||||
|
|
||||||
size_t unique_limit = std::min(limit, getDictionary().size());
|
size_t unique_limit = getDictionary().size();
|
||||||
Permutation unique_perm;
|
Permutation unique_perm;
|
||||||
getDictionary().getNestedColumn()->getPermutation(reverse, unique_limit, nan_direction_hint, unique_perm);
|
getDictionary().getNestedColumn()->getPermutation(reverse, unique_limit, nan_direction_hint, unique_perm);
|
||||||
|
|
||||||
|
@ -20,6 +20,11 @@
|
|||||||
|
|
||||||
using namespace Poco::XML;
|
using namespace Poco::XML;
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
/// For cutting prerpocessed path to this base
|
||||||
|
std::string main_config_path;
|
||||||
|
|
||||||
/// Extracts from a string the first encountered number consisting of at least two digits.
|
/// Extracts from a string the first encountered number consisting of at least two digits.
|
||||||
static std::string numberFromHost(const std::string & s)
|
static std::string numberFromHost(const std::string & s)
|
||||||
@ -40,13 +45,6 @@ static std::string numberFromHost(const std::string & s)
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string preprocessedConfigPath(const std::string & path)
|
|
||||||
{
|
|
||||||
Poco::Path preprocessed_path(path);
|
|
||||||
preprocessed_path.setBaseName(preprocessed_path.getBaseName() + PREPROCESSED_SUFFIX);
|
|
||||||
return preprocessed_path.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ConfigProcessor::isPreprocessedFile(const std::string & path)
|
bool ConfigProcessor::isPreprocessedFile(const std::string & path)
|
||||||
{
|
{
|
||||||
return endsWith(Poco::Path(path).getBaseName(), PREPROCESSED_SUFFIX);
|
return endsWith(Poco::Path(path).getBaseName(), PREPROCESSED_SUFFIX);
|
||||||
@ -59,7 +57,6 @@ ConfigProcessor::ConfigProcessor(
|
|||||||
bool log_to_console,
|
bool log_to_console,
|
||||||
const Substitutions & substitutions_)
|
const Substitutions & substitutions_)
|
||||||
: path(path_)
|
: path(path_)
|
||||||
, preprocessed_path(preprocessedConfigPath(path))
|
|
||||||
, throw_on_bad_incl(throw_on_bad_incl_)
|
, throw_on_bad_incl(throw_on_bad_incl_)
|
||||||
, substitutions(substitutions_)
|
, substitutions(substitutions_)
|
||||||
/// We need larger name pool to allow to support vast amount of users in users.xml files for ClickHouse.
|
/// We need larger name pool to allow to support vast amount of users in users.xml files for ClickHouse.
|
||||||
@ -522,7 +519,7 @@ ConfigProcessor::LoadedConfig ConfigProcessor::loadConfig(bool allow_zk_includes
|
|||||||
|
|
||||||
ConfigurationPtr configuration(new Poco::Util::XMLConfiguration(config_xml));
|
ConfigurationPtr configuration(new Poco::Util::XMLConfiguration(config_xml));
|
||||||
|
|
||||||
return LoadedConfig{configuration, has_zk_includes, /* loaded_from_preprocessed = */ false, config_xml};
|
return LoadedConfig{configuration, has_zk_includes, /* loaded_from_preprocessed = */ false, config_xml, path};
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigProcessor::LoadedConfig ConfigProcessor::loadConfigWithZooKeeperIncludes(
|
ConfigProcessor::LoadedConfig ConfigProcessor::loadConfigWithZooKeeperIncludes(
|
||||||
@ -556,11 +553,44 @@ ConfigProcessor::LoadedConfig ConfigProcessor::loadConfigWithZooKeeperIncludes(
|
|||||||
|
|
||||||
ConfigurationPtr configuration(new Poco::Util::XMLConfiguration(config_xml));
|
ConfigurationPtr configuration(new Poco::Util::XMLConfiguration(config_xml));
|
||||||
|
|
||||||
return LoadedConfig{configuration, has_zk_includes, !processed_successfully, config_xml};
|
return LoadedConfig{configuration, has_zk_includes, !processed_successfully, config_xml, path};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigProcessor::savePreprocessedConfig(const LoadedConfig & loaded_config)
|
void ConfigProcessor::savePreprocessedConfig(const LoadedConfig & loaded_config, std::string preprocessed_dir)
|
||||||
{
|
{
|
||||||
|
if (preprocessed_path.empty())
|
||||||
|
{
|
||||||
|
auto new_path = loaded_config.config_path;
|
||||||
|
if (new_path.substr(0, main_config_path.size()) == main_config_path)
|
||||||
|
new_path.replace(0, main_config_path.size(), "");
|
||||||
|
std::replace(new_path.begin(), new_path.end(), '/', '_');
|
||||||
|
|
||||||
|
if (preprocessed_dir.empty())
|
||||||
|
{
|
||||||
|
if (!loaded_config.configuration->has("path"))
|
||||||
|
{
|
||||||
|
// Will use current directory
|
||||||
|
auto parent_path = Poco::Path(loaded_config.config_path).makeParent();
|
||||||
|
preprocessed_dir = parent_path.toString();
|
||||||
|
Poco::Path poco_new_path(new_path);
|
||||||
|
poco_new_path.setBaseName(poco_new_path.getBaseName() + PREPROCESSED_SUFFIX);
|
||||||
|
new_path = poco_new_path.toString();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
preprocessed_dir = loaded_config.configuration->getString("path") + "/preprocessed_configs/";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
preprocessed_dir += "/preprocessed_configs/";
|
||||||
|
}
|
||||||
|
|
||||||
|
preprocessed_path = preprocessed_dir + new_path;
|
||||||
|
auto path = Poco::Path(preprocessed_path).makeParent();
|
||||||
|
if (!path.toString().empty())
|
||||||
|
Poco::File(path).createDirectories();
|
||||||
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
DOMWriter().writeNode(preprocessed_path, loaded_config.preprocessed_xml);
|
DOMWriter().writeNode(preprocessed_path, loaded_config.preprocessed_xml);
|
||||||
@ -570,3 +600,10 @@ void ConfigProcessor::savePreprocessedConfig(const LoadedConfig & loaded_config)
|
|||||||
LOG_WARNING(log, "Couldn't save preprocessed config to " << preprocessed_path << ": " << e.displayText());
|
LOG_WARNING(log, "Couldn't save preprocessed config to " << preprocessed_path << ": " << e.displayText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConfigProcessor::setConfigPath(const std::string & config_path)
|
||||||
|
{
|
||||||
|
main_config_path = config_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -24,6 +24,9 @@ namespace zkutil
|
|||||||
class ZooKeeperNodeCache;
|
class ZooKeeperNodeCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
using ConfigurationPtr = Poco::AutoPtr<Poco::Util::AbstractConfiguration>;
|
using ConfigurationPtr = Poco::AutoPtr<Poco::Util::AbstractConfiguration>;
|
||||||
using XMLDocumentPtr = Poco::AutoPtr<Poco::XML::Document>;
|
using XMLDocumentPtr = Poco::AutoPtr<Poco::XML::Document>;
|
||||||
|
|
||||||
@ -72,6 +75,7 @@ public:
|
|||||||
bool has_zk_includes;
|
bool has_zk_includes;
|
||||||
bool loaded_from_preprocessed;
|
bool loaded_from_preprocessed;
|
||||||
XMLDocumentPtr preprocessed_xml;
|
XMLDocumentPtr preprocessed_xml;
|
||||||
|
std::string config_path;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// If allow_zk_includes is true, expect that the configuration XML can contain from_zk nodes.
|
/// If allow_zk_includes is true, expect that the configuration XML can contain from_zk nodes.
|
||||||
@ -85,7 +89,12 @@ public:
|
|||||||
zkutil::ZooKeeperNodeCache & zk_node_cache,
|
zkutil::ZooKeeperNodeCache & zk_node_cache,
|
||||||
bool fallback_to_preprocessed = false);
|
bool fallback_to_preprocessed = false);
|
||||||
|
|
||||||
void savePreprocessedConfig(const LoadedConfig & loaded_config);
|
/// Save preprocessed config to specified directory.
|
||||||
|
/// If preprocessed_dir is empty - calculate from loaded_config.path + /preprocessed_configs/
|
||||||
|
void savePreprocessedConfig(const LoadedConfig & loaded_config, std::string preprocessed_dir);
|
||||||
|
|
||||||
|
/// Set path of main config.xml . It will be cutted from all configs placed to preprocessed_configs/
|
||||||
|
void setConfigPath(const std::string & config_path);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
using Files = std::vector<std::string>;
|
using Files = std::vector<std::string>;
|
||||||
@ -99,7 +108,7 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
const std::string path;
|
const std::string path;
|
||||||
const std::string preprocessed_path;
|
std::string preprocessed_path;
|
||||||
|
|
||||||
bool throw_on_bad_incl;
|
bool throw_on_bad_incl;
|
||||||
|
|
||||||
@ -127,3 +136,5 @@ private:
|
|||||||
zkutil::ZooKeeperNodeCache * zk_node_cache,
|
zkutil::ZooKeeperNodeCache * zk_node_cache,
|
||||||
std::unordered_set<std::string> & contributing_zk_paths);
|
std::unordered_set<std::string> & contributing_zk_paths);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -15,10 +15,12 @@ constexpr decltype(ConfigReloader::reload_interval) ConfigReloader::reload_inter
|
|||||||
ConfigReloader::ConfigReloader(
|
ConfigReloader::ConfigReloader(
|
||||||
const std::string & path_,
|
const std::string & path_,
|
||||||
const std::string & include_from_path_,
|
const std::string & include_from_path_,
|
||||||
|
const std::string & preprocessed_dir_,
|
||||||
zkutil::ZooKeeperNodeCache && zk_node_cache_,
|
zkutil::ZooKeeperNodeCache && zk_node_cache_,
|
||||||
Updater && updater_,
|
Updater && updater_,
|
||||||
bool already_loaded)
|
bool already_loaded)
|
||||||
: path(path_), include_from_path(include_from_path_)
|
: path(path_), include_from_path(include_from_path_)
|
||||||
|
, preprocessed_dir(preprocessed_dir_)
|
||||||
, zk_node_cache(std::move(zk_node_cache_))
|
, zk_node_cache(std::move(zk_node_cache_))
|
||||||
, updater(std::move(updater_))
|
, updater(std::move(updater_))
|
||||||
{
|
{
|
||||||
@ -98,7 +100,7 @@ void ConfigReloader::reloadIfNewer(bool force, bool throw_on_error, bool fallbac
|
|||||||
tryLogCurrentException(log, "Error loading config from `" + path + "'");
|
tryLogCurrentException(log, "Error loading config from `" + path + "'");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
config_processor.savePreprocessedConfig(loaded_config);
|
config_processor.savePreprocessedConfig(loaded_config, preprocessed_dir);
|
||||||
|
|
||||||
/** We should remember last modification time if and only if config was sucessfully loaded
|
/** We should remember last modification time if and only if config was sucessfully loaded
|
||||||
* Otherwise a race condition could occur during config files update:
|
* Otherwise a race condition could occur during config files update:
|
||||||
|
@ -33,6 +33,7 @@ public:
|
|||||||
ConfigReloader(
|
ConfigReloader(
|
||||||
const std::string & path,
|
const std::string & path,
|
||||||
const std::string & include_from_path,
|
const std::string & include_from_path,
|
||||||
|
const std::string & preprocessed_dir,
|
||||||
zkutil::ZooKeeperNodeCache && zk_node_cache,
|
zkutil::ZooKeeperNodeCache && zk_node_cache,
|
||||||
Updater && updater,
|
Updater && updater,
|
||||||
bool already_loaded);
|
bool already_loaded);
|
||||||
@ -70,6 +71,7 @@ private:
|
|||||||
|
|
||||||
std::string path;
|
std::string path;
|
||||||
std::string include_from_path;
|
std::string include_from_path;
|
||||||
|
std::string preprocessed_dir;
|
||||||
FilesChangesTracker files;
|
FilesChangesTracker files;
|
||||||
zkutil::ZooKeeperNodeCache zk_node_cache;
|
zkutil::ZooKeeperNodeCache zk_node_cache;
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@ int main(int argc, char ** argv)
|
|||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
ConfigProcessor processor(argv[1], false, true);
|
DB::ConfigProcessor processor(argv[1], false, true);
|
||||||
auto config = processor.loadConfig().configuration;
|
auto config = processor.loadConfig().configuration;
|
||||||
zkutil::ZooKeeper zk(*config, "zookeeper");
|
zkutil::ZooKeeper zk(*config, "zookeeper");
|
||||||
zkutil::EventPtr watch = std::make_shared<Poco::Event>();
|
zkutil::EventPtr watch = std::make_shared<Poco::Event>();
|
||||||
|
@ -66,6 +66,8 @@
|
|||||||
/// the number is unmotivated
|
/// the number is unmotivated
|
||||||
#define DEFAULT_COUNT_OF_HTTP_CONNECTIONS_PER_ENDPOINT 15
|
#define DEFAULT_COUNT_OF_HTTP_CONNECTIONS_PER_ENDPOINT 15
|
||||||
|
|
||||||
|
#define DBMS_DEFAULT_PATH "/var/lib/clickhouse/"
|
||||||
|
|
||||||
// more aliases: https://mailman.videolan.org/pipermail/x264-devel/2014-May/010660.html
|
// more aliases: https://mailman.videolan.org/pipermail/x264-devel/2014-May/010660.html
|
||||||
|
|
||||||
#if defined(_MSC_VER)
|
#if defined(_MSC_VER)
|
||||||
|
28
dbms/src/DataStreams/AddingDefaultBlockInputStream.cpp
Normal file
28
dbms/src/DataStreams/AddingDefaultBlockInputStream.cpp
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
#include <DataStreams/AddingDefaultBlockInputStream.h>
|
||||||
|
#include <Interpreters/addMissingDefaults.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
AddingDefaultBlockInputStream::AddingDefaultBlockInputStream(
|
||||||
|
const BlockInputStreamPtr & input_,
|
||||||
|
const Block & header_,
|
||||||
|
const ColumnDefaults & column_defaults_,
|
||||||
|
const Context & context_)
|
||||||
|
: input(input_), header(header_),
|
||||||
|
column_defaults(column_defaults_), context(context_)
|
||||||
|
{
|
||||||
|
children.emplace_back(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
Block AddingDefaultBlockInputStream::readImpl()
|
||||||
|
{
|
||||||
|
Block src = children.back()->read();
|
||||||
|
if (!src)
|
||||||
|
return src;
|
||||||
|
|
||||||
|
return addMissingDefaults(src, header.getNamesAndTypesList(), column_defaults, context);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
40
dbms/src/DataStreams/AddingDefaultBlockInputStream.h
Normal file
40
dbms/src/DataStreams/AddingDefaultBlockInputStream.h
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DataStreams/IProfilingBlockInputStream.h>
|
||||||
|
#include <Storages/ColumnDefault.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/** This stream adds three types of columns into block
|
||||||
|
* 1. Columns, that are missed inside request, but present in table without defaults (missed columns)
|
||||||
|
* 2. Columns, that are missed inside request, but present in table with defaults (columns with default values)
|
||||||
|
* 3. Columns that materialized from other columns (materialized columns)
|
||||||
|
* All three types of columns are materialized (not constants).
|
||||||
|
*/
|
||||||
|
class AddingDefaultBlockInputStream : public IProfilingBlockInputStream
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
AddingDefaultBlockInputStream(
|
||||||
|
const BlockInputStreamPtr & input_,
|
||||||
|
const Block & header_,
|
||||||
|
const ColumnDefaults & column_defaults_,
|
||||||
|
const Context & context_);
|
||||||
|
|
||||||
|
String getName() const override { return "AddingDefault"; }
|
||||||
|
Block getHeader() const override { return header; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
Block readImpl() override;
|
||||||
|
|
||||||
|
BlockInputStreamPtr input;
|
||||||
|
/// Blocks after this stream should have this structure
|
||||||
|
const Block header;
|
||||||
|
const ColumnDefaults column_defaults;
|
||||||
|
const Context & context;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -1,11 +1,5 @@
|
|||||||
#include <DataStreams/AddingDefaultBlockOutputStream.h>
|
#include <DataStreams/AddingDefaultBlockOutputStream.h>
|
||||||
|
#include <Interpreters/addMissingDefaults.h>
|
||||||
#include <Common/typeid_cast.h>
|
|
||||||
#include <DataTypes/NestedUtils.h>
|
|
||||||
#include <DataTypes/DataTypeArray.h>
|
|
||||||
#include <Columns/ColumnArray.h>
|
|
||||||
#include <Interpreters/evaluateMissingDefaults.h>
|
|
||||||
#include <Core/Block.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -13,68 +7,7 @@ namespace DB
|
|||||||
|
|
||||||
void AddingDefaultBlockOutputStream::write(const Block & block)
|
void AddingDefaultBlockOutputStream::write(const Block & block)
|
||||||
{
|
{
|
||||||
Block res;
|
output->write(addMissingDefaults(block, output_block.getNamesAndTypesList(), column_defaults, context));
|
||||||
/// We take given columns from input block
|
|
||||||
/// and missed columns without default value (default and meterialized will be computed later)
|
|
||||||
for (const auto & column : output_block)
|
|
||||||
{
|
|
||||||
if (block.has(column.name))
|
|
||||||
res.insert(block.getByName(column.name));
|
|
||||||
else if (!column_defaults.count(column.name))
|
|
||||||
res.insert(column);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds not specified default values.
|
|
||||||
size_t rows = block.rows();
|
|
||||||
|
|
||||||
/// For missing columns of nested structure, you need to create not a column of empty arrays, but a column of arrays of correct lengths.
|
|
||||||
/// First, remember the offset columns for all arrays in the block.
|
|
||||||
std::map<String, ColumnPtr> offset_columns;
|
|
||||||
|
|
||||||
for (size_t i = 0, size = block.columns(); i < size; ++i)
|
|
||||||
{
|
|
||||||
const auto & elem = block.getByPosition(i);
|
|
||||||
|
|
||||||
if (const ColumnArray * array = typeid_cast<const ColumnArray *>(&*elem.column))
|
|
||||||
{
|
|
||||||
String offsets_name = Nested::extractTableName(elem.name);
|
|
||||||
auto & offsets_column = offset_columns[offsets_name];
|
|
||||||
|
|
||||||
/// If for some reason there are different offset columns for one nested structure, then we take nonempty.
|
|
||||||
if (!offsets_column || offsets_column->empty())
|
|
||||||
offsets_column = array->getOffsetsPtr();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// In this loop we fill missed columns
|
|
||||||
for (auto & column : res)
|
|
||||||
{
|
|
||||||
if (block.has(column.name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
String offsets_name = Nested::extractTableName(column.name);
|
|
||||||
if (offset_columns.count(offsets_name))
|
|
||||||
{
|
|
||||||
ColumnPtr offsets_column = offset_columns[offsets_name];
|
|
||||||
DataTypePtr nested_type = typeid_cast<const DataTypeArray &>(*column.type).getNestedType();
|
|
||||||
UInt64 nested_rows = rows ? get<UInt64>((*offsets_column)[rows - 1]) : 0;
|
|
||||||
|
|
||||||
ColumnPtr nested_column = nested_type->createColumnConstWithDefaultValue(nested_rows)->convertToFullColumnIfConst();
|
|
||||||
column.column = ColumnArray::create(nested_column, offsets_column);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/** It is necessary to turn a constant column into a full column, since in part of blocks (from other parts),
|
|
||||||
* it can be full (or the interpreter may decide that it is constant everywhere).
|
|
||||||
*/
|
|
||||||
column.column = column.type->createColumnConstWithDefaultValue(rows)->convertToFullColumnIfConst();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Computes explicitly specified values (in column_defaults) by default and materialized columns.
|
|
||||||
evaluateMissingDefaults(res, output_block.getNamesAndTypesList(), column_defaults, context);
|
|
||||||
|
|
||||||
output->write(res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddingDefaultBlockOutputStream::flush()
|
void AddingDefaultBlockOutputStream::flush()
|
||||||
|
@ -1,43 +1,8 @@
|
|||||||
include(${ClickHouse_SOURCE_DIR}/cmake/dbms_glob_sources.cmake)
|
include(${ClickHouse_SOURCE_DIR}/cmake/dbms_glob_sources.cmake)
|
||||||
include(${ClickHouse_SOURCE_DIR}/cmake/dbms_generate_function.cmake)
|
|
||||||
|
|
||||||
set (FUNCTIONS_GENERATED_DIR ${CMAKE_CURRENT_BINARY_DIR}/generated/)
|
|
||||||
|
|
||||||
generate_function_register(Arithmetic
|
|
||||||
FunctionPlus
|
|
||||||
FunctionMinus
|
|
||||||
FunctionMultiply
|
|
||||||
FunctionDivideFloating
|
|
||||||
FunctionDivideIntegral
|
|
||||||
FunctionDivideIntegralOrZero
|
|
||||||
FunctionModulo
|
|
||||||
FunctionNegate
|
|
||||||
FunctionAbs
|
|
||||||
FunctionBitAnd
|
|
||||||
FunctionBitOr
|
|
||||||
FunctionBitXor
|
|
||||||
FunctionBitNot
|
|
||||||
FunctionBitShiftLeft
|
|
||||||
FunctionBitShiftRight
|
|
||||||
FunctionBitRotateLeft
|
|
||||||
FunctionBitRotateRight
|
|
||||||
FunctionLeast
|
|
||||||
FunctionGreatest
|
|
||||||
FunctionBitTest
|
|
||||||
FunctionBitTestAny
|
|
||||||
FunctionBitTestAll
|
|
||||||
FunctionGCD
|
|
||||||
FunctionLCM
|
|
||||||
FunctionIntExp2
|
|
||||||
FunctionIntExp10
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
add_headers_and_sources(clickhouse_functions .)
|
add_headers_and_sources(clickhouse_functions .)
|
||||||
add_headers_and_sources(clickhouse_functions ./GatherUtils)
|
add_headers_and_sources(clickhouse_functions ./GatherUtils)
|
||||||
add_headers_and_sources(clickhouse_functions ./Conditional)
|
add_headers_and_sources(clickhouse_functions ./Conditional)
|
||||||
#add_headers_and_sources(clickhouse_functions ${ClickHouse_BINARY_DIR}/dbms/src/Functions)
|
|
||||||
add_headers_and_sources(clickhouse_functions ${FUNCTIONS_GENERATED_DIR})
|
|
||||||
|
|
||||||
|
|
||||||
list(REMOVE_ITEM clickhouse_functions_sources IFunction.cpp FunctionFactory.cpp FunctionHelpers.cpp)
|
list(REMOVE_ITEM clickhouse_functions_sources IFunction.cpp FunctionFactory.cpp FunctionHelpers.cpp)
|
||||||
@ -59,8 +24,7 @@ if (CMAKE_BUILD_TYPE_UC STREQUAL "RELEASE" OR CMAKE_BUILD_TYPE_UC STREQUAL "RELW
|
|||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (USE_ICU)
|
if (USE_ICU)
|
||||||
#target_link_libraries (clickhouse_functions ${ICU_LIBS})
|
target_link_libraries (clickhouse_functions PRIVATE ${ICU_LIBRARIES})
|
||||||
target_include_directories (clickhouse_functions SYSTEM PRIVATE ${ICU_INCLUDE_DIR})
|
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
if (USE_VECTORCLASS)
|
if (USE_VECTORCLASS)
|
||||||
|
921
dbms/src/Functions/FunctionBinaryArithmetic.h
Normal file
921
dbms/src/Functions/FunctionBinaryArithmetic.h
Normal file
@ -0,0 +1,921 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DataTypes/DataTypesNumber.h>
|
||||||
|
#include <DataTypes/DataTypesDecimal.h>
|
||||||
|
#include <DataTypes/DataTypeDate.h>
|
||||||
|
#include <DataTypes/DataTypeDateTime.h>
|
||||||
|
#include <DataTypes/DataTypeInterval.h>
|
||||||
|
#include <DataTypes/DataTypeAggregateFunction.h>
|
||||||
|
#include <DataTypes/Native.h>
|
||||||
|
#include <Columns/ColumnVector.h>
|
||||||
|
#include <Columns/ColumnDecimal.h>
|
||||||
|
#include <Columns/ColumnConst.h>
|
||||||
|
#include <Columns/ColumnAggregateFunction.h>
|
||||||
|
#include <Functions/IFunction.h>
|
||||||
|
#include <Functions/FunctionHelpers.h>
|
||||||
|
#include <DataTypes/NumberTraits.h>
|
||||||
|
#include <Common/typeid_cast.h>
|
||||||
|
#include <Common/Arena.h>
|
||||||
|
#include <Functions/intDiv.h>
|
||||||
|
#include <Functions/castTypeToEither.h>
|
||||||
|
#include <Common/config.h>
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
|
#include <llvm/IR/IRBuilder.h> // Y_IGNORE
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ErrorCodes
|
||||||
|
{
|
||||||
|
extern const int ILLEGAL_COLUMN;
|
||||||
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||||
|
extern const int LOGICAL_ERROR;
|
||||||
|
extern const int DECIMAL_OVERFLOW;
|
||||||
|
extern const int CANNOT_ADD_DIFFERENT_AGGREGATE_STATES;
|
||||||
|
extern const int ILLEGAL_DIVISION;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Arithmetic operations: +, -, *, /, %,
|
||||||
|
* intDiv (integer division)
|
||||||
|
* Bitwise operations: |, &, ^, ~.
|
||||||
|
* Etc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename A, typename B, typename Op, typename ResultType_ = typename Op::ResultType>
|
||||||
|
struct BinaryOperationImplBase
|
||||||
|
{
|
||||||
|
using ResultType = ResultType_;
|
||||||
|
|
||||||
|
static void NO_INLINE vector_vector(const PaddedPODArray<A> & a, const PaddedPODArray<B> & b, PaddedPODArray<ResultType> & c)
|
||||||
|
{
|
||||||
|
size_t size = a.size();
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = Op::template apply<ResultType>(a[i], b[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NO_INLINE vector_constant(const PaddedPODArray<A> & a, B b, PaddedPODArray<ResultType> & c)
|
||||||
|
{
|
||||||
|
size_t size = a.size();
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = Op::template apply<ResultType>(a[i], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NO_INLINE constant_vector(A a, const PaddedPODArray<B> & b, PaddedPODArray<ResultType> & c)
|
||||||
|
{
|
||||||
|
size_t size = b.size();
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = Op::template apply<ResultType>(a, b[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ResultType constant_constant(A a, B b)
|
||||||
|
{
|
||||||
|
return Op::template apply<ResultType>(a, b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A, typename B, typename Op, typename ResultType = typename Op::ResultType>
|
||||||
|
struct BinaryOperationImpl : BinaryOperationImplBase<A, B, Op, ResultType>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename, typename> struct PlusImpl;
|
||||||
|
template <typename, typename> struct MinusImpl;
|
||||||
|
template <typename, typename> struct MultiplyImpl;
|
||||||
|
template <typename, typename> struct DivideFloatingImpl;
|
||||||
|
template <typename, typename> struct DivideIntegralImpl;
|
||||||
|
template <typename, typename> struct DivideIntegralOrZeroImpl;
|
||||||
|
template <typename, typename> struct LeastBaseImpl;
|
||||||
|
template <typename, typename> struct GreatestBaseImpl;
|
||||||
|
template <typename, typename> struct ModuloImpl;
|
||||||
|
|
||||||
|
|
||||||
|
template <typename T> struct NativeType { using Type = T; };
|
||||||
|
template <> struct NativeType<Decimal32> { using Type = Int32; };
|
||||||
|
template <> struct NativeType<Decimal64> { using Type = Int64; };
|
||||||
|
template <> struct NativeType<Decimal128> { using Type = Int128; };
|
||||||
|
|
||||||
|
/// Binary operations for Decimals need scale args
|
||||||
|
/// +|- scale one of args (which scale factor is not 1). ScaleR = oneof(Scale1, Scale2);
|
||||||
|
/// * no agrs scale. ScaleR = Scale1 + Scale2;
|
||||||
|
/// / first arg scale. ScaleR = Scale1 (scale_a = DecimalType<B>::getScale()).
|
||||||
|
template <typename A, typename B, template <typename, typename> typename Operation, typename ResultType_, bool _check_overflow = true>
|
||||||
|
struct DecimalBinaryOperation
|
||||||
|
{
|
||||||
|
static constexpr bool is_plus_minus = std::is_same_v<Operation<Int32, Int32>, PlusImpl<Int32, Int32>> ||
|
||||||
|
std::is_same_v<Operation<Int32, Int32>, MinusImpl<Int32, Int32>>;
|
||||||
|
static constexpr bool is_multiply = std::is_same_v<Operation<Int32, Int32>, MultiplyImpl<Int32, Int32>>;
|
||||||
|
static constexpr bool is_float_division = std::is_same_v<Operation<Int32, Int32>, DivideFloatingImpl<Int32, Int32>>;
|
||||||
|
static constexpr bool is_int_division = std::is_same_v<Operation<Int32, Int32>, DivideIntegralImpl<Int32, Int32>> ||
|
||||||
|
std::is_same_v<Operation<Int32, Int32>, DivideIntegralOrZeroImpl<Int32, Int32>>;
|
||||||
|
static constexpr bool is_division = is_float_division || is_int_division;
|
||||||
|
static constexpr bool is_compare = std::is_same_v<Operation<Int32, Int32>, LeastBaseImpl<Int32, Int32>> ||
|
||||||
|
std::is_same_v<Operation<Int32, Int32>, GreatestBaseImpl<Int32, Int32>>;
|
||||||
|
static constexpr bool is_plus_minus_compare = is_plus_minus || is_compare;
|
||||||
|
static constexpr bool can_overflow = is_plus_minus || is_multiply;
|
||||||
|
|
||||||
|
using ResultType = ResultType_;
|
||||||
|
using NativeResultType = typename NativeType<ResultType>::Type;
|
||||||
|
using Op = std::conditional_t<is_float_division,
|
||||||
|
DivideIntegralImpl<NativeResultType, NativeResultType>, /// substitute divide by intDiv (throw on division by zero)
|
||||||
|
Operation<NativeResultType, NativeResultType>>;
|
||||||
|
using ColVecA = std::conditional_t<IsDecimalNumber<A>, ColumnDecimal<A>, ColumnVector<A>>;
|
||||||
|
using ColVecB = std::conditional_t<IsDecimalNumber<B>, ColumnDecimal<B>, ColumnVector<B>>;
|
||||||
|
using ArrayA = typename ColVecA::Container;
|
||||||
|
using ArrayB = typename ColVecB::Container;
|
||||||
|
using ArrayC = typename ColumnDecimal<ResultType>::Container;
|
||||||
|
using SelfNoOverflow = DecimalBinaryOperation<A, B, Operation, ResultType_, false>;
|
||||||
|
|
||||||
|
static void vector_vector(const ArrayA & a, const ArrayB & b, ArrayC & c, ResultType scale_a, ResultType scale_b, bool check_overflow)
|
||||||
|
{
|
||||||
|
if (check_overflow)
|
||||||
|
vector_vector(a, b, c, scale_a, scale_b);
|
||||||
|
else
|
||||||
|
SelfNoOverflow::vector_vector(a, b, c, scale_a, scale_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vector_constant(const ArrayA & a, B b, ArrayC & c, ResultType scale_a, ResultType scale_b, bool check_overflow)
|
||||||
|
{
|
||||||
|
if (check_overflow)
|
||||||
|
vector_constant(a, b, c, scale_a, scale_b);
|
||||||
|
else
|
||||||
|
SelfNoOverflow::vector_constant(a, b, c, scale_a, scale_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constant_vector(A a, const ArrayB & b, ArrayC & c, ResultType scale_a, ResultType scale_b, bool check_overflow)
|
||||||
|
{
|
||||||
|
if (check_overflow)
|
||||||
|
constant_vector(a, b, c, scale_a, scale_b);
|
||||||
|
else
|
||||||
|
SelfNoOverflow::constant_vector(a, b, c, scale_a, scale_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ResultType constant_constant(A a, B b, ResultType scale_a, ResultType scale_b, bool check_overflow)
|
||||||
|
{
|
||||||
|
if (check_overflow)
|
||||||
|
return constant_constant(a, b, scale_a, scale_b);
|
||||||
|
else
|
||||||
|
return SelfNoOverflow::constant_constant(a, b, scale_a, scale_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NO_INLINE vector_vector(const ArrayA & a, const ArrayB & b, ArrayC & c,
|
||||||
|
ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
||||||
|
{
|
||||||
|
size_t size = a.size();
|
||||||
|
if constexpr (is_plus_minus_compare)
|
||||||
|
{
|
||||||
|
if (scale_a != 1)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = applyScaled<true>(a[i], b[i], scale_a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (scale_b != 1)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = applyScaled<false>(a[i], b[i], scale_b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if constexpr (is_division && IsDecimalNumber<B>)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = applyScaledDiv(a[i], b[i], scale_a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// default: use it if no return before
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = apply(a[i], b[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NO_INLINE vector_constant(const ArrayA & a, B b, ArrayC & c,
|
||||||
|
ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
||||||
|
{
|
||||||
|
size_t size = a.size();
|
||||||
|
if constexpr (is_plus_minus_compare)
|
||||||
|
{
|
||||||
|
if (scale_a != 1)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = applyScaled<true>(a[i], b, scale_a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (scale_b != 1)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = applyScaled<false>(a[i], b, scale_b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if constexpr (is_division && IsDecimalNumber<B>)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = applyScaledDiv(a[i], b, scale_a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// default: use it if no return before
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = apply(a[i], b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void NO_INLINE constant_vector(A a, const ArrayB & b, ArrayC & c,
|
||||||
|
ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
||||||
|
{
|
||||||
|
size_t size = b.size();
|
||||||
|
if constexpr (is_plus_minus_compare)
|
||||||
|
{
|
||||||
|
if (scale_a != 1)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = applyScaled<true>(a, b[i], scale_a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (scale_b != 1)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = applyScaled<false>(a, b[i], scale_b);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if constexpr (is_division && IsDecimalNumber<B>)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = applyScaledDiv(a, b[i], scale_a);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// default: use it if no return before
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = apply(a, b[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ResultType constant_constant(A a, B b, ResultType scale_a [[maybe_unused]], ResultType scale_b [[maybe_unused]])
|
||||||
|
{
|
||||||
|
if constexpr (is_plus_minus_compare)
|
||||||
|
{
|
||||||
|
if (scale_a != 1)
|
||||||
|
return applyScaled<true>(a, b, scale_a);
|
||||||
|
else if (scale_b != 1)
|
||||||
|
return applyScaled<false>(a, b, scale_b);
|
||||||
|
}
|
||||||
|
else if constexpr (is_division && IsDecimalNumber<B>)
|
||||||
|
return applyScaledDiv(a, b, scale_a);
|
||||||
|
return apply(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
/// there's implicit type convertion here
|
||||||
|
static NativeResultType apply(NativeResultType a, NativeResultType b)
|
||||||
|
{
|
||||||
|
if constexpr (can_overflow && _check_overflow)
|
||||||
|
{
|
||||||
|
NativeResultType res;
|
||||||
|
if (Op::template apply<NativeResultType>(a, b, res))
|
||||||
|
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return Op::template apply<NativeResultType>(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool scale_left>
|
||||||
|
static NativeResultType applyScaled(NativeResultType a, NativeResultType b, NativeResultType scale)
|
||||||
|
{
|
||||||
|
if constexpr (is_plus_minus_compare)
|
||||||
|
{
|
||||||
|
NativeResultType res;
|
||||||
|
|
||||||
|
if constexpr (_check_overflow)
|
||||||
|
{
|
||||||
|
bool overflow = false;
|
||||||
|
if constexpr (scale_left)
|
||||||
|
overflow |= common::mulOverflow(a, scale, a);
|
||||||
|
else
|
||||||
|
overflow |= common::mulOverflow(b, scale, b);
|
||||||
|
|
||||||
|
if constexpr (can_overflow)
|
||||||
|
overflow |= Op::template apply<NativeResultType>(a, b, res);
|
||||||
|
else
|
||||||
|
res = Op::template apply<NativeResultType>(a, b);
|
||||||
|
|
||||||
|
if (overflow)
|
||||||
|
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if constexpr (scale_left)
|
||||||
|
a *= scale;
|
||||||
|
else
|
||||||
|
b *= scale;
|
||||||
|
res = Op::template apply<NativeResultType>(a, b);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static NativeResultType applyScaledDiv(NativeResultType a, NativeResultType b, NativeResultType scale)
|
||||||
|
{
|
||||||
|
if constexpr (is_division)
|
||||||
|
{
|
||||||
|
if constexpr (_check_overflow)
|
||||||
|
{
|
||||||
|
bool overflow = false;
|
||||||
|
if constexpr (!IsDecimalNumber<A>)
|
||||||
|
overflow |= common::mulOverflow(scale, scale, scale);
|
||||||
|
overflow |= common::mulOverflow(a, scale, a);
|
||||||
|
if (overflow)
|
||||||
|
throw Exception("Decimal math overflow", ErrorCodes::DECIMAL_OVERFLOW);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if constexpr (!IsDecimalNumber<A>)
|
||||||
|
scale *= scale;
|
||||||
|
a *= scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Op::template apply<NativeResultType>(a, b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/// Used to indicate undefined operation
|
||||||
|
struct InvalidType;
|
||||||
|
|
||||||
|
template <bool V, typename T> struct Case : std::bool_constant<V> { using type = T; };
|
||||||
|
|
||||||
|
/// Switch<Case<C0, T0>, ...> -- select the first Ti for which Ci is true; InvalidType if none.
|
||||||
|
template <typename... Ts> using Switch = typename std::disjunction<Ts..., Case<true, InvalidType>>::type;
|
||||||
|
|
||||||
|
template <typename DataType> constexpr bool IsIntegral = false;
|
||||||
|
template <> constexpr bool IsIntegral<DataTypeUInt8> = true;
|
||||||
|
template <> constexpr bool IsIntegral<DataTypeUInt16> = true;
|
||||||
|
template <> constexpr bool IsIntegral<DataTypeUInt32> = true;
|
||||||
|
template <> constexpr bool IsIntegral<DataTypeUInt64> = true;
|
||||||
|
template <> constexpr bool IsIntegral<DataTypeInt8> = true;
|
||||||
|
template <> constexpr bool IsIntegral<DataTypeInt16> = true;
|
||||||
|
template <> constexpr bool IsIntegral<DataTypeInt32> = true;
|
||||||
|
template <> constexpr bool IsIntegral<DataTypeInt64> = true;
|
||||||
|
|
||||||
|
template <typename DataType> constexpr bool IsFloatingPoint = false;
|
||||||
|
template <> constexpr bool IsFloatingPoint<DataTypeFloat32> = true;
|
||||||
|
template <> constexpr bool IsFloatingPoint<DataTypeFloat64> = true;
|
||||||
|
|
||||||
|
template <typename DataType> constexpr bool IsDateOrDateTime = false;
|
||||||
|
template <> constexpr bool IsDateOrDateTime<DataTypeDate> = true;
|
||||||
|
template <> constexpr bool IsDateOrDateTime<DataTypeDateTime> = true;
|
||||||
|
|
||||||
|
template <typename T0, typename T1> constexpr bool UseLeftDecimal = false;
|
||||||
|
template <> constexpr bool UseLeftDecimal<DataTypeDecimal<Decimal128>, DataTypeDecimal<Decimal32>> = true;
|
||||||
|
template <> constexpr bool UseLeftDecimal<DataTypeDecimal<Decimal128>, DataTypeDecimal<Decimal64>> = true;
|
||||||
|
template <> constexpr bool UseLeftDecimal<DataTypeDecimal<Decimal64>, DataTypeDecimal<Decimal32>> = true;
|
||||||
|
|
||||||
|
template <typename T> using DataTypeFromFieldType = std::conditional_t<std::is_same_v<T, NumberTraits::Error>, InvalidType, DataTypeNumber<T>>;
|
||||||
|
|
||||||
|
template <template <typename, typename> class Operation, typename LeftDataType, typename RightDataType>
|
||||||
|
struct BinaryOperationTraits
|
||||||
|
{
|
||||||
|
using T0 = typename LeftDataType::FieldType;
|
||||||
|
using T1 = typename RightDataType::FieldType;
|
||||||
|
private: /// it's not correct for Decimal
|
||||||
|
using Op = Operation<T0, T1>;
|
||||||
|
public:
|
||||||
|
|
||||||
|
static constexpr bool allow_decimal =
|
||||||
|
std::is_same_v<Operation<T0, T0>, PlusImpl<T0, T0>> ||
|
||||||
|
std::is_same_v<Operation<T0, T0>, MinusImpl<T0, T0>> ||
|
||||||
|
std::is_same_v<Operation<T0, T0>, MultiplyImpl<T0, T0>> ||
|
||||||
|
std::is_same_v<Operation<T0, T0>, DivideFloatingImpl<T0, T0>> ||
|
||||||
|
std::is_same_v<Operation<T0, T0>, DivideIntegralImpl<T0, T0>> ||
|
||||||
|
std::is_same_v<Operation<T0, T0>, DivideIntegralOrZeroImpl<T0, T0>> ||
|
||||||
|
std::is_same_v<Operation<T0, T0>, LeastBaseImpl<T0, T0>> ||
|
||||||
|
std::is_same_v<Operation<T0, T0>, GreatestBaseImpl<T0, T0>>;
|
||||||
|
|
||||||
|
/// Appropriate result type for binary operator on numeric types. "Date" can also mean
|
||||||
|
/// DateTime, but if both operands are Dates, their type must be the same (e.g. Date - DateTime is invalid).
|
||||||
|
using ResultDataType = Switch<
|
||||||
|
/// Decimal cases
|
||||||
|
Case<!allow_decimal && (IsDataTypeDecimal<LeftDataType> || IsDataTypeDecimal<RightDataType>), InvalidType>,
|
||||||
|
Case<IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType> && UseLeftDecimal<LeftDataType, RightDataType>, LeftDataType>,
|
||||||
|
Case<IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType>, RightDataType>,
|
||||||
|
Case<IsDataTypeDecimal<LeftDataType> && !IsDataTypeDecimal<RightDataType> && IsIntegral<RightDataType>, LeftDataType>,
|
||||||
|
Case<!IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType> && IsIntegral<LeftDataType>, RightDataType>,
|
||||||
|
/// Decimal <op> Real is not supported (traditional DBs convert Decimal <op> Real to Real)
|
||||||
|
Case<IsDataTypeDecimal<LeftDataType> && !IsDataTypeDecimal<RightDataType> && !IsIntegral<RightDataType>, InvalidType>,
|
||||||
|
Case<!IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType> && !IsIntegral<LeftDataType>, InvalidType>,
|
||||||
|
/// number <op> number -> see corresponding impl
|
||||||
|
Case<!IsDateOrDateTime<LeftDataType> && !IsDateOrDateTime<RightDataType>,
|
||||||
|
DataTypeFromFieldType<typename Op::ResultType>>,
|
||||||
|
/// Date + Integral -> Date
|
||||||
|
/// Integral + Date -> Date
|
||||||
|
Case<std::is_same_v<Op, PlusImpl<T0, T1>>, Switch<
|
||||||
|
Case<IsIntegral<RightDataType>, LeftDataType>,
|
||||||
|
Case<IsIntegral<LeftDataType>, RightDataType>>>,
|
||||||
|
/// Date - Date -> Int32
|
||||||
|
/// Date - Integral -> Date
|
||||||
|
Case<std::is_same_v<Op, MinusImpl<T0, T1>>, Switch<
|
||||||
|
Case<std::is_same_v<LeftDataType, RightDataType>, DataTypeInt32>,
|
||||||
|
Case<IsDateOrDateTime<LeftDataType> && IsIntegral<RightDataType>, LeftDataType>>>,
|
||||||
|
/// least(Date, Date) -> Date
|
||||||
|
/// greatest(Date, Date) -> Date
|
||||||
|
Case<std::is_same_v<LeftDataType, RightDataType> && (std::is_same_v<Op, LeastBaseImpl<T0, T1>> || std::is_same_v<Op, GreatestBaseImpl<T0, T1>>),
|
||||||
|
LeftDataType>,
|
||||||
|
/// Date % Int32 -> int32
|
||||||
|
Case<std::is_same_v<Op, ModuloImpl<T0, T1>>, Switch<
|
||||||
|
Case<IsDateOrDateTime<LeftDataType> && IsIntegral<RightDataType>, RightDataType>,
|
||||||
|
Case<IsDateOrDateTime<LeftDataType> && IsFloatingPoint<RightDataType>, DataTypeInt32>>>>;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <template <typename, typename> class Op, typename Name, bool CanBeExecutedOnDefaultArguments = true>
|
||||||
|
class FunctionBinaryArithmetic : public IFunction
|
||||||
|
{
|
||||||
|
const Context & context;
|
||||||
|
bool check_decimal_overflow = true;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
static bool castType(const IDataType * type, F && f)
|
||||||
|
{
|
||||||
|
return castTypeToEither<
|
||||||
|
DataTypeUInt8,
|
||||||
|
DataTypeUInt16,
|
||||||
|
DataTypeUInt32,
|
||||||
|
DataTypeUInt64,
|
||||||
|
DataTypeInt8,
|
||||||
|
DataTypeInt16,
|
||||||
|
DataTypeInt32,
|
||||||
|
DataTypeInt64,
|
||||||
|
DataTypeFloat32,
|
||||||
|
DataTypeFloat64,
|
||||||
|
DataTypeDate,
|
||||||
|
DataTypeDateTime,
|
||||||
|
DataTypeDecimal<Decimal32>,
|
||||||
|
DataTypeDecimal<Decimal64>,
|
||||||
|
DataTypeDecimal<Decimal128>
|
||||||
|
>(type, std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
static bool castBothTypes(const IDataType * left, const IDataType * right, F && f)
|
||||||
|
{
|
||||||
|
return castType(left, [&](const auto & left_) { return castType(right, [&](const auto & right_) { return f(left_, right_); }); });
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionBuilderPtr getFunctionForIntervalArithmetic(const DataTypePtr & type0, const DataTypePtr & type1) const
|
||||||
|
{
|
||||||
|
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Interval.
|
||||||
|
/// We construct another function (example: addMonths) and call it.
|
||||||
|
|
||||||
|
bool function_is_plus = std::is_same_v<Op<UInt8, UInt8>, PlusImpl<UInt8, UInt8>>;
|
||||||
|
bool function_is_minus = std::is_same_v<Op<UInt8, UInt8>, MinusImpl<UInt8, UInt8>>;
|
||||||
|
|
||||||
|
if (!function_is_plus && !function_is_minus)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
int interval_arg = 1;
|
||||||
|
const DataTypeInterval * interval_data_type = checkAndGetDataType<DataTypeInterval>(type1.get());
|
||||||
|
if (!interval_data_type)
|
||||||
|
{
|
||||||
|
interval_arg = 0;
|
||||||
|
interval_data_type = checkAndGetDataType<DataTypeInterval>(type0.get());
|
||||||
|
}
|
||||||
|
if (!interval_data_type)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (interval_arg == 0 && function_is_minus)
|
||||||
|
throw Exception("Wrong order of arguments for function " + getName() + ": argument of type Interval cannot be first.",
|
||||||
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||||
|
|
||||||
|
const DataTypeDate * date_data_type = checkAndGetDataType<DataTypeDate>(interval_arg == 0 ? type1.get() : type0.get());
|
||||||
|
const DataTypeDateTime * date_time_data_type = nullptr;
|
||||||
|
if (!date_data_type)
|
||||||
|
{
|
||||||
|
date_time_data_type = checkAndGetDataType<DataTypeDateTime>(interval_arg == 0 ? type1.get() : type0.get());
|
||||||
|
if (!date_time_data_type)
|
||||||
|
throw Exception("Wrong argument types for function " + getName() + ": if one argument is Interval, then another must be Date or DateTime.",
|
||||||
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream function_name;
|
||||||
|
function_name << (function_is_plus ? "add" : "subtract") << interval_data_type->kindToString() << 's';
|
||||||
|
|
||||||
|
return FunctionFactory::instance().get(function_name.str(), context);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAggregateMultiply(const DataTypePtr & type0, const DataTypePtr & type1) const
|
||||||
|
{
|
||||||
|
if constexpr (!std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WhichDataType which0(type0);
|
||||||
|
WhichDataType which1(type1);
|
||||||
|
|
||||||
|
return (which0.isAggregateFunction() && which1.isNativeUInt())
|
||||||
|
|| (which0.isNativeUInt() && which1.isAggregateFunction());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isAggregateAddition(const DataTypePtr & type0, const DataTypePtr & type1) const
|
||||||
|
{
|
||||||
|
if constexpr (!std::is_same_v<Op<UInt8, UInt8>, PlusImpl<UInt8, UInt8>>)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
WhichDataType which0(type0);
|
||||||
|
WhichDataType which1(type1);
|
||||||
|
|
||||||
|
return which0.isAggregateFunction() && which1.isAggregateFunction();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Multiply aggregation state by integer constant: by merging it with itself specified number of times.
|
||||||
|
void executeAggregateMultiply(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) const
|
||||||
|
{
|
||||||
|
ColumnNumbers new_arguments = arguments;
|
||||||
|
if (WhichDataType(block.getByPosition(new_arguments[1]).type).isAggregateFunction())
|
||||||
|
std::swap(new_arguments[0], new_arguments[1]);
|
||||||
|
|
||||||
|
if (!block.getByPosition(new_arguments[1]).column->isColumnConst())
|
||||||
|
throw Exception{"Illegal column " + block.getByPosition(new_arguments[1]).column->getName()
|
||||||
|
+ " of argument of aggregation state multiply. Should be integer constant", ErrorCodes::ILLEGAL_COLUMN};
|
||||||
|
|
||||||
|
const ColumnAggregateFunction * column = typeid_cast<const ColumnAggregateFunction *>(block.getByPosition(new_arguments[0]).column.get());
|
||||||
|
IAggregateFunction * function = column->getAggregateFunction().get();
|
||||||
|
|
||||||
|
auto arena = std::make_shared<Arena>();
|
||||||
|
|
||||||
|
auto column_to = ColumnAggregateFunction::create(column->getAggregateFunction(), Arenas(1, arena));
|
||||||
|
column_to->reserve(input_rows_count);
|
||||||
|
|
||||||
|
auto column_from = ColumnAggregateFunction::create(column->getAggregateFunction(), Arenas(1, arena));
|
||||||
|
column_from->reserve(input_rows_count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < input_rows_count; ++i)
|
||||||
|
{
|
||||||
|
column_to->insertDefault();
|
||||||
|
column_from->insertFrom(column->getData()[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto & vec_to = column_to->getData();
|
||||||
|
auto & vec_from = column_from->getData();
|
||||||
|
|
||||||
|
UInt64 m = typeid_cast<const ColumnConst *>(block.getByPosition(new_arguments[1]).column.get())->getValue<UInt64>();
|
||||||
|
|
||||||
|
/// We use exponentiation by squaring algorithm to perform multiplying aggregate states by N in O(log(N)) operations
|
||||||
|
/// https://en.wikipedia.org/wiki/Exponentiation_by_squaring
|
||||||
|
while (m)
|
||||||
|
{
|
||||||
|
if (m % 2)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < input_rows_count; ++i)
|
||||||
|
function->merge(vec_to[i], vec_from[i], arena.get());
|
||||||
|
--m;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < input_rows_count; ++i)
|
||||||
|
function->merge(vec_from[i], vec_from[i], arena.get());
|
||||||
|
m /= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block.getByPosition(result).column = std::move(column_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Merge two aggregation states together.
|
||||||
|
void executeAggregateAddition(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) const
|
||||||
|
{
|
||||||
|
const ColumnAggregateFunction * columns[2];
|
||||||
|
for (size_t i = 0; i < 2; ++i)
|
||||||
|
columns[i] = typeid_cast<const ColumnAggregateFunction *>(block.getByPosition(arguments[i]).column.get());
|
||||||
|
|
||||||
|
auto column_to = ColumnAggregateFunction::create(columns[0]->getAggregateFunction());
|
||||||
|
column_to->reserve(input_rows_count);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < input_rows_count; ++i)
|
||||||
|
{
|
||||||
|
column_to->insertFrom(columns[0]->getData()[i]);
|
||||||
|
column_to->insertMergeFrom(columns[1]->getData()[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.getByPosition(result).column = std::move(column_to);
|
||||||
|
}
|
||||||
|
|
||||||
|
void executeDateTimeIntervalPlusMinus(Block & block, const ColumnNumbers & arguments,
|
||||||
|
size_t result, size_t input_rows_count, const FunctionBuilderPtr & function_builder) const
|
||||||
|
{
|
||||||
|
ColumnNumbers new_arguments = arguments;
|
||||||
|
|
||||||
|
/// Interval argument must be second.
|
||||||
|
if (WhichDataType(block.getByPosition(arguments[0]).type).isInterval())
|
||||||
|
std::swap(new_arguments[0], new_arguments[1]);
|
||||||
|
|
||||||
|
/// Change interval argument type to its representation
|
||||||
|
Block new_block = block;
|
||||||
|
new_block.getByPosition(new_arguments[1]).type = std::make_shared<DataTypeNumber<DataTypeInterval::FieldType>>();
|
||||||
|
|
||||||
|
ColumnsWithTypeAndName new_arguments_with_type_and_name =
|
||||||
|
{new_block.getByPosition(new_arguments[0]), new_block.getByPosition(new_arguments[1])};
|
||||||
|
auto function = function_builder->build(new_arguments_with_type_and_name);
|
||||||
|
|
||||||
|
function->execute(new_block, new_arguments, result, input_rows_count);
|
||||||
|
block.getByPosition(result).column = new_block.getByPosition(result).column;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr auto name = Name::name;
|
||||||
|
static FunctionPtr create(const Context & context) { return std::make_shared<FunctionBinaryArithmetic>(context); }
|
||||||
|
|
||||||
|
FunctionBinaryArithmetic(const Context & context_)
|
||||||
|
: context(context_),
|
||||||
|
check_decimal_overflow(decimalCheckArithmeticOverflow(context))
|
||||||
|
{}
|
||||||
|
|
||||||
|
String getName() const override
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getNumberOfArguments() const override { return 2; }
|
||||||
|
|
||||||
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||||
|
{
|
||||||
|
/// Special case when multiply aggregate function state
|
||||||
|
if (isAggregateMultiply(arguments[0], arguments[1]))
|
||||||
|
{
|
||||||
|
if (WhichDataType(arguments[0]).isAggregateFunction())
|
||||||
|
return arguments[0];
|
||||||
|
return arguments[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Special case - addition of two aggregate functions states
|
||||||
|
if (isAggregateAddition(arguments[0], arguments[1]))
|
||||||
|
{
|
||||||
|
if (!arguments[0]->equals(*arguments[1]))
|
||||||
|
throw Exception("Cannot add aggregate states of different functions: "
|
||||||
|
+ arguments[0]->getName() + " and " + arguments[1]->getName(), ErrorCodes::CANNOT_ADD_DIFFERENT_AGGREGATE_STATES);
|
||||||
|
|
||||||
|
return arguments[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Interval.
|
||||||
|
if (auto function_builder = getFunctionForIntervalArithmetic(arguments[0], arguments[1]))
|
||||||
|
{
|
||||||
|
ColumnsWithTypeAndName new_arguments(2);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 2; ++i)
|
||||||
|
new_arguments[i].type = arguments[i];
|
||||||
|
|
||||||
|
/// Interval argument must be second.
|
||||||
|
if (WhichDataType(new_arguments[0].type).isInterval())
|
||||||
|
std::swap(new_arguments[0], new_arguments[1]);
|
||||||
|
|
||||||
|
/// Change interval argument to its representation
|
||||||
|
new_arguments[1].type = std::make_shared<DataTypeNumber<DataTypeInterval::FieldType>>();
|
||||||
|
|
||||||
|
auto function = function_builder->build(new_arguments);
|
||||||
|
return function->getReturnType();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataTypePtr type_res;
|
||||||
|
bool valid = castBothTypes(arguments[0].get(), arguments[1].get(), [&](const auto & left, const auto & right)
|
||||||
|
{
|
||||||
|
using LeftDataType = std::decay_t<decltype(left)>;
|
||||||
|
using RightDataType = std::decay_t<decltype(right)>;
|
||||||
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
||||||
|
if constexpr (!std::is_same_v<ResultDataType, InvalidType>)
|
||||||
|
{
|
||||||
|
if constexpr (IsDataTypeDecimal<LeftDataType> && IsDataTypeDecimal<RightDataType>)
|
||||||
|
{
|
||||||
|
constexpr bool is_multiply = std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>;
|
||||||
|
constexpr bool is_division = std::is_same_v<Op<UInt8, UInt8>, DivideFloatingImpl<UInt8, UInt8>> ||
|
||||||
|
std::is_same_v<Op<UInt8, UInt8>, DivideIntegralImpl<UInt8, UInt8>> ||
|
||||||
|
std::is_same_v<Op<UInt8, UInt8>, DivideIntegralOrZeroImpl<UInt8, UInt8>>;
|
||||||
|
|
||||||
|
ResultDataType result_type = decimalResultType(left, right, is_multiply, is_division);
|
||||||
|
type_res = std::make_shared<ResultDataType>(result_type.getPrecision(), result_type.getScale());
|
||||||
|
}
|
||||||
|
else if constexpr (IsDataTypeDecimal<LeftDataType>)
|
||||||
|
type_res = std::make_shared<LeftDataType>(left.getPrecision(), left.getScale());
|
||||||
|
else if constexpr (IsDataTypeDecimal<RightDataType>)
|
||||||
|
type_res = std::make_shared<RightDataType>(right.getPrecision(), right.getScale());
|
||||||
|
else
|
||||||
|
type_res = std::make_shared<ResultDataType>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (!valid)
|
||||||
|
throw Exception("Illegal types " + arguments[0]->getName() + " and " + arguments[1]->getName() + " of arguments of function " + getName(),
|
||||||
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||||
|
return type_res;
|
||||||
|
}
|
||||||
|
|
||||||
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t input_rows_count) override
|
||||||
|
{
|
||||||
|
/// Special case when multiply aggregate function state
|
||||||
|
if (isAggregateMultiply(block.getByPosition(arguments[0]).type, block.getByPosition(arguments[1]).type))
|
||||||
|
{
|
||||||
|
executeAggregateMultiply(block, arguments, result, input_rows_count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Special case - addition of two aggregate functions states
|
||||||
|
if (isAggregateAddition(block.getByPosition(arguments[0]).type, block.getByPosition(arguments[1]).type))
|
||||||
|
{
|
||||||
|
executeAggregateAddition(block, arguments, result, input_rows_count);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Special case when the function is plus or minus, one of arguments is Date/DateTime and another is Interval.
|
||||||
|
if (auto function_builder = getFunctionForIntervalArithmetic(block.getByPosition(arguments[0]).type, block.getByPosition(arguments[1]).type))
|
||||||
|
{
|
||||||
|
executeDateTimeIntervalPlusMinus(block, arguments, result, input_rows_count, function_builder);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto * left_generic = block.getByPosition(arguments[0]).type.get();
|
||||||
|
auto * right_generic = block.getByPosition(arguments[1]).type.get();
|
||||||
|
bool valid = castBothTypes(left_generic, right_generic, [&](const auto & left, const auto & right)
|
||||||
|
{
|
||||||
|
using LeftDataType = std::decay_t<decltype(left)>;
|
||||||
|
using RightDataType = std::decay_t<decltype(right)>;
|
||||||
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
||||||
|
if constexpr (!std::is_same_v<ResultDataType, InvalidType>)
|
||||||
|
{
|
||||||
|
constexpr bool result_is_decimal = IsDataTypeDecimal<LeftDataType> || IsDataTypeDecimal<RightDataType>;
|
||||||
|
constexpr bool is_multiply = std::is_same_v<Op<UInt8, UInt8>, MultiplyImpl<UInt8, UInt8>>;
|
||||||
|
constexpr bool is_division = std::is_same_v<Op<UInt8, UInt8>, DivideFloatingImpl<UInt8, UInt8>> ||
|
||||||
|
std::is_same_v<Op<UInt8, UInt8>, DivideIntegralImpl<UInt8, UInt8>> ||
|
||||||
|
std::is_same_v<Op<UInt8, UInt8>, DivideIntegralOrZeroImpl<UInt8, UInt8>>;
|
||||||
|
|
||||||
|
using T0 = typename LeftDataType::FieldType;
|
||||||
|
using T1 = typename RightDataType::FieldType;
|
||||||
|
using ResultType = typename ResultDataType::FieldType;
|
||||||
|
using ColVecT0 = std::conditional_t<IsDecimalNumber<T0>, ColumnDecimal<T0>, ColumnVector<T0>>;
|
||||||
|
using ColVecT1 = std::conditional_t<IsDecimalNumber<T1>, ColumnDecimal<T1>, ColumnVector<T1>>;
|
||||||
|
using ColVecResult = std::conditional_t<IsDecimalNumber<ResultType>, ColumnDecimal<ResultType>, ColumnVector<ResultType>>;
|
||||||
|
|
||||||
|
/// Decimal operations need scale. Operations are on result type.
|
||||||
|
using OpImpl = std::conditional_t<IsDataTypeDecimal<ResultDataType>,
|
||||||
|
DecimalBinaryOperation<T0, T1, Op, ResultType>,
|
||||||
|
BinaryOperationImpl<T0, T1, Op<T0, T1>, ResultType>>;
|
||||||
|
|
||||||
|
auto col_left_raw = block.getByPosition(arguments[0]).column.get();
|
||||||
|
auto col_right_raw = block.getByPosition(arguments[1]).column.get();
|
||||||
|
if (auto col_left = checkAndGetColumnConst<ColVecT0>(col_left_raw))
|
||||||
|
{
|
||||||
|
if (auto col_right = checkAndGetColumnConst<ColVecT1>(col_right_raw))
|
||||||
|
{
|
||||||
|
/// the only case with a non-vector result
|
||||||
|
if constexpr (result_is_decimal)
|
||||||
|
{
|
||||||
|
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
||||||
|
typename ResultDataType::FieldType scale_a = type.scaleFactorFor(left, is_multiply);
|
||||||
|
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
|
||||||
|
if constexpr (IsDataTypeDecimal<RightDataType> && is_division)
|
||||||
|
scale_a = right.getScaleMultiplier();
|
||||||
|
|
||||||
|
auto res = OpImpl::constant_constant(col_left->template getValue<T0>(), col_right->template getValue<T1>(),
|
||||||
|
scale_a, scale_b, check_decimal_overflow);
|
||||||
|
block.getByPosition(result).column =
|
||||||
|
ResultDataType(type.getPrecision(), type.getScale()).createColumnConst(
|
||||||
|
col_left->size(), toField(res, type.getScale()));
|
||||||
|
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto res = OpImpl::constant_constant(col_left->template getValue<T0>(), col_right->template getValue<T1>());
|
||||||
|
block.getByPosition(result).column = ResultDataType().createColumnConst(col_left->size(), toField(res));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
typename ColVecResult::MutablePtr col_res = nullptr;
|
||||||
|
if constexpr (result_is_decimal)
|
||||||
|
{
|
||||||
|
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
||||||
|
col_res = ColVecResult::create(0, type.getScale());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
col_res = ColVecResult::create();
|
||||||
|
|
||||||
|
auto & vec_res = col_res->getData();
|
||||||
|
vec_res.resize(block.rows());
|
||||||
|
|
||||||
|
if (auto col_left_const = checkAndGetColumnConst<ColVecT0>(col_left_raw))
|
||||||
|
{
|
||||||
|
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
|
||||||
|
{
|
||||||
|
if constexpr (result_is_decimal)
|
||||||
|
{
|
||||||
|
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
||||||
|
|
||||||
|
typename ResultDataType::FieldType scale_a = type.scaleFactorFor(left, is_multiply);
|
||||||
|
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
|
||||||
|
if constexpr (IsDataTypeDecimal<RightDataType> && is_division)
|
||||||
|
scale_a = right.getScaleMultiplier();
|
||||||
|
|
||||||
|
OpImpl::constant_vector(col_left_const->template getValue<T0>(), col_right->getData(), vec_res,
|
||||||
|
scale_a, scale_b, check_decimal_overflow);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
OpImpl::constant_vector(col_left_const->template getValue<T0>(), col_right->getData(), vec_res);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else if (auto col_left = checkAndGetColumn<ColVecT0>(col_left_raw))
|
||||||
|
{
|
||||||
|
if constexpr (result_is_decimal)
|
||||||
|
{
|
||||||
|
ResultDataType type = decimalResultType(left, right, is_multiply, is_division);
|
||||||
|
|
||||||
|
typename ResultDataType::FieldType scale_a = type.scaleFactorFor(left, is_multiply);
|
||||||
|
typename ResultDataType::FieldType scale_b = type.scaleFactorFor(right, is_multiply || is_division);
|
||||||
|
if constexpr (IsDataTypeDecimal<RightDataType> && is_division)
|
||||||
|
scale_a = right.getScaleMultiplier();
|
||||||
|
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
|
||||||
|
{
|
||||||
|
OpImpl::vector_vector(col_left->getData(), col_right->getData(), vec_res, scale_a, scale_b,
|
||||||
|
check_decimal_overflow);
|
||||||
|
}
|
||||||
|
else if (auto col_right_const = checkAndGetColumnConst<ColVecT1>(col_right_raw))
|
||||||
|
{
|
||||||
|
OpImpl::vector_constant(col_left->getData(), col_right_const->template getValue<T1>(), vec_res,
|
||||||
|
scale_a, scale_b, check_decimal_overflow);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (auto col_right = checkAndGetColumn<ColVecT1>(col_right_raw))
|
||||||
|
OpImpl::vector_vector(col_left->getData(), col_right->getData(), vec_res);
|
||||||
|
else if (auto col_right_const = checkAndGetColumnConst<ColVecT1>(col_right_raw))
|
||||||
|
OpImpl::vector_constant(col_left->getData(), col_right_const->template getValue<T1>(), vec_res);
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
block.getByPosition(result).column = std::move(col_res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (!valid)
|
||||||
|
throw Exception(getName() + "'s arguments do not match the expected data types", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
bool isCompilableImpl(const DataTypes & arguments) const override
|
||||||
|
{
|
||||||
|
return castBothTypes(arguments[0].get(), arguments[1].get(), [&](const auto & left, const auto & right)
|
||||||
|
{
|
||||||
|
using LeftDataType = std::decay_t<decltype(left)>;
|
||||||
|
using RightDataType = std::decay_t<decltype(right)>;
|
||||||
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
||||||
|
using OpSpec = Op<typename LeftDataType::FieldType, typename RightDataType::FieldType>;
|
||||||
|
return !std::is_same_v<ResultDataType, InvalidType> && !IsDataTypeDecimal<ResultDataType> && OpSpec::compilable;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
|
||||||
|
{
|
||||||
|
llvm::Value * result = nullptr;
|
||||||
|
castBothTypes(types[0].get(), types[1].get(), [&](const auto & left, const auto & right)
|
||||||
|
{
|
||||||
|
using LeftDataType = std::decay_t<decltype(left)>;
|
||||||
|
using RightDataType = std::decay_t<decltype(right)>;
|
||||||
|
using ResultDataType = typename BinaryOperationTraits<Op, LeftDataType, RightDataType>::ResultDataType;
|
||||||
|
using OpSpec = Op<typename LeftDataType::FieldType, typename RightDataType::FieldType>;
|
||||||
|
if constexpr (!std::is_same_v<ResultDataType, InvalidType> && !IsDataTypeDecimal<ResultDataType> && OpSpec::compilable)
|
||||||
|
{
|
||||||
|
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
||||||
|
auto type = std::make_shared<ResultDataType>();
|
||||||
|
auto * lval = nativeCast(b, types[0], values[0](), type);
|
||||||
|
auto * rval = nativeCast(b, types[1], values[1](), type);
|
||||||
|
result = OpSpec::compile(b, lval, rval, std::is_signed_v<typename ResultDataType::FieldType>);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool canBeExecutedOnDefaultArguments() const override { return CanBeExecutedOnDefaultArguments; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
204
dbms/src/Functions/FunctionBitTestMany.h
Normal file
204
dbms/src/Functions/FunctionBitTestMany.h
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DataTypes/DataTypesNumber.h>
|
||||||
|
#include <Columns/ColumnVector.h>
|
||||||
|
#include <Functions/IFunction.h>
|
||||||
|
#include <Functions/FunctionHelpers.h>
|
||||||
|
#include <IO/WriteHelpers.h>
|
||||||
|
#include <ext/range.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ErrorCodes
|
||||||
|
{
|
||||||
|
extern const int ILLEGAL_DIVISION;
|
||||||
|
extern const int ILLEGAL_COLUMN;
|
||||||
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||||
|
extern const int TOO_LESS_ARGUMENTS_FOR_FUNCTION;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Impl, typename Name>
|
||||||
|
struct FunctionBitTestMany : public IFunction
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static constexpr auto name = Name::name;
|
||||||
|
static FunctionPtr create(const Context &) { return std::make_shared<FunctionBitTestMany>(); }
|
||||||
|
|
||||||
|
String getName() const override { return name; }
|
||||||
|
|
||||||
|
bool isVariadic() const override { return true; }
|
||||||
|
size_t getNumberOfArguments() const override { return 0; }
|
||||||
|
|
||||||
|
DataTypePtr getReturnTypeImpl(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 at least 2.", ErrorCodes::TOO_LESS_ARGUMENTS_FOR_FUNCTION};
|
||||||
|
|
||||||
|
const auto & first_arg = arguments.front();
|
||||||
|
|
||||||
|
if (!isInteger(first_arg))
|
||||||
|
throw Exception{"Illegal type " + first_arg->getName() + " of first argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||||
|
|
||||||
|
|
||||||
|
for (const auto i : ext::range(1, arguments.size()))
|
||||||
|
{
|
||||||
|
const auto & pos_arg = arguments[i];
|
||||||
|
|
||||||
|
if (!isUnsignedInteger(pos_arg))
|
||||||
|
throw Exception{"Illegal type " + pos_arg->getName() + " of " + toString(i) + " argument of function " + getName(), ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT};
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_shared<DataTypeUInt8>();
|
||||||
|
}
|
||||||
|
|
||||||
|
void executeImpl(Block & block , const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
|
||||||
|
{
|
||||||
|
const auto value_col = block.getByPosition(arguments.front()).column.get();
|
||||||
|
|
||||||
|
if (!execute<UInt8>(block, arguments, result, value_col)
|
||||||
|
&& !execute<UInt16>(block, arguments, result, value_col)
|
||||||
|
&& !execute<UInt32>(block, arguments, result, value_col)
|
||||||
|
&& !execute<UInt64>(block, arguments, result, value_col)
|
||||||
|
&& !execute<Int8>(block, arguments, result, value_col)
|
||||||
|
&& !execute<Int16>(block, arguments, result, value_col)
|
||||||
|
&& !execute<Int32>(block, arguments, result, value_col)
|
||||||
|
&& !execute<Int64>(block, arguments, result, value_col))
|
||||||
|
throw Exception{"Illegal column " + value_col->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template <typename T>
|
||||||
|
bool execute(
|
||||||
|
Block & block, const ColumnNumbers & arguments, const size_t result,
|
||||||
|
const IColumn * const value_col_untyped)
|
||||||
|
{
|
||||||
|
if (const auto value_col = checkAndGetColumn<ColumnVector<T>>(value_col_untyped))
|
||||||
|
{
|
||||||
|
const auto size = value_col->size();
|
||||||
|
bool is_const;
|
||||||
|
const auto const_mask = createConstMaskIfConst<T>(block, arguments, is_const);
|
||||||
|
const auto & val = value_col->getData();
|
||||||
|
|
||||||
|
auto out_col = ColumnVector<UInt8>::create(size);
|
||||||
|
auto & out = out_col->getData();
|
||||||
|
|
||||||
|
if (is_const)
|
||||||
|
{
|
||||||
|
for (const auto i : ext::range(0, size))
|
||||||
|
out[i] = Impl::apply(val[i], const_mask);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto mask = createMask<T>(size, block, arguments);
|
||||||
|
|
||||||
|
for (const auto i : ext::range(0, size))
|
||||||
|
out[i] = Impl::apply(val[i], mask[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
block.getByPosition(result).column = std::move(out_col);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (const auto value_col_const = checkAndGetColumnConst<ColumnVector<T>>(value_col_untyped))
|
||||||
|
{
|
||||||
|
const auto size = value_col_const->size();
|
||||||
|
bool is_const;
|
||||||
|
const auto const_mask = createConstMaskIfConst<T>(block, arguments, is_const);
|
||||||
|
const auto val = value_col_const->template getValue<T>();
|
||||||
|
|
||||||
|
if (is_const)
|
||||||
|
{
|
||||||
|
block.getByPosition(result).column = block.getByPosition(result).type->createColumnConst(size, toField(Impl::apply(val, const_mask)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const auto mask = createMask<T>(size, block, arguments);
|
||||||
|
auto out_col = ColumnVector<UInt8>::create(size);
|
||||||
|
|
||||||
|
auto & out = out_col->getData();
|
||||||
|
|
||||||
|
for (const auto i : ext::range(0, size))
|
||||||
|
out[i] = Impl::apply(val, mask[i]);
|
||||||
|
|
||||||
|
block.getByPosition(result).column = std::move(out_col);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ValueType>
|
||||||
|
ValueType createConstMaskIfConst(const Block & block, const ColumnNumbers & arguments, bool & out_is_const)
|
||||||
|
{
|
||||||
|
out_is_const = true;
|
||||||
|
ValueType mask = 0;
|
||||||
|
|
||||||
|
for (const auto i : ext::range(1, arguments.size()))
|
||||||
|
{
|
||||||
|
if (auto pos_col_const = checkAndGetColumnConst<ColumnVector<ValueType>>(block.getByPosition(arguments[i]).column.get()))
|
||||||
|
{
|
||||||
|
const auto pos = pos_col_const->template getValue<ValueType>();
|
||||||
|
mask = mask | (1 << pos);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
out_is_const = false;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename ValueType>
|
||||||
|
PaddedPODArray<ValueType> createMask(const size_t size, const Block & block, const ColumnNumbers & arguments)
|
||||||
|
{
|
||||||
|
PaddedPODArray<ValueType> mask(size, ValueType{});
|
||||||
|
|
||||||
|
for (const auto i : ext::range(1, arguments.size()))
|
||||||
|
{
|
||||||
|
const auto pos_col = block.getByPosition(arguments[i]).column.get();
|
||||||
|
|
||||||
|
if (!addToMaskImpl<UInt8>(mask, pos_col)
|
||||||
|
&& !addToMaskImpl<UInt16>(mask, pos_col)
|
||||||
|
&& !addToMaskImpl<UInt32>(mask, pos_col)
|
||||||
|
&& !addToMaskImpl<UInt64>(mask, pos_col))
|
||||||
|
throw Exception{"Illegal column " + pos_col->getName() + " of argument of function " + getName(), ErrorCodes::ILLEGAL_COLUMN};
|
||||||
|
}
|
||||||
|
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename PosType, typename ValueType>
|
||||||
|
bool addToMaskImpl(PaddedPODArray<ValueType> & mask, const IColumn * const pos_col_untyped)
|
||||||
|
{
|
||||||
|
if (const auto pos_col = checkAndGetColumn<ColumnVector<PosType>>(pos_col_untyped))
|
||||||
|
{
|
||||||
|
const auto & pos = pos_col->getData();
|
||||||
|
|
||||||
|
for (const auto i : ext::range(0, mask.size()))
|
||||||
|
mask[i] = mask[i] | (1 << pos[i]);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else if (const auto pos_col_const = checkAndGetColumnConst<ColumnVector<PosType>>(pos_col_untyped))
|
||||||
|
{
|
||||||
|
const auto & pos = pos_col_const->template getValue<PosType>();
|
||||||
|
const auto new_mask = 1 << pos;
|
||||||
|
|
||||||
|
for (const auto i : ext::range(0, mask.size()))
|
||||||
|
mask[i] = mask[i] | new_mask;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
220
dbms/src/Functions/FunctionUnaryArithmetic.h
Normal file
220
dbms/src/Functions/FunctionUnaryArithmetic.h
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DataTypes/DataTypesNumber.h>
|
||||||
|
#include <DataTypes/DataTypesDecimal.h>
|
||||||
|
#include <DataTypes/Native.h>
|
||||||
|
#include <Columns/ColumnVector.h>
|
||||||
|
#include <Columns/ColumnDecimal.h>
|
||||||
|
#include <Functions/IFunction.h>
|
||||||
|
#include <Functions/FunctionHelpers.h>
|
||||||
|
#include <Functions/castTypeToEither.h>
|
||||||
|
#include <Common/config.h>
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
|
#include <llvm/IR/IRBuilder.h> // Y_IGNORE
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ErrorCodes
|
||||||
|
{
|
||||||
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||||
|
extern const int LOGICAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
template <typename A, typename Op>
|
||||||
|
struct UnaryOperationImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename Op::ResultType;
|
||||||
|
using ColVecA = std::conditional_t<IsDecimalNumber<A>, ColumnDecimal<A>, ColumnVector<A>>;
|
||||||
|
using ColVecC = std::conditional_t<IsDecimalNumber<ResultType>, ColumnDecimal<ResultType>, ColumnVector<ResultType>>;
|
||||||
|
using ArrayA = typename ColVecA::Container;
|
||||||
|
using ArrayC = typename ColVecC::Container;
|
||||||
|
|
||||||
|
static void NO_INLINE vector(const ArrayA & a, ArrayC & c)
|
||||||
|
{
|
||||||
|
size_t size = a.size();
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = Op::apply(a[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void constant(A a, ResultType & c)
|
||||||
|
{
|
||||||
|
c = Op::apply(a);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
template <typename FunctionName>
|
||||||
|
struct FunctionUnaryArithmeticMonotonicity;
|
||||||
|
|
||||||
|
template <typename> struct AbsImpl;
|
||||||
|
template <typename> struct NegateImpl;
|
||||||
|
|
||||||
|
/// Used to indicate undefined operation
|
||||||
|
struct InvalidType;
|
||||||
|
|
||||||
|
|
||||||
|
template <template <typename> class Op, typename Name, bool is_injective>
|
||||||
|
class FunctionUnaryArithmetic : public IFunction
|
||||||
|
{
|
||||||
|
static constexpr bool allow_decimal = std::is_same_v<Op<Int8>, NegateImpl<Int8>> || std::is_same_v<Op<Int8>, AbsImpl<Int8>>;
|
||||||
|
|
||||||
|
template <typename F>
|
||||||
|
static bool castType(const IDataType * type, F && f)
|
||||||
|
{
|
||||||
|
return castTypeToEither<
|
||||||
|
DataTypeUInt8,
|
||||||
|
DataTypeUInt16,
|
||||||
|
DataTypeUInt32,
|
||||||
|
DataTypeUInt64,
|
||||||
|
DataTypeInt8,
|
||||||
|
DataTypeInt16,
|
||||||
|
DataTypeInt32,
|
||||||
|
DataTypeInt64,
|
||||||
|
DataTypeFloat32,
|
||||||
|
DataTypeFloat64,
|
||||||
|
DataTypeDecimal<Decimal32>,
|
||||||
|
DataTypeDecimal<Decimal64>,
|
||||||
|
DataTypeDecimal<Decimal128>
|
||||||
|
>(type, std::forward<F>(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr auto name = Name::name;
|
||||||
|
static FunctionPtr create(const Context &) { return std::make_shared<FunctionUnaryArithmetic>(); }
|
||||||
|
|
||||||
|
String getName() const override
|
||||||
|
{
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getNumberOfArguments() const override { return 1; }
|
||||||
|
bool isInjective(const Block &) override { return is_injective; }
|
||||||
|
|
||||||
|
bool useDefaultImplementationForConstants() const override { return true; }
|
||||||
|
|
||||||
|
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
|
||||||
|
{
|
||||||
|
DataTypePtr result;
|
||||||
|
bool valid = castType(arguments[0].get(), [&](const auto & type)
|
||||||
|
{
|
||||||
|
using DataType = std::decay_t<decltype(type)>;
|
||||||
|
using T0 = typename DataType::FieldType;
|
||||||
|
|
||||||
|
if constexpr (IsDataTypeDecimal<DataType>)
|
||||||
|
{
|
||||||
|
if constexpr (!allow_decimal)
|
||||||
|
return false;
|
||||||
|
result = std::make_shared<DataType>(type.getPrecision(), type.getScale());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
result = std::make_shared<DataTypeNumber<typename Op<T0>::ResultType>>();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (!valid)
|
||||||
|
throw Exception("Illegal type " + arguments[0]->getName() + " of argument of function " + getName(),
|
||||||
|
ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void executeImpl(Block & block, const ColumnNumbers & arguments, size_t result, size_t /*input_rows_count*/) override
|
||||||
|
{
|
||||||
|
bool valid = castType(block.getByPosition(arguments[0]).type.get(), [&](const auto & type)
|
||||||
|
{
|
||||||
|
using DataType = std::decay_t<decltype(type)>;
|
||||||
|
using T0 = typename DataType::FieldType;
|
||||||
|
|
||||||
|
if constexpr (IsDataTypeDecimal<DataType>)
|
||||||
|
{
|
||||||
|
if constexpr (allow_decimal)
|
||||||
|
{
|
||||||
|
if (auto col = checkAndGetColumn<ColumnDecimal<T0>>(block.getByPosition(arguments[0]).column.get()))
|
||||||
|
{
|
||||||
|
auto col_res = ColumnDecimal<typename Op<T0>::ResultType>::create(0, type.getScale());
|
||||||
|
auto & vec_res = col_res->getData();
|
||||||
|
vec_res.resize(col->getData().size());
|
||||||
|
UnaryOperationImpl<T0, Op<T0>>::vector(col->getData(), vec_res);
|
||||||
|
block.getByPosition(result).column = std::move(col_res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (auto col = checkAndGetColumn<ColumnVector<T0>>(block.getByPosition(arguments[0]).column.get()))
|
||||||
|
{
|
||||||
|
auto col_res = ColumnVector<typename Op<T0>::ResultType>::create();
|
||||||
|
auto & vec_res = col_res->getData();
|
||||||
|
vec_res.resize(col->getData().size());
|
||||||
|
UnaryOperationImpl<T0, Op<T0>>::vector(col->getData(), vec_res);
|
||||||
|
block.getByPosition(result).column = std::move(col_res);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
if (!valid)
|
||||||
|
throw Exception(getName() + "'s argument does not match the expected data type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
bool isCompilableImpl(const DataTypes & arguments) const override
|
||||||
|
{
|
||||||
|
return castType(arguments[0].get(), [&](const auto & type)
|
||||||
|
{
|
||||||
|
using DataType = std::decay_t<decltype(type)>;
|
||||||
|
return !IsDataTypeDecimal<DataType> && Op<typename DataType::FieldType>::compilable;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Value * compileImpl(llvm::IRBuilderBase & builder, const DataTypes & types, ValuePlaceholders values) const override
|
||||||
|
{
|
||||||
|
llvm::Value * result = nullptr;
|
||||||
|
castType(types[0].get(), [&](const auto & type)
|
||||||
|
{
|
||||||
|
using DataType = std::decay_t<decltype(type)>;
|
||||||
|
using T0 = typename DataType::FieldType;
|
||||||
|
using T1 = typename Op<T0>::ResultType;
|
||||||
|
if constexpr (!std::is_same_v<T1, InvalidType> && !IsDataTypeDecimal<DataType> && Op<T0>::compilable)
|
||||||
|
{
|
||||||
|
auto & b = static_cast<llvm::IRBuilder<> &>(builder);
|
||||||
|
auto * v = nativeCast(b, types[0], values[0](), std::make_shared<DataTypeNumber<T1>>());
|
||||||
|
result = Op<T0>::compile(b, v, std::is_signed_v<T1>);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
bool hasInformationAboutMonotonicity() const override
|
||||||
|
{
|
||||||
|
return FunctionUnaryArithmeticMonotonicity<Name>::has();
|
||||||
|
}
|
||||||
|
|
||||||
|
Monotonicity getMonotonicityForRange(const IDataType &, const Field & left, const Field & right) const override
|
||||||
|
{
|
||||||
|
return FunctionUnaryArithmeticMonotonicity<Name>::get(left, right);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct PositiveMonotonicity
|
||||||
|
{
|
||||||
|
static bool has() { return true; }
|
||||||
|
static IFunction::Monotonicity get(const Field &, const Field &)
|
||||||
|
{
|
||||||
|
return { true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
@ -6,10 +6,6 @@ namespace DB
|
|||||||
|
|
||||||
void registerFunctionsRound(FunctionFactory & factory)
|
void registerFunctionsRound(FunctionFactory & factory)
|
||||||
{
|
{
|
||||||
factory.registerFunction<FunctionRoundToExp2>();
|
|
||||||
factory.registerFunction<FunctionRoundDuration>();
|
|
||||||
factory.registerFunction<FunctionRoundAge>();
|
|
||||||
|
|
||||||
factory.registerFunction<FunctionRound>("round", FunctionFactory::CaseInsensitive);
|
factory.registerFunction<FunctionRound>("round", FunctionFactory::CaseInsensitive);
|
||||||
factory.registerFunction<FunctionFloor>("floor", FunctionFactory::CaseInsensitive);
|
factory.registerFunction<FunctionFloor>("floor", FunctionFactory::CaseInsensitive);
|
||||||
factory.registerFunction<FunctionCeil>("ceil", FunctionFactory::CaseInsensitive);
|
factory.registerFunction<FunctionCeil>("ceil", FunctionFactory::CaseInsensitive);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Functions/FunctionsArithmetic.h>
|
#include <Functions/FunctionUnaryArithmetic.h>
|
||||||
#include <Functions/FunctionHelpers.h>
|
#include <Functions/FunctionHelpers.h>
|
||||||
#include <IO/WriteHelpers.h>
|
#include <IO/WriteHelpers.h>
|
||||||
|
|
||||||
@ -21,6 +21,9 @@ namespace DB
|
|||||||
namespace ErrorCodes
|
namespace ErrorCodes
|
||||||
{
|
{
|
||||||
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
|
||||||
|
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||||
|
extern const int ILLEGAL_COLUMN;
|
||||||
|
extern const int LOGICAL_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -38,120 +41,8 @@ namespace ErrorCodes
|
|||||||
* Type of the result is the type of argument.
|
* Type of the result is the type of argument.
|
||||||
* For integer arguments, when passing negative scale, overflow can occur.
|
* For integer arguments, when passing negative scale, overflow can occur.
|
||||||
* In that case, the behavior is implementation specific.
|
* In that case, the behavior is implementation specific.
|
||||||
*
|
|
||||||
* roundToExp2 - down to the nearest power of two (see below);
|
|
||||||
*
|
|
||||||
* Deprecated functions:
|
|
||||||
* roundDuration - down to the nearest of: 0, 1, 10, 30, 60, 120, 180, 240, 300, 600, 1200, 1800, 3600, 7200, 18000, 36000;
|
|
||||||
* roundAge - down to the nearest of: 0, 18, 25, 35, 45, 55.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline std::enable_if_t<std::is_integral_v<T> && (sizeof(T) <= sizeof(UInt32)), T>
|
|
||||||
roundDownToPowerOfTwo(T x)
|
|
||||||
{
|
|
||||||
return x <= 0 ? 0 : (T(1) << (31 - __builtin_clz(x)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline std::enable_if_t<std::is_integral_v<T> && (sizeof(T) == sizeof(UInt64)), T>
|
|
||||||
roundDownToPowerOfTwo(T x)
|
|
||||||
{
|
|
||||||
return x <= 0 ? 0 : (T(1) << (63 - __builtin_clzll(x)));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline std::enable_if_t<std::is_same_v<T, Float32>, T>
|
|
||||||
roundDownToPowerOfTwo(T x)
|
|
||||||
{
|
|
||||||
return ext::bit_cast<T>(ext::bit_cast<UInt32>(x) & ~((1ULL << 23) - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
inline std::enable_if_t<std::is_same_v<T, Float64>, T>
|
|
||||||
roundDownToPowerOfTwo(T x)
|
|
||||||
{
|
|
||||||
return ext::bit_cast<T>(ext::bit_cast<UInt64>(x) & ~((1ULL << 52) - 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
/** For integer data types:
|
|
||||||
* - if number is greater than zero, round it down to nearest power of two (example: roundToExp2(100) = 64, roundToExp2(64) = 64);
|
|
||||||
* - otherwise, return 0.
|
|
||||||
*
|
|
||||||
* For floating point data types: zero out mantissa, but leave exponent.
|
|
||||||
* - if number is greater than zero, round it down to nearest power of two (example: roundToExp2(3) = 2);
|
|
||||||
* - negative powers are also used (example: roundToExp2(0.7) = 0.5);
|
|
||||||
* - if number is zero, return zero;
|
|
||||||
* - if number is less than zero, the result is symmetrical: roundToExp2(x) = -roundToExp2(-x). (example: roundToExp2(-0.3) = -0.25);
|
|
||||||
*/
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct RoundToExp2Impl
|
|
||||||
{
|
|
||||||
using ResultType = T;
|
|
||||||
|
|
||||||
static inline T apply(T x)
|
|
||||||
{
|
|
||||||
return roundDownToPowerOfTwo<T>(x);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if USE_EMBEDDED_COMPILER
|
|
||||||
static constexpr bool compilable = false;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
template <typename A>
|
|
||||||
struct RoundDurationImpl
|
|
||||||
{
|
|
||||||
using ResultType = UInt16;
|
|
||||||
|
|
||||||
static inline ResultType apply(A x)
|
|
||||||
{
|
|
||||||
return x < 1 ? 0
|
|
||||||
: (x < 10 ? 1
|
|
||||||
: (x < 30 ? 10
|
|
||||||
: (x < 60 ? 30
|
|
||||||
: (x < 120 ? 60
|
|
||||||
: (x < 180 ? 120
|
|
||||||
: (x < 240 ? 180
|
|
||||||
: (x < 300 ? 240
|
|
||||||
: (x < 600 ? 300
|
|
||||||
: (x < 1200 ? 600
|
|
||||||
: (x < 1800 ? 1200
|
|
||||||
: (x < 3600 ? 1800
|
|
||||||
: (x < 7200 ? 3600
|
|
||||||
: (x < 18000 ? 7200
|
|
||||||
: (x < 36000 ? 18000
|
|
||||||
: 36000))))))))))))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if USE_EMBEDDED_COMPILER
|
|
||||||
static constexpr bool compilable = false;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename A>
|
|
||||||
struct RoundAgeImpl
|
|
||||||
{
|
|
||||||
using ResultType = UInt8;
|
|
||||||
|
|
||||||
static inline ResultType apply(A x)
|
|
||||||
{
|
|
||||||
return x < 1 ? 0
|
|
||||||
: (x < 18 ? 17
|
|
||||||
: (x < 25 ? 18
|
|
||||||
: (x < 35 ? 25
|
|
||||||
: (x < 45 ? 35
|
|
||||||
: (x < 55 ? 45
|
|
||||||
: 55)))));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if USE_EMBEDDED_COMPILER
|
|
||||||
static constexpr bool compilable = false;
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/** This parameter controls the behavior of the rounding functions.
|
/** This parameter controls the behavior of the rounding functions.
|
||||||
*/
|
*/
|
||||||
@ -665,36 +556,14 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct NameRoundToExp2 { static constexpr auto name = "roundToExp2"; };
|
|
||||||
struct NameRoundDuration { static constexpr auto name = "roundDuration"; };
|
|
||||||
struct NameRoundAge { static constexpr auto name = "roundAge"; };
|
|
||||||
|
|
||||||
struct NameRound { static constexpr auto name = "round"; };
|
struct NameRound { static constexpr auto name = "round"; };
|
||||||
struct NameCeil { static constexpr auto name = "ceil"; };
|
struct NameCeil { static constexpr auto name = "ceil"; };
|
||||||
struct NameFloor { static constexpr auto name = "floor"; };
|
struct NameFloor { static constexpr auto name = "floor"; };
|
||||||
struct NameTrunc { static constexpr auto name = "trunc"; };
|
struct NameTrunc { static constexpr auto name = "trunc"; };
|
||||||
|
|
||||||
using FunctionRoundToExp2 = FunctionUnaryArithmetic<RoundToExp2Impl, NameRoundToExp2, false>;
|
|
||||||
using FunctionRoundDuration = FunctionUnaryArithmetic<RoundDurationImpl, NameRoundDuration, false>;
|
|
||||||
using FunctionRoundAge = FunctionUnaryArithmetic<RoundAgeImpl, NameRoundAge, false>;
|
|
||||||
|
|
||||||
using FunctionRound = FunctionRounding<NameRound, RoundingMode::Round>;
|
using FunctionRound = FunctionRounding<NameRound, RoundingMode::Round>;
|
||||||
using FunctionFloor = FunctionRounding<NameFloor, RoundingMode::Floor>;
|
using FunctionFloor = FunctionRounding<NameFloor, RoundingMode::Floor>;
|
||||||
using FunctionCeil = FunctionRounding<NameCeil, RoundingMode::Ceil>;
|
using FunctionCeil = FunctionRounding<NameCeil, RoundingMode::Ceil>;
|
||||||
using FunctionTrunc = FunctionRounding<NameTrunc, RoundingMode::Trunc>;
|
using FunctionTrunc = FunctionRounding<NameTrunc, RoundingMode::Trunc>;
|
||||||
|
|
||||||
|
|
||||||
struct PositiveMonotonicity
|
|
||||||
{
|
|
||||||
static bool has() { return true; }
|
|
||||||
static IFunction::Monotonicity get(const Field &, const Field &)
|
|
||||||
{
|
|
||||||
return { true };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <> struct FunctionUnaryArithmeticMonotonicity<NameRoundToExp2> : PositiveMonotonicity {};
|
|
||||||
template <> struct FunctionUnaryArithmeticMonotonicity<NameRoundDuration> : PositiveMonotonicity {};
|
|
||||||
template <> struct FunctionUnaryArithmeticMonotonicity<NameRoundAge> : PositiveMonotonicity {};
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
54
dbms/src/Functions/abs.cpp
Normal file
54
dbms/src/Functions/abs.cpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionUnaryArithmetic.h>
|
||||||
|
#include <DataTypes/NumberTraits.h>
|
||||||
|
#include <Common/FieldVisitors.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
struct AbsImpl
|
||||||
|
{
|
||||||
|
using ResultType = std::conditional_t<IsDecimalNumber<A>, A, typename NumberTraits::ResultOfAbs<A>::Type>;
|
||||||
|
|
||||||
|
static inline ResultType apply(A a)
|
||||||
|
{
|
||||||
|
if constexpr (IsDecimalNumber<A>)
|
||||||
|
return a < 0 ? A(-a) : a;
|
||||||
|
else if constexpr (std::is_integral_v<A> && std::is_signed_v<A>)
|
||||||
|
return a < 0 ? static_cast<ResultType>(~a) + 1 : a;
|
||||||
|
else if constexpr (std::is_integral_v<A> && std::is_unsigned_v<A>)
|
||||||
|
return static_cast<ResultType>(a);
|
||||||
|
else if constexpr (std::is_floating_point_v<A>)
|
||||||
|
return static_cast<ResultType>(std::abs(a));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// special type handling, some other time
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameAbs { static constexpr auto name = "abs"; };
|
||||||
|
using FunctionAbs = FunctionUnaryArithmetic<AbsImpl, NameAbs, false>;
|
||||||
|
|
||||||
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameAbs>
|
||||||
|
{
|
||||||
|
static bool has() { return true; }
|
||||||
|
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
||||||
|
{
|
||||||
|
Float64 left_float = left.isNull() ? -std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
||||||
|
Float64 right_float = right.isNull() ? std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
||||||
|
|
||||||
|
if ((left_float < 0 && right_float > 0) || (left_float > 0 && right_float < 0))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return { true, (left_float > 0) };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void registerFunctionAbs(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionAbs>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
dbms/src/Functions/bitAnd.cpp
Normal file
38
dbms/src/Functions/bitAnd.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct BitAndImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) & static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
if (!left->getType()->isIntegerTy())
|
||||||
|
throw Exception("BitAndImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
return b.CreateAnd(left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitAnd { static constexpr auto name = "bitAnd"; };
|
||||||
|
using FunctionBitAnd = FunctionBinaryArithmetic<BitAndImpl, NameBitAnd>;
|
||||||
|
|
||||||
|
void registerFunctionBitAnd(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitAnd>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
dbms/src/Functions/bitNot.cpp
Normal file
47
dbms/src/Functions/bitNot.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionUnaryArithmetic.h>
|
||||||
|
#include <DataTypes/NumberTraits.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
struct BitNotImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfBitNot<A>::Type;
|
||||||
|
|
||||||
|
static inline ResultType apply(A a)
|
||||||
|
{
|
||||||
|
return ~static_cast<ResultType>(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * arg, bool)
|
||||||
|
{
|
||||||
|
if (!arg->getType()->isIntegerTy())
|
||||||
|
throw Exception("BitNotImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
return b.CreateNot(arg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitNot { static constexpr auto name = "bitNot"; };
|
||||||
|
using FunctionBitNot = FunctionUnaryArithmetic<BitNotImpl, NameBitNot, true>;
|
||||||
|
|
||||||
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameBitNot>
|
||||||
|
{
|
||||||
|
static bool has() { return false; }
|
||||||
|
static IFunction::Monotonicity get(const Field &, const Field &)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void registerFunctionBitNot(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitNot>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
dbms/src/Functions/bitOr.cpp
Normal file
38
dbms/src/Functions/bitOr.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct BitOrImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) | static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
if (!left->getType()->isIntegerTy())
|
||||||
|
throw Exception("BitOrImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
return b.CreateOr(left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitOr { static constexpr auto name = "bitOr"; };
|
||||||
|
using FunctionBitOr = FunctionBinaryArithmetic<BitOrImpl, NameBitOr>;
|
||||||
|
|
||||||
|
void registerFunctionBitOr(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitOr>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
41
dbms/src/Functions/bitRotateLeft.cpp
Normal file
41
dbms/src/Functions/bitRotateLeft.cpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct BitRotateLeftImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return (static_cast<Result>(a) << static_cast<Result>(b))
|
||||||
|
| (static_cast<Result>(a) >> ((sizeof(Result) * 8) - static_cast<Result>(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
if (!left->getType()->isIntegerTy())
|
||||||
|
throw Exception("BitRotateLeftImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
auto * size = llvm::ConstantInt::get(left->getType(), left->getType()->getPrimitiveSizeInBits());
|
||||||
|
/// XXX how is this supposed to behave in signed mode?
|
||||||
|
return b.CreateOr(b.CreateShl(left, right), b.CreateLShr(left, b.CreateSub(size, right)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitRotateLeft { static constexpr auto name = "bitRotateLeft"; };
|
||||||
|
using FunctionBitRotateLeft = FunctionBinaryArithmetic<BitRotateLeftImpl, NameBitRotateLeft>;
|
||||||
|
|
||||||
|
void registerFunctionBitRotateLeft(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitRotateLeft>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
40
dbms/src/Functions/bitRotateRight.cpp
Normal file
40
dbms/src/Functions/bitRotateRight.cpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct BitRotateRightImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return (static_cast<Result>(a) >> static_cast<Result>(b))
|
||||||
|
| (static_cast<Result>(a) << ((sizeof(Result) * 8) - static_cast<Result>(b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
if (!left->getType()->isIntegerTy())
|
||||||
|
throw Exception("BitRotateRightImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
auto * size = llvm::ConstantInt::get(left->getType(), left->getType()->getPrimitiveSizeInBits());
|
||||||
|
return b.CreateOr(b.CreateLShr(left, right), b.CreateShl(left, b.CreateSub(size, right)));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitRotateRight { static constexpr auto name = "bitRotateRight"; };
|
||||||
|
using FunctionBitRotateRight = FunctionBinaryArithmetic<BitRotateRightImpl, NameBitRotateRight>;
|
||||||
|
|
||||||
|
void registerFunctionBitRotateRight(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitRotateRight>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
dbms/src/Functions/bitShiftLeft.cpp
Normal file
38
dbms/src/Functions/bitShiftLeft.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct BitShiftLeftImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) << static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
if (!left->getType()->isIntegerTy())
|
||||||
|
throw Exception("BitShiftLeftImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
return b.CreateShl(left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitShiftLeft { static constexpr auto name = "bitShiftLeft"; };
|
||||||
|
using FunctionBitShiftLeft = FunctionBinaryArithmetic<BitShiftLeftImpl, NameBitShiftLeft>;
|
||||||
|
|
||||||
|
void registerFunctionBitShiftLeft(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitShiftLeft>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
dbms/src/Functions/bitShiftRight.cpp
Normal file
38
dbms/src/Functions/bitShiftRight.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct BitShiftRightImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) >> static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool is_signed)
|
||||||
|
{
|
||||||
|
if (!left->getType()->isIntegerTy())
|
||||||
|
throw Exception("BitShiftRightImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
return is_signed ? b.CreateAShr(left, right) : b.CreateLShr(left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitShiftRight { static constexpr auto name = "bitShiftRight"; };
|
||||||
|
using FunctionBitShiftRight = FunctionBinaryArithmetic<BitShiftRightImpl, NameBitShiftRight>;
|
||||||
|
|
||||||
|
void registerFunctionBitShiftRight(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitShiftRight>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
dbms/src/Functions/bitTest.cpp
Normal file
31
dbms/src/Functions/bitTest.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct BitTestImpl
|
||||||
|
{
|
||||||
|
using ResultType = UInt8;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return (typename NumberTraits::ToInteger<A>::Type(a) >> typename NumberTraits::ToInteger<B>::Type(b)) & 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// TODO
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitTest { static constexpr auto name = "bitTest"; };
|
||||||
|
using FunctionBitTest = FunctionBinaryArithmetic<BitTestImpl, NameBitTest>;
|
||||||
|
|
||||||
|
void registerFunctionBitTest(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitTest>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
21
dbms/src/Functions/bitTestAll.cpp
Normal file
21
dbms/src/Functions/bitTestAll.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBitTestMany.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
struct BitTestAllImpl
|
||||||
|
{
|
||||||
|
template <typename A, typename B>
|
||||||
|
static inline UInt8 apply(A a, B b) { return (a & b) == b; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitTestAll { static constexpr auto name = "bitTestAll"; };
|
||||||
|
using FunctionBitTestAll = FunctionBitTestMany<BitTestAllImpl, NameBitTestAll>;
|
||||||
|
|
||||||
|
void registerFunctionBitTestAll(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitTestAll>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
21
dbms/src/Functions/bitTestAny.cpp
Normal file
21
dbms/src/Functions/bitTestAny.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBitTestMany.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
struct BitTestAnyImpl
|
||||||
|
{
|
||||||
|
template <typename A, typename B>
|
||||||
|
static inline UInt8 apply(A a, B b) { return (a & b) != 0; }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitTestAny { static constexpr auto name = "bitTestAny"; };
|
||||||
|
using FunctionBitTestAny = FunctionBitTestMany<BitTestAnyImpl, NameBitTestAny>;
|
||||||
|
|
||||||
|
void registerFunctionBitTestAny(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitTestAny>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
dbms/src/Functions/bitXor.cpp
Normal file
38
dbms/src/Functions/bitXor.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct BitXorImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfBit<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) ^ static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
if (!left->getType()->isIntegerTy())
|
||||||
|
throw Exception("BitXorImpl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
return b.CreateXor(left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameBitXor { static constexpr auto name = "bitXor"; };
|
||||||
|
using FunctionBitXor = FunctionBinaryArithmetic<BitXorImpl, NameBitXor>;
|
||||||
|
|
||||||
|
void registerFunctionBitXor(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionBitXor>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
17
dbms/src/Functions/castTypeToEither.h
Normal file
17
dbms/src/Functions/castTypeToEither.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Common/typeid_cast.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
class IDataType;
|
||||||
|
|
||||||
|
template <typename... Ts, typename F>
|
||||||
|
static bool castTypeToEither(const IDataType * type, F && f)
|
||||||
|
{
|
||||||
|
/// XXX can't use && here because gcc-7 complains about parentheses around && within ||
|
||||||
|
return ((typeid_cast<const Ts *>(type) ? f(*typeid_cast<const Ts *>(type)) : false) || ...);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
39
dbms/src/Functions/divide.cpp
Normal file
39
dbms/src/Functions/divide.cpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct DivideFloatingImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfFloatingPointDivision<A, B>::Type;
|
||||||
|
static const constexpr bool allow_decimal = true;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
if (left->getType()->isIntegerTy())
|
||||||
|
throw Exception("DivideFloatingImpl expected a floating-point type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
return b.CreateFDiv(left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameDivide { static constexpr auto name = "divide"; };
|
||||||
|
using FunctionDivide = FunctionBinaryArithmetic<DivideFloatingImpl, NameDivide>;
|
||||||
|
|
||||||
|
void registerFunctionDivide(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionDivide>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
dbms/src/Functions/gcd.cpp
Normal file
36
dbms/src/Functions/gcd.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
#include <boost/integer/common_factor.hpp>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct GCDImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
|
||||||
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<B>::Type(b), typename NumberTraits::ToInteger<A>::Type(a));
|
||||||
|
return boost::integer::gcd(
|
||||||
|
typename NumberTraits::ToInteger<Result>::Type(a),
|
||||||
|
typename NumberTraits::ToInteger<Result>::Type(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// exceptions (and a non-trivial algorithm)
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameGCD { static constexpr auto name = "gcd"; };
|
||||||
|
using FunctionGCD = FunctionBinaryArithmetic<GCDImpl, NameGCD, false>;
|
||||||
|
|
||||||
|
void registerFunctionGCD(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionGCD>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
dbms/src/Functions/greatest.cpp
Normal file
61
dbms/src/Functions/greatest.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
#include <Core/AccurateComparison.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct GreatestBaseImpl
|
||||||
|
{
|
||||||
|
using ResultType = NumberTraits::ResultOfGreatest<A, B>;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) > static_cast<Result>(b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool is_signed)
|
||||||
|
{
|
||||||
|
if (!left->getType()->isIntegerTy())
|
||||||
|
/// XXX maxnum is basically fmax(), it may or may not match whatever apply() does
|
||||||
|
/// XXX CreateMaxNum is broken on LLVM 5.0 and 6.0 (generates minnum instead; fixed in 7)
|
||||||
|
return b.CreateBinaryIntrinsic(llvm::Intrinsic::maxnum, left, right);
|
||||||
|
return b.CreateSelect(is_signed ? b.CreateICmpSGT(left, right) : b.CreateICmpUGT(left, right), left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct GreatestSpecialImpl
|
||||||
|
{
|
||||||
|
using ResultType = std::make_unsigned_t<A>;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
static_assert(std::is_same_v<Result, ResultType>, "ResultType != Result");
|
||||||
|
return accurate::greaterOp(a, b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// ???
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
using GreatestImpl = std::conditional_t<!NumberTraits::LeastGreatestSpecialCase<A, B>, GreatestBaseImpl<A, B>, GreatestSpecialImpl<A, B>>;
|
||||||
|
|
||||||
|
struct NameGreatest { static constexpr auto name = "greatest"; };
|
||||||
|
using FunctionGreatest = FunctionBinaryArithmetic<GreatestImpl, NameGreatest>;
|
||||||
|
|
||||||
|
void registerFunctionGreatest(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionGreatest>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
104
dbms/src/Functions/intDiv.cpp
Normal file
104
dbms/src/Functions/intDiv.cpp
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
#include <Functions/intDiv.h>
|
||||||
|
|
||||||
|
#if __SSE2__
|
||||||
|
#define LIBDIVIDE_USE_SSE2 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <libdivide.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
/// Optimizations for integer division by a constant.
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct DivideIntegralByConstantImpl
|
||||||
|
: BinaryOperationImplBase<A, B, DivideIntegralImpl<A, B>>
|
||||||
|
{
|
||||||
|
using ResultType = typename DivideIntegralImpl<A, B>::ResultType;
|
||||||
|
|
||||||
|
static void vector_constant(const PaddedPODArray<A> & a, B b, PaddedPODArray<ResultType> & c)
|
||||||
|
{
|
||||||
|
if (unlikely(b == 0))
|
||||||
|
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||||
|
|
||||||
|
if (unlikely(std::is_signed_v<B> && b == -1))
|
||||||
|
{
|
||||||
|
size_t size = a.size();
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = -c[i];
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
libdivide::divider<A> divider(b);
|
||||||
|
|
||||||
|
size_t size = a.size();
|
||||||
|
const A * a_pos = a.data();
|
||||||
|
const A * a_end = a_pos + size;
|
||||||
|
ResultType * c_pos = c.data();
|
||||||
|
|
||||||
|
#if __SSE2__
|
||||||
|
static constexpr size_t values_per_sse_register = 16 / sizeof(A);
|
||||||
|
const A * a_end_sse = a_pos + size / values_per_sse_register * values_per_sse_register;
|
||||||
|
|
||||||
|
while (a_pos < a_end_sse)
|
||||||
|
{
|
||||||
|
_mm_storeu_si128(reinterpret_cast<__m128i *>(c_pos),
|
||||||
|
_mm_loadu_si128(reinterpret_cast<const __m128i *>(a_pos)) / divider);
|
||||||
|
|
||||||
|
a_pos += values_per_sse_register;
|
||||||
|
c_pos += values_per_sse_register;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while (a_pos < a_end)
|
||||||
|
{
|
||||||
|
*c_pos = *a_pos / divider;
|
||||||
|
++a_pos;
|
||||||
|
++c_pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Specializations are specified for dividing numbers of the type UInt64 and UInt32 by the numbers of the same sign.
|
||||||
|
* Can be expanded to all possible combinations, but more code is needed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <> struct BinaryOperationImpl<UInt64, UInt8, DivideIntegralImpl<UInt64, UInt8>> : DivideIntegralByConstantImpl<UInt64, UInt8> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt64, UInt16, DivideIntegralImpl<UInt64, UInt16>> : DivideIntegralByConstantImpl<UInt64, UInt16> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt64, UInt32, DivideIntegralImpl<UInt64, UInt32>> : DivideIntegralByConstantImpl<UInt64, UInt32> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt64, UInt64, DivideIntegralImpl<UInt64, UInt64>> : DivideIntegralByConstantImpl<UInt64, UInt64> {};
|
||||||
|
|
||||||
|
template <> struct BinaryOperationImpl<UInt32, UInt8, DivideIntegralImpl<UInt32, UInt8>> : DivideIntegralByConstantImpl<UInt32, UInt8> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt32, UInt16, DivideIntegralImpl<UInt32, UInt16>> : DivideIntegralByConstantImpl<UInt32, UInt16> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt32, UInt32, DivideIntegralImpl<UInt32, UInt32>> : DivideIntegralByConstantImpl<UInt32, UInt32> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt32, UInt64, DivideIntegralImpl<UInt32, UInt64>> : DivideIntegralByConstantImpl<UInt32, UInt64> {};
|
||||||
|
|
||||||
|
template <> struct BinaryOperationImpl<Int64, Int8, DivideIntegralImpl<Int64, Int8>> : DivideIntegralByConstantImpl<Int64, Int8> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int64, Int16, DivideIntegralImpl<Int64, Int16>> : DivideIntegralByConstantImpl<Int64, Int16> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int64, Int32, DivideIntegralImpl<Int64, Int32>> : DivideIntegralByConstantImpl<Int64, Int32> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int64, Int64, DivideIntegralImpl<Int64, Int64>> : DivideIntegralByConstantImpl<Int64, Int64> {};
|
||||||
|
|
||||||
|
template <> struct BinaryOperationImpl<Int32, Int8, DivideIntegralImpl<Int32, Int8>> : DivideIntegralByConstantImpl<Int32, Int8> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int32, Int16, DivideIntegralImpl<Int32, Int16>> : DivideIntegralByConstantImpl<Int32, Int16> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int32, Int32, DivideIntegralImpl<Int32, Int32>> : DivideIntegralByConstantImpl<Int32, Int32> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int32, Int64, DivideIntegralImpl<Int32, Int64>> : DivideIntegralByConstantImpl<Int32, Int64> {};
|
||||||
|
|
||||||
|
|
||||||
|
struct NameIntDiv { static constexpr auto name = "intDiv"; };
|
||||||
|
using FunctionIntDiv = FunctionBinaryArithmetic<DivideIntegralImpl, NameIntDiv, false>;
|
||||||
|
|
||||||
|
void registerFunctionIntDiv(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionIntDiv>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
66
dbms/src/Functions/intDiv.h
Normal file
66
dbms/src/Functions/intDiv.h
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <type_traits>
|
||||||
|
#include <common/likely.h>
|
||||||
|
#include <Common/Exception.h>
|
||||||
|
#include <Common/config.h>
|
||||||
|
#include <DataTypes/NumberTraits.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ErrorCodes
|
||||||
|
{
|
||||||
|
extern const int ILLEGAL_DIVISION;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
inline void throwIfDivisionLeadsToFPE(A a, B b)
|
||||||
|
{
|
||||||
|
/// Is it better to use siglongjmp instead of checks?
|
||||||
|
|
||||||
|
if (unlikely(b == 0))
|
||||||
|
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
|
||||||
|
|
||||||
|
/// http://avva.livejournal.com/2548306.html
|
||||||
|
if (unlikely(std::is_signed_v<A> && std::is_signed_v<B> && a == std::numeric_limits<A>::min() && b == -1))
|
||||||
|
throw Exception("Division of minimal signed number by minus one", ErrorCodes::ILLEGAL_DIVISION);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
inline bool divisionLeadsToFPE(A a, B b)
|
||||||
|
{
|
||||||
|
if (unlikely(b == 0))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (unlikely(std::is_signed_v<A> && std::is_signed_v<B> && a == std::numeric_limits<A>::min() && b == -1))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct DivideIntegralImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfIntegerDivision<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
throwIfDivisionLeadsToFPE(a, b);
|
||||||
|
return a / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// don't know how to throw from LLVM IR
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
31
dbms/src/Functions/intDivOrZero.cpp
Normal file
31
dbms/src/Functions/intDivOrZero.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct DivideIntegralOrZeroImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfIntegerDivision<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return unlikely(divisionLeadsToFPE(a, b)) ? 0 : a / b;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// TODO implement the checks
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameIntDivOrZero { static constexpr auto name = "intDivOrZero"; };
|
||||||
|
using FunctionIntDivOrZero = FunctionBinaryArithmetic<DivideIntegralOrZeroImpl, NameIntDivOrZero>;
|
||||||
|
|
||||||
|
void registerFunctionIntDivOrZero(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionIntDivOrZero>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
48
dbms/src/Functions/intExp10.cpp
Normal file
48
dbms/src/Functions/intExp10.cpp
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionUnaryArithmetic.h>
|
||||||
|
#include <Common/FieldVisitors.h>
|
||||||
|
#include <common/intExp.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
struct IntExp10Impl
|
||||||
|
{
|
||||||
|
using ResultType = UInt64;
|
||||||
|
|
||||||
|
static inline ResultType apply(A a)
|
||||||
|
{
|
||||||
|
return intExp10(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// library function
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameIntExp10 { static constexpr auto name = "intExp10"; };
|
||||||
|
/// Assumed to be injective for the purpose of query optimization, but in fact it is not injective because of possible overflow.
|
||||||
|
using FunctionIntExp10 = FunctionUnaryArithmetic<IntExp10Impl, NameIntExp10, true>;
|
||||||
|
|
||||||
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameIntExp10>
|
||||||
|
{
|
||||||
|
static bool has() { return true; }
|
||||||
|
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
||||||
|
{
|
||||||
|
Float64 left_float = left.isNull() ? -std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
||||||
|
Float64 right_float = right.isNull() ? std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
||||||
|
|
||||||
|
if (left_float < 0 || right_float > 19)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return { true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void registerFunctionIntExp10(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionIntExp10>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
55
dbms/src/Functions/intExp2.cpp
Normal file
55
dbms/src/Functions/intExp2.cpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionUnaryArithmetic.h>
|
||||||
|
#include <Common/FieldVisitors.h>
|
||||||
|
#include <common/intExp.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
struct IntExp2Impl
|
||||||
|
{
|
||||||
|
using ResultType = UInt64;
|
||||||
|
|
||||||
|
static inline ResultType apply(A a)
|
||||||
|
{
|
||||||
|
return intExp2(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * arg, bool)
|
||||||
|
{
|
||||||
|
if (!arg->getType()->isIntegerTy())
|
||||||
|
throw Exception("IntExp2Impl expected an integral type", ErrorCodes::LOGICAL_ERROR);
|
||||||
|
return b.CreateShl(llvm::ConstantInt::get(arg->getType(), 1), arg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Assumed to be injective for the purpose of query optimization, but in fact it is not injective because of possible overflow.
|
||||||
|
struct NameIntExp2 { static constexpr auto name = "intExp2"; };
|
||||||
|
using FunctionIntExp2 = FunctionUnaryArithmetic<IntExp2Impl, NameIntExp2, true>;
|
||||||
|
|
||||||
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameIntExp2>
|
||||||
|
{
|
||||||
|
static bool has() { return true; }
|
||||||
|
static IFunction::Monotonicity get(const Field & left, const Field & right)
|
||||||
|
{
|
||||||
|
Float64 left_float = left.isNull() ? -std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), left);
|
||||||
|
Float64 right_float = right.isNull() ? std::numeric_limits<Float64>::infinity() : applyVisitor(FieldVisitorConvertToNumber<Float64>(), right);
|
||||||
|
|
||||||
|
if (left_float < 0 || right_float > 63)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return { true };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void registerFunctionIntExp2(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionIntExp2>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
36
dbms/src/Functions/lcm.cpp
Normal file
36
dbms/src/Functions/lcm.cpp
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
#include <boost/integer/common_factor.hpp>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct LCMImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
|
||||||
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<B>::Type(b), typename NumberTraits::ToInteger<A>::Type(a));
|
||||||
|
return boost::integer::lcm(
|
||||||
|
typename NumberTraits::ToInteger<Result>::Type(a),
|
||||||
|
typename NumberTraits::ToInteger<Result>::Type(b));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// exceptions (and a non-trivial algorithm)
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameLCM { static constexpr auto name = "lcm"; };
|
||||||
|
using FunctionLCM = FunctionBinaryArithmetic<LCMImpl, NameLCM, false>;
|
||||||
|
|
||||||
|
void registerFunctionLCM(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionLCM>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
61
dbms/src/Functions/least.cpp
Normal file
61
dbms/src/Functions/least.cpp
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
#include <Core/AccurateComparison.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct LeastBaseImpl
|
||||||
|
{
|
||||||
|
using ResultType = NumberTraits::ResultOfLeast<A, B>;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
/** gcc 4.9.2 successfully vectorizes a loop from this function. */
|
||||||
|
return static_cast<Result>(a) < static_cast<Result>(b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool is_signed)
|
||||||
|
{
|
||||||
|
if (!left->getType()->isIntegerTy())
|
||||||
|
/// XXX minnum is basically fmin(), it may or may not match whatever apply() does
|
||||||
|
return b.CreateMinNum(left, right);
|
||||||
|
return b.CreateSelect(is_signed ? b.CreateICmpSLT(left, right) : b.CreateICmpULT(left, right), left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct LeastSpecialImpl
|
||||||
|
{
|
||||||
|
using ResultType = std::make_signed_t<A>;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
static_assert(std::is_same_v<Result, ResultType>, "ResultType != Result");
|
||||||
|
return accurate::lessOp(a, b) ? static_cast<Result>(a) : static_cast<Result>(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// ???
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
using LeastImpl = std::conditional_t<!NumberTraits::LeastGreatestSpecialCase<A, B>, LeastBaseImpl<A, B>, LeastSpecialImpl<A, B>>;
|
||||||
|
|
||||||
|
struct NameLeast { static constexpr auto name = "least"; };
|
||||||
|
using FunctionLeast = FunctionBinaryArithmetic<LeastImpl, NameLeast>;
|
||||||
|
|
||||||
|
void registerFunctionLeast(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionLeast>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
dbms/src/Functions/minus.cpp
Normal file
45
dbms/src/Functions/minus.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
#include <common/arithmeticOverflow.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct MinusImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfSubtraction<A, B>::Type;
|
||||||
|
static const constexpr bool allow_decimal = true;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply operation and check overflow. It's used for Deciamal operations. @returns true if overflowed, false othervise.
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline bool apply(A a, B b, Result & c)
|
||||||
|
{
|
||||||
|
return common::subOverflow(static_cast<Result>(a), b, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
return left->getType()->isIntegerTy() ? b.CreateSub(left, right) : b.CreateFSub(left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameMinus { static constexpr auto name = "minus"; };
|
||||||
|
using FunctionMinus = FunctionBinaryArithmetic<MinusImpl, NameMinus>;
|
||||||
|
|
||||||
|
void registerFunctionMinus(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionMinus>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
102
dbms/src/Functions/modulo.cpp
Normal file
102
dbms/src/Functions/modulo.cpp
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
|
||||||
|
#if __SSE2__
|
||||||
|
#define LIBDIVIDE_USE_SSE2 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <libdivide.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
namespace ErrorCodes
|
||||||
|
{
|
||||||
|
extern const int ILLEGAL_DIVISION;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct ModuloImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfModulo<A, B>::Type;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
throwIfDivisionLeadsToFPE(typename NumberTraits::ToInteger<A>::Type(a), typename NumberTraits::ToInteger<B>::Type(b));
|
||||||
|
return typename NumberTraits::ToInteger<A>::Type(a) % typename NumberTraits::ToInteger<B>::Type(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false; /// don't know how to throw from LLVM IR
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct ModuloByConstantImpl
|
||||||
|
: BinaryOperationImplBase<A, B, ModuloImpl<A, B>>
|
||||||
|
{
|
||||||
|
using ResultType = typename ModuloImpl<A, B>::ResultType;
|
||||||
|
|
||||||
|
static void vector_constant(const PaddedPODArray<A> & a, B b, PaddedPODArray<ResultType> & c)
|
||||||
|
{
|
||||||
|
if (unlikely(b == 0))
|
||||||
|
throw Exception("Division by zero", ErrorCodes::ILLEGAL_DIVISION);
|
||||||
|
|
||||||
|
#pragma GCC diagnostic push
|
||||||
|
#pragma GCC diagnostic ignored "-Wsign-compare"
|
||||||
|
|
||||||
|
if (unlikely((std::is_signed_v<B> && b == -1) || b == 1))
|
||||||
|
{
|
||||||
|
size_t size = a.size();
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma GCC diagnostic pop
|
||||||
|
|
||||||
|
libdivide::divider<A> divider(b);
|
||||||
|
|
||||||
|
/// Here we failed to make the SSE variant from libdivide give an advantage.
|
||||||
|
size_t size = a.size();
|
||||||
|
for (size_t i = 0; i < size; ++i)
|
||||||
|
c[i] = a[i] - (a[i] / divider) * b; /// NOTE: perhaps, the division semantics with the remainder of negative numbers is not preserved.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Specializations are specified for dividing numbers of the type UInt64 and UInt32 by the numbers of the same sign.
|
||||||
|
* Can be expanded to all possible combinations, but more code is needed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <> struct BinaryOperationImpl<UInt64, UInt8, ModuloImpl<UInt64, UInt8>> : ModuloByConstantImpl<UInt64, UInt8> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt64, UInt16, ModuloImpl<UInt64, UInt16>> : ModuloByConstantImpl<UInt64, UInt16> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt64, UInt32, ModuloImpl<UInt64, UInt32>> : ModuloByConstantImpl<UInt64, UInt32> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt64, UInt64, ModuloImpl<UInt64, UInt64>> : ModuloByConstantImpl<UInt64, UInt64> {};
|
||||||
|
|
||||||
|
template <> struct BinaryOperationImpl<UInt32, UInt8, ModuloImpl<UInt32, UInt8>> : ModuloByConstantImpl<UInt32, UInt8> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt32, UInt16, ModuloImpl<UInt32, UInt16>> : ModuloByConstantImpl<UInt32, UInt16> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt32, UInt32, ModuloImpl<UInt32, UInt32>> : ModuloByConstantImpl<UInt32, UInt32> {};
|
||||||
|
template <> struct BinaryOperationImpl<UInt32, UInt64, ModuloImpl<UInt32, UInt64>> : ModuloByConstantImpl<UInt32, UInt64> {};
|
||||||
|
|
||||||
|
template <> struct BinaryOperationImpl<Int64, Int8, ModuloImpl<Int64, Int8>> : ModuloByConstantImpl<Int64, Int8> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int64, Int16, ModuloImpl<Int64, Int16>> : ModuloByConstantImpl<Int64, Int16> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int64, Int32, ModuloImpl<Int64, Int32>> : ModuloByConstantImpl<Int64, Int32> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int64, Int64, ModuloImpl<Int64, Int64>> : ModuloByConstantImpl<Int64, Int64> {};
|
||||||
|
|
||||||
|
template <> struct BinaryOperationImpl<Int32, Int8, ModuloImpl<Int32, Int8>> : ModuloByConstantImpl<Int32, Int8> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int32, Int16, ModuloImpl<Int32, Int16>> : ModuloByConstantImpl<Int32, Int16> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int32, Int32, ModuloImpl<Int32, Int32>> : ModuloByConstantImpl<Int32, Int32> {};
|
||||||
|
template <> struct BinaryOperationImpl<Int32, Int64, ModuloImpl<Int32, Int64>> : ModuloByConstantImpl<Int32, Int64> {};
|
||||||
|
|
||||||
|
|
||||||
|
struct NameModulo { static constexpr auto name = "modulo"; };
|
||||||
|
using FunctionModulo = FunctionBinaryArithmetic<ModuloImpl, NameModulo, false>;
|
||||||
|
|
||||||
|
void registerFunctionModulo(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionModulo>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
dbms/src/Functions/multiply.cpp
Normal file
45
dbms/src/Functions/multiply.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
#include <common/arithmeticOverflow.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct MultiplyImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
||||||
|
static const constexpr bool allow_decimal = true;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
return static_cast<Result>(a) * b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply operation and check overflow. It's used for Deciamal operations. @returns true if overflowed, false othervise.
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline bool apply(A a, B b, Result & c)
|
||||||
|
{
|
||||||
|
return common::mulOverflow(static_cast<Result>(a), b, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
return left->getType()->isIntegerTy() ? b.CreateMul(left, right) : b.CreateFMul(left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameMultiply { static constexpr auto name = "multiply"; };
|
||||||
|
using FunctionMultiply = FunctionBinaryArithmetic<MultiplyImpl, NameMultiply>;
|
||||||
|
|
||||||
|
void registerFunctionMultiply(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionMultiply>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
45
dbms/src/Functions/negate.cpp
Normal file
45
dbms/src/Functions/negate.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionUnaryArithmetic.h>
|
||||||
|
#include <DataTypes/NumberTraits.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
struct NegateImpl
|
||||||
|
{
|
||||||
|
using ResultType = std::conditional_t<IsDecimalNumber<A>, A, typename NumberTraits::ResultOfNegate<A>::Type>;
|
||||||
|
|
||||||
|
static inline ResultType apply(A a)
|
||||||
|
{
|
||||||
|
return -static_cast<ResultType>(a);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * arg, bool)
|
||||||
|
{
|
||||||
|
return arg->getType()->isIntegerTy() ? b.CreateNeg(arg) : b.CreateFNeg(arg);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameNegate { static constexpr auto name = "negate"; };
|
||||||
|
using FunctionNegate = FunctionUnaryArithmetic<NegateImpl, NameNegate, true>;
|
||||||
|
|
||||||
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameNegate>
|
||||||
|
{
|
||||||
|
static bool has() { return true; }
|
||||||
|
static IFunction::Monotonicity get(const Field &, const Field &)
|
||||||
|
{
|
||||||
|
return { true, false };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void registerFunctionNegate(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionNegate>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
dbms/src/Functions/plus.cpp
Normal file
46
dbms/src/Functions/plus.cpp
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionBinaryArithmetic.h>
|
||||||
|
#include <common/arithmeticOverflow.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A, typename B>
|
||||||
|
struct PlusImpl
|
||||||
|
{
|
||||||
|
using ResultType = typename NumberTraits::ResultOfAdditionMultiplication<A, B>::Type;
|
||||||
|
static const constexpr bool allow_decimal = true;
|
||||||
|
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline Result apply(A a, B b)
|
||||||
|
{
|
||||||
|
/// Next everywhere, static_cast - so that there is no wrong result in expressions of the form Int64 c = UInt32(a) * Int32(-1).
|
||||||
|
return static_cast<Result>(a) + b;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Apply operation and check overflow. It's used for Deciamal operations. @returns true if overflowed, false othervise.
|
||||||
|
template <typename Result = ResultType>
|
||||||
|
static inline bool apply(A a, B b, Result & c)
|
||||||
|
{
|
||||||
|
return common::addOverflow(static_cast<Result>(a), b, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = true;
|
||||||
|
|
||||||
|
static inline llvm::Value * compile(llvm::IRBuilder<> & b, llvm::Value * left, llvm::Value * right, bool)
|
||||||
|
{
|
||||||
|
return left->getType()->isIntegerTy() ? b.CreateAdd(left, right) : b.CreateFAdd(left, right);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NamePlus { static constexpr auto name = "plus"; };
|
||||||
|
using FunctionPlus = FunctionBinaryArithmetic<PlusImpl, NamePlus>;
|
||||||
|
|
||||||
|
void registerFunctionPlus(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionPlus>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
69
dbms/src/Functions/registerFunctionsArithmetic.cpp
Normal file
69
dbms/src/Functions/registerFunctionsArithmetic.cpp
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
class FunctionFactory;
|
||||||
|
|
||||||
|
void registerFunctionPlus(FunctionFactory & factory);
|
||||||
|
void registerFunctionMinus(FunctionFactory & factory);
|
||||||
|
void registerFunctionMultiply(FunctionFactory & factory);
|
||||||
|
void registerFunctionDivide(FunctionFactory & factory);
|
||||||
|
void registerFunctionIntDiv(FunctionFactory & factory);
|
||||||
|
void registerFunctionIntDivOrZero(FunctionFactory & factory);
|
||||||
|
void registerFunctionModulo(FunctionFactory & factory);
|
||||||
|
void registerFunctionNegate(FunctionFactory & factory);
|
||||||
|
void registerFunctionAbs(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitAnd(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitOr(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitXor(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitNot(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitShiftLeft(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitShiftRight(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitRotateLeft(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitRotateRight(FunctionFactory & factory);
|
||||||
|
void registerFunctionLeast(FunctionFactory & factory);
|
||||||
|
void registerFunctionGreatest(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitTest(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitTestAny(FunctionFactory & factory);
|
||||||
|
void registerFunctionBitTestAll(FunctionFactory & factory);
|
||||||
|
void registerFunctionGCD(FunctionFactory & factory);
|
||||||
|
void registerFunctionLCM(FunctionFactory & factory);
|
||||||
|
void registerFunctionIntExp2(FunctionFactory & factory);
|
||||||
|
void registerFunctionIntExp10(FunctionFactory & factory);
|
||||||
|
void registerFunctionRoundToExp2(FunctionFactory & factory);
|
||||||
|
void registerFunctionRoundDuration(FunctionFactory & factory);
|
||||||
|
void registerFunctionRoundAge(FunctionFactory & factory);
|
||||||
|
|
||||||
|
void registerFunctionsArithmetic(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
registerFunctionPlus(factory);
|
||||||
|
registerFunctionMinus(factory);
|
||||||
|
registerFunctionMultiply(factory);
|
||||||
|
registerFunctionDivide(factory);
|
||||||
|
registerFunctionIntDiv(factory);
|
||||||
|
registerFunctionIntDivOrZero(factory);
|
||||||
|
registerFunctionModulo(factory);
|
||||||
|
registerFunctionNegate(factory);
|
||||||
|
registerFunctionAbs(factory);
|
||||||
|
registerFunctionBitAnd(factory);
|
||||||
|
registerFunctionBitOr(factory);
|
||||||
|
registerFunctionBitXor(factory);
|
||||||
|
registerFunctionBitNot(factory);
|
||||||
|
registerFunctionBitShiftLeft(factory);
|
||||||
|
registerFunctionBitShiftRight(factory);
|
||||||
|
registerFunctionBitRotateLeft(factory);
|
||||||
|
registerFunctionBitRotateRight(factory);
|
||||||
|
registerFunctionLeast(factory);
|
||||||
|
registerFunctionGreatest(factory);
|
||||||
|
registerFunctionBitTest(factory);
|
||||||
|
registerFunctionBitTestAny(factory);
|
||||||
|
registerFunctionBitTestAll(factory);
|
||||||
|
registerFunctionGCD(factory);
|
||||||
|
registerFunctionLCM(factory);
|
||||||
|
registerFunctionIntExp2(factory);
|
||||||
|
registerFunctionIntExp10(factory);
|
||||||
|
registerFunctionRoundToExp2(factory);
|
||||||
|
registerFunctionRoundDuration(factory);
|
||||||
|
registerFunctionRoundAge(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
dbms/src/Functions/roundAge.cpp
Normal file
38
dbms/src/Functions/roundAge.cpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionUnaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
struct RoundAgeImpl
|
||||||
|
{
|
||||||
|
using ResultType = UInt8;
|
||||||
|
|
||||||
|
static inline ResultType apply(A x)
|
||||||
|
{
|
||||||
|
return x < 1 ? 0
|
||||||
|
: (x < 18 ? 17
|
||||||
|
: (x < 25 ? 18
|
||||||
|
: (x < 35 ? 25
|
||||||
|
: (x < 45 ? 35
|
||||||
|
: (x < 55 ? 45
|
||||||
|
: 55)))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameRoundAge { static constexpr auto name = "roundAge"; };
|
||||||
|
using FunctionRoundAge = FunctionUnaryArithmetic<RoundAgeImpl, NameRoundAge, false>;
|
||||||
|
|
||||||
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameRoundAge> : PositiveMonotonicity {};
|
||||||
|
|
||||||
|
void registerFunctionRoundAge(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionRoundAge>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
47
dbms/src/Functions/roundDuration.cpp
Normal file
47
dbms/src/Functions/roundDuration.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionUnaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename A>
|
||||||
|
struct RoundDurationImpl
|
||||||
|
{
|
||||||
|
using ResultType = UInt16;
|
||||||
|
|
||||||
|
static inline ResultType apply(A x)
|
||||||
|
{
|
||||||
|
return x < 1 ? 0
|
||||||
|
: (x < 10 ? 1
|
||||||
|
: (x < 30 ? 10
|
||||||
|
: (x < 60 ? 30
|
||||||
|
: (x < 120 ? 60
|
||||||
|
: (x < 180 ? 120
|
||||||
|
: (x < 240 ? 180
|
||||||
|
: (x < 300 ? 240
|
||||||
|
: (x < 600 ? 300
|
||||||
|
: (x < 1200 ? 600
|
||||||
|
: (x < 1800 ? 1200
|
||||||
|
: (x < 3600 ? 1800
|
||||||
|
: (x < 7200 ? 3600
|
||||||
|
: (x < 18000 ? 7200
|
||||||
|
: (x < 36000 ? 18000
|
||||||
|
: 36000))))))))))))));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameRoundDuration { static constexpr auto name = "roundDuration"; };
|
||||||
|
using FunctionRoundDuration = FunctionUnaryArithmetic<RoundDurationImpl, NameRoundDuration, false>;
|
||||||
|
|
||||||
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameRoundDuration> : PositiveMonotonicity {};
|
||||||
|
|
||||||
|
void registerFunctionRoundDuration(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionRoundDuration>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
73
dbms/src/Functions/roundToExp2.cpp
Normal file
73
dbms/src/Functions/roundToExp2.cpp
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
#include <type_traits>
|
||||||
|
#include <ext/bit_cast.h>
|
||||||
|
#include <Functions/FunctionFactory.h>
|
||||||
|
#include <Functions/FunctionUnaryArithmetic.h>
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::enable_if_t<std::is_integral_v<T> && (sizeof(T) <= sizeof(UInt32)), T>
|
||||||
|
roundDownToPowerOfTwo(T x)
|
||||||
|
{
|
||||||
|
return x <= 0 ? 0 : (T(1) << (31 - __builtin_clz(x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::enable_if_t<std::is_integral_v<T> && (sizeof(T) == sizeof(UInt64)), T>
|
||||||
|
roundDownToPowerOfTwo(T x)
|
||||||
|
{
|
||||||
|
return x <= 0 ? 0 : (T(1) << (63 - __builtin_clzll(x)));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::enable_if_t<std::is_same_v<T, Float32>, T>
|
||||||
|
roundDownToPowerOfTwo(T x)
|
||||||
|
{
|
||||||
|
return ext::bit_cast<T>(ext::bit_cast<UInt32>(x) & ~((1ULL << 23) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
inline std::enable_if_t<std::is_same_v<T, Float64>, T>
|
||||||
|
roundDownToPowerOfTwo(T x)
|
||||||
|
{
|
||||||
|
return ext::bit_cast<T>(ext::bit_cast<UInt64>(x) & ~((1ULL << 52) - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
/** For integer data types:
|
||||||
|
* - if number is greater than zero, round it down to nearest power of two (example: roundToExp2(100) = 64, roundToExp2(64) = 64);
|
||||||
|
* - otherwise, return 0.
|
||||||
|
*
|
||||||
|
* For floating point data types: zero out mantissa, but leave exponent.
|
||||||
|
* - if number is greater than zero, round it down to nearest power of two (example: roundToExp2(3) = 2);
|
||||||
|
* - negative powers are also used (example: roundToExp2(0.7) = 0.5);
|
||||||
|
* - if number is zero, return zero;
|
||||||
|
* - if number is less than zero, the result is symmetrical: roundToExp2(x) = -roundToExp2(-x). (example: roundToExp2(-0.3) = -0.25);
|
||||||
|
*/
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct RoundToExp2Impl
|
||||||
|
{
|
||||||
|
using ResultType = T;
|
||||||
|
|
||||||
|
static inline T apply(T x)
|
||||||
|
{
|
||||||
|
return roundDownToPowerOfTwo<T>(x);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if USE_EMBEDDED_COMPILER
|
||||||
|
static constexpr bool compilable = false;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NameRoundToExp2 { static constexpr auto name = "roundToExp2"; };
|
||||||
|
using FunctionRoundToExp2 = FunctionUnaryArithmetic<RoundToExp2Impl, NameRoundToExp2, false>;
|
||||||
|
|
||||||
|
template <> struct FunctionUnaryArithmeticMonotonicity<NameRoundToExp2> : PositiveMonotonicity {};
|
||||||
|
|
||||||
|
void registerFunctionRoundToExp2(FunctionFactory & factory)
|
||||||
|
{
|
||||||
|
factory.registerFunction<FunctionRoundToExp2>();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -51,7 +51,7 @@ void WriteBufferFromPocoSocket::nextImpl()
|
|||||||
}
|
}
|
||||||
catch (const Poco::IOException & e)
|
catch (const Poco::IOException & e)
|
||||||
{
|
{
|
||||||
throw NetException(e.displayText() + ", while reading from socket (" + peer_address.toString() + ")", ErrorCodes::NETWORK_ERROR);
|
throw NetException(e.displayText() + ", while writing to socket (" + peer_address.toString() + ")", ErrorCodes::NETWORK_ERROR);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (res < 0)
|
if (res < 0)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#include <Interpreters/ExternalLoader.h>
|
#include "ExternalLoader.h"
|
||||||
|
#include <Core/Defines.h>
|
||||||
#include <Common/StringUtils/StringUtils.h>
|
#include <Common/StringUtils/StringUtils.h>
|
||||||
#include <Common/MemoryTracker.h>
|
#include <Common/MemoryTracker.h>
|
||||||
#include <Common/Exception.h>
|
#include <Common/Exception.h>
|
||||||
@ -42,12 +43,12 @@ void ExternalLoader::reloadPeriodically()
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
ExternalLoader::ExternalLoader(const Poco::Util::AbstractConfiguration & config,
|
ExternalLoader::ExternalLoader(const Poco::Util::AbstractConfiguration & config_main,
|
||||||
const ExternalLoaderUpdateSettings & update_settings,
|
const ExternalLoaderUpdateSettings & update_settings,
|
||||||
const ExternalLoaderConfigSettings & config_settings,
|
const ExternalLoaderConfigSettings & config_settings,
|
||||||
std::unique_ptr<IExternalLoaderConfigRepository> config_repository,
|
std::unique_ptr<IExternalLoaderConfigRepository> config_repository,
|
||||||
Logger * log, const std::string & loadable_object_name)
|
Logger * log, const std::string & loadable_object_name)
|
||||||
: config(config)
|
: config_main(config_main)
|
||||||
, update_settings(update_settings)
|
, update_settings(update_settings)
|
||||||
, config_settings(config_settings)
|
, config_settings(config_settings)
|
||||||
, config_repository(std::move(config_repository))
|
, config_repository(std::move(config_repository))
|
||||||
@ -214,7 +215,7 @@ void ExternalLoader::reloadAndUpdate(bool throw_on_error)
|
|||||||
|
|
||||||
void ExternalLoader::reloadFromConfigFiles(const bool throw_on_error, const bool force_reload, const std::string & only_dictionary)
|
void ExternalLoader::reloadFromConfigFiles(const bool throw_on_error, const bool force_reload, const std::string & only_dictionary)
|
||||||
{
|
{
|
||||||
const auto config_paths = config_repository->list(config, config_settings.path_setting_name);
|
const auto config_paths = config_repository->list(config_main, config_settings.path_setting_name);
|
||||||
|
|
||||||
for (const auto & config_path : config_paths)
|
for (const auto & config_path : config_paths)
|
||||||
{
|
{
|
||||||
@ -262,7 +263,7 @@ void ExternalLoader::reloadFromConfigFile(const std::string & config_path, const
|
|||||||
const auto last_modified = config_repository->getLastModificationTime(config_path);
|
const auto last_modified = config_repository->getLastModificationTime(config_path);
|
||||||
if (force_reload || last_modified > config_last_modified)
|
if (force_reload || last_modified > config_last_modified)
|
||||||
{
|
{
|
||||||
auto loaded_config = config_repository->load(config_path);
|
auto loaded_config = config_repository->load(config_path, config_main.getString("path", DBMS_DEFAULT_PATH));
|
||||||
|
|
||||||
loadable_objects_defined_in_config[config_path].clear();
|
loadable_objects_defined_in_config[config_path].clear();
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ public:
|
|||||||
using ObjectsMap = std::unordered_map<std::string, LoadableInfo>;
|
using ObjectsMap = std::unordered_map<std::string, LoadableInfo>;
|
||||||
|
|
||||||
/// Objects will be loaded immediately and then will be updated in separate thread, each 'reload_period' seconds.
|
/// Objects will be loaded immediately and then will be updated in separate thread, each 'reload_period' seconds.
|
||||||
ExternalLoader(const Configuration & config,
|
ExternalLoader(const Configuration & config_main,
|
||||||
const ExternalLoaderUpdateSettings & update_settings,
|
const ExternalLoaderUpdateSettings & update_settings,
|
||||||
const ExternalLoaderConfigSettings & config_settings,
|
const ExternalLoaderConfigSettings & config_settings,
|
||||||
std::unique_ptr<IExternalLoaderConfigRepository> config_repository,
|
std::unique_ptr<IExternalLoaderConfigRepository> config_repository,
|
||||||
@ -151,7 +151,7 @@ private:
|
|||||||
|
|
||||||
pcg64 rnd_engine{randomSeed()};
|
pcg64 rnd_engine{randomSeed()};
|
||||||
|
|
||||||
const Configuration & config;
|
const Configuration & config_main;
|
||||||
const ExternalLoaderUpdateSettings & update_settings;
|
const ExternalLoaderUpdateSettings & update_settings;
|
||||||
const ExternalLoaderConfigSettings & config_settings;
|
const ExternalLoaderConfigSettings & config_settings;
|
||||||
|
|
||||||
|
@ -61,11 +61,11 @@ Poco::Timestamp ExternalLoaderConfigRepository::getLastModificationTime(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Poco::AutoPtr<Poco::Util::AbstractConfiguration> ExternalLoaderConfigRepository::load(
|
Poco::AutoPtr<Poco::Util::AbstractConfiguration> ExternalLoaderConfigRepository::load(
|
||||||
const std::string & config_file) const
|
const std::string & config_file, const std::string & preprocessed_dir) const
|
||||||
{
|
{
|
||||||
ConfigProcessor config_processor{config_file};
|
ConfigProcessor config_processor{config_file};
|
||||||
ConfigProcessor::LoadedConfig preprocessed = config_processor.loadConfig();
|
ConfigProcessor::LoadedConfig preprocessed = config_processor.loadConfig();
|
||||||
config_processor.savePreprocessedConfig(preprocessed);
|
config_processor.savePreprocessedConfig(preprocessed, preprocessed_dir);
|
||||||
return preprocessed.configuration;
|
return preprocessed.configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@ public:
|
|||||||
|
|
||||||
Poco::Timestamp getLastModificationTime(const std::string & config_file) const override;
|
Poco::Timestamp getLastModificationTime(const std::string & config_file) const override;
|
||||||
|
|
||||||
Poco::AutoPtr<Poco::Util::AbstractConfiguration> load(const std::string & config_file) const override;
|
Poco::AutoPtr<Poco::Util::AbstractConfiguration> load(const std::string & config_file, const std::string & preprocessed_dir = "") const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@ public:
|
|||||||
|
|
||||||
virtual Poco::Timestamp getLastModificationTime(const std::string & config_file) const = 0;
|
virtual Poco::Timestamp getLastModificationTime(const std::string & config_file) const = 0;
|
||||||
|
|
||||||
virtual Poco::AutoPtr<Poco::Util::AbstractConfiguration> load(const std::string & config_file) const = 0;
|
virtual Poco::AutoPtr<Poco::Util::AbstractConfiguration> load(const std::string & config_file, const std::string & preprocessed_dir = "") const = 0;
|
||||||
|
|
||||||
virtual ~IExternalLoaderConfigRepository() {}
|
virtual ~IExternalLoaderConfigRepository() {}
|
||||||
};
|
};
|
||||||
|
@ -57,7 +57,7 @@ BlockIO InterpreterAlterQuery::execute()
|
|||||||
if (!partition_commands.empty())
|
if (!partition_commands.empty())
|
||||||
{
|
{
|
||||||
partition_commands.validate(*table);
|
partition_commands.validate(*table);
|
||||||
table->partition(query_ptr, partition_commands, context);
|
table->alterPartition(query_ptr, partition_commands, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!alter_commands.empty())
|
if (!alter_commands.empty())
|
||||||
|
@ -89,7 +89,7 @@ BlockIO InterpreterDropQuery::executeToTable(String & database_name_, String & t
|
|||||||
database_and_table.second->shutdown();
|
database_and_table.second->shutdown();
|
||||||
/// If table was already dropped by anyone, an exception will be thrown
|
/// If table was already dropped by anyone, an exception will be thrown
|
||||||
auto table_lock = database_and_table.second->lockForAlter(__PRETTY_FUNCTION__);
|
auto table_lock = database_and_table.second->lockForAlter(__PRETTY_FUNCTION__);
|
||||||
/// Delete table metdata and table itself from memory
|
/// Delete table metadata and table itself from memory
|
||||||
database_and_table.first->removeTable(context, database_and_table.second->getTableName());
|
database_and_table.first->removeTable(context, database_and_table.second->getTableName());
|
||||||
/// Delete table data
|
/// Delete table data
|
||||||
database_and_table.second->drop();
|
database_and_table.second->drop();
|
||||||
|
80
dbms/src/Interpreters/addMissingDefaults.cpp
Normal file
80
dbms/src/Interpreters/addMissingDefaults.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#include <Interpreters/addMissingDefaults.h>
|
||||||
|
|
||||||
|
#include <Common/typeid_cast.h>
|
||||||
|
#include <DataTypes/NestedUtils.h>
|
||||||
|
#include <DataTypes/DataTypeArray.h>
|
||||||
|
#include <Columns/ColumnArray.h>
|
||||||
|
#include <Interpreters/evaluateMissingDefaults.h>
|
||||||
|
#include <Core/Block.h>
|
||||||
|
#include <Storages/ColumnDefault.h>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
Block addMissingDefaults(const Block & block,
|
||||||
|
const NamesAndTypesList & required_columns,
|
||||||
|
const ColumnDefaults & column_defaults,
|
||||||
|
const Context & context)
|
||||||
|
{
|
||||||
|
/// For missing columns of nested structure, you need to create not a column of empty arrays, but a column of arrays of correct lengths.
|
||||||
|
/// First, remember the offset columns for all arrays in the block.
|
||||||
|
std::map<String, ColumnPtr> offset_columns;
|
||||||
|
|
||||||
|
for (size_t i = 0, size = block.columns(); i < size; ++i)
|
||||||
|
{
|
||||||
|
const auto & elem = block.getByPosition(i);
|
||||||
|
|
||||||
|
if (const ColumnArray * array = typeid_cast<const ColumnArray *>(&*elem.column))
|
||||||
|
{
|
||||||
|
String offsets_name = Nested::extractTableName(elem.name);
|
||||||
|
auto & offsets_column = offset_columns[offsets_name];
|
||||||
|
|
||||||
|
/// If for some reason there are different offset columns for one nested structure, then we take nonempty.
|
||||||
|
if (!offsets_column || offsets_column->empty())
|
||||||
|
offsets_column = array->getOffsetsPtr();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t rows = block.rows();
|
||||||
|
Block res;
|
||||||
|
|
||||||
|
/// We take given columns from input block and missed columns without default value
|
||||||
|
/// (default and materialized will be computed later).
|
||||||
|
for (const auto & column : required_columns)
|
||||||
|
{
|
||||||
|
if (block.has(column.name))
|
||||||
|
{
|
||||||
|
res.insert(block.getByName(column.name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (column_defaults.count(column.name))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
String offsets_name = Nested::extractTableName(column.name);
|
||||||
|
if (offset_columns.count(offsets_name))
|
||||||
|
{
|
||||||
|
ColumnPtr offsets_column = offset_columns[offsets_name];
|
||||||
|
DataTypePtr nested_type = typeid_cast<const DataTypeArray &>(*column.type).getNestedType();
|
||||||
|
UInt64 nested_rows = rows ? get<UInt64>((*offsets_column)[rows - 1]) : 0;
|
||||||
|
|
||||||
|
ColumnPtr nested_column = nested_type->createColumnConstWithDefaultValue(nested_rows)->convertToFullColumnIfConst();
|
||||||
|
auto new_column = ColumnArray::create(nested_column, offsets_column);
|
||||||
|
res.insert(ColumnWithTypeAndName(std::move(new_column), column.type, column.name));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** It is necessary to turn a constant column into a full column, since in part of blocks (from other parts),
|
||||||
|
* it can be full (or the interpreter may decide that it is constant everywhere).
|
||||||
|
*/
|
||||||
|
auto new_column = column.type->createColumnConstWithDefaultValue(rows)->convertToFullColumnIfConst();
|
||||||
|
res.insert(ColumnWithTypeAndName(std::move(new_column), column.type, column.name));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Computes explicitly specified values (in column_defaults) by default and materialized columns.
|
||||||
|
evaluateMissingDefaults(res, required_columns, column_defaults, context);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
26
dbms/src/Interpreters/addMissingDefaults.h
Normal file
26
dbms/src/Interpreters/addMissingDefaults.h
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
|
||||||
|
namespace DB
|
||||||
|
{
|
||||||
|
|
||||||
|
class Block;
|
||||||
|
class Context;
|
||||||
|
class NamesAndTypesList;
|
||||||
|
struct ColumnDefault;
|
||||||
|
|
||||||
|
/** Adds three types of columns into block
|
||||||
|
* 1. Columns, that are missed inside request, but present in table without defaults (missed columns)
|
||||||
|
* 2. Columns, that are missed inside request, but present in table with defaults (columns with default values)
|
||||||
|
* 3. Columns that materialized from other columns (materialized columns)
|
||||||
|
* All three types of columns are materialized (not constants).
|
||||||
|
*/
|
||||||
|
Block addMissingDefaults(
|
||||||
|
const Block & block,
|
||||||
|
const NamesAndTypesList & required_columns,
|
||||||
|
const std::unordered_map<std::string, ColumnDefault> & column_defaults,
|
||||||
|
const Context & context);
|
||||||
|
|
||||||
|
}
|
@ -189,11 +189,11 @@ void runOneTest(const TestDescriptor & test_descriptor)
|
|||||||
const auto path_name = createTmpPath("users.xml");
|
const auto path_name = createTmpPath("users.xml");
|
||||||
createFile(path_name, test_descriptor.config_content);
|
createFile(path_name, test_descriptor.config_content);
|
||||||
|
|
||||||
ConfigurationPtr config;
|
DB::ConfigurationPtr config;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
config = ConfigProcessor(path_name).loadConfig().configuration;
|
config = DB::ConfigProcessor(path_name).loadConfig().configuration;
|
||||||
}
|
}
|
||||||
catch (const Poco::Exception & ex)
|
catch (const Poco::Exception & ex)
|
||||||
{
|
{
|
||||||
|
@ -256,7 +256,7 @@ public:
|
|||||||
/** ALTER tables with regard to its partitions.
|
/** ALTER tables with regard to its partitions.
|
||||||
* Should handle locks for each command on its own.
|
* Should handle locks for each command on its own.
|
||||||
*/
|
*/
|
||||||
virtual void partition(const ASTPtr & /* query */, const PartitionCommands & /* commands */, const Context & /* context */)
|
virtual void alterPartition(const ASTPtr & /* query */, const PartitionCommands & /* commands */, const Context & /* context */)
|
||||||
{
|
{
|
||||||
throw Exception("Partition operations are not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED);
|
throw Exception("Partition operations are not supported by storage " + getName(), ErrorCodes::NOT_IMPLEMENTED);
|
||||||
}
|
}
|
||||||
|
@ -30,14 +30,15 @@ constexpr double BackgroundProcessingPool::sleep_seconds_random_part;
|
|||||||
|
|
||||||
void BackgroundProcessingPoolTaskInfo::wake()
|
void BackgroundProcessingPoolTaskInfo::wake()
|
||||||
{
|
{
|
||||||
if (removed)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Poco::Timestamp current_time;
|
Poco::Timestamp current_time;
|
||||||
|
|
||||||
{
|
{
|
||||||
std::unique_lock lock(pool.tasks_mutex);
|
std::unique_lock lock(pool.tasks_mutex);
|
||||||
|
|
||||||
|
/// This will ensure that iterator is valid. Must be done under the same mutex when the iterator is invalidated.
|
||||||
|
if (removed)
|
||||||
|
return;
|
||||||
|
|
||||||
auto next_time_to_execute = iterator->first;
|
auto next_time_to_execute = iterator->first;
|
||||||
auto this_task_handle = iterator->second;
|
auto this_task_handle = iterator->second;
|
||||||
|
|
||||||
@ -93,6 +94,7 @@ void BackgroundProcessingPool::removeTask(const TaskHandle & task)
|
|||||||
{
|
{
|
||||||
std::unique_lock lock(tasks_mutex);
|
std::unique_lock lock(tasks_mutex);
|
||||||
tasks.erase(task->iterator);
|
tasks.erase(task->iterator);
|
||||||
|
/// Note that the task may be still accessible through TaskHandle (shared_ptr).
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2181,7 +2181,7 @@ String MergeTreeData::getPartitionIDFromQuery(const ASTPtr & ast, const Context
|
|||||||
{
|
{
|
||||||
WriteBufferFromOwnString buf;
|
WriteBufferFromOwnString buf;
|
||||||
writeCString("Parsed partition value: ", buf);
|
writeCString("Parsed partition value: ", buf);
|
||||||
partition.serializeTextQuoted(*this, buf, format_settings);
|
partition.serializeText(*this, buf, format_settings);
|
||||||
writeCString(" doesn't match partition value for an existing part with the same partition ID: ", buf);
|
writeCString(" doesn't match partition value for an existing part with the same partition ID: ", buf);
|
||||||
writeString(existing_part_in_partition->name, buf);
|
writeString(existing_part_in_partition->name, buf);
|
||||||
throw Exception(buf.str(), ErrorCodes::INVALID_PARTITION_VALUE);
|
throw Exception(buf.str(), ErrorCodes::INVALID_PARTITION_VALUE);
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
#include <IO/HashingWriteBuffer.h>
|
#include <IO/HashingWriteBuffer.h>
|
||||||
#include <Common/FieldVisitors.h>
|
#include <Common/FieldVisitors.h>
|
||||||
#include <DataTypes/DataTypeDate.h>
|
#include <DataTypes/DataTypeDate.h>
|
||||||
|
#include <DataTypes/DataTypeTuple.h>
|
||||||
|
#include <Columns/ColumnTuple.h>
|
||||||
#include <Common/SipHash.h>
|
#include <Common/SipHash.h>
|
||||||
#include <Common/typeid_cast.h>
|
#include <Common/typeid_cast.h>
|
||||||
#include <Common/hex.h>
|
#include <Common/hex.h>
|
||||||
@ -77,32 +79,38 @@ String MergeTreePartition::getID(const MergeTreeData & storage) const
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergeTreePartition::serializeTextQuoted(const MergeTreeData & storage, WriteBuffer & out, const FormatSettings & format_settings) const
|
void MergeTreePartition::serializeText(const MergeTreeData & storage, WriteBuffer & out, const FormatSettings & format_settings) const
|
||||||
{
|
{
|
||||||
size_t key_size = storage.partition_key_sample.columns();
|
size_t key_size = storage.partition_key_sample.columns();
|
||||||
|
|
||||||
if (key_size == 0)
|
if (key_size == 0)
|
||||||
{
|
{
|
||||||
writeCString("tuple()", out);
|
writeCString("tuple()", out);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (key_size == 1)
|
||||||
if (key_size > 1)
|
{
|
||||||
writeChar('(', out);
|
const DataTypePtr & type = storage.partition_key_sample.getByPosition(0).type;
|
||||||
|
auto column = type->createColumn();
|
||||||
|
column->insert(value[0]);
|
||||||
|
type->serializeText(*column, 0, out, format_settings);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DataTypes types;
|
||||||
|
Columns columns;
|
||||||
for (size_t i = 0; i < key_size; ++i)
|
for (size_t i = 0; i < key_size; ++i)
|
||||||
{
|
{
|
||||||
if (i > 0)
|
const auto & type = storage.partition_key_sample.getByPosition(i).type;
|
||||||
writeCString(", ", out);
|
types.push_back(type);
|
||||||
|
|
||||||
const DataTypePtr & type = storage.partition_key_sample.getByPosition(i).type;
|
|
||||||
auto column = type->createColumn();
|
auto column = type->createColumn();
|
||||||
column->insert(value[i]);
|
column->insert(value[i]);
|
||||||
type->serializeTextQuoted(*column, 0, out, format_settings);
|
columns.push_back(std::move(column));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (key_size > 1)
|
DataTypeTuple tuple_type(types);
|
||||||
writeChar(')', out);
|
auto tuple_column = ColumnTuple::create(columns);
|
||||||
|
tuple_type.serializeText(*tuple_column, 0, out, format_settings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MergeTreePartition::load(const MergeTreeData & storage, const String & part_path)
|
void MergeTreePartition::load(const MergeTreeData & storage, const String & part_path)
|
||||||
|
@ -26,7 +26,7 @@ public:
|
|||||||
|
|
||||||
String getID(const MergeTreeData & storage) const;
|
String getID(const MergeTreeData & storage) const;
|
||||||
|
|
||||||
void serializeTextQuoted(const MergeTreeData & storage, WriteBuffer & out, const FormatSettings & format_settings) const;
|
void serializeText(const MergeTreeData & storage, WriteBuffer & out, const FormatSettings & format_settings) const;
|
||||||
|
|
||||||
void load(const MergeTreeData & storage, const String & part_path);
|
void load(const MergeTreeData & storage, const String & part_path);
|
||||||
void store(const MergeTreeData & storage, const String & part_path, MergeTreeDataPartChecksums & checksums) const;
|
void store(const MergeTreeData & storage, const String & part_path, MergeTreeDataPartChecksums & checksums) const;
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
#include <Interpreters/InterpreterSelectQuery.h>
|
#include <Interpreters/InterpreterSelectQuery.h>
|
||||||
#include <Interpreters/InterpreterInsertQuery.h>
|
#include <Interpreters/InterpreterInsertQuery.h>
|
||||||
#include <Interpreters/InterpreterAlterQuery.h>
|
#include <Interpreters/InterpreterAlterQuery.h>
|
||||||
|
#include <Interpreters/castColumn.h>
|
||||||
#include <Interpreters/evaluateConstantExpression.h>
|
#include <Interpreters/evaluateConstantExpression.h>
|
||||||
|
#include <DataStreams/AddingDefaultBlockInputStream.h>
|
||||||
|
#include <DataStreams/ConvertingBlockInputStream.h>
|
||||||
#include <DataStreams/IProfilingBlockInputStream.h>
|
#include <DataStreams/IProfilingBlockInputStream.h>
|
||||||
#include <Databases/IDatabase.h>
|
#include <Databases/IDatabase.h>
|
||||||
#include <Storages/StorageBuffer.h>
|
#include <Storages/StorageBuffer.h>
|
||||||
@ -145,7 +148,50 @@ BlockInputStreams StorageBuffer::read(
|
|||||||
if (destination.get() == this)
|
if (destination.get() == this)
|
||||||
throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP);
|
throw Exception("Destination table is myself. Read will cause infinite loop.", ErrorCodes::INFINITE_LOOP);
|
||||||
|
|
||||||
streams_from_dst = destination->read(column_names, query_info, context, processed_stage, max_block_size, num_streams);
|
/// Collect columns from the destination tables which can be requested.
|
||||||
|
/// Find out if there is a struct mismatch and we need to convert read blocks from the destination tables.
|
||||||
|
Names columns_intersection;
|
||||||
|
bool struct_mismatch = false;
|
||||||
|
for (const String & column_name : column_names)
|
||||||
|
{
|
||||||
|
if (destination->hasColumn(column_name))
|
||||||
|
{
|
||||||
|
columns_intersection.emplace_back(column_name);
|
||||||
|
if (!destination->getColumn(column_name).type->equals(*getColumn(column_name).type))
|
||||||
|
{
|
||||||
|
LOG_WARNING(log, "Destination table " << backQuoteIfNeed(destination_database) << "." << backQuoteIfNeed(destination_table)
|
||||||
|
<< " has different type of column " << backQuoteIfNeed(column_name) << " ("
|
||||||
|
<< destination->getColumn(column_name).type->getName() << " != " << getColumn(column_name).type->getName()
|
||||||
|
<< "). Data from destination table is converted.");
|
||||||
|
struct_mismatch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG_WARNING(log, "Destination table " << backQuoteIfNeed(destination_database) << "." << backQuoteIfNeed(destination_table)
|
||||||
|
<< " doesn't have column " << backQuoteIfNeed(column_name) << ". The default values are used.");
|
||||||
|
struct_mismatch = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (columns_intersection.empty())
|
||||||
|
LOG_WARNING(log, "Destination table " << backQuoteIfNeed(destination_database) << "." << backQuoteIfNeed(destination_table)
|
||||||
|
<< " has no common columns with block in buffer. Block of data is skipped.");
|
||||||
|
else
|
||||||
|
streams_from_dst = destination->read(columns_intersection, query_info, context, processed_stage, max_block_size, num_streams);
|
||||||
|
|
||||||
|
if (struct_mismatch && !streams_from_dst.empty())
|
||||||
|
{
|
||||||
|
/// Add streams to convert read blocks from the destination table.
|
||||||
|
auto header = getSampleBlock();
|
||||||
|
for (auto & stream_from_dst : streams_from_dst)
|
||||||
|
{
|
||||||
|
stream_from_dst = std::make_shared<AddingDefaultBlockInputStream>(
|
||||||
|
stream_from_dst, header, getColumns().defaults, context);
|
||||||
|
stream_from_dst = std::make_shared<ConvertingBlockInputStream>(
|
||||||
|
context, stream_from_dst, header, ConvertingBlockInputStream::MatchColumnsMode::Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockInputStreams streams_from_buffers;
|
BlockInputStreams streams_from_buffers;
|
||||||
@ -233,6 +279,9 @@ public:
|
|||||||
if (!block)
|
if (!block)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Check table structure.
|
||||||
|
storage.check(block, true);
|
||||||
|
|
||||||
size_t rows = block.rows();
|
size_t rows = block.rows();
|
||||||
if (!rows)
|
if (!rows)
|
||||||
return;
|
return;
|
||||||
@ -241,23 +290,8 @@ public:
|
|||||||
if (!storage.no_destination)
|
if (!storage.no_destination)
|
||||||
{
|
{
|
||||||
destination = storage.context.tryGetTable(storage.destination_database, storage.destination_table);
|
destination = storage.context.tryGetTable(storage.destination_database, storage.destination_table);
|
||||||
|
|
||||||
if (destination)
|
|
||||||
{
|
|
||||||
if (destination.get() == &storage)
|
if (destination.get() == &storage)
|
||||||
throw Exception("Destination table is myself. Write will cause infinite loop.", ErrorCodes::INFINITE_LOOP);
|
throw Exception("Destination table is myself. Write will cause infinite loop.", ErrorCodes::INFINITE_LOOP);
|
||||||
|
|
||||||
/// Check table structure.
|
|
||||||
try
|
|
||||||
{
|
|
||||||
destination->check(block, true);
|
|
||||||
}
|
|
||||||
catch (Exception & e)
|
|
||||||
{
|
|
||||||
e.addMessage("(when looking at destination table " + storage.destination_database + "." + storage.destination_table + ")");
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t bytes = block.bytes();
|
size_t bytes = block.bytes();
|
||||||
@ -548,7 +582,7 @@ void StorageBuffer::writeBlockToDestination(const Block & block, StoragePtr tabl
|
|||||||
|
|
||||||
if (!table)
|
if (!table)
|
||||||
{
|
{
|
||||||
LOG_ERROR(log, "Destination table " << destination_database << "." << destination_table << " doesn't exist. Block of data is discarded.");
|
LOG_ERROR(log, "Destination table " << backQuoteIfNeed(destination_database) << "." << backQuoteIfNeed(destination_table) << " doesn't exist. Block of data is discarded.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -561,48 +595,46 @@ void StorageBuffer::writeBlockToDestination(const Block & block, StoragePtr tabl
|
|||||||
* This will support some of the cases (but not all) when the table structure does not match.
|
* This will support some of the cases (but not all) when the table structure does not match.
|
||||||
*/
|
*/
|
||||||
Block structure_of_destination_table = allow_materialized ? table->getSampleBlock() : table->getSampleBlockNonMaterialized();
|
Block structure_of_destination_table = allow_materialized ? table->getSampleBlock() : table->getSampleBlockNonMaterialized();
|
||||||
Names columns_intersection;
|
Block block_to_write;
|
||||||
columns_intersection.reserve(block.columns());
|
|
||||||
for (size_t i : ext::range(0, structure_of_destination_table.columns()))
|
for (size_t i : ext::range(0, structure_of_destination_table.columns()))
|
||||||
{
|
{
|
||||||
auto dst_col = structure_of_destination_table.getByPosition(i);
|
auto dst_col = structure_of_destination_table.getByPosition(i);
|
||||||
if (block.has(dst_col.name))
|
if (block.has(dst_col.name))
|
||||||
{
|
{
|
||||||
if (!block.getByName(dst_col.name).type->equals(*dst_col.type))
|
auto column = block.getByName(dst_col.name);
|
||||||
|
if (!column.type->equals(*dst_col.type))
|
||||||
{
|
{
|
||||||
LOG_ERROR(log, "Destination table " << destination_database << "." << destination_table
|
LOG_WARNING(log, "Destination table " << backQuoteIfNeed(destination_database) << "." << backQuoteIfNeed(destination_table)
|
||||||
<< " have different type of column " << dst_col.name << " ("
|
<< " have different type of column " << backQuoteIfNeed(column.name) << " ("
|
||||||
<< block.getByName(dst_col.name).type->getName() << " != " << dst_col.type->getName()
|
<< dst_col.type->getName() << " != " << column.type->getName()
|
||||||
<< "). Block of data is discarded.");
|
<< "). Block of data is converted.");
|
||||||
|
column.column = castColumn(column, dst_col.type, context);
|
||||||
|
column.type = dst_col.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
block_to_write.insert(column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (block_to_write.columns() == 0)
|
||||||
|
{
|
||||||
|
LOG_ERROR(log, "Destination table " << backQuoteIfNeed(destination_database) << "." << backQuoteIfNeed(destination_table)
|
||||||
|
<< " have no common columns with block in buffer. Block of data is discarded.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
columns_intersection.push_back(dst_col.name);
|
if (block_to_write.columns() != block.columns())
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (columns_intersection.empty())
|
|
||||||
{
|
|
||||||
LOG_ERROR(log, "Destination table " << destination_database << "." << destination_table << " have no common columns with block in buffer. Block of data is discarded.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (columns_intersection.size() != block.columns())
|
|
||||||
LOG_WARNING(log, "Not all columns from block in buffer exist in destination table "
|
LOG_WARNING(log, "Not all columns from block in buffer exist in destination table "
|
||||||
<< destination_database << "." << destination_table << ". Some columns are discarded.");
|
<< backQuoteIfNeed(destination_database) << "." << backQuoteIfNeed(destination_table) << ". Some columns are discarded.");
|
||||||
|
|
||||||
auto list_of_columns = std::make_shared<ASTExpressionList>();
|
auto list_of_columns = std::make_shared<ASTExpressionList>();
|
||||||
insert->columns = list_of_columns;
|
insert->columns = list_of_columns;
|
||||||
list_of_columns->children.reserve(columns_intersection.size());
|
list_of_columns->children.reserve(block_to_write.columns());
|
||||||
for (const String & column : columns_intersection)
|
for (const auto & column : block_to_write)
|
||||||
list_of_columns->children.push_back(std::make_shared<ASTIdentifier>(column));
|
list_of_columns->children.push_back(std::make_shared<ASTIdentifier>(column.name));
|
||||||
|
|
||||||
InterpreterInsertQuery interpreter{insert, context, allow_materialized};
|
InterpreterInsertQuery interpreter{insert, context, allow_materialized};
|
||||||
|
|
||||||
Block block_to_write;
|
|
||||||
for (const auto & name : columns_intersection)
|
|
||||||
block_to_write.insert(block.getByName(name));
|
|
||||||
|
|
||||||
auto block_io = interpreter.execute();
|
auto block_io = interpreter.execute();
|
||||||
block_io.out->writePrefix();
|
block_io.out->writePrefix();
|
||||||
block_io.out->write(block_to_write);
|
block_io.out->write(block_to_write);
|
||||||
|
@ -240,10 +240,10 @@ bool StorageMaterializedView::optimize(const ASTPtr & query, const ASTPtr & part
|
|||||||
return getTargetTable()->optimize(query, partition, final, deduplicate, context);
|
return getTargetTable()->optimize(query, partition, final, deduplicate, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageMaterializedView::partition(const ASTPtr & query, const PartitionCommands &commands, const Context &context)
|
void StorageMaterializedView::alterPartition(const ASTPtr & query, const PartitionCommands &commands, const Context &context)
|
||||||
{
|
{
|
||||||
checkStatementCanBeForwarded();
|
checkStatementCanBeForwarded();
|
||||||
getTargetTable()->partition(query, commands, context);
|
getTargetTable()->alterPartition(query, commands, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageMaterializedView::mutate(const MutationCommands & commands, const Context & context)
|
void StorageMaterializedView::mutate(const MutationCommands & commands, const Context & context)
|
||||||
|
@ -35,7 +35,7 @@ public:
|
|||||||
|
|
||||||
bool optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) override;
|
bool optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) override;
|
||||||
|
|
||||||
void partition(const ASTPtr & query, const PartitionCommands & commands, const Context & context) override;
|
void alterPartition(const ASTPtr & query, const PartitionCommands & commands, const Context & context) override;
|
||||||
|
|
||||||
void mutate(const MutationCommands & commands, const Context & context) override;
|
void mutate(const MutationCommands & commands, const Context & context) override;
|
||||||
|
|
||||||
|
@ -781,7 +781,7 @@ bool StorageMergeTree::optimize(
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageMergeTree::partition(const ASTPtr & query, const PartitionCommands & commands, const Context & context)
|
void StorageMergeTree::alterPartition(const ASTPtr & query, const PartitionCommands & commands, const Context & context)
|
||||||
{
|
{
|
||||||
for (const PartitionCommand & command : commands)
|
for (const PartitionCommand & command : commands)
|
||||||
{
|
{
|
||||||
@ -824,7 +824,7 @@ void StorageMergeTree::partition(const ASTPtr & query, const PartitionCommands &
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
IStorage::partition(query, commands, context); // should throw an exception.
|
IStorage::alterPartition(query, commands, context); // should throw an exception.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -60,7 +60,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) override;
|
bool optimize(const ASTPtr & query, const ASTPtr & partition, bool final, bool deduplicate, const Context & context) override;
|
||||||
|
|
||||||
void partition(const ASTPtr & query, const PartitionCommands & commands, const Context & context) override;
|
void alterPartition(const ASTPtr & query, const PartitionCommands & commands, const Context & context) override;
|
||||||
|
|
||||||
void mutate(const MutationCommands & commands, const Context & context) override;
|
void mutate(const MutationCommands & commands, const Context & context) override;
|
||||||
|
|
||||||
|
@ -3282,7 +3282,7 @@ void StorageReplicatedMergeTree::alter(const AlterCommands & params,
|
|||||||
LOG_DEBUG(log, "ALTER finished");
|
LOG_DEBUG(log, "ALTER finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageReplicatedMergeTree::partition(const ASTPtr & query, const PartitionCommands & commands, const Context & context)
|
void StorageReplicatedMergeTree::alterPartition(const ASTPtr & query, const PartitionCommands & commands, const Context & context)
|
||||||
{
|
{
|
||||||
for (const PartitionCommand & command : commands)
|
for (const PartitionCommand & command : commands)
|
||||||
{
|
{
|
||||||
@ -3329,7 +3329,7 @@ void StorageReplicatedMergeTree::partition(const ASTPtr & query, const Partition
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
IStorage::partition(query, commands, context); // should throw an exception.
|
IStorage::alterPartition(query, commands, context); // should throw an exception.
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,7 @@ public:
|
|||||||
|
|
||||||
void alter(const AlterCommands & params, const String & database_name, const String & table_name, const Context & context) override;
|
void alter(const AlterCommands & params, const String & database_name, const String & table_name, const Context & context) override;
|
||||||
|
|
||||||
void partition(const ASTPtr & query, const PartitionCommands & commands, const Context & context) override;
|
void alterPartition(const ASTPtr & query, const PartitionCommands & commands, const Context & context) override;
|
||||||
|
|
||||||
void mutate(const MutationCommands & commands, const Context & context) override;
|
void mutate(const MutationCommands & commands, const Context & context) override;
|
||||||
|
|
||||||
|
@ -61,7 +61,7 @@ void StorageSystemParts::processNextStorage(MutableColumns & columns, const Stor
|
|||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
{
|
{
|
||||||
WriteBufferFromOwnString out;
|
WriteBufferFromOwnString out;
|
||||||
part->partition.serializeTextQuoted(*info.data, out, format_settings);
|
part->partition.serializeText(*info.data, out, format_settings);
|
||||||
columns[i++]->insert(out.str());
|
columns[i++]->insert(out.str());
|
||||||
}
|
}
|
||||||
columns[i++]->insert(part->name);
|
columns[i++]->insert(part->name);
|
||||||
|
@ -106,7 +106,7 @@ void StorageSystemPartsColumns::processNextStorage(MutableColumns & columns, con
|
|||||||
size_t j = 0;
|
size_t j = 0;
|
||||||
{
|
{
|
||||||
WriteBufferFromOwnString out;
|
WriteBufferFromOwnString out;
|
||||||
part->partition.serializeTextQuoted(*info.data, out, format_settings);
|
part->partition.serializeText(*info.data, out, format_settings);
|
||||||
columns[j++]->insert(out.str());
|
columns[j++]->insert(out.str());
|
||||||
}
|
}
|
||||||
columns[j++]->insert(part->name);
|
columns[j++]->insert(part->name);
|
||||||
|
@ -10,41 +10,41 @@ Sum after DETACH PARTITION:
|
|||||||
0
|
0
|
||||||
*** Partitioned by week ***
|
*** Partitioned by week ***
|
||||||
Parts before OPTIMIZE:
|
Parts before OPTIMIZE:
|
||||||
\'1999-12-27\' 19991227_1_1_0
|
1999-12-27 19991227_1_1_0
|
||||||
\'2000-01-03\' 20000103_2_2_0
|
2000-01-03 20000103_2_2_0
|
||||||
\'2000-01-03\' 20000103_3_3_0
|
2000-01-03 20000103_3_3_0
|
||||||
Parts after OPTIMIZE:
|
Parts after OPTIMIZE:
|
||||||
\'1999-12-27\' 19991227_1_1_0
|
1999-12-27 19991227_1_1_0
|
||||||
\'2000-01-03\' 20000103_2_3_1
|
2000-01-03 20000103_2_3_1
|
||||||
Sum before DROP PARTITION:
|
Sum before DROP PARTITION:
|
||||||
15
|
15
|
||||||
Sum after DROP PARTITION:
|
Sum after DROP PARTITION:
|
||||||
12
|
12
|
||||||
*** Partitioned by a (Date, UInt8) tuple ***
|
*** Partitioned by a (Date, UInt8) tuple ***
|
||||||
Parts before OPTIMIZE:
|
Parts before OPTIMIZE:
|
||||||
(\'2000-01-01\', 1) 20000101-1_1_1_0
|
(\'2000-01-01\',1) 20000101-1_1_1_0
|
||||||
(\'2000-01-01\', 1) 20000101-1_5_5_0
|
(\'2000-01-01\',1) 20000101-1_5_5_0
|
||||||
(\'2000-01-01\', 2) 20000101-2_2_2_0
|
(\'2000-01-01\',2) 20000101-2_2_2_0
|
||||||
(\'2000-01-02\', 1) 20000102-1_3_3_0
|
(\'2000-01-02\',1) 20000102-1_3_3_0
|
||||||
(\'2000-01-02\', 1) 20000102-1_4_4_0
|
(\'2000-01-02\',1) 20000102-1_4_4_0
|
||||||
Parts after OPTIMIZE:
|
Parts after OPTIMIZE:
|
||||||
(\'2000-01-01\', 1) 20000101-1_1_5_1
|
(\'2000-01-01\',1) 20000101-1_1_5_1
|
||||||
(\'2000-01-01\', 2) 20000101-2_2_2_0
|
(\'2000-01-01\',2) 20000101-2_2_2_0
|
||||||
(\'2000-01-02\', 1) 20000102-1_3_4_1
|
(\'2000-01-02\',1) 20000102-1_3_4_1
|
||||||
Sum before DETACH PARTITION:
|
Sum before DETACH PARTITION:
|
||||||
15
|
15
|
||||||
Sum after DETACH PARTITION:
|
Sum after DETACH PARTITION:
|
||||||
9
|
9
|
||||||
*** Partitioned by String ***
|
*** Partitioned by String ***
|
||||||
Parts before OPTIMIZE:
|
Parts before OPTIMIZE:
|
||||||
\'bbb\' 7d878f3d88441d2b3dc371e2a3050f6d_2_2_0
|
bbb 7d878f3d88441d2b3dc371e2a3050f6d_2_2_0
|
||||||
\'bbb\' 7d878f3d88441d2b3dc371e2a3050f6d_3_3_0
|
bbb 7d878f3d88441d2b3dc371e2a3050f6d_3_3_0
|
||||||
\'aaa\' 9b50856126a8a6064f11f027d455bf58_1_1_0
|
aaa 9b50856126a8a6064f11f027d455bf58_1_1_0
|
||||||
\'aaa\' 9b50856126a8a6064f11f027d455bf58_4_4_0
|
aaa 9b50856126a8a6064f11f027d455bf58_4_4_0
|
||||||
Parts after OPTIMIZE:
|
Parts after OPTIMIZE:
|
||||||
\'bbb\' 7d878f3d88441d2b3dc371e2a3050f6d_2_2_0
|
bbb 7d878f3d88441d2b3dc371e2a3050f6d_2_2_0
|
||||||
\'bbb\' 7d878f3d88441d2b3dc371e2a3050f6d_3_3_0
|
bbb 7d878f3d88441d2b3dc371e2a3050f6d_3_3_0
|
||||||
\'aaa\' 9b50856126a8a6064f11f027d455bf58_1_4_1
|
aaa 9b50856126a8a6064f11f027d455bf58_1_4_1
|
||||||
Sum before DROP PARTITION:
|
Sum before DROP PARTITION:
|
||||||
15
|
15
|
||||||
Sum after DROP PARTITION:
|
Sum after DROP PARTITION:
|
||||||
|
@ -10,41 +10,41 @@ Sum after DETACH PARTITION:
|
|||||||
0
|
0
|
||||||
*** Partitioned by week ***
|
*** Partitioned by week ***
|
||||||
Parts before OPTIMIZE:
|
Parts before OPTIMIZE:
|
||||||
\'1999-12-27\' 19991227_0_0_0
|
1999-12-27 19991227_0_0_0
|
||||||
\'2000-01-03\' 20000103_0_0_0
|
2000-01-03 20000103_0_0_0
|
||||||
\'2000-01-03\' 20000103_1_1_0
|
2000-01-03 20000103_1_1_0
|
||||||
Parts after OPTIMIZE:
|
Parts after OPTIMIZE:
|
||||||
\'1999-12-27\' 19991227_0_0_0
|
1999-12-27 19991227_0_0_0
|
||||||
\'2000-01-03\' 20000103_0_1_1
|
2000-01-03 20000103_0_1_1
|
||||||
Sum before DROP PARTITION:
|
Sum before DROP PARTITION:
|
||||||
15
|
15
|
||||||
Sum after DROP PARTITION:
|
Sum after DROP PARTITION:
|
||||||
12
|
12
|
||||||
*** Partitioned by a (Date, UInt8) tuple ***
|
*** Partitioned by a (Date, UInt8) tuple ***
|
||||||
Parts before OPTIMIZE:
|
Parts before OPTIMIZE:
|
||||||
(\'2000-01-01\', 1) 20000101-1_0_0_0
|
(\'2000-01-01\',1) 20000101-1_0_0_0
|
||||||
(\'2000-01-01\', 1) 20000101-1_1_1_0
|
(\'2000-01-01\',1) 20000101-1_1_1_0
|
||||||
(\'2000-01-01\', 2) 20000101-2_0_0_0
|
(\'2000-01-01\',2) 20000101-2_0_0_0
|
||||||
(\'2000-01-02\', 1) 20000102-1_0_0_0
|
(\'2000-01-02\',1) 20000102-1_0_0_0
|
||||||
(\'2000-01-02\', 1) 20000102-1_1_1_0
|
(\'2000-01-02\',1) 20000102-1_1_1_0
|
||||||
Parts after OPTIMIZE:
|
Parts after OPTIMIZE:
|
||||||
(\'2000-01-01\', 1) 20000101-1_0_1_1
|
(\'2000-01-01\',1) 20000101-1_0_1_1
|
||||||
(\'2000-01-01\', 2) 20000101-2_0_0_0
|
(\'2000-01-01\',2) 20000101-2_0_0_0
|
||||||
(\'2000-01-02\', 1) 20000102-1_0_1_1
|
(\'2000-01-02\',1) 20000102-1_0_1_1
|
||||||
Sum before DETACH PARTITION:
|
Sum before DETACH PARTITION:
|
||||||
15
|
15
|
||||||
Sum after DETACH PARTITION:
|
Sum after DETACH PARTITION:
|
||||||
9
|
9
|
||||||
*** Partitioned by String ***
|
*** Partitioned by String ***
|
||||||
Parts before OPTIMIZE:
|
Parts before OPTIMIZE:
|
||||||
\'bbb\' 7d878f3d88441d2b3dc371e2a3050f6d_0_0_0
|
bbb 7d878f3d88441d2b3dc371e2a3050f6d_0_0_0
|
||||||
\'bbb\' 7d878f3d88441d2b3dc371e2a3050f6d_1_1_0
|
bbb 7d878f3d88441d2b3dc371e2a3050f6d_1_1_0
|
||||||
\'aaa\' 9b50856126a8a6064f11f027d455bf58_0_0_0
|
aaa 9b50856126a8a6064f11f027d455bf58_0_0_0
|
||||||
\'aaa\' 9b50856126a8a6064f11f027d455bf58_1_1_0
|
aaa 9b50856126a8a6064f11f027d455bf58_1_1_0
|
||||||
Parts after OPTIMIZE:
|
Parts after OPTIMIZE:
|
||||||
\'bbb\' 7d878f3d88441d2b3dc371e2a3050f6d_0_0_0
|
bbb 7d878f3d88441d2b3dc371e2a3050f6d_0_0_0
|
||||||
\'bbb\' 7d878f3d88441d2b3dc371e2a3050f6d_1_1_0
|
bbb 7d878f3d88441d2b3dc371e2a3050f6d_1_1_0
|
||||||
\'aaa\' 9b50856126a8a6064f11f027d455bf58_0_1_1
|
aaa 9b50856126a8a6064f11f027d455bf58_0_1_1
|
||||||
Sum before DROP PARTITION:
|
Sum before DROP PARTITION:
|
||||||
15
|
15
|
||||||
Sum after DROP PARTITION:
|
Sum after DROP PARTITION:
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
0 z
|
||||||
|
1 w
|
||||||
|
3 y
|
||||||
|
4 w
|
||||||
|
6 y
|
||||||
|
8 z
|
||||||
|
10 x
|
||||||
|
11 x
|
||||||
|
12 a
|
||||||
|
13 b
|
||||||
|
14 c
|
||||||
|
15 d
|
||||||
|
16 e
|
||||||
|
17 f
|
||||||
|
18 g
|
||||||
|
19 h
|
||||||
|
w
|
||||||
|
w
|
||||||
|
x
|
||||||
|
x
|
||||||
|
y
|
||||||
|
y
|
||||||
|
z
|
||||||
|
z
|
@ -0,0 +1,8 @@
|
|||||||
|
set allow_experimental_low_cardinality_type = 1;
|
||||||
|
drop table if exists test.lc_perm;
|
||||||
|
create table test.lc_perm (val UInt32, str LowCardinality(String)) engine = MergeTree order by val;
|
||||||
|
insert into test.lc_perm values (1, 'w'), (10, 'x'), (3, 'y'), (8, 'z'), (4, 'w'), (6, 'y'), (11, 'x'), (0, 'z'), (12, 'a'), (13, 'b'), (14, 'c'), (15, 'd'), (16, 'e'), (17, 'f'), (18, 'g'), (19, 'h');
|
||||||
|
select * from test.lc_perm;
|
||||||
|
select str from test.lc_perm where val < 12 order by str;
|
||||||
|
drop table if exists test.lc_perm;
|
||||||
|
|
@ -0,0 +1,25 @@
|
|||||||
|
init
|
||||||
|
1 100
|
||||||
|
2 200
|
||||||
|
-
|
||||||
|
1 100
|
||||||
|
2 200
|
||||||
|
3 300
|
||||||
|
alt
|
||||||
|
100 DEFZ
|
||||||
|
200 DEFZ
|
||||||
|
-
|
||||||
|
0 100
|
||||||
|
0 200
|
||||||
|
3 300
|
||||||
|
4 400
|
||||||
|
opt
|
||||||
|
100 DEFZ
|
||||||
|
200 DEFZ
|
||||||
|
300 DEFZ
|
||||||
|
400 DEFZ
|
||||||
|
-
|
||||||
|
0 100
|
||||||
|
0 200
|
||||||
|
0 300
|
||||||
|
0 400
|
@ -0,0 +1,32 @@
|
|||||||
|
DROP TABLE IF EXISTS test.dst;
|
||||||
|
DROP TABLE IF EXISTS test.buffer;
|
||||||
|
SET send_logs_level = 'error';
|
||||||
|
|
||||||
|
CREATE TABLE test.dst (x UInt64, y UInt64) ENGINE = MergeTree ORDER BY tuple();
|
||||||
|
CREATE TABLE test.buffer (x UInt64, y UInt64) ENGINE = Buffer(test, dst, 1, 99999, 99999, 1, 1, 99999, 99999);
|
||||||
|
|
||||||
|
INSERT INTO test.buffer VALUES (1, 100);
|
||||||
|
INSERT INTO test.buffer VALUES (2, 200);
|
||||||
|
INSERT INTO test.buffer VALUES (3, 300);
|
||||||
|
SELECT 'init';
|
||||||
|
SELECT * FROM test.dst ORDER BY x;
|
||||||
|
SELECT '-';
|
||||||
|
SELECT * FROM test.buffer ORDER BY x;
|
||||||
|
|
||||||
|
ALTER TABLE test.dst DROP COLUMN x, MODIFY COLUMN y String, ADD COLUMN z String DEFAULT 'DEFZ';
|
||||||
|
|
||||||
|
INSERT INTO test.buffer VALUES (4, 400);
|
||||||
|
SELECT 'alt';
|
||||||
|
SELECT * FROM test.dst ORDER BY y;
|
||||||
|
SELECT '-';
|
||||||
|
SELECT * FROM test.buffer ORDER BY y;
|
||||||
|
|
||||||
|
OPTIMIZE TABLE test.buffer;
|
||||||
|
SELECT 'opt';
|
||||||
|
SELECT * FROM test.dst ORDER BY y;
|
||||||
|
SELECT '-';
|
||||||
|
SELECT * FROM test.buffer ORDER BY y;
|
||||||
|
|
||||||
|
SET send_logs_level = 'warning';
|
||||||
|
DROP TABLE IF EXISTS test.dst;
|
||||||
|
DROP TABLE IF EXISTS test.buffer;
|
@ -12,4 +12,4 @@ SELECT reverse([]);
|
|||||||
SELECT reverse([[[[]]]]);
|
SELECT reverse([[[[]]]]);
|
||||||
|
|
||||||
SET send_logs_level = 'none';
|
SET send_logs_level = 'none';
|
||||||
SELECT '[RE7', ( SELECT '\0' ) AS riwwq, ( SELECT reverse([( SELECT bitTestAll(NULL) ) , ( SELECT '\0' ) AS ddfweeuy]) ) AS xuvv, '', ( SELECT * FROM mysql() ) AS wqgdswyc, ( SELECT * FROM mysql() ); -- { serverError 42 }
|
SELECT '[RE7', ( SELECT '\0' ) AS riwwq, ( SELECT reverse([( SELECT bitTestAll(NULL) ) , ( SELECT '\0' ) AS ddfweeuy]) ) AS xuvv, '', ( SELECT * FROM file() ) AS wqgdswyc, ( SELECT * FROM file() ); -- { serverError 42 }
|
||||||
|
3
debian/clickhouse-client.postinst
vendored
3
debian/clickhouse-client.postinst
vendored
@ -4,6 +4,3 @@ set -e
|
|||||||
CLICKHOUSE_USER=${CLICKHOUSE_USER=clickhouse}
|
CLICKHOUSE_USER=${CLICKHOUSE_USER=clickhouse}
|
||||||
|
|
||||||
mkdir -p /etc/clickhouse-client/conf.d
|
mkdir -p /etc/clickhouse-client/conf.d
|
||||||
|
|
||||||
# user created by clickhouse-server package
|
|
||||||
chown -R ${CLICKHOUSE_USER} /etc/clickhouse-client || true
|
|
||||||
|
2
debian/clickhouse-server.cron.d
vendored
2
debian/clickhouse-server.cron.d
vendored
@ -1 +1 @@
|
|||||||
#*/10 * * * * root (which service > /dev/null 2>&1 && (service clickhouse-server condstart || true)) || /etc/init.d/clickhouse-server condstart > /dev/null 2>&1
|
#*/10 * * * * root (which service > /dev/null 2>&1 && (service clickhouse-server condstart ||:)) || /etc/init.d/clickhouse-server condstart > /dev/null 2>&1
|
||||||
|
6
debian/clickhouse-server.init
vendored
6
debian/clickhouse-server.init
vendored
@ -100,10 +100,6 @@ check_config()
|
|||||||
|
|
||||||
initdb()
|
initdb()
|
||||||
{
|
{
|
||||||
if [ -d ${SYSCONFDIR} ]; then
|
|
||||||
su -s /bin/sh ${CLICKHOUSE_USER} -c "test -w ${SYSCONFDIR}" || chown ${CLICKHOUSE_USER}:${CLICKHOUSE_GROUP} ${SYSCONFDIR}
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -x "$BINDIR/$EXTRACT_FROM_CONFIG" ]; then
|
if [ -x "$BINDIR/$EXTRACT_FROM_CONFIG" ]; then
|
||||||
CLICKHOUSE_DATADIR_FROM_CONFIG=$(su -s $SHELL ${CLICKHOUSE_USER} -c "$BINDIR/$EXTRACT_FROM_CONFIG --config-file=\"$CLICKHOUSE_CONFIG\" --key=path")
|
CLICKHOUSE_DATADIR_FROM_CONFIG=$(su -s $SHELL ${CLICKHOUSE_USER} -c "$BINDIR/$EXTRACT_FROM_CONFIG --config-file=\"$CLICKHOUSE_CONFIG\" --key=path")
|
||||||
if [ "(" "$?" -ne "0" ")" -o "(" -z "${CLICKHOUSE_DATADIR_FROM_CONFIG}" ")" ]; then
|
if [ "(" "$?" -ne "0" ")" -o "(" -z "${CLICKHOUSE_DATADIR_FROM_CONFIG}" ")" ]; then
|
||||||
@ -128,7 +124,7 @@ initdb()
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if ! $(su -s $SHELL ${CLICKHOUSE_USER} -c "test -O \"${CLICKHOUSE_DATADIR_FROM_CONFIG}\" && test -G \"${CLICKHOUSE_DATADIR_FROM_CONFIG}\""); then
|
if ! $(su -s $SHELL ${CLICKHOUSE_USER} -c "test -O \"${CLICKHOUSE_DATADIR_FROM_CONFIG}\" && test -G \"${CLICKHOUSE_DATADIR_FROM_CONFIG}\""); then
|
||||||
if [ $(dirname "${CLICKHOUSE_DATADIR_FROM_CONFIG}") == "/" ]; then
|
if [ $(dirname "${CLICKHOUSE_DATADIR_FROM_CONFIG}") = "/" ]; then
|
||||||
echo "Directory ${CLICKHOUSE_DATADIR_FROM_CONFIG} seems too dangerous to chown."
|
echo "Directory ${CLICKHOUSE_DATADIR_FROM_CONFIG} seems too dangerous to chown."
|
||||||
else
|
else
|
||||||
if [ ! -e "${CLICKHOUSE_DATADIR_FROM_CONFIG}" ]; then
|
if [ ! -e "${CLICKHOUSE_DATADIR_FROM_CONFIG}" ]; then
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user