mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-21 23:21:59 +00:00
Merge branch 'master' into complete_zk_api
This commit is contained in:
commit
0c43246216
6
.gitmodules
vendored
6
.gitmodules
vendored
@ -142,9 +142,6 @@
|
||||
[submodule "contrib/replxx"]
|
||||
path = contrib/replxx
|
||||
url = https://github.com/ClickHouse-Extras/replxx.git
|
||||
[submodule "contrib/ryu"]
|
||||
path = contrib/ryu
|
||||
url = https://github.com/ClickHouse-Extras/ryu.git
|
||||
[submodule "contrib/avro"]
|
||||
path = contrib/avro
|
||||
url = https://github.com/ClickHouse-Extras/avro.git
|
||||
@ -209,3 +206,6 @@
|
||||
path = contrib/abseil-cpp
|
||||
url = https://github.com/ClickHouse-Extras/abseil-cpp.git
|
||||
branch = lts_2020_02_25
|
||||
[submodule "contrib/dragonbox"]
|
||||
path = contrib/dragonbox
|
||||
url = https://github.com/ClickHouse-Extras/dragonbox.git
|
||||
|
3
contrib/CMakeLists.txt
vendored
3
contrib/CMakeLists.txt
vendored
@ -35,7 +35,6 @@ add_subdirectory (libmetrohash)
|
||||
add_subdirectory (lz4-cmake)
|
||||
add_subdirectory (murmurhash)
|
||||
add_subdirectory (replxx-cmake)
|
||||
add_subdirectory (ryu-cmake)
|
||||
add_subdirectory (unixodbc-cmake)
|
||||
add_subdirectory (xz)
|
||||
|
||||
@ -322,3 +321,5 @@ endif()
|
||||
if (USE_INTERNAL_ROCKSDB_LIBRARY)
|
||||
add_subdirectory(rocksdb-cmake)
|
||||
endif()
|
||||
|
||||
add_subdirectory(dragonbox)
|
||||
|
1
contrib/dragonbox
vendored
Submodule
1
contrib/dragonbox
vendored
Submodule
@ -0,0 +1 @@
|
||||
Subproject commit b2751c65c0592c0239aec3becd53d0ea2fde9329
|
1
contrib/ryu
vendored
1
contrib/ryu
vendored
@ -1 +0,0 @@
|
||||
Subproject commit 5b4a853534b47438b4d97935370f6b2397137c2b
|
@ -1,10 +0,0 @@
|
||||
SET(LIBRARY_DIR ${ClickHouse_SOURCE_DIR}/contrib/ryu)
|
||||
|
||||
add_library(ryu
|
||||
${LIBRARY_DIR}/ryu/d2fixed.c
|
||||
${LIBRARY_DIR}/ryu/d2s.c
|
||||
${LIBRARY_DIR}/ryu/f2s.c
|
||||
${LIBRARY_DIR}/ryu/generic_128.c
|
||||
)
|
||||
|
||||
target_include_directories(ryu SYSTEM BEFORE PUBLIC "${LIBRARY_DIR}")
|
@ -94,8 +94,8 @@ if [ -n "$(ls /docker-entrypoint-initdb.d/)" ] || [ -n "$CLICKHOUSE_DB" ]; then
|
||||
pid="$!"
|
||||
|
||||
# check if clickhouse is ready to accept connections
|
||||
# will try to send ping clickhouse via http_port (max 12 retries, with 1 sec delay)
|
||||
if ! wget --spider --quiet --prefer-family=IPv6 --tries=12 --waitretry=1 --retry-connrefused "http://localhost:$HTTP_PORT/ping" ; then
|
||||
# will try to send ping clickhouse via http_port (max 12 retries by default, with 1 sec delay)
|
||||
if ! wget --spider --quiet --prefer-family=IPv6 --tries="${CLICKHOUSE_INIT_TIMEOUT:-12}" --waitretry=1 --retry-connrefused "http://localhost:$HTTP_PORT/ping" ; then
|
||||
echo >&2 'ClickHouse init process failed.'
|
||||
exit 1
|
||||
fi
|
||||
|
@ -137,7 +137,6 @@ function clone_submodules
|
||||
contrib/libxml2
|
||||
contrib/poco
|
||||
contrib/libunwind
|
||||
contrib/ryu
|
||||
contrib/fmtlib
|
||||
contrib/base64
|
||||
contrib/cctz
|
||||
@ -155,6 +154,7 @@ function clone_submodules
|
||||
contrib/croaring
|
||||
contrib/miniselect
|
||||
contrib/xz
|
||||
contrib/dragonbox
|
||||
)
|
||||
|
||||
git submodule sync
|
||||
|
@ -184,6 +184,10 @@ Sparse indexes allow you to work with a very large number of table rows, because
|
||||
|
||||
ClickHouse does not require a unique primary key. You can insert multiple rows with the same primary key.
|
||||
|
||||
You can use `Nullable`-typed expressions in the `PRIMARY KEY` and `ORDER BY` clauses. To allow this feature, turn on the [allow_nullable_key](../../../operations/settings/settings.md#allow-nullable-key) setting.
|
||||
|
||||
The [NULLS_LAST](../../../sql-reference/statements/select/order-by.md#sorting-of-special-values) principle applies for `NULL` values in the `ORDER BY` clause.
|
||||
|
||||
### Selecting the Primary Key {#selecting-the-primary-key}
|
||||
|
||||
The number of columns in the primary key is not explicitly limited. Depending on the data structure, you can include more or fewer columns in the primary key. This may:
|
||||
|
@ -2364,4 +2364,15 @@ Allows configurable `NULL` representation for [TSV](../../interfaces/formats.md#
|
||||
|
||||
Default value: `\N`.
|
||||
|
||||
## allow_nullable_key {#allow-nullable-key}
|
||||
|
||||
Allows using of the [Nullable](../../sql-reference/data-types/nullable.md#data_type-nullable)-typed values in a sorting and a primary key for [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md#table_engines-mergetree) tables.
|
||||
|
||||
Possible values:
|
||||
|
||||
- 1 — `Nullable`-type expressions are allowed in keys.
|
||||
- 0 — `Nullable`-type expressions are not allowed in keys.
|
||||
|
||||
Default value: `0`.
|
||||
|
||||
[Original article](https://clickhouse.tech/docs/en/operations/settings/settings/) <!-- hide -->
|
||||
|
@ -591,3 +591,7 @@ Result:
|
||||
```
|
||||
|
||||
[Original article](https://clickhouse.tech/docs/en/query_language/functions/string_search_functions/) <!--hide-->
|
||||
|
||||
## countMatches(haystack, pattern) {#countmatcheshaystack-pattern}
|
||||
|
||||
Returns the number of regular expression matches for a `pattern` in a `haystack`.
|
||||
|
150
docs/en/sql-reference/statements/explain.md
Normal file
150
docs/en/sql-reference/statements/explain.md
Normal file
@ -0,0 +1,150 @@
|
||||
---
|
||||
toc_priority: 39
|
||||
toc_title: EXPLAIN
|
||||
---
|
||||
|
||||
# EXPLAIN Statement {#explain}
|
||||
|
||||
Show the execution plan of a statement.
|
||||
|
||||
Syntax:
|
||||
|
||||
```sql
|
||||
EXPLAIN [AST | SYNTAX | PLAN | PIPELINE] [setting = value, ...] SELECT ... [FORMAT ...]
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```sql
|
||||
EXPLAIN SELECT sum(number) FROM numbers(10) UNION ALL SELECT sum(number) FROM numbers(10) ORDER BY sum(number) ASC FORMAT TSV;
|
||||
```
|
||||
|
||||
```sql
|
||||
Union
|
||||
Expression (Projection)
|
||||
Expression (Before ORDER BY and SELECT)
|
||||
Aggregating
|
||||
Expression (Before GROUP BY)
|
||||
SettingQuotaAndLimits (Set limits and quota after reading from storage)
|
||||
ReadFromStorage (SystemNumbers)
|
||||
Expression (Projection)
|
||||
MergingSorted (Merge sorted streams for ORDER BY)
|
||||
MergeSorting (Merge sorted blocks for ORDER BY)
|
||||
PartialSorting (Sort each block for ORDER BY)
|
||||
Expression (Before ORDER BY and SELECT)
|
||||
Aggregating
|
||||
Expression (Before GROUP BY)
|
||||
SettingQuotaAndLimits (Set limits and quota after reading from storage)
|
||||
ReadFromStorage (SystemNumbers)
|
||||
```
|
||||
|
||||
## EXPLAIN Types {#explain-types}
|
||||
|
||||
- `AST` — Abstract syntax tree.
|
||||
- `SYNTAX` — Query text after AST-level optimizations.
|
||||
- `PLAN` — Query execution plan.
|
||||
- `PIPELINE` — Query execution pipeline.
|
||||
|
||||
### EXPLAIN AST {#explain-ast}
|
||||
|
||||
Dump query AST.
|
||||
|
||||
Example:
|
||||
|
||||
```sql
|
||||
EXPLAIN AST SELECT 1;
|
||||
```
|
||||
|
||||
```sql
|
||||
SelectWithUnionQuery (children 1)
|
||||
ExpressionList (children 1)
|
||||
SelectQuery (children 1)
|
||||
ExpressionList (children 1)
|
||||
Literal UInt64_1
|
||||
```
|
||||
|
||||
### EXPLAIN SYNTAX {#explain-syntax}
|
||||
|
||||
Return query after syntax optimizations.
|
||||
|
||||
Example:
|
||||
|
||||
```sql
|
||||
EXPLAIN SYNTAX SELECT * FROM system.numbers AS a, system.numbers AS b, system.numbers AS c;
|
||||
```
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
`--a.number` AS `a.number`,
|
||||
`--b.number` AS `b.number`,
|
||||
number AS `c.number`
|
||||
FROM
|
||||
(
|
||||
SELECT
|
||||
number AS `--a.number`,
|
||||
b.number AS `--b.number`
|
||||
FROM system.numbers AS a
|
||||
CROSS JOIN system.numbers AS b
|
||||
) AS `--.s`
|
||||
CROSS JOIN system.numbers AS c
|
||||
```
|
||||
### EXPLAIN PLAN {#explain-plan}
|
||||
|
||||
Dump query plan steps.
|
||||
|
||||
Settings:
|
||||
|
||||
- `header` — Print output header for step. Default: 0.
|
||||
- `description` — Print step description. Default: 1.
|
||||
- `actions` — Print detailed information about step actions. Default: 0.
|
||||
|
||||
Example:
|
||||
|
||||
```sql
|
||||
EXPLAIN SELECT sum(number) FROM numbers(10) GROUP BY number % 4;
|
||||
```
|
||||
|
||||
```sql
|
||||
Union
|
||||
Expression (Projection)
|
||||
Expression (Before ORDER BY and SELECT)
|
||||
Aggregating
|
||||
Expression (Before GROUP BY)
|
||||
SettingQuotaAndLimits (Set limits and quota after reading from storage)
|
||||
ReadFromStorage (SystemNumbers)
|
||||
```
|
||||
|
||||
!!! note "Note"
|
||||
Step and query cost estimation is not supported.
|
||||
|
||||
### EXPLAIN PIPELINE {#explain-pipeline}
|
||||
|
||||
Settings:
|
||||
|
||||
- `header` — Print header for each output port. Default: 0.
|
||||
- `graph` — Use DOT graph description language. Default: 0.
|
||||
- `compact` — Print graph in compact mode if graph is enabled. Default: 1.
|
||||
|
||||
Example:
|
||||
|
||||
```sql
|
||||
EXPLAIN PIPELINE SELECT sum(number) FROM numbers_mt(100000) GROUP BY number % 4;
|
||||
```
|
||||
|
||||
```sql
|
||||
(Union)
|
||||
(Expression)
|
||||
ExpressionTransform
|
||||
(Expression)
|
||||
ExpressionTransform
|
||||
(Aggregating)
|
||||
Resize 2 → 1
|
||||
AggregatingTransform × 2
|
||||
(Expression)
|
||||
ExpressionTransform × 2
|
||||
(SettingQuotaAndLimits)
|
||||
(ReadFromStorage)
|
||||
NumbersMt × 2 0 → 1
|
||||
```
|
||||
|
||||
[Оriginal article](https://clickhouse.tech/docs/en/sql-reference/statements/explain/) <!--hide-->
|
@ -29,3 +29,4 @@ Statements represent various kinds of action you can perform using SQL queries.
|
||||
- [SET ROLE](../../sql-reference/statements/set-role.md)
|
||||
- [TRUNCATE](../../sql-reference/statements/truncate.md)
|
||||
- [USE](../../sql-reference/statements/use.md)
|
||||
- [EXPLAIN](../../sql-reference/statements/explain.md)
|
||||
|
@ -177,6 +177,10 @@ Marks numbers: 0 1 2 3 4 5 6 7 8
|
||||
|
||||
ClickHouse не требует уникального первичного ключа. Можно вставить много строк с одинаковым первичным ключом.
|
||||
|
||||
Ключ в `PRIMARY KEY` и `ORDER BY` может иметь тип `Nullable`. За поддержку этой возможности отвечает настройка [allow_nullable_key](../../../operations/settings/settings.md#allow-nullable-key).
|
||||
|
||||
При сортировке с использованием выражения `ORDER BY` для значений `NULL` всегда работает принцип [NULLS_LAST](../../../sql-reference/statements/select/order-by.md#sorting-of-special-values).
|
||||
|
||||
### Выбор первичного ключа {#vybor-pervichnogo-kliucha}
|
||||
|
||||
Количество столбцов в первичном ключе не ограничено явным образом. В зависимости от структуры данных в первичный ключ можно включать больше или меньше столбцов. Это может:
|
||||
|
@ -2235,4 +2235,15 @@ SELECT CAST(toNullable(toInt32(0)) AS Int32) as x, toTypeName(x);
|
||||
|
||||
Значение по умолчанию: `\N`.
|
||||
|
||||
## allow_nullable_key {#allow-nullable-key}
|
||||
|
||||
Включает или отключает поддержку типа [Nullable](../../sql-reference/data-types/nullable.md#data_type-nullable) для ключей таблиц [MergeTree](../../engines/table-engines/mergetree-family/mergetree.md#table_engines-mergetree).
|
||||
|
||||
Возможные значения:
|
||||
|
||||
- 1 — включает поддержку типа `Nullable` для ключей таблиц.
|
||||
- 0 — отключает поддержку типа `Nullable` для ключей таблиц.
|
||||
|
||||
Значение по умолчанию: `0`.
|
||||
|
||||
[Оригинальная статья](https://clickhouse.tech/docs/ru/operations/settings/settings/) <!--hide-->
|
||||
|
@ -1,12 +1,36 @@
|
||||
---
|
||||
toc_priority: 14
|
||||
toc_title: "\u266A\u64CD\u573A\u266A"
|
||||
toc_title: 体验平台
|
||||
---
|
||||
|
||||
# ClickHouse体验平台 {#clickhouse-playground}
|
||||
|
||||
[ClickHouse体验平台](https://play.clickhouse.tech?file=welcome) 允许人们通过即时运行查询来尝试ClickHouse,而无需设置他们的服务器或集群。
|
||||
体验平台中提供了几个示例数据集以及显示ClickHouse特性的示例查询。
|
||||
|
||||
体验平台中提供几个示例数据集以及显示ClickHouse特性的示例查询。还有一些ClickHouse LTS版本可供尝试。
|
||||
|
||||
ClickHouse体验平台提供了小型集群[Managed Service for ClickHouse](https://cloud.yandex.com/services/managed-clickhouse)实例配置(4 vCPU, 32 GB RAM)它们托管在[Yandex.Cloud](https://cloud.yandex.com/). 更多信息查询[cloud providers](../commercial/cloud.md).
|
||||
|
||||
您可以使用任何HTTP客户端对ClickHouse体验平台进行查询,例如[curl](https://curl.haxx.se)或者[wget](https://www.gnu.org/software/wget/),或使用[JDBC](../interfaces/jdbc.md)或者[ODBC](../interfaces/odbc.md)驱动连接。关于支持ClickHouse的软件产品的更多信息详见[here](../interfaces/index.md).
|
||||
|
||||
## Credentials {#credentials}
|
||||
|
||||
| 参数 | 值 |
|
||||
|:--------------------|:----------------------------------------|
|
||||
| HTTPS端点 | `https://play-api.clickhouse.tech:8443` |
|
||||
| TCP端点 | `play-api.clickhouse.tech:9440` |
|
||||
| 用户 | `playground` |
|
||||
| 密码 | `clickhouse` |
|
||||
|
||||
还有一些带有特定ClickHouse版本的附加信息来试验它们之间的差异(端口和用户/密码与上面相同):
|
||||
|
||||
- 20.3 LTS: `play-api-v20-3.clickhouse.tech`
|
||||
- 19.14 LTS: `play-api-v19-14.clickhouse.tech`
|
||||
|
||||
!!! note "注意"
|
||||
所有这些端点都需要安全的TLS连接。
|
||||
|
||||
## 查询限制 {#limitations}
|
||||
|
||||
查询以只读用户身份执行。 这意味着一些局限性:
|
||||
|
||||
@ -14,33 +38,34 @@ toc_title: "\u266A\u64CD\u573A\u266A"
|
||||
- 不允许插入查询
|
||||
|
||||
还强制执行以下设置:
|
||||
- [`max_result_bytes=10485760`](../operations/settings/query_complexity/#max-result-bytes)
|
||||
- [`max_result_rows=2000`](../operations/settings/query_complexity/#setting-max_result_rows)
|
||||
- [`result_overflow_mode=break`](../operations/settings/query_complexity/#result-overflow-mode)
|
||||
- [`max_execution_time=60000`](../operations/settings/query_complexity/#max-execution-time)
|
||||
- [max_result_bytes=10485760](../operations/settings/query-complexity/#max-result-bytes)
|
||||
- [max_result_rows=2000](../operations/settings/query-complexity/#setting-max_result_rows)
|
||||
- [result_overflow_mode=break](../operations/settings/query-complexity/#result-overflow-mode)
|
||||
- [max_execution_time=60000](../operations/settings/query-complexity/#max-execution-time)
|
||||
|
||||
ClickHouse体验还有如下:
|
||||
[ClickHouse管理服务](https://cloud.yandex.com/services/managed-clickhouse)
|
||||
实例托管 [Yandex云](https://cloud.yandex.com/)。
|
||||
更多信息 [云提供商](../commercial/cloud.md)。
|
||||
|
||||
ClickHouse体验平台界面实际上是通过ClickHouse [HTTP API](../interfaces/http.md)接口实现的.
|
||||
体验平台后端只是一个ClickHouse集群,没有任何额外的服务器端应用程序。
|
||||
体验平台也同样提供了ClickHouse HTTPS服务端口。
|
||||
## 示例 {#examples}
|
||||
|
||||
您可以使用任何HTTP客户端向体验平台进行查询,例如 [curl](https://curl.haxx.se) 或 [wget](https://www.gnu.org/software/wget/),或使用以下方式建立连接 [JDBC](../interfaces/jdbc.md) 或 [ODBC](../interfaces/odbc.md) 驱动。
|
||||
有关支持ClickHouse的软件产品的更多信息,请访问 [这里](../interfaces/index.md)。
|
||||
|
||||
| 参数 | 值 |
|
||||
|:---------|:--------------------------------------|
|
||||
| 服务端口 | https://play-api.clickhouse.tech:8443 |
|
||||
| 用户 | `playground` |
|
||||
| 密码 | `clickhouse` |
|
||||
|
||||
请注意,此服务端口需要安全连接。
|
||||
|
||||
示例:
|
||||
使用`curl`连接Https服务:
|
||||
|
||||
``` bash
|
||||
curl "https://play-api.clickhouse.tech:8443/?query=SELECT+'Play+ClickHouse!';&user=playground&password=clickhouse&database=datasets"
|
||||
curl "https://play-api.clickhouse.tech:8443/?query=SELECT+'Play+ClickHouse\!';&user=playground&password=clickhouse&database=datasets"
|
||||
```
|
||||
|
||||
TCP连接示例[CLI](../interfaces/cli.md):
|
||||
|
||||
``` bash
|
||||
clickhouse client --secure -h play-api.clickhouse.tech --port 9440 -u playground --password clickhouse -q "SELECT 'Play ClickHouse\!'"
|
||||
```
|
||||
|
||||
## Implementation Details {#implementation-details}
|
||||
|
||||
ClickHouse体验平台界面实际上是通过ClickHouse [HTTP API](../interfaces/http.md)接口实现的。
|
||||
ClickHouse体验平台是一个ClickHouse集群,没有任何附加的服务器端应用程序。如上所述,ClickHouse的HTTPS和TCP/TLS端点也可以作为体验平台的一部分公开使用, 代理通过[Cloudflare Spectrum](https://www.cloudflare.com/products/cloudflare-spectrum/)增加一层额外的保护和改善连接。
|
||||
|
||||
!!! warning "注意"
|
||||
**强烈不推荐**在任何其他情况下将ClickHouse服务器暴露给公共互联网。确保它只在私有网络上侦听,并由正确配置的防火墙监控。
|
||||
|
@ -1,19 +1,27 @@
|
||||
---
|
||||
toc_folder_title: Interfaces
|
||||
toc_priority: 14
|
||||
toc_title: 客户端
|
||||
---
|
||||
|
||||
# 客户端 {#interfaces}
|
||||
|
||||
ClickHouse提供了两个网络接口(两者都可以选择包装在TLS中以提高安全性):
|
||||
ClickHouse提供了两个网络接口(两个都可以选择包装在TLS中以增加安全性):
|
||||
|
||||
- [HTTP](http.md),记录在案,易于使用.
|
||||
- [本地TCP](tcp.md),这有较少的开销.
|
||||
- [HTTP](http.md), 包含文档,易于使用。
|
||||
- [Native TCP](../interfaces/tcp.md),简单,方便使用。
|
||||
|
||||
在大多数情况下,建议使用适当的工具或库,而不是直接与这些工具或库进行交互。 Yandex的官方支持如下:
|
||||
\* [命令行客户端](cli.md)
|
||||
\* [JDBC驱动程序](jdbc.md)
|
||||
\* [ODBC驱动程序](odbc.md)
|
||||
\* [C++客户端库](cpp.md)
|
||||
在大多数情况下,建议使用适当的工具或库,而不是直接与它们交互。Yandex官方支持的项目有:
|
||||
|
||||
还有许多第三方库可供使用ClickHouse:
|
||||
\* [客户端库](third-party/client-libraries.md)
|
||||
\* [集成](third-party/integrations.md)
|
||||
\* [可视界面](third-party/gui.md)
|
||||
- [命令行客户端](../interfaces/cli.md)
|
||||
- [JDBC驱动](../interfaces/jdbc.md)
|
||||
- [ODBC驱动](../interfaces/odbc.md)
|
||||
- [C++客户端](../interfaces/cpp.md)
|
||||
|
||||
[来源文章](https://clickhouse.tech/docs/zh/interfaces/) <!--hide-->
|
||||
还有一些广泛的第三方库可供ClickHouse使用:
|
||||
|
||||
- [客户端库](../interfaces/third-party/client-libraries.md)
|
||||
- [第三方集成库](../interfaces/third-party/integrations.md)
|
||||
- [可视化UI](../interfaces/third-party/gui.md)
|
||||
|
||||
[来源文章](https://clickhouse.tech/docs/en/interfaces/) <!--hide-->
|
||||
|
@ -352,17 +352,20 @@
|
||||
/// The query is saved in browser history (in state JSON object)
|
||||
/// as well as in URL fragment identifier.
|
||||
if (query != previous_query) {
|
||||
previous_query = query;
|
||||
var state = {
|
||||
query: query,
|
||||
status: this.status,
|
||||
response: this.response.length > 100000 ? null : this.response /// Lower than the browser's limit.
|
||||
};
|
||||
var title = "ClickHouse Query: " + query;
|
||||
history.pushState(
|
||||
{
|
||||
query: query,
|
||||
status: this.status,
|
||||
response: this.response.length > 100000 ? null : this.response /// Lower than the browser's limit.
|
||||
},
|
||||
title,
|
||||
window.location.pathname + '?user=' + encodeURIComponent(user) + '#' + window.btoa(query));
|
||||
var url = window.location.pathname + '?user=' + encodeURIComponent(user) + '#' + window.btoa(query);
|
||||
if (previous_query == '') {
|
||||
history.replaceState(state, title, url);
|
||||
} else {
|
||||
history.pushState(state, title, url);
|
||||
}
|
||||
document.title = title;
|
||||
previous_query = query;
|
||||
}
|
||||
} else {
|
||||
//console.log(this);
|
||||
|
@ -242,7 +242,7 @@ target_link_libraries (clickhouse_common_io
|
||||
PUBLIC
|
||||
common
|
||||
${DOUBLE_CONVERSION_LIBRARIES}
|
||||
ryu
|
||||
dragonbox_to_chars
|
||||
)
|
||||
|
||||
if(RE2_LIBRARY)
|
||||
|
@ -37,12 +37,16 @@ void encodeSHA256(const void * text, size_t size, unsigned char * out)
|
||||
|
||||
String getOpenSSLErrors()
|
||||
{
|
||||
BIO * mem = BIO_new(BIO_s_mem());
|
||||
SCOPE_EXIT(BIO_free(mem));
|
||||
ERR_print_errors(mem);
|
||||
char * buf = nullptr;
|
||||
size_t size = BIO_get_mem_data(mem, &buf);
|
||||
return String(buf, size);
|
||||
String res;
|
||||
ERR_print_errors_cb([](const char * str, size_t len, void * ctx)
|
||||
{
|
||||
String & out = *reinterpret_cast<String*>(ctx);
|
||||
if (!out.empty())
|
||||
out += ", ";
|
||||
out.append(str, len);
|
||||
return 1;
|
||||
}, &res);
|
||||
return res;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -213,7 +213,6 @@ std::pair<ResponsePtr, Undo> TestKeeperCreateRequest::process(TestKeeper::Contai
|
||||
if (is_sequential)
|
||||
{
|
||||
auto seq_num = it->second.seq_num;
|
||||
++it->second.seq_num;
|
||||
|
||||
std::stringstream seq_num_str; // STYLE_CHECK_ALLOW_STD_STRING_STREAM
|
||||
seq_num_str.exceptions(std::ios::failbit);
|
||||
@ -222,18 +221,19 @@ std::pair<ResponsePtr, Undo> TestKeeperCreateRequest::process(TestKeeper::Contai
|
||||
path_created += seq_num_str.str();
|
||||
}
|
||||
|
||||
/// Increment sequential number even if node is not sequential
|
||||
++it->second.seq_num;
|
||||
|
||||
response.path_created = path_created;
|
||||
container.emplace(path_created, std::move(created_node));
|
||||
|
||||
undo = [&container, path_created, is_sequential = is_sequential, parent_path = it->first]
|
||||
undo = [&container, path_created, parent_path = it->first]
|
||||
{
|
||||
container.erase(path_created);
|
||||
auto & undo_parent = container.at(parent_path);
|
||||
--undo_parent.stat.cversion;
|
||||
--undo_parent.stat.numChildren;
|
||||
|
||||
if (is_sequential)
|
||||
--undo_parent.seq_num;
|
||||
--undo_parent.seq_num;
|
||||
};
|
||||
|
||||
++it->second.stat.cversion;
|
||||
|
@ -398,6 +398,7 @@ class IColumn;
|
||||
M(Bool, allow_non_metadata_alters, true, "Allow to execute alters which affects not only tables metadata, but also data on disk", 0) \
|
||||
M(Bool, enable_global_with_statement, false, "Propagate WITH statements to UNION queries and all subqueries", 0) \
|
||||
M(Bool, aggregate_functions_null_for_empty, false, "Rewrite all aggregate functions in a query, adding -OrNull suffix to them", 0) \
|
||||
M(Bool, optimize_skip_merged_partitions, false, "Skip partitions with one part with level > 0 in optimize final", 0) \
|
||||
\
|
||||
M(Bool, use_antlr_parser, false, "Parse incoming queries using ANTLR-generated parser", 0) \
|
||||
\
|
||||
|
@ -112,9 +112,12 @@ static void validateKeyTypes(const DataTypes & key_types)
|
||||
if (key_types.empty() || key_types.size() > 2)
|
||||
throw Exception{"Expected a single IP address or IP with mask", ErrorCodes::TYPE_MISMATCH};
|
||||
|
||||
const auto & actual_type = key_types[0]->getName();
|
||||
if (actual_type != "UInt32" && actual_type != "FixedString(16)")
|
||||
throw Exception{"Key does not match, expected either UInt32 or FixedString(16)", ErrorCodes::TYPE_MISMATCH};
|
||||
const auto * key_ipv4type = typeid_cast<const DataTypeUInt32 *>(key_types[0].get());
|
||||
const auto * key_ipv6type = typeid_cast<const DataTypeFixedString *>(key_types[0].get());
|
||||
|
||||
if (key_ipv4type == nullptr && (key_ipv6type == nullptr || key_ipv6type->getN() != 16))
|
||||
throw Exception{"Key does not match, expected either `IPv4` (`UInt32`) or `IPv6` (`FixedString(16)`)",
|
||||
ErrorCodes::TYPE_MISMATCH};
|
||||
|
||||
if (key_types.size() > 1)
|
||||
{
|
||||
|
@ -30,6 +30,7 @@ namespace ErrorCodes
|
||||
extern const int UNKNOWN_POLICY;
|
||||
extern const int UNKNOWN_VOLUME;
|
||||
extern const int LOGICAL_ERROR;
|
||||
extern const int NOT_ENOUGH_SPACE;
|
||||
}
|
||||
|
||||
|
||||
@ -210,6 +211,14 @@ ReservationPtr StoragePolicy::reserve(UInt64 bytes) const
|
||||
}
|
||||
|
||||
|
||||
ReservationPtr StoragePolicy::reserveAndCheck(UInt64 bytes) const
|
||||
{
|
||||
if (auto res = reserve(bytes, 0))
|
||||
return res;
|
||||
throw Exception(ErrorCodes::NOT_ENOUGH_SPACE, "Cannot reserve {}, not enough space", ReadableSize(bytes));
|
||||
}
|
||||
|
||||
|
||||
ReservationPtr StoragePolicy::makeEmptyReservationOnLargestDisk() const
|
||||
{
|
||||
UInt64 max_space = 0;
|
||||
@ -226,7 +235,14 @@ ReservationPtr StoragePolicy::makeEmptyReservationOnLargestDisk() const
|
||||
}
|
||||
}
|
||||
}
|
||||
return max_disk->reserve(0);
|
||||
auto reservation = max_disk->reserve(0);
|
||||
if (!reservation)
|
||||
{
|
||||
/// I'm not sure if it's really a logical error, but exception message
|
||||
/// "Cannot reserve 0 bytes" looks too strange to throw it with another exception code.
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Cannot reserve 0 bytes");
|
||||
}
|
||||
return reservation;
|
||||
}
|
||||
|
||||
|
||||
|
@ -61,10 +61,13 @@ public:
|
||||
|
||||
const String & getName() const { return name; }
|
||||
|
||||
/// Returns valid reservation or null
|
||||
/// Returns valid reservation or nullptr
|
||||
ReservationPtr reserve(UInt64 bytes) const;
|
||||
|
||||
/// Reserve space on any volume with index > min_volume_index
|
||||
/// Reserves space on any volume or throws
|
||||
ReservationPtr reserveAndCheck(UInt64 bytes) const;
|
||||
|
||||
/// Reserves space on any volume with index > min_volume_index or returns nullptr
|
||||
ReservationPtr reserve(UInt64 bytes, size_t min_volume_index) const;
|
||||
|
||||
/// Find volume index, which contains disk
|
||||
|
@ -82,10 +82,9 @@ struct KeyHolder<CipherMode::MySQLCompatibility>
|
||||
return foldEncryptionKeyInMySQLCompatitableMode(cipher_key_size, key, folded_key);
|
||||
}
|
||||
|
||||
~KeyHolder()
|
||||
{
|
||||
OPENSSL_cleanse(folded_key.data(), folded_key.size());
|
||||
}
|
||||
/// There is a function to clear key securely.
|
||||
/// It makes absolutely zero sense to call it here because
|
||||
/// key comes from column and already copied multiple times through various memory buffers.
|
||||
|
||||
private:
|
||||
std::array<char, EVP_MAX_KEY_LENGTH> folded_key;
|
||||
@ -119,7 +118,7 @@ inline void validateCipherMode(const EVP_CIPHER * evp_cipher)
|
||||
}
|
||||
}
|
||||
|
||||
throw DB::Exception("Unsupported cipher mode " + std::string(EVP_CIPHER_name(evp_cipher)), DB::ErrorCodes::BAD_ARGUMENTS);
|
||||
throw DB::Exception("Unsupported cipher mode", DB::ErrorCodes::BAD_ARGUMENTS);
|
||||
}
|
||||
|
||||
template <CipherMode mode>
|
||||
|
29
src/Functions/countMatches.cpp
Normal file
29
src/Functions/countMatches.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "FunctionFactory.h"
|
||||
#include "countMatches.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct FunctionCountMatchesCaseSensitive
|
||||
{
|
||||
static constexpr auto name = "countMatches";
|
||||
static constexpr bool case_insensitive = false;
|
||||
};
|
||||
struct FunctionCountMatchesCaseInsensitive
|
||||
{
|
||||
static constexpr auto name = "countMatchesCaseInsensitive";
|
||||
static constexpr bool case_insensitive = true;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
void registerFunctionCountMatches(FunctionFactory & factory)
|
||||
{
|
||||
factory.registerFunction<FunctionCountMatches<FunctionCountMatchesCaseSensitive>>(FunctionFactory::CaseSensitive);
|
||||
factory.registerFunction<FunctionCountMatches<FunctionCountMatchesCaseInsensitive>>(FunctionFactory::CaseSensitive);
|
||||
}
|
||||
|
||||
}
|
125
src/Functions/countMatches.h
Normal file
125
src/Functions/countMatches.h
Normal file
@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
|
||||
#include <Functions/IFunctionImpl.h>
|
||||
#include <Functions/FunctionFactory.h>
|
||||
#include <Functions/FunctionHelpers.h>
|
||||
#include <Columns/ColumnString.h>
|
||||
#include <Columns/ColumnsNumber.h>
|
||||
#include <DataTypes/DataTypesNumber.h>
|
||||
#include <DataTypes/DataTypeString.h>
|
||||
#include <Functions/Regexps.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int ILLEGAL_TYPE_OF_ARGUMENT;
|
||||
extern const int ILLEGAL_COLUMN;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
|
||||
using Pos = const char *;
|
||||
|
||||
template <class CountMatchesBase>
|
||||
class FunctionCountMatches : public IFunction
|
||||
{
|
||||
public:
|
||||
static constexpr auto name = CountMatchesBase::name;
|
||||
static FunctionPtr create(const Context &) { return std::make_shared<FunctionCountMatches<CountMatchesBase>>(); }
|
||||
|
||||
String getName() const override { return name; }
|
||||
size_t getNumberOfArguments() const override { return 2; }
|
||||
|
||||
DataTypePtr getReturnTypeImpl(const ColumnsWithTypeAndName & arguments) const override
|
||||
{
|
||||
if (!isStringOrFixedString(arguments[1].type))
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of second argument (pattern) of function {}. Must be String/FixedString.",
|
||||
arguments[1].type->getName(), getName());
|
||||
if (!isStringOrFixedString(arguments[0].type))
|
||||
throw Exception(ErrorCodes::ILLEGAL_TYPE_OF_ARGUMENT,
|
||||
"Illegal type {} of first argument (haystack) of function {}. Must be String/FixedString.",
|
||||
arguments[0].type->getName(), getName());
|
||||
const auto * column = arguments[1].column.get();
|
||||
if (!column || !checkAndGetColumnConstStringOrFixedString(column))
|
||||
throw Exception(ErrorCodes::ILLEGAL_COLUMN,
|
||||
"The second argument of function {} should be a constant string with the pattern",
|
||||
getName());
|
||||
|
||||
return std::make_shared<DataTypeUInt64>();
|
||||
}
|
||||
|
||||
ColumnPtr executeImpl(const ColumnsWithTypeAndName & arguments, const DataTypePtr & result_type, size_t input_rows_count) const override
|
||||
{
|
||||
const ColumnConst * column_pattern = checkAndGetColumnConstStringOrFixedString(arguments[1].column.get());
|
||||
Regexps::Pool::Pointer re = Regexps::get<false /* like */, true /* is_no_capture */, CountMatchesBase::case_insensitive>(column_pattern->getValue<String>());
|
||||
OptimizedRegularExpression::MatchVec matches;
|
||||
|
||||
const IColumn * column_haystack = arguments[0].column.get();
|
||||
|
||||
if (const ColumnString * col_str = checkAndGetColumn<ColumnString>(column_haystack))
|
||||
{
|
||||
auto result_column = ColumnUInt64::create();
|
||||
|
||||
const ColumnString::Chars & src_chars = col_str->getChars();
|
||||
const ColumnString::Offsets & src_offsets = col_str->getOffsets();
|
||||
|
||||
ColumnUInt64::Container & vec_res = result_column->getData();
|
||||
vec_res.resize(input_rows_count);
|
||||
|
||||
size_t size = src_offsets.size();
|
||||
ColumnString::Offset current_src_offset = 0;
|
||||
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
{
|
||||
Pos pos = reinterpret_cast<Pos>(&src_chars[current_src_offset]);
|
||||
current_src_offset = src_offsets[i];
|
||||
Pos end = reinterpret_cast<Pos>(&src_chars[current_src_offset]) - 1;
|
||||
|
||||
StringRef str(pos, end - pos);
|
||||
vec_res[i] = countMatches(str, re, matches);
|
||||
}
|
||||
|
||||
return result_column;
|
||||
}
|
||||
else if (const ColumnConst * col_const_str = checkAndGetColumnConstStringOrFixedString(column_haystack))
|
||||
{
|
||||
StringRef str = col_const_str->getDataColumn().getDataAt(0);
|
||||
uint64_t matches_count = countMatches(str, re, matches);
|
||||
return result_type->createColumnConst(input_rows_count, matches_count);
|
||||
}
|
||||
else
|
||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Error in FunctionCountMatches::getReturnTypeImpl()");
|
||||
}
|
||||
|
||||
static uint64_t countMatches(StringRef src, Regexps::Pool::Pointer & re, OptimizedRegularExpression::MatchVec & matches)
|
||||
{
|
||||
/// Only one match is required, no need to copy more.
|
||||
static const unsigned matches_limit = 1;
|
||||
|
||||
Pos pos = reinterpret_cast<Pos>(src.data);
|
||||
Pos end = reinterpret_cast<Pos>(src.data + src.size);
|
||||
|
||||
uint64_t match_count = 0;
|
||||
while (true)
|
||||
{
|
||||
if (pos >= end)
|
||||
break;
|
||||
if (!re->match(pos, end - pos, matches, matches_limit))
|
||||
break;
|
||||
/// Progress should be made, but with empty match the progress will not be done.
|
||||
/// Also note that simply check is pattern empty is not enough,
|
||||
/// since for example "'[f]{0}'" will match zero bytes:
|
||||
if (!matches[0].length)
|
||||
break;
|
||||
pos += matches[0].offset + matches[0].length;
|
||||
match_count++;
|
||||
}
|
||||
|
||||
return match_count;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
@ -32,6 +32,7 @@ void registerFunctionTrim(FunctionFactory &);
|
||||
void registerFunctionRegexpQuoteMeta(FunctionFactory &);
|
||||
void registerFunctionNormalizeQuery(FunctionFactory &);
|
||||
void registerFunctionNormalizedQueryHash(FunctionFactory &);
|
||||
void registerFunctionCountMatches(FunctionFactory &);
|
||||
|
||||
#if USE_BASE64
|
||||
void registerFunctionBase64Encode(FunctionFactory &);
|
||||
@ -66,6 +67,7 @@ void registerFunctionsString(FunctionFactory & factory)
|
||||
registerFunctionRegexpQuoteMeta(factory);
|
||||
registerFunctionNormalizeQuery(factory);
|
||||
registerFunctionNormalizedQueryHash(factory);
|
||||
registerFunctionCountMatches(factory);
|
||||
#if USE_BASE64
|
||||
registerFunctionBase64Encode(factory);
|
||||
registerFunctionBase64Decode(factory);
|
||||
|
@ -208,6 +208,7 @@ SRCS(
|
||||
cos.cpp
|
||||
cosh.cpp
|
||||
countDigits.cpp
|
||||
countMatches.cpp
|
||||
countSubstrings.cpp
|
||||
countSubstringsCaseInsensitive.cpp
|
||||
countSubstringsCaseInsensitiveUTF8.cpp
|
||||
|
@ -10,7 +10,7 @@
|
||||
namespace DB::S3
|
||||
{
|
||||
PocoHTTPResponseStream::PocoHTTPResponseStream(std::shared_ptr<Poco::Net::HTTPClientSession> session_, std::istream & response_stream_)
|
||||
: Aws::IStream(response_stream_.rdbuf()), session(std::move(session_))
|
||||
: Aws::IOStream(response_stream_.rdbuf()), session(std::move(session_))
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ namespace DB::S3
|
||||
/**
|
||||
* Wrapper of IStream to store response stream and corresponding HTTP session.
|
||||
*/
|
||||
class PocoHTTPResponseStream : public Aws::IStream
|
||||
class PocoHTTPResponseStream : public Aws::IOStream
|
||||
{
|
||||
public:
|
||||
PocoHTTPResponseStream(std::shared_ptr<Poco::Net::HTTPClientSession> session_, std::istream & response_stream_);
|
||||
|
@ -29,7 +29,7 @@
|
||||
#include <IO/DoubleConverter.h>
|
||||
#include <IO/WriteBufferFromString.h>
|
||||
|
||||
#include <ryu/ryu.h>
|
||||
#include <dragonbox/dragonbox_to_chars.h>
|
||||
|
||||
#include <Formats/FormatSettings.h>
|
||||
|
||||
@ -228,14 +228,14 @@ inline size_t writeFloatTextFastPath(T x, char * buffer)
|
||||
if (DecomposedFloat64(x).is_inside_int64())
|
||||
result = itoa(Int64(x), buffer) - buffer;
|
||||
else
|
||||
result = d2s_buffered_n(x, buffer);
|
||||
result = jkj::dragonbox::to_chars_n(x, buffer) - buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (DecomposedFloat32(x).is_inside_int32())
|
||||
result = itoa(Int32(x), buffer) - buffer;
|
||||
else
|
||||
result = f2s_buffered_n(x, buffer);
|
||||
result = jkj::dragonbox::to_chars_n(x, buffer) - buffer;
|
||||
}
|
||||
|
||||
if (result <= 0)
|
||||
|
@ -80,8 +80,8 @@ target_link_libraries (parse_date_time_best_effort PRIVATE clickhouse_common_io)
|
||||
add_executable (zlib_ng_bug zlib_ng_bug.cpp)
|
||||
target_link_libraries (zlib_ng_bug PRIVATE ${ZLIB_LIBRARIES})
|
||||
|
||||
add_executable (ryu_test ryu_test.cpp)
|
||||
target_link_libraries (ryu_test PRIVATE ryu)
|
||||
add_executable (dragonbox_test dragonbox_test.cpp)
|
||||
target_link_libraries (dragonbox_test PRIVATE dragonbox_to_chars)
|
||||
|
||||
add_executable (zstd_buffers zstd_buffers.cpp)
|
||||
target_link_libraries (zstd_buffers PRIVATE clickhouse_common_io)
|
||||
|
@ -1,8 +1,7 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cstring>
|
||||
#include <ryu/ryu.h>
|
||||
|
||||
#include <dragonbox/dragonbox_to_chars.h>
|
||||
|
||||
struct DecomposedFloat64
|
||||
{
|
||||
@ -84,7 +83,8 @@ int main(int argc, char ** argv)
|
||||
double x = argc > 1 ? std::stod(argv[1]) : 0;
|
||||
char buf[32];
|
||||
|
||||
d2s_buffered(x, buf);
|
||||
std::cout << "dragonbox output" << std::endl;
|
||||
jkj::dragonbox::to_chars(x, buf);
|
||||
std::cout << buf << "\n";
|
||||
|
||||
std::cout << DecomposedFloat64(x).isInsideInt64() << "\n";
|
@ -567,7 +567,7 @@ void DistributedBlockOutputStream::writeToShard(const Block & block, const std::
|
||||
/// and keep monitor thread out from reading incomplete data
|
||||
std::string first_file_tmp_path{};
|
||||
|
||||
auto reservation = storage.getStoragePolicy()->reserve(block.bytes());
|
||||
auto reservation = storage.getStoragePolicy()->reserveAndCheck(block.bytes());
|
||||
auto disk = reservation->getDisk()->getPath();
|
||||
auto data_path = storage.getRelativeDataPath();
|
||||
|
||||
|
@ -734,7 +734,7 @@ void MergeTreeData::loadDataParts(bool skip_sanity_checks)
|
||||
for (auto && part : write_ahead_log->restore(metadata_snapshot))
|
||||
parts_from_wal.push_back(std::move(part));
|
||||
}
|
||||
else if (startsWith(it->name(), MergeTreeWriteAheadLog::WAL_FILE_NAME))
|
||||
else if (startsWith(it->name(), MergeTreeWriteAheadLog::WAL_FILE_NAME) && settings->in_memory_parts_enable_wal)
|
||||
{
|
||||
MergeTreeWriteAheadLog wal(*this, disk_ptr, it->name());
|
||||
for (auto && part : wal.restore(metadata_snapshot))
|
||||
@ -3140,8 +3140,7 @@ inline ReservationPtr checkAndReturnReservation(UInt64 expected_size, Reservatio
|
||||
ReservationPtr MergeTreeData::reserveSpace(UInt64 expected_size) const
|
||||
{
|
||||
expected_size = std::max(RESERVATION_MIN_ESTIMATION_SIZE, expected_size);
|
||||
auto reservation = getStoragePolicy()->reserve(expected_size);
|
||||
return checkAndReturnReservation(expected_size, std::move(reservation));
|
||||
return getStoragePolicy()->reserveAndCheck(expected_size);
|
||||
}
|
||||
|
||||
ReservationPtr MergeTreeData::reserveSpace(UInt64 expected_size, SpacePtr space)
|
||||
|
@ -203,7 +203,7 @@ UInt64 MergeTreeDataMergerMutator::getMaxSourcePartSizeForMutation() const
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool MergeTreeDataMergerMutator::selectPartsToMerge(
|
||||
SelectPartsDecision MergeTreeDataMergerMutator::selectPartsToMerge(
|
||||
FutureMergedMutatedPart & future_part,
|
||||
bool aggressive,
|
||||
size_t max_total_size_to_merge,
|
||||
@ -219,7 +219,7 @@ bool MergeTreeDataMergerMutator::selectPartsToMerge(
|
||||
{
|
||||
if (out_disable_reason)
|
||||
*out_disable_reason = "There are no parts in the table";
|
||||
return false;
|
||||
return SelectPartsDecision::CANNOT_SELECT;
|
||||
}
|
||||
|
||||
time_t current_time = std::time(nullptr);
|
||||
@ -341,7 +341,7 @@ bool MergeTreeDataMergerMutator::selectPartsToMerge(
|
||||
{
|
||||
if (out_disable_reason)
|
||||
*out_disable_reason = "There is no need to merge parts according to merge selector algorithm";
|
||||
return false;
|
||||
return SelectPartsDecision::CANNOT_SELECT;
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,27 +355,37 @@ bool MergeTreeDataMergerMutator::selectPartsToMerge(
|
||||
|
||||
LOG_DEBUG(log, "Selected {} parts from {} to {}", parts.size(), parts.front()->name, parts.back()->name);
|
||||
future_part.assign(std::move(parts));
|
||||
return true;
|
||||
return SelectPartsDecision::SELECTED;
|
||||
}
|
||||
|
||||
bool MergeTreeDataMergerMutator::selectAllPartsToMergeWithinPartition(
|
||||
SelectPartsDecision MergeTreeDataMergerMutator::selectAllPartsToMergeWithinPartition(
|
||||
FutureMergedMutatedPart & future_part,
|
||||
UInt64 & available_disk_space,
|
||||
const AllowedMergingPredicate & can_merge,
|
||||
const String & partition_id,
|
||||
bool final,
|
||||
String * out_disable_reason)
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
String * out_disable_reason,
|
||||
bool optimize_skip_merged_partitions)
|
||||
{
|
||||
MergeTreeData::DataPartsVector parts = selectAllPartsFromPartition(partition_id);
|
||||
|
||||
if (parts.empty())
|
||||
return false;
|
||||
return SelectPartsDecision::CANNOT_SELECT;
|
||||
|
||||
if (!final && parts.size() == 1)
|
||||
{
|
||||
if (out_disable_reason)
|
||||
*out_disable_reason = "There is only one part inside partition";
|
||||
return false;
|
||||
return SelectPartsDecision::CANNOT_SELECT;
|
||||
}
|
||||
|
||||
/// If final, optimize_skip_merged_partitions is true and we have only one part in partition with level > 0
|
||||
/// than we don't select it to merge. But if there are some expired TTL then merge is needed
|
||||
if (final && optimize_skip_merged_partitions && parts.size() == 1 && parts[0]->info.level > 0 &&
|
||||
(!metadata_snapshot->hasAnyTTL() || parts[0]->checkAllTTLCalculated(metadata_snapshot)))
|
||||
{
|
||||
return SelectPartsDecision::NOTHING_TO_MERGE;
|
||||
}
|
||||
|
||||
auto it = parts.begin();
|
||||
@ -387,7 +397,7 @@ bool MergeTreeDataMergerMutator::selectAllPartsToMergeWithinPartition(
|
||||
/// For the case of one part, we check that it can be merged "with itself".
|
||||
if ((it != parts.begin() || parts.size() == 1) && !can_merge(*prev_it, *it, out_disable_reason))
|
||||
{
|
||||
return false;
|
||||
return SelectPartsDecision::CANNOT_SELECT;
|
||||
}
|
||||
|
||||
sum_bytes += (*it)->getBytesOnDisk();
|
||||
@ -417,14 +427,14 @@ bool MergeTreeDataMergerMutator::selectAllPartsToMergeWithinPartition(
|
||||
if (out_disable_reason)
|
||||
*out_disable_reason = fmt::format("Insufficient available disk space, required {}", ReadableSize(required_disk_space));
|
||||
|
||||
return false;
|
||||
return SelectPartsDecision::CANNOT_SELECT;
|
||||
}
|
||||
|
||||
LOG_DEBUG(log, "Selected {} parts from {} to {}", parts.size(), parts.front()->name, parts.back()->name);
|
||||
future_part.assign(std::move(parts));
|
||||
|
||||
available_disk_space -= required_disk_space;
|
||||
return true;
|
||||
return SelectPartsDecision::SELECTED;
|
||||
}
|
||||
|
||||
|
||||
|
@ -15,6 +15,13 @@ namespace DB
|
||||
|
||||
class MergeProgressCallback;
|
||||
|
||||
enum class SelectPartsDecision
|
||||
{
|
||||
SELECTED = 0,
|
||||
CANNOT_SELECT = 1,
|
||||
NOTHING_TO_MERGE = 2,
|
||||
};
|
||||
|
||||
/// Auxiliary struct holding metainformation for the future merged or mutated part.
|
||||
struct FutureMergedMutatedPart
|
||||
{
|
||||
@ -79,7 +86,7 @@ public:
|
||||
* - Parts between which another part can still appear can not be merged. Refer to METR-7001.
|
||||
* - A part that already merges with something in one place, you can not start to merge into something else in another place.
|
||||
*/
|
||||
bool selectPartsToMerge(
|
||||
SelectPartsDecision selectPartsToMerge(
|
||||
FutureMergedMutatedPart & future_part,
|
||||
bool aggressive,
|
||||
size_t max_total_size_to_merge,
|
||||
@ -88,15 +95,19 @@ public:
|
||||
String * out_disable_reason = nullptr);
|
||||
|
||||
/** Select all the parts in the specified partition for merge, if possible.
|
||||
* final - choose to merge even a single part - that is, allow to merge one part "with itself".
|
||||
* final - choose to merge even a single part - that is, allow to merge one part "with itself",
|
||||
* but if setting optimize_skip_merged_partitions is true than single part with level > 0
|
||||
* and without expired TTL won't be merged with itself.
|
||||
*/
|
||||
bool selectAllPartsToMergeWithinPartition(
|
||||
SelectPartsDecision selectAllPartsToMergeWithinPartition(
|
||||
FutureMergedMutatedPart & future_part,
|
||||
UInt64 & available_disk_space,
|
||||
const AllowedMergingPredicate & can_merge,
|
||||
const String & partition_id,
|
||||
bool final,
|
||||
String * out_disable_reason = nullptr);
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
String * out_disable_reason = nullptr,
|
||||
bool optimize_skip_merged_partitions = false);
|
||||
|
||||
/** Merge the parts.
|
||||
* If `reservation != nullptr`, now and then reduces the size of the reserved space
|
||||
|
@ -22,7 +22,7 @@ struct MergeTreeWriterSettings
|
||||
MergeTreeWriterSettings(const Settings & global_settings, bool can_use_adaptive_granularity_,
|
||||
size_t aio_threshold_, bool blocks_are_granules_size_ = false)
|
||||
: min_compress_block_size(global_settings.min_compress_block_size)
|
||||
, max_compress_block_size(global_settings.min_compress_block_size)
|
||||
, max_compress_block_size(global_settings.max_compress_block_size)
|
||||
, aio_threshold(aio_threshold_)
|
||||
, can_use_adaptive_granularity(can_use_adaptive_granularity_)
|
||||
, blocks_are_granules_size(blocks_are_granules_size_) {}
|
||||
|
@ -635,7 +635,7 @@ void StorageMergeTree::loadMutations()
|
||||
}
|
||||
|
||||
std::shared_ptr<StorageMergeTree::MergeMutateSelectedEntry> StorageMergeTree::selectPartsToMerge(
|
||||
const StorageMetadataPtr & metadata_snapshot, bool aggressive, const String & partition_id, bool final, String * out_disable_reason, TableLockHolder & /* table_lock_holder */)
|
||||
const StorageMetadataPtr & metadata_snapshot, bool aggressive, const String & partition_id, bool final, String * out_disable_reason, TableLockHolder & /* table_lock_holder */, bool optimize_skip_merged_partitions, SelectPartsDecision * select_decision_out)
|
||||
{
|
||||
std::unique_lock lock(currently_processing_in_background_mutex);
|
||||
auto data_settings = getSettings();
|
||||
@ -659,7 +659,7 @@ std::shared_ptr<StorageMergeTree::MergeMutateSelectedEntry> StorageMergeTree::se
|
||||
&& getCurrentMutationVersion(left, lock) == getCurrentMutationVersion(right, lock);
|
||||
};
|
||||
|
||||
bool selected = false;
|
||||
SelectPartsDecision select_decision = SelectPartsDecision::CANNOT_SELECT;
|
||||
|
||||
if (partition_id.empty())
|
||||
{
|
||||
@ -671,7 +671,7 @@ std::shared_ptr<StorageMergeTree::MergeMutateSelectedEntry> StorageMergeTree::se
|
||||
/// possible.
|
||||
if (max_source_parts_size > 0)
|
||||
{
|
||||
selected = merger_mutator.selectPartsToMerge(
|
||||
select_decision = merger_mutator.selectPartsToMerge(
|
||||
future_part,
|
||||
aggressive,
|
||||
max_source_parts_size,
|
||||
@ -687,13 +687,13 @@ std::shared_ptr<StorageMergeTree::MergeMutateSelectedEntry> StorageMergeTree::se
|
||||
while (true)
|
||||
{
|
||||
UInt64 disk_space = getStoragePolicy()->getMaxUnreservedFreeSpace();
|
||||
selected = merger_mutator.selectAllPartsToMergeWithinPartition(
|
||||
future_part, disk_space, can_merge, partition_id, final, out_disable_reason);
|
||||
select_decision = merger_mutator.selectAllPartsToMergeWithinPartition(
|
||||
future_part, disk_space, can_merge, partition_id, final, metadata_snapshot, out_disable_reason, optimize_skip_merged_partitions);
|
||||
|
||||
/// If final - we will wait for currently processing merges to finish and continue.
|
||||
/// TODO Respect query settings for timeout
|
||||
if (final
|
||||
&& !selected
|
||||
&& select_decision != SelectPartsDecision::SELECTED
|
||||
&& !currently_merging_mutating_parts.empty()
|
||||
&& out_disable_reason
|
||||
&& out_disable_reason->empty())
|
||||
@ -713,7 +713,12 @@ std::shared_ptr<StorageMergeTree::MergeMutateSelectedEntry> StorageMergeTree::se
|
||||
}
|
||||
}
|
||||
|
||||
if (!selected)
|
||||
/// In case of final we need to know the decision of select in StorageMergeTree::merge
|
||||
/// to treat NOTHING_TO_MERGE as successful merge (otherwise optimize final will be uncompleted)
|
||||
if (select_decision_out)
|
||||
*select_decision_out = select_decision;
|
||||
|
||||
if (select_decision != SelectPartsDecision::SELECTED)
|
||||
{
|
||||
if (out_disable_reason)
|
||||
{
|
||||
@ -736,12 +741,20 @@ bool StorageMergeTree::merge(
|
||||
const String & partition_id,
|
||||
bool final,
|
||||
bool deduplicate,
|
||||
String * out_disable_reason)
|
||||
String * out_disable_reason,
|
||||
bool optimize_skip_merged_partitions)
|
||||
{
|
||||
auto table_lock_holder = lockForShare(RWLockImpl::NO_QUERY, getSettings()->lock_acquire_timeout_for_background_operations);
|
||||
auto metadata_snapshot = getInMemoryMetadataPtr();
|
||||
|
||||
auto merge_mutate_entry = selectPartsToMerge(metadata_snapshot, aggressive, partition_id, final, out_disable_reason, table_lock_holder);
|
||||
SelectPartsDecision select_decision;
|
||||
|
||||
auto merge_mutate_entry = selectPartsToMerge(metadata_snapshot, aggressive, partition_id, final, out_disable_reason, table_lock_holder, optimize_skip_merged_partitions, &select_decision);
|
||||
|
||||
/// If there is nothing to merge then we treat this merge as successful (needed for optimize final optimization)
|
||||
if (select_decision == SelectPartsDecision::NOTHING_TO_MERGE)
|
||||
return true;
|
||||
|
||||
if (!merge_mutate_entry)
|
||||
return false;
|
||||
|
||||
@ -1036,7 +1049,7 @@ bool StorageMergeTree::optimize(
|
||||
|
||||
for (const String & partition_id : partition_ids)
|
||||
{
|
||||
if (!merge(true, partition_id, true, deduplicate, &disable_reason))
|
||||
if (!merge(true, partition_id, true, deduplicate, &disable_reason, context.getSettingsRef().optimize_skip_merged_partitions))
|
||||
{
|
||||
constexpr const char * message = "Cannot OPTIMIZE table: {}";
|
||||
if (disable_reason.empty())
|
||||
@ -1055,7 +1068,7 @@ bool StorageMergeTree::optimize(
|
||||
if (partition)
|
||||
partition_id = getPartitionIDFromQuery(partition, context);
|
||||
|
||||
if (!merge(true, partition_id, final, deduplicate, &disable_reason))
|
||||
if (!merge(true, partition_id, final, deduplicate, &disable_reason, context.getSettingsRef().optimize_skip_merged_partitions))
|
||||
{
|
||||
constexpr const char * message = "Cannot OPTIMIZE table: {}";
|
||||
if (disable_reason.empty())
|
||||
|
@ -132,7 +132,7 @@ private:
|
||||
* If aggressive - when selects parts don't takes into account their ratio size and novelty (used for OPTIMIZE query).
|
||||
* Returns true if merge is finished successfully.
|
||||
*/
|
||||
bool merge(bool aggressive, const String & partition_id, bool final, bool deduplicate, String * out_disable_reason = nullptr);
|
||||
bool merge(bool aggressive, const String & partition_id, bool final, bool deduplicate, String * out_disable_reason = nullptr, bool optimize_skip_merged_partitions = false);
|
||||
|
||||
ActionLock stopMergesAndWait();
|
||||
|
||||
@ -174,7 +174,15 @@ private:
|
||||
{}
|
||||
};
|
||||
|
||||
std::shared_ptr<MergeMutateSelectedEntry> selectPartsToMerge(const StorageMetadataPtr & metadata_snapshot, bool aggressive, const String & partition_id, bool final, String * disable_reason, TableLockHolder & table_lock_holder);
|
||||
std::shared_ptr<MergeMutateSelectedEntry> selectPartsToMerge(
|
||||
const StorageMetadataPtr & metadata_snapshot,
|
||||
bool aggressive,
|
||||
const String & partition_id,
|
||||
bool final,
|
||||
String * disable_reason,
|
||||
TableLockHolder & table_lock_holder,
|
||||
bool optimize_skip_merged_partitions = false,
|
||||
SelectPartsDecision * select_decision_out = nullptr);
|
||||
bool mergeSelectedParts(const StorageMetadataPtr & metadata_snapshot, bool deduplicate, MergeMutateSelectedEntry & entry, TableLockHolder & table_lock_holder);
|
||||
|
||||
std::shared_ptr<MergeMutateSelectedEntry> selectPartsToMutate(const StorageMetadataPtr & metadata_snapshot, String * disable_reason, TableLockHolder & table_lock_holder);
|
||||
|
@ -2752,7 +2752,7 @@ void StorageReplicatedMergeTree::mergeSelectingTask()
|
||||
future_merged_part.uuid = UUIDHelpers::generateV4();
|
||||
|
||||
if (max_source_parts_size_for_merge > 0 &&
|
||||
merger_mutator.selectPartsToMerge(future_merged_part, false, max_source_parts_size_for_merge, merge_pred, merge_with_ttl_allowed, nullptr))
|
||||
merger_mutator.selectPartsToMerge(future_merged_part, false, max_source_parts_size_for_merge, merge_pred, merge_with_ttl_allowed, nullptr) == SelectPartsDecision::SELECTED)
|
||||
{
|
||||
create_result = createLogEntryToMergeParts(
|
||||
zookeeper,
|
||||
@ -3891,6 +3891,7 @@ bool StorageReplicatedMergeTree::optimize(
|
||||
};
|
||||
|
||||
const auto storage_settings_ptr = getSettings();
|
||||
auto metadata_snapshot = getInMemoryMetadataPtr();
|
||||
|
||||
if (!partition && final)
|
||||
{
|
||||
@ -3913,13 +3914,14 @@ bool StorageReplicatedMergeTree::optimize(
|
||||
ReplicatedMergeTreeMergePredicate can_merge = queue.getMergePredicate(zookeeper);
|
||||
|
||||
FutureMergedMutatedPart future_merged_part;
|
||||
|
||||
if (storage_settings.get()->assign_part_uuids)
|
||||
future_merged_part.uuid = UUIDHelpers::generateV4();
|
||||
|
||||
bool selected = merger_mutator.selectAllPartsToMergeWithinPartition(
|
||||
future_merged_part, disk_space, can_merge, partition_id, true, nullptr);
|
||||
SelectPartsDecision select_decision = merger_mutator.selectAllPartsToMergeWithinPartition(
|
||||
future_merged_part, disk_space, can_merge, partition_id, true, metadata_snapshot, nullptr, query_context.getSettingsRef().optimize_skip_merged_partitions);
|
||||
|
||||
if (!selected)
|
||||
if (select_decision != SelectPartsDecision::SELECTED)
|
||||
break;
|
||||
|
||||
ReplicatedMergeTreeLogEntryData merge_entry;
|
||||
@ -3955,22 +3957,26 @@ bool StorageReplicatedMergeTree::optimize(
|
||||
future_merged_part.uuid = UUIDHelpers::generateV4();
|
||||
|
||||
String disable_reason;
|
||||
bool selected = false;
|
||||
SelectPartsDecision select_decision = SelectPartsDecision::CANNOT_SELECT;
|
||||
|
||||
if (!partition)
|
||||
{
|
||||
selected = merger_mutator.selectPartsToMerge(
|
||||
select_decision = merger_mutator.selectPartsToMerge(
|
||||
future_merged_part, true, storage_settings_ptr->max_bytes_to_merge_at_max_space_in_pool, can_merge, false, &disable_reason);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
UInt64 disk_space = getStoragePolicy()->getMaxUnreservedFreeSpace();
|
||||
String partition_id = getPartitionIDFromQuery(partition, query_context);
|
||||
selected = merger_mutator.selectAllPartsToMergeWithinPartition(
|
||||
future_merged_part, disk_space, can_merge, partition_id, final, &disable_reason);
|
||||
select_decision = merger_mutator.selectAllPartsToMergeWithinPartition(
|
||||
future_merged_part, disk_space, can_merge, partition_id, final, metadata_snapshot, &disable_reason, query_context.getSettingsRef().optimize_skip_merged_partitions);
|
||||
}
|
||||
|
||||
if (!selected)
|
||||
/// If there is nothing to merge then we treat this merge as successful (needed for optimize final optimization)
|
||||
if (select_decision == SelectPartsDecision::NOTHING_TO_MERGE)
|
||||
break;
|
||||
|
||||
if (select_decision != SelectPartsDecision::SELECTED)
|
||||
{
|
||||
constexpr const char * message_fmt = "Cannot select parts for optimization: {}";
|
||||
if (disable_reason.empty())
|
||||
@ -4566,19 +4572,19 @@ StorageReplicatedMergeTree::allocateBlockNumber(
|
||||
}
|
||||
|
||||
|
||||
Strings StorageReplicatedMergeTree::waitForAllReplicasToProcessLogEntry(
|
||||
const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active)
|
||||
Strings StorageReplicatedMergeTree::waitForAllTableReplicasToProcessLogEntry(
|
||||
const String & table_zookeeper_path, const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active)
|
||||
{
|
||||
LOG_DEBUG(log, "Waiting for all replicas to process {}", entry.znode_name);
|
||||
|
||||
auto zookeeper = getZooKeeper();
|
||||
Strings replicas = zookeeper->getChildren(zookeeper_path + "/replicas");
|
||||
Strings replicas = zookeeper->getChildren(table_zookeeper_path + "/replicas");
|
||||
Strings unwaited;
|
||||
for (const String & replica : replicas)
|
||||
{
|
||||
if (wait_for_non_active || zookeeper->exists(zookeeper_path + "/replicas/" + replica + "/is_active"))
|
||||
if (wait_for_non_active || zookeeper->exists(table_zookeeper_path + "/replicas/" + replica + "/is_active"))
|
||||
{
|
||||
if (!waitForReplicaToProcessLogEntry(replica, entry, wait_for_non_active))
|
||||
if (!waitForTableReplicaToProcessLogEntry(table_zookeeper_path, replica, entry, wait_for_non_active))
|
||||
unwaited.push_back(replica);
|
||||
}
|
||||
else
|
||||
@ -4592,8 +4598,15 @@ Strings StorageReplicatedMergeTree::waitForAllReplicasToProcessLogEntry(
|
||||
}
|
||||
|
||||
|
||||
bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(
|
||||
const String & replica, const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active)
|
||||
Strings StorageReplicatedMergeTree::waitForAllReplicasToProcessLogEntry(
|
||||
const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active)
|
||||
{
|
||||
return waitForAllTableReplicasToProcessLogEntry(zookeeper_path, entry, wait_for_non_active);
|
||||
}
|
||||
|
||||
|
||||
bool StorageReplicatedMergeTree::waitForTableReplicaToProcessLogEntry(
|
||||
const String & table_zookeeper_path, const String & replica, const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active)
|
||||
{
|
||||
String entry_str = entry.toString();
|
||||
String log_node_name;
|
||||
@ -4619,7 +4632,7 @@ bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(
|
||||
const auto & stop_waiting = [&]()
|
||||
{
|
||||
bool stop_waiting_itself = waiting_itself && is_dropped;
|
||||
bool stop_waiting_non_active = !wait_for_non_active && !getZooKeeper()->exists(zookeeper_path + "/replicas/" + replica + "/is_active");
|
||||
bool stop_waiting_non_active = !wait_for_non_active && !getZooKeeper()->exists(table_zookeeper_path + "/replicas/" + replica + "/is_active");
|
||||
return stop_waiting_itself || stop_waiting_non_active;
|
||||
};
|
||||
constexpr auto event_wait_timeout_ms = 1000;
|
||||
@ -4639,7 +4652,7 @@ bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(
|
||||
{
|
||||
zkutil::EventPtr event = std::make_shared<Poco::Event>();
|
||||
|
||||
String log_pointer = getZooKeeper()->get(zookeeper_path + "/replicas/" + replica + "/log_pointer", nullptr, event);
|
||||
String log_pointer = getZooKeeper()->get(table_zookeeper_path + "/replicas/" + replica + "/log_pointer", nullptr, event);
|
||||
if (!log_pointer.empty() && parse<UInt64>(log_pointer) > log_index)
|
||||
break;
|
||||
|
||||
@ -4655,9 +4668,9 @@ bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(
|
||||
* looking for a node with the same content. And if we do not find it - then the replica has already taken this entry in its queue.
|
||||
*/
|
||||
|
||||
String log_pointer = getZooKeeper()->get(zookeeper_path + "/replicas/" + replica + "/log_pointer");
|
||||
String log_pointer = getZooKeeper()->get(table_zookeeper_path + "/replicas/" + replica + "/log_pointer");
|
||||
|
||||
Strings log_entries = getZooKeeper()->getChildren(zookeeper_path + "/log");
|
||||
Strings log_entries = getZooKeeper()->getChildren(table_zookeeper_path + "/log");
|
||||
UInt64 log_index = 0;
|
||||
bool found = false;
|
||||
|
||||
@ -4669,7 +4682,7 @@ bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(
|
||||
continue;
|
||||
|
||||
String log_entry_str;
|
||||
bool exists = getZooKeeper()->tryGet(zookeeper_path + "/log/" + log_entry_name, log_entry_str);
|
||||
bool exists = getZooKeeper()->tryGet(table_zookeeper_path + "/log/" + log_entry_name, log_entry_str);
|
||||
if (exists && entry_str == log_entry_str)
|
||||
{
|
||||
found = true;
|
||||
@ -4687,7 +4700,7 @@ bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(
|
||||
{
|
||||
zkutil::EventPtr event = std::make_shared<Poco::Event>();
|
||||
|
||||
String log_pointer_new = getZooKeeper()->get(zookeeper_path + "/replicas/" + replica + "/log_pointer", nullptr, event);
|
||||
String log_pointer_new = getZooKeeper()->get(table_zookeeper_path + "/replicas/" + replica + "/log_pointer", nullptr, event);
|
||||
if (!log_pointer_new.empty() && parse<UInt64>(log_pointer_new) > log_index)
|
||||
break;
|
||||
|
||||
@ -4711,13 +4724,13 @@ bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(
|
||||
* Therefore, we search by comparing the content.
|
||||
*/
|
||||
|
||||
Strings queue_entries = getZooKeeper()->getChildren(zookeeper_path + "/replicas/" + replica + "/queue");
|
||||
Strings queue_entries = getZooKeeper()->getChildren(table_zookeeper_path + "/replicas/" + replica + "/queue");
|
||||
String queue_entry_to_wait_for;
|
||||
|
||||
for (const String & entry_name : queue_entries)
|
||||
{
|
||||
String queue_entry_str;
|
||||
bool exists = getZooKeeper()->tryGet(zookeeper_path + "/replicas/" + replica + "/queue/" + entry_name, queue_entry_str);
|
||||
bool exists = getZooKeeper()->tryGet(table_zookeeper_path + "/replicas/" + replica + "/queue/" + entry_name, queue_entry_str);
|
||||
if (exists && queue_entry_str == entry_str)
|
||||
{
|
||||
queue_entry_to_wait_for = entry_name;
|
||||
@ -4735,12 +4748,19 @@ bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(
|
||||
LOG_DEBUG(log, "Waiting for {} to disappear from {} queue", queue_entry_to_wait_for, replica);
|
||||
|
||||
/// Third - wait until the entry disappears from the replica queue or replica become inactive.
|
||||
String path_to_wait_on = zookeeper_path + "/replicas/" + replica + "/queue/" + queue_entry_to_wait_for;
|
||||
String path_to_wait_on = table_zookeeper_path + "/replicas/" + replica + "/queue/" + queue_entry_to_wait_for;
|
||||
|
||||
return getZooKeeper()->waitForDisappear(path_to_wait_on, stop_waiting);
|
||||
}
|
||||
|
||||
|
||||
bool StorageReplicatedMergeTree::waitForReplicaToProcessLogEntry(
|
||||
const String & replica, const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active)
|
||||
{
|
||||
return waitForTableReplicaToProcessLogEntry(zookeeper_path, replica, entry, wait_for_non_active);
|
||||
}
|
||||
|
||||
|
||||
void StorageReplicatedMergeTree::getStatus(Status & res, bool with_zk_fields)
|
||||
{
|
||||
auto zookeeper = tryGetZooKeeper();
|
||||
|
@ -537,12 +537,16 @@ private:
|
||||
* NOTE: This method must be called without table lock held.
|
||||
* Because it effectively waits for other thread that usually has to also acquire a lock to proceed and this yields deadlock.
|
||||
* TODO: There are wrong usages of this method that are not fixed yet.
|
||||
*
|
||||
* One method for convenient use on current table, another for waiting on foregin shards.
|
||||
*/
|
||||
Strings waitForAllTableReplicasToProcessLogEntry(const String & table_zookeeper_path, const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active = true);
|
||||
Strings waitForAllReplicasToProcessLogEntry(const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active = true);
|
||||
|
||||
/** Wait until the specified replica executes the specified action from the log.
|
||||
* NOTE: See comment about locks above.
|
||||
*/
|
||||
bool waitForTableReplicaToProcessLogEntry(const String & table_zookeeper_path, const String & replica_name, const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active = true);
|
||||
bool waitForReplicaToProcessLogEntry(const String & replica_name, const ReplicatedMergeTreeLogEntryData & entry, bool wait_for_non_active = true);
|
||||
|
||||
/// Throw an exception if the table is readonly.
|
||||
|
@ -3,11 +3,16 @@ import string
|
||||
import threading
|
||||
import time
|
||||
from multiprocessing.dummy import Pool
|
||||
from helpers.test_tools import assert_logs_contain_with_retry
|
||||
|
||||
import pytest
|
||||
from helpers.client import QueryRuntimeException
|
||||
from helpers.cluster import ClickHouseCluster
|
||||
|
||||
# FIXME: each sleep(1) is a time bomb, and not only this cause false positive
|
||||
# it also makes the test not reliable (i.e. assertions may be wrong, due timing issues)
|
||||
# Seems that some SYSTEM query should be added to wait those things insteadof sleep.
|
||||
|
||||
cluster = ClickHouseCluster(__file__)
|
||||
|
||||
node1 = cluster.add_instance('node1',
|
||||
@ -58,6 +63,14 @@ def check_used_disks_with_retry(node, table_name, expected_disks, retries):
|
||||
time.sleep(0.5)
|
||||
return False
|
||||
|
||||
# Use unique table name for flaky checker, that run tests multiple times
|
||||
def unique_table_name(base_name):
|
||||
return f'{base_name}_{int(time.time())}'
|
||||
|
||||
def wait_parts_mover(node, table, *args, **kwargs):
|
||||
# wait for MergeTreePartsMover
|
||||
assert_logs_contain_with_retry(node, f'default.{table}.*Removed part from old location', *args, **kwargs)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name,engine,alter", [
|
||||
("mt_test_rule_with_invalid_destination", "MergeTree()", 0),
|
||||
@ -68,6 +81,8 @@ def check_used_disks_with_retry(node, table_name, expected_disks, retries):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_rule_with_invalid_destination', '1')", 1),
|
||||
])
|
||||
def test_rule_with_invalid_destination(started_cluster, name, engine, alter):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
def get_command(x, policy):
|
||||
x = x or ""
|
||||
@ -129,6 +144,8 @@ def test_rule_with_invalid_destination(started_cluster, name, engine, alter):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_inserts_to_disk_work', '1')", 1),
|
||||
])
|
||||
def test_inserts_to_disk_work(started_cluster, name, engine, positive):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -164,6 +181,8 @@ def test_inserts_to_disk_work(started_cluster, name, engine, positive):
|
||||
"ReplicatedMergeTree('/clickhouse/test_moves_work_after_storage_policy_change', '1')"),
|
||||
])
|
||||
def test_moves_work_after_storage_policy_change(started_cluster, name, engine):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -184,10 +203,6 @@ def test_moves_work_after_storage_policy_change(started_cluster, name, engine):
|
||||
wait_expire_1 = 12
|
||||
wait_expire_2 = 4
|
||||
time_1 = time.time() + wait_expire_1
|
||||
time_2 = time.time() + wait_expire_1 + wait_expire_2
|
||||
|
||||
wait_expire_1_thread = threading.Thread(target=time.sleep, args=(wait_expire_1,))
|
||||
wait_expire_1_thread.start()
|
||||
|
||||
data = [] # 10MB in total
|
||||
for i in range(10):
|
||||
@ -197,8 +212,7 @@ def test_moves_work_after_storage_policy_change(started_cluster, name, engine):
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {"jbod1"}
|
||||
|
||||
wait_expire_1_thread.join()
|
||||
time.sleep(wait_expire_2 / 2)
|
||||
wait_parts_mover(node1, name, retry_count=40)
|
||||
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {"external"}
|
||||
@ -218,6 +232,8 @@ def test_moves_work_after_storage_policy_change(started_cluster, name, engine):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_moves_to_disk_work', '1')", 1),
|
||||
])
|
||||
def test_moves_to_disk_work(started_cluster, name, engine, positive):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -230,7 +246,7 @@ def test_moves_to_disk_work(started_cluster, name, engine, positive):
|
||||
""".format(name=name, engine=engine))
|
||||
|
||||
wait_expire_1 = 12
|
||||
wait_expire_2 = 4
|
||||
wait_expire_2 = 20
|
||||
time_1 = time.time() + wait_expire_1
|
||||
time_2 = time.time() + wait_expire_1 + wait_expire_2
|
||||
|
||||
@ -264,6 +280,8 @@ def test_moves_to_disk_work(started_cluster, name, engine, positive):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_moves_to_volume_work', '1')"),
|
||||
])
|
||||
def test_moves_to_volume_work(started_cluster, name, engine):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -280,9 +298,6 @@ def test_moves_to_volume_work(started_cluster, name, engine):
|
||||
wait_expire_1 = 10
|
||||
time_1 = time.time() + wait_expire_1
|
||||
|
||||
wait_expire_1_thread = threading.Thread(target=time.sleep, args=(wait_expire_1,))
|
||||
wait_expire_1_thread.start()
|
||||
|
||||
for p in range(2):
|
||||
data = [] # 10MB in total
|
||||
for i in range(5):
|
||||
@ -295,8 +310,7 @@ def test_moves_to_volume_work(started_cluster, name, engine):
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {'jbod1', 'jbod2'}
|
||||
|
||||
wait_expire_1_thread.join()
|
||||
time.sleep(1)
|
||||
wait_parts_mover(node1, name, retry_count=40)
|
||||
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {"external"}
|
||||
@ -316,6 +330,8 @@ def test_moves_to_volume_work(started_cluster, name, engine):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_inserts_to_volume_work', '1')", 1),
|
||||
])
|
||||
def test_inserts_to_volume_work(started_cluster, name, engine, positive):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -355,6 +371,8 @@ def test_inserts_to_volume_work(started_cluster, name, engine, positive):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_moves_to_disk_eventually_work', '1')"),
|
||||
])
|
||||
def test_moves_to_disk_eventually_work(started_cluster, name, engine):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
name_temp = name + "_temp"
|
||||
|
||||
@ -395,7 +413,8 @@ def test_moves_to_disk_eventually_work(started_cluster, name, engine):
|
||||
|
||||
node1.query("DROP TABLE {} NO DELAY".format(name_temp))
|
||||
|
||||
time.sleep(2)
|
||||
wait_parts_mover(node1, name)
|
||||
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {"jbod2"}
|
||||
|
||||
@ -407,7 +426,7 @@ def test_moves_to_disk_eventually_work(started_cluster, name, engine):
|
||||
|
||||
|
||||
def test_replicated_download_ttl_info(started_cluster):
|
||||
name = "test_replicated_ttl_info"
|
||||
name = unique_table_name("test_replicated_ttl_info")
|
||||
engine = "ReplicatedMergeTree('/clickhouse/test_replicated_download_ttl_info', '{replica}')"
|
||||
try:
|
||||
for i, node in enumerate((node1, node2), start=1):
|
||||
@ -426,6 +445,7 @@ def test_replicated_download_ttl_info(started_cluster):
|
||||
node2.query("INSERT INTO {} (s1, d1) VALUES (randomPrintableASCII(1024*1024), toDateTime({}))".format(name, time.time() - 100))
|
||||
|
||||
assert set(get_used_disks_for_table(node2, name)) == {"external"}
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
assert node1.query("SELECT count() FROM {}".format(name)).splitlines() == ["1"]
|
||||
@ -448,6 +468,8 @@ def test_replicated_download_ttl_info(started_cluster):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_merges_to_disk_work', '1')", 1),
|
||||
])
|
||||
def test_merges_to_disk_work(started_cluster, name, engine, positive):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -463,7 +485,7 @@ def test_merges_to_disk_work(started_cluster, name, engine, positive):
|
||||
node1.query("SYSTEM STOP MOVES {}".format(name))
|
||||
|
||||
wait_expire_1 = 16
|
||||
wait_expire_2 = 4
|
||||
wait_expire_2 = 20
|
||||
time_1 = time.time() + wait_expire_1
|
||||
time_2 = time.time() + wait_expire_1 + wait_expire_2
|
||||
|
||||
@ -490,7 +512,6 @@ def test_merges_to_disk_work(started_cluster, name, engine, positive):
|
||||
node1.query("SYSTEM START MERGES {}".format(name))
|
||||
node1.query("OPTIMIZE TABLE {}".format(name))
|
||||
|
||||
time.sleep(1)
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {"external" if positive else "jbod1"}
|
||||
assert "1" == node1.query(
|
||||
@ -508,6 +529,8 @@ def test_merges_to_disk_work(started_cluster, name, engine, positive):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_merges_with_full_disk_work', '1')"),
|
||||
])
|
||||
def test_merges_with_full_disk_work(started_cluster, name, engine):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
name_temp = name + "_temp"
|
||||
|
||||
@ -581,6 +604,8 @@ def test_merges_with_full_disk_work(started_cluster, name, engine):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_merges_work', '1')", 1),
|
||||
])
|
||||
def test_moves_after_merges_work(started_cluster, name, engine, positive):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -593,7 +618,7 @@ def test_moves_after_merges_work(started_cluster, name, engine, positive):
|
||||
""".format(name=name, engine=engine))
|
||||
|
||||
wait_expire_1 = 16
|
||||
wait_expire_2 = 4
|
||||
wait_expire_2 = 20
|
||||
time_1 = time.time() + wait_expire_1
|
||||
time_2 = time.time() + wait_expire_1 + wait_expire_2
|
||||
|
||||
@ -610,7 +635,6 @@ def test_moves_after_merges_work(started_cluster, name, engine, positive):
|
||||
"INSERT INTO {} (s1, d1) VALUES {}".format(name, ",".join(["(" + ",".join(x) + ")" for x in data])))
|
||||
|
||||
node1.query("OPTIMIZE TABLE {}".format(name))
|
||||
time.sleep(1)
|
||||
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {"jbod1"}
|
||||
@ -644,6 +668,8 @@ def test_moves_after_merges_work(started_cluster, name, engine, positive):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_moves_after_alter_work', '1')", 1, "TO DISK 'external'"),
|
||||
])
|
||||
def test_ttls_do_not_work_after_alter(started_cluster, name, engine, positive, bar):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -683,6 +709,8 @@ def test_ttls_do_not_work_after_alter(started_cluster, name, engine, positive, b
|
||||
"ReplicatedMergeTree('/clickhouse/test_materialize_ttl_in_partition', '1')"),
|
||||
])
|
||||
def test_materialize_ttl_in_partition(started_cluster, name, engine):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -702,8 +730,6 @@ def test_materialize_ttl_in_partition(started_cluster, name, engine):
|
||||
node1.query(
|
||||
"INSERT INTO {} (p1, s1, d1) VALUES {}".format(name, ",".join(["(" + ",".join(x) + ")" for x in data])))
|
||||
|
||||
time.sleep(0.5)
|
||||
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {"jbod1"}
|
||||
|
||||
@ -713,7 +739,7 @@ def test_materialize_ttl_in_partition(started_cluster, name, engine):
|
||||
d1 TO DISK 'external' SETTINGS materialize_ttl_after_modify = 0
|
||||
""".format(name=name))
|
||||
|
||||
time.sleep(0.5)
|
||||
time.sleep(3)
|
||||
|
||||
used_disks = get_used_disks_for_table(node1, name)
|
||||
assert set(used_disks) == {"jbod1"}
|
||||
@ -728,7 +754,7 @@ def test_materialize_ttl_in_partition(started_cluster, name, engine):
|
||||
MATERIALIZE TTL IN PARTITION 4
|
||||
""".format(name=name))
|
||||
|
||||
time.sleep(0.5)
|
||||
time.sleep(3)
|
||||
|
||||
used_disks_sets = []
|
||||
for i in range(len(data)):
|
||||
@ -751,6 +777,8 @@ def test_materialize_ttl_in_partition(started_cluster, name, engine):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_alter_multiple_ttls_negative', '1')", False),
|
||||
])
|
||||
def test_alter_multiple_ttls(started_cluster, name, engine, positive):
|
||||
name = unique_table_name(name)
|
||||
|
||||
"""Copyright 2019, Altinity LTD
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -845,6 +873,8 @@ limitations under the License."""
|
||||
"ReplicatedMergeTree('/clickhouse/concurrently_altering_ttl_replicated_mt', '1')",),
|
||||
])
|
||||
def test_concurrent_alter_with_ttl_move(started_cluster, name, engine):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -951,6 +981,8 @@ def test_concurrent_alter_with_ttl_move(started_cluster, name, engine):
|
||||
("test_double_move_while_select_positive", 1),
|
||||
])
|
||||
def test_double_move_while_select(started_cluster, name, positive):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
@ -990,7 +1022,7 @@ def test_double_move_while_select(started_cluster, name, positive):
|
||||
node1.query(
|
||||
"INSERT INTO {name} VALUES (4, randomPrintableASCII(9*1024*1024))".format(name=name))
|
||||
|
||||
time.sleep(1)
|
||||
wait_parts_mover(node1, name, retry_count=40)
|
||||
|
||||
# If SELECT locked old part on external, move shall fail.
|
||||
assert node1.query(
|
||||
@ -1014,6 +1046,8 @@ def test_double_move_while_select(started_cluster, name, positive):
|
||||
"ReplicatedMergeTree('/clickhouse/replicated_test_alter_with_merge_work', '1')", 1),
|
||||
])
|
||||
def test_alter_with_merge_work(started_cluster, name, engine, positive):
|
||||
name = unique_table_name(name)
|
||||
|
||||
"""Copyright 2019, Altinity LTD
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -1103,6 +1137,8 @@ limitations under the License."""
|
||||
("replicated_mt_test_disabled_ttl_move_on_insert_work", "VOLUME", "ReplicatedMergeTree('/clickhouse/replicated_test_disabled_ttl_move_on_insert_work', '1')"),
|
||||
])
|
||||
def test_disabled_ttl_move_on_insert(started_cluster, name, dest_type, engine):
|
||||
name = unique_table_name(name)
|
||||
|
||||
try:
|
||||
node1.query("""
|
||||
CREATE TABLE {name} (
|
||||
|
22
tests/performance/countMatches.xml
Normal file
22
tests/performance/countMatches.xml
Normal file
@ -0,0 +1,22 @@
|
||||
<test>
|
||||
<preconditions>
|
||||
<table_exists>test.hits</table_exists>
|
||||
</preconditions>
|
||||
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatches(URL, 'yandex'))</query>
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatches(URL, 'yandex|google'))</query>
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatches(URL, '(\\w+=\\w+)'))</query>
|
||||
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatches(URL, 'yandex')) SETTINGS max_threads=2</query>
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatches(URL, 'yandex|google')) SETTINGS max_threads=2</query>
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatches(URL, '(\\w+=\\w+)')) SETTINGS max_threads=2</query>
|
||||
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatchesCaseInsensitive(URL, 'yandex')) SETTINGS max_threads=2</query>
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatchesCaseInsensitive(URL, 'yandex|google')) SETTINGS max_threads=2</query>
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatchesCaseInsensitive(URL, '(\\w+=\\w+)')) SETTINGS max_threads=2</query>
|
||||
|
||||
<!-- Another variant of case-insensitivity -->
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatches(URL, '(?i)yandex')) SETTINGS max_threads=2</query>
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatches(URL, '(?i)yandex|google')) SETTINGS max_threads=2</query>
|
||||
<query>SELECT count() FROM test.hits WHERE NOT ignore(countMatches(URL, '(?i)(\\w+=\\w+)')) SETTINGS max_threads=2</query>
|
||||
</test>
|
@ -50,6 +50,8 @@
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
***ipv4 trie dict mask***
|
||||
1
|
||||
1
|
||||
@ -111,6 +113,16 @@
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
***ipv6 trie dict***
|
||||
1
|
||||
1
|
||||
@ -418,8 +430,3 @@
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
1
|
||||
|
@ -95,6 +95,10 @@ SELECT 11212 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'asn', tuple(I
|
||||
|
||||
SELECT 11211 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'asn', tuple(IPv4StringToNum('202.79.32.2')));
|
||||
|
||||
-- check that dictionary works with aliased types `IPv4` and `IPv6`
|
||||
SELECT 11211 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'asn', tuple(toIPv4('202.79.32.2')));
|
||||
SELECT 11212 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'asn', tuple(toIPv6('::ffff:101.79.55.22')));
|
||||
|
||||
CREATE TABLE database_for_dict.table_from_ipv4_trie_dict
|
||||
(
|
||||
prefix String,
|
||||
@ -217,6 +221,17 @@ SELECT 18 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4
|
||||
SELECT 19 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.255.128.255')));
|
||||
SELECT 20 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.255.255.128')));
|
||||
|
||||
SELECT 3 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7f00:0')));
|
||||
SELECT 4 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7f00:1')));
|
||||
SELECT 3 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7f00:7f')));
|
||||
SELECT 2 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7f00:ff7f')));
|
||||
SELECT 15 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:7f7f')));
|
||||
SELECT 16 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:8009')));
|
||||
SELECT 16 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:807f')));
|
||||
SELECT 18 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:800a')));
|
||||
SELECT 19 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:80ff')));
|
||||
SELECT 20 == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:ff80')));
|
||||
|
||||
SELECT 1 == dictHas('database_for_dict.dict_ipv4_trie', tuple(IPv4StringToNum('127.0.0.0')));
|
||||
SELECT 1 == dictHas('database_for_dict.dict_ipv4_trie', tuple(IPv4StringToNum('127.0.0.1')));
|
||||
SELECT 1 == dictHas('database_for_dict.dict_ipv4_trie', tuple(IPv4StringToNum('127.0.0.127')));
|
||||
@ -446,7 +461,7 @@ FROM VALUES ('number UInt32', 5, 13, 24, 48, 49, 99, 127);
|
||||
|
||||
INSERT INTO database_for_dict.table_ip_trie VALUES ('101.79.55.22', 'JA');
|
||||
|
||||
INSERT INTO database_for_dict.table_ipv4_trie
|
||||
INSERT INTO database_for_dict.table_ip_trie
|
||||
SELECT
|
||||
'255.255.255.255/' || toString(number) AS prefix,
|
||||
toString(number) AS val
|
||||
@ -607,27 +622,20 @@ SELECT '99' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv6
|
||||
SELECT '127' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv6StringToNum('ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffe')));
|
||||
SELECT '127' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv6StringToNum('ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff')));
|
||||
|
||||
|
||||
SELECT '3' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.0.0.0')));
|
||||
SELECT '4' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.0.0.1')));
|
||||
SELECT '3' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.0.0.127')));
|
||||
SELECT '2' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.0.255.127')));
|
||||
SELECT '15' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.255.127.127')));
|
||||
SELECT '16' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.255.128.9')));
|
||||
SELECT '16' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.255.128.127')));
|
||||
SELECT '18' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.255.128.10')));
|
||||
SELECT '19' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.255.128.255')));
|
||||
SELECT '20' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv4StringToNum('127.255.255.128')));
|
||||
|
||||
SELECT '3' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7f00:0')));
|
||||
SELECT '4' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7f00:1')));
|
||||
SELECT '3' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7f00:7f')));
|
||||
SELECT '2' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7f00:ff7f')));
|
||||
SELECT '15' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:7f7f')));
|
||||
SELECT '16' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:8009')));
|
||||
SELECT '16' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:807f')));
|
||||
SELECT '18' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:800a')));
|
||||
SELECT '19' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:80ff')));
|
||||
SELECT '20' == dictGetUInt32('database_for_dict.dict_ipv4_trie', 'val', tuple(IPv6StringToNum('::ffff:7fff:ff80')));
|
||||
SELECT '' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('0.0.0.0')));
|
||||
SELECT '' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('128.0.0.0')));
|
||||
SELECT '' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('240.0.0.0')));
|
||||
SELECT '5' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('248.0.0.0')));
|
||||
SELECT '5' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('252.0.0.0')));
|
||||
SELECT '5' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.240.0.0')));
|
||||
SELECT '13' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.248.0.0')));
|
||||
SELECT '13' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.252.0.0')));
|
||||
SELECT '13' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.255.254.0')));
|
||||
SELECT '24' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.255.255.0')));
|
||||
SELECT '24' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.255.255.128')));
|
||||
SELECT '24' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.255.255.248')));
|
||||
SELECT '30' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.255.255.252')));
|
||||
SELECT '30' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.255.255.254')));
|
||||
SELECT '30' == dictGetString('database_for_dict.dict_ip_trie', 'val', tuple(IPv4StringToNum('255.255.255.255')));
|
||||
|
||||
DROP DATABASE IF EXISTS database_for_dict;
|
||||
|
@ -0,0 +1,2 @@
|
||||
optimize_final 200001 1 1
|
||||
optimize_final 202001 1 1
|
@ -0,0 +1,20 @@
|
||||
DROP TABLE IF EXISTS optimize_final;
|
||||
|
||||
SET optimize_skip_merged_partitions=1;
|
||||
|
||||
CREATE TABLE optimize_final(t DateTime, x Int32) ENGINE = MergeTree() PARTITION BY toYYYYMM(t) ORDER BY x;
|
||||
|
||||
INSERT INTO optimize_final SELECT toDate('2020-01-01'), number FROM numbers(5);
|
||||
INSERT INTO optimize_final SELECT toDate('2020-01-01'), number + 5 FROM numbers(5);
|
||||
|
||||
OPTIMIZE TABLE optimize_final FINAL;
|
||||
|
||||
INSERT INTO optimize_final SELECT toDate('2000-01-01'), number FROM numbers(5);
|
||||
INSERT INTO optimize_final SELECT toDate('2000-01-01'), number + 5 FROM numbers(5);
|
||||
|
||||
OPTIMIZE TABLE optimize_final FINAL;
|
||||
|
||||
SELECT table, partition, active, level from system.parts where table = 'optimize_final' and active = 1;
|
||||
|
||||
DROP TABLE optimize_final;
|
||||
|
24
tests/queries/0_stateless/01595_countMatches.reference
Normal file
24
tests/queries/0_stateless/01595_countMatches.reference
Normal file
@ -0,0 +1,24 @@
|
||||
basic
|
||||
0
|
||||
0
|
||||
0
|
||||
1
|
||||
case sensitive
|
||||
2
|
||||
1
|
||||
2
|
||||
2
|
||||
2
|
||||
2
|
||||
4
|
||||
4
|
||||
case insensitive
|
||||
2
|
||||
1
|
||||
2
|
||||
2
|
||||
2
|
||||
2
|
||||
4
|
||||
4
|
||||
errors
|
29
tests/queries/0_stateless/01595_countMatches.sql
Normal file
29
tests/queries/0_stateless/01595_countMatches.sql
Normal file
@ -0,0 +1,29 @@
|
||||
select 'basic';
|
||||
select countMatches('', 'foo');
|
||||
select countMatches('foo', '');
|
||||
-- simply stop if zero bytes was processed
|
||||
select countMatches('foo', '[f]{0}');
|
||||
-- but this is ok
|
||||
select countMatches('foo', '[f]{0}foo');
|
||||
|
||||
select 'case sensitive';
|
||||
select countMatches('foobarfoo', 'foo');
|
||||
select countMatches('foobarfoo', 'foo.*');
|
||||
select countMatches('oooo', 'oo');
|
||||
select countMatches(concat(toString(number), 'foofoo'), 'foo') from numbers(2);
|
||||
select countMatches('foobarbazfoobarbaz', 'foo(bar)(?:baz|)');
|
||||
select countMatches('foo.com bar.com baz.com bam.com', '([^. ]+)\.([^. ]+)');
|
||||
select countMatches('foo.com@foo.com bar.com@foo.com baz.com@foo.com bam.com@foo.com', '([^. ]+)\.([^. ]+)@([^. ]+)\.([^. ]+)');
|
||||
|
||||
select 'case insensitive';
|
||||
select countMatchesCaseInsensitive('foobarfoo', 'FOo');
|
||||
select countMatchesCaseInsensitive('foobarfoo', 'FOo.*');
|
||||
select countMatchesCaseInsensitive('oooo', 'Oo');
|
||||
select countMatchesCaseInsensitive(concat(toString(number), 'Foofoo'), 'foo') from numbers(2);
|
||||
select countMatchesCaseInsensitive('foOBarBAZfoobarbaz', 'foo(bar)(?:baz|)');
|
||||
select countMatchesCaseInsensitive('foo.com BAR.COM baz.com bam.com', '([^. ]+)\.([^. ]+)');
|
||||
select countMatchesCaseInsensitive('foo.com@foo.com bar.com@foo.com BAZ.com@foo.com bam.com@foo.com', '([^. ]+)\.([^. ]+)@([^. ]+)\.([^. ]+)');
|
||||
|
||||
select 'errors';
|
||||
select countMatches(1, 'foo') from numbers(1); -- { serverError 43; }
|
||||
select countMatches('foobarfoo', toString(number)) from numbers(1); -- { serverError 44; }
|
@ -72,6 +72,94 @@ If you want to run only a single test such as the `/clickhouse/rbac/syntax/grant
|
||||
|
||||
For more information, please see [Filtering](https://testflows.com/handbook/#Filtering) section in the [TestFlows Handbook].
|
||||
|
||||
## How To Debug Why Test Failed
|
||||
|
||||
### Step 1: find which tests failed
|
||||
|
||||
If [TestFlows] check does not pass you should look at the end of the `test_run.txt.out.log` to find the list
|
||||
of failing tests. For example,
|
||||
|
||||
```bash
|
||||
clickhouse_testflows_tests_volume
|
||||
Start tests
|
||||
➤ Dec 02,2020 22:22:24 /clickhouse
|
||||
...
|
||||
Failing
|
||||
|
||||
✘ [ Fail ] /clickhouse/rbac/syntax/grant privilege/grant privileges/privilege='SELECT', on=('db0.table0', 'db0.*', '*.*', 'tb0', '*'), allow_column=True, allow_introspection=False
|
||||
✘ [ Fail ] /clickhouse/rbac/syntax/grant privilege/grant privileges
|
||||
✘ [ Fail ] /clickhouse/rbac/syntax/grant privilege
|
||||
✘ [ Fail ] /clickhouse/rbac/syntax
|
||||
✘ [ Fail ] /clickhouse/rbac
|
||||
✘ [ Fail ] /clickhouse
|
||||
```
|
||||
|
||||
In this case the failing test is
|
||||
|
||||
```
|
||||
/clickhouse/rbac/syntax/grant privilege/grant privileges/privilege='SELECT', on=('db0.table0', 'db0.*', '*.*', 'tb0', '*'), allow_column=True, allow_introspection=False
|
||||
```
|
||||
|
||||
while the others
|
||||
|
||||
```
|
||||
✘ [ Fail ] /clickhouse/rbac/syntax/grant privilege/grant privileges
|
||||
✘ [ Fail ] /clickhouse/rbac/syntax/grant privilege
|
||||
✘ [ Fail ] /clickhouse/rbac/syntax
|
||||
✘ [ Fail ] /clickhouse/rbac
|
||||
✘ [ Fail ] /clickhouse
|
||||
```
|
||||
|
||||
failed because the first fail gets "bubble-up" the test execution tree all the way to the top level test which is the
|
||||
`/clickhouse`.
|
||||
|
||||
### Step 2: download `test.log` that contains all raw messages
|
||||
|
||||
You need to download the `test.log` that contains all raw messages.
|
||||
|
||||
### Step 3: get messages for the failing test
|
||||
|
||||
Once you know the name of the failing test and you have the `test.log` that contains all the raw messages
|
||||
for all the tests, you can use `tfs show test messages` command.
|
||||
|
||||
> You get the `tfs` command by installing [TestFlows].
|
||||
|
||||
For example,
|
||||
|
||||
```bash
|
||||
cat test.log | tfs show test messages "/clickhouse/rbac/syntax/grant privilege/grant privileges/privilege='SELECT', on=\('db0.table0', 'db0.\*', '\*.\*', 'tb0', '\*'\), allow_column=True, allow_introspection=False"
|
||||
```
|
||||
|
||||
> Note: that characters that are treated as special in extended regular expressions need to be escaped. In this case
|
||||
> we have to escape the `*`, `(`, and the `)` characters in the test name.
|
||||
|
||||
### Step 4: working with the `test.log`
|
||||
|
||||
You can use the `test.log` with many of the commands provided by the
|
||||
`tfs` utility.
|
||||
|
||||
> See `tfs --help` for more information.
|
||||
|
||||
For example, you can get a list of failing tests from the `test.log` using the
|
||||
`tfs show fails` command as follows
|
||||
|
||||
```bash
|
||||
$ cat test.log | tfs show fails
|
||||
```
|
||||
|
||||
or get the results using the `tfs show results` command as follows
|
||||
|
||||
```bash
|
||||
$ cat test.log | tfs show results
|
||||
```
|
||||
|
||||
or you can transform the log to see only the new fails using the
|
||||
`tfs transform fail --new` command as follows
|
||||
|
||||
```bash
|
||||
$ cat test.log | tfs transform fails --new
|
||||
```
|
||||
|
||||
[Python 3]: https://www.python.org/
|
||||
[Ubuntu]: https://ubuntu.com/
|
||||
[TestFlows]: https://testflows.com
|
||||
|
@ -24,6 +24,7 @@ issue_17146 = "https://github.com/ClickHouse/ClickHouse/issues/17146"
|
||||
issue_17147 = "https://github.com/ClickHouse/ClickHouse/issues/17147"
|
||||
issue_17653 = "https://github.com/ClickHouse/ClickHouse/issues/17653"
|
||||
issue_17655 = "https://github.com/ClickHouse/ClickHouse/issues/17655"
|
||||
issue_17766 = "https://github.com/ClickHouse/ClickHouse/issues/17766"
|
||||
|
||||
xfails = {
|
||||
"syntax/show create quota/I show create quota current":
|
||||
@ -112,6 +113,8 @@ xfails = {
|
||||
[(Fail, issue_17147)],
|
||||
"privileges/show dictionaries/:/check privilege/:/exists/EXISTS with privilege":
|
||||
[(Fail, issue_17655)],
|
||||
"privileges/public tables/query log":
|
||||
[(Fail, issue_17766)]
|
||||
}
|
||||
|
||||
xflags = {
|
||||
|
@ -354,13 +354,14 @@
|
||||
* 5.2.8.296 [RQ.SRS-006.RBAC.RowPolicy.ShowRowPolicies.Syntax](#rqsrs-006rbacrowpolicyshowrowpoliciessyntax)
|
||||
* 5.2.9 [Table Privileges](#table-privileges)
|
||||
* 5.2.9.1 [RQ.SRS-006.RBAC.Table.PublicTables](#rqsrs-006rbactablepublictables)
|
||||
* 5.2.9.2 [Distributed Tables](#distributed-tables)
|
||||
* 5.2.9.2.1 [RQ.SRS-006.RBAC.Table.DistributedTable.Create](#rqsrs-006rbactabledistributedtablecreate)
|
||||
* 5.2.9.2.2 [RQ.SRS-006.RBAC.Table.DistributedTable.Select](#rqsrs-006rbactabledistributedtableselect)
|
||||
* 5.2.9.2.3 [RQ.SRS-006.RBAC.Table.DistributedTable.Insert](#rqsrs-006rbactabledistributedtableinsert)
|
||||
* 5.2.9.2.4 [RQ.SRS-006.RBAC.Table.DistributedTable.SpecialTables](#rqsrs-006rbactabledistributedtablespecialtables)
|
||||
* 5.2.9.2.5 [RQ.SRS-006.RBAC.Table.DistributedTable.LocalUser](#rqsrs-006rbactabledistributedtablelocaluser)
|
||||
* 5.2.9.2.6 [RQ.SRS-006.RBAC.Table.DistributedTable.SameUserDifferentNodesDifferentPrivileges](#rqsrs-006rbactabledistributedtablesameuserdifferentnodesdifferentprivileges)
|
||||
* 5.2.9.2 [RQ.SRS-006.RBAC.Table.QueryLog](#rqsrs-006rbactablequerylog)
|
||||
* 5.2.9.3 [Distributed Tables](#distributed-tables)
|
||||
* 5.2.9.3.1 [RQ.SRS-006.RBAC.Table.DistributedTable.Create](#rqsrs-006rbactabledistributedtablecreate)
|
||||
* 5.2.9.3.2 [RQ.SRS-006.RBAC.Table.DistributedTable.Select](#rqsrs-006rbactabledistributedtableselect)
|
||||
* 5.2.9.3.3 [RQ.SRS-006.RBAC.Table.DistributedTable.Insert](#rqsrs-006rbactabledistributedtableinsert)
|
||||
* 5.2.9.3.4 [RQ.SRS-006.RBAC.Table.DistributedTable.SpecialTables](#rqsrs-006rbactabledistributedtablespecialtables)
|
||||
* 5.2.9.3.5 [RQ.SRS-006.RBAC.Table.DistributedTable.LocalUser](#rqsrs-006rbactabledistributedtablelocaluser)
|
||||
* 5.2.9.3.6 [RQ.SRS-006.RBAC.Table.DistributedTable.SameUserDifferentNodesDifferentPrivileges](#rqsrs-006rbactabledistributedtablesameuserdifferentnodesdifferentprivileges)
|
||||
* 5.2.10 [Views](#views)
|
||||
* 5.2.10.1 [View](#view)
|
||||
* 5.2.10.1.1 [RQ.SRS-006.RBAC.View](#rqsrs-006rbacview)
|
||||
@ -489,11 +490,43 @@
|
||||
* 5.2.11.48 [RQ.SRS-006.RBAC.Privileges.ShowDictionaries.Query](#rqsrs-006rbacprivilegesshowdictionariesquery)
|
||||
* 5.2.11.49 [RQ.SRS-006.RBAC.Privileges.ShowCreateDictionary](#rqsrs-006rbacprivilegesshowcreatedictionary)
|
||||
* 5.2.11.50 [RQ.SRS-006.RBAC.Privileges.ExistsDictionary](#rqsrs-006rbacprivilegesexistsdictionary)
|
||||
* 5.2.11.51 [Grant Option](#grant-option)
|
||||
* 5.2.11.51.1 [RQ.SRS-006.RBAC.Privileges.GrantOption](#rqsrs-006rbacprivilegesgrantoption)
|
||||
* 5.2.11.52 [RQ.SRS-006.RBAC.Privileges.All](#rqsrs-006rbacprivilegesall)
|
||||
* 5.2.11.53 [RQ.SRS-006.RBAC.Privileges.All.GrantRevoke](#rqsrs-006rbacprivilegesallgrantrevoke)
|
||||
* 5.2.11.54 [RQ.SRS-006.RBAC.Privileges.AdminOption](#rqsrs-006rbacprivilegesadminoption)
|
||||
* 5.2.11.51 [RQ.SRS-006.RBAC.Privileges.CreateUser](#rqsrs-006rbacprivilegescreateuser)
|
||||
* 5.2.11.52 [RQ.SRS-006.RBAC.Privileges.CreateUser.DefaultRole](#rqsrs-006rbacprivilegescreateuserdefaultrole)
|
||||
* 5.2.11.53 [RQ.SRS-006.RBAC.Privileges.AlterUser](#rqsrs-006rbacprivilegesalteruser)
|
||||
* 5.2.11.54 [RQ.SRS-006.RBAC.Privileges.DropUser](#rqsrs-006rbacprivilegesdropuser)
|
||||
* 5.2.11.55 [RQ.SRS-006.RBAC.Privileges.CreateRole](#rqsrs-006rbacprivilegescreaterole)
|
||||
* 5.2.11.56 [RQ.SRS-006.RBAC.Privileges.AlterRole](#rqsrs-006rbacprivilegesalterrole)
|
||||
* 5.2.11.57 [RQ.SRS-006.RBAC.Privileges.DropRole](#rqsrs-006rbacprivilegesdroprole)
|
||||
* 5.2.11.58 [RQ.SRS-006.RBAC.Privileges.CreateRowPolicy](#rqsrs-006rbacprivilegescreaterowpolicy)
|
||||
* 5.2.11.59 [RQ.SRS-006.RBAC.Privileges.AlterRowPolicy](#rqsrs-006rbacprivilegesalterrowpolicy)
|
||||
* 5.2.11.60 [RQ.SRS-006.RBAC.Privileges.DropRowPolicy](#rqsrs-006rbacprivilegesdroprowpolicy)
|
||||
* 5.2.11.61 [RQ.SRS-006.RBAC.Privileges.CreateQuota](#rqsrs-006rbacprivilegescreatequota)
|
||||
* 5.2.11.62 [RQ.SRS-006.RBAC.Privileges.AlterQuota](#rqsrs-006rbacprivilegesalterquota)
|
||||
* 5.2.11.63 [RQ.SRS-006.RBAC.Privileges.DropQuota](#rqsrs-006rbacprivilegesdropquota)
|
||||
* 5.2.11.64 [RQ.SRS-006.RBAC.Privileges.CreateSettingsProfile](#rqsrs-006rbacprivilegescreatesettingsprofile)
|
||||
* 5.2.11.65 [RQ.SRS-006.RBAC.Privileges.AlterSettingsProfile](#rqsrs-006rbacprivilegesaltersettingsprofile)
|
||||
* 5.2.11.66 [RQ.SRS-006.RBAC.Privileges.DropSettingsProfile](#rqsrs-006rbacprivilegesdropsettingsprofile)
|
||||
* 5.2.11.67 [RQ.SRS-006.RBAC.Privileges.RoleAdmin](#rqsrs-006rbacprivilegesroleadmin)
|
||||
* 5.2.11.68 [RQ.SRS-006.RBAC.Privileges.ShowUsers](#rqsrs-006rbacprivilegesshowusers)
|
||||
* 5.2.11.69 [RQ.SRS-006.RBAC.Privileges.ShowUsers.Query](#rqsrs-006rbacprivilegesshowusersquery)
|
||||
* 5.2.11.70 [RQ.SRS-006.RBAC.Privileges.ShowCreateUser](#rqsrs-006rbacprivilegesshowcreateuser)
|
||||
* 5.2.11.71 [RQ.SRS-006.RBAC.Privileges.ShowRoles](#rqsrs-006rbacprivilegesshowroles)
|
||||
* 5.2.11.72 [RQ.SRS-006.RBAC.Privileges.ShowRoles.Query](#rqsrs-006rbacprivilegesshowrolesquery)
|
||||
* 5.2.11.73 [RQ.SRS-006.RBAC.Privileges.ShowCreateRole](#rqsrs-006rbacprivilegesshowcreaterole)
|
||||
* 5.2.11.74 [RQ.SRS-006.RBAC.Privileges.ShowRowPolicies](#rqsrs-006rbacprivilegesshowrowpolicies)
|
||||
* 5.2.11.75 [RQ.SRS-006.RBAC.Privileges.ShowRowPolicies.Query](#rqsrs-006rbacprivilegesshowrowpoliciesquery)
|
||||
* 5.2.11.76 [RQ.SRS-006.RBAC.Privileges.ShowCreateRowPolicy](#rqsrs-006rbacprivilegesshowcreaterowpolicy)
|
||||
* 5.2.11.77 [RQ.SRS-006.RBAC.Privileges.ShowQuotas](#rqsrs-006rbacprivilegesshowquotas)
|
||||
* 5.2.11.78 [RQ.SRS-006.RBAC.Privileges.ShowQuotas.Query](#rqsrs-006rbacprivilegesshowquotasquery)
|
||||
* 5.2.11.79 [RQ.SRS-006.RBAC.Privileges.ShowCreateQuota](#rqsrs-006rbacprivilegesshowcreatequota)
|
||||
* 5.2.11.80 [RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles](#rqsrs-006rbacprivilegesshowsettingsprofiles)
|
||||
* 5.2.11.81 [RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles.Query](#rqsrs-006rbacprivilegesshowsettingsprofilesquery)
|
||||
* 5.2.11.82 [RQ.SRS-006.RBAC.Privileges.ShowCreateSettingsProfile](#rqsrs-006rbacprivilegesshowcreatesettingsprofile)
|
||||
* 5.2.11.83 [Grant Option](#grant-option)
|
||||
* 5.2.11.83.1 [RQ.SRS-006.RBAC.Privileges.GrantOption](#rqsrs-006rbacprivilegesgrantoption)
|
||||
* 5.2.11.84 [RQ.SRS-006.RBAC.Privileges.All](#rqsrs-006rbacprivilegesall)
|
||||
* 5.2.11.85 [RQ.SRS-006.RBAC.Privileges.All.GrantRevoke](#rqsrs-006rbacprivilegesallgrantrevoke)
|
||||
* 5.2.11.86 [RQ.SRS-006.RBAC.Privileges.AdminOption](#rqsrs-006rbacprivilegesadminoption)
|
||||
* 5.2.12 [Required Privileges](#required-privileges)
|
||||
* 5.2.12.1 [RQ.SRS-006.RBAC.RequiredPrivileges.Create](#rqsrs-006rbacrequiredprivilegescreate)
|
||||
* 5.2.12.2 [RQ.SRS-006.RBAC.RequiredPrivileges.Alter](#rqsrs-006rbacrequiredprivilegesalter)
|
||||
@ -2864,6 +2897,11 @@ version: 1.0
|
||||
* system.contributors
|
||||
* system.functions
|
||||
|
||||
##### RQ.SRS-006.RBAC.Table.QueryLog
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL return only queries executed by the user when the user is selecting from system.query_log.
|
||||
|
||||
##### Distributed Tables
|
||||
|
||||
###### RQ.SRS-006.RBAC.Table.DistributedTable.Create
|
||||
@ -3932,6 +3970,199 @@ version: 1.0
|
||||
[ClickHouse] SHALL successfully execute `EXISTS dictionary` statement if and only if the user has **show dictionaries** privilege,
|
||||
or any privilege on the dictionary either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateUser
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE USER` statement if and only if the user has **create user** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateUser.DefaultRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE USER` statement with `DEFAULT ROLE <role>` clause if and only if
|
||||
the user has **create user** privilege and the role with **admin option**, or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterUser
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER USER` statement if and only if the user has **alter user** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropUser
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP USER` statement if and only if the user has **drop user** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE ROLE` statement if and only if the user has **create role** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER ROLE` statement if and only if the user has **alter role** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP ROLE` statement if and only if the user has **drop role** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateRowPolicy
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE ROW POLICY` statement if and only if the user has **create row policy** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterRowPolicy
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER ROW POLICY` statement if and only if the user has **alter row policy** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropRowPolicy
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP ROW POLICY` statement if and only if the user has **drop row policy** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateQuota
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE QUOTA` statement if and only if the user has **create quota** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterQuota
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER QUOTA` statement if and only if the user has **alter quota** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropQuota
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP QUOTA` statement if and only if the user has **drop quota** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateSettingsProfile
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE SETTINGS PROFILE` statement if and only if the user has **create settings profile** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterSettingsProfile
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER SETTINGS PROFILE` statement if and only if the user has **alter settings profile** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropSettingsProfile
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP SETTINGS PROFILE` statement if and only if the user has **drop settings profile** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.RoleAdmin
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute any role grant or revoke by a user with `ROLE ADMIN` privilege.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowUsers
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW USERS` privilege when
|
||||
the user is granted `SHOW USERS`, `SHOW CREATE USER`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowUsers.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW USERS` statement if and only if the user has **show users** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateUser
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE USER` statement if and only if the user has **show create user** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowRoles
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW ROLES` privilege when
|
||||
the user is granted `SHOW ROLES`, `SHOW CREATE ROLE`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowRoles.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW ROLES` statement if and only if the user has **show roles** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE ROLE` statement if and only if the user has **show create role** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowRowPolicies
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW ROW POLICIES` privilege when
|
||||
the user is granted `SHOW ROW POLICIES`, `SHOW POLICIES`, `SHOW CREATE ROW POLICY`,
|
||||
`SHOW CREATE POLICY`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowRowPolicies.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW ROW POLICIES` or `SHOW POLICIES` statement if and only if the user has **show row policies** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateRowPolicy
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE ROW POLICY` or `SHOW CREATE POLICY` statement if and only if the user has **show create row policy** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowQuotas
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW QUOTAS` privilege when
|
||||
the user is granted `SHOW QUOTAS`, `SHOW CREATE QUOTA`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowQuotas.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW QUOTAS` statement if and only if the user has **show quotas** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateQuota
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE QUOTA` statement if and only if the user has **show create quota** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW SETTINGS PROFILES` privilege when
|
||||
the user is granted `SHOW SETTINGS PROFILES`, `SHOW PROFILES`, `SHOW CREATE SETTINGS PROFILE`,
|
||||
`SHOW SETTINGS PROFILE`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW SETTINGS PROFILES` or `SHOW PROFILES` statement if and only if the user has **show settings profiles** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateSettingsProfile
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE SETTINGS PROFILE` or `SHOW CREATE PROFILE` statement if and only if the user has **show create settings profile** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### Grant Option
|
||||
|
||||
###### RQ.SRS-006.RBAC.Privileges.GrantOption
|
||||
|
@ -379,13 +379,14 @@ SRS_006_ClickHouse_Role_Based_Access_Control = Specification(
|
||||
* 5.2.8.296 [RQ.SRS-006.RBAC.RowPolicy.ShowRowPolicies.Syntax](#rqsrs-006rbacrowpolicyshowrowpoliciessyntax)
|
||||
* 5.2.9 [Table Privileges](#table-privileges)
|
||||
* 5.2.9.1 [RQ.SRS-006.RBAC.Table.PublicTables](#rqsrs-006rbactablepublictables)
|
||||
* 5.2.9.2 [Distributed Tables](#distributed-tables)
|
||||
* 5.2.9.2.1 [RQ.SRS-006.RBAC.Table.DistributedTable.Create](#rqsrs-006rbactabledistributedtablecreate)
|
||||
* 5.2.9.2.2 [RQ.SRS-006.RBAC.Table.DistributedTable.Select](#rqsrs-006rbactabledistributedtableselect)
|
||||
* 5.2.9.2.3 [RQ.SRS-006.RBAC.Table.DistributedTable.Insert](#rqsrs-006rbactabledistributedtableinsert)
|
||||
* 5.2.9.2.4 [RQ.SRS-006.RBAC.Table.DistributedTable.SpecialTables](#rqsrs-006rbactabledistributedtablespecialtables)
|
||||
* 5.2.9.2.5 [RQ.SRS-006.RBAC.Table.DistributedTable.LocalUser](#rqsrs-006rbactabledistributedtablelocaluser)
|
||||
* 5.2.9.2.6 [RQ.SRS-006.RBAC.Table.DistributedTable.SameUserDifferentNodesDifferentPrivileges](#rqsrs-006rbactabledistributedtablesameuserdifferentnodesdifferentprivileges)
|
||||
* 5.2.9.2 [RQ.SRS-006.RBAC.Table.QueryLog](#rqsrs-006rbactablequerylog)
|
||||
* 5.2.9.3 [Distributed Tables](#distributed-tables)
|
||||
* 5.2.9.3.1 [RQ.SRS-006.RBAC.Table.DistributedTable.Create](#rqsrs-006rbactabledistributedtablecreate)
|
||||
* 5.2.9.3.2 [RQ.SRS-006.RBAC.Table.DistributedTable.Select](#rqsrs-006rbactabledistributedtableselect)
|
||||
* 5.2.9.3.3 [RQ.SRS-006.RBAC.Table.DistributedTable.Insert](#rqsrs-006rbactabledistributedtableinsert)
|
||||
* 5.2.9.3.4 [RQ.SRS-006.RBAC.Table.DistributedTable.SpecialTables](#rqsrs-006rbactabledistributedtablespecialtables)
|
||||
* 5.2.9.3.5 [RQ.SRS-006.RBAC.Table.DistributedTable.LocalUser](#rqsrs-006rbactabledistributedtablelocaluser)
|
||||
* 5.2.9.3.6 [RQ.SRS-006.RBAC.Table.DistributedTable.SameUserDifferentNodesDifferentPrivileges](#rqsrs-006rbactabledistributedtablesameuserdifferentnodesdifferentprivileges)
|
||||
* 5.2.10 [Views](#views)
|
||||
* 5.2.10.1 [View](#view)
|
||||
* 5.2.10.1.1 [RQ.SRS-006.RBAC.View](#rqsrs-006rbacview)
|
||||
@ -514,11 +515,43 @@ SRS_006_ClickHouse_Role_Based_Access_Control = Specification(
|
||||
* 5.2.11.48 [RQ.SRS-006.RBAC.Privileges.ShowDictionaries.Query](#rqsrs-006rbacprivilegesshowdictionariesquery)
|
||||
* 5.2.11.49 [RQ.SRS-006.RBAC.Privileges.ShowCreateDictionary](#rqsrs-006rbacprivilegesshowcreatedictionary)
|
||||
* 5.2.11.50 [RQ.SRS-006.RBAC.Privileges.ExistsDictionary](#rqsrs-006rbacprivilegesexistsdictionary)
|
||||
* 5.2.11.51 [Grant Option](#grant-option)
|
||||
* 5.2.11.51.1 [RQ.SRS-006.RBAC.Privileges.GrantOption](#rqsrs-006rbacprivilegesgrantoption)
|
||||
* 5.2.11.52 [RQ.SRS-006.RBAC.Privileges.All](#rqsrs-006rbacprivilegesall)
|
||||
* 5.2.11.53 [RQ.SRS-006.RBAC.Privileges.All.GrantRevoke](#rqsrs-006rbacprivilegesallgrantrevoke)
|
||||
* 5.2.11.54 [RQ.SRS-006.RBAC.Privileges.AdminOption](#rqsrs-006rbacprivilegesadminoption)
|
||||
* 5.2.11.51 [RQ.SRS-006.RBAC.Privileges.CreateUser](#rqsrs-006rbacprivilegescreateuser)
|
||||
* 5.2.11.52 [RQ.SRS-006.RBAC.Privileges.CreateUser.DefaultRole](#rqsrs-006rbacprivilegescreateuserdefaultrole)
|
||||
* 5.2.11.53 [RQ.SRS-006.RBAC.Privileges.AlterUser](#rqsrs-006rbacprivilegesalteruser)
|
||||
* 5.2.11.54 [RQ.SRS-006.RBAC.Privileges.DropUser](#rqsrs-006rbacprivilegesdropuser)
|
||||
* 5.2.11.55 [RQ.SRS-006.RBAC.Privileges.CreateRole](#rqsrs-006rbacprivilegescreaterole)
|
||||
* 5.2.11.56 [RQ.SRS-006.RBAC.Privileges.AlterRole](#rqsrs-006rbacprivilegesalterrole)
|
||||
* 5.2.11.57 [RQ.SRS-006.RBAC.Privileges.DropRole](#rqsrs-006rbacprivilegesdroprole)
|
||||
* 5.2.11.58 [RQ.SRS-006.RBAC.Privileges.CreateRowPolicy](#rqsrs-006rbacprivilegescreaterowpolicy)
|
||||
* 5.2.11.59 [RQ.SRS-006.RBAC.Privileges.AlterRowPolicy](#rqsrs-006rbacprivilegesalterrowpolicy)
|
||||
* 5.2.11.60 [RQ.SRS-006.RBAC.Privileges.DropRowPolicy](#rqsrs-006rbacprivilegesdroprowpolicy)
|
||||
* 5.2.11.61 [RQ.SRS-006.RBAC.Privileges.CreateQuota](#rqsrs-006rbacprivilegescreatequota)
|
||||
* 5.2.11.62 [RQ.SRS-006.RBAC.Privileges.AlterQuota](#rqsrs-006rbacprivilegesalterquota)
|
||||
* 5.2.11.63 [RQ.SRS-006.RBAC.Privileges.DropQuota](#rqsrs-006rbacprivilegesdropquota)
|
||||
* 5.2.11.64 [RQ.SRS-006.RBAC.Privileges.CreateSettingsProfile](#rqsrs-006rbacprivilegescreatesettingsprofile)
|
||||
* 5.2.11.65 [RQ.SRS-006.RBAC.Privileges.AlterSettingsProfile](#rqsrs-006rbacprivilegesaltersettingsprofile)
|
||||
* 5.2.11.66 [RQ.SRS-006.RBAC.Privileges.DropSettingsProfile](#rqsrs-006rbacprivilegesdropsettingsprofile)
|
||||
* 5.2.11.67 [RQ.SRS-006.RBAC.Privileges.RoleAdmin](#rqsrs-006rbacprivilegesroleadmin)
|
||||
* 5.2.11.68 [RQ.SRS-006.RBAC.Privileges.ShowUsers](#rqsrs-006rbacprivilegesshowusers)
|
||||
* 5.2.11.69 [RQ.SRS-006.RBAC.Privileges.ShowUsers.Query](#rqsrs-006rbacprivilegesshowusersquery)
|
||||
* 5.2.11.70 [RQ.SRS-006.RBAC.Privileges.ShowCreateUser](#rqsrs-006rbacprivilegesshowcreateuser)
|
||||
* 5.2.11.71 [RQ.SRS-006.RBAC.Privileges.ShowRoles](#rqsrs-006rbacprivilegesshowroles)
|
||||
* 5.2.11.72 [RQ.SRS-006.RBAC.Privileges.ShowRoles.Query](#rqsrs-006rbacprivilegesshowrolesquery)
|
||||
* 5.2.11.73 [RQ.SRS-006.RBAC.Privileges.ShowCreateRole](#rqsrs-006rbacprivilegesshowcreaterole)
|
||||
* 5.2.11.74 [RQ.SRS-006.RBAC.Privileges.ShowRowPolicies](#rqsrs-006rbacprivilegesshowrowpolicies)
|
||||
* 5.2.11.75 [RQ.SRS-006.RBAC.Privileges.ShowRowPolicies.Query](#rqsrs-006rbacprivilegesshowrowpoliciesquery)
|
||||
* 5.2.11.76 [RQ.SRS-006.RBAC.Privileges.ShowCreateRowPolicy](#rqsrs-006rbacprivilegesshowcreaterowpolicy)
|
||||
* 5.2.11.77 [RQ.SRS-006.RBAC.Privileges.ShowQuotas](#rqsrs-006rbacprivilegesshowquotas)
|
||||
* 5.2.11.78 [RQ.SRS-006.RBAC.Privileges.ShowQuotas.Query](#rqsrs-006rbacprivilegesshowquotasquery)
|
||||
* 5.2.11.79 [RQ.SRS-006.RBAC.Privileges.ShowCreateQuota](#rqsrs-006rbacprivilegesshowcreatequota)
|
||||
* 5.2.11.80 [RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles](#rqsrs-006rbacprivilegesshowsettingsprofiles)
|
||||
* 5.2.11.81 [RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles.Query](#rqsrs-006rbacprivilegesshowsettingsprofilesquery)
|
||||
* 5.2.11.82 [RQ.SRS-006.RBAC.Privileges.ShowCreateSettingsProfile](#rqsrs-006rbacprivilegesshowcreatesettingsprofile)
|
||||
* 5.2.11.83 [Grant Option](#grant-option)
|
||||
* 5.2.11.83.1 [RQ.SRS-006.RBAC.Privileges.GrantOption](#rqsrs-006rbacprivilegesgrantoption)
|
||||
* 5.2.11.84 [RQ.SRS-006.RBAC.Privileges.All](#rqsrs-006rbacprivilegesall)
|
||||
* 5.2.11.85 [RQ.SRS-006.RBAC.Privileges.All.GrantRevoke](#rqsrs-006rbacprivilegesallgrantrevoke)
|
||||
* 5.2.11.86 [RQ.SRS-006.RBAC.Privileges.AdminOption](#rqsrs-006rbacprivilegesadminoption)
|
||||
* 5.2.12 [Required Privileges](#required-privileges)
|
||||
* 5.2.12.1 [RQ.SRS-006.RBAC.RequiredPrivileges.Create](#rqsrs-006rbacrequiredprivilegescreate)
|
||||
* 5.2.12.2 [RQ.SRS-006.RBAC.RequiredPrivileges.Alter](#rqsrs-006rbacrequiredprivilegesalter)
|
||||
@ -2889,6 +2922,11 @@ version: 1.0
|
||||
* system.contributors
|
||||
* system.functions
|
||||
|
||||
##### RQ.SRS-006.RBAC.Table.QueryLog
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL return only queries executed by the user when the user is selecting from system.query_log.
|
||||
|
||||
##### Distributed Tables
|
||||
|
||||
###### RQ.SRS-006.RBAC.Table.DistributedTable.Create
|
||||
@ -3957,6 +3995,199 @@ version: 1.0
|
||||
[ClickHouse] SHALL successfully execute `EXISTS dictionary` statement if and only if the user has **show dictionaries** privilege,
|
||||
or any privilege on the dictionary either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateUser
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE USER` statement if and only if the user has **create user** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateUser.DefaultRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE USER` statement with `DEFAULT ROLE <role>` clause if and only if
|
||||
the user has **create user** privilege and the role with **admin option**, or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterUser
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER USER` statement if and only if the user has **alter user** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropUser
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP USER` statement if and only if the user has **drop user** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE ROLE` statement if and only if the user has **create role** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER ROLE` statement if and only if the user has **alter role** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP ROLE` statement if and only if the user has **drop role** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateRowPolicy
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE ROW POLICY` statement if and only if the user has **create row policy** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterRowPolicy
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER ROW POLICY` statement if and only if the user has **alter row policy** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropRowPolicy
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP ROW POLICY` statement if and only if the user has **drop row policy** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateQuota
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE QUOTA` statement if and only if the user has **create quota** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterQuota
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER QUOTA` statement if and only if the user has **alter quota** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropQuota
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP QUOTA` statement if and only if the user has **drop quota** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.CreateSettingsProfile
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `CREATE SETTINGS PROFILE` statement if and only if the user has **create settings profile** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.AlterSettingsProfile
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `ALTER SETTINGS PROFILE` statement if and only if the user has **alter settings profile** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.DropSettingsProfile
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `DROP SETTINGS PROFILE` statement if and only if the user has **drop settings profile** privilege,
|
||||
or either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.RoleAdmin
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute any role grant or revoke by a user with `ROLE ADMIN` privilege.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowUsers
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW USERS` privilege when
|
||||
the user is granted `SHOW USERS`, `SHOW CREATE USER`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowUsers.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW USERS` statement if and only if the user has **show users** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateUser
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE USER` statement if and only if the user has **show create user** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowRoles
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW ROLES` privilege when
|
||||
the user is granted `SHOW ROLES`, `SHOW CREATE ROLE`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowRoles.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW ROLES` statement if and only if the user has **show roles** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateRole
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE ROLE` statement if and only if the user has **show create role** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowRowPolicies
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW ROW POLICIES` privilege when
|
||||
the user is granted `SHOW ROW POLICIES`, `SHOW POLICIES`, `SHOW CREATE ROW POLICY`,
|
||||
`SHOW CREATE POLICY`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowRowPolicies.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW ROW POLICIES` or `SHOW POLICIES` statement if and only if the user has **show row policies** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateRowPolicy
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE ROW POLICY` or `SHOW CREATE POLICY` statement if and only if the user has **show create row policy** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowQuotas
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW QUOTAS` privilege when
|
||||
the user is granted `SHOW QUOTAS`, `SHOW CREATE QUOTA`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowQuotas.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW QUOTAS` statement if and only if the user has **show quotas** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateQuota
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE QUOTA` statement if and only if the user has **show create quota** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully grant `SHOW SETTINGS PROFILES` privilege when
|
||||
the user is granted `SHOW SETTINGS PROFILES`, `SHOW PROFILES`, `SHOW CREATE SETTINGS PROFILE`,
|
||||
`SHOW SETTINGS PROFILE`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles.Query
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW SETTINGS PROFILES` or `SHOW PROFILES` statement if and only if the user has **show settings profiles** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### RQ.SRS-006.RBAC.Privileges.ShowCreateSettingsProfile
|
||||
version: 1.0
|
||||
|
||||
[ClickHouse] SHALL successfully execute `SHOW CREATE SETTINGS PROFILE` or `SHOW CREATE PROFILE` statement if and only if the user has **show create settings profile** privilege,
|
||||
either directly or through a role.
|
||||
|
||||
##### Grant Option
|
||||
|
||||
###### RQ.SRS-006.RBAC.Privileges.GrantOption
|
||||
@ -9065,6 +9296,19 @@ RQ_SRS_006_RBAC_Table_PublicTables = Requirement(
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Table_QueryLog = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Table.QueryLog',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL return only queries executed by the user when the user is selecting from system.query_log.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Table_DistributedTable_Create = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Table.DistributedTable.Create',
|
||||
version='1.0',
|
||||
@ -11033,6 +11277,455 @@ RQ_SRS_006_RBAC_Privileges_ExistsDictionary = Requirement(
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_CreateUser = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.CreateUser',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `CREATE USER` statement if and only if the user has **create user** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_CreateUser_DefaultRole = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.CreateUser.DefaultRole',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `CREATE USER` statement with `DEFAULT ROLE <role>` clause if and only if\n'
|
||||
'the user has **create user** privilege and the role with **admin option**, or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_AlterUser = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.AlterUser',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `ALTER USER` statement if and only if the user has **alter user** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_DropUser = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.DropUser',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `DROP USER` statement if and only if the user has **drop user** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_CreateRole = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.CreateRole',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `CREATE ROLE` statement if and only if the user has **create role** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_AlterRole = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.AlterRole',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `ALTER ROLE` statement if and only if the user has **alter role** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_DropRole = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.DropRole',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `DROP ROLE` statement if and only if the user has **drop role** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_CreateRowPolicy = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.CreateRowPolicy',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `CREATE ROW POLICY` statement if and only if the user has **create row policy** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_AlterRowPolicy = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.AlterRowPolicy',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `ALTER ROW POLICY` statement if and only if the user has **alter row policy** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_DropRowPolicy = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.DropRowPolicy',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `DROP ROW POLICY` statement if and only if the user has **drop row policy** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_CreateQuota = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.CreateQuota',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `CREATE QUOTA` statement if and only if the user has **create quota** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_AlterQuota = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.AlterQuota',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `ALTER QUOTA` statement if and only if the user has **alter quota** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_DropQuota = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.DropQuota',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `DROP QUOTA` statement if and only if the user has **drop quota** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_CreateSettingsProfile = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.CreateSettingsProfile',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `CREATE SETTINGS PROFILE` statement if and only if the user has **create settings profile** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_AlterSettingsProfile = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.AlterSettingsProfile',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `ALTER SETTINGS PROFILE` statement if and only if the user has **alter settings profile** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_DropSettingsProfile = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.DropSettingsProfile',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `DROP SETTINGS PROFILE` statement if and only if the user has **drop settings profile** privilege,\n'
|
||||
'or either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_RoleAdmin = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.RoleAdmin',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute any role grant or revoke by a user with `ROLE ADMIN` privilege.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowUsers = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowUsers',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully grant `SHOW USERS` privilege when\n'
|
||||
'the user is granted `SHOW USERS`, `SHOW CREATE USER`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowUsers_Query = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowUsers.Query',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW USERS` statement if and only if the user has **show users** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateUser = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowCreateUser',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW CREATE USER` statement if and only if the user has **show create user** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowRoles = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowRoles',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully grant `SHOW ROLES` privilege when\n'
|
||||
'the user is granted `SHOW ROLES`, `SHOW CREATE ROLE`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowRoles_Query = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowRoles.Query',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW ROLES` statement if and only if the user has **show roles** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateRole = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowCreateRole',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW CREATE ROLE` statement if and only if the user has **show create role** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowRowPolicies = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowRowPolicies',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully grant `SHOW ROW POLICIES` privilege when\n'
|
||||
'the user is granted `SHOW ROW POLICIES`, `SHOW POLICIES`, `SHOW CREATE ROW POLICY`,\n'
|
||||
'`SHOW CREATE POLICY`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowRowPolicies_Query = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowRowPolicies.Query',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW ROW POLICIES` or `SHOW POLICIES` statement if and only if the user has **show row policies** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateRowPolicy = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowCreateRowPolicy',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW CREATE ROW POLICY` or `SHOW CREATE POLICY` statement if and only if the user has **show create row policy** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowQuotas = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowQuotas',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully grant `SHOW QUOTAS` privilege when\n'
|
||||
'the user is granted `SHOW QUOTAS`, `SHOW CREATE QUOTA`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowQuotas_Query = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowQuotas.Query',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW QUOTAS` statement if and only if the user has **show quotas** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateQuota = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowCreateQuota',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW CREATE QUOTA` statement if and only if the user has **show create quota** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowSettingsProfiles = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully grant `SHOW SETTINGS PROFILES` privilege when\n'
|
||||
'the user is granted `SHOW SETTINGS PROFILES`, `SHOW PROFILES`, `SHOW CREATE SETTINGS PROFILE`,\n'
|
||||
'`SHOW SETTINGS PROFILE`, `SHOW ACCESS`, or `ACCESS MANAGEMENT`.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowSettingsProfiles_Query = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowSettingsProfiles.Query',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW SETTINGS PROFILES` or `SHOW PROFILES` statement if and only if the user has **show settings profiles** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateSettingsProfile = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.ShowCreateSettingsProfile',
|
||||
version='1.0',
|
||||
priority=None,
|
||||
group=None,
|
||||
type=None,
|
||||
uid=None,
|
||||
description=(
|
||||
'[ClickHouse] SHALL successfully execute `SHOW CREATE SETTINGS PROFILE` or `SHOW CREATE PROFILE` statement if and only if the user has **show create settings profile** privilege,\n'
|
||||
'either directly or through a role.\n'
|
||||
'\n'
|
||||
),
|
||||
link=None)
|
||||
|
||||
RQ_SRS_006_RBAC_Privileges_GrantOption = Requirement(
|
||||
name='RQ.SRS-006.RBAC.Privileges.GrantOption',
|
||||
version='1.0',
|
||||
|
137
tests/testflows/rbac/tests/privileges/alter/alter_quota.py
Normal file
137
tests/testflows/rbac/tests/privileges/alter/alter_quota.py
Normal file
@ -0,0 +1,137 @@
|
||||
from contextlib import contextmanager
|
||||
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@contextmanager
|
||||
def quota(node, name):
|
||||
try:
|
||||
with Given("I have a quota"):
|
||||
node.query(f"CREATE QUOTA {name}")
|
||||
|
||||
yield
|
||||
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {name}")
|
||||
|
||||
@TestSuite
|
||||
def alter_quota_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER QUOTA` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=alter_quota, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in alter_quota.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def alter_quota_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER QUOTA` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=alter_quota, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in alter_quota.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("ALTER QUOTA",),
|
||||
])
|
||||
def alter_quota(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `ALTER QUOTA` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("ALTER QUOTA without privilege"):
|
||||
alter_quota_name = f"alter_quota_{getuid()}"
|
||||
|
||||
with quota(node, alter_quota_name):
|
||||
|
||||
with When("I check the user can't alter a quota"):
|
||||
node.query(f"ALTER QUOTA {alter_quota_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("ALTER QUOTA with privilege"):
|
||||
alter_quota_name = f"alter_quota_{getuid()}"
|
||||
|
||||
with quota(node, alter_quota_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a user"):
|
||||
node.query(f"ALTER QUOTA {alter_quota_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("ALTER QUOTA on cluster"):
|
||||
alter_quota_name = f"alter_quota_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a quota on a cluster"):
|
||||
node.query(f"CREATE QUOTA {alter_quota_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a quota"):
|
||||
node.query(f"ALTER QUOTA {alter_quota_name} ON CLUSTER sharded_cluster",
|
||||
settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {alter_quota_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("ALTER QUOTA with revoked privilege"):
|
||||
alter_quota_name = f"alter_quota_{getuid()}"
|
||||
|
||||
with quota(node, alter_quota_name):
|
||||
|
||||
with When(f"I grant {privilege} on the database"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege} on the database"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user can't alter a quota"):
|
||||
node.query(f"ALTER QUOTA {alter_quota_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("alter quota")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_AlterQuota("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of ALTER QUOTA.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=alter_quota_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=alter_quota_granted_via_role, setup=instrument_clickhouse_server_log)
|
118
tests/testflows/rbac/tests/privileges/alter/alter_role.py
Normal file
118
tests/testflows/rbac/tests/privileges/alter/alter_role.py
Normal file
@ -0,0 +1,118 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER ROLE` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=alter_role, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in alter_role.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER ROLE` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=alter_role, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in alter_role.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("ALTER ROLE",),
|
||||
])
|
||||
def alter_role(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `ALTER ROLE` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("ALTER ROLE without privilege"):
|
||||
alter_role_name = f"alter_role_{getuid()}"
|
||||
with role(node, alter_role_name):
|
||||
|
||||
with When("I check the user can't alter a role"):
|
||||
node.query(f"ALTER ROLE {alter_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("ALTER ROLE with privilege"):
|
||||
alter_role_name = f"alter_role_{getuid()}"
|
||||
with role(node, alter_role_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a role"):
|
||||
node.query(f"ALTER ROLE {alter_role_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("ALTER ROLE on cluster"):
|
||||
alter_role_name = f"alter_role_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a role on a cluster"):
|
||||
node.query(f"CREATE ROLE {alter_role_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a role"):
|
||||
node.query(f"ALTER ROLE {alter_role_name} ON CLUSTER sharded_cluster", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP ROLE IF EXISTS {alter_role_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("ALTER ROLE with revoked privilege"):
|
||||
alter_role_name = f"alter_role_{getuid()}"
|
||||
with role(node, alter_role_name):
|
||||
with When(f"I grant {privilege} on the database"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege} on the database"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot alter a role"):
|
||||
node.query(f"ALTER ROLE {alter_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("alter role")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_AlterRole("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of ALTER ROLE.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
144
tests/testflows/rbac/tests/privileges/alter/alter_row_policy.py
Normal file
144
tests/testflows/rbac/tests/privileges/alter/alter_row_policy.py
Normal file
@ -0,0 +1,144 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER ROW POLICY` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=alter_row_policy, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in alter_row_policy.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER ROW POLICY` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=alter_row_policy, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in alter_row_policy.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("ALTER ROW POLICY",),
|
||||
("ALTER POLICY",),
|
||||
])
|
||||
def alter_row_policy(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `ALTER ROW POLICY` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("ALTER ROW POLICY without privilege"):
|
||||
alter_row_policy_name = f"alter_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {alter_row_policy_name} ON {table_name}")
|
||||
|
||||
with When("I check the user can't alter a row policy"):
|
||||
node.query(f"ALTER ROW POLICY {alter_row_policy_name} ON {table_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {alter_row_policy_name} ON {table_name}")
|
||||
|
||||
with Scenario("ALTER ROW POLICY with privilege"):
|
||||
alter_row_policy_name = f"alter_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {alter_row_policy_name} ON {table_name}")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a row policy"):
|
||||
node.query(f"ALTER ROW POLICY {alter_row_policy_name} ON {table_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {alter_row_policy_name} ON {table_name}")
|
||||
|
||||
with Scenario("ALTER ROW POLICY on cluster"):
|
||||
alter_row_policy_name = f"alter_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a row policy on a cluster"):
|
||||
node.query(f"CREATE ROW POLICY {alter_row_policy_name} ON CLUSTER sharded_cluster ON {table_name}")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a row policy"):
|
||||
node.query(f"ALTER ROW POLICY {alter_row_policy_name} ON CLUSTER sharded_cluster ON {table_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {alter_row_policy_name} ON CLUSTER sharded_cluster ON {table_name}")
|
||||
|
||||
with Scenario("ALTER ROW POLICY with revoked privilege"):
|
||||
alter_row_policy_name = f"alter_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {alter_row_policy_name} ON {table_name}")
|
||||
|
||||
with When(f"I grant {privilege} on the database"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege} on the database"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot alter row policy"):
|
||||
node.query(f"ALTER ROW POLICY {alter_row_policy_name} ON {table_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {alter_row_policy_name} ON {table_name}")
|
||||
|
||||
@TestFeature
|
||||
@Name("alter row policy")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_AlterRowPolicy("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of ALTER ROW POLICY.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
@ -0,0 +1,138 @@
|
||||
from contextlib import contextmanager
|
||||
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@contextmanager
|
||||
def settings_profile(node, name):
|
||||
try:
|
||||
with Given("I have a settings_profile"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {name}")
|
||||
|
||||
yield
|
||||
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {name}")
|
||||
|
||||
@TestSuite
|
||||
def alter_settings_profile_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER SETTINGS PROFILE` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=alter_settings_profile, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in alter_settings_profile.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def alter_settings_profile_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER SETTINGS PROFILE` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=alter_settings_profile, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in alter_settings_profile.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("ALTER SETTINGS PROFILE",),
|
||||
("ALTER PROFILE",),
|
||||
])
|
||||
def alter_settings_profile(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `ALTER SETTINGS PROFILE` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("ALTER SETTINGS PROFILE without privilege"):
|
||||
alter_settings_profile_name = f"alter_settings_profile_{getuid()}"
|
||||
|
||||
with settings_profile(node, alter_settings_profile_name):
|
||||
|
||||
with When("I check the user can't alter a settings_profile"):
|
||||
node.query(f"ALTER SETTINGS PROFILE {alter_settings_profile_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("ALTER SETTINGS PROFILE with privilege"):
|
||||
alter_settings_profile_name = f"alter_settings_profile_{getuid()}"
|
||||
|
||||
with settings_profile(node, alter_settings_profile_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a user"):
|
||||
node.query(f"ALTER SETTINGS PROFILE {alter_settings_profile_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("ALTER SETTINGS PROFILE on cluster"):
|
||||
alter_settings_profile_name = f"alter_settings_profile_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a settings_profile on a cluster"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {alter_settings_profile_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a settings_profile"):
|
||||
node.query(f"ALTER SETTINGS PROFILE {alter_settings_profile_name} ON CLUSTER sharded_cluster",
|
||||
settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {alter_settings_profile_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("ALTER SETTINGS PROFILE with revoked privilege"):
|
||||
alter_settings_profile_name = f"alter_settings_profile_{getuid()}"
|
||||
|
||||
with settings_profile(node, alter_settings_profile_name):
|
||||
|
||||
with When(f"I grant {privilege} on the database"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege} on the database"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user can't alter a settings_profile"):
|
||||
node.query(f"ALTER SETTINGS PROFILE {alter_settings_profile_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("alter settings profile")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_AlterSettingsProfile("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of ALTER SETTINGS PROFILE.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=alter_settings_profile_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=alter_settings_profile_granted_via_role, setup=instrument_clickhouse_server_log)
|
118
tests/testflows/rbac/tests/privileges/alter/alter_user.py
Normal file
118
tests/testflows/rbac/tests/privileges/alter/alter_user.py
Normal file
@ -0,0 +1,118 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def alter_user_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER USER` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=alter_user, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in alter_user.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def alter_user_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `ALTER USER` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=alter_user, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in alter_user.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("ALTER USER",),
|
||||
])
|
||||
def alter_user(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `ALTER USER` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("ALTER USER without privilege"):
|
||||
|
||||
alter_user_name = f"alter_user_{getuid()}"
|
||||
with user(node, alter_user_name):
|
||||
|
||||
with When("I check the user can't alter a user"):
|
||||
node.query(f"ALTER USER {alter_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("ALTER USER with privilege"):
|
||||
alter_user_name = f"alter_user_{getuid()}"
|
||||
with user(node, alter_user_name):
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a user"):
|
||||
node.query(f"ALTER USER {alter_user_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("ALTER USER on cluster"):
|
||||
alter_user_name = f"alter_user_{getuid()}"
|
||||
try:
|
||||
with Given("I have a user on a cluster"):
|
||||
node.query(f"CREATE USER {alter_user_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can alter a user"):
|
||||
node.query(f"ALTER USER {alter_user_name} ON CLUSTER sharded_cluster",
|
||||
settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {alter_user_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("ALTER USER with revoked privilege"):
|
||||
alter_user_name = f"alter_user_{getuid()}"
|
||||
with user(node, alter_user_name):
|
||||
with When(f"I grant {privilege} on the database"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege} on the database"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user can't alter a user"):
|
||||
node.query(f"ALTER USER {alter_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("alter user")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_AlterUser("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of ALTER USER.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=alter_user_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=alter_user_granted_via_role, setup=instrument_clickhouse_server_log)
|
128
tests/testflows/rbac/tests/privileges/create/create_quota.py
Normal file
128
tests/testflows/rbac/tests/privileges/create/create_quota.py
Normal file
@ -0,0 +1,128 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE QUOTA` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=create_quota, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in create_quota.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE QUOTA` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=create_quota, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in create_quota.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("CREATE QUOTA",),
|
||||
])
|
||||
def create_quota(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `CREATE QUOTA` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("CREATE QUOTA without privilege"):
|
||||
create_quota_name = f"create_quota_{getuid()}"
|
||||
|
||||
try:
|
||||
with When("I check the user can't create a quota"):
|
||||
node.query(f"CREATE QUOTA {create_quota_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {create_quota_name}")
|
||||
|
||||
with Scenario("CREATE QUOTA with privilege"):
|
||||
create_quota_name = f"create_quota_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a quota"):
|
||||
node.query(f"CREATE QUOTA {create_quota_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {create_quota_name}")
|
||||
|
||||
with Scenario("CREATE QUOTA on cluster"):
|
||||
create_quota_name = f"create_quota_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a quota"):
|
||||
node.query(f"CREATE QUOTA {create_quota_name} ON CLUSTER sharded_cluster", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {create_quota_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("CREATE QUOTA with revoked privilege"):
|
||||
create_quota_name = f"create_quota_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot create a quota"):
|
||||
node.query(f"CREATE QUOTA {create_quota_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {create_quota_name}")
|
||||
|
||||
@TestFeature
|
||||
@Name("create quota")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_CreateQuota("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of CREATE QUOTA.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
128
tests/testflows/rbac/tests/privileges/create/create_role.py
Normal file
128
tests/testflows/rbac/tests/privileges/create/create_role.py
Normal file
@ -0,0 +1,128 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE ROLE` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=create_role, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in create_role.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE ROLE` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=create_role, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in create_role.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("CREATE ROLE",),
|
||||
])
|
||||
def create_role(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `CREATE ROLE` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("CREATE ROLE without privilege"):
|
||||
create_role_name = f"create_role_{getuid()}"
|
||||
|
||||
try:
|
||||
with When("I check the user can't create a role"):
|
||||
node.query(f"CREATE ROLE {create_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the role"):
|
||||
node.query(f"DROP ROLE IF EXISTS {create_role_name}")
|
||||
|
||||
with Scenario("CREATE ROLE with privilege"):
|
||||
create_role_name = f"create_role_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a role"):
|
||||
node.query(f"CREATE ROLE {create_role_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the role"):
|
||||
node.query(f"DROP ROLE IF EXISTS {create_role_name}")
|
||||
|
||||
with Scenario("CREATE ROLE on cluster"):
|
||||
create_role_name = f"create_role_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a role"):
|
||||
node.query(f"CREATE ROLE {create_role_name} ON CLUSTER sharded_cluster", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the role"):
|
||||
node.query(f"DROP ROLE IF EXISTS {create_role_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("CREATE ROLE with revoked privilege"):
|
||||
create_role_name = f"create_role_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot create a role"):
|
||||
node.query(f"CREATE ROLE {create_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the role"):
|
||||
node.query(f"DROP ROLE IF EXISTS {create_role_name}")
|
||||
|
||||
@TestFeature
|
||||
@Name("create role")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_CreateRole("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of CREATE ROLE.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
@ -0,0 +1,133 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE ROW POLICY` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=create_row_policy, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in create_row_policy.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE ROW POLICY` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=create_row_policy, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in create_row_policy.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("CREATE ROW POLICY",),
|
||||
("CREATE POLICY",),
|
||||
])
|
||||
def create_row_policy(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `CREATE ROW POLICY` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("CREATE ROW POLICY without privilege"):
|
||||
create_row_policy_name = f"create_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with When("I check the user can't create a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {create_row_policy_name} ON {table_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {create_row_policy_name} ON {table_name}")
|
||||
|
||||
with Scenario("CREATE ROW POLICY with privilege"):
|
||||
create_row_policy_name = f"create_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {create_row_policy_name} ON {table_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {create_row_policy_name} ON {table_name}")
|
||||
|
||||
with Scenario("CREATE ROW POLICY on cluster"):
|
||||
create_row_policy_name = f"create_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {create_row_policy_name} ON CLUSTER sharded_cluster ON {table_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {create_row_policy_name} ON CLUSTER sharded_cluster ON {table_name}")
|
||||
|
||||
with Scenario("CREATE ROW POLICY with revoked privilege"):
|
||||
create_row_policy_name = f"create_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot create a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {create_row_policy_name} ON {table_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {create_row_policy_name} ON {table_name}")
|
||||
|
||||
@TestFeature
|
||||
@Name("create row policy")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_CreateRowPolicy("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of CREATE ROW POLICY.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
@ -0,0 +1,129 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE SETTINGS PROFILE` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=create_settings_profile, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in create_settings_profile.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE SETTINGS PROFILE` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=create_settings_profile, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in create_settings_profile.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("CREATE SETTINGS PROFILE",),
|
||||
("CREATE PROFILE",),
|
||||
])
|
||||
def create_settings_profile(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `CREATE SETTINGS PROFILE` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("CREATE SETTINGS PROFILE without privilege"):
|
||||
create_settings_profile_name = f"create_settings_profile_{getuid()}"
|
||||
|
||||
try:
|
||||
with When("I check the user can't create a settings_profile"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {create_settings_profile_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {create_settings_profile_name}")
|
||||
|
||||
with Scenario("CREATE SETTINGS PROFILE with privilege"):
|
||||
create_settings_profile_name = f"create_settings_profile_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a settings_profile"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {create_settings_profile_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {create_settings_profile_name}")
|
||||
|
||||
with Scenario("CREATE SETTINGS PROFILE on cluster"):
|
||||
create_settings_profile_name = f"create_settings_profile_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a settings_profile"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {create_settings_profile_name} ON CLUSTER sharded_cluster", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {create_settings_profile_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("CREATE SETTINGS PROFILE with revoked privilege"):
|
||||
create_settings_profile_name = f"create_settings_profile_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot create a settings_profile"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {create_settings_profile_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {create_settings_profile_name}")
|
||||
|
||||
@TestFeature
|
||||
@Name("create settings profile")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_CreateSettingsProfile("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of CREATE SETTINGS PROFILE.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
276
tests/testflows/rbac/tests/privileges/create/create_user.py
Normal file
276
tests/testflows/rbac/tests/privileges/create/create_user.py
Normal file
@ -0,0 +1,276 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def create_user_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE USER` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=create_user, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in create_user.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def create_user_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE USER` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=create_user, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in create_user.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("CREATE USER",),
|
||||
])
|
||||
def create_user(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `CREATE USER` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("CREATE USER without privilege"):
|
||||
create_user_name = f"create_user_{getuid()}"
|
||||
|
||||
try:
|
||||
with When("I check the user can't create a user"):
|
||||
node.query(f"CREATE USER {create_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {create_user_name}")
|
||||
|
||||
with Scenario("CREATE USER with privilege"):
|
||||
create_user_name = f"create_user_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a user"):
|
||||
node.query(f"CREATE USER {create_user_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {create_user_name}")
|
||||
|
||||
with Scenario("CREATE USER on cluster"):
|
||||
create_user_name = f"create_user_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a user"):
|
||||
node.query(f"CREATE USER {create_user_name} ON CLUSTER sharded_cluster",
|
||||
settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {create_user_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("CREATE USER with revoked privilege"):
|
||||
create_user_name = f"create_user_{getuid()}"
|
||||
|
||||
try:
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user can't create a user"):
|
||||
node.query(f"CREATE USER {create_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {create_user_name}")
|
||||
|
||||
@TestSuite
|
||||
def default_role_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE USER` with `DEFAULT ROLE` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(test=default_role, flags=TE)(grant_target_name=user_name, user_name=user_name)
|
||||
|
||||
@TestSuite
|
||||
def default_role_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `CREATE USER` with `DEFAULT ROLE` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(test=default_role, flags=TE)(grant_target_name=role_name, user_name=user_name)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_CreateUser_DefaultRole("1.0"),
|
||||
)
|
||||
def default_role(self, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `CREATE USER` with `DEFAULT ROLE` if and only if the user has
|
||||
`CREATE USER` privilege and the role with `ADMIN OPTION`.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("CREATE USER with DEFAULT ROLE without privilege"):
|
||||
create_user_name = f"create_user_{getuid()}"
|
||||
default_role_name = f"default_role_{getuid()}"
|
||||
|
||||
with role(node, default_role_name):
|
||||
try:
|
||||
with When(f"I grant CREATE USER"):
|
||||
node.query(f"GRANT CREATE USER ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can't create a user"):
|
||||
node.query(f"CREATE USER {create_user_name} DEFAULT ROLE {default_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {create_user_name}")
|
||||
|
||||
with Scenario("CREATE USER with DEFAULT ROLE with role privilege"):
|
||||
create_user_name = f"create_user_{getuid()}"
|
||||
default_role_name = f"default_role_{getuid()}"
|
||||
|
||||
with role(node, default_role_name):
|
||||
try:
|
||||
with When(f"I grant CREATE USER"):
|
||||
node.query(f"GRANT CREATE USER ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I grant the role with ADMIN OPTION"):
|
||||
node.query(f"GRANT {default_role_name} TO {grant_target_name} WITH ADMIN OPTION")
|
||||
|
||||
with Then("I check the user can create a user"):
|
||||
node.query(f"CREATE USER {create_user_name} DEFAULT ROLE {default_role_name}", settings=[("user",user_name)])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {create_user_name}")
|
||||
|
||||
with Scenario("CREATE USER with DEFAULT ROLE on cluster"):
|
||||
create_user_name = f"create_user_{getuid()}"
|
||||
default_role_name = f"default_role_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have role on a cluster"):
|
||||
node.query(f"CREATE ROLE {default_role_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When(f"I grant CREATE USER"):
|
||||
node.query(f"GRANT CREATE USER ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I grant the role with ADMIN OPTION"):
|
||||
node.query(f"GRANT {default_role_name} TO {grant_target_name} WITH ADMIN OPTION")
|
||||
|
||||
with Then("I check the user can create a user"):
|
||||
node.query(f"CREATE USER {create_user_name} ON CLUSTER sharded_cluster DEFAULT ROLE {default_role_name}",
|
||||
settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user", flags=TE):
|
||||
node.query(f"DROP USER IF EXISTS {create_user_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with And("I drop the role from the cluster"):
|
||||
node.query(f"DROP ROLE {default_role_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("CREATE USER with DEFAULT ROLE with revoked role privilege"):
|
||||
create_user_name = f"create_user_{getuid()}"
|
||||
default_role_name = f"default_role_{getuid()}"
|
||||
|
||||
with role(node, default_role_name):
|
||||
try:
|
||||
with When(f"I grant CREATE USER"):
|
||||
node.query(f"GRANT CREATE USER ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I grant the role with ADMIN OPTION"):
|
||||
node.query(f"GRANT {default_role_name} TO {grant_target_name} WITH ADMIN OPTION")
|
||||
|
||||
with And(f"I revoke the role"):
|
||||
node.query(f"REVOKE {default_role_name} FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user can't create a user"):
|
||||
node.query(f"CREATE USER {create_user_name} DEFAULT ROLE {default_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {create_user_name}")
|
||||
|
||||
with Scenario("CREATE USER with DEFAULT ROLE with ACCESS MANAGEMENT privilege"):
|
||||
create_user_name = f"create_user_{getuid()}"
|
||||
default_role_name = f"default_role_{getuid()}"
|
||||
|
||||
with role(node, default_role_name):
|
||||
try:
|
||||
with When(f"I grant ACCESS MANAGEMENT "):
|
||||
node.query(f"GRANT ACCESS MANAGEMENT ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can create a user"):
|
||||
node.query(f"CREATE USER {create_user_name} DEFAULT ROLE {default_role_name}", settings=[("user",user_name)])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {create_user_name}")
|
||||
|
||||
@TestFeature
|
||||
@Name("create user")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_CreateUser("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of CREATE USER.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=create_user_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=create_user_granted_via_role, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=default_role_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=default_role_granted_via_role, setup=instrument_clickhouse_server_log)
|
139
tests/testflows/rbac/tests/privileges/drop/drop_quota.py
Normal file
139
tests/testflows/rbac/tests/privileges/drop/drop_quota.py
Normal file
@ -0,0 +1,139 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `DROP QUOTA` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=drop_quota, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in drop_quota.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `DROP QUOTA` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=drop_quota, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in drop_quota.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("DROP QUOTA",),
|
||||
])
|
||||
def drop_quota(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `DROP QUOTA` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("DROP QUOTA without privilege"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a quota"):
|
||||
node.query(f"CREATE QUOTA {drop_row_policy_name}")
|
||||
|
||||
with When("I check the user can't drop a quota"):
|
||||
node.query(f"DROP QUOTA {drop_row_policy_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {drop_row_policy_name}")
|
||||
|
||||
with Scenario("DROP QUOTA with privilege"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a quota"):
|
||||
node.query(f"CREATE QUOTA {drop_row_policy_name}")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a quota"):
|
||||
node.query(f"DROP QUOTA {drop_row_policy_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {drop_row_policy_name}")
|
||||
|
||||
with Scenario("DROP QUOTA on cluster"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a quota on a cluster"):
|
||||
node.query(f"CREATE QUOTA {drop_row_policy_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a quota"):
|
||||
node.query(f"DROP QUOTA {drop_row_policy_name} ON CLUSTER sharded_cluster", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {drop_row_policy_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("DROP QUOTA with revoked privilege"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a quota"):
|
||||
node.query(f"CREATE QUOTA {drop_row_policy_name}")
|
||||
|
||||
with When(f"I grant {privilege} on the database"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege} on the database"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot drop quota"):
|
||||
node.query(f"DROP QUOTA {drop_row_policy_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {drop_row_policy_name}")
|
||||
|
||||
@TestFeature
|
||||
@Name("drop quota")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_DropQuota("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of DROP QUOTA.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
118
tests/testflows/rbac/tests/privileges/drop/drop_role.py
Normal file
118
tests/testflows/rbac/tests/privileges/drop/drop_role.py
Normal file
@ -0,0 +1,118 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `DROP ROLE` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=drop_role, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in drop_role.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `DROP ROLE` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=drop_role, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in drop_role.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("DROP ROLE",),
|
||||
])
|
||||
def drop_role(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `DROP ROLE` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("DROP ROLE without privilege"):
|
||||
drop_role_name = f"drop_role_{getuid()}"
|
||||
with role(node, drop_role_name):
|
||||
|
||||
with When("I check the user can't drop a role"):
|
||||
node.query(f"DROP ROLE {drop_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("DROP ROLE with privilege"):
|
||||
drop_role_name = f"drop_role_{getuid()}"
|
||||
with role(node, drop_role_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a role"):
|
||||
node.query(f"DROP ROLE {drop_role_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("DROP ROLE on cluster"):
|
||||
drop_role_name = f"drop_role_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a role on a cluster"):
|
||||
node.query(f"CREATE ROLE {drop_role_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a role"):
|
||||
node.query(f"DROP ROLE {drop_role_name} ON CLUSTER sharded_cluster", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP ROLE IF EXISTS {drop_role_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("DROP ROLE with revoked privilege"):
|
||||
drop_role_name = f"drop_role_{getuid()}"
|
||||
with role(node, drop_role_name):
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user can't drop a role"):
|
||||
node.query(f"DROP ROLE {drop_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("drop role")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_DropRole("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of DROP ROLE.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
144
tests/testflows/rbac/tests/privileges/drop/drop_row_policy.py
Normal file
144
tests/testflows/rbac/tests/privileges/drop/drop_row_policy.py
Normal file
@ -0,0 +1,144 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `DROP ROW POLICY` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=drop_row_policy, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in drop_row_policy.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `DROP ROW POLICY` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=drop_row_policy, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in drop_row_policy.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("DROP ROW POLICY",),
|
||||
("DROP POLICY",),
|
||||
])
|
||||
def drop_row_policy(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `DROP ROW POLICY` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("DROP ROW POLICY without privilege"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {drop_row_policy_name} ON {table_name}")
|
||||
|
||||
with When("I check the user can't drop a row policy"):
|
||||
node.query(f"DROP ROW POLICY {drop_row_policy_name} ON {table_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {drop_row_policy_name} ON {table_name}")
|
||||
|
||||
with Scenario("DROP ROW POLICY with privilege"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {drop_row_policy_name} ON {table_name}")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a row policy"):
|
||||
node.query(f"DROP ROW POLICY {drop_row_policy_name} ON {table_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {drop_row_policy_name} ON {table_name}")
|
||||
|
||||
with Scenario("DROP ROW POLICY on cluster"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a row policy on a cluster"):
|
||||
node.query(f"CREATE ROW POLICY {drop_row_policy_name} ON CLUSTER sharded_cluster ON {table_name}")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a row policy"):
|
||||
node.query(f"DROP ROW POLICY {drop_row_policy_name} ON CLUSTER sharded_cluster ON {table_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {drop_row_policy_name} ON CLUSTER sharded_cluster ON {table_name}")
|
||||
|
||||
with Scenario("DROP ROW POLICY with revoked privilege"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
table_name = f"table_name_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {drop_row_policy_name} ON {table_name}")
|
||||
|
||||
with When(f"I grant {privilege} on the database"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege} on the database"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot drop row policy"):
|
||||
node.query(f"DROP ROW POLICY {drop_row_policy_name} ON {table_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {drop_row_policy_name} ON {table_name}")
|
||||
|
||||
@TestFeature
|
||||
@Name("drop row policy")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_DropRowPolicy("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of DROP ROW POLICY.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
@ -0,0 +1,140 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `DROP SETTINGS PROFILE` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=drop_settings_profile, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in drop_settings_profile.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `DROP SETTINGS PROFILE` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=drop_settings_profile, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in drop_settings_profile.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("DROP SETTINGS PROFILE",),
|
||||
("DROP PROFILE",),
|
||||
])
|
||||
def drop_settings_profile(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `DROP SETTINGS PROFILE` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("DROP SETTINGS PROFILE without privilege"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a settings_profile"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {drop_row_policy_name}")
|
||||
|
||||
with When("I check the user can't drop a settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE {drop_row_policy_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {drop_row_policy_name}")
|
||||
|
||||
with Scenario("DROP SETTINGS PROFILE with privilege"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a settings_profile"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {drop_row_policy_name}")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE {drop_row_policy_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {drop_row_policy_name}")
|
||||
|
||||
with Scenario("DROP SETTINGS PROFILE on cluster"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a settings_profile on a cluster"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {drop_row_policy_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE {drop_row_policy_name} ON CLUSTER sharded_cluster", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {drop_row_policy_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("DROP SETTINGS PROFILE with revoked privilege"):
|
||||
drop_row_policy_name = f"drop_row_policy_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a settings_profile"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {drop_row_policy_name}")
|
||||
|
||||
with When(f"I grant {privilege} on the database"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege} on the database"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot drop settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE {drop_row_policy_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {drop_row_policy_name}")
|
||||
|
||||
@TestFeature
|
||||
@Name("drop settings profile")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_DropSettingsProfile("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of DROP SETTINGS PROFILE.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
118
tests/testflows/rbac/tests/privileges/drop/drop_user.py
Normal file
118
tests/testflows/rbac/tests/privileges/drop/drop_user.py
Normal file
@ -0,0 +1,118 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def drop_user_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `DROP USER` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=drop_user, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in drop_user.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def drop_user_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `DROP USER` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=drop_user, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in drop_user.examples
|
||||
], args=Args(name="check privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("DROP USER",),
|
||||
])
|
||||
def drop_user(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `DROP USER` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("DROP USER without privilege"):
|
||||
|
||||
drop_user_name = f"drop_user_{getuid()}"
|
||||
with user(node, drop_user_name):
|
||||
|
||||
with When("I check the user can't drop a user"):
|
||||
node.query(f"DROP USER {drop_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("DROP USER with privilege"):
|
||||
drop_user_name = f"drop_user_{getuid()}"
|
||||
with user(node, drop_user_name):
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a user"):
|
||||
node.query(f"DROP USER {drop_user_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("DROP USER on cluster"):
|
||||
drop_user_name = f"drop_user_{getuid()}"
|
||||
try:
|
||||
with Given("I have a user on a cluster"):
|
||||
node.query(f"CREATE USER {drop_user_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can drop a user"):
|
||||
node.query(f"DROP USER {drop_user_name} ON CLUSTER sharded_cluster",
|
||||
settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP USER IF EXISTS {drop_user_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("DROP USER with revoked privilege"):
|
||||
drop_user_name = f"drop_user_{getuid()}"
|
||||
with user(node, drop_user_name):
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user can't drop a user"):
|
||||
node.query(f"DROP USER {drop_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("drop user")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_DropUser("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of DROP USER.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=drop_user_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=drop_user_granted_via_role, setup=instrument_clickhouse_server_log)
|
@ -20,11 +20,17 @@ def feature(self):
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.optimize", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.kill_query", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.kill_mutation", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.role_admin", "feature"), flags=TE), {})
|
||||
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.show.show_tables", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.show.show_dictionaries", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.show.show_databases", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.show.show_columns", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.show.show_users", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.show.show_roles", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.show.show_quotas", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.show.show_settings_profiles", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.show.show_row_policies", "feature"), flags=TE), {})
|
||||
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_column", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_index", "feature"), flags=TE), {})
|
||||
@ -36,11 +42,21 @@ def feature(self):
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_freeze", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_fetch", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_move", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_user", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_role", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_row_policy", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_quota", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.alter.alter_settings_profile", "feature"), flags=TE), {})
|
||||
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.create.create_database", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.create.create_dictionary", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.create.create_temp_table", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.create.create_table", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.create.create_user", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.create.create_role", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.create.create_row_policy", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.create.create_quota", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.create.create_settings_profile", "feature"), flags=TE), {})
|
||||
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.attach.attach_database", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.attach.attach_dictionary", "feature"), flags=TE), {})
|
||||
@ -50,6 +66,11 @@ def feature(self):
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.drop.drop_database", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.drop.drop_dictionary", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.drop.drop_table", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.drop.drop_user", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.drop.drop_role", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.drop.drop_row_policy", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.drop.drop_quota", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.drop.drop_settings_profile", "feature"), flags=TE), {})
|
||||
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.detach.detach_database", "feature"), flags=TE), {})
|
||||
run_scenario(pool, tasks, Feature(test=load("rbac.tests.privileges.detach.detach_dictionary", "feature"), flags=TE), {})
|
||||
|
@ -30,9 +30,29 @@ def public_tables(self, node=None):
|
||||
with And("I check the user is able to select on system.functions"):
|
||||
node.query("SELECT count(*) FROM system.functions", settings = [("user",user_name)])
|
||||
|
||||
@TestScenario
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Table_QueryLog("1.0"),
|
||||
)
|
||||
def query_log(self, node=None):
|
||||
"""Check that a user with no privilege is only able to see their own queries.
|
||||
"""
|
||||
user_name = f"user_{getuid()}"
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
with Given("I create a query"):
|
||||
node.query("SELECT 1")
|
||||
|
||||
with Then("The user reads system.query_log"):
|
||||
output = node.query("SELECT count() FROM system.query_log", settings = [("user",user_name)]).output
|
||||
assert output == 0, error()
|
||||
|
||||
@TestFeature
|
||||
@Name("public tables")
|
||||
def feature(self, node="clickhouse1"):
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Scenario(run=public_tables, setup=instrument_clickhouse_server_log, flags=TE)
|
||||
Scenario(run=public_tables, setup=instrument_clickhouse_server_log, flags=TE)
|
||||
Scenario(run=query_log, setup=instrument_clickhouse_server_log, flags=TE)
|
119
tests/testflows/rbac/tests/privileges/role_admin.py
Normal file
119
tests/testflows/rbac/tests/privileges/role_admin.py
Normal file
@ -0,0 +1,119 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to grant role with `ROLE ADMIN` privilege granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(test=role_admin, flags=TE)(grant_target_name=user_name, user_name=user_name)
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to grant role with `ROLE ADMIN` privilege granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(test=role_admin, flags=TE)(grant_target_name=role_name, user_name=user_name)
|
||||
|
||||
@TestSuite
|
||||
def role_admin(self, grant_target_name, user_name, node=None):
|
||||
"""Check that user is able to execute to grant roles if and only if they have `ROLE ADMIN`.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("Grant role without privilege"):
|
||||
role_admin_name = f"role_admin_{getuid()}"
|
||||
target_user_name = f"target_user_{getuid()}"
|
||||
|
||||
with user(node, target_user_name), role(node, role_admin_name):
|
||||
|
||||
with When("I check the user can't grant a role"):
|
||||
node.query(f"GRANT {role_admin_name} TO {target_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("Grant role with privilege"):
|
||||
role_admin_name = f"role_admin_{getuid()}"
|
||||
target_user_name = f"target_user_{getuid()}"
|
||||
|
||||
with user(node, target_user_name), role(node, role_admin_name):
|
||||
|
||||
with When(f"I grant ROLE ADMIN"):
|
||||
node.query(f"GRANT ROLE ADMIN ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can grant a role"):
|
||||
node.query(f"GRANT {role_admin_name} TO {target_user_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("Grant role on cluster"):
|
||||
role_admin_name = f"role_admin_{getuid()}"
|
||||
target_user_name = f"target_user_{getuid()}"
|
||||
|
||||
try:
|
||||
with Given("I have a role on a cluster"):
|
||||
node.query(f"CREATE ROLE {role_admin_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with And("I have a user on a cluster"):
|
||||
node.query(f"CREATE USER {target_user_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with When("I grant ROLE ADMIN privilege"):
|
||||
node.query(f"GRANT ROLE ADMIN ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can grant a role"):
|
||||
node.query(f"GRANT {role_admin_name} TO {target_user_name} ON CLUSTER sharded_cluster", settings = [("user", f"{user_name}")])
|
||||
|
||||
finally:
|
||||
with Finally("I drop the user"):
|
||||
node.query(f"DROP ROLE IF EXISTS {role_admin_name} ON CLUSTER sharded_cluster")
|
||||
|
||||
with Scenario("Grant role with revoked privilege"):
|
||||
role_admin_name = f"role_admin_{getuid()}"
|
||||
target_user_name = f"target_user_{getuid()}"
|
||||
|
||||
with user(node, target_user_name), role(node, role_admin_name):
|
||||
|
||||
with When(f"I grant ROLE ADMIN on the database"):
|
||||
node.query(f"GRANT ROLE ADMIN ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke ROLE ADMIN on the database"):
|
||||
node.query(f"REVOKE ROLE ADMIN ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot grant a role"):
|
||||
node.query(f"GRANT {role_admin_name} TO {target_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("role admin")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_RoleAdmin("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of ROLE ADMIN.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
171
tests/testflows/rbac/tests/privileges/show/show_quotas.py
Normal file
171
tests/testflows/rbac/tests/privileges/show/show_quotas.py
Normal file
@ -0,0 +1,171 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@contextmanager
|
||||
def quota(node, name):
|
||||
try:
|
||||
with Given("I have a quota"):
|
||||
node.query(f"CREATE QUOTA {name}")
|
||||
|
||||
yield
|
||||
|
||||
finally:
|
||||
with Finally("I drop the quota"):
|
||||
node.query(f"DROP QUOTA IF EXISTS {name}")
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW QUOTAS` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW QUOTAS` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("SHOW ACCESS",),
|
||||
("SHOW QUOTAS",),
|
||||
("SHOW CREATE QUOTA",),
|
||||
])
|
||||
def check_privilege(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Run checks for commands that require SHOW QUOTAS privilege.
|
||||
"""
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
Suite(test=show_quotas, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
Suite(test=show_create, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowQuotas_Query("1.0"),
|
||||
)
|
||||
def show_quotas(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW QUOTAS` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW QUOTAS without privilege"):
|
||||
|
||||
with When("I check the user can't use SHOW QUOTAS"):
|
||||
node.query(f"SHOW QUOTAS", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW QUOTAS with privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW QUOTAS"):
|
||||
node.query(f"SHOW QUOTAS", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW QUOTAS with revoked privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW QUOTAS"):
|
||||
node.query(f"SHOW QUOTAS", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateQuota("1.0"),
|
||||
)
|
||||
def show_create(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW CREATE QUOTA` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW CREATE QUOTA without privilege"):
|
||||
target_quota_name = f"target_quota_{getuid()}"
|
||||
|
||||
with quota(node, target_quota_name):
|
||||
|
||||
with When("I check the user can't use SHOW CREATE QUOTA"):
|
||||
node.query(f"SHOW CREATE QUOTA {target_quota_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW CREATE QUOTA with privilege"):
|
||||
target_quota_name = f"target_quota_{getuid()}"
|
||||
|
||||
with quota(node, target_quota_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW CREATE QUOTA"):
|
||||
node.query(f"SHOW CREATE QUOTA {target_quota_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW CREATE QUOTA with revoked privilege"):
|
||||
target_quota_name = f"target_quota_{getuid()}"
|
||||
|
||||
with quota(node, target_quota_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW CREATE QUOTA"):
|
||||
node.query(f"SHOW CREATE QUOTA {target_quota_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("show quotas")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowQuotas("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of SHOW QUOTAS.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
159
tests/testflows/rbac/tests/privileges/show/show_roles.py
Normal file
159
tests/testflows/rbac/tests/privileges/show/show_roles.py
Normal file
@ -0,0 +1,159 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW ROLES` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW ROLES` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("SHOW ACCESS",),
|
||||
("SHOW ROLES",),
|
||||
("SHOW CREATE ROLE",),
|
||||
])
|
||||
def check_privilege(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Run checks for commands that require SHOW ROLES privilege.
|
||||
"""
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
Suite(test=show_roles, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
Suite(test=show_create, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowRoles_Query("1.0"),
|
||||
)
|
||||
def show_roles(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW ROLES` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW ROLES without privilege"):
|
||||
|
||||
with When("I check the user can't use SHOW ROLES"):
|
||||
node.query(f"SHOW ROLES", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW ROLES with privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW ROLES"):
|
||||
node.query(f"SHOW ROLES", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW ROLES with revoked privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW ROLES"):
|
||||
node.query(f"SHOW ROLES", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateRole("1.0"),
|
||||
)
|
||||
def show_create(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW CREATE ROLE` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW CREATE ROLE without privilege"):
|
||||
target_role_name = f"target_role_{getuid()}"
|
||||
|
||||
with role(node, target_role_name):
|
||||
|
||||
with When("I check the user can't use SHOW CREATE ROLE"):
|
||||
node.query(f"SHOW CREATE ROLE {target_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW CREATE ROLE with privilege"):
|
||||
target_role_name = f"target_role_{getuid()}"
|
||||
|
||||
with role(node, target_role_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW CREATE ROLE"):
|
||||
node.query(f"SHOW CREATE ROLE {target_role_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW CREATE ROLE with revoked privilege"):
|
||||
target_role_name = f"target_role_{getuid()}"
|
||||
|
||||
with role(node, target_role_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW CREATE ROLE"):
|
||||
node.query(f"SHOW CREATE ROLE {target_role_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("show roles")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowRoles("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of SHOW ROLES.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
176
tests/testflows/rbac/tests/privileges/show/show_row_policies.py
Normal file
176
tests/testflows/rbac/tests/privileges/show/show_row_policies.py
Normal file
@ -0,0 +1,176 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@contextmanager
|
||||
def row_policy(node, name, table):
|
||||
try:
|
||||
with Given("I have a row policy"):
|
||||
node.query(f"CREATE ROW POLICY {name} ON {table}")
|
||||
|
||||
yield
|
||||
|
||||
finally:
|
||||
with Finally("I drop the row policy"):
|
||||
node.query(f"DROP ROW POLICY IF EXISTS {name} ON {table}")
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW ROW POLICIES` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW ROW POLICIES` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("SHOW ACCESS",),
|
||||
("SHOW ROW POLICIES",),
|
||||
("SHOW POLICIES",),
|
||||
("SHOW CREATE ROW POLICY",),
|
||||
("SHOW CREATE POLICY",),
|
||||
])
|
||||
def check_privilege(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Run checks for commands that require SHOW ROW POLICIES privilege.
|
||||
"""
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
Suite(test=show_row_policies, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
Suite(test=show_create, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowRowPolicies_Query("1.0"),
|
||||
)
|
||||
def show_row_policies(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW ROW POLICIES` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW ROW POLICIES without privilege"):
|
||||
|
||||
with When("I check the user can't use SHOW ROW POLICIES"):
|
||||
node.query(f"SHOW ROW POLICIES", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW ROW POLICIES with privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW ROW POLICIES"):
|
||||
node.query(f"SHOW ROW POLICIES", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW ROW POLICIES with revoked privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW ROW POLICIES"):
|
||||
node.query(f"SHOW ROW POLICIES", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateRowPolicy("1.0"),
|
||||
)
|
||||
def show_create(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW CREATE ROW POLICY` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW CREATE ROW POLICY without privilege"):
|
||||
target_row_policy_name = f"target_row_policy_{getuid()}"
|
||||
table_name = f"table_{getuid()}"
|
||||
|
||||
with row_policy(node, target_row_policy_name, table_name):
|
||||
|
||||
with When("I check the user can't use SHOW CREATE ROW POLICY"):
|
||||
node.query(f"SHOW CREATE ROW POLICY {target_row_policy_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW CREATE ROW POLICY with privilege"):
|
||||
target_row_policy_name = f"target_row_policy_{getuid()}"
|
||||
table_name = f"table_{getuid()}"
|
||||
|
||||
with row_policy(node, target_row_policy_name, table_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW CREATE ROW POLICY"):
|
||||
node.query(f"SHOW CREATE ROW POLICY {target_row_policy_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW CREATE ROW POLICY with revoked privilege"):
|
||||
target_row_policy_name = f"target_row_policy_{getuid()}"
|
||||
table_name = f"table_{getuid()}"
|
||||
|
||||
with row_policy(node, target_row_policy_name, table_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW CREATE ROW POLICY"):
|
||||
node.query(f"SHOW CREATE ROW POLICY {target_row_policy_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("show row policies")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowRowPolicies("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of SHOW ROW POLICYS.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
@ -0,0 +1,173 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@contextmanager
|
||||
def settings_profile(node, name):
|
||||
try:
|
||||
with Given("I have a settings_profile"):
|
||||
node.query(f"CREATE SETTINGS PROFILE {name}")
|
||||
|
||||
yield
|
||||
|
||||
finally:
|
||||
with Finally("I drop the settings_profile"):
|
||||
node.query(f"DROP SETTINGS PROFILE IF EXISTS {name}")
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW SETTINGS PROFILES` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW SETTINGS PROFILES` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("SHOW ACCESS",),
|
||||
("SHOW SETTINGS PROFILES",),
|
||||
("SHOW PROFILES",),
|
||||
("SHOW CREATE SETTINGS PROFILE",),
|
||||
("SHOW CREATE PROFILE",),
|
||||
])
|
||||
def check_privilege(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Run checks for commands that require SHOW SETTINGS PROFILES privilege.
|
||||
"""
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
Suite(test=show_settings_profiles, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
Suite(test=show_create, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowSettingsProfiles_Query("1.0"),
|
||||
)
|
||||
def show_settings_profiles(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW SETTINGS PROFILES` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW SETTINGS PROFILES without privilege"):
|
||||
|
||||
with When("I check the user can't use SHOW SETTINGS PROFILES"):
|
||||
node.query(f"SHOW SETTINGS PROFILES", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW SETTINGS PROFILES with privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW SETTINGS PROFILES"):
|
||||
node.query(f"SHOW SETTINGS PROFILES", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW SETTINGS PROFILES with revoked privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW SETTINGS PROFILES"):
|
||||
node.query(f"SHOW SETTINGS PROFILES", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateSettingsProfile("1.0"),
|
||||
)
|
||||
def show_create(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW CREATE SETTINGS PROFILE` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW CREATE SETTINGS PROFILE without privilege"):
|
||||
target_settings_profile_name = f"target_settings_profile_{getuid()}"
|
||||
|
||||
with settings_profile(node, target_settings_profile_name):
|
||||
|
||||
with When("I check the user can't use SHOW CREATE SETTINGS PROFILE"):
|
||||
node.query(f"SHOW CREATE SETTINGS PROFILE {target_settings_profile_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW CREATE SETTINGS PROFILE with privilege"):
|
||||
target_settings_profile_name = f"target_settings_profile_{getuid()}"
|
||||
|
||||
with settings_profile(node, target_settings_profile_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW CREATE SETTINGS PROFILE"):
|
||||
node.query(f"SHOW CREATE SETTINGS PROFILE {target_settings_profile_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW CREATE SETTINGS PROFILE with revoked privilege"):
|
||||
target_settings_profile_name = f"target_settings_profile_{getuid()}"
|
||||
|
||||
with settings_profile(node, target_settings_profile_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW CREATE SETTINGS PROFILE"):
|
||||
node.query(f"SHOW CREATE SETTINGS PROFILE {target_settings_profile_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("show settings profiles")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowSettingsProfiles("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of SHOW SETTINGS PROFILES.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
159
tests/testflows/rbac/tests/privileges/show/show_users.py
Normal file
159
tests/testflows/rbac/tests/privileges/show/show_users.py
Normal file
@ -0,0 +1,159 @@
|
||||
from testflows.core import *
|
||||
from testflows.asserts import error
|
||||
|
||||
from rbac.requirements import *
|
||||
from rbac.helper.common import *
|
||||
import rbac.helper.errors as errors
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_directly(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW USERS` with privileges are granted directly.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"):
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[user_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestSuite
|
||||
def privileges_granted_via_role(self, node=None):
|
||||
"""Check that a user is able to execute `SHOW USERS` with privileges are granted through a role.
|
||||
"""
|
||||
|
||||
user_name = f"user_{getuid()}"
|
||||
role_name = f"role_{getuid()}"
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with user(node, f"{user_name}"), role(node, f"{role_name}"):
|
||||
|
||||
with When("I grant the role to the user"):
|
||||
node.query(f"GRANT {role_name} TO {user_name}")
|
||||
|
||||
Suite(run=check_privilege, flags=TE,
|
||||
examples=Examples("privilege grant_target_name user_name", [
|
||||
tuple(list(row)+[role_name,user_name]) for row in check_privilege.examples
|
||||
], args=Args(name="privilege={privilege}", format_name=True)))
|
||||
|
||||
@TestOutline(Suite)
|
||||
@Examples("privilege",[
|
||||
("ACCESS MANAGEMENT",),
|
||||
("SHOW ACCESS",),
|
||||
("SHOW USERS",),
|
||||
("SHOW CREATE USER",),
|
||||
])
|
||||
def check_privilege(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Run checks for commands that require SHOW USERS privilege.
|
||||
"""
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
Suite(test=show_users, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
Suite(test=show_create, setup=instrument_clickhouse_server_log)(privilege=privilege, grant_target_name=grant_target_name, user_name=user_name)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowUsers_Query("1.0"),
|
||||
)
|
||||
def show_users(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW USERS` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW USERS without privilege"):
|
||||
|
||||
with When("I check the user can't use SHOW USERS"):
|
||||
node.query(f"SHOW USERS", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW USERS with privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW USERS"):
|
||||
node.query(f"SHOW USERS", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW USERS with revoked privilege"):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW USERS"):
|
||||
node.query(f"SHOW USERS", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestSuite
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowCreateUser("1.0"),
|
||||
)
|
||||
def show_create(self, privilege, grant_target_name, user_name, node=None):
|
||||
"""Check that user is only able to execute `SHOW CREATE USER` when they have the necessary privilege.
|
||||
"""
|
||||
exitcode, message = errors.not_enough_privileges(name=user_name)
|
||||
|
||||
if node is None:
|
||||
node = self.context.node
|
||||
|
||||
with Scenario("SHOW CREATE USER without privilege"):
|
||||
target_user_name = f"target_user_{getuid()}"
|
||||
|
||||
with user(node, target_user_name):
|
||||
|
||||
with When("I check the user can't use SHOW CREATE USER"):
|
||||
node.query(f"SHOW CREATE USER {target_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
with Scenario("SHOW CREATE USER with privilege"):
|
||||
target_user_name = f"target_user_{getuid()}"
|
||||
|
||||
with user(node, target_user_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with Then("I check the user can use SHOW CREATE USER"):
|
||||
node.query(f"SHOW CREATE USER {target_user_name}", settings = [("user", f"{user_name}")])
|
||||
|
||||
with Scenario("SHOW CREATE USER with revoked privilege"):
|
||||
target_user_name = f"target_user_{getuid()}"
|
||||
|
||||
with user(node, target_user_name):
|
||||
|
||||
with When(f"I grant {privilege}"):
|
||||
node.query(f"GRANT {privilege} ON *.* TO {grant_target_name}")
|
||||
|
||||
with And(f"I revoke {privilege}"):
|
||||
node.query(f"REVOKE {privilege} ON *.* FROM {grant_target_name}")
|
||||
|
||||
with Then("I check the user cannot use SHOW CREATE USER"):
|
||||
node.query(f"SHOW CREATE USER {target_user_name}", settings=[("user",user_name)],
|
||||
exitcode=exitcode, message=message)
|
||||
|
||||
@TestFeature
|
||||
@Name("show users")
|
||||
@Requirements(
|
||||
RQ_SRS_006_RBAC_Privileges_ShowUsers("1.0"),
|
||||
)
|
||||
def feature(self, node="clickhouse1"):
|
||||
"""Check the RBAC functionality of SHOW USERS.
|
||||
"""
|
||||
self.context.node = self.context.cluster.node(node)
|
||||
|
||||
Suite(run=privileges_granted_directly, setup=instrument_clickhouse_server_log)
|
||||
Suite(run=privileges_granted_via_role, setup=instrument_clickhouse_server_log)
|
@ -1,15 +1,19 @@
|
||||
v20.11.5.18-stable 2020-12-06
|
||||
v20.11.4.13-stable 2020-11-20
|
||||
v20.11.3.3-stable 2020-11-13
|
||||
v20.11.2.1-stable 2020-11-11
|
||||
v20.10.6.27-stable 2020-12-06
|
||||
v20.10.5.10-stable 2020-11-20
|
||||
v20.10.4.1-stable 2020-11-13
|
||||
v20.10.3.30-stable 2020-10-29
|
||||
v20.10.2.20-stable 2020-10-23
|
||||
v20.9.7.11-stable 2020-12-07
|
||||
v20.9.6.14-stable 2020-11-20
|
||||
v20.9.5.5-stable 2020-11-13
|
||||
v20.9.4.76-stable 2020-10-29
|
||||
v20.9.3.45-stable 2020-10-09
|
||||
v20.9.2.20-stable 2020-09-22
|
||||
v20.8.8.2-lts 2020-12-07
|
||||
v20.8.7.15-lts 2020-11-20
|
||||
v20.8.6.6-lts 2020-11-13
|
||||
v20.8.5.45-lts 2020-10-29
|
||||
|
|
Loading…
Reference in New Issue
Block a user