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
8namespace DB
9{
10namespace 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
18SettingsConstraints::SettingsConstraints() = default;
19SettingsConstraints::SettingsConstraints(const SettingsConstraints & src) = default;
20SettingsConstraints & SettingsConstraints::operator=(const SettingsConstraints & src) = default;
21SettingsConstraints::SettingsConstraints(SettingsConstraints && src) = default;
22SettingsConstraints & SettingsConstraints::operator=(SettingsConstraints && src) = default;
23SettingsConstraints::~SettingsConstraints() = default;
24
25
26void SettingsConstraints::clear()
27{
28 constraints_by_index.clear();
29}
30
31
32void 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
39Field 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
50void 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
57Field 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
68void 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
75bool 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
86void 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
96void 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
115void 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
130SettingsConstraints::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
147void 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
194void 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
201SettingsConstraints::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
209const 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
218void 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
239void 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
268bool 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
274bool operator ==(const SettingsConstraints & lhs, const SettingsConstraints & rhs)
275{
276 return lhs.constraints_by_index == rhs.constraints_by_index;
277}
278}
279