diff --git a/docs/en/sql-reference/statements/alter/role.md b/docs/en/sql-reference/statements/alter/role.md index d39e8153a18..1dbbdec9077 100644 --- a/docs/en/sql-reference/statements/alter/role.md +++ b/docs/en/sql-reference/statements/alter/role.md @@ -13,5 +13,10 @@ Syntax: ``` sql ALTER ROLE [IF EXISTS] name1 [RENAME TO new_name |, name2 [,...]] [ON CLUSTER cluster_name] - [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...] + [ADD|MODIFY SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...] + [DROP SETTINGS variable [,...] ] + [ADD PROFILES 'profile_name' [,...] ] + [DROP PROFILES 'profile_name' [,...] ] + [DROP ALL PROFILES] + [DROP ALL SETTINGS] ``` diff --git a/docs/en/sql-reference/statements/alter/settings-profile.md b/docs/en/sql-reference/statements/alter/settings-profile.md index 62d9b9eb77d..23773def8ba 100644 --- a/docs/en/sql-reference/statements/alter/settings-profile.md +++ b/docs/en/sql-reference/statements/alter/settings-profile.md @@ -13,6 +13,11 @@ Syntax: ``` sql ALTER SETTINGS PROFILE [IF EXISTS] name1 [RENAME TO new_name |, name2 [,...]] [ON CLUSTER cluster_name] - [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...] + [ADD|MODIFY SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...] [TO {{role1 | user1 [, role2 | user2 ...]} | NONE | ALL | ALL EXCEPT {role1 | user1 [, role2 | user2 ...]}}] + [DROP SETTINGS variable [,...] ] + [ADD PROFILES 'profile_name' [,...] ] + [DROP PROFILES 'profile_name' [,...] ] + [DROP ALL PROFILES] + [DROP ALL SETTINGS] ``` diff --git a/docs/en/sql-reference/statements/alter/user.md b/docs/en/sql-reference/statements/alter/user.md index 1514b16a657..3b46165a018 100644 --- a/docs/en/sql-reference/statements/alter/user.md +++ b/docs/en/sql-reference/statements/alter/user.md @@ -18,7 +18,12 @@ ALTER USER [IF EXISTS] name1 [RENAME TO new_name |, name2 [,...]] [VALID UNTIL datetime] [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ] [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]] - [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY | WRITABLE] | PROFILE 'profile_name'] [,...] + [ADD|MODIFY SETTINGS variable [=value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE|CONST|CHANGEABLE_IN_READONLY] [,...] ] + [DROP SETTINGS variable [,...] ] + [ADD PROFILES 'profile_name' [,...] ] + [DROP PROFILES 'profile_name' [,...] ] + [DROP ALL PROFILES] + [DROP ALL SETTINGS] ``` To use `ALTER USER` you must have the [ALTER USER](../../../sql-reference/statements/grant.md#access-management) privilege. diff --git a/docs/ru/sql-reference/statements/alter/role.md b/docs/ru/sql-reference/statements/alter/role.md index 33c10dc1b0a..1ae29fa762e 100644 --- a/docs/ru/sql-reference/statements/alter/role.md +++ b/docs/ru/sql-reference/statements/alter/role.md @@ -13,5 +13,10 @@ sidebar_label: ROLE ``` sql ALTER ROLE [IF EXISTS] name1 [RENAME TO new_name |, name2 [,...]] [ON CLUSTER cluster_name] - [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...] + [ADD|MODIFY SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...] + [DROP SETTINGS variable [,...] ] + [ADD PROFILES 'profile_name' [,...] ] + [DROP PROFILES 'profile_name' [,...] ] + [DROP ALL PROFILES] + [DROP ALL SETTINGS] ``` diff --git a/docs/ru/sql-reference/statements/alter/settings-profile.md b/docs/ru/sql-reference/statements/alter/settings-profile.md index 2c3967e807d..5e5f6221844 100644 --- a/docs/ru/sql-reference/statements/alter/settings-profile.md +++ b/docs/ru/sql-reference/statements/alter/settings-profile.md @@ -13,6 +13,11 @@ sidebar_label: SETTINGS PROFILE ``` sql ALTER SETTINGS PROFILE [IF EXISTS] name1 [RENAME TO new_name |, name2 [,...]] [ON CLUSTER cluster_name] - [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...] + [ADD|MODIFY SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...] [TO {{role1 | user1 [, role2 | user2 ...]} | NONE | ALL | ALL EXCEPT {role1 | user1 [, role2 | user2 ...]}}] + [DROP SETTINGS variable [,...] ] + [ADD PROFILES 'profile_name' [,...] ] + [DROP PROFILES 'profile_name' [,...] ] + [DROP ALL PROFILES] + [DROP ALL SETTINGS] ``` diff --git a/docs/ru/sql-reference/statements/alter/user.md b/docs/ru/sql-reference/statements/alter/user.md index a00ea054863..e7a711a8ad1 100644 --- a/docs/ru/sql-reference/statements/alter/user.md +++ b/docs/ru/sql-reference/statements/alter/user.md @@ -19,7 +19,12 @@ ALTER USER [IF EXISTS] name1 [RENAME TO new_name |, name2 [,...]] [VALID UNTIL datetime] [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ] [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]] - [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [READONLY | WRITABLE] | PROFILE 'profile_name'] [,...] + [ADD|MODIFY SETTINGS variable [=value] [MIN [=] min_value] [MAX [=] max_value] [READONLY|WRITABLE|CONST|CHANGEABLE_IN_READONLY] [,...] ] + [DROP SETTINGS variable [,...] ] + [ADD PROFILES 'profile_name' [,...] ] + [DROP PROFILES 'profile_name' [,...] ] + [DROP ALL PROFILES] + [DROP ALL SETTINGS] ``` Для выполнения `ALTER USER` необходима привилегия [ALTER USER](../grant.md#grant-access-management). diff --git a/src/Access/RoleCache.cpp b/src/Access/RoleCache.cpp index cc1f1520b67..888ed13b05a 100644 --- a/src/Access/RoleCache.cpp +++ b/src/Access/RoleCache.cpp @@ -45,7 +45,7 @@ namespace roles_info.names_of_roles[role_id] = role->getName(); roles_info.access.makeUnion(role->access); - roles_info.settings_from_enabled_roles.merge(role->settings); + roles_info.settings_from_enabled_roles.merge(role->settings, /* normalize= */ false); for (const auto & granted_role : role->granted_roles.getGranted()) collectRoles(roles_info, skip_ids, get_role_function, granted_role, false, false); diff --git a/src/Access/SettingsConstraints.cpp b/src/Access/SettingsConstraints.cpp index cb1d433766a..ec7fe5728fe 100644 --- a/src/Access/SettingsConstraints.cpp +++ b/src/Access/SettingsConstraints.cpp @@ -137,6 +137,13 @@ void SettingsConstraints::merge(const SettingsConstraints & other) } +void SettingsConstraints::check(const Settings & current_settings, const AlterSettingsProfileElements & profile_elements, SettingSource source) const +{ + check(current_settings, profile_elements.add_settings, source); + check(current_settings, profile_elements.modify_settings, source); + /// We don't check `drop_settings` here. +} + void SettingsConstraints::check(const Settings & current_settings, const SettingsProfileElements & profile_elements, SettingSource source) const { for (const auto & element : profile_elements) diff --git a/src/Access/SettingsConstraints.h b/src/Access/SettingsConstraints.h index f5e4335252c..c48ceaf1d16 100644 --- a/src/Access/SettingsConstraints.h +++ b/src/Access/SettingsConstraints.h @@ -74,10 +74,11 @@ public: void merge(const SettingsConstraints & other); /// Checks whether `change` violates these constraints and throws an exception if so. - void check(const Settings & current_settings, const SettingsProfileElements & profile_elements, SettingSource source) const; void check(const Settings & current_settings, const SettingChange & change, SettingSource source) const; void check(const Settings & current_settings, const SettingsChanges & changes, SettingSource source) const; void check(const Settings & current_settings, SettingsChanges & changes, SettingSource source) const; + void check(const Settings & current_settings, const SettingsProfileElements & profile_elements, SettingSource source) const; + void check(const Settings & current_settings, const AlterSettingsProfileElements & profile_elements, SettingSource source) const; /// Checks whether `change` violates these constraints and throws an exception if so. (setting short name is expected inside `changes`) void check(const MergeTreeSettings & current_settings, const SettingChange & change) const; diff --git a/src/Access/SettingsProfileElement.cpp b/src/Access/SettingsProfileElement.cpp index 1de61771f93..6497ab3b3f4 100644 --- a/src/Access/SettingsProfileElement.cpp +++ b/src/Access/SettingsProfileElement.cpp @@ -9,6 +9,8 @@ #include #include #include +#include +#include namespace DB @@ -19,6 +21,7 @@ namespace ErrorCodes extern const int NOT_IMPLEMENTED; } + SettingsProfileElement::SettingsProfileElement(const ASTSettingsProfileElement & ast) { init(ast, nullptr); @@ -116,16 +119,20 @@ std::shared_ptr SettingsProfileElement::toASTWithName } -SettingsProfileElements::SettingsProfileElements(const ASTSettingsProfileElements & ast) +SettingsProfileElements::SettingsProfileElements(const ASTSettingsProfileElements & ast, bool normalize_) { for (const auto & ast_element : ast.elements) emplace_back(*ast_element); + if (normalize_) + normalize(); } -SettingsProfileElements::SettingsProfileElements(const ASTSettingsProfileElements & ast, const AccessControl & access_control) +SettingsProfileElements::SettingsProfileElements(const ASTSettingsProfileElements & ast, const AccessControl & access_control, bool normalize_) { for (const auto & ast_element : ast.elements) emplace_back(*ast_element, access_control); + if (normalize_) + normalize(); } @@ -133,7 +140,11 @@ std::shared_ptr SettingsProfileElements::toAST() con { auto res = std::make_shared(); for (const auto & element : *this) - res->elements.push_back(element.toAST()); + { + auto element_ast = element.toAST(); + if (!element_ast->empty()) + res->elements.push_back(element_ast); + } return res; } @@ -141,7 +152,11 @@ std::shared_ptr SettingsProfileElements::toASTWithNa { auto res = std::make_shared(); for (const auto & element : *this) - res->elements.push_back(element.toASTWithNames(access_control)); + { + auto element_ast = element.toASTWithNames(access_control); + if (!element_ast->empty()) + res->elements.push_back(element_ast); + } return res; } @@ -220,9 +235,11 @@ void SettingsProfileElements::removeSettingsKeepProfiles() } -void SettingsProfileElements::merge(const SettingsProfileElements & other) +void SettingsProfileElements::merge(const SettingsProfileElements & other, bool normalize_) { insert(end(), other.begin(), other.end()); + if (normalize_) + normalize(); } @@ -280,6 +297,81 @@ std::vector SettingsProfileElements::toProfileIDs() const return res; } + +void SettingsProfileElements::normalize() +{ + /// Ensure that each element represents either a setting or a profile. + { + SettingsProfileElements new_elements; + for (auto & element : *this) + { + if (element.parent_profile && !element.setting_name.empty()) + { + SettingsProfileElement new_element; + new_element.parent_profile = element.parent_profile; + element.parent_profile.reset(); + new_elements.push_back(std::move(new_element)); + } + } + insert(end(), new_elements.begin(), new_elements.end()); + } + + /// Partitioning: first profiles, then settings. + /// We use std::stable_partition() here because we want to preserve the relative order of profiles and the relative order of settings. + /// (We need that order to be preserved to remove duplicates correctly - see below.) + auto profiles_begin = begin(); + auto profiles_end = std::stable_partition(begin(), end(), [](const SettingsProfileElement & element) { return static_cast(element.parent_profile); }); + auto settings_begin = profiles_end; + auto settings_end = end(); + + /// Remove duplicates among profiles. + /// We keep the last position of any used profile. + /// It's important to keep exactly the last position (and not just any position) because profiles can override settings from each other. + /// For example, [pr_A, pr_B, pr_A, pr_C] is always the same as [pr_B, pr_A, pr_C], but can be not the same as [pr_A, pr_B, pr_C] + /// if pr_A and pr_B give different values to same settings. + { + boost::container::flat_set profile_ids; + profile_ids.reserve(profiles_end - profiles_begin); + auto it = profiles_end; + while (it != profiles_begin) + { + --it; + auto & element = *it; + if (element.parent_profile && !profile_ids.emplace(*element.parent_profile).second) + element.parent_profile.reset(); + } + } + + /// Remove duplicates among settings. + /// We keep the first position of any used setting, and merge settings with the same name to that first element. + { + boost::container::flat_map setting_name_to_first_encounter; + setting_name_to_first_encounter.reserve(settings_end - settings_begin); + for (auto it = settings_begin; it != settings_end; ++it) + { + auto & element = *it; + auto first = setting_name_to_first_encounter.emplace(element.setting_name, it).first->second; + if (it != first) + { + auto & first_element = *first; + if (element.value) + first_element.value = element.value; + if (element.min_value) + first_element.min_value = element.min_value; + if (element.max_value) + first_element.max_value = element.max_value; + if (element.writability) + first_element.writability = element.writability; + element.setting_name.clear(); + } + } + } + + /// Remove empty elements. + std::erase_if(*this, [](const SettingsProfileElement & element) { return element.empty(); }); +} + + bool SettingsProfileElements::isBackupAllowed() const { for (const auto & setting : *this) @@ -296,4 +388,139 @@ bool SettingsProfileElements::isAllowBackupSetting(const String & setting_name) return Settings::resolveName(setting_name) == ALLOW_BACKUP_SETTING_NAME; } + +AlterSettingsProfileElements::AlterSettingsProfileElements(const SettingsProfileElements & ast) +{ + drop_all_settings = true; + drop_all_profiles = true; + add_settings = ast; +} + +AlterSettingsProfileElements::AlterSettingsProfileElements(const ASTSettingsProfileElements & ast) + : AlterSettingsProfileElements(SettingsProfileElements{ast}) +{ +} + +AlterSettingsProfileElements::AlterSettingsProfileElements(const ASTSettingsProfileElements & ast, const AccessControl & access_control) + : AlterSettingsProfileElements(SettingsProfileElements{ast, access_control}) +{ +} + +AlterSettingsProfileElements::AlterSettingsProfileElements(const ASTAlterSettingsProfileElements & ast) +{ + drop_all_settings = ast.drop_all_settings; + drop_all_profiles = ast.drop_all_profiles; + + if (ast.add_settings) + add_settings = SettingsProfileElements{*ast.add_settings, /* normalize= */ false}; /// For "ALTER" the normalization is unnecessary. + + if (ast.modify_settings) + modify_settings = SettingsProfileElements{*ast.modify_settings, /* normalize= */ false}; + + if (ast.drop_settings) + drop_settings = SettingsProfileElements{*ast.drop_settings, /* normalize= */ false}; +} + +AlterSettingsProfileElements::AlterSettingsProfileElements(const ASTAlterSettingsProfileElements & ast, const AccessControl & access_control) +{ + drop_all_settings = ast.drop_all_settings; + drop_all_profiles = ast.drop_all_profiles; + + if (ast.add_settings) + add_settings = SettingsProfileElements{*ast.add_settings, access_control, /* normalize= */ false}; /// For "ALTER" the normalization is unnecessary. + + if (ast.modify_settings) + modify_settings = SettingsProfileElements{*ast.modify_settings, access_control, /* normalize= */ false}; + + if (ast.drop_settings) + drop_settings = SettingsProfileElements{*ast.drop_settings, access_control, /* normalize= */ false}; +} + +void SettingsProfileElements::applyChanges(const AlterSettingsProfileElements & changes) +{ + /// Apply "DROP" changes. + if (changes.drop_all_profiles) + { + for (auto & element : *this) + element.parent_profile.reset(); /// We only make this element empty, the element will be removed in normalizeProfileElements(). + } + + if (changes.drop_all_settings) + { + for (auto & element : *this) + element.setting_name.clear(); /// We only make this element empty, the element will be removed in normalizeProfileElements(). + } + + auto apply_drop_setting = [&](const String & setting_name) + { + for (auto & element : *this) + { + if (element.setting_name == setting_name) + element.setting_name.clear(); + } + }; + + auto apply_drop_profile = [&](const UUID & profile_id) + { + for (auto & element : *this) + { + if (element.parent_profile == profile_id) + element.parent_profile.reset(); + } + }; + + for (const auto & drop : changes.drop_settings) + { + if (drop.parent_profile) + apply_drop_profile(*drop.parent_profile); + if (!drop.setting_name.empty()) + apply_drop_setting(drop.setting_name); + } + + auto apply_modify_setting = [&](const SettingsProfileElement & modify) + { + SettingsProfileElement new_element; + new_element.setting_name = modify.setting_name; + new_element.value = modify.value; + new_element.min_value = modify.min_value; + new_element.max_value = modify.max_value; + new_element.writability = modify.writability; + push_back(new_element); /// normalizeProfileElements() will merge this new element with the previous elements. + }; + + /// Apply "ADD" changes. + auto apply_add_setting = [&](const SettingsProfileElement & add) + { + /// "ADD SETTING" must replace the value and the constraints of a setting, so first we need drop the previous elements for that setting. + apply_drop_setting(add.setting_name); + apply_modify_setting(add); + }; + + auto apply_add_profile = [&](const UUID & profile_id) + { + SettingsProfileElement new_element; + new_element.parent_profile = profile_id; + push_back(new_element); /// We don't care about possible duplicates here, normalizeProfileElements() will remove duplicates. + }; + + for (const auto & add : changes.add_settings) + { + if (add.parent_profile) + apply_add_profile(*add.parent_profile); + if (!add.setting_name.empty()) + apply_add_setting(add); + } + + /// Apply "MODIFY" changes. + for (const auto & modify : changes.modify_settings) + { + chassert(!modify.parent_profile); /// There is no such thing as "MODIFY PROFILE". + if (!modify.setting_name.empty()) + apply_modify_setting(modify); + } + + /// Remove empty elements and duplicates, and sort the result. + normalize(); +} + } diff --git a/src/Access/SettingsProfileElement.h b/src/Access/SettingsProfileElement.h index ea42b823ce1..e418889217b 100644 --- a/src/Access/SettingsProfileElement.h +++ b/src/Access/SettingsProfileElement.h @@ -13,8 +13,10 @@ namespace DB struct Settings; class SettingsChanges; class SettingsConstraints; +struct AlterSettingsProfileElements; class ASTSettingsProfileElement; class ASTSettingsProfileElements; +class ASTAlterSettingsProfileElements; class AccessControl; @@ -44,6 +46,8 @@ struct SettingsProfileElement std::shared_ptr toAST() const; std::shared_ptr toASTWithNames(const AccessControl & access_control) const; + bool empty() const { return !parent_profile && (setting_name.empty() || (!value && !min_value && !max_value && !writability)); } + bool isConstraint() const; private: @@ -57,8 +61,9 @@ public: SettingsProfileElements() = default; /// The constructor from AST requires the AccessControl if `ast.id_mode == false`. - SettingsProfileElements(const ASTSettingsProfileElements & ast); /// NOLINT - SettingsProfileElements(const ASTSettingsProfileElements & ast, const AccessControl & access_control); + SettingsProfileElements(const ASTSettingsProfileElements & ast, bool normalize_ = true); /// NOLINT + SettingsProfileElements(const ASTSettingsProfileElements & ast, const AccessControl & access_control, bool normalize_ = true); + std::shared_ptr toAST() const; std::shared_ptr toASTWithNames(const AccessControl & access_control) const; @@ -70,16 +75,41 @@ public: void removeSettingsKeepProfiles(); - void merge(const SettingsProfileElements & other); - Settings toSettings() const; SettingsChanges toSettingsChanges() const; SettingsConstraints toSettingsConstraints(const AccessControl & access_control) const; std::vector toProfileIDs() const; - bool isBackupAllowed() const; + /// Normalizes this list of profile elements: removes duplicates and empty elements, and also sorts the elements + /// in the following order: first profiles, then settings. + /// The function is called automatically after parsing profile elements from an AST and + /// at the end of an "ALTER PROFILE (USER/ROLE) command". + void normalize(); + /// Appends all the elements of another list of profile elements to this list. + void merge(const SettingsProfileElements & other, bool normalize_ = true); + + /// Applies changes from an "ALTER PROFILE (USER/ROLE)" command. Always normalizes the result. + void applyChanges(const AlterSettingsProfileElements & changes); + + bool isBackupAllowed() const; static bool isAllowBackupSetting(const String & setting_name); }; +struct AlterSettingsProfileElements +{ + bool drop_all_settings = false; + bool drop_all_profiles = false; + SettingsProfileElements add_settings; + SettingsProfileElements modify_settings; + SettingsProfileElements drop_settings; + + AlterSettingsProfileElements() = default; + explicit AlterSettingsProfileElements(const SettingsProfileElements & ast); + explicit AlterSettingsProfileElements(const ASTSettingsProfileElements & ast); + explicit AlterSettingsProfileElements(const ASTAlterSettingsProfileElements & ast); + AlterSettingsProfileElements(const ASTSettingsProfileElements & ast, const AccessControl & access_control); + AlterSettingsProfileElements(const ASTAlterSettingsProfileElements & ast, const AccessControl & access_control); +}; + } diff --git a/src/Access/SettingsProfilesCache.cpp b/src/Access/SettingsProfilesCache.cpp index 275b3aeb6b5..4fb75e75e5a 100644 --- a/src/Access/SettingsProfilesCache.cpp +++ b/src/Access/SettingsProfilesCache.cpp @@ -135,8 +135,8 @@ void SettingsProfilesCache::mergeSettingsAndConstraintsFor(EnabledSettings & ena merged_settings.emplace_back(new_element); } - merged_settings.merge(enabled.params.settings_from_enabled_roles); - merged_settings.merge(enabled.params.settings_from_user); + merged_settings.merge(enabled.params.settings_from_enabled_roles, /* normalize= */ false); + merged_settings.merge(enabled.params.settings_from_user, /* normalize= */ false); auto info = std::make_shared(access_control); diff --git a/src/Interpreters/Access/InterpreterCreateRoleQuery.cpp b/src/Interpreters/Access/InterpreterCreateRoleQuery.cpp index 4c084309e37..6d8c757246c 100644 --- a/src/Interpreters/Access/InterpreterCreateRoleQuery.cpp +++ b/src/Interpreters/Access/InterpreterCreateRoleQuery.cpp @@ -23,7 +23,7 @@ namespace Role & role, const ASTCreateRoleQuery & query, const String & override_name, - const std::optional & override_settings) + const std::optional & override_settings) { if (!override_name.empty()) role.setName(override_name); @@ -33,9 +33,11 @@ namespace role.setName(query.names.front()); if (override_settings) - role.settings = *override_settings; + role.settings.applyChanges(*override_settings); + else if (query.alter_settings) + role.settings.applyChanges(AlterSettingsProfileElements{*query.alter_settings}); else if (query.settings) - role.settings = *query.settings; + role.settings.applyChanges(AlterSettingsProfileElements{*query.settings}); } } @@ -51,14 +53,14 @@ BlockIO InterpreterCreateRoleQuery::execute() else getContext()->checkAccess(AccessType::CREATE_ROLE); - std::optional settings_from_query; - if (query.settings) - { - settings_from_query = SettingsProfileElements{*query.settings, access_control}; + std::optional settings_from_query; + if (query.alter_settings) + settings_from_query = AlterSettingsProfileElements{*query.alter_settings, access_control}; + else if (query.settings) + settings_from_query = AlterSettingsProfileElements{SettingsProfileElements(*query.settings, access_control)}; - if (!query.attach) - getContext()->checkSettingsConstraints(*settings_from_query, SettingSource::ROLE); - } + if (settings_from_query && !query.attach) + getContext()->checkSettingsConstraints(*settings_from_query, SettingSource::ROLE); if (!query.cluster.empty()) return executeDDLQueryOnCluster(updated_query_ptr, getContext()); diff --git a/src/Interpreters/Access/InterpreterCreateSettingsProfileQuery.cpp b/src/Interpreters/Access/InterpreterCreateSettingsProfileQuery.cpp index aadc00d3e6f..5287edb1ee9 100644 --- a/src/Interpreters/Access/InterpreterCreateSettingsProfileQuery.cpp +++ b/src/Interpreters/Access/InterpreterCreateSettingsProfileQuery.cpp @@ -25,7 +25,7 @@ namespace SettingsProfile & profile, const ASTCreateSettingsProfileQuery & query, const String & override_name, - const std::optional & override_settings, + const std::optional & override_settings, const std::optional & override_to_roles) { if (!override_name.empty()) @@ -36,9 +36,11 @@ namespace profile.setName(query.names.front()); if (override_settings) - profile.elements = *override_settings; + profile.elements.applyChanges(*override_settings); + else if (query.alter_settings) + profile.elements.applyChanges(AlterSettingsProfileElements{*query.alter_settings}); else if (query.settings) - profile.elements = *query.settings; + profile.elements.applyChanges(AlterSettingsProfileElements{*query.settings}); if (override_to_roles) profile.to_roles = *override_to_roles; @@ -59,14 +61,14 @@ BlockIO InterpreterCreateSettingsProfileQuery::execute() else getContext()->checkAccess(AccessType::CREATE_SETTINGS_PROFILE); - std::optional settings_from_query; - if (query.settings) - { - settings_from_query = SettingsProfileElements{*query.settings, access_control}; + std::optional settings_from_query; + if (query.alter_settings) + settings_from_query = AlterSettingsProfileElements{*query.alter_settings, access_control}; + else if (query.settings) + settings_from_query = AlterSettingsProfileElements{SettingsProfileElements(*query.settings, access_control)}; - if (!query.attach) - getContext()->checkSettingsConstraints(*settings_from_query, SettingSource::PROFILE); - } + if (settings_from_query && !query.attach) + getContext()->checkSettingsConstraints(*settings_from_query, SettingSource::PROFILE); if (!query.cluster.empty()) { diff --git a/src/Interpreters/Access/InterpreterCreateUserQuery.cpp b/src/Interpreters/Access/InterpreterCreateUserQuery.cpp index f2e65ca4a10..4af6a747c47 100644 --- a/src/Interpreters/Access/InterpreterCreateUserQuery.cpp +++ b/src/Interpreters/Access/InterpreterCreateUserQuery.cpp @@ -43,7 +43,7 @@ namespace const std::vector authentication_methods, const std::shared_ptr & override_name, const std::optional & override_default_roles, - const std::optional & override_settings, + const std::optional & override_settings, const std::optional & override_grantees, const std::optional & global_valid_until, bool reset_authentication_methods, @@ -172,9 +172,11 @@ namespace user.default_database = query.default_database->database_name; if (override_settings) - user.settings = *override_settings; + user.settings.applyChanges(*override_settings); + else if (query.alter_settings) + user.settings.applyChanges(AlterSettingsProfileElements{*query.alter_settings}); else if (query.settings) - user.settings = *query.settings; + user.settings.applyChanges(AlterSettingsProfileElements{*query.settings}); if (override_grantees) user.grantees = *override_grantees; @@ -219,14 +221,14 @@ BlockIO InterpreterCreateUserQuery::execute() } } - std::optional settings_from_query; - if (query.settings) - { - settings_from_query = SettingsProfileElements{*query.settings, access_control}; + std::optional settings_from_query; + if (query.alter_settings) + settings_from_query = AlterSettingsProfileElements{*query.alter_settings, access_control}; + else if (query.settings) + settings_from_query = AlterSettingsProfileElements{*query.settings, access_control}; - if (!query.attach) - getContext()->checkSettingsConstraints(*settings_from_query, SettingSource::USER); - } + if (settings_from_query && !query.attach) + getContext()->checkSettingsConstraints(*settings_from_query, SettingSource::USER); if (!query.cluster.empty()) return executeDDLQueryOnCluster(updated_query_ptr, getContext()); diff --git a/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp b/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp index 8b7cef056ed..c01d94059cb 100644 --- a/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp +++ b/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp @@ -71,10 +71,13 @@ namespace if (!user.settings.empty()) { + std::shared_ptr query_settings; if (attach_mode) - query->settings = user.settings.toAST(); + query_settings = user.settings.toAST(); else - query->settings = user.settings.toASTWithNames(*access_control); + query_settings = user.settings.toASTWithNames(*access_control); + if (!query_settings->empty()) + query->settings = query_settings; } if (user.grantees != RolesOrUsersSet::AllTag{}) @@ -105,10 +108,13 @@ namespace if (!role.settings.empty()) { + std::shared_ptr query_settings; if (attach_mode) - query->settings = role.settings.toAST(); + query_settings = role.settings.toAST(); else - query->settings = role.settings.toASTWithNames(*access_control); + query_settings = role.settings.toASTWithNames(*access_control); + if (!query_settings->empty()) + query->settings = query_settings; } return query; @@ -123,12 +129,16 @@ namespace if (!profile.elements.empty()) { + std::shared_ptr query_settings; if (attach_mode) - query->settings = profile.elements.toAST(); + query_settings = profile.elements.toAST(); else - query->settings = profile.elements.toASTWithNames(*access_control); - if (query->settings) - query->settings->setUseInheritKeyword(true); + query_settings = profile.elements.toASTWithNames(*access_control); + if (!query_settings->empty()) + { + query_settings->setUseInheritKeyword(true); + query->settings = query_settings; + } } if (!profile.to_roles.empty()) diff --git a/src/Interpreters/Context.cpp b/src/Interpreters/Context.cpp index 7f0ad013c1d..bf8d39c8974 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -2521,7 +2521,7 @@ void Context::applySettingsChanges(const SettingsChanges & changes) applySettingsChangesWithLock(changes, lock); } -void Context::checkSettingsConstraintsWithLock(const SettingsProfileElements & profile_elements, SettingSource source) +void Context::checkSettingsConstraintsWithLock(const AlterSettingsProfileElements & profile_elements, SettingSource source) { getSettingsConstraintsAndCurrentProfilesWithLock()->constraints.check(*settings, profile_elements, source); if (getApplicationType() == ApplicationType::LOCAL || getApplicationType() == ApplicationType::SERVER) @@ -2561,7 +2561,7 @@ void Context::checkMergeTreeSettingsConstraintsWithLock(const MergeTreeSettings getSettingsConstraintsAndCurrentProfilesWithLock()->constraints.check(merge_tree_settings, changes); } -void Context::checkSettingsConstraints(const SettingsProfileElements & profile_elements, SettingSource source) +void Context::checkSettingsConstraints(const AlterSettingsProfileElements & profile_elements, SettingSource source) { SharedLockGuard lock(mutex); checkSettingsConstraintsWithLock(profile_elements, source); diff --git a/src/Interpreters/Context.h b/src/Interpreters/Context.h index 327ac0af5fd..8e7c3585ed9 100644 --- a/src/Interpreters/Context.h +++ b/src/Interpreters/Context.h @@ -145,7 +145,7 @@ struct Settings; struct SettingChange; class SettingsChanges; struct SettingsConstraintsAndProfileIDs; -class SettingsProfileElements; +struct AlterSettingsProfileElements; class RemoteHostFilter; class IDisk; using DiskPtr = std::shared_ptr; @@ -865,7 +865,7 @@ public: void applySettingsChanges(const SettingsChanges & changes); /// Checks the constraints. - void checkSettingsConstraints(const SettingsProfileElements & profile_elements, SettingSource source); + void checkSettingsConstraints(const AlterSettingsProfileElements & profile_elements, SettingSource source); void checkSettingsConstraints(const SettingChange & change, SettingSource source); void checkSettingsConstraints(const SettingsChanges & changes, SettingSource source); void checkSettingsConstraints(SettingsChanges & changes, SettingSource source); @@ -1413,7 +1413,7 @@ private: void setCurrentDatabaseWithLock(const String & name, const std::lock_guard & lock); - void checkSettingsConstraintsWithLock(const SettingsProfileElements & profile_elements, SettingSource source); + void checkSettingsConstraintsWithLock(const AlterSettingsProfileElements & profile_elements, SettingSource source); void checkSettingsConstraintsWithLock(const SettingChange & change, SettingSource source); @@ -1422,6 +1422,9 @@ private: void checkSettingsConstraintsWithLock(SettingsChanges & changes, SettingSource source); void clampToSettingsConstraintsWithLock(SettingsChanges & changes, SettingSource source); + void checkSettingsConstraintsWithLock(const AlterSettingsProfileElements & profile_elements, SettingSource source) const; + + void clampToSettingsConstraintsWithLock(SettingsChanges & changes, SettingSource source) const; void checkMergeTreeSettingsConstraintsWithLock(const MergeTreeSettings & merge_tree_settings, const SettingsChanges & changes) const; diff --git a/src/Parsers/Access/ASTCreateRoleQuery.cpp b/src/Parsers/Access/ASTCreateRoleQuery.cpp index eeeb34c97e4..2da4f2bf683 100644 --- a/src/Parsers/Access/ASTCreateRoleQuery.cpp +++ b/src/Parsers/Access/ASTCreateRoleQuery.cpp @@ -31,6 +31,12 @@ namespace format.ostr << (format.hilite ? IAST::hilite_keyword : "") << " SETTINGS " << (format.hilite ? IAST::hilite_none : ""); settings.format(format); } + + void formatAlterSettings(const ASTAlterSettingsProfileElements & alter_settings, const IAST::FormatSettings & format) + { + format.ostr << " "; + alter_settings.format(format); + } } @@ -47,6 +53,9 @@ ASTPtr ASTCreateRoleQuery::clone() const if (settings) res->settings = std::static_pointer_cast(settings->clone()); + if (alter_settings) + res->alter_settings = std::static_pointer_cast(alter_settings->clone()); + return res; } @@ -82,7 +91,9 @@ void ASTCreateRoleQuery::formatImpl(const FormatSettings & format, FormatState & if (!new_name.empty()) formatRenameTo(new_name, format); - if (settings && (!settings->empty() || alter)) + if (alter_settings) + formatAlterSettings(*alter_settings, format); + else if (settings) formatSettings(*settings, format); } diff --git a/src/Parsers/Access/ASTCreateRoleQuery.h b/src/Parsers/Access/ASTCreateRoleQuery.h index 4e465553164..ab6ecfe4427 100644 --- a/src/Parsers/Access/ASTCreateRoleQuery.h +++ b/src/Parsers/Access/ASTCreateRoleQuery.h @@ -7,6 +7,7 @@ namespace DB { class ASTSettingsProfileElements; +class ASTAlterSettingsProfileElements; /** CREATE ROLE [IF NOT EXISTS | OR REPLACE] name @@ -14,7 +15,12 @@ class ASTSettingsProfileElements; * * ALTER ROLE [IF EXISTS] name * [RENAME TO new_name] - * [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...] + * [ADD|MODIFY SETTINGS variable [=value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] [,...] ] + * [DROP SETTINGS variable [,...] ] + * [ADD PROFILES 'profile_name' [,...] ] + * [DROP PROFILES 'profile_name' [,...] ] + * [DROP ALL PROFILES] + * [DROP ALL SETTINGS] */ class ASTCreateRoleQuery : public IAST, public ASTQueryWithOnCluster { @@ -31,6 +37,7 @@ public: String storage_name; std::shared_ptr settings; + std::shared_ptr alter_settings; String getID(char) const override; ASTPtr clone() const override; diff --git a/src/Parsers/Access/ASTCreateSettingsProfileQuery.cpp b/src/Parsers/Access/ASTCreateSettingsProfileQuery.cpp index e1b42bfb33d..06103ef5dce 100644 --- a/src/Parsers/Access/ASTCreateSettingsProfileQuery.cpp +++ b/src/Parsers/Access/ASTCreateSettingsProfileQuery.cpp @@ -33,6 +33,12 @@ namespace settings.format(format); } + void formatAlterSettings(const ASTAlterSettingsProfileElements & alter_settings, const IAST::FormatSettings & format) + { + format.ostr << " "; + alter_settings.format(format); + } + void formatToRoles(const ASTRolesOrUsersSet & roles, const IAST::FormatSettings & settings) { settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " TO " << (settings.hilite ? IAST::hilite_none : ""); @@ -57,6 +63,9 @@ ASTPtr ASTCreateSettingsProfileQuery::clone() const if (settings) res->settings = std::static_pointer_cast(settings->clone()); + if (alter_settings) + res->alter_settings = std::static_pointer_cast(alter_settings->clone()); + return res; } @@ -92,7 +101,9 @@ void ASTCreateSettingsProfileQuery::formatImpl(const FormatSettings & format, Fo if (!new_name.empty()) formatRenameTo(new_name, format); - if (settings && (!settings->empty() || alter)) + if (alter_settings) + formatAlterSettings(*alter_settings, format); + else if (settings) formatSettings(*settings, format); if (to_roles && (!to_roles->empty() || alter)) diff --git a/src/Parsers/Access/ASTCreateSettingsProfileQuery.h b/src/Parsers/Access/ASTCreateSettingsProfileQuery.h index be01aae1e26..c86badb1392 100644 --- a/src/Parsers/Access/ASTCreateSettingsProfileQuery.h +++ b/src/Parsers/Access/ASTCreateSettingsProfileQuery.h @@ -7,6 +7,7 @@ namespace DB { class ASTSettingsProfileElements; +class ASTAlterSettingsProfileElements; class ASTRolesOrUsersSet; @@ -16,7 +17,12 @@ class ASTRolesOrUsersSet; * * ALTER SETTINGS PROFILE [IF EXISTS] name * [RENAME TO new_name] - * [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...] + * [ADD|MODIFY SETTINGS variable [=value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] [,...] ] + * [DROP SETTINGS variable [,...] ] + * [ADD PROFILES 'profile_name' [,...] ] + * [DROP PROFILES 'profile_name' [,...] ] + * [DROP ALL PROFILES] + * [DROP ALL SETTINGS] * [TO {role [,...] | ALL | ALL EXCEPT role [,...]}] */ class ASTCreateSettingsProfileQuery : public IAST, public ASTQueryWithOnCluster @@ -34,6 +40,7 @@ public: String new_name; std::shared_ptr settings; + std::shared_ptr alter_settings; std::shared_ptr to_roles; @@ -44,4 +51,5 @@ public: ASTPtr getRewrittenASTWithoutOnCluster(const WithoutOnClusterASTRewriteParams &) const override { return removeOnCluster(clone()); } QueryKind getQueryKind() const override { return QueryKind::Create; } }; + } diff --git a/src/Parsers/Access/ASTCreateUserQuery.cpp b/src/Parsers/Access/ASTCreateUserQuery.cpp index eb4503acf82..d771a9dc8bf 100644 --- a/src/Parsers/Access/ASTCreateUserQuery.cpp +++ b/src/Parsers/Access/ASTCreateUserQuery.cpp @@ -149,13 +149,17 @@ namespace default_roles.format(settings); } - void formatSettings(const ASTSettingsProfileElements & settings, const IAST::FormatSettings & format) { format.ostr << (format.hilite ? IAST::hilite_keyword : "") << " SETTINGS " << (format.hilite ? IAST::hilite_none : ""); settings.format(format); } + void formatAlterSettings(const ASTAlterSettingsProfileElements & alter_settings, const IAST::FormatSettings & format) + { + format.ostr << " "; + alter_settings.format(format); + } void formatGrantees(const ASTRolesOrUsersSet & grantees, const IAST::FormatSettings & settings) { @@ -198,6 +202,9 @@ ASTPtr ASTCreateUserQuery::clone() const if (settings) res->settings = std::static_pointer_cast(settings->clone()); + if (alter_settings) + res->alter_settings = std::static_pointer_cast(alter_settings->clone()); + for (const auto & authentication_method : authentication_methods) { auto ast_clone = std::static_pointer_cast(authentication_method->clone()); @@ -278,7 +285,9 @@ void ASTCreateUserQuery::formatImpl(const FormatSettings & format, FormatState & if (default_roles) formatDefaultRoles(*default_roles, format); - if (settings && (!settings->empty() || alter)) + if (alter_settings) + formatAlterSettings(*alter_settings, format); + else if (settings) formatSettings(*settings, format); if (grantees) diff --git a/src/Parsers/Access/ASTCreateUserQuery.h b/src/Parsers/Access/ASTCreateUserQuery.h index 8926c7cad44..df94433fb31 100644 --- a/src/Parsers/Access/ASTCreateUserQuery.h +++ b/src/Parsers/Access/ASTCreateUserQuery.h @@ -13,6 +13,7 @@ class ASTUserNamesWithHost; class ASTRolesOrUsersSet; class ASTDatabaseOrNone; class ASTSettingsProfileElements; +class ASTAlterSettingsProfileElements; class ASTAuthenticationData; @@ -30,7 +31,12 @@ class ASTAuthenticationData; * [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE] * [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ] * [DEFAULT DATABASE database | NONE] - * [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...] + * [ADD|MODIFY SETTINGS variable [=value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] [,...] ] + * [DROP SETTINGS variable [,...] ] + * [ADD PROFILES 'profile_name' [,...] ] + * [DROP PROFILES 'profile_name' [,...] ] + * [DROP ALL PROFILES] + * [DROP ALL SETTINGS] * [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]] */ class ASTCreateUserQuery : public IAST, public ASTQueryWithOnCluster @@ -58,6 +64,7 @@ public: std::shared_ptr default_roles; std::shared_ptr settings; + std::shared_ptr alter_settings; std::shared_ptr grantees; std::shared_ptr default_database; diff --git a/src/Parsers/Access/ASTSettingsProfileElement.cpp b/src/Parsers/Access/ASTSettingsProfileElement.cpp index 014b97132de..3e734b9218e 100644 --- a/src/Parsers/Access/ASTSettingsProfileElement.cpp +++ b/src/Parsers/Access/ASTSettingsProfileElement.cpp @@ -3,6 +3,7 @@ #include #include #include +#include namespace DB @@ -21,8 +22,54 @@ namespace settings.ostr << backQuote(str); } } + + void formatSettingsProfileElementsForAlter(std::string_view kind, const ASTSettingsProfileElements & elements, const IAST::FormatSettings & settings) + { + bool need_comma = false; + + size_t num_profiles = elements.getNumberOfProfiles(); + if (num_profiles > 0) + { + settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << kind << " " << (num_profiles == 1 ? "PROFILE" : "PROFILES") + << (settings.hilite ? IAST::hilite_none : "") << " "; + + for (const auto & element : elements.elements) + { + if (!element->parent_profile.empty()) + { + if (need_comma) + settings.ostr << ", "; + formatProfileNameOrID(element->parent_profile, /* is_id= */ false, settings); + need_comma = true; + } + } + } + + size_t num_settings = elements.getNumberOfSettings(); + if (num_settings > 0) + { + if (need_comma) + settings.ostr << ", "; + need_comma = false; + + settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << kind << " " << (num_settings == 1 ? "SETTING" : "SETTINGS") + << (settings.hilite ? IAST::hilite_none : "") << " "; + + for (const auto & element : elements.elements) + { + if (!element->setting_name.empty()) + { + if (need_comma) + settings.ostr << ", "; + element->format(settings); + need_comma = true; + } + } + } + } } + void ASTSettingsProfileElement::formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const { if (!parent_profile.empty()) @@ -82,6 +129,27 @@ bool ASTSettingsProfileElements::empty() const return true; } +size_t ASTSettingsProfileElements::getNumberOfSettings() const +{ + return std::count_if(elements.begin(), elements.end(), [](const auto & element){ return !element->setting_name.empty(); }); +} + +size_t ASTSettingsProfileElements::getNumberOfProfiles() const +{ + return std::count_if(elements.begin(), elements.end(), [](const auto & element){ return !element->parent_profile.empty(); }); +} + + +ASTPtr ASTSettingsProfileElements::clone() const +{ + auto res = std::make_shared(*this); + + for (auto & element : res->elements) + element = std::static_pointer_cast(element->clone()); + + return res; +} + void ASTSettingsProfileElements::formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const { @@ -109,4 +177,108 @@ void ASTSettingsProfileElements::setUseInheritKeyword(bool use_inherit_keyword_) element->use_inherit_keyword = use_inherit_keyword_; } + +void ASTSettingsProfileElements::add(ASTSettingsProfileElements && other) +{ + insertAtEnd(elements, std::move(other.elements)); +} + + +String ASTAlterSettingsProfileElements::getID(char) const +{ + return "AlterSettingsProfileElements"; +} + +ASTPtr ASTAlterSettingsProfileElements::clone() const +{ + auto res = std::make_shared(*this); + + if (add_settings) + res->add_settings = std::static_pointer_cast(add_settings->clone()); + + if (modify_settings) + res->modify_settings = std::static_pointer_cast(modify_settings->clone()); + + if (drop_settings) + res->drop_settings = std::static_pointer_cast(drop_settings->clone()); + + return res; +} + +void ASTAlterSettingsProfileElements::formatImpl(const FormatSettings & format, FormatState &, FormatStateStacked) const +{ + bool need_comma = false; + + if (drop_all_settings) + { + format.ostr << (format.hilite ? IAST::hilite_keyword : "") << "DROP ALL SETTINGS" << (format.hilite ? IAST::hilite_none : ""); + need_comma = true; + } + + if (drop_all_profiles) + { + if (need_comma) + format.ostr << ", "; + format.ostr << (format.hilite ? IAST::hilite_keyword : "") << "DROP ALL PROFILES" << (format.hilite ? IAST::hilite_none : ""); + need_comma = true; + } + + if (drop_settings && !drop_settings->empty()) + { + if (need_comma) + format.ostr << ", "; + formatSettingsProfileElementsForAlter("DROP", *drop_settings, format); + need_comma = true; + } + + if (add_settings && !add_settings->empty()) + { + if (need_comma) + format.ostr << ", "; + formatSettingsProfileElementsForAlter("ADD", *add_settings, format); + need_comma = true; + } + + if (modify_settings && !modify_settings->empty()) + { + if (need_comma) + format.ostr << ", "; + formatSettingsProfileElementsForAlter("MODIFY", *modify_settings, format); + } +} + +void ASTAlterSettingsProfileElements::add(ASTAlterSettingsProfileElements && other) +{ + drop_all_settings |= other.drop_all_settings; + drop_all_profiles |= other.drop_all_profiles; + + if (other.add_settings) + { + if (!add_settings) + add_settings = std::make_shared(); + add_settings->add(std::move(*other.add_settings)); + } + + if (other.add_settings) + { + if (!add_settings) + add_settings = std::make_shared(); + add_settings->add(std::move(*other.add_settings)); + } + + if (other.modify_settings) + { + if (!modify_settings) + modify_settings = std::make_shared(); + modify_settings->add(std::move(*other.modify_settings)); + } + + if (other.drop_settings) + { + if (!drop_settings) + drop_settings = std::make_shared(); + drop_settings->add(std::move(*other.drop_settings)); + } +} + } diff --git a/src/Parsers/Access/ASTSettingsProfileElement.h b/src/Parsers/Access/ASTSettingsProfileElement.h index 13c1926d9b0..32a0d15e173 100644 --- a/src/Parsers/Access/ASTSettingsProfileElement.h +++ b/src/Parsers/Access/ASTSettingsProfileElement.h @@ -39,10 +39,41 @@ public: bool empty() const; + size_t getNumberOfSettings() const; + size_t getNumberOfProfiles() const; + String getID(char) const override { return "SettingsProfileElements"; } - ASTPtr clone() const override { return std::make_shared(*this); } + ASTPtr clone() const override; void formatImpl(const FormatSettings & settings, FormatState &, FormatStateStacked) const override; void setUseInheritKeyword(bool use_inherit_keyword_); + + void add(ASTSettingsProfileElements && other); }; + +/* Represents a clause used to alter settings or profiles assigned to a user or a role or another profile. + * [ADD|MODIFY SETTINGS variable [=value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] [,...] ] + * [DROP SETTINGS variable [,...] ] + * [ADD PROFILES 'profile_name' [,...] ] + * [DROP PROFILES 'profile_name' [,...] ] + * [DROP ALL PROFILES] + * [DROP ALL SETTINGS] +*/ +class ASTAlterSettingsProfileElements : public IAST +{ +public: + std::shared_ptr add_settings; + std::shared_ptr modify_settings; + std::shared_ptr drop_settings; + + bool drop_all_settings = false; + bool drop_all_profiles = false; + + String getID(char) const override; + ASTPtr clone() const override; + void formatImpl(const FormatSettings & format, FormatState &, FormatStateStacked) const override; + + void add(ASTAlterSettingsProfileElements && other); +}; + } diff --git a/src/Parsers/Access/ParserCreateRoleQuery.cpp b/src/Parsers/Access/ParserCreateRoleQuery.cpp index cdcb399e408..032a7d0ce96 100644 --- a/src/Parsers/Access/ParserCreateRoleQuery.cpp +++ b/src/Parsers/Access/ParserCreateRoleQuery.cpp @@ -25,20 +25,31 @@ namespace }); } - bool parseSettings(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::vector> & settings) + bool parseSettings(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::shared_ptr & settings) { return IParserBase::wrapParseImpl(pos, [&] { - if (!ParserKeyword{Keyword::SETTINGS}.ignore(pos, expected)) - return false; - - ASTPtr new_settings_ast; + ASTPtr ast; ParserSettingsProfileElements elements_p; elements_p.useIDMode(id_mode); - if (!elements_p.parse(pos, new_settings_ast, expected)) + if (!elements_p.parse(pos, ast, expected)) return false; - settings = std::move(new_settings_ast->as().elements); + settings = typeid_cast>(ast); + return true; + }); + } + + bool parseAlterSettings(IParserBase::Pos & pos, Expected & expected, std::shared_ptr & alter_settings) + { + return IParserBase::wrapParseImpl(pos, [&] + { + ASTPtr ast; + ParserAlterSettingsProfileElements elements_p; + if (!elements_p.parse(pos, ast, expected)) + return false; + + alter_settings = typeid_cast>(ast); return true; }); } @@ -91,6 +102,7 @@ bool ParserCreateRoleQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec String new_name; std::shared_ptr settings; + std::shared_ptr alter_settings; String cluster; String storage_name; @@ -99,14 +111,27 @@ bool ParserCreateRoleQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec if (alter && new_name.empty() && (names.size() == 1) && parseRenameTo(pos, expected, new_name)) continue; - std::vector> new_settings; - if (parseSettings(pos, expected, attach_mode, new_settings)) + if (alter) { - if (!settings) - settings = std::make_shared(); - - insertAtEnd(settings->elements, std::move(new_settings)); - continue; + std::shared_ptr new_alter_settings; + if (parseAlterSettings(pos, expected, new_alter_settings)) + { + if (!alter_settings) + alter_settings = std::make_shared(); + alter_settings->add(std::move(*new_alter_settings)); + continue; + } + } + else + { + std::shared_ptr new_settings; + if (parseSettings(pos, expected, attach_mode, new_settings)) + { + if (!settings) + settings = std::make_shared(); + settings->add(std::move(*new_settings)); + continue; + } } if (cluster.empty() && parseOnCluster(pos, expected, cluster)) @@ -130,6 +155,7 @@ bool ParserCreateRoleQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec query->names = std::move(names); query->new_name = std::move(new_name); query->settings = std::move(settings); + query->alter_settings = std::move(alter_settings); query->storage_name = std::move(storage_name); return true; diff --git a/src/Parsers/Access/ParserCreateRoleQuery.h b/src/Parsers/Access/ParserCreateRoleQuery.h index 883ea89854a..3ab18b47299 100644 --- a/src/Parsers/Access/ParserCreateRoleQuery.h +++ b/src/Parsers/Access/ParserCreateRoleQuery.h @@ -11,7 +11,12 @@ namespace DB * * ALTER ROLE [IF EXISTS] name * [RENAME TO new_name] - * [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...] + * [ADD|MODIFY SETTINGS variable [=value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] [,...] ] + * [DROP SETTINGS variable [,...] ] + * [ADD PROFILES 'profile_name' [,...] ] + * [DROP PROFILES 'profile_name' [,...] ] + * [DROP ALL PROFILES] + * [DROP ALL SETTINGS] */ class ParserCreateRoleQuery : public IParserBase { diff --git a/src/Parsers/Access/ParserCreateSettingsProfileQuery.cpp b/src/Parsers/Access/ParserCreateSettingsProfileQuery.cpp index 6526aaf4fc5..8c069546602 100644 --- a/src/Parsers/Access/ParserCreateSettingsProfileQuery.cpp +++ b/src/Parsers/Access/ParserCreateSettingsProfileQuery.cpp @@ -27,20 +27,32 @@ namespace }); } - bool parseSettings(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::vector> & settings) + bool parseSettings(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::shared_ptr & settings) { return IParserBase::wrapParseImpl(pos, [&] { - if (!ParserKeyword{Keyword::SETTINGS}.ignore(pos, expected)) - return false; - - ASTPtr new_settings_ast; + ASTPtr ast; ParserSettingsProfileElements elements_p; elements_p.useInheritKeyword(true).useIDMode(id_mode); - if (!elements_p.parse(pos, new_settings_ast, expected)) + if (!elements_p.parse(pos, ast, expected)) return false; - settings = std::move(new_settings_ast->as().elements); + settings = typeid_cast>(ast); + return true; + }); + } + + bool parseAlterSettings(IParserBase::Pos & pos, Expected & expected, std::shared_ptr & alter_settings) + { + return IParserBase::wrapParseImpl(pos, [&] + { + ASTPtr ast; + ParserAlterSettingsProfileElements elements_p; + elements_p.useInheritKeyword(true); + if (!elements_p.parse(pos, ast, expected)) + return false; + + alter_settings = typeid_cast>(ast); return true; }); } @@ -111,6 +123,7 @@ bool ParserCreateSettingsProfileQuery::parseImpl(Pos & pos, ASTPtr & node, Expec String new_name; std::shared_ptr settings; + std::shared_ptr alter_settings; String cluster; String storage_name; @@ -119,14 +132,27 @@ bool ParserCreateSettingsProfileQuery::parseImpl(Pos & pos, ASTPtr & node, Expec if (alter && new_name.empty() && (names.size() == 1) && parseRenameTo(pos, expected, new_name)) continue; - std::vector> new_settings; - if (parseSettings(pos, expected, attach_mode, new_settings)) + if (alter) { - if (!settings) - settings = std::make_shared(); - - insertAtEnd(settings->elements, std::move(new_settings)); - continue; + std::shared_ptr new_alter_settings; + if (parseAlterSettings(pos, expected, new_alter_settings)) + { + if (!alter_settings) + alter_settings = std::make_shared(); + alter_settings->add(std::move(*new_alter_settings)); + continue; + } + } + else + { + std::shared_ptr new_settings; + if (parseSettings(pos, expected, attach_mode, new_settings)) + { + if (!settings) + settings = std::make_shared(); + settings->add(std::move(*new_settings)); + continue; + } } if (cluster.empty() && parseOnCluster(pos, expected, cluster)) @@ -156,6 +182,7 @@ bool ParserCreateSettingsProfileQuery::parseImpl(Pos & pos, ASTPtr & node, Expec query->names = std::move(names); query->new_name = std::move(new_name); query->settings = std::move(settings); + query->alter_settings = std::move(alter_settings); query->to_roles = std::move(to_roles); query->storage_name = std::move(storage_name); diff --git a/src/Parsers/Access/ParserCreateSettingsProfileQuery.h b/src/Parsers/Access/ParserCreateSettingsProfileQuery.h index bee5bdcb2d1..b48d46c1b36 100644 --- a/src/Parsers/Access/ParserCreateSettingsProfileQuery.h +++ b/src/Parsers/Access/ParserCreateSettingsProfileQuery.h @@ -11,7 +11,12 @@ namespace DB * * ALTER SETTINGS PROFILE [IF EXISTS] name * [RENAME TO new_name] - * [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | INHERIT 'profile_name'] [,...] + * [ADD|MODIFY SETTINGS variable [=value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] [,...] ] + * [DROP SETTINGS variable [,...] ] + * [ADD PROFILES 'profile_name' [,...] ] + * [DROP PROFILES 'profile_name' [,...] ] + * [DROP ALL PROFILES] + * [DROP ALL SETTINGS] */ class ParserCreateSettingsProfileQuery : public IParserBase { diff --git a/src/Parsers/Access/ParserCreateUserQuery.cpp b/src/Parsers/Access/ParserCreateUserQuery.cpp index 657302574c2..fd551fecffa 100644 --- a/src/Parsers/Access/ParserCreateUserQuery.cpp +++ b/src/Parsers/Access/ParserCreateUserQuery.cpp @@ -427,20 +427,31 @@ namespace } - bool parseSettings(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::vector> & settings) + bool parseSettings(IParserBase::Pos & pos, Expected & expected, bool id_mode, std::shared_ptr & settings) { return IParserBase::wrapParseImpl(pos, [&] { - if (!ParserKeyword{Keyword::SETTINGS}.ignore(pos, expected)) - return false; - - ASTPtr new_settings_ast; + ASTPtr ast; ParserSettingsProfileElements elements_p; elements_p.useIDMode(id_mode); - if (!elements_p.parse(pos, new_settings_ast, expected)) + if (!elements_p.parse(pos, ast, expected)) return false; - settings = std::move(new_settings_ast->as().elements); + settings = typeid_cast>(ast); + return true; + }); + } + + bool parseAlterSettings(IParserBase::Pos & pos, Expected & expected, std::shared_ptr & alter_settings) + { + return IParserBase::wrapParseImpl(pos, [&] + { + ASTPtr ast; + ParserAlterSettingsProfileElements elements_p; + if (!elements_p.parse(pos, ast, expected)) + return false; + + alter_settings = typeid_cast>(ast); return true; }); } @@ -556,6 +567,7 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec std::vector> auth_data; std::shared_ptr default_roles; std::shared_ptr settings; + std::shared_ptr alter_settings; std::shared_ptr grantees; std::shared_ptr default_database; ASTPtr global_valid_until; @@ -604,14 +616,27 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec continue; } - std::vector> new_settings; - if (parseSettings(pos, expected, attach_mode, new_settings)) + if (alter) { - if (!settings) - settings = std::make_shared(); - - insertAtEnd(settings->elements, std::move(new_settings)); - continue; + std::shared_ptr new_alter_settings; + if (parseAlterSettings(pos, expected, new_alter_settings)) + { + if (!alter_settings) + alter_settings = std::make_shared(); + alter_settings->add(std::move(*new_alter_settings)); + continue; + } + } + else + { + std::shared_ptr new_settings; + if (parseSettings(pos, expected, attach_mode, new_settings)) + { + if (!settings) + settings = std::make_shared(); + settings->add(std::move(*new_settings)); + continue; + } } if (!default_roles && parseDefaultRoles(pos, expected, attach_mode, default_roles)) @@ -691,6 +716,7 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec query->remove_hosts = std::move(remove_hosts); query->default_roles = std::move(default_roles); query->settings = std::move(settings); + query->alter_settings = std::move(alter_settings); query->grantees = std::move(grantees); query->default_database = std::move(default_database); query->global_valid_until = std::move(global_valid_until); diff --git a/src/Parsers/Access/ParserCreateUserQuery.h b/src/Parsers/Access/ParserCreateUserQuery.h index 0cc8c9b6649..4dfff8713d7 100644 --- a/src/Parsers/Access/ParserCreateUserQuery.h +++ b/src/Parsers/Access/ParserCreateUserQuery.h @@ -18,7 +18,12 @@ namespace DB * [NOT IDENTIFIED | IDENTIFIED {[WITH {no_password|plaintext_password|sha256_password|sha256_hash|double_sha1_password|double_sha1_hash}] BY {'password'|'hash'}}|{WITH ldap SERVER 'server_name'}|{WITH kerberos [REALM 'realm']}] * [[ADD|DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE] * [DEFAULT ROLE role [,...] | ALL | ALL EXCEPT role [,...] ] - * [SETTINGS variable [= value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] | PROFILE 'profile_name'] [,...] + * [ADD|MODIFY SETTINGS variable [=value] [MIN [=] min_value] [MAX [=] max_value] [CONST|READONLY|WRITABLE|CHANGEABLE_IN_READONLY] [,...] ] + * [DROP SETTINGS variable [,...] ] + * [ADD PROFILES 'profile_name' [,...] ] + * [DROP PROFILES 'profile_name' [,...] ] + * [DROP ALL PROFILES] + * [DROP ALL SETTINGS] * [GRANTEES {user | role | ANY | NONE} [,...] [EXCEPT {user | role} [,...]]] */ class ParserCreateUserQuery : public IParserBase diff --git a/src/Parsers/Access/ParserSettingsProfileElement.cpp b/src/Parsers/Access/ParserSettingsProfileElement.cpp index 88ee4af3cec..f76b171aad7 100644 --- a/src/Parsers/Access/ParserSettingsProfileElement.cpp +++ b/src/Parsers/Access/ParserSettingsProfileElement.cpp @@ -7,27 +7,13 @@ #include #include #include +#include namespace DB { namespace { - bool parseProfileKeyword(IParserBase::Pos & pos, Expected & expected, bool use_inherit_keyword) - { - if (ParserKeyword{Keyword::PROFILE}.ignore(pos, expected)) - return true; - - if (use_inherit_keyword && ParserKeyword{Keyword::INHERIT}.ignore(pos, expected)) - { - ParserKeyword{Keyword::PROFILE}.ignore(pos, expected); - return true; - } - - return false; - } - - bool parseProfileNameOrID(IParserBase::Pos & pos, Expected & expected, bool id_mode, String & res) { return IParserBase::wrapParseImpl(pos, [&] @@ -119,6 +105,17 @@ namespace } + bool parseSettingName(IParserBase::Pos & pos, Expected & expected, String & res) + { + ASTPtr name_ast; + if (!ParserCompoundIdentifier{}.parse(pos, name_ast, expected)) + return false; + + res = getIdentifierName(name_ast); + return true; + } + + bool parseSettingNameWithValueOrConstraints( IParserBase::Pos & pos, Expected & expected, @@ -130,11 +127,10 @@ namespace { return IParserBase::wrapParseImpl(pos, [&] { - ASTPtr name_ast; - if (!ParserCompoundIdentifier{}.parse(pos, name_ast, expected)) + String res_setting_name; + if (!parseSettingName(pos, expected, res_setting_name)) return false; - String res_setting_name = getIdentifierName(name_ast); std::optional res_value; std::optional res_min_value; std::optional res_max_value; @@ -169,52 +165,112 @@ namespace } - bool parseSettingsProfileElement(IParserBase::Pos & pos, - Expected & expected, - bool id_mode, - bool use_inherit_keyword, - bool previous_element_was_parent_profile, - std::shared_ptr & result) + bool parseSettingsProfileElements(IParserBase::Pos & pos, + Expected & expected, + bool id_mode, + bool use_inherit_keyword, + std::vector> & res) { - return IParserBase::wrapParseImpl(pos, [&] + std::vector> elements; + bool found_none = false; + + bool expect_profiles = false; + bool expect_settings = false; + + auto parse_element = [&] { - String parent_profile; - String setting_name; - std::optional value; - std::optional min_value; - std::optional max_value; - std::optional writability; + if (ParserKeyword{Keyword::SETTINGS}.ignore(pos, expected) || ParserKeyword{Keyword::SETTING}.ignore(pos, expected)) + { + expect_settings = true; + } - bool ok = parseSettingNameWithValueOrConstraints(pos, expected, setting_name, value, min_value, max_value, writability); + bool expect_settings_next = expect_settings; - if (!ok && (parseProfileKeyword(pos, expected, use_inherit_keyword) || previous_element_was_parent_profile)) - ok = parseProfileNameOrID(pos, expected, id_mode, parent_profile); + if (ParserKeyword{Keyword::PROFILES}.ignore(pos, expected) || ParserKeyword{Keyword::PROFILE}.ignore(pos, expected)) + { + expect_profiles = true; + expect_settings = false; + } + else if (use_inherit_keyword && ParserKeyword{Keyword::INHERIT}.ignore(pos, expected)) + { + if (!ParserKeyword{Keyword::PROFILES}.ignore(pos, expected)) + ParserKeyword{Keyword::PROFILE}.ignore(pos, expected); + expect_profiles = true; + expect_settings = false; + } - if (!ok) + if (!expect_profiles && !expect_settings) return false; - result = std::make_shared(); - result->parent_profile = std::move(parent_profile); - result->setting_name = std::move(setting_name); - result->value = std::move(value); - result->min_value = std::move(min_value); - result->max_value = std::move(max_value); - result->writability = writability; - result->id_mode = id_mode; - result->use_inherit_keyword = use_inherit_keyword; - return true; - }); + if (ParserKeyword{Keyword::NONE}.ignore(pos, expected)) + { + found_none = true; + expect_settings = expect_settings_next; + return true; + } + + if (expect_settings) + { + String setting_name; + std::optional value; + std::optional min_value; + std::optional max_value; + std::optional writability; + if (parseSettingNameWithValueOrConstraints(pos, expected, setting_name, value, min_value, max_value, writability)) + { + auto element = std::make_shared(); + element->setting_name = std::move(setting_name); + element->value = std::move(value); + element->min_value = std::move(min_value); + element->max_value = std::move(max_value); + element->writability = std::move(writability); + elements.push_back(element); + expect_profiles = false; + expect_settings = expect_settings_next; + return true; + } + } + + if (expect_profiles) + { + String profile_name; + if (parseProfileNameOrID(pos, expected, id_mode, profile_name)) + { + auto element = std::make_shared(); + element->parent_profile = std::move(profile_name); + element->id_mode = id_mode; + element->use_inherit_keyword = use_inherit_keyword; + elements.push_back(element); + expect_settings = expect_settings_next; + return true; + } + } + + return false; + }; + + if (!ParserList::parseUtil(pos, expected, parse_element, false)) + return false; + + if (elements.empty() && !found_none) + return false; + + res = std::move(elements); + return true; } } bool ParserSettingsProfileElement::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) { - std::shared_ptr res; - if (!parseSettingsProfileElement(pos, expected, id_mode, use_inherit_keyword, false, res)) + std::vector> elements; + if (!parseSettingsProfileElements(pos, expected, id_mode, use_inherit_keyword, elements)) return false; - node = res; + if (elements.size() != 1) + return false; + + node = elements[0]; return true; } @@ -222,28 +278,8 @@ bool ParserSettingsProfileElement::parseImpl(Pos & pos, ASTPtr & node, Expected bool ParserSettingsProfileElements::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) { std::vector> elements; - - if (ParserKeyword{Keyword::NONE}.ignore(pos, expected)) - { - } - else - { - bool previous_element_was_parent_profile = false; - - auto parse_element = [&] - { - std::shared_ptr element; - if (!parseSettingsProfileElement(pos, expected, id_mode, use_inherit_keyword, previous_element_was_parent_profile, element)) - return false; - - elements.push_back(element); - previous_element_was_parent_profile = !element->parent_profile.empty(); - return true; - }; - - if (!ParserList::parseUtil(pos, expected, parse_element, false)) - return false; - } + if (!parseSettingsProfileElements(pos, expected, id_mode, use_inherit_keyword, elements)) + return false; auto result = std::make_shared(); result->elements = std::move(elements); @@ -251,4 +287,148 @@ bool ParserSettingsProfileElements::parseImpl(Pos & pos, ASTPtr & node, Expected return true; } + +bool ParserAlterSettingsProfileElements::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) +{ + std::vector> add_settings; + std::vector> modify_settings; + std::vector> drop_settings; + bool drop_all_settings = false; + bool drop_all_profiles = false; + + std::vector> old_style_settings; + if (parseSettingsProfileElements(pos, expected, /* id_mode= */ false, use_inherit_keyword, old_style_settings)) + { + /// old style: "SETTINGS ..." replaces all the settings ad profiles. + add_settings = std::move(old_style_settings); + drop_all_settings = true; + drop_all_profiles = true; + } + else + { + /// new style: "MODIFY SETTINGS ..., ADD PROFILES ..., DROP PROFILES ..., DROP SETTINGS ..." + std::string_view action; + std::string_view target; + + auto parse_element = [&] + { + if (ParserKeyword{Keyword::ADD}.ignore(pos, expected)) + { + action = "ADD"; + target = ""; + } + else if (ParserKeyword{Keyword::DROP}.ignore(pos, expected)) + { + action = "DROP"; + target = ""; + } + else if (ParserKeyword{Keyword::MODIFY}.ignore(pos, expected)) + { + action = "MODIFY"; + target = ""; + } + + if (!action.empty()) + { + if (ParserKeyword{Keyword::ALL_PROFILES}.ignore(pos, expected)) + target = "ALL PROFILES"; + else if (ParserKeyword{Keyword::ALL_SETTINGS}.ignore(pos, expected)) + target = "ALL SETTINGS"; + else if (ParserKeyword{Keyword::PROFILES}.ignore(pos, expected) || ParserKeyword{Keyword::PROFILE}.ignore(pos, expected)) + target = "PROFILES"; + else if (ParserKeyword{Keyword::SETTINGS}.ignore(pos, expected) || ParserKeyword{Keyword::SETTING}.ignore(pos, expected)) + target = "SETTINGS"; + } + + if (target.empty()) + return false; + + if (target == "PROFILES") + { + auto element = std::make_shared(); + if (!parseProfileNameOrID(pos, expected, /* id_mode= */ false, element->parent_profile)) + return false; + if (action == "ADD") + { + add_settings.push_back(element); + return true; + } + if (action == "DROP") + { + drop_settings.push_back(element); + return true; + } + return false; + } + + if (target == "SETTINGS") + { + auto element = std::make_shared(); + if (action == "ADD" || action == "MODIFY") + { + if (!parseSettingNameWithValueOrConstraints(pos, expected, element->setting_name, element->value, element->min_value, element->max_value, element->writability)) + return false; + if (action == "ADD") + add_settings.push_back(element); + else + modify_settings.push_back(element); + return true; + } + if (action == "DROP") + { + ASTPtr name_ast; + if (!ParserCompoundIdentifier{}.parse(pos, name_ast, expected)) + return false; + element->setting_name = getIdentifierName(name_ast); + drop_settings.push_back(element); + return true; + } + return false; + } + + if (action == "DROP" && target == "ALL PROFILES") + { + drop_all_profiles = true; + return true; + } + + if (action == "DROP" && target == "ALL SETTINGS") + { + drop_all_settings = true; + return true; + } + + return false; + }; + + if (!ParserList::parseUtil(pos, expected, parse_element, false)) + return false; + } + + if (add_settings.empty() && modify_settings.empty() && drop_settings.empty() && !drop_all_settings && !drop_all_profiles) + return false; + + auto result = std::make_shared(); + if (!add_settings.empty()) + { + result->add_settings = std::make_shared(); + result->add_settings->elements = std::move(add_settings); + } + if (!modify_settings.empty()) + { + result->modify_settings = std::make_shared(); + result->modify_settings->elements = std::move(modify_settings); + } + if (!drop_settings.empty()) + { + result->drop_settings = std::make_shared(); + result->drop_settings->elements = std::move(drop_settings); + } + result->drop_all_settings = drop_all_settings; + result->drop_all_profiles = drop_all_profiles; + + node = result; + return true; +} + } diff --git a/src/Parsers/Access/ParserSettingsProfileElement.h b/src/Parsers/Access/ParserSettingsProfileElement.h index 082fc66625f..0761311e833 100644 --- a/src/Parsers/Access/ParserSettingsProfileElement.h +++ b/src/Parsers/Access/ParserSettingsProfileElement.h @@ -39,4 +39,18 @@ private: bool use_inherit_keyword = false; }; + +class ParserAlterSettingsProfileElements : public IParserBase +{ +public: + ParserAlterSettingsProfileElements & useInheritKeyword(bool use_inherit_keyword_ = true) { use_inherit_keyword = use_inherit_keyword_; return *this; } + +protected: + const char * getName() const override { return "AlterSettingsProfileElements"; } + bool parseImpl(Pos & pos, ASTPtr & node, Expected & expected) override; + +private: + bool use_inherit_keyword = false; +}; + } diff --git a/src/Parsers/CommonParsers.h b/src/Parsers/CommonParsers.h index 3927ac96b2f..00c3103e82c 100644 --- a/src/Parsers/CommonParsers.h +++ b/src/Parsers/CommonParsers.h @@ -10,22 +10,25 @@ namespace DB { #define APPLY_FOR_PARSER_KEYWORDS(MR_MACROS) \ + MR_MACROS(ADD, "ADD") \ MR_MACROS(ADD_COLUMN, "ADD COLUMN") \ MR_MACROS(ADD_CONSTRAINT, "ADD CONSTRAINT") \ MR_MACROS(ADD_INDEX, "ADD INDEX") \ MR_MACROS(ADD_PROJECTION, "ADD PROJECTION") \ MR_MACROS(ADD_STATISTICS, "ADD STATISTICS") \ - MR_MACROS(ADD, "ADD") \ MR_MACROS(ADMIN_OPTION_FOR, "ADMIN OPTION FOR") \ MR_MACROS(AFTER, "AFTER") \ MR_MACROS(ALGORITHM, "ALGORITHM") \ MR_MACROS(ALIAS, "ALIAS") \ MR_MACROS(ALL, "ALL") \ + MR_MACROS(ALL_PROFILES, "ALL PROFILES") \ + MR_MACROS(ALL_SETTINGS, "ALL SETTINGS") \ MR_MACROS(ALTER_COLUMN, "ALTER COLUMN") \ MR_MACROS(ALTER_DATABASE, "ALTER DATABASE") \ MR_MACROS(ALTER_LIVE_VIEW, "ALTER LIVE VIEW") \ MR_MACROS(ALTER_POLICY, "ALTER POLICY") \ MR_MACROS(ALTER_PROFILE, "ALTER PROFILE") \ + MR_MACROS(ALTER_PROFILES, "ALTER PROFILES") \ MR_MACROS(ALTER_QUOTA, "ALTER QUOTA") \ MR_MACROS(ALTER_ROLE, "ALTER ROLE") \ MR_MACROS(ALTER_ROW_POLICY, "ALTER ROW POLICY") \ @@ -380,6 +383,7 @@ namespace DB MR_MACROS(PRIMARY_KEY, "PRIMARY KEY") \ MR_MACROS(PRIMARY, "PRIMARY") \ MR_MACROS(PROFILE, "PROFILE") \ + MR_MACROS(PROFILES, "PROFILES") \ MR_MACROS(PROJECTION, "PROJECTION") \ MR_MACROS(PROTOBUF, "Protobuf") \ MR_MACROS(PULL, "PULL") \ @@ -444,7 +448,8 @@ namespace DB MR_MACROS(SET_ROLE_DEFAULT, "SET ROLE DEFAULT") \ MR_MACROS(SET_ROLE, "SET ROLE") \ MR_MACROS(SET_TRANSACTION_SNAPSHOT, "SET TRANSACTION SNAPSHOT") \ - MR_MACROS(SET, "SET") \ + MR_MACROS(SET, "SET") \ + MR_MACROS(SETTING, "SETTING") \ MR_MACROS(SETTINGS, "SETTINGS") \ MR_MACROS(SHOW_ACCESS, "SHOW ACCESS") \ MR_MACROS(SHOW_CREATE, "SHOW CREATE") \ diff --git a/tests/queries/0_stateless/01294_create_settings_profile.reference b/tests/queries/0_stateless/01294_create_settings_profile.reference index 7e1838c8dae..38d5a8d29af 100644 --- a/tests/queries/0_stateless/01294_create_settings_profile.reference +++ b/tests/queries/0_stateless/01294_create_settings_profile.reference @@ -49,7 +49,7 @@ CREATE SETTINGS PROFILE `s4_01294` TO r1_01294 CREATE SETTINGS PROFILE `s1_01294` SETTINGS readonly = 1 CREATE SETTINGS PROFILE `s2_01294` SETTINGS readonly CONST CREATE SETTINGS PROFILE `s3_01294` SETTINGS INHERIT `readonly` -CREATE SETTINGS PROFILE `s4_01294` SETTINGS INHERIT `readonly`, INHERIT `readonly` +CREATE SETTINGS PROFILE `s4_01294` SETTINGS INHERIT `readonly` CREATE SETTINGS PROFILE `s5_01294` SETTINGS INHERIT `readonly`, readonly = 1 CREATE SETTINGS PROFILE `s6_01294` SETTINGS INHERIT `readonly`, readonly CONST -- system.settings_profiles diff --git a/tests/queries/0_stateless/02943_alter_user_modify_profiles.reference b/tests/queries/0_stateless/02943_alter_user_modify_profiles.reference new file mode 100644 index 00000000000..98de0d53b2f --- /dev/null +++ b/tests/queries/0_stateless/02943_alter_user_modify_profiles.reference @@ -0,0 +1,21 @@ +CREATE USER test_user IDENTIFIED WITH no_password +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_a` +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_a`, PROFILE `profile_b` +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_b`, PROFILE `profile_a`, PROFILE `profile_c` +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_a`, PROFILE `profile_d` +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_e` +CREATE USER test_user IDENTIFIED WITH no_password +CREATE ROLE test_role +CREATE ROLE test_role SETTINGS PROFILE `profile_a` +CREATE ROLE test_role SETTINGS PROFILE `profile_a`, PROFILE `profile_b` +CREATE ROLE test_role SETTINGS PROFILE `profile_b`, PROFILE `profile_a`, PROFILE `profile_c` +CREATE ROLE test_role SETTINGS PROFILE `profile_a`, PROFILE `profile_d` +CREATE ROLE test_role SETTINGS PROFILE `profile_e` +CREATE ROLE test_role +CREATE SETTINGS PROFILE `test_profile` +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_a` +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_a`, INHERIT `profile_b` +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_b`, INHERIT `profile_a`, INHERIT `profile_c` +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_a`, INHERIT `profile_d` +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_e` +CREATE SETTINGS PROFILE `test_profile` diff --git a/tests/queries/0_stateless/02943_alter_user_modify_profiles.sh b/tests/queries/0_stateless/02943_alter_user_modify_profiles.sh new file mode 100755 index 00000000000..8a16cacbbf7 --- /dev/null +++ b/tests/queries/0_stateless/02943_alter_user_modify_profiles.sh @@ -0,0 +1,76 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +test_user="test_user_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +test_role="test_role_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +test_profile="test_profile_02932_${CLICKHOUSE_DATABASE}_$RANDOM" + +profile_a="profile_a_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +profile_b="profile_b_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +profile_c="profile_c_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +profile_d="profile_d_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +profile_e="profile_e_02932_${CLICKHOUSE_DATABASE}_$RANDOM" + +$CLICKHOUSE_CLIENT -q "DROP PROFILE IF EXISTS ${profile_a};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE IF EXISTS ${profile_b};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE IF EXISTS ${profile_c};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE IF EXISTS ${profile_d};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE IF EXISTS ${profile_e};" + +$CLICKHOUSE_CLIENT -q "CREATE PROFILE ${profile_a};" +$CLICKHOUSE_CLIENT -q "CREATE PROFILE ${profile_b};" +$CLICKHOUSE_CLIENT -q "CREATE PROFILE ${profile_c};" +$CLICKHOUSE_CLIENT -q "CREATE PROFILE ${profile_d};" +$CLICKHOUSE_CLIENT -q "CREATE PROFILE ${profile_e};" + +function show_create() +{ + local type="$1" + local name="$2" + + $CLICKHOUSE_CLIENT -q "SHOW CREATE $type ${name};" | sed -e "s/${name}/test_${type}/g" -e "s/${profile_a}/profile_a/g" -e "s/${profile_b}/profile_b/g" -e "s/${profile_c}/profile_c/g" -e "s/${profile_d}/profile_d/g" -e "s/${profile_e}/profile_e/g" +} + +function run_test() +{ + local type="$1" + local name="$2" + + $CLICKHOUSE_CLIENT -q "DROP $type IF EXISTS ${name};" + + $CLICKHOUSE_CLIENT -q "CREATE $type ${name};" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD PROFILE ${profile_a};" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD PROFILE ${profile_b};" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD PROFILES ${profile_a}, ${profile_c};" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD PROFILE ${profile_d} DROP PROFILES ${profile_b}, ${profile_c};" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP ALL PROFILES, ADD PROFILE ${profile_e};" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP ALL PROFILES;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "DROP $type ${name};" +} + +run_test user ${test_user} +run_test role ${test_role} +run_test profile ${test_profile} + +$CLICKHOUSE_CLIENT -q "DROP PROFILE ${profile_a};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE ${profile_b};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE ${profile_c};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE ${profile_d};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE ${profile_e};" diff --git a/tests/queries/0_stateless/02943_alter_user_modify_profiles_and_settings.reference b/tests/queries/0_stateless/02943_alter_user_modify_profiles_and_settings.reference new file mode 100644 index 00000000000..6df5a48c865 --- /dev/null +++ b/tests/queries/0_stateless/02943_alter_user_modify_profiles_and_settings.reference @@ -0,0 +1,39 @@ +CREATE USER test_user IDENTIFIED WITH no_password +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_a`, PROFILE `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE USER test_user IDENTIFIED WITH no_password +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_a`, PROFILE `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE USER test_user IDENTIFIED WITH no_password +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_a`, PROFILE `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE USER test_user IDENTIFIED WITH no_password +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_a`, PROFILE `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE USER test_user IDENTIFIED WITH no_password +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_x = 123, custom_t = 7, custom_s = \'str\' +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_a`, custom_x = 123, custom_t = 7, custom_s = \'str\' +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS PROFILE `profile_b`, PROFILE `profile_a`, custom_x = 321, custom_s = \'str\' CONST +CREATE USER test_user IDENTIFIED WITH no_password +CREATE ROLE test_role +CREATE ROLE test_role SETTINGS PROFILE `profile_a`, PROFILE `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE ROLE test_role +CREATE ROLE test_role SETTINGS PROFILE `profile_a`, PROFILE `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE ROLE test_role +CREATE ROLE test_role SETTINGS PROFILE `profile_a`, PROFILE `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE ROLE test_role +CREATE ROLE test_role SETTINGS PROFILE `profile_a`, PROFILE `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE ROLE test_role +CREATE ROLE test_role SETTINGS custom_x = 123, custom_t = 7, custom_s = \'str\' +CREATE ROLE test_role SETTINGS PROFILE `profile_a`, custom_x = 123, custom_t = 7, custom_s = \'str\' +CREATE ROLE test_role SETTINGS PROFILE `profile_b`, PROFILE `profile_a`, custom_x = 321, custom_s = \'str\' CONST +CREATE ROLE test_role +CREATE SETTINGS PROFILE `test_profile` +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_a`, INHERIT `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE SETTINGS PROFILE `test_profile` +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_a`, INHERIT `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE SETTINGS PROFILE `test_profile` +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_a`, INHERIT `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE SETTINGS PROFILE `test_profile` +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_a`, INHERIT `profile_b`, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE SETTINGS PROFILE `test_profile` +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_x = 123, custom_t = 7, custom_s = \'str\' +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_a`, custom_x = 123, custom_t = 7, custom_s = \'str\' +CREATE SETTINGS PROFILE `test_profile` SETTINGS INHERIT `profile_b`, INHERIT `profile_a`, custom_x = 321, custom_s = \'str\' CONST +CREATE SETTINGS PROFILE `test_profile` diff --git a/tests/queries/0_stateless/02943_alter_user_modify_profiles_and_settings.sh b/tests/queries/0_stateless/02943_alter_user_modify_profiles_and_settings.sh new file mode 100755 index 00000000000..2e75d7025be --- /dev/null +++ b/tests/queries/0_stateless/02943_alter_user_modify_profiles_and_settings.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +test_user="test_user_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +test_role="test_role_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +test_profile="test_profile_02932_${CLICKHOUSE_DATABASE}_$RANDOM" + +profile_a="profile_a_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +profile_b="profile_b_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +profile_c="profile_c_02932_${CLICKHOUSE_DATABASE}_$RANDOM" + +$CLICKHOUSE_CLIENT -q "DROP PROFILE IF EXISTS ${profile_a};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE IF EXISTS ${profile_b};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE IF EXISTS ${profile_c};" + +$CLICKHOUSE_CLIENT -q "CREATE PROFILE ${profile_a};" +$CLICKHOUSE_CLIENT -q "CREATE PROFILE ${profile_b};" +$CLICKHOUSE_CLIENT -q "CREATE PROFILE ${profile_c};" + +function show_create() +{ + local type="$1" + local name="$2" + + $CLICKHOUSE_CLIENT -q "SHOW CREATE $type ${name};" | sed -e "s/${name}/test_${type}/g" -e "s/${profile_a}/profile_a/g" -e "s/${profile_b}/profile_b/g" -e "s/${profile_c}/profile_c/g" +} + +function run_test() +{ + local type="$1" + local name="$2" + + $CLICKHOUSE_CLIENT -q "DROP $type IF EXISTS ${name};" + + $CLICKHOUSE_CLIENT -q "CREATE $type ${name};" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTINGS custom_x = 123, custom_y=56.5, custom_w='www', ADD PROFILES ${profile_a}, ${profile_b}" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP ALL SETTINGS, DROP ALL PROFILES" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTINGS custom_x = 123, custom_y=56.5, custom_w='www', PROFILES ${profile_a}, ${profile_b}" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP ALL SETTINGS, ALL PROFILES" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTING custom_x = 123, ADD SETTING custom_y=56.5, ADD SETTING custom_w='www', ADD PROFILE ${profile_a}, ADD PROFILE ${profile_b}" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP ALL PROFILES, DROP ALL SETTINGS" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTING custom_x = 123 ADD SETTING custom_y=56.5 ADD SETTING custom_w='www' ADD PROFILE ${profile_a} ADD PROFILE ${profile_b}" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP ALL SETTINGS DROP ALL PROFILES" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTING custom_x = 123, custom_t = 7, custom_s = 'str'" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD PROFILE ${profile_a}" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD PROFILE ${profile_b}, ${profile_a}, DROP SETTING custom_t, MODIFY SETTING custom_s READONLY, custom_x = 321" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP ALL SETTINGS, ALL PROFILES" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "DROP $type ${name};" +} + +run_test user ${test_user} +run_test role ${test_role} +run_test profile ${test_profile} + +$CLICKHOUSE_CLIENT -q "DROP PROFILE ${profile_a};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE ${profile_b};" +$CLICKHOUSE_CLIENT -q "DROP PROFILE ${profile_c};" diff --git a/tests/queries/0_stateless/02943_alter_user_modify_settings.reference b/tests/queries/0_stateless/02943_alter_user_modify_settings.reference new file mode 100644 index 00000000000..c148bfa591d --- /dev/null +++ b/tests/queries/0_stateless/02943_alter_user_modify_settings.reference @@ -0,0 +1,42 @@ +CREATE USER test_user IDENTIFIED WITH no_password +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_a = 100 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_a = 100, custom_b = 200 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_a = 100, custom_b = 300, custom_c = 400 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_b = 300, custom_c = 400, custom_d = 500 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 0 MAX 1000 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 0 MAX 2000 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 500 MAX 2000 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 700 MIN 500 MAX 2000 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 800 MIN 150 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_c = 400, custom_d = 500 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_c = 400, custom_d = 500, custom_x = 1, custom_y = 2 +CREATE USER test_user IDENTIFIED WITH no_password SETTINGS custom_z = 3 +CREATE USER test_user IDENTIFIED WITH no_password +CREATE ROLE test_role +CREATE ROLE test_role SETTINGS custom_a = 100 +CREATE ROLE test_role SETTINGS custom_a = 100, custom_b = 200 +CREATE ROLE test_role SETTINGS custom_a = 100, custom_b = 300, custom_c = 400 +CREATE ROLE test_role SETTINGS custom_b = 300, custom_c = 400, custom_d = 500 +CREATE ROLE test_role SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 0 MAX 1000 +CREATE ROLE test_role SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 0 MAX 2000 +CREATE ROLE test_role SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 500 MAX 2000 +CREATE ROLE test_role SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 700 MIN 500 MAX 2000 +CREATE ROLE test_role SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 800 MIN 150 +CREATE ROLE test_role SETTINGS custom_c = 400, custom_d = 500 +CREATE ROLE test_role SETTINGS custom_c = 400, custom_d = 500, custom_x = 1, custom_y = 2 +CREATE ROLE test_role SETTINGS custom_z = 3 +CREATE ROLE test_role +CREATE SETTINGS PROFILE `test_profile` +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_a = 100 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_a = 100, custom_b = 200 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_a = 100, custom_b = 300, custom_c = 400 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_b = 300, custom_c = 400, custom_d = 500 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 0 MAX 1000 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 0 MAX 2000 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 500 MAX 2000 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 700 MIN 500 MAX 2000 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 800 MIN 150 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_c = 400, custom_d = 500 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_c = 400, custom_d = 500, custom_x = 1, custom_y = 2 +CREATE SETTINGS PROFILE `test_profile` SETTINGS custom_z = 3 +CREATE SETTINGS PROFILE `test_profile` diff --git a/tests/queries/0_stateless/02943_alter_user_modify_settings.sh b/tests/queries/0_stateless/02943_alter_user_modify_settings.sh new file mode 100755 index 00000000000..d9153e8b4e1 --- /dev/null +++ b/tests/queries/0_stateless/02943_alter_user_modify_settings.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash + +CURDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +# shellcheck source=../shell_config.sh +. "$CURDIR"/../shell_config.sh + +test_user="test_user_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +test_role="test_role_02932_${CLICKHOUSE_DATABASE}_$RANDOM" +test_profile="test_profile_02932_${CLICKHOUSE_DATABASE}_$RANDOM" + +function show_create() +{ + local type="$1" + local name="$2" + + $CLICKHOUSE_CLIENT -q "SHOW CREATE $type ${name};" | sed "s/${name}/test_${type}/g" +} + +function run_test() +{ + local type="$1" + local name="$2" + + $CLICKHOUSE_CLIENT -q "DROP $type IF EXISTS ${name};" + + $CLICKHOUSE_CLIENT -q "CREATE $type ${name};" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTING custom_a=100;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTING custom_b=200;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTINGS custom_b=300, custom_c=400;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTING custom_d=500 DROP SETTING custom_a;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTING custom_e=600 MIN 0 MAX 1000;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} MODIFY SETTING custom_e MAX 2000;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} MODIFY SETTING custom_e MIN 500;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} MODIFY SETTING custom_e=700;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} ADD SETTING custom_e=800 MIN 150;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP SETTINGS custom_b, custom_e;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} MODIFY SETTINGS custom_x=1, custom_y=2;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP ALL SETTINGS, ADD SETTING custom_z=3;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "ALTER $type ${name} DROP ALL SETTINGS;" + show_create $type ${name} + + $CLICKHOUSE_CLIENT -q "DROP $type ${name};" +} + +run_test user ${test_user} +run_test role ${test_role} +run_test profile ${test_profile}