mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-10 09:32:06 +00:00
Merge pull request #40631 from ClickHouse/readonly-settings-allow
Allow to modify constrained settings in readonly mode
This commit is contained in:
commit
cf2db48c29
@ -26,13 +26,33 @@ The constraints are defined as the following:
|
||||
<setting_name_4>
|
||||
<readonly/>
|
||||
</setting_name_4>
|
||||
<setting_name_5>
|
||||
<min>lower_boundary</min>
|
||||
<max>upper_boundary</max>
|
||||
<changeable_in_readonly/>
|
||||
</setting_name_5>
|
||||
</constraints>
|
||||
</user_name>
|
||||
</profiles>
|
||||
```
|
||||
|
||||
If the user tries to violate the constraints an exception is thrown and the setting isn’t changed.
|
||||
There are supported three types of constraints: `min`, `max`, `readonly`. The `min` and `max` constraints specify upper and lower boundaries for a numeric setting and can be used in combination. The `readonly` constraint specifies that the user cannot change the corresponding setting at all.
|
||||
There are supported few types of constraints: `min`, `max`, `readonly` (with alias `const`) and `changeable_in_readonly`. The `min` and `max` constraints specify upper and lower boundaries for a numeric setting and can be used in combination. The `readonly` or `const` constraint specifies that the user cannot change the corresponding setting at all. The `changeable_in_readonly` constraint type allows user to change the setting within `min`/`max` range even if `readonly` setting is set to 1, otherwise settings are not allow to be changed in `readonly=1` mode. Note that `changeable_in_readonly` is supported only if `settings_constraints_replace_previous` is enabled:
|
||||
``` xml
|
||||
<access_control_improvements>
|
||||
<settings_constraints_replace_previous>true<settings_constraints_replace_previous>
|
||||
</access_control_improvements>
|
||||
```
|
||||
|
||||
If there are multiple profiles active for a user, then constraints are merged. Merge process depends on `settings_constraints_replace_previous`:
|
||||
- **true** (recommended): constraints for the same setting are replaced during merge, such that the last constraint is used and all previous are ignored including fields that are not set in new constraint.
|
||||
- **false** (default): constraints for the same setting are merged in a way that every not set type of constraint is taken from previous profile and every set type of constraint is replaced by value from new profile.
|
||||
|
||||
Read-only mode is enabled by `readonly` setting (not to confuse with `readonly` constraint type):
|
||||
- `readonly=0`: No read-only restrictions.
|
||||
- `readonly=1`: Only read queries are allowed and settings cannot be changes unless `changeable_in_readonly` is set.
|
||||
- `readonly=2`: Only read queries are allowed, but settings can be changed, except for `readonly` setting itself.
|
||||
|
||||
|
||||
**Example:** Let `users.xml` includes lines:
|
||||
|
||||
|
@ -37,8 +37,7 @@ After setting `readonly = 1`, the user can’t change `readonly` and `allow_ddl`
|
||||
|
||||
When using the `GET` method in the [HTTP interface](../../interfaces/http.md), `readonly = 1` is set automatically. To modify data, use the `POST` method.
|
||||
|
||||
Setting `readonly = 1` prohibit the user from changing all the settings. There is a way to prohibit the user
|
||||
from changing only specific settings, for details see [constraints on settings](../../operations/settings/constraints-on-settings.md).
|
||||
Setting `readonly = 1` prohibit the user from changing all the settings. There is a way to prohibit the user from changing only specific settings. Also there is a way to allow changing only specific settings under `readonly = 1` restrictions. For details see [constraints on settings](../../operations/settings/constraints-on-settings.md).
|
||||
|
||||
Default value: 0
|
||||
|
||||
|
@ -13,5 +13,5 @@ Syntax:
|
||||
``` sql
|
||||
ALTER ROLE [IF EXISTS] name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
```
|
||||
|
@ -13,5 +13,5 @@ Syntax:
|
||||
``` sql
|
||||
ALTER SETTINGS PROFILE [IF EXISTS] TO name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...]
|
||||
```
|
||||
|
@ -11,7 +11,7 @@ Syntax:
|
||||
|
||||
``` sql
|
||||
CREATE ROLE [IF NOT EXISTS | OR REPLACE] name1 [, name2 ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
## Managing Roles
|
||||
|
@ -12,7 +12,7 @@ Syntax:
|
||||
``` sql
|
||||
CREATE SETTINGS PROFILE [IF NOT EXISTS | OR REPLACE] TO name1 [ON CLUSTER cluster_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
`ON CLUSTER` clause allows creating settings profiles on a cluster, see [Distributed DDL](../../../sql-reference/distributed-ddl.md).
|
||||
|
@ -13,6 +13,6 @@ sidebar_label: ROLE
|
||||
``` sql
|
||||
ALTER ROLE [IF EXISTS] name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
|
@ -13,6 +13,6 @@ sidebar_label: SETTINGS PROFILE
|
||||
``` sql
|
||||
ALTER SETTINGS PROFILE [IF EXISTS] TO name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
|
@ -12,7 +12,7 @@ sidebar_label: "Роль"
|
||||
|
||||
```sql
|
||||
CREATE ROLE [IF NOT EXISTS | OR REPLACE] name1 [, name2 ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
## Управление ролями {#managing-roles}
|
||||
|
@ -13,7 +13,7 @@ sidebar_label: "Профиль настроек"
|
||||
``` sql
|
||||
CREATE SETTINGS PROFILE [IF NOT EXISTS | OR REPLACE] TO name1 [ON CLUSTER cluster_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
Секция `ON CLUSTER` позволяет создавать профили на кластере, см. [Распределенные DDL запросы](../../../sql-reference/distributed-ddl.md).
|
||||
|
@ -500,7 +500,7 @@ ALTER USER [IF EXISTS] name [ON CLUSTER cluster_name]
|
||||
[IDENTIFIED [WITH {PLAINTEXT_PASSWORD|SHA256_PASSWORD|DOUBLE_SHA1_PASSWORD}] BY {'password'|'hash'}]
|
||||
[[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
[DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
### 说明 {#alter-user-dscr}
|
||||
@ -540,7 +540,7 @@ ALTER USER user DEFAULT ROLE ALL EXCEPT role1, role2
|
||||
``` sql
|
||||
ALTER ROLE [IF EXISTS] name [ON CLUSTER cluster_name]
|
||||
[RENAME TO new_name]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
## 修改row policy {#alter-row-policy-statement}
|
||||
@ -584,7 +584,7 @@ ALTER QUOTA [IF EXISTS] name [ON CLUSTER cluster_name]
|
||||
``` sql
|
||||
ALTER SETTINGS PROFILE [IF EXISTS] name [ON CLUSTER cluster_name]
|
||||
[RENAME TO new_name]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...]
|
||||
```
|
||||
|
||||
[Original article](https://clickhouse.com/docs/en/query_language/alter/) <!--hide-->
|
||||
|
@ -13,5 +13,5 @@ sidebar_label: 角色
|
||||
``` sql
|
||||
ALTER ROLE [IF EXISTS] name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
```
|
||||
|
@ -13,5 +13,5 @@ sidebar_label: 配置文件设置
|
||||
``` sql
|
||||
ALTER SETTINGS PROFILE [IF EXISTS] TO name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1]
|
||||
[, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
|
||||
[SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...]
|
||||
```
|
||||
|
@ -65,7 +65,7 @@
|
||||
For example, as below:
|
||||
{"date_time":"1650918987.180175","thread_name":"#1","thread_id":"254545","level":"Trace","query_id":"","logger_name":"BaseDaemon","message":"Received signal 2","source_file":"../base/daemon/BaseDaemon.cpp; virtual void SignalListener::run()","source_line":"192"}
|
||||
To enable JSON logging support, please uncomment the entire <formatting> tag below.
|
||||
|
||||
|
||||
a) You can modify key names by changing values under tag values inside <names> tag.
|
||||
For example, to change DATE_TIME to MY_DATE_TIME, you can do like:
|
||||
<date_time>MY_DATE_TIME</date_time>
|
||||
@ -661,6 +661,13 @@
|
||||
executed by any user. You can change this behaviour by setting this to true.
|
||||
If it's set to true then this query requires "GRANT SELECT ON information_schema.<table>" just like as for ordinary tables. -->
|
||||
<select_from_information_schema_requires_grant>false</select_from_information_schema_requires_grant>
|
||||
|
||||
<!-- By default, for backward compatibility a settings profile constraint for a specific setting inherit every not set field from
|
||||
previous profile. You can change this behaviour by setting this to true.
|
||||
If it's set to true then if settings profile has a constraint for a specific setting, then this constraint completely cancels all
|
||||
actions of previous constraint (defined in other profiles) for the same specific setting, including fields that are not set by new constraint.
|
||||
It also enables 'changeable_in_readonly' constraint type -->
|
||||
<settings_constraints_replace_previous>false</settings_constraints_replace_previous>
|
||||
</access_control_improvements>
|
||||
|
||||
<!-- Default profile of settings. -->
|
||||
|
@ -171,6 +171,7 @@ void AccessControl::setUpFromMainConfig(const Poco::Util::AbstractConfiguration
|
||||
setOnClusterQueriesRequireClusterGrant(config_.getBool("access_control_improvements.on_cluster_queries_require_cluster_grant", false));
|
||||
setSelectFromSystemDatabaseRequiresGrant(config_.getBool("access_control_improvements.select_from_system_db_requires_grant", false));
|
||||
setSelectFromInformationSchemaRequiresGrant(config_.getBool("access_control_improvements.select_from_information_schema_requires_grant", false));
|
||||
setSettingsConstraintsReplacePrevious(config_.getBool("access_control_improvements.settings_constraints_replace_previous", false));
|
||||
|
||||
addStoragesFromMainConfig(config_, config_path_, get_zookeeper_function_);
|
||||
}
|
||||
|
@ -158,6 +158,9 @@ public:
|
||||
void setSelectFromInformationSchemaRequiresGrant(bool enable) { select_from_information_schema_requires_grant = enable; }
|
||||
bool doesSelectFromInformationSchemaRequireGrant() const { return select_from_information_schema_requires_grant; }
|
||||
|
||||
void setSettingsConstraintsReplacePrevious(bool enable) { settings_constraints_replace_previous = enable; }
|
||||
bool doesSettingsConstraintsReplacePrevious() const { return settings_constraints_replace_previous; }
|
||||
|
||||
std::shared_ptr<const ContextAccess> getContextAccess(
|
||||
const UUID & user_id,
|
||||
const std::vector<UUID> & current_roles,
|
||||
@ -223,6 +226,7 @@ private:
|
||||
std::atomic_bool on_cluster_queries_require_cluster_grant = false;
|
||||
std::atomic_bool select_from_system_db_requires_grant = false;
|
||||
std::atomic_bool select_from_information_schema_requires_grant = false;
|
||||
std::atomic_bool settings_constraints_replace_previous = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -35,88 +35,43 @@ void SettingsConstraints::clear()
|
||||
constraints.clear();
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::setMinValue(std::string_view setting_name, const Field & min_value)
|
||||
void SettingsConstraints::set(const String & setting_name, const Field & min_value, const Field & max_value, SettingConstraintWritability writability)
|
||||
{
|
||||
getConstraintRef(setting_name).min_value = Settings::castValueUtil(setting_name, min_value);
|
||||
auto & constraint = constraints[setting_name];
|
||||
if (!min_value.isNull())
|
||||
constraint.min_value = Settings::castValueUtil(setting_name, min_value);
|
||||
if (!max_value.isNull())
|
||||
constraint.max_value = Settings::castValueUtil(setting_name, max_value);
|
||||
constraint.writability = writability;
|
||||
}
|
||||
|
||||
Field SettingsConstraints::getMinValue(std::string_view setting_name) const
|
||||
void SettingsConstraints::get(const Settings & current_settings, std::string_view setting_name, Field & min_value, Field & max_value, SettingConstraintWritability & writability) const
|
||||
{
|
||||
const auto * ptr = tryGetConstraint(setting_name);
|
||||
if (ptr)
|
||||
return ptr->min_value;
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::setMaxValue(std::string_view setting_name, const Field & max_value)
|
||||
{
|
||||
getConstraintRef(setting_name).max_value = Settings::castValueUtil(setting_name, max_value);
|
||||
}
|
||||
|
||||
Field SettingsConstraints::getMaxValue(std::string_view setting_name) const
|
||||
{
|
||||
const auto * ptr = tryGetConstraint(setting_name);
|
||||
if (ptr)
|
||||
return ptr->max_value;
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::setReadOnly(std::string_view setting_name, bool read_only)
|
||||
{
|
||||
getConstraintRef(setting_name).read_only = read_only;
|
||||
}
|
||||
|
||||
bool SettingsConstraints::isReadOnly(std::string_view setting_name) const
|
||||
{
|
||||
const auto * ptr = tryGetConstraint(setting_name);
|
||||
if (ptr)
|
||||
return ptr->read_only;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::set(std::string_view setting_name, const Field & min_value, const Field & max_value, bool read_only)
|
||||
{
|
||||
auto & ref = getConstraintRef(setting_name);
|
||||
ref.min_value = Settings::castValueUtil(setting_name, min_value);
|
||||
ref.max_value = Settings::castValueUtil(setting_name, max_value);
|
||||
ref.read_only = read_only;
|
||||
}
|
||||
|
||||
void SettingsConstraints::get(std::string_view setting_name, Field & min_value, Field & max_value, bool & read_only) const
|
||||
{
|
||||
const auto * ptr = tryGetConstraint(setting_name);
|
||||
if (ptr)
|
||||
{
|
||||
min_value = ptr->min_value;
|
||||
max_value = ptr->max_value;
|
||||
read_only = ptr->read_only;
|
||||
}
|
||||
else
|
||||
{
|
||||
min_value = Field{};
|
||||
max_value = Field{};
|
||||
read_only = false;
|
||||
}
|
||||
auto checker = getChecker(current_settings, setting_name);
|
||||
min_value = checker.constraint.min_value;
|
||||
max_value = checker.constraint.max_value;
|
||||
writability = checker.constraint.writability;
|
||||
}
|
||||
|
||||
void SettingsConstraints::merge(const SettingsConstraints & other)
|
||||
{
|
||||
for (const auto & [other_name, other_constraint] : other.constraints)
|
||||
if (access_control->doesSettingsConstraintsReplacePrevious())
|
||||
{
|
||||
auto & constraint = getConstraintRef(other_name);
|
||||
if (!other_constraint.min_value.isNull())
|
||||
constraint.min_value = other_constraint.min_value;
|
||||
if (!other_constraint.max_value.isNull())
|
||||
constraint.max_value = other_constraint.max_value;
|
||||
if (other_constraint.read_only)
|
||||
constraint.read_only = true;
|
||||
for (const auto & [other_name, other_constraint] : other.constraints)
|
||||
constraints[other_name] = other_constraint;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto & [other_name, other_constraint] : other.constraints)
|
||||
{
|
||||
auto & constraint = constraints[other_name];
|
||||
if (!other_constraint.min_value.isNull())
|
||||
constraint.min_value = other_constraint.min_value;
|
||||
if (!other_constraint.max_value.isNull())
|
||||
constraint.max_value = other_constraint.max_value;
|
||||
if (other_constraint.writability == SettingConstraintWritability::CONST)
|
||||
constraint.writability = SettingConstraintWritability::CONST; // NOTE: In this mode <readonly/> flag cannot be overridden to be false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -180,26 +135,6 @@ bool SettingsConstraints::checkImpl(const Settings & current_settings, SettingCh
|
||||
}
|
||||
};
|
||||
|
||||
bool cannot_compare = false;
|
||||
auto less = [&](const Field & left, const Field & right)
|
||||
{
|
||||
cannot_compare = false;
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
return applyVisitor(FieldVisitorAccurateLess{}, left, right);
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
return applyVisitor(FieldVisitorAccurateLess{}, left, right);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
cannot_compare = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
{
|
||||
try
|
||||
@ -239,115 +174,119 @@ bool SettingsConstraints::checkImpl(const Settings & current_settings, SettingCh
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!current_settings.allow_ddl && setting_name == "allow_ddl")
|
||||
return getChecker(current_settings, setting_name).check(change, new_value, reaction);
|
||||
}
|
||||
|
||||
bool SettingsConstraints::Checker::check(SettingChange & change, const Field & new_value, ReactionOnViolation reaction) const
|
||||
{
|
||||
const String & setting_name = change.name;
|
||||
|
||||
auto less_or_cannot_compare = [=](const Field & left, const Field & right)
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
throw Exception("Cannot modify 'allow_ddl' setting when DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
|
||||
return applyVisitor(FieldVisitorAccurateLess{}, left, right);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/** The `readonly` value is understood as follows:
|
||||
* 0 - everything allowed.
|
||||
* 1 - only read queries can be made; you can not change the settings.
|
||||
* 2 - You can only do read queries and you can change the settings, except for the `readonly` setting.
|
||||
*/
|
||||
if (current_settings.readonly == 1)
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
throw Exception("Cannot modify '" + setting_name + "' setting in readonly mode", ErrorCodes::READONLY);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (current_settings.readonly > 1 && setting_name == "readonly")
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
throw Exception("Cannot modify 'readonly' setting in readonly mode", ErrorCodes::READONLY);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const Constraint * constraint = tryGetConstraint(setting_name);
|
||||
if (constraint)
|
||||
{
|
||||
if (constraint->read_only)
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
throw Exception("Setting " + setting_name + " should not be changed", ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const Field & min_value = constraint->min_value;
|
||||
const Field & max_value = constraint->max_value;
|
||||
if (!min_value.isNull() && !max_value.isNull() && (less(max_value, min_value) || cannot_compare))
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
throw Exception("Setting " + setting_name + " should not be changed", ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!min_value.isNull() && (less(new_value, min_value) || cannot_compare))
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
try
|
||||
{
|
||||
throw Exception(
|
||||
"Setting " + setting_name + " shouldn't be less than " + applyVisitor(FieldVisitorToString(), constraint->min_value),
|
||||
ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
|
||||
return applyVisitor(FieldVisitorAccurateLess{}, left, right);
|
||||
}
|
||||
else
|
||||
change.value = min_value;
|
||||
}
|
||||
|
||||
if (!max_value.isNull() && (less(max_value, new_value) || cannot_compare))
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
catch (...)
|
||||
{
|
||||
throw Exception(
|
||||
"Setting " + setting_name + " shouldn't be greater than " + applyVisitor(FieldVisitorToString(), constraint->max_value),
|
||||
ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
change.value = max_value;
|
||||
}
|
||||
};
|
||||
|
||||
if (!explain.empty())
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
throw Exception(explain, code);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (constraint.writability == SettingConstraintWritability::CONST)
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
throw Exception("Setting " + setting_name + " should not be changed", ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto & min_value = constraint.min_value;
|
||||
const auto & max_value = constraint.max_value;
|
||||
|
||||
if (!min_value.isNull() && !max_value.isNull() && less_or_cannot_compare(max_value, min_value))
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
throw Exception("Setting " + setting_name + " should not be changed", ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!min_value.isNull() && less_or_cannot_compare(new_value, min_value))
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
{
|
||||
throw Exception(
|
||||
"Setting " + setting_name + " shouldn't be less than " + applyVisitor(FieldVisitorToString(), min_value),
|
||||
ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
|
||||
}
|
||||
else
|
||||
change.value = min_value;
|
||||
}
|
||||
|
||||
if (!max_value.isNull() && less_or_cannot_compare(max_value, new_value))
|
||||
{
|
||||
if (reaction == THROW_ON_VIOLATION)
|
||||
{
|
||||
throw Exception(
|
||||
"Setting " + setting_name + " shouldn't be greater than " + applyVisitor(FieldVisitorToString(), max_value),
|
||||
ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
|
||||
}
|
||||
else
|
||||
change.value = max_value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
SettingsConstraints::Constraint & SettingsConstraints::getConstraintRef(std::string_view setting_name)
|
||||
SettingsConstraints::Checker SettingsConstraints::getChecker(const Settings & current_settings, std::string_view setting_name) const
|
||||
{
|
||||
if (!current_settings.allow_ddl && setting_name == "allow_ddl")
|
||||
return Checker("Cannot modify 'allow_ddl' setting when DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
|
||||
|
||||
/** The `readonly` value is understood as follows:
|
||||
* 0 - no read-only restrictions.
|
||||
* 1 - only read requests, as well as changing settings with `changable_in_readonly` flag.
|
||||
* 2 - only read requests, as well as changing settings, except for the `readonly` setting.
|
||||
*/
|
||||
|
||||
if (current_settings.readonly > 1 && setting_name == "readonly")
|
||||
return Checker("Cannot modify 'readonly' setting in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
auto it = constraints.find(setting_name);
|
||||
if (it == constraints.end())
|
||||
if (current_settings.readonly == 1)
|
||||
{
|
||||
auto setting_name_ptr = std::make_shared<const String>(setting_name);
|
||||
Constraint new_constraint;
|
||||
new_constraint.setting_name = setting_name_ptr;
|
||||
it = constraints.emplace(*setting_name_ptr, std::move(new_constraint)).first;
|
||||
if (it == constraints.end() || it->second.writability != SettingConstraintWritability::CHANGEABLE_IN_READONLY)
|
||||
return Checker("Cannot modify '" + String(setting_name) + "' setting in readonly mode", ErrorCodes::READONLY);
|
||||
}
|
||||
return it->second;
|
||||
else // For both readonly=0 and readonly=2
|
||||
{
|
||||
if (it == constraints.end())
|
||||
return Checker(); // Allowed
|
||||
}
|
||||
return Checker(it->second);
|
||||
}
|
||||
|
||||
const SettingsConstraints::Constraint * SettingsConstraints::tryGetConstraint(std::string_view setting_name) const
|
||||
{
|
||||
auto it = constraints.find(setting_name);
|
||||
if (it == constraints.end())
|
||||
return nullptr;
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
|
||||
bool SettingsConstraints::Constraint::operator==(const Constraint & other) const
|
||||
{
|
||||
return (read_only == other.read_only) && (min_value == other.min_value) && (max_value == other.max_value)
|
||||
&& (*setting_name == *other.setting_name);
|
||||
return writability == other.writability && min_value == other.min_value && max_value == other.max_value;
|
||||
}
|
||||
|
||||
bool operator ==(const SettingsConstraints & left, const SettingsConstraints & right)
|
||||
{
|
||||
return (left.constraints == right.constraints);
|
||||
return left.constraints == right.constraints;
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <Access/SettingsProfileElement.h>
|
||||
#include <Common/SettingsChanges.h>
|
||||
#include <unordered_map>
|
||||
|
||||
|
||||
namespace Poco::Util
|
||||
{
|
||||
class AbstractConfiguration;
|
||||
@ -35,18 +35,22 @@ class AccessControl;
|
||||
* <max>20000000000</max>
|
||||
* </max_memory_usage>
|
||||
* <force_index_by_date>
|
||||
* <readonly/>
|
||||
* <const/>
|
||||
* </force_index_by_date>
|
||||
* <max_threads>
|
||||
* <changable_in_readonly/>
|
||||
* </max_threads>
|
||||
* </constraints>
|
||||
* </user_profile>
|
||||
* </profiles>
|
||||
*
|
||||
* This class also checks that we are not in the read-only mode.
|
||||
* If a setting cannot be change due to the read-only mode this class throws an exception.
|
||||
* The value of `readonly` value is understood as follows:
|
||||
* 0 - everything allowed.
|
||||
* 1 - only read queries can be made; you can not change the settings.
|
||||
* 2 - you can only do read queries and you can change the settings, except for the `readonly` setting.
|
||||
* The value of `readonly` is understood as follows:
|
||||
* 0 - not read-only mode, no additional checks.
|
||||
* 1 - only read queries, as well as changing settings with <changable_in_readonly/> flag.
|
||||
* 2 - only read queries and you can change the settings, except for the `readonly` setting.
|
||||
*
|
||||
*/
|
||||
class SettingsConstraints
|
||||
{
|
||||
@ -61,17 +65,8 @@ public:
|
||||
void clear();
|
||||
bool empty() const { return constraints.empty(); }
|
||||
|
||||
void setMinValue(std::string_view setting_name, const Field & min_value);
|
||||
Field getMinValue(std::string_view setting_name) const;
|
||||
|
||||
void setMaxValue(std::string_view setting_name, const Field & max_value);
|
||||
Field getMaxValue(std::string_view setting_name) const;
|
||||
|
||||
void setReadOnly(std::string_view setting_name, bool read_only);
|
||||
bool isReadOnly(std::string_view setting_name) const;
|
||||
|
||||
void set(std::string_view setting_name, const Field & min_value, const Field & max_value, bool read_only);
|
||||
void get(std::string_view setting_name, Field & min_value, Field & max_value, bool & read_only) const;
|
||||
void set(const String & setting_name, const Field & min_value, const Field & max_value, SettingConstraintWritability writability);
|
||||
void get(const Settings & current_settings, std::string_view setting_name, Field & min_value, Field & max_value, SettingConstraintWritability & writability) const;
|
||||
|
||||
void merge(const SettingsConstraints & other);
|
||||
|
||||
@ -87,10 +82,15 @@ public:
|
||||
friend bool operator !=(const SettingsConstraints & left, const SettingsConstraints & right) { return !(left == right); }
|
||||
|
||||
private:
|
||||
enum ReactionOnViolation
|
||||
{
|
||||
THROW_ON_VIOLATION,
|
||||
CLAMP_ON_VIOLATION,
|
||||
};
|
||||
|
||||
struct Constraint
|
||||
{
|
||||
std::shared_ptr<const String> setting_name;
|
||||
bool read_only = false;
|
||||
SettingConstraintWritability writability = SettingConstraintWritability::WRITABLE;
|
||||
Field min_value;
|
||||
Field max_value;
|
||||
|
||||
@ -98,18 +98,53 @@ private:
|
||||
bool operator !=(const Constraint & other) const { return !(*this == other); }
|
||||
};
|
||||
|
||||
enum ReactionOnViolation
|
||||
struct Checker
|
||||
{
|
||||
THROW_ON_VIOLATION,
|
||||
CLAMP_ON_VIOLATION,
|
||||
Constraint constraint;
|
||||
String explain;
|
||||
int code = 0;
|
||||
|
||||
// Allows everything
|
||||
Checker() = default;
|
||||
|
||||
// Forbidden with explanation
|
||||
Checker(const String & explain_, int code_)
|
||||
: constraint{.writability = SettingConstraintWritability::CONST}
|
||||
, explain(explain_)
|
||||
, code(code_)
|
||||
{}
|
||||
|
||||
// Allow or forbid depending on range defined by constraint, also used to return stored constraint
|
||||
explicit Checker(const Constraint & constraint_)
|
||||
: constraint(constraint_)
|
||||
{}
|
||||
|
||||
// Perform checking
|
||||
bool check(SettingChange & change, const Field & new_value, ReactionOnViolation reaction) const;
|
||||
};
|
||||
|
||||
struct StringHash
|
||||
{
|
||||
using is_transparent = void;
|
||||
size_t operator()(std::string_view txt) const
|
||||
{
|
||||
return std::hash<std::string_view>{}(txt);
|
||||
}
|
||||
size_t operator()(const String & txt) const
|
||||
{
|
||||
return std::hash<String>{}(txt);
|
||||
}
|
||||
};
|
||||
|
||||
bool checkImpl(const Settings & current_settings, SettingChange & change, ReactionOnViolation reaction) const;
|
||||
|
||||
Constraint & getConstraintRef(std::string_view setting_name);
|
||||
const Constraint * tryGetConstraint(std::string_view setting_name) const;
|
||||
Checker getChecker(const Settings & current_settings, std::string_view setting_name) const;
|
||||
|
||||
std::unordered_map<std::string_view, Constraint> constraints;
|
||||
const AccessControl * access_control = nullptr;
|
||||
// Special container for heterogeneous lookups: to avoid `String` construction during `find(std::string_view)`
|
||||
using Constraints = std::unordered_map<String, Constraint, StringHash, std::equal_to<>>;
|
||||
Constraints constraints;
|
||||
|
||||
const AccessControl * access_control;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <Access/AccessControl.h>
|
||||
#include <Access/SettingsProfile.h>
|
||||
#include <Core/Settings.h>
|
||||
#include <Common/SettingConstraintWritability.h>
|
||||
#include <Common/SettingsChanges.h>
|
||||
#include <IO/ReadHelpers.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
@ -18,6 +19,10 @@ namespace
|
||||
constexpr const char ALLOW_BACKUP_SETTING_NAME[] = "allow_backup";
|
||||
}
|
||||
|
||||
namespace ErrorCodes
|
||||
{
|
||||
extern const int NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
SettingsProfileElement::SettingsProfileElement(const ASTSettingsProfileElement & ast)
|
||||
{
|
||||
@ -46,17 +51,20 @@ void SettingsProfileElement::init(const ASTSettingsProfileElement & ast, const A
|
||||
{
|
||||
setting_name = ast.setting_name;
|
||||
|
||||
/// Optionally check if a setting with that name is allowed.
|
||||
if (access_control)
|
||||
{
|
||||
/// Check if a setting with that name is allowed.
|
||||
if (setting_name != ALLOW_BACKUP_SETTING_NAME)
|
||||
access_control->checkSettingNameIsAllowed(setting_name);
|
||||
/// Check if a CHANGEABLE_IN_READONLY is allowed.
|
||||
if (ast.writability == SettingConstraintWritability::CHANGEABLE_IN_READONLY && !access_control->doesSettingsConstraintsReplacePrevious())
|
||||
throw Exception("CHANGEABLE_IN_READONLY for " + setting_name + " is not allowed unless settings_constraints_replace_previous is enabled", ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
|
||||
value = ast.value;
|
||||
min_value = ast.min_value;
|
||||
max_value = ast.max_value;
|
||||
readonly = ast.readonly;
|
||||
writability = ast.writability;
|
||||
|
||||
if (!value.isNull())
|
||||
value = Settings::castValueUtil(setting_name, value);
|
||||
@ -80,7 +88,7 @@ std::shared_ptr<ASTSettingsProfileElement> SettingsProfileElement::toAST() const
|
||||
ast->value = value;
|
||||
ast->min_value = min_value;
|
||||
ast->max_value = max_value;
|
||||
ast->readonly = readonly;
|
||||
ast->writability = writability;
|
||||
|
||||
return ast;
|
||||
}
|
||||
@ -101,7 +109,7 @@ std::shared_ptr<ASTSettingsProfileElement> SettingsProfileElement::toASTWithName
|
||||
ast->value = value;
|
||||
ast->min_value = min_value;
|
||||
ast->max_value = max_value;
|
||||
ast->readonly = readonly;
|
||||
ast->writability = writability;
|
||||
|
||||
return ast;
|
||||
}
|
||||
@ -205,17 +213,12 @@ SettingsConstraints SettingsProfileElements::toSettingsConstraints(const AccessC
|
||||
{
|
||||
SettingsConstraints res{access_control};
|
||||
for (const auto & elem : *this)
|
||||
{
|
||||
if (!elem.setting_name.empty() && (elem.setting_name != ALLOW_BACKUP_SETTING_NAME))
|
||||
{
|
||||
if (!elem.min_value.isNull())
|
||||
res.setMinValue(elem.setting_name, elem.min_value);
|
||||
if (!elem.max_value.isNull())
|
||||
res.setMaxValue(elem.setting_name, elem.max_value);
|
||||
if (elem.readonly)
|
||||
res.setReadOnly(elem.setting_name, *elem.readonly);
|
||||
}
|
||||
}
|
||||
if (!elem.setting_name.empty() && elem.setting_name != ALLOW_BACKUP_SETTING_NAME)
|
||||
res.set(
|
||||
elem.setting_name,
|
||||
elem.min_value,
|
||||
elem.max_value,
|
||||
elem.writability ? *elem.writability : SettingConstraintWritability::WRITABLE);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include <Core/Field.h>
|
||||
#include <Core/UUID.h>
|
||||
#include <Common/SettingConstraintWritability.h>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
@ -25,9 +26,9 @@ struct SettingsProfileElement
|
||||
Field value;
|
||||
Field min_value;
|
||||
Field max_value;
|
||||
std::optional<bool> readonly;
|
||||
std::optional<SettingConstraintWritability> writability;
|
||||
|
||||
auto toTuple() const { return std::tie(parent_profile, setting_name, value, min_value, max_value, readonly); }
|
||||
auto toTuple() const { return std::tie(parent_profile, setting_name, value, min_value, max_value, writability); }
|
||||
friend bool operator==(const SettingsProfileElement & lhs, const SettingsProfileElement & rhs) { return lhs.toTuple() == rhs.toTuple(); }
|
||||
friend bool operator!=(const SettingsProfileElement & lhs, const SettingsProfileElement & rhs) { return !(lhs == rhs); }
|
||||
friend bool operator <(const SettingsProfileElement & lhs, const SettingsProfileElement & rhs) { return lhs.toTuple() < rhs.toTuple(); }
|
||||
|
@ -441,17 +441,32 @@ namespace
|
||||
String path_to_name = path_to_constraints + "." + setting_name;
|
||||
config.keys(path_to_name, constraint_types);
|
||||
|
||||
size_t writability_count = 0;
|
||||
for (const String & constraint_type : constraint_types)
|
||||
{
|
||||
if (constraint_type == "min")
|
||||
profile_element.min_value = Settings::stringToValueUtil(setting_name, config.getString(path_to_name + "." + constraint_type));
|
||||
else if (constraint_type == "max")
|
||||
profile_element.max_value = Settings::stringToValueUtil(setting_name, config.getString(path_to_name + "." + constraint_type));
|
||||
else if (constraint_type == "readonly")
|
||||
profile_element.readonly = true;
|
||||
else if (constraint_type == "readonly" || constraint_type == "const")
|
||||
{
|
||||
writability_count++;
|
||||
profile_element.writability = SettingConstraintWritability::CONST;
|
||||
}
|
||||
else if (constraint_type == "changeable_in_readonly")
|
||||
{
|
||||
writability_count++;
|
||||
if (access_control.doesSettingsConstraintsReplacePrevious())
|
||||
profile_element.writability = SettingConstraintWritability::CHANGEABLE_IN_READONLY;
|
||||
else
|
||||
throw Exception("Setting changeable_in_readonly for " + setting_name + " is not allowed unless settings_constraints_replace_previous is enabled", ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
else
|
||||
throw Exception("Setting " + constraint_type + " value for " + setting_name + " isn't supported", ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
if (writability_count > 1)
|
||||
throw Exception("Not more than one constraint writability specifier (const/readonly/changeable_in_readonly) is allowed for " + setting_name, ErrorCodes::NOT_IMPLEMENTED);
|
||||
|
||||
profile_elements.push_back(std::move(profile_element));
|
||||
}
|
||||
|
||||
|
23
src/Common/SettingConstraintWritability.h
Normal file
23
src/Common/SettingConstraintWritability.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
enum class SettingConstraintWritability
|
||||
{
|
||||
// Default. Setting can be change within specified range only in `readonly=0` or `readonly=2` mode.
|
||||
WRITABLE,
|
||||
|
||||
// Setting cannot be changed at all.
|
||||
// Either READONLY or CONST keyword in SQL syntax can be used (<readonly/> or <const/> in config.xml) to enable this.
|
||||
// NOTE: name `CONST` is chosen to avoid confusion with `readonly` setting.
|
||||
CONST,
|
||||
|
||||
// Setting can be changed within specified range, regardless of `readonly` setting value.
|
||||
CHANGEABLE_IN_READONLY,
|
||||
|
||||
MAX
|
||||
};
|
||||
|
||||
}
|
@ -352,7 +352,7 @@ static constexpr UInt64 operator""_GiB(unsigned long long value)
|
||||
M(UInt64, max_ast_elements, 50000, "Maximum size of query syntax tree in number of nodes. Checked after parsing.", 0) \
|
||||
M(UInt64, max_expanded_ast_elements, 500000, "Maximum size of query syntax tree in number of nodes after expansion of aliases and the asterisk.", 0) \
|
||||
\
|
||||
M(UInt64, readonly, 0, "0 - everything is allowed. 1 - only read requests. 2 - only read requests, as well as changing settings, except for the 'readonly' setting.", 0) \
|
||||
M(UInt64, readonly, 0, "0 - no read-only restrictions. 1 - only read requests, as well as changing explicitly allowed settings. 2 - only read requests, as well as changing settings, except for the 'readonly' setting.", 0) \
|
||||
\
|
||||
M(UInt64, max_rows_in_set, 0, "Maximum size of the set (in number of elements) resulting from the execution of the IN section.", 0) \
|
||||
M(UInt64, max_bytes_in_set, 0, "Maximum size of the set (in bytes in memory) resulting from the execution of the IN section.", 0) \
|
||||
|
@ -10,11 +10,11 @@ class ASTSettingsProfileElements;
|
||||
|
||||
|
||||
/** CREATE ROLE [IF NOT EXISTS | OR REPLACE] name
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
*
|
||||
* ALTER ROLE [IF EXISTS] name
|
||||
* [RENAME TO new_name]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
*/
|
||||
class ASTCreateRoleQuery : public IAST, public ASTQueryWithOnCluster
|
||||
{
|
||||
|
@ -11,12 +11,12 @@ class ASTRolesOrUsersSet;
|
||||
|
||||
|
||||
/** CREATE SETTINGS PROFILE [IF NOT EXISTS | OR REPLACE] name
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
|
||||
*
|
||||
* ALTER SETTINGS PROFILE [IF EXISTS] name
|
||||
* [RENAME TO new_name]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
* [TO {role [,...] | ALL | ALL EXCEPT role [,...]}]
|
||||
*/
|
||||
class ASTCreateSettingsProfileQuery : public IAST, public ASTQueryWithOnCluster
|
||||
|
@ -19,7 +19,7 @@ class ASTSettingsProfileElements;
|
||||
* [HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...]]
|
||||
* [DEFAULT DATABASE database | NONE]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
* [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
|
||||
*
|
||||
* ALTER USER [IF EXISTS] name
|
||||
@ -28,7 +28,7 @@ class ASTSettingsProfileElements;
|
||||
* [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
||||
* [DEFAULT DATABASE database | NONE]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
* [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
|
||||
*/
|
||||
class ASTCreateUserQuery : public IAST, public ASTQueryWithOnCluster
|
||||
|
@ -52,10 +52,24 @@ void ASTSettingsProfileElement::formatImpl(const FormatSettings & settings, Form
|
||||
<< applyVisitor(FieldVisitorToString{}, max_value);
|
||||
}
|
||||
|
||||
if (readonly)
|
||||
if (writability)
|
||||
{
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << (*readonly ? " READONLY" : " WRITABLE")
|
||||
<< (settings.hilite ? IAST::hilite_none : "");
|
||||
switch (*writability)
|
||||
{
|
||||
case SettingConstraintWritability::WRITABLE:
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " WRITABLE"
|
||||
<< (settings.hilite ? IAST::hilite_none : "");
|
||||
break;
|
||||
case SettingConstraintWritability::CONST:
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " CONST"
|
||||
<< (settings.hilite ? IAST::hilite_none : "");
|
||||
break;
|
||||
case SettingConstraintWritability::CHANGEABLE_IN_READONLY:
|
||||
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " CHANGEABLE_IN_READONLY"
|
||||
<< (settings.hilite ? IAST::hilite_none : "");
|
||||
break;
|
||||
case SettingConstraintWritability::MAX: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,12 +2,12 @@
|
||||
|
||||
#include <Parsers/IAST.h>
|
||||
#include <Core/Field.h>
|
||||
|
||||
#include <Common/SettingConstraintWritability.h>
|
||||
|
||||
namespace DB
|
||||
{
|
||||
/** Represents a settings profile's element like the following
|
||||
* {variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE]} | PROFILE 'profile_name'
|
||||
* {variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY]} | PROFILE 'profile_name'
|
||||
*/
|
||||
class ASTSettingsProfileElement : public IAST
|
||||
{
|
||||
@ -17,7 +17,7 @@ public:
|
||||
Field value;
|
||||
Field min_value;
|
||||
Field max_value;
|
||||
std::optional<bool> readonly;
|
||||
std::optional<SettingConstraintWritability> writability;
|
||||
bool id_mode = false; /// If true then `parent_profile` keeps UUID, not a name.
|
||||
bool use_inherit_keyword = false; /// If true then this element is a part of ASTCreateSettingsProfileQuery.
|
||||
|
||||
@ -30,7 +30,7 @@ public:
|
||||
|
||||
|
||||
/** Represents settings profile's elements like the following
|
||||
* {{variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE]} | PROFILE 'profile_name'} [,...]
|
||||
* {{variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY]} | PROFILE 'profile_name'} [,...]
|
||||
*/
|
||||
class ASTSettingsProfileElements : public IAST
|
||||
{
|
||||
|
@ -7,11 +7,11 @@ namespace DB
|
||||
{
|
||||
/** Parses queries like
|
||||
* CREATE ROLE [IF NOT EXISTS | OR REPLACE] name
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
*
|
||||
* ALTER ROLE [IF EXISTS] name
|
||||
* [RENAME TO new_name]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
*/
|
||||
class ParserCreateRoleQuery : public IParserBase
|
||||
{
|
||||
|
@ -7,11 +7,11 @@ namespace DB
|
||||
{
|
||||
/** Parses queries like
|
||||
* CREATE SETTINGS PROFILE [IF NOT EXISTS | OR REPLACE] name
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...]
|
||||
*
|
||||
* ALTER SETTINGS PROFILE [IF EXISTS] name
|
||||
* [RENAME TO new_name]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | INHERIT 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...]
|
||||
*/
|
||||
class ParserCreateSettingsProfileQuery : public IParserBase
|
||||
{
|
||||
|
@ -10,7 +10,7 @@ namespace DB
|
||||
* [NOT IDENTIFIED | IDENTIFIED {[WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}}|{WITH ldap SERVER 'server_name'}|{WITH kerberos [REALM 'realm']}]
|
||||
* [HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...]]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
* [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
|
||||
*
|
||||
* ALTER USER [IF EXISTS] name
|
||||
@ -18,7 +18,7 @@ namespace DB
|
||||
* [NOT IDENTIFIED | IDENTIFIED {[WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}}|{WITH ldap SERVER 'server_name'}|{WITH kerberos [REALM 'realm']}]
|
||||
* [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE]
|
||||
* [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE] | PROFILE 'profile_name'] [,...]
|
||||
* [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...]
|
||||
* [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]]
|
||||
*/
|
||||
class ParserCreateUserQuery : public IParserBase
|
||||
|
@ -95,18 +95,23 @@ namespace
|
||||
}
|
||||
|
||||
|
||||
bool parseReadonlyOrWritableKeyword(IParserBase::Pos & pos, Expected & expected, std::optional<bool> & readonly)
|
||||
bool parseConstraintWritabilityKeyword(IParserBase::Pos & pos, Expected & expected, std::optional<SettingConstraintWritability> & writability)
|
||||
{
|
||||
return IParserBase::wrapParseImpl(pos, [&]
|
||||
{
|
||||
if (ParserKeyword{"READONLY"}.ignore(pos, expected))
|
||||
if (ParserKeyword{"READONLY"}.ignore(pos, expected) || ParserKeyword{"CONST"}.ignore(pos, expected))
|
||||
{
|
||||
readonly = true;
|
||||
writability = SettingConstraintWritability::CONST;
|
||||
return true;
|
||||
}
|
||||
else if (ParserKeyword{"WRITABLE"}.ignore(pos, expected))
|
||||
{
|
||||
readonly = false;
|
||||
writability = SettingConstraintWritability::WRITABLE;
|
||||
return true;
|
||||
}
|
||||
else if (ParserKeyword{"CHANGEABLE_IN_READONLY"}.ignore(pos, expected))
|
||||
{
|
||||
writability = SettingConstraintWritability::CHANGEABLE_IN_READONLY;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@ -122,7 +127,7 @@ namespace
|
||||
Field & value,
|
||||
Field & min_value,
|
||||
Field & max_value,
|
||||
std::optional<bool> & readonly)
|
||||
std::optional<SettingConstraintWritability> & writability)
|
||||
{
|
||||
return IParserBase::wrapParseImpl(pos, [&]
|
||||
{
|
||||
@ -134,11 +139,11 @@ namespace
|
||||
Field res_value;
|
||||
Field res_min_value;
|
||||
Field res_max_value;
|
||||
std::optional<bool> res_readonly;
|
||||
std::optional<SettingConstraintWritability> res_writability;
|
||||
|
||||
bool has_value_or_constraint = false;
|
||||
while (parseValue(pos, expected, res_value) || parseMinMaxValue(pos, expected, res_min_value, res_max_value)
|
||||
|| parseReadonlyOrWritableKeyword(pos, expected, res_readonly))
|
||||
|| parseConstraintWritabilityKeyword(pos, expected, res_writability))
|
||||
{
|
||||
has_value_or_constraint = true;
|
||||
}
|
||||
@ -147,7 +152,7 @@ namespace
|
||||
return false;
|
||||
|
||||
if (boost::iequals(res_setting_name, "PROFILE") && res_value.isNull() && res_min_value.isNull() && res_max_value.isNull()
|
||||
&& res_readonly)
|
||||
&& res_writability == SettingConstraintWritability::CONST)
|
||||
{
|
||||
/// Ambiguity: "profile readonly" can be treated either as a profile named "readonly" or
|
||||
/// as a setting named 'profile' with the readonly constraint.
|
||||
@ -159,7 +164,7 @@ namespace
|
||||
value = std::move(res_value);
|
||||
min_value = std::move(res_min_value);
|
||||
max_value = std::move(res_max_value);
|
||||
readonly = res_readonly;
|
||||
writability = res_writability;
|
||||
return true;
|
||||
});
|
||||
}
|
||||
@ -179,9 +184,9 @@ namespace
|
||||
Field value;
|
||||
Field min_value;
|
||||
Field max_value;
|
||||
std::optional<bool> readonly;
|
||||
std::optional<SettingConstraintWritability> writability;
|
||||
|
||||
bool ok = parseSettingNameWithValueOrConstraints(pos, expected, setting_name, value, min_value, max_value, readonly);
|
||||
bool ok = parseSettingNameWithValueOrConstraints(pos, expected, setting_name, value, min_value, max_value, writability);
|
||||
|
||||
if (!ok && (parseProfileKeyword(pos, expected, use_inherit_keyword) || previous_element_was_parent_profile))
|
||||
ok = parseProfileNameOrID(pos, expected, id_mode, parent_profile);
|
||||
@ -195,7 +200,7 @@ namespace
|
||||
result->value = std::move(value);
|
||||
result->min_value = std::move(min_value);
|
||||
result->max_value = std::move(max_value);
|
||||
result->readonly = readonly;
|
||||
result->writability = writability;
|
||||
result->id_mode = id_mode;
|
||||
result->use_inherit_keyword = use_inherit_keyword;
|
||||
return true;
|
||||
|
@ -6,7 +6,7 @@
|
||||
namespace DB
|
||||
{
|
||||
/** Parses a string like this:
|
||||
* {variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE]} | PROFILE 'profile_name'
|
||||
* {variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY]} | PROFILE 'profile_name'
|
||||
*/
|
||||
class ParserSettingsProfileElement : public IParserBase
|
||||
{
|
||||
|
@ -40,8 +40,8 @@ void StorageSystemSettings::fillData(MutableColumns & res_columns, ContextPtr co
|
||||
res_columns[3]->insert(setting.getDescription());
|
||||
|
||||
Field min, max;
|
||||
bool read_only = false;
|
||||
constraints.get(setting_name, min, max, read_only);
|
||||
SettingConstraintWritability writability = SettingConstraintWritability::WRITABLE;
|
||||
constraints.get(settings, setting_name, min, max, writability);
|
||||
|
||||
/// These two columns can accept strings only.
|
||||
if (!min.isNull())
|
||||
@ -49,17 +49,9 @@ void StorageSystemSettings::fillData(MutableColumns & res_columns, ContextPtr co
|
||||
if (!max.isNull())
|
||||
max = Settings::valueToStringUtil(setting_name, max);
|
||||
|
||||
if (!read_only)
|
||||
{
|
||||
if ((settings.readonly == 1)
|
||||
|| ((settings.readonly > 1) && (setting_name == "readonly"))
|
||||
|| ((!settings.allow_ddl) && (setting_name == "allow_ddl")))
|
||||
read_only = true;
|
||||
}
|
||||
|
||||
res_columns[4]->insert(min);
|
||||
res_columns[5]->insert(max);
|
||||
res_columns[6]->insert(read_only);
|
||||
res_columns[6]->insert(writability == SettingConstraintWritability::CONST);
|
||||
res_columns[7]->insert(setting.getTypeName());
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,24 @@
|
||||
#include <Access/SettingsProfile.h>
|
||||
#include <Interpreters/Context.h>
|
||||
#include <boost/range/algorithm_ext/push_back.hpp>
|
||||
#include <Common/SettingConstraintWritability.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
|
||||
const std::vector<std::pair<String, Int8>> & getSettingConstraintWritabilityEnumValues()
|
||||
{
|
||||
static const std::vector<std::pair<String, Int8>> values = []
|
||||
{
|
||||
std::vector<std::pair<String, Int8>> res;
|
||||
for (auto value : collections::range(SettingConstraintWritability::MAX))
|
||||
res.emplace_back(toString(value), static_cast<Int8>(value));
|
||||
return res;
|
||||
}();
|
||||
return values;
|
||||
}
|
||||
|
||||
NamesAndTypesList StorageSystemSettingsProfileElements::getNamesAndTypes()
|
||||
{
|
||||
NamesAndTypesList names_and_types{
|
||||
@ -28,7 +41,7 @@ NamesAndTypesList StorageSystemSettingsProfileElements::getNamesAndTypes()
|
||||
{"value", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeString>())},
|
||||
{"min", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeString>())},
|
||||
{"max", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeString>())},
|
||||
{"readonly", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeUInt8>())},
|
||||
{"writability", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeEnum8>(getSettingConstraintWritabilityEnumValues()))},
|
||||
{"inherit_profile", std::make_shared<DataTypeNullable>(std::make_shared<DataTypeString>())},
|
||||
};
|
||||
return names_and_types;
|
||||
@ -62,8 +75,8 @@ void StorageSystemSettingsProfileElements::fillData(MutableColumns & res_columns
|
||||
auto & column_min_null_map = assert_cast<ColumnNullable &>(*res_columns[i++]).getNullMapData();
|
||||
auto & column_max = assert_cast<ColumnString &>(assert_cast<ColumnNullable &>(*res_columns[i]).getNestedColumn());
|
||||
auto & column_max_null_map = assert_cast<ColumnNullable &>(*res_columns[i++]).getNullMapData();
|
||||
auto & column_readonly = assert_cast<ColumnUInt8 &>(assert_cast<ColumnNullable &>(*res_columns[i]).getNestedColumn()).getData();
|
||||
auto & column_readonly_null_map = assert_cast<ColumnNullable &>(*res_columns[i++]).getNullMapData();
|
||||
auto & column_writability = assert_cast<ColumnInt8 &>(assert_cast<ColumnNullable &>(*res_columns[i]).getNestedColumn());
|
||||
auto & column_writability_null_map = assert_cast<ColumnNullable &>(*res_columns[i++]).getNullMapData();
|
||||
auto & column_inherit_profile = assert_cast<ColumnString &>(assert_cast<ColumnNullable &>(*res_columns[i]).getNestedColumn());
|
||||
auto & column_inherit_profile_null_map = assert_cast<ColumnNullable &>(*res_columns[i++]).getNullMapData();
|
||||
|
||||
@ -100,16 +113,16 @@ void StorageSystemSettingsProfileElements::fillData(MutableColumns & res_columns
|
||||
inserted_max = true;
|
||||
}
|
||||
|
||||
bool inserted_readonly = false;
|
||||
if (element.readonly && !element.setting_name.empty())
|
||||
bool inserted_writability = false;
|
||||
if (element.writability && !element.setting_name.empty())
|
||||
{
|
||||
column_readonly.push_back(*element.readonly);
|
||||
column_readonly_null_map.push_back(false);
|
||||
inserted_readonly = true;
|
||||
column_writability.insertValue(static_cast<Int8>(*element.writability));
|
||||
column_writability_null_map.push_back(false);
|
||||
inserted_writability = true;
|
||||
}
|
||||
|
||||
bool inserted_setting_name = false;
|
||||
if (inserted_value || inserted_min || inserted_max || inserted_readonly)
|
||||
if (inserted_value || inserted_min || inserted_max || inserted_writability)
|
||||
{
|
||||
const auto & setting_name = element.setting_name;
|
||||
column_setting_name.insertData(setting_name.data(), setting_name.size());
|
||||
|
@ -4,5 +4,6 @@
|
||||
<on_cluster_queries_require_cluster_grant>true</on_cluster_queries_require_cluster_grant>
|
||||
<select_from_system_db_requires_grant>true</select_from_system_db_requires_grant>
|
||||
<select_from_information_schema_requires_grant>true</select_from_information_schema_requires_grant>
|
||||
<settings_constraints_replace_previous>true</settings_constraints_replace_previous>
|
||||
</access_control_improvements>
|
||||
</clickhouse>
|
||||
|
@ -24,5 +24,6 @@
|
||||
<on_cluster_queries_require_cluster_grant>true</on_cluster_queries_require_cluster_grant>
|
||||
<select_from_system_db_requires_grant>true</select_from_system_db_requires_grant>
|
||||
<select_from_information_schema_requires_grant>true</select_from_information_schema_requires_grant>
|
||||
<settings_constraints_replace_previous>true</settings_constraints_replace_previous>
|
||||
</access_control_improvements>
|
||||
</clickhouse>
|
||||
|
@ -3,5 +3,6 @@
|
||||
<users_without_row_policies_can_read_rows remove="remove"/>
|
||||
<select_from_system_db_requires_grant remove="remove"/>
|
||||
<select_from_information_schema_requires_grant remove="remove"/>
|
||||
<settings_constraints_replace_previous remove="remove"/>
|
||||
</access_control_improvements>
|
||||
</clickhouse>
|
||||
|
@ -93,7 +93,7 @@ def test_alter():
|
||||
instance.query("GRANT SELECT ON mydb.mytable TO u1")
|
||||
instance.query("GRANT SELECT ON mydb.* TO rx WITH GRANT OPTION")
|
||||
instance.query(
|
||||
"ALTER SETTINGS PROFILE s1 SETTINGS max_memory_usage = 987654321 READONLY"
|
||||
"ALTER SETTINGS PROFILE s1 SETTINGS max_memory_usage = 987654321 CONST"
|
||||
)
|
||||
|
||||
def check():
|
||||
@ -124,7 +124,7 @@ def test_alter():
|
||||
)
|
||||
assert (
|
||||
instance.query("SHOW CREATE SETTINGS PROFILE s1")
|
||||
== "CREATE SETTINGS PROFILE s1 SETTINGS max_memory_usage = 987654321 READONLY\n"
|
||||
== "CREATE SETTINGS PROFILE s1 SETTINGS max_memory_usage = 987654321 CONST\n"
|
||||
)
|
||||
assert (
|
||||
instance.query("SHOW CREATE SETTINGS PROFILE s2")
|
||||
|
@ -216,7 +216,18 @@ def test_settings_from_granted_role():
|
||||
"\\N",
|
||||
"\\N",
|
||||
],
|
||||
["xyz", "\\N", "\\N", 1, "max_ast_depth", 2000, "\\N", "\\N", "\\N", "\\N"],
|
||||
[
|
||||
"xyz",
|
||||
"\\N",
|
||||
"\\N",
|
||||
1,
|
||||
"max_ast_depth",
|
||||
2000,
|
||||
"\\N",
|
||||
"\\N",
|
||||
"\\N",
|
||||
"\\N",
|
||||
],
|
||||
]
|
||||
assert system_settings_profile_elements(role_name="worker") == [
|
||||
["\\N", "\\N", "worker", 0, "\\N", "\\N", "\\N", "\\N", "\\N", "xyz"]
|
||||
@ -288,12 +299,12 @@ def test_settings_from_granted_role():
|
||||
|
||||
def test_inheritance():
|
||||
instance.query(
|
||||
"CREATE SETTINGS PROFILE xyz SETTINGS max_memory_usage = 100000002 READONLY"
|
||||
"CREATE SETTINGS PROFILE xyz SETTINGS max_memory_usage = 100000002 CONST"
|
||||
)
|
||||
instance.query("CREATE SETTINGS PROFILE alpha SETTINGS PROFILE xyz TO robin")
|
||||
assert (
|
||||
instance.query("SHOW CREATE SETTINGS PROFILE xyz")
|
||||
== "CREATE SETTINGS PROFILE xyz SETTINGS max_memory_usage = 100000002 READONLY\n"
|
||||
== "CREATE SETTINGS PROFILE xyz SETTINGS max_memory_usage = 100000002 CONST\n"
|
||||
)
|
||||
assert (
|
||||
instance.query("SHOW CREATE SETTINGS PROFILE alpha")
|
||||
@ -315,7 +326,18 @@ def test_inheritance():
|
||||
["xyz", "local directory", 1, 0, "[]", "[]"]
|
||||
]
|
||||
assert system_settings_profile_elements(profile_name="xyz") == [
|
||||
["xyz", "\\N", "\\N", 0, "max_memory_usage", 100000002, "\\N", "\\N", 1, "\\N"]
|
||||
[
|
||||
"xyz",
|
||||
"\\N",
|
||||
"\\N",
|
||||
0,
|
||||
"max_memory_usage",
|
||||
100000002,
|
||||
"\\N",
|
||||
"\\N",
|
||||
"CONST",
|
||||
"\\N",
|
||||
]
|
||||
]
|
||||
assert system_settings_profile("alpha") == [
|
||||
["alpha", "local directory", 1, 0, "['robin']", "[]"]
|
||||
@ -366,6 +388,66 @@ def test_alter_and_drop():
|
||||
instance.query("SET max_memory_usage = 120000000", user="robin")
|
||||
|
||||
|
||||
def test_changeable_in_readonly():
|
||||
instance.query(
|
||||
"CREATE SETTINGS PROFILE xyz SETTINGS max_memory_usage = 100000003 MIN 90000000 MAX 110000000 CHANGEABLE_IN_READONLY SETTINGS readonly = 1 TO robin"
|
||||
)
|
||||
assert (
|
||||
instance.query(
|
||||
"SELECT value FROM system.settings WHERE name = 'max_memory_usage'",
|
||||
user="robin",
|
||||
)
|
||||
== "100000003\n"
|
||||
)
|
||||
assert (
|
||||
instance.query(
|
||||
"SELECT value FROM system.settings WHERE name = 'readonly'",
|
||||
user="robin",
|
||||
)
|
||||
== "1\n"
|
||||
)
|
||||
assert (
|
||||
"Setting max_memory_usage shouldn't be less than 90000000"
|
||||
in instance.query_and_get_error("SET max_memory_usage = 80000000", user="robin")
|
||||
)
|
||||
assert (
|
||||
"Setting max_memory_usage shouldn't be greater than 110000000"
|
||||
in instance.query_and_get_error(
|
||||
"SET max_memory_usage = 120000000", user="robin"
|
||||
)
|
||||
)
|
||||
|
||||
assert system_settings_profile_elements(profile_name="xyz") == [
|
||||
[
|
||||
"xyz",
|
||||
"\\N",
|
||||
"\\N",
|
||||
0,
|
||||
"max_memory_usage",
|
||||
100000003,
|
||||
90000000,
|
||||
110000000,
|
||||
"CHANGEABLE_IN_READONLY",
|
||||
"\\N",
|
||||
],
|
||||
[
|
||||
"xyz",
|
||||
"\\N",
|
||||
"\\N",
|
||||
1,
|
||||
"readonly",
|
||||
1,
|
||||
"\\N",
|
||||
"\\N",
|
||||
"\\N",
|
||||
"\\N",
|
||||
],
|
||||
]
|
||||
|
||||
instance.query("SET max_memory_usage = 90000000", user="robin")
|
||||
instance.query("SET max_memory_usage = 110000000", user="robin")
|
||||
|
||||
|
||||
def test_show_profiles():
|
||||
instance.query("CREATE SETTINGS PROFILE xyz")
|
||||
assert instance.query("SHOW SETTINGS PROFILES") == "default\nreadonly\nxyz\n"
|
||||
|
@ -58,9 +58,9 @@ CREATE USER u2_01292 SETTINGS PROFILE default
|
||||
CREATE USER u3_01292 SETTINGS max_memory_usage = 5000000
|
||||
CREATE USER u4_01292 SETTINGS max_memory_usage MIN 5000000
|
||||
CREATE USER u5_01292 SETTINGS max_memory_usage MAX 5000000
|
||||
CREATE USER u6_01292 SETTINGS max_memory_usage READONLY
|
||||
CREATE USER u6_01292 SETTINGS max_memory_usage CONST
|
||||
CREATE USER u7_01292 SETTINGS max_memory_usage WRITABLE
|
||||
CREATE USER u8_01292 SETTINGS max_memory_usage = 5000000 MIN 4000000 MAX 6000000 READONLY
|
||||
CREATE USER u8_01292 SETTINGS max_memory_usage = 5000000 MIN 4000000 MAX 6000000 CONST
|
||||
CREATE USER u9_01292 SETTINGS PROFILE default, max_memory_usage = 5000000 WRITABLE
|
||||
CREATE USER u1_01292 SETTINGS readonly = 1
|
||||
CREATE USER u2_01292 SETTINGS readonly = 1
|
||||
@ -111,7 +111,7 @@ u4_01292 local directory double_sha1_password {} ['::/0'] [] [] [] 1 [] ['r1_012
|
||||
-- system.settings_profile_elements
|
||||
\N u1_01292 \N 0 readonly 1 \N \N \N \N
|
||||
\N u2_01292 \N 0 \N \N \N \N \N default
|
||||
\N u3_01292 \N 0 max_memory_usage 5000000 4000000 6000000 0 \N
|
||||
\N u3_01292 \N 0 max_memory_usage 5000000 4000000 6000000 WRITABLE \N
|
||||
\N u4_01292 \N 0 \N \N \N \N \N default
|
||||
\N u4_01292 \N 1 max_memory_usage 5000000 \N \N \N \N
|
||||
\N u4_01292 \N 2 readonly 1 \N \N \N \N
|
||||
|
@ -122,9 +122,9 @@ CREATE USER u2_01292 SETTINGS PROFILE 'default';
|
||||
CREATE USER u3_01292 SETTINGS max_memory_usage=5000000;
|
||||
CREATE USER u4_01292 SETTINGS max_memory_usage MIN=5000000;
|
||||
CREATE USER u5_01292 SETTINGS max_memory_usage MAX=5000000;
|
||||
CREATE USER u6_01292 SETTINGS max_memory_usage READONLY;
|
||||
CREATE USER u6_01292 SETTINGS max_memory_usage CONST;
|
||||
CREATE USER u7_01292 SETTINGS max_memory_usage WRITABLE;
|
||||
CREATE USER u8_01292 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 READONLY;
|
||||
CREATE USER u8_01292 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 CONST;
|
||||
CREATE USER u9_01292 SETTINGS PROFILE 'default', max_memory_usage=5000000 WRITABLE;
|
||||
SHOW CREATE USER u1_01292;
|
||||
SHOW CREATE USER u2_01292;
|
||||
|
@ -15,9 +15,9 @@ CREATE ROLE r2_01293 SETTINGS PROFILE default
|
||||
CREATE ROLE r3_01293 SETTINGS max_memory_usage = 5000000
|
||||
CREATE ROLE r4_01293 SETTINGS max_memory_usage MIN 5000000
|
||||
CREATE ROLE r5_01293 SETTINGS max_memory_usage MAX 5000000
|
||||
CREATE ROLE r6_01293 SETTINGS max_memory_usage READONLY
|
||||
CREATE ROLE r6_01293 SETTINGS max_memory_usage CONST
|
||||
CREATE ROLE r7_01293 SETTINGS max_memory_usage WRITABLE
|
||||
CREATE ROLE r8_01293 SETTINGS max_memory_usage = 5000000 MIN 4000000 MAX 6000000 READONLY
|
||||
CREATE ROLE r8_01293 SETTINGS max_memory_usage = 5000000 MIN 4000000 MAX 6000000 CONST
|
||||
CREATE ROLE r9_01293 SETTINGS PROFILE default, max_memory_usage = 5000000 WRITABLE
|
||||
CREATE ROLE r1_01293 SETTINGS readonly = 1
|
||||
CREATE ROLE r2_01293 SETTINGS readonly = 1
|
||||
@ -32,7 +32,7 @@ r1_01293 local directory
|
||||
-- system.settings_profile_elements
|
||||
\N \N r1_01293 0 readonly 1 \N \N \N \N
|
||||
\N \N r2_01293 0 \N \N \N \N \N default
|
||||
\N \N r3_01293 0 max_memory_usage 5000000 4000000 6000000 0 \N
|
||||
\N \N r3_01293 0 max_memory_usage 5000000 4000000 6000000 WRITABLE \N
|
||||
\N \N r4_01293 0 \N \N \N \N \N default
|
||||
\N \N r4_01293 1 max_memory_usage 5000000 \N \N \N \N
|
||||
\N \N r4_01293 2 readonly 1 \N \N \N \N
|
||||
|
@ -31,9 +31,9 @@ CREATE ROLE r2_01293 SETTINGS PROFILE 'default';
|
||||
CREATE ROLE r3_01293 SETTINGS max_memory_usage=5000000;
|
||||
CREATE ROLE r4_01293 SETTINGS max_memory_usage MIN=5000000;
|
||||
CREATE ROLE r5_01293 SETTINGS max_memory_usage MAX=5000000;
|
||||
CREATE ROLE r6_01293 SETTINGS max_memory_usage READONLY;
|
||||
CREATE ROLE r6_01293 SETTINGS max_memory_usage CONST;
|
||||
CREATE ROLE r7_01293 SETTINGS max_memory_usage WRITABLE;
|
||||
CREATE ROLE r8_01293 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 READONLY;
|
||||
CREATE ROLE r8_01293 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 CONST;
|
||||
CREATE ROLE r9_01293 SETTINGS PROFILE 'default', max_memory_usage=5000000 WRITABLE;
|
||||
SHOW CREATE ROLE r1_01293;
|
||||
SHOW CREATE ROLE r2_01293;
|
||||
|
@ -11,9 +11,9 @@ CREATE SETTINGS PROFILE s2_01294 SETTINGS INHERIT default
|
||||
CREATE SETTINGS PROFILE s3_01294 SETTINGS max_memory_usage = 5000000
|
||||
CREATE SETTINGS PROFILE s4_01294 SETTINGS max_memory_usage MIN 5000000
|
||||
CREATE SETTINGS PROFILE s5_01294 SETTINGS max_memory_usage MAX 5000000
|
||||
CREATE SETTINGS PROFILE s6_01294 SETTINGS max_memory_usage READONLY
|
||||
CREATE SETTINGS PROFILE s6_01294 SETTINGS max_memory_usage CONST
|
||||
CREATE SETTINGS PROFILE s7_01294 SETTINGS max_memory_usage WRITABLE
|
||||
CREATE SETTINGS PROFILE s8_01294 SETTINGS max_memory_usage = 5000000 MIN 4000000 MAX 6000000 READONLY
|
||||
CREATE SETTINGS PROFILE s8_01294 SETTINGS max_memory_usage = 5000000 MIN 4000000 MAX 6000000 CONST
|
||||
CREATE SETTINGS PROFILE s9_01294 SETTINGS INHERIT default, max_memory_usage = 5000000 WRITABLE
|
||||
CREATE SETTINGS PROFILE s10_01294 SETTINGS INHERIT s1_01294, INHERIT s3_01294, INHERIT default, readonly = 0, max_memory_usage MAX 6000000
|
||||
CREATE SETTINGS PROFILE s1_01294 SETTINGS readonly = 0
|
||||
@ -47,11 +47,11 @@ CREATE SETTINGS PROFILE s3_01294 TO r1_01294
|
||||
CREATE SETTINGS PROFILE s4_01294 TO r1_01294
|
||||
-- readonly ambiguity
|
||||
CREATE SETTINGS PROFILE s1_01294 SETTINGS readonly = 1
|
||||
CREATE SETTINGS PROFILE s2_01294 SETTINGS readonly READONLY
|
||||
CREATE SETTINGS PROFILE s2_01294 SETTINGS readonly CONST
|
||||
CREATE SETTINGS PROFILE s3_01294 SETTINGS INHERIT readonly
|
||||
CREATE SETTINGS PROFILE s4_01294 SETTINGS INHERIT readonly, INHERIT readonly
|
||||
CREATE SETTINGS PROFILE s5_01294 SETTINGS INHERIT readonly, readonly = 1
|
||||
CREATE SETTINGS PROFILE s6_01294 SETTINGS INHERIT readonly, readonly READONLY
|
||||
CREATE SETTINGS PROFILE s6_01294 SETTINGS INHERIT readonly, readonly CONST
|
||||
-- system.settings_profiles
|
||||
s1_01294 local directory 0 0 [] []
|
||||
s2_01294 local directory 1 0 ['r1_01294'] []
|
||||
@ -61,8 +61,8 @@ s5_01294 local directory 3 0 ['u1_01294'] []
|
||||
s6_01294 local directory 0 1 [] ['r1_01294','u1_01294']
|
||||
-- system.settings_profile_elements
|
||||
s2_01294 \N \N 0 readonly 0 \N \N \N \N
|
||||
s3_01294 \N \N 0 max_memory_usage 5000000 4000000 6000000 1 \N
|
||||
s3_01294 \N \N 0 max_memory_usage 5000000 4000000 6000000 CONST \N
|
||||
s4_01294 \N \N 0 max_memory_usage 5000000 \N \N \N \N
|
||||
s5_01294 \N \N 0 \N \N \N \N \N default
|
||||
s5_01294 \N \N 1 readonly 0 \N \N \N \N
|
||||
s5_01294 \N \N 2 max_memory_usage \N \N 6000000 0 \N
|
||||
s5_01294 \N \N 2 max_memory_usage \N \N 6000000 WRITABLE \N
|
||||
|
@ -25,9 +25,9 @@ CREATE PROFILE s2_01294 SETTINGS INHERIT 'default';
|
||||
CREATE PROFILE s3_01294 SETTINGS max_memory_usage=5000000;
|
||||
CREATE PROFILE s4_01294 SETTINGS max_memory_usage MIN=5000000;
|
||||
CREATE PROFILE s5_01294 SETTINGS max_memory_usage MAX=5000000;
|
||||
CREATE PROFILE s6_01294 SETTINGS max_memory_usage READONLY;
|
||||
CREATE PROFILE s6_01294 SETTINGS max_memory_usage CONST;
|
||||
CREATE PROFILE s7_01294 SETTINGS max_memory_usage WRITABLE;
|
||||
CREATE PROFILE s8_01294 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 READONLY;
|
||||
CREATE PROFILE s8_01294 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 CONST;
|
||||
CREATE PROFILE s9_01294 SETTINGS INHERIT 'default', max_memory_usage=5000000 WRITABLE;
|
||||
CREATE PROFILE s10_01294 SETTINGS INHERIT s1_01294, s3_01294, INHERIT default, readonly=0, max_memory_usage MAX 6000000;
|
||||
SHOW CREATE PROFILE s1_01294;
|
||||
@ -106,7 +106,7 @@ DROP PROFILE s1_01294, s2_01294, s3_01294, s4_01294, s5_01294, s6_01294;
|
||||
SELECT '-- system.settings_profiles';
|
||||
CREATE PROFILE s1_01294;
|
||||
CREATE PROFILE s2_01294 SETTINGS readonly=0 TO r1_01294;;
|
||||
CREATE PROFILE s3_01294 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 READONLY TO r1_01294;
|
||||
CREATE PROFILE s3_01294 SETTINGS max_memory_usage=5000000 MIN 4000000 MAX 6000000 CONST TO r1_01294;
|
||||
CREATE PROFILE s4_01294 SETTINGS max_memory_usage=5000000 TO r1_01294;
|
||||
CREATE PROFILE s5_01294 SETTINGS INHERIT default, readonly=0, max_memory_usage MAX 6000000 WRITABLE TO u1_01294;
|
||||
CREATE PROFILE s6_01294 TO ALL EXCEPT u1_01294, r1_01294;
|
||||
|
@ -956,7 +956,7 @@ CREATE TABLE system.settings_profile_elements
|
||||
`value` Nullable(String),
|
||||
`min` Nullable(String),
|
||||
`max` Nullable(String),
|
||||
`readonly` Nullable(UInt8),
|
||||
`writability` Nullable(Enum8('WRITABLE' = 0, 'CONST' = 1, 'CHANGEABLE_IN_READONLY' = 2)),
|
||||
`inherit_profile` Nullable(String)
|
||||
)
|
||||
ENGINE = SystemSettingsProfileElements
|
||||
|
Loading…
Reference in New Issue
Block a user