diff --git a/docs/en/sql-reference/statements/alter/role.md b/docs/en/sql-reference/statements/alter/role.md index c068d6c4fce..34f98da4722 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 [ON CLUSTER cluster_name1] [RENAME TO new_name1] [, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...] - [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] [READONLY|WRITABLE|CONST|CHANGEABLE_IN_READONLY] [,...] ] + [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 adfe65e3c80..543b8a63064 100644 --- a/docs/en/sql-reference/statements/alter/settings-profile.md +++ b/docs/en/sql-reference/statements/alter/settings-profile.md @@ -13,5 +13,10 @@ Syntax: ``` sql ALTER SETTINGS PROFILE [IF EXISTS] TO name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1] [, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...] - [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] [READONLY|WRITABLE|CONST|CHANGEABLE_IN_READONLY] [,...] ] + [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 fd7da05167c..4525c9c9a65 100644 --- a/docs/en/sql-reference/statements/alter/user.md +++ b/docs/en/sql-reference/statements/alter/user.md @@ -17,7 +17,12 @@ ALTER USER [IF EXISTS] name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1] [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#grant-access-management) privilege. diff --git a/docs/ru/sql-reference/statements/alter/role.md b/docs/ru/sql-reference/statements/alter/role.md index 0c117d29be8..489ec6e88ac 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 [ON CLUSTER cluster_name1] [RENAME TO new_name1] [, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...] - [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] [READONLY|WRITABLE|CONST|CHANGEABLE_IN_READONLY] [,...] ] + [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 90d7a7f6202..fe6b0ba456f 100644 --- a/docs/ru/sql-reference/statements/alter/settings-profile.md +++ b/docs/ru/sql-reference/statements/alter/settings-profile.md @@ -13,5 +13,10 @@ sidebar_label: SETTINGS PROFILE ``` sql ALTER SETTINGS PROFILE [IF EXISTS] TO name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1] [, name2 [ON CLUSTER cluster_name2] [RENAME TO new_name2] ...] - [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] [READONLY|WRITABLE|CONST|CHANGEABLE_IN_READONLY] [,...] ] + [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 1d6ad34ebed..300c9a47021 100644 --- a/docs/ru/sql-reference/statements/alter/user.md +++ b/docs/ru/sql-reference/statements/alter/user.md @@ -17,7 +17,12 @@ ALTER USER [IF EXISTS] name1 [ON CLUSTER cluster_name1] [RENAME TO new_name1] [[ADD | DROP] HOST {LOCAL | NAME 'name' | REGEXP 'name_regexp' | IP 'address' | LIKE 'pattern'} [,...] | ANY | NONE] [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 2d94df2eea5..d2937107afa 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 db805c83e17..dc02941e1bb 100644 --- a/src/Access/SettingsConstraints.cpp +++ b/src/Access/SettingsConstraints.cpp @@ -133,6 +133,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 5bbff86ff61..8e35f0a9a29 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 9358391cb93..ed873193fe7 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; } @@ -176,9 +191,11 @@ void SettingsProfileElements::replaceDependencies(const std::unordered_map 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) @@ -252,4 +344,139 @@ bool SettingsProfileElements::isAllowBackupSetting(const String & setting_name) return Settings::Traits::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 7078f565295..742b7b1e40b 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,24 +61,50 @@ 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; - std::vector findDependencies() const; - void replaceDependencies(const std::unordered_map & old_to_new_ids); - - 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); + + std::vector findDependencies() const; + void replaceDependencies(const std::unordered_map & old_to_new_ids); + + 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 f03e68ba455..591a87e52a4 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 fef1f285c8b..45d7e0d5b23 100644 --- a/src/Interpreters/Access/InterpreterCreateRoleQuery.cpp +++ b/src/Interpreters/Access/InterpreterCreateRoleQuery.cpp @@ -22,7 +22,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); @@ -32,9 +32,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}); } } @@ -50,14 +52,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 3a96c0a96ff..5514d885ddd 100644 --- a/src/Interpreters/Access/InterpreterCreateSettingsProfileQuery.cpp +++ b/src/Interpreters/Access/InterpreterCreateSettingsProfileQuery.cpp @@ -24,7 +24,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()) @@ -35,9 +35,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; @@ -58,14 +60,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 00e21f27d2e..67df60e5f0c 100644 --- a/src/Interpreters/Access/InterpreterCreateUserQuery.cpp +++ b/src/Interpreters/Access/InterpreterCreateUserQuery.cpp @@ -35,7 +35,7 @@ namespace const std::optional auth_data, 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 & valid_until, bool allow_implicit_no_password, @@ -104,9 +104,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; @@ -162,14 +164,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 ec2e60b2ef7..14d455b9150 100644 --- a/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp +++ b/src/Interpreters/Access/InterpreterShowCreateAccessEntityQuery.cpp @@ -75,10 +75,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{}) @@ -109,10 +112,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; @@ -127,12 +133,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 3da0e2508a0..5c404898239 100644 --- a/src/Interpreters/Context.cpp +++ b/src/Interpreters/Context.cpp @@ -1997,11 +1997,6 @@ void Context::applySettingsChanges(const SettingsChanges & changes) applySettingsChangesWithLock(changes, lock); } -void Context::checkSettingsConstraintsWithLock(const SettingsProfileElements & profile_elements, SettingSource source) const -{ - getSettingsConstraintsAndCurrentProfilesWithLock()->constraints.check(settings, profile_elements, source); -} - void Context::checkSettingsConstraintsWithLock(const SettingChange & change, SettingSource source) const { getSettingsConstraintsAndCurrentProfilesWithLock()->constraints.check(settings, change, source); @@ -2017,6 +2012,11 @@ void Context::checkSettingsConstraintsWithLock(SettingsChanges & changes, Settin getSettingsConstraintsAndCurrentProfilesWithLock()->constraints.check(settings, changes, source); } +void Context::checkSettingsConstraintsWithLock(const AlterSettingsProfileElements & profile_elements, SettingSource source) const +{ + getSettingsConstraintsAndCurrentProfilesWithLock()->constraints.check(settings, profile_elements, source); +} + void Context::clampToSettingsConstraintsWithLock(SettingsChanges & changes, SettingSource source) const { getSettingsConstraintsAndCurrentProfilesWithLock()->constraints.clamp(settings, changes, source); @@ -2027,12 +2027,6 @@ void Context::checkMergeTreeSettingsConstraintsWithLock(const MergeTreeSettings getSettingsConstraintsAndCurrentProfilesWithLock()->constraints.check(merge_tree_settings, changes); } -void Context::checkSettingsConstraints(const SettingsProfileElements & profile_elements, SettingSource source) const -{ - SharedLockGuard lock(mutex); - checkSettingsConstraintsWithLock(profile_elements, source); -} - void Context::checkSettingsConstraints(const SettingChange & change, SettingSource source) const { SharedLockGuard lock(mutex); @@ -2051,6 +2045,12 @@ void Context::checkSettingsConstraints(SettingsChanges & changes, SettingSource checkSettingsConstraintsWithLock(changes, source); } +void Context::checkSettingsConstraints(const AlterSettingsProfileElements & profile_elements, SettingSource source) const +{ + SharedLockGuard lock(mutex); + checkSettingsConstraintsWithLock(profile_elements, source); +} + void Context::clampToSettingsConstraints(SettingsChanges & changes, SettingSource source) const { SharedLockGuard lock(mutex); diff --git a/src/Interpreters/Context.h b/src/Interpreters/Context.h index 329dc928eb5..682f8515752 100644 --- a/src/Interpreters/Context.h +++ b/src/Interpreters/Context.h @@ -128,7 +128,7 @@ class AccessControl; class Credentials; class GSSAcceptorContext; struct SettingsConstraintsAndProfileIDs; -class SettingsProfileElements; +struct AlterSettingsProfileElements; class RemoteHostFilter; struct StorageID; class IDisk; @@ -776,10 +776,10 @@ public: void applySettingsChanges(const SettingsChanges & changes); /// Checks the constraints. - void checkSettingsConstraints(const SettingsProfileElements & profile_elements, SettingSource source) const; void checkSettingsConstraints(const SettingChange & change, SettingSource source) const; void checkSettingsConstraints(const SettingsChanges & changes, SettingSource source) const; void checkSettingsConstraints(SettingsChanges & changes, SettingSource source) const; + void checkSettingsConstraints(const AlterSettingsProfileElements & profile_elements, SettingSource source) const; void clampToSettingsConstraints(SettingsChanges & changes, SettingSource source) const; void checkMergeTreeSettingsConstraints(const MergeTreeSettings & merge_tree_settings, const SettingsChanges & changes) const; @@ -1268,14 +1268,14 @@ private: void setCurrentDatabaseWithLock(const String & name, const std::lock_guard & lock); - void checkSettingsConstraintsWithLock(const SettingsProfileElements & profile_elements, SettingSource source) const; - void checkSettingsConstraintsWithLock(const SettingChange & change, SettingSource source) const; void checkSettingsConstraintsWithLock(const SettingsChanges & changes, SettingSource source) const; void checkSettingsConstraintsWithLock(SettingsChanges & changes, SettingSource source) const; + 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 3379486d789..bf563451a14 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 96306fa0dd2..e2fbdc57aa7 100644 --- a/src/Parsers/Access/ASTCreateUserQuery.cpp +++ b/src/Parsers/Access/ASTCreateUserQuery.cpp @@ -133,13 +133,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) { @@ -181,6 +185,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()); + if (auth_data) { res->auth_data = std::static_pointer_cast(auth_data->clone()); @@ -242,7 +249,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 4e14d86c425..517bfaa7c58 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 @@ -55,6 +61,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 7b29b15cb29..a61b8ac123b 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 << backQuoteIfNeed(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,35 @@ bool ASTSettingsProfileElements::empty() const return true; } +size_t ASTSettingsProfileElements::getNumberOfSettings() const +{ + size_t count = 0; + for (const auto & element : elements) + if (!element->setting_name.empty()) + ++count; + return count; +} + +size_t ASTSettingsProfileElements::getNumberOfProfiles() const +{ + size_t count = 0; + for (const auto & element : elements) + if (!element->parent_profile.empty()) + ++count; + return count; +} + + +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 +185,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 99a97f6901d..4f223d2dce6 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{"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 fcb6ebd8806..ed559c200c7 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{"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 6fec40ec84a..6e276102e26 100644 --- a/src/Parsers/Access/ParserCreateUserQuery.cpp +++ b/src/Parsers/Access/ParserCreateUserQuery.cpp @@ -342,20 +342,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{"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; }); } @@ -463,6 +474,7 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec std::shared_ptr 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 valid_until; @@ -495,14 +507,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)) @@ -574,6 +599,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->valid_until = std::move(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 36330b96622..fbd91835298 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{"PROFILE"}.ignore(pos, expected)) - return true; - - if (use_inherit_keyword && ParserKeyword{"INHERIT"}.ignore(pos, expected)) - { - ParserKeyword{"PROFILE"}.ignore(pos, expected); - return true; - } - - return false; - } - - bool parseProfileNameOrID(IParserBase::Pos & pos, Expected & expected, bool id_mode, String & res) { return IParserBase::wrapParseImpl(pos, [&] @@ -120,6 +106,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, @@ -131,11 +128,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; @@ -170,52 +166,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{"SETTINGS"}.ignore(pos, expected) || ParserKeyword{"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{"PROFILES"}.ignore(pos, expected) || ParserKeyword{"PROFILE"}.ignore(pos, expected)) + { + expect_profiles = true; + expect_settings = false; + } + else if (use_inherit_keyword && ParserKeyword{"INHERIT"}.ignore(pos, expected)) + { + if (!ParserKeyword{"PROFILES"}.ignore(pos, expected)) + ParserKeyword{"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{"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; } @@ -223,28 +279,8 @@ bool ParserSettingsProfileElement::parseImpl(Pos & pos, ASTPtr & node, Expected bool ParserSettingsProfileElements::parseImpl(Pos & pos, ASTPtr & node, Expected & expected) { std::vector> elements; - - if (ParserKeyword{"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); @@ -252,4 +288,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 mode; + std::string_view submode; + + auto parse_element = [&] + { + if (ParserKeyword{"ADD"}.ignore(pos, expected)) + { + mode = "ADD"; + submode = ""; + } + else if (ParserKeyword{"DROP"}.ignore(pos, expected)) + { + mode = "DROP"; + submode = ""; + } + else if (ParserKeyword{"MODIFY"}.ignore(pos, expected)) + { + mode = "MODIFY"; + submode = ""; + } + + if (!mode.empty()) + { + if (ParserKeyword{"ALL PROFILES"}.ignore(pos, expected)) + submode = "ALL PROFILES"; + else if (ParserKeyword{"ALL SETTINGS"}.ignore(pos, expected)) + submode = "ALL SETTINGS"; + else if (ParserKeyword{"PROFILES"}.ignore(pos, expected) || ParserKeyword{"PROFILE"}.ignore(pos, expected)) + submode = "PROFILES"; + else if (ParserKeyword{"SETTINGS"}.ignore(pos, expected) || ParserKeyword{"SETTING"}.ignore(pos, expected)) + submode = "SETTINGS"; + } + + if (submode.empty()) + return false; + + if (submode == "PROFILES") + { + auto element = std::make_shared(); + if (!parseProfileNameOrID(pos, expected, /* id_mode= */ false, element->parent_profile)) + return false; + if (mode == "ADD") + { + add_settings.push_back(element); + return true; + } + if (mode == "DROP") + { + drop_settings.push_back(element); + return true; + } + return false; + } + + if (submode == "SETTINGS") + { + auto element = std::make_shared(); + if (mode == "ADD" || mode == "MODIFY") + { + if (!parseSettingNameWithValueOrConstraints(pos, expected, element->setting_name, element->value, element->min_value, element->max_value, element->writability)) + return false; + if (mode == "ADD") + add_settings.push_back(element); + else + modify_settings.push_back(element); + return true; + } + if (mode == "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 (mode == "DROP" && submode == "ALL PROFILES") + { + drop_all_profiles = true; + return true; + } + + if (mode == "DROP" && submode == "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/tests/queries/0_stateless/01294_create_settings_profile.reference b/tests/queries/0_stateless/01294_create_settings_profile.reference index a10d5758752..ab67ee24a43 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..8c435e31354 --- /dev/null +++ b/tests/queries/0_stateless/02943_alter_user_modify_profiles.reference @@ -0,0 +1,21 @@ +CREATE USER test_user +CREATE USER test_user SETTINGS PROFILE profile_a +CREATE USER test_user SETTINGS PROFILE profile_a, PROFILE profile_b +CREATE USER test_user SETTINGS PROFILE profile_b, PROFILE profile_a, PROFILE profile_c +CREATE USER test_user SETTINGS PROFILE profile_a, PROFILE profile_d +CREATE USER test_user SETTINGS PROFILE profile_e +CREATE USER test_user +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..7ae78818372 --- /dev/null +++ b/tests/queries/0_stateless/02943_alter_user_modify_profiles_and_settings.reference @@ -0,0 +1,39 @@ +CREATE USER test_user +CREATE USER test_user SETTINGS PROFILE profile_a, PROFILE profile_b, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE USER test_user +CREATE USER test_user SETTINGS PROFILE profile_a, PROFILE profile_b, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE USER test_user +CREATE USER test_user SETTINGS PROFILE profile_a, PROFILE profile_b, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE USER test_user +CREATE USER test_user SETTINGS PROFILE profile_a, PROFILE profile_b, custom_x = 123, custom_y = 56.5, custom_w = \'www\' +CREATE USER test_user +CREATE USER test_user SETTINGS custom_x = 123, custom_t = 7, custom_s = \'str\' +CREATE USER test_user SETTINGS PROFILE profile_a, custom_x = 123, custom_t = 7, custom_s = \'str\' +CREATE USER test_user SETTINGS PROFILE profile_b, PROFILE profile_a, custom_x = 321, custom_s = \'str\' CONST +CREATE USER test_user +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..8ed61ef77c7 --- /dev/null +++ b/tests/queries/0_stateless/02943_alter_user_modify_settings.reference @@ -0,0 +1,42 @@ +CREATE USER test_user +CREATE USER test_user SETTINGS custom_a = 100 +CREATE USER test_user SETTINGS custom_a = 100, custom_b = 200 +CREATE USER test_user SETTINGS custom_a = 100, custom_b = 300, custom_c = 400 +CREATE USER test_user SETTINGS custom_b = 300, custom_c = 400, custom_d = 500 +CREATE USER test_user SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 0 MAX 1000 +CREATE USER test_user SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 0 MAX 2000 +CREATE USER test_user SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 600 MIN 500 MAX 2000 +CREATE USER test_user SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 700 MIN 500 MAX 2000 +CREATE USER test_user SETTINGS custom_b = 300, custom_c = 400, custom_d = 500, custom_e = 800 MIN 150 +CREATE USER test_user SETTINGS custom_c = 400, custom_d = 500 +CREATE USER test_user SETTINGS custom_c = 400, custom_d = 500, custom_x = 1, custom_y = 2 +CREATE USER test_user SETTINGS custom_z = 3 +CREATE USER test_user +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}