fix stateless tests

This commit is contained in:
Sergei Trifonov 2022-09-06 20:28:10 +02:00
parent 6217559175
commit 77ee4c04aa
14 changed files with 91 additions and 117 deletions

View File

@ -667,7 +667,7 @@
previous profile. You can change this behaviour by setting this to true.
If it's set to true then if settings profile has a constraint for a specific setting, then this constraint completely cancels all
actions of previous constraint (defined in other profiles) for the same specific setting, including fields that are not set by new constraint.
Also it enables 'changeable_in_readonly' constraint type -->
It also enables 'changeable_in_readonly' constraint type -->
<settings_constraints_replace_previous>false</settings_constraints_replace_previous>
</access_control_improvements>

View File

@ -35,33 +35,24 @@ void SettingsConstraints::clear()
constraints.clear();
}
void SettingsConstraints::setMinValue(const String & setting_name, const Field & min_value)
void SettingsConstraints::set(const String & setting_name, const Field & min_value, const Field & max_value, SettingConstraintType type)
{
constraints[setting_name].min_value = Settings::castValueUtil(setting_name, min_value);
if (min_value.isNull() && max_value.isNull() && type == SettingConstraintType::NONE)
return; // Do not even create entry to avoid issues during profile inheritance
auto & constraint = constraints[setting_name];
if (!min_value.isNull())
constraint.min_value = Settings::castValueUtil(setting_name, min_value);
if (!max_value.isNull())
constraint.max_value = Settings::castValueUtil(setting_name, max_value);
constraint.type = type;
}
void SettingsConstraints::setMaxValue(const String & setting_name, const Field & max_value)
{
constraints[setting_name].max_value = Settings::castValueUtil(setting_name, max_value);
}
void SettingsConstraints::setIsConst(const String & setting_name, bool is_const)
{
constraints[setting_name].is_const = is_const;
}
void SettingsConstraints::setChangableInReadonly(const String & setting_name, bool changeable_in_readonly)
{
constraints[setting_name].changeable_in_readonly = changeable_in_readonly;
}
void SettingsConstraints::get(const Settings & current_settings, std::string_view setting_name, Field & min_value, Field & max_value, bool & is_const) const
void SettingsConstraints::get(const Settings & current_settings, std::string_view setting_name, Field & min_value, Field & max_value, SettingConstraintType & type) const
{
auto range = getRange(current_settings, setting_name);
min_value = range.min_value;
max_value = range.max_value;
is_const = range.is_const;
type = range.type;
}
void SettingsConstraints::merge(const SettingsConstraints & other)
@ -80,8 +71,8 @@ void SettingsConstraints::merge(const SettingsConstraints & other)
constraint.min_value = other_constraint.min_value;
if (!other_constraint.max_value.isNull())
constraint.max_value = other_constraint.max_value;
if (other_constraint.is_const)
constraint.is_const = true; // NOTE: In this mode <readonly/> flag cannot be overridden to be false
if (other_constraint.type == SettingConstraintType::CONST)
constraint.type = SettingConstraintType::CONST; // NOTE: In this mode <readonly/> flag cannot be overridden to be false
}
}
}
@ -217,7 +208,7 @@ bool SettingsConstraints::Range::check(SettingChange & change, const Field & new
return false;
}
if (is_const)
if (type == SettingConstraintType::CONST)
{
if (reaction == THROW_ON_VIOLATION)
throw Exception("Setting " + setting_name + " should not be changed", ErrorCodes::SETTING_CONSTRAINT_VIOLATION);
@ -277,7 +268,7 @@ SettingsConstraints::Range SettingsConstraints::getRange(const Settings & curren
auto it = constraints.find(setting_name);
if (current_settings.readonly == 1)
{
if (it == constraints.end() || !it->second.changeable_in_readonly)
if (it == constraints.end() || it->second.type != SettingConstraintType::CHANGEABLE_IN_READONLY)
return Range::forbidden("Cannot modify '" + String(setting_name) + "' setting in readonly mode", ErrorCodes::READONLY);
}
else // For both readonly=0 and readonly=2
@ -290,7 +281,7 @@ SettingsConstraints::Range SettingsConstraints::getRange(const Settings & curren
bool SettingsConstraints::Range::operator==(const Range & other) const
{
return is_const == other.is_const && changeable_in_readonly == other.changeable_in_readonly && min_value == other.min_value && max_value == other.max_value;
return type == other.type && min_value == other.min_value && max_value == other.max_value;
}
bool operator ==(const SettingsConstraints & left, const SettingsConstraints & right)

View File

@ -1,9 +1,9 @@
#pragma once
#include <Access/SettingsProfileElement.h>
#include <Common/SettingsChanges.h>
#include <unordered_map>
namespace Poco::Util
{
class AbstractConfiguration;
@ -65,12 +65,8 @@ public:
void clear();
bool empty() const { return constraints.empty(); }
void setMinValue(const String & setting_name, const Field & min_value);
void setMaxValue(const String & setting_name, const Field & max_value);
void setIsConst(const String & setting_name, bool is_const);
void setChangableInReadonly(const String & setting_name, bool is_const);
void get(const Settings & current_settings, std::string_view setting_name, Field & min_value, Field & max_value, bool & is_const) const;
void set(const String & setting_name, const Field & min_value, const Field & max_value, SettingConstraintType type);
void get(const Settings & current_settings, std::string_view setting_name, Field & min_value, Field & max_value, SettingConstraintType & type) const;
void merge(const SettingsConstraints & other);
@ -94,8 +90,7 @@ private:
struct Range
{
bool is_const = false;
bool changeable_in_readonly = false;
SettingConstraintType type = SettingConstraintType::NONE;
Field min_value;
Field max_value;
@ -115,7 +110,7 @@ private:
static Range forbidden(const String & explain, int code)
{
return Range{.is_const = true, .explain = explain, .code = code};
return Range{.type = SettingConstraintType::CONST, .explain = explain, .code = code};
}
};

View File

@ -60,25 +60,10 @@ void SettingsProfileElement::init(const ASTSettingsProfileElement & ast, const A
value = ast.value;
min_value = ast.min_value;
max_value = ast.max_value;
changeable_in_readonly = false;
switch (ast.type)
{
case ASTSettingsProfileElement::ConstraintType::NONE:
is_const.reset();
break;
case ASTSettingsProfileElement::ConstraintType::WRITABLE:
is_const.emplace(false);
break;
case ASTSettingsProfileElement::ConstraintType::CONST:
is_const.emplace(true);
break;
case ASTSettingsProfileElement::ConstraintType::CHANGEABLE_IN_READONLY:
if (!access_control->doesSettingsConstraintsReplacePrevious())
throw Exception("CHANGEABLE_IN_READONLY for " + setting_name + " is not allowed unless settings_constraints_replace_previous is enabled", ErrorCodes::NOT_IMPLEMENTED);
is_const.reset();
changeable_in_readonly = true;
break;
}
type = ast.type;
if (type == SettingConstraintType::CHANGEABLE_IN_READONLY && !access_control->doesSettingsConstraintsReplacePrevious())
throw Exception("CHANGEABLE_IN_READONLY for " + setting_name + " is not allowed unless settings_constraints_replace_previous is enabled", ErrorCodes::NOT_IMPLEMENTED);
if (!value.isNull())
value = Settings::castValueUtil(setting_name, value);
@ -102,12 +87,7 @@ std::shared_ptr<ASTSettingsProfileElement> SettingsProfileElement::toAST() const
ast->value = value;
ast->min_value = min_value;
ast->max_value = max_value;
if (changeable_in_readonly)
ast->type = ASTSettingsProfileElement::ConstraintType::CHANGEABLE_IN_READONLY;
if (is_const.has_value())
ast->type = *is_const ? ASTSettingsProfileElement::ConstraintType::CONST : ASTSettingsProfileElement::ConstraintType::WRITABLE;
else
ast->type = ASTSettingsProfileElement::ConstraintType::NONE;
ast->type = type;
return ast;
}
@ -128,12 +108,7 @@ std::shared_ptr<ASTSettingsProfileElement> SettingsProfileElement::toASTWithName
ast->value = value;
ast->min_value = min_value;
ast->max_value = max_value;
if (changeable_in_readonly)
ast->type = ASTSettingsProfileElement::ConstraintType::CHANGEABLE_IN_READONLY;
if (is_const.has_value())
ast->type = *is_const ? ASTSettingsProfileElement::ConstraintType::CONST : ASTSettingsProfileElement::ConstraintType::WRITABLE;
else
ast->type = ASTSettingsProfileElement::ConstraintType::NONE;
ast->type = type;
return ast;
}
@ -237,19 +212,8 @@ SettingsConstraints SettingsProfileElements::toSettingsConstraints(const AccessC
{
SettingsConstraints res{access_control};
for (const auto & elem : *this)
{
if (!elem.setting_name.empty() && (elem.setting_name != ALLOW_BACKUP_SETTING_NAME))
{
if (!elem.min_value.isNull())
res.setMinValue(elem.setting_name, elem.min_value);
if (!elem.max_value.isNull())
res.setMaxValue(elem.setting_name, elem.max_value);
if (elem.is_const)
res.setIsConst(elem.setting_name, *elem.is_const);
if (elem.changeable_in_readonly)
res.setChangableInReadonly(elem.setting_name, true);
}
}
if (!elem.setting_name.empty() && elem.setting_name != ALLOW_BACKUP_SETTING_NAME)
res.set(elem.setting_name, elem.min_value, elem.max_value, elem.type);
return res;
}

View File

@ -2,6 +2,7 @@
#include <Core/Field.h>
#include <Core/UUID.h>
#include <Common/SettingConstraintType.h>
#include <optional>
#include <unordered_map>
#include <vector>
@ -25,10 +26,9 @@ struct SettingsProfileElement
Field value;
Field min_value;
Field max_value;
std::optional<bool> is_const;
bool changeable_in_readonly;
SettingConstraintType type = SettingConstraintType::NONE;
auto toTuple() const { return std::tie(parent_profile, setting_name, value, min_value, max_value, is_const, changeable_in_readonly); }
auto toTuple() const { return std::tie(parent_profile, setting_name, value, min_value, max_value, type); }
friend bool operator==(const SettingsProfileElement & lhs, const SettingsProfileElement & rhs) { return lhs.toTuple() == rhs.toTuple(); }
friend bool operator!=(const SettingsProfileElement & lhs, const SettingsProfileElement & rhs) { return !(lhs == rhs); }
friend bool operator <(const SettingsProfileElement & lhs, const SettingsProfileElement & rhs) { return lhs.toTuple() < rhs.toTuple(); }

View File

@ -441,6 +441,7 @@ namespace
String path_to_name = path_to_constraints + "." + setting_name;
config.keys(path_to_name, constraint_types);
size_t type_specifiers_count = 0;
for (const String & constraint_type : constraint_types)
{
if (constraint_type == "min")
@ -448,19 +449,23 @@ namespace
else if (constraint_type == "max")
profile_element.max_value = Settings::stringToValueUtil(setting_name, config.getString(path_to_name + "." + constraint_type));
else if (constraint_type == "readonly" || constraint_type == "const")
profile_element.is_const = true;
{
type_specifiers_count++;
profile_element.type = SettingConstraintType::CONST;
}
else if (constraint_type == "changeable_in_readonly")
{
type_specifiers_count++;
if (access_control.doesSettingsConstraintsReplacePrevious())
profile_element.changeable_in_readonly = true;
profile_element.type = SettingConstraintType::CHANGEABLE_IN_READONLY;
else
throw Exception("Setting changeable_in_readonly for " + setting_name + " is not allowed unless settings_constraints_replace_previous is enabled", ErrorCodes::NOT_IMPLEMENTED);
}
else
throw Exception("Setting " + constraint_type + " value for " + setting_name + " isn't supported", ErrorCodes::NOT_IMPLEMENTED);
}
if (profile_element.is_const && profile_element.changeable_in_readonly)
throw Exception("Both settings changeable_in_readonly and const/readonly cannot be used for " + setting_name, ErrorCodes::NOT_IMPLEMENTED);
if (type_specifiers_count > 1)
throw Exception("Not more than one constraint type specifier (const/readonly/changeable_in_readonly) is allowed for " + setting_name, ErrorCodes::NOT_IMPLEMENTED);
profile_elements.push_back(std::move(profile_element));
}

View File

@ -0,0 +1,26 @@
#pragma once
#include <Core/Field.h>
namespace DB
{
enum class SettingConstraintType
{
// Default. Behave in the same way as WRITABLE, but is not inherited unless `settings_constraints_replace_previous` is set.
NONE,
// Setting can be change within specified range only in `readonly=0` or `readonly=2` mode.
WRITABLE,
// Setting cannot be changed at all.
// Either READONLY or CONST keyword in SQL syntax can be used (<readonly/> or <const/> in config.xml) to enable this.
// NOTE: name `CONST` is choosen to avoid confusion with `readonly` setting.
CONST,
// Setting can be changed within specified range, regardless of `readonly` setting value.
CHANGEABLE_IN_READONLY,
};
}

View File

@ -54,17 +54,17 @@ void ASTSettingsProfileElement::formatImpl(const FormatSettings & settings, Form
switch (type)
{
case ConstraintType::NONE:
case SettingConstraintType::NONE:
break;
case ConstraintType::WRITABLE:
case SettingConstraintType::WRITABLE:
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " WRITABLE"
<< (settings.hilite ? IAST::hilite_none : "");
break;
case ConstraintType::CONST:
case SettingConstraintType::CONST:
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " READONLY"
<< (settings.hilite ? IAST::hilite_none : "");
break;
case ConstraintType::CHANGEABLE_IN_READONLY:
case SettingConstraintType::CHANGEABLE_IN_READONLY:
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " CHANGEABLE_IN_READONLY"
<< (settings.hilite ? IAST::hilite_none : "");
break;

View File

@ -2,7 +2,7 @@
#include <Parsers/IAST.h>
#include <Core/Field.h>
#include <Common/SettingConstraintType.h>
namespace DB
{
@ -17,14 +17,7 @@ public:
Field value;
Field min_value;
Field max_value;
enum class ConstraintType
{
NONE, // default
WRITABLE,
CONST, // equals READONLY
CHANGEABLE_IN_READONLY,
};
ConstraintType type;
SettingConstraintType type = SettingConstraintType::NONE;
bool id_mode = false; /// If true then `parent_profile` keeps UUID, not a name.
bool use_inherit_keyword = false; /// If true then this element is a part of ASTCreateSettingsProfileQuery.

View File

@ -95,23 +95,23 @@ namespace
}
bool parseConstraintTypeKeyword(IParserBase::Pos & pos, Expected & expected, ASTSettingsProfileElement::ConstraintType & type)
bool parseConstraintTypeKeyword(IParserBase::Pos & pos, Expected & expected, SettingConstraintType & type)
{
return IParserBase::wrapParseImpl(pos, [&]
{
if (ParserKeyword{"READONLY"}.ignore(pos, expected) || ParserKeyword{"CONST"}.ignore(pos, expected))
{
type = ASTSettingsProfileElement::ConstraintType::CONST;
type = SettingConstraintType::CONST;
return true;
}
else if (ParserKeyword{"WRITABLE"}.ignore(pos, expected))
{
type = ASTSettingsProfileElement::ConstraintType::WRITABLE;
type = SettingConstraintType::WRITABLE;
return true;
}
else if (ParserKeyword{"CHANGEABLE_IN_READONLY"}.ignore(pos, expected))
{
type = ASTSettingsProfileElement::ConstraintType::CHANGEABLE_IN_READONLY;
type = SettingConstraintType::CHANGEABLE_IN_READONLY;
return true;
}
else
@ -127,7 +127,7 @@ namespace
Field & value,
Field & min_value,
Field & max_value,
ASTSettingsProfileElement::ConstraintType & type)
SettingConstraintType & type)
{
return IParserBase::wrapParseImpl(pos, [&]
{
@ -139,7 +139,7 @@ namespace
Field res_value;
Field res_min_value;
Field res_max_value;
ASTSettingsProfileElement::ConstraintType res_type;
SettingConstraintType res_type;
bool has_value_or_constraint = false;
while (parseValue(pos, expected, res_value) || parseMinMaxValue(pos, expected, res_min_value, res_max_value)
@ -152,7 +152,7 @@ namespace
return false;
if (boost::iequals(res_setting_name, "PROFILE") && res_value.isNull() && res_min_value.isNull() && res_max_value.isNull()
&& res_type == ASTSettingsProfileElement::ConstraintType::CONST)
&& res_type == SettingConstraintType::CONST)
{
/// Ambiguity: "profile readonly" can be treated either as a profile named "readonly" or
/// as a setting named 'profile' with the readonly constraint.
@ -184,7 +184,7 @@ namespace
Field value;
Field min_value;
Field max_value;
ASTSettingsProfileElement::ConstraintType type;
SettingConstraintType type = SettingConstraintType::NONE;
bool ok = parseSettingNameWithValueOrConstraints(pos, expected, setting_name, value, min_value, max_value, type);

View File

@ -6,7 +6,7 @@
namespace DB
{
/** Parses a string like this:
* {variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE]} | PROFILE 'profile_name'
* {variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY]} | PROFILE 'profile_name'
*/
class ParserSettingsProfileElement : public IParserBase
{

View File

@ -40,8 +40,8 @@ void StorageSystemSettings::fillData(MutableColumns & res_columns, ContextPtr co
res_columns[3]->insert(setting.getDescription());
Field min, max;
bool is_const = false;
constraints.get(settings, setting_name, min, max, is_const);
SettingConstraintType type;
constraints.get(settings, setting_name, min, max, type);
/// These two columns can accept strings only.
if (!min.isNull())
@ -51,7 +51,7 @@ void StorageSystemSettings::fillData(MutableColumns & res_columns, ContextPtr co
res_columns[4]->insert(min);
res_columns[5]->insert(max);
res_columns[6]->insert(is_const);
res_columns[6]->insert(type == SettingConstraintType::CONST);
res_columns[7]->insert(setting.getTypeName());
}
}

View File

@ -104,15 +104,15 @@ void StorageSystemSettingsProfileElements::fillData(MutableColumns & res_columns
}
bool inserted_readonly = false;
if (element.is_const && !element.setting_name.empty())
if ((element.type == SettingConstraintType::CONST || element.type == SettingConstraintType::WRITABLE) && !element.setting_name.empty())
{
column_readonly.push_back(*element.is_const);
column_readonly.push_back(element.type == SettingConstraintType::CONST);
column_readonly_null_map.push_back(false);
inserted_readonly = true;
}
bool inserted_changeable_in_readonly = false;
if (element.changeable_in_readonly && !element.setting_name.empty())
if (element.type == SettingConstraintType::CHANGEABLE_IN_READONLY && !element.setting_name.empty())
{
column_changeable_in_readonly.push_back(true);
column_changeable_in_readonly_null_map.push_back(false);

View File

@ -969,7 +969,7 @@ CREATE TABLE system.settings_profile_elements
`min` Nullable(String),
`max` Nullable(String),
`readonly` Nullable(UInt8),
`allowance` Nullable(UInt8),
`changeable_in_readonly` Nullable(UInt8),
`inherit_profile` Nullable(String)
)
ENGINE = SystemSettingsProfileElements