Merge branch 'master' into Azure_read_buffer

This commit is contained in:
Smita Kulkarni 2024-03-23 11:46:32 +01:00
commit a8238ded09
156 changed files with 2801 additions and 1200 deletions

View File

@ -43,30 +43,29 @@ At a minimum, the following information should be added (but add more as needed)
--- ---
### Modify your CI run: ### Modify your CI run:
##### NOTE: **NOTE:** If your merge the PR with modified CI you **MUST KNOW** what you are doing
- if your merge the PR with modified CI you **MUST** know what you are doing. **NOTE:** Set desired options before CI starts or re-push after updates
- modifiers can be applied only if set before CI starts
- remove `!` to apply #### Run only:
- return all `!` to restore defaults - [ ] <!---ci_set_integration--> Integration tests
``` - [ ] <!---ci_set_arm--> Integration tests (arm64)
!#ci_set_<SET_NAME> - to run only preconfigured set of tests, e.g.: - [ ] <!---ci_set_stateless--> Stateless tests (release)
!#ci_set_arm - to run only integration tests on ARM - [ ] <!---ci_set_stateless_asan--> Stateless tests (asan)
!#ci_set_integration - to run only integration tests on AMD - [ ] <!---ci_set_stateful--> Stateful tests (release)
!#ci_set_analyzer - to run only tests for analyzer - [ ] <!---ci_set_stateful_asan--> Stateful tests (asan)
NOTE: you can configure your own ci set - [ ] <!---ci_set_reduced--> No sanitizers
``` - [ ] <!---ci_set_analyzer--> Tests with analyzer
``` - [ ] <!---ci_set_fast--> Fast tests
!#job_<JOB NAME> - to run only specified job, e.g.: - [ ] <!---job_package_debug--> Only package_debug build
!#job_stateless_tests_release - [ ] <!---PLACE_YOUR_TAG_CONFIGURED_IN_ci_config.py_FILE_HERE--> Add your CI variant description here
!#job_package_debug
!#job_style_check #### CI options:
!#job_integration_tests_asan - [ ] <!---do_not_test--> do not test (only style check)
``` - [ ] <!---no_merge_commit--> disable merge-commit (no merge from master before tests)
``` - [ ] <!---no_ci_cache--> disable CI cache (job reuse)
!#batch_2 - to run only 2nd batch for all multi-batch jobs
!#btach_1_2_3 - to run only 1, 2, 3rd batch for all multi-batch jobs #### Only specified batches in multi-batch jobs:
``` - [ ] <!---batch_0--> 1
``` - [ ] <!---batch_1--> 2
!#no_merge_commit - to disable merge commit (no merge from master) - [ ] <!---batch_2--> 3
!#do_not_test - to disable whole CI (except style check) - [ ] <!---batch_3--> 4
```

View File

@ -281,15 +281,15 @@ void EnvironmentImpl::nodeIdImpl(NodeId& id)
/// #include <sys/ioctl.h> /// #include <sys/ioctl.h>
#if defined(sun) || defined(__sun) #if defined(sun) || defined(__sun)
#include <sys/sockio.h> #include <sys/sockio.h>
#include <netdb.h>
#include <net/if.h>
#include <net/if_arp.h>
#endif #endif
/// #include <sys/socket.h> /// #include <sys/socket.h>
/// #include <sys/types.h> /// #include <sys/types.h>
/// #include <netinet/in.h> /// #include <netinet/in.h>
/// #include <net/if.h> /// #include <net/if.h>
/// #include <arpa/inet.h> /// #include <arpa/inet.h>
/// #include <netdb.h>
/// #include <net/if.h>
/// #include <net/if_arp.h>
/// #include <unistd.h> /// #include <unistd.h>

View File

@ -31,7 +31,7 @@
namespace Poco { namespace Poco {
#if (POCO_OS == POCO_OS_LINUX) || (POCO_OS == POCO_OS_ANDROID) || (POCO_OS == POCO_OS_CYGWIN) || (POCO_OS == POCO_OS_FREE_BSD) #if (POCO_OS == POCO_OS_LINUX) || (POCO_OS == POCO_OS_ANDROID) || (POCO_OS == POCO_OS_CYGWIN) || (POCO_OS == POCO_OS_FREE_BSD) || (POCO_OS == POCO_OS_SOLARIS)
union semun union semun
{ {
int val; int val;

View File

@ -31,7 +31,7 @@
namespace Poco { namespace Poco {
#if (POCO_OS == POCO_OS_LINUX) || (POCO_OS == POCO_OS_ANDROID) || (POCO_OS == POCO_OS_CYGWIN) || (POCO_OS == POCO_OS_FREE_BSD) #if (POCO_OS == POCO_OS_LINUX) || (POCO_OS == POCO_OS_ANDROID) || (POCO_OS == POCO_OS_CYGWIN) || (POCO_OS == POCO_OS_FREE_BSD) || (POCO_OS == POCO_OS_SOLARIS)
union semun union semun
{ {
int val; int val;

View File

@ -9,6 +9,10 @@ elseif (OS_DARWIN OR OS_FREEBSD)
target_compile_definitions (_poco_net PUBLIC POCO_HAVE_FD_POLL) target_compile_definitions (_poco_net PUBLIC POCO_HAVE_FD_POLL)
endif () endif ()
if (OS_SUNOS)
target_link_libraries (_poco_net PUBLIC socket nsl)
endif ()
# TODO: remove these warning exclusions # TODO: remove these warning exclusions
target_compile_options (_poco_net target_compile_options (_poco_net
PRIVATE PRIVATE

View File

@ -86,6 +86,8 @@ elseif (OS_DARWIN)
target_compile_definitions(_c-ares PRIVATE -D_DARWIN_C_SOURCE) target_compile_definitions(_c-ares PRIVATE -D_DARWIN_C_SOURCE)
elseif (OS_FREEBSD) elseif (OS_FREEBSD)
target_include_directories(_c-ares SYSTEM PUBLIC "${ClickHouse_SOURCE_DIR}/contrib/c-ares-cmake/freebsd") target_include_directories(_c-ares SYSTEM PUBLIC "${ClickHouse_SOURCE_DIR}/contrib/c-ares-cmake/freebsd")
elseif (OS_SUNOS)
target_include_directories(_c-ares SYSTEM PUBLIC "${ClickHouse_SOURCE_DIR}/contrib/c-ares-cmake/solaris")
endif() endif()
add_library(ch_contrib::c-ares ALIAS _c-ares) add_library(ch_contrib::c-ares ALIAS _c-ares)

View File

@ -0,0 +1,104 @@
/* include/ares_build.h. Generated from ares_build.h.in by configure. */
#ifndef __CARES_BUILD_H
#define __CARES_BUILD_H
/* Copyright (C) 2009 - 2021 by Daniel Stenberg et al
*
* Permission to use, copy, modify, and distribute this software and its
* documentation for any purpose and without fee is hereby granted, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of M.I.T. not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. M.I.T. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*/
/* ================================================================ */
/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */
/* ================================================================ */
/*
* NOTE 1:
* -------
*
* Nothing in this file is intended to be modified or adjusted by the
* c-ares library user nor by the c-ares library builder.
*
* If you think that something actually needs to be changed, adjusted
* or fixed in this file, then, report it on the c-ares development
* mailing list: http://lists.haxx.se/listinfo/c-ares/
*
* This header file shall only export symbols which are 'cares' or 'CARES'
* prefixed, otherwise public name space would be polluted.
*
* NOTE 2:
* -------
*
* Right now you might be staring at file ares_build.h.in or ares_build.h,
* this is due to the following reason:
*
* On systems capable of running the configure script, the configure process
* will overwrite the distributed ares_build.h file with one that is suitable
* and specific to the library being configured and built, which is generated
* from the ares_build.h.in template file.
*
*/
/* ================================================================ */
/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */
/* ================================================================ */
#ifdef CARES_TYPEOF_ARES_SOCKLEN_T
# error "CARES_TYPEOF_ARES_SOCKLEN_T shall not be defined except in ares_build.h"
Error Compilation_aborted_CARES_TYPEOF_ARES_SOCKLEN_T_already_defined
#endif
#define CARES_HAVE_ARPA_NAMESER_H 1
#define CARES_HAVE_ARPA_NAMESER_COMPAT_H 1
/* ================================================================ */
/* EXTERNAL INTERFACE SETTINGS FOR CONFIGURE CAPABLE SYSTEMS ONLY */
/* ================================================================ */
/* Configure process defines this to 1 when it finds out that system */
/* header file ws2tcpip.h must be included by the external interface. */
/* #undef CARES_PULL_WS2TCPIP_H */
#ifdef CARES_PULL_WS2TCPIP_H
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# include <winsock2.h>
# include <ws2tcpip.h>
#endif
/* Configure process defines this to 1 when it finds out that system */
/* header file sys/types.h must be included by the external interface. */
#define CARES_PULL_SYS_TYPES_H 1
#ifdef CARES_PULL_SYS_TYPES_H
# include <sys/types.h>
#endif
/* Configure process defines this to 1 when it finds out that system */
/* header file sys/socket.h must be included by the external interface. */
#define CARES_PULL_SYS_SOCKET_H 1
#ifdef CARES_PULL_SYS_SOCKET_H
# include <sys/socket.h>
#endif
/* Integral data type used for ares_socklen_t. */
#define CARES_TYPEOF_ARES_SOCKLEN_T socklen_t
/* Data type definition of ares_socklen_t. */
typedef CARES_TYPEOF_ARES_SOCKLEN_T ares_socklen_t;
/* Integral data type used for ares_ssize_t. */
#define CARES_TYPEOF_ARES_SSIZE_T ssize_t
/* Data type definition of ares_ssize_t. */
typedef CARES_TYPEOF_ARES_SSIZE_T ares_ssize_t;
#endif /* __CARES_BUILD_H */

View File

@ -0,0 +1,503 @@
/* src/lib/ares_config.h. Generated from ares_config.h.in by configure. */
/* src/lib/ares_config.h.in. Generated from configure.ac by autoheader. */
/* Define if building universal (internal helper macro) */
/* #undef AC_APPLE_UNIVERSAL_BUILD */
/* define this if ares is built for a big endian system */
/* #undef ARES_BIG_ENDIAN */
/* Defined for build that exposes internal static functions for testing. */
/* #undef CARES_EXPOSE_STATICS */
/* a suitable file/device to read random data from */
#define CARES_RANDOM_FILE "/dev/urandom"
/* Defined for build with symbol hiding. */
#define CARES_SYMBOL_HIDING 1
/* Definition to make a library symbol externally visible. */
#define CARES_SYMBOL_SCOPE_EXTERN __attribute__ ((__visibility__ ("default")))
/* the signed version of size_t */
#define CARES_TYPEOF_ARES_SSIZE_T ssize_t
/* Use resolver library to configure cares */
/* #undef CARES_USE_LIBRESOLV */
/* if a /etc/inet dir is being used */
#define ETC_INET 1
/* Define to the type of arg 2 for gethostname. */
#define GETHOSTNAME_TYPE_ARG2 int
/* Define to the type qualifier of arg 1 for getnameinfo. */
#define GETNAMEINFO_QUAL_ARG1 const
/* Define to the type of arg 1 for getnameinfo. */
#define GETNAMEINFO_TYPE_ARG1 struct sockaddr *
/* Define to the type of arg 2 for getnameinfo. */
#define GETNAMEINFO_TYPE_ARG2 socklen_t
/* Define to the type of args 4 and 6 for getnameinfo. */
#define GETNAMEINFO_TYPE_ARG46 socklen_t
/* Define to the type of arg 7 for getnameinfo. */
#define GETNAMEINFO_TYPE_ARG7 int
/* Specifies the number of arguments to getservbyport_r */
#define GETSERVBYPORT_R_ARGS 5
/* Specifies the size of the buffer to pass to getservbyport_r */
#define GETSERVBYPORT_R_BUFSIZE 4096
/* Define to 1 if you have AF_INET6. */
#define HAVE_AF_INET6 1
/* Define to 1 if you have the <arpa/inet.h> header file. */
#define HAVE_ARPA_INET_H 1
/* Define to 1 if you have the <arpa/nameser_compat.h> header file. */
#define HAVE_ARPA_NAMESER_COMPAT_H 1
/* Define to 1 if you have the <arpa/nameser.h> header file. */
#define HAVE_ARPA_NAMESER_H 1
/* Define to 1 if you have the <assert.h> header file. */
#define HAVE_ASSERT_H 1
/* Define to 1 if you have the `bitncmp' function. */
/* #undef HAVE_BITNCMP */
/* Define to 1 if bool is an available type. */
#define HAVE_BOOL_T 1
/* Define to 1 if you have the clock_gettime function and monotonic timer. */
#define HAVE_CLOCK_GETTIME_MONOTONIC 1
/* Define to 1 if you have the closesocket function. */
/* #undef HAVE_CLOSESOCKET */
/* Define to 1 if you have the CloseSocket camel case function. */
/* #undef HAVE_CLOSESOCKET_CAMEL */
/* Define to 1 if you have the connect function. */
#define HAVE_CONNECT 1
/* define if the compiler supports basic C++11 syntax */
#define HAVE_CXX11 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the <errno.h> header file. */
#define HAVE_ERRNO_H 1
/* Define to 1 if you have the fcntl function. */
#define HAVE_FCNTL 1
/* Define to 1 if you have the <fcntl.h> header file. */
#define HAVE_FCNTL_H 1
/* Define to 1 if you have a working fcntl O_NONBLOCK function. */
#define HAVE_FCNTL_O_NONBLOCK 1
/* Define to 1 if you have the freeaddrinfo function. */
#define HAVE_FREEADDRINFO 1
/* Define to 1 if you have a working getaddrinfo function. */
#define HAVE_GETADDRINFO 1
/* Define to 1 if the getaddrinfo function is threadsafe. */
#define HAVE_GETADDRINFO_THREADSAFE 1
/* Define to 1 if you have the getenv function. */
#define HAVE_GETENV 1
/* Define to 1 if you have the gethostbyaddr function. */
#define HAVE_GETHOSTBYADDR 1
/* Define to 1 if you have the gethostbyname function. */
#define HAVE_GETHOSTBYNAME 1
/* Define to 1 if you have the gethostname function. */
#define HAVE_GETHOSTNAME 1
/* Define to 1 if you have the getnameinfo function. */
#define HAVE_GETNAMEINFO 1
/* Define to 1 if you have the getservbyport_r function. */
#define HAVE_GETSERVBYPORT_R 1
/* Define to 1 if you have the `gettimeofday' function. */
#define HAVE_GETTIMEOFDAY 1
/* Define to 1 if you have the `if_indextoname' function. */
#define HAVE_IF_INDEXTONAME 1
/* Define to 1 if you have a IPv6 capable working inet_net_pton function. */
/* #undef HAVE_INET_NET_PTON */
/* Define to 1 if you have a IPv6 capable working inet_ntop function. */
#define HAVE_INET_NTOP 1
/* Define to 1 if you have a IPv6 capable working inet_pton function. */
#define HAVE_INET_PTON 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the ioctl function. */
#define HAVE_IOCTL 1
/* Define to 1 if you have the ioctlsocket function. */
/* #undef HAVE_IOCTLSOCKET */
/* Define to 1 if you have the IoctlSocket camel case function. */
/* #undef HAVE_IOCTLSOCKET_CAMEL */
/* Define to 1 if you have a working IoctlSocket camel case FIONBIO function.
*/
/* #undef HAVE_IOCTLSOCKET_CAMEL_FIONBIO */
/* Define to 1 if you have a working ioctlsocket FIONBIO function. */
/* #undef HAVE_IOCTLSOCKET_FIONBIO */
/* Define to 1 if you have a working ioctl FIONBIO function. */
/* #undef HAVE_IOCTL_FIONBIO */
/* Define to 1 if you have a working ioctl SIOCGIFADDR function. */
/* #undef HAVE_IOCTL_SIOCGIFADDR */
/* Define to 1 if you have the `resolve' library (-lresolve). */
/* #undef HAVE_LIBRESOLVE */
/* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1
/* if your compiler supports LL */
#define HAVE_LL 1
/* Define to 1 if the compiler supports the 'long long' data type. */
#define HAVE_LONGLONG 1
/* Define to 1 if you have the malloc.h header file. */
#define HAVE_MALLOC_H 1
/* Define to 1 if you have the memory.h header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the MSG_NOSIGNAL flag. */
#define HAVE_MSG_NOSIGNAL 1
/* Define to 1 if you have the <netdb.h> header file. */
#define HAVE_NETDB_H 1
/* Define to 1 if you have the <netinet/in.h> header file. */
#define HAVE_NETINET_IN_H 1
/* Define to 1 if you have the <netinet/tcp.h> header file. */
#define HAVE_NETINET_TCP_H 1
/* Define to 1 if you have the <net/if.h> header file. */
#define HAVE_NET_IF_H 1
/* Define to 1 if you have PF_INET6. */
#define HAVE_PF_INET6 1
/* Define to 1 if you have the recv function. */
#define HAVE_RECV 1
/* Define to 1 if you have the recvfrom function. */
#define HAVE_RECVFROM 1
/* Define to 1 if you have the send function. */
#define HAVE_SEND 1
/* Define to 1 if you have the setsockopt function. */
#define HAVE_SETSOCKOPT 1
/* Define to 1 if you have a working setsockopt SO_NONBLOCK function. */
/* #undef HAVE_SETSOCKOPT_SO_NONBLOCK */
/* Define to 1 if you have the <signal.h> header file. */
#define HAVE_SIGNAL_H 1
/* Define to 1 if sig_atomic_t is an available typedef. */
#define HAVE_SIG_ATOMIC_T 1
/* Define to 1 if sig_atomic_t is already defined as volatile. */
/* #undef HAVE_SIG_ATOMIC_T_VOLATILE */
/* Define to 1 if your struct sockaddr_in6 has sin6_scope_id. */
#define HAVE_SOCKADDR_IN6_SIN6_SCOPE_ID 1
/* Define to 1 if you have the socket function. */
#define HAVE_SOCKET 1
/* Define to 1 if you have the <socket.h> header file. */
/* #undef HAVE_SOCKET_H */
/* Define to 1 if you have the <stdbool.h> header file. */
#define HAVE_STDBOOL_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdio.h> header file. */
#define HAVE_STDIO_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the strcasecmp function. */
#define HAVE_STRCASECMP 1
/* Define to 1 if you have the strcmpi function. */
/* #undef HAVE_STRCMPI */
/* Define to 1 if you have the strdup function. */
#define HAVE_STRDUP 1
/* Define to 1 if you have the stricmp function. */
/* #undef HAVE_STRICMP */
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the strncasecmp function. */
#define HAVE_STRNCASECMP 1
/* Define to 1 if you have the strncmpi function. */
/* #undef HAVE_STRNCMPI */
/* Define to 1 if you have the strnicmp function. */
/* #undef HAVE_STRNICMP */
/* Define to 1 if you have the <stropts.h> header file. */
#define HAVE_STROPTS_H 1
/* Define to 1 if you have struct addrinfo. */
#define HAVE_STRUCT_ADDRINFO 1
/* Define to 1 if you have struct in6_addr. */
#define HAVE_STRUCT_IN6_ADDR 1
/* Define to 1 if you have struct sockaddr_in6. */
#define HAVE_STRUCT_SOCKADDR_IN6 1
/* if struct sockaddr_storage is defined */
#define HAVE_STRUCT_SOCKADDR_STORAGE 1
/* Define to 1 if you have the timeval struct. */
#define HAVE_STRUCT_TIMEVAL 1
/* Define to 1 if you have the <sys/ioctl.h> header file. */
#define HAVE_SYS_IOCTL_H 1
/* Define to 1 if you have the <sys/param.h> header file. */
#define HAVE_SYS_PARAM_H 1
/* Define to 1 if you have the <sys/select.h> header file. */
#define HAVE_SYS_SELECT_H 1
/* Define to 1 if you have the <sys/socket.h> header file. */
#define HAVE_SYS_SOCKET_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <sys/uio.h> header file. */
#define HAVE_SYS_UIO_H 1
/* Define to 1 if you have the <time.h> header file. */
#define HAVE_TIME_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to 1 if you have the windows.h header file. */
/* #undef HAVE_WINDOWS_H */
/* Define to 1 if you have the winsock2.h header file. */
/* #undef HAVE_WINSOCK2_H */
/* Define to 1 if you have the winsock.h header file. */
/* #undef HAVE_WINSOCK_H */
/* Define to 1 if you have the writev function. */
#define HAVE_WRITEV 1
/* Define to 1 if you have the ws2tcpip.h header file. */
/* #undef HAVE_WS2TCPIP_H */
/* Define if __system_property_get exists. */
/* #undef HAVE___SYSTEM_PROPERTY_GET */
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#define LT_OBJDIR ".libs/"
/* Define to 1 if you need the malloc.h header file even with stdlib.h */
/* #undef NEED_MALLOC_H */
/* Define to 1 if you need the memory.h header file even with stdlib.h */
/* #undef NEED_MEMORY_H */
/* Define to 1 if _REENTRANT preprocessor symbol must be defined. */
#define NEED_REENTRANT 1
/* Define to 1 if _THREAD_SAFE preprocessor symbol must be defined. */
/* #undef NEED_THREAD_SAFE */
/* cpu-machine-OS */
#define OS "x86_64-pc-solaris2.11"
/* Name of package */
#define PACKAGE "c-ares"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT "c-ares mailing list: http://lists.haxx.se/listinfo/c-ares"
/* Define to the full name of this package. */
#define PACKAGE_NAME "c-ares"
/* Define to the full name and version of this package. */
#define PACKAGE_STRING "c-ares 1.18.1"
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME "c-ares"
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION "1.18.1"
/* Define to the type qualifier pointed by arg 5 for recvfrom. */
#define RECVFROM_QUAL_ARG5
/* Define to the type of arg 1 for recvfrom. */
#define RECVFROM_TYPE_ARG1 int
/* Define to the type pointed by arg 2 for recvfrom. */
#define RECVFROM_TYPE_ARG2 void
/* Define to 1 if the type pointed by arg 2 for recvfrom is void. */
#define RECVFROM_TYPE_ARG2_IS_VOID 1
/* Define to the type of arg 3 for recvfrom. */
#define RECVFROM_TYPE_ARG3 size_t
/* Define to the type of arg 4 for recvfrom. */
#define RECVFROM_TYPE_ARG4 int
/* Define to the type pointed by arg 5 for recvfrom. */
#define RECVFROM_TYPE_ARG5 struct sockaddr
/* Define to 1 if the type pointed by arg 5 for recvfrom is void. */
/* #undef RECVFROM_TYPE_ARG5_IS_VOID */
/* Define to the type pointed by arg 6 for recvfrom. */
#define RECVFROM_TYPE_ARG6 void
/* Define to 1 if the type pointed by arg 6 for recvfrom is void. */
#define RECVFROM_TYPE_ARG6_IS_VOID 1
/* Define to the function return type for recvfrom. */
#define RECVFROM_TYPE_RETV ssize_t
/* Define to the type of arg 1 for recv. */
#define RECV_TYPE_ARG1 int
/* Define to the type of arg 2 for recv. */
#define RECV_TYPE_ARG2 void *
/* Define to the type of arg 3 for recv. */
#define RECV_TYPE_ARG3 size_t
/* Define to the type of arg 4 for recv. */
#define RECV_TYPE_ARG4 int
/* Define to the function return type for recv. */
#define RECV_TYPE_RETV ssize_t
/* Define as the return type of signal handlers (`int' or `void'). */
#define RETSIGTYPE void
/* Define to the type qualifier of arg 2 for send. */
#define SEND_QUAL_ARG2 const
/* Define to the type of arg 1 for send. */
#define SEND_TYPE_ARG1 int
/* Define to the type of arg 2 for send. */
#define SEND_TYPE_ARG2 void *
/* Define to the type of arg 3 for send. */
#define SEND_TYPE_ARG3 size_t
/* Define to the type of arg 4 for send. */
#define SEND_TYPE_ARG4 int
/* Define to the function return type for send. */
#define SEND_TYPE_RETV ssize_t
/* Define to 1 if all of the C90 standard headers exist (not just the ones
required in a freestanding environment). This macro is provided for
backward compatibility; new code need not use it. */
#define STDC_HEADERS 1
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. This
macro is obsolete. */
#define TIME_WITH_SYS_TIME 1
/* Define to disable non-blocking sockets. */
/* #undef USE_BLOCKING_SOCKETS */
/* Version number of package */
#define VERSION "1.18.1"
/* Define to avoid automatic inclusion of winsock.h */
/* #undef WIN32_LEAN_AND_MEAN */
/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
significant byte first (like Motorola and SPARC, unlike Intel). */
#if defined AC_APPLE_UNIVERSAL_BUILD
# if defined __BIG_ENDIAN__
# define WORDS_BIGENDIAN 1
# endif
#else
# ifndef WORDS_BIGENDIAN
/* # undef WORDS_BIGENDIAN */
# endif
#endif
/* Define to 1 if OS is AIX. */
#ifndef _ALL_SOURCE
/* # undef _ALL_SOURCE */
#endif
/* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */
/* Define for large files, on AIX-style hosts. */
/* #undef _LARGE_FILES */
/* Define to empty if `const' does not conform to ANSI C. */
/* #undef const */
/* Type to use in place of in_addr_t when system does not provide it. */
/* #undef in_addr_t */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef size_t */

2
contrib/cppkafka vendored

@ -1 +1 @@
Subproject commit 5a119f689f8a4d90d10a9635e7ee2bee5c127de1 Subproject commit 9c5ea0e332486961e612deacc6e3f0c1874c688d

View File

@ -51,3 +51,8 @@
#define USE_OPENSSL #define USE_OPENSSL
#define USE_THREADS_POSIX #define USE_THREADS_POSIX
#define USE_ARES #define USE_ARES
#ifdef __illumos__
#define HAVE_POSIX_STRERROR_R 1
#define HAVE_STRERROR_R 1
#endif

View File

@ -4,7 +4,11 @@ sidebar_position: 50
sidebar_label: MySQL sidebar_label: MySQL
--- ---
# MySQL import CloudNotSupportedBadge from '@theme/badges/CloudNotSupportedBadge';
# MySQL Database Engine
<CloudNotSupportedBadge />
Allows to connect to databases on a remote MySQL server and perform `INSERT` and `SELECT` queries to exchange data between ClickHouse and MySQL. Allows to connect to databases on a remote MySQL server and perform `INSERT` and `SELECT` queries to exchange data between ClickHouse and MySQL.

View File

@ -4,7 +4,11 @@ sidebar_position: 138
sidebar_label: MySQL sidebar_label: MySQL
--- ---
# MySQL import CloudAvailableBadge from '@theme/badges/CloudAvailableBadge';
# MySQL Table Engine
<CloudAvailableBadge />
The MySQL engine allows you to perform `SELECT` and `INSERT` queries on data that is stored on a remote MySQL server. The MySQL engine allows you to perform `SELECT` and `INSERT` queries on data that is stored on a remote MySQL server.

View File

@ -8,6 +8,10 @@ sidebar_label: PostgreSQL
The PostgreSQL engine allows to perform `SELECT` and `INSERT` queries on data that is stored on a remote PostgreSQL server. The PostgreSQL engine allows to perform `SELECT` and `INSERT` queries on data that is stored on a remote PostgreSQL server.
:::note
Currently, only PostgreSQL versions 12 and up are supported.
:::
## Creating a Table {#creating-a-table} ## Creating a Table {#creating-a-table}
``` sql ``` sql

View File

@ -75,14 +75,14 @@ This is the output of `DESCRIBE`. Down further in this guide the field type cho
</TabItem> </TabItem>
<TabItem value="selfmanaged" label="Self-managed"> <TabItem value="selfmanaged" label="Self-managed">
1. Download the snapshot of the dataset from February 2021: [cell_towers.csv.xz](https://datasets.clickhouse.com/cell_towers.csv.xz) (729 MB). 1. Download the snapshot of the dataset from February 2021: [cell_towers.csv.xz](https://datasets.clickhouse.com/cell_towers.csv.xz) (686 MB).
2. Validate the integrity (optional step): 2. Validate the integrity (optional step):
```bash ```bash
md5sum cell_towers.csv.xz md5sum cell_towers.csv.xz
``` ```
```response ```response
8cf986f4a0d9f12c6f384a0e9192c908 cell_towers.csv.xz 8a797f7bdb55faba93f6cbc37d47b037 cell_towers.csv.xz
``` ```
3. Decompress it with the following command: 3. Decompress it with the following command:
@ -132,7 +132,7 @@ SELECT radio, count() AS c FROM cell_towers GROUP BY radio ORDER BY c DESC
┌─radio─┬────────c─┐ ┌─radio─┬────────c─┐
│ UMTS │ 20686487 │ │ UMTS │ 20686487 │
│ LTE │ 12101148 │ │ LTE │ 12101148 │
│ GSM │ 9931312 │ GSM │ 9931304
│ CDMA │ 556344 │ │ CDMA │ 556344 │
│ NR │ 867 │ │ NR │ 867 │
└───────┴──────────┘ └───────┴──────────┘

View File

@ -262,7 +262,7 @@ The required version can be downloaded with `curl` or `wget` from repository htt
After that downloaded archives should be unpacked and installed with installation scripts. Example for the latest stable version: After that downloaded archives should be unpacked and installed with installation scripts. Example for the latest stable version:
``` bash ``` bash
LATEST_VERSION=$(curl -s https://packages.clickhouse.com/tgz/stable/ | \ LATEST_VERSION=$(curl -s https://raw.githubusercontent.com/ClickHouse/ClickHouse/master/utils/list-versions/version_date.tsv | \
grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | sort -V -r | head -n 1) grep -Eo '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | sort -V -r | head -n 1)
export LATEST_VERSION export LATEST_VERSION

View File

@ -379,6 +379,18 @@ Type: UInt64
Default: 0 Default: 0
## max_waiting_queries
Limit on total number of concurrently waiting queries. Execution of a waiting query is blocked while required tables are loading asynchronously (see `async_load_databases`). Note that waiting queries are not counted when `max_concurrent_queries`, `max_concurrent_insert_queries`, `max_concurrent_select_queries`, `max_concurrent_queries_for_user` and `max_concurrent_queries_for_all_users` limits are checked. This correction is done to avoid hitting these limits just after server startup. Zero means unlimited.
:::note
This setting can be modified at runtime and will take effect immediately. Queries that are already running will remain unchanged.
:::
Type: UInt64
Default: 0
## max_connections ## max_connections
Max server connections. Max server connections.
@ -1725,7 +1737,7 @@ Default value: `0.5`.
Asynchronous loading of databases and tables. Asynchronous loading of databases and tables.
If `true` all non-system databases with `Ordinary`, `Atomic` and `Replicated` engine will be loaded asynchronously after the ClickHouse server start up. See `system.asynchronous_loader` table, `tables_loader_background_pool_size` and `tables_loader_foreground_pool_size` server settings. Any query that tries to access a table, that is not yet loaded, will wait for exactly this table to be started up. If load job fails, query will rethrow an error (instead of shutting down the whole server in case of `async_load_databases = false`). The table that is waited for by at least one query will be loaded with higher priority. DDL queries on a database will wait for exactly that database to be started up. If `true` all non-system databases with `Ordinary`, `Atomic` and `Replicated` engine will be loaded asynchronously after the ClickHouse server start up. See `system.asynchronous_loader` table, `tables_loader_background_pool_size` and `tables_loader_foreground_pool_size` server settings. Any query that tries to access a table, that is not yet loaded, will wait for exactly this table to be started up. If load job fails, query will rethrow an error (instead of shutting down the whole server in case of `async_load_databases = false`). The table that is waited for by at least one query will be loaded with higher priority. DDL queries on a database will wait for exactly that database to be started up. Also consider setting a limit `max_waiting_queries` for the total number of waiting queries.
If `false`, all databases are loaded when the server starts. If `false`, all databases are loaded when the server starts.
@ -2926,7 +2938,7 @@ Default: 0
## ignore_empty_sql_security_in_create_view_query {#ignore_empty_sql_security_in_create_view_query} ## ignore_empty_sql_security_in_create_view_query {#ignore_empty_sql_security_in_create_view_query}
If true, ClickHouse doesn't write defaults for empty SQL security statement in CREATE VIEW queries. If true, ClickHouse doesn't write defaults for empty SQL security statement in CREATE VIEW queries.
:::note :::note
This setting is only necessary for the migration period and will become obsolete in 24.4 This setting is only necessary for the migration period and will become obsolete in 24.4

View File

@ -21,6 +21,35 @@ The queries to terminate are selected from the system.processes table using the
Examples: Examples:
First, you'll need to get the list of incomplete queries. This SQL query provides them according to those running the longest:
List from a single ClickHouse node:
``` sql
SELECT
initial_query_id,
query_id,
formatReadableTimeDelta(elapsed) AS time_delta,
query,
*
FROM system.processes
WHERE query ILIKE 'SELECT%'
ORDER BY time_delta DESC;
```
List from a ClickHouse cluster:
``` sql
SELECT
initial_query_id,
query_id,
formatReadableTimeDelta(elapsed) AS time_delta,
query,
*
FROM clusterAllReplicas(default, system.processes)
WHERE query ILIKE 'SELECT%'
ORDER BY time_delta DESC;
```
Kill the query:
``` sql ``` sql
-- Forcibly terminates all queries with the specified query_id: -- Forcibly terminates all queries with the specified query_id:
KILL QUERY WHERE query_id='2-857d-4a57-9ee0-327da5d60a90' KILL QUERY WHERE query_id='2-857d-4a57-9ee0-327da5d60a90'
@ -44,6 +73,11 @@ A test query (`TEST`) only checks the users rights and displays a list of que
## KILL MUTATION ## KILL MUTATION
The presence of long-running or incomplete mutations often indicates that a ClickHouse service is running poorly. The asynchronous nature of mutations can cause them to consume all available resources on a system. You may need to either:
- Pause all new mutations, `INSERT`s , and `SELECT`s and allow the queue of mutations to complete.
- Or manually kill some of these mutations by sending a `KILL` command.
``` sql ``` sql
KILL MUTATION [ON CLUSTER cluster] KILL MUTATION [ON CLUSTER cluster]
WHERE <where expression to SELECT FROM system.mutations query> WHERE <where expression to SELECT FROM system.mutations query>
@ -57,6 +91,39 @@ A test query (`TEST`) only checks the users rights and displays a list of mut
Examples: Examples:
Get a `count()` of the number of incomplete mutations:
Count of mutations from a single ClickHouse node:
``` sql
SELECT count(*)
FROM system.mutations
WHERE is_done = 0;
```
Count of mutations from a ClickHouse cluster of replicas:
``` sql
SELECT count(*)
FROM clusterAllReplicas('default', system.mutations)
WHERE is_done = 0;
```
Query the list of incomplete mutations:
List of mutations from a single ClickHouse node:
``` sql
SELECT mutation_id, *
FROM system.mutations
WHERE is_done = 0;
```
List of mutations from a ClickHouse cluster:
``` sql
SELECT mutation_id, *
FROM clusterAllReplicas('default', system.mutations)
WHERE is_done = 0;
```
Kill the mutations as needed:
``` sql ``` sql
-- Cancel and remove all mutations of the single table: -- Cancel and remove all mutations of the single table:
KILL MUTATION WHERE database = 'default' AND table = 'table' KILL MUTATION WHERE database = 'default' AND table = 'table'

View File

@ -1,4 +1,3 @@
#include <boost/algorithm/string/join.hpp>
#include <cstdlib> #include <cstdlib>
#include <fcntl.h> #include <fcntl.h>
#include <map> #include <map>
@ -7,7 +6,6 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <Common/ThreadStatus.h> #include <Common/ThreadStatus.h>
#include <Common/scope_guard_safe.h>
#include <boost/program_options.hpp> #include <boost/program_options.hpp>
#include <boost/algorithm/string/replace.hpp> #include <boost/algorithm/string/replace.hpp>
#include <filesystem> #include <filesystem>
@ -45,8 +43,6 @@
#include <Processors/Transforms/getSourceFromASTInsertQuery.h> #include <Processors/Transforms/getSourceFromASTInsertQuery.h>
#include <Interpreters/InterpreterSetQuery.h>
#include <Functions/registerFunctions.h> #include <Functions/registerFunctions.h>
#include <AggregateFunctions/registerAggregateFunctions.h> #include <AggregateFunctions/registerAggregateFunctions.h>
#include <Formats/registerFormats.h> #include <Formats/registerFormats.h>
@ -1180,7 +1176,7 @@ void Client::processConfig()
pager = config().getString("pager", ""); pager = config().getString("pager", "");
is_default_format = !config().has("vertical") && !config().has("format"); is_default_format = !config().has("vertical") && !config().has("output-format") && !config().has("format");
if (is_default_format && checkIfStdoutIsRegularFile()) if (is_default_format && checkIfStdoutIsRegularFile())
{ {
is_default_format = false; is_default_format = false;
@ -1189,9 +1185,13 @@ void Client::processConfig()
format = format_from_file_name ? *format_from_file_name : "TabSeparated"; format = format_from_file_name ? *format_from_file_name : "TabSeparated";
} }
else if (config().has("vertical")) else if (config().has("vertical"))
format = config().getString("format", "Vertical"); {
format = config().getString("output-format", config().getString("format", "Vertical"));
}
else else
format = config().getString("format", is_interactive ? "PrettyCompact" : "TabSeparated"); {
format = config().getString("output-format", config().getString("format", is_interactive ? "PrettyCompact" : "TabSeparated"));
}
format_max_block_size = config().getUInt64("format_max_block_size", format_max_block_size = config().getUInt64("format_max_block_size",
global_context->getSettingsRef().max_block_size); global_context->getSettingsRef().max_block_size);

View File

@ -819,7 +819,6 @@ void LocalServer::addOptions(OptionsDescription & options_description)
("file,F", po::value<std::string>(), "path to file with data of the initial table (stdin if not specified)") ("file,F", po::value<std::string>(), "path to file with data of the initial table (stdin if not specified)")
("input-format", po::value<std::string>(), "input format of the initial table data") ("input-format", po::value<std::string>(), "input format of the initial table data")
("output-format", po::value<std::string>(), "default output format")
("logger.console", po::value<bool>()->implicit_value(true), "Log to console") ("logger.console", po::value<bool>()->implicit_value(true), "Log to console")
("logger.log", po::value<std::string>(), "Log file name") ("logger.log", po::value<std::string>(), "Log file name")

View File

@ -1440,6 +1440,7 @@ try
global_context->getProcessList().setMaxSize(new_server_settings.max_concurrent_queries); global_context->getProcessList().setMaxSize(new_server_settings.max_concurrent_queries);
global_context->getProcessList().setMaxInsertQueriesAmount(new_server_settings.max_concurrent_insert_queries); global_context->getProcessList().setMaxInsertQueriesAmount(new_server_settings.max_concurrent_insert_queries);
global_context->getProcessList().setMaxSelectQueriesAmount(new_server_settings.max_concurrent_select_queries); global_context->getProcessList().setMaxSelectQueriesAmount(new_server_settings.max_concurrent_select_queries);
global_context->getProcessList().setMaxWaitingQueriesAmount(new_server_settings.max_waiting_queries);
if (config->has("keeper_server")) if (config->has("keeper_server"))
global_context->updateKeeperConfiguration(*config); global_context->updateKeeperConfiguration(*config);

View File

@ -408,6 +408,65 @@ public:
friend bool operator!=(const Node & left, const Node & right) { return !(left == right); } friend bool operator!=(const Node & left, const Node & right) { return !(left == right); }
bool contains(const Node & other)
{
if (min_flags_with_children.contains(other.max_flags_with_children))
return true;
if (!flags.contains(other.flags))
return false;
/// Let's assume that the current node has the following rights:
///
/// SELECT ON *.* TO user1;
/// REVOKE SELECT ON system.* FROM user1;
/// REVOKE SELECT ON mydb.* FROM user1;
///
/// And the other node has the rights:
///
/// SELECT ON *.* TO user2;
/// REVOKE SELECT ON system.* FROM user2;
///
/// First, we check that each child from the other node is present in the current node:
///
/// SELECT ON *.* TO user1; -- checked
/// REVOKE SELECT ON system.* FROM user1; -- checked
if (other.children)
{
for (const auto & [name, node] : *other.children)
{
const auto & child = tryGetChild(name);
if (child == nullptr)
{
if (!flags.contains(node.flags))
return false;
}
else
{
if (!child->contains(node))
return false;
}
}
}
if (!children)
return true;
/// Then we check that each of our children has no other rights revoked.
///
/// REVOKE SELECT ON mydb.* FROM user1; -- check failed, returning false
for (const auto & [name, node] : *children)
{
if (other.children && other.children->contains(name))
continue;
if (!node.flags.contains(other.flags))
return false;
}
return true;
}
void makeUnion(const Node & other) void makeUnion(const Node & other)
{ {
makeUnionRec(other); makeUnionRec(other);
@ -1004,6 +1063,24 @@ bool AccessRights::isGrantedImpl(const AccessFlags & flags, const Args &... args
return helper(root); return helper(root);
} }
template <bool grant_option>
bool AccessRights::containsImpl(const AccessRights & other) const
{
auto helper = [&](const std::unique_ptr<Node> & root_node) -> bool
{
if (!root_node)
return !other.root;
if (!other.root)
return true;
return root_node->contains(*other.root);
};
if constexpr (grant_option)
return helper(root_with_grant_option);
else
return helper(root);
}
template <bool grant_option> template <bool grant_option>
bool AccessRights::isGrantedImplHelper(const AccessRightsElement & element) const bool AccessRights::isGrantedImplHelper(const AccessRightsElement & element) const
{ {
@ -1068,6 +1145,8 @@ bool AccessRights::hasGrantOption(const AccessFlags & flags, std::string_view da
bool AccessRights::hasGrantOption(const AccessRightsElement & element) const { return isGrantedImpl<true>(element); } bool AccessRights::hasGrantOption(const AccessRightsElement & element) const { return isGrantedImpl<true>(element); }
bool AccessRights::hasGrantOption(const AccessRightsElements & elements) const { return isGrantedImpl<true>(elements); } bool AccessRights::hasGrantOption(const AccessRightsElements & elements) const { return isGrantedImpl<true>(elements); }
bool AccessRights::contains(const AccessRights & access_rights) const { return containsImpl<false>(access_rights); }
bool AccessRights::containsWithGrantOption(const AccessRights & access_rights) const { return containsImpl<true>(access_rights); }
bool operator ==(const AccessRights & left, const AccessRights & right) bool operator ==(const AccessRights & left, const AccessRights & right)
{ {

View File

@ -95,6 +95,10 @@ public:
bool hasGrantOption(const AccessRightsElement & element) const; bool hasGrantOption(const AccessRightsElement & element) const;
bool hasGrantOption(const AccessRightsElements & elements) const; bool hasGrantOption(const AccessRightsElements & elements) const;
/// Checks if a given `access_rights` is a subset for the current access rights.
bool contains(const AccessRights & access_rights) const;
bool containsWithGrantOption(const AccessRights & access_rights) const;
/// Merges two sets of access rights together. /// Merges two sets of access rights together.
/// It's used to combine access rights from multiple roles. /// It's used to combine access rights from multiple roles.
void makeUnion(const AccessRights & other); void makeUnion(const AccessRights & other);
@ -153,6 +157,9 @@ private:
template <bool grant_option> template <bool grant_option>
bool isGrantedImpl(const AccessRightsElements & elements) const; bool isGrantedImpl(const AccessRightsElements & elements) const;
template <bool grant_option>
bool containsImpl(const AccessRights & other) const;
template <bool grant_option> template <bool grant_option>
bool isGrantedImplHelper(const AccessRightsElement & element) const; bool isGrantedImplHelper(const AccessRightsElement & element) const;

View File

@ -2892,7 +2892,9 @@ void ClientBase::init(int argc, char ** argv)
("suggestion_limit", po::value<int>()->default_value(10000), "Suggestion limit for how many databases, tables and columns to fetch.") ("suggestion_limit", po::value<int>()->default_value(10000), "Suggestion limit for how many databases, tables and columns to fetch.")
("format,f", po::value<std::string>(), "default output format") ("format,f", po::value<std::string>(), "default output format (and input format for clickhouse-local)")
("output-format", po::value<std::string>(), "default output format (this option has preference over --format)")
("vertical,E", "vertical output format, same as --format=Vertical or FORMAT Vertical or \\G at end of command") ("vertical,E", "vertical output format, same as --format=Vertical or FORMAT Vertical or \\G at end of command")
("highlight", po::value<bool>()->default_value(true), "enable or disable basic syntax highlight in interactive command line") ("highlight", po::value<bool>()->default_value(true), "enable or disable basic syntax highlight in interactive command line")
@ -2975,6 +2977,8 @@ void ClientBase::init(int argc, char ** argv)
config().setBool("ignore-error", true); config().setBool("ignore-error", true);
if (options.count("format")) if (options.count("format"))
config().setString("format", options["format"].as<std::string>()); config().setString("format", options["format"].as<std::string>());
if (options.count("output-format"))
config().setString("output-format", options["output-format"].as<std::string>());
if (options.count("vertical")) if (options.count("vertical"))
config().setBool("vertical", true); config().setBool("vertical", true);
if (options.count("stacktrace")) if (options.count("stacktrace"))

View File

@ -140,6 +140,11 @@ void LoadJob::finish()
finish_time = std::chrono::system_clock::now(); finish_time = std::chrono::system_clock::now();
if (waiters > 0) if (waiters > 0)
finished.notify_all(); finished.notify_all();
else
{
on_waiters_increment = {};
on_waiters_decrement = {};
}
} }
void LoadJob::scheduled(UInt64 job_id_) void LoadJob::scheduled(UInt64 job_id_)
@ -765,11 +770,25 @@ void AsyncLoader::wait(std::unique_lock<std::mutex> & job_lock, const LoadJobPtr
if (job->load_status != LoadStatus::PENDING) // Shortcut just to avoid incrementing ProfileEvents if (job->load_status != LoadStatus::PENDING) // Shortcut just to avoid incrementing ProfileEvents
return; return;
if (job->on_waiters_increment)
job->on_waiters_increment(job);
// WARNING: it is important not to throw below this point to avoid `on_waiters_increment` call w/o matching `on_waiters_decrement` call
Stopwatch watch; Stopwatch watch;
job->waiters++; job->waiters++;
job->finished.wait(job_lock, [&] { return job->load_status != LoadStatus::PENDING; }); job->finished.wait(job_lock, [&] { return job->load_status != LoadStatus::PENDING; });
job->waiters--; job->waiters--;
ProfileEvents::increment(ProfileEvents::AsyncLoaderWaitMicroseconds, watch.elapsedMicroseconds()); ProfileEvents::increment(ProfileEvents::AsyncLoaderWaitMicroseconds, watch.elapsedMicroseconds());
if (job->on_waiters_decrement)
job->on_waiters_decrement(job);
if (job->waiters == 0)
{
job->on_waiters_increment = {};
job->on_waiters_decrement = {};
}
} }
bool AsyncLoader::canSpawnWorker(Pool & pool, std::unique_lock<std::mutex> &) bool AsyncLoader::canSpawnWorker(Pool & pool, std::unique_lock<std::mutex> &)
@ -859,7 +878,7 @@ void AsyncLoader::worker(Pool & pool)
try try
{ {
current_load_job = job.get(); current_load_job = job.get();
SCOPE_EXIT({ current_load_job = nullptr; }); // Note that recursive job execution is not supported SCOPE_EXIT({ current_load_job = nullptr; }); // Note that recursive job execution is not supported, but jobs can wait one another
job->execute(*this, pool_id, job); job->execute(*this, pool_id, job);
exception_from_job = {}; exception_from_job = {};
} }

View File

@ -59,7 +59,8 @@ enum class LoadStatus
class LoadJob : private boost::noncopyable class LoadJob : private boost::noncopyable
{ {
public: public:
template <class LoadJobSetType, class Func, class DFFunc> // NOTE: makeLoadJob() helper should be used instead of direct ctor call
template <class LoadJobSetType, class DFFunc, class Func>
LoadJob(LoadJobSetType && dependencies_, String name_, size_t pool_id_, DFFunc && dependency_failure_, Func && func_) LoadJob(LoadJobSetType && dependencies_, String name_, size_t pool_id_, DFFunc && dependency_failure_, Func && func_)
: dependencies(std::forward<LoadJobSetType>(dependencies_)) : dependencies(std::forward<LoadJobSetType>(dependencies_))
, name(std::move(name_)) , name(std::move(name_))
@ -69,6 +70,19 @@ public:
, func(std::forward<Func>(func_)) , func(std::forward<Func>(func_))
{} {}
// NOTE: makeLoadJob() helper should be used instead of direct ctor call
template <class LoadJobSetType, class WIFunc, class WDFunc, class DFFunc, class Func>
LoadJob(LoadJobSetType && dependencies_, String name_, size_t pool_id_, WIFunc && on_waiters_increment_, WDFunc && on_waiters_decrement_, DFFunc && dependency_failure_, Func && func_)
: dependencies(std::forward<LoadJobSetType>(dependencies_))
, name(std::move(name_))
, execution_pool_id(pool_id_)
, pool_id(pool_id_)
, on_waiters_increment(std::forward<WIFunc>(on_waiters_increment_))
, on_waiters_decrement(std::forward<WDFunc>(on_waiters_decrement_))
, dependency_failure(std::forward<DFFunc>(dependency_failure_))
, func(std::forward<Func>(func_))
{}
// Current job status. // Current job status.
LoadStatus status() const; LoadStatus status() const;
std::exception_ptr exception() const; std::exception_ptr exception() const;
@ -112,6 +126,13 @@ private:
std::atomic<size_t> execution_pool_id; std::atomic<size_t> execution_pool_id;
std::atomic<size_t> pool_id; std::atomic<size_t> pool_id;
// Handlers that is called by every new waiting thread, just before going to sleep.
// If `on_waiters_increment` throws, then wait is canceled, and corresponding `on_waiters_decrement` will never be called.
// It can be used for counting and limits on number of waiters.
// Note that implementations are called under `LoadJob::mutex` and should be fast.
std::function<void(const LoadJobPtr & self)> on_waiters_increment;
std::function<void(const LoadJobPtr & self)> on_waiters_decrement;
// Handler for failed or canceled dependencies. // Handler for failed or canceled dependencies.
// If job needs to be canceled on `dependency` failure, then function should set `cancel` to a specific reason. // If job needs to be canceled on `dependency` failure, then function should set `cancel` to a specific reason.
// Note that implementation should be fast and cannot use AsyncLoader, because it is called under `AsyncLoader::mutex`. // Note that implementation should be fast and cannot use AsyncLoader, because it is called under `AsyncLoader::mutex`.
@ -140,8 +161,50 @@ void cancelOnDependencyFailure(const LoadJobPtr & self, const LoadJobPtr & depen
void ignoreDependencyFailure(const LoadJobPtr & self, const LoadJobPtr & dependency, std::exception_ptr & cancel); void ignoreDependencyFailure(const LoadJobPtr & self, const LoadJobPtr & dependency, std::exception_ptr & cancel);
template <class F> concept LoadJobDependencyFailure = std::invocable<F, const LoadJobPtr &, const LoadJobPtr &, std::exception_ptr &>; template <class F> concept LoadJobDependencyFailure = std::invocable<F, const LoadJobPtr &, const LoadJobPtr &, std::exception_ptr &>;
template <class F> concept LoadJobOnWaiters = std::invocable<F, const LoadJobPtr &>;
template <class F> concept LoadJobFunc = std::invocable<F, AsyncLoader &, const LoadJobPtr &>; template <class F> concept LoadJobFunc = std::invocable<F, AsyncLoader &, const LoadJobPtr &>;
LoadJobPtr makeLoadJob(LoadJobSet && dependencies, String name, LoadJobOnWaiters auto && on_waiters_increment, LoadJobOnWaiters auto && on_waiters_decrement, LoadJobDependencyFailure auto && dependency_failure, LoadJobFunc auto && func)
{
return std::make_shared<LoadJob>(std::move(dependencies), std::move(name), 0, on_waiters_increment, on_waiters_decrement, std::forward<decltype(dependency_failure)>(dependency_failure), std::forward<decltype(func)>(func));
}
LoadJobPtr makeLoadJob(const LoadJobSet & dependencies, String name, LoadJobOnWaiters auto && on_waiters_increment, LoadJobOnWaiters auto && on_waiters_decrement, LoadJobDependencyFailure auto && dependency_failure, LoadJobFunc auto && func)
{
return std::make_shared<LoadJob>(dependencies, std::move(name), 0, on_waiters_increment, on_waiters_decrement, std::forward<decltype(dependency_failure)>(dependency_failure), std::forward<decltype(func)>(func));
}
LoadJobPtr makeLoadJob(LoadJobSet && dependencies, size_t pool_id, String name, LoadJobOnWaiters auto && on_waiters_increment, LoadJobOnWaiters auto && on_waiters_decrement, LoadJobDependencyFailure auto && dependency_failure, LoadJobFunc auto && func)
{
return std::make_shared<LoadJob>(std::move(dependencies), std::move(name), pool_id, on_waiters_increment, on_waiters_decrement, std::forward<decltype(dependency_failure)>(dependency_failure), std::forward<decltype(func)>(func));
}
LoadJobPtr makeLoadJob(const LoadJobSet & dependencies, size_t pool_id, String name, LoadJobOnWaiters auto && on_waiters_increment, LoadJobOnWaiters auto && on_waiters_decrement, LoadJobDependencyFailure auto && dependency_failure, LoadJobFunc auto && func)
{
return std::make_shared<LoadJob>(dependencies, std::move(name), pool_id, on_waiters_increment, on_waiters_decrement, std::forward<decltype(dependency_failure)>(dependency_failure), std::forward<decltype(func)>(func));
}
LoadJobPtr makeLoadJob(LoadJobSet && dependencies, String name, LoadJobOnWaiters auto && on_waiters_increment, LoadJobOnWaiters auto && on_waiters_decrement, LoadJobFunc auto && func)
{
return std::make_shared<LoadJob>(std::move(dependencies), std::move(name), 0, on_waiters_increment, on_waiters_decrement, cancelOnDependencyFailure, std::forward<decltype(func)>(func));
}
LoadJobPtr makeLoadJob(const LoadJobSet & dependencies, String name, LoadJobOnWaiters auto && on_waiters_increment, LoadJobOnWaiters auto && on_waiters_decrement, LoadJobFunc auto && func)
{
return std::make_shared<LoadJob>(dependencies, std::move(name), 0, on_waiters_increment, on_waiters_decrement, cancelOnDependencyFailure, std::forward<decltype(func)>(func));
}
LoadJobPtr makeLoadJob(LoadJobSet && dependencies, size_t pool_id, String name, LoadJobOnWaiters auto && on_waiters_increment, LoadJobOnWaiters auto && on_waiters_decrement, LoadJobFunc auto && func)
{
return std::make_shared<LoadJob>(std::move(dependencies), std::move(name), pool_id, on_waiters_increment, on_waiters_decrement, cancelOnDependencyFailure, std::forward<decltype(func)>(func));
}
LoadJobPtr makeLoadJob(const LoadJobSet & dependencies, size_t pool_id, String name, LoadJobOnWaiters auto && on_waiters_increment, LoadJobOnWaiters auto && on_waiters_decrement, LoadJobFunc auto && func)
{
return std::make_shared<LoadJob>(dependencies, std::move(name), pool_id, on_waiters_increment, on_waiters_decrement, cancelOnDependencyFailure, std::forward<decltype(func)>(func));
}
LoadJobPtr makeLoadJob(LoadJobSet && dependencies, String name, LoadJobDependencyFailure auto && dependency_failure, LoadJobFunc auto && func) LoadJobPtr makeLoadJob(LoadJobSet && dependencies, String name, LoadJobDependencyFailure auto && dependency_failure, LoadJobFunc auto && func)
{ {
return std::make_shared<LoadJob>(std::move(dependencies), std::move(name), 0, std::forward<decltype(dependency_failure)>(dependency_failure), std::forward<decltype(func)>(func)); return std::make_shared<LoadJob>(std::move(dependencies), std::move(name), 0, std::forward<decltype(dependency_failure)>(dependency_failure), std::forward<decltype(func)>(func));

View File

@ -643,6 +643,72 @@ TEST(AsyncLoader, CustomDependencyFailure)
ASSERT_EQ(good_count.load(), 3); ASSERT_EQ(good_count.load(), 3);
} }
TEST(AsyncLoader, WaitersLimit)
{
AsyncLoaderTest t(16);
std::atomic<int> waiters_total{0};
int waiters_limit = 5;
auto waiters_inc = [&] (const LoadJobPtr &) {
int value = waiters_total.load();
while (true)
{
if (value >= waiters_limit)
throw Exception(ErrorCodes::ASYNC_LOAD_FAILED, "Too many waiters: {}", value);
if (waiters_total.compare_exchange_strong(value, value + 1))
break;
}
};
auto waiters_dec = [&] (const LoadJobPtr &) {
waiters_total.fetch_sub(1);
};
std::barrier sync(2);
t.loader.start();
auto job_func = [&] (AsyncLoader &, const LoadJobPtr &) {
sync.arrive_and_wait(); // (A)
};
auto job = makeLoadJob({}, "job", waiters_inc, waiters_dec, job_func);
auto task = t.schedule({job});
std::atomic<int> failure{0};
std::atomic<int> success{0};
std::vector<std::thread> waiters;
waiters.reserve(10);
auto waiter = [&] {
try
{
t.loader.wait(job);
success.fetch_add(1);
}
catch(...)
{
failure.fetch_add(1);
}
};
for (int i = 0; i < 10; i++)
waiters.emplace_back(waiter);
while (failure.load() != 5)
std::this_thread::yield();
ASSERT_EQ(job->waitersCount(), 5);
sync.arrive_and_wait(); // (A)
for (auto & thread : waiters)
thread.join();
ASSERT_EQ(success.load(), 5);
ASSERT_EQ(failure.load(), 5);
ASSERT_EQ(waiters_total.load(), 0);
t.loader.wait();
}
TEST(AsyncLoader, TestConcurrency) TEST(AsyncLoader, TestConcurrency)
{ {
AsyncLoaderTest t(10); AsyncLoaderTest t(10);

View File

@ -64,6 +64,7 @@ namespace DB
M(UInt64, max_concurrent_queries, 0, "Maximum number of concurrently executed queries. Zero means unlimited.", 0) \ M(UInt64, max_concurrent_queries, 0, "Maximum number of concurrently executed queries. Zero means unlimited.", 0) \
M(UInt64, max_concurrent_insert_queries, 0, "Maximum number of concurrently INSERT queries. Zero means unlimited.", 0) \ M(UInt64, max_concurrent_insert_queries, 0, "Maximum number of concurrently INSERT queries. Zero means unlimited.", 0) \
M(UInt64, max_concurrent_select_queries, 0, "Maximum number of concurrently SELECT queries. Zero means unlimited.", 0) \ M(UInt64, max_concurrent_select_queries, 0, "Maximum number of concurrently SELECT queries. Zero means unlimited.", 0) \
M(UInt64, max_waiting_queries, 0, "Maximum number of concurrently waiting queries blocked due to `async_load_databases`. Note that waiting queries are not considered by `max_concurrent_*queries*` limits. Zero means unlimited.", 0) \
\ \
M(Double, cache_size_to_ram_max_ratio, 0.5, "Set cache size to RAM max ratio. Allows to lower cache size on low-memory systems.", 0) \ M(Double, cache_size_to_ram_max_ratio, 0.5, "Set cache size to RAM max ratio. Allows to lower cache size on low-memory systems.", 0) \
M(String, uncompressed_cache_policy, DEFAULT_UNCOMPRESSED_CACHE_POLICY, "Uncompressed cache policy name.", 0) \ M(String, uncompressed_cache_policy, DEFAULT_UNCOMPRESSED_CACHE_POLICY, "Uncompressed cache policy name.", 0) \

View File

@ -265,7 +265,7 @@ class IColumn;
M(UInt64, log_queries_cut_to_length, 100000, "If query length is greater than specified threshold (in bytes), then cut query when writing to query log. Also limit length of printed query in ordinary text log.", 0) \ M(UInt64, log_queries_cut_to_length, 100000, "If query length is greater than specified threshold (in bytes), then cut query when writing to query log. Also limit length of printed query in ordinary text log.", 0) \
M(Float, log_queries_probability, 1., "Log queries with the specified probability.", 0) \ M(Float, log_queries_probability, 1., "Log queries with the specified probability.", 0) \
\ \
M(Bool, log_processors_profiles, false, "Log Processors profile events.", 0) \ M(Bool, log_processors_profiles, true, "Log Processors profile events.", 0) \
M(DistributedProductMode, distributed_product_mode, DistributedProductMode::DENY, "How are distributed subqueries performed inside IN or JOIN sections?", IMPORTANT) \ M(DistributedProductMode, distributed_product_mode, DistributedProductMode::DENY, "How are distributed subqueries performed inside IN or JOIN sections?", IMPORTANT) \
\ \
M(UInt64, max_concurrent_queries_for_all_users, 0, "The maximum number of concurrent requests for all users.", 0) \ M(UInt64, max_concurrent_queries_for_all_users, 0, "The maximum number of concurrent requests for all users.", 0) \
@ -287,6 +287,7 @@ class IColumn;
M(UInt64, read_backoff_min_concurrency, 1, "Settings to try keeping the minimal number of threads in case of slow reads.", 0) \ M(UInt64, read_backoff_min_concurrency, 1, "Settings to try keeping the minimal number of threads in case of slow reads.", 0) \
\ \
M(Float, memory_tracker_fault_probability, 0., "For testing of `exception safety` - throw an exception every time you allocate memory with the specified probability.", 0) \ M(Float, memory_tracker_fault_probability, 0., "For testing of `exception safety` - throw an exception every time you allocate memory with the specified probability.", 0) \
M(Float, merge_tree_read_split_ranges_into_intersecting_and_non_intersecting_injection_probability, 0.0, "For testing of `PartsSplitter` - split read ranges into intersecting and non intersecting every time you read from MergeTree with the specified probability.", 0) \
\ \
M(Bool, enable_http_compression, false, "Compress the result if the client over HTTP said that it understands data compressed by gzip, deflate, zstd, br, lz4, bz2, xz.", 0) \ M(Bool, enable_http_compression, false, "Compress the result if the client over HTTP said that it understands data compressed by gzip, deflate, zstd, br, lz4, bz2, xz.", 0) \
M(Int64, http_zlib_compression_level, 3, "Compression level - used if the client on HTTP said that it understands data compressed by gzip or deflate.", 0) \ M(Int64, http_zlib_compression_level, 3, "Compression level - used if the client on HTTP said that it understands data compressed by gzip or deflate.", 0) \

View File

@ -95,6 +95,7 @@ static std::map<ClickHouseVersion, SettingsChangesHistory::SettingsChanges> sett
{"traverse_shadow_remote_data_paths", false, false, "Traverse shadow directory when query system.remote_data_paths."}, {"traverse_shadow_remote_data_paths", false, false, "Traverse shadow directory when query system.remote_data_paths."},
{"throw_if_deduplication_in_dependent_materialized_views_enabled_with_async_insert", false, true, "Deduplication is dependent materialized view cannot work together with async inserts."}, {"throw_if_deduplication_in_dependent_materialized_views_enabled_with_async_insert", false, true, "Deduplication is dependent materialized view cannot work together with async inserts."},
{"parallel_replicas_allow_in_with_subquery", false, true, "If true, subquery for IN will be executed on every follower replica"}, {"parallel_replicas_allow_in_with_subquery", false, true, "If true, subquery for IN will be executed on every follower replica"},
{"log_processors_profiles", false, true, "Enable by default"},
{"function_locate_has_mysql_compatible_argument_order", false, true, "Increase compatibility with MySQL's locate function."}, {"function_locate_has_mysql_compatible_argument_order", false, true, "Increase compatibility with MySQL's locate function."},
{"filesystem_cache_reserve_space_wait_lock_timeout_milliseconds", 1000, 1000, "Wait time to lock cache for sapce reservation in filesystem cache"}, {"filesystem_cache_reserve_space_wait_lock_timeout_milliseconds", 1000, 1000, "Wait time to lock cache for sapce reservation in filesystem cache"},
{"max_parser_backtracks", 0, 1000000, "Limiting the complexity of parsing"}, {"max_parser_backtracks", 0, 1000000, "Limiting the complexity of parsing"},
@ -103,6 +104,7 @@ static std::map<ClickHouseVersion, SettingsChangesHistory::SettingsChanges> sett
{"keeper_retry_initial_backoff_ms", 100, 100, "Initial backoff timeout for general keeper operations"}, {"keeper_retry_initial_backoff_ms", 100, 100, "Initial backoff timeout for general keeper operations"},
{"keeper_retry_max_backoff_ms", 5000, 5000, "Max backoff timeout for general keeper operations"}, {"keeper_retry_max_backoff_ms", 5000, 5000, "Max backoff timeout for general keeper operations"},
{"s3queue_allow_experimental_sharded_mode", false, false, "Enable experimental sharded mode of S3Queue table engine. It is experimental because it will be rewritten"}, {"s3queue_allow_experimental_sharded_mode", false, false, "Enable experimental sharded mode of S3Queue table engine. It is experimental because it will be rewritten"},
{"merge_tree_read_split_ranges_into_intersecting_and_non_intersecting_injection_probability", 0.0, 0.0, "For testing of `PartsSplitter` - split read ranges into intersecting and non intersecting every time you read from MergeTree with the specified probability."},
}}, }},
{"24.2", {{"allow_suspicious_variant_types", true, false, "Don't allow creating Variant type with suspicious variants by default"}, {"24.2", {{"allow_suspicious_variant_types", true, false, "Don't allow creating Variant type with suspicious variants by default"},
{"validate_experimental_and_suspicious_types_inside_nested_types", false, true, "Validate usage of experimental and suspicious types inside nested types"}, {"validate_experimental_and_suspicious_types_inside_nested_types", false, true, "Validate usage of experimental and suspicious types inside nested types"},

View File

@ -275,7 +275,10 @@ public:
} }
readPODBinary(stack_trace, in); readPODBinary(stack_trace, in);
readVectorBinary(thread_frame_pointers, in);
if (sig != SanitizerTrap)
readVectorBinary(thread_frame_pointers, in);
readBinary(thread_num, in); readBinary(thread_num, in);
readPODBinary(thread_ptr, in); readPODBinary(thread_ptr, in);
@ -542,6 +545,16 @@ private:
#if defined(SANITIZER) #if defined(SANITIZER)
template <typename T>
struct ValueHolder
{
ValueHolder(T value_) : value(value_)
{}
T value;
};
extern "C" void __sanitizer_set_death_callback(void (*)()); extern "C" void __sanitizer_set_death_callback(void (*)());
/// Sanitizers may not expect some function calls from death callback. /// Sanitizers may not expect some function calls from death callback.
@ -559,10 +572,13 @@ static DISABLE_SANITIZER_INSTRUMENTATION void sanitizerDeathCallback()
const StackTrace stack_trace; const StackTrace stack_trace;
int sig = SignalListener::SanitizerTrap; writeBinary(SignalListener::SanitizerTrap, out);
writeBinary(sig, out);
writePODBinary(stack_trace, out); writePODBinary(stack_trace, out);
writeBinary(UInt32(getThreadId()), out); /// We create a dummy struct with a constructor so DISABLE_SANITIZER_INSTRUMENTATION is not applied to it
/// otherwise, Memory sanitizer can't know that values initiialized inside this function are actually initialized
/// because instrumentations are disabled leading to false positives later on
ValueHolder<UInt32> thread_id{static_cast<UInt32>(getThreadId())};
writeBinary(thread_id.value, out);
writePODBinary(current_thread, out); writePODBinary(current_thread, out);
out.next(); out.next();

View File

@ -1127,23 +1127,24 @@ struct AccurateOrNullConvertStrategyAdditions
UInt32 scale { 0 }; UInt32 scale { 0 };
}; };
enum class BehaviourOnErrorFromString
struct ConvertDefaultBehaviorTag {}; {
struct ConvertReturnNullOnErrorTag {}; ConvertDefaultBehaviorTag,
struct ConvertReturnZeroOnErrorTag {}; ConvertReturnNullOnErrorTag,
ConvertReturnZeroOnErrorTag
};
/** Conversion of number types to each other, enums to numbers, dates and datetimes to numbers and back: done by straight assignment. /** Conversion of number types to each other, enums to numbers, dates and datetimes to numbers and back: done by straight assignment.
* (Date is represented internally as number of days from some day; DateTime - as unix timestamp) * (Date is represented internally as number of days from some day; DateTime - as unix timestamp)
*/ */
template <typename FromDataType, typename ToDataType, typename Name, template <typename FromDataType, typename ToDataType, typename Name,
typename SpecialTag = ConvertDefaultBehaviorTag,
FormatSettings::DateTimeOverflowBehavior date_time_overflow_behavior = default_date_time_overflow_behavior> FormatSettings::DateTimeOverflowBehavior date_time_overflow_behavior = default_date_time_overflow_behavior>
struct ConvertImpl struct ConvertImpl
{ {
template <typename Additions = void *> template <typename Additions = void *>
static ColumnPtr NO_SANITIZE_UNDEFINED execute( static ColumnPtr NO_SANITIZE_UNDEFINED execute(
const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type [[maybe_unused]], size_t input_rows_count, const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type [[maybe_unused]], size_t input_rows_count,
Additions additions = Additions()) BehaviourOnErrorFromString from_string_tag [[maybe_unused]], Additions additions = Additions())
{ {
const ColumnWithTypeAndName & named_from = arguments[0]; const ColumnWithTypeAndName & named_from = arguments[0];
@ -1176,8 +1177,7 @@ struct ConvertImpl
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTimeImpl<date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTimeImpl<date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
} }
else if constexpr (std::is_same_v<FromDataType, DataTypeDateTime64> && std::is_same_v<ToDataType, DataTypeDate32> else if constexpr (std::is_same_v<FromDataType, DataTypeDateTime64> && std::is_same_v<ToDataType, DataTypeDate32>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<DataTypeDateTime64, DataTypeDate32, TransformDateTime64<ToDate32Impl>, false>::template execute<Additions>( return DateTimeTransformImpl<DataTypeDateTime64, DataTypeDate32, TransformDateTime64<ToDate32Impl>, false>::template execute<Additions>(
arguments, result_type, input_rows_count, additions); arguments, result_type, input_rows_count, additions);
@ -1194,8 +1194,7 @@ struct ConvertImpl
else if constexpr (( else if constexpr ((
std::is_same_v<FromDataType, DataTypeUInt32> std::is_same_v<FromDataType, DataTypeUInt32>
|| std::is_same_v<FromDataType, DataTypeUInt64>) || std::is_same_v<FromDataType, DataTypeUInt64>)
&& std::is_same_v<ToDataType, DataTypeDate> && std::is_same_v<ToDataType, DataTypeDate>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTransform32Or64<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTransform32Or64<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
@ -1203,8 +1202,7 @@ struct ConvertImpl
else if constexpr (( else if constexpr ((
std::is_same_v<FromDataType, DataTypeInt8> std::is_same_v<FromDataType, DataTypeInt8>
|| std::is_same_v<FromDataType, DataTypeInt16>) || std::is_same_v<FromDataType, DataTypeInt16>)
&& std::is_same_v<ToDataType, DataTypeDate> && std::is_same_v<ToDataType, DataTypeDate>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTransform8Or16Signed<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTransform8Or16Signed<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
@ -1214,8 +1212,7 @@ struct ConvertImpl
|| std::is_same_v<FromDataType, DataTypeInt64> || std::is_same_v<FromDataType, DataTypeInt64>
|| std::is_same_v<FromDataType, DataTypeFloat32> || std::is_same_v<FromDataType, DataTypeFloat32>
|| std::is_same_v<FromDataType, DataTypeFloat64>) || std::is_same_v<FromDataType, DataTypeFloat64>)
&& std::is_same_v<ToDataType, DataTypeDate> && std::is_same_v<ToDataType, DataTypeDate>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTransform32Or64Signed<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTransform32Or64Signed<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
@ -1223,8 +1220,7 @@ struct ConvertImpl
else if constexpr (( else if constexpr ((
std::is_same_v<FromDataType, DataTypeUInt32> std::is_same_v<FromDataType, DataTypeUInt32>
|| std::is_same_v<FromDataType, DataTypeUInt64>) || std::is_same_v<FromDataType, DataTypeUInt64>)
&& std::is_same_v<ToDataType, DataTypeDate32> && std::is_same_v<ToDataType, DataTypeDate32>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDate32Transform32Or64<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDate32Transform32Or64<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
@ -1232,8 +1228,7 @@ struct ConvertImpl
else if constexpr (( else if constexpr ((
std::is_same_v<FromDataType, DataTypeInt8> std::is_same_v<FromDataType, DataTypeInt8>
|| std::is_same_v<FromDataType, DataTypeInt16>) || std::is_same_v<FromDataType, DataTypeInt16>)
&& std::is_same_v<ToDataType, DataTypeDate32> && std::is_same_v<ToDataType, DataTypeDate32>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDate32Transform8Or16Signed<typename FromDataType::FieldType>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDate32Transform8Or16Signed<typename FromDataType::FieldType>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
@ -1243,8 +1238,7 @@ struct ConvertImpl
|| std::is_same_v<FromDataType, DataTypeInt64> || std::is_same_v<FromDataType, DataTypeInt64>
|| std::is_same_v<FromDataType, DataTypeFloat32> || std::is_same_v<FromDataType, DataTypeFloat32>
|| std::is_same_v<FromDataType, DataTypeFloat64>) || std::is_same_v<FromDataType, DataTypeFloat64>)
&& std::is_same_v<ToDataType, DataTypeDate32> && std::is_same_v<ToDataType, DataTypeDate32>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDate32Transform32Or64Signed<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDate32Transform32Or64Signed<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
@ -1254,15 +1248,13 @@ struct ConvertImpl
std::is_same_v<FromDataType, DataTypeInt8> std::is_same_v<FromDataType, DataTypeInt8>
|| std::is_same_v<FromDataType, DataTypeInt16> || std::is_same_v<FromDataType, DataTypeInt16>
|| std::is_same_v<FromDataType, DataTypeInt32>) || std::is_same_v<FromDataType, DataTypeInt32>)
&& std::is_same_v<ToDataType, DataTypeDateTime> && std::is_same_v<ToDataType, DataTypeDateTime>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTimeTransformSigned<typename FromDataType::FieldType, UInt32, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTimeTransformSigned<typename FromDataType::FieldType, UInt32, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
} }
else if constexpr (std::is_same_v<FromDataType, DataTypeUInt64> else if constexpr (std::is_same_v<FromDataType, DataTypeUInt64>
&& std::is_same_v<ToDataType, DataTypeDateTime> && std::is_same_v<ToDataType, DataTypeDateTime>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTimeTransform64<typename FromDataType::FieldType, UInt32, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTimeTransform64<typename FromDataType::FieldType, UInt32, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
@ -1271,8 +1263,7 @@ struct ConvertImpl
std::is_same_v<FromDataType, DataTypeInt64> std::is_same_v<FromDataType, DataTypeInt64>
|| std::is_same_v<FromDataType, DataTypeFloat32> || std::is_same_v<FromDataType, DataTypeFloat32>
|| std::is_same_v<FromDataType, DataTypeFloat64>) || std::is_same_v<FromDataType, DataTypeFloat64>)
&& std::is_same_v<ToDataType, DataTypeDateTime> && std::is_same_v<ToDataType, DataTypeDateTime>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTimeTransform64Signed<typename FromDataType::FieldType, UInt32, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTimeTransform64Signed<typename FromDataType::FieldType, UInt32, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count);
@ -1282,15 +1273,13 @@ struct ConvertImpl
|| std::is_same_v<FromDataType, DataTypeInt16> || std::is_same_v<FromDataType, DataTypeInt16>
|| std::is_same_v<FromDataType, DataTypeInt32> || std::is_same_v<FromDataType, DataTypeInt32>
|| std::is_same_v<FromDataType, DataTypeInt64>) || std::is_same_v<FromDataType, DataTypeInt64>)
&& std::is_same_v<ToDataType, DataTypeDateTime64> && std::is_same_v<ToDataType, DataTypeDateTime64>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTime64TransformSigned<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTime64TransformSigned<typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count, additions); arguments, result_type, input_rows_count, additions);
} }
else if constexpr (std::is_same_v<FromDataType, DataTypeUInt64> else if constexpr (std::is_same_v<FromDataType, DataTypeUInt64>
&& std::is_same_v<ToDataType, DataTypeDateTime64> && std::is_same_v<ToDataType, DataTypeDateTime64>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTime64TransformUnsigned<UInt64, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTime64TransformUnsigned<UInt64, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count, additions); arguments, result_type, input_rows_count, additions);
@ -1298,23 +1287,20 @@ struct ConvertImpl
else if constexpr (( else if constexpr ((
std::is_same_v<FromDataType, DataTypeFloat32> std::is_same_v<FromDataType, DataTypeFloat32>
|| std::is_same_v<FromDataType, DataTypeFloat64>) || std::is_same_v<FromDataType, DataTypeFloat64>)
&& std::is_same_v<ToDataType, DataTypeDateTime64> && std::is_same_v<ToDataType, DataTypeDateTime64>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTime64TransformFloat<FromDataType, typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTime64TransformFloat<FromDataType, typename FromDataType::FieldType, default_date_time_overflow_behavior>, false>::template execute<Additions>(
arguments, result_type, input_rows_count, additions); arguments, result_type, input_rows_count, additions);
} }
/// Conversion of DateTime64 to Date or DateTime: discards fractional part. /// Conversion of DateTime64 to Date or DateTime: discards fractional part.
else if constexpr (std::is_same_v<FromDataType, DataTypeDateTime64> else if constexpr (std::is_same_v<FromDataType, DataTypeDateTime64>
&& std::is_same_v<ToDataType, DataTypeDate> && std::is_same_v<ToDataType, DataTypeDate>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, TransformDateTime64<ToDateImpl<date_time_overflow_behavior>>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, TransformDateTime64<ToDateImpl<date_time_overflow_behavior>>, false>::template execute<Additions>(
arguments, result_type, input_rows_count, additions); arguments, result_type, input_rows_count, additions);
} }
else if constexpr (std::is_same_v<FromDataType, DataTypeDateTime64> else if constexpr (std::is_same_v<FromDataType, DataTypeDateTime64>
&& std::is_same_v<ToDataType, DataTypeDateTime> && std::is_same_v<ToDataType, DataTypeDateTime>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, TransformDateTime64<ToDateTimeImpl<date_time_overflow_behavior>>, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, TransformDateTime64<ToDateTimeImpl<date_time_overflow_behavior>>, false>::template execute<Additions>(
arguments, result_type, input_rows_count, additions); arguments, result_type, input_rows_count, additions);
@ -1324,8 +1310,7 @@ struct ConvertImpl
std::is_same_v<FromDataType, DataTypeDate> std::is_same_v<FromDataType, DataTypeDate>
|| std::is_same_v<FromDataType, DataTypeDate32> || std::is_same_v<FromDataType, DataTypeDate32>
|| std::is_same_v<FromDataType, DataTypeDateTime>) || std::is_same_v<FromDataType, DataTypeDateTime>)
&& std::is_same_v<ToDataType, DataTypeDateTime64> && std::is_same_v<ToDataType, DataTypeDateTime64>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTime64Transform, false>::template execute<Additions>( return DateTimeTransformImpl<FromDataType, ToDataType, ToDateTime64Transform, false>::template execute<Additions>(
arguments, result_type, input_rows_count, additions); arguments, result_type, input_rows_count, additions);
@ -1443,8 +1428,7 @@ struct ConvertImpl
/// Conversion from FixedString to String. /// Conversion from FixedString to String.
/// Cutting sequences of zero bytes from end of strings. /// Cutting sequences of zero bytes from end of strings.
else if constexpr (std::is_same_v<ToDataType, DataTypeString> else if constexpr (std::is_same_v<ToDataType, DataTypeString>
&& std::is_same_v<FromDataType, DataTypeFixedString> && std::is_same_v<FromDataType, DataTypeFixedString>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
ColumnUInt8::MutablePtr null_map = copyNullMap(arguments[0].column); ColumnUInt8::MutablePtr null_map = copyNullMap(arguments[0].column);
const auto & nested = columnGetNested(arguments[0]); const auto & nested = columnGetNested(arguments[0]);
@ -1488,8 +1472,7 @@ struct ConvertImpl
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function {}", throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column {} of first argument of function {}",
arguments[0].column->getName(), Name::name); arguments[0].column->getName(), Name::name);
} }
else if constexpr (std::is_same_v<ToDataType, DataTypeString> else if constexpr (std::is_same_v<ToDataType, DataTypeString>)
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
/// Anything else to String. /// Anything else to String.
@ -1550,27 +1533,35 @@ struct ConvertImpl
&& std::is_same_v<FromDataType, DataTypeString> && std::is_same_v<FromDataType, DataTypeString>
&& std::is_same_v<ToDataType, DataTypeUInt32>) && std::is_same_v<ToDataType, DataTypeUInt32>)
{ {
return ConvertImpl<FromDataType, DataTypeDateTime, Name, SpecialTag, date_time_overflow_behavior>::template execute<Additions>( return ConvertImpl<FromDataType, DataTypeDateTime, Name, date_time_overflow_behavior>::template execute<Additions>(
arguments, result_type, input_rows_count); arguments, result_type, input_rows_count, from_string_tag);
} }
else if constexpr ((std::is_same_v<FromDataType, DataTypeString> || std::is_same_v<FromDataType, DataTypeFixedString>) else if constexpr ((std::is_same_v<FromDataType, DataTypeString> || std::is_same_v<FromDataType, DataTypeFixedString>))
&& std::is_same_v<SpecialTag, ConvertDefaultBehaviorTag>)
{ {
return ConvertThroughParsing<FromDataType, ToDataType, Name, ConvertFromStringExceptionMode::Throw, ConvertFromStringParsingMode::Normal>::execute( switch (from_string_tag)
arguments, result_type, input_rows_count, additions); {
} case BehaviourOnErrorFromString::ConvertDefaultBehaviorTag:
else if constexpr ((std::is_same_v<FromDataType, DataTypeString> || std::is_same_v<FromDataType, DataTypeFixedString>) return ConvertThroughParsing<FromDataType,
&& std::is_same_v<SpecialTag, ConvertReturnNullOnErrorTag>) ToDataType,
{ Name,
return ConvertThroughParsing<FromDataType, ToDataType, Name, ConvertFromStringExceptionMode::Null, ConvertFromStringParsingMode::Normal>::execute( ConvertFromStringExceptionMode::Throw,
arguments, result_type, input_rows_count, additions); ConvertFromStringParsingMode::Normal>::execute(
} arguments, result_type, input_rows_count, additions);
else if constexpr ((std::is_same_v<FromDataType, DataTypeString> || std::is_same_v<FromDataType, DataTypeFixedString>) case BehaviourOnErrorFromString::ConvertReturnNullOnErrorTag:
&& is_any_of<ToDataType, DataTypeIPv4, DataTypeIPv6> return ConvertThroughParsing<FromDataType,
&& std::is_same_v<SpecialTag, ConvertReturnZeroOnErrorTag>) ToDataType,
{ Name,
return ConvertThroughParsing<FromDataType, ToDataType, Name, ConvertFromStringExceptionMode::Zero, ConvertFromStringParsingMode::Normal>::execute( ConvertFromStringExceptionMode::Null,
arguments, result_type, input_rows_count, additions); ConvertFromStringParsingMode::Normal>::execute(
arguments, result_type, input_rows_count, additions);
case BehaviourOnErrorFromString::ConvertReturnZeroOnErrorTag:
return ConvertThroughParsing<FromDataType,
ToDataType,
Name,
ConvertFromStringExceptionMode::Zero,
ConvertFromStringParsingMode::Normal>::execute(
arguments, result_type, input_rows_count, additions);
}
} }
else else
{ {
@ -2165,12 +2156,11 @@ private:
if (context) if (context)
date_time_overflow_behavior = context->getSettingsRef().date_time_overflow_behavior.value; date_time_overflow_behavior = context->getSettingsRef().date_time_overflow_behavior.value;
auto call = [&](const auto & types, const auto & tag) -> bool auto call = [&](const auto & types, BehaviourOnErrorFromString from_string_tag) -> bool
{ {
using Types = std::decay_t<decltype(types)>; using Types = std::decay_t<decltype(types)>;
using LeftDataType = typename Types::LeftType; using LeftDataType = typename Types::LeftType;
using RightDataType = typename Types::RightType; using RightDataType = typename Types::RightType;
using SpecialTag = std::decay_t<decltype(tag)>;
if constexpr (IsDataTypeDecimal<RightDataType>) if constexpr (IsDataTypeDecimal<RightDataType>)
{ {
@ -2191,13 +2181,13 @@ private:
switch (date_time_overflow_behavior) switch (date_time_overflow_behavior)
{ {
case FormatSettings::DateTimeOverflowBehavior::Throw: case FormatSettings::DateTimeOverflowBehavior::Throw:
result_column = ConvertImpl<LeftDataType, RightDataType, Name, SpecialTag, FormatSettings::DateTimeOverflowBehavior::Throw>::execute(arguments, result_type, input_rows_count, scale); result_column = ConvertImpl<LeftDataType, RightDataType, Name, FormatSettings::DateTimeOverflowBehavior::Throw>::execute(arguments, result_type, input_rows_count, from_string_tag, scale);
break; break;
case FormatSettings::DateTimeOverflowBehavior::Ignore: case FormatSettings::DateTimeOverflowBehavior::Ignore:
result_column = ConvertImpl<LeftDataType, RightDataType, Name, SpecialTag, FormatSettings::DateTimeOverflowBehavior::Ignore>::execute(arguments, result_type, input_rows_count, scale); result_column = ConvertImpl<LeftDataType, RightDataType, Name, FormatSettings::DateTimeOverflowBehavior::Ignore>::execute(arguments, result_type, input_rows_count, from_string_tag, scale);
break; break;
case FormatSettings::DateTimeOverflowBehavior::Saturate: case FormatSettings::DateTimeOverflowBehavior::Saturate:
result_column = ConvertImpl<LeftDataType, RightDataType, Name, SpecialTag, FormatSettings::DateTimeOverflowBehavior::Saturate>::execute(arguments, result_type, input_rows_count, scale); result_column = ConvertImpl<LeftDataType, RightDataType, Name, FormatSettings::DateTimeOverflowBehavior::Saturate>::execute(arguments, result_type, input_rows_count, from_string_tag, scale);
break; break;
} }
@ -2208,20 +2198,20 @@ private:
switch (date_time_overflow_behavior) switch (date_time_overflow_behavior)
{ {
case FormatSettings::DateTimeOverflowBehavior::Throw: case FormatSettings::DateTimeOverflowBehavior::Throw:
result_column = ConvertImpl<LeftDataType, RightDataType, Name, SpecialTag, FormatSettings::DateTimeOverflowBehavior::Throw>::execute(arguments, result_type, input_rows_count, dt64->getScale()); result_column = ConvertImpl<LeftDataType, RightDataType, Name, FormatSettings::DateTimeOverflowBehavior::Throw>::execute(arguments, result_type, input_rows_count, from_string_tag, dt64->getScale());
break; break;
case FormatSettings::DateTimeOverflowBehavior::Ignore: case FormatSettings::DateTimeOverflowBehavior::Ignore:
result_column = ConvertImpl<LeftDataType, RightDataType, Name, SpecialTag, FormatSettings::DateTimeOverflowBehavior::Ignore>::execute(arguments, result_type, input_rows_count, dt64->getScale()); result_column = ConvertImpl<LeftDataType, RightDataType, Name, FormatSettings::DateTimeOverflowBehavior::Ignore>::execute(arguments, result_type, input_rows_count, from_string_tag, dt64->getScale());
break; break;
case FormatSettings::DateTimeOverflowBehavior::Saturate: case FormatSettings::DateTimeOverflowBehavior::Saturate:
result_column = ConvertImpl<LeftDataType, RightDataType, Name, SpecialTag, FormatSettings::DateTimeOverflowBehavior::Saturate>::execute(arguments, result_type, input_rows_count, dt64->getScale()); result_column = ConvertImpl<LeftDataType, RightDataType, Name, FormatSettings::DateTimeOverflowBehavior::Saturate>::execute(arguments, result_type, input_rows_count, from_string_tag, dt64->getScale());
break; break;
} }
} }
#define GENERATE_OVERFLOW_MODE_CASE(OVERFLOW_MODE) \ #define GENERATE_OVERFLOW_MODE_CASE(OVERFLOW_MODE) \
case FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE: \ case FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE: \
result_column = ConvertImpl<LeftDataType, RightDataType, Name, SpecialTag, FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE>::execute( \ result_column = ConvertImpl<LeftDataType, RightDataType, Name, FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE>::execute( \
arguments, result_type, input_rows_count); \ arguments, result_type, input_rows_count, from_string_tag); \
break; break;
else if constexpr (IsDataTypeDecimalOrNumber<LeftDataType> && IsDataTypeDecimalOrNumber<RightDataType>) else if constexpr (IsDataTypeDecimalOrNumber<LeftDataType> && IsDataTypeDecimalOrNumber<RightDataType>)
@ -2262,7 +2252,7 @@ private:
} }
#undef GENERATE_OVERFLOW_MODE_CASE #undef GENERATE_OVERFLOW_MODE_CASE
else else
result_column = ConvertImpl<LeftDataType, RightDataType, Name, SpecialTag>::execute(arguments, result_type, input_rows_count); result_column = ConvertImpl<LeftDataType, RightDataType, Name>::execute(arguments, result_type, input_rows_count, from_string_tag);
return true; return true;
}; };
@ -2275,7 +2265,7 @@ private:
if (to_datetime64 || scale != 0) /// When scale = 0, the data type is DateTime otherwise the data type is DateTime64 if (to_datetime64 || scale != 0) /// When scale = 0, the data type is DateTime otherwise the data type is DateTime64
{ {
if (!callOnIndexAndDataType<DataTypeDateTime64>(from_type->getTypeId(), call, ConvertDefaultBehaviorTag{})) if (!callOnIndexAndDataType<DataTypeDateTime64>(from_type->getTypeId(), call, BehaviourOnErrorFromString::ConvertDefaultBehaviorTag))
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}", throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of argument of function {}",
arguments[0].type->getName(), getName()); arguments[0].type->getName(), getName());
@ -2292,23 +2282,25 @@ private:
bool done = false; bool done = false;
if constexpr (is_any_of<ToDataType, DataTypeString, DataTypeFixedString>) if constexpr (is_any_of<ToDataType, DataTypeString, DataTypeFixedString>)
{ {
done = callOnIndexAndDataType<ToDataType>(from_type->getTypeId(), call, ConvertDefaultBehaviorTag{}); done = callOnIndexAndDataType<ToDataType>(from_type->getTypeId(), call, BehaviourOnErrorFromString::ConvertDefaultBehaviorTag);
} }
else else
{ {
bool cast_ipv4_ipv6_default_on_conversion_error = false; bool cast_ipv4_ipv6_default_on_conversion_error = false;
if constexpr (is_any_of<ToDataType, DataTypeIPv4, DataTypeIPv6>) if constexpr (is_any_of<ToDataType, DataTypeIPv4, DataTypeIPv6>)
{
if (context && (cast_ipv4_ipv6_default_on_conversion_error = context->getSettingsRef().cast_ipv4_ipv6_default_on_conversion_error)) if (context && (cast_ipv4_ipv6_default_on_conversion_error = context->getSettingsRef().cast_ipv4_ipv6_default_on_conversion_error))
done = callOnIndexAndDataType<ToDataType>(from_type->getTypeId(), call, ConvertReturnZeroOnErrorTag{}); done = callOnIndexAndDataType<ToDataType>(from_type->getTypeId(), call, BehaviourOnErrorFromString::ConvertReturnZeroOnErrorTag);
}
if (!cast_ipv4_ipv6_default_on_conversion_error) if (!cast_ipv4_ipv6_default_on_conversion_error)
{ {
/// We should use ConvertFromStringExceptionMode::Null mode when converting from String (or FixedString) /// We should use ConvertFromStringExceptionMode::Null mode when converting from String (or FixedString)
/// to Nullable type, to avoid 'value is too short' error on attempt to parse empty string from NULL values. /// to Nullable type, to avoid 'value is too short' error on attempt to parse empty string from NULL values.
if (to_nullable && WhichDataType(from_type).isStringOrFixedString()) if (to_nullable && WhichDataType(from_type).isStringOrFixedString())
done = callOnIndexAndDataType<ToDataType>(from_type->getTypeId(), call, ConvertReturnNullOnErrorTag{}); done = callOnIndexAndDataType<ToDataType>(from_type->getTypeId(), call, BehaviourOnErrorFromString::ConvertReturnNullOnErrorTag);
else else
done = callOnIndexAndDataType<ToDataType>(from_type->getTypeId(), call, ConvertDefaultBehaviorTag{}); done = callOnIndexAndDataType<ToDataType>(from_type->getTypeId(), call, BehaviourOnErrorFromString::ConvertDefaultBehaviorTag);
} }
} }
@ -3185,8 +3177,8 @@ private:
{ {
#define GENERATE_OVERFLOW_MODE_CASE(OVERFLOW_MODE, ADDITIONS) \ #define GENERATE_OVERFLOW_MODE_CASE(OVERFLOW_MODE, ADDITIONS) \
case FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE: \ case FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE: \
result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName, ConvertDefaultBehaviorTag, FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE>::execute( \ result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName, FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE>::execute( \
arguments, result_type, input_rows_count, ADDITIONS()); \ arguments, result_type, input_rows_count, BehaviourOnErrorFromString::ConvertDefaultBehaviorTag, ADDITIONS()); \
break; break;
if (wrapper_cast_type == CastType::accurate) if (wrapper_cast_type == CastType::accurate)
{ {
@ -3215,8 +3207,8 @@ private:
{ {
#define GENERATE_OVERFLOW_MODE_CASE(OVERFLOW_MODE, ADDITIONS) \ #define GENERATE_OVERFLOW_MODE_CASE(OVERFLOW_MODE, ADDITIONS) \
case FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE: \ case FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE: \
result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName, ConvertDefaultBehaviorTag, FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE>::template execute<ADDITIONS>( \ result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName, FormatSettings::DateTimeOverflowBehavior::OVERFLOW_MODE>::template execute<ADDITIONS>( \
arguments, result_type, input_rows_count); \ arguments, result_type, input_rows_count, BehaviourOnErrorFromString::ConvertDefaultBehaviorTag); \
break; break;
if (wrapper_cast_type == CastType::accurate) if (wrapper_cast_type == CastType::accurate)
{ {
@ -3376,7 +3368,7 @@ arguments, result_type, input_rows_count); \
AccurateConvertStrategyAdditions additions; AccurateConvertStrategyAdditions additions;
additions.scale = scale; additions.scale = scale;
result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName>::execute( result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName>::execute(
arguments, result_type, input_rows_count, additions); arguments, result_type, input_rows_count, BehaviourOnErrorFromString::ConvertDefaultBehaviorTag, additions);
return true; return true;
} }
@ -3385,7 +3377,7 @@ arguments, result_type, input_rows_count); \
AccurateOrNullConvertStrategyAdditions additions; AccurateOrNullConvertStrategyAdditions additions;
additions.scale = scale; additions.scale = scale;
result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName>::execute( result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName>::execute(
arguments, result_type, input_rows_count, additions); arguments, result_type, input_rows_count, BehaviourOnErrorFromString::ConvertDefaultBehaviorTag, additions);
return true; return true;
} }
@ -3397,14 +3389,14 @@ arguments, result_type, input_rows_count); \
/// Consistent with CAST(Nullable(String) AS Nullable(Numbers)) /// Consistent with CAST(Nullable(String) AS Nullable(Numbers))
/// In case when converting to Nullable type, we apply different parsing rule, /// In case when converting to Nullable type, we apply different parsing rule,
/// that will not throw an exception but return NULL in case of malformed input. /// that will not throw an exception but return NULL in case of malformed input.
result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName, ConvertReturnNullOnErrorTag>::execute( result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName>::execute(
arguments, result_type, input_rows_count, scale); arguments, result_type, input_rows_count, BehaviourOnErrorFromString::ConvertReturnNullOnErrorTag, scale);
return true; return true;
} }
} }
result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName>::execute(arguments, result_type, input_rows_count, scale); result_column = ConvertImpl<LeftDataType, RightDataType, FunctionCastName>::execute(arguments, result_type, input_rows_count, BehaviourOnErrorFromString::ConvertDefaultBehaviorTag, scale);
return true; return true;
}); });

View File

@ -48,11 +48,12 @@ dispatchForIntervalColumns(const ColumnWithTypeAndName & interval_column, const
return {interval_type->getKind(), num_units}; return {interval_type->getKind(), num_units};
} }
ColumnPtr executeWindowBound(const ColumnPtr & column, int index, const String & function_name) ColumnPtr executeWindowBound(const ColumnPtr & column, size_t index, const String & function_name)
{ {
chassert(index == 0 || index == 1);
if (const ColumnTuple * col_tuple = checkAndGetColumn<ColumnTuple>(column.get()); col_tuple) if (const ColumnTuple * col_tuple = checkAndGetColumn<ColumnTuple>(column.get()); col_tuple)
{ {
if (!checkColumn<ColumnVector<UInt32>>(*col_tuple->getColumnPtr(index))) if (index >= col_tuple->tupleSize() || !checkColumn<ColumnVector<UInt32>>(*col_tuple->getColumnPtr(index)))
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal column for first argument of function {}. " throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal column for first argument of function {}. "
"Must be a Tuple(DataTime, DataTime)", function_name); "Must be a Tuple(DataTime, DataTime)", function_name);
return col_tuple->getColumnPtr(index); return col_tuple->getColumnPtr(index);
@ -109,6 +110,17 @@ bool checkIntervalOrTimeZoneArgument(const ColumnWithTypeAndName & argument, con
return true; return true;
} }
enum TimeWindowFunctionName
{
TUMBLE,
TUMBLE_START,
TUMBLE_END,
HOP,
HOP_START,
HOP_END,
WINDOW_ID
};
template <TimeWindowFunctionName type> template <TimeWindowFunctionName type>
struct TimeWindowImpl struct TimeWindowImpl
{ {

View File

@ -21,16 +21,6 @@ namespace DB
* hopEnd(window_id) * hopEnd(window_id)
* hopEnd(time_attr, hop_interval, window_interval [, timezone]) * hopEnd(time_attr, hop_interval, window_interval [, timezone])
*/ */
enum TimeWindowFunctionName
{
TUMBLE,
TUMBLE_START,
TUMBLE_END,
HOP,
HOP_START,
HOP_END,
WINDOW_ID
};
template <IntervalKind::Kind unit> template <IntervalKind::Kind unit>
struct ToStartOfTransform; struct ToStartOfTransform;
@ -73,17 +63,15 @@ struct ToStartOfTransform;
TRANSFORM_TIME(Second) TRANSFORM_TIME(Second)
#undef TRANSFORM_TIME #undef TRANSFORM_TIME
/// NOLINTBEGIN(bugprone-macro-parentheses)
#define TRANSFORM_SUBSECONDS(INTERVAL_KIND, DEF_SCALE) \ #define TRANSFORM_SUBSECONDS(INTERVAL_KIND, DEF_SCALE) \
template<> \ template<> \
struct ToStartOfTransform<IntervalKind::Kind::INTERVAL_KIND> \ struct ToStartOfTransform<IntervalKind::Kind::INTERVAL_KIND> \
{ \ { \
static Int64 execute(Int64 t, UInt64 delta, const UInt32 scale) \ static Int64 execute(Int64 t, UInt64 delta, const UInt32 scale) \
{ \ { \
if (scale <= DEF_SCALE) \ if (scale <= (DEF_SCALE)) \
{ \ { \
auto val = t * DecimalUtils::scaleMultiplier<DateTime64>(DEF_SCALE - scale); \ auto val = t * DecimalUtils::scaleMultiplier<DateTime64>((DEF_SCALE) - scale); \
if (delta == 1) \ if (delta == 1) \
return val; \ return val; \
else \ else \
@ -91,7 +79,7 @@ template<> \
} \ } \
else \ else \
{ \ { \
return t - (t % (delta * DecimalUtils::scaleMultiplier<DateTime64>(scale - DEF_SCALE))) ; \ return t - (t % (delta * DecimalUtils::scaleMultiplier<DateTime64>(scale - (DEF_SCALE)))) ; \
} \ } \
} \ } \
}; };
@ -131,7 +119,7 @@ template<> \
struct AddTime<IntervalKind::Kind::INTERVAL_KIND> \ struct AddTime<IntervalKind::Kind::INTERVAL_KIND> \
{ \ { \
static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) \ static inline NO_SANITIZE_UNDEFINED UInt32 execute(UInt32 t, Int64 delta, const DateLUTImpl &) \
{ return static_cast<UInt32>(t + delta * INTERVAL); } \ { return static_cast<UInt32>(t + delta * (INTERVAL)); } \
}; };
ADD_TIME(Day, 86400) ADD_TIME(Day, 86400)
ADD_TIME(Hour, 3600) ADD_TIME(Hour, 3600)
@ -145,12 +133,12 @@ template <> \
{ \ { \
static inline NO_SANITIZE_UNDEFINED Int64 execute(Int64 t, UInt64 delta, const UInt32 scale) \ static inline NO_SANITIZE_UNDEFINED Int64 execute(Int64 t, UInt64 delta, const UInt32 scale) \
{ \ { \
if (scale < DEF_SCALE) \ if (scale < (DEF_SCALE)) \
{ \ { \
return t + delta * DecimalUtils::scaleMultiplier<DateTime64>(DEF_SCALE - scale); \ return t + delta * DecimalUtils::scaleMultiplier<DateTime64>((DEF_SCALE) - scale); \
} \ } \
else \ else \
return t + delta * DecimalUtils::scaleMultiplier<DateTime64>(scale - DEF_SCALE); \ return t + delta * DecimalUtils::scaleMultiplier<DateTime64>(scale - (DEF_SCALE)); \
} \ } \
}; };
ADD_SUBSECONDS(Millisecond, 3) ADD_SUBSECONDS(Millisecond, 3)
@ -158,6 +146,4 @@ template <> \
ADD_SUBSECONDS(Nanosecond, 9) ADD_SUBSECONDS(Nanosecond, 9)
#undef ADD_SUBSECONDS #undef ADD_SUBSECONDS
/// NOLINTEND(bugprone-macro-parentheses)
} }

View File

@ -194,11 +194,6 @@ namespace
auto outcome = client_ptr->CompleteMultipartUpload(request); auto outcome = client_ptr->CompleteMultipartUpload(request);
if (blob_storage_log)
blob_storage_log->addEvent(BlobStorageLogElement::EventType::MultiPartUploadComplete,
dest_bucket, dest_key, /* local_path_ */ {}, /* data_size */ 0,
outcome.IsSuccess() ? nullptr : &outcome.GetError());
if (blob_storage_log) if (blob_storage_log)
blob_storage_log->addEvent(BlobStorageLogElement::EventType::MultiPartUploadComplete, blob_storage_log->addEvent(BlobStorageLogElement::EventType::MultiPartUploadComplete,
dest_bucket, dest_key, /* local_path_ */ {}, /* data_size */ 0, dest_bucket, dest_key, /* local_path_ */ {}, /* data_size */ 0,

View File

@ -178,6 +178,22 @@ namespace
elements_to_revoke.emplace_back(std::move(element_to_revoke)); elements_to_revoke.emplace_back(std::move(element_to_revoke));
} }
/// Additional check for REVOKE
///
/// If user1 has the rights
/// GRANT SELECT ON *.* TO user1;
/// REVOKE SELECT ON system.* FROM user1;
/// REVOKE SELECT ON mydb.* FROM user1;
///
/// And user2 has the rights
/// GRANT SELECT ON *.* TO user2;
/// REVOKE SELECT ON system.* FROM user2;
///
/// the query `REVOKE SELECT ON *.* FROM user1` executed by user2 should succeed.
if (current_user_access.getAccessRights()->containsWithGrantOption(access_to_revoke))
return;
/// Technically, this check always fails if `containsWithGrantOption` returns `false`. But we still call it to get a nice exception message.
current_user_access.checkGrantOption(elements_to_revoke); current_user_access.checkGrantOption(elements_to_revoke);
} }

View File

@ -219,7 +219,7 @@ AsynchronousInsertQueue::AsynchronousInsertQueue(ContextPtr context_, size_t poo
dump_by_first_update_threads.emplace_back([this, i] { processBatchDeadlines(i); }); dump_by_first_update_threads.emplace_back([this, i] { processBatchDeadlines(i); });
} }
AsynchronousInsertQueue::~AsynchronousInsertQueue() void AsynchronousInsertQueue::flushAndShutdown()
{ {
try try
{ {
@ -258,6 +258,19 @@ AsynchronousInsertQueue::~AsynchronousInsertQueue()
} }
} }
AsynchronousInsertQueue::~AsynchronousInsertQueue()
{
for (const auto & shard : queue_shards)
{
for (const auto & [first_update, elem] : shard.queue)
{
const auto & insert_query = elem.key.query->as<const ASTInsertQuery &>();
LOG_WARNING(log, "Has unprocessed async insert for {}.{}",
backQuoteIfNeed(insert_query.getDatabase()), backQuoteIfNeed(insert_query.getTable()));
}
}
}
void AsynchronousInsertQueue::scheduleDataProcessingJob( void AsynchronousInsertQueue::scheduleDataProcessingJob(
const InsertQuery & key, InsertDataPtr data, ContextPtr global_context, size_t shard_num) const InsertQuery & key, InsertDataPtr data, ContextPtr global_context, size_t shard_num)
{ {

View File

@ -63,6 +63,10 @@ public:
PushResult pushQueryWithBlock(ASTPtr query, Block block, ContextPtr query_context); PushResult pushQueryWithBlock(ASTPtr query, Block block, ContextPtr query_context);
size_t getPoolSize() const { return pool_size; } size_t getPoolSize() const { return pool_size; }
/// This method should be called manually because it's not flushed automatically in dtor
/// because all tables may be already unloaded when we destroy AsynchronousInsertQueue
void flushAndShutdown();
private: private:
struct InsertQuery struct InsertQuery

View File

@ -561,7 +561,13 @@ struct ContextSharedPart : boost::noncopyable
return; return;
/// Need to flush the async insert queue before shutting down the database catalog /// Need to flush the async insert queue before shutting down the database catalog
async_insert_queue.reset(); std::shared_ptr<AsynchronousInsertQueue> delete_async_insert_queue;
{
std::lock_guard lock(mutex);
delete_async_insert_queue = std::move(async_insert_queue);
}
if (delete_async_insert_queue)
delete_async_insert_queue->flushAndShutdown();
/// Stop periodic reloading of the configuration files. /// Stop periodic reloading of the configuration files.
/// This must be done first because otherwise the reloading may pass a changed config /// This must be done first because otherwise the reloading may pass a changed config
@ -585,6 +591,8 @@ struct ContextSharedPart : boost::noncopyable
LOG_TRACE(log, "Shutting down database catalog"); LOG_TRACE(log, "Shutting down database catalog");
DatabaseCatalog::shutdown(); DatabaseCatalog::shutdown();
delete_async_insert_queue.reset();
SHUTDOWN(log, "merges executor", merge_mutate_executor, wait()); SHUTDOWN(log, "merges executor", merge_mutate_executor, wait());
SHUTDOWN(log, "fetches executor", fetch_executor, wait()); SHUTDOWN(log, "fetches executor", fetch_executor, wait());
SHUTDOWN(log, "moves executor", moves_executor, wait()); SHUTDOWN(log, "moves executor", moves_executor, wait());
@ -4990,8 +4998,9 @@ PartUUIDsPtr Context::getIgnoredPartUUIDs() const
return ignored_part_uuids; return ignored_part_uuids;
} }
AsynchronousInsertQueue * Context::getAsynchronousInsertQueue() const AsynchronousInsertQueue * Context::tryGetAsynchronousInsertQueue() const
{ {
SharedLockGuard lock(shared->mutex);
return shared->async_insert_queue.get(); return shared->async_insert_queue.get();
} }
@ -4999,6 +5008,8 @@ void Context::setAsynchronousInsertQueue(const std::shared_ptr<AsynchronousInser
{ {
AsynchronousInsertQueue::validateSettings(settings, getLogger("Context")); AsynchronousInsertQueue::validateSettings(settings, getLogger("Context"));
SharedLockGuard lock(shared->mutex);
if (std::chrono::milliseconds(settings.async_insert_poll_timeout_ms) == std::chrono::milliseconds::zero()) if (std::chrono::milliseconds(settings.async_insert_poll_timeout_ms) == std::chrono::milliseconds::zero())
throw Exception(ErrorCodes::INVALID_SETTING_VALUE, "Setting async_insert_poll_timeout_ms can't be zero"); throw Exception(ErrorCodes::INVALID_SETTING_VALUE, "Setting async_insert_poll_timeout_ms can't be zero");

View File

@ -1209,7 +1209,7 @@ public:
PartUUIDsPtr getPartUUIDs() const; PartUUIDsPtr getPartUUIDs() const;
PartUUIDsPtr getIgnoredPartUUIDs() const; PartUUIDsPtr getIgnoredPartUUIDs() const;
AsynchronousInsertQueue * getAsynchronousInsertQueue() const; AsynchronousInsertQueue * tryGetAsynchronousInsertQueue() const;
void setAsynchronousInsertQueue(const std::shared_ptr<AsynchronousInsertQueue> & ptr); void setAsynchronousInsertQueue(const std::shared_ptr<AsynchronousInsertQueue> & ptr);
ReadTaskCallback getReadTaskCallback() const; ReadTaskCallback getReadTaskCallback() const;

View File

@ -219,6 +219,10 @@ InterpreterFactory::InterpreterPtr InterpreterFactory::get(ASTPtr & query, Conte
} }
else if (query->as<ASTExplainQuery>()) else if (query->as<ASTExplainQuery>())
{ {
const auto kind = query->as<ASTExplainQuery>()->getKind();
if (kind == ASTExplainQuery::ParsedAST || kind == ASTExplainQuery::AnalyzedSyntax)
context->setSetting("allow_experimental_analyzer", false);
interpreter_name = "InterpreterExplainQuery"; interpreter_name = "InterpreterExplainQuery";
} }
else if (query->as<ASTShowProcesslistQuery>()) else if (query->as<ASTShowProcesslistQuery>())

View File

@ -719,7 +719,7 @@ BlockIO InterpreterSystemQuery::execute()
case Type::FLUSH_ASYNC_INSERT_QUEUE: case Type::FLUSH_ASYNC_INSERT_QUEUE:
{ {
getContext()->checkAccess(AccessType::SYSTEM_FLUSH_ASYNC_INSERT_QUEUE); getContext()->checkAccess(AccessType::SYSTEM_FLUSH_ASYNC_INSERT_QUEUE);
auto * queue = getContext()->getAsynchronousInsertQueue(); auto * queue = getContext()->tryGetAsynchronousInsertQueue();
if (!queue) if (!queue)
throw Exception(ErrorCodes::BAD_ARGUMENTS, throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Cannot flush asynchronous insert queue because it is not initialized"); "Cannot flush asynchronous insert queue because it is not initialized");

View File

@ -83,25 +83,31 @@ ProcessList::insert(const String & query_, const IAST * ast, ContextMutablePtr q
IAST::QueryKind query_kind = ast->getQueryKind(); IAST::QueryKind query_kind = ast->getQueryKind();
const auto queue_max_wait_ms = settings.queue_max_wait_ms.totalMilliseconds(); const auto queue_max_wait_ms = settings.queue_max_wait_ms.totalMilliseconds();
if (!is_unlimited_query && max_size && processes.size() >= max_size) UInt64 waiting_queries = waiting_queries_amount.load();
if (!is_unlimited_query && max_size && processes.size() >= max_size + waiting_queries)
{ {
if (queue_max_wait_ms) if (queue_max_wait_ms)
LOG_WARNING(getLogger("ProcessList"), "Too many simultaneous queries, will wait {} ms.", queue_max_wait_ms); LOG_WARNING(getLogger("ProcessList"), "Too many simultaneous queries, will wait {} ms.", queue_max_wait_ms);
if (!queue_max_wait_ms || !have_space.wait_for(lock, std::chrono::milliseconds(queue_max_wait_ms), [&]{ return processes.size() < max_size; })) if (!queue_max_wait_ms || !have_space.wait_for(lock, std::chrono::milliseconds(queue_max_wait_ms),
throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES, "Too many simultaneous queries. Maximum: {}", max_size); [&]{ waiting_queries = waiting_queries_amount.load(); return processes.size() < max_size + waiting_queries; }))
throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES,
"Too many simultaneous queries. Maximum: {}{}",
max_size, waiting_queries == 0 ? "" : fmt::format(", waiting: {}", waiting_queries));
} }
if (!is_unlimited_query) if (!is_unlimited_query)
{ {
QueryAmount amount = getQueryKindAmount(query_kind); QueryAmount amount = getQueryKindAmount(query_kind);
if (max_insert_queries_amount && query_kind == IAST::QueryKind::Insert && amount >= max_insert_queries_amount) UInt64 waiting_inserts = waiting_insert_queries_amount.load();
UInt64 waiting_selects = waiting_select_queries_amount.load();
if (max_insert_queries_amount && query_kind == IAST::QueryKind::Insert && amount >= max_insert_queries_amount + waiting_inserts)
throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES, throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES,
"Too many simultaneous insert queries. Maximum: {}, current: {}", "Too many simultaneous insert queries. Maximum: {}, current: {}{}",
max_insert_queries_amount, amount); max_insert_queries_amount, amount, waiting_inserts == 0 ? "" : fmt::format(", waiting: {}", waiting_inserts));
if (max_select_queries_amount && query_kind == IAST::QueryKind::Select && amount >= max_select_queries_amount) if (max_select_queries_amount && query_kind == IAST::QueryKind::Select && amount >= max_select_queries_amount + waiting_selects)
throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES, throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES,
"Too many simultaneous select queries. Maximum: {}, current: {}", "Too many simultaneous select queries. Maximum: {}, current: {}{}",
max_select_queries_amount, amount); max_select_queries_amount, amount, waiting_selects == 0 ? "" : fmt::format(", waiting: {}", waiting_selects));
} }
{ {
@ -124,10 +130,12 @@ ProcessList::insert(const String & query_, const IAST * ast, ContextMutablePtr q
* once is already processing 50+ concurrent queries (including analysts or any other users). * once is already processing 50+ concurrent queries (including analysts or any other users).
*/ */
waiting_queries = waiting_queries_amount.load();
if (!is_unlimited_query && settings.max_concurrent_queries_for_all_users if (!is_unlimited_query && settings.max_concurrent_queries_for_all_users
&& processes.size() >= settings.max_concurrent_queries_for_all_users) && processes.size() >= settings.max_concurrent_queries_for_all_users + waiting_queries_amount)
throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES, "Too many simultaneous queries for all users. " throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES, "Too many simultaneous queries for all users. "
"Current: {}, maximum: {}", processes.size(), settings.max_concurrent_queries_for_all_users.toString()); "Current: {}, maximum: {}{}", processes.size(), settings.max_concurrent_queries_for_all_users.toString(),
waiting_queries == 0 ? "" : fmt::format(", waiting: {}", waiting_queries));
} }
/** Why we use current user? /** Why we use current user?
@ -145,13 +153,15 @@ ProcessList::insert(const String & query_, const IAST * ast, ContextMutablePtr q
if (user_process_list != user_to_queries.end()) if (user_process_list != user_to_queries.end())
{ {
UInt64 user_waiting_queries = user_process_list->second.waiting_queries_amount.load();
if (!is_unlimited_query && settings.max_concurrent_queries_for_user if (!is_unlimited_query && settings.max_concurrent_queries_for_user
&& user_process_list->second.queries.size() >= settings.max_concurrent_queries_for_user) && user_process_list->second.queries.size() >= settings.max_concurrent_queries_for_user + user_waiting_queries)
throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES, throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES,
"Too many simultaneous queries for user {}. " "Too many simultaneous queries for user {}. "
"Current: {}, maximum: {}", "Current: {}, maximum: {}{}",
client_info.current_user, user_process_list->second.queries.size(), client_info.current_user, user_process_list->second.queries.size(),
settings.max_concurrent_queries_for_user.toString()); settings.max_concurrent_queries_for_user.toString(),
user_waiting_queries == 0 ? "" : fmt::format(", waiting: {}", user_waiting_queries));
auto running_query = user_process_list->second.queries.find(client_info.current_query_id); auto running_query = user_process_list->second.queries.find(client_info.current_query_id);
@ -757,4 +767,69 @@ ProcessList::QueryAmount ProcessList::getQueryKindAmount(const IAST::QueryKind &
return found->second; return found->second;
} }
void ProcessList::increaseWaitingQueryAmount(const QueryStatusPtr & status)
{
UInt64 limit = max_waiting_queries_amount.load();
UInt64 value = waiting_queries_amount.load();
while (true)
{
if (value >= limit)
throw Exception(ErrorCodes::TOO_MANY_SIMULTANEOUS_QUERIES,
"Too many simultaneous waiting queries. Maximum: {}, waiting: {}",
limit, value);
if (waiting_queries_amount.compare_exchange_strong(value, value + 1))
break;
}
// WARNING: After this point we should not throw, otherwise corresponding `decreaseWaitingQueryAmount` will not be called.
// Update query kind counters
if (status->query_kind == IAST::QueryKind::Insert)
waiting_insert_queries_amount.fetch_add(1);
if (status->query_kind == IAST::QueryKind::Select)
waiting_select_queries_amount.fetch_add(1);
// Update per-user counter
status->getUserProcessList()->waiting_queries_amount.fetch_add(1);
// We have to notify because some queries might be waiting on `have_space`
// and this query leaves its space by transitioning to waiting state
have_space.notify_all();
}
void ProcessList::decreaseWaitingQueryAmount(const QueryStatusPtr & status)
{
if (status->getUserProcessList()->waiting_queries_amount.fetch_sub(1) == 0)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong insert waiting query amount for user: decrease to negative");
if (status->query_kind == IAST::QueryKind::Insert && waiting_insert_queries_amount.fetch_sub(1) == 0)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong insert waiting query amount: decrease to negative");
if (status->query_kind == IAST::QueryKind::Select && waiting_select_queries_amount.fetch_sub(1) == 0)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong select waiting query amount: decrease to negative");
if (waiting_queries_amount.fetch_sub(1) == 0)
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong waiting query amount: decrease to negative");
}
void ProcessList::incrementWaiters()
{
ContextPtr context = CurrentThread::getQueryContext();
QueryStatusPtr status = context->getProcessListElement();
// Query became "waiting" with the first thread that waits
if (status->waiting_threads.fetch_add(1) == 0)
increaseWaitingQueryAmount(status);
}
void ProcessList::decrementWaiters()
{
ContextPtr context = CurrentThread::getQueryContext();
QueryStatusPtr status = context->getProcessListElement();
// Query became "non-waiting" with the last thread that no longer waits
if (status->waiting_threads.fetch_sub(1) == 1)
decreaseWaitingQueryAmount(status);
}
} }

View File

@ -42,10 +42,6 @@ class ThreadStatus;
class ProcessListEntry; class ProcessListEntry;
/** List of currently executing queries.
* Also implements limit on their number.
*/
/** Information of process list element. /** Information of process list element.
* To output in SHOW PROCESSLIST query. Does not contain any complex objects, that do something on copy or destructor. * To output in SHOW PROCESSLIST query. Does not contain any complex objects, that do something on copy or destructor.
*/ */
@ -114,8 +110,13 @@ protected:
/// Including EndOfStream or Exception. /// Including EndOfStream or Exception.
std::atomic<bool> is_all_data_sent { false }; std::atomic<bool> is_all_data_sent { false };
/// Number of threads for the query that are waiting for load jobs
std::atomic<UInt64> waiting_threads{0};
/// For initialization of ProcessListForUser during process insertion.
void setUserProcessList(ProcessListForUser * user_process_list_); void setUserProcessList(ProcessListForUser * user_process_list_);
/// Be careful using it. For example, queries field of ProcessListForUser could be modified concurrently. /// Be careful using it. For example, queries field of ProcessListForUser could be modified concurrently.
ProcessListForUser * getUserProcessList() { return user_process_list; }
const ProcessListForUser * getUserProcessList() const { return user_process_list; } const ProcessListForUser * getUserProcessList() const { return user_process_list; }
/// Sets an entry in the ProcessList associated with this QueryStatus. /// Sets an entry in the ProcessList associated with this QueryStatus.
@ -283,6 +284,9 @@ struct ProcessListForUser
/// Count network usage for all simultaneously running queries of single user. /// Count network usage for all simultaneously running queries of single user.
ThrottlerPtr user_throttler; ThrottlerPtr user_throttler;
/// Number of queries waiting on load jobs
std::atomic<UInt64> waiting_queries_amount{0};
ProcessListForUserInfo getInfo(bool get_profile_events = false) const; ProcessListForUserInfo getInfo(bool get_profile_events = false) const;
/// Clears MemoryTracker for the user. /// Clears MemoryTracker for the user.
@ -341,6 +345,9 @@ protected:
}; };
/** List of currently executing queries.
* Also implements limit on their number.
*/
class ProcessList : public ProcessListBase class ProcessList : public ProcessListBase
{ {
public: public:
@ -399,10 +406,21 @@ protected:
/// amount of queries by query kind. /// amount of queries by query kind.
QueryKindAmounts query_kind_amounts; QueryKindAmounts query_kind_amounts;
/// limit for waiting queries. 0 means no limit. Otherwise, when limit exceeded, an exception is thrown.
std::atomic<UInt64> max_waiting_queries_amount{0};
/// amounts of waiting queries
std::atomic<UInt64> waiting_queries_amount{0};
std::atomic<UInt64> waiting_insert_queries_amount{0};
std::atomic<UInt64> waiting_select_queries_amount{0};
void increaseQueryKindAmount(const IAST::QueryKind & query_kind); void increaseQueryKindAmount(const IAST::QueryKind & query_kind);
void decreaseQueryKindAmount(const IAST::QueryKind & query_kind); void decreaseQueryKindAmount(const IAST::QueryKind & query_kind);
QueryAmount getQueryKindAmount(const IAST::QueryKind & query_kind) const; QueryAmount getQueryKindAmount(const IAST::QueryKind & query_kind) const;
void increaseWaitingQueryAmount(const QueryStatusPtr & status);
void decreaseWaitingQueryAmount(const QueryStatusPtr & status);
public: public:
using EntryPtr = std::shared_ptr<ProcessListEntry>; using EntryPtr = std::shared_ptr<ProcessListEntry>;
@ -458,6 +476,21 @@ public:
return max_select_queries_amount; return max_select_queries_amount;
} }
void setMaxWaitingQueriesAmount(UInt64 max_waiting_queries_amount_)
{
max_waiting_queries_amount.store(max_waiting_queries_amount_);
// NOTE: We cannot cancel waiting queries when limit is lowered. They have to wait anyways, but new queries will be canceled instead of waiting.
}
size_t getMaxWaitingQueriesAmount() const
{
return max_waiting_queries_amount.load();
}
// Handlers for AsyncLoader waiters
void incrementWaiters();
void decrementWaiters();
/// Try call cancel() for input and output streams of query with specified id and user /// Try call cancel() for input and output streams of query with specified id and user
CancellationCode sendCancelToQuery(const String & current_query_id, const String & current_user, bool kill = false); CancellationCode sendCancelToQuery(const String & current_query_id, const String & current_user, bool kill = false);
CancellationCode sendCancelToQuery(QueryStatusPtr elem, bool kill = false); CancellationCode sendCancelToQuery(QueryStatusPtr elem, bool kill = false);

View File

@ -996,7 +996,7 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
std::unique_ptr<IInterpreter> interpreter; std::unique_ptr<IInterpreter> interpreter;
bool async_insert = false; bool async_insert = false;
auto * queue = context->getAsynchronousInsertQueue(); auto * queue = context->tryGetAsynchronousInsertQueue();
auto logger = getLogger("executeQuery"); auto logger = getLogger("executeQuery");
if (insert_query && async_insert_enabled) if (insert_query && async_insert_enabled)

View File

@ -1,5 +1,7 @@
#include <Planner/PlannerExpressionAnalysis.h> #include <Planner/PlannerExpressionAnalysis.h>
#include <Columns/ColumnNullable.h>
#include <DataTypes/DataTypesNumber.h> #include <DataTypes/DataTypesNumber.h>
#include <DataTypes/DataTypeNullable.h> #include <DataTypes/DataTypeNullable.h>
@ -111,7 +113,8 @@ std::optional<AggregationAnalysisResult> analyzeAggregation(const QueryTreeNodeP
continue; continue;
auto expression_type_after_aggregation = group_by_use_nulls ? makeNullableSafe(expression_dag_node->result_type) : expression_dag_node->result_type; auto expression_type_after_aggregation = group_by_use_nulls ? makeNullableSafe(expression_dag_node->result_type) : expression_dag_node->result_type;
available_columns_after_aggregation.emplace_back(expression_dag_node->column, expression_type_after_aggregation, expression_dag_node->result_name); auto column_after_aggregation = group_by_use_nulls && expression_dag_node->column != nullptr ? makeNullableSafe(expression_dag_node->column) : expression_dag_node->column;
available_columns_after_aggregation.emplace_back(std::move(column_after_aggregation), expression_type_after_aggregation, expression_dag_node->result_name);
aggregation_keys.push_back(expression_dag_node->result_name); aggregation_keys.push_back(expression_dag_node->result_name);
before_aggregation_actions->getOutputs().push_back(expression_dag_node); before_aggregation_actions->getOutputs().push_back(expression_dag_node);
before_aggregation_actions_output_node_names.insert(expression_dag_node->result_name); before_aggregation_actions_output_node_names.insert(expression_dag_node->result_name);
@ -161,7 +164,8 @@ std::optional<AggregationAnalysisResult> analyzeAggregation(const QueryTreeNodeP
continue; continue;
auto expression_type_after_aggregation = group_by_use_nulls ? makeNullableSafe(expression_dag_node->result_type) : expression_dag_node->result_type; auto expression_type_after_aggregation = group_by_use_nulls ? makeNullableSafe(expression_dag_node->result_type) : expression_dag_node->result_type;
available_columns_after_aggregation.emplace_back(expression_dag_node->column, expression_type_after_aggregation, expression_dag_node->result_name); auto column_after_aggregation = group_by_use_nulls && expression_dag_node->column != nullptr ? makeNullableSafe(expression_dag_node->column) : expression_dag_node->column;
available_columns_after_aggregation.emplace_back(std::move(column_after_aggregation), expression_type_after_aggregation, expression_dag_node->result_name);
aggregation_keys.push_back(expression_dag_node->result_name); aggregation_keys.push_back(expression_dag_node->result_name);
before_aggregation_actions->getOutputs().push_back(expression_dag_node); before_aggregation_actions->getOutputs().push_back(expression_dag_node);
before_aggregation_actions_output_node_names.insert(expression_dag_node->result_name); before_aggregation_actions_output_node_names.insert(expression_dag_node->result_name);

View File

@ -325,11 +325,21 @@ void DWARFBlockInputFormat::skipAttribute(
const llvm::DWARFAbbreviationDeclaration::AttributeSpec & attr, uint64_t * offset, const llvm::DWARFAbbreviationDeclaration::AttributeSpec & attr, uint64_t * offset,
const UnitState & unit) const const UnitState & unit) const
{ {
if (!llvm::DWARFFormValue::skipValue( if (attr.Form == llvm::dwarf::DW_FORM_strx3)
attr.Form, *extractor, offset, unit.dwarf_unit->getFormParams())) {
throw Exception(ErrorCodes::CANNOT_PARSE_DWARF, /// DWARFFormValue::skipValue() fails on DW_FORM_strx3 because the `switch` statement is
"Failed to skip attribute {} of form {} at offset {}", /// missing this form for some reason. Maybe it's a bug in llvm.
llvm::dwarf::AttributeString(attr.Attr), attr.Form, *offset); /// Use extractValue() to work around.
parseAttribute(attr, offset, unit);
}
else
{
if (!llvm::DWARFFormValue::skipValue(
attr.Form, *extractor, offset, unit.dwarf_unit->getFormParams()))
throw Exception(ErrorCodes::CANNOT_PARSE_DWARF,
"Failed to skip attribute {} of form {} at offset {}",
llvm::dwarf::AttributeString(attr.Attr), attr.Form, *offset);
}
} }
uint64_t DWARFBlockInputFormat::parseAddress(llvm::dwarf::Attribute attr, const llvm::DWARFFormValue & val, const UnitState & unit) uint64_t DWARFBlockInputFormat::parseAddress(llvm::dwarf::Attribute attr, const llvm::DWARFFormValue & val, const UnitState & unit)

View File

@ -34,6 +34,12 @@ std::string toString(const Values & value)
return fmt::format("({})", fmt::join(value, ", ")); return fmt::format("({})", fmt::join(value, ", "));
} }
/** We rely that FieldVisitorAccurateLess will have strict weak ordering for any Field values including
* NaN, Null and containers (Array, Tuple, Map) that contain NaN or Null. But right now it does not properly
* support NaN and Nulls inside containers, because it uses Field operator< or accurate::lessOp for comparison
* that compares Nulls and NaNs differently than FieldVisitorAccurateLess.
* TODO: Update Field operator< to compare NaNs and Nulls the same way as FieldVisitorAccurateLess.
*/
bool isSafePrimaryDataKeyType(const IDataType & data_type) bool isSafePrimaryDataKeyType(const IDataType & data_type)
{ {
auto type_id = data_type.getTypeId(); auto type_id = data_type.getTypeId();
@ -316,12 +322,12 @@ struct PartRangeIndex
bool operator==(const PartRangeIndex & other) const bool operator==(const PartRangeIndex & other) const
{ {
return part_index == other.part_index && range.begin == other.range.begin && range.end == other.range.end; return std::tie(part_index, range.begin, range.end) == std::tie(other.part_index, other.range.begin, other.range.end);
} }
bool operator<(const PartRangeIndex & other) const bool operator<(const PartRangeIndex & other) const
{ {
return part_index < other.part_index && range.begin < other.range.begin && range.end < other.range.end; return std::tie(part_index, range.begin, range.end) < std::tie(other.part_index, other.range.begin, other.range.end);
} }
size_t part_index; size_t part_index;
@ -786,7 +792,7 @@ ASTs buildFilters(const KeyDescription & primary_key, const std::vector<Values>
const auto & type = primary_key.data_types.at(i); const auto & type = primary_key.data_types.at(i);
// PK may contain functions of the table columns, so we need the actual PK AST with all expressions it contains. // PK may contain functions of the table columns, so we need the actual PK AST with all expressions it contains.
auto pk_ast = primary_key.expression_list_ast->children.at(i); auto pk_ast = primary_key.expression_list_ast->children.at(i)->clone();
// If PK is nullable, prepend a null mask column for > comparison. // If PK is nullable, prepend a null mask column for > comparison.
// Also transform the AST into assumeNotNull(pk) so that the result type is not-nullable. // Also transform the AST into assumeNotNull(pk) so that the result type is not-nullable.

View File

@ -766,6 +766,82 @@ Pipe ReadFromMergeTree::spreadMarkRangesAmongStreams(RangesInDataParts && parts_
auto read_type = is_parallel_reading_from_replicas ? ReadType::ParallelReplicas : ReadType::Default; auto read_type = is_parallel_reading_from_replicas ? ReadType::ParallelReplicas : ReadType::Default;
double read_split_ranges_into_intersecting_and_non_intersecting_injection_probability = settings.merge_tree_read_split_ranges_into_intersecting_and_non_intersecting_injection_probability;
std::bernoulli_distribution fault(read_split_ranges_into_intersecting_and_non_intersecting_injection_probability);
if (read_type != ReadType::ParallelReplicas &&
num_streams > 1 &&
read_split_ranges_into_intersecting_and_non_intersecting_injection_probability > 0.0 &&
fault(thread_local_rng) &&
!isQueryWithFinal() &&
data.merging_params.is_deleted_column.empty() &&
!prewhere_info)
{
NameSet column_names_set(column_names.begin(), column_names.end());
Names in_order_column_names_to_read(column_names);
/// Add columns needed to calculate the sorting expression
for (const auto & column_name : metadata_for_reading->getColumnsRequiredForSortingKey())
{
if (column_names_set.contains(column_name))
continue;
in_order_column_names_to_read.push_back(column_name);
column_names_set.insert(column_name);
}
auto in_order_reading_step_getter = [this, &in_order_column_names_to_read, &info](auto parts)
{
return this->read(
std::move(parts),
in_order_column_names_to_read,
ReadType::InOrder,
1 /* num_streams */,
0 /* min_marks_for_concurrent_read */,
info.use_uncompressed_cache);
};
auto sorting_expr = std::make_shared<ExpressionActions>(metadata_for_reading->getSortingKey().expression->getActionsDAG().clone());
SplitPartsWithRangesByPrimaryKeyResult split_ranges_result = splitPartsWithRangesByPrimaryKey(
metadata_for_reading->getPrimaryKey(),
std::move(sorting_expr),
std::move(parts_with_ranges),
num_streams,
context,
std::move(in_order_reading_step_getter),
true /*split_parts_ranges_into_intersecting_and_non_intersecting_final*/,
true /*split_intersecting_parts_ranges_into_layers*/);
auto merging_pipes = std::move(split_ranges_result.merging_pipes);
auto non_intersecting_parts_ranges_read_pipe = read(std::move(split_ranges_result.non_intersecting_parts_ranges),
column_names,
read_type,
num_streams,
info.min_marks_for_concurrent_read,
info.use_uncompressed_cache);
if (merging_pipes.empty())
return non_intersecting_parts_ranges_read_pipe;
Pipes pipes;
pipes.resize(2);
pipes[0] = Pipe::unitePipes(std::move(merging_pipes));
pipes[1] = std::move(non_intersecting_parts_ranges_read_pipe);
auto conversion_action = ActionsDAG::makeConvertingActions(
pipes[0].getHeader().getColumnsWithTypeAndName(),
pipes[1].getHeader().getColumnsWithTypeAndName(),
ActionsDAG::MatchColumnsMode::Name);
pipes[0].addSimpleTransform(
[conversion_action](const Block & header)
{
auto converting_expr = std::make_shared<ExpressionActions>(conversion_action);
return std::make_shared<ExpressionTransform>(header, converting_expr);
});
return Pipe::unitePipes(std::move(pipes));
}
return read(std::move(parts_with_ranges), return read(std::move(parts_with_ranges),
column_names, column_names,
read_type, read_type,

View File

@ -923,7 +923,7 @@ void TCPHandler::processInsertQuery()
Block processed_block; Block processed_block;
const auto & settings = query_context->getSettingsRef(); const auto & settings = query_context->getSettingsRef();
auto * insert_queue = query_context->getAsynchronousInsertQueue(); auto * insert_queue = query_context->tryGetAsynchronousInsertQueue();
const auto & insert_query = assert_cast<const ASTInsertQuery &>(*state.parsed_query); const auto & insert_query = assert_cast<const ASTInsertQuery &>(*state.parsed_query);
bool async_insert_enabled = settings.async_insert; bool async_insert_enabled = settings.async_insert;

View File

@ -599,24 +599,14 @@ void KafkaConsumer::setExceptionInfo(const std::string & text, bool with_stacktr
exceptions_buffer.push_back({enriched_text, static_cast<UInt64>(Poco::Timestamp().epochTime())}); exceptions_buffer.push_back({enriched_text, static_cast<UInt64>(Poco::Timestamp().epochTime())});
} }
/*
* Needed until
* https://github.com/mfontanini/cppkafka/pull/309
* is merged,
* because consumer->get_member_id() contains a leak
*/
std::string KafkaConsumer::getMemberId() const std::string KafkaConsumer::getMemberId() const
{ {
if (!consumer) if (!consumer)
return ""; return "";
char * memberid_ptr = rd_kafka_memberid(consumer->get_handle()); return consumer->get_member_id();
std::string memberid_string = memberid_ptr;
rd_kafka_mem_free(nullptr, memberid_ptr);
return memberid_string;
} }
KafkaConsumer::Stat KafkaConsumer::getStat() const KafkaConsumer::Stat KafkaConsumer::getStat() const
{ {
KafkaConsumer::Stat::Assignments assignments; KafkaConsumer::Stat::Assignments assignments;

View File

@ -1,10 +1,8 @@
#include "MergeTreeDataPartCompact.h" #include "MergeTreeDataPartCompact.h"
#include <DataTypes/NestedUtils.h> #include <DataTypes/NestedUtils.h>
#include <Storages/MergeTree/MergeTreeReaderCompact.h> #include <Storages/MergeTree/MergeTreeReaderCompactSingleBuffer.h>
#include <Storages/MergeTree/MergeTreeDataPartWriterCompact.h> #include <Storages/MergeTree/MergeTreeDataPartWriterCompact.h>
#include <Interpreters/Context.h>
#include <Storages/MergeTree/LoadedMergeTreeDataPartInfoForReader.h> #include <Storages/MergeTree/LoadedMergeTreeDataPartInfoForReader.h>
#include <Compression/CompressedReadBufferFromFile.h>
namespace DB namespace DB
@ -41,21 +39,12 @@ IMergeTreeDataPart::MergeTreeReaderPtr MergeTreeDataPartCompact::getReader(
const ReadBufferFromFileBase::ProfileCallback & profile_callback) const const ReadBufferFromFileBase::ProfileCallback & profile_callback) const
{ {
auto read_info = std::make_shared<LoadedMergeTreeDataPartInfoForReader>(shared_from_this(), alter_conversions); auto read_info = std::make_shared<LoadedMergeTreeDataPartInfoForReader>(shared_from_this(), alter_conversions);
auto * load_marks_threadpool
= reader_settings.read_settings.load_marks_asynchronously ? &read_info->getContext()->getLoadMarksThreadpool() : nullptr;
return std::make_unique<MergeTreeReaderCompact>( return std::make_unique<MergeTreeReaderCompactSingleBuffer>(
read_info, read_info, columns_to_read, virtual_fields,
columns_to_read, storage_snapshot, uncompressed_cache,
virtual_fields, mark_cache, mark_ranges, reader_settings,
storage_snapshot, avg_value_size_hints, profile_callback, CLOCK_MONOTONIC_COARSE);
uncompressed_cache,
mark_cache,
mark_ranges,
reader_settings,
load_marks_threadpool,
avg_value_size_hints,
profile_callback);
} }
IMergeTreeDataPart::MergeTreeWriterPtr MergeTreeDataPartCompact::getWriter( IMergeTreeDataPart::MergeTreeWriterPtr MergeTreeDataPartCompact::getWriter(

View File

@ -13,6 +13,11 @@ namespace DB
class MMappedFileCache; class MMappedFileCache;
using MMappedFileCachePtr = std::shared_ptr<MMappedFileCache>; using MMappedFileCachePtr = std::shared_ptr<MMappedFileCache>;
enum class CompactPartsReadMethod
{
SingleBuffer,
MultiBuffer,
};
struct MergeTreeReaderSettings struct MergeTreeReaderSettings
{ {
@ -25,12 +30,20 @@ struct MergeTreeReaderSettings
bool checksum_on_read = true; bool checksum_on_read = true;
/// True if we read in order of sorting key. /// True if we read in order of sorting key.
bool read_in_order = false; bool read_in_order = false;
/// Use one buffer for each column or for all columns while reading from compact.
CompactPartsReadMethod compact_parts_read_method = CompactPartsReadMethod::SingleBuffer;
/// True if we read stream for dictionary of LowCardinality type.
bool is_low_cardinality_dictionary = false;
/// True if data may be compressed by different codecs in one stream.
bool allow_different_codecs = false;
/// Deleted mask is applied to all reads except internal select from mutate some part columns. /// Deleted mask is applied to all reads except internal select from mutate some part columns.
bool apply_deleted_mask = true; bool apply_deleted_mask = true;
/// Put reading task in a common I/O pool, return Async state on prepare() /// Put reading task in a common I/O pool, return Async state on prepare()
bool use_asynchronous_read_from_pool = false; bool use_asynchronous_read_from_pool = false;
/// If PREWHERE has multiple conditions combined with AND, execute them in separate read/filtering steps. /// If PREWHERE has multiple conditions combined with AND, execute them in separate read/filtering steps.
bool enable_multiple_prewhere_read_steps = false; bool enable_multiple_prewhere_read_steps = false;
/// If true, try to lower size of read buffer according to granule size and compressed block size.
bool adjust_read_buffer_size = true;
}; };
struct MergeTreeWriterSettings struct MergeTreeWriterSettings

View File

@ -20,14 +20,23 @@ std::unique_ptr<MergeTreeReaderStream> makeIndexReader(
auto context = part->storage.getContext(); auto context = part->storage.getContext();
auto * load_marks_threadpool = settings.read_settings.load_marks_asynchronously ? &context->getLoadMarksThreadpool() : nullptr; auto * load_marks_threadpool = settings.read_settings.load_marks_asynchronously ? &context->getLoadMarksThreadpool() : nullptr;
return std::make_unique<MergeTreeReaderStream>( auto marks_loader = std::make_shared<MergeTreeMarksLoader>(
std::make_shared<LoadedMergeTreeDataPartInfoForReader>(part, std::make_shared<AlterConversions>()), std::make_shared<LoadedMergeTreeDataPartInfoForReader>(part, std::make_shared<AlterConversions>()),
mark_cache,
part->index_granularity_info.getMarksFilePath(index->getFileName()),
marks_count,
part->index_granularity_info,
settings.save_marks_in_cache,
settings.read_settings,
load_marks_threadpool,
/*num_columns_in_mark=*/ 1);
return std::make_unique<MergeTreeReaderStreamSingleColumn>(
part->getDataPartStoragePtr(),
index->getFileName(), extension, marks_count, index->getFileName(), extension, marks_count,
all_mark_ranges, all_mark_ranges, std::move(settings), uncompressed_cache,
std::move(settings), mark_cache, uncompressed_cache, part->getFileSizeOrZero(index->getFileName() + extension), std::move(marks_loader),
part->getFileSizeOrZero(index->getFileName() + extension), ReadBufferFromFileBase::ProfileCallback{}, CLOCK_MONOTONIC_COARSE);
&part->index_granularity_info,
ReadBufferFromFileBase::ProfileCallback{}, CLOCK_MONOTONIC_COARSE, false, load_marks_threadpool);
} }
} }

View File

@ -28,6 +28,23 @@ namespace ErrorCodes
extern const int LOGICAL_ERROR; extern const int LOGICAL_ERROR;
} }
MergeTreeMarksGetter::MergeTreeMarksGetter(MarkCache::MappedPtr marks_, size_t num_columns_in_mark_)
: marks(std::move(marks_)), num_columns_in_mark(num_columns_in_mark_)
{
assert(marks);
}
MarkInCompressedFile MergeTreeMarksGetter::getMark(size_t row_index, size_t column_index) const
{
#ifndef NDEBUG
if (column_index >= num_columns_in_mark)
throw Exception(ErrorCodes::LOGICAL_ERROR,
"Column index: {} is out of range [0, {})", column_index, num_columns_in_mark);
#endif
return marks->get(row_index * num_columns_in_mark + column_index);
}
MergeTreeMarksLoader::MergeTreeMarksLoader( MergeTreeMarksLoader::MergeTreeMarksLoader(
MergeTreeDataPartInfoForReaderPtr data_part_reader_, MergeTreeDataPartInfoForReaderPtr data_part_reader_,
MarkCache * mark_cache_, MarkCache * mark_cache_,
@ -37,58 +54,49 @@ MergeTreeMarksLoader::MergeTreeMarksLoader(
bool save_marks_in_cache_, bool save_marks_in_cache_,
const ReadSettings & read_settings_, const ReadSettings & read_settings_,
ThreadPool * load_marks_threadpool_, ThreadPool * load_marks_threadpool_,
size_t columns_in_mark_) size_t num_columns_in_mark_)
: data_part_reader(data_part_reader_) : data_part_reader(data_part_reader_)
, mark_cache(mark_cache_) , mark_cache(mark_cache_)
, mrk_path(mrk_path_) , mrk_path(mrk_path_)
, marks_count(marks_count_) , marks_count(marks_count_)
, index_granularity_info(index_granularity_info_) , index_granularity_info(index_granularity_info_)
, save_marks_in_cache(save_marks_in_cache_) , save_marks_in_cache(save_marks_in_cache_)
, columns_in_mark(columns_in_mark_)
, read_settings(read_settings_) , read_settings(read_settings_)
, num_columns_in_mark(num_columns_in_mark_)
, load_marks_threadpool(load_marks_threadpool_) , load_marks_threadpool(load_marks_threadpool_)
{ {
if (load_marks_threadpool) if (load_marks_threadpool)
{
future = loadMarksAsync(); future = loadMarksAsync();
}
} }
MergeTreeMarksLoader::~MergeTreeMarksLoader() MergeTreeMarksLoader::~MergeTreeMarksLoader()
{ {
if (future.valid()) if (future.valid())
{
future.wait(); future.wait();
}
} }
MergeTreeMarksGetterPtr MergeTreeMarksLoader::loadMarks()
MarkInCompressedFile MergeTreeMarksLoader::getMark(size_t row_index, size_t column_index)
{ {
if (!marks) std::lock_guard lock(load_mutex);
if (marks)
return std::make_unique<MergeTreeMarksGetter>(marks, num_columns_in_mark);
Stopwatch watch(CLOCK_MONOTONIC);
if (future.valid())
{ {
Stopwatch watch(CLOCK_MONOTONIC); marks = future.get();
future = {};
if (future.valid()) }
{ else
marks = future.get(); {
future = {}; marks = loadMarksSync();
}
else
{
marks = loadMarks();
}
watch.stop();
ProfileEvents::increment(ProfileEvents::WaitMarksLoadMicroseconds, watch.elapsedMicroseconds());
} }
#ifndef NDEBUG watch.stop();
if (column_index >= columns_in_mark) ProfileEvents::increment(ProfileEvents::WaitMarksLoadMicroseconds, watch.elapsedMicroseconds());
throw Exception(ErrorCodes::LOGICAL_ERROR, "Column index: {} is out of range [0, {})", column_index, columns_in_mark); return std::make_unique<MergeTreeMarksGetter>(marks, num_columns_in_mark);
#endif
return marks->get(row_index * columns_in_mark + column_index);
} }
@ -100,12 +108,12 @@ MarkCache::MappedPtr MergeTreeMarksLoader::loadMarksImpl()
auto data_part_storage = data_part_reader->getDataPartStorage(); auto data_part_storage = data_part_reader->getDataPartStorage();
size_t file_size = data_part_storage->getFileSize(mrk_path); size_t file_size = data_part_storage->getFileSize(mrk_path);
size_t mark_size = index_granularity_info.getMarkSizeInBytes(columns_in_mark); size_t mark_size = index_granularity_info.getMarkSizeInBytes(num_columns_in_mark);
size_t expected_uncompressed_size = mark_size * marks_count; size_t expected_uncompressed_size = mark_size * marks_count;
// We first read the marks into a temporary simple array, then compress them into a more compact // We first read the marks into a temporary simple array, then compress them into a more compact
// representation. // representation.
PODArray<MarkInCompressedFile> plain_marks(marks_count * columns_in_mark); // temporary PODArray<MarkInCompressedFile> plain_marks(marks_count * num_columns_in_mark); // temporary
auto full_mark_path = std::string(fs::path(data_part_storage->getFullPath()) / mrk_path); auto full_mark_path = std::string(fs::path(data_part_storage->getFullPath()) / mrk_path);
if (file_size == 0 && marks_count != 0) if (file_size == 0 && marks_count != 0)
@ -159,7 +167,7 @@ MarkCache::MappedPtr MergeTreeMarksLoader::loadMarksImpl()
size_t granularity; size_t granularity;
reader->readStrict( reader->readStrict(
reinterpret_cast<char *>(plain_marks.data() + i * columns_in_mark), columns_in_mark * sizeof(MarkInCompressedFile)); reinterpret_cast<char *>(plain_marks.data() + i * num_columns_in_mark), num_columns_in_mark * sizeof(MarkInCompressedFile));
readBinaryLittleEndian(granularity, *reader); readBinaryLittleEndian(granularity, *reader);
} }
@ -182,13 +190,13 @@ MarkCache::MappedPtr MergeTreeMarksLoader::loadMarksImpl()
auto res = std::make_shared<MarksInCompressedFile>(plain_marks); auto res = std::make_shared<MarksInCompressedFile>(plain_marks);
ProfileEvents::increment(ProfileEvents::LoadedMarksCount, marks_count * columns_in_mark); ProfileEvents::increment(ProfileEvents::LoadedMarksCount, marks_count * num_columns_in_mark);
ProfileEvents::increment(ProfileEvents::LoadedMarksMemoryBytes, res->approximateMemoryUsage()); ProfileEvents::increment(ProfileEvents::LoadedMarksMemoryBytes, res->approximateMemoryUsage());
return res; return res;
} }
MarkCache::MappedPtr MergeTreeMarksLoader::loadMarks() MarkCache::MappedPtr MergeTreeMarksLoader::loadMarksSync()
{ {
MarkCache::MappedPtr loaded_marks; MarkCache::MappedPtr loaded_marks;
@ -227,7 +235,7 @@ std::future<MarkCache::MappedPtr> MergeTreeMarksLoader::loadMarksAsync()
[this]() -> MarkCache::MappedPtr [this]() -> MarkCache::MappedPtr
{ {
ProfileEvents::increment(ProfileEvents::BackgroundLoadingMarksTasks); ProfileEvents::increment(ProfileEvents::BackgroundLoadingMarksTasks);
return loadMarks(); return loadMarksSync();
}, },
*load_marks_threadpool, *load_marks_threadpool,
"LoadMarksThread"); "LoadMarksThread");

View File

@ -10,13 +10,33 @@ namespace DB
{ {
struct MergeTreeIndexGranularityInfo; struct MergeTreeIndexGranularityInfo;
using MarksPtr = MarkCache::MappedPtr;
class Threadpool; class Threadpool;
/// Class that helps to get marks by indexes.
/// Always immutable and thread safe.
/// Marks can be shared between several threads
/// that read columns from the same file.
class MergeTreeMarksGetter
{
public:
MergeTreeMarksGetter(MarkCache::MappedPtr marks_, size_t num_columns_in_mark_);
MarkInCompressedFile getMark(size_t row_index, size_t column_index) const;
size_t getNumColumns() const { return num_columns_in_mark; }
private:
const MarkCache::MappedPtr marks;
const size_t num_columns_in_mark;
};
using MergeTreeMarksGetterPtr = std::unique_ptr<const MergeTreeMarksGetter>;
/// Class that helps to load marks on demand.
/// Thread safe, but locks while loading marks.
class MergeTreeMarksLoader class MergeTreeMarksLoader
{ {
public: public:
using MarksPtr = MarkCache::MappedPtr;
MergeTreeMarksLoader( MergeTreeMarksLoader(
MergeTreeDataPartInfoForReaderPtr data_part_reader_, MergeTreeDataPartInfoForReaderPtr data_part_reader_,
MarkCache * mark_cache_, MarkCache * mark_cache_,
@ -26,24 +46,27 @@ public:
bool save_marks_in_cache_, bool save_marks_in_cache_,
const ReadSettings & read_settings_, const ReadSettings & read_settings_,
ThreadPool * load_marks_threadpool_, ThreadPool * load_marks_threadpool_,
size_t columns_in_mark_ = 1); size_t num_columns_in_mark_);
~MergeTreeMarksLoader(); ~MergeTreeMarksLoader();
MarkInCompressedFile getMark(size_t row_index, size_t column_index = 0); MergeTreeMarksGetterPtr loadMarks();
size_t getNumColumns() const { return num_columns_in_mark; }
private: private:
MergeTreeDataPartInfoForReaderPtr data_part_reader; const MergeTreeDataPartInfoForReaderPtr data_part_reader;
MarkCache * mark_cache = nullptr; MarkCache * const mark_cache;
String mrk_path; const String mrk_path;
size_t marks_count; const size_t marks_count;
const MergeTreeIndexGranularityInfo & index_granularity_info; const MergeTreeIndexGranularityInfo & index_granularity_info;
bool save_marks_in_cache = false; const bool save_marks_in_cache;
size_t columns_in_mark; const ReadSettings read_settings;
MarkCache::MappedPtr marks; const size_t num_columns_in_mark;
ReadSettings read_settings;
MarkCache::MappedPtr loadMarks(); std::mutex load_mutex;
MarkCache::MappedPtr marks;
MarkCache::MappedPtr loadMarksSync();
std::future<MarkCache::MappedPtr> loadMarksAsync(); std::future<MarkCache::MappedPtr> loadMarksAsync();
MarkCache::MappedPtr loadMarksImpl(); MarkCache::MappedPtr loadMarksImpl();
@ -51,4 +74,6 @@ private:
ThreadPool * load_marks_threadpool; ThreadPool * load_marks_threadpool;
}; };
using MergeTreeMarksLoaderPtr = std::shared_ptr<MergeTreeMarksLoader>;
} }

View File

@ -10,10 +10,8 @@ namespace DB
namespace ErrorCodes namespace ErrorCodes
{ {
extern const int CANNOT_READ_ALL_DATA; extern const int CANNOT_READ_ALL_DATA;
extern const int ARGUMENT_OUT_OF_BOUND;
} }
MergeTreeReaderCompact::MergeTreeReaderCompact( MergeTreeReaderCompact::MergeTreeReaderCompact(
MergeTreeDataPartInfoForReaderPtr data_part_info_for_read_, MergeTreeDataPartInfoForReaderPtr data_part_info_for_read_,
NamesAndTypesList columns_, NamesAndTypesList columns_,
@ -23,7 +21,6 @@ MergeTreeReaderCompact::MergeTreeReaderCompact(
MarkCache * mark_cache_, MarkCache * mark_cache_,
MarkRanges mark_ranges_, MarkRanges mark_ranges_,
MergeTreeReaderSettings settings_, MergeTreeReaderSettings settings_,
ThreadPool * load_marks_threadpool_,
ValueSizeMap avg_value_size_hints_, ValueSizeMap avg_value_size_hints_,
const ReadBufferFromFileBase::ProfileCallback & profile_callback_, const ReadBufferFromFileBase::ProfileCallback & profile_callback_,
clockid_t clock_type_) clockid_t clock_type_)
@ -37,91 +34,22 @@ MergeTreeReaderCompact::MergeTreeReaderCompact(
mark_ranges_, mark_ranges_,
settings_, settings_,
avg_value_size_hints_) avg_value_size_hints_)
, marks_loader( , marks_loader(std::make_shared<MergeTreeMarksLoader>(
data_part_info_for_read_, data_part_info_for_read_,
mark_cache, mark_cache,
data_part_info_for_read_->getIndexGranularityInfo().getMarksFilePath(MergeTreeDataPartCompact::DATA_FILE_NAME), data_part_info_for_read_->getIndexGranularityInfo().getMarksFilePath(MergeTreeDataPartCompact::DATA_FILE_NAME),
data_part_info_for_read_->getMarksCount(), data_part_info_for_read_->getMarksCount(),
data_part_info_for_read_->getIndexGranularityInfo(), data_part_info_for_read_->getIndexGranularityInfo(),
settings.save_marks_in_cache, settings.save_marks_in_cache,
settings.read_settings, settings.read_settings,
load_marks_threadpool_, settings_.read_settings.load_marks_asynchronously
data_part_info_for_read_->getColumns().size()) ? &data_part_info_for_read_->getContext()->getLoadMarksThreadpool() : nullptr,
data_part_info_for_read_->getColumns().size()))
, profile_callback(profile_callback_) , profile_callback(profile_callback_)
, clock_type(clock_type_) , clock_type(clock_type_)
{ {
} }
void MergeTreeReaderCompact::initialize()
{
try
{
fillColumnPositions();
/// Do not use max_read_buffer_size, but try to lower buffer size with maximal size of granule to avoid reading much data.
auto buffer_size = getReadBufferSize(*data_part_info_for_read, marks_loader, column_positions, all_mark_ranges);
if (buffer_size)
settings.read_settings = settings.read_settings.adjustBufferSize(buffer_size);
if (!settings.read_settings.local_fs_buffer_size || !settings.read_settings.remote_fs_buffer_size)
throw Exception(ErrorCodes::CANNOT_READ_ALL_DATA, "Cannot read to empty buffer.");
const String path = MergeTreeDataPartCompact::DATA_FILE_NAME_WITH_EXTENSION;
auto data_part_storage = data_part_info_for_read->getDataPartStorage();
if (uncompressed_cache)
{
auto buffer = std::make_unique<CachedCompressedReadBuffer>(
std::string(fs::path(data_part_storage->getFullPath()) / path),
[this, path, data_part_storage]()
{
return data_part_storage->readFile(
path,
settings.read_settings,
std::nullopt, std::nullopt);
},
uncompressed_cache,
/* allow_different_codecs = */ true);
if (profile_callback)
buffer->setProfileCallback(profile_callback, clock_type);
if (!settings.checksum_on_read)
buffer->disableChecksumming();
cached_buffer = std::move(buffer);
data_buffer = cached_buffer.get();
compressed_data_buffer = cached_buffer.get();
}
else
{
auto buffer =
std::make_unique<CompressedReadBufferFromFile>(
data_part_storage->readFile(
path,
settings.read_settings,
std::nullopt, std::nullopt),
/* allow_different_codecs = */ true);
if (profile_callback)
buffer->setProfileCallback(profile_callback, clock_type);
if (!settings.checksum_on_read)
buffer->disableChecksumming();
non_cached_buffer = std::move(buffer);
data_buffer = non_cached_buffer.get();
compressed_data_buffer = non_cached_buffer.get();
}
}
catch (...)
{
if (!isRetryableException(std::current_exception()))
data_part_info_for_read->reportBroken();
throw;
}
}
void MergeTreeReaderCompact::fillColumnPositions() void MergeTreeReaderCompact::fillColumnPositions()
{ {
size_t columns_num = columns_to_read.size(); size_t columns_num = columns_to_read.size();
@ -150,31 +78,7 @@ void MergeTreeReaderCompact::fillColumnPositions()
/// we have to read its offsets if they exist. /// we have to read its offsets if they exist.
if (!position && is_array) if (!position && is_array)
{ {
NameAndTypePair column_to_read_with_subcolumns = column_to_read; auto column_to_read_with_subcolumns = getColumnConvertedToSubcolumnOfNested(column_to_read);
auto [name_in_storage, subcolumn_name] = Nested::splitName(column_to_read.name);
/// If it is a part of Nested, we need to get the column from
/// storage metadata which is converted to Nested type with subcolumns.
/// It is required for proper counting of shared streams.
if (!subcolumn_name.empty())
{
/// If column is renamed get the new name from storage metadata.
if (alter_conversions->columnHasNewName(name_in_storage))
name_in_storage = alter_conversions->getColumnNewName(name_in_storage);
if (!storage_columns_with_collected_nested)
{
auto options = GetColumnsOptions(GetColumnsOptions::AllPhysical).withExtendedObjects();
auto storage_columns_list = Nested::collect(storage_snapshot->getColumns(options));
storage_columns_with_collected_nested = ColumnsDescription(std::move(storage_columns_list));
}
column_to_read_with_subcolumns = storage_columns_with_collected_nested
->getColumnOrSubcolumn(
GetColumnsOptions::All,
Nested::concatenateName(name_in_storage, subcolumn_name));
}
auto name_level_for_offsets = findColumnForOffsets(column_to_read_with_subcolumns); auto name_level_for_offsets = findColumnForOffsets(column_to_read_with_subcolumns);
if (name_level_for_offsets.has_value()) if (name_level_for_offsets.has_value())
@ -191,189 +95,60 @@ void MergeTreeReaderCompact::fillColumnPositions()
} }
} }
size_t MergeTreeReaderCompact::readRows( NameAndTypePair MergeTreeReaderCompact::getColumnConvertedToSubcolumnOfNested(const NameAndTypePair & column)
size_t from_mark, size_t current_task_last_mark, bool continue_reading, size_t max_rows_to_read, Columns & res_columns)
{ {
if (!initialized) if (!isArray(column.type))
return column;
/// If it is a part of Nested, we need to get the column from
/// storage metadata which is converted to Nested type with subcolumns.
/// It is required for proper counting of shared streams.
auto [name_in_storage, subcolumn_name] = Nested::splitName(column.name);
if (subcolumn_name.empty())
return column;
/// If column is renamed get the new name from storage metadata.
if (alter_conversions->columnHasNewName(name_in_storage))
name_in_storage = alter_conversions->getColumnNewName(name_in_storage);
if (!storage_columns_with_collected_nested)
{ {
initialize(); auto options = GetColumnsOptions(GetColumnsOptions::AllPhysical).withExtendedObjects();
initialized = true; auto storage_columns_list = Nested::collect(storage_snapshot->getColumns(options));
storage_columns_with_collected_nested = ColumnsDescription(std::move(storage_columns_list));
} }
if (continue_reading) return storage_columns_with_collected_nested->getColumnOrSubcolumn(
from_mark = next_mark; GetColumnsOptions::All,
Nested::concatenateName(name_in_storage, subcolumn_name));
size_t read_rows = 0;
size_t num_columns = columns_to_read.size();
checkNumberOfColumns(num_columns);
MutableColumns mutable_columns(num_columns);
for (size_t i = 0; i < num_columns; ++i)
{
if (column_positions[i] && res_columns[i] == nullptr)
res_columns[i] = columns_to_read[i].type->createColumn(*serializations[i]);
}
while (read_rows < max_rows_to_read)
{
size_t rows_to_read = data_part_info_for_read->getIndexGranularity().getMarkRows(from_mark);
/// If we need to read multiple subcolumns from a single column in storage,
/// we will read it this column only once and then reuse to extract all subcolumns.
std::unordered_map<String, ColumnPtr> columns_cache_for_subcolumns;
for (size_t pos = 0; pos < num_columns; ++pos)
{
if (!res_columns[pos])
continue;
try
{
auto & column = res_columns[pos];
size_t column_size_before_reading = column->size();
readData(columns_to_read[pos], column, from_mark, current_task_last_mark, *column_positions[pos], rows_to_read, columns_for_offsets[pos], columns_cache_for_subcolumns);
size_t read_rows_in_column = column->size() - column_size_before_reading;
if (read_rows_in_column != rows_to_read)
throw Exception(ErrorCodes::CANNOT_READ_ALL_DATA,
"Cannot read all data in MergeTreeReaderCompact. Rows read: {}. Rows expected: {}.",
read_rows_in_column, rows_to_read);
}
catch (...)
{
if (!isRetryableException(std::current_exception()))
data_part_info_for_read->reportBroken();
/// Better diagnostics.
try
{
rethrow_exception(std::current_exception());
}
catch (Exception & e)
{
e.addMessage(getMessageForDiagnosticOfBrokenPart(from_mark, max_rows_to_read));
}
throw;
}
}
++from_mark;
read_rows += rows_to_read;
}
next_mark = from_mark;
return read_rows;
} }
void MergeTreeReaderCompact::readData( void MergeTreeReaderCompact::readData(
const NameAndTypePair & name_and_type, ColumnPtr & column, const NameAndTypePair & name_and_type,
size_t from_mark, size_t current_task_last_mark, size_t column_position, size_t rows_to_read, ColumnPtr & column,
ColumnNameLevel name_level_for_offsets, std::unordered_map<String, ColumnPtr> & columns_cache_for_subcolumns) size_t rows_to_read,
const InputStreamGetter & getter)
{ {
const auto & [name, type] = name_and_type; try
std::optional<NameAndTypePair> column_for_offsets;
if (name_level_for_offsets.has_value())
{ {
const auto & part_columns = data_part_info_for_read->getColumnsDescription(); const auto [name, type] = name_and_type;
column_for_offsets = part_columns.getPhysical(name_level_for_offsets->first); size_t column_size_before_reading = column->size();
}
adjustUpperBound(current_task_last_mark); /// Must go before seek. ISerialization::DeserializeBinaryBulkSettings deserialize_settings;
deserialize_settings.getter = getter;
deserialize_settings.avg_value_size_hint = avg_value_size_hints[name];
if (!isContinuousReading(from_mark, column_position)) if (name_and_type.isSubcolumn())
seekToMark(from_mark, column_position);
/// If we read only offsets we have to read prefix anyway
/// to preserve correctness of serialization.
auto buffer_getter_for_prefix = [&](const auto &) -> ReadBuffer *
{
return data_buffer;
};
auto buffer_getter = [&](const ISerialization::SubstreamPath & substream_path) -> ReadBuffer *
{
/// Offset stream from another column could be read, in case of current
/// column does not exists (see findColumnForOffsets() in
/// MergeTreeReaderCompact::fillColumnPositions())
if (name_level_for_offsets.has_value())
{ {
bool is_offsets = !substream_path.empty() && substream_path.back().type == ISerialization::Substream::ArraySizes; const auto & type_in_storage = name_and_type.getTypeInStorage();
if (!is_offsets) const auto & name_in_storage = name_and_type.getNameInStorage();
return nullptr;
/// Offset stream can be read only from columns of current level or auto serialization = getSerializationInPart({name_in_storage, type_in_storage});
/// below (since it is OK to read all parent streams from the ColumnPtr temp_column = type_in_storage->createColumn(*serialization);
/// alternative).
///
/// Consider the following columns in nested "root":
/// - root.array Array(UInt8) - exists
/// - root.nested_array Array(Array(UInt8)) - does not exists (only_offsets_level=1)
///
/// For root.nested_array it will try to read multiple streams:
/// - offsets (substream_path = {ArraySizes})
/// OK
/// - root.nested_array elements (substream_path = {ArrayElements, ArraySizes})
/// NOT OK - cannot use root.array offsets stream for this
///
/// Here only_offsets_level is the level of the alternative stream,
/// and substream_path.size() is the level of the current stream.
if (name_level_for_offsets->second < ISerialization::getArrayLevel(substream_path))
return nullptr;
}
return data_buffer; serialization->deserializeBinaryBulkWithMultipleStreams(temp_column, rows_to_read, deserialize_settings, deserialize_binary_bulk_state_map[name], nullptr);
}; auto subcolumn = type_in_storage->getSubcolumn(name_and_type.getSubcolumnName(), temp_column);
ISerialization::DeserializeBinaryBulkStatePtr state;
ISerialization::DeserializeBinaryBulkStatePtr state_for_prefix;
ISerialization::DeserializeBinaryBulkSettings deserialize_settings;
deserialize_settings.avg_value_size_hint = avg_value_size_hints[name];
bool columns_cache_was_used = false;
if (name_and_type.isSubcolumn())
{
NameAndTypePair name_type_in_storage{name_and_type.getNameInStorage(), name_and_type.getTypeInStorage()};
ColumnPtr temp_column;
auto it = columns_cache_for_subcolumns.find(name_type_in_storage.name);
if (!column_for_offsets && it != columns_cache_for_subcolumns.end())
{
temp_column = it->second;
auto subcolumn = name_type_in_storage.type->getSubcolumn(name_and_type.getSubcolumnName(), temp_column);
if (column->empty())
column = IColumn::mutate(subcolumn);
else
column->assumeMutable()->insertRangeFrom(*subcolumn, 0, subcolumn->size());
columns_cache_was_used = true;
}
else
{
/// In case of reading only offset use the correct serialization for reading of the prefix
auto serialization = getSerializationInPart(name_type_in_storage);
temp_column = name_type_in_storage.type->createColumn(*serialization);
if (column_for_offsets)
{
auto serialization_for_prefix = getSerializationInPart(*column_for_offsets);
deserialize_settings.getter = buffer_getter_for_prefix;
serialization_for_prefix->deserializeBinaryBulkStatePrefix(deserialize_settings, state_for_prefix);
}
deserialize_settings.getter = buffer_getter;
serialization->deserializeBinaryBulkStatePrefix(deserialize_settings, state);
serialization->deserializeBinaryBulkWithMultipleStreams(temp_column, rows_to_read, deserialize_settings, state, nullptr);
if (!column_for_offsets)
columns_cache_for_subcolumns[name_type_in_storage.name] = temp_column;
auto subcolumn = name_type_in_storage.type->getSubcolumn(name_and_type.getSubcolumnName(), temp_column);
/// TODO: Avoid extra copying. /// TODO: Avoid extra copying.
if (column->empty()) if (column->empty())
@ -381,185 +156,98 @@ void MergeTreeReaderCompact::readData(
else else
column->assumeMutable()->insertRangeFrom(*subcolumn, 0, subcolumn->size()); column->assumeMutable()->insertRangeFrom(*subcolumn, 0, subcolumn->size());
} }
} else
else
{
/// In case of reading only offsets use the correct serialization for reading the prefix
auto serialization = getSerializationInPart(name_and_type);
if (column_for_offsets)
{ {
auto serialization_for_prefix = getSerializationInPart(*column_for_offsets); auto serialization = getSerializationInPart(name_and_type);
serialization->deserializeBinaryBulkWithMultipleStreams(column, rows_to_read, deserialize_settings, deserialize_binary_bulk_state_map[name], nullptr);
deserialize_settings.getter = buffer_getter_for_prefix;
serialization_for_prefix->deserializeBinaryBulkStatePrefix(deserialize_settings, state_for_prefix);
} }
deserialize_settings.getter = buffer_getter; size_t read_rows_in_column = column->size() - column_size_before_reading;
serialization->deserializeBinaryBulkStatePrefix(deserialize_settings, state); if (read_rows_in_column != rows_to_read)
serialization->deserializeBinaryBulkWithMultipleStreams(column, rows_to_read, deserialize_settings, state, nullptr); throw Exception(ErrorCodes::CANNOT_READ_ALL_DATA,
} "Cannot read all data in MergeTreeReaderCompact. Rows read: {}. Rows expected: {}.",
read_rows_in_column, rows_to_read);
/// The buffer is left in inconsistent state after reading single offsets or using columns cache during subcolumns reading.
if (name_level_for_offsets.has_value() || columns_cache_was_used)
last_read_granule.reset();
else
last_read_granule.emplace(from_mark, column_position);
}
void MergeTreeReaderCompact::prefetchBeginOfRange(Priority priority)
try
{
if (!initialized)
{
initialize();
initialized = true;
}
adjustUpperBound(all_mark_ranges.back().end);
seekToMark(all_mark_ranges.front().begin, 0);
data_buffer->prefetch(priority);
}
catch (...)
{
if (!isRetryableException(std::current_exception()))
data_part_info_for_read->reportBroken();
throw;
}
void MergeTreeReaderCompact::seekToMark(size_t row_index, size_t column_index)
{
MarkInCompressedFile mark = marks_loader.getMark(row_index, column_index);
try
{
compressed_data_buffer->seek(mark.offset_in_compressed_file, mark.offset_in_decompressed_block);
} }
catch (Exception & e) catch (Exception & e)
{ {
/// Better diagnostics. e.addMessage("(while reading column " + name_and_type.name + ")");
if (e.code() == ErrorCodes::ARGUMENT_OUT_OF_BOUND)
e.addMessage("(while seeking to mark (" + toString(row_index) + ", " + toString(column_index) + ")");
throw; throw;
} }
} }
void MergeTreeReaderCompact::adjustUpperBound(size_t last_mark)
void MergeTreeReaderCompact::readPrefix(
const NameAndTypePair & name_and_type,
const InputStreamGetter & buffer_getter,
const InputStreamGetter & buffer_getter_for_prefix,
const ColumnNameLevel & name_level_for_offsets)
{ {
size_t right_offset = 0; try
if (last_mark < data_part_info_for_read->getMarksCount()) /// Otherwise read until the end of file
right_offset = marks_loader.getMark(last_mark).offset_in_compressed_file;
if (right_offset == 0)
{ {
/// If already reading till the end of file. ISerialization::DeserializeBinaryBulkSettings deserialize_settings;
if (last_right_offset && *last_right_offset == 0)
return;
last_right_offset = 0; // Zero value means the end of file. if (name_level_for_offsets.has_value())
data_buffer->setReadUntilEnd(); {
const auto & part_columns = data_part_info_for_read->getColumnsDescription();
auto column_for_offsets = part_columns.getPhysical(name_level_for_offsets->first);
auto serialization_for_prefix = getSerializationInPart(column_for_offsets);
deserialize_settings.getter = buffer_getter_for_prefix;
ISerialization::DeserializeBinaryBulkStatePtr state_for_prefix;
serialization_for_prefix->deserializeBinaryBulkStatePrefix(deserialize_settings, state_for_prefix);
}
SerializationPtr serialization;
if (name_and_type.isSubcolumn())
serialization = getSerializationInPart({name_and_type.getNameInStorage(), name_and_type.getTypeInStorage()});
else
serialization = getSerializationInPart(name_and_type);
deserialize_settings.getter = buffer_getter;
serialization->deserializeBinaryBulkStatePrefix(deserialize_settings, deserialize_binary_bulk_state_map[name_and_type.name]);
} }
else catch (Exception & e)
{ {
if (last_right_offset && right_offset <= last_right_offset.value()) e.addMessage("(while reading column " + name_and_type.name + ")");
return; throw;
last_right_offset = right_offset;
data_buffer->setReadUntilPosition(right_offset);
} }
} }
bool MergeTreeReaderCompact::isContinuousReading(size_t mark, size_t column_position) void MergeTreeReaderCompact::createColumnsForReading(Columns & res_columns) const
{ {
if (!last_read_granule) for (size_t i = 0; i < columns_to_read.size(); ++i)
{
if (column_positions[i] && res_columns[i] == nullptr)
res_columns[i] = columns_to_read[i].type->createColumn(*serializations[i]);
}
}
bool MergeTreeReaderCompact::needSkipStream(size_t column_pos, const ISerialization::SubstreamPath & substream) const
{
/// Offset stream can be read only from columns of current level or
/// below (since it is OK to read all parent streams from the
/// alternative).
///
/// Consider the following columns in nested "root":
/// - root.array Array(UInt8) - exists
/// - root.nested_array Array(Array(UInt8)) - does not exists (only_offsets_level=1)
///
/// For root.nested_array it will try to read multiple streams:
/// - offsets (substream_path = {ArraySizes})
/// OK
/// - root.nested_array elements (substream_path = {ArrayElements, ArraySizes})
/// NOT OK - cannot use root.array offsets stream for this
///
/// Here only_offsets_level is the level of the alternative stream,
/// and substream_path.size() is the level of the current stream.
if (!columns_for_offsets[column_pos])
return false; return false;
const auto & [last_mark, last_column] = *last_read_granule;
return (mark == last_mark && column_position == last_column + 1)
|| (mark == last_mark + 1 && column_position == 0 && last_column == data_part_info_for_read->getColumns().size() - 1);
}
namespace bool is_offsets = !substream.empty() && substream.back().type == ISerialization::Substream::ArraySizes;
{ return !is_offsets || columns_for_offsets[column_pos]->second < ISerialization::getArrayLevel(substream);
/// A simple class that helps to iterate over 2-dim marks of compact parts.
class MarksCounter
{
public:
MarksCounter(size_t rows_num_, size_t columns_num_)
: rows_num(rows_num_), columns_num(columns_num_) {}
struct Iterator
{
size_t row;
size_t column;
MarksCounter * counter;
Iterator(size_t row_, size_t column_, MarksCounter * counter_)
: row(row_), column(column_), counter(counter_) {}
Iterator operator++()
{
if (column + 1 == counter->columns_num)
{
++row;
column = 0;
}
else
{
++column;
}
return *this;
}
bool operator==(const Iterator & other) const { return row == other.row && column == other.column; }
bool operator!=(const Iterator & other) const { return !(*this == other); }
};
Iterator get(size_t row, size_t column) { return Iterator(row, column, this); }
Iterator end() { return get(rows_num, 0); }
private:
size_t rows_num;
size_t columns_num;
};
}
size_t MergeTreeReaderCompact::getReadBufferSize(
const IMergeTreeDataPartInfoForReader & data_part_info_for_reader,
MergeTreeMarksLoader & marks_loader,
const ColumnPositions & column_positions,
const MarkRanges & mark_ranges)
{
size_t buffer_size = 0;
size_t columns_num = column_positions.size();
size_t file_size = data_part_info_for_reader.getFileSizeOrZero(MergeTreeDataPartCompact::DATA_FILE_NAME_WITH_EXTENSION);
MarksCounter counter(data_part_info_for_reader.getMarksCount(), data_part_info_for_reader.getColumns().size());
for (const auto & mark_range : mark_ranges)
{
for (size_t mark = mark_range.begin; mark < mark_range.end; ++mark)
{
for (size_t i = 0; i < columns_num; ++i)
{
if (!column_positions[i])
continue;
auto it = counter.get(mark, *column_positions[i]);
size_t cur_offset = marks_loader.getMark(it.row, it.column).offset_in_compressed_file;
while (it != counter.end() && cur_offset == marks_loader.getMark(it.row, it.column).offset_in_compressed_file)
++it;
size_t next_offset = (it == counter.end() ? file_size : marks_loader.getMark(it.row, it.column).offset_in_compressed_file);
buffer_size = std::max(buffer_size, next_offset - cur_offset);
}
}
}
return buffer_size;
} }
} }

View File

@ -3,7 +3,7 @@
#include <Core/NamesAndTypes.h> #include <Core/NamesAndTypes.h>
#include <Storages/MergeTree/IMergeTreeReader.h> #include <Storages/MergeTree/IMergeTreeReader.h>
#include <IO/ReadBufferFromFileBase.h> #include <IO/ReadBufferFromFileBase.h>
#include <DataTypes/Serializations/ISerialization.h>
namespace DB namespace DB
{ {
@ -14,7 +14,7 @@ using DataPartCompactPtr = std::shared_ptr<const MergeTreeDataPartCompact>;
class IMergeTreeDataPart; class IMergeTreeDataPart;
using DataPartPtr = std::shared_ptr<const IMergeTreeDataPart>; using DataPartPtr = std::shared_ptr<const IMergeTreeDataPart>;
/// Reader for compact parts /// Base class of readers for compact parts.
class MergeTreeReaderCompact : public IMergeTreeReader class MergeTreeReaderCompact : public IMergeTreeReader
{ {
public: public:
@ -27,31 +27,38 @@ public:
MarkCache * mark_cache_, MarkCache * mark_cache_,
MarkRanges mark_ranges_, MarkRanges mark_ranges_,
MergeTreeReaderSettings settings_, MergeTreeReaderSettings settings_,
ThreadPool * load_marks_threadpool_, ValueSizeMap avg_value_size_hints_,
ValueSizeMap avg_value_size_hints_ = {}, const ReadBufferFromFileBase::ProfileCallback & profile_callback_,
const ReadBufferFromFileBase::ProfileCallback & profile_callback_ = {}, clockid_t clock_type_);
clockid_t clock_type_ = CLOCK_MONOTONIC_COARSE);
/// Return the number of rows has been read or zero if there is no columns to read. bool canReadIncompleteGranules() const final { return false; }
/// If continue_reading is true, continue reading from last state, otherwise seek to from_mark
size_t readRows(size_t from_mark, size_t current_task_last_mark,
bool continue_reading, size_t max_rows_to_read, Columns & res_columns) override;
bool canReadIncompleteGranules() const override { return false; } protected:
void prefetchBeginOfRange(Priority priority) override;
private:
bool isContinuousReading(size_t mark, size_t column_position);
void fillColumnPositions(); void fillColumnPositions();
void initialize(); NameAndTypePair getColumnConvertedToSubcolumnOfNested(const NameAndTypePair & column);
ReadBuffer * data_buffer; using InputStreamGetter = ISerialization::InputStreamGetter;
CompressedReadBufferBase * compressed_data_buffer;
std::unique_ptr<CachedCompressedReadBuffer> cached_buffer;
std::unique_ptr<CompressedReadBufferFromFile> non_cached_buffer;
MergeTreeMarksLoader marks_loader; void readData(
const NameAndTypePair & name_and_type,
ColumnPtr & column,
size_t rows_to_read,
const InputStreamGetter & getter);
void readPrefix(
const NameAndTypePair & name_and_type,
const InputStreamGetter & buffer_getter,
const InputStreamGetter & buffer_getter_for_prefix,
const ColumnNameLevel & name_level_for_offsets);
void createColumnsForReading(Columns & res_columns) const;
bool needSkipStream(size_t column_pos, const ISerialization::SubstreamPath & substream) const;
const MergeTreeMarksLoaderPtr marks_loader;
MergeTreeMarksGetterPtr marks_getter;
ReadBufferFromFileBase::ProfileCallback profile_callback;
clockid_t clock_type;
/// Storage columns with collected separate arrays of Nested to columns of Nested type. /// Storage columns with collected separate arrays of Nested to columns of Nested type.
/// They maybe be needed for finding offsets of missed Nested columns in parts. /// They maybe be needed for finding offsets of missed Nested columns in parts.
@ -67,32 +74,9 @@ private:
/// Element of the vector is the level of the alternative stream. /// Element of the vector is the level of the alternative stream.
std::vector<ColumnNameLevel> columns_for_offsets; std::vector<ColumnNameLevel> columns_for_offsets;
/// For asynchronous reading from remote fs. Same meaning as in MergeTreeReaderStream. /// Mark to read in next 'readRows' call in case,
std::optional<size_t> last_right_offset; /// when 'continue_reading' is true.
size_t next_mark = 0; size_t next_mark = 0;
std::optional<std::pair<size_t, size_t>> last_read_granule;
void seekToMark(size_t row_index, size_t column_index);
void readData(const NameAndTypePair & name_and_type, ColumnPtr & column, size_t from_mark,
size_t current_task_last_mark, size_t column_position,
size_t rows_to_read, ColumnNameLevel name_level_for_offsets, std::unordered_map<String, ColumnPtr> & columns_cache_for_subcolumns);
/// Returns maximal value of granule size in compressed file from @mark_ranges.
/// This value is used as size of read buffer.
static size_t getReadBufferSize(
const IMergeTreeDataPartInfoForReader & data_part_info_for_reader,
MergeTreeMarksLoader & marks_loader,
const ColumnPositions & column_positions,
const MarkRanges & mark_ranges);
/// For asynchronous reading from remote fs.
void adjustUpperBound(size_t last_mark);
ReadBufferFromFileBase::ProfileCallback profile_callback;
clockid_t clock_type;
bool initialized = false;
}; };
} }

View File

@ -0,0 +1,108 @@
#include <Storages/MergeTree/MergeTreeReaderCompactSingleBuffer.h>
#include <Storages/MergeTree/MergeTreeDataPartCompact.h>
#include <Storages/MergeTree/checkDataPart.h>
#include <DataTypes/DataTypeArray.h>
#include <DataTypes/NestedUtils.h>
namespace DB
{
size_t MergeTreeReaderCompactSingleBuffer::readRows(
size_t from_mark, size_t current_task_last_mark, bool continue_reading, size_t max_rows_to_read, Columns & res_columns)
try
{
init();
if (continue_reading)
from_mark = next_mark;
size_t read_rows = 0;
size_t num_columns = columns_to_read.size();
checkNumberOfColumns(num_columns);
createColumnsForReading(res_columns);
while (read_rows < max_rows_to_read)
{
size_t rows_to_read = data_part_info_for_read->getIndexGranularity().getMarkRows(from_mark);
for (size_t pos = 0; pos < num_columns; ++pos)
{
if (!res_columns[pos])
continue;
auto & column = res_columns[pos];
stream->adjustRightMark(current_task_last_mark); /// Must go before seek.
stream->seekToMarkAndColumn(from_mark, *column_positions[pos]);
auto buffer_getter = [&](const ISerialization::SubstreamPath & substream_path) -> ReadBuffer *
{
if (needSkipStream(pos, substream_path))
return nullptr;
return stream->getDataBuffer();
};
/// If we read only offsets we have to read prefix anyway
/// to preserve correctness of serialization.
auto buffer_getter_for_prefix = [&](const auto &) -> ReadBuffer *
{
return stream->getDataBuffer();
};
readPrefix(columns_to_read[pos], buffer_getter, buffer_getter_for_prefix, columns_for_offsets[pos]);
readData(columns_to_read[pos], column, rows_to_read, buffer_getter);
}
++from_mark;
read_rows += rows_to_read;
}
next_mark = from_mark;
return read_rows;
}
catch (...)
{
if (!isRetryableException(std::current_exception()))
data_part_info_for_read->reportBroken();
/// Better diagnostics.
try
{
rethrow_exception(std::current_exception());
}
catch (Exception & e)
{
e.addMessage(getMessageForDiagnosticOfBrokenPart(from_mark, max_rows_to_read));
}
throw;
}
void MergeTreeReaderCompactSingleBuffer::init()
try
{
if (initialized)
return;
auto stream_settings = settings;
stream_settings.allow_different_codecs = true;
stream = std::make_unique<MergeTreeReaderStreamAllOfMultipleColumns>(
data_part_info_for_read->getDataPartStorage(), MergeTreeDataPartCompact::DATA_FILE_NAME,
MergeTreeDataPartCompact::DATA_FILE_EXTENSION, data_part_info_for_read->getMarksCount(),
all_mark_ranges, stream_settings,uncompressed_cache,
data_part_info_for_read->getFileSizeOrZero(MergeTreeDataPartCompact::DATA_FILE_NAME_WITH_EXTENSION),
marks_loader, profile_callback, clock_type);
initialized = true;
}
catch (...)
{
if (!isRetryableException(std::current_exception()))
data_part_info_for_read->reportBroken();
throw;
}
}

View File

@ -0,0 +1,33 @@
#pragma once
#include <Storages/MergeTree/MergeTreeReaderCompact.h>
#include <Storages/MergeTree/MergeTreeReaderStream.h>
namespace DB
{
/// Reader for compact parts, that uses one buffer for
/// all column and doesn't support parallel prefetch of columns.
/// It's suitable for compact parts with small size of stripe.
class MergeTreeReaderCompactSingleBuffer : public MergeTreeReaderCompact
{
public:
template <typename... Args>
explicit MergeTreeReaderCompactSingleBuffer(Args &&... args)
: MergeTreeReaderCompact{std::forward<Args>(args)...}
{
fillColumnPositions();
}
/// Returns the number of rows has been read or zero if there is no columns to read.
/// If continue_reading is true, continue reading from last state, otherwise seek to from_mark
size_t readRows(size_t from_mark, size_t current_task_last_mark,
bool continue_reading, size_t max_rows_to_read, Columns & res_columns) override;
private:
void init();
bool initialized = false;
std::unique_ptr<MergeTreeReaderStream> stream;
};
}

View File

@ -16,43 +16,28 @@ namespace ErrorCodes
} }
MergeTreeReaderStream::MergeTreeReaderStream( MergeTreeReaderStream::MergeTreeReaderStream(
MergeTreeDataPartInfoForReaderPtr data_part_reader_, DataPartStoragePtr data_part_storage_,
const String & path_prefix_, const String & path_prefix_,
const String & data_file_extension_, const String & data_file_extension_,
size_t marks_count_, size_t marks_count_,
const MarkRanges & all_mark_ranges_, const MarkRanges & all_mark_ranges_,
const MergeTreeReaderSettings & settings_, const MergeTreeReaderSettings & settings_,
MarkCache * mark_cache_, UncompressedCache * uncompressed_cache_,
UncompressedCache * uncompressed_cache_, size_t file_size_,
size_t file_size_, MergeTreeMarksLoaderPtr marks_loader_,
const MergeTreeIndexGranularityInfo * index_granularity_info_, const ReadBufferFromFileBase::ProfileCallback & profile_callback_,
const ReadBufferFromFileBase::ProfileCallback & profile_callback_, clockid_t clock_type_)
clockid_t clock_type_, : profile_callback(profile_callback_)
bool is_low_cardinality_dictionary_,
ThreadPool * load_marks_cache_threadpool_)
: settings(settings_)
, profile_callback(profile_callback_)
, clock_type(clock_type_) , clock_type(clock_type_)
, all_mark_ranges(all_mark_ranges_) , all_mark_ranges(all_mark_ranges_)
, file_size(file_size_) , data_part_storage(std::move(data_part_storage_))
, uncompressed_cache(uncompressed_cache_)
, data_part_storage(data_part_reader_->getDataPartStorage())
, path_prefix(path_prefix_) , path_prefix(path_prefix_)
, data_file_extension(data_file_extension_) , data_file_extension(data_file_extension_)
, is_low_cardinality_dictionary(is_low_cardinality_dictionary_) , uncompressed_cache(uncompressed_cache_)
, settings(settings_)
, marks_count(marks_count_) , marks_count(marks_count_)
, mark_cache(mark_cache_) , file_size(file_size_)
, save_marks_in_cache(settings.save_marks_in_cache) , marks_loader(std::move(marks_loader_))
, index_granularity_info(index_granularity_info_)
, marks_loader(
data_part_reader_,
mark_cache,
index_granularity_info->getMarksFilePath(path_prefix),
marks_count,
*index_granularity_info,
save_marks_in_cache,
settings.read_settings,
load_marks_cache_threadpool_)
{ {
} }
@ -60,21 +45,12 @@ void MergeTreeReaderStream::init()
{ {
if (initialized) if (initialized)
return; return;
initialized = true; initialized = true;
marks_getter = marks_loader->loadMarks();
/// Compute the size of the buffer. /// Compute the size of the buffer.
size_t max_mark_range_bytes = 0; auto [max_mark_range_bytes, sum_mark_range_bytes] = estimateMarkRangeBytes(all_mark_ranges);
size_t sum_mark_range_bytes = 0;
for (const auto & mark_range : all_mark_ranges)
{
size_t left_mark = mark_range.begin;
size_t right_mark = mark_range.end;
size_t left_offset = left_mark < marks_count ? marks_loader.getMark(left_mark).offset_in_compressed_file : 0;
auto mark_range_bytes = getRightOffset(right_mark) - left_offset;
max_mark_range_bytes = std::max(max_mark_range_bytes, mark_range_bytes);
sum_mark_range_bytes += mark_range_bytes;
}
std::optional<size_t> estimated_sum_mark_range_bytes; std::optional<size_t> estimated_sum_mark_range_bytes;
if (sum_mark_range_bytes) if (sum_mark_range_bytes)
@ -83,7 +59,7 @@ void MergeTreeReaderStream::init()
/// Avoid empty buffer. May happen while reading dictionary for DataTypeLowCardinality. /// Avoid empty buffer. May happen while reading dictionary for DataTypeLowCardinality.
/// For example: part has single dictionary and all marks point to the same position. /// For example: part has single dictionary and all marks point to the same position.
ReadSettings read_settings = settings.read_settings; ReadSettings read_settings = settings.read_settings;
if (max_mark_range_bytes != 0) if (settings.adjust_read_buffer_size && max_mark_range_bytes != 0)
read_settings = read_settings.adjustBufferSize(max_mark_range_bytes); read_settings = read_settings.adjustBufferSize(max_mark_range_bytes);
//// Empty buffer does not makes progress. //// Empty buffer does not makes progress.
@ -102,7 +78,8 @@ void MergeTreeReaderStream::init()
read_settings, read_settings,
estimated_sum_mark_range_bytes, std::nullopt); estimated_sum_mark_range_bytes, std::nullopt);
}, },
uncompressed_cache); uncompressed_cache,
settings.allow_different_codecs);
if (profile_callback) if (profile_callback)
buffer->setProfileCallback(profile_callback, clock_type); buffer->setProfileCallback(profile_callback, clock_type);
@ -121,7 +98,7 @@ void MergeTreeReaderStream::init()
path_prefix + data_file_extension, path_prefix + data_file_extension,
read_settings, read_settings,
estimated_sum_mark_range_bytes, estimated_sum_mark_range_bytes,
std::nullopt)); std::nullopt), settings.allow_different_codecs);
if (profile_callback) if (profile_callback)
buffer->setProfileCallback(profile_callback, clock_type); buffer->setProfileCallback(profile_callback, clock_type);
@ -135,99 +112,10 @@ void MergeTreeReaderStream::init()
} }
} }
void MergeTreeReaderStream::seekToMarkAndColumn(size_t row_index, size_t column_position)
size_t MergeTreeReaderStream::getRightOffset(size_t right_mark)
{
/// NOTE: if we are reading the whole file, then right_mark == marks_count
/// and we will use max_read_buffer_size for buffer size, thus avoiding the need to load marks.
/// Special case, can happen in Collapsing/Replacing engines
if (marks_count == 0)
return 0;
assert(right_mark <= marks_count);
if (0 < right_mark && right_mark < marks_count)
{
/// Find the right border of the last mark we need to read.
/// To do that let's find the upper bound of the offset of the last
/// included mark.
if (is_low_cardinality_dictionary)
{
/// In LowCardinality dictionary several consecutive marks can point to the same offset.
///
/// Also, in some cases, when one granule is not-atomically written (which is possible at merges)
/// one granule may require reading of two dictionaries which starts from different marks.
/// The only correct way is to take offset from at least next different granule from the right one.
/// So, that's why we have to read one extra granule to the right,
/// while reading dictionary of LowCardinality.
///
/// Example:
/// Mark 0, points to [0, 8]
/// Mark 1, points to [0, 8]
/// Mark 2, points to [0, 8]
/// Mark 3, points to [0, 8]
/// Mark 4, points to [42336, 2255]
/// Mark 5, points to [42336, 2255] <--- for example need to read until 5
/// Mark 6, points to [42336, 2255] <--- not suitable, because have same offset
/// Mark 7, points to [84995, 7738] <--- next different mark
/// Mark 8, points to [84995, 7738]
/// Mark 9, points to [126531, 8637] <--- what we are looking for
auto indices = collections::range(right_mark, marks_count);
auto next_different_mark = [&](auto lhs, auto rhs)
{
return marks_loader.getMark(lhs).asTuple() < marks_loader.getMark(rhs).asTuple();
};
auto it = std::upper_bound(indices.begin(), indices.end(), right_mark, std::move(next_different_mark));
if (it == indices.end())
return file_size;
right_mark = *it;
}
/// This is a good scenario. The compressed block is finished within the right mark,
/// and previous mark was different.
if (marks_loader.getMark(right_mark).offset_in_decompressed_block == 0
&& marks_loader.getMark(right_mark) != marks_loader.getMark(right_mark - 1))
return marks_loader.getMark(right_mark).offset_in_compressed_file;
/// If right_mark has non-zero offset in decompressed block, we have to
/// read its compressed block in a whole, because it may consist of data from previous granule.
///
/// For example:
/// Mark 6, points to [42336, 2255]
/// Mark 7, points to [84995, 7738] <--- right_mark
/// Mark 8, points to [84995, 7738]
/// Mark 9, points to [126531, 8637] <--- what we are looking for
///
/// Since mark 7 starts from offset in decompressed block 7738,
/// it has some data from mark 6 and we have to read
/// compressed block [84995; 126531 in a whole.
auto indices = collections::range(right_mark, marks_count);
auto next_different_compressed_offset = [&](auto lhs, auto rhs)
{
return marks_loader.getMark(lhs).offset_in_compressed_file < marks_loader.getMark(rhs).offset_in_compressed_file;
};
auto it = std::upper_bound(indices.begin(), indices.end(), right_mark, std::move(next_different_compressed_offset));
if (it != indices.end())
return marks_loader.getMark(*it).offset_in_compressed_file;
}
else if (right_mark == 0)
return marks_loader.getMark(right_mark).offset_in_compressed_file;
return file_size;
}
void MergeTreeReaderStream::seekToMark(size_t index)
{ {
init(); init();
MarkInCompressedFile mark = marks_loader.getMark(index); const auto & mark = marks_getter->getMark(row_index, column_position);
try try
{ {
@ -237,7 +125,7 @@ void MergeTreeReaderStream::seekToMark(size_t index)
{ {
/// Better diagnostics. /// Better diagnostics.
if (e.code() == ErrorCodes::ARGUMENT_OUT_OF_BOUND) if (e.code() == ErrorCodes::ARGUMENT_OUT_OF_BOUND)
e.addMessage("(while seeking to mark " + toString(index) e.addMessage("(while seeking to mark " + toString(row_index)
+ " of column " + path_prefix + "; offsets are: " + " of column " + path_prefix + "; offsets are: "
+ toString(mark.offset_in_compressed_file) + " " + toString(mark.offset_in_compressed_file) + " "
+ toString(mark.offset_in_decompressed_block) + ")"); + toString(mark.offset_in_decompressed_block) + ")");
@ -274,6 +162,7 @@ void MergeTreeReaderStream::adjustRightMark(size_t right_mark)
*/ */
init(); init();
auto right_offset = getRightOffset(right_mark); auto right_offset = getRightOffset(right_mark);
if (!right_offset) if (!right_offset)
{ {
if (last_right_offset && *last_right_offset == 0) if (last_right_offset && *last_right_offset == 0)
@ -304,4 +193,276 @@ CompressedReadBufferBase * MergeTreeReaderStream::getCompressedDataBuffer()
return compressed_data_buffer; return compressed_data_buffer;
} }
size_t MergeTreeReaderStreamSingleColumn::getRightOffset(size_t right_mark) const
{
/// NOTE: if we are reading the whole file, then right_mark == marks_count
/// and we will use max_read_buffer_size for buffer size, thus avoiding the need to load marks.
/// Special case, can happen in Collapsing/Replacing engines
if (marks_count == 0)
return 0;
assert(right_mark <= marks_count);
if (right_mark == 0)
return marks_getter->getMark(right_mark, 0).offset_in_compressed_file;
if (right_mark == marks_count)
return file_size;
/// Find the right border of the last mark we need to read.
/// To do that let's find the upper bound of the offset of the last
/// included mark.
if (settings.is_low_cardinality_dictionary)
{
/// In LowCardinality dictionary several consecutive marks can point to the same offset.
///
/// Also, in some cases, when one granule is not-atomically written (which is possible at merges)
/// one granule may require reading of two dictionaries which starts from different marks.
/// The only correct way is to take offset from at least next different granule from the right one.
/// So, that's why we have to read one extra granule to the right,
/// while reading dictionary of LowCardinality.
///
/// Example:
/// Mark 0, points to [0, 8]
/// Mark 1, points to [0, 8]
/// Mark 2, points to [0, 8]
/// Mark 3, points to [0, 8]
/// Mark 4, points to [42336, 2255]
/// Mark 5, points to [42336, 2255] <--- for example need to read until 5
/// Mark 6, points to [42336, 2255] <--- not suitable, because have same offset
/// Mark 7, points to [84995, 7738] <--- next different mark
/// Mark 8, points to [84995, 7738]
/// Mark 9, points to [126531, 8637] <--- what we are looking for
auto indices = collections::range(right_mark, marks_count);
auto next_different_mark = [&](auto lhs, auto rhs)
{
return marks_getter->getMark(lhs, 0).asTuple() < marks_getter->getMark(rhs, 0).asTuple();
};
auto it = std::upper_bound(indices.begin(), indices.end(), right_mark, std::move(next_different_mark));
if (it == indices.end())
return file_size;
right_mark = *it;
}
/// This is a good scenario. The compressed block is finished within the right mark,
/// and previous mark was different.
if (marks_getter->getMark(right_mark, 0).offset_in_decompressed_block == 0
&& marks_getter->getMark(right_mark, 0) != marks_getter->getMark(right_mark - 1, 0))
return marks_getter->getMark(right_mark, 0).offset_in_compressed_file;
/// If right_mark has non-zero offset in decompressed block, we have to
/// read its compressed block in a whole, because it may consist of data from previous granule.
///
/// For example:
/// Mark 6, points to [42336, 2255]
/// Mark 7, points to [84995, 7738] <--- right_mark
/// Mark 8, points to [84995, 7738]
/// Mark 9, points to [126531, 8637] <--- what we are looking for
///
/// Since mark 7 starts from offset in decompressed block 7738,
/// it has some data from mark 6 and we have to read
/// compressed block [84995; 126531 in a whole.
auto indices = collections::range(right_mark, marks_count);
auto next_different_compressed_offset = [&](auto lhs, auto rhs)
{
return marks_getter->getMark(lhs, 0).offset_in_compressed_file < marks_getter->getMark(rhs, 0).offset_in_compressed_file;
};
auto it = std::upper_bound(indices.begin(), indices.end(), right_mark, std::move(next_different_compressed_offset));
if (it != indices.end())
return marks_getter->getMark(*it, 0).offset_in_compressed_file;
return file_size;
}
std::pair<size_t, size_t> MergeTreeReaderStreamSingleColumn::estimateMarkRangeBytes(const MarkRanges & mark_ranges) const
{
assert(marks_getter != nullptr);
size_t max_range_bytes = 0;
size_t sum_range_bytes = 0;
for (const auto & mark_range : mark_ranges)
{
size_t left_mark = mark_range.begin;
size_t right_mark = mark_range.end;
size_t left_offset = left_mark < marks_count ? marks_getter->getMark(left_mark, 0).offset_in_compressed_file : 0;
auto mark_range_bytes = getRightOffset(right_mark) - left_offset;
max_range_bytes = std::max(max_range_bytes, mark_range_bytes);
sum_range_bytes += mark_range_bytes;
}
return {max_range_bytes, sum_range_bytes};
}
size_t MergeTreeReaderStreamMultipleColumns::getRightOffsetOneColumn(size_t right_mark_non_included, size_t column_position) const
{
/// NOTE: if we are reading the whole file, then right_mark == marks_count
/// and we will use max_read_buffer_size for buffer size, thus avoiding the need to load marks.
/// Special case, can happen in Collapsing/Replacing engines
if (marks_count == 0)
return 0;
assert(right_mark_non_included <= marks_count);
if (right_mark_non_included == 0)
return marks_getter->getMark(right_mark_non_included, column_position).offset_in_compressed_file;
size_t right_mark_included = right_mark_non_included - 1;
if (right_mark_non_included != marks_count
&& marks_getter->getMark(right_mark_non_included, column_position).offset_in_decompressed_block != 0)
++right_mark_included;
/// The right bound for case, where there is no smaller suitable mark
/// is the start of the next stripe (in which the next column is written)
/// because each stripe always start from a new compressed block.
const auto & right_mark_in_file = marks_getter->getMark(right_mark_included, column_position);
auto next_stripe_right_mark_in_file = getStartOfNextStripeMark(right_mark_included, column_position);
/// Try to find suitable right mark in current stripe.
for (size_t mark = right_mark_included + 1; mark < marks_count; ++mark)
{
const auto & current_mark = marks_getter->getMark(mark, column_position);
/// We found first mark that starts from the new compressed block.
if (current_mark.offset_in_compressed_file > right_mark_in_file.offset_in_compressed_file)
{
/// If it is in current stripe return it to reduce amount of read data.
if (current_mark < next_stripe_right_mark_in_file)
return current_mark.offset_in_compressed_file;
/// Otherwise return start of new stripe as an upper bound.
break;
}
}
return next_stripe_right_mark_in_file.offset_in_compressed_file;
}
std::pair<size_t, size_t>
MergeTreeReaderStreamMultipleColumns::estimateMarkRangeBytesOneColumn(const MarkRanges & mark_ranges, size_t column_position) const
{
assert(marks_getter != nullptr);
/// As a maximal range we return the maximal size of a whole stripe.
size_t max_range_bytes = 0;
size_t sum_range_bytes = 0;
for (const auto & mark_range : mark_ranges)
{
auto start_of_stripe_mark = marks_getter->getMark(mark_range.begin, column_position);
auto start_of_next_stripe_mark = getStartOfNextStripeMark(mark_range.begin, column_position);
for (size_t mark = mark_range.begin; mark < mark_range.end; ++mark)
{
const auto & current_mark = marks_getter->getMark(mark, column_position);
/// We found a start of new stripe, now update values.
if (current_mark > start_of_next_stripe_mark)
{
auto current_range_bytes = getRightOffsetOneColumn(mark, column_position) - start_of_stripe_mark.offset_in_compressed_file;
max_range_bytes = std::max(max_range_bytes, current_range_bytes);
sum_range_bytes += current_range_bytes;
start_of_stripe_mark = current_mark;
start_of_next_stripe_mark = getStartOfNextStripeMark(mark, column_position);
}
}
auto current_range_bytes = getRightOffsetOneColumn(mark_range.end, column_position) - start_of_stripe_mark.offset_in_compressed_file;
max_range_bytes = std::max(max_range_bytes, current_range_bytes);
sum_range_bytes += current_range_bytes;
}
return {max_range_bytes, sum_range_bytes};
}
MarkInCompressedFile MergeTreeReaderStreamMultipleColumns::getStartOfNextStripeMark(size_t row_index, size_t column_position) const
{
const auto & current_mark = marks_getter->getMark(row_index, column_position);
if (marks_getter->getNumColumns() == 1)
return MarkInCompressedFile{file_size, 0};
if (column_position + 1 == marks_getter->getNumColumns())
{
/**
* In case of the last column (c3), we have the following picture:
* c1 c2 c3
* x x x
* (row_index, 0) -> o x o <- (row_index, column_position)
* x x x
* ------- <- start of new stripe
* what we are -> o x x
* looking for x x x
* x x x
* -------
* So, we need to iterate forward.
*/
size_t mark_index = row_index + 1;
while (mark_index < marks_count && marks_getter->getMark(mark_index, 0) <= current_mark)
++mark_index;
return mark_index == marks_count
? MarkInCompressedFile{file_size, 0}
: marks_getter->getMark(mark_index, 0);
}
/**
* Otherwise, we have the following picture:
* c1 c2 c3
* x x o <- what we are looking for
* (row, column) --> o o <- (row, column + 1)
* x x x
* ------- <- start of new stripe
* So, we need to iterate backward.
*/
ssize_t mark_index = row_index;
while (mark_index >= 0 && marks_getter->getMark(mark_index, column_position + 1) >= current_mark)
--mark_index;
return marks_getter->getMark(mark_index + 1, column_position + 1);
}
size_t MergeTreeReaderStreamOneOfMultipleColumns::getRightOffset(size_t right_mark_non_included) const
{
return getRightOffsetOneColumn(right_mark_non_included, column_position);
}
std::pair<size_t, size_t> MergeTreeReaderStreamOneOfMultipleColumns::estimateMarkRangeBytes(const MarkRanges & mark_ranges) const
{
return estimateMarkRangeBytesOneColumn(mark_ranges, column_position);
}
size_t MergeTreeReaderStreamAllOfMultipleColumns::getRightOffset(size_t right_mark_non_included) const
{
return getRightOffsetOneColumn(right_mark_non_included, marks_loader->getNumColumns() - 1);
}
std::pair<size_t, size_t> MergeTreeReaderStreamAllOfMultipleColumns::estimateMarkRangeBytes(const MarkRanges & mark_ranges) const
{
size_t max_range_bytes = 0;
size_t sum_range_bytes = 0;
for (size_t i = 0; i < marks_getter->getNumColumns(); ++i)
{
auto [current_max, current_sum] = estimateMarkRangeBytesOneColumn(mark_ranges, i);
max_range_bytes = std::max(max_range_bytes, current_max);
sum_range_bytes += current_sum;
}
return {max_range_bytes, sum_range_bytes};
}
} }

View File

@ -14,27 +14,31 @@
namespace DB namespace DB
{ {
/// Class for reading a single column (or index). /// Basic and the most low-level class
/// for reading single columns or indexes.
class MergeTreeReaderStream class MergeTreeReaderStream
{ {
public: public:
MergeTreeReaderStream( MergeTreeReaderStream(
MergeTreeDataPartInfoForReaderPtr data_part_reader_, DataPartStoragePtr data_part_storage_,
const String & path_prefix_, const String & path_prefix_,
const String & data_file_extension_, const String & data_file_extension_,
size_t marks_count_, size_t marks_count_,
const MarkRanges & all_mark_ranges, const MarkRanges & all_mark_ranges_,
const MergeTreeReaderSettings & settings_, const MergeTreeReaderSettings & settings_,
MarkCache * mark_cache, UncompressedCache * uncompressed_cache_,
UncompressedCache * uncompressed_cache,
size_t file_size_, size_t file_size_,
const MergeTreeIndexGranularityInfo * index_granularity_info_, MergeTreeMarksLoaderPtr marks_loader_,
const ReadBufferFromFileBase::ProfileCallback & profile_callback, const ReadBufferFromFileBase::ProfileCallback & profile_callback_,
clockid_t clock_type, clockid_t clock_type_);
bool is_low_cardinality_dictionary_,
ThreadPool * load_marks_cache_threadpool_);
void seekToMark(size_t index); virtual ~MergeTreeReaderStream() = default;
/// Seeks to start of @row_index mark. Column position is implementation defined.
virtual void seekToMark(size_t row_index) = 0;
/// Seeks to exact mark in file.
void seekToMarkAndColumn(size_t row_index, size_t column_position);
void seekToStart(); void seekToStart();
@ -48,39 +52,111 @@ public:
CompressedReadBufferBase * getCompressedDataBuffer(); CompressedReadBufferBase * getCompressedDataBuffer();
private: private:
void init(); /// Returns offset in file up to which it's needed to read file to read all rows up to @right_mark mark.
size_t getRightOffset(size_t right_mark); virtual size_t getRightOffset(size_t right_mark) const = 0;
/// Returns estimated max amount of bytes to read among mark ranges (which is used as size for read buffer)
/// and total amount of bytes to read in all mark ranges.
virtual std::pair<size_t, size_t> estimateMarkRangeBytes(const MarkRanges & mark_ranges) const = 0;
const MergeTreeReaderSettings settings;
const ReadBufferFromFileBase::ProfileCallback profile_callback; const ReadBufferFromFileBase::ProfileCallback profile_callback;
clockid_t clock_type; const clockid_t clock_type;
const MarkRanges all_mark_ranges; const MarkRanges all_mark_ranges;
size_t file_size;
UncompressedCache * uncompressed_cache;
DataPartStoragePtr data_part_storage; const DataPartStoragePtr data_part_storage;
std::string path_prefix; const std::string path_prefix;
std::string data_file_extension; const std::string data_file_extension;
bool is_low_cardinality_dictionary = false;
size_t marks_count;
UncompressedCache * const uncompressed_cache;
ReadBuffer * data_buffer; ReadBuffer * data_buffer;
CompressedReadBufferBase * compressed_data_buffer; CompressedReadBufferBase * compressed_data_buffer;
MarkCache * mark_cache;
bool save_marks_in_cache;
bool initialized = false; bool initialized = false;
std::optional<size_t> last_right_offset; std::optional<size_t> last_right_offset;
const MergeTreeIndexGranularityInfo * index_granularity_info;
std::unique_ptr<CachedCompressedReadBuffer> cached_buffer; std::unique_ptr<CachedCompressedReadBuffer> cached_buffer;
std::unique_ptr<CompressedReadBufferFromFile> non_cached_buffer; std::unique_ptr<CompressedReadBufferFromFile> non_cached_buffer;
MergeTreeMarksLoader marks_loader; protected:
void init();
const MergeTreeReaderSettings settings;
const size_t marks_count;
const size_t file_size;
const MergeTreeMarksLoaderPtr marks_loader;
MergeTreeMarksGetterPtr marks_getter;
};
/// Class for reading a single column (or index) from file
/// that contains a single column (for wide parts).
class MergeTreeReaderStreamSingleColumn : public MergeTreeReaderStream
{
public:
template <typename... Args>
explicit MergeTreeReaderStreamSingleColumn(Args &&... args)
: MergeTreeReaderStream{std::forward<Args>(args)...}
{
}
size_t getRightOffset(size_t right_mark_non_included) const override;
std::pair<size_t, size_t> estimateMarkRangeBytes(const MarkRanges & mark_ranges) const override;
void seekToMark(size_t row_index) override { seekToMarkAndColumn(row_index, 0); }
};
/// Base class for reading from file that contains multiple columns.
/// It is used to read from compact parts.
/// See more details about data layout in MergeTreeDataPartCompact.h.
class MergeTreeReaderStreamMultipleColumns : public MergeTreeReaderStream
{
public:
template <typename... Args>
explicit MergeTreeReaderStreamMultipleColumns(Args &&... args)
: MergeTreeReaderStream{std::forward<Args>(args)...}
{
}
protected:
size_t getRightOffsetOneColumn(size_t right_mark_non_included, size_t column_position) const;
std::pair<size_t, size_t> estimateMarkRangeBytesOneColumn(const MarkRanges & mark_ranges, size_t column_position) const;
MarkInCompressedFile getStartOfNextStripeMark(size_t row_index, size_t column_position) const;
};
/// Class for reading a single column from file that contains multiple columns
/// (for parallel reading from compact parts with large stripes).
class MergeTreeReaderStreamOneOfMultipleColumns : public MergeTreeReaderStreamMultipleColumns
{
public:
template <typename... Args>
explicit MergeTreeReaderStreamOneOfMultipleColumns(size_t column_position_, Args &&... args)
: MergeTreeReaderStreamMultipleColumns{std::forward<Args>(args)...}
, column_position(column_position_)
{
}
size_t getRightOffset(size_t right_mark_non_included) const override;
std::pair<size_t, size_t> estimateMarkRangeBytes(const MarkRanges & mark_ranges) const override;
void seekToMark(size_t row_index) override { seekToMarkAndColumn(row_index, column_position); }
private:
const size_t column_position;
};
/// Class for reading multiple columns from file that contains multiple columns
/// (for reading from compact parts with small stripes).
class MergeTreeReaderStreamAllOfMultipleColumns : public MergeTreeReaderStreamMultipleColumns
{
public:
template <typename... Args>
explicit MergeTreeReaderStreamAllOfMultipleColumns(Args &&... args)
: MergeTreeReaderStreamMultipleColumns{std::forward<Args>(args)...}
{
}
size_t getRightOffset(size_t right_mark_non_included) const override;
std::pair<size_t, size_t> estimateMarkRangeBytes(const MarkRanges & mark_ranges) const override;
void seekToMark(size_t row_index) override { seekToMarkAndColumn(row_index, 0); }
}; };
} }

View File

@ -225,18 +225,29 @@ void MergeTreeReaderWide::addStreams(
return; return;
} }
has_any_stream = true;
bool is_lc_dict = substream_path.size() > 1 && substream_path[substream_path.size() - 2].type == ISerialization::Substream::Type::DictionaryKeys;
auto context = data_part_info_for_read->getContext(); auto context = data_part_info_for_read->getContext();
auto * load_marks_threadpool = settings.read_settings.load_marks_asynchronously ? &context->getLoadMarksThreadpool() : nullptr; auto * load_marks_threadpool = settings.read_settings.load_marks_asynchronously ? &context->getLoadMarksThreadpool() : nullptr;
streams.emplace(*stream_name, std::make_unique<MergeTreeReaderStream>( auto marks_loader = std::make_shared<MergeTreeMarksLoader>(
data_part_info_for_read, *stream_name, DATA_FILE_EXTENSION, data_part_info_for_read,
data_part_info_for_read->getMarksCount(), all_mark_ranges, settings, mark_cache, mark_cache,
data_part_info_for_read->getIndexGranularityInfo().getMarksFilePath(*stream_name),
data_part_info_for_read->getMarksCount(),
data_part_info_for_read->getIndexGranularityInfo(),
settings.save_marks_in_cache,
settings.read_settings,
load_marks_threadpool,
/*num_columns_in_mark=*/ 1);
has_any_stream = true;
auto stream_settings = settings;
stream_settings.is_low_cardinality_dictionary = substream_path.size() > 1 && substream_path[substream_path.size() - 2].type == ISerialization::Substream::Type::DictionaryKeys;
streams.emplace(*stream_name, std::make_unique<MergeTreeReaderStreamSingleColumn>(
data_part_info_for_read->getDataPartStorage(), *stream_name, DATA_FILE_EXTENSION,
data_part_info_for_read->getMarksCount(), all_mark_ranges, stream_settings,
uncompressed_cache, data_part_info_for_read->getFileSizeOrZero(*stream_name + DATA_FILE_EXTENSION), uncompressed_cache, data_part_info_for_read->getFileSizeOrZero(*stream_name + DATA_FILE_EXTENSION),
&data_part_info_for_read->getIndexGranularityInfo(), std::move(marks_loader), profile_callback, clock_type));
profile_callback, clock_type, is_lc_dict, load_marks_threadpool));
}; };
serialization->enumerateStreams(callback); serialization->enumerateStreams(callback);

View File

@ -184,9 +184,11 @@ private:
auto & compressed_data = compressed->getData(); auto & compressed_data = compressed->getData();
auto & uncompressed_data = uncompressed->getData(); auto & uncompressed_data = uncompressed->getData();
auto marks_getter = marks_loader->loadMarks();
for (size_t i = 0; i < num_rows; ++i) for (size_t i = 0; i < num_rows; ++i)
{ {
auto mark = marks_loader->getMark(i, col_idx); auto mark = marks_getter->getMark(i, col_idx);
compressed_data[i] = mark.offset_in_compressed_file; compressed_data[i] = mark.offset_in_compressed_file;
uncompressed_data[i] = mark.offset_in_decompressed_block; uncompressed_data[i] = mark.offset_in_decompressed_block;

View File

@ -34,7 +34,7 @@ void StorageSystemAsynchronousInserts::fillData(MutableColumns & res_columns, Co
{ {
using namespace std::chrono; using namespace std::chrono;
auto * insert_queue = context->getAsynchronousInsertQueue(); auto * insert_queue = context->tryGetAsynchronousInsertQueue();
if (!insert_queue) if (!insert_queue)
return; return;

View File

@ -4,10 +4,12 @@
#include <DataTypes/DataTypesNumber.h> #include <DataTypes/DataTypesNumber.h>
#include <DataTypes/DataTypeDateTime.h> #include <DataTypes/DataTypeDateTime.h>
#include <DataTypes/DataTypeNullable.h> #include <DataTypes/DataTypeNullable.h>
#include <DataTypes/DataTypeUUID.h>
#include <Storages/IStorage.h> #include <Storages/IStorage.h>
#include <Storages/MergeTree/DataPartStorageOnDiskFull.h> #include <Storages/MergeTree/DataPartStorageOnDiskFull.h>
#include <Storages/System/StorageSystemPartsBase.h> #include <Storages/System/StorageSystemPartsBase.h>
#include <Storages/System/getQueriedColumnsMaskAndHeader.h> #include <Storages/System/getQueriedColumnsMaskAndHeader.h>
#include <Storages/VirtualColumnUtils.h>
#include <Processors/Sources/SourceFromSingleChunk.h> #include <Processors/Sources/SourceFromSingleChunk.h>
#include <QueryPipeline/Pipe.h> #include <QueryPipeline/Pipe.h>
#include <IO/SharedThreadPools.h> #include <IO/SharedThreadPools.h>
@ -320,7 +322,7 @@ protected:
std::shared_ptr<StorageSystemDetachedParts> storage; std::shared_ptr<StorageSystemDetachedParts> storage;
std::vector<UInt8> columns_mask; std::vector<UInt8> columns_mask;
const ActionsDAG::Node * predicate = nullptr; ActionsDAGPtr filter;
const size_t max_block_size; const size_t max_block_size;
const size_t num_streams; const size_t num_streams;
}; };
@ -329,7 +331,20 @@ void ReadFromSystemDetachedParts::applyFilters(ActionDAGNodes added_filter_nodes
{ {
filter_actions_dag = ActionsDAG::buildFilterActionsDAG(added_filter_nodes.nodes); filter_actions_dag = ActionsDAG::buildFilterActionsDAG(added_filter_nodes.nodes);
if (filter_actions_dag) if (filter_actions_dag)
predicate = filter_actions_dag->getOutputs().at(0); {
const auto * predicate = filter_actions_dag->getOutputs().at(0);
Block block;
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeString>(), "database"));
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeString>(), "table"));
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeString>(), "engine"));
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeUInt8>(), "active"));
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeUUID>(), "uuid"));
filter = VirtualColumnUtils::splitFilterDagForAllowedInputs(predicate, &block);
if (filter)
VirtualColumnUtils::buildSetsForDAG(filter, context);
}
} }
void StorageSystemDetachedParts::read( void StorageSystemDetachedParts::read(
@ -358,7 +373,7 @@ void StorageSystemDetachedParts::read(
void ReadFromSystemDetachedParts::initializePipeline(QueryPipelineBuilder & pipeline, const BuildQueryPipelineSettings &) void ReadFromSystemDetachedParts::initializePipeline(QueryPipelineBuilder & pipeline, const BuildQueryPipelineSettings &)
{ {
auto state = std::make_shared<SourceState>(StoragesInfoStream(predicate, context)); auto state = std::make_shared<SourceState>(StoragesInfoStream(nullptr, filter, context));
Pipe pipe; Pipe pipe;

View File

@ -11,7 +11,7 @@ namespace DB
{ {
StoragesDroppedInfoStream::StoragesDroppedInfoStream(const ActionsDAG::Node * predicate, ContextPtr context) StoragesDroppedInfoStream::StoragesDroppedInfoStream(const ActionsDAGPtr & filter, ContextPtr context)
: StoragesInfoStreamBase(context) : StoragesInfoStreamBase(context)
{ {
/// Will apply WHERE to subset of columns and then add more columns. /// Will apply WHERE to subset of columns and then add more columns.
@ -74,7 +74,8 @@ StoragesDroppedInfoStream::StoragesDroppedInfoStream(const ActionsDAG::Node * pr
if (block_to_filter.rows()) if (block_to_filter.rows())
{ {
/// Filter block_to_filter with columns 'database', 'table', 'engine', 'active'. /// Filter block_to_filter with columns 'database', 'table', 'engine', 'active'.
VirtualColumnUtils::filterBlockWithPredicate(predicate, block_to_filter, context); if (filter)
VirtualColumnUtils::filterBlockWithDAG(filter, block_to_filter, context);
rows = block_to_filter.rows(); rows = block_to_filter.rows();
} }

View File

@ -9,7 +9,7 @@ namespace DB
class StoragesDroppedInfoStream : public StoragesInfoStreamBase class StoragesDroppedInfoStream : public StoragesInfoStreamBase
{ {
public: public:
StoragesDroppedInfoStream(const ActionsDAG::Node * predicate, ContextPtr context); StoragesDroppedInfoStream(const ActionsDAGPtr & filter, ContextPtr context);
protected: protected:
bool tryLockTable(StoragesInfo &) override bool tryLockTable(StoragesInfo &) override
{ {
@ -30,9 +30,9 @@ public:
std::string getName() const override { return "SystemDroppedTablesParts"; } std::string getName() const override { return "SystemDroppedTablesParts"; }
protected: protected:
std::unique_ptr<StoragesInfoStreamBase> getStoragesInfoStream(const ActionsDAG::Node * predicate, ContextPtr context) override std::unique_ptr<StoragesInfoStreamBase> getStoragesInfoStream(const ActionsDAGPtr &, const ActionsDAGPtr & filter, ContextPtr context) override
{ {
return std::make_unique<StoragesDroppedInfoStream>(predicate, context); return std::make_unique<StoragesDroppedInfoStream>(filter, context);
} }
}; };

View File

@ -83,7 +83,7 @@ StoragesInfo::getProjectionParts(MergeTreeData::DataPartStateVector & state, boo
return data->getProjectionPartsVectorForInternalUsage({State::Active}, &state); return data->getProjectionPartsVectorForInternalUsage({State::Active}, &state);
} }
StoragesInfoStream::StoragesInfoStream(const ActionsDAG::Node * predicate, ContextPtr context) StoragesInfoStream::StoragesInfoStream(const ActionsDAGPtr & filter_by_database, const ActionsDAGPtr & filter_by_other_columns, ContextPtr context)
: StoragesInfoStreamBase(context) : StoragesInfoStreamBase(context)
{ {
/// Will apply WHERE to subset of columns and then add more columns. /// Will apply WHERE to subset of columns and then add more columns.
@ -115,7 +115,8 @@ StoragesInfoStream::StoragesInfoStream(const ActionsDAG::Node * predicate, Conte
std::move(database_column_mut), std::make_shared<DataTypeString>(), "database")); std::move(database_column_mut), std::make_shared<DataTypeString>(), "database"));
/// Filter block_to_filter with column 'database'. /// Filter block_to_filter with column 'database'.
VirtualColumnUtils::filterBlockWithPredicate(predicate, block_to_filter, context); if (filter_by_database)
VirtualColumnUtils::filterBlockWithDAG(filter_by_database, block_to_filter, context);
rows = block_to_filter.rows(); rows = block_to_filter.rows();
/// Block contains new columns, update database_column. /// Block contains new columns, update database_column.
@ -189,19 +190,20 @@ StoragesInfoStream::StoragesInfoStream(const ActionsDAG::Node * predicate, Conte
block_to_filter.insert(ColumnWithTypeAndName(std::move(table_column_mut), std::make_shared<DataTypeString>(), "table")); block_to_filter.insert(ColumnWithTypeAndName(std::move(table_column_mut), std::make_shared<DataTypeString>(), "table"));
block_to_filter.insert(ColumnWithTypeAndName(std::move(engine_column_mut), std::make_shared<DataTypeString>(), "engine")); block_to_filter.insert(ColumnWithTypeAndName(std::move(engine_column_mut), std::make_shared<DataTypeString>(), "engine"));
block_to_filter.insert(ColumnWithTypeAndName(std::move(active_column_mut), std::make_shared<DataTypeUInt8>(), "active")); block_to_filter.insert(ColumnWithTypeAndName(std::move(active_column_mut), std::make_shared<DataTypeUInt8>(), "active"));
block_to_filter.insert(ColumnWithTypeAndName(std::move(storage_uuid_column_mut), std::make_shared<DataTypeUUID>(), "storage_uuid")); block_to_filter.insert(ColumnWithTypeAndName(std::move(storage_uuid_column_mut), std::make_shared<DataTypeUUID>(), "uuid"));
if (rows) if (rows)
{ {
/// Filter block_to_filter with columns 'database', 'table', 'engine', 'active'. /// Filter block_to_filter with columns 'database', 'table', 'engine', 'active'.
VirtualColumnUtils::filterBlockWithPredicate(predicate, block_to_filter, context); if (filter_by_other_columns)
VirtualColumnUtils::filterBlockWithDAG(filter_by_other_columns, block_to_filter, context);
rows = block_to_filter.rows(); rows = block_to_filter.rows();
} }
database_column = block_to_filter.getByName("database").column; database_column = block_to_filter.getByName("database").column;
table_column = block_to_filter.getByName("table").column; table_column = block_to_filter.getByName("table").column;
active_column = block_to_filter.getByName("active").column; active_column = block_to_filter.getByName("active").column;
storage_uuid_column = block_to_filter.getByName("storage_uuid").column; storage_uuid_column = block_to_filter.getByName("uuid").column;
} }
class ReadFromSystemPartsBase : public SourceStepWithFilter class ReadFromSystemPartsBase : public SourceStepWithFilter
@ -226,7 +228,8 @@ protected:
std::shared_ptr<StorageSystemPartsBase> storage; std::shared_ptr<StorageSystemPartsBase> storage;
std::vector<UInt8> columns_mask; std::vector<UInt8> columns_mask;
const bool has_state_column; const bool has_state_column;
const ActionsDAG::Node * predicate = nullptr; ActionsDAGPtr filter_by_database;
ActionsDAGPtr filter_by_other_columns;
}; };
ReadFromSystemPartsBase::ReadFromSystemPartsBase( ReadFromSystemPartsBase::ReadFromSystemPartsBase(
@ -254,7 +257,25 @@ void ReadFromSystemPartsBase::applyFilters(ActionDAGNodes added_filter_nodes)
{ {
filter_actions_dag = ActionsDAG::buildFilterActionsDAG(added_filter_nodes.nodes); filter_actions_dag = ActionsDAG::buildFilterActionsDAG(added_filter_nodes.nodes);
if (filter_actions_dag) if (filter_actions_dag)
predicate = filter_actions_dag->getOutputs().at(0); {
const auto * predicate = filter_actions_dag->getOutputs().at(0);
Block block;
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeString>(), "database"));
filter_by_database = VirtualColumnUtils::splitFilterDagForAllowedInputs(predicate, &block);
if (filter_by_database)
VirtualColumnUtils::buildSetsForDAG(filter_by_database, context);
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeString>(), "table"));
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeString>(), "engine"));
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeUInt8>(), "active"));
block.insert(ColumnWithTypeAndName({}, std::make_shared<DataTypeUUID>(), "uuid"));
filter_by_other_columns = VirtualColumnUtils::splitFilterDagForAllowedInputs(predicate, &block);
if (filter_by_other_columns)
VirtualColumnUtils::buildSetsForDAG(filter_by_other_columns, context);
}
} }
void StorageSystemPartsBase::read( void StorageSystemPartsBase::read(
@ -288,7 +309,7 @@ void StorageSystemPartsBase::read(
void ReadFromSystemPartsBase::initializePipeline(QueryPipelineBuilder & pipeline, const BuildQueryPipelineSettings &) void ReadFromSystemPartsBase::initializePipeline(QueryPipelineBuilder & pipeline, const BuildQueryPipelineSettings &)
{ {
auto stream = storage->getStoragesInfoStream(predicate, context); auto stream = storage->getStoragesInfoStream(filter_by_database, filter_by_other_columns, context);
auto header = getOutputStream().header; auto header = getOutputStream().header;
MutableColumns res_columns = header.cloneEmptyColumns(); MutableColumns res_columns = header.cloneEmptyColumns();

View File

@ -115,7 +115,7 @@ protected:
class StoragesInfoStream : public StoragesInfoStreamBase class StoragesInfoStream : public StoragesInfoStreamBase
{ {
public: public:
StoragesInfoStream(const ActionsDAG::Node * predicate, ContextPtr context); StoragesInfoStream(const ActionsDAGPtr & filter_by_database, const ActionsDAGPtr & filter_by_other_columns, ContextPtr context);
}; };
/** Implements system table 'parts' which allows to get information about data parts for tables of MergeTree family. /** Implements system table 'parts' which allows to get information about data parts for tables of MergeTree family.
@ -145,9 +145,9 @@ protected:
StorageSystemPartsBase(const StorageID & table_id_, ColumnsDescription && columns); StorageSystemPartsBase(const StorageID & table_id_, ColumnsDescription && columns);
virtual std::unique_ptr<StoragesInfoStreamBase> getStoragesInfoStream(const ActionsDAG::Node * predicate, ContextPtr context) virtual std::unique_ptr<StoragesInfoStreamBase> getStoragesInfoStream(const ActionsDAGPtr & filter_by_database, const ActionsDAGPtr & filter_by_other_columns, ContextPtr context)
{ {
return std::make_unique<StoragesInfoStream>(predicate, context); return std::make_unique<StoragesInfoStream>(filter_by_database, filter_by_other_columns, context);
} }
virtual void virtual void

View File

@ -135,8 +135,12 @@ public:
auto & [_, storage, promise, with_zk_fields] = req; auto & [_, storage, promise, with_zk_fields] = req;
auto get_status_task = [this, storage, with_zk_fields, promise] () mutable auto get_status_task = [this, storage, with_zk_fields, promise, thread_group = CurrentThread::getGroup()]() mutable
{ {
SCOPE_EXIT_SAFE(if (thread_group) CurrentThread::detachFromGroupIfNotDetached(););
if (thread_group)
CurrentThread::attachToGroupIfDetached(thread_group);
try try
{ {
ReplicatedTableStatus status; ReplicatedTableStatus status;

View File

@ -70,6 +70,7 @@ void StorageSystemServerSettings::fillData(MutableColumns & res_columns, Context
{"max_concurrent_queries", {std::to_string(context->getProcessList().getMaxSize()), ChangeableWithoutRestart::Yes}}, {"max_concurrent_queries", {std::to_string(context->getProcessList().getMaxSize()), ChangeableWithoutRestart::Yes}},
{"max_concurrent_insert_queries", {std::to_string(context->getProcessList().getMaxInsertQueriesAmount()), ChangeableWithoutRestart::Yes}}, {"max_concurrent_insert_queries", {std::to_string(context->getProcessList().getMaxInsertQueriesAmount()), ChangeableWithoutRestart::Yes}},
{"max_concurrent_select_queries", {std::to_string(context->getProcessList().getMaxSelectQueriesAmount()), ChangeableWithoutRestart::Yes}}, {"max_concurrent_select_queries", {std::to_string(context->getProcessList().getMaxSelectQueriesAmount()), ChangeableWithoutRestart::Yes}},
{"max_waiting_queries", {std::to_string(context->getProcessList().getMaxWaitingQueriesAmount()), ChangeableWithoutRestart::Yes}},
{"background_buffer_flush_schedule_pool_size", {std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundBufferFlushSchedulePoolSize)), ChangeableWithoutRestart::IncreaseOnly}}, {"background_buffer_flush_schedule_pool_size", {std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundBufferFlushSchedulePoolSize)), ChangeableWithoutRestart::IncreaseOnly}},
{"background_schedule_pool_size", {std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundSchedulePoolSize)), ChangeableWithoutRestart::IncreaseOnly}}, {"background_schedule_pool_size", {std::to_string(CurrentMetrics::get(CurrentMetrics::BackgroundSchedulePoolSize)), ChangeableWithoutRestart::IncreaseOnly}},

View File

@ -53,9 +53,9 @@ namespace DB
namespace VirtualColumnUtils namespace VirtualColumnUtils
{ {
static void makeSets(const ExpressionActionsPtr & actions, const ContextPtr & context) void buildSetsForDAG(const ActionsDAGPtr & dag, const ContextPtr & context)
{ {
for (const auto & node : actions->getNodes()) for (const auto & node : dag->getNodes())
{ {
if (node.type == ActionsDAG::ActionType::COLUMN) if (node.type == ActionsDAG::ActionType::COLUMN)
{ {
@ -78,8 +78,8 @@ static void makeSets(const ExpressionActionsPtr & actions, const ContextPtr & co
void filterBlockWithDAG(ActionsDAGPtr dag, Block & block, ContextPtr context) void filterBlockWithDAG(ActionsDAGPtr dag, Block & block, ContextPtr context)
{ {
buildSetsForDAG(dag, context);
auto actions = std::make_shared<ExpressionActions>(dag); auto actions = std::make_shared<ExpressionActions>(dag);
makeSets(actions, context);
Block block_with_filter = block; Block block_with_filter = block;
actions->execute(block_with_filter, /*dry_run=*/ false, /*allow_duplicates_in_input=*/ true); actions->execute(block_with_filter, /*dry_run=*/ false, /*allow_duplicates_in_input=*/ true);

View File

@ -25,6 +25,9 @@ void filterBlockWithPredicate(const ActionsDAG::Node * predicate, Block & block,
/// Just filters block. Block should contain all the required columns. /// Just filters block. Block should contain all the required columns.
void filterBlockWithDAG(ActionsDAGPtr dag, Block & block, ContextPtr context); void filterBlockWithDAG(ActionsDAGPtr dag, Block & block, ContextPtr context);
/// Builds sets used by ActionsDAG inplace.
void buildSetsForDAG(const ActionsDAGPtr & dag, const ContextPtr & context);
/// Recursively checks if all functions used in DAG are deterministic in scope of query. /// Recursively checks if all functions used in DAG are deterministic in scope of query.
bool isDeterministicInScopeOfQuery(const ActionsDAG::Node * node); bool isDeterministicInScopeOfQuery(const ActionsDAG::Node * node);

View File

@ -1248,9 +1248,7 @@ def _configure_jobs(
for token_ in ci_controlling_tokens: for token_ in ci_controlling_tokens:
label_config = CI_CONFIG.get_label_config(token_) label_config = CI_CONFIG.get_label_config(token_)
assert label_config, f"Unknonwn token [{token_}]" assert label_config, f"Unknonwn token [{token_}]"
print( print(f"NOTE: CI modifier: [{token_}], add jobs: [{label_config.run_jobs}]")
f"NOTE: CI controlling token: [{ci_controlling_tokens}], add jobs: [{label_config.run_jobs}]"
)
jobs_to_do_requested += label_config.run_jobs jobs_to_do_requested += label_config.run_jobs
# handle specific job requests # handle specific job requests
@ -1264,7 +1262,7 @@ def _configure_jobs(
for job in requested_jobs: for job in requested_jobs:
job_with_parents = CI_CONFIG.get_job_with_parents(job) job_with_parents = CI_CONFIG.get_job_with_parents(job)
print( print(
f"NOTE: CI controlling token: [#job_{job}], add jobs: [{job_with_parents}]" f"NOTE: CI modifier: [#job_{job}], add jobs: [{job_with_parents}]"
) )
# always add requested job itself, even if it could be skipped # always add requested job itself, even if it could be skipped
jobs_to_do_requested.append(job_with_parents[0]) jobs_to_do_requested.append(job_with_parents[0])
@ -1273,6 +1271,7 @@ def _configure_jobs(
jobs_to_do_requested.append(parent) jobs_to_do_requested.append(parent)
if jobs_to_do_requested: if jobs_to_do_requested:
jobs_to_do_requested = list(set(jobs_to_do_requested))
print( print(
f"NOTE: Only specific job(s) were requested by commit message tokens: [{jobs_to_do_requested}]" f"NOTE: Only specific job(s) were requested by commit message tokens: [{jobs_to_do_requested}]"
) )
@ -1408,7 +1407,7 @@ def _update_gh_statuses_action(indata: Dict, s3: S3Helper) -> None:
def _fetch_commit_tokens(message: str, pr_info: PRInfo) -> List[str]: def _fetch_commit_tokens(message: str, pr_info: PRInfo) -> List[str]:
pattern = r"([^!]|^)#(\w+)" pattern = r"(#|- \[x\] +<!---)(\w+)"
matches = [match[-1] for match in re.findall(pattern, message)] matches = [match[-1] for match in re.findall(pattern, message)]
res = [ res = [
match match

View File

@ -46,9 +46,14 @@ class Labels(metaclass=WithIter):
NO_MERGE_COMMIT = "no_merge_commit" NO_MERGE_COMMIT = "no_merge_commit"
NO_CI_CACHE = "no_ci_cache" NO_CI_CACHE = "no_ci_cache"
CI_SET_REDUCED = "ci_set_reduced" CI_SET_REDUCED = "ci_set_reduced"
CI_SET_FAST = "ci_set_fast"
CI_SET_ARM = "ci_set_arm" CI_SET_ARM = "ci_set_arm"
CI_SET_INTEGRATION = "ci_set_integration" CI_SET_INTEGRATION = "ci_set_integration"
CI_SET_ANALYZER = "ci_set_analyzer" CI_SET_ANALYZER = "ci_set_analyzer"
CI_SET_STATLESS = "ci_set_stateless"
CI_SET_STATEFUL = "ci_set_stateful"
CI_SET_STATLESS_ASAN = "ci_set_stateless_asan"
CI_SET_STATEFUL_ASAN = "ci_set_stateful_asan"
libFuzzer = "libFuzzer" libFuzzer = "libFuzzer"
@ -380,7 +385,11 @@ upgrade_check_digest = DigestConfig(
docker=["clickhouse/upgrade-check"], docker=["clickhouse/upgrade-check"],
) )
integration_check_digest = DigestConfig( integration_check_digest = DigestConfig(
include_paths=["./tests/ci/integration_test_check.py", "./tests/integration"], include_paths=[
"./tests/ci/integration_test_check.py",
"./tests/ci/integration_tests_runner.py",
"./tests/integration/",
],
exclude_files=[".md"], exclude_files=[".md"],
docker=IMAGES.copy(), docker=IMAGES.copy(),
) )
@ -809,9 +818,15 @@ class CIConfig:
CI_CONFIG = CIConfig( CI_CONFIG = CIConfig(
label_configs={ label_configs={
Labels.DO_NOT_TEST_LABEL: LabelConfig(run_jobs=[JobNames.STYLE_CHECK]), Labels.DO_NOT_TEST_LABEL: LabelConfig(run_jobs=[JobNames.STYLE_CHECK]),
Labels.CI_SET_FAST: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
]
),
Labels.CI_SET_ARM: LabelConfig( Labels.CI_SET_ARM: LabelConfig(
run_jobs=[ run_jobs=[
# JobNames.STYLE_CHECK, JobNames.STYLE_CHECK,
Build.PACKAGE_AARCH64, Build.PACKAGE_AARCH64,
JobNames.INTEGRATION_TEST_ARM, JobNames.INTEGRATION_TEST_ARM,
] ]
@ -833,6 +848,38 @@ CI_CONFIG = CIConfig(
JobNames.INTEGRATION_TEST_ASAN_ANALYZER, JobNames.INTEGRATION_TEST_ASAN_ANALYZER,
] ]
), ),
Labels.CI_SET_STATLESS: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
Build.PACKAGE_RELEASE,
JobNames.STATELESS_TEST_RELEASE,
]
),
Labels.CI_SET_STATLESS_ASAN: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
Build.PACKAGE_ASAN,
JobNames.STATELESS_TEST_ASAN,
]
),
Labels.CI_SET_STATEFUL: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
Build.PACKAGE_RELEASE,
JobNames.STATEFUL_TEST_RELEASE,
]
),
Labels.CI_SET_STATEFUL_ASAN: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
Build.PACKAGE_ASAN,
JobNames.STATEFUL_TEST_ASAN,
]
),
Labels.CI_SET_REDUCED: LabelConfig( Labels.CI_SET_REDUCED: LabelConfig(
run_jobs=[ run_jobs=[
job job
@ -844,6 +891,7 @@ CI_CONFIG = CIConfig(
"tsan", "tsan",
"msan", "msan",
"ubsan", "ubsan",
"coverage",
# skip build report jobs as not all builds will be done # skip build report jobs as not all builds will be done
"build check", "build check",
) )

View File

@ -25,7 +25,8 @@ from report import (
read_test_results, read_test_results,
) )
from stopwatch import Stopwatch from stopwatch import Stopwatch
from tee_popen import TeePopen
import integration_tests_runner as runner
def get_json_params_dict( def get_json_params_dict(
@ -206,11 +207,8 @@ def main():
json_params.write(params_text) json_params.write(params_text)
logging.info("Parameters file %s is written: %s", json_path, params_text) logging.info("Parameters file %s is written: %s", json_path, params_text)
output_path_log = result_path / "main_script_log.txt" for k, v in my_env.items():
os.environ[k] = v
runner_path = repo_path / "tests" / "integration" / "ci-runner.py"
run_command = f"sudo -E {runner_path}"
logging.info("Going to run command: `%s`", run_command)
logging.info( logging.info(
"ENV parameters for runner:\n%s", "ENV parameters for runner:\n%s",
"\n".join( "\n".join(
@ -218,31 +216,13 @@ def main():
), ),
) )
integration_infrastructure_fail = False try:
with TeePopen(run_command, output_path_log, my_env) as process: runner.run()
retcode = process.wait() except Exception as e:
if retcode == 0: logging.error("Exception: %s", e)
logging.info("Run tests successfully") state, description, test_results, additional_logs = ERROR, "infrastructure error", [TestResult("infrastructure error", ERROR, stopwatch.duration_seconds)], [] # type: ignore
elif retcode == 13:
logging.warning(
"There were issues with infrastructure. Not writing status report to restart job."
)
integration_infrastructure_fail = True
sys.exit(1)
else:
logging.info("Some tests failed")
# subprocess.check_call(f"sudo chown -R ubuntu:ubuntu {temp_path}", shell=True)
if not integration_infrastructure_fail:
state, description, test_results, additional_logs = process_results(result_path)
else: else:
state, description, test_results, additional_logs = ( state, description, test_results, additional_logs = process_results(result_path)
ERROR,
"no description",
[TestResult("infrastructure error", ERROR, stopwatch.duration_seconds)],
[],
)
JobReport( JobReport(
description=description, description=description,
@ -250,7 +230,7 @@ def main():
status=state, status=state,
start_time=stopwatch.start_time_str, start_time=stopwatch.start_time_str,
duration=stopwatch.duration_seconds, duration=stopwatch.duration_seconds,
additional_files=[output_path_log] + additional_logs, additional_files=additional_logs,
).dump(to_file=args.report_to_file if args.report_to_file else None) ).dump(to_file=args.report_to_file if args.report_to_file else None)
if state != SUCCESS: if state != SUCCESS:

View File

@ -13,11 +13,13 @@ import string
import subprocess import subprocess
import sys import sys
import time import time
from typing import Any, Dict
import zlib # for crc32 import zlib # for crc32
from collections import defaultdict from collections import defaultdict
from itertools import chain from itertools import chain
from integration_test_images import IMAGES from integration_test_images import IMAGES
from env_helper import CI
MAX_RETRY = 1 MAX_RETRY = 1
NUM_WORKERS = 5 NUM_WORKERS = 5
@ -102,7 +104,7 @@ def get_counters(fname):
"PASSED": set([]), "PASSED": set([]),
"FAILED": set([]), "FAILED": set([]),
"SKIPPED": set([]), "SKIPPED": set([]),
} } # type: Dict[str, Any]
with open(fname, "r", encoding="utf-8") as out: with open(fname, "r", encoding="utf-8") as out:
for line in out: for line in out:
@ -292,7 +294,7 @@ class ClickhouseIntegrationTestsRunner:
return name + ":latest" return name + ":latest"
return name return name
def get_image_version(self, name: str): def get_image_version(self, name: str) -> Any:
if name in self.image_versions: if name in self.image_versions:
return self.image_versions[name] return self.image_versions[name]
logging.warning( logging.warning(
@ -380,15 +382,15 @@ class ClickhouseIntegrationTestsRunner:
os.chmod(CLICKHOUSE_ODBC_BRIDGE_BINARY_PATH, 0o777) os.chmod(CLICKHOUSE_ODBC_BRIDGE_BINARY_PATH, 0o777)
os.chmod(CLICKHOUSE_LIBRARY_BRIDGE_BINARY_PATH, 0o777) os.chmod(CLICKHOUSE_LIBRARY_BRIDGE_BINARY_PATH, 0o777)
shutil.copy( shutil.copy(
CLICKHOUSE_BINARY_PATH, os.getenv("CLICKHOUSE_TESTS_SERVER_BIN_PATH") CLICKHOUSE_BINARY_PATH, os.getenv("CLICKHOUSE_TESTS_SERVER_BIN_PATH") # type: ignore
) )
shutil.copy( shutil.copy(
CLICKHOUSE_ODBC_BRIDGE_BINARY_PATH, CLICKHOUSE_ODBC_BRIDGE_BINARY_PATH,
os.getenv("CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH"), os.getenv("CLICKHOUSE_TESTS_ODBC_BRIDGE_BIN_PATH"), # type: ignore
) )
shutil.copy( shutil.copy(
CLICKHOUSE_LIBRARY_BRIDGE_BINARY_PATH, CLICKHOUSE_LIBRARY_BRIDGE_BINARY_PATH,
os.getenv("CLICKHOUSE_TESTS_LIBRARY_BRIDGE_BIN_PATH"), os.getenv("CLICKHOUSE_TESTS_LIBRARY_BRIDGE_BIN_PATH"), # type: ignore
) )
@staticmethod @staticmethod
@ -466,7 +468,7 @@ class ClickhouseIntegrationTestsRunner:
@staticmethod @staticmethod
def group_test_by_file(tests): def group_test_by_file(tests):
result = {} result = {} # type: Dict
for test in tests: for test in tests:
test_file = test.split("::")[0] test_file = test.split("::")[0]
if test_file not in result: if test_file not in result:
@ -475,7 +477,7 @@ class ClickhouseIntegrationTestsRunner:
return result return result
@staticmethod @staticmethod
def _update_counters(main_counters, current_counters, broken_tests): def _update_counters(main_counters, current_counters):
for test in current_counters["PASSED"]: for test in current_counters["PASSED"]:
if test not in main_counters["PASSED"]: if test not in main_counters["PASSED"]:
if test in main_counters["FAILED"]: if test in main_counters["FAILED"]:
@ -483,21 +485,14 @@ class ClickhouseIntegrationTestsRunner:
if test in main_counters["BROKEN"]: if test in main_counters["BROKEN"]:
main_counters["BROKEN"].remove(test) main_counters["BROKEN"].remove(test)
if test not in broken_tests: main_counters["PASSED"].append(test)
main_counters["PASSED"].append(test)
else:
main_counters["NOT_FAILED"].append(test)
for state in ("ERROR", "FAILED"): for state in ("ERROR", "FAILED"):
for test in current_counters[state]: for test in current_counters[state]:
if test in main_counters["PASSED"]: if test in main_counters["PASSED"]:
main_counters["PASSED"].remove(test) main_counters["PASSED"].remove(test)
if test not in broken_tests: if test not in main_counters[state]:
if test not in main_counters[state]: main_counters[state].append(test)
main_counters[state].append(test)
else:
if test not in main_counters["BROKEN"]:
main_counters["BROKEN"].append(test)
for state in ("SKIPPED",): for state in ("SKIPPED",):
for test in current_counters[state]: for test in current_counters[state]:
@ -564,7 +559,6 @@ class ClickhouseIntegrationTestsRunner:
tests_in_group, tests_in_group,
num_tries, num_tries,
num_workers, num_workers,
broken_tests,
): ):
try: try:
return self.run_test_group( return self.run_test_group(
@ -573,7 +567,6 @@ class ClickhouseIntegrationTestsRunner:
tests_in_group, tests_in_group,
num_tries, num_tries,
num_workers, num_workers,
broken_tests,
) )
except Exception as e: except Exception as e:
logging.info("Failed to run %s:\n%s", test_group, e) logging.info("Failed to run %s:\n%s", test_group, e)
@ -582,8 +575,8 @@ class ClickhouseIntegrationTestsRunner:
"PASSED": [], "PASSED": [],
"FAILED": [], "FAILED": [],
"SKIPPED": [], "SKIPPED": [],
} } # type: Dict
tests_times = defaultdict(float) tests_times = defaultdict(float) # type: Dict
for test in tests_in_group: for test in tests_in_group:
counters["ERROR"].append(test) counters["ERROR"].append(test)
tests_times[test] = 0 tests_times[test] = 0
@ -596,7 +589,6 @@ class ClickhouseIntegrationTestsRunner:
tests_in_group, tests_in_group,
num_tries, num_tries,
num_workers, num_workers,
broken_tests,
): ):
counters = { counters = {
"ERROR": [], "ERROR": [],
@ -605,8 +597,8 @@ class ClickhouseIntegrationTestsRunner:
"SKIPPED": [], "SKIPPED": [],
"BROKEN": [], "BROKEN": [],
"NOT_FAILED": [], "NOT_FAILED": [],
} } # type: Dict
tests_times = defaultdict(float) tests_times = defaultdict(float) # type: Dict
if self.soft_deadline_time < time.time(): if self.soft_deadline_time < time.time():
for test in tests_in_group: for test in tests_in_group:
@ -706,7 +698,7 @@ class ClickhouseIntegrationTestsRunner:
) )
times_lines = parse_test_times(info_path) times_lines = parse_test_times(info_path)
new_tests_times = get_test_times(times_lines) new_tests_times = get_test_times(times_lines)
self._update_counters(counters, new_counters, broken_tests) self._update_counters(counters, new_counters)
for test_name, test_time in new_tests_times.items(): for test_name, test_time in new_tests_times.items():
tests_times[test_name] = test_time tests_times[test_name] = test_time
@ -783,7 +775,7 @@ class ClickhouseIntegrationTestsRunner:
final_retry += 1 final_retry += 1
logging.info("Running tests for the %s time", i) logging.info("Running tests for the %s time", i)
counters, tests_times, log_paths = self.try_run_test_group( counters, tests_times, log_paths = self.try_run_test_group(
repo_path, "bugfix" if should_fail else "flaky", tests_to_run, 1, 1, [] repo_path, "bugfix" if should_fail else "flaky", tests_to_run, 1, 1
) )
logs += log_paths logs += log_paths
if counters["FAILED"]: if counters["FAILED"]:
@ -904,7 +896,7 @@ class ClickhouseIntegrationTestsRunner:
"SKIPPED": [], "SKIPPED": [],
"BROKEN": [], "BROKEN": [],
"NOT_FAILED": [], "NOT_FAILED": [],
} } # type: Dict
tests_times = defaultdict(float) tests_times = defaultdict(float)
tests_log_paths = defaultdict(list) tests_log_paths = defaultdict(list)
@ -915,20 +907,10 @@ class ClickhouseIntegrationTestsRunner:
logging.info("Shuffling test groups") logging.info("Shuffling test groups")
random.shuffle(items_to_run) random.shuffle(items_to_run)
broken_tests = []
if self.use_analyzer:
with open(
f"{repo_path}/tests/analyzer_integration_broken_tests.txt",
"r",
encoding="utf-8",
) as f:
broken_tests = f.read().splitlines()
logging.info("Broken tests in the list: %s", len(broken_tests))
for group, tests in items_to_run: for group, tests in items_to_run:
logging.info("Running test group %s containing %s tests", group, len(tests)) logging.info("Running test group %s containing %s tests", group, len(tests))
group_counters, group_test_times, log_paths = self.try_run_test_group( group_counters, group_test_times, log_paths = self.try_run_test_group(
repo_path, group, tests, MAX_RETRY, NUM_WORKERS, broken_tests repo_path, group, tests, MAX_RETRY, NUM_WORKERS
) )
total_tests = 0 total_tests = 0
for counter, value in group_counters.items(): for counter, value in group_counters.items():
@ -1005,7 +987,7 @@ def write_results(results_file, status_file, results, status):
out.writerow(status) out.writerow(status)
if __name__ == "__main__": def run():
logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s") logging.basicConfig(level=logging.INFO, format="%(asctime)s %(message)s")
repo_path = os.environ.get("CLICKHOUSE_TESTS_REPO_PATH") repo_path = os.environ.get("CLICKHOUSE_TESTS_REPO_PATH")
@ -1013,29 +995,36 @@ if __name__ == "__main__":
result_path = os.environ.get("CLICKHOUSE_TESTS_RESULT_PATH") result_path = os.environ.get("CLICKHOUSE_TESTS_RESULT_PATH")
params_path = os.environ.get("CLICKHOUSE_TESTS_JSON_PARAMS_PATH") params_path = os.environ.get("CLICKHOUSE_TESTS_JSON_PARAMS_PATH")
assert params_path
with open(params_path, "r", encoding="utf-8") as jfd: with open(params_path, "r", encoding="utf-8") as jfd:
params = json.loads(jfd.read()) params = json.loads(jfd.read())
runner = ClickhouseIntegrationTestsRunner(result_path, params) runner = ClickhouseIntegrationTestsRunner(result_path, params)
logging.info("Running tests") logging.info("Running tests")
# Avoid overlaps with previous runs if CI:
logging.info("Clearing dmesg before run") # Avoid overlaps with previous runs
subprocess.check_call( # STYLE_CHECK_ALLOW_SUBPROCESS_CHECK_CALL logging.info("Clearing dmesg before run")
"dmesg --clear", shell=True subprocess.check_call( # STYLE_CHECK_ALLOW_SUBPROCESS_CHECK_CALL
) "sudo -E dmesg --clear", shell=True
)
state, description, test_results, _ = runner.run_impl(repo_path, build_path) state, description, test_results, _ = runner.run_impl(repo_path, build_path)
logging.info("Tests finished") logging.info("Tests finished")
# Dump dmesg (to capture possible OOMs) if CI:
logging.info("Dumping dmesg") # Dump dmesg (to capture possible OOMs)
subprocess.check_call( # STYLE_CHECK_ALLOW_SUBPROCESS_CHECK_CALL logging.info("Dumping dmesg")
"dmesg -T", shell=True subprocess.check_call( # STYLE_CHECK_ALLOW_SUBPROCESS_CHECK_CALL
) "sudo -E dmesg -T", shell=True
)
status = (state, description) status = (state, description)
out_results_file = os.path.join(str(runner.path()), "test_results.tsv") out_results_file = os.path.join(str(runner.path()), "test_results.tsv")
out_status_file = os.path.join(str(runner.path()), "check_status.tsv") out_status_file = os.path.join(str(runner.path()), "check_status.tsv")
write_results(out_results_file, out_status_file, test_results, status) write_results(out_results_file, out_status_file, test_results, status)
logging.info("Result written") logging.info("Result written")
if __name__ == "__main__":
run()

View File

@ -52,6 +52,11 @@ def get_options(i: int, upgrade_check: bool) -> str:
if i % 5 == 1: if i % 5 == 1:
client_options.append("memory_tracker_fault_probability=0.001") client_options.append("memory_tracker_fault_probability=0.001")
if i % 5 == 1:
client_options.append(
"merge_tree_read_split_ranges_into_intersecting_and_non_intersecting_injection_probability=0.05"
)
if i % 2 == 1 and not upgrade_check: if i % 2 == 1 and not upgrade_check:
client_options.append("group_by_use_nulls=1") client_options.append("group_by_use_nulls=1")

View File

@ -8,8 +8,9 @@ import subprocess
import sys import sys
from concurrent.futures import ProcessPoolExecutor from concurrent.futures import ProcessPoolExecutor
from pathlib import Path from pathlib import Path
from typing import List, Tuple from typing import List, Tuple, Union
import magic
from docker_images_helper import get_docker_image, pull_image from docker_images_helper import get_docker_image, pull_image
from env_helper import CI, REPO_COPY, TEMP_PATH from env_helper import CI, REPO_COPY, TEMP_PATH
from git_helper import GIT_PREFIX, git_runner from git_helper import GIT_PREFIX, git_runner
@ -95,34 +96,25 @@ def commit_push_staged(pr_info: PRInfo) -> None:
git_runner(push_cmd) git_runner(push_cmd)
def is_python(file: str) -> bool: def _check_mime(file: Union[Path, str], mime: str) -> bool:
# WARNING: python-magic v2:0.4.24-2 is used in ubuntu 22.04,
# and `Support os.PathLike values in magic.from_file` is only from 0.4.25
try:
return bool(magic.from_file(os.path.join(REPO_COPY, file), mime=True) == mime)
except (IsADirectoryError, FileNotFoundError) as e:
# Process submodules and removed files w/o errors
logging.warning("Captured error on file '%s': %s", file, e)
return False
def is_python(file: Union[Path, str]) -> bool:
"""returns if the changed file in the repository is python script""" """returns if the changed file in the repository is python script"""
# WARNING: python-magic v2:0.4.24-2 is used in ubuntu 22.04, return _check_mime(file, "text/x-script.python")
# and `Support os.PathLike values in magic.from_file` is only from 0.4.25
# try:
# return bool(
# magic.from_file(os.path.join(REPO_COPY, file), mime=True)
# == "text/x-script.python"
# )
# except IsADirectoryError:
# # Process submodules w/o errors
# return False
return file.endswith(".py")
def is_shell(file: str) -> bool: def is_shell(file: Union[Path, str]) -> bool:
"""returns if the changed file in the repository is shell script""" """returns if the changed file in the repository is shell script"""
# WARNING: python-magic v2:0.4.24-2 is used in ubuntu 22.04, return _check_mime(file, "text/x-shellscript")
# and `Support os.PathLike values in magic.from_file` is only from 0.4.25
# try:
# return bool(
# magic.from_file(os.path.join(REPO_COPY, file), mime=True)
# == "text/x-shellscript"
# )
# except IsADirectoryError:
# # Process submodules w/o errors
# return False
return file.endswith(".sh")
def main(): def main():

View File

@ -843,6 +843,9 @@ class SettingsRandomizer:
"prefer_warmed_unmerged_parts_seconds": lambda: random.randint(0, 10), "prefer_warmed_unmerged_parts_seconds": lambda: random.randint(0, 10),
"use_page_cache_for_disks_without_file_cache": lambda: random.random() < 0.7, "use_page_cache_for_disks_without_file_cache": lambda: random.random() < 0.7,
"page_cache_inject_eviction": lambda: random.random() < 0.5, "page_cache_inject_eviction": lambda: random.random() < 0.5,
"merge_tree_read_split_ranges_into_intersecting_and_non_intersecting_injection_probability": lambda: round(
random.random(), 2
),
} }
@staticmethod @staticmethod

View File

@ -1,7 +1,7 @@
version: '2.3' version: '2.3'
services: services:
mysql_client: mysql_client:
image: mysql:5.7 image: mysql:8.0
restart: always restart: always
environment: environment:
MYSQL_ALLOW_EMPTY_PASSWORD: 1 MYSQL_ALLOW_EMPTY_PASSWORD: 1

View File

@ -1,8 +1,10 @@
import base64 import base64
import errno import errno
from functools import cache
import http.client import http.client
import logging import logging
import os import os
import platform
import stat import stat
import os.path as p import os.path as p
import pprint import pprint
@ -4743,3 +4745,8 @@ class ClickHouseKiller(object):
def __exit__(self, exc_type, exc_val, exc_tb): def __exit__(self, exc_type, exc_val, exc_tb):
self.clickhouse_node.start_clickhouse() self.clickhouse_node.start_clickhouse()
@cache
def is_arm():
return any(arch in platform.processor().lower() for arch in ("arm, aarch"))

View File

@ -1,5 +1,5 @@
import pytest import pytest
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
cluster = ClickHouseCluster(__file__) cluster = ClickHouseCluster(__file__)
node1 = cluster.add_instance("node1", main_configs=["configs/config_with_hosts.xml"]) node1 = cluster.add_instance("node1", main_configs=["configs/config_with_hosts.xml"])
@ -16,9 +16,11 @@ node5 = cluster.add_instance(
"node5", main_configs=["configs/config_without_allowed_hosts.xml"] "node5", main_configs=["configs/config_without_allowed_hosts.xml"]
) )
node6 = cluster.add_instance("node6", main_configs=["configs/config_for_remote.xml"]) node6 = cluster.add_instance("node6", main_configs=["configs/config_for_remote.xml"])
node7 = cluster.add_instance(
"node7", main_configs=["configs/config_for_redirect.xml"], with_hdfs=True if not is_arm():
) node7 = cluster.add_instance(
"node7", main_configs=["configs/config_for_redirect.xml"], with_hdfs=True
)
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
@ -270,6 +272,7 @@ def test_table_function_remote(start_cluster):
) )
@pytest.mark.skipif(is_arm(), reason="skip for ARM")
def test_redirect(start_cluster): def test_redirect(start_cluster):
hdfs_api = start_cluster.hdfs_api hdfs_api = start_cluster.hdfs_api
@ -284,6 +287,7 @@ def test_redirect(start_cluster):
node7.query("DROP TABLE table_test_7_1") node7.query("DROP TABLE table_test_7_1")
@pytest.mark.skipif(is_arm(), reason="skip for ARM")
def test_HDFS(start_cluster): def test_HDFS(start_cluster):
assert "not allowed" in node7.query_and_get_error( assert "not allowed" in node7.query_and_get_error(
"CREATE TABLE table_test_7_2 (word String) ENGINE=HDFS('http://hdfs1:50075/webhdfs/v1/simple_storage?op=OPEN&namenoderpcaddress=hdfs1:9000&offset=0', 'CSV')" "CREATE TABLE table_test_7_2 (word String) ENGINE=HDFS('http://hdfs1:50075/webhdfs/v1/simple_storage?op=OPEN&namenoderpcaddress=hdfs1:9000&offset=0', 'CSV')"
@ -293,6 +297,7 @@ def test_HDFS(start_cluster):
) )
@pytest.mark.skipif(is_arm(), reason="skip for ARM")
def test_schema_inference(start_cluster): def test_schema_inference(start_cluster):
error = node7.query_and_get_error("desc url('http://test.com`, 'TSVRaw'')") error = node7.query_and_get_error("desc url('http://test.com`, 'TSVRaw'')")
assert error.find("ReadWriteBufferFromHTTPBase") == -1 assert error.find("ReadWriteBufferFromHTTPBase") == -1

View File

@ -1,14 +1,17 @@
import pytest import pytest
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
from helpers.test_tools import TSV from helpers.test_tools import TSV
disk_types = { disk_types = {
"default": "Local", "default": "Local",
"disk_s3": "S3", "disk_s3": "S3",
"disk_hdfs": "HDFS",
"disk_encrypted": "S3", "disk_encrypted": "S3",
} }
# do not test HDFS on ARM
if not is_arm():
disk_types["disk_hdfs"] = "HDFS"
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def cluster(): def cluster():
@ -18,7 +21,7 @@ def cluster():
"node", "node",
main_configs=["configs/storage.xml"], main_configs=["configs/storage.xml"],
with_minio=True, with_minio=True,
with_hdfs=True, with_hdfs=not is_arm(),
) )
cluster.start() cluster.start()

View File

@ -1,5 +1,5 @@
import pytest import pytest
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
from helpers.test_tools import TSV from helpers.test_tools import TSV
from pyhdfs import HdfsClient from pyhdfs import HdfsClient
@ -10,6 +10,9 @@ disk_types = {
"disk_encrypted": "S3", "disk_encrypted": "S3",
} }
if is_arm():
pytestmark = pytest.mark.skip
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def cluster(): def cluster():

View File

@ -8,9 +8,13 @@ from confluent_kafka.avro.cached_schema_registry_client import (
CachedSchemaRegistryClient, CachedSchemaRegistryClient,
) )
from confluent_kafka.avro.serializer.message_serializer import MessageSerializer from confluent_kafka.avro.serializer.message_serializer import MessageSerializer
from helpers.cluster import ClickHouseCluster, ClickHouseInstance from helpers.cluster import ClickHouseCluster, ClickHouseInstance, is_arm
from urllib import parse from urllib import parse
# Skip on ARM due to Confluent/Kafka
if is_arm():
pytestmark = pytest.mark.skip
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def started_cluster(): def started_cluster():

View File

@ -3,7 +3,7 @@ import os.path as p
import pytest import pytest
import uuid import uuid
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
from helpers.test_tools import TSV from helpers.test_tools import TSV
from string import Template from string import Template
@ -14,6 +14,9 @@ instance = cluster.add_instance(
datasource = "self" datasource = "self"
records = 1000 records = 1000
if is_arm():
pytestmark = pytest.mark.skip
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def started_cluster(): def started_cluster():

View File

@ -2,10 +2,14 @@ import time
import logging import logging
import pytest import pytest
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
from kafka import KafkaAdminClient, KafkaProducer, KafkaConsumer, BrokerConnection from kafka import KafkaAdminClient, KafkaProducer, KafkaConsumer, BrokerConnection
from kafka.admin import NewTopic from kafka.admin import NewTopic
if is_arm():
pytestmark = pytest.mark.skip
cluster = ClickHouseCluster(__file__) cluster = ClickHouseCluster(__file__)
instance = cluster.add_instance( instance = cluster.add_instance(
"instance", "instance",

View File

@ -1,11 +1,15 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import pytest import pytest
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
import helpers.keeper_utils as keeper_utils import helpers.keeper_utils as keeper_utils
from minio.deleteobjects import DeleteObject from minio.deleteobjects import DeleteObject
import os import os
if is_arm():
pytestmark = pytest.mark.skip
CURRENT_TEST_DIR = os.path.dirname(os.path.abspath(__file__)) CURRENT_TEST_DIR = os.path.dirname(os.path.abspath(__file__))
cluster = ClickHouseCluster(__file__) cluster = ClickHouseCluster(__file__)
node_logs = cluster.add_instance( node_logs = cluster.add_instance(

View File

@ -1,5 +1,9 @@
import pytest import pytest
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
if is_arm():
pytestmark = pytest.mark.skip
cluster = ClickHouseCluster(__file__) cluster = ClickHouseCluster(__file__)
instance1 = cluster.add_instance( instance1 = cluster.add_instance(

View File

@ -2,10 +2,13 @@ import logging
import sys import sys
import pytest import pytest
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
from pyhdfs import HdfsClient from pyhdfs import HdfsClient
if is_arm():
pytestmark = pytest.mark.skip
@pytest.fixture(scope="module") @pytest.fixture(scope="module")
def started_cluster(): def started_cluster():

View File

@ -6,6 +6,7 @@ from helpers.cluster import (
ClickHouseCluster, ClickHouseCluster,
ClickHouseInstance, ClickHouseInstance,
get_docker_compose_path, get_docker_compose_path,
is_arm,
) )
import logging import logging
@ -13,6 +14,10 @@ from . import materialized_with_ddl
DOCKER_COMPOSE_PATH = get_docker_compose_path() DOCKER_COMPOSE_PATH = get_docker_compose_path()
# skip all test on arm due to no arm support in mysql57
if is_arm():
pytestmark = pytest.mark.skip
cluster = ClickHouseCluster(__file__) cluster = ClickHouseCluster(__file__)
mysql_node = None mysql_node = None
mysql8_node = None mysql8_node = None

View File

@ -3,7 +3,7 @@ import time
import os import os
import pytest import pytest
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
from helpers.utility import generate_values from helpers.utility import generate_values
from helpers.wait_for_helpers import wait_for_delete_inactive_parts from helpers.wait_for_helpers import wait_for_delete_inactive_parts
from helpers.wait_for_helpers import wait_for_delete_empty_parts from helpers.wait_for_helpers import wait_for_delete_empty_parts
@ -16,6 +16,10 @@ CONFIG_PATH = os.path.join(
) )
if is_arm():
pytestmark = pytest.mark.skip
def create_table(cluster, table_name, additional_settings=None): def create_table(cluster, table_name, additional_settings=None):
node = cluster.instances["node"] node = cluster.instances["node"]

View File

@ -12,11 +12,16 @@ from typing import Literal
import docker import docker
import pymysql.connections import pymysql.connections
import pytest import pytest
from helpers.cluster import ClickHouseCluster, get_docker_compose_path, run_and_check from helpers.cluster import (
ClickHouseCluster,
get_docker_compose_path,
run_and_check,
)
SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
DOCKER_COMPOSE_PATH = get_docker_compose_path() DOCKER_COMPOSE_PATH = get_docker_compose_path()
cluster = ClickHouseCluster(__file__) cluster = ClickHouseCluster(__file__)
node = cluster.add_instance( node = cluster.add_instance(
"node", "node",

View File

@ -1,10 +1,15 @@
import pytest import pytest
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
from helpers.network import PartitionManager from helpers.network import PartitionManager
import threading import threading
import time import time
# skip all tests in the module on ARM due to HDFS
if is_arm():
pytestmark = pytest.mark.skip
cluster = ClickHouseCluster(__file__) cluster = ClickHouseCluster(__file__)
node1 = cluster.add_instance( node1 = cluster.add_instance(
"node1", "node1",

View File

@ -2,10 +2,13 @@ import os
import pytest import pytest
import time import time
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
from helpers.test_tools import TSV from helpers.test_tools import TSV
from pyhdfs import HdfsClient from pyhdfs import HdfsClient
if is_arm():
pytestmark = pytest.mark.skip
cluster = ClickHouseCluster(__file__) cluster = ClickHouseCluster(__file__)
node1 = cluster.add_instance( node1 = cluster.add_instance(
"node1", "node1",

View File

@ -22,7 +22,7 @@ import kafka.errors
import pytest import pytest
from google.protobuf.internal.encoder import _VarintBytes from google.protobuf.internal.encoder import _VarintBytes
from helpers.client import QueryRuntimeException from helpers.client import QueryRuntimeException
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
from helpers.network import PartitionManager from helpers.network import PartitionManager
from helpers.test_tools import TSV from helpers.test_tools import TSV
from kafka import KafkaAdminClient, KafkaProducer, KafkaConsumer, BrokerConnection from kafka import KafkaAdminClient, KafkaProducer, KafkaConsumer, BrokerConnection
@ -40,6 +40,8 @@ from . import kafka_pb2
from . import social_pb2 from . import social_pb2
from . import message_with_repeated_pb2 from . import message_with_repeated_pb2
if is_arm():
pytestmark = pytest.mark.skip
# TODO: add test for run-time offset update in CH, if we manually update it on Kafka side. # TODO: add test for run-time offset update in CH, if we manually update it on Kafka side.
# TODO: add test for SELECT LIMIT is working. # TODO: add test for SELECT LIMIT is working.

View File

@ -3,9 +3,12 @@ import pytest
import os import os
from helpers.cluster import ClickHouseCluster from helpers.cluster import ClickHouseCluster, is_arm
import subprocess import subprocess
if is_arm():
pytestmark = pytest.mark.skip
cluster = ClickHouseCluster(__file__) cluster = ClickHouseCluster(__file__)
node1 = cluster.add_instance( node1 = cluster.add_instance(
"node1", "node1",

View File

@ -7,11 +7,11 @@ INSERT INTO sorted (x) SELECT intDiv(number, 100000) AS x FROM system.numbers LI
SET max_threads = 1; SET max_threads = 1;
SELECT count() FROM sorted; SELECT count() FROM sorted;
SELECT DISTINCT x FROM sorted; SELECT x FROM (SELECT DISTINCT x FROM sorted) ORDER BY x;
INSERT INTO sorted (x) SELECT (intHash64(number) % 1000 = 0 ? 999 : intDiv(number, 100000)) AS x FROM system.numbers LIMIT 1000000; INSERT INTO sorted (x) SELECT (intHash64(number) % 1000 = 0 ? 999 : intDiv(number, 100000)) AS x FROM system.numbers LIMIT 1000000;
SELECT count() FROM sorted; SELECT count() FROM sorted;
SELECT DISTINCT x FROM sorted; SELECT x FROM (SELECT DISTINCT x FROM sorted) ORDER BY x;
DROP TABLE sorted; DROP TABLE sorted;

View File

@ -1,8 +1,10 @@
SET merge_tree_read_split_ranges_into_intersecting_and_non_intersecting_injection_probability = 0.0;
DROP TABLE IF EXISTS index_for_like; DROP TABLE IF EXISTS index_for_like;
set allow_deprecated_syntax_for_merge_tree=1; set allow_deprecated_syntax_for_merge_tree=1;
CREATE TABLE index_for_like (s String, d Date DEFAULT today()) ENGINE = MergeTree(d, (s, d), 1); CREATE TABLE index_for_like (s String, d Date DEFAULT today()) ENGINE = MergeTree(d, (s, d), 1);
INSERT INTO index_for_like (s) VALUES ('Hello'), ('Hello, World'), ('Hello, World 1'), ('Hello 1'), ('Goodbye'), ('Goodbye, World'), ('Goodbye 1'), ('Goodbye, World 1'); INSERT INTO index_for_like (s) VALUES ('Hello'), ('Hello, World'), ('Hello, World 1'), ('Hello 1'), ('Goodbye'), ('Goodbye, World'), ('Goodbye 1'), ('Goodbye, World 1');
SET max_rows_to_read = 3; SET max_rows_to_read = 3;
SELECT s FROM index_for_like WHERE s LIKE 'Hello, World%'; SELECT s FROM index_for_like WHERE s LIKE 'Hello, World%';

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