diff options
author | vvvv <vvvv@yandex-team.com> | 2024-11-07 12:29:36 +0300 |
---|---|---|
committer | vvvv <vvvv@yandex-team.com> | 2024-11-07 13:49:47 +0300 |
commit | d4c258e9431675bab6745c8638df6e3dfd4dca6b (patch) | |
tree | b5efcfa11351152a4c872fccaea35749141c0b11 /yql/essentials/sql/v1/sql_query.cpp | |
parent | 13a4f274caef5cfdaf0263b24e4d6bdd5521472b (diff) | |
download | ydb-d4c258e9431675bab6745c8638df6e3dfd4dca6b.tar.gz |
Moved other yql/essentials libs YQL-19206
init
commit_hash:7d4c435602078407bbf20dd3c32f9c90d2bbcbc0
Diffstat (limited to 'yql/essentials/sql/v1/sql_query.cpp')
-rw-r--r-- | yql/essentials/sql/v1/sql_query.cpp | 3446 |
1 files changed, 3446 insertions, 0 deletions
diff --git a/yql/essentials/sql/v1/sql_query.cpp b/yql/essentials/sql/v1/sql_query.cpp new file mode 100644 index 0000000000..d0fb8737e1 --- /dev/null +++ b/yql/essentials/sql/v1/sql_query.cpp @@ -0,0 +1,3446 @@ +#include "sql_query.h" +#include "sql_expression.h" +#include "sql_select.h" +#include "sql_into_tables.h" +#include "sql_values.h" +#include "node.h" +#include <yql/essentials/parser/proto_ast/gen/v1/SQLv1Lexer.h> +#include <yql/essentials/parser/proto_ast/gen/v1_antlr4/SQLv1Antlr4Lexer.h> +#include <yql/essentials/sql/v1/object_processing.h> +#include <yql/essentials/utils/yql_paths.h> +#include <util/generic/scope.h> +#include <util/string/join.h> +#ifdef GetMessage +#undef GetMessage +#endif + +namespace NSQLTranslationV1 { + +using NALPDefault::SQLv1LexerTokens; +using NALPDefaultAntlr4::SQLv1Antlr4Lexer; + +using namespace NSQLv1Generated; + +void FillTargetList(TTranslation& ctx, const TRule_set_target_list& node, TVector<TString>& targetList) { + targetList.push_back(ColumnNameAsSingleStr(ctx, node.GetRule_set_target2().GetRule_column_name1())); + for (auto& block: node.GetBlock3()) { + targetList.push_back(ColumnNameAsSingleStr(ctx, block.GetRule_set_target2().GetRule_column_name1())); + } +} + +bool PackageVersionFromString(const TString& s, ui32& version) { + if (s == "release") { + version = 0; + return true; + } + if (s == "draft") { + version = 1; + return true; + } + return TryFromString(s, version); +} + +void TSqlQuery::AddStatementToBlocks(TVector<TNodePtr>& blocks, TNodePtr node) { + blocks.emplace_back(node); +} + +static bool AsyncReplicationSettingsEntry(std::map<TString, TNodePtr>& out, + const TRule_replication_settings_entry& in, TTranslation& ctx, bool create) +{ + auto key = IdEx(in.GetRule_an_id1(), ctx); + auto value = BuildLiteralSmartString(ctx.Context(), ctx.Token(in.GetToken3())); + + TSet<TString> configSettings = { + "connection_string", + "endpoint", + "database", + "token", + "token_secret_name", + "user", + "password", + "password_secret_name", + }; + + TSet<TString> stateSettings = { + "state", + "failover_mode", + }; + + const auto keyName = to_lower(key.Name); + if (!configSettings.count(keyName) && !stateSettings.count(keyName)) { + ctx.Context().Error() << "Unknown replication setting: " << key.Name; + return false; + } + + if (create && stateSettings.count(keyName)) { + ctx.Context().Error() << key.Name << " is not supported in CREATE"; + return false; + } + + if (!out.emplace(keyName, value).second) { + ctx.Context().Error() << "Duplicate replication setting: " << key.Name; + } + + return true; +} + +static bool AsyncReplicationSettings(std::map<TString, TNodePtr>& out, + const TRule_replication_settings& in, TTranslation& ctx, bool create) +{ + if (!AsyncReplicationSettingsEntry(out, in.GetRule_replication_settings_entry1(), ctx, create)) { + return false; + } + + for (auto& block : in.GetBlock2()) { + if (!AsyncReplicationSettingsEntry(out, block.GetRule_replication_settings_entry2(), ctx, create)) { + return false; + } + } + + return true; +} + +static bool AsyncReplicationTarget(std::vector<std::pair<TString, TString>>& out, TStringBuf prefixPath, + const TRule_replication_target& in, TTranslation& ctx) +{ + const TString remote = Id(in.GetRule_object_ref1().GetRule_id_or_at2(), ctx).second; + const TString local = Id(in.GetRule_object_ref3().GetRule_id_or_at2(), ctx).second; + out.emplace_back(remote, BuildTablePath(prefixPath, local)); + return true; +} + +static bool AsyncReplicationAlterAction(std::map<TString, TNodePtr>& settings, + const TRule_alter_replication_action& in, TTranslation& ctx) +{ + // TODO(ilnaz): support other actions + return AsyncReplicationSettings(settings, in.GetRule_alter_replication_set_setting1().GetRule_replication_settings3(), ctx, false); +} + +bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& core) { + TString internalStatementName; + TString humanStatementName; + ParseStatementName(core, internalStatementName, humanStatementName); + const auto& altCase = core.Alt_case(); + if (Mode == NSQLTranslation::ESqlMode::LIMITED_VIEW && (altCase >= TRule_sql_stmt_core::kAltSqlStmtCore4 && + altCase != TRule_sql_stmt_core::kAltSqlStmtCore13)) { + Error() << humanStatementName << " statement is not supported in limited views"; + return false; + } + + if (Mode == NSQLTranslation::ESqlMode::SUBQUERY && (altCase >= TRule_sql_stmt_core::kAltSqlStmtCore4 && + altCase != TRule_sql_stmt_core::kAltSqlStmtCore13 && altCase != TRule_sql_stmt_core::kAltSqlStmtCore6 && + altCase != TRule_sql_stmt_core::kAltSqlStmtCore18)) { + Error() << humanStatementName << " statement is not supported in subqueries"; + return false; + } + + switch (altCase) { + case TRule_sql_stmt_core::kAltSqlStmtCore1: { + bool success = false; + TNodePtr nodeExpr = PragmaStatement(core.GetAlt_sql_stmt_core1().GetRule_pragma_stmt1(), success); + if (!success) { + return false; + } + if (nodeExpr) { + AddStatementToBlocks(blocks, nodeExpr); + } + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore2: { + if (Ctx.ParallelModeCount > 0) { + Error() << humanStatementName << " statement is not supported in parallel mode"; + return false; + } + + Ctx.BodyPart(); + TSqlSelect select(Ctx, Mode); + TPosition pos; + auto source = select.Build(core.GetAlt_sql_stmt_core2().GetRule_select_stmt1(), pos); + if (!source) { + return false; + } + blocks.emplace_back(BuildSelectResult(pos, std::move(source), + Mode != NSQLTranslation::ESqlMode::LIMITED_VIEW && Mode != NSQLTranslation::ESqlMode::SUBQUERY, Mode == NSQLTranslation::ESqlMode::SUBQUERY, + Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore3: { + Ctx.BodyPart(); + TVector<TSymbolNameWithPos> names; + auto nodeExpr = NamedNode(core.GetAlt_sql_stmt_core3().GetRule_named_nodes_stmt1(), names); + if (!nodeExpr) { + return false; + } + TVector<TNodePtr> nodes; + auto subquery = nodeExpr->GetSource(); + if (subquery && Mode == NSQLTranslation::ESqlMode::LIBRARY && Ctx.ScopeLevel == 0) { + for (size_t i = 0; i < names.size(); ++i) { + nodes.push_back(BuildInvalidSubqueryRef(subquery->GetPos())); + } + } else if (subquery) { + const auto alias = Ctx.MakeName("subquerynode"); + const auto ref = Ctx.MakeName("subquery"); + blocks.push_back(BuildSubquery(subquery, alias, + Mode == NSQLTranslation::ESqlMode::SUBQUERY, names.size() == 1 ? -1 : names.size(), Ctx.Scoped)); + blocks.back()->SetLabel(ref); + + for (size_t i = 0; i < names.size(); ++i) { + nodes.push_back(BuildSubqueryRef(blocks.back(), ref, names.size() == 1 ? -1 : i)); + } + } else if (!Ctx.CompactNamedExprs || nodeExpr->GetUdfNode()) { + // Unlike other nodes, TUdfNode is not an independent node, but more like a set of parameters which should be + // applied on UDF call site. For example, TUdfNode can not be Translate()d + // So we can't add it to blocks and use reference, instead we store the TUdfNode itself as named node + // TODO: remove this special case + if (names.size() > 1) { + auto tupleRes = BuildTupleResult(nodeExpr, names.size()); + for (size_t i = 0; i < names.size(); ++i) { + nodes.push_back(nodeExpr->Y("Nth", tupleRes, nodeExpr->Q(ToString(i)))); + } + } else { + nodes.push_back(std::move(nodeExpr)); + } + } else { + const auto ref = Ctx.MakeName("namedexprnode"); + blocks.push_back(BuildNamedExpr(names.size() > 1 ? BuildTupleResult(nodeExpr, names.size()) : nodeExpr)); + blocks.back()->SetLabel(ref); + for (size_t i = 0; i < names.size(); ++i) { + nodes.push_back(BuildNamedExprReference(blocks.back(), ref, names.size() == 1 ? TMaybe<size_t>() : i)); + } + } + + for (size_t i = 0; i < names.size(); ++i) { + PushNamedNode(names[i].Pos, names[i].Name, nodes[i]); + } + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore4: { + Ctx.BodyPart(); + const auto& rule = core.GetAlt_sql_stmt_core4().GetRule_create_table_stmt1(); + + bool replaceIfExists = false; + if (rule.HasBlock2()) { // OR REPLACE + replaceIfExists = true; + Y_DEBUG_ABORT_UNLESS( + (IS_TOKEN(rule.GetBlock2().GetToken1().GetId(), OR) && + IS_TOKEN(rule.GetBlock2().GetToken2().GetId(), REPLACE)) + ); + } + + const bool isCreateTableAs = rule.HasBlock15(); + const auto& block = rule.GetBlock3(); + ETableType tableType = ETableType::Table; + bool temporary = false; + if (block.HasAlt2() && + IS_TOKEN(block.GetAlt2().GetToken1().GetId(), TABLESTORE) + ) { + tableType = ETableType::TableStore; + if (isCreateTableAs) { + Context().Error(GetPos(block.GetAlt2().GetToken1())) + << "CREATE TABLE AS is not supported for TABLESTORE"; + return false; + } + } else if (block.HasAlt3() && + IS_TOKEN(block.GetAlt3().GetToken1().GetId(), EXTERNAL) + ) { + tableType = ETableType::ExternalTable; + if (isCreateTableAs) { + Context().Error(GetPos(block.GetAlt3().GetToken1())) + << "CREATE TABLE AS is not supported for EXTERNAL TABLE"; + return false; + } + } else if (block.HasAlt4() && IS_TOKEN(block.GetAlt4().GetToken1().GetId(), TEMP) || + block.HasAlt5() && IS_TOKEN(block.GetAlt5().GetToken1().GetId(), TEMPORARY)) { + temporary = true; + } + + bool existingOk = false; + if (rule.HasBlock4()) { // IF NOT EXISTS + existingOk = true; + Y_DEBUG_ABORT_UNLESS( + IS_TOKEN(rule.GetBlock4().GetToken1().GetId(), IF) && + IS_TOKEN(rule.GetBlock4().GetToken2().GetId(), NOT) && + IS_TOKEN(rule.GetBlock4().GetToken3().GetId(), EXISTS) + ); + } + + if (replaceIfExists && tableType != ETableType::ExternalTable) { + Context().Error(GetPos(rule.GetBlock2().GetToken1())) + << "OR REPLACE feature is supported only for EXTERNAL DATA SOURCE and EXTERNAL TABLE"; + return false; + } + + TTableRef tr; + if (!SimpleTableRefImpl(rule.GetRule_simple_table_ref5(), tr)) { + return false; + } + + TCreateTableParameters params{.TableType=tableType, .Temporary=temporary}; + if (!CreateTableEntry(rule.GetRule_create_table_entry7(), params, isCreateTableAs)) { + return false; + } + for (auto& block: rule.GetBlock8()) { + if (!CreateTableEntry(block.GetRule_create_table_entry2(), params, isCreateTableAs)) { + return false; + } + } + + if (rule.HasBlock11()) { + Context().Error(GetPos(rule.GetBlock11().GetRule_table_inherits1().GetToken1())) + << "INHERITS clause is not supported yet"; + return false; + } + + if (rule.HasBlock12()) { + if (tableType == ETableType::TableStore) { + Context().Error(GetPos(rule.GetBlock12().GetRule_table_partition_by1().GetToken1())) + << "PARTITION BY is not supported for TABLESTORE"; + return false; + } + const auto list = rule.GetBlock12().GetRule_table_partition_by1().GetRule_pure_column_list4(); + params.PartitionByColumns.push_back(IdEx(list.GetRule_an_id2(), *this)); + for (auto& node : list.GetBlock3()) { + params.PartitionByColumns.push_back(IdEx(node.GetRule_an_id2(), *this)); + } + } + + if (rule.HasBlock13()) { + if (!CreateTableSettings(rule.GetBlock13().GetRule_with_table_settings1(), params)) { + return false; + } + } + + if (rule.HasBlock14()) { + Context().Error(GetPos(rule.GetBlock14().GetRule_table_tablestore1().GetToken1())) + << "TABLESTORE clause is not supported yet"; + return false; + } + + TSourcePtr tableSource = nullptr; + if (isCreateTableAs) { + tableSource = TSqlAsValues(Ctx, Mode).Build(rule.GetBlock15().GetRule_table_as_source1().GetRule_values_source2(), "CreateTableAs"); + if (!tableSource) { + return false; + } + } + + if (!ValidateExternalTable(params)) { + return false; + } + + AddStatementToBlocks(blocks, BuildCreateTable(Ctx.Pos(), tr, existingOk, replaceIfExists, params, std::move(tableSource), Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore5: { + Ctx.BodyPart(); + const auto& rule = core.GetAlt_sql_stmt_core5().GetRule_drop_table_stmt1(); + const auto& block = rule.GetBlock2(); + ETableType tableType = ETableType::Table; + if (block.HasAlt2()) { + tableType = ETableType::TableStore; + } + if (block.HasAlt3()) { + tableType = ETableType::ExternalTable; + } + + bool missingOk = false; + if (rule.HasBlock3()) { // IF EXISTS + missingOk = true; + Y_DEBUG_ABORT_UNLESS( + IS_TOKEN(rule.GetBlock3().GetToken1().GetId(), IF) && + IS_TOKEN(rule.GetBlock3().GetToken2().GetId(), EXISTS) + ); + } + + TTableRef tr; + if (!SimpleTableRefImpl(rule.GetRule_simple_table_ref4(), tr)) { + return false; + } + + AddStatementToBlocks(blocks, BuildDropTable(Ctx.Pos(), tr, missingOk, tableType, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore6: { + const auto& rule = core.GetAlt_sql_stmt_core6().GetRule_use_stmt1(); + Token(rule.GetToken1()); + if (!ClusterExpr(rule.GetRule_cluster_expr2(), true, Ctx.Scoped->CurrService, Ctx.Scoped->CurrCluster)) { + return false; + } + + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore7: { + Ctx.BodyPart(); + TSqlIntoTable intoTable(Ctx, Mode); + TNodePtr block(intoTable.Build(core.GetAlt_sql_stmt_core7().GetRule_into_table_stmt1())); + if (!block) { + return false; + } + blocks.emplace_back(block); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore8: { + if (Ctx.ParallelModeCount > 0) { + Error() << humanStatementName << " statement is not supported in parallel mode"; + return false; + } + + Ctx.BodyPart(); + const auto& rule = core.GetAlt_sql_stmt_core8().GetRule_commit_stmt1(); + Token(rule.GetToken1()); + blocks.emplace_back(BuildCommitClusters(Ctx.Pos())); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore9: { + Ctx.BodyPart(); + auto updateNode = Build(core.GetAlt_sql_stmt_core9().GetRule_update_stmt1()); + if (!updateNode) { + return false; + } + AddStatementToBlocks(blocks, updateNode); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore10: { + Ctx.BodyPart(); + auto deleteNode = Build(core.GetAlt_sql_stmt_core10().GetRule_delete_stmt1()); + if (!deleteNode) { + return false; + } + blocks.emplace_back(deleteNode); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore11: { + if (Ctx.ParallelModeCount > 0) { + Error() << humanStatementName << " statement is not supported in parallel mode"; + return false; + } + + Ctx.BodyPart(); + const auto& rule = core.GetAlt_sql_stmt_core11().GetRule_rollback_stmt1(); + Token(rule.GetToken1()); + blocks.emplace_back(BuildRollbackClusters(Ctx.Pos())); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore12: + if (!DeclareStatement(core.GetAlt_sql_stmt_core12().GetRule_declare_stmt1())) { + return false; + } + break; + case TRule_sql_stmt_core::kAltSqlStmtCore13: + if (!ImportStatement(core.GetAlt_sql_stmt_core13().GetRule_import_stmt1())) { + return false; + } + break; + case TRule_sql_stmt_core::kAltSqlStmtCore14: + if (!ExportStatement(core.GetAlt_sql_stmt_core14().GetRule_export_stmt1())) { + return false; + } + break; + case TRule_sql_stmt_core::kAltSqlStmtCore15: { + Ctx.BodyPart(); + const auto& rule = core.GetAlt_sql_stmt_core15().GetRule_alter_table_stmt1(); + const bool isTablestore = IS_TOKEN(rule.GetToken2().GetId(), TABLESTORE); + TTableRef tr; + if (!SimpleTableRefImpl(rule.GetRule_simple_table_ref3(), tr)) { + return false; + } + + TAlterTableParameters params; + if (isTablestore) { + params.TableType = ETableType::TableStore; + } + if (!AlterTableAction(rule.GetRule_alter_table_action4(), params)) { + return false; + } + + for (auto& block : rule.GetBlock5()) { + if (!AlterTableAction(block.GetRule_alter_table_action2(), params)) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildAlterTable(Ctx.Pos(), tr, params, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore16: { + // alter_external_table_stmt: ALTER EXTERNAL TABLE simple_table_ref alter_external_table_action (COMMA alter_external_table_action)* + Ctx.BodyPart(); + const auto& rule = core.GetAlt_sql_stmt_core16().GetRule_alter_external_table_stmt1(); + TTableRef tr; + if (!SimpleTableRefImpl(rule.GetRule_simple_table_ref4(), tr)) { + return false; + } + + TAlterTableParameters params; + params.TableType = ETableType::ExternalTable; + if (!AlterExternalTableAction(rule.GetRule_alter_external_table_action5(), params)) { + return false; + } + + for (auto& block : rule.GetBlock6()) { + if (!AlterExternalTableAction(block.GetRule_alter_external_table_action2(), params)) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildAlterTable(Ctx.Pos(), tr, params, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore17: { + Ctx.BodyPart(); + auto node = DoStatement(core.GetAlt_sql_stmt_core17().GetRule_do_stmt1(), false); + if (!node) { + return false; + } + + blocks.push_back(node); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore18: { + Ctx.BodyPart(); + TNodePtr lambda; + TSymbolNameWithPos nameAndPos; + const auto& stmt = core.GetAlt_sql_stmt_core18().GetRule_define_action_or_subquery_stmt1(); + const TString kind = to_lower(Ctx.Token(stmt.GetToken2())); + YQL_ENSURE(kind == "action" || kind == "subquery"); + if (!DefineActionOrSubqueryStatement(stmt, nameAndPos, lambda)) { + return false; + } + + if (Ctx.CompactNamedExprs) { + const auto ref = Ctx.MakeName("named" + kind + "node"); + blocks.push_back(BuildNamedExpr(lambda)); + blocks.back()->SetLabel(ref); + lambda = BuildNamedExprReference(blocks.back(), ref, {}); + } + + PushNamedNode(nameAndPos.Pos, nameAndPos.Name, lambda); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore19: { + Ctx.BodyPart(); + auto node = IfStatement(core.GetAlt_sql_stmt_core19().GetRule_if_stmt1()); + if (!node) { + return false; + } + + blocks.push_back(node); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore20: { + Ctx.BodyPart(); + auto node = ForStatement(core.GetAlt_sql_stmt_core20().GetRule_for_stmt1()); + if (!node) { + return false; + } + + blocks.push_back(node); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore21: { + if (Ctx.ParallelModeCount > 0) { + Error() << humanStatementName << " statement is not supported in parallel mode"; + return false; + } + + Ctx.BodyPart(); + TSqlValues values(Ctx, Mode); + TPosition pos; + auto source = values.Build(core.GetAlt_sql_stmt_core21().GetRule_values_stmt1(), pos, {}, TPosition()); + if (!source) { + return false; + } + blocks.emplace_back(BuildSelectResult(pos, std::move(source), + Mode != NSQLTranslation::ESqlMode::LIMITED_VIEW && Mode != NSQLTranslation::ESqlMode::SUBQUERY, Mode == NSQLTranslation::ESqlMode::SUBQUERY, + Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore22: { + // create_user_stmt: CREATE USER role_name create_user_option?; + Ctx.BodyPart(); + auto& node = core.GetAlt_sql_stmt_core22().GetRule_create_user_stmt1(); + + Ctx.Token(node.GetToken1()); + const TPosition pos = Ctx.Pos(); + + TString service = Ctx.Scoped->CurrService; + TDeferredAtom cluster = Ctx.Scoped->CurrCluster; + if (cluster.Empty()) { + Error() << "USE statement is missing - no default cluster is selected"; + return false; + } + + TDeferredAtom roleName; + bool allowSystemRoles = false; + if (!RoleNameClause(node.GetRule_role_name3(), roleName, allowSystemRoles)) { + return false; + } + + TMaybe<TRoleParameters> roleParams; + if (node.HasBlock4()) { + roleParams.ConstructInPlace(); + if (!RoleParameters(node.GetBlock4().GetRule_create_user_option1(), *roleParams)) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildCreateUser(pos, service, cluster, roleName, roleParams, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore23: { + // 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(); + + Ctx.Token(node.GetToken1()); + const TPosition pos = Ctx.Pos(); + + TString service = Ctx.Scoped->CurrService; + TDeferredAtom cluster = Ctx.Scoped->CurrCluster; + if (cluster.Empty()) { + Error() << "USE statement is missing - no default cluster is selected"; + return false; + } + + TDeferredAtom roleName; + { + bool allowSystemRoles = true; + if (!RoleNameClause(node.GetRule_role_name3(), roleName, allowSystemRoles)) { + return false; + } + } + + TNodePtr stmt; + switch (node.GetBlock4().Alt_case()) { + case TRule_alter_user_stmt_TBlock4::kAlt1: { + TRoleParameters roleParams; + if (!RoleParameters(node.GetBlock4().GetAlt1().GetRule_create_user_option2(), roleParams)) { + return false; + } + stmt = BuildAlterUser(pos, service, cluster, roleName, roleParams, Ctx.Scoped); + break; + } + case TRule_alter_user_stmt_TBlock4::kAlt2: { + TDeferredAtom tgtRoleName; + bool allowSystemRoles = false; + if (!RoleNameClause(node.GetBlock4().GetAlt2().GetRule_role_name3(), tgtRoleName, allowSystemRoles)) { + return false; + } + stmt = BuildRenameUser(pos, service, cluster, roleName, tgtRoleName,Ctx.Scoped); + break; + } + case TRule_alter_user_stmt_TBlock4::ALT_NOT_SET: + Y_ABORT("You should change implementation according to grammar changes"); + } + + AddStatementToBlocks(blocks, stmt); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore24: { + // create_group_stmt: CREATE GROUP role_name (WITH USER role_name (COMMA role_name)* COMMA?)?; + Ctx.BodyPart(); + auto& node = core.GetAlt_sql_stmt_core24().GetRule_create_group_stmt1(); + + Ctx.Token(node.GetToken1()); + const TPosition pos = Ctx.Pos(); + + TString service = Ctx.Scoped->CurrService; + TDeferredAtom cluster = Ctx.Scoped->CurrCluster; + if (cluster.Empty()) { + Error() << "USE statement is missing - no default cluster is selected"; + return false; + } + + TDeferredAtom roleName; + bool allowSystemRoles = false; + if (!RoleNameClause(node.GetRule_role_name3(), roleName, allowSystemRoles)) { + return false; + } + + TRoleParameters roleParams; + 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)) { + return false; + } + + for (auto& item : addDropNode.GetBlock4()) { + roleParams.Roles.emplace_back(); + if (!RoleNameClause(item.GetRule_role_name2(), roleParams.Roles.back(), allowSystemRoles)) { + return false; + } + } + } + + AddStatementToBlocks(blocks, BuildCreateGroup(pos, service, cluster, roleName, roleParams, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore25: { + // alter_group_stmt: ALTER GROUP role_name ((ADD|DROP) USER role_name (COMMA role_name)* COMMA? | RENAME TO role_name); + Ctx.BodyPart(); + auto& node = core.GetAlt_sql_stmt_core25().GetRule_alter_group_stmt1(); + + Ctx.Token(node.GetToken1()); + const TPosition pos = Ctx.Pos(); + + TString service = Ctx.Scoped->CurrService; + TDeferredAtom cluster = Ctx.Scoped->CurrCluster; + if (cluster.Empty()) { + Error() << "USE statement is missing - no default cluster is selected"; + return false; + } + + TDeferredAtom roleName; + { + bool allowSystemRoles = true; + if (!RoleNameClause(node.GetRule_role_name3(), roleName, allowSystemRoles)) { + return false; + } + } + + TNodePtr stmt; + switch (node.GetBlock4().Alt_case()) { + case TRule_alter_group_stmt_TBlock4::kAlt1: { + auto& addDropNode = node.GetBlock4().GetAlt1(); + const bool isDrop = IS_TOKEN(addDropNode.GetToken1().GetId(), DROP); + TVector<TDeferredAtom> roles; + bool allowSystemRoles = false; + roles.emplace_back(); + if (!RoleNameClause(addDropNode.GetRule_role_name3(), roles.back(), allowSystemRoles)) { + return false; + } + + for (auto& item : addDropNode.GetBlock4()) { + roles.emplace_back(); + if (!RoleNameClause(item.GetRule_role_name2(), roles.back(), allowSystemRoles)) { + return false; + } + } + + stmt = BuildAlterGroup(pos, service, cluster, roleName, roles, isDrop, Ctx.Scoped); + break; + } + case TRule_alter_group_stmt_TBlock4::kAlt2: { + TDeferredAtom tgtRoleName; + bool allowSystemRoles = false; + if (!RoleNameClause(node.GetBlock4().GetAlt2().GetRule_role_name3(), tgtRoleName, allowSystemRoles)) { + return false; + } + stmt = BuildRenameGroup(pos, service, cluster, roleName, tgtRoleName, Ctx.Scoped); + break; + } + case TRule_alter_group_stmt_TBlock4::ALT_NOT_SET: + Y_ABORT("You should change implementation according to grammar changes"); + } + + AddStatementToBlocks(blocks, stmt); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore26: { + // drop_role_stmt: DROP (USER|GROUP) (IF EXISTS)? role_name (COMMA role_name)* COMMA?; + Ctx.BodyPart(); + auto& node = core.GetAlt_sql_stmt_core26().GetRule_drop_role_stmt1(); + + Ctx.Token(node.GetToken1()); + const TPosition pos = Ctx.Pos(); + + TString service = Ctx.Scoped->CurrService; + TDeferredAtom cluster = Ctx.Scoped->CurrCluster; + if (cluster.Empty()) { + Error() << "USE statement is missing - no default cluster is selected"; + return false; + } + + const bool isUser = IS_TOKEN(node.GetToken2().GetId(), USER); + bool missingOk = false; + if (node.HasBlock3()) { // IF EXISTS + missingOk = true; + Y_DEBUG_ABORT_UNLESS( + IS_TOKEN(node.GetBlock3().GetToken1().GetId(), IF) && + IS_TOKEN(node.GetBlock3().GetToken2().GetId(), EXISTS) + ); + } + + TVector<TDeferredAtom> roles; + bool allowSystemRoles = true; + roles.emplace_back(); + if (!RoleNameClause(node.GetRule_role_name4(), roles.back(), allowSystemRoles)) { + return false; + } + + for (auto& item : node.GetBlock5()) { + roles.emplace_back(); + if (!RoleNameClause(item.GetRule_role_name2(), roles.back(), allowSystemRoles)) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildDropRoles(pos, service, cluster, roles, isUser, missingOk, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore27: { + // create_object_stmt: CREATE OBJECT (IF NOT EXISTS)? name (TYPE type [WITH k=v,...]); + auto& node = core.GetAlt_sql_stmt_core27().GetRule_create_object_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref4().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + bool existingOk = false; + if (node.HasBlock3()) { // IF NOT EXISTS + existingOk = true; + Y_DEBUG_ABORT_UNLESS( + IS_TOKEN(node.GetBlock3().GetToken1().GetId(), IF) && + IS_TOKEN(node.GetBlock3().GetToken2().GetId(), NOT) && + IS_TOKEN(node.GetBlock3().GetToken3().GetId(), EXISTS) + ); + } + + const TString& objectId = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + const TString& typeId = Id(node.GetRule_object_type_ref7().GetRule_an_id_or_type1(), *this); + std::map<TString, TDeferredAtom> kv; + if (node.HasBlock9()) { + if (!ParseObjectFeatures(kv, node.GetBlock9().GetRule_create_object_features1().GetRule_object_features2())) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), objectId, typeId, existingOk, false, std::move(kv), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore28: { + // alter_object_stmt: ALTER OBJECT name (TYPE type [SET k=v,...]); + auto& node = core.GetAlt_sql_stmt_core28().GetRule_alter_object_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref3().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref3().GetRule_id_or_at2(), *this).second; + const TString& typeId = Id(node.GetRule_object_type_ref6().GetRule_an_id_or_type1(), *this); + std::map<TString, TDeferredAtom> kv; + if (!ParseObjectFeatures(kv, node.GetRule_alter_object_features8().GetRule_object_features2())) { + return false; + } + + AddStatementToBlocks(blocks, BuildAlterObjectOperation(Ctx.Pos(), objectId, typeId, std::move(kv), std::set<TString>(), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore29: { + // drop_object_stmt: DROP OBJECT (IF EXISTS)? name (TYPE type [WITH k=v,...]); + auto& node = core.GetAlt_sql_stmt_core29().GetRule_drop_object_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref4().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + bool missingOk = false; + if (node.HasBlock3()) { // IF EXISTS + missingOk = true; + Y_DEBUG_ABORT_UNLESS( + IS_TOKEN(node.GetBlock3().GetToken1().GetId(), IF) && + IS_TOKEN(node.GetBlock3().GetToken2().GetId(), EXISTS) + ); + } + + const TString& objectId = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + const TString& typeId = Id(node.GetRule_object_type_ref7().GetRule_an_id_or_type1(), *this); + std::map<TString, TDeferredAtom> kv; + if (node.HasBlock9()) { + if (!ParseObjectFeatures(kv, node.GetBlock9().GetRule_drop_object_features1().GetRule_object_features2())) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), objectId, typeId, missingOk, std::move(kv), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore30: { + // create_external_data_source_stmt: CREATE (OR REPLACE)? EXTERNAL DATA SOURCE (IF NOT EXISTS)? name WITH (k=v,...); + auto& node = core.GetAlt_sql_stmt_core30().GetRule_create_external_data_source_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref7().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref7().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + bool replaceIfExists = false; + if (node.HasBlock2()) { // OR REPLACE + replaceIfExists = true; + Y_DEBUG_ABORT_UNLESS( + IS_TOKEN(node.GetBlock2().GetToken1().GetId(), OR) && + IS_TOKEN(node.GetBlock2().GetToken2().GetId(), REPLACE) + ); + } + + bool existingOk = false; + if (node.HasBlock6()) { // IF NOT EXISTS + existingOk = true; + Y_DEBUG_ABORT_UNLESS( + IS_TOKEN(node.GetBlock6().GetToken1().GetId(), IF) && + IS_TOKEN(node.GetBlock6().GetToken2().GetId(), NOT) && + IS_TOKEN(node.GetBlock6().GetToken3().GetId(), EXISTS) + ); + } + + const TString& objectId = Id(node.GetRule_object_ref7().GetRule_id_or_at2(), *this).second; + std::map<TString, TDeferredAtom> kv; + if (!ParseExternalDataSourceSettings(kv, node.GetRule_with_table_settings8())) { + return false; + } + + AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), "EXTERNAL_DATA_SOURCE", existingOk, replaceIfExists, std::move(kv), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore31: { + // alter_external_data_source_stmt: ALTER EXTERNAL DATA SOURCE object_ref alter_external_data_source_action (COMMA alter_external_data_source_action)* + Ctx.BodyPart(); + const auto& node = core.GetAlt_sql_stmt_core31().GetRule_alter_external_data_source_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref5().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second; + std::map<TString, TDeferredAtom> kv; + std::set<TString> toReset; + if (!ParseExternalDataSourceSettings(kv, toReset, node.GetRule_alter_external_data_source_action6())) { + return false; + } + + for (const auto& action : node.GetBlock7()) { + if (!ParseExternalDataSourceSettings(kv, toReset, action.GetRule_alter_external_data_source_action2())) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildAlterObjectOperation(Ctx.Pos(), objectId, "EXTERNAL_DATA_SOURCE", std::move(kv), std::move(toReset), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore32: { + // drop_external_data_source_stmt: DROP EXTERNAL DATA SOURCE (IF EXISTS)? name; + auto& node = core.GetAlt_sql_stmt_core32().GetRule_drop_external_data_source_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref6().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref6().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + bool missingOk = false; + if (node.HasBlock5()) { // IF EXISTS + missingOk = true; + Y_DEBUG_ABORT_UNLESS( + IS_TOKEN(node.GetBlock5().GetToken1().GetId(), IF) && + IS_TOKEN(node.GetBlock5().GetToken2().GetId(), EXISTS) + ); + } + + const TString& objectId = Id(node.GetRule_object_ref6().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), "EXTERNAL_DATA_SOURCE", missingOk, {}, context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore33: { + // create_replication_stmt: CREATE ASYNC REPLICATION + auto& node = core.GetAlt_sql_stmt_core33().GetRule_create_replication_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref4().HasBlock1()) { + const auto& cluster = node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(); + if (!ClusterExpr(cluster, false, context.ServiceId, context.Cluster)) { + return false; + } + } + + auto prefixPath = Ctx.GetPrefixPath(context.ServiceId, context.Cluster); + + std::vector<std::pair<TString, TString>> targets; + if (!AsyncReplicationTarget(targets, prefixPath, node.GetRule_replication_target6(), *this)) { + return false; + } + for (auto& block : node.GetBlock7()) { + if (!AsyncReplicationTarget(targets, prefixPath, block.GetRule_replication_target2(), *this)) { + return false; + } + } + + std::map<TString, TNodePtr> settings; + if (!AsyncReplicationSettings(settings, node.GetRule_replication_settings10(), *this, true)) { + return false; + } + + const TString id = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, BuildCreateAsyncReplication(Ctx.Pos(), BuildTablePath(prefixPath, id), + std::move(targets), std::move(settings), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore34: { + // drop_replication_stmt: DROP ASYNC REPLICATION + auto& node = core.GetAlt_sql_stmt_core34().GetRule_drop_replication_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref4().HasBlock1()) { + const auto& cluster = node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(); + if (!ClusterExpr(cluster, false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString id = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, BuildDropAsyncReplication(Ctx.Pos(), + BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), id), + node.HasBlock5(), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore35: { + Ctx.BodyPart(); + // create_topic_stmt: CREATE TOPIC (IF NOT EXISTS)? topic1 (CONSUMER ...)? [WITH (opt1 = val1, ...]? + auto& rule = core.GetAlt_sql_stmt_core35().GetRule_create_topic_stmt1(); + TTopicRef tr; + if (!TopicRefImpl(rule.GetRule_topic_ref4(), tr)) { + return false; + } + bool existingOk = false; + if (rule.HasBlock3()) { // if not exists + existingOk = true; + } + + TCreateTopicParameters params; + params.ExistingOk = existingOk; + if (rule.HasBlock5()) { //create_topic_entry (consumers) + auto& entries = rule.GetBlock5().GetRule_create_topic_entries1(); + auto& firstEntry = entries.GetRule_create_topic_entry2(); + if (!CreateTopicEntry(firstEntry, params)) { + return false; + } + const auto& list = entries.GetBlock3(); + for (auto& node : list) { + if (!CreateTopicEntry(node.GetRule_create_topic_entry2(), params)) { + return false; + } + } + + } + if (rule.HasBlock6()) { // with_topic_settings + auto& topic_settings_node = rule.GetBlock6().GetRule_with_topic_settings1().GetRule_topic_settings3(); + CreateTopicSettings(topic_settings_node, params.TopicSettings); + } + + AddStatementToBlocks(blocks, BuildCreateTopic(Ctx.Pos(), tr, params, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore36: { +// alter_topic_stmt: ALTER TOPIC topic_ref alter_topic_action (COMMA alter_topic_action)*; +// alter_topic_stmt: ALTER TOPIC IF EXISTS topic_ref alter_topic_action (COMMA alter_topic_action)*; + + Ctx.BodyPart(); + auto& rule = core.GetAlt_sql_stmt_core36().GetRule_alter_topic_stmt1(); + TTopicRef tr; + bool missingOk = false; + if (rule.HasBlock3()) { // IF EXISTS + missingOk = true; + } + if (!TopicRefImpl(rule.GetRule_topic_ref4(), tr)) { + return false; + } + + TAlterTopicParameters params; + params.MissingOk = missingOk; + auto& firstEntry = rule.GetRule_alter_topic_action5(); + if (!AlterTopicAction(firstEntry, params)) { + return false; + } + const auto& list = rule.GetBlock6(); + for (auto& node : list) { + if (!AlterTopicAction(node.GetRule_alter_topic_action2(), params)) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildAlterTopic(Ctx.Pos(), tr, params, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore37: { + // drop_topic_stmt: DROP TOPIC (IF EXISTS)? topic_ref; + Ctx.BodyPart(); + const auto& rule = core.GetAlt_sql_stmt_core37().GetRule_drop_topic_stmt1(); + + TDropTopicParameters params; + if (rule.HasBlock3()) { // IF EXISTS + params.MissingOk = true; + } else { + params.MissingOk = false; + } + + TTopicRef tr; + if (!TopicRefImpl(rule.GetRule_topic_ref4(), tr)) { + return false; + } + AddStatementToBlocks(blocks, BuildDropTopic(Ctx.Pos(), tr, params, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore38: { + // GRANT permission_name_target ON an_id_schema (COMMA an_id_schema)* TO role_name (COMMA role_name)* COMMA? (WITH GRANT OPTION)?; + Ctx.BodyPart(); + auto& node = core.GetAlt_sql_stmt_core38().GetRule_grant_permissions_stmt1(); + + Ctx.Token(node.GetToken1()); + const TPosition pos = Ctx.Pos(); + + TString service = Ctx.Scoped->CurrService; + TDeferredAtom cluster = Ctx.Scoped->CurrCluster; + if (cluster.Empty()) { + Error() << "USE statement is missing - no default cluster is selected"; + return false; + } + + TVector<TDeferredAtom> permissions; + if (!PermissionNameClause(node.GetRule_permission_name_target2(), permissions, node.has_block10())) { + return false; + } + + TVector<TDeferredAtom> schemaPaths; + schemaPaths.emplace_back(Ctx.Pos(), Id(node.GetRule_an_id_schema4(), *this)); + for (const auto& item : node.GetBlock5()) { + schemaPaths.emplace_back(Ctx.Pos(), Id(item.GetRule_an_id_schema2(), *this)); + } + + TVector<TDeferredAtom> roleNames; + const bool allowSystemRoles = false; + roleNames.emplace_back(); + if (!RoleNameClause(node.GetRule_role_name7(), roleNames.back(), allowSystemRoles)) { + return false; + } + for (const auto& item : node.GetBlock8()) { + roleNames.emplace_back(); + if (!RoleNameClause(item.GetRule_role_name2(), roleNames.back(), allowSystemRoles)) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildGrantPermissions(pos, service, cluster, permissions, schemaPaths, roleNames, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore39: + { + // REVOKE (GRANT OPTION FOR)? permission_name_target ON an_id_schema (COMMA an_id_schema)* FROM role_name (COMMA role_name)*; + Ctx.BodyPart(); + auto& node = core.GetAlt_sql_stmt_core39().GetRule_revoke_permissions_stmt1(); + + Ctx.Token(node.GetToken1()); + const TPosition pos = Ctx.Pos(); + + TString service = Ctx.Scoped->CurrService; + TDeferredAtom cluster = Ctx.Scoped->CurrCluster; + if (cluster.Empty()) { + Error() << "USE statement is missing - no default cluster is selected"; + return false; + } + + TVector<TDeferredAtom> permissions; + if (!PermissionNameClause(node.GetRule_permission_name_target3(), permissions, node.HasBlock2())) { + return false; + } + + TVector<TDeferredAtom> schemaPaths; + schemaPaths.emplace_back(Ctx.Pos(), Id(node.GetRule_an_id_schema5(), *this)); + for (const auto& item : node.GetBlock6()) { + schemaPaths.emplace_back(Ctx.Pos(), Id(item.GetRule_an_id_schema2(), *this)); + } + + TVector<TDeferredAtom> roleNames; + const bool allowSystemRoles = false; + roleNames.emplace_back(); + if (!RoleNameClause(node.GetRule_role_name8(), roleNames.back(), allowSystemRoles)) { + return false; + } + for (const auto& item : node.GetBlock9()) { + roleNames.emplace_back(); + if (!RoleNameClause(item.GetRule_role_name2(), roleNames.back(), allowSystemRoles)) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildRevokePermissions(pos, service, cluster, permissions, schemaPaths, roleNames, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore40: + { + // ALTER TABLESTORE object_ref alter_table_store_action (COMMA alter_table_store_action)*; + auto& node = core.GetAlt_sql_stmt_core40().GetRule_alter_table_store_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + + if (node.GetRule_object_ref3().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref3().GetRule_id_or_at2(), *this).second; + const TString& typeId = "TABLESTORE"; + std::map<TString, TDeferredAtom> kv; + if (!ParseTableStoreFeatures(kv, node.GetRule_alter_table_store_action4())) { + return false; + } + + AddStatementToBlocks(blocks, BuildAlterObjectOperation(Ctx.Pos(), objectId, typeId, std::move(kv), std::set<TString>(), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore41: + { + // create_object_stmt: UPSERT OBJECT name (TYPE type [WITH k=v,...]); + auto& node = core.GetAlt_sql_stmt_core41().GetRule_upsert_object_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref3().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref3().GetRule_id_or_at2(), *this).second; + const TString& typeId = Id(node.GetRule_object_type_ref6().GetRule_an_id_or_type1(), *this); + std::map<TString, TDeferredAtom> kv; + if (node.HasBlock8()) { + if (!ParseObjectFeatures(kv, node.GetBlock8().GetRule_create_object_features1().GetRule_object_features2())) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildUpsertObjectOperation(Ctx.Pos(), objectId, typeId, std::move(kv), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore42: { + // create_view_stmt: CREATE VIEW name WITH (k = v, ...) AS select_stmt; + auto& node = core.GetAlt_sql_stmt_core42().GetRule_create_view_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref3().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + false, + context.ServiceId, + context.Cluster)) { + return false; + } + } + + std::map<TString, TDeferredAtom> features; + if (node.HasBlock4()) { + if (!ParseObjectFeatures(features, node.GetBlock4().GetRule_create_object_features1().GetRule_object_features2())) { + return false; + } + } + if (!ParseViewQuery(features, node.GetRule_select_stmt6())) { + return false; + } + + const TString objectId = Id(node.GetRule_object_ref3().GetRule_id_or_at2(), *this).second; + constexpr const char* TypeId = "VIEW"; + AddStatementToBlocks(blocks, + BuildCreateObjectOperation(Ctx.Pos(), + BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), + TypeId, + false, + false, + std::move(features), + context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore43: { + // drop_view_stmt: DROP VIEW name; + auto& node = core.GetAlt_sql_stmt_core43().GetRule_drop_view_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref3().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + false, + context.ServiceId, + context.Cluster)) { + return false; + } + } + + const TString objectId = Id(node.GetRule_object_ref3().GetRule_id_or_at2(), *this).second; + constexpr const char* TypeId = "VIEW"; + AddStatementToBlocks(blocks, + BuildDropObjectOperation(Ctx.Pos(), + BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), + TypeId, + false, + {}, + context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore44: { + // alter_replication_stmt: ALTER ASYNC REPLICATION + auto& node = core.GetAlt_sql_stmt_core44().GetRule_alter_replication_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref4().HasBlock1()) { + const auto& cluster = node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(); + if (!ClusterExpr(cluster, false, context.ServiceId, context.Cluster)) { + return false; + } + } + + std::map<TString, TNodePtr> settings; + if (!AsyncReplicationAlterAction(settings, node.GetRule_alter_replication_action5(), *this)) { + return false; + } + for (auto& block : node.GetBlock6()) { + if (!AsyncReplicationAlterAction(settings, block.GetRule_alter_replication_action2(), *this)) { + return false; + } + } + + const TString id = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, BuildAlterAsyncReplication(Ctx.Pos(), + BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), id), + std::move(settings), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore45: { + // create_resource_pool_stmt: CREATE RESOURCE POOL name WITH (k=v,...); + auto& node = core.GetAlt_sql_stmt_core45().GetRule_create_resource_pool_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref4().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + std::map<TString, TDeferredAtom> kv; + if (!ParseResourcePoolSettings(kv, node.GetRule_with_table_settings5())) { + return false; + } + + AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL", false, false, std::move(kv), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore46: { + // alter_resource_pool_stmt: ALTER RESOURCE POOL object_ref alter_resource_pool_action (COMMA alter_external_data_source_action)* + Ctx.BodyPart(); + const auto& node = core.GetAlt_sql_stmt_core46().GetRule_alter_resource_pool_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref4().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + std::map<TString, TDeferredAtom> kv; + std::set<TString> toReset; + if (!ParseResourcePoolSettings(kv, toReset, node.GetRule_alter_resource_pool_action5())) { + return false; + } + + for (const auto& action : node.GetBlock6()) { + if (!ParseResourcePoolSettings(kv, toReset, action.GetRule_alter_resource_pool_action2())) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildAlterObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL", std::move(kv), std::move(toReset), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore47: { + // drop_resource_pool_stmt: DROP RESOURCE POOL name; + auto& node = core.GetAlt_sql_stmt_core47().GetRule_drop_resource_pool_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref4().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref4().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref4().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL", false, {}, context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore48: { + // create_backup_collection_stmt: CREATE BACKUP COLLECTION name WITH (k=v,...); + auto& node = core.GetAlt_sql_stmt_core48().GetRule_create_backup_collection_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_backup_collection2().GetRule_object_ref3().HasBlock1()) { + if (!ClusterExpr(node.GetRule_backup_collection2().GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + false, + context.ServiceId, + context.Cluster)) { + return false; + } + } + + std::map<TString, TDeferredAtom> kv; + if (!ParseBackupCollectionSettings(kv, node.GetRule_backup_collection_settings6())) { + return false; + } + + bool database = false; + TVector<TDeferredAtom> tables; + if (node.HasBlock3()) { + database = node.GetBlock3().GetRule_create_backup_collection_entries1().has_alt_create_backup_collection_entries1(); + if (node.GetBlock3().GetRule_create_backup_collection_entries1().has_alt_create_backup_collection_entries2()) { + if (!ParseBackupCollectionTables( + tables, + node + .GetBlock3() + .GetRule_create_backup_collection_entries1() + .alt_create_backup_collection_entries2() + .GetRule_create_backup_collection_entries_many1() + .GetRule_table_list2())) + { + return false; + } + } + } + + const TString& objectId = Id(node.GetRule_backup_collection2().GetRule_object_ref3().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, + BuildCreateBackupCollection(Ctx.Pos(), + BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), + TCreateBackupCollectionParameters { + .Settings = std::move(kv), + .Database = database, + .Tables = tables, + .ExistingOk = false, + }, + context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore49: { + // alter_backup_collection_stmt: ALTER BACKUP COLLECTION name alter_backup_collection_action (COMMA alter_backup_collection_action)*; + auto& node = core.GetAlt_sql_stmt_core49().GetRule_alter_backup_collection_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_backup_collection2().GetRule_object_ref3().HasBlock1()) { + if (!ClusterExpr(node.GetRule_backup_collection2().GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + false, + context.ServiceId, + context.Cluster)) { + return false; + } + } + + std::map<TString, TDeferredAtom> kv; + std::set<TString> toReset; + + bool addDatabase = false; + bool dropDatabase = false; + TVector<TDeferredAtom> addTables; + TVector<TDeferredAtom> removeTables; + + switch (node.GetBlock3().Alt_case()) { + case TRule_alter_backup_collection_stmt_TBlock3::kAlt1: { + if (!ParseBackupCollectionSettings(kv, toReset, node.GetBlock3().GetAlt1().GetRule_alter_backup_collection_actions1())) { + return false; + } + break; + } + case TRule_alter_backup_collection_stmt_TBlock3::kAlt2: { + if (!ParseBackupCollectionEntries( + addDatabase, + dropDatabase, + addTables, + removeTables, + node.GetBlock3().GetAlt2().GetRule_alter_backup_collection_entries1())) + { + return false; + } + break; + } + case TRule_alter_backup_collection_stmt_TBlock3::ALT_NOT_SET: {} // do nothing + } + + auto database = addDatabase ? + TAlterBackupCollectionParameters::EDatabase::Add : + dropDatabase ? + TAlterBackupCollectionParameters::EDatabase::Drop : + TAlterBackupCollectionParameters::EDatabase::Unchanged; + + const TString& objectId = Id(node.GetRule_backup_collection2().GetRule_object_ref3().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, + BuildAlterBackupCollection(Ctx.Pos(), + BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), + TAlterBackupCollectionParameters { + .Settings = std::move(kv), + .SettingsToReset = std::move(toReset), + .Database = database, + .TablesToAdd = addTables, + .TablesToDrop = removeTables, + .MissingOk = false, + }, + context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore50: { + // drop_backup_collection_stmt: DROP BACKUP COLLECTION name; + auto& node = core.GetAlt_sql_stmt_core50().GetRule_drop_backup_collection_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_backup_collection2().GetRule_object_ref3().HasBlock1()) { + if (!ClusterExpr(node.GetRule_backup_collection2().GetRule_object_ref3().GetBlock1().GetRule_cluster_expr1(), + false, + context.ServiceId, + context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_backup_collection2().GetRule_object_ref3().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, + BuildDropBackupCollection(Ctx.Pos(), + BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), + TDropBackupCollectionParameters { + .MissingOk = false, + }, + context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore51: { + // analyze_stmt: ANALYZE table_ref + Ctx.BodyPart(); + const auto& rule = core.GetAlt_sql_stmt_core51().GetRule_analyze_stmt1(); + + if (!rule.GetRule_analyze_table_list2().GetBlock2().empty()) { + Error() << "ANALYZE with multitables hasn't been implemented yet"; + return false; + } + auto analyzeTable = rule.GetRule_analyze_table_list2().GetRule_analyze_table1(); + + TVector<TString> columns; + if (analyzeTable.HasBlock2()) { + auto columnsNode = + analyzeTable.GetBlock2().GetRule_column_list2(); + + if (columnsNode.HasRule_column_name1()) { + columns.push_back(Id(columnsNode.GetRule_column_name1().GetRule_an_id2(), *this)); + for (const auto& columnNode: columnsNode.GetBlock2()) { + columns.push_back(Id(columnNode.GetRule_column_name2().GetRule_an_id2(), *this)); + } + } + } + + TTableRef tr; + if (!SimpleTableRefImpl(rule.GetRule_analyze_table_list2().GetRule_analyze_table1().GetRule_simple_table_ref1(), tr)) { + return false; + } + + auto params = TAnalyzeParams{.Table = std::make_shared<TTableRef>(tr), .Columns = std::move(columns)}; + AddStatementToBlocks(blocks, BuildAnalyze(Ctx.Pos(), tr.Service, tr.Cluster, params, Ctx.Scoped)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore52: { + // create_resource_pool_classifier_stmt: CREATE RESOURCE POOL CLASSIFIER name WITH (k=v,...); + auto& node = core.GetAlt_sql_stmt_core52().GetRule_create_resource_pool_classifier_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref5().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second; + std::map<TString, TDeferredAtom> kv; + if (!ParseResourcePoolClassifierSettings(kv, node.GetRule_with_table_settings6())) { + return false; + } + + AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL_CLASSIFIER", false, false, std::move(kv), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore53: { + // alter_resource_pool_classifier_stmt: ALTER RESOURCE POOL CLASSIFIER object_ref alter_resource_pool_classifier_action (COMMA alter_resource_pool_classifier_action)* + Ctx.BodyPart(); + const auto& node = core.GetAlt_sql_stmt_core53().GetRule_alter_resource_pool_classifier_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref5().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second; + std::map<TString, TDeferredAtom> kv; + std::set<TString> toReset; + if (!ParseResourcePoolClassifierSettings(kv, toReset, node.GetRule_alter_resource_pool_classifier_action6())) { + return false; + } + + for (const auto& action : node.GetBlock7()) { + if (!ParseResourcePoolClassifierSettings(kv, toReset, action.GetRule_alter_resource_pool_classifier_action2())) { + return false; + } + } + + AddStatementToBlocks(blocks, BuildAlterObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL_CLASSIFIER", std::move(kv), std::move(toReset), context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore54: { + // drop_resource_pool_classifier_stmt: DROP RESOURCE POOL CLASSIFIER name; + auto& node = core.GetAlt_sql_stmt_core54().GetRule_drop_resource_pool_classifier_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref5().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL_CLASSIFIER", false, {}, context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore55: { + // backup_stmt: BACKUP object_ref (INCREMENTAL)?; + auto& node = core.GetAlt_sql_stmt_core55().GetRule_backup_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref2().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref2().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + bool incremental = node.HasBlock3(); + + const TString& objectId = Id(node.GetRule_object_ref2().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, + BuildBackup( + Ctx.Pos(), + BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), + TBackupParameters{ + .Incremental = incremental, + }, + context)); + break; + } + case TRule_sql_stmt_core::kAltSqlStmtCore56: { + // restore_stmt: RESTORE object_ref (AT STRING_VALUE)?; + auto& node = core.GetAlt_sql_stmt_core56().GetRule_restore_stmt1(); + TObjectOperatorContext context(Ctx.Scoped); + if (node.GetRule_object_ref2().HasBlock1()) { + if (!ClusterExpr(node.GetRule_object_ref2().GetBlock1().GetRule_cluster_expr1(), + false, context.ServiceId, context.Cluster)) { + return false; + } + } + + TString at; + if (node.HasBlock3()) { + const TString stringValue = Ctx.Token(node.GetBlock3().GetToken2()); + const auto unescaped = StringContent(Ctx, Ctx.Pos(), stringValue); + if (!unescaped) { + return false; + } + at = unescaped->Content; + } + + const TString& objectId = Id(node.GetRule_object_ref2().GetRule_id_or_at2(), *this).second; + AddStatementToBlocks(blocks, + BuildRestore( + Ctx.Pos(), + BuildTablePath(Ctx.GetPrefixPath(context.ServiceId, context.Cluster), objectId), + TRestoreParameters{ + .At = at, + }, + context)); + break; + } + case TRule_sql_stmt_core::ALT_NOT_SET: + Ctx.IncrementMonCounter("sql_errors", "UnknownStatement" + internalStatementName); + AltNotImplemented("sql_stmt_core", core); + return false; + } + + Ctx.IncrementMonCounter("sql_features", internalStatementName); + return !Ctx.HasPendingErrors; +} + +bool TSqlQuery::DeclareStatement(const TRule_declare_stmt& stmt) { + TNodePtr defaultValue; + if (stmt.HasBlock5()) { + TSqlExpression sqlExpr(Ctx, Mode); + auto exprOrId = sqlExpr.LiteralExpr(stmt.GetBlock5().GetRule_literal_value2()); + if (!exprOrId) { + return false; + } + if (!exprOrId->Expr) { + Ctx.Error() << "Identifier is not expected here"; + return false; + } + defaultValue = exprOrId->Expr; + } + if (defaultValue) { + Error() << "DEFAULT value not supported yet"; + return false; + } + if (!Ctx.IsParseHeading()) { + Error() << "DECLARE statement should be in beginning of query, but it's possible to use PRAGMA or USE before it"; + return false; + } + + TString varName; + if (!NamedNodeImpl(stmt.GetRule_bind_parameter2(), varName, *this)) { + return false; + } + const auto varPos = Ctx.Pos(); + const auto typeNode = TypeNode(stmt.GetRule_type_name4()); + if (!typeNode) { + return false; + } + if (IsAnonymousName(varName)) { + Ctx.Error(varPos) << "Can not use anonymous name '" << varName << "' in DECLARE statement"; + return false; + } + + if (Ctx.IsAlreadyDeclared(varName)) { + Ctx.Warning(varPos, TIssuesIds::YQL_DUPLICATE_DECLARE) << "Duplicate declaration of '" << varName << "' will be ignored"; + } else { + PushNamedAtom(varPos, varName); + Ctx.DeclareVariable(varName, varPos, typeNode); + } + return true; +} + +bool TSqlQuery::ExportStatement(const TRule_export_stmt& stmt) { + if (Mode != NSQLTranslation::ESqlMode::LIBRARY || !TopLevel) { + Error() << "EXPORT statement should be used only in a library on the top level"; + return false; + } + + TVector<TSymbolNameWithPos> bindNames; + if (!BindList(stmt.GetRule_bind_parameter_list2(), bindNames)) { + return false; + } + + for (auto& bindName : bindNames) { + if (!Ctx.AddExport(bindName.Pos, bindName.Name)) { + return false; + } + } + return true; +} + +bool TSqlQuery::AlterTableAction(const TRule_alter_table_action& node, TAlterTableParameters& params) { + if (params.RenameTo) { + // rename action is followed by some other actions + Error() << "RENAME TO can not be used together with another table action"; + return false; + } + + switch (node.Alt_case()) { + case TRule_alter_table_action::kAltAlterTableAction1: { + // ADD COLUMN + const auto& addRule = node.GetAlt_alter_table_action1().GetRule_alter_table_add_column1(); + if (!AlterTableAddColumn(addRule, params)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction2: { + // DROP COLUMN + const auto& dropRule = node.GetAlt_alter_table_action2().GetRule_alter_table_drop_column1(); + if (!AlterTableDropColumn(dropRule, params)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction3: { + // ALTER COLUMN + const auto& alterRule = node.GetAlt_alter_table_action3().GetRule_alter_table_alter_column1(); + if (!AlterTableAlterColumn(alterRule, params)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction4: { + // ADD FAMILY + const auto& familyEntry = node.GetAlt_alter_table_action4().GetRule_alter_table_add_column_family1() + .GetRule_family_entry2(); + if (!AlterTableAddFamily(familyEntry, params)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction5: { + // ALTER FAMILY + const auto& alterRule = node.GetAlt_alter_table_action5().GetRule_alter_table_alter_column_family1(); + if (!AlterTableAlterFamily(alterRule, params)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction6: { + // SET (uncompat) + const auto& setRule = node.GetAlt_alter_table_action6().GetRule_alter_table_set_table_setting_uncompat1(); + if (!AlterTableSetTableSetting(setRule, params.TableSettings, params.TableType)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction7: { + // SET (compat) + const auto& setRule = node.GetAlt_alter_table_action7().GetRule_alter_table_set_table_setting_compat1(); + if (!AlterTableSetTableSetting(setRule, params.TableSettings, params.TableType)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction8: { + // RESET + const auto& setRule = node.GetAlt_alter_table_action8().GetRule_alter_table_reset_table_setting1(); + if (!AlterTableResetTableSetting(setRule, params.TableSettings, params.TableType)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction9: { + // ADD INDEX + const auto& addIndex = node.GetAlt_alter_table_action9().GetRule_alter_table_add_index1(); + if (!AlterTableAddIndex(addIndex, params)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction10: { + // DROP INDEX + const auto& dropIndex = node.GetAlt_alter_table_action10().GetRule_alter_table_drop_index1(); + AlterTableDropIndex(dropIndex, params); + break; + } + case TRule_alter_table_action::kAltAlterTableAction11: { + // RENAME TO + if (!params.IsEmpty()) { + // rename action follows some other actions + Error() << "RENAME TO can not be used together with another table action"; + return false; + } + + const auto& renameTo = node.GetAlt_alter_table_action11().GetRule_alter_table_rename_to1(); + AlterTableRenameTo(renameTo, params); + break; + } + case TRule_alter_table_action::kAltAlterTableAction12: { + // ADD CHANGEFEED + const auto& rule = node.GetAlt_alter_table_action12().GetRule_alter_table_add_changefeed1(); + if (!AlterTableAddChangefeed(rule, params)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction13: { + // ALTER CHANGEFEED + const auto& rule = node.GetAlt_alter_table_action13().GetRule_alter_table_alter_changefeed1(); + if (!AlterTableAlterChangefeed(rule, params)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction14: { + // DROP CHANGEFEED + const auto& rule = node.GetAlt_alter_table_action14().GetRule_alter_table_drop_changefeed1(); + AlterTableDropChangefeed(rule, params); + break; + } + case TRule_alter_table_action::kAltAlterTableAction15: { + // RENAME INDEX TO + if (!params.IsEmpty()) { + // rename action follows some other actions + Error() << "RENAME INDEX TO can not be used together with another table action"; + return false; + } + + const auto& renameTo = node.GetAlt_alter_table_action15().GetRule_alter_table_rename_index_to1(); + AlterTableRenameIndexTo(renameTo, params); + break; + } + case TRule_alter_table_action::kAltAlterTableAction16: { + // ALTER INDEX + const auto& rule = node.GetAlt_alter_table_action16().GetRule_alter_table_alter_index1(); + if (!AlterTableAlterIndex(rule, params)) { + return false; + } + break; + } + case TRule_alter_table_action::kAltAlterTableAction17: { + // ALTER COLUMN id DROP NOT NULL + const auto& alterRule = node.GetAlt_alter_table_action17().GetRule_alter_table_alter_column_drop_not_null1(); + + if (!AlterTableAlterColumnDropNotNull(alterRule, params)) { + return false; + } + + break; + } + + case TRule_alter_table_action::ALT_NOT_SET: { + AltNotImplemented("alter_table_action", node); + return false; + } + } + return true; +} + +bool TSqlQuery::AlterExternalTableAction(const TRule_alter_external_table_action& node, TAlterTableParameters& params) { + if (params.RenameTo) { + // rename action is followed by some other actions + Error() << "RENAME TO can not be used together with another table action"; + return false; + } + + switch (node.Alt_case()) { + case TRule_alter_external_table_action::kAltAlterExternalTableAction1: { + // ADD COLUMN + const auto& addRule = node.GetAlt_alter_external_table_action1().GetRule_alter_table_add_column1(); + if (!AlterTableAddColumn(addRule, params)) { + return false; + } + break; + } + case TRule_alter_external_table_action::kAltAlterExternalTableAction2: { + // DROP COLUMN + const auto& dropRule = node.GetAlt_alter_external_table_action2().GetRule_alter_table_drop_column1(); + if (!AlterTableDropColumn(dropRule, params)) { + return false; + } + break; + } + case TRule_alter_external_table_action::kAltAlterExternalTableAction3: { + // SET (uncompat) + const auto& setRule = node.GetAlt_alter_external_table_action3().GetRule_alter_table_set_table_setting_uncompat1(); + if (!AlterTableSetTableSetting(setRule, params.TableSettings, params.TableType)) { + return false; + } + break; + } + case TRule_alter_external_table_action::kAltAlterExternalTableAction4: { + // SET (compat) + const auto& setRule = node.GetAlt_alter_external_table_action4().GetRule_alter_table_set_table_setting_compat1(); + if (!AlterTableSetTableSetting(setRule, params.TableSettings, params.TableType)) { + return false; + } + break; + } + case TRule_alter_external_table_action::kAltAlterExternalTableAction5: { + // RESET + const auto& setRule = node.GetAlt_alter_external_table_action5().GetRule_alter_table_reset_table_setting1(); + if (!AlterTableResetTableSetting(setRule, params.TableSettings, params.TableType)) { + return false; + } + break; + } + + case TRule_alter_external_table_action::ALT_NOT_SET: + AltNotImplemented("alter_external_table_action", node); + return false; + } + return true; +} + +bool TSqlQuery::AlterTableAddColumn(const TRule_alter_table_add_column& node, TAlterTableParameters& params) { + auto columnSchema = ColumnSchemaImpl(node.GetRule_column_schema3()); + if (!columnSchema) { + return false; + } + if (columnSchema->Families.size() > 1) { + Ctx.Error() << "Several column families for a single column are not yet supported"; + return false; + } + params.AddColumns.push_back(*columnSchema); + return true; +} + +bool TSqlQuery::AlterTableDropColumn(const TRule_alter_table_drop_column& node, TAlterTableParameters& params) { + TString name = Id(node.GetRule_an_id3(), *this); + params.DropColumns.push_back(name); + return true; +} + +bool TSqlQuery::AlterTableAlterColumn(const TRule_alter_table_alter_column& node, + TAlterTableParameters& params) +{ + TString name = Id(node.GetRule_an_id3(), *this); + const TPosition pos(Context().Pos()); + TVector<TIdentifier> families; + const auto& familyRelation = node.GetRule_family_relation5(); + families.push_back(IdEx(familyRelation.GetRule_an_id2(), *this)); + params.AlterColumns.emplace_back(pos, name, nullptr, false, families, false, nullptr, TColumnSchema::ETypeOfChange::SetFamily); + return true; +} + +bool TSqlQuery::AlterTableAddFamily(const TRule_family_entry& node, TAlterTableParameters& params) { + TFamilyEntry family(IdEx(node.GetRule_an_id2(), *this)); + if (!FillFamilySettings(node.GetRule_family_settings3(), family)) { + return false; + } + params.AddColumnFamilies.push_back(family); + return true; +} + +bool TSqlQuery::AlterTableAlterFamily(const TRule_alter_table_alter_column_family& node, + TAlterTableParameters& params) +{ + TFamilyEntry* entry = nullptr; + TIdentifier name = IdEx(node.GetRule_an_id3(), *this); + for (auto& family : params.AlterColumnFamilies) { + if (family.Name.Name == name.Name) { + entry = &family; + break; + } + } + if (!entry) { + entry = ¶ms.AlterColumnFamilies.emplace_back(name); + } + TIdentifier settingName = IdEx(node.GetRule_an_id5(), *this); + const TRule_family_setting_value& value = node.GetRule_family_setting_value6(); + if (to_lower(settingName.Name) == "data") { + if (entry->Data) { + Ctx.Error() << "Redefinition of 'data' setting for column family '" << name.Name + << "' in one alter"; + return false; + } + const TString stringValue(Ctx.Token(value.GetAlt_family_setting_value1().GetToken1())); + entry->Data = BuildLiteralSmartString(Ctx, stringValue); + } else if (to_lower(settingName.Name) == "compression") { + if (entry->Compression) { + Ctx.Error() << "Redefinition of 'compression' setting for column family '" << name.Name + << "' in one alter"; + return false; + } + const TString stringValue(Ctx.Token(value.GetAlt_family_setting_value1().GetToken1())); + entry->Compression = BuildLiteralSmartString(Ctx, stringValue); + } else if (to_lower(settingName.Name) == "compression_level") { + if (entry->CompressionLevel) { + Ctx.Error() << "Redefinition of 'compression_level' setting for column family '" << name.Name << "' in one alter"; + return false; + } + entry->CompressionLevel = LiteralNumber(Ctx, value.GetAlt_family_setting_value2().GetRule_integer1()); + } else { + Ctx.Error() << "Unknown table setting: " << settingName.Name; + return false; + } + return true; +} + +bool TSqlQuery::AlterTableSetTableSetting( + const TRule_alter_table_set_table_setting_uncompat& node, TTableSettings& tableSettings, ETableType tableType +) { + return StoreTableSettingsEntry( + IdEx(node.GetRule_an_id2(), *this), + node.GetRule_table_setting_value3(), + tableSettings, + tableType, + true + ); +} + +bool TSqlQuery::AlterTableSetTableSetting( + const TRule_alter_table_set_table_setting_compat& node, TTableSettings& tableSettings, ETableType tableType +) { + const auto storeSetting = [&](const TRule_alter_table_setting_entry& entry) { + return StoreTableSettingsEntry( + IdEx(entry.GetRule_an_id1(), *this), + entry.GetRule_table_setting_value3(), + tableSettings, + tableType, + true + ); + }; + + const auto& firstEntry = node.GetRule_alter_table_setting_entry3(); + if (!storeSetting(firstEntry)) { + return false; + } + for (const auto& block : node.GetBlock4()) { + const auto& entry = block.GetRule_alter_table_setting_entry2(); + if (!storeSetting(entry)) { + return false; + } + } + return true; +} + +bool TSqlQuery::AlterTableResetTableSetting( + const TRule_alter_table_reset_table_setting& node, TTableSettings& tableSettings, ETableType tableType +) { + const auto resetSetting = [&](const TRule_an_id& id) { + return ResetTableSettingsEntry(IdEx(id, *this), tableSettings, tableType); + }; + + const auto& firstEntry = node.GetRule_an_id3(); + if (!resetSetting(firstEntry)) { + return false; + } + for (const auto& block : node.GetBlock4()) { + const auto& entry = block.GetRule_an_id2(); + if (!resetSetting(entry)) { + return false; + } + } + return true; +} + +bool TSqlQuery::AlterTableAddIndex(const TRule_alter_table_add_index& node, TAlterTableParameters& params) { + if (!CreateTableIndex(node.GetRule_table_index2(), params.AddIndexes)) { + return false; + } + return true; +} + +void TSqlQuery::AlterTableDropIndex(const TRule_alter_table_drop_index& node, TAlterTableParameters& params) { + params.DropIndexes.emplace_back(IdEx(node.GetRule_an_id3(), *this)); +} + +void TSqlQuery::AlterTableRenameTo(const TRule_alter_table_rename_to& node, TAlterTableParameters& params) { + params.RenameTo = IdEx(node.GetRule_an_id_table3(), *this); +} + +void TSqlQuery::AlterTableRenameIndexTo(const TRule_alter_table_rename_index_to& node, TAlterTableParameters& params) { + auto src = IdEx(node.GetRule_an_id3(), *this); + auto dst = IdEx(node.GetRule_an_id5(), *this); + + params.RenameIndexTo = std::make_pair(src, dst); +} + +bool TSqlQuery::AlterTableAlterIndex(const TRule_alter_table_alter_index& node, TAlterTableParameters& params) { + const auto indexName = IdEx(node.GetRule_an_id3(), *this); + params.AlterIndexes.emplace_back(indexName); + TTableSettings& indexTableSettings = params.AlterIndexes.back().TableSettings; + + const auto& action = node.GetRule_alter_table_alter_index_action4(); + + switch (action.Alt_case()) { + case TRule_alter_table_alter_index_action::kAltAlterTableAlterIndexAction1: { + // SET setting value + const auto& rule = action.GetAlt_alter_table_alter_index_action1().GetRule_alter_table_set_table_setting_uncompat1(); + if (!AlterTableSetTableSetting(rule, indexTableSettings, params.TableType)) { + return false; + } + break; + } + case TRule_alter_table_alter_index_action::kAltAlterTableAlterIndexAction2: { + // SET (setting1 = value1, ...) + const auto& rule = action.GetAlt_alter_table_alter_index_action2().GetRule_alter_table_set_table_setting_compat1(); + if (!AlterTableSetTableSetting(rule, indexTableSettings, params.TableType)) { + return false; + } + break; + } + case TRule_alter_table_alter_index_action::kAltAlterTableAlterIndexAction3: { + // RESET (setting1, ...) + const auto& rule = action.GetAlt_alter_table_alter_index_action3().GetRule_alter_table_reset_table_setting1(); + if (!AlterTableResetTableSetting(rule, indexTableSettings, params.TableType)) { + return false; + } + break; + } + case TRule_alter_table_alter_index_action::ALT_NOT_SET: + AltNotImplemented("alter_table_alter_index_action", action); + return false; + } + + return true; +} + +bool TSqlQuery::AlterTableAlterColumnDropNotNull(const TRule_alter_table_alter_column_drop_not_null& node, TAlterTableParameters& params) { + TString name = Id(node.GetRule_an_id3(), *this); + const TPosition pos(Context().Pos()); + params.AlterColumns.emplace_back(pos, name, nullptr, false, TVector<TIdentifier>(), false, nullptr, TColumnSchema::ETypeOfChange::DropNotNullConstraint); + return true; +} + +bool TSqlQuery::AlterTableAddChangefeed(const TRule_alter_table_add_changefeed& node, TAlterTableParameters& params) { + TSqlExpression expr(Ctx, Mode); + return CreateChangefeed(node.GetRule_changefeed2(), expr, params.AddChangefeeds); +} + +bool TSqlQuery::AlterTableAlterChangefeed(const TRule_alter_table_alter_changefeed& node, TAlterTableParameters& params) { + params.AlterChangefeeds.emplace_back(IdEx(node.GetRule_an_id3(), *this)); + + const auto& alter = node.GetRule_changefeed_alter_settings4(); + switch (alter.Alt_case()) { + case TRule_changefeed_alter_settings::kAltChangefeedAlterSettings1: { + // DISABLE + params.AlterChangefeeds.back().Disable = true; + break; + } + case TRule_changefeed_alter_settings::kAltChangefeedAlterSettings2: { + // SET + const auto& rule = alter.GetAlt_changefeed_alter_settings2().GetRule_changefeed_settings3(); + TSqlExpression expr(Ctx, Mode); + if (!ChangefeedSettings(rule, expr, params.AlterChangefeeds.back().Settings, true)) { + return false; + } + break; + } + + case TRule_changefeed_alter_settings::ALT_NOT_SET: + AltNotImplemented("changefeed_alter_settings", alter); + return false; + } + + return true; +} + +void TSqlQuery::AlterTableDropChangefeed(const TRule_alter_table_drop_changefeed& node, TAlterTableParameters& params) { + params.DropChangefeeds.emplace_back(IdEx(node.GetRule_an_id3(), *this)); +} + +TNodePtr TSqlQuery::PragmaStatement(const TRule_pragma_stmt& stmt, bool& success) { + success = false; + const TString& prefix = OptIdPrefixAsStr(stmt.GetRule_opt_id_prefix_or_type2(), *this); + const TString& lowerPrefix = to_lower(prefix); + const TString pragma(Id(stmt.GetRule_an_id3(), *this)); + TString normalizedPragma(pragma); + TMaybe<TIssue> normalizeError = NormalizeName(Ctx.Pos(), normalizedPragma); + if (!normalizeError.Empty()) { + Error() << normalizeError->GetMessage(); + Ctx.IncrementMonCounter("sql_errors", "NormalizePragmaError"); + return {}; + } + + TVector<TDeferredAtom> values; + TVector<const TRule_pragma_value*> pragmaValues; + bool pragmaValueDefault = false; + if (stmt.GetBlock4().HasAlt1()) { + pragmaValues.push_back(&stmt.GetBlock4().GetAlt1().GetRule_pragma_value2()); + } + else if (stmt.GetBlock4().HasAlt2()) { + pragmaValues.push_back(&stmt.GetBlock4().GetAlt2().GetRule_pragma_value2()); + for (auto& additionalValue : stmt.GetBlock4().GetAlt2().GetBlock3()) { + pragmaValues.push_back(&additionalValue.GetRule_pragma_value2()); + } + } + + const bool withConfigure = prefix || normalizedPragma == "file" || normalizedPragma == "folder" || normalizedPragma == "udf"; + static const THashSet<TStringBuf> lexicalScopePragmas = { + "classicdivision", + "strictjoinkeytypes", + "disablestrictjoinkeytypes", + "checkedops", + "unicodeliterals", + "disableunicodeliterals", + "warnuntypedstringliterals", + "disablewarnuntypedstringliterals", + }; + const bool hasLexicalScope = withConfigure || lexicalScopePragmas.contains(normalizedPragma); + const bool withFileAlias = normalizedPragma == "file" || normalizedPragma == "folder" || normalizedPragma == "library" || normalizedPragma == "udf"; + for (auto pragmaValue : pragmaValues) { + if (pragmaValue->HasAlt_pragma_value3()) { + auto value = Token(pragmaValue->GetAlt_pragma_value3().GetToken1()); + auto parsed = StringContentOrIdContent(Ctx, Ctx.Pos(), value); + if (!parsed) { + return {}; + } + + TString prefix; + if (withFileAlias && (values.size() == 0)) { + prefix = Ctx.Settings.FileAliasPrefix; + } + + values.push_back(TDeferredAtom(Ctx.Pos(), prefix + parsed->Content)); + } + else if (pragmaValue->HasAlt_pragma_value2() + && pragmaValue->GetAlt_pragma_value2().GetRule_id1().HasAlt_id2() + && "default" == to_lower(Id(pragmaValue->GetAlt_pragma_value2().GetRule_id1(), *this))) + { + pragmaValueDefault = true; + } + else if (withConfigure && pragmaValue->HasAlt_pragma_value5()) { + TString bindName; + if (!NamedNodeImpl(pragmaValue->GetAlt_pragma_value5().GetRule_bind_parameter1(), bindName, *this)) { + return {}; + } + auto namedNode = GetNamedNode(bindName); + if (!namedNode) { + return {}; + } + + TString prefix; + if (withFileAlias && (values.size() == 0)) { + prefix = Ctx.Settings.FileAliasPrefix; + } + + TDeferredAtom atom; + MakeTableFromExpression(Ctx.Pos(), Ctx, namedNode, atom, prefix); + values.push_back(atom); + } else { + Error() << "Expected string" << (withConfigure ? ", named parameter" : "") << " or 'default' keyword as pragma value for pragma: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + } + + if (prefix.empty()) { + if (!TopLevel && !hasLexicalScope) { + Error() << "This pragma '" << pragma << "' is not allowed to be used in actions or subqueries"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return{}; + } + + if (normalizedPragma == "refselect") { + Ctx.PragmaRefSelect = true; + Ctx.IncrementMonCounter("sql_pragma", "RefSelect"); + } else if (normalizedPragma == "sampleselect") { + Ctx.PragmaSampleSelect = true; + Ctx.IncrementMonCounter("sql_pragma", "SampleSelect"); + } else if (normalizedPragma == "allowdotinalias") { + Ctx.PragmaAllowDotInAlias = true; + Ctx.IncrementMonCounter("sql_pragma", "AllowDotInAlias"); + } else if (normalizedPragma == "udf") { + if ((values.size() != 1 && values.size() != 2) || pragmaValueDefault) { + Error() << "Expected file alias as pragma value"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + if (Ctx.Settings.FileAliasPrefix) { + if (values.size() == 1) { + values.emplace_back(TDeferredAtom(Ctx.Pos(), "")); + } + + TString prefix; + if (!values[1].GetLiteral(prefix, Ctx)) { + Error() << "Expected literal UDF module prefix in views"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + values[1] = TDeferredAtom(Ctx.Pos(), Ctx.Settings.FileAliasPrefix + prefix); + } + + Ctx.IncrementMonCounter("sql_pragma", "udf"); + success = true; + return BuildPragma(Ctx.Pos(), TString(ConfigProviderName), "ImportUdfs", values, false); + } else if (normalizedPragma == "packageversion") { + if (values.size() != 2 || pragmaValueDefault) { + Error() << "Expected package name and version"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + ui32 version = 0; + TString versionString; + TString packageName; + if (!values[0].GetLiteral(packageName, Ctx) || !values[1].GetLiteral(versionString, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + if (!PackageVersionFromString(versionString, version)) { + Error() << "Unable to parse package version, possible values 0, 1, draft, release"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + Ctx.SetPackageVersion(packageName, version); + Ctx.IncrementMonCounter("sql_pragma", "PackageVersion"); + success = true; + return BuildPragma(Ctx.Pos(), TString(ConfigProviderName), "SetPackageVersion", TVector<TDeferredAtom>{ values[0], TDeferredAtom(values[1].Build()->GetPos(), ToString(version)) }, false); + } else if (normalizedPragma == "file") { + if (values.size() < 2U || values.size() > 3U || pragmaValueDefault) { + Error() << "Expected file alias, url and optional token name as pragma values"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + Ctx.IncrementMonCounter("sql_pragma", "file"); + success = true; + return BuildPragma(Ctx.Pos(), TString(ConfigProviderName), "AddFileByUrl", values, false); + } else if (normalizedPragma == "fileoption") { + if (values.size() < 3U) { + Error() << "Expected file alias, option key and value"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + Ctx.IncrementMonCounter("sql_pragma", "FileOption"); + success = true; + return BuildPragma(Ctx.Pos(), TString(ConfigProviderName), "SetFileOption", values, false); + } else if (normalizedPragma == "folder") { + if (values.size() < 2U || values.size() > 3U || pragmaValueDefault) { + Error() << "Expected folder alias, url and optional token name as pragma values"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.IncrementMonCounter("sql_pragma", "folder"); + success = true; + return BuildPragma(Ctx.Pos(), TString(ConfigProviderName), "AddFolderByUrl", values, false); + } else if (normalizedPragma == "library") { + if (values.size() < 1) { + Error() << "Expected non-empty file alias"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return{}; + } + if (values.size() > 3) { + Error() << "Expected file alias and optional url and token name as pragma values"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return{}; + } + + TString alias; + if (!values.front().GetLiteral(alias, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return{}; + } + + TContext::TLibraryStuff library; + std::get<TPosition>(library) = values.front().Build()->GetPos(); + if (values.size() > 1) { + auto& first = std::get<1U>(library); + first.emplace(); + first->second = values[1].Build()->GetPos(); + if (!values[1].GetLiteral(first->first, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return{}; + } + + TSet<TString> names; + SubstParameters(first->first, Nothing(), &names); + for (const auto& name : names) { + auto namedNode = GetNamedNode(name); + if (!namedNode) { + return{}; + } + } + if (values.size() > 2) { + auto& second = std::get<2U>(library); + second.emplace(); + second->second = values[2].Build()->GetPos(); + if (!values[2].GetLiteral(second->first, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return{}; + } + } + } + + Ctx.Libraries[alias] = std::move(library); + Ctx.IncrementMonCounter("sql_pragma", "library"); + } else if (normalizedPragma == "package") { + if (values.size() < 2U || values.size() > 3U) { + Error() << "Expected package name, url and optional token name as pragma values"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + TString packageName; + if (!values.front().GetLiteral(packageName, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + TContext::TPackageStuff package; + std::get<TPosition>(package) = values.front().Build()->GetPos(); + + auto fillLiteral = [&](auto& literal, size_t index) { + if (values.size() <= index) { + return true; + } + + constexpr bool optional = std::is_base_of_v< + std::optional<TContext::TLiteralWithPosition>, + std::decay_t<decltype(literal)> + >; + + TContext::TLiteralWithPosition* literalPtr; + + if constexpr (optional) { + literal.emplace(); + literalPtr = &*literal; + } else { + literalPtr = &literal; + } + + literalPtr->second = values[index].Build()->GetPos(); + + if (!values[index].GetLiteral(literalPtr->first, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return false; + } + + return true; + }; + + // fill url + auto& urlLiteral = std::get<1U>(package); + if (!fillLiteral(urlLiteral, 1U)) { + return {}; + } + + TSet<TString> names; + SubstParameters(urlLiteral.first, Nothing(), &names); + for (const auto& name : names) { + auto namedNode = GetNamedNode(name); + if (!namedNode) { + return {}; + } + } + + // fill token + if (!fillLiteral(std::get<2U>(package), 2U)) { + return {}; + } + + Ctx.Packages[packageName] = std::move(package); + Ctx.IncrementMonCounter("sql_pragma", "package"); + } else if (normalizedPragma == "overridelibrary") { + if (values.size() != 1U) { + Error() << "Expected override library alias as pragma value"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + TString alias; + if (!values.front().GetLiteral(alias, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + TContext::TOverrideLibraryStuff overrideLibrary; + std::get<TPosition>(overrideLibrary) = values.front().Build()->GetPos(); + + Ctx.OverrideLibraries[alias] = std::move(overrideLibrary); + Ctx.IncrementMonCounter("sql_pragma", "overridelibrary"); + } else if (normalizedPragma == "directread") { + Ctx.PragmaDirectRead = true; + Ctx.IncrementMonCounter("sql_pragma", "DirectRead"); + } else if (normalizedPragma == "equijoin") { + Ctx.IncrementMonCounter("sql_pragma", "EquiJoin"); + } else if (normalizedPragma == "autocommit") { + Ctx.PragmaAutoCommit = true; + Ctx.IncrementMonCounter("sql_pragma", "AutoCommit"); + } else if (normalizedPragma == "usetableprefixforeach") { + Ctx.PragmaUseTablePrefixForEach = true; + Ctx.IncrementMonCounter("sql_pragma", "UseTablePrefixForEach"); + } else if (normalizedPragma == "tablepathprefix") { + TString value; + TMaybe<TString> arg; + + if (values.size() == 1 || values.size() == 2) { + if (!values.front().GetLiteral(value, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + if (values.size() == 2) { + arg = value; + if (!values.back().GetLiteral(value, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + } + + if (!Ctx.SetPathPrefix(value, arg)) { + return {}; + } + } else { + Error() << "Expected path prefix or tuple of (Provider, PathPrefix) or" + << " (Cluster, PathPrefix) as pragma value"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + Ctx.IncrementMonCounter("sql_pragma", "PathPrefix"); + } else if (normalizedPragma == "groupbylimit") { + if (values.size() != 1 || !values[0].GetLiteral() || !TryFromString(*values[0].GetLiteral(), Ctx.PragmaGroupByLimit)) { + Error() << "Expected unsigned integer literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.IncrementMonCounter("sql_pragma", "GroupByLimit"); + } else if (normalizedPragma == "groupbycubelimit") { + if (values.size() != 1 || !values[0].GetLiteral() || !TryFromString(*values[0].GetLiteral(), Ctx.PragmaGroupByCubeLimit)) { + Error() << "Expected unsigned integer literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.IncrementMonCounter("sql_pragma", "GroupByCubeLimit"); + } else if (normalizedPragma == "simplecolumns") { + Ctx.SimpleColumns = true; + Ctx.IncrementMonCounter("sql_pragma", "SimpleColumns"); + } else if (normalizedPragma == "disablesimplecolumns") { + Ctx.SimpleColumns = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableSimpleColumns"); + } else if (normalizedPragma == "coalescejoinkeysonqualifiedall") { + Ctx.CoalesceJoinKeysOnQualifiedAll = true; + Ctx.IncrementMonCounter("sql_pragma", "CoalesceJoinKeysOnQualifiedAll"); + } else if (normalizedPragma == "disablecoalescejoinkeysonqualifiedall") { + Ctx.CoalesceJoinKeysOnQualifiedAll = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableCoalesceJoinKeysOnQualifiedAll"); + } else if (normalizedPragma == "resultrowslimit") { + if (values.size() != 1 || !values[0].GetLiteral() || !TryFromString(*values[0].GetLiteral(), Ctx.ResultRowsLimit)) { + Error() << "Expected unsigned integer literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + Ctx.IncrementMonCounter("sql_pragma", "ResultRowsLimit"); + } else if (normalizedPragma == "resultsizelimit") { + if (values.size() != 1 || !values[0].GetLiteral() || !TryFromString(*values[0].GetLiteral(), Ctx.ResultSizeLimit)) { + Error() << "Expected unsigned integer literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + Ctx.IncrementMonCounter("sql_pragma", "ResultSizeLimit"); + } else if (normalizedPragma == "warning") { + if (values.size() != 2U || values.front().Empty() || values.back().Empty()) { + Error() << "Expected arguments <action>, <issueId> for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + TString action; + TString codePattern; + if (!values[0].GetLiteral(action, Ctx) || !values[1].GetLiteral(codePattern, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + TWarningRule rule; + TString parseError; + auto parseResult = TWarningRule::ParseFrom(codePattern, action, rule, parseError); + switch (parseResult) { + case TWarningRule::EParseResult::PARSE_OK: + break; + case TWarningRule::EParseResult::PARSE_PATTERN_FAIL: + case TWarningRule::EParseResult::PARSE_ACTION_FAIL: + Ctx.Error() << parseError; + return {}; + default: + Y_ENSURE(false, "Unknown parse result"); + } + + Ctx.WarningPolicy.AddRule(rule); + if (rule.GetPattern() == "*" && rule.GetAction() == EWarningAction::ERROR) { + // Keep 'unused symbol' warning as warning unless explicitly set to error + Ctx.SetWarningPolicyFor(TIssuesIds::YQL_UNUSED_SYMBOL, EWarningAction::DEFAULT); + } + + Ctx.IncrementMonCounter("sql_pragma", "warning"); + } else if (normalizedPragma == "greetings") { + if (values.size() > 1) { + Error() << "Multiple arguments are not expected for " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + if (values.empty()) { + values.emplace_back(TDeferredAtom(Ctx.Pos(), "Hello, world! And best wishes from the YQL Team!")); + } + + TString arg; + if (!values.front().GetLiteral(arg, Ctx)) { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.Info(Ctx.Pos()) << arg; + } else if (normalizedPragma == "warningmsg") { + if (values.size() != 1 || !values[0].GetLiteral()) { + Error() << "Expected string literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.Warning(Ctx.Pos(), TIssuesIds::YQL_PRAGMA_WARNING_MSG) << *values[0].GetLiteral(); + } else if (normalizedPragma == "errormsg") { + if (values.size() != 1 || !values[0].GetLiteral()) { + Error() << "Expected string literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.Error(Ctx.Pos()) << *values[0].GetLiteral(); + } else if (normalizedPragma == "classicdivision") { + if (values.size() != 1 || !values[0].GetLiteral() || !TryFromString(*values[0].GetLiteral(), Ctx.Scoped->PragmaClassicDivision)) { + Error() << "Expected boolean literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.IncrementMonCounter("sql_pragma", "ClassicDivision"); + } else if (normalizedPragma == "checkedops") { + if (values.size() != 1 || !values[0].GetLiteral() || !TryFromString(*values[0].GetLiteral(), Ctx.Scoped->PragmaCheckedOps)) { + Error() << "Expected boolean literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.IncrementMonCounter("sql_pragma", "CheckedOps"); + } else if (normalizedPragma == "disableunordered") { + Ctx.Warning(Ctx.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA) + << "Use of deprecated DisableUnordered pragma. It will be dropped soon"; + } else if (normalizedPragma == "pullupflatmapoverjoin") { + Ctx.PragmaPullUpFlatMapOverJoin = true; + Ctx.IncrementMonCounter("sql_pragma", "PullUpFlatMapOverJoin"); + } else if (normalizedPragma == "disablepullupflatmapoverjoin") { + Ctx.PragmaPullUpFlatMapOverJoin = false; + Ctx.IncrementMonCounter("sql_pragma", "DisablePullUpFlatMapOverJoin"); + } else if (normalizedPragma == "filterpushdownoverjoinoptionalside") { + Ctx.FilterPushdownOverJoinOptionalSide = true; + Ctx.IncrementMonCounter("sql_pragma", "FilterPushdownOverJoinOptionalSide"); + } else if (normalizedPragma == "disablefilterpushdownoverjoinoptionalside") { + Ctx.FilterPushdownOverJoinOptionalSide = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableFilterPushdownOverJoinOptionalSide"); + } else if (normalizedPragma == "rotatejointree") { + if (values.size() != 1 || !values[0].GetLiteral() || !TryFromString(*values[0].GetLiteral(), Ctx.RotateJoinTree)) { + Error() << "Expected boolean literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + } else if (normalizedPragma == "allowunnamedcolumns") { + Ctx.WarnUnnamedColumns = false; + Ctx.IncrementMonCounter("sql_pragma", "AllowUnnamedColumns"); + } else if (normalizedPragma == "warnunnamedcolumns") { + Ctx.WarnUnnamedColumns = true; + Ctx.IncrementMonCounter("sql_pragma", "WarnUnnamedColumns"); + } else if (normalizedPragma == "discoverymode") { + Ctx.DiscoveryMode = true; + Ctx.IncrementMonCounter("sql_pragma", "DiscoveryMode"); + } else if (normalizedPragma == "enablesystemcolumns") { + if (values.size() != 1 || !values[0].GetLiteral() || !TryFromString(*values[0].GetLiteral(), Ctx.EnableSystemColumns)) { + Error() << "Expected boolean literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.IncrementMonCounter("sql_pragma", "EnableSystemColumns"); + } else if (normalizedPragma == "ansiinforemptyornullableitemscollections") { + Ctx.AnsiInForEmptyOrNullableItemsCollections = true; + Ctx.IncrementMonCounter("sql_pragma", "AnsiInForEmptyOrNullableItemsCollections"); + } else if (normalizedPragma == "disableansiinforemptyornullableitemscollections") { + Ctx.AnsiInForEmptyOrNullableItemsCollections = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableAnsiInForEmptyOrNullableItemsCollections"); + } else if (normalizedPragma == "dqengine" || normalizedPragma == "blockengine") { + Ctx.IncrementMonCounter("sql_pragma", "DqEngine"); + if (values.size() != 1 || !values[0].GetLiteral() + || ! (*values[0].GetLiteral() == "disable" || *values[0].GetLiteral() == "auto" || *values[0].GetLiteral() == "force")) + { + Error() << "Expected `disable|auto|force' argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + const bool isDqEngine = normalizedPragma == "dqengine"; + auto& enable = isDqEngine ? Ctx.DqEngineEnable : Ctx.BlockEngineEnable; + auto& force = isDqEngine ? Ctx.DqEngineForce : Ctx.BlockEngineForce; + if (*values[0].GetLiteral() == "disable") { + enable = false; + force = false; + } else if (*values[0].GetLiteral() == "force") { + enable = true; + force = true; + } else if (*values[0].GetLiteral() == "auto") { + enable = true; + force = false; + } + } else if (normalizedPragma == "ansirankfornullablekeys") { + Ctx.AnsiRankForNullableKeys = true; + Ctx.IncrementMonCounter("sql_pragma", "AnsiRankForNullableKeys"); + } else if (normalizedPragma == "disableansirankfornullablekeys") { + Ctx.AnsiRankForNullableKeys = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableAnsiRankForNullableKeys"); + } else if (normalizedPragma == "ansiorderbylimitinunionall") { + Ctx.IncrementMonCounter("sql_pragma", "AnsiOrderByLimitInUnionAll"); + } else if (normalizedPragma == "disableansiorderbylimitinunionall") { + Error() << "DisableAnsiOrderByLimitInUnionAll pragma is deprecated and no longer supported"; + Ctx.IncrementMonCounter("sql_errors", "DeprecatedPragma"); + return {}; + } else if (normalizedPragma == "ansioptionalas") { + Ctx.AnsiOptionalAs = true; + Ctx.IncrementMonCounter("sql_pragma", "AnsiOptionalAs"); + } else if (normalizedPragma == "disableansioptionalas") { + Ctx.AnsiOptionalAs = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableAnsiOptionalAs"); + } else if (normalizedPragma == "warnonansialiasshadowing") { + Ctx.WarnOnAnsiAliasShadowing = true; + Ctx.IncrementMonCounter("sql_pragma", "WarnOnAnsiAliasShadowing"); + } else if (normalizedPragma == "disablewarnonansialiasshadowing") { + Ctx.WarnOnAnsiAliasShadowing = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableWarnOnAnsiAliasShadowing"); + } else if (normalizedPragma == "regexusere2") { + if (values.size() != 1U || !values.front().GetLiteral() || !TryFromString(*values.front().GetLiteral(), Ctx.PragmaRegexUseRe2)) { + Error() << "Expected 'true' or 'false' for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.IncrementMonCounter("sql_pragma", "RegexUseRe2"); + } else if (normalizedPragma == "jsonqueryreturnsjsondocument") { + Ctx.JsonQueryReturnsJsonDocument = true; + Ctx.IncrementMonCounter("sql_pragma", "JsonQueryReturnsJsonDocument"); + } else if (normalizedPragma == "disablejsonqueryreturnsjsondocument") { + Ctx.JsonQueryReturnsJsonDocument = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableJsonQueryReturnsJsonDocument"); + } else if (normalizedPragma == "orderedcolumns") { + Ctx.OrderedColumns = true; + Ctx.IncrementMonCounter("sql_pragma", "OrderedColumns"); + } else if (normalizedPragma == "disableorderedcolumns") { + Ctx.OrderedColumns = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableOrderedColumns"); + } else if (normalizedPragma == "positionalunionall") { + Ctx.PositionalUnionAll = true; + // PositionalUnionAll implies OrderedColumns + Ctx.OrderedColumns = true; + Ctx.IncrementMonCounter("sql_pragma", "PositionalUnionAll"); + } else if (normalizedPragma == "pqreadby") { + if (values.size() != 1 || !values[0].GetLiteral()) { + Error() << "Expected string literal as a single argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + // special guard to raise error on situation: + // use cluster1; + // pragma PqReadPqBy="cluster2"; + const TString* currentClusterLiteral = Ctx.Scoped->CurrCluster.GetLiteral(); + if (currentClusterLiteral && *values[0].GetLiteral() != "dq" && *currentClusterLiteral != *values[0].GetLiteral()) { + Error() << "Cluster in PqReadPqBy pragma differs from cluster specified in USE statement: " << *values[0].GetLiteral() << " != " << *currentClusterLiteral; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + Ctx.PqReadByRtmrCluster = *values[0].GetLiteral(); + Ctx.IncrementMonCounter("sql_pragma", "PqReadBy"); + } else if (normalizedPragma == "bogousstaringroupbyoverjoin") { + Ctx.BogousStarInGroupByOverJoin = true; + Ctx.IncrementMonCounter("sql_pragma", "BogousStarInGroupByOverJoin"); + } else if (normalizedPragma == "strictjoinkeytypes") { + Ctx.Scoped->StrictJoinKeyTypes = true; + Ctx.IncrementMonCounter("sql_pragma", "StrictJoinKeyTypes"); + } else if (normalizedPragma == "disablestrictjoinkeytypes") { + Ctx.Scoped->StrictJoinKeyTypes = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableStrictJoinKeyTypes"); + } else if (normalizedPragma == "unicodeliterals") { + Ctx.Scoped->UnicodeLiterals = true; + Ctx.IncrementMonCounter("sql_pragma", "UnicodeLiterals"); + } else if (normalizedPragma == "disableunicodeliterals") { + Ctx.Scoped->UnicodeLiterals = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableUnicodeLiterals"); + } else if (normalizedPragma == "warnuntypedstringliterals") { + Ctx.Scoped->WarnUntypedStringLiterals = true; + Ctx.IncrementMonCounter("sql_pragma", "WarnUntypedStringLiterals"); + } else if (normalizedPragma == "disablewarnuntypedstringliterals") { + Ctx.Scoped->WarnUntypedStringLiterals = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableWarnUntypedStringLiterals"); + } else if (normalizedPragma == "unorderedsubqueries") { + Ctx.UnorderedSubqueries = true; + Ctx.IncrementMonCounter("sql_pragma", "UnorderedSubqueries"); + } else if (normalizedPragma == "disableunorderedsubqueries") { + Ctx.UnorderedSubqueries = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableUnorderedSubqueries"); + } else if (normalizedPragma == "datawatermarks") { + if (values.size() != 1 || !values[0].GetLiteral() + || ! (*values[0].GetLiteral() == "enable" || *values[0].GetLiteral() == "disable")) + { + Error() << "Expected `enable|disable' argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + if (*values[0].GetLiteral() == "enable") { + Ctx.PragmaDataWatermarks = true; + } else if (*values[0].GetLiteral() == "disable") { + Ctx.PragmaDataWatermarks = false; + } + + Ctx.IncrementMonCounter("sql_pragma", "DataWatermarks"); + } else if (normalizedPragma == "flexibletypes") { + Ctx.FlexibleTypes = true; + Ctx.IncrementMonCounter("sql_pragma", "FlexibleTypes"); + } else if (normalizedPragma == "disableflexibletypes") { + Ctx.FlexibleTypes = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableFlexibleTypes"); + } else if (normalizedPragma == "ansicurrentrow") { + Ctx.AnsiCurrentRow = true; + Ctx.IncrementMonCounter("sql_pragma", "AnsiCurrentRow"); + } else if (normalizedPragma == "disableansicurrentrow") { + Ctx.AnsiCurrentRow = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableAnsiCurrentRow"); + } else if (normalizedPragma == "emitaggapply") { + Ctx.EmitAggApply = true; + Ctx.IncrementMonCounter("sql_pragma", "EmitAggApply"); + } else if (normalizedPragma == "disableemitaggapply") { + Ctx.EmitAggApply = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableEmitAggApply"); + } else if (normalizedPragma == "useblocks") { + Ctx.UseBlocks = true; + Ctx.IncrementMonCounter("sql_pragma", "UseBlocks"); + } else if (normalizedPragma == "disableuseblocks") { + Ctx.UseBlocks = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableUseBlocks"); + } else if (normalizedPragma == "ansilike") { + Ctx.AnsiLike = true; + Ctx.IncrementMonCounter("sql_pragma", "AnsiLike"); + } else if (normalizedPragma == "disableansilike") { + Ctx.AnsiLike = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableAnsiLike"); + } else if (normalizedPragma == "unorderedresult") { + Ctx.UnorderedResult = true; + Ctx.IncrementMonCounter("sql_pragma", "UnorderedResult"); + } else if (normalizedPragma == "disableunorderedresult") { + Ctx.UnorderedResult = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableUnorderedResult"); + } else if (normalizedPragma == "featurer010") { + if (values.size() == 1 && values[0].GetLiteral()) { + const auto& value = *values[0].GetLiteral(); + if ("prototype" == value) + Ctx.FeatureR010 = true; + else { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + } + else { + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + Ctx.IncrementMonCounter("sql_pragma", "FeatureR010"); + } else if (normalizedPragma == "compactgroupby") { + Ctx.CompactGroupBy = true; + Ctx.IncrementMonCounter("sql_pragma", "CompactGroupBy"); + } else if (normalizedPragma == "disablecompactgroupby") { + Ctx.CompactGroupBy = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableCompactGroupBy"); + } else if (normalizedPragma == "costbasedoptimizer") { + Ctx.IncrementMonCounter("sql_pragma", "CostBasedOptimizer"); + if (values.size() == 1 && values[0].GetLiteral()) { + Ctx.CostBasedOptimizer = to_lower(*values[0].GetLiteral()); + } + if (values.size() != 1 || !values[0].GetLiteral() + || ! (Ctx.CostBasedOptimizer == "disable" || Ctx.CostBasedOptimizer == "pg" || Ctx.CostBasedOptimizer == "native")) + { + Error() << "Expected `disable|pg|native' argument for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + } else if (normalizedPragma == "compactnamedexprs") { + Ctx.CompactNamedExprs = true; + Ctx.IncrementMonCounter("sql_pragma", "CompactNamedExprs"); + } else if (normalizedPragma == "disablecompactnamedexprs") { + Ctx.CompactNamedExprs = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableCompactNamedExprs"); + } else if (normalizedPragma == "validateunusedexprs") { + Ctx.ValidateUnusedExprs = true; + Ctx.IncrementMonCounter("sql_pragma", "ValidateUnusedExprs"); + } else if (normalizedPragma == "disablevalidateunusedexprs") { + Ctx.ValidateUnusedExprs = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableValidateUnusedExprs"); + } else if (normalizedPragma == "ansiimplicitcrossjoin") { + Ctx.AnsiImplicitCrossJoin = true; + Ctx.IncrementMonCounter("sql_pragma", "AnsiImplicitCrossJoin"); + } else if (normalizedPragma == "disableansiimplicitcrossjoin") { + Ctx.AnsiImplicitCrossJoin = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableAnsiImplicitCrossJoin"); + } else if (normalizedPragma == "distinctoverwindow") { + Ctx.DistinctOverWindow = true; + Ctx.IncrementMonCounter("sql_pragma", "DistinctOverWindow"); + } else if (normalizedPragma == "disabledistinctoverwindow") { + Ctx.DistinctOverWindow = false; + Ctx.IncrementMonCounter("sql_pragma", "DisableDistinctOverWindow"); + } else { + Error() << "Unknown pragma: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "UnknownPragma"); + return {}; + } + } else { + if (lowerPrefix == "yson") { + if (!TopLevel) { + Error() << "This pragma '" << pragma << "' is not allowed to be used in actions"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + if (normalizedPragma == "fast") { + Ctx.Warning(Ctx.Pos(), TIssuesIds::YQL_DEPRECATED_PRAGMA) + << "Use of deprecated yson.Fast pragma. It will be dropped soon"; + success = true; + return {}; + } else if (normalizedPragma == "autoconvert") { + Ctx.PragmaYsonAutoConvert = true; + success = true; + return {}; + } else if (normalizedPragma == "strict") { + if (values.size() == 0U) { + Ctx.PragmaYsonStrict = true; + success = true; + } else if (values.size() == 1U && values.front().GetLiteral() && TryFromString(*values.front().GetLiteral(), Ctx.PragmaYsonStrict)) { + success = true; + } else { + Error() << "Expected 'true', 'false' or no parameter for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + } + return {}; + } else if (normalizedPragma == "disablestrict") { + if (values.size() == 0U) { + Ctx.PragmaYsonStrict = false; + success = true; + return {}; + } + bool pragmaYsonDisableStrict; + if (values.size() == 1U && values.front().GetLiteral() && TryFromString(*values.front().GetLiteral(), pragmaYsonDisableStrict)) { + Ctx.PragmaYsonStrict = !pragmaYsonDisableStrict; + success = true; + } else { + Error() << "Expected 'true', 'false' or no parameter for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + } + return {}; + } else if (normalizedPragma == "casttostring" || normalizedPragma == "disablecasttostring") { + const bool allow = normalizedPragma == "casttostring"; + if (values.size() == 0U) { + Ctx.YsonCastToString = allow; + success = true; + return {}; + } + bool pragmaYsonCastToString; + if (values.size() == 1U && values.front().GetLiteral() && TryFromString(*values.front().GetLiteral(), pragmaYsonCastToString)) { + Ctx.PragmaYsonStrict = allow ? pragmaYsonCastToString : !pragmaYsonCastToString; + success = true; + } else { + Error() << "Expected 'true', 'false' or no parameter for: " << pragma; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + } + return {}; + } else { + Error() << "Unknown pragma: '" << pragma << "'"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + + } else if (std::find(Providers.cbegin(), Providers.cend(), lowerPrefix) == Providers.cend()) { + if (!Ctx.HasCluster(prefix)) { + Error() << "Unknown pragma prefix: " << prefix << ", please use cluster name or one of provider " << + JoinRange(", ", Providers.cbegin(), Providers.cend()); + Ctx.IncrementMonCounter("sql_errors", "UnknownPragma"); + return {}; + } + } + + if (normalizedPragma != "flags" && normalizedPragma != "packageversion") { + if (values.size() > 1) { + Error() << "Expected at most one value in the pragma"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + } else { + if (pragmaValueDefault || values.size() < 1) { + Error() << "Expected at least one value in the pragma"; + Ctx.IncrementMonCounter("sql_errors", "BadPragmaValue"); + return {}; + } + } + + success = true; + Ctx.IncrementMonCounter("sql_pragma", pragma); + return BuildPragma(Ctx.Pos(), lowerPrefix, normalizedPragma, values, pragmaValueDefault); + } + success = true; + return {}; +} + +TNodePtr TSqlQuery::Build(const TRule_delete_stmt& stmt) { + TTableRef table; + if (!SimpleTableRefImpl(stmt.GetRule_simple_table_ref3(), table)) { + return nullptr; + } + + const bool isKikimr = table.Service == KikimrProviderName; + if (!isKikimr) { + Ctx.Error(GetPos(stmt.GetToken1())) << "DELETE is unsupported for " << table.Service; + return nullptr; + } + + TSourcePtr source = BuildTableSource(Ctx.Pos(), table); + + TNodePtr options = nullptr; + if (stmt.HasBlock5()) { + options = ReturningList(stmt.GetBlock5().GetRule_returning_columns_list1()); + options = options->Y(options); + } + + if (stmt.HasBlock4()) { + switch (stmt.GetBlock4().Alt_case()) { + case TRule_delete_stmt_TBlock4::kAlt1: { + const auto& alt = stmt.GetBlock4().GetAlt1(); + + TColumnRefScope scope(Ctx, EColumnRefState::Allow); + TSqlExpression sqlExpr(Ctx, Mode); + auto whereExpr = sqlExpr.Build(alt.GetRule_expr2()); + if (!whereExpr) { + return nullptr; + } + source->AddFilter(Ctx, whereExpr); + break; + } + + case TRule_delete_stmt_TBlock4::kAlt2: { + const auto& alt = stmt.GetBlock4().GetAlt2(); + + auto values = TSqlIntoValues(Ctx, Mode).Build(alt.GetRule_into_values_source2(), "DELETE ON"); + if (!values) { + return nullptr; + } + + return BuildWriteColumns(Ctx.Pos(), Ctx.Scoped, table, EWriteColumnMode::DeleteOn, std::move(values), options); + } + + case TRule_delete_stmt_TBlock4::ALT_NOT_SET: + return nullptr; + } + } + + return BuildDelete(Ctx.Pos(), Ctx.Scoped, table, std::move(source), options); +} + +TNodePtr TSqlQuery::Build(const TRule_update_stmt& stmt) { + TTableRef table; + if (!SimpleTableRefImpl(stmt.GetRule_simple_table_ref2(), table)) { + return nullptr; + } + + const bool isKikimr = table.Service == KikimrProviderName; + + if (!isKikimr) { + Ctx.Error(GetPos(stmt.GetToken1())) << "UPDATE is unsupported for " << table.Service; + return nullptr; + } + + TNodePtr options = nullptr; + if (stmt.HasBlock4()) { + options = ReturningList(stmt.GetBlock4().GetRule_returning_columns_list1()); + options = options->Y(options); + } + + switch (stmt.GetBlock3().Alt_case()) { + case TRule_update_stmt_TBlock3::kAlt1: { + const auto& alt = stmt.GetBlock3().GetAlt1(); + TSourcePtr values = Build(alt.GetRule_set_clause_choice2()); + auto source = BuildTableSource(Ctx.Pos(), table); + + if (alt.HasBlock3()) { + TColumnRefScope scope(Ctx, EColumnRefState::Allow); + TSqlExpression sqlExpr(Ctx, Mode); + auto whereExpr = sqlExpr.Build(alt.GetBlock3().GetRule_expr2()); + if (!whereExpr) { + return nullptr; + } + source->AddFilter(Ctx, whereExpr); + } + + return BuildUpdateColumns(Ctx.Pos(), Ctx.Scoped, table, std::move(values), std::move(source), options); + } + + case TRule_update_stmt_TBlock3::kAlt2: { + const auto& alt = stmt.GetBlock3().GetAlt2(); + + auto values = TSqlIntoValues(Ctx, Mode).Build(alt.GetRule_into_values_source2(), "UPDATE ON"); + if (!values) { + return nullptr; + } + + return BuildWriteColumns(Ctx.Pos(), Ctx.Scoped, table, EWriteColumnMode::UpdateOn, std::move(values), options); + } + + case TRule_update_stmt_TBlock3::ALT_NOT_SET: + return nullptr; + } +} + +TSourcePtr TSqlQuery::Build(const TRule_set_clause_choice& stmt) { + switch (stmt.Alt_case()) { + case TRule_set_clause_choice::kAltSetClauseChoice1: + return Build(stmt.GetAlt_set_clause_choice1().GetRule_set_clause_list1()); + case TRule_set_clause_choice::kAltSetClauseChoice2: + return Build(stmt.GetAlt_set_clause_choice2().GetRule_multiple_column_assignment1()); + case TRule_set_clause_choice::ALT_NOT_SET: + AltNotImplemented("set_clause_choice", stmt); + return nullptr; + } +} + +bool TSqlQuery::FillSetClause(const TRule_set_clause& node, TVector<TString>& targetList, TVector<TNodePtr>& values) { + targetList.push_back(ColumnNameAsSingleStr(*this, node.GetRule_set_target1().GetRule_column_name1())); + TColumnRefScope scope(Ctx, EColumnRefState::Allow); + TSqlExpression sqlExpr(Ctx, Mode); + if (!Expr(sqlExpr, values, node.GetRule_expr3())) { + return false; + } + return true; +} + +TSourcePtr TSqlQuery::Build(const TRule_set_clause_list& stmt) { + TVector<TString> targetList; + TVector<TNodePtr> values; + const TPosition pos(Ctx.Pos()); + if (!FillSetClause(stmt.GetRule_set_clause1(), targetList, values)) { + return nullptr; + } + for (auto& block: stmt.GetBlock2()) { + if (!FillSetClause(block.GetRule_set_clause2(), targetList, values)) { + return nullptr; + } + } + Y_DEBUG_ABORT_UNLESS(targetList.size() == values.size()); + return BuildUpdateValues(pos, targetList, values); +} + +TSourcePtr TSqlQuery::Build(const TRule_multiple_column_assignment& stmt) { + TVector<TString> targetList; + FillTargetList(*this, stmt.GetRule_set_target_list1(), targetList); + auto simpleValuesNode = stmt.GetRule_simple_values_source4(); + const TPosition pos(Ctx.Pos()); + switch (simpleValuesNode.Alt_case()) { + case TRule_simple_values_source::kAltSimpleValuesSource1: { + TVector<TNodePtr> values; + TSqlExpression sqlExpr(Ctx, Mode); + if (!ExprList(sqlExpr, values, simpleValuesNode.GetAlt_simple_values_source1().GetRule_expr_list1())) { + return nullptr; + } + return BuildUpdateValues(pos, targetList, values); + } + case TRule_simple_values_source::kAltSimpleValuesSource2: { + TSqlSelect select(Ctx, Mode); + TPosition selectPos; + auto source = select.Build(simpleValuesNode.GetAlt_simple_values_source2().GetRule_select_stmt1(), selectPos); + if (!source) { + return nullptr; + } + return BuildWriteValues(pos, "UPDATE", targetList, std::move(source)); + } + case TRule_simple_values_source::ALT_NOT_SET: + Ctx.IncrementMonCounter("sql_errors", "UnknownSimpleValuesSourceAlt"); + AltNotImplemented("simple_values_source", simpleValuesNode); + return nullptr; + } +} + +TNodePtr TSqlQuery::Build(const TSQLv1ParserAST& ast) { + if (Mode == NSQLTranslation::ESqlMode::QUERY) { + // inject externally declared named expressions + for (auto [name, type] : Ctx.Settings.DeclaredNamedExprs) { + if (name.empty()) { + Error() << "Empty names for externally declared expressions are not allowed"; + return nullptr; + } + TString varName = "$" + name; + if (IsAnonymousName(varName)) { + Error() << "Externally declared name '" << name << "' is anonymous"; + return nullptr; + } + + auto parsed = ParseType(type, *Ctx.Pool, Ctx.Issues, Ctx.Pos()); + if (!parsed) { + Error() << "Failed to parse type for externally declared name '" << name << "'"; + return nullptr; + } + + TNodePtr typeNode = BuildBuiltinFunc(Ctx, Ctx.Pos(), "ParseType", { BuildLiteralRawString(Ctx.Pos(), type) }); + PushNamedAtom(Ctx.Pos(), varName); + // no duplicates are possible at this stage + bool isWeak = true; + Ctx.DeclareVariable(varName, {}, typeNode, isWeak); + // avoid 'Symbol is not used' warning for externally declared expression + YQL_ENSURE(GetNamedNode(varName)); + } + } + + const auto& query = ast.GetRule_sql_query(); + TVector<TNodePtr> blocks; + Ctx.PushCurrentBlocks(&blocks); + Y_DEFER { + Ctx.PopCurrentBlocks(); + }; + if (query.Alt_case() == TRule_sql_query::kAltSqlQuery1) { + const auto& statements = query.GetAlt_sql_query1().GetRule_sql_stmt_list1(); + if (!Statement(blocks, statements.GetRule_sql_stmt2().GetRule_sql_stmt_core2())) { + return nullptr; + } + for (auto block: statements.GetBlock3()) { + if (!Statement(blocks, block.GetRule_sql_stmt2().GetRule_sql_stmt_core2())) { + return nullptr; + } + } + } + + ui32 topLevelSelects = 0; + bool hasTailOps = false; + for (auto& block : blocks) { + if (block->SubqueryAlias()) { + continue; + } + + if (block->HasSelectResult()) { + ++topLevelSelects; + } else if (topLevelSelects) { + hasTailOps = true; + } + } + + if ((Mode == NSQLTranslation::ESqlMode::SUBQUERY || Mode == NSQLTranslation::ESqlMode::LIMITED_VIEW) && (topLevelSelects != 1 || hasTailOps)) { + Error() << "Strictly one select/process/reduce statement is expected at the end of " + << (Mode == NSQLTranslation::ESqlMode::LIMITED_VIEW ? "view" : "subquery"); + return nullptr; + } + + if (!Ctx.PragmaAutoCommit && Ctx.Settings.EndOfQueryCommit && IsQueryMode(Mode)) { + AddStatementToBlocks(blocks, BuildCommitClusters(Ctx.Pos())); + } + + auto result = BuildQuery(Ctx.Pos(), blocks, true, Ctx.Scoped); + WarnUnusedNodes(); + return result; +} + +TNodePtr TSqlQuery::Build(const std::vector<::NSQLv1Generated::TRule_sql_stmt_core>& statements) { + if (Mode == NSQLTranslation::ESqlMode::QUERY) { + // inject externally declared named expressions + for (auto [name, type] : Ctx.Settings.DeclaredNamedExprs) { + if (name.empty()) { + Error() << "Empty names for externally declared expressions are not allowed"; + return nullptr; + } + TString varName = "$" + name; + if (IsAnonymousName(varName)) { + Error() << "Externally declared name '" << name << "' is anonymous"; + return nullptr; + } + + auto parsed = ParseType(type, *Ctx.Pool, Ctx.Issues, Ctx.Pos()); + if (!parsed) { + Error() << "Failed to parse type for externally declared name '" << name << "'"; + return nullptr; + } + + TNodePtr typeNode = BuildBuiltinFunc(Ctx, Ctx.Pos(), "ParseType", { BuildLiteralRawString(Ctx.Pos(), type) }); + PushNamedAtom(Ctx.Pos(), varName); + // no duplicates are possible at this stage + bool isWeak = true; + Ctx.DeclareVariable(varName, {}, typeNode, isWeak); + // avoid 'Symbol is not used' warning for externally declared expression + YQL_ENSURE(GetNamedNode(varName)); + } + } + + TVector<TNodePtr> blocks; + Ctx.PushCurrentBlocks(&blocks); + Y_DEFER { + Ctx.PopCurrentBlocks(); + }; + for (const auto& statement : statements) { + if (!Statement(blocks, statement)) { + return nullptr; + } + } + + ui32 topLevelSelects = 0; + bool hasTailOps = false; + for (auto& block : blocks) { + if (block->SubqueryAlias()) { + continue; + } + + if (block->HasSelectResult()) { + ++topLevelSelects; + } else if (topLevelSelects) { + hasTailOps = true; + } + } + + if ((Mode == NSQLTranslation::ESqlMode::SUBQUERY || Mode == NSQLTranslation::ESqlMode::LIMITED_VIEW) && (topLevelSelects != 1 || hasTailOps)) { + Error() << "Strictly one select/process/reduce statement is expected at the end of " + << (Mode == NSQLTranslation::ESqlMode::LIMITED_VIEW ? "view" : "subquery"); + return nullptr; + } + + if (!Ctx.PragmaAutoCommit && Ctx.Settings.EndOfQueryCommit && IsQueryMode(Mode)) { + AddStatementToBlocks(blocks, BuildCommitClusters(Ctx.Pos())); + } + + auto result = BuildQuery(Ctx.Pos(), blocks, true, Ctx.Scoped); + return result; +} +namespace { + + static bool BuildColumnFeatures(std::map<TString, TDeferredAtom>& result, const TRule_column_schema& columnSchema, const NYql::TPosition& pos, TSqlTranslation& translation) { + const TString columnName(Id(columnSchema.GetRule_an_id_schema1(), translation)); + TString columnType; + + const auto constraints = ColumnConstraints(columnSchema, translation); + if (!constraints) { + return false; + } + + auto& typeBind = columnSchema.GetRule_type_name_or_bind2(); + switch (typeBind.Alt_case()) { + case TRule_type_name_or_bind::kAltTypeNameOrBind1: + { + auto& typeNameOrBind = typeBind.GetAlt_type_name_or_bind1().GetRule_type_name1(); + if (typeNameOrBind.Alt_case() != TRule_type_name::kAltTypeName2) { + return false; + } + auto& alt = typeNameOrBind.GetAlt_type_name2(); + auto& block = alt.GetBlock1(); + auto& simpleType = block.GetAlt2().GetRule_type_name_simple1(); + columnType = Id(simpleType.GetRule_an_id_pure1(), translation); + if (columnType.empty()) { + return false; + } + break; + } + case TRule_type_name_or_bind::kAltTypeNameOrBind2: + return false; + case TRule_type_name_or_bind::ALT_NOT_SET: + Y_ABORT("You should change implementation according to grammar changes"); + } + + result["NAME"] = TDeferredAtom(pos, columnName); + YQL_ENSURE(columnType, "Unknown column type"); + result["TYPE"] = TDeferredAtom(pos, columnType); + if (!constraints->Nullable) { + result["NOT_NULL"] = TDeferredAtom(pos, "true"); + } + return true; + } +} + +bool TSqlQuery::ParseTableStoreFeatures(std::map<TString, TDeferredAtom> & result, const TRule_alter_table_store_action & actions) { + switch (actions.Alt_case()) { + case TRule_alter_table_store_action::kAltAlterTableStoreAction1: { + // ADD COLUMN + const auto& addRule = actions.GetAlt_alter_table_store_action1().GetRule_alter_table_add_column1(); + if (!BuildColumnFeatures(result, addRule.GetRule_column_schema3(), Ctx.Pos(), *this)) { + return false; + } + result["ACTION"] = TDeferredAtom(Ctx.Pos(), "NEW_COLUMN"); + break; + } + case TRule_alter_table_store_action::kAltAlterTableStoreAction2: { + // DROP COLUMN + const auto& dropRule = actions.GetAlt_alter_table_store_action2().GetRule_alter_table_drop_column1(); + TString columnName = Id(dropRule.GetRule_an_id3(), *this); + if (!columnName) { + return false; + } + result["NAME"] = TDeferredAtom(Ctx.Pos(), columnName); + result["ACTION"] = TDeferredAtom(Ctx.Pos(), "DROP_COLUMN"); + break; + } + case TRule_alter_table_store_action::ALT_NOT_SET: + Y_ABORT("You should change implementation according to grammar changes"); + } + return true; +} + +} // namespace NSQLTranslationV1 |