mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 15:12:02 +00:00
Merge branch 'master' into change-accurate-comparison
This commit is contained in:
commit
e50d82ab45
@ -464,6 +464,7 @@ find_contrib_lib(double-conversion) # Must be before parquet
|
||||
include (cmake/find/ssl.cmake)
|
||||
include (cmake/find/ldap.cmake) # after ssl
|
||||
include (cmake/find/icu.cmake)
|
||||
include (cmake/find/xz.cmake)
|
||||
include (cmake/find/zlib.cmake)
|
||||
include (cmake/find/zstd.cmake)
|
||||
include (cmake/find/ltdl.cmake) # for odbc
|
||||
|
27
cmake/find/xz.cmake
Normal file
27
cmake/find/xz.cmake
Normal file
@ -0,0 +1,27 @@
|
||||
option (USE_INTERNAL_XZ_LIBRARY "Set to OFF to use system xz (lzma) library instead of bundled" ${NOT_UNBUNDLED})
|
||||
|
||||
if(NOT EXISTS "${ClickHouse_SOURCE_DIR}/contrib/xz/src/liblzma/api/lzma.h")
|
||||
if(USE_INTERNAL_XZ_LIBRARY)
|
||||
message(WARNING "submodule contrib/xz is missing. to fix try run: \n git submodule update --init --recursive")
|
||||
message (${RECONFIGURE_MESSAGE_LEVEL} "Can't find internal xz (lzma) library")
|
||||
set(USE_INTERNAL_XZ_LIBRARY 0)
|
||||
endif()
|
||||
set(MISSING_INTERNAL_XZ_LIBRARY 1)
|
||||
endif()
|
||||
|
||||
if (NOT USE_INTERNAL_XZ_LIBRARY)
|
||||
find_library (XZ_LIBRARY lzma)
|
||||
find_path (XZ_INCLUDE_DIR NAMES lzma.h PATHS ${XZ_INCLUDE_PATHS})
|
||||
if (NOT XZ_LIBRARY OR NOT XZ_INCLUDE_DIR)
|
||||
message (${RECONFIGURE_MESSAGE_LEVEL} "Can't find system xz (lzma) library")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (XZ_LIBRARY AND XZ_INCLUDE_DIR)
|
||||
elseif (NOT MISSING_INTERNAL_XZ_LIBRARY)
|
||||
set (USE_INTERNAL_XZ_LIBRARY 1)
|
||||
set (XZ_LIBRARY liblzma)
|
||||
set (XZ_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/xz/src/liblzma/api)
|
||||
endif ()
|
||||
|
||||
message (STATUS "Using xz (lzma): ${XZ_INCLUDE_DIR} : ${XZ_LIBRARY}")
|
2
contrib/NuRaft
vendored
2
contrib/NuRaft
vendored
@ -1 +1 @@
|
||||
Subproject commit 70468326ad5d72e9497944838484c591dae054ea
|
||||
Subproject commit 241fd3754a1eb4d82ab68a9a875dc99391ec9f02
|
@ -138,7 +138,8 @@
|
||||
"docker/test/stateless_unbundled",
|
||||
"docker/test/stateless_pytest",
|
||||
"docker/test/integration/base",
|
||||
"docker/test/fuzzer"
|
||||
"docker/test/fuzzer",
|
||||
"docker/test/keeper-jepsen"
|
||||
]
|
||||
},
|
||||
"docker/packager/unbundled": {
|
||||
@ -159,5 +160,9 @@
|
||||
"docker/test/sqlancer": {
|
||||
"name": "yandex/clickhouse-sqlancer-test",
|
||||
"dependent": []
|
||||
},
|
||||
"docker/test/keeper-jepsen": {
|
||||
"name": "yandex/clickhouse-keeper-jepsen-test",
|
||||
"dependent": []
|
||||
}
|
||||
}
|
||||
|
39
docker/test/keeper-jepsen/Dockerfile
Normal file
39
docker/test/keeper-jepsen/Dockerfile
Normal file
@ -0,0 +1,39 @@
|
||||
# docker build -t yandex/clickhouse-keeper-jepsen-test .
|
||||
FROM yandex/clickhouse-test-base
|
||||
|
||||
ENV DEBIAN_FRONTEND=noninteractive
|
||||
ENV CLOJURE_VERSION=1.10.3.814
|
||||
|
||||
# arguments
|
||||
ENV PR_TO_TEST=""
|
||||
ENV SHA_TO_TEST=""
|
||||
|
||||
ENV NODES_USERNAME="root"
|
||||
ENV NODES_PASSWORD=""
|
||||
ENV TESTS_TO_RUN="30"
|
||||
ENV TIME_LIMIT="30"
|
||||
|
||||
|
||||
# volumes
|
||||
ENV NODES_FILE_PATH="/nodes.txt"
|
||||
ENV TEST_OUTPUT="/test_output"
|
||||
|
||||
RUN mkdir "/root/.ssh"
|
||||
RUN touch "/root/.ssh/known_hosts"
|
||||
|
||||
# install java
|
||||
RUN apt-get update && apt-get install default-jre default-jdk libjna-java libjna-jni ssh gnuplot graphviz --yes --no-install-recommends
|
||||
|
||||
# install clojure
|
||||
RUN curl -O "https://download.clojure.org/install/linux-install-${CLOJURE_VERSION}.sh" && \
|
||||
chmod +x "linux-install-${CLOJURE_VERSION}.sh" && \
|
||||
bash "./linux-install-${CLOJURE_VERSION}.sh"
|
||||
|
||||
# install leiningen
|
||||
RUN curl -O "https://raw.githubusercontent.com/technomancy/leiningen/stable/bin/lein" && \
|
||||
chmod +x ./lein && \
|
||||
mv ./lein /usr/bin
|
||||
|
||||
COPY run.sh /
|
||||
|
||||
CMD ["/bin/bash", "/run.sh"]
|
22
docker/test/keeper-jepsen/run.sh
Normal file
22
docker/test/keeper-jepsen/run.sh
Normal file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
|
||||
CLICKHOUSE_PACKAGE=${CLICKHOUSE_PACKAGE:="https://clickhouse-builds.s3.yandex.net/$PR_TO_TEST/$SHA_TO_TEST/clickhouse_build_check/clang-11_relwithdebuginfo_none_bundled_unsplitted_disable_False_binary/clickhouse"}
|
||||
CLICKHOUSE_REPO_PATH=${CLICKHOUSE_REPO_PATH:=""}
|
||||
|
||||
|
||||
if [ -z "$CLICKHOUSE_REPO_PATH" ]; then
|
||||
CLICKHOUSE_REPO_PATH=ch
|
||||
rm -rf ch ||:
|
||||
mkdir ch ||:
|
||||
wget -nv -nd -c "https://clickhouse-test-reports.s3.yandex.net/$PR_TO_TEST/$SHA_TO_TEST/repo/clickhouse_no_subs.tar.gz"
|
||||
tar -C ch --strip-components=1 -xf clickhouse_no_subs.tar.gz
|
||||
ls -lath ||:
|
||||
fi
|
||||
|
||||
cd "$CLICKHOUSE_REPO_PATH/tests/jepsen.clickhouse-keeper"
|
||||
|
||||
(lein run test-all --nodes-file "$NODES_FILE_PATH" --username "$NODES_USERNAME" --logging-json --password "$NODES_PASSWORD" --time-limit "$TIME_LIMIT" --concurrency 50 -r 50 --snapshot-distance 100 --stale-log-gap 100 --reserved-log-items 10 --lightweight-run --clickhouse-source "$CLICKHOUSE_PACKAGE" -q --test-count "$TESTS_TO_RUN" || true) | tee "$TEST_OUTPUT/jepsen_run_all_tests.log"
|
||||
|
||||
mv store "$TEST_OUTPUT/"
|
@ -31,9 +31,10 @@ toc_title: Cloud
|
||||
|
||||
## Alibaba Cloud {#alibaba-cloud}
|
||||
|
||||
Alibaba Cloud Managed Service for ClickHouse [China Site](https://www.aliyun.com/product/clickhouse) (Will be available at international site at May, 2021) provides the following key features:
|
||||
- Highly reliable cloud disk storage engine based on Alibaba Cloud Apsara distributed system
|
||||
- Expand capacity on demand without manual data migration
|
||||
Alibaba Cloud Managed Service for ClickHouse. [China Site](https://www.aliyun.com/product/clickhouse) (will be available at the international site in May 2021). Provides the following key features:
|
||||
|
||||
- Highly reliable cloud disk storage engine based on [Alibaba Cloud Apsara](https://www.alibabacloud.com/product/apsara-stack) distributed system
|
||||
- Expand capacity on-demand without manual data migration
|
||||
- Support single-node, single-replica, multi-node, and multi-replica architectures, and support hot and cold data tiering
|
||||
- Support access allow-list, one-key recovery, multi-layer network security protection, cloud disk encryption
|
||||
- Seamless integration with cloud log systems, databases, and data application tools
|
||||
|
@ -3,15 +3,52 @@ toc_priority: 32
|
||||
toc_title: Atomic
|
||||
---
|
||||
|
||||
|
||||
# Atomic {#atomic}
|
||||
|
||||
It supports non-blocking `DROP` and `RENAME TABLE` queries and atomic `EXCHANGE TABLES t1 AND t2` queries. `Atomic` database engine is used by default.
|
||||
It supports non-blocking [DROP TABLE](#drop-detach-table) and [RENAME TABLE](#rename-table) queries and atomic [EXCHANGE TABLES t1 AND t2](#exchange-tables) queries. `Atomic` database engine is used by default.
|
||||
|
||||
## Creating a Database {#creating-a-database}
|
||||
|
||||
```sql
|
||||
CREATE DATABASE test ENGINE = Atomic;
|
||||
``` sql
|
||||
CREATE DATABASE test[ ENGINE = Atomic];
|
||||
```
|
||||
|
||||
[Original article](https://clickhouse.tech/docs/en/engines/database-engines/atomic/) <!--hide-->
|
||||
## Specifics and recommendations {#specifics-and-recommendations}
|
||||
|
||||
### Table UUID {#table-uuid}
|
||||
|
||||
All tables in database `Atomic` have persistent [UUID](../../sql-reference/data-types/uuid.md) and store data in directory `/clickhouse_path/store/xxx/xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/`, where `xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy` is UUID of the table.
|
||||
Usually, the UUID is generated automatically, but the user can also explicitly specify the UUID in the same way when creating the table (this is not recommended). To display the `SHOW CREATE` query with the UUID you can use setting [show_table_uuid_in_table_create_query_if_not_nil](../../operations/settings/settings.md#show_table_uuid_in_table_create_query_if_not_nil). For example:
|
||||
|
||||
```sql
|
||||
CREATE TABLE name UUID '28f1c61c-2970-457a-bffe-454156ddcfef' (n UInt64) ENGINE = ...;
|
||||
```
|
||||
### RENAME TABLE {#rename-table}
|
||||
|
||||
`RENAME` queries are performed without changing UUID and moving table data. These queries do not wait for the completion of queries using the table and will be executed instantly.
|
||||
|
||||
### DROP/DETACH TABLE {#drop-detach-table}
|
||||
|
||||
On `DROP TABLE` no data is removed, database `Atomic` just marks table as dropped by moving metadata to `/clickhouse_path/metadata_dropped/` and notifies background thread. Delay before final table data deletion is specify by [database_atomic_delay_before_drop_table_sec](../../operations/server-configuration-parameters/settings.md#database_atomic_delay_before_drop_table_sec) setting.
|
||||
You can specify synchronous mode using `SYNC` modifier. Use the [database_atomic_wait_for_drop_and_detach_synchronously](../../operations/settings/settings.md#database_atomic_wait_for_drop_and_detach_synchronously) setting to do this. In this case `DROP` waits for running `SELECT`, `INSERT` and other queries which are using the table to finish. Table will be actually removed when it's not in use.
|
||||
|
||||
### EXCHANGE TABLES {#exchange-tables}
|
||||
|
||||
`EXCHANGE` query swaps tables atomically. So instead of this non-atomic operation:
|
||||
|
||||
```sql
|
||||
RENAME TABLE new_table TO tmp, old_table TO new_table, tmp TO old_table;
|
||||
```
|
||||
you can use one atomic query:
|
||||
|
||||
``` sql
|
||||
EXCHANGE TABLES new_table AND old_table;
|
||||
```
|
||||
|
||||
### ReplicatedMergeTree in Atomic Database {#replicatedmergetree-in-atomic-database}
|
||||
|
||||
For [ReplicatedMergeTree](../table-engines/mergetree-family/replication.md#table_engines-replication) tables is recomended do not specify parameters of engine - path in ZooKeeper and replica name. In this case will be used parameters of the configuration [default_replica_path](../../operations/server-configuration-parameters/settings.md#default_replica_path) and [default_replica_name](../../operations/server-configuration-parameters/settings.md#default_replica_name). If you want specify parameters of engine explicitly than recomended to use {uuid} macros. This is useful so that unique paths are automatically generated for each table in the ZooKeeper.
|
||||
|
||||
## See Also
|
||||
|
||||
- [system.databases](../../operations/system-tables/databases.md) system table
|
||||
|
@ -23,6 +23,7 @@ toc_title: Client Libraries
|
||||
- [SeasClick C++ client](https://github.com/SeasX/SeasClick)
|
||||
- [one-ck](https://github.com/lizhichao/one-ck)
|
||||
- [glushkovds/phpclickhouse-laravel](https://packagist.org/packages/glushkovds/phpclickhouse-laravel)
|
||||
- [kolya7k ClickHouse PHP extension](https://github.com//kolya7k/clickhouse-php)
|
||||
- Go
|
||||
- [clickhouse](https://github.com/kshvakov/clickhouse/)
|
||||
- [go-clickhouse](https://github.com/roistat/go-clickhouse)
|
||||
|
@ -100,6 +100,11 @@ Default value: `1073741824` (1 GB).
|
||||
<size_limit>1073741824</size_limit>
|
||||
</core_dump>
|
||||
```
|
||||
## database_atomic_delay_before_drop_table_sec {#database_atomic_delay_before_drop_table_sec}
|
||||
|
||||
Sets the delay before remove table data in seconds. If the query has `SYNC` modifier, this setting is ignored.
|
||||
|
||||
Default value: `480` (8 minute).
|
||||
|
||||
## default_database {#default-database}
|
||||
|
||||
@ -125,6 +130,25 @@ Settings profiles are located in the file specified in the parameter `user_confi
|
||||
<default_profile>default</default_profile>
|
||||
```
|
||||
|
||||
## default_replica_path {#default_replica_path}
|
||||
|
||||
The path to the table in ZooKeeper.
|
||||
|
||||
**Example**
|
||||
|
||||
``` xml
|
||||
<default_replica_path>/clickhouse/tables/{uuid}/{shard}</default_replica_path>
|
||||
```
|
||||
## default_replica_name {#default_replica_name}
|
||||
|
||||
The replica name in ZooKeeper.
|
||||
|
||||
**Example**
|
||||
|
||||
``` xml
|
||||
<default_replica_name>{replica}</default_replica_name>
|
||||
```
|
||||
|
||||
## dictionaries_config {#server_configuration_parameters-dictionaries_config}
|
||||
|
||||
The path to the config file for external dictionaries.
|
||||
|
@ -2787,6 +2787,28 @@ Possible values:
|
||||
|
||||
Default value: `0`.
|
||||
|
||||
## database_atomic_wait_for_drop_and_detach_synchronously {#database_atomic_wait_for_drop_and_detach_synchronously}
|
||||
|
||||
Adds a modifier `SYNC` to all `DROP` and `DETACH` queries.
|
||||
|
||||
Possible values:
|
||||
|
||||
- 0 — Queries will be executed with delay.
|
||||
- 1 — Queries will be executed without delay.
|
||||
|
||||
Default value: `0`.
|
||||
|
||||
## show_table_uuid_in_table_create_query_if_not_nil {#show_table_uuid_in_table_create_query_if_not_nil}
|
||||
|
||||
Sets the `SHOW TABLE` query display.
|
||||
|
||||
Possible values:
|
||||
|
||||
- 0 — The query will be displayed without table UUID.
|
||||
- 1 — The query will be displayed with table UUID.
|
||||
|
||||
Default value: `0`.
|
||||
|
||||
## allow_experimental_live_view {#allow-experimental-live-view}
|
||||
|
||||
Allows creation of experimental [live views](../../sql-reference/statements/create/view.md#live-view).
|
||||
|
@ -20,10 +20,12 @@ Columns:
|
||||
|
||||
When connecting to the server by `clickhouse-client`, you see the string similar to `Connected to ClickHouse server version 19.18.1 revision 54429.`. This field contains the `revision`, but not the `version` of a server.
|
||||
|
||||
- `timer_type` ([Enum8](../../sql-reference/data-types/enum.md)) — Timer type:
|
||||
- `trace_type` ([Enum8](../../sql-reference/data-types/enum.md)) — Trace type:
|
||||
|
||||
- `Real` represents wall-clock time.
|
||||
- `CPU` represents CPU time.
|
||||
- `Real` represents collecting stack traces by wall-clock time.
|
||||
- `CPU` represents collecting stack traces by CPU time.
|
||||
- `Memory` represents collecting allocations and deallocations when memory allocation exceeds the subsequent watermark.
|
||||
- `MemorySample` represents collecting random allocations and deallocations.
|
||||
|
||||
- `thread_number` ([UInt32](../../sql-reference/data-types/int-uint.md)) — Thread identifier.
|
||||
|
||||
|
@ -5,13 +5,14 @@ toc_title: ATTACH
|
||||
|
||||
# ATTACH Statement {#attach}
|
||||
|
||||
This query is exactly the same as [CREATE](../../sql-reference/statements/create/table.md), but
|
||||
Attaches the table, for example, when moving a database to another server.
|
||||
|
||||
- Instead of the word `CREATE` it uses the word `ATTACH`.
|
||||
- The query does not create data on the disk, but assumes that data is already in the appropriate places, and just adds information about the table to the server.
|
||||
After executing an ATTACH query, the server will know about the existence of the table.
|
||||
The query does not create data on the disk, but assumes that data is already in the appropriate places, and just adds information about the table to the server. After executing an `ATTACH` query, the server will know about the existence of the table.
|
||||
|
||||
If the table was previously detached ([DETACH](../../sql-reference/statements/detach.md)), meaning that its structure is known, you can use shorthand without defining the structure.
|
||||
If the table was previously detached ([DETACH](../../sql-reference/statements/detach.md)) query, meaning that its structure is known, you can use shorthand without defining the structure.
|
||||
|
||||
## Syntax Forms {#syntax-forms}
|
||||
### Attach Existing Table {#attach-existing-table}
|
||||
|
||||
``` sql
|
||||
ATTACH TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster]
|
||||
@ -21,4 +22,38 @@ This query is used when starting the server. The server stores table metadata as
|
||||
|
||||
If the table was detached permanently, it won't be reattached at the server start, so you need to use `ATTACH` query explicitly.
|
||||
|
||||
[Original article](https://clickhouse.tech/docs/en/sql-reference/statements/attach/) <!--hide-->
|
||||
### Сreate New Table And Attach Data {#create-new-table-and-attach-data}
|
||||
|
||||
**With specify path to table data**
|
||||
|
||||
```sql
|
||||
ATTACH TABLE name FROM 'path/to/data/' (col1 Type1, ...)
|
||||
```
|
||||
|
||||
It creates new table with provided structure and attaches table data from provided directory in `user_files`.
|
||||
|
||||
**Example**
|
||||
|
||||
Query:
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS test;
|
||||
INSERT INTO TABLE FUNCTION file('01188_attach/test/data.TSV', 'TSV', 's String, n UInt8') VALUES ('test', 42);
|
||||
ATTACH TABLE test FROM '01188_attach/test' (s String, n UInt8) ENGINE = File(TSV);
|
||||
SELECT * FROM test;
|
||||
```
|
||||
Result:
|
||||
|
||||
```sql
|
||||
┌─s────┬──n─┐
|
||||
│ test │ 42 │
|
||||
└──────┴────┘
|
||||
```
|
||||
|
||||
**With specify table UUID** (Only for `Atomic` database)
|
||||
|
||||
```sql
|
||||
ATTACH TABLE name UUID '<uuid>' (col1 Type1, ...)
|
||||
```
|
||||
|
||||
It creates new table with provided structure and attaches data from table with the specified UUID.
|
@ -287,7 +287,9 @@ REPLACE TABLE myOldTable SELECT * FROM myOldTable WHERE CounterID <12345;
|
||||
|
||||
### Syntax
|
||||
|
||||
{CREATE [OR REPLACE]|REPLACE} TABLE [db.]table_name
|
||||
``` sql
|
||||
{CREATE [OR REPLACE] | REPLACE} TABLE [db.]table_name
|
||||
```
|
||||
|
||||
All syntax forms for `CREATE` query also work for this query. `REPLACE` for a non-existent table will cause an error.
|
||||
|
||||
|
@ -29,3 +29,14 @@ toc_title: "Поставщики облачных услуг ClickHouse"
|
||||
- cross-az масштабирование для повышения производительности и обеспечения высокой доступности
|
||||
- встроенный мониторинг и редактор SQL-запросов
|
||||
|
||||
## Alibaba Cloud {#alibaba-cloud}
|
||||
|
||||
Управляемый облачный сервис Alibaba для ClickHouse: [китайская площадка](https://www.aliyun.com/product/clickhouse), будет доступен на международной площадке в мае 2021 года. Сервис предоставляет следующие возможности:
|
||||
|
||||
- надежный сервер для облачного хранилища на основе распределенной системы [Alibaba Cloud Apsara](https://www.alibabacloud.com/product/apsara-stack);
|
||||
- расширяемая по запросу емкость, без переноса данных вручную;
|
||||
- поддержка одноузловой и многоузловой архитектуры, архитектуры с одной или несколькими репликами, а также многоуровневого хранения cold и hot data;
|
||||
- поддержка прав доступа, one-key восстановления, многоуровневая защита сети, шифрование облачного диска;
|
||||
- полная интеграция с облачными системами логирования, базами данных и инструментами обработки данных;
|
||||
- встроенная платформа для мониторинга и управления базами данных;
|
||||
- техническая поддержка от экспертов по работе с базами данных.
|
@ -27,7 +27,7 @@ ClickHouse - полноценная колоночная СУБД. Данные
|
||||
|
||||
`IColumn` предоставляет методы для общих реляционных преобразований данных, но они не отвечают всем потребностям. Например, `ColumnUInt64` не имеет метода для вычисления суммы двух столбцов, а `ColumnString` не имеет метода для запуска поиска по подстроке. Эти бесчисленные процедуры реализованы вне `IColumn`.
|
||||
|
||||
Различные функции на колонках могут быть реализованы обобщенным, неэффективным путем, используя `IColumn` методы для извлечения значений `Field`, или специальным путем, используя знания о внутреннем распределение данных в памяти в конкретной реализации `IColumn`. Для этого функции приводятся к конкретному типу `IColumn` и работают напрямую с его внутренним представлением. Например, в `ColumnUInt64` есть метод getData, который возвращает ссылку на внутренний массив, чтение и заполнение которого, выполняется отдельной процедурой напрямую. Фактически, мы имеем "дырявую абстракции", обеспечивающие эффективные специализации различных процедур.
|
||||
Различные функции на колонках могут быть реализованы обобщенным, неэффективным путем, используя `IColumn` методы для извлечения значений `Field`, или специальным путем, используя знания о внутреннем распределение данных в памяти в конкретной реализации `IColumn`. Для этого функции приводятся к конкретному типу `IColumn` и работают напрямую с его внутренним представлением. Например, в `ColumnUInt64` есть метод `getData`, который возвращает ссылку на внутренний массив, чтение и заполнение которого, выполняется отдельной процедурой напрямую. Фактически, мы имеем "дырявые абстракции", обеспечивающие эффективные специализации различных процедур.
|
||||
|
||||
## Типы данных (Data Types) {#data_types}
|
||||
|
||||
@ -42,7 +42,7 @@ ClickHouse - полноценная колоночная СУБД. Данные
|
||||
|
||||
## Блоки (Block) {#block}
|
||||
|
||||
`Block` это контейнер, который представляет фрагмент (chunk) таблицы в памяти. Это набор троек - `(IColumn, IDataType, имя колонки)`. В процессе выполнения запроса, данные обрабатываются `Block`ами. Если у нас есть `Block`, значит у нас есть данные (в объекте `IColumn`), информация о типе (в `IDataType`), которая говорит нам, как работать с колонкой, и имя колонки (оригинальное имя колонки таблицы или служебное имя, присвоенное для получения промежуточных результатов вычислений).
|
||||
`Block` это контейнер, который представляет фрагмент (chunk) таблицы в памяти. Это набор троек - `(IColumn, IDataType, имя колонки)`. В процессе выполнения запроса, данные обрабатываются `Block`-ами. Если у нас есть `Block`, значит у нас есть данные (в объекте `IColumn`), информация о типе (в `IDataType`), которая говорит нам, как работать с колонкой, и имя колонки (оригинальное имя колонки таблицы или служебное имя, присвоенное для получения промежуточных результатов вычислений).
|
||||
|
||||
При вычислении некоторой функции на колонках в блоке мы добавляем еще одну колонку с результатами в блок, не трогая колонки аргументов функции, потому что операции иммутабельные. Позже ненужные колонки могут быть удалены из блока, но не модифицированы. Это удобно для устранения общих подвыражений.
|
||||
|
||||
@ -58,7 +58,7 @@ ClickHouse - полноценная колоночная СУБД. Данные
|
||||
2. Реализацию форматов данных. Например, при выводе данных в терминал в формате `Pretty`, вы создаете выходной поток блоков, который форматирует поступающие в него блоки.
|
||||
3. Трансформацию данных. Допустим, у вас есть `IBlockInputStream` и вы хотите создать отфильтрованный поток. Вы создаете `FilterBlockInputStream` и инициализируете его вашим потоком. Затем вы тянете (pull) блоки из `FilterBlockInputStream`, а он тянет блоки исходного потока, фильтрует их и возвращает отфильтрованные блоки вам. Таким образом построены конвейеры выполнения запросов.
|
||||
|
||||
Имеются и более сложные трансформации. Например, когда вы тянете блоки из `AggregatingBlockInputStream`, он считывает все данные из своего источника, агрегирует их, и возвращает поток агрегированных данных вам. Другой пример: конструктор `UnionBlockInputStream` принимает множество источников входных данных и число потоков. Такой `Stream` работает в несколько потоков и читает данные источников параллельно.
|
||||
Имеются и более сложные трансформации. Например, когда вы тянете блоки из `AggregatingBlockInputStream`, он считывает все данные из своего источника, агрегирует их, и возвращает поток агрегированных данных вам. Другой пример: конструктор `UnionBlockInputStream` принимает множество источников входных данных и число потоков. Такой `Stream` работает в несколько потоков и читает данные источников параллельно.
|
||||
|
||||
> Потоки блоков используют «втягивающий» (pull) подход к управлению потоком выполнения: когда вы вытягиваете блок из первого потока, он, следовательно, вытягивает необходимые блоки из вложенных потоков, так и работает весь конвейер выполнения. Ни «pull» ни «push» не имеют явного преимущества, потому что поток управления неявный, и это ограничивает в реализации различных функций, таких как одновременное выполнение нескольких запросов (слияние нескольких конвейеров вместе). Это ограничение можно преодолеть с помощью сопрограмм (coroutines) или просто запуском дополнительных потоков, которые ждут друг друга. У нас может быть больше возможностей, если мы сделаем поток управления явным: если мы локализуем логику для передачи данных из одной расчетной единицы в другую вне этих расчетных единиц. Читайте эту [статью](http://journal.stuffwithstuff.com/2013/01/13/iteration-inside-and-out/) для углубленного изучения.
|
||||
|
||||
@ -110,9 +110,9 @@ ClickHouse - полноценная колоночная СУБД. Данные
|
||||
> Генераторы парсеров не используются по историческим причинам.
|
||||
|
||||
## Интерпретаторы {#interpreters}
|
||||
|
||||
|
||||
Интерпретаторы отвечают за создание конвейера выполнения запроса из `AST`. Есть простые интерпретаторы, такие как `InterpreterExistsQuery` и `InterpreterDropQuery` или более сложный `InterpreterSelectQuery`. Конвейер выполнения запроса представляет собой комбинацию входных и выходных потоков блоков. Например, результатом интерпретации `SELECT` запроса является `IBlockInputStream` для чтения результирующего набора данных; результат интерпретации `INSERT` запроса - это `IBlockOutputStream`, для записи данных, предназначенных для вставки; результат интерпретации `INSERT SELECT` запроса - это `IBlockInputStream`, который возвращает пустой результирующий набор при первом чтении, но копирует данные из `SELECT` к `INSERT`.
|
||||
|
||||
|
||||
`InterpreterSelectQuery` использует `ExpressionAnalyzer` и `ExpressionActions` механизмы для анализа запросов и преобразований. Именно здесь выполняется большинство оптимизаций запросов на основе правил. `ExpressionAnalyzer` написан довольно грязно и должен быть переписан: различные преобразования запросов и оптимизации должны быть извлечены в отдельные классы, чтобы позволить модульные преобразования или запросы.
|
||||
|
||||
## Функции {#functions}
|
||||
@ -162,9 +162,9 @@ ClickHouse имеет сильную типизацию, поэтому нет
|
||||
|
||||
Сервера в кластере в основном независимы. Вы можете создать `Распределенную` (`Distributed`) таблицу на одном или всех серверах в кластере. Такая таблица сама по себе не хранит данные - она только предоставляет возможность "просмотра" всех локальных таблиц на нескольких узлах кластера. При выполнении `SELECT` распределенная таблица переписывает запрос, выбирает удаленные узлы в соответствии с настройками балансировки нагрузки и отправляет им запрос. Распределенная таблица просит удаленные сервера обработать запрос до той стадии, когда промежуточные результаты с разных серверов могут быть объединены. Затем он получает промежуточные результаты и объединяет их. Распределенная таблица пытается возложить как можно больше работы на удаленные серверы и сократить объем промежуточных данных, передаваемых по сети.
|
||||
|
||||
Ситуация усложняется, при использовании подзапросы в случае IN или JOIN, когда каждый из них использует таблицу `Distributed`. Есть разные стратегии для выполнения таких запросов.
|
||||
Ситуация усложняется, при использовании подзапросов в случае `IN` или `JOIN`, когда каждый из них использует таблицу `Distributed`. Есть разные стратегии для выполнения таких запросов.
|
||||
|
||||
Глобального плана выполнения распределенных запросов не существует. Каждый узел имеет собственный локальный план для своей части работы. У нас есть простое однонаправленное выполнение распределенных запросов: мы отправляем запросы на удаленные узлы и затем объединяем результаты. Но это невозможно для сложных запросов GROUP BY высокой кардинальности или запросов с большим числом временных данных в JOIN: в таких случаях нам необходимо перераспределить («reshuffle») данные между серверами, что требует дополнительной координации. ClickHouse не поддерживает выполнение запросов такого рода, и нам нужно работать над этим.
|
||||
Глобального плана выполнения распределенных запросов не существует. Каждый узел имеет собственный локальный план для своей части работы. У нас есть простое однонаправленное выполнение распределенных запросов: мы отправляем запросы на удаленные узлы и затем объединяем результаты. Но это невозможно для сложных запросов `GROUP BY` высокой кардинальности или запросов с большим числом временных данных в `JOIN`: в таких случаях нам необходимо перераспределить («reshuffle») данные между серверами, что требует дополнительной координации. ClickHouse не поддерживает выполнение запросов такого рода, и нам нужно работать над этим.
|
||||
|
||||
## Merge Tree {#merge-tree}
|
||||
|
||||
@ -190,7 +190,7 @@ ClickHouse имеет сильную типизацию, поэтому нет
|
||||
|
||||
Репликация использует асинхронную multi-master схему. Вы можете вставить данные в любую реплику, которая имеет открытую сессию в `ZooKeeper`, и данные реплицируются на все другие реплики асинхронно. Поскольку ClickHouse не поддерживает UPDATE, репликация исключает конфликты (conflict-free replication). Поскольку подтверждение вставок кворумом не реализовано, только что вставленные данные могут быть потеряны в случае сбоя одного узла.
|
||||
|
||||
Метаданные для репликации хранятся в `ZooKeeper`. Существует журнал репликации, в котором перечислены действия, которые необходимо выполнить. Среди этих действий: получить часть (get the part); объединить части (merge parts); удалить партицию (drop a partition) и так далее. Каждая реплика копирует журнал репликации в свою очередь, а затем выполняет действия из очереди. Например, при вставке в журнале создается действие «получить часть» (get the part), и каждая реплика загружает эту часть. Слияния координируются между репликами, чтобы получить идентичные до байта результаты. Все части объединяются одинаково на всех репликах. Одна из реплик-лидеров инициирует новое слияние кусков первой и записывает действия «слияния частей» в журнал. Несколько реплик (или все) могут быть лидерами одновременно. Реплике можно запретить быть лидером с помощью `merge_tree` настройки `replicated_can_become_leader`.
|
||||
Метаданные для репликации хранятся в `ZooKeeper`. Существует журнал репликации, в котором перечислены действия, которые необходимо выполнить. Среди этих действий: получить часть (get the part); объединить части (merge parts); удалить партицию (drop a partition) и так далее. Каждая реплика копирует журнал репликации в свою очередь, а затем выполняет действия из очереди. Например, при вставке в журнале создается действие «получить часть» (get the part), и каждая реплика загружает эту часть. Слияния координируются между репликами, чтобы получить идентичные до байта результаты. Все части объединяются одинаково на всех репликах. Одна из реплик-лидеров инициирует новое слияние кусков первой и записывает действия «слияния частей» в журнал. Несколько реплик (или все) могут быть лидерами одновременно. Реплике можно запретить быть лидером с помощью `merge_tree` настройки `replicated_can_become_leader`.
|
||||
|
||||
Репликация является физической: между узлами передаются только сжатые части, а не запросы. Слияния обрабатываются на каждой реплике независимо, в большинстве случаев, чтобы снизить затраты на сеть, во избежание усиления роли сети. Крупные объединенные части отправляются по сети только в случае значительной задержки репликации.
|
||||
|
||||
|
@ -7,15 +7,15 @@ toc_title: "Инструкция для разработчиков"
|
||||
|
||||
Сборка ClickHouse поддерживается на Linux, FreeBSD, Mac OS X.
|
||||
|
||||
# Если вы используете Windows {#esli-vy-ispolzuete-windows}
|
||||
## Если вы используете Windows {#esli-vy-ispolzuete-windows}
|
||||
|
||||
Если вы используете Windows, вам потребуется создать виртуальную машину с Ubuntu. Для работы с виртуальной машиной, установите VirtualBox. Скачать Ubuntu можно на сайте: https://www.ubuntu.com/#download Создайте виртуальную машину из полученного образа. Выделите для неё не менее 4 GB оперативной памяти. Для запуска терминала в Ubuntu, найдите в меню программу со словом terminal (gnome-terminal, konsole или что-то в этом роде) или нажмите Ctrl+Alt+T.
|
||||
|
||||
# Если вы используете 32-битную систему {#esli-vy-ispolzuete-32-bitnuiu-sistemu}
|
||||
## Если вы используете 32-битную систему {#esli-vy-ispolzuete-32-bitnuiu-sistemu}
|
||||
|
||||
ClickHouse не работает и не собирается на 32-битных системах. Получите доступ к 64-битной системе и продолжайте.
|
||||
|
||||
# Создание репозитория на GitHub {#sozdanie-repozitoriia-na-github}
|
||||
## Создание репозитория на GitHub {#sozdanie-repozitoriia-na-github}
|
||||
|
||||
Для работы с репозиторием ClickHouse, вам потребуется аккаунт на GitHub. Наверное, он у вас уже есть.
|
||||
|
||||
@ -34,7 +34,7 @@ ClickHouse не работает и не собирается на 32-битны
|
||||
|
||||
Подробное руководство по использованию Git: https://git-scm.com/book/ru/v2
|
||||
|
||||
# Клонирование репозитория на рабочую машину {#klonirovanie-repozitoriia-na-rabochuiu-mashinu}
|
||||
## Клонирование репозитория на рабочую машину {#klonirovanie-repozitoriia-na-rabochuiu-mashinu}
|
||||
|
||||
Затем вам потребуется загрузить исходники для работы на свой компьютер. Это называется «клонирование репозитория», потому что создаёт на вашем компьютере локальную копию репозитория, с которой вы будете работать.
|
||||
|
||||
@ -78,7 +78,7 @@ ClickHouse не работает и не собирается на 32-битны
|
||||
|
||||
После этого, вы сможете добавлять в свой репозиторий обновления из репозитория Яндекса с помощью команды `git pull upstream master`.
|
||||
|
||||
## Работа с сабмодулями Git {#rabota-s-sabmoduliami-git}
|
||||
### Работа с сабмодулями Git {#rabota-s-sabmoduliami-git}
|
||||
|
||||
Работа с сабмодулями git может быть достаточно болезненной. Следующие команды позволят содержать их в порядке:
|
||||
|
||||
@ -110,7 +110,7 @@ The next commands would help you to reset all submodules to the initial state (!
|
||||
git submodule foreach git submodule foreach git reset --hard
|
||||
git submodule foreach git submodule foreach git clean -xfd
|
||||
|
||||
# Система сборки {#sistema-sborki}
|
||||
## Система сборки {#sistema-sborki}
|
||||
|
||||
ClickHouse использует систему сборки CMake и Ninja.
|
||||
|
||||
@ -130,11 +130,11 @@ Ninja - система запуска сборочных задач.
|
||||
|
||||
Проверьте версию CMake: `cmake --version`. Если версия меньше 3.3, то установите новую версию с сайта https://cmake.org/download/
|
||||
|
||||
# Необязательные внешние библиотеки {#neobiazatelnye-vneshnie-biblioteki}
|
||||
## Необязательные внешние библиотеки {#neobiazatelnye-vneshnie-biblioteki}
|
||||
|
||||
ClickHouse использует для сборки некоторое количество внешних библиотек. Но ни одну из них не требуется отдельно устанавливать, так как они собираются вместе с ClickHouse, из исходников, которые расположены в submodules. Посмотреть набор этих библиотек можно в директории contrib.
|
||||
|
||||
# Компилятор C++ {#kompiliator-c}
|
||||
## Компилятор C++ {#kompiliator-c}
|
||||
|
||||
В качестве компилятора C++ поддерживается GCC начиная с версии 9 или Clang начиная с версии 8.
|
||||
|
||||
@ -148,7 +148,7 @@ ClickHouse использует для сборки некоторое коли
|
||||
|
||||
Если вы решили использовать Clang, вы также можете установить `libc++` и `lld`, если вы знаете, что это такое. При желании, установите `ccache`.
|
||||
|
||||
# Процесс сборки {#protsess-sborki}
|
||||
## Процесс сборки {#protsess-sborki}
|
||||
|
||||
Теперь вы готовы к сборке ClickHouse. Для размещения собранных файлов, рекомендуется создать отдельную директорию build внутри директории ClickHouse:
|
||||
|
||||
@ -206,7 +206,7 @@ Mac OS X:
|
||||
|
||||
ls -l programs/clickhouse
|
||||
|
||||
# Запуск собранной версии ClickHouse {#zapusk-sobrannoi-versii-clickhouse}
|
||||
## Запуск собранной версии ClickHouse {#zapusk-sobrannoi-versii-clickhouse}
|
||||
|
||||
Для запуска сервера из под текущего пользователя, с выводом логов в терминал и с использованием примеров конфигурационных файлов, расположенных в исходниках, перейдите в директорию `ClickHouse/programs/server/` (эта директория находится не в директории build) и выполните:
|
||||
|
||||
@ -233,7 +233,7 @@ Mac OS X:
|
||||
sudo service clickhouse-server stop
|
||||
sudo -u clickhouse ClickHouse/build/programs/clickhouse server --config-file /etc/clickhouse-server/config.xml
|
||||
|
||||
# Среда разработки {#sreda-razrabotki}
|
||||
## Среда разработки {#sreda-razrabotki}
|
||||
|
||||
Если вы не знаете, какую среду разработки использовать, то рекомендуется использовать CLion. CLion является платным ПО, но его можно использовать бесплатно в течение пробного периода. Также он бесплатен для учащихся. CLion можно использовать как под Linux, так и под Mac OS X.
|
||||
|
||||
@ -243,7 +243,7 @@ Mac OS X:
|
||||
|
||||
На всякий случай заметим, что CLion самостоятельно создаёт свою build директорию, самостоятельно выбирает тип сборки debug по-умолчанию, для конфигурации использует встроенную в CLion версию CMake вместо установленного вами, а для запуска задач использует make вместо ninja. Это нормально, просто имейте это ввиду, чтобы не возникало путаницы.
|
||||
|
||||
# Написание кода {#napisanie-koda}
|
||||
## Написание кода {#napisanie-koda}
|
||||
|
||||
Описание архитектуры ClickHouse: https://clickhouse.tech/docs/ru/development/architecture/
|
||||
|
||||
@ -253,7 +253,7 @@ Mac OS X:
|
||||
|
||||
Список задач: https://github.com/ClickHouse/ClickHouse/issues?q=is%3Aopen+is%3Aissue+label%3A%22easy+task%22
|
||||
|
||||
# Тестовые данные {#testovye-dannye}
|
||||
## Тестовые данные {#testovye-dannye}
|
||||
|
||||
Разработка ClickHouse часто требует загрузки реалистичных наборов данных. Особенно это важно для тестирования производительности. Специально для вас мы подготовили набор данных, представляющий собой анонимизированные данные Яндекс.Метрики. Загрузка этих данных потребует ещё 3 GB места на диске. Для выполнения большинства задач разработки, загружать эти данные не обязательно.
|
||||
|
||||
@ -274,7 +274,7 @@ Mac OS X:
|
||||
clickhouse-client --max_insert_block_size 100000 --query "INSERT INTO test.hits FORMAT TSV" < hits_v1.tsv
|
||||
clickhouse-client --max_insert_block_size 100000 --query "INSERT INTO test.visits FORMAT TSV" < visits_v1.tsv
|
||||
|
||||
# Создание Pull Request {#sozdanie-pull-request}
|
||||
## Создание Pull Request {#sozdanie-pull-request}
|
||||
|
||||
Откройте свой форк репозитория в интерфейсе GitHub. Если вы вели разработку в бранче, выберите этот бранч. На странице будет доступна кнопка «Pull request». По сути, это означает «создать заявку на принятие моих изменений в основной репозиторий».
|
||||
|
||||
|
@ -3,15 +3,52 @@ toc_priority: 32
|
||||
toc_title: Atomic
|
||||
---
|
||||
|
||||
|
||||
# Atomic {#atomic}
|
||||
|
||||
Поддерживает неблокирующие запросы `DROP` и `RENAME TABLE` и запросы `EXCHANGE TABLES t1 AND t2`. Движок `Atomic` используется по умолчанию.
|
||||
Поддерживает неблокирующие запросы [DROP TABLE](#drop-detach-table) и [RENAME TABLE](#rename-table) и атомарные запросы [EXCHANGE TABLES t1 AND t](#exchange-tables). Движок `Atomic` используется по умолчанию.
|
||||
|
||||
## Создание БД {#creating-a-database}
|
||||
|
||||
```sql
|
||||
CREATE DATABASE test ENGINE = Atomic;
|
||||
``` sql
|
||||
CREATE DATABASE test[ ENGINE = Atomic];
|
||||
```
|
||||
|
||||
[Оригинальная статья](https://clickhouse.tech/docs/ru/engines/database-engines/atomic/) <!--hide-->
|
||||
## Особенности и рекомендации {#specifics-and-recommendations}
|
||||
|
||||
### UUID {#table-uuid}
|
||||
|
||||
Каждая таблица в базе данных `Atomic` имеет уникальный [UUID](../../sql-reference/data-types/uuid.md) и хранит данные в папке `/clickhouse_path/store/xxx/xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy/`, где `xxxyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy` - это UUID таблицы.
|
||||
Обычно UUID генерируется автоматически, но пользователь также может явно указать UUID в момент создания таблицы (однако это не рекомендуется). Для отображения UUID в запросе `SHOW CREATE` вы можете использовать настройку [show_table_uuid_in_table_create_query_if_not_nil](../../operations/settings/settings.md#show_table_uuid_in_table_create_query_if_not_nil). Результат выполнения в таком случае будет иметь вид:
|
||||
|
||||
```sql
|
||||
CREATE TABLE name UUID '28f1c61c-2970-457a-bffe-454156ddcfef' (n UInt64) ENGINE = ...;
|
||||
```
|
||||
### RENAME TABLE {#rename-table}
|
||||
|
||||
Запросы `RENAME` выполняются без изменения UUID и перемещения табличных данных. Эти запросы не ожидают завершения использующих таблицу запросов и будут выполнены мгновенно.
|
||||
|
||||
### DROP/DETACH TABLE {#drop-detach-table}
|
||||
|
||||
При выполнении запроса `DROP TABLE` никакие данные не удаляются. Таблица помечается как удаленная, метаданные перемещаются в папку `/clickhouse_path/metadata_dropped/` и база данных уведомляет фоновый поток. Задержка перед окончательным удалением данных задается настройкой [database_atomic_delay_before_drop_table_sec](../../operations/server-configuration-parameters/settings.md#database_atomic_delay_before_drop_table_sec).
|
||||
Вы можете задать синхронный режим, определяя модификатор `SYNC`. Используйте для этого настройку [database_atomic_wait_for_drop_and_detach_synchronously](../../operations/settings/settings.md#database_atomic_wait_for_drop_and_detach_synchronously). В этом случае запрос `DROP` ждет завершения `SELECT`, `INSERT` и других запросов, которые используют таблицу. Таблица будет фактически удалена, когда она не будет использоваться.
|
||||
|
||||
### EXCHANGE TABLES {#exchange-tables}
|
||||
|
||||
Запрос `EXCHANGE` меняет местами две таблицы атомарно. Вместо неатомарной операции:
|
||||
|
||||
```sql
|
||||
RENAME TABLE new_table TO tmp, old_table TO new_table, tmp TO old_table;
|
||||
```
|
||||
вы можете использовать один атомарный запрос:
|
||||
|
||||
``` sql
|
||||
EXCHANGE TABLES new_table AND old_table;
|
||||
```
|
||||
|
||||
### ReplicatedMergeTree in Atomic Database {#replicatedmergetree-in-atomic-database}
|
||||
|
||||
Для таблиц [ReplicatedMergeTree](../table-engines/mergetree-family/replication.md#table_engines-replication) рекомендуется не указывать параметры движка - путь в ZooKeeper и имя реплики. В этом случае будут использоваться параметры конфигурации: [default_replica_path](../../operations/server-configuration-parameters/settings.md#default_replica_path) и [default_replica_name](../../operations/server-configuration-parameters/settings.md#default_replica_name). Если вы хотите определить параметры движка явно, рекомендуется использовать макрос {uuid}. Это удобно, так как автоматически генерируются уникальные пути для каждой таблицы в ZooKeeper.
|
||||
|
||||
## Смотрите также
|
||||
|
||||
- Системная таблица [system.databases](../../operations/system-tables/databases.md).
|
||||
|
@ -101,6 +101,12 @@ ClickHouse проверяет условия для `min_part_size` и `min_part
|
||||
</core_dump>
|
||||
```
|
||||
|
||||
## database_atomic_delay_before_drop_table_sec {#database_atomic_delay_before_drop_table_sec}
|
||||
|
||||
Устанавливает задержку перед удалением табличных данных, в секундах. Если запрос имеет идентификатор `SYNC`, эта настройка игнорируется.
|
||||
|
||||
Значение по умолчанию: `480` (8 минут).
|
||||
|
||||
## default\_database {#default-database}
|
||||
|
||||
База данных по умолчанию.
|
||||
|
@ -2690,6 +2690,28 @@ SELECT * FROM test2;
|
||||
|
||||
Значение по умолчанию: `0`.
|
||||
|
||||
## database_atomic_wait_for_drop_and_detach_synchronously {#database_atomic_wait_for_drop_and_detach_synchronously}
|
||||
|
||||
Добавляет модификатор `SYNC` ко всем запросам `DROP` и `DETACH`.
|
||||
|
||||
Возможные значения:
|
||||
|
||||
- 0 — Запросы будут выполняться с задержкой.
|
||||
- 1 — Запросы будут выполняться без задержки.
|
||||
|
||||
Значение по умолчанию: `0`.
|
||||
|
||||
## show_table_uuid_in_table_create_query_if_not_nil {#show_table_uuid_in_table_create_query_if_not_nil}
|
||||
|
||||
Устанавливает отображение запроса `SHOW TABLE`.
|
||||
|
||||
Возможные значения:
|
||||
|
||||
- 0 — Запрос будет отображаться без UUID таблицы.
|
||||
- 1 — Запрос будет отображаться с UUID таблицы.
|
||||
|
||||
Значение по умолчанию: `0`.
|
||||
|
||||
## allow_experimental_live_view {#allow-experimental-live-view}
|
||||
|
||||
Включает экспериментальную возможность использования [LIVE-представлений](../../sql-reference/statements/create/view.md#live-view).
|
||||
|
@ -12,10 +12,10 @@ toc_title: USER
|
||||
``` sql
|
||||
ALTER USER [IF EXISTS] name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...]
|
||||
[IDENTIFIED [WITH {PLAINTEXT_PASSWORD|SHA256_PASSWORD|DOUBLE_SHA1_PASSWORD}] BY {'password'|'hash'}]
|
||||
[[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
[NOT IDENTIFIED | IDENTIFIED {[WITH {no_password | plaintext_password | sha256_password | sha256_hash | double_sha1_password | double_sha1_hash}] BY {'password' | 'hash'}} | {WITH ldap SERVER 'server_name'} | {WITH kerberos [REALM 'realm']}]
|
||||
[[ADD | DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
[DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY | WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
Для выполнения `ALTER USER` необходима привилегия [ALTER USER](../grant.md#grant-access-management).
|
||||
|
@ -5,12 +5,14 @@ toc_title: ATTACH
|
||||
|
||||
# ATTACH Statement {#attach}
|
||||
|
||||
Запрос полностью аналогичен запросу `CREATE`, но:
|
||||
Выполняет подключение таблицы, например, при перемещении базы данных на другой сервер.
|
||||
|
||||
- вместо слова `CREATE` используется слово `ATTACH`;
|
||||
- запрос не создаёт данные на диске, а предполагает, что данные уже лежат в соответствующих местах, и всего лишь добавляет информацию о таблице на сервер. После выполнения запроса `ATTACH` сервер будет знать о существовании таблицы.
|
||||
Запрос не создаёт данные на диске, а предполагает, что данные уже лежат в соответствующих местах, и всего лишь добавляет информацию о таблице на сервер. После выполнения запроса `ATTACH` сервер будет знать о существовании таблицы.
|
||||
|
||||
Если таблица перед этим была отключена ([DETACH](../../sql-reference/statements/detach.md)), т.е. её структура известна, можно использовать сокращенную форму записи без определения структуры.
|
||||
Если таблица перед этим была отключена при помощи ([DETACH](../../sql-reference/statements/detach.md)), т.е. её структура известна, можно использовать сокращенную форму записи без определения структуры.
|
||||
|
||||
## Варианты синтаксиса {#syntax-forms}
|
||||
### Присоединение существующей таблицы {#attach-existing-table}
|
||||
|
||||
``` sql
|
||||
ATTACH TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster]
|
||||
@ -20,4 +22,38 @@ ATTACH TABLE [IF NOT EXISTS] [db.]name [ON CLUSTER cluster]
|
||||
|
||||
Если таблица была отключена перманентно, она не будет подключена обратно во время старта сервера, так что нужно явно использовать запрос `ATTACH`, чтобы подключить ее.
|
||||
|
||||
### Создание новой таблицы и присоединение данных {#create-new-table-and-attach-data}
|
||||
|
||||
**С указанием пути к табличным данным**
|
||||
|
||||
```sql
|
||||
ATTACH TABLE name FROM 'path/to/data/' (col1 Type1, ...)
|
||||
```
|
||||
|
||||
Cоздает новую таблицу с указанной структурой и присоединяет табличные данные из соответствующего каталога в `user_files`.
|
||||
|
||||
**Пример**
|
||||
|
||||
Запрос:
|
||||
|
||||
```sql
|
||||
DROP TABLE IF EXISTS test;
|
||||
INSERT INTO TABLE FUNCTION file('01188_attach/test/data.TSV', 'TSV', 's String, n UInt8') VALUES ('test', 42);
|
||||
ATTACH TABLE test FROM '01188_attach/test' (s String, n UInt8) ENGINE = File(TSV);
|
||||
SELECT * FROM test;
|
||||
```
|
||||
Результат:
|
||||
|
||||
```sql
|
||||
┌─s────┬──n─┐
|
||||
│ test │ 42 │
|
||||
└──────┴────┘
|
||||
```
|
||||
|
||||
**С указанием UUID таблицы** (Только для баз данных `Atomic`)
|
||||
|
||||
```sql
|
||||
ATTACH TABLE name UUID '<uuid>' (col1 Type1, ...)
|
||||
```
|
||||
|
||||
Cоздает новую таблицу с указанной структурой и присоединяет данные из таблицы с указанным UUID.
|
||||
|
@ -9,15 +9,17 @@ toc_title: "Пользователь"
|
||||
|
||||
Синтаксис:
|
||||
|
||||
```sql
|
||||
``` sql
|
||||
CREATE USER [IF NOT EXISTS | OR REPLACE] name1 [ON CLUSTER cluster_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] ...]
|
||||
[IDENTIFIED [WITH {NO_PASSWORD|PLAINTEXT_PASSWORD|SHA256_PASSWORD|SHA256_HASH|DOUBLE_SHA1_PASSWORD|DOUBLE_SHA1_HASH}] BY {'password'|'hash'}]
|
||||
[NOT IDENTIFIED | IDENTIFIED {[WITH {no_password | plaintext_password | sha256_password | sha256_hash | double_sha1_password | double_sha1_hash}] BY {'password' | 'hash'}} | {WITH ldap SERVER 'server_name'} | {WITH kerberos [REALM 'realm']}]
|
||||
[HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
[DEFAULT ROLE role [,...]]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY | WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
`ON CLUSTER` позволяет создавать пользователей в кластере, см. [Распределенные DDL](../../../sql-reference/distributed-ddl.md).
|
||||
|
||||
## Идентификация
|
||||
|
||||
Существует несколько способов идентификации пользователя:
|
||||
@ -28,6 +30,8 @@ CREATE USER [IF NOT EXISTS | OR REPLACE] name1 [ON CLUSTER cluster_name1]
|
||||
- `IDENTIFIED WITH sha256_hash BY 'hash'`
|
||||
- `IDENTIFIED WITH double_sha1_password BY 'qwerty'`
|
||||
- `IDENTIFIED WITH double_sha1_hash BY 'hash'`
|
||||
- `IDENTIFIED WITH ldap SERVER 'server_name'`
|
||||
- `IDENTIFIED WITH kerberos` or `IDENTIFIED WITH kerberos REALM 'realm'`
|
||||
|
||||
## Пользовательский хост
|
||||
|
||||
|
@ -121,7 +121,7 @@ struct QuantileExact : QuantileExactBase<Value, QuantileExact<Value>>
|
||||
|
||||
/// QuantileExactExclusive is equivalent to Excel PERCENTILE.EXC, R-6, SAS-4, SciPy-(0,0)
|
||||
template <typename Value>
|
||||
/// There is no virtual-like functions. So we don't inherit from QuantileExactBase.
|
||||
/// There are no virtual-like functions. So we don't inherit from QuantileExactBase.
|
||||
struct QuantileExactExclusive : public QuantileExact<Value>
|
||||
{
|
||||
using QuantileExact<Value>::array;
|
||||
@ -189,7 +189,7 @@ struct QuantileExactExclusive : public QuantileExact<Value>
|
||||
|
||||
/// QuantileExactInclusive is equivalent to Excel PERCENTILE and PERCENTILE.INC, R-7, SciPy-(1,1)
|
||||
template <typename Value>
|
||||
/// There is no virtual-like functions. So we don't inherit from QuantileExactBase.
|
||||
/// There are no virtual-like functions. So we don't inherit from QuantileExactBase.
|
||||
struct QuantileExactInclusive : public QuantileExact<Value>
|
||||
{
|
||||
using QuantileExact<Value>::array;
|
||||
|
@ -375,11 +375,9 @@ if (ZSTD_LIBRARY)
|
||||
endif ()
|
||||
endif()
|
||||
|
||||
set (LZMA_LIBRARY liblzma)
|
||||
set (LZMA_INCLUDE_DIR ${ClickHouse_SOURCE_DIR}/contrib/xz/src/liblzma/api)
|
||||
if (LZMA_LIBRARY)
|
||||
target_link_libraries (clickhouse_common_io PUBLIC ${LZMA_LIBRARY})
|
||||
target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${LZMA_INCLUDE_DIR})
|
||||
if (XZ_LIBRARY)
|
||||
target_link_libraries (clickhouse_common_io PUBLIC ${XZ_LIBRARY})
|
||||
target_include_directories (clickhouse_common_io SYSTEM BEFORE PUBLIC ${XZ_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
if (USE_ICU)
|
||||
|
@ -59,13 +59,16 @@ public:
|
||||
|
||||
StringRef serializeValueIntoArena(size_t /*n*/, Arena & arena, char const *& begin) const override
|
||||
{
|
||||
return { arena.allocContinue(0, begin), 0 };
|
||||
/// Has to put one useless byte into Arena, because serialization into zero number of bytes is ambiguous.
|
||||
char * res = arena.allocContinue(1, begin);
|
||||
*res = 0;
|
||||
return { res, 1 };
|
||||
}
|
||||
|
||||
const char * deserializeAndInsertFromArena(const char * pos) override
|
||||
{
|
||||
++s;
|
||||
return pos;
|
||||
return pos + 1;
|
||||
}
|
||||
|
||||
const char * skipSerializedInArena(const char * pos) const override
|
||||
|
@ -60,7 +60,7 @@ void Epoll::remove(int fd)
|
||||
size_t Epoll::getManyReady(int max_events, epoll_event * events_out, bool blocking) const
|
||||
{
|
||||
if (events_count == 0)
|
||||
throw Exception("There is no events in epoll", ErrorCodes::LOGICAL_ERROR);
|
||||
throw Exception("There are no events in epoll", ErrorCodes::LOGICAL_ERROR);
|
||||
|
||||
int ready_size;
|
||||
int timeout = blocking ? -1 : 0;
|
||||
|
@ -150,6 +150,13 @@ void KeeperServer::putRequest(const KeeperStorage::RequestForSession & request_f
|
||||
|
||||
int64_t KeeperServer::getSessionID(int64_t session_timeout_ms)
|
||||
{
|
||||
/// Just some sanity check. We don't want to make a lot of clients wait with lock.
|
||||
if (active_session_id_requests > 10)
|
||||
throw Exception(ErrorCodes::RAFT_ERROR, "Too many concurrent SessionID requests already in flight");
|
||||
|
||||
++active_session_id_requests;
|
||||
SCOPE_EXIT({ --active_session_id_requests; });
|
||||
|
||||
auto entry = nuraft::buffer::alloc(sizeof(int64_t));
|
||||
/// Just special session request
|
||||
nuraft::buffer_serializer bs(entry);
|
||||
|
@ -34,6 +34,7 @@ private:
|
||||
std::atomic<bool> initialized_flag = false;
|
||||
std::condition_variable initialized_cv;
|
||||
std::atomic<bool> initial_batch_committed = false;
|
||||
std::atomic<size_t> active_session_id_requests = 0;
|
||||
|
||||
nuraft::cb_func::ReturnCode callbackFunc(nuraft::cb_func::Type type, nuraft::cb_func::Param * param);
|
||||
|
||||
|
@ -169,15 +169,15 @@ void KeeperStateMachine::create_snapshot(
|
||||
bool ret = true;
|
||||
try
|
||||
{
|
||||
auto snapshot_buf = snapshot_manager.serializeSnapshotToBuffer(*snapshot);
|
||||
auto result_path = snapshot_manager.serializeSnapshotBufferToDisk(*snapshot_buf, snapshot->snapshot_meta->get_last_log_idx());
|
||||
{
|
||||
std::lock_guard lock(snapshots_lock);
|
||||
auto snapshot_buf = snapshot_manager.serializeSnapshotToBuffer(*snapshot);
|
||||
auto result_path = snapshot_manager.serializeSnapshotBufferToDisk(*snapshot_buf, snapshot->snapshot_meta->get_last_log_idx());
|
||||
latest_snapshot_buf = snapshot_buf;
|
||||
latest_snapshot_meta = snapshot->snapshot_meta;
|
||||
}
|
||||
|
||||
LOG_DEBUG(log, "Created persistent snapshot {} with path {}", latest_snapshot_meta->get_last_log_idx(), result_path);
|
||||
LOG_DEBUG(log, "Created persistent snapshot {} with path {}", latest_snapshot_meta->get_last_log_idx(), result_path);
|
||||
}
|
||||
|
||||
{
|
||||
/// Must do it with lock (clearing elements from list)
|
||||
@ -228,37 +228,19 @@ void KeeperStateMachine::save_logical_snp_obj(
|
||||
nuraft::ptr<nuraft::buffer> snp_buf = s.serialize();
|
||||
cloned_meta = nuraft::snapshot::deserialize(*snp_buf);
|
||||
|
||||
/// Sometimes NuRaft can call save and create snapshots from different threads
|
||||
/// at once. To avoid race conditions we serialize snapshots through snapshots_queue
|
||||
/// TODO: make something better
|
||||
CreateSnapshotTask snapshot_task;
|
||||
std::shared_ptr<std::promise<void>> waiter = std::make_shared<std::promise<void>>();
|
||||
auto future = waiter->get_future();
|
||||
snapshot_task.snapshot = nullptr;
|
||||
snapshot_task.create_snapshot = [this, waiter, cloned_buffer, log_idx = s.get_last_log_idx()] (KeeperStorageSnapshotPtr &&)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto result_path = snapshot_manager.serializeSnapshotBufferToDisk(*cloned_buffer, log_idx);
|
||||
LOG_DEBUG(log, "Saved snapshot {} to path {}", log_idx, result_path);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException(log);
|
||||
}
|
||||
waiter->set_value();
|
||||
};
|
||||
snapshots_queue.push(std::move(snapshot_task));
|
||||
future.wait();
|
||||
|
||||
try
|
||||
{
|
||||
std::lock_guard lock(snapshots_lock);
|
||||
auto result_path = snapshot_manager.serializeSnapshotBufferToDisk(*cloned_buffer, s.get_last_log_idx());
|
||||
latest_snapshot_buf = cloned_buffer;
|
||||
latest_snapshot_meta = cloned_meta;
|
||||
LOG_DEBUG(log, "Saved snapshot {} to path {}", s.get_last_log_idx(), result_path);
|
||||
obj_id++;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException(log);
|
||||
}
|
||||
|
||||
|
||||
obj_id++;
|
||||
}
|
||||
|
||||
int KeeperStateMachine::read_logical_snp_obj(
|
||||
|
@ -177,7 +177,7 @@ inline bool_if_big_int_vs_float<TABigInt, TAFloat> equalsOpTmpl(TABigInt, TAFloa
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Final realiztions */
|
||||
/* Final implementations */
|
||||
|
||||
|
||||
template <typename A, typename B>
|
||||
@ -274,12 +274,14 @@ inline bool greaterOp<DB::UInt64, DB::Float32>(DB::UInt64 u, DB::Float32 f)
|
||||
template <>
|
||||
inline bool greaterOp<DB::Float64, DB::UInt128>(DB::Float64 f, DB::UInt128 u)
|
||||
{
|
||||
/// TODO: This is wrong.
|
||||
return u.low == 0 && greaterOp(f, u.high);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool greaterOp<DB::UInt128, DB::Float64>(DB::UInt128 u, DB::Float64 f)
|
||||
{
|
||||
/// TODO: This is wrong.
|
||||
return u.low != 0 || greaterOp(u.high, f);
|
||||
}
|
||||
|
||||
@ -367,6 +369,7 @@ inline bool equalsOp<DB::Int64, DB::Float32>(DB::Int64 u, DB::Float32 f)
|
||||
template <>
|
||||
inline bool equalsOp<DB::UInt128, DB::Float64>(DB::UInt128 u, DB::Float64 f)
|
||||
{
|
||||
/// TODO: This is wrong.
|
||||
return u.low == 0 && equalsOp(static_cast<UInt64>(u.high), f);
|
||||
}
|
||||
|
||||
|
@ -18,97 +18,72 @@
|
||||
namespace DB
|
||||
{
|
||||
|
||||
static const size_t MAX_CONNECTIONS = 16;
|
||||
|
||||
inline static UInt16 getPortFromContext(const Context & context, bool secure)
|
||||
namespace ErrorCodes
|
||||
{
|
||||
return secure ? context.getTCPPortSecure().value_or(0) : context.getTCPPort();
|
||||
extern const int BAD_ARGUMENTS;
|
||||
}
|
||||
|
||||
static ConnectionPoolWithFailoverPtr createPool(
|
||||
const std::string & host,
|
||||
UInt16 port,
|
||||
bool secure,
|
||||
const std::string & db,
|
||||
const std::string & user,
|
||||
const std::string & password)
|
||||
namespace
|
||||
{
|
||||
ConnectionPoolPtrs pools;
|
||||
pools.emplace_back(std::make_shared<ConnectionPool>(
|
||||
MAX_CONNECTIONS,
|
||||
host,
|
||||
port,
|
||||
db,
|
||||
user,
|
||||
password,
|
||||
"", /* cluster */
|
||||
"", /* cluster_secret */
|
||||
"ClickHouseDictionarySource",
|
||||
Protocol::Compression::Enable,
|
||||
secure ? Protocol::Secure::Enable : Protocol::Secure::Disable));
|
||||
return std::make_shared<ConnectionPoolWithFailover>(pools, LoadBalancing::RANDOM);
|
||||
}
|
||||
constexpr size_t MAX_CONNECTIONS = 16;
|
||||
|
||||
inline UInt16 getPortFromContext(const Context & context, bool secure)
|
||||
{
|
||||
return secure ? context.getTCPPortSecure().value_or(0) : context.getTCPPort();
|
||||
}
|
||||
|
||||
ConnectionPoolWithFailoverPtr createPool(const ClickHouseDictionarySource::Configuration & configuration)
|
||||
{
|
||||
if (configuration.is_local)
|
||||
return nullptr;
|
||||
|
||||
ConnectionPoolPtrs pools;
|
||||
pools.emplace_back(std::make_shared<ConnectionPool>(
|
||||
MAX_CONNECTIONS,
|
||||
configuration.host,
|
||||
configuration.port,
|
||||
configuration.db,
|
||||
configuration.user,
|
||||
configuration.password,
|
||||
"", /* cluster */
|
||||
"", /* cluster_secret */
|
||||
"ClickHouseDictionarySource",
|
||||
Protocol::Compression::Enable,
|
||||
configuration.secure ? Protocol::Secure::Enable : Protocol::Secure::Disable));
|
||||
|
||||
return std::make_shared<ConnectionPoolWithFailover>(pools, LoadBalancing::RANDOM);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
ClickHouseDictionarySource::ClickHouseDictionarySource(
|
||||
const DictionaryStructure & dict_struct_,
|
||||
const Poco::Util::AbstractConfiguration & config,
|
||||
const std::string & path_to_settings,
|
||||
const std::string & config_prefix,
|
||||
const Configuration & configuration_,
|
||||
const Block & sample_block_,
|
||||
const Context & context_,
|
||||
const std::string & default_database)
|
||||
const Context & context_)
|
||||
: update_time{std::chrono::system_clock::from_time_t(0)}
|
||||
, dict_struct{dict_struct_}
|
||||
, secure(config.getBool(config_prefix + ".secure", false))
|
||||
, host{config.getString(config_prefix + ".host", "localhost")}
|
||||
, port(config.getInt(config_prefix + ".port", getPortFromContext(context_, secure)))
|
||||
, user{config.getString(config_prefix + ".user", "default")}
|
||||
, password{config.getString(config_prefix + ".password", "")}
|
||||
, db{config.getString(config_prefix + ".db", default_database)}
|
||||
, table{config.getString(config_prefix + ".table")}
|
||||
, where{config.getString(config_prefix + ".where", "")}
|
||||
, update_field{config.getString(config_prefix + ".update_field", "")}
|
||||
, invalidate_query{config.getString(config_prefix + ".invalidate_query", "")}
|
||||
, query_builder{dict_struct, db, "", table, where, IdentifierQuotingStyle::Backticks}
|
||||
, configuration{configuration_}
|
||||
, query_builder{dict_struct, configuration.db, "", configuration.table, configuration.where, IdentifierQuotingStyle::Backticks}
|
||||
, sample_block{sample_block_}
|
||||
, context(context_)
|
||||
, is_local{isLocalAddress({host, port}, getPortFromContext(context_, secure))}
|
||||
, pool{is_local ? nullptr : createPool(host, port, secure, db, user, password)}
|
||||
, context{context_}
|
||||
, pool{createPool(configuration)}
|
||||
, load_all_query{query_builder.composeLoadAllQuery()}
|
||||
{
|
||||
/// We should set user info even for the case when the dictionary is loaded in-process (without TCP communication).
|
||||
if (is_local)
|
||||
{
|
||||
context.setUser(user, password, Poco::Net::SocketAddress("127.0.0.1", 0));
|
||||
context = copyContextAndApplySettings(path_to_settings, context, config);
|
||||
}
|
||||
|
||||
/// Query context is needed because some code in executeQuery function may assume it exists.
|
||||
/// Current example is Context::getSampleBlockCache from InterpreterSelectWithUnionQuery::getSampleBlock.
|
||||
context.makeQueryContext();
|
||||
}
|
||||
|
||||
|
||||
ClickHouseDictionarySource::ClickHouseDictionarySource(const ClickHouseDictionarySource & other)
|
||||
: update_time{other.update_time}
|
||||
, dict_struct{other.dict_struct}
|
||||
, secure{other.secure}
|
||||
, host{other.host}
|
||||
, port{other.port}
|
||||
, user{other.user}
|
||||
, password{other.password}
|
||||
, db{other.db}
|
||||
, table{other.table}
|
||||
, where{other.where}
|
||||
, update_field{other.update_field}
|
||||
, invalidate_query{other.invalidate_query}
|
||||
, configuration{other.configuration}
|
||||
, invalidate_query_response{other.invalidate_query_response}
|
||||
, query_builder{dict_struct, db, "", table, where, IdentifierQuotingStyle::Backticks}
|
||||
, query_builder{dict_struct, configuration.db, "", configuration.table, configuration.where, IdentifierQuotingStyle::Backticks}
|
||||
, sample_block{other.sample_block}
|
||||
, context(other.context)
|
||||
, is_local{other.is_local}
|
||||
, pool{is_local ? nullptr : createPool(host, port, secure, db, user, password)}
|
||||
, context{other.context}
|
||||
, pool{createPool(configuration)}
|
||||
, load_all_query{other.load_all_query}
|
||||
{
|
||||
context.makeQueryContext();
|
||||
@ -121,7 +96,7 @@ std::string ClickHouseDictionarySource::getUpdateFieldAndDate()
|
||||
time_t hr_time = std::chrono::system_clock::to_time_t(update_time) - 1;
|
||||
std::string str_time = DateLUT::instance().timeToString(hr_time);
|
||||
update_time = std::chrono::system_clock::now();
|
||||
return query_builder.composeUpdateQuery(update_field, str_time);
|
||||
return query_builder.composeUpdateQuery(configuration.update_field, str_time);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -155,9 +130,9 @@ BlockInputStreamPtr ClickHouseDictionarySource::loadKeys(const Columns & key_col
|
||||
|
||||
bool ClickHouseDictionarySource::isModified() const
|
||||
{
|
||||
if (!invalidate_query.empty())
|
||||
if (!configuration.invalidate_query.empty())
|
||||
{
|
||||
auto response = doInvalidateQuery(invalidate_query);
|
||||
auto response = doInvalidateQuery(configuration.invalidate_query);
|
||||
LOG_TRACE(log, "Invalidate query has returned: {}, previous value: {}", response, invalidate_query_response);
|
||||
if (invalidate_query_response == response)
|
||||
return false;
|
||||
@ -168,21 +143,21 @@ bool ClickHouseDictionarySource::isModified() const
|
||||
|
||||
bool ClickHouseDictionarySource::hasUpdateField() const
|
||||
{
|
||||
return !update_field.empty();
|
||||
return !configuration.update_field.empty();
|
||||
}
|
||||
|
||||
std::string ClickHouseDictionarySource::toString() const
|
||||
{
|
||||
return "ClickHouse: " + db + '.' + table + (where.empty() ? "" : ", where: " + where);
|
||||
const std::string & where = configuration.where;
|
||||
return "ClickHouse: " + configuration.db + '.' + configuration.table + (where.empty() ? "" : ", where: " + where);
|
||||
}
|
||||
|
||||
|
||||
BlockInputStreamPtr ClickHouseDictionarySource::createStreamForQuery(const String & query)
|
||||
{
|
||||
/// Sample block should not contain first row default values
|
||||
auto empty_sample_block = sample_block.cloneEmpty();
|
||||
|
||||
if (is_local)
|
||||
if (configuration.is_local)
|
||||
{
|
||||
auto stream = executeQuery(query, context, true).getInputStream();
|
||||
stream = std::make_shared<ConvertingBlockInputStream>(stream, empty_sample_block, ConvertingBlockInputStream::MatchColumnsMode::Position);
|
||||
@ -195,7 +170,7 @@ BlockInputStreamPtr ClickHouseDictionarySource::createStreamForQuery(const Strin
|
||||
std::string ClickHouseDictionarySource::doInvalidateQuery(const std::string & request) const
|
||||
{
|
||||
LOG_TRACE(log, "Performing invalidate query");
|
||||
if (is_local)
|
||||
if (configuration.is_local)
|
||||
{
|
||||
Context query_context = context;
|
||||
auto input_block = executeQuery(request, query_context, true).getInputStream();
|
||||
@ -210,7 +185,6 @@ std::string ClickHouseDictionarySource::doInvalidateQuery(const std::string & re
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void registerDictionarySourceClickHouse(DictionarySourceFactory & factory)
|
||||
{
|
||||
auto create_table_source = [=](const DictionaryStructure & dict_struct,
|
||||
@ -218,12 +192,48 @@ void registerDictionarySourceClickHouse(DictionarySourceFactory & factory)
|
||||
const std::string & config_prefix,
|
||||
Block & sample_block,
|
||||
const Context & context,
|
||||
const std::string & default_database,
|
||||
const std::string & default_database [[maybe_unused]],
|
||||
bool /* check_config */) -> DictionarySourcePtr
|
||||
{
|
||||
return std::make_unique<ClickHouseDictionarySource>(
|
||||
dict_struct, config, config_prefix, config_prefix + ".clickhouse", sample_block, context, default_database);
|
||||
bool secure = config.getBool(config_prefix + ".secure", false);
|
||||
Context context_copy = context;
|
||||
|
||||
UInt16 default_port = getPortFromContext(context_copy, secure);
|
||||
|
||||
std::string settings_config_prefix = config_prefix + ".clickhouse";
|
||||
std::string host = config.getString(settings_config_prefix + ".host", "localhost");
|
||||
UInt16 port = static_cast<UInt16>(config.getUInt(settings_config_prefix + ".port", default_port));
|
||||
|
||||
ClickHouseDictionarySource::Configuration configuration {
|
||||
.secure = config.getBool(settings_config_prefix + ".secure", false),
|
||||
.host = host,
|
||||
.port = port,
|
||||
.user = config.getString(settings_config_prefix + ".user", "default"),
|
||||
.password = config.getString(settings_config_prefix + ".password", ""),
|
||||
.db = config.getString(settings_config_prefix + ".db", default_database),
|
||||
.table = config.getString(settings_config_prefix + ".table"),
|
||||
.where = config.getString(settings_config_prefix + ".where", ""),
|
||||
.update_field = config.getString(settings_config_prefix + ".update_field", ""),
|
||||
.invalidate_query = config.getString(settings_config_prefix + ".invalidate_query", ""),
|
||||
.is_local = isLocalAddress({host, port}, default_port)
|
||||
};
|
||||
|
||||
/// We should set user info even for the case when the dictionary is loaded in-process (without TCP communication).
|
||||
if (configuration.is_local)
|
||||
{
|
||||
context_copy.setUser(configuration.user, configuration.password, Poco::Net::SocketAddress("127.0.0.1", 0));
|
||||
context_copy = copyContextAndApplySettings(config_prefix, context_copy, config);
|
||||
}
|
||||
|
||||
String dictionary_name = config.getString(".dictionary.name", "");
|
||||
String dictionary_database = config.getString(".dictionary.database", "");
|
||||
|
||||
if (dictionary_name == configuration.table && dictionary_database == configuration.db)
|
||||
throw Exception(ErrorCodes::BAD_ARGUMENTS, "ClickHouseDictionarySource table cannot be dictionary table");
|
||||
|
||||
return std::make_unique<ClickHouseDictionarySource>(dict_struct, configuration, sample_block, context_copy);
|
||||
};
|
||||
|
||||
factory.registerSource("clickhouse", create_table_source);
|
||||
}
|
||||
|
||||
|
@ -18,14 +18,26 @@ namespace DB
|
||||
class ClickHouseDictionarySource final : public IDictionarySource
|
||||
{
|
||||
public:
|
||||
struct Configuration
|
||||
{
|
||||
const bool secure;
|
||||
const std::string host;
|
||||
const UInt16 port;
|
||||
const std::string user;
|
||||
const std::string password;
|
||||
const std::string db;
|
||||
const std::string table;
|
||||
const std::string where;
|
||||
const std::string update_field;
|
||||
const std::string invalidate_query;
|
||||
const bool is_local;
|
||||
};
|
||||
|
||||
ClickHouseDictionarySource(
|
||||
const DictionaryStructure & dict_struct_,
|
||||
const Poco::Util::AbstractConfiguration & config,
|
||||
const std::string & path_to_settings,
|
||||
const std::string & config_prefix,
|
||||
const Configuration & configuration_,
|
||||
const Block & sample_block_,
|
||||
const Context & context,
|
||||
const std::string & default_database);
|
||||
const Context & context);
|
||||
|
||||
/// copy-constructor is provided in order to support cloneability
|
||||
ClickHouseDictionarySource(const ClickHouseDictionarySource & other);
|
||||
@ -50,7 +62,7 @@ public:
|
||||
|
||||
/// Used for detection whether the hashtable should be preallocated
|
||||
/// (since if there is WHERE then it can filter out too much)
|
||||
bool hasWhere() const { return !where.empty(); }
|
||||
bool hasWhere() const { return !configuration.where.empty(); }
|
||||
|
||||
private:
|
||||
std::string getUpdateFieldAndDate();
|
||||
@ -61,21 +73,11 @@ private:
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> update_time;
|
||||
const DictionaryStructure dict_struct;
|
||||
const bool secure;
|
||||
const std::string host;
|
||||
const UInt16 port;
|
||||
const std::string user;
|
||||
const std::string password;
|
||||
const std::string db;
|
||||
const std::string table;
|
||||
const std::string where;
|
||||
const std::string update_field;
|
||||
std::string invalidate_query;
|
||||
const Configuration configuration;
|
||||
mutable std::string invalidate_query_response;
|
||||
ExternalQueryBuilder query_builder;
|
||||
Block sample_block;
|
||||
Context context;
|
||||
const bool is_local;
|
||||
ConnectionPoolWithFailoverPtr pool;
|
||||
const std::string load_all_query;
|
||||
Poco::Logger * log = &Poco::Logger::get("ClickHouseDictionarySource");
|
||||
|
@ -101,7 +101,7 @@ using RequestedIds = const VectorUInt64 *;
|
||||
using LibraryLoadIdsFunc = RawClickHouseLibraryTable (*)(LibraryData, LibrarySettings, RequestedColumnsNames, RequestedIds);
|
||||
|
||||
using RequestedKeys = Table *;
|
||||
/// There is no requested columns names for load keys func
|
||||
/// There are no requested column names for load keys func
|
||||
using LibraryLoadKeysFunc = RawClickHouseLibraryTable (*)(LibraryData, LibrarySettings, RequestedKeys);
|
||||
|
||||
using LibraryIsModifiedFunc = bool (*)(LibraryContext, LibrarySettings);
|
||||
|
@ -383,7 +383,7 @@ void registerDiskLocal(DiskFactory & factory)
|
||||
|
||||
if (Poco::File disk{path}; !disk.canRead() || !disk.canWrite())
|
||||
{
|
||||
throw Exception("There is no RW access to disk " + name + " (" + path + ")", ErrorCodes::PATH_ACCESS_DENIED);
|
||||
throw Exception("There is no RW access to the disk " + name + " (" + path + ")", ErrorCodes::PATH_ACCESS_DENIED);
|
||||
}
|
||||
|
||||
bool has_space_ratio = config.has(config_prefix + ".keep_free_space_ratio");
|
||||
|
@ -65,12 +65,12 @@ public:
|
||||
const auto & re2 = regexp->getRE2();
|
||||
|
||||
if (!re2)
|
||||
throw Exception("There is no groups in regexp: " + needle, ErrorCodes::BAD_ARGUMENTS);
|
||||
throw Exception("There are no groups in regexp: " + needle, ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
const size_t groups_count = re2->NumberOfCapturingGroups();
|
||||
|
||||
if (!groups_count)
|
||||
throw Exception("There is no groups in regexp: " + needle, ErrorCodes::BAD_ARGUMENTS);
|
||||
throw Exception("There are no groups in regexp: " + needle, ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
// Including 0-group, which is the whole regexp.
|
||||
PODArrayWithStackMemory<re2_st::StringPiece, 128> matched_groups(groups_count + 1);
|
||||
|
@ -1220,29 +1220,35 @@ Block Aggregator::prepareBlockAndFill(
|
||||
return res;
|
||||
}
|
||||
|
||||
void Aggregator::fillAggregateColumnsWithSingleKey(
|
||||
AggregatedDataVariants & data_variants,
|
||||
MutableColumns & final_aggregate_columns)
|
||||
void Aggregator::addSingleKeyToAggregateColumns(
|
||||
const AggregatedDataVariants & data_variants,
|
||||
MutableColumns & aggregate_columns) const
|
||||
{
|
||||
AggregatedDataWithoutKey & data = data_variants.without_key;
|
||||
|
||||
const auto & data = data_variants.without_key;
|
||||
for (size_t i = 0; i < params.aggregates_size; ++i)
|
||||
{
|
||||
ColumnAggregateFunction & column_aggregate_func = assert_cast<ColumnAggregateFunction &>(*final_aggregate_columns[i]);
|
||||
for (auto & pool : data_variants.aggregates_pools)
|
||||
{
|
||||
column_aggregate_func.addArena(pool);
|
||||
}
|
||||
auto & column_aggregate_func = assert_cast<ColumnAggregateFunction &>(*aggregate_columns[i]);
|
||||
column_aggregate_func.getData().push_back(data + offsets_of_aggregate_states[i]);
|
||||
}
|
||||
data = nullptr;
|
||||
}
|
||||
|
||||
void Aggregator::addArenasToAggregateColumns(
|
||||
const AggregatedDataVariants & data_variants,
|
||||
MutableColumns & aggregate_columns) const
|
||||
{
|
||||
for (size_t i = 0; i < params.aggregates_size; ++i)
|
||||
{
|
||||
auto & column_aggregate_func = assert_cast<ColumnAggregateFunction &>(*aggregate_columns[i]);
|
||||
for (const auto & pool : data_variants.aggregates_pools)
|
||||
column_aggregate_func.addArena(pool);
|
||||
}
|
||||
}
|
||||
|
||||
void Aggregator::createStatesAndFillKeyColumnsWithSingleKey(
|
||||
AggregatedDataVariants & data_variants,
|
||||
Columns & key_columns,
|
||||
size_t key_row,
|
||||
MutableColumns & final_key_columns)
|
||||
MutableColumns & final_key_columns) const
|
||||
{
|
||||
AggregateDataPtr place = data_variants.aggregates_pool->alignedAlloc(total_size_of_aggregate_states, align_aggregate_states);
|
||||
createAggregateStates(place);
|
||||
|
@ -1295,14 +1295,18 @@ protected:
|
||||
AggregateFunctionInstructions & instructions,
|
||||
NestedColumnsHolder & nested_columns_holder);
|
||||
|
||||
void fillAggregateColumnsWithSingleKey(
|
||||
AggregatedDataVariants & data_variants,
|
||||
MutableColumns & final_aggregate_columns);
|
||||
void addSingleKeyToAggregateColumns(
|
||||
const AggregatedDataVariants & data_variants,
|
||||
MutableColumns & aggregate_columns) const;
|
||||
|
||||
void addArenasToAggregateColumns(
|
||||
const AggregatedDataVariants & data_variants,
|
||||
MutableColumns & aggregate_columns) const;
|
||||
|
||||
void createStatesAndFillKeyColumnsWithSingleKey(
|
||||
AggregatedDataVariants & data_variants,
|
||||
Columns & key_columns, size_t key_row,
|
||||
MutableColumns & final_key_columns);
|
||||
MutableColumns & final_key_columns) const;
|
||||
};
|
||||
|
||||
|
||||
|
@ -863,7 +863,7 @@ void ActionsDAG::compileFunctions(size_t min_count_to_compile_expression)
|
||||
if (!used_in_result.count(frame.node) && cur.all_parents_compilable)
|
||||
should_compile = false;
|
||||
|
||||
/// There is not reason to inline single node.
|
||||
/// There is no reason to inline single node.
|
||||
/// The result of compiling function in isolation is pretty much the same as its `execute` method.
|
||||
if (cur.num_inlineable_nodes <= 1)
|
||||
should_compile = false;
|
||||
|
@ -24,11 +24,13 @@ FinishAggregatingInOrderAlgorithm::FinishAggregatingInOrderAlgorithm(
|
||||
const Block & header_,
|
||||
size_t num_inputs_,
|
||||
AggregatingTransformParamsPtr params_,
|
||||
SortDescription description_)
|
||||
SortDescription description_,
|
||||
size_t max_block_size_)
|
||||
: header(header_)
|
||||
, num_inputs(num_inputs_)
|
||||
, params(params_)
|
||||
, description(std::move(description_))
|
||||
, max_block_size(max_block_size_)
|
||||
{
|
||||
/// Replace column names in description to positions.
|
||||
for (auto & column_description : description)
|
||||
@ -56,6 +58,13 @@ void FinishAggregatingInOrderAlgorithm::consume(Input & input, size_t source_num
|
||||
|
||||
IMergingAlgorithm::Status FinishAggregatingInOrderAlgorithm::merge()
|
||||
{
|
||||
if (!inputs_to_update.empty())
|
||||
{
|
||||
Status status(inputs_to_update.back());
|
||||
inputs_to_update.pop_back();
|
||||
return status;
|
||||
}
|
||||
|
||||
/// Find the input with smallest last row.
|
||||
std::optional<size_t> best_input;
|
||||
for (size_t i = 0; i < num_inputs; ++i)
|
||||
@ -94,16 +103,30 @@ IMergingAlgorithm::Status FinishAggregatingInOrderAlgorithm::merge()
|
||||
states[i].to_row = (it == indices.end() ? states[i].num_rows : *it);
|
||||
}
|
||||
|
||||
Status status(*best_input);
|
||||
status.chunk = aggregate();
|
||||
addToAggregation();
|
||||
|
||||
/// At least one chunk should be fully aggregated.
|
||||
assert(!inputs_to_update.empty());
|
||||
Status status(inputs_to_update.back());
|
||||
inputs_to_update.pop_back();
|
||||
|
||||
/// Do not merge blocks, if there are too few rows.
|
||||
if (accumulated_rows >= max_block_size)
|
||||
status.chunk = aggregate();
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
Chunk FinishAggregatingInOrderAlgorithm::aggregate()
|
||||
{
|
||||
BlocksList blocks;
|
||||
auto aggregated = params->aggregator.mergeBlocks(blocks, false);
|
||||
blocks.clear();
|
||||
accumulated_rows = 0;
|
||||
return {aggregated.getColumns(), aggregated.rows()};
|
||||
}
|
||||
|
||||
void FinishAggregatingInOrderAlgorithm::addToAggregation()
|
||||
{
|
||||
for (size_t i = 0; i < num_inputs; ++i)
|
||||
{
|
||||
const auto & state = states[i];
|
||||
@ -112,7 +135,7 @@ Chunk FinishAggregatingInOrderAlgorithm::aggregate()
|
||||
|
||||
if (state.to_row - state.current_row == state.num_rows)
|
||||
{
|
||||
blocks.emplace_back(header.cloneWithColumns(states[i].all_columns));
|
||||
blocks.emplace_back(header.cloneWithColumns(state.all_columns));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -125,10 +148,11 @@ Chunk FinishAggregatingInOrderAlgorithm::aggregate()
|
||||
}
|
||||
|
||||
states[i].current_row = states[i].to_row;
|
||||
accumulated_rows += blocks.back().rows();
|
||||
|
||||
if (!states[i].isValid())
|
||||
inputs_to_update.push_back(i);
|
||||
}
|
||||
|
||||
auto aggregated = params->aggregator.mergeBlocks(blocks, false);
|
||||
return {aggregated.getColumns(), aggregated.rows()};
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,7 +37,8 @@ public:
|
||||
const Block & header_,
|
||||
size_t num_inputs_,
|
||||
AggregatingTransformParamsPtr params_,
|
||||
SortDescription description_);
|
||||
SortDescription description_,
|
||||
size_t max_block_size_);
|
||||
|
||||
void initialize(Inputs inputs) override;
|
||||
void consume(Input & input, size_t source_num) override;
|
||||
@ -45,6 +46,7 @@ public:
|
||||
|
||||
private:
|
||||
Chunk aggregate();
|
||||
void addToAggregation();
|
||||
|
||||
struct State
|
||||
{
|
||||
@ -66,8 +68,13 @@ private:
|
||||
size_t num_inputs;
|
||||
AggregatingTransformParamsPtr params;
|
||||
SortDescription description;
|
||||
size_t max_block_size;
|
||||
|
||||
Inputs current_inputs;
|
||||
std::vector<State> states;
|
||||
std::vector<size_t> inputs_to_update;
|
||||
BlocksList blocks;
|
||||
size_t accumulated_rows = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -16,13 +16,15 @@ public:
|
||||
const Block & header,
|
||||
size_t num_inputs,
|
||||
AggregatingTransformParamsPtr params,
|
||||
SortDescription description)
|
||||
SortDescription description,
|
||||
size_t max_block_size)
|
||||
: IMergingTransform(
|
||||
num_inputs, header, header, true,
|
||||
header,
|
||||
num_inputs,
|
||||
params,
|
||||
std::move(description))
|
||||
std::move(description),
|
||||
max_block_size)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -100,7 +100,8 @@ void AggregatingStep::transformPipeline(QueryPipeline & pipeline, const BuildQue
|
||||
pipeline.getHeader(),
|
||||
pipeline.getNumStreams(),
|
||||
transform_params,
|
||||
group_by_sort_description);
|
||||
group_by_sort_description,
|
||||
max_block_size);
|
||||
|
||||
pipeline.addTransform(std::move(transform));
|
||||
aggregating_sorted = collector.detachProcessors(1);
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <Processors/Transforms/AggregatingInOrderTransform.h>
|
||||
#include <DataTypes/DataTypeLowCardinality.h>
|
||||
#include <Core/SortCursor.h>
|
||||
#include <ext/range.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -58,6 +59,7 @@ void AggregatingInOrderTransform::consume(Chunk chunk)
|
||||
LOG_TRACE(log, "Aggregating in order");
|
||||
is_consume_started = true;
|
||||
}
|
||||
|
||||
src_rows += rows;
|
||||
src_bytes += chunk.bytes();
|
||||
|
||||
@ -82,58 +84,55 @@ void AggregatingInOrderTransform::consume(Chunk chunk)
|
||||
res_aggregate_columns.resize(params->params.aggregates_size);
|
||||
|
||||
for (size_t i = 0; i < params->params.keys_size; ++i)
|
||||
{
|
||||
res_key_columns[i] = res_header.safeGetByPosition(i).type->createColumn();
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < params->params.aggregates_size; ++i)
|
||||
{
|
||||
res_aggregate_columns[i] = res_header.safeGetByPosition(i + params->params.keys_size).type->createColumn();
|
||||
}
|
||||
|
||||
params->aggregator.createStatesAndFillKeyColumnsWithSingleKey(variants, key_columns, key_begin, res_key_columns);
|
||||
params->aggregator.addArenasToAggregateColumns(variants, res_aggregate_columns);
|
||||
++cur_block_size;
|
||||
}
|
||||
ssize_t mid = 0;
|
||||
ssize_t high = 0;
|
||||
ssize_t low = -1;
|
||||
|
||||
|
||||
/// Will split block into segments with the same key
|
||||
while (key_end != rows)
|
||||
{
|
||||
high = rows;
|
||||
/// Find the first position of new (not current) key in current chunk
|
||||
while (high - low > 1)
|
||||
{
|
||||
mid = (low + high) / 2;
|
||||
if (!less(res_key_columns, key_columns, cur_block_size - 1, mid, group_by_description))
|
||||
low = mid;
|
||||
else
|
||||
high = mid;
|
||||
}
|
||||
key_end = high;
|
||||
auto indices = ext::range(key_begin, rows);
|
||||
auto it = std::upper_bound(indices.begin(), indices.end(), cur_block_size - 1,
|
||||
[&](size_t lhs_row, size_t rhs_row)
|
||||
{
|
||||
return less(res_key_columns, key_columns, lhs_row, rhs_row, group_by_description);
|
||||
});
|
||||
|
||||
key_end = (it == indices.end() ? rows : *it);
|
||||
|
||||
/// Add data to aggr. state if interval is not empty. Empty when haven't found current key in new block.
|
||||
if (key_begin != key_end)
|
||||
{
|
||||
params->aggregator.executeOnIntervalWithoutKeyImpl(variants.without_key, key_begin, key_end, aggregate_function_instructions.data(), variants.aggregates_pool);
|
||||
}
|
||||
|
||||
low = key_begin = key_end;
|
||||
/// We finalize last key aggregation state if a new key found.
|
||||
if (key_begin != rows)
|
||||
if (key_end != rows)
|
||||
{
|
||||
params->aggregator.fillAggregateColumnsWithSingleKey(variants, res_aggregate_columns);
|
||||
params->aggregator.addSingleKeyToAggregateColumns(variants, res_aggregate_columns);
|
||||
|
||||
/// If res_block_size is reached we have to stop consuming and generate the block. Save the extra rows into new chunk.
|
||||
if (cur_block_size == res_block_size)
|
||||
{
|
||||
Columns source_columns = chunk.detachColumns();
|
||||
|
||||
for (auto & source_column : source_columns)
|
||||
source_column = source_column->cut(key_begin, rows - key_begin);
|
||||
source_column = source_column->cut(key_end, rows - key_end);
|
||||
|
||||
current_chunk = Chunk(source_columns, rows - key_begin);
|
||||
current_chunk = Chunk(source_columns, rows - key_end);
|
||||
src_rows -= current_chunk.getNumRows();
|
||||
block_end_reached = true;
|
||||
need_generate = true;
|
||||
cur_block_size = 0;
|
||||
|
||||
variants.without_key = nullptr;
|
||||
|
||||
/// Arenas cannot be destroyed here, since later, in FinalizingSimpleTransform
|
||||
/// there will be finalizeChunk(), but even after
|
||||
/// finalizeChunk() we cannot destroy arena, since some memory
|
||||
@ -155,10 +154,13 @@ void AggregatingInOrderTransform::consume(Chunk chunk)
|
||||
}
|
||||
|
||||
/// We create a new state for the new key and update res_key_columns
|
||||
params->aggregator.createStatesAndFillKeyColumnsWithSingleKey(variants, key_columns, key_begin, res_key_columns);
|
||||
params->aggregator.createStatesAndFillKeyColumnsWithSingleKey(variants, key_columns, key_end, res_key_columns);
|
||||
++cur_block_size;
|
||||
}
|
||||
|
||||
key_begin = key_end;
|
||||
}
|
||||
|
||||
block_end_reached = false;
|
||||
}
|
||||
|
||||
@ -234,7 +236,10 @@ IProcessor::Status AggregatingInOrderTransform::prepare()
|
||||
void AggregatingInOrderTransform::generate()
|
||||
{
|
||||
if (cur_block_size && is_consume_finished)
|
||||
params->aggregator.fillAggregateColumnsWithSingleKey(variants, res_aggregate_columns);
|
||||
{
|
||||
params->aggregator.addSingleKeyToAggregateColumns(variants, res_aggregate_columns);
|
||||
variants.without_key = nullptr;
|
||||
}
|
||||
|
||||
Block res = res_header.cloneEmpty();
|
||||
|
||||
|
@ -609,9 +609,13 @@ void IMergeTreeDataPart::loadIndex()
|
||||
|
||||
size_t marks_count = index_granularity.getMarksCount();
|
||||
|
||||
Serializations serializations(key_size);
|
||||
for (size_t j = 0; j < key_size; ++j)
|
||||
serializations[j] = primary_key.data_types[j]->getDefaultSerialization();
|
||||
|
||||
for (size_t i = 0; i < marks_count; ++i) //-V756
|
||||
for (size_t j = 0; j < key_size; ++j)
|
||||
primary_key.data_types[j]->getDefaultSerialization()->deserializeBinary(*loaded_index[j], *index_file);
|
||||
serializations[j]->deserializeBinary(*loaded_index[j], *index_file);
|
||||
|
||||
for (size_t i = 0; i < key_size; ++i)
|
||||
{
|
||||
|
@ -813,7 +813,7 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks)
|
||||
|
||||
if (part_names_with_disks.empty() && parts_from_wal.empty())
|
||||
{
|
||||
LOG_DEBUG(log, "There is no data parts");
|
||||
LOG_DEBUG(log, "There are no data parts");
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,9 @@
|
||||
<force_sync>false</force_sync>
|
||||
<startup_timeout>120000</startup_timeout>
|
||||
<raft_logs_level>trace</raft_logs_level>
|
||||
<heart_beat_interval_ms>1000</heart_beat_interval_ms>
|
||||
<election_timeout_lower_bound_ms>2000</election_timeout_lower_bound_ms>
|
||||
<election_timeout_upper_bound_ms>4000</election_timeout_upper_bound_ms>
|
||||
<quorum_reads>{quorum_reads}</quorum_reads>
|
||||
<snapshot_distance>{snapshot_distance}</snapshot_distance>
|
||||
<stale_log_gap>{stale_log_gap}</stale_log_gap>
|
||||
|
@ -16,3 +16,5 @@
|
||||
(def coordination-logs-dir (str coordination-data-dir "/logs"))
|
||||
|
||||
(def stderr-file (str logs-dir "/stderr.log"))
|
||||
|
||||
(def binaries-cache-dir (str common-prefix "/binaries"))
|
||||
|
@ -17,9 +17,7 @@
|
||||
|
||||
(defn get-clickhouse-url
|
||||
[url]
|
||||
(let [download-result (cu/wget! url)]
|
||||
(do (c/exec :mv download-result common-prefix)
|
||||
(str common-prefix "/" download-result))))
|
||||
(non-precise-cached-wget! url))
|
||||
|
||||
(defn download-clickhouse
|
||||
[source]
|
||||
@ -49,6 +47,7 @@
|
||||
|
||||
(defn chmod-binary
|
||||
[path]
|
||||
(info "Binary path chmod" path)
|
||||
(c/exec :chmod :+x path))
|
||||
|
||||
(defn install-downloaded-clickhouse
|
||||
@ -90,6 +89,13 @@
|
||||
(c/exec :echo (slurp (io/resource "listen.xml")) :> (str sub-configs-dir "/listen.xml"))
|
||||
(c/exec :echo (cluster-config test node (slurp (io/resource "keeper_config.xml"))) :> (str sub-configs-dir "/keeper_config.xml")))
|
||||
|
||||
(defn collect-traces
|
||||
[test node]
|
||||
(let [pid (c/exec :pidof "clickhouse")]
|
||||
(c/exec :timeout :-s "KILL" "60" :gdb :-ex "set pagination off" :-ex (str "set logging file " logs-dir "/gdb.log") :-ex
|
||||
"set logging on" :-ex "backtrace" :-ex "thread apply all backtrace"
|
||||
:-ex "backtrace" :-ex "detach" :-ex "quit" :--pid pid :|| :true)))
|
||||
|
||||
(defn db
|
||||
[version reuse-binary]
|
||||
(reify db/DB
|
||||
@ -110,19 +116,31 @@
|
||||
|
||||
(teardown! [_ test node]
|
||||
(info node "Tearing down clickhouse")
|
||||
(kill-clickhouse! node test)
|
||||
(c/su
|
||||
(kill-clickhouse! node test)
|
||||
(if (not reuse-binary)
|
||||
(c/exec :rm :-rf binary-path))
|
||||
(c/exec :rm :-rf pid-file-path)
|
||||
(c/exec :rm :-rf data-dir)
|
||||
;(c/exec :rm :-rf logs-dir)
|
||||
(c/exec :rm :-rf logs-dir)
|
||||
(c/exec :rm :-rf configs-dir)))
|
||||
|
||||
db/LogFiles
|
||||
(log-files [_ test node]
|
||||
(c/su
|
||||
(if (cu/exists? pid-file-path)
|
||||
(do
|
||||
(info node "Collecting traces")
|
||||
(collect-traces test node))
|
||||
(info node "Pid files doesn't exists"))
|
||||
(kill-clickhouse! node test)
|
||||
(c/cd data-dir
|
||||
(c/exec :tar :czf "coordination.tar.gz" "coordination")))
|
||||
[stderr-file (str logs-dir "/clickhouse-server.log") (str data-dir "/coordination.tar.gz")])))
|
||||
(if (cu/exists? coordination-data-dir)
|
||||
(do
|
||||
(info node "Coordination files exists, going to compress")
|
||||
(c/cd data-dir
|
||||
(c/exec :tar :czf "coordination.tar.gz" "coordination")))))
|
||||
(let [common-logs [stderr-file (str logs-dir "/clickhouse-server.log") (str data-dir "/coordination.tar.gz")]
|
||||
gdb-log (str logs-dir "/gdb.log")]
|
||||
(if (cu/exists? (str logs-dir "/gdb.log"))
|
||||
(conj common-logs gdb-log)
|
||||
common-logs)))))
|
||||
|
@ -18,7 +18,8 @@
|
||||
:nodename node))
|
||||
|
||||
(setup! [this test]
|
||||
(zk-create-if-not-exists conn k "#{}"))
|
||||
(exec-with-retries 30 (fn []
|
||||
(zk-create-if-not-exists conn k "#{}"))))
|
||||
|
||||
(invoke! [this test op]
|
||||
(case (:f op)
|
||||
|
@ -6,11 +6,24 @@
|
||||
[jepsen.control.util :as cu]
|
||||
[jepsen.clickhouse-keeper.constants :refer :all]
|
||||
[jepsen.control :as c]
|
||||
[clojure.tools.logging :refer :all])
|
||||
[clojure.tools.logging :refer :all]
|
||||
[clojure.java.io :as io])
|
||||
(:import (org.apache.zookeeper.data Stat)
|
||||
(org.apache.zookeeper CreateMode
|
||||
ZooKeeper)
|
||||
(org.apache.zookeeper ZooKeeper KeeperException KeeperException$BadVersionException)))
|
||||
(org.apache.zookeeper ZooKeeper KeeperException KeeperException$BadVersionException)
|
||||
(java.security MessageDigest)))
|
||||
|
||||
(defn exec-with-retries
|
||||
[retries f & args]
|
||||
(let [res (try {:value (apply f args)}
|
||||
(catch Exception e
|
||||
(if (zero? retries)
|
||||
(throw e)
|
||||
{:exception e})))]
|
||||
(if (:exception res)
|
||||
(do (Thread/sleep 1000) (recur (dec retries) f args))
|
||||
(:value res))))
|
||||
|
||||
(defn parse-long
|
||||
"Parses a string to a Long. Passes through `nil` and empty strings."
|
||||
@ -32,7 +45,7 @@
|
||||
|
||||
(defn zk-connect
|
||||
[host port timeout]
|
||||
(zk/connect (str host ":" port) :timeout-msec timeout))
|
||||
(exec-with-retries 15 (fn [] (zk/connect (str host ":" port) :timeout-msec timeout))))
|
||||
|
||||
(defn zk-create-range
|
||||
[conn n]
|
||||
@ -168,13 +181,23 @@
|
||||
:--keeper_server.logs_storage_path coordination-logs-dir)
|
||||
(wait-clickhouse-alive! node test)))
|
||||
|
||||
(defn exec-with-retries
|
||||
[retries f & args]
|
||||
(let [res (try {:value (apply f args)}
|
||||
(catch Exception e
|
||||
(if (zero? retries)
|
||||
(throw e)
|
||||
{:exception e})))]
|
||||
(if (:exception res)
|
||||
(do (Thread/sleep 1000) (recur (dec retries) f args))
|
||||
(:value res))))
|
||||
(defn md5 [^String s]
|
||||
(let [algorithm (MessageDigest/getInstance "MD5")
|
||||
raw (.digest algorithm (.getBytes s))]
|
||||
(format "%032x" (BigInteger. 1 raw))))
|
||||
|
||||
(defn non-precise-cached-wget!
|
||||
[url]
|
||||
(let [encoded-url (md5 url)
|
||||
expected-file-name (.getName (io/file url))
|
||||
dest-file (str binaries-cache-dir "/" encoded-url)
|
||||
dest-symlink (str common-prefix "/" expected-file-name)
|
||||
wget-opts (concat cu/std-wget-opts [:-O dest-file])]
|
||||
(when-not (cu/exists? dest-file)
|
||||
(info "Downloading" url)
|
||||
(do (c/exec :mkdir :-p binaries-cache-dir)
|
||||
(c/cd binaries-cache-dir
|
||||
(cu/wget-helper! wget-opts url))))
|
||||
(c/exec :rm :-rf dest-symlink)
|
||||
(c/exec :ln :-s dest-file dest-symlink)
|
||||
dest-symlink))
|
||||
|
@ -58,7 +58,7 @@
|
||||
SELECT dictGet('default.simple_key_flat_dictionary', {column_name}, number)
|
||||
FROM system.numbers
|
||||
LIMIT {elements_count}
|
||||
FORMAR Null;
|
||||
FORMAT Null;
|
||||
</query>
|
||||
|
||||
<query>
|
||||
|
@ -20,51 +20,51 @@
|
||||
170.21 MiB 170.21 MiB 170.21 MiB
|
||||
462.69 MiB 462.69 MiB 462.69 MiB
|
||||
1.23 GiB 1.23 GiB 1.23 GiB
|
||||
3.34 GiB 3.34 GiB -2.00 GiB
|
||||
9.08 GiB 9.08 GiB -2.00 GiB
|
||||
24.67 GiB 24.67 GiB -2.00 GiB
|
||||
67.06 GiB 67.06 GiB -2.00 GiB
|
||||
182.29 GiB 182.29 GiB -2.00 GiB
|
||||
495.51 GiB 495.51 GiB -2.00 GiB
|
||||
1.32 TiB 1.32 TiB -2.00 GiB
|
||||
3.58 TiB 3.58 TiB -2.00 GiB
|
||||
9.72 TiB 9.72 TiB -2.00 GiB
|
||||
26.42 TiB 26.42 TiB -2.00 GiB
|
||||
71.82 TiB 71.82 TiB -2.00 GiB
|
||||
195.22 TiB 195.22 TiB -2.00 GiB
|
||||
530.66 TiB 530.66 TiB -2.00 GiB
|
||||
1.41 PiB 1.41 PiB -2.00 GiB
|
||||
3.83 PiB 3.83 PiB -2.00 GiB
|
||||
10.41 PiB 10.41 PiB -2.00 GiB
|
||||
28.29 PiB 28.29 PiB -2.00 GiB
|
||||
76.91 PiB 76.91 PiB -2.00 GiB
|
||||
209.06 PiB 209.06 PiB -2.00 GiB
|
||||
568.30 PiB 568.30 PiB -2.00 GiB
|
||||
1.51 EiB 1.51 EiB -2.00 GiB
|
||||
4.10 EiB 4.10 EiB -2.00 GiB
|
||||
11.15 EiB 11.15 EiB -2.00 GiB
|
||||
30.30 EiB 0.00 B -2.00 GiB
|
||||
82.37 EiB 0.00 B -2.00 GiB
|
||||
223.89 EiB 0.00 B -2.00 GiB
|
||||
608.60 EiB 0.00 B -2.00 GiB
|
||||
1.62 ZiB 0.00 B -2.00 GiB
|
||||
4.39 ZiB 0.00 B -2.00 GiB
|
||||
11.94 ZiB 0.00 B -2.00 GiB
|
||||
32.45 ZiB 0.00 B -2.00 GiB
|
||||
88.21 ZiB 0.00 B -2.00 GiB
|
||||
239.77 ZiB 0.00 B -2.00 GiB
|
||||
651.77 ZiB 0.00 B -2.00 GiB
|
||||
1.73 YiB 0.00 B -2.00 GiB
|
||||
4.70 YiB 0.00 B -2.00 GiB
|
||||
12.78 YiB 0.00 B -2.00 GiB
|
||||
34.75 YiB 0.00 B -2.00 GiB
|
||||
94.46 YiB 0.00 B -2.00 GiB
|
||||
256.78 YiB 0.00 B -2.00 GiB
|
||||
698.00 YiB 0.00 B -2.00 GiB
|
||||
1897.37 YiB 0.00 B -2.00 GiB
|
||||
5157.59 YiB 0.00 B -2.00 GiB
|
||||
14019.80 YiB 0.00 B -2.00 GiB
|
||||
38109.75 YiB 0.00 B -2.00 GiB
|
||||
103593.05 YiB 0.00 B -2.00 GiB
|
||||
281595.11 YiB 0.00 B -2.00 GiB
|
||||
765454.88 YiB 0.00 B -2.00 GiB
|
||||
3.34 GiB 3.34 GiB 2.00 GiB
|
||||
9.08 GiB 9.08 GiB 2.00 GiB
|
||||
24.67 GiB 24.67 GiB 2.00 GiB
|
||||
67.06 GiB 67.06 GiB 2.00 GiB
|
||||
182.29 GiB 182.29 GiB 2.00 GiB
|
||||
495.51 GiB 495.51 GiB 2.00 GiB
|
||||
1.32 TiB 1.32 TiB 2.00 GiB
|
||||
3.58 TiB 3.58 TiB 2.00 GiB
|
||||
9.72 TiB 9.72 TiB 2.00 GiB
|
||||
26.42 TiB 26.42 TiB 2.00 GiB
|
||||
71.82 TiB 71.82 TiB 2.00 GiB
|
||||
195.22 TiB 195.22 TiB 2.00 GiB
|
||||
530.66 TiB 530.66 TiB 2.00 GiB
|
||||
1.41 PiB 1.41 PiB 2.00 GiB
|
||||
3.83 PiB 3.83 PiB 2.00 GiB
|
||||
10.41 PiB 10.41 PiB 2.00 GiB
|
||||
28.29 PiB 28.29 PiB 2.00 GiB
|
||||
76.91 PiB 76.91 PiB 2.00 GiB
|
||||
209.06 PiB 209.06 PiB 2.00 GiB
|
||||
568.30 PiB 568.30 PiB 2.00 GiB
|
||||
1.51 EiB 1.51 EiB 2.00 GiB
|
||||
4.10 EiB 4.10 EiB 2.00 GiB
|
||||
11.15 EiB 11.15 EiB 2.00 GiB
|
||||
30.30 EiB 16.00 EiB 2.00 GiB
|
||||
82.37 EiB 16.00 EiB 2.00 GiB
|
||||
223.89 EiB 16.00 EiB 2.00 GiB
|
||||
608.60 EiB 16.00 EiB 2.00 GiB
|
||||
1.62 ZiB 16.00 EiB 2.00 GiB
|
||||
4.39 ZiB 16.00 EiB 2.00 GiB
|
||||
11.94 ZiB 16.00 EiB 2.00 GiB
|
||||
32.45 ZiB 16.00 EiB 2.00 GiB
|
||||
88.21 ZiB 16.00 EiB 2.00 GiB
|
||||
239.77 ZiB 16.00 EiB 2.00 GiB
|
||||
651.77 ZiB 16.00 EiB 2.00 GiB
|
||||
1.73 YiB 16.00 EiB 2.00 GiB
|
||||
4.70 YiB 16.00 EiB 2.00 GiB
|
||||
12.78 YiB 16.00 EiB 2.00 GiB
|
||||
34.75 YiB 16.00 EiB 2.00 GiB
|
||||
94.46 YiB 16.00 EiB 2.00 GiB
|
||||
256.78 YiB 16.00 EiB 2.00 GiB
|
||||
698.00 YiB 16.00 EiB 2.00 GiB
|
||||
1897.37 YiB 16.00 EiB 2.00 GiB
|
||||
5157.59 YiB 16.00 EiB 2.00 GiB
|
||||
14019.80 YiB 16.00 EiB 2.00 GiB
|
||||
38109.75 YiB 16.00 EiB 2.00 GiB
|
||||
103593.05 YiB 16.00 EiB 2.00 GiB
|
||||
281595.11 YiB 16.00 EiB 2.00 GiB
|
||||
765454.88 YiB 16.00 EiB 2.00 GiB
|
||||
|
@ -1,4 +1,4 @@
|
||||
WITH round(exp(number), 6) AS x, toUInt64(x) AS y, toInt32(x) AS z
|
||||
WITH round(exp(number), 6) AS x, x > 0xFFFFFFFFFFFFFFFF ? 0xFFFFFFFFFFFFFFFF : toUInt64(x) AS y, x > 0x7FFFFFFF ? 0x7FFFFFFF : toInt32(x) AS z
|
||||
SELECT formatReadableSize(x), formatReadableSize(y), formatReadableSize(z)
|
||||
FROM system.numbers
|
||||
LIMIT 70;
|
||||
|
@ -3,5 +3,3 @@
|
||||
a
|
||||
a
|
||||
---
|
||||
a
|
||||
a
|
||||
|
@ -43,7 +43,7 @@ SELECT * FROM d;
|
||||
SELECT '---';
|
||||
|
||||
INSERT INTO m VALUES ('b');
|
||||
SELECT v FROM d ORDER BY v; -- { clientError 36 }
|
||||
SELECT toString(v) FROM (SELECT v FROM d ORDER BY v) FORMAT Null; -- { serverError 36 }
|
||||
|
||||
|
||||
DROP TABLE m;
|
||||
|
@ -0,0 +1,3 @@
|
||||
1 1
|
||||
2 2
|
||||
3 3
|
@ -0,0 +1,53 @@
|
||||
DROP DATABASE IF EXISTS 01780_db;
|
||||
CREATE DATABASE 01780_db;
|
||||
|
||||
DROP DICTIONARY IF EXISTS dict1;
|
||||
CREATE DICTIONARY dict1
|
||||
(
|
||||
id UInt64,
|
||||
value String
|
||||
)
|
||||
PRIMARY KEY id
|
||||
SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 TABLE 'dict1'))
|
||||
LAYOUT(DIRECT());
|
||||
|
||||
SELECT * FROM dict1; --{serverError 36}
|
||||
|
||||
DROP DICTIONARY dict1;
|
||||
|
||||
DROP DICTIONARY IF EXISTS dict2;
|
||||
CREATE DICTIONARY 01780_db.dict2
|
||||
(
|
||||
id UInt64,
|
||||
value String
|
||||
)
|
||||
PRIMARY KEY id
|
||||
SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 DATABASE '01780_db' TABLE 'dict2'))
|
||||
LAYOUT(DIRECT());
|
||||
|
||||
SELECT * FROM 01780_db.dict2; --{serverError 36}
|
||||
DROP DICTIONARY 01780_db.dict2;
|
||||
|
||||
DROP TABLE IF EXISTS 01780_db.dict3_source;
|
||||
CREATE TABLE 01780_db.dict3_source
|
||||
(
|
||||
id UInt64,
|
||||
value String
|
||||
) ENGINE = TinyLog;
|
||||
|
||||
INSERT INTO 01780_db.dict3_source VALUES (1, '1'), (2, '2'), (3, '3');
|
||||
|
||||
CREATE DICTIONARY 01780_db.dict3
|
||||
(
|
||||
id UInt64,
|
||||
value String
|
||||
)
|
||||
PRIMARY KEY id
|
||||
SOURCE(CLICKHOUSE(HOST 'localhost' PORT 9000 TABLE 'dict3_source' DATABASE '01780_db'))
|
||||
LAYOUT(DIRECT());
|
||||
|
||||
SELECT * FROM 01780_db.dict3;
|
||||
|
||||
DROP DICTIONARY 01780_db.dict3;
|
||||
|
||||
DROP DATABASE 01780_db;
|
@ -0,0 +1,11 @@
|
||||
94950
|
||||
84950
|
||||
74950
|
||||
64950
|
||||
54950
|
||||
=======
|
||||
94950
|
||||
84950
|
||||
74950
|
||||
64950
|
||||
54950
|
16
tests/queries/0_stateless/01786_group_by_pk_many_streams.sql
Normal file
16
tests/queries/0_stateless/01786_group_by_pk_many_streams.sql
Normal file
@ -0,0 +1,16 @@
|
||||
DROP TABLE IF EXISTS group_by_pk;
|
||||
|
||||
CREATE TABLE group_by_pk (k UInt64, v UInt64)
|
||||
ENGINE = MergeTree ORDER BY k PARTITION BY v % 50;
|
||||
|
||||
INSERT INTO group_by_pk SELECT number / 100, number FROM numbers(1000);
|
||||
|
||||
SELECT sum(v) AS s FROM group_by_pk GROUP BY k ORDER BY s DESC LIMIT 5
|
||||
SETTINGS optimize_aggregation_in_order = 1, max_block_size = 1;
|
||||
|
||||
SELECT '=======';
|
||||
|
||||
SELECT sum(v) AS s FROM group_by_pk GROUP BY k ORDER BY s DESC LIMIT 5
|
||||
SETTINGS optimize_aggregation_in_order = 0, max_block_size = 1;
|
||||
|
||||
DROP TABLE IF EXISTS group_by_pk;
|
@ -0,0 +1 @@
|
||||
1
|
@ -0,0 +1 @@
|
||||
SELECT 1 GROUP BY emptyArrayToSingle(arrayFilter(x -> 1, []));
|
@ -695,6 +695,7 @@
|
||||
"01685_ssd_cache_dictionary_complex_key",
|
||||
"01760_system_dictionaries",
|
||||
"01760_polygon_dictionaries",
|
||||
"01778_hierarchical_dictionaries"
|
||||
"01778_hierarchical_dictionaries",
|
||||
"01780_clickhouse_dictionary_source_loop"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user