Skip to content

Commit 8fd1136

Browse files
Merge branch 'master' of https://github.com/ClickHouse/ClickHouse into add_bech32_enc_dec
2 parents a91b415 + 14b7f64 commit 8fd1136

File tree

15 files changed

+209
-43
lines changed

15 files changed

+209
-43
lines changed

docs/en/operations/settings/constraints-on-settings.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,14 @@ Constraints are defined as follows:
4646
<max>upper_boundary</max>
4747
<changeable_in_readonly/>
4848
</setting_name_5>
49+
<setting_name_6>
50+
<min>lower_boundary</min>
51+
<max>upper_boundary</max>
52+
<disallowed>value1</disallowed>
53+
<disallowed>value2</disallowed>
54+
<disallowed>value3</disallowed>
55+
<changeable_in_readonly/>
56+
</setting_name_6>
4957
</constraints>
5058
</user_name>
5159
</profiles>
@@ -59,12 +67,16 @@ setting remains unchanged.
5967
There are a few types of constraints supported in ClickHouse:
6068
- `min`
6169
- `max`
70+
- `disallowed`
6271
- `readonly` (with alias `const`)
6372
- `changeable_in_readonly`
6473

6574
The `min` and `max` constraints specify upper and lower boundaries for a numeric
6675
setting and can be used in combination with each other.
6776

77+
The `disallowed` constraint can be used to specify specific value(s) which should not
78+
be allowed for a specific setting.
79+
6880
The `readonly` or `const` constraint specifies that the user cannot change the
6981
corresponding setting at all.
7082

src/Access/SettingsConstraints.cpp

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -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

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

466502
bool operator ==(const SettingsConstraints & left, const SettingsConstraints & right)

src/Access/SettingsConstraints.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ class SettingsConstraints
7171
void clear();
7272
bool empty() const { return constraints.empty(); }
7373

74-
void set(const String & full_name, const Field & min_value, const Field & max_value, SettingConstraintWritability writability);
75-
void get(const Settings & current_settings, std::string_view short_name, Field & min_value, Field & max_value, SettingConstraintWritability & writability) const;
76-
void get(const MergeTreeSettings & current_settings, std::string_view short_name, Field & min_value, Field & max_value, SettingConstraintWritability & writability) const;
74+
void set(const String & full_name, const Field & min_value, const Field & max_value, const std::vector<Field> & allowed_values, SettingConstraintWritability writability);
75+
void get(const Settings & current_settings, std::string_view short_name, Field & min_value, Field & max_value, std::vector<Field> & allowed_values, SettingConstraintWritability & writability) const;
76+
void get(const MergeTreeSettings & current_settings, std::string_view short_name, Field & min_value, Field & max_value, std::vector<Field> & allowed_values, SettingConstraintWritability & writability) const;
7777

7878
void merge(const SettingsConstraints & other);
7979

@@ -107,6 +107,7 @@ class SettingsConstraints
107107
SettingConstraintWritability writability = SettingConstraintWritability::WRITABLE;
108108
Field min_value{};
109109
Field max_value{};
110+
std::vector<Field> disallowed_values{};
110111

111112
bool operator ==(const Constraint & other) const;
112113
bool operator !=(const Constraint & other) const { return !(*this == other); }

src/Access/SettingsProfileElement.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,22 @@ void SettingsProfileElement::init(const ASTSettingsProfileElement & ast, const A
6565
min_value = ast.min_value;
6666
max_value = ast.max_value;
6767
writability = ast.writability;
68+
disallowed_values = ast.disallowed_values;
6869

6970
if (value)
7071
value = Settings::castValueUtil(setting_name, *value);
7172
if (min_value)
7273
min_value = Settings::castValueUtil(setting_name, *min_value);
7374
if (max_value)
7475
max_value = Settings::castValueUtil(setting_name, *max_value);
76+
for (auto & allowed_value : disallowed_values)
77+
value = Settings::castValueUtil(setting_name, allowed_value);
7578
}
7679
}
7780

7881
bool SettingsProfileElement::isConstraint() const
7982
{
80-
return this->writability || this->min_value || this->max_value;
83+
return this->writability || this->min_value || this->max_value || !this->disallowed_values.empty();
8184
}
8285

8386
std::shared_ptr<ASTSettingsProfileElement> SettingsProfileElement::toAST() const
@@ -92,6 +95,7 @@ std::shared_ptr<ASTSettingsProfileElement> SettingsProfileElement::toAST() const
9295
ast->value = value;
9396
ast->min_value = min_value;
9497
ast->max_value = max_value;
98+
ast->disallowed_values = disallowed_values;
9599
ast->writability = writability;
96100

97101
return ast;
@@ -113,6 +117,7 @@ std::shared_ptr<ASTSettingsProfileElement> SettingsProfileElement::toASTWithName
113117
ast->value = value;
114118
ast->min_value = min_value;
115119
ast->max_value = max_value;
120+
ast->disallowed_values = disallowed_values;
116121
ast->writability = writability;
117122

118123
return ast;
@@ -277,6 +282,7 @@ SettingsConstraints SettingsProfileElements::toSettingsConstraints(const AccessC
277282
elem.setting_name,
278283
elem.min_value ? *elem.min_value : Field{},
279284
elem.max_value ? *elem.max_value : Field{},
285+
elem.disallowed_values,
280286
elem.writability ? *elem.writability : SettingConstraintWritability::WRITABLE);
281287
return res;
282288
}
@@ -360,6 +366,8 @@ void SettingsProfileElements::normalize()
360366
first_element.min_value = element.min_value;
361367
if (element.max_value)
362368
first_element.max_value = element.max_value;
369+
if (!element.disallowed_values.empty())
370+
first_element.disallowed_values = element.disallowed_values;
363371
if (element.writability)
364372
first_element.writability = element.writability;
365373
element.setting_name.clear();
@@ -484,6 +492,7 @@ void SettingsProfileElements::applyChanges(const AlterSettingsProfileElements &
484492
new_element.value = modify.value;
485493
new_element.min_value = modify.min_value;
486494
new_element.max_value = modify.max_value;
495+
new_element.disallowed_values = modify.disallowed_values;
487496
new_element.writability = modify.writability;
488497
push_back(new_element); /// normalizeProfileElements() will merge this new element with the previous elements.
489498
};

src/Access/SettingsProfileElement.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,10 @@ struct SettingsProfileElement
2828
std::optional<Field> value;
2929
std::optional<Field> min_value;
3030
std::optional<Field> max_value;
31+
std::vector<Field> disallowed_values;
3132
std::optional<SettingConstraintWritability> writability;
3233

33-
auto toTuple() const { return std::tie(parent_profile, setting_name, value, min_value, max_value, writability); }
34+
auto toTuple() const { return std::tie(parent_profile, setting_name, value, min_value, max_value, disallowed_values, writability); }
3435
friend bool operator==(const SettingsProfileElement & lhs, const SettingsProfileElement & rhs) { return lhs.toTuple() == rhs.toTuple(); }
3536
friend bool operator!=(const SettingsProfileElement & lhs, const SettingsProfileElement & rhs) { return !(lhs == rhs); }
3637
friend bool operator <(const SettingsProfileElement & lhs, const SettingsProfileElement & rhs) { return lhs.toTuple() < rhs.toTuple(); }
@@ -46,7 +47,7 @@ struct SettingsProfileElement
4647
std::shared_ptr<ASTSettingsProfileElement> toAST() const;
4748
std::shared_ptr<ASTSettingsProfileElement> toASTWithNames(const AccessControl & access_control) const;
4849

49-
bool empty() const { return !parent_profile && (setting_name.empty() || (!value && !min_value && !max_value && !writability)); }
50+
bool empty() const { return !parent_profile && (setting_name.empty() || (!value && !min_value && !max_value && disallowed_values.empty() && !writability)); }
5051

5152
bool isConstraint() const;
5253

src/Access/UsersConfigAccessStorage.cpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,19 @@ namespace
699699
profile_element.min_value = settingStringToValueUtil(setting_name, config.getString(path_to_name + "." + constraint_type));
700700
else if (constraint_type == "max")
701701
profile_element.max_value = settingStringToValueUtil(setting_name, config.getString(path_to_name + "." + constraint_type));
702+
/// When the xml config is parsed, the first constraint_type is parsed as `disallowed` and the subsequent ones are parsed as
703+
/// disallowed[1], disallowed[2] and so on. So, both `disallowed` and `disallowed[` should be considered as valid constraint types.
704+
/// Example:
705+
/// <constraints>
706+
/// <max_execution_time>
707+
/// <max>50</max>
708+
/// <disallowed>3</disallowed>
709+
/// <disallowed>4</disallowed>
710+
/// <disallowed>5</disallowed>
711+
/// </max_execution_time>
712+
/// </constraints>
713+
else if (constraint_type == "disallowed" || constraint_type.starts_with("disallowed["))
714+
profile_element.disallowed_values.push_back(settingStringToValueUtil(setting_name, config.getString(path_to_name + "." + constraint_type)));
702715
else if (constraint_type == "readonly" || constraint_type == "const")
703716
{
704717
writability_count++;

src/Core/Settings.cpp

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7168,22 +7168,28 @@ void Settings::dumpToSystemSettingsColumns(MutableColumnsAndConstraints & params
71687168

71697169
Field min;
71707170
Field max;
7171+
std::vector<Field> disallowed_values;
71717172
SettingConstraintWritability writability = SettingConstraintWritability::WRITABLE;
7172-
params.constraints.get(*this, setting_name, min, max, writability);
7173+
params.constraints.get(*this, setting_name, min, max, disallowed_values, writability);
71737174

71747175
/// These two columns can accept strings only.
71757176
if (!min.isNull())
71767177
min = Settings::valueToStringUtil(setting_name, min);
71777178
if (!max.isNull())
71787179
max = Settings::valueToStringUtil(setting_name, max);
71797180

7181+
Array disallowed_array;
7182+
for (const auto & value : disallowed_values)
7183+
disallowed_array.emplace_back(Settings::valueToStringUtil(setting_name, value));
7184+
71807185
res_columns[4]->insert(min);
71817186
res_columns[5]->insert(max);
7182-
res_columns[6]->insert(writability == SettingConstraintWritability::CONST);
7183-
res_columns[7]->insert(setting.getTypeName());
7184-
res_columns[8]->insert(setting.getDefaultValueString());
7185-
res_columns[10]->insert(setting.getTier() == SettingsTierType::OBSOLETE);
7186-
res_columns[11]->insert(setting.getTier());
7187+
res_columns[6]->insert(disallowed_array);
7188+
res_columns[7]->insert(writability == SettingConstraintWritability::CONST);
7189+
res_columns[8]->insert(setting.getTypeName());
7190+
res_columns[9]->insert(setting.getDefaultValueString());
7191+
res_columns[11]->insert(setting.getTier() == SettingsTierType::OBSOLETE);
7192+
res_columns[12]->insert(setting.getTier());
71877193
};
71887194

71897195
const auto & settings_to_aliases = SettingsImpl::Traits::settingsToAliases();
@@ -7193,15 +7199,15 @@ void Settings::dumpToSystemSettingsColumns(MutableColumnsAndConstraints & params
71937199
res_columns[0]->insert(setting_name);
71947200

71957201
fill_data_for_setting(setting_name, setting);
7196-
res_columns[9]->insert("");
7202+
res_columns[10]->insert("");
71977203

71987204
if (auto it = settings_to_aliases.find(setting_name); it != settings_to_aliases.end())
71997205
{
72007206
for (const auto alias : it->second)
72017207
{
72027208
res_columns[0]->insert(alias);
72037209
fill_data_for_setting(alias, setting);
7204-
res_columns[9]->insert(setting_name);
7210+
res_columns[10]->insert(setting_name);
72057211
}
72067212
}
72077213
}

src/Parsers/Access/ASTSettingsProfileElement.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ class ASTSettingsProfileElement : public IAST
1717
std::optional<Field> value;
1818
std::optional<Field> min_value;
1919
std::optional<Field> max_value;
20+
std::vector<Field> disallowed_values;
2021
std::optional<SettingConstraintWritability> writability;
2122
bool id_mode = false; /// If true then `parent_profile` keeps UUID, not a name.
2223
bool use_inherit_keyword = false; /// If true then this element is a part of ASTCreateSettingsProfileQuery.

src/Storages/MergeTree/MergeTreeSettings.cpp

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2281,29 +2281,35 @@ void MergeTreeSettings::dumpToSystemMergeTreeSettingsColumns(MutableColumnsAndCo
22812281
for (const auto & setting : impl->all())
22822282
{
22832283
const auto & setting_name = setting.getName();
2284-
res_columns[0]->insert(setting_name);
2285-
res_columns[1]->insert(setting.getValueString());
2286-
res_columns[2]->insert(setting.getDefaultValueString());
2287-
res_columns[3]->insert(setting.isValueChanged());
2288-
res_columns[4]->insert(setting.getDescription());
2289-
2284+
size_t col = 0;
2285+
res_columns[col++]->insert(setting_name);
2286+
res_columns[col++]->insert(setting.getValueString());
2287+
res_columns[col++]->insert(setting.getDefaultValueString());
2288+
res_columns[col++]->insert(setting.isValueChanged());
2289+
res_columns[col++]->insert(setting.getDescription());
22902290
Field min;
22912291
Field max;
2292+
std::vector<Field> disallowed_values;
22922293
SettingConstraintWritability writability = SettingConstraintWritability::WRITABLE;
2293-
constraints.get(*this, setting_name, min, max, writability);
2294+
constraints.get(*this, setting_name, min, max, disallowed_values, writability);
22942295

22952296
/// These two columns can accept strings only.
22962297
if (!min.isNull())
22972298
min = MergeTreeSettings::valueToStringUtil(setting_name, min);
22982299
if (!max.isNull())
22992300
max = MergeTreeSettings::valueToStringUtil(setting_name, max);
23002301

2301-
res_columns[5]->insert(min);
2302-
res_columns[6]->insert(max);
2303-
res_columns[7]->insert(writability == SettingConstraintWritability::CONST);
2304-
res_columns[8]->insert(setting.getTypeName());
2305-
res_columns[9]->insert(setting.getTier() == SettingsTierType::OBSOLETE);
2306-
res_columns[10]->insert(setting.getTier());
2302+
Array disallowed_array;
2303+
for (const auto & value : disallowed_values)
2304+
disallowed_array.emplace_back(MergeTreeSettings::valueToStringUtil(setting_name, value));
2305+
2306+
res_columns[col++]->insert(min);
2307+
res_columns[col++]->insert(max);
2308+
res_columns[col++]->insert(disallowed_array);
2309+
res_columns[col++]->insert(writability == SettingConstraintWritability::CONST);
2310+
res_columns[col++]->insert(setting.getTypeName());
2311+
res_columns[col++]->insert(setting.getTier() == SettingsTierType::OBSOLETE);
2312+
res_columns[col++]->insert(setting.getTier());
23072313
}
23082314
}
23092315

0 commit comments

Comments
 (0)