mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-23 08:02:02 +00:00
Merge branch 'master' of github.com:ClickHouse/ClickHouse into fix-inconsistent-formatting
This commit is contained in:
commit
da2ae34c1d
2
contrib/NuRaft
vendored
2
contrib/NuRaft
vendored
@ -1 +1 @@
|
|||||||
Subproject commit 5bb3a0e8257bacd65b099cb1b7239bd6b9a2c477
|
Subproject commit 4a12f99dfc9d47c687ff7700b927cc76856225d1
|
2
contrib/rapidjson
vendored
2
contrib/rapidjson
vendored
@ -1 +1 @@
|
|||||||
Subproject commit a9bc56c9165f1dbbbcada64221bd3a59042c5b95
|
Subproject commit 800ca2f38fc3b387271d9e1926fcfc9070222104
|
@ -214,8 +214,7 @@ function check_server_start()
|
|||||||
function check_logs_for_critical_errors()
|
function check_logs_for_critical_errors()
|
||||||
{
|
{
|
||||||
# Sanitizer asserts
|
# Sanitizer asserts
|
||||||
rg -Fa "==================" /var/log/clickhouse-server/stderr.log | rg -v "in query:" >> /test_output/tmp
|
sed -n '/WARNING:.*anitizer/,/^$/p' >> /test_output/tmp
|
||||||
rg -Fa "WARNING" /var/log/clickhouse-server/stderr.log >> /test_output/tmp
|
|
||||||
rg -Fav -e "ASan doesn't fully support makecontext/swapcontext functions" -e "DB::Exception" /test_output/tmp > /dev/null \
|
rg -Fav -e "ASan doesn't fully support makecontext/swapcontext functions" -e "DB::Exception" /test_output/tmp > /dev/null \
|
||||||
&& echo -e "Sanitizer assert (in stderr.log)$FAIL$(head_escaped /test_output/tmp)" >> /test_output/test_results.tsv \
|
&& echo -e "Sanitizer assert (in stderr.log)$FAIL$(head_escaped /test_output/tmp)" >> /test_output/test_results.tsv \
|
||||||
|| echo -e "No sanitizer asserts$OK" >> /test_output/test_results.tsv
|
|| echo -e "No sanitizer asserts$OK" >> /test_output/test_results.tsv
|
||||||
|
@ -88,6 +88,7 @@ rm /etc/clickhouse-server/config.d/merge_tree.xml
|
|||||||
rm /etc/clickhouse-server/config.d/enable_wait_for_shutdown_replicated_tables.xml
|
rm /etc/clickhouse-server/config.d/enable_wait_for_shutdown_replicated_tables.xml
|
||||||
rm /etc/clickhouse-server/config.d/zero_copy_destructive_operations.xml
|
rm /etc/clickhouse-server/config.d/zero_copy_destructive_operations.xml
|
||||||
rm /etc/clickhouse-server/config.d/storage_conf_02963.xml
|
rm /etc/clickhouse-server/config.d/storage_conf_02963.xml
|
||||||
|
rm /etc/clickhouse-server/config.d/backoff_failed_mutation.xml
|
||||||
rm /etc/clickhouse-server/users.d/nonconst_timezone.xml
|
rm /etc/clickhouse-server/users.d/nonconst_timezone.xml
|
||||||
rm /etc/clickhouse-server/users.d/s3_cache_new.xml
|
rm /etc/clickhouse-server/users.d/s3_cache_new.xml
|
||||||
rm /etc/clickhouse-server/users.d/replicated_ddl_entry.xml
|
rm /etc/clickhouse-server/users.d/replicated_ddl_entry.xml
|
||||||
@ -134,6 +135,7 @@ rm /etc/clickhouse-server/config.d/merge_tree.xml
|
|||||||
rm /etc/clickhouse-server/config.d/enable_wait_for_shutdown_replicated_tables.xml
|
rm /etc/clickhouse-server/config.d/enable_wait_for_shutdown_replicated_tables.xml
|
||||||
rm /etc/clickhouse-server/config.d/zero_copy_destructive_operations.xml
|
rm /etc/clickhouse-server/config.d/zero_copy_destructive_operations.xml
|
||||||
rm /etc/clickhouse-server/config.d/storage_conf_02963.xml
|
rm /etc/clickhouse-server/config.d/storage_conf_02963.xml
|
||||||
|
rm /etc/clickhouse-server/config.d/backoff_failed_mutation.xml
|
||||||
rm /etc/clickhouse-server/config.d/block_number.xml
|
rm /etc/clickhouse-server/config.d/block_number.xml
|
||||||
rm /etc/clickhouse-server/users.d/nonconst_timezone.xml
|
rm /etc/clickhouse-server/users.d/nonconst_timezone.xml
|
||||||
rm /etc/clickhouse-server/users.d/s3_cache_new.xml
|
rm /etc/clickhouse-server/users.d/s3_cache_new.xml
|
||||||
|
@ -5345,24 +5345,6 @@ SELECT map('a', range(number), 'b', number, 'c', 'str_' || toString(number)) as
|
|||||||
|
|
||||||
Default value: `false`.
|
Default value: `false`.
|
||||||
|
|
||||||
## default_normal_view_sql_security {#default_normal_view_sql_security}
|
|
||||||
|
|
||||||
Allows to set default `SQL SECURITY` option while creating a normal view. [More about SQL security](../../sql-reference/statements/create/view.md#sql_security).
|
|
||||||
|
|
||||||
The default value is `INVOKER`.
|
|
||||||
|
|
||||||
## default_materialized_view_sql_security {#default_materialized_view_sql_security}
|
|
||||||
|
|
||||||
Allows to set a default value for SQL SECURITY option when creating a materialized view. [More about SQL security](../../sql-reference/statements/create/view.md#sql_security).
|
|
||||||
|
|
||||||
The default value is `DEFINER`.
|
|
||||||
|
|
||||||
## default_view_definer {#default_view_definer}
|
|
||||||
|
|
||||||
Allows to set default `DEFINER` option while creating a view. [More about SQL security](../../sql-reference/statements/create/view.md#sql_security).
|
|
||||||
|
|
||||||
The default value is `CURRENT_USER`.
|
|
||||||
|
|
||||||
## max_partition_size_to_drop
|
## max_partition_size_to_drop
|
||||||
|
|
||||||
Restriction on dropping partitions in query time. The value 0 means that you can drop partitions without any restrictions.
|
Restriction on dropping partitions in query time. The value 0 means that you can drop partitions without any restrictions.
|
||||||
|
@ -13,9 +13,7 @@ Creates a new view. Views can be [normal](#normal-view), [materialized](#materia
|
|||||||
Syntax:
|
Syntax:
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
CREATE [OR REPLACE] VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name]
|
CREATE [OR REPLACE] VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name] AS SELECT ...
|
||||||
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
|
|
||||||
AS SELECT ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Normal views do not store any data. They just perform a read from another table on each access. In other words, a normal view is nothing more than a saved query. When reading from a view, this saved query is used as a subquery in the [FROM](../../../sql-reference/statements/select/from.md) clause.
|
Normal views do not store any data. They just perform a read from another table on each access. In other words, a normal view is nothing more than a saved query. When reading from a view, this saved query is used as a subquery in the [FROM](../../../sql-reference/statements/select/from.md) clause.
|
||||||
@ -54,9 +52,7 @@ SELECT * FROM view(column1=value1, column2=value2 ...)
|
|||||||
## Materialized View
|
## Materialized View
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE]
|
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT ...
|
||||||
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
|
|
||||||
AS SELECT ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
:::tip
|
:::tip
|
||||||
@ -95,49 +91,6 @@ Views look the same as normal tables. For example, they are listed in the result
|
|||||||
|
|
||||||
To delete a view, use [DROP VIEW](../../../sql-reference/statements/drop.md#drop-view). Although `DROP TABLE` works for VIEWs as well.
|
To delete a view, use [DROP VIEW](../../../sql-reference/statements/drop.md#drop-view). Although `DROP TABLE` works for VIEWs as well.
|
||||||
|
|
||||||
## SQL security {#sql_security}
|
|
||||||
|
|
||||||
`DEFINER` and `SQL SECURITY` allow you to specify which ClickHouse user to use when executing the view's underlying query.
|
|
||||||
`SQL SECURITY` has three legal values: `DEFINER`, `INVOKER`, or `NONE`. You can specify any existing user or `CURRENT_USER` in the `DEFINER` clause.
|
|
||||||
|
|
||||||
The following table will explain which rights are required for which user in order to select from view.
|
|
||||||
Note that regardless of the SQL security option, in every case it is still required to have `GRANT SELECT ON <view>` in order to read from it.
|
|
||||||
|
|
||||||
| SQL security option | View | Materialized View |
|
|
||||||
|---------------------|-----------------------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
|
|
||||||
| `DEFINER alice` | `alice` must have a `SELECT` grant for the view's source table. | `alice` must have a `SELECT` grant for the view's source table and an `INSERT` grant for the view's target table. |
|
|
||||||
| `INVOKER` | User must have a `SELECT` grant for the view's source table. | `SQL SECURITY INVOKER` can't be specified for materialized views. |
|
|
||||||
| `NONE` | - | - |
|
|
||||||
|
|
||||||
:::note
|
|
||||||
`SQL SECURITY NONE` is a deprecated option. Any user with the rights to create views with `SQL SECURITY NONE` will be able to execute any arbitrary query.
|
|
||||||
Thus, it is required to have `GRANT ALLOW SQL SECURITY NONE TO <user>` in order to create a view with this option.
|
|
||||||
:::
|
|
||||||
|
|
||||||
If `DEFINER`/`SQL SECURITY` aren't specified, the default values are used:
|
|
||||||
- `SQL SECURITY`: `INVOKER` for normal views and `DEFINER` for materialized views ([configurable by settings](../../../operations/settings/settings.md#default_normal_view_sql_security))
|
|
||||||
- `DEFINER`: `CURRENT_USER` ([configurable by settings](../../../operations/settings/settings.md#default_view_definer))
|
|
||||||
|
|
||||||
If a view is attached without `DEFINER`/`SQL SECURITY` specified, the default value is `SQL SECURITY NONE` for the materialized view and `SQL SECURITY INVOKER` for the normal view.
|
|
||||||
|
|
||||||
To change SQL security for an existing view, use
|
|
||||||
```sql
|
|
||||||
ALTER TABLE MODIFY SQL SECURITY { DEFINER | INVOKER | NONE } [DEFINER = { user | CURRENT_USER }]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Examples sql security
|
|
||||||
```sql
|
|
||||||
CREATE test_view
|
|
||||||
DEFINER = alice SQL SECURITY DEFINER
|
|
||||||
AS SELECT ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE test_view
|
|
||||||
SQL SECURITY INVOKER
|
|
||||||
AS SELECT ...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Live View [Deprecated]
|
## Live View [Deprecated]
|
||||||
|
|
||||||
This feature is deprecated and will be removed in the future.
|
This feature is deprecated and will be removed in the future.
|
||||||
|
@ -114,7 +114,6 @@ Hierarchy of privileges:
|
|||||||
- `ALTER VIEW`
|
- `ALTER VIEW`
|
||||||
- `ALTER VIEW REFRESH`
|
- `ALTER VIEW REFRESH`
|
||||||
- `ALTER VIEW MODIFY QUERY`
|
- `ALTER VIEW MODIFY QUERY`
|
||||||
- `ALTER VIEW MODIFY SQL SECURITY`
|
|
||||||
- [CREATE](#grant-create)
|
- [CREATE](#grant-create)
|
||||||
- `CREATE DATABASE`
|
- `CREATE DATABASE`
|
||||||
- `CREATE TABLE`
|
- `CREATE TABLE`
|
||||||
@ -308,7 +307,6 @@ Allows executing [ALTER](../../sql-reference/statements/alter/index.md) queries
|
|||||||
- `ALTER VIEW` Level: `GROUP`
|
- `ALTER VIEW` Level: `GROUP`
|
||||||
- `ALTER VIEW REFRESH`. Level: `VIEW`. Aliases: `ALTER LIVE VIEW REFRESH`, `REFRESH VIEW`
|
- `ALTER VIEW REFRESH`. Level: `VIEW`. Aliases: `ALTER LIVE VIEW REFRESH`, `REFRESH VIEW`
|
||||||
- `ALTER VIEW MODIFY QUERY`. Level: `VIEW`. Aliases: `ALTER TABLE MODIFY QUERY`
|
- `ALTER VIEW MODIFY QUERY`. Level: `VIEW`. Aliases: `ALTER TABLE MODIFY QUERY`
|
||||||
- `ALTER VIEW MODIFY SQL SECURITY`. Level: `VIEW`. Aliases: `ALTER TABLE MODIFY SQL SECURITY`
|
|
||||||
|
|
||||||
Examples of how this hierarchy is treated:
|
Examples of how this hierarchy is treated:
|
||||||
|
|
||||||
@ -411,7 +409,6 @@ Allows a user to execute queries that manage users, roles and row policies.
|
|||||||
- `SHOW_ROW_POLICIES`. Level: `GLOBAL`. Aliases: `SHOW POLICIES`, `SHOW CREATE ROW POLICY`, `SHOW CREATE POLICY`
|
- `SHOW_ROW_POLICIES`. Level: `GLOBAL`. Aliases: `SHOW POLICIES`, `SHOW CREATE ROW POLICY`, `SHOW CREATE POLICY`
|
||||||
- `SHOW_QUOTAS`. Level: `GLOBAL`. Aliases: `SHOW CREATE QUOTA`
|
- `SHOW_QUOTAS`. Level: `GLOBAL`. Aliases: `SHOW CREATE QUOTA`
|
||||||
- `SHOW_SETTINGS_PROFILES`. Level: `GLOBAL`. Aliases: `SHOW PROFILES`, `SHOW CREATE SETTINGS PROFILE`, `SHOW CREATE PROFILE`
|
- `SHOW_SETTINGS_PROFILES`. Level: `GLOBAL`. Aliases: `SHOW PROFILES`, `SHOW CREATE SETTINGS PROFILE`, `SHOW CREATE PROFILE`
|
||||||
- `ALLOW SQL SECURITY NONE`. Level: `GLOBAL`. Aliases: `CREATE SQL SECURITY NONE`, `SQL SECURITY NONE`, `SECURITY NONE`
|
|
||||||
|
|
||||||
The `ROLE ADMIN` privilege allows a user to assign and revoke any roles including those which are not assigned to the user with the admin option.
|
The `ROLE ADMIN` privilege allows a user to assign and revoke any roles including those which are not assigned to the user with the admin option.
|
||||||
|
|
||||||
|
@ -11,9 +11,7 @@ sidebar_label: "Представление"
|
|||||||
## Обычные представления {#normal}
|
## Обычные представления {#normal}
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
CREATE [OR REPLACE] VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name]
|
CREATE [OR REPLACE] VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER cluster_name] AS SELECT ...
|
||||||
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
|
|
||||||
AS SELECT ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Обычные представления не хранят никаких данных, они выполняют чтение данных из другой таблицы при каждом доступе. Другими словами, обычное представление — это не что иное, как сохраненный запрос. При чтении данных из представления этот сохраненный запрос используется как подзапрос в секции [FROM](../../../sql-reference/statements/select/from.md).
|
Обычные представления не хранят никаких данных, они выполняют чтение данных из другой таблицы при каждом доступе. Другими словами, обычное представление — это не что иное, как сохраненный запрос. При чтении данных из представления этот сохраненный запрос используется как подзапрос в секции [FROM](../../../sql-reference/statements/select/from.md).
|
||||||
@ -39,9 +37,7 @@ SELECT a, b, c FROM (SELECT ...)
|
|||||||
## Материализованные представления {#materialized}
|
## Материализованные представления {#materialized}
|
||||||
|
|
||||||
``` sql
|
``` sql
|
||||||
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE]
|
CREATE MATERIALIZED VIEW [IF NOT EXISTS] [db.]table_name [ON CLUSTER] [TO[db.]name] [ENGINE = engine] [POPULATE] AS SELECT ...
|
||||||
[DEFINER = { user | CURRENT_USER }] [SQL SECURITY { DEFINER | INVOKER | NONE }]
|
|
||||||
AS SELECT ...
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Материализованные (MATERIALIZED) представления хранят данные, преобразованные соответствующим запросом [SELECT](../../../sql-reference/statements/select/index.md).
|
Материализованные (MATERIALIZED) представления хранят данные, преобразованные соответствующим запросом [SELECT](../../../sql-reference/statements/select/index.md).
|
||||||
@ -70,52 +66,6 @@ AS SELECT ...
|
|||||||
|
|
||||||
Чтобы удалить представление, следует использовать [DROP VIEW](../../../sql-reference/statements/drop.md#drop-view). Впрочем, `DROP TABLE` тоже работает для представлений.
|
Чтобы удалить представление, следует использовать [DROP VIEW](../../../sql-reference/statements/drop.md#drop-view). Впрочем, `DROP TABLE` тоже работает для представлений.
|
||||||
|
|
||||||
## SQL безопасность {#sql_security}
|
|
||||||
|
|
||||||
Параметры `DEFINER` и `SQL SECURITY` позволяют задать правило от имени какого пользователя будут выполняться запросы к таблицам, на которые ссылается представление.
|
|
||||||
Для `SQL SECURITY` допустимо три значения: `DEFINER`, `INVOKER`, или `NONE`.
|
|
||||||
Для `DEFINER` можно указать имя любого существующего пользователя или же `CURRENT_USER`.
|
|
||||||
|
|
||||||
Далее приведена таблица, объясняющая какие права необходимы каким пользователям при заданных параметрах SQL безопасности.
|
|
||||||
Обратите внимание, что, в независимости от заданных параметров SQL безопасности,
|
|
||||||
у пользователя должно быть право `GRANT SELECT ON <view>` для чтения из представления.
|
|
||||||
|
|
||||||
| SQL security option | View | Materialized View |
|
|
||||||
|---------------------|----------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------|
|
|
||||||
| `DEFINER alice` | У `alice` должно быть право `SELECT` на таблицу-источник. | У `alice` должны быть права `SELECT` на таблицу-источник и `INSERT` на таблицу-назначение. |
|
|
||||||
| `INVOKER` | У пользователя выполняющего запрос к представлению должно быть право `SELECT` на таблицу-источник. | Тип `SQL SECURITY INVOKER` не может быть указан для материализованных представлений. |
|
|
||||||
| `NONE` | - | - |
|
|
||||||
|
|
||||||
:::note
|
|
||||||
Тип `SQL SECURITY NONE` не безопасен для использования. Любой пользователь с правом создавать представления с `SQL SECURITY NONE` сможет исполнять любые запросы без проверки прав.
|
|
||||||
По умолчанию, у пользователей нет прав указывать `SQL SECURITY NONE`, однако, при необходимости, это право можно выдать с помощью `GRANT ALLOW SQL SECURITY NONE TO <user>`.
|
|
||||||
:::
|
|
||||||
|
|
||||||
Если `DEFINER`/`SQL SECURITY` не указан, будут использованы значения по умолчанию:
|
|
||||||
- `SQL SECURITY`: `INVOKER` для обычных представлений и `DEFINER` для материализованных ([изменяется в настройках](../../../operations/settings/settings.md#default_normal_view_sql_security))
|
|
||||||
- `DEFINER`: `CURRENT_USER` ([изменяется в настройках](../../../operations/settings/settings.md#default_view_definer))
|
|
||||||
|
|
||||||
Если представление подключается с помощью ключевого слова `ATTACH` и настройки SQL безопасности не были заданы,
|
|
||||||
то по умолчанию будет использоваться `SQL SECURITY NONE` для материализованных представлений и `SQL SECURITY INVOKER` для обычных.
|
|
||||||
|
|
||||||
Изменить параметры SQL безопасности возможно с помощью следующего запроса:
|
|
||||||
```sql
|
|
||||||
ALTER TABLE MODIFY SQL SECURITY { DEFINER | INVOKER | NONE } [DEFINER = { user | CURRENT_USER }]
|
|
||||||
```
|
|
||||||
|
|
||||||
### Примеры представлений с SQL безопасностью
|
|
||||||
```sql
|
|
||||||
CREATE test_view
|
|
||||||
DEFINER = alice SQL SECURITY DEFINER
|
|
||||||
AS SELECT ...
|
|
||||||
```
|
|
||||||
|
|
||||||
```sql
|
|
||||||
CREATE test_view
|
|
||||||
SQL SECURITY INVOKER
|
|
||||||
AS SELECT ...
|
|
||||||
```
|
|
||||||
|
|
||||||
## LIVE-представления [экспериментальный функционал] {#live-view}
|
## LIVE-представления [экспериментальный функционал] {#live-view}
|
||||||
|
|
||||||
:::note Важно
|
:::note Важно
|
||||||
|
@ -103,7 +103,6 @@ namespace
|
|||||||
const Flags & getColumnFlags() const { return all_flags_for_target[COLUMN]; }
|
const Flags & getColumnFlags() const { return all_flags_for_target[COLUMN]; }
|
||||||
const Flags & getDictionaryFlags() const { return all_flags_for_target[DICTIONARY]; }
|
const Flags & getDictionaryFlags() const { return all_flags_for_target[DICTIONARY]; }
|
||||||
const Flags & getNamedCollectionFlags() const { return all_flags_for_target[NAMED_COLLECTION]; }
|
const Flags & getNamedCollectionFlags() const { return all_flags_for_target[NAMED_COLLECTION]; }
|
||||||
const Flags & getUserNameFlags() const { return all_flags_for_target[USER_NAME]; }
|
|
||||||
const Flags & getAllFlagsGrantableOnGlobalLevel() const { return getAllFlags(); }
|
const Flags & getAllFlagsGrantableOnGlobalLevel() const { return getAllFlags(); }
|
||||||
const Flags & getAllFlagsGrantableOnGlobalWithParameterLevel() const { return getGlobalWithParameterFlags(); }
|
const Flags & getAllFlagsGrantableOnGlobalWithParameterLevel() const { return getGlobalWithParameterFlags(); }
|
||||||
const Flags & getAllFlagsGrantableOnDatabaseLevel() const { return all_flags_grantable_on_database_level; }
|
const Flags & getAllFlagsGrantableOnDatabaseLevel() const { return all_flags_grantable_on_database_level; }
|
||||||
@ -122,7 +121,6 @@ namespace
|
|||||||
COLUMN,
|
COLUMN,
|
||||||
DICTIONARY,
|
DICTIONARY,
|
||||||
NAMED_COLLECTION,
|
NAMED_COLLECTION,
|
||||||
USER_NAME,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Node;
|
struct Node;
|
||||||
@ -302,7 +300,7 @@ namespace
|
|||||||
collectAllFlags(child.get());
|
collectAllFlags(child.get());
|
||||||
|
|
||||||
all_flags_grantable_on_table_level = all_flags_for_target[TABLE] | all_flags_for_target[DICTIONARY] | all_flags_for_target[COLUMN];
|
all_flags_grantable_on_table_level = all_flags_for_target[TABLE] | all_flags_for_target[DICTIONARY] | all_flags_for_target[COLUMN];
|
||||||
all_flags_grantable_on_global_with_parameter_level = all_flags_for_target[NAMED_COLLECTION] | all_flags_for_target[USER_NAME];
|
all_flags_grantable_on_global_with_parameter_level = all_flags_for_target[NAMED_COLLECTION];
|
||||||
all_flags_grantable_on_database_level = all_flags_for_target[DATABASE] | all_flags_grantable_on_table_level;
|
all_flags_grantable_on_database_level = all_flags_for_target[DATABASE] | all_flags_grantable_on_table_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -353,7 +351,7 @@ namespace
|
|||||||
std::unordered_map<std::string_view, Flags> keyword_to_flags_map;
|
std::unordered_map<std::string_view, Flags> keyword_to_flags_map;
|
||||||
std::vector<Flags> access_type_to_flags_mapping;
|
std::vector<Flags> access_type_to_flags_mapping;
|
||||||
Flags all_flags;
|
Flags all_flags;
|
||||||
Flags all_flags_for_target[static_cast<size_t>(USER_NAME) + 1];
|
Flags all_flags_for_target[static_cast<size_t>(NAMED_COLLECTION) + 1];
|
||||||
Flags all_flags_grantable_on_database_level;
|
Flags all_flags_grantable_on_database_level;
|
||||||
Flags all_flags_grantable_on_table_level;
|
Flags all_flags_grantable_on_table_level;
|
||||||
Flags all_flags_grantable_on_global_with_parameter_level;
|
Flags all_flags_grantable_on_global_with_parameter_level;
|
||||||
@ -373,11 +371,7 @@ std::unordered_map<AccessFlags::ParameterType, AccessFlags> AccessFlags::splitIn
|
|||||||
if (named_collection_flags)
|
if (named_collection_flags)
|
||||||
result.emplace(ParameterType::NAMED_COLLECTION, named_collection_flags);
|
result.emplace(ParameterType::NAMED_COLLECTION, named_collection_flags);
|
||||||
|
|
||||||
auto user_flags = AccessFlags::allUserNameFlags() & *this;
|
auto other_flags = (~AccessFlags::allNamedCollectionFlags()) & *this;
|
||||||
if (user_flags)
|
|
||||||
result.emplace(ParameterType::USER_NAME, user_flags);
|
|
||||||
|
|
||||||
auto other_flags = (~named_collection_flags & ~user_flags) & *this;
|
|
||||||
if (other_flags)
|
if (other_flags)
|
||||||
result.emplace(ParameterType::NONE, other_flags);
|
result.emplace(ParameterType::NONE, other_flags);
|
||||||
|
|
||||||
@ -393,9 +387,6 @@ AccessFlags::ParameterType AccessFlags::getParameterType() const
|
|||||||
if (AccessFlags::allNamedCollectionFlags().contains(*this))
|
if (AccessFlags::allNamedCollectionFlags().contains(*this))
|
||||||
return AccessFlags::NAMED_COLLECTION;
|
return AccessFlags::NAMED_COLLECTION;
|
||||||
|
|
||||||
if (AccessFlags::allUserNameFlags().contains(*this))
|
|
||||||
return AccessFlags::USER_NAME;
|
|
||||||
|
|
||||||
throw Exception(ErrorCodes::MIXED_ACCESS_PARAMETER_TYPES, "Having mixed parameter types: {}", toString());
|
throw Exception(ErrorCodes::MIXED_ACCESS_PARAMETER_TYPES, "Having mixed parameter types: {}", toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,7 +405,6 @@ AccessFlags AccessFlags::allTableFlags() { return Helper::instance().getTableFla
|
|||||||
AccessFlags AccessFlags::allColumnFlags() { return Helper::instance().getColumnFlags(); }
|
AccessFlags AccessFlags::allColumnFlags() { return Helper::instance().getColumnFlags(); }
|
||||||
AccessFlags AccessFlags::allDictionaryFlags() { return Helper::instance().getDictionaryFlags(); }
|
AccessFlags AccessFlags::allDictionaryFlags() { return Helper::instance().getDictionaryFlags(); }
|
||||||
AccessFlags AccessFlags::allNamedCollectionFlags() { return Helper::instance().getNamedCollectionFlags(); }
|
AccessFlags AccessFlags::allNamedCollectionFlags() { return Helper::instance().getNamedCollectionFlags(); }
|
||||||
AccessFlags AccessFlags::allUserNameFlags() { return Helper::instance().getUserNameFlags(); }
|
|
||||||
AccessFlags AccessFlags::allFlagsGrantableOnGlobalLevel() { return Helper::instance().getAllFlagsGrantableOnGlobalLevel(); }
|
AccessFlags AccessFlags::allFlagsGrantableOnGlobalLevel() { return Helper::instance().getAllFlagsGrantableOnGlobalLevel(); }
|
||||||
AccessFlags AccessFlags::allFlagsGrantableOnGlobalWithParameterLevel() { return Helper::instance().getAllFlagsGrantableOnGlobalWithParameterLevel(); }
|
AccessFlags AccessFlags::allFlagsGrantableOnGlobalWithParameterLevel() { return Helper::instance().getAllFlagsGrantableOnGlobalWithParameterLevel(); }
|
||||||
AccessFlags AccessFlags::allFlagsGrantableOnDatabaseLevel() { return Helper::instance().getAllFlagsGrantableOnDatabaseLevel(); }
|
AccessFlags AccessFlags::allFlagsGrantableOnDatabaseLevel() { return Helper::instance().getAllFlagsGrantableOnDatabaseLevel(); }
|
||||||
|
@ -57,7 +57,6 @@ public:
|
|||||||
{
|
{
|
||||||
NONE,
|
NONE,
|
||||||
NAMED_COLLECTION,
|
NAMED_COLLECTION,
|
||||||
USER_NAME,
|
|
||||||
};
|
};
|
||||||
ParameterType getParameterType() const;
|
ParameterType getParameterType() const;
|
||||||
std::unordered_map<ParameterType, AccessFlags> splitIntoParameterTypes() const;
|
std::unordered_map<ParameterType, AccessFlags> splitIntoParameterTypes() const;
|
||||||
@ -104,9 +103,6 @@ public:
|
|||||||
/// Returns all the flags related to a named collection.
|
/// Returns all the flags related to a named collection.
|
||||||
static AccessFlags allNamedCollectionFlags();
|
static AccessFlags allNamedCollectionFlags();
|
||||||
|
|
||||||
/// Returns all the flags related to a user.
|
|
||||||
static AccessFlags allUserNameFlags();
|
|
||||||
|
|
||||||
/// Returns all the flags which could be granted on the global level.
|
/// Returns all the flags which could be granted on the global level.
|
||||||
/// The same as allFlags().
|
/// The same as allFlags().
|
||||||
static AccessFlags allFlagsGrantableOnGlobalLevel();
|
static AccessFlags allFlagsGrantableOnGlobalLevel();
|
||||||
|
@ -12,7 +12,7 @@ enum class AccessType
|
|||||||
/// Macro M should be defined as M(name, aliases, node_type, parent_group_name)
|
/// Macro M should be defined as M(name, aliases, node_type, parent_group_name)
|
||||||
/// where name is identifier with underscores (instead of spaces);
|
/// where name is identifier with underscores (instead of spaces);
|
||||||
/// aliases is a string containing comma-separated list;
|
/// aliases is a string containing comma-separated list;
|
||||||
/// node_type either specifies access type's level (GLOBAL/NAMED_COLLECTION/USER_NAME/DATABASE/TABLE/DICTIONARY/VIEW/COLUMNS),
|
/// node_type either specifies access type's level (GLOBAL/NAMED_COLLECTION/DATABASE/TABLE/DICTIONARY/VIEW/COLUMNS),
|
||||||
/// or specifies that the access type is a GROUP of other access types;
|
/// or specifies that the access type is a GROUP of other access types;
|
||||||
/// parent_group_name is the name of the group containing this access type (or NONE if there is no such group).
|
/// parent_group_name is the name of the group containing this access type (or NONE if there is no such group).
|
||||||
/// NOTE A parent group must be declared AFTER all its children.
|
/// NOTE A parent group must be declared AFTER all its children.
|
||||||
@ -83,7 +83,6 @@ enum class AccessType
|
|||||||
M(ALTER_VIEW_REFRESH, "ALTER LIVE VIEW REFRESH, REFRESH VIEW", VIEW, ALTER_VIEW) \
|
M(ALTER_VIEW_REFRESH, "ALTER LIVE VIEW REFRESH, REFRESH VIEW", VIEW, ALTER_VIEW) \
|
||||||
M(ALTER_VIEW_MODIFY_QUERY, "ALTER TABLE MODIFY QUERY", VIEW, ALTER_VIEW) \
|
M(ALTER_VIEW_MODIFY_QUERY, "ALTER TABLE MODIFY QUERY", VIEW, ALTER_VIEW) \
|
||||||
M(ALTER_VIEW_MODIFY_REFRESH, "ALTER TABLE MODIFY QUERY", VIEW, ALTER_VIEW) \
|
M(ALTER_VIEW_MODIFY_REFRESH, "ALTER TABLE MODIFY QUERY", VIEW, ALTER_VIEW) \
|
||||||
M(ALTER_VIEW_MODIFY_SQL_SECURITY, "ALTER TABLE MODIFY SQL SECURITY", VIEW, ALTER_VIEW) \
|
|
||||||
M(ALTER_VIEW, "", GROUP, ALTER) /* allows to execute ALTER VIEW REFRESH, ALTER VIEW MODIFY QUERY, ALTER VIEW MODIFY REFRESH;
|
M(ALTER_VIEW, "", GROUP, ALTER) /* allows to execute ALTER VIEW REFRESH, ALTER VIEW MODIFY QUERY, ALTER VIEW MODIFY REFRESH;
|
||||||
implicitly enabled by the grant ALTER_TABLE */\
|
implicitly enabled by the grant ALTER_TABLE */\
|
||||||
\
|
\
|
||||||
@ -140,7 +139,6 @@ enum class AccessType
|
|||||||
M(CREATE_SETTINGS_PROFILE, "CREATE PROFILE", GLOBAL, ACCESS_MANAGEMENT) \
|
M(CREATE_SETTINGS_PROFILE, "CREATE PROFILE", GLOBAL, ACCESS_MANAGEMENT) \
|
||||||
M(ALTER_SETTINGS_PROFILE, "ALTER PROFILE", GLOBAL, ACCESS_MANAGEMENT) \
|
M(ALTER_SETTINGS_PROFILE, "ALTER PROFILE", GLOBAL, ACCESS_MANAGEMENT) \
|
||||||
M(DROP_SETTINGS_PROFILE, "DROP PROFILE", GLOBAL, ACCESS_MANAGEMENT) \
|
M(DROP_SETTINGS_PROFILE, "DROP PROFILE", GLOBAL, ACCESS_MANAGEMENT) \
|
||||||
M(ALLOW_SQL_SECURITY_NONE, "CREATE SQL SECURITY NONE, ALLOW SQL SECURITY NONE, SQL SECURITY NONE, SECURITY NONE", GLOBAL, ACCESS_MANAGEMENT) \
|
|
||||||
M(SHOW_USERS, "SHOW CREATE USER", GLOBAL, SHOW_ACCESS) \
|
M(SHOW_USERS, "SHOW CREATE USER", GLOBAL, SHOW_ACCESS) \
|
||||||
M(SHOW_ROLES, "SHOW CREATE ROLE", GLOBAL, SHOW_ACCESS) \
|
M(SHOW_ROLES, "SHOW CREATE ROLE", GLOBAL, SHOW_ACCESS) \
|
||||||
M(SHOW_ROW_POLICIES, "SHOW POLICIES, SHOW CREATE ROW POLICY, SHOW CREATE POLICY", TABLE, SHOW_ACCESS) \
|
M(SHOW_ROW_POLICIES, "SHOW POLICIES, SHOW CREATE ROW POLICY, SHOW CREATE POLICY", TABLE, SHOW_ACCESS) \
|
||||||
@ -152,7 +150,6 @@ enum class AccessType
|
|||||||
M(SHOW_NAMED_COLLECTIONS_SECRETS, "SHOW NAMED COLLECTIONS SECRETS", NAMED_COLLECTION, NAMED_COLLECTION_ADMIN) \
|
M(SHOW_NAMED_COLLECTIONS_SECRETS, "SHOW NAMED COLLECTIONS SECRETS", NAMED_COLLECTION, NAMED_COLLECTION_ADMIN) \
|
||||||
M(NAMED_COLLECTION, "NAMED COLLECTION USAGE, USE NAMED COLLECTION", NAMED_COLLECTION, NAMED_COLLECTION_ADMIN) \
|
M(NAMED_COLLECTION, "NAMED COLLECTION USAGE, USE NAMED COLLECTION", NAMED_COLLECTION, NAMED_COLLECTION_ADMIN) \
|
||||||
M(NAMED_COLLECTION_ADMIN, "NAMED COLLECTION CONTROL", NAMED_COLLECTION, ALL) \
|
M(NAMED_COLLECTION_ADMIN, "NAMED COLLECTION CONTROL", NAMED_COLLECTION, ALL) \
|
||||||
M(SET_DEFINER, "", USER_NAME, ALL) \
|
|
||||||
\
|
\
|
||||||
M(SYSTEM_SHUTDOWN, "SYSTEM KILL, SHUTDOWN", GLOBAL, SYSTEM) \
|
M(SYSTEM_SHUTDOWN, "SYSTEM KILL, SHUTDOWN", GLOBAL, SYSTEM) \
|
||||||
M(SYSTEM_DROP_DNS_CACHE, "SYSTEM DROP DNS, DROP DNS CACHE, DROP DNS", GLOBAL, SYSTEM_DROP_CACHE) \
|
M(SYSTEM_DROP_DNS_CACHE, "SYSTEM DROP DNS, DROP DNS CACHE, DROP DNS", GLOBAL, SYSTEM_DROP_CACHE) \
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
#include <Core/Types.h>
|
|
||||||
|
|
||||||
enum class SQLSecurityType
|
|
||||||
{
|
|
||||||
INVOKER,
|
|
||||||
DEFINER,
|
|
||||||
NONE,
|
|
||||||
};
|
|
@ -53,8 +53,7 @@ TEST(AccessRights, Union)
|
|||||||
"SHOW ROW POLICIES, SYSTEM MERGES, SYSTEM TTL MERGES, SYSTEM FETCHES, "
|
"SHOW ROW POLICIES, SYSTEM MERGES, SYSTEM TTL MERGES, SYSTEM FETCHES, "
|
||||||
"SYSTEM MOVES, SYSTEM PULLING REPLICATION LOG, SYSTEM CLEANUP, SYSTEM VIEWS, SYSTEM SENDS, SYSTEM REPLICATION QUEUES, "
|
"SYSTEM MOVES, SYSTEM PULLING REPLICATION LOG, SYSTEM CLEANUP, SYSTEM VIEWS, SYSTEM SENDS, SYSTEM REPLICATION QUEUES, "
|
||||||
"SYSTEM DROP REPLICA, SYSTEM SYNC REPLICA, SYSTEM RESTART REPLICA, "
|
"SYSTEM DROP REPLICA, SYSTEM SYNC REPLICA, SYSTEM RESTART REPLICA, "
|
||||||
"SYSTEM RESTORE REPLICA, SYSTEM WAIT LOADING PARTS, SYSTEM SYNC DATABASE REPLICA, SYSTEM FLUSH DISTRIBUTED, dictGet ON db1.*, "
|
"SYSTEM RESTORE REPLICA, SYSTEM WAIT LOADING PARTS, SYSTEM SYNC DATABASE REPLICA, SYSTEM FLUSH DISTRIBUTED, dictGet ON db1.*, GRANT NAMED COLLECTION ADMIN ON db1");
|
||||||
"GRANT SET DEFINER ON db1, GRANT NAMED COLLECTION ADMIN ON db1");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5118,6 +5118,15 @@ ProjectionNames QueryAnalyzer::resolveFunction(QueryTreeNodePtr & node, Identifi
|
|||||||
true /*allow_lambda_expression*/,
|
true /*allow_lambda_expression*/,
|
||||||
allow_table_expressions /*allow_table_expression*/);
|
allow_table_expressions /*allow_table_expression*/);
|
||||||
|
|
||||||
|
if (function_node_ptr->toAST()->hasSecretParts())
|
||||||
|
{
|
||||||
|
for (auto & argument : arguments_projection_names)
|
||||||
|
{
|
||||||
|
SipHash hash;
|
||||||
|
hash.update(argument);
|
||||||
|
argument = getHexUIntLowercase(hash.get128());
|
||||||
|
}
|
||||||
|
}
|
||||||
auto & function_node = *function_node_ptr;
|
auto & function_node = *function_node_ptr;
|
||||||
|
|
||||||
/// Replace right IN function argument if it is table or table function with subquery that read ordinary columns
|
/// Replace right IN function argument if it is table or table function with subquery that read ordinary columns
|
||||||
|
@ -868,9 +868,6 @@ class IColumn;
|
|||||||
M(Bool, print_pretty_type_names, true, "Print pretty type names in DESCRIBE query and toTypeName() function", 0) \
|
M(Bool, print_pretty_type_names, true, "Print pretty type names in DESCRIBE query and toTypeName() function", 0) \
|
||||||
M(Bool, create_table_empty_primary_key_by_default, false, "Allow to create *MergeTree tables with empty primary key when ORDER BY and PRIMARY KEY not specified", 0) \
|
M(Bool, create_table_empty_primary_key_by_default, false, "Allow to create *MergeTree tables with empty primary key when ORDER BY and PRIMARY KEY not specified", 0) \
|
||||||
M(Bool, allow_named_collection_override_by_default, true, "Allow named collections' fields override by default.", 0)\
|
M(Bool, allow_named_collection_override_by_default, true, "Allow named collections' fields override by default.", 0)\
|
||||||
M(SQLSecurityType, default_normal_view_sql_security, SQLSecurityType::INVOKER, "Allows to set a default value for SQL SECURITY option when creating a normal view.", 0) \
|
|
||||||
M(SQLSecurityType, default_materialized_view_sql_security, SQLSecurityType::DEFINER, "Allows to set a default value for SQL SECURITY option when creating a materialized view.", 0) \
|
|
||||||
M(String, default_view_definer, "CURRENT_USER", "Allows to set a default value for DEFINER option when creating view.", 0) \
|
|
||||||
M(Bool, allow_experimental_shared_merge_tree, false, "Only available in ClickHouse Cloud", 0) \
|
M(Bool, allow_experimental_shared_merge_tree, false, "Only available in ClickHouse Cloud", 0) \
|
||||||
M(UInt64, cache_warmer_threads, 4, "Only available in ClickHouse Cloud", 0) \
|
M(UInt64, cache_warmer_threads, 4, "Only available in ClickHouse Cloud", 0) \
|
||||||
M(Int64, ignore_cold_parts_seconds, 0, "Only available in ClickHouse Cloud", 0) \
|
M(Int64, ignore_cold_parts_seconds, 0, "Only available in ClickHouse Cloud", 0) \
|
||||||
|
@ -103,10 +103,7 @@ static std::map<ClickHouseVersion, SettingsChangesHistory::SettingsChanges> sett
|
|||||||
{"min_external_table_block_size_bytes", DEFAULT_INSERT_BLOCK_SIZE * 256, DEFAULT_INSERT_BLOCK_SIZE * 256, "Squash blocks passed to external table to specified size in bytes, if blocks are not big enough."},
|
{"min_external_table_block_size_bytes", DEFAULT_INSERT_BLOCK_SIZE * 256, DEFAULT_INSERT_BLOCK_SIZE * 256, "Squash blocks passed to external table to specified size in bytes, if blocks are not big enough."},
|
||||||
{"parallel_replicas_prefer_local_join", true, true, "If true, and JOIN can be executed with parallel replicas algorithm, and all storages of right JOIN part are *MergeTree, local JOIN will be used instead of GLOBAL JOIN."},
|
{"parallel_replicas_prefer_local_join", true, true, "If true, and JOIN can be executed with parallel replicas algorithm, and all storages of right JOIN part are *MergeTree, local JOIN will be used instead of GLOBAL JOIN."},
|
||||||
{"extract_key_value_pairs_max_pairs_per_row", 0, 0, "Max number of pairs that can be produced by the `extractKeyValuePairs` function. Used as a safeguard against consuming too much memory."},
|
{"extract_key_value_pairs_max_pairs_per_row", 0, 0, "Max number of pairs that can be produced by the `extractKeyValuePairs` function. Used as a safeguard against consuming too much memory."},
|
||||||
{"async_insert_busy_timeout_decrease_rate", 0.2, 0.2, "The exponential growth rate at which the adaptive asynchronous insert timeout decreases"},
|
}},
|
||||||
{"default_view_definer", "", "CURRENT_USER", "Allows to set default `DEFINER` option while creating a view"},
|
|
||||||
{"default_materialized_view_sql_security", "INVOKER", "DEFINER", "Allows to set a default value for SQL SECURITY option when creating a materialized view"},
|
|
||||||
{"default_normal_view_sql_security", "INVOKER", "INVOKER", "Allows to set default `SQL SECURITY` option while creating a normal view"}}},
|
|
||||||
{"24.1", {{"print_pretty_type_names", false, true, "Better user experience."},
|
{"24.1", {{"print_pretty_type_names", false, true, "Better user experience."},
|
||||||
{"input_format_json_read_bools_as_strings", false, true, "Allow to read bools as strings in JSON formats by default"},
|
{"input_format_json_read_bools_as_strings", false, true, "Allow to read bools as strings in JSON formats by default"},
|
||||||
{"output_format_arrow_use_signed_indexes_for_dictionary", false, true, "Use signed indexes type for Arrow dictionaries by default as it's recommended"},
|
{"output_format_arrow_use_signed_indexes_for_dictionary", false, true, "Use signed indexes type for Arrow dictionaries by default as it's recommended"},
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
#include <Core/SettingsEnums.h>
|
#include <Core/SettingsEnums.h>
|
||||||
#include <magic_enum.hpp>
|
#include <magic_enum.hpp>
|
||||||
#include <Access/Common/SQLSecurityDefs.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -207,9 +206,4 @@ IMPLEMENT_SETTING_ENUM(DateTimeOverflowBehavior, ErrorCodes::BAD_ARGUMENTS,
|
|||||||
{{"throw", FormatSettings::DateTimeOverflowBehavior::Throw},
|
{{"throw", FormatSettings::DateTimeOverflowBehavior::Throw},
|
||||||
{"ignore", FormatSettings::DateTimeOverflowBehavior::Ignore},
|
{"ignore", FormatSettings::DateTimeOverflowBehavior::Ignore},
|
||||||
{"saturate", FormatSettings::DateTimeOverflowBehavior::Saturate}})
|
{"saturate", FormatSettings::DateTimeOverflowBehavior::Saturate}})
|
||||||
|
|
||||||
IMPLEMENT_SETTING_ENUM(SQLSecurityType, ErrorCodes::BAD_ARGUMENTS,
|
|
||||||
{{"DEFINER", SQLSecurityType::DEFINER},
|
|
||||||
{"INVOKER", SQLSecurityType::INVOKER},
|
|
||||||
{"NONE", SQLSecurityType::NONE}})
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@
|
|||||||
#include <Formats/FormatSettings.h>
|
#include <Formats/FormatSettings.h>
|
||||||
#include <IO/ReadSettings.h>
|
#include <IO/ReadSettings.h>
|
||||||
#include <Common/ShellCommandSettings.h>
|
#include <Common/ShellCommandSettings.h>
|
||||||
#include <Parsers/ASTSQLSecurity.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
@ -267,5 +266,4 @@ DECLARE_SETTING_ENUM(SchemaInferenceMode)
|
|||||||
|
|
||||||
DECLARE_SETTING_ENUM_WITH_RENAME(DateTimeOverflowBehavior, FormatSettings::DateTimeOverflowBehavior)
|
DECLARE_SETTING_ENUM_WITH_RENAME(DateTimeOverflowBehavior, FormatSettings::DateTimeOverflowBehavior)
|
||||||
|
|
||||||
DECLARE_SETTING_ENUM(SQLSecurityType)
|
|
||||||
}
|
}
|
||||||
|
@ -261,7 +261,7 @@ void AsynchronousInsertQueue::preprocessInsertQuery(const ASTPtr & query, const
|
|||||||
|
|
||||||
InterpreterInsertQuery interpreter(query, query_context, query_context->getSettingsRef().insert_allow_materialized_columns);
|
InterpreterInsertQuery interpreter(query, query_context, query_context->getSettingsRef().insert_allow_materialized_columns);
|
||||||
auto table = interpreter.getTable(insert_query);
|
auto table = interpreter.getTable(insert_query);
|
||||||
auto sample_block = interpreter.getSampleBlock(insert_query, table, table->getInMemoryMetadataPtr(), query_context);
|
auto sample_block = interpreter.getSampleBlock(insert_query, table, table->getInMemoryMetadataPtr());
|
||||||
|
|
||||||
if (!FormatFactory::instance().isInputFormat(insert_query.format))
|
if (!FormatFactory::instance().isInputFormat(insert_query.format))
|
||||||
throw Exception(ErrorCodes::UNKNOWN_FORMAT, "Unknown input format {}", insert_query.format);
|
throw Exception(ErrorCodes::UNKNOWN_FORMAT, "Unknown input format {}", insert_query.format);
|
||||||
|
@ -794,7 +794,6 @@ ContextMutablePtr Context::createGlobal(ContextSharedPart * shared_part)
|
|||||||
{
|
{
|
||||||
auto res = std::shared_ptr<Context>(new Context);
|
auto res = std::shared_ptr<Context>(new Context);
|
||||||
res->shared = shared_part;
|
res->shared = shared_part;
|
||||||
res->query_access_info = std::make_shared<QueryAccessInfo>();
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -814,9 +813,7 @@ SharedContextHolder Context::createShared()
|
|||||||
ContextMutablePtr Context::createCopy(const ContextPtr & other)
|
ContextMutablePtr Context::createCopy(const ContextPtr & other)
|
||||||
{
|
{
|
||||||
SharedLockGuard lock(other->mutex);
|
SharedLockGuard lock(other->mutex);
|
||||||
auto new_context = std::shared_ptr<Context>(new Context(*other));
|
return std::shared_ptr<Context>(new Context(*other));
|
||||||
new_context->query_access_info = std::make_shared<QueryAccessInfo>(*other->query_access_info);
|
|
||||||
return new_context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ContextMutablePtr Context::createCopy(const ContextWeakPtr & other)
|
ContextMutablePtr Context::createCopy(const ContextWeakPtr & other)
|
||||||
@ -1610,12 +1607,12 @@ void Context::addQueryAccessInfo(
|
|||||||
if (isGlobalContext())
|
if (isGlobalContext())
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Global context cannot have query access info");
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Global context cannot have query access info");
|
||||||
|
|
||||||
std::lock_guard lock(query_access_info->mutex);
|
std::lock_guard lock(query_access_info.mutex);
|
||||||
query_access_info->databases.emplace(quoted_database_name);
|
query_access_info.databases.emplace(quoted_database_name);
|
||||||
query_access_info->tables.emplace(full_quoted_table_name);
|
query_access_info.tables.emplace(full_quoted_table_name);
|
||||||
|
|
||||||
for (const auto & column_name : column_names)
|
for (const auto & column_name : column_names)
|
||||||
query_access_info->columns.emplace(full_quoted_table_name + "." + backQuoteIfNeed(column_name));
|
query_access_info.columns.emplace(full_quoted_table_name + "." + backQuoteIfNeed(column_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::addQueryAccessInfo(const Names & partition_names)
|
void Context::addQueryAccessInfo(const Names & partition_names)
|
||||||
@ -1623,9 +1620,9 @@ void Context::addQueryAccessInfo(const Names & partition_names)
|
|||||||
if (isGlobalContext())
|
if (isGlobalContext())
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Global context cannot have query access info");
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Global context cannot have query access info");
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(query_access_info->mutex);
|
std::lock_guard<std::mutex> lock(query_access_info.mutex);
|
||||||
for (const auto & partition_name : partition_names)
|
for (const auto & partition_name : partition_names)
|
||||||
query_access_info->partitions.emplace(partition_name);
|
query_access_info.partitions.emplace(partition_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::addViewAccessInfo(const String & view_name)
|
void Context::addViewAccessInfo(const String & view_name)
|
||||||
@ -1633,8 +1630,8 @@ void Context::addViewAccessInfo(const String & view_name)
|
|||||||
if (isGlobalContext())
|
if (isGlobalContext())
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Global context cannot have query access info");
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Global context cannot have query access info");
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(query_access_info->mutex);
|
std::lock_guard<std::mutex> lock(query_access_info.mutex);
|
||||||
query_access_info->views.emplace(view_name);
|
query_access_info.views.emplace(view_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::addQueryAccessInfo(const QualifiedProjectionName & qualified_projection_name)
|
void Context::addQueryAccessInfo(const QualifiedProjectionName & qualified_projection_name)
|
||||||
@ -1645,8 +1642,8 @@ void Context::addQueryAccessInfo(const QualifiedProjectionName & qualified_proje
|
|||||||
if (isGlobalContext())
|
if (isGlobalContext())
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Global context cannot have query access info");
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Global context cannot have query access info");
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(query_access_info->mutex);
|
std::lock_guard<std::mutex> lock(query_access_info.mutex);
|
||||||
query_access_info->projections.emplace(fmt::format(
|
query_access_info.projections.emplace(fmt::format(
|
||||||
"{}.{}", qualified_projection_name.storage_id.getFullTableName(), backQuoteIfNeed(qualified_projection_name.projection_name)));
|
"{}.{}", qualified_projection_name.storage_id.getFullTableName(), backQuoteIfNeed(qualified_projection_name.projection_name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2297,8 +2294,7 @@ void Context::setMacros(std::unique_ptr<Macros> && macros)
|
|||||||
ContextMutablePtr Context::getQueryContext() const
|
ContextMutablePtr Context::getQueryContext() const
|
||||||
{
|
{
|
||||||
auto ptr = query_context.lock();
|
auto ptr = query_context.lock();
|
||||||
if (!ptr)
|
if (!ptr) throw Exception(ErrorCodes::THERE_IS_NO_QUERY, "There is no query or query context has expired");
|
||||||
throw Exception(ErrorCodes::THERE_IS_NO_QUERY, "There is no query or query context has expired");
|
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,11 +350,8 @@ protected:
|
|||||||
std::set<std::string> projections{};
|
std::set<std::string> projections{};
|
||||||
std::set<std::string> views{};
|
std::set<std::string> views{};
|
||||||
};
|
};
|
||||||
using QueryAccessInfoPtr = std::shared_ptr<QueryAccessInfo>;
|
|
||||||
|
|
||||||
/// In some situations, we want to be able to transfer the access info from children back to parents (e.g. definers context).
|
QueryAccessInfo query_access_info;
|
||||||
/// Therefore, query_access_info must be a pointer.
|
|
||||||
QueryAccessInfoPtr query_access_info;
|
|
||||||
|
|
||||||
/// Record names of created objects of factories (for testing, etc)
|
/// Record names of created objects of factories (for testing, etc)
|
||||||
struct QueryFactoriesInfo
|
struct QueryFactoriesInfo
|
||||||
@ -679,9 +676,7 @@ public:
|
|||||||
const Block * tryGetSpecialScalar(const String & name) const;
|
const Block * tryGetSpecialScalar(const String & name) const;
|
||||||
void addSpecialScalar(const String & name, const Block & block);
|
void addSpecialScalar(const String & name, const Block & block);
|
||||||
|
|
||||||
const QueryAccessInfo & getQueryAccessInfo() const { return *getQueryAccessInfoPtr(); }
|
const QueryAccessInfo & getQueryAccessInfo() const { return query_access_info; }
|
||||||
const QueryAccessInfoPtr getQueryAccessInfoPtr() const { return query_access_info; }
|
|
||||||
void setQueryAccessInfo(QueryAccessInfoPtr other) { query_access_info = other; }
|
|
||||||
|
|
||||||
void addQueryAccessInfo(
|
void addQueryAccessInfo(
|
||||||
const String & quoted_database_name,
|
const String & quoted_database_name,
|
||||||
|
@ -8,7 +8,6 @@
|
|||||||
#include <Interpreters/AddDefaultDatabaseVisitor.h>
|
#include <Interpreters/AddDefaultDatabaseVisitor.h>
|
||||||
#include <Interpreters/Context.h>
|
#include <Interpreters/Context.h>
|
||||||
#include <Interpreters/FunctionNameNormalizer.h>
|
#include <Interpreters/FunctionNameNormalizer.h>
|
||||||
#include <Interpreters/InterpreterCreateQuery.h>
|
|
||||||
#include <Interpreters/MutationsInterpreter.h>
|
#include <Interpreters/MutationsInterpreter.h>
|
||||||
#include <Interpreters/MutationsNonDeterministicHelpers.h>
|
#include <Interpreters/MutationsNonDeterministicHelpers.h>
|
||||||
#include <Interpreters/QueryLog.h>
|
#include <Interpreters/QueryLog.h>
|
||||||
@ -72,13 +71,6 @@ BlockIO InterpreterAlterQuery::execute()
|
|||||||
|
|
||||||
BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter)
|
BlockIO InterpreterAlterQuery::executeToTable(const ASTAlterQuery & alter)
|
||||||
{
|
{
|
||||||
for (auto & child : alter.command_list->children)
|
|
||||||
{
|
|
||||||
auto * command_ast = child->as<ASTAlterCommand>();
|
|
||||||
if (command_ast->sql_security)
|
|
||||||
InterpreterCreateQuery::processSQLSecurityOption(getContext(), command_ast->sql_security->as<ASTSQLSecurity &>());
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockIO res;
|
BlockIO res;
|
||||||
|
|
||||||
if (!UserDefinedSQLFunctionFactory::instance().empty())
|
if (!UserDefinedSQLFunctionFactory::instance().empty())
|
||||||
@ -496,11 +488,6 @@ AccessRightsElements InterpreterAlterQuery::getRequiredAccessForCommand(const AS
|
|||||||
required_access.emplace_back(AccessType::ALTER_MODIFY_COMMENT, database, table);
|
required_access.emplace_back(AccessType::ALTER_MODIFY_COMMENT, database, table);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ASTAlterCommand::MODIFY_SQL_SECURITY:
|
|
||||||
{
|
|
||||||
required_access.emplace_back(AccessType::ALTER_VIEW_MODIFY_SQL_SECURITY, database, table);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return required_access;
|
return required_access;
|
||||||
|
@ -2,9 +2,6 @@
|
|||||||
|
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
|
||||||
#include <Access/AccessControl.h>
|
|
||||||
#include <Access/User.h>
|
|
||||||
|
|
||||||
#include "Common/Exception.h"
|
#include "Common/Exception.h"
|
||||||
#include <Common/StringUtils/StringUtils.h>
|
#include <Common/StringUtils/StringUtils.h>
|
||||||
#include <Common/escapeForFileName.h>
|
#include <Common/escapeForFileName.h>
|
||||||
@ -1097,8 +1094,6 @@ BlockIO InterpreterCreateQuery::createTable(ASTCreateQuery & create)
|
|||||||
|
|
||||||
String current_database = getContext()->getCurrentDatabase();
|
String current_database = getContext()->getCurrentDatabase();
|
||||||
auto database_name = create.database ? create.getDatabase() : current_database;
|
auto database_name = create.database ? create.getDatabase() : current_database;
|
||||||
if (create.sql_security)
|
|
||||||
processSQLSecurityOption(getContext(), create.sql_security->as<ASTSQLSecurity &>(), create.attach, create.is_materialized_view);
|
|
||||||
|
|
||||||
DDLGuardPtr ddl_guard;
|
DDLGuardPtr ddl_guard;
|
||||||
|
|
||||||
@ -1888,61 +1883,6 @@ void InterpreterCreateQuery::addColumnsDescriptionToCreateQueryIfNecessary(ASTCr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void InterpreterCreateQuery::processSQLSecurityOption(ContextPtr context_, ASTSQLSecurity & sql_security, bool is_attach, bool is_materialized_view)
|
|
||||||
{
|
|
||||||
/// If no SQL security is specified, apply default from default_*_view_sql_security setting.
|
|
||||||
if (!sql_security.type.has_value())
|
|
||||||
{
|
|
||||||
SQLSecurityType default_security;
|
|
||||||
|
|
||||||
if (is_materialized_view)
|
|
||||||
default_security = context_->getSettingsRef().default_materialized_view_sql_security;
|
|
||||||
else
|
|
||||||
default_security = context_->getSettingsRef().default_normal_view_sql_security;
|
|
||||||
|
|
||||||
if (default_security == SQLSecurityType::DEFINER)
|
|
||||||
{
|
|
||||||
String default_definer = context_->getSettingsRef().default_view_definer;
|
|
||||||
if (default_definer == "CURRENT_USER")
|
|
||||||
sql_security.is_definer_current_user = true;
|
|
||||||
else
|
|
||||||
sql_security.definer = std::make_shared<ASTUserNameWithHost>(default_definer);
|
|
||||||
}
|
|
||||||
|
|
||||||
sql_security.type = default_security;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Resolves `DEFINER = CURRENT_USER`. Can change the SQL security type if we try to resolve the user during the attachment.
|
|
||||||
const auto current_user_name = context_->getUserName();
|
|
||||||
if (sql_security.is_definer_current_user)
|
|
||||||
{
|
|
||||||
if (current_user_name.empty())
|
|
||||||
/// This can happen only when attaching a view for the first time after migration and with `CURRENT_USER` default.
|
|
||||||
if (is_materialized_view)
|
|
||||||
sql_security.type = SQLSecurityType::NONE;
|
|
||||||
else
|
|
||||||
sql_security.type = SQLSecurityType::INVOKER;
|
|
||||||
else if (sql_security.definer)
|
|
||||||
sql_security.definer->replace(current_user_name);
|
|
||||||
else
|
|
||||||
sql_security.definer = std::make_shared<ASTUserNameWithHost>(current_user_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks the permissions for the specified definer user.
|
|
||||||
if (sql_security.definer && !sql_security.is_definer_current_user && !is_attach)
|
|
||||||
{
|
|
||||||
const auto definer_name = sql_security.definer->toString();
|
|
||||||
|
|
||||||
/// Validate that the user exists.
|
|
||||||
context_->getAccessControl().getID<User>(definer_name);
|
|
||||||
if (definer_name != current_user_name)
|
|
||||||
context_->checkAccess(AccessType::SET_DEFINER, definer_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sql_security.type == SQLSecurityType::NONE && !is_attach)
|
|
||||||
context_->checkAccess(AccessType::ALLOW_SQL_SECURITY_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void registerInterpreterCreateQuery(InterpreterFactory & factory)
|
void registerInterpreterCreateQuery(InterpreterFactory & factory)
|
||||||
{
|
{
|
||||||
auto create_fn = [] (const InterpreterFactory::Arguments & args)
|
auto create_fn = [] (const InterpreterFactory::Arguments & args)
|
||||||
|
@ -80,9 +80,6 @@ public:
|
|||||||
|
|
||||||
void extendQueryLogElemImpl(QueryLogElement & elem, const ASTPtr & ast, ContextPtr) const override;
|
void extendQueryLogElemImpl(QueryLogElement & elem, const ASTPtr & ast, ContextPtr) const override;
|
||||||
|
|
||||||
/// Check access right, validate definer statement and replace `CURRENT USER` with actual name.
|
|
||||||
static void processSQLSecurityOption(ContextPtr context_, ASTSQLSecurity & sql_security, bool is_attach = false, bool is_materialized_view = false);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct TableProperties
|
struct TableProperties
|
||||||
{
|
{
|
||||||
|
@ -125,10 +125,7 @@ StoragePtr InterpreterInsertQuery::getTable(ASTInsertQuery & query)
|
|||||||
Block InterpreterInsertQuery::getSampleBlock(
|
Block InterpreterInsertQuery::getSampleBlock(
|
||||||
const ASTInsertQuery & query,
|
const ASTInsertQuery & query,
|
||||||
const StoragePtr & table,
|
const StoragePtr & table,
|
||||||
const StorageMetadataPtr & metadata_snapshot,
|
const StorageMetadataPtr & metadata_snapshot) const
|
||||||
ContextPtr context_,
|
|
||||||
bool no_destination,
|
|
||||||
bool allow_materialized)
|
|
||||||
{
|
{
|
||||||
/// If the query does not include information about columns
|
/// If the query does not include information about columns
|
||||||
if (!query.columns)
|
if (!query.columns)
|
||||||
@ -142,7 +139,7 @@ Block InterpreterInsertQuery::getSampleBlock(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Form the block based on the column names from the query
|
/// Form the block based on the column names from the query
|
||||||
const auto columns_ast = processColumnTransformers(context_->getCurrentDatabase(), table, metadata_snapshot, query.columns);
|
const auto columns_ast = processColumnTransformers(getContext()->getCurrentDatabase(), table, metadata_snapshot, query.columns);
|
||||||
Names names;
|
Names names;
|
||||||
names.reserve(columns_ast->children.size());
|
names.reserve(columns_ast->children.size());
|
||||||
for (const auto & identifier : columns_ast->children)
|
for (const auto & identifier : columns_ast->children)
|
||||||
@ -151,7 +148,7 @@ Block InterpreterInsertQuery::getSampleBlock(
|
|||||||
names.emplace_back(std::move(current_name));
|
names.emplace_back(std::move(current_name));
|
||||||
}
|
}
|
||||||
|
|
||||||
return getSampleBlock(names, table, metadata_snapshot, allow_materialized);
|
return getSampleBlock(names, table, metadata_snapshot);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<Names> InterpreterInsertQuery::getInsertColumnNames() const
|
std::optional<Names> InterpreterInsertQuery::getInsertColumnNames() const
|
||||||
@ -176,8 +173,7 @@ std::optional<Names> InterpreterInsertQuery::getInsertColumnNames() const
|
|||||||
Block InterpreterInsertQuery::getSampleBlock(
|
Block InterpreterInsertQuery::getSampleBlock(
|
||||||
const Names & names,
|
const Names & names,
|
||||||
const StoragePtr & table,
|
const StoragePtr & table,
|
||||||
const StorageMetadataPtr & metadata_snapshot,
|
const StorageMetadataPtr & metadata_snapshot) const
|
||||||
bool allow_materialized)
|
|
||||||
{
|
{
|
||||||
Block table_sample_physical = metadata_snapshot->getSampleBlock();
|
Block table_sample_physical = metadata_snapshot->getSampleBlock();
|
||||||
Block table_sample_insertable = metadata_snapshot->getSampleBlockInsertable();
|
Block table_sample_insertable = metadata_snapshot->getSampleBlockInsertable();
|
||||||
@ -264,8 +260,7 @@ Chain InterpreterInsertQuery::buildChain(
|
|||||||
const StorageMetadataPtr & metadata_snapshot,
|
const StorageMetadataPtr & metadata_snapshot,
|
||||||
const Names & columns,
|
const Names & columns,
|
||||||
ThreadStatusesHolderPtr thread_status_holder,
|
ThreadStatusesHolderPtr thread_status_holder,
|
||||||
std::atomic_uint64_t * elapsed_counter_ms,
|
std::atomic_uint64_t * elapsed_counter_ms)
|
||||||
bool check_access)
|
|
||||||
{
|
{
|
||||||
ProfileEvents::increment(ProfileEvents::InsertQueriesWithSubqueries);
|
ProfileEvents::increment(ProfileEvents::InsertQueriesWithSubqueries);
|
||||||
ProfileEvents::increment(ProfileEvents::QueriesWithSubqueries);
|
ProfileEvents::increment(ProfileEvents::QueriesWithSubqueries);
|
||||||
@ -276,9 +271,7 @@ Chain InterpreterInsertQuery::buildChain(
|
|||||||
if (!running_group)
|
if (!running_group)
|
||||||
running_group = std::make_shared<ThreadGroup>(getContext());
|
running_group = std::make_shared<ThreadGroup>(getContext());
|
||||||
|
|
||||||
auto sample = getSampleBlock(columns, table, metadata_snapshot, allow_materialized);
|
auto sample = getSampleBlock(columns, table, metadata_snapshot);
|
||||||
if (check_access)
|
|
||||||
getContext()->checkAccess(AccessType::INSERT, table->getStorageID(), sample.getNames());
|
|
||||||
|
|
||||||
Chain sink = buildSink(table, metadata_snapshot, thread_status_holder, running_group, elapsed_counter_ms);
|
Chain sink = buildSink(table, metadata_snapshot, thread_status_holder, running_group, elapsed_counter_ms);
|
||||||
Chain chain = buildPreSinkChain(sink.getInputHeader(), table, metadata_snapshot, sample);
|
Chain chain = buildPreSinkChain(sink.getInputHeader(), table, metadata_snapshot, sample);
|
||||||
@ -404,7 +397,7 @@ BlockIO InterpreterInsertQuery::execute()
|
|||||||
auto table_lock = table->lockForShare(getContext()->getInitialQueryId(), settings.lock_acquire_timeout);
|
auto table_lock = table->lockForShare(getContext()->getInitialQueryId(), settings.lock_acquire_timeout);
|
||||||
auto metadata_snapshot = table->getInMemoryMetadataPtr();
|
auto metadata_snapshot = table->getInMemoryMetadataPtr();
|
||||||
|
|
||||||
auto query_sample_block = getSampleBlock(query, table, metadata_snapshot, getContext(), no_destination, allow_materialized);
|
auto query_sample_block = getSampleBlock(query, table, metadata_snapshot);
|
||||||
|
|
||||||
/// For table functions we check access while executing
|
/// For table functions we check access while executing
|
||||||
/// getTable() -> ITableFunction::execute().
|
/// getTable() -> ITableFunction::execute().
|
||||||
|
@ -46,21 +46,14 @@ public:
|
|||||||
const StorageMetadataPtr & metadata_snapshot,
|
const StorageMetadataPtr & metadata_snapshot,
|
||||||
const Names & columns,
|
const Names & columns,
|
||||||
ThreadStatusesHolderPtr thread_status_holder = {},
|
ThreadStatusesHolderPtr thread_status_holder = {},
|
||||||
std::atomic_uint64_t * elapsed_counter_ms = nullptr,
|
std::atomic_uint64_t * elapsed_counter_ms = nullptr);
|
||||||
bool check_access = false);
|
|
||||||
|
|
||||||
static void extendQueryLogElemImpl(QueryLogElement & elem, ContextPtr context_);
|
static void extendQueryLogElemImpl(QueryLogElement & elem, ContextPtr context_);
|
||||||
|
|
||||||
void extendQueryLogElemImpl(QueryLogElement & elem, const ASTPtr & ast, ContextPtr context_) const override;
|
void extendQueryLogElemImpl(QueryLogElement & elem, const ASTPtr & ast, ContextPtr context_) const override;
|
||||||
|
|
||||||
StoragePtr getTable(ASTInsertQuery & query);
|
StoragePtr getTable(ASTInsertQuery & query);
|
||||||
static Block getSampleBlock(
|
Block getSampleBlock(const ASTInsertQuery & query, const StoragePtr & table, const StorageMetadataPtr & metadata_snapshot) const;
|
||||||
const ASTInsertQuery & query,
|
|
||||||
const StoragePtr & table,
|
|
||||||
const StorageMetadataPtr & metadata_snapshot,
|
|
||||||
ContextPtr context_,
|
|
||||||
bool no_destination = false,
|
|
||||||
bool allow_materialized = false);
|
|
||||||
|
|
||||||
bool supportsTransactions() const override { return true; }
|
bool supportsTransactions() const override { return true; }
|
||||||
|
|
||||||
@ -69,7 +62,7 @@ public:
|
|||||||
bool shouldAddSquashingFroStorage(const StoragePtr & table) const;
|
bool shouldAddSquashingFroStorage(const StoragePtr & table) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static Block getSampleBlock(const Names & names, const StoragePtr & table, const StorageMetadataPtr & metadata_snapshot, bool allow_materialized);
|
Block getSampleBlock(const Names & names, const StoragePtr & table, const StorageMetadataPtr & metadata_snapshot) const;
|
||||||
|
|
||||||
ASTPtr query_ptr;
|
ASTPtr query_ptr;
|
||||||
const bool allow_materialized;
|
const bool allow_materialized;
|
||||||
|
@ -830,7 +830,7 @@ InterpreterSelectQuery::InterpreterSelectQuery(
|
|||||||
if (query.prewhere() && !query.where())
|
if (query.prewhere() && !query.where())
|
||||||
analysis_result.prewhere_info->need_filter = true;
|
analysis_result.prewhere_info->need_filter = true;
|
||||||
|
|
||||||
if (table_id && got_storage_from_query && !joined_tables.isLeftTableFunction() && !options.ignore_access_check)
|
if (table_id && got_storage_from_query && !joined_tables.isLeftTableFunction())
|
||||||
{
|
{
|
||||||
/// The current user should have the SELECT privilege. If this table_id is for a table
|
/// The current user should have the SELECT privilege. If this table_id is for a table
|
||||||
/// function we don't check access rights here because in this case they have been already
|
/// function we don't check access rights here because in this case they have been already
|
||||||
|
@ -46,10 +46,6 @@ struct SelectQueryOptions
|
|||||||
/// Bypass setting constraints for some internal queries such as projection ASTs.
|
/// Bypass setting constraints for some internal queries such as projection ASTs.
|
||||||
bool ignore_setting_constraints = false;
|
bool ignore_setting_constraints = false;
|
||||||
|
|
||||||
/// Bypass access check for select query.
|
|
||||||
/// This allows to skip double access check in some specific cases (e.g. insert into table with materialized view)
|
|
||||||
bool ignore_access_check = false;
|
|
||||||
|
|
||||||
/// These two fields are used to evaluate shardNum() and shardCount() function when
|
/// These two fields are used to evaluate shardNum() and shardCount() function when
|
||||||
/// prefer_localhost_replica == 1 and local instance is selected. They are needed because local
|
/// prefer_localhost_replica == 1 and local instance is selected. They are needed because local
|
||||||
/// instance might have multiple shards and scalars can only hold one value.
|
/// instance might have multiple shards and scalars can only hold one value.
|
||||||
@ -133,12 +129,6 @@ struct SelectQueryOptions
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
SelectQueryOptions & ignoreAccessCheck(bool value = true)
|
|
||||||
{
|
|
||||||
ignore_access_check = value;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
SelectQueryOptions & setInternal(bool value = false)
|
SelectQueryOptions & setInternal(bool value = false)
|
||||||
{
|
{
|
||||||
is_internal = value;
|
is_internal = value;
|
||||||
|
@ -479,11 +479,6 @@ void ASTAlterCommand::formatImpl(const FormatSettings & settings, FormatState &
|
|||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << " TO ";
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << " TO ";
|
||||||
rename_to->formatImpl(settings, state, frame);
|
rename_to->formatImpl(settings, state, frame);
|
||||||
}
|
}
|
||||||
else if (type == ASTAlterCommand::MODIFY_SQL_SECURITY)
|
|
||||||
{
|
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << "MODIFY " << (settings.hilite ? hilite_none : "");
|
|
||||||
sql_security->formatImpl(settings, state, frame);
|
|
||||||
}
|
|
||||||
else if (type == ASTAlterCommand::APPLY_DELETED_MASK)
|
else if (type == ASTAlterCommand::APPLY_DELETED_MASK)
|
||||||
{
|
{
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << "APPLY DELETED MASK" << (settings.hilite ? hilite_none : "");
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << "APPLY DELETED MASK" << (settings.hilite ? hilite_none : "");
|
||||||
|
@ -84,7 +84,6 @@ public:
|
|||||||
MODIFY_DATABASE_SETTING,
|
MODIFY_DATABASE_SETTING,
|
||||||
|
|
||||||
MODIFY_COMMENT,
|
MODIFY_COMMENT,
|
||||||
MODIFY_SQL_SECURITY,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type = NO_TYPE;
|
Type type = NO_TYPE;
|
||||||
@ -167,9 +166,6 @@ public:
|
|||||||
/// For MODIFY_QUERY
|
/// For MODIFY_QUERY
|
||||||
IAST * select = nullptr;
|
IAST * select = nullptr;
|
||||||
|
|
||||||
/// For MODIFY_SQL_SECURITY
|
|
||||||
IAST * sql_security = nullptr;
|
|
||||||
|
|
||||||
/// In ALTER CHANNEL, ADD, DROP, SUSPEND, RESUME, REFRESH, MODIFY queries, the list of live views is stored here
|
/// In ALTER CHANNEL, ADD, DROP, SUSPEND, RESUME, REFRESH, MODIFY queries, the list of live views is stored here
|
||||||
IAST * values = nullptr;
|
IAST * values = nullptr;
|
||||||
|
|
||||||
|
@ -12,37 +12,6 @@
|
|||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
|
|
||||||
void ASTSQLSecurity::formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const
|
|
||||||
{
|
|
||||||
if (!type.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (definer || is_definer_current_user)
|
|
||||||
{
|
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << "DEFINER" << (settings.hilite ? hilite_none : "");
|
|
||||||
settings.ostr << " = ";
|
|
||||||
if (definer)
|
|
||||||
definer->formatImpl(settings, state, frame);
|
|
||||||
else
|
|
||||||
settings.ostr << "CURRENT_USER";
|
|
||||||
settings.ostr << " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << "SQL SECURITY" << (settings.hilite ? hilite_none : "");
|
|
||||||
switch (*type)
|
|
||||||
{
|
|
||||||
case SQLSecurityType::INVOKER:
|
|
||||||
settings.ostr << " INVOKER";
|
|
||||||
break;
|
|
||||||
case SQLSecurityType::DEFINER:
|
|
||||||
settings.ostr << " DEFINER";
|
|
||||||
break;
|
|
||||||
case SQLSecurityType::NONE:
|
|
||||||
settings.ostr << " NONE";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ASTPtr ASTStorage::clone() const
|
ASTPtr ASTStorage::clone() const
|
||||||
{
|
{
|
||||||
auto res = std::make_shared<ASTStorage>(*this);
|
auto res = std::make_shared<ASTStorage>(*this);
|
||||||
@ -324,9 +293,10 @@ void ASTCreateQuery::formatQueryImpl(const FormatSettings & settings, FormatStat
|
|||||||
else if (is_window_view)
|
else if (is_window_view)
|
||||||
what = "WINDOW VIEW";
|
what = "WINDOW VIEW";
|
||||||
|
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << action << (settings.hilite ? hilite_none : "");
|
settings.ostr
|
||||||
settings.ostr << " ";
|
<< (settings.hilite ? hilite_keyword : "")
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << (temporary ? "TEMPORARY " : "")
|
<< action << " "
|
||||||
|
<< (temporary ? "TEMPORARY " : "")
|
||||||
<< what << " "
|
<< what << " "
|
||||||
<< (if_not_exists ? "IF NOT EXISTS " : "")
|
<< (if_not_exists ? "IF NOT EXISTS " : "")
|
||||||
<< (settings.hilite ? hilite_none : "");
|
<< (settings.hilite ? hilite_none : "");
|
||||||
@ -490,16 +460,10 @@ void ASTCreateQuery::formatQueryImpl(const FormatSettings & settings, FormatStat
|
|||||||
else if (is_create_empty)
|
else if (is_create_empty)
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << " EMPTY" << (settings.hilite ? hilite_none : "");
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << " EMPTY" << (settings.hilite ? hilite_none : "");
|
||||||
|
|
||||||
if (sql_security && sql_security->as<ASTSQLSecurity &>().type.has_value())
|
|
||||||
{
|
|
||||||
settings.ostr << settings.nl_or_ws;
|
|
||||||
sql_security->formatImpl(settings, state, frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (select)
|
if (select)
|
||||||
{
|
{
|
||||||
settings.ostr << settings.nl_or_ws;
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << " AS"
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << "AS "
|
<< settings.nl_or_ws
|
||||||
<< (comment ? "(" : "") << (settings.hilite ? hilite_none : "");
|
<< (comment ? "(" : "") << (settings.hilite ? hilite_none : "");
|
||||||
select->formatImpl(settings, state, frame);
|
select->formatImpl(settings, state, frame);
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << (comment ? ")" : "") << (settings.hilite ? hilite_none : "");
|
settings.ostr << (settings.hilite ? hilite_keyword : "") << (comment ? ")" : "") << (settings.hilite ? hilite_none : "");
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
#include <Parsers/ASTDictionary.h>
|
#include <Parsers/ASTDictionary.h>
|
||||||
#include <Parsers/ASTDictionaryAttributeDeclaration.h>
|
#include <Parsers/ASTDictionaryAttributeDeclaration.h>
|
||||||
#include <Parsers/ASTTableOverrides.h>
|
#include <Parsers/ASTTableOverrides.h>
|
||||||
#include <Parsers/ASTSQLSecurity.h>
|
|
||||||
#include <Parsers/ASTRefreshStrategy.h>
|
#include <Parsers/ASTRefreshStrategy.h>
|
||||||
#include <Interpreters/StorageID.h>
|
#include <Interpreters/StorageID.h>
|
||||||
|
|
||||||
@ -16,7 +15,6 @@ class ASTFunction;
|
|||||||
class ASTSetQuery;
|
class ASTSetQuery;
|
||||||
class ASTSelectWithUnionQuery;
|
class ASTSelectWithUnionQuery;
|
||||||
|
|
||||||
|
|
||||||
class ASTStorage : public IAST
|
class ASTStorage : public IAST
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -113,7 +111,6 @@ public:
|
|||||||
IAST * as_table_function = nullptr;
|
IAST * as_table_function = nullptr;
|
||||||
ASTSelectWithUnionQuery * select = nullptr;
|
ASTSelectWithUnionQuery * select = nullptr;
|
||||||
IAST * comment = nullptr;
|
IAST * comment = nullptr;
|
||||||
ASTPtr sql_security = nullptr;
|
|
||||||
|
|
||||||
ASTTableOverrideList * table_overrides = nullptr; /// For CREATE DATABASE with engines that automatically create tables
|
ASTTableOverrideList * table_overrides = nullptr; /// For CREATE DATABASE with engines that automatically create tables
|
||||||
|
|
||||||
|
@ -35,6 +35,11 @@ public:
|
|||||||
tryGetIdentifierNameInto(table, name);
|
tryGetIdentifierNameInto(table, name);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Table clone() const
|
||||||
|
{
|
||||||
|
return Table{.database = database->clone(), .table = table->clone()};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Element
|
struct Element
|
||||||
@ -61,6 +66,11 @@ public:
|
|||||||
{
|
{
|
||||||
auto res = std::make_shared<ASTRenameQuery>(*this);
|
auto res = std::make_shared<ASTRenameQuery>(*this);
|
||||||
cloneOutputOptions(*res);
|
cloneOutputOptions(*res);
|
||||||
|
for (auto & element : res->elements)
|
||||||
|
{
|
||||||
|
element.from = element.from.clone();
|
||||||
|
element.to = element.to.clone();
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
#include <Parsers/ASTSQLSecurity.h>
|
|
||||||
#include <IO/Operators.h>
|
|
||||||
|
|
||||||
namespace DB
|
|
||||||
{
|
|
||||||
|
|
||||||
void ASTSQLSecurity::formatImpl(const FormatSettings & settings, FormatState & state, FormatStateStacked frame) const
|
|
||||||
{
|
|
||||||
if (!type.has_value())
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (definer || is_definer_current_user)
|
|
||||||
{
|
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << "DEFINER" << (settings.hilite ? hilite_none : "");
|
|
||||||
settings.ostr << " = ";
|
|
||||||
if (definer)
|
|
||||||
definer->formatImpl(settings, state, frame);
|
|
||||||
else
|
|
||||||
settings.ostr << "CURRENT_USER";
|
|
||||||
settings.ostr << " ";
|
|
||||||
}
|
|
||||||
|
|
||||||
settings.ostr << (settings.hilite ? hilite_keyword : "") << "SQL SECURITY" << (settings.hilite ? hilite_none : "");
|
|
||||||
switch (*type)
|
|
||||||
{
|
|
||||||
case SQLSecurityType::INVOKER:
|
|
||||||
settings.ostr << " INVOKER";
|
|
||||||
break;
|
|
||||||
case SQLSecurityType::DEFINER:
|
|
||||||
settings.ostr << " DEFINER";
|
|
||||||
break;
|
|
||||||
case SQLSecurityType::NONE:
|
|
||||||
settings.ostr << " NONE";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <Parsers/Access/ASTUserNameWithHost.h>
|
|
||||||
#include <Access/Common/SQLSecurityDefs.h>
|
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
|
||||||
{
|
|
||||||
|
|
||||||
/// DEFINER = <user_name | CURRENT_USER> SQL SECURITY <DEFINER | INVOKER | NONE>
|
|
||||||
/// If type was not set during parsing, the default type from settings will be used.
|
|
||||||
/// Currently supports only views.
|
|
||||||
class ASTSQLSecurity : public IAST
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool is_definer_current_user{false};
|
|
||||||
std::shared_ptr<ASTUserNameWithHost> definer = nullptr;
|
|
||||||
std::optional<SQLSecurityType> type = std::nullopt;
|
|
||||||
|
|
||||||
String getID(char) const override { return "View SQL Security"; }
|
|
||||||
ASTPtr clone() const override { return std::make_shared<ASTSQLSecurity>(*this); }
|
|
||||||
|
|
||||||
void formatImpl(const FormatSettings & s, FormatState & state, FormatStateStacked frame) const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -28,12 +28,6 @@ void ASTUserNameWithHost::concatParts()
|
|||||||
host_pattern.clear();
|
host_pattern.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ASTUserNameWithHost::replace(const String name_)
|
|
||||||
{
|
|
||||||
base_name = name_;
|
|
||||||
host_pattern.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void ASTUserNamesWithHost::formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
|
void ASTUserNamesWithHost::formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const
|
||||||
{
|
{
|
||||||
|
@ -27,7 +27,6 @@ public:
|
|||||||
String getID(char) const override { return "UserNameWithHost"; }
|
String getID(char) const override { return "UserNameWithHost"; }
|
||||||
ASTPtr clone() const override { return std::make_shared<ASTUserNameWithHost>(*this); }
|
ASTPtr clone() const override { return std::make_shared<ASTUserNameWithHost>(*this); }
|
||||||
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override;
|
||||||
void replace(const String name_);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -40,7 +40,6 @@ bool ParserAlterCommand::parseImpl(Pos & pos, ASTPtr & node, Expected & expected
|
|||||||
ParserKeyword s_modify_setting("MODIFY SETTING");
|
ParserKeyword s_modify_setting("MODIFY SETTING");
|
||||||
ParserKeyword s_reset_setting("RESET SETTING");
|
ParserKeyword s_reset_setting("RESET SETTING");
|
||||||
ParserKeyword s_modify_query("MODIFY QUERY");
|
ParserKeyword s_modify_query("MODIFY QUERY");
|
||||||
ParserKeyword s_modify_sql_security("MODIFY SQL SECURITY");
|
|
||||||
ParserKeyword s_modify_refresh("MODIFY REFRESH");
|
ParserKeyword s_modify_refresh("MODIFY REFRESH");
|
||||||
|
|
||||||
ParserKeyword s_add_index("ADD INDEX");
|
ParserKeyword s_add_index("ADD INDEX");
|
||||||
@ -143,7 +142,6 @@ bool ParserAlterCommand::parseImpl(Pos & pos, ASTPtr & node, Expected & expected
|
|||||||
/* allow_empty = */ false);
|
/* allow_empty = */ false);
|
||||||
ParserNameList values_p;
|
ParserNameList values_p;
|
||||||
ParserSelectWithUnionQuery select_p;
|
ParserSelectWithUnionQuery select_p;
|
||||||
ParserSQLSecurity sql_security_p;
|
|
||||||
ParserRefreshStrategy refresh_p;
|
ParserRefreshStrategy refresh_p;
|
||||||
ParserTTLExpressionList parser_ttl_list;
|
ParserTTLExpressionList parser_ttl_list;
|
||||||
|
|
||||||
@ -168,7 +166,6 @@ bool ParserAlterCommand::parseImpl(Pos & pos, ASTPtr & node, Expected & expected
|
|||||||
ASTPtr command_select;
|
ASTPtr command_select;
|
||||||
ASTPtr command_values;
|
ASTPtr command_values;
|
||||||
ASTPtr command_rename_to;
|
ASTPtr command_rename_to;
|
||||||
ASTPtr command_sql_security;
|
|
||||||
|
|
||||||
if (with_round_bracket)
|
if (with_round_bracket)
|
||||||
{
|
{
|
||||||
@ -877,14 +874,6 @@ bool ParserAlterCommand::parseImpl(Pos & pos, ASTPtr & node, Expected & expected
|
|||||||
return false;
|
return false;
|
||||||
command->type = ASTAlterCommand::MODIFY_QUERY;
|
command->type = ASTAlterCommand::MODIFY_QUERY;
|
||||||
}
|
}
|
||||||
else if (s_modify_sql_security.ignore(pos, expected))
|
|
||||||
{
|
|
||||||
/// This is a hack so we can reuse parser from create and don't have to write `MODIFY SQL SECURITY SQL SECURITY INVOKER`
|
|
||||||
pos -= 2;
|
|
||||||
if (!sql_security_p.parse(pos, command_sql_security, expected))
|
|
||||||
return false;
|
|
||||||
command->type = ASTAlterCommand::MODIFY_SQL_SECURITY;
|
|
||||||
}
|
|
||||||
else if (s_modify_refresh.ignore(pos, expected))
|
else if (s_modify_refresh.ignore(pos, expected))
|
||||||
{
|
{
|
||||||
if (!refresh_p.parse(pos, command->refresh, expected))
|
if (!refresh_p.parse(pos, command->refresh, expected))
|
||||||
@ -959,8 +948,6 @@ bool ParserAlterCommand::parseImpl(Pos & pos, ASTPtr & node, Expected & expected
|
|||||||
command->select = command->children.emplace_back(std::move(command_select)).get();
|
command->select = command->children.emplace_back(std::move(command_select)).get();
|
||||||
if (command_values)
|
if (command_values)
|
||||||
command->values = command->children.emplace_back(std::move(command_values)).get();
|
command->values = command->children.emplace_back(std::move(command_values)).get();
|
||||||
if (command_sql_security)
|
|
||||||
command->sql_security = command->children.emplace_back(std::move(command_sql_security)).get();
|
|
||||||
if (command_rename_to)
|
if (command_rename_to)
|
||||||
command->rename_to = command->children.emplace_back(std::move(command_rename_to)).get();
|
command->rename_to = command->children.emplace_back(std::move(command_rename_to)).get();
|
||||||
|
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
#include <IO/ReadHelpers.h>
|
#include <IO/ReadHelpers.h>
|
||||||
#include <Parsers/Access/ParserUserNameWithHost.h>
|
|
||||||
#include <Parsers/ASTConstraintDeclaration.h>
|
#include <Parsers/ASTConstraintDeclaration.h>
|
||||||
#include <Parsers/ASTCreateQuery.h>
|
#include <Parsers/ASTCreateQuery.h>
|
||||||
#include <Parsers/ASTExpressionList.h>
|
#include <Parsers/ASTExpressionList.h>
|
||||||
@ -85,65 +84,6 @@ bool ParserNestedTable::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ParserSQLSecurity::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
|
||||||
{
|
|
||||||
ParserToken s_eq(TokenType::Equals);
|
|
||||||
ParserKeyword s_definer("DEFINER");
|
|
||||||
|
|
||||||
bool is_definer_current_user = false;
|
|
||||||
ASTPtr definer;
|
|
||||||
std::optional<SQLSecurityType> type;
|
|
||||||
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
if (!definer && s_definer.ignore(pos, expected))
|
|
||||||
{
|
|
||||||
s_eq.ignore(pos, expected);
|
|
||||||
if (ParserKeyword{"CURRENT_USER"}.ignore(pos, expected))
|
|
||||||
is_definer_current_user = true;
|
|
||||||
else if (!ParserUserNameWithHost{}.parse(pos, definer, expected))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!type && ParserKeyword{"SQL SECURITY"}.ignore(pos, expected))
|
|
||||||
{
|
|
||||||
if (s_definer.ignore(pos, expected))
|
|
||||||
type = SQLSecurityType::DEFINER;
|
|
||||||
else if (ParserKeyword{"INVOKER"}.ignore(pos, expected))
|
|
||||||
type = SQLSecurityType::INVOKER;
|
|
||||||
else if (ParserKeyword{"NONE"}.ignore(pos, expected))
|
|
||||||
type = SQLSecurityType::NONE;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!type)
|
|
||||||
{
|
|
||||||
if (is_definer_current_user || definer)
|
|
||||||
type = SQLSecurityType::DEFINER;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else if (type == SQLSecurityType::DEFINER && !definer)
|
|
||||||
is_definer_current_user = true;
|
|
||||||
|
|
||||||
auto result = std::make_shared<ASTSQLSecurity>();
|
|
||||||
result->is_definer_current_user = is_definer_current_user;
|
|
||||||
result->type = type;
|
|
||||||
if (definer)
|
|
||||||
result->definer = typeid_cast<std::shared_ptr<ASTUserNameWithHost>>(definer);
|
|
||||||
|
|
||||||
node = std::move(result);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ParserIdentifierWithParameters::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
bool ParserIdentifierWithParameters::parseImpl(Pos & pos, ASTPtr & node, Expected & expected)
|
||||||
{
|
{
|
||||||
@ -909,7 +849,6 @@ bool ParserCreateLiveViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & e
|
|||||||
ParserStorage storage_inner{ParserStorage::TABLE_ENGINE};
|
ParserStorage storage_inner{ParserStorage::TABLE_ENGINE};
|
||||||
ParserTablePropertiesDeclarationList table_properties_p;
|
ParserTablePropertiesDeclarationList table_properties_p;
|
||||||
ParserSelectWithUnionQuery select_p;
|
ParserSelectWithUnionQuery select_p;
|
||||||
ParserSQLSecurity sql_security_p;
|
|
||||||
|
|
||||||
ASTPtr table;
|
ASTPtr table;
|
||||||
ASTPtr to_table;
|
ASTPtr to_table;
|
||||||
@ -918,7 +857,6 @@ bool ParserCreateLiveViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & e
|
|||||||
ASTPtr as_table;
|
ASTPtr as_table;
|
||||||
ASTPtr select;
|
ASTPtr select;
|
||||||
ASTPtr live_view_periodic_refresh;
|
ASTPtr live_view_periodic_refresh;
|
||||||
ASTPtr sql_security;
|
|
||||||
|
|
||||||
String cluster_str;
|
String cluster_str;
|
||||||
bool attach = false;
|
bool attach = false;
|
||||||
@ -935,8 +873,6 @@ bool ParserCreateLiveViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & e
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
sql_security_p.parse(pos, sql_security, expected);
|
|
||||||
|
|
||||||
if (!s_live.ignore(pos, expected))
|
if (!s_live.ignore(pos, expected))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -989,9 +925,6 @@ bool ParserCreateLiveViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & e
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sql_security && !sql_security_p.parse(pos, sql_security, expected))
|
|
||||||
sql_security = std::make_shared<ASTSQLSecurity>();
|
|
||||||
|
|
||||||
/// AS SELECT ...
|
/// AS SELECT ...
|
||||||
if (!s_as.ignore(pos, expected))
|
if (!s_as.ignore(pos, expected))
|
||||||
return false;
|
return false;
|
||||||
@ -1034,9 +967,6 @@ bool ParserCreateLiveViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & e
|
|||||||
if (comment)
|
if (comment)
|
||||||
query->set(query->comment, comment);
|
query->set(query->comment, comment);
|
||||||
|
|
||||||
if (sql_security)
|
|
||||||
query->sql_security = typeid_cast<std::shared_ptr<ASTSQLSecurity>>(sql_security);
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1454,7 +1384,6 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
|||||||
ParserTablePropertiesDeclarationList table_properties_p;
|
ParserTablePropertiesDeclarationList table_properties_p;
|
||||||
ParserSelectWithUnionQuery select_p;
|
ParserSelectWithUnionQuery select_p;
|
||||||
ParserNameList names_p;
|
ParserNameList names_p;
|
||||||
ParserSQLSecurity sql_security_p;
|
|
||||||
|
|
||||||
ASTPtr table;
|
ASTPtr table;
|
||||||
ASTPtr to_table;
|
ASTPtr to_table;
|
||||||
@ -1464,7 +1393,6 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
|||||||
ASTPtr as_database;
|
ASTPtr as_database;
|
||||||
ASTPtr as_table;
|
ASTPtr as_table;
|
||||||
ASTPtr select;
|
ASTPtr select;
|
||||||
ASTPtr sql_security;
|
|
||||||
ASTPtr refresh_strategy;
|
ASTPtr refresh_strategy;
|
||||||
|
|
||||||
String cluster_str;
|
String cluster_str;
|
||||||
@ -1490,8 +1418,6 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
|||||||
replace_view = true;
|
replace_view = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sql_security_p.parse(pos, sql_security, expected);
|
|
||||||
|
|
||||||
if (!replace_view && s_materialized.ignore(pos, expected))
|
if (!replace_view && s_materialized.ignore(pos, expected))
|
||||||
{
|
{
|
||||||
is_materialized_view = true;
|
is_materialized_view = true;
|
||||||
@ -1584,9 +1510,6 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sql_security && !sql_security_p.parse(pos, sql_security, expected))
|
|
||||||
sql_security = std::make_shared<ASTSQLSecurity>();
|
|
||||||
|
|
||||||
/// AS SELECT ...
|
/// AS SELECT ...
|
||||||
if (!s_as.ignore(pos, expected))
|
if (!s_as.ignore(pos, expected))
|
||||||
return false;
|
return false;
|
||||||
@ -1629,7 +1552,6 @@ bool ParserCreateViewQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
|
|||||||
query->set(query->refresh_strategy, refresh_strategy);
|
query->set(query->refresh_strategy, refresh_strategy);
|
||||||
if (comment)
|
if (comment)
|
||||||
query->set(query->comment, comment);
|
query->set(query->comment, comment);
|
||||||
query->sql_security = typeid_cast<std::shared_ptr<ASTSQLSecurity>>(sql_security);
|
|
||||||
|
|
||||||
tryGetIdentifierNameInto(as_database, query->as_database);
|
tryGetIdentifierNameInto(as_database, query->as_database);
|
||||||
tryGetIdentifierNameInto(as_table, query->as_table);
|
tryGetIdentifierNameInto(as_table, query->as_table);
|
||||||
|
@ -25,14 +25,6 @@ protected:
|
|||||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Parses sql security option. DEFINER = user_name SQL SECURITY DEFINER
|
|
||||||
*/
|
|
||||||
class ParserSQLSecurity : public IParserBase
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
const char * getName() const override { return "sql security"; }
|
|
||||||
bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Storage engine or Codec. For example:
|
/** Storage engine or Codec. For example:
|
||||||
* Memory()
|
* Memory()
|
||||||
|
@ -62,18 +62,6 @@ public:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE TokenIterator & operator-=(int value)
|
|
||||||
{
|
|
||||||
index -= value;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE TokenIterator & operator+=(int value)
|
|
||||||
{
|
|
||||||
index += value;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
ALWAYS_INLINE bool operator<(const TokenIterator & rhs) const { return index < rhs.index; }
|
ALWAYS_INLINE bool operator<(const TokenIterator & rhs) const { return index < rhs.index; }
|
||||||
ALWAYS_INLINE bool operator<=(const TokenIterator & rhs) const { return index <= rhs.index; }
|
ALWAYS_INLINE bool operator<=(const TokenIterator & rhs) const { return index <= rhs.index; }
|
||||||
ALWAYS_INLINE bool operator==(const TokenIterator & rhs) const { return index == rhs.index; }
|
ALWAYS_INLINE bool operator==(const TokenIterator & rhs) const { return index == rhs.index; }
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
#include <Common/ConcurrencyControl.h>
|
#include <Common/ConcurrencyControl.h>
|
||||||
|
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <mutex>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
|
||||||
|
@ -188,244 +188,6 @@ private:
|
|||||||
std::exception_ptr any_exception;
|
std::exception_ptr any_exception;
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Generates one chain part for every view in buildPushingToViewsChain
|
|
||||||
std::optional<Chain> generateViewChain(
|
|
||||||
ContextPtr context,
|
|
||||||
const StorageID & view_id,
|
|
||||||
ThreadGroupPtr running_group,
|
|
||||||
Chain & result_chain,
|
|
||||||
ViewsDataPtr views_data,
|
|
||||||
ThreadStatusesHolderPtr thread_status_holder,
|
|
||||||
bool async_insert,
|
|
||||||
const Block & storage_header,
|
|
||||||
bool disable_deduplication_for_children)
|
|
||||||
{
|
|
||||||
auto view = DatabaseCatalog::instance().tryGetTable(view_id, context);
|
|
||||||
if (view == nullptr)
|
|
||||||
{
|
|
||||||
LOG_WARNING(
|
|
||||||
getLogger("PushingToViews"), "Trying to access table {} but it doesn't exist", view_id.getFullTableName());
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto view_metadata_snapshot = view->getInMemoryMetadataPtr();
|
|
||||||
auto select_context = view_metadata_snapshot->getSQLSecurityOverriddenContext(context);
|
|
||||||
select_context->setQueryAccessInfo(context->getQueryAccessInfoPtr());
|
|
||||||
|
|
||||||
auto insert_context = Context::createCopy(select_context);
|
|
||||||
|
|
||||||
const auto & insert_settings = insert_context->getSettingsRef();
|
|
||||||
|
|
||||||
// Do not deduplicate insertions into MV if the main insertion is Ok
|
|
||||||
if (disable_deduplication_for_children)
|
|
||||||
{
|
|
||||||
insert_context->setSetting("insert_deduplicate", Field{false});
|
|
||||||
}
|
|
||||||
else if (insert_settings.update_insert_deduplication_token_in_dependent_materialized_views &&
|
|
||||||
!insert_settings.insert_deduplication_token.value.empty())
|
|
||||||
{
|
|
||||||
/** Update deduplication token passed to dependent MV with current view id. So it is possible to properly handle
|
|
||||||
* deduplication in complex INSERT flows.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
*
|
|
||||||
* landing -┬--> mv_1_1 ---> ds_1_1 ---> mv_2_1 --┬-> ds_2_1 ---> mv_3_1 ---> ds_3_1
|
|
||||||
* | |
|
|
||||||
* └--> mv_1_2 ---> ds_1_2 ---> mv_2_2 --┘
|
|
||||||
*
|
|
||||||
* Here we want to avoid deduplication for two different blocks generated from `mv_2_1` and `mv_2_2` that will
|
|
||||||
* be inserted into `ds_2_1`.
|
|
||||||
*
|
|
||||||
* We are forced to use view id instead of table id because there are some possible INSERT flows where no tables
|
|
||||||
* are involved.
|
|
||||||
*
|
|
||||||
* Example:
|
|
||||||
*
|
|
||||||
* landing -┬--> mv_1_1 --┬-> ds_1_1
|
|
||||||
* | |
|
|
||||||
* └--> mv_1_2 --┘
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
auto insert_deduplication_token = insert_settings.insert_deduplication_token.value;
|
|
||||||
|
|
||||||
if (view_id.hasUUID())
|
|
||||||
insert_deduplication_token += "_" + toString(view_id.uuid);
|
|
||||||
else
|
|
||||||
insert_deduplication_token += "_" + view_id.getFullNameNotQuoted();
|
|
||||||
|
|
||||||
insert_context->setSetting("insert_deduplication_token", insert_deduplication_token);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Processing of blocks for MVs is done block by block, and there will
|
|
||||||
// be no parallel reading after (plus it is not a costless operation)
|
|
||||||
select_context->setSetting("parallelize_output_from_storages", Field{false});
|
|
||||||
|
|
||||||
// Separate min_insert_block_size_rows/min_insert_block_size_bytes for children
|
|
||||||
if (insert_settings.min_insert_block_size_rows_for_materialized_views)
|
|
||||||
insert_context->setSetting("min_insert_block_size_rows", insert_settings.min_insert_block_size_rows_for_materialized_views.value);
|
|
||||||
if (insert_settings.min_insert_block_size_bytes_for_materialized_views)
|
|
||||||
insert_context->setSetting("min_insert_block_size_bytes", insert_settings.min_insert_block_size_bytes_for_materialized_views.value);
|
|
||||||
|
|
||||||
ASTPtr query;
|
|
||||||
Chain out;
|
|
||||||
|
|
||||||
/// We are creating a ThreadStatus per view to store its metrics individually
|
|
||||||
/// Since calling ThreadStatus() changes current_thread we save it and restore it after the calls
|
|
||||||
/// Later on, before doing any task related to a view, we'll switch to its ThreadStatus, do the work,
|
|
||||||
/// and switch back to the original thread_status.
|
|
||||||
auto * original_thread = current_thread;
|
|
||||||
SCOPE_EXIT({ current_thread = original_thread; });
|
|
||||||
current_thread = nullptr;
|
|
||||||
std::unique_ptr<ThreadStatus> view_thread_status_ptr = std::make_unique<ThreadStatus>(/*check_current_thread_on_destruction=*/ false);
|
|
||||||
/// Copy of a ThreadStatus should be internal.
|
|
||||||
view_thread_status_ptr->setInternalThread();
|
|
||||||
view_thread_status_ptr->attachToGroup(running_group);
|
|
||||||
|
|
||||||
auto * view_thread_status = view_thread_status_ptr.get();
|
|
||||||
views_data->thread_status_holder->thread_statuses.push_front(std::move(view_thread_status_ptr));
|
|
||||||
|
|
||||||
auto runtime_stats = std::make_unique<QueryViewsLogElement::ViewRuntimeStats>();
|
|
||||||
runtime_stats->target_name = view_id.getFullTableName();
|
|
||||||
runtime_stats->thread_status = view_thread_status;
|
|
||||||
runtime_stats->event_time = std::chrono::system_clock::now();
|
|
||||||
runtime_stats->event_status = QueryViewsLogElement::ViewStatus::EXCEPTION_BEFORE_START;
|
|
||||||
|
|
||||||
auto & type = runtime_stats->type;
|
|
||||||
auto & target_name = runtime_stats->target_name;
|
|
||||||
auto * view_counter_ms = &runtime_stats->elapsed_ms;
|
|
||||||
|
|
||||||
if (auto * materialized_view = dynamic_cast<StorageMaterializedView *>(view.get()))
|
|
||||||
{
|
|
||||||
auto lock = materialized_view->tryLockForShare(context->getInitialQueryId(), context->getSettingsRef().lock_acquire_timeout);
|
|
||||||
|
|
||||||
if (lock == nullptr)
|
|
||||||
{
|
|
||||||
// In case the materialized view is dropped/detached at this point, we register a warning and ignore it
|
|
||||||
assert(materialized_view->is_dropped || materialized_view->is_detached);
|
|
||||||
LOG_WARNING(
|
|
||||||
getLogger("PushingToViews"), "Trying to access table {} but it doesn't exist", view_id.getFullTableName());
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
type = QueryViewsLogElement::ViewType::MATERIALIZED;
|
|
||||||
result_chain.addTableLock(lock);
|
|
||||||
|
|
||||||
StoragePtr inner_table = materialized_view->tryGetTargetTable();
|
|
||||||
/// If target table was dropped, ignore this materialized view.
|
|
||||||
if (!inner_table)
|
|
||||||
{
|
|
||||||
if (context->getSettingsRef().ignore_materialized_views_with_dropped_target_table)
|
|
||||||
return std::nullopt;
|
|
||||||
|
|
||||||
throw Exception(
|
|
||||||
ErrorCodes::UNKNOWN_TABLE,
|
|
||||||
"Target table '{}' of view '{}' doesn't exists. To ignore this view use setting "
|
|
||||||
"ignore_materialized_views_with_dropped_target_table",
|
|
||||||
materialized_view->getTargetTableId().getFullTableName(),
|
|
||||||
view_id.getFullTableName());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto inner_table_id = inner_table->getStorageID();
|
|
||||||
auto inner_metadata_snapshot = inner_table->getInMemoryMetadataPtr();
|
|
||||||
|
|
||||||
const auto & select_query = view_metadata_snapshot->getSelectQuery();
|
|
||||||
if (select_query.select_table_id != views_data->source_storage_id)
|
|
||||||
{
|
|
||||||
/// It may happen if materialize view query was changed and it doesn't depend on this source table anymore.
|
|
||||||
/// See setting `allow_experimental_alter_materialized_view_structure`
|
|
||||||
LOG_DEBUG(
|
|
||||||
getLogger("PushingToViews"), "Table '{}' is not a source for view '{}' anymore, current source is '{}'",
|
|
||||||
select_query.select_table_id.getFullTableName(), view_id.getFullTableName(), views_data->source_storage_id);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
query = select_query.inner_query;
|
|
||||||
|
|
||||||
target_name = inner_table_id.getFullTableName();
|
|
||||||
|
|
||||||
Block header;
|
|
||||||
|
|
||||||
/// Get list of columns we get from select query.
|
|
||||||
if (select_context->getSettingsRef().allow_experimental_analyzer)
|
|
||||||
header = InterpreterSelectQueryAnalyzer::getSampleBlock(query, select_context);
|
|
||||||
else
|
|
||||||
header = InterpreterSelectQuery(query, select_context, SelectQueryOptions()).getSampleBlock();
|
|
||||||
|
|
||||||
/// Insert only columns returned by select.
|
|
||||||
Names insert_columns;
|
|
||||||
const auto & inner_table_columns = inner_metadata_snapshot->getColumns();
|
|
||||||
for (const auto & column : header)
|
|
||||||
{
|
|
||||||
/// But skip columns which storage doesn't have.
|
|
||||||
if (inner_table_columns.hasNotAlias(column.name))
|
|
||||||
insert_columns.emplace_back(column.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
InterpreterInsertQuery interpreter(nullptr, insert_context, false, false, false);
|
|
||||||
out = interpreter.buildChain(inner_table, inner_metadata_snapshot, insert_columns, thread_status_holder, view_counter_ms, !materialized_view->hasInnerTable());
|
|
||||||
|
|
||||||
if (interpreter.shouldAddSquashingFroStorage(inner_table))
|
|
||||||
{
|
|
||||||
bool table_prefers_large_blocks = inner_table->prefersLargeBlocks();
|
|
||||||
const auto & settings = insert_context->getSettingsRef();
|
|
||||||
|
|
||||||
out.addSource(std::make_shared<SquashingChunksTransform>(
|
|
||||||
out.getInputHeader(),
|
|
||||||
table_prefers_large_blocks ? settings.min_insert_block_size_rows : settings.max_block_size,
|
|
||||||
table_prefers_large_blocks ? settings.min_insert_block_size_bytes : 0ULL));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto counting = std::make_shared<CountingTransform>(out.getInputHeader(), current_thread, insert_context->getQuota());
|
|
||||||
counting->setProcessListElement(insert_context->getProcessListElement());
|
|
||||||
counting->setProgressCallback(insert_context->getProgressCallback());
|
|
||||||
out.addSource(std::move(counting));
|
|
||||||
|
|
||||||
out.addStorageHolder(view);
|
|
||||||
out.addStorageHolder(inner_table);
|
|
||||||
}
|
|
||||||
else if (auto * live_view = dynamic_cast<StorageLiveView *>(view.get()))
|
|
||||||
{
|
|
||||||
runtime_stats->type = QueryViewsLogElement::ViewType::LIVE;
|
|
||||||
query = live_view->getInnerQuery();
|
|
||||||
out = buildPushingToViewsChain(
|
|
||||||
view, view_metadata_snapshot, insert_context, ASTPtr(),
|
|
||||||
/* no_destination= */ true,
|
|
||||||
thread_status_holder, running_group, view_counter_ms, async_insert, storage_header);
|
|
||||||
}
|
|
||||||
else if (auto * window_view = dynamic_cast<StorageWindowView *>(view.get()))
|
|
||||||
{
|
|
||||||
runtime_stats->type = QueryViewsLogElement::ViewType::WINDOW;
|
|
||||||
query = window_view->getMergeableQuery();
|
|
||||||
out = buildPushingToViewsChain(
|
|
||||||
view, view_metadata_snapshot, insert_context, ASTPtr(),
|
|
||||||
/* no_destination= */ true,
|
|
||||||
thread_status_holder, running_group, view_counter_ms, async_insert);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
out = buildPushingToViewsChain(
|
|
||||||
view, view_metadata_snapshot, insert_context, ASTPtr(),
|
|
||||||
/* no_destination= */ false,
|
|
||||||
thread_status_holder, running_group, view_counter_ms, async_insert);
|
|
||||||
|
|
||||||
views_data->views.emplace_back(ViewRuntimeData{
|
|
||||||
std::move(query),
|
|
||||||
out.getInputHeader(),
|
|
||||||
view_id,
|
|
||||||
nullptr,
|
|
||||||
std::move(runtime_stats)});
|
|
||||||
|
|
||||||
if (type == QueryViewsLogElement::ViewType::MATERIALIZED)
|
|
||||||
{
|
|
||||||
auto executing_inner_query = std::make_shared<ExecutingInnerQueryFromViewTransform>(
|
|
||||||
storage_header, views_data->views.back(), views_data);
|
|
||||||
executing_inner_query->setRuntimeData(view_thread_status, view_counter_ms);
|
|
||||||
|
|
||||||
out.addSource(std::move(executing_inner_query));
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Chain buildPushingToViewsChain(
|
Chain buildPushingToViewsChain(
|
||||||
const StoragePtr & storage,
|
const StoragePtr & storage,
|
||||||
@ -470,45 +232,259 @@ Chain buildPushingToViewsChain(
|
|||||||
auto table_id = storage->getStorageID();
|
auto table_id = storage->getStorageID();
|
||||||
auto views = DatabaseCatalog::instance().getDependentViews(table_id);
|
auto views = DatabaseCatalog::instance().getDependentViews(table_id);
|
||||||
|
|
||||||
|
/// We need special context for materialized views insertions
|
||||||
|
ContextMutablePtr select_context;
|
||||||
|
ContextMutablePtr insert_context;
|
||||||
ViewsDataPtr views_data;
|
ViewsDataPtr views_data;
|
||||||
if (!views.empty())
|
if (!views.empty())
|
||||||
{
|
{
|
||||||
auto process_context = Context::createCopy(context); /// This context will be used in `process` function
|
select_context = Context::createCopy(context);
|
||||||
views_data = std::make_shared<ViewsData>(thread_status_holder, process_context, table_id, metadata_snapshot, storage);
|
insert_context = Context::createCopy(context);
|
||||||
|
|
||||||
|
const auto & insert_settings = insert_context->getSettingsRef();
|
||||||
|
|
||||||
|
// Do not deduplicate insertions into MV if the main insertion is Ok
|
||||||
|
if (disable_deduplication_for_children)
|
||||||
|
{
|
||||||
|
insert_context->setSetting("insert_deduplicate", Field{false});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing of blocks for MVs is done block by block, and there will
|
||||||
|
// be no parallel reading after (plus it is not a costless operation)
|
||||||
|
select_context->setSetting("parallelize_output_from_storages", Field{false});
|
||||||
|
|
||||||
|
// Separate min_insert_block_size_rows/min_insert_block_size_bytes for children
|
||||||
|
if (insert_settings.min_insert_block_size_rows_for_materialized_views)
|
||||||
|
insert_context->setSetting("min_insert_block_size_rows", insert_settings.min_insert_block_size_rows_for_materialized_views.value);
|
||||||
|
if (insert_settings.min_insert_block_size_bytes_for_materialized_views)
|
||||||
|
insert_context->setSetting("min_insert_block_size_bytes", insert_settings.min_insert_block_size_bytes_for_materialized_views.value);
|
||||||
|
|
||||||
|
views_data = std::make_shared<ViewsData>(thread_status_holder, select_context, table_id, metadata_snapshot, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Chain> chains;
|
std::vector<Chain> chains;
|
||||||
|
|
||||||
for (const auto & view_id : views)
|
for (const auto & view_id : views)
|
||||||
{
|
{
|
||||||
try
|
auto view = DatabaseCatalog::instance().tryGetTable(view_id, context);
|
||||||
|
if (view == nullptr)
|
||||||
{
|
{
|
||||||
auto out = generateViewChain(
|
LOG_WARNING(
|
||||||
context, view_id, running_group, result_chain,
|
getLogger("PushingToViews"), "Trying to access table {} but it doesn't exist", view_id.getFullTableName());
|
||||||
views_data, thread_status_holder, async_insert, storage_header, disable_deduplication_for_children);
|
continue;
|
||||||
|
|
||||||
if (!out.has_value())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
chains.emplace_back(std::move(*out));
|
|
||||||
|
|
||||||
/// Add the view to the query access info so it can appear in system.query_log
|
|
||||||
/// hasQueryContext - for materialized tables with background replication process query context is not added
|
|
||||||
if (!no_destination && context->hasQueryContext())
|
|
||||||
{
|
|
||||||
context->getQueryContext()->addQueryAccessInfo(
|
|
||||||
backQuoteIfNeed(view_id.getDatabaseName()),
|
|
||||||
views_data->views.back().runtime_stats->target_name,
|
|
||||||
/*column_names=*/ {});
|
|
||||||
|
|
||||||
context->getQueryContext()->addViewAccessInfo(view_id.getFullTableName());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (const Exception & e)
|
|
||||||
|
auto view_metadata_snapshot = view->getInMemoryMetadataPtr();
|
||||||
|
|
||||||
|
ASTPtr query;
|
||||||
|
Chain out;
|
||||||
|
|
||||||
|
/// We are creating a ThreadStatus per view to store its metrics individually
|
||||||
|
/// Since calling ThreadStatus() changes current_thread we save it and restore it after the calls
|
||||||
|
/// Later on, before doing any task related to a view, we'll switch to its ThreadStatus, do the work,
|
||||||
|
/// and switch back to the original thread_status.
|
||||||
|
auto * original_thread = current_thread;
|
||||||
|
SCOPE_EXIT({ current_thread = original_thread; });
|
||||||
|
current_thread = nullptr;
|
||||||
|
std::unique_ptr<ThreadStatus> view_thread_status_ptr = std::make_unique<ThreadStatus>(/*check_current_thread_on_destruction=*/ false);
|
||||||
|
/// Copy of a ThreadStatus should be internal.
|
||||||
|
view_thread_status_ptr->setInternalThread();
|
||||||
|
view_thread_status_ptr->attachToGroup(running_group);
|
||||||
|
|
||||||
|
auto * view_thread_status = view_thread_status_ptr.get();
|
||||||
|
views_data->thread_status_holder->thread_statuses.push_front(std::move(view_thread_status_ptr));
|
||||||
|
|
||||||
|
auto runtime_stats = std::make_unique<QueryViewsLogElement::ViewRuntimeStats>();
|
||||||
|
runtime_stats->target_name = view_id.getFullTableName();
|
||||||
|
runtime_stats->thread_status = view_thread_status;
|
||||||
|
runtime_stats->event_time = std::chrono::system_clock::now();
|
||||||
|
runtime_stats->event_status = QueryViewsLogElement::ViewStatus::EXCEPTION_BEFORE_START;
|
||||||
|
|
||||||
|
auto & type = runtime_stats->type;
|
||||||
|
auto & target_name = runtime_stats->target_name;
|
||||||
|
auto * view_counter_ms = &runtime_stats->elapsed_ms;
|
||||||
|
|
||||||
|
const auto & insert_settings = insert_context->getSettingsRef();
|
||||||
|
ContextMutablePtr view_insert_context = insert_context;
|
||||||
|
|
||||||
|
if (!disable_deduplication_for_children &&
|
||||||
|
insert_settings.update_insert_deduplication_token_in_dependent_materialized_views &&
|
||||||
|
!insert_settings.insert_deduplication_token.value.empty())
|
||||||
{
|
{
|
||||||
LOG_ERROR(&Poco::Logger::get("PushingToViews"), "Failed to push block to view {}, {}", view_id, e.message());
|
/** Update deduplication token passed to dependent MV with current view id. So it is possible to properly handle
|
||||||
if (!context->getSettingsRef().materialized_views_ignore_errors)
|
* deduplication in complex INSERT flows.
|
||||||
throw;
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* landing -┬--> mv_1_1 ---> ds_1_1 ---> mv_2_1 --┬-> ds_2_1 ---> mv_3_1 ---> ds_3_1
|
||||||
|
* | |
|
||||||
|
* └--> mv_1_2 ---> ds_1_2 ---> mv_2_2 --┘
|
||||||
|
*
|
||||||
|
* Here we want to avoid deduplication for two different blocks generated from `mv_2_1` and `mv_2_2` that will
|
||||||
|
* be inserted into `ds_2_1`.
|
||||||
|
*
|
||||||
|
* We are forced to use view id instead of table id because there are some possible INSERT flows where no tables
|
||||||
|
* are involved.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
*
|
||||||
|
* landing -┬--> mv_1_1 --┬-> ds_1_1
|
||||||
|
* | |
|
||||||
|
* └--> mv_1_2 --┘
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
auto insert_deduplication_token = insert_settings.insert_deduplication_token.value;
|
||||||
|
|
||||||
|
if (view_id.hasUUID())
|
||||||
|
insert_deduplication_token += "_" + toString(view_id.uuid);
|
||||||
|
else
|
||||||
|
insert_deduplication_token += "_" + view_id.getFullNameNotQuoted();
|
||||||
|
|
||||||
|
view_insert_context = Context::createCopy(insert_context);
|
||||||
|
view_insert_context->setSetting("insert_deduplication_token", insert_deduplication_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auto * materialized_view = dynamic_cast<StorageMaterializedView *>(view.get()))
|
||||||
|
{
|
||||||
|
auto lock = materialized_view->tryLockForShare(context->getInitialQueryId(), context->getSettingsRef().lock_acquire_timeout);
|
||||||
|
|
||||||
|
if (lock == nullptr)
|
||||||
|
{
|
||||||
|
// In case the materialized view is dropped/detached at this point, we register a warning and ignore it
|
||||||
|
assert(materialized_view->is_dropped || materialized_view->is_detached);
|
||||||
|
LOG_WARNING(
|
||||||
|
getLogger("PushingToViews"), "Trying to access table {} but it doesn't exist", view_id.getFullTableName());
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
type = QueryViewsLogElement::ViewType::MATERIALIZED;
|
||||||
|
result_chain.addTableLock(lock);
|
||||||
|
|
||||||
|
StoragePtr inner_table = materialized_view->tryGetTargetTable();
|
||||||
|
/// If target table was dropped, ignore this materialized view.
|
||||||
|
if (!inner_table)
|
||||||
|
{
|
||||||
|
if (context->getSettingsRef().ignore_materialized_views_with_dropped_target_table)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
throw Exception(
|
||||||
|
ErrorCodes::UNKNOWN_TABLE,
|
||||||
|
"Target table '{}' of view '{}' doesn't exists. To ignore this view use setting "
|
||||||
|
"ignore_materialized_views_with_dropped_target_table",
|
||||||
|
materialized_view->getTargetTableId().getFullTableName(),
|
||||||
|
view_id.getFullTableName());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto inner_table_id = inner_table->getStorageID();
|
||||||
|
auto inner_metadata_snapshot = inner_table->getInMemoryMetadataPtr();
|
||||||
|
|
||||||
|
const auto & select_query = view_metadata_snapshot->getSelectQuery();
|
||||||
|
if (select_query.select_table_id != table_id)
|
||||||
|
{
|
||||||
|
/// It may happen if materialize view query was changed and it doesn't depend on this source table anymore.
|
||||||
|
/// See setting `allow_experimental_alter_materialized_view_structure`
|
||||||
|
LOG_DEBUG(
|
||||||
|
getLogger("PushingToViews"), "Table '{}' is not a source for view '{}' anymore, current source is '{}'",
|
||||||
|
select_query.select_table_id.getFullTableName(), view_id.getFullTableName(), table_id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
query = select_query.inner_query;
|
||||||
|
|
||||||
|
target_name = inner_table_id.getFullTableName();
|
||||||
|
|
||||||
|
Block header;
|
||||||
|
|
||||||
|
/// Get list of columns we get from select query.
|
||||||
|
if (select_context->getSettingsRef().allow_experimental_analyzer)
|
||||||
|
header = InterpreterSelectQueryAnalyzer::getSampleBlock(query, select_context);
|
||||||
|
else
|
||||||
|
header = InterpreterSelectQuery(query, select_context, SelectQueryOptions()).getSampleBlock();
|
||||||
|
|
||||||
|
/// Insert only columns returned by select.
|
||||||
|
Names insert_columns;
|
||||||
|
const auto & inner_table_columns = inner_metadata_snapshot->getColumns();
|
||||||
|
for (const auto & column : header)
|
||||||
|
{
|
||||||
|
/// But skip columns which storage doesn't have.
|
||||||
|
if (inner_table_columns.hasNotAlias(column.name))
|
||||||
|
insert_columns.emplace_back(column.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
InterpreterInsertQuery interpreter(nullptr, view_insert_context, false, false, false);
|
||||||
|
out = interpreter.buildChain(inner_table, inner_metadata_snapshot, insert_columns, thread_status_holder, view_counter_ms);
|
||||||
|
|
||||||
|
if (interpreter.shouldAddSquashingFroStorage(inner_table))
|
||||||
|
{
|
||||||
|
bool table_prefers_large_blocks = inner_table->prefersLargeBlocks();
|
||||||
|
const auto & settings = view_insert_context->getSettingsRef();
|
||||||
|
|
||||||
|
out.addSource(std::make_shared<SquashingChunksTransform>(
|
||||||
|
out.getInputHeader(),
|
||||||
|
table_prefers_large_blocks ? settings.min_insert_block_size_rows : settings.max_block_size,
|
||||||
|
table_prefers_large_blocks ? settings.min_insert_block_size_bytes : 0ULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto counting = std::make_shared<CountingTransform>(out.getInputHeader(), current_thread, view_insert_context->getQuota());
|
||||||
|
counting->setProcessListElement(view_insert_context->getProcessListElement());
|
||||||
|
counting->setProgressCallback(view_insert_context->getProgressCallback());
|
||||||
|
out.addSource(std::move(counting));
|
||||||
|
|
||||||
|
out.addStorageHolder(view);
|
||||||
|
out.addStorageHolder(inner_table);
|
||||||
|
}
|
||||||
|
else if (auto * live_view = dynamic_cast<StorageLiveView *>(view.get()))
|
||||||
|
{
|
||||||
|
runtime_stats->type = QueryViewsLogElement::ViewType::LIVE;
|
||||||
|
query = live_view->getInnerQuery(); // Used only to log in system.query_views_log
|
||||||
|
out = buildPushingToViewsChain(
|
||||||
|
view, view_metadata_snapshot, view_insert_context, ASTPtr(),
|
||||||
|
/* no_destination= */ true,
|
||||||
|
thread_status_holder, running_group, view_counter_ms, async_insert, storage_header);
|
||||||
|
}
|
||||||
|
else if (auto * window_view = dynamic_cast<StorageWindowView *>(view.get()))
|
||||||
|
{
|
||||||
|
runtime_stats->type = QueryViewsLogElement::ViewType::WINDOW;
|
||||||
|
query = window_view->getMergeableQuery(); // Used only to log in system.query_views_log
|
||||||
|
out = buildPushingToViewsChain(
|
||||||
|
view, view_metadata_snapshot, view_insert_context, ASTPtr(),
|
||||||
|
/* no_destination= */ true,
|
||||||
|
thread_status_holder, running_group, view_counter_ms, async_insert);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
out = buildPushingToViewsChain(
|
||||||
|
view, view_metadata_snapshot, view_insert_context, ASTPtr(),
|
||||||
|
/* no_destination= */ false,
|
||||||
|
thread_status_holder, running_group, view_counter_ms, async_insert);
|
||||||
|
|
||||||
|
views_data->views.emplace_back(ViewRuntimeData{
|
||||||
|
std::move(query),
|
||||||
|
out.getInputHeader(),
|
||||||
|
view_id,
|
||||||
|
nullptr,
|
||||||
|
std::move(runtime_stats)});
|
||||||
|
|
||||||
|
if (type == QueryViewsLogElement::ViewType::MATERIALIZED)
|
||||||
|
{
|
||||||
|
auto executing_inner_query = std::make_shared<ExecutingInnerQueryFromViewTransform>(
|
||||||
|
storage_header, views_data->views.back(), views_data);
|
||||||
|
executing_inner_query->setRuntimeData(view_thread_status, view_counter_ms);
|
||||||
|
|
||||||
|
out.addSource(std::move(executing_inner_query));
|
||||||
|
}
|
||||||
|
|
||||||
|
chains.emplace_back(std::move(out));
|
||||||
|
|
||||||
|
/// Add the view to the query access info so it can appear in system.query_log
|
||||||
|
/// hasQueryContext - for materialized tables with background replication process query context is not added
|
||||||
|
if (!no_destination && context->hasQueryContext())
|
||||||
|
{
|
||||||
|
context->getQueryContext()->addQueryAccessInfo(
|
||||||
|
backQuoteIfNeed(view_id.getDatabaseName()),
|
||||||
|
views_data->views.back().runtime_stats->target_name,
|
||||||
|
/*column_names=*/ {});
|
||||||
|
|
||||||
|
context->getQueryContext()->addViewAccessInfo(view_id.getFullTableName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -605,12 +581,12 @@ static QueryPipeline process(Block block, ViewRuntimeData & view, const ViewsDat
|
|||||||
|
|
||||||
if (local_context->getSettingsRef().allow_experimental_analyzer)
|
if (local_context->getSettingsRef().allow_experimental_analyzer)
|
||||||
{
|
{
|
||||||
InterpreterSelectQueryAnalyzer interpreter(view.query, local_context, local_context->getViewSource(), SelectQueryOptions().ignoreAccessCheck());
|
InterpreterSelectQueryAnalyzer interpreter(view.query, local_context, local_context->getViewSource(), SelectQueryOptions());
|
||||||
pipeline = interpreter.buildQueryPipeline();
|
pipeline = interpreter.buildQueryPipeline();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InterpreterSelectQuery interpreter(view.query, local_context, SelectQueryOptions().ignoreAccessCheck());
|
InterpreterSelectQuery interpreter(view.query, local_context, SelectQueryOptions());
|
||||||
pipeline = interpreter.buildQueryPipeline();
|
pipeline = interpreter.buildQueryPipeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@
|
|||||||
#include <Common/Stopwatch.h>
|
#include <Common/Stopwatch.h>
|
||||||
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
|
@ -32,7 +32,7 @@ class HTTPHandler : public HTTPRequestHandler
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
HTTPHandler(IServer & server_, const std::string & name, const std::optional<String> & content_type_override_);
|
HTTPHandler(IServer & server_, const std::string & name, const std::optional<String> & content_type_override_);
|
||||||
virtual ~HTTPHandler() override;
|
~HTTPHandler() override;
|
||||||
|
|
||||||
void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override;
|
void handleRequest(HTTPServerRequest & request, HTTPServerResponse & response, const ProfileEvents::Event & write_event) override;
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
#include <Core/Types.h>
|
#include <Core/Types.h>
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
|
@ -442,14 +442,6 @@ std::optional<AlterCommand> AlterCommand::parse(const ASTAlterCommand * command_
|
|||||||
command.if_exists = command_ast->if_exists;
|
command.if_exists = command_ast->if_exists;
|
||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
else if (command_ast->type == ASTAlterCommand::MODIFY_SQL_SECURITY)
|
|
||||||
{
|
|
||||||
AlterCommand command;
|
|
||||||
command.ast = command_ast->clone();
|
|
||||||
command.type = AlterCommand::MODIFY_SQL_SECURITY;
|
|
||||||
command.sql_security = command_ast->sql_security->clone();
|
|
||||||
return command;
|
|
||||||
}
|
|
||||||
else
|
else
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
@ -862,8 +854,6 @@ void AlterCommand::apply(StorageInMemoryMetadata & metadata, ContextPtr context)
|
|||||||
for (auto & index : metadata.secondary_indices)
|
for (auto & index : metadata.secondary_indices)
|
||||||
rename_visitor.visit(index.definition_ast);
|
rename_visitor.visit(index.definition_ast);
|
||||||
}
|
}
|
||||||
else if (type == MODIFY_SQL_SECURITY)
|
|
||||||
metadata.setDefiner(sql_security->as<ASTSQLSecurity &>());
|
|
||||||
else
|
else
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong parameter type in ALTER query");
|
throw Exception(ErrorCodes::LOGICAL_ERROR, "Wrong parameter type in ALTER query");
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,6 @@ struct AlterCommand
|
|||||||
MODIFY_DATABASE_SETTING,
|
MODIFY_DATABASE_SETTING,
|
||||||
COMMENT_TABLE,
|
COMMENT_TABLE,
|
||||||
REMOVE_SAMPLE_BY,
|
REMOVE_SAMPLE_BY,
|
||||||
MODIFY_SQL_SECURITY,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Which property user wants to remove from column
|
/// Which property user wants to remove from column
|
||||||
@ -148,9 +147,6 @@ struct AlterCommand
|
|||||||
/// For MODIFY_QUERY
|
/// For MODIFY_QUERY
|
||||||
ASTPtr select = nullptr;
|
ASTPtr select = nullptr;
|
||||||
|
|
||||||
/// For MODIFY_SQL_SECURITY
|
|
||||||
ASTPtr sql_security = nullptr;
|
|
||||||
|
|
||||||
/// For MODIFY_REFRESH
|
/// For MODIFY_REFRESH
|
||||||
ASTPtr refresh = nullptr;
|
ASTPtr refresh = nullptr;
|
||||||
|
|
||||||
|
@ -35,9 +35,9 @@
|
|||||||
#include <Storages/extractKeyExpressionList.h>
|
#include <Storages/extractKeyExpressionList.h>
|
||||||
#include <Storages/PartitionCommands.h>
|
#include <Storages/PartitionCommands.h>
|
||||||
#include <Interpreters/PartLog.h>
|
#include <Interpreters/PartLog.h>
|
||||||
|
#include <Poco/Timestamp.h>
|
||||||
#include <Common/threadPoolCallbackRunner.h>
|
#include <Common/threadPoolCallbackRunner.h>
|
||||||
|
|
||||||
|
|
||||||
#include <boost/multi_index_container.hpp>
|
#include <boost/multi_index_container.hpp>
|
||||||
#include <boost/multi_index/ordered_index.hpp>
|
#include <boost/multi_index/ordered_index.hpp>
|
||||||
#include <boost/multi_index/global_fun.hpp>
|
#include <boost/multi_index/global_fun.hpp>
|
||||||
@ -1353,6 +1353,93 @@ protected:
|
|||||||
const MergeListEntry * merge_entry,
|
const MergeListEntry * merge_entry,
|
||||||
std::shared_ptr<ProfileEvents::Counters::Snapshot> profile_counters);
|
std::shared_ptr<ProfileEvents::Counters::Snapshot> profile_counters);
|
||||||
|
|
||||||
|
class PartMutationBackoffPolicy
|
||||||
|
{
|
||||||
|
struct PartMutationInfo
|
||||||
|
{
|
||||||
|
size_t retry_count;
|
||||||
|
size_t latest_fail_time_us;
|
||||||
|
size_t max_postpone_time_ms;
|
||||||
|
size_t max_postpone_power;
|
||||||
|
|
||||||
|
PartMutationInfo(size_t max_postpone_time_ms_)
|
||||||
|
: retry_count(0ull)
|
||||||
|
, latest_fail_time_us(static_cast<size_t>(Poco::Timestamp().epochMicroseconds()))
|
||||||
|
, max_postpone_time_ms(max_postpone_time_ms_)
|
||||||
|
, max_postpone_power((max_postpone_time_ms_) ? (static_cast<size_t>(std::log2(max_postpone_time_ms_))) : (0ull))
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
size_t getNextMinExecutionTimeUsResolution() const
|
||||||
|
{
|
||||||
|
if (max_postpone_time_ms == 0)
|
||||||
|
return static_cast<size_t>(Poco::Timestamp().epochMicroseconds());
|
||||||
|
size_t current_backoff_interval_us = (1 << retry_count) * 1000ul;
|
||||||
|
return latest_fail_time_us + current_backoff_interval_us;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPartFailure()
|
||||||
|
{
|
||||||
|
if (max_postpone_time_ms == 0)
|
||||||
|
return;
|
||||||
|
retry_count = std::min(max_postpone_power, retry_count + 1);
|
||||||
|
latest_fail_time_us = static_cast<size_t>(Poco::Timestamp().epochMicroseconds());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool partCanBeMutated()
|
||||||
|
{
|
||||||
|
if (max_postpone_time_ms == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
auto current_time_us = static_cast<size_t>(Poco::Timestamp().epochMicroseconds());
|
||||||
|
return current_time_us >= getNextMinExecutionTimeUsResolution();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using DataPartsWithRetryInfo = std::unordered_map<String, PartMutationInfo>;
|
||||||
|
DataPartsWithRetryInfo failed_mutation_parts;
|
||||||
|
mutable std::mutex parts_info_lock;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void resetMutationFailures()
|
||||||
|
{
|
||||||
|
std::unique_lock _lock(parts_info_lock);
|
||||||
|
failed_mutation_parts.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void removePartFromFailed(const String & part_name)
|
||||||
|
{
|
||||||
|
std::unique_lock _lock(parts_info_lock);
|
||||||
|
failed_mutation_parts.erase(part_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addPartMutationFailure (const String& part_name, size_t max_postpone_time_ms_)
|
||||||
|
{
|
||||||
|
std::unique_lock _lock(parts_info_lock);
|
||||||
|
auto part_info_it = failed_mutation_parts.find(part_name);
|
||||||
|
if (part_info_it == failed_mutation_parts.end())
|
||||||
|
{
|
||||||
|
auto [it, success] = failed_mutation_parts.emplace(part_name, PartMutationInfo(max_postpone_time_ms_));
|
||||||
|
std::swap(it, part_info_it);
|
||||||
|
}
|
||||||
|
auto& part_info = part_info_it->second;
|
||||||
|
part_info.addPartFailure();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool partCanBeMutated(const String& part_name)
|
||||||
|
{
|
||||||
|
|
||||||
|
std::unique_lock _lock(parts_info_lock);
|
||||||
|
auto iter = failed_mutation_parts.find(part_name);
|
||||||
|
if (iter == failed_mutation_parts.end())
|
||||||
|
return true;
|
||||||
|
return iter->second.partCanBeMutated();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/// Controls postponing logic for failed mutations.
|
||||||
|
PartMutationBackoffPolicy mutation_backoff_policy;
|
||||||
|
|
||||||
/// If part is assigned to merge or mutation (possibly replicated)
|
/// If part is assigned to merge or mutation (possibly replicated)
|
||||||
/// Should be overridden by children, because they can have different
|
/// Should be overridden by children, because they can have different
|
||||||
/// mechanisms for parts locking
|
/// mechanisms for parts locking
|
||||||
|
@ -146,6 +146,7 @@ struct Settings;
|
|||||||
M(UInt64, vertical_merge_algorithm_min_rows_to_activate, 16 * 8192, "Minimal (approximate) sum of rows in merging parts to activate Vertical merge algorithm.", 0) \
|
M(UInt64, vertical_merge_algorithm_min_rows_to_activate, 16 * 8192, "Minimal (approximate) sum of rows in merging parts to activate Vertical merge algorithm.", 0) \
|
||||||
M(UInt64, vertical_merge_algorithm_min_bytes_to_activate, 0, "Minimal (approximate) uncompressed size in bytes in merging parts to activate Vertical merge algorithm.", 0) \
|
M(UInt64, vertical_merge_algorithm_min_bytes_to_activate, 0, "Minimal (approximate) uncompressed size in bytes in merging parts to activate Vertical merge algorithm.", 0) \
|
||||||
M(UInt64, vertical_merge_algorithm_min_columns_to_activate, 11, "Minimal amount of non-PK columns to activate Vertical merge algorithm.", 0) \
|
M(UInt64, vertical_merge_algorithm_min_columns_to_activate, 11, "Minimal amount of non-PK columns to activate Vertical merge algorithm.", 0) \
|
||||||
|
M(UInt64, max_postpone_time_for_failed_mutations_ms, 5ULL * 60 * 1000, "The maximum postpone time for failed mutations.", 0) \
|
||||||
\
|
\
|
||||||
/** Compatibility settings */ \
|
/** Compatibility settings */ \
|
||||||
M(Bool, allow_suspicious_indices, false, "Reject primary/secondary indexes and sorting keys with identical expressions", 0) \
|
M(Bool, allow_suspicious_indices, false, "Reject primary/secondary indexes and sorting keys with identical expressions", 0) \
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include <Storages/MergeTree/ReplicatedMergeMutateTaskBase.h>
|
#include <Storages/MergeTree/ReplicatedMergeMutateTaskBase.h>
|
||||||
|
|
||||||
#include <Storages/StorageReplicatedMergeTree.h>
|
#include <Storages/StorageReplicatedMergeTree.h>
|
||||||
|
#include <Storages/MergeTree/MergeTreeData.h>
|
||||||
#include <Storages/MergeTree/ReplicatedMergeTreeQueue.h>
|
#include <Storages/MergeTree/ReplicatedMergeTreeQueue.h>
|
||||||
#include <Common/ProfileEventsScope.h>
|
#include <Common/ProfileEventsScope.h>
|
||||||
|
|
||||||
@ -110,11 +111,14 @@ bool ReplicatedMergeMutateTaskBase::executeStep()
|
|||||||
auto mutations_end_it = in_partition->second.upper_bound(result_data_version);
|
auto mutations_end_it = in_partition->second.upper_bound(result_data_version);
|
||||||
for (auto it = mutations_begin_it; it != mutations_end_it; ++it)
|
for (auto it = mutations_begin_it; it != mutations_end_it; ++it)
|
||||||
{
|
{
|
||||||
|
auto & src_part = log_entry->source_parts.at(0);
|
||||||
ReplicatedMergeTreeQueue::MutationStatus & status = *it->second;
|
ReplicatedMergeTreeQueue::MutationStatus & status = *it->second;
|
||||||
status.latest_failed_part = log_entry->source_parts.at(0);
|
status.latest_failed_part = src_part;
|
||||||
status.latest_failed_part_info = source_part_info;
|
status.latest_failed_part_info = source_part_info;
|
||||||
status.latest_fail_time = time(nullptr);
|
status.latest_fail_time = time(nullptr);
|
||||||
status.latest_fail_reason = getExceptionMessage(saved_exception, false);
|
status.latest_fail_reason = getExceptionMessage(saved_exception, false);
|
||||||
|
if (result_data_version == it->first)
|
||||||
|
storage.mutation_backoff_policy.addPartMutationFailure(src_part, storage.getSettings()->max_postpone_time_for_failed_mutations_ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -142,6 +146,12 @@ bool ReplicatedMergeMutateTaskBase::executeImpl()
|
|||||||
{
|
{
|
||||||
storage.queue.removeProcessedEntry(storage.getZooKeeper(), selected_entry->log_entry);
|
storage.queue.removeProcessedEntry(storage.getZooKeeper(), selected_entry->log_entry);
|
||||||
state = State::SUCCESS;
|
state = State::SUCCESS;
|
||||||
|
|
||||||
|
auto & log_entry = selected_entry->log_entry;
|
||||||
|
if (log_entry->type == ReplicatedMergeTreeLogEntryData::MUTATE_PART)
|
||||||
|
{
|
||||||
|
storage.mutation_backoff_policy.removePartFromFailed(log_entry->source_parts.at(0));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
|
@ -13,6 +13,7 @@
|
|||||||
#include <base/sort.h>
|
#include <base/sort.h>
|
||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
#include <Poco/Timestamp.h>
|
||||||
|
|
||||||
namespace DB
|
namespace DB
|
||||||
{
|
{
|
||||||
@ -1353,9 +1354,17 @@ bool ReplicatedMergeTreeQueue::shouldExecuteLogEntry(
|
|||||||
sum_parts_size_in_bytes += part_in_memory->block.bytes();
|
sum_parts_size_in_bytes += part_in_memory->block.bytes();
|
||||||
else
|
else
|
||||||
sum_parts_size_in_bytes += part->getBytesOnDisk();
|
sum_parts_size_in_bytes += part->getBytesOnDisk();
|
||||||
|
|
||||||
|
if (entry.type == LogEntry::MUTATE_PART && !storage.mutation_backoff_policy.partCanBeMutated(part->name))
|
||||||
|
{
|
||||||
|
constexpr auto fmt_string = "Not executing log entry {} of type {} for part {} "
|
||||||
|
"because recently it has failed. According to exponential backoff policy, put aside this log entry.";
|
||||||
|
|
||||||
|
LOG_DEBUG(LogToStr(out_postpone_reason, log), fmt_string, entry.znode_name, entry.typeToString(), entry.new_part_name);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (merger_mutator.merges_blocker.isCancelled())
|
if (merger_mutator.merges_blocker.isCancelled())
|
||||||
{
|
{
|
||||||
constexpr auto fmt_string = "Not executing log entry {} of type {} for part {} because merges and mutations are cancelled now.";
|
constexpr auto fmt_string = "Not executing log entry {} of type {} for part {} because merges and mutations are cancelled now.";
|
||||||
|
@ -1,8 +1,5 @@
|
|||||||
#include <Storages/StorageInMemoryMetadata.h>
|
#include <Storages/StorageInMemoryMetadata.h>
|
||||||
|
|
||||||
#include <Access/AccessControl.h>
|
|
||||||
#include <Access/User.h>
|
|
||||||
|
|
||||||
#include <Common/HashTable/HashMap.h>
|
#include <Common/HashTable/HashMap.h>
|
||||||
#include <Common/HashTable/HashSet.h>
|
#include <Common/HashTable/HashSet.h>
|
||||||
#include <Common/quoteString.h>
|
#include <Common/quoteString.h>
|
||||||
@ -10,7 +7,6 @@
|
|||||||
#include <Core/ColumnWithTypeAndName.h>
|
#include <Core/ColumnWithTypeAndName.h>
|
||||||
#include <DataTypes/NestedUtils.h>
|
#include <DataTypes/NestedUtils.h>
|
||||||
#include <DataTypes/DataTypeEnum.h>
|
#include <DataTypes/DataTypeEnum.h>
|
||||||
#include <Interpreters/Context.h>
|
|
||||||
#include <IO/ReadBufferFromString.h>
|
#include <IO/ReadBufferFromString.h>
|
||||||
#include <IO/ReadHelpers.h>
|
#include <IO/ReadHelpers.h>
|
||||||
#include <IO/Operators.h>
|
#include <IO/Operators.h>
|
||||||
@ -27,7 +23,6 @@ namespace ErrorCodes
|
|||||||
extern const int NOT_FOUND_COLUMN_IN_BLOCK;
|
extern const int NOT_FOUND_COLUMN_IN_BLOCK;
|
||||||
extern const int TYPE_MISMATCH;
|
extern const int TYPE_MISMATCH;
|
||||||
extern const int EMPTY_LIST_OF_COLUMNS_PASSED;
|
extern const int EMPTY_LIST_OF_COLUMNS_PASSED;
|
||||||
extern const int LOGICAL_ERROR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageInMemoryMetadata::StorageInMemoryMetadata(const StorageInMemoryMetadata & other)
|
StorageInMemoryMetadata::StorageInMemoryMetadata(const StorageInMemoryMetadata & other)
|
||||||
@ -46,8 +41,6 @@ StorageInMemoryMetadata::StorageInMemoryMetadata(const StorageInMemoryMetadata &
|
|||||||
, settings_changes(other.settings_changes ? other.settings_changes->clone() : nullptr)
|
, settings_changes(other.settings_changes ? other.settings_changes->clone() : nullptr)
|
||||||
, select(other.select)
|
, select(other.select)
|
||||||
, refresh(other.refresh ? other.refresh->clone() : nullptr)
|
, refresh(other.refresh ? other.refresh->clone() : nullptr)
|
||||||
, definer(other.definer)
|
|
||||||
, sql_security_type(other.sql_security_type)
|
|
||||||
, comment(other.comment)
|
, comment(other.comment)
|
||||||
, metadata_version(other.metadata_version)
|
, metadata_version(other.metadata_version)
|
||||||
{
|
{
|
||||||
@ -78,8 +71,6 @@ StorageInMemoryMetadata & StorageInMemoryMetadata::operator=(const StorageInMemo
|
|||||||
settings_changes.reset();
|
settings_changes.reset();
|
||||||
select = other.select;
|
select = other.select;
|
||||||
refresh = other.refresh ? other.refresh->clone() : nullptr;
|
refresh = other.refresh ? other.refresh->clone() : nullptr;
|
||||||
definer = other.definer;
|
|
||||||
sql_security_type = other.sql_security_type;
|
|
||||||
comment = other.comment;
|
comment = other.comment;
|
||||||
metadata_version = other.metadata_version;
|
metadata_version = other.metadata_version;
|
||||||
return *this;
|
return *this;
|
||||||
@ -90,69 +81,6 @@ void StorageInMemoryMetadata::setComment(const String & comment_)
|
|||||||
comment = comment_;
|
comment = comment_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageInMemoryMetadata::setDefiner(const ASTSQLSecurity & sql_security)
|
|
||||||
{
|
|
||||||
if (sql_security.definer)
|
|
||||||
definer = sql_security.definer->toString();
|
|
||||||
|
|
||||||
sql_security_type = sql_security.type;
|
|
||||||
}
|
|
||||||
|
|
||||||
UUID StorageInMemoryMetadata::getDefinerID(DB::ContextPtr context) const
|
|
||||||
{
|
|
||||||
if (!definer)
|
|
||||||
{
|
|
||||||
if (const auto definer_id = context->getUserID())
|
|
||||||
return *definer_id;
|
|
||||||
|
|
||||||
throw Exception(ErrorCodes::LOGICAL_ERROR, "No user in context for sub query execution.");
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto & access_control = context->getAccessControl();
|
|
||||||
return access_control.getID<User>(*definer);
|
|
||||||
}
|
|
||||||
|
|
||||||
ContextMutablePtr StorageInMemoryMetadata::getSQLSecurityOverriddenContext(ContextPtr context) const
|
|
||||||
{
|
|
||||||
if (!sql_security_type.has_value())
|
|
||||||
return Context::createCopy(context);
|
|
||||||
|
|
||||||
if (sql_security_type == SQLSecurityType::INVOKER)
|
|
||||||
return Context::createCopy(context);
|
|
||||||
|
|
||||||
auto new_context = Context::createCopy(context->getGlobalContext());
|
|
||||||
new_context->setClientInfo(context->getClientInfo());
|
|
||||||
new_context->makeQueryContext();
|
|
||||||
|
|
||||||
const auto & database = context->getCurrentDatabase();
|
|
||||||
if (!database.empty())
|
|
||||||
new_context->setCurrentDatabase(database);
|
|
||||||
|
|
||||||
new_context->setInsertionTable(context->getInsertionTable(), context->getInsertionTableColumnNames());
|
|
||||||
new_context->setProgressCallback(context->getProgressCallback());
|
|
||||||
new_context->setProcessListElement(context->getProcessListElement());
|
|
||||||
|
|
||||||
if (context->getCurrentTransaction())
|
|
||||||
new_context->setCurrentTransaction(context->getCurrentTransaction());
|
|
||||||
|
|
||||||
if (context->getZooKeeperMetadataTransaction())
|
|
||||||
new_context->initZooKeeperMetadataTransaction(context->getZooKeeperMetadataTransaction());
|
|
||||||
|
|
||||||
if (sql_security_type == SQLSecurityType::NONE)
|
|
||||||
{
|
|
||||||
new_context->applySettingsChanges(context->getSettingsRef().changes());
|
|
||||||
return new_context;
|
|
||||||
}
|
|
||||||
|
|
||||||
new_context->setUser(getDefinerID(context));
|
|
||||||
|
|
||||||
auto changed_settings = context->getSettingsRef().changes();
|
|
||||||
new_context->clampToSettingsConstraints(changed_settings, SettingSource::QUERY);
|
|
||||||
new_context->applySettingsChanges(changed_settings);
|
|
||||||
|
|
||||||
return new_context;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StorageInMemoryMetadata::setColumns(ColumnsDescription columns_)
|
void StorageInMemoryMetadata::setColumns(ColumnsDescription columns_)
|
||||||
{
|
{
|
||||||
if (columns_.getAllPhysical().empty())
|
if (columns_.getAllPhysical().empty())
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Parsers/Access/ASTUserNameWithHost.h>
|
|
||||||
#include <Parsers/ASTCreateQuery.h>
|
|
||||||
#include <Parsers/IAST_fwd.h>
|
#include <Parsers/IAST_fwd.h>
|
||||||
#include <Storages/ColumnDependency.h>
|
#include <Storages/ColumnDependency.h>
|
||||||
#include <Storages/ColumnsDescription.h>
|
#include <Storages/ColumnsDescription.h>
|
||||||
@ -53,14 +51,6 @@ struct StorageInMemoryMetadata
|
|||||||
/// Materialized view REFRESH parameters.
|
/// Materialized view REFRESH parameters.
|
||||||
ASTPtr refresh;
|
ASTPtr refresh;
|
||||||
|
|
||||||
/// DEFINER <user_name>. Allows to specify a definer of the table.
|
|
||||||
/// Supported for MaterializedView and View.
|
|
||||||
std::optional<String> definer;
|
|
||||||
|
|
||||||
/// SQL SECURITY <DEFINER | INVOKER | NONE>
|
|
||||||
/// Supported for MaterializedView and View.
|
|
||||||
std::optional<SQLSecurityType> sql_security_type;
|
|
||||||
|
|
||||||
String comment;
|
String comment;
|
||||||
|
|
||||||
/// Version of metadata. Managed properly by ReplicatedMergeTree only
|
/// Version of metadata. Managed properly by ReplicatedMergeTree only
|
||||||
@ -115,15 +105,6 @@ struct StorageInMemoryMetadata
|
|||||||
/// Get copy of current metadata with metadata_version_
|
/// Get copy of current metadata with metadata_version_
|
||||||
StorageInMemoryMetadata withMetadataVersion(int32_t metadata_version_) const;
|
StorageInMemoryMetadata withMetadataVersion(int32_t metadata_version_) const;
|
||||||
|
|
||||||
/// Sets a definer for the storage.
|
|
||||||
void setDefiner(const ASTSQLSecurity & sql_security);
|
|
||||||
UUID getDefinerID(ContextPtr context) const;
|
|
||||||
|
|
||||||
/// Returns a copy of the context with the correct user from SQL security options.
|
|
||||||
/// If the SQL security wasn't set, this is equivalent to `Context::createCopy(context)`.
|
|
||||||
/// The context from this function must be used every time whenever views execute any read/write operations or subqueries.
|
|
||||||
ContextMutablePtr getSQLSecurityOverriddenContext(ContextPtr context) const;
|
|
||||||
|
|
||||||
/// Returns combined set of columns
|
/// Returns combined set of columns
|
||||||
const ColumnsDescription & getColumns() const;
|
const ColumnsDescription & getColumns() const;
|
||||||
|
|
||||||
|
@ -39,7 +39,6 @@ namespace ErrorCodes
|
|||||||
extern const int BAD_ARGUMENTS;
|
extern const int BAD_ARGUMENTS;
|
||||||
extern const int NOT_IMPLEMENTED;
|
extern const int NOT_IMPLEMENTED;
|
||||||
extern const int INCORRECT_QUERY;
|
extern const int INCORRECT_QUERY;
|
||||||
extern const int QUERY_IS_NOT_SUPPORTED_IN_MATERIALIZED_VIEW;
|
|
||||||
extern const int TOO_MANY_MATERIALIZED_VIEWS;
|
extern const int TOO_MANY_MATERIALIZED_VIEWS;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,11 +77,6 @@ StorageMaterializedView::StorageMaterializedView(
|
|||||||
{
|
{
|
||||||
StorageInMemoryMetadata storage_metadata;
|
StorageInMemoryMetadata storage_metadata;
|
||||||
storage_metadata.setColumns(columns_);
|
storage_metadata.setColumns(columns_);
|
||||||
if (query.sql_security)
|
|
||||||
storage_metadata.setDefiner(query.sql_security->as<ASTSQLSecurity &>());
|
|
||||||
|
|
||||||
if (storage_metadata.sql_security_type == SQLSecurityType::INVOKER)
|
|
||||||
throw Exception(ErrorCodes::QUERY_IS_NOT_SUPPORTED_IN_MATERIALIZED_VIEW, "SQL SECURITY INVOKER can't be specified for MATERIALIZED VIEW");
|
|
||||||
|
|
||||||
if (!query.select)
|
if (!query.select)
|
||||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "SELECT query is not specified for {}", getName());
|
throw Exception(ErrorCodes::INCORRECT_QUERY, "SELECT query is not specified for {}", getName());
|
||||||
@ -181,28 +175,19 @@ void StorageMaterializedView::read(
|
|||||||
const size_t max_block_size,
|
const size_t max_block_size,
|
||||||
const size_t num_streams)
|
const size_t num_streams)
|
||||||
{
|
{
|
||||||
auto context = getInMemoryMetadataPtr()->getSQLSecurityOverriddenContext(local_context);
|
|
||||||
auto storage = getTargetTable();
|
auto storage = getTargetTable();
|
||||||
auto lock = storage->lockForShare(context->getCurrentQueryId(), context->getSettingsRef().lock_acquire_timeout);
|
auto lock = storage->lockForShare(local_context->getCurrentQueryId(), local_context->getSettingsRef().lock_acquire_timeout);
|
||||||
auto target_metadata_snapshot = storage->getInMemoryMetadataPtr();
|
auto target_metadata_snapshot = storage->getInMemoryMetadataPtr();
|
||||||
auto target_storage_snapshot = storage->getStorageSnapshot(target_metadata_snapshot, context);
|
auto target_storage_snapshot = storage->getStorageSnapshot(target_metadata_snapshot, local_context);
|
||||||
|
|
||||||
if (query_info.order_optimizer)
|
if (query_info.order_optimizer)
|
||||||
query_info.input_order_info = query_info.order_optimizer->getInputOrder(target_metadata_snapshot, context);
|
query_info.input_order_info = query_info.order_optimizer->getInputOrder(target_metadata_snapshot, local_context);
|
||||||
|
|
||||||
if (!getInMemoryMetadataPtr()->select.select_table_id.empty())
|
storage->read(query_plan, column_names, target_storage_snapshot, query_info, local_context, processed_stage, max_block_size, num_streams);
|
||||||
context->checkAccess(AccessType::SELECT, getInMemoryMetadataPtr()->select.select_table_id, column_names);
|
|
||||||
|
|
||||||
auto storage_id = storage->getStorageID();
|
|
||||||
/// We don't need to check access if the inner table was created automatically.
|
|
||||||
if (!has_inner_table && !storage_id.empty())
|
|
||||||
context->checkAccess(AccessType::SELECT, storage_id, column_names);
|
|
||||||
|
|
||||||
storage->read(query_plan, column_names, target_storage_snapshot, query_info, context, processed_stage, max_block_size, num_streams);
|
|
||||||
|
|
||||||
if (query_plan.isInitialized())
|
if (query_plan.isInitialized())
|
||||||
{
|
{
|
||||||
auto mv_header = getHeaderForProcessingStage(column_names, storage_snapshot, query_info, context, processed_stage);
|
auto mv_header = getHeaderForProcessingStage(column_names, storage_snapshot, query_info, local_context, processed_stage);
|
||||||
auto target_header = query_plan.getCurrentDataStream().header;
|
auto target_header = query_plan.getCurrentDataStream().header;
|
||||||
|
|
||||||
/// No need to convert columns that does not exists in MV
|
/// No need to convert columns that does not exists in MV
|
||||||
@ -237,20 +222,11 @@ void StorageMaterializedView::read(
|
|||||||
|
|
||||||
SinkToStoragePtr StorageMaterializedView::write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, ContextPtr local_context, bool async_insert)
|
SinkToStoragePtr StorageMaterializedView::write(const ASTPtr & query, const StorageMetadataPtr & /*metadata_snapshot*/, ContextPtr local_context, bool async_insert)
|
||||||
{
|
{
|
||||||
auto context = getInMemoryMetadataPtr()->getSQLSecurityOverriddenContext(local_context);
|
|
||||||
auto storage = getTargetTable();
|
auto storage = getTargetTable();
|
||||||
auto lock = storage->lockForShare(context->getCurrentQueryId(), context->getSettingsRef().lock_acquire_timeout);
|
auto lock = storage->lockForShare(local_context->getCurrentQueryId(), local_context->getSettingsRef().lock_acquire_timeout);
|
||||||
|
|
||||||
auto metadata_snapshot = storage->getInMemoryMetadataPtr();
|
auto metadata_snapshot = storage->getInMemoryMetadataPtr();
|
||||||
|
auto sink = storage->write(query, metadata_snapshot, local_context, async_insert);
|
||||||
auto storage_id = storage->getStorageID();
|
|
||||||
/// We don't need to check access if the inner table was created automatically.
|
|
||||||
if (!has_inner_table && !storage_id.empty())
|
|
||||||
{
|
|
||||||
auto query_sample_block = InterpreterInsertQuery::getSampleBlock(query->as<ASTInsertQuery &>(), storage, metadata_snapshot, context);
|
|
||||||
context->checkAccess(AccessType::INSERT, storage_id, query_sample_block.getNames());
|
|
||||||
}
|
|
||||||
|
|
||||||
auto sink = storage->write(query, metadata_snapshot, context, async_insert);
|
|
||||||
|
|
||||||
sink->addTableLock(lock);
|
sink->addTableLock(lock);
|
||||||
return sink;
|
return sink;
|
||||||
@ -321,7 +297,7 @@ bool StorageMaterializedView::optimize(
|
|||||||
|
|
||||||
std::tuple<ContextMutablePtr, std::shared_ptr<ASTInsertQuery>> StorageMaterializedView::prepareRefresh() const
|
std::tuple<ContextMutablePtr, std::shared_ptr<ASTInsertQuery>> StorageMaterializedView::prepareRefresh() const
|
||||||
{
|
{
|
||||||
auto refresh_context = getInMemoryMetadataPtr()->getSQLSecurityOverriddenContext(getContext());
|
auto refresh_context = Context::createCopy(getContext());
|
||||||
/// Generate a random query id.
|
/// Generate a random query id.
|
||||||
refresh_context->setCurrentQueryId("");
|
refresh_context->setCurrentQueryId("");
|
||||||
|
|
||||||
@ -402,24 +378,15 @@ void StorageMaterializedView::checkAlterIsPossible(const AlterCommands & command
|
|||||||
{
|
{
|
||||||
for (const auto & command : commands)
|
for (const auto & command : commands)
|
||||||
{
|
{
|
||||||
if (command.type == AlterCommand::MODIFY_SQL_SECURITY)
|
if (command.isCommentAlter())
|
||||||
{
|
|
||||||
if (command.sql_security->as<ASTSQLSecurity &>().type == SQLSecurityType::INVOKER)
|
|
||||||
throw Exception(ErrorCodes::QUERY_IS_NOT_SUPPORTED_IN_MATERIALIZED_VIEW, "SQL SECURITY INVOKER can't be specified for MATERIALIZED VIEW");
|
|
||||||
|
|
||||||
continue;
|
continue;
|
||||||
}
|
if (command.type == AlterCommand::MODIFY_QUERY)
|
||||||
else if (command.isCommentAlter())
|
|
||||||
continue;
|
continue;
|
||||||
else if (command.type == AlterCommand::MODIFY_QUERY)
|
if (command.type == AlterCommand::MODIFY_REFRESH && refresher)
|
||||||
continue;
|
continue;
|
||||||
else if (command.type == AlterCommand::MODIFY_REFRESH && refresher)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Alter of type '{}' is not supported by storage {}",
|
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Alter of type '{}' is not supported by storage {}",
|
||||||
command.type, getName());
|
command.type, getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageMaterializedView::checkMutationIsPossible(const MutationCommands & commands, const Settings & settings) const
|
void StorageMaterializedView::checkMutationIsPossible(const MutationCommands & commands, const Settings & settings) const
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
|
#include <Poco/Timestamp.h>
|
||||||
#include <base/sort.h>
|
#include <base/sort.h>
|
||||||
#include <Backups/BackupEntriesCollector.h>
|
#include <Backups/BackupEntriesCollector.h>
|
||||||
#include <Databases/IDatabase.h>
|
#include <Databases/IDatabase.h>
|
||||||
@ -538,6 +539,8 @@ void StorageMergeTree::updateMutationEntriesErrors(FutureMergedMutatedPartPtr re
|
|||||||
|
|
||||||
Int64 sources_data_version = result_part->parts.at(0)->info.getDataVersion();
|
Int64 sources_data_version = result_part->parts.at(0)->info.getDataVersion();
|
||||||
Int64 result_data_version = result_part->part_info.getDataVersion();
|
Int64 result_data_version = result_part->part_info.getDataVersion();
|
||||||
|
auto & failed_part = result_part->parts.at(0);
|
||||||
|
|
||||||
if (sources_data_version != result_data_version)
|
if (sources_data_version != result_data_version)
|
||||||
{
|
{
|
||||||
std::lock_guard lock(currently_processing_in_background_mutex);
|
std::lock_guard lock(currently_processing_in_background_mutex);
|
||||||
@ -555,14 +558,21 @@ void StorageMergeTree::updateMutationEntriesErrors(FutureMergedMutatedPartPtr re
|
|||||||
entry.latest_failed_part_info = MergeTreePartInfo();
|
entry.latest_failed_part_info = MergeTreePartInfo();
|
||||||
entry.latest_fail_time = 0;
|
entry.latest_fail_time = 0;
|
||||||
entry.latest_fail_reason.clear();
|
entry.latest_fail_reason.clear();
|
||||||
|
if (static_cast<UInt64>(result_part->part_info.mutation) == it->first)
|
||||||
|
mutation_backoff_policy.removePartFromFailed(failed_part->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
entry.latest_failed_part = result_part->parts.at(0)->name;
|
entry.latest_failed_part = failed_part->name;
|
||||||
entry.latest_failed_part_info = result_part->parts.at(0)->info;
|
entry.latest_failed_part_info = failed_part->info;
|
||||||
entry.latest_fail_time = time(nullptr);
|
entry.latest_fail_time = time(nullptr);
|
||||||
entry.latest_fail_reason = exception_message;
|
entry.latest_fail_reason = exception_message;
|
||||||
|
|
||||||
|
if (static_cast<UInt64>(result_part->part_info.mutation) == it->first)
|
||||||
|
{
|
||||||
|
mutation_backoff_policy.addPartMutationFailure(failed_part->name, getSettings()->max_postpone_time_for_failed_mutations_ms);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -833,6 +843,8 @@ CancellationCode StorageMergeTree::killMutation(const String & mutation_id)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutation_backoff_policy.resetMutationFailures();
|
||||||
|
|
||||||
if (!to_kill)
|
if (!to_kill)
|
||||||
return CancellationCode::NotFound;
|
return CancellationCode::NotFound;
|
||||||
|
|
||||||
@ -1217,6 +1229,12 @@ MergeMutateSelectedEntryPtr StorageMergeTree::selectPartsToMutate(
|
|||||||
TransactionID first_mutation_tid = mutations_begin_it->second.tid;
|
TransactionID first_mutation_tid = mutations_begin_it->second.tid;
|
||||||
MergeTreeTransactionPtr txn;
|
MergeTreeTransactionPtr txn;
|
||||||
|
|
||||||
|
if (!mutation_backoff_policy.partCanBeMutated(part->name))
|
||||||
|
{
|
||||||
|
LOG_DEBUG(log, "According to exponential backoff policy, do not perform mutations for the part {} yet. Put it aside.", part->name);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!first_mutation_tid.isPrehistoric())
|
if (!first_mutation_tid.isPrehistoric())
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -7460,6 +7460,7 @@ CancellationCode StorageReplicatedMergeTree::killMutation(const String & mutatio
|
|||||||
Int64 block_number = pair.second;
|
Int64 block_number = pair.second;
|
||||||
getContext()->getMergeList().cancelPartMutations(getStorageID(), partition_id, block_number);
|
getContext()->getMergeList().cancelPartMutations(getStorageID(), partition_id, block_number);
|
||||||
}
|
}
|
||||||
|
mutation_backoff_policy.resetMutationFailures();
|
||||||
return CancellationCode::CancelSent;
|
return CancellationCode::CancelSent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
#include <Parsers/ASTSubquery.h>
|
#include <Parsers/ASTSubquery.h>
|
||||||
#include <Parsers/ASTTablesInSelectQuery.h>
|
#include <Parsers/ASTTablesInSelectQuery.h>
|
||||||
|
|
||||||
#include <Storages/AlterCommands.h>
|
|
||||||
#include <Storages/StorageView.h>
|
#include <Storages/StorageView.h>
|
||||||
#include <Storages/StorageFactory.h>
|
#include <Storages/StorageFactory.h>
|
||||||
#include <Storages/SelectQueryDescription.h>
|
#include <Storages/SelectQueryDescription.h>
|
||||||
@ -36,7 +35,6 @@ namespace ErrorCodes
|
|||||||
{
|
{
|
||||||
extern const int INCORRECT_QUERY;
|
extern const int INCORRECT_QUERY;
|
||||||
extern const int LOGICAL_ERROR;
|
extern const int LOGICAL_ERROR;
|
||||||
extern const int NOT_IMPLEMENTED;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -92,10 +90,10 @@ bool hasJoin(const ASTSelectWithUnionQuery & ast)
|
|||||||
/** There are no limits on the maximum size of the result for the view.
|
/** There are no limits on the maximum size of the result for the view.
|
||||||
* Since the result of the view is not the result of the entire query.
|
* Since the result of the view is not the result of the entire query.
|
||||||
*/
|
*/
|
||||||
ContextPtr getViewContext(ContextPtr context, const StorageSnapshotPtr & storage_snapshot)
|
ContextPtr getViewContext(ContextPtr context)
|
||||||
{
|
{
|
||||||
auto view_context = storage_snapshot->metadata->getSQLSecurityOverriddenContext(context);
|
auto view_context = Context::createCopy(context);
|
||||||
Settings view_settings = view_context->getSettings();
|
Settings view_settings = context->getSettings();
|
||||||
view_settings.max_result_rows = 0;
|
view_settings.max_result_rows = 0;
|
||||||
view_settings.max_result_bytes = 0;
|
view_settings.max_result_bytes = 0;
|
||||||
view_settings.extremes = false;
|
view_settings.extremes = false;
|
||||||
@ -124,8 +122,6 @@ StorageView::StorageView(
|
|||||||
storage_metadata.setColumns(columns_);
|
storage_metadata.setColumns(columns_);
|
||||||
|
|
||||||
storage_metadata.setComment(comment);
|
storage_metadata.setComment(comment);
|
||||||
if (query.sql_security)
|
|
||||||
storage_metadata.setDefiner(query.sql_security->as<ASTSQLSecurity &>());
|
|
||||||
|
|
||||||
if (!query.select)
|
if (!query.select)
|
||||||
throw Exception(ErrorCodes::INCORRECT_QUERY, "SELECT query is not specified for {}", getName());
|
throw Exception(ErrorCodes::INCORRECT_QUERY, "SELECT query is not specified for {}", getName());
|
||||||
@ -164,13 +160,13 @@ void StorageView::read(
|
|||||||
|
|
||||||
if (context->getSettingsRef().allow_experimental_analyzer)
|
if (context->getSettingsRef().allow_experimental_analyzer)
|
||||||
{
|
{
|
||||||
InterpreterSelectQueryAnalyzer interpreter(current_inner_query, getViewContext(context, storage_snapshot), options);
|
InterpreterSelectQueryAnalyzer interpreter(current_inner_query, getViewContext(context), options);
|
||||||
interpreter.addStorageLimits(*query_info.storage_limits);
|
interpreter.addStorageLimits(*query_info.storage_limits);
|
||||||
query_plan = std::move(interpreter).extractQueryPlan();
|
query_plan = std::move(interpreter).extractQueryPlan();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
InterpreterSelectWithUnionQuery interpreter(current_inner_query, getViewContext(context, storage_snapshot), options, column_names);
|
InterpreterSelectWithUnionQuery interpreter(current_inner_query, getViewContext(context), options, column_names);
|
||||||
interpreter.addStorageLimits(*query_info.storage_limits);
|
interpreter.addStorageLimits(*query_info.storage_limits);
|
||||||
interpreter.buildQueryPlan(query_plan);
|
interpreter.buildQueryPlan(query_plan);
|
||||||
}
|
}
|
||||||
@ -286,15 +282,6 @@ ASTPtr StorageView::restoreViewName(ASTSelectQuery & select_query, const ASTPtr
|
|||||||
return subquery->children[0];
|
return subquery->children[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageView::checkAlterIsPossible(const AlterCommands & commands, ContextPtr /* local_context */) const
|
|
||||||
{
|
|
||||||
for (const auto & command : commands)
|
|
||||||
{
|
|
||||||
if (!command.isCommentAlter() && command.type != AlterCommand::MODIFY_SQL_SECURITY)
|
|
||||||
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Alter of type '{}' is not supported by storage {}", command.type, getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void registerStorageView(StorageFactory & factory)
|
void registerStorageView(StorageFactory & factory)
|
||||||
{
|
{
|
||||||
factory.registerStorage("View", [](const StorageFactory::Arguments & args)
|
factory.registerStorage("View", [](const StorageFactory::Arguments & args)
|
||||||
|
@ -26,8 +26,6 @@ public:
|
|||||||
bool supportsSampling() const override { return true; }
|
bool supportsSampling() const override { return true; }
|
||||||
bool supportsFinal() const override { return true; }
|
bool supportsFinal() const override { return true; }
|
||||||
|
|
||||||
void checkAlterIsPossible(const AlterCommands & commands, ContextPtr local_context) const override;
|
|
||||||
|
|
||||||
void read(
|
void read(
|
||||||
QueryPlan & query_plan,
|
QueryPlan & query_plan,
|
||||||
const Names & column_names,
|
const Names & column_names,
|
||||||
|
@ -29,7 +29,6 @@ namespace
|
|||||||
VIEW,
|
VIEW,
|
||||||
COLUMN,
|
COLUMN,
|
||||||
NAMED_COLLECTION,
|
NAMED_COLLECTION,
|
||||||
USER_NAME,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
DataTypeEnum8::Values getLevelEnumValues()
|
DataTypeEnum8::Values getLevelEnumValues()
|
||||||
@ -42,7 +41,6 @@ namespace
|
|||||||
enum_values.emplace_back("VIEW", static_cast<Int8>(VIEW));
|
enum_values.emplace_back("VIEW", static_cast<Int8>(VIEW));
|
||||||
enum_values.emplace_back("COLUMN", static_cast<Int8>(COLUMN));
|
enum_values.emplace_back("COLUMN", static_cast<Int8>(COLUMN));
|
||||||
enum_values.emplace_back("NAMED_COLLECTION", static_cast<Int8>(NAMED_COLLECTION));
|
enum_values.emplace_back("NAMED_COLLECTION", static_cast<Int8>(NAMED_COLLECTION));
|
||||||
enum_values.emplace_back("USER_NAME", static_cast<Int8>(USER_NAME));
|
|
||||||
return enum_values;
|
return enum_values;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,9 +35,8 @@ static constexpr std::string_view schemata = R"(
|
|||||||
`DEFAULT_CHARACTER_SET_SCHEMA` Nullable(String),
|
`DEFAULT_CHARACTER_SET_SCHEMA` Nullable(String),
|
||||||
`DEFAULT_CHARACTER_SET_NAME` Nullable(String),
|
`DEFAULT_CHARACTER_SET_NAME` Nullable(String),
|
||||||
`SQL_PATH` Nullable(String)
|
`SQL_PATH` Nullable(String)
|
||||||
)
|
) AS
|
||||||
SQL SECURITY INVOKER
|
SELECT
|
||||||
AS SELECT
|
|
||||||
name AS catalog_name,
|
name AS catalog_name,
|
||||||
name AS schema_name,
|
name AS schema_name,
|
||||||
'default' AS schema_owner,
|
'default' AS schema_owner,
|
||||||
@ -74,9 +73,8 @@ static constexpr std::string_view tables = R"(
|
|||||||
`DATA_LENGTH` Nullable(UInt64),
|
`DATA_LENGTH` Nullable(UInt64),
|
||||||
`TABLE_COLLATION` Nullable(String),
|
`TABLE_COLLATION` Nullable(String),
|
||||||
`TABLE_COMMENT` Nullable(String)
|
`TABLE_COMMENT` Nullable(String)
|
||||||
)
|
) AS
|
||||||
SQL SECURITY INVOKER
|
SELECT
|
||||||
AS SELECT
|
|
||||||
database AS table_catalog,
|
database AS table_catalog,
|
||||||
database AS table_schema,
|
database AS table_schema,
|
||||||
name AS table_name,
|
name AS table_name,
|
||||||
@ -124,9 +122,8 @@ static constexpr std::string_view views = R"(
|
|||||||
`IS_TRIGGER_UPDATABLE` Enum8('NO' = 0, 'YES' = 1),
|
`IS_TRIGGER_UPDATABLE` Enum8('NO' = 0, 'YES' = 1),
|
||||||
`IS_TRIGGER_DELETABLE` Enum8('NO' = 0, 'YES' = 1),
|
`IS_TRIGGER_DELETABLE` Enum8('NO' = 0, 'YES' = 1),
|
||||||
`IS_TRIGGER_INSERTABLE_INTO` Enum8('NO' = 0, 'YES' = 1)
|
`IS_TRIGGER_INSERTABLE_INTO` Enum8('NO' = 0, 'YES' = 1)
|
||||||
)
|
) AS
|
||||||
SQL SECURITY INVOKER
|
SELECT
|
||||||
AS SELECT
|
|
||||||
database AS table_catalog,
|
database AS table_catalog,
|
||||||
database AS table_schema,
|
database AS table_schema,
|
||||||
name AS table_name,
|
name AS table_name,
|
||||||
@ -206,9 +203,8 @@ static constexpr std::string_view columns = R"(
|
|||||||
`EXTRA` Nullable(String),
|
`EXTRA` Nullable(String),
|
||||||
`COLUMN_COMMENT` String,
|
`COLUMN_COMMENT` String,
|
||||||
`COLUMN_TYPE` String
|
`COLUMN_TYPE` String
|
||||||
)
|
) AS
|
||||||
SQL SECURITY INVOKER
|
SELECT
|
||||||
AS SELECT
|
|
||||||
database AS table_catalog,
|
database AS table_catalog,
|
||||||
database AS table_schema,
|
database AS table_schema,
|
||||||
table AS table_name,
|
table AS table_name,
|
||||||
@ -295,9 +291,8 @@ static constexpr std::string_view key_column_usage = R"(
|
|||||||
`REFERENCED_TABLE_SCHEMA` Nullable(String),
|
`REFERENCED_TABLE_SCHEMA` Nullable(String),
|
||||||
`REFERENCED_TABLE_NAME` Nullable(String),
|
`REFERENCED_TABLE_NAME` Nullable(String),
|
||||||
`REFERENCED_COLUMN_NAME` Nullable(String)
|
`REFERENCED_COLUMN_NAME` Nullable(String)
|
||||||
)
|
) AS
|
||||||
SQL SECURITY INVOKER
|
SELECT
|
||||||
AS SELECT
|
|
||||||
'def' AS constraint_catalog,
|
'def' AS constraint_catalog,
|
||||||
database AS constraint_schema,
|
database AS constraint_schema,
|
||||||
'PRIMARY' AS constraint_name,
|
'PRIMARY' AS constraint_name,
|
||||||
@ -351,9 +346,8 @@ static constexpr std::string_view referential_constraints = R"(
|
|||||||
`DELETE_RULE` String,
|
`DELETE_RULE` String,
|
||||||
`TABLE_NAME` String,
|
`TABLE_NAME` String,
|
||||||
`REFERENCED_TABLE_NAME` String
|
`REFERENCED_TABLE_NAME` String
|
||||||
)
|
) AS
|
||||||
SQL SECURITY INVOKER
|
SELECT
|
||||||
AS SELECT
|
|
||||||
'' AS constraint_catalog,
|
'' AS constraint_catalog,
|
||||||
NULL AS constraint_name,
|
NULL AS constraint_name,
|
||||||
'' AS constraint_schema,
|
'' AS constraint_schema,
|
||||||
@ -418,9 +412,8 @@ static constexpr std::string_view statistics = R"(
|
|||||||
`INDEX_COMMENT` String,
|
`INDEX_COMMENT` String,
|
||||||
`IS_VISIBLE` String,
|
`IS_VISIBLE` String,
|
||||||
`EXPRESSION` Nullable(String)
|
`EXPRESSION` Nullable(String)
|
||||||
)
|
) AS
|
||||||
SQL SECURITY INVOKER
|
SELECT
|
||||||
AS SELECT
|
|
||||||
'' AS table_catalog,
|
'' AS table_catalog,
|
||||||
'' AS table_schema,
|
'' AS table_schema,
|
||||||
'' AS table_name,
|
'' AS table_name,
|
||||||
|
@ -2,7 +2,6 @@ test_build_sets_from_multiple_threads/test.py::test_set
|
|||||||
test_concurrent_backups_s3/test.py::test_concurrent_backups
|
test_concurrent_backups_s3/test.py::test_concurrent_backups
|
||||||
test_distributed_type_object/test.py::test_distributed_type_object
|
test_distributed_type_object/test.py::test_distributed_type_object
|
||||||
test_executable_table_function/test.py::test_executable_function_input_python
|
test_executable_table_function/test.py::test_executable_function_input_python
|
||||||
test_mask_sensitive_info/test.py::test_encryption_functions
|
|
||||||
test_merge_table_over_distributed/test.py::test_global_in
|
test_merge_table_over_distributed/test.py::test_global_in
|
||||||
test_merge_table_over_distributed/test.py::test_select_table_name_from_merge_over_distributed
|
test_merge_table_over_distributed/test.py::test_select_table_name_from_merge_over_distributed
|
||||||
test_passing_max_partitions_to_read_remotely/test.py::test_default_database_on_cluster
|
test_passing_max_partitions_to_read_remotely/test.py::test_default_database_on_cluster
|
||||||
|
@ -368,6 +368,7 @@ class PRInfo:
|
|||||||
(ext in DIFF_IN_DOCUMENTATION_EXT and path_in_docs)
|
(ext in DIFF_IN_DOCUMENTATION_EXT and path_in_docs)
|
||||||
or "docker/docs" in f
|
or "docker/docs" in f
|
||||||
or "docs_check.py" in f
|
or "docs_check.py" in f
|
||||||
|
or "aspell-dict.txt" in f
|
||||||
or ext == ".md"
|
or ext == ".md"
|
||||||
):
|
):
|
||||||
return False
|
return False
|
||||||
|
5
tests/config/config.d/backoff_failed_mutation.xml
Normal file
5
tests/config/config.d/backoff_failed_mutation.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<clickhouse>
|
||||||
|
<merge_tree>
|
||||||
|
<max_postpone_time_for_failed_mutations_ms>200</max_postpone_time_for_failed_mutations_ms>
|
||||||
|
</merge_tree>
|
||||||
|
</clickhouse>
|
@ -30,6 +30,7 @@ ln -sf $SRC_PATH/config.d/graphite_alternative.xml $DEST_SERVER_PATH/config.d/
|
|||||||
ln -sf $SRC_PATH/config.d/database_atomic.xml $DEST_SERVER_PATH/config.d/
|
ln -sf $SRC_PATH/config.d/database_atomic.xml $DEST_SERVER_PATH/config.d/
|
||||||
ln -sf $SRC_PATH/config.d/max_concurrent_queries.xml $DEST_SERVER_PATH/config.d/
|
ln -sf $SRC_PATH/config.d/max_concurrent_queries.xml $DEST_SERVER_PATH/config.d/
|
||||||
ln -sf $SRC_PATH/config.d/merge_tree_settings.xml $DEST_SERVER_PATH/config.d/
|
ln -sf $SRC_PATH/config.d/merge_tree_settings.xml $DEST_SERVER_PATH/config.d/
|
||||||
|
ln -sf $SRC_PATH/config.d/backoff_failed_mutation.xml $DEST_SERVER_PATH/config.d/
|
||||||
ln -sf $SRC_PATH/config.d/merge_tree_old_dirs_cleanup.xml $DEST_SERVER_PATH/config.d/
|
ln -sf $SRC_PATH/config.d/merge_tree_old_dirs_cleanup.xml $DEST_SERVER_PATH/config.d/
|
||||||
ln -sf $SRC_PATH/config.d/test_cluster_with_incorrect_pw.xml $DEST_SERVER_PATH/config.d/
|
ln -sf $SRC_PATH/config.d/test_cluster_with_incorrect_pw.xml $DEST_SERVER_PATH/config.d/
|
||||||
ln -sf $SRC_PATH/config.d/keeper_port.xml $DEST_SERVER_PATH/config.d/
|
ln -sf $SRC_PATH/config.d/keeper_port.xml $DEST_SERVER_PATH/config.d/
|
||||||
|
@ -104,8 +104,8 @@ def run_and_check(
|
|||||||
res = subprocess.run(
|
res = subprocess.run(
|
||||||
args, stdout=stdout, stderr=stderr, env=env, shell=shell, timeout=timeout
|
args, stdout=stdout, stderr=stderr, env=env, shell=shell, timeout=timeout
|
||||||
)
|
)
|
||||||
out = res.stdout.decode("utf-8")
|
out = res.stdout.decode("utf-8", "ignore")
|
||||||
err = res.stderr.decode("utf-8")
|
err = res.stderr.decode("utf-8", "ignore")
|
||||||
# check_call(...) from subprocess does not print stderr, so we do it manually
|
# check_call(...) from subprocess does not print stderr, so we do it manually
|
||||||
for outline in out.splitlines():
|
for outline in out.splitlines():
|
||||||
logging.debug(f"Stdout:{outline}")
|
logging.debug(f"Stdout:{outline}")
|
||||||
|
0
tests/integration/test_failed_mutations/__init__.py
Normal file
0
tests/integration/test_failed_mutations/__init__.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<clickhouse>
|
||||||
|
<merge_tree>
|
||||||
|
<max_postpone_time_for_failed_mutations_ms>60000</max_postpone_time_for_failed_mutations_ms>
|
||||||
|
</merge_tree>
|
||||||
|
</clickhouse>
|
@ -0,0 +1,5 @@
|
|||||||
|
<clickhouse>
|
||||||
|
<merge_tree>
|
||||||
|
<max_postpone_time_for_failed_mutations_ms>0</max_postpone_time_for_failed_mutations_ms>
|
||||||
|
</merge_tree>
|
||||||
|
</clickhouse>
|
201
tests/integration/test_failed_mutations/test.py
Normal file
201
tests/integration/test_failed_mutations/test.py
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
import time
|
||||||
|
import pytest
|
||||||
|
from helpers.cluster import ClickHouseCluster
|
||||||
|
|
||||||
|
cluster = ClickHouseCluster(__file__)
|
||||||
|
|
||||||
|
node_with_backoff = cluster.add_instance(
|
||||||
|
"node_with_backoff",
|
||||||
|
macros={"cluster": "test_cluster"},
|
||||||
|
main_configs=["configs/config.d/backoff_mutation_policy.xml"],
|
||||||
|
with_zookeeper=True,
|
||||||
|
stay_alive=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
node_no_backoff = cluster.add_instance(
|
||||||
|
"node_no_backoff",
|
||||||
|
macros={"cluster": "test_cluster"},
|
||||||
|
main_configs=["configs/config.d/no_backoff_mutation_policy.xml"],
|
||||||
|
with_zookeeper=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
REPLICATED_POSPONE_MUTATION_LOG = (
|
||||||
|
"According to exponential backoff policy, put aside this log entry"
|
||||||
|
)
|
||||||
|
POSPONE_MUTATION_LOG = (
|
||||||
|
"According to exponential backoff policy, do not perform mutations for the part"
|
||||||
|
)
|
||||||
|
|
||||||
|
all_nodes = [node_with_backoff, node_no_backoff]
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_cluster(use_replicated_table):
|
||||||
|
for node in all_nodes:
|
||||||
|
node.query("DROP TABLE IF EXISTS test_mutations SYNC")
|
||||||
|
|
||||||
|
engine = (
|
||||||
|
"ReplicatedMergeTree('/clickhouse/{cluster}/tables/test/test_mutations', '{instance}')"
|
||||||
|
if use_replicated_table
|
||||||
|
else "MergeTree()"
|
||||||
|
)
|
||||||
|
|
||||||
|
for node in all_nodes:
|
||||||
|
node.rotate_logs()
|
||||||
|
node.query(f"CREATE TABLE test_mutations(x UInt32) ENGINE {engine} ORDER BY x")
|
||||||
|
node.query("INSERT INTO test_mutations SELECT * FROM system.numbers LIMIT 10")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def started_cluster():
|
||||||
|
try:
|
||||||
|
cluster.start()
|
||||||
|
yield cluster
|
||||||
|
|
||||||
|
finally:
|
||||||
|
cluster.shutdown()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("node, found_in_log"),
|
||||||
|
[
|
||||||
|
(
|
||||||
|
node_with_backoff,
|
||||||
|
True,
|
||||||
|
),
|
||||||
|
(
|
||||||
|
node_no_backoff,
|
||||||
|
False,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_exponential_backoff_with_merge_tree(started_cluster, node, found_in_log):
|
||||||
|
prepare_cluster(False)
|
||||||
|
|
||||||
|
# Executing incorrect mutation.
|
||||||
|
node.query(
|
||||||
|
"ALTER TABLE test_mutations DELETE WHERE x IN (SELECT x FROM notexist_table) SETTINGS allow_nondeterministic_mutations=1"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert node.contains_in_log(POSPONE_MUTATION_LOG) == found_in_log
|
||||||
|
node.rotate_logs()
|
||||||
|
|
||||||
|
node.query("KILL MUTATION WHERE table='test_mutations'")
|
||||||
|
# Check that after kill new parts mutations are postponing.
|
||||||
|
node.query(
|
||||||
|
"ALTER TABLE test_mutations DELETE WHERE x IN (SELECT x FROM notexist_table) SETTINGS allow_nondeterministic_mutations=1"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert node.contains_in_log(POSPONE_MUTATION_LOG) == found_in_log
|
||||||
|
|
||||||
|
|
||||||
|
def test_exponential_backoff_with_replicated_tree(started_cluster):
|
||||||
|
prepare_cluster(True)
|
||||||
|
|
||||||
|
node_with_backoff.query(
|
||||||
|
"ALTER TABLE test_mutations DELETE WHERE x IN (SELECT x FROM notexist_table) SETTINGS allow_nondeterministic_mutations=1"
|
||||||
|
)
|
||||||
|
|
||||||
|
assert node_with_backoff.wait_for_log_line(REPLICATED_POSPONE_MUTATION_LOG)
|
||||||
|
assert not node_no_backoff.contains_in_log(REPLICATED_POSPONE_MUTATION_LOG)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("node"),
|
||||||
|
[
|
||||||
|
(node_with_backoff),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_exponential_backoff_create_dependent_table(started_cluster, node):
|
||||||
|
prepare_cluster(False)
|
||||||
|
|
||||||
|
# Executing incorrect mutation.
|
||||||
|
node.query(
|
||||||
|
"ALTER TABLE test_mutations DELETE WHERE x IN (SELECT x FROM dep_table) SETTINGS allow_nondeterministic_mutations=1"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Creating dependent table for mutation.
|
||||||
|
node.query("CREATE TABLE dep_table(x UInt32) ENGINE MergeTree() ORDER BY x")
|
||||||
|
|
||||||
|
retry_count = 100
|
||||||
|
no_unfinished_mutation = False
|
||||||
|
for _ in range(0, retry_count):
|
||||||
|
if node.query("SELECT count() FROM system.mutations WHERE is_done=0") == "0\n":
|
||||||
|
no_unfinished_mutation = True
|
||||||
|
break
|
||||||
|
|
||||||
|
assert no_unfinished_mutation
|
||||||
|
node.query("DROP TABLE IF EXISTS dep_table SYNC")
|
||||||
|
|
||||||
|
|
||||||
|
def test_exponential_backoff_setting_override(started_cluster):
|
||||||
|
node = node_with_backoff
|
||||||
|
node.rotate_logs()
|
||||||
|
node.query("DROP TABLE IF EXISTS test_mutations SYNC")
|
||||||
|
node.query(
|
||||||
|
"CREATE TABLE test_mutations(x UInt32) ENGINE=MergeTree() ORDER BY x SETTINGS max_postpone_time_for_failed_mutations_ms=0"
|
||||||
|
)
|
||||||
|
node.query("INSERT INTO test_mutations SELECT * FROM system.numbers LIMIT 10")
|
||||||
|
|
||||||
|
# Executing incorrect mutation.
|
||||||
|
node.query(
|
||||||
|
"ALTER TABLE test_mutations DELETE WHERE x IN (SELECT x FROM dep_table) SETTINGS allow_nondeterministic_mutations=1"
|
||||||
|
)
|
||||||
|
assert not node.contains_in_log(POSPONE_MUTATION_LOG)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("replicated_table"),
|
||||||
|
[
|
||||||
|
(False),
|
||||||
|
(True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_backoff_clickhouse_restart(started_cluster, replicated_table):
|
||||||
|
prepare_cluster(replicated_table)
|
||||||
|
node = node_with_backoff
|
||||||
|
|
||||||
|
# Executing incorrect mutation.
|
||||||
|
node.query(
|
||||||
|
"ALTER TABLE test_mutations DELETE WHERE x IN (SELECT x FROM dep_table) SETTINGS allow_nondeterministic_mutations=1"
|
||||||
|
)
|
||||||
|
assert node.wait_for_log_line(
|
||||||
|
REPLICATED_POSPONE_MUTATION_LOG if replicated_table else POSPONE_MUTATION_LOG
|
||||||
|
)
|
||||||
|
|
||||||
|
node.restart_clickhouse()
|
||||||
|
node.rotate_logs()
|
||||||
|
|
||||||
|
assert node.wait_for_log_line(
|
||||||
|
REPLICATED_POSPONE_MUTATION_LOG if replicated_table else POSPONE_MUTATION_LOG
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("replicated_table"),
|
||||||
|
[
|
||||||
|
(False),
|
||||||
|
(True),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
def test_no_backoff_after_killing_mutation(started_cluster, replicated_table):
|
||||||
|
prepare_cluster(replicated_table)
|
||||||
|
node = node_with_backoff
|
||||||
|
|
||||||
|
# Executing incorrect mutation.
|
||||||
|
node.query(
|
||||||
|
"ALTER TABLE test_mutations DELETE WHERE x IN (SELECT x FROM dep_table) SETTINGS allow_nondeterministic_mutations=1"
|
||||||
|
)
|
||||||
|
# Executing correct mutation.
|
||||||
|
node.query("ALTER TABLE test_mutations DELETE WHERE x=1")
|
||||||
|
assert node.wait_for_log_line(
|
||||||
|
REPLICATED_POSPONE_MUTATION_LOG if replicated_table else POSPONE_MUTATION_LOG
|
||||||
|
)
|
||||||
|
mutation_ids = node.query("select mutation_id from system.mutations").split()
|
||||||
|
|
||||||
|
node.query(
|
||||||
|
f"KILL MUTATION WHERE table = 'test_mutations' AND mutation_id = '{mutation_ids[0]}'"
|
||||||
|
)
|
||||||
|
node.rotate_logs()
|
||||||
|
assert not node.contains_in_log(
|
||||||
|
REPLICATED_POSPONE_MUTATION_LOG if replicated_table else POSPONE_MUTATION_LOG
|
||||||
|
)
|
@ -723,7 +723,6 @@ def test_materialized_view(started_cluster):
|
|||||||
pg_manager.execute(f"INSERT INTO test_table SELECT 3, 4")
|
pg_manager.execute(f"INSERT INTO test_table SELECT 3, 4")
|
||||||
check_tables_are_synchronized(instance, "test_table")
|
check_tables_are_synchronized(instance, "test_table")
|
||||||
assert "1\t2\n3\t4" == instance.query("SELECT * FROM mv ORDER BY 1, 2").strip()
|
assert "1\t2\n3\t4" == instance.query("SELECT * FROM mv ORDER BY 1, 2").strip()
|
||||||
instance.query("DROP VIEW mv")
|
|
||||||
pg_manager.drop_materialized_db()
|
pg_manager.drop_materialized_db()
|
||||||
|
|
||||||
|
|
||||||
|
@ -1 +1 @@
|
|||||||
CREATE VIEW default.test_view_00599\n(\n `id` UInt64\n)\nSQL SECURITY INVOKER\nAS SELECT *\nFROM default.test_00599\nWHERE id = (\n SELECT 1\n)
|
CREATE VIEW default.test_view_00599\n(\n `id` UInt64\n) AS\nSELECT *\nFROM default.test_00599\nWHERE id = (\n SELECT 1\n)
|
||||||
|
@ -6,9 +6,8 @@ CREATE MATERIALIZED VIEW default.t_mv_00751
|
|||||||
)
|
)
|
||||||
ENGINE = MergeTree
|
ENGINE = MergeTree
|
||||||
ORDER BY date
|
ORDER BY date
|
||||||
SETTINGS index_granularity = 8192
|
SETTINGS index_granularity = 8192 AS
|
||||||
DEFINER = default SQL SECURITY DEFINER
|
SELECT
|
||||||
AS SELECT
|
|
||||||
date,
|
date,
|
||||||
platform,
|
platform,
|
||||||
app
|
app
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
CREATE VIEW default.t\n(\n `number` UInt64\n)\nSQL SECURITY INVOKER\nAS SELECT number\nFROM system.numbers
|
CREATE VIEW default.t\n(\n `number` UInt64\n) AS\nSELECT number\nFROM system.numbers
|
||||||
CREATE VIEW default.t\n(\n `next_number` UInt64\n)\nSQL SECURITY INVOKER\nAS SELECT number + 1 AS next_number\nFROM system.numbers
|
CREATE VIEW default.t\n(\n `next_number` UInt64\n) AS\nSELECT number + 1 AS next_number\nFROM system.numbers
|
||||||
|
@ -6,6 +6,6 @@ CREATE TABLE default.distributed\n(\n `n` Int8\n)\nENGINE = Distributed(\'tes
|
|||||||
CREATE TABLE default.distributed_tf\n(\n `n` Int8\n) AS cluster(\'test_shard_localhost\', \'default\', \'buffer\')
|
CREATE TABLE default.distributed_tf\n(\n `n` Int8\n) AS cluster(\'test_shard_localhost\', \'default\', \'buffer\')
|
||||||
CREATE TABLE default.url\n(\n `n` UInt64,\n `col` String\n)\nENGINE = URL(\'https://localhost:8443/?query=select+n,+_table+from+default.merge+format+CSV\', \'CSV\')
|
CREATE TABLE default.url\n(\n `n` UInt64,\n `col` String\n)\nENGINE = URL(\'https://localhost:8443/?query=select+n,+_table+from+default.merge+format+CSV\', \'CSV\')
|
||||||
CREATE TABLE default.rich_syntax\n(\n `n` Int64\n) AS remote(\'localhos{x|y|t}\', cluster(\'test_shard_localhost\', remote(\'127.0.0.{1..4}\', \'default\', \'view\')))
|
CREATE TABLE default.rich_syntax\n(\n `n` Int64\n) AS remote(\'localhos{x|y|t}\', cluster(\'test_shard_localhost\', remote(\'127.0.0.{1..4}\', \'default\', \'view\')))
|
||||||
CREATE VIEW default.view\n(\n `n` Int64\n)\nSQL SECURITY INVOKER\nAS SELECT toInt64(n) AS n\nFROM\n(\n SELECT toString(n) AS n\n FROM default.merge\n WHERE _table != \'qwerty\'\n ORDER BY _table ASC\n)\nUNION ALL\nSELECT *\nFROM default.file
|
CREATE VIEW default.view\n(\n `n` Int64\n) AS\nSELECT toInt64(n) AS n\nFROM\n(\n SELECT toString(n) AS n\n FROM default.merge\n WHERE _table != \'qwerty\'\n ORDER BY _table ASC\n)\nUNION ALL\nSELECT *\nFROM default.file
|
||||||
CREATE DICTIONARY default.dict\n(\n `n` UInt64,\n `col` String DEFAULT \'42\'\n)\nPRIMARY KEY n\nSOURCE(CLICKHOUSE(HOST \'localhost\' PORT 9440 SECURE 1 USER \'default\' TABLE \'url\'))\nLIFETIME(MIN 0 MAX 1)\nLAYOUT(CACHE(SIZE_IN_CELLS 1))
|
CREATE DICTIONARY default.dict\n(\n `n` UInt64,\n `col` String DEFAULT \'42\'\n)\nPRIMARY KEY n\nSOURCE(CLICKHOUSE(HOST \'localhost\' PORT 9440 SECURE 1 USER \'default\' TABLE \'url\'))\nLIFETIME(MIN 0 MAX 1)\nLAYOUT(CACHE(SIZE_IN_CELLS 1))
|
||||||
16
|
16
|
||||||
|
@ -4,18 +4,18 @@
|
|||||||
2 4
|
2 4
|
||||||
3 9
|
3 9
|
||||||
4 16
|
4 16
|
||||||
CREATE MATERIALIZED VIEW default.mv UUID \'e15f3ab5-6cae-4df3-b879-f40deafd82c2\'\n(\n `n` Int32,\n `n2` Int64\n)\nENGINE = MergeTree\nPARTITION BY n % 10\nORDER BY n\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT\n n,\n n * n AS n2\nFROM default.src
|
CREATE MATERIALIZED VIEW default.mv UUID \'e15f3ab5-6cae-4df3-b879-f40deafd82c2\'\n(\n `n` Int32,\n `n2` Int64\n)\nENGINE = MergeTree\nPARTITION BY n % 10\nORDER BY n AS\nSELECT\n n,\n n * n AS n2\nFROM default.src
|
||||||
1 1
|
1 1
|
||||||
2 4
|
2 4
|
||||||
CREATE MATERIALIZED VIEW default.mv UUID \'e15f3ab5-6cae-4df3-b879-f40deafd82c2\'\n(\n `n` Int32,\n `n2` Int64\n)\nENGINE = MergeTree\nPARTITION BY n % 10\nORDER BY n\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT\n n,\n n * n AS n2\nFROM default.src
|
CREATE MATERIALIZED VIEW default.mv UUID \'e15f3ab5-6cae-4df3-b879-f40deafd82c2\'\n(\n `n` Int32,\n `n2` Int64\n)\nENGINE = MergeTree\nPARTITION BY n % 10\nORDER BY n AS\nSELECT\n n,\n n * n AS n2\nFROM default.src
|
||||||
1 1
|
1 1
|
||||||
2 4
|
2 4
|
||||||
3 9
|
3 9
|
||||||
4 16
|
4 16
|
||||||
CREATE MATERIALIZED VIEW default.mv UUID \'e15f3ab5-6cae-4df3-b879-f40deafd82c2\' TO INNER UUID \'3bd68e3c-2693-4352-ad66-a66eba9e345e\'\n(\n `n` Int32,\n `n2` Int64\n)\nENGINE = MergeTree\nPARTITION BY n % 10\nORDER BY n\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT\n n,\n n * n AS n2\nFROM default.src
|
CREATE MATERIALIZED VIEW default.mv UUID \'e15f3ab5-6cae-4df3-b879-f40deafd82c2\' TO INNER UUID \'3bd68e3c-2693-4352-ad66-a66eba9e345e\'\n(\n `n` Int32,\n `n2` Int64\n)\nENGINE = MergeTree\nPARTITION BY n % 10\nORDER BY n AS\nSELECT\n n,\n n * n AS n2\nFROM default.src
|
||||||
1 1
|
1 1
|
||||||
2 4
|
2 4
|
||||||
CREATE MATERIALIZED VIEW default.mv UUID \'e15f3ab5-6cae-4df3-b879-f40deafd82c2\' TO INNER UUID \'3bd68e3c-2693-4352-ad66-a66eba9e345e\'\n(\n `n` Int32,\n `n2` Int64\n)\nENGINE = MergeTree\nPARTITION BY n % 10\nORDER BY n\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT\n n,\n n * n AS n2\nFROM default.src
|
CREATE MATERIALIZED VIEW default.mv UUID \'e15f3ab5-6cae-4df3-b879-f40deafd82c2\' TO INNER UUID \'3bd68e3c-2693-4352-ad66-a66eba9e345e\'\n(\n `n` Int32,\n `n2` Int64\n)\nENGINE = MergeTree\nPARTITION BY n % 10\nORDER BY n AS\nSELECT\n n,\n n * n AS n2\nFROM default.src
|
||||||
1 1
|
1 1
|
||||||
2 4
|
2 4
|
||||||
3 9
|
3 9
|
||||||
|
@ -49,7 +49,6 @@ ALTER DATABASE [] \N ALTER
|
|||||||
ALTER VIEW REFRESH ['ALTER LIVE VIEW REFRESH','REFRESH VIEW'] VIEW ALTER VIEW
|
ALTER VIEW REFRESH ['ALTER LIVE VIEW REFRESH','REFRESH VIEW'] VIEW ALTER VIEW
|
||||||
ALTER VIEW MODIFY QUERY ['ALTER TABLE MODIFY QUERY'] VIEW ALTER VIEW
|
ALTER VIEW MODIFY QUERY ['ALTER TABLE MODIFY QUERY'] VIEW ALTER VIEW
|
||||||
ALTER VIEW MODIFY REFRESH ['ALTER TABLE MODIFY QUERY'] VIEW ALTER VIEW
|
ALTER VIEW MODIFY REFRESH ['ALTER TABLE MODIFY QUERY'] VIEW ALTER VIEW
|
||||||
ALTER VIEW MODIFY SQL SECURITY ['ALTER TABLE MODIFY SQL SECURITY'] VIEW ALTER VIEW
|
|
||||||
ALTER VIEW [] \N ALTER
|
ALTER VIEW [] \N ALTER
|
||||||
ALTER [] \N ALL
|
ALTER [] \N ALL
|
||||||
CREATE DATABASE [] DATABASE CREATE
|
CREATE DATABASE [] DATABASE CREATE
|
||||||
@ -91,7 +90,6 @@ DROP QUOTA [] GLOBAL ACCESS MANAGEMENT
|
|||||||
CREATE SETTINGS PROFILE ['CREATE PROFILE'] GLOBAL ACCESS MANAGEMENT
|
CREATE SETTINGS PROFILE ['CREATE PROFILE'] GLOBAL ACCESS MANAGEMENT
|
||||||
ALTER SETTINGS PROFILE ['ALTER PROFILE'] GLOBAL ACCESS MANAGEMENT
|
ALTER SETTINGS PROFILE ['ALTER PROFILE'] GLOBAL ACCESS MANAGEMENT
|
||||||
DROP SETTINGS PROFILE ['DROP PROFILE'] GLOBAL ACCESS MANAGEMENT
|
DROP SETTINGS PROFILE ['DROP PROFILE'] GLOBAL ACCESS MANAGEMENT
|
||||||
ALLOW SQL SECURITY NONE ['CREATE SQL SECURITY NONE','ALLOW SQL SECURITY NONE','SQL SECURITY NONE','SECURITY NONE'] GLOBAL ACCESS MANAGEMENT
|
|
||||||
SHOW USERS ['SHOW CREATE USER'] GLOBAL SHOW ACCESS
|
SHOW USERS ['SHOW CREATE USER'] GLOBAL SHOW ACCESS
|
||||||
SHOW ROLES ['SHOW CREATE ROLE'] GLOBAL SHOW ACCESS
|
SHOW ROLES ['SHOW CREATE ROLE'] GLOBAL SHOW ACCESS
|
||||||
SHOW ROW POLICIES ['SHOW POLICIES','SHOW CREATE ROW POLICY','SHOW CREATE POLICY'] TABLE SHOW ACCESS
|
SHOW ROW POLICIES ['SHOW POLICIES','SHOW CREATE ROW POLICY','SHOW CREATE POLICY'] TABLE SHOW ACCESS
|
||||||
@ -103,7 +101,6 @@ SHOW NAMED COLLECTIONS ['SHOW NAMED COLLECTIONS'] NAMED_COLLECTION NAMED COLLECT
|
|||||||
SHOW NAMED COLLECTIONS SECRETS ['SHOW NAMED COLLECTIONS SECRETS'] NAMED_COLLECTION NAMED COLLECTION ADMIN
|
SHOW NAMED COLLECTIONS SECRETS ['SHOW NAMED COLLECTIONS SECRETS'] NAMED_COLLECTION NAMED COLLECTION ADMIN
|
||||||
NAMED COLLECTION ['NAMED COLLECTION USAGE','USE NAMED COLLECTION'] NAMED_COLLECTION NAMED COLLECTION ADMIN
|
NAMED COLLECTION ['NAMED COLLECTION USAGE','USE NAMED COLLECTION'] NAMED_COLLECTION NAMED COLLECTION ADMIN
|
||||||
NAMED COLLECTION ADMIN ['NAMED COLLECTION CONTROL'] NAMED_COLLECTION ALL
|
NAMED COLLECTION ADMIN ['NAMED COLLECTION CONTROL'] NAMED_COLLECTION ALL
|
||||||
SET DEFINER [] USER_NAME ALL
|
|
||||||
SYSTEM SHUTDOWN ['SYSTEM KILL','SHUTDOWN'] GLOBAL SYSTEM
|
SYSTEM SHUTDOWN ['SYSTEM KILL','SHUTDOWN'] GLOBAL SYSTEM
|
||||||
SYSTEM DROP DNS CACHE ['SYSTEM DROP DNS','DROP DNS CACHE','DROP DNS'] GLOBAL SYSTEM DROP CACHE
|
SYSTEM DROP DNS CACHE ['SYSTEM DROP DNS','DROP DNS CACHE','DROP DNS'] GLOBAL SYSTEM DROP CACHE
|
||||||
SYSTEM DROP MARK CACHE ['SYSTEM DROP MARK','DROP MARK CACHE','DROP MARKS'] GLOBAL SYSTEM DROP CACHE
|
SYSTEM DROP MARK CACHE ['SYSTEM DROP MARK','DROP MARK CACHE','DROP MARKS'] GLOBAL SYSTEM DROP CACHE
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
CREATE VIEW test_1602.v\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n)\nSQL SECURITY INVOKER\nAS SELECT *\nFROM test_1602.tbl
|
CREATE VIEW test_1602.v\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n) AS\nSELECT *\nFROM test_1602.tbl
|
||||||
CREATE MATERIALIZED VIEW test_1602.vv\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n)\nENGINE = MergeTree\nPARTITION BY toYYYYMM(EventDate)\nORDER BY (CounterID, EventDate, intHash32(UserID))\nSETTINGS index_granularity = 8192\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT *\nFROM test_1602.tbl
|
CREATE MATERIALIZED VIEW test_1602.vv\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n)\nENGINE = MergeTree\nPARTITION BY toYYYYMM(EventDate)\nORDER BY (CounterID, EventDate, intHash32(UserID))\nSETTINGS index_granularity = 8192 AS\nSELECT *\nFROM test_1602.tbl
|
||||||
CREATE VIEW test_1602.VIEW\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n)\nSQL SECURITY INVOKER\nAS SELECT *\nFROM test_1602.tbl
|
CREATE VIEW test_1602.VIEW\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n) AS\nSELECT *\nFROM test_1602.tbl
|
||||||
CREATE VIEW test_1602.DATABASE\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n)\nSQL SECURITY INVOKER\nAS SELECT *\nFROM test_1602.tbl
|
CREATE VIEW test_1602.DATABASE\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n) AS\nSELECT *\nFROM test_1602.tbl
|
||||||
CREATE VIEW test_1602.DICTIONARY\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n)\nSQL SECURITY INVOKER\nAS SELECT *\nFROM test_1602.tbl
|
CREATE VIEW test_1602.DICTIONARY\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n) AS\nSELECT *\nFROM test_1602.tbl
|
||||||
CREATE VIEW test_1602.`TABLE`\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n)\nSQL SECURITY INVOKER\nAS SELECT *\nFROM test_1602.tbl
|
CREATE VIEW test_1602.`TABLE`\n(\n `EventDate` DateTime,\n `CounterID` UInt32,\n `UserID` UInt32\n) AS\nSELECT *\nFROM test_1602.tbl
|
||||||
|
@ -20,7 +20,7 @@ $CLICKHOUSE_CLIENT -nm -q "
|
|||||||
insert into test_shard values (1, 1);
|
insert into test_shard values (1, 1);
|
||||||
insert into test_local values (1, 2);
|
insert into test_local values (1, 2);
|
||||||
|
|
||||||
create materialized view $CLICKHOUSE_DATABASE.test_distributed engine Distributed('test_cluster_two_shards', $CLICKHOUSE_DATABASE, 'test_shard', k) as select k, v from test_source;
|
create materialized view test_distributed engine Distributed('test_cluster_two_shards', $CLICKHOUSE_DATABASE, 'test_shard', k) as select k, v from test_source;
|
||||||
|
|
||||||
select * from test_distributed td asof join $CLICKHOUSE_DATABASE.test_local tl on td.k = tl.k and td.v < tl.v;
|
select * from test_distributed td asof join $CLICKHOUSE_DATABASE.test_local tl on td.k = tl.k and td.v < tl.v;
|
||||||
select td.v, td.k, td.v, tl.v, tl.k, td.v from test_distributed td asof join $CLICKHOUSE_DATABASE.test_local tl on td.k = tl.k and td.v < tl.v FORMAT TSVWithNamesAndTypes;
|
select td.v, td.k, td.v, tl.v, tl.k, td.v from test_distributed td asof join $CLICKHOUSE_DATABASE.test_local tl on td.k = tl.k and td.v < tl.v FORMAT TSVWithNamesAndTypes;
|
||||||
|
@ -1 +1 @@
|
|||||||
CREATE VIEW default.my_view\n(\n `Id` UInt32,\n `Object.Key` Array(UInt16),\n `Object.Value` Array(String)\n)\nSQL SECURITY INVOKER\nAS SELECT * REPLACE arrayMap(x -> (x + 1), `Object.Key`) AS `Object.Key`\nFROM default.my_table
|
CREATE VIEW default.my_view\n(\n `Id` UInt32,\n `Object.Key` Array(UInt16),\n `Object.Value` Array(String)\n) AS\nSELECT * REPLACE arrayMap(x -> (x + 1), `Object.Key`) AS `Object.Key`\nFROM default.my_table
|
||||||
|
@ -60,4 +60,4 @@
|
|||||||
178
|
178
|
||||||
188
|
188
|
||||||
198
|
198
|
||||||
02177_MV_3 19 0 2
|
02177_MV_3 20 0 1
|
||||||
|
@ -9,7 +9,7 @@ CREATE TABLE default.numbers1\n(\n `number` UInt64\n)\nENGINE = Memory
|
|||||||
CREATE TABLE default.numbers2\n(\n `number` UInt64\n)\nENGINE = MergeTree\nORDER BY intHash32(number)\nSAMPLE BY intHash32(number)\nSETTINGS index_granularity = 8192
|
CREATE TABLE default.numbers2\n(\n `number` UInt64\n)\nENGINE = MergeTree\nORDER BY intHash32(number)\nSAMPLE BY intHash32(number)\nSETTINGS index_granularity = 8192
|
||||||
45
|
45
|
||||||
CREATE TABLE default.numbers3\n(\n `number` UInt64\n)\nENGINE = Log
|
CREATE TABLE default.numbers3\n(\n `number` UInt64\n)\nENGINE = Log
|
||||||
CREATE MATERIALIZED VIEW default.test_view_filtered\n(\n `EventDate` Date,\n `CounterID` UInt32\n)\nENGINE = Memory\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT\n CounterID,\n EventDate\nFROM default.test_table\nWHERE EventDate < \'2013-01-01\'
|
CREATE MATERIALIZED VIEW default.test_view_filtered\n(\n `EventDate` Date,\n `CounterID` UInt32\n)\nENGINE = Memory AS\nSELECT\n CounterID,\n EventDate\nFROM default.test_table\nWHERE EventDate < \'2013-01-01\'
|
||||||
2014-01-02 0 0 1969-12-31 16:00:00 2014-01-02 03:04:06
|
2014-01-02 0 0 1969-12-31 16:00:00 2014-01-02 03:04:06
|
||||||
1 2014-01-01 19:04:06
|
1 2014-01-01 19:04:06
|
||||||
CREATE TABLE default.t1\n(\n `Rows` UInt64,\n `MaxHitTime` DateTime(\'UTC\')\n)\nENGINE = MergeTree\nORDER BY Rows\nSETTINGS index_granularity = 8192
|
CREATE TABLE default.t1\n(\n `Rows` UInt64,\n `MaxHitTime` DateTime(\'UTC\')\n)\nENGINE = MergeTree\nORDER BY Rows\nSETTINGS index_granularity = 8192
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
CREATE DATABASE INFORMATION_SCHEMA\nENGINE = Memory
|
CREATE VIEW INFORMATION_SCHEMA.COLUMNS\n(\n `table_catalog` String,\n `table_schema` String,\n `table_name` String,\n `column_name` String,\n `ordinal_position` UInt64,\n `column_default` String,\n `is_nullable` String,\n `data_type` String,\n `character_maximum_length` Nullable(UInt64),\n `character_octet_length` Nullable(UInt64),\n `numeric_precision` Nullable(UInt64),\n `numeric_precision_radix` Nullable(UInt64),\n `numeric_scale` Nullable(UInt64),\n `datetime_precision` Nullable(UInt64),\n `character_set_catalog` Nullable(String),\n `character_set_schema` Nullable(String),\n `character_set_name` Nullable(String),\n `collation_catalog` Nullable(String),\n `collation_schema` Nullable(String),\n `collation_name` Nullable(String),\n `domain_catalog` Nullable(String),\n `domain_schema` Nullable(String),\n `domain_name` Nullable(String),\n `extra` Nullable(String),\n `column_comment` String,\n `column_type` String,\n `TABLE_CATALOG` String,\n `TABLE_SCHEMA` String,\n `TABLE_NAME` String,\n `COLUMN_NAME` String,\n `ORDINAL_POSITION` UInt64,\n `COLUMN_DEFAULT` String,\n `IS_NULLABLE` String,\n `DATA_TYPE` String,\n `CHARACTER_MAXIMUM_LENGTH` Nullable(UInt64),\n `CHARACTER_OCTET_LENGTH` Nullable(UInt64),\n `NUMERIC_PRECISION` Nullable(UInt64),\n `NUMERIC_PRECISION_RADIX` Nullable(UInt64),\n `NUMERIC_SCALE` Nullable(UInt64),\n `DATETIME_PRECISION` Nullable(UInt64),\n `CHARACTER_SET_CATALOG` Nullable(String),\n `CHARACTER_SET_SCHEMA` Nullable(String),\n `CHARACTER_SET_NAME` Nullable(String),\n `COLLATION_CATALOG` Nullable(String),\n `COLLATION_SCHEMA` Nullable(String),\n `COLLATION_NAME` Nullable(String),\n `DOMAIN_CATALOG` Nullable(String),\n `DOMAIN_SCHEMA` Nullable(String),\n `DOMAIN_NAME` Nullable(String),\n `EXTRA` Nullable(String),\n `COLUMN_COMMENT` String,\n `COLUMN_TYPE` String\n) AS\nSELECT\n database AS table_catalog,\n database AS table_schema,\n `table` AS table_name,\n name AS column_name,\n position AS ordinal_position,\n default_expression AS column_default,\n type LIKE \'Nullable(%)\' AS is_nullable,\n type AS data_type,\n character_octet_length AS character_maximum_length,\n character_octet_length,\n numeric_precision,\n numeric_precision_radix,\n numeric_scale,\n datetime_precision,\n NULL AS character_set_catalog,\n NULL AS character_set_schema,\n NULL AS character_set_name,\n NULL AS collation_catalog,\n NULL AS collation_schema,\n NULL AS collation_name,\n NULL AS domain_catalog,\n NULL AS domain_schema,\n NULL AS domain_name,\n multiIf(default_kind = \'DEFAULT\', \'DEFAULT_GENERATED\', default_kind = \'MATERIALIZED\', \'STORED GENERATED\', default_kind = \'ALIAS\', \'VIRTUAL GENERATED\', \'\') AS extra,\n comment AS column_comment,\n type AS column_type,\n table_catalog AS TABLE_CATALOG,\n table_schema AS TABLE_SCHEMA,\n table_name AS TABLE_NAME,\n column_name AS COLUMN_NAME,\n ordinal_position AS ORDINAL_POSITION,\n column_default AS COLUMN_DEFAULT,\n is_nullable AS IS_NULLABLE,\n data_type AS DATA_TYPE,\n character_maximum_length AS CHARACTER_MAXIMUM_LENGTH,\n character_octet_length AS CHARACTER_OCTET_LENGTH,\n numeric_precision AS NUMERIC_PRECISION,\n numeric_precision_radix AS NUMERIC_PRECISION_RADIX,\n numeric_scale AS NUMERIC_SCALE,\n datetime_precision AS DATETIME_PRECISION,\n character_set_catalog AS CHARACTER_SET_CATALOG,\n character_set_schema AS CHARACTER_SET_SCHEMA,\n character_set_name AS CHARACTER_SET_NAME,\n collation_catalog AS COLLATION_CATALOG,\n collation_schema AS COLLATION_SCHEMA,\n collation_name AS COLLATION_NAME,\n domain_catalog AS DOMAIN_CATALOG,\n domain_schema AS DOMAIN_SCHEMA,\n domain_name AS DOMAIN_NAME,\n extra AS EXTRA,\n column_comment AS COLUMN_COMMENT,\n column_type AS COLUMN_TYPE\nFROM system.columns
|
||||||
CREATE VIEW INFORMATION_SCHEMA.COLUMNS\n(\n `table_catalog` String,\n `table_schema` String,\n `table_name` String,\n `column_name` String,\n `ordinal_position` UInt64,\n `column_default` String,\n `is_nullable` String,\n `data_type` String,\n `character_maximum_length` Nullable(UInt64),\n `character_octet_length` Nullable(UInt64),\n `numeric_precision` Nullable(UInt64),\n `numeric_precision_radix` Nullable(UInt64),\n `numeric_scale` Nullable(UInt64),\n `datetime_precision` Nullable(UInt64),\n `character_set_catalog` Nullable(String),\n `character_set_schema` Nullable(String),\n `character_set_name` Nullable(String),\n `collation_catalog` Nullable(String),\n `collation_schema` Nullable(String),\n `collation_name` Nullable(String),\n `domain_catalog` Nullable(String),\n `domain_schema` Nullable(String),\n `domain_name` Nullable(String),\n `extra` Nullable(String),\n `column_comment` String,\n `column_type` String,\n `TABLE_CATALOG` String,\n `TABLE_SCHEMA` String,\n `TABLE_NAME` String,\n `COLUMN_NAME` String,\n `ORDINAL_POSITION` UInt64,\n `COLUMN_DEFAULT` String,\n `IS_NULLABLE` String,\n `DATA_TYPE` String,\n `CHARACTER_MAXIMUM_LENGTH` Nullable(UInt64),\n `CHARACTER_OCTET_LENGTH` Nullable(UInt64),\n `NUMERIC_PRECISION` Nullable(UInt64),\n `NUMERIC_PRECISION_RADIX` Nullable(UInt64),\n `NUMERIC_SCALE` Nullable(UInt64),\n `DATETIME_PRECISION` Nullable(UInt64),\n `CHARACTER_SET_CATALOG` Nullable(String),\n `CHARACTER_SET_SCHEMA` Nullable(String),\n `CHARACTER_SET_NAME` Nullable(String),\n `COLLATION_CATALOG` Nullable(String),\n `COLLATION_SCHEMA` Nullable(String),\n `COLLATION_NAME` Nullable(String),\n `DOMAIN_CATALOG` Nullable(String),\n `DOMAIN_SCHEMA` Nullable(String),\n `DOMAIN_NAME` Nullable(String),\n `EXTRA` Nullable(String),\n `COLUMN_COMMENT` String,\n `COLUMN_TYPE` String\n)\nSQL SECURITY INVOKER\nAS SELECT\n database AS table_catalog,\n database AS table_schema,\n `table` AS table_name,\n name AS column_name,\n position AS ordinal_position,\n default_expression AS column_default,\n type LIKE \'Nullable(%)\' AS is_nullable,\n type AS data_type,\n character_octet_length AS character_maximum_length,\n character_octet_length,\n numeric_precision,\n numeric_precision_radix,\n numeric_scale,\n datetime_precision,\n NULL AS character_set_catalog,\n NULL AS character_set_schema,\n NULL AS character_set_name,\n NULL AS collation_catalog,\n NULL AS collation_schema,\n NULL AS collation_name,\n NULL AS domain_catalog,\n NULL AS domain_schema,\n NULL AS domain_name,\n multiIf(default_kind = \'DEFAULT\', \'DEFAULT_GENERATED\', default_kind = \'MATERIALIZED\', \'STORED GENERATED\', default_kind = \'ALIAS\', \'VIRTUAL GENERATED\', \'\') AS extra,\n comment AS column_comment,\n type AS column_type,\n table_catalog AS TABLE_CATALOG,\n table_schema AS TABLE_SCHEMA,\n table_name AS TABLE_NAME,\n column_name AS COLUMN_NAME,\n ordinal_position AS ORDINAL_POSITION,\n column_default AS COLUMN_DEFAULT,\n is_nullable AS IS_NULLABLE,\n data_type AS DATA_TYPE,\n character_maximum_length AS CHARACTER_MAXIMUM_LENGTH,\n character_octet_length AS CHARACTER_OCTET_LENGTH,\n numeric_precision AS NUMERIC_PRECISION,\n numeric_precision_radix AS NUMERIC_PRECISION_RADIX,\n numeric_scale AS NUMERIC_SCALE,\n datetime_precision AS DATETIME_PRECISION,\n character_set_catalog AS CHARACTER_SET_CATALOG,\n character_set_schema AS CHARACTER_SET_SCHEMA,\n character_set_name AS CHARACTER_SET_NAME,\n collation_catalog AS COLLATION_CATALOG,\n collation_schema AS COLLATION_SCHEMA,\n collation_name AS COLLATION_NAME,\n domain_catalog AS DOMAIN_CATALOG,\n domain_schema AS DOMAIN_SCHEMA,\n domain_name AS DOMAIN_NAME,\n extra AS EXTRA,\n column_comment AS COLUMN_COMMENT,\n column_type AS COLUMN_TYPE\nFROM system.columns
|
CREATE VIEW INFORMATION_SCHEMA.TABLES (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
|
||||||
CREATE VIEW INFORMATION_SCHEMA.TABLES (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
|
CREATE VIEW INFORMATION_SCHEMA.tables (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
|
||||||
CREATE VIEW INFORMATION_SCHEMA.tables (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
|
CREATE VIEW information_schema.TABLES (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
|
||||||
CREATE VIEW information_schema.TABLES (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
|
CREATE VIEW information_schema.tables (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
|
||||||
CREATE VIEW information_schema.tables (`table_catalog` String, `table_schema` String, `table_name` String, `table_type` String, `table_rows` Nullable(UInt64), `data_length` Nullable(UInt64), `table_collation` Nullable(String), `table_comment` Nullable(String), `TABLE_CATALOG` String, `TABLE_SCHEMA` String, `TABLE_NAME` String, `TABLE_TYPE` String, `TABLE_ROWS` Nullable(UInt64), `DATA_LENGTH` Nullable(UInt64), `TABLE_COLLATION` Nullable(String), `TABLE_COMMENT` Nullable(String)) SQL SECURITY INVOKER AS SELECT database AS table_catalog, database AS table_schema, name AS table_name, multiIf(is_temporary, \'LOCAL TEMPORARY\', engine LIKE \'%View\', \'VIEW\', engine LIKE \'System%\', \'SYSTEM VIEW\', has_own_data = 0, \'FOREIGN TABLE\', \'BASE TABLE\') AS table_type, total_rows AS table_rows, total_bytes AS data_length, \'utf8mb4_0900_ai_ci\' AS table_collation, comment AS table_comment, table_catalog AS TABLE_CATALOG, table_schema AS TABLE_SCHEMA, table_name AS TABLE_NAME, table_type AS TABLE_TYPE, table_rows AS TABLE_ROWS, data_length AS DATA_LENGTH, table_collation AS TABLE_COLLATION, table_comment AS TABLE_COMMENT FROM system.tables
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
CREATE TABLE default.t\n(\n `1` UInt8\n)\nENGINE = Memory
|
CREATE TABLE default.t\n(\n `1` UInt8\n)\nENGINE = Memory
|
||||||
0
|
0
|
||||||
CREATE MATERIALIZED VIEW default.mv\n(\n `1` UInt8\n)\nENGINE = Memory\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT 1
|
CREATE MATERIALIZED VIEW default.mv\n(\n `1` UInt8\n)\nENGINE = Memory AS\nSELECT 1
|
||||||
0
|
0
|
||||||
|
@ -18,7 +18,7 @@ Using HTTP with query params
|
|||||||
Using client options
|
Using client options
|
||||||
0
|
0
|
||||||
2
|
2
|
||||||
CREATE VIEW default.`02539_settings_alias_view`\n(\n `1` UInt8\n)\nSQL SECURITY INVOKER\nAS SELECT 1\nSETTINGS replication_alter_partitions_sync = 2
|
CREATE VIEW default.`02539_settings_alias_view`\n(\n `1` UInt8\n) AS\nSELECT 1\nSETTINGS replication_alter_partitions_sync = 2
|
||||||
replication_alter_partitions_sync 0 1 alter_sync
|
replication_alter_partitions_sync 0 1 alter_sync
|
||||||
replication_alter_partitions_sync 2 1 alter_sync
|
replication_alter_partitions_sync 2 1 alter_sync
|
||||||
alter_sync 0 1
|
alter_sync 0 1
|
||||||
|
@ -11,7 +11,7 @@ $CLICKHOUSE_CLIENT -n -q "
|
|||||||
|
|
||||||
CREATE TABLE input (key Int) Engine=Null;
|
CREATE TABLE input (key Int) Engine=Null;
|
||||||
CREATE TABLE output AS input Engine=Null;
|
CREATE TABLE output AS input Engine=Null;
|
||||||
CREATE MATERIALIZED VIEW mv TO output SQL SECURITY NONE AS SELECT * FROM input;
|
CREATE MATERIALIZED VIEW mv TO output AS SELECT * FROM input;
|
||||||
"
|
"
|
||||||
|
|
||||||
for allow_experimental_analyzer in 0 1; do
|
for allow_experimental_analyzer in 0 1; do
|
||||||
|
@ -1 +1 @@
|
|||||||
CREATE VIEW default.test_view\n(\n `date` Date,\n `__sign` Int8,\n `from` Float64,\n `to` Float64\n)\nSQL SECURITY INVOKER\nAS WITH cte AS\n (\n SELECT\n date,\n __sign,\n from,\n to\n FROM default.test_table\n FINAL\n )\nSELECT\n date,\n __sign,\n from,\n to\nFROM cte
|
CREATE VIEW default.test_view\n(\n `date` Date,\n `__sign` Int8,\n `from` Float64,\n `to` Float64\n) AS\nWITH cte AS\n (\n SELECT\n date,\n __sign,\n from,\n to\n FROM default.test_table\n FINAL\n )\nSELECT\n date,\n __sign,\n from,\n to\nFROM cte
|
||||||
|
@ -1,32 +0,0 @@
|
|||||||
===== StorageView =====
|
|
||||||
OK
|
|
||||||
OK
|
|
||||||
OK
|
|
||||||
2
|
|
||||||
2
|
|
||||||
OK
|
|
||||||
OK
|
|
||||||
2
|
|
||||||
2
|
|
||||||
OK
|
|
||||||
2
|
|
||||||
2
|
|
||||||
OK
|
|
||||||
===== MaterializedView =====
|
|
||||||
OK
|
|
||||||
0
|
|
||||||
0
|
|
||||||
OK
|
|
||||||
OK
|
|
||||||
OK
|
|
||||||
2
|
|
||||||
OK
|
|
||||||
OK
|
|
||||||
===== TestGrants =====
|
|
||||||
OK
|
|
||||||
OK
|
|
||||||
===== TestRowPolicy =====
|
|
||||||
1 1
|
|
||||||
2 2
|
|
||||||
6 6
|
|
||||||
9 9
|
|
@ -1,226 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
||||||
# shellcheck source=../shell_config.sh
|
|
||||||
. "$CURDIR"/../shell_config.sh
|
|
||||||
|
|
||||||
|
|
||||||
user1="user02884_1_$RANDOM$RANDOM"
|
|
||||||
user2="user02884_2_$RANDOM$RANDOM"
|
|
||||||
user3="user02884_3_$RANDOM$RANDOM"
|
|
||||||
db="db02884_$RANDOM$RANDOM"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --multiquery <<EOF
|
|
||||||
DROP DATABASE IF EXISTS $db;
|
|
||||||
CREATE DATABASE $db;
|
|
||||||
CREATE TABLE $db.test_table (s String) ENGINE = MergeTree ORDER BY s;
|
|
||||||
|
|
||||||
DROP USER IF EXISTS $user1, $user2, $user3;
|
|
||||||
CREATE USER $user1, $user2, $user3;
|
|
||||||
GRANT SELECT ON $db.* TO $user1;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
echo "===== StorageView ====="
|
|
||||||
${CLICKHOUSE_CLIENT} --multiquery <<EOF
|
|
||||||
CREATE VIEW $db.test_view_1 (s String)
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
|
|
||||||
CREATE DEFINER $user1 VIEW $db.test_view_2 (s String)
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_view_3 (s String)
|
|
||||||
DEFINER = $user1 SQL SECURITY DEFINER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_view_4 (s String)
|
|
||||||
DEFINER = $user1 SQL SECURITY INVOKER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_view_5 (s String)
|
|
||||||
SQL SECURITY INVOKER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_view_6 (s String)
|
|
||||||
SQL SECURITY DEFINER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_view_7 (s String)
|
|
||||||
DEFINER CURRENT_USER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_view_8 (s String)
|
|
||||||
DEFINER $user3
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_view_9 (s String)
|
|
||||||
SQL SECURITY NONE
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_view_10 (s String)
|
|
||||||
SQL SECURITY DEFINER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --query "SHOW TABLE $db.test_view_1" 2>&1 | grep -c "INVOKER") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --query "SHOW TABLE $db.test_view_2" 2>&1 | grep -c "DEFINER = $user1") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --multiquery <<EOF
|
|
||||||
GRANT SELECT ON $db.test_view_1 TO $user2;
|
|
||||||
GRANT SELECT ON $db.test_view_2 TO $user2;
|
|
||||||
GRANT SELECT ON $db.test_view_3 TO $user2;
|
|
||||||
GRANT SELECT ON $db.test_view_4 TO $user2;
|
|
||||||
GRANT SELECT ON $db.test_view_5 TO $user2;
|
|
||||||
GRANT SELECT ON $db.test_view_6 TO $user2;
|
|
||||||
GRANT SELECT ON $db.test_view_7 TO $user2;
|
|
||||||
GRANT SELECT ON $db.test_view_8 TO $user2;
|
|
||||||
GRANT SELECT ON $db.test_view_9 TO $user2;
|
|
||||||
GRANT SELECT ON $db.test_view_10 TO $user2;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "INSERT INTO $db.test_table VALUES ('foo'), ('bar');"
|
|
||||||
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_view_1" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_view_2"
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_view_3"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_view_4" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_view_5" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_view_6"
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_view_7"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_view_8" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_view_9"
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_view_10"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "ALTER TABLE $db.test_view_10 MODIFY SQL SECURITY INVOKER"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_view_10" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
|
|
||||||
|
|
||||||
echo "===== MaterializedView ====="
|
|
||||||
${CLICKHOUSE_CLIENT} --query "
|
|
||||||
CREATE MATERIALIZED VIEW $db.test_mv_1 (s String)
|
|
||||||
ENGINE = MergeTree ORDER BY s
|
|
||||||
DEFINER = $user1 SQL SECURITY DEFINER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
"
|
|
||||||
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --query "
|
|
||||||
CREATE MATERIALIZED VIEW $db.test_mv_2 (s String)
|
|
||||||
ENGINE = MergeTree ORDER BY s
|
|
||||||
SQL SECURITY INVOKER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
" 2>&1 | grep -c "SQL SECURITY INVOKER can't be specified for MATERIALIZED VIEW") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "
|
|
||||||
CREATE MATERIALIZED VIEW $db.test_mv_3 (s String)
|
|
||||||
ENGINE = MergeTree ORDER BY s
|
|
||||||
SQL SECURITY NONE
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "CREATE TABLE $db.test_mv_data (s String) ENGINE = MergeTree ORDER BY s;"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "
|
|
||||||
CREATE MATERIALIZED VIEW $db.test_mv_4
|
|
||||||
TO $db.test_mv_data
|
|
||||||
DEFINER = $user1 SQL SECURITY DEFINER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "
|
|
||||||
CREATE MATERIALIZED VIEW $db.test_mv_5 (s String)
|
|
||||||
ENGINE = MergeTree ORDER BY s
|
|
||||||
DEFINER = $user2 SQL SECURITY DEFINER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT SELECT ON $db.test_mv_5 TO $user2"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "ALTER TABLE $db.test_mv_5 MODIFY SQL SECURITY NONE"
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_mv_5"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT SELECT ON $db.test_mv_1 TO $user2"
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT SELECT ON $db.test_mv_3 TO $user2"
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT SELECT ON $db.test_mv_4 TO $user2"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_mv_1"
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_mv_3"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "REVOKE SELECT ON $db.test_mv_data FROM $user1"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_mv_4" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --query "INSERT INTO $db.test_table VALUES ('foo'), ('bar');" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --materialized_views_ignore_errors 1 --query "INSERT INTO $db.test_table VALUES ('foo'), ('bar');" 2>&1 | grep -c "Failed to push block to view") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT INSERT ON $db.test_mv_data TO $user1"
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT SELECT ON $db.test_mv_data TO $user1"
|
|
||||||
${CLICKHOUSE_CLIENT} --query "INSERT INTO $db.test_table VALUES ('foo'), ('bar');"
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT count() FROM $db.test_mv_4"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "REVOKE SELECT ON $db.test_table FROM $user1"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_mv_4" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --query "INSERT INTO $db.test_table VALUES ('foo'), ('bar');" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
|
|
||||||
|
|
||||||
echo "===== TestGrants ====="
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT CREATE ON *.* TO $user1"
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT SELECT ON $db.test_table TO $user1, $user2"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user1 --query "
|
|
||||||
CREATE VIEW $db.test_view_g_1
|
|
||||||
DEFINER = CURRENT_USER SQL SECURITY DEFINER
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
"
|
|
||||||
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --user $user1 --query "
|
|
||||||
CREATE VIEW $db.test_view_g_2
|
|
||||||
DEFINER = $user2
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT SET DEFINER ON $user2 TO $user1"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user1 --query "
|
|
||||||
CREATE VIEW $db.test_view_g_2
|
|
||||||
DEFINER = $user2
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
"
|
|
||||||
|
|
||||||
(( $(${CLICKHOUSE_CLIENT} --user $user1 --query "
|
|
||||||
CREATE VIEW $db.test_view_g_3
|
|
||||||
SQL SECURITY NONE
|
|
||||||
AS SELECT * FROM $db.test_table;
|
|
||||||
" 2>&1 | grep -c "Not enough privileges") >= 1 )) && echo "OK" || echo "UNEXPECTED"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "GRANT SET DEFINER ON $user2 TO $user1"
|
|
||||||
|
|
||||||
|
|
||||||
echo "===== TestRowPolicy ====="
|
|
||||||
${CLICKHOUSE_CLIENT} --multiquery <<EOF
|
|
||||||
CREATE TABLE $db.test_row_t (x Int32, y Int32) ENGINE = MergeTree ORDER BY x;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_view_row_1 DEFINER = $user1 SQL SECURITY DEFINER AS SELECT x, y AS z FROM $db.test_row_t;
|
|
||||||
CREATE ROW POLICY r1 ON $db.test_row_t FOR SELECT USING x <= y TO $user1;
|
|
||||||
CREATE ROW POLICY r2 ON $db.test_view_row_1 FOR SELECT USING x >= z TO $user2;
|
|
||||||
|
|
||||||
INSERT INTO $db.test_row_t VALUES (1, 2), (1, 1), (2, 2), (3, 2), (4, 0);
|
|
||||||
|
|
||||||
GRANT SELECT ON $db.test_view_row_1 to $user2;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_view_row_1"
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --multiquery <<EOF
|
|
||||||
CREATE TABLE $db.test_row_t2 (x Int32, y Int32) ENGINE = MergeTree ORDER BY x;
|
|
||||||
|
|
||||||
CREATE VIEW $db.test_mv_row_2 DEFINER = $user1 SQL SECURITY DEFINER AS SELECT x, y AS z FROM $db.test_row_t2;
|
|
||||||
CREATE ROW POLICY r1 ON $db.test_row_t2 FOR SELECT USING x <= y TO $user1;
|
|
||||||
CREATE ROW POLICY r2 ON $db.test_mv_row_2 FOR SELECT USING x >= z TO $user2;
|
|
||||||
|
|
||||||
INSERT INTO $db.test_row_t2 VALUES (5, 6), (6, 5), (6, 6), (8, 7), (9, 9);
|
|
||||||
|
|
||||||
GRANT SELECT ON $db.test_mv_row_2 to $user2;
|
|
||||||
EOF
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --user $user2 --query "SELECT * FROM $db.test_mv_row_2"
|
|
||||||
|
|
||||||
|
|
||||||
${CLICKHOUSE_CLIENT} --query "DROP DATABASE IF EXISTS $db;"
|
|
||||||
${CLICKHOUSE_CLIENT} --query "DROP USER IF EXISTS $user1, $user2, $user3";
|
|
@ -5,8 +5,7 @@ Row 1:
|
|||||||
statement: CREATE VIEW default.v1
|
statement: CREATE VIEW default.v1
|
||||||
(
|
(
|
||||||
`v` UInt64
|
`v` UInt64
|
||||||
)
|
) AS
|
||||||
SQL SECURITY INVOKER
|
SELECT v
|
||||||
AS SELECT v
|
|
||||||
FROM default.t1
|
FROM default.t1
|
||||||
SETTINGS additional_table_filters = {'default.t1':'s != \'s1%\''}
|
SETTINGS additional_table_filters = {'default.t1':'s != \'s1%\''}
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
v UInt64
|
v UInt64
|
||||||
v2 UInt8
|
v2 UInt8
|
||||||
CREATE MATERIALIZED VIEW default.pipe TO default.dest\n(\n `v` UInt64,\n `v2` UInt8\n)\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT\n v * 2 AS v,\n 1 AS v2\nFROM default.src
|
CREATE MATERIALIZED VIEW default.pipe TO default.dest\n(\n `v` UInt64,\n `v2` UInt8\n) AS\nSELECT\n v * 2 AS v,\n 1 AS v2\nFROM default.src
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<1: created view> a [] 1
|
<1: created view> a [] 1
|
||||||
CREATE MATERIALIZED VIEW default.a\nREFRESH AFTER 1 SECOND\n(\n `x` UInt64\n)\nENGINE = Memory\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT number AS x\nFROM numbers(2)\nUNION ALL\nSELECT rand64() AS x
|
CREATE MATERIALIZED VIEW default.a\nREFRESH AFTER 1 SECOND\n(\n `x` UInt64\n)\nENGINE = Memory AS\nSELECT number AS x\nFROM numbers(2)\nUNION ALL\nSELECT rand64() AS x
|
||||||
<2: refreshed> 3 1 1
|
<2: refreshed> 3 1 1
|
||||||
<3: time difference at least> 500
|
<3: time difference at least> 500
|
||||||
<4: next refresh in> 1
|
<4: next refresh in> 1
|
||||||
<4.5: altered> Scheduled Finished 2052-01-01 00:00:00
|
<4.5: altered> Scheduled Finished 2052-01-01 00:00:00
|
||||||
CREATE MATERIALIZED VIEW default.a\nREFRESH EVERY 2 YEAR\n(\n `x` Int16\n)\nENGINE = Memory\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT x * 2 AS x\nFROM default.src
|
CREATE MATERIALIZED VIEW default.a\nREFRESH EVERY 2 YEAR\n(\n `x` Int16\n)\nENGINE = Memory AS\nSELECT x * 2 AS x\nFROM default.src
|
||||||
<5: no refresh> 3
|
<5: no refresh> 3
|
||||||
<6: refreshed> 2
|
<6: refreshed> 2
|
||||||
<7: refreshed> Scheduled Finished 2054-01-01 00:00:00
|
<7: refreshed> Scheduled Finished 2054-01-01 00:00:00
|
||||||
CREATE MATERIALIZED VIEW default.b\nREFRESH EVERY 2 YEAR DEPENDS ON default.a\n(\n `y` Int32\n)\nENGINE = MergeTree\nORDER BY y\nSETTINGS index_granularity = 8192\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT x * 10 AS y\nFROM default.a
|
CREATE MATERIALIZED VIEW default.b\nREFRESH EVERY 2 YEAR DEPENDS ON default.a\n(\n `y` Int32\n)\nENGINE = MergeTree\nORDER BY y\nSETTINGS index_granularity = 8192 AS\nSELECT x * 10 AS y\nFROM default.a
|
||||||
<8: refreshed> 20
|
<8: refreshed> 20
|
||||||
<9: refreshed> a Scheduled Finished 2054-01-01 00:00:00
|
<9: refreshed> a Scheduled Finished 2054-01-01 00:00:00
|
||||||
<9: refreshed> b Scheduled Finished 2054-01-01 00:00:00
|
<9: refreshed> b Scheduled Finished 2054-01-01 00:00:00
|
||||||
@ -25,7 +25,7 @@ CREATE MATERIALIZED VIEW default.b\nREFRESH EVERY 2 YEAR DEPENDS ON default.a\n(
|
|||||||
<17: chain-refreshed> a Scheduled 2062-01-01 00:00:00
|
<17: chain-refreshed> a Scheduled 2062-01-01 00:00:00
|
||||||
<17: chain-refreshed> b Scheduled 2062-01-01 00:00:00
|
<17: chain-refreshed> b Scheduled 2062-01-01 00:00:00
|
||||||
<18: removed dependency> b Scheduled [] 2062-03-03 03:03:03 2064-01-01 00:00:00 5
|
<18: removed dependency> b Scheduled [] 2062-03-03 03:03:03 2064-01-01 00:00:00 5
|
||||||
CREATE MATERIALIZED VIEW default.b\nREFRESH EVERY 2 YEAR\n(\n `y` Int32\n)\nENGINE = MergeTree\nORDER BY y\nSETTINGS index_granularity = 8192\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT x * 10 AS y\nFROM default.a
|
CREATE MATERIALIZED VIEW default.b\nREFRESH EVERY 2 YEAR\n(\n `y` Int32\n)\nENGINE = MergeTree\nORDER BY y\nSETTINGS index_granularity = 8192 AS\nSELECT x * 10 AS y\nFROM default.a
|
||||||
<19: exception> 1
|
<19: exception> 1
|
||||||
<20: unexception> 1
|
<20: unexception> 1
|
||||||
<21: rename> 1
|
<21: rename> 1
|
||||||
@ -34,9 +34,9 @@ CREATE MATERIALIZED VIEW default.b\nREFRESH EVERY 2 YEAR\n(\n `y` Int32\n)\nE
|
|||||||
<24: rename during refresh> 1
|
<24: rename during refresh> 1
|
||||||
<25: rename during refresh> f Running
|
<25: rename during refresh> f Running
|
||||||
<27: cancelled> f Scheduled
|
<27: cancelled> f Scheduled
|
||||||
CREATE MATERIALIZED VIEW default.g\nREFRESH EVERY 1 WEEK OFFSET 3 DAY 4 HOUR RANDOMIZE FOR 4 DAY 1 HOUR\n(\n `x` Int64\n)\nENGINE = Memory\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT 42
|
CREATE MATERIALIZED VIEW default.g\nREFRESH EVERY 1 WEEK OFFSET 3 DAY 4 HOUR RANDOMIZE FOR 4 DAY 1 HOUR\n(\n `x` Int64\n)\nENGINE = Memory AS\nSELECT 42
|
||||||
<29: randomize> 1 1
|
<29: randomize> 1 1
|
||||||
CREATE MATERIALIZED VIEW default.h\nREFRESH EVERY 1 SECOND TO default.dest\n(\n `x` Int64\n)\nDEFINER = default SQL SECURITY DEFINER\nAS SELECT x * 10 AS x\nFROM default.src
|
CREATE MATERIALIZED VIEW default.h\nREFRESH EVERY 1 SECOND TO default.dest\n(\n `x` Int64\n) AS\nSELECT x * 10 AS x\nFROM default.src
|
||||||
<30: to existing table> 10
|
<30: to existing table> 10
|
||||||
<31: to existing table> 10
|
<31: to existing table> 10
|
||||||
<31: to existing table> 20
|
<31: to existing table> 20
|
||||||
|
@ -2,7 +2,7 @@ CREATE TABLE default.a\n(\n `x` Int64\n)\nENGINE = URL(\'https://example.com/
|
|||||||
CREATE TABLE default.b\n(\n `x` Int64\n)\nENGINE = URL(\'https://example.com/\', \'CSV\', headers())
|
CREATE TABLE default.b\n(\n `x` Int64\n)\nENGINE = URL(\'https://example.com/\', \'CSV\', headers())
|
||||||
CREATE TABLE default.c\n(\n `x` Int64\n)\nENGINE = S3(\'https://example.s3.amazonaws.com/a.csv\', \'NOSIGN\', \'CSV\', headers(\'foo\' = \'[HIDDEN]\'))
|
CREATE TABLE default.c\n(\n `x` Int64\n)\nENGINE = S3(\'https://example.s3.amazonaws.com/a.csv\', \'NOSIGN\', \'CSV\', headers(\'foo\' = \'[HIDDEN]\'))
|
||||||
CREATE TABLE default.d\n(\n `x` Int64\n)\nENGINE = S3(\'https://example.s3.amazonaws.com/a.csv\', \'NOSIGN\', headers(\'foo\' = \'[HIDDEN]\'))
|
CREATE TABLE default.d\n(\n `x` Int64\n)\nENGINE = S3(\'https://example.s3.amazonaws.com/a.csv\', \'NOSIGN\', headers(\'foo\' = \'[HIDDEN]\'))
|
||||||
CREATE VIEW default.e\n(\n `x` Int64\n)\nSQL SECURITY INVOKER\nAS SELECT count()\nFROM url(\'https://example.com/\', CSV, headers(\'foo\' = \'[HIDDEN]\', \'a\' = \'[HIDDEN]\'))
|
CREATE VIEW default.e\n(\n `x` Int64\n) AS\nSELECT count()\nFROM url(\'https://example.com/\', CSV, headers(\'foo\' = \'[HIDDEN]\', \'a\' = \'[HIDDEN]\'))
|
||||||
CREATE VIEW default.f\n(\n `x` Int64\n)\nSQL SECURITY INVOKER\nAS SELECT count()\nFROM url(\'https://example.com/\', CSV, headers())
|
CREATE VIEW default.f\n(\n `x` Int64\n) AS\nSELECT count()\nFROM url(\'https://example.com/\', CSV, headers())
|
||||||
CREATE VIEW default.g\n(\n `x` Int64\n)\nSQL SECURITY INVOKER\nAS SELECT count()\nFROM s3(\'https://example.s3.amazonaws.com/a.csv\', CSV, headers(\'foo\' = \'[HIDDEN]\'))
|
CREATE VIEW default.g\n(\n `x` Int64\n) AS\nSELECT count()\nFROM s3(\'https://example.s3.amazonaws.com/a.csv\', CSV, headers(\'foo\' = \'[HIDDEN]\'))
|
||||||
CREATE VIEW default.h\n(\n `x` Int64\n)\nSQL SECURITY INVOKER\nAS SELECT count()\nFROM s3(\'https://example.s3.amazonaws.com/a.csv\', headers(\'foo\' = \'[HIDDEN]\'))
|
CREATE VIEW default.h\n(\n `x` Int64\n) AS\nSELECT count()\nFROM s3(\'https://example.s3.amazonaws.com/a.csv\', headers(\'foo\' = \'[HIDDEN]\'))
|
||||||
|
Loading…
Reference in New Issue
Block a user