From 18a13a03dd7689540d7148c9dca1141c69e856b1 Mon Sep 17 00:00:00 2001 From: chertus Date: Wed, 10 Jul 2019 21:12:50 +0300 Subject: [PATCH 01/32] memory tracked new/delete concept --- contrib/jemalloc-cmake/CMakeLists.txt | 1 - dbms/src/Common/CurrentThread.cpp | 6 +++++ dbms/src/Common/CurrentThread.h | 1 + dbms/src/Common/MemoryTracker.cpp | 28 ++++++++++++++++++---- dbms/src/Common/MemoryTracker.h | 6 ++++- dbms/src/Common/ThreadStatus.cpp | 5 ++++ dbms/src/Common/ThreadStatus.h | 2 ++ dbms/src/Common/new_delete.cpp | 34 +++++++++++++++++++++++++++ 8 files changed, 77 insertions(+), 6 deletions(-) create mode 100644 dbms/src/Common/new_delete.cpp diff --git a/contrib/jemalloc-cmake/CMakeLists.txt b/contrib/jemalloc-cmake/CMakeLists.txt index 4840197c2fd..47f057c0559 100644 --- a/contrib/jemalloc-cmake/CMakeLists.txt +++ b/contrib/jemalloc-cmake/CMakeLists.txt @@ -15,7 +15,6 @@ ${JEMALLOC_SOURCE_DIR}/src/extent_mmap.c ${JEMALLOC_SOURCE_DIR}/src/hash.c ${JEMALLOC_SOURCE_DIR}/src/hook.c ${JEMALLOC_SOURCE_DIR}/src/jemalloc.c -${JEMALLOC_SOURCE_DIR}/src/jemalloc_cpp.cpp ${JEMALLOC_SOURCE_DIR}/src/large.c ${JEMALLOC_SOURCE_DIR}/src/log.c ${JEMALLOC_SOURCE_DIR}/src/malloc_io.c diff --git a/dbms/src/Common/CurrentThread.cpp b/dbms/src/Common/CurrentThread.cpp index 5186cec0c41..5ef8d60309c 100644 --- a/dbms/src/Common/CurrentThread.cpp +++ b/dbms/src/Common/CurrentThread.cpp @@ -46,6 +46,12 @@ MemoryTracker * CurrentThread::getMemoryTracker() return ¤t_thread->memory_tracker; } +Int64 & CurrentThread::getUntrackedMemory() +{ + /// It assumes that (current_thread != nullptr) is already checked with getMemoryTracker() + return current_thread->untracked_memory; +} + void CurrentThread::updateProgressIn(const Progress & value) { if (unlikely(!current_thread)) diff --git a/dbms/src/Common/CurrentThread.h b/dbms/src/Common/CurrentThread.h index 3c248ad903f..cfab52dcdf5 100644 --- a/dbms/src/Common/CurrentThread.h +++ b/dbms/src/Common/CurrentThread.h @@ -47,6 +47,7 @@ public: static ProfileEvents::Counters & getProfileEvents(); static MemoryTracker * getMemoryTracker(); + static Int64 & getUntrackedMemory(); /// Update read and write rows (bytes) statistics (used in system.query_thread_log) static void updateProgressIn(const Progress & value); diff --git a/dbms/src/Common/MemoryTracker.cpp b/dbms/src/Common/MemoryTracker.cpp index bc324be4904..2d9380707d7 100644 --- a/dbms/src/Common/MemoryTracker.cpp +++ b/dbms/src/Common/MemoryTracker.cpp @@ -17,6 +17,7 @@ namespace DB static constexpr size_t log_peak_memory_usage_every = 1ULL << 30; +static constexpr Int64 untracked_memory_limit = 4 * 1024 * 1024; MemoryTracker::~MemoryTracker() @@ -191,19 +192,38 @@ namespace CurrentMemoryTracker void alloc(Int64 size) { if (auto memory_tracker = DB::CurrentThread::getMemoryTracker()) - memory_tracker->alloc(size); + { + Int64 & untracked = DB::CurrentThread::getUntrackedMemory(); + untracked += size; + if (untracked > untracked_memory_limit) + { + memory_tracker->alloc(untracked); + untracked = 0; + } + } } void realloc(Int64 old_size, Int64 new_size) { - if (auto memory_tracker = DB::CurrentThread::getMemoryTracker()) - memory_tracker->alloc(new_size - old_size); + Int64 addition = new_size - old_size; + if (addition > 0) + alloc(addition); + else + free(-addition); } void free(Int64 size) { if (auto memory_tracker = DB::CurrentThread::getMemoryTracker()) - memory_tracker->free(size); + { + Int64 & untracked = DB::CurrentThread::getUntrackedMemory(); + untracked -= size; + if (untracked < -untracked_memory_limit) + { + memory_tracker->free(-untracked); + untracked = 0; + } + } } } diff --git a/dbms/src/Common/MemoryTracker.h b/dbms/src/Common/MemoryTracker.h index 9f439c7550c..4ce0ac262fa 100644 --- a/dbms/src/Common/MemoryTracker.h +++ b/dbms/src/Common/MemoryTracker.h @@ -45,7 +45,11 @@ public: void realloc(Int64 old_size, Int64 new_size) { - alloc(new_size - old_size); + Int64 addition = new_size - old_size; + if (addition > 0) + alloc(addition); + else + free(-addition); } /** This function should be called after memory deallocation. diff --git a/dbms/src/Common/ThreadStatus.cpp b/dbms/src/Common/ThreadStatus.cpp index c2e415ab363..2e57a5237b4 100644 --- a/dbms/src/Common/ThreadStatus.cpp +++ b/dbms/src/Common/ThreadStatus.cpp @@ -50,6 +50,11 @@ ThreadStatus::ThreadStatus() ThreadStatus::~ThreadStatus() { + if (untracked_memory > 0) + memory_tracker.alloc(untracked_memory); + else + memory_tracker.free(-untracked_memory); + if (deleter) deleter(); current_thread = nullptr; diff --git a/dbms/src/Common/ThreadStatus.h b/dbms/src/Common/ThreadStatus.h index 0d36024b15d..6ea6f5c19fb 100644 --- a/dbms/src/Common/ThreadStatus.h +++ b/dbms/src/Common/ThreadStatus.h @@ -92,6 +92,8 @@ public: /// TODO: merge them into common entity ProfileEvents::Counters performance_counters{VariableContext::Thread}; MemoryTracker memory_tracker{VariableContext::Thread}; + /// Small amount of untracked memory (per thread atomic-less counter) + Int64 untracked_memory = 0; /// Statistics of read and write rows/bytes Progress progress_in; diff --git a/dbms/src/Common/new_delete.cpp b/dbms/src/Common/new_delete.cpp new file mode 100644 index 00000000000..faf638e713b --- /dev/null +++ b/dbms/src/Common/new_delete.cpp @@ -0,0 +1,34 @@ +#include + +#include +#include + +/// Replace default new/delete with memory tracking versions. +/// @sa https://en.cppreference.com/w/cpp/memory/new/operator_new +/// https://en.cppreference.com/w/cpp/memory/new/operator_delete +#if 1 + +void * operator new (std::size_t size) +{ + CurrentMemoryTracker::alloc(size); + + auto * ptr = malloc(size); + if (likely(ptr != nullptr)) + return ptr; + + CurrentMemoryTracker::free(size); + + /// @note no std::get_new_handler logic implemented + std::__throw_bad_alloc(); +} + +/// Called instead of 'delete(void * ptr)' if a user-defined replacement is provided +void operator delete (void * ptr, std::size_t size) noexcept +{ + CurrentMemoryTracker::free(size); + + if (likely(ptr != nullptr)) + free(ptr); +} + +#endif From 9739ac13e43c5d7148b5495d60f661c89296ccd1 Mon Sep 17 00:00:00 2001 From: chertus Date: Thu, 11 Jul 2019 18:40:55 +0300 Subject: [PATCH 02/32] move new/delete overloads to dbms/src/Common --- dbms/CMakeLists.txt | 1 + dbms/src/Common/MemoryTracker.cpp | 10 ++- dbms/src/Common/config.h.in | 1 + dbms/src/Common/new_delete.cpp | 93 ++++++++++++++++++++----- libs/libcommon/CMakeLists.txt | 1 + libs/libcommon/include/common/memory.h | 96 ++++++++++++++++++++++++++ libs/libcommon/src/jemalloc_cpp.cpp | 37 ++++++++++ 7 files changed, 221 insertions(+), 18 deletions(-) create mode 100644 libs/libcommon/include/common/memory.h create mode 100644 libs/libcommon/src/jemalloc_cpp.cpp diff --git a/dbms/CMakeLists.txt b/dbms/CMakeLists.txt index bcbe435569f..260040579df 100644 --- a/dbms/CMakeLists.txt +++ b/dbms/CMakeLists.txt @@ -365,6 +365,7 @@ endif() if (USE_JEMALLOC) target_include_directories (dbms SYSTEM BEFORE PRIVATE ${JEMALLOC_INCLUDE_DIR}) # used in Interpreters/AsynchronousMetrics.cpp + target_include_directories (clickhouse_common_io SYSTEM BEFORE PRIVATE ${JEMALLOC_INCLUDE_DIR}) # new_delete.cpp endif () target_include_directories (dbms PUBLIC ${DBMS_INCLUDE_DIR} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/src/Formats/include) diff --git a/dbms/src/Common/MemoryTracker.cpp b/dbms/src/Common/MemoryTracker.cpp index 2d9380707d7..a7af62636a0 100644 --- a/dbms/src/Common/MemoryTracker.cpp +++ b/dbms/src/Common/MemoryTracker.cpp @@ -85,7 +85,7 @@ void MemoryTracker::alloc(Int64 size) if (unlikely(fault_probability && drand48() < fault_probability)) { free(size); - +#if 0 std::stringstream message; message << "Memory tracker"; if (description) @@ -95,12 +95,15 @@ void MemoryTracker::alloc(Int64 size) << ", maximum: " << formatReadableSizeWithBinarySuffix(current_limit); throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); +#else + throw DB::Exception("Memory limit exceeded", DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); +#endif } if (unlikely(current_limit && will_be > current_limit)) { free(size); - +#if 0 std::stringstream message; message << "Memory limit"; if (description) @@ -110,6 +113,9 @@ void MemoryTracker::alloc(Int64 size) << ", maximum: " << formatReadableSizeWithBinarySuffix(current_limit); throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); +#else + throw DB::Exception("Memory limit exceeded", DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); +#endif } auto peak_old = peak.load(std::memory_order_relaxed); diff --git a/dbms/src/Common/config.h.in b/dbms/src/Common/config.h.in index 9b38dd9fc04..556eeebec58 100644 --- a/dbms/src/Common/config.h.in +++ b/dbms/src/Common/config.h.in @@ -9,5 +9,6 @@ #cmakedefine01 USE_CPUINFO #cmakedefine01 USE_BROTLI #cmakedefine01 USE_MIMALLOC +#cmakedefine01 USE_JEMALLOC #cmakedefine01 CLICKHOUSE_SPLIT_BINARY diff --git a/dbms/src/Common/new_delete.cpp b/dbms/src/Common/new_delete.cpp index faf638e713b..c0e6633c2c5 100644 --- a/dbms/src/Common/new_delete.cpp +++ b/dbms/src/Common/new_delete.cpp @@ -1,6 +1,6 @@ #include -#include +#include #include /// Replace default new/delete with memory tracking versions. @@ -8,27 +8,88 @@ /// https://en.cppreference.com/w/cpp/memory/new/operator_delete #if 1 -void * operator new (std::size_t size) +/// new + +void * operator new(std::size_t size) { CurrentMemoryTracker::alloc(size); - - auto * ptr = malloc(size); - if (likely(ptr != nullptr)) - return ptr; - - CurrentMemoryTracker::free(size); - - /// @note no std::get_new_handler logic implemented - std::__throw_bad_alloc(); + return Memory::newImpl(size); } -/// Called instead of 'delete(void * ptr)' if a user-defined replacement is provided -void operator delete (void * ptr, std::size_t size) noexcept +void * operator new[](std::size_t size) +{ + CurrentMemoryTracker::alloc(size); + return Memory::newImpl(size); +} + +void * operator new(std::size_t size, const std::nothrow_t &) noexcept +{ + CurrentMemoryTracker::alloc(size); + return Memory::newNoExept(size); +} + +void * operator new[](std::size_t size, const std::nothrow_t &) noexcept +{ + CurrentMemoryTracker::alloc(size); + return Memory::newNoExept(size); +} + +/// delete + +#if 0 +void operator delete(void * ptr) noexcept +{ + Memory::deleteImpl(ptr); +} + +void operator delete[](void * ptr) noexcept +{ + Memory::deleteImpl(ptr); +} + + +void operator delete(void * ptr, const std::nothrow_t &) noexcept +{ + Memory::deleteImpl(ptr); +} + +void operator delete[](void * ptr, const std::nothrow_t &) noexcept +{ + Memory::deleteImpl(ptr); +} +#endif + +void operator delete(void * ptr, std::size_t size) noexcept { CurrentMemoryTracker::free(size); - - if (likely(ptr != nullptr)) - free(ptr); + Memory::deleteSized(ptr, size); } +void operator delete[](void * ptr, std::size_t size) noexcept +{ + CurrentMemoryTracker::free(size); + Memory::deleteSized(ptr, size); +} + +#else + +/// new + +void * operator new(std::size_t size) { return Memory::newImpl(size); } +void * operator new[](std::size_t size) { return Memory::newImpl(size); } + +void * operator new(std::size_t size, const std::nothrow_t &) noexcept { return Memory::newNoExept(size); } +void * operator new[](std::size_t size, const std::nothrow_t &) noexcept { return Memory::newNoExept(size); } + +/// delete + +void operator delete(void * ptr) noexcept { Memory::deleteImpl(ptr); } +void operator delete[](void * ptr) noexcept { Memory::deleteImpl(ptr); } + +void operator delete(void * ptr, const std::nothrow_t &) noexcept { Memory::deleteImpl(ptr); } +void operator delete[](void * ptr, const std::nothrow_t &) noexcept { Memory::deleteImpl(ptr); } + +void operator delete(void * ptr, std::size_t size) noexcept { Memory::deleteSized(ptr, size); } +void operator delete[](void * ptr, std::size_t size) noexcept { Memory::deleteSized(ptr, size); } + #endif diff --git a/libs/libcommon/CMakeLists.txt b/libs/libcommon/CMakeLists.txt index e3fac95694a..914fc8ba4da 100644 --- a/libs/libcommon/CMakeLists.txt +++ b/libs/libcommon/CMakeLists.txt @@ -23,6 +23,7 @@ add_library(common src/getThreadNumber.cpp src/argsToConfig.cpp src/StackTrace.cpp + src/jemalloc_cpp.cpp include/common/SimpleCache.h include/common/StackTrace.h diff --git a/libs/libcommon/include/common/memory.h b/libs/libcommon/include/common/memory.h new file mode 100644 index 00000000000..4a93f27a4a2 --- /dev/null +++ b/libs/libcommon/include/common/memory.h @@ -0,0 +1,96 @@ +#pragma once + +#if __has_include() +#include +#endif + +#if USE_JEMALLOC + +#include +#include +#include + +#if defined(_MSC_VER) + #define ALWAYS_INLINE __forceinline + #define NO_INLINE static __declspec(noinline) +#else + #define ALWAYS_INLINE __attribute__((__always_inline__)) + #define NO_INLINE __attribute__((__noinline__)) +#endif + +namespace JeMalloc +{ + +void * handleOOM(std::size_t size, bool nothrow); + +ALWAYS_INLINE inline void * newImpl(std::size_t size) +{ + void * ptr = je_malloc(size); + if (likely(ptr != nullptr)) + return ptr; + + return handleOOM(size, false); +} + +ALWAYS_INLINE inline void * newNoExept(std::size_t size) noexcept +{ + void * ptr = je_malloc(size); + if (likely(ptr != nullptr)) + return ptr; + + return handleOOM(size, true); +} + +ALWAYS_INLINE inline void deleteImpl(void * ptr) noexcept +{ + je_free(ptr); +} + +ALWAYS_INLINE inline void deleteSized(void * ptr, std::size_t size) noexcept +{ + if (unlikely(ptr == nullptr)) + return; + + je_sdallocx(ptr, size, 0); +} + +} + +namespace Memory +{ + using namespace JeMalloc; +} + +#else + +namespace Memory +{ + +ALWAYS_INLINE inline void * newImpl(std::size_t size) +{ + auto * ptr = malloc(size); + if (likely(ptr != nullptr)) + return ptr; + + /// @note no std::get_new_handler logic implemented + std::__throw_bad_alloc(); +} + +ALWAYS_INLINE inline void * newNoExept(std::size_t size) noexcept +{ + return malloc(size); +} + +ALWAYS_INLINE inline void deleteImpl(void * ptr) noexcept +{ + free(ptr); +} + +ALWAYS_INLINE inline void deleteSized(void * ptr, std::size_t size) noexcept +{ + free(ptr); +} + +} + +#endif diff --git a/libs/libcommon/src/jemalloc_cpp.cpp b/libs/libcommon/src/jemalloc_cpp.cpp new file mode 100644 index 00000000000..72b3b1ef856 --- /dev/null +++ b/libs/libcommon/src/jemalloc_cpp.cpp @@ -0,0 +1,37 @@ +#include + +#if USE_JEMALLOC + +namespace JeMalloc +{ + +void * handleOOM(std::size_t size, bool nothrow) +{ + void * ptr = nullptr; + + while (ptr == nullptr) + { + std::new_handler handler = std::get_new_handler(); + if (handler == nullptr) + break; + + try + { + handler(); + } + catch (const std::bad_alloc &) + { + break; + } + + ptr = je_malloc(size); + } + + if (ptr == nullptr && !nothrow) + std::__throw_bad_alloc(); + return ptr; +} + +} + +#endif // USE_JEMALLOC From 9bd42366f0b50b74b1790983a5cf272bf3166d88 Mon Sep 17 00:00:00 2001 From: chertus Date: Fri, 12 Jul 2019 17:41:59 +0300 Subject: [PATCH 03/32] build fixes --- dbms/CMakeLists.txt | 6 ++++++ dbms/src/Common/new_delete.cpp | 21 ++++++++------------- libs/libcommon/include/common/memory.h | 20 ++++++++++---------- libs/libcommon/src/jemalloc_cpp.cpp | 2 +- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/dbms/CMakeLists.txt b/dbms/CMakeLists.txt index 260040579df..79cfd602a7c 100644 --- a/dbms/CMakeLists.txt +++ b/dbms/CMakeLists.txt @@ -90,6 +90,12 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_definitions ("-fno-tree-loop-distribute-patterns") endif () +if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR + CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + # Enable C++14 sized global deallocation functions. It should be anabled by setting -std=c++14 + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsized-deallocation") +endif () + add_subdirectory (src) set(dbms_headers) diff --git a/dbms/src/Common/new_delete.cpp b/dbms/src/Common/new_delete.cpp index c0e6633c2c5..b3e349ffc1a 100644 --- a/dbms/src/Common/new_delete.cpp +++ b/dbms/src/Common/new_delete.cpp @@ -36,7 +36,14 @@ void * operator new[](std::size_t size, const std::nothrow_t &) noexcept /// delete -#if 0 +/// C++17 std 21.6.2.1 (11) +/// If a function without a size parameter is defined, the program should also define the corresponding function with a size parameter. +/// If a function with a size parameter is defined, the program shall also define the corresponding version without the size parameter. + +/// cppreference: +/// It's unspecified whether size-aware or size-unaware version is called when deleting objects of +/// incomplete type and arrays of non-class and trivially-destructible class types. + void operator delete(void * ptr) noexcept { Memory::deleteImpl(ptr); @@ -47,18 +54,6 @@ void operator delete[](void * ptr) noexcept Memory::deleteImpl(ptr); } - -void operator delete(void * ptr, const std::nothrow_t &) noexcept -{ - Memory::deleteImpl(ptr); -} - -void operator delete[](void * ptr, const std::nothrow_t &) noexcept -{ - Memory::deleteImpl(ptr); -} -#endif - void operator delete(void * ptr, std::size_t size) noexcept { CurrentMemoryTracker::free(size); diff --git a/libs/libcommon/include/common/memory.h b/libs/libcommon/include/common/memory.h index 4a93f27a4a2..bd4c3ac578c 100644 --- a/libs/libcommon/include/common/memory.h +++ b/libs/libcommon/include/common/memory.h @@ -14,7 +14,7 @@ #define ALWAYS_INLINE __forceinline #define NO_INLINE static __declspec(noinline) #else - #define ALWAYS_INLINE __attribute__((__always_inline__)) + #define ALWAYS_INLINE inline __attribute__((__always_inline__)) #define NO_INLINE __attribute__((__noinline__)) #endif @@ -23,7 +23,7 @@ namespace JeMalloc void * handleOOM(std::size_t size, bool nothrow); -ALWAYS_INLINE inline void * newImpl(std::size_t size) +ALWAYS_INLINE void * newImpl(std::size_t size) { void * ptr = je_malloc(size); if (likely(ptr != nullptr)) @@ -32,7 +32,7 @@ ALWAYS_INLINE inline void * newImpl(std::size_t size) return handleOOM(size, false); } -ALWAYS_INLINE inline void * newNoExept(std::size_t size) noexcept +ALWAYS_INLINE void * newNoExept(std::size_t size) noexcept { void * ptr = je_malloc(size); if (likely(ptr != nullptr)) @@ -41,12 +41,12 @@ ALWAYS_INLINE inline void * newNoExept(std::size_t size) noexcept return handleOOM(size, true); } -ALWAYS_INLINE inline void deleteImpl(void * ptr) noexcept +ALWAYS_INLINE void deleteImpl(void * ptr) noexcept { je_free(ptr); } -ALWAYS_INLINE inline void deleteSized(void * ptr, std::size_t size) noexcept +ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size) noexcept { if (unlikely(ptr == nullptr)) return; @@ -66,27 +66,27 @@ namespace Memory namespace Memory { -ALWAYS_INLINE inline void * newImpl(std::size_t size) +ALWAYS_INLINE void * newImpl(std::size_t size) { auto * ptr = malloc(size); if (likely(ptr != nullptr)) return ptr; /// @note no std::get_new_handler logic implemented - std::__throw_bad_alloc(); + throw std::bad_alloc{}; } -ALWAYS_INLINE inline void * newNoExept(std::size_t size) noexcept +ALWAYS_INLINE void * newNoExept(std::size_t size) noexcept { return malloc(size); } -ALWAYS_INLINE inline void deleteImpl(void * ptr) noexcept +ALWAYS_INLINE void deleteImpl(void * ptr) noexcept { free(ptr); } -ALWAYS_INLINE inline void deleteSized(void * ptr, std::size_t size) noexcept +ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size) noexcept { free(ptr); } diff --git a/libs/libcommon/src/jemalloc_cpp.cpp b/libs/libcommon/src/jemalloc_cpp.cpp index 72b3b1ef856..fbf074a0fb5 100644 --- a/libs/libcommon/src/jemalloc_cpp.cpp +++ b/libs/libcommon/src/jemalloc_cpp.cpp @@ -28,7 +28,7 @@ void * handleOOM(std::size_t size, bool nothrow) } if (ptr == nullptr && !nothrow) - std::__throw_bad_alloc(); + throw std::bad_alloc{}; return ptr; } From a4bbb391953d79d6096c26b61fb10579912d3bc4 Mon Sep 17 00:00:00 2001 From: chertus Date: Fri, 12 Jul 2019 20:06:02 +0300 Subject: [PATCH 04/32] fix crash cause of recursion in allocs and memory tracking --- dbms/src/Common/MemoryTracker.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/dbms/src/Common/MemoryTracker.cpp b/dbms/src/Common/MemoryTracker.cpp index a7af62636a0..380dee75748 100644 --- a/dbms/src/Common/MemoryTracker.cpp +++ b/dbms/src/Common/MemoryTracker.cpp @@ -85,7 +85,10 @@ void MemoryTracker::alloc(Int64 size) if (unlikely(fault_probability && drand48() < fault_probability)) { free(size); -#if 0 + + /// Prevent recursion. Exception::ctor -> std::string -> new[] -> MemoryTracker::alloc + auto untrack_lock = blocker.cancel(); + std::stringstream message; message << "Memory tracker"; if (description) @@ -95,15 +98,15 @@ void MemoryTracker::alloc(Int64 size) << ", maximum: " << formatReadableSizeWithBinarySuffix(current_limit); throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); -#else - throw DB::Exception("Memory limit exceeded", DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); -#endif } if (unlikely(current_limit && will_be > current_limit)) { free(size); -#if 0 + + /// Prevent recursion. Exception::ctor -> std::string -> new[] -> MemoryTracker::alloc + auto untrack_lock = blocker.cancel(); + std::stringstream message; message << "Memory limit"; if (description) @@ -113,9 +116,6 @@ void MemoryTracker::alloc(Int64 size) << ", maximum: " << formatReadableSizeWithBinarySuffix(current_limit); throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); -#else - throw DB::Exception("Memory limit exceeded", DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); -#endif } auto peak_old = peak.load(std::memory_order_relaxed); From 8c715d9b91dde9b37e0c8697ba7b714574ff3f1e Mon Sep 17 00:00:00 2001 From: chertus Date: Fri, 12 Jul 2019 20:22:20 +0300 Subject: [PATCH 05/32] minor fix in cmake-files --- CMakeLists.txt | 5 +++++ dbms/CMakeLists.txt | 6 ------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c270cf7feb7..955550f0061 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,6 +182,11 @@ else () set (CXX_FLAGS_INTERNAL_COMPILER "-std=c++1z") endif () +if (COMPILER_GCC OR COMPILER_CLANG) + # Enable C++14 sized global deallocation functions. It should be enabled by setting -std=c++14 but I'm not sure. + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsized-deallocation") +endif () + option(WITH_COVERAGE "Build with coverage." 0) if(WITH_COVERAGE AND COMPILER_CLANG) set(COMPILER_FLAGS "${COMPILER_FLAGS} -fprofile-instr-generate -fcoverage-mapping") diff --git a/dbms/CMakeLists.txt b/dbms/CMakeLists.txt index 5af4449a5f4..97807a5c9d4 100644 --- a/dbms/CMakeLists.txt +++ b/dbms/CMakeLists.txt @@ -90,12 +90,6 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") add_definitions ("-fno-tree-loop-distribute-patterns") endif () -if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR - CMAKE_CXX_COMPILER_ID STREQUAL "GNU") - # Enable C++14 sized global deallocation functions. It should be anabled by setting -std=c++14 - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsized-deallocation") -endif () - add_subdirectory (src) set(dbms_headers) From 81d8597bb9062e0d747ae02675eda2c98696e594 Mon Sep 17 00:00:00 2001 From: chertus Date: Mon, 15 Jul 2019 16:19:56 +0300 Subject: [PATCH 06/32] memory tracking for size-unaware deletes with jemalloc --- dbms/src/Common/new_delete.cpp | 53 +++++++++++++++---- ...0877_memory_limit_for_new_delete.reference | 0 .../00877_memory_limit_for_new_delete.sql | 7 +++ 3 files changed, 50 insertions(+), 10 deletions(-) create mode 100644 dbms/tests/queries/0_stateless/00877_memory_limit_for_new_delete.reference create mode 100644 dbms/tests/queries/0_stateless/00877_memory_limit_for_new_delete.sql diff --git a/dbms/src/Common/new_delete.cpp b/dbms/src/Common/new_delete.cpp index b3e349ffc1a..f790eef85cf 100644 --- a/dbms/src/Common/new_delete.cpp +++ b/dbms/src/Common/new_delete.cpp @@ -8,30 +8,61 @@ /// https://en.cppreference.com/w/cpp/memory/new/operator_delete #if 1 +namespace Memory +{ + +ALWAYS_INLINE void * trackMemory(void * ptr [[maybe_unused]], std::size_t size [[maybe_unused]]) noexcept +{ +#ifdef USE_JEMALLOC + if (likely(ptr != nullptr)) + CurrentMemoryTracker::alloc(sallocx(ptr, 0)); +#else + CurrentMemoryTracker::alloc(size); +#endif + + return ptr; +} + +ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]]) noexcept +{ +#ifdef USE_JEMALLOC + if (likely(ptr != nullptr)) + CurrentMemoryTracker::free(sallocx(ptr, 0)); +#endif +} + +ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]], std::size_t size [[maybe_unused]]) noexcept +{ +#ifdef USE_JEMALLOC + if (likely(ptr != nullptr)) + CurrentMemoryTracker::free(sallocx(ptr, 0)); +#else + CurrentMemoryTracker::free(size); +#endif +} + +} + /// new void * operator new(std::size_t size) { - CurrentMemoryTracker::alloc(size); - return Memory::newImpl(size); + return Memory::trackMemory(Memory::newImpl(size), size); } void * operator new[](std::size_t size) { - CurrentMemoryTracker::alloc(size); - return Memory::newImpl(size); + return Memory::trackMemory(Memory::newImpl(size), size); } void * operator new(std::size_t size, const std::nothrow_t &) noexcept { - CurrentMemoryTracker::alloc(size); - return Memory::newNoExept(size); + return Memory::trackMemory(Memory::newNoExept(size), size); } void * operator new[](std::size_t size, const std::nothrow_t &) noexcept { - CurrentMemoryTracker::alloc(size); - return Memory::newNoExept(size); + return Memory::trackMemory(Memory::newNoExept(size), size); } /// delete @@ -46,23 +77,25 @@ void * operator new[](std::size_t size, const std::nothrow_t &) noexcept void operator delete(void * ptr) noexcept { + Memory::untrackMemory(ptr); Memory::deleteImpl(ptr); } void operator delete[](void * ptr) noexcept { + Memory::untrackMemory(ptr); Memory::deleteImpl(ptr); } void operator delete(void * ptr, std::size_t size) noexcept { - CurrentMemoryTracker::free(size); + Memory::untrackMemory(ptr, size); Memory::deleteSized(ptr, size); } void operator delete[](void * ptr, std::size_t size) noexcept { - CurrentMemoryTracker::free(size); + Memory::untrackMemory(ptr, size); Memory::deleteSized(ptr, size); } diff --git a/dbms/tests/queries/0_stateless/00877_memory_limit_for_new_delete.reference b/dbms/tests/queries/0_stateless/00877_memory_limit_for_new_delete.reference new file mode 100644 index 00000000000..e69de29bb2d diff --git a/dbms/tests/queries/0_stateless/00877_memory_limit_for_new_delete.sql b/dbms/tests/queries/0_stateless/00877_memory_limit_for_new_delete.sql new file mode 100644 index 00000000000..111104bb06e --- /dev/null +++ b/dbms/tests/queries/0_stateless/00877_memory_limit_for_new_delete.sql @@ -0,0 +1,7 @@ +SET max_memory_usage = 1000000000; + +SELECT sum(ignore(*)) FROM ( + SELECT number, argMax(number, (number, toFixedString(toString(number), 1024))) + FROM numbers(1000000) + GROUP BY number +) -- { serverError 241 } From bd821d189873987071a52eae122f3bcd3b0545d4 Mon Sep 17 00:00:00 2001 From: chertus Date: Mon, 15 Jul 2019 21:57:00 +0300 Subject: [PATCH 07/32] exception safety and fix sanitizers' builds --- dbms/src/Common/new_delete.cpp | 59 +++++++++++++++++--------- libs/libcommon/include/common/memory.h | 23 +++++----- 2 files changed, 51 insertions(+), 31 deletions(-) diff --git a/dbms/src/Common/new_delete.cpp b/dbms/src/Common/new_delete.cpp index f790eef85cf..6a5a881cbf4 100644 --- a/dbms/src/Common/new_delete.cpp +++ b/dbms/src/Common/new_delete.cpp @@ -11,34 +11,47 @@ namespace Memory { -ALWAYS_INLINE void * trackMemory(void * ptr [[maybe_unused]], std::size_t size [[maybe_unused]]) noexcept +ALWAYS_INLINE void trackMemory(std::size_t size) { -#ifdef USE_JEMALLOC - if (likely(ptr != nullptr)) - CurrentMemoryTracker::alloc(sallocx(ptr, 0)); +#if USE_JEMALLOC + /// The nallocx() function allocates no memory, but it performs the same size computation as the mallocx() function + /// @note je_mallocx() != je_malloc(). It's expected they don't differ much in allocation logic. + CurrentMemoryTracker::alloc(nallocx(size, 0)); #else CurrentMemoryTracker::alloc(size); #endif - - return ptr; } -ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]]) noexcept +ALWAYS_INLINE bool trackMemoryNoExept(std::size_t size) noexcept { -#ifdef USE_JEMALLOC - if (likely(ptr != nullptr)) - CurrentMemoryTracker::free(sallocx(ptr, 0)); -#endif + try + { + if (likely(size != 0)) + trackMemory(size); + } + catch (...) + { + return false; + } + + return true; } -ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]], std::size_t size [[maybe_unused]]) noexcept +ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]], std::size_t size [[maybe_unused]] = 0) noexcept { -#ifdef USE_JEMALLOC - if (likely(ptr != nullptr)) - CurrentMemoryTracker::free(sallocx(ptr, 0)); + try + { +#if USE_JEMALLOC + /// @note It's also possible to use je_malloc_usable_size() here. + if (likely(ptr != nullptr)) + CurrentMemoryTracker::free(sallocx(ptr, 0)); #else - CurrentMemoryTracker::free(size); + if (size) + CurrentMemoryTracker::free(size); #endif + } + catch (...) + {} } } @@ -47,22 +60,28 @@ ALWAYS_INLINE void untrackMemory(void * ptr [[maybe_unused]], std::size_t size [ void * operator new(std::size_t size) { - return Memory::trackMemory(Memory::newImpl(size), size); + Memory::trackMemory(size); + return Memory::newImpl(size); } void * operator new[](std::size_t size) { - return Memory::trackMemory(Memory::newImpl(size), size); + Memory::trackMemory(size); + return Memory::newImpl(size); } void * operator new(std::size_t size, const std::nothrow_t &) noexcept { - return Memory::trackMemory(Memory::newNoExept(size), size); + if (likely(Memory::trackMemoryNoExept(size))) + return Memory::newNoExept(size); + return nullptr; } void * operator new[](std::size_t size, const std::nothrow_t &) noexcept { - return Memory::trackMemory(Memory::newNoExept(size), size); + if (likely(Memory::trackMemoryNoExept(size))) + return Memory::newNoExept(size); + return nullptr; } /// delete diff --git a/libs/libcommon/include/common/memory.h b/libs/libcommon/include/common/memory.h index bd4c3ac578c..bcb49b957b2 100644 --- a/libs/libcommon/include/common/memory.h +++ b/libs/libcommon/include/common/memory.h @@ -1,15 +1,12 @@ #pragma once +#include +#include + #if __has_include() #include #endif -#if USE_JEMALLOC - -#include -#include -#include - #if defined(_MSC_VER) #define ALWAYS_INLINE __forceinline #define NO_INLINE static __declspec(noinline) @@ -18,6 +15,10 @@ #define NO_INLINE __attribute__((__noinline__)) #endif +#if USE_JEMALLOC + +#include + namespace JeMalloc { @@ -25,7 +26,7 @@ void * handleOOM(std::size_t size, bool nothrow); ALWAYS_INLINE void * newImpl(std::size_t size) { - void * ptr = je_malloc(size); + void * ptr = malloc(size); if (likely(ptr != nullptr)) return ptr; @@ -34,7 +35,7 @@ ALWAYS_INLINE void * newImpl(std::size_t size) ALWAYS_INLINE void * newNoExept(std::size_t size) noexcept { - void * ptr = je_malloc(size); + void * ptr = malloc(size); if (likely(ptr != nullptr)) return ptr; @@ -43,7 +44,7 @@ ALWAYS_INLINE void * newNoExept(std::size_t size) noexcept ALWAYS_INLINE void deleteImpl(void * ptr) noexcept { - je_free(ptr); + free(ptr); } ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size) noexcept @@ -51,7 +52,7 @@ ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size) noexcept if (unlikely(ptr == nullptr)) return; - je_sdallocx(ptr, size, 0); + sdallocx(ptr, size, 0); } } @@ -86,7 +87,7 @@ ALWAYS_INLINE void deleteImpl(void * ptr) noexcept free(ptr); } -ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size) noexcept +ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size [[maybe_unused]]) noexcept { free(ptr); } From 7065504d5ea75c8f55291a3e6c176fa297d5f582 Mon Sep 17 00:00:00 2001 From: chertus Date: Tue, 16 Jul 2019 14:56:46 +0300 Subject: [PATCH 08/32] fix throw from ThreadStatus dtor --- dbms/src/Common/ThreadStatus.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/dbms/src/Common/ThreadStatus.cpp b/dbms/src/Common/ThreadStatus.cpp index 2e57a5237b4..e9408d09bd5 100644 --- a/dbms/src/Common/ThreadStatus.cpp +++ b/dbms/src/Common/ThreadStatus.cpp @@ -50,10 +50,18 @@ ThreadStatus::ThreadStatus() ThreadStatus::~ThreadStatus() { - if (untracked_memory > 0) - memory_tracker.alloc(untracked_memory); - else - memory_tracker.free(-untracked_memory); + try + { + if (untracked_memory > 0) + memory_tracker.alloc(untracked_memory); + else + memory_tracker.free(-untracked_memory); + } + catch (const DB::Exception &) + { + /// It's a minor tracked memory leak here (not the memory itself but it's counter). + /// We've already allocated a little bit more then the limit and cannot track it in the thread memory tracker or its parent. + } if (deleter) deleter(); From 4bc79bca35a046ed2e657e6edf3f332fe60873e9 Mon Sep 17 00:00:00 2001 From: chertus Date: Tue, 16 Jul 2019 17:18:01 +0300 Subject: [PATCH 09/32] fix unbandled build & add comment --- dbms/src/Common/MemoryTracker.cpp | 1 + dbms/src/Common/config.h.in | 1 - libs/libcommon/include/common/memory.h | 12 ++++++++++-- libs/libcommon/src/jemalloc_cpp.cpp | 2 +- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/dbms/src/Common/MemoryTracker.cpp b/dbms/src/Common/MemoryTracker.cpp index 380dee75748..f59e4789b43 100644 --- a/dbms/src/Common/MemoryTracker.cpp +++ b/dbms/src/Common/MemoryTracker.cpp @@ -17,6 +17,7 @@ namespace DB static constexpr size_t log_peak_memory_usage_every = 1ULL << 30; +/// Each thread could new/delete memory in range of (-untracked_memory_limit, untracked_memory_limit) without access to common counters. static constexpr Int64 untracked_memory_limit = 4 * 1024 * 1024; diff --git a/dbms/src/Common/config.h.in b/dbms/src/Common/config.h.in index 556eeebec58..9b38dd9fc04 100644 --- a/dbms/src/Common/config.h.in +++ b/dbms/src/Common/config.h.in @@ -9,6 +9,5 @@ #cmakedefine01 USE_CPUINFO #cmakedefine01 USE_BROTLI #cmakedefine01 USE_MIMALLOC -#cmakedefine01 USE_JEMALLOC #cmakedefine01 CLICKHOUSE_SPLIT_BINARY diff --git a/libs/libcommon/include/common/memory.h b/libs/libcommon/include/common/memory.h index bcb49b957b2..93a8c600754 100644 --- a/libs/libcommon/include/common/memory.h +++ b/libs/libcommon/include/common/memory.h @@ -7,6 +7,16 @@ #include #endif +#if USE_JEMALLOC +#include + +#if JEMALLOC_VERSION_MAJOR < 4 + #undef USE_JEMALLOC + #define USE_JEMALLOC 0 + #include +#endif +#endif + #if defined(_MSC_VER) #define ALWAYS_INLINE __forceinline #define NO_INLINE static __declspec(noinline) @@ -17,8 +27,6 @@ #if USE_JEMALLOC -#include - namespace JeMalloc { diff --git a/libs/libcommon/src/jemalloc_cpp.cpp b/libs/libcommon/src/jemalloc_cpp.cpp index fbf074a0fb5..fdf941652c7 100644 --- a/libs/libcommon/src/jemalloc_cpp.cpp +++ b/libs/libcommon/src/jemalloc_cpp.cpp @@ -5,7 +5,7 @@ namespace JeMalloc { -void * handleOOM(std::size_t size, bool nothrow) +NO_INLINE void * handleOOM(std::size_t size, bool nothrow) { void * ptr = nullptr; From 019c156afacbc50478a933ff8bb40f67f92e2989 Mon Sep 17 00:00:00 2001 From: chertus Date: Tue, 16 Jul 2019 19:36:10 +0300 Subject: [PATCH 10/32] fix je_nalocx() call with 0 & remove std::new_handler logic --- dbms/src/Common/new_delete.cpp | 6 +-- libs/libcommon/CMakeLists.txt | 1 - libs/libcommon/include/common/memory.h | 72 ++++++-------------------- libs/libcommon/src/jemalloc_cpp.cpp | 37 ------------- 4 files changed, 19 insertions(+), 97 deletions(-) delete mode 100644 libs/libcommon/src/jemalloc_cpp.cpp diff --git a/dbms/src/Common/new_delete.cpp b/dbms/src/Common/new_delete.cpp index 6a5a881cbf4..7cab8bb5b0c 100644 --- a/dbms/src/Common/new_delete.cpp +++ b/dbms/src/Common/new_delete.cpp @@ -16,7 +16,8 @@ ALWAYS_INLINE void trackMemory(std::size_t size) #if USE_JEMALLOC /// The nallocx() function allocates no memory, but it performs the same size computation as the mallocx() function /// @note je_mallocx() != je_malloc(). It's expected they don't differ much in allocation logic. - CurrentMemoryTracker::alloc(nallocx(size, 0)); + if (likely(size != 0)) + CurrentMemoryTracker::alloc(nallocx(size, 0)); #else CurrentMemoryTracker::alloc(size); #endif @@ -26,8 +27,7 @@ ALWAYS_INLINE bool trackMemoryNoExept(std::size_t size) noexcept { try { - if (likely(size != 0)) - trackMemory(size); + trackMemory(size); } catch (...) { diff --git a/libs/libcommon/CMakeLists.txt b/libs/libcommon/CMakeLists.txt index e388b30db1c..1e3be7f61d6 100644 --- a/libs/libcommon/CMakeLists.txt +++ b/libs/libcommon/CMakeLists.txt @@ -23,7 +23,6 @@ add_library(common src/getThreadNumber.cpp src/argsToConfig.cpp src/StackTrace.cpp - src/jemalloc_cpp.cpp include/common/SimpleCache.h include/common/StackTrace.h diff --git a/libs/libcommon/include/common/memory.h b/libs/libcommon/include/common/memory.h index 93a8c600754..d8dced79cfb 100644 --- a/libs/libcommon/include/common/memory.h +++ b/libs/libcommon/include/common/memory.h @@ -17,60 +17,8 @@ #endif #endif -#if defined(_MSC_VER) - #define ALWAYS_INLINE __forceinline - #define NO_INLINE static __declspec(noinline) -#else - #define ALWAYS_INLINE inline __attribute__((__always_inline__)) - #define NO_INLINE __attribute__((__noinline__)) -#endif - -#if USE_JEMALLOC - -namespace JeMalloc -{ - -void * handleOOM(std::size_t size, bool nothrow); - -ALWAYS_INLINE void * newImpl(std::size_t size) -{ - void * ptr = malloc(size); - if (likely(ptr != nullptr)) - return ptr; - - return handleOOM(size, false); -} - -ALWAYS_INLINE void * newNoExept(std::size_t size) noexcept -{ - void * ptr = malloc(size); - if (likely(ptr != nullptr)) - return ptr; - - return handleOOM(size, true); -} - -ALWAYS_INLINE void deleteImpl(void * ptr) noexcept -{ - free(ptr); -} - -ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size) noexcept -{ - if (unlikely(ptr == nullptr)) - return; - - sdallocx(ptr, size, 0); -} - -} - -namespace Memory -{ - using namespace JeMalloc; -} - -#else +#define ALWAYS_INLINE inline __attribute__((__always_inline__)) +#define NO_INLINE __attribute__((__noinline__)) namespace Memory { @@ -95,11 +43,23 @@ ALWAYS_INLINE void deleteImpl(void * ptr) noexcept free(ptr); } +#if USE_JEMALLOC + +ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size) noexcept +{ + if (unlikely(ptr == nullptr)) + return; + + sdallocx(ptr, size, 0); +} + +#else + ALWAYS_INLINE void deleteSized(void * ptr, std::size_t size [[maybe_unused]]) noexcept { free(ptr); } -} - #endif + +} diff --git a/libs/libcommon/src/jemalloc_cpp.cpp b/libs/libcommon/src/jemalloc_cpp.cpp deleted file mode 100644 index fdf941652c7..00000000000 --- a/libs/libcommon/src/jemalloc_cpp.cpp +++ /dev/null @@ -1,37 +0,0 @@ -#include - -#if USE_JEMALLOC - -namespace JeMalloc -{ - -NO_INLINE void * handleOOM(std::size_t size, bool nothrow) -{ - void * ptr = nullptr; - - while (ptr == nullptr) - { - std::new_handler handler = std::get_new_handler(); - if (handler == nullptr) - break; - - try - { - handler(); - } - catch (const std::bad_alloc &) - { - break; - } - - ptr = je_malloc(size); - } - - if (ptr == nullptr && !nothrow) - throw std::bad_alloc{}; - return ptr; -} - -} - -#endif // USE_JEMALLOC From 3db106c1f241cead73cd2296ec8d5d8d81d556aa Mon Sep 17 00:00:00 2001 From: chertus Date: Tue, 16 Jul 2019 21:09:06 +0300 Subject: [PATCH 11/32] disable new/delete memory tracking for unbundled build --- dbms/src/Common/new_delete.cpp | 2 +- libs/libcommon/include/common/config_common.h.in | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dbms/src/Common/new_delete.cpp b/dbms/src/Common/new_delete.cpp index 7cab8bb5b0c..d9140f9459d 100644 --- a/dbms/src/Common/new_delete.cpp +++ b/dbms/src/Common/new_delete.cpp @@ -6,7 +6,7 @@ /// Replace default new/delete with memory tracking versions. /// @sa https://en.cppreference.com/w/cpp/memory/new/operator_new /// https://en.cppreference.com/w/cpp/memory/new/operator_delete -#if 1 +#if NOT_UNBUNDLED namespace Memory { diff --git a/libs/libcommon/include/common/config_common.h.in b/libs/libcommon/include/common/config_common.h.in index 0cc0950efba..247afd87aea 100644 --- a/libs/libcommon/include/common/config_common.h.in +++ b/libs/libcommon/include/common/config_common.h.in @@ -7,3 +7,4 @@ #cmakedefine01 USE_READLINE #cmakedefine01 USE_LIBEDIT #cmakedefine01 HAVE_READLINE_HISTORY +#cmakedefine01 NOT_UNBUNDLED From ce1bc54c0c1701d6c95d58ec76fc2b80a737102c Mon Sep 17 00:00:00 2001 From: chertus Date: Wed, 17 Jul 2019 15:40:05 +0300 Subject: [PATCH 12/32] infinite loop detection in MemoryTracker + shrink joins perf test into 1Gb memory usage --- dbms/src/Common/MemoryTracker.cpp | 10 +++++++++- dbms/src/Common/MemoryTracker.h | 2 ++ dbms/tests/performance/joins_in_memory.xml | 10 +++++----- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/dbms/src/Common/MemoryTracker.cpp b/dbms/src/Common/MemoryTracker.cpp index f59e4789b43..91309af1d6c 100644 --- a/dbms/src/Common/MemoryTracker.cpp +++ b/dbms/src/Common/MemoryTracker.cpp @@ -1,3 +1,5 @@ +#include + #include "MemoryTracker.h" #include #include @@ -115,7 +117,12 @@ void MemoryTracker::alloc(Int64 size) message << " exceeded: would use " << formatReadableSizeWithBinarySuffix(will_be) << " (attempt to allocate chunk of " << size << " bytes)" << ", maximum: " << formatReadableSizeWithBinarySuffix(current_limit); - +#ifndef NDEBUG + /// Prevent infinite loop in case of catching & new in cycle. + if (last_attempt_failed) + std::abort(); +#endif + ++last_attempt_failed; throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); } @@ -130,6 +137,7 @@ void MemoryTracker::alloc(Int64 size) if (auto loaded_next = parent.load(std::memory_order_relaxed)) loaded_next->alloc(size); + last_attempt_failed = 0; } diff --git a/dbms/src/Common/MemoryTracker.h b/dbms/src/Common/MemoryTracker.h index 4ce0ac262fa..73f46a447c7 100644 --- a/dbms/src/Common/MemoryTracker.h +++ b/dbms/src/Common/MemoryTracker.h @@ -115,6 +115,8 @@ public: /// To be able to temporarily stop memory tracker DB::SimpleActionBlocker blocker; + + UInt32 last_attempt_failed = 0; }; diff --git a/dbms/tests/performance/joins_in_memory.xml b/dbms/tests/performance/joins_in_memory.xml index 23b009a6027..1da400c48f4 100644 --- a/dbms/tests/performance/joins_in_memory.xml +++ b/dbms/tests/performance/joins_in_memory.xml @@ -13,11 +13,11 @@ CREATE TABLE ints (i64 Int64, i32 Int32, i16 Int16, i8 Int8) ENGINE = Memory - INSERT INTO ints SELECT number AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(10000) - INSERT INTO ints SELECT 10000 + number % 1000 AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(10000) - INSERT INTO ints SELECT 20000 + number % 100 AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(10000) - INSERT INTO ints SELECT 30000 + number % 10 AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(10000) - INSERT INTO ints SELECT 40000 + number % 1 AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(10000) + INSERT INTO ints SELECT number AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(5000) + INSERT INTO ints SELECT 10000 + number % 1000 AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(5000) + INSERT INTO ints SELECT 20000 + number % 100 AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(5000) + INSERT INTO ints SELECT 30000 + number % 10 AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(5000) + INSERT INTO ints SELECT 40000 + number % 1 AS i64, i64 AS i32, i64 AS i16, i64 AS i8 FROM numbers(5000) SELECT COUNT() FROM ints l ANY LEFT JOIN ints r USING i64 WHERE i32 = 200042 SELECT COUNT() FROM ints l ANY LEFT JOIN ints r USING i64,i32,i16,i8 WHERE i32 = 200042 From 90487058c42eaef00ac96aa4d639f00cb112fbca Mon Sep 17 00:00:00 2001 From: chertus Date: Wed, 17 Jul 2019 18:16:28 +0300 Subject: [PATCH 13/32] remove wrong infinite loop ckeck --- dbms/src/Common/MemoryTracker.cpp | 8 +------- dbms/src/Common/MemoryTracker.h | 2 -- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/dbms/src/Common/MemoryTracker.cpp b/dbms/src/Common/MemoryTracker.cpp index 91309af1d6c..5a8e6118030 100644 --- a/dbms/src/Common/MemoryTracker.cpp +++ b/dbms/src/Common/MemoryTracker.cpp @@ -117,12 +117,7 @@ void MemoryTracker::alloc(Int64 size) message << " exceeded: would use " << formatReadableSizeWithBinarySuffix(will_be) << " (attempt to allocate chunk of " << size << " bytes)" << ", maximum: " << formatReadableSizeWithBinarySuffix(current_limit); -#ifndef NDEBUG - /// Prevent infinite loop in case of catching & new in cycle. - if (last_attempt_failed) - std::abort(); -#endif - ++last_attempt_failed; + throw DB::Exception(message.str(), DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED); } @@ -137,7 +132,6 @@ void MemoryTracker::alloc(Int64 size) if (auto loaded_next = parent.load(std::memory_order_relaxed)) loaded_next->alloc(size); - last_attempt_failed = 0; } diff --git a/dbms/src/Common/MemoryTracker.h b/dbms/src/Common/MemoryTracker.h index 73f46a447c7..4ce0ac262fa 100644 --- a/dbms/src/Common/MemoryTracker.h +++ b/dbms/src/Common/MemoryTracker.h @@ -115,8 +115,6 @@ public: /// To be able to temporarily stop memory tracker DB::SimpleActionBlocker blocker; - - UInt32 last_attempt_failed = 0; }; From afa2bd6dfb9c56ad3265ab050fb7c8f6b0450e7d Mon Sep 17 00:00:00 2001 From: chertus Date: Thu, 18 Jul 2019 01:48:31 +0300 Subject: [PATCH 14/32] allow alloc 4Mb more after out-of-limit exception --- dbms/src/Common/MemoryTracker.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dbms/src/Common/MemoryTracker.cpp b/dbms/src/Common/MemoryTracker.cpp index 5a8e6118030..b3d661d95ee 100644 --- a/dbms/src/Common/MemoryTracker.cpp +++ b/dbms/src/Common/MemoryTracker.cpp @@ -206,8 +206,11 @@ namespace CurrentMemoryTracker untracked += size; if (untracked > untracked_memory_limit) { - memory_tracker->alloc(untracked); + /// Zero untracked before track. If tracker throws out-of-limit we would be able to alloc up to untracked_memory_limit bytes + /// more. It could be usefull for enlarge Exception message in rethrow logic. + Int64 tmp = untracked; untracked = 0; + memory_tracker->alloc(tmp); } } } From 34e1b81f84a456d63ad8c307a0154b92bb83f424 Mon Sep 17 00:00:00 2001 From: chertus Date: Thu, 18 Jul 2019 18:07:41 +0300 Subject: [PATCH 15/32] trying to speedup Allocator::realloc() --- dbms/src/Common/Allocator.h | 133 +++++++++++++++++++----------------- 1 file changed, 72 insertions(+), 61 deletions(-) diff --git a/dbms/src/Common/Allocator.h b/dbms/src/Common/Allocator.h index abaa5927e3d..0efbc410a00 100644 --- a/dbms/src/Common/Allocator.h +++ b/dbms/src/Common/Allocator.h @@ -114,7 +114,76 @@ public: void * alloc(size_t size, size_t alignment = 0) { CurrentMemoryTracker::alloc(size); + return allocNoTrack(size, alignment); + } + /// Free memory range. + void free(void * buf, size_t size) + { + freeNoTrack(buf, size); + CurrentMemoryTracker::free(size); + } + + /** Enlarge memory range. + * Data from old range is moved to the beginning of new range. + * Address of memory range could change. + */ + void * realloc(void * buf, size_t old_size, size_t new_size, size_t alignment = 0) + { + if (old_size == new_size) + { + /// nothing to do. + /// BTW, it's not possible to change alignment while doing realloc. + } + else if (old_size < mmap_threshold && new_size < mmap_threshold && alignment <= MALLOC_MIN_ALIGNMENT) + { + /// Resize malloc'd memory region with no special alignment requirement. + CurrentMemoryTracker::realloc(old_size, new_size); + + void * new_buf = ::realloc(buf, new_size); + if (nullptr == new_buf) + DB::throwFromErrno("Allocator: Cannot realloc from " + formatReadableSizeWithBinarySuffix(old_size) + " to " + formatReadableSizeWithBinarySuffix(new_size) + ".", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY); + + buf = new_buf; + if constexpr (clear_memory) + if (new_size > old_size) + memset(reinterpret_cast(buf) + old_size, 0, new_size - old_size); + } + else if (old_size >= mmap_threshold && new_size >= mmap_threshold) + { + /// Resize mmap'd memory region. + CurrentMemoryTracker::realloc(old_size, new_size); + + // On apple and freebsd self-implemented mremap used (common/mremap.h) + buf = clickhouse_mremap(buf, old_size, new_size, MREMAP_MAYMOVE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (MAP_FAILED == buf) + DB::throwFromErrno("Allocator: Cannot mremap memory chunk from " + formatReadableSizeWithBinarySuffix(old_size) + " to " + formatReadableSizeWithBinarySuffix(new_size) + ".", DB::ErrorCodes::CANNOT_MREMAP); + + /// No need for zero-fill, because mmap guarantees it. + } + else + { + /// All other cases that requires a copy. + CurrentMemoryTracker::realloc(old_size, new_size); + + void * new_buf = allocNoTrack(new_size, alignment); + memcpy(new_buf, buf, std::min(old_size, new_size)); + freeNoTrack(buf, old_size); + buf = new_buf; + } + + return buf; + } + +protected: + static constexpr size_t getStackThreshold() + { + return 0; + } + +private: + void * allocNoTrack(size_t size, size_t alignment) + { void * buf; if (size >= mmap_threshold) @@ -149,15 +218,14 @@ public: if (0 != res) DB::throwFromErrno("Cannot allocate memory (posix_memalign) " + formatReadableSizeWithBinarySuffix(size) + ".", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY, res); - if (clear_memory) + if constexpr (clear_memory) memset(buf, 0, size); } } return buf; } - /// Free memory range. - void free(void * buf, size_t size) + void freeNoTrack(void * buf, size_t size) { if (size >= mmap_threshold) { @@ -168,63 +236,6 @@ public: { ::free(buf); } - - CurrentMemoryTracker::free(size); - } - - /** Enlarge memory range. - * Data from old range is moved to the beginning of new range. - * Address of memory range could change. - */ - void * realloc(void * buf, size_t old_size, size_t new_size, size_t alignment = 0) - { - if (old_size == new_size) - { - /// nothing to do. - /// BTW, it's not possible to change alignment while doing realloc. - } - else if (old_size < mmap_threshold && new_size < mmap_threshold && alignment <= MALLOC_MIN_ALIGNMENT) - { - /// Resize malloc'd memory region with no special alignment requirement. - CurrentMemoryTracker::realloc(old_size, new_size); - - void * new_buf = ::realloc(buf, new_size); - if (nullptr == new_buf) - DB::throwFromErrno("Allocator: Cannot realloc from " + formatReadableSizeWithBinarySuffix(old_size) + " to " + formatReadableSizeWithBinarySuffix(new_size) + ".", DB::ErrorCodes::CANNOT_ALLOCATE_MEMORY); - - buf = new_buf; - if (clear_memory && new_size > old_size) - memset(reinterpret_cast(buf) + old_size, 0, new_size - old_size); - } - else if (old_size >= mmap_threshold && new_size >= mmap_threshold) - { - /// Resize mmap'd memory region. - CurrentMemoryTracker::realloc(old_size, new_size); - - // On apple and freebsd self-implemented mremap used (common/mremap.h) - buf = clickhouse_mremap(buf, old_size, new_size, MREMAP_MAYMOVE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (MAP_FAILED == buf) - DB::throwFromErrno("Allocator: Cannot mremap memory chunk from " + formatReadableSizeWithBinarySuffix(old_size) + " to " + formatReadableSizeWithBinarySuffix(new_size) + ".", DB::ErrorCodes::CANNOT_MREMAP); - - /// No need for zero-fill, because mmap guarantees it. - } - else - { - /// All other cases that requires a copy. MemoryTracker is called inside 'alloc', 'free' methods. - - void * new_buf = alloc(new_size, alignment); - memcpy(new_buf, buf, std::min(old_size, new_size)); - free(buf, old_size); - buf = new_buf; - } - - return buf; - } - -protected: - static constexpr size_t getStackThreshold() - { - return 0; } }; @@ -267,7 +278,7 @@ public: { if (size <= N) { - if (Base::clear_memory) + if constexpr (Base::clear_memory) memset(stack_memory, 0, N); return stack_memory; } From d8579714b8ea1a9117816f26a706e1a2293f8024 Mon Sep 17 00:00:00 2001 From: chertus Date: Thu, 18 Jul 2019 18:25:23 +0300 Subject: [PATCH 16/32] trying to speedup Allocator::realloc() (attempt 2) --- dbms/src/Common/Allocator.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/dbms/src/Common/Allocator.h b/dbms/src/Common/Allocator.h index 0efbc410a00..e9569673678 100644 --- a/dbms/src/Common/Allocator.h +++ b/dbms/src/Common/Allocator.h @@ -108,6 +108,7 @@ class AllocatorWithHint : Hint { protected: static constexpr bool clear_memory = clear_memory_; + static constexpr size_t small_memory_threshold = mmap_threshold; public: /// Allocate memory range. @@ -161,9 +162,9 @@ public: /// No need for zero-fill, because mmap guarantees it. } - else + else if (new_size < small_memory_threshold) { - /// All other cases that requires a copy. + /// Small allocs that requires a copy. Assume there's enough memory in system. Call CurrentMemoryTracker once. CurrentMemoryTracker::realloc(old_size, new_size); void * new_buf = allocNoTrack(new_size, alignment); @@ -171,6 +172,15 @@ public: freeNoTrack(buf, old_size); buf = new_buf; } + else + { + /// Big allocs that requires a copy. MemoryTracker is called inside 'alloc', 'free' methods. + + void * new_buf = alloc(new_size, alignment); + memcpy(new_buf, buf, std::min(old_size, new_size)); + free(buf, old_size); + buf = new_buf; + } return buf; } From 30ca545b09f0c55409acbab0cf6293427d595af9 Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Thu, 18 Jul 2019 18:56:58 +0300 Subject: [PATCH 17/32] fix name of a parameter --- docs/en/query_language/functions/ext_dict_functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/query_language/functions/ext_dict_functions.md b/docs/en/query_language/functions/ext_dict_functions.md index 017d941b9f6..959c09f08b0 100644 --- a/docs/en/query_language/functions/ext_dict_functions.md +++ b/docs/en/query_language/functions/ext_dict_functions.md @@ -116,7 +116,7 @@ Type: `UInt8`. For the hierarchical dictionary, returns an array of dictionary keys starting from passed `id_expr` and continuing along the chain of parent elements. ``` -dictGetHierarchy('dict_name', id) +dictGetHierarchy('dict_name', id_expr) ``` **Parameters** From 7d4ebe576d70edf90bd3ef7faddef769d522b3aa Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Thu, 18 Jul 2019 18:59:22 +0300 Subject: [PATCH 18/32] fix name of a parameter --- docs/en/query_language/functions/ext_dict_functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/query_language/functions/ext_dict_functions.md b/docs/en/query_language/functions/ext_dict_functions.md index 959c09f08b0..6494a2b643e 100644 --- a/docs/en/query_language/functions/ext_dict_functions.md +++ b/docs/en/query_language/functions/ext_dict_functions.md @@ -96,7 +96,7 @@ LIMIT 3 Checks whether the dictionary has the key. ``` -dictHas('dict_name', id) +dictHas('dict_name', id_expr) ``` **Parameters** From 9d5693bd69e7a40d0af9e94edafafe436a30be65 Mon Sep 17 00:00:00 2001 From: Amos Bird Date: Fri, 19 Jul 2019 02:16:36 +0800 Subject: [PATCH 19/32] fix clang build with certain toolchain --- dbms/src/Common/TaskStatsInfoGetter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dbms/src/Common/TaskStatsInfoGetter.cpp b/dbms/src/Common/TaskStatsInfoGetter.cpp index 35a68c5a90c..c8a0d5dcbf5 100644 --- a/dbms/src/Common/TaskStatsInfoGetter.cpp +++ b/dbms/src/Common/TaskStatsInfoGetter.cpp @@ -34,6 +34,11 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } +// Replace NLMSG_OK with explicit casts since that system macro contains signedness bugs which are not going to be fixed. +static inline bool is_nlmsg_ok(const struct nlmsghdr * const nlh, const ssize_t len) +{ + return len >= static_cast(sizeof(*nlh)) && nlh->nlmsg_len >= sizeof(*nlh) && static_cast(len) >= nlh->nlmsg_len; +} namespace { @@ -128,7 +133,7 @@ struct NetlinkMessage if (header.nlmsg_type == NLMSG_ERROR) throw Exception("Can't receive Netlink response: error " + std::to_string(error.error), ErrorCodes::NETLINK_ERROR); - if (!NLMSG_OK((&header), bytes_received)) + if (!is_nlmsg_ok((&header), bytes_received)) throw Exception("Can't receive Netlink response: wrong number of bytes received", ErrorCodes::NETLINK_ERROR); } }; From d3449e118f63b7cfdb5678827cdbb4c7d08c9f53 Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Thu, 18 Jul 2019 21:24:38 +0300 Subject: [PATCH 20/32] Update TaskStatsInfoGetter.cpp --- dbms/src/Common/TaskStatsInfoGetter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dbms/src/Common/TaskStatsInfoGetter.cpp b/dbms/src/Common/TaskStatsInfoGetter.cpp index c8a0d5dcbf5..b361161483a 100644 --- a/dbms/src/Common/TaskStatsInfoGetter.cpp +++ b/dbms/src/Common/TaskStatsInfoGetter.cpp @@ -133,7 +133,7 @@ struct NetlinkMessage if (header.nlmsg_type == NLMSG_ERROR) throw Exception("Can't receive Netlink response: error " + std::to_string(error.error), ErrorCodes::NETLINK_ERROR); - if (!is_nlmsg_ok((&header), bytes_received)) + if (!is_nlmsg_ok(&header, bytes_received)) throw Exception("Can't receive Netlink response: wrong number of bytes received", ErrorCodes::NETLINK_ERROR); } }; From 71eed6507e10900141b09af2303f8ca98c646beb Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Thu, 18 Jul 2019 23:10:31 +0300 Subject: [PATCH 21/32] Removed manual memory tracking when appropriate --- dbms/src/AggregateFunctions/QuantileTiming.h | 7 ------- dbms/src/Common/CombinedCardinalityEstimator.h | 7 ------- dbms/src/Common/HyperLogLogWithSmallSetOptimization.h | 7 ------- 3 files changed, 21 deletions(-) diff --git a/dbms/src/AggregateFunctions/QuantileTiming.h b/dbms/src/AggregateFunctions/QuantileTiming.h index 131ca91dbbf..fbf4da725c0 100644 --- a/dbms/src/AggregateFunctions/QuantileTiming.h +++ b/dbms/src/AggregateFunctions/QuantileTiming.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include #include @@ -513,8 +512,6 @@ private: void mediumToLarge() { - CurrentMemoryTracker::alloc(sizeof(detail::QuantileTimingLarge)); - /// While the data is copied from medium, it is not possible to set `large` value (otherwise it will overwrite some data). detail::QuantileTimingLarge * tmp_large = new detail::QuantileTimingLarge; @@ -528,8 +525,6 @@ private: void tinyToLarge() { - CurrentMemoryTracker::alloc(sizeof(detail::QuantileTimingLarge)); - /// While the data is copied from `medium` it is not possible to set `large` value (otherwise it will overwrite some data). detail::QuantileTimingLarge * tmp_large = new detail::QuantileTimingLarge; @@ -562,8 +557,6 @@ public: else if (kind == Kind::Large) { delete large; - - CurrentMemoryTracker::free(sizeof(detail::QuantileTimingLarge)); } } diff --git a/dbms/src/Common/CombinedCardinalityEstimator.h b/dbms/src/Common/CombinedCardinalityEstimator.h index 824f0a8c018..e048e47cab5 100644 --- a/dbms/src/Common/CombinedCardinalityEstimator.h +++ b/dbms/src/Common/CombinedCardinalityEstimator.h @@ -3,7 +3,6 @@ #include #include #include -#include #include @@ -230,7 +229,6 @@ private: if (getContainerType() != details::ContainerType::SMALL) throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR); - CurrentMemoryTracker::alloc(sizeof(Medium)); auto tmp_medium = std::make_unique(); for (const auto & x : small) @@ -247,7 +245,6 @@ private: if ((container_type != details::ContainerType::SMALL) && (container_type != details::ContainerType::MEDIUM)) throw Poco::Exception("Internal error", ErrorCodes::LOGICAL_ERROR); - CurrentMemoryTracker::alloc(sizeof(Large)); auto tmp_large = std::make_unique(); if (container_type == details::ContainerType::SMALL) @@ -277,15 +274,11 @@ private: { delete medium; medium = nullptr; - - CurrentMemoryTracker::free(sizeof(Medium)); } else if (container_type == details::ContainerType::LARGE) { delete large; large = nullptr; - - CurrentMemoryTracker::free(sizeof(Large)); } } diff --git a/dbms/src/Common/HyperLogLogWithSmallSetOptimization.h b/dbms/src/Common/HyperLogLogWithSmallSetOptimization.h index 836fbda222e..548b745cb6e 100644 --- a/dbms/src/Common/HyperLogLogWithSmallSetOptimization.h +++ b/dbms/src/Common/HyperLogLogWithSmallSetOptimization.h @@ -4,7 +4,6 @@ #include #include -#include namespace DB @@ -39,8 +38,6 @@ private: void toLarge() { - CurrentMemoryTracker::alloc(sizeof(Large)); - /// At the time of copying data from `tiny`, setting the value of `large` is still not possible (otherwise it will overwrite some data). Large * tmp_large = new Large; @@ -56,11 +53,7 @@ public: ~HyperLogLogWithSmallSetOptimization() { if (isLarge()) - { delete large; - - CurrentMemoryTracker::free(sizeof(Large)); - } } void insert(Key value) From 839acc948ec623fc1cb2dcf23dba86d8a81f992e Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Thu, 18 Jul 2019 23:44:05 +0300 Subject: [PATCH 22/32] add russian documentation about functions for working with external dictionaries --- .../functions/ext_dict_functions.md | 200 +++++++++++++++--- 1 file changed, 176 insertions(+), 24 deletions(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index 8901292aeb2..56aa65976b5 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -1,40 +1,192 @@ # Функции для работы с внешними словарями {#ext_dict_functions} -Информация о подключении и настройке внешних словарей смотрите в разделе [Внешние словари](../dicts/external_dicts.md). +Для получения информации по подключению и настройке внешних словарей, читайте [Внешние словари](../dicts/external_dicts.md). -## dictGetUInt8, dictGetUInt16, dictGetUInt32, dictGetUInt64 +## dictGet -## dictGetInt8, dictGetInt16, dictGetInt32, dictGetInt64 +Получение значения из внешнего словаря -## dictGetFloat32, dictGetFloat64 +``` +dictGet('dict_name', 'attr_name', id_expr) +dictGetOrDefault('dict_name', 'attr_name', id_expr, default_value_expr) +``` -## dictGetDate, dictGetDateTime +**Параметры** -## dictGetUUID +- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). +- `attr_name` — Название колонки словаря. [String literal](../syntax.md#syntax-string-literal). +- `id_expr` — Значение ключа. [Expression](../syntax.md#syntax-expressions) Возвращает значение типа [UInt64](../../data_types/int_uint.md) или [Tuple](../../data_types/tuple.md) в зависимости от конфигурации словаря. +- `default_value_expr` — Значение которое возвращается, если словарь не содержит колонку с ключом `id_expr`. [Expression](../syntax.md#syntax-expressions) возвращает значение такого же типа, что и у атрибута `attr_name`. -## dictGetString -`dictGetT('dict_name', 'attr_name', id)` -- получить из словаря dict_name значение атрибута attr_name по ключу id. -`dict_name` и `attr_name` - константные строки. -`id` должен иметь тип UInt64. -Если ключа `id` нет в словаре - вернуть значение по умолчанию, заданное в описании словаря. +**Возвращаемое значение** -## dictGetTOrDefault +- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным типом [attribute's data Тип](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря для заданного `id_expr`. +- Если запращиваемого `id_expr` не оказалось в словаре: -`dictGetT('dict_name', 'attr_name', id, default)` + - `dictGet` возвратит содержимое элемента `` определенного в настройках словаря. + - `dictGetOrDefault` вернет значение переданного `default_value_expr` параметра. -Аналогично функциям `dictGetT`, но значение по умолчанию берётся из последнего аргумента функции. +ClickHouse бросает исключение, если не может обработать значение атрибута или значение не сопоставимо с типом атрибута. -## dictIsIn -`dictIsIn('dict_name', child_id, ancestor_id)` -- для иерархического словаря dict_name - узнать, находится ли ключ child_id внутри ancestor_id (или совпадает с ancestor_id). Возвращает UInt8. +**Примеры использования** + +Создайте файл `ext-dict-text.csv` со следующим содержимым: + +```text +1,1 +2,2 +``` + +Первая колонка - это `id`, вторая - `c1` + +Конфигурация внешнего словаря: + +```xml + + + ext-dict-test + + + /path-to/ext-dict-test.csv + CSV + + + + + + + + id + + + c1 + <Тип>UInt32 + + + + 0 + + +``` + +Выполните запрос: + +```sql +SELECT + dictGetOrDefault('ext-dict-test', 'c1', number + 1, toUInt32(number * 10)) AS val, + toТипName(val) AS Тип +FROM system.numbers +LIMIT 3 +``` +```text +┌─val─┬─Тип───┐ +│ 1 │ UInt32 │ +│ 2 │ UInt32 │ +│ 20 │ UInt32 │ +└─────┴────────┘ +``` + +**Читайте так же** + +- [External Dictionaries](../dicts/external_dicts.md) -## dictGetHierarchy -`dictGetHierarchy('dict_name', id)` -- для иерархического словаря dict_name - вернуть массив ключей словаря, начиная с id и продолжая цепочкой родительских элементов. Возвращает Array(UInt64). ## dictHas -`dictHas('dict_name', id)` -- проверить наличие ключа в словаре. Возвращает значение типа UInt8, равное 0, если ключа нет и 1, если ключ есть. -[Оригинальная статья](https://clickhouse.yandex/docs/ru/query_language/functions/ext_dict_functions/) +Проверяет наличие строки с заданным ключом в словаре. + +``` +dictHas('dict_name', id_expr) +``` + +**Параметры** + +- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). +- `id_expr` — Значение ключа. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). + +**Возвращаемое значение** + +- 0, если ключ не был обнаружен +- 1, если ключ присутствует в словаре + +Тип: `UInt8`. + +## dictGetHierarchy + +Для иерархических словарей, возвращает массив ключей, содержащий ключ `id_expr` и все ключи родительских элементов по цепочке. + +``` +dictGetHierarchy('dict_name', id_expr) +``` + +**Параметры** + +- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). +- `id_expr` — Значение ключа. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). + +**Возвращаемое значение** + +Иерархию ключей словаря. + +Тип: Array(UInt64). + +## dictIsIn + +Осуществляет проверку - является ли ключ родительским в иерархии словаря. + +`dictIsIn ('dict_name', child_id_expr, ancestor_id_expr)` + +**Параметры** + +- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). +- `child_id_expr` — Ключ который должен быть проверен. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). +- `ancestor_id_expr` — Родительский ключ для ключа `child_id_expr`. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). + +**Возвращаемое значение** + +- 0, если `child_id_expr` не является потомком для `ancestor_id_expr`. +- 1, если `child_id_expr` является потомком для `ancestor_id_expr` или если `child_id_expr` равен `ancestor_id_expr`. + +Тип: `UInt8`. + +## Другие функции {#ext_dict_functions-other} + +ClickHouse поддерживает специализированные функции для конвертации значений атрибутов словаря к определенному типу, независимо от настроек словаря. + +Функции: + +- `dictGetInt8`, `dictGetInt16`, `dictGetInt32`, `dictGetInt64` +- `dictGetUInt8`, `dictGetUInt16`, `dictGetUInt32`, `dictGetUInt64` +- `dictGetFloat32`, `dictGetFloat64` +- `dictGetDate` +- `dictGetDateTime` +- `dictGetUUID` +- `dictGetString` + +Все эти функции имеют так же `OrDefault` версию. Например, `dictGetDateOrDefault`. + +Синтаксис: + +``` +dictGet[Тип]('dict_name', 'attr_name', id_expr) +dictGet[Тип]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr) +``` + +**Параметры** + +- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). +- `attr_name` — Название колонки словаря. [String literal](../syntax.md#syntax-string-literal). +- `id_expr` — Значение ключа. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). +- `default_value_expr` — Значение которое возвращается, если словарь не содержит строку с ключом `id_expr`. [Expression](../syntax.md#syntax-expressions) возвращает значение с таким же типом, что и тип атрибута `attr_name`. + +**Возвращаемое значение** + +- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным типом [attribute's data Тип](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря с заданым ключом `id_expr`. +- Если запращиваемого `id_expr` не оказалось в словаре: + + - `dictGet[Тип]` возвратит содержимое элемента `` определенного в настройках словаря. + - `dictGet[Тип]OrDefault` вернет значение переданного `default_value_expr` параметра. + +ClickHouse бросает исключение, если не может обработать значение атрибута или значение не сопоставимо с типом атрибута + +[Исходная статья](https://clickhouse.yandex/docs/en/query_language/functions/ext_dict_functions/) From dcc0433dd6dcb727c913709139d218c260d843f7 Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Thu, 18 Jul 2019 23:59:01 +0300 Subject: [PATCH 23/32] fix typos --- .../functions/ext_dict_functions.md | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index 56aa65976b5..1574e1f93b1 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -1,6 +1,6 @@ # Функции для работы с внешними словарями {#ext_dict_functions} -Для получения информации по подключению и настройке внешних словарей, читайте [Внешние словари](../dicts/external_dicts.md). +Для получения информации по подключению и настройке внешних словарей, читайте [внешние словари](../dicts/external_dicts.md). ## dictGet @@ -13,20 +13,20 @@ dictGetOrDefault('dict_name', 'attr_name', id_expr, default_value_expr) **Параметры** -- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). -- `attr_name` — Название колонки словаря. [String literal](../syntax.md#syntax-string-literal). -- `id_expr` — Значение ключа. [Expression](../syntax.md#syntax-expressions) Возвращает значение типа [UInt64](../../data_types/int_uint.md) или [Tuple](../../data_types/tuple.md) в зависимости от конфигурации словаря. -- `default_value_expr` — Значение которое возвращается, если словарь не содержит колонку с ключом `id_expr`. [Expression](../syntax.md#syntax-expressions) возвращает значение такого же типа, что и у атрибута `attr_name`. +- `dict_name` — Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal). +- `attr_name` — Название колонки словаря. [Строковый литерал](../syntax.md#syntax-string-literal). +- `id_expr` — Значение ключа. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md) или [Tuple](../../data_types/tuple.md) в зависимости от конфигурации словаря. +- `default_value_expr` — Значение которое возвращается, если словарь не содержит колонку с ключом `id_expr`. [Выражение](../syntax.md#syntax-expressions) возвращает значение такого же типа, что и у атрибута `attr_name`. **Возвращаемое значение** -- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным типом [attribute's data Тип](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря для заданного `id_expr`. +- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным типом [типы данных атрибутов](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря для заданного `id_expr`. - Если запращиваемого `id_expr` не оказалось в словаре: - `dictGet` возвратит содержимое элемента `` определенного в настройках словаря. - `dictGetOrDefault` вернет значение переданного `default_value_expr` параметра. -ClickHouse бросает исключение, если не может обработать значение атрибута или значение не сопоставимо с типом атрибута. +ClickHouse бросает исключение, если не может обработать значение атрибута или значение несопоставимо с типом атрибута. **Примеры использования** @@ -88,7 +88,7 @@ LIMIT 3 **Читайте так же** -- [External Dictionaries](../dicts/external_dicts.md) +- [Внешние словари](../dicts/external_dicts.md) ## dictHas @@ -101,8 +101,8 @@ dictHas('dict_name', id_expr) **Параметры** -- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). -- `id_expr` — Значение ключа. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). +- `dict_name` — Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal). +- `id_expr` — Значение ключа. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). **Возвращаемое значение** @@ -121,8 +121,8 @@ dictGetHierarchy('dict_name', id_expr) **Параметры** -- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). -- `id_expr` — Значение ключа. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). +- `dict_name` — Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal). +- `id_expr` — Значение ключа. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). **Возвращаемое значение** @@ -138,9 +138,9 @@ dictGetHierarchy('dict_name', id_expr) **Параметры** -- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). -- `child_id_expr` — Ключ который должен быть проверен. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). -- `ancestor_id_expr` — Родительский ключ для ключа `child_id_expr`. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). +- `dict_name` — Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal). +- `child_id_expr` — Ключ который должен быть проверен. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). +- `ancestor_id_expr` — Родительский ключ для ключа `child_id_expr`. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). **Возвращаемое значение** @@ -174,19 +174,19 @@ dictGet[Тип]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr) **Параметры** -- `dict_name` — Название словаря. [String literal](../syntax.md#syntax-string-literal). -- `attr_name` — Название колонки словаря. [String literal](../syntax.md#syntax-string-literal). -- `id_expr` — Значение ключа. [Expression](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). -- `default_value_expr` — Значение которое возвращается, если словарь не содержит строку с ключом `id_expr`. [Expression](../syntax.md#syntax-expressions) возвращает значение с таким же типом, что и тип атрибута `attr_name`. +- `dict_name` — Название словаря. [Строковый литерал](../syntax.md#syntax-string-literal). +- `attr_name` — Название колонки словаря. [Строковый литерал](../syntax.md#syntax-string-literal). +- `id_expr` — Значение ключа. [Выражение](../syntax.md#syntax-expressions) возвращает значение типа [UInt64](../../data_types/int_uint.md). +- `default_value_expr` — Значение которое возвращается, если словарь не содержит строку с ключом `id_expr`. [Выражение](../syntax.md#syntax-expressions) возвращает значение с таким же типом, что и тип атрибута `attr_name`. **Возвращаемое значение** -- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным типом [attribute's data Тип](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря с заданым ключом `id_expr`. +- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным типом [типы данных атрибута](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря с заданым ключом `id_expr`. - Если запращиваемого `id_expr` не оказалось в словаре: - `dictGet[Тип]` возвратит содержимое элемента `` определенного в настройках словаря. - `dictGet[Тип]OrDefault` вернет значение переданного `default_value_expr` параметра. -ClickHouse бросает исключение, если не может обработать значение атрибута или значение не сопоставимо с типом атрибута +ClickHouse бросает исключение, если не может обработать значение атрибута или значение несопоставимо с типом атрибута [Исходная статья](https://clickhouse.yandex/docs/en/query_language/functions/ext_dict_functions/) From a5196861ffc39bcc7228061e403f79dadc64ec4a Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Fri, 19 Jul 2019 00:05:38 +0300 Subject: [PATCH 24/32] fix typos --- docs/ru/query_language/functions/ext_dict_functions.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index 1574e1f93b1..b1be0b7b906 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -1,6 +1,6 @@ # Функции для работы с внешними словарями {#ext_dict_functions} -Для получения информации по подключению и настройке внешних словарей, читайте [внешние словари](../dicts/external_dicts.md). +Для получения информации по подключению и настройке внешних словарей, читайте раздел про [внешние словари](../dicts/external_dicts.md). ## dictGet @@ -20,7 +20,7 @@ dictGetOrDefault('dict_name', 'attr_name', id_expr, default_value_expr) **Возвращаемое значение** -- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным типом [типы данных атрибутов](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря для заданного `id_expr`. +- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных атрибута](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря для заданного `id_expr`. - Если запращиваемого `id_expr` не оказалось в словаре: - `dictGet` возвратит содержимое элемента `` определенного в настройках словаря. @@ -86,7 +86,7 @@ LIMIT 3 └─────┴────────┘ ``` -**Читайте так же** +**Смотрите также** - [Внешние словари](../dicts/external_dicts.md) @@ -181,7 +181,7 @@ dictGet[Тип]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr) **Возвращаемое значение** -- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным типом [типы данных атрибута](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря с заданым ключом `id_expr`. +- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных атрибута](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря с заданым ключом `id_expr`. - Если запращиваемого `id_expr` не оказалось в словаре: - `dictGet[Тип]` возвратит содержимое элемента `` определенного в настройках словаря. From 1cb5b77fa870fadb34c9bbade950983e1a7ec4c9 Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Fri, 19 Jul 2019 00:09:56 +0300 Subject: [PATCH 25/32] fix typos --- docs/ru/query_language/functions/ext_dict_functions.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index b1be0b7b906..27fa0a31133 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -20,7 +20,7 @@ dictGetOrDefault('dict_name', 'attr_name', id_expr, default_value_expr) **Возвращаемое значение** -- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных атрибута](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря для заданного `id_expr`. +- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), то функция возвращает значение для заданного ключа `id_expr`. - Если запращиваемого `id_expr` не оказалось в словаре: - `dictGet` возвратит содержимое элемента `` определенного в настройках словаря. @@ -28,7 +28,7 @@ dictGetOrDefault('dict_name', 'attr_name', id_expr, default_value_expr) ClickHouse бросает исключение, если не может обработать значение атрибута или значение несопоставимо с типом атрибута. -**Примеры использования** +**Пример использования** Создайте файл `ext-dict-text.csv` со следующим содержимым: @@ -181,7 +181,7 @@ dictGet[Тип]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr) **Возвращаемое значение** -- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных атрибута](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), функция возвращает значение атрибута словаря с заданым ключом `id_expr`. +- Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes),то функция возвращает значение для заданного ключа `id_expr`. - Если запращиваемого `id_expr` не оказалось в словаре: - `dictGet[Тип]` возвратит содержимое элемента `` определенного в настройках словаря. From 60f3e4dd8c122dbf7d046d61f223f18dbe1e75b8 Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Fri, 19 Jul 2019 00:11:00 +0300 Subject: [PATCH 26/32] fix typos --- docs/ru/query_language/functions/ext_dict_functions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index 27fa0a31133..38e974e2043 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -1,10 +1,10 @@ # Функции для работы с внешними словарями {#ext_dict_functions} -Для получения информации по подключению и настройке внешних словарей, читайте раздел про [внешние словари](../dicts/external_dicts.md). +Для получения информации о подключении и настройке, читайте раздел про [внешние словари](../dicts/external_dicts.md). ## dictGet -Получение значения из внешнего словаря +Получение значения из внешнего словаря. ``` dictGet('dict_name', 'attr_name', id_expr) From 43c13d8283b10b06955b22ef23dfe7c6d6b00bf8 Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Fri, 19 Jul 2019 00:15:34 +0300 Subject: [PATCH 27/32] fix typos --- docs/ru/query_language/functions/ext_dict_functions.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index 38e974e2043..102b72135f9 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -21,7 +21,7 @@ dictGetOrDefault('dict_name', 'attr_name', id_expr, default_value_expr) **Возвращаемое значение** - Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes), то функция возвращает значение для заданного ключа `id_expr`. -- Если запращиваемого `id_expr` не оказалось в словаре: +- Если запрашиваемого `id_expr` не оказалось в словаре: - `dictGet` возвратит содержимое элемента `` определенного в настройках словаря. - `dictGetOrDefault` вернет значение переданного `default_value_expr` параметра. @@ -128,7 +128,7 @@ dictGetHierarchy('dict_name', id_expr) Иерархию ключей словаря. -Тип: Array(UInt64). +Тип: [Array(UInt64)](../../data_types/array.md). ## dictIsIn From 8e93b500fdf929053324f75081ac0d0d02a50fed Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Fri, 19 Jul 2019 00:16:45 +0300 Subject: [PATCH 28/32] fix typos --- docs/ru/query_language/functions/ext_dict_functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index 102b72135f9..d9a55c112f3 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -189,4 +189,4 @@ dictGet[Тип]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr) ClickHouse бросает исключение, если не может обработать значение атрибута или значение несопоставимо с типом атрибута -[Исходная статья](https://clickhouse.yandex/docs/en/query_language/functions/ext_dict_functions/) +[Оригинальная статья](https://clickhouse.yandex/docs/en/query_language/functions/ext_dict_functions/) From c2fee7345d3efe8e6200c1ff022b57a68db5943e Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Fri, 19 Jul 2019 00:21:16 +0300 Subject: [PATCH 29/32] fix typos --- docs/ru/query_language/functions/ext_dict_functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index d9a55c112f3..a8d54ce2585 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -182,7 +182,7 @@ dictGet[Тип]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr) **Возвращаемое значение** - Если ClickHouse успешно обрабатывает атрибут в соотвествии с указаным [типом данных](../dicts/external_dicts_dict_structure.md#ext_dict_structure-attributes),то функция возвращает значение для заданного ключа `id_expr`. -- Если запращиваемого `id_expr` не оказалось в словаре: +- Если запрашиваемого `id_expr` не оказалось в словаре: - `dictGet[Тип]` возвратит содержимое элемента `` определенного в настройках словаря. - `dictGet[Тип]OrDefault` вернет значение переданного `default_value_expr` параметра. From 7808f944e97eafab93e67a75685014549ecbd7a9 Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Fri, 19 Jul 2019 00:36:25 +0300 Subject: [PATCH 30/32] changes after code review --- .../query_language/functions/ext_dict_functions.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index a8d54ce2585..4ed10f98268 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -60,7 +60,7 @@ ClickHouse бросает исключение, если не может обр c1 - <Тип>UInt32 + UInt32 @@ -74,12 +74,12 @@ ClickHouse бросает исключение, если не может обр ```sql SELECT dictGetOrDefault('ext-dict-test', 'c1', number + 1, toUInt32(number * 10)) AS val, - toТипName(val) AS Тип + toТипName(val) AS Type FROM system.numbers LIMIT 3 ``` ```text -┌─val─┬─Тип───┐ +┌─val─┬─type───┐ │ 1 │ UInt32 │ │ 2 │ UInt32 │ │ 20 │ UInt32 │ @@ -93,7 +93,7 @@ LIMIT 3 ## dictHas -Проверяет наличие строки с заданным ключом в словаре. +Проверяет наличие записи с заданным ключом в словаре. ``` dictHas('dict_name', id_expr) @@ -113,7 +113,7 @@ dictHas('dict_name', id_expr) ## dictGetHierarchy -Для иерархических словарей, возвращает массив ключей, содержащий ключ `id_expr` и все ключи родительских элементов по цепочке. +Для иерархических словарей возвращает массив ключей, содержащий ключ `id_expr` и все ключи родительских элементов по цепочке. ``` dictGetHierarchy('dict_name', id_expr) @@ -189,4 +189,4 @@ dictGet[Тип]OrDefault('dict_name', 'attr_name', id_expr, default_value_expr) ClickHouse бросает исключение, если не может обработать значение атрибута или значение несопоставимо с типом атрибута -[Оригинальная статья](https://clickhouse.yandex/docs/en/query_language/functions/ext_dict_functions/) +[Оригинальная статья](https://clickhouse.yandex/docs/ru/query_language/functions/ext_dict_functions/) From 0a3bd5ed00132e23b0111ad20c502d76fb09f38f Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Fri, 19 Jul 2019 00:41:06 +0300 Subject: [PATCH 31/32] changes after code review --- docs/ru/query_language/functions/ext_dict_functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index 4ed10f98268..6e0c1172934 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -132,7 +132,7 @@ dictGetHierarchy('dict_name', id_expr) ## dictIsIn -Осуществляет проверку - является ли ключ родительским в иерархии словаря. +Осуществляет проверку - является ли ключ родительским во всех цепочке иерархии словаря. `dictIsIn ('dict_name', child_id_expr, ancestor_id_expr)` From 86d421cee6454276b2f9b9afa9b544efc475013d Mon Sep 17 00:00:00 2001 From: Artem Konovalov Date: Fri, 19 Jul 2019 00:42:48 +0300 Subject: [PATCH 32/32] changes after code review --- docs/ru/query_language/functions/ext_dict_functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ru/query_language/functions/ext_dict_functions.md b/docs/ru/query_language/functions/ext_dict_functions.md index 6e0c1172934..3fb4a110e88 100644 --- a/docs/ru/query_language/functions/ext_dict_functions.md +++ b/docs/ru/query_language/functions/ext_dict_functions.md @@ -132,7 +132,7 @@ dictGetHierarchy('dict_name', id_expr) ## dictIsIn -Осуществляет проверку - является ли ключ родительским во всех цепочке иерархии словаря. +Осуществляет проверку - является ли ключ родительским во всей иерархической цепочке словаря. `dictIsIn ('dict_name', child_id_expr, ancestor_id_expr)`