aboutsummaryrefslogtreecommitdiffstats
path: root/yql
diff options
context:
space:
mode:
authorflown4qqqq <flown4qqqq@yandex-team.com>2024-12-24 13:03:06 +0300
committerflown4qqqq <flown4qqqq@yandex-team.com>2024-12-24 13:23:51 +0300
commitf872328e9834aeb436c174b0aca5108a8e943c1a (patch)
tree4502adfe038da859bf27d33701e8cdb7d247ea40 /yql
parent29d2318eb62c7e894d88fc6c82062edc7d28406a (diff)
downloadydb-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.in14
-rw-r--r--yql/essentials/sql/v1/SQLv1Antlr4.g.in14
-rw-r--r--yql/essentials/sql/v1/format/sql_format_ut.h10
-rw-r--r--yql/essentials/sql/v1/node.h8
-rw-r--r--yql/essentials/sql/v1/sql_query.cpp30
-rw-r--r--yql/essentials/sql/v1/sql_translation.cpp81
-rw-r--r--yql/essentials/sql/v1/sql_translation.h2
-rw-r--r--yql/essentials/sql/v1/sql_ut.cpp76
-rw-r--r--yql/essentials/sql/v1/sql_ut_antlr4.cpp76
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;"