@@ -77,7 +77,7 @@ void SettingsConstraints::clear()
7777 settings_alias_cache.clear ();
7878}
7979
80- void SettingsConstraints::set (const String & full_name, const Field & min_value, const Field & max_value, SettingConstraintWritability writability)
80+ void SettingsConstraints::set (const String & full_name, const Field & min_value, const Field & max_value, const std::vector<Field> & disallowed_values, SettingConstraintWritability writability)
8181{
8282 std::string resolved_name{resolveSettingName (full_name)};
8383
@@ -90,23 +90,30 @@ void SettingsConstraints::set(const String & full_name, const Field & min_value,
9090 constraint.min_value = settingCastValueUtil (resolved_name, min_value);
9191 if (!max_value.isNull ())
9292 constraint.max_value = settingCastValueUtil (resolved_name, max_value);
93+ if (!disallowed_values.empty ())
94+ {
95+ for (const auto & allowed_value : disallowed_values)
96+ constraint.disallowed_values .emplace_back (settingCastValueUtil (resolved_name, allowed_value));
97+ }
9398 constraint.writability = writability;
9499}
95100
96- void SettingsConstraints::get (const Settings & current_settings, std::string_view short_name, Field & min_value, Field & max_value, SettingConstraintWritability & writability) const
101+ void SettingsConstraints::get (const Settings & current_settings, std::string_view short_name, Field & min_value, Field & max_value, std::vector<Field> & disallowed_values, SettingConstraintWritability & writability) const
97102{
98103 // NOTE: for `Settings` short name is equal to full name
99104 auto checker = getChecker (current_settings, short_name);
100105 min_value = checker.constraint .min_value ;
101106 max_value = checker.constraint .max_value ;
107+ disallowed_values = checker.constraint .disallowed_values ;
102108 writability = checker.constraint .writability ;
103109}
104110
105- void SettingsConstraints::get (const MergeTreeSettings &, std::string_view short_name, Field & min_value, Field & max_value, SettingConstraintWritability & writability) const
111+ void SettingsConstraints::get (const MergeTreeSettings &, std::string_view short_name, Field & min_value, Field & max_value, std::vector<Field> & disallowed_values, SettingConstraintWritability & writability) const
106112{
107113 auto checker = getMergeTreeChecker (short_name);
108114 min_value = checker.constraint .min_value ;
109115 max_value = checker.constraint .max_value ;
116+ disallowed_values = checker.constraint .disallowed_values ;
110117 writability = checker.constraint .writability ;
111118}
112119
@@ -128,6 +135,8 @@ void SettingsConstraints::merge(const SettingsConstraints & other)
128135 constraint.min_value = other_constraint.min_value ;
129136 if (!other_constraint.max_value .isNull ())
130137 constraint.max_value = other_constraint.max_value ;
138+ if (!other_constraint.disallowed_values .empty ())
139+ constraint.disallowed_values = other_constraint.disallowed_values ;
131140 if (other_constraint.writability == SettingConstraintWritability::CONST)
132141 constraint.writability = SettingConstraintWritability::CONST; // NOTE: In this mode <readonly/> flag cannot be overridden to be false
133142 }
@@ -170,6 +179,10 @@ void SettingsConstraints::check(const Settings & current_settings, const Setting
170179 check (current_settings, value, source);
171180 }
172181
182+ // / Don't check disallowed_values here in the profile elements because they make constrains more restrictive
183+ // / and don't allow to bypass constraints from config by creating a user with custom constraints. The check
184+ // / for disallowed values are instead implemented in SettingsConstraints::Checker::check.
185+
173186 SettingConstraintWritability new_value = SettingConstraintWritability::WRITABLE;
174187 SettingConstraintWritability old_value = SettingConstraintWritability::WRITABLE;
175188
@@ -336,6 +349,20 @@ bool SettingsConstraints::Checker::check(SettingChange & change,
336349 }
337350 };
338351
352+ auto equals_or_cannot_compare = [=](const Field & left, const Field & right)
353+ {
354+ if (reaction == THROW_ON_VIOLATION)
355+ return accurateEquals (left, right);
356+ try
357+ {
358+ return accurateEquals (left, right);
359+ }
360+ catch (...)
361+ {
362+ return true ;
363+ }
364+ };
365+
339366
340367 if (constraint.writability == SettingConstraintWritability::CONST)
341368 {
@@ -346,6 +373,7 @@ bool SettingsConstraints::Checker::check(SettingChange & change,
346373
347374 const auto & min_value = constraint.min_value ;
348375 const auto & max_value = constraint.max_value ;
376+ const auto & disallowed_values = constraint.disallowed_values ;
349377
350378 if (!min_value.isNull () && !max_value.isNull () && less_or_cannot_compare (max_value, min_value))
351379 {
@@ -379,6 +407,14 @@ bool SettingsConstraints::Checker::check(SettingChange & change,
379407 change.value = max_value;
380408 }
381409
410+ for (const auto & value : disallowed_values)
411+ {
412+ bool equals = equals_or_cannot_compare (value, new_value);
413+ if (equals)
414+ throw Exception (ErrorCodes::SETTING_CONSTRAINT_VIOLATION, " Setting {} shouldn't be {}" ,
415+ setting_name, applyVisitor (FieldVisitorToString (), value));
416+ }
417+
382418 if (!getSettingSourceRestrictions (setting_name).isSourceAllowed (source))
383419 {
384420 if (reaction == THROW_ON_VIOLATION)
@@ -460,7 +496,7 @@ SettingsConstraints::Checker SettingsConstraints::getMergeTreeChecker(std::strin
460496
461497bool SettingsConstraints::Constraint::operator ==(const Constraint & other) const
462498{
463- return writability == other.writability && min_value == other.min_value && max_value == other.max_value ;
499+ return writability == other.writability && min_value == other.min_value && max_value == other.max_value && disallowed_values == other. disallowed_values ;
464500}
465501
466502bool operator ==(const SettingsConstraints & left, const SettingsConstraints & right)
0 commit comments