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