From d547db3e321104af8b6f639782b06889f592c37f Mon Sep 17 00:00:00 2001 From: "Chun-Sheng, Li" Date: Wed, 5 Jan 2022 11:45:47 +0800 Subject: [PATCH 01/57] Adding /replicas_status request --- docs/en/interfaces/http.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/en/interfaces/http.md b/docs/en/interfaces/http.md index f8f6f26d208..9a4c8bc4408 100644 --- a/docs/en/interfaces/http.md +++ b/docs/en/interfaces/http.md @@ -23,11 +23,13 @@ Web UI can be accessed here: `http://localhost:8123/play`. ![Web UI](../images/play.png) -In health-check scripts use `GET /ping` request. This handler always returns “Ok.” (with a line feed at the end). Available from version 18.12.13. +In health-check scripts use `GET /ping` or `GET /replicas_status` request. This handler always returns “Ok.” (with a line feed at the end). Available from version 18.12.13. ``` bash $ curl 'http://localhost:8123/ping' Ok. +$ curl 'http://localhost:8123/replicas_status' +Ok. ``` Send the request as a URL ‘query’ parameter, or as a POST. Or send the beginning of the query in the ‘query’ parameter, and the rest in the POST (we’ll explain later why this is necessary). The size of the URL is limited to 16 KB, so keep this in mind when sending large queries. From e4c03bbc97fe1db431c9da38ac83e4f6a3d10038 Mon Sep 17 00:00:00 2001 From: "Chun-Sheng, Li" Date: Thu, 6 Jan 2022 09:45:27 +0800 Subject: [PATCH 02/57] Changing message for the replicas_status request --- docs/en/interfaces/http.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/interfaces/http.md b/docs/en/interfaces/http.md index 9a4c8bc4408..8c0c5230818 100644 --- a/docs/en/interfaces/http.md +++ b/docs/en/interfaces/http.md @@ -23,7 +23,7 @@ Web UI can be accessed here: `http://localhost:8123/play`. ![Web UI](../images/play.png) -In health-check scripts use `GET /ping` or `GET /replicas_status` request. This handler always returns “Ok.” (with a line feed at the end). Available from version 18.12.13. +In health-check scripts use `GET /ping` request. This handler always returns “Ok.” (with a line feed at the end). Available from version 18.12.13. If users want to get more sophisticated health checks, it can use the `GET /replicas_status` request. ``` bash $ curl 'http://localhost:8123/ping' From e3b4cef072cc2d429b844ae96995a090955d7923 Mon Sep 17 00:00:00 2001 From: Tiaonmmn Date: Tue, 25 Jan 2022 20:38:15 +0800 Subject: [PATCH 03/57] Update requirements.md --- docs/zh/operations/requirements.md | 44 +++++++++++++++--------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/zh/operations/requirements.md b/docs/zh/operations/requirements.md index c3013f738a2..964d7aa34f4 100644 --- a/docs/zh/operations/requirements.md +++ b/docs/zh/operations/requirements.md @@ -1,59 +1,59 @@ --- toc_priority: 44 -toc_title: "要求" +toc_title: "必备条件" --- -# 要求 {#requirements} +# 必备条件 {#requirements} ## CPU {#cpu} -对于从预构建的deb包进行安装,请使用具有x86_64架构并支持SSE4.2指令的CPU。 要使用不支持SSE4.2或具有AArch64或PowerPC64LE体系结构的处理器运行ClickHouse,您应该从源代码构建ClickHouse。 +如果您使用预编译的DEB/RPM包安装ClickHouse,请使用支持SSE4.2指令集的x86_64架构的CPU。如果需要在不支持SSE4.2指令集的CPU上,或者在AArch64(ARM)和PowerPC64LE(IBM Power)架构上运行ClickHouse,您应该从源码编译ClickHouse。 -ClickHouse实现并行数据处理并使用所有可用的硬件资源。 在选择处理器时,考虑到ClickHouse在具有大量内核但时钟速率较低的配置中的工作效率要高于具有较少内核和较高时钟速率的配置。 例如,具有2600MHz的16核心优于具有3600MHz的8核心。 +ClickHouse实现了并行数据处理,处理时会使用所有的可用资源。在选择处理器时,请注意:ClickHouse在具有大量计算核、时钟频率稍低的平台上比计算核少、时钟频率高的平台上效率更高。例如,ClickHouse在16核 2.6GHz的CPU上运行速度高于8核 3.6GHz的CPU。 -建议使用 **睿频加速** 和 **超线程** 技术。 它显着提高了典型工作负载的性能。 +建议使用 **睿频加速** 和 **超线程** 技术。 它显着提高了正常工作负载的性能。 ## RAM {#ram} -我们建议使用至少4GB的RAM来执行重要的查询。 ClickHouse服务器可以使用少得多的RAM运行,但它需要处理查询的内存。 +我们建议使用至少4GB的内存来执行重要的查询。 ClickHouse服务器可以使用很少的内存运行,但它需要一定量的内存用于处理查询。 -RAM所需的体积取决于: +ClickHouse所需内存取决于: -- 查询的复杂性。 -- 查询中处理的数据量。 +- 查询的复杂程度。 +- 查询处理的数据量。 -要计算所需的RAM体积,您应该估计临时数据的大小 [GROUP BY](../sql-reference/statements/select/group-by.md#select-group-by-clause), [DISTINCT](../sql-reference/statements/select/distinct.md#select-distinct), [JOIN](../sql-reference/statements/select/join.md#select-join) 和您使用的其他操作。 +要计算所需的内存大小,您应该考虑用于[GROUP BY](../sql-reference/statements/select/group-by.md#select-group-by-clause)、[DISTINCT](../sql-reference/statements/select/distinct.md#select-distinct)、[JOIN](../sql-reference/statements/select/join.md#select-join) 和其他操作所需的临时数据量。 -ClickHouse可以使用外部存储器来存储临时数据。看 [在外部存储器中分组](../sql-reference/statements/select/group-by.md#select-group-by-in-external-memory) 有关详细信息。 +ClickHouse可以使用外部存储器来存储临时数据。详情请见[在外部存储器中分组](../sql-reference/statements/select/group-by.md#select-group-by-in-external-memory)。 ## 交换文件 {#swap-file} -禁用生产环境的交换文件。 +请在生产环境禁用交换文件。 ## 存储子系统 {#storage-subsystem} 您需要有2GB的可用磁盘空间来安装ClickHouse。 -数据所需的存储量应单独计算。 评估应包括: +数据所需的存储空间应单独计算。预估存储容量时请考虑: -- 估计数据量。 +- 数据量 - 您可以采取数据的样本并从中获取行的平均大小。 然后将该值乘以计划存储的行数。 + 您可以对数据进行采样并计算每行的平均占用空间。然后将该值乘以计划存储的行数。 -- 数据压缩系数。 +- 数据压缩比 - 要估计数据压缩系数,请将数据的样本加载到ClickHouse中,并将数据的实际大小与存储的表的大小进行比较。 例如,点击流数据通常被压缩6-10倍。 + 要计算数据压缩比,请将样本数据写入ClickHouse,并将原始数据大小与ClickHouse实际存储的数据进行比较。例如,用户点击行为的原始数据压缩比通常为6-10。 -要计算要存储的最终数据量,请将压缩系数应用于估计的数据量。 如果计划将数据存储在多个副本中,则将估计的量乘以副本数。 +请将原始数据的大小除以压缩比来获得实际所需存储的大小。如果您打算将数据存放于几个副本中,请将存储容量乘上副本数。 ## 网络 {#network} -如果可能的话,使用10G或更高级别的网络。 +如果可能的话,请使用10G或更高级别的网络。 -网络带宽对于处理具有大量中间结果数据的分布式查询至关重要。 此外,网络速度会影响复制过程。 +网络带宽对于处理具有大量中间结果数据的分布式查询至关重要。此外,网络速度会影响复制过程。 ## 软件 {#software} -ClickHouse主要是为Linux系列操作系统开发的。 推荐的Linux发行版是Ubuntu。 `tzdata` 软件包应安装在系统中。 +ClickHouse主要是为Linux系列操作系统开发的。推荐的Linux发行版是Ubuntu。您需要检查`tzdata`(对于Ubuntu)软件包是否在安装ClickHouse之前已经安装。 -ClickHouse也可以在其他操作系统系列中工作。 查看详细信息 [开始](../getting-started/index.md) 文档的部分。 +ClickHouse也可以在其他操作系统系列中工作。详情请查看[开始](../getting-started/index.md)。 From be55296e06d5d557ab9fae136437f8acfa229f1f Mon Sep 17 00:00:00 2001 From: cnmade Date: Thu, 27 Jan 2022 14:54:12 +0800 Subject: [PATCH 04/57] Translate zh/sql-reference/statements/use: remove old file --- docs/zh/sql-reference/statements/{use.md => use.md.bak} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/zh/sql-reference/statements/{use.md => use.md.bak} (100%) diff --git a/docs/zh/sql-reference/statements/use.md b/docs/zh/sql-reference/statements/use.md.bak similarity index 100% rename from docs/zh/sql-reference/statements/use.md rename to docs/zh/sql-reference/statements/use.md.bak From 6621618d16eedc9d9ddb96096fe01a9db4b0ef9e Mon Sep 17 00:00:00 2001 From: cnmade Date: Thu, 27 Jan 2022 14:55:28 +0800 Subject: [PATCH 05/57] Translate zh/sql-reference/statements/use: add translate --- docs/zh/sql-reference/statements/use.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 docs/zh/sql-reference/statements/use.md diff --git a/docs/zh/sql-reference/statements/use.md b/docs/zh/sql-reference/statements/use.md new file mode 100644 index 00000000000..41cba58bb9d --- /dev/null +++ b/docs/zh/sql-reference/statements/use.md @@ -0,0 +1,16 @@ +--- +toc_priority: 53 +toc_title: USE +--- + +# USE 语句 {#use} + +``` sql +USE db +``` + +用于设置会话的当前数据库。 + +如果查询语句中没有在表名前面以加点的方式指明数据库名, 则用当前数据库进行搜索。 + +使用 HTTP 协议时无法进行此查询,因为没有会话的概念。 From 9f7799637c5daa686f2591908f8c478c84e89958 Mon Sep 17 00:00:00 2001 From: cnmade Date: Thu, 27 Jan 2022 14:57:59 +0800 Subject: [PATCH 06/57] Translate zh/sql-reference/statements/use: fix en translate , remove bakup file --- docs/en/sql-reference/statements/use.md | 8 ++++---- docs/zh/sql-reference/statements/use.md.bak | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) delete mode 120000 docs/zh/sql-reference/statements/use.md.bak diff --git a/docs/en/sql-reference/statements/use.md b/docs/en/sql-reference/statements/use.md index 41cba58bb9d..841c23d333d 100644 --- a/docs/en/sql-reference/statements/use.md +++ b/docs/en/sql-reference/statements/use.md @@ -3,14 +3,14 @@ toc_priority: 53 toc_title: USE --- -# USE 语句 {#use} +# USE Statement {#use} ``` sql USE db ``` -用于设置会话的当前数据库。 +Lets you set the current database for the session. -如果查询语句中没有在表名前面以加点的方式指明数据库名, 则用当前数据库进行搜索。 +The current database is used for searching for tables if the database is not explicitly defined in the query with a dot before the table name. -使用 HTTP 协议时无法进行此查询,因为没有会话的概念。 +This query can’t be made when using the HTTP protocol, since there is no concept of a session. diff --git a/docs/zh/sql-reference/statements/use.md.bak b/docs/zh/sql-reference/statements/use.md.bak deleted file mode 120000 index 7bdbf049326..00000000000 --- a/docs/zh/sql-reference/statements/use.md.bak +++ /dev/null @@ -1 +0,0 @@ -../../../en/sql-reference/statements/use.md \ No newline at end of file From 963e3daba1711487685988282e3ba092b9554fc9 Mon Sep 17 00:00:00 2001 From: cnmade Date: Thu, 27 Jan 2022 15:30:59 +0800 Subject: [PATCH 07/57] Fixed zh...statements/alter: rename old file --- docs/zh/sql-reference/statements/alter/{index.md => index.md.bak} | 0 .../statements/alter/index/{index.md => index.md.bak} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename docs/zh/sql-reference/statements/alter/{index.md => index.md.bak} (100%) rename docs/zh/sql-reference/statements/alter/index/{index.md => index.md.bak} (100%) diff --git a/docs/zh/sql-reference/statements/alter/index.md b/docs/zh/sql-reference/statements/alter/index.md.bak similarity index 100% rename from docs/zh/sql-reference/statements/alter/index.md rename to docs/zh/sql-reference/statements/alter/index.md.bak diff --git a/docs/zh/sql-reference/statements/alter/index/index.md b/docs/zh/sql-reference/statements/alter/index/index.md.bak similarity index 100% rename from docs/zh/sql-reference/statements/alter/index/index.md rename to docs/zh/sql-reference/statements/alter/index/index.md.bak From cd13f9a5b33ca5b358fa2576784221792f5258d2 Mon Sep 17 00:00:00 2001 From: cnmade Date: Thu, 27 Jan 2022 15:36:49 +0800 Subject: [PATCH 08/57] Fixed zh...statements/alter: reimport files --- .../sql-reference/statements/alter/index.md | 60 +++++++++++++++++++ .../statements/alter/index/index.md | 23 +++++++ 2 files changed, 83 insertions(+) create mode 100644 docs/zh/sql-reference/statements/alter/index.md create mode 100644 docs/zh/sql-reference/statements/alter/index/index.md diff --git a/docs/zh/sql-reference/statements/alter/index.md b/docs/zh/sql-reference/statements/alter/index.md new file mode 100644 index 00000000000..0d5909518ed --- /dev/null +++ b/docs/zh/sql-reference/statements/alter/index.md @@ -0,0 +1,60 @@ +--- +toc_priority: 35 +toc_title: ALTER +--- + +## ALTER {#query_language_queries_alter} + +Most `ALTER TABLE` queries modify table settings or data: + +- [COLUMN](../../../sql-reference/statements/alter/column.md) +- [PARTITION](../../../sql-reference/statements/alter/partition.md) +- [DELETE](../../../sql-reference/statements/alter/delete.md) +- [UPDATE](../../../sql-reference/statements/alter/update.md) +- [ORDER BY](../../../sql-reference/statements/alter/order-by.md) +- [INDEX](../../../sql-reference/statements/alter/index/index.md) +- [CONSTRAINT](../../../sql-reference/statements/alter/constraint.md) +- [TTL](../../../sql-reference/statements/alter/ttl.md) + +!!! note "Note" + Most `ALTER TABLE` queries are supported only for [\*MergeTree](../../../engines/table-engines/mergetree-family/index.md) tables, as well as [Merge](../../../engines/table-engines/special/merge.md) and [Distributed](../../../engines/table-engines/special/distributed.md). + +These `ALTER` statements manipulate views: + +- [ALTER TABLE ... MODIFY QUERY](../../../sql-reference/statements/alter/view.md) — Modifies a [Materialized view](../create/view.md#materialized) structure. +- [ALTER LIVE VIEW](../../../sql-reference/statements/alter/view.md#alter-live-view) — Refreshes a [Live view](../create/view.md#live-view). + +These `ALTER` statements modify entities related to role-based access control: + +- [USER](../../../sql-reference/statements/alter/user.md) +- [ROLE](../../../sql-reference/statements/alter/role.md) +- [QUOTA](../../../sql-reference/statements/alter/quota.md) +- [ROW POLICY](../../../sql-reference/statements/alter/row-policy.md) +- [SETTINGS PROFILE](../../../sql-reference/statements/alter/settings-profile.md) + +[ALTER TABLE ... MODIFY COMMENT](../../../sql-reference/statements/alter/comment.md) statement adds, modifies, or removes comments to the table, regardless if it was set before or not. + +## Mutations {#mutations} + +`ALTER` queries that are intended to manipulate table data are implemented with a mechanism called “mutations”, most notably [ALTER TABLE … DELETE](../../../sql-reference/statements/alter/delete.md) and [ALTER TABLE … UPDATE](../../../sql-reference/statements/alter/update.md). They are asynchronous background processes similar to merges in [MergeTree](../../../engines/table-engines/mergetree-family/index.md) tables that to produce new “mutated” versions of parts. + +For `*MergeTree` tables mutations execute by **rewriting whole data parts**. There is no atomicity - parts are substituted for mutated parts as soon as they are ready and a `SELECT` query that started executing during a mutation will see data from parts that have already been mutated along with data from parts that have not been mutated yet. + +Mutations are totally ordered by their creation order and are applied to each part in that order. Mutations are also partially ordered with `INSERT INTO` queries: data that was inserted into the table before the mutation was submitted will be mutated and data that was inserted after that will not be mutated. Note that mutations do not block inserts in any way. + +A mutation query returns immediately after the mutation entry is added (in case of replicated tables to ZooKeeper, for non-replicated tables - to the filesystem). The mutation itself executes asynchronously using the system profile settings. To track the progress of mutations you can use the [`system.mutations`](../../../operations/system-tables/mutations.md#system_tables-mutations) table. A mutation that was successfully submitted will continue to execute even if ClickHouse servers are restarted. There is no way to roll back the mutation once it is submitted, but if the mutation is stuck for some reason it can be cancelled with the [`KILL MUTATION`](../../../sql-reference/statements/misc.md#kill-mutation) query. + +Entries for finished mutations are not deleted right away (the number of preserved entries is determined by the `finished_mutations_to_keep` storage engine parameter). Older mutation entries are deleted. + +## Synchronicity of ALTER Queries {#synchronicity-of-alter-queries} + +For non-replicated tables, all `ALTER` queries are performed synchronously. For replicated tables, the query just adds instructions for the appropriate actions to `ZooKeeper`, and the actions themselves are performed as soon as possible. However, the query can wait for these actions to be completed on all the replicas. + +For all `ALTER` queries, you can use the [replication_alter_partitions_sync](../../../operations/settings/settings.md#replication-alter-partitions-sync) setting to set up waiting. + +You can specify how long (in seconds) to wait for inactive replicas to execute all `ALTER` queries with the [replication_wait_for_inactive_replica_timeout](../../../operations/settings/settings.md#replication-wait-for-inactive-replica-timeout) setting. + +!!! info "Note" + For all `ALTER` queries, if `replication_alter_partitions_sync = 2` and some replicas are not active for more than the time, specified in the `replication_wait_for_inactive_replica_timeout` setting, then an exception `UNFINISHED` is thrown. + +For `ALTER TABLE ... UPDATE|DELETE` queries the synchronicity is defined by the [mutations_sync](../../../operations/settings/settings.md#mutations_sync) setting. diff --git a/docs/zh/sql-reference/statements/alter/index/index.md b/docs/zh/sql-reference/statements/alter/index/index.md new file mode 100644 index 00000000000..1e3f957cf63 --- /dev/null +++ b/docs/zh/sql-reference/statements/alter/index/index.md @@ -0,0 +1,23 @@ +--- +toc_hidden_folder: true +toc_priority: 42 +toc_title: INDEX +--- + +# 操作数据跳过索引 {#manipulations-with-data-skipping-indices} + +可以使用以下操作: + +- `ALTER TABLE [db].name ADD INDEX name expression TYPE type GRANULARITY value [FIRST|AFTER name]` - 向表元数据添加索引描述。 + +- `ALTER TABLE [db].name DROP INDEX name` - 从表元数据中删除索引描述并从磁盘中删除索引文件。 + +- `ALTER TABLE [db.]table MATERIALIZE INDEX name IN PARTITION partition_name` - 查询在分区`partition_name`中重建二级索引`name`。 操作为[mutation](../../../../sql-reference/statements/alter/index.md#mutations). + +前两个命令是轻量级的,它们只更改元数据或删除文件。 + +Also, they are replicated, syncing indices metadata via ZooKeeper. +此外,它们会被复制,会通过ZooKeeper同步索引元数据。 + +!!! note "注意" +索引操作仅支持具有以下特征的表 [`*MergeTree`](../../../../engines/table-engines/mergetree-family/mergetree.md)引擎 (包括[replicated](../../../../engines/table-engines/mergetree-family/replication.md)). From fc035f49c00f6419f7f2a11f036bbced15de7c70 Mon Sep 17 00:00:00 2001 From: cnmade Date: Thu, 27 Jan 2022 16:07:25 +0800 Subject: [PATCH 09/57] Fixed zh...statements/alter: translate to zh --- .../sql-reference/statements/alter/index.md | 82 ++++++++++++++----- .../statements/alter/index/index.md | 2 +- 2 files changed, 62 insertions(+), 22 deletions(-) diff --git a/docs/zh/sql-reference/statements/alter/index.md b/docs/zh/sql-reference/statements/alter/index.md index 0d5909518ed..60f6fc022fc 100644 --- a/docs/zh/sql-reference/statements/alter/index.md +++ b/docs/zh/sql-reference/statements/alter/index.md @@ -5,7 +5,7 @@ toc_title: ALTER ## ALTER {#query_language_queries_alter} -Most `ALTER TABLE` queries modify table settings or data: +大多数 `ALTER TABLE` 查询修改表设置或数据: - [COLUMN](../../../sql-reference/statements/alter/column.md) - [PARTITION](../../../sql-reference/statements/alter/partition.md) @@ -16,15 +16,15 @@ Most `ALTER TABLE` queries modify table settings or data: - [CONSTRAINT](../../../sql-reference/statements/alter/constraint.md) - [TTL](../../../sql-reference/statements/alter/ttl.md) -!!! note "Note" - Most `ALTER TABLE` queries are supported only for [\*MergeTree](../../../engines/table-engines/mergetree-family/index.md) tables, as well as [Merge](../../../engines/table-engines/special/merge.md) and [Distributed](../../../engines/table-engines/special/distributed.md). +!!! note "备注" + 大多数 `ALTER TABLE` 查询只支持[\*MergeTree](../../../engines/table-engines/mergetree-family/index.md)表,以及[Merge](../../../engines/table-engines/special/merge.md)和[Distributed](../../../engines/table-engines/special/distributed.md)。 -These `ALTER` statements manipulate views: +这些 `ALTER` 语句操作视图: -- [ALTER TABLE ... MODIFY QUERY](../../../sql-reference/statements/alter/view.md) — Modifies a [Materialized view](../create/view.md#materialized) structure. -- [ALTER LIVE VIEW](../../../sql-reference/statements/alter/view.md#alter-live-view) — Refreshes a [Live view](../create/view.md#live-view). +- [ALTER TABLE ... MODIFY QUERY](../../../sql-reference/statements/alter/view.md) — 修改一个 [Materialized view](../create/view.md#materialized) 结构. +- [ALTER LIVE VIEW](../../../sql-reference/statements/alter/view.md#alter-live-view) — 刷新一个 [Live view](../create/view.md#live-view). -These `ALTER` statements modify entities related to role-based access control: +这些 `ALTER` 语句修改与基于角色的访问控制相关的实体: - [USER](../../../sql-reference/statements/alter/user.md) - [ROLE](../../../sql-reference/statements/alter/role.md) @@ -32,29 +32,69 @@ These `ALTER` statements modify entities related to role-based access control: - [ROW POLICY](../../../sql-reference/statements/alter/row-policy.md) - [SETTINGS PROFILE](../../../sql-reference/statements/alter/settings-profile.md) -[ALTER TABLE ... MODIFY COMMENT](../../../sql-reference/statements/alter/comment.md) statement adds, modifies, or removes comments to the table, regardless if it was set before or not. +[ALTER TABLE ... MODIFY COMMENT](../../../sql-reference/statements/alter/comment.md) 语句添加、修改或删除表中的注释,无论之前是否设置过。 + +## Mutations 突变 {#mutations} + +用来操作表数据的ALTER查询是通过一种叫做“突变”的机制来实现的,最明显的是[ALTER TABLE … DELETE](../../../sql-reference/statements/alter/delete.md)和[ALTER TABLE … UPDATE](../../../sql-reference/statements/alter/update.md)。它们是异步的后台进程,类似于[MergeTree](../../../engines/table-engines/mergetree-family/index.md)表的合并,产生新的“突变”版本的部件。 + + + +对于 `*MergeTree` 表,通过重写整个数据部分来执行突变。没有原子性——一旦突变的部件准备好,部件就会被替换,并且在突变期间开始执行的 `SELECT` 查询将看到来自已经突变的部件的数据,以及来自尚未突变的部件的数据。 + + + +突变完全按照它们的产生顺序排列,并按此顺序应用于每个部分。突变还与“INSERT INTO”查询进行部分排序:在提交突变之前插入表中的数据将被突变,而在此之后插入的数据将不会被突变。注意,突变不会以任何方式阻止插入。 + + + +突变查询在添加突变条目后立即返回(对于复制表到ZooKeeper,对于非复制表到文件系统)。突变本身使用系统配置文件设置异步执行。要跟踪突变的进程,可以使用[`system.mutations`](../../../operations/system-tables/mutations.md#system_tables-mutations) 表。成功提交的变异将继续执行,即使ClickHouse服务器重新启动。没有办法回滚突变一旦提交,但如果突变卡住了,它可以取消与[`KILL MUTATION`](../../../sql-reference/statements/misc.md#kill-mutation) 查询。 + + + +完成突变的条目不会立即删除(保留条目的数量由 `finished_mutations_to_keep` 存储引擎参数决定)。删除旧的突变条目。 + +## ALTER 查询的同步性 {#synchronicity-of-alter-queries} + + +对于非复制表,所有的 `ALTER` 查询都是同步执行的。对于复制表,查询只是向“ZooKeeper”添加相应动作的指令,动作本身会尽快执行。但是,查询可以等待所有副本上的这些操作完成。 + +对于所有的“ALTER”查询,您可以使用[replication_alter_partitions_sync](../../../operations/settings/settings.md#replication-alter-partitions-sync)设置等待。 + +通过[replication_wait_for_inactive_replica_timeout](../../../operations/settings/settings.md#replication-wait-for-inactive-replica-timeout]设置,可以指定不活动的副本执行所有 `ALTER` 查询的等待时间(以秒为单位)。 + + + +!!! info "备注" + + 对于所有的 `ALTER` 查询,如果 `replication_alter_partitions_sync = 2` 和一些副本的不激活时间超过时间(在 `replication_wait_for_inactive_replica_timeout` 设置中指定),那么将抛出一个异常 `UNFINISHED`。 + + + +对于 `ALTER TABLE ... UPDATE|DELETE` 查询由 [mutations_sync](../../../operations/settings/settings.md#mutations_sync) 设置定义的同步度。 + + + + + + + + + + + + + + -## Mutations {#mutations} -`ALTER` queries that are intended to manipulate table data are implemented with a mechanism called “mutations”, most notably [ALTER TABLE … DELETE](../../../sql-reference/statements/alter/delete.md) and [ALTER TABLE … UPDATE](../../../sql-reference/statements/alter/update.md). They are asynchronous background processes similar to merges in [MergeTree](../../../engines/table-engines/mergetree-family/index.md) tables that to produce new “mutated” versions of parts. -For `*MergeTree` tables mutations execute by **rewriting whole data parts**. There is no atomicity - parts are substituted for mutated parts as soon as they are ready and a `SELECT` query that started executing during a mutation will see data from parts that have already been mutated along with data from parts that have not been mutated yet. -Mutations are totally ordered by their creation order and are applied to each part in that order. Mutations are also partially ordered with `INSERT INTO` queries: data that was inserted into the table before the mutation was submitted will be mutated and data that was inserted after that will not be mutated. Note that mutations do not block inserts in any way. -A mutation query returns immediately after the mutation entry is added (in case of replicated tables to ZooKeeper, for non-replicated tables - to the filesystem). The mutation itself executes asynchronously using the system profile settings. To track the progress of mutations you can use the [`system.mutations`](../../../operations/system-tables/mutations.md#system_tables-mutations) table. A mutation that was successfully submitted will continue to execute even if ClickHouse servers are restarted. There is no way to roll back the mutation once it is submitted, but if the mutation is stuck for some reason it can be cancelled with the [`KILL MUTATION`](../../../sql-reference/statements/misc.md#kill-mutation) query. -Entries for finished mutations are not deleted right away (the number of preserved entries is determined by the `finished_mutations_to_keep` storage engine parameter). Older mutation entries are deleted. -## Synchronicity of ALTER Queries {#synchronicity-of-alter-queries} -For non-replicated tables, all `ALTER` queries are performed synchronously. For replicated tables, the query just adds instructions for the appropriate actions to `ZooKeeper`, and the actions themselves are performed as soon as possible. However, the query can wait for these actions to be completed on all the replicas. -For all `ALTER` queries, you can use the [replication_alter_partitions_sync](../../../operations/settings/settings.md#replication-alter-partitions-sync) setting to set up waiting. -You can specify how long (in seconds) to wait for inactive replicas to execute all `ALTER` queries with the [replication_wait_for_inactive_replica_timeout](../../../operations/settings/settings.md#replication-wait-for-inactive-replica-timeout) setting. -!!! info "Note" - For all `ALTER` queries, if `replication_alter_partitions_sync = 2` and some replicas are not active for more than the time, specified in the `replication_wait_for_inactive_replica_timeout` setting, then an exception `UNFINISHED` is thrown. -For `ALTER TABLE ... UPDATE|DELETE` queries the synchronicity is defined by the [mutations_sync](../../../operations/settings/settings.md#mutations_sync) setting. diff --git a/docs/zh/sql-reference/statements/alter/index/index.md b/docs/zh/sql-reference/statements/alter/index/index.md index 1e3f957cf63..16f48e55b2f 100644 --- a/docs/zh/sql-reference/statements/alter/index/index.md +++ b/docs/zh/sql-reference/statements/alter/index/index.md @@ -20,4 +20,4 @@ Also, they are replicated, syncing indices metadata via ZooKeeper. 此外,它们会被复制,会通过ZooKeeper同步索引元数据。 !!! note "注意" -索引操作仅支持具有以下特征的表 [`*MergeTree`](../../../../engines/table-engines/mergetree-family/mergetree.md)引擎 (包括[replicated](../../../../engines/table-engines/mergetree-family/replication.md)). + 索引操作仅支持具有以下特征的表 [`*MergeTree`](../../../../engines/table-engines/mergetree-family/mergetree.md)引擎 (包括[replicated](../../../../engines/table-engines/mergetree-family/replication.md)). From f4f6b7ddf305b2fe24167f6a52ce7e1dce268918 Mon Sep 17 00:00:00 2001 From: cnmade Date: Thu, 27 Jan 2022 16:08:01 +0800 Subject: [PATCH 10/57] Fixed zh...statements/alter: remove bak file --- .../statements/alter/index.md.bak | 23 ------------------- .../statements/alter/index/index.md.bak | 1 - 2 files changed, 24 deletions(-) delete mode 100644 docs/zh/sql-reference/statements/alter/index.md.bak delete mode 120000 docs/zh/sql-reference/statements/alter/index/index.md.bak diff --git a/docs/zh/sql-reference/statements/alter/index.md.bak b/docs/zh/sql-reference/statements/alter/index.md.bak deleted file mode 100644 index 2f60dbb262e..00000000000 --- a/docs/zh/sql-reference/statements/alter/index.md.bak +++ /dev/null @@ -1,23 +0,0 @@ ---- -toc_hidden_folder: true -toc_priority: 42 -toc_title: INDEX ---- - -# 操作数据跳过索引 {#manipulations-with-data-skipping-indices} - -可以使用以下操作: - -- `ALTER TABLE [db].name ADD INDEX name expression TYPE type GRANULARITY value [FIRST|AFTER name]` - 向表元数据添加索引描述。 - -- `ALTER TABLE [db].name DROP INDEX name` - 从表元数据中删除索引描述并从磁盘中删除索引文件。 - -- `ALTER TABLE [db.]table MATERIALIZE INDEX name IN PARTITION partition_name` - 查询在分区`partition_name`中重建二级索引`name`。 操作为[mutation](../../../sql-reference/statements/alter/index.md#mutations). - -前两个命令是轻量级的,它们只更改元数据或删除文件。 - -Also, they are replicated, syncing indices metadata via ZooKeeper. -此外,它们会被复制,会通过ZooKeeper同步索引元数据。 - -!!! note "注意" -索引操作仅支持具有以下特征的表 [`*MergeTree`](../../../engines/table-engines/mergetree-family/mergetree.md)引擎 (包括[replicated](../../../engines/table-engines/mergetree-family/replication.md)). diff --git a/docs/zh/sql-reference/statements/alter/index/index.md.bak b/docs/zh/sql-reference/statements/alter/index/index.md.bak deleted file mode 120000 index b754fa71b83..00000000000 --- a/docs/zh/sql-reference/statements/alter/index/index.md.bak +++ /dev/null @@ -1 +0,0 @@ -../../../../../en/sql-reference/statements/alter/index/index.md \ No newline at end of file From af5ad2f3704502fc24df313c45eb29757f673ac5 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Thu, 27 Jan 2022 10:07:53 +0000 Subject: [PATCH 11/57] Remove DecimalPaddedPODArray --- .../AggregateFunctionSumMap.h | 4 +- src/Columns/ColumnDecimal.cpp | 19 ----- src/Columns/ColumnDecimal.h | 73 ++++--------------- src/Functions/FunctionMathUnary.h | 2 +- src/Functions/FunctionsConversion.h | 31 ++++---- src/Functions/FunctionsRound.h | 8 +- src/Functions/array/arrayAggregation.cpp | 16 ++-- src/Functions/array/arrayCompact.cpp | 2 +- src/Functions/array/arrayCumSum.cpp | 7 +- .../array/arrayCumSumNonNegative.cpp | 2 +- src/Functions/array/arrayDifference.cpp | 2 +- src/Functions/dateName.cpp | 2 +- src/Functions/formatDateTime.cpp | 2 +- 13 files changed, 53 insertions(+), 117 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionSumMap.h b/src/AggregateFunctions/AggregateFunctionSumMap.h index 7e661a92c5b..295258cd8cf 100644 --- a/src/AggregateFunctions/AggregateFunctionSumMap.h +++ b/src/AggregateFunctions/AggregateFunctionSumMap.h @@ -226,7 +226,7 @@ public: { // FIXME why is storing NearestFieldType not enough, and we // have to check for decimals again here? - UInt32 scale = static_cast &>(key_column).getData().getScale(); + UInt32 scale = static_cast &>(key_column).getScale(); it = merged_maps.find(DecimalField(key, scale)); } else @@ -251,7 +251,7 @@ public: if constexpr (is_decimal) { - UInt32 scale = static_cast &>(key_column).getData().getScale(); + UInt32 scale = static_cast &>(key_column).getScale(); merged_maps.emplace(DecimalField(key, scale), std::move(new_values)); } else diff --git a/src/Columns/ColumnDecimal.cpp b/src/Columns/ColumnDecimal.cpp index 99085f0f976..4941585f8dd 100644 --- a/src/Columns/ColumnDecimal.cpp +++ b/src/Columns/ColumnDecimal.cpp @@ -32,12 +32,6 @@ namespace ErrorCodes extern const int LOGICAL_ERROR; } -template class DecimalPaddedPODArray; -template class DecimalPaddedPODArray; -template class DecimalPaddedPODArray; -template class DecimalPaddedPODArray; -template class DecimalPaddedPODArray; - template int ColumnDecimal::compareAt(size_t n, size_t m, const IColumn & rhs_, int) const { @@ -131,19 +125,6 @@ void ColumnDecimal::updateHashFast(SipHash & hash) const template void ColumnDecimal::getPermutation(bool reverse, size_t limit, int , IColumn::Permutation & res) const { -#if 1 /// TODO: perf test - if (data.size() <= std::numeric_limits::max()) - { - PaddedPODArray tmp_res; - permutation(reverse, limit, tmp_res); - - res.resize(tmp_res.size()); - for (size_t i = 0; i < tmp_res.size(); ++i) - res[i] = tmp_res[i]; - return; - } -#endif - permutation(reverse, limit, res); } diff --git a/src/Columns/ColumnDecimal.h b/src/Columns/ColumnDecimal.h index b55083cd671..1a4b06b46e4 100644 --- a/src/Columns/ColumnDecimal.h +++ b/src/Columns/ColumnDecimal.h @@ -1,66 +1,21 @@ #pragma once +#include + +#include +#include +#include +#include +#include +#include #include #include #include -#include -#include -#include -#include -#include -#include - -#include namespace DB { -/// PaddedPODArray extended by Decimal scale -template -class DecimalPaddedPODArray : public PaddedPODArray -{ -public: - using Base = PaddedPODArray; - using Base::operator[]; - - DecimalPaddedPODArray(size_t size, UInt32 scale_) - : Base(size), - scale(scale_) - {} - - DecimalPaddedPODArray(const DecimalPaddedPODArray & other) - : Base(other.begin(), other.end()), - scale(other.scale) - {} - - DecimalPaddedPODArray(DecimalPaddedPODArray && other) - { - this->swap(other); - std::swap(scale, other.scale); - } - - DecimalPaddedPODArray & operator=(DecimalPaddedPODArray && other) - { - this->swap(other); - std::swap(scale, other.scale); - return *this; - } - - UInt32 getScale() const { return scale; } - -private: - UInt32 scale; -}; - -/// Prevent implicit template instantiation of DecimalPaddedPODArray for common decimal types - -extern template class DecimalPaddedPODArray; -extern template class DecimalPaddedPODArray; -extern template class DecimalPaddedPODArray; -extern template class DecimalPaddedPODArray; -extern template class DecimalPaddedPODArray; - /// A ColumnVector for Decimals template class ColumnDecimal final : public COWHelper> @@ -72,16 +27,16 @@ private: public: using ValueType = T; using NativeT = typename T::NativeType; - using Container = DecimalPaddedPODArray; + using Container = PaddedPODArray; private: ColumnDecimal(const size_t n, UInt32 scale_) - : data(n, scale_), + : data(n), scale(scale_) {} ColumnDecimal(const ColumnDecimal & src) - : data(src.data), + : data(src.data.begin(), src.data.end()), scale(src.scale) {} @@ -195,7 +150,7 @@ public: const T & getElement(size_t n) const { return data[n]; } T & getElement(size_t n) { return data[n]; } - UInt32 getScale() const {return scale;} + UInt32 getScale() const { return scale; } protected: Container data; @@ -206,8 +161,8 @@ protected: { size_t s = data.size(); res.resize(s); - for (U i = 0; i < s; ++i) - res[i] = i; + for (size_t i = 0; i < s; ++i) + res[i] = static_cast(i); auto sort_end = res.end(); if (limit && limit < s) diff --git a/src/Functions/FunctionMathUnary.h b/src/Functions/FunctionMathUnary.h index e0b9355e0a6..fa10c004e87 100644 --- a/src/Functions/FunctionMathUnary.h +++ b/src/Functions/FunctionMathUnary.h @@ -125,7 +125,7 @@ private: { const auto & src_data = col->getData(); const size_t size = src_data.size(); - UInt32 scale = src_data.getScale(); + UInt32 scale = col->getScale(); auto dst = ColumnVector::create(); auto & dst_data = dst->getData(); diff --git a/src/Functions/FunctionsConversion.h b/src/Functions/FunctionsConversion.h index d65d0604547..476b7eee5f7 100644 --- a/src/Functions/FunctionsConversion.h +++ b/src/Functions/FunctionsConversion.h @@ -152,9 +152,10 @@ struct ConvertImpl if (const ColVecFrom * col_from = checkAndGetColumn(named_from.column.get())) { typename ColVecTo::MutablePtr col_to = nullptr; + UInt32 scale = 0; + if constexpr (IsDataTypeDecimal) { - UInt32 scale; if constexpr (std::is_same_v || std::is_same_v) { @@ -208,11 +209,11 @@ struct ConvertImpl bool convert_result = false; if constexpr (IsDataTypeDecimal && IsDataTypeDecimal) - convert_result = tryConvertDecimals(vec_from[i], vec_from.getScale(), vec_to.getScale(), result); + convert_result = tryConvertDecimals(vec_from[i], scale, scale, result); else if constexpr (IsDataTypeDecimal && IsDataTypeNumber) - convert_result = tryConvertFromDecimal(vec_from[i], vec_from.getScale(), result); + convert_result = tryConvertFromDecimal(vec_from[i], scale, result); else if constexpr (IsDataTypeNumber && IsDataTypeDecimal) - convert_result = tryConvertToDecimal(vec_from[i], vec_to.getScale(), result); + convert_result = tryConvertToDecimal(vec_from[i], scale, result); if (convert_result) vec_to[i] = result; @@ -225,11 +226,11 @@ struct ConvertImpl else { if constexpr (IsDataTypeDecimal && IsDataTypeDecimal) - vec_to[i] = convertDecimals(vec_from[i], vec_from.getScale(), vec_to.getScale()); + vec_to[i] = convertDecimals(vec_from[i], scale, scale); else if constexpr (IsDataTypeDecimal && IsDataTypeNumber) - vec_to[i] = convertFromDecimal(vec_from[i], vec_from.getScale()); + vec_to[i] = convertFromDecimal(vec_from[i], scale); else if constexpr (IsDataTypeNumber && IsDataTypeDecimal) - vec_to[i] = convertToDecimal(vec_from[i], vec_to.getScale()); + vec_to[i] = convertToDecimal(vec_from[i], scale); else throw Exception("Unsupported data type in conversion function", ErrorCodes::CANNOT_CONVERT_TYPE); } @@ -820,7 +821,7 @@ struct ConvertImpl) data_to.resize(size * (strlen("YYYY-MM-DD hh:mm:ss") + 1)); else if constexpr (std::is_same_v) - data_to.resize(size * (strlen("YYYY-MM-DD hh:mm:ss.") + vec_from.getScale() + 1)); + data_to.resize(size * (strlen("YYYY-MM-DD hh:mm:ss.") + col_from->getScale() + 1)); else data_to.resize(size * 3); /// Arbitrary @@ -1169,7 +1170,7 @@ struct ConvertThroughParsing if constexpr (to_datetime64) { DateTime64 res = 0; - parseDateTime64BestEffort(res, vec_to.getScale(), read_buffer, *local_time_zone, *utc_time_zone); + parseDateTime64BestEffort(res, col_to->getScale(), read_buffer, *local_time_zone, *utc_time_zone); vec_to[i] = res; } else @@ -1184,7 +1185,7 @@ struct ConvertThroughParsing if constexpr (to_datetime64) { DateTime64 res = 0; - parseDateTime64BestEffortUS(res, vec_to.getScale(), read_buffer, *local_time_zone, *utc_time_zone); + parseDateTime64BestEffortUS(res, col_to->getScale(), read_buffer, *local_time_zone, *utc_time_zone); vec_to[i] = res; } else @@ -1199,12 +1200,12 @@ struct ConvertThroughParsing if constexpr (to_datetime64) { DateTime64 value = 0; - readDateTime64Text(value, vec_to.getScale(), read_buffer, *local_time_zone); + readDateTime64Text(value, col_to->getScale(), read_buffer, *local_time_zone); vec_to[i] = value; } else if constexpr (IsDataTypeDecimal) SerializationDecimal::readText( - vec_to[i], read_buffer, ToDataType::maxPrecision(), vec_to.getScale()); + vec_to[i], read_buffer, ToDataType::maxPrecision(), col_to->getScale()); else { parseImpl(vec_to[i], read_buffer, local_time_zone); @@ -1223,7 +1224,7 @@ struct ConvertThroughParsing if constexpr (to_datetime64) { DateTime64 res = 0; - parsed = tryParseDateTime64BestEffort(res, vec_to.getScale(), read_buffer, *local_time_zone, *utc_time_zone); + parsed = tryParseDateTime64BestEffort(res, col_to->getScale(), read_buffer, *local_time_zone, *utc_time_zone); vec_to[i] = res; } else @@ -1244,12 +1245,12 @@ struct ConvertThroughParsing if constexpr (to_datetime64) { DateTime64 value = 0; - parsed = tryReadDateTime64Text(value, vec_to.getScale(), read_buffer, *local_time_zone); + parsed = tryReadDateTime64Text(value, col_to->getScale(), read_buffer, *local_time_zone); vec_to[i] = value; } else if constexpr (IsDataTypeDecimal) parsed = SerializationDecimal::tryReadText( - vec_to[i], read_buffer, ToDataType::maxPrecision(), vec_to.getScale()); + vec_to[i], read_buffer, ToDataType::maxPrecision(), col_to->getScale()); else parsed = tryParseImpl(vec_to[i], read_buffer, local_time_zone); } diff --git a/src/Functions/FunctionsRound.h b/src/Functions/FunctionsRound.h index edba82a5b4e..8c30885d5dd 100644 --- a/src/Functions/FunctionsRound.h +++ b/src/Functions/FunctionsRound.h @@ -422,9 +422,9 @@ private: using Container = typename ColumnDecimal::Container; public: - static NO_INLINE void apply(const Container & in, Container & out, Scale scale_arg) + static NO_INLINE void apply(const Container & in, UInt32 in_scale, Container & out, Scale scale_arg) { - scale_arg = in.getScale() - scale_arg; + scale_arg = in_scale - scale_arg; if (scale_arg > 0) { size_t scale = intExp10(scale_arg); @@ -498,11 +498,11 @@ public: const auto * const col = checkAndGetColumn>(col_general); const typename ColumnDecimal::Container & vec_src = col->getData(); - auto col_res = ColumnDecimal::create(vec_src.size(), vec_src.getScale()); + auto col_res = ColumnDecimal::create(vec_src.size(), col->getScale()); auto & vec_res = col_res->getData(); if (!vec_res.empty()) - DecimalRoundingImpl::apply(col->getData(), vec_res, scale_arg); + DecimalRoundingImpl::apply(col->getData(), col->getScale(), vec_res, scale_arg); return col_res; } diff --git a/src/Functions/array/arrayAggregation.cpp b/src/Functions/array/arrayAggregation.cpp index da2304e1bb6..ee08c4f7f37 100644 --- a/src/Functions/array/arrayAggregation.cpp +++ b/src/Functions/array/arrayAggregation.cpp @@ -157,11 +157,11 @@ struct ArrayAggregateImpl return false; const AggregationType x = column_const->template getValue(); // NOLINT - const auto & data = checkAndGetColumn(&column_const->getDataColumn())->getData(); + const ColVecType * column_typed = checkAndGetColumn(&column_const->getDataColumn()); typename ColVecResultType::MutablePtr res_column; if constexpr (is_decimal) - res_column = ColVecResultType::create(offsets.size(), data.getScale()); + res_column = ColVecResultType::create(offsets.size(), column_typed->getScale()); else res_column = ColVecResultType::create(offsets.size()); @@ -185,7 +185,7 @@ struct ArrayAggregateImpl { if constexpr (is_decimal) { - res[i] = DecimalUtils::convertTo(x, data.getScale()); + res[i] = DecimalUtils::convertTo(x, column_typed->getScale()); } else { @@ -210,11 +210,11 @@ struct ArrayAggregateImpl throw Exception(ErrorCodes::DECIMAL_OVERFLOW, "Decimal math overflow"); } - auto result_scale = data.getScale() * array_size; + auto result_scale = column_typed->getScale() * array_size; if (unlikely(result_scale > DecimalUtils::max_precision)) throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Scale {} is out of bounds", result_scale); - res[i] = DecimalUtils::convertTo(product, data.getScale() * array_size); + res[i] = DecimalUtils::convertTo(product, result_scale); } else { @@ -236,7 +236,7 @@ struct ArrayAggregateImpl typename ColVecResultType::MutablePtr res_column; if constexpr (is_decimal) - res_column = ColVecResultType::create(offsets.size(), data.getScale()); + res_column = ColVecResultType::create(offsets.size(), column->getScale()); else res_column = ColVecResultType::create(offsets.size()); @@ -309,7 +309,7 @@ struct ArrayAggregateImpl if constexpr (is_decimal) { aggregate_value = aggregate_value / AggregationType(count); - res[i] = DecimalUtils::convertTo(aggregate_value, data.getScale()); + res[i] = DecimalUtils::convertTo(aggregate_value, column->getScale()); } else { @@ -318,7 +318,7 @@ struct ArrayAggregateImpl } else if constexpr (aggregate_operation == AggregateOperation::product && is_decimal) { - auto result_scale = data.getScale() * count; + auto result_scale = column->getScale() * count; if (unlikely(result_scale > DecimalUtils::max_precision)) throw Exception(ErrorCodes::ARGUMENT_OUT_OF_BOUND, "Scale {} is out of bounds", result_scale); diff --git a/src/Functions/array/arrayCompact.cpp b/src/Functions/array/arrayCompact.cpp index 7914b9a154e..c2908e37e12 100644 --- a/src/Functions/array/arrayCompact.cpp +++ b/src/Functions/array/arrayCompact.cpp @@ -40,7 +40,7 @@ struct ArrayCompactImpl typename ColVecType::MutablePtr res_values_column; if constexpr (is_decimal) - res_values_column = ColVecType::create(src_values.size(), src_values.getScale()); + res_values_column = ColVecType::create(src_values.size(), src_values_column->getScale()); else res_values_column = ColVecType::create(src_values.size()); diff --git a/src/Functions/array/arrayCumSum.cpp b/src/Functions/array/arrayCumSum.cpp index da8ef3d7852..467d9ad3951 100644 --- a/src/Functions/array/arrayCumSum.cpp +++ b/src/Functions/array/arrayCumSum.cpp @@ -101,9 +101,8 @@ struct ArrayCumSumImpl typename ColVecResult::MutablePtr res_nested; if constexpr (is_decimal) { - const typename ColVecType::Container & data = - checkAndGetColumn(&column_const->getDataColumn())->getData(); - res_nested = ColVecResult::create(0, data.getScale()); + const ColVecType * column_typed = checkAndGetColumn(&column_const->getDataColumn()); + res_nested = ColVecResult::create(0, column_typed->getScale()); } else res_nested = ColVecResult::create(); @@ -120,7 +119,7 @@ struct ArrayCumSumImpl typename ColVecResult::MutablePtr res_nested; if constexpr (is_decimal) - res_nested = ColVecResult::create(0, data.getScale()); + res_nested = ColVecResult::create(0, column->getScale()); else res_nested = ColVecResult::create(); diff --git a/src/Functions/array/arrayCumSumNonNegative.cpp b/src/Functions/array/arrayCumSumNonNegative.cpp index c40df27c1cc..476bbd08163 100644 --- a/src/Functions/array/arrayCumSumNonNegative.cpp +++ b/src/Functions/array/arrayCumSumNonNegative.cpp @@ -83,7 +83,7 @@ struct ArrayCumSumNonNegativeImpl typename ColVecResult::MutablePtr res_nested; if constexpr (is_decimal) - res_nested = ColVecResult::create(0, data.getScale()); + res_nested = ColVecResult::create(0, column->getScale()); else res_nested = ColVecResult::create(); diff --git a/src/Functions/array/arrayDifference.cpp b/src/Functions/array/arrayDifference.cpp index 97243f2cf74..c5fdf27100b 100644 --- a/src/Functions/array/arrayDifference.cpp +++ b/src/Functions/array/arrayDifference.cpp @@ -105,7 +105,7 @@ struct ArrayDifferenceImpl typename ColVecResult::MutablePtr res_nested; if constexpr (is_decimal) - res_nested = ColVecResult::create(0, data.getScale()); + res_nested = ColVecResult::create(0, column->getScale()); else res_nested = ColVecResult::create(); diff --git a/src/Functions/dateName.cpp b/src/Functions/dateName.cpp index c89a7f80dfd..eef9bc3955b 100644 --- a/src/Functions/dateName.cpp +++ b/src/Functions/dateName.cpp @@ -148,7 +148,7 @@ public: UInt32 scale [[maybe_unused]] = 0; if constexpr (std::is_same_v) { - scale = times_data.getScale(); + scale = times->getScale(); } auto result_column = ColumnString::create(); diff --git a/src/Functions/formatDateTime.cpp b/src/Functions/formatDateTime.cpp index 9f303b86ad3..e2ec90f4e61 100644 --- a/src/Functions/formatDateTime.cpp +++ b/src/Functions/formatDateTime.cpp @@ -440,7 +440,7 @@ public: UInt32 scale [[maybe_unused]] = 0; if constexpr (std::is_same_v) { - scale = vec.getScale(); + scale = times->getScale(); } auto col_res = ColumnString::create(); From 368fd975efaaa034b889c6fa3a8762708d5b2dbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=B0=95=EB=8F=99=EC=B2=A0?= <36955431+dongcheolpark@users.noreply.github.com> Date: Thu, 27 Jan 2022 20:02:36 +0900 Subject: [PATCH 12/57] Translate ko/index.md : add translate --- docs/ko/images/column-oriented.gif | Bin 0 -> 43771 bytes docs/ko/images/logo.svg | 1 + docs/ko/images/play.png | Bin 0 -> 26602 bytes docs/ko/images/row-oriented.gif | Bin 0 -> 39281 bytes docs/ko/index.md | 94 +++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+) create mode 100644 docs/ko/images/column-oriented.gif create mode 100644 docs/ko/images/logo.svg create mode 100644 docs/ko/images/play.png create mode 100644 docs/ko/images/row-oriented.gif create mode 100644 docs/ko/index.md diff --git a/docs/ko/images/column-oriented.gif b/docs/ko/images/column-oriented.gif new file mode 100644 index 0000000000000000000000000000000000000000..d5ac7c82848cb633cb0f1b341909c0b955e79fcf GIT binary patch literal 43771 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 literal 0 HcmV?d00001 diff --git a/docs/ko/images/logo.svg b/docs/ko/images/logo.svg new file mode 100644 index 00000000000..b5ab923ff65 --- /dev/null +++ b/docs/ko/images/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/docs/ko/images/play.png b/docs/ko/images/play.png new file mode 100644 index 0000000000000000000000000000000000000000..b75aebe40895e1e6d92aa2189e2c910df3eaff5f GIT binary patch literal 26602 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

)tVttp`^7_wBmx@aUR{6nxL3BQiH?(D^G@ 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 literal 0 HcmV?d00001 diff --git a/docs/ko/index.md b/docs/ko/index.md new file mode 100644 index 00000000000..f2a6396c069 --- /dev/null +++ b/docs/ko/index.md @@ -0,0 +1,94 @@ +--- +toc_priority: 0 +toc_title: 목차 +--- + +# ClickHouse란? {#what-is-clickhouse} + +ClickHouse® 는 query의 온라인 분석 처리(OLAP)를 위한 열 지향(column-oriented) 데이터베이스 관리 시스템(DBMS)입니다. + +"보통의" 행 지향(row-oriented) DMBS에서는 데이터가 다음과 같은 순서로 저장됩니다. + +| row | WatchID | JavaEnable | Title | GoodEvent | EventTime | +|-----|-------------|------------|--------------------|-----------|---------------------| +| #0 | 89354350662 | 1 | Investor Relations | 1 | 2016-05-18 05:19:20 | +| #1 | 90329509958 | 0 | Contact us | 1 | 2016-05-18 08:10:20 | +| #2 | 89953706054 | 1 | Mission | 1 | 2016-05-18 07:38:00 | +| #N | … | … | … | … | … | + +즉, 행과 관련된 모든 값들은 물리적으로 나란히 저장됩니다. + +행 지향(row-oriented) DMBS의 예시로는 MySQL, Postgres, 그리고 MS SQL 서버 등이 있습니다. + +열 지향 (column-oriented) DBMS에서는 데이터가 아래와 같은 방식으로 저장됩니다: + +| Row: | #0 | #1 | #2 | #N | +|-------------|---------------------|---------------------|---------------------|-----| +| WatchID: | 89354350662 | 90329509958 | 89953706054 | … | +| JavaEnable: | 1 | 0 | 1 | … | +| Title: | Investor Relations | Contact us | Mission | … | +| GoodEvent: | 1 | 1 | 1 | … | +| EventTime: | 2016-05-18 05:19:20 | 2016-05-18 08:10:20 | 2016-05-18 07:38:00 | … | + +이 예에서는 데이터가 정렬된 순서만을 보여줍니다. 다른 열의 값들은 서로 분리되어 저장되고, 같은 열의 정보들은 함께 저장됩니다. + +열 지향(column-oriented) DBMS 의 종류는 Vertica, Paraccel (Actian Matrix and Amazon Redshift), Sybase IQ, Exasol, Infobright, InfiniDB, MonetDB (VectorWise and Actian Vector), LucidDB, SAP HANA, Google Dremel, Google PowerDrill, Druid, 그리고 kdb+ 등이 있습니다. + +데이터를 저장하기 위한 서로 다른 순서는 다른 시나리오에 더 적합합니다. 데이터 접근 시나리오는 쿼리가 수행되는 빈도, 비율 및 비율을 나타내거나, 각 쿼리 유형(행, 열 및 바이트)에 대해 읽은 데이터의 양 데이터 읽기와 업데이트 사이의 관계, 데이터의 작업 크기 및 로컬에서 사용되는 방법 트랜잭션이 사용되는지 여부, 트랜잭션이 얼마나 격리되어 있는지, 데이터 복제 및 논리적 무결성에 대한 요구 사항, 각 쿼리 유형에 대한 대기 시간 및 처리량 요구 사항 등이 있습니다. + +시스템의 부하가 높을수록 사용 시나리오의 요구 사항에 맞게 시스템 설정을 사용자 지정하는 것이 더 중요하며 이 사용자 지정은 더욱 세분화됩니다. 상당히 다른 시나리오에 똑같이 적합한 시스템은 없습니다. 만약 높은 부하에서 시스템이 넓은 시나리오 집합에 대해 적응한다면 시스템은 모든 시나리오를 모두 제대로 처리하지 못하거나 가능한 시나리오 중 하나 또는 몇 개에 대해서만 잘 작동할 것입니다. + +## OLAP 시나리오의 중요 속성들 {#key-properties-of-olap-scenario} + +- 요청(request)의 대부분은 읽기 접근에 관한 것입니다. +- 데이터는 단일 행이 아니라 상당히 큰 일괄 처리(\> 1000개 행)로 업데이트됩니다. 또는 전혀 업데이트되지 않습니다. +- 데이터는 DB에 추가되지만 수정되지는 않습니다. +- 읽기의 경우 DB에서 상당히 많은 수의 행이 추출되지만 열은 일부만 추출됩니다. +- 테이블은 "넓습니다". 이는 열의 수가 많다는 것을 의미합니다. +- 쿼리는 상대적으로 드뭅니다(일반적으로 서버당 수백 또는 초당 쿼리 미만). +- 간단한 쿼리의 경우 약 50ms의 대기 시간이 허용됩니다. +- 열 값은 숫자와 짧은 문자열(예: URL당 60바이트)과 같이 상당히 작습니다 +- 단일 쿼리를 처리할 때 높은 처리량이 필요합니다(서버당 초당 최대 수십억 행). +- 트랜잭션이 필요하지 않습니다. +- 데이터 일관성에 대한 요구 사항이 낮습니다. +- 쿼리당 하나의 큰 테이블이 존재하고 하나를 제외한 모든 테이블은 작습니다. +- 쿼리 결과가 원본 데이터보다 훨씬 작습니다. 즉, 데이터가 필터링되거나 집계되므로 결과가 단일 서버의 RAM에 꼭 들어맞습니다. + +OLAP 시나리오가 다른 일반적인 시나리오(OLTP 또는 키-값 액세스와 같은)와 매우 다르다는 것을 쉽게 알 수 있습니다. 따라서 적절한 성능을 얻으려면 분석 쿼리를 처리하기 위해 OLTP 또는 키-값 DB를 사용하는 것은 의미가 없습니다. 예를 들어 분석에 MongoDB나 Redis를 사용하려고 하면 OLAP 데이터베이스에 비해 성능이 매우 저하됩니다. + +## 왜 열 지향 데이터베이스가 OLAP 시나리오에 적합한가{#why-column-oriented-databases-work-better-in-the-olap-scenario} + +열 지향(column-oriented) 데이터베이스는 OLAP 시나리오에 더 적합합니다. 대부분의 쿼리를 처리하는 데 있어서 행 지향(row-oriented) 데이터베이스보다 100배 이상 빠릅니다. 그 이유는 아래에 자세히 설명되어 있지만 사실은 시각적으로 더 쉽게 설명할 수 있습니다. + +**행 지향 DBMS** + +![Row-oriented](images/row-oriented.gif#) + +**열 지향 DBMS** + +![Column-oriented](images/column-oriented.gif#) + +차이가 보이시나요? + +### 입출력 {#inputoutput} + +1. 분석 쿼리의 경우 적은 수의 테이블 열만 읽어야 합니다. 열 지향 데이터베이스에서는 필요한 데이터만 읽을 수 있습니다. 예를 들어 100개 중 5개의 열이 필요한 경우 I/O가 20배 감소할 것으로 예상할 수 있습니다. +2. 데이터는 패킷으로 읽히므로 압축하기가 더 쉽습니다. 열의 데이터도 압축하기 쉽습니다. 이것은 I/O의 볼륨을 더욱 감소시킵니다. +3. 감소된 I/O로 인해 시스템 캐시에 더 많은 데이터가 들어갑니다. + +예를 들어, "각 광고 플랫폼에 대한 레코드 수 계산" 쿼리는 압축되지 않은 1바이트를 차지하는 하나의 "광고 플랫폼 ID" 열을 읽어야 합니다. 트래픽의 대부분이 광고 플랫폼에서 발생하지 않은 경우 이 열의 최소 10배 압축을 기대할 수 있습니다. 빠른 압축 알고리즘을 사용하면 초당 최소 몇 기가바이트의 압축되지 않은 데이터의 속도로 데이터 압축 해제가 가능합니다. 즉, 이 쿼리는 단일 서버에서 초당 약 수십억 행의 속도로 처리될 수 있습니다. 이 속도는 정말 실제로 달성됩니다. + +### CPU {#cpu} + +쿼리를 수행하려면 많은 행을 처리해야 하므로 별도의 행이 아닌 전체 벡터에 대한 모든 연산을 디스패치하거나 쿼리 엔진을 구현하여 디스패치 비용이 거의 들지 않습니다. 반쯤 괜찮은 디스크 하위 시스템에서 이렇게 하지 않으면 쿼리 인터프리터가 불가피하게 CPU를 정지시킵니다. 데이터를 열에 저장하고 가능한 경우 열별로 처리하는 것이 좋습니다. + +이를 수행하기위한 두가지 방법이 있습니다. + +1. 벡터 엔진. 모든 연산은 별도의 값 대신 벡터에 대해 작성됩니다. 즉, 작업을 자주 호출할 필요가 없으며 파견 비용도 무시할 수 있습니다. 작업 코드에는 최적화된 내부 주기가 포함되어 있습니다. +2. 코드 생성. 쿼리에 대해 생성된 코드에는 모든 간접 호출이 있습니다. + +이것은 단순한 쿼리를 실행할 때 의미가 없기 때문에 "일반" 데이터베이스에서는 수행되지 않습니다. 그러나 예외가 있습니다. 예를 들어 MemSQL은 코드 생성을 사용하여 SQL 쿼리를 처리할 때 대기 시간을 줄입니다. (비교되게, 분석 DBMS는 대기 시간이 아닌 처리량 최적화가 필요합니다.) + +CPU 효율성을 위해 쿼리 언어는 선언적(SQL 또는 MDX)이거나 최소한 벡터(J, K)여야 합니다. 쿼리는 최적화를 허용하는 암시적 루프만 포함해야 합니다. + +{## [원문](https://clickhouse.com/docs/en/) ##} From faee95b897d594e7b68fc62d71fb5fb31c4fed30 Mon Sep 17 00:00:00 2001 From: Amos Bird Date: Thu, 27 Jan 2022 21:42:08 +0800 Subject: [PATCH 13/57] Make ORDER BY tuple almost as fast as ORDER BY columns We have special optimizations for multiple column ORDER BY: https://github.com/ClickHouse/ClickHouse/pull/10831 . It's beneficial to also apply to tuple columns. Before: select * from numbers(300000000) order by (1 - number , number + 1 , number) limit 10; 2.613 sec. After: select * from numbers(300000000) order by (1 - number , number + 1 , number) limit 10; 0.755 sec No tuple: select * from numbers(300000000) order by 1 - number , number + 1 , number limit 10; 0.755 sec --- src/Interpreters/sortBlock.cpp | 115 ++++++++++++++++----------- src/Interpreters/sortBlock.h | 14 ---- tests/performance/order_by_tuple.xml | 8 ++ 3 files changed, 75 insertions(+), 62 deletions(-) create mode 100644 tests/performance/order_by_tuple.xml diff --git a/src/Interpreters/sortBlock.cpp b/src/Interpreters/sortBlock.cpp index edf911fa61c..d8daaac6a36 100644 --- a/src/Interpreters/sortBlock.cpp +++ b/src/Interpreters/sortBlock.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -22,6 +23,28 @@ static bool isCollationRequired(const SortColumnDescription & description) return description.collator != nullptr; } +/// Column with description for sort +struct ColumnWithSortDescription +{ + const IColumn * column = nullptr; + const SortColumnDescription * description = nullptr; + + /// It means, that this column is ColumnConst + bool column_const = false; +}; +using ColumnsWithSortDescriptions = std::vector; + +void flattenTupleColumnRecursively( + ColumnsWithSortDescriptions & res, const ColumnTuple * tuple, const SortColumnDescription * description, bool is_constant) +{ + for (const auto & column : tuple->getColumns()) + { + if (const auto * subtuple = typeid_cast(column.get())) + flattenTupleColumnRecursively(res, subtuple, description, is_constant); + else + res.emplace_back(ColumnWithSortDescription{column.get(), description, is_constant}); + } +} ColumnsWithSortDescriptions getColumnsWithSortDescription(const Block & block, const SortDescription & description) { @@ -35,13 +58,14 @@ ColumnsWithSortDescriptions getColumnsWithSortDescription(const Block & block, c ? block.getByName(description[i].column_name).column.get() : block.safeGetByPosition(description[i].column_number).column.get(); - res.emplace_back(ColumnWithSortDescription{column, description[i], isColumnConst(*column)}); + if (const auto * tuple = typeid_cast(column)) + flattenTupleColumnRecursively(res, tuple, &description[i], isColumnConst(*column)); + else + res.emplace_back(ColumnWithSortDescription{column, &description[i], isColumnConst(*column)}); } - return res; } - struct PartialSortingLess { const ColumnsWithSortDescriptions & columns; @@ -56,7 +80,7 @@ struct PartialSortingLess if (elem.column_const) res = 0; else - res = elem.description.direction * elem.column->compareAt(a, b, *elem.column, elem.description.nulls_direction); + res = elem.description->direction * elem.column->compareAt(a, b, *elem.column, elem.description->nulls_direction); if (res < 0) return true; else if (res > 0) @@ -86,13 +110,13 @@ struct PartialSortingLessWithCollation { res = 0; } - else if (isCollationRequired(elem.description)) + else if (isCollationRequired(*elem.description)) { - res = elem.column->compareAtWithCollation(a, b, *elem.column, elem.description.nulls_direction, *elem.description.collator); + res = elem.column->compareAtWithCollation(a, b, *elem.column, elem.description->nulls_direction, *elem.description->collator); } else - res = elem.column->compareAt(a, b, *elem.column, elem.description.nulls_direction); - res *= elem.description.direction; + res = elem.column->compareAt(a, b, *elem.column, elem.description->nulls_direction); + res *= elem.description->direction; if (res < 0) return true; else if (res > 0) @@ -107,47 +131,35 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) if (!block) return; + ColumnsWithSortDescriptions columns_with_sort_desc = getColumnsWithSortDescription(block, description); + IColumn::Permutation perm; /// If only one column to sort by - if (description.size() == 1) + if (columns_with_sort_desc.size() == 1) { - IColumn::Permutation perm; - bool reverse = description[0].direction == -1; - - const IColumn * column = !description[0].column_name.empty() - ? block.getByName(description[0].column_name).column.get() - : block.safeGetByPosition(description[0].column_number).column.get(); - - bool is_column_const = false; + bool reverse = columns_with_sort_desc[0].description->direction == -1; + const IColumn * column = columns_with_sort_desc[0].column; + bool is_column_const = columns_with_sort_desc[0].column_const; if (isCollationRequired(description[0])) { if (!column->isCollationSupported()) - throw Exception("Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, containing them.", ErrorCodes::BAD_COLLATION); + throw Exception( + "Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, " + "containing them.", + ErrorCodes::BAD_COLLATION); - if (isColumnConst(*column)) - is_column_const = true; - else + if (!is_column_const) column->getPermutationWithCollation(*description[0].collator, reverse, limit, description[0].nulls_direction, perm); } - else if (!isColumnConst(*column)) + else if (!is_column_const) { - int nan_direction_hint = description[0].nulls_direction; + int nan_direction_hint = columns_with_sort_desc[0].description->nulls_direction; column->getPermutation(reverse, limit, nan_direction_hint, perm); } - else - /// we don't need to do anything with const column - is_column_const = true; - - size_t columns = block.columns(); - for (size_t i = 0; i < columns; ++i) - { - if (!is_column_const) - block.getByPosition(i).column = block.getByPosition(i).column->permute(perm, limit); - } } else { size_t size = block.rows(); - IColumn::Permutation perm(size); + perm.resize(size); for (size_t i = 0; i < size; ++i) perm[i] = i; @@ -155,16 +167,15 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) limit = 0; bool need_collation = false; - ColumnsWithSortDescriptions columns_with_sort_desc = getColumnsWithSortDescription(block, description); - - for (size_t i = 0, num_sort_columns = description.size(); i < num_sort_columns; ++i) + for (const auto & column : columns_with_sort_desc) { - const IColumn * column = columns_with_sort_desc[i].column; - if (isCollationRequired(description[i])) + if (isCollationRequired(*column.description)) { - if (!column->isCollationSupported()) - throw Exception("Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, containing them.", ErrorCodes::BAD_COLLATION); - + if (!column.column->isCollationSupported()) + throw Exception( + "Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, " + "containing them.", + ErrorCodes::BAD_COLLATION); need_collation = true; } } @@ -184,15 +195,20 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) if (column.column_const) continue; - if (isCollationRequired(column.description)) + if (isCollationRequired(*column.description)) { column.column->updatePermutationWithCollation( - *column.description.collator, column.description.direction < 0, limit, column.description.nulls_direction, perm, ranges); + *column.description->collator, + column.description->direction < 0, + limit, + column.description->nulls_direction, + perm, + ranges); } else { column.column->updatePermutation( - column.description.direction < 0, limit, column.description.nulls_direction, perm, ranges); + column.description->direction < 0, limit, column.description->nulls_direction, perm, ranges); } } } @@ -209,12 +225,15 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) break; column.column->updatePermutation( - column.description.direction < 0, limit, column.description.nulls_direction, perm, ranges); + column.description->direction < 0, limit, column.description->nulls_direction, perm, ranges); } } + } - size_t columns = block.columns(); - for (size_t i = 0; i < columns; ++i) + size_t columns = block.columns(); + for (size_t i = 0; i < columns; ++i) + { + if (!isColumnConst(*block.getByPosition(i).column)) block.getByPosition(i).column = block.getByPosition(i).column->permute(perm, limit); } } diff --git a/src/Interpreters/sortBlock.h b/src/Interpreters/sortBlock.h index faf9384901b..8f28076c6af 100644 --- a/src/Interpreters/sortBlock.h +++ b/src/Interpreters/sortBlock.h @@ -29,18 +29,4 @@ void stableGetPermutation(const Block & block, const SortDescription & descripti */ bool isAlreadySorted(const Block & block, const SortDescription & description); -/// Column with description for sort -struct ColumnWithSortDescription -{ - const IColumn * column = nullptr; - SortColumnDescription description; - - /// It means, that this column is ColumnConst - bool column_const = false; -}; - -using ColumnsWithSortDescriptions = std::vector; - -ColumnsWithSortDescriptions getColumnsWithSortDescription(const Block & block, const SortDescription & description); - } diff --git a/tests/performance/order_by_tuple.xml b/tests/performance/order_by_tuple.xml new file mode 100644 index 00000000000..72fb1812bbc --- /dev/null +++ b/tests/performance/order_by_tuple.xml @@ -0,0 +1,8 @@ + + + sorting + comparison + + + select * from numbers(300000000) order by (1 - number , number + 1 , number) limit 10; + From 24267c8cfb98556dbd2770cbd78586ae4f818100 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Thu, 27 Jan 2022 16:02:31 +0000 Subject: [PATCH 14/57] Fixed tests --- src/Functions/FunctionsConversion.h | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Functions/FunctionsConversion.h b/src/Functions/FunctionsConversion.h index 476b7eee5f7..909803d7cd7 100644 --- a/src/Functions/FunctionsConversion.h +++ b/src/Functions/FunctionsConversion.h @@ -152,10 +152,11 @@ struct ConvertImpl if (const ColVecFrom * col_from = checkAndGetColumn(named_from.column.get())) { typename ColVecTo::MutablePtr col_to = nullptr; - UInt32 scale = 0; if constexpr (IsDataTypeDecimal) { + UInt32 scale; + if constexpr (std::is_same_v || std::is_same_v) { @@ -209,11 +210,11 @@ struct ConvertImpl bool convert_result = false; if constexpr (IsDataTypeDecimal && IsDataTypeDecimal) - convert_result = tryConvertDecimals(vec_from[i], scale, scale, result); + convert_result = tryConvertDecimals(vec_from[i], col_from->getScale(), col_to->getScale(), result); else if constexpr (IsDataTypeDecimal && IsDataTypeNumber) - convert_result = tryConvertFromDecimal(vec_from[i], scale, result); + convert_result = tryConvertFromDecimal(vec_from[i], col_from->getScale(), result); else if constexpr (IsDataTypeNumber && IsDataTypeDecimal) - convert_result = tryConvertToDecimal(vec_from[i], scale, result); + convert_result = tryConvertToDecimal(vec_from[i], col_to->getScale(), result); if (convert_result) vec_to[i] = result; @@ -226,11 +227,11 @@ struct ConvertImpl else { if constexpr (IsDataTypeDecimal && IsDataTypeDecimal) - vec_to[i] = convertDecimals(vec_from[i], scale, scale); + vec_to[i] = convertDecimals(vec_from[i], col_from->getScale(), col_to->getScale()); else if constexpr (IsDataTypeDecimal && IsDataTypeNumber) - vec_to[i] = convertFromDecimal(vec_from[i], scale); + vec_to[i] = convertFromDecimal(vec_from[i], col_from->getScale()); else if constexpr (IsDataTypeNumber && IsDataTypeDecimal) - vec_to[i] = convertToDecimal(vec_from[i], scale); + vec_to[i] = convertToDecimal(vec_from[i], col_to->getScale()); else throw Exception("Unsupported data type in conversion function", ErrorCodes::CANNOT_CONVERT_TYPE); } From 91878b97e4a23315c58e33fcec63f16a37a07980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ra=C3=BAl=20Mar=C3=ADn?= Date: Mon, 24 Jan 2022 10:56:52 +0100 Subject: [PATCH 15/57] Address AggregateFunctionIf.cpp warnings /mnt/ch/ClickHouse/src/AggregateFunctions/AggregateFunctionIf.cpp:75:79: warning: declaration shadows a field of 'AggregateFunctionIfNullUnary' [-Wshadow] inline bool singleFilter(const IColumn ** columns, size_t row_num, size_t num_arguments) const ^ /mnt/ch/ClickHouse/src/AggregateFunctions/AggregateFunctionIf.cpp:53:12: note: previous declaration is here size_t num_arguments; ^ --- src/AggregateFunctions/AggregateFunctionIf.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/AggregateFunctions/AggregateFunctionIf.cpp b/src/AggregateFunctions/AggregateFunctionIf.cpp index 5ba54ff8505..ce71e76de43 100644 --- a/src/AggregateFunctions/AggregateFunctionIf.cpp +++ b/src/AggregateFunctions/AggregateFunctionIf.cpp @@ -72,7 +72,7 @@ private: using Base = AggregateFunctionNullBase>; - inline bool singleFilter(const IColumn ** columns, size_t row_num, size_t num_arguments) const + inline bool singleFilter(const IColumn ** columns, size_t row_num) const { const IColumn * filter_column = columns[num_arguments - 1]; @@ -112,7 +112,7 @@ public: { const ColumnNullable * column = assert_cast(columns[0]); const IColumn * nested_column = &column->getNestedColumn(); - if (!column->isNullAt(row_num) && singleFilter(columns, row_num, num_arguments)) + if (!column->isNullAt(row_num) && singleFilter(columns, row_num)) { this->setFlag(place); this->nested_function->add(this->nestedPlace(place), &nested_column, row_num, arena); From 6adb3aa49a9087ef29cdae96f34da49109307bfc Mon Sep 17 00:00:00 2001 From: Amos Bird Date: Tue, 25 Jan 2022 11:43:28 +0800 Subject: [PATCH 16/57] Fix missing -Werror --- CMakeLists.txt | 24 +++++++++++++----------- cmake/darwin/default_libs.cmake | 4 ---- cmake/freebsd/default_libs.cmake | 4 ---- cmake/linux/default_libs.cmake | 3 --- 4 files changed, 13 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b11ea650dc7..8494aee6448 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -403,17 +403,6 @@ else () option(WERROR "Enable -Werror compiler option" ON) endif () -if (WERROR) - # Don't pollute CMAKE_CXX_FLAGS with -Werror as it will break some CMake checks. - # Instead, adopt modern cmake usage requirement. - target_compile_options(global-libs INTERFACE "-Werror") -endif () - -# Make this extra-checks for correct library dependencies. -if (OS_LINUX AND NOT SANITIZE) - target_link_options(global-libs INTERFACE "-Wl,--no-undefined") -endif () - # Increase stack size on Musl. We need big stack for our recursive-descend parser. if (USE_MUSL) set (CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,stack-size=2097152") @@ -421,6 +410,7 @@ endif () include(cmake/dbms_glob_sources.cmake) +add_library(global-group INTERFACE) if (OS_LINUX OR OS_ANDROID) include(cmake/linux/default_libs.cmake) elseif (OS_DARWIN) @@ -428,6 +418,18 @@ elseif (OS_DARWIN) elseif (OS_FREEBSD) include(cmake/freebsd/default_libs.cmake) endif () +link_libraries(global-group) + +if (WERROR) + # Don't pollute CMAKE_CXX_FLAGS with -Werror as it will break some CMake checks. + # Instead, adopt modern cmake usage requirement. + target_compile_options(global-group INTERFACE "-Werror") +endif () + +# Make this extra-checks for correct library dependencies. +if (OS_LINUX AND NOT SANITIZE) + target_link_options(global-group INTERFACE "-Wl,--no-undefined") +endif () ###################################### ### Add targets below this comment ### diff --git a/cmake/darwin/default_libs.cmake b/cmake/darwin/default_libs.cmake index a6ee800d59b..ca4beaea8b6 100644 --- a/cmake/darwin/default_libs.cmake +++ b/cmake/darwin/default_libs.cmake @@ -24,14 +24,10 @@ find_package(Threads REQUIRED) include (cmake/find/cxx.cmake) -add_library(global-group INTERFACE) - target_link_libraries(global-group INTERFACE $ ) -link_libraries(global-group) - # FIXME: remove when all contribs will get custom cmake lists install( TARGETS global-group global-libs diff --git a/cmake/freebsd/default_libs.cmake b/cmake/freebsd/default_libs.cmake index a5847c95387..f7a333df6e6 100644 --- a/cmake/freebsd/default_libs.cmake +++ b/cmake/freebsd/default_libs.cmake @@ -25,14 +25,10 @@ find_package(Threads REQUIRED) include (cmake/find/unwind.cmake) include (cmake/find/cxx.cmake) -add_library(global-group INTERFACE) - target_link_libraries(global-group INTERFACE $ ) -link_libraries(global-group) - # FIXME: remove when all contribs will get custom cmake lists install( TARGETS global-group global-libs diff --git a/cmake/linux/default_libs.cmake b/cmake/linux/default_libs.cmake index 426ae482ea3..98951822015 100644 --- a/cmake/linux/default_libs.cmake +++ b/cmake/linux/default_libs.cmake @@ -45,15 +45,12 @@ endif () include (cmake/find/unwind.cmake) include (cmake/find/cxx.cmake) -add_library(global-group INTERFACE) target_link_libraries(global-group INTERFACE -Wl,--start-group $ -Wl,--end-group ) -link_libraries(global-group) - # FIXME: remove when all contribs will get custom cmake lists install( TARGETS global-group global-libs From 6c0959b907a4e47008b34babe2f7c8e51f7b63c0 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Fri, 28 Jan 2022 03:25:15 +0300 Subject: [PATCH 17/57] fix asynchronous inserts with Native format --- src/Processors/Formats/Impl/NativeFormat.cpp | 20 +++++--- .../02187_async_inserts_all_formats.python | 50 +++++++++++++++++++ .../02187_async_inserts_all_formats.reference | 40 +++++++++++++++ .../02187_async_inserts_all_formats.sh | 9 ++++ .../0_stateless/helpers/pure_http_client.py | 42 ++++++++++------ 5 files changed, 141 insertions(+), 20 deletions(-) create mode 100644 tests/queries/0_stateless/02187_async_inserts_all_formats.python create mode 100644 tests/queries/0_stateless/02187_async_inserts_all_formats.reference create mode 100755 tests/queries/0_stateless/02187_async_inserts_all_formats.sh diff --git a/src/Processors/Formats/Impl/NativeFormat.cpp b/src/Processors/Formats/Impl/NativeFormat.cpp index 19e2ede6b65..bd95cfd6376 100644 --- a/src/Processors/Formats/Impl/NativeFormat.cpp +++ b/src/Processors/Formats/Impl/NativeFormat.cpp @@ -15,21 +15,22 @@ namespace DB class NativeInputFormat final : public IInputFormat { public: - NativeInputFormat(ReadBuffer & buf, const Block & header) - : IInputFormat(header, buf) - , reader(buf, header, 0) {} + NativeInputFormat(ReadBuffer & buf, const Block & header_) + : IInputFormat(header_, buf) + , reader(std::make_unique(buf, header_, 0)) + , header(header_) {} String getName() const override { return "Native"; } void resetParser() override { IInputFormat::resetParser(); - reader.resetParser(); + reader->resetParser(); } Chunk generate() override { - auto block = reader.read(); + auto block = reader->read(); if (!block) return {}; @@ -40,8 +41,15 @@ public: return Chunk(block.getColumns(), num_rows); } + void setReadBuffer(ReadBuffer & in_) override + { + reader = std::make_unique(in_, header, 0); + IInputFormat::setReadBuffer(in_); + } + private: - NativeReader reader; + std::unique_ptr reader; + Block header; }; class NativeOutputFormat final : public IOutputFormat diff --git a/tests/queries/0_stateless/02187_async_inserts_all_formats.python b/tests/queries/0_stateless/02187_async_inserts_all_formats.python new file mode 100644 index 00000000000..0a909451259 --- /dev/null +++ b/tests/queries/0_stateless/02187_async_inserts_all_formats.python @@ -0,0 +1,50 @@ +#!/usr/bin/env python3 +import os +import sys + +CURDIR = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, os.path.join(CURDIR, 'helpers')) + +CLICKHOUSE_URL = os.environ.get('CLICKHOUSE_URL') +CLICKHOUSE_TMP = os.environ.get('CLICKHOUSE_TMP') + +from pure_http_client import ClickHouseClient + +client = ClickHouseClient() + +def run_test(data_format, gen_data_template, settings): + print(data_format) + client.query("TRUNCATE TABLE t_async_insert") + + expected = client.query(gen_data_template.format("TSV")).strip() + data = client.query(gen_data_template.format(data_format), settings=settings,binary_result=True) + + insert_query = "INSERT INTO t_async_insert FORMAT {}".format(data_format) + client.query_with_data(insert_query, data, settings=settings) + + result = client.query("SELECT * FROM t_async_insert FORMAT TSV").strip() + if result != expected: + print("Failed for format {}.\nExpected:\n{}\nGot:\n{}\n".format(data_format, expected, result)) + exit(1) + +formats = client.query("SELECT name FROM system.formats WHERE is_input AND is_output \ + AND name NOT IN ('CapnProto', 'RawBLOB', 'Template', 'ProtobufSingle', 'LineAsString', 'Protobuf') ORDER BY name").strip().split('\n') + +# Generic formats +client.query("DROP TABLE IF EXISTS t_async_insert") +client.query("CREATE TABLE t_async_insert (id UInt64, s String, arr Array(UInt64)) ENGINE = Memory") +gen_data_query = "SELECT number AS id, toString(number) AS s, range(number) AS arr FROM numbers(10) FORMAT {}" + +for data_format in formats: + run_test(data_format, gen_data_query, settings={"async_insert": 1, "wait_for_async_insert": 1}) + +# LineAsString +client.query("DROP TABLE IF EXISTS t_async_insert") +client.query("CREATE TABLE t_async_insert (s String) ENGINE = Memory") +gen_data_query = "SELECT toString(number) AS s FROM numbers(10) FORMAT {}" + +run_test('LineAsString', gen_data_query, settings={"async_insert": 1, "wait_for_async_insert": 1}) + +# TODO: add CapnProto and Protobuf + +print("OK") diff --git a/tests/queries/0_stateless/02187_async_inserts_all_formats.reference b/tests/queries/0_stateless/02187_async_inserts_all_formats.reference new file mode 100644 index 00000000000..b4a5b6c3a42 --- /dev/null +++ b/tests/queries/0_stateless/02187_async_inserts_all_formats.reference @@ -0,0 +1,40 @@ +Arrow +ArrowStream +Avro +CSV +CSVWithNames +CSVWithNamesAndTypes +CustomSeparated +CustomSeparatedWithNames +CustomSeparatedWithNamesAndTypes +JSONCompactEachRow +JSONCompactEachRowWithNames +JSONCompactEachRowWithNamesAndTypes +JSONCompactStringsEachRow +JSONCompactStringsEachRowWithNames +JSONCompactStringsEachRowWithNamesAndTypes +JSONEachRow +JSONStringsEachRow +MsgPack +Native +ORC +Parquet +RowBinary +RowBinaryWithNames +RowBinaryWithNamesAndTypes +TSKV +TSV +TSVRaw +TSVRawWithNames +TSVRawWithNamesAndTypes +TSVWithNames +TSVWithNamesAndTypes +TabSeparated +TabSeparatedRaw +TabSeparatedRawWithNames +TabSeparatedRawWithNamesAndTypes +TabSeparatedWithNames +TabSeparatedWithNamesAndTypes +Values +LineAsString +OK diff --git a/tests/queries/0_stateless/02187_async_inserts_all_formats.sh b/tests/queries/0_stateless/02187_async_inserts_all_formats.sh new file mode 100755 index 00000000000..0031f72fbe5 --- /dev/null +++ b/tests/queries/0_stateless/02187_async_inserts_all_formats.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +# Tags: no-fasttest + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +# We should have correct env vars from shell_config.sh to run this test +python3 "$CURDIR"/02187_async_inserts_all_formats.python diff --git a/tests/queries/0_stateless/helpers/pure_http_client.py b/tests/queries/0_stateless/helpers/pure_http_client.py index 9f79c4ac529..3335f141bb5 100644 --- a/tests/queries/0_stateless/helpers/pure_http_client.py +++ b/tests/queries/0_stateless/helpers/pure_http_client.py @@ -14,22 +14,23 @@ class ClickHouseClient: def __init__(self, host = CLICKHOUSE_SERVER_URL_STR): self.host = host - def query(self, query, connection_timeout = 1500): + def query(self, query, connection_timeout=1500, settings=dict(), binary_result=False): NUMBER_OF_TRIES = 30 DELAY = 10 + params = { + 'timeout_before_checking_execution_speed': 120, + 'max_execution_time': 6000, + 'database': CLICKHOUSE_DATABASE, + } + + # Add extra settings to params + params = {**params, **settings} + for i in range(NUMBER_OF_TRIES): - r = requests.post( - self.host, - params = { - 'timeout_before_checking_execution_speed': 120, - 'max_execution_time': 6000, - 'database': CLICKHOUSE_DATABASE - }, - timeout = connection_timeout, - data = query) + r = requests.post(self.host, params=params, timeout=connection_timeout, data=query) if r.status_code == 200: - return r.text + return r.content if binary_result else r.text else: print('ATTENTION: try #%d failed' % i) if i != (NUMBER_OF_TRIES-1): @@ -44,9 +45,22 @@ class ClickHouseClient: df = pd.read_csv(io.StringIO(data), sep = '\t') return df - def query_with_data(self, query, content): - content = content.encode('utf-8') - r = requests.post(self.host, data=content) + def query_with_data(self, query, data, connection_timeout=1500, settings=dict()): + params = { + 'query': query, + 'timeout_before_checking_execution_speed': 120, + 'max_execution_time': 6000, + 'database': CLICKHOUSE_DATABASE, + } + + headers = { + "Content-Type": "application/binary" + } + + # Add extra settings to params + params = {**params, **settings} + + r = requests.post(self.host, params=params, timeout=connection_timeout, data=data, headers=headers) result = r.text if r.status_code == 200: return result From 4e0712207945131b9b39203cb5efcb1f77910fca Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Thu, 27 Jan 2022 13:54:38 +0300 Subject: [PATCH 18/57] Slightly optimize ColumnArray::get()/operator[] (by using reserve over resize) Note, that simple "SELECT range(100)" will execute ColumnArray::operator[] 14 times (most of them from DB::checkColumnStructure()) Signed-off-by: Azat Khuzhin --- src/Columns/ColumnArray.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/Columns/ColumnArray.cpp b/src/Columns/ColumnArray.cpp index 929c0153a0a..1d312ddbc7d 100644 --- a/src/Columns/ColumnArray.cpp +++ b/src/Columns/ColumnArray.cpp @@ -134,10 +134,11 @@ Field ColumnArray::operator[](size_t n) const throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Array of size {} is too large to be manipulated as single field, maximum size {}", size, max_array_size_as_field); - Array res(size); + Array res; + res.reserve(size); for (size_t i = 0; i < size; ++i) - res[i] = getData()[offset + i]; + res.push_back(getData()[offset + i]); return res; } @@ -152,11 +153,12 @@ void ColumnArray::get(size_t n, Field & res) const throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Array of size {} is too large to be manipulated as single field, maximum size {}", size, max_array_size_as_field); - res = Array(size); + res = Array(); Array & res_arr = DB::get(res); + res_arr.reserve(size); for (size_t i = 0; i < size; ++i) - getData().get(offset + i, res_arr[i]); + res_arr.push_back(getData()[offset + i]); } From 7a3324b29aec78ad7f77d514f709346b56386038 Mon Sep 17 00:00:00 2001 From: lgbo-ustc Date: Fri, 28 Jan 2022 14:03:34 +0800 Subject: [PATCH 19/57] add expire() in PoolBase --- src/Common/PoolBase.h | 29 +++++++++++++++- src/Common/tests/gtest_poolbase.cpp | 52 +++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 1 deletion(-) create mode 100644 src/Common/tests/gtest_poolbase.cpp diff --git a/src/Common/PoolBase.h b/src/Common/PoolBase.h index 85d4e84abca..ab3f717e509 100644 --- a/src/Common/PoolBase.h +++ b/src/Common/PoolBase.h @@ -41,6 +41,7 @@ private: ObjectPtr object; bool in_use = false; + bool is_expired = false; PoolBase & pool; }; @@ -87,6 +88,15 @@ public: Object & operator*() & { return *data->data.object; } const Object & operator*() const & { return *data->data.object; } + /** + * Expire an object to make it reallocated later + */ + void expire() + { + std::unique_lock lock(data->data.pool.mutex); + data->data.is_expired = true; + } + bool isNull() const { return data == nullptr; } PoolBase * getPool() const @@ -113,7 +123,17 @@ public: { for (auto & item : items) if (!item->in_use) - return Entry(*item); + { + if (!item->is_expired) + return Entry(*item); + else + { + expireObject(item->object); + item->object = allocObject(); + item->is_expired = false; + return Entry(*item); + } + } if (items.size() < max_items) { @@ -139,6 +159,12 @@ public: items.emplace_back(std::make_shared(allocObject(), *this)); } + inline size_t aliveSize() + { + std::unique_lock lock(mutex); + return items.size(); + } + private: /** The maximum size of the pool. */ unsigned max_items; @@ -162,4 +188,5 @@ protected: /** Creates a new object to put into the pool. */ virtual ObjectPtr allocObject() = 0; + virtual void expireObject(ObjectPtr) {} }; diff --git a/src/Common/tests/gtest_poolbase.cpp b/src/Common/tests/gtest_poolbase.cpp new file mode 100644 index 00000000000..988f499ead0 --- /dev/null +++ b/src/Common/tests/gtest_poolbase.cpp @@ -0,0 +1,52 @@ +#include +#include +#include +#include +using namespace DB; + +class PoolObject +{ +public: + int x = 0; +}; + +class MyPoolBase : public PoolBase +{ +public: + using Object = PoolBase::Object; + using ObjectPtr = std::shared_ptr; + using Ptr = PoolBase::Ptr; + + int last_destroy_value = 0; + MyPoolBase() : PoolBase(100, &Poco::Logger::get("MyPoolBase")) { } + +protected: + ObjectPtr allocObject() override { return std::make_shared(); } + + void expireObject(ObjectPtr obj) override + { + LOG_TRACE(log, "expire object"); + ASSERT_TRUE(obj->x == 100); + last_destroy_value = obj->x; + } +}; + +TEST(PoolBase, testDestroy1) +{ + MyPoolBase pool; + { + auto obj_entry = pool.get(-1); + ASSERT_TRUE(!obj_entry.isNull()); + obj_entry->x = 100; + obj_entry.expire(); + } + ASSERT_EQ(1, pool.aliveSize()); + + { + auto obj_entry = pool.get(-1); + ASSERT_TRUE(!obj_entry.isNull()); + ASSERT_EQ(obj_entry->x, 0); + ASSERT_EQ(1, pool.aliveSize()); + } + ASSERT_EQ(100, pool.last_destroy_value); +} From 438e0ab0017bbe8344a47362c3222f104d681125 Mon Sep 17 00:00:00 2001 From: cnmade Date: Fri, 28 Jan 2022 15:25:52 +0800 Subject: [PATCH 20/57] Fixed zh...statements/set: rename old file to bak --- docs/zh/sql-reference/statements/{set.md => set.md.bak} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/zh/sql-reference/statements/{set.md => set.md.bak} (100%) diff --git a/docs/zh/sql-reference/statements/set.md b/docs/zh/sql-reference/statements/set.md.bak similarity index 100% rename from docs/zh/sql-reference/statements/set.md rename to docs/zh/sql-reference/statements/set.md.bak From f469df607611959d05355e387a8d1dbf853e59c1 Mon Sep 17 00:00:00 2001 From: cnmade Date: Fri, 28 Jan 2022 15:32:11 +0800 Subject: [PATCH 21/57] Fixed zh...statements/set: translate to zh and remove bak file --- docs/zh/sql-reference/statements/set.md | 23 +++++++++++++++++++++ docs/zh/sql-reference/statements/set.md.bak | 1 - 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 docs/zh/sql-reference/statements/set.md delete mode 120000 docs/zh/sql-reference/statements/set.md.bak diff --git a/docs/zh/sql-reference/statements/set.md b/docs/zh/sql-reference/statements/set.md new file mode 100644 index 00000000000..a9888a7080e --- /dev/null +++ b/docs/zh/sql-reference/statements/set.md @@ -0,0 +1,23 @@ +--- +toc_priority: 50 +toc_title: SET +--- + +# SET 语句 {#query-set} + +``` sql +SET param = value +``` + +给当前会话的 `param` [配置项](../../operations/settings/index.md)赋值。你不能用这样的方式修改[服务器相关设置](../../operations/server-configuration-parameters/index.md)。 + + +您还可以在单个查询中设置指定设置配置文件中的所有值。 + + + +``` sql +SET profile = 'profile-name-from-the-settings-file' +``` + +更多详情, 详见 [配置项](../../operations/settings/settings.md). diff --git a/docs/zh/sql-reference/statements/set.md.bak b/docs/zh/sql-reference/statements/set.md.bak deleted file mode 120000 index 02e106afc9f..00000000000 --- a/docs/zh/sql-reference/statements/set.md.bak +++ /dev/null @@ -1 +0,0 @@ -../../../en/sql-reference/statements/set.md \ No newline at end of file From f5ffdaac2117f87c8051c0dc002210668fb30248 Mon Sep 17 00:00:00 2001 From: lgbo-ustc Date: Fri, 28 Jan 2022 16:55:17 +0800 Subject: [PATCH 22/57] fixed code styles --- src/Common/PoolBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/PoolBase.h b/src/Common/PoolBase.h index ab3f717e509..654dafaefee 100644 --- a/src/Common/PoolBase.h +++ b/src/Common/PoolBase.h @@ -89,7 +89,7 @@ public: const Object & operator*() const & { return *data->data.object; } /** - * Expire an object to make it reallocated later + * Expire an object to make it reallocated later. */ void expire() { From c57639314356a251636c5e59e9e14457425e9383 Mon Sep 17 00:00:00 2001 From: lgbo-ustc Date: Fri, 28 Jan 2022 18:34:54 +0800 Subject: [PATCH 23/57] fixed code styles --- src/Common/PoolBase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/PoolBase.h b/src/Common/PoolBase.h index 654dafaefee..d35d487e18d 100644 --- a/src/Common/PoolBase.h +++ b/src/Common/PoolBase.h @@ -123,7 +123,7 @@ public: { for (auto & item : items) if (!item->in_use) - { + { if (!item->is_expired) return Entry(*item); else From b0c862c2974a1fb3a8ce6dc0a3c3f79c7432ce51 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Wed, 26 Jan 2022 11:33:52 +0300 Subject: [PATCH 24/57] Fix memory accounting for queries that uses < max_untracker_memory MemoryTracker starts accounting memory directly only after per-thread allocation exceeded max_untracker_memory (or memory_profiler_step). But even memory under this limit should be accounted too, and there is code to do this in ThreadStatus dtor, however due to PullingAsyncPipelineExecutor detached the query from thread group that memory was not accounted. So remove CurrentThread::detachQueryIfNotDetached() from threads that uses ThreadFromGlobalPool since it has ThreadStatus, and the query will be detached using CurrentThread::defaultThreadDeleter. Note, that before this patch memory accounting works for HTTP queries due to it had been accounted from ParallelFormattingOutputFormat, but not for TCP. Signed-off-by: Azat Khuzhin --- src/Core/BackgroundSchedulePool.cpp | 3 --- src/Interpreters/ExternalLoader.cpp | 6 ------ src/Processors/Executors/CompletedPipelineExecutor.cpp | 6 ------ src/Processors/Executors/PipelineExecutor.cpp | 5 ----- .../Executors/PullingAsyncPipelineExecutor.cpp | 7 ------- .../Executors/PushingAsyncPipelineExecutor.cpp | 8 -------- .../Formats/Impl/ParallelParsingInputFormat.cpp | 9 --------- src/Storages/MergeTree/MergeTreeData.cpp | 5 ----- src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp | 2 -- 9 files changed, 51 deletions(-) diff --git a/src/Core/BackgroundSchedulePool.cpp b/src/Core/BackgroundSchedulePool.cpp index 9a42f752db2..18c43d8c45f 100644 --- a/src/Core/BackgroundSchedulePool.cpp +++ b/src/Core/BackgroundSchedulePool.cpp @@ -5,7 +5,6 @@ #include #include #include -#include namespace DB @@ -246,7 +245,6 @@ void BackgroundSchedulePool::threadFunction() setThreadName(thread_name.c_str()); attachToThreadGroup(); - SCOPE_EXIT({ CurrentThread::detachQueryIfNotDetached(); }); while (!shutdown) { @@ -273,7 +271,6 @@ void BackgroundSchedulePool::delayExecutionThreadFunction() setThreadName((thread_name + "/D").c_str()); attachToThreadGroup(); - SCOPE_EXIT({ CurrentThread::detachQueryIfNotDetached(); }); while (!shutdown) { diff --git a/src/Interpreters/ExternalLoader.cpp b/src/Interpreters/ExternalLoader.cpp index b2cd9495feb..ae4eb748cc3 100644 --- a/src/Interpreters/ExternalLoader.cpp +++ b/src/Interpreters/ExternalLoader.cpp @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -969,11 +968,6 @@ private: if (thread_group) CurrentThread::attachTo(thread_group); - SCOPE_EXIT_SAFE( - if (thread_group) - CurrentThread::detachQueryIfNotDetached(); - ); - LOG_TRACE(log, "Start loading object '{}'", name); try { diff --git a/src/Processors/Executors/CompletedPipelineExecutor.cpp b/src/Processors/Executors/CompletedPipelineExecutor.cpp index 45b02cba298..8ec1916f4ce 100644 --- a/src/Processors/Executors/CompletedPipelineExecutor.cpp +++ b/src/Processors/Executors/CompletedPipelineExecutor.cpp @@ -4,7 +4,6 @@ #include #include #include -#include #include namespace DB @@ -40,11 +39,6 @@ static void threadFunction(CompletedPipelineExecutor::Data & data, ThreadGroupSt if (thread_group) CurrentThread::attachTo(thread_group); - SCOPE_EXIT_SAFE( - if (thread_group) - CurrentThread::detachQueryIfNotDetached(); - ); - data.executor->execute(num_threads); } catch (...) diff --git a/src/Processors/Executors/PipelineExecutor.cpp b/src/Processors/Executors/PipelineExecutor.cpp index e722f8718f7..80aacf14fe6 100644 --- a/src/Processors/Executors/PipelineExecutor.cpp +++ b/src/Processors/Executors/PipelineExecutor.cpp @@ -301,11 +301,6 @@ void PipelineExecutor::executeImpl(size_t num_threads) if (thread_group) CurrentThread::attachTo(thread_group); - SCOPE_EXIT_SAFE( - if (thread_group) - CurrentThread::detachQueryIfNotDetached(); - ); - try { executeSingleThread(thread_num); diff --git a/src/Processors/Executors/PullingAsyncPipelineExecutor.cpp b/src/Processors/Executors/PullingAsyncPipelineExecutor.cpp index 0ba07df95a6..198d5ce5d8d 100644 --- a/src/Processors/Executors/PullingAsyncPipelineExecutor.cpp +++ b/src/Processors/Executors/PullingAsyncPipelineExecutor.cpp @@ -4,9 +4,7 @@ #include #include #include - #include -#include namespace DB { @@ -77,11 +75,6 @@ static void threadFunction(PullingAsyncPipelineExecutor::Data & data, ThreadGrou if (thread_group) CurrentThread::attachTo(thread_group); - SCOPE_EXIT_SAFE( - if (thread_group) - CurrentThread::detachQueryIfNotDetached(); - ); - data.executor->execute(num_threads); } catch (...) diff --git a/src/Processors/Executors/PushingAsyncPipelineExecutor.cpp b/src/Processors/Executors/PushingAsyncPipelineExecutor.cpp index 68898bdc2c2..6c2e62b77dc 100644 --- a/src/Processors/Executors/PushingAsyncPipelineExecutor.cpp +++ b/src/Processors/Executors/PushingAsyncPipelineExecutor.cpp @@ -2,11 +2,8 @@ #include #include #include -#include - #include #include -#include #include namespace DB @@ -107,11 +104,6 @@ static void threadFunction(PushingAsyncPipelineExecutor::Data & data, ThreadGrou if (thread_group) CurrentThread::attachTo(thread_group); - SCOPE_EXIT_SAFE( - if (thread_group) - CurrentThread::detachQueryIfNotDetached(); - ); - data.executor->execute(num_threads); } catch (...) diff --git a/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp b/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp index 213226c9d68..360f1ec9bf0 100644 --- a/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp +++ b/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp @@ -2,17 +2,12 @@ #include #include #include -#include namespace DB { void ParallelParsingInputFormat::segmentatorThreadFunction(ThreadGroupStatusPtr thread_group) { - SCOPE_EXIT_SAFE( - if (thread_group) - CurrentThread::detachQueryIfNotDetached(); - ); if (thread_group) CurrentThread::attachTo(thread_group); @@ -59,10 +54,6 @@ void ParallelParsingInputFormat::segmentatorThreadFunction(ThreadGroupStatusPtr void ParallelParsingInputFormat::parserThreadFunction(ThreadGroupStatusPtr thread_group, size_t current_ticket_number) { - SCOPE_EXIT_SAFE( - if (thread_group) - CurrentThread::detachQueryIfNotDetached(); - ); if (thread_group) CurrentThread::attachTo(thread_group); diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index 9a455c2d93c..eb84782abc5 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -67,7 +67,6 @@ #include #include -#include #include #include @@ -1590,10 +1589,6 @@ void MergeTreeData::clearPartsFromFilesystem(const DataPartsVector & parts_to_re { pool.scheduleOrThrowOnError([&, thread_group = CurrentThread::getGroup()] { - SCOPE_EXIT_SAFE( - if (thread_group) - CurrentThread::detachQueryIfNotDetached(); - ); if (thread_group) CurrentThread::attachTo(thread_group); diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index cdedd37e14a..0f8bc02475a 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -1,5 +1,4 @@ #include /// For calculations related to sampling coefficients. -#include #include #include @@ -988,7 +987,6 @@ RangesInDataParts MergeTreeDataSelectExecutor::filterPartsByPrimaryKeyAndSkipInd for (size_t part_index = 0; part_index < parts.size(); ++part_index) pool.scheduleOrThrowOnError([&, part_index, thread_group = CurrentThread::getGroup()] { - SCOPE_EXIT_SAFE(if (thread_group) CurrentThread::detachQueryIfNotDetached();); if (thread_group) CurrentThread::attachTo(thread_group); From 1519985c9866c3ff9dbc18930a8b54106dcf72c3 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Thu, 27 Jan 2022 11:55:38 +0300 Subject: [PATCH 25/57] Fix possible "Can't attach query to the thread, it is already attached" After detachQueryIfNotDetached() had been removed it is not enough to use attachTo() for ThreadPool (scheduleOrThrowOnError()) since the query may be already attached, if the thread doing multiple jobs, so CurrentThread::attachToIfDetached() should be used instead. This should fix all the places from the failures on CI [1]: $ fgrep DB::CurrentThread::attachTo -A1 ~/Downloads/47.txt | fgrep -v attachTo | cut -d' ' -f5,6 | sort | uniq -c 92 -- 2 /fasttest-workspace/build/../../ClickHouse/contrib/libcxx/include/deque:1393: DB::ParallelParsingInputFormat::parserThreadFunction(std::__1::shared_ptr, 4 /fasttest-workspace/build/../../ClickHouse/src/Storages/MergeTree/MergeTreeData.cpp:1595: void 87 /fasttest-workspace/build/../../ClickHouse/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp:993: void [1]: https://github.com/ClickHouse/ClickHouse/runs/4954466034?check_suite_focus=true Signed-off-by: Azat Khuzhin --- src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp | 2 +- src/Storages/MergeTree/MergeTreeData.cpp | 2 +- src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp b/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp index 360f1ec9bf0..bfdb9de7d26 100644 --- a/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp +++ b/src/Processors/Formats/Impl/ParallelParsingInputFormat.cpp @@ -55,7 +55,7 @@ void ParallelParsingInputFormat::segmentatorThreadFunction(ThreadGroupStatusPtr void ParallelParsingInputFormat::parserThreadFunction(ThreadGroupStatusPtr thread_group, size_t current_ticket_number) { if (thread_group) - CurrentThread::attachTo(thread_group); + CurrentThread::attachToIfDetached(thread_group); const auto parser_unit_number = current_ticket_number % processing_units.size(); auto & unit = processing_units[parser_unit_number]; diff --git a/src/Storages/MergeTree/MergeTreeData.cpp b/src/Storages/MergeTree/MergeTreeData.cpp index eb84782abc5..67c304b4511 100644 --- a/src/Storages/MergeTree/MergeTreeData.cpp +++ b/src/Storages/MergeTree/MergeTreeData.cpp @@ -1590,7 +1590,7 @@ void MergeTreeData::clearPartsFromFilesystem(const DataPartsVector & parts_to_re pool.scheduleOrThrowOnError([&, thread_group = CurrentThread::getGroup()] { if (thread_group) - CurrentThread::attachTo(thread_group); + CurrentThread::attachToIfDetached(thread_group); LOG_DEBUG(log, "Removing part from filesystem {}", part->name); part->remove(); diff --git a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp index 0f8bc02475a..a277cda9e50 100644 --- a/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp +++ b/src/Storages/MergeTree/MergeTreeDataSelectExecutor.cpp @@ -988,7 +988,7 @@ RangesInDataParts MergeTreeDataSelectExecutor::filterPartsByPrimaryKeyAndSkipInd pool.scheduleOrThrowOnError([&, part_index, thread_group = CurrentThread::getGroup()] { if (thread_group) - CurrentThread::attachTo(thread_group); + CurrentThread::attachToIfDetached(thread_group); process_part(part_index); }); From 162f96f8e181795225bf3c9edbb22d8e37360a8b Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Thu, 27 Jan 2022 18:51:36 +0300 Subject: [PATCH 26/57] Get back detachQueryIfNotDetached() into ExternalLoader ExternalLoader from ThreadPool (async loading) is done from the server context, not from the query context, and the context will already go away, so we should detachQueryIfNotDetached() to avoid trigger assertion in ThreadStatus. CI: https://s3.amazonaws.com/clickhouse-test-reports/34001/8cace291d17fa9553a98b2a1e8bf15b30fe5a1bd/stateless_tests__debug__actions__[3/3].html Signed-off-by: Azat Khuzhin --- src/Interpreters/ExternalLoader.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Interpreters/ExternalLoader.cpp b/src/Interpreters/ExternalLoader.cpp index ae4eb748cc3..aab3a9e7437 100644 --- a/src/Interpreters/ExternalLoader.cpp +++ b/src/Interpreters/ExternalLoader.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -965,6 +966,11 @@ private: /// Does the loading, possibly in the separate thread. void doLoading(const String & name, size_t loading_id, bool forced_to_reload, size_t min_id_to_finish_loading_dependencies_, bool async, ThreadGroupStatusPtr thread_group = {}) { + SCOPE_EXIT_SAFE( + if (thread_group) + CurrentThread::detachQueryIfNotDetached(); + ); + if (thread_group) CurrentThread::attachTo(thread_group); From 0e1c6b6b9c2124a5dab43cba87c9246e9d888545 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Thu, 27 Jan 2022 18:12:26 +0300 Subject: [PATCH 27/57] Make 01810_max_part_removal_threads_long more accurate v0: After parts removing stop attach/detach for each remove, there are only 11 threads, primary query and 10 for removal. CI: https://s3.amazonaws.com/clickhouse-test-reports/34001/8cace291d17fa9553a98b2a1e8bf15b30fe5a1bd/stateless_tests__debug__actions__[3/3].html v2: Make 01810_max_part_removal_threads_long more deterministic Sometimes, if removing parts will be too fast (like in [1]), then it may use less threads, since thread pool will reuse existing, so increase number of rows per part and compare threads directly w/o throwIf(). [1]: https://s3.amazonaws.com/clickhouse-test-reports/34001/e74b6379a58c4607bdb54cd4c457410166e869ca/stateless_tests_flaky_check__address__actions_.html v3: Less flaky 01810_max_part_removal_threads_long v4: Make check not strict in 01810_max_part_removal_threads_long Signed-off-by: Azat Khuzhin --- .../01810_max_part_removal_threads_long.sh | 44 ++++++++++++++++--- 1 file changed, 38 insertions(+), 6 deletions(-) diff --git a/tests/queries/0_stateless/01810_max_part_removal_threads_long.sh b/tests/queries/0_stateless/01810_max_part_removal_threads_long.sh index c5aaa794ac9..f5ab71d8d34 100755 --- a/tests/queries/0_stateless/01810_max_part_removal_threads_long.sh +++ b/tests/queries/0_stateless/01810_max_part_removal_threads_long.sh @@ -16,22 +16,54 @@ $CLICKHOUSE_CLIENT -nm -q "create database ordinary_$CLICKHOUSE_DATABASE engine= $CLICKHOUSE_CLIENT -nm -q """ use ordinary_$CLICKHOUSE_DATABASE; drop table if exists data_01810; - create table data_01810 (key Int) Engine=MergeTree() order by key partition by key settings max_part_removal_threads=10, concurrent_part_removal_threshold=49; - insert into data_01810 select * from numbers(50); + + create table data_01810 (key Int) + Engine=MergeTree() + order by key + partition by key%100 + settings max_part_removal_threads=10, concurrent_part_removal_threshold=99, min_bytes_for_wide_part=0; + + insert into data_01810 select * from numbers(100); drop table data_01810 settings log_queries=1; system flush logs; - select throwIf(length(thread_ids)<50) from system.query_log where event_date >= yesterday() and current_database = currentDatabase() and query = 'drop table data_01810 settings log_queries=1;' and type = 'QueryFinish' format Null; + + -- sometimes the same thread can be used to remove part, due to ThreadPool, + -- hence we cannot compare strictly. + select throwIf(not(length(thread_ids) between 6 and 11)) + from system.query_log + where + event_date >= yesterday() and + current_database = currentDatabase() and + query = 'drop table data_01810 settings log_queries=1;' and + type = 'QueryFinish' + format Null; """ # ReplicatedMergeTree $CLICKHOUSE_CLIENT -nm -q """ use ordinary_$CLICKHOUSE_DATABASE; drop table if exists rep_data_01810; - create table rep_data_01810 (key Int) Engine=ReplicatedMergeTree('/clickhouse/tables/$CLICKHOUSE_TEST_ZOOKEEPER_PREFIX/rep_data_01810', '1') order by key partition by key settings max_part_removal_threads=10, concurrent_part_removal_threshold=49; - insert into rep_data_01810 select * from numbers(50); + + create table rep_data_01810 (key Int) + Engine=ReplicatedMergeTree('/clickhouse/tables/$CLICKHOUSE_TEST_ZOOKEEPER_PREFIX/rep_data_01810', '1') + order by key + partition by key%100 + settings max_part_removal_threads=10, concurrent_part_removal_threshold=99, min_bytes_for_wide_part=0; + + insert into rep_data_01810 select * from numbers(100); drop table rep_data_01810 settings log_queries=1; system flush logs; - select throwIf(length(thread_ids)<50) from system.query_log where event_date >= yesterday() and current_database = currentDatabase() and query = 'drop table rep_data_01810 settings log_queries=1;' and type = 'QueryFinish' format Null; + + -- sometimes the same thread can be used to remove part, due to ThreadPool, + -- hence we cannot compare strictly. + select throwIf(not(length(thread_ids) between 6 and 11)) + from system.query_log + where + event_date >= yesterday() and + current_database = currentDatabase() and + query = 'drop table rep_data_01810 settings log_queries=1;' and + type = 'QueryFinish' + format Null; """ $CLICKHOUSE_CLIENT -nm -q "drop database ordinary_$CLICKHOUSE_DATABASE" From 5295adb83d1678cbbb197a145957accf445a1e34 Mon Sep 17 00:00:00 2001 From: Sergei Trifonov Date: Fri, 28 Jan 2022 16:43:25 +0300 Subject: [PATCH 28/57] add c++expr script examples --- utils/c++expr | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/utils/c++expr b/utils/c++expr index ec24b97ebc9..c498e780d05 100755 --- a/utils/c++expr +++ b/utils/c++expr @@ -3,7 +3,7 @@ set -e usage() { cat <&2 -USAGE: c++expr [-c CXX | -C | -I] [-i INCLUDE] [-b STEPS] [-t TESTS] [-o FILE] [-O CXX_OPTS...] [-g 'GLOBAL CODE'] 'MAIN CODE' +USAGE: c++expr [-c CXX | -C | -I] [-i INCLUDE] [-l LIB] [-b STEPS] [-t TESTS] [-o FILE] [-O CXX_OPTS...] [-g 'GLOBAL CODE'] 'MAIN CODE' OPTIONS: -c CXX use specified c++ compiler -C use cmake @@ -15,6 +15,19 @@ OPTIONS: -t TESTS_NUM make program to benchmark specified code snippet and run TESTS_NUM tests -o FILE do not run, just save binary executable file -O CXX_OPTS forward option compiler (e.g. -O "-O3 -std=c++20") +EXAMPLES: + $ c++expr -g 'int fib(int n) { return n < 2 ? n : fib(n-2) + fib(n-1); }' 'OUT(fib(10)) OUT(fib(20)) OUT(fib(30))' + fib(10) -> 55 + fib(20) -> 6765 + fib(30) -> 832040 + $ c++expr -I -i Interpreters/Context.h 'OUT(sizeof(DB::Context))' + sizeof(DB::Context) -> 7776 + $ c++expr -I -i Common/Stopwatch.h -b 10000 'Stopwatch sw;' + Steps per test: 10000 + Test #0: 0.0178 us 5.61798e+07 sps + ... + Test #4: 0.0179 us 5.58659e+07 sps + Average: 0.0179 us 5.58659e+07 sps EOF exit 1 } @@ -37,7 +50,7 @@ CMD_PARAMS= # Parse command line # -if [ "$1" == "--help" ]; then usage; fi +if [ "$1" == "--help" ] || [ -z "$1" ]; then usage; fi while getopts "vc:CIi:l:b:t:o:O:g:" OPT; do case "$OPT" in v) set -x; ;; From 66cb64393beaf418b55bd59c65e1ecf76f783f19 Mon Sep 17 00:00:00 2001 From: Ramazan Polat Date: Fri, 28 Jan 2022 18:57:56 +0300 Subject: [PATCH 29/57] Make clickhouse-diagnostics also work for altinity release Because altinity release has `altinitystable` at the end of version number, `parse_version` function throws parse error. --- utils/clickhouse-diagnostics/clickhouse-diagnostics | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/clickhouse-diagnostics/clickhouse-diagnostics b/utils/clickhouse-diagnostics/clickhouse-diagnostics index ffddee0bdc4..83c0af9cd11 100644 --- a/utils/clickhouse-diagnostics/clickhouse-diagnostics +++ b/utils/clickhouse-diagnostics/clickhouse-diagnostics @@ -953,7 +953,7 @@ def parse_version(version): """ Parse version string. """ - return [int(x) for x in version.strip().split('.')] + return [int(x) for x in version.strip().split('.') if x.isnumeric()] if __name__ == '__main__': From 09a3ab79b795a9ba4159fd9c495d9d0b3c59b1b6 Mon Sep 17 00:00:00 2001 From: Amos Bird Date: Sat, 29 Jan 2022 01:37:39 +0800 Subject: [PATCH 30/57] Fix all const --- src/Interpreters/sortBlock.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Interpreters/sortBlock.cpp b/src/Interpreters/sortBlock.cpp index d8daaac6a36..d25a262997b 100644 --- a/src/Interpreters/sortBlock.cpp +++ b/src/Interpreters/sortBlock.cpp @@ -132,6 +132,18 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) return; ColumnsWithSortDescriptions columns_with_sort_desc = getColumnsWithSortDescription(block, description); + bool all_const = true; + for (const auto & column : columns_with_sort_desc) + { + if (!column.column_const) + { + all_const = false; + break; + } + } + if (all_const) + return; + IColumn::Permutation perm; /// If only one column to sort by if (columns_with_sort_desc.size() == 1) @@ -229,13 +241,9 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) } } } - size_t columns = block.columns(); for (size_t i = 0; i < columns; ++i) - { - if (!isColumnConst(*block.getByPosition(i).column)) - block.getByPosition(i).column = block.getByPosition(i).column->permute(perm, limit); - } + block.getByPosition(i).column = block.getByPosition(i).column->permute(perm, limit); } From c0a5bd187a4e6e66a947be47cc5392bc738b3d81 Mon Sep 17 00:00:00 2001 From: Federico Rodriguez Date: Fri, 28 Jan 2022 14:26:18 -0500 Subject: [PATCH 31/57] ExecuteScalarSubqueriesVisitor missing static const Added string_view --- src/Interpreters/ExecuteScalarSubqueriesVisitor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Interpreters/ExecuteScalarSubqueriesVisitor.cpp b/src/Interpreters/ExecuteScalarSubqueriesVisitor.cpp index 2117eec0063..a81d4204565 100644 --- a/src/Interpreters/ExecuteScalarSubqueriesVisitor.cpp +++ b/src/Interpreters/ExecuteScalarSubqueriesVisitor.cpp @@ -68,7 +68,7 @@ void ExecuteScalarSubqueriesMatcher::visit(ASTPtr & ast, Data & data) static bool worthConvertingToLiteral(const Block & scalar) { const auto * scalar_type_name = scalar.safeGetByPosition(0).type->getFamilyName(); - std::set useless_literal_types = {"Array", "Tuple", "AggregateFunction", "Function", "Set", "LowCardinality"}; + static const std::set useless_literal_types = {"Array", "Tuple", "AggregateFunction", "Function", "Set", "LowCardinality"}; return !useless_literal_types.count(scalar_type_name); } From 2b3ce910c6c4b536c2898612d9fc828873007229 Mon Sep 17 00:00:00 2001 From: Amos Bird Date: Sat, 29 Jan 2022 04:49:58 +0800 Subject: [PATCH 32/57] Fix collation check --- src/Columns/IColumn.h | 13 +++-- src/Interpreters/sortBlock.cpp | 93 +++++++++++----------------------- 2 files changed, 37 insertions(+), 69 deletions(-) diff --git a/src/Columns/IColumn.h b/src/Columns/IColumn.h index b1a6e83ee98..7e5d01ef912 100644 --- a/src/Columns/IColumn.h +++ b/src/Columns/IColumn.h @@ -318,15 +318,18 @@ public: virtual void updatePermutation(bool reverse, size_t limit, int nan_direction_hint, Permutation & res, EqualRanges & equal_ranges) const = 0; /** Equivalent to getPermutation and updatePermutation but collator is used to compare values. - * Supported for String, LowCardinality(String), Nullable(String) and for Array and Tuple, containing them. + * Overrides in String, LowCardinality(String), Nullable(String) and for Array and Tuple, containing them. */ - virtual void getPermutationWithCollation(const Collator &, bool, size_t, int, Permutation &) const + virtual void + getPermutationWithCollation(const Collator &, bool reverse, size_t limit, int nan_direction_hint, Permutation & res) const { - throw Exception("Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, containing them.", ErrorCodes::BAD_COLLATION); + getPermutation(reverse, limit, nan_direction_hint, res); } - virtual void updatePermutationWithCollation(const Collator &, bool, size_t, int, Permutation &, EqualRanges&) const + + virtual void updatePermutationWithCollation( + const Collator &, bool reverse, size_t limit, int nan_direction_hint, Permutation & res, EqualRanges & equal_ranges) const { - throw Exception("Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, containing them.", ErrorCodes::BAD_COLLATION); + updatePermutation(reverse, limit, nan_direction_hint, res, equal_ranges); } /** Copies each element according offsets parameter. diff --git a/src/Interpreters/sortBlock.cpp b/src/Interpreters/sortBlock.cpp index d25a262997b..781e75090d1 100644 --- a/src/Interpreters/sortBlock.cpp +++ b/src/Interpreters/sortBlock.cpp @@ -58,6 +58,14 @@ ColumnsWithSortDescriptions getColumnsWithSortDescription(const Block & block, c ? block.getByName(description[i].column_name).column.get() : block.safeGetByPosition(description[i].column_number).column.get(); + if (isCollationRequired(description[i])) + { + if (!column->isCollationSupported()) + throw Exception( + "Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, " + "containing them.", + ErrorCodes::BAD_COLLATION); + } if (const auto * tuple = typeid_cast(column)) flattenTupleColumnRecursively(res, tuple, &description[i], isColumnConst(*column)); else @@ -150,19 +158,9 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) { bool reverse = columns_with_sort_desc[0].description->direction == -1; const IColumn * column = columns_with_sort_desc[0].column; - bool is_column_const = columns_with_sort_desc[0].column_const; if (isCollationRequired(description[0])) - { - if (!column->isCollationSupported()) - throw Exception( - "Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, " - "containing them.", - ErrorCodes::BAD_COLLATION); - - if (!is_column_const) - column->getPermutationWithCollation(*description[0].collator, reverse, limit, description[0].nulls_direction, perm); - } - else if (!is_column_const) + column->getPermutationWithCollation(*description[0].collator, reverse, limit, description[0].nulls_direction, perm); + else { int nan_direction_hint = columns_with_sort_desc[0].description->nulls_direction; column->getPermutation(reverse, limit, nan_direction_hint, perm); @@ -178,64 +176,31 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) if (limit >= size) limit = 0; - bool need_collation = false; + EqualRanges ranges; + ranges.emplace_back(0, perm.size()); for (const auto & column : columns_with_sort_desc) { + while (!ranges.empty() && limit && limit <= ranges.back().first) + ranges.pop_back(); + + if (ranges.empty()) + break; + + if (column.column_const) + continue; + if (isCollationRequired(*column.description)) { - if (!column.column->isCollationSupported()) - throw Exception( - "Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, " - "containing them.", - ErrorCodes::BAD_COLLATION); - need_collation = true; + column.column->updatePermutationWithCollation( + *column.description->collator, + column.description->direction < 0, + limit, + column.description->nulls_direction, + perm, + ranges); } - } - - if (need_collation) - { - EqualRanges ranges; - ranges.emplace_back(0, perm.size()); - for (const auto & column : columns_with_sort_desc) + else { - while (!ranges.empty() && limit && limit <= ranges.back().first) - ranges.pop_back(); - - if (ranges.empty()) - break; - - if (column.column_const) - continue; - - if (isCollationRequired(*column.description)) - { - column.column->updatePermutationWithCollation( - *column.description->collator, - column.description->direction < 0, - limit, - column.description->nulls_direction, - perm, - ranges); - } - else - { - column.column->updatePermutation( - column.description->direction < 0, limit, column.description->nulls_direction, perm, ranges); - } - } - } - else - { - EqualRanges ranges; - ranges.emplace_back(0, perm.size()); - for (const auto & column : columns_with_sort_desc) - { - while (!ranges.empty() && limit && limit <= ranges.back().first) - ranges.pop_back(); - - if (ranges.empty()) - break; - column.column->updatePermutation( column.description->direction < 0, limit, column.description->nulls_direction, perm, ranges); } From 379f8d3d7e531840503456c84cf000c14df578b0 Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Sat, 29 Jan 2022 01:09:46 +0300 Subject: [PATCH 33/57] Update use.md --- docs/en/sql-reference/statements/use.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/sql-reference/statements/use.md b/docs/en/sql-reference/statements/use.md index 41cba58bb9d..841c23d333d 100644 --- a/docs/en/sql-reference/statements/use.md +++ b/docs/en/sql-reference/statements/use.md @@ -3,14 +3,14 @@ toc_priority: 53 toc_title: USE --- -# USE 语句 {#use} +# USE Statement {#use} ``` sql USE db ``` -用于设置会话的当前数据库。 +Lets you set the current database for the session. -如果查询语句中没有在表名前面以加点的方式指明数据库名, 则用当前数据库进行搜索。 +The current database is used for searching for tables if the database is not explicitly defined in the query with a dot before the table name. -使用 HTTP 协议时无法进行此查询,因为没有会话的概念。 +This query can’t be made when using the HTTP protocol, since there is no concept of a session. From 7c3d6f5fda4302cebdb980b08ae7f2fe4dcc9a95 Mon Sep 17 00:00:00 2001 From: Anton Popov Date: Sat, 29 Jan 2022 01:24:30 +0300 Subject: [PATCH 34/57] Update 02187_async_inserts_all_formats.sh --- tests/queries/0_stateless/02187_async_inserts_all_formats.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/queries/0_stateless/02187_async_inserts_all_formats.sh b/tests/queries/0_stateless/02187_async_inserts_all_formats.sh index 0031f72fbe5..4b0b8d84c58 100755 --- a/tests/queries/0_stateless/02187_async_inserts_all_formats.sh +++ b/tests/queries/0_stateless/02187_async_inserts_all_formats.sh @@ -1,5 +1,5 @@ #!/usr/bin/env bash -# Tags: no-fasttest +# Tags: no-fasttest, long CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) # shellcheck source=../shell_config.sh From 5f1969c6c571f4130d38b6e7ab7e10f5b5b24ced Mon Sep 17 00:00:00 2001 From: lgbo-ustc Date: Sat, 29 Jan 2022 09:46:00 +0800 Subject: [PATCH 35/57] fixed code styles --- src/Common/PoolBase.h | 12 +++++++----- src/Common/tests/gtest_poolbase.cpp | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/Common/PoolBase.h b/src/Common/PoolBase.h index d35d487e18d..a82a6efc4c1 100644 --- a/src/Common/PoolBase.h +++ b/src/Common/PoolBase.h @@ -41,7 +41,7 @@ private: ObjectPtr object; bool in_use = false; - bool is_expired = false; + std::atomic is_expired = false; PoolBase & pool; }; @@ -93,7 +93,6 @@ public: */ void expire() { - std::unique_lock lock(data->data.pool.mutex); data->data.is_expired = true; } @@ -122,10 +121,13 @@ public: while (true) { for (auto & item : items) + { if (!item->in_use) { - if (!item->is_expired) + if (likely(!item->is_expired)) + { return Entry(*item); + } else { expireObject(item->object); @@ -134,7 +136,7 @@ public: return Entry(*item); } } - + } if (items.size() < max_items) { ObjectPtr object = allocObject(); @@ -159,7 +161,7 @@ public: items.emplace_back(std::make_shared(allocObject(), *this)); } - inline size_t aliveSize() + inline size_t size() { std::unique_lock lock(mutex); return items.size(); diff --git a/src/Common/tests/gtest_poolbase.cpp b/src/Common/tests/gtest_poolbase.cpp index 988f499ead0..20c3281c964 100644 --- a/src/Common/tests/gtest_poolbase.cpp +++ b/src/Common/tests/gtest_poolbase.cpp @@ -40,13 +40,13 @@ TEST(PoolBase, testDestroy1) obj_entry->x = 100; obj_entry.expire(); } - ASSERT_EQ(1, pool.aliveSize()); + ASSERT_EQ(1, pool.size()); { auto obj_entry = pool.get(-1); ASSERT_TRUE(!obj_entry.isNull()); ASSERT_EQ(obj_entry->x, 0); - ASSERT_EQ(1, pool.aliveSize()); + ASSERT_EQ(1, pool.size()); } ASSERT_EQ(100, pool.last_destroy_value); } From 47c123de3c264c7d5971c2beb553ea433b236e5a Mon Sep 17 00:00:00 2001 From: cnmade Date: Sat, 29 Jan 2022 16:35:18 +0800 Subject: [PATCH 36/57] Fixed zh...statements/exists: rename old file --- docs/zh/sql-reference/statements/{exists.md => exists.md.bak} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename docs/zh/sql-reference/statements/{exists.md => exists.md.bak} (100%) diff --git a/docs/zh/sql-reference/statements/exists.md b/docs/zh/sql-reference/statements/exists.md.bak similarity index 100% rename from docs/zh/sql-reference/statements/exists.md rename to docs/zh/sql-reference/statements/exists.md.bak From 36b58881cc3bb2dc777f7d26ab268fff35a66e30 Mon Sep 17 00:00:00 2001 From: cnmade Date: Sat, 29 Jan 2022 16:36:03 +0800 Subject: [PATCH 37/57] Fixed zh...statements/exists: reimport file --- docs/zh/sql-reference/statements/exists.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/zh/sql-reference/statements/exists.md diff --git a/docs/zh/sql-reference/statements/exists.md b/docs/zh/sql-reference/statements/exists.md new file mode 100644 index 00000000000..b7c4a487791 --- /dev/null +++ b/docs/zh/sql-reference/statements/exists.md @@ -0,0 +1,12 @@ +--- +toc_priority: 45 +toc_title: EXISTS +--- + +# EXISTS Statement {#exists-statement} + +``` sql +EXISTS [TEMPORARY] [TABLE|DICTIONARY] [db.]name [INTO OUTFILE filename] [FORMAT format] +``` + +Returns a single `UInt8`-type column, which contains the single value `0` if the table or database does not exist, or `1` if the table exists in the specified database. From 242a39d475d0d0e06ce36aee7fdcdfef914d8ee4 Mon Sep 17 00:00:00 2001 From: cnmade Date: Sat, 29 Jan 2022 16:37:45 +0800 Subject: [PATCH 38/57] Fixed zh...statements/exists: translate to zh --- docs/zh/sql-reference/statements/exists.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/zh/sql-reference/statements/exists.md b/docs/zh/sql-reference/statements/exists.md index b7c4a487791..69b26fea918 100644 --- a/docs/zh/sql-reference/statements/exists.md +++ b/docs/zh/sql-reference/statements/exists.md @@ -3,10 +3,10 @@ toc_priority: 45 toc_title: EXISTS --- -# EXISTS Statement {#exists-statement} +# EXISTS 语句 {#exists-statement} ``` sql EXISTS [TEMPORARY] [TABLE|DICTIONARY] [db.]name [INTO OUTFILE filename] [FORMAT format] ``` -Returns a single `UInt8`-type column, which contains the single value `0` if the table or database does not exist, or `1` if the table exists in the specified database. +返回一个单独的 `UInt8`类型的列,如果表或数据库不存在,则包含一个值 `0`,如果表在指定的数据库中存在,则包含一个值 `1`。 \ No newline at end of file From 4c2b3f73da357eb3b62a942c72956033f7bf5cd0 Mon Sep 17 00:00:00 2001 From: cnmade Date: Sat, 29 Jan 2022 16:38:12 +0800 Subject: [PATCH 39/57] Fixed zh...statements/exists: remove bak file --- docs/zh/sql-reference/statements/exists.md.bak | 1 - 1 file changed, 1 deletion(-) delete mode 120000 docs/zh/sql-reference/statements/exists.md.bak diff --git a/docs/zh/sql-reference/statements/exists.md.bak b/docs/zh/sql-reference/statements/exists.md.bak deleted file mode 120000 index d69e8224fe6..00000000000 --- a/docs/zh/sql-reference/statements/exists.md.bak +++ /dev/null @@ -1 +0,0 @@ -../../../en/sql-reference/statements/exists.md \ No newline at end of file From 06318ebed5b1c29fa9fb85acd1ec27f9a259c668 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 28 Jan 2022 19:41:20 +0300 Subject: [PATCH 40/57] SerializationArray: Use reserve() over resize() Signed-off-by: Azat Khuzhin --- src/DataTypes/Serializations/SerializationArray.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/DataTypes/Serializations/SerializationArray.cpp b/src/DataTypes/Serializations/SerializationArray.cpp index e3b535a2a11..30ee5e98b74 100644 --- a/src/DataTypes/Serializations/SerializationArray.cpp +++ b/src/DataTypes/Serializations/SerializationArray.cpp @@ -37,10 +37,11 @@ void SerializationArray::deserializeBinary(Field & field, ReadBuffer & istr) con { size_t size; readVarUInt(size, istr); - field = Array(size); + field = Array(); Array & arr = get(field); + arr.reserve(size); for (size_t i = 0; i < size; ++i) - nested->deserializeBinary(arr[i], istr); + nested->deserializeBinary(arr.emplace_back(), istr); } From 097bd6b7e8bcde5022ffe9c7626a0cdf7c4e7454 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 28 Jan 2022 20:17:55 +0300 Subject: [PATCH 41/57] Simplify ColumnArray::operator[] using ColumnArray::get() Signed-off-by: Azat Khuzhin --- src/Columns/ColumnArray.cpp | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/src/Columns/ColumnArray.cpp b/src/Columns/ColumnArray.cpp index 1d312ddbc7d..a8f4b6a62fa 100644 --- a/src/Columns/ColumnArray.cpp +++ b/src/Columns/ColumnArray.cpp @@ -127,19 +127,8 @@ size_t ColumnArray::size() const Field ColumnArray::operator[](size_t n) const { - size_t offset = offsetAt(n); - size_t size = sizeAt(n); - - if (size > max_array_size_as_field) - throw Exception(ErrorCodes::TOO_LARGE_ARRAY_SIZE, "Array of size {} is too large to be manipulated as single field, maximum size {}", - size, max_array_size_as_field); - - Array res; - res.reserve(size); - - for (size_t i = 0; i < size; ++i) - res.push_back(getData()[offset + i]); - + Field res; + get(n, res); return res; } From 9e740eb9d0613dd23819615c1776564522027c4c Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 28 Jan 2022 19:43:55 +0300 Subject: [PATCH 42/57] Use reserve() over resize() for Tuple Signed-off-by: Azat Khuzhin --- src/Columns/ColumnTuple.cpp | 14 +++++++++----- .../Serializations/SerializationTuple.cpp | 8 ++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/src/Columns/ColumnTuple.cpp b/src/Columns/ColumnTuple.cpp index d667b264d55..c0b9a5500ff 100644 --- a/src/Columns/ColumnTuple.cpp +++ b/src/Columns/ColumnTuple.cpp @@ -101,17 +101,21 @@ MutableColumnPtr ColumnTuple::cloneResized(size_t new_size) const Field ColumnTuple::operator[](size_t n) const { - return collections::map(columns, [n] (const auto & column) { return (*column)[n]; }); + Field res; + get(n, res); + return res; } void ColumnTuple::get(size_t n, Field & res) const { const size_t tuple_size = columns.size(); - Tuple tuple(tuple_size); - for (const auto i : collections::range(0, tuple_size)) - columns[i]->get(n, tuple[i]); - res = tuple; + res = Tuple(); + Tuple & res_tuple = DB::get(res); + res_tuple.reserve(tuple_size); + + for (const auto i : collections::range(0, tuple_size)) + res_tuple.push_back((*columns[i])[n]); } bool ColumnTuple::isDefaultAt(size_t n) const diff --git a/src/DataTypes/Serializations/SerializationTuple.cpp b/src/DataTypes/Serializations/SerializationTuple.cpp index cd5a6b65a3c..d9074dcc536 100644 --- a/src/DataTypes/Serializations/SerializationTuple.cpp +++ b/src/DataTypes/Serializations/SerializationTuple.cpp @@ -44,11 +44,11 @@ void SerializationTuple::deserializeBinary(Field & field, ReadBuffer & istr) con { const size_t size = elems.size(); - Tuple tuple(size); + field = Tuple(); + Tuple & tuple = get(field); + tuple.reserve(size); for (const auto i : collections::range(0, size)) - elems[i]->deserializeBinary(tuple[i], istr); - - field = tuple; + elems[i]->deserializeBinary(tuple.emplace_back(), istr); } void SerializationTuple::serializeBinary(const IColumn & column, size_t row_num, WriteBuffer & ostr) const From b511abb72c6a2a9d2a4b899033e6d239f3a68318 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Fri, 28 Jan 2022 20:02:13 +0300 Subject: [PATCH 43/57] Use reserve() over resize() for Map Signed-off-by: Azat Khuzhin --- src/Columns/ColumnMap.cpp | 10 ++++++---- src/DataTypes/Serializations/SerializationMap.cpp | 8 +++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Columns/ColumnMap.cpp b/src/Columns/ColumnMap.cpp index e595525d9e8..9e2c84a5321 100644 --- a/src/Columns/ColumnMap.cpp +++ b/src/Columns/ColumnMap.cpp @@ -64,8 +64,9 @@ MutableColumnPtr ColumnMap::cloneResized(size_t new_size) const Field ColumnMap::operator[](size_t n) const { - auto array = DB::get((*nested)[n]); - return Map(std::make_move_iterator(array.begin()), std::make_move_iterator(array.end())); + Field res; + get(n, res); + return res; } void ColumnMap::get(size_t n, Field & res) const @@ -74,11 +75,12 @@ void ColumnMap::get(size_t n, Field & res) const size_t offset = offsets[n - 1]; size_t size = offsets[n] - offsets[n - 1]; - res = Map(size); + res = Map(); auto & map = DB::get(res); + map.reserve(size); for (size_t i = 0; i < size; ++i) - getNestedData().get(offset + i, map[i]); + map.push_back(getNestedData()[offset + i]); } bool ColumnMap::isDefaultAt(size_t n) const diff --git a/src/DataTypes/Serializations/SerializationMap.cpp b/src/DataTypes/Serializations/SerializationMap.cpp index 3f17061a744..24d06d8f3b2 100644 --- a/src/DataTypes/Serializations/SerializationMap.cpp +++ b/src/DataTypes/Serializations/SerializationMap.cpp @@ -53,13 +53,15 @@ void SerializationMap::deserializeBinary(Field & field, ReadBuffer & istr) const { size_t size; readVarUInt(size, istr); - field = Map(size); - for (auto & elem : field.get()) + field = Map(); + Map & map = field.get(); + map.reserve(size); + for (size_t i = 0; i < size; ++i) { Tuple tuple(2); key->deserializeBinary(tuple[0], istr); value->deserializeBinary(tuple[1], istr); - elem = std::move(tuple); + map.push_back(std::move(tuple)); } } From 3bc10871eb3ac825455c3d8c03a9e32f2884f9ef Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 29 Jan 2022 12:30:31 +0300 Subject: [PATCH 44/57] Minor cleanup in ColumnTuple Signed-off-by: Azat Khuzhin --- src/Columns/ColumnTuple.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Columns/ColumnTuple.cpp b/src/Columns/ColumnTuple.cpp index c0b9a5500ff..0310eca7adc 100644 --- a/src/Columns/ColumnTuple.cpp +++ b/src/Columns/ColumnTuple.cpp @@ -9,9 +9,6 @@ #include #include #include -#include -#include -#include #include @@ -114,7 +111,7 @@ void ColumnTuple::get(size_t n, Field & res) const Tuple & res_tuple = DB::get(res); res_tuple.reserve(tuple_size); - for (const auto i : collections::range(0, tuple_size)) + for (size_t i = 0; i < tuple_size; ++i) res_tuple.push_back((*columns[i])[n]); } @@ -487,7 +484,7 @@ void ColumnTuple::getExtremes(Field & min, Field & max) const Tuple min_tuple(tuple_size); Tuple max_tuple(tuple_size); - for (const auto i : collections::range(0, tuple_size)) + for (size_t i = 0; i < tuple_size; ++i) columns[i]->getExtremes(min_tuple[i], max_tuple[i]); min = min_tuple; @@ -508,7 +505,7 @@ bool ColumnTuple::structureEquals(const IColumn & rhs) const if (tuple_size != rhs_tuple->columns.size()) return false; - for (const auto i : collections::range(0, tuple_size)) + for (size_t i = 0; i < tuple_size; ++i) if (!columns[i]->structureEquals(*rhs_tuple->columns[i])) return false; From f88ab143c4d80ad80991d980f2d3a635d837ad5c Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 29 Jan 2022 12:32:15 +0300 Subject: [PATCH 45/57] Headers cleanup in ColumnArray Signed-off-by: Azat Khuzhin --- src/Columns/ColumnArray.cpp | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/Columns/ColumnArray.cpp b/src/Columns/ColumnArray.cpp index a8f4b6a62fa..3abc60f33bc 100644 --- a/src/Columns/ColumnArray.cpp +++ b/src/Columns/ColumnArray.cpp @@ -1,5 +1,3 @@ -#include // memcpy - #include #include #include @@ -9,12 +7,7 @@ #include #include #include - -#include -#include - #include - #include #include #include @@ -22,6 +15,8 @@ #include #include #include +#include +#include // memcpy namespace DB From 9766619b55b41b6636e43a0f6dc8ff2a15a4b256 Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 29 Jan 2022 12:32:25 +0300 Subject: [PATCH 46/57] Headers cleanup in ColumnMap Signed-off-by: Azat Khuzhin --- src/Columns/ColumnMap.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Columns/ColumnMap.cpp b/src/Columns/ColumnMap.cpp index 9e2c84a5321..ef5d96da0f7 100644 --- a/src/Columns/ColumnMap.cpp +++ b/src/Columns/ColumnMap.cpp @@ -4,8 +4,6 @@ #include #include #include -#include -#include #include #include #include From d37ebda2d2bd1f87048477a96bf1c89e0259a52a Mon Sep 17 00:00:00 2001 From: Azat Khuzhin Date: Sat, 29 Jan 2022 12:35:32 +0300 Subject: [PATCH 47/57] Tiny cleanup in SerializationTuple Signed-off-by: Azat Khuzhin --- .../Serializations/SerializationTuple.cpp | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/DataTypes/Serializations/SerializationTuple.cpp b/src/DataTypes/Serializations/SerializationTuple.cpp index d9074dcc536..8dc15fc9841 100644 --- a/src/DataTypes/Serializations/SerializationTuple.cpp +++ b/src/DataTypes/Serializations/SerializationTuple.cpp @@ -1,4 +1,3 @@ -#include #include #include #include @@ -47,7 +46,7 @@ void SerializationTuple::deserializeBinary(Field & field, ReadBuffer & istr) con field = Tuple(); Tuple & tuple = get(field); tuple.reserve(size); - for (const auto i : collections::range(0, size)) + for (size_t i = 0; i < size; ++i) elems[i]->deserializeBinary(tuple.emplace_back(), istr); } @@ -73,7 +72,7 @@ static void addElementSafe(size_t num_elems, IColumn & column, F && impl) // Check that all columns now have the same size. size_t new_size = column.size(); - for (auto i : collections::range(1, num_elems)) + for (size_t i = 1; i < num_elems; ++i) { const auto & element_column = extractElementColumn(column, i); if (element_column.size() != new_size) @@ -87,7 +86,7 @@ static void addElementSafe(size_t num_elems, IColumn & column, F && impl) } catch (...) { - for (const auto & i : collections::range(0, num_elems)) + for (size_t i = 0; i < num_elems; ++i) { auto & element_column = extractElementColumn(column, i); if (element_column.size() > old_size) @@ -102,7 +101,7 @@ void SerializationTuple::deserializeBinary(IColumn & column, ReadBuffer & istr) { addElementSafe(elems.size(), column, [&] { - for (const auto & i : collections::range(0, elems.size())) + for (size_t i = 0; i < elems.size(); ++i) elems[i]->deserializeBinary(extractElementColumn(column, i), istr); }); } @@ -110,7 +109,7 @@ void SerializationTuple::deserializeBinary(IColumn & column, ReadBuffer & istr) void SerializationTuple::serializeText(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const { writeChar('(', ostr); - for (const auto i : collections::range(0, elems.size())) + for (size_t i = 0; i < elems.size(); ++i) { if (i != 0) writeChar(',', ostr); @@ -126,7 +125,7 @@ void SerializationTuple::deserializeText(IColumn & column, ReadBuffer & istr, co addElementSafe(elems.size(), column, [&] { - for (const auto i : collections::range(0, size)) + for (size_t i = 0; i < size; ++i) { skipWhitespaceIfAny(istr); if (i != 0) @@ -158,7 +157,7 @@ void SerializationTuple::serializeTextJSON(const IColumn & column, size_t row_nu && have_explicit_names) { writeChar('{', ostr); - for (const auto i : collections::range(0, elems.size())) + for (size_t i = 0; i < elems.size(); ++i) { if (i != 0) { @@ -173,7 +172,7 @@ void SerializationTuple::serializeTextJSON(const IColumn & column, size_t row_nu else { writeChar('[', ostr); - for (const auto i : collections::range(0, elems.size())) + for (size_t i = 0; i < elems.size(); ++i) { if (i != 0) writeChar(',', ostr); @@ -195,7 +194,7 @@ void SerializationTuple::deserializeTextJSON(IColumn & column, ReadBuffer & istr addElementSafe(elems.size(), column, [&] { // Require all elements but in arbitrary order. - for (auto i : collections::range(0, elems.size())) + for (size_t i = 0; i < elems.size(); ++i) { if (i > 0) { @@ -226,7 +225,7 @@ void SerializationTuple::deserializeTextJSON(IColumn & column, ReadBuffer & istr addElementSafe(elems.size(), column, [&] { - for (const auto i : collections::range(0, size)) + for (size_t i = 0; i < size; ++i) { skipWhitespaceIfAny(istr); if (i != 0) @@ -246,7 +245,7 @@ void SerializationTuple::deserializeTextJSON(IColumn & column, ReadBuffer & istr void SerializationTuple::serializeTextXML(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const { writeCString("", ostr); - for (const auto i : collections::range(0, elems.size())) + for (size_t i = 0; i < elems.size(); ++i) { writeCString("", ostr); elems[i]->serializeTextXML(extractElementColumn(column, i), row_num, ostr, settings); @@ -257,7 +256,7 @@ void SerializationTuple::serializeTextXML(const IColumn & column, size_t row_num void SerializationTuple::serializeTextCSV(const IColumn & column, size_t row_num, WriteBuffer & ostr, const FormatSettings & settings) const { - for (const auto i : collections::range(0, elems.size())) + for (size_t i = 0; i < elems.size(); ++i) { if (i != 0) writeChar(settings.csv.tuple_delimiter, ostr); @@ -270,7 +269,7 @@ void SerializationTuple::deserializeTextCSV(IColumn & column, ReadBuffer & istr, addElementSafe(elems.size(), column, [&] { const size_t size = elems.size(); - for (const auto i : collections::range(0, size)) + for (size_t i = 0; i < size; ++i) { if (i != 0) { @@ -362,7 +361,7 @@ void SerializationTuple::serializeBinaryBulkWithMultipleStreams( { auto * tuple_state = checkAndGetState(state); - for (const auto i : collections::range(0, elems.size())) + for (size_t i = 0; i < elems.size(); ++i) { const auto & element_col = extractElementColumn(column, i); elems[i]->serializeBinaryBulkWithMultipleStreams(element_col, offset, limit, settings, tuple_state->states[i]); @@ -382,7 +381,7 @@ void SerializationTuple::deserializeBinaryBulkWithMultipleStreams( auto & column_tuple = assert_cast(*mutable_column); settings.avg_value_size_hint = 0; - for (const auto i : collections::range(0, elems.size())) + for (size_t i = 0; i < elems.size(); ++i) elems[i]->deserializeBinaryBulkWithMultipleStreams(column_tuple.getColumnPtr(i), limit, settings, tuple_state->states[i], cache); } From 04cff632db23c7adbbf08ce616258e4149aacd65 Mon Sep 17 00:00:00 2001 From: Amos Bird Date: Sat, 29 Jan 2022 20:55:52 +0800 Subject: [PATCH 48/57] Revise --- src/Columns/IColumn.h | 13 +++++------ src/Interpreters/sortBlock.cpp | 40 +++++++++++++++++++--------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/Columns/IColumn.h b/src/Columns/IColumn.h index 7e5d01ef912..b1a6e83ee98 100644 --- a/src/Columns/IColumn.h +++ b/src/Columns/IColumn.h @@ -318,18 +318,15 @@ public: virtual void updatePermutation(bool reverse, size_t limit, int nan_direction_hint, Permutation & res, EqualRanges & equal_ranges) const = 0; /** Equivalent to getPermutation and updatePermutation but collator is used to compare values. - * Overrides in String, LowCardinality(String), Nullable(String) and for Array and Tuple, containing them. + * Supported for String, LowCardinality(String), Nullable(String) and for Array and Tuple, containing them. */ - virtual void - getPermutationWithCollation(const Collator &, bool reverse, size_t limit, int nan_direction_hint, Permutation & res) const + virtual void getPermutationWithCollation(const Collator &, bool, size_t, int, Permutation &) const { - getPermutation(reverse, limit, nan_direction_hint, res); + throw Exception("Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, containing them.", ErrorCodes::BAD_COLLATION); } - - virtual void updatePermutationWithCollation( - const Collator &, bool reverse, size_t limit, int nan_direction_hint, Permutation & res, EqualRanges & equal_ranges) const + virtual void updatePermutationWithCollation(const Collator &, bool, size_t, int, Permutation &, EqualRanges&) const { - updatePermutation(reverse, limit, nan_direction_hint, res, equal_ranges); + throw Exception("Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, containing them.", ErrorCodes::BAD_COLLATION); } /** Copies each element according offsets parameter. diff --git a/src/Interpreters/sortBlock.cpp b/src/Interpreters/sortBlock.cpp index 781e75090d1..837ce90d3b8 100644 --- a/src/Interpreters/sortBlock.cpp +++ b/src/Interpreters/sortBlock.cpp @@ -27,7 +27,7 @@ static bool isCollationRequired(const SortColumnDescription & description) struct ColumnWithSortDescription { const IColumn * column = nullptr; - const SortColumnDescription * description = nullptr; + SortColumnDescription description; /// It means, that this column is ColumnConst bool column_const = false; @@ -35,14 +35,18 @@ struct ColumnWithSortDescription using ColumnsWithSortDescriptions = std::vector; void flattenTupleColumnRecursively( - ColumnsWithSortDescriptions & res, const ColumnTuple * tuple, const SortColumnDescription * description, bool is_constant) + ColumnsWithSortDescriptions & res, const ColumnTuple * tuple, const SortColumnDescription & description, bool has_collation) { for (const auto & column : tuple->getColumns()) { if (const auto * subtuple = typeid_cast(column.get())) - flattenTupleColumnRecursively(res, subtuple, description, is_constant); + flattenTupleColumnRecursively(res, subtuple, description, has_collation); else - res.emplace_back(ColumnWithSortDescription{column.get(), description, is_constant}); + { + res.emplace_back(ColumnWithSortDescription{column.get(), description, isColumnConst(*column)}); + if (has_collation && !res.back().column->isCollationSupported()) + res.back().description.collator = nullptr; + } } } @@ -67,9 +71,9 @@ ColumnsWithSortDescriptions getColumnsWithSortDescription(const Block & block, c ErrorCodes::BAD_COLLATION); } if (const auto * tuple = typeid_cast(column)) - flattenTupleColumnRecursively(res, tuple, &description[i], isColumnConst(*column)); + flattenTupleColumnRecursively(res, tuple, description[i], isCollationRequired(description[i])); else - res.emplace_back(ColumnWithSortDescription{column, &description[i], isColumnConst(*column)}); + res.emplace_back(ColumnWithSortDescription{column, description[i], isColumnConst(*column)}); } return res; } @@ -88,7 +92,7 @@ struct PartialSortingLess if (elem.column_const) res = 0; else - res = elem.description->direction * elem.column->compareAt(a, b, *elem.column, elem.description->nulls_direction); + res = elem.description.direction * elem.column->compareAt(a, b, *elem.column, elem.description.nulls_direction); if (res < 0) return true; else if (res > 0) @@ -118,13 +122,13 @@ struct PartialSortingLessWithCollation { res = 0; } - else if (isCollationRequired(*elem.description)) + else if (isCollationRequired(elem.description)) { - res = elem.column->compareAtWithCollation(a, b, *elem.column, elem.description->nulls_direction, *elem.description->collator); + res = elem.column->compareAtWithCollation(a, b, *elem.column, elem.description.nulls_direction, *elem.description.collator); } else - res = elem.column->compareAt(a, b, *elem.column, elem.description->nulls_direction); - res *= elem.description->direction; + res = elem.column->compareAt(a, b, *elem.column, elem.description.nulls_direction); + res *= elem.description.direction; if (res < 0) return true; else if (res > 0) @@ -156,13 +160,13 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) /// If only one column to sort by if (columns_with_sort_desc.size() == 1) { - bool reverse = columns_with_sort_desc[0].description->direction == -1; + bool reverse = columns_with_sort_desc[0].description.direction == -1; const IColumn * column = columns_with_sort_desc[0].column; if (isCollationRequired(description[0])) column->getPermutationWithCollation(*description[0].collator, reverse, limit, description[0].nulls_direction, perm); else { - int nan_direction_hint = columns_with_sort_desc[0].description->nulls_direction; + int nan_direction_hint = columns_with_sort_desc[0].description.nulls_direction; column->getPermutation(reverse, limit, nan_direction_hint, perm); } } @@ -189,20 +193,20 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) if (column.column_const) continue; - if (isCollationRequired(*column.description)) + if (isCollationRequired(column.description)) { column.column->updatePermutationWithCollation( - *column.description->collator, - column.description->direction < 0, + *column.description.collator, + column.description.direction < 0, limit, - column.description->nulls_direction, + column.description.nulls_direction, perm, ranges); } else { column.column->updatePermutation( - column.description->direction < 0, limit, column.description->nulls_direction, perm, ranges); + column.description.direction < 0, limit, column.description.nulls_direction, perm, ranges); } } } From cfd3421e4f520a22b740fe14cd4a89eda13fd935 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Sat, 29 Jan 2022 15:50:10 +0000 Subject: [PATCH 49/57] Dictionary PRIMARY KEY parsing fix --- src/Parsers/ParserDictionary.cpp | 15 ++++- ...88_parser_dictionary_primary_key.reference | 8 +++ .../02188_parser_dictionary_primary_key.sql | 65 +++++++++++++++++++ 3 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 tests/queries/0_stateless/02188_parser_dictionary_primary_key.reference create mode 100644 tests/queries/0_stateless/02188_parser_dictionary_primary_key.sql diff --git a/src/Parsers/ParserDictionary.cpp b/src/Parsers/ParserDictionary.cpp index 399dda08911..ef914e2264a 100644 --- a/src/Parsers/ParserDictionary.cpp +++ b/src/Parsers/ParserDictionary.cpp @@ -188,8 +188,19 @@ bool ParserDictionary::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) ASTPtr ast_settings; /// Primary is required to be the first in dictionary definition - if (primary_key_keyword.ignore(pos) && !expression_list_p.parse(pos, primary_key, expected)) - return false; + if (primary_key_keyword.ignore(pos)) + { + bool was_open = false; + + if (open.ignore(pos, expected)) + was_open = true; + + if (!expression_list_p.parse(pos, primary_key, expected)) + return false; + + if (was_open && !close.ignore(pos, expected)) + return false; + } /// Loop is used to avoid strict order of dictionary properties while (true) diff --git a/tests/queries/0_stateless/02188_parser_dictionary_primary_key.reference b/tests/queries/0_stateless/02188_parser_dictionary_primary_key.reference new file mode 100644 index 00000000000..0e4e614d264 --- /dev/null +++ b/tests/queries/0_stateless/02188_parser_dictionary_primary_key.reference @@ -0,0 +1,8 @@ +Dictionary output +0 Value +Dictionary output +0 Value +Dictionary output +0 Value +Dictionary output +0 Value diff --git a/tests/queries/0_stateless/02188_parser_dictionary_primary_key.sql b/tests/queries/0_stateless/02188_parser_dictionary_primary_key.sql new file mode 100644 index 00000000000..a939c30b57b --- /dev/null +++ b/tests/queries/0_stateless/02188_parser_dictionary_primary_key.sql @@ -0,0 +1,65 @@ +DROP TABLE IF EXISTS 02188_test_dictionary_source; +CREATE TABLE 02188_test_dictionary_source +( + id UInt64, + value String +) +ENGINE=TinyLog; + +INSERT INTO 02188_test_dictionary_source VALUES (0, 'Value'); + +DROP DICTIONARY IF EXISTS 02188_test_dictionary_simple_primary_key; +CREATE DICTIONARY 02188_test_dictionary_simple_primary_key +( + id UInt64, + value String +) +PRIMARY KEY id +SOURCE(CLICKHOUSE(TABLE '02188_test_dictionary_source')) +LAYOUT(DIRECT()); + +SELECT 'Dictionary output'; +SELECT * FROM 02188_test_dictionary_simple_primary_key; +DROP DICTIONARY 02188_test_dictionary_simple_primary_key; + +CREATE DICTIONARY 02188_test_dictionary_simple_primary_key +( + id UInt64, + value String +) +PRIMARY KEY (id) +SOURCE(CLICKHOUSE(TABLE '02188_test_dictionary_source')) +LAYOUT(DIRECT()); + +SELECT 'Dictionary output'; +SELECT * FROM 02188_test_dictionary_simple_primary_key; +DROP DICTIONARY 02188_test_dictionary_simple_primary_key; + +DROP DICTIONARY IF EXISTS 02188_test_dictionary_complex_primary_key; +CREATE DICTIONARY 02188_test_dictionary_complex_primary_key +( + id UInt64, + value String +) +PRIMARY KEY id, value +SOURCE(CLICKHOUSE(TABLE '02188_test_dictionary_source')) +LAYOUT(COMPLEX_KEY_DIRECT()); + +SELECT 'Dictionary output'; +SELECT * FROM 02188_test_dictionary_complex_primary_key; +DROP DICTIONARY 02188_test_dictionary_complex_primary_key; + +CREATE DICTIONARY 02188_test_dictionary_complex_primary_key +( + id UInt64, + value String +) +PRIMARY KEY (id, value) +SOURCE(CLICKHOUSE(TABLE '02188_test_dictionary_source')) +LAYOUT(COMPLEX_KEY_DIRECT()); + +SELECT 'Dictionary output'; +SELECT * FROM 02188_test_dictionary_complex_primary_key; +DROP DICTIONARY 02188_test_dictionary_complex_primary_key; + +DROP TABLE 02188_test_dictionary_source; From 9c91a50050d515b874716657c6027d5f9e245f4f Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Sat, 29 Jan 2022 15:50:55 +0000 Subject: [PATCH 50/57] Sort block refactoring --- src/Interpreters/sortBlock.cpp | 239 +++++++++++++++++---------------- src/Interpreters/sortBlock.h | 2 - 2 files changed, 123 insertions(+), 118 deletions(-) diff --git a/src/Interpreters/sortBlock.cpp b/src/Interpreters/sortBlock.cpp index 837ce90d3b8..c8a2d0903f2 100644 --- a/src/Interpreters/sortBlock.cpp +++ b/src/Interpreters/sortBlock.cpp @@ -1,14 +1,10 @@ #include -#include #include #include -#include #include -#include #include -#include namespace DB { @@ -18,11 +14,6 @@ namespace ErrorCodes extern const int BAD_COLLATION; } -static bool isCollationRequired(const SortColumnDescription & description) -{ - return description.collator != nullptr; -} - /// Column with description for sort struct ColumnWithSortDescription { @@ -32,87 +23,25 @@ struct ColumnWithSortDescription /// It means, that this column is ColumnConst bool column_const = false; }; + using ColumnsWithSortDescriptions = std::vector; -void flattenTupleColumnRecursively( - ColumnsWithSortDescriptions & res, const ColumnTuple * tuple, const SortColumnDescription & description, bool has_collation) +namespace { - for (const auto & column : tuple->getColumns()) - { - if (const auto * subtuple = typeid_cast(column.get())) - flattenTupleColumnRecursively(res, subtuple, description, has_collation); - else - { - res.emplace_back(ColumnWithSortDescription{column.get(), description, isColumnConst(*column)}); - if (has_collation && !res.back().column->isCollationSupported()) - res.back().description.collator = nullptr; - } - } + +inline bool isCollationRequired(const SortColumnDescription & description) +{ + return description.collator != nullptr; } -ColumnsWithSortDescriptions getColumnsWithSortDescription(const Block & block, const SortDescription & description) -{ - size_t size = description.size(); - ColumnsWithSortDescriptions res; - res.reserve(size); - - for (size_t i = 0; i < size; ++i) - { - const IColumn * column = !description[i].column_name.empty() - ? block.getByName(description[i].column_name).column.get() - : block.safeGetByPosition(description[i].column_number).column.get(); - - if (isCollationRequired(description[i])) - { - if (!column->isCollationSupported()) - throw Exception( - "Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, " - "containing them.", - ErrorCodes::BAD_COLLATION); - } - if (const auto * tuple = typeid_cast(column)) - flattenTupleColumnRecursively(res, tuple, description[i], isCollationRequired(description[i])); - else - res.emplace_back(ColumnWithSortDescription{column, description[i], isColumnConst(*column)}); - } - return res; -} - -struct PartialSortingLess +template +struct PartialSortingLessImpl { const ColumnsWithSortDescriptions & columns; - explicit PartialSortingLess(const ColumnsWithSortDescriptions & columns_) : columns(columns_) {} + explicit PartialSortingLessImpl(const ColumnsWithSortDescriptions & columns_) : columns(columns_) { } - bool operator() (size_t a, size_t b) const - { - for (const auto & elem : columns) - { - int res; - if (elem.column_const) - res = 0; - else - res = elem.description.direction * elem.column->compareAt(a, b, *elem.column, elem.description.nulls_direction); - if (res < 0) - return true; - else if (res > 0) - return false; - } - return false; - } -}; - - -struct PartialSortingLessWithCollation -{ - const ColumnsWithSortDescriptions & columns; - - explicit PartialSortingLessWithCollation(const ColumnsWithSortDescriptions & columns_) - : columns(columns_) - { - } - - bool operator() (size_t a, size_t b) const + inline bool operator()(size_t a, size_t b) const { for (const auto & elem : columns) { @@ -121,13 +50,25 @@ struct PartialSortingLessWithCollation if (elem.column_const) { res = 0; + continue; } - else if (isCollationRequired(elem.description)) + + if constexpr (check_collation) { - res = elem.column->compareAtWithCollation(a, b, *elem.column, elem.description.nulls_direction, *elem.description.collator); + if (isCollationRequired(elem.description)) + { + res = elem.column->compareAtWithCollation(a, b, *elem.column, elem.description.nulls_direction, *elem.description.collator); + } + else + { + res = elem.column->compareAt(a, b, *elem.column, elem.description.nulls_direction); + } } else + { res = elem.column->compareAt(a, b, *elem.column, elem.description.nulls_direction); + } + res *= elem.description.direction; if (res < 0) return true; @@ -138,14 +79,72 @@ struct PartialSortingLessWithCollation } }; +using PartialSortingLess = PartialSortingLessImpl; +using PartialSortingLessWithCollation = PartialSortingLessImpl; + +} + +void convertTupleColumnIntoSortDescriptions( + const ColumnTuple * tuple, const SortColumnDescription & description, ColumnsWithSortDescriptions & result) +{ + for (const auto & column : tuple->getColumns()) + { + if (const auto * subtuple = typeid_cast(column.get())) + { + convertTupleColumnIntoSortDescriptions(subtuple, description, result); + } + else + { + result.emplace_back(ColumnWithSortDescription{column.get(), description, isColumnConst(*column)}); + + if (isCollationRequired(description) && !result.back().column->isCollationSupported()) + result.back().description.collator = nullptr; + } + } +} + +ColumnsWithSortDescriptions getColumnsWithSortDescription(const Block & block, const SortDescription & description) +{ + size_t size = description.size(); + + ColumnsWithSortDescriptions result; + result.reserve(size); + + for (size_t i = 0; i < size; ++i) + { + const auto & sort_column_description = description[i]; + + const IColumn * column = !sort_column_description.column_name.empty() + ? block.getByName(sort_column_description.column_name).column.get() + : block.safeGetByPosition(sort_column_description.column_number).column.get(); + + if (isCollationRequired(sort_column_description)) + { + if (!column->isCollationSupported()) + throw Exception( + "Collations could be specified only for String, LowCardinality(String), Nullable(String) or for Array or Tuple, " + "containing them.", + ErrorCodes::BAD_COLLATION); + } + + if (const auto * tuple = typeid_cast(column)) + convertTupleColumnIntoSortDescriptions(tuple, sort_column_description, result); + else + result.emplace_back(ColumnWithSortDescription{column, sort_column_description, isColumnConst(*column)}); + } + + return result; +} + void sortBlock(Block & block, const SortDescription & description, UInt64 limit) { if (!block) return; - ColumnsWithSortDescriptions columns_with_sort_desc = getColumnsWithSortDescription(block, description); + ColumnsWithSortDescriptions columns_with_sort_descriptions = getColumnsWithSortDescription(block, description); + bool all_const = true; - for (const auto & column : columns_with_sort_desc) + for (const auto & column : columns_with_sort_descriptions) { if (!column.column_const) { @@ -156,33 +155,37 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) if (all_const) return; - IColumn::Permutation perm; + IColumn::Permutation permutation; + /// If only one column to sort by - if (columns_with_sort_desc.size() == 1) + if (columns_with_sort_descriptions.size() == 1) { - bool reverse = columns_with_sort_desc[0].description.direction == -1; - const IColumn * column = columns_with_sort_desc[0].column; - if (isCollationRequired(description[0])) - column->getPermutationWithCollation(*description[0].collator, reverse, limit, description[0].nulls_direction, perm); + auto & column_with_sort_description = columns_with_sort_descriptions[0]; + + bool reverse = column_with_sort_description.description.direction == -1; + int nan_direction_hint = column_with_sort_description.description.nulls_direction; + const auto & column = column_with_sort_description.column; + + if (isCollationRequired(column_with_sort_description.description)) + column->getPermutationWithCollation( + *column_with_sort_description.description.collator, reverse, limit, nan_direction_hint, permutation); else - { - int nan_direction_hint = columns_with_sort_desc[0].description.nulls_direction; - column->getPermutation(reverse, limit, nan_direction_hint, perm); - } + column->getPermutation(reverse, limit, nan_direction_hint, permutation); } else { size_t size = block.rows(); - perm.resize(size); + permutation.resize(size); for (size_t i = 0; i < size; ++i) - perm[i] = i; + permutation[i] = i; if (limit >= size) limit = 0; EqualRanges ranges; - ranges.emplace_back(0, perm.size()); - for (const auto & column : columns_with_sort_desc) + ranges.emplace_back(0, permutation.size()); + + for (const auto & column_with_sort_description : columns_with_sort_descriptions) { while (!ranges.empty() && limit && limit <= ranges.back().first) ranges.pop_back(); @@ -190,32 +193,34 @@ void sortBlock(Block & block, const SortDescription & description, UInt64 limit) if (ranges.empty()) break; - if (column.column_const) + if (column_with_sort_description.column_const) continue; - if (isCollationRequired(column.description)) + bool is_collation_required = isCollationRequired(column_with_sort_description.description); + bool reverse = column_with_sort_description.description.direction < 0; + int nan_direction_hint = column_with_sort_description.description.nulls_direction; + const auto & column = column_with_sort_description.column; + + if (is_collation_required) { - column.column->updatePermutationWithCollation( - *column.description.collator, - column.description.direction < 0, - limit, - column.description.nulls_direction, - perm, - ranges); + column->updatePermutationWithCollation( + *column_with_sort_description.description.collator, reverse, limit, nan_direction_hint, permutation, ranges); } else { - column.column->updatePermutation( - column.description.direction < 0, limit, column.description.nulls_direction, perm, ranges); + column->updatePermutation(reverse, limit, nan_direction_hint, permutation, ranges); } } } + size_t columns = block.columns(); for (size_t i = 0; i < columns; ++i) - block.getByPosition(i).column = block.getByPosition(i).column->permute(perm, limit); + { + auto & column_to_sort = block.getByPosition(i).column; + column_to_sort = column_to_sort->permute(permutation, limit); + } } - void stableGetPermutation(const Block & block, const SortDescription & description, IColumn::Permutation & out_permutation) { if (!block) @@ -231,7 +236,6 @@ void stableGetPermutation(const Block & block, const SortDescription & descripti std::stable_sort(out_permutation.begin(), out_permutation.end(), PartialSortingLess(columns_with_sort_desc)); } - bool isAlreadySorted(const Block & block, const SortDescription & description) { if (!block) @@ -272,12 +276,15 @@ void stableSortBlock(Block & block, const SortDescription & description) if (!block) return; - IColumn::Permutation perm; - stableGetPermutation(block, description, perm); + IColumn::Permutation permutation; + stableGetPermutation(block, description, permutation); size_t columns = block.columns(); for (size_t i = 0; i < columns; ++i) - block.safeGetByPosition(i).column = block.safeGetByPosition(i).column->permute(perm, 0); + { + auto & column_to_sort = block.safeGetByPosition(i).column; + column_to_sort = column_to_sort->permute(permutation, 0); + } } } diff --git a/src/Interpreters/sortBlock.h b/src/Interpreters/sortBlock.h index 8f28076c6af..31ae78e90b0 100644 --- a/src/Interpreters/sortBlock.h +++ b/src/Interpreters/sortBlock.h @@ -10,7 +10,6 @@ namespace DB /// Sort one block by `description`. If limit != 0, then the partial sort of the first `limit` rows is produced. void sortBlock(Block & block, const SortDescription & description, UInt64 limit = 0); - /** Used only in StorageMergeTree to sort the data with INSERT. * Sorting is stable. This is important for keeping the order of rows in the CollapsingMergeTree engine * - because based on the order of rows it is determined whether to delete or leave groups of rows when collapsing. @@ -23,7 +22,6 @@ void stableSortBlock(Block & block, const SortDescription & description); */ void stableGetPermutation(const Block & block, const SortDescription & description, IColumn::Permutation & out_permutation); - /** Quickly check whether the block is already sorted. If the block is not sorted - returns false as fast as possible. * Collations are not supported. */ From 2260bcea6a25a191278b778c483a7a5b5eb49fc8 Mon Sep 17 00:00:00 2001 From: Maksim Kita Date: Sat, 29 Jan 2022 17:09:28 +0100 Subject: [PATCH 51/57] Update index.md --- .../sql-reference/statements/alter/index.md | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/docs/zh/sql-reference/statements/alter/index.md b/docs/zh/sql-reference/statements/alter/index.md index 60f6fc022fc..f7d983cab4e 100644 --- a/docs/zh/sql-reference/statements/alter/index.md +++ b/docs/zh/sql-reference/statements/alter/index.md @@ -72,29 +72,3 @@ toc_title: ALTER 对于 `ALTER TABLE ... UPDATE|DELETE` 查询由 [mutations_sync](../../../operations/settings/settings.md#mutations_sync) 设置定义的同步度。 - - - - - - - - - - - - - - - - - - - - - - - - - - From 2f5fc12056c203291a8bec90346f2c7c3ef34af3 Mon Sep 17 00:00:00 2001 From: tesw yew isal <278153+cnmade@users.noreply.github.com> Date: Sun, 30 Jan 2022 05:19:47 +0800 Subject: [PATCH 52/57] Translate to zh: faq/general/dbms-naming: rename old file --- docs/zh/faq/general/dbms-naming.md | 1 - docs/zh/faq/general/dbms-naming.md.bak | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 120000 docs/zh/faq/general/dbms-naming.md create mode 100644 docs/zh/faq/general/dbms-naming.md.bak diff --git a/docs/zh/faq/general/dbms-naming.md b/docs/zh/faq/general/dbms-naming.md deleted file mode 120000 index 0df856af0ca..00000000000 --- a/docs/zh/faq/general/dbms-naming.md +++ /dev/null @@ -1 +0,0 @@ -../../../en/faq/general/dbms-naming.md \ No newline at end of file diff --git a/docs/zh/faq/general/dbms-naming.md.bak b/docs/zh/faq/general/dbms-naming.md.bak new file mode 100644 index 00000000000..0df856af0ca --- /dev/null +++ b/docs/zh/faq/general/dbms-naming.md.bak @@ -0,0 +1 @@ +../../../en/faq/general/dbms-naming.md \ No newline at end of file From 3003001da59b290e1830ac46af4b5abc3258bb90 Mon Sep 17 00:00:00 2001 From: tesw yew isal <278153+cnmade@users.noreply.github.com> Date: Sun, 30 Jan 2022 05:20:26 +0800 Subject: [PATCH 53/57] Translate to zh: faq/general/dbms-naming: import file --- docs/zh/faq/general/dbms-naming.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 docs/zh/faq/general/dbms-naming.md diff --git a/docs/zh/faq/general/dbms-naming.md b/docs/zh/faq/general/dbms-naming.md new file mode 100644 index 00000000000..d12dcad4684 --- /dev/null +++ b/docs/zh/faq/general/dbms-naming.md @@ -0,0 +1,17 @@ +--- +title: "What does \u201CClickHouse\u201D mean?" +toc_hidden: true +toc_priority: 10 +--- + +# What Does “ClickHouse” Mean? {#what-does-clickhouse-mean} + +It’s a combination of “**Click**stream” and “Data ware**House**”. It comes from the original use case at Yandex.Metrica, where ClickHouse was supposed to keep records of all clicks by people from all over the Internet, and it still does the job. You can read more about this use case on [ClickHouse history](../../introduction/history.md) page. + +This two-part meaning has two consequences: + +- The only correct way to write Click**H**ouse is with capital H. +- If you need to abbreviate it, use **CH**. For some historical reasons, abbreviating as CK is also popular in China, mostly because one of the first talks about ClickHouse in Chinese used this form. + +!!! info "Fun fact" + Many years after ClickHouse got its name, this approach of combining two words that are meaningful on their own has been highlighted as the best way to name a database in a [research by Andy Pavlo](https://www.cs.cmu.edu/~pavlo/blog/2020/03/on-naming-a-database-management-system.html), an Associate Professor of Databases at Carnegie Mellon University. ClickHouse shared his “best database name of all time” award with Postgres. From 47133b9a57290e9009f785f4c7da9cd80ab2decd Mon Sep 17 00:00:00 2001 From: tesw yew isal <278153+cnmade@users.noreply.github.com> Date: Sun, 30 Jan 2022 05:29:02 +0800 Subject: [PATCH 54/57] Translate to zh: faq/general/dbms-naming: translate to zh --- docs/zh/faq/general/dbms-naming.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/zh/faq/general/dbms-naming.md b/docs/zh/faq/general/dbms-naming.md index d12dcad4684..8d4353f9322 100644 --- a/docs/zh/faq/general/dbms-naming.md +++ b/docs/zh/faq/general/dbms-naming.md @@ -1,17 +1,17 @@ --- -title: "What does \u201CClickHouse\u201D mean?" +title: "\u201CClickHouse\u201D 有什么含义?" toc_hidden: true toc_priority: 10 --- -# What Does “ClickHouse” Mean? {#what-does-clickhouse-mean} +# “ClickHouse” 有什么含义? {#what-does-clickhouse-mean} -It’s a combination of “**Click**stream” and “Data ware**House**”. It comes from the original use case at Yandex.Metrica, where ClickHouse was supposed to keep records of all clicks by people from all over the Internet, and it still does the job. You can read more about this use case on [ClickHouse history](../../introduction/history.md) page. +它是“**点击**流”和“数据**仓库**”的组合。它来自于Yandex最初的用例。在Metrica网站上,ClickHouse本应该保存人们在互联网上的所有点击记录,现在它仍然在做这项工作。你可以在[ClickHouse history](../../introduction/history.md)页面上阅读更多关于这个用例的信息。 -This two-part meaning has two consequences: +这个由两部分组成的意思有两个结果: -- The only correct way to write Click**H**ouse is with capital H. -- If you need to abbreviate it, use **CH**. For some historical reasons, abbreviating as CK is also popular in China, mostly because one of the first talks about ClickHouse in Chinese used this form. +- 唯一正确的写“Click**H** house”的方式是用大写H。 +- 如果需要缩写,请使用“**CH**”。由于一些历史原因,缩写CK在中国也很流行,主要是因为中文中最早的一个关于ClickHouse的演讲使用了这种形式。 -!!! info "Fun fact" - Many years after ClickHouse got its name, this approach of combining two words that are meaningful on their own has been highlighted as the best way to name a database in a [research by Andy Pavlo](https://www.cs.cmu.edu/~pavlo/blog/2020/03/on-naming-a-database-management-system.html), an Associate Professor of Databases at Carnegie Mellon University. ClickHouse shared his “best database name of all time” award with Postgres. +!!! info “有趣的事实” + 多年后ClickHouse闻名于世, 这种命名方法:结合各有深意的两个词被赞扬为最好的数据库命名方式, 卡内基梅隆大学数据库副教授[Andy Pavlo做的研究](https://www.cs.cmu.edu/~pavlo/blog/2020/03/on-naming-a-database-management-system.html) 。ClickHouse与Postgres共同获得“史上最佳数据库名”奖。 From a27f01aa297e363997efa8f943c97fdfb38e1d86 Mon Sep 17 00:00:00 2001 From: tesw yew isal <278153+cnmade@users.noreply.github.com> Date: Sun, 30 Jan 2022 05:29:56 +0800 Subject: [PATCH 55/57] Translate to zh: faq/general/dbms-naming: delete old name --- docs/zh/faq/general/dbms-naming.md.bak | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/zh/faq/general/dbms-naming.md.bak diff --git a/docs/zh/faq/general/dbms-naming.md.bak b/docs/zh/faq/general/dbms-naming.md.bak deleted file mode 100644 index 0df856af0ca..00000000000 --- a/docs/zh/faq/general/dbms-naming.md.bak +++ /dev/null @@ -1 +0,0 @@ -../../../en/faq/general/dbms-naming.md \ No newline at end of file From 7135ff448fb1b98840e649936bb6e8c7c8de2e77 Mon Sep 17 00:00:00 2001 From: Alexey Milovidov Date: Sun, 30 Jan 2022 01:09:15 +0300 Subject: [PATCH 56/57] Revert #33957 --- base/glibc-compatibility/musl/getauxval.c | 89 +++++++++-------------- 1 file changed, 34 insertions(+), 55 deletions(-) diff --git a/base/glibc-compatibility/musl/getauxval.c b/base/glibc-compatibility/musl/getauxval.c index b534678d76f..dad7aa938d7 100644 --- a/base/glibc-compatibility/musl/getauxval.c +++ b/base/glibc-compatibility/musl/getauxval.c @@ -1,80 +1,59 @@ -#include "atomic.h" #include -#include // open -#include // O_RDONLY -#include // read, close -#include // ssize_t -#include // perror, fprintf -#include // ElfW +#include "atomic.h" +#include // __environ #include -#define ARRAY_SIZE(a) sizeof((a))/sizeof((a[0])) - -// We don't have libc struct available here. -// Compute aux vector manually (from /proc/self/auxv). -// -// Right now there is only 51 AT_* constants, -// so 64 should be enough until this implementation will be replaced with musl. -static unsigned long __auxv[64]; +// We don't have libc struct available here. Compute aux vector manually. +static unsigned long * __auxv = NULL; static unsigned long __auxv_secure = 0; +static size_t __find_auxv(unsigned long type) +{ + size_t i; + for (i = 0; __auxv[i]; i += 2) + { + if (__auxv[i] == type) + return i + 1; + } + return (size_t) -1; +} + unsigned long __getauxval(unsigned long type) { if (type == AT_SECURE) return __auxv_secure; - if (type >= ARRAY_SIZE(__auxv)) + if (__auxv) { - errno = ENOENT; - return 0; + size_t index = __find_auxv(type); + if (index != ((size_t) -1)) + return __auxv[index]; } - return __auxv[type]; + errno = ENOENT; + return 0; } static void * volatile getauxval_func; -ssize_t __retry_read(int fd, void *buf, size_t count) -{ - for (;;) - { - ssize_t ret = read(fd, buf, count); - if (ret == -1) - { - if (errno == EINTR) - continue; - perror("Cannot read /proc/self/auxv"); - abort(); - } - return ret; - } -} static unsigned long __auxv_init(unsigned long type) { - // od -t dL /proc/self/auxv - int fd = open("/proc/self/auxv", O_RDONLY); - if (fd == -1) { - perror("Cannot read /proc/self/auxv (likely kernel is too old or procfs is not mounted)"); - abort(); - } - - ElfW(auxv_t) aux; - - /// NOTE: sizeof(aux) is very small (less then PAGE_SIZE), so partial read should not be possible. - _Static_assert(sizeof(aux) < 4096, "Unexpected sizeof(aux)"); - while (__retry_read(fd, &aux, sizeof(aux)) == sizeof(aux)) + if (!__environ) { - if (aux.a_type >= ARRAY_SIZE(__auxv)) - { - fprintf(stderr, "AT_* is out of range: %li (maximum allowed is %zu)\n", aux.a_type, ARRAY_SIZE(__auxv)); - abort(); - } - __auxv[aux.a_type] = aux.a_un.a_val; + // __environ is not initialized yet so we can't initialize __auxv right now. + // That's normally occurred only when getauxval() is called from some sanitizer's internal code. + errno = ENOENT; + return 0; } - close(fd); - // AT_SECURE - __auxv_secure = __getauxval(AT_SECURE); + // Initialize __auxv and __auxv_secure. + size_t i; + for (i = 0; __environ[i]; i++); + __auxv = (unsigned long *) (__environ + i + 1); + + size_t secure_idx = __find_auxv(AT_SECURE); + if (secure_idx != ((size_t) -1)) + __auxv_secure = __auxv[secure_idx]; // Now we've initialized __auxv, next time getauxval() will only call __get_auxval(). a_cas_p(&getauxval_func, (void *)__auxv_init, (void *)__getauxval); From fb64b8c9a3acfa9cac62ab975e42d59abbe83b79 Mon Sep 17 00:00:00 2001 From: alexey-milovidov Date: Sun, 30 Jan 2022 02:59:41 +0300 Subject: [PATCH 57/57] Update http.md --- docs/en/interfaces/http.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/interfaces/http.md b/docs/en/interfaces/http.md index 8c0c5230818..d72fb4d6f17 100644 --- a/docs/en/interfaces/http.md +++ b/docs/en/interfaces/http.md @@ -23,7 +23,7 @@ Web UI can be accessed here: `http://localhost:8123/play`. ![Web UI](../images/play.png) -In health-check scripts use `GET /ping` request. This handler always returns “Ok.” (with a line feed at the end). Available from version 18.12.13. If users want to get more sophisticated health checks, it can use the `GET /replicas_status` request. +In health-check scripts use `GET /ping` request. This handler always returns “Ok.” (with a line feed at the end). Available from version 18.12.13. See also `/replicas_status` to check replica's delay. ``` bash $ curl 'http://localhost:8123/ping'