diff options
author | deminds <[email protected]> | 2025-06-04 12:36:47 +0300 |
---|---|---|
committer | deminds <[email protected]> | 2025-06-04 12:51:33 +0300 |
commit | 14c42c6f9a85a081cde9ba58db0b008b3ade11f7 (patch) | |
tree | 7d76d30ab6610e938b0399956cd45fcc5105854b /yql/essentials | |
parent | 62b27c7d93a63b7c2ab41fe6882cbcbdfc5218a6 (diff) |
support database settings in ALTER DATABASE
The main goal is to enable setting SchemeLimits (ShardsLimit and PathsLimit) via YQL.
Issue:
- https://github.com/ydb-platform/ydb/issues/16742
commit_hash:e873849e3b4bb494e349ccebbb1ff6d51655c5d1
Diffstat (limited to 'yql/essentials')
-rw-r--r-- | yql/essentials/sql/v1/SQLv1.g.in | 14 | ||||
-rw-r--r-- | yql/essentials/sql/v1/SQLv1Antlr4.g.in | 14 | ||||
-rw-r--r-- | yql/essentials/sql/v1/format/sql_format_ut.h | 2 | ||||
-rw-r--r-- | yql/essentials/sql/v1/node.h | 1 | ||||
-rw-r--r-- | yql/essentials/sql/v1/object_processing.cpp | 2 | ||||
-rw-r--r-- | yql/essentials/sql/v1/query.cpp | 24 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_query.cpp | 45 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_translation.cpp | 82 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_translation.h | 2 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_ut_common.h | 39 |
10 files changed, 208 insertions, 17 deletions
diff --git a/yql/essentials/sql/v1/SQLv1.g.in b/yql/essentials/sql/v1/SQLv1.g.in index ed04c337507..96013b9a7fa 100644 --- a/yql/essentials/sql/v1/SQLv1.g.in +++ b/yql/essentials/sql/v1/SQLv1.g.in @@ -693,7 +693,19 @@ table_tablestore: TABLESTORE simple_table_ref_core; table_settings_entry: an_id EQUALS table_setting_value; table_as_source: AS values_source; -alter_database_stmt: ALTER DATABASE an_id_schema OWNER TO role_name; +alter_database_stmt: ALTER DATABASE an_id_schema alter_database_action; +alter_database_action: + OWNER TO role_name + | set_database_settings +; +set_database_settings: SET LPAREN database_settings RPAREN; +database_settings: database_setting (COMMA database_setting)*; +database_setting: an_id EQUALS database_setting_value; +database_setting_value: + bool_value + | integer + | STRING_VALUE +; alter_table_stmt: ALTER TABLE simple_table_ref alter_table_action (COMMA alter_table_action)*; alter_table_action: diff --git a/yql/essentials/sql/v1/SQLv1Antlr4.g.in b/yql/essentials/sql/v1/SQLv1Antlr4.g.in index 6bfdf3820f7..5ac9de84998 100644 --- a/yql/essentials/sql/v1/SQLv1Antlr4.g.in +++ b/yql/essentials/sql/v1/SQLv1Antlr4.g.in @@ -685,7 +685,19 @@ backup_collection_settings_entry: an_id EQUALS table_setting_value; backup_stmt: BACKUP object_ref (INCREMENTAL)?; restore_stmt: RESTORE object_ref (AT STRING_VALUE)?; -alter_database_stmt: ALTER DATABASE an_id_schema OWNER TO role_name; +alter_database_stmt: ALTER DATABASE an_id_schema alter_database_action; +alter_database_action: + OWNER TO role_name + | set_database_settings +; +set_database_settings: SET LPAREN database_settings RPAREN; +database_settings: database_setting (COMMA database_setting)*; +database_setting: an_id EQUALS database_setting_value; +database_setting_value: + bool_value + | integer + | STRING_VALUE +; table_inherits: INHERITS LPAREN simple_table_ref_core (COMMA simple_table_ref_core)* RPAREN; table_partition_by: PARTITION BY HASH pure_column_list; diff --git a/yql/essentials/sql/v1/format/sql_format_ut.h b/yql/essentials/sql/v1/format/sql_format_ut.h index da60492b238..b61cc593936 100644 --- a/yql/essentials/sql/v1/format/sql_format_ut.h +++ b/yql/essentials/sql/v1/format/sql_format_ut.h @@ -23,6 +23,8 @@ Y_UNIT_TEST(DotAfterDigits) { Y_UNIT_TEST(AlterDatabase) { TCases cases { {"use plato;alter database `/Root/test` owner to user1;", "USE plato;\n\nALTER DATABASE `/Root/test` OWNER TO user1;\n"}, + {"use plato;alter database `/Root/test` set (key1 = 1);", "USE plato;\n\nALTER DATABASE `/Root/test` SET (key1 = 1);\n"}, + {"use plato;alter database `/Root/test` set (\n\tkey1 = 1,\n\tkey2 = \"2\"\n);", "USE plato;\n\nALTER DATABASE `/Root/test` SET (key1 = 1, key2 = '2');\n"} }; TSetup setup; diff --git a/yql/essentials/sql/v1/node.h b/yql/essentials/sql/v1/node.h index c0e9e838ca7..bb4448df2aa 100644 --- a/yql/essentials/sql/v1/node.h +++ b/yql/essentials/sql/v1/node.h @@ -1281,6 +1281,7 @@ namespace NSQLTranslationV1 { struct TAlterDatabaseParameters { TDeferredAtom DbPath; std::optional<TDeferredAtom> Owner; + THashMap<TString, TNodePtr> DatabaseSettings; }; struct TTableRef; diff --git a/yql/essentials/sql/v1/object_processing.cpp b/yql/essentials/sql/v1/object_processing.cpp index 69d6ae342a4..fed6b422195 100644 --- a/yql/essentials/sql/v1/object_processing.cpp +++ b/yql/essentials/sql/v1/object_processing.cpp @@ -59,7 +59,7 @@ INode::TPtr TCreateObject::FillFeatures(INode::TPtr options) const { namespace { -bool InitFeatures(TContext& ctx, ISource* src, std::map<TString, TDeferredAtom>& features) { +bool InitFeatures(TContext& ctx, ISource* src, const std::map<TString, TDeferredAtom>& features) { for (auto& [key, value] : features) { if (value.HasNode() && !value.Build()->Init(ctx, src)) { return false; diff --git a/yql/essentials/sql/v1/query.cpp b/yql/essentials/sql/v1/query.cpp index 40caf8b34de..631aa097b58 100644 --- a/yql/essentials/sql/v1/query.cpp +++ b/yql/essentials/sql/v1/query.cpp @@ -1317,6 +1317,19 @@ TNodePtr BuildCreateTable(TPosition pos, const TTableRef& tr, bool existingOk, b return new TCreateTableNode(pos, tr, existingOk, replaceIfExists, params, std::move(values), scoped); } +namespace { + +bool InitDatabaseSettings(TContext& ctx, ISource* src, const THashMap<TString, TNodePtr>& settings) { + for (const auto& [setting, value] : settings) { + if (!value || !value->Init(ctx, src)) { + return false; + } + } + return true; +} + +} + class TAlterDatabaseNode final : public TAstListNode { public: TAlterDatabaseNode( @@ -1343,6 +1356,10 @@ public: if (Params.Owner.has_value()) { options = L(options, Q(Y(Q("owner"), Params.Owner.value().Build()))); } + if (!InitDatabaseSettings(ctx, src, Params.DatabaseSettings)) { + return false; + } + AddDatabaseSettings(options, Params.DatabaseSettings); Add("block", Q(Y( Y("let", "sink", Y("DataSink", BuildQuotedAtom(Pos, Service), cluster)), @@ -1362,6 +1379,13 @@ private: TScopedStatePtr Scoped; TDeferredAtom Cluster; TString Service; + + void AddDatabaseSettings(TNodePtr& options, const THashMap<TString, TNodePtr>& settings) { + for (const auto& [setting, value] : settings) { + options = L(options, Q(Y(BuildQuotedAtom(Pos, setting), value))); + } + } + }; TNodePtr BuildAlterDatabase( diff --git a/yql/essentials/sql/v1/sql_query.cpp b/yql/essentials/sql/v1/sql_query.cpp index 8ba4226d684..d852fee277f 100644 --- a/yql/essentials/sql/v1/sql_query.cpp +++ b/yql/essentials/sql/v1/sql_query.cpp @@ -1937,26 +1937,45 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core& break; } case TRule_sql_stmt_core::kAltSqlStmtCore61: { - // alter_database_stmt: ALTER DATABASE an_id_schema OWNER TO role_name - auto& node = core.GetAlt_sql_stmt_core61().GetRule_alter_database_stmt1(); - - TDeferredAtom roleName; - { - bool allowSystemRoles = true; - if (!RoleNameClause(node.GetRule_role_name6(), roleName, allowSystemRoles)) { - return false; + // alter_database_stmt: ALTER DATABASE an_id_schema alter_database_action + const auto& node = core.GetAlt_sql_stmt_core61().GetRule_alter_database_stmt1(); + const auto& action = node.GetRule_alter_database_action4(); + + TAlterDatabaseParameters params; + params.DbPath = TDeferredAtom(Ctx.Pos(), Id(node.GetRule_an_id_schema3(), *this)); + + switch (action.GetAltCase()) { + case TRule_alter_database_action::kAltAlterDatabaseAction1: { + // OWNER TO role_name + const auto& ownerAction = action.GetAlt_alter_database_action1(); + TDeferredAtom roleName; + { + bool allowSystemRoles = true; + if (!RoleNameClause(ownerAction.GetRule_role_name3(), roleName, allowSystemRoles)) { + return false; + } + } + params.Owner = roleName; + break; } + case TRule_alter_database_action::kAltAlterDatabaseAction2: { + // SET ( database_settings ) + const auto& settings = action.GetAlt_alter_database_action2().GetRule_set_database_settings1().GetRule_database_settings3(); + if (!ParseDatabaseSettings(settings, params.DatabaseSettings)) { + return false; + } + break; + } + case TRule_alter_database_action::ALT_NOT_SET: + AltNotImplemented("alter_database_action", action); + return false; } - TAlterDatabaseParameters alterDatabaseParams; - alterDatabaseParams.Owner = roleName; - alterDatabaseParams.DbPath = TDeferredAtom(Ctx.Pos(), Id(node.GetRule_an_id_schema3(), *this)); - const TPosition pos = Ctx.Pos(); TString service = Ctx.Scoped->CurrService; TDeferredAtom cluster = Ctx.Scoped->CurrCluster; - auto stmt = BuildAlterDatabase(pos, service, cluster, alterDatabaseParams, Ctx.Scoped); + auto stmt = BuildAlterDatabase(pos, service, cluster, params, Ctx.Scoped); AddStatementToBlocks(blocks, stmt); break; } diff --git a/yql/essentials/sql/v1/sql_translation.cpp b/yql/essentials/sql/v1/sql_translation.cpp index 264601ed460..e96096ccbda 100644 --- a/yql/essentials/sql/v1/sql_translation.cpp +++ b/yql/essentials/sql/v1/sql_translation.cpp @@ -745,6 +745,88 @@ bool TSqlTranslation::CreateTableIndex(const TRule_table_index& node, TVector<TI return true; } +bool TSqlTranslation::ParseDatabaseSettings(const TRule_database_settings& in, THashMap<TString, TNodePtr>& out) { + if (!ParseDatabaseSetting(in.GetRule_database_setting1(), out)) { + return false; + } + for (const auto& setting : in.GetBlock2()) { + if (!ParseDatabaseSetting(setting.GetRule_database_setting2(), out)) { + return false; + } + } + return true; +} + +namespace { + +TMaybe<bool> ParseBool(TContext& ctx, const TRule_bool_value& node) { + bool value = false; + const TString& token = ctx.Token(node.GetToken1()); + if (!TryFromString<bool>(token, value)) { + ctx.Error() << "Cannot parse bool from " << token; + return Nothing(); + } + return value; +} + +TMaybe<ui64> ParseInteger(TContext& ctx, const TRule_integer& node) { + ui64 value = 0; + const TString& token = ctx.Token(node.GetToken1()); + TString suffix; + if (!ParseNumbers(ctx, token, value, suffix)) { + ctx.Error() << "Cannot parse integer from " << token; + return Nothing(); + } + return value; +} + +TNodePtr ParseDatabaseSettingValue(TContext& ctx, const TRule_database_setting_value& node) { + switch (node.GetAltCase()) { + case TRule_database_setting_value::kAltDatabaseSettingValue1: + // bool + if (auto result = ParseBool(ctx, node.GetAlt_database_setting_value1().GetRule_bool_value1())) { + return BuildLiteralBool(ctx.Pos(), *result); + } + return nullptr; + + case TRule_database_setting_value::kAltDatabaseSettingValue2: + // integer + if (auto result = ParseInteger(ctx, node.GetAlt_database_setting_value2().GetRule_integer1())) { + return MakeIntrusive<TLiteralNumberNode<ui64>>(ctx.Pos(), "Uint64", ToString(*result)); + } + return nullptr; + + case TRule_database_setting_value::kAltDatabaseSettingValue3: { + // string + const auto& token = node.GetAlt_database_setting_value3().GetToken1(); + if (auto result = BuildLiteralSmartString(ctx, ctx.Token(token))) { + return result; + } + return nullptr; + } + case TRule_database_setting_value::ALT_NOT_SET: + YQL_ENSURE(false, "You should change implementation according to grammar changes."); + } +} + +} + +bool TSqlTranslation::ParseDatabaseSetting(const TRule_database_setting& in, THashMap<TString, TNodePtr>& out) { + const auto setting = to_upper(Id(in.GetRule_an_id1(), *this)); + + if (out.contains(setting)) { + Ctx.Error() << "Duplicate setting: " << setting; + return false; + } + + auto node = ParseDatabaseSettingValue(Ctx, in.GetRule_database_setting_value3()); + if (!node) { + return false; + } + out[setting] = node; + return true; +} + bool TSqlTranslation::CreateIndexSettings(const TRule_with_index_settings& settingsNode, TIndexDescription::EType indexType, TIndexDescription::TIndexSettings& indexSettings) { diff --git a/yql/essentials/sql/v1/sql_translation.h b/yql/essentials/sql/v1/sql_translation.h index a052f1979b0..99192e4e102 100644 --- a/yql/essentials/sql/v1/sql_translation.h +++ b/yql/essentials/sql/v1/sql_translation.h @@ -267,6 +267,8 @@ protected: TVector<TDeferredAtom>& removeTables, const TRule_alter_backup_collection_entries& entries); bool ParseTransferLambda(TString& lambdaText, const TRule_lambda_or_parameter& lambdaOrParameter); + bool ParseDatabaseSettings(const TRule_database_settings& in, THashMap<TString, TNodePtr>& out); + bool ParseDatabaseSetting(const TRule_database_setting& in, THashMap<TString, TNodePtr>& out); bool ValidateAuthMethod(const std::map<TString, TDeferredAtom>& result); bool ValidateExternalTable(const TCreateTableParameters& params); diff --git a/yql/essentials/sql/v1/sql_ut_common.h b/yql/essentials/sql/v1/sql_ut_common.h index d4f9285a9e3..8b33aa8be0d 100644 --- a/yql/essentials/sql/v1/sql_ut_common.h +++ b/yql/essentials/sql/v1/sql_ut_common.h @@ -1192,7 +1192,7 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { Y_UNIT_TEST(AlterDatabaseAst) { NYql::TAstParseResult request = SqlToYql("USE plato; ALTER DATABASE `/Root/test` OWNER TO user1;"); - UNIT_ASSERT(request.IsOk()); + UNIT_ASSERT_C(request.IsOk(), request.Issues.ToString()); TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { Y_UNUSED(word); @@ -1207,6 +1207,43 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["\'mode \'alterDatabase"]); } + Y_UNIT_TEST(AlterDatabaseSetting) { + NYql::TAstParseResult res = SqlToYql("USE plato; ALTER DATABASE `/Root/test` SET (key1 = 1);"); + UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString()); + + TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) { + if (word == "Write!") { + UNIT_ASSERT_STRING_CONTAINS(line, "(Key '('databasePath (String '\"/Root/test\")))"); + UNIT_ASSERT_STRING_CONTAINS(line, "'('('mode 'alterDatabase) '('\"KEY1\" (Uint64 '\"1\")))"); + } + }; + + TWordCountHive elementStat = { {"Write!"} }; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1); + } + + Y_UNIT_TEST(AlterDatabaseSettings) { + NYql::TAstParseResult res = SqlToYql("USE plato; ALTER DATABASE `/Root/test` SET (key1 = 1, key2 = \"2\", key3 = true);"); + UNIT_ASSERT_C(res.IsOk(), res.Issues.ToString()); + + TVerifyLineFunc verifyLine = [&](const TString& word, const TString& line) { + if (word == "Write!") { + UNIT_ASSERT_STRING_CONTAINS(line, "(Key '('databasePath (String '\"/Root/test\")))"); + UNIT_ASSERT_STRING_CONTAINS(line, "'('mode 'alterDatabase)"); + UNIT_ASSERT_STRING_CONTAINS(line, "'('\"KEY1\" (Uint64 '\"1\"))"); + UNIT_ASSERT_STRING_CONTAINS(line, "'('\"KEY2\" (String '\"2\"))"); + UNIT_ASSERT_STRING_CONTAINS(line, "'('\"KEY3\" (Bool '\"true\"))"); + } + }; + + TWordCountHive elementStat = { {"Write!"} }; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(elementStat["Write!"], 1); + } + Y_UNIT_TEST(CreateTableNonNullableYqlTypeAstCorrect) { NYql::TAstParseResult res = SqlToYql("USE plato; CREATE TABLE t (a int32 not null);"); UNIT_ASSERT(res.Root); |