diff options
author | flown4qqqq <flown4qqqq@yandex-team.com> | 2024-12-24 13:03:06 +0300 |
---|---|---|
committer | flown4qqqq <flown4qqqq@yandex-team.com> | 2024-12-24 13:23:51 +0300 |
commit | f872328e9834aeb436c174b0aca5108a8e943c1a (patch) | |
tree | 4502adfe038da859bf27d33701e8cdb7d247ea40 /yql | |
parent | 29d2318eb62c7e894d88fc6c82062edc7d28406a (diff) | |
download | ydb-f872328e9834aeb436c174b0aca5108a8e943c1a.tar.gz |
PR from branch users/flown4qqqq/yql-new-query-alter-user-login-nologin
New option in query:
```
CREATE USER user LOGIN;
CREATE USER user NOLOGIN;
ALTER USER user LOGIN;
ALTER USER user NOLOGIN;
```
> implementation in sql
> add ut
commit_hash:7d7c0f251711814d86ae6cb49b4d052317a74001
Diffstat (limited to 'yql')
-rw-r--r-- | yql/essentials/sql/v1/SQLv1.g.in | 14 | ||||
-rw-r--r-- | yql/essentials/sql/v1/SQLv1Antlr4.g.in | 14 | ||||
-rw-r--r-- | yql/essentials/sql/v1/format/sql_format_ut.h | 10 | ||||
-rw-r--r-- | yql/essentials/sql/v1/node.h | 8 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_query.cpp | 30 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_translation.cpp | 81 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_translation.h | 2 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_ut.cpp | 76 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_ut_antlr4.cpp | 76 |
9 files changed, 288 insertions, 23 deletions
diff --git a/yql/essentials/sql/v1/SQLv1.g.in b/yql/essentials/sql/v1/SQLv1.g.in index 670ad27e3e..61e7dc6f25 100644 --- a/yql/essentials/sql/v1/SQLv1.g.in +++ b/yql/essentials/sql/v1/SQLv1.g.in @@ -825,8 +825,8 @@ alter_table_alter_index_action: drop_table_stmt: DROP (TABLE | TABLESTORE | EXTERNAL TABLE) (IF EXISTS)? simple_table_ref; -create_user_stmt: CREATE USER role_name create_user_option?; -alter_user_stmt: ALTER USER role_name (WITH? create_user_option | RENAME TO role_name); +create_user_stmt: CREATE USER role_name create_user_option*; +alter_user_stmt: ALTER USER role_name (WITH? create_user_option+ | RENAME TO role_name); create_group_stmt: CREATE GROUP role_name (WITH USER role_name (COMMA role_name)* COMMA?)?; alter_group_stmt: ALTER GROUP role_name ((ADD|DROP) USER role_name (COMMA role_name)* COMMA? | RENAME TO role_name); @@ -834,7 +834,9 @@ alter_group_stmt: ALTER GROUP role_name ((ADD|DROP) USER role_name (COMMA role_n drop_role_stmt: DROP (USER|GROUP) (IF EXISTS)? role_name (COMMA role_name)* COMMA?; role_name: an_id_or_type | bind_parameter; -create_user_option: ENCRYPTED? PASSWORD expr; +create_user_option: password_option | login_option; +password_option: ENCRYPTED? PASSWORD expr; +login_option: LOGIN | NOLOGIN; grant_permissions_stmt: GRANT permission_name_target ON an_id_schema (COMMA an_id_schema)* TO role_name (COMMA role_name)* COMMA? (WITH GRANT OPTION)?; revoke_permissions_stmt: REVOKE (GRANT OPTION FOR)? permission_name_target ON an_id_schema (COMMA an_id_schema)* FROM role_name (COMMA role_name)*; @@ -1368,6 +1370,7 @@ keyword_as_compat: | LEGACY | LIKE | LOCAL + | LOGIN | MANAGE | MATCH | MATCHES @@ -1380,6 +1383,7 @@ keyword_as_compat: // | NATURAL | NEXT | NO + | NOLOGIN // | NOTNULL | NULLS | OBJECT @@ -1592,6 +1596,7 @@ keyword_compat: ( | LEGACY | LIKE | LOCAL + | LOGIN | MANAGE | MATCH | MATCHES @@ -1604,6 +1609,7 @@ keyword_compat: ( | NATURAL | NEXT | NO + | NOLOGIN | NOTNULL | NULLS | OBJECT @@ -1949,6 +1955,7 @@ LIKE: L I K E; LIMIT: L I M I T; LIST: L I S T; LOCAL: L O C A L; +LOGIN: L O G I N; MANAGE: M A N A G E; MATCH: M A T C H; MATCHES: M A T C H E S; @@ -1961,6 +1968,7 @@ NANOSECONDS: N A N O S E C O N D S; NATURAL: N A T U R A L; NEXT: N E X T; NO: N O; +NOLOGIN: N O L O G I N; NOT: N O T; NOTNULL: N O T N U L L; NULL: N U L L; diff --git a/yql/essentials/sql/v1/SQLv1Antlr4.g.in b/yql/essentials/sql/v1/SQLv1Antlr4.g.in index 89131437e9..96e450501c 100644 --- a/yql/essentials/sql/v1/SQLv1Antlr4.g.in +++ b/yql/essentials/sql/v1/SQLv1Antlr4.g.in @@ -824,8 +824,8 @@ alter_table_alter_index_action: drop_table_stmt: DROP (TABLE | TABLESTORE | EXTERNAL TABLE) (IF EXISTS)? simple_table_ref; -create_user_stmt: CREATE USER role_name create_user_option?; -alter_user_stmt: ALTER USER role_name (WITH? create_user_option | RENAME TO role_name); +create_user_stmt: CREATE USER role_name create_user_option*; +alter_user_stmt: ALTER USER role_name (WITH? create_user_option+ | RENAME TO role_name); create_group_stmt: CREATE GROUP role_name (WITH USER role_name (COMMA role_name)* COMMA?)?; alter_group_stmt: ALTER GROUP role_name ((ADD|DROP) USER role_name (COMMA role_name)* COMMA? | RENAME TO role_name); @@ -833,7 +833,9 @@ alter_group_stmt: ALTER GROUP role_name ((ADD|DROP) USER role_name (COMMA role_n drop_role_stmt: DROP (USER|GROUP) (IF EXISTS)? role_name (COMMA role_name)* COMMA?; role_name: an_id_or_type | bind_parameter; -create_user_option: ENCRYPTED? PASSWORD expr; +create_user_option: password_option | login_option; +password_option: ENCRYPTED? PASSWORD expr; +login_option: LOGIN | NOLOGIN; grant_permissions_stmt: GRANT permission_name_target ON an_id_schema (COMMA an_id_schema)* TO role_name (COMMA role_name)* COMMA? (WITH GRANT OPTION)?; revoke_permissions_stmt: REVOKE (GRANT OPTION FOR)? permission_name_target ON an_id_schema (COMMA an_id_schema)* FROM role_name (COMMA role_name)*; @@ -1367,6 +1369,7 @@ keyword_as_compat: | LEGACY | LIKE | LOCAL + | LOGIN | MANAGE | MATCH | MATCHES @@ -1379,6 +1382,7 @@ keyword_as_compat: // | NATURAL | NEXT | NO + | NOLOGIN // | NOTNULL | NULLS | OBJECT @@ -1591,6 +1595,7 @@ keyword_compat: ( | LEGACY | LIKE | LOCAL + | LOGIN | MANAGE | MATCH | MATCHES @@ -1603,6 +1608,7 @@ keyword_compat: ( | NATURAL | NEXT | NO + | NOLOGIN | NOTNULL | NULLS | OBJECT @@ -1948,6 +1954,7 @@ LIKE: L I K E; LIMIT: L I M I T; LIST: L I S T; LOCAL: L O C A L; +LOGIN: L O G I N; MANAGE: M A N A G E; MATCH: M A T C H; MATCHES: M A T C H E S; @@ -1960,6 +1967,7 @@ NANOSECONDS: N A N O S E C O N D S; NATURAL: N A T U R A L; NEXT: N E X T; NO: N O; +NOLOGIN: N O L O G I N; NOT: N O T; NOTNULL: N O T N U L L; NULL: N U L L; diff --git a/yql/essentials/sql/v1/format/sql_format_ut.h b/yql/essentials/sql/v1/format/sql_format_ut.h index 0f7b708ca3..bd62ddf368 100644 --- a/yql/essentials/sql/v1/format/sql_format_ut.h +++ b/yql/essentials/sql/v1/format/sql_format_ut.h @@ -73,8 +73,13 @@ Y_UNIT_TEST(DropRole) { Y_UNIT_TEST(CreateUser) { TCases cases = { - {"use plato;create user user;","USE plato;\n\nCREATE USER user;\n"}, - {"use plato;create user user encrypted password 'foo';","USE plato;\n\nCREATE USER user ENCRYPTED PASSWORD 'foo';\n"}, + {"use plato;create user user;", "USE plato;\n\nCREATE USER user;\n"}, + {"use plato;create user user encrypted password 'foo';", "USE plato;\n\nCREATE USER user ENCRYPTED PASSWORD 'foo';\n"}, + {"use plato;CREATE USER user1;", "USE plato;\n\nCREATE USER user1;\n"}, + {"use plato;create user user1 encrypted password '123' login;", "USE plato;\n\nCREATE USER user1 ENCRYPTED PASSWORD '123' LOGIN;\n"}, + {"use plato;cREATE USER user1 PASSWORD '123' NOLOGIN;", "USE plato;\n\nCREATE USER user1 PASSWORD '123' NOLOGIN;\n"}, + {"use plato;CREATE USER user1 LOGIN;", "USE plato;\n\nCREATE USER user1 LOGIN;\n"}, + {"use plato;CREATE USER user1 NOLOGIN;", "USE plato;\n\nCREATE USER user1 NOLOGIN;\n"}, }; TSetup setup; @@ -97,6 +102,7 @@ Y_UNIT_TEST(AlterUser) { {"use plato;alter user user rename to user;","USE plato;\n\nALTER USER user RENAME TO user;\n"}, {"use plato;alter user user encrypted password 'foo';","USE plato;\n\nALTER USER user ENCRYPTED PASSWORD 'foo';\n"}, {"use plato;alter user user with encrypted password 'foo';","USE plato;\n\nALTER USER user WITH ENCRYPTED PASSWORD 'foo';\n"}, + {"use plato;ALTER USER user1 NOLOGIN;", "USE plato;\n\nALTER USER user1 NOLOGIN;\n"}, }; TSetup setup; diff --git a/yql/essentials/sql/v1/node.h b/yql/essentials/sql/v1/node.h index fe6ddb08ac..609cd82dd4 100644 --- a/yql/essentials/sql/v1/node.h +++ b/yql/essentials/sql/v1/node.h @@ -1305,6 +1305,14 @@ namespace NSQLTranslationV1 { TMaybe<TDeferredAtom> Password; bool IsPasswordEncrypted = false; TVector<TDeferredAtom> Roles; + + enum class ETypeOfLogin { + Undefined, + Login, + NoLogin + }; + + ETypeOfLogin CanLogin = ETypeOfLogin::Undefined; }; struct TSequenceParameters { diff --git a/yql/essentials/sql/v1/sql_query.cpp b/yql/essentials/sql/v1/sql_query.cpp index 5252417526..a2dd6cc68a 100644 --- a/yql/essentials/sql/v1/sql_query.cpp +++ b/yql/essentials/sql/v1/sql_query.cpp @@ -583,7 +583,7 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& break; } case TRule_sql_stmt_core::kAltSqlStmtCore22: { - // create_user_stmt: CREATE USER role_name create_user_option?; + // create_user_stmt: CREATE USER role_name (create_user_option)*; Ctx.BodyPart(); auto& node = core.GetAlt_sql_stmt_core22().GetRule_create_user_stmt1(); @@ -604,9 +604,19 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& } TMaybe<TRoleParameters> roleParams; - if (node.HasBlock4()) { + const auto& options = node.GetBlock4(); + + if (options.size() > 0) { roleParams.ConstructInPlace(); - if (!RoleParameters(node.GetBlock4().GetRule_create_user_option1(), *roleParams)) { + std::vector<TRule_create_user_option> opts; + opts.reserve(options.size()); + for (const auto& opt : options) { + opts.push_back(opt.GetRule_create_user_option1()); + } + + bool isCreateUser = true; + + if (!RoleParameters(opts, *roleParams, isCreateUser)) { return false; } } @@ -615,7 +625,7 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& break; } case TRule_sql_stmt_core::kAltSqlStmtCore23: { - // alter_user_stmt: ALTER USER role_name (WITH? create_user_option | RENAME TO role_name); + // alter_user_stmt: ALTER USER role_name (WITH? create_user_option+ | RENAME TO role_name); Ctx.BodyPart(); auto& node = core.GetAlt_sql_stmt_core23().GetRule_alter_user_stmt1(); @@ -641,7 +651,17 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& switch (node.GetBlock4().Alt_case()) { case TRule_alter_user_stmt_TBlock4::kAlt1: { TRoleParameters roleParams; - if (!RoleParameters(node.GetBlock4().GetAlt1().GetRule_create_user_option2(), roleParams)) { + + auto options = node.GetBlock4().GetAlt1().GetBlock2(); + std::vector<TRule_create_user_option> opts; + opts.reserve(options.size()); + for (const auto& opt : options) { + opts.push_back(opt.GetRule_create_user_option1()); + } + + bool isCreateUser = false; + + if (!RoleParameters(opts, roleParams, isCreateUser)) { return false; } stmt = BuildAlterUser(pos, service, cluster, roleName, roleParams, Ctx.Scoped); diff --git a/yql/essentials/sql/v1/sql_translation.cpp b/yql/essentials/sql/v1/sql_translation.cpp index 61a273b260..16de88ffe0 100644 --- a/yql/essentials/sql/v1/sql_translation.cpp +++ b/yql/essentials/sql/v1/sql_translation.cpp @@ -3818,19 +3818,82 @@ bool TSqlTranslation::RoleNameClause(const TRule_role_name& node, TDeferredAtom& return true; } -bool TSqlTranslation::RoleParameters(const TRule_create_user_option& node, TRoleParameters& result) { - // create_user_option: ENCRYPTED? PASSWORD expr; +bool TSqlTranslation::RoleParameters(const std::vector<TRule_create_user_option>& optionsList, TRoleParameters& result, bool isCreateUser) { + enum class ECreateUserOption { + Login, + Password + }; + + std::set<ECreateUserOption> used = {}; + + auto ParseCreateUserOption = [&used, this](const TRule_create_user_option& option, TRoleParameters& result) -> bool { + // create_user_option: password_option | login_option; + // password_option: ENCRYPTED? PASSWORD expr; + // login_option: LOGIN | NOLOGIN; + + switch (option.Alt_case()) { + case TRule_create_user_option::kAltCreateUserOption1: + { + TSqlExpression expr(Ctx, Mode); + TNodePtr password = expr.Build(option.GetAlt_create_user_option1().GetRule_password_option1().GetRule_expr3()); + if (!password) { + Error() << "Couldn't parse the password"; + return false; + } + + result.IsPasswordEncrypted = option.GetAlt_create_user_option1().GetRule_password_option1().HasBlock1(); + if (!password->IsNull()) { + result.Password = MakeAtomFromExpression(Ctx.Pos(), Ctx, password); + } + + if (used.contains(ECreateUserOption::Password)) { + Error() << "Conflicting or redundant options"; + return false; + } + + used.insert(ECreateUserOption::Password); + + break; + } + case TRule_create_user_option::kAltCreateUserOption2: + { + if (used.contains(ECreateUserOption::Login)) { + Error() << "Conflicting or redundant options"; + return false; + } + + used.insert(ECreateUserOption::Login); + + const auto token = option.GetAlt_create_user_option2().GetRule_login_option1().GetToken1().GetId(); + if (IS_TOKEN(token, LOGIN)) { + result.CanLogin = TRoleParameters::ETypeOfLogin::Login; + } else if (IS_TOKEN(token, NOLOGIN)) { + result.CanLogin = TRoleParameters::ETypeOfLogin::NoLogin; + } else { + Y_ABORT("You should change implementation according to grammar changes"); + } + + break; + } + case TRule_create_user_option::ALT_NOT_SET: + { + Y_ABORT("You should change implementation according to grammar changes"); + } + } + + return true; + }; + result = TRoleParameters{}; - TSqlExpression expr(Ctx, Mode); - TNodePtr password = expr.Build(node.GetRule_expr3()); - if (!password) { - return false; + if (isCreateUser) { + result.CanLogin = TRoleParameters::ETypeOfLogin::NoLogin; } - result.IsPasswordEncrypted = node.HasBlock1(); - if (!password->IsNull()) { - result.Password = MakeAtomFromExpression(Ctx.Pos(), Ctx, password); + for (const auto& option : optionsList) { + if (!ParseCreateUserOption(option, result)) { + return false; + } } return true; diff --git a/yql/essentials/sql/v1/sql_translation.h b/yql/essentials/sql/v1/sql_translation.h index 325640d74a..3587b57a7a 100644 --- a/yql/essentials/sql/v1/sql_translation.h +++ b/yql/essentials/sql/v1/sql_translation.h @@ -242,7 +242,7 @@ protected: bool ParseResourcePoolClassifierSettings(std::map<TString, TDeferredAtom>& result, const TRule_with_table_settings& settings); bool ParseResourcePoolClassifierSettings(std::map<TString, TDeferredAtom>& result, std::set<TString>& toReset, const TRule_alter_resource_pool_classifier_action& alterAction); bool RoleNameClause(const TRule_role_name& node, TDeferredAtom& result, bool allowSystemRoles); - bool RoleParameters(const TRule_create_user_option& node, TRoleParameters& result); + bool RoleParameters(const std::vector<TRule_create_user_option>& optionsList, TRoleParameters& result, bool isCreateUser); bool PermissionNameClause(const TRule_permission_name_target& node, TVector<TDeferredAtom>& result, bool withGrantOption); bool PermissionNameClause(const TRule_permission_name& node, TDeferredAtom& result); bool PermissionNameClause(const TRule_permission_id& node, TDeferredAtom& result); diff --git a/yql/essentials/sql/v1/sql_ut.cpp b/yql/essentials/sql/v1/sql_ut.cpp index 1c87beee05..efbed0e665 100644 --- a/yql/essentials/sql/v1/sql_ut.cpp +++ b/yql/essentials/sql/v1/sql_ut.cpp @@ -4957,6 +4957,82 @@ select FormatType($f()); "<main>:1:8: Error: Can't use window function LastValue without window specification (OVER keyword is missing)\n"); } + Y_UNIT_TEST(CreateAlterUserWithLoginNoLogin) { + auto reqCreateUser = SqlToYql(R"( + USE plato; + CREATE USER user1; + )"); + + UNIT_ASSERT(reqCreateUser.IsOk()); + UNIT_ASSERT(reqCreateUser.Root); + + auto reqAlterUser = SqlToYql(R"( + USE plato; + ALTER USER user1; + )"); + + UNIT_ASSERT(!reqAlterUser.IsOk()); + UNIT_ASSERT_STRING_CONTAINS(reqAlterUser.Issues.ToString(), "Error: Unexpected token ';' : cannot match to any predicted input..."); + + auto reqPasswordAndLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 PASSWORD '123' LOGIN; + )"); + + UNIT_ASSERT(reqPasswordAndLogin.IsOk()); + UNIT_ASSERT(reqPasswordAndLogin.Root); + + auto reqPasswordAndNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 PASSWORD '123' NOLOGIN; + )"); + + UNIT_ASSERT(reqPasswordAndNoLogin.IsOk()); + UNIT_ASSERT(reqPasswordAndNoLogin.Root); + + auto reqLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 LOGIN; + )"); + + UNIT_ASSERT(reqLogin.IsOk()); + UNIT_ASSERT(reqLogin.Root); + + auto reqNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 NOLOGIN; + )"); + + UNIT_ASSERT(reqNoLogin.IsOk()); + UNIT_ASSERT(reqNoLogin.Root); + + auto reqLoginNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 LOGIN NOLOGIN; + )"); + + UNIT_ASSERT(!reqLoginNoLogin.IsOk()); + UNIT_ASSERT_STRING_CONTAINS(reqLoginNoLogin.Issues.ToString(), "Error: Conflicting or redundant options"); + + auto reqAlterLoginNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 LOGIN; + ALTER USER user1 NOLOGIN; + )"); + + UNIT_ASSERT(reqAlterLoginNoLogin.IsOk()); + UNIT_ASSERT(reqAlterLoginNoLogin.Root); + + auto reqAlterLoginNoLoginWithPassword = SqlToYql(R"( + USE plato; + CREATE USER user1 LOGIN; + ALTER USER user1 PASSWORD '321' NOLOGIN; + )"); + + UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.IsOk()); + UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.Root); + } + Y_UNIT_TEST(CreateAlterUserWithoutCluster) { ExpectFailWithError("\n CREATE USER user ENCRYPTED PASSWORD 'foobar';", "<main>:2:2: Error: USE statement is missing - no default cluster is selected\n"); ExpectFailWithError("ALTER USER CURRENT_USER RENAME TO $foo;", "<main>:1:1: Error: USE statement is missing - no default cluster is selected\n"); diff --git a/yql/essentials/sql/v1/sql_ut_antlr4.cpp b/yql/essentials/sql/v1/sql_ut_antlr4.cpp index 705ca3d19c..c5df28bbaa 100644 --- a/yql/essentials/sql/v1/sql_ut_antlr4.cpp +++ b/yql/essentials/sql/v1/sql_ut_antlr4.cpp @@ -471,6 +471,82 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["EquiJoin"]); } + Y_UNIT_TEST(CreateAlterUserWithLoginNoLogin) { + auto reqCreateUser = SqlToYql(R"( + USE plato; + CREATE USER user1; + )"); + + UNIT_ASSERT(reqCreateUser.IsOk()); + UNIT_ASSERT(reqCreateUser.Root); + + auto reqAlterUser = SqlToYql(R"( + USE plato; + ALTER USER user1; + )"); + + UNIT_ASSERT(!reqAlterUser.IsOk()); + UNIT_ASSERT_STRING_CONTAINS(reqAlterUser.Issues.ToString(), "Error: mismatched input ';' expecting {ENCRYPTED, LOGIN, NOLOGIN, PASSWORD, RENAME, WITH}"); + + auto reqPasswordAndLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 LOgin; + )"); + + UNIT_ASSERT(reqPasswordAndLogin.IsOk()); + UNIT_ASSERT(reqPasswordAndLogin.Root); + + auto reqPasswordAndNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 PASSWORD '123' NOLOGIN; + )"); + + UNIT_ASSERT(reqPasswordAndNoLogin.IsOk()); + UNIT_ASSERT(reqPasswordAndNoLogin.Root); + + auto reqLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 LOGIN; + )"); + + UNIT_ASSERT(reqLogin.IsOk()); + UNIT_ASSERT(reqLogin.Root); + + auto reqNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 NOLOGIN; + )"); + + UNIT_ASSERT(reqNoLogin.IsOk()); + UNIT_ASSERT(reqNoLogin.Root); + + auto reqLoginNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 LOGIN NOLOGIN; + )"); + + UNIT_ASSERT(!reqLoginNoLogin.IsOk()); + UNIT_ASSERT_STRING_CONTAINS(reqLoginNoLogin.Issues.ToString(), "Error: Conflicting or redundant options"); + + auto reqAlterLoginNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 LOGIN; + ALTER USER user1 NOLOGIN; + )"); + + UNIT_ASSERT(reqAlterLoginNoLogin.IsOk()); + UNIT_ASSERT(reqAlterLoginNoLogin.Root); + + auto reqAlterLoginNoLoginWithPassword = SqlToYql(R"( + USE plato; + CREATE USER user1 LOGIN; + ALTER USER user1 PASSWORD '321' NOLOGIN; + )"); + + UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.IsOk()); + UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.Root); + } + Y_UNIT_TEST(JoinWithoutConcreteColumns) { NYql::TAstParseResult res = SqlToYql( " use plato;" |