Skip to content

Commit 2e0fad1

Browse files
committed
YQ-3491 support sql for resource pool classifiers (#7389)
1 parent f31bfa8 commit 2e0fad1

File tree

9 files changed

+310
-23
lines changed

9 files changed

+310
-23
lines changed

ydb/core/kqp/ut/scheme/kqp_scheme_ut.cpp

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6200,8 +6200,7 @@ Y_UNIT_TEST_SUITE(KqpScheme) {
62006200
// ALTER RESOURCE POOL
62016201
checkDisabled(R"(
62026202
ALTER RESOURCE POOL MyResourcePool
6203-
SET (CONCURRENT_QUERY_LIMIT = 30),
6204-
SET QUEUE_SIZE 100,
6203+
SET (CONCURRENT_QUERY_LIMIT = 30, QUEUE_SIZE = 100),
62056204
RESET (QUERY_MEMORY_LIMIT_PERCENT_PER_NODE);
62066205
)");
62076206

@@ -6238,7 +6237,7 @@ Y_UNIT_TEST_SUITE(KqpScheme) {
62386237

62396238
result = session.ExecuteSchemeQuery(R"(
62406239
ALTER RESOURCE POOL MyResourcePool
6241-
SET ANOTHER_LIMIT 5,
6240+
SET (ANOTHER_LIMIT = 5),
62426241
RESET (SOME_LIMIT);
62436242
)").GetValueSync();
62446243
UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::GENERIC_ERROR);
@@ -6351,8 +6350,7 @@ Y_UNIT_TEST_SUITE(KqpScheme) {
63516350
{
63526351
auto query = R"(
63536352
ALTER RESOURCE POOL MyResourcePool
6354-
SET (CONCURRENT_QUERY_LIMIT = 30),
6355-
SET QUEUE_SIZE 100,
6353+
SET (CONCURRENT_QUERY_LIMIT = 30, QUEUE_SIZE = 100),
63566354
RESET (QUERY_MEMORY_LIMIT_PERCENT_PER_NODE);
63576355
)";
63586356
auto result = session.ExecuteSchemeQuery(query).GetValueSync();
@@ -6381,8 +6379,7 @@ Y_UNIT_TEST_SUITE(KqpScheme) {
63816379

63826380
auto query = R"(
63836381
ALTER RESOURCE POOL MyResourcePool
6384-
SET (CONCURRENT_QUERY_LIMIT = 30),
6385-
SET QUEUE_SIZE 100,
6382+
SET (CONCURRENT_QUERY_LIMIT = 30, QUEUE_SIZE = 100),
63866383
RESET (QUERY_MEMORY_LIMIT_PERCENT_PER_NODE);
63876384
)";
63886385
auto result = session.ExecuteSchemeQuery(query).GetValueSync();

ydb/library/yql/sql/v1/SQLv1.g.in

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ sql_stmt_core:
6666
| create_resource_pool_stmt
6767
| alter_resource_pool_stmt
6868
| drop_resource_pool_stmt
69+
| create_resource_pool_classifier_stmt
70+
| alter_resource_pool_classifier_stmt
71+
| drop_resource_pool_classifier_stmt
6972
;
7073

7174
expr:
@@ -809,13 +812,26 @@ alter_resource_pool_stmt: ALTER RESOURCE POOL object_ref
809812
alter_resource_pool_action (COMMA alter_resource_pool_action)*
810813
;
811814
alter_resource_pool_action:
812-
alter_table_set_table_setting_uncompat
813-
| alter_table_set_table_setting_compat
815+
alter_table_set_table_setting_compat
814816
| alter_table_reset_table_setting
815817
;
816818

817819
drop_resource_pool_stmt: DROP RESOURCE POOL object_ref;
818820

821+
create_resource_pool_classifier_stmt: CREATE RESOURCE POOL CLASSIFIER object_ref
822+
with_table_settings
823+
;
824+
825+
alter_resource_pool_classifier_stmt: ALTER RESOURCE POOL CLASSIFIER object_ref
826+
alter_resource_pool_classifier_action (COMMA alter_resource_pool_classifier_action)*
827+
;
828+
alter_resource_pool_classifier_action:
829+
alter_table_set_table_setting_compat
830+
| alter_table_reset_table_setting
831+
;
832+
833+
drop_resource_pool_classifier_stmt: DROP RESOURCE POOL CLASSIFIER object_ref;
834+
819835
create_replication_stmt: CREATE ASYNC REPLICATION object_ref
820836
FOR replication_target (COMMA replication_target)*
821837
WITH LPAREN replication_settings RPAREN
@@ -1200,6 +1216,7 @@ keyword_as_compat:
12001216
| CASCADE
12011217
| CHANGEFEED
12021218
| CHECK
1219+
| CLASSIFIER
12031220
// | COLLATE
12041221
| COMMIT
12051222
| CONDITIONAL
@@ -1412,6 +1429,7 @@ keyword_compat: (
14121429
| CASCADE
14131430
| CHANGEFEED
14141431
| CHECK
1432+
| CLASSIFIER
14151433
| COLLATE
14161434
| COMMIT
14171435
| CONDITIONAL
@@ -1728,6 +1746,7 @@ CASE: C A S E;
17281746
CAST: C A S T;
17291747
CHANGEFEED: C H A N G E F E E D;
17301748
CHECK: C H E C K;
1749+
CLASSIFIER: C L A S S I F I E R;
17311750
COLLATE: C O L L A T E;
17321751
COLUMN: C O L U M N;
17331752
COLUMNS: C O L U M N S;

ydb/library/yql/sql/v1/format/sql_format.cpp

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1527,6 +1527,39 @@ friend struct TStaticData;
15271527
VisitAllFields(TRule_drop_resource_pool_stmt::GetDescriptor(), msg);
15281528
}
15291529

1530+
void VisitCreateResourcePoolClassifier(const TRule_create_resource_pool_classifier_stmt& msg) {
1531+
PosFromToken(msg.GetToken1());
1532+
NewLine();
1533+
VisitAllFields(TRule_create_resource_pool_classifier_stmt::GetDescriptor(), msg);
1534+
}
1535+
1536+
void VisitAlterResourcePoolClassifier(const TRule_alter_resource_pool_classifier_stmt& msg) {
1537+
PosFromToken(msg.GetToken1());
1538+
NewLine();
1539+
VisitToken(msg.GetToken1());
1540+
VisitToken(msg.GetToken2());
1541+
VisitToken(msg.GetToken3());
1542+
VisitToken(msg.GetToken4());
1543+
Visit(msg.GetRule_object_ref5());
1544+
1545+
NewLine();
1546+
PushCurrentIndent();
1547+
Visit(msg.GetRule_alter_resource_pool_classifier_action6());
1548+
for (const auto& action : msg.GetBlock7()) {
1549+
Visit(action.GetToken1()); // comma
1550+
NewLine();
1551+
Visit(action.GetRule_alter_resource_pool_classifier_action2());
1552+
}
1553+
1554+
PopCurrentIndent();
1555+
}
1556+
1557+
void VisitDropResourcePoolClassifier(const TRule_drop_resource_pool_classifier_stmt& msg) {
1558+
PosFromToken(msg.GetToken1());
1559+
NewLine();
1560+
VisitAllFields(TRule_drop_resource_pool_classifier_stmt::GetDescriptor(), msg);
1561+
}
1562+
15301563
void VisitAllFields(const NProtoBuf::Descriptor* descr, const NProtoBuf::Message& msg) {
15311564
VisitAllFieldsImpl<TPrettyVisitor, &TPrettyVisitor::Visit>(this, descr, msg);
15321565
}
@@ -2743,7 +2776,10 @@ TStaticData::TStaticData()
27432776
{TRule_drop_view_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitDropView)},
27442777
{TRule_create_resource_pool_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitCreateResourcePool)},
27452778
{TRule_alter_resource_pool_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitAlterResourcePool)},
2746-
{TRule_drop_resource_pool_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitDropResourcePool)}
2779+
{TRule_drop_resource_pool_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitDropResourcePool)},
2780+
{TRule_create_resource_pool_classifier_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitCreateResourcePoolClassifier)},
2781+
{TRule_alter_resource_pool_classifier_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitAlterResourcePoolClassifier)},
2782+
{TRule_drop_resource_pool_classifier_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitDropResourcePoolClassifier)}
27472783
})
27482784
, ObfuscatingVisitDispatch({
27492785
{TToken::GetDescriptor(), MakeObfuscatingFunctor(&TObfuscatingVisitor::VisitToken)},

ydb/library/yql/sql/v1/format/sql_format_ut.cpp

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1588,8 +1588,8 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A);
15881588
"CREATE RESOURCE POOL naMe WITH (a = \"b\");\n"},
15891589
{"create resource pool eds with (a=\"a\",b=\"b\",c = true)",
15901590
"CREATE RESOURCE POOL eds WITH (\n\ta = \"a\",\n\tb = \"b\",\n\tc = TRUE\n);\n"},
1591-
{"alTer reSOurcE poOl naMe sEt a tRue, resEt (b, c), seT (x=y, z=false)",
1592-
"ALTER RESOURCE POOL naMe\n\tSET a TRUE,\n\tRESET (b, c),\n\tSET (x = y, z = FALSE);\n"},
1591+
{"alTer reSOurcE poOl naMe resEt (b, c), seT (x=y, z=false)",
1592+
"ALTER RESOURCE POOL naMe\n\tRESET (b, c),\n\tSET (x = y, z = FALSE);\n"},
15931593
{"alter resource pool eds reset (a), set (x=y)",
15941594
"ALTER RESOURCE POOL eds\n\tRESET (a),\n\tSET (x = y);\n"},
15951595
{"dRop reSourCe poOl naMe",
@@ -1599,4 +1599,22 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A);
15991599
TSetup setup;
16001600
setup.Run(cases);
16011601
}
1602+
1603+
Y_UNIT_TEST(ResourcePoolClassifierOperations) {
1604+
TCases cases = {
1605+
{"creAte reSourCe poOl ClaSsiFIer naMe With (a = \"b\")",
1606+
"CREATE RESOURCE POOL CLASSIFIER naMe WITH (a = \"b\");\n"},
1607+
{"create resource pool classifier eds with (a=\"a\",b=\"b\",c = true)",
1608+
"CREATE RESOURCE POOL CLASSIFIER eds WITH (\n\ta = \"a\",\n\tb = \"b\",\n\tc = TRUE\n);\n"},
1609+
{"alTer reSOurcE poOl ClaSsiFIer naMe resEt (b, c), seT (x=y, z=false)",
1610+
"ALTER RESOURCE POOL CLASSIFIER naMe\n\tRESET (b, c),\n\tSET (x = y, z = FALSE);\n"},
1611+
{"alter resource pool classifier eds reset (a), set (x=y)",
1612+
"ALTER RESOURCE POOL CLASSIFIER eds\n\tRESET (a),\n\tSET (x = y);\n"},
1613+
{"dRop reSourCe poOl ClaSsiFIer naMe",
1614+
"DROP RESOURCE POOL CLASSIFIER naMe;\n"},
1615+
};
1616+
1617+
TSetup setup;
1618+
setup.Run(cases);
1619+
}
16021620
}

ydb/library/yql/sql/v1/sql.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ bool NeedUseForAllStatements(const TRule_sql_stmt_core::AltCase& subquery) {
167167
case TRule_sql_stmt_core::kAltSqlStmtCore45: // create resource pool
168168
case TRule_sql_stmt_core::kAltSqlStmtCore46: // alter resource pool
169169
case TRule_sql_stmt_core::kAltSqlStmtCore47: // drop resource pool
170+
case TRule_sql_stmt_core::kAltSqlStmtCore48: // create resource pool classifier
171+
case TRule_sql_stmt_core::kAltSqlStmtCore49: // alter resource pool classifier
172+
case TRule_sql_stmt_core::kAltSqlStmtCore50: // drop resource pool classifier
170173
return false;
171174
}
172175
}

ydb/library/yql/sql/v1/sql_query.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,6 +1344,69 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core&
13441344
AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL", false, {}, context));
13451345
break;
13461346
}
1347+
case TRule_sql_stmt_core::kAltSqlStmtCore48: {
1348+
// create_resource_pool_classifier_stmt: CREATE RESOURCE POOL CLASSIFIER name WITH (k=v,...);
1349+
auto& node = core.GetAlt_sql_stmt_core48().GetRule_create_resource_pool_classifier_stmt1();
1350+
TObjectOperatorContext context(Ctx.Scoped);
1351+
if (node.GetRule_object_ref5().HasBlock1()) {
1352+
if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(),
1353+
false, context.ServiceId, context.Cluster)) {
1354+
return false;
1355+
}
1356+
}
1357+
1358+
const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second;
1359+
std::map<TString, TDeferredAtom> kv;
1360+
if (!ParseResourcePoolClassifierSettings(kv, node.GetRule_with_table_settings6())) {
1361+
return false;
1362+
}
1363+
1364+
AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL_CLASSIFIER", false, false, std::move(kv), context));
1365+
break;
1366+
}
1367+
case TRule_sql_stmt_core::kAltSqlStmtCore49: {
1368+
// alter_resource_pool_classifier_stmt: ALTER RESOURCE POOL CLASSIFIER object_ref alter_resource_pool_classifier_action (COMMA alter_resource_pool_classifier_action)*
1369+
Ctx.BodyPart();
1370+
const auto& node = core.GetAlt_sql_stmt_core49().GetRule_alter_resource_pool_classifier_stmt1();
1371+
TObjectOperatorContext context(Ctx.Scoped);
1372+
if (node.GetRule_object_ref5().HasBlock1()) {
1373+
if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(),
1374+
false, context.ServiceId, context.Cluster)) {
1375+
return false;
1376+
}
1377+
}
1378+
1379+
const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second;
1380+
std::map<TString, TDeferredAtom> kv;
1381+
std::set<TString> toReset;
1382+
if (!ParseResourcePoolClassifierSettings(kv, toReset, node.GetRule_alter_resource_pool_classifier_action6())) {
1383+
return false;
1384+
}
1385+
1386+
for (const auto& action : node.GetBlock7()) {
1387+
if (!ParseResourcePoolClassifierSettings(kv, toReset, action.GetRule_alter_resource_pool_classifier_action2())) {
1388+
return false;
1389+
}
1390+
}
1391+
1392+
AddStatementToBlocks(blocks, BuildAlterObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL_CLASSIFIER", std::move(kv), std::move(toReset), context));
1393+
break;
1394+
}
1395+
case TRule_sql_stmt_core::kAltSqlStmtCore50: {
1396+
// drop_resource_pool_classifier_stmt: DROP RESOURCE POOL CLASSIFIER name;
1397+
auto& node = core.GetAlt_sql_stmt_core50().GetRule_drop_resource_pool_classifier_stmt1();
1398+
TObjectOperatorContext context(Ctx.Scoped);
1399+
if (node.GetRule_object_ref5().HasBlock1()) {
1400+
if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(),
1401+
false, context.ServiceId, context.Cluster)) {
1402+
return false;
1403+
}
1404+
}
1405+
1406+
const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second;
1407+
AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL_CLASSIFIER", false, {}, context));
1408+
break;
1409+
}
13471410
case TRule_sql_stmt_core::ALT_NOT_SET:
13481411
Ctx.IncrementMonCounter("sql_errors", "UnknownStatement" + internalStatementName);
13491412
AltNotImplemented("sql_stmt_core", core);

ydb/library/yql/sql/v1/sql_translation.cpp

Lines changed: 72 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4672,34 +4672,98 @@ bool TSqlTranslation::ParseResourcePoolSettings(std::map<TString, TDeferredAtom>
46724672
bool TSqlTranslation::ParseResourcePoolSettings(std::map<TString, TDeferredAtom>& result, std::set<TString>& toReset, const TRule_alter_resource_pool_action& alterAction) {
46734673
switch (alterAction.Alt_case()) {
46744674
case TRule_alter_resource_pool_action::kAltAlterResourcePoolAction1: {
4675-
const auto& action = alterAction.GetAlt_alter_resource_pool_action1().GetRule_alter_table_set_table_setting_uncompat1();
4676-
if (!StoreResourcePoolSettingsEntry(IdEx(action.GetRule_an_id2(), *this), &action.GetRule_table_setting_value3(), result)) {
4675+
const auto& action = alterAction.GetAlt_alter_resource_pool_action1().GetRule_alter_table_set_table_setting_compat1();
4676+
if (!StoreResourcePoolSettingsEntry(action.GetRule_alter_table_setting_entry3(), result)) {
46774677
return false;
46784678
}
4679+
for (const auto& entry : action.GetBlock4()) {
4680+
if (!StoreResourcePoolSettingsEntry(entry.GetRule_alter_table_setting_entry2(), result)) {
4681+
return false;
4682+
}
4683+
}
46794684
return true;
46804685
}
46814686
case TRule_alter_resource_pool_action::kAltAlterResourcePoolAction2: {
4682-
const auto& action = alterAction.GetAlt_alter_resource_pool_action2().GetRule_alter_table_set_table_setting_compat1();
4683-
if (!StoreResourcePoolSettingsEntry(action.GetRule_alter_table_setting_entry3(), result)) {
4687+
const auto& action = alterAction.GetAlt_alter_resource_pool_action2().GetRule_alter_table_reset_table_setting1();
4688+
const TString firstKey = to_lower(IdEx(action.GetRule_an_id3(), *this).Name);
4689+
toReset.insert(firstKey);
4690+
for (const auto& key : action.GetBlock4()) {
4691+
toReset.insert(to_lower(IdEx(key.GetRule_an_id2(), *this).Name));
4692+
}
4693+
return true;
4694+
}
4695+
case TRule_alter_resource_pool_action::ALT_NOT_SET:
4696+
Y_ABORT("You should change implementation according to grammar changes");
4697+
}
4698+
}
4699+
4700+
bool TSqlTranslation::StoreResourcePoolClassifierSettingsEntry(const TIdentifier& id, const TRule_table_setting_value* value, std::map<TString, TDeferredAtom>& result) {
4701+
YQL_ENSURE(value);
4702+
4703+
const TString key = to_lower(id.Name);
4704+
if (result.find(key) != result.end()) {
4705+
Ctx.Error() << to_upper(key) << " duplicate keys";
4706+
return false;
4707+
}
4708+
4709+
switch (value->Alt_case()) {
4710+
case TRule_table_setting_value::kAltTableSettingValue2:
4711+
return StoreString(*value, result[key], Ctx, to_upper(key));
4712+
4713+
case TRule_table_setting_value::kAltTableSettingValue3:
4714+
return StoreInt(*value, result[key], Ctx, to_upper(key));
4715+
4716+
default:
4717+
Ctx.Error() << to_upper(key) << " value should be a string literal or integer";
4718+
return false;
4719+
}
4720+
4721+
return true;
4722+
}
4723+
4724+
bool TSqlTranslation::StoreResourcePoolClassifierSettingsEntry(const TRule_alter_table_setting_entry& entry, std::map<TString, TDeferredAtom>& result) {
4725+
const TIdentifier id = IdEx(entry.GetRule_an_id1(), *this);
4726+
return StoreResourcePoolClassifierSettingsEntry(id, &entry.GetRule_table_setting_value3(), result);
4727+
}
4728+
4729+
bool TSqlTranslation::ParseResourcePoolClassifierSettings(std::map<TString, TDeferredAtom>& result, const TRule_with_table_settings& settingsNode) {
4730+
const auto& firstEntry = settingsNode.GetRule_table_settings_entry3();
4731+
if (!StoreResourcePoolClassifierSettingsEntry(IdEx(firstEntry.GetRule_an_id1(), *this), &firstEntry.GetRule_table_setting_value3(), result)) {
4732+
return false;
4733+
}
4734+
for (const auto& block : settingsNode.GetBlock4()) {
4735+
const auto& entry = block.GetRule_table_settings_entry2();
4736+
if (!StoreResourcePoolClassifierSettingsEntry(IdEx(entry.GetRule_an_id1(), *this), &entry.GetRule_table_setting_value3(), result)) {
4737+
return false;
4738+
}
4739+
}
4740+
return true;
4741+
}
4742+
4743+
bool TSqlTranslation::ParseResourcePoolClassifierSettings(std::map<TString, TDeferredAtom>& result, std::set<TString>& toReset, const TRule_alter_resource_pool_classifier_action& alterAction) {
4744+
switch (alterAction.Alt_case()) {
4745+
case TRule_alter_resource_pool_classifier_action::kAltAlterResourcePoolClassifierAction1: {
4746+
const auto& action = alterAction.GetAlt_alter_resource_pool_classifier_action1().GetRule_alter_table_set_table_setting_compat1();
4747+
if (!StoreResourcePoolClassifierSettingsEntry(action.GetRule_alter_table_setting_entry3(), result)) {
46844748
return false;
46854749
}
46864750
for (const auto& entry : action.GetBlock4()) {
4687-
if (!StoreResourcePoolSettingsEntry(entry.GetRule_alter_table_setting_entry2(), result)) {
4751+
if (!StoreResourcePoolClassifierSettingsEntry(entry.GetRule_alter_table_setting_entry2(), result)) {
46884752
return false;
46894753
}
46904754
}
46914755
return true;
46924756
}
4693-
case TRule_alter_resource_pool_action::kAltAlterResourcePoolAction3: {
4694-
const auto& action = alterAction.GetAlt_alter_resource_pool_action3().GetRule_alter_table_reset_table_setting1();
4757+
case TRule_alter_resource_pool_classifier_action::kAltAlterResourcePoolClassifierAction2: {
4758+
const auto& action = alterAction.GetAlt_alter_resource_pool_classifier_action2().GetRule_alter_table_reset_table_setting1();
46954759
const TString firstKey = to_lower(IdEx(action.GetRule_an_id3(), *this).Name);
46964760
toReset.insert(firstKey);
46974761
for (const auto& key : action.GetBlock4()) {
46984762
toReset.insert(to_lower(IdEx(key.GetRule_an_id2(), *this).Name));
46994763
}
47004764
return true;
47014765
}
4702-
case TRule_alter_resource_pool_action::ALT_NOT_SET:
4766+
case TRule_alter_resource_pool_classifier_action::ALT_NOT_SET:
47034767
Y_ABORT("You should change implementation according to grammar changes");
47044768
}
47054769
}

0 commit comments

Comments
 (0)