diff --git a/.gitmodules b/.gitmodules index ecefbc32ae6..7a2c5600e65 100644 --- a/.gitmodules +++ b/.gitmodules @@ -184,7 +184,7 @@ url = https://github.com/ClickHouse-Extras/krb5 [submodule "contrib/cyrus-sasl"] path = contrib/cyrus-sasl - url = https://github.com/cyrusimap/cyrus-sasl + url = https://github.com/ClickHouse-Extras/cyrus-sasl branch = cyrus-sasl-2.1 [submodule "contrib/croaring"] path = contrib/croaring diff --git a/base/glibc-compatibility/musl/sched_getcpu.c b/base/glibc-compatibility/musl/sched_getcpu.c index 57b8b416043..f290f01d153 100644 --- a/base/glibc-compatibility/musl/sched_getcpu.c +++ b/base/glibc-compatibility/musl/sched_getcpu.c @@ -31,7 +31,7 @@ static void *volatile vdso_func = (void *)getcpu_init; int sched_getcpu(void) { int r; - unsigned cpu; + unsigned cpu = 0; #ifdef VDSO_GETCPU_SYM getcpu_f f = (getcpu_f)vdso_func; diff --git a/base/mysqlxx/Connection.cpp b/base/mysqlxx/Connection.cpp index 55757008562..8a15115cb06 100644 --- a/base/mysqlxx/Connection.cpp +++ b/base/mysqlxx/Connection.cpp @@ -116,8 +116,8 @@ void Connection::connect(const char* db, if (!mysql_real_connect(driver.get(), server, user, password, db, port, ifNotEmpty(socket), driver->client_flag)) throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get())); - /// Sets UTF-8 as default encoding. - if (mysql_set_character_set(driver.get(), "UTF8")) + /// Sets UTF-8 as default encoding. See https://mariadb.com/kb/en/mysql_set_character_set/ + if (mysql_set_character_set(driver.get(), "utf8mb4")) throw ConnectionFailed(errorMessage(driver.get()), mysql_errno(driver.get())); is_connected = true; diff --git a/cmake/autogenerated_versions.txt b/cmake/autogenerated_versions.txt index 5d643cc4bee..ce92ae203ea 100644 --- a/cmake/autogenerated_versions.txt +++ b/cmake/autogenerated_versions.txt @@ -1,9 +1,9 @@ # This strings autochanged from release_lib.sh: -SET(VERSION_REVISION 54447) +SET(VERSION_REVISION 54448) SET(VERSION_MAJOR 21) -SET(VERSION_MINOR 2) +SET(VERSION_MINOR 3) SET(VERSION_PATCH 1) -SET(VERSION_GITHASH 53d0c9fa7255aa1dc48991d19f4246ff71cc2fd7) -SET(VERSION_DESCRIBE v21.2.1.1-prestable) -SET(VERSION_STRING 21.2.1.1) +SET(VERSION_GITHASH ef72ba7349f230321750c13ee63b49a11a7c0adc) +SET(VERSION_DESCRIBE v21.3.1.1-prestable) +SET(VERSION_STRING 21.3.1.1) # end of autochange diff --git a/contrib/hyperscan b/contrib/hyperscan index 3907fd00ee8..e9f08df0213 160000 --- a/contrib/hyperscan +++ b/contrib/hyperscan @@ -1 +1 @@ -Subproject commit 3907fd00ee8b2538739768fa9533f8635a276531 +Subproject commit e9f08df0213fc637aac0a5bbde9beeaeba2fe9fa diff --git a/contrib/poco b/contrib/poco index 2c32e17c7df..e11f3c97157 160000 --- a/contrib/poco +++ b/contrib/poco @@ -1 +1 @@ -Subproject commit 2c32e17c7dfee1f8bf24227b697cdef5fddf0823 +Subproject commit e11f3c971570cf6a31006cd21cadf41a259c360a diff --git a/debian/changelog b/debian/changelog index 1cec020f026..53b36cae114 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,5 @@ -clickhouse (21.2.1.1) unstable; urgency=low +clickhouse (21.3.1.1) unstable; urgency=low * Modified source code - -- clickhouse-release Mon, 11 Jan 2021 11:12:08 +0300 + -- clickhouse-release Mon, 01 Feb 2021 12:50:53 +0300 diff --git a/docker/client/Dockerfile b/docker/client/Dockerfile index 5022687c47b..43921a4d3c4 100644 --- a/docker/client/Dockerfile +++ b/docker/client/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:18.04 ARG repository="deb https://repo.clickhouse.tech/deb/stable/ main/" -ARG version=21.2.1.* +ARG version=21.3.1.* RUN apt-get update \ && apt-get install --yes --no-install-recommends \ diff --git a/docker/server/Dockerfile b/docker/server/Dockerfile index 3528ae68ef6..8e39af5646c 100644 --- a/docker/server/Dockerfile +++ b/docker/server/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:20.04 ARG repository="deb https://repo.clickhouse.tech/deb/stable/ main/" -ARG version=21.2.1.* +ARG version=21.3.1.* ARG gosu_ver=1.10 # user/group precreated explicitly with fixed uid/gid on purpose. diff --git a/docker/test/Dockerfile b/docker/test/Dockerfile index df918928f99..f151ae8fddf 100644 --- a/docker/test/Dockerfile +++ b/docker/test/Dockerfile @@ -1,7 +1,7 @@ FROM ubuntu:18.04 ARG repository="deb https://repo.clickhouse.tech/deb/stable/ main/" -ARG version=21.2.1.* +ARG version=21.3.1.* RUN apt-get update && \ apt-get install -y apt-transport-https dirmngr && \ diff --git a/docker/test/fasttest/run.sh b/docker/test/fasttest/run.sh index 8c98da4257e..6356a8fb67d 100755 --- a/docker/test/fasttest/run.sh +++ b/docker/test/fasttest/run.sh @@ -253,8 +253,12 @@ function run_tests 00701_rollup 00834_cancel_http_readonly_queries_on_client_close 00911_tautological_compare + + # Hyperscan 00926_multimatch 00929_multi_match_edit_distance + 01681_hyperscan_debug_assertion + 01031_mutations_interpreter_and_context 01053_ssd_dictionary # this test mistakenly requires acces to /var/lib/clickhouse -- can't run this locally, disabled 01083_expressions_in_engine_arguments diff --git a/docker/test/style/Dockerfile b/docker/test/style/Dockerfile index 7047007d2fc..74af8eafc17 100644 --- a/docker/test/style/Dockerfile +++ b/docker/test/style/Dockerfile @@ -1,12 +1,16 @@ # docker build -t yandex/clickhouse-style-test . FROM ubuntu:20.04 -RUN apt-get update && env DEBIAN_FRONTEND=noninteractive apt-get install --yes shellcheck libxml2-utils git python3-pip python3-pytest && pip3 install codespell +RUN apt-get update && env DEBIAN_FRONTEND=noninteractive apt-get install --yes shellcheck libxml2-utils git python3-pip pylint && pip3 install codespell +# For |& syntax +SHELL ["bash", "-c"] + CMD cd /ClickHouse/utils/check-style && \ - ./check-style -n | tee /test_output/style_output.txt && \ - ./check-typos | tee /test_output/typos_output.txt && \ - ./check-whitespaces -n | tee /test_output/whitespaces_output.txt && \ - ./check-duplicate-includes.sh | tee /test_output/duplicate_output.txt && \ - ./shellcheck-run.sh | tee /test_output/shellcheck_output.txt + ./check-style -n |& tee /test_output/style_output.txt && \ + ./check-typos |& tee /test_output/typos_output.txt && \ + ./check-whitespaces -n |& tee /test_output/whitespaces_output.txt && \ + ./check-duplicate-includes.sh |& tee /test_output/duplicate_output.txt && \ + ./shellcheck-run.sh |& tee /test_output/shellcheck_output.txt && \ + true diff --git a/docs/en/operations/settings/settings.md b/docs/en/operations/settings/settings.md index 99d6a0aad5f..edfd391c71e 100644 --- a/docs/en/operations/settings/settings.md +++ b/docs/en/operations/settings/settings.md @@ -1944,6 +1944,21 @@ Possible values: Default value: 16. +## background_message_broker_schedule_pool_size {#background_message_broker_schedule_pool_size} + +Sets the number of threads performing background tasks for message streaming. This setting is applied at the ClickHouse server start and can’t be changed in a user session. + +Possible values: + +- Any positive integer. + +Default value: 16. + +**See Also** + +- [Kafka](../../engines/table-engines/integrations/kafka.md#kafka) engine +- [RabbitMQ](../../engines/table-engines/integrations/rabbitmq.md#rabbitmq-engine) engine + ## validate_polygons {#validate_polygons} Enables or disables throwing an exception in the [pointInPolygon](../../sql-reference/functions/geo/index.md#pointinpolygon) function, if the polygon is self-intersecting or self-tangent. diff --git a/docs/en/sql-reference/aggregate-functions/parametric-functions.md b/docs/en/sql-reference/aggregate-functions/parametric-functions.md index 3b02e145ff4..4b3bf12aa8c 100644 --- a/docs/en/sql-reference/aggregate-functions/parametric-functions.md +++ b/docs/en/sql-reference/aggregate-functions/parametric-functions.md @@ -241,7 +241,7 @@ windowFunnel(window, [mode])(timestamp, cond1, cond2, ..., condN) **Parameters** -- `window` — Length of the sliding window in seconds. +- `window` — Length of the sliding window. The unit of `window` depends on the timestamp itself and varies. Determined using the expression `timestamp of cond2 <= timestamp of cond1 + window`. - `mode` - It is an optional argument. - `'strict'` - When the `'strict'` is set, the windowFunnel() applies conditions only for the unique values. - `timestamp` — Name of the column containing the timestamp. Data types supported: [Date](../../sql-reference/data-types/date.md), [DateTime](../../sql-reference/data-types/datetime.md#data_type-datetime) and other unsigned integer types (note that even though timestamp supports the `UInt64` type, it’s value can’t exceed the Int64 maximum, which is 2^63 - 1). diff --git a/docs/en/sql-reference/functions/encryption-functions.md b/docs/en/sql-reference/functions/encryption-functions.md index bef2f8137d0..9e360abfe26 100644 --- a/docs/en/sql-reference/functions/encryption-functions.md +++ b/docs/en/sql-reference/functions/encryption-functions.md @@ -11,7 +11,7 @@ Key length depends on encryption mode. It is 16, 24, and 32 bytes long for `-128 Initialization vector length is always 16 bytes (bytes in excess of 16 are ignored). -Note that these functions work slowly. +Note that these functions work slowly until ClickHouse 21.1. ## encrypt {#encrypt} @@ -41,7 +41,7 @@ encrypt('mode', 'plaintext', 'key' [, iv, aad]) **Returned value** -- Ciphered String. [String](../../sql-reference/data-types/string.md#string). +- Ciphertext binary string. [String](../../sql-reference/data-types/string.md#string). **Examples** @@ -52,57 +52,38 @@ Query: ``` sql CREATE TABLE encryption_test ( - input String, - key String DEFAULT unhex('fb9958e2e897ef3fdb49067b51a24af645b3626eed2f9ea1dc7fd4dd71b7e38f9a68db2a3184f952382c783785f9d77bf923577108a88adaacae5c141b1576b0'), - iv String DEFAULT unhex('8CA3554377DFF8A369BC50A89780DD85'), - key32 String DEFAULT substring(key, 1, 32), - key24 String DEFAULT substring(key, 1, 24), - key16 String DEFAULT substring(key, 1, 16) -) Engine = Memory; + `comment` String, + `secret` String +) +ENGINE = Memory ``` -Insert this data: +Insert some data (please avoid storing the keys/ivs in the database as this undermines the whole concept of encryption), also storing 'hints' is unsafe too and used only for illustrative purposes: Query: ``` sql -INSERT INTO encryption_test (input) VALUES (''), ('text'), ('What Is ClickHouse?'); +INSERT INTO encryption_test VALUES('aes-256-cfb128 no IV', encrypt('aes-256-cfb128', 'Secret', '12345678910121314151617181920212')),\ +('aes-256-cfb128 no IV, different key', encrypt('aes-256-cfb128', 'Secret', 'keykeykeykeykeykeykeykeykeykeyke')),\ +('aes-256-cfb128 with IV', encrypt('aes-256-cfb128', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv')),\ +('aes-256-cbc no IV', encrypt('aes-256-cbc', 'Secret', '12345678910121314151617181920212')); ``` -Example without `iv`: - Query: ``` sql -SELECT 'aes-128-ecb' AS mode, hex(encrypt(mode, input, key16)) FROM encryption_test; +SELECT comment, hex(secret) FROM encryption_test; ``` Result: ``` text -┌─mode────────┬─hex(encrypt('aes-128-ecb', input, key16))────────────────────────┐ -│ aes-128-ecb │ 4603E6862B0D94BBEC68E0B0DF51D60F │ -│ aes-128-ecb │ 3004851B86D3F3950672DE7085D27C03 │ -│ aes-128-ecb │ E807F8C8D40A11F65076361AFC7D8B68D8658C5FAA6457985CAA380F16B3F7E4 │ -└─────────────┴──────────────────────────────────────────────────────────────────┘ -``` - -Example with `iv`: - -Query: - -``` sql -SELECT 'aes-256-ctr' AS mode, hex(encrypt(mode, input, key32, iv)) FROM encryption_test; -``` - -Result: - -``` text -┌─mode────────┬─hex(encrypt('aes-256-ctr', input, key32, iv))─┐ -│ aes-256-ctr │ │ -│ aes-256-ctr │ 7FB039F7 │ -│ aes-256-ctr │ 5CBD20F7ABD3AC41FCAA1A5C0E119E2B325949 │ -└─────────────┴───────────────────────────────────────────────┘ +┌─comment─────────────────────────────┬─hex(secret)──────────────────────┐ +│ aes-256-cfb128 no IV │ B4972BDC4459 │ +│ aes-256-cfb128 no IV, different key │ 2FF57C092DC9 │ +│ aes-256-cfb128 with IV │ 5E6CB398F653 │ +│ aes-256-cbc no IV │ 1BC0629A92450D9E73A00E7D02CF4142 │ +└─────────────────────────────────────┴──────────────────────────────────┘ ``` Example with `-gcm`: @@ -110,40 +91,26 @@ Example with `-gcm`: Query: ``` sql -SELECT 'aes-256-gcm' AS mode, hex(encrypt(mode, input, key32, iv)) FROM encryption_test; +INSERT INTO encryption_test VALUES('aes-256-gcm', encrypt('aes-256-gcm', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv')), \ +('aes-256-gcm with AAD', encrypt('aes-256-gcm', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv', 'aad')); + +SELECT comment, hex(secret) FROM encryption_test WHERE comment LIKE '%gcm%'; ``` Result: ``` text -┌─mode────────┬─hex(encrypt('aes-256-gcm', input, key32, iv))──────────────────────────┐ -│ aes-256-gcm │ E99DBEBC01F021758352D7FBD9039EFA │ -│ aes-256-gcm │ 8742CE3A7B0595B281C712600D274CA881F47414 │ -│ aes-256-gcm │ A44FD73ACEB1A64BDE2D03808A2576EDBB60764CC6982DB9AF2C33C893D91B00C60DC5 │ -└─────────────┴────────────────────────────────────────────────────────────────────────┘ -``` - -Example with `-gcm` mode and with `aad`: - -Query: - -``` sql -SELECT 'aes-192-gcm' AS mode, hex(encrypt(mode, input, key24, iv, 'AAD')) FROM encryption_test; -``` - -Result: - -``` text -┌─mode────────┬─hex(encrypt('aes-192-gcm', input, key24, iv, 'AAD'))───────────────────┐ -│ aes-192-gcm │ 04C13E4B1D62481ED22B3644595CB5DB │ -│ aes-192-gcm │ 9A6CF0FD2B329B04EAD18301818F016DF8F77447 │ -│ aes-192-gcm │ B961E9FD9B940EBAD7ADDA75C9F198A40797A5EA1722D542890CC976E21113BBB8A7AA │ -└─────────────┴────────────────────────────────────────────────────────────────────────┘ +┌─comment──────────────┬─hex(secret)──────────────────────────────────┐ +│ aes-256-gcm │ A8A3CCBC6426CFEEB60E4EAE03D3E94204C1B09E0254 │ +│ aes-256-gcm with AAD │ A8A3CCBC6426D9A1017A0A932322F1852260A4AD6837 │ +└──────────────────────┴──────────────────────────────────────────────┘ ``` ## aes_encrypt_mysql {#aes_encrypt_mysql} -Compatible with mysql encryption and can be decrypted with [AES_DECRYPT](https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html#function_aes-decrypt) function. +Compatible with mysql encryption and resulting ciphertext can be decrypted with [AES_DECRYPT](https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html#function_aes-decrypt) function. + +Will produce same ciphertext as `encrypt` on equal inputs. But when `key` or `iv` are longer than they should normally be, `aes_encrypt_mysql` will stick to what MySQL's `aes_encrypt` does: 'fold' `key` and ignore excess bits of `IV`. Supported encryption modes: @@ -156,7 +123,7 @@ Supported encryption modes: **Syntax** -```sql +``` sql aes_encrypt_mysql('mode', 'plaintext', 'key' [, iv]) ``` @@ -164,78 +131,98 @@ aes_encrypt_mysql('mode', 'plaintext', 'key' [, iv]) - `mode` — Encryption mode. [String](../../sql-reference/data-types/string.md#string). - `plaintext` — Text that needs to be encrypted. [String](../../sql-reference/data-types/string.md#string). -- `key` — Encryption key. [String](../../sql-reference/data-types/string.md#string). -- `iv` — Initialization vector. Optinal. [String](../../sql-reference/data-types/string.md#string). +- `key` — Encryption key. If key is longer than required by mode, MySQL-specific key folding is performed. [String](../../sql-reference/data-types/string.md#string). +- `iv` — Initialization vector. Optinal, only first 16 bytes are taken into account [String](../../sql-reference/data-types/string.md#string). **Returned value** -- Ciphered String. [String](../../sql-reference/data-types/string.md#string). +- Ciphertext binary string. [String](../../sql-reference/data-types/string.md#string). + **Examples** -Create this table: +Given equal input `encrypt` and `aes_encrypt_mysql` produce the same ciphertext: Query: ``` sql -CREATE TABLE encryption_test -( - input String, - key String DEFAULT unhex('fb9958e2e897ef3fdb49067b51a24af645b3626eed2f9ea1dc7fd4dd71b7e38f9a68db2a3184f952382c783785f9d77bf923577108a88adaacae5c141b1576b0'), - iv String DEFAULT unhex('8CA3554377DFF8A369BC50A89780DD85'), - key32 String DEFAULT substring(key, 1, 32), - key24 String DEFAULT substring(key, 1, 24), - key16 String DEFAULT substring(key, 1, 16) -) Engine = Memory; +SELECT encrypt('aes-256-cfb128', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv') = aes_encrypt_mysql('aes-256-cfb128', 'Secret', '12345678910121314151617181920212', 'iviviviviviviviv') AS ciphertexts_equal; ``` -Insert this data: +Result: -Query: - -``` sql -INSERT INTO encryption_test (input) VALUES (''), ('text'), ('What Is ClickHouse?'); +``` +┌─ciphertexts_equal─┐ +│ 1 │ +└───────────────────┘ ``` -Example without `iv`: + +But `encrypt` fails when `key` or `iv` is longer than expected: Query: ``` sql -SELECT 'aes-128-cbc' AS mode, hex(aes_encrypt_mysql(mode, input, key32)) FROM encryption_test; +SELECT encrypt('aes-256-cfb128', 'Secret', '123456789101213141516171819202122', 'iviviviviviviviv123'); ``` Result: ``` text -┌─mode────────┬─hex(aes_encrypt_mysql('aes-128-cbc', input, key32))──────────────┐ -│ aes-128-cbc │ FEA8CFDE6EE2C6E7A2CC6ADDC9F62C83 │ -│ aes-128-cbc │ 78B16CD4BE107660156124C5FEE6454A │ -│ aes-128-cbc │ 67C0B119D96F18E2823968D42871B3D179221B1E7EE642D628341C2B29BA2E18 │ -└─────────────┴──────────────────────────────────────────────────────────────────┘ +Received exception from server (version 21.1.2): +Code: 36. DB::Exception: Received from localhost:9000. DB::Exception: Invalid key size: 33 expected 32: While processing encrypt('aes-256-cfb128', 'Secret', '123456789101213141516171819202122', 'iviviviviviviviv123'). ``` -Example with `iv`: +While `aes_encrypt_mysql` produces MySQL-compatitalbe output: Query: ``` sql -SELECT 'aes-256-cfb128' AS mode, hex(aes_encrypt_mysql(mode, input, key32, iv)) FROM encryption_test; +SELECT hex(aes_encrypt_mysql('aes-256-cfb128', 'Secret', '123456789101213141516171819202122', 'iviviviviviviviv123')) AS ciphertext; +``` + +Result: + +```text +┌─ciphertext───┐ +│ 24E9E4966469 │ +└──────────────┘ +``` + +Notice how supplying even longer `IV` produces the same result + +Query: + +``` sql +SELECT hex(aes_encrypt_mysql('aes-256-cfb128', 'Secret', '123456789101213141516171819202122', 'iviviviviviviviv123456')) AS ciphertext ``` Result: ``` text -┌─mode───────────┬─hex(aes_encrypt_mysql('aes-256-cfb128', input, key32, iv))─┐ -│ aes-256-cfb128 │ │ -│ aes-256-cfb128 │ 7FB039F7 │ -│ aes-256-cfb128 │ 5CBD20F7ABD3AC41FCAA1A5C0E119E2BB5174F │ -└────────────────┴────────────────────────────────────────────────────────────┘ +┌─ciphertext───┐ +│ 24E9E4966469 │ +└──────────────┘ +``` + +Which is binary equal to what MySQL produces on same inputs: + +``` sql +mysql> SET block_encryption_mode='aes-256-cfb128'; +Query OK, 0 rows affected (0.00 sec) + +mysql> SELECT aes_encrypt('Secret', '123456789101213141516171819202122', 'iviviviviviviviv123456') as ciphertext; ++------------------------+ +| ciphertext | ++------------------------+ +| 0x24E9E4966469 | ++------------------------+ +1 row in set (0.00 sec) ``` ## decrypt {#decrypt} -This function decrypts data using these modes: +This function decrypts ciphertext into a plaintext using these modes: - aes-128-ecb, aes-192-ecb, aes-256-ecb - aes-128-cbc, aes-192-cbc, aes-256-cbc @@ -247,7 +234,7 @@ This function decrypts data using these modes: **Syntax** -```sql +``` sql decrypt('mode', 'ciphertext', 'key' [, iv, aad]) ``` @@ -265,51 +252,56 @@ decrypt('mode', 'ciphertext', 'key' [, iv, aad]) **Examples** -Create this table: +Re-using table from [encrypt](./encryption-functions.md#encrypt). Query: ``` sql -CREATE TABLE encryption_test -( - input String, - key String DEFAULT unhex('fb9958e2e897ef3fdb49067b51a24af645b3626eed2f9ea1dc7fd4dd71b7e38f9a68db2a3184f952382c783785f9d77bf923577108a88adaacae5c141b1576b0'), - iv String DEFAULT unhex('8CA3554377DFF8A369BC50A89780DD85'), - key32 String DEFAULT substring(key, 1, 32), - key24 String DEFAULT substring(key, 1, 24), - key16 String DEFAULT substring(key, 1, 16) -) Engine = Memory; -``` - -Insert this data: - -Query: - -``` sql -INSERT INTO encryption_test (input) VALUES (''), ('text'), ('What Is ClickHouse?'); -``` - -Query: - -``` sql - -SELECT 'aes-128-ecb' AS mode, decrypt(mode, encrypt(mode, input, key16), key16) FROM encryption_test; +SELECT comment, hex(secret) FROM encryption_test; ``` Result: -```text -┌─mode────────┬─decrypt('aes-128-ecb', encrypt('aes-128-ecb', input, key16), key16)─┐ -│ aes-128-ecb │ │ -│ aes-128-ecb │ text │ -│ aes-128-ecb │ What Is ClickHouse? │ -└─────────────┴─────────────────────────────────────────────────────────────────────┘ +``` text +┌─comment──────────────┬─hex(secret)──────────────────────────────────┐ +│ aes-256-gcm │ A8A3CCBC6426CFEEB60E4EAE03D3E94204C1B09E0254 │ +│ aes-256-gcm with AAD │ A8A3CCBC6426D9A1017A0A932322F1852260A4AD6837 │ +└──────────────────────┴──────────────────────────────────────────────┘ +┌─comment─────────────────────────────┬─hex(secret)──────────────────────┐ +│ aes-256-cfb128 no IV │ B4972BDC4459 │ +│ aes-256-cfb128 no IV, different key │ 2FF57C092DC9 │ +│ aes-256-cfb128 with IV │ 5E6CB398F653 │ +│ aes-256-cbc no IV │ 1BC0629A92450D9E73A00E7D02CF4142 │ +└─────────────────────────────────────┴──────────────────────────────────┘ ``` +Now let's try to decrypt all that data. + +Query: + +``` sql +SELECT comment, decrypt('aes-256-cfb128', secret, '12345678910121314151617181920212') as plaintext FROM encryption_test +``` + +Result: +``` text +┌─comment─────────────────────────────┬─plaintext─┐ +│ aes-256-cfb128 no IV │ Secret │ +│ aes-256-cfb128 no IV, different key │ �4� + � │ +│ aes-256-cfb128 with IV │ ���6�~ │ + │aes-256-cbc no IV │ �2*4�h3c�4w��@ +└─────────────────────────────────────┴───────────┘ +``` + +Notice how only portion of the data was properly decrypted, and the rest is gibberish since either `mode`, `key`, or `iv` were different upon encryption. + ## aes_decrypt_mysql {#aes_decrypt_mysql} Compatible with mysql encryption and decrypts data encrypted with [AES_ENCRYPT](https://dev.mysql.com/doc/refman/8.0/en/encryption-functions.html#function_aes-encrypt) function. +Will produce same plaintext as `decrypt` on equal inputs. But when `key` or `iv` are longer than they should normally be, `aes_decrypt_mysql` will stick to what MySQL's `aes_decrypt` does: 'fold' `key` and ignore excess bits of `IV`. + Supported decryption modes: - aes-128-ecb, aes-192-ecb, aes-256-ecb @@ -321,7 +313,7 @@ Supported decryption modes: **Syntax** -```sql +``` sql aes_decrypt_mysql('mode', 'ciphertext', 'key' [, iv]) ``` @@ -338,44 +330,30 @@ aes_decrypt_mysql('mode', 'ciphertext', 'key' [, iv]) **Examples** -Create this table: - -Query: - +Let's decrypt data we've previously encrypted with MySQL: ``` sql -CREATE TABLE encryption_test -( - input String, - key String DEFAULT unhex('fb9958e2e897ef3fdb49067b51a24af645b3626eed2f9ea1dc7fd4dd71b7e38f9a68db2a3184f952382c783785f9d77bf923577108a88adaacae5c141b1576b0'), - iv String DEFAULT unhex('8CA3554377DFF8A369BC50A89780DD85'), - key32 String DEFAULT substring(key, 1, 32), - key24 String DEFAULT substring(key, 1, 24), - key16 String DEFAULT substring(key, 1, 16) -) Engine = Memory; -``` +mysql> SET block_encryption_mode='aes-256-cfb128'; +Query OK, 0 rows affected (0.00 sec) -Insert this data: - -Query: - -``` sql -INSERT INTO encryption_test (input) VALUES (''), ('text'), ('What Is ClickHouse?'); +mysql> SELECT aes_encrypt('Secret', '123456789101213141516171819202122', 'iviviviviviviviv123456') as ciphertext; ++------------------------+ +| ciphertext | ++------------------------+ +| 0x24E9E4966469 | ++------------------------+ +1 row in set (0.00 sec) ``` Query: - ``` sql -SELECT 'aes-128-cbc' AS mode, aes_decrypt_mysql(mode, aes_encrypt_mysql(mode, input, key), key) FROM encryption_test; +SELECT aes_decrypt_mysql('aes-256-cfb128', unhex('24E9E4966469'), '123456789101213141516171819202122', 'iviviviviviviviv123456') AS plaintext ``` Result: - ``` text -┌─mode────────┬─aes_decrypt_mysql('aes-128-cbc', aes_encrypt_mysql('aes-128-cbc', input, key), key)─┐ -│ aes-128-cbc │ │ -│ aes-128-cbc │ text │ -│ aes-128-cbc │ What Is ClickHouse? │ -└─────────────┴─────────────────────────────────────────────────────────────────────────────────────┘ +┌─plaintext─┐ +│ Secret │ +└───────────┘ ``` [Original article](https://clickhouse.tech/docs/en/sql-reference/functions/encryption_functions/) diff --git a/docs/en/sql-reference/functions/ip-address-functions.md b/docs/en/sql-reference/functions/ip-address-functions.md index faf551601ac..1361eb65a56 100644 --- a/docs/en/sql-reference/functions/ip-address-functions.md +++ b/docs/en/sql-reference/functions/ip-address-functions.md @@ -115,9 +115,20 @@ LIMIT 10 ## IPv6StringToNum(s) {#ipv6stringtonums} -The reverse function of IPv6NumToString. If the IPv6 address has an invalid format, it returns a string of null bytes. +The reverse function of IPv6NumToString. If the IPv6 address has an invalid format, it returns a string of null bytes. +If the IP address is a valid IPv4 address then the IPv6 equivalent of the IPv4 address is returned. HEX can be uppercase or lowercase. +``` sql +SELECT cutIPv6(IPv6StringToNum('127.0.0.1'), 0, 0); +``` + +``` text +┌─cutIPv6(IPv6StringToNum('127.0.0.1'), 0, 0)─┐ +│ ::ffff:127.0.0.1 │ +└─────────────────────────────────────────────┘ +``` + ## IPv4ToIPv6(x) {#ipv4toipv6x} Takes a `UInt32` number. Interprets it as an IPv4 address in [big endian](https://en.wikipedia.org/wiki/Endianness). Returns a `FixedString(16)` value containing the IPv6 address in binary format. Examples: @@ -214,6 +225,7 @@ SELECT ## toIPv6(string) {#toipv6string} An alias to `IPv6StringToNum()` that takes a string form of IPv6 address and returns value of [IPv6](../../sql-reference/data-types/domains/ipv6.md) type, which is binary equal to value returned by `IPv6StringToNum()`. +If the IP address is a valid IPv4 address then the IPv6 equivalent of the IPv4 address is returned. ``` sql WITH @@ -243,6 +255,15 @@ SELECT └───────────────────────────────────┴──────────────────────────────────┘ ``` +``` sql +SELECT toIPv6('127.0.0.1') +``` + +``` text +┌─toIPv6('127.0.0.1')─┐ +│ ::ffff:127.0.0.1 │ +└─────────────────────┘ +``` ## isIPv4String diff --git a/programs/benchmark/Benchmark.cpp b/programs/benchmark/Benchmark.cpp index ae1d16ce402..a0e2ea155ba 100644 --- a/programs/benchmark/Benchmark.cpp +++ b/programs/benchmark/Benchmark.cpp @@ -62,12 +62,12 @@ public: bool randomize_, size_t max_iterations_, double max_time_, const String & json_path_, size_t confidence_, const String & query_id_, const String & query_to_execute_, bool continue_on_errors_, - bool print_stacktrace_, const Settings & settings_) + bool reconnect_, bool print_stacktrace_, const Settings & settings_) : concurrency(concurrency_), delay(delay_), queue(concurrency), randomize(randomize_), cumulative(cumulative_), max_iterations(max_iterations_), max_time(max_time_), json_path(json_path_), confidence(confidence_), query_id(query_id_), - query_to_execute(query_to_execute_), continue_on_errors(continue_on_errors_), + query_to_execute(query_to_execute_), continue_on_errors(continue_on_errors_), reconnect(reconnect_), print_stacktrace(print_stacktrace_), settings(settings_), shared_context(Context::createShared()), global_context(Context::createGlobal(shared_context.get())), pool(concurrency) @@ -155,6 +155,7 @@ private: String query_id; String query_to_execute; bool continue_on_errors; + bool reconnect; bool print_stacktrace; const Settings & settings; SharedContextHolder shared_context; @@ -404,9 +405,14 @@ private: void execute(EntryPtrs & connection_entries, Query & query, size_t connection_index) { Stopwatch watch; + + Connection & connection = **connection_entries[connection_index]; + + if (reconnect) + connection.disconnect(); + RemoteBlockInputStream stream( - *(*connection_entries[connection_index]), - query, {}, global_context, nullptr, Scalars(), Tables(), query_processing_stage); + connection, query, {}, global_context, nullptr, Scalars(), Tables(), query_processing_stage); if (!query_id.empty()) stream.setQueryId(query_id); @@ -589,6 +595,7 @@ int mainEntryClickHouseBenchmark(int argc, char ** argv) ("confidence", value()->default_value(5), "set the level of confidence for T-test [0=80%, 1=90%, 2=95%, 3=98%, 4=99%, 5=99.5%(default)") ("query_id", value()->default_value(""), "") ("continue_on_errors", "continue testing even if a query fails") + ("reconnect", "establish new connection for every query") ; Settings settings; @@ -638,7 +645,8 @@ int mainEntryClickHouseBenchmark(int argc, char ** argv) options["confidence"].as(), options["query_id"].as(), options["query"].as(), - options.count("continue_on_errors") > 0, + options.count("continue_on_errors"), + options.count("reconnect"), print_stacktrace, settings); return benchmark.run(); diff --git a/src/AggregateFunctions/IAggregateFunction.h b/src/AggregateFunctions/IAggregateFunction.h index 4c76ccf7720..c84be1a6b5b 100644 --- a/src/AggregateFunctions/IAggregateFunction.h +++ b/src/AggregateFunctions/IAggregateFunction.h @@ -513,7 +513,7 @@ private: } protected: - void extractColumns(const IColumn ** columns, const IColumn ** aggr_columns) const + ssize_t extractColumns(const IColumn ** columns, const IColumn ** aggr_columns, ssize_t if_argument_pos) const { if (tuple_argument) { @@ -526,6 +526,13 @@ protected: for (size_t i = 0; i < args_count; ++i) columns[i] = aggr_columns[i]; } + if (if_argument_pos >= 0) + { + columns[args_count] = aggr_columns[if_argument_pos]; + return args_count; + } + else + return -1; } bool tuple_argument; @@ -551,8 +558,8 @@ public: Arena * arena, ssize_t if_argument_pos = -1) const override { - const IColumn * ex_columns[args_count]; - extractColumns(ex_columns, columns); + const IColumn * ex_columns[args_count + (if_argument_pos >= 0)]; + if_argument_pos = extractColumns(ex_columns, columns, if_argument_pos); Base::addBatch(batch_size, places, place_offset, ex_columns, arena, if_argument_pos); } @@ -560,8 +567,8 @@ public: void addBatchSinglePlace( size_t batch_size, AggregateDataPtr place, const IColumn ** columns, Arena * arena, ssize_t if_argument_pos = -1) const override { - const IColumn * ex_columns[args_count]; - extractColumns(ex_columns, columns); + const IColumn * ex_columns[args_count + (if_argument_pos >= 0)]; + if_argument_pos = extractColumns(ex_columns, columns, if_argument_pos); Base::addBatchSinglePlace(batch_size, place, ex_columns, arena, if_argument_pos); } @@ -574,8 +581,8 @@ public: Arena * arena, ssize_t if_argument_pos = -1) const override { - const IColumn * ex_columns[args_count]; - extractColumns(ex_columns, columns); + const IColumn * ex_columns[args_count + (if_argument_pos >= 0)]; + if_argument_pos = extractColumns(ex_columns, columns, if_argument_pos); Base::addBatchSinglePlaceNotNull(batch_size, place, ex_columns, null_map, arena, if_argument_pos); } @@ -584,8 +591,8 @@ public: size_t batch_begin, size_t batch_end, AggregateDataPtr place, const IColumn ** columns, Arena * arena, ssize_t if_argument_pos = -1) const override { - const IColumn * ex_columns[args_count]; - extractColumns(ex_columns, columns); + const IColumn * ex_columns[args_count + (if_argument_pos >= 0)]; + if_argument_pos = extractColumns(ex_columns, columns, if_argument_pos); Base::addBatchSinglePlaceFromInterval(batch_begin, batch_end, place, ex_columns, arena, if_argument_pos); } @@ -595,7 +602,7 @@ public: const override { const IColumn * ex_columns[args_count]; - extractColumns(ex_columns, columns); + extractColumns(ex_columns, columns, -1); Base::addBatchArray(batch_size, places, place_offset, ex_columns, offsets, arena); } @@ -610,7 +617,7 @@ public: Arena * arena) const override { const IColumn * ex_columns[args_count]; - extractColumns(ex_columns, columns); + extractColumns(ex_columns, columns, -1); Base::addBatchLookupTable8(batch_size, map, place_offset, init, key, ex_columns, arena); } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e62d2c0bec5..34c437ebde6 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -191,6 +191,7 @@ add_object_library(clickhouse_processors_sources Processors/Sources) add_object_library(clickhouse_processors_merges Processors/Merges) add_object_library(clickhouse_processors_merges_algorithms Processors/Merges/Algorithms) add_object_library(clickhouse_processors_queryplan Processors/QueryPlan) +add_object_library(clickhouse_processors_queryplan_optimizations Processors/QueryPlan/Optimizations) add_object_library(clickhouse_coordination Coordination) set (DBMS_COMMON_LIBRARIES) diff --git a/src/Client/tests/test_connect.cpp b/src/Client/tests/test_connect.cpp index 1259980f9a6..1b98b936a52 100644 --- a/src/Client/tests/test_connect.cpp +++ b/src/Client/tests/test_connect.cpp @@ -7,8 +7,10 @@ #include #include #include -#include +#include #include +#include +#include /** In a loop it connects to the server and immediately breaks the connection. @@ -18,22 +20,26 @@ int main(int argc, char ** argv) try { + using namespace DB; + size_t num_iterations = 1; size_t num_threads = 1; std::string host = "localhost"; uint16_t port = 9000; if (argc >= 2) - num_iterations = DB::parse(argv[1]); + num_iterations = parse(argv[1]); if (argc >= 3) - num_threads = DB::parse(argv[2]); + num_threads = parse(argv[2]); if (argc >= 4) host = argv[3]; if (argc >= 5) - port = DB::parse(argv[4]); + port = parse(argv[4]); + + WriteBufferFromFileDescriptor out(STDERR_FILENO); std::atomic_bool cancel{false}; std::vector threads(num_threads); @@ -45,44 +51,32 @@ try { std::cerr << "."; - Poco::Net::SocketAddress address(host, port); - - int fd = socket(PF_INET, SOCK_STREAM, IPPROTO_IP); - - if (fd < 0) - DB::throwFromErrno("Cannot create socket", 0); - - linger linger_value; - linger_value.l_onoff = 1; - linger_value.l_linger = 0; - - if (0 != setsockopt(fd, SOL_SOCKET, SO_LINGER, &linger_value, sizeof(linger_value))) - DB::throwFromErrno("Cannot set linger", 0); - try { - Stopwatch watch; + Poco::Net::SocketAddress address(host, port); + Poco::Net::StreamSocket socket; + //socket.setLinger(1, 0); - int res = connect(fd, address.addr(), address.length()); - - if (res != 0 && errno != EINPROGRESS && errno != EWOULDBLOCK) + socket.connectNB(address); + if (!socket.poll(Poco::Timespan(1000000), + Poco::Net::Socket::SELECT_READ | Poco::Net::Socket::SELECT_WRITE | Poco::Net::Socket::SELECT_ERROR)) { - close(fd); - DB::throwFromErrno("Cannot connect", 0); - } + /// Allow to debug the server. +/* auto command = ShellCommand::execute("kill -STOP $(pidof clickhouse-server)"); + copyData(command->err, out); + copyData(command->out, out); + command->wait();*/ - close(fd); - - if (watch.elapsedSeconds() > 0.1) - { - std::cerr << watch.elapsedSeconds() << "\n"; - cancel = true; - break; + std::cerr << "Timeout\n"; +/* cancel = true; + break;*/ } } catch (const Poco::Exception & e) { std::cerr << e.displayText() << "\n"; + cancel = true; + break; } } }); diff --git a/src/Columns/ColumnFixedString.cpp b/src/Columns/ColumnFixedString.cpp index a20e5d3ca0d..55e387ff2ee 100644 --- a/src/Columns/ColumnFixedString.cpp +++ b/src/Columns/ColumnFixedString.cpp @@ -289,7 +289,8 @@ ColumnPtr ColumnFixedString::filter(const IColumn::Filter & filt, ssize_t result while (filt_pos < filt_end_sse) { - int mask = _mm_movemask_epi8(_mm_cmpgt_epi8(_mm_loadu_si128(reinterpret_cast(filt_pos)), zero16)); + UInt16 mask = _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_loadu_si128(reinterpret_cast(filt_pos)), zero16)); + mask = ~mask; if (0 == mask) { diff --git a/src/Columns/ColumnVector.cpp b/src/Columns/ColumnVector.cpp index fcbcc63731a..a075c10a8a9 100644 --- a/src/Columns/ColumnVector.cpp +++ b/src/Columns/ColumnVector.cpp @@ -356,7 +356,8 @@ ColumnPtr ColumnVector::filter(const IColumn::Filter & filt, ssize_t result_s while (filt_pos < filt_end_sse) { - int mask = _mm_movemask_epi8(_mm_cmpgt_epi8(_mm_loadu_si128(reinterpret_cast(filt_pos)), zero16)); + UInt16 mask = _mm_movemask_epi8(_mm_cmpeq_epi8(_mm_loadu_si128(reinterpret_cast(filt_pos)), zero16)); + mask = ~mask; if (0 == mask) { diff --git a/src/Columns/ColumnsCommon.cpp b/src/Columns/ColumnsCommon.cpp index f3f10a25df3..3c356afa4da 100644 --- a/src/Columns/ColumnsCommon.cpp +++ b/src/Columns/ColumnsCommon.cpp @@ -17,13 +17,17 @@ namespace DB static UInt64 toBits64(const Int8 * bytes64) { static const __m128i zero16 = _mm_setzero_si128(); - return static_cast(_mm_movemask_epi8(_mm_cmpgt_epi8(_mm_loadu_si128(reinterpret_cast(bytes64)), zero16))) - | (static_cast(_mm_movemask_epi8(_mm_cmpgt_epi8(_mm_loadu_si128(reinterpret_cast(bytes64 + 16)), zero16))) - << 16) - | (static_cast(_mm_movemask_epi8(_mm_cmpgt_epi8(_mm_loadu_si128(reinterpret_cast(bytes64 + 32)), zero16))) - << 32) - | (static_cast(_mm_movemask_epi8(_mm_cmpgt_epi8(_mm_loadu_si128(reinterpret_cast(bytes64 + 48)), zero16))) - << 48); + UInt64 res = + static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast(bytes64)), zero16))) + | (static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast(bytes64 + 16)), zero16))) << 16) + | (static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast(bytes64 + 32)), zero16))) << 32) + | (static_cast(_mm_movemask_epi8(_mm_cmpeq_epi8( + _mm_loadu_si128(reinterpret_cast(bytes64 + 48)), zero16))) << 48); + + return ~res; } #endif @@ -49,7 +53,7 @@ size_t countBytesInFilter(const UInt8 * filt, size_t sz) #endif for (; pos < end; ++pos) - count += *pos > 0; + count += *pos != 0; return count; } @@ -82,7 +86,7 @@ size_t countBytesInFilterWithNull(const IColumn::Filter & filt, const UInt8 * nu #endif for (; pos < end; ++pos) - count += (*pos & ~*pos2) > 0; + count += (*pos & ~*pos2) != 0; return count; } @@ -232,9 +236,10 @@ namespace while (filt_pos < filt_end_aligned) { - const auto mask = _mm_movemask_epi8(_mm_cmpgt_epi8( + UInt16 mask = _mm_movemask_epi8(_mm_cmpeq_epi8( _mm_loadu_si128(reinterpret_cast(filt_pos)), zero_vec)); + mask = ~mask; if (mask == 0) { diff --git a/src/Common/Exception.cpp b/src/Common/Exception.cpp index 231b45a49c6..f5a40a11d9c 100644 --- a/src/Common/Exception.cpp +++ b/src/Common/Exception.cpp @@ -119,6 +119,13 @@ void tryLogCurrentException(const char * log_name, const std::string & start_of_ void tryLogCurrentException(Poco::Logger * logger, const std::string & start_of_message) { + /// Under high memory pressure, any new allocation will definitelly lead + /// to MEMORY_LIMIT_EXCEEDED exception. + /// + /// And in this case the exception will not be logged, so let's block the + /// MemoryTracker until the exception will be logged. + MemoryTracker::LockExceptionInThread lock_memory_tracker; + try { if (start_of_message.empty()) diff --git a/src/Common/FieldVisitorsAccurateComparison.h b/src/Common/FieldVisitorsAccurateComparison.h index 91fa4bf28de..84099eafb0f 100644 --- a/src/Common/FieldVisitorsAccurateComparison.h +++ b/src/Common/FieldVisitorsAccurateComparison.h @@ -53,7 +53,7 @@ public: if constexpr (std::is_arithmetic_v) { ReadBufferFromString in(l); - T parsed; + U parsed; readText(parsed, in); return operator()(parsed, r); } @@ -113,7 +113,7 @@ public: if constexpr (std::is_arithmetic_v) { ReadBufferFromString in(l); - T parsed; + U parsed; readText(parsed, in); return operator()(parsed, r); } diff --git a/src/Common/StringSearcher.h b/src/Common/StringSearcher.h index e39253c439f..0d2ee26889f 100644 --- a/src/Common/StringSearcher.h +++ b/src/Common/StringSearcher.h @@ -98,14 +98,31 @@ public: } else { - const auto first_u32 = UTF8::convert(needle); - const auto first_l_u32 = Poco::Unicode::toLower(first_u32); - const auto first_u_u32 = Poco::Unicode::toUpper(first_u32); + auto first_u32 = UTF8::convertUTF8ToCodePoint(needle, needle_size); + + /// Invalid UTF-8 + if (!first_u32) + { + /// Process it verbatim as a sequence of bytes. + size_t src_len = UTF8::seqLength(*needle); + + memcpy(l_seq, needle, src_len); + memcpy(u_seq, needle, src_len); + } + else + { + uint32_t first_l_u32 = Poco::Unicode::toLower(*first_u32); + uint32_t first_u_u32 = Poco::Unicode::toUpper(*first_u32); + + /// lower and uppercase variants of the first octet of the first character in `needle` + size_t length_l = UTF8::convertCodePointToUTF8(first_l_u32, l_seq, sizeof(l_seq)); + size_t length_r = UTF8::convertCodePointToUTF8(first_u_u32, u_seq, sizeof(u_seq)); + + if (length_l != length_r) + throw Exception{"UTF8 sequences with different lowercase and uppercase lengths are not supported", ErrorCodes::UNSUPPORTED_PARAMETER}; + } - /// lower and uppercase variants of the first octet of the first character in `needle` - UTF8::convert(first_l_u32, l_seq, sizeof(l_seq)); l = l_seq[0]; - UTF8::convert(first_u_u32, u_seq, sizeof(u_seq)); u = u_seq[0]; } @@ -128,18 +145,21 @@ public: continue; } - const auto src_len = UTF8::seqLength(*needle_pos); - const auto c_u32 = UTF8::convert(needle_pos); + size_t src_len = std::min(needle_end - needle_pos, UTF8::seqLength(*needle_pos)); + auto c_u32 = UTF8::convertUTF8ToCodePoint(needle_pos, src_len); - const auto c_l_u32 = Poco::Unicode::toLower(c_u32); - const auto c_u_u32 = Poco::Unicode::toUpper(c_u32); + if (c_u32) + { + int c_l_u32 = Poco::Unicode::toLower(*c_u32); + int c_u_u32 = Poco::Unicode::toUpper(*c_u32); - const auto dst_l_len = static_cast(UTF8::convert(c_l_u32, l_seq, sizeof(l_seq))); - const auto dst_u_len = static_cast(UTF8::convert(c_u_u32, u_seq, sizeof(u_seq))); + uint8_t dst_l_len = static_cast(UTF8::convertCodePointToUTF8(c_l_u32, l_seq, sizeof(l_seq))); + uint8_t dst_u_len = static_cast(UTF8::convertCodePointToUTF8(c_u_u32, u_seq, sizeof(u_seq))); - /// @note Unicode standard states it is a rare but possible occasion - if (!(dst_l_len == dst_u_len && dst_u_len == src_len)) - throw Exception{"UTF8 sequences with different lowercase and uppercase lengths are not supported", ErrorCodes::UNSUPPORTED_PARAMETER}; + /// @note Unicode standard states it is a rare but possible occasion + if (!(dst_l_len == dst_u_len && dst_u_len == src_len)) + throw Exception{"UTF8 sequences with different lowercase and uppercase lengths are not supported", ErrorCodes::UNSUPPORTED_PARAMETER}; + } cache_actual_len += src_len; if (cache_actual_len < n) @@ -164,7 +184,7 @@ public: } template > - ALWAYS_INLINE bool compare(const CharT * /*haystack*/, const CharT * /*haystack_end*/, const CharT * pos) const + ALWAYS_INLINE bool compare(const CharT * /*haystack*/, const CharT * haystack_end, const CharT * pos) const { #ifdef __SSE4_1__ @@ -183,11 +203,20 @@ public: pos += cache_valid_len; auto needle_pos = needle + cache_valid_len; - while (needle_pos < needle_end && - Poco::Unicode::toLower(UTF8::convert(pos)) == - Poco::Unicode::toLower(UTF8::convert(needle_pos))) + while (needle_pos < needle_end) { - /// @note assuming sequences for lowercase and uppercase have exact same length + auto haystack_code_point = UTF8::convertUTF8ToCodePoint(pos, haystack_end - pos); + auto needle_code_point = UTF8::convertUTF8ToCodePoint(needle_pos, needle_end - needle_pos); + + /// Invalid UTF-8, should not compare equals + if (!haystack_code_point || !needle_code_point) + break; + + /// Not equals case insensitive. + if (Poco::Unicode::toLower(*haystack_code_point) != Poco::Unicode::toLower(*needle_code_point)) + break; + + /// @note assuming sequences for lowercase and uppercase have exact same length (that is not always true) const auto len = UTF8::seqLength(*pos); pos += len; needle_pos += len; @@ -209,10 +238,19 @@ public: pos += first_needle_symbol_is_ascii; auto needle_pos = needle + first_needle_symbol_is_ascii; - while (needle_pos < needle_end && - Poco::Unicode::toLower(UTF8::convert(pos)) == - Poco::Unicode::toLower(UTF8::convert(needle_pos))) + while (needle_pos < needle_end) { + auto haystack_code_point = UTF8::convertUTF8ToCodePoint(pos, haystack_end - pos); + auto needle_code_point = UTF8::convertUTF8ToCodePoint(needle_pos, needle_end - needle_pos); + + /// Invalid UTF-8, should not compare equals + if (!haystack_code_point || !needle_code_point) + break; + + /// Not equals case insensitive. + if (Poco::Unicode::toLower(*haystack_code_point) != Poco::Unicode::toLower(*needle_code_point)) + break; + const auto len = UTF8::seqLength(*pos); pos += len; needle_pos += len; @@ -270,11 +308,20 @@ public: auto haystack_pos = haystack + cache_valid_len; auto needle_pos = needle + cache_valid_len; - while (haystack_pos < haystack_end && needle_pos < needle_end && - Poco::Unicode::toLower(UTF8::convert(haystack_pos)) == - Poco::Unicode::toLower(UTF8::convert(needle_pos))) + while (haystack_pos < haystack_end && needle_pos < needle_end) { - /// @note assuming sequences for lowercase and uppercase have exact same length + auto haystack_code_point = UTF8::convertUTF8ToCodePoint(haystack_pos, haystack_end - haystack_pos); + auto needle_code_point = UTF8::convertUTF8ToCodePoint(needle_pos, needle_end - needle_pos); + + /// Invalid UTF-8, should not compare equals + if (!haystack_code_point || !needle_code_point) + break; + + /// Not equals case insensitive. + if (Poco::Unicode::toLower(*haystack_code_point) != Poco::Unicode::toLower(*needle_code_point)) + break; + + /// @note assuming sequences for lowercase and uppercase have exact same length (that is not always true) const auto len = UTF8::seqLength(*haystack_pos); haystack_pos += len; needle_pos += len; @@ -302,10 +349,19 @@ public: auto haystack_pos = haystack + first_needle_symbol_is_ascii; auto needle_pos = needle + first_needle_symbol_is_ascii; - while (haystack_pos < haystack_end && needle_pos < needle_end && - Poco::Unicode::toLower(UTF8::convert(haystack_pos)) == - Poco::Unicode::toLower(UTF8::convert(needle_pos))) + while (haystack_pos < haystack_end && needle_pos < needle_end) { + auto haystack_code_point = UTF8::convertUTF8ToCodePoint(haystack_pos, haystack_end - haystack_pos); + auto needle_code_point = UTF8::convertUTF8ToCodePoint(needle_pos, needle_end - needle_pos); + + /// Invalid UTF-8, should not compare equals + if (!haystack_code_point || !needle_code_point) + break; + + /// Not equals case insensitive. + if (Poco::Unicode::toLower(*haystack_code_point) != Poco::Unicode::toLower(*needle_code_point)) + break; + const auto len = UTF8::seqLength(*haystack_pos); haystack_pos += len; needle_pos += len; diff --git a/src/Common/UTF8Helpers.h b/src/Common/UTF8Helpers.h index e795b6846b2..f25ed55a6af 100644 --- a/src/Common/UTF8Helpers.h +++ b/src/Common/UTF8Helpers.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -73,26 +74,27 @@ inline size_t countCodePoints(const UInt8 * data, size_t size) return res; } + template > -int convert(const CharT * bytes) +size_t convertCodePointToUTF8(uint32_t code_point, CharT * out_bytes, size_t out_length) { static const Poco::UTF8Encoding utf8; - return utf8.convert(reinterpret_cast(bytes)); + int res = utf8.convert(code_point, reinterpret_cast(out_bytes), out_length); + assert(res >= 0); + return res; } template > -int convert(int ch, CharT * bytes, int length) +std::optional convertUTF8ToCodePoint(const CharT * in_bytes, size_t in_length) { static const Poco::UTF8Encoding utf8; - return utf8.convert(ch, reinterpret_cast(bytes), length); + int res = utf8.queryConvert(reinterpret_cast(in_bytes), in_length); + + if (res >= 0) + return res; + return {}; } -template > -int queryConvert(const CharT * bytes, int length) -{ - static const Poco::UTF8Encoding utf8; - return utf8.queryConvert(reinterpret_cast(bytes), length); -} /// returns UTF-8 wcswidth. Invalid sequence is treated as zero width character. /// `prefix` is used to compute the `\t` width which extends the string before diff --git a/src/Common/Volnitsky.h b/src/Common/Volnitsky.h index a1fa83b4f33..c674015fba9 100644 --- a/src/Common/Volnitsky.h +++ b/src/Common/Volnitsky.h @@ -60,7 +60,7 @@ namespace VolnitskyTraits static inline Ngram toNGram(const UInt8 * const pos) { return unalignedLoad(pos); } template - static inline void putNGramASCIICaseInsensitive(const UInt8 * const pos, const int offset, const Callback & putNGramBase) + static inline void putNGramASCIICaseInsensitive(const UInt8 * pos, int offset, Callback && putNGramBase) { struct Chars { @@ -109,199 +109,234 @@ namespace VolnitskyTraits putNGramBase(n, offset); } - template - static inline void putNGram(const UInt8 * const pos, const int offset, [[maybe_unused]] const UInt8 * const begin, const Callback & putNGramBase) + template + static inline void putNGramUTF8CaseInsensitive( + const UInt8 * pos, int offset, const UInt8 * begin, size_t size, Callback && putNGramBase) { - if constexpr (CaseSensitive) + const UInt8 * end = begin + size; + + struct Chars { - putNGramBase(toNGram(pos), offset); + UInt8 c0; + UInt8 c1; + }; + + union + { + VolnitskyTraits::Ngram n; + Chars chars; + }; + + n = toNGram(pos); + + if (isascii(chars.c0) && isascii(chars.c1)) + { + putNGramASCIICaseInsensitive(pos, offset, putNGramBase); } else { - if constexpr (ASCII) + /** n-gram (in the case of n = 2) + * can be entirely located within one code point, + * or intersect with two code points. + * + * In the first case, you need to consider up to two alternatives - this code point in upper and lower case, + * and in the second case - up to four alternatives - fragments of two code points in all combinations of cases. + * + * It does not take into account the dependence of the case-transformation from the locale (for example - Turkish `Ii`) + * as well as composition / decomposition and other features. + * + * It also does not work if characters with lower and upper cases are represented by different number of bytes or code points. + */ + + using Seq = UInt8[6]; + + if (UTF8::isContinuationOctet(chars.c1)) { - putNGramASCIICaseInsensitive(pos, offset, putNGramBase); - } - else - { - struct Chars + /// ngram is inside a sequence + auto seq_pos = pos; + UTF8::syncBackward(seq_pos, begin); + + auto u32 = UTF8::convertUTF8ToCodePoint(seq_pos, end - seq_pos); + /// Invalid UTF-8 + if (!u32) { - UInt8 c0; - UInt8 c1; - }; - - union - { - VolnitskyTraits::Ngram n; - Chars chars; - }; - - n = toNGram(pos); - - if (isascii(chars.c0) && isascii(chars.c1)) - putNGramASCIICaseInsensitive(pos, offset, putNGramBase); + putNGramBase(n, offset); + } else { - /** n-gram (in the case of n = 2) - * can be entirely located within one code point, - * or intersect with two code points. - * - * In the first case, you need to consider up to two alternatives - this code point in upper and lower case, - * and in the second case - up to four alternatives - fragments of two code points in all combinations of cases. - * - * It does not take into account the dependence of the case-transformation from the locale (for example - Turkish `Ii`) - * as well as composition / decomposition and other features. - * - * It also does not work if characters with lower and upper cases are represented by different number of bytes or code points. - */ + int l_u32 = Poco::Unicode::toLower(*u32); + int u_u32 = Poco::Unicode::toUpper(*u32); - using Seq = UInt8[6]; - - if (UTF8::isContinuationOctet(chars.c1)) + /// symbol is case-independent + if (l_u32 == u_u32) { - /// ngram is inside a sequence - auto seq_pos = pos; - UTF8::syncBackward(seq_pos, begin); - - const auto u32 = UTF8::convert(seq_pos); - const auto l_u32 = Poco::Unicode::toLower(u32); - const auto u_u32 = Poco::Unicode::toUpper(u32); - - /// symbol is case-independent - if (l_u32 == u_u32) - putNGramBase(n, offset); - else - { - /// where is the given ngram in respect to the start of UTF-8 sequence? - const auto seq_ngram_offset = pos - seq_pos; - - Seq seq; - - /// put ngram for lowercase - UTF8::convert(l_u32, seq, sizeof(seq)); - chars.c0 = seq[seq_ngram_offset]; - chars.c1 = seq[seq_ngram_offset + 1]; - putNGramBase(n, offset); - - /// put ngram for uppercase - UTF8::convert(u_u32, seq, sizeof(seq)); - chars.c0 = seq[seq_ngram_offset]; //-V519 - chars.c1 = seq[seq_ngram_offset + 1]; //-V519 - putNGramBase(n, offset); - } + putNGramBase(n, offset); } else { - /// ngram is on the boundary of two sequences - /// first sequence may start before u_pos if it is not ASCII - auto first_seq_pos = pos; - UTF8::syncBackward(first_seq_pos, begin); - /// where is the given ngram in respect to the start of first UTF-8 sequence? - const auto seq_ngram_offset = pos - first_seq_pos; + /// where is the given ngram in respect to the start of UTF-8 sequence? + size_t seq_ngram_offset = pos - seq_pos; - const auto first_u32 = UTF8::convert(first_seq_pos); - const auto first_l_u32 = Poco::Unicode::toLower(first_u32); - const auto first_u_u32 = Poco::Unicode::toUpper(first_u32); + Seq seq; - /// second sequence always start immediately after u_pos - auto second_seq_pos = pos + 1; + /// put ngram for lowercase + size_t length_l [[maybe_unused]] = UTF8::convertCodePointToUTF8(l_u32, seq, sizeof(seq)); + assert(length_l >= 2); + chars.c0 = seq[seq_ngram_offset]; + chars.c1 = seq[seq_ngram_offset + 1]; + putNGramBase(n, offset); - const auto second_u32 = UTF8::convert(second_seq_pos); /// TODO This assumes valid UTF-8 or zero byte after needle. - const auto second_l_u32 = Poco::Unicode::toLower(second_u32); - const auto second_u_u32 = Poco::Unicode::toUpper(second_u32); + /// put ngram for uppercase + size_t length_r [[maybe_unused]] = UTF8::convertCodePointToUTF8(u_u32, seq, sizeof(seq)); + assert(length_r >= 2); + chars.c0 = seq[seq_ngram_offset]; //-V519 + chars.c1 = seq[seq_ngram_offset + 1]; //-V519 + putNGramBase(n, offset); + } + } + } + else + { + /// ngram is on the boundary of two sequences + /// first sequence may start before u_pos if it is not ASCII + auto first_seq_pos = pos; + UTF8::syncBackward(first_seq_pos, begin); + /// where is the given ngram in respect to the start of first UTF-8 sequence? + size_t seq_ngram_offset = pos - first_seq_pos; - /// both symbols are case-independent - if (first_l_u32 == first_u_u32 && second_l_u32 == second_u_u32) - { - putNGramBase(n, offset); - } - else if (first_l_u32 == first_u_u32) - { - /// first symbol is case-independent - Seq seq; + auto first_u32 = UTF8::convertUTF8ToCodePoint(first_seq_pos, end - first_seq_pos); + int first_l_u32 = 0; + int first_u_u32 = 0; - /// put ngram for lowercase - UTF8::convert(second_l_u32, seq, sizeof(seq)); - chars.c1 = seq[0]; - putNGramBase(n, offset); + if (first_u32) + { + first_l_u32 = Poco::Unicode::toLower(*first_u32); + first_u_u32 = Poco::Unicode::toUpper(*first_u32); + } - /// put ngram from uppercase, if it is different - UTF8::convert(second_u_u32, seq, sizeof(seq)); - if (chars.c1 != seq[0]) - { - chars.c1 = seq[0]; - putNGramBase(n, offset); - } - } - else if (second_l_u32 == second_u_u32) - { - /// second symbol is case-independent - Seq seq; + /// second sequence always start immediately after u_pos + auto second_seq_pos = pos + 1; - /// put ngram for lowercase - UTF8::convert(first_l_u32, seq, sizeof(seq)); - chars.c0 = seq[seq_ngram_offset]; - putNGramBase(n, offset); + auto second_u32 = UTF8::convertUTF8ToCodePoint(second_seq_pos, end - second_seq_pos); + int second_l_u32 = 0; + int second_u_u32 = 0; - /// put ngram for uppercase, if it is different - UTF8::convert(first_u_u32, seq, sizeof(seq)); - if (chars.c0 != seq[seq_ngram_offset]) - { - chars.c0 = seq[seq_ngram_offset]; - putNGramBase(n, offset); - } - } - else - { - Seq first_l_seq; - Seq first_u_seq; - Seq second_l_seq; - Seq second_u_seq; + if (second_u32) + { + second_l_u32 = Poco::Unicode::toLower(*second_u32); + second_u_u32 = Poco::Unicode::toUpper(*second_u32); + } - UTF8::convert(first_l_u32, first_l_seq, sizeof(first_l_seq)); - UTF8::convert(first_u_u32, first_u_seq, sizeof(first_u_seq)); - UTF8::convert(second_l_u32, second_l_seq, sizeof(second_l_seq)); - UTF8::convert(second_u_u32, second_u_seq, sizeof(second_u_seq)); + /// both symbols are case-independent + if (first_l_u32 == first_u_u32 && second_l_u32 == second_u_u32) + { + putNGramBase(n, offset); + } + else if (first_l_u32 == first_u_u32) + { + /// first symbol is case-independent + Seq seq; - auto c0l = first_l_seq[seq_ngram_offset]; - auto c0u = first_u_seq[seq_ngram_offset]; - auto c1l = second_l_seq[0]; - auto c1u = second_u_seq[0]; + /// put ngram for lowercase + size_t size_l [[maybe_unused]] = UTF8::convertCodePointToUTF8(second_l_u32, seq, sizeof(seq)); + assert(size_l >= 1); + chars.c1 = seq[0]; + putNGramBase(n, offset); - /// ngram for ll - chars.c0 = c0l; - chars.c1 = c1l; - putNGramBase(n, offset); + /// put ngram from uppercase, if it is different + size_t size_u [[maybe_unused]] = UTF8::convertCodePointToUTF8(second_u_u32, seq, sizeof(seq)); + assert(size_u >= 1); + if (chars.c1 != seq[0]) + { + chars.c1 = seq[0]; + putNGramBase(n, offset); + } + } + else if (second_l_u32 == second_u_u32) + { + /// second symbol is case-independent + Seq seq; - if (c0l != c0u) - { - /// ngram for Ul - chars.c0 = c0u; - chars.c1 = c1l; - putNGramBase(n, offset); - } + /// put ngram for lowercase + size_t size_l [[maybe_unused]] = UTF8::convertCodePointToUTF8(first_l_u32, seq, sizeof(seq)); + assert(size_l > seq_ngram_offset); + chars.c0 = seq[seq_ngram_offset]; + putNGramBase(n, offset); - if (c1l != c1u) - { - /// ngram for lU - chars.c0 = c0l; - chars.c1 = c1u; - putNGramBase(n, offset); - } + /// put ngram for uppercase, if it is different + size_t size_u [[maybe_unused]] = UTF8::convertCodePointToUTF8(first_u_u32, seq, sizeof(seq)); + assert(size_u > seq_ngram_offset); + if (chars.c0 != seq[seq_ngram_offset]) + { + chars.c0 = seq[seq_ngram_offset]; + putNGramBase(n, offset); + } + } + else + { + Seq first_l_seq; + Seq first_u_seq; + Seq second_l_seq; + Seq second_u_seq; - if (c0l != c0u && c1l != c1u) - { - /// ngram for UU - chars.c0 = c0u; - chars.c1 = c1u; - putNGramBase(n, offset); - } - } + size_t size_first_l [[maybe_unused]] = UTF8::convertCodePointToUTF8(first_l_u32, first_l_seq, sizeof(first_l_seq)); + size_t size_first_u [[maybe_unused]] = UTF8::convertCodePointToUTF8(first_u_u32, first_u_seq, sizeof(first_u_seq)); + size_t size_second_l [[maybe_unused]] = UTF8::convertCodePointToUTF8(second_l_u32, second_l_seq, sizeof(second_l_seq)); + size_t size_second_u [[maybe_unused]] = UTF8::convertCodePointToUTF8(second_u_u32, second_u_seq, sizeof(second_u_seq)); + + assert(size_first_l > seq_ngram_offset); + assert(size_first_u > seq_ngram_offset); + assert(size_second_l > 0); + assert(size_second_u > 0); + + auto c0l = first_l_seq[seq_ngram_offset]; + auto c0u = first_u_seq[seq_ngram_offset]; + auto c1l = second_l_seq[0]; + auto c1u = second_u_seq[0]; + + /// ngram for ll + chars.c0 = c0l; + chars.c1 = c1l; + putNGramBase(n, offset); + + if (c0l != c0u) + { + /// ngram for Ul + chars.c0 = c0u; + chars.c1 = c1l; + putNGramBase(n, offset); + } + + if (c1l != c1u) + { + /// ngram for lU + chars.c0 = c0l; + chars.c1 = c1u; + putNGramBase(n, offset); + } + + if (c0l != c0u && c1l != c1u) + { + /// ngram for UU + chars.c0 = c0u; + chars.c1 = c1u; + putNGramBase(n, offset); } } } } } + + template + static inline void putNGram(const UInt8 * pos, int offset, [[maybe_unused]] const UInt8 * begin, size_t size, Callback && putNGramBase) + { + if constexpr (CaseSensitive) + putNGramBase(toNGram(pos), offset); + else if constexpr (ASCII) + putNGramASCIICaseInsensitive(pos, offset, std::forward(putNGramBase)); + else + putNGramUTF8CaseInsensitive(pos, offset, begin, size, std::forward(putNGramBase)); + } } @@ -310,17 +345,17 @@ template class VolnitskyBase { protected: - const UInt8 * const needle; - const size_t needle_size; - const UInt8 * const needle_end = needle + needle_size; + const UInt8 * needle; + size_t needle_size; + const UInt8 * needle_end = needle + needle_size; /// For how long we move, if the n-gram from haystack is not found in the hash table. - const size_t step = needle_size - sizeof(VolnitskyTraits::Ngram) + 1; + size_t step = needle_size - sizeof(VolnitskyTraits::Ngram) + 1; /** max needle length is 255, max distinct ngrams for case-sensitive is (255 - 1), case-insensitive is 4 * (255 - 1) * storage of 64K ngrams (n = 2, 128 KB) should be large enough for both cases */ std::unique_ptr hash; /// Hash table. - const bool fallback; /// Do we need to use the fallback algorithm. + bool fallback; /// Do we need to use the fallback algorithm. FallbackSearcher fallback_searcher; @@ -346,7 +381,7 @@ public: /// ssize_t is used here because unsigned can't be used with condition like `i >= 0`, unsigned always >= 0 /// And also adding from the end guarantees that we will find first occurrence because we will lookup bigger offsets first. for (auto i = static_cast(needle_size - sizeof(VolnitskyTraits::Ngram)); i >= 0; --i) - VolnitskyTraits::putNGram(this->needle + i, i + 1, this->needle, callback); + VolnitskyTraits::putNGram(needle + i, i + 1, needle, needle_size, callback); } @@ -493,6 +528,7 @@ public: reinterpret_cast(cur_needle_data) + i, i + 1, reinterpret_cast(cur_needle_data), + cur_needle_size, callback); } } diff --git a/src/Common/memcmpSmall.h b/src/Common/memcmpSmall.h index bafc08a9cbe..db8641cb44d 100644 --- a/src/Common/memcmpSmall.h +++ b/src/Common/memcmpSmall.h @@ -120,9 +120,10 @@ inline int memcmpSmallLikeZeroPaddedAllowOverflow15(const Char * a, size_t a_siz for (size_t offset = min_size; offset < max_size; offset += 16) { - uint16_t mask = _mm_movemask_epi8(_mm_cmpgt_epi8( + uint16_t mask = _mm_movemask_epi8(_mm_cmpeq_epi8( _mm_loadu_si128(reinterpret_cast(longest + offset)), zero16)); + mask = ~mask; if (mask) { diff --git a/src/Core/Defines.h b/src/Core/Defines.h index fdf250a6dd1..ff033aa6183 100644 --- a/src/Core/Defines.h +++ b/src/Core/Defines.h @@ -36,6 +36,7 @@ #define DEFAULT_MERGE_BLOCK_SIZE 8192 #define DEFAULT_TEMPORARY_LIVE_VIEW_TIMEOUT_SEC 5 +#define DEFAULT_PERIODIC_LIVE_VIEW_REFRESH_SEC 60 #define SHOW_CHARS_ON_SYNTAX_ERROR ptrdiff_t(160) #define DEFAULT_LIVE_VIEW_HEARTBEAT_INTERVAL_SEC 15 #define DBMS_DEFAULT_DISTRIBUTED_CONNECTIONS_POOL_SIZE 1024 diff --git a/src/Core/Settings.h b/src/Core/Settings.h index e3783746eec..c4cf3803913 100644 --- a/src/Core/Settings.h +++ b/src/Core/Settings.h @@ -391,6 +391,7 @@ class IColumn; M(Bool, validate_polygons, true, "Throw exception if polygon is invalid in function pointInPolygon (e.g. self-tangent, self-intersecting). If the setting is false, the function will accept invalid polygons but may silently return wrong result.", 0) \ M(UInt64, max_parser_depth, DBMS_DEFAULT_MAX_PARSER_DEPTH, "Maximum parser depth (recursion depth of recursive descend parser).", 0) \ M(Seconds, temporary_live_view_timeout, DEFAULT_TEMPORARY_LIVE_VIEW_TIMEOUT_SEC, "Timeout after which temporary live view is deleted.", 0) \ + M(Seconds, periodic_live_view_refresh, DEFAULT_PERIODIC_LIVE_VIEW_REFRESH_SEC, "Interval after which periodically refreshed live view is forced to refresh.", 0) \ M(Bool, transform_null_in, false, "If enabled, NULL values will be matched with 'IN' operator as if they are considered equal.", 0) \ M(Bool, allow_nondeterministic_mutations, false, "Allow non-deterministic functions in ALTER UPDATE/ALTER DELETE statements", 0) \ M(Seconds, lock_acquire_timeout, DBMS_DEFAULT_LOCK_ACQUIRE_TIMEOUT_SEC, "How long locking request should wait before failing", 0) \ diff --git a/src/Functions/FunctionBinaryArithmetic.h b/src/Functions/FunctionBinaryArithmetic.h index c10ff581b0e..f61c9c91d00 100644 --- a/src/Functions/FunctionBinaryArithmetic.h +++ b/src/Functions/FunctionBinaryArithmetic.h @@ -504,7 +504,7 @@ private: using namespace traits_; using namespace impl_; -template