diff options
author | flown4qqqq <flown4qqqq@yandex-team.com> | 2025-01-24 00:47:07 +0300 |
---|---|---|
committer | flown4qqqq <flown4qqqq@yandex-team.com> | 2025-01-24 01:01:16 +0300 |
commit | bf896a57c7a7ffb14ba93068a4c3efeab50dfb36 (patch) | |
tree | 38715f12c0ddf2192da62ab1a41f119c0293e1e2 /yql | |
parent | e0834724754ae9e26fd14e27027e69cc22d1939f (diff) | |
download | ydb-bf896a57c7a7ffb14ba93068a4c3efeab50dfb36.tar.gz |
Add option 'HASH' into CREATE USER
Need for: <https://github.com/ydb-platform/ydb-rfc/blob/main/backup_fstek.md>
```
CREATE USER my_user HASH '
{"hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=",
"salt": "U+tzBtgo06EBQCjlARA6Jg==",
"type": "argon2id"
}'
```
commit_hash:a0c695c2e2f7f794e5c4db978fe33a7bfea59e2c
Diffstat (limited to 'yql')
-rw-r--r-- | yql/essentials/sql/v1/SQLv1.g.in | 10 | ||||
-rw-r--r-- | yql/essentials/sql/v1/SQLv1Antlr4.g.in | 10 | ||||
-rw-r--r-- | yql/essentials/sql/v1/format/sql_format_ut.h | 4 | ||||
-rw-r--r-- | yql/essentials/sql/v1/node.h | 26 | ||||
-rw-r--r-- | yql/essentials/sql/v1/query.cpp | 166 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_query.cpp | 52 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_translation.cpp | 123 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_translation.h | 5 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_ut.cpp | 57 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_ut_antlr4.cpp | 59 |
10 files changed, 331 insertions, 181 deletions
diff --git a/yql/essentials/sql/v1/SQLv1.g.in b/yql/essentials/sql/v1/SQLv1.g.in index 510afaa501..d28661e68e 100644 --- a/yql/essentials/sql/v1/SQLv1.g.in +++ b/yql/essentials/sql/v1/SQLv1.g.in @@ -828,8 +828,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 user_option*; +alter_user_stmt: ALTER USER role_name (WITH? 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); @@ -837,8 +837,12 @@ 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: password_option | login_option; + +user_option: authentication_option | login_option; + +authentication_option: password_option | hash_option; password_option: ENCRYPTED? PASSWORD expr; +hash_option: HASH 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)?; diff --git a/yql/essentials/sql/v1/SQLv1Antlr4.g.in b/yql/essentials/sql/v1/SQLv1Antlr4.g.in index 2ddfd0c748..3f4089a93b 100644 --- a/yql/essentials/sql/v1/SQLv1Antlr4.g.in +++ b/yql/essentials/sql/v1/SQLv1Antlr4.g.in @@ -827,8 +827,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 user_option*; +alter_user_stmt: ALTER USER role_name (WITH? 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); @@ -836,8 +836,12 @@ 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: password_option | login_option; + +user_option: authentication_option | login_option; + +authentication_option: password_option | hash_option; password_option: ENCRYPTED? PASSWORD expr; +hash_option: HASH 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)?; diff --git a/yql/essentials/sql/v1/format/sql_format_ut.h b/yql/essentials/sql/v1/format/sql_format_ut.h index a8fd6b7b3f..722863ecaa 100644 --- a/yql/essentials/sql/v1/format/sql_format_ut.h +++ b/yql/essentials/sql/v1/format/sql_format_ut.h @@ -80,6 +80,8 @@ Y_UNIT_TEST(CreateUser) { {"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"}, + {"use plato;CReATE UseR user1 HasH '{\"hash\": \"p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=\",\"salt\": \"U+tzBtgo06EBQCjlARA6Jg==\",\"type\": \"argon2id\"}'", + "USE plato;\n\nCREATE USER user1 HASH '{\"hash\": \"p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=\",\"salt\": \"U+tzBtgo06EBQCjlARA6Jg==\",\"type\": \"argon2id\"}';\n"}, }; TSetup setup; @@ -103,6 +105,8 @@ Y_UNIT_TEST(AlterUser) { {"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"}, + {"use plato;alter UseR user1 HasH '{\"hash\": \"p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=\",\"salt\": \"U+tzBtgo06EBQCjlARA6Jg==\",\"type\": \"argon2id\"}'", + "USE plato;\n\nALTER USER user1 HASH '{\"hash\": \"p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=\",\"salt\": \"U+tzBtgo06EBQCjlARA6Jg==\",\"type\": \"argon2id\"}';\n"}, }; TSetup setup; diff --git a/yql/essentials/sql/v1/node.h b/yql/essentials/sql/v1/node.h index e19245f695..c9402a2f97 100644 --- a/yql/essentials/sql/v1/node.h +++ b/yql/essentials/sql/v1/node.h @@ -1302,19 +1302,21 @@ namespace NSQLTranslationV1 { }; struct TRoleParameters { - TMaybe<TDeferredAtom> Password; - bool IsPasswordEncrypted = false; + protected: + TRoleParameters() {} + public: TVector<TDeferredAtom> Roles; + }; - enum class ETypeOfLogin { - Undefined, - Login, - NoLogin - }; - - ETypeOfLogin CanLogin = ETypeOfLogin::Undefined; + struct TUserParameters : TRoleParameters { + TMaybe<TDeferredAtom> Password; + bool IsPasswordEncrypted = false; + std::optional<bool> CanLogin; + TMaybe<TDeferredAtom> Hash; }; + struct TCreateGroupParameters : TRoleParameters {}; + struct TSequenceParameters { bool MissingOk = false; TMaybe<TDeferredAtom> StartValue; @@ -1522,9 +1524,9 @@ namespace NSQLTranslationV1 { ); // Implemented in query.cpp - TNodePtr BuildCreateUser(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TMaybe<TRoleParameters>& params, TScopedStatePtr scoped); - TNodePtr BuildCreateGroup(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TMaybe<TRoleParameters>& params, TScopedStatePtr scoped); - TNodePtr BuildAlterUser(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TRoleParameters& params, TScopedStatePtr scoped); + TNodePtr BuildCreateGroup(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TMaybe<TCreateGroupParameters>& params, TScopedStatePtr scoped); + TNodePtr BuildControlUser(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, + const TMaybe<TUserParameters>& params, TScopedStatePtr scoped, bool isCreateUser); TNodePtr BuildRenameUser(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TDeferredAtom& newName, TScopedStatePtr scoped); TNodePtr BuildAlterGroup(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TVector<TDeferredAtom>& toChange, bool isDrop, TScopedStatePtr scoped); diff --git a/yql/essentials/sql/v1/query.cpp b/yql/essentials/sql/v1/query.cpp index a1323972c4..8ed6827cf3 100644 --- a/yql/essentials/sql/v1/query.cpp +++ b/yql/essentials/sql/v1/query.cpp @@ -842,7 +842,7 @@ TNodePtr BuildTableKeys(TPosition pos, const TString& service, const TDeferredAt return new TPrepTableKeys(pos, service, cluster, func, args); } -class TInputOptions final: public TAstListNode { +class TInputOptions final : public TAstListNode { public: TInputOptions(TPosition pos, const TTableHints& hints) : TAstListNode(pos) @@ -889,7 +889,7 @@ TNodePtr BuildInputOptions(TPosition pos, const TTableHints& hints) { return new TInputOptions(pos, hints); } -class TIntoTableOptions: public TAstListNode { +class TIntoTableOptions : public TAstListNode { public: TIntoTableOptions(TPosition pos, const TVector<TString>& columns, const TTableHints& hints) : TAstListNode(pos) @@ -944,7 +944,7 @@ TNodePtr BuildIntoTableOptions(TPosition pos, const TVector<TString>& eraseColum return new TIntoTableOptions(pos, eraseColumns, hints); } -class TInputTablesNode final: public TAstListNode { +class TInputTablesNode final : public TAstListNode { public: TInputTablesNode(TPosition pos, const TTableList& tables, bool inSubquery, TScopedStatePtr scoped) : TAstListNode(pos) @@ -1000,7 +1000,7 @@ TNodePtr BuildInputTables(TPosition pos, const TTableList& tables, bool inSubque return new TInputTablesNode(pos, tables, inSubquery, scoped); } -class TCreateTableNode final: public TAstListNode { +class TCreateTableNode final : public TAstListNode { public: TCreateTableNode(TPosition pos, const TTableRef& tr, bool existingOk, bool replaceIfExists, const TCreateTableParameters& params, TSourcePtr values, TScopedStatePtr scoped) : TAstListNode(pos) @@ -1314,7 +1314,7 @@ TNodePtr BuildCreateTable(TPosition pos, const TTableRef& tr, bool existingOk, b return new TCreateTableNode(pos, tr, existingOk, replaceIfExists, params, std::move(values), scoped); } -class TAlterTableNode final: public TAstListNode { +class TAlterTableNode final : public TAstListNode { public: TAlterTableNode(TPosition pos, const TTableRef& tr, const TAlterTableParameters& params, TScopedStatePtr scoped) : TAstListNode(pos) @@ -1544,7 +1544,7 @@ TNodePtr BuildAlterTable(TPosition pos, const TTableRef& tr, const TAlterTablePa return new TAlterTableNode(pos, tr, params, scoped); } -class TDropTableNode final: public TAstListNode { +class TDropTableNode final : public TAstListNode { public: TDropTableNode(TPosition pos, const TTableRef& tr, bool missingOk, ETableType tableType, TScopedStatePtr scoped) : TAstListNode(pos) @@ -1633,7 +1633,7 @@ static INode::TPtr CreateConsumerDesc(const TTopicConsumerDescription& desc, con ); } -class TCreateTopicNode final: public TAstListNode { +class TCreateTopicNode final : public TAstListNode { public: TCreateTopicNode(TPosition pos, const TTopicRef& tr, const TCreateTopicParameters& params, TScopedStatePtr scoped) : TAstListNode(pos) @@ -1725,7 +1725,7 @@ TNodePtr BuildCreateTopic( return new TCreateTopicNode(pos, tr, params, scoped); } -class TAlterTopicNode final: public TAstListNode { +class TAlterTopicNode final : public TAstListNode { public: TAlterTopicNode(TPosition pos, const TTopicRef& tr, const TAlterTopicParameters& params, TScopedStatePtr scoped) : TAstListNode(pos) @@ -1847,7 +1847,7 @@ TNodePtr BuildAlterTopic( return new TAlterTopicNode(pos, tr, params, scoped); } -class TDropTopicNode final: public TAstListNode { +class TDropTopicNode final : public TAstListNode { public: TDropTopicNode(TPosition pos, const TTopicRef& tr, const TDropTopicParameters& params, TScopedStatePtr scoped) : TAstListNode(pos) @@ -1895,37 +1895,47 @@ TNodePtr BuildDropTopic(TPosition pos, const TTopicRef& tr, const TDropTopicPara return new TDropTopicNode(pos, tr, params, scoped); } -class TCreateRole final: public TAstListNode { +class TControlUser final : public TAstListNode { public: - TCreateRole(TPosition pos, bool isUser, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TMaybe<TRoleParameters>& params, TScopedStatePtr scoped) + TControlUser(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TMaybe<TUserParameters>& params, TScopedStatePtr scoped, bool IsCreateUser) : TAstListNode(pos) - , IsUser(isUser) , Service(service) , Cluster(cluster) , Name(name) , Params(params) , Scoped(scoped) + , IsCreateUser(IsCreateUser) { FakeSource = BuildFakeSource(pos); scoped->UseCluster(service, cluster); } - bool DoInit(TContext& ctx, ISource* src) override { - Y_UNUSED(src); + bool DoInit(TContext& ctx, ISource*) override { auto name = Name.Build(); TNodePtr password; - if (Params && Params->Password) { - password = Params->Password->Build(); + TNodePtr hash; + + if (Params) { + if (Params->Password) { + password = Params->Password->Build(); + } else if (Params->Hash) { + hash = Params->Hash->Build(); + } } + TNodePtr cluster = Scoped->WrapCluster(Cluster, ctx); - if (!name->Init(ctx, FakeSource.Get()) || !cluster->Init(ctx, FakeSource.Get())) { - return false; - } - if (password && !password->Init(ctx, FakeSource.Get())) { + if (!name->Init(ctx, FakeSource.Get()) + || !cluster->Init(ctx, FakeSource.Get()) + || password && !password->Init(ctx, FakeSource.Get()) + || hash && !hash->Init(ctx, FakeSource.Get()) + ) + { return false; } + auto options = Y(Q(Y(Q("mode"), Q(IsCreateUser ? "createUser" : "alterUser")))) ; + TVector<TNodePtr> roles; if (Params && !Params->Roles.empty()) { for (auto& item : Params->Roles) { @@ -1934,9 +1944,10 @@ public: return false; } } + + options = L(options, Q(Y(Q("roles"), Q(new TAstListNodeImpl(Pos, std::move(roles)))))); } - auto options = Y(Q(Y(Q("mode"), Q(IsUser ? "createUser" : "createGroup")))); if (Params) { if (Params->IsPasswordEncrypted) { options = L(options, Q(Y(Q("passwordEncrypted")))); @@ -1944,17 +1955,15 @@ public: if (Params->Password) { options = L(options, Q(Y(Q("password"), password))); + } else if (Params->Hash) { + options = L(options, Q(Y(Q("hash"), hash))); } else { options = L(options, Q(Y(Q("nullPassword")))); } - if (!Params->Roles.empty()) { - options = L(options, Q(Y(Q("roles"), Q(new TAstListNodeImpl(Pos, std::move(roles)))))); - } - - if (Params->CanLogin == TRoleParameters::ETypeOfLogin::Login) { + if (Params->CanLogin.has_value() && Params->CanLogin.value() || !Params->CanLogin.has_value() && IsCreateUser) { options = L(options, Q(Y(Q("login")))); - } else if (Params->CanLogin == TRoleParameters::ETypeOfLogin::NoLogin) { + } else { options = L(options, Q(Y(Q("noLogin")))); } } @@ -1971,29 +1980,31 @@ public: TPtr DoClone() const final { return {}; } + private: - const bool IsUser; const TString Service; TDeferredAtom Cluster; TDeferredAtom Name; - const TMaybe<TRoleParameters> Params; + const TMaybe<TUserParameters> Params; TScopedStatePtr Scoped; TSourcePtr FakeSource; + bool IsCreateUser; }; -TNodePtr BuildCreateUser(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TMaybe<TRoleParameters>& params, TScopedStatePtr scoped) { - bool isUser = true; - return new TCreateRole(pos, isUser, service, cluster, name, params, scoped); -} - -TNodePtr BuildCreateGroup(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TMaybe<TRoleParameters>& params, TScopedStatePtr scoped) { - bool isUser = false; - return new TCreateRole(pos, isUser, service, cluster, name, params, scoped); +TNodePtr BuildControlUser( TPosition pos, + const TString& service, + const TDeferredAtom& cluster, + const TDeferredAtom& name, + const TMaybe<TUserParameters>& params, + TScopedStatePtr scoped, + bool isCreateUser) +{ + return new TControlUser(pos, service, cluster, name, params, scoped, isCreateUser); } -class TAlterUser final: public TAstListNode { +class TCreateGroup final : public TAstListNode { public: - TAlterUser(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TRoleParameters& params, TScopedStatePtr scoped) + TCreateGroup(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TMaybe<TCreateGroupParameters>& params, TScopedStatePtr scoped) : TAstListNode(pos) , Service(service) , Cluster(cluster) @@ -2005,42 +2016,26 @@ public: scoped->UseCluster(service, cluster); } - bool DoInit(TContext& ctx, ISource* src) override { - Y_UNUSED(src); - auto name = Name.Build(); - TNodePtr password; - if (Params.Password) { - password = Params.Password->Build(); - } - TNodePtr cluster = Scoped->WrapCluster(Cluster, ctx); - - if (!name->Init(ctx, FakeSource.Get()) || !cluster->Init(ctx, FakeSource.Get())) { - return false; - } - if (password && !password->Init(ctx, FakeSource.Get())) { - return false; - } + bool DoInit(TContext& ctx, ISource*) override { + auto options = Y(Q(Y(Q("mode"), Q("createGroup")))); - auto options = Y(Q(Y(Q("mode"), Q("alterUser")))); - if (Params.IsPasswordEncrypted) { - options = L(options, Q(Y(Q("passwordEncrypted")))); - } + TVector<TNodePtr> roles; + if (Params && !Params->Roles.empty()) { + for (auto& item : Params->Roles) { + roles.push_back(item.Build()); + if (!roles.back()->Init(ctx, FakeSource.Get())) { + return false; + } + } - if (Params.Password) { - options = L(options, Q(Y(Q("password"), password))); - } else { - options = L(options, Q(Y(Q("nullPassword")))); + options = L(options, Q(Y(Q("roles"), Q(new TAstListNodeImpl(Pos, std::move(roles)))))); } - if (Params.CanLogin == TRoleParameters::ETypeOfLogin::Login) { - options = L(options, Q(Y(Q("login")))); - } else if (Params.CanLogin == TRoleParameters::ETypeOfLogin::NoLogin) { - options = L(options, Q(Y(Q("noLogin")))); - } + TNodePtr cluster = Scoped->WrapCluster(Cluster, ctx); Add("block", Q(Y( Y("let", "sink", Y("DataSink", BuildQuotedAtom(Pos, Service), cluster)), - Y("let", "world", Y(TString(WriteName), "world", "sink", Y("Key", Q(Y(Q("role"), Y("String", name)))), Y("Void"), Q(options))), + Y("let", "world", Y(TString(WriteName), "world", "sink", Y("Key", Q(Y(Q("role"), Y("String", Name.Build())))), Y("Void"), Q(options))), Y("return", ctx.PragmaAutoCommit ? Y(TString(CommitName), "world", "sink") : AstNode("world")) ))); @@ -2050,20 +2045,21 @@ public: TPtr DoClone() const final { return {}; } + private: const TString Service; TDeferredAtom Cluster; TDeferredAtom Name; - const TRoleParameters Params; + const TMaybe<TCreateGroupParameters> Params; TScopedStatePtr Scoped; TSourcePtr FakeSource; }; -TNodePtr BuildAlterUser(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TRoleParameters& params, TScopedStatePtr scoped) { - return new TAlterUser(pos, service, cluster, name, params, scoped); +TNodePtr BuildCreateGroup(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TMaybe<TCreateGroupParameters>& params, TScopedStatePtr scoped) { + return new TCreateGroup(pos, service, cluster, name, params, scoped); } -class TAlterSequence final: public TAstListNode { +class TAlterSequence final : public TAstListNode { public: TAlterSequence(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TString& id, const TSequenceParameters& params, TScopedStatePtr scoped) : TAstListNode(pos) @@ -2167,7 +2163,7 @@ TNodePtr BuildAlterSequence(TPosition pos, const TString& service, const TDeferr return new TAlterSequence(pos, service, cluster, id, params, scoped); } -class TRenameRole final: public TAstListNode { +class TRenameRole final : public TAstListNode { public: TRenameRole(TPosition pos, bool isUser, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TDeferredAtom& newName, TScopedStatePtr scoped) : TAstListNode(pos) @@ -2230,7 +2226,7 @@ TNodePtr BuildRenameGroup(TPosition pos, const TString& service, const TDeferred return new TRenameRole(pos, isUser, service, cluster, name, newName, scoped); } -class TAlterGroup final: public TAstListNode { +class TAlterGroup final : public TAstListNode { public: TAlterGroup(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TVector<TDeferredAtom>& toChange, bool isDrop, TScopedStatePtr scoped) : TAstListNode(pos) @@ -2293,7 +2289,7 @@ TNodePtr BuildAlterGroup(TPosition pos, const TString& service, const TDeferredA return new TAlterGroup(pos, service, cluster, name, toChange, isDrop, scoped); } -class TDropRoles final: public TAstListNode { +class TDropRoles final : public TAstListNode { public: TDropRoles(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TVector<TDeferredAtom>& toDrop, bool isUser, bool missingOk, TScopedStatePtr scoped) : TAstListNode(pos) @@ -2517,7 +2513,7 @@ private: }; // TAsyncReplication -class TCreateAsyncReplication final: public TAsyncReplication { +class TCreateAsyncReplication final : public TAsyncReplication { public: explicit TCreateAsyncReplication(TPosition pos, const TString& id, std::vector<std::pair<TString, TString>>&& targets, @@ -2571,7 +2567,7 @@ TNodePtr BuildCreateAsyncReplication(TPosition pos, const TString& id, return new TCreateAsyncReplication(pos, id, std::move(targets), std::move(settings), context); } -class TDropAsyncReplication final: public TAsyncReplication { +class TDropAsyncReplication final : public TAsyncReplication { public: explicit TDropAsyncReplication(TPosition pos, const TString& id, bool cascade, const TObjectOperatorContext& context) : TAsyncReplication(pos, id, cascade ? "dropCascade" : "drop", context) @@ -2589,7 +2585,7 @@ TNodePtr BuildDropAsyncReplication(TPosition pos, const TString& id, bool cascad return new TDropAsyncReplication(pos, id, cascade, context); } -class TAlterAsyncReplication final: public TAsyncReplication { +class TAlterAsyncReplication final : public TAsyncReplication { public: explicit TAlterAsyncReplication(TPosition pos, const TString& id, std::map<TString, TNodePtr>&& settings, @@ -2805,7 +2801,7 @@ static const TMap<EWriteColumnMode, TString> columnModeToStrMapKikimr { {EWriteColumnMode::DeleteOn, "delete_on"}, }; -class TWriteTableNode final: public TAstListNode { +class TWriteTableNode final : public TAstListNode { public: TWriteTableNode(TPosition pos, const TString& label, const TTableRef& table, EWriteColumnMode mode, TNodePtr options, TScopedStatePtr scoped) @@ -2876,7 +2872,7 @@ TNodePtr BuildWriteTable(TPosition pos, const TString& label, const TTableRef& t return new TWriteTableNode(pos, label, table, mode, std::move(options), scoped); } -class TClustersSinkOperationBase: public TAstListNode { +class TClustersSinkOperationBase : public TAstListNode { protected: TClustersSinkOperationBase(TPosition pos) : TAstListNode(pos) @@ -2936,7 +2932,7 @@ TNodePtr BuildRollbackClusters(TPosition pos) { return new TRollbackClustersNode(pos); } -class TWriteResultNode final: public TAstListNode { +class TWriteResultNode final : public TAstListNode { public: TWriteResultNode(TPosition pos, const TString& label, TNodePtr settings) : TAstListNode(pos) @@ -2972,7 +2968,7 @@ TNodePtr BuildWriteResult(TPosition pos, const TString& label, TNodePtr settings return new TWriteResultNode(pos, label, settings); } -class TYqlProgramNode: public TAstListNode { +class TYqlProgramNode : public TAstListNode { public: TYqlProgramNode(TPosition pos, const TVector<TNodePtr>& blocks, bool topLevel, TScopedStatePtr scoped, bool useSeq) : TAstListNode(pos) @@ -3310,7 +3306,7 @@ TNodePtr BuildQuery(TPosition pos, const TVector<TNodePtr>& blocks, bool topLeve return new TYqlProgramNode(pos, blocks, topLevel, scoped, useSeq); } -class TPragmaNode final: public INode { +class TPragmaNode final : public INode { public: TPragmaNode(TPosition pos, const TString& prefix, const TString& name, const TVector<TDeferredAtom>& values, bool valueDefault) : INode(pos) @@ -3394,7 +3390,7 @@ TNodePtr BuildPragma(TPosition pos, const TString& prefix, const TString& name, return new TPragmaNode(pos, prefix, name, values, valueDefault); } -class TSqlLambda final: public TAstListNode { +class TSqlLambda final : public TAstListNode { public: TSqlLambda(TPosition pos, TVector<TString>&& args, TVector<TNodePtr>&& exprSeq) : TAstListNode(pos) @@ -3553,7 +3549,7 @@ TNodePtr BuildWorldForNode(TPosition pos, TNodePtr list, TNodePtr bodyNode, TNod return new TWorldFor(pos, list, bodyNode, elseNode, isEvaluate, isParallel); } -class TAnalyzeNode final: public TAstListNode { +class TAnalyzeNode final : public TAstListNode { public: TAnalyzeNode(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TAnalyzeParams& params, TScopedStatePtr scoped) : TAstListNode(pos) diff --git a/yql/essentials/sql/v1/sql_query.cpp b/yql/essentials/sql/v1/sql_query.cpp index a04ce65986..7510acc467 100644 --- a/yql/essentials/sql/v1/sql_query.cpp +++ b/yql/essentials/sql/v1/sql_query.cpp @@ -648,7 +648,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 (user_option)*; Ctx.BodyPart(); auto& node = core.GetAlt_sql_stmt_core22().GetRule_create_user_stmt1(); @@ -668,27 +668,26 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& return false; } - TMaybe<TRoleParameters> roleParams; + TMaybe<TUserParameters> createUserParams; const auto& options = node.GetBlock4(); - { - roleParams.ConstructInPlace(); - std::vector<TRule_create_user_option> opts; - opts.reserve(options.size()); - for (const auto& opt : options) { - opts.push_back(opt.GetRule_create_user_option1()); - } + createUserParams.ConstructInPlace(); + std::vector<TRule_user_option> opts; + opts.reserve(options.size()); + for (const auto& opt : options) { + opts.push_back(opt.GetRule_user_option1()); + } - if (!RoleParameters(opts, *roleParams)) { - return false; - } + bool isCreateUser = true; + if (!UserParameters(opts, *createUserParams, isCreateUser)) { + return false; } - AddStatementToBlocks(blocks, BuildCreateUser(pos, service, cluster, roleName, roleParams, Ctx.Scoped)); + AddStatementToBlocks(blocks, BuildControlUser(pos, service, cluster, roleName, createUserParams, Ctx.Scoped, isCreateUser)); 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? user_option+ | RENAME TO role_name); Ctx.BodyPart(); auto& node = core.GetAlt_sql_stmt_core23().GetRule_alter_user_stmt1(); @@ -713,19 +712,20 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& TNodePtr stmt; switch (node.GetBlock4().Alt_case()) { case TRule_alter_user_stmt_TBlock4::kAlt1: { - TRoleParameters roleParams; + TUserParameters alterUserParams; auto options = node.GetBlock4().GetAlt1().GetBlock2(); - std::vector<TRule_create_user_option> opts; + std::vector<TRule_user_option> opts; opts.reserve(options.size()); for (const auto& opt : options) { - opts.push_back(opt.GetRule_create_user_option1()); + opts.push_back(opt.GetRule_user_option1()); } - if (!RoleParameters(opts, roleParams)) { + bool isCreateUser = false; + if (!UserParameters(opts, alterUserParams, isCreateUser)) { return false; } - stmt = BuildAlterUser(pos, service, cluster, roleName, roleParams, Ctx.Scoped); + stmt = BuildControlUser(pos, service, cluster, roleName, alterUserParams, Ctx.Scoped, isCreateUser); break; } case TRule_alter_user_stmt_TBlock4::kAlt2: { @@ -734,7 +734,7 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& if (!RoleNameClause(node.GetBlock4().GetAlt2().GetRule_role_name3(), tgtRoleName, allowSystemRoles)) { return false; } - stmt = BuildRenameUser(pos, service, cluster, roleName, tgtRoleName,Ctx.Scoped); + stmt = BuildRenameUser(pos, service, cluster, roleName, tgtRoleName, Ctx.Scoped); break; } case TRule_alter_user_stmt_TBlock4::ALT_NOT_SET: @@ -765,25 +765,25 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& return false; } - TRoleParameters roleParams; + TCreateGroupParameters createGroupParams; if (node.HasBlock4()) { auto& addDropNode = node.GetBlock4(); TVector<TDeferredAtom> roles; bool allowSystemRoles = false; - roleParams.Roles.emplace_back(); - if (!RoleNameClause(addDropNode.GetRule_role_name3(), roleParams.Roles.back(), allowSystemRoles)) { + createGroupParams.Roles.emplace_back(); + if (!RoleNameClause(addDropNode.GetRule_role_name3(), createGroupParams.Roles.back(), allowSystemRoles)) { return false; } for (auto& item : addDropNode.GetBlock4()) { - roleParams.Roles.emplace_back(); - if (!RoleNameClause(item.GetRule_role_name2(), roleParams.Roles.back(), allowSystemRoles)) { + createGroupParams.Roles.emplace_back(); + if (!RoleNameClause(item.GetRule_role_name2(), createGroupParams.Roles.back(), allowSystemRoles)) { return false; } } } - AddStatementToBlocks(blocks, BuildCreateGroup(pos, service, cluster, roleName, roleParams, Ctx.Scoped)); + AddStatementToBlocks(blocks, BuildCreateGroup(pos, service, cluster, roleName, createGroupParams, Ctx.Scoped)); break; } case TRule_sql_stmt_core::kAltSqlStmtCore25: { diff --git a/yql/essentials/sql/v1/sql_translation.cpp b/yql/essentials/sql/v1/sql_translation.cpp index a4c55cad71..1c60897886 100644 --- a/yql/essentials/sql/v1/sql_translation.cpp +++ b/yql/essentials/sql/v1/sql_translation.cpp @@ -3818,64 +3818,113 @@ bool TSqlTranslation::RoleNameClause(const TRule_role_name& node, TDeferredAtom& return true; } -bool TSqlTranslation::RoleParameters(const std::vector<TRule_create_user_option>& optionsList, TRoleParameters& result) { - enum class ECreateUserOption { +bool TSqlTranslation::PasswordParameter(const TRule_password_option& passwordOption, TUserParameters& result) { + // password_option: ENCRYPTED? PASSWORD expr; + TSqlExpression expr(Ctx, Mode); + TNodePtr password = expr.Build(passwordOption.GetRule_expr3()); + if (!password) { + Error() << "Couldn't parse the password"; + return false; + } + + result.IsPasswordEncrypted = passwordOption.HasBlock1(); + if (!password->IsNull()) { + result.Password = MakeAtomFromExpression(Ctx.Pos(), Ctx, password); + } + + return true; +} + +bool TSqlTranslation::HashParameter(const TRule_hash_option& hashOption, TUserParameters& result) { + // hash_option: HASH expr; + TSqlExpression expr(Ctx, Mode); + TNodePtr hash = expr.Build(hashOption.GetRule_expr2()); + + if (!hash) { + Error() << "Couldn't parse the hash of password"; + return false; + } + + if (!hash->IsNull()) { + result.Hash = MakeAtomFromExpression(Ctx.Pos(), Ctx, hash); + } + + return true; +} + +void TSqlTranslation::LoginParameter(const TRule_login_option& loginOption, std::optional<bool>& canLogin) { + // login_option: LOGIN | NOLOGIN; + + auto token = loginOption.GetToken1().GetId(); + if (IS_TOKEN(token, LOGIN)) { + canLogin = true; + } else if (IS_TOKEN(token, NOLOGIN)) { + canLogin = false; + } else { + Y_ABORT("You should change implementation according to grammar changes"); + } +} + +bool TSqlTranslation::UserParameters(const std::vector<TRule_user_option>& optionsList, TUserParameters& result, bool isCreateUser) { + enum class EUserOption { Login, - Password + Authentication }; - std::set<ECreateUserOption> used = {}; + std::set<EUserOption> 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; + auto ParseUserOption = [&used, this](const TRule_user_option& option, TUserParameters& result) -> bool { + // user_option: authentication_option | login_option; + // authentication_option: password_option | hash_option; switch (option.Alt_case()) { - case TRule_create_user_option::kAltCreateUserOption1: + case TRule_user_option::kAltUserOption1: { - 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"; + if (used.contains(EUserOption::Authentication)) { + Error() << "Conflicting or redundant options"; return false; } - result.IsPasswordEncrypted = option.GetAlt_create_user_option1().GetRule_password_option1().HasBlock1(); - if (!password->IsNull()) { - result.Password = MakeAtomFromExpression(Ctx.Pos(), Ctx, password); - } + used.insert(EUserOption::Authentication); - if (used.contains(ECreateUserOption::Password)) { - Error() << "Conflicting or redundant options"; - return false; - } + const auto& authenticationOption = option.GetAlt_user_option1().GetRule_authentication_option1(); + + switch (authenticationOption.Alt_case()) { + case TRule_authentication_option::kAltAuthenticationOption1: { + if (!PasswordParameter(authenticationOption.GetAlt_authentication_option1().GetRule_password_option1(), result)){ + return false; + } - used.insert(ECreateUserOption::Password); + break; + } + case TRule_authentication_option::kAltAuthenticationOption2: { + if (!HashParameter(authenticationOption.GetAlt_authentication_option2().GetRule_hash_option1(), result)){ + return false; + } + + break; + } + case TRule_authentication_option::ALT_NOT_SET: { + Y_ABORT("You should change implementation according to grammar changes"); + } + } break; } - case TRule_create_user_option::kAltCreateUserOption2: + case TRule_user_option::kAltUserOption2: { - if (used.contains(ECreateUserOption::Login)) { + if (used.contains(EUserOption::Login)) { Error() << "Conflicting or redundant options"; return false; } - used.insert(ECreateUserOption::Login); + used.insert(EUserOption::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"); - } + LoginParameter(option.GetAlt_user_option2().GetRule_login_option1(), result.CanLogin); break; } - case TRule_create_user_option::ALT_NOT_SET: + case TRule_user_option::ALT_NOT_SET: { Y_ABORT("You should change implementation according to grammar changes"); } @@ -3884,10 +3933,12 @@ bool TSqlTranslation::RoleParameters(const std::vector<TRule_create_user_option> return true; }; - result = TRoleParameters{}; + if (isCreateUser) { + result.CanLogin = true; + } for (const auto& option : optionsList) { - if (!ParseCreateUserOption(option, result)) { + if (!ParseUserOption(option, result)) { return false; } } diff --git a/yql/essentials/sql/v1/sql_translation.h b/yql/essentials/sql/v1/sql_translation.h index 74b78ae330..a052f1979b 100644 --- a/yql/essentials/sql/v1/sql_translation.h +++ b/yql/essentials/sql/v1/sql_translation.h @@ -242,7 +242,10 @@ 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 std::vector<TRule_create_user_option>& optionsList, TRoleParameters& result); + bool PasswordParameter(const TRule_password_option& passwordOption, TUserParameters& result); + bool HashParameter(const TRule_hash_option& hashOption, TUserParameters& result); + void LoginParameter(const TRule_login_option& loginOption, std::optional<bool>& canLogin); + bool UserParameters(const std::vector<TRule_user_option>& optionsList, TUserParameters& 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 9baf35f8f4..6bdd2c7e44 100644 --- a/yql/essentials/sql/v1/sql_ut.cpp +++ b/yql/essentials/sql/v1/sql_ut.cpp @@ -5017,7 +5017,6 @@ select FormatType($f()); )"); UNIT_ASSERT(reqCreateUser.IsOk()); - UNIT_ASSERT(reqCreateUser.Root); auto reqAlterUser = SqlToYql(R"( USE plato; @@ -5033,7 +5032,6 @@ select FormatType($f()); )"); UNIT_ASSERT(reqPasswordAndLogin.IsOk()); - UNIT_ASSERT(reqPasswordAndLogin.Root); auto reqPasswordAndNoLogin = SqlToYql(R"( USE plato; @@ -5041,7 +5039,6 @@ select FormatType($f()); )"); UNIT_ASSERT(reqPasswordAndNoLogin.IsOk()); - UNIT_ASSERT(reqPasswordAndNoLogin.Root); auto reqLogin = SqlToYql(R"( USE plato; @@ -5049,7 +5046,6 @@ select FormatType($f()); )"); UNIT_ASSERT(reqLogin.IsOk()); - UNIT_ASSERT(reqLogin.Root); auto reqNoLogin = SqlToYql(R"( USE plato; @@ -5057,7 +5053,6 @@ select FormatType($f()); )"); UNIT_ASSERT(reqNoLogin.IsOk()); - UNIT_ASSERT(reqNoLogin.Root); auto reqLoginNoLogin = SqlToYql(R"( USE plato; @@ -5074,7 +5069,6 @@ select FormatType($f()); )"); UNIT_ASSERT(reqAlterLoginNoLogin.IsOk()); - UNIT_ASSERT(reqAlterLoginNoLogin.Root); auto reqAlterLoginNoLoginWithPassword = SqlToYql(R"( USE plato; @@ -5083,7 +5077,56 @@ select FormatType($f()); )"); UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.IsOk()); - UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.Root); + } + + Y_UNIT_TEST(CreateUserWithHash) { + auto reqCreateUser = SqlToYql(R"( + USE plato; + CREATE USER user1 HASH '{ + "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=", + "salt": "U+tzBtgo06EBQCjlARA6Jg==", + "type": "argon2id" + }'; + )"); + + UNIT_ASSERT(reqCreateUser.IsOk()); + + auto reqCreateUserWithNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 HASH '{ + "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=", + "salt": "U+tzBtgo06EBQCjlARA6Jg==", + "type": "argon2id" + }' + NOLOGIN; + )"); + + UNIT_ASSERT(reqCreateUserWithNoLogin.IsOk()); + + auto reqCreateUserWithPassword = SqlToYql(R"( + USE plato; + CREATE USER user1 HASH '{ + "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=", + "salt": "U+tzBtgo06EBQCjlARA6Jg==", + "type": "argon2id" + }' + PASSWORD '123'; + )"); + + UNIT_ASSERT(!reqCreateUserWithPassword.IsOk()); + UNIT_ASSERT_STRING_CONTAINS(reqCreateUserWithPassword.Issues.ToString(), "Error: Conflicting or redundant options"); + + auto reqAlterUser = SqlToYql(R"( + USE plato; + CREATE USER user1; + ALTER USER user1 HASH '{ + "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=", + "salt": "U+tzBtgo06EBQCjlARA6Jg==", + "type": "argon2id" + }'; + )"); + + UNIT_ASSERT(reqAlterUser.IsOk()); } Y_UNIT_TEST(CreateAlterUserWithoutCluster) { diff --git a/yql/essentials/sql/v1/sql_ut_antlr4.cpp b/yql/essentials/sql/v1/sql_ut_antlr4.cpp index 05e85605ef..4b1233bfee 100644 --- a/yql/essentials/sql/v1/sql_ut_antlr4.cpp +++ b/yql/essentials/sql/v1/sql_ut_antlr4.cpp @@ -483,7 +483,6 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { )"); UNIT_ASSERT(reqCreateUser.IsOk()); - UNIT_ASSERT(reqCreateUser.Root); auto reqAlterUser = SqlToYql(R"( USE plato; @@ -491,7 +490,7 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { )"); UNIT_ASSERT(!reqAlterUser.IsOk()); - UNIT_ASSERT_STRING_CONTAINS(reqAlterUser.Issues.ToString(), "Error: mismatched input ';' expecting {ENCRYPTED, LOGIN, NOLOGIN, PASSWORD, RENAME, WITH}"); + UNIT_ASSERT_STRING_CONTAINS(reqAlterUser.Issues.ToString(), "Error: mismatched input ';' expecting {ENCRYPTED, HASH, LOGIN, NOLOGIN, PASSWORD, RENAME, WITH}"); auto reqPasswordAndLogin = SqlToYql(R"( USE plato; @@ -499,7 +498,6 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { )"); UNIT_ASSERT(reqPasswordAndLogin.IsOk()); - UNIT_ASSERT(reqPasswordAndLogin.Root); auto reqPasswordAndNoLogin = SqlToYql(R"( USE plato; @@ -507,7 +505,6 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { )"); UNIT_ASSERT(reqPasswordAndNoLogin.IsOk()); - UNIT_ASSERT(reqPasswordAndNoLogin.Root); auto reqLogin = SqlToYql(R"( USE plato; @@ -515,7 +512,6 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { )"); UNIT_ASSERT(reqLogin.IsOk()); - UNIT_ASSERT(reqLogin.Root); auto reqNoLogin = SqlToYql(R"( USE plato; @@ -523,7 +519,6 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { )"); UNIT_ASSERT(reqNoLogin.IsOk()); - UNIT_ASSERT(reqNoLogin.Root); auto reqLoginNoLogin = SqlToYql(R"( USE plato; @@ -540,7 +535,6 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { )"); UNIT_ASSERT(reqAlterLoginNoLogin.IsOk()); - UNIT_ASSERT(reqAlterLoginNoLogin.Root); auto reqAlterLoginNoLoginWithPassword = SqlToYql(R"( USE plato; @@ -549,7 +543,56 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { )"); UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.IsOk()); - UNIT_ASSERT(reqAlterLoginNoLoginWithPassword.Root); + } + + Y_UNIT_TEST(CreateUserWithHash) { + auto reqCreateUser = SqlToYql(R"( + USE plato; + CREATE USER user1 HASH '{ + "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=", + "salt": "U+tzBtgo06EBQCjlARA6Jg==", + "type": "argon2id" + }'; + )"); + + UNIT_ASSERT(reqCreateUser.IsOk()); + + auto reqCreateUserWithNoLogin = SqlToYql(R"( + USE plato; + CREATE USER user1 HASH '{ + "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=", + "salt": "U+tzBtgo06EBQCjlARA6Jg==", + "type": "argon2id" + }' + NOLOGIN; + )"); + + UNIT_ASSERT(reqCreateUserWithNoLogin.IsOk()); + + auto reqCreateUserWithPassword = SqlToYql(R"( + USE plato; + CREATE USER user1 HASH '{ + "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=", + "salt": "U+tzBtgo06EBQCjlARA6Jg==", + "type": "argon2id" + }' + PASSWORD '123'; + )"); + + UNIT_ASSERT(!reqCreateUserWithPassword.IsOk()); + UNIT_ASSERT_STRING_CONTAINS(reqCreateUserWithPassword.Issues.ToString(), "Error: Conflicting or redundant options"); + + auto reqAlterUser = SqlToYql(R"( + USE plato; + CREATE USER user1; + ALTER USER user1 HASH '{ + "hash": "p4ffeMugohqyBwyckYCK1TjJfz3LIHbKiGL+t+oEhzw=", + "salt": "U+tzBtgo06EBQCjlARA6Jg==", + "type": "argon2id" + }'; + )"); + + UNIT_ASSERT(reqAlterUser.IsOk()); } Y_UNIT_TEST(JoinWithoutConcreteColumns) { |