merge remote-tracking branch 'origin/master' into restore-replace-to-null-settings-fix

This commit is contained in:
Yatsishin Ilya 2024-09-11 18:23:07 +00:00
commit ccc6390a9e
374 changed files with 7708 additions and 2724 deletions

31
CITATION.cff Normal file
View File

@ -0,0 +1,31 @@
# This CITATION.cff file was generated with cffinit.
cff-version: 1.2.0
title: "ClickHouse"
message: "If you use this software, please cite it as below."
type: software
authors:
- family-names: "Milovidov"
given-names: "Alexey"
repository-code: 'https://github.com/ClickHouse/ClickHouse'
url: 'https://clickhouse.com'
license: Apache-2.0
preferred-citation:
type: article
authors:
- family-names: "Schulze"
given-names: "Robert"
- family-names: "Schreiber"
given-names: "Tom"
- family-names: "Yatsishin"
given-names: "Ilya"
- family-names: "Dahimene"
given-names: "Ryadh"
- family-names: "Milovidov"
given-names: "Alexey"
journal: "Proceedings of the VLDB Endowment"
title: "ClickHouse - Lightning Fast Analytics for Everyone"
year: 2024
volume: 17
issue: 12
doi: 10.14778/3685800.3685802

View File

@ -42,21 +42,19 @@ Keep an eye out for upcoming meetups and events around the world. Somewhere else
The following upcoming meetups are featuring creator of ClickHouse & CTO, Alexey Milovidov: The following upcoming meetups are featuring creator of ClickHouse & CTO, Alexey Milovidov:
* [ClickHouse Guangzhou User Group Meetup](https://mp.weixin.qq.com/s/GSvo-7xUoVzCsuUvlLTpCw) - August 25
* [San Francisco Meetup (Cloudflare)](https://www.meetup.com/clickhouse-silicon-valley-meetup-group/events/302540575) - September 5
* [Raleigh Meetup (Deutsche Bank)](https://www.meetup.com/triangletechtalks/events/302723486/) - September 9 * [Raleigh Meetup (Deutsche Bank)](https://www.meetup.com/triangletechtalks/events/302723486/) - September 9
* [New York Meetup (Rokt)](https://www.meetup.com/clickhouse-new-york-user-group/events/302575342) - September 10 * [New York Meetup (Rokt)](https://www.meetup.com/clickhouse-new-york-user-group/events/302575342) - September 10
* [Chicago Meetup (Jump Capital)](https://lu.ma/43tvmrfw) - September 12 * [Chicago Meetup (Jump Capital)](https://lu.ma/43tvmrfw) - September 12
Other upcoming meetups Other upcoming meetups
* [Seattle Meetup (Statsig)](https://www.meetup.com/clickhouse-seattle-user-group/events/302518075/) - August 27
* [Melbourne Meetup](https://www.meetup.com/clickhouse-australia-user-group/events/302732666/) - August 27
* [Sydney Meetup](https://www.meetup.com/clickhouse-australia-user-group/events/302862966/) - September 5
* [Zurich Meetup](https://www.meetup.com/clickhouse-switzerland-meetup-group/events/302267429/) - September 5
* [Toronto Meetup (Shopify)](https://www.meetup.com/clickhouse-toronto-user-group/events/301490855/) - September 10 * [Toronto Meetup (Shopify)](https://www.meetup.com/clickhouse-toronto-user-group/events/301490855/) - September 10
* [Austin Meetup](https://www.meetup.com/clickhouse-austin-user-group/events/302558689/) - September 17 * [Austin Meetup](https://www.meetup.com/clickhouse-austin-user-group/events/302558689/) - September 17
* [London Meetup](https://www.meetup.com/clickhouse-london-user-group/events/302977267) - September 17 * [London Meetup](https://www.meetup.com/clickhouse-london-user-group/events/302977267) - September 17
* [Bangalore Meetup](https://www.meetup.com/clickhouse-bangalore-user-group/events/303208274/) - September 18
* [Tel Aviv Meetup](https://www.meetup.com/clickhouse-meetup-israel/events/303095121) - September 22 * [Tel Aviv Meetup](https://www.meetup.com/clickhouse-meetup-israel/events/303095121) - September 22
* [Jakarta Meetup](https://www.meetup.com/clickhouse-indonesia-user-group/events/303191359/) - October 1
* [Singapore Meetup](https://www.meetup.com/clickhouse-singapore-meetup-group/events/303212064/) - October 3
* [Madrid Meetup](https://www.meetup.com/clickhouse-spain-user-group/events/303096564/) - October 22 * [Madrid Meetup](https://www.meetup.com/clickhouse-spain-user-group/events/303096564/) - October 22
* [Barcelona Meetup](https://www.meetup.com/clickhouse-spain-user-group/events/303096876/) - October 29 * [Barcelona Meetup](https://www.meetup.com/clickhouse-spain-user-group/events/303096876/) - October 29
* [Oslo Meetup](https://www.meetup.com/open-source-real-time-data-warehouse-real-time-analytics/events/302938622) - October 31 * [Oslo Meetup](https://www.meetup.com/open-source-real-time-data-warehouse-real-time-analytics/events/302938622) - October 31
@ -64,7 +62,13 @@ Other upcoming meetups
* [Dubai Meetup](https://www.meetup.com/clickhouse-dubai-meetup-group/events/303096989/) - November 21 * [Dubai Meetup](https://www.meetup.com/clickhouse-dubai-meetup-group/events/303096989/) - November 21
* [Paris Meetup](https://www.meetup.com/clickhouse-france-user-group/events/303096434) - November 26 * [Paris Meetup](https://www.meetup.com/clickhouse-france-user-group/events/303096434) - November 26
Recently completed events
* [ClickHouse Guangzhou User Group Meetup](https://mp.weixin.qq.com/s/GSvo-7xUoVzCsuUvlLTpCw) - August 25
* [Seattle Meetup (Statsig)](https://www.meetup.com/clickhouse-seattle-user-group/events/302518075/) - August 27
* [Melbourne Meetup](https://www.meetup.com/clickhouse-australia-user-group/events/302732666/) - August 27
* [Sydney Meetup](https://www.meetup.com/clickhouse-australia-user-group/events/302862966/) - September 5
* [Zurich Meetup](https://www.meetup.com/clickhouse-switzerland-meetup-group/events/302267429/) - September 5
* [San Francisco Meetup (Cloudflare)](https://www.meetup.com/clickhouse-silicon-valley-meetup-group/events/302540575) - September 5
## Recent Recordings ## Recent Recordings
* **Recent Meetup Videos**: [Meetup Playlist](https://www.youtube.com/playlist?list=PL0Z2YDlm0b3iNDUzpY1S3L_iV4nARda_U) Whenever possible recordings of the ClickHouse Community Meetups are edited and presented as individual talks. Current featuring "Modern SQL in 2023", "Fast, Concurrent, and Consistent Asynchronous INSERTS in ClickHouse", and "Full-Text Indices: Design and Experiments" * **Recent Meetup Videos**: [Meetup Playlist](https://www.youtube.com/playlist?list=PL0Z2YDlm0b3iNDUzpY1S3L_iV4nARda_U) Whenever possible recordings of the ClickHouse Community Meetups are edited and presented as individual talks. Current featuring "Modern SQL in 2023", "Fast, Concurrent, and Consistent Asynchronous INSERTS in ClickHouse", and "Full-Text Indices: Design and Experiments"

View File

@ -66,13 +66,11 @@ TRAP(gethostbyname)
TRAP(gethostbyname2) TRAP(gethostbyname2)
TRAP(gethostent) TRAP(gethostent)
TRAP(getlogin) TRAP(getlogin)
TRAP(getmntent)
TRAP(getnetbyaddr) TRAP(getnetbyaddr)
TRAP(getnetbyname) TRAP(getnetbyname)
TRAP(getnetent) TRAP(getnetent)
TRAP(getnetgrent) TRAP(getnetgrent)
TRAP(getnetgrent_r) TRAP(getnetgrent_r)
TRAP(getopt)
TRAP(getopt_long) TRAP(getopt_long)
TRAP(getopt_long_only) TRAP(getopt_long_only)
TRAP(getpass) TRAP(getpass)
@ -133,7 +131,6 @@ TRAP(nrand48)
TRAP(__ppc_get_timebase_freq) TRAP(__ppc_get_timebase_freq)
TRAP(ptsname) TRAP(ptsname)
TRAP(putchar_unlocked) TRAP(putchar_unlocked)
TRAP(putenv)
TRAP(pututline) TRAP(pututline)
TRAP(pututxline) TRAP(pututxline)
TRAP(putwchar_unlocked) TRAP(putwchar_unlocked)
@ -148,7 +145,6 @@ TRAP(sethostent)
TRAP(sethostid) TRAP(sethostid)
TRAP(setkey) TRAP(setkey)
//TRAP(setlocale) // Used by replxx at startup //TRAP(setlocale) // Used by replxx at startup
TRAP(setlogmask)
TRAP(setnetent) TRAP(setnetent)
TRAP(setnetgrent) TRAP(setnetgrent)
TRAP(setprotoent) TRAP(setprotoent)
@ -203,7 +199,6 @@ TRAP(lgammal)
TRAP(nftw) TRAP(nftw)
TRAP(nl_langinfo) TRAP(nl_langinfo)
TRAP(putc_unlocked) TRAP(putc_unlocked)
TRAP(rand)
/** In the current POSIX.1 specification (POSIX.1-2008), readdir() is not required to be thread-safe. However, in modern /** In the current POSIX.1 specification (POSIX.1-2008), readdir() is not required to be thread-safe. However, in modern
* implementations (including the glibc implementation), concurrent calls to readdir() that specify different directory streams * implementations (including the glibc implementation), concurrent calls to readdir() that specify different directory streams
* are thread-safe. In cases where multiple threads must read from the same directory stream, using readdir() with external * are thread-safe. In cases where multiple threads must read from the same directory stream, using readdir() with external
@ -288,4 +283,14 @@ TRAP(tss_get)
TRAP(tss_set) TRAP(tss_set)
TRAP(tss_delete) TRAP(tss_delete)
#ifndef USE_MUSL
/// These produce duplicate symbol errors when statically linking with musl.
/// Maybe we can remove them from the musl fork.
TRAP(getopt)
TRAP(putenv)
TRAP(setlogmask)
TRAP(rand)
TRAP(getmntent)
#endif
#endif #endif

View File

@ -48,25 +48,17 @@ std::string PathImpl::currentImpl()
std::string PathImpl::homeImpl() std::string PathImpl::homeImpl()
{ {
std::string path; std::string path;
#if defined(_POSIX_C_SOURCE) || defined(_BSD_SOURCE) || defined(_POSIX_C_SOURCE)
size_t buf_size = 1024; // Same as glibc use for getpwuid size_t buf_size = 1024; // Same as glibc use for getpwuid
std::vector<char> buf(buf_size); std::vector<char> buf(buf_size);
struct passwd res; struct passwd res;
struct passwd* pwd = nullptr; struct passwd* pwd = nullptr;
getpwuid_r(getuid(), &res, buf.data(), buf_size, &pwd); getpwuid_r(getuid(), &res, buf.data(), buf_size, &pwd);
#else
struct passwd* pwd = getpwuid(getuid());
#endif
if (pwd) if (pwd)
path = pwd->pw_dir; path = pwd->pw_dir;
else else
{ {
#if defined(_POSIX_C_SOURCE) || defined(_BSD_SOURCE) || defined(_POSIX_C_SOURCE)
getpwuid_r(getuid(), &res, buf.data(), buf_size, &pwd); getpwuid_r(getuid(), &res, buf.data(), buf_size, &pwd);
#else
pwd = getpwuid(geteuid());
#endif
if (pwd) if (pwd)
path = pwd->pw_dir; path = pwd->pw_dir;
else else
@ -82,7 +74,7 @@ std::string PathImpl::configHomeImpl()
{ {
std::string path = PathImpl::homeImpl(); std::string path = PathImpl::homeImpl();
std::string::size_type n = path.size(); std::string::size_type n = path.size();
if (n > 0 && path[n - 1] == '/') if (n > 0 && path[n - 1] == '/')
#if POCO_OS == POCO_OS_MAC_OS_X #if POCO_OS == POCO_OS_MAC_OS_X
path.append("Library/Preferences/"); path.append("Library/Preferences/");
#else #else
@ -97,7 +89,7 @@ std::string PathImpl::dataHomeImpl()
{ {
std::string path = PathImpl::homeImpl(); std::string path = PathImpl::homeImpl();
std::string::size_type n = path.size(); std::string::size_type n = path.size();
if (n > 0 && path[n - 1] == '/') if (n > 0 && path[n - 1] == '/')
#if POCO_OS == POCO_OS_MAC_OS_X #if POCO_OS == POCO_OS_MAC_OS_X
path.append("Library/Application Support/"); path.append("Library/Application Support/");
#else #else
@ -112,7 +104,7 @@ std::string PathImpl::cacheHomeImpl()
{ {
std::string path = PathImpl::homeImpl(); std::string path = PathImpl::homeImpl();
std::string::size_type n = path.size(); std::string::size_type n = path.size();
if (n > 0 && path[n - 1] == '/') if (n > 0 && path[n - 1] == '/')
#if POCO_OS == POCO_OS_MAC_OS_X #if POCO_OS == POCO_OS_MAC_OS_X
path.append("Library/Caches/"); path.append("Library/Caches/");
#else #else
@ -127,7 +119,7 @@ std::string PathImpl::tempHomeImpl()
{ {
std::string path = PathImpl::homeImpl(); std::string path = PathImpl::homeImpl();
std::string::size_type n = path.size(); std::string::size_type n = path.size();
if (n > 0 && path[n - 1] == '/') if (n > 0 && path[n - 1] == '/')
#if POCO_OS == POCO_OS_MAC_OS_X #if POCO_OS == POCO_OS_MAC_OS_X
path.append("Library/Caches/"); path.append("Library/Caches/");
#else #else
@ -159,7 +151,7 @@ std::string PathImpl::tempImpl()
std::string PathImpl::configImpl() std::string PathImpl::configImpl()
{ {
std::string path; std::string path;
#if POCO_OS == POCO_OS_MAC_OS_X #if POCO_OS == POCO_OS_MAC_OS_X
path = "/Library/Preferences/"; path = "/Library/Preferences/";
#else #else

2
contrib/curl vendored

@ -1 +1 @@
Subproject commit de7b3e89218467159a7af72d58cea8425946e97d Subproject commit 83bedbd730d62b83744cc26fa0433d3f6e2e4cd6

2
contrib/grpc vendored

@ -1 +1 @@
Subproject commit 1716359d2e28d304a250f9df0e6c0ccad03de8db Subproject commit 7bc3abe952aba1dc7bce7f2f790dc781cb51a41e

View File

@ -15,162 +15,64 @@ set(ICUDATA_SOURCE_DIR "${ClickHouse_SOURCE_DIR}/contrib/icudata/")
# These lists of sources were generated from build log of the original ICU build system (configure + make). # These lists of sources were generated from build log of the original ICU build system (configure + make).
set(ICUUC_SOURCES set(ICUUC_SOURCES
"${ICU_SOURCE_DIR}/common/errorcode.cpp"
"${ICU_SOURCE_DIR}/common/putil.cpp"
"${ICU_SOURCE_DIR}/common/umath.cpp"
"${ICU_SOURCE_DIR}/common/utypes.cpp"
"${ICU_SOURCE_DIR}/common/uinvchar.cpp"
"${ICU_SOURCE_DIR}/common/umutex.cpp"
"${ICU_SOURCE_DIR}/common/ucln_cmn.cpp"
"${ICU_SOURCE_DIR}/common/uinit.cpp"
"${ICU_SOURCE_DIR}/common/uobject.cpp"
"${ICU_SOURCE_DIR}/common/cmemory.cpp"
"${ICU_SOURCE_DIR}/common/charstr.cpp"
"${ICU_SOURCE_DIR}/common/cstr.cpp"
"${ICU_SOURCE_DIR}/common/udata.cpp"
"${ICU_SOURCE_DIR}/common/ucmndata.cpp"
"${ICU_SOURCE_DIR}/common/udatamem.cpp"
"${ICU_SOURCE_DIR}/common/umapfile.cpp"
"${ICU_SOURCE_DIR}/common/udataswp.cpp"
"${ICU_SOURCE_DIR}/common/utrie_swap.cpp"
"${ICU_SOURCE_DIR}/common/ucol_swp.cpp"
"${ICU_SOURCE_DIR}/common/utrace.cpp"
"${ICU_SOURCE_DIR}/common/uhash.cpp"
"${ICU_SOURCE_DIR}/common/uhash_us.cpp"
"${ICU_SOURCE_DIR}/common/uenum.cpp"
"${ICU_SOURCE_DIR}/common/ustrenum.cpp"
"${ICU_SOURCE_DIR}/common/uvector.cpp"
"${ICU_SOURCE_DIR}/common/ustack.cpp"
"${ICU_SOURCE_DIR}/common/uvectr32.cpp"
"${ICU_SOURCE_DIR}/common/uvectr64.cpp"
"${ICU_SOURCE_DIR}/common/ucnv.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_bld.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_cnv.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_io.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_cb.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_err.cpp"
"${ICU_SOURCE_DIR}/common/ucnvlat1.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_u7.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_u8.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_u16.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_u32.cpp"
"${ICU_SOURCE_DIR}/common/ucnvscsu.cpp"
"${ICU_SOURCE_DIR}/common/ucnvbocu.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_ext.cpp"
"${ICU_SOURCE_DIR}/common/ucnvmbcs.cpp"
"${ICU_SOURCE_DIR}/common/ucnv2022.cpp"
"${ICU_SOURCE_DIR}/common/ucnvhz.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_lmb.cpp"
"${ICU_SOURCE_DIR}/common/ucnvisci.cpp"
"${ICU_SOURCE_DIR}/common/ucnvdisp.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_set.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_ct.cpp"
"${ICU_SOURCE_DIR}/common/resource.cpp"
"${ICU_SOURCE_DIR}/common/uresbund.cpp"
"${ICU_SOURCE_DIR}/common/ures_cnv.cpp"
"${ICU_SOURCE_DIR}/common/uresdata.cpp"
"${ICU_SOURCE_DIR}/common/resbund.cpp"
"${ICU_SOURCE_DIR}/common/resbund_cnv.cpp"
"${ICU_SOURCE_DIR}/common/ucurr.cpp"
"${ICU_SOURCE_DIR}/common/localebuilder.cpp"
"${ICU_SOURCE_DIR}/common/localeprioritylist.cpp"
"${ICU_SOURCE_DIR}/common/messagepattern.cpp"
"${ICU_SOURCE_DIR}/common/ucat.cpp"
"${ICU_SOURCE_DIR}/common/locmap.cpp"
"${ICU_SOURCE_DIR}/common/uloc.cpp"
"${ICU_SOURCE_DIR}/common/locid.cpp"
"${ICU_SOURCE_DIR}/common/locutil.cpp"
"${ICU_SOURCE_DIR}/common/locavailable.cpp"
"${ICU_SOURCE_DIR}/common/locdispnames.cpp"
"${ICU_SOURCE_DIR}/common/locdspnm.cpp"
"${ICU_SOURCE_DIR}/common/loclikely.cpp"
"${ICU_SOURCE_DIR}/common/locresdata.cpp"
"${ICU_SOURCE_DIR}/common/lsr.cpp"
"${ICU_SOURCE_DIR}/common/loclikelysubtags.cpp"
"${ICU_SOURCE_DIR}/common/locdistance.cpp"
"${ICU_SOURCE_DIR}/common/localematcher.cpp"
"${ICU_SOURCE_DIR}/common/bytestream.cpp"
"${ICU_SOURCE_DIR}/common/stringpiece.cpp"
"${ICU_SOURCE_DIR}/common/bytesinkutil.cpp"
"${ICU_SOURCE_DIR}/common/stringtriebuilder.cpp"
"${ICU_SOURCE_DIR}/common/bytestriebuilder.cpp"
"${ICU_SOURCE_DIR}/common/bytestrie.cpp"
"${ICU_SOURCE_DIR}/common/bytestrieiterator.cpp"
"${ICU_SOURCE_DIR}/common/ucharstrie.cpp"
"${ICU_SOURCE_DIR}/common/ucharstriebuilder.cpp"
"${ICU_SOURCE_DIR}/common/ucharstrieiterator.cpp"
"${ICU_SOURCE_DIR}/common/dictionarydata.cpp"
"${ICU_SOURCE_DIR}/common/edits.cpp"
"${ICU_SOURCE_DIR}/common/appendable.cpp" "${ICU_SOURCE_DIR}/common/appendable.cpp"
"${ICU_SOURCE_DIR}/common/ustr_cnv.cpp"
"${ICU_SOURCE_DIR}/common/unistr_cnv.cpp"
"${ICU_SOURCE_DIR}/common/unistr.cpp"
"${ICU_SOURCE_DIR}/common/unistr_case.cpp"
"${ICU_SOURCE_DIR}/common/unistr_props.cpp"
"${ICU_SOURCE_DIR}/common/utf_impl.cpp"
"${ICU_SOURCE_DIR}/common/ustring.cpp"
"${ICU_SOURCE_DIR}/common/ustrcase.cpp"
"${ICU_SOURCE_DIR}/common/ucasemap.cpp"
"${ICU_SOURCE_DIR}/common/ucasemap_titlecase_brkiter.cpp"
"${ICU_SOURCE_DIR}/common/cstring.cpp"
"${ICU_SOURCE_DIR}/common/ustrfmt.cpp"
"${ICU_SOURCE_DIR}/common/ustrtrns.cpp"
"${ICU_SOURCE_DIR}/common/ustr_wcs.cpp"
"${ICU_SOURCE_DIR}/common/utext.cpp"
"${ICU_SOURCE_DIR}/common/unistr_case_locale.cpp"
"${ICU_SOURCE_DIR}/common/ustrcase_locale.cpp"
"${ICU_SOURCE_DIR}/common/unistr_titlecase_brkiter.cpp"
"${ICU_SOURCE_DIR}/common/ustr_titlecase_brkiter.cpp"
"${ICU_SOURCE_DIR}/common/normalizer2impl.cpp"
"${ICU_SOURCE_DIR}/common/normalizer2.cpp"
"${ICU_SOURCE_DIR}/common/filterednormalizer2.cpp"
"${ICU_SOURCE_DIR}/common/normlzr.cpp"
"${ICU_SOURCE_DIR}/common/unorm.cpp"
"${ICU_SOURCE_DIR}/common/unormcmp.cpp"
"${ICU_SOURCE_DIR}/common/loadednormalizer2impl.cpp"
"${ICU_SOURCE_DIR}/common/chariter.cpp"
"${ICU_SOURCE_DIR}/common/schriter.cpp"
"${ICU_SOURCE_DIR}/common/uchriter.cpp"
"${ICU_SOURCE_DIR}/common/uiter.cpp"
"${ICU_SOURCE_DIR}/common/patternprops.cpp"
"${ICU_SOURCE_DIR}/common/uchar.cpp"
"${ICU_SOURCE_DIR}/common/uprops.cpp"
"${ICU_SOURCE_DIR}/common/ucase.cpp"
"${ICU_SOURCE_DIR}/common/propname.cpp"
"${ICU_SOURCE_DIR}/common/ubidi_props.cpp"
"${ICU_SOURCE_DIR}/common/characterproperties.cpp"
"${ICU_SOURCE_DIR}/common/ubidi.cpp"
"${ICU_SOURCE_DIR}/common/ubidiwrt.cpp"
"${ICU_SOURCE_DIR}/common/ubidiln.cpp"
"${ICU_SOURCE_DIR}/common/ushape.cpp"
"${ICU_SOURCE_DIR}/common/uscript.cpp"
"${ICU_SOURCE_DIR}/common/uscript_props.cpp"
"${ICU_SOURCE_DIR}/common/usc_impl.cpp"
"${ICU_SOURCE_DIR}/common/unames.cpp"
"${ICU_SOURCE_DIR}/common/utrie.cpp"
"${ICU_SOURCE_DIR}/common/utrie2.cpp"
"${ICU_SOURCE_DIR}/common/utrie2_builder.cpp"
"${ICU_SOURCE_DIR}/common/ucptrie.cpp"
"${ICU_SOURCE_DIR}/common/umutablecptrie.cpp"
"${ICU_SOURCE_DIR}/common/bmpset.cpp" "${ICU_SOURCE_DIR}/common/bmpset.cpp"
"${ICU_SOURCE_DIR}/common/unisetspan.cpp"
"${ICU_SOURCE_DIR}/common/uset_props.cpp"
"${ICU_SOURCE_DIR}/common/uniset_props.cpp"
"${ICU_SOURCE_DIR}/common/uniset_closure.cpp"
"${ICU_SOURCE_DIR}/common/uset.cpp"
"${ICU_SOURCE_DIR}/common/uniset.cpp"
"${ICU_SOURCE_DIR}/common/usetiter.cpp"
"${ICU_SOURCE_DIR}/common/ruleiter.cpp"
"${ICU_SOURCE_DIR}/common/caniter.cpp"
"${ICU_SOURCE_DIR}/common/unifilt.cpp"
"${ICU_SOURCE_DIR}/common/unifunct.cpp"
"${ICU_SOURCE_DIR}/common/uarrsort.cpp"
"${ICU_SOURCE_DIR}/common/brkiter.cpp"
"${ICU_SOURCE_DIR}/common/ubrk.cpp"
"${ICU_SOURCE_DIR}/common/brkeng.cpp" "${ICU_SOURCE_DIR}/common/brkeng.cpp"
"${ICU_SOURCE_DIR}/common/brkiter.cpp"
"${ICU_SOURCE_DIR}/common/bytesinkutil.cpp"
"${ICU_SOURCE_DIR}/common/bytestream.cpp"
"${ICU_SOURCE_DIR}/common/bytestrie.cpp"
"${ICU_SOURCE_DIR}/common/bytestriebuilder.cpp"
"${ICU_SOURCE_DIR}/common/bytestrieiterator.cpp"
"${ICU_SOURCE_DIR}/common/caniter.cpp"
"${ICU_SOURCE_DIR}/common/characterproperties.cpp"
"${ICU_SOURCE_DIR}/common/chariter.cpp"
"${ICU_SOURCE_DIR}/common/charstr.cpp"
"${ICU_SOURCE_DIR}/common/cmemory.cpp"
"${ICU_SOURCE_DIR}/common/cstr.cpp"
"${ICU_SOURCE_DIR}/common/cstring.cpp"
"${ICU_SOURCE_DIR}/common/cwchar.cpp"
"${ICU_SOURCE_DIR}/common/dictbe.cpp" "${ICU_SOURCE_DIR}/common/dictbe.cpp"
"${ICU_SOURCE_DIR}/common/dictionarydata.cpp"
"${ICU_SOURCE_DIR}/common/dtintrv.cpp"
"${ICU_SOURCE_DIR}/common/edits.cpp"
"${ICU_SOURCE_DIR}/common/emojiprops.cpp"
"${ICU_SOURCE_DIR}/common/errorcode.cpp"
"${ICU_SOURCE_DIR}/common/filteredbrk.cpp" "${ICU_SOURCE_DIR}/common/filteredbrk.cpp"
"${ICU_SOURCE_DIR}/common/filterednormalizer2.cpp"
"${ICU_SOURCE_DIR}/common/icudataver.cpp"
"${ICU_SOURCE_DIR}/common/icuplug.cpp"
"${ICU_SOURCE_DIR}/common/loadednormalizer2impl.cpp"
"${ICU_SOURCE_DIR}/common/localebuilder.cpp"
"${ICU_SOURCE_DIR}/common/localematcher.cpp"
"${ICU_SOURCE_DIR}/common/localeprioritylist.cpp"
"${ICU_SOURCE_DIR}/common/locavailable.cpp"
"${ICU_SOURCE_DIR}/common/locbased.cpp"
"${ICU_SOURCE_DIR}/common/locdispnames.cpp"
"${ICU_SOURCE_DIR}/common/locdistance.cpp"
"${ICU_SOURCE_DIR}/common/locdspnm.cpp"
"${ICU_SOURCE_DIR}/common/locid.cpp"
"${ICU_SOURCE_DIR}/common/loclikely.cpp"
"${ICU_SOURCE_DIR}/common/loclikelysubtags.cpp"
"${ICU_SOURCE_DIR}/common/locmap.cpp"
"${ICU_SOURCE_DIR}/common/locresdata.cpp"
"${ICU_SOURCE_DIR}/common/locutil.cpp"
"${ICU_SOURCE_DIR}/common/lsr.cpp"
"${ICU_SOURCE_DIR}/common/lstmbe.cpp"
"${ICU_SOURCE_DIR}/common/messagepattern.cpp"
"${ICU_SOURCE_DIR}/common/mlbe.cpp"
"${ICU_SOURCE_DIR}/common/normalizer2.cpp"
"${ICU_SOURCE_DIR}/common/normalizer2impl.cpp"
"${ICU_SOURCE_DIR}/common/normlzr.cpp"
"${ICU_SOURCE_DIR}/common/parsepos.cpp"
"${ICU_SOURCE_DIR}/common/patternprops.cpp"
"${ICU_SOURCE_DIR}/common/pluralmap.cpp"
"${ICU_SOURCE_DIR}/common/propname.cpp"
"${ICU_SOURCE_DIR}/common/propsvec.cpp"
"${ICU_SOURCE_DIR}/common/punycode.cpp"
"${ICU_SOURCE_DIR}/common/putil.cpp"
"${ICU_SOURCE_DIR}/common/rbbi.cpp" "${ICU_SOURCE_DIR}/common/rbbi.cpp"
"${ICU_SOURCE_DIR}/common/rbbi_cache.cpp"
"${ICU_SOURCE_DIR}/common/rbbidata.cpp" "${ICU_SOURCE_DIR}/common/rbbidata.cpp"
"${ICU_SOURCE_DIR}/common/rbbinode.cpp" "${ICU_SOURCE_DIR}/common/rbbinode.cpp"
"${ICU_SOURCE_DIR}/common/rbbirb.cpp" "${ICU_SOURCE_DIR}/common/rbbirb.cpp"
@ -178,166 +80,180 @@ set(ICUUC_SOURCES
"${ICU_SOURCE_DIR}/common/rbbisetb.cpp" "${ICU_SOURCE_DIR}/common/rbbisetb.cpp"
"${ICU_SOURCE_DIR}/common/rbbistbl.cpp" "${ICU_SOURCE_DIR}/common/rbbistbl.cpp"
"${ICU_SOURCE_DIR}/common/rbbitblb.cpp" "${ICU_SOURCE_DIR}/common/rbbitblb.cpp"
"${ICU_SOURCE_DIR}/common/rbbi_cache.cpp" "${ICU_SOURCE_DIR}/common/resbund.cpp"
"${ICU_SOURCE_DIR}/common/resbund_cnv.cpp"
"${ICU_SOURCE_DIR}/common/resource.cpp"
"${ICU_SOURCE_DIR}/common/restrace.cpp"
"${ICU_SOURCE_DIR}/common/ruleiter.cpp"
"${ICU_SOURCE_DIR}/common/schriter.cpp"
"${ICU_SOURCE_DIR}/common/serv.cpp" "${ICU_SOURCE_DIR}/common/serv.cpp"
"${ICU_SOURCE_DIR}/common/servnotf.cpp"
"${ICU_SOURCE_DIR}/common/servls.cpp"
"${ICU_SOURCE_DIR}/common/servlk.cpp" "${ICU_SOURCE_DIR}/common/servlk.cpp"
"${ICU_SOURCE_DIR}/common/servlkf.cpp" "${ICU_SOURCE_DIR}/common/servlkf.cpp"
"${ICU_SOURCE_DIR}/common/servls.cpp"
"${ICU_SOURCE_DIR}/common/servnotf.cpp"
"${ICU_SOURCE_DIR}/common/servrbf.cpp" "${ICU_SOURCE_DIR}/common/servrbf.cpp"
"${ICU_SOURCE_DIR}/common/servslkf.cpp" "${ICU_SOURCE_DIR}/common/servslkf.cpp"
"${ICU_SOURCE_DIR}/common/uidna.cpp"
"${ICU_SOURCE_DIR}/common/usprep.cpp"
"${ICU_SOURCE_DIR}/common/uts46.cpp"
"${ICU_SOURCE_DIR}/common/punycode.cpp"
"${ICU_SOURCE_DIR}/common/util.cpp"
"${ICU_SOURCE_DIR}/common/util_props.cpp"
"${ICU_SOURCE_DIR}/common/parsepos.cpp"
"${ICU_SOURCE_DIR}/common/locbased.cpp"
"${ICU_SOURCE_DIR}/common/cwchar.cpp"
"${ICU_SOURCE_DIR}/common/wintz.cpp"
"${ICU_SOURCE_DIR}/common/dtintrv.cpp"
"${ICU_SOURCE_DIR}/common/ucnvsel.cpp"
"${ICU_SOURCE_DIR}/common/propsvec.cpp"
"${ICU_SOURCE_DIR}/common/ulist.cpp"
"${ICU_SOURCE_DIR}/common/uloc_tag.cpp"
"${ICU_SOURCE_DIR}/common/icudataver.cpp"
"${ICU_SOURCE_DIR}/common/icuplug.cpp"
"${ICU_SOURCE_DIR}/common/sharedobject.cpp" "${ICU_SOURCE_DIR}/common/sharedobject.cpp"
"${ICU_SOURCE_DIR}/common/simpleformatter.cpp" "${ICU_SOURCE_DIR}/common/simpleformatter.cpp"
"${ICU_SOURCE_DIR}/common/unifiedcache.cpp"
"${ICU_SOURCE_DIR}/common/uloc_keytype.cpp"
"${ICU_SOURCE_DIR}/common/ubiditransform.cpp"
"${ICU_SOURCE_DIR}/common/pluralmap.cpp"
"${ICU_SOURCE_DIR}/common/static_unicode_sets.cpp" "${ICU_SOURCE_DIR}/common/static_unicode_sets.cpp"
"${ICU_SOURCE_DIR}/common/restrace.cpp" "${ICU_SOURCE_DIR}/common/stringpiece.cpp"
"${ICU_SOURCE_DIR}/common/emojiprops.cpp" "${ICU_SOURCE_DIR}/common/stringtriebuilder.cpp"
"${ICU_SOURCE_DIR}/common/lstmbe.cpp") "${ICU_SOURCE_DIR}/common/uarrsort.cpp"
"${ICU_SOURCE_DIR}/common/ubidi.cpp"
"${ICU_SOURCE_DIR}/common/ubidi_props.cpp"
"${ICU_SOURCE_DIR}/common/ubidiln.cpp"
"${ICU_SOURCE_DIR}/common/ubiditransform.cpp"
"${ICU_SOURCE_DIR}/common/ubidiwrt.cpp"
"${ICU_SOURCE_DIR}/common/ubrk.cpp"
"${ICU_SOURCE_DIR}/common/ucase.cpp"
"${ICU_SOURCE_DIR}/common/ucasemap.cpp"
"${ICU_SOURCE_DIR}/common/ucasemap_titlecase_brkiter.cpp"
"${ICU_SOURCE_DIR}/common/ucat.cpp"
"${ICU_SOURCE_DIR}/common/uchar.cpp"
"${ICU_SOURCE_DIR}/common/ucharstrie.cpp"
"${ICU_SOURCE_DIR}/common/ucharstriebuilder.cpp"
"${ICU_SOURCE_DIR}/common/ucharstrieiterator.cpp"
"${ICU_SOURCE_DIR}/common/uchriter.cpp"
"${ICU_SOURCE_DIR}/common/ucln_cmn.cpp"
"${ICU_SOURCE_DIR}/common/ucmndata.cpp"
"${ICU_SOURCE_DIR}/common/ucnv.cpp"
"${ICU_SOURCE_DIR}/common/ucnv2022.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_bld.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_cb.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_cnv.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_ct.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_err.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_ext.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_io.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_lmb.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_set.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_u16.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_u32.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_u7.cpp"
"${ICU_SOURCE_DIR}/common/ucnv_u8.cpp"
"${ICU_SOURCE_DIR}/common/ucnvbocu.cpp"
"${ICU_SOURCE_DIR}/common/ucnvdisp.cpp"
"${ICU_SOURCE_DIR}/common/ucnvhz.cpp"
"${ICU_SOURCE_DIR}/common/ucnvisci.cpp"
"${ICU_SOURCE_DIR}/common/ucnvlat1.cpp"
"${ICU_SOURCE_DIR}/common/ucnvmbcs.cpp"
"${ICU_SOURCE_DIR}/common/ucnvscsu.cpp"
"${ICU_SOURCE_DIR}/common/ucnvsel.cpp"
"${ICU_SOURCE_DIR}/common/ucol_swp.cpp"
"${ICU_SOURCE_DIR}/common/ucptrie.cpp"
"${ICU_SOURCE_DIR}/common/ucurr.cpp"
"${ICU_SOURCE_DIR}/common/udata.cpp"
"${ICU_SOURCE_DIR}/common/udatamem.cpp"
"${ICU_SOURCE_DIR}/common/udataswp.cpp"
"${ICU_SOURCE_DIR}/common/uenum.cpp"
"${ICU_SOURCE_DIR}/common/uhash.cpp"
"${ICU_SOURCE_DIR}/common/uhash_us.cpp"
"${ICU_SOURCE_DIR}/common/uidna.cpp"
"${ICU_SOURCE_DIR}/common/uinit.cpp"
"${ICU_SOURCE_DIR}/common/uinvchar.cpp"
"${ICU_SOURCE_DIR}/common/uiter.cpp"
"${ICU_SOURCE_DIR}/common/ulist.cpp"
"${ICU_SOURCE_DIR}/common/uloc.cpp"
"${ICU_SOURCE_DIR}/common/uloc_keytype.cpp"
"${ICU_SOURCE_DIR}/common/uloc_tag.cpp"
"${ICU_SOURCE_DIR}/common/ulocale.cpp"
"${ICU_SOURCE_DIR}/common/ulocbuilder.cpp"
"${ICU_SOURCE_DIR}/common/umapfile.cpp"
"${ICU_SOURCE_DIR}/common/umath.cpp"
"${ICU_SOURCE_DIR}/common/umutablecptrie.cpp"
"${ICU_SOURCE_DIR}/common/umutex.cpp"
"${ICU_SOURCE_DIR}/common/unames.cpp"
"${ICU_SOURCE_DIR}/common/unifiedcache.cpp"
"${ICU_SOURCE_DIR}/common/unifilt.cpp"
"${ICU_SOURCE_DIR}/common/unifunct.cpp"
"${ICU_SOURCE_DIR}/common/uniset.cpp"
"${ICU_SOURCE_DIR}/common/uniset_closure.cpp"
"${ICU_SOURCE_DIR}/common/uniset_props.cpp"
"${ICU_SOURCE_DIR}/common/unisetspan.cpp"
"${ICU_SOURCE_DIR}/common/unistr.cpp"
"${ICU_SOURCE_DIR}/common/unistr_case.cpp"
"${ICU_SOURCE_DIR}/common/unistr_case_locale.cpp"
"${ICU_SOURCE_DIR}/common/unistr_cnv.cpp"
"${ICU_SOURCE_DIR}/common/unistr_props.cpp"
"${ICU_SOURCE_DIR}/common/unistr_titlecase_brkiter.cpp"
"${ICU_SOURCE_DIR}/common/unorm.cpp"
"${ICU_SOURCE_DIR}/common/unormcmp.cpp"
"${ICU_SOURCE_DIR}/common/uobject.cpp"
"${ICU_SOURCE_DIR}/common/uprops.cpp"
"${ICU_SOURCE_DIR}/common/ures_cnv.cpp"
"${ICU_SOURCE_DIR}/common/uresbund.cpp"
"${ICU_SOURCE_DIR}/common/uresdata.cpp"
"${ICU_SOURCE_DIR}/common/usc_impl.cpp"
"${ICU_SOURCE_DIR}/common/uscript.cpp"
"${ICU_SOURCE_DIR}/common/uscript_props.cpp"
"${ICU_SOURCE_DIR}/common/uset.cpp"
"${ICU_SOURCE_DIR}/common/uset_props.cpp"
"${ICU_SOURCE_DIR}/common/usetiter.cpp"
"${ICU_SOURCE_DIR}/common/ushape.cpp"
"${ICU_SOURCE_DIR}/common/usprep.cpp"
"${ICU_SOURCE_DIR}/common/ustack.cpp"
"${ICU_SOURCE_DIR}/common/ustr_cnv.cpp"
"${ICU_SOURCE_DIR}/common/ustr_titlecase_brkiter.cpp"
"${ICU_SOURCE_DIR}/common/ustr_wcs.cpp"
"${ICU_SOURCE_DIR}/common/ustrcase.cpp"
"${ICU_SOURCE_DIR}/common/ustrcase_locale.cpp"
"${ICU_SOURCE_DIR}/common/ustrenum.cpp"
"${ICU_SOURCE_DIR}/common/ustrfmt.cpp"
"${ICU_SOURCE_DIR}/common/ustring.cpp"
"${ICU_SOURCE_DIR}/common/ustrtrns.cpp"
"${ICU_SOURCE_DIR}/common/utext.cpp"
"${ICU_SOURCE_DIR}/common/utf_impl.cpp"
"${ICU_SOURCE_DIR}/common/util.cpp"
"${ICU_SOURCE_DIR}/common/util_props.cpp"
"${ICU_SOURCE_DIR}/common/utrace.cpp"
"${ICU_SOURCE_DIR}/common/utrie.cpp"
"${ICU_SOURCE_DIR}/common/utrie2.cpp"
"${ICU_SOURCE_DIR}/common/utrie2_builder.cpp"
"${ICU_SOURCE_DIR}/common/utrie_swap.cpp"
"${ICU_SOURCE_DIR}/common/uts46.cpp"
"${ICU_SOURCE_DIR}/common/utypes.cpp"
"${ICU_SOURCE_DIR}/common/uvector.cpp"
"${ICU_SOURCE_DIR}/common/uvectr32.cpp"
"${ICU_SOURCE_DIR}/common/uvectr64.cpp"
"${ICU_SOURCE_DIR}/common/wintz.cpp")
set(ICUI18N_SOURCES set(ICUI18N_SOURCES
"${ICU_SOURCE_DIR}/i18n/ucln_in.cpp" "${ICU_SOURCE_DIR}/i18n/alphaindex.cpp"
"${ICU_SOURCE_DIR}/i18n/fmtable.cpp" "${ICU_SOURCE_DIR}/i18n/anytrans.cpp"
"${ICU_SOURCE_DIR}/i18n/format.cpp"
"${ICU_SOURCE_DIR}/i18n/msgfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/umsg.cpp"
"${ICU_SOURCE_DIR}/i18n/numfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/unum.cpp"
"${ICU_SOURCE_DIR}/i18n/decimfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/dcfmtsym.cpp"
"${ICU_SOURCE_DIR}/i18n/fmtable_cnv.cpp"
"${ICU_SOURCE_DIR}/i18n/choicfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/datefmt.cpp"
"${ICU_SOURCE_DIR}/i18n/smpdtfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/reldtfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/dtfmtsym.cpp"
"${ICU_SOURCE_DIR}/i18n/udat.cpp"
"${ICU_SOURCE_DIR}/i18n/dtptngen.cpp"
"${ICU_SOURCE_DIR}/i18n/udatpg.cpp"
"${ICU_SOURCE_DIR}/i18n/nfrs.cpp"
"${ICU_SOURCE_DIR}/i18n/nfrule.cpp"
"${ICU_SOURCE_DIR}/i18n/nfsubs.cpp"
"${ICU_SOURCE_DIR}/i18n/rbnf.cpp"
"${ICU_SOURCE_DIR}/i18n/numsys.cpp"
"${ICU_SOURCE_DIR}/i18n/unumsys.cpp"
"${ICU_SOURCE_DIR}/i18n/ucsdet.cpp"
"${ICU_SOURCE_DIR}/i18n/ucal.cpp"
"${ICU_SOURCE_DIR}/i18n/calendar.cpp"
"${ICU_SOURCE_DIR}/i18n/gregocal.cpp"
"${ICU_SOURCE_DIR}/i18n/timezone.cpp"
"${ICU_SOURCE_DIR}/i18n/simpletz.cpp"
"${ICU_SOURCE_DIR}/i18n/olsontz.cpp"
"${ICU_SOURCE_DIR}/i18n/astro.cpp" "${ICU_SOURCE_DIR}/i18n/astro.cpp"
"${ICU_SOURCE_DIR}/i18n/taiwncal.cpp" "${ICU_SOURCE_DIR}/i18n/basictz.cpp"
"${ICU_SOURCE_DIR}/i18n/bocsu.cpp"
"${ICU_SOURCE_DIR}/i18n/brktrans.cpp"
"${ICU_SOURCE_DIR}/i18n/buddhcal.cpp" "${ICU_SOURCE_DIR}/i18n/buddhcal.cpp"
"${ICU_SOURCE_DIR}/i18n/persncal.cpp" "${ICU_SOURCE_DIR}/i18n/calendar.cpp"
"${ICU_SOURCE_DIR}/i18n/islamcal.cpp" "${ICU_SOURCE_DIR}/i18n/casetrn.cpp"
"${ICU_SOURCE_DIR}/i18n/japancal.cpp"
"${ICU_SOURCE_DIR}/i18n/gregoimp.cpp"
"${ICU_SOURCE_DIR}/i18n/hebrwcal.cpp"
"${ICU_SOURCE_DIR}/i18n/indiancal.cpp"
"${ICU_SOURCE_DIR}/i18n/chnsecal.cpp"
"${ICU_SOURCE_DIR}/i18n/cecal.cpp" "${ICU_SOURCE_DIR}/i18n/cecal.cpp"
"${ICU_SOURCE_DIR}/i18n/coptccal.cpp" "${ICU_SOURCE_DIR}/i18n/chnsecal.cpp"
"${ICU_SOURCE_DIR}/i18n/dangical.cpp" "${ICU_SOURCE_DIR}/i18n/choicfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/ethpccal.cpp"
"${ICU_SOURCE_DIR}/i18n/coleitr.cpp" "${ICU_SOURCE_DIR}/i18n/coleitr.cpp"
"${ICU_SOURCE_DIR}/i18n/coll.cpp" "${ICU_SOURCE_DIR}/i18n/coll.cpp"
"${ICU_SOURCE_DIR}/i18n/sortkey.cpp"
"${ICU_SOURCE_DIR}/i18n/bocsu.cpp"
"${ICU_SOURCE_DIR}/i18n/ucoleitr.cpp"
"${ICU_SOURCE_DIR}/i18n/ucol.cpp"
"${ICU_SOURCE_DIR}/i18n/ucol_res.cpp"
"${ICU_SOURCE_DIR}/i18n/ucol_sit.cpp"
"${ICU_SOURCE_DIR}/i18n/collation.cpp" "${ICU_SOURCE_DIR}/i18n/collation.cpp"
"${ICU_SOURCE_DIR}/i18n/collationsettings.cpp" "${ICU_SOURCE_DIR}/i18n/collationbuilder.cpp"
"${ICU_SOURCE_DIR}/i18n/collationcompare.cpp"
"${ICU_SOURCE_DIR}/i18n/collationdata.cpp" "${ICU_SOURCE_DIR}/i18n/collationdata.cpp"
"${ICU_SOURCE_DIR}/i18n/collationtailoring.cpp" "${ICU_SOURCE_DIR}/i18n/collationdatabuilder.cpp"
"${ICU_SOURCE_DIR}/i18n/collationdatareader.cpp" "${ICU_SOURCE_DIR}/i18n/collationdatareader.cpp"
"${ICU_SOURCE_DIR}/i18n/collationdatawriter.cpp" "${ICU_SOURCE_DIR}/i18n/collationdatawriter.cpp"
"${ICU_SOURCE_DIR}/i18n/collationfastlatin.cpp"
"${ICU_SOURCE_DIR}/i18n/collationfastlatinbuilder.cpp"
"${ICU_SOURCE_DIR}/i18n/collationfcd.cpp" "${ICU_SOURCE_DIR}/i18n/collationfcd.cpp"
"${ICU_SOURCE_DIR}/i18n/collationiterator.cpp" "${ICU_SOURCE_DIR}/i18n/collationiterator.cpp"
"${ICU_SOURCE_DIR}/i18n/utf16collationiterator.cpp"
"${ICU_SOURCE_DIR}/i18n/utf8collationiterator.cpp"
"${ICU_SOURCE_DIR}/i18n/uitercollationiterator.cpp"
"${ICU_SOURCE_DIR}/i18n/collationsets.cpp"
"${ICU_SOURCE_DIR}/i18n/collationcompare.cpp"
"${ICU_SOURCE_DIR}/i18n/collationfastlatin.cpp"
"${ICU_SOURCE_DIR}/i18n/collationkeys.cpp" "${ICU_SOURCE_DIR}/i18n/collationkeys.cpp"
"${ICU_SOURCE_DIR}/i18n/rulebasedcollator.cpp"
"${ICU_SOURCE_DIR}/i18n/collationroot.cpp" "${ICU_SOURCE_DIR}/i18n/collationroot.cpp"
"${ICU_SOURCE_DIR}/i18n/collationrootelements.cpp" "${ICU_SOURCE_DIR}/i18n/collationrootelements.cpp"
"${ICU_SOURCE_DIR}/i18n/collationdatabuilder.cpp"
"${ICU_SOURCE_DIR}/i18n/collationweights.cpp"
"${ICU_SOURCE_DIR}/i18n/collationruleparser.cpp" "${ICU_SOURCE_DIR}/i18n/collationruleparser.cpp"
"${ICU_SOURCE_DIR}/i18n/collationbuilder.cpp" "${ICU_SOURCE_DIR}/i18n/collationsets.cpp"
"${ICU_SOURCE_DIR}/i18n/collationfastlatinbuilder.cpp" "${ICU_SOURCE_DIR}/i18n/collationsettings.cpp"
"${ICU_SOURCE_DIR}/i18n/listformatter.cpp" "${ICU_SOURCE_DIR}/i18n/collationtailoring.cpp"
"${ICU_SOURCE_DIR}/i18n/ulistformatter.cpp" "${ICU_SOURCE_DIR}/i18n/collationweights.cpp"
"${ICU_SOURCE_DIR}/i18n/strmatch.cpp" "${ICU_SOURCE_DIR}/i18n/compactdecimalformat.cpp"
"${ICU_SOURCE_DIR}/i18n/usearch.cpp" "${ICU_SOURCE_DIR}/i18n/coptccal.cpp"
"${ICU_SOURCE_DIR}/i18n/search.cpp"
"${ICU_SOURCE_DIR}/i18n/stsearch.cpp"
"${ICU_SOURCE_DIR}/i18n/translit.cpp"
"${ICU_SOURCE_DIR}/i18n/utrans.cpp"
"${ICU_SOURCE_DIR}/i18n/esctrn.cpp"
"${ICU_SOURCE_DIR}/i18n/unesctrn.cpp"
"${ICU_SOURCE_DIR}/i18n/funcrepl.cpp"
"${ICU_SOURCE_DIR}/i18n/strrepl.cpp"
"${ICU_SOURCE_DIR}/i18n/tridpars.cpp"
"${ICU_SOURCE_DIR}/i18n/cpdtrans.cpp" "${ICU_SOURCE_DIR}/i18n/cpdtrans.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt_data.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt_pars.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt_rule.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt_set.cpp"
"${ICU_SOURCE_DIR}/i18n/nultrans.cpp"
"${ICU_SOURCE_DIR}/i18n/remtrans.cpp"
"${ICU_SOURCE_DIR}/i18n/casetrn.cpp"
"${ICU_SOURCE_DIR}/i18n/titletrn.cpp"
"${ICU_SOURCE_DIR}/i18n/tolowtrn.cpp"
"${ICU_SOURCE_DIR}/i18n/toupptrn.cpp"
"${ICU_SOURCE_DIR}/i18n/anytrans.cpp"
"${ICU_SOURCE_DIR}/i18n/name2uni.cpp"
"${ICU_SOURCE_DIR}/i18n/uni2name.cpp"
"${ICU_SOURCE_DIR}/i18n/nortrans.cpp"
"${ICU_SOURCE_DIR}/i18n/quant.cpp"
"${ICU_SOURCE_DIR}/i18n/transreg.cpp"
"${ICU_SOURCE_DIR}/i18n/brktrans.cpp"
"${ICU_SOURCE_DIR}/i18n/regexcmp.cpp"
"${ICU_SOURCE_DIR}/i18n/rematch.cpp"
"${ICU_SOURCE_DIR}/i18n/repattrn.cpp"
"${ICU_SOURCE_DIR}/i18n/regexst.cpp"
"${ICU_SOURCE_DIR}/i18n/regextxt.cpp"
"${ICU_SOURCE_DIR}/i18n/regeximp.cpp"
"${ICU_SOURCE_DIR}/i18n/uregex.cpp"
"${ICU_SOURCE_DIR}/i18n/uregexc.cpp"
"${ICU_SOURCE_DIR}/i18n/ulocdata.cpp"
"${ICU_SOURCE_DIR}/i18n/measfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/currfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/curramt.cpp"
"${ICU_SOURCE_DIR}/i18n/currunit.cpp"
"${ICU_SOURCE_DIR}/i18n/measure.cpp"
"${ICU_SOURCE_DIR}/i18n/utmscale.cpp"
"${ICU_SOURCE_DIR}/i18n/csdetect.cpp" "${ICU_SOURCE_DIR}/i18n/csdetect.cpp"
"${ICU_SOURCE_DIR}/i18n/csmatch.cpp" "${ICU_SOURCE_DIR}/i18n/csmatch.cpp"
"${ICU_SOURCE_DIR}/i18n/csr2022.cpp" "${ICU_SOURCE_DIR}/i18n/csr2022.cpp"
@ -346,60 +262,80 @@ set(ICUI18N_SOURCES
"${ICU_SOURCE_DIR}/i18n/csrsbcs.cpp" "${ICU_SOURCE_DIR}/i18n/csrsbcs.cpp"
"${ICU_SOURCE_DIR}/i18n/csrucode.cpp" "${ICU_SOURCE_DIR}/i18n/csrucode.cpp"
"${ICU_SOURCE_DIR}/i18n/csrutf8.cpp" "${ICU_SOURCE_DIR}/i18n/csrutf8.cpp"
"${ICU_SOURCE_DIR}/i18n/inputext.cpp" "${ICU_SOURCE_DIR}/i18n/curramt.cpp"
"${ICU_SOURCE_DIR}/i18n/wintzimpl.cpp" "${ICU_SOURCE_DIR}/i18n/currfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/windtfmt.cpp" "${ICU_SOURCE_DIR}/i18n/currpinf.cpp"
"${ICU_SOURCE_DIR}/i18n/winnmfmt.cpp" "${ICU_SOURCE_DIR}/i18n/currunit.cpp"
"${ICU_SOURCE_DIR}/i18n/basictz.cpp" "${ICU_SOURCE_DIR}/i18n/dangical.cpp"
"${ICU_SOURCE_DIR}/i18n/dtrule.cpp" "${ICU_SOURCE_DIR}/i18n/datefmt.cpp"
"${ICU_SOURCE_DIR}/i18n/rbtz.cpp" "${ICU_SOURCE_DIR}/i18n/dayperiodrules.cpp"
"${ICU_SOURCE_DIR}/i18n/tzrule.cpp" "${ICU_SOURCE_DIR}/i18n/dcfmtsym.cpp"
"${ICU_SOURCE_DIR}/i18n/tztrans.cpp" "${ICU_SOURCE_DIR}/i18n/decContext.cpp"
"${ICU_SOURCE_DIR}/i18n/vtzone.cpp" "${ICU_SOURCE_DIR}/i18n/decNumber.cpp"
"${ICU_SOURCE_DIR}/i18n/zonemeta.cpp" "${ICU_SOURCE_DIR}/i18n/decimfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/standardplural.cpp" "${ICU_SOURCE_DIR}/i18n/displayoptions.cpp"
"${ICU_SOURCE_DIR}/i18n/upluralrules.cpp" "${ICU_SOURCE_DIR}/i18n/double-conversion-bignum-dtoa.cpp"
"${ICU_SOURCE_DIR}/i18n/plurrule.cpp" "${ICU_SOURCE_DIR}/i18n/double-conversion-bignum.cpp"
"${ICU_SOURCE_DIR}/i18n/plurfmt.cpp" "${ICU_SOURCE_DIR}/i18n/double-conversion-cached-powers.cpp"
"${ICU_SOURCE_DIR}/i18n/selfmt.cpp" "${ICU_SOURCE_DIR}/i18n/double-conversion-double-to-string.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-fast-dtoa.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-string-to-double.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-strtod.cpp"
"${ICU_SOURCE_DIR}/i18n/dtfmtsym.cpp"
"${ICU_SOURCE_DIR}/i18n/dtitvfmt.cpp" "${ICU_SOURCE_DIR}/i18n/dtitvfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/dtitvinf.cpp" "${ICU_SOURCE_DIR}/i18n/dtitvinf.cpp"
"${ICU_SOURCE_DIR}/i18n/udateintervalformat.cpp" "${ICU_SOURCE_DIR}/i18n/dtptngen.cpp"
"${ICU_SOURCE_DIR}/i18n/tmunit.cpp" "${ICU_SOURCE_DIR}/i18n/dtrule.cpp"
"${ICU_SOURCE_DIR}/i18n/tmutamt.cpp" "${ICU_SOURCE_DIR}/i18n/erarules.cpp"
"${ICU_SOURCE_DIR}/i18n/tmutfmt.cpp" "${ICU_SOURCE_DIR}/i18n/esctrn.cpp"
"${ICU_SOURCE_DIR}/i18n/currpinf.cpp" "${ICU_SOURCE_DIR}/i18n/ethpccal.cpp"
"${ICU_SOURCE_DIR}/i18n/uspoof.cpp" "${ICU_SOURCE_DIR}/i18n/fmtable.cpp"
"${ICU_SOURCE_DIR}/i18n/uspoof_impl.cpp" "${ICU_SOURCE_DIR}/i18n/fmtable_cnv.cpp"
"${ICU_SOURCE_DIR}/i18n/uspoof_build.cpp" "${ICU_SOURCE_DIR}/i18n/format.cpp"
"${ICU_SOURCE_DIR}/i18n/uspoof_conf.cpp" "${ICU_SOURCE_DIR}/i18n/formatted_string_builder.cpp"
"${ICU_SOURCE_DIR}/i18n/smpdtfst.cpp" "${ICU_SOURCE_DIR}/i18n/formattedval_iterimpl.cpp"
"${ICU_SOURCE_DIR}/i18n/ztrans.cpp" "${ICU_SOURCE_DIR}/i18n/formattedval_sbimpl.cpp"
"${ICU_SOURCE_DIR}/i18n/zrule.cpp" "${ICU_SOURCE_DIR}/i18n/formattedvalue.cpp"
"${ICU_SOURCE_DIR}/i18n/vzone.cpp"
"${ICU_SOURCE_DIR}/i18n/fphdlimp.cpp" "${ICU_SOURCE_DIR}/i18n/fphdlimp.cpp"
"${ICU_SOURCE_DIR}/i18n/fpositer.cpp" "${ICU_SOURCE_DIR}/i18n/fpositer.cpp"
"${ICU_SOURCE_DIR}/i18n/ufieldpositer.cpp" "${ICU_SOURCE_DIR}/i18n/funcrepl.cpp"
"${ICU_SOURCE_DIR}/i18n/decNumber.cpp"
"${ICU_SOURCE_DIR}/i18n/decContext.cpp"
"${ICU_SOURCE_DIR}/i18n/alphaindex.cpp"
"${ICU_SOURCE_DIR}/i18n/tznames.cpp"
"${ICU_SOURCE_DIR}/i18n/tznames_impl.cpp"
"${ICU_SOURCE_DIR}/i18n/tzgnames.cpp"
"${ICU_SOURCE_DIR}/i18n/tzfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/compactdecimalformat.cpp"
"${ICU_SOURCE_DIR}/i18n/gender.cpp" "${ICU_SOURCE_DIR}/i18n/gender.cpp"
"${ICU_SOURCE_DIR}/i18n/region.cpp" "${ICU_SOURCE_DIR}/i18n/gregocal.cpp"
"${ICU_SOURCE_DIR}/i18n/scriptset.cpp" "${ICU_SOURCE_DIR}/i18n/gregoimp.cpp"
"${ICU_SOURCE_DIR}/i18n/uregion.cpp" "${ICU_SOURCE_DIR}/i18n/hebrwcal.cpp"
"${ICU_SOURCE_DIR}/i18n/reldatefmt.cpp" "${ICU_SOURCE_DIR}/i18n/indiancal.cpp"
"${ICU_SOURCE_DIR}/i18n/quantityformatter.cpp" "${ICU_SOURCE_DIR}/i18n/inputext.cpp"
"${ICU_SOURCE_DIR}/i18n/islamcal.cpp"
"${ICU_SOURCE_DIR}/i18n/iso8601cal.cpp"
"${ICU_SOURCE_DIR}/i18n/japancal.cpp"
"${ICU_SOURCE_DIR}/i18n/listformatter.cpp"
"${ICU_SOURCE_DIR}/i18n/measfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/measunit.cpp" "${ICU_SOURCE_DIR}/i18n/measunit.cpp"
"${ICU_SOURCE_DIR}/i18n/sharedbreakiterator.cpp" "${ICU_SOURCE_DIR}/i18n/measunit_extra.cpp"
"${ICU_SOURCE_DIR}/i18n/scientificnumberformatter.cpp" "${ICU_SOURCE_DIR}/i18n/measure.cpp"
"${ICU_SOURCE_DIR}/i18n/dayperiodrules.cpp" "${ICU_SOURCE_DIR}/i18n/messageformat2.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_arguments.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_checker.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_data_model.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_errors.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_evaluation.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_formattable.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_formatter.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_function_registry.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_parser.cpp"
"${ICU_SOURCE_DIR}/i18n/messageformat2_serializer.cpp"
"${ICU_SOURCE_DIR}/i18n/msgfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/name2uni.cpp"
"${ICU_SOURCE_DIR}/i18n/nfrs.cpp"
"${ICU_SOURCE_DIR}/i18n/nfrule.cpp"
"${ICU_SOURCE_DIR}/i18n/nfsubs.cpp"
"${ICU_SOURCE_DIR}/i18n/nortrans.cpp"
"${ICU_SOURCE_DIR}/i18n/nultrans.cpp"
"${ICU_SOURCE_DIR}/i18n/number_affixutils.cpp" "${ICU_SOURCE_DIR}/i18n/number_affixutils.cpp"
"${ICU_SOURCE_DIR}/i18n/number_asformat.cpp"
"${ICU_SOURCE_DIR}/i18n/number_capi.cpp"
"${ICU_SOURCE_DIR}/i18n/number_compact.cpp" "${ICU_SOURCE_DIR}/i18n/number_compact.cpp"
"${ICU_SOURCE_DIR}/i18n/number_currencysymbols.cpp"
"${ICU_SOURCE_DIR}/i18n/number_decimalquantity.cpp" "${ICU_SOURCE_DIR}/i18n/number_decimalquantity.cpp"
"${ICU_SOURCE_DIR}/i18n/number_decimfmtprops.cpp" "${ICU_SOURCE_DIR}/i18n/number_decimfmtprops.cpp"
"${ICU_SOURCE_DIR}/i18n/number_fluent.cpp" "${ICU_SOURCE_DIR}/i18n/number_fluent.cpp"
@ -407,7 +343,9 @@ set(ICUI18N_SOURCES
"${ICU_SOURCE_DIR}/i18n/number_grouping.cpp" "${ICU_SOURCE_DIR}/i18n/number_grouping.cpp"
"${ICU_SOURCE_DIR}/i18n/number_integerwidth.cpp" "${ICU_SOURCE_DIR}/i18n/number_integerwidth.cpp"
"${ICU_SOURCE_DIR}/i18n/number_longnames.cpp" "${ICU_SOURCE_DIR}/i18n/number_longnames.cpp"
"${ICU_SOURCE_DIR}/i18n/number_mapper.cpp"
"${ICU_SOURCE_DIR}/i18n/number_modifiers.cpp" "${ICU_SOURCE_DIR}/i18n/number_modifiers.cpp"
"${ICU_SOURCE_DIR}/i18n/number_multiplier.cpp"
"${ICU_SOURCE_DIR}/i18n/number_notation.cpp" "${ICU_SOURCE_DIR}/i18n/number_notation.cpp"
"${ICU_SOURCE_DIR}/i18n/number_output.cpp" "${ICU_SOURCE_DIR}/i18n/number_output.cpp"
"${ICU_SOURCE_DIR}/i18n/number_padding.cpp" "${ICU_SOURCE_DIR}/i18n/number_padding.cpp"
@ -415,46 +353,125 @@ set(ICUI18N_SOURCES
"${ICU_SOURCE_DIR}/i18n/number_patternstring.cpp" "${ICU_SOURCE_DIR}/i18n/number_patternstring.cpp"
"${ICU_SOURCE_DIR}/i18n/number_rounding.cpp" "${ICU_SOURCE_DIR}/i18n/number_rounding.cpp"
"${ICU_SOURCE_DIR}/i18n/number_scientific.cpp" "${ICU_SOURCE_DIR}/i18n/number_scientific.cpp"
"${ICU_SOURCE_DIR}/i18n/number_utils.cpp" "${ICU_SOURCE_DIR}/i18n/number_simple.cpp"
"${ICU_SOURCE_DIR}/i18n/number_asformat.cpp"
"${ICU_SOURCE_DIR}/i18n/number_mapper.cpp"
"${ICU_SOURCE_DIR}/i18n/number_multiplier.cpp"
"${ICU_SOURCE_DIR}/i18n/number_currencysymbols.cpp"
"${ICU_SOURCE_DIR}/i18n/number_skeletons.cpp" "${ICU_SOURCE_DIR}/i18n/number_skeletons.cpp"
"${ICU_SOURCE_DIR}/i18n/number_capi.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-string-to-double.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-double-to-string.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-bignum-dtoa.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-bignum.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-cached-powers.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-fast-dtoa.cpp"
"${ICU_SOURCE_DIR}/i18n/double-conversion-strtod.cpp"
"${ICU_SOURCE_DIR}/i18n/string_segment.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_parsednumber.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_impl.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_symbols.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_decimal.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_scientific.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_currency.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_affixes.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_compositions.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_validators.cpp"
"${ICU_SOURCE_DIR}/i18n/numrange_fluent.cpp"
"${ICU_SOURCE_DIR}/i18n/numrange_impl.cpp"
"${ICU_SOURCE_DIR}/i18n/erarules.cpp"
"${ICU_SOURCE_DIR}/i18n/formattedvalue.cpp"
"${ICU_SOURCE_DIR}/i18n/formattedval_iterimpl.cpp"
"${ICU_SOURCE_DIR}/i18n/formattedval_sbimpl.cpp"
"${ICU_SOURCE_DIR}/i18n/formatted_string_builder.cpp"
"${ICU_SOURCE_DIR}/i18n/measunit_extra.cpp"
"${ICU_SOURCE_DIR}/i18n/number_symbolswrapper.cpp" "${ICU_SOURCE_DIR}/i18n/number_symbolswrapper.cpp"
"${ICU_SOURCE_DIR}/i18n/number_usageprefs.cpp" "${ICU_SOURCE_DIR}/i18n/number_usageprefs.cpp"
"${ICU_SOURCE_DIR}/i18n/number_utils.cpp"
"${ICU_SOURCE_DIR}/i18n/numfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_affixes.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_compositions.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_currency.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_decimal.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_impl.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_parsednumber.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_scientific.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_symbols.cpp"
"${ICU_SOURCE_DIR}/i18n/numparse_validators.cpp"
"${ICU_SOURCE_DIR}/i18n/numrange_capi.cpp" "${ICU_SOURCE_DIR}/i18n/numrange_capi.cpp"
"${ICU_SOURCE_DIR}/i18n/numrange_fluent.cpp"
"${ICU_SOURCE_DIR}/i18n/numrange_impl.cpp"
"${ICU_SOURCE_DIR}/i18n/numsys.cpp"
"${ICU_SOURCE_DIR}/i18n/olsontz.cpp"
"${ICU_SOURCE_DIR}/i18n/persncal.cpp"
"${ICU_SOURCE_DIR}/i18n/pluralranges.cpp" "${ICU_SOURCE_DIR}/i18n/pluralranges.cpp"
"${ICU_SOURCE_DIR}/i18n/plurfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/plurrule.cpp"
"${ICU_SOURCE_DIR}/i18n/quant.cpp"
"${ICU_SOURCE_DIR}/i18n/quantityformatter.cpp"
"${ICU_SOURCE_DIR}/i18n/rbnf.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt_data.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt_pars.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt_rule.cpp"
"${ICU_SOURCE_DIR}/i18n/rbt_set.cpp"
"${ICU_SOURCE_DIR}/i18n/rbtz.cpp"
"${ICU_SOURCE_DIR}/i18n/regexcmp.cpp"
"${ICU_SOURCE_DIR}/i18n/regeximp.cpp"
"${ICU_SOURCE_DIR}/i18n/regexst.cpp"
"${ICU_SOURCE_DIR}/i18n/regextxt.cpp"
"${ICU_SOURCE_DIR}/i18n/region.cpp"
"${ICU_SOURCE_DIR}/i18n/reldatefmt.cpp"
"${ICU_SOURCE_DIR}/i18n/reldtfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/rematch.cpp"
"${ICU_SOURCE_DIR}/i18n/remtrans.cpp"
"${ICU_SOURCE_DIR}/i18n/repattrn.cpp"
"${ICU_SOURCE_DIR}/i18n/rulebasedcollator.cpp"
"${ICU_SOURCE_DIR}/i18n/scientificnumberformatter.cpp"
"${ICU_SOURCE_DIR}/i18n/scriptset.cpp"
"${ICU_SOURCE_DIR}/i18n/search.cpp"
"${ICU_SOURCE_DIR}/i18n/selfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/sharedbreakiterator.cpp"
"${ICU_SOURCE_DIR}/i18n/simpletz.cpp"
"${ICU_SOURCE_DIR}/i18n/smpdtfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/smpdtfst.cpp"
"${ICU_SOURCE_DIR}/i18n/sortkey.cpp"
"${ICU_SOURCE_DIR}/i18n/standardplural.cpp"
"${ICU_SOURCE_DIR}/i18n/string_segment.cpp"
"${ICU_SOURCE_DIR}/i18n/strmatch.cpp"
"${ICU_SOURCE_DIR}/i18n/strrepl.cpp"
"${ICU_SOURCE_DIR}/i18n/stsearch.cpp"
"${ICU_SOURCE_DIR}/i18n/taiwncal.cpp"
"${ICU_SOURCE_DIR}/i18n/timezone.cpp"
"${ICU_SOURCE_DIR}/i18n/titletrn.cpp"
"${ICU_SOURCE_DIR}/i18n/tmunit.cpp"
"${ICU_SOURCE_DIR}/i18n/tmutamt.cpp"
"${ICU_SOURCE_DIR}/i18n/tmutfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/tolowtrn.cpp"
"${ICU_SOURCE_DIR}/i18n/toupptrn.cpp"
"${ICU_SOURCE_DIR}/i18n/translit.cpp"
"${ICU_SOURCE_DIR}/i18n/transreg.cpp"
"${ICU_SOURCE_DIR}/i18n/tridpars.cpp"
"${ICU_SOURCE_DIR}/i18n/tzfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/tzgnames.cpp"
"${ICU_SOURCE_DIR}/i18n/tznames.cpp"
"${ICU_SOURCE_DIR}/i18n/tznames_impl.cpp"
"${ICU_SOURCE_DIR}/i18n/tzrule.cpp"
"${ICU_SOURCE_DIR}/i18n/tztrans.cpp"
"${ICU_SOURCE_DIR}/i18n/ucal.cpp"
"${ICU_SOURCE_DIR}/i18n/ucln_in.cpp"
"${ICU_SOURCE_DIR}/i18n/ucol.cpp"
"${ICU_SOURCE_DIR}/i18n/ucol_res.cpp"
"${ICU_SOURCE_DIR}/i18n/ucol_sit.cpp"
"${ICU_SOURCE_DIR}/i18n/ucoleitr.cpp"
"${ICU_SOURCE_DIR}/i18n/ucsdet.cpp"
"${ICU_SOURCE_DIR}/i18n/udat.cpp"
"${ICU_SOURCE_DIR}/i18n/udateintervalformat.cpp"
"${ICU_SOURCE_DIR}/i18n/udatpg.cpp"
"${ICU_SOURCE_DIR}/i18n/ufieldpositer.cpp"
"${ICU_SOURCE_DIR}/i18n/uitercollationiterator.cpp"
"${ICU_SOURCE_DIR}/i18n/ulistformatter.cpp"
"${ICU_SOURCE_DIR}/i18n/ulocdata.cpp"
"${ICU_SOURCE_DIR}/i18n/umsg.cpp"
"${ICU_SOURCE_DIR}/i18n/unesctrn.cpp"
"${ICU_SOURCE_DIR}/i18n/uni2name.cpp"
"${ICU_SOURCE_DIR}/i18n/units_complexconverter.cpp" "${ICU_SOURCE_DIR}/i18n/units_complexconverter.cpp"
"${ICU_SOURCE_DIR}/i18n/units_converter.cpp" "${ICU_SOURCE_DIR}/i18n/units_converter.cpp"
"${ICU_SOURCE_DIR}/i18n/units_data.cpp" "${ICU_SOURCE_DIR}/i18n/units_data.cpp"
"${ICU_SOURCE_DIR}/i18n/units_router.cpp") "${ICU_SOURCE_DIR}/i18n/units_router.cpp"
"${ICU_SOURCE_DIR}/i18n/unum.cpp"
"${ICU_SOURCE_DIR}/i18n/unumsys.cpp"
"${ICU_SOURCE_DIR}/i18n/upluralrules.cpp"
"${ICU_SOURCE_DIR}/i18n/uregex.cpp"
"${ICU_SOURCE_DIR}/i18n/uregexc.cpp"
"${ICU_SOURCE_DIR}/i18n/uregion.cpp"
"${ICU_SOURCE_DIR}/i18n/usearch.cpp"
"${ICU_SOURCE_DIR}/i18n/uspoof.cpp"
"${ICU_SOURCE_DIR}/i18n/uspoof_build.cpp"
"${ICU_SOURCE_DIR}/i18n/uspoof_conf.cpp"
"${ICU_SOURCE_DIR}/i18n/uspoof_impl.cpp"
"${ICU_SOURCE_DIR}/i18n/utf16collationiterator.cpp"
"${ICU_SOURCE_DIR}/i18n/utf8collationiterator.cpp"
"${ICU_SOURCE_DIR}/i18n/utmscale.cpp"
"${ICU_SOURCE_DIR}/i18n/utrans.cpp"
"${ICU_SOURCE_DIR}/i18n/vtzone.cpp"
"${ICU_SOURCE_DIR}/i18n/vzone.cpp"
"${ICU_SOURCE_DIR}/i18n/windtfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/winnmfmt.cpp"
"${ICU_SOURCE_DIR}/i18n/wintzimpl.cpp"
"${ICU_SOURCE_DIR}/i18n/zonemeta.cpp"
"${ICU_SOURCE_DIR}/i18n/zrule.cpp"
"${ICU_SOURCE_DIR}/i18n/ztrans.cpp")
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/empty.cpp" CONTENT " ") file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/empty.cpp" CONTENT " ")
enable_language(ASM) enable_language(ASM)
@ -464,6 +481,11 @@ if (ARCH_S390X)
else() else()
set(ICUDATA_SOURCE_FILE "${ICUDATA_SOURCE_DIR}/icudt75l_dat.S" ) set(ICUDATA_SOURCE_FILE "${ICUDATA_SOURCE_DIR}/icudt75l_dat.S" )
endif() endif()
# ^^ you might be confused how for different little endian platforms (x86, ARM) the same assembly files can be used.
# These files are indeed assembly but they only contain data ('.long' directive), which makes them portable accross CPUs.
# Only the endianness and the character set (ASCII, EBCDIC) makes a difference, also see
# https://unicode-org.github.io/icu/userguide/icu_data/#sharing-icu-data-between-platforms, 'Sharing ICU Data Between Platforms')
# (and as an experiment, try re-generating the data files on x86 vs. ARM, ... you'll get exactly the same files)
set(ICUDATA_SOURCES set(ICUDATA_SOURCES
"${ICUDATA_SOURCE_FILE}" "${ICUDATA_SOURCE_FILE}"

2
contrib/libarchive vendored

@ -1 +1 @@
Subproject commit ee45796171324519f0c0bfd012018dd099296336 Subproject commit 313aa1fa10b657de791e3202c168a6c833bc3543

View File

@ -1,6 +1,6 @@
set (LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/libarchive") set (LIBRARY_DIR "${ClickHouse_SOURCE_DIR}/contrib/libarchive")
set(SRCS set(SRCS
"${LIBRARY_DIR}/libarchive/archive_acl.c" "${LIBRARY_DIR}/libarchive/archive_acl.c"
"${LIBRARY_DIR}/libarchive/archive_blake2sp_ref.c" "${LIBRARY_DIR}/libarchive/archive_blake2sp_ref.c"
"${LIBRARY_DIR}/libarchive/archive_blake2s_ref.c" "${LIBRARY_DIR}/libarchive/archive_blake2s_ref.c"
@ -135,7 +135,7 @@ set(SRCS
) )
add_library(_libarchive ${SRCS}) add_library(_libarchive ${SRCS})
target_include_directories(_libarchive PUBLIC target_include_directories(_libarchive PUBLIC
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
"${LIBRARY_DIR}/libarchive" "${LIBRARY_DIR}/libarchive"
) )
@ -157,7 +157,7 @@ if (TARGET ch_contrib::zlib)
endif() endif()
if (TARGET ch_contrib::zstd) if (TARGET ch_contrib::zstd)
target_compile_definitions(_libarchive PUBLIC HAVE_ZSTD_H=1 HAVE_LIBZSTD=1 HAVE_LIBZSTD_COMPRESSOR=1) target_compile_definitions(_libarchive PUBLIC HAVE_ZSTD_H=1 HAVE_LIBZSTD=1 HAVE_ZSTD_compressStream=1)
target_link_libraries(_libarchive PRIVATE ch_contrib::zstd) target_link_libraries(_libarchive PRIVATE ch_contrib::zstd)
endif() endif()
@ -179,4 +179,4 @@ if (OS_LINUX)
) )
endif() endif()
add_library(ch_contrib::libarchive ALIAS _libarchive) add_library(ch_contrib::libarchive ALIAS _libarchive)

View File

@ -334,13 +334,16 @@ typedef uint64_t uintmax_t;
/* #undef ARCHIVE_XATTR_LINUX */ /* #undef ARCHIVE_XATTR_LINUX */
/* Version number of bsdcpio */ /* Version number of bsdcpio */
#define BSDCPIO_VERSION_STRING "3.7.0" #define BSDCPIO_VERSION_STRING "3.7.4"
/* Version number of bsdtar */ /* Version number of bsdtar */
#define BSDTAR_VERSION_STRING "3.7.0" #define BSDTAR_VERSION_STRING "3.7.4"
/* Version number of bsdcat */ /* Version number of bsdcat */
#define BSDCAT_VERSION_STRING "3.7.0" #define BSDCAT_VERSION_STRING "3.7.4"
/* Version number of bsdunzip */
#define BSDUNZIP_VERSION_STRING "3.7.4"
/* Define to 1 if you have the `acl_create_entry' function. */ /* Define to 1 if you have the `acl_create_entry' function. */
/* #undef HAVE_ACL_CREATE_ENTRY */ /* #undef HAVE_ACL_CREATE_ENTRY */
@ -642,8 +645,8 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the `getgrnam_r' function. */ /* Define to 1 if you have the `getgrnam_r' function. */
#define HAVE_GETGRNAM_R 1 #define HAVE_GETGRNAM_R 1
/* Define to 1 if platform uses `optreset` to reset `getopt` */ /* Define to 1 if you have the `getline' function. */
#define HAVE_GETOPT_OPTRESET 1 #define HAVE_GETLINE 1
/* Define to 1 if you have the `getpid' function. */ /* Define to 1 if you have the `getpid' function. */
#define HAVE_GETPID 1 #define HAVE_GETPID 1
@ -750,6 +753,12 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the `pcreposix' library (-lpcreposix). */ /* Define to 1 if you have the `pcreposix' library (-lpcreposix). */
/* #undef HAVE_LIBPCREPOSIX */ /* #undef HAVE_LIBPCREPOSIX */
/* Define to 1 if you have the `pcre2-8' library (-lpcre2-8). */
/* #undef HAVE_LIBPCRE2 */
/* Define to 1 if you have the `pcreposix' library (-lpcre2posix). */
/* #undef HAVE_LIBPCRE2POSIX */
/* Define to 1 if you have the `xml2' library (-lxml2). */ /* Define to 1 if you have the `xml2' library (-lxml2). */
#define HAVE_LIBXML2 1 #define HAVE_LIBXML2 1
@ -765,9 +774,8 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the `zstd' library (-lzstd). */ /* Define to 1 if you have the `zstd' library (-lzstd). */
/* #undef HAVE_LIBZSTD */ /* #undef HAVE_LIBZSTD */
/* Define to 1 if you have the `zstd' library (-lzstd) with compression /* Define to 1 if you have the ZSTD_compressStream function. */
support. */ /* #undef HAVE_ZSTD_compressStream */
/* #undef HAVE_LIBZSTD_COMPRESSOR */
/* Define to 1 if you have the <limits.h> header file. */ /* Define to 1 if you have the <limits.h> header file. */
#define HAVE_LIMITS_H 1 #define HAVE_LIMITS_H 1
@ -923,6 +931,9 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the <pcreposix.h> header file. */ /* Define to 1 if you have the <pcreposix.h> header file. */
/* #undef HAVE_PCREPOSIX_H */ /* #undef HAVE_PCREPOSIX_H */
/* Define to 1 if you have the <pcre2posix.h> header file. */
/* #undef HAVE_PCRE2POSIX_H */
/* Define to 1 if you have the `pipe' function. */ /* Define to 1 if you have the `pipe' function. */
#define HAVE_PIPE 1 #define HAVE_PIPE 1
@ -1029,6 +1040,12 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the `strrchr' function. */ /* Define to 1 if you have the `strrchr' function. */
#define HAVE_STRRCHR 1 #define HAVE_STRRCHR 1
/* Define to 1 if the system has the type `struct statfs'. */
/* #undef HAVE_STRUCT_STATFS */
/* Define to 1 if `f_iosize' is a member of `struct statfs'. */
/* #undef HAVE_STRUCT_STATFS_F_IOSIZE */
/* Define to 1 if `f_namemax' is a member of `struct statfs'. */ /* Define to 1 if `f_namemax' is a member of `struct statfs'. */
/* #undef HAVE_STRUCT_STATFS_F_NAMEMAX */ /* #undef HAVE_STRUCT_STATFS_F_NAMEMAX */
@ -1077,6 +1094,9 @@ typedef uint64_t uintmax_t;
/* Define to 1 if you have the `symlink' function. */ /* Define to 1 if you have the `symlink' function. */
#define HAVE_SYMLINK 1 #define HAVE_SYMLINK 1
/* Define to 1 if you have the `sysconf' function. */
#define HAVE_SYSCONF 1
/* Define to 1 if you have the <sys/acl.h> header file. */ /* Define to 1 if you have the <sys/acl.h> header file. */
/* #undef HAVE_SYS_ACL_H */ /* #undef HAVE_SYS_ACL_H */
@ -1273,13 +1293,13 @@ typedef uint64_t uintmax_t;
/* #undef HAVE__MKGMTIME */ /* #undef HAVE__MKGMTIME */
/* Define as const if the declaration of iconv() needs const. */ /* Define as const if the declaration of iconv() needs const. */
#define ICONV_CONST #define ICONV_CONST
/* Version number of libarchive as a single integer */ /* Version number of libarchive as a single integer */
#define LIBARCHIVE_VERSION_NUMBER "3007000" #define LIBARCHIVE_VERSION_NUMBER "3007004"
/* Version number of libarchive */ /* Version number of libarchive */
#define LIBARCHIVE_VERSION_STRING "3.7.0" #define LIBARCHIVE_VERSION_STRING "3.7.4"
/* Define to 1 if `lstat' dereferences a symlink specified with a trailing /* Define to 1 if `lstat' dereferences a symlink specified with a trailing
slash. */ slash. */
@ -1333,7 +1353,7 @@ typedef uint64_t uintmax_t;
#endif /* SAFE_TO_DEFINE_EXTENSIONS */ #endif /* SAFE_TO_DEFINE_EXTENSIONS */
/* Version number of package */ /* Version number of package */
#define VERSION "3.7.0" #define VERSION "3.7.4"
/* Number of bits in a file offset, on hosts where this is settable. */ /* Number of bits in a file offset, on hosts where this is settable. */
/* #undef _FILE_OFFSET_BITS */ /* #undef _FILE_OFFSET_BITS */

2
contrib/libuv vendored

@ -1 +1 @@
Subproject commit 4482964660c77eec1166cd7d14fb915e3dbd774a Subproject commit 714b58b9849568211ade86b44dd91d37f8a2175e

View File

@ -10,6 +10,7 @@ set(uv_sources
src/random.c src/random.c
src/strscpy.c src/strscpy.c
src/strtok.c src/strtok.c
src/thread-common.c
src/threadpool.c src/threadpool.c
src/timer.c src/timer.c
src/uv-common.c src/uv-common.c
@ -70,10 +71,7 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112) list(APPEND uv_defines _GNU_SOURCE _POSIX_C_SOURCE=200112)
list(APPEND uv_libraries rt) list(APPEND uv_libraries rt)
list(APPEND uv_sources list(APPEND uv_sources
src/unix/epoll.c src/unix/linux.c
src/unix/linux-core.c
src/unix/linux-inotify.c
src/unix/linux-syscalls.c
src/unix/procfs-exepath.c src/unix/procfs-exepath.c
src/unix/random-getrandom.c src/unix/random-getrandom.c
src/unix/random-sysctl-linux.c) src/unix/random-sysctl-linux.c)

View File

@ -140,6 +140,12 @@ if (CMAKE_CROSSCOMPILING)
message (STATUS "CROSS COMPILING SET LLVM HOST TRIPLE ${LLVM_HOST_TRIPLE}") message (STATUS "CROSS COMPILING SET LLVM HOST TRIPLE ${LLVM_HOST_TRIPLE}")
endif() endif()
# llvm-project/llvm/cmake/config-ix.cmake does a weird thing: it defines _LARGEFILE64_SOURCE,
# then checks if lseek64() function exists, then undefines _LARGEFILE64_SOURCE.
# Then the actual code that uses this function *doesn't* define _LARGEFILE64_SOURCE, so lseek64()
# may not exist and compilation fails. This happens with musl.
add_compile_definitions("_LARGEFILE64_SOURCE")
add_subdirectory ("${LLVM_SOURCE_DIR}" "${LLVM_BINARY_DIR}") add_subdirectory ("${LLVM_SOURCE_DIR}" "${LLVM_BINARY_DIR}")
set_directory_properties (PROPERTIES set_directory_properties (PROPERTIES

2
contrib/openssl vendored

@ -1 +1 @@
Subproject commit 66deddc1e53cda8706604a019777259372d1bd62 Subproject commit b3e62c440f390e12e77c80675f883af82ad3d5ed

2
contrib/sysroot vendored

@ -1 +1 @@
Subproject commit cc385041b226d1fc28ead14dbab5d40a5f821dd8 Subproject commit 5be834147d5b5dd77ca2b821f356982029320513

View File

@ -34,7 +34,7 @@ RUN arch=${TARGETARCH:-amd64} \
# lts / testing / prestable / etc # lts / testing / prestable / etc
ARG REPO_CHANNEL="stable" ARG REPO_CHANNEL="stable"
ARG REPOSITORY="https://packages.clickhouse.com/tgz/${REPO_CHANNEL}" ARG REPOSITORY="https://packages.clickhouse.com/tgz/${REPO_CHANNEL}"
ARG VERSION="24.8.3.59" ARG VERSION="24.8.4.13"
ARG PACKAGES="clickhouse-keeper" ARG PACKAGES="clickhouse-keeper"
ARG DIRECT_DOWNLOAD_URLS="" ARG DIRECT_DOWNLOAD_URLS=""

View File

@ -32,7 +32,7 @@ RUN arch=${TARGETARCH:-amd64} \
# lts / testing / prestable / etc # lts / testing / prestable / etc
ARG REPO_CHANNEL="stable" ARG REPO_CHANNEL="stable"
ARG REPOSITORY="https://packages.clickhouse.com/tgz/${REPO_CHANNEL}" ARG REPOSITORY="https://packages.clickhouse.com/tgz/${REPO_CHANNEL}"
ARG VERSION="24.8.3.59" ARG VERSION="24.8.4.13"
ARG PACKAGES="clickhouse-client clickhouse-server clickhouse-common-static" ARG PACKAGES="clickhouse-client clickhouse-server clickhouse-common-static"
ARG DIRECT_DOWNLOAD_URLS="" ARG DIRECT_DOWNLOAD_URLS=""

View File

@ -28,7 +28,7 @@ RUN sed -i "s|http://archive.ubuntu.com|${apt_archive}|g" /etc/apt/sources.list
ARG REPO_CHANNEL="stable" ARG REPO_CHANNEL="stable"
ARG REPOSITORY="deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg] https://packages.clickhouse.com/deb ${REPO_CHANNEL} main" ARG REPOSITORY="deb [signed-by=/usr/share/keyrings/clickhouse-keyring.gpg] https://packages.clickhouse.com/deb ${REPO_CHANNEL} main"
ARG VERSION="24.8.3.59" ARG VERSION="24.8.4.13"
ARG PACKAGES="clickhouse-client clickhouse-server clickhouse-common-static" ARG PACKAGES="clickhouse-client clickhouse-server clickhouse-common-static"
#docker-official-library:off #docker-official-library:off

View File

@ -124,6 +124,8 @@ function setup_logs_replication
check_logs_credentials || return 0 check_logs_credentials || return 0
__set_connection_args __set_connection_args
echo "My hostname is ${HOSTNAME}"
echo 'Create all configured system logs' echo 'Create all configured system logs'
clickhouse-client --query "SYSTEM FLUSH LOGS" clickhouse-client --query "SYSTEM FLUSH LOGS"
@ -184,7 +186,17 @@ function setup_logs_replication
/^TTL /d /^TTL /d
') ')
echo -e "Creating remote destination table ${table}_${hash} with statement:\n${statement}" >&2 echo -e "Creating remote destination table ${table}_${hash} with statement:" >&2
echo "::group::${table}"
# there's the only way big "$statement" can be printed without causing EAGAIN error
# cat: write error: Resource temporarily unavailable
statement_print="${statement}"
if [ "${#statement_print}" -gt 4000 ]; then
statement_print="${statement::1999}\n…\n${statement:${#statement}-1999}"
fi
echo -e "$statement_print"
echo "::endgroup::"
echo "$statement" | clickhouse-client --database_replicated_initial_query_timeout_sec=10 \ echo "$statement" | clickhouse-client --database_replicated_initial_query_timeout_sec=10 \
--distributed_ddl_task_timeout=30 --distributed_ddl_output_mode=throw_only_active \ --distributed_ddl_task_timeout=30 --distributed_ddl_output_mode=throw_only_active \

View File

@ -3,6 +3,8 @@
FROM alpine:3.18 FROM alpine:3.18
RUN apk add --no-cache -U iproute2 \ RUN apk add --no-cache -U iproute2 \
&& for bin in iptables iptables-restore iptables-save; \ && for bin in \
iptables iptables-restore iptables-save \
ip6tables ip6tables-restore ip6tables-save; \
do ln -sf xtables-nft-multi "/sbin/$bin"; \ do ln -sf xtables-nft-multi "/sbin/$bin"; \
done done

View File

@ -0,0 +1,17 @@
---
sidebar_position: 1
sidebar_label: 2024
---
# 2024 Changelog
### ClickHouse release v24.3.11.7-lts (28795d0a47e) FIXME as compared to v24.3.10.33-lts (37b6502ebf0)
#### Bug Fix (user-visible misbehavior in an official stable release)
* Backported in [#67479](https://github.com/ClickHouse/ClickHouse/issues/67479): In rare cases ClickHouse could consider parts as broken because of some unexpected projections on disk. Now it's fixed. [#66898](https://github.com/ClickHouse/ClickHouse/pull/66898) ([alesapin](https://github.com/alesapin)).
* Backported in [#69243](https://github.com/ClickHouse/ClickHouse/issues/69243): `UNION` clause in subqueries wasn't handled correctly in queries with parallel replicas and lead to LOGICAL_ERROR `Duplicate announcement received for replica`. [#69146](https://github.com/ClickHouse/ClickHouse/pull/69146) ([Igor Nikonov](https://github.com/devcrafter)).
#### NOT FOR CHANGELOG / INSIGNIFICANT
* Backported in [#69221](https://github.com/ClickHouse/ClickHouse/issues/69221): Disable memory test with sanitizer. [#69193](https://github.com/ClickHouse/ClickHouse/pull/69193) ([alesapin](https://github.com/alesapin)).

View File

@ -0,0 +1,18 @@
---
sidebar_position: 1
sidebar_label: 2024
---
# 2024 Changelog
### ClickHouse release v24.5.8.10-stable (f11729638ea) FIXME as compared to v24.5.7.31-stable (6c185e9aec1)
#### Bug Fix (user-visible misbehavior in an official stable release)
* Backported in [#69295](https://github.com/ClickHouse/ClickHouse/issues/69295): TODO. [#68744](https://github.com/ClickHouse/ClickHouse/pull/68744) ([Nikolai Kochetov](https://github.com/KochetovNicolai)).
* Backported in [#69245](https://github.com/ClickHouse/ClickHouse/issues/69245): `UNION` clause in subqueries wasn't handled correctly in queries with parallel replicas and lead to LOGICAL_ERROR `Duplicate announcement received for replica`. [#69146](https://github.com/ClickHouse/ClickHouse/pull/69146) ([Igor Nikonov](https://github.com/devcrafter)).
* Fix crash when using `s3` table function with GLOB paths and filters. [#69176](https://github.com/ClickHouse/ClickHouse/pull/69176) ([János Benjamin Antal](https://github.com/antaljanosbenjamin)).
#### NOT FOR CHANGELOG / INSIGNIFICANT
* Backported in [#69223](https://github.com/ClickHouse/ClickHouse/issues/69223): Disable memory test with sanitizer. [#69193](https://github.com/ClickHouse/ClickHouse/pull/69193) ([alesapin](https://github.com/alesapin)).

View File

@ -0,0 +1,16 @@
---
sidebar_position: 1
sidebar_label: 2024
---
# 2024 Changelog
### ClickHouse release v24.6.6.6-stable (a4c4580e639) FIXME as compared to v24.6.5.30-stable (e6e196c92d6)
#### Bug Fix (user-visible misbehavior in an official stable release)
* Backported in [#69197](https://github.com/ClickHouse/ClickHouse/issues/69197): TODO. [#68744](https://github.com/ClickHouse/ClickHouse/pull/68744) ([Nikolai Kochetov](https://github.com/KochetovNicolai)).
#### NOT FOR CHANGELOG / INSIGNIFICANT
* Backported in [#69225](https://github.com/ClickHouse/ClickHouse/issues/69225): Disable memory test with sanitizer. [#69193](https://github.com/ClickHouse/ClickHouse/pull/69193) ([alesapin](https://github.com/alesapin)).

View File

@ -0,0 +1,17 @@
---
sidebar_position: 1
sidebar_label: 2024
---
# 2024 Changelog
### ClickHouse release v24.7.6.8-stable (7779883593a) FIXME as compared to v24.7.5.37-stable (f2533ca97be)
#### Bug Fix (user-visible misbehavior in an official stable release)
* Backported in [#69198](https://github.com/ClickHouse/ClickHouse/issues/69198): TODO. [#68744](https://github.com/ClickHouse/ClickHouse/pull/68744) ([Nikolai Kochetov](https://github.com/KochetovNicolai)).
* Backported in [#69249](https://github.com/ClickHouse/ClickHouse/issues/69249): `UNION` clause in subqueries wasn't handled correctly in queries with parallel replicas and lead to LOGICAL_ERROR `Duplicate announcement received for replica`. [#69146](https://github.com/ClickHouse/ClickHouse/pull/69146) ([Igor Nikonov](https://github.com/devcrafter)).
#### NOT FOR CHANGELOG / INSIGNIFICANT
* Backported in [#69227](https://github.com/ClickHouse/ClickHouse/issues/69227): Disable memory test with sanitizer. [#69193](https://github.com/ClickHouse/ClickHouse/pull/69193) ([alesapin](https://github.com/alesapin)).

View File

@ -0,0 +1,22 @@
---
sidebar_position: 1
sidebar_label: 2024
---
# 2024 Changelog
### ClickHouse release v24.8.4.13-lts (53195bc189b) FIXME as compared to v24.8.3.59-lts (e729b9fa40e)
#### Improvement
* Backported in [#68699](https://github.com/ClickHouse/ClickHouse/issues/68699): Delete old code of named collections from dictionaries and substitute it to the new, which allows to use DDL created named collections in dictionaries. Closes [#60936](https://github.com/ClickHouse/ClickHouse/issues/60936), closes [#36890](https://github.com/ClickHouse/ClickHouse/issues/36890). [#68412](https://github.com/ClickHouse/ClickHouse/pull/68412) ([Kseniia Sumarokova](https://github.com/kssenii)).
#### Bug Fix (user-visible misbehavior in an official stable release)
* Backported in [#69231](https://github.com/ClickHouse/ClickHouse/issues/69231): Fix parsing error when null should be inserted as default in some cases during JSON type parsing. [#68955](https://github.com/ClickHouse/ClickHouse/pull/68955) ([Kruglov Pavel](https://github.com/Avogar)).
* Backported in [#69251](https://github.com/ClickHouse/ClickHouse/issues/69251): `UNION` clause in subqueries wasn't handled correctly in queries with parallel replicas and lead to LOGICAL_ERROR `Duplicate announcement received for replica`. [#69146](https://github.com/ClickHouse/ClickHouse/pull/69146) ([Igor Nikonov](https://github.com/devcrafter)).
#### NOT FOR CHANGELOG / INSIGNIFICANT
* Backported in [#69189](https://github.com/ClickHouse/ClickHouse/issues/69189): Don't create Object type if use_json_alias_for_old_object_type=1 but allow_experimental_object_type=0. [#69150](https://github.com/ClickHouse/ClickHouse/pull/69150) ([Kruglov Pavel](https://github.com/Avogar)).
* Backported in [#69229](https://github.com/ClickHouse/ClickHouse/issues/69229): Disable memory test with sanitizer. [#69193](https://github.com/ClickHouse/ClickHouse/pull/69193) ([alesapin](https://github.com/alesapin)).
* Backported in [#69219](https://github.com/ClickHouse/ClickHouse/issues/69219): Disable perf-like test with sanitizers. [#69194](https://github.com/ClickHouse/ClickHouse/pull/69194) ([alesapin](https://github.com/alesapin)).

View File

@ -0,0 +1,72 @@
---
slug: /en/engines/table-engines/integrations/azure-queue
sidebar_position: 181
sidebar_label: AzureQueue
---
# AzureQueue Table Engine
This engine provides an integration with [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs) ecosystem, allowing streaming data import.
## Create Table {#creating-a-table}
``` sql
CREATE TABLE test (name String, value UInt32)
ENGINE = AzureQueue(...)
[SETTINGS]
[mode = '',]
[after_processing = 'keep',]
[keeper_path = '',]
...
```
**Engine parameters**
`AzureQueue` parameters are the same as `AzureBlobStorage` table engine supports. See parameters section [here](../../../engines/table-engines/integrations/azureBlobStorage.md).
**Example**
```sql
CREATE TABLE azure_queue_engine_table (name String, value UInt32)
ENGINE=AzureQueue('DefaultEndpointsProtocol=http;AccountName=devstoreaccount1;AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;BlobEndpoint=http://azurite1:10000/devstoreaccount1/data/')
SETTINGS
mode = 'unordered'
```
## Settings {#settings}
The set of supported settings is the same as for `S3Queue` table engine, but without `s3queue_` prefix. See [full list of settings settings](../../../engines/table-engines/integrations/s3queue.md#settings).
## Description {#description}
`SELECT` is not particularly useful for streaming import (except for debugging), because each file can be imported only once. It is more practical to create real-time threads using [materialized views](../../../sql-reference/statements/create/view.md). To do this:
1. Use the engine to create a table for consuming from specified path in S3 and consider it a data stream.
2. Create a table with the desired structure.
3. Create a materialized view that converts data from the engine and puts it into a previously created table.
When the `MATERIALIZED VIEW` joins the engine, it starts collecting data in the background.
Example:
``` sql
CREATE TABLE azure_queue_engine_table (name String, value UInt32)
ENGINE=AzureQueue('<endpoint>', 'CSV', 'gzip')
SETTINGS
mode = 'unordered';
CREATE TABLE stats (name String, value UInt32)
ENGINE = MergeTree() ORDER BY name;
CREATE MATERIALIZED VIEW consumer TO stats
AS SELECT name, value FROM azure_queue_engine_table;
SELECT * FROM stats ORDER BY name;
```
## Virtual columns {#virtual-columns}
- `_path` — Path to the file.
- `_file` — Name of the file.
For more information about virtual columns see [here](../../../engines/table-engines/index.md#table_engines-virtual_columns).

View File

@ -35,7 +35,7 @@ CREATE TABLE s3_engine_table (name String, value UInt32)
[SETTINGS ...] [SETTINGS ...]
``` ```
### Engine parameters ### Engine parameters {#parameters}
- `path` — Bucket url with path to file. Supports following wildcards in readonly mode: `*`, `**`, `?`, `{abc,def}` and `{N..M}` where `N`, `M` — numbers, `'abc'`, `'def'` — strings. For more information see [below](#wildcards-in-path). - `path` — Bucket url with path to file. Supports following wildcards in readonly mode: `*`, `**`, `?`, `{abc,def}` and `{N..M}` where `N`, `M` — numbers, `'abc'`, `'def'` — strings. For more information see [below](#wildcards-in-path).
- `NOSIGN` - If this keyword is provided in place of credentials, all the requests will not be signed. - `NOSIGN` - If this keyword is provided in place of credentials, all the requests will not be signed.

View File

@ -5,6 +5,7 @@ sidebar_label: S3Queue
--- ---
# S3Queue Table Engine # S3Queue Table Engine
This engine provides integration with [Amazon S3](https://aws.amazon.com/s3/) ecosystem and allows streaming import. This engine is similar to the [Kafka](../../../engines/table-engines/integrations/kafka.md), [RabbitMQ](../../../engines/table-engines/integrations/rabbitmq.md) engines, but provides S3-specific features. This engine provides integration with [Amazon S3](https://aws.amazon.com/s3/) ecosystem and allows streaming import. This engine is similar to the [Kafka](../../../engines/table-engines/integrations/kafka.md), [RabbitMQ](../../../engines/table-engines/integrations/rabbitmq.md) engines, but provides S3-specific features.
## Create Table {#creating-a-table} ## Create Table {#creating-a-table}
@ -16,27 +17,25 @@ CREATE TABLE s3_queue_engine_table (name String, value UInt32)
[mode = '',] [mode = '',]
[after_processing = 'keep',] [after_processing = 'keep',]
[keeper_path = '',] [keeper_path = '',]
[s3queue_loading_retries = 0,] [loading_retries = 0,]
[s3queue_processing_threads_num = 1,] [processing_threads_num = 1,]
[s3queue_enable_logging_to_s3queue_log = 0,] [enable_logging_to_s3queue_log = 0,]
[s3queue_polling_min_timeout_ms = 1000,] [polling_min_timeout_ms = 1000,]
[s3queue_polling_max_timeout_ms = 10000,] [polling_max_timeout_ms = 10000,]
[s3queue_polling_backoff_ms = 0,] [polling_backoff_ms = 0,]
[s3queue_tracked_file_ttl_sec = 0,] [tracked_file_ttl_sec = 0,]
[s3queue_tracked_files_limit = 1000,] [tracked_files_limit = 1000,]
[s3queue_cleanup_interval_min_ms = 10000,] [cleanup_interval_min_ms = 10000,]
[s3queue_cleanup_interval_max_ms = 30000,] [cleanup_interval_max_ms = 30000,]
``` ```
Starting with `24.7` settings without `s3queue_` prefix are also supported. :::warning
Before `24.7`, it is required to use `s3queue_` prefix for all settings apart from `mode`, `after_processing` and `keeper_path`.
:::
**Engine parameters** **Engine parameters**
- `path` — Bucket url with path to file. Supports following wildcards in readonly mode: `*`, `**`, `?`, `{abc,def}` and `{N..M}` where `N`, `M` — numbers, `'abc'`, `'def'` — strings. For more information see [below](#wildcards-in-path). `S3Queue` parameters are the same as `S3` table engine supports. See parameters section [here](../../../engines/table-engines/integrations/s3.md#parameters).
- `NOSIGN` - If this keyword is provided in place of credentials, all the requests will not be signed.
- `format` — The [format](../../../interfaces/formats.md#formats) of the file.
- `aws_access_key_id`, `aws_secret_access_key` - Long-term credentials for the [AWS](https://aws.amazon.com/) account user. You can use these to authenticate your requests. Parameter is optional. If credentials are not specified, they are used from the configuration file. For more information see [Using S3 for Data Storage](../mergetree-family/mergetree.md#table_engine-mergetree-s3).
- `compression` — Compression type. Supported values: `none`, `gzip/gz`, `brotli/br`, `xz/LZMA`, `zstd/zst`. Parameter is optional. By default, it will autodetect compression by file extension.
**Example** **Example**

View File

@ -111,15 +111,16 @@ ANN indexes are built during column insertion and merge. As a result, `INSERT` a
tables. ANNIndexes are ideally used only with immutable or rarely changed data, respectively when are far more read requests than write tables. ANNIndexes are ideally used only with immutable or rarely changed data, respectively when are far more read requests than write
requests. requests.
ANN indexes support these queries: ANN indexes support this type of query:
``` sql ``` sql
SELECT * WITH [...] AS reference_vector
FROM table SELECT *
[WHERE ...] FROM table
ORDER BY Distance(vectors, Point) WHERE ... -- WHERE clause is optional
LIMIT N ORDER BY Distance(vectors, reference_vector)
``` LIMIT N
```
:::tip :::tip
To avoid writing out large vectors, you can use [query To avoid writing out large vectors, you can use [query

View File

@ -989,19 +989,52 @@ ALTER TABLE tab DROP STATISTICS a;
These lightweight statistics aggregate information about distribution of values in columns. Statistics are stored in every part and updated when every insert comes. These lightweight statistics aggregate information about distribution of values in columns. Statistics are stored in every part and updated when every insert comes.
They can be used for prewhere optimization only if we enable `set allow_statistics_optimize = 1`. They can be used for prewhere optimization only if we enable `set allow_statistics_optimize = 1`.
#### Available Types of Column Statistics {#available-types-of-column-statistics} ### Available Types of Column Statistics {#available-types-of-column-statistics}
- `MinMax`
The minimum and maximum column value which allows to estimate the selectivity of range filters on numeric columns.
Syntax: `minmax`
- `TDigest` - `TDigest`
[TDigest](https://github.com/tdunning/t-digest) sketches which allow to compute approximate percentiles (e.g. the 90th percentile) for numeric columns. [TDigest](https://github.com/tdunning/t-digest) sketches which allow to compute approximate percentiles (e.g. the 90th percentile) for numeric columns.
Syntax: `tdigest`
- `Uniq` - `Uniq`
[HyperLogLog](https://en.wikipedia.org/wiki/HyperLogLog) sketches which provide an estimation how many distinct values a column contains. [HyperLogLog](https://en.wikipedia.org/wiki/HyperLogLog) sketches which provide an estimation how many distinct values a column contains.
- `count_min` Syntax: `uniq`
- `CountMin`
[CountMin](https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch) sketches which provide an approximate count of the frequency of each value in a column.
Syntax `countmin`
### Supported Data Types {#supported-data-types}
| | (U)Int*, Float*, Decimal(*), Date*, Boolean, Enum* | String or FixedString |
|-----------|----------------------------------------------------|-----------------------|
| CountMin | ✔ | ✔ |
| MinMax | ✔ | ✗ |
| TDigest | ✔ | ✗ |
| Uniq | ✔ | ✔ |
### Supported Operations {#supported-operations}
| | Equality filters (==) | Range filters (>, >=, <, <=) |
|-----------|-----------------------|------------------------------|
| CountMin | ✔ | ✗ |
| MinMax | ✗ | ✔ |
| TDigest | ✗ | ✔ |
| Uniq | ✔ | ✗ |
[Count-min](https://en.wikipedia.org/wiki/Count%E2%80%93min_sketch) sketches which provide an approximate count of the frequency of each value in a column.
## Column-level Settings {#column-level-settings} ## Column-level Settings {#column-level-settings}

View File

@ -39,6 +39,7 @@ The supported formats are:
| [JSONCompact](#jsoncompact) | ✔ | ✔ | | [JSONCompact](#jsoncompact) | ✔ | ✔ |
| [JSONCompactStrings](#jsoncompactstrings) | ✗ | ✔ | | [JSONCompactStrings](#jsoncompactstrings) | ✗ | ✔ |
| [JSONCompactColumns](#jsoncompactcolumns) | ✔ | ✔ | | [JSONCompactColumns](#jsoncompactcolumns) | ✔ | ✔ |
| [JSONCompactWithProgress](#jsoncompactwithprogress) | ✗ | ✔ |
| [JSONEachRow](#jsoneachrow) | ✔ | ✔ | | [JSONEachRow](#jsoneachrow) | ✔ | ✔ |
| [PrettyJSONEachRow](#prettyjsoneachrow) | ✗ | ✔ | | [PrettyJSONEachRow](#prettyjsoneachrow) | ✗ | ✔ |
| [JSONEachRowWithProgress](#jsoneachrowwithprogress) | ✗ | ✔ | | [JSONEachRowWithProgress](#jsoneachrowwithprogress) | ✗ | ✔ |
@ -988,6 +989,59 @@ Example:
Columns that are not present in the block will be filled with default values (you can use [input_format_defaults_for_omitted_fields](/docs/en/operations/settings/settings-formats.md/#input_format_defaults_for_omitted_fields) setting here) Columns that are not present in the block will be filled with default values (you can use [input_format_defaults_for_omitted_fields](/docs/en/operations/settings/settings-formats.md/#input_format_defaults_for_omitted_fields) setting here)
## JSONCompactWithProgress (#jsoncompactwithprogress)
In this format, ClickHouse outputs each row as a separated, newline-delimited JSON Object.
Each row is either a metadata object, data object, progress information or statistics object:
1. **Metadata Object (`meta`)**
- Describes the structure of the data rows.
- Fields: `name` (column name), `type` (data type, e.g., `UInt32`, `String`, etc.).
- Example: `{"meta": [{"name":"id", "type":"UInt32"}, {"name":"name", "type":"String"}]}`
- Appears before any data objects.
2. **Data Object (`data`)**
- Represents a row of query results.
- Fields: An array with values corresponding to the columns defined in the metadata.
- Example: `{"data":["1", "John Doe"]}`
- Appears after the metadata object, one per row.
3. **Progress Information Object (`progress`)**
- Provides real-time progress feedback during query execution.
- Fields: `read_rows`, `read_bytes`, `written_rows`, `written_bytes`, `total_rows_to_read`, `result_rows`, `result_bytes`, `elapsed_ns`.
- Example: `{"progress":{"read_rows":"8","read_bytes":"168"}}`
- May appear intermittently.
4. **Statistics Object (`statistics`)**
- Summarizes query execution statistics.
- Fields: `rows`, `rows_before_limit_at_least`, `elapsed`, `rows_read`, `bytes_read`.
- Example: `{"statistics": {"rows":2, "elapsed":0.001995, "rows_read":8}}`
- Appears at the end.
5. **Exception Object (`exception`)**
- Represents an error that occurred during query execution.
- Fields: A single text field containing the error message.
- Example: `{"exception": "Code: 395. DB::Exception: Value passed to 'throwIf' function is non-zero..."}`
- Appears when an error is encountered.
6. **Totals Object (`totals`)**
- Provides the totals for each numeric column in the result set.
- Fields: An array with total values corresponding to the columns defined in the metadata.
- Example: `{"totals": ["", "3"]}`
- Appears at the end of the data rows, if applicable.
Example:
```json
{"meta": [{"name":"id", "type":"UInt32"}, {"name":"name", "type":"String"}]}
{"progress":{"read_rows":"8","read_bytes":"168","written_rows":"0","written_bytes":"0","total_rows_to_read":"2","result_rows":"0","result_bytes":"0","elapsed_ns":"0"}}
{"data":["1", "John Doe"]}
{"data":["2", "Joe Doe"]}
{"statistics": {"rows":2, "rows_before_limit_at_least":8, "elapsed":0.001995, "rows_read":8, "bytes_read":168}}
```
## JSONEachRow {#jsoneachrow} ## JSONEachRow {#jsoneachrow}
In this format, ClickHouse outputs each row as a separated, newline-delimited JSON Object. In this format, ClickHouse outputs each row as a separated, newline-delimited JSON Object.

View File

@ -233,6 +233,16 @@ Features:
- Useful tools: Zookeeper data exploration, query EXPLAIN, kill queries, etc. - Useful tools: Zookeeper data exploration, query EXPLAIN, kill queries, etc.
- Visualization metric charts: queries and resource usage, number of merges/mutation, merge performance, query performance, etc. - Visualization metric charts: queries and resource usage, number of merges/mutation, merge performance, query performance, etc.
### CKibana {#ckibana}
[CKibana](https://github.com/TongchengOpenSource/ckibana) is a lightweight service that allows you to effortlessly search, explore, and visualize ClickHouse data using the native Kibana UI.
Features:
- Translates chart requests from the native Kibana UI into ClickHouse query syntax.
- Supports advanced features such as sampling and caching to enhance query performance.
- Minimizes the learning cost for users after migrating from ElasticSearch to ClickHouse.
## Commercial {#commercial} ## Commercial {#commercial}
### DataGrip {#datagrip} ### DataGrip {#datagrip}

View File

@ -1463,26 +1463,29 @@ Examples:
## logger {#logger} ## logger {#logger}
Logging settings. The location and format of log messages.
Keys: Keys:
- `level` Logging level. Acceptable values: `trace`, `debug`, `information`, `warning`, `error`. - `level` Log level. Acceptable values: `none` (turn logging off), `fatal`, `critical`, `error`, `warning`, `notice`, `information`,
- `log` The log file. Contains all the entries according to `level`. `debug`, `trace`, `test`
- `errorlog` Error log file. - `log` The path to the log file.
- `size` Size of the file. Applies to `log` and `errorlog`. Once the file reaches `size`, ClickHouse archives and renames it, and creates a new log file in its place. - `errorlog` The path to the error log file.
- `count` The number of archived log files that ClickHouse stores. - `size` Rotation policy: Maximum size of the log files in bytes. Once the log file size exceeds this threshold, it is renamed and archived, and a new log file is created.
- `console` Send `log` and `errorlog` to the console instead of file. To enable, set to `1` or `true`. - `count` Rotation policy: How many historical log files Clickhouse are kept at most.
- `console_log_level` Logging level for console. Default to `level`. - `stream_compress` Compress log messages using LZ4. Set to `1` or `true` to enable.
- `use_syslog` - Log to syslog as well. - `console` Do not write log messages to log files, instead print them in the console. Set to `1` or `true` to enable. Default is
- `syslog_level` - Logging level for logging to syslog. `1` if Clickhouse does not run in daemon mode, `0` otherwise.
- `stream_compress` Compress `log` and `errorlog` with `lz4` stream compression. To enable, set to `1` or `true`. - `console_log_level` Log level for console output. Defaults to `level`.
- `formatting` Specify log format to be printed in console log (currently only `json` supported). - `formatting` Log format for console output. Currently, only `json` is supported).
- `use_syslog` - Also forward log output to syslog.
- `syslog_level` - Log level for logging to syslog.
Both log and error log file names (only file names, not directories) support date and time format specifiers. **Log format specifiers**
**Format specifiers** File names in `log` and `errorLog` paths support below format specifiers for the resulting file name (the directory part does not support them).
Using the following format specifiers, you can define a pattern for the resulting file name. “Example” column shows possible results for `2023-07-06 18:32:07`.
Column “Example” shows the output at `2023-07-06 18:32:07`.
| Specifier | Description | Example | | Specifier | Description | Example |
|-------------|---------------------------------------------------------------------------------------------------------------------|--------------------------| |-------------|---------------------------------------------------------------------------------------------------------------------|--------------------------|
@ -1537,18 +1540,37 @@ Using the following format specifiers, you can define a pattern for the resultin
</logger> </logger>
``` ```
Writing to the console can be configured. Config example: To print log messages only in the console:
``` xml ``` xml
<logger> <logger>
<level>information</level> <level>information</level>
<console>1</console> <console>true</console>
</logger>
```
**Per-level Overrides**
The log level of individual log names can be overridden. For example, to mute all messages of loggers "Backup" and "RBAC".
```xml
<logger>
<levels>
<logger>
<name>Backup</name>
<level>none</level>
</logger>
<logger>
<name>RBAC</name>
<level>none</level>
</logger>
</levels>
</logger> </logger>
``` ```
### syslog ### syslog
Writing to the syslog is also supported. Config example: To write log messages additionally to syslog:
``` xml ``` xml
<logger> <logger>
@ -1562,14 +1584,12 @@ Writing to the syslog is also supported. Config example:
</logger> </logger>
``` ```
Keys for syslog: Keys for `<syslog>`:
- use_syslog — Required setting if you want to write to the syslog. - `address` — The address of syslog in format `host\[:port\]`. If omitted, the local daemon is used.
- address — The host\[:port\] of syslogd. If omitted, the local daemon is used. - `hostname` — The name of the host from which logs are send. Optional.
- hostname — Optional. The name of the host that logs are sent from. - `facility` — The syslog [facility keyword](https://en.wikipedia.org/wiki/Syslog#Facility). Must be specified uppercase with a “LOG_” prefix, e.g. `LOG_USER`, `LOG_DAEMON`, `LOG_LOCAL3`, etc. Default value: `LOG_USER` if `address` is specified, `LOG_DAEMON` otherwise.
- facility — [The syslog facility keyword](https://en.wikipedia.org/wiki/Syslog#Facility) in uppercase letters with the “LOG_” prefix: (`LOG_USER`, `LOG_DAEMON`, `LOG_LOCAL3`, and so on). - `format` Log message format. Possible values: `bsd` and `syslog.`
Default value: `LOG_USER` if `address` is specified, `LOG_DAEMON` otherwise.
- format Message format. Possible values: `bsd` and `syslog.`
### Log formats ### Log formats
@ -1588,6 +1608,7 @@ You can specify the log format that will be outputted in the console log. Curren
"source_line": "192" "source_line": "192"
} }
``` ```
To enable JSON logging support, use the following snippet: To enable JSON logging support, use the following snippet:
```xml ```xml

View File

@ -3226,7 +3226,7 @@ Default value: `0`.
## lightweight_deletes_sync {#lightweight_deletes_sync} ## lightweight_deletes_sync {#lightweight_deletes_sync}
The same as 'mutation_sync', but controls only execution of lightweight deletes. The same as [`mutations_sync`](#mutations_sync), but controls only execution of lightweight deletes.
Possible values: Possible values:

View File

@ -47,6 +47,8 @@ keeper foo bar
- `ls '[path]'` -- Lists the nodes for the given path (default: cwd) - `ls '[path]'` -- Lists the nodes for the given path (default: cwd)
- `cd '[path]'` -- Changes the working path (default `.`) - `cd '[path]'` -- Changes the working path (default `.`)
- `cp '<src>' '<dest>'` -- Copies 'src' node to 'dest' path
- `mv '<src>' '<dest>'` -- Moves 'src' node to the 'dest' path
- `exists '<path>'` -- Returns `1` if node exists, `0` otherwise - `exists '<path>'` -- Returns `1` if node exists, `0` otherwise
- `set '<path>' <value> [version]` -- Updates the node's value. Only updates if version matches (default: -1) - `set '<path>' <value> [version]` -- Updates the node's value. Only updates if version matches (default: -1)
- `create '<path>' <value> [mode]` -- Creates new node with the set value - `create '<path>' <value> [mode]` -- Creates new node with the set value

View File

@ -2035,6 +2035,7 @@ Query:
SELECT arrayZip(['a', 'b', 'c'], [5, 2, 1]); SELECT arrayZip(['a', 'b', 'c'], [5, 2, 1]);
``` ```
Result: Result:
``` text ``` text
@ -2043,6 +2044,43 @@ Result:
└──────────────────────────────────────┘ └──────────────────────────────────────┘
``` ```
## arrayZipUnaligned
Combines multiple arrays into a single array, allowing for unaligned arrays. The resulting array contains the corresponding elements of the source arrays grouped into tuples in the listed order of arguments.
**Syntax**
``` sql
arrayZipUnaligned(arr1, arr2, ..., arrN)
```
**Arguments**
- `arrN` — [Array](../data-types/array.md).
The function can take any number of arrays of different types.
**Returned value**
- Array with elements from the source arrays grouped into [tuples](../data-types/tuple.md). Data types in the tuple are the same as types of the input arrays and in the same order as arrays are passed. [Array](../data-types/array.md). If the arrays have different sizes, the shorter arrays will be padded with `null` values.
**Example**
Query:
``` sql
SELECT arrayZipUnaligned(['a'], [1, 2, 3]);
```
Result:
``` text
┌─arrayZipUnaligned(['a'], [1, 2, 3])─┐
│ [('a',1),(NULL,2),(NULL,3)] │
└─────────────────────────────────────┘
```
## arrayAUC ## arrayAUC
Calculate AUC (Area Under the Curve, which is a concept in machine learning, see more details: <https://en.wikipedia.org/wiki/Receiver_operating_characteristic#Area_under_the_curve>). Calculate AUC (Area Under the Curve, which is a concept in machine learning, see more details: <https://en.wikipedia.org/wiki/Receiver_operating_characteristic#Area_under_the_curve>).

View File

@ -1617,45 +1617,348 @@ The calculation is performed relative to specific points in time:
If unit `WEEK` was specified, `toStartOfInterval` assumes that weeks start on Monday. Note that this behavior is different from that of function `toStartOfWeek` in which weeks start by default on Sunday. If unit `WEEK` was specified, `toStartOfInterval` assumes that weeks start on Monday. Note that this behavior is different from that of function `toStartOfWeek` in which weeks start by default on Sunday.
**See Also** **Syntax**
```sql
toStartOfInterval(value, INTERVAL x unit[, time_zone])
toStartOfInterval(value, INTERVAL x unit[, origin[, time_zone]])
```
The second overload emulates TimescaleDB's `time_bucket()` function, respectively PostgreSQL's `date_bin()` function, e.g.
``` SQL
SELECT toStartOfInterval(toDateTime('2023-01-01 14:45:00'), INTERVAL 1 MINUTE, toDateTime('2023-01-01 14:35:30'));
```
**See Also**
- [date_trunc](#date_trunc) - [date_trunc](#date_trunc)
## toTime ## toTime
Converts a date with time to a certain fixed date, while preserving the time. Converts a date with time to a certain fixed date, while preserving the time.
**Syntax**
```sql
toTime(date[,timezone])
```
**Arguments**
- `date` — Date to convert to a time. [Date](../data-types/date.md)/[DateTime](../data-types/datetime.md)/[DateTime64](../data-types/datetime64.md).
- `timezone` (optional) — Timezone for the returned value. [String](../data-types/string.md).
**Returned value**
- DateTime with date equated to `1970-01-02` while preserving the time. [DateTime](../data-types/datetime.md).
:::note
If the `date` input argument contained sub-second components,
they will be dropped in the returned `DateTime` value with second-accuracy.
:::
**Example**
Query:
```sql
SELECT toTime(toDateTime64('1970-12-10 01:20:30.3000',3)) AS result, toTypeName(result);
```
Result:
```response
┌──────────────result─┬─toTypeName(result)─┐
│ 1970-01-02 01:20:30 │ DateTime │
└─────────────────────┴────────────────────┘
```
## toRelativeYearNum ## toRelativeYearNum
Converts a date, or date with time, to the number of the year, starting from a certain fixed point in the past. Converts a date, or date with time, to the number of years elapsed since a certain fixed point in the past.
**Syntax**
```sql
toRelativeYearNum(date)
```
**Arguments**
- `date` — Date or date with time. [Date](../data-types/date.md)/[DateTime](../data-types/datetime.md)/[DateTime64](../data-types/datetime64.md).
**Returned value**
- The number of years from a fixed reference point in the past. [UInt16](../data-types/int-uint.md).
**Example**
Query:
```sql
SELECT
toRelativeYearNum(toDate('2002-12-08')) AS y1,
toRelativeYearNum(toDate('2010-10-26')) AS y2
```
Result:
```response
┌───y1─┬───y2─┐
│ 2002 │ 2010 │
└──────┴──────┘
```
## toRelativeQuarterNum ## toRelativeQuarterNum
Converts a date, or date with time, to the number of the quarter, starting from a certain fixed point in the past. Converts a date, or date with time, to the number of quarters elapsed since a certain fixed point in the past.
**Syntax**
```sql
toRelativeQuarterNum(date)
```
**Arguments**
- `date` — Date or date with time. [Date](../data-types/date.md)/[DateTime](../data-types/datetime.md)/[DateTime64](../data-types/datetime64.md).
**Returned value**
- The number of quarters from a fixed reference point in the past. [UInt32](../data-types/int-uint.md).
**Example**
Query:
```sql
SELECT
toRelativeQuarterNum(toDate('1993-11-25')) AS q1,
toRelativeQuarterNum(toDate('2005-01-05')) AS q2
```
Result:
```response
┌───q1─┬───q2─┐
│ 7975 │ 8020 │
└──────┴──────┘
```
## toRelativeMonthNum ## toRelativeMonthNum
Converts a date, or date with time, to the number of the month, starting from a certain fixed point in the past. Converts a date, or date with time, to the number of months elapsed since a certain fixed point in the past.
**Syntax**
```sql
toRelativeMonthNum(date)
```
**Arguments**
- `date` — Date or date with time. [Date](../data-types/date.md)/[DateTime](../data-types/datetime.md)/[DateTime64](../data-types/datetime64.md).
**Returned value**
- The number of months from a fixed reference point in the past. [UInt32](../data-types/int-uint.md).
**Example**
Query:
```sql
SELECT
toRelativeMonthNum(toDate('2001-04-25')) AS m1,
toRelativeMonthNum(toDate('2009-07-08')) AS m2
```
Result:
```response
┌────m1─┬────m2─┐
│ 24016 │ 24115 │
└───────┴───────┘
```
## toRelativeWeekNum ## toRelativeWeekNum
Converts a date, or date with time, to the number of the week, starting from a certain fixed point in the past. Converts a date, or date with time, to the number of weeks elapsed since a certain fixed point in the past.
**Syntax**
```sql
toRelativeWeekNum(date)
```
**Arguments**
- `date` — Date or date with time. [Date](../data-types/date.md)/[DateTime](../data-types/datetime.md)/[DateTime64](../data-types/datetime64.md).
**Returned value**
- The number of weeks from a fixed reference point in the past. [UInt32](../data-types/int-uint.md).
**Example**
Query:
```sql
SELECT
toRelativeWeekNum(toDate('2000-02-29')) AS w1,
toRelativeWeekNum(toDate('2001-01-12')) AS w2
```
Result:
```response
┌───w1─┬───w2─┐
│ 1574 │ 1619 │
└──────┴──────┘
```
## toRelativeDayNum ## toRelativeDayNum
Converts a date, or date with time, to the number of the day, starting from a certain fixed point in the past. Converts a date, or date with time, to the number of days elapsed since a certain fixed point in the past.
**Syntax**
```sql
toRelativeDayNum(date)
```
**Arguments**
- `date` — Date or date with time. [Date](../data-types/date.md)/[DateTime](../data-types/datetime.md)/[DateTime64](../data-types/datetime64.md).
**Returned value**
- The number of days from a fixed reference point in the past. [UInt32](../data-types/int-uint.md).
**Example**
Query:
```sql
SELECT
toRelativeDayNum(toDate('1993-10-05')) AS d1,
toRelativeDayNum(toDate('2000-09-20')) AS d2
```
Result:
```response
┌───d1─┬────d2─┐
│ 8678 │ 11220 │
└──────┴───────┘
```
## toRelativeHourNum ## toRelativeHourNum
Converts a date, or date with time, to the number of the hour, starting from a certain fixed point in the past. Converts a date, or date with time, to the number of hours elapsed since a certain fixed point in the past.
**Syntax**
```sql
toRelativeHourNum(date)
```
**Arguments**
- `date` — Date or date with time. [Date](../data-types/date.md)/[DateTime](../data-types/datetime.md)/[DateTime64](../data-types/datetime64.md).
**Returned value**
- The number of hours from a fixed reference point in the past. [UInt32](../data-types/int-uint.md).
**Example**
Query:
```sql
SELECT
toRelativeHourNum(toDateTime('1993-10-05 05:20:36')) AS h1,
toRelativeHourNum(toDateTime('2000-09-20 14:11:29')) AS h2
```
Result:
```response
┌─────h1─┬─────h2─┐
│ 208276 │ 269292 │
└────────┴────────┘
```
## toRelativeMinuteNum ## toRelativeMinuteNum
Converts a date, or date with time, to the number of the minute, starting from a certain fixed point in the past. Converts a date, or date with time, to the number of minutes elapsed since a certain fixed point in the past.
**Syntax**
```sql
toRelativeMinuteNum(date)
```
**Arguments**
- `date` — Date or date with time. [Date](../data-types/date.md)/[DateTime](../data-types/datetime.md)/[DateTime64](../data-types/datetime64.md).
**Returned value**
- The number of minutes from a fixed reference point in the past. [UInt32](../data-types/int-uint.md).
**Example**
Query:
```sql
SELECT
toRelativeMinuteNum(toDateTime('1993-10-05 05:20:36')) AS m1,
toRelativeMinuteNum(toDateTime('2000-09-20 14:11:29')) AS m2
```
Result:
```response
┌───────m1─┬───────m2─┐
│ 12496580 │ 16157531 │
└──────────┴──────────┘
```
## toRelativeSecondNum ## toRelativeSecondNum
Converts a date, or date with time, to the number of the second, starting from a certain fixed point in the past. Converts a date, or date with time, to the number of the seconds elapsed since a certain fixed point in the past.
**Syntax**
```sql
toRelativeSecondNum(date)
```
**Arguments**
- `date` — Date or date with time. [Date](../data-types/date.md)/[DateTime](../data-types/datetime.md)/[DateTime64](../data-types/datetime64.md).
**Returned value**
- The number of seconds from a fixed reference point in the past. [UInt32](../data-types/int-uint.md).
**Example**
Query:
```sql
SELECT
toRelativeSecondNum(toDateTime('1993-10-05 05:20:36')) AS s1,
toRelativeSecondNum(toDateTime('2000-09-20 14:11:29')) AS s2
```
Result:
```response
┌────────s1─┬────────s2─┐
│ 749794836 │ 969451889 │
└───────────┴───────────┘
```
## toISOYear ## toISOYear
@ -3884,19 +4187,29 @@ Result:
└───────────────────────────────────────────────────────────────────────┘ └───────────────────────────────────────────────────────────────────────┘
``` ```
## timeSlots(StartTime, Duration,\[, Size\]) ## timeSlots
For a time interval starting at StartTime and continuing for Duration seconds, it returns an array of moments in time, consisting of points from this interval rounded down to the Size in seconds. Size is an optional parameter set to 1800 (30 minutes) by default. For a time interval starting at StartTime and continuing for Duration seconds, it returns an array of moments in time, consisting of points from this interval rounded down to the Size in seconds. Size is an optional parameter set to 1800 (30 minutes) by default.
This is necessary, for example, when searching for pageviews in the corresponding session. This is necessary, for example, when searching for pageviews in the corresponding session.
Accepts DateTime and DateTime64 as StartTime argument. For DateTime, Duration and Size arguments must be `UInt32`. For DateTime64 they must be `Decimal64`. Accepts DateTime and DateTime64 as StartTime argument. For DateTime, Duration and Size arguments must be `UInt32`. For DateTime64 they must be `Decimal64`.
Returns an array of DateTime/DateTime64 (return type matches the type of StartTime). For DateTime64, the return value's scale can differ from the scale of StartTime --- the highest scale among all given arguments is taken. Returns an array of DateTime/DateTime64 (return type matches the type of StartTime). For DateTime64, the return value's scale can differ from the scale of StartTime --- the highest scale among all given arguments is taken.
Example: **Syntax**
```sql
timeSlots(StartTime, Duration,\[, Size\])
```
**Example**
```sql ```sql
SELECT timeSlots(toDateTime('2012-01-01 12:20:00'), toUInt32(600)); SELECT timeSlots(toDateTime('2012-01-01 12:20:00'), toUInt32(600));
SELECT timeSlots(toDateTime('1980-12-12 21:01:02', 'UTC'), toUInt32(600), 299); SELECT timeSlots(toDateTime('1980-12-12 21:01:02', 'UTC'), toUInt32(600), 299);
SELECT timeSlots(toDateTime64('1980-12-12 21:01:02.1234', 4, 'UTC'), toDecimal64(600.1, 1), toDecimal64(299, 0)); SELECT timeSlots(toDateTime64('1980-12-12 21:01:02.1234', 4, 'UTC'), toDecimal64(600.1, 1), toDecimal64(299, 0));
``` ```
Result:
``` text ``` text
┌─timeSlots(toDateTime('2012-01-01 12:20:00'), toUInt32(600))─┐ ┌─timeSlots(toDateTime('2012-01-01 12:20:00'), toUInt32(600))─┐
│ ['2012-01-01 12:00:00','2012-01-01 12:30:00'] │ │ ['2012-01-01 12:00:00','2012-01-01 12:30:00'] │

View File

@ -20,10 +20,10 @@ overlay(s, replace, offset[, length])
**Parameters** **Parameters**
- `input`: A string type [String](../data-types/string.md). - `s`: A string type [String](../data-types/string.md).
- `replace`: A string type [String](../data-types/string.md). - `replace`: A string type [String](../data-types/string.md).
- `offset`: An integer type [Int](../data-types/int-uint.md). If `offset` is negative, it is counted from the end of the `input` string. - `offset`: An integer type [Int](../data-types/int-uint.md) (1-based). If `offset` is negative, it is counted from the end of the string `s`.
- `length`: Optional. An integer type [Int](../data-types/int-uint.md). `length` specifies the length of the snippet within input to be replaced. If `length` is not specified, the number of bytes removed from `input` equals the length of `replace`; otherwise `length` bytes are removed. - `length`: Optional. An integer type [Int](../data-types/int-uint.md). `length` specifies the length of the snippet within the input string `s` to be replaced. If `length` is not specified, the number of bytes removed from `s` equals the length of `replace`; otherwise `length` bytes are removed.
**Returned value** **Returned value**
@ -32,22 +32,35 @@ overlay(s, replace, offset[, length])
**Example** **Example**
```sql ```sql
SELECT overlay('ClickHouse SQL', 'CORE', 12) AS res; SELECT overlay('My father is from Mexico.', 'mother', 4) AS res;
``` ```
Result: Result:
```text ```text
┌─res─────────────┐ ┌─res──────────────────────┐
│ ClickHouse CORE │ │ My mother is from Mexico.│
└─────────────────┘ └──────────────────────────┘
```
```sql
SELECT overlay('My father is from Mexico.', 'dad', 4, 6) AS res;
```
Result:
```text
┌─res───────────────────┐
│ My dad is from Mexico.│
└───────────────────────┘
``` ```
## overlayUTF8 ## overlayUTF8
Replace part of the string `input` with another string `replace`, starting at the 1-based index `offset`. Replace part of the string `input` with another string `replace`, starting at the 1-based index `offset`.
Assumes that the string contains valid UTF-8 encoded text. If this assumption is violated, no exception is thrown and the result is undefined. Assumes that the string contains valid UTF-8 encoded text.
If this assumption is violated, no exception is thrown and the result is undefined.
**Syntax** **Syntax**
@ -59,8 +72,8 @@ overlayUTF8(s, replace, offset[, length])
- `s`: A string type [String](../data-types/string.md). - `s`: A string type [String](../data-types/string.md).
- `replace`: A string type [String](../data-types/string.md). - `replace`: A string type [String](../data-types/string.md).
- `offset`: An integer type [Int](../data-types/int-uint.md). If `offset` is negative, it is counted from the end of the `input` string. - `offset`: An integer type [Int](../data-types/int-uint.md) (1-based). If `offset` is negative, it is counted from the end of the input string `s`.
- `length`: Optional. An integer type [Int](../data-types/int-uint.md). `length` specifies the length of the snippet within input to be replaced. If `length` is not specified, the number of characters removed from `input` equals the length of `replace`; otherwise `length` characters are removed. - `length`: Optional. An integer type [Int](../data-types/int-uint.md). `length` specifies the length of the snippet within the input string `s` to be replaced. If `length` is not specified, the number of characters removed from `s` equals the length of `replace`; otherwise `length` characters are removed.
**Returned value** **Returned value**
@ -69,15 +82,15 @@ overlayUTF8(s, replace, offset[, length])
**Example** **Example**
```sql ```sql
SELECT overlayUTF8('ClickHouse是一款OLAP数据库', '开源', 12, 2) AS res; SELECT overlay('Mein Vater ist aus Österreich.', 'der Türkei', 20) AS res;
``` ```
Result: Result:
```text ```text
┌─res────────────────────────┐ ┌─res───────────────────────────
ClickHouse是开源OLAP数据库 Mein Vater ist aus der Türkei.
└────────────────────────────┘ └───────────────────────────────
``` ```
## replaceOne ## replaceOne

View File

@ -3906,7 +3906,7 @@ Result:
## toDateTime64 ## toDateTime64
Converts the argument to the [DateTime64](../data-types/datetime64.md) data type. Converts an input value to a value of type [DateTime64](../data-types/datetime64.md).
**Syntax** **Syntax**
@ -3918,7 +3918,7 @@ toDateTime64(expr, scale, [timezone])
- `expr` — The value. [String](../data-types/string.md), [UInt32](../data-types/int-uint.md), [Float](../data-types/float.md) or [DateTime](../data-types/datetime.md). - `expr` — The value. [String](../data-types/string.md), [UInt32](../data-types/int-uint.md), [Float](../data-types/float.md) or [DateTime](../data-types/datetime.md).
- `scale` - Tick size (precision): 10<sup>-precision</sup> seconds. Valid range: [ 0 : 9 ]. - `scale` - Tick size (precision): 10<sup>-precision</sup> seconds. Valid range: [ 0 : 9 ].
- `timezone` - Time zone of the specified datetime64 object. - `timezone` (optional) - Time zone of the specified datetime64 object.
**Returned value** **Returned value**
@ -3977,10 +3977,137 @@ SELECT toDateTime64('2019-01-01 00:00:00', 3, 'Asia/Istanbul') AS value, toTypeN
## toDateTime64OrZero ## toDateTime64OrZero
Like [toDateTime64](#todatetime64), this function converts an input value to a value of type [DateTime64](../data-types/datetime64.md) but returns the min value of [DateTime64](../data-types/datetime64.md) if an invalid argument is received.
**Syntax**
``` sql
toDateTime64OrZero(expr, scale, [timezone])
```
**Arguments**
- `expr` — The value. [String](../data-types/string.md), [UInt32](../data-types/int-uint.md), [Float](../data-types/float.md) or [DateTime](../data-types/datetime.md).
- `scale` - Tick size (precision): 10<sup>-precision</sup> seconds. Valid range: [ 0 : 9 ].
- `timezone` (optional) - Time zone of the specified DateTime64 object.
**Returned value**
- A calendar date and time of day, with sub-second precision, otherwise the minimum value of `DateTime64`: `1970-01-01 01:00:00.000`. [DateTime64](../data-types/datetime64.md).
**Example**
Query:
```sql
SELECT toDateTime64OrZero('2008-10-12 00:00:00 00:30:30', 3) AS invalid_arg
```
Result:
```response
┌─────────────invalid_arg─┐
│ 1970-01-01 01:00:00.000 │
└─────────────────────────┘
```
**See also**
- [toDateTime64](#todatetime64).
- [toDateTime64OrNull](#todatetime64ornull).
- [toDateTime64OrDefault](#todatetime64ordefault).
## toDateTime64OrNull ## toDateTime64OrNull
Like [toDateTime64](#todatetime64), this function converts an input value to a value of type [DateTime64](../data-types/datetime64.md) but returns `NULL` if an invalid argument is received.
**Syntax**
``` sql
toDateTime64OrNull(expr, scale, [timezone])
```
**Arguments**
- `expr` — The value. [String](../data-types/string.md), [UInt32](../data-types/int-uint.md), [Float](../data-types/float.md) or [DateTime](../data-types/datetime.md).
- `scale` - Tick size (precision): 10<sup>-precision</sup> seconds. Valid range: [ 0 : 9 ].
- `timezone` (optional) - Time zone of the specified DateTime64 object.
**Returned value**
- A calendar date and time of day, with sub-second precision, otherwise `NULL`. [DateTime64](../data-types/datetime64.md)/[NULL](../data-types/nullable.md).
**Example**
Query:
```sql
SELECT
toDateTime64OrNull('1976-10-18 00:00:00.30', 3) AS valid_arg,
toDateTime64OrNull('1976-10-18 00:00:00 30', 3) AS invalid_arg
```
Result:
```response
┌───────────────valid_arg─┬─invalid_arg─┐
│ 1976-10-18 00:00:00.300 │ ᴺᵁᴸᴸ │
└─────────────────────────┴─────────────┘
```
**See also**
- [toDateTime64](#todatetime64).
- [toDateTime64OrZero](#todatetime64orzero).
- [toDateTime64OrDefault](#todatetime64ordefault).
## toDateTime64OrDefault ## toDateTime64OrDefault
Like [toDateTime64](#todatetime64), this function converts an input value to a value of type [DateTime64](../data-types/datetime64.md),
but returns either the default value of [DateTime64](../data-types/datetime64.md)
or the provided default if an invalid argument is received.
**Syntax**
``` sql
toDateTime64OrNull(expr, scale, [timezone, default])
```
**Arguments**
- `expr` — The value. [String](../data-types/string.md), [UInt32](../data-types/int-uint.md), [Float](../data-types/float.md) or [DateTime](../data-types/datetime.md).
- `scale` - Tick size (precision): 10<sup>-precision</sup> seconds. Valid range: [ 0 : 9 ].
- `timezone` (optional) - Time zone of the specified DateTime64 object.
- `default` (optional) - Default value to return if an invalid argument is received. [DateTime64](../data-types/datetime64.md).
**Returned value**
- A calendar date and time of day, with sub-second precision, otherwise the minimum value of `DateTime64` or the `default` value if provided. [DateTime64](../data-types/datetime64.md).
**Example**
Query:
```sql
SELECT
toDateTime64OrDefault('1976-10-18 00:00:00 30', 3) AS invalid_arg,
toDateTime64OrDefault('1976-10-18 00:00:00 30', 3, 'UTC', toDateTime64('2001-01-01 00:00:00.00',3)) AS invalid_arg_with_default
```
Result:
```response
┌─────────────invalid_arg─┬─invalid_arg_with_default─┐
│ 1970-01-01 01:00:00.000 │ 2000-12-31 23:00:00.000 │
└─────────────────────────┴──────────────────────────┘
```
**See also**
- [toDateTime64](#todatetime64).
- [toDateTime64OrZero](#todatetime64orzero).
- [toDateTime64OrNull](#todatetime64ornull).
## toDecimal32 ## toDecimal32
Converts an input value to a value of type [`Decimal(9, S)`](../data-types/decimal.md) with scale of `S`. Throws an exception in case of an error. Converts an input value to a value of type [`Decimal(9, S)`](../data-types/decimal.md) with scale of `S`. Throws an exception in case of an error.

View File

@ -265,8 +265,6 @@ SELECT now() AS current_date_time, current_date_time + INTERVAL '4' day + INTERV
└─────────────────────┴────────────────────────────────────────────────────────────┘ └─────────────────────┴────────────────────────────────────────────────────────────┘
``` ```
You can work with dates without using `INTERVAL`, just by adding or subtracting seconds, minutes, and hours. For example, an interval of one day can be set by adding `60*60*24`.
:::note :::note
The `INTERVAL` syntax or `addDays` function are always preferred. Simple addition or subtraction (syntax like `now() + ...`) doesn't consider time settings. For example, daylight saving time. The `INTERVAL` syntax or `addDays` function are always preferred. Simple addition or subtraction (syntax like `now() + ...`) doesn't consider time settings. For example, daylight saving time.
::: :::

View File

@ -351,7 +351,7 @@ ALTER TABLE mt DELETE IN PARTITION ID '2' WHERE p = 2;
You can specify the partition expression in `ALTER ... PARTITION` queries in different ways: You can specify the partition expression in `ALTER ... PARTITION` queries in different ways:
- As a value from the `partition` column of the `system.parts` table. For example, `ALTER TABLE visits DETACH PARTITION 201901`. - As a value from the `partition` column of the `system.parts` table. For example, `ALTER TABLE visits DETACH PARTITION 201901`.
- Using the keyword `ALL`. It can be used only with DROP/DETACH/ATTACH. For example, `ALTER TABLE visits ATTACH PARTITION ALL`. - Using the keyword `ALL`. It can be used only with DROP/DETACH/ATTACH/ATTACH FROM. For example, `ALTER TABLE visits ATTACH PARTITION ALL`.
- As a tuple of expressions or constants that matches (in types) the table partitioning keys tuple. In the case of a single element partitioning key, the expression should be wrapped in the `tuple (...)` function. For example, `ALTER TABLE visits DETACH PARTITION tuple(toYYYYMM(toDate('2019-01-25')))`. - As a tuple of expressions or constants that matches (in types) the table partitioning keys tuple. In the case of a single element partitioning key, the expression should be wrapped in the `tuple (...)` function. For example, `ALTER TABLE visits DETACH PARTITION tuple(toYYYYMM(toDate('2019-01-25')))`.
- Using the partition ID. Partition ID is a string identifier of the partition (human-readable, if possible) that is used as the names of partitions in the file system and in ZooKeeper. The partition ID must be specified in the `PARTITION ID` clause, in a single quotes. For example, `ALTER TABLE visits DETACH PARTITION ID '201901'`. - Using the partition ID. Partition ID is a string identifier of the partition (human-readable, if possible) that is used as the names of partitions in the file system and in ZooKeeper. The partition ID must be specified in the `PARTITION ID` clause, in a single quotes. For example, `ALTER TABLE visits DETACH PARTITION ID '201901'`.
- In the [ALTER ATTACH PART](#attach-partitionpart) and [DROP DETACHED PART](#drop-detached-partitionpart) query, to specify the name of a part, use string literal with a value from the `name` column of the [system.detached_parts](/docs/en/operations/system-tables/detached_parts.md/#system_tables-detached_parts) table. For example, `ALTER TABLE visits ATTACH PART '201901_1_1_0'`. - In the [ALTER ATTACH PART](#attach-partitionpart) and [DROP DETACHED PART](#drop-detached-partitionpart) query, to specify the name of a part, use string literal with a value from the `name` column of the [system.detached_parts](/docs/en/operations/system-tables/detached_parts.md/#system_tables-detached_parts) table. For example, `ALTER TABLE visits ATTACH PART '201901_1_1_0'`.

View File

@ -13,7 +13,7 @@ The lightweight `DELETE` statement removes rows from the table `[db.]table` that
DELETE FROM [db.]table [ON CLUSTER cluster] [IN PARTITION partition_expr] WHERE expr; DELETE FROM [db.]table [ON CLUSTER cluster] [IN PARTITION partition_expr] WHERE expr;
``` ```
It is called "lightweight `DELETE`" to contrast it to the [ALTER table DELETE](/en/sql-reference/statements/alter/delete) command, which is a heavyweight process. It is called "lightweight `DELETE`" to contrast it to the [ALTER TABLE ... DELETE](/en/sql-reference/statements/alter/delete) command, which is a heavyweight process.
## Examples ## Examples
@ -22,23 +22,27 @@ It is called "lightweight `DELETE`" to contrast it to the [ALTER table DELETE](/
DELETE FROM hits WHERE Title LIKE '%hello%'; DELETE FROM hits WHERE Title LIKE '%hello%';
``` ```
## Lightweight `DELETE` does not delete data from storage immediately ## Lightweight `DELETE` does not delete data immediately
With lightweight `DELETE`, deleted rows are internally marked as deleted immediately and will be automatically filtered out of all subsequent queries. However, cleanup of data happens during the next merge. As a result, it is possible that for an unspecified period, data is not actually deleted from storage and is only marked as deleted. Lightweight `DELETE` is implemented as a [mutation](/en/sql-reference/statements/alter#mutations) that marks rows as deleted but does not immediately physically delete them.
If you need to guarantee that your data is deleted from storage in a predictable time, consider using the [ALTER table DELETE](/en/sql-reference/statements/alter/delete) command. Note that deleting data using `ALTER table DELETE` may consume significant resources as it recreates all affected parts. By default, `DELETE` statements wait until marking the rows as deleted is completed before returning. This can take a long time if the amount of data is large. Alternatively, you can run it asynchronously in the background using the setting [`lightweight_deletes_sync`](/en/operations/settings/settings#lightweight_deletes_sync). If disabled, the `DELETE` statement is going to return immediately, but the data can still be visible to queries until the background mutation is finished.
The mutation does not physically delete the rows that have been marked as deleted, this will only happen during the next merge. As a result, it is possible that for an unspecified period, data is not actually deleted from storage and is only marked as deleted.
If you need to guarantee that your data is deleted from storage in a predictable time, consider using the table setting [`min_age_to_force_merge_seconds`](https://clickhouse.com/docs/en/operations/settings/merge-tree-settings#min_age_to_force_merge_seconds). Or you can use the [ALTER TABLE ... DELETE](/en/sql-reference/statements/alter/delete) command. Note that deleting data using `ALTER TABLE ... DELETE` may consume significant resources as it recreates all affected parts.
## Deleting large amounts of data ## Deleting large amounts of data
Large deletes can negatively affect ClickHouse performance. If you are attempting to delete all rows from a table, consider using the [`TRUNCATE TABLE`](/en/sql-reference/statements/truncate) command. Large deletes can negatively affect ClickHouse performance. If you are attempting to delete all rows from a table, consider using the [`TRUNCATE TABLE`](/en/sql-reference/statements/truncate) command.
If you anticipate frequent deletes, consider using a [custom partitioning key](/en/engines/table-engines/mergetree-family/custom-partitioning-key). You can then use the [`ALTER TABLE...DROP PARTITION`](/en/sql-reference/statements/alter/partition#drop-partitionpart) command to quickly drop all rows associated with that partition. If you anticipate frequent deletes, consider using a [custom partitioning key](/en/engines/table-engines/mergetree-family/custom-partitioning-key). You can then use the [`ALTER TABLE ... DROP PARTITION`](/en/sql-reference/statements/alter/partition#drop-partitionpart) command to quickly drop all rows associated with that partition.
## Limitations of lightweight `DELETE` ## Limitations of lightweight `DELETE`
### Lightweight `DELETE`s with projections ### Lightweight `DELETE`s with projections
By default, `DELETE` does not work for tables with projections. This is because rows in a projection may be affected by a `DELETE` operation. But there is a [MergeTree setting](https://clickhouse.com/docs/en/operations/settings/merge-tree-settings) `lightweight_mutation_projection_mode` can change the behavior. By default, `DELETE` does not work for tables with projections. This is because rows in a projection may be affected by a `DELETE` operation. But there is a [MergeTree setting](https://clickhouse.com/docs/en/operations/settings/merge-tree-settings) `lightweight_mutation_projection_mode` to change the behavior.
## Performance considerations when using lightweight `DELETE` ## Performance considerations when using lightweight `DELETE`
@ -48,7 +52,7 @@ The following can also negatively impact lightweight `DELETE` performance:
- A heavy `WHERE` condition in a `DELETE` query. - A heavy `WHERE` condition in a `DELETE` query.
- If the mutations queue is filled with many other mutations, this can possibly lead to performance issues as all mutations on a table are executed sequentially. - If the mutations queue is filled with many other mutations, this can possibly lead to performance issues as all mutations on a table are executed sequentially.
- The affected table having a very large number of data parts. - The affected table has a very large number of data parts.
- Having a lot of data in compact parts. In a Compact part, all columns are stored in one file. - Having a lot of data in compact parts. In a Compact part, all columns are stored in one file.
## Delete permissions ## Delete permissions
@ -61,31 +65,31 @@ GRANT ALTER DELETE ON db.table to username;
## How lightweight DELETEs work internally in ClickHouse ## How lightweight DELETEs work internally in ClickHouse
1. A "mask" is applied to affected rows 1. **A "mask" is applied to affected rows**
When a `DELETE FROM table ...` query is executed, ClickHouse saves a mask where each row is marked as either “existing” or as “deleted”. Those “deleted” rows are omitted for subsequent queries. However, rows are actually only removed later by subsequent merges. Writing this mask is much more lightweight than what is done by an `ALTER table DELETE` query. When a `DELETE FROM table ...` query is executed, ClickHouse saves a mask where each row is marked as either “existing” or as “deleted”. Those “deleted” rows are omitted for subsequent queries. However, rows are actually only removed later by subsequent merges. Writing this mask is much more lightweight than what is done by an `ALTER TABLE ... DELETE` query.
The mask is implemented as a hidden `_row_exists` system column that stores `True` for all visible rows and `False` for deleted ones. This column is only present in a part if some rows in the part were deleted. This column does not exist when a part has all values equal to `True`. The mask is implemented as a hidden `_row_exists` system column that stores `True` for all visible rows and `False` for deleted ones. This column is only present in a part if some rows in the part were deleted. This column does not exist when a part has all values equal to `True`.
2. `SELECT` queries are transformed to include the mask 2. **`SELECT` queries are transformed to include the mask**
When a masked column is used in a query, the `SELECT ... FROM table WHERE condition` query internally is extended by the predicate on `_row_exists` and is transformed to: When a masked column is used in a query, the `SELECT ... FROM table WHERE condition` query internally is extended by the predicate on `_row_exists` and is transformed to:
```sql ```sql
SELECT ... FROM table PREWHERE _row_exists WHERE condition SELECT ... FROM table PREWHERE _row_exists WHERE condition
``` ```
At execution time, the column `_row_exists` is read to determine which rows should not be returned. If there are many deleted rows, ClickHouse can determine which granules can be fully skipped when reading the rest of the columns. At execution time, the column `_row_exists` is read to determine which rows should not be returned. If there are many deleted rows, ClickHouse can determine which granules can be fully skipped when reading the rest of the columns.
3. `DELETE` queries are transformed to `ALTER table UPDATE` queries 3. **`DELETE` queries are transformed to `ALTER TABLE ... UPDATE` queries**
The `DELETE FROM table WHERE condition` is translated into an `ALTER table UPDATE _row_exists = 0 WHERE condition` mutation. The `DELETE FROM table WHERE condition` is translated into an `ALTER TABLE table UPDATE _row_exists = 0 WHERE condition` mutation.
Internally, this mutation is executed in two steps: Internally, this mutation is executed in two steps:
1. A `SELECT count() FROM table WHERE condition` command is executed for each individual part to determine if the part is affected. 1. A `SELECT count() FROM table WHERE condition` command is executed for each individual part to determine if the part is affected.
2. Based on the commands above, affected parts are then mutated, and hardlinks are created for unaffected parts. In the case of wide parts, the `_row_exists` column for each row is updated and all other columns' files are hardlinked. For compact parts, all columns are re-written because they are all stored together in one file. 2. Based on the commands above, affected parts are then mutated, and hardlinks are created for unaffected parts. In the case of wide parts, the `_row_exists` column for each row is updated, and all other columns' files are hardlinked. For compact parts, all columns are re-written because they are all stored together in one file.
From the steps above, we can see that lightweight deletes using the masking technique improves performance over traditional `ALTER table DELETE` commands because `ALTER table DELETE` reads and re-writes all the columns' files for affected parts. From the steps above, we can see that lightweight `DELETE` using the masking technique improves performance over traditional `ALTER TABLE ... DELETE` because it does not re-write all the columns' files for affected parts.
## Related content ## Related content

View File

@ -15,7 +15,14 @@ The `FROM` clause specifies the source to read data from:
Subquery is another `SELECT` query that may be specified in parenthesis inside `FROM` clause. Subquery is another `SELECT` query that may be specified in parenthesis inside `FROM` clause.
`FROM` clause can contain multiple data sources, separated by commas, which is equivalent of performing [CROSS JOIN](../../../sql-reference/statements/select/join.md) on them. The `FROM` can contain multiple data sources, separated by commas, which is equivalent of performing [CROSS JOIN](../../../sql-reference/statements/select/join.md) on them.
`FROM` can optionally appear before a `SELECT` clause. This is a ClickHouse-specific extension of standard SQL which makes `SELECT` statements easier to read. Example:
```sql
FROM table
SELECT *
```
## FINAL Modifier ## FINAL Modifier
@ -45,19 +52,19 @@ As an alternative to using `FINAL`, it is sometimes possible to use different qu
### Example Usage ### Example Usage
**Using the `FINAL` keyword** Using the `FINAL` keyword
```sql ```sql
SELECT x, y FROM mytable FINAL WHERE x > 1; SELECT x, y FROM mytable FINAL WHERE x > 1;
``` ```
**Using `FINAL` as a query-level setting** Using `FINAL` as a query-level setting
```sql ```sql
SELECT x, y FROM mytable WHERE x > 1 SETTINGS final = 1; SELECT x, y FROM mytable WHERE x > 1 SETTINGS final = 1;
``` ```
**Using `FINAL` as a session-level setting** Using `FINAL` as a session-level setting
```sql ```sql
SET final = 1; SET final = 1;

View File

@ -11,6 +11,7 @@
#include <Core/ServerUUID.h> #include <Core/ServerUUID.h>
#include <Common/logger_useful.h> #include <Common/logger_useful.h>
#include <Common/CgroupsMemoryUsageObserver.h> #include <Common/CgroupsMemoryUsageObserver.h>
#include <Common/MemoryWorker.h>
#include <Common/ErrorHandlers.h> #include <Common/ErrorHandlers.h>
#include <Common/assertProcessUserMatchesDataOwner.h> #include <Common/assertProcessUserMatchesDataOwner.h>
#include <Common/makeSocketAddress.h> #include <Common/makeSocketAddress.h>
@ -384,6 +385,9 @@ try
LOG_INFO(log, "Background threads finished in {} ms", watch.elapsedMilliseconds()); LOG_INFO(log, "Background threads finished in {} ms", watch.elapsedMilliseconds());
}); });
MemoryWorker memory_worker(config().getUInt64("memory_worker_period_ms", 0));
memory_worker.start();
static ServerErrorHandler error_handler; static ServerErrorHandler error_handler;
Poco::ErrorHandler::set(&error_handler); Poco::ErrorHandler::set(&error_handler);
@ -425,8 +429,9 @@ try
for (const auto & server : *servers) for (const auto & server : *servers)
metrics.emplace_back(ProtocolServerMetrics{server.getPortName(), server.currentThreads(), server.refusedConnections()}); metrics.emplace_back(ProtocolServerMetrics{server.getPortName(), server.currentThreads(), server.refusedConnections()});
return metrics; return metrics;
} },
); /*update_jemalloc_epoch_=*/memory_worker.getSource() != MemoryWorker::MemoryUsageSource::Jemalloc,
/*update_rss_=*/memory_worker.getSource() == MemoryWorker::MemoryUsageSource::None);
std::vector<std::string> listen_hosts = DB::getMultipleValuesFromConfig(config(), "", "listen_host"); std::vector<std::string> listen_hosts = DB::getMultipleValuesFromConfig(config(), "", "listen_host");
@ -655,7 +660,6 @@ try
GWPAsan::initFinished(); GWPAsan::initFinished();
#endif #endif
LOG_INFO(log, "Ready for connections."); LOG_INFO(log, "Ready for connections.");
waitForTerminationRequest(); waitForTerminationRequest();

View File

@ -11,7 +11,6 @@
#include <Poco/Util/HelpFormatter.h> #include <Poco/Util/HelpFormatter.h>
#include <Poco/Environment.h> #include <Poco/Environment.h>
#include <Poco/Config.h> #include <Poco/Config.h>
#include <Common/Jemalloc.h>
#include <Common/scope_guard_safe.h> #include <Common/scope_guard_safe.h>
#include <Common/logger_useful.h> #include <Common/logger_useful.h>
#include <base/phdr_cache.h> #include <base/phdr_cache.h>
@ -25,6 +24,7 @@
#include <base/Numa.h> #include <base/Numa.h>
#include <Common/PoolId.h> #include <Common/PoolId.h>
#include <Common/MemoryTracker.h> #include <Common/MemoryTracker.h>
#include <Common/MemoryWorker.h>
#include <Common/ClickHouseRevision.h> #include <Common/ClickHouseRevision.h>
#include <Common/DNSResolver.h> #include <Common/DNSResolver.h>
#include <Common/CgroupsMemoryUsageObserver.h> #include <Common/CgroupsMemoryUsageObserver.h>
@ -111,6 +111,8 @@
#include <filesystem> #include <filesystem>
#include <unordered_set> #include <unordered_set>
#include <Common/Jemalloc.h>
#include "config.h" #include "config.h"
#include <Common/config_version.h> #include <Common/config_version.h>
@ -449,9 +451,12 @@ void checkForUsersNotInMainConfig(
} }
} }
namespace
{
/// Unused in other builds /// Unused in other builds
#if defined(OS_LINUX) #if defined(OS_LINUX)
static String readLine(const String & path) String readLine(const String & path)
{ {
ReadBufferFromFile in(path); ReadBufferFromFile in(path);
String contents; String contents;
@ -459,7 +464,7 @@ static String readLine(const String & path)
return contents; return contents;
} }
static int readNumber(const String & path) int readNumber(const String & path)
{ {
ReadBufferFromFile in(path); ReadBufferFromFile in(path);
int result; int result;
@ -469,7 +474,7 @@ static int readNumber(const String & path)
#endif #endif
static void sanityChecks(Server & server) void sanityChecks(Server & server)
{ {
std::string data_path = getCanonicalPath(server.config().getString("path", DBMS_DEFAULT_PATH)); std::string data_path = getCanonicalPath(server.config().getString("path", DBMS_DEFAULT_PATH));
std::string logs_path = server.config().getString("logger.log", ""); std::string logs_path = server.config().getString("logger.log", "");
@ -590,6 +595,8 @@ static void sanityChecks(Server & server)
} }
} }
}
void loadStartupScripts(const Poco::Util::AbstractConfiguration & config, ContextMutablePtr context, Poco::Logger * log) void loadStartupScripts(const Poco::Util::AbstractConfiguration & config, ContextMutablePtr context, Poco::Logger * log)
{ {
try try
@ -906,6 +913,8 @@ try
LOG_INFO(log, "Background threads finished in {} ms", watch.elapsedMilliseconds()); LOG_INFO(log, "Background threads finished in {} ms", watch.elapsedMilliseconds());
}); });
MemoryWorker memory_worker(global_context->getServerSettings().memory_worker_period_ms);
/// This object will periodically calculate some metrics. /// This object will periodically calculate some metrics.
ServerAsynchronousMetrics async_metrics( ServerAsynchronousMetrics async_metrics(
global_context, global_context,
@ -924,8 +933,9 @@ try
for (const auto & server : servers) for (const auto & server : servers)
metrics.emplace_back(ProtocolServerMetrics{server.getPortName(), server.currentThreads(), server.refusedConnections()}); metrics.emplace_back(ProtocolServerMetrics{server.getPortName(), server.currentThreads(), server.refusedConnections()});
return metrics; return metrics;
} },
); /*update_jemalloc_epoch_=*/memory_worker.getSource() != MemoryWorker::MemoryUsageSource::Jemalloc,
/*update_rss_=*/memory_worker.getSource() == MemoryWorker::MemoryUsageSource::None);
/// NOTE: global context should be destroyed *before* GlobalThreadPool::shutdown() /// NOTE: global context should be destroyed *before* GlobalThreadPool::shutdown()
/// Otherwise GlobalThreadPool::shutdown() will hang, since Context holds some threads. /// Otherwise GlobalThreadPool::shutdown() will hang, since Context holds some threads.
@ -1204,6 +1214,8 @@ try
FailPointInjection::enableFromGlobalConfig(config()); FailPointInjection::enableFromGlobalConfig(config());
memory_worker.start();
int default_oom_score = 0; int default_oom_score = 0;
#if !defined(NDEBUG) #if !defined(NDEBUG)
@ -1547,15 +1559,6 @@ try
total_memory_tracker.setDescription("(total)"); total_memory_tracker.setDescription("(total)");
total_memory_tracker.setMetric(CurrentMetrics::MemoryTracking); total_memory_tracker.setMetric(CurrentMetrics::MemoryTracking);
if (cgroups_memory_usage_observer)
{
double hard_limit_ratio = new_server_settings.cgroup_memory_watcher_hard_limit_ratio;
double soft_limit_ratio = new_server_settings.cgroup_memory_watcher_soft_limit_ratio;
cgroups_memory_usage_observer->setMemoryUsageLimits(
static_cast<uint64_t>(max_server_memory_usage * hard_limit_ratio),
static_cast<uint64_t>(max_server_memory_usage * soft_limit_ratio));
}
size_t merges_mutations_memory_usage_soft_limit = new_server_settings.merges_mutations_memory_usage_soft_limit; size_t merges_mutations_memory_usage_soft_limit = new_server_settings.merges_mutations_memory_usage_soft_limit;
size_t default_merges_mutations_server_memory_usage = static_cast<size_t>(current_physical_server_memory * new_server_settings.merges_mutations_memory_usage_to_ram_ratio); size_t default_merges_mutations_server_memory_usage = static_cast<size_t>(current_physical_server_memory * new_server_settings.merges_mutations_memory_usage_to_ram_ratio);
@ -1584,8 +1587,6 @@ try
background_memory_tracker.setDescription("(background)"); background_memory_tracker.setDescription("(background)");
background_memory_tracker.setMetric(CurrentMetrics::MergesMutationsMemoryTracking); background_memory_tracker.setMetric(CurrentMetrics::MergesMutationsMemoryTracking);
total_memory_tracker.setAllowUseJemallocMemory(new_server_settings.allow_use_jemalloc_memory);
auto * global_overcommit_tracker = global_context->getGlobalOvercommitTracker(); auto * global_overcommit_tracker = global_context->getGlobalOvercommitTracker();
total_memory_tracker.setOvercommitTracker(global_overcommit_tracker); total_memory_tracker.setOvercommitTracker(global_overcommit_tracker);

View File

@ -116,15 +116,17 @@ class GroupConcatImpl final
SerializationPtr serialization; SerializationPtr serialization;
UInt64 limit; UInt64 limit;
const String delimiter; const String delimiter;
const DataTypePtr type;
public: public:
GroupConcatImpl(const DataTypePtr & data_type_, const Array & parameters_, UInt64 limit_, const String & delimiter_) GroupConcatImpl(const DataTypePtr & data_type_, const Array & parameters_, UInt64 limit_, const String & delimiter_)
: IAggregateFunctionDataHelper<GroupConcatData<has_limit>, GroupConcatImpl<has_limit>>( : IAggregateFunctionDataHelper<GroupConcatData<has_limit>, GroupConcatImpl<has_limit>>(
{data_type_}, parameters_, std::make_shared<DataTypeString>()) {data_type_}, parameters_, std::make_shared<DataTypeString>())
, serialization(this->argument_types[0]->getDefaultSerialization())
, limit(limit_) , limit(limit_)
, delimiter(delimiter_) , delimiter(delimiter_)
, type(data_type_)
{ {
serialization = isFixedString(type) ? std::make_shared<DataTypeString>()->getDefaultSerialization() : this->argument_types[0]->getDefaultSerialization();
} }
String getName() const override { return name; } String getName() const override { return name; }
@ -140,7 +142,14 @@ public:
if (cur_data.data_size != 0) if (cur_data.data_size != 0)
cur_data.insertChar(delimiter.c_str(), delimiter.size(), arena); cur_data.insertChar(delimiter.c_str(), delimiter.size(), arena);
cur_data.insert(columns[0], serialization, row_num, arena); if (isFixedString(type))
{
ColumnWithTypeAndName col = {columns[0]->getPtr(), type, "column"};
const auto & col_str = castColumn(col, std::make_shared<DataTypeString>());
cur_data.insert(col_str.get(), serialization, row_num, arena);
}
else
cur_data.insert(columns[0], serialization, row_num, arena);
} }
void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs, Arena * arena) const override void merge(AggregateDataPtr __restrict place, ConstAggregateDataPtr rhs, Arena * arena) const override

View File

@ -459,6 +459,8 @@ public:
bool isParallelizeMergePrepareNeeded() const override { return is_parallelize_merge_prepare_needed; } bool isParallelizeMergePrepareNeeded() const override { return is_parallelize_merge_prepare_needed; }
constexpr static bool parallelizeMergeWithKey() { return true; }
void parallelizeMergePrepare(AggregateDataPtrs & places, ThreadPool & thread_pool, std::atomic<bool> & is_cancelled) const override void parallelizeMergePrepare(AggregateDataPtrs & places, ThreadPool & thread_pool, std::atomic<bool> & is_cancelled) const override
{ {
if constexpr (is_parallelize_merge_prepare_needed) if constexpr (is_parallelize_merge_prepare_needed)

View File

@ -145,6 +145,8 @@ public:
virtual bool isParallelizeMergePrepareNeeded() const { return false; } virtual bool isParallelizeMergePrepareNeeded() const { return false; }
constexpr static bool parallelizeMergeWithKey() { return false; }
virtual void parallelizeMergePrepare(AggregateDataPtrs & /*places*/, ThreadPool & /*thread_pool*/, std::atomic<bool> & /*is_cancelled*/) const virtual void parallelizeMergePrepare(AggregateDataPtrs & /*places*/, ThreadPool & /*thread_pool*/, std::atomic<bool> & /*is_cancelled*/) const
{ {
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "parallelizeMergePrepare() with thread pool parameter isn't implemented for {} ", getName()); throw Exception(ErrorCodes::NOT_IMPLEMENTED, "parallelizeMergePrepare() with thread pool parameter isn't implemented for {} ", getName());
@ -169,7 +171,7 @@ public:
/// Merges states (on which src places points to) with other states (on which dst places points to) of current aggregation function /// Merges states (on which src places points to) with other states (on which dst places points to) of current aggregation function
/// then destroy states (on which src places points to). /// then destroy states (on which src places points to).
virtual void mergeAndDestroyBatch(AggregateDataPtr * dst_places, AggregateDataPtr * src_places, size_t size, size_t offset, Arena * arena) const = 0; virtual void mergeAndDestroyBatch(AggregateDataPtr * dst_places, AggregateDataPtr * src_places, size_t size, size_t offset, ThreadPool & thread_pool, std::atomic<bool> & is_cancelled, Arena * arena) const = 0;
/// Serializes state (to transmit it over the network, for example). /// Serializes state (to transmit it over the network, for example).
virtual void serialize(ConstAggregateDataPtr __restrict place, WriteBuffer & buf, std::optional<size_t> version = std::nullopt) const = 0; /// NOLINT virtual void serialize(ConstAggregateDataPtr __restrict place, WriteBuffer & buf, std::optional<size_t> version = std::nullopt) const = 0; /// NOLINT
@ -499,11 +501,15 @@ public:
static_cast<const Derived *>(this)->merge(places[i] + place_offset, rhs[i], arena); static_cast<const Derived *>(this)->merge(places[i] + place_offset, rhs[i], arena);
} }
void mergeAndDestroyBatch(AggregateDataPtr * dst_places, AggregateDataPtr * rhs_places, size_t size, size_t offset, Arena * arena) const override void mergeAndDestroyBatch(AggregateDataPtr * dst_places, AggregateDataPtr * rhs_places, size_t size, size_t offset, ThreadPool & thread_pool, std::atomic<bool> & is_cancelled, Arena * arena) const override
{ {
for (size_t i = 0; i < size; ++i) for (size_t i = 0; i < size; ++i)
{ {
static_cast<const Derived *>(this)->merge(dst_places[i] + offset, rhs_places[i] + offset, arena); if constexpr (Derived::parallelizeMergeWithKey())
static_cast<const Derived *>(this)->merge(dst_places[i] + offset, rhs_places[i] + offset, thread_pool, is_cancelled, arena);
else
static_cast<const Derived *>(this)->merge(dst_places[i] + offset, rhs_places[i] + offset, arena);
static_cast<const Derived *>(this)->destroy(rhs_places[i] + offset); static_cast<const Derived *>(this)->destroy(rhs_places[i] + offset);
} }
} }

View File

@ -101,6 +101,13 @@ public:
auto merge(const UniqExactSet & other, ThreadPool * thread_pool = nullptr, std::atomic<bool> * is_cancelled = nullptr) auto merge(const UniqExactSet & other, ThreadPool * thread_pool = nullptr, std::atomic<bool> * is_cancelled = nullptr)
{ {
/// If the size is large, we may convert the singleLevelHash to twoLevelHash and merge in parallel.
if (other.size() > 40000)
{
if (isSingleLevel())
convertToTwoLevel();
}
if (isSingleLevel() && other.isTwoLevel()) if (isSingleLevel() && other.isTwoLevel())
convertToTwoLevel(); convertToTwoLevel();

View File

@ -913,11 +913,15 @@ void RestorerFromBackup::createTable(const QualifiedTableName & table_name)
table_info.database = DatabaseCatalog::instance().getDatabase(table_name.database); table_info.database = DatabaseCatalog::instance().getDatabase(table_name.database);
DatabasePtr database = table_info.database; DatabasePtr database = table_info.database;
auto query_context = Context::createCopy(context);
query_context->setSetting("database_replicated_allow_explicit_uuid", 3);
query_context->setSetting("database_replicated_allow_replicated_engine_arguments", 3);
/// Execute CREATE TABLE query (we call IDatabase::createTableRestoredFromBackup() to allow the database to do some /// Execute CREATE TABLE query (we call IDatabase::createTableRestoredFromBackup() to allow the database to do some
/// database-specific things). /// database-specific things).
database->createTableRestoredFromBackup( database->createTableRestoredFromBackup(
create_table_query, create_table_query,
context, query_context,
restore_coordination, restore_coordination,
std::chrono::duration_cast<std::chrono::milliseconds>(create_table_timeout).count()); std::chrono::duration_cast<std::chrono::milliseconds>(create_table_timeout).count());
} }

View File

@ -176,7 +176,7 @@ add_library (clickhouse_new_delete STATIC Common/new_delete.cpp)
target_link_libraries (clickhouse_new_delete PRIVATE clickhouse_common_io) target_link_libraries (clickhouse_new_delete PRIVATE clickhouse_common_io)
if (TARGET ch_contrib::jemalloc) if (TARGET ch_contrib::jemalloc)
target_link_libraries (clickhouse_new_delete PRIVATE ch_contrib::jemalloc) target_link_libraries (clickhouse_new_delete PRIVATE ch_contrib::jemalloc)
target_link_libraries (clickhouse_common_io PRIVATE ch_contrib::jemalloc) target_link_libraries (clickhouse_common_io PUBLIC ch_contrib::jemalloc)
target_link_libraries (clickhouse_storages_system PRIVATE ch_contrib::jemalloc) target_link_libraries (clickhouse_storages_system PRIVATE ch_contrib::jemalloc)
endif() endif()

View File

@ -1896,6 +1896,21 @@ void ClientBase::processParsedSingleQuery(const String & full_query, const Strin
/// Temporarily apply query settings to context. /// Temporarily apply query settings to context.
std::optional<Settings> old_settings; std::optional<Settings> old_settings;
SCOPE_EXIT_SAFE({ SCOPE_EXIT_SAFE({
try
{
/// We need to park ParallelFormating threads,
/// because they can use settings from global context
/// and it can lead to data race with `setSettings`
resetOutput();
}
catch (...)
{
if (!have_error)
{
client_exception = std::make_unique<Exception>(getCurrentExceptionMessageAndPattern(print_stack_trace), getCurrentExceptionCode());
have_error = true;
}
}
if (old_settings) if (old_settings)
client_context->setSettings(*old_settings); client_context->setSettings(*old_settings);
}); });

View File

@ -168,7 +168,7 @@ std::vector<ConnectionPoolWithFailover::TryResult> ConnectionPoolWithFailover::g
{ return tryGetEntry(pool, timeouts, fail_message, settings, &table_to_check, /*async_callback=*/ {}); }; { return tryGetEntry(pool, timeouts, fail_message, settings, &table_to_check, /*async_callback=*/ {}); };
return getManyImpl(settings, pool_mode, try_get_entry, return getManyImpl(settings, pool_mode, try_get_entry,
/*skip_unavailable_endpoints=*/ std::nullopt, /*skip_unavailable_endpoints=*/ false, /// skip_unavailable_endpoints is used to get the min number of entries, and we need at least one
/*priority_func=*/ {}, /*priority_func=*/ {},
settings.distributed_insert_skip_read_only_replicas); settings.distributed_insert_skip_read_only_replicas);
} }

View File

@ -42,7 +42,7 @@ public:
size_t max_error_cap = DBMS_CONNECTION_POOL_WITH_FAILOVER_MAX_ERROR_COUNT); size_t max_error_cap = DBMS_CONNECTION_POOL_WITH_FAILOVER_MAX_ERROR_COUNT);
using Entry = IConnectionPool::Entry; using Entry = IConnectionPool::Entry;
using PoolWithFailoverBase<IConnectionPool>::isTryResultInvalid; using PoolWithFailoverBase<IConnectionPool>::getValidTryResult;
/** Allocates connection to work. */ /** Allocates connection to work. */
Entry get(const ConnectionTimeouts & timeouts) override; Entry get(const ConnectionTimeouts & timeouts) override;
@ -98,7 +98,7 @@ public:
std::vector<Base::ShuffledPool> getShuffledPools(const Settings & settings, GetPriorityFunc priority_func = {}, bool use_slowdown_count = false); std::vector<Base::ShuffledPool> getShuffledPools(const Settings & settings, GetPriorityFunc priority_func = {}, bool use_slowdown_count = false);
size_t getMaxErrorCup() const { return Base::max_error_cap; } size_t getMaxErrorCap() const { return Base::max_error_cap; }
void updateSharedError(std::vector<ShuffledPool> & shuffled_pools) void updateSharedError(std::vector<ShuffledPool> & shuffled_pools)
{ {

View File

@ -327,7 +327,7 @@ HedgedConnectionsFactory::State HedgedConnectionsFactory::processFinishedConnect
ShuffledPool & shuffled_pool = shuffled_pools[index]; ShuffledPool & shuffled_pool = shuffled_pools[index];
LOG_INFO(log, "Connection failed at try №{}, reason: {}", (shuffled_pool.error_count + 1), fail_message); LOG_INFO(log, "Connection failed at try №{}, reason: {}", (shuffled_pool.error_count + 1), fail_message);
shuffled_pool.error_count = std::min(pool->getMaxErrorCup(), shuffled_pool.error_count + 1); shuffled_pool.error_count = std::min(pool->getMaxErrorCap(), shuffled_pool.error_count + 1);
shuffled_pool.slowdown_count = 0; shuffled_pool.slowdown_count = 0;
if (shuffled_pool.error_count >= max_tries) if (shuffled_pool.error_count >= max_tries)

View File

@ -1,5 +1,3 @@
#include <Common/AsynchronousMetrics.h>
#include <IO/MMappedFileCache.h> #include <IO/MMappedFileCache.h>
#include <IO/ReadHelpers.h> #include <IO/ReadHelpers.h>
#include <IO/UncompressedCache.h> #include <IO/UncompressedCache.h>
@ -8,8 +6,10 @@
#include <base/find_symbols.h> #include <base/find_symbols.h>
#include <base/getPageSize.h> #include <base/getPageSize.h>
#include <sys/resource.h> #include <sys/resource.h>
#include <Common/AsynchronousMetrics.h>
#include <Common/CurrentMetrics.h> #include <Common/CurrentMetrics.h>
#include <Common/Exception.h> #include <Common/Exception.h>
#include <Common/Jemalloc.h>
#include <Common/filesystemHelpers.h> #include <Common/filesystemHelpers.h>
#include <Common/formatReadable.h> #include <Common/formatReadable.h>
#include <Common/logger_useful.h> #include <Common/logger_useful.h>
@ -69,10 +69,14 @@ static void openCgroupv2MetricFile(const std::string & filename, std::optional<R
AsynchronousMetrics::AsynchronousMetrics( AsynchronousMetrics::AsynchronousMetrics(
unsigned update_period_seconds, unsigned update_period_seconds,
const ProtocolServerMetricsFunc & protocol_server_metrics_func_) const ProtocolServerMetricsFunc & protocol_server_metrics_func_,
bool update_jemalloc_epoch_,
bool update_rss_)
: update_period(update_period_seconds) : update_period(update_period_seconds)
, log(getLogger("AsynchronousMetrics")) , log(getLogger("AsynchronousMetrics"))
, protocol_server_metrics_func(protocol_server_metrics_func_) , protocol_server_metrics_func(protocol_server_metrics_func_)
, update_jemalloc_epoch(update_jemalloc_epoch_)
, update_rss(update_rss_)
{ {
#if defined(OS_LINUX) #if defined(OS_LINUX)
openFileIfExists("/proc/cpuinfo", cpuinfo); openFileIfExists("/proc/cpuinfo", cpuinfo);
@ -411,9 +415,7 @@ Value saveJemallocMetricImpl(
const std::string & jemalloc_full_name, const std::string & jemalloc_full_name,
const std::string & clickhouse_full_name) const std::string & clickhouse_full_name)
{ {
Value value{}; auto value = getJemallocValue<Value>(jemalloc_full_name.c_str());
size_t size = sizeof(value);
mallctl(jemalloc_full_name.c_str(), &value, &size, nullptr, 0);
values[clickhouse_full_name] = AsynchronousMetricValue(value, "An internal metric of the low-level memory allocator (jemalloc). See https://jemalloc.net/jemalloc.3.html"); values[clickhouse_full_name] = AsynchronousMetricValue(value, "An internal metric of the low-level memory allocator (jemalloc). See https://jemalloc.net/jemalloc.3.html");
return value; return value;
} }
@ -768,8 +770,11 @@ void AsynchronousMetrics::update(TimePoint update_time, bool force_update)
// 'epoch' is a special mallctl -- it updates the statistics. Without it, all // 'epoch' is a special mallctl -- it updates the statistics. Without it, all
// the following calls will return stale values. It increments and returns // the following calls will return stale values. It increments and returns
// the current epoch number, which might be useful to log as a sanity check. // the current epoch number, which might be useful to log as a sanity check.
auto epoch = updateJemallocEpoch(); auto epoch = update_jemalloc_epoch ? updateJemallocEpoch() : getJemallocValue<uint64_t>("epoch");
new_values["jemalloc.epoch"] = { epoch, "An internal incremental update number of the statistics of jemalloc (Jason Evans' memory allocator), used in all other `jemalloc` metrics." }; new_values["jemalloc.epoch"]
= {epoch,
"An internal incremental update number of the statistics of jemalloc (Jason Evans' memory allocator), used in all other "
"`jemalloc` metrics."};
// Collect the statistics themselves. // Collect the statistics themselves.
saveJemallocMetric<size_t>(new_values, "allocated"); saveJemallocMetric<size_t>(new_values, "allocated");
@ -782,10 +787,10 @@ void AsynchronousMetrics::update(TimePoint update_time, bool force_update)
saveJemallocMetric<size_t>(new_values, "background_thread.num_threads"); saveJemallocMetric<size_t>(new_values, "background_thread.num_threads");
saveJemallocMetric<uint64_t>(new_values, "background_thread.num_runs"); saveJemallocMetric<uint64_t>(new_values, "background_thread.num_runs");
saveJemallocMetric<uint64_t>(new_values, "background_thread.run_intervals"); saveJemallocMetric<uint64_t>(new_values, "background_thread.run_intervals");
saveJemallocProf<size_t>(new_values, "active"); saveJemallocProf<bool>(new_values, "active");
saveAllArenasMetric<size_t>(new_values, "pactive"); saveAllArenasMetric<size_t>(new_values, "pactive");
[[maybe_unused]] size_t je_malloc_pdirty = saveAllArenasMetric<size_t>(new_values, "pdirty"); saveAllArenasMetric<size_t>(new_values, "pdirty");
[[maybe_unused]] size_t je_malloc_pmuzzy = saveAllArenasMetric<size_t>(new_values, "pmuzzy"); saveAllArenasMetric<size_t>(new_values, "pmuzzy");
saveAllArenasMetric<size_t>(new_values, "dirty_purged"); saveAllArenasMetric<size_t>(new_values, "dirty_purged");
saveAllArenasMetric<size_t>(new_values, "muzzy_purged"); saveAllArenasMetric<size_t>(new_values, "muzzy_purged");
#endif #endif
@ -814,41 +819,8 @@ void AsynchronousMetrics::update(TimePoint update_time, bool force_update)
" It is unspecified whether it includes the per-thread stacks and most of the allocated memory, that is allocated with the 'mmap' system call." " It is unspecified whether it includes the per-thread stacks and most of the allocated memory, that is allocated with the 'mmap' system call."
" This metric exists only for completeness reasons. I recommend to use the `MemoryResident` metric for monitoring."}; " This metric exists only for completeness reasons. I recommend to use the `MemoryResident` metric for monitoring."};
/// We must update the value of total_memory_tracker periodically. if (update_rss)
/// Otherwise it might be calculated incorrectly - it can include a "drift" of memory amount. MemoryTracker::updateRSS(data.resident);
/// See https://github.com/ClickHouse/ClickHouse/issues/10293
{
Int64 amount = total_memory_tracker.get();
Int64 peak = total_memory_tracker.getPeak();
Int64 rss = data.resident;
Int64 free_memory_in_allocator_arenas = 0;
#if USE_JEMALLOC
/// According to jemalloc man, pdirty is:
///
/// Number of pages within unused extents that are potentially
/// dirty, and for which madvise() or similar has not been called.
///
/// So they will be subtracted from RSS to make accounting more
/// accurate, since those pages are not really RSS but a memory
/// that can be used at anytime via jemalloc.
free_memory_in_allocator_arenas = je_malloc_pdirty * getPageSize();
#endif
Int64 difference = rss - amount;
/// Log only if difference is high. This is for convenience. The threshold is arbitrary.
if (difference >= 1048576 || difference <= -1048576)
LOG_TRACE(log,
"MemoryTracking: was {}, peak {}, free memory in arenas {}, will set to {} (RSS), difference: {}",
ReadableSize(amount),
ReadableSize(peak),
ReadableSize(free_memory_in_allocator_arenas),
ReadableSize(rss),
ReadableSize(difference));
MemoryTracker::setRSS(rss, free_memory_in_allocator_arenas);
}
} }
{ {

View File

@ -1,15 +1,14 @@
#pragma once #pragma once
#include <Common/CgroupsMemoryUsageObserver.h>
#include <Common/MemoryStatisticsOS.h> #include <Common/MemoryStatisticsOS.h>
#include <Common/ThreadPool.h> #include <Common/ThreadPool.h>
#include <Common/Stopwatch.h> #include <Common/Stopwatch.h>
#include <IO/ReadBufferFromFile.h> #include <IO/ReadBufferFromFile.h>
#include <condition_variable> #include <condition_variable>
#include <map>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <thread>
#include <vector> #include <vector>
#include <optional> #include <optional>
#include <unordered_map> #include <unordered_map>
@ -69,7 +68,9 @@ public:
AsynchronousMetrics( AsynchronousMetrics(
unsigned update_period_seconds, unsigned update_period_seconds,
const ProtocolServerMetricsFunc & protocol_server_metrics_func_); const ProtocolServerMetricsFunc & protocol_server_metrics_func_,
bool update_jemalloc_epoch_,
bool update_rss_);
virtual ~AsynchronousMetrics(); virtual ~AsynchronousMetrics();
@ -112,6 +113,9 @@ private:
MemoryStatisticsOS memory_stat TSA_GUARDED_BY(data_mutex); MemoryStatisticsOS memory_stat TSA_GUARDED_BY(data_mutex);
#endif #endif
[[maybe_unused]] const bool update_jemalloc_epoch;
[[maybe_unused]] const bool update_rss;
#if defined(OS_LINUX) #if defined(OS_LINUX)
std::optional<ReadBufferFromFilePRead> meminfo TSA_GUARDED_BY(data_mutex); std::optional<ReadBufferFromFilePRead> meminfo TSA_GUARDED_BY(data_mutex);
std::optional<ReadBufferFromFilePRead> loadavg TSA_GUARDED_BY(data_mutex); std::optional<ReadBufferFromFilePRead> loadavg TSA_GUARDED_BY(data_mutex);

View File

@ -14,239 +14,21 @@
#include <fmt/ranges.h> #include <fmt/ranges.h>
#include <cstdint> #include <cstdint>
#include <filesystem>
#include <memory>
#include <optional>
#include "config.h"
#if USE_JEMALLOC
# include <jemalloc/jemalloc.h>
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#endif
using namespace DB; using namespace DB;
namespace fs = std::filesystem;
namespace DB
{
namespace ErrorCodes
{
extern const int FILE_DOESNT_EXIST;
extern const int INCORRECT_DATA;
}
}
namespace
{
/// Format is
/// kernel 5
/// rss 15
/// [...]
using Metrics = std::map<std::string, uint64_t>;
Metrics readAllMetricsFromStatFile(ReadBufferFromFile & buf)
{
Metrics metrics;
while (!buf.eof())
{
std::string current_key;
readStringUntilWhitespace(current_key, buf);
assertChar(' ', buf);
uint64_t value = 0;
readIntText(value, buf);
assertChar('\n', buf);
auto [_, inserted] = metrics.emplace(std::move(current_key), value);
chassert(inserted, "Duplicate keys in stat file");
}
return metrics;
}
uint64_t readMetricFromStatFile(ReadBufferFromFile & buf, const std::string & key)
{
const auto all_metrics = readAllMetricsFromStatFile(buf);
if (const auto it = all_metrics.find(key); it != all_metrics.end())
return it->second;
throw Exception(ErrorCodes::INCORRECT_DATA, "Cannot find '{}' in '{}'", key, buf.getFileName());
}
struct CgroupsV1Reader : ICgroupsReader
{
explicit CgroupsV1Reader(const fs::path & stat_file_dir) : buf(stat_file_dir / "memory.stat") { }
uint64_t readMemoryUsage() override
{
std::lock_guard lock(mutex);
buf.rewind();
return readMetricFromStatFile(buf, "rss");
}
std::string dumpAllStats() override
{
std::lock_guard lock(mutex);
buf.rewind();
return fmt::format("{}", readAllMetricsFromStatFile(buf));
}
private:
std::mutex mutex;
ReadBufferFromFile buf TSA_GUARDED_BY(mutex);
};
struct CgroupsV2Reader : ICgroupsReader
{
explicit CgroupsV2Reader(const fs::path & stat_file_dir)
: current_buf(stat_file_dir / "memory.current"), stat_buf(stat_file_dir / "memory.stat")
{
}
uint64_t readMemoryUsage() override
{
std::lock_guard lock(mutex);
current_buf.rewind();
stat_buf.rewind();
int64_t mem_usage = 0;
/// memory.current contains a single number
/// the reason why we subtract it described here: https://github.com/ClickHouse/ClickHouse/issues/64652#issuecomment-2149630667
readIntText(mem_usage, current_buf);
mem_usage -= readMetricFromStatFile(stat_buf, "inactive_file");
chassert(mem_usage >= 0, "Negative memory usage");
return mem_usage;
}
std::string dumpAllStats() override
{
std::lock_guard lock(mutex);
stat_buf.rewind();
return fmt::format("{}", readAllMetricsFromStatFile(stat_buf));
}
private:
std::mutex mutex;
ReadBufferFromFile current_buf TSA_GUARDED_BY(mutex);
ReadBufferFromFile stat_buf TSA_GUARDED_BY(mutex);
};
/// Caveats:
/// - All of the logic in this file assumes that the current process is the only process in the
/// containing cgroup (or more precisely: the only process with significant memory consumption).
/// If this is not the case, then other processe's memory consumption may affect the internal
/// memory tracker ...
/// - Cgroups v1 and v2 allow nested cgroup hierarchies. As v1 is deprecated for over half a
/// decade and will go away at some point, hierarchical detection is only implemented for v2.
/// - I did not test what happens if a host has v1 and v2 simultaneously enabled. I believe such
/// systems existed only for a short transition period.
std::optional<std::string> getCgroupsV1Path()
{
auto path = default_cgroups_mount / "memory/memory.stat";
if (!fs::exists(path))
return {};
return {default_cgroups_mount / "memory"};
}
std::pair<std::string, CgroupsMemoryUsageObserver::CgroupsVersion> getCgroupsPath()
{
auto v2_path = getCgroupsV2PathContainingFile("memory.current");
if (v2_path.has_value())
return {*v2_path, CgroupsMemoryUsageObserver::CgroupsVersion::V2};
auto v1_path = getCgroupsV1Path();
if (v1_path.has_value())
return {*v1_path, CgroupsMemoryUsageObserver::CgroupsVersion::V1};
throw Exception(ErrorCodes::FILE_DOESNT_EXIST, "Cannot find cgroups v1 or v2 current memory file");
}
}
namespace DB namespace DB
{ {
CgroupsMemoryUsageObserver::CgroupsMemoryUsageObserver(std::chrono::seconds wait_time_) CgroupsMemoryUsageObserver::CgroupsMemoryUsageObserver(std::chrono::seconds wait_time_)
: log(getLogger("CgroupsMemoryUsageObserver")), wait_time(wait_time_) : log(getLogger("CgroupsMemoryUsageObserver")), wait_time(wait_time_)
{ {}
const auto [cgroup_path, version] = getCgroupsPath();
cgroup_reader = createCgroupsReader(version, cgroup_path);
LOG_INFO(
log,
"Will read the current memory usage from '{}' (cgroups version: {}), wait time is {} sec",
cgroup_path,
(version == CgroupsVersion::V1) ? "v1" : "v2",
wait_time.count());
}
CgroupsMemoryUsageObserver::~CgroupsMemoryUsageObserver() CgroupsMemoryUsageObserver::~CgroupsMemoryUsageObserver()
{ {
stopThread(); stopThread();
} }
void CgroupsMemoryUsageObserver::setMemoryUsageLimits(uint64_t hard_limit_, uint64_t soft_limit_)
{
std::lock_guard<std::mutex> limit_lock(limit_mutex);
if (hard_limit_ == hard_limit && soft_limit_ == soft_limit)
return;
hard_limit = hard_limit_;
soft_limit = soft_limit_;
on_hard_limit = [this, hard_limit_](bool up)
{
if (up)
{
LOG_WARNING(log, "Exceeded hard memory limit ({})", ReadableSize(hard_limit_));
/// Update current usage in memory tracker. Also reset free_memory_in_allocator_arenas to zero though we don't know if they are
/// really zero. Trying to avoid OOM ...
MemoryTracker::setRSS(hard_limit_, 0);
}
else
{
LOG_INFO(log, "Dropped below hard memory limit ({})", ReadableSize(hard_limit_));
}
};
on_soft_limit = [this, soft_limit_](bool up)
{
if (up)
{
LOG_WARNING(log, "Exceeded soft memory limit ({})", ReadableSize(soft_limit_));
# if USE_JEMALLOC
LOG_INFO(log, "Purging jemalloc arenas");
mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", nullptr, nullptr, nullptr, 0);
# endif
/// Reset current usage in memory tracker. Expect zero for free_memory_in_allocator_arenas as we just purged them.
uint64_t memory_usage = cgroup_reader->readMemoryUsage();
LOG_TRACE(
log,
"Read current memory usage {} bytes ({}) from cgroups, full available stats: {}",
memory_usage,
ReadableSize(memory_usage),
cgroup_reader->dumpAllStats());
MemoryTracker::setRSS(memory_usage, 0);
LOG_INFO(log, "Purged jemalloc arenas. Current memory usage is {}", ReadableSize(memory_usage));
}
else
{
LOG_INFO(log, "Dropped below soft memory limit ({})", ReadableSize(soft_limit_));
}
};
LOG_INFO(log, "Set new limits, soft limit: {}, hard limit: {}", ReadableSize(soft_limit_), ReadableSize(hard_limit_));
}
void CgroupsMemoryUsageObserver::setOnMemoryAmountAvailableChangedFn(OnMemoryAmountAvailableChangedFn on_memory_amount_available_changed_) void CgroupsMemoryUsageObserver::setOnMemoryAmountAvailableChangedFn(OnMemoryAmountAvailableChangedFn on_memory_amount_available_changed_)
{ {
std::lock_guard<std::mutex> memory_amount_available_changed_lock(memory_amount_available_changed_mutex); std::lock_guard<std::mutex> memory_amount_available_changed_lock(memory_amount_available_changed_mutex);
@ -300,35 +82,6 @@ void CgroupsMemoryUsageObserver::runThread()
std::lock_guard<std::mutex> memory_amount_available_changed_lock(memory_amount_available_changed_mutex); std::lock_guard<std::mutex> memory_amount_available_changed_lock(memory_amount_available_changed_mutex);
on_memory_amount_available_changed(); on_memory_amount_available_changed();
} }
std::lock_guard<std::mutex> limit_lock(limit_mutex);
if (soft_limit > 0 && hard_limit > 0)
{
uint64_t memory_usage = cgroup_reader->readMemoryUsage();
LOG_TRACE(log, "Read current memory usage {} bytes ({}) from cgroups", memory_usage, ReadableSize(memory_usage));
if (memory_usage > hard_limit)
{
if (last_memory_usage <= hard_limit)
on_hard_limit(true);
}
else
{
if (last_memory_usage > hard_limit)
on_hard_limit(false);
}
if (memory_usage > soft_limit)
{
if (last_memory_usage <= soft_limit)
on_soft_limit(true);
}
else
{
if (last_memory_usage > soft_limit)
on_soft_limit(false);
}
last_memory_usage = memory_usage;
}
} }
catch (...) catch (...)
{ {
@ -337,13 +90,6 @@ void CgroupsMemoryUsageObserver::runThread()
} }
} }
std::unique_ptr<ICgroupsReader> createCgroupsReader(CgroupsMemoryUsageObserver::CgroupsVersion version, const fs::path & cgroup_path)
{
if (version == CgroupsMemoryUsageObserver::CgroupsVersion::V2)
return std::make_unique<CgroupsV2Reader>(cgroup_path);
else
return std::make_unique<CgroupsV1Reader>(cgroup_path);
}
} }
#endif #endif

View File

@ -3,53 +3,27 @@
#include <Common/ThreadPool.h> #include <Common/ThreadPool.h>
#include <chrono> #include <chrono>
#include <memory>
#include <mutex> #include <mutex>
namespace DB namespace DB
{ {
struct ICgroupsReader /// Periodically reads the the maximum memory available to the process (which can change due to cgroups settings).
{ /// You can specify a callback to react on changes. The callback typically reloads the configuration, i.e. Server
virtual ~ICgroupsReader() = default; /// or Keeper configuration file. This reloads settings 'max_server_memory_usage' (Server) and 'max_memory_usage_soft_limit'
/// (Keeper) from which various other internal limits are calculated, including the soft and hard limits for (1.).
virtual uint64_t readMemoryUsage() = 0; /// The goal of this is to provide elasticity when the container is scaled-up/scaled-down. The mechanism (polling
/// cgroups) is quite implicit, unfortunately there is currently no better way to communicate memory threshold changes
virtual std::string dumpAllStats() = 0; /// to the database.
};
/// Does two things:
/// 1. Periodically reads the memory usage of the process from Linux cgroups.
/// You can specify soft or hard memory limits:
/// - When the soft memory limit is hit, drop jemalloc cache.
/// - When the hard memory limit is hit, update MemoryTracking metric to throw memory exceptions faster.
/// The goal of this is to avoid that the process hits the maximum allowed memory limit at which there is a good
/// chance that the Limux OOM killer terminates it. All of this is done is because internal memory tracking in
/// ClickHouse can unfortunately under-estimate the actually used memory.
/// 2. Periodically reads the the maximum memory available to the process (which can change due to cgroups settings).
/// You can specify a callback to react on changes. The callback typically reloads the configuration, i.e. Server
/// or Keeper configuration file. This reloads settings 'max_server_memory_usage' (Server) and 'max_memory_usage_soft_limit'
/// (Keeper) from which various other internal limits are calculated, including the soft and hard limits for (1.).
/// The goal of this is to provide elasticity when the container is scaled-up/scaled-down. The mechanism (polling
/// cgroups) is quite implicit, unfortunately there is currently no better way to communicate memory threshold changes
/// to the database.
#if defined(OS_LINUX) #if defined(OS_LINUX)
class CgroupsMemoryUsageObserver class CgroupsMemoryUsageObserver
{ {
public: public:
using OnMemoryLimitFn = std::function<void(bool)>;
using OnMemoryAmountAvailableChangedFn = std::function<void()>; using OnMemoryAmountAvailableChangedFn = std::function<void()>;
enum class CgroupsVersion : uint8_t
{
V1,
V2
};
explicit CgroupsMemoryUsageObserver(std::chrono::seconds wait_time_); explicit CgroupsMemoryUsageObserver(std::chrono::seconds wait_time_);
~CgroupsMemoryUsageObserver(); ~CgroupsMemoryUsageObserver();
void setMemoryUsageLimits(uint64_t hard_limit_, uint64_t soft_limit_);
void setOnMemoryAmountAvailableChangedFn(OnMemoryAmountAvailableChangedFn on_memory_amount_available_changed_); void setOnMemoryAmountAvailableChangedFn(OnMemoryAmountAvailableChangedFn on_memory_amount_available_changed_);
void startThread(); void startThread();
@ -60,32 +34,22 @@ private:
const std::chrono::seconds wait_time; const std::chrono::seconds wait_time;
std::mutex limit_mutex; std::mutex limit_mutex;
size_t hard_limit TSA_GUARDED_BY(limit_mutex) = 0;
size_t soft_limit TSA_GUARDED_BY(limit_mutex) = 0;
OnMemoryLimitFn on_hard_limit TSA_GUARDED_BY(limit_mutex);
OnMemoryLimitFn on_soft_limit TSA_GUARDED_BY(limit_mutex);
std::mutex memory_amount_available_changed_mutex; std::mutex memory_amount_available_changed_mutex;
OnMemoryAmountAvailableChangedFn on_memory_amount_available_changed TSA_GUARDED_BY(memory_amount_available_changed_mutex); OnMemoryAmountAvailableChangedFn on_memory_amount_available_changed TSA_GUARDED_BY(memory_amount_available_changed_mutex);
uint64_t last_memory_usage = 0; /// how much memory does the process use
uint64_t last_available_memory_amount; /// how much memory can the process use uint64_t last_available_memory_amount; /// how much memory can the process use
void stopThread(); void stopThread();
void runThread(); void runThread();
std::unique_ptr<ICgroupsReader> cgroup_reader;
std::mutex thread_mutex; std::mutex thread_mutex;
std::condition_variable cond; std::condition_variable cond;
ThreadFromGlobalPool thread; ThreadFromGlobalPool thread;
bool quit = false; bool quit = false;
}; };
std::unique_ptr<ICgroupsReader>
createCgroupsReader(CgroupsMemoryUsageObserver::CgroupsVersion version, const std::filesystem::path & cgroup_path);
#else #else
class CgroupsMemoryUsageObserver class CgroupsMemoryUsageObserver
{ {

View File

@ -5,7 +5,6 @@
#include <Common/Exception.h> #include <Common/Exception.h>
#include <Common/Stopwatch.h> #include <Common/Stopwatch.h>
#include <Common/logger_useful.h> #include <Common/logger_useful.h>
#include <jemalloc/jemalloc.h>
#define STRINGIFY_HELPER(x) #x #define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x) #define STRINGIFY(x) STRINGIFY_HELPER(x)
@ -26,7 +25,6 @@ namespace ErrorCodes
void purgeJemallocArenas() void purgeJemallocArenas()
{ {
LOG_TRACE(getLogger("SystemJemalloc"), "Purging unused memory");
Stopwatch watch; Stopwatch watch;
mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", nullptr, nullptr, nullptr, 0); mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", nullptr, nullptr, nullptr, 0);
ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurge); ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurge);
@ -46,20 +44,6 @@ void checkJemallocProfilingEnabled()
"set: MALLOC_CONF=background_thread:true,prof:true"); "set: MALLOC_CONF=background_thread:true,prof:true");
} }
template <typename T>
void setJemallocValue(const char * name, T value)
{
T old_value;
size_t old_value_size = sizeof(T);
if (mallctl(name, &old_value, &old_value_size, reinterpret_cast<void*>(&value), sizeof(T)))
{
LOG_WARNING(getLogger("Jemalloc"), "mallctl for {} failed", name);
return;
}
LOG_INFO(getLogger("Jemalloc"), "Value for {} set to {} (from {})", name, value, old_value);
}
void setJemallocProfileActive(bool value) void setJemallocProfileActive(bool value)
{ {
checkJemallocProfilingEnabled(); checkJemallocProfilingEnabled();

View File

@ -5,6 +5,8 @@
#if USE_JEMALLOC #if USE_JEMALLOC
#include <string> #include <string>
#include <Common/logger_useful.h>
#include <jemalloc/jemalloc.h>
namespace DB namespace DB
{ {
@ -21,6 +23,59 @@ void setJemallocBackgroundThreads(bool enabled);
void setJemallocMaxBackgroundThreads(size_t max_threads); void setJemallocMaxBackgroundThreads(size_t max_threads);
template <typename T>
void setJemallocValue(const char * name, T value)
{
T old_value;
size_t old_value_size = sizeof(T);
mallctl(name, &old_value, &old_value_size, reinterpret_cast<void*>(&value), sizeof(T));
LOG_INFO(getLogger("Jemalloc"), "Value for {} set to {} (from {})", name, value, old_value);
}
template <typename T>
T getJemallocValue(const char * name)
{
T value;
size_t value_size = sizeof(T);
mallctl(name, &value, &value_size, nullptr, 0);
return value;
}
/// Each mallctl call consists of string name lookup which can be expensive.
/// This can be avoided by translating name to "Management Information Base" (MIB)
/// and using it in mallctlbymib calls
template <typename T>
struct JemallocMibCache
{
explicit JemallocMibCache(const char * name)
{
mallctlnametomib(name, mib, &mib_length);
}
void setValue(T value)
{
mallctlbymib(mib, mib_length, nullptr, nullptr, reinterpret_cast<void*>(&value), sizeof(T));
}
T getValue()
{
T value;
size_t value_size = sizeof(T);
mallctlbymib(mib, mib_length, &value, &value_size, nullptr, 0);
return value;
}
void run()
{
mallctlbymib(mib, mib_length, nullptr, nullptr, nullptr, 0);
}
private:
static constexpr size_t max_mib_length = 4;
size_t mib[max_mib_length];
size_t mib_length = max_mib_length;
};
} }
#endif #endif

View File

@ -20,13 +20,9 @@
#if USE_JEMALLOC #if USE_JEMALLOC
# include <jemalloc/jemalloc.h> # include <jemalloc/jemalloc.h>
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
#endif #endif
#include <atomic> #include <atomic>
#include <cmath>
#include <random> #include <random>
#include <cstdlib> #include <cstdlib>
#include <string> #include <string>
@ -115,8 +111,6 @@ void AllocationTrace::onFreeImpl(void * ptr, size_t size) const
namespace ProfileEvents namespace ProfileEvents
{ {
extern const Event QueryMemoryLimitExceeded; extern const Event QueryMemoryLimitExceeded;
extern const Event MemoryAllocatorPurge;
extern const Event MemoryAllocatorPurgeTimeMicroseconds;
} }
using namespace std::chrono_literals; using namespace std::chrono_literals;
@ -126,15 +120,13 @@ static constexpr size_t log_peak_memory_usage_every = 1ULL << 30;
MemoryTracker total_memory_tracker(nullptr, VariableContext::Global); MemoryTracker total_memory_tracker(nullptr, VariableContext::Global);
MemoryTracker background_memory_tracker(&total_memory_tracker, VariableContext::User, false); MemoryTracker background_memory_tracker(&total_memory_tracker, VariableContext::User, false);
std::atomic<Int64> MemoryTracker::free_memory_in_allocator_arenas;
MemoryTracker::MemoryTracker(VariableContext level_) : parent(&total_memory_tracker), level(level_) {} MemoryTracker::MemoryTracker(VariableContext level_) : parent(&total_memory_tracker), level(level_) {}
MemoryTracker::MemoryTracker(MemoryTracker * parent_, VariableContext level_) : parent(parent_), level(level_) {} MemoryTracker::MemoryTracker(MemoryTracker * parent_, VariableContext level_) : parent(parent_), level(level_) {}
MemoryTracker::MemoryTracker(MemoryTracker * parent_, VariableContext level_, bool log_peak_memory_usage_in_destructor_) MemoryTracker::MemoryTracker(MemoryTracker * parent_, VariableContext level_, bool log_peak_memory_usage_in_destructor_)
: parent(parent_) : parent(parent_), log_peak_memory_usage_in_destructor(log_peak_memory_usage_in_destructor_), level(level_)
, log_peak_memory_usage_in_destructor(log_peak_memory_usage_in_destructor_) {
, level(level_) }
{}
MemoryTracker::~MemoryTracker() MemoryTracker::~MemoryTracker()
{ {
@ -204,10 +196,14 @@ void MemoryTracker::debugLogBigAllocationWithoutCheck(Int64 size [[maybe_unused]
return; return;
MemoryTrackerBlockerInThread blocker(VariableContext::Global); MemoryTrackerBlockerInThread blocker(VariableContext::Global);
LOG_TEST(getLogger("MemoryTracker"), "Too big allocation ({} bytes) without checking memory limits, " LOG_TEST(
"it may lead to OOM. Stack trace: {}", size, StackTrace().toString()); getLogger("MemoryTracker"),
"Too big allocation ({} bytes) without checking memory limits, "
"it may lead to OOM. Stack trace: {}",
size,
StackTrace().toString());
#else #else
return; /// Avoid trash logging in release builds /// Avoid trash logging in release builds
#endif #endif
} }
@ -228,6 +224,7 @@ AllocationTrace MemoryTracker::allocImpl(Int64 size, bool throw_if_memory_exceed
{ {
/// For global memory tracker always update memory usage. /// For global memory tracker always update memory usage.
amount.fetch_add(size, std::memory_order_relaxed); amount.fetch_add(size, std::memory_order_relaxed);
rss.fetch_add(size, std::memory_order_relaxed);
auto metric_loaded = metric.load(std::memory_order_relaxed); auto metric_loaded = metric.load(std::memory_order_relaxed);
if (metric_loaded != CurrentMetrics::end()) if (metric_loaded != CurrentMetrics::end())
@ -249,6 +246,7 @@ AllocationTrace MemoryTracker::allocImpl(Int64 size, bool throw_if_memory_exceed
* So, we allow over-allocations. * So, we allow over-allocations.
*/ */
Int64 will_be = size ? size + amount.fetch_add(size, std::memory_order_relaxed) : amount.load(std::memory_order_relaxed); Int64 will_be = size ? size + amount.fetch_add(size, std::memory_order_relaxed) : amount.load(std::memory_order_relaxed);
Int64 will_be_rss = size ? size + rss.fetch_add(size, std::memory_order_relaxed) : rss.load(std::memory_order_relaxed);
auto metric_loaded = metric.load(std::memory_order_relaxed); auto metric_loaded = metric.load(std::memory_order_relaxed);
if (metric_loaded != CurrentMetrics::end() && size) if (metric_loaded != CurrentMetrics::end() && size)
@ -275,6 +273,7 @@ AllocationTrace MemoryTracker::allocImpl(Int64 size, bool throw_if_memory_exceed
{ {
/// Revert /// Revert
amount.fetch_sub(size, std::memory_order_relaxed); amount.fetch_sub(size, std::memory_order_relaxed);
rss.fetch_sub(size, std::memory_order_relaxed);
/// Prevent recursion. Exception::ctor -> std::string -> new[] -> MemoryTracker::alloc /// Prevent recursion. Exception::ctor -> std::string -> new[] -> MemoryTracker::alloc
MemoryTrackerBlockerInThread untrack_lock(VariableContext::Global); MemoryTrackerBlockerInThread untrack_lock(VariableContext::Global);
@ -297,33 +296,8 @@ AllocationTrace MemoryTracker::allocImpl(Int64 size, bool throw_if_memory_exceed
} }
} }
Int64 limit_to_check = current_hard_limit; if (unlikely(
current_hard_limit && (will_be > current_hard_limit || (level == VariableContext::Global && will_be_rss > current_hard_limit))))
#if USE_JEMALLOC
if (level == VariableContext::Global && allow_use_jemalloc_memory.load(std::memory_order_relaxed))
{
/// Jemalloc arenas may keep some extra memory.
/// This memory was substucted from RSS to decrease memory drift.
/// In case memory is close to limit, try to pugre the arenas.
/// This is needed to avoid OOM, because some allocations are directly done with mmap.
Int64 current_free_memory_in_allocator_arenas = free_memory_in_allocator_arenas.load(std::memory_order_relaxed);
if (current_free_memory_in_allocator_arenas > 0 && current_hard_limit && current_free_memory_in_allocator_arenas + will_be > current_hard_limit)
{
if (free_memory_in_allocator_arenas.exchange(-current_free_memory_in_allocator_arenas) > 0)
{
Stopwatch watch;
mallctl("arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge", nullptr, nullptr, nullptr, 0);
ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurge);
ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurgeTimeMicroseconds, watch.elapsedMicroseconds());
}
}
limit_to_check += abs(current_free_memory_in_allocator_arenas);
}
#endif
if (unlikely(current_hard_limit && will_be > limit_to_check))
{ {
if (memoryTrackerCanThrow(level, false) && throw_if_memory_exceeded) if (memoryTrackerCanThrow(level, false) && throw_if_memory_exceeded)
{ {
@ -335,6 +309,7 @@ AllocationTrace MemoryTracker::allocImpl(Int64 size, bool throw_if_memory_exceed
{ {
/// Revert /// Revert
amount.fetch_sub(size, std::memory_order_relaxed); amount.fetch_sub(size, std::memory_order_relaxed);
rss.fetch_sub(size, std::memory_order_relaxed);
/// Prevent recursion. Exception::ctor -> std::string -> new[] -> MemoryTracker::alloc /// Prevent recursion. Exception::ctor -> std::string -> new[] -> MemoryTracker::alloc
MemoryTrackerBlockerInThread untrack_lock(VariableContext::Global); MemoryTrackerBlockerInThread untrack_lock(VariableContext::Global);
@ -343,12 +318,13 @@ AllocationTrace MemoryTracker::allocImpl(Int64 size, bool throw_if_memory_exceed
throw DB::Exception( throw DB::Exception(
DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED, DB::ErrorCodes::MEMORY_LIMIT_EXCEEDED,
"Memory limit{}{} exceeded: " "Memory limit{}{} exceeded: "
"would use {} (attempt to allocate chunk of {} bytes), maximum: {}." "would use {} (attempt to allocate chunk of {} bytes), current RSS {}, maximum: {}."
"{}{}", "{}{}",
description ? " " : "", description ? " " : "",
description ? description : "", description ? description : "",
formatReadableSizeWithBinarySuffix(will_be), formatReadableSizeWithBinarySuffix(will_be),
size, size,
formatReadableSizeWithBinarySuffix(rss.load(std::memory_order_relaxed)),
formatReadableSizeWithBinarySuffix(current_hard_limit), formatReadableSizeWithBinarySuffix(current_hard_limit),
overcommit_result == OvercommitResult::NONE ? "" : " OvercommitTracker decision: ", overcommit_result == OvercommitResult::NONE ? "" : " OvercommitTracker decision: ",
toDescription(overcommit_result)); toDescription(overcommit_result));
@ -442,6 +418,7 @@ AllocationTrace MemoryTracker::free(Int64 size, double _sample_probability)
{ {
/// For global memory tracker always update memory usage. /// For global memory tracker always update memory usage.
amount.fetch_sub(size, std::memory_order_relaxed); amount.fetch_sub(size, std::memory_order_relaxed);
rss.fetch_sub(size, std::memory_order_relaxed);
auto metric_loaded = metric.load(std::memory_order_relaxed); auto metric_loaded = metric.load(std::memory_order_relaxed);
if (metric_loaded != CurrentMetrics::end()) if (metric_loaded != CurrentMetrics::end())
CurrentMetrics::sub(metric_loaded, size); CurrentMetrics::sub(metric_loaded, size);
@ -455,7 +432,12 @@ AllocationTrace MemoryTracker::free(Int64 size, double _sample_probability)
} }
Int64 accounted_size = size; Int64 accounted_size = size;
if (level == VariableContext::Thread || level == VariableContext::Global) if (level == VariableContext::Global)
{
amount.fetch_sub(accounted_size, std::memory_order_relaxed);
rss.fetch_sub(accounted_size, std::memory_order_relaxed);
}
else if (level == VariableContext::Thread)
{ {
/// Could become negative if memory allocated in this thread is freed in another one /// Could become negative if memory allocated in this thread is freed in another one
amount.fetch_sub(accounted_size, std::memory_order_relaxed); amount.fetch_sub(accounted_size, std::memory_order_relaxed);
@ -529,21 +511,29 @@ void MemoryTracker::reset()
} }
void MemoryTracker::setRSS(Int64 rss_, Int64 free_memory_in_allocator_arenas_) void MemoryTracker::updateRSS(Int64 rss_)
{ {
Int64 new_amount = rss_; total_memory_tracker.rss.store(rss_, std::memory_order_relaxed);
}
void MemoryTracker::updateAllocated(Int64 allocated_)
{
Int64 new_amount = allocated_;
LOG_INFO(
getLogger("MemoryTracker"),
"Correcting the value of global memory tracker from {} to {}",
ReadableSize(total_memory_tracker.amount.load(std::memory_order_relaxed)),
ReadableSize(allocated_));
total_memory_tracker.amount.store(new_amount, std::memory_order_relaxed); total_memory_tracker.amount.store(new_amount, std::memory_order_relaxed);
free_memory_in_allocator_arenas.store(free_memory_in_allocator_arenas_, std::memory_order_relaxed);
auto metric_loaded = total_memory_tracker.metric.load(std::memory_order_relaxed); auto metric_loaded = total_memory_tracker.metric.load(std::memory_order_relaxed);
if (metric_loaded != CurrentMetrics::end()) if (metric_loaded != CurrentMetrics::end())
CurrentMetrics::set(metric_loaded, new_amount); CurrentMetrics::set(metric_loaded, new_amount);
bool log_memory_usage = true; bool log_memory_usage = true;
total_memory_tracker.updatePeak(rss_, log_memory_usage); total_memory_tracker.updatePeak(new_amount, log_memory_usage);
} }
void MemoryTracker::setSoftLimit(Int64 value) void MemoryTracker::setSoftLimit(Int64 value)
{ {
soft_limit.store(value, std::memory_order_relaxed); soft_limit.store(value, std::memory_order_relaxed);

View File

@ -2,7 +2,6 @@
#include <atomic> #include <atomic>
#include <chrono> #include <chrono>
#include <optional>
#include <base/types.h> #include <base/types.h>
#include <Common/CurrentMetrics.h> #include <Common/CurrentMetrics.h>
#include <Common/VariableContext.h> #include <Common/VariableContext.h>
@ -57,9 +56,8 @@ private:
std::atomic<Int64> soft_limit {0}; std::atomic<Int64> soft_limit {0};
std::atomic<Int64> hard_limit {0}; std::atomic<Int64> hard_limit {0};
std::atomic<Int64> profiler_limit {0}; std::atomic<Int64> profiler_limit {0};
std::atomic_bool allow_use_jemalloc_memory {true};
static std::atomic<Int64> free_memory_in_allocator_arenas; std::atomic<Int64> rss{0};
Int64 profiler_step = 0; Int64 profiler_step = 0;
@ -122,6 +120,11 @@ public:
return amount.load(std::memory_order_relaxed); return amount.load(std::memory_order_relaxed);
} }
Int64 getRSS() const
{
return rss.load(std::memory_order_relaxed);
}
// Merges and mutations may pass memory ownership to other threads thus in the end of execution // Merges and mutations may pass memory ownership to other threads thus in the end of execution
// MemoryTracker for background task may have a non-zero counter. // MemoryTracker for background task may have a non-zero counter.
// This method is intended to fix the counter inside of background_memory_tracker. // This method is intended to fix the counter inside of background_memory_tracker.
@ -154,14 +157,6 @@ public:
{ {
return soft_limit.load(std::memory_order_relaxed); return soft_limit.load(std::memory_order_relaxed);
} }
void setAllowUseJemallocMemory(bool value)
{
allow_use_jemalloc_memory.store(value, std::memory_order_relaxed);
}
bool getAllowUseJemallocMmemory() const
{
return allow_use_jemalloc_memory.load(std::memory_order_relaxed);
}
/** Set limit if it was not set. /** Set limit if it was not set.
* Otherwise, set limit to new value, if new value is greater than previous limit. * Otherwise, set limit to new value, if new value is greater than previous limit.
@ -249,10 +244,9 @@ public:
/// Reset the accumulated data. /// Reset the accumulated data.
void reset(); void reset();
/// Reset current counter to an RSS value. /// update values based on external information (e.g. jemalloc's stat)
/// Jemalloc may have pre-allocated arenas, they are accounted in RSS. static void updateRSS(Int64 rss_);
/// We can free this arenas in case of exception to avoid OOM. static void updateAllocated(Int64 allocated_);
static void setRSS(Int64 rss_, Int64 free_memory_in_allocator_arenas_);
/// Prints info about peak memory consumption into log. /// Prints info about peak memory consumption into log.
void logPeakMemoryUsage(); void logPeakMemoryUsage();

333
src/Common/MemoryWorker.cpp Normal file
View File

@ -0,0 +1,333 @@
#include <Common/MemoryWorker.h>
#include <IO/ReadBufferFromFile.h>
#include <IO/ReadBufferFromFileDescriptor.h>
#include <IO/ReadHelpers.h>
#include <base/cgroupsv2.h>
#include <Common/Jemalloc.h>
#include <Common/MemoryTracker.h>
#include <Common/ProfileEvents.h>
#include <Common/formatReadable.h>
#include <Common/logger_useful.h>
#include <fmt/ranges.h>
#include <filesystem>
#include <optional>
namespace fs = std::filesystem;
namespace ProfileEvents
{
extern const Event MemoryAllocatorPurge;
extern const Event MemoryAllocatorPurgeTimeMicroseconds;
extern const Event MemoryWorkerRun;
extern const Event MemoryWorkerRunElapsedMicroseconds;
}
namespace DB
{
namespace ErrorCodes
{
extern const int FILE_DOESNT_EXIST;
extern const int LOGICAL_ERROR;
}
#if defined(OS_LINUX)
namespace
{
using Metrics = std::map<std::string, uint64_t>;
/// Format is
/// kernel 5
/// rss 15
/// [...]
Metrics readAllMetricsFromStatFile(ReadBufferFromFile & buf)
{
Metrics metrics;
while (!buf.eof())
{
std::string current_key;
readStringUntilWhitespace(current_key, buf);
assertChar(' ', buf);
uint64_t value = 0;
readIntText(value, buf);
assertChar('\n', buf);
auto [_, inserted] = metrics.emplace(std::move(current_key), value);
chassert(inserted, "Duplicate keys in stat file");
}
return metrics;
}
uint64_t readMetricFromStatFile(ReadBufferFromFile & buf, std::string_view key)
{
while (!buf.eof())
{
std::string current_key;
readStringUntilWhitespace(current_key, buf);
if (current_key != key)
{
std::string dummy;
readStringUntilNewlineInto(dummy, buf);
buf.ignore();
continue;
}
assertChar(' ', buf);
uint64_t value = 0;
readIntText(value, buf);
return value;
}
LOG_ERROR(getLogger("CgroupsReader"), "Cannot find '{}' in '{}'", key, buf.getFileName());
return 0;
}
struct CgroupsV1Reader : ICgroupsReader
{
explicit CgroupsV1Reader(const fs::path & stat_file_dir) : buf(stat_file_dir / "memory.stat") { }
uint64_t readMemoryUsage() override
{
std::lock_guard lock(mutex);
buf.rewind();
return readMetricFromStatFile(buf, "rss");
}
std::string dumpAllStats() override
{
std::lock_guard lock(mutex);
buf.rewind();
return fmt::format("{}", readAllMetricsFromStatFile(buf));
}
private:
std::mutex mutex;
ReadBufferFromFile buf TSA_GUARDED_BY(mutex);
};
struct CgroupsV2Reader : ICgroupsReader
{
explicit CgroupsV2Reader(const fs::path & stat_file_dir) : stat_buf(stat_file_dir / "memory.stat") { }
uint64_t readMemoryUsage() override
{
std::lock_guard lock(mutex);
stat_buf.rewind();
return readMetricFromStatFile(stat_buf, "anon");
}
std::string dumpAllStats() override
{
std::lock_guard lock(mutex);
stat_buf.rewind();
return fmt::format("{}", readAllMetricsFromStatFile(stat_buf));
}
private:
std::mutex mutex;
ReadBufferFromFile stat_buf TSA_GUARDED_BY(mutex);
};
/// Caveats:
/// - All of the logic in this file assumes that the current process is the only process in the
/// containing cgroup (or more precisely: the only process with significant memory consumption).
/// If this is not the case, then other processe's memory consumption may affect the internal
/// memory tracker ...
/// - Cgroups v1 and v2 allow nested cgroup hierarchies. As v1 is deprecated for over half a
/// decade and will go away at some point, hierarchical detection is only implemented for v2.
/// - I did not test what happens if a host has v1 and v2 simultaneously enabled. I believe such
/// systems existed only for a short transition period.
std::optional<std::string> getCgroupsV1Path()
{
auto path = default_cgroups_mount / "memory/memory.stat";
if (!fs::exists(path))
return {};
return {default_cgroups_mount / "memory"};
}
std::pair<std::string, ICgroupsReader::CgroupsVersion> getCgroupsPath()
{
auto v2_path = getCgroupsV2PathContainingFile("memory.current");
if (v2_path.has_value())
return {*v2_path, ICgroupsReader::CgroupsVersion::V2};
auto v1_path = getCgroupsV1Path();
if (v1_path.has_value())
return {*v1_path, ICgroupsReader::CgroupsVersion::V1};
throw Exception(ErrorCodes::FILE_DOESNT_EXIST, "Cannot find cgroups v1 or v2 current memory file");
}
}
std::shared_ptr<ICgroupsReader> ICgroupsReader::createCgroupsReader(ICgroupsReader::CgroupsVersion version, const std::filesystem::path & cgroup_path)
{
if (version == CgroupsVersion::V2)
return std::make_shared<CgroupsV2Reader>(cgroup_path);
else
{
chassert(version == CgroupsVersion::V1);
return std::make_shared<CgroupsV1Reader>(cgroup_path);
}
}
#endif
namespace
{
std::string_view sourceToString(MemoryWorker::MemoryUsageSource source)
{
switch (source)
{
case MemoryWorker::MemoryUsageSource::Cgroups: return "Cgroups";
case MemoryWorker::MemoryUsageSource::Jemalloc: return "Jemalloc";
case MemoryWorker::MemoryUsageSource::None: return "None";
}
}
}
/// We try to pick the best possible supported source for reading memory usage.
/// Supported sources in order of priority
/// - reading from cgroups' pseudo-files (fastest and most accurate)
/// - reading jemalloc's resident stat (doesn't take into account allocations that didn't use jemalloc)
/// Also, different tick rates are used because not all options are equally fast
MemoryWorker::MemoryWorker(uint64_t period_ms_)
: log(getLogger("MemoryWorker"))
, period_ms(period_ms_)
{
#if defined(OS_LINUX)
try
{
static constexpr uint64_t cgroups_memory_usage_tick_ms{50};
const auto [cgroup_path, version] = getCgroupsPath();
LOG_INFO(
getLogger("CgroupsReader"),
"Will create cgroup reader from '{}' (cgroups version: {})",
cgroup_path,
(version == ICgroupsReader::CgroupsVersion::V1) ? "v1" : "v2");
cgroups_reader = ICgroupsReader::createCgroupsReader(version, cgroup_path);
source = MemoryUsageSource::Cgroups;
if (period_ms == 0)
period_ms = cgroups_memory_usage_tick_ms;
return;
}
catch (...)
{
tryLogCurrentException(log, "Cannot use cgroups reader");
}
#endif
#if USE_JEMALLOC
static constexpr uint64_t jemalloc_memory_usage_tick_ms{100};
source = MemoryUsageSource::Jemalloc;
if (period_ms == 0)
period_ms = jemalloc_memory_usage_tick_ms;
#endif
}
MemoryWorker::MemoryUsageSource MemoryWorker::getSource()
{
return source;
}
void MemoryWorker::start()
{
if (source == MemoryUsageSource::None)
return;
LOG_INFO(
getLogger("MemoryWorker"),
"Starting background memory thread with period of {}ms, using {} as source",
period_ms,
sourceToString(source));
background_thread = ThreadFromGlobalPool([this] { backgroundThread(); });
}
MemoryWorker::~MemoryWorker()
{
{
std::unique_lock lock(mutex);
shutdown = true;
}
cv.notify_all();
if (background_thread.joinable())
background_thread.join();
}
uint64_t MemoryWorker::getMemoryUsage()
{
switch (source)
{
case MemoryUsageSource::Cgroups:
return cgroups_reader != nullptr ? cgroups_reader->readMemoryUsage() : 0;
case MemoryUsageSource::Jemalloc:
#if USE_JEMALLOC
return resident_mib.getValue();
#else
return 0;
#endif
case MemoryUsageSource::None:
throw DB::Exception(ErrorCodes::LOGICAL_ERROR, "Trying to fetch memory usage while no memory source can be used");
}
}
void MemoryWorker::backgroundThread()
{
std::chrono::milliseconds chrono_period_ms{period_ms};
[[maybe_unused]] bool first_run = true;
std::unique_lock lock(mutex);
while (true)
{
cv.wait_for(lock, chrono_period_ms, [this] { return shutdown; });
if (shutdown)
return;
Stopwatch total_watch;
#if USE_JEMALLOC
if (source == MemoryUsageSource::Jemalloc)
epoch_mib.setValue(0);
#endif
Int64 resident = getMemoryUsage();
MemoryTracker::updateRSS(resident);
#if USE_JEMALLOC
if (resident > total_memory_tracker.getHardLimit())
{
Stopwatch purge_watch;
purge_mib.run();
ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurge);
ProfileEvents::increment(ProfileEvents::MemoryAllocatorPurgeTimeMicroseconds, purge_watch.elapsedMicroseconds());
}
#endif
#if USE_JEMALLOC
if (unlikely(first_run || total_memory_tracker.get() < 0))
{
if (source != MemoryUsageSource::Jemalloc)
epoch_mib.setValue(0);
MemoryTracker::updateAllocated(allocated_mib.getValue());
}
#endif
ProfileEvents::increment(ProfileEvents::MemoryWorkerRun);
ProfileEvents::increment(ProfileEvents::MemoryWorkerRunElapsedMicroseconds, total_watch.elapsedMicroseconds());
first_run = false;
}
}
}

84
src/Common/MemoryWorker.h Normal file
View File

@ -0,0 +1,84 @@
#pragma once
#include <Common/CgroupsMemoryUsageObserver.h>
#include <Common/ThreadPool.h>
#include <Common/Jemalloc.h>
namespace DB
{
struct ICgroupsReader
{
enum class CgroupsVersion : uint8_t
{
V1,
V2
};
#if defined(OS_LINUX)
static std::shared_ptr<ICgroupsReader>
createCgroupsReader(ICgroupsReader::CgroupsVersion version, const std::filesystem::path & cgroup_path);
#endif
virtual ~ICgroupsReader() = default;
virtual uint64_t readMemoryUsage() = 0;
virtual std::string dumpAllStats() = 0;
};
/// Correct MemoryTracker based on external information (e.g. Cgroups or stats.resident from jemalloc)
/// The worker spawns a background thread which periodically reads current resident memory from the source,
/// whose value is sent to global MemoryTracker.
/// It can do additional things like purging jemalloc dirty pages if the current memory usage is higher than global hard limit.
class MemoryWorker
{
public:
explicit MemoryWorker(uint64_t period_ms_);
enum class MemoryUsageSource : uint8_t
{
None,
Cgroups,
Jemalloc
};
MemoryUsageSource getSource();
void start();
~MemoryWorker();
private:
uint64_t getMemoryUsage();
void backgroundThread();
ThreadFromGlobalPool background_thread;
std::mutex mutex;
std::condition_variable cv;
bool shutdown = false;
LoggerPtr log;
uint64_t period_ms;
MemoryUsageSource source{MemoryUsageSource::None};
std::shared_ptr<ICgroupsReader> cgroups_reader;
#if USE_JEMALLOC
JemallocMibCache<uint64_t> epoch_mib{"epoch"};
JemallocMibCache<size_t> resident_mib{"stats.resident"};
JemallocMibCache<size_t> allocated_mib{"stats.allocated"};
#define STRINGIFY_HELPER(x) #x
#define STRINGIFY(x) STRINGIFY_HELPER(x)
JemallocMibCache<size_t> purge_mib{"arena." STRINGIFY(MALLCTL_ARENAS_ALL) ".purge"};
#undef STRINGIFY
#undef STRINGIFY_HELPER
#endif
};
}

View File

@ -122,6 +122,20 @@ public:
return result.entry.isNull() || !result.is_usable || (skip_read_only_replicas && result.is_readonly); return result.entry.isNull() || !result.is_usable || (skip_read_only_replicas && result.is_readonly);
} }
TryResult getValidTryResult(const std::vector<TryResult> & results, bool skip_read_only_replicas) const
{
if (results.empty())
throw DB::Exception(DB::ErrorCodes::ALL_CONNECTION_TRIES_FAILED, "Cannot get any valid connection because all connection tries failed");
auto result = results.front();
if (isTryResultInvalid(result, skip_read_only_replicas))
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR,
"Got an invalid connection result: entry.isNull {}, is_usable {}, is_up_to_date {}, delay {}, is_readonly {}, skip_read_only_replicas {}",
result.entry.isNull(), result.is_usable, result.is_up_to_date, result.delay, result.is_readonly, skip_read_only_replicas);
return result;
}
size_t getPoolSize() const { return nested_pools.size(); } size_t getPoolSize() const { return nested_pools.size(); }
protected: protected:

View File

@ -827,6 +827,9 @@ The server successfully detected this situation and will download merged part fr
M(GWPAsanAllocateSuccess, "Number of successful allocations done by GWPAsan") \ M(GWPAsanAllocateSuccess, "Number of successful allocations done by GWPAsan") \
M(GWPAsanAllocateFailed, "Number of failed allocations done by GWPAsan (i.e. filled pool)") \ M(GWPAsanAllocateFailed, "Number of failed allocations done by GWPAsan (i.e. filled pool)") \
M(GWPAsanFree, "Number of free operations done by GWPAsan") \ M(GWPAsanFree, "Number of free operations done by GWPAsan") \
\
M(MemoryWorkerRun, "Number of runs done by MemoryWorker in background") \
M(MemoryWorkerRunElapsedMicroseconds, "Total time spent by MemoryWorker for background work") \
#ifdef APPLY_FOR_EXTERNAL_EVENTS #ifdef APPLY_FOR_EXTERNAL_EVENTS

View File

@ -67,10 +67,18 @@ std::string SigsegvErrorString(const siginfo_t & info, [[maybe_unused]] const uc
= info.si_addr == nullptr ? "NULL pointer"s : (shouldShowAddress(info.si_addr) ? fmt::format("{}", info.si_addr) : ""s); = info.si_addr == nullptr ? "NULL pointer"s : (shouldShowAddress(info.si_addr) ? fmt::format("{}", info.si_addr) : ""s);
const std::string_view access = const std::string_view access =
#if defined(__x86_64__) && !defined(OS_FREEBSD) && !defined(OS_DARWIN) && !defined(__arm__) && !defined(__powerpc__) #if defined(__arm__)
(context.uc_mcontext.gregs[REG_ERR] & 0x02) ? "write" : "read"; "<not available on ARM>";
#elif defined(__powerpc__)
"<not available on PowerPC>";
#elif defined(OS_DARWIN)
"<not available on Darwin>";
#elif defined(OS_FREEBSD)
"<not available on FreeBSD>";
#elif !defined(__x86_64__)
"<not available>";
#else #else
""; (context.uc_mcontext.gregs[REG_ERR] & 0x02) ? "write" : "read";
#endif #endif
std::string_view message; std::string_view message;

View File

@ -6,7 +6,7 @@
#include <filesystem> #include <filesystem>
#include <IO/WriteBufferFromFile.h> #include <IO/WriteBufferFromFile.h>
#include <Common/CgroupsMemoryUsageObserver.h> #include <Common/MemoryWorker.h>
#include <Common/filesystemHelpers.h> #include <Common/filesystemHelpers.h>
using namespace DB; using namespace DB;
@ -126,7 +126,7 @@ const std::string EXPECTED[2]
"\"workingset_restore_anon\": 0, \"workingset_restore_file\": 0, \"zswap\": 0, \"zswapped\": 0, \"zswpin\": 0, \"zswpout\": 0}"}; "\"workingset_restore_anon\": 0, \"workingset_restore_file\": 0, \"zswap\": 0, \"zswapped\": 0, \"zswpin\": 0, \"zswpout\": 0}"};
class CgroupsMemoryUsageObserverFixture : public ::testing::TestWithParam<CgroupsMemoryUsageObserver::CgroupsVersion> class CgroupsMemoryUsageObserverFixture : public ::testing::TestWithParam<ICgroupsReader::CgroupsVersion>
{ {
void SetUp() override void SetUp() override
{ {
@ -138,7 +138,7 @@ class CgroupsMemoryUsageObserverFixture : public ::testing::TestWithParam<Cgroup
stat_file.write(SAMPLE_FILE[version].data(), SAMPLE_FILE[version].size()); stat_file.write(SAMPLE_FILE[version].data(), SAMPLE_FILE[version].size());
stat_file.sync(); stat_file.sync();
if (GetParam() == CgroupsMemoryUsageObserver::CgroupsVersion::V2) if (GetParam() == ICgroupsReader::CgroupsVersion::V2)
{ {
auto current_file = WriteBufferFromFile(tmp_dir + "/memory.current"); auto current_file = WriteBufferFromFile(tmp_dir + "/memory.current");
current_file.write("29645422592", 11); current_file.write("29645422592", 11);
@ -154,18 +154,18 @@ protected:
TEST_P(CgroupsMemoryUsageObserverFixture, ReadMemoryUsageTest) TEST_P(CgroupsMemoryUsageObserverFixture, ReadMemoryUsageTest)
{ {
const auto version = GetParam(); const auto version = GetParam();
auto reader = createCgroupsReader(version, tmp_dir); auto reader = ICgroupsReader::createCgroupsReader(version, tmp_dir);
ASSERT_EQ( ASSERT_EQ(
reader->readMemoryUsage(), reader->readMemoryUsage(),
version == CgroupsMemoryUsageObserver::CgroupsVersion::V1 ? /* rss from memory.stat */ 2232029184 version == ICgroupsReader::CgroupsVersion::V1 ? /* rss from memory.stat */ 2232029184
: /* value from memory.current - inactive_file */ 20952338432); : /* anon from memory.stat */ 10429399040);
} }
TEST_P(CgroupsMemoryUsageObserverFixture, DumpAllStatsTest) TEST_P(CgroupsMemoryUsageObserverFixture, DumpAllStatsTest)
{ {
const auto version = GetParam(); const auto version = GetParam();
auto reader = createCgroupsReader(version, tmp_dir); auto reader = ICgroupsReader::createCgroupsReader(version, tmp_dir);
ASSERT_EQ(reader->dumpAllStats(), EXPECTED[static_cast<uint8_t>(version)]); ASSERT_EQ(reader->dumpAllStats(), EXPECTED[static_cast<uint8_t>(version)]);
} }
@ -173,6 +173,6 @@ TEST_P(CgroupsMemoryUsageObserverFixture, DumpAllStatsTest)
INSTANTIATE_TEST_SUITE_P( INSTANTIATE_TEST_SUITE_P(
CgroupsMemoryUsageObserverTests, CgroupsMemoryUsageObserverTests,
CgroupsMemoryUsageObserverFixture, CgroupsMemoryUsageObserverFixture,
::testing::Values(CgroupsMemoryUsageObserver::CgroupsVersion::V1, CgroupsMemoryUsageObserver::CgroupsVersion::V2)); ::testing::Values(ICgroupsReader::CgroupsVersion::V1, ICgroupsReader::CgroupsVersion::V2));
#endif #endif

View File

@ -55,10 +55,29 @@ void CompressedWriteBuffer::nextImpl()
out.write(compressed_buffer.data(), compressed_size); out.write(compressed_buffer.data(), compressed_size);
} }
/// Increase buffer size for next data if adaptive buffer size is used and nextImpl was called because of end of buffer.
if (!available() && use_adaptive_buffer_size && memory.size() < adaptive_buffer_max_size)
{
memory.resize(std::min(memory.size() * 2, adaptive_buffer_max_size));
BufferBase::set(memory.data(), memory.size(), 0);
}
} }
CompressedWriteBuffer::CompressedWriteBuffer(WriteBuffer & out_, CompressionCodecPtr codec_, size_t buf_size) void CompressedWriteBuffer::finalizeImpl()
: BufferWithOwnMemory<WriteBuffer>(buf_size), out(out_), codec(std::move(codec_)) {
/// Don't try to resize buffer in nextImpl.
use_adaptive_buffer_size = false;
next();
}
CompressedWriteBuffer::CompressedWriteBuffer(
WriteBuffer & out_, CompressionCodecPtr codec_, size_t buf_size, bool use_adaptive_buffer_size_, size_t adaptive_buffer_initial_size)
: BufferWithOwnMemory<WriteBuffer>(use_adaptive_buffer_size_ ? adaptive_buffer_initial_size : buf_size)
, out(out_)
, codec(std::move(codec_))
, use_adaptive_buffer_size(use_adaptive_buffer_size_)
, adaptive_buffer_max_size(buf_size)
{ {
} }

View File

@ -19,7 +19,9 @@ public:
explicit CompressedWriteBuffer( explicit CompressedWriteBuffer(
WriteBuffer & out_, WriteBuffer & out_,
CompressionCodecPtr codec_ = CompressionCodecFactory::instance().getDefaultCodec(), CompressionCodecPtr codec_ = CompressionCodecFactory::instance().getDefaultCodec(),
size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE); size_t buf_size = DBMS_DEFAULT_BUFFER_SIZE,
bool use_adaptive_buffer_size_ = false,
size_t adaptive_buffer_initial_size = DBMS_DEFAULT_INITIAL_ADAPTIVE_BUFFER_SIZE);
~CompressedWriteBuffer() override; ~CompressedWriteBuffer() override;
@ -45,10 +47,17 @@ public:
private: private:
void nextImpl() override; void nextImpl() override;
void finalizeImpl() override;
WriteBuffer & out; WriteBuffer & out;
CompressionCodecPtr codec; CompressionCodecPtr codec;
/// If true, the size of internal buffer will be exponentially increased up to
/// adaptive_buffer_max_size after each nextImpl call. It can be used to avoid
/// large buffer allocation when actual size of written data is small.
bool use_adaptive_buffer_size;
size_t adaptive_buffer_max_size;
PODArray<char> compressed_buffer; PODArray<char> compressed_buffer;
}; };

View File

@ -114,8 +114,13 @@ void updateKeeperInformation(KeeperDispatcher & keeper_dispatcher, AsynchronousM
} }
KeeperAsynchronousMetrics::KeeperAsynchronousMetrics( KeeperAsynchronousMetrics::KeeperAsynchronousMetrics(
ContextPtr context_, unsigned update_period_seconds, const ProtocolServerMetricsFunc & protocol_server_metrics_func_) ContextPtr context_,
: AsynchronousMetrics(update_period_seconds, protocol_server_metrics_func_), context(std::move(context_)) unsigned update_period_seconds,
const ProtocolServerMetricsFunc & protocol_server_metrics_func_,
bool update_jemalloc_epoch_,
bool update_rss_)
: AsynchronousMetrics(update_period_seconds, protocol_server_metrics_func_, update_jemalloc_epoch_, update_rss_)
, context(std::move(context_))
{ {
} }

View File

@ -13,9 +13,13 @@ class KeeperAsynchronousMetrics : public AsynchronousMetrics
{ {
public: public:
KeeperAsynchronousMetrics( KeeperAsynchronousMetrics(
ContextPtr context_, unsigned update_period_seconds, const ProtocolServerMetricsFunc & protocol_server_metrics_func_); ContextPtr context_,
~KeeperAsynchronousMetrics() override; unsigned update_period_seconds,
const ProtocolServerMetricsFunc & protocol_server_metrics_func_,
bool update_jemalloc_epoch_,
bool update_rss_);
~KeeperAsynchronousMetrics() override;
private: private:
ContextPtr context; ContextPtr context;

View File

@ -148,7 +148,14 @@ void KeeperDispatcher::requestThread()
Int64 mem_soft_limit = keeper_context->getKeeperMemorySoftLimit(); Int64 mem_soft_limit = keeper_context->getKeeperMemorySoftLimit();
if (configuration_and_settings->standalone_keeper && isExceedingMemorySoftLimit() && checkIfRequestIncreaseMem(request.request)) if (configuration_and_settings->standalone_keeper && isExceedingMemorySoftLimit() && checkIfRequestIncreaseMem(request.request))
{ {
LOG_WARNING(log, "Processing requests refused because of max_memory_usage_soft_limit {}, the total used memory is {}, request type is {}", ReadableSize(mem_soft_limit), ReadableSize(total_memory_tracker.get()), request.request->getOpNum()); LOG_WARNING(
log,
"Processing requests refused because of max_memory_usage_soft_limit {}, the total allocated memory is {}, RSS is {}, request type "
"is {}",
ReadableSize(mem_soft_limit),
ReadableSize(total_memory_tracker.get()),
ReadableSize(total_memory_tracker.getRSS()),
request.request->getOpNum());
addErrorResponses({request}, Coordination::Error::ZCONNECTIONLOSS); addErrorResponses({request}, Coordination::Error::ZCONNECTIONLOSS);
continue; continue;
} }

View File

@ -602,7 +602,7 @@ bool KeeperServer::isLeaderAlive() const
bool KeeperServer::isExceedingMemorySoftLimit() const bool KeeperServer::isExceedingMemorySoftLimit() const
{ {
Int64 mem_soft_limit = keeper_context->getKeeperMemorySoftLimit(); Int64 mem_soft_limit = keeper_context->getKeeperMemorySoftLimit();
return mem_soft_limit > 0 && total_memory_tracker.get() >= mem_soft_limit; return mem_soft_limit > 0 && std::max(total_memory_tracker.get(), total_memory_tracker.getRSS()) >= mem_soft_limit;
} }
/// TODO test whether taking failed peer in count /// TODO test whether taking failed peer in count

View File

@ -20,6 +20,9 @@ static constexpr auto DBMS_DEFAULT_POLL_INTERVAL = 10;
/// The size of the I/O buffer by default. /// The size of the I/O buffer by default.
static constexpr auto DBMS_DEFAULT_BUFFER_SIZE = 1048576ULL; static constexpr auto DBMS_DEFAULT_BUFFER_SIZE = 1048576ULL;
/// The initial size of adaptive I/O buffer by default.
static constexpr auto DBMS_DEFAULT_INITIAL_ADAPTIVE_BUFFER_SIZE = 16384ULL;
static constexpr auto PADDING_FOR_SIMD = 64; static constexpr auto PADDING_FOR_SIMD = 64;
/** Which blocks by default read the data (by number of rows). /** Which blocks by default read the data (by number of rows).
@ -40,7 +43,7 @@ static constexpr auto SHOW_CHARS_ON_SYNTAX_ERROR = ptrdiff_t(160);
/// each period reduces the error counter by 2 times /// each period reduces the error counter by 2 times
/// too short a period can cause errors to disappear immediately after creation. /// too short a period can cause errors to disappear immediately after creation.
static constexpr auto DBMS_CONNECTION_POOL_WITH_FAILOVER_DEFAULT_DECREASE_ERROR_PERIOD = 60; static constexpr auto DBMS_CONNECTION_POOL_WITH_FAILOVER_DEFAULT_DECREASE_ERROR_PERIOD = 60;
/// replica error max cap, this is to prevent replica from accumulating too many errors and taking to long to recover. /// replica error max cap, this is to prevent replica from accumulating too many errors and taking too long to recover.
static constexpr auto DBMS_CONNECTION_POOL_WITH_FAILOVER_MAX_ERROR_COUNT = 1000; static constexpr auto DBMS_CONNECTION_POOL_WITH_FAILOVER_MAX_ERROR_COUNT = 1000;
/// The boundary on which the blocks for asynchronous file operations should be aligned. /// The boundary on which the blocks for asynchronous file operations should be aligned.

View File

@ -148,6 +148,7 @@ namespace DB
M(Bool, storage_metadata_write_full_object_key, false, "Write disk metadata files with VERSION_FULL_OBJECT_KEY format", 0) \ M(Bool, storage_metadata_write_full_object_key, false, "Write disk metadata files with VERSION_FULL_OBJECT_KEY format", 0) \
M(UInt64, max_materialized_views_count_for_table, 0, "A limit on the number of materialized views attached to a table.", 0) \ M(UInt64, max_materialized_views_count_for_table, 0, "A limit on the number of materialized views attached to a table.", 0) \
M(UInt32, max_database_replicated_create_table_thread_pool_size, 1, "The number of threads to create tables during replica recovery in DatabaseReplicated. Zero means number of threads equal number of cores.", 0) \ M(UInt32, max_database_replicated_create_table_thread_pool_size, 1, "The number of threads to create tables during replica recovery in DatabaseReplicated. Zero means number of threads equal number of cores.", 0) \
M(Bool, database_replicated_allow_detach_permanently, true, "Allow detaching tables permanently in Replicated databases", 0) \
M(Bool, format_alter_operations_with_parentheses, false, "If enabled, each operation in alter queries will be surrounded with parentheses in formatted queries to make them less ambiguous.", 0) \ M(Bool, format_alter_operations_with_parentheses, false, "If enabled, each operation in alter queries will be surrounded with parentheses in formatted queries to make them less ambiguous.", 0) \
M(String, default_replica_path, "/clickhouse/tables/{uuid}/{shard}", "The path to the table in ZooKeeper", 0) \ M(String, default_replica_path, "/clickhouse/tables/{uuid}/{shard}", "The path to the table in ZooKeeper", 0) \
M(String, default_replica_name, "{replica}", "The replica name in ZooKeeper", 0) \ M(String, default_replica_name, "{replica}", "The replica name in ZooKeeper", 0) \
@ -169,6 +170,7 @@ namespace DB
M(Bool, prepare_system_log_tables_on_startup, false, "If true, ClickHouse creates all configured `system.*_log` tables before the startup. It can be helpful if some startup scripts depend on these tables.", 0) \ M(Bool, prepare_system_log_tables_on_startup, false, "If true, ClickHouse creates all configured `system.*_log` tables before the startup. It can be helpful if some startup scripts depend on these tables.", 0) \
M(Double, gwp_asan_force_sample_probability, 0.0003, "Probability that an allocation from specific places will be sampled by GWP Asan (i.e. PODArray allocations)", 0) \ M(Double, gwp_asan_force_sample_probability, 0.0003, "Probability that an allocation from specific places will be sampled by GWP Asan (i.e. PODArray allocations)", 0) \
M(UInt64, config_reload_interval_ms, 2000, "How often clickhouse will reload config and check for new changes", 0) \ M(UInt64, config_reload_interval_ms, 2000, "How often clickhouse will reload config and check for new changes", 0) \
M(UInt64, memory_worker_period_ms, 0, "Tick period of background memory worker which corrects memory tracker memory usages and cleans up unused pages during higher memory usage. If set to 0, default value will be used depending on the memory usage source", 0) \
M(Bool, disable_insertion_and_mutation, false, "Disable all insert/alter/delete queries. This setting will be enabled if someone needs read-only nodes to prevent insertion and mutation affect reading performance.", 0) M(Bool, disable_insertion_and_mutation, false, "Disable all insert/alter/delete queries. This setting will be enabled if someone needs read-only nodes to prevent insertion and mutation affect reading performance.", 0)
/// If you add a setting which can be updated at runtime, please update 'changeable_settings' map in StorageSystemServerSettings.cpp /// If you add a setting which can be updated at runtime, please update 'changeable_settings' map in StorageSystemServerSettings.cpp

View File

@ -710,7 +710,8 @@ class IColumn;
M(UInt64, max_distributed_depth, 5, "Maximum distributed query depth", 0) \ M(UInt64, max_distributed_depth, 5, "Maximum distributed query depth", 0) \
M(Bool, database_replicated_always_detach_permanently, false, "Execute DETACH TABLE as DETACH TABLE PERMANENTLY if database engine is Replicated", 0) \ M(Bool, database_replicated_always_detach_permanently, false, "Execute DETACH TABLE as DETACH TABLE PERMANENTLY if database engine is Replicated", 0) \
M(Bool, database_replicated_allow_only_replicated_engine, false, "Allow to create only Replicated tables in database with engine Replicated", 0) \ M(Bool, database_replicated_allow_only_replicated_engine, false, "Allow to create only Replicated tables in database with engine Replicated", 0) \
M(Bool, database_replicated_allow_replicated_engine_arguments, true, "Allow to create only Replicated tables in database with engine Replicated with explicit arguments", 0) \ M(UInt64, database_replicated_allow_replicated_engine_arguments, 0, "0 - Don't allow to explicitly specify ZooKeeper path and replica name for *MergeTree tables in Replicated databases. 1 - Allow. 2 - Allow, but ignore the specified path and use default one instead.", 0) \
M(UInt64, database_replicated_allow_explicit_uuid, 0, "0 - Don't allow to explicitly specify UUIDs for tables in Replicated databases. 1 - Allow. 2 - Allow, but ignore the specified UUID and generate a random one instead.", 0) \
M(Bool, database_replicated_allow_heavy_create, false, "Allow long-running DDL queries (CREATE AS SELECT and POPULATE) in Replicated database engine. Note that it can block DDL queue for a long time.", 0) \ M(Bool, database_replicated_allow_heavy_create, false, "Allow long-running DDL queries (CREATE AS SELECT and POPULATE) in Replicated database engine. Note that it can block DDL queue for a long time.", 0) \
M(Bool, cloud_mode, false, "Only available in ClickHouse Cloud", 0) \ M(Bool, cloud_mode, false, "Only available in ClickHouse Cloud", 0) \
M(UInt64, cloud_mode_engine, 1, "Only available in ClickHouse Cloud", 0) \ M(UInt64, cloud_mode_engine, 1, "Only available in ClickHouse Cloud", 0) \

View File

@ -76,12 +76,14 @@ static std::initializer_list<std::pair<ClickHouseVersion, SettingsChangesHistory
{"create_if_not_exists", false, false, "New setting."}, {"create_if_not_exists", false, false, "New setting."},
{"allow_materialized_view_with_bad_select", true, true, "Support (but not enable yet) stricter validation in CREATE MATERIALIZED VIEW"}, {"allow_materialized_view_with_bad_select", true, true, "Support (but not enable yet) stricter validation in CREATE MATERIALIZED VIEW"},
{"output_format_always_quote_identifiers", false, false, "New setting."}, {"output_format_always_quote_identifiers", false, false, "New setting."},
{"output_format_identifier_quoting_style", "Backticks", "Backticks", "New setting."} {"output_format_identifier_quoting_style", "Backticks", "Backticks", "New setting."},
{"database_replicated_allow_replicated_engine_arguments", 1, 0, "Don't allow explicit arguments by default"},
{"database_replicated_allow_explicit_uuid", 0, 0, "Added a new setting to disallow explicitly specifying table UUID"},
} }
}, },
{"24.8", {"24.8",
{ {
{"rows_before_aggregation", true, true, "Provide exact value for rows_before_aggregation statistic, represents the number of rows read before aggregation"}, {"rows_before_aggregation", false, false, "Provide exact value for rows_before_aggregation statistic, represents the number of rows read before aggregation"},
{"restore_replace_external_table_functions_to_null", false, false, "New setting."}, {"restore_replace_external_table_functions_to_null", false, false, "New setting."},
{"restore_replace_external_engines_to_null", false, false, "New setting."}, {"restore_replace_external_engines_to_null", false, false, "New setting."},
{"input_format_json_max_depth", 1000000, 1000, "It was unlimited in previous versions, but that was unsafe."}, {"input_format_json_max_depth", 1000000, 1000, "It was unlimited in previous versions, but that was unsafe."},

View File

@ -420,6 +420,21 @@ bool ISerialization::isEphemeralSubcolumn(const DB::ISerialization::SubstreamPat
return path[last_elem].type == Substream::VariantElementNullMap; return path[last_elem].type == Substream::VariantElementNullMap;
} }
bool ISerialization::isDynamicSubcolumn(const DB::ISerialization::SubstreamPath & path, size_t prefix_len)
{
if (prefix_len == 0 || prefix_len > path.size())
return false;
for (size_t i = 0; i != prefix_len; ++i)
{
if (path[i].type == SubstreamType::DynamicData || path[i].type == SubstreamType::DynamicStructure
|| path[i].type == SubstreamType::ObjectData || path[i].type == SubstreamType::ObjectStructure)
return true;
}
return false;
}
ISerialization::SubstreamData ISerialization::createFromPath(const SubstreamPath & path, size_t prefix_len) ISerialization::SubstreamData ISerialization::createFromPath(const SubstreamPath & path, size_t prefix_len)
{ {
assert(prefix_len <= path.size()); assert(prefix_len <= path.size());

View File

@ -457,6 +457,9 @@ public:
/// for writing/reading data. For example, it's a null-map subcolumn of Variant type (it's always constructed from discriminators);. /// for writing/reading data. For example, it's a null-map subcolumn of Variant type (it's always constructed from discriminators);.
static bool isEphemeralSubcolumn(const SubstreamPath & path, size_t prefix_len); static bool isEphemeralSubcolumn(const SubstreamPath & path, size_t prefix_len);
/// Returns true if stream with specified path corresponds to dynamic subcolumn.
static bool isDynamicSubcolumn(const SubstreamPath & path, size_t prefix_len);
protected: protected:
template <typename State, typename StatePtr> template <typename State, typename StatePtr>
State * checkAndGetState(const StatePtr & state) const; State * checkAndGetState(const StatePtr & state) const;

View File

@ -63,6 +63,7 @@ namespace ErrorCodes
extern const int NO_ACTIVE_REPLICAS; extern const int NO_ACTIVE_REPLICAS;
extern const int CANNOT_GET_REPLICATED_DATABASE_SNAPSHOT; extern const int CANNOT_GET_REPLICATED_DATABASE_SNAPSHOT;
extern const int CANNOT_RESTORE_TABLE; extern const int CANNOT_RESTORE_TABLE;
extern const int SUPPORT_IS_DISABLED;
} }
static constexpr const char * REPLICATED_DATABASE_MARK = "DatabaseReplicated"; static constexpr const char * REPLICATED_DATABASE_MARK = "DatabaseReplicated";
@ -441,7 +442,8 @@ void DatabaseReplicated::tryConnectToZooKeeperAndInitDatabase(LoadingStrictnessL
bool is_create_query = mode == LoadingStrictnessLevel::CREATE; bool is_create_query = mode == LoadingStrictnessLevel::CREATE;
String replica_host_id; String replica_host_id;
if (current_zookeeper->tryGet(replica_path, replica_host_id)) bool replica_exists_in_zk = current_zookeeper->tryGet(replica_path, replica_host_id);
if (replica_exists_in_zk)
{ {
if (replica_host_id == DROPPED_MARK && !is_create_query) if (replica_host_id == DROPPED_MARK && !is_create_query)
{ {
@ -454,7 +456,7 @@ void DatabaseReplicated::tryConnectToZooKeeperAndInitDatabase(LoadingStrictnessL
String host_id = getHostID(getContext(), db_uuid, cluster_auth_info.cluster_secure_connection); String host_id = getHostID(getContext(), db_uuid, cluster_auth_info.cluster_secure_connection);
String host_id_default = getHostID(getContext(), db_uuid, false); String host_id_default = getHostID(getContext(), db_uuid, false);
if (is_create_query || (replica_host_id != host_id && replica_host_id != host_id_default)) if (replica_host_id != host_id && replica_host_id != host_id_default)
{ {
throw Exception( throw Exception(
ErrorCodes::REPLICA_ALREADY_EXISTS, ErrorCodes::REPLICA_ALREADY_EXISTS,
@ -484,13 +486,20 @@ void DatabaseReplicated::tryConnectToZooKeeperAndInitDatabase(LoadingStrictnessL
current_zookeeper->set(replica_path + "/replica_group", replica_group_name, -1); current_zookeeper->set(replica_path + "/replica_group", replica_group_name, -1);
createEmptyLogEntry(current_zookeeper); createEmptyLogEntry(current_zookeeper);
} }
/// Needed to mark all the queries
/// in the range (max log ptr at replica ZooKeeper nodes creation, max log ptr after replica recovery] as successful.
String max_log_ptr_at_creation_str;
if (current_zookeeper->tryGet(replica_path + "/max_log_ptr_at_creation", max_log_ptr_at_creation_str))
max_log_ptr_at_creation = parse<UInt32>(max_log_ptr_at_creation_str);
} }
else if (is_create_query)
if (is_create_query)
{ {
/// Create new replica. Throws if replica with the same name already exists /// Create replica nodes in ZooKeeper. If newly initialized nodes already exist, reuse them.
createReplicaNodesInZooKeeper(current_zookeeper); createReplicaNodesInZooKeeper(current_zookeeper);
} }
else else if (!replica_exists_in_zk)
{ {
/// It's not CREATE query, but replica does not exist. Probably it was dropped. /// It's not CREATE query, but replica does not exist. Probably it was dropped.
/// Do not create anything, continue as readonly. /// Do not create anything, continue as readonly.
@ -606,37 +615,84 @@ void DatabaseReplicated::createReplicaNodesInZooKeeper(const zkutil::ZooKeeperPt
"already contains some data and it does not look like Replicated database path.", zookeeper_path); "already contains some data and it does not look like Replicated database path.", zookeeper_path);
/// Write host name to replica_path, it will protect from multiple replicas with the same name /// Write host name to replica_path, it will protect from multiple replicas with the same name
auto host_id = getHostID(getContext(), db_uuid, cluster_auth_info.cluster_secure_connection); const auto host_id = getHostID(getContext(), db_uuid, cluster_auth_info.cluster_secure_connection);
const std::vector<String> check_paths = {
replica_path,
replica_path + "/replica_group",
replica_path + "/digest",
};
bool nodes_exist = true;
auto check_responses = current_zookeeper->tryGet(check_paths);
for (size_t i = 0; i < check_responses.size(); ++i)
{
const auto response = check_responses[i];
if (response.error == Coordination::Error::ZNONODE)
{
nodes_exist = false;
break;
} else if (response.error != Coordination::Error::ZOK)
{
throw zkutil::KeeperException::fromPath(response.error, check_paths[i]);
}
}
if (nodes_exist)
{
const std::vector<String> expected_data = {
host_id,
replica_group_name,
"0",
};
for (size_t i = 0; i != expected_data.size(); ++i)
{
if (check_responses[i].data != expected_data[i])
{
throw Exception(
ErrorCodes::REPLICA_ALREADY_EXISTS,
"Replica node {} in ZooKeeper already exists and contains unexpected value: {}",
quoteString(check_paths[i]), quoteString(check_responses[i].data));
}
}
LOG_DEBUG(log, "Newly initialized replica nodes found in ZooKeeper, reusing them");
createEmptyLogEntry(current_zookeeper);
return;
}
for (int attempts = 10; attempts > 0; --attempts) for (int attempts = 10; attempts > 0; --attempts)
{ {
Coordination::Stat stat; Coordination::Stat stat;
String max_log_ptr_str = current_zookeeper->get(zookeeper_path + "/max_log_ptr", &stat); const String max_log_ptr_str = current_zookeeper->get(zookeeper_path + "/max_log_ptr", &stat);
Coordination::Requests ops; const Coordination::Requests ops = {
ops.emplace_back(zkutil::makeCreateRequest(replica_path, host_id, zkutil::CreateMode::Persistent)); zkutil::makeCreateRequest(replica_path, host_id, zkutil::CreateMode::Persistent),
ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/log_ptr", "0", zkutil::CreateMode::Persistent)); zkutil::makeCreateRequest(replica_path + "/log_ptr", "0", zkutil::CreateMode::Persistent),
ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/digest", "0", zkutil::CreateMode::Persistent)); zkutil::makeCreateRequest(replica_path + "/digest", "0", zkutil::CreateMode::Persistent),
ops.emplace_back(zkutil::makeCreateRequest(replica_path + "/replica_group", replica_group_name, zkutil::CreateMode::Persistent)); zkutil::makeCreateRequest(replica_path + "/replica_group", replica_group_name, zkutil::CreateMode::Persistent),
/// In addition to creating the replica nodes, we record the max_log_ptr at the instant where
/// we declared ourself as an existing replica. We'll need this during recoverLostReplica to /// Previously, this method was not idempotent and max_log_ptr_at_creation could be stored in memory.
/// notify other nodes that issued new queries while this node was recovering. /// we need to store max_log_ptr_at_creation in ZooKeeper to make this method idempotent during replica creation.
ops.emplace_back(zkutil::makeCheckRequest(zookeeper_path + "/max_log_ptr", stat.version)); zkutil::makeCreateRequest(replica_path + "/max_log_ptr_at_creation", max_log_ptr_str, zkutil::CreateMode::Persistent),
zkutil::makeCheckRequest(zookeeper_path + "/max_log_ptr", stat.version),
};
Coordination::Responses ops_responses;
const auto code = current_zookeeper->tryMulti(ops, ops_responses);
Coordination::Responses responses;
const auto code = current_zookeeper->tryMulti(ops, responses);
if (code == Coordination::Error::ZOK) if (code == Coordination::Error::ZOK)
{ {
max_log_ptr_at_creation = parse<UInt32>(max_log_ptr_str); max_log_ptr_at_creation = parse<UInt32>(max_log_ptr_str);
break; createEmptyLogEntry(current_zookeeper);
return;
} }
else if (code == Coordination::Error::ZNODEEXISTS || attempts == 1)
if (attempts == 1)
{ {
/// If its our last attempt, or if the replica already exists, fail immediately. zkutil::KeeperMultiException::check(code, ops, ops_responses);
zkutil::KeeperMultiException::check(code, ops, responses);
} }
} }
createEmptyLogEntry(current_zookeeper);
} }
void DatabaseReplicated::beforeLoadingMetadata(ContextMutablePtr context_, LoadingStrictnessLevel mode) void DatabaseReplicated::beforeLoadingMetadata(ContextMutablePtr context_, LoadingStrictnessLevel mode)
@ -852,18 +908,6 @@ void DatabaseReplicated::checkTableEngine(const ASTCreateQuery & query, ASTStora
bool maybe_replica_macros = info.expanded_other; bool maybe_replica_macros = info.expanded_other;
bool enable_functional_tests_helper = getContext()->getConfigRef().has("_functional_tests_helper_database_replicated_replace_args_macros"); bool enable_functional_tests_helper = getContext()->getConfigRef().has("_functional_tests_helper_database_replicated_replace_args_macros");
if (!enable_functional_tests_helper)
{
if (query_context->getSettingsRef().database_replicated_allow_replicated_engine_arguments)
LOG_WARNING(log, "It's not recommended to explicitly specify zookeeper_path and replica_name in ReplicatedMergeTree arguments");
else
throw Exception(ErrorCodes::INCORRECT_QUERY,
"It's not allowed to specify explicit zookeeper_path and replica_name "
"for ReplicatedMergeTree arguments in Replicated database. If you really want to "
"specify them explicitly, enable setting "
"database_replicated_allow_replicated_engine_arguments.");
}
if (maybe_shard_macros && maybe_replica_macros) if (maybe_shard_macros && maybe_replica_macros)
return; return;
@ -876,7 +920,9 @@ void DatabaseReplicated::checkTableEngine(const ASTCreateQuery & query, ASTStora
return; return;
} }
throw Exception(ErrorCodes::INCORRECT_QUERY, /// We will replace it with default arguments if the setting is 2
if (query_context->getSettingsRef().database_replicated_allow_replicated_engine_arguments != 2)
throw Exception(ErrorCodes::INCORRECT_QUERY,
"Explicit zookeeper_path and replica_name are specified in ReplicatedMergeTree arguments. " "Explicit zookeeper_path and replica_name are specified in ReplicatedMergeTree arguments. "
"If you really want to specify it explicitly, then you should use some macros " "If you really want to specify it explicitly, then you should use some macros "
"to distinguish different shards and replicas"); "to distinguish different shards and replicas");
@ -1145,6 +1191,9 @@ void DatabaseReplicated::recoverLostReplica(const ZooKeeperPtr & current_zookeep
/// so we need to allow experimental features that can be used in a CREATE query /// so we need to allow experimental features that can be used in a CREATE query
enableAllExperimentalSettings(query_context); enableAllExperimentalSettings(query_context);
query_context->setSetting("database_replicated_allow_explicit_uuid", 3);
query_context->setSetting("database_replicated_allow_replicated_engine_arguments", 3);
auto txn = std::make_shared<ZooKeeperMetadataTransaction>(current_zookeeper, zookeeper_path, false, ""); auto txn = std::make_shared<ZooKeeperMetadataTransaction>(current_zookeeper, zookeeper_path, false, "");
query_context->initZooKeeperMetadataTransaction(txn); query_context->initZooKeeperMetadataTransaction(txn);
return query_context; return query_context;
@ -1693,6 +1742,9 @@ void DatabaseReplicated::detachTablePermanently(ContextPtr local_context, const
{ {
waitDatabaseStarted(); waitDatabaseStarted();
if (!local_context->getServerSettings().database_replicated_allow_detach_permanently)
throw Exception(ErrorCodes::SUPPORT_IS_DISABLED, "Support for DETACH TABLE PERMANENTLY is disabled");
auto txn = local_context->getZooKeeperMetadataTransaction(); auto txn = local_context->getZooKeeperMetadataTransaction();
assert(!ddl_worker->isCurrentlyActive() || txn); assert(!ddl_worker->isCurrentlyActive() || txn);
if (txn && txn->isInitialQuery()) if (txn && txn->isInitialQuery())

View File

@ -339,7 +339,15 @@ DiskLocal::writeFile(const String & path, size_t buf_size, WriteMode mode, const
{ {
int flags = (mode == WriteMode::Append) ? (O_APPEND | O_CREAT | O_WRONLY) : -1; int flags = (mode == WriteMode::Append) ? (O_APPEND | O_CREAT | O_WRONLY) : -1;
return std::make_unique<WriteBufferFromFile>( return std::make_unique<WriteBufferFromFile>(
fs::path(disk_path) / path, buf_size, flags, settings.local_throttler); fs::path(disk_path) / path,
buf_size,
flags,
settings.local_throttler,
0666,
nullptr,
0,
settings.use_adaptive_write_buffer,
settings.adaptive_write_buffer_initial_size);
} }
std::vector<String> DiskLocal::getBlobPath(const String & path) const std::vector<String> DiskLocal::getBlobPath(const String & path) const

View File

@ -59,7 +59,7 @@ WriteBufferFromAzureBlobStorage::WriteBufferFromAzureBlobStorage(
const WriteSettings & write_settings_, const WriteSettings & write_settings_,
std::shared_ptr<const AzureBlobStorage::RequestSettings> settings_, std::shared_ptr<const AzureBlobStorage::RequestSettings> settings_,
ThreadPoolCallbackRunnerUnsafe<void> schedule_) ThreadPoolCallbackRunnerUnsafe<void> schedule_)
: WriteBufferFromFileBase(buf_size_, nullptr, 0) : WriteBufferFromFileBase(std::min(buf_size_, static_cast<size_t>(DBMS_DEFAULT_BUFFER_SIZE)), nullptr, 0)
, log(getLogger("WriteBufferFromAzureBlobStorage")) , log(getLogger("WriteBufferFromAzureBlobStorage"))
, buffer_allocation_policy(createBufferAllocationPolicy(*settings_)) , buffer_allocation_policy(createBufferAllocationPolicy(*settings_))
, max_single_part_upload_size(settings_->max_single_part_upload_size) , max_single_part_upload_size(settings_->max_single_part_upload_size)
@ -244,11 +244,21 @@ void WriteBufferFromAzureBlobStorage::allocateBuffer()
buffer_allocation_policy->nextBuffer(); buffer_allocation_policy->nextBuffer();
chassert(0 == hidden_size); chassert(0 == hidden_size);
auto size = buffer_allocation_policy->getBufferSize(); /// First buffer was already allocated in BufferWithOwnMemory constructor with buffer size provided in constructor.
/// It will be reallocated in subsequent nextImpl calls up to the desired buffer size from buffer_allocation_policy.
if (buffer_allocation_policy->getBufferNumber() == 1) if (buffer_allocation_policy->getBufferNumber() == 1)
size = std::min(size_t(DBMS_DEFAULT_BUFFER_SIZE), size); {
/// Reduce memory size if initial size was larger then desired size from buffer_allocation_policy.
/// Usually it doesn't happen but we have it in unit tests.
if (memory.size() > buffer_allocation_policy->getBufferSize())
{
memory.resize(buffer_allocation_policy->getBufferSize());
WriteBuffer::set(memory.data(), memory.size());
}
return;
}
auto size = buffer_allocation_policy->getBufferSize();
memory = Memory(size); memory = Memory(size);
WriteBuffer::set(memory.data(), memory.size()); WriteBuffer::set(memory.data(), memory.size());
} }

View File

@ -289,7 +289,7 @@ std::unique_ptr<WriteBufferFromFileBase> AzureObjectStorage::writeObject( /// NO
return std::make_unique<WriteBufferFromAzureBlobStorage>( return std::make_unique<WriteBufferFromAzureBlobStorage>(
client.get(), client.get(),
object.remote_path, object.remote_path,
buf_size, write_settings.use_adaptive_write_buffer ? write_settings.adaptive_write_buffer_initial_size : buf_size,
patchSettings(write_settings), patchSettings(write_settings),
settings.get(), settings.get(),
std::move(scheduler)); std::move(scheduler));

View File

@ -282,7 +282,7 @@ std::unique_ptr<WriteBufferFromFileBase> S3ObjectStorage::writeObject( /// NOLIN
client.get(), client.get(),
uri.bucket, uri.bucket,
object.remote_path, object.remote_path,
buf_size, write_settings.use_adaptive_write_buffer ? write_settings.adaptive_write_buffer_initial_size : buf_size,
request_settings, request_settings,
std::move(blob_storage_log), std::move(blob_storage_log),
attributes, attributes,

View File

@ -302,8 +302,12 @@ DataTypePtr tryInferDataTypeByEscapingRule(const String & field, const FormatSet
/// Try to determine the type of value inside quotes /// Try to determine the type of value inside quotes
auto type = tryInferDataTypeForSingleField(data, format_settings); auto type = tryInferDataTypeForSingleField(data, format_settings);
/// If we couldn't infer any type or it's a number and csv.try_infer_numbers_from_strings = 0, we determine it as a string. /// Return String type if one of the following conditions apply
if (!type || (format_settings.csv.try_infer_strings_from_quoted_tuples && isTuple(type)) || (!format_settings.csv.try_infer_numbers_from_strings && isNumber(type))) /// - we couldn't infer any type
/// - it's a number and csv.try_infer_numbers_from_strings = 0
/// - it's a tuple and try_infer_strings_from_quoted_tuples = 0
/// - it's a Bool type (we don't allow reading bool values from strings)
if (!type || (format_settings.csv.try_infer_strings_from_quoted_tuples && isTuple(type)) || (!format_settings.csv.try_infer_numbers_from_strings && isNumber(type)) || isBool(type))
return std::make_shared<DataTypeString>(); return std::make_shared<DataTypeString>();
return type; return type;

View File

@ -483,6 +483,33 @@ namespace JSONUtils
writeArrayEnd(out, 1); writeArrayEnd(out, 1);
} }
void writeCompactMetadata(const Names & names, const DataTypes & types, const FormatSettings & settings, WriteBuffer & out)
{
writeCompactArrayStart(out, 0, "meta");
for (size_t i = 0; i < names.size(); ++i)
{
writeCompactObjectStart(out);
writeTitle("name", out, 0, "");
/// The field names are pre-escaped to be put into JSON string literal.
writeChar('"', out);
writeString(names[i], out);
writeChar('"', out);
writeFieldCompactDelimiter(out);
writeTitle("type", out, 0, "");
writeJSONString(types[i]->getName(), out, settings);
writeCompactObjectEnd(out);
if (i + 1 < names.size())
writeFieldCompactDelimiter(out);
}
writeCompactArrayEnd(out);
}
void writeAdditionalInfo( void writeAdditionalInfo(
size_t rows, size_t rows,
size_t rows_before_limit, size_t rows_before_limit,
@ -530,6 +557,45 @@ namespace JSONUtils
} }
} }
void writeCompactAdditionalInfo(
size_t rows,
size_t rows_before_limit,
bool applied_limit,
const Stopwatch & watch,
const Progress & progress,
bool write_statistics,
WriteBuffer & out)
{
writeCompactObjectStart(out);
writeCompactObjectStart(out, 0, "statistics");
writeTitle("rows", out, 0, "");
writeIntText(rows, out);
if (applied_limit)
{
writeFieldCompactDelimiter(out);
writeTitle("rows_before_limit_at_least", out, 0, "");
writeIntText(rows_before_limit, out);
}
if (write_statistics)
{
writeFieldCompactDelimiter(out);
writeTitle("elapsed", out, 0, "");
writeText(watch.elapsedSeconds(), out);
writeFieldCompactDelimiter(out);
writeTitle("rows_read", out, 0, "");
writeText(progress.read_rows.load(), out);
writeFieldCompactDelimiter(out);
writeTitle("bytes_read", out, 0, "");
writeText(progress.read_bytes.load(), out);
}
writeCompactObjectEnd(out);
writeCompactObjectEnd(out);
}
void writeException(const String & exception_message, WriteBuffer & out, const FormatSettings & settings, size_t indent) void writeException(const String & exception_message, WriteBuffer & out, const FormatSettings & settings, size_t indent)
{ {
writeTitle("exception", out, indent, " "); writeTitle("exception", out, indent, " ");

View File

@ -99,6 +99,7 @@ namespace JSONUtils
WriteBuffer & out); WriteBuffer & out);
void writeMetadata(const Names & names, const DataTypes & types, const FormatSettings & settings, WriteBuffer & out); void writeMetadata(const Names & names, const DataTypes & types, const FormatSettings & settings, WriteBuffer & out);
void writeCompactMetadata(const Names & names, const DataTypes & types, const FormatSettings & settings, WriteBuffer & out);
void writeAdditionalInfo( void writeAdditionalInfo(
size_t rows, size_t rows,
@ -111,6 +112,15 @@ namespace JSONUtils
bool write_statistics, bool write_statistics,
WriteBuffer & out); WriteBuffer & out);
void writeCompactAdditionalInfo(
size_t rows,
size_t rows_before_limit,
bool applied_limit,
const Stopwatch & watch,
const Progress & progress,
bool write_statistics,
WriteBuffer & out);
void writeException(const String & exception_message, WriteBuffer & out, const FormatSettings & settings, size_t indent = 0); void writeException(const String & exception_message, WriteBuffer & out, const FormatSettings & settings, size_t indent = 0);
void skipColon(ReadBuffer & in); void skipColon(ReadBuffer & in);

View File

@ -95,6 +95,7 @@ void registerOutputFormatMarkdown(FormatFactory & factory);
void registerOutputFormatPostgreSQLWire(FormatFactory & factory); void registerOutputFormatPostgreSQLWire(FormatFactory & factory);
void registerOutputFormatPrometheus(FormatFactory & factory); void registerOutputFormatPrometheus(FormatFactory & factory);
void registerOutputFormatSQLInsert(FormatFactory & factory); void registerOutputFormatSQLInsert(FormatFactory & factory);
void registerOutputFormatJSONCompactWithProgress(FormatFactory & factory);
/// Input only formats. /// Input only formats.
@ -242,6 +243,7 @@ void registerFormats()
registerOutputFormatCapnProto(factory); registerOutputFormatCapnProto(factory);
registerOutputFormatPrometheus(factory); registerOutputFormatPrometheus(factory);
registerOutputFormatSQLInsert(factory); registerOutputFormatSQLInsert(factory);
registerOutputFormatJSONCompactWithProgress(factory);
registerInputFormatRegexp(factory); registerInputFormatRegexp(factory);
registerInputFormatJSONAsString(factory); registerInputFormatJSONAsString(factory);

View File

@ -492,7 +492,7 @@ struct ToStartOfInterval<IntervalKind::Kind::Nanosecond>
{ {
throwDateTimeIsNotSupported(TO_START_OF_INTERVAL_NAME); throwDateTimeIsNotSupported(TO_START_OF_INTERVAL_NAME);
} }
static Int64 execute(Int64 t, Int64 nanoseconds, const DateLUTImpl &, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 nanoseconds, const DateLUTImpl &, Int64 scale_multiplier, Int64 /*origin*/ = 0)
{ {
if (scale_multiplier < 1000000000) if (scale_multiplier < 1000000000)
{ {
@ -527,7 +527,7 @@ struct ToStartOfInterval<IntervalKind::Kind::Microsecond>
{ {
throwDateTimeIsNotSupported(TO_START_OF_INTERVAL_NAME); throwDateTimeIsNotSupported(TO_START_OF_INTERVAL_NAME);
} }
static Int64 execute(Int64 t, Int64 microseconds, const DateLUTImpl &, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 microseconds, const DateLUTImpl &, Int64 scale_multiplier, Int64 /*origin*/ = 0)
{ {
if (scale_multiplier < 1000000) if (scale_multiplier < 1000000)
{ {
@ -570,7 +570,7 @@ struct ToStartOfInterval<IntervalKind::Kind::Millisecond>
{ {
throwDateTimeIsNotSupported(TO_START_OF_INTERVAL_NAME); throwDateTimeIsNotSupported(TO_START_OF_INTERVAL_NAME);
} }
static Int64 execute(Int64 t, Int64 milliseconds, const DateLUTImpl &, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 milliseconds, const DateLUTImpl &, Int64 scale_multiplier, Int64 /*origin*/ = 0)
{ {
if (scale_multiplier < 1000) if (scale_multiplier < 1000)
{ {
@ -613,7 +613,7 @@ struct ToStartOfInterval<IntervalKind::Kind::Second>
{ {
return time_zone.toStartOfSecondInterval(t, seconds); return time_zone.toStartOfSecondInterval(t, seconds);
} }
static Int64 execute(Int64 t, Int64 seconds, const DateLUTImpl & time_zone, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 seconds, const DateLUTImpl & time_zone, Int64 scale_multiplier, Int64 /*origin*/ = 0)
{ {
return time_zone.toStartOfSecondInterval(t / scale_multiplier, seconds); return time_zone.toStartOfSecondInterval(t / scale_multiplier, seconds);
} }
@ -634,7 +634,7 @@ struct ToStartOfInterval<IntervalKind::Kind::Minute>
{ {
return time_zone.toStartOfMinuteInterval(t, minutes); return time_zone.toStartOfMinuteInterval(t, minutes);
} }
static Int64 execute(Int64 t, Int64 minutes, const DateLUTImpl & time_zone, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 minutes, const DateLUTImpl & time_zone, Int64 scale_multiplier, Int64 /*origin*/ = 0)
{ {
return time_zone.toStartOfMinuteInterval(t / scale_multiplier, minutes); return time_zone.toStartOfMinuteInterval(t / scale_multiplier, minutes);
} }
@ -655,7 +655,7 @@ struct ToStartOfInterval<IntervalKind::Kind::Hour>
{ {
return time_zone.toStartOfHourInterval(t, hours); return time_zone.toStartOfHourInterval(t, hours);
} }
static Int64 execute(Int64 t, Int64 hours, const DateLUTImpl & time_zone, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 hours, const DateLUTImpl & time_zone, Int64 scale_multiplier, Int64 /*origin*/ = 0)
{ {
return time_zone.toStartOfHourInterval(t / scale_multiplier, hours); return time_zone.toStartOfHourInterval(t / scale_multiplier, hours);
} }
@ -676,7 +676,7 @@ struct ToStartOfInterval<IntervalKind::Kind::Day>
{ {
return static_cast<UInt32>(time_zone.toStartOfDayInterval(time_zone.toDayNum(t), days)); return static_cast<UInt32>(time_zone.toStartOfDayInterval(time_zone.toDayNum(t), days));
} }
static Int64 execute(Int64 t, Int64 days, const DateLUTImpl & time_zone, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 days, const DateLUTImpl & time_zone, Int64 scale_multiplier, Int64 /*origin*/ = 0)
{ {
return time_zone.toStartOfDayInterval(time_zone.toDayNum(t / scale_multiplier), days); return time_zone.toStartOfDayInterval(time_zone.toDayNum(t / scale_multiplier), days);
} }
@ -697,9 +697,13 @@ struct ToStartOfInterval<IntervalKind::Kind::Week>
{ {
return time_zone.toStartOfWeekInterval(time_zone.toDayNum(t), weeks); return time_zone.toStartOfWeekInterval(time_zone.toDayNum(t), weeks);
} }
static UInt16 execute(Int64 t, Int64 weeks, const DateLUTImpl & time_zone, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 weeks, const DateLUTImpl & time_zone, Int64 scale_multiplier, Int64 origin = 0)
{ {
return time_zone.toStartOfWeekInterval(time_zone.toDayNum(t / scale_multiplier), weeks); if (origin == 0)
return time_zone.toStartOfWeekInterval(time_zone.toDayNum(t / scale_multiplier), weeks);
else
return ToStartOfInterval<IntervalKind::Kind::Day>::execute(t, weeks * 7, time_zone, scale_multiplier, origin);
} }
}; };
@ -718,9 +722,23 @@ struct ToStartOfInterval<IntervalKind::Kind::Month>
{ {
return time_zone.toStartOfMonthInterval(time_zone.toDayNum(t), months); return time_zone.toStartOfMonthInterval(time_zone.toDayNum(t), months);
} }
static UInt16 execute(Int64 t, Int64 months, const DateLUTImpl & time_zone, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 months, const DateLUTImpl & time_zone, Int64 scale_multiplier, Int64 origin = 0)
{ {
return time_zone.toStartOfMonthInterval(time_zone.toDayNum(t / scale_multiplier), months); const Int64 scaled_time = t / scale_multiplier;
if (origin == 0)
return time_zone.toStartOfMonthInterval(time_zone.toDayNum(scaled_time), months);
else
{
const Int64 scaled_origin = origin / scale_multiplier;
const Int64 days = time_zone.toDayOfMonth(scaled_time + scaled_origin) - time_zone.toDayOfMonth(scaled_origin);
Int64 months_to_add = time_zone.toMonth(scaled_time + scaled_origin) - time_zone.toMonth(scaled_origin);
const Int64 years = time_zone.toYear(scaled_time + scaled_origin) - time_zone.toYear(scaled_origin);
months_to_add = days < 0 ? months_to_add - 1 : months_to_add;
months_to_add += years * 12;
Int64 month_multiplier = (months_to_add / months) * months;
return (time_zone.addMonths(time_zone.toDate(scaled_origin), month_multiplier) - time_zone.toDate(scaled_origin));
}
} }
}; };
@ -739,9 +757,12 @@ struct ToStartOfInterval<IntervalKind::Kind::Quarter>
{ {
return time_zone.toStartOfQuarterInterval(time_zone.toDayNum(t), quarters); return time_zone.toStartOfQuarterInterval(time_zone.toDayNum(t), quarters);
} }
static UInt16 execute(Int64 t, Int64 quarters, const DateLUTImpl & time_zone, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 quarters, const DateLUTImpl & time_zone, Int64 scale_multiplier, Int64 origin = 0)
{ {
return time_zone.toStartOfQuarterInterval(time_zone.toDayNum(t / scale_multiplier), quarters); if (origin == 0)
return time_zone.toStartOfQuarterInterval(time_zone.toDayNum(t / scale_multiplier), quarters);
else
return ToStartOfInterval<IntervalKind::Kind::Month>::execute(t, quarters * 3, time_zone, scale_multiplier, origin);
} }
}; };
@ -760,9 +781,12 @@ struct ToStartOfInterval<IntervalKind::Kind::Year>
{ {
return time_zone.toStartOfYearInterval(time_zone.toDayNum(t), years); return time_zone.toStartOfYearInterval(time_zone.toDayNum(t), years);
} }
static UInt16 execute(Int64 t, Int64 years, const DateLUTImpl & time_zone, Int64 scale_multiplier) static Int64 execute(Int64 t, Int64 years, const DateLUTImpl & time_zone, Int64 scale_multiplier, Int64 origin = 0)
{ {
return time_zone.toStartOfYearInterval(time_zone.toDayNum(t / scale_multiplier), years); if (origin == 0)
return time_zone.toStartOfYearInterval(time_zone.toDayNum(t / scale_multiplier), years);
else
return ToStartOfInterval<IntervalKind::Kind::Month>::execute(t, years * 12, time_zone, scale_multiplier, origin);
} }
}; };

View File

@ -4134,6 +4134,29 @@ private:
}; };
} }
/// Create wrapper only if we support this conversion.
WrapperType createWrapperIfCanConvert(const DataTypePtr & from, const DataTypePtr & to) const
{
try
{
/// We can avoid try/catch here if we will implement check that 2 types can be casted, but it
/// requires quite a lot of work. By now let's simply use try/catch.
/// First, check that we can create a wrapper.
WrapperType wrapper = prepareUnpackDictionaries(from, to);
/// Second, check if we can perform a conversion on column with default value.
/// (we cannot just check empty column as we do some checks only during iteration over rows).
auto test_col = from->createColumn();
test_col->insertDefault();
ColumnsWithTypeAndName column_from = {{test_col->getPtr(), from, "" }};
wrapper(column_from, to, nullptr, 1);
return wrapper;
}
catch (const Exception &)
{
return {};
}
}
WrapperType createVariantToColumnWrapper(const DataTypeVariant & from_variant, const DataTypePtr & to_type) const WrapperType createVariantToColumnWrapper(const DataTypeVariant & from_variant, const DataTypePtr & to_type) const
{ {
const auto & variant_types = from_variant.getVariants(); const auto & variant_types = from_variant.getVariants();
@ -4142,7 +4165,19 @@ private:
/// Create conversion wrapper for each variant. /// Create conversion wrapper for each variant.
for (const auto & variant_type : variant_types) for (const auto & variant_type : variant_types)
variant_wrappers.push_back(prepareUnpackDictionaries(variant_type, to_type)); {
WrapperType wrapper;
if (cast_type == CastType::accurateOrNull)
{
/// Create wrapper only if we support conversion from variant to the resulting type.
wrapper = createWrapperIfCanConvert(variant_type, to_type);
}
else
{
wrapper = prepareUnpackDictionaries(variant_type, to_type);
}
variant_wrappers.push_back(wrapper);
}
return [variant_wrappers, variant_types, to_type] return [variant_wrappers, variant_types, to_type]
(ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count) -> ColumnPtr (ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, const ColumnNullable *, size_t input_rows_count) -> ColumnPtr
@ -4157,7 +4192,11 @@ private:
auto variant_col = column_variant.getVariantPtrByGlobalDiscriminator(i); auto variant_col = column_variant.getVariantPtrByGlobalDiscriminator(i);
ColumnsWithTypeAndName variant = {{variant_col, variant_types[i], "" }}; ColumnsWithTypeAndName variant = {{variant_col, variant_types[i], "" }};
const auto & variant_wrapper = variant_wrappers[i]; const auto & variant_wrapper = variant_wrappers[i];
casted_variant_columns.push_back(variant_wrapper(variant, result_type, nullptr, variant_col->size())); ColumnPtr casted_variant;
/// Check if we have wrapper for this variant.
if (variant_wrapper)
casted_variant = variant_wrapper(variant, result_type, nullptr, variant_col->size());
casted_variant_columns.push_back(std::move(casted_variant));
} }
/// Second, construct resulting column from casted variant columns according to discriminators. /// Second, construct resulting column from casted variant columns according to discriminators.
@ -4167,7 +4206,7 @@ private:
for (size_t i = 0; i != input_rows_count; ++i) for (size_t i = 0; i != input_rows_count; ++i)
{ {
auto global_discr = column_variant.globalDiscriminatorByLocal(local_discriminators[i]); auto global_discr = column_variant.globalDiscriminatorByLocal(local_discriminators[i]);
if (global_discr == ColumnVariant::NULL_DISCRIMINATOR) if (global_discr == ColumnVariant::NULL_DISCRIMINATOR || !casted_variant_columns[global_discr])
res->insertDefault(); res->insertDefault();
else else
res->insertFrom(*casted_variant_columns[global_discr], column_variant.offsetAt(i)); res->insertFrom(*casted_variant_columns[global_discr], column_variant.offsetAt(i));
@ -4357,10 +4396,27 @@ private:
casted_variant_columns.reserve(variant_types.size()); casted_variant_columns.reserve(variant_types.size());
for (size_t i = 0; i != variant_types.size(); ++i) for (size_t i = 0; i != variant_types.size(); ++i)
{ {
/// Skip shared variant, it will be processed later.
if (i == column_dynamic.getSharedVariantDiscriminator())
{
casted_variant_columns.push_back(nullptr);
continue;
}
const auto & variant_col = variant_column.getVariantPtrByGlobalDiscriminator(i); const auto & variant_col = variant_column.getVariantPtrByGlobalDiscriminator(i);
ColumnsWithTypeAndName variant = {{variant_col, variant_types[i], ""}}; ColumnsWithTypeAndName variant = {{variant_col, variant_types[i], ""}};
auto variant_wrapper = prepareUnpackDictionaries(variant_types[i], result_type); WrapperType variant_wrapper;
casted_variant_columns.push_back(variant_wrapper(variant, result_type, nullptr, variant_col->size())); if (cast_type == CastType::accurateOrNull)
/// Create wrapper only if we support conversion from variant to the resulting type.
variant_wrapper = createWrapperIfCanConvert(variant_types[i], result_type);
else
variant_wrapper = prepareUnpackDictionaries(variant_types[i], result_type);
ColumnPtr casted_variant;
/// Check if we have wrapper for this variant.
if (variant_wrapper)
casted_variant = variant_wrapper(variant, result_type, nullptr, variant_col->size());
casted_variant_columns.push_back(casted_variant);
} }
/// Second, collect all variants stored in shared variant and cast them to result type. /// Second, collect all variants stored in shared variant and cast them to result type.
@ -4416,8 +4472,18 @@ private:
for (size_t i = 0; i != variant_types_from_shared_variant.size(); ++i) for (size_t i = 0; i != variant_types_from_shared_variant.size(); ++i)
{ {
ColumnsWithTypeAndName variant = {{variant_columns_from_shared_variant[i]->getPtr(), variant_types_from_shared_variant[i], ""}}; ColumnsWithTypeAndName variant = {{variant_columns_from_shared_variant[i]->getPtr(), variant_types_from_shared_variant[i], ""}};
auto variant_wrapper = prepareUnpackDictionaries(variant_types_from_shared_variant[i], result_type); WrapperType variant_wrapper;
casted_shared_variant_columns.push_back(variant_wrapper(variant, result_type, nullptr, variant_columns_from_shared_variant[i]->size())); if (cast_type == CastType::accurateOrNull)
/// Create wrapper only if we support conversion from variant to the resulting type.
variant_wrapper = createWrapperIfCanConvert(variant_types_from_shared_variant[i], result_type);
else
variant_wrapper = prepareUnpackDictionaries(variant_types_from_shared_variant[i], result_type);
ColumnPtr casted_variant;
/// Check if we have wrapper for this variant.
if (variant_wrapper)
casted_variant = variant_wrapper(variant, result_type, nullptr, variant_columns_from_shared_variant[i]->size());
casted_shared_variant_columns.push_back(casted_variant);
} }
/// Construct result column from all casted variants. /// Construct result column from all casted variants.
@ -4427,11 +4493,23 @@ private:
{ {
auto global_discr = variant_column.globalDiscriminatorByLocal(local_discriminators[i]); auto global_discr = variant_column.globalDiscriminatorByLocal(local_discriminators[i]);
if (global_discr == ColumnVariant::NULL_DISCRIMINATOR) if (global_discr == ColumnVariant::NULL_DISCRIMINATOR)
{
res->insertDefault(); res->insertDefault();
}
else if (global_discr == shared_variant_discr) else if (global_discr == shared_variant_discr)
res->insertFrom(*casted_shared_variant_columns[shared_variant_indexes[i]], shared_variant_offsets[i]); {
if (casted_shared_variant_columns[shared_variant_indexes[i]])
res->insertFrom(*casted_shared_variant_columns[shared_variant_indexes[i]], shared_variant_offsets[i]);
else
res->insertDefault();
}
else else
res->insertFrom(*casted_variant_columns[global_discr], offsets[i]); {
if (casted_variant_columns[global_discr])
res->insertFrom(*casted_variant_columns[global_discr], offsets[i]);
else
res->insertDefault();
}
} }
return res; return res;

View File

@ -6,6 +6,7 @@
# include <Columns/ColumnString.h> # include <Columns/ColumnString.h>
# include <Functions/LowerUpperImpl.h> # include <Functions/LowerUpperImpl.h>
# include <base/scope_guard.h>
# include <unicode/ucasemap.h> # include <unicode/ucasemap.h>
# include <unicode/unistr.h> # include <unicode/unistr.h>
# include <unicode/urename.h> # include <unicode/urename.h>
@ -49,6 +50,11 @@ struct LowerUpperUTF8Impl
if (U_FAILURE(error_code)) if (U_FAILURE(error_code))
throw DB::Exception(ErrorCodes::LOGICAL_ERROR, "Error calling ucasemap_open: {}", u_errorName(error_code)); throw DB::Exception(ErrorCodes::LOGICAL_ERROR, "Error calling ucasemap_open: {}", u_errorName(error_code));
SCOPE_EXIT(
{
ucasemap_close(case_map);
});
size_t curr_offset = 0; size_t curr_offset = 0;
for (size_t row_i = 0; row_i < input_rows_count; ++row_i) for (size_t row_i = 0; row_i < input_rows_count; ++row_i)
{ {

View File

@ -1,11 +1,15 @@
#include <Functions/IFunction.h> #include <Columns/ColumnArray.h>
#include <Functions/FunctionFactory.h> #include <Columns/ColumnDecimal.h>
#include <Columns/ColumnFixedString.h>
#include <Columns/ColumnNullable.h>
#include <Columns/ColumnString.h>
#include <Core/Settings.h>
#include <DataTypes/DataTypeArray.h> #include <DataTypes/DataTypeArray.h>
#include <DataTypes/getLeastSupertype.h> #include <DataTypes/getLeastSupertype.h>
#include <Columns/ColumnArray.h> #include <Functions/FunctionFactory.h>
#include <Core/Settings.h> #include <Functions/IFunction.h>
#include <Interpreters/castColumn.h>
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Interpreters/castColumn.h>
namespace DB namespace DB
@ -44,11 +48,13 @@ public:
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override
{ {
size_t num_elements = arguments.size(); const size_t num_elements = arguments.size();
if (num_elements == 0) if (num_elements == 0)
{
/// We should return constant empty array. /// We should return constant empty array.
return result_type->createColumnConstWithDefaultValue(input_rows_count); return result_type->createColumnConstWithDefaultValue(input_rows_count);
}
const DataTypePtr & elem_type = static_cast<const DataTypeArray &>(*result_type).getNestedType(); const DataTypePtr & elem_type = static_cast<const DataTypeArray &>(*result_type).getNestedType();
@ -60,7 +66,6 @@ public:
Columns columns_holder(num_elements); Columns columns_holder(num_elements);
ColumnRawPtrs column_ptrs(num_elements); ColumnRawPtrs column_ptrs(num_elements);
for (size_t i = 0; i < num_elements; ++i) for (size_t i = 0; i < num_elements; ++i)
{ {
const auto & arg = arguments[i]; const auto & arg = arguments[i];
@ -77,35 +82,199 @@ public:
} }
/// Create and fill the result array. /// Create and fill the result array.
auto out = ColumnArray::create(elem_type->createColumn()); auto out = ColumnArray::create(elem_type->createColumn());
IColumn & out_data = out->getData(); IColumn & out_data = out->getData();
IColumn::Offsets & out_offsets = out->getOffsets(); IColumn::Offsets & out_offsets = out->getOffsets();
out_data.reserve(input_rows_count * num_elements); /// Fill out_offsets
out_offsets.resize(input_rows_count); out_offsets.resize_exact(input_rows_count);
IColumn::Offset current_offset = 0; IColumn::Offset current_offset = 0;
for (size_t i = 0; i < input_rows_count; ++i) for (size_t i = 0; i < input_rows_count; ++i)
{ {
for (size_t j = 0; j < num_elements; ++j)
out_data.insertFrom(*column_ptrs[j], i);
current_offset += num_elements; current_offset += num_elements;
out_offsets[i] = current_offset; out_offsets[i] = current_offset;
} }
/// Fill out_data
out_data.reserve(input_rows_count * num_elements);
if (num_elements == 1)
out_data.insertRangeFrom(*column_ptrs[0], 0, input_rows_count);
else
execute(column_ptrs, out_data, input_rows_count);
return out; return out;
} }
private: private:
bool execute(const ColumnRawPtrs & columns, IColumn & out_data, size_t input_rows_count) const
{
return executeNumber<UInt8>(columns, out_data, input_rows_count) || executeNumber<UInt16>(columns, out_data, input_rows_count)
|| executeNumber<UInt32>(columns, out_data, input_rows_count) || executeNumber<UInt64>(columns, out_data, input_rows_count)
|| executeNumber<UInt128>(columns, out_data, input_rows_count) || executeNumber<UInt256>(columns, out_data, input_rows_count)
|| executeNumber<Int8>(columns, out_data, input_rows_count) || executeNumber<Int16>(columns, out_data, input_rows_count)
|| executeNumber<Int32>(columns, out_data, input_rows_count) || executeNumber<Int64>(columns, out_data, input_rows_count)
|| executeNumber<Int128>(columns, out_data, input_rows_count) || executeNumber<Int256>(columns, out_data, input_rows_count)
|| executeNumber<Float32>(columns, out_data, input_rows_count) || executeNumber<Float64>(columns, out_data, input_rows_count)
|| executeNumber<Decimal32>(columns, out_data, input_rows_count)
|| executeNumber<Decimal64>(columns, out_data, input_rows_count)
|| executeNumber<Decimal128>(columns, out_data, input_rows_count)
|| executeNumber<Decimal256>(columns, out_data, input_rows_count)
|| executeNumber<DateTime64>(columns, out_data, input_rows_count) || executeString(columns, out_data, input_rows_count)
|| executeNullable(columns, out_data, input_rows_count) || executeTuple(columns, out_data, input_rows_count)
|| executeFixedString(columns, out_data, input_rows_count) || executeGeneric(columns, out_data, input_rows_count);
}
template <typename T>
bool executeNumber(const ColumnRawPtrs & columns, IColumn & out_data, size_t input_rows_count) const
{
using Container = ColumnVectorOrDecimal<T>::Container;
std::vector<const Container *> containers(columns.size(), nullptr);
for (size_t i = 0; i < columns.size(); ++i)
{
const ColumnVectorOrDecimal<T> * concrete_column = checkAndGetColumn<ColumnVectorOrDecimal<T>>(columns[i]);
if (!concrete_column)
return false;
containers[i] = &concrete_column->getData();
}
ColumnVectorOrDecimal<T> & concrete_out_data = assert_cast<ColumnVectorOrDecimal<T> &>(out_data);
Container & out_container = concrete_out_data.getData();
out_container.resize_exact(columns.size() * input_rows_count);
for (size_t row_i = 0; row_i < input_rows_count; ++row_i)
{
const size_t base = row_i * columns.size();
for (size_t col_i = 0; col_i < columns.size(); ++col_i)
out_container[base + col_i] = (*containers[col_i])[row_i];
}
return true;
}
bool executeString(const ColumnRawPtrs & columns, IColumn & out_data, size_t input_rows_count) const
{
size_t total_bytes = 0;
std::vector<const ColumnString *> concrete_columns(columns.size(), nullptr);
for (size_t i = 0; i < columns.size(); ++i)
{
const ColumnString * concrete_column = checkAndGetColumn<ColumnString>(columns[i]);
if (!concrete_column)
return false;
total_bytes += concrete_column->getChars().size();
concrete_columns[i] = concrete_column;
}
ColumnString & concrete_out_data = assert_cast<ColumnString &>(out_data);
auto & out_chars = concrete_out_data.getChars();
auto & out_offsets = concrete_out_data.getOffsets();
out_chars.resize_exact(total_bytes);
out_offsets.resize_exact(input_rows_count * columns.size());
size_t cur_out_offset = 0;
for (size_t row_i = 0; row_i < input_rows_count; ++row_i)
{
const size_t base = row_i * columns.size();
for (size_t col_i = 0; col_i < columns.size(); ++col_i)
{
StringRef ref = concrete_columns[col_i]->getDataAt(row_i);
memcpySmallAllowReadWriteOverflow15(&out_chars[cur_out_offset], ref.data, ref.size);
out_chars[cur_out_offset + ref.size] = 0;
cur_out_offset += ref.size + 1;
out_offsets[base + col_i] = cur_out_offset;
}
}
return true;
}
bool executeFixedString(const ColumnRawPtrs & columns, IColumn & out_data, size_t input_rows_count) const
{
std::vector<const ColumnFixedString *> concrete_columns(columns.size(), nullptr);
for (size_t i = 0; i < columns.size(); ++i)
{
const ColumnFixedString * concrete_column = checkAndGetColumn<ColumnFixedString>(columns[i]);
if (!concrete_column)
return false;
concrete_columns[i] = concrete_column;
}
ColumnFixedString & concrete_out_data = assert_cast<ColumnFixedString &>(out_data);
auto & out_chars = concrete_out_data.getChars();
const size_t n = concrete_out_data.getN();
size_t total_bytes = n * columns.size() * input_rows_count;
out_chars.resize_exact(total_bytes);
size_t curr_out_offset = 0;
for (size_t row_i = 0; row_i < input_rows_count; ++row_i)
{
for (size_t col_i = 0; col_i < columns.size(); ++col_i)
{
StringRef ref = concrete_columns[col_i]->getDataAt(row_i);
memcpySmallAllowReadWriteOverflow15(&out_chars[curr_out_offset], ref.data, n);
curr_out_offset += n;
}
}
return true;
}
bool executeNullable(const ColumnRawPtrs & columns, IColumn & out_data, size_t input_rows_count) const
{
ColumnRawPtrs null_maps(columns.size(), nullptr);
ColumnRawPtrs nested_columns(columns.size(), nullptr);
for (size_t i = 0; i < columns.size(); ++i)
{
const ColumnNullable * concrete_column = checkAndGetColumn<ColumnNullable>(columns[i]);
if (!concrete_column)
return false;
null_maps[i] = &concrete_column->getNullMapColumn();
nested_columns[i] = &concrete_column->getNestedColumn();
}
ColumnNullable & concrete_out_data = assert_cast<ColumnNullable &>(out_data);
auto & out_null_map = concrete_out_data.getNullMapColumn();
auto & out_nested_column = concrete_out_data.getNestedColumn();
execute(null_maps, out_null_map, input_rows_count);
execute(nested_columns, out_nested_column, input_rows_count);
return true;
}
bool executeTuple(const ColumnRawPtrs & columns, IColumn & out_data, size_t input_rows_count) const
{
ColumnTuple * concrete_out_data = typeid_cast<ColumnTuple *>(&out_data);
if (!concrete_out_data)
return false;
const size_t tuple_size = concrete_out_data->tupleSize();
for (size_t i = 0; i < tuple_size; ++i)
{
ColumnRawPtrs elem_columns(columns.size(), nullptr);
for (size_t j = 0; j < columns.size(); ++j)
{
const ColumnTuple * concrete_column = assert_cast<const ColumnTuple *>(columns[j]);
elem_columns[j] = &concrete_column->getColumn(i);
}
execute(elem_columns, concrete_out_data->getColumn(i), input_rows_count);
}
return true;
}
bool executeGeneric(const ColumnRawPtrs & columns, IColumn & out_data, size_t input_rows_count) const
{
for (size_t i = 0; i < input_rows_count; ++i)
for (const auto * column : columns)
out_data.insertFrom(*column, i);
return true;
}
String getName() const override String getName() const override
{ {
return name; return name;
} }
bool addField(DataTypePtr type_res, const Field & f, Array & arr) const;
bool use_variant_as_common_type = false; bool use_variant_as_common_type = false;
}; };

View File

@ -1,7 +1,8 @@
#include <Columns/ColumnTuple.h>
#include <Columns/ColumnArray.h> #include <Columns/ColumnArray.h>
#include <DataTypes/DataTypeTuple.h> #include <Columns/ColumnNullable.h>
#include <Columns/ColumnTuple.h>
#include <DataTypes/DataTypeArray.h> #include <DataTypes/DataTypeArray.h>
#include <DataTypes/DataTypeTuple.h>
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <Functions/FunctionHelpers.h> #include <Functions/FunctionHelpers.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
@ -12,23 +13,22 @@ namespace DB
namespace ErrorCodes namespace ErrorCodes
{ {
extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int SIZES_OF_ARRAYS_DONT_MATCH; extern const int SIZES_OF_ARRAYS_DONT_MATCH;
extern const int TOO_FEW_ARGUMENTS_FOR_FUNCTION; extern const int TOO_FEW_ARGUMENTS_FOR_FUNCTION;
extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_COLUMN;
} }
/// arrayZip(['a', 'b', 'c'], ['d', 'e', 'f']) = [('a', 'd'), ('b', 'e'), ('c', 'f')] /// arrayZip(['a', 'b', 'c'], ['d', 'e', 'f']) = [('a', 'd'), ('b', 'e'), ('c', 'f')]
/// arrayZipUnaligned(['a', 'b', 'c'], ['d', 'e']) = [('a', 'd'), ('b', 'e'), ('c', null)]
template <bool allow_unaligned>
class FunctionArrayZip : public IFunction class FunctionArrayZip : public IFunction
{ {
public: public:
static constexpr auto name = "arrayZip"; static constexpr auto name = allow_unaligned ? "arrayZipUnaligned" : "arrayZip";
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionArrayZip>(); } static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionArrayZip>(); }
String getName() const override String getName() const override { return name; }
{
return name;
}
bool isVariadic() const override { return true; } bool isVariadic() const override { return true; }
size_t getNumberOfArguments() const override { return 0; } size_t getNumberOfArguments() const override { return 0; }
@ -39,8 +39,11 @@ public:
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
{ {
if (arguments.empty()) if (arguments.empty())
throw Exception(ErrorCodes::TOO_FEW_ARGUMENTS_FOR_FUNCTION, throw Exception(
"Function {} needs at least one argument; passed {}." , getName(), arguments.size()); ErrorCodes::TOO_FEW_ARGUMENTS_FOR_FUNCTION,
"Function {} needs at least one argument; passed {}.",
getName(),
arguments.size());
DataTypes arguments_types; DataTypes arguments_types;
for (size_t index = 0; index < arguments.size(); ++index) for (size_t index = 0; index < arguments.size(); ++index)
@ -48,56 +51,142 @@ public:
const DataTypeArray * array_type = checkAndGetDataType<DataTypeArray>(arguments[index].type.get()); const DataTypeArray * array_type = checkAndGetDataType<DataTypeArray>(arguments[index].type.get());
if (!array_type) if (!array_type)
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Argument {} of function {} must be array. Found {} instead.", throw Exception(
toString(index + 1), getName(), arguments[0].type->getName()); ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
"Argument {} of function {} must be array. Found {} instead.",
toString(index + 1),
getName(),
arguments[0].type->getName());
arguments_types.emplace_back(array_type->getNestedType()); auto nested_type = array_type->getNestedType();
if constexpr (allow_unaligned)
nested_type = makeNullable(nested_type);
arguments_types.emplace_back(nested_type);
} }
return std::make_shared<DataTypeArray>(std::make_shared<DataTypeTuple>(arguments_types)); return std::make_shared<DataTypeArray>(std::make_shared<DataTypeTuple>(arguments_types));
} }
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override ColumnPtr
executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & /*result_type*/, size_t input_rows_count) const override
{ {
size_t num_arguments = arguments.size(); size_t num_arguments = arguments.size();
Columns holders(num_arguments);
ColumnPtr first_array_column;
Columns tuple_columns(num_arguments); Columns tuple_columns(num_arguments);
bool has_unaligned = false;
size_t unaligned_index = 0;
for (size_t i = 0; i < num_arguments; ++i) for (size_t i = 0; i < num_arguments; ++i)
{ {
/// Constant columns cannot be inside tuple. It's only possible to have constant tuple as a whole. /// Constant columns cannot be inside tuple. It's only possible to have constant tuple as a whole.
ColumnPtr holder = arguments[i].column->convertToFullColumnIfConst(); ColumnPtr holder = arguments[i].column->convertToFullColumnIfConst();
holders[i] = holder;
const ColumnArray * column_array = checkAndGetColumn<ColumnArray>(holder.get()); const ColumnArray * column_array = checkAndGetColumn<ColumnArray>(holder.get());
if (!column_array) if (!column_array)
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Argument {} of function {} must be array. Found column {} instead.", throw Exception(
i + 1, getName(), holder->getName()); ErrorCodes::ILLEGAL_COLUMN,
"Argument {} of function {} must be array. Found column {} instead.",
if (i == 0) i + 1,
{ getName(),
first_array_column = holder; holder->getName());
}
else if (!column_array->hasEqualOffsets(static_cast<const ColumnArray &>(*first_array_column)))
{
throw Exception(ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH,
"The argument 1 and argument {} of function {} have different array sizes",
i + 1, getName());
}
tuple_columns[i] = column_array->getDataPtr(); tuple_columns[i] = column_array->getDataPtr();
if (i && !column_array->hasEqualOffsets(static_cast<const ColumnArray &>(*holders[0])))
{
has_unaligned = true;
unaligned_index = i;
}
} }
return ColumnArray::create( if constexpr (!allow_unaligned)
ColumnTuple::create(tuple_columns), static_cast<const ColumnArray &>(*first_array_column).getOffsetsPtr()); {
if (has_unaligned)
throw Exception(
ErrorCodes::SIZES_OF_ARRAYS_DONT_MATCH,
"The argument 1 and argument {} of function {} have different array sizes",
unaligned_index + 1,
getName());
else
return ColumnArray::create(
ColumnTuple::create(std::move(tuple_columns)), static_cast<const ColumnArray &>(*holders[0]).getOffsetsPtr());
}
else
return executeUnaligned(holders, tuple_columns, input_rows_count, has_unaligned);
}
private:
ColumnPtr executeUnaligned(const Columns & holders, Columns & tuple_columns, size_t input_rows_count, bool has_unaligned) const
{
std::vector<const ColumnArray *> array_columns(holders.size());
for (size_t i = 0; i < holders.size(); ++i)
array_columns[i] = checkAndGetColumn<ColumnArray>(holders[i].get());
for (auto & tuple_column : tuple_columns)
tuple_column = makeNullable(tuple_column);
if (!has_unaligned)
return ColumnArray::create(ColumnTuple::create(std::move(tuple_columns)), array_columns[0]->getOffsetsPtr());
MutableColumns res_tuple_columns(tuple_columns.size());
for (size_t i = 0; i < tuple_columns.size(); ++i)
{
res_tuple_columns[i] = tuple_columns[i]->cloneEmpty();
res_tuple_columns[i]->reserve(tuple_columns[i]->size());
}
auto res_offsets_column = ColumnArray::ColumnOffsets::create(input_rows_count);
auto & res_offsets = assert_cast<ColumnArray::ColumnOffsets &>(*res_offsets_column).getData();
size_t curr_offset = 0;
for (size_t row_i = 0; row_i < input_rows_count; ++row_i)
{
size_t max_size = 0;
for (size_t arg_i = 0; arg_i < holders.size(); ++arg_i)
{
const auto * array_column = array_columns[arg_i];
const auto & offsets = array_column->getOffsets();
size_t array_offset = offsets[row_i - 1];
size_t array_size = offsets[row_i] - array_offset;
res_tuple_columns[arg_i]->insertRangeFrom(*tuple_columns[arg_i], array_offset, array_size);
max_size = std::max(max_size, array_size);
}
for (size_t arg_i = 0; arg_i < holders.size(); ++arg_i)
{
const auto * array_column = array_columns[arg_i];
const auto & offsets = array_column->getOffsets();
size_t array_offset = offsets[row_i - 1];
size_t array_size = offsets[row_i] - array_offset;
res_tuple_columns[arg_i]->insertManyDefaults(max_size - array_size);
}
curr_offset += max_size;
res_offsets[row_i] = curr_offset;
}
return ColumnArray::create(ColumnTuple::create(std::move(res_tuple_columns)), std::move(res_offsets_column));
} }
}; };
REGISTER_FUNCTION(ArrayZip) REGISTER_FUNCTION(ArrayZip)
{ {
factory.registerFunction<FunctionArrayZip>(); factory.registerFunction<FunctionArrayZip<false>>(
{.description = R"(
Combines multiple arrays into a single array. The resulting array contains the corresponding elements of the source arrays grouped into tuples in the listed order of arguments.
)",
.categories{"String"}});
factory.registerFunction<FunctionArrayZip<true>>(
{.description = R"(
Combines multiple arrays into a single array, allowing for unaligned arrays. The resulting array contains the corresponding elements of the source arrays grouped into tuples in the listed order of arguments.
If the arrays have different sizes, the shorter arrays will be padded with `null` values.
)",
.categories{"String"}}
);
} }
} }

View File

@ -2,6 +2,8 @@
#include <Columns/ColumnMap.h> #include <Columns/ColumnMap.h>
#include <Columns/ColumnNullable.h> #include <Columns/ColumnNullable.h>
#include <Columns/ColumnsCommon.h> #include <Columns/ColumnsCommon.h>
#include <Columns/ColumnsNumber.h>
#include <Core/Settings.h>
#include <DataTypes/DataTypeArray.h> #include <DataTypes/DataTypeArray.h>
#include <DataTypes/DataTypeMap.h> #include <DataTypes/DataTypeMap.h>
#include <DataTypes/DataTypeTuple.h> #include <DataTypes/DataTypeTuple.h>
@ -13,7 +15,6 @@
#include <Interpreters/Context.h> #include <Interpreters/Context.h>
#include <Interpreters/castColumn.h> #include <Interpreters/castColumn.h>
#include <Common/HashTable/HashSet.h> #include <Common/HashTable/HashSet.h>
#include <Core/Settings.h>
namespace DB namespace DB
@ -36,11 +37,18 @@ class FunctionMap : public IFunction
public: public:
static constexpr auto name = "map"; static constexpr auto name = "map";
explicit FunctionMap(bool use_variant_as_common_type_) : use_variant_as_common_type(use_variant_as_common_type_) {} explicit FunctionMap(ContextPtr context_)
: context(context_)
, use_variant_as_common_type(
context->getSettingsRef().allow_experimental_variant_type && context->getSettingsRef().use_variant_as_common_type)
, function_array(FunctionFactory::instance().get("array", context))
, function_map_from_arrays(FunctionFactory::instance().get("mapFromArrays", context))
{
}
static FunctionPtr create(ContextPtr context) static FunctionPtr create(ContextPtr context)
{ {
return std::make_shared<FunctionMap>(context->getSettingsRef().allow_experimental_variant_type && context->getSettingsRef().use_variant_as_common_type); return std::make_shared<FunctionMap>(context);
} }
String getName() const override String getName() const override
@ -101,62 +109,38 @@ public:
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override
{ {
size_t num_elements = arguments.size(); size_t num_elements = arguments.size();
if (num_elements == 0) if (num_elements == 0)
return result_type->createColumnConstWithDefaultValue(input_rows_count); return result_type->createColumnConstWithDefaultValue(input_rows_count);
ColumnsWithTypeAndName key_args;
ColumnsWithTypeAndName value_args;
for (size_t i = 0; i < num_elements; i += 2)
{
key_args.emplace_back(arguments[i]);
value_args.emplace_back(arguments[i+1]);
}
const auto & result_type_map = static_cast<const DataTypeMap &>(*result_type); const auto & result_type_map = static_cast<const DataTypeMap &>(*result_type);
const DataTypePtr & key_type = result_type_map.getKeyType(); const DataTypePtr & key_type = result_type_map.getKeyType();
const DataTypePtr & value_type = result_type_map.getValueType(); const DataTypePtr & value_type = result_type_map.getValueType();
const DataTypePtr & key_array_type = std::make_shared<DataTypeArray>(key_type);
const DataTypePtr & value_array_type = std::make_shared<DataTypeArray>(value_type);
Columns columns_holder(num_elements); /// key_array = array(args[0], args[2]...)
ColumnRawPtrs column_ptrs(num_elements); ColumnPtr key_array = function_array->build(key_args)->execute(key_args, key_array_type, input_rows_count);
/// value_array = array(args[1], args[3]...)
ColumnPtr value_array = function_array->build(value_args)->execute(value_args, value_array_type, input_rows_count);
for (size_t i = 0; i < num_elements; ++i) /// result = mapFromArrays(key_array, value_array)
{ ColumnsWithTypeAndName map_args{{key_array, key_array_type, ""}, {value_array, value_array_type, ""}};
const auto & arg = arguments[i]; return function_map_from_arrays->build(map_args)->execute(map_args, result_type, input_rows_count);
const auto to_type = i % 2 == 0 ? key_type : value_type;
ColumnPtr preprocessed_column = castColumn(arg, to_type);
preprocessed_column = preprocessed_column->convertToFullColumnIfConst();
columns_holder[i] = std::move(preprocessed_column);
column_ptrs[i] = columns_holder[i].get();
}
/// Create and fill the result map.
MutableColumnPtr keys_data = key_type->createColumn();
MutableColumnPtr values_data = value_type->createColumn();
MutableColumnPtr offsets = DataTypeNumber<IColumn::Offset>().createColumn();
size_t total_elements = input_rows_count * num_elements / 2;
keys_data->reserve(total_elements);
values_data->reserve(total_elements);
offsets->reserve(input_rows_count);
IColumn::Offset current_offset = 0;
for (size_t i = 0; i < input_rows_count; ++i)
{
for (size_t j = 0; j < num_elements; j += 2)
{
keys_data->insertFrom(*column_ptrs[j], i);
values_data->insertFrom(*column_ptrs[j + 1], i);
}
current_offset += num_elements / 2;
offsets->insert(current_offset);
}
auto nested_column = ColumnArray::create(
ColumnTuple::create(Columns{std::move(keys_data), std::move(values_data)}),
std::move(offsets));
return ColumnMap::create(nested_column);
} }
private: private:
ContextPtr context;
bool use_variant_as_common_type = false; bool use_variant_as_common_type = false;
FunctionOverloadResolverPtr function_array;
FunctionOverloadResolverPtr function_map_from_arrays;
}; };
/// mapFromArrays(keys, values) is a function that allows you to make key-value pair from a pair of arrays or maps /// mapFromArrays(keys, values) is a function that allows you to make key-value pair from a pair of arrays or maps
@ -173,6 +157,7 @@ public:
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
bool useDefaultImplementationForNulls() const override { return false; } bool useDefaultImplementationForNulls() const override { return false; }
bool useDefaultImplementationForConstants() const override { return true; } bool useDefaultImplementationForConstants() const override { return true; }
bool useDefaultImplementationForLowCardinalityColumns() const override { return false; }
DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override DataTypePtr getReturnTypeImpl(const DataTypes & arguments) const override
{ {

View File

@ -2,7 +2,7 @@
#include <Functions/IFunction.h> #include <Functions/IFunction.h>
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <Columns/ColumnLowCardinality.h> #include <Columns/ColumnLowCardinality.h>
#include <DataTypes/DataTypeLowCardinality.h> #include <Columns/ColumnSparse.h>
namespace DB namespace DB
{ {
@ -18,11 +18,6 @@ public:
return std::make_shared<FunctionMaterialize>(); return std::make_shared<FunctionMaterialize>();
} }
bool useDefaultImplementationForNulls() const override
{
return false;
}
/// Get the function name. /// Get the function name.
String getName() const override String getName() const override
{ {
@ -34,8 +29,16 @@ public:
return true; return true;
} }
bool useDefaultImplementationForNulls() const override { return false; }
bool useDefaultImplementationForNothing() const override { return false; }
bool useDefaultImplementationForConstants() const override { return false; }
bool useDefaultImplementationForLowCardinalityColumns() const override { return false; } bool useDefaultImplementationForLowCardinalityColumns() const override { return false; }
bool useDefaultImplementationForSparseColumns() const override { return false; }
bool isSuitableForConstantFolding() const override { return false; } bool isSuitableForConstantFolding() const override { return false; }
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
@ -52,7 +55,7 @@ public:
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr &, size_t /*input_rows_count*/) const override
{ {
return arguments[0].column->convertToFullColumnIfConst(); return recursiveRemoveSparse(arguments[0].column->convertToFullColumnIfConst());
} }
bool hasInformationAboutMonotonicity() const override { return true; } bool hasInformationAboutMonotonicity() const override { return true; }

View File

@ -1,12 +1,12 @@
#include <Columns/ColumnConst.h> #include <Columns/ColumnConst.h>
#include <Columns/ColumnString.h> #include <Columns/ColumnString.h>
#include <Common/StringUtils.h>
#include <Common/UTF8Helpers.h>
#include <DataTypes/DataTypeString.h> #include <DataTypes/DataTypeString.h>
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <Functions/FunctionHelpers.h> #include <Functions/FunctionHelpers.h>
#include <Functions/GatherUtils/Sources.h> #include <Functions/GatherUtils/Sources.h>
#include <Functions/IFunction.h> #include <Functions/IFunction.h>
#include <Common/StringUtils.h>
#include <Common/UTF8Helpers.h>
namespace DB namespace DB
{ {
@ -16,8 +16,8 @@ namespace
/// If 'is_utf8' - measure offset and length in code points instead of bytes. /// If 'is_utf8' - measure offset and length in code points instead of bytes.
/// Syntax: /// Syntax:
/// - overlay(input, replace, offset[, length]) /// - overlay(s, replace, offset[, length])
/// - overlayUTF8(input, replace, offset[, length]) - measure offset and length in code points instead of bytes /// - overlayUTF8(s, replace, offset[, length]) - measure offset and length in code points instead of bytes
template <bool is_utf8> template <bool is_utf8>
class FunctionOverlay : public IFunction class FunctionOverlay : public IFunction
{ {
@ -34,7 +34,7 @@ public:
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
{ {
FunctionArgumentDescriptors mandatory_args{ FunctionArgumentDescriptors mandatory_args{
{"input", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isString), nullptr, "String"}, {"s", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isString), nullptr, "String"},
{"replace", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isString), nullptr, "String"}, {"replace", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isString), nullptr, "String"},
{"offset", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isNativeInteger), nullptr, "(U)Int8/16/32/64"}, {"offset", static_cast<FunctionArgumentDescriptor::TypeValidator>(&isNativeInteger), nullptr, "(U)Int8/16/32/64"},
}; };
@ -100,7 +100,6 @@ public:
res_data.reserve(col_input_string->getChars().size()); res_data.reserve(col_input_string->getChars().size());
} }
#define OVERLAY_EXECUTE_CASE(HAS_FOUR_ARGS, OFFSET_IS_CONST, LENGTH_IS_CONST) \ #define OVERLAY_EXECUTE_CASE(HAS_FOUR_ARGS, OFFSET_IS_CONST, LENGTH_IS_CONST) \
if (input_is_const && replace_is_const) \ if (input_is_const && replace_is_const) \
constantConstant<HAS_FOUR_ARGS, OFFSET_IS_CONST, LENGTH_IS_CONST>( \ constantConstant<HAS_FOUR_ARGS, OFFSET_IS_CONST, LENGTH_IS_CONST>( \
@ -186,7 +185,6 @@ public:
return res_col; return res_col;
} }
private: private:
/// input offset is 1-based, maybe negative /// input offset is 1-based, maybe negative
/// output result is 0-based valid offset, within [0, input_size] /// output result is 0-based valid offset, within [0, input_size]
@ -229,6 +227,7 @@ private:
ColumnString::Chars & res_data, ColumnString::Chars & res_data,
ColumnString::Offsets & res_offsets) const ColumnString::Offsets & res_offsets) const
{ {
/// Free us from handling negative length in the code below
if (has_four_args && length_is_const && const_length < 0) if (has_four_args && length_is_const && const_length < 0)
{ {
constantConstant<true, offset_is_const, false>( constantConstant<true, offset_is_const, false>(
@ -343,6 +342,7 @@ private:
ColumnString::Chars & res_data, ColumnString::Chars & res_data,
ColumnString::Offsets & res_offsets) const ColumnString::Offsets & res_offsets) const
{ {
/// Free us from handling negative length in the code below
if (has_four_args && length_is_const && const_length < 0) if (has_four_args && length_is_const && const_length < 0)
{ {
vectorConstant<true, offset_is_const, false>( vectorConstant<true, offset_is_const, false>(
@ -461,6 +461,7 @@ private:
ColumnString::Chars & res_data, ColumnString::Chars & res_data,
ColumnString::Offsets & res_offsets) const ColumnString::Offsets & res_offsets) const
{ {
/// Free us from handling negative length in the code below
if (has_four_args && length_is_const && const_length < 0) if (has_four_args && length_is_const && const_length < 0)
{ {
constantVector<true, offset_is_const, false>( constantVector<true, offset_is_const, false>(
@ -577,6 +578,7 @@ private:
ColumnString::Chars & res_data, ColumnString::Chars & res_data,
ColumnString::Offsets & res_offsets) const ColumnString::Offsets & res_offsets) const
{ {
/// Free us from handling negative length in the code below
if (has_four_args && length_is_const && const_length < 0) if (has_four_args && length_is_const && const_length < 0)
{ {
vectorVector<true, offset_is_const, false>( vectorVector<true, offset_is_const, false>(

View File

@ -124,7 +124,7 @@ public:
std::string_view sqid = col_non_const->getDataAt(i).toView(); std::string_view sqid = col_non_const->getDataAt(i).toView();
std::vector<UInt64> integers = sqids.decode(String(sqid)); std::vector<UInt64> integers = sqids.decode(String(sqid));
res_nested_data.insert(integers.begin(), integers.end()); res_nested_data.insert(integers.begin(), integers.end());
res_offsets_data.push_back(integers.size()); res_offsets_data.push_back(res_offsets_data.back() + integers.size());
} }
} }
else else

View File

@ -10,21 +10,31 @@
#include <Functions/FunctionFactory.h> #include <Functions/FunctionFactory.h>
#include <Functions/IFunction.h> #include <Functions/IFunction.h>
#include <IO/WriteHelpers.h> #include <IO/WriteHelpers.h>
#include <algorithm>
namespace DB namespace DB
{ {
namespace ErrorCodes namespace ErrorCodes
{ {
extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH; extern const int ARGUMENT_OUT_OF_BOUND;
extern const int BAD_ARGUMENTS;
extern const int ILLEGAL_COLUMN; extern const int ILLEGAL_COLUMN;
extern const int ILLEGAL_TYPE_OF_ARGUMENT; extern const int ILLEGAL_TYPE_OF_ARGUMENT;
extern const int ARGUMENT_OUT_OF_BOUND; extern const int NUMBER_OF_ARGUMENTS_DOESNT_MATCH;
} }
class FunctionToStartOfInterval : public IFunction class FunctionToStartOfInterval : public IFunction
{ {
private:
enum class Overload
{
Default, /// toStartOfInterval(time, interval) or toStartOfInterval(time, interval, timezone)
Origin /// toStartOfInterval(time, interval, origin) or toStartOfInterval(time, interval, origin, timezone)
};
mutable Overload overload;
public: public:
static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionToStartOfInterval>(); } static FunctionPtr create(ContextPtr) { return std::make_shared<FunctionToStartOfInterval>(); }
@ -34,7 +44,7 @@ public:
size_t getNumberOfArguments() const override { return 0; } size_t getNumberOfArguments() const override { return 0; }
bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; } bool isSuitableForShortCircuitArgumentsExecution(const DataTypesWithConstInfo & /*arguments*/) const override { return false; }
bool useDefaultImplementationForConstants() const override { return true; } bool useDefaultImplementationForConstants() const override { return true; }
ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1, 2}; } ColumnNumbers getArgumentsThatAreAlwaysConstant() const override { return {1, 2, 3}; }
bool hasInformationAboutMonotonicity() const override { return true; } bool hasInformationAboutMonotonicity() const override { return true; }
Monotonicity getMonotonicityForRange(const IDataType &, const Field &, const Field &) const override { return { .is_monotonic = true, .is_always_monotonic = true }; } Monotonicity getMonotonicityForRange(const IDataType &, const Field &, const Field &) const override { return { .is_monotonic = true, .is_always_monotonic = true }; }
@ -72,6 +82,9 @@ public:
"Illegal type {} of 2nd argument of function {}, expected a time interval", "Illegal type {} of 2nd argument of function {}, expected a time interval",
type_arg2->getName(), getName()); type_arg2->getName(), getName());
overload = Overload::Default;
/// Determine result type for default overload (no origin)
switch (interval_type->getKind()) // NOLINT(bugprone-switch-missing-default-case) switch (interval_type->getKind()) // NOLINT(bugprone-switch-missing-default-case)
{ {
case IntervalKind::Kind::Nanosecond: case IntervalKind::Kind::Nanosecond:
@ -97,13 +110,49 @@ public:
auto check_third_argument = [&] auto check_third_argument = [&]
{ {
const DataTypePtr & type_arg3 = arguments[2].type; const DataTypePtr & type_arg3 = arguments[2].type;
if (!isString(type_arg3)) if (isString(type_arg3))
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, {
"Illegal type {} of 3rd argument of function {}, expected a constant timezone string", if (value_is_date && result_type == ResultType::Date)
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
"A timezone argument of function {} with interval type {} is allowed only when the 1st argument has the type DateTime or DateTime64",
getName(), interval_type->getKind().toString());
}
else if (isDateOrDate32OrDateTimeOrDateTime64(type_arg3))
{
overload = Overload::Origin;
const DataTypePtr & type_arg1 = arguments[0].type;
if (isDate(type_arg1) && isDate(type_arg3))
result_type = ResultType::Date;
else if (isDate32(type_arg1) && isDate32(type_arg3))
result_type = ResultType::Date32;
else if (isDateTime(type_arg1) && isDateTime(type_arg3))
result_type = ResultType::DateTime;
else if (isDateTime64(type_arg1) && isDateTime64(type_arg3))
result_type = ResultType::DateTime64;
else
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Datetime argument and origin argument for function {} must have the same type", getName());
}
else
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of 3rd argument of function {}. "
"This argument is optional and must be a constant String with timezone name or a Date/Date32/DateTime/DateTime64 with a constant origin",
type_arg3->getName(), getName()); type_arg3->getName(), getName());
if (value_is_date && result_type == ResultType::Date) /// weird why this is && instead of || but too afraid to change it };
auto check_fourth_argument = [&]
{
if (overload != Overload::Origin) /// sanity check
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of 3rd argument of function {}. "
"The third argument must a Date/Date32/DateTime/DateTime64 with a constant origin",
arguments[2].type->getName(), getName());
const DataTypePtr & type_arg4 = arguments[3].type;
if (!isString(type_arg4))
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal type {} of 4th argument of function {}. "
"This argument is optional and must be a constant String with timezone name",
type_arg4->getName(), getName());
if (value_is_date && result_type == ResultType::Date)
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
"The timezone argument of function {} with interval type {} is allowed only when the 1st argument has type DateTime or DateTimt64", "A timezone argument of function {} with interval type {} is allowed only when the 1st argument has the type DateTime or DateTime64",
getName(), interval_type->getKind().toString()); getName(), interval_type->getKind().toString());
}; };
@ -118,10 +167,17 @@ public:
check_second_argument(); check_second_argument();
check_third_argument(); check_third_argument();
} }
else if (arguments.size() == 4)
{
check_first_argument();
check_second_argument();
check_third_argument();
check_fourth_argument();
}
else else
{ {
throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH, throw Exception(ErrorCodes::NUMBER_OF_ARGUMENTS_DOESNT_MATCH,
"Number of arguments for function {} doesn't match: passed {}, should be 2 or 3", "Number of arguments for function {} doesn't match: passed {}, must be 2, 3 or 4",
getName(), arguments.size()); getName(), arguments.size());
} }
@ -132,10 +188,19 @@ public:
case ResultType::Date32: case ResultType::Date32:
return std::make_shared<DataTypeDate32>(); return std::make_shared<DataTypeDate32>();
case ResultType::DateTime: case ResultType::DateTime:
return std::make_shared<DataTypeDateTime>(extractTimeZoneNameFromFunctionArguments(arguments, 2, 0, false)); {
const size_t time_zone_arg_num = (overload == Overload::Default) ? 2 : 3;
return std::make_shared<DataTypeDateTime>(extractTimeZoneNameFromFunctionArguments(arguments, time_zone_arg_num, 0, false));
}
case ResultType::DateTime64: case ResultType::DateTime64:
{ {
UInt32 scale = 0; UInt32 scale = 0;
if (isDateTime64(arguments[0].type) && overload == Overload::Origin)
{
scale = assert_cast<const DataTypeDateTime64 &>(*arguments[0].type.get()).getScale();
if (assert_cast<const DataTypeDateTime64 &>(*arguments[2].type.get()).getScale() != scale)
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Datetime argument and origin argument for function {} must have the same scale", getName());
}
if (interval_type->getKind() == IntervalKind::Kind::Nanosecond) if (interval_type->getKind() == IntervalKind::Kind::Nanosecond)
scale = 9; scale = 9;
else if (interval_type->getKind() == IntervalKind::Kind::Microsecond) else if (interval_type->getKind() == IntervalKind::Kind::Microsecond)
@ -143,69 +208,103 @@ public:
else if (interval_type->getKind() == IntervalKind::Kind::Millisecond) else if (interval_type->getKind() == IntervalKind::Kind::Millisecond)
scale = 3; scale = 3;
return std::make_shared<DataTypeDateTime64>(scale, extractTimeZoneNameFromFunctionArguments(arguments, 2, 0, false)); const size_t time_zone_arg_num = (overload == Overload::Default) ? 2 : 3;
return std::make_shared<DataTypeDateTime64>(scale, extractTimeZoneNameFromFunctionArguments(arguments, time_zone_arg_num, 0, false));
} }
} }
std::unreachable(); std::unreachable();
} }
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t /* input_rows_count */) const override
{ {
const auto & time_column = arguments[0]; const auto & time_column = arguments[0];
const auto & interval_column = arguments[1]; const auto & interval_column = arguments[1];
const auto & time_zone = extractTimeZoneFromFunctionArguments(arguments, 2, 0);
auto result_column = dispatchForTimeColumn(time_column, interval_column, result_type, time_zone, input_rows_count); ColumnWithTypeAndName origin_column;
if (overload == Overload::Origin)
origin_column = arguments[2];
const size_t time_zone_arg_num = (overload == Overload::Default) ? 2 : 3;
const auto & time_zone = extractTimeZoneFromFunctionArguments(arguments, time_zone_arg_num, 0);
ColumnPtr result_column;
if (isDate(result_type))
result_column = dispatchForTimeColumn<DataTypeDate>(time_column, interval_column, origin_column, result_type, time_zone);
else if (isDate32(result_type))
result_column = dispatchForTimeColumn<DataTypeDate32>(time_column, interval_column, origin_column, result_type, time_zone);
else if (isDateTime(result_type))
result_column = dispatchForTimeColumn<DataTypeDateTime>(time_column, interval_column, origin_column, result_type, time_zone);
else if (isDateTime64(result_type))
result_column = dispatchForTimeColumn<DataTypeDateTime64>(time_column, interval_column, origin_column, result_type, time_zone);
return result_column; return result_column;
} }
private: private:
template <typename ReturnType>
ColumnPtr dispatchForTimeColumn( ColumnPtr dispatchForTimeColumn(
const ColumnWithTypeAndName & time_column, const ColumnWithTypeAndName & interval_column, const ColumnWithTypeAndName & time_column, const ColumnWithTypeAndName & interval_column, const ColumnWithTypeAndName & origin_column, const DataTypePtr & result_type, const DateLUTImpl & time_zone) const
const DataTypePtr & result_type, const DateLUTImpl & time_zone,
size_t input_rows_count) const
{ {
const auto & time_column_type = *time_column.type.get(); const auto & time_column_type = *time_column.type.get();
const auto & time_column_col = *time_column.column.get(); const auto & time_column_col = *time_column.column.get();
if (isDateTime64(time_column_type)) if (isDate(time_column_type))
{
const auto * time_column_vec = checkAndGetColumn<ColumnDateTime64>(&time_column_col);
auto scale = assert_cast<const DataTypeDateTime64 &>(time_column_type).getScale();
if (time_column_vec)
return dispatchForIntervalColumn(assert_cast<const DataTypeDateTime64 &>(time_column_type), *time_column_vec, interval_column, result_type, time_zone, input_rows_count, scale);
}
else if (isDateTime(time_column_type))
{
const auto * time_column_vec = checkAndGetColumn<ColumnDateTime>(&time_column_col);
if (time_column_vec)
return dispatchForIntervalColumn(assert_cast<const DataTypeDateTime &>(time_column_type), *time_column_vec, interval_column, result_type, time_zone, input_rows_count);
}
else if (isDate(time_column_type))
{ {
const auto * time_column_vec = checkAndGetColumn<ColumnDate>(&time_column_col); const auto * time_column_vec = checkAndGetColumn<ColumnDate>(&time_column_col);
if (time_column_vec) if (time_column_vec)
return dispatchForIntervalColumn(assert_cast<const DataTypeDate &>(time_column_type), *time_column_vec, interval_column, result_type, time_zone, input_rows_count); return dispatchForIntervalColumn<ReturnType, DataTypeDate, ColumnDate>(assert_cast<const DataTypeDate &>(time_column_type), *time_column_vec, interval_column, origin_column, result_type, time_zone);
} }
else if (isDate32(time_column_type)) else if (isDate32(time_column_type))
{ {
const auto * time_column_vec = checkAndGetColumn<ColumnDate32>(&time_column_col); const auto * time_column_vec = checkAndGetColumn<ColumnDate32>(&time_column_col);
if (time_column_vec) if (time_column_vec)
return dispatchForIntervalColumn(assert_cast<const DataTypeDate32 &>(time_column_type), *time_column_vec, interval_column, result_type, time_zone, input_rows_count); return dispatchForIntervalColumn<ReturnType, DataTypeDate32, ColumnDate32>(assert_cast<const DataTypeDate32 &>(time_column_type), *time_column_vec, interval_column, origin_column, result_type, time_zone);
}
else if (isDateTime(time_column_type))
{
const auto * time_column_vec = checkAndGetColumn<ColumnDateTime>(&time_column_col);
if (time_column_vec)
return dispatchForIntervalColumn<ReturnType, DataTypeDateTime, ColumnDateTime>(assert_cast<const DataTypeDateTime &>(time_column_type), *time_column_vec, interval_column, origin_column, result_type, time_zone);
}
else if (isDateTime64(time_column_type))
{
const auto * time_column_vec = checkAndGetColumn<ColumnDateTime64>(&time_column_col);
auto scale = assert_cast<const DataTypeDateTime64 &>(time_column_type).getScale();
if (time_column_vec)
return dispatchForIntervalColumn<ReturnType, DataTypeDateTime64, ColumnDateTime64>(assert_cast<const DataTypeDateTime64 &>(time_column_type), *time_column_vec, interval_column, origin_column, result_type, time_zone, scale);
} }
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal column for 1st argument of function {}, expected a Date, Date32, DateTime or DateTime64", getName()); throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal column for 1st argument of function {}, expected a Date, Date32, DateTime or DateTime64", getName());
} }
template <typename TimeDataType, typename TimeColumnType> template <typename ReturnType, typename TimeDataType, typename TimeColumnType>
ColumnPtr dispatchForIntervalColumn( ColumnPtr dispatchForIntervalColumn(
const TimeDataType & time_data_type, const TimeColumnType & time_column, const ColumnWithTypeAndName & interval_column, const TimeDataType & time_data_type, const TimeColumnType & time_column, const ColumnWithTypeAndName & interval_column, const ColumnWithTypeAndName & origin_column,
const DataTypePtr & result_type, const DateLUTImpl & time_zone, size_t input_rows_count, UInt16 scale = 1) const const DataTypePtr & result_type, const DateLUTImpl & time_zone, UInt16 scale = 1) const
{ {
const auto * interval_type = checkAndGetDataType<DataTypeInterval>(interval_column.type.get()); const auto * interval_type = checkAndGetDataType<DataTypeInterval>(interval_column.type.get());
if (!interval_type) if (!interval_type)
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column for 2nd argument of function {}, must be a time interval", getName()); throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column for 2nd argument of function {}, must be a time interval", getName());
switch (interval_type->getKind()) // NOLINT(bugprone-switch-missing-default-case)
{
case IntervalKind::Kind::Nanosecond:
case IntervalKind::Kind::Microsecond:
case IntervalKind::Kind::Millisecond:
if (isDateOrDate32(time_data_type) || isDateTime(time_data_type))
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal interval kind for argument data type {}", isDate(time_data_type) ? "Date" : "DateTime");
break;
case IntervalKind::Kind::Second:
case IntervalKind::Kind::Minute:
case IntervalKind::Kind::Hour:
if (isDateOrDate32(time_data_type))
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT, "Illegal interval kind for argument data type Date");
break;
default:
break;
}
const auto * interval_column_const_int64 = checkAndGetColumnConst<ColumnInt64>(interval_column.column.get()); const auto * interval_column_const_int64 = checkAndGetColumnConst<ColumnInt64>(interval_column.column.get());
if (!interval_column_const_int64) if (!interval_column_const_int64)
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column for 2nd argument of function {}, must be a const time interval", getName()); throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Illegal column for 2nd argument of function {}, must be a const time interval", getName());
@ -217,51 +316,102 @@ private:
switch (interval_type->getKind()) // NOLINT(bugprone-switch-missing-default-case) switch (interval_type->getKind()) // NOLINT(bugprone-switch-missing-default-case)
{ {
case IntervalKind::Kind::Nanosecond: case IntervalKind::Kind::Nanosecond:
return execute<TimeDataType, TimeColumnType, DataTypeDateTime64, IntervalKind::Kind::Nanosecond>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Nanosecond>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Microsecond: case IntervalKind::Kind::Microsecond:
return execute<TimeDataType, TimeColumnType, DataTypeDateTime64, IntervalKind::Kind::Microsecond>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Microsecond>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Millisecond: case IntervalKind::Kind::Millisecond:
return execute<TimeDataType, TimeColumnType, DataTypeDateTime64, IntervalKind::Kind::Millisecond>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Millisecond>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Second: case IntervalKind::Kind::Second:
return execute<TimeDataType, TimeColumnType, DataTypeDateTime, IntervalKind::Kind::Second>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Second>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Minute: case IntervalKind::Kind::Minute:
return execute<TimeDataType, TimeColumnType, DataTypeDateTime, IntervalKind::Kind::Minute>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Minute>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Hour: case IntervalKind::Kind::Hour:
return execute<TimeDataType, TimeColumnType, DataTypeDateTime, IntervalKind::Kind::Hour>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Hour>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Day: case IntervalKind::Kind::Day:
return execute<TimeDataType, TimeColumnType, DataTypeDateTime, IntervalKind::Kind::Day>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Day>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Week: case IntervalKind::Kind::Week:
return execute<TimeDataType, TimeColumnType, DataTypeDate, IntervalKind::Kind::Week>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Week>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Month: case IntervalKind::Kind::Month:
return execute<TimeDataType, TimeColumnType, DataTypeDate, IntervalKind::Kind::Month>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Month>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Quarter: case IntervalKind::Kind::Quarter:
return execute<TimeDataType, TimeColumnType, DataTypeDate, IntervalKind::Kind::Quarter>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Quarter>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
case IntervalKind::Kind::Year: case IntervalKind::Kind::Year:
return execute<TimeDataType, TimeColumnType, DataTypeDate, IntervalKind::Kind::Year>(time_data_type, time_column, num_units, result_type, time_zone, input_rows_count, scale); return execute<ReturnType, TimeDataType, TimeColumnType, IntervalKind::Kind::Year>(time_data_type, time_column, num_units, origin_column, result_type, time_zone, scale);
} }
std::unreachable(); std::unreachable();
} }
template <typename TimeDataType, typename TimeColumnType, typename ResultDataType, IntervalKind::Kind unit> template <typename ResultDataType, typename TimeDataType, typename TimeColumnType, IntervalKind::Kind unit>
ColumnPtr execute( ColumnPtr execute(const TimeDataType &, const TimeColumnType & time_column_type, Int64 num_units, const ColumnWithTypeAndName & origin_column, const DataTypePtr & result_type, const DateLUTImpl & time_zone, UInt16 scale) const
const TimeDataType &, const TimeColumnType & time_column_type, Int64 num_units,
const DataTypePtr & result_type, const DateLUTImpl & time_zone, size_t input_rows_count, UInt16 scale) const
{ {
using ResultColumnType = typename ResultDataType::ColumnType; using ResultColumnType = typename ResultDataType::ColumnType;
using ResultFieldType = typename ResultDataType::FieldType;
const auto & time_data = time_column_type.getData(); const auto & time_data = time_column_type.getData();
size_t size = time_data.size();
auto result_col = result_type->createColumn(); auto result_col = result_type->createColumn();
auto * col_to = assert_cast<ResultColumnType *>(result_col.get()); auto * col_to = assert_cast<ResultColumnType *>(result_col.get());
auto & result_data = col_to->getData(); auto & result_data = col_to->getData();
result_data.resize(input_rows_count); result_data.resize(size);
Int64 scale_multiplier = DecimalUtils::scaleMultiplier<DateTime64>(scale); Int64 scale_multiplier = DecimalUtils::scaleMultiplier<DateTime64>(scale);
for (size_t i = 0; i != input_rows_count; ++i) if (origin_column.column) // Overload: Origin
result_data[i] = static_cast<ResultFieldType>(ToStartOfInterval<unit>::execute(time_data[i], num_units, time_zone, scale_multiplier)); {
const bool is_small_interval = (unit == IntervalKind::Kind::Nanosecond || unit == IntervalKind::Kind::Microsecond || unit == IntervalKind::Kind::Millisecond);
const bool is_result_date = isDateOrDate32(result_type);
Int64 result_scale = scale_multiplier;
Int64 origin_scale = 1;
if (isDateTime64(result_type)) /// We have origin scale only in case if arguments are DateTime64.
origin_scale = assert_cast<const DataTypeDateTime64 &>(*origin_column.type).getScaleMultiplier();
else if (!is_small_interval) /// In case of large interval and arguments are not DateTime64, we should not have scale in result.
result_scale = 1;
if (is_small_interval)
result_scale = assert_cast<const DataTypeDateTime64 &>(*result_type).getScaleMultiplier();
/// In case if we have a difference between time arguments and Interval, we need to calculate the difference between them
/// to get the right precision for the result. In case of large intervals, we should not have scale difference.
Int64 scale_diff = is_small_interval ? std::max(result_scale / origin_scale, origin_scale / result_scale) : 1;
static constexpr Int64 SECONDS_PER_DAY = 86'400;
UInt64 origin = origin_column.column->get64(0);
for (size_t i = 0; i != size; ++i)
{
UInt64 time_arg = time_data[i];
if (origin > static_cast<size_t>(time_arg))
throw Exception(ErrorCodes::BAD_ARGUMENTS, "The origin must be before the end date / date with time");
if (is_result_date) /// All internal calculations of ToStartOfInterval<...> expect arguments to be seconds or milli-, micro-, nanoseconds.
{
time_arg *= SECONDS_PER_DAY;
origin *= SECONDS_PER_DAY;
}
Int64 offset = ToStartOfInterval<unit>::execute(time_arg - origin, num_units, time_zone, result_scale, origin);
/// In case if arguments are DateTime64 with large interval, we should apply scale on it.
offset *= (!is_small_interval) ? result_scale : 1;
if (is_result_date) /// Convert back to date after calculations.
{
offset /= SECONDS_PER_DAY;
origin /= SECONDS_PER_DAY;
}
result_data[i] = 0;
result_data[i] += (result_scale < origin_scale) ? (origin + offset) / scale_diff : (origin + offset) * scale_diff;
}
}
else // Overload: Default
{
for (size_t i = 0; i != size; ++i)
result_data[i] = static_cast<typename ResultDataType::FieldType>(ToStartOfInterval<unit>::execute(time_data[i], num_units, time_zone, scale_multiplier));
}
return result_col; return result_col;
} }

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