Merge branch 'master' into complete_zk_api

This commit is contained in:
alesapin 2020-12-07 10:24:55 +03:00
commit 0c43246216
79 changed files with 4963 additions and 230 deletions

6
.gitmodules vendored
View File

@ -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

View File

@ -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

@ -0,0 +1 @@
Subproject commit b2751c65c0592c0239aec3becd53d0ea2fde9329

1
contrib/ryu vendored

@ -1 +0,0 @@
Subproject commit 5b4a853534b47438b4d97935370f6b2397137c2b

View File

@ -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}")

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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 -->

View File

@ -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`.

View 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-->

View File

@ -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)

View File

@ -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}
Количество столбцов в первичном ключе не ограничено явным образом. В зависимости от структуры данных в первичный ключ можно включать больше или меньше столбцов. Это может:

View File

@ -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-->

View File

@ -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服务器暴露给公共互联网。确保它只在私有网络上侦听并由正确配置的防火墙监控。

View File

@ -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-->

View File

@ -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);

View File

@ -242,7 +242,7 @@ target_link_libraries (clickhouse_common_io
PUBLIC
common
${DOUBLE_CONVERSION_LIBRARIES}
ryu
dragonbox_to_chars
)
if(RE2_LIBRARY)

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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) \
\

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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

View File

@ -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>

View 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);
}
}

View 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;
}
};
}

View File

@ -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);

View File

@ -208,6 +208,7 @@ SRCS(
cos.cpp
cosh.cpp
countDigits.cpp
countMatches.cpp
countSubstrings.cpp
countSubstringsCaseInsensitive.cpp
countSubstringsCaseInsensitiveUTF8.cpp

View File

@ -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_))
{
}

View File

@ -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_);

View File

@ -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)

View File

@ -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)

View File

@ -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";

View File

@ -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();

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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_) {}

View File

@ -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())

View File

@ -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);

View File

@ -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();

View File

@ -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.

View File

@ -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} (

View 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>

View File

@ -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

View File

@ -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;

View File

@ -0,0 +1,2 @@
optimize_final 200001 1 1
optimize_final 202001 1 1

View File

@ -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;

View 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

View 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; }

View File

@ -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

View File

@ -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 = {

View File

@ -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

View File

@ -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',

View 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)

View 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)

View 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)

View File

@ -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)

View 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)

View 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)

View 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)

View File

@ -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)

View File

@ -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)

View 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)

View 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)

View 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)

View 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)

View File

@ -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)

View 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)

View File

@ -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), {})

View File

@ -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)

View 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)

View 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)

View 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)

View 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)

View File

@ -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)

View 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)

View File

@ -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

1 v20.11.4.13-stable v20.11.5.18-stable 2020-11-20 2020-12-06
1 v20.11.5.18-stable 2020-12-06
2 v20.11.4.13-stable v20.11.4.13-stable 2020-11-20 2020-11-20
3 v20.11.3.3-stable v20.11.3.3-stable 2020-11-13 2020-11-13
4 v20.11.2.1-stable v20.11.2.1-stable 2020-11-11 2020-11-11
5 v20.10.6.27-stable 2020-12-06
6 v20.10.5.10-stable v20.10.5.10-stable 2020-11-20 2020-11-20
7 v20.10.4.1-stable v20.10.4.1-stable 2020-11-13 2020-11-13
8 v20.10.3.30-stable v20.10.3.30-stable 2020-10-29 2020-10-29
9 v20.10.2.20-stable v20.10.2.20-stable 2020-10-23 2020-10-23
10 v20.9.7.11-stable 2020-12-07
11 v20.9.6.14-stable v20.9.6.14-stable 2020-11-20 2020-11-20
12 v20.9.5.5-stable v20.9.5.5-stable 2020-11-13 2020-11-13
13 v20.9.4.76-stable v20.9.4.76-stable 2020-10-29 2020-10-29
14 v20.9.3.45-stable v20.9.3.45-stable 2020-10-09 2020-10-09
15 v20.9.2.20-stable v20.9.2.20-stable 2020-09-22 2020-09-22
16 v20.8.8.2-lts 2020-12-07
17 v20.8.7.15-lts v20.8.7.15-lts 2020-11-20 2020-11-20
18 v20.8.6.6-lts v20.8.6.6-lts 2020-11-13 2020-11-13
19 v20.8.5.45-lts v20.8.5.45-lts 2020-10-29 2020-10-29