diff options
author | ditimizhev <[email protected]> | 2025-01-16 16:05:48 +0300 |
---|---|---|
committer | ditimizhev <[email protected]> | 2025-01-16 16:30:54 +0300 |
commit | 7e86bcaf10a418760708b0be68e02abd715b745b (patch) | |
tree | a48b0c9efd485af31384bfaf60c8aada1f9ca3a8 | |
parent | e812e2c1c25ae5339044634886773f66f443c061 (diff) |
Add BATCH to update/delete stmts
Impl BATCH to UPDATE and DELETE stmts without ON for batch execution in YDB.
commit_hash:426f8fae0635a8fec07e940e0f0dfba1974531bf
-rw-r--r-- | yql/essentials/providers/common/provider/yql_provider.cpp | 4 | ||||
-rw-r--r-- | yql/essentials/providers/common/provider/yql_provider.h | 1 | ||||
-rw-r--r-- | yql/essentials/sql/v1/SQLv1.g.in | 7 | ||||
-rw-r--r-- | yql/essentials/sql/v1/SQLv1Antlr4.g.in | 7 | ||||
-rw-r--r-- | yql/essentials/sql/v1/format/sql_format.cpp | 30 | ||||
-rw-r--r-- | yql/essentials/sql/v1/insert.cpp | 25 | ||||
-rw-r--r-- | yql/essentials/sql/v1/source.h | 2 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_query.cpp | 64 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_ut.cpp | 47 | ||||
-rw-r--r-- | yql/essentials/sql/v1/sql_ut_antlr4.cpp | 49 |
10 files changed, 195 insertions, 41 deletions
diff --git a/yql/essentials/providers/common/provider/yql_provider.cpp b/yql/essentials/providers/common/provider/yql_provider.cpp index ec2c3ca57c9..79156cd8d37 100644 --- a/yql/essentials/providers/common/provider/yql_provider.cpp +++ b/yql/essentials/providers/common/provider/yql_provider.cpp @@ -225,6 +225,7 @@ NYson::EYsonFormat GetYsonFormat(const IDataProvider::TFillSettings& fillSetting TWriteTableSettings ParseWriteTableSettings(TExprList node, TExprContext& ctx) { TMaybeNode<TCoAtom> mode; TMaybeNode<TCoAtom> temporary; + TMaybeNode<TCoAtom> isBatch; TMaybeNode<TExprList> columns; TMaybeNode<TExprList> returningList; TMaybeNode<TCoAtomList> primaryKey; @@ -347,6 +348,8 @@ TWriteTableSettings ParseWriteTableSettings(TExprList node, TExprContext& ctx) { } else if (name == "returning") { YQL_ENSURE(tuple.Value().Maybe<TExprList>()); returningList = tuple.Value().Cast<TExprList>(); + } else if (name == "is_batch") { + isBatch = Build<TCoAtom>(ctx, node.Pos()).Value("true").Done(); } else { other.push_back(tuple); } @@ -380,6 +383,7 @@ TWriteTableSettings ParseWriteTableSettings(TExprList node, TExprContext& ctx) { TWriteTableSettings ret(otherSettings); ret.Mode = mode; ret.Temporary = temporary; + ret.IsBatch = isBatch; ret.Columns = columns; ret.ReturningList = returningList; ret.PrimaryKey = primaryKey; diff --git a/yql/essentials/providers/common/provider/yql_provider.h b/yql/essentials/providers/common/provider/yql_provider.h index 362b1e19584..978621d4b16 100644 --- a/yql/essentials/providers/common/provider/yql_provider.h +++ b/yql/essentials/providers/common/provider/yql_provider.h @@ -37,6 +37,7 @@ constexpr TStringBuf PgCatalogFileName = "_yql_pg_catalog"; struct TWriteTableSettings { NNodes::TMaybeNode<NNodes::TCoAtom> Mode; NNodes::TMaybeNode<NNodes::TCoAtom> Temporary; + NNodes::TMaybeNode<NNodes::TCoAtom> IsBatch; NNodes::TMaybeNode<NNodes::TExprList> Columns; NNodes::TMaybeNode<NNodes::TExprList> ReturningList; NNodes::TMaybeNode<NNodes::TCoAtomList> PrimaryKey; diff --git a/yql/essentials/sql/v1/SQLv1.g.in b/yql/essentials/sql/v1/SQLv1.g.in index 61e7dc6f251..192a1a6026f 100644 --- a/yql/essentials/sql/v1/SQLv1.g.in +++ b/yql/essentials/sql/v1/SQLv1.g.in @@ -930,8 +930,8 @@ simple_table_ref_core: object_ref | COMMAT? bind_parameter; simple_table_ref: simple_table_ref_core table_hints?; into_simple_table_ref: simple_table_ref (ERASE BY pure_column_list)?; -delete_stmt: DELETE FROM simple_table_ref (WHERE expr | ON into_values_source)? returning_columns_list?; -update_stmt: UPDATE simple_table_ref (SET set_clause_choice (WHERE expr)? | ON into_values_source) returning_columns_list?; +delete_stmt: BATCH? DELETE FROM simple_table_ref (WHERE expr | ON into_values_source)? returning_columns_list?; +update_stmt: BATCH? UPDATE simple_table_ref (SET set_clause_choice (WHERE expr)? | ON into_values_source) returning_columns_list?; /// out of 2003 standart set_clause_choice: set_clause_list | multiple_column_assignment; @@ -1278,6 +1278,7 @@ keyword_as_compat: | ATTRIBUTES | AUTOINCREMENT | BACKUP + | BATCH | BEFORE | BEGIN | BERNOULLI @@ -1504,6 +1505,7 @@ keyword_compat: ( | ATTRIBUTES | AUTOINCREMENT | BACKUP + | BATCH | BEFORE | BEGIN | BERNOULLI @@ -1830,6 +1832,7 @@ ATTRIBUTES: A T T R I B U T E S; AUTOINCREMENT: A U T O I N C R E M E N T; AUTOMAP: A U T O M A P; BACKUP: B A C K U P; +BATCH: B A T C H; COLLECTION: C O L L E C T I O N; BEFORE: B E F O R E; BEGIN: B E G I N; diff --git a/yql/essentials/sql/v1/SQLv1Antlr4.g.in b/yql/essentials/sql/v1/SQLv1Antlr4.g.in index 96e450501cd..3ae4ad6492d 100644 --- a/yql/essentials/sql/v1/SQLv1Antlr4.g.in +++ b/yql/essentials/sql/v1/SQLv1Antlr4.g.in @@ -929,8 +929,8 @@ simple_table_ref_core: object_ref | COMMAT? bind_parameter; simple_table_ref: simple_table_ref_core table_hints?; into_simple_table_ref: simple_table_ref (ERASE BY pure_column_list)?; -delete_stmt: DELETE FROM simple_table_ref (WHERE expr | ON into_values_source)? returning_columns_list?; -update_stmt: UPDATE simple_table_ref (SET set_clause_choice (WHERE expr)? | ON into_values_source) returning_columns_list?; +delete_stmt: BATCH? DELETE FROM simple_table_ref (WHERE expr | ON into_values_source)? returning_columns_list?; +update_stmt: BATCH? UPDATE simple_table_ref (SET set_clause_choice (WHERE expr)? | ON into_values_source) returning_columns_list?; /// out of 2003 standart set_clause_choice: set_clause_list | multiple_column_assignment; @@ -1277,6 +1277,7 @@ keyword_as_compat: | ATTRIBUTES | AUTOINCREMENT | BACKUP + | BATCH | BEFORE | BEGIN | BERNOULLI @@ -1503,6 +1504,7 @@ keyword_compat: ( | ATTRIBUTES | AUTOINCREMENT | BACKUP + | BATCH | BEFORE | BEGIN | BERNOULLI @@ -1829,6 +1831,7 @@ ATTRIBUTES: A T T R I B U T E S; AUTOINCREMENT: A U T O I N C R E M E N T; AUTOMAP: A U T O M A P; BACKUP: B A C K U P; +BATCH: B A T C H; COLLECTION: C O L L E C T I O N; BEFORE: B E F O R E; BEGIN: B E G I N; diff --git a/yql/essentials/sql/v1/format/sql_format.cpp b/yql/essentials/sql/v1/format/sql_format.cpp index b7a8d5c48de..b6986310aa1 100644 --- a/yql/essentials/sql/v1/format/sql_format.cpp +++ b/yql/essentials/sql/v1/format/sql_format.cpp @@ -1084,11 +1084,11 @@ private: void VisitUpdate(const TRule_update_stmt& msg) { NewLine(); - Visit(msg.GetToken1()); - Visit(msg.GetRule_simple_table_ref2()); - switch (msg.GetBlock3().Alt_case()) { - case TRule_update_stmt_TBlock3::kAlt1: { - const auto& alt = msg.GetBlock3().GetAlt1(); + Visit(msg.GetToken2()); + Visit(msg.GetRule_simple_table_ref3()); + switch (msg.GetBlock4().Alt_case()) { + case TRule_update_stmt_TBlock4::kAlt1: { + const auto& alt = msg.GetBlock4().GetAlt1(); NewLine(); Visit(alt.GetToken1()); const auto& choice = alt.GetRule_set_clause_choice2(); @@ -1175,8 +1175,8 @@ private: PopCurrentIndent(); break; } - case TRule_update_stmt_TBlock3::kAlt2: { - const auto& alt = msg.GetBlock3().GetAlt2(); + case TRule_update_stmt_TBlock4::kAlt2: { + const auto& alt = msg.GetBlock4().GetAlt2(); NewLine(); Visit(alt.GetToken1()); Visit(alt.GetRule_into_values_source2()); @@ -1189,19 +1189,19 @@ private: void VisitDelete(const TRule_delete_stmt& msg) { NewLine(); - Visit(msg.GetToken1()); Visit(msg.GetToken2()); - Visit(msg.GetRule_simple_table_ref3()); - if (msg.HasBlock4()) { - switch (msg.GetBlock4().Alt_case()) { - case TRule_delete_stmt_TBlock4::kAlt1: { - const auto& alt = msg.GetBlock4().GetAlt1(); + Visit(msg.GetToken3()); + Visit(msg.GetRule_simple_table_ref4()); + if (msg.HasBlock5()) { + switch (msg.GetBlock5().Alt_case()) { + case TRule_delete_stmt_TBlock5::kAlt1: { + const auto& alt = msg.GetBlock5().GetAlt1(); NewLine(); Visit(alt); break; } - case TRule_delete_stmt_TBlock4::kAlt2: { - const auto& alt = msg.GetBlock4().GetAlt2(); + case TRule_delete_stmt_TBlock5::kAlt2: { + const auto& alt = msg.GetBlock5().GetAlt2(); NewLine(); Visit(alt); break; diff --git a/yql/essentials/sql/v1/insert.cpp b/yql/essentials/sql/v1/insert.cpp index 181137457f1..4af4986085b 100644 --- a/yql/essentials/sql/v1/insert.cpp +++ b/yql/essentials/sql/v1/insert.cpp @@ -300,6 +300,10 @@ public: Update = std::move(update); } + void ResetIsBatch(bool isBatch) { + IsBatch = isBatch; + } + bool DoInit(TContext& ctx, ISource* src) override { TTableList tableList; TNodePtr values; @@ -347,6 +351,10 @@ public: options = L(options, Q(Y(Q("update"), Update->Build(ctx)))); } + if (IsBatch) { + options = L(options, Q(Y(Q("is_batch"), Q("true")))); + } + auto write = BuildWriteTable(Pos, "values", Table, Mode, std::move(options), Scoped); if (!write->Init(ctx, FakeSource.Get())) { return false; @@ -379,6 +387,7 @@ protected: TSourcePtr Update; TSourcePtr FakeSource; TNodePtr Options; + bool IsBatch = false; }; EWriteColumnMode ToWriteColumnsMode(ESQLWriteColumnMode sqlWriteColumnMode) { @@ -398,12 +407,28 @@ TNodePtr BuildUpdateColumns(TPosition pos, TScopedStatePtr scoped, const TTableR return writeNode; } +TNodePtr BuildBatchUpdate(TPosition pos, TScopedStatePtr scoped, const TTableRef& table, TSourcePtr values, TSourcePtr source, TNodePtr options) { + YQL_ENSURE(values, "Invalid values node"); + TIntrusivePtr<TWriteColumnsNode> writeNode = new TWriteColumnsNode(pos, scoped, table, EWriteColumnMode::Update, nullptr, options); + writeNode->ResetSource(std::move(source)); + writeNode->ResetUpdate(std::move(values)); + writeNode->ResetIsBatch(true); + return writeNode; +} + TNodePtr BuildDelete(TPosition pos, TScopedStatePtr scoped, const TTableRef& table, TSourcePtr source, TNodePtr options) { TIntrusivePtr<TWriteColumnsNode> writeNode = new TWriteColumnsNode(pos, scoped, table, EWriteColumnMode::Delete, nullptr, options); writeNode->ResetSource(std::move(source)); return writeNode; } +TNodePtr BuildBatchDelete(TPosition pos, TScopedStatePtr scoped, const TTableRef& table, TSourcePtr source, TNodePtr options) { + TIntrusivePtr<TWriteColumnsNode> writeNode = new TWriteColumnsNode(pos, scoped, table, EWriteColumnMode::Delete, nullptr, options); + writeNode->ResetSource(std::move(source)); + writeNode->ResetIsBatch(true); + return writeNode; +} + class TEraseColumnsNode: public TAstListNode { public: diff --git a/yql/essentials/sql/v1/source.h b/yql/essentials/sql/v1/source.h index 20a7da80f90..f69684b03e6 100644 --- a/yql/essentials/sql/v1/source.h +++ b/yql/essentials/sql/v1/source.h @@ -299,7 +299,9 @@ namespace NSQLTranslationV1 { TNodePtr BuildIntoTableOptions(TPosition pos, const TVector<TString>& eraseColumns, const TTableHints& hints); TNodePtr BuildWriteColumns(TPosition pos, TScopedStatePtr scoped, const TTableRef& table, EWriteColumnMode mode, TSourcePtr values, TNodePtr options = nullptr); TNodePtr BuildUpdateColumns(TPosition pos, TScopedStatePtr scoped, const TTableRef& table, TSourcePtr values, TSourcePtr source, TNodePtr options = nullptr); + TNodePtr BuildBatchUpdate(TPosition pos, TScopedStatePtr scoped, const TTableRef& table, TSourcePtr values, TSourcePtr source, TNodePtr options = nullptr); TNodePtr BuildDelete(TPosition pos, TScopedStatePtr scoped, const TTableRef& table, TSourcePtr source, TNodePtr options = nullptr); + TNodePtr BuildBatchDelete(TPosition pos, TScopedStatePtr scoped, const TTableRef& table, TSourcePtr source, TNodePtr options = nullptr); // Implemented in query.cpp TNodePtr BuildTableKey(TPosition pos, const TString& service, const TDeferredAtom& cluster, const TDeferredAtom& name, const TViewDescription& view); diff --git a/yql/essentials/sql/v1/sql_query.cpp b/yql/essentials/sql/v1/sql_query.cpp index ac2baf978b3..9b8c1182db6 100644 --- a/yql/essentials/sql/v1/sql_query.cpp +++ b/yql/essentials/sql/v1/sql_query.cpp @@ -3232,28 +3232,30 @@ TNodePtr TSqlQuery::PragmaStatement(const TRule_pragma_stmt& stmt, bool& success TNodePtr TSqlQuery::Build(const TRule_delete_stmt& stmt) { TTableRef table; - if (!SimpleTableRefImpl(stmt.GetRule_simple_table_ref3(), table)) { + if (!SimpleTableRefImpl(stmt.GetRule_simple_table_ref4(), table)) { return nullptr; } const bool isKikimr = table.Service == KikimrProviderName; if (!isKikimr) { - Ctx.Error(GetPos(stmt.GetToken1())) << "DELETE is unsupported for " << table.Service; + Ctx.Error(GetPos(stmt.GetToken2())) << "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()); + if (stmt.HasBlock6()) { + options = ReturningList(stmt.GetBlock6().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(); + const bool isBatch = stmt.HasBlock1(); + + if (stmt.HasBlock5()) { + switch (stmt.GetBlock5().Alt_case()) { + case TRule_delete_stmt_TBlock5::kAlt1: { + const auto& alt = stmt.GetBlock5().GetAlt1(); TColumnRefScope scope(Ctx, EColumnRefState::Allow); TSqlExpression sqlExpr(Ctx, Mode); @@ -3265,47 +3267,58 @@ TNodePtr TSqlQuery::Build(const TRule_delete_stmt& stmt) { break; } - case TRule_delete_stmt_TBlock4::kAlt2: { - const auto& alt = stmt.GetBlock4().GetAlt2(); + case TRule_delete_stmt_TBlock5::kAlt2: { + const auto& alt = stmt.GetBlock5().GetAlt2(); auto values = TSqlIntoValues(Ctx, Mode).Build(alt.GetRule_into_values_source2(), "DELETE ON"); if (!values) { return nullptr; } + if (isBatch) { + Ctx.Error(GetPos(stmt.GetToken2())) << "BATCH DELETE is unsupported with ON"; + return nullptr; + } + return BuildWriteColumns(Ctx.Pos(), Ctx.Scoped, table, EWriteColumnMode::DeleteOn, std::move(values), options); } - case TRule_delete_stmt_TBlock4::ALT_NOT_SET: + case TRule_delete_stmt_TBlock5::ALT_NOT_SET: return nullptr; } } + if (isBatch) { + return BuildBatchDelete(Ctx.Pos(), Ctx.Scoped, table, std::move(source), options); + } + 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)) { + if (!SimpleTableRefImpl(stmt.GetRule_simple_table_ref3(), table)) { return nullptr; } const bool isKikimr = table.Service == KikimrProviderName; if (!isKikimr) { - Ctx.Error(GetPos(stmt.GetToken1())) << "UPDATE is unsupported for " << table.Service; + Ctx.Error(GetPos(stmt.GetToken2())) << "UPDATE is unsupported for " << table.Service; return nullptr; } TNodePtr options = nullptr; - if (stmt.HasBlock4()) { - options = ReturningList(stmt.GetBlock4().GetRule_returning_columns_list1()); + if (stmt.HasBlock5()) { + options = ReturningList(stmt.GetBlock5().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(); + const bool isBatch = stmt.HasBlock1(); + + switch (stmt.GetBlock4().Alt_case()) { + case TRule_update_stmt_TBlock4::kAlt1: { + const auto& alt = stmt.GetBlock4().GetAlt1(); TSourcePtr values = Build(alt.GetRule_set_clause_choice2()); auto source = BuildTableSource(Ctx.Pos(), table); @@ -3319,21 +3332,30 @@ TNodePtr TSqlQuery::Build(const TRule_update_stmt& stmt) { source->AddFilter(Ctx, whereExpr); } + if (isBatch) { + return BuildBatchUpdate(Ctx.Pos(), Ctx.Scoped, table, std::move(values), std::move(source), options); + } + 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(); + case TRule_update_stmt_TBlock4::kAlt2: { + const auto& alt = stmt.GetBlock4().GetAlt2(); auto values = TSqlIntoValues(Ctx, Mode).Build(alt.GetRule_into_values_source2(), "UPDATE ON"); if (!values) { return nullptr; } + if (isBatch) { + Ctx.Error(GetPos(stmt.GetToken2())) << "BATCH UPDATE is unsupported with ON"; + return nullptr; + } + return BuildWriteColumns(Ctx.Pos(), Ctx.Scoped, table, EWriteColumnMode::UpdateOn, std::move(values), options); } - case TRule_update_stmt_TBlock3::ALT_NOT_SET: + case TRule_update_stmt_TBlock4::ALT_NOT_SET: return nullptr; } } diff --git a/yql/essentials/sql/v1/sql_ut.cpp b/yql/essentials/sql/v1/sql_ut.cpp index efbed0e6653..e7b30928c32 100644 --- a/yql/essentials/sql/v1/sql_ut.cpp +++ b/yql/essentials/sql/v1/sql_ut.cpp @@ -1214,6 +1214,23 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } + Y_UNIT_TEST(DeleteFromTableBatch) { + NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input;", 10, "kikimr"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)")); + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)")); + } + }; + + TWordCountHive elementStat = {{TString("Write"), 0}}; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + } + Y_UNIT_TEST(DeleteFromTableOnValues) { NYql::TAstParseResult res = SqlToYql("delete from plato.Input on (key) values (1);", 10, "kikimr"); @@ -1248,6 +1265,13 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } + Y_UNIT_TEST(DeleteFromTableOnBatch) { + NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input on (key) values (1);", + 10, "kikimr"); + UNIT_ASSERT(!res.Root); + UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH DELETE is unsupported with ON\n"); + } + Y_UNIT_TEST(UpdateByValues) { NYql::TAstParseResult res = SqlToYql("update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr"); UNIT_ASSERT(res.Root); @@ -1274,6 +1298,23 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]); } + Y_UNIT_TEST(UpdateByValuesBatch) { + NYql::TAstParseResult res = SqlToYql("batch update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)")); + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)")); + } + }; + + TWordCountHive elementStat = {{TString("Write"), 0}}; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + } + Y_UNIT_TEST(UpdateByMultiValues) { NYql::TAstParseResult res = SqlToYql("update plato.Input set (key, value, subkey) = ('2','ddd',':') where key = 200;", 10, "kikimr"); UNIT_ASSERT(res.Root); @@ -1384,6 +1425,12 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } + Y_UNIT_TEST(UpdateOnBatch) { + NYql::TAstParseResult res = SqlToYql("batch update plato.Input on (key, value) values (5, 'cool')", 10, "kikimr"); + UNIT_ASSERT(!res.Root); + UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH UPDATE is unsupported with ON\n"); + } + Y_UNIT_TEST(UnionAllTest) { NYql::TAstParseResult res = SqlToYql("PRAGMA DisableEmitUnionMerge; SELECT key FROM plato.Input UNION ALL select subkey FROM plato.Input;"); UNIT_ASSERT(res.Root); diff --git a/yql/essentials/sql/v1/sql_ut_antlr4.cpp b/yql/essentials/sql/v1/sql_ut_antlr4.cpp index c5df28bbaa3..5337f573510 100644 --- a/yql/essentials/sql/v1/sql_ut_antlr4.cpp +++ b/yql/essentials/sql/v1/sql_ut_antlr4.cpp @@ -1290,6 +1290,23 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } + Y_UNIT_TEST(DeleteFromTableBatch) { + NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input;", 10, "kikimr"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'delete)")); + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)")); + } + }; + + TWordCountHive elementStat = {{TString("Write"), 0}}; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + } + Y_UNIT_TEST(DeleteFromTableOnValues) { NYql::TAstParseResult res = SqlToYql("delete from plato.Input on (key) values (1);", 10, "kikimr"); @@ -1324,6 +1341,13 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } + Y_UNIT_TEST(DeleteFromTableOnBatch) { + NYql::TAstParseResult res = SqlToYql("batch delete from plato.Input on (key) values (1);", + 10, "kikimr"); + UNIT_ASSERT(!res.Root); + UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH DELETE is unsupported with ON\n"); + } + Y_UNIT_TEST(UpdateByValues) { NYql::TAstParseResult res = SqlToYql("update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr"); UNIT_ASSERT(res.Root); @@ -1350,6 +1374,23 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["AsStruct"]); } + Y_UNIT_TEST(UpdateByValuesBatch) { + NYql::TAstParseResult res = SqlToYql("batch update plato.Input set key = 777, value = 'cool' where key = 200;", 10, "kikimr"); + UNIT_ASSERT(res.Root); + + TVerifyLineFunc verifyLine = [](const TString& word, const TString& line) { + if (word == "Write") { + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("('mode 'update)")); + UNIT_ASSERT_VALUES_UNEQUAL(TString::npos, line.find("'('is_batch 'true)")); + } + }; + + TWordCountHive elementStat = {{TString("Write"), 0}}; + VerifyProgram(res, elementStat, verifyLine); + + UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); + } + Y_UNIT_TEST(UpdateByMultiValues) { NYql::TAstParseResult res = SqlToYql("update plato.Input set (key, value, subkey) = ('2','ddd',':') where key = 200;", 10, "kikimr"); UNIT_ASSERT(res.Root); @@ -1460,6 +1501,12 @@ Y_UNIT_TEST_SUITE(SqlParsingOnly) { UNIT_ASSERT_VALUES_EQUAL(1, elementStat["Write"]); } + Y_UNIT_TEST(UpdateOnBatch) { + NYql::TAstParseResult res = SqlToYql("batch update plato.Input on (key, value) values (5, 'cool')", 10, "kikimr"); + UNIT_ASSERT(!res.Root); + UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:1:6: Error: BATCH UPDATE is unsupported with ON\n"); + } + Y_UNIT_TEST(UnionAllTest) { NYql::TAstParseResult res = SqlToYql("PRAGMA DisableEmitUnionMerge; SELECT key FROM plato.Input UNION ALL select subkey FROM plato.Input;"); UNIT_ASSERT(res.Root); @@ -5860,7 +5907,7 @@ Y_UNIT_TEST_SUITE(AnsiIdentsNegative) { "*/ select 1;"; res = SqlToYql(req); UNIT_ASSERT(!res.Root); - UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: mismatched input '*' expecting {';', '(', '$', ALTER, ANALYZE, BACKUP, COMMIT, CREATE, DECLARE, DEFINE, DELETE, DISCARD, DO, DROP, EVALUATE, EXPLAIN, EXPORT, FOR, FROM, GRANT, IF, IMPORT, INSERT, PARALLEL, PRAGMA, PROCESS, REDUCE, REPLACE, RESTORE, REVOKE, ROLLBACK, SELECT, UPDATE, UPSERT, USE, VALUES}\n"); + UNIT_ASSERT_NO_DIFF(Err2Str(res), "<main>:4:0: Error: mismatched input '*' expecting {';', '(', '$', ALTER, ANALYZE, BACKUP, BATCH, COMMIT, CREATE, DECLARE, DEFINE, DELETE, DISCARD, DO, DROP, EVALUATE, EXPLAIN, EXPORT, FOR, FROM, GRANT, IF, IMPORT, INSERT, PARALLEL, PRAGMA, PROCESS, REDUCE, REPLACE, RESTORE, REVOKE, ROLLBACK, SELECT, UPDATE, UPSERT, USE, VALUES}\n"); res = SqlToYqlWithAnsiLexer(req); UNIT_ASSERT(res.Root); } |