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