| 1 | #include <Access/SettingsConstraints.h> |
| 2 | #include <Core/Settings.h> |
| 3 | #include <Common/FieldVisitors.h> |
| 4 | #include <IO/WriteHelpers.h> |
| 5 | #include <Poco/Util/AbstractConfiguration.h> |
| 6 | |
| 7 | |
| 8 | namespace DB |
| 9 | { |
| 10 | namespace ErrorCodes |
| 11 | { |
| 12 | extern const int READONLY; |
| 13 | extern const int QUERY_IS_PROHIBITED; |
| 14 | extern const int NO_ELEMENTS_IN_CONFIG; |
| 15 | extern const int SETTING_CONSTRAINT_VIOLATION; |
| 16 | } |
| 17 | |
| 18 | SettingsConstraints::SettingsConstraints() = default; |
| 19 | SettingsConstraints::SettingsConstraints(const SettingsConstraints & src) = default; |
| 20 | SettingsConstraints & SettingsConstraints::operator=(const SettingsConstraints & src) = default; |
| 21 | SettingsConstraints::SettingsConstraints(SettingsConstraints && src) = default; |
| 22 | SettingsConstraints & SettingsConstraints::operator=(SettingsConstraints && src) = default; |
| 23 | SettingsConstraints::~SettingsConstraints() = default; |
| 24 | |
| 25 | |
| 26 | void SettingsConstraints::clear() |
| 27 | { |
| 28 | constraints_by_index.clear(); |
| 29 | } |
| 30 | |
| 31 | |
| 32 | void SettingsConstraints::setMinValue(const StringRef & name, const Field & min_value) |
| 33 | { |
| 34 | size_t setting_index = Settings::findIndexStrict(name); |
| 35 | getConstraintRef(setting_index).min_value = Settings::valueToCorrespondingType(setting_index, min_value); |
| 36 | } |
| 37 | |
| 38 | |
| 39 | Field SettingsConstraints::getMinValue(const StringRef & name) const |
| 40 | { |
| 41 | size_t setting_index = Settings::findIndexStrict(name); |
| 42 | const auto * ptr = tryGetConstraint(setting_index); |
| 43 | if (ptr) |
| 44 | return ptr->min_value; |
| 45 | else |
| 46 | return {}; |
| 47 | } |
| 48 | |
| 49 | |
| 50 | void SettingsConstraints::setMaxValue(const StringRef & name, const Field & max_value) |
| 51 | { |
| 52 | size_t setting_index = Settings::findIndexStrict(name); |
| 53 | getConstraintRef(setting_index).max_value = Settings::valueToCorrespondingType(setting_index, max_value); |
| 54 | } |
| 55 | |
| 56 | |
| 57 | Field SettingsConstraints::getMaxValue(const StringRef & name) const |
| 58 | { |
| 59 | size_t setting_index = Settings::findIndexStrict(name); |
| 60 | const auto * ptr = tryGetConstraint(setting_index); |
| 61 | if (ptr) |
| 62 | return ptr->max_value; |
| 63 | else |
| 64 | return {}; |
| 65 | } |
| 66 | |
| 67 | |
| 68 | void SettingsConstraints::setReadOnly(const StringRef & name, bool read_only) |
| 69 | { |
| 70 | size_t setting_index = Settings::findIndexStrict(name); |
| 71 | getConstraintRef(setting_index).read_only = read_only; |
| 72 | } |
| 73 | |
| 74 | |
| 75 | bool SettingsConstraints::isReadOnly(const StringRef & name) const |
| 76 | { |
| 77 | size_t setting_index = Settings::findIndexStrict(name); |
| 78 | const auto * ptr = tryGetConstraint(setting_index); |
| 79 | if (ptr) |
| 80 | return ptr->read_only; |
| 81 | else |
| 82 | return false; |
| 83 | } |
| 84 | |
| 85 | |
| 86 | void SettingsConstraints::set(const StringRef & name, const Field & min_value, const Field & max_value, bool read_only) |
| 87 | { |
| 88 | size_t setting_index = Settings::findIndexStrict(name); |
| 89 | auto & ref = getConstraintRef(setting_index); |
| 90 | ref.min_value = min_value; |
| 91 | ref.max_value = max_value; |
| 92 | ref.read_only = read_only; |
| 93 | } |
| 94 | |
| 95 | |
| 96 | void SettingsConstraints::get(const StringRef & name, Field & min_value, Field & max_value, bool & read_only) const |
| 97 | { |
| 98 | size_t setting_index = Settings::findIndexStrict(name); |
| 99 | const auto * ptr = tryGetConstraint(setting_index); |
| 100 | if (ptr) |
| 101 | { |
| 102 | min_value = ptr->min_value; |
| 103 | max_value = ptr->max_value; |
| 104 | read_only = ptr->read_only; |
| 105 | } |
| 106 | else |
| 107 | { |
| 108 | min_value = Field{}; |
| 109 | max_value = Field{}; |
| 110 | read_only = false; |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | |
| 115 | void SettingsConstraints::merge(const SettingsConstraints & other) |
| 116 | { |
| 117 | for (const auto & [setting_index, other_constraint] : other.constraints_by_index) |
| 118 | { |
| 119 | auto & constraint = constraints_by_index[setting_index]; |
| 120 | if (!other_constraint.min_value.isNull()) |
| 121 | constraint.min_value = other_constraint.min_value; |
| 122 | if (!other_constraint.max_value.isNull()) |
| 123 | constraint.max_value = other_constraint.max_value; |
| 124 | if (other_constraint.read_only) |
| 125 | constraint.read_only = true; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | |
| 130 | SettingsConstraints::Infos SettingsConstraints::getInfo() const |
| 131 | { |
| 132 | Infos result; |
| 133 | result.reserve(constraints_by_index.size()); |
| 134 | for (const auto & [setting_index, constraint] : constraints_by_index) |
| 135 | { |
| 136 | result.emplace_back(); |
| 137 | Info & info = result.back(); |
| 138 | info.name = Settings::getName(setting_index); |
| 139 | info.min = constraint.min_value; |
| 140 | info.max = constraint.max_value; |
| 141 | info.read_only = constraint.read_only; |
| 142 | } |
| 143 | return result; |
| 144 | } |
| 145 | |
| 146 | |
| 147 | void SettingsConstraints::check(const Settings & current_settings, const SettingChange & change) const |
| 148 | { |
| 149 | const String & name = change.name; |
| 150 | size_t setting_index = Settings::findIndex(name); |
| 151 | if (setting_index == Settings::npos) |
| 152 | return; |
| 153 | |
| 154 | Field new_value = Settings::valueToCorrespondingType(setting_index, change.value); |
| 155 | Field current_value = current_settings.get(setting_index); |
| 156 | |
| 157 | /// Setting isn't checked if value wasn't changed. |
| 158 | if (current_value == new_value) |
| 159 | return; |
| 160 | |
| 161 | if (!current_settings.allow_ddl && name == "allow_ddl" ) |
| 162 | throw Exception("Cannot modify 'allow_ddl' setting when DDL queries are prohibited for the user" , ErrorCodes::QUERY_IS_PROHIBITED); |
| 163 | |
| 164 | /** The `readonly` value is understood as follows: |
| 165 | * 0 - everything allowed. |
| 166 | * 1 - only read queries can be made; you can not change the settings. |
| 167 | * 2 - You can only do read queries and you can change the settings, except for the `readonly` setting. |
| 168 | */ |
| 169 | if (current_settings.readonly == 1) |
| 170 | throw Exception("Cannot modify '" + name + "' setting in readonly mode" , ErrorCodes::READONLY); |
| 171 | |
| 172 | if (current_settings.readonly > 1 && name == "readonly" ) |
| 173 | throw Exception("Cannot modify 'readonly' setting in readonly mode" , ErrorCodes::READONLY); |
| 174 | |
| 175 | const Constraint * constraint = tryGetConstraint(setting_index); |
| 176 | if (constraint) |
| 177 | { |
| 178 | if (constraint->read_only) |
| 179 | throw Exception("Setting " + name + " should not be changed" , ErrorCodes::SETTING_CONSTRAINT_VIOLATION); |
| 180 | |
| 181 | if (!constraint->min_value.isNull() && (new_value < constraint->min_value)) |
| 182 | throw Exception( |
| 183 | "Setting " + name + " shouldn't be less than " + applyVisitor(FieldVisitorToString(), constraint->min_value), |
| 184 | ErrorCodes::SETTING_CONSTRAINT_VIOLATION); |
| 185 | |
| 186 | if (!constraint->max_value.isNull() && (new_value > constraint->max_value)) |
| 187 | throw Exception( |
| 188 | "Setting " + name + " shouldn't be greater than " + applyVisitor(FieldVisitorToString(), constraint->max_value), |
| 189 | ErrorCodes::SETTING_CONSTRAINT_VIOLATION); |
| 190 | } |
| 191 | } |
| 192 | |
| 193 | |
| 194 | void SettingsConstraints::check(const Settings & current_settings, const SettingsChanges & changes) const |
| 195 | { |
| 196 | for (const auto & change : changes) |
| 197 | check(current_settings, change); |
| 198 | } |
| 199 | |
| 200 | |
| 201 | SettingsConstraints::Constraint & SettingsConstraints::getConstraintRef(size_t index) |
| 202 | { |
| 203 | auto it = constraints_by_index.find(index); |
| 204 | if (it == constraints_by_index.end()) |
| 205 | it = constraints_by_index.emplace(index, Constraint{}).first; |
| 206 | return it->second; |
| 207 | } |
| 208 | |
| 209 | const SettingsConstraints::Constraint * SettingsConstraints::tryGetConstraint(size_t index) const |
| 210 | { |
| 211 | auto it = constraints_by_index.find(index); |
| 212 | if (it == constraints_by_index.end()) |
| 213 | return nullptr; |
| 214 | return &it->second; |
| 215 | } |
| 216 | |
| 217 | |
| 218 | void SettingsConstraints::setProfile(const String & profile_name, const Poco::Util::AbstractConfiguration & config) |
| 219 | { |
| 220 | String elem = "profiles." + profile_name; |
| 221 | |
| 222 | Poco::Util::AbstractConfiguration::Keys config_keys; |
| 223 | config.keys(elem, config_keys); |
| 224 | |
| 225 | for (const std::string & key : config_keys) |
| 226 | { |
| 227 | if (key == "profile" || 0 == key.compare(0, strlen("profile[" ), "profile[" )) /// Inheritance of profiles from the current one. |
| 228 | setProfile(config.getString(elem + "." + key), config); |
| 229 | else |
| 230 | continue; |
| 231 | } |
| 232 | |
| 233 | String path_to_constraints = "profiles." + profile_name + ".constraints" ; |
| 234 | if (config.has(path_to_constraints)) |
| 235 | loadFromConfig(path_to_constraints, config); |
| 236 | } |
| 237 | |
| 238 | |
| 239 | void SettingsConstraints::loadFromConfig(const String & path_to_constraints, const Poco::Util::AbstractConfiguration & config) |
| 240 | { |
| 241 | if (!config.has(path_to_constraints)) |
| 242 | throw Exception("There is no path '" + path_to_constraints + "' in configuration file." , ErrorCodes::NO_ELEMENTS_IN_CONFIG); |
| 243 | |
| 244 | Poco::Util::AbstractConfiguration::Keys names; |
| 245 | config.keys(path_to_constraints, names); |
| 246 | |
| 247 | for (const String & name : names) |
| 248 | { |
| 249 | String path_to_name = path_to_constraints + "." + name; |
| 250 | Poco::Util::AbstractConfiguration::Keys constraint_types; |
| 251 | config.keys(path_to_name, constraint_types); |
| 252 | for (const String & constraint_type : constraint_types) |
| 253 | { |
| 254 | auto get_constraint_value = [&]{ return config.getString(path_to_name + "." + constraint_type); }; |
| 255 | if (constraint_type == "min" ) |
| 256 | setMinValue(name, get_constraint_value()); |
| 257 | else if (constraint_type == "max" ) |
| 258 | setMaxValue(name, get_constraint_value()); |
| 259 | else if (constraint_type == "readonly" ) |
| 260 | setReadOnly(name, true); |
| 261 | else |
| 262 | throw Exception("Setting " + constraint_type + " value for " + name + " isn't supported" , ErrorCodes::NOT_IMPLEMENTED); |
| 263 | } |
| 264 | } |
| 265 | } |
| 266 | |
| 267 | |
| 268 | bool SettingsConstraints::Constraint::operator==(const Constraint & rhs) const |
| 269 | { |
| 270 | return (read_only == rhs.read_only) && (min_value == rhs.min_value) && (max_value == rhs.max_value); |
| 271 | } |
| 272 | |
| 273 | |
| 274 | bool operator ==(const SettingsConstraints & lhs, const SettingsConstraints & rhs) |
| 275 | { |
| 276 | return lhs.constraints_by_index == rhs.constraints_by_index; |
| 277 | } |
| 278 | } |
| 279 | |