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

This commit is contained in:
Ivan Blinkov 2018-11-28 11:59:45 +03:00
commit 178fe43f81
112 changed files with 3900 additions and 2650 deletions

View File

@ -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

View File

@ -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)

View File

@ -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
View 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)

View File

@ -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
View 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 ()

View File

@ -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 ()

View File

@ -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)

View File

@ -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})

View File

@ -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, "");

View File

@ -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

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -0,0 +1,2 @@
add_executable(test-connect test_connect.cpp)
target_link_libraries (test-connect dbms)

View 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";
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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);
}; };
}

View File

@ -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:

View File

@ -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;

View File

@ -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>();

View File

@ -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)

View 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);
}
}

View 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;
};
}

View File

@ -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()

View File

@ -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)

View 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; }
};
}

View 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;
}
};
}

View 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

View File

@ -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);

View File

@ -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 {};
} }

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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) || ...);
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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
};
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View 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);
}
}

View 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>();
}
}

View 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>();
}
}

View 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>();
}
}

View File

@ -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)

View File

@ -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();

View File

@ -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;

View File

@ -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;
} }

View File

@ -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;
}; };
} }

View File

@ -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() {}
}; };

View File

@ -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())

View File

@ -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();

View 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;
}
}

View 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);
}

View File

@ -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)
{ {

View File

@ -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);
} }

View File

@ -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).
} }
} }

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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);

View File

@ -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)

View File

@ -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;

View File

@ -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.
} }
} }
} }

View File

@ -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;

View File

@ -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.
} }
} }
} }

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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:

View File

@ -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:

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -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