Merge remote-tracking branch 'rschu1ze/master' into more-mysql-compat

This commit is contained in:
Robert Schulze 2023-10-08 07:49:37 +00:00
commit f4bc58ea92
No known key found for this signature in database
GPG Key ID: 26703B55FB13728A
47 changed files with 812 additions and 725 deletions

View File

@ -0,0 +1,16 @@
version: '2.3'
services:
openldap:
image: bitnami/openldap:2.6.6
restart: always
environment:
LDAP_ROOT: dc=example,dc=org
LDAP_ADMIN_DN: cn=admin,dc=example,dc=org
LDAP_ADMIN_USERNAME: admin
LDAP_ADMIN_PASSWORD: clickhouse
LDAP_USER_DC: users
LDAP_USERS: janedoe,johndoe
LDAP_PASSWORDS: qwerty,qwertz
LDAP_PORT_NUMBER: ${LDAP_INTERNAL_PORT:-1389}
ports:
- ${LDAP_EXTERNAL_PORT:-1389}:${LDAP_INTERNAL_PORT:-1389}

View File

@ -60,7 +60,7 @@ Before using cache, add it to `config.xml`
- limit_size: Required. The maximum size(in bytes) of local cache files.
- bytes_read_before_flush: Control bytes before flush to local filesystem when downloading file from remote filesystem. The default value is 1MB.
When ClickHouse is started up with local cache for remote filesystem enabled, users can still choose not to use cache with `settings use_local_cache_for_remote_fs = 0` in their query. `use_local_cache_for_remote_fs` is `false` in default.
When ClickHouse is started up with local cache for remote filesystem enabled, users can still choose not to use cache with `settings use_local_cache_for_remote_storage = 0` in their query. `use_local_cache_for_remote_storage` is `1` by default.
### Query Hive Table with ORC Input Format

View File

@ -69,7 +69,7 @@ may return cached results then.
The query cache can be cleared using statement `SYSTEM DROP QUERY CACHE`. The content of the query cache is displayed in system table
`system.query_cache`. The number of query cache hits and misses since database start are shown as events "QueryCacheHits" and
"QueryCacheMisses" in system table [system.events](system-tables/events.md). Both counters are only updated for `SELECT` queries which run
with setting `use_query_cache = true`, other queries do not affect "QueryCacheMisses". Field `query_log_usage` in system table
with setting `use_query_cache = true`, other queries do not affect "QueryCacheMisses". Field `query_cache_usage` in system table
[system.query_log](system-tables/query_log.md) shows for each executed query whether the query result was written into or read from the
query cache. Asynchronous metrics "QueryCacheEntries" and "QueryCacheBytes" in system table
[system.asynchronous_metrics](system-tables/asynchronous_metrics.md) show how many entries / bytes the query cache currently contains.

View File

@ -502,7 +502,7 @@ Possible values:
Default value: 480.
After merging several parts into a new part, ClickHouse marks the original parts as inactive and deletes them only after `old_parts_lifetime` seconds.
Inactive parts are removed if they are not used by current queries, i.e. if the `refcount` of the part is zero.
Inactive parts are removed if they are not used by current queries, i.e. if the `refcount` of the part is 1.
`fsync` is not called for new parts, so for some time new parts exist only in the server's RAM (OS cache). If the server is rebooted spontaneously, new parts can be lost or damaged.
To protect data inactive parts are not deleted immediately.

View File

@ -3279,6 +3279,17 @@ Possible values:
Default value: `0`.
## use_mysql_types_in_show_columns {#use_mysql_types_in_show_columns}
Show the names of MySQL data types corresponding to ClickHouse data types in [SHOW COLUMNS](../../sql-reference/statements/show.md#show_columns) and SELECTs on [system.columns](../system-tables/columns.md).
Possible values:
- 0 - Show names of native ClickHouse data types.
- 1 - Show names of MySQL data types corresponding to ClickHouse data types.
Default value: `0`.
## execute_merges_on_single_replica_time_threshold {#execute-merges-on-single-replica-time-threshold}
Enables special logic to perform merges on replicas.

View File

@ -14,7 +14,7 @@ The `system.columns` table contains the following columns (the column type is sh
- `database` ([String](../../sql-reference/data-types/string.md)) — Database name.
- `table` ([String](../../sql-reference/data-types/string.md)) — Table name.
- `name` ([String](../../sql-reference/data-types/string.md)) — Column name.
- `type` ([String](../../sql-reference/data-types/string.md)) — Column type.
- `type` ([String](../../sql-reference/data-types/string.md)) — Column type. If setting `[use_mysql_types_in_show_columns](../../operations/settings/settings.md#use_mysql_types_in_show_columns) = 1` (default: 0), then the equivalent type name in MySQL is shown.
- `position` ([UInt64](../../sql-reference/data-types/int-uint.md)) — Ordinal position of a column in a table starting with 1.
- `default_kind` ([String](../../sql-reference/data-types/string.md)) — Expression type (`DEFAULT`, `MATERIALIZED`, `ALIAS`) for the default value, or an empty string if it is not defined.
- `default_expression` ([String](../../sql-reference/data-types/string.md)) — Expression for the default value, or an empty string if it is not defined.

View File

@ -34,10 +34,10 @@ Returns an array of selected substrings. Empty substrings may be selected when:
Type: [Array](../../sql-reference/data-types/array.md)([String](../../sql-reference/data-types/string.md)).
:::note
The behavior of parameter `max_substrings` changed starting with ClickHouse v22.11. In versions older than that, `max_substrings` > 0 meant that `max_substring`-many splits were performed and that the remainder of the string was returned as the final element of the list.
The behavior of parameter `max_substrings` changed starting with ClickHouse v22.11. In versions older than that, `max_substrings > 0` meant that `max_substring`-many splits were performed and that the remainder of the string was returned as the final element of the list.
For example,
- in v22.10: `SELECT splitByChar('=', 'a=b=c=d', 2); -- ['a','b','c=d']`
- in v22.11: `SELECT splitByChar('=', 'a=b=c=d', 2); -- ['a','b']`
- in v22.10: `SELECT splitByChar('=', 'a=b=c=d', 2);` returned `['a','b','c=d']`
- in v22.11: `SELECT splitByChar('=', 'a=b=c=d', 2);` returned `['a','b']`
A behavior similar to ClickHouse pre-v22.11 can be achieved by setting
[splitby_max_substrings_includes_remaining_string](../../operations/settings/settings.md#splitby_max_substrings_includes_remaining_string)

View File

@ -189,7 +189,7 @@ Result:
- [Create Tables](https://clickhouse.com/docs/en/getting-started/tutorial/#create-tables)
- [SHOW CREATE TABLE](https://clickhouse.com/docs/en/sql-reference/statements/show/#show-create-table)
## SHOW COLUMNS
## SHOW COLUMNS {#show_columns}
Displays a list of columns
@ -206,15 +206,15 @@ The optional keyword `EXTENDED` currently has no effect, it only exists for MySQ
The optional keyword `FULL` causes the output to include the collation, comment and privilege columns.
The statement produces a result table with the following structure:
- field - The name of the column (String)
- type - The column data type (String)
- null - `YES` if the column data type is Nullable, `NO` otherwise (String)
- key - `PRI` if the column is part of the primary key, `SOR` if the column is part of the sorting key, empty otherwise (String)
- default - Default expression of the column if it is of type `ALIAS`, `DEFAULT`, or `MATERIALIZED`, otherwise `NULL`. (Nullable(String))
- extra - Additional information, currently unused (String)
- collation - (only if `FULL` keyword was specified) Collation of the column, always `NULL` because ClickHouse has no per-column collations (Nullable(String))
- comment - (only if `FULL` keyword was specified) Comment on the column (String)
- privilege - (only if `FULL` keyword was specified) The privilege you have on this column, currently not available (String)
- `field` - The name of the column (String)
- `type` - The column data type. If setting `[use_mysql_types_in_show_columns](../../operations/settings/settings.md#use_mysql_types_in_show_columns) = 1` (default: 0), then the equivalent type name in MySQL is shown. (String)
- `null` - `YES` if the column data type is Nullable, `NO` otherwise (String)
- `key` - `PRI` if the column is part of the primary key, `SOR` if the column is part of the sorting key, empty otherwise (String)
- `default` - Default expression of the column if it is of type `ALIAS`, `DEFAULT`, or `MATERIALIZED`, otherwise `NULL`. (Nullable(String))
- `extra` - Additional information, currently unused (String)
- `collation` - (only if `FULL` keyword was specified) Collation of the column, always `NULL` because ClickHouse has no per-column collations (Nullable(String))
- `comment` - (only if `FULL` keyword was specified) Comment on the column (String)
- `privilege` - (only if `FULL` keyword was specified) The privilege you have on this column, currently not available (String)
**Examples**
@ -286,21 +286,21 @@ equivalent. If no database is specified, the query assumes the current database
The optional keyword `EXTENDED` currently has no effect, it only exists for MySQL compatibility.
The statement produces a result table with the following structure:
- table - The name of the table. (String)
- non_unique - Always `1` as ClickHouse does not support uniqueness constraints. (UInt8)
- key_name - The name of the index, `PRIMARY` if the index is a primary key index. (String)
- seq_in_index - For a primary key index, the position of the column starting from `1`. For a data skipping index: always `1`. (UInt8)
- column_name - For a primary key index, the name of the column. For a data skipping index: `''` (empty string), see field "expression". (String)
- collation - The sorting of the column in the index: `A` if ascending, `D` if descending, `NULL` if unsorted. (Nullable(String))
- cardinality - An estimation of the index cardinality (number of unique values in the index). Currently always 0. (UInt64)
- sub_part - Always `NULL` because ClickHouse does not support index prefixes like MySQL. (Nullable(String))
- packed - Always `NULL` because ClickHouse does not support packed indexes (like MySQL). (Nullable(String))
- null - Currently unused
- index_type - The index type, e.g. `PRIMARY`, `MINMAX`, `BLOOM_FILTER` etc. (String)
- comment - Additional information about the index, currently always `''` (empty string). (String)
- index_comment - `''` (empty string) because indexes in ClickHouse cannot have a `COMMENT` field (like in MySQL). (String)
- visible - If the index is visible to the optimizer, always `YES`. (String)
- expression - For a data skipping index, the index expression. For a primary key index: `''` (empty string). (String)
- `table` - The name of the table. (String)
- `non_unique` - Always `1` as ClickHouse does not support uniqueness constraints. (UInt8)
- `key_name` - The name of the index, `PRIMARY` if the index is a primary key index. (String)
- `seq_in_index` - For a primary key index, the position of the column starting from `1`. For a data skipping index: always `1`. (UInt8)
- `column_name` - For a primary key index, the name of the column. For a data skipping index: `''` (empty string), see field "expression". (String)
- `collation` - The sorting of the column in the index: `A` if ascending, `D` if descending, `NULL` if unsorted. (Nullable(String))
- `cardinality` - An estimation of the index cardinality (number of unique values in the index). Currently always 0. (UInt64)
- `sub_part` - Always `NULL` because ClickHouse does not support index prefixes like MySQL. (Nullable(String))
- `packed` - Always `NULL` because ClickHouse does not support packed indexes (like MySQL). (Nullable(String))
- `null` - Currently unused
- `index_type` - The index type, e.g. `PRIMARY`, `MINMAX`, `BLOOM_FILTER` etc. (String)
- `comment` - Additional information about the index, currently always `''` (empty string). (String)
- `index_comment` - `''` (empty string) because indexes in ClickHouse cannot have a `COMMENT` field (like in MySQL). (String)
- `visible` - If the index is visible to the optimizer, always `YES`. (String)
- `expression` - For a data skipping index, the index expression. For a primary key index: `''` (empty string). (String)
**Examples**

View File

@ -61,7 +61,7 @@ PARTITION BY expr
- limit_size: 必需的。本地缓存文件的最大大小(单位为字节)。
- bytes_read_before_flush: 从远程文件系统下载文件时刷新到本地文件系统前的控制字节数。缺省值为1MB。
当ClickHouse为远程文件系统启用了本地缓存时用户仍然可以选择不使用缓存并在查询中设置`use_local_cache_for_remote_fs = 0 `, `use_local_cache_for_remote_fs` 默认为 `false`。
当ClickHouse为远程文件系统启用了本地缓存时用户仍然可以选择不使用缓存并在查询中设置 `use_local_cache_for_remote_storage = 0`, `use_local_cache_for_remote_storage` 默认为 `1`。
### 查询 ORC 输入格式的Hive 表

View File

@ -84,8 +84,8 @@ function deb2tgz {
FILE=$1
PKG_NAME=${FILE##*/}; PKG_NAME=${PKG_NAME%%_*}
PKG_DIR="$PKG_NAME-$CLICKHOUSE_VERSION_STRING"
PKG_PATH="$OUTPUT_DIR/$PKG_NAME-$CLICKHOUSE_VERSION_STRING"
TARBALL="$OUTPUT_DIR/$PKG_NAME-$CLICKHOUSE_VERSION_STRING-$DEB_ARCH.tgz"
PKG_PATH="$OUTPUT_DIR/$PKG_DIR"
TARBALL="$OUTPUT_DIR/$PKG_DIR-$DEB_ARCH.tgz"
rm -rf "$PKG_PATH"
dpkg-deb -R "$FILE" "$PKG_PATH"
mkdir -p "$PKG_PATH/install"

View File

@ -1,6 +1,13 @@
# package sources should be placed in ${PWD}/root
# nfpm should run from the same directory with a config
name: "clickhouse-client"
description: |
Client binary for ClickHouse
ClickHouse is a column-oriented database management system.
that allows generating analytical data reports in real time.
This package provides clickhouse-client, clickhouse-local and clickhouse-benchmark.
# Common packages config
arch: "${DEB_ARCH}" # amd64, arm64
platform: "linux"
version: "${CLICKHOUSE_VERSION_STRING}"
@ -9,19 +16,17 @@ homepage: "https://clickhouse.com"
license: "Apache"
section: "database"
priority: "optional"
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
deb:
fields:
Source: clickhouse
# Package specific content
replaces:
- clickhouse-compressor
conflicts:
- clickhouse-compressor
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
description: |
Client binary for ClickHouse
ClickHouse is a column-oriented database management system.
that allows generating analytical data reports in real time.
This package provides clickhouse-client, clickhouse-local and clickhouse-benchmark.
overrides:
deb:
depends:
@ -30,10 +35,6 @@ overrides:
depends:
- clickhouse-common-static = ${CLICKHOUSE_VERSION_STRING}
deb:
fields:
Source: clickhouse
contents:
- src: root/etc/clickhouse-client/config.xml
dst: /etc/clickhouse-client/config.xml

View File

@ -1,6 +1,13 @@
# package sources should be placed in ${PWD}/root
# nfpm should run from the same directory with a config
name: "clickhouse-common-static-dbg"
description: |
debugging symbols for clickhouse-common-static
This package contains the debugging symbols for clickhouse-common.
#
# Common packages config
arch: "${DEB_ARCH}" # amd64, arm64
platform: "linux"
version: "${CLICKHOUSE_VERSION_STRING}"
@ -9,21 +16,17 @@ homepage: "https://clickhouse.com"
license: "Apache"
section: "database"
priority: "optional"
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
deb:
fields:
Source: clickhouse
# Package specific content
replaces:
- clickhouse-common-dbg
conflicts:
- clickhouse-common-dbg
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
description: |
debugging symbols for clickhouse-common-static
This package contains the debugging symbols for clickhouse-common.
deb:
fields:
Source: clickhouse
contents:
- src: root/usr/lib/debug/usr/bin/clickhouse.debug
dst: /usr/lib/debug/usr/bin/clickhouse.debug

View File

@ -1,6 +1,13 @@
# package sources should be placed in ${PWD}/root
# nfpm should run from the same directory with a config
name: "clickhouse-common-static"
description: |
Common files for ClickHouse
ClickHouse is a column-oriented database management system
that allows generating analytical data reports in real time.
This package provides common files for both clickhouse server and client
# Common packages config
arch: "${DEB_ARCH}" # amd64, arm64
platform: "linux"
version: "${CLICKHOUSE_VERSION_STRING}"
@ -9,7 +16,12 @@ homepage: "https://clickhouse.com"
license: "Apache"
section: "database"
priority: "optional"
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
deb:
fields:
Source: clickhouse
# Package specific content
replaces:
- clickhouse-common
- clickhouse-server-base
@ -19,17 +31,6 @@ provides:
suggests:
- clickhouse-common-static-dbg
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
description: |
Common files for ClickHouse
ClickHouse is a column-oriented database management system
that allows generating analytical data reports in real time.
This package provides common files for both clickhouse server and client
deb:
fields:
Source: clickhouse
contents:
- src: root/usr/bin/clickhouse
dst: /usr/bin/clickhouse

View File

@ -1,6 +1,13 @@
# package sources should be placed in ${PWD}/root
# nfpm should run from the same directory with a config
name: "clickhouse-keeper-dbg"
description: |
debugging symbols for clickhouse-keeper
This package contains the debugging symbols for clickhouse-keeper.
#
# Common packages config
arch: "${DEB_ARCH}" # amd64, arm64
platform: "linux"
version: "${CLICKHOUSE_VERSION_STRING}"
@ -10,14 +17,11 @@ license: "Apache"
section: "database"
priority: "optional"
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
description: |
debugging symbols for clickhouse-keeper
This package contains the debugging symbols for clickhouse-keeper.
deb:
fields:
Source: clickhouse
# Package specific content
contents:
- src: root/usr/lib/debug/usr/bin/clickhouse-keeper.debug
dst: /usr/lib/debug/usr/bin/clickhouse-keeper.debug

View File

@ -3,12 +3,12 @@ set -e
# set -x
PROGRAM=clickhouse-keeper
KEEPER_USER=${KEEPER_USER:=clickhouse}
KEEPER_GROUP=${KEEPER_GROUP:=clickhouse}
KEEPER_USER=${KEEPER_USER:-clickhouse}
KEEPER_GROUP=${KEEPER_GROUP:-clickhouse}
# Please note that we don't support paths with whitespaces. This is rather ignorant.
KEEPER_CONFDIR=${KEEPER_CONFDIR:=/etc/$PROGRAM}
KEEPER_DATADIR=${KEEPER_DATADIR:=/var/lib/clickhouse}
KEEPER_LOGDIR=${KEEPER_LOGDIR:=/var/log/$PROGRAM}
KEEPER_CONFDIR=${KEEPER_CONFDIR:-/etc/$PROGRAM}
KEEPER_DATADIR=${KEEPER_DATADIR:-/var/lib/clickhouse}
KEEPER_LOGDIR=${KEEPER_LOGDIR:-/var/log/$PROGRAM}
[ -f /usr/share/debconf/confmodule ] && . /usr/share/debconf/confmodule
[ -f /etc/default/clickhouse-keeper ] && . /etc/default/clickhouse-keeper

View File

@ -1,6 +1,13 @@
# package sources should be placed in ${PWD}/root
# nfpm should run from the same directory with a config
name: "clickhouse-keeper"
description: |
Static clickhouse-keeper binary
A stand-alone clickhouse-keeper package
#
# Common packages config
arch: "${DEB_ARCH}" # amd64, arm64
platform: "linux"
version: "${CLICKHOUSE_VERSION_STRING}"
@ -9,29 +16,25 @@ homepage: "https://clickhouse.com"
license: "Apache"
section: "database"
priority: "optional"
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
deb:
fields:
Source: clickhouse
# Package specific content
conflicts:
- clickhouse-server
suggests:
- clickhouse-keeper-dbg
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
description: |
Static clickhouse-keeper binary
A stand-alone clickhouse-keeper package
deb:
fields:
Source: clickhouse
contents:
- src: root/etc/clickhouse-keeper/keeper_config.xml
dst: /etc/clickhouse-keeper/keeper_config.xml
type: config|noreplace
- src: root/usr/bin/clickhouse-keeper
dst: /usr/bin/clickhouse-keeper
- src: clickhouse-keeper.service
dst: /lib/systemd/system/clickhouse-keeper.service
- src: root/usr/bin/clickhouse-keeper
dst: /usr/bin/clickhouse-keeper
- src: clickhouse-keeper
dst: /usr/bin/clickhouse-keeper-client
type: symlink

View File

@ -3,16 +3,21 @@ set -e
# set -x
PROGRAM=clickhouse-server
CLICKHOUSE_USER=${CLICKHOUSE_USER:=clickhouse}
CLICKHOUSE_GROUP=${CLICKHOUSE_GROUP:=${CLICKHOUSE_USER}}
CLICKHOUSE_USER=${CLICKHOUSE_USER:-clickhouse}
CLICKHOUSE_GROUP=${CLICKHOUSE_GROUP:-${CLICKHOUSE_USER}}
# Please note that we don't support paths with whitespaces. This is rather ignorant.
CLICKHOUSE_CONFDIR=${CLICKHOUSE_CONFDIR:=/etc/clickhouse-server}
CLICKHOUSE_DATADIR=${CLICKHOUSE_DATADIR:=/var/lib/clickhouse}
CLICKHOUSE_LOGDIR=${CLICKHOUSE_LOGDIR:=/var/log/clickhouse-server}
CLICKHOUSE_BINDIR=${CLICKHOUSE_BINDIR:=/usr/bin}
CLICKHOUSE_GENERIC_PROGRAM=${CLICKHOUSE_GENERIC_PROGRAM:=clickhouse}
CLICKHOUSE_CONFDIR=${CLICKHOUSE_CONFDIR:-/etc/clickhouse-server}
CLICKHOUSE_DATADIR=${CLICKHOUSE_DATADIR:-/var/lib/clickhouse}
CLICKHOUSE_LOGDIR=${CLICKHOUSE_LOGDIR:-/var/log/clickhouse-server}
CLICKHOUSE_BINDIR=${CLICKHOUSE_BINDIR:-/usr/bin}
CLICKHOUSE_GENERIC_PROGRAM=${CLICKHOUSE_GENERIC_PROGRAM:-clickhouse}
CLICKHOUSE_PIDDIR=/var/run/$PROGRAM
# Provide clickhouse-keeper
KEEPER_CONFDIR=${KEEPER_CONFDIR:-/etc/clickhouse-keeper}
KEEPER_DATADIR=${KEEPER_DATADIR:-/var/lib/clickhouse}
KEEPER_LOGDIR=${KEEPER_LOGDIR:-/var/log/clickhouse-keeper}
[ -f /usr/share/debconf/confmodule ] && . /usr/share/debconf/confmodule
[ -f /etc/default/clickhouse ] && . /etc/default/clickhouse
@ -54,4 +59,20 @@ if [ "$1" = configure ] || [ -n "$not_deb_os" ]; then
fi
done
fi
# Setup clickhouse-keeper directories
chown -R "${CLICKHOUSE_USER}:${CLICKHOUSE_GROUP}" "${KEEPER_CONFDIR}"
chmod 0755 "${KEEPER_CONFDIR}"
if ! [ -d "${KEEPER_DATADIR}" ]; then
mkdir -p "${KEEPER_DATADIR}"
chown -R "${CLICKHOUSE_USER}:${CLICKHOUSE_GROUP}" "${KEEPER_DATADIR}"
chmod 0700 "${KEEPER_DATADIR}"
fi
if ! [ -d "${KEEPER_LOGDIR}" ]; then
mkdir -p "${KEEPER_LOGDIR}"
chown -R "${CLICKHOUSE_USER}:${CLICKHOUSE_GROUP}" "${KEEPER_LOGDIR}"
chmod 0770 "${KEEPER_LOGDIR}"
fi
fi

View File

@ -1,6 +1,13 @@
# package sources should be placed in ${PWD}/root
# nfpm should run from the same directory with a config
name: "clickhouse-server"
description: |
Server binary for ClickHouse
ClickHouse is a column-oriented database management system
that allows generating analytical data reports in real time.
This package provides clickhouse common configuration files
# Common packages config
arch: "${DEB_ARCH}" # amd64, arm64
platform: "linux"
version: "${CLICKHOUSE_VERSION_STRING}"
@ -9,24 +16,21 @@ homepage: "https://clickhouse.com"
license: "Apache"
section: "database"
priority: "optional"
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
deb:
fields:
Source: clickhouse
conflicts:
- clickhouse-keeper
# Package specific content
replaces:
- clickhouse-server-common
- clickhouse-server-base
provides:
- clickhouse-keeper
- clickhouse-server-common
recommends:
- libcap2-bin
maintainer: "ClickHouse Dev Team <packages+linux@clickhouse.com>"
description: |
Server binary for ClickHouse
ClickHouse is a column-oriented database management system
that allows generating analytical data reports in real time.
This package provides clickhouse common configuration files
overrides:
deb:
depends:
@ -35,10 +39,6 @@ overrides:
depends:
- clickhouse-common-static = ${CLICKHOUSE_VERSION_STRING}
deb:
fields:
Source: clickhouse
contents:
- src: root/etc/clickhouse-server/config.xml
dst: /etc/clickhouse-server/config.xml
@ -52,16 +52,25 @@ contents:
dst: /lib/systemd/system/clickhouse-server.service
- src: root/usr/bin/clickhouse-copier
dst: /usr/bin/clickhouse-copier
- src: root/usr/bin/clickhouse-report
dst: /usr/bin/clickhouse-report
- src: root/usr/bin/clickhouse-server
dst: /usr/bin/clickhouse-server
# clickhouse-keeper part
- src: root/etc/clickhouse-keeper/keeper_config.xml
dst: /etc/clickhouse-keeper/keeper_config.xml
type: config|noreplace
- src: clickhouse-keeper.service
dst: /lib/systemd/system/clickhouse-keeper.service
- src: clickhouse
dst: /usr/bin/clickhouse-keeper
type: symlink
- src: clickhouse
dst: /usr/bin/clickhouse-keeper-client
type: symlink
- src: root/usr/bin/clickhouse-report
dst: /usr/bin/clickhouse-report
- src: root/usr/bin/clickhouse-server
dst: /usr/bin/clickhouse-server
- src: clickhouse
dst: /usr/bin/clickhouse-keeper-converter
type: symlink
# docs
- src: ../AUTHORS
dst: /usr/share/doc/clickhouse-server/AUTHORS

View File

@ -4,6 +4,16 @@
namespace DB
{
std::vector<UUID> EnabledRolesInfo::getCurrentRoles() const
{
return std::vector<UUID>{current_roles.begin(), current_roles.end()};
}
std::vector<UUID> EnabledRolesInfo::getEnabledRoles() const
{
return std::vector<UUID>{enabled_roles.begin(), enabled_roles.end()};
}
Strings EnabledRolesInfo::getCurrentRolesNames() const
{
Strings result;

View File

@ -20,6 +20,9 @@ struct EnabledRolesInfo
AccessRights access;
SettingsProfileElements settings_from_enabled_roles;
std::vector<UUID> getCurrentRoles() const;
std::vector<UUID> getEnabledRoles() const;
Strings getCurrentRolesNames() const;
Strings getEnabledRolesNames() const;

View File

@ -61,7 +61,7 @@ private: // IAccessStorage implementations.
bool areLDAPCredentialsValidNoLock(const User & user, const Credentials & credentials,
const ExternalAuthenticators & external_authenticators, LDAPClient::SearchResultsList & role_search_results) const;
mutable std::recursive_mutex mutex;
mutable std::recursive_mutex mutex; // Note: Reentrace possible by internal role lookup via access_control
AccessControl & access_control;
String ldap_server_name;
LDAPClient::RoleSearchParamsList role_search_params;

View File

@ -44,12 +44,12 @@ private:
bool removeImpl(const UUID & id, bool throw_if_not_exists) override;
bool updateImpl(const UUID & id, const UpdateFunc & update_func, bool throw_if_not_exists) override;
bool insertNoLock(const UUID & id, const AccessEntityPtr & entity, bool replace_if_exists, bool throw_if_exists) TSA_REQUIRES(mutex);
bool removeNoLock(const UUID & id, bool throw_if_not_exists) TSA_REQUIRES(mutex);
bool updateNoLock(const UUID & id, const UpdateFunc & update_func, bool throw_if_not_exists) TSA_REQUIRES(mutex);
bool insertNoLock(const UUID & id, const AccessEntityPtr & entity, bool replace_if_exists, bool throw_if_exists);
bool removeNoLock(const UUID & id, bool throw_if_not_exists);
bool updateNoLock(const UUID & id, const UpdateFunc & update_func, bool throw_if_not_exists);
void removeAllExceptNoLock(const std::vector<UUID> & ids_to_keep) TSA_REQUIRES(mutex);
void removeAllExceptNoLock(const boost::container::flat_set<UUID> & ids_to_keep) TSA_REQUIRES(mutex);
void removeAllExceptNoLock(const std::vector<UUID> & ids_to_keep);
void removeAllExceptNoLock(const boost::container::flat_set<UUID> & ids_to_keep);
struct Entry
{
@ -57,9 +57,9 @@ private:
AccessEntityPtr entity;
};
mutable std::mutex mutex;
std::unordered_map<UUID, Entry> entries_by_id TSA_GUARDED_BY(mutex); /// We want to search entries both by ID and by the pair of name and type.
std::unordered_map<String, Entry *> entries_by_name_and_type[static_cast<size_t>(AccessEntityType::MAX)] TSA_GUARDED_BY(mutex);
mutable std::recursive_mutex mutex; // Note: Reentrace possible via LDAPAccessStorage
std::unordered_map<UUID, Entry> entries_by_id; /// We want to search entries both by ID and by the pair of name and type.
std::unordered_map<String, Entry *> entries_by_name_and_type[static_cast<size_t>(AccessEntityType::MAX)];
AccessChangesNotifier & changes_notifier;
const bool backup_allowed = false;
};

View File

@ -205,7 +205,7 @@ class IColumn;
M(Bool, allow_experimental_inverted_index, false, "If it is set to true, allow to use experimental inverted index.", 0) \
\
M(UInt64, mysql_max_rows_to_insert, 65536, "The maximum number of rows in MySQL batch insertion of the MySQL storage engine", 0) \
M(Bool, use_mysql_types_in_show_columns, false, "Use MySQL converted types when connected via MySQL compatibility for show columns query", 0) \
M(Bool, use_mysql_types_in_show_columns, false, "Show MySQL types in SHOW COLUMNS and system.columns", 0) \
\
M(UInt64, optimize_min_equality_disjunction_chain_length, 3, "The minimum length of the expression `expr = x1 OR ... expr = xN` for optimization ", 0) \
\

View File

@ -14,6 +14,8 @@
#include <Parsers/ASTIdentifier.h>
#include <Common/quoteString.h>
#include <boost/container/flat_set.hpp>
namespace DB
{

View File

@ -60,10 +60,12 @@ namespace ErrorCodes
extern const int BAD_ARGUMENTS;
}
AsynchronousInsertQueue::InsertQuery::InsertQuery(const ASTPtr & query_, const Settings & settings_)
AsynchronousInsertQueue::InsertQuery::InsertQuery(const ASTPtr & query_, const Settings & settings_, const std::optional<UUID> & user_id_, const std::vector<UUID> & current_roles_)
: query(query_->clone())
, query_str(queryToString(query))
, settings(settings_)
, user_id(user_id_)
, current_roles(current_roles_)
, hash(calculateHash())
{
}
@ -72,6 +74,8 @@ AsynchronousInsertQueue::InsertQuery::InsertQuery(const InsertQuery & other)
: query(other.query->clone())
, query_str(other.query_str)
, settings(other.settings)
, user_id(other.user_id)
, current_roles(other.current_roles)
, hash(other.hash)
{
}
@ -83,6 +87,8 @@ AsynchronousInsertQueue::InsertQuery::operator=(const InsertQuery & other)
{
query = other.query->clone();
query_str = other.query_str;
user_id = other.user_id;
current_roles = other.current_roles;
settings = other.settings;
hash = other.hash;
}
@ -95,6 +101,13 @@ UInt128 AsynchronousInsertQueue::InsertQuery::calculateHash() const
SipHash siphash;
query->updateTreeHash(siphash);
if (user_id)
{
siphash.update(*user_id);
for (const auto & current_role : current_roles)
siphash.update(current_role);
}
for (const auto & setting : settings.allChanged())
{
/// We don't consider this setting because it is only for deduplication,
@ -110,7 +123,7 @@ UInt128 AsynchronousInsertQueue::InsertQuery::calculateHash() const
bool AsynchronousInsertQueue::InsertQuery::operator==(const InsertQuery & other) const
{
return query_str == other.query_str && settings == other.settings;
return query_str == other.query_str && user_id == other.user_id && current_roles == other.current_roles && settings == other.settings;
}
AsynchronousInsertQueue::InsertData::Entry::Entry(String && bytes_, String && query_id_, const String & async_dedup_token_, MemoryTracker * user_memory_tracker_)
@ -257,12 +270,9 @@ AsynchronousInsertQueue::push(ASTPtr query, ContextPtr query_context)
}
}
if (auto quota = query_context->getQuota())
quota->used(QuotaType::WRITTEN_BYTES, bytes.size());
auto entry = std::make_shared<InsertData::Entry>(std::move(bytes), query_context->getCurrentQueryId(), settings.insert_deduplication_token, CurrentThread::getUserMemoryTracker());
InsertQuery key{query, settings};
InsertQuery key{query, settings, query_context->getUserID(), query_context->getCurrentRoles()};
InsertDataPtr data_to_process;
std::future<void> insert_future;
@ -469,6 +479,11 @@ try
/// 'resetParser' doesn't work for parallel parsing.
key.settings.set("input_format_parallel_parsing", false);
insert_context->makeQueryContext();
/// Access rights must be checked for the user who executed the initial INSERT query.
if (key.user_id)
insert_context->setUser(*key.user_id, key.current_roles);
insert_context->setSettings(key.settings);
/// Set initial_query_id, because it's used in InterpreterInsertQuery for table lock.

View File

@ -53,9 +53,11 @@ private:
ASTPtr query;
String query_str;
Settings settings;
std::optional<UUID> user_id;
std::vector<UUID> current_roles;
UInt128 hash;
InsertQuery(const ASTPtr & query_, const Settings & settings_);
InsertQuery(const ASTPtr & query_, const Settings & settings_, const std::optional<UUID> & user_id_, const std::vector<UUID> & current_roles_);
InsertQuery(const InsertQuery & other);
InsertQuery & operator=(const InsertQuery & other);
bool operator==(const InsertQuery & other) const;

View File

@ -10,6 +10,8 @@
#include <Common/ProfileEvents.h>
#include <Common/SipHash.h>
#include <Common/TTLCachePolicy.h>
#include <Common/formatReadable.h>
#include <Common/quoteString.h>
#include <Core/Settings.h>
#include <base/defines.h> /// chassert
@ -190,7 +192,7 @@ QueryCache::Writer::Writer(
if (auto entry = cache.getWithKey(key); entry.has_value() && !IsStale()(entry->key))
{
skip_insert = true; /// Key already contained in cache and did not expire yet --> don't replace it
LOG_TRACE(logger, "Skipped insert (non-stale entry found), query: {}", key.query_string);
LOG_TRACE(logger, "Skipped insert because the cache contains a non-stale query result for query {}", doubleQuoteString(key.query_string));
}
}
@ -260,16 +262,17 @@ void QueryCache::Writer::finalizeWrite()
/// Check some reasons why the entry must not be cached:
if (std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - query_start_time) < min_query_runtime)
if (auto query_runtime = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now() - query_start_time); query_runtime < min_query_runtime)
{
LOG_TRACE(logger, "Skipped insert (query not expensive enough), query: {}", key.query_string);
LOG_TRACE(logger, "Skipped insert because the query is not expensive enough, query runtime: {} msec (minimum query runtime: {} msec), query: {}",
query_runtime.count(), min_query_runtime.count(), doubleQuoteString(key.query_string));
return;
}
if (auto entry = cache.getWithKey(key); entry.has_value() && !IsStale()(entry->key))
{
/// Same check as in ctor because a parallel Writer could have inserted the current key in the meantime
LOG_TRACE(logger, "Skipped insert (non-stale entry found), query: {}", key.query_string);
LOG_TRACE(logger, "Skipped insert because the cache contains a non-stale query result for query {}", doubleQuoteString(key.query_string));
return;
}
@ -352,13 +355,14 @@ void QueryCache::Writer::finalizeWrite()
if ((new_entry_size_in_bytes > max_entry_size_in_bytes) || (new_entry_size_in_rows > max_entry_size_in_rows))
{
LOG_TRACE(logger, "Skipped insert (query result too big), new_entry_size_in_bytes: {} ({}), new_entry_size_in_rows: {} ({}), query: {}", new_entry_size_in_bytes, max_entry_size_in_bytes, new_entry_size_in_rows, max_entry_size_in_rows, key.query_string);
LOG_TRACE(logger, "Skipped insert because the query result is too big, query result size: {} (maximum size: {}), query result size in rows: {} (maximum size: {}), query: {}",
formatReadableSizeWithBinarySuffix(new_entry_size_in_bytes, 0), formatReadableSizeWithBinarySuffix(max_entry_size_in_bytes, 0), new_entry_size_in_rows, max_entry_size_in_rows, doubleQuoteString(key.query_string));
return;
}
cache.set(key, query_result);
LOG_TRACE(logger, "Stored query result, query: {}", key.query_string);
LOG_TRACE(logger, "Stored query result of query {}", doubleQuoteString(key.query_string));
was_finalized = true;
}
@ -389,7 +393,7 @@ QueryCache::Reader::Reader(Cache & cache_, const Key & key, const std::lock_guar
if (!entry.has_value())
{
LOG_TRACE(logger, "No entry found for query {}", key.query_string);
LOG_TRACE(logger, "No query result found for query {}", doubleQuoteString(key.query_string));
return;
}
@ -398,13 +402,13 @@ QueryCache::Reader::Reader(Cache & cache_, const Key & key, const std::lock_guar
if (!entry_key.is_shared && entry_key.user_name != key.user_name)
{
LOG_TRACE(logger, "Inaccessible entry found for query {}", key.query_string);
LOG_TRACE(logger, "Inaccessible query result found for query {}", doubleQuoteString(key.query_string));
return;
}
if (IsStale()(entry_key))
{
LOG_TRACE(logger, "Stale entry found for query {}", key.query_string);
LOG_TRACE(logger, "Stale query result found for query {}", doubleQuoteString(key.query_string));
return;
}
@ -442,7 +446,7 @@ QueryCache::Reader::Reader(Cache & cache_, const Key & key, const std::lock_guar
buildSourceFromChunks(entry_key.header, std::move(decompressed_chunks), entry_mapped->totals, entry_mapped->extremes);
}
LOG_TRACE(logger, "Entry found for query {}", key.query_string);
LOG_TRACE(logger, "Query result found for query {}", doubleQuoteString(key.query_string));
}
bool QueryCache::Reader::hasCacheEntryForKey() const

View File

@ -1209,14 +1209,14 @@ void Context::setCurrentRolesDefault()
setCurrentRoles(user->granted_roles.findGranted(user->default_roles));
}
boost::container::flat_set<UUID> Context::getCurrentRoles() const
std::vector<UUID> Context::getCurrentRoles() const
{
return getRolesInfo()->current_roles;
return getRolesInfo()->getCurrentRoles();
}
boost::container::flat_set<UUID> Context::getEnabledRoles() const
std::vector<UUID> Context::getEnabledRoles() const
{
return getRolesInfo()->enabled_roles;
return getRolesInfo()->getEnabledRoles();
}
std::shared_ptr<const EnabledRolesInfo> Context::getRolesInfo() const
@ -3418,7 +3418,9 @@ void Context::initializeSystemLogs()
/// for example, system.filesystem_cache_log will be triggered by parts loading
/// of any other table if it is stored on a disk with cache.
callOnce(shared->system_logs_initializer, [&] {
shared->system_logs = std::make_unique<SystemLogs>(getGlobalContext(), getConfigRef());
auto system_logs = std::make_unique<SystemLogs>(getGlobalContext(), getConfigRef());
auto lock = getGlobalLock();
shared->system_logs = std::move(system_logs);
});
}

View File

@ -28,7 +28,6 @@
#include "config.h"
#include <boost/container/flat_set.hpp>
#include <functional>
#include <memory>
#include <mutex>
@ -571,8 +570,8 @@ public:
void setCurrentRoles(const std::vector<UUID> & current_roles_);
void setCurrentRolesDefault();
boost::container::flat_set<UUID> getCurrentRoles() const;
boost::container::flat_set<UUID> getEnabledRoles() const;
std::vector<UUID> getCurrentRoles() const;
std::vector<UUID> getEnabledRoles() const;
std::shared_ptr<const EnabledRolesInfo> getRolesInfo() const;
void setCurrentProfile(const String & profile_name, bool check_constraints = true);

View File

@ -1098,7 +1098,7 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
{
if (astContainsNonDeterministicFunctions(ast, context) && !settings.query_cache_store_results_of_queries_with_nondeterministic_functions)
throw Exception(ErrorCodes::CANNOT_USE_QUERY_CACHE_WITH_NONDETERMINISTIC_FUNCTIONS,
"Unable to cache the query result because the query contains a non-deterministic function. Use setting query_cache_store_results_of_queries_with_nondeterministic_functions = 1 to store the query result regardless.");
"Unable to cache the query result because the query contains a non-deterministic function. Use setting `query_cache_store_results_of_queries_with_nondeterministic_functions = 1` to cache the query result regardless");
QueryCache::Key key(
ast, res.pipeline.getHeader(),
@ -1107,7 +1107,11 @@ static std::tuple<ASTPtr, BlockIO> executeQueryImpl(
settings.query_cache_compress_entries);
const size_t num_query_runs = query_cache->recordQueryRun(key);
if (num_query_runs > settings.query_cache_min_query_runs)
if (num_query_runs <= settings.query_cache_min_query_runs)
{
LOG_TRACE(&Poco::Logger::get("QueryCache"), "Skipped insert because the query ran {} times but the minimum required number of query runs to cache the query result is {}", num_query_runs, settings.query_cache_min_query_runs);
}
else
{
auto query_cache_writer = std::make_shared<QueryCache::Writer>(query_cache->createWriter(
key,

View File

@ -1263,8 +1263,12 @@ HTTPRequestHandlerFactoryPtr createDynamicHandlerFactory(IServer & server,
if (config.has(config_prefix + ".handler.content_type"))
content_type_override = config.getString(config_prefix + ".handler.content_type");
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(
server, std::move(query_param_name), std::move(content_type_override));
auto creator = [&server, query_param_name, content_type_override] () -> std::unique_ptr<DynamicQueryHandler>
{
return std::make_unique<DynamicQueryHandler>(server, query_param_name, content_type_override);
};
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(std::move(creator));
factory->addFiltersFromConfig(config, config_prefix);
@ -1335,25 +1339,40 @@ HTTPRequestHandlerFactoryPtr createPredefinedHandlerFactory(IServer & server,
auto regex = getCompiledRegex(url_expression);
if (capturingNamedQueryParam(analyze_receive_params, regex))
{
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(
server,
std::move(analyze_receive_params),
std::move(predefined_query),
std::move(regex),
std::move(headers_name_with_regex),
std::move(content_type_override));
auto creator = [
&server,
analyze_receive_params,
predefined_query,
regex,
headers_name_with_regex,
content_type_override]
-> std::unique_ptr<PredefinedQueryHandler>
{
return std::make_unique<PredefinedQueryHandler>(
server, analyze_receive_params, predefined_query, regex,
headers_name_with_regex, content_type_override);
};
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(std::move(creator));
factory->addFiltersFromConfig(config, config_prefix);
return factory;
}
}
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(
server,
std::move(analyze_receive_params),
std::move(predefined_query),
CompiledRegexPtr{},
std::move(headers_name_with_regex),
std::move(content_type_override));
auto creator = [
&server,
analyze_receive_params,
predefined_query,
headers_name_with_regex,
content_type_override]
-> std::unique_ptr<PredefinedQueryHandler>
{
return std::make_unique<PredefinedQueryHandler>(
server, analyze_receive_params, predefined_query, CompiledRegexPtr{},
headers_name_with_regex, content_type_override);
};
factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PredefinedQueryHandler>>(std::move(creator));
factory->addFiltersFromConfig(config, config_prefix);
return factory;

View File

@ -119,17 +119,25 @@ HTTPRequestHandlerFactoryPtr createHandlerFactory(IServer & server, const Poco::
throw Exception(ErrorCodes::LOGICAL_ERROR, "LOGICAL ERROR: Unknown HTTP handler factory name.");
}
static const auto ping_response_expression = "Ok.\n";
static const auto root_response_expression = "config://http_server_default_response";
void addCommonDefaultHandlersFactory(HTTPRequestHandlerFactoryMain & factory, IServer & server)
{
auto root_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(server, root_response_expression);
auto root_creator = [&server]() -> std::unique_ptr<StaticRequestHandler>
{
constexpr auto root_response_expression = "config://http_server_default_response";
return std::make_unique<StaticRequestHandler>(server, root_response_expression);
};
auto root_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(std::move(root_creator));
root_handler->attachStrictPath("/");
root_handler->allowGetAndHeadRequest();
factory.addHandler(root_handler);
auto ping_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(server, ping_response_expression);
auto ping_creator = [&server]() -> std::unique_ptr<StaticRequestHandler>
{
constexpr auto ping_response_expression = "Ok.\n";
return std::make_unique<StaticRequestHandler>(server, ping_response_expression);
};
auto ping_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(std::move(ping_creator));
ping_handler->attachStrictPath("/ping");
ping_handler->allowGetAndHeadRequest();
factory.addPathToHints("/ping");
@ -167,7 +175,11 @@ void addDefaultHandlersFactory(
{
addCommonDefaultHandlersFactory(factory, server);
auto query_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(server, "query");
auto dynamic_creator = [&server] () -> std::unique_ptr<DynamicQueryHandler>
{
return std::make_unique<DynamicQueryHandler>(server, "query");
};
auto query_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<DynamicQueryHandler>>(std::move(dynamic_creator));
query_handler->allowPostAndGetParamsAndOptionsRequest();
factory.addHandler(query_handler);
@ -175,8 +187,12 @@ void addDefaultHandlersFactory(
/// Otherwise it will be created separately, see createHandlerFactory(...).
if (config.has("prometheus") && config.getInt("prometheus.port", 0) == 0)
{
auto prometheus_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>>(
server, PrometheusMetricsWriter(config, "prometheus", async_metrics));
PrometheusMetricsWriter writer(config, "prometheus", async_metrics);
auto creator = [&server, writer] () -> std::unique_ptr<PrometheusRequestHandler>
{
return std::make_unique<PrometheusRequestHandler>(server, writer);
};
auto prometheus_handler = std::make_shared<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>>(std::move(creator));
prometheus_handler->attachStrictPath(config.getString("prometheus.endpoint", "/metrics"));
prometheus_handler->allowGetAndHeadRequest();
factory.addHandler(prometheus_handler);

View File

@ -25,18 +25,17 @@ class HandlingRuleHTTPHandlerFactory : public HTTPRequestHandlerFactory
public:
using Filter = std::function<bool(const HTTPServerRequest &)>;
template <typename... TArgs>
explicit HandlingRuleHTTPHandlerFactory(TArgs &&... args)
using Creator = std::function<std::unique_ptr<TEndpoint>()>;
explicit HandlingRuleHTTPHandlerFactory(Creator && creator_)
: creator(std::move(creator_))
{}
explicit HandlingRuleHTTPHandlerFactory(IServer & server)
{
creator = [my_args = std::tuple<TArgs...>(std::forward<TArgs>(args) ...)]()
{
return std::apply([&](auto && ... endpoint_args)
{
return std::make_unique<TEndpoint>(std::forward<decltype(endpoint_args)>(endpoint_args)...);
}, std::move(my_args));
};
creator = [&server]() -> std::unique_ptr<TEndpoint> { return std::make_unique<TEndpoint>(server); };
}
void addFilter(Filter cur_filter)
{
Filter prev_filter = filter;

View File

@ -47,25 +47,31 @@ createPrometheusHandlerFactory(IServer & server,
AsynchronousMetrics & async_metrics,
const std::string & config_prefix)
{
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>>(
server, PrometheusMetricsWriter(config, config_prefix + ".handler", async_metrics));
PrometheusMetricsWriter writer(config, config_prefix + ".handler", async_metrics);
auto creator = [&server, writer]() -> std::unique_ptr<PrometheusRequestHandler>
{
return std::make_unique<PrometheusRequestHandler>(server, writer);
};
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>>(std::move(creator));
factory->addFiltersFromConfig(config, config_prefix);
return factory;
}
HTTPRequestHandlerFactoryPtr
createPrometheusMainHandlerFactory(IServer & server,
const Poco::Util::AbstractConfiguration & config,
AsynchronousMetrics & async_metrics,
const std::string & name)
HTTPRequestHandlerFactoryPtr createPrometheusMainHandlerFactory(
IServer & server, const Poco::Util::AbstractConfiguration & config, AsynchronousMetrics & async_metrics, const std::string & name)
{
auto factory = std::make_shared<HTTPRequestHandlerFactoryMain>(name);
auto handler = std::make_shared<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>>(
server, PrometheusMetricsWriter(config, "prometheus", async_metrics));
PrometheusMetricsWriter writer(config, "prometheus", async_metrics);
auto creator = [&server, writer]() -> std::unique_ptr<PrometheusRequestHandler>
{
return std::make_unique<PrometheusRequestHandler>(server, writer);
};
auto handler = std::make_shared<HandlingRuleHTTPHandlerFactory<PrometheusRequestHandler>>(std::move(creator));
handler->attachStrictPath(config.getString("prometheus.endpoint", "/metrics"));
handler->allowGetAndHeadRequest();
factory->addHandler(handler);
return factory;
}
}

View File

@ -168,8 +168,13 @@ HTTPRequestHandlerFactoryPtr createStaticHandlerFactory(IServer & server,
int status = config.getInt(config_prefix + ".handler.status", 200);
std::string response_content = config.getRawString(config_prefix + ".handler.response_content", "Ok.\n");
std::string response_content_type = config.getString(config_prefix + ".handler.content_type", "text/plain; charset=UTF-8");
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(
server, std::move(response_content), std::move(status), std::move(response_content_type));
auto creator = [&server, response_content, status, response_content_type]() -> std::unique_ptr<StaticRequestHandler>
{
return std::make_unique<StaticRequestHandler>(server, response_content, status, response_content_type);
};
auto factory = std::make_shared<HandlingRuleHTTPHandlerFactory<StaticRequestHandler>>(std::move(creator));
factory->addFiltersFromConfig(config, config_prefix);

View File

@ -132,18 +132,6 @@ protected:
bool check_access_for_columns = check_access_for_tables && !access->isGranted(AccessType::SHOW_COLUMNS, database_name, table_name);
auto get_type_name = [this](const IDataType& type) -> std::string
{
// Check if the use_mysql_types_in_show_columns setting is enabled and client is connected via MySQL protocol
if (use_mysql_types && client_info_interface == DB::ClientInfo::Interface::MYSQL)
{
return type.getSQLCompatibleName();
}
else
{
return type.getName();
}
};
size_t position = 0;
for (const auto & column : columns)
{
@ -161,7 +149,7 @@ protected:
if (columns_mask[src_index++])
res_columns[res_index++]->insert(column.name);
if (columns_mask[src_index++])
res_columns[res_index++]->insert(get_type_name(*column.type));
res_columns[res_index++]->insert(use_mysql_types ? (column.type->getSQLCompatibleName()) : (column.type->getName()));
if (columns_mask[src_index++])
res_columns[res_index++]->insert(position);

View File

@ -52,7 +52,7 @@ std::vector<size_t> TableFunctionS3::skipAnalysisForArguments(const QueryTreeNod
return result;
}
/// This is needed to avoid copy-pase. Because s3Cluster arguments only differ in additional argument (first) - cluster name
/// This is needed to avoid copy-paste. Because s3Cluster arguments only differ in additional argument (first) - cluster name
void TableFunctionS3::parseArgumentsImpl(ASTs & args, const ContextPtr & context)
{
if (auto named_collection = tryGetNamedCollectionWithOverrides(args, context))

View File

@ -434,6 +434,7 @@ class ClickHouseCluster:
self.with_net_trics = False
self.with_redis = False
self.with_cassandra = False
self.with_ldap = False
self.with_jdbc_bridge = False
self.with_nginx = False
self.with_hive = False
@ -514,6 +515,13 @@ class ClickHouseCluster:
self.cassandra_ip = None
self.cassandra_id = self.get_instance_docker_id(self.cassandra_host)
# available when with_ldap == True
self.ldap_host = "openldap"
self.ldap_ip = None
self.ldap_container = None
self.ldap_port = 1389
self.ldap_id = self.get_instance_docker_id(self.ldap_host)
# available when with_rabbitmq == True
self.rabbitmq_host = "rabbitmq1"
self.rabbitmq_ip = None
@ -1396,6 +1404,23 @@ class ClickHouseCluster:
]
return self.base_cassandra_cmd
def setup_ldap_cmd(self, instance, env_variables, docker_compose_yml_dir):
self.with_ldap = True
env_variables["LDAP_EXTERNAL_PORT"] = str(self.ldap_port)
self.base_cmd.extend(
["--file", p.join(docker_compose_yml_dir, "docker_compose_ldap.yml")]
)
self.base_ldap_cmd = [
"docker-compose",
"--env-file",
instance.env_file,
"--project-name",
self.project_name,
"--file",
p.join(docker_compose_yml_dir, "docker_compose_ldap.yml"),
]
return self.base_ldap_cmd
def setup_jdbc_bridge_cmd(self, instance, env_variables, docker_compose_yml_dir):
self.with_jdbc_bridge = True
env_variables["JDBC_DRIVER_LOGS"] = self.jdbc_driver_logs_dir
@ -1483,6 +1508,7 @@ class ClickHouseCluster:
with_minio=False,
with_azurite=False,
with_cassandra=False,
with_ldap=False,
with_jdbc_bridge=False,
with_hive=False,
with_coredns=False,
@ -1583,6 +1609,7 @@ class ClickHouseCluster:
with_hive=with_hive,
with_coredns=with_coredns,
with_cassandra=with_cassandra,
with_ldap=with_ldap,
allow_analyzer=allow_analyzer,
server_bin_path=self.server_bin_path,
odbc_bridge_bin_path=self.odbc_bridge_bin_path,
@ -1807,6 +1834,11 @@ class ClickHouseCluster:
)
)
if with_ldap and not self.with_ldap:
cmds.append(
self.setup_ldap_cmd(instance, env_variables, docker_compose_yml_dir)
)
if with_jdbc_bridge and not self.with_jdbc_bridge:
cmds.append(
self.setup_jdbc_bridge_cmd(
@ -2489,6 +2521,32 @@ class ClickHouseCluster:
raise Exception("Can't wait Cassandra to start")
def wait_ldap_to_start(self, timeout=180):
self.ldap_ip = self.get_instance_ip(self.ldap_host)
self.ldap_container = self.get_docker_handle(self.ldap_id)
start = time.time()
while time.time() - start < timeout:
try:
logging.info(
f"Check LDAP Online {self.ldap_id} {self.ldap_ip} {self.ldap_port}"
)
self.exec_in_container(
self.ldap_id,
[
"bash",
"-c",
f"/opt/bitnami/openldap/bin/ldapsearch -x -H ldap://{self.ldap_ip}:{self.ldap_port} -D cn=admin,dc=example,dc=org -w clickhouse -b dc=example,dc=org",
],
user="root",
)
logging.info("LDAP Online")
return
except Exception as ex:
logging.warning("Can't connect to LDAP: %s", str(ex))
time.sleep(1)
raise Exception("Can't wait LDAP to start")
def start(self):
pytest_xdist_logging_to_separate_files.setup()
logging.info("Running tests in {}".format(self.base_path))
@ -2811,6 +2869,11 @@ class ClickHouseCluster:
self.up_called = True
self.wait_cassandra_to_start()
if self.with_ldap and self.base_ldap_cmd:
subprocess_check_call(self.base_ldap_cmd + ["up", "-d"])
self.up_called = True
self.wait_ldap_to_start()
if self.with_jdbc_bridge and self.base_jdbc_bridge_cmd:
os.makedirs(self.jdbc_driver_logs_dir)
os.chmod(self.jdbc_driver_logs_dir, stat.S_IRWXU | stat.S_IRWXO)
@ -3087,6 +3150,7 @@ class ClickHouseInstance:
with_hive,
with_coredns,
with_cassandra,
with_ldap,
allow_analyzer,
server_bin_path,
odbc_bridge_bin_path,
@ -3170,6 +3234,7 @@ class ClickHouseInstance:
self.with_minio = with_minio
self.with_azurite = with_azurite
self.with_cassandra = with_cassandra
self.with_ldap = with_ldap
self.with_jdbc_bridge = with_jdbc_bridge
self.with_hive = with_hive
self.with_coredns = with_coredns

View File

@ -0,0 +1,22 @@
<clickhouse>
<ldap_servers>
<openldap>
<host>openldap</host>
<port>1389</port>
<bind_dn>cn={user_name},ou=users,dc=example,dc=org</bind_dn>
<enable_tls>no</enable_tls>
</openldap>
</ldap_servers>
<user_directories>
<ldap>
<server>openldap</server>
<role_mapping>
<base_dn>dc=example,dc=org</base_dn>
<scope>subtree</scope>
<search_filter>(&amp;(objectClass=groupOfNames)(member={bind_dn}))</search_filter>
<attribute>cn</attribute>
<prefix>clickhouse-</prefix>
</role_mapping>
</ldap>
</user_directories>
</clickhouse>

View File

@ -0,0 +1,95 @@
import logging
import pytest
from helpers.cluster import ClickHouseCluster
from helpers.test_tools import TSV
LDAP_ADMIN_BIND_DN = "cn=admin,dc=example,dc=org"
LDAP_ADMIN_PASSWORD = "clickhouse"
cluster = ClickHouseCluster(__file__)
instance = cluster.add_instance(
"instance", main_configs=["configs/ldap_with_role_mapping.xml"], with_ldap=True
)
@pytest.fixture(scope="module", autouse=True)
def ldap_cluster():
try:
cluster.start()
yield cluster
finally:
cluster.shutdown()
def add_ldap_group(ldap_cluster, group_cn, member_cn):
code, (stdout, stderr) = ldap_cluster.ldap_container.exec_run(
[
"sh",
"-c",
"""echo "dn: cn={group_cn},dc=example,dc=org
objectClass: top
objectClass: groupOfNames
member: cn={member_cn},ou=users,dc=example,dc=org" | \
ldapadd -H ldap://{host}:{port} -D "{admin_bind_dn}" -x -w {admin_password}
""".format(
host=ldap_cluster.ldap_host,
port=ldap_cluster.ldap_port,
admin_bind_dn=LDAP_ADMIN_BIND_DN,
admin_password=LDAP_ADMIN_PASSWORD,
group_cn=group_cn,
member_cn=member_cn,
),
],
demux=True,
)
logging.debug(
f"test_ldap_external_user_directory code:{code} stdout:{stdout}, stderr:{stderr}"
)
assert code == 0
def test_authentication_pass():
assert instance.query(
"select currentUser()", user="janedoe", password="qwerty"
) == TSV([["janedoe"]])
def test_authentication_fail():
# User doesn't exist.
assert "doesnotexist: Authentication failed" in instance.query_and_get_error(
"SELECT currentUser()", user="doesnotexist"
)
# Wrong password.
assert "janedoe: Authentication failed" in instance.query_and_get_error(
"SELECT currentUser()", user="janedoe", password="123"
)
def test_role_mapping(ldap_cluster):
instance.query("CREATE ROLE role_1")
instance.query("CREATE ROLE role_2")
add_ldap_group(ldap_cluster, group_cn="clickhouse-role_1", member_cn="johndoe")
add_ldap_group(ldap_cluster, group_cn="clickhouse-role_2", member_cn="johndoe")
assert instance.query(
"select currentUser()", user="johndoe", password="qwertz"
) == TSV([["johndoe"]])
assert instance.query(
"select role_name from system.current_roles ORDER BY role_name",
user="johndoe",
password="qwertz",
) == TSV([["role_1"], ["role_2"]])
instance.query("CREATE ROLE role_3")
add_ldap_group(ldap_cluster, group_cn="clickhouse-role_3", member_cn="johndoe")
# Check that non-existing role in ClickHouse is ignored during role update
# See https://github.com/ClickHouse/ClickHouse/issues/54318
add_ldap_group(ldap_cluster, group_cn="clickhouse-role_4", member_cn="johndoe")
assert instance.query(
"select role_name from system.current_roles ORDER BY role_name",
user="johndoe",
password="qwertz",
) == TSV([["role_1"], ["role_2"], ["role_3"]])

View File

@ -16,10 +16,13 @@ ${CLICKHOUSE_CLIENT} -q "CREATE ROLE r02247"
${CLICKHOUSE_CLIENT} -q "CREATE USER u02247"
${CLICKHOUSE_CLIENT} -q "GRANT ALL ON *.* TO r02247"
${CLICKHOUSE_CLIENT} -q "GRANT r02247 to u02247"
${CLICKHOUSE_CLIENT} -q "CREATE QUOTA q02247 FOR INTERVAL 100 YEAR MAX WRITTEN BYTES = 25 TO r02247"
${CLICKHOUSE_CLIENT} -q "CREATE QUOTA q02247 FOR INTERVAL 100 YEAR MAX WRITTEN BYTES = 30 TO r02247"
# The value 'qwqw' means about 13 bytes are to be written, so the current quota (30 bytes) gives the ability to write 'qwqw' 2 times.
${CLICKHOUSE_CLIENT} --user u02247 --async_insert 1 -q "INSERT INTO written_bytes_02247 VALUES ('qwqw')"
#${CLICKHOUSE_CLIENT} --user u02247 -q "SHOW CURRENT QUOTA"
${CLICKHOUSE_CLIENT} --user u02247 --async_insert 0 -q "INSERT INTO written_bytes_02247 VALUES ('qwqw')"
#${CLICKHOUSE_CLIENT} --user u02247 -q "SHOW CURRENT QUOTA"
${CLICKHOUSE_CLIENT} --user u02247 --async_insert 1 -q "INSERT INTO written_bytes_02247 VALUES ('qwqw')" 2>&1 | grep -m1 -o QUOTA_EXCEEDED
${CLICKHOUSE_CLIENT} --user u02247 --async_insert 0 -q "INSERT INTO written_bytes_02247 VALUES ('qwqw')" 2>&1 | grep -m1 -o QUOTA_EXCEEDED
@ -30,7 +33,10 @@ ${CLICKHOUSE_CLIENT} -q "DROP QUOTA q02247"
${CLICKHOUSE_CLIENT} -q "CREATE QUOTA q02247 FOR INTERVAL 100 YEAR MAX WRITTEN BYTES = 1000 TO r02247"
${CLICKHOUSE_CLIENT} -q "TRUNCATE TABLE written_bytes_02247"
# Numbers from 0 to 50 means about 540 bytes are to be written, so the current quota (1000 bytes) is enough to do so.
${CLICKHOUSE_CLIENT} --user u02247 -q "INSERT INTO written_bytes_02247 SELECT toString(number) FROM numbers(50)"
# Numbers from 0 to 100 means about 1090 bytes are to be written, so the current quota (1000 bytes total - 540 bytes already used) is NOT enough to do so.
${CLICKHOUSE_CLIENT} --user u02247 -q "INSERT INTO written_bytes_02247 SELECT toString(number) FROM numbers(100)" 2>&1 | grep -m1 -o QUOTA_EXCEEDED
${CLICKHOUSE_CLIENT} -q "SELECT written_bytes > 100 FROM system.quotas_usage WHERE quota_name = 'q02247'"

View File

@ -1,366 +1,148 @@
Drop tables if they exist
Create tab table
Create pseudo-random database name
Create tab duplicate table
Run MySQL test
field type null key default extra
aggregate_function AggregateFunction(sum, Int32) NO NULL
array_value Array(Int32) NO NULL
boolean_value UInt8 NO NULL
date32_value Date32 NO NULL
date_value Date NO NULL
datetime64_value DateTime64(3) NO NULL
datetime_value DateTime NO NULL
decimal_value Decimal(10, 2) NO NULL
enum_value Enum8('apple' = 1, 'banana' = 2, 'orange' = 3) NO NULL
fixed_string_value FixedString(10) NO NULL
float32 Float32 NO NULL
float64 Float64 NO NULL
int128 Int128 NO NULL
int16 Int16 NO NULL
int256 Int256 NO NULL
int32 Int32 NO NULL
int64 Int64 NO NULL
int8 Int8 NO NULL
ipv4_value IPv4 NO NULL
ipv6_value IPv6 NO NULL
json_value Object('json') NO NULL
low_cardinality LowCardinality(String) NO NULL
low_cardinality_date LowCardinality(DateTime) NO NULL
map_value Map(String, Int32) NO NULL
nested.nested_int Array(Int32) NO NULL
nested.nested_string Array(String) NO NULL
nint32 Nullable(Int32) YES NULL
nullable_value Nullable(Int32) YES NULL
string_value String NO NULL
tuple_value Tuple(Int32, String) NO NULL
uint128 UInt128 NO NULL
uint16 UInt16 NO NULL
uint256 UInt256 NO NULL
uint32 UInt32 NO NULL
uint64 UInt64 NO PRI SOR NULL
uint8 UInt8 NO NULL
uuid_value UUID NO NULL
field type null key default extra
aggregate_function TEXT NO NULL
array_value TEXT NO NULL
boolean_value TINYINT UNSIGNED NO NULL
date32_value DATE NO NULL
date_value DATE NO NULL
datetime64_value DATETIME NO NULL
datetime_value DATETIME NO NULL
decimal_value DECIMAL(10, 2) NO NULL
enum_value ENUM('apple', 'banana', 'orange') NO NULL
fixed_string_value TEXT NO NULL
float32 FLOAT NO NULL
float64 DOUBLE NO NULL
int128 TEXT NO NULL
int16 SMALLINT NO NULL
int256 TEXT NO NULL
int32 INTEGER NO NULL
int64 BIGINT NO NULL
int8 TINYINT NO NULL
ipv4_value TEXT NO NULL
ipv6_value TEXT NO NULL
json_value JSON NO NULL
low_cardinality BLOB NO NULL
low_cardinality_date DATETIME NO NULL
map_value JSON NO NULL
nested.nested_int TEXT NO NULL
nested.nested_string TEXT NO NULL
nint32 INTEGER NO NULL
nullable_value INTEGER NO NULL
string_value BLOB NO NULL
tuple_value JSON NO NULL
uint128 TEXT NO NULL
uint16 SMALLINT UNSIGNED NO NULL
uint256 TEXT NO NULL
uint32 INTEGER UNSIGNED NO NULL
uint64 BIGINT UNSIGNED NO PRI SOR NULL
uint8 TINYINT UNSIGNED NO NULL
uuid_value CHAR NO NULL
field type null key default extra
aggregate_function TEXT NO NULL
array_value TEXT NO NULL
boolean_value TINYINT UNSIGNED NO NULL
date32_value DATE NO NULL
date_value DATE NO NULL
datetime64_value DATETIME NO NULL
datetime_value DATETIME NO NULL
decimal_value DECIMAL(10, 2) NO NULL
enum_value ENUM('apple', 'banana', 'orange') NO NULL
fixed_string_value TEXT NO NULL
float32 FLOAT NO NULL
float64 DOUBLE NO NULL
int128 TEXT NO NULL
int16 SMALLINT NO NULL
int256 TEXT NO NULL
int32 INTEGER NO NULL
int64 BIGINT NO NULL
int8 TINYINT NO NULL
ipv4_value TEXT NO NULL
ipv6_value TEXT NO NULL
json_value JSON NO NULL
low_cardinality BLOB NO NULL
low_cardinality_date DATETIME NO NULL
map_value JSON NO NULL
nested.nested_int TEXT NO NULL
nested.nested_string TEXT NO NULL
nint32 INTEGER NO NULL
nullable_value INTEGER NO NULL
string_value BLOB NO NULL
tuple_value JSON NO NULL
uint128 TEXT NO NULL
uint16 SMALLINT UNSIGNED NO NULL
uint256 TEXT NO NULL
uint32 INTEGER UNSIGNED NO NULL
uint64 BIGINT UNSIGNED NO PRI SOR NULL
uint8 TINYINT UNSIGNED NO NULL
uuid_value CHAR NO NULL
field type null key default extra collation comment privileges
aggregate_function TEXT NO NULL NULL
array_value TEXT NO NULL NULL
boolean_value TINYINT UNSIGNED NO NULL NULL
date32_value DATE NO NULL NULL
date_value DATE NO NULL NULL
datetime64_value DATETIME NO NULL NULL
datetime_value DATETIME NO NULL NULL
decimal_value DECIMAL(10, 2) NO NULL NULL
enum_value ENUM('apple', 'banana', 'orange') NO NULL NULL
fixed_string_value TEXT NO NULL NULL
float32 FLOAT NO NULL NULL
float64 DOUBLE NO NULL NULL
int128 TEXT NO NULL NULL
int16 SMALLINT NO NULL NULL
int256 TEXT NO NULL NULL
int32 INTEGER NO NULL NULL
int64 BIGINT NO NULL NULL
int8 TINYINT NO NULL NULL
ipv4_value TEXT NO NULL NULL
ipv6_value TEXT NO NULL NULL
json_value JSON NO NULL NULL
low_cardinality BLOB NO NULL NULL
low_cardinality_date DATETIME NO NULL NULL
map_value JSON NO NULL NULL
nested.nested_int TEXT NO NULL NULL
nested.nested_string TEXT NO NULL NULL
nint32 INTEGER NO NULL NULL
nullable_value INTEGER NO NULL NULL
string_value BLOB NO NULL NULL
tuple_value JSON NO NULL NULL
uint128 TEXT NO NULL NULL
uint16 SMALLINT UNSIGNED NO NULL NULL
uint256 TEXT NO NULL NULL
uint32 INTEGER UNSIGNED NO NULL NULL
uint64 BIGINT UNSIGNED NO PRI SOR NULL NULL
uint8 TINYINT UNSIGNED NO NULL NULL
uuid_value CHAR NO NULL NULL
field type null key default extra
int128 TEXT NO NULL
int16 SMALLINT NO NULL
int256 TEXT NO NULL
int32 INTEGER NO NULL
int64 BIGINT NO NULL
int8 TINYINT NO NULL
nested.nested_int TEXT NO NULL
nint32 INTEGER NO NULL
uint128 TEXT NO NULL
uint16 SMALLINT UNSIGNED NO NULL
uint256 TEXT NO NULL
uint32 INTEGER UNSIGNED NO NULL
uint64 BIGINT UNSIGNED NO PRI SOR NULL
uint8 TINYINT UNSIGNED NO NULL
field type null key default extra
aggregate_function TEXT NO NULL
array_value TEXT NO NULL
boolean_value TINYINT UNSIGNED NO NULL
date32_value DATE NO NULL
date_value DATE NO NULL
datetime64_value DATETIME NO NULL
datetime_value DATETIME NO NULL
decimal_value DECIMAL(10, 2) NO NULL
enum_value ENUM('apple', 'banana', 'orange') NO NULL
fixed_string_value TEXT NO NULL
float32 FLOAT NO NULL
float64 DOUBLE NO NULL
ipv4_value TEXT NO NULL
ipv6_value TEXT NO NULL
json_value JSON NO NULL
low_cardinality BLOB NO NULL
low_cardinality_date DATETIME NO NULL
map_value JSON NO NULL
nested.nested_string TEXT NO NULL
nullable_value INTEGER NO NULL
string_value BLOB NO NULL
tuple_value JSON NO NULL
uuid_value CHAR NO NULL
field type null key default extra
int128 TEXT NO NULL
int16 SMALLINT NO NULL
int256 TEXT NO NULL
int32 INTEGER NO NULL
int64 BIGINT NO NULL
int8 TINYINT NO NULL
nested.nested_int TEXT NO NULL
nint32 INTEGER NO NULL
uint128 TEXT NO NULL
uint16 SMALLINT UNSIGNED NO NULL
uint256 TEXT NO NULL
uint32 INTEGER UNSIGNED NO NULL
uint64 BIGINT UNSIGNED NO PRI SOR NULL
uint8 TINYINT UNSIGNED NO NULL
field type null key default extra
aggregate_function TEXT NO NULL
array_value TEXT NO NULL
boolean_value TINYINT UNSIGNED NO NULL
date32_value DATE NO NULL
date_value DATE NO NULL
datetime64_value DATETIME NO NULL
datetime_value DATETIME NO NULL
decimal_value DECIMAL(10, 2) NO NULL
enum_value ENUM('apple', 'banana', 'orange') NO NULL
fixed_string_value TEXT NO NULL
float32 FLOAT NO NULL
float64 DOUBLE NO NULL
ipv4_value TEXT NO NULL
ipv6_value TEXT NO NULL
json_value JSON NO NULL
low_cardinality BLOB NO NULL
low_cardinality_date DATETIME NO NULL
map_value JSON NO NULL
nested.nested_string TEXT NO NULL
nullable_value INTEGER NO NULL
string_value BLOB NO NULL
tuple_value JSON NO NULL
uuid_value CHAR NO NULL
field type null key default extra
int128 TEXT NO NULL
int16 SMALLINT NO NULL
int256 TEXT NO NULL
int32 INTEGER NO NULL
int64 BIGINT NO NULL
int8 TINYINT NO NULL
nested.nested_int TEXT NO NULL
nint32 INTEGER NO NULL
uint128 TEXT NO NULL
uint16 SMALLINT UNSIGNED NO NULL
uint256 TEXT NO NULL
uint32 INTEGER UNSIGNED NO NULL
uint64 BIGINT UNSIGNED NO PRI SOR NULL
uint8 TINYINT UNSIGNED NO NULL
field type null key default extra
aggregate_function TEXT NO NULL
field type null key default extra
aggregate_function TEXT NO NULL
array_value TEXT NO NULL
boolean_value TINYINT UNSIGNED NO NULL
date32_value DATE NO NULL
date_value DATE NO NULL
datetime64_value DATETIME NO NULL
datetime_value DATETIME NO NULL
decimal_value DECIMAL(10, 2) NO NULL
enum_value ENUM('apple', 'banana', 'orange') NO NULL
fixed_string_value TEXT NO NULL
float32 FLOAT NO NULL
float64 DOUBLE NO NULL
int128 TEXT NO NULL
int16 SMALLINT NO NULL
int256 TEXT NO NULL
int32 INTEGER NO NULL
int64 BIGINT NO NULL
int8 TINYINT NO NULL
ipv4_value TEXT NO NULL
ipv6_value TEXT NO NULL
json_value JSON NO NULL
low_cardinality BLOB NO NULL
low_cardinality_date DATETIME NO NULL
map_value JSON NO NULL
nested.nested_int TEXT NO NULL
nested.nested_string TEXT NO NULL
nint32 INTEGER NO NULL
nullable_value INTEGER NO NULL
string_value BLOB NO NULL
tuple_value JSON NO NULL
uint128 TEXT NO NULL
uint16 SMALLINT UNSIGNED NO NULL
uint256 TEXT NO NULL
uint32 INTEGER UNSIGNED NO NULL
uint64 BIGINT UNSIGNED NO PRI SOR NULL
uint8 TINYINT UNSIGNED NO NULL
uuid_value CHAR NO NULL
field type null key default extra
aggregate_function TEXT NO NULL
array_value TEXT NO NULL
boolean_value TINYINT UNSIGNED NO NULL
date32_value DATE NO NULL
date_value DATE NO NULL
datetime64_value DATETIME NO NULL
datetime_value DATETIME NO NULL
decimal_value DECIMAL(10, 2) NO NULL
enum_value ENUM('apple', 'banana', 'orange') NO NULL
fixed_string_value TEXT NO NULL
float32 FLOAT NO NULL
float64 DOUBLE NO NULL
int128 TEXT NO NULL
int16 SMALLINT NO NULL
int256 TEXT NO NULL
int32 INTEGER NO NULL
int64 BIGINT NO NULL
int8 TINYINT NO NULL
ipv4_value TEXT NO NULL
ipv6_value TEXT NO NULL
json_value JSON NO NULL
low_cardinality BLOB NO NULL
low_cardinality_date DATETIME NO NULL
map_value JSON NO NULL
nested.nested_int TEXT NO NULL
nested.nested_string TEXT NO NULL
nint32 INTEGER NO NULL
nullable_value INTEGER NO NULL
string_value BLOB NO NULL
tuple_value JSON NO NULL
uint128 TEXT NO NULL
uint16 SMALLINT UNSIGNED NO NULL
uint256 TEXT NO NULL
uint32 INTEGER UNSIGNED NO NULL
uint64 BIGINT UNSIGNED NO PRI SOR NULL
uint8 TINYINT UNSIGNED NO NULL
uuid_value CHAR NO NULL
field type null key default extra
aggregate_function TEXT NO NULL
array_value TEXT NO NULL
boolean_value TINYINT UNSIGNED NO NULL
date32_value DATE NO NULL
date_value DATE NO NULL
datetime64_value DATETIME NO NULL
datetime_value DATETIME NO NULL
decimal_value DECIMAL(10, 2) NO NULL
enum_value ENUM('apple', 'banana', 'orange') NO NULL
fixed_string_value TEXT NO NULL
float32 FLOAT NO NULL
float64 DOUBLE NO NULL
int128 TEXT NO NULL
int16 SMALLINT NO NULL
int256 TEXT NO NULL
int32 INTEGER NO NULL
int64 BIGINT NO NULL
int8 TINYINT NO NULL
ipv4_value TEXT NO NULL
ipv6_value TEXT NO NULL
json_value JSON NO NULL
low_cardinality BLOB NO NULL
low_cardinality_date DATETIME NO NULL
map_value JSON NO NULL
nested.nested_int TEXT NO NULL
nested.nested_string TEXT NO NULL
nint32 INTEGER NO NULL
nullable_value INTEGER NO NULL
string_value BLOB NO NULL
tuple_value JSON NO NULL
uint128 TEXT NO NULL
uint16 SMALLINT UNSIGNED NO NULL
uint256 TEXT NO NULL
uint32 INTEGER UNSIGNED NO NULL
uint64 BIGINT UNSIGNED NO PRI SOR NULL
uint8 TINYINT UNSIGNED NO NULL
uuid_value CHAR NO NULL
aggregate_function AggregateFunction(sum, Int32) NO \N
array_value Array(Int32) NO \N
boolean_value UInt8 NO \N
date32_value Date32 NO \N
date_value Date NO \N
datetime64_value DateTime64(3) NO \N
datetime_value DateTime NO \N
decimal_value Decimal(10, 2) NO \N
enum_value Enum8(\'apple\' = 1, \'banana\' = 2, \'orange\' = 3) NO \N
fixed_string_value FixedString(10) NO \N
float32 Float32 NO \N
float64 Float64 NO \N
int128 Int128 NO \N
int16 Int16 NO \N
int256 Int256 NO \N
int32 Int32 NO \N
int64 Int64 NO \N
int8 Int8 NO \N
ipv4_value IPv4 NO \N
ipv6_value IPv6 NO \N
json_value Object(\'json\') NO \N
low_cardinality LowCardinality(String) NO \N
low_cardinality_date LowCardinality(DateTime) NO \N
map_value Map(String, Int32) NO \N
nested.nested_int Array(Int32) NO \N
nested.nested_string Array(String) NO \N
nint32 Nullable(Int32) YES \N
nullable_value Nullable(Int32) YES \N
string_value String NO \N
tuple_value Tuple(Int32, String) NO \N
uint128 UInt128 NO \N
uint16 UInt16 NO \N
uint256 UInt256 NO \N
uint32 UInt32 NO \N
uint64 UInt64 NO PRI SOR \N
uint8 UInt8 NO \N
uuid_value UUID NO \N
aggregate_function TEXT NO \N
array_value TEXT NO \N
boolean_value TINYINT UNSIGNED NO \N
date32_value DATE NO \N
date_value DATE NO \N
datetime64_value DATETIME NO \N
datetime_value DATETIME NO \N
decimal_value DECIMAL(10, 2) NO \N
enum_value ENUM(\'apple\', \'banana\', \'orange\') NO \N
fixed_string_value TEXT NO \N
float32 FLOAT NO \N
float64 DOUBLE NO \N
int128 TEXT NO \N
int16 SMALLINT NO \N
int256 TEXT NO \N
int32 INTEGER NO \N
int64 BIGINT NO \N
int8 TINYINT NO \N
ipv4_value TEXT NO \N
ipv6_value TEXT NO \N
json_value JSON NO \N
low_cardinality BLOB NO \N
low_cardinality_date DATETIME NO \N
map_value JSON NO \N
nested.nested_int TEXT NO \N
nested.nested_string TEXT NO \N
nint32 INTEGER NO \N
nullable_value INTEGER NO \N
string_value BLOB NO \N
tuple_value JSON NO \N
uint128 TEXT NO \N
uint16 SMALLINT UNSIGNED NO \N
uint256 TEXT NO \N
uint32 INTEGER UNSIGNED NO \N
uint64 BIGINT UNSIGNED NO PRI SOR \N
uint8 TINYINT UNSIGNED NO \N
uuid_value CHAR NO \N
uint8 UInt8
uint16 UInt16
uint32 UInt32
uint64 UInt64
uint128 UInt128
uint256 UInt256
int8 Int8
int16 Int16
int32 Int32
int64 Int64
int128 Int128
int256 Int256
nint32 Nullable(Int32)
float32 Float32
float64 Float64
decimal_value Decimal(10, 2)
boolean_value UInt8
string_value String
fixed_string_value FixedString(10)
date_value Date
date32_value Date32
datetime_value DateTime
datetime64_value DateTime64(3)
json_value Object(\'json\')
uuid_value UUID
enum_value Enum8(\'apple\' = 1, \'banana\' = 2, \'orange\' = 3)
low_cardinality LowCardinality(String)
low_cardinality_date LowCardinality(DateTime)
aggregate_function AggregateFunction(sum, Int32)
array_value Array(Int32)
map_value Map(String, Int32)
tuple_value Tuple(Int32, String)
nullable_value Nullable(Int32)
ipv4_value IPv4
ipv6_value IPv6
nested.nested_int Array(Int32)
nested.nested_string Array(String)
uint8 TINYINT UNSIGNED
uint16 SMALLINT UNSIGNED
uint32 INTEGER UNSIGNED
uint64 BIGINT UNSIGNED
uint128 TEXT
uint256 TEXT
int8 TINYINT
int16 SMALLINT
int32 INTEGER
int64 BIGINT
int128 TEXT
int256 TEXT
nint32 INTEGER
float32 FLOAT
float64 DOUBLE
decimal_value DECIMAL(10, 2)
boolean_value TINYINT UNSIGNED
string_value BLOB
fixed_string_value TEXT
date_value DATE
date32_value DATE
datetime_value DATETIME
datetime64_value DATETIME
json_value JSON
uuid_value CHAR
enum_value ENUM(\'apple\', \'banana\', \'orange\')
low_cardinality BLOB
low_cardinality_date DATETIME
aggregate_function TEXT
array_value TEXT
map_value JSON
tuple_value JSON
nullable_value INTEGER
ipv4_value TEXT
ipv6_value TEXT
nested.nested_int TEXT
nested.nested_string TEXT

View File

@ -1,153 +0,0 @@
#!/usr/bin/env bash
# Tags: no-fasttest, no-parallel
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=../shell_config.sh
. "$CURDIR"/../shell_config.sh
# This script tests the MySQL compatibility of the SHOW COLUMNS command in ClickHouse
USER="default"
PASSWORD=""
HOST="127.0.0.1"
PORT=9004
# First run the clickhouse test to create the ClickHouse Tables
echo "Drop tables if they exist"
${CLICKHOUSE_CLIENT} --query "DROP TABLE IF EXISTS tab"
${CLICKHOUSE_CLIENT} --query "DROP DATABASE IF EXISTS database_123456789abcdef"
${CLICKHOUSE_CLIENT} --query "DROP TABLE IF EXISTS database_123456789abcdef.tab"
echo "Create tab table "
${CLICKHOUSE_CLIENT} -n -q "
SET allow_suspicious_low_cardinality_types=1;
SET allow_experimental_object_type=1;
CREATE TABLE tab
(
uint8 UInt8,
uint16 UInt16,
uint32 UInt32,
uint64 UInt64,
uint128 UInt128,
uint256 UInt256,
int8 Int8,
int16 Int16,
int32 Int32,
int64 Int64,
int128 Int128,
int256 Int256,
nint32 Nullable(Int32),
float32 Float32,
float64 Float64,
decimal_value Decimal(10, 2),
boolean_value UInt8,
string_value String,
fixed_string_value FixedString(10),
date_value Date,
date32_value Date32,
datetime_value DateTime,
datetime64_value DateTime64(3),
json_value JSON,
uuid_value UUID,
enum_value Enum8('apple' = 1, 'banana' = 2, 'orange' = 3),
low_cardinality LowCardinality(String),
low_cardinality_date LowCardinality(DateTime),
aggregate_function AggregateFunction(sum, Int32),
array_value Array(Int32),
map_value Map(String, Int32),
tuple_value Tuple(Int32, String),
nullable_value Nullable(Int32),
ipv4_value IPv4,
ipv6_value IPv6,
nested Nested
(
nested_int Int32,
nested_string String
)
) ENGINE = MergeTree
ORDER BY uint64;
"
echo "Create pseudo-random database name"
${CLICKHOUSE_CLIENT} --query "CREATE DATABASE database_123456789abcdef;"
echo "Create tab duplicate table"
${CLICKHOUSE_CLIENT} -n -q "
SET allow_suspicious_low_cardinality_types=1;
SET allow_experimental_object_type =1;
CREATE TABLE database_123456789abcdef.tab
(
uint8 UInt8,
uint16 UInt16,
uint32 UInt32,
uint64 UInt64,
uint128 UInt128,
uint256 UInt256,
int8 Int8,
int16 Int16,
int32 Int32,
int64 Int64,
int128 Int128,
int256 Int256,
nint32 Nullable(Int32),
float32 Float32,
float64 Float64,
decimal_value Decimal(10, 2),
boolean_value UInt8,
string_value String,
fixed_string_value FixedString(10),
date_value Date,
date32_value Date32,
datetime_value DateTime,
datetime64_value DateTime64(3),
json_value JSON,
uuid_value UUID,
enum_value Enum8('apple' = 1, 'banana' = 2, 'orange' = 3),
low_cardinality LowCardinality(String),
low_cardinality_date LowCardinality(DateTime),
aggregate_function AggregateFunction(sum, Int32),
array_value Array(Int32),
map_value Map(String, Int32),
tuple_value Tuple(Int32, String),
nullable_value Nullable(Int32),
ipv4_value IPv4,
ipv6_value IPv6,
nested Nested
(
nested_int Int32,
nested_string String
)
) ENGINE = MergeTree
ORDER BY uint64;
"
# Write sql to temp file
TEMP_FILE=$(mktemp)
cat <<EOT > $TEMP_FILE
SHOW COLUMNS FROM tab;
SET use_mysql_types_in_show_columns=1;
SHOW COLUMNS FROM tab;
SHOW EXTENDED COLUMNS FROM tab;
SHOW FULL COLUMNS FROM tab;
SHOW COLUMNS FROM tab LIKE '%int%';
SHOW COLUMNS FROM tab NOT LIKE '%int%';
SHOW COLUMNS FROM tab ILIKE '%INT%';
SHOW COLUMNS FROM tab NOT ILIKE '%INT%';
SHOW COLUMNS FROM tab WHERE field LIKE '%int%';
SHOW COLUMNS FROM tab LIMIT 1;
SHOW COLUMNS FROM tab;
SHOW COLUMNS FROM tab FROM database_123456789abcdef;
SHOW COLUMNS FROM database_123456789abcdef.tab;
DROP DATABASE database_123456789abcdef;
DROP TABLE tab;
EOT
# Now run the MySQL test script on the ClickHouse DB
echo "Run MySQL test"
MYSQL_PWD=$PASSWORD ${MYSQL_CLIENT} --user="$USER" --host="$HOST" --port="$PORT" < $TEMP_FILE
# Clean up the temp file
rm $TEMP_FILE

View File

@ -0,0 +1,63 @@
-- Tags: no-fasttest, no-parallel
-- no-fasttest: json type needs rapidjson library
-- no-parallel: can't provide currentDatabase() to SHOW COLUMNS
-- Tests setting 'use_mysql_types_in_show_columns' in SHOW COLUMNS and SELECTs on system.columns
DROP TABLE IF EXISTS tab;
SET allow_suspicious_low_cardinality_types=1;
SET allow_experimental_object_type=1;
CREATE TABLE tab
(
uint8 UInt8,
uint16 UInt16,
uint32 UInt32,
uint64 UInt64,
uint128 UInt128,
uint256 UInt256,
int8 Int8,
int16 Int16,
int32 Int32,
int64 Int64,
int128 Int128,
int256 Int256,
nint32 Nullable(Int32),
float32 Float32,
float64 Float64,
decimal_value Decimal(10, 2),
boolean_value UInt8,
string_value String,
fixed_string_value FixedString(10),
date_value Date,
date32_value Date32,
datetime_value DateTime,
datetime64_value DateTime64(3),
json_value JSON,
uuid_value UUID,
enum_value Enum8('apple' = 1, 'banana' = 2, 'orange' = 3),
low_cardinality LowCardinality(String),
low_cardinality_date LowCardinality(DateTime),
aggregate_function AggregateFunction(sum, Int32),
array_value Array(Int32),
map_value Map(String, Int32),
tuple_value Tuple(Int32, String),
nullable_value Nullable(Int32),
ipv4_value IPv4,
ipv6_value IPv6,
nested Nested
(
nested_int Int32,
nested_string String
)
) ENGINE = MergeTree
ORDER BY uint64;
SHOW COLUMNS FROM tab SETTINGS use_mysql_types_in_show_columns = 0;
SHOW COLUMNS FROM tab SETTINGS use_mysql_types_in_show_columns = 1;
SELECT name, type FROM system.columns WHERE database = currentDatabase() AND table = 'tab' SETTINGS use_mysql_types_in_show_columns = 0;
SELECT name, type FROM system.columns WHERE database = currentDatabase() AND table = 'tab' SETTINGS use_mysql_types_in_show_columns = 1;
DROP TABLE tab;

View File

@ -0,0 +1,6 @@
1 value_1
2 value_2
Not enough privileges
Not enough privileges
5 value_5
6 value_6

View File

@ -0,0 +1,58 @@
#!/usr/bin/env bash
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
. "$CURDIR"/../shell_config.sh
restricted_user=${CLICKHOUSE_DATABASE}_restricted_user_$RANDOM$RANDOM
# The 'restricted_user' will not have access to the dictionary 'dict',
# so they shouldn't be able to insert to a table with DEFAULT dictGet(dict, ...)
$CLICKHOUSE_CLIENT --multiquery <<EOF
DROP USER IF EXISTS ${restricted_user};
DROP TABLE IF EXISTS table_with_default;
DROP DICTIONARY IF EXISTS dict;
DROP TABLE IF EXISTS table_for_dict;
CREATE USER ${restricted_user};
CREATE TABLE table_for_dict (key UInt64, value String) ENGINE = Memory();
INSERT INTO table_for_dict VALUES (1, 'value_1'), (2, 'value_2'), (3, 'value_3'), (4, 'value_4'), (5, 'value_5'), (6, 'value_6');
CREATE DICTIONARY dict
(
key UInt64,
value String
)
PRIMARY KEY key
SOURCE(CLICKHOUSE(HOST 'localhost' PORT tcpPort() USER 'default' TABLE 'table_for_dict' DB currentDatabase()))
LIFETIME(1)
LAYOUT(FLAT());
CREATE TABLE table_with_default(key UInt64, value String DEFAULT dictGetString(dict, 'value', key)) ENGINE = Memory();
GRANT INSERT, SELECT ON table_with_default TO ${restricted_user};
EOF
$CLICKHOUSE_CLIENT --query "INSERT INTO table_with_default (key) VALUES (1)"
$CLICKHOUSE_CLIENT --async_insert=1 --query "INSERT INTO table_with_default (key) VALUES (2)"
$CLICKHOUSE_CLIENT --query "SELECT * FROM table_with_default WHERE key IN [1, 2] ORDER BY key"
$CLICKHOUSE_CLIENT --user "${restricted_user}" --query "INSERT INTO table_with_default (key) VALUES (3)" 2>&1 | grep -m 1 -oF "Not enough privileges"
$CLICKHOUSE_CLIENT --user "${restricted_user}" --async_insert=1 --query "INSERT INTO table_with_default (key) VALUES (4)" 2>&1 | grep -m 1 -oF 'Not enough privileges'
$CLICKHOUSE_CLIENT --query "SELECT * FROM table_with_default WHERE key IN [3, 4] ORDER BY key"
# We give the 'restricted_user' access to the dictionary 'dict',
# so now they should be able to insert to a table with DEFAULT dictGet(dict, ...)
$CLICKHOUSE_CLIENT --query "GRANT dictGet ON dict TO ${restricted_user}"
$CLICKHOUSE_CLIENT --user "${restricted_user}" --query "INSERT INTO table_with_default (key) VALUES (5)"
$CLICKHOUSE_CLIENT --user "${restricted_user}" --async_insert=1 --query "INSERT INTO table_with_default (key) VALUES (6)"
$CLICKHOUSE_CLIENT --query "SELECT * FROM table_with_default WHERE key IN [5, 6] ORDER BY key"
$CLICKHOUSE_CLIENT --multiquery <<EOF
DROP USER ${restricted_user};
DROP TABLE table_with_default;
DROP DICTIONARY dict;
DROP TABLE table_for_dict;
EOF