mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 15:42:02 +00:00
Implement max
constraints for number settings.
This commit is contained in:
parent
8277e9d8f1
commit
d43e7a8960
@ -426,6 +426,7 @@ namespace ErrorCodes
|
||||
extern const int BROTLI_WRITE_FAILED = 449;
|
||||
extern const int BAD_TTL_EXPRESSION = 450;
|
||||
extern const int BAD_TTL_FILE = 451;
|
||||
extern const int SETTING_CONSTRAINT_VIOLATION = 452;
|
||||
|
||||
extern const int KEEPER_EXCEPTION = 999;
|
||||
extern const int POCO_EXCEPTION = 1000;
|
||||
|
@ -34,6 +34,8 @@ void Settings::setProfile(const String & profile_name, const Poco::Util::Abstrac
|
||||
|
||||
for (const std::string & key : config_keys)
|
||||
{
|
||||
if (key == "constraints")
|
||||
continue;
|
||||
if (key == "profile") /// Inheritance of one profile from another.
|
||||
setProfile(config.getString(elem + "." + key), config);
|
||||
else
|
||||
|
@ -620,14 +620,22 @@ void Context::calculateUserSettings()
|
||||
/// NOTE: we ignore global_context settings (from which it is usually copied)
|
||||
/// NOTE: global_context settings are immutable and not auto updated
|
||||
settings = Settings();
|
||||
settings_constraints = SettingsConstraints();
|
||||
|
||||
/// 2) Apply settings from default profile
|
||||
auto default_profile_name = getDefaultProfileName();
|
||||
if (profile != default_profile_name)
|
||||
settings.setProfile(default_profile_name, *shared->users_config);
|
||||
setProfile(default_profile_name);
|
||||
|
||||
/// 3) Apply settings from current user
|
||||
setProfile(profile);
|
||||
}
|
||||
|
||||
|
||||
void Context::setProfile(const String & profile)
|
||||
{
|
||||
settings.setProfile(profile, *shared->users_config);
|
||||
settings_constraints.setProfile(profile, *shared->users_config);
|
||||
}
|
||||
|
||||
|
||||
@ -1033,7 +1041,7 @@ void Context::setSetting(const String & name, const String & value)
|
||||
auto lock = getLock();
|
||||
if (name == "profile")
|
||||
{
|
||||
settings.setProfile(value, *shared->users_config);
|
||||
setProfile(value);
|
||||
return;
|
||||
}
|
||||
settings.set(name, value);
|
||||
@ -1045,7 +1053,7 @@ void Context::setSetting(const String & name, const Field & value)
|
||||
auto lock = getLock();
|
||||
if (name == "profile")
|
||||
{
|
||||
settings.setProfile(value.safeGet<String>(), *shared->users_config);
|
||||
setProfile(value.safeGet<String>());
|
||||
return;
|
||||
}
|
||||
settings.set(name, value);
|
||||
@ -1068,13 +1076,13 @@ void Context::applySettingsChanges(const SettingsChanges & changes)
|
||||
|
||||
void Context::checkSettingsConstraints(const SettingChange & change)
|
||||
{
|
||||
SettingsConstraints::check(settings, change);
|
||||
settings_constraints.check(settings, change);
|
||||
}
|
||||
|
||||
|
||||
void Context::checkSettingsConstraints(const SettingsChanges & changes)
|
||||
{
|
||||
SettingsConstraints::check(settings, changes);
|
||||
settings_constraints.check(settings, changes);
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
#include <Core/NamesAndTypes.h>
|
||||
#include <Core/Types.h>
|
||||
#include <Interpreters/ClientInfo.h>
|
||||
#include <Interpreters/SettingsConstraints.h>
|
||||
#include <Core/Settings.h>
|
||||
#include <Parsers/IAST_fwd.h>
|
||||
#include <Common/LRUCache.h>
|
||||
@ -125,6 +126,7 @@ private:
|
||||
std::shared_ptr<QuotaForIntervals> quota; /// Current quota. By default - empty quota, that have no limits.
|
||||
String current_database;
|
||||
Settings settings; /// Setting for query execution.
|
||||
SettingsConstraints settings_constraints;
|
||||
using ProgressCallback = std::function<void(const Progress & progress)>;
|
||||
ProgressCallback progress_callback; /// Callback for tracking progress of query execution.
|
||||
QueryStatus * process_list_elem = nullptr; /// For tracking total resource usage for query.
|
||||
@ -484,6 +486,8 @@ private:
|
||||
*/
|
||||
void checkDatabaseAccessRightsImpl(const std::string & database_name) const;
|
||||
|
||||
void setProfile(const String & profile);
|
||||
|
||||
EmbeddedDictionaries & getEmbeddedDictionariesImpl(bool throw_on_error) const;
|
||||
ExternalDictionaries & getExternalDictionariesImpl(bool throw_on_error) const;
|
||||
ExternalModels & getExternalModelsImpl(bool throw_on_error) const;
|
||||
|
@ -1,5 +1,8 @@
|
||||
#include <Interpreters/SettingsConstraints.h>
|
||||
#include <Core/Settings.h>
|
||||
#include <Common/FieldVisitors.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
#include <Poco/Util/AbstractConfiguration.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -9,43 +12,35 @@ namespace ErrorCodes
|
||||
extern const int READONLY;
|
||||
extern const int QUERY_IS_PROHIBITED;
|
||||
extern const int NO_ELEMENTS_IN_CONFIG;
|
||||
extern const int SETTING_CONSTRAINT_VIOLATION;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
thread_local Settings temp_settings;
|
||||
|
||||
void checkImpl(const Settings & current_settings, size_t index)
|
||||
{
|
||||
const auto & new_setting = temp_settings[index];
|
||||
Field new_value = new_setting.getValue();
|
||||
|
||||
const auto & current_setting = current_settings[index];
|
||||
Field current_value = current_setting.getValue();
|
||||
|
||||
/// Setting isn't checked if value wasn't changed.
|
||||
if (current_value == new_value)
|
||||
return;
|
||||
|
||||
const StringRef & name = new_setting.getName();
|
||||
if (!current_settings.allow_ddl && name == "allow_ddl")
|
||||
throw Exception("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 - 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)
|
||||
throw Exception("Cannot modify '" + name.toString() + "' setting in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
if (current_settings.readonly > 1 && name == "readonly")
|
||||
throw Exception("Cannot modify 'readonly' setting in readonly mode", ErrorCodes::READONLY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::check(const Settings & current_settings, const SettingChange & change)
|
||||
SettingsConstraints::SettingsConstraints() = default;
|
||||
SettingsConstraints::SettingsConstraints(const SettingsConstraints & src) = default;
|
||||
SettingsConstraints & SettingsConstraints::operator=(const SettingsConstraints & src) = default;
|
||||
SettingsConstraints::SettingsConstraints(SettingsConstraints && src) = default;
|
||||
SettingsConstraints & SettingsConstraints::operator=(SettingsConstraints && src) = default;
|
||||
SettingsConstraints::~SettingsConstraints() = default;
|
||||
|
||||
|
||||
void SettingsConstraints::setMaxValue(const String & name, const String & max_value)
|
||||
{
|
||||
max_settings.set(name, max_value);
|
||||
}
|
||||
|
||||
void SettingsConstraints::setMaxValue(const String & name, const Field & max_value)
|
||||
{
|
||||
max_settings.set(name, max_value);
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::check(const Settings & current_settings, const SettingChange & change) const
|
||||
{
|
||||
size_t index = current_settings.find(change.name);
|
||||
if (index == Settings::npos)
|
||||
@ -56,10 +51,86 @@ void SettingsConstraints::check(const Settings & current_settings, const Setting
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::check(const Settings & current_settings, const SettingsChanges & changes)
|
||||
void SettingsConstraints::check(const Settings & current_settings, const SettingsChanges & changes) const
|
||||
{
|
||||
for (const auto & change : changes)
|
||||
check(current_settings, change);
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::checkImpl(const Settings & current_settings, size_t index) const
|
||||
{
|
||||
const auto & new_setting = temp_settings[index];
|
||||
Field new_value = new_setting.getValue();
|
||||
|
||||
const auto & current_setting = current_settings[index];
|
||||
Field current_value = current_setting.getValue();
|
||||
|
||||
/// Setting isn't checked if value wasn't changed.
|
||||
if (current_value == new_value)
|
||||
return;
|
||||
|
||||
const StringRef & name = new_setting.getName();
|
||||
if (!current_settings.allow_ddl && name == "allow_ddl")
|
||||
throw Exception("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 - 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)
|
||||
throw Exception("Cannot modify '" + name.toString() + "' setting in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
if (current_settings.readonly > 1 && name == "readonly")
|
||||
throw Exception("Cannot modify 'readonly' setting in readonly mode", ErrorCodes::READONLY);
|
||||
|
||||
const auto & max_setting = max_settings[index];
|
||||
if (max_setting.isChanged())
|
||||
{
|
||||
Field max_value = max_setting.getValue();
|
||||
if (new_value > max_value)
|
||||
throw Exception(
|
||||
"Setting " + name.toString() + " shouldn't be greater than " + applyVisitor(FieldVisitorToString(), max_value),
|
||||
ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::setProfile(const String & profile_name, const Poco::Util::AbstractConfiguration & config)
|
||||
{
|
||||
String parent_profile = "profiles." + profile_name + ".profile";
|
||||
if (config.has(parent_profile))
|
||||
setProfile(parent_profile, config); // Inheritance of one profile from another.
|
||||
|
||||
String path_to_constraints = "profiles." + profile_name + ".constraints";
|
||||
if (config.has(path_to_constraints))
|
||||
loadFromConfig(path_to_constraints, config);
|
||||
}
|
||||
|
||||
|
||||
void SettingsConstraints::loadFromConfig(const String & path_to_constraints, const Poco::Util::AbstractConfiguration & config)
|
||||
{
|
||||
if (!config.has(path_to_constraints))
|
||||
throw Exception("There is no path '" + path_to_constraints + "' in configuration file.", ErrorCodes::NO_ELEMENTS_IN_CONFIG);
|
||||
|
||||
Poco::Util::AbstractConfiguration::Keys names;
|
||||
config.keys(path_to_constraints, names);
|
||||
|
||||
for (const String & name : names)
|
||||
{
|
||||
String path_to_name = path_to_constraints + "." + name;
|
||||
Poco::Util::AbstractConfiguration::Keys constraint_types;
|
||||
config.keys(path_to_name, constraint_types);
|
||||
for (const String & constraint_type : constraint_types)
|
||||
{
|
||||
String path_to_type = path_to_name + "." + constraint_type;
|
||||
if (constraint_type == "max")
|
||||
setMaxValue(name, config.getString(path_to_type));
|
||||
else
|
||||
throw Exception("Setting " + constraint_type + " value for " + name + " isn't supported", ErrorCodes::NOT_IMPLEMENTED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include <Common/SettingsChanges.h>
|
||||
#include <Core/Settings.h>
|
||||
|
||||
namespace Poco
|
||||
{
|
||||
namespace Util
|
||||
{
|
||||
class AbstractConfiguration;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
@ -8,7 +18,20 @@ struct Settings;
|
||||
|
||||
/** Checks if specified changes of settings are allowed or not.
|
||||
* If the changes are not allowed (i.e. violates some constraints) this class throws an exception.
|
||||
* This class checks that we are not in the read-only mode.
|
||||
* The constraints are set by editing the `users.xml` file.
|
||||
* For examples, the following lines in `users.xml` will set that `max_memory_usage` cannot be greater than 20000000000:
|
||||
* <profiles>
|
||||
* <user_profile>
|
||||
* <max_memory_usage>10000000000</max_memory_usage>
|
||||
* ...
|
||||
* <constraints>
|
||||
* <max_memory_usage>
|
||||
* <max>20000000000</max>
|
||||
* </max_memory_usage>
|
||||
* </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.
|
||||
@ -18,8 +41,31 @@ struct Settings;
|
||||
class SettingsConstraints
|
||||
{
|
||||
public:
|
||||
static void check(const Settings & current_settings, const SettingChange & change);
|
||||
static void check(const Settings & current_settings, const SettingsChanges & changes);
|
||||
SettingsConstraints();
|
||||
SettingsConstraints(const SettingsConstraints & src);
|
||||
SettingsConstraints & operator =(const SettingsConstraints & src);
|
||||
SettingsConstraints(SettingsConstraints && src);
|
||||
SettingsConstraints & operator =(SettingsConstraints && src);
|
||||
~SettingsConstraints();
|
||||
|
||||
void setMaxValue(const String & name, const String & max_value);
|
||||
void setMaxValue(const String & name, const Field & max_value);
|
||||
|
||||
void check(const Settings & current_settings, const SettingChange & change) const;
|
||||
void check(const Settings & current_settings, const SettingsChanges & changes) const;
|
||||
|
||||
/** Set multiple settings from "profile" (in server configuration file (users.xml), profiles contain groups of multiple settings).
|
||||
* The profile can also be set using the `set` functions, like the profile setting.
|
||||
*/
|
||||
void setProfile(const String & profile_name, const Poco::Util::AbstractConfiguration & config);
|
||||
|
||||
/// Loads the constraints from configuration file, at "path" prefix in configuration.
|
||||
void loadFromConfig(const String & path, const Poco::Util::AbstractConfiguration & config);
|
||||
|
||||
private:
|
||||
void checkImpl(const Settings & current_settings, size_t index) const;
|
||||
|
||||
Settings max_settings;
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user