Skip to content

Commit 5b42f4b

Browse files
committed
YQ-3491 support sql for resource pool classifiers (#7389)
1 parent 163d6fb commit 5b42f4b

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
@@ -6215,8 +6215,7 @@ Y_UNIT_TEST_SUITE(KqpScheme) {
62156215
// ALTER RESOURCE POOL
62166216
checkDisabled(R"(
62176217
ALTER RESOURCE POOL MyResourcePool
6218-
SET (CONCURRENT_QUERY_LIMIT = 30),
6219-
SET QUEUE_SIZE 100,
6218+
SET (CONCURRENT_QUERY_LIMIT = 30, QUEUE_SIZE = 100),
62206219
RESET (QUERY_MEMORY_LIMIT_PERCENT_PER_NODE);
62216220
)");
62226221

@@ -6253,7 +6252,7 @@ Y_UNIT_TEST_SUITE(KqpScheme) {
62536252

62546253
result = session.ExecuteSchemeQuery(R"(
62556254
ALTER RESOURCE POOL MyResourcePool
6256-
SET ANOTHER_LIMIT 5,
6255+
SET (ANOTHER_LIMIT = 5),
62576256
RESET (SOME_LIMIT);
62586257
)").GetValueSync();
62596258
UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::GENERIC_ERROR);
@@ -6366,8 +6365,7 @@ Y_UNIT_TEST_SUITE(KqpScheme) {
63666365
{
63676366
auto query = R"(
63686367
ALTER RESOURCE POOL MyResourcePool
6369-
SET (CONCURRENT_QUERY_LIMIT = 30),
6370-
SET QUEUE_SIZE 100,
6368+
SET (CONCURRENT_QUERY_LIMIT = 30, QUEUE_SIZE = 100),
63716369
RESET (QUERY_MEMORY_LIMIT_PERCENT_PER_NODE);
63726370
)";
63736371
auto result = session.ExecuteSchemeQuery(query).GetValueSync();
@@ -6396,8 +6394,7 @@ Y_UNIT_TEST_SUITE(KqpScheme) {
63966394

63976395
auto query = R"(
63986396
ALTER RESOURCE POOL MyResourcePool
6399-
SET (CONCURRENT_QUERY_LIMIT = 30),
6400-
SET QUEUE_SIZE 100,
6397+
SET (CONCURRENT_QUERY_LIMIT = 30, QUEUE_SIZE = 100),
64016398
RESET (QUERY_MEMORY_LIMIT_PERCENT_PER_NODE);
64026399
)";
64036400
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
@@ -1529,6 +1529,39 @@ friend struct TStaticData;
15291529
VisitAllFields(TRule_drop_resource_pool_stmt::GetDescriptor(), msg);
15301530
}
15311531

1532+
void VisitCreateResourcePoolClassifier(const TRule_create_resource_pool_classifier_stmt& msg) {
1533+
PosFromToken(msg.GetToken1());
1534+
NewLine();
1535+
VisitAllFields(TRule_create_resource_pool_classifier_stmt::GetDescriptor(), msg);
1536+
}
1537+
1538+
void VisitAlterResourcePoolClassifier(const TRule_alter_resource_pool_classifier_stmt& msg) {
1539+
PosFromToken(msg.GetToken1());
1540+
NewLine();
1541+
VisitToken(msg.GetToken1());
1542+
VisitToken(msg.GetToken2());
1543+
VisitToken(msg.GetToken3());
1544+
VisitToken(msg.GetToken4());
1545+
Visit(msg.GetRule_object_ref5());
1546+
1547+
NewLine();
1548+
PushCurrentIndent();
1549+
Visit(msg.GetRule_alter_resource_pool_classifier_action6());
1550+
for (const auto& action : msg.GetBlock7()) {
1551+
Visit(action.GetToken1()); // comma
1552+
NewLine();
1553+
Visit(action.GetRule_alter_resource_pool_classifier_action2());
1554+
}
1555+
1556+
PopCurrentIndent();
1557+
}
1558+
1559+
void VisitDropResourcePoolClassifier(const TRule_drop_resource_pool_classifier_stmt& msg) {
1560+
PosFromToken(msg.GetToken1());
1561+
NewLine();
1562+
VisitAllFields(TRule_drop_resource_pool_classifier_stmt::GetDescriptor(), msg);
1563+
}
1564+
15321565
void VisitAllFields(const NProtoBuf::Descriptor* descr, const NProtoBuf::Message& msg) {
15331566
VisitAllFieldsImpl<TPrettyVisitor, &TPrettyVisitor::Visit>(this, descr, msg);
15341567
}
@@ -2745,7 +2778,10 @@ TStaticData::TStaticData()
27452778
{TRule_drop_view_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitDropView)},
27462779
{TRule_create_resource_pool_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitCreateResourcePool)},
27472780
{TRule_alter_resource_pool_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitAlterResourcePool)},
2748-
{TRule_drop_resource_pool_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitDropResourcePool)}
2781+
{TRule_drop_resource_pool_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitDropResourcePool)},
2782+
{TRule_create_resource_pool_classifier_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitCreateResourcePoolClassifier)},
2783+
{TRule_alter_resource_pool_classifier_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitAlterResourcePoolClassifier)},
2784+
{TRule_drop_resource_pool_classifier_stmt::GetDescriptor(), MakePrettyFunctor(&TPrettyVisitor::VisitDropResourcePoolClassifier)}
27492785
})
27502786
, ObfuscatingVisitDispatch({
27512787
{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
@@ -1604,8 +1604,8 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A);
16041604
"CREATE RESOURCE POOL naMe WITH (a = \"b\");\n"},
16051605
{"create resource pool eds with (a=\"a\",b=\"b\",c = true)",
16061606
"CREATE RESOURCE POOL eds WITH (\n\ta = \"a\",\n\tb = \"b\",\n\tc = TRUE\n);\n"},
1607-
{"alTer reSOurcE poOl naMe sEt a tRue, resEt (b, c), seT (x=y, z=false)",
1608-
"ALTER RESOURCE POOL naMe\n\tSET a TRUE,\n\tRESET (b, c),\n\tSET (x = y, z = FALSE);\n"},
1607+
{"alTer reSOurcE poOl naMe resEt (b, c), seT (x=y, z=false)",
1608+
"ALTER RESOURCE POOL naMe\n\tRESET (b, c),\n\tSET (x = y, z = FALSE);\n"},
16091609
{"alter resource pool eds reset (a), set (x=y)",
16101610
"ALTER RESOURCE POOL eds\n\tRESET (a),\n\tSET (x = y);\n"},
16111611
{"dRop reSourCe poOl naMe",
@@ -1615,4 +1615,22 @@ FROM Input MATCH_RECOGNIZE (PATTERN (A) DEFINE A AS A);
16151615
TSetup setup;
16161616
setup.Run(cases);
16171617
}
1618+
1619+
Y_UNIT_TEST(ResourcePoolClassifierOperations) {
1620+
TCases cases = {
1621+
{"creAte reSourCe poOl ClaSsiFIer naMe With (a = \"b\")",
1622+
"CREATE RESOURCE POOL CLASSIFIER naMe WITH (a = \"b\");\n"},
1623+
{"create resource pool classifier eds with (a=\"a\",b=\"b\",c = true)",
1624+
"CREATE RESOURCE POOL CLASSIFIER eds WITH (\n\ta = \"a\",\n\tb = \"b\",\n\tc = TRUE\n);\n"},
1625+
{"alTer reSOurcE poOl ClaSsiFIer naMe resEt (b, c), seT (x=y, z=false)",
1626+
"ALTER RESOURCE POOL CLASSIFIER naMe\n\tRESET (b, c),\n\tSET (x = y, z = FALSE);\n"},
1627+
{"alter resource pool classifier eds reset (a), set (x=y)",
1628+
"ALTER RESOURCE POOL CLASSIFIER eds\n\tRESET (a),\n\tSET (x = y);\n"},
1629+
{"dRop reSourCe poOl ClaSsiFIer naMe",
1630+
"DROP RESOURCE POOL CLASSIFIER naMe;\n"},
1631+
};
1632+
1633+
TSetup setup;
1634+
setup.Run(cases);
1635+
}
16181636
}

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
@@ -1362,6 +1362,69 @@ bool TSqlQuery::Statement(TVector<TNodePtr>& blocks, const TRule_sql_stmt_core&
13621362
AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL", false, {}, context));
13631363
break;
13641364
}
1365+
case TRule_sql_stmt_core::kAltSqlStmtCore48: {
1366+
// create_resource_pool_classifier_stmt: CREATE RESOURCE POOL CLASSIFIER name WITH (k=v,...);
1367+
auto& node = core.GetAlt_sql_stmt_core48().GetRule_create_resource_pool_classifier_stmt1();
1368+
TObjectOperatorContext context(Ctx.Scoped);
1369+
if (node.GetRule_object_ref5().HasBlock1()) {
1370+
if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(),
1371+
false, context.ServiceId, context.Cluster)) {
1372+
return false;
1373+
}
1374+
}
1375+
1376+
const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second;
1377+
std::map<TString, TDeferredAtom> kv;
1378+
if (!ParseResourcePoolClassifierSettings(kv, node.GetRule_with_table_settings6())) {
1379+
return false;
1380+
}
1381+
1382+
AddStatementToBlocks(blocks, BuildCreateObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL_CLASSIFIER", false, false, std::move(kv), context));
1383+
break;
1384+
}
1385+
case TRule_sql_stmt_core::kAltSqlStmtCore49: {
1386+
// alter_resource_pool_classifier_stmt: ALTER RESOURCE POOL CLASSIFIER object_ref alter_resource_pool_classifier_action (COMMA alter_resource_pool_classifier_action)*
1387+
Ctx.BodyPart();
1388+
const auto& node = core.GetAlt_sql_stmt_core49().GetRule_alter_resource_pool_classifier_stmt1();
1389+
TObjectOperatorContext context(Ctx.Scoped);
1390+
if (node.GetRule_object_ref5().HasBlock1()) {
1391+
if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(),
1392+
false, context.ServiceId, context.Cluster)) {
1393+
return false;
1394+
}
1395+
}
1396+
1397+
const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second;
1398+
std::map<TString, TDeferredAtom> kv;
1399+
std::set<TString> toReset;
1400+
if (!ParseResourcePoolClassifierSettings(kv, toReset, node.GetRule_alter_resource_pool_classifier_action6())) {
1401+
return false;
1402+
}
1403+
1404+
for (const auto& action : node.GetBlock7()) {
1405+
if (!ParseResourcePoolClassifierSettings(kv, toReset, action.GetRule_alter_resource_pool_classifier_action2())) {
1406+
return false;
1407+
}
1408+
}
1409+
1410+
AddStatementToBlocks(blocks, BuildAlterObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL_CLASSIFIER", std::move(kv), std::move(toReset), context));
1411+
break;
1412+
}
1413+
case TRule_sql_stmt_core::kAltSqlStmtCore50: {
1414+
// drop_resource_pool_classifier_stmt: DROP RESOURCE POOL CLASSIFIER name;
1415+
auto& node = core.GetAlt_sql_stmt_core50().GetRule_drop_resource_pool_classifier_stmt1();
1416+
TObjectOperatorContext context(Ctx.Scoped);
1417+
if (node.GetRule_object_ref5().HasBlock1()) {
1418+
if (!ClusterExpr(node.GetRule_object_ref5().GetBlock1().GetRule_cluster_expr1(),
1419+
false, context.ServiceId, context.Cluster)) {
1420+
return false;
1421+
}
1422+
}
1423+
1424+
const TString& objectId = Id(node.GetRule_object_ref5().GetRule_id_or_at2(), *this).second;
1425+
AddStatementToBlocks(blocks, BuildDropObjectOperation(Ctx.Pos(), objectId, "RESOURCE_POOL_CLASSIFIER", false, {}, context));
1426+
break;
1427+
}
13651428
case TRule_sql_stmt_core::ALT_NOT_SET:
13661429
Ctx.IncrementMonCounter("sql_errors", "UnknownStatement" + internalStatementName);
13671430
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
@@ -4712,34 +4712,98 @@ bool TSqlTranslation::ParseResourcePoolSettings(std::map<TString, TDeferredAtom>
47124712
bool TSqlTranslation::ParseResourcePoolSettings(std::map<TString, TDeferredAtom>& result, std::set<TString>& toReset, const TRule_alter_resource_pool_action& alterAction) {
47134713
switch (alterAction.Alt_case()) {
47144714
case TRule_alter_resource_pool_action::kAltAlterResourcePoolAction1: {
4715-
const auto& action = alterAction.GetAlt_alter_resource_pool_action1().GetRule_alter_table_set_table_setting_uncompat1();
4716-
if (!StoreResourcePoolSettingsEntry(IdEx(action.GetRule_an_id2(), *this), &action.GetRule_table_setting_value3(), result)) {
4715+
const auto& action = alterAction.GetAlt_alter_resource_pool_action1().GetRule_alter_table_set_table_setting_compat1();
4716+
if (!StoreResourcePoolSettingsEntry(action.GetRule_alter_table_setting_entry3(), result)) {
47174717
return false;
47184718
}
4719+
for (const auto& entry : action.GetBlock4()) {
4720+
if (!StoreResourcePoolSettingsEntry(entry.GetRule_alter_table_setting_entry2(), result)) {
4721+
return false;
4722+
}
4723+
}
47194724
return true;
47204725
}
47214726
case TRule_alter_resource_pool_action::kAltAlterResourcePoolAction2: {
4722-
const auto& action = alterAction.GetAlt_alter_resource_pool_action2().GetRule_alter_table_set_table_setting_compat1();
4723-
if (!StoreResourcePoolSettingsEntry(action.GetRule_alter_table_setting_entry3(), result)) {
4727+
const auto& action = alterAction.GetAlt_alter_resource_pool_action2().GetRule_alter_table_reset_table_setting1();
4728+
const TString firstKey = to_lower(IdEx(action.GetRule_an_id3(), *this).Name);
4729+
toReset.insert(firstKey);
4730+
for (const auto& key : action.GetBlock4()) {
4731+
toReset.insert(to_lower(IdEx(key.GetRule_an_id2(), *this).Name));
4732+
}
4733+
return true;
4734+
}
4735+
case TRule_alter_resource_pool_action::ALT_NOT_SET:
4736+
Y_ABORT("You should change implementation according to grammar changes");
4737+
}
4738+
}
4739+
4740+
bool TSqlTranslation::StoreResourcePoolClassifierSettingsEntry(const TIdentifier& id, const TRule_table_setting_value* value, std::map<TString, TDeferredAtom>& result) {
4741+
YQL_ENSURE(value);
4742+
4743+
const TString key = to_lower(id.Name);
4744+
if (result.find(key) != result.end()) {
4745+
Ctx.Error() << to_upper(key) << " duplicate keys";
4746+
return false;
4747+
}
4748+
4749+
switch (value->Alt_case()) {
4750+
case TRule_table_setting_value::kAltTableSettingValue2:
4751+
return StoreString(*value, result[key], Ctx, to_upper(key));
4752+
4753+
case TRule_table_setting_value::kAltTableSettingValue3:
4754+
return StoreInt(*value, result[key], Ctx, to_upper(key));
4755+
4756+
default:
4757+
Ctx.Error() << to_upper(key) << " value should be a string literal or integer";
4758+
return false;
4759+
}
4760+
4761+
return true;
4762+
}
4763+
4764+
bool TSqlTranslation::StoreResourcePoolClassifierSettingsEntry(const TRule_alter_table_setting_entry& entry, std::map<TString, TDeferredAtom>& result) {
4765+
const TIdentifier id = IdEx(entry.GetRule_an_id1(), *this);
4766+
return StoreResourcePoolClassifierSettingsEntry(id, &entry.GetRule_table_setting_value3(), result);
4767+
}
4768+
4769+
bool TSqlTranslation::ParseResourcePoolClassifierSettings(std::map<TString, TDeferredAtom>& result, const TRule_with_table_settings& settingsNode) {
4770+
const auto& firstEntry = settingsNode.GetRule_table_settings_entry3();
4771+
if (!StoreResourcePoolClassifierSettingsEntry(IdEx(firstEntry.GetRule_an_id1(), *this), &firstEntry.GetRule_table_setting_value3(), result)) {
4772+
return false;
4773+
}
4774+
for (const auto& block : settingsNode.GetBlock4()) {
4775+
const auto& entry = block.GetRule_table_settings_entry2();
4776+
if (!StoreResourcePoolClassifierSettingsEntry(IdEx(entry.GetRule_an_id1(), *this), &entry.GetRule_table_setting_value3(), result)) {
4777+
return false;
4778+
}
4779+
}
4780+
return true;
4781+
}
4782+
4783+
bool TSqlTranslation::ParseResourcePoolClassifierSettings(std::map<TString, TDeferredAtom>& result, std::set<TString>& toReset, const TRule_alter_resource_pool_classifier_action& alterAction) {
4784+
switch (alterAction.Alt_case()) {
4785+
case TRule_alter_resource_pool_classifier_action::kAltAlterResourcePoolClassifierAction1: {
4786+
const auto& action = alterAction.GetAlt_alter_resource_pool_classifier_action1().GetRule_alter_table_set_table_setting_compat1();
4787+
if (!StoreResourcePoolClassifierSettingsEntry(action.GetRule_alter_table_setting_entry3(), result)) {
47244788
return false;
47254789
}
47264790
for (const auto& entry : action.GetBlock4()) {
4727-
if (!StoreResourcePoolSettingsEntry(entry.GetRule_alter_table_setting_entry2(), result)) {
4791+
if (!StoreResourcePoolClassifierSettingsEntry(entry.GetRule_alter_table_setting_entry2(), result)) {
47284792
return false;
47294793
}
47304794
}
47314795
return true;
47324796
}
4733-
case TRule_alter_resource_pool_action::kAltAlterResourcePoolAction3: {
4734-
const auto& action = alterAction.GetAlt_alter_resource_pool_action3().GetRule_alter_table_reset_table_setting1();
4797+
case TRule_alter_resource_pool_classifier_action::kAltAlterResourcePoolClassifierAction2: {
4798+
const auto& action = alterAction.GetAlt_alter_resource_pool_classifier_action2().GetRule_alter_table_reset_table_setting1();
47354799
const TString firstKey = to_lower(IdEx(action.GetRule_an_id3(), *this).Name);
47364800
toReset.insert(firstKey);
47374801
for (const auto& key : action.GetBlock4()) {
47384802
toReset.insert(to_lower(IdEx(key.GetRule_an_id2(), *this).Name));
47394803
}
47404804
return true;
47414805
}
4742-
case TRule_alter_resource_pool_action::ALT_NOT_SET:
4806+
case TRule_alter_resource_pool_classifier_action::ALT_NOT_SET:
47434807
Y_ABORT("You should change implementation according to grammar changes");
47444808
}
47454809
}

0 commit comments

Comments
 (0)