mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-25 17:12:03 +00:00
fix stateless tests
This commit is contained in:
parent
6217559175
commit
77ee4c04aa
@ -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>
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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};
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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(); }
|
||||
|
@ -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));
|
||||
}
|
||||
|
26
src/Common/SettingConstraintType.h
Normal file
26
src/Common/SettingConstraintType.h
Normal 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,
|
||||
};
|
||||
|
||||
}
|
@ -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;
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
{
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user