Merge branch 'master' into Azure_write_buffer_parallel_upload

This commit is contained in:
Smita Kulkarni 2024-04-26 10:27:41 +02:00
commit 3ace11f92d
145 changed files with 2003 additions and 562 deletions

View File

@ -39,15 +39,8 @@ Every month we get together with the community (users, contributors, customers,
## Upcoming Events
Keep an eye out for upcoming meetups and eventsaround the world. Somewhere else you want us to be? Please feel free to reach out to tyler `<at>` clickhouse `<dot>` com. You can also peruse [ClickHouse Events](https://clickhouse.com/company/news-events) for a list of all upcoming trainings, meetups, speaking engagements, etc.
Keep an eye out for upcoming meetups and events around the world. Somewhere else you want us to be? Please feel free to reach out to tyler `<at>` clickhouse `<dot>` com. You can also peruse [ClickHouse Events](https://clickhouse.com/company/news-events) for a list of all upcoming trainings, meetups, speaking engagements, etc.
* [ClickHouse Meetup in Bellevue](https://www.meetup.com/clickhouse-seattle-user-group/events/298650371/) - Mar 11
* [ClickHouse Meetup at Ramp's Offices in NYC](https://www.meetup.com/clickhouse-new-york-user-group/events/298640542/) - Mar 19
* [ClickHouse Melbourne Meetup](https://www.meetup.com/clickhouse-australia-user-group/events/299479750/) - Mar 20
* [ClickHouse Meetup in Paris](https://www.meetup.com/clickhouse-france-user-group/events/298997115/) - Mar 21
* [ClickHouse Meetup in Bengaluru](https://www.meetup.com/clickhouse-bangalore-user-group/events/299479850/) - Mar 23
* [ClickHouse Meetup in Zurich](https://www.meetup.com/clickhouse-switzerland-meetup-group/events/299628922/) - Apr 16
* [ClickHouse Meetup in Copenhagen](https://www.meetup.com/clickhouse-denmark-meetup-group/events/299629133/) - Apr 23
* [ClickHouse Meetup in Dubai](https://www.meetup.com/clickhouse-dubai-meetup-group/events/299629189/) - May 28

View File

@ -66,9 +66,11 @@ public:
/// The thread and process ids are set.
Message(
const std::string & source, const std::string & text, Priority prio, const char * file, int line, std::string_view fmt_str = {});
const std::string & source, const std::string & text, Priority prio, const char * file, int line,
std::string_view fmt_str = {}, const std::vector<std::string> & fmt_str_args = {});
Message(
std::string && source, std::string && text, Priority prio, const char * file, int line, std::string_view fmt_str);
std::string && source, std::string && text, Priority prio, const char * file, int line,
std::string_view fmt_str, std::vector<std::string> && fmt_str_args);
/// Creates a Message with the given source, text, priority,
/// source file path and line.
///
@ -161,6 +163,9 @@ public:
std::string_view getFormatString() const;
void setFormatString(std::string_view fmt_str);
const std::vector<std::string> & getFormatStringArgs() const;
void setFormatStringArgs(const std::vector<std::string> & fmt_str_args);
int getSourceLine() const;
/// Returns the source file line of the statement
/// generating the log message. May be 0
@ -210,6 +215,7 @@ private:
int _line;
StringMap * _pMap;
std::string_view _fmt_str;
std::vector<std::string> _fmt_str_args;
};

View File

@ -46,7 +46,9 @@ Message::Message(const std::string& source, const std::string& text, Priority pr
}
Message::Message(const std::string& source, const std::string& text, Priority prio, const char* file, int line, std::string_view fmt_str):
Message::Message(
const std::string& source, const std::string& text, Priority prio, const char* file, int line,
std::string_view fmt_str, const std::vector<std::string>& fmt_str_args):
_source(source),
_text(text),
_prio(prio),
@ -54,13 +56,16 @@ Message::Message(const std::string& source, const std::string& text, Priority pr
_file(file),
_line(line),
_pMap(0),
_fmt_str(fmt_str)
_fmt_str(fmt_str),
_fmt_str_args(fmt_str_args)
{
init();
}
Message::Message(std::string && source, std::string && text, Priority prio, const char * file, int line, std::string_view fmt_str):
Message::Message(
std::string && source, std::string && text, Priority prio, const char * file, int line,
std::string_view fmt_str, std::vector<std::string> && fmt_str_args):
_source(std::move(source)),
_text(std::move(text)),
_prio(prio),
@ -68,7 +73,8 @@ Message::Message(std::string && source, std::string && text, Priority prio, cons
_file(file),
_line(line),
_pMap(0),
_fmt_str(fmt_str)
_fmt_str(fmt_str),
_fmt_str_args(std::move(fmt_str_args))
{
init();
}
@ -83,7 +89,8 @@ Message::Message(const Message& msg):
_pid(msg._pid),
_file(msg._file),
_line(msg._line),
_fmt_str(msg._fmt_str)
_fmt_str(msg._fmt_str),
_fmt_str_args(msg._fmt_str_args)
{
if (msg._pMap)
_pMap = new StringMap(*msg._pMap);
@ -102,7 +109,8 @@ Message::Message(const Message& msg, const std::string& text):
_pid(msg._pid),
_file(msg._file),
_line(msg._line),
_fmt_str(msg._fmt_str)
_fmt_str(msg._fmt_str),
_fmt_str_args(msg._fmt_str_args)
{
if (msg._pMap)
_pMap = new StringMap(*msg._pMap);
@ -154,6 +162,7 @@ void Message::swap(Message& msg)
swap(_line, msg._line);
swap(_pMap, msg._pMap);
swap(_fmt_str, msg._fmt_str);
swap(_fmt_str_args, msg._fmt_str_args);
}
@ -227,6 +236,17 @@ void Message::setFormatString(std::string_view fmt_str)
}
const std::vector<std::string>& Message::getFormatStringArgs() const
{
return _fmt_str_args;
}
void Message::setFormatStringArgs(const std::vector<std::string>& fmt_str_args)
{
_fmt_str_args = fmt_str_args;
}
bool Message::has(const std::string& param) const
{
return _pMap && (_pMap->find(param) != _pMap->end());

View File

@ -93,6 +93,7 @@ enable_language(ASM)
if(COMPILER_CLANG)
add_definitions(-Wno-unused-command-line-argument)
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fuse-ld=lld") # only relevant for -DENABLE_OPENSSL_DYNAMIC=1
endif()
if(ARCH_AMD64)
@ -146,7 +147,7 @@ elseif(ARCH_AARCH64)
else()
macro(perl_generate_asm FILE_IN FILE_OUT)
add_custom_command(OUTPUT ${FILE_OUT}
COMMAND /usr/bin/env perl ${FILE_IN} "linux64" ${FILE_OUT})
COMMAND ${CMAKE_COMMAND} -E env "CC=${CMAKE_CXX_COMPILER}" /usr/bin/env perl ${FILE_IN} "linux64" ${FILE_OUT})
endmacro()
perl_generate_asm(${OPENSSL_SOURCE_DIR}/crypto/aes/asm/aesv8-armx.pl ${OPENSSL_BINARY_DIR}/crypto/aes/aesv8-armx.S)
@ -175,7 +176,7 @@ elseif(ARCH_AARCH64)
elseif(ARCH_PPC64LE)
macro(perl_generate_asm FILE_IN FILE_OUT)
add_custom_command(OUTPUT ${FILE_OUT}
COMMAND /usr/bin/env perl ${FILE_IN} "linux64v2" ${FILE_OUT})
COMMAND ${CMAKE_COMMAND} -E env "CC=${CMAKE_CXX_COMPILER}" /usr/bin/env perl ${FILE_IN} "linux64v2" ${FILE_OUT})
endmacro()
perl_generate_asm(${OPENSSL_SOURCE_DIR}/crypto/aes/asm/aesp8-ppc.pl ${OPENSSL_BINARY_DIR}/crypto/aes/aesp8-ppc.s)
@ -185,7 +186,7 @@ elseif(ARCH_PPC64LE)
elseif(ARCH_S390X)
macro(perl_generate_asm FILE_IN FILE_OUT)
add_custom_command(OUTPUT ${FILE_OUT}
COMMAND /usr/bin/env perl ${FILE_IN} "linux64" ${FILE_OUT})
COMMAND ${CMAKE_COMMAND} -E env "CC=${CMAKE_CXX_COMPILER}" /usr/bin/env perl ${FILE_IN} "linux64" ${FILE_OUT})
endmacro()
perl_generate_asm(${OPENSSL_SOURCE_DIR}/crypto/aes/asm/aes-s390x.pl ${OPENSSL_BINARY_DIR}/crypto/aes/aes-s390x.S)
@ -193,7 +194,7 @@ elseif(ARCH_S390X)
elseif(ARCH_RISCV64)
macro(perl_generate_asm FILE_IN FILE_OUT)
add_custom_command(OUTPUT ${FILE_OUT}
COMMAND /usr/bin/env perl ${FILE_IN} "linux64" ${FILE_OUT})
COMMAND ${CMAKE_COMMAND} -E env "CC=${CMAKE_CXX_COMPILER}" /usr/bin/env perl ${FILE_IN} "linux64" ${FILE_OUT})
endmacro()
perl_generate_asm(${OPENSSL_SOURCE_DIR}/crypto/riscv64cpuid.pl ${OPENSSL_BINARY_DIR}/crypto/riscv64cpuid.S)
@ -960,11 +961,6 @@ set(CRYPTO_SRC
${OPENSSL_SOURCE_DIR}/crypto/x509/x_req.c
${OPENSSL_SOURCE_DIR}/crypto/x509/x_x509.c
${OPENSSL_SOURCE_DIR}/crypto/x509/x_x509a.c
${OPENSSL_SOURCE_DIR}/engines/e_capi.c
${OPENSSL_SOURCE_DIR}/engines/e_dasync.c
${OPENSSL_SOURCE_DIR}/engines/e_loader_attic.c
${OPENSSL_SOURCE_DIR}/engines/e_ossltest.c
${OPENSSL_SOURCE_DIR}/engines/e_padlock.c
${OPENSSL_SOURCE_DIR}/providers/baseprov.c
${OPENSSL_SOURCE_DIR}/providers/common/bio_prov.c
${OPENSSL_SOURCE_DIR}/providers/common/capabilities.c
@ -985,8 +981,6 @@ set(CRYPTO_SRC
${OPENSSL_SOURCE_DIR}/providers/common/securitycheck.c
${OPENSSL_SOURCE_DIR}/providers/common/securitycheck_default.c
${OPENSSL_SOURCE_DIR}/providers/defltprov.c
${OPENSSL_SOURCE_DIR}/providers/fips/fips_entry.c
${OPENSSL_SOURCE_DIR}/providers/fips/fipsprov.c
${OPENSSL_SOURCE_DIR}/providers/implementations/asymciphers/rsa_enc.c
${OPENSSL_SOURCE_DIR}/providers/implementations/asymciphers/sm2_enc.c
${OPENSSL_SOURCE_DIR}/providers/implementations/ciphers/cipher_aes.c
@ -1145,11 +1139,19 @@ set(CRYPTO_SRC
${OPENSSL_SOURCE_DIR}/providers/implementations/signature/sm2_sig.c
${OPENSSL_SOURCE_DIR}/providers/implementations/storemgmt/file_store.c
${OPENSSL_SOURCE_DIR}/providers/implementations/storemgmt/file_store_any2obj.c
${OPENSSL_SOURCE_DIR}/providers/legacyprov.c
${OPENSSL_SOURCE_DIR}/providers/nullprov.c
${OPENSSL_SOURCE_DIR}/providers/prov_running.c
${OPENSSL_SOURCE_DIR}/ssl/record/methods/tls_pad.c
${OPENSSL_SOURCE_DIR}/ssl/record/methods/ssl3_cbc.c
)
if(NOT ENABLE_OPENSSL_DYNAMIC)
set(CRYPTO_SRC ${CRYPTO_SRC}
${OPENSSL_SOURCE_DIR}/providers/fips/fips_entry.c
${OPENSSL_SOURCE_DIR}/providers/fips/fipsprov.c
)
endif()
if(ARCH_AMD64)
if (OS_DARWIN)
set(CRYPTO_SRC ${CRYPTO_SRC}
@ -1376,8 +1378,6 @@ set(SSL_SRC
${OPENSSL_SOURCE_DIR}/ssl/quic/uint_set.c
${OPENSSL_SOURCE_DIR}/ssl/record/rec_layer_d1.c
${OPENSSL_SOURCE_DIR}/ssl/record/rec_layer_s3.c
${OPENSSL_SOURCE_DIR}/ssl/record/methods/tls_pad.c
${OPENSSL_SOURCE_DIR}/ssl/record/methods/ssl3_cbc.c
${OPENSSL_SOURCE_DIR}/ssl/record/methods/dtls_meth.c
${OPENSSL_SOURCE_DIR}/ssl/record/methods/ssl3_meth.c
${OPENSSL_SOURCE_DIR}/ssl/record/methods/tls13_meth.c

View File

@ -44,8 +44,6 @@ source /utils.lib
if [[ -n "$USE_DATABASE_REPLICATED" ]] && [[ "$USE_DATABASE_REPLICATED" -eq 1 ]]; then
echo "Azure is disabled"
elif [[ -n "$USE_SHARED_CATALOG" ]] && [[ "$USE_SHARED_CATALOG" -eq 1 ]]; then
echo "Azure is disabled"
else
azurite-blob --blobHost 0.0.0.0 --blobPort 10000 --debug /azurite_log &
fi

View File

@ -304,10 +304,10 @@ We use the term `MergeTree` to refer to all table engines in the `MergeTree fami
If you had a `MergeTree` table that was manually replicated, you can convert it to a replicated table. You might need to do this if you have already collected a large amount of data in a `MergeTree` table and now you want to enable replication.
`MergeTree` table can be automatically converted on server restart if `convert_to_replicated` flag is set at the table's data directory (`/var/lib/clickhouse/store/xxx/xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/` for `Atomic` database).
`MergeTree` table can be automatically converted on server restart if `convert_to_replicated` flag is set at the table's data directory (`/store/xxx/xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/` for `Atomic` database).
Create empty `convert_to_replicated` file and the table will be loaded as replicated on next server restart.
This query can be used to get the table's data path.
This query can be used to get the table's data path. If table has many data paths, you have to use the first one.
```sql
SELECT data_paths FROM system.tables WHERE table = 'table_name' AND database = 'database_name';

View File

@ -76,7 +76,7 @@ ClickHouse, Inc. does **not** maintain the tools and libraries listed below and
- [clickhouse-maxmind-geoip](https://github.com/AlexeyKupershtokh/clickhouse-maxmind-geoip)
- AutoML
- [MindsDB](https://mindsdb.com/)
- [MindsDB](https://github.com/mindsdb/mindsdb) - Predictive AI layer for ClickHouse database.
- [MindsDB](https://github.com/mindsdb/mindsdb) - Integrates with ClickHouse, making data from ClickHouse accessible to a diverse range of AI/ML models.
## Programming Language Ecosystems {#programming-language-ecosystems}

View File

@ -523,7 +523,7 @@ See settings `cgroups_memory_usage_observer_wait_time` and `cgroup_memory_watche
Type: Double
Default: 0.95
Default: 0.9
## max_table_size_to_drop

View File

@ -30,6 +30,16 @@ Columns:
- `source_file` (LowCardinality(String)) — Source file from which the logging was done.
- `source_line` (UInt64) — Source line from which the logging was done.
- `message_format_string` (LowCardinality(String)) — A format string that was used to format the message.
- `value1` (String) - Argument 1 that was used to format the message.
- `value2` (String) - Argument 2 that was used to format the message.
- `value3` (String) - Argument 3 that was used to format the message.
- `value4` (String) - Argument 4 that was used to format the message.
- `value5` (String) - Argument 5 that was used to format the message.
- `value6` (String) - Argument 6 that was used to format the message.
- `value7` (String) - Argument 7 that was used to format the message.
- `value8` (String) - Argument 8 that was used to format the message.
- `value9` (String) - Argument 9 that was used to format the message.
- `value10` (String) - Argument 10 that was used to format the message.
**Example**
@ -55,4 +65,14 @@ revision: 54440
source_file: /ClickHouse/src/Interpreters/DNSCacheUpdater.cpp; void DB::DNSCacheUpdater::start()
source_line: 45
message_format_string: Update period {} seconds
value1: 15
value2:
value3:
value4:
value5:
value6:
value7:
value8:
value9:
value10:
```

View File

@ -1907,6 +1907,12 @@ If the addition results in a value outside the bounds of the data type, the resu
date_add(unit, value, date)
```
Alternative syntax:
``` sql
date_add(date, INTERVAL value unit)
```
Aliases: `dateAdd`, `DATE_ADD`.
**Arguments**
@ -1946,6 +1952,20 @@ Result:
└───────────────────────────────────────────────┘
```
```sql
SELECT date_add(toDate('2018-01-01'), INTERVAL 3 YEAR);
```
Result:
```text
┌─plus(toDate('2018-01-01'), toIntervalYear(3))─┐
│ 2021-01-01 │
└───────────────────────────────────────────────┘
```
**See Also**
- [addDate](#addDate)
@ -1962,6 +1982,13 @@ If the subtraction results in a value outside the bounds of the data type, the r
date_sub(unit, value, date)
```
Alternative syntax:
``` sql
date_sub(date, INTERVAL value unit)
```
Aliases: `dateSub`, `DATE_SUB`.
**Arguments**
@ -2002,6 +2029,19 @@ Result:
└────────────────────────────────────────────────┘
```
``` sql
SELECT date_sub(toDate('2018-01-01'), INTERVAL 3 YEAR);
```
Result:
``` text
┌─minus(toDate('2018-01-01'), toIntervalYear(3))─┐
│ 2015-01-01 │
└────────────────────────────────────────────────┘
```
**See Also**
- [subDate](#subDate)

View File

@ -8,7 +8,7 @@ sidebar_label: Mathematical
## e
Returns e ([Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant))
Returns e ([Euler's constant](https://en.wikipedia.org/wiki/Euler%27s_constant)).
**Syntax**
@ -45,7 +45,7 @@ exp(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -65,7 +65,7 @@ Alias: `ln(x)`
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -83,7 +83,7 @@ exp2(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -111,7 +111,7 @@ log2(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -129,7 +129,7 @@ exp10(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -157,7 +157,7 @@ log10(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -173,7 +173,7 @@ sqrt(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -189,7 +189,7 @@ cbrt(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -207,7 +207,7 @@ erf(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -239,7 +239,7 @@ erfc(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -257,7 +257,7 @@ lgamma(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -275,7 +275,7 @@ gamma(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -293,7 +293,7 @@ sin(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -323,7 +323,7 @@ cos(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -341,7 +341,7 @@ tan(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -359,7 +359,7 @@ asin(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -377,7 +377,7 @@ acos(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -395,7 +395,7 @@ atan(x)
**Arguments**
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md)
- `x` - [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -434,7 +434,7 @@ cosh(x)
**Arguments**
- `x` — The angle, in radians. Values from the interval: `-∞ < x < +∞`. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — The angle, in radians. Values from the interval: `-∞ < x < +∞`. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -468,7 +468,7 @@ acosh(x)
**Arguments**
- `x` — Hyperbolic cosine of angle. Values from the interval: `1 <= x < +∞`. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — Hyperbolic cosine of angle. Values from the interval: `1 <= x < +∞`. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -502,7 +502,7 @@ sinh(x)
**Arguments**
- `x` — The angle, in radians. Values from the interval: `-∞ < x < +∞`. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — The angle, in radians. Values from the interval: `-∞ < x < +∞`. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -536,7 +536,7 @@ asinh(x)
**Arguments**
- `x` — Hyperbolic sine of angle. Values from the interval: `-∞ < x < +∞`. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — Hyperbolic sine of angle. Values from the interval: `-∞ < x < +∞`. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -557,6 +557,37 @@ Result:
│ 0 │
└──────────┘
```
## tanh
Returns the [hyperbolic tangent](https://www.mathworks.com/help/matlab/ref/tanh.html).
**Syntax**
``` sql
tanh(x)
```
**Arguments**
- `x` — The angle, in radians. Values from the interval: `-∞ < x < +∞`. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
- Values from the interval: `-1 < tanh(x) < 1`.
Type: [Float*](../../sql-reference/data-types/float.md#float32-float64).
**Example**
``` sql
SELECT tanh(0);
```
Result:
```result
0
```
## atanh
@ -570,7 +601,7 @@ atanh(x)
**Arguments**
- `x` — Hyperbolic tangent of angle. Values from the interval: `1 < x < 1`. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — Hyperbolic tangent of angle. Values from the interval: `1 < x < 1`. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -604,8 +635,8 @@ atan2(y, x)
**Arguments**
- `y` — y-coordinate of the point through which the ray passes. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — x-coordinate of the point through which the ray passes. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `y` — y-coordinate of the point through which the ray passes. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md).
- `x` — x-coordinate of the point through which the ray passes. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md).
**Returned value**
@ -639,8 +670,8 @@ hypot(x, y)
**Arguments**
- `x` — The first cathetus of a right-angle triangle. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `y` — The second cathetus of a right-angle triangle. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — The first cathetus of a right-angle triangle. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md).
- `y` — The second cathetus of a right-angle triangle. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md).
**Returned value**
@ -674,7 +705,7 @@ log1p(x)
**Arguments**
- `x` — Values from the interval: `-1 < x < +∞`. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — Values from the interval: `-1 < x < +∞`. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -716,6 +747,8 @@ sign(x)
- 0 for `x = 0`
- 1 for `x > 0`
Type: [Int8](../../sql-reference/data-types/int-uint.md).
**Examples**
Sign for the zero value:
@ -772,7 +805,7 @@ degrees(x)
**Arguments**
- `x` — Input in radians. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — Input in radians. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**
@ -806,7 +839,7 @@ radians(x)
**Arguments**
- `x` — Input in degrees. [Float64](../../sql-reference/data-types/float.md#float32-float64).
- `x` — Input in degrees. [(U)Int*](../../sql-reference/data-types/int-uint.md), [Float*](../../sql-reference/data-types/float.md) or [Decimal*](../../sql-reference/data-types/decimal.md).
**Returned value**

View File

@ -88,20 +88,93 @@ Result:
## length
Returns the length of a string in bytes (not: in characters or Unicode code points).
The function also works for arrays.
Returns the length of a string in bytes rather than in characters or Unicode code points. The function also works for arrays.
Alias: `OCTET_LENGTH`
**Syntax**
```sql
length(s)
```
**Parameters**
- `s`: An input string or array. [String](../data-types/string)/[Array](../data-types/array).
**Returned value**
- Length of the string or array `s` in bytes. [UInt64](../data-types/int-uint).
**Example**
Query:
```sql
SELECT length('Hello, world!');
```
Result:
```response
┌─length('Hello, world!')─┐
│ 13 │
└─────────────────────────┘
```
Query:
```sql
SELECT length([1, 2, 3, 4]);
```
Result:
```response
┌─length([1, 2, 3, 4])─┐
│ 4 │
└──────────────────────┘
```
## lengthUTF8
Returns the length of a string in Unicode code points (not: in bytes or characters). It assumes that the string contains valid UTF-8 encoded text. If this assumption is violated, no exception is thrown and the result is undefined.
Returns the length of a string in Unicode code points rather than in bytes or characters. It assumes that the string contains valid UTF-8 encoded text. If this assumption is violated, no exception is thrown and the result is undefined.
Alias:
Aliases:
- `CHAR_LENGTH`
- `CHARACTER_LENGTH`
**Syntax**
```sql
lengthUTF8(s)
```
**Parameters**
- `s`: String containing valid UTF-8 encoded text. [String](../data-types/string).
**Returned value**
- Length of the string `s` in Unicode code points. [UInt64](../data-types/int-uint.md).
**Example**
Query:
```sql
SELECT lengthUTF8('Здравствуй, мир!');
```
Result:
```response
┌─lengthUTF8('Здравствуй, мир!')─┐
│ 16 │
└────────────────────────────────┘
```
## left
Returns a substring of string `s` with a specified `offset` starting from the left.
@ -1055,6 +1128,34 @@ Result:
Like `base58Decode` but returns an empty string in case of error.
**Syntax**
```sql
tryBase58Decode(encoded)
```
**Parameters**
- `encoded`: [String](../../sql-reference/data-types/string.md) column or constant. If the string is not a valid Base58-encoded value, returns an empty string in case of error.
**Returned value**
- A string containing the decoded value of the argument.
**Examples**
Query:
```sql
SELECT tryBase58Decode('3dc8KtHrwM') as res;
```
```response
┌─res─────┐
│ Encoded │
└─────────┘
```
## base64Encode
Encodes a String or FixedString as base64.
@ -1071,6 +1172,30 @@ Alias: `FROM_BASE64`.
Like `base64Decode` but returns an empty string in case of error.
**Syntax**
```sql
tryBase64Decode(encoded)
```
**Parameters**
- `encoded`: [String](../../sql-reference/data-types/string.md) column or constant. If the string is not a valid Base58-encoded value, returns an empty string in case of error.
**Examples**
Query:
```sql
SELECT tryBase64Decode('RW5jb2RlZA==') as res;
```
```response
┌─res─────┐
│ Encoded │
└─────────┘
```
## endsWith {#endswith}
Returns whether string `str` ends with `suffix`.

View File

@ -1322,9 +1322,9 @@ Result:
## countSubstrings
Returns how often substring `needle` occurs in string `haystack`.
Returns how often a substring `needle` occurs in a string `haystack`.
Functions `countSubstringsCaseInsensitive` and `countSubstringsCaseInsensitiveUTF8` provide a case-insensitive and case-insensitive + UTF-8 variants of this function.
Functions [`countSubstringsCaseInsensitive`](#countsubstringscaseinsensitive) and [`countSubstringsCaseInsensitiveUTF8`](#countsubstringscaseinsensitiveutf8) provide case-insensitive and case-insensitive + UTF-8 variants of this function respectively.
**Syntax**
@ -1371,6 +1371,113 @@ Result:
│ 1 │
└────────────────────────────────────────┘
```
## countSubstringsCaseInsensitive
Returns how often a substring `needle` occurs in a string `haystack`. Ignores case.
**Syntax**
``` sql
countSubstringsCaseInsensitive(haystack, needle[, start_pos])
```
**Arguments**
- `haystack` — String in which the search is performed. [String](../../sql-reference/syntax.md#syntax-string-literal).
- `needle` — Substring to be searched. [String](../../sql-reference/syntax.md#syntax-string-literal).
- `start_pos` Position (1-based) in `haystack` at which the search starts. [UInt](../../sql-reference/data-types/int-uint.md). Optional.
**Returned values**
- The number of occurrences.
Type: [UInt64](../../sql-reference/data-types/int-uint.md).
**Examples**
Query:
``` sql
SELECT countSubstringsCaseInsensitive('AAAA', 'aa');
```
Result:
``` text
┌─countSubstringsCaseInsensitive('AAAA', 'aa')─┐
│ 2 │
└──────────────────────────────────────────────┘
```
Example with `start_pos` argument:
Query:
```sql
SELECT countSubstringsCaseInsensitive('abc___ABC___abc', 'abc', 4);
```
Result:
``` text
┌─countSubstringsCaseInsensitive('abc___ABC___abc', 'abc', 4)─┐
│ 2 │
└─────────────────────────────────────────────────────────────┘
```
## countSubstringsCaseInsensitiveUTF8
Returns how often a substring `needle` occurs in a string `haystack`. Ignores case and assumes that `haystack` is a UTF8 string.
**Syntax**
``` sql
countSubstringsCaseInsensitiveUTF8(haystack, needle[, start_pos])
```
**Arguments**
- `haystack` — UTF-8 string in which the search is performed. [String](../../sql-reference/syntax.md#syntax-string-literal).
- `needle` — Substring to be searched. [String](../../sql-reference/syntax.md#syntax-string-literal).
- `start_pos` Position (1-based) in `haystack` at which the search starts. [UInt](../../sql-reference/data-types/int-uint.md). Optional.
**Returned values**
- The number of occurrences.
Type: [UInt64](../../sql-reference/data-types/int-uint.md).
**Examples**
Query:
``` sql
SELECT countSubstringsCaseInsensitiveUTF8('ложка, кошка, картошка', 'КА');
```
Result:
``` text
┌─countSubstringsCaseInsensitiveUTF8('ложка, кошка, картошка', 'КА')─┐
│ 4 │
└────────────────────────────────────────────────────────────────────┘
```
Example with `start_pos` argument:
Query:
```sql
SELECT countSubstringsCaseInsensitiveUTF8('ложка, кошка, картошка', 'КА', 13);
```
Result:
``` text
┌─countSubstringsCaseInsensitiveUTF8('ложка, кошка, картошка', 'КА', 13)─┐
│ 2 │
└────────────────────────────────────────────────────────────────────────┘
```
## countMatches
@ -1421,7 +1528,40 @@ Result:
## countMatchesCaseInsensitive
Like `countMatches(haystack, pattern)` but matching ignores the case.
Returns the number of regular expression matches for a pattern in a haystack like [`countMatches`](#countmatches) but matching ignores the case.
**Syntax**
``` sql
countMatchesCaseInsensitive(haystack, pattern)
```
**Arguments**
- `haystack` — The string to search in. [String](../../sql-reference/syntax.md#syntax-string-literal).
- `pattern` — The regular expression with [re2 syntax](https://github.com/google/re2/wiki/Syntax). [String](../../sql-reference/data-types/string.md).
**Returned value**
- The number of matches.
Type: [UInt64](../../sql-reference/data-types/int-uint.md).
**Examples**
Query:
``` sql
SELECT countMatchesCaseInsensitive('AAAA', 'aa');
```
Result:
``` text
┌─countMatchesCaseInsensitive('AAAA', 'aa')────┐
│ 2 │
└──────────────────────────────────────────────┘
```
## regexpExtract

View File

@ -817,6 +817,42 @@ Result:
└─────────────────────────────────────┘
```
## flattenTuple
Returns a flattened `output` tuple from a nested named `input` tuple. Elements of the `output` tuple are the paths from the original `input` tuple. For instance: `Tuple(a Int, Tuple(b Int, c Int)) -> Tuple(a Int, b Int, c Int)`. `flattenTuple` can be used to select all paths from type `Object` as separate columns.
**Syntax**
```sql
flattenTuple(input)
```
**Parameters**
- `input`: Nested named tuple to flatten. [Tuple](../data-types/tuple).
**Returned value**
- `output` tuple whose elements are paths from the original `input`. [Tuple](../data-types/tuple).
**Example**
Query:
``` sql
CREATE TABLE t_flatten_tuple(t Tuple(t1 Nested(a UInt32, s String), b UInt32, t2 Tuple(k String, v UInt32))) ENGINE = Memory;
INSERT INTO t_flatten_tuple VALUES (([(1, 'a'), (2, 'b')], 3, ('c', 4)));
SELECT flattenTuple(t) FROM t_flatten_tuple;
```
Result:
``` text
┌─flattenTuple(t)───────────┐
│ ([1,2],['a','b'],3,'c',4) │
└───────────────────────────┘
```
## Distance functions
All supported functions are described in [distance functions documentation](../../sql-reference/functions/distance-functions.md).

View File

@ -21,6 +21,8 @@ SELECT [DISTINCT [ON (column1, column2, ...)]] expr_list
[WHERE expr]
[GROUP BY expr_list] [WITH ROLLUP|WITH CUBE] [WITH TOTALS]
[HAVING expr]
[WINDOW window_expr_list]
[QUALIFY expr]
[ORDER BY expr_list] [WITH FILL] [FROM expr] [TO expr] [STEP expr] [INTERPOLATE [(expr_list)]]
[LIMIT [offset_value, ]n BY columns]
[LIMIT [n, ]m] [WITH TIES]
@ -45,6 +47,7 @@ Specifics of each optional clause are covered in separate sections, which are li
- [GROUP BY clause](../../../sql-reference/statements/select/group-by.md)
- [LIMIT BY clause](../../../sql-reference/statements/select/limit-by.md)
- [HAVING clause](../../../sql-reference/statements/select/having.md)
- [QUALIFY clause](../../../sql-reference/statements/select/qualify.md)
- [LIMIT clause](../../../sql-reference/statements/select/limit.md)
- [OFFSET clause](../../../sql-reference/statements/select/offset.md)
- [UNION clause](../../../sql-reference/statements/select/union.md)

View File

@ -0,0 +1,34 @@
---
slug: /en/sql-reference/statements/select/qualify
sidebar_label: QUALIFY
---
# QUALIFY Clause
Allows filtering window functions results. It is similar to the [WHERE](../../../sql-reference/statements/select/where.md) clause, but the difference is that `WHERE` is performed before window functions evaluation, while `QUALIFY` is performed after it.
It is possible to reference window functions results from `SELECT` clause in `QUALIFY` clause by their alias. Alternatively, `QUALIFY` clause can filter on results of additional window functions that are not returned in query results.
## Limitations
`QUALIFY` cant be used if there are no window functions to evaluate. Use `WHERE` instead.
## Examples
Example:
``` sql
SELECT number, COUNT() OVER (PARTITION BY number % 3) AS partition_count
FROM numbers(10)
QUALIFY partition_count = 4
ORDER BY number;
```
``` text
┌─number─┬─partition_count─┐
│ 0 │ 4 │
│ 3 │ 4 │
│ 6 │ 4 │
│ 9 │ 4 │
└────────┴─────────────────┘
```

View File

@ -29,6 +29,16 @@ slug: /ru/operations/system-tables/text_log
- `source_file` (LowCardinality(String)) — исходный файл, из которого была сделана запись.
- `source_line` (UInt64) — исходная строка, из которой была сделана запись.
- `message_format_string` (LowCardinality(String)) — форматная строка, с помощью которой было отформатировано сообщение.
- `value1` (String) - аргумент 1, который использовался для форматирования сообщения.
- `value2` (String) - аргумент 2, который использовался для форматирования сообщения.
- `value3` (String) - аргумент 3, который использовался для форматирования сообщения.
- `value4` (String) - аргумент 4, который использовался для форматирования сообщения.
- `value5` (String) - аргумент 5, который использовался для форматирования сообщения.
- `value6` (String) - аргумент 6, который использовался для форматирования сообщения.
- `value7` (String) - аргумент 7, который использовался для форматирования сообщения.
- `value8` (String) - аргумент 8, который использовался для форматирования сообщения.
- `value9` (String) - аргумент 9, который использовался для форматирования сообщения.
- `value10` (String) - аргумент 10, который использовался для форматирования сообщения.
**Пример**
@ -53,4 +63,14 @@ revision: 54440
source_file: /ClickHouse/src/Interpreters/DNSCacheUpdater.cpp; void DB::DNSCacheUpdater::start()
source_line: 45
message_format_string: Update period {} seconds
value1: 15
value2:
value3:
value4:
value5:
value6:
value7:
value8:
value9:
value10:
```

View File

@ -729,6 +729,15 @@ int mainEntryClickHouseInstall(int argc, char ** argv)
}
}
/// Don't allow relative paths because install script may cd to / when installing
/// And having path=./ may break the system
if (log_path.is_relative())
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Log path is relative: {}", log_path.string());
if (data_path.is_relative())
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Data path is relative: {}", data_path.string());
if (pid_path.is_relative())
throw Exception(ErrorCodes::BAD_ARGUMENTS, "Pid path is relative: {}", pid_path.string());
/// Create directories for data and log.
if (fs::exists(log_path))

View File

@ -742,9 +742,9 @@
It also enables 'changeable_in_readonly' constraint type -->
<settings_constraints_replace_previous>true</settings_constraints_replace_previous>
<!-- By default, for backward compatibility create table with a specific table engine ignores grant,
<!-- By default, for backward compatibility creating table with a specific table engine ignores grant,
however you can change this behaviour by setting this to true -->
<table_engines_require_grant>true</table_engines_require_grant>
<table_engines_require_grant>false</table_engines_require_grant>
<!-- Number of seconds since last access a role is stored in the Role Cache -->
<role_cache_expiration_time_seconds>600</role_cache_expiration_time_seconds>

View File

@ -7,6 +7,10 @@ max-branches=50
max-nested-blocks=10
max-statements=200
[tool.isort]
profile = "black"
src_paths = ["src", "tests/ci", "tests/sqllogic"]
[tool.pylint.FORMAT]
#ignore-long-lines = (# )?<?https?://\S+>?$

View File

@ -607,7 +607,8 @@ AuthResult AccessControl::authenticate(const Credentials & credentials, const Po
/// We use the same message for all authentication failures because we don't want to give away any unnecessary information for security reasons,
/// only the log will show the exact reason.
throw Exception(PreformattedMessage{message.str(),
"{}: Authentication failed: password is incorrect, or there is no user with such name.{}"},
"{}: Authentication failed: password is incorrect, or there is no user with such name.{}",
std::vector<std::string>{credentials.getUserName()}},
ErrorCodes::AUTHENTICATION_FAILED);
}
}

View File

@ -56,7 +56,9 @@ void IdentifierNode::updateTreeHashImpl(HashState & state, CompareOptions) const
QueryTreeNodePtr IdentifierNode::cloneImpl() const
{
return std::make_shared<IdentifierNode>(identifier);
auto clone_identifier_node = std::make_shared<IdentifierNode>(identifier);
clone_identifier_node->table_expression_modifiers = table_expression_modifiers;
return clone_identifier_node;
}
ASTPtr IdentifierNode::toASTImpl(const ConvertToASTOptions & /* options */) const

View File

@ -7895,6 +7895,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
if (query_node_typed.hasHaving() && query_node_typed.isGroupByWithTotals() && is_rollup_or_cube)
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "WITH TOTALS and WITH ROLLUP or CUBE are not supported together in presence of HAVING");
if (query_node_typed.hasQualify() && query_node_typed.isGroupByWithTotals() && is_rollup_or_cube)
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "WITH TOTALS and WITH ROLLUP or CUBE are not supported together in presence of QUALIFY");
/// Initialize aliases in query node scope
QueryExpressionsAliasVisitor visitor(scope);
@ -7919,6 +7922,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
if (query_node_typed.hasWindow())
visitor.visit(query_node_typed.getWindowNode());
if (query_node_typed.hasQualify())
visitor.visit(query_node_typed.getQualify());
if (query_node_typed.hasOrderBy())
visitor.visit(query_node_typed.getOrderByNode());
@ -8067,6 +8073,9 @@ void QueryAnalyzer::resolveQuery(const QueryTreeNodePtr & query_node, Identifier
if (query_node_typed.hasWindow())
resolveWindowNodeList(query_node_typed.getWindowNode(), scope);
if (query_node_typed.hasQualify())
resolveExpressionNode(query_node_typed.getQualify(), scope, false /*allow_lambda_expression*/, false /*allow_table_expression*/);
if (query_node_typed.hasOrderBy())
{
replaceNodesWithPositionalArguments(query_node_typed.getOrderByNode(), query_node_typed.getProjection().getNodes(), scope);

View File

@ -51,10 +51,15 @@ public:
{
const auto & second_const_value = second_const_node->getValue();
if (second_const_value.isNull()
|| (lower_name == "sum" && isInt64OrUInt64FieldType(second_const_value.getType()) && second_const_value.get<UInt64>() == 0))
|| (lower_name == "sum" && isInt64OrUInt64FieldType(second_const_value.getType()) && second_const_value.get<UInt64>() == 0
&& !function_node->getResultType()->isNullable()))
{
/// avg(if(cond, a, null)) -> avgIf(a, cond)
/// avg(if(cond, nullable_a, null)) -> avgIfOrNull(a, cond)
/// sum(if(cond, a, 0)) -> sumIf(a, cond)
/// sum(if(cond, nullable_a, 0)) **is not** equivalent to sumIfOrNull(cond, nullable_a) as
/// it changes the output when no rows pass the condition (from 0 to NULL)
function_arguments_nodes.resize(2);
function_arguments_nodes[0] = std::move(if_arguments_nodes[1]);
function_arguments_nodes[1] = std::move(if_arguments_nodes[0]);
@ -66,10 +71,13 @@ public:
{
const auto & first_const_value = first_const_node->getValue();
if (first_const_value.isNull()
|| (lower_name == "sum" && isInt64OrUInt64FieldType(first_const_value.getType()) && first_const_value.get<UInt64>() == 0))
|| (lower_name == "sum" && isInt64OrUInt64FieldType(first_const_value.getType()) && first_const_value.get<UInt64>() == 0
&& !function_node->getResultType()->isNullable()))
{
/// avg(if(cond, null, a) -> avgIf(a, !cond))
/// avg(if(cond, null, a) -> avgIfOrNullable(a, !cond))
/// sum(if(cond, 0, a) -> sumIf(a, !cond))
/// sum(if(cond, 0, nullable_a) **is not** sumIf(a, !cond)) -> Same as above
auto not_function = std::make_shared<FunctionNode>("not");
auto & not_function_arguments = not_function->getArguments().getNodes();
not_function_arguments.push_back(std::move(if_arguments_nodes[0]));

View File

@ -197,6 +197,12 @@ void QueryNode::dumpTreeImpl(WriteBuffer & buffer, FormatState & format_state, s
getWindow().dumpTreeImpl(buffer, format_state, indent + 4);
}
if (hasQualify())
{
buffer << '\n' << std::string(indent + 2, ' ') << "QUALIFY\n";
getQualify()->dumpTreeImpl(buffer, format_state, indent + 4);
}
if (hasOrderBy())
{
buffer << '\n' << std::string(indent + 2, ' ') << "ORDER BY\n";
@ -381,6 +387,9 @@ ASTPtr QueryNode::toASTImpl(const ConvertToASTOptions & options) const
if (hasWindow())
select_query->setExpression(ASTSelectQuery::Expression::WINDOW, getWindow().toAST(options));
if (hasQualify())
select_query->setExpression(ASTSelectQuery::Expression::QUALIFY, getQualify()->toAST(options));
if (hasOrderBy())
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, getOrderBy().toAST(options));

View File

@ -416,6 +416,24 @@ public:
return children[window_child_index];
}
/// Returns true if query node QUALIFY section is not empty, false otherwise
bool hasQualify() const
{
return getQualify() != nullptr;
}
/// Get QUALIFY section node
const QueryTreeNodePtr & getQualify() const
{
return children[qualify_child_index];
}
/// Get QUALIFY section node
QueryTreeNodePtr & getQualify()
{
return children[qualify_child_index];
}
/// Returns true if query node ORDER BY section is not empty, false otherwise
bool hasOrderBy() const
{
@ -622,13 +640,14 @@ private:
static constexpr size_t group_by_child_index = 5;
static constexpr size_t having_child_index = 6;
static constexpr size_t window_child_index = 7;
static constexpr size_t order_by_child_index = 8;
static constexpr size_t interpolate_child_index = 9;
static constexpr size_t limit_by_limit_child_index = 10;
static constexpr size_t limit_by_offset_child_index = 11;
static constexpr size_t limit_by_child_index = 12;
static constexpr size_t limit_child_index = 13;
static constexpr size_t offset_child_index = 14;
static constexpr size_t qualify_child_index = 8;
static constexpr size_t order_by_child_index = 9;
static constexpr size_t interpolate_child_index = 10;
static constexpr size_t limit_by_limit_child_index = 11;
static constexpr size_t limit_by_offset_child_index = 12;
static constexpr size_t limit_by_child_index = 13;
static constexpr size_t limit_child_index = 14;
static constexpr size_t offset_child_index = 15;
static constexpr size_t children_size = offset_child_index + 1;
};

View File

@ -330,6 +330,10 @@ QueryTreeNodePtr QueryTreeBuilder::buildSelectExpression(const ASTPtr & select_q
if (window_list)
current_query_tree->getWindowNode() = buildWindowList(window_list, current_context);
auto qualify_expression = select_query_typed.qualify();
if (qualify_expression)
current_query_tree->getQualify() = buildExpression(qualify_expression, current_context);
auto select_order_by_list = select_query_typed.orderBy();
if (select_order_by_list)
current_query_tree->getOrderByNode() = buildSortList(select_order_by_list, current_context);

View File

@ -56,6 +56,9 @@ void validateFilters(const QueryTreeNodePtr & query_node)
if (query_node_typed.hasHaving())
validateFilter(query_node_typed.getHaving(), "HAVING", query_node);
if (query_node_typed.hasQualify())
validateFilter(query_node_typed.getQualify(), "QUALIFY", query_node);
}
namespace

View File

@ -47,7 +47,7 @@ Suggest::Suggest()
"GRANT", "REVOKE", "OPTION", "ADMIN", "EXCEPT", "REPLACE", "IDENTIFIED", "HOST",
"NAME", "READONLY", "WRITABLE", "PERMISSIVE", "FOR", "RESTRICTIVE", "RANDOMIZED", "INTERVAL",
"LIMITS", "ONLY", "TRACKING", "IP", "REGEXP", "ILIKE", "CLEANUP", "APPEND",
"IGNORE NULLS", "RESPECT NULLS", "OVER", "PASTE"});
"IGNORE NULLS", "RESPECT NULLS", "OVER", "PASTE", "WINDOW", "QUALIFY"});
}
static String getLoadSuggestionQuery(Int32 suggestion_limit, bool basic_suggestion)

View File

@ -154,7 +154,7 @@ public:
void updatePermutation(PermutationSortDirection, PermutationSortStability,
size_t, int, Permutation &, EqualRanges &) const override
{
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method getPermutation is not supported for ColumnUnique.");
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Method updatePermutation is not supported for ColumnUnique.");
}
std::vector<MutableColumnPtr> scatter(IColumn::ColumnIndex, const IColumn::Selector &) const override

View File

@ -77,7 +77,7 @@ void CgroupsMemoryUsageObserver::setMemoryUsageLimits(uint64_t hard_limit_, uint
{
if (up)
{
LOG_WARNING(log, "Exceeded sort memory limit ({})", ReadableSize(soft_limit_));
LOG_WARNING(log, "Exceeded soft memory limit ({})", ReadableSize(soft_limit_));
#if USE_JEMALLOC
LOG_INFO(log, "Purging jemalloc arenas");

View File

@ -391,6 +391,7 @@ PreformattedMessage getCurrentExceptionMessageAndPattern(bool with_stacktrace, b
{
WriteBufferFromOwnString stream;
std::string_view message_format_string;
std::vector<std::string> message_format_string_args;
try
{
@ -402,6 +403,7 @@ PreformattedMessage getCurrentExceptionMessageAndPattern(bool with_stacktrace, b
<< (with_extra_info ? getExtraExceptionInfo(e) : "")
<< " (version " << VERSION_STRING << VERSION_OFFICIAL << ")";
message_format_string = e.tryGetMessageFormatString();
message_format_string_args = e.getMessageFormatStringArgs();
}
catch (const Poco::Exception & e)
{
@ -462,7 +464,7 @@ PreformattedMessage getCurrentExceptionMessageAndPattern(bool with_stacktrace, b
catch (...) {} // NOLINT(bugprone-empty-catch)
}
return PreformattedMessage{stream.str(), message_format_string};
return PreformattedMessage{stream.str(), message_format_string, message_format_string_args};
}
@ -581,7 +583,7 @@ PreformattedMessage getExceptionMessageAndPattern(const Exception & e, bool with
}
catch (...) {} // NOLINT(bugprone-empty-catch)
return PreformattedMessage{stream.str(), e.tryGetMessageFormatString()};
return PreformattedMessage{stream.str(), e.tryGetMessageFormatString(), e.getMessageFormatStringArgs()};
}
std::string getExceptionMessage(std::exception_ptr e, bool with_stacktrace)

View File

@ -13,6 +13,7 @@
#include <memory>
#include <vector>
#include <fmt/core.h>
#include <fmt/format.h>
#include <Poco/Exception.h>
@ -59,6 +60,7 @@ public:
std::terminate();
capture_thread_frame_pointers = thread_frame_pointers;
message_format_string = msg.format_string;
message_format_string_args = msg.format_string_args;
}
Exception(PreformattedMessage && msg, int code): Exception(std::move(msg.text), code)
@ -67,6 +69,7 @@ public:
std::terminate();
capture_thread_frame_pointers = thread_frame_pointers;
message_format_string = msg.format_string;
message_format_string_args = msg.format_string_args;
}
/// Collect call stacks of all previous jobs' schedulings leading to this thread job's execution
@ -107,12 +110,7 @@ public:
// Format message with fmt::format, like the logging functions.
template <typename... Args>
Exception(int code, FormatStringHelper<Args...> fmt, Args &&... args)
: Exception(fmt::format(fmt.fmt_str, std::forward<Args>(args)...), code)
{
capture_thread_frame_pointers = thread_frame_pointers;
message_format_string = fmt.message_format_string;
}
Exception(int code, FormatStringHelper<Args...> fmt, Args &&... args) : Exception(fmt.format(std::forward<Args>(args)...), code) {}
struct CreateFromPocoTag {};
struct CreateFromSTDTag {};
@ -152,6 +150,8 @@ public:
std::string_view tryGetMessageFormatString() const { return message_format_string; }
std::vector<std::string> getMessageFormatStringArgs() const { return message_format_string_args; }
private:
#ifndef STD_EXCEPTION_HAS_STACK_TRACE
StackTrace trace;
@ -162,6 +162,7 @@ private:
protected:
std::string_view message_format_string;
std::vector<std::string> message_format_string_args;
/// Local copy of static per-thread thread_frame_pointers, should be mutable to be unpoisoned on printout
mutable std::vector<StackTrace::FramePointers> capture_thread_frame_pointers;
};
@ -193,26 +194,29 @@ public:
// Format message with fmt::format, like the logging functions.
template <typename... Args>
ErrnoException(int code, FormatStringHelper<Args...> fmt, Args &&... args)
: Exception(fmt::format(fmt.fmt_str, std::forward<Args>(args)...), code), saved_errno(errno)
: Exception(fmt.format(std::forward<Args>(args)...), code), saved_errno(errno)
{
addMessage(", {}", errnoToString(saved_errno));
}
template <typename... Args>
ErrnoException(int code, int with_errno, FormatStringHelper<Args...> fmt, Args &&... args)
: Exception(fmt.format(std::forward<Args>(args)...), code), saved_errno(with_errno)
{
capture_thread_frame_pointers = thread_frame_pointers;
message_format_string = fmt.message_format_string;
addMessage(", {}", errnoToString(saved_errno));
}
template <typename... Args>
[[noreturn]] static void throwWithErrno(int code, int with_errno, FormatStringHelper<Args...> fmt, Args &&... args)
{
auto e = ErrnoException(fmt::format(fmt.fmt_str, std::forward<Args>(args)...), code, with_errno);
e.message_format_string = fmt.message_format_string;
auto e = ErrnoException(code, with_errno, std::move(fmt), std::forward<Args>(args)...);
throw e; /// NOLINT
}
template <typename... Args>
[[noreturn]] static void throwFromPath(int code, const std::string & path, FormatStringHelper<Args...> fmt, Args &&... args)
{
auto e = ErrnoException(fmt::format(fmt.fmt_str, std::forward<Args>(args)...), code, errno);
e.message_format_string = fmt.message_format_string;
auto e = ErrnoException(code, errno, std::move(fmt), std::forward<Args>(args)...);
e.path = path;
throw e; /// NOLINT
}
@ -221,8 +225,7 @@ public:
[[noreturn]] static void
throwFromPathWithErrno(int code, const std::string & path, int with_errno, FormatStringHelper<Args...> fmt, Args &&... args)
{
auto e = ErrnoException(fmt::format(fmt.fmt_str, std::forward<Args>(args)...), code, with_errno);
e.message_format_string = fmt.message_format_string;
auto e = ErrnoException(code, with_errno, std::move(fmt), std::forward<Args>(args)...);
e.path = path;
throw e; /// NOLINT
}

View File

@ -2,8 +2,11 @@
#include <base/defines.h>
#include <base/types.h>
#include <fmt/args.h>
#include <fmt/core.h>
#include <fmt/format.h>
#include <mutex>
#include <type_traits>
#include <unordered_map>
#include <Poco/Logger.h>
#include <Poco/Message.h>
@ -14,6 +17,10 @@ struct PreformattedMessage;
consteval void formatStringCheckArgsNumImpl(std::string_view str, size_t nargs);
template <typename T> constexpr std::string_view tryGetStaticFormatString(T && x);
[[maybe_unused]] inline void tryGetFormattedArgs(std::vector<std::string>&) {};
template <typename T, typename... Ts> [[maybe_unused]] inline void tryGetFormattedArgs(std::vector<std::string>&, T &&, Ts && ...);
template <typename... Args> inline std::string tryGetArgsAndFormat(std::vector<std::string>&, fmt::format_string<Args...>, Args && ...);
/// Extract format string from a string literal and constructs consteval fmt::format_string
template <typename... Args>
struct FormatStringHelperImpl
@ -39,6 +46,7 @@ struct PreformattedMessage
{
std::string text;
std::string_view format_string;
std::vector<std::string> format_string_args;
template <typename... Args>
static PreformattedMessage create(FormatStringHelper<Args...> fmt, Args &&... args);
@ -47,22 +55,26 @@ struct PreformattedMessage
operator std::string () && { return std::move(text); } /// NOLINT
operator fmt::format_string<> () const { UNREACHABLE(); } /// NOLINT
void apply(std::string & out_text, std::string_view & out_format_string) const &
void apply(std::string & out_text, std::string_view & out_format_string, std::vector<std::string> & out_format_string_args) const &
{
out_text = text;
out_format_string = format_string;
out_format_string_args = format_string_args;
}
void apply(std::string & out_text, std::string_view & out_format_string) &&
void apply(std::string & out_text, std::string_view & out_format_string, std::vector<std::string> & out_format_string_args) &&
{
out_text = std::move(text);
out_format_string = format_string;
out_format_string_args = std::move(format_string_args);
}
};
template <typename... Args>
PreformattedMessage FormatStringHelperImpl<Args...>::format(Args && ...args) const
{
return PreformattedMessage{fmt::format(fmt_str, std::forward<Args>(args)...), message_format_string};
std::vector<std::string> out_format_string_args;
std::string msg_text = tryGetArgsAndFormat(out_format_string_args, fmt_str, std::forward<Args>(args)...);
return PreformattedMessage{msg_text, message_format_string, out_format_string_args};
}
template <typename... Args>
@ -113,12 +125,23 @@ template <typename T> constexpr std::string_view tryGetStaticFormatString(T && x
}
}
template <typename T, typename... Ts> void tryGetFormattedArgs(std::vector<std::string>& out, T && x, Ts && ...rest)
{
if constexpr (std::is_base_of_v<fmt::detail::view, std::decay_t<T>>)
out.push_back(fmt::format("{}", std::remove_reference_t<T>(x)));
else
out.push_back(fmt::format("{}", std::forward<T>(x)));
tryGetFormattedArgs(out, std::forward<Ts>(rest)...);
}
/// Constexpr ifs are not like ifdefs, and compiler still checks that unneeded code can be compiled
/// This template is useful to avoid compilation failures when condition of some "constexpr if" is false
template<bool enable> struct ConstexprIfsAreNotIfdefs
{
template <typename T> constexpr static std::string_view getStaticFormatString(T &&) { return {}; }
template <typename T> static PreformattedMessage getPreformatted(T &&) { return {}; }
template <typename... Args> static std::string getArgsAndFormat(std::vector<std::string>&, fmt::format_string<Args...>, Args &&...) { return {}; }
};
template<> struct ConstexprIfsAreNotIfdefs<true>
@ -133,8 +156,19 @@ template<> struct ConstexprIfsAreNotIfdefs<true>
}
template <typename T> static T && getPreformatted(T && x) { return std::forward<T>(x); }
template <typename... Args> static std::string getArgsAndFormat(std::vector<std::string>& out, fmt::format_string<Args...> fmt_str, Args && ...args)
{
return tryGetArgsAndFormat(out, std::move(fmt_str), std::forward<Args>(args)...);
}
};
template <typename... Args> inline std::string tryGetArgsAndFormat(std::vector<std::string>& out, fmt::format_string<Args...> fmt_str, Args && ...args)
{
tryGetFormattedArgs(out, args...);
return fmt::format(fmt_str, std::forward<Args>(args)...);
}
template <typename... Ts> constexpr size_t numArgs(Ts &&...) { return sizeof...(Ts); }
template <typename T, typename... Ts> constexpr auto firstArg(T && x, Ts &&...) { return std::forward<T>(x); }
/// For implicit conversion of fmt::basic_runtime<> to char* for std::string ctor

View File

@ -518,7 +518,8 @@ bool ZooKeeper::existsWatch(const std::string & path, Coordination::Stat * stat,
return code != Coordination::Error::ZNONODE;
}
Coordination::Error ZooKeeper::getImpl(const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback)
Coordination::Error ZooKeeper::getImpl(
const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallbackPtr watch_callback)
{
auto future_result = asyncTryGetNoThrow(path, watch_callback);
@ -541,6 +542,11 @@ Coordination::Error ZooKeeper::getImpl(const std::string & path, std::string & r
}
}
Coordination::Error ZooKeeper::getImpl(const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback)
{
return getImpl(path, res, stat, watch_callback ? std::make_shared<Coordination::WatchCallback>(watch_callback) : Coordination::WatchCallbackPtr{});
}
std::string ZooKeeper::get(const std::string & path, Coordination::Stat * stat, const EventPtr & watch)
{
Coordination::Error code = Coordination::Error::ZOK;
@ -561,6 +567,17 @@ std::string ZooKeeper::getWatch(const std::string & path, Coordination::Stat * s
throw KeeperException(code, "Can't get data for node '{}': node doesn't exist", path);
}
std::string ZooKeeper::getWatch(const std::string & path, Coordination::Stat * stat, Coordination::WatchCallbackPtr watch_callback)
{
Coordination::Error code = Coordination::Error::ZOK;
std::string res;
if (tryGetWatch(path, res, stat, watch_callback, &code))
return res;
else
throw KeeperException(code, "Can't get data for node '{}': node doesn't exist", path);
}
bool ZooKeeper::tryGet(
const std::string & path,
std::string & res,
@ -571,6 +588,25 @@ bool ZooKeeper::tryGet(
return tryGetWatch(path, res, stat, callbackForEvent(watch), return_code);
}
bool ZooKeeper::tryGetWatch(
const std::string & path,
std::string & res,
Coordination::Stat * stat,
Coordination::WatchCallbackPtr watch_callback,
Coordination::Error * return_code)
{
Coordination::Error code = getImpl(path, res, stat, watch_callback);
if (!(code == Coordination::Error::ZOK || code == Coordination::Error::ZNONODE))
throw KeeperException::fromPath(code, path);
if (return_code)
*return_code = code;
return code == Coordination::Error::ZOK;
}
bool ZooKeeper::tryGetWatch(
const std::string & path,
std::string & res,
@ -589,6 +625,7 @@ bool ZooKeeper::tryGetWatch(
return code == Coordination::Error::ZOK;
}
Coordination::Error ZooKeeper::setImpl(const std::string & path, const std::string & data,
int32_t version, Coordination::Stat * stat)
{
@ -1062,6 +1099,11 @@ std::future<Coordination::GetResponse> ZooKeeper::asyncGet(const std::string & p
}
std::future<Coordination::GetResponse> ZooKeeper::asyncTryGetNoThrow(const std::string & path, Coordination::WatchCallback watch_callback)
{
return asyncTryGetNoThrow(path, watch_callback ? std::make_shared<Coordination::WatchCallback>(watch_callback) : Coordination::WatchCallbackPtr{});
}
std::future<Coordination::GetResponse> ZooKeeper::asyncTryGetNoThrow(const std::string & path, Coordination::WatchCallbackPtr watch_callback)
{
auto promise = std::make_shared<std::promise<Coordination::GetResponse>>();
auto future = promise->get_future();
@ -1071,8 +1113,7 @@ std::future<Coordination::GetResponse> ZooKeeper::asyncTryGetNoThrow(const std::
promise->set_value(response);
};
impl->get(path, std::move(callback),
watch_callback ? std::make_shared<Coordination::WatchCallback>(watch_callback) : Coordination::WatchCallbackPtr{});
impl->get(path, std::move(callback), watch_callback);
return future;
}

View File

@ -306,6 +306,7 @@ public:
std::string get(const std::string & path, Coordination::Stat * stat = nullptr, const EventPtr & watch = nullptr);
std::string getWatch(const std::string & path, Coordination::Stat * stat, Coordination::WatchCallback watch_callback);
std::string getWatch(const std::string & path, Coordination::Stat * stat, Coordination::WatchCallbackPtr watch_callback);
using MultiGetResponse = MultiReadResponses<Coordination::GetResponse, false>;
using MultiTryGetResponse = MultiReadResponses<Coordination::GetResponse, true>;
@ -338,6 +339,13 @@ public:
Coordination::WatchCallback watch_callback,
Coordination::Error * code = nullptr);
bool tryGetWatch(
const std::string & path,
std::string & res,
Coordination::Stat * stat,
Coordination::WatchCallbackPtr watch_callback,
Coordination::Error * code = nullptr);
template <typename TIter>
MultiTryGetResponse tryGet(TIter start, TIter end)
{
@ -520,6 +528,8 @@ public:
/// Like the previous one but don't throw any exceptions on future.get()
FutureGet asyncTryGetNoThrow(const std::string & path, Coordination::WatchCallback watch_callback = {});
FutureGet asyncTryGetNoThrow(const std::string & path, Coordination::WatchCallbackPtr watch_callback = {});
using FutureExists = std::future<Coordination::ExistsResponse>;
FutureExists asyncExists(const std::string & path, Coordination::WatchCallback watch_callback = {});
/// Like the previous one but don't throw any exceptions on future.get()
@ -625,6 +635,8 @@ private:
Coordination::Error removeImpl(const std::string & path, int32_t version);
Coordination::Error getImpl(
const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallback watch_callback);
Coordination::Error getImpl(
const std::string & path, std::string & res, Coordination::Stat * stat, Coordination::WatchCallbackPtr watch_callback);
Coordination::Error setImpl(const std::string & path, const std::string & data, int32_t version, Coordination::Stat * stat);
Coordination::Error getChildrenImpl(
const std::string & path,

View File

@ -22,13 +22,16 @@ ZooKeeperLock::ZooKeeperLock(
const ZooKeeperPtr & zookeeper_,
const std::string & lock_prefix_,
const std::string & lock_name_,
const std::string & lock_message_)
const std::string & lock_message_,
bool throw_if_lost_)
: zookeeper(zookeeper_)
, lock_path(fs::path(lock_prefix_) / lock_name_)
, lock_message(lock_message_)
, throw_if_lost(throw_if_lost_)
, log(getLogger("zkutil::Lock"))
{
zookeeper->createIfNotExists(lock_prefix_, "");
LOG_TRACE(log, "Trying to create zookeeper lock on path {} for session {}", lock_path, zookeeper->getClientID());
}
ZooKeeperLock::~ZooKeeperLock()
@ -45,7 +48,7 @@ ZooKeeperLock::~ZooKeeperLock()
bool ZooKeeperLock::isLocked() const
{
return locked;
return locked && !zookeeper->expired();
}
const std::string & ZooKeeperLock::getLockPath() const
@ -56,7 +59,10 @@ const std::string & ZooKeeperLock::getLockPath() const
void ZooKeeperLock::unlock()
{
if (!locked)
{
LOG_TRACE(log, "Lock on path {} for session {} is not locked, exiting", lock_path, zookeeper->getClientID());
return;
}
locked = false;
@ -71,12 +77,19 @@ void ZooKeeperLock::unlock()
bool result = zookeeper->exists(lock_path, &stat);
if (result && stat.ephemeralOwner == zookeeper->getClientID())
{
zookeeper->remove(lock_path, -1);
LOG_TRACE(log, "Lock on path {} for session {} is unlocked", lock_path, zookeeper->getClientID());
}
else if (result)
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "Lock is lost, it has another owner. Path: {}, message: {}, owner: {}, our id: {}",
lock_path, lock_message, stat.ephemeralOwner, zookeeper->getClientID());
else if (throw_if_lost)
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "Lock is lost, node does not exist. Path: {}, message: {}, our id: {}",
lock_path, lock_message, zookeeper->getClientID());
else
throw DB::Exception(DB::ErrorCodes::LOGICAL_ERROR, "Lock is lost, node does not exist. Path: {}, message: {}", lock_path, lock_message);
LOG_INFO(log, "Lock is lost, node does not exist. Path: {}, message: {}, our id: {}",
lock_path, lock_message, zookeeper->getClientID());
}
bool ZooKeeperLock::tryLock()
@ -96,9 +109,9 @@ bool ZooKeeperLock::tryLock()
}
std::unique_ptr<ZooKeeperLock> createSimpleZooKeeperLock(
const ZooKeeperPtr & zookeeper, const String & lock_prefix, const String & lock_name, const String & lock_message)
const ZooKeeperPtr & zookeeper, const String & lock_prefix, const String & lock_name, const String & lock_message, bool throw_if_lost)
{
return std::make_unique<ZooKeeperLock>(zookeeper, lock_prefix, lock_name, lock_message);
return std::make_unique<ZooKeeperLock>(zookeeper, lock_prefix, lock_name, lock_message, throw_if_lost);
}

View File

@ -32,7 +32,8 @@ public:
const ZooKeeperPtr & zookeeper_,
const std::string & lock_prefix_,
const std::string & lock_name_,
const std::string & lock_message_ = "");
const std::string & lock_message_ = "",
bool throw_if_lost_ = true);
~ZooKeeperLock();
@ -46,12 +47,13 @@ private:
std::string lock_path;
std::string lock_message;
bool throw_if_lost{true};
LoggerPtr log;
bool locked = false;
};
std::unique_ptr<ZooKeeperLock> createSimpleZooKeeperLock(
const ZooKeeperPtr & zookeeper, const String & lock_prefix, const String & lock_name, const String & lock_message);
const ZooKeeperPtr & zookeeper, const String & lock_prefix, const String & lock_name, const String & lock_message, bool throw_if_lost = true);
}

View File

@ -1,5 +1,6 @@
#include <Common/COW.h>
#include <iostream>
#include <base/defines.h>
class IColumn : public COW<IColumn>
@ -15,8 +16,6 @@ public:
virtual int get() const = 0;
virtual void set(int value) = 0;
virtual MutablePtr test() const = 0;
};
using ColumnPtr = IColumn::Ptr;
@ -31,58 +30,63 @@ private:
explicit ConcreteColumn(int data_) : data(data_) {}
ConcreteColumn(const ConcreteColumn &) = default;
MutableColumnPtr test() const override
{
MutableColumnPtr res = create(123);
return res;
}
public:
int get() const override { return data; }
void set(int value) override { data = value; }
};
template <typename ColPtr>
void print(const ColumnPtr & x, const ColPtr & y)
{
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << y.get() << "\n";
}
int main(int, char **)
{
ColumnPtr x = ConcreteColumn::create(1);
ColumnPtr y = x;//x->test();
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << y.get() << "\n";
ColumnPtr y = x;
print(x, y);
chassert(x->get() == 1 && y->get() == 1);
chassert(x->use_count() == 2 && y->use_count() == 2);
chassert(x.get() == y.get());
{
MutableColumnPtr mut = IColumn::mutate(std::move(y));
mut->set(2);
print(x, mut);
chassert(x->get() == 1 && mut->get() == 2);
chassert(x->use_count() == 1 && mut->use_count() == 1);
chassert(x.get() != mut.get());
std::cerr << "refcounts: " << x->use_count() << ", " << mut->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << mut.get() << "\n";
y = std::move(mut);
}
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << y.get() << "\n";
print(x, y);
chassert(x->get() == 1 && y->get() == 2);
chassert(x->use_count() == 1 && y->use_count() == 1);
chassert(x.get() != y.get());
x = ConcreteColumn::create(0);
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << y.get() << "\n";
print(x, y);
chassert(x->get() == 0 && y->get() == 2);
chassert(x->use_count() == 1 && y->use_count() == 1);
chassert(x.get() != y.get());
{
MutableColumnPtr mut = IColumn::mutate(std::move(y));
mut->set(3);
print(x, mut);
chassert(x->get() == 0 && mut->get() == 3);
chassert(x->use_count() == 1 && mut->use_count() == 1);
chassert(x.get() != mut.get());
std::cerr << "refcounts: " << x->use_count() << ", " << mut->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << mut.get() << "\n";
y = std::move(mut);
}
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
print(x, y);
chassert(x->get() == 0 && y->get() == 3);
chassert(x->use_count() == 1 && y->use_count() == 1);
chassert(x.get() != y.get());
return 0;
}

View File

@ -1,5 +1,6 @@
#include <Common/COW.h>
#include <iostream>
#include <base/defines.h>
class IColumn : public COW<IColumn>
@ -61,47 +62,58 @@ public:
void set(int value) override { wrapped->set(value); }
};
template <typename ColPtr>
void print(const ColumnPtr & x, const ColPtr & y)
{
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << y.get() << "\n";
}
int main(int, char **)
{
ColumnPtr x = ColumnComposition::create(1);
ColumnPtr y = x;
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << y.get() << "\n";
print(x, y);
chassert(x->get() == 1 && y->get() == 1);
chassert(x->use_count() == 2 && y->use_count() == 2);
chassert(x.get() == y.get());
{
MutableColumnPtr mut = IColumn::mutate(std::move(y));
mut->set(2);
print(x, mut);
chassert(x->get() == 1 && mut->get() == 2);
chassert(x->use_count() == 1 && mut->use_count() == 1);
chassert(x.get() != mut.get());
std::cerr << "refcounts: " << x->use_count() << ", " << mut->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << mut.get() << "\n";
y = std::move(mut);
}
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << y.get() << "\n";
print(x, y);
chassert(x->get() == 1 && y->get() == 2);
chassert(x->use_count() == 1 && y->use_count() == 1);
chassert(x.get() != y.get());
x = ColumnComposition::create(0);
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << y.get() << "\n";
print(x, y);
chassert(x->get() == 0 && y->get() == 2);
chassert(x->use_count() == 1 && y->use_count() == 1);
chassert(x.get() != y.get());
{
MutableColumnPtr mut = IColumn::mutate(std::move(y));
mut->set(3);
print(x, mut);
chassert(x->get() == 0 && mut->get() == 3);
chassert(x->use_count() == 1 && mut->use_count() == 1);
chassert(x.get() != mut.get());
std::cerr << "refcounts: " << x->use_count() << ", " << mut->use_count() << "\n";
std::cerr << "addresses: " << x.get() << ", " << mut.get() << "\n";
y = std::move(mut);
}
std::cerr << "values: " << x->get() << ", " << y->get() << "\n";
std::cerr << "refcounts: " << x->use_count() << ", " << y->use_count() << "\n";
print(x, y);
chassert(x->get() == 0 && y->get() == 3);
chassert(x->use_count() == 1 && y->use_count() == 1);
chassert(x.get() != y.get());
return 0;
}

View File

@ -2,6 +2,7 @@
/// Macros for convenient usage of Poco logger.
#include <unistd.h>
#include <fmt/args.h>
#include <fmt/format.h>
#include <Poco/Logger.h>
#include <Poco/Message.h>
@ -80,6 +81,7 @@ namespace impl
\
std::string_view _format_string; \
std::string _formatted_message; \
std::vector<std::string> _format_string_args; \
\
if constexpr (LogTypeInfo::is_static) \
{ \
@ -91,17 +93,17 @@ namespace impl
if constexpr (is_preformatted_message) \
{ \
static_assert(_nargs == 1 || !is_preformatted_message); \
ConstexprIfsAreNotIfdefs<is_preformatted_message>::getPreformatted(LOG_IMPL_FIRST_ARG(__VA_ARGS__)).apply(_formatted_message, _format_string); \
ConstexprIfsAreNotIfdefs<is_preformatted_message>::getPreformatted(LOG_IMPL_FIRST_ARG(__VA_ARGS__)).apply(_formatted_message, _format_string, _format_string_args); \
} \
else \
{ \
_formatted_message = _nargs == 1 ? firstArg(__VA_ARGS__) : fmt::format(__VA_ARGS__); \
_formatted_message = _nargs == 1 ? firstArg(__VA_ARGS__) : ConstexprIfsAreNotIfdefs<!is_preformatted_message>::getArgsAndFormat(_format_string_args, __VA_ARGS__); \
} \
\
std::string _file_function = __FILE__ "; "; \
_file_function += __PRETTY_FUNCTION__; \
Poco::Message _poco_message(_logger->name(), std::move(_formatted_message), \
(PRIORITY), _file_function.c_str(), __LINE__, _format_string); \
(PRIORITY), _file_function.c_str(), __LINE__, _format_string, _format_string_args); \
_channel->log(_poco_message); \
} \
catch (const Poco::Exception & logger_exception) \

View File

@ -132,7 +132,9 @@ static PollPidResult pollPid(pid_t pid, int timeout_in_ms)
if (kq == -1)
return PollPidResult::FAILED;
struct kevent change = {.ident = 0};
struct kevent change;
change.ident = 0;
EV_SET(&change, pid, EVFILT_PROC, EV_ADD, NOTE_EXIT, 0, NULL);
int event_add_result = HANDLE_EINTR(kevent(kq, &change, 1, NULL, 0, NULL));
@ -144,7 +146,9 @@ static PollPidResult pollPid(pid_t pid, int timeout_in_ms)
return PollPidResult::FAILED;
}
struct kevent event = {.ident = 0};
struct kevent event;
event.ident = 0;
struct timespec remaining_timespec = {.tv_sec = timeout_in_ms / 1000, .tv_nsec = (timeout_in_ms % 1000) * 1000000};
int ready = HANDLE_EINTR(kevent(kq, nullptr, 0, &event, 1, &remaining_timespec));
PollPidResult result = ready < 0 ? PollPidResult::FAILED : PollPidResult::RESTART;

View File

@ -95,16 +95,21 @@ static void setReplicatedEngine(ASTCreateQuery * create_query, ContextPtr contex
create_query->storage->set(create_query->storage->engine, engine->clone());
}
String DatabaseOrdinary::getConvertToReplicatedFlagPath(const String & name, bool tableStarted)
String DatabaseOrdinary::getConvertToReplicatedFlagPath(const String & name, const StoragePolicyPtr storage_policy, bool tableStarted)
{
fs::path data_path;
if (storage_policy->getDisks().empty())
data_path = getContext()->getPath();
else
data_path = storage_policy->getDisks()[0]->getPath();
if (!tableStarted)
{
auto create_query = tryGetCreateTableQuery(name, getContext());
data_path = fs::path(getContext()->getPath()) / getTableDataPath(create_query->as<ASTCreateQuery &>());
data_path = data_path / getTableDataPath(create_query->as<ASTCreateQuery &>());
}
else
data_path = fs::path(getContext()->getPath()) / getTableDataPath(name);
data_path = data_path / getTableDataPath(name);
return (data_path / CONVERT_TO_REPLICATED_FLAG_NAME).string();
}
@ -120,7 +125,14 @@ void DatabaseOrdinary::convertMergeTreeToReplicatedIfNeeded(ASTPtr ast, const Qu
if (!create_query->storage || !create_query->storage->engine->name.ends_with("MergeTree") || create_query->storage->engine->name.starts_with("Replicated") || create_query->storage->engine->name.starts_with("Shared"))
return;
auto convert_to_replicated_flag_path = getConvertToReplicatedFlagPath(qualified_name.table, false);
/// Get table's storage policy
MergeTreeSettings default_settings = getContext()->getMergeTreeSettings();
auto policy = getContext()->getStoragePolicy(default_settings.storage_policy);
if (auto * query_settings = create_query->storage->settings)
if (Field * policy_setting = query_settings->changes.tryGet("storage_policy"))
policy = getContext()->getStoragePolicy(policy_setting->safeGet<String>());
auto convert_to_replicated_flag_path = getConvertToReplicatedFlagPath(qualified_name.table, policy, false);
if (!fs::exists(convert_to_replicated_flag_path))
return;
@ -288,7 +300,7 @@ void DatabaseOrdinary::restoreMetadataAfterConvertingToReplicated(StoragePtr tab
if (!rmt)
return;
auto convert_to_replicated_flag_path = getConvertToReplicatedFlagPath(name.table, true);
auto convert_to_replicated_flag_path = getConvertToReplicatedFlagPath(name.table, table->getStoragePolicy(), true);
if (!fs::exists(convert_to_replicated_flag_path))
return;

View File

@ -86,7 +86,7 @@ protected:
private:
void convertMergeTreeToReplicatedIfNeeded(ASTPtr ast, const QualifiedTableName & qualified_name, const String & file_name);
void restoreMetadataAfterConvertingToReplicated(StoragePtr table, const QualifiedTableName & name);
String getConvertToReplicatedFlagPath(const String & name, bool tableStarted);
String getConvertToReplicatedFlagPath(const String & name, StoragePolicyPtr storage_policy, bool tableStarted);
};
}

View File

@ -1708,9 +1708,18 @@ void registerDatabaseReplicated(DatabaseFactory & factory)
String shard_name = safeGetLiteralValue<String>(arguments[1], "Replicated");
String replica_name = safeGetLiteralValue<String>(arguments[2], "Replicated");
zookeeper_path = args.context->getMacros()->expand(zookeeper_path);
shard_name = args.context->getMacros()->expand(shard_name);
replica_name = args.context->getMacros()->expand(replica_name);
/// Expand macros.
Macros::MacroExpansionInfo info;
info.table_id.database_name = args.database_name;
info.table_id.uuid = args.uuid;
zookeeper_path = args.context->getMacros()->expand(zookeeper_path, info);
info.level = 0;
info.table_id.uuid = UUIDHelpers::Nil;
shard_name = args.context->getMacros()->expand(shard_name, info);
info.level = 0;
replica_name = args.context->getMacros()->expand(replica_name, info);
DatabaseReplicatedSettings database_replicated_settings{};
if (engine_define->settings)

View File

@ -188,7 +188,8 @@ Azure::Storage::Blobs::BlobClientOptions getAzureBlobClientOptions(const Poco::U
retry_options.MaxRetryDelay = std::chrono::milliseconds(config.getUInt(config_prefix + ".retry_max_backoff_ms", 1000));
using CurlOptions = Azure::Core::Http::CurlTransportOptions;
CurlOptions curl_options{.NoSignal = true};
CurlOptions curl_options;
curl_options.NoSignal = true;
if (config.has(config_prefix + ".curl_ip_resolve"))
{

View File

@ -21,6 +21,8 @@ struct AzureBlobStorageEndpoint
String getEndpoint()
{
String url = storage_account_url;
if (url.ends_with('/'))
url.pop_back();
if (!account_name.empty())
url += "/" + account_name;

View File

@ -3736,6 +3736,7 @@ namespace
throw Exception(ErrorCodes::BAD_ARGUMENTS, "ClickHouse doesn't support type recursion ({})", field_descriptor->full_name());
}
pending_resolution.emplace(field_descriptor);
SCOPE_EXIT({ pending_resolution.erase(field_descriptor); });
if (allow_repeat && field_descriptor->is_map())
{

View File

@ -794,7 +794,7 @@ inline bool tryParseImpl<DataTypeIPv6>(DataTypeIPv6::FieldType & x, ReadBuffer &
if (isNativeNumber(result_type) && !(result_type.getName() == "IPv4" || result_type.getName() == "IPv6"))
message_buf << ". Note: there are to" << result_type.getName() << "OrZero and to" << result_type.getName() << "OrNull functions, which returns zero/NULL instead of throwing exception.";
throw Exception(PreformattedMessage{message_buf.str(), "Cannot parse string {} as {}: syntax error {}"}, ErrorCodes::CANNOT_PARSE_TEXT);
throw Exception(PreformattedMessage{message_buf.str(), "Cannot parse string {} as {}: syntax error {}", {String(read_buffer.buffer().begin(), read_buffer.buffer().size()), result_type.getName()}}, ErrorCodes::CANNOT_PARSE_TEXT);
}

View File

@ -38,8 +38,14 @@ public:
: format_name(std::move(format_name_))
, arguments_column_names(std::move(arguments_column_names_))
, context(std::move(context_))
, format_settings(getFormatSettings(context))
{
FormatFactory::instance().checkFormatName(format_name);
/// We don't need handling exceptions while formatting as a row.
/// But it can be enabled in query sent via http interface.
format_settings.json.valid_output_on_exception = false;
format_settings.xml.valid_output_on_exception = false;
}
String getName() const override { return name; }
@ -68,7 +74,6 @@ public:
}
materializeBlockInplace(arg_columns);
auto format_settings = getFormatSettings(context);
auto out = FormatFactory::instance().getOutputFormat(format_name, buffer, arg_columns, context, format_settings);
/// This function make sense only for row output formats.
@ -104,6 +109,7 @@ private:
String format_name;
Names arguments_column_names;
ContextPtr context;
FormatSettings format_settings;
};
template <bool no_newline>

View File

@ -82,6 +82,7 @@ void EvictionCandidates::removeQueueEntries(const CachePriorityGuard::Lock & loc
auto queue_iterator = candidate->getQueueIterator();
queue_iterator->invalidate();
chassert(candidate->releasable());
candidate->file_segment->resetQueueIterator();
/// We need to set removed flag in file segment metadata,
/// because in dynamic cache resize we first remove queue entries,
@ -122,7 +123,13 @@ void EvictionCandidates::evict()
while (!key_candidates.candidates.empty())
{
auto & candidate = key_candidates.candidates.back();
chassert(candidate->releasable());
if (!candidate->releasable())
{
throw Exception(ErrorCodes::LOGICAL_ERROR,
"Eviction candidate is not releasable: {} (evicting or removed flag: {})",
candidate->file_segment->getInfoForLog(), candidate->isEvictingOrRemoved(*locked_key));
}
const auto segment = candidate->file_segment;
IFileCachePriority::IteratorPtr iterator;

View File

@ -128,6 +128,11 @@ const FileCache::UserInfo & FileCache::getInternalUser()
return user;
}
bool FileCache::isInitialized() const
{
return is_initialized.load(std::memory_order_seq_cst);
}
const String & FileCache::getBasePath() const
{
return metadata.getBaseDirectory();

View File

@ -80,6 +80,8 @@ public:
void initialize();
bool isInitialized() const;
const String & getBasePath() const;
static Key createKeyForPath(const String & path);

View File

@ -790,6 +790,9 @@ InterpreterCreateQuery::TableProperties InterpreterCreateQuery::getTableProperti
auto as_storage_metadata = as_storage->getInMemoryMetadataPtr();
properties.columns = as_storage_metadata->getColumns();
if (!create.comment && !as_storage_metadata->comment.empty())
create.set(create.comment, std::make_shared<ASTLiteral>(as_storage_metadata->comment));
/// Secondary indices and projections make sense only for MergeTree family of storage engines.
/// We should not copy them for other storages.
if (create.storage && endsWith(create.storage->engine->name, "MergeTree"))

View File

@ -397,22 +397,31 @@ BlockIO InterpreterSystemQuery::execute()
{
auto caches = FileCacheFactory::instance().getAll();
for (const auto & [_, cache_data] : caches)
{
if (!cache_data->cache->isInitialized())
continue;
cache_data->cache->removeAllReleasable(user_id);
}
}
else
{
auto cache = FileCacheFactory::instance().getByName(query.filesystem_cache_name)->cache;
if (query.key_to_drop.empty())
if (cache->isInitialized())
{
cache->removeAllReleasable(user_id);
}
else
{
auto key = FileCacheKey::fromKeyString(query.key_to_drop);
if (query.offset_to_drop.has_value())
cache->removeFileSegment(key, query.offset_to_drop.value(), user_id);
if (query.key_to_drop.empty())
{
cache->removeAllReleasable(user_id);
}
else
cache->removeKey(key, user_id);
{
auto key = FileCacheKey::fromKeyString(query.key_to_drop);
if (query.offset_to_drop.has_value())
cache->removeFileSegment(key, query.offset_to_drop.value(), user_id);
else
cache->removeKey(key, user_id);
}
}
}
break;

View File

@ -212,6 +212,7 @@ scope_guard MergeTreeTransaction::beforeCommit()
void MergeTreeTransaction::afterCommit(CSN assigned_csn) noexcept
{
auto blocker = CannotAllocateThreadFaultInjector::blockFaultInjections();
LockMemoryExceptionInThread memory_tracker_lock(VariableContext::Global);
/// Write allocated CSN into version metadata, so we will know CSN without reading it from transaction log
/// and we will be able to remove old entries from transaction log in ZK.
@ -248,6 +249,7 @@ void MergeTreeTransaction::afterCommit(CSN assigned_csn) noexcept
bool MergeTreeTransaction::rollback() noexcept
{
auto blocker = CannotAllocateThreadFaultInjector::blockFaultInjections();
LockMemoryExceptionInThread memory_tracker_lock(VariableContext::Global);
CSN expected = Tx::UnknownCSN;
bool need_rollback = csn.compare_exchange_strong(expected, Tx::RolledBackCSN);

View File

@ -86,6 +86,7 @@ struct QueryLogElement
String exception;
String stack_trace;
std::string_view exception_format_string{};
std::vector<std::string> exception_format_string_args{};
ClientInfo client_info;

View File

@ -53,6 +53,16 @@ ColumnsDescription TextLogElement::getColumnsDescription()
{"source_line", std::make_shared<DataTypeUInt64>(), "Source line from which the logging was done."},
{"message_format_string", std::make_shared<DataTypeLowCardinality>(std::make_shared<DataTypeString>()), "A format string that was used to format the message."},
{"value1", std::make_shared<DataTypeString>(), "Argument 1 that was used to format the message."},
{"value2", std::make_shared<DataTypeString>(), "Argument 2 that was used to format the message."},
{"value3", std::make_shared<DataTypeString>(), "Argument 3 that was used to format the message."},
{"value4", std::make_shared<DataTypeString>(), "Argument 4 that was used to format the message."},
{"value5", std::make_shared<DataTypeString>(), "Argument 5 that was used to format the message."},
{"value6", std::make_shared<DataTypeString>(), "Argument 6 that was used to format the message."},
{"value7", std::make_shared<DataTypeString>(), "Argument 7 that was used to format the message."},
{"value8", std::make_shared<DataTypeString>(), "Argument 8 that was used to format the message."},
{"value9", std::make_shared<DataTypeString>(), "Argument 9 that was used to format the message."},
{"value10", std::make_shared<DataTypeString>(), "Argument 10 that was used to format the message."},
};
}
@ -79,6 +89,16 @@ void TextLogElement::appendToBlock(MutableColumns & columns) const
columns[i++]->insert(source_line);
columns[i++]->insert(message_format_string);
columns[i++]->insert(value1);
columns[i++]->insert(value2);
columns[i++]->insert(value3);
columns[i++]->insert(value4);
columns[i++]->insert(value5);
columns[i++]->insert(value6);
columns[i++]->insert(value7);
columns[i++]->insert(value8);
columns[i++]->insert(value9);
columns[i++]->insert(value10);
}
TextLog::TextLog(ContextPtr context_,

View File

@ -29,6 +29,16 @@ struct TextLogElement
UInt64 source_line{};
std::string_view message_format_string;
String value1;
String value2;
String value3;
String value4;
String value5;
String value6;
String value7;
String value8;
String value9;
String value10;
static std::string name() { return "TextLog"; }
static ColumnsDescription getColumnsDescription();

View File

@ -1249,7 +1249,7 @@ bool TreeRewriterResult::collectUsedColumns(const ASTPtr & query, bool is_select
if (no_throw)
return false;
throw Exception(PreformattedMessage{ss.str(), format_string}, ErrorCodes::UNKNOWN_IDENTIFIER);
throw Exception(PreformattedMessage{ss.str(), format_string, std::vector<std::string>{}}, ErrorCodes::UNKNOWN_IDENTIFIER);
}
required_source_columns.swap(source_columns);

View File

@ -200,6 +200,7 @@ static void logException(ContextPtr context, QueryLogElement & elem, bool log_er
/// so we pass elem.exception_format_string as format string instead.
PreformattedMessage message;
message.format_string = elem.exception_format_string;
message.format_string_args = elem.exception_format_string_args;
if (elem.stack_trace.empty() || !log_error)
message.text = fmt::format("{} (from {}){} (in query: {})", elem.exception,
@ -504,6 +505,7 @@ void logQueryException(
auto exception_message = getCurrentExceptionMessageAndPattern(/* with_stacktrace */ false);
elem.exception = std::move(exception_message.text);
elem.exception_format_string = exception_message.format_string;
elem.exception_format_string_args = exception_message.format_string_args;
QueryStatusPtr process_list_elem = context->getProcessListElement();
@ -597,6 +599,7 @@ void logExceptionBeforeStart(
auto exception_message = getCurrentExceptionMessageAndPattern(/* with_stacktrace */ false);
elem.exception = std::move(exception_message.text);
elem.exception_format_string = exception_message.format_string;
elem.exception_format_string_args = exception_message.format_string_args;
elem.client_info = context->getClientInfo();

View File

@ -131,6 +131,21 @@ void OwnSplitChannel::logSplit(const Poco::Message & msg)
elem.source_line = msg.getSourceLine();
elem.message_format_string = msg.getFormatString();
#define SET_VALUE_IF_EXISTS(INDEX) if ((INDEX) <= msg.getFormatStringArgs().size()) (elem.value##INDEX) = msg.getFormatStringArgs()[(INDEX) - 1]
SET_VALUE_IF_EXISTS(1);
SET_VALUE_IF_EXISTS(2);
SET_VALUE_IF_EXISTS(3);
SET_VALUE_IF_EXISTS(4);
SET_VALUE_IF_EXISTS(5);
SET_VALUE_IF_EXISTS(6);
SET_VALUE_IF_EXISTS(7);
SET_VALUE_IF_EXISTS(8);
SET_VALUE_IF_EXISTS(9);
SET_VALUE_IF_EXISTS(10);
#undef SET_VALUE_IF_EXISTS
std::shared_ptr<SystemLogQueue<TextLogElement>> text_log_locked{};
text_log_locked = text_log.lock();
if (text_log_locked)

View File

@ -144,6 +144,12 @@ void ASTSelectQuery::formatImpl(const FormatSettings & s, FormatState & state, F
window()->as<ASTExpressionList &>().formatImplMultiline(s, state, frame);
}
if (qualify())
{
s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "QUALIFY " << (s.hilite ? hilite_none : "");
qualify()->formatImpl(s, state, frame);
}
if (!order_by_all && orderBy())
{
s.ostr << (s.hilite ? hilite_keyword : "") << s.nl_or_ws << indent_str << "ORDER BY" << (s.hilite ? hilite_none : "");

View File

@ -25,6 +25,7 @@ public:
GROUP_BY,
HAVING,
WINDOW,
QUALIFY,
ORDER_BY,
LIMIT_BY_OFFSET,
LIMIT_BY_LENGTH,
@ -55,6 +56,8 @@ public:
return "HAVING";
case Expression::WINDOW:
return "WINDOW";
case Expression::QUALIFY:
return "QUALIFY";
case Expression::ORDER_BY:
return "ORDER BY";
case Expression::LIMIT_BY_OFFSET:
@ -95,6 +98,7 @@ public:
ASTPtr & refPrewhere() { return getExpression(Expression::PREWHERE); }
ASTPtr & refWhere() { return getExpression(Expression::WHERE); }
ASTPtr & refHaving() { return getExpression(Expression::HAVING); }
ASTPtr & refQualify() { return getExpression(Expression::QUALIFY); }
ASTPtr with() const { return getExpression(Expression::WITH); }
ASTPtr select() const { return getExpression(Expression::SELECT); }
@ -104,6 +108,7 @@ public:
ASTPtr groupBy() const { return getExpression(Expression::GROUP_BY); }
ASTPtr having() const { return getExpression(Expression::HAVING); }
ASTPtr window() const { return getExpression(Expression::WINDOW); }
ASTPtr qualify() const { return getExpression(Expression::QUALIFY); }
ASTPtr orderBy() const { return getExpression(Expression::ORDER_BY); }
ASTPtr limitByOffset() const { return getExpression(Expression::LIMIT_BY_OFFSET); }
ASTPtr limitByLength() const { return getExpression(Expression::LIMIT_BY_LENGTH); }
@ -113,7 +118,7 @@ public:
ASTPtr settings() const { return getExpression(Expression::SETTINGS); }
ASTPtr interpolate() const { return getExpression(Expression::INTERPOLATE); }
bool hasFiltration() const { return where() || prewhere() || having(); }
bool hasFiltration() const { return where() || prewhere() || having() || qualify(); }
/// Set/Reset/Remove expression.
void setExpression(Expression expr, ASTPtr && ast);

View File

@ -507,6 +507,7 @@ namespace DB
MR_MACROS(WHEN, "WHEN") \
MR_MACROS(WHERE, "WHERE") \
MR_MACROS(WINDOW, "WINDOW") \
MR_MACROS(QUALIFY, "QUALIFY") \
MR_MACROS(WITH_ADMIN_OPTION, "WITH ADMIN OPTION") \
MR_MACROS(WITH_CHECK, "WITH CHECK") \
MR_MACROS(WITH_FILL, "WITH FILL") \

View File

@ -1481,6 +1481,7 @@ const char * ParserAlias::restricted_keywords[] =
"USING",
"WHERE",
"WINDOW",
"QUALIFY",
"WITH",
"INTERSECT",
"EXCEPT",

View File

@ -49,6 +49,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
ParserKeyword s_totals(Keyword::TOTALS);
ParserKeyword s_having(Keyword::HAVING);
ParserKeyword s_window(Keyword::WINDOW);
ParserKeyword s_qualify(Keyword::QUALIFY);
ParserKeyword s_order_by(Keyword::ORDER_BY);
ParserKeyword s_limit(Keyword::LIMIT);
ParserKeyword s_settings(Keyword::SETTINGS);
@ -86,6 +87,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
ASTPtr group_expression_list;
ASTPtr having_expression;
ASTPtr window_list;
ASTPtr qualify_expression;
ASTPtr order_expression_list;
ASTPtr interpolate_expression_list;
ASTPtr limit_by_length;
@ -266,6 +268,13 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
}
}
/// QUALIFY expr
if (s_qualify.ignore(pos, expected))
{
if (!exp_elem.parse(pos, qualify_expression, expected))
return false;
}
/// ORDER BY expr ASC|DESC COLLATE 'locale' list
if (s_order_by.ignore(pos, expected))
{
@ -489,6 +498,7 @@ bool ParserSelectQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
select_query->setExpression(ASTSelectQuery::Expression::GROUP_BY, std::move(group_expression_list));
select_query->setExpression(ASTSelectQuery::Expression::HAVING, std::move(having_expression));
select_query->setExpression(ASTSelectQuery::Expression::WINDOW, std::move(window_list));
select_query->setExpression(ASTSelectQuery::Expression::QUALIFY, std::move(qualify_expression));
select_query->setExpression(ASTSelectQuery::Expression::ORDER_BY, std::move(order_expression_list));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY_OFFSET, std::move(limit_by_offset));
select_query->setExpression(ASTSelectQuery::Expression::LIMIT_BY_LENGTH, std::move(limit_by_length));

View File

@ -1367,6 +1367,16 @@ void Planner::buildPlanForQueryNode()
select_query_info.has_aggregates = hasAggregateFunctionNodes(query_tree);
select_query_info.need_aggregate = query_node.hasGroupBy() || select_query_info.has_aggregates;
if (!select_query_info.has_window && query_node.hasQualify())
{
if (query_node.hasHaving())
query_node.getHaving() = mergeConditionNodes({query_node.getHaving(), query_node.getQualify()}, query_context);
else
query_node.getHaving() = query_node.getQualify();
query_node.getQualify() = {};
}
if (!select_query_info.need_aggregate && query_node.hasHaving())
{
if (query_node.hasWhere())
@ -1636,6 +1646,9 @@ void Planner::buildPlanForQueryNode()
addWindowSteps(query_plan, planner_context, window_analysis_result);
}
if (expression_analysis_result.hasQualify())
addFilterStep(query_plan, expression_analysis_result.getQualify(), "QUALIFY", result_actions_to_execute);
const auto & projection_analysis_result = expression_analysis_result.getProjection();
addExpressionStep(query_plan, projection_analysis_result.projection_actions, "Projection", result_actions_to_execute);

View File

@ -513,6 +513,16 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
if (window_analysis_result_optional)
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
std::optional<FilterAnalysisResult> qualify_analysis_result_optional;
std::optional<size_t> qualify_action_step_index_optional;
if (query_node.hasQualify())
{
qualify_analysis_result_optional = analyzeFilter(query_node.getQualify(), current_output_columns, planner_context, actions_chain);
qualify_action_step_index_optional = actions_chain.getLastStepIndex();
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
}
auto projection_analysis_result = analyzeProjection(query_node, current_output_columns, planner_context, actions_chain);
current_output_columns = actions_chain.getLastStepAvailableOutputColumns();
@ -604,7 +614,7 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
PlannerExpressionsAnalysisResult expressions_analysis_result(std::move(projection_analysis_result));
if (where_action_step_index_optional && where_analysis_result_optional)
if (where_analysis_result_optional && where_action_step_index_optional)
{
auto & where_analysis_result = *where_analysis_result_optional;
auto & where_actions_chain_node = actions_chain.at(*where_action_step_index_optional);
@ -615,7 +625,7 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
if (aggregation_analysis_result_optional)
expressions_analysis_result.addAggregation(std::move(*aggregation_analysis_result_optional));
if (having_action_step_index_optional && having_analysis_result_optional)
if (having_analysis_result_optional && having_action_step_index_optional)
{
auto & having_analysis_result = *having_analysis_result_optional;
auto & having_actions_chain_node = actions_chain.at(*having_action_step_index_optional);
@ -626,6 +636,14 @@ PlannerExpressionsAnalysisResult buildExpressionAnalysisResult(const QueryTreeNo
if (window_analysis_result_optional)
expressions_analysis_result.addWindow(std::move(*window_analysis_result_optional));
if (qualify_analysis_result_optional && qualify_action_step_index_optional)
{
auto & qualify_analysis_result = *qualify_analysis_result_optional;
auto & qualify_actions_chain_node = actions_chain.at(*qualify_action_step_index_optional);
qualify_analysis_result.remove_filter_column = !qualify_actions_chain_node->getChildRequiredOutputColumnsNames().contains(qualify_analysis_result.filter_column_name);
expressions_analysis_result.addQualify(std::move(qualify_analysis_result));
}
if (sort_analysis_result_optional)
expressions_analysis_result.addSort(std::move(*sort_analysis_result_optional));

View File

@ -129,6 +129,21 @@ public:
window_analysis_result = std::move(window_analysis_result_);
}
bool hasQualify() const
{
return qualify_analysis_result.filter_actions != nullptr;
}
const FilterAnalysisResult & getQualify() const
{
return qualify_analysis_result;
}
void addQualify(FilterAnalysisResult qualify_analysis_result_)
{
qualify_analysis_result = std::move(qualify_analysis_result_);
}
bool hasSort() const
{
return sort_analysis_result.before_order_by_actions != nullptr;
@ -165,6 +180,7 @@ private:
AggregationAnalysisResult aggregation_analysis_result;
FilterAnalysisResult having_analysis_result;
WindowAnalysisResult window_analysis_result;
FilterAnalysisResult qualify_analysis_result;
SortAnalysisResult sort_analysis_result;
LimitByAnalysisResult limit_by_analysis_result;
};

View File

@ -125,9 +125,8 @@ TableExpressionSet extractTableExpressionsSet(const QueryTreeNodePtr & node)
return res;
}
std::optional<JoinTableSide> extractJoinTableSideFromExpression(//const ActionsDAG::Node * expression_root_node,
std::optional<JoinTableSide> extractJoinTableSideFromExpression(
const IQueryTreeNode * expression_root_node,
//const std::unordered_set<const ActionsDAG::Node *> & join_expression_dag_input_nodes,
const TableExpressionSet & left_table_expressions,
const TableExpressionSet & right_table_expressions,
const JoinNode & join_node)
@ -136,30 +135,11 @@ std::optional<JoinTableSide> extractJoinTableSideFromExpression(//const ActionsD
std::vector<const IQueryTreeNode *> nodes_to_process;
nodes_to_process.push_back(expression_root_node);
// std::cerr << "==== extractJoinTableSideFromExpression\n";
// std::cerr << "inp nodes" << std::endl;
// for (const auto * node : join_expression_dag_input_nodes)
// std::cerr << reinterpret_cast<const void *>(node) << ' ' << node->result_name << std::endl;
// std::cerr << "l names" << std::endl;
// for (const auto & l : left_table_expression_columns_names)
// std::cerr << l << std::endl;
// std::cerr << "r names" << std::endl;
// for (const auto & r : right_table_expression_columns_names)
// std::cerr << r << std::endl;
// const auto * left_table_expr = join_node.getLeftTableExpression().get();
// const auto * right_table_expr = join_node.getRightTableExpression().get();
while (!nodes_to_process.empty())
{
const auto * node_to_process = nodes_to_process.back();
nodes_to_process.pop_back();
//std::cerr << "... " << reinterpret_cast<const void *>(node_to_process) << ' ' << node_to_process->result_name << std::endl;
if (const auto * function_node = node_to_process->as<FunctionNode>())
{
for (const auto & child : function_node->getArguments())
@ -172,22 +152,7 @@ std::optional<JoinTableSide> extractJoinTableSideFromExpression(//const ActionsD
if (!column_node)
continue;
// if (!join_expression_dag_input_nodes.contains(node_to_process))
// continue;
const auto & input_name = column_node->getColumnName();
// bool left_table_expression_contains_input = left_table_expression_columns_names.contains(input_name);
// bool right_table_expression_contains_input = right_table_expression_columns_names.contains(input_name);
// if (!left_table_expression_contains_input && !right_table_expression_contains_input)
// throw Exception(ErrorCodes::INVALID_JOIN_ON_EXPRESSION,
// "JOIN {} actions has column {} that do not exist in left {} or right {} table expression columns",
// join_node.formatASTForErrorMessage(),
// input_name,
// boost::join(left_table_expression_columns_names, ", "),
// boost::join(right_table_expression_columns_names, ", "));
const auto * column_source = column_node->getColumnSource().get();
if (!column_source)
throw Exception(ErrorCodes::LOGICAL_ERROR, "No source for column {} in JOIN {}", input_name, join_node.formatASTForErrorMessage());
@ -235,9 +200,6 @@ void buildJoinClause(
ActionsDAGPtr & left_dag,
ActionsDAGPtr & right_dag,
const PlannerContextPtr & planner_context,
//ActionsDAGPtr join_expression_dag,
//const std::unordered_set<const ActionsDAG::Node *> & join_expression_dag_input_nodes,
//const ActionsDAG::Node * join_expressions_actions_node,
const QueryTreeNodePtr & join_expression,
const TableExpressionSet & left_table_expressions,
const TableExpressionSet & right_table_expressions,
@ -245,22 +207,16 @@ void buildJoinClause(
JoinClause & join_clause)
{
std::string function_name;
//std::cerr << join_expression_dag->dumpDAG() << std::endl;
auto * function_node = join_expression->as<FunctionNode>();
if (function_node)
function_name = function_node->getFunction()->getName();
// if (join_expressions_actions_node->function)
// function_name = join_expressions_actions_node->function->getName();
/// For 'and' function go into children
if (function_name == "and")
{
for (const auto & child : function_node->getArguments())
{
buildJoinClause(//join_expression_dag,
//join_expression_dag_input_nodes,
buildJoinClause(
left_dag,
right_dag,
planner_context,
@ -279,17 +235,15 @@ void buildJoinClause(
if (function_name == "equals" || function_name == "isNotDistinctFrom" || is_asof_join_inequality)
{
const auto left_child = function_node->getArguments().getNodes().at(0);//join_expressions_actions_node->children.at(0);
const auto right_child = function_node->getArguments().getNodes().at(1); //join_expressions_actions_node->children.at(1);
const auto left_child = function_node->getArguments().getNodes().at(0);
const auto right_child = function_node->getArguments().getNodes().at(1);
auto left_expression_side_optional = extractJoinTableSideFromExpression(left_child.get(),
//join_expression_dag_input_nodes,
left_table_expressions,
right_table_expressions,
join_node);
auto right_expression_side_optional = extractJoinTableSideFromExpression(right_child.get(),
//join_expression_dag_input_nodes,
left_table_expressions,
right_table_expressions,
join_node);
@ -314,7 +268,6 @@ void buildJoinClause(
}
else
{
// std::cerr << "===============\n";
auto left_expression_side = *left_expression_side_optional;
auto right_expression_side = *right_expression_side_optional;
@ -361,8 +314,7 @@ void buildJoinClause(
return;
}
auto expression_side_optional = extractJoinTableSideFromExpression(//join_expressions_actions_node,
//join_expression_dag_input_nodes,
auto expression_side_optional = extractJoinTableSideFromExpression(
join_expression.get(),
left_table_expressions,
right_table_expressions,
@ -377,32 +329,15 @@ void buildJoinClause(
join_clause.addCondition(expression_side, node);
}
JoinClausesAndActions buildJoinClausesAndActions(//const ColumnsWithTypeAndName & join_expression_input_columns,
JoinClausesAndActions buildJoinClausesAndActions(
const ColumnsWithTypeAndName & left_table_expression_columns,
const ColumnsWithTypeAndName & right_table_expression_columns,
const JoinNode & join_node,
const PlannerContextPtr & planner_context)
{
//ActionsDAGPtr join_expression_actions = std::make_shared<ActionsDAG>(join_expression_input_columns);
ActionsDAGPtr left_join_actions = std::make_shared<ActionsDAG>(left_table_expression_columns);
ActionsDAGPtr right_join_actions = std::make_shared<ActionsDAG>(right_table_expression_columns);
// LOG_TRACE(getLogger("Planner"), "buildJoinClausesAndActions cols {} ", left_join_actions->dumpDAG());
// LOG_TRACE(getLogger("Planner"), "buildJoinClausesAndActions cols {} ", right_join_actions->dumpDAG());
/** In ActionsDAG if input node has constant representation additional constant column is added.
* That way we cannot simply check that node has INPUT type during resolution of expression join table side.
* Put all nodes after actions dag initialization in set.
* To check if actions dag node is input column, we check if set contains it.
*/
// const auto & join_expression_actions_nodes = join_expression_actions->getNodes();
// std::unordered_set<const ActionsDAG::Node *> join_expression_dag_input_nodes;
// join_expression_dag_input_nodes.reserve(join_expression_actions_nodes.size());
// for (const auto & node : join_expression_actions_nodes)
// join_expression_dag_input_nodes.insert(&node);
/** It is possible to have constant value in JOIN ON section, that we need to ignore during DAG construction.
* If we do not ignore it, this function will be replaced by underlying constant.
* For example ASOF JOIN does not support JOIN with constants, and we should process it like ordinary JOIN.
@ -411,9 +346,6 @@ JoinClausesAndActions buildJoinClausesAndActions(//const ColumnsWithTypeAndName
* ON (t1.id = t2.id) AND 1 != 1 AND (t1.value >= t1.value);
*/
auto join_expression = join_node.getJoinExpression();
// LOG_TRACE(getLogger("Planner"), "buildJoinClausesAndActions expr {} ", join_expression->formatConvertedASTForErrorMessage());
// LOG_TRACE(getLogger("Planner"), "buildJoinClausesAndActions expr {} ", join_expression->dumpTree());
auto * constant_join_expression = join_expression->as<ConstantNode>();
if (constant_join_expression && constant_join_expression->hasSourceExpression())
@ -425,19 +357,6 @@ JoinClausesAndActions buildJoinClausesAndActions(//const ColumnsWithTypeAndName
"JOIN {} join expression expected function",
join_node.formatASTForErrorMessage());
// PlannerActionsVisitor join_expression_visitor(planner_context);
// auto join_expression_dag_node_raw_pointers = join_expression_visitor.visit(join_expression_actions, join_expression);
// if (join_expression_dag_node_raw_pointers.size() != 1)
// throw Exception(ErrorCodes::LOGICAL_ERROR,
// "JOIN {} ON clause contains multiple expressions",
// join_node.formatASTForErrorMessage());
// const auto * join_expressions_actions_root_node = join_expression_dag_node_raw_pointers[0];
// if (!join_expressions_actions_root_node->function)
// throw Exception(ErrorCodes::INVALID_JOIN_ON_EXPRESSION,
// "JOIN {} join expression expected function",
// join_node.formatASTForErrorMessage());
size_t left_table_expression_columns_size = left_table_expression_columns.size();
Names join_left_actions_names;
@ -470,7 +389,6 @@ JoinClausesAndActions buildJoinClausesAndActions(//const ColumnsWithTypeAndName
auto join_right_table_expressions = extractTableExpressionsSet(join_node.getRightTableExpression());
JoinClausesAndActions result;
//result.join_expression_actions = join_expression_actions;
const auto & function_name = function_node->getFunction()->getName();
if (function_name == "or")
@ -479,8 +397,7 @@ JoinClausesAndActions buildJoinClausesAndActions(//const ColumnsWithTypeAndName
{
result.join_clauses.emplace_back();
buildJoinClause(//join_expression_actions,
//join_expression_dag_input_nodes,
buildJoinClause(
left_join_actions,
right_join_actions,
planner_context,
@ -499,9 +416,7 @@ JoinClausesAndActions buildJoinClausesAndActions(//const ColumnsWithTypeAndName
left_join_actions,
right_join_actions,
planner_context,
//join_expression_actions,
//join_expression_dag_input_nodes,
join_expression, //join_expressions_actions_root_node,
join_expression,
join_left_table_expressions,
join_right_table_expressions,
join_node,
@ -621,12 +536,6 @@ JoinClausesAndActions buildJoinClausesAndActions(//const ColumnsWithTypeAndName
result.left_join_expressions_actions = left_join_actions->clone();
result.left_join_tmp_expression_actions = std::move(left_join_actions);
result.left_join_expressions_actions->removeUnusedActions(join_left_actions_names);
// for (const auto & name : join_right_actions_names)
// std::cerr << ".. " << name << std::endl;
// std::cerr << right_join_actions->dumpDAG() << std::endl;
result.right_join_expressions_actions = right_join_actions->clone();
result.right_join_tmp_expression_actions = std::move(right_join_actions);
result.right_join_expressions_actions->removeUnusedActions(join_right_actions_names);
@ -648,10 +557,7 @@ JoinClausesAndActions buildJoinClausesAndActions(
"JOIN {} join does not have ON section",
join_node_typed.formatASTForErrorMessage());
// auto join_expression_input_columns = left_table_expression_columns;
// join_expression_input_columns.insert(join_expression_input_columns.end(), right_table_expression_columns.begin(), right_table_expression_columns.end());
return buildJoinClausesAndActions(/*join_expression_input_columns,*/ left_table_expression_columns, right_table_expression_columns, join_node_typed, planner_context);
return buildJoinClausesAndActions(left_table_expression_columns, right_table_expression_columns, join_node_typed, planner_context);
}
std::optional<bool> tryExtractConstantFromJoinNode(const QueryTreeNodePtr & join_node)

View File

@ -987,7 +987,7 @@ Pipe ReadFromMergeTree::spreadMarkRangesAmongStreamsWithOrder(
/// We take full part if it contains enough marks or
/// if we know limit and part contains less than 'limit' rows.
bool take_full_part = marks_in_part <= need_marks || (input_order_info->limit && input_order_info->limit < part.getRowsCount());
bool take_full_part = marks_in_part <= need_marks || (input_order_info->limit && input_order_info->limit > part.getRowsCount());
/// We take the whole part if it is small enough.
if (take_full_part)
@ -1792,6 +1792,11 @@ bool ReadFromMergeTree::requestOutputEachPartitionThroughSeparatePort()
if (isQueryWithFinal())
return false;
/// With parallel replicas we have to have only a single instance of `MergeTreeReadPoolParallelReplicas` per replica.
/// With aggregation-by-partitions optimisation we might create a separate pool for each partition.
if (is_parallel_reading_from_replicas)
return false;
const auto & settings = context->getSettingsRef();
const auto partitions_cnt = countPartitions(prepared_parts);

View File

@ -915,11 +915,22 @@ void StorageKafka::updateGlobalConfiguration(cppkafka::Configuration & kafka_con
#endif // USE_KRB5
// No need to add any prefix, messages can be distinguished
kafka_config.set_log_callback([this](cppkafka::KafkaHandleBase &, int level, const std::string & facility, const std::string & message)
{
auto [poco_level, client_logs_level] = parseSyslogLevel(level);
LOG_IMPL(log, client_logs_level, poco_level, "[rdk:{}] {}", facility, message);
});
kafka_config.set_log_callback(
[this](cppkafka::KafkaHandleBase & handle, int level, const std::string & facility, const std::string & message)
{
auto [poco_level, client_logs_level] = parseSyslogLevel(level);
const auto & kafka_object_config = handle.get_configuration();
const std::string client_id_key{"client.id"};
chassert(kafka_object_config.has_property(client_id_key) && "Kafka configuration doesn't have expected client.id set");
LOG_IMPL(
log,
client_logs_level,
poco_level,
"[client.id:{}] [rdk:{}] {}",
kafka_object_config.get(client_id_key),
facility,
message);
});
/// NOTE: statistics should be consumed, otherwise it creates too much
/// entries in the queue, that leads to memory leak and slow shutdown.

View File

@ -2250,9 +2250,11 @@ static BoolMask forAnyHyperrectangle(
if (left_bounded && right_bounded)
hyperrectangle[prefix_size] = Range(left_keys[prefix_size], true, right_keys[prefix_size], true);
else if (left_bounded)
hyperrectangle[prefix_size] = Range::createLeftBounded(left_keys[prefix_size], true, data_types[prefix_size]->isNullable());
hyperrectangle[prefix_size]
= Range::createLeftBounded(left_keys[prefix_size], true, isNullableOrLowCardinalityNullable(data_types[prefix_size]));
else if (right_bounded)
hyperrectangle[prefix_size] = Range::createRightBounded(right_keys[prefix_size], true, data_types[prefix_size]->isNullable());
hyperrectangle[prefix_size]
= Range::createRightBounded(right_keys[prefix_size], true, isNullableOrLowCardinalityNullable(data_types[prefix_size]));
return callback(hyperrectangle);
}
@ -2262,13 +2264,15 @@ static BoolMask forAnyHyperrectangle(
if (left_bounded && right_bounded)
hyperrectangle[prefix_size] = Range(left_keys[prefix_size], false, right_keys[prefix_size], false);
else if (left_bounded)
hyperrectangle[prefix_size] = Range::createLeftBounded(left_keys[prefix_size], false, data_types[prefix_size]->isNullable());
hyperrectangle[prefix_size]
= Range::createLeftBounded(left_keys[prefix_size], false, isNullableOrLowCardinalityNullable(data_types[prefix_size]));
else if (right_bounded)
hyperrectangle[prefix_size] = Range::createRightBounded(right_keys[prefix_size], false, data_types[prefix_size]->isNullable());
hyperrectangle[prefix_size]
= Range::createRightBounded(right_keys[prefix_size], false, isNullableOrLowCardinalityNullable(data_types[prefix_size]));
for (size_t i = prefix_size + 1; i < key_size; ++i)
{
if (data_types[i]->isNullable())
if (isNullableOrLowCardinalityNullable(data_types[i]))
hyperrectangle[i] = Range::createWholeUniverse();
else
hyperrectangle[i] = Range::createWholeUniverseWithoutNull();
@ -2324,7 +2328,7 @@ BoolMask KeyCondition::checkInRange(
key_ranges.reserve(used_key_size);
for (size_t i = 0; i < used_key_size; ++i)
{
if (data_types[i]->isNullable())
if (isNullableOrLowCardinalityNullable(data_types[i]))
key_ranges.push_back(Range::createWholeUniverse());
else
key_ranges.push_back(Range::createWholeUniverseWithoutNull());

View File

@ -383,7 +383,8 @@ MergeTreeDataSelectSamplingData MergeTreeDataSelectExecutor::getSampling(
if (has_lower_limit)
{
if (!key_condition.addCondition(
sampling_key.column_names[0], Range::createLeftBounded(lower, true, sampling_key.data_types[0]->isNullable())))
sampling_key.column_names[0],
Range::createLeftBounded(lower, true, isNullableOrLowCardinalityNullable(sampling_key.data_types[0]))))
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Sampling column not in primary key");
ASTPtr args = std::make_shared<ASTExpressionList>();
@ -401,7 +402,8 @@ MergeTreeDataSelectSamplingData MergeTreeDataSelectExecutor::getSampling(
if (has_upper_limit)
{
if (!key_condition.addCondition(
sampling_key.column_names[0], Range::createRightBounded(upper, false, sampling_key.data_types[0]->isNullable())))
sampling_key.column_names[0],
Range::createRightBounded(upper, false, isNullableOrLowCardinalityNullable(sampling_key.data_types[0]))))
throw Exception(ErrorCodes::ILLEGAL_COLUMN, "Sampling column not in primary key");
ASTPtr args = std::make_shared<ASTExpressionList>();

View File

@ -299,8 +299,35 @@ void StorageBuffer::read(
}
else
{
auto src_table_query_info = query_info;
if (src_table_query_info.prewhere_info)
{
auto actions_dag = ActionsDAG::makeConvertingActions(
header_after_adding_defaults.getColumnsWithTypeAndName(),
header.getColumnsWithTypeAndName(),
ActionsDAG::MatchColumnsMode::Name);
if (src_table_query_info.prewhere_info->row_level_filter)
{
src_table_query_info.prewhere_info->row_level_filter = ActionsDAG::merge(
std::move(*actions_dag->clone()),
std::move(*src_table_query_info.prewhere_info->row_level_filter));
src_table_query_info.prewhere_info->row_level_filter->removeUnusedActions();
}
if (src_table_query_info.prewhere_info->prewhere_actions)
{
src_table_query_info.prewhere_info->prewhere_actions = ActionsDAG::merge(
std::move(*actions_dag->clone()),
std::move(*src_table_query_info.prewhere_info->prewhere_actions));
src_table_query_info.prewhere_info->prewhere_actions->removeUnusedActions();
}
}
destination->read(
query_plan, columns_intersection, destination_snapshot, query_info,
query_plan, columns_intersection, destination_snapshot, src_table_query_info,
local_context, processed_stage, max_block_size, num_streams);
if (query_plan.isInitialized())

View File

@ -401,6 +401,11 @@ StorageReplicatedMergeTree::StorageReplicatedMergeTree(
{
try
{
if (LoadingStrictnessLevel::ATTACH == mode && current_zookeeper && current_zookeeper->exists(replica_path + "/is_active"))
{
throw Exception(ErrorCodes::REPLICA_ALREADY_EXISTS, "There already is an active replica with this replica path {}", replica_path);
}
if (current_zookeeper && current_zookeeper->exists(replica_path + "/host"))
{
/// Check it earlier if we can (we don't want incompatible version to start).
@ -5836,11 +5841,24 @@ bool StorageReplicatedMergeTree::optimize(
if (select_decision != SelectPartsDecision::SELECTED)
{
constexpr const char * message_fmt = "Cannot select parts for optimization: {}";
assert(disable_reason.text != unknown_disable_reason);
if (!partition_id.empty())
disable_reason.text += fmt::format(" (in partition {})", partition_id);
return handle_noop(message_fmt, disable_reason.text);
if (try_no + 1 < max_retries)
{
/// Here we trying to have a similar behaviour to ordinary MergeTree: if some merges are already in progress - let's wait for them to finish.
/// This way `optimize final` won't just silently be a noop (if also `optimize_throw_if_noop=false`), but will wait for the active merges and repeat an attempt to schedule final merge.
/// This guarantees are enough for tests, because there we have full control over insertions.
const auto wait_timeout = query_context->getSettingsRef().receive_timeout.totalMilliseconds() / max_retries;
/// DEFAULT (and not LIGHTWEIGHT) because merges are not condidered lightweight; empty `source_replicas` means "all replicas"
waitForProcessingQueue(wait_timeout, SyncReplicaMode::DEFAULT, {});
continue;
}
else
{
constexpr const char * message_fmt = "Cannot select parts for optimization: {}";
assert(disable_reason.text != unknown_disable_reason);
if (!partition_id.empty())
disable_reason.text += fmt::format(" (in partition {})", partition_id);
return handle_noop(message_fmt, disable_reason.text);
}
}
ReplicatedMergeTreeLogEntryData merge_entry;

View File

@ -622,6 +622,20 @@ Chunk SystemZooKeeperSource::generate()
ZooKeeperRetriesControl("", nullptr, retries_seetings, query_status).retryLoop(
[&]() { get_responses = get_zookeeper()->tryGet(paths_to_get); });
/// Add children count to query total rows. We can not get total rows in advance,
/// because it is too heavy to get row count for non exact paths.
/// Please be aware that there might be minor setbacks in the query progress,
/// but overall it should reflect the advancement of the query.
size_t children_count = 0;
for (size_t i = 0, size = get_tasks.size(); i < size; ++i)
{
auto & res = get_responses[i];
if (res.error == Coordination::Error::ZNONODE)
continue; /// Node was deleted meanwhile.
children_count += res.stat.numChildren;
}
addTotalRowsApprox(children_count);
for (size_t i = 0, size = get_tasks.size(); i < size; ++i)
{
auto & res = get_responses[i];

View File

@ -10,11 +10,8 @@ from pathlib import Path
from shutil import copy2
from typing import List, Optional, Union
# isort: off
from github.Commit import Commit
# isort: on
from build_download_helper import download_build_with_progress
from commit_status_helper import post_commit_status
from compress_files import SUFFIX, compress_fast, decompress_fast

View File

@ -1,31 +1,27 @@
#!/usr/bin/env python3
import argparse
from pathlib import Path
from typing import Tuple
import subprocess
import logging
import subprocess
import sys
import time
from pathlib import Path
from typing import Tuple
from ci_config import CI_CONFIG, BuildConfig
from env_helper import (
REPO_COPY,
S3_BUILDS_BUCKET,
TEMP_PATH,
)
from git_helper import Git
from pr_info import PRInfo
from report import FAILURE, JobReport, StatusType, SUCCESS
from tee_popen import TeePopen
import docker_images_helper
from ci_config import CI_CONFIG, BuildConfig
from env_helper import REPO_COPY, S3_BUILDS_BUCKET, TEMP_PATH
from git_helper import Git
from lambda_shared_package.lambda_shared.pr import Labels
from pr_info import PRInfo
from report import FAILURE, SUCCESS, JobReport, StatusType
from stopwatch import Stopwatch
from tee_popen import TeePopen
from version_helper import (
ClickHouseVersion,
get_version_from_repo,
update_version_local,
)
from stopwatch import Stopwatch
IMAGE_NAME = "clickhouse/binary-builder"
BUILD_LOG_NAME = "build_log.log"
@ -111,6 +107,10 @@ def build_clickhouse(
return build_log_path, SUCCESS if success else FAILURE
def is_release_pr(pr_info: PRInfo) -> bool:
return Labels.RELEASE in pr_info.labels or Labels.RELEASE_LTS in pr_info.labels
def get_release_or_pr(pr_info: PRInfo, version: ClickHouseVersion) -> Tuple[str, str]:
"Return prefixes for S3 artifacts paths"
# FIXME performance
@ -119,7 +119,7 @@ def get_release_or_pr(pr_info: PRInfo, version: ClickHouseVersion) -> Tuple[str,
# It should be fixed in performance-comparison image eventually
# For performance tests we always set PRs prefix
performance_pr = "PRs/0"
if "release" in pr_info.labels or "release-lts" in pr_info.labels:
if is_release_pr(pr_info):
# for release pull requests we use branch names prefixes, not pr numbers
return pr_info.head_ref, performance_pr
if pr_info.number == 0:
@ -163,7 +163,7 @@ def main():
official_flag = pr_info.number == 0
version_type = "testing"
if "release" in pr_info.labels or "release-lts" in pr_info.labels:
if is_release_pr(pr_info):
version_type = "stable"
official_flag = True

View File

@ -8,11 +8,8 @@ import time
from pathlib import Path
from typing import Any, Callable, List, Optional, Union
# isort: off
import requests
# isort: on
import get_robot_token as grt # we need an updated ROBOT_TOKEN
from ci_config import CI_CONFIG

View File

@ -139,7 +139,8 @@ def main():
additional_files=[report_path],
).dump()
if summary_status == ERROR:
# We should fail the report job to rerun it in the following attempts
if summary_status != SUCCESS:
sys.exit(1)

View File

@ -9,7 +9,7 @@ from threading import Thread
from typing import Any, Dict, List, Optional
import requests
from lambda_shared.pr import check_pr_description
from lambda_shared.pr import Labels, check_pr_description
from lambda_shared.token import get_cached_access_token
NEED_RERUN_OR_CANCELL_WORKFLOWS = {
@ -261,7 +261,7 @@ def main(event):
print("Freshly opened PR, nothing to do")
return
if action == "closed" or label == "do not test":
if action == "closed" or label == Labels.DO_NOT_TEST:
print("PR merged/closed or manually labeled 'do not test', will kill workflows")
workflow_descriptions = get_workflows_description_for_pull_request(
pull_request, token
@ -281,7 +281,7 @@ def main(event):
exec_workflow_url(urls_to_cancel, token)
return
if label == "can be tested":
if label == Labels.CAN_BE_TESTED:
print("PR marked with can be tested label, rerun workflow")
workflow_descriptions = get_workflows_description_for_pull_request(
pull_request, token

View File

@ -37,19 +37,10 @@ from env_helper import TEMP_PATH
from get_robot_token import get_best_robot_token
from git_helper import git_runner, is_shallow
from github_helper import GitHub, PullRequest, PullRequests, Repository
from lambda_shared_package.lambda_shared.pr import Labels
from ssh import SSHKey
class Labels:
MUST_BACKPORT = "pr-must-backport"
MUST_BACKPORT_CLOUD = "pr-must-backport-cloud"
BACKPORT = "pr-backport"
BACKPORTS_CREATED = "pr-backports-created"
BACKPORTS_CREATED_CLOUD = "pr-backports-created-cloud"
CHERRYPICK = "pr-cherrypick"
DO_NOT_TEST = "do not test"
class ReleaseBranch:
CHERRYPICK_DESCRIPTION = """Original pull-request #{pr_number}
@ -99,7 +90,7 @@ close it.
name: str,
pr: PullRequest,
repo: Repository,
backport_created_label: str = Labels.BACKPORTS_CREATED,
backport_created_label: str = Labels.PR_BACKPORTS_CREATED,
):
self.name = name
self.pr = pr
@ -247,12 +238,12 @@ close it.
pr_number=self.pr.number,
pr_url=self.pr.html_url,
backport_created_label=self.backport_created_label,
label_cherrypick=Labels.CHERRYPICK,
label_cherrypick=Labels.PR_CHERRYPICK,
),
base=self.backport_branch,
head=self.cherrypick_branch,
)
self.cherrypick_pr.add_to_labels(Labels.CHERRYPICK)
self.cherrypick_pr.add_to_labels(Labels.PR_CHERRYPICK)
self.cherrypick_pr.add_to_labels(Labels.DO_NOT_TEST)
self._assign_new_pr(self.cherrypick_pr)
# update cherrypick PR to get the state for PR.mergable
@ -288,7 +279,7 @@ close it.
base=self.name,
head=self.backport_branch,
)
self.backport_pr.add_to_labels(Labels.BACKPORT)
self.backport_pr.add_to_labels(Labels.PR_BACKPORT)
self._assign_new_pr(self.backport_pr)
def ping_cherry_pick_assignees(self, dry_run: bool) -> None:
@ -518,7 +509,7 @@ class Backport:
)
bp_cp_prs = self.gh.get_pulls_from_search(
query=f"type:pr repo:{self._repo_name} {query_suffix}",
label=f"{Labels.BACKPORT},{Labels.CHERRYPICK}",
label=f"{Labels.PR_BACKPORT},{Labels.PR_CHERRYPICK}",
)
for br in branches:
br.pop_prs(bp_cp_prs)
@ -588,8 +579,8 @@ def parse_args():
)
parser.add_argument(
"--backport-created-label",
default=Labels.BACKPORTS_CREATED,
choices=(Labels.BACKPORTS_CREATED, Labels.BACKPORTS_CREATED_CLOUD),
default=Labels.PR_BACKPORTS_CREATED,
choices=(Labels.PR_BACKPORTS_CREATED, Labels.PR_BACKPORTS_CREATED_CLOUD),
help="label to mark PRs as backported",
)
parser.add_argument(

View File

@ -17,7 +17,7 @@ from typing import Any, Dict, List, Optional, Sequence, Set, Tuple, Union
import docker_images_helper
import upload_result_helper
from build_check import get_release_or_pr
from ci_config import CI_CONFIG, Build, CIStages, JobNames, Labels
from ci_config import CI_CONFIG, Build, CILabels, CIStages, JobNames
from ci_utils import GHActions, is_hex, normalize_string
from clickhouse_helper import (
CiLogsCredentials,
@ -800,7 +800,7 @@ class CiOptions:
if not res.ci_jobs:
res.ci_jobs = []
res.ci_jobs.append(match.removeprefix("job_"))
elif match.startswith("ci_set_") and match in Labels:
elif match.startswith("ci_set_") and match in CILabels:
if not res.ci_sets:
res.ci_sets = []
res.ci_sets.append(match)
@ -816,12 +816,12 @@ class CiOptions:
res.exclude_keywords.append(
normalize_check_name(match.removeprefix("ci_exclude_"))
)
elif match == Labels.NO_CI_CACHE:
elif match == CILabels.NO_CI_CACHE:
res.no_ci_cache = True
print("NOTE: CI Cache will be disabled")
elif match == Labels.DO_NOT_TEST_LABEL:
elif match == CILabels.DO_NOT_TEST_LABEL:
res.do_not_test = True
elif match == Labels.NO_MERGE_COMMIT:
elif match == CILabels.NO_MERGE_COMMIT:
res.no_merge_commit = True
print("NOTE: Merge Commit will be disabled")
elif match.startswith("batch_"):
@ -920,7 +920,7 @@ class CiOptions:
# 3. Handle "do not test"
if self.do_not_test:
label_config = CI_CONFIG.get_label_config(Labels.DO_NOT_TEST_LABEL)
label_config = CI_CONFIG.get_label_config(CILabels.DO_NOT_TEST_LABEL)
assert label_config
print(
f"NOTE: CI 'do not test' setting applied, set jobs: [{label_config.run_jobs}]"
@ -1547,7 +1547,7 @@ def _fetch_commit_tokens(message: str, pr_info: PRInfo) -> List[str]:
res = [
match
for match in matches
if match in Labels or match.startswith("job_") or match.startswith("batch_")
if match in CILabels or match.startswith("job_") or match.startswith("batch_")
]
print(f"CI modifyers from commit message: [{res}]")
res_2 = []
@ -1556,7 +1556,9 @@ def _fetch_commit_tokens(message: str, pr_info: PRInfo) -> List[str]:
res_2 = [
match
for match in matches
if match in Labels or match.startswith("job_") or match.startswith("batch_")
if match in CILabels
or match.startswith("job_")
or match.startswith("batch_")
]
print(f"CI modifyers from PR body: [{res_2}]")
return list(set(res + res_2))

View File

@ -1,8 +1,8 @@
#!/usr/bin/env python3
from copy import deepcopy
import logging
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
from copy import deepcopy
from dataclasses import dataclass, field
from pathlib import Path
from typing import Callable, Dict, Iterable, List, Literal, Optional, Union
@ -37,7 +37,7 @@ class Runners(metaclass=WithIter):
FUZZER_UNIT_TESTER = "fuzzer-unit-tester"
class Labels(metaclass=WithIter):
class CILabels(metaclass=WithIter):
"""
Label names or commit tokens in normalized form
"""
@ -181,6 +181,13 @@ class JobNames(metaclass=WithIter):
BUGFIX_VALIDATE = "Bugfix validation"
class StatusNames(metaclass=WithIter):
"Class with statuses that aren't related to particular jobs"
CI = "CI running"
MERGEABLE = "Mergeable Check"
SYNC = "A Sync"
# dynamically update JobName with Build jobs
for attr_name in dir(Build):
if not attr_name.startswith("__") and not callable(getattr(Build, attr_name)):
@ -275,7 +282,7 @@ builds_job_config = JobConfig(
run_command="build_check.py $BUILD_NAME",
)
fuzzer_build_job_config = deepcopy(builds_job_config)
fuzzer_build_job_config.run_by_label = Labels.libFuzzer
fuzzer_build_job_config.run_by_label = CILabels.libFuzzer
@dataclass
@ -818,28 +825,28 @@ class CIConfig:
CI_CONFIG = CIConfig(
label_configs={
Labels.DO_NOT_TEST_LABEL: LabelConfig(run_jobs=[JobNames.STYLE_CHECK]),
Labels.CI_SET_FAST: LabelConfig(
CILabels.DO_NOT_TEST_LABEL: LabelConfig(run_jobs=[JobNames.STYLE_CHECK]),
CILabels.CI_SET_FAST: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
]
),
Labels.CI_SET_ARM: LabelConfig(
CILabels.CI_SET_ARM: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
Build.PACKAGE_AARCH64,
JobNames.INTEGRATION_TEST_ARM,
]
),
Labels.CI_SET_INTEGRATION: LabelConfig(
CILabels.CI_SET_INTEGRATION: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
Build.PACKAGE_RELEASE,
JobNames.INTEGRATION_TEST,
]
),
Labels.CI_SET_ANALYZER: LabelConfig(
CILabels.CI_SET_ANALYZER: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
@ -849,7 +856,7 @@ CI_CONFIG = CIConfig(
JobNames.INTEGRATION_TEST_ASAN_ANALYZER,
]
),
Labels.CI_SET_STATLESS: LabelConfig(
CILabels.CI_SET_STATLESS: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
@ -857,7 +864,7 @@ CI_CONFIG = CIConfig(
JobNames.STATELESS_TEST_RELEASE,
]
),
Labels.CI_SET_STATLESS_ASAN: LabelConfig(
CILabels.CI_SET_STATLESS_ASAN: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
@ -865,7 +872,7 @@ CI_CONFIG = CIConfig(
JobNames.STATELESS_TEST_ASAN,
]
),
Labels.CI_SET_STATEFUL: LabelConfig(
CILabels.CI_SET_STATEFUL: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
@ -873,7 +880,7 @@ CI_CONFIG = CIConfig(
JobNames.STATEFUL_TEST_RELEASE,
]
),
Labels.CI_SET_STATEFUL_ASAN: LabelConfig(
CILabels.CI_SET_STATEFUL_ASAN: LabelConfig(
run_jobs=[
JobNames.STYLE_CHECK,
JobNames.FAST_TEST,
@ -881,7 +888,7 @@ CI_CONFIG = CIConfig(
JobNames.STATEFUL_TEST_ASAN,
]
),
Labels.CI_SET_REDUCED: LabelConfig(
CILabels.CI_SET_REDUCED: LabelConfig(
run_jobs=[
job
for job in JobNames
@ -1335,7 +1342,7 @@ CI_CONFIG = CIConfig(
JobNames.LIBFUZZER_TEST: TestConfig(
Build.FUZZERS,
job_config=JobConfig(
run_by_label=Labels.libFuzzer,
run_by_label=CILabels.libFuzzer,
timeout=10800,
run_command='libfuzzer_test_check.py "$CHECK_NAME" 10800',
),
@ -1348,7 +1355,7 @@ CI_CONFIG.validate()
# checks required by Mergeable Check
REQUIRED_CHECKS = [
"PR Check",
"A Sync", # Cloud sync
StatusNames.SYNC,
JobNames.BUILD_CHECK,
JobNames.BUILD_CHECK_SPECIAL,
JobNames.DOCS_CHECK,
@ -1461,9 +1468,9 @@ CHECK_DESCRIPTIONS = [
lambda x: x.startswith("Integration tests ("),
),
CheckDescription(
"Mergeable Check",
StatusNames.MERGEABLE,
"Checks if all other necessary checks are successful",
lambda x: x == "Mergeable Check",
lambda x: x == StatusNames.MERGEABLE,
),
CheckDescription(
"Performance Comparison",

View File

@ -9,7 +9,6 @@ from dataclasses import asdict, dataclass
from pathlib import Path
from typing import Dict, List, Optional, Union
# isort: off
from github import Github
from github.Commit import Commit
from github.CommitStatus import CommitStatus
@ -18,11 +17,10 @@ from github.GithubObject import NotSet
from github.IssueComment import IssueComment
from github.Repository import Repository
# isort: on
from ci_config import CHECK_DESCRIPTIONS, REQUIRED_CHECKS, CheckDescription
from ci_config import CHECK_DESCRIPTIONS, REQUIRED_CHECKS, CheckDescription, StatusNames
from env_helper import GITHUB_REPOSITORY, GITHUB_RUN_URL, TEMP_PATH
from pr_info import SKIP_MERGEABLE_CHECK_LABEL, PRInfo
from lambda_shared_package.lambda_shared.pr import Labels
from pr_info import PRInfo
from report import (
ERROR,
FAILURE,
@ -38,9 +36,7 @@ from upload_result_helper import upload_results
RETRY = 5
CommitStatuses = List[CommitStatus]
MERGEABLE_NAME = "Mergeable Check"
GH_REPO = None # type: Optional[Repository]
CI_STATUS_NAME = "CI running"
STATUS_FILE_PATH = Path(TEMP_PATH) / "status.json"
@ -161,16 +157,16 @@ def set_status_comment(commit: Commit, pr_info: PRInfo) -> None:
if not statuses:
return
if not [status for status in statuses if status.context == CI_STATUS_NAME]:
if not [status for status in statuses if status.context == StatusNames.CI]:
# This is the case, when some statuses already exist for the check,
# but not the CI_STATUS_NAME. We should create it as pending.
# but not the StatusNames.CI. We should create it as pending.
# W/o pr_info to avoid recursion, and yes, one extra create_ci_report
post_commit_status(
commit,
PENDING,
create_ci_report(pr_info, statuses),
"The report for running CI",
CI_STATUS_NAME,
StatusNames.CI,
)
# We update the report in generate_status_comment function, so do it each
@ -302,7 +298,7 @@ def create_ci_report(pr_info: PRInfo, statuses: CommitStatuses) -> str:
)
)
return upload_results(
S3Helper(), pr_info.number, pr_info.sha, test_results, [], CI_STATUS_NAME
S3Helper(), pr_info.number, pr_info.sha, test_results, [], StatusNames.CI
)
@ -431,7 +427,7 @@ def set_mergeable_check(
state: StatusType = SUCCESS,
) -> None:
commit.create_status(
context=MERGEABLE_NAME,
context=StatusNames.MERGEABLE,
description=format_description(description),
state=state,
target_url=GITHUB_RUN_URL,
@ -441,7 +437,7 @@ def set_mergeable_check(
def update_mergeable_check(commit: Commit, pr_info: PRInfo, check_name: str) -> None:
"check if the check_name in REQUIRED_CHECKS and then trigger update"
not_run = (
pr_info.labels.intersection({SKIP_MERGEABLE_CHECK_LABEL, "release"})
pr_info.labels.intersection({Labels.SKIP_MERGEABLE_CHECK, Labels.RELEASE})
or check_name not in REQUIRED_CHECKS
or pr_info.release_pr
or pr_info.number == 0
@ -459,14 +455,14 @@ def update_mergeable_check(commit: Commit, pr_info: PRInfo, check_name: str) ->
def trigger_mergeable_check(commit: Commit, statuses: CommitStatuses) -> None:
"""calculate and update MERGEABLE_NAME"""
"""calculate and update StatusNames.MERGEABLE"""
required_checks = [
status for status in statuses if status.context in REQUIRED_CHECKS
]
mergeable_status = None
for status in statuses:
if status.context == MERGEABLE_NAME:
if status.context == StatusNames.MERGEABLE:
mergeable_status = status
break

View File

@ -8,11 +8,8 @@ import sys
from pathlib import Path
from typing import List, Tuple
# isort: off
from pip._vendor.packaging.version import Version
# isort: on
from build_download_helper import download_builds_filter
from docker_images_helper import DockerImage, get_docker_image, pull_image
from env_helper import REPORT_PATH, TEMP_PATH

View File

@ -8,11 +8,8 @@ import time
from pathlib import Path
from typing import List, Optional, Tuple
# isort: off
from github import Github
# isort: on
from clickhouse_helper import ClickHouseHelper, prepare_tests_results_for_clickhouse
from commit_status_helper import format_description, get_commit, post_commit_status
from docker_images_helper import DockerImageData, docker_login, get_images_oredered_list
@ -225,9 +222,11 @@ def main():
parent_version = (
None
if not image.parent
else image_tags[image.parent]
if not args.suffix
else f"{image_tags[image.parent]}-{args.suffix}"
else (
image_tags[image.parent]
if not args.suffix
else f"{image_tags[image.parent]}-{args.suffix}"
)
)
res = process_single_image(

View File

@ -8,11 +8,8 @@ import subprocess
import sys
from typing import List, Tuple
# isort: off
from github import Github
# isort: on
from clickhouse_helper import ClickHouseHelper, prepare_tests_results_for_clickhouse
from commit_status_helper import format_description, get_commit, post_commit_status
from docker_images_helper import docker_login, get_images_oredered_list

View File

@ -1,13 +1,10 @@
#!/usr/bin/env python3
import logging
# isort: off
from github import Github
# isort: on
from ci_config import StatusNames
from commit_status_helper import (
CI_STATUS_NAME,
get_commit,
get_commit_filtered_statuses,
post_commit_status,
@ -29,7 +26,7 @@ def main():
trigger_mergeable_check(commit, statuses)
if not pr_info.is_merge_queue:
statuses = [s for s in statuses if s.context == CI_STATUS_NAME]
statuses = [s for s in statuses if s.context == StatusNames.CI]
if not statuses:
return
# Take the latest status
@ -40,7 +37,7 @@ def main():
SUCCESS,
status.target_url,
"All checks finished",
CI_STATUS_NAME,
StatusNames.CI,
pr_info,
dump_to_file=True,
)

View File

@ -33,7 +33,7 @@ set -e
trap "bash -ex /packages/preserve_logs.sh" ERR
test_env='TEST_THE_DEFAULT_PARAMETER=15'
echo "$test_env" >> /etc/default/clickhouse
systemctl start clickhouse-server
systemctl restart clickhouse-server
clickhouse-client -q 'SELECT version()'
grep "$test_env" /proc/$(cat /var/run/clickhouse-server/clickhouse-server.pid)/environ"""
initd_test = r"""#!/bin/bash

View File

@ -48,11 +48,35 @@ TRUSTED_CONTRIBUTORS = {
]
}
class Labels:
CAN_BE_TESTED = "can be tested"
DO_NOT_TEST = "do not test"
MUST_BACKPORT = "pr-must-backport"
MUST_BACKPORT_CLOUD = "pr-must-backport-cloud"
JEPSEN_TEST = "jepsen-test"
SKIP_MERGEABLE_CHECK = "skip mergeable check"
PR_BACKPORT = "pr-backport"
PR_BACKPORTS_CREATED = "pr-backports-created"
PR_BACKPORTS_CREATED_CLOUD = "pr-backports-created-cloud"
PR_CHERRYPICK = "pr-cherrypick"
PR_CI = "pr-ci"
PR_FEATURE = "pr-feature"
PR_SYNCED_TO_CLOUD = "pr-synced-to-cloud"
PR_SYNC_UPSTREAM = "pr-sync-upstream"
RELEASE = "release"
RELEASE_LTS = "release-lts"
SUBMODULE_CHANGED = "submodule changed"
# pr-bugfix autoport can lead to issues in releases, let's do ci fixes only
AUTO_BACKPORT = {"pr-ci"}
# Descriptions are used in .github/PULL_REQUEST_TEMPLATE.md, keep comments there
# updated accordingly
# The following lists are append only, try to avoid editing them
# They still could be cleaned out after the decent time though.
LABELS = {
LABEL_CATEGORIES = {
"pr-backward-incompatible": ["Backward Incompatible Change"],
"pr-bugfix": [
"Bug Fix",
@ -81,7 +105,9 @@ LABELS = {
"pr-ci": ["CI Fix or Improvement (changelog entry is not required)"],
}
CATEGORY_TO_LABEL = {c: lb for lb, categories in LABELS.items() for c in categories}
CATEGORY_TO_LABEL = {
c: lb for lb, categories in LABEL_CATEGORIES.items() for c in categories
}
def check_pr_description(pr_body: str, repo_name: str) -> Tuple[str, str]:
@ -93,7 +119,7 @@ def check_pr_description(pr_body: str, repo_name: str) -> Tuple[str, str]:
# Check if body contains "Reverts ClickHouse/ClickHouse#36337"
if [True for line in lines if re.match(rf"\AReverts {repo_name}#[\d]+\Z", line)]:
return "", LABELS["pr-not-for-changelog"][0]
return "", LABEL_CATEGORIES["pr-not-for-changelog"][0]
category = ""
entry = ""

View File

@ -9,13 +9,10 @@ from os import getenv
from pprint import pformat
from typing import Dict, List
# isort: off
from github.PaginatedList import PaginatedList
from github.PullRequestReview import PullRequestReview
from github.WorkflowRun import WorkflowRun
# isort: on
from commit_status_helper import get_commit_filtered_statuses
from get_robot_token import get_best_robot_token
from github_helper import GitHub, NamedUser, PullRequest, Repository

View File

@ -9,11 +9,8 @@ import sys
import traceback
from pathlib import Path
# isort: off
from github import Github
# isort: on
from build_download_helper import download_builds_filter
from ci_config import CI_CONFIG
from clickhouse_helper import get_instance_id, get_instance_type

View File

@ -6,12 +6,8 @@ import re
from typing import Dict, List, Set, Union
from urllib.parse import quote
# isort: off
# for some reason this line moves to the end
from unidiff import PatchSet # type: ignore
# isort: on
from build_download_helper import get_gh_api
from env_helper import (
GITHUB_EVENT_PATH,
@ -19,8 +15,8 @@ from env_helper import (
GITHUB_RUN_URL,
GITHUB_SERVER_URL,
)
from lambda_shared_package.lambda_shared.pr import Labels
SKIP_MERGEABLE_CHECK_LABEL = "skip mergeable check"
NeedsDataType = Dict[str, Dict[str, Union[str, Dict[str, str]]]]
DIFF_IN_DOCUMENTATION_EXT = [
@ -256,7 +252,7 @@ class PRInfo:
self.head_ref = pull_request["head"]["ref"]
self.head_name = pull_request["head"]["repo"]["full_name"]
self.pr_html_url = pull_request["html_url"]
if "pr-backport" in self.labels:
if Labels.PR_BACKPORT in self.labels:
# head1...head2 gives changes in head2 since merge base
# Thag's why we need {self.head_ref}...master to get
# files changed in upstream AND master...{self.head_ref}
@ -279,7 +275,7 @@ class PRInfo:
]
else:
self.diff_urls.append(self.compare_pr_url(pull_request))
if "release" in self.labels:
if Labels.RELEASE in self.labels:
# For release PRs we must get not only files changed in the PR
# itself, but as well files changed since we branched out
self.diff_urls.append(

View File

@ -25,6 +25,7 @@ from contextlib import contextmanager
from typing import Any, Final, Iterator, List, Optional, Tuple
from git_helper import Git, commit, release_branch
from lambda_shared_package.lambda_shared.pr import Labels
from report import SUCCESS
from version_helper import (
FILE_WITH_VERSION_PATH,
@ -407,9 +408,9 @@ class Release:
self._git.update()
new_version = self.version.patch_update()
version_type = self.get_stable_release_type()
pr_labels = "--label release"
pr_labels = f"--label {Labels.RELEASE}"
if version_type == VersionType.LTS:
pr_labels += " --label release-lts"
pr_labels += f" --label {Labels.RELEASE_LTS}"
new_version.with_description(version_type)
self._update_cmake_contributors(new_version)
self._commit_cmake_contributors(new_version)

View File

@ -3,14 +3,10 @@ import logging
import sys
from typing import Tuple
# isort: off
from github import Github
# isort: on
from cherry_pick import Labels
from ci_config import StatusNames
from commit_status_helper import (
CI_STATUS_NAME,
create_ci_report,
format_description,
get_commit,
@ -23,6 +19,7 @@ from get_robot_token import get_best_robot_token
from lambda_shared_package.lambda_shared.pr import (
CATEGORY_TO_LABEL,
TRUSTED_CONTRIBUTORS,
Labels,
check_pr_description,
)
from pr_info import PRInfo
@ -32,13 +29,8 @@ TRUSTED_ORG_IDS = {
54801242, # clickhouse
}
OK_SKIP_LABELS = {"release", "pr-backport", "pr-cherrypick"}
CAN_BE_TESTED_LABEL = "can be tested"
FEATURE_LABEL = "pr-feature"
SUBMODULE_CHANGED_LABEL = "submodule changed"
OK_SKIP_LABELS = {Labels.RELEASE, Labels.PR_BACKPORT, Labels.PR_CHERRYPICK}
PR_CHECK = "PR Check"
# pr-bugfix autoport can lead to issues in releases, let's do ci fixes only
AUTO_BACKPORT_LABELS = ["pr-ci"]
def pr_is_by_trusted_user(pr_user_login, pr_user_orgs):
@ -71,11 +63,12 @@ def should_run_ci_for_pr(pr_info: PRInfo) -> Tuple[bool, str]:
if OK_SKIP_LABELS.intersection(pr_info.labels):
return True, "Don't try new checks for release/backports/cherry-picks"
if CAN_BE_TESTED_LABEL not in pr_info.labels and not pr_is_by_trusted_user(
if Labels.CAN_BE_TESTED not in pr_info.labels and not pr_is_by_trusted_user(
pr_info.user_login, pr_info.user_orgs
):
print(
f"PRs by untrusted users need the '{CAN_BE_TESTED_LABEL}' label - please contact a member of the core team"
f"PRs by untrusted users need the '{Labels.CAN_BE_TESTED}' label - "
"please contact a member of the core team"
)
return False, "Needs 'can be tested' label"
@ -119,11 +112,11 @@ def main():
pr_labels_to_remove.append(label)
if pr_info.has_changes_in_submodules():
pr_labels_to_add.append(SUBMODULE_CHANGED_LABEL)
elif SUBMODULE_CHANGED_LABEL in pr_info.labels:
pr_labels_to_remove.append(SUBMODULE_CHANGED_LABEL)
pr_labels_to_add.append(Labels.SUBMODULE_CHANGED)
elif Labels.SUBMODULE_CHANGED in pr_info.labels:
pr_labels_to_remove.append(Labels.SUBMODULE_CHANGED)
if any(label in AUTO_BACKPORT_LABELS for label in pr_labels_to_add):
if any(label in Labels.AUTO_BACKPORT for label in pr_labels_to_add):
backport_labels = [Labels.MUST_BACKPORT, Labels.MUST_BACKPORT_CLOUD]
pr_labels_to_add += [
label for label in backport_labels if label not in pr_info.labels
@ -163,16 +156,19 @@ def main():
)
sys.exit(1)
if FEATURE_LABEL in pr_info.labels and not pr_info.has_changes_in_documentation():
if (
Labels.PR_FEATURE in pr_info.labels
and not pr_info.has_changes_in_documentation()
):
print(
f"The '{FEATURE_LABEL}' in the labels, "
f"The '{Labels.PR_FEATURE}' in the labels, "
"but there's no changed documentation"
)
post_commit_status(
commit,
FAILURE,
"",
f"expect adding docs for {FEATURE_LABEL}",
f"expect adding docs for {Labels.PR_FEATURE}",
PR_CHECK,
pr_info,
)
@ -209,7 +205,7 @@ def main():
PENDING,
ci_report_url,
description,
CI_STATUS_NAME,
StatusNames.CI,
pr_info,
)

View File

@ -77,23 +77,35 @@ def commit_push_staged(pr_info: PRInfo) -> None:
return
git_staged = git_runner("git diff --cached --name-only")
if not git_staged:
logging.info("No fixes are staged")
return
remote_url = pr_info.event["pull_request"]["base"]["repo"]["ssh_url"]
head = git_runner("git rev-parse HEAD^{}")
git_runner(f"{GIT_PREFIX} commit -m 'Automatic style fix'")
# The fetch to avoid issue 'pushed branch tip is behind its remote'
fetch_cmd = (
f"{GIT_PREFIX} fetch {remote_url} --no-recurse-submodules --depth=2 {head}"
)
push_cmd = f"{GIT_PREFIX} push {remote_url} HEAD:{pr_info.head_ref}"
def push_fix() -> None:
"""
Stash staged changes to commit them on the top of the PR's head.
`pull_request` event runs on top of a temporary merge_commit, we need to avoid
including it in the autofix
"""
remote_url = pr_info.event["pull_request"]["base"]["repo"]["ssh_url"]
head = pr_info.sha
git_runner(f"{GIT_PREFIX} commit -m 'Automatic style fix'")
fix_commit = git_runner("git rev-parse HEAD")
logging.info(
"Fetching PR's head, check it out and cherry-pick autofix: %s", head
)
git_runner(
f"{GIT_PREFIX} fetch {remote_url} --no-recurse-submodules --depth=1 {head}"
)
git_runner(f"git reset --hard {head}")
git_runner(f"{GIT_PREFIX} cherry-pick {fix_commit}")
git_runner(f"{GIT_PREFIX} push {remote_url} HEAD:{pr_info.head_ref}")
if os.getenv("ROBOT_CLICKHOUSE_SSH_KEY", ""):
with SSHKey("ROBOT_CLICKHOUSE_SSH_KEY"):
git_runner(fetch_cmd)
git_runner(push_cmd)
push_fix()
return
git_runner(fetch_cmd)
git_runner(push_cmd)
push_fix()
def _check_mime(file: Union[Path, str], mime: str) -> bool:
@ -179,8 +191,14 @@ def main():
future = executor.submit(subprocess.run, cmd_shell, shell=True)
_ = future.result()
autofix_description = ""
if args.push:
commit_push_staged(pr_info)
try:
commit_push_staged(pr_info)
except subprocess.SubprocessError:
# do not fail the whole script if the autofix didn't work out
logging.error("Unable to push the autofix. Continue.")
autofix_description = "Failed to push autofix to the PR. "
subprocess.check_call(
f"python3 ../../utils/check-style/process_style_check_result.py --in-results-dir {temp_path} "
@ -192,7 +210,7 @@ def main():
state, description, test_results, additional_files = process_result(temp_path)
JobReport(
description=description,
description=f"{autofix_description}{description}",
test_results=test_results,
status=state,
start_time=stopwatch.start_time_str,

View File

@ -5,10 +5,11 @@
import argparse
import sys
from get_robot_token import get_best_robot_token
from pr_info import PRInfo
from github_helper import GitHub
from ci_config import StatusNames
from commit_status_helper import get_commit, post_commit_status
from get_robot_token import get_best_robot_token
from github_helper import GitHub
from pr_info import PRInfo
from report import SUCCESS
@ -56,7 +57,7 @@ def set_sync_status(gh, pr_info, sync_pr):
# FIXME: uncomment posting red Sync status to prohibit merge in MQ if PR state fetching works good
if not sync_pr:
# post_commit_status(
# get_commit(gh, pr_info.sha), FAILURE, "", "Sync PR not found", "A Sync"
# get_commit(gh, pr_info.sha), FAILURE, "", "Sync PR not found", StatusNames.SYNC
# )
return
@ -73,7 +74,9 @@ def set_sync_status(gh, pr_info, sync_pr):
if sync_pr.mergeable_state == "clean":
print(f"Sync PR [{sync_pr.number}] is clean")
post_commit_status(get_commit(gh, pr_info.sha), SUCCESS, "", "", "A Sync")
post_commit_status(
get_commit(gh, pr_info.sha), SUCCESS, "", "", StatusNames.SYNC
)
else:
print(
f"Sync PR [{sync_pr}] is not mergeable, state [{sync_pr.mergeable_state}]"
@ -83,7 +86,7 @@ def set_sync_status(gh, pr_info, sync_pr):
# FAILURE,
# "",
# f"state: {sync_pr.mergeable_state}",
# "A Sync",
# StatusNames.SYNC,
# )

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