diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_delete_index.cpp b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_delete_index.cpp index 37326b6221c9..ef3ff1232d38 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_delete_index.cpp +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_delete_index.cpp @@ -74,34 +74,47 @@ TExprBase BuildDeleteIndexStagesImpl(const TKikimrTableDescription& table, auto deleteIndexKeys = project(indexTableColumns); - if (indexDesc->Type == TIndexDescription::EType::GlobalSyncVectorKMeansTree) { - if (indexDesc->KeyColumns.size() > 1) { - const auto& prefixTable = kqpCtx.Tables->ExistingTable(kqpCtx.Cluster, TStringBuilder() << del.Table().Path().Value() - << "/" << indexDesc->Name << "/" << NKikimr::NTableIndex::NKMeans::PrefixTable); - deleteIndexKeys = BuildVectorIndexPrefixRows(table, prefixTable, false, indexDesc, deleteIndexKeys, indexTableColumns, del.Pos(), ctx); + switch (indexDesc->Type) { + case TIndexDescription::EType::GlobalSync: + case TIndexDescription::EType::GlobalAsync: + case TIndexDescription::EType::GlobalSyncUnique: { + // deleteIndexKeys are already correct + break; + } + case TIndexDescription::EType::GlobalSyncVectorKMeansTree: { + if (indexDesc->KeyColumns.size() > 1) { + const auto& prefixTable = kqpCtx.Tables->ExistingTable(kqpCtx.Cluster, TStringBuilder() << del.Table().Path().Value() + << "/" << indexDesc->Name << "/" << NKikimr::NTableIndex::NKMeans::PrefixTable); + deleteIndexKeys = BuildVectorIndexPrefixRows(table, prefixTable, false, indexDesc, deleteIndexKeys, indexTableColumns, del.Pos(), ctx); + } + deleteIndexKeys = BuildVectorIndexPostingRows(table, del.Table(), indexDesc->Name, + indexTableColumns, deleteIndexKeys, false, del.Pos(), ctx); + break; + } + case TIndexDescription::EType::GlobalFulltext: { + // For fulltext indexes, we need to tokenize the text from the rows being deleted + // and then delete the corresponding token rows from the index table + auto deleteKeysPrecompute = Build(ctx, del.Pos()) + .Connection() + .Output() + .Stage(ReadTableToStage(deleteIndexKeys, ctx)) + .Index().Build("0") + .Build() + .Build() + .Done(); + deleteIndexKeys = BuildFulltextIndexRows(table, indexDesc, deleteKeysPrecompute, indexTableColumnsSet, indexTableColumns, /*includeDataColumns=*/false, + del.Pos(), ctx); + break; } - - auto resolveUnion = BuildVectorIndexPostingRows(table, del.Table(), indexDesc->Name, - indexTableColumns, deleteIndexKeys, false, del.Pos(), ctx); - - auto indexDelete = Build(ctx, del.Pos()) - .Table(tableNode) - .Input(resolveUnion) - .ReturningColumns().Build() - .IsBatch(ctx.NewAtom(del.Pos(), "false")) - .Done(); - - effects.emplace_back(indexDelete); - } else { - auto indexDelete = Build(ctx, del.Pos()) - .Table(tableNode) - .Input(deleteIndexKeys) - .ReturningColumns().Build() - .IsBatch(ctx.NewAtom(del.Pos(), "false")) - .Done(); - - effects.emplace_back(std::move(indexDelete)); } + + auto indexDelete = Build(ctx, del.Pos()) + .Table(tableNode) + .Input(deleteIndexKeys) + .ReturningColumns().Build() + .IsBatch(ctx.NewAtom(del.Pos(), "false")) + .Done(); + effects.emplace_back(std::move(indexDelete)); } return Build(ctx, del.Pos()) diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_effects_impl.h b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_effects_impl.h index e743eaedea06..623132772e89 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_effects_impl.h +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_effects_impl.h @@ -98,6 +98,8 @@ NYql::NNodes::TKqpCnStreamLookup BuildStreamLookupOverPrecompute(const NYql::TKi NYql::NNodes::TExprBase input, const NYql::NNodes::TKqpTable& kqpTableNode, const NYql::TPositionHandle& pos, NYql::TExprContext& ctx, const TVector& extraColumnsToRead = {}); +NYql::NNodes::TDqStageBase ReadTableToStage(const NYql::NNodes::TExprBase& expr, NYql::TExprContext& ctx); + NYql::NNodes::TExprBase BuildVectorIndexPostingRows(const NYql::TKikimrTableDescription& table, const NYql::NNodes::TKqpTable& tableNode, const TString& indexName, @@ -118,9 +120,7 @@ std::pair BuildVectorIndexPref TVector& indexTableColumns, NYql::TPositionHandle pos, NYql::TExprContext& ctx); NYql::NNodes::TExprBase BuildFulltextIndexRows(const NYql::TKikimrTableDescription& table, const NYql::TIndexDescription* indexDesc, - const NYql::NNodes::TExprBase& inputRows, const THashSet& inputColumns, const TVector& indexTableColumns, + const NYql::NNodes::TExprBase& inputRows, const THashSet& inputColumns, TVector& indexTableColumns, bool includeDataColumns, NYql::TPositionHandle pos, NYql::TExprContext& ctx); -TVector BuildFulltextIndexColumns(const NYql::TKikimrTableDescription& table, const NYql::TIndexDescription* indexDesc); - } // NKikimr::NKqp::NOpt diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_fulltext_index.cpp b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_fulltext_index.cpp index 8f599a996c10..0293b9c29a8c 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_fulltext_index.cpp +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_fulltext_index.cpp @@ -8,7 +8,7 @@ using namespace NYql::NDq; using namespace NYql::NNodes; TExprBase BuildFulltextIndexRows(const TKikimrTableDescription& table, const TIndexDescription* indexDesc, - const NNodes::TExprBase& inputRows, const THashSet& inputColumns, const TVector& indexTableColumns, + const NNodes::TExprBase& inputRows, const THashSet& inputColumns, TVector& indexTableColumns, bool includeDataColumns, TPositionHandle pos, NYql::TExprContext& ctx) { // Extract fulltext index settings @@ -31,17 +31,10 @@ TExprBase BuildFulltextIndexRows(const TKikimrTableDescription& table, const TIn // Build output row structure for each token TVector tokenRowTuples; - // Add token column (first column in fulltext index) - auto tokenTuple = Build(ctx, pos) - .Name().Build(NTableIndex::NFulltext::TokenColumn) - .Value(tokenArg) - .Done(); - tokenRowTuples.emplace_back(tokenTuple); - - // Add all other columns (primary key + data columns) - for (const auto& column : indexTableColumns) { + indexTableColumns.clear(); + auto addIndexColumn = [&](const TStringBuf& column) { + indexTableColumns.emplace_back(column); auto columnAtom = ctx.NewAtom(pos, column); - if (inputColumns.contains(column)) { auto tuple = Build(ctx, pos) .Name(columnAtom) @@ -50,20 +43,37 @@ TExprBase BuildFulltextIndexRows(const TKikimrTableDescription& table, const TIn .Name(columnAtom) .Build() .Done(); - tokenRowTuples.emplace_back(tuple); } else { auto columnType = table.GetColumnType(TString(column)); - auto tuple = Build(ctx, pos) .Name(columnAtom) .Value() .OptionalType(NCommon::BuildTypeExpr(pos, *columnType, ctx)) .Build() .Done(); - tokenRowTuples.emplace_back(tuple); } + }; + + // Add token column first + indexTableColumns.emplace_back(NTableIndex::NFulltext::TokenColumn); + auto tokenTuple = Build(ctx, pos) + .Name().Build(NTableIndex::NFulltext::TokenColumn) + .Value(tokenArg) + .Done(); + tokenRowTuples.emplace_back(tokenTuple); + + // Add primary key columns + for (const auto& column : table.Metadata->KeyColumnNames) { + addIndexColumn(column); + } + + // Add data columns (covered columns) + if (includeDataColumns) { + for (const auto& column : indexDesc->DataColumns) { + addIndexColumn(column); + } } // Create lambda that builds output row for each token @@ -126,29 +136,4 @@ TExprBase BuildFulltextIndexRows(const TKikimrTableDescription& table, const TIn .Done(); } -TVector BuildFulltextIndexColumns(const TKikimrTableDescription& table, const TIndexDescription* indexDesc) { - TVector indexTableColumns; - THashSet indexTableColumnSet; - - // Add token column first (replaces the text column) - indexTableColumns.emplace_back(NTableIndex::NFulltext::TokenColumn); - indexTableColumnSet.insert(NTableIndex::NFulltext::TokenColumn); - - // Add primary key columns - for (const auto& column : table.Metadata->KeyColumnNames) { - if (indexTableColumnSet.insert(column).second) { - indexTableColumns.emplace_back(column); - } - } - - // Add data columns (covered columns) - for (const auto& column : indexDesc->DataColumns) { - if (indexTableColumnSet.insert(column).second) { - indexTableColumns.emplace_back(column); - } - } - - return indexTableColumns; -} - } diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert_index.cpp b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert_index.cpp index 210ac9c0bf2d..66fd6c5fb1a1 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert_index.cpp +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert_index.cpp @@ -227,11 +227,9 @@ TExprBase KqpBuildInsertIndexStages(TExprBase node, TExprContext& ctx, const TKq break; } case TIndexDescription::EType::GlobalFulltext: { - // For fulltext indexes, we need to tokenize the text and create index rows - upsertIndexRows = BuildFulltextIndexRows(table, indexDesc, insertRowsPrecompute, inputColumnsSet, indexTableColumns, + // For fulltext indexes, we need to tokenize the text and create index rows and refill index columns + upsertIndexRows = BuildFulltextIndexRows(table, indexDesc, insertRowsPrecompute, inputColumnsSet, indexTableColumns, /*includeDataColumns=*/true, insert.Pos(), ctx); - // Update columns to reflect transformation: text column -> __ydb_token - indexTableColumns = BuildFulltextIndexColumns(table, indexDesc); break; } } diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_update.cpp b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_update.cpp index 35311a1507e2..b3e1eb0bcd54 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_update.cpp +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_update.cpp @@ -240,4 +240,40 @@ TExprBase KqpBuildUpdateStages(TExprBase node, TExprContext& ctx, const TKqpOpti } } +TDqStageBase ReadTableToStage(const TExprBase& expr, TExprContext& ctx) { + if (expr.Maybe()) { + return expr.Cast(); + } + if (expr.Maybe()) { + return expr.Cast().Output().Stage(); + } + auto pos = expr.Pos(); + TVector inputs; + TVector args; + TNodeOnNodeOwnedMap replaces; + int i = 1; + VisitExpr(expr.Ptr(), [&](const TExprNode::TPtr& node) { + TExprBase expr(node); + if (auto cast = expr.Maybe()) { + auto newArg = ctx.NewArgument(pos, TStringBuilder() << "rows" << i); + inputs.emplace_back(node); + args.emplace_back(newArg); + replaces.emplace(expr.Raw(), newArg); + return false; + } + return true; + }); + return Build(ctx, pos) + .Inputs() + .Add(inputs) + .Build() + .Program() + .Args(args) + .Body(ctx.ReplaceNodes(expr.Ptr(), replaces)) + .Build() + .Settings() + .Build() + .Done(); +} + } // namespace NKikimr::NKqp::NOpt diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_vector_index.cpp b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_vector_index.cpp index b426a0884be2..040df03517b1 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_vector_index.cpp +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_vector_index.cpp @@ -6,42 +6,6 @@ using namespace NYql; using namespace NYql::NDq; using namespace NYql::NNodes; -TDqStageBase ReadTableToStage(const TExprBase& expr, TExprContext& ctx) { - if (expr.Maybe()) { - return expr.Cast(); - } - if (expr.Maybe()) { - return expr.Cast().Output().Stage(); - } - auto pos = expr.Pos(); - TVector inputs; - TVector args; - TNodeOnNodeOwnedMap replaces; - int i = 1; - VisitExpr(expr.Ptr(), [&](const TExprNode::TPtr& node) { - TExprBase expr(node); - if (auto cast = expr.Maybe()) { - auto newArg = ctx.NewArgument(pos, TStringBuilder() << "rows" << i); - inputs.emplace_back(node); - args.emplace_back(newArg); - replaces.emplace(expr.Raw(), newArg); - return false; - } - return true; - }); - return Build(ctx, pos) - .Inputs() - .Add(inputs) - .Build() - .Program() - .Args(args) - .Body(ctx.ReplaceNodes(expr.Ptr(), replaces)) - .Build() - .Settings() - .Build() - .Done(); -} - TExprBase BuildVectorIndexPostingRows(const TKikimrTableDescription& table, const TKqpTable& tableNode, const TString& indexName, diff --git a/ydb/core/kqp/ut/indexes/kqp_indexes_fulltext_ut.cpp b/ydb/core/kqp/ut/indexes/kqp_indexes_fulltext_ut.cpp index 5550f5239583..c9975be3814b 100644 --- a/ydb/core/kqp/ut/indexes/kqp_indexes_fulltext_ut.cpp +++ b/ydb/core/kqp/ut/indexes/kqp_indexes_fulltext_ut.cpp @@ -44,7 +44,7 @@ void UpsertTexts(NQuery::TQueryClient& db) { (100, "Cats chase small animals.", "cats data"), (200, "Dogs chase small cats.", "dogs data"), (300, "Cats love cats.", "cats cats data"), - (400, "Foxes love dogs.", "fox data") + (400, "Foxes love dogs.", "foxes data") )sql"; auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); @@ -122,10 +122,10 @@ Y_UNIT_TEST(AddIndexCovered) { [["cats data"];[100u];"chase"]; [["dogs data"];[200u];"chase"]; [["dogs data"];[200u];"dogs"]; - [["fox data"];[400u];"dogs"]; - [["fox data"];[400u];"foxes"]; + [["foxes data"];[400u];"dogs"]; + [["foxes data"];[400u];"foxes"]; [["cats cats data"];[300u];"love"]; - [["fox data"];[400u];"love"]; + [["foxes data"];[400u];"love"]; [["cats data"];[100u];"small"]; [["dogs data"];[200u];"small"] ])", NYdb::FormatResultSetYson(index)); @@ -201,7 +201,6 @@ Y_UNIT_TEST(InsertRowMultipleTimes) { auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); } - index = ReadIndex(db); CompareYson(R"([ [[100u];"cats"]; @@ -349,26 +348,384 @@ Y_UNIT_TEST(UpsertRowCovered) { } Y_UNIT_TEST(DeleteRow) { - // TODO: deletes are not implemented + auto kikimr = Kikimr(); + auto db = kikimr.GetQueryClient(); + + CreateTexts(db); + UpsertTexts(db); + AddIndex(db); + auto index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[200u];"cats"]; + [[300u];"cats"]; + [[100u];"chase"]; + [[200u];"chase"]; + [[200u];"dogs"]; + [[400u];"dogs"]; + [[400u];"foxes"]; + [[300u];"love"]; + [[400u];"love"]; + [[100u];"small"]; + [[200u];"small"] + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by PK + TString query = R"sql( + DELETE FROM `/Root/Texts` WHERE Key = 200; + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[300u];"cats"]; + [[100u];"chase"]; + [[400u];"dogs"]; + [[400u];"foxes"]; + [[300u];"love"]; + [[400u];"love"]; + [[100u];"small"] + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by filter + TString query = R"sql( + DELETE FROM `/Root/Texts` WHERE Data = "foxes data"; + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[300u];"cats"]; + [[100u];"chase"]; + [[300u];"love"]; + [[100u];"small"] + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by ON + TString query = R"sql( + DELETE FROM `/Root/Texts` ON SELECT 300 AS `Key`; + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[100u];"chase"]; + [[100u];"small"] + ])", NYdb::FormatResultSetYson(index)); +} + +Y_UNIT_TEST(DeleteRowMultipleTimes) { + auto kikimr = Kikimr(); + auto db = kikimr.GetQueryClient(); + + CreateTexts(db); + UpsertTexts(db); + AddIndex(db); + auto index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[200u];"cats"]; + [[300u];"cats"]; + [[100u];"chase"]; + [[200u];"chase"]; + [[200u];"dogs"]; + [[400u];"dogs"]; + [[400u];"foxes"]; + [[300u];"love"]; + [[400u];"love"]; + [[100u];"small"]; + [[200u];"small"] + ])", NYdb::FormatResultSetYson(index)); - // TODO: test delete by key and filter + { // DeleteRow by multiple PK + TString query = R"sql( + DELETE FROM `/Root/Texts` WHERE Key = 200 OR Key = 400; + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[300u];"cats"]; + [[100u];"chase"]; + [[300u];"love"]; + [[100u];"small"] + ])", NYdb::FormatResultSetYson(index)); +} + +Y_UNIT_TEST(DeleteRowReturning) { + auto kikimr = Kikimr(); + auto db = kikimr.GetQueryClient(); + + CreateTexts(db); + UpsertTexts(db); + AddIndex(db); + auto index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[200u];"cats"]; + [[300u];"cats"]; + [[100u];"chase"]; + [[200u];"chase"]; + [[200u];"dogs"]; + [[400u];"dogs"]; + [[400u];"foxes"]; + [[300u];"love"]; + [[400u];"love"]; + [[100u];"small"]; + [[200u];"small"] + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by PK + TString query = R"sql( + DELETE FROM `/Root/Texts` WHERE Key = 200 + RETURNING * + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([ + [["dogs data"];[200u];["Dogs chase small cats."]] + ])", NYdb::FormatResultSetYson(result.GetResultSet(0))); + } + index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[300u];"cats"]; + [[100u];"chase"]; + [[400u];"dogs"]; + [[400u];"foxes"]; + [[300u];"love"]; + [[400u];"love"]; + [[100u];"small"] + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by filter + TString query = R"sql( + DELETE FROM `/Root/Texts` WHERE Data = "foxes data" + RETURNING * + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([ + [["foxes data"];[400u];["Foxes love dogs."]] + ])", NYdb::FormatResultSetYson(result.GetResultSet(0))); + } + index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[300u];"cats"]; + [[100u];"chase"]; + [[300u];"love"]; + [[100u];"small"] + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by ON + TString query = R"sql( + DELETE FROM `/Root/Texts` ON SELECT 300 AS `Key` + RETURNING * + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([ + [["cats cats data"];[300u];["Cats love cats."]] + ])", NYdb::FormatResultSetYson(result.GetResultSet(0))); + } + index = ReadIndex(db); + CompareYson(R"([ + [[100u];"animals"]; + [[100u];"cats"]; + [[100u];"chase"]; + [[100u];"small"] + ])", NYdb::FormatResultSetYson(index)); } Y_UNIT_TEST(DeleteRowCovered) { - // TODO: deletes are not implemented + auto kikimr = Kikimr(); + auto db = kikimr.GetQueryClient(); + + CreateTexts(db); + UpsertTexts(db); + AddIndexCovered(db); + auto index = ReadIndex(db); + CompareYson(R"([ + [["cats data"];[100u];"animals"]; + [["cats data"];[100u];"cats"]; + [["dogs data"];[200u];"cats"]; + [["cats cats data"];[300u];"cats"]; + [["cats data"];[100u];"chase"]; + [["dogs data"];[200u];"chase"]; + [["dogs data"];[200u];"dogs"]; + [["foxes data"];[400u];"dogs"]; + [["foxes data"];[400u];"foxes"]; + [["cats cats data"];[300u];"love"]; + [["foxes data"];[400u];"love"]; + [["cats data"];[100u];"small"]; + [["dogs data"];[200u];"small"] + ])", NYdb::FormatResultSetYson(index)); - // TODO: test delete by key and filter + { // DeleteRow by PK + TString query = R"sql( + DELETE FROM `/Root/Texts` WHERE Key = 200; + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + index = ReadIndex(db); + CompareYson(R"([ + [["cats data"];[100u];"animals"]; + [["cats data"];[100u];"cats"]; + [["cats cats data"];[300u];"cats"]; + [["cats data"];[100u];"chase"]; + [["foxes data"];[400u];"dogs"]; + [["foxes data"];[400u];"foxes"]; + [["cats cats data"];[300u];"love"]; + [["foxes data"];[400u];"love"]; + [["cats data"];[100u];"small"]; + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by filter + TString query = R"sql( + DELETE FROM `/Root/Texts` WHERE Data = "foxes data"; + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + index = ReadIndex(db); + CompareYson(R"([ + [["cats data"];[100u];"animals"]; + [["cats data"];[100u];"cats"]; + [["cats cats data"];[300u];"cats"]; + [["cats data"];[100u];"chase"]; + [["cats cats data"];[300u];"love"]; + [["cats data"];[100u];"small"]; + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by ON + TString query = R"sql( + DELETE FROM `/Root/Texts` ON SELECT 300 AS `Key`; + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + index = ReadIndex(db); + CompareYson(R"([ + [["cats data"];[100u];"animals"]; + [["cats data"];[100u];"cats"]; + [["cats data"];[100u];"chase"]; + [["cats data"];[100u];"small"]; + ])", NYdb::FormatResultSetYson(index)); } -Y_UNIT_TEST(UpdateRow) { - // TODO: deletes are not implemented +Y_UNIT_TEST(DeleteRowCoveredReturning) { + auto kikimr = Kikimr(); + auto db = kikimr.GetQueryClient(); + + CreateTexts(db); + UpsertTexts(db); + AddIndexCovered(db); + auto index = ReadIndex(db); + CompareYson(R"([ + [["cats data"];[100u];"animals"]; + [["cats data"];[100u];"cats"]; + [["dogs data"];[200u];"cats"]; + [["cats cats data"];[300u];"cats"]; + [["cats data"];[100u];"chase"]; + [["dogs data"];[200u];"chase"]; + [["dogs data"];[200u];"dogs"]; + [["foxes data"];[400u];"dogs"]; + [["foxes data"];[400u];"foxes"]; + [["cats cats data"];[300u];"love"]; + [["foxes data"];[400u];"love"]; + [["cats data"];[100u];"small"]; + [["dogs data"];[200u];"small"] + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by PK + TString query = R"sql( + DELETE FROM `/Root/Texts` WHERE Key = 200 + RETURNING * + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([ + [["dogs data"];[200u];["Dogs chase small cats."]] + ])", NYdb::FormatResultSetYson(result.GetResultSet(0))); + } + index = ReadIndex(db); + CompareYson(R"([ + [["cats data"];[100u];"animals"]; + [["cats data"];[100u];"cats"]; + [["cats cats data"];[300u];"cats"]; + [["cats data"];[100u];"chase"]; + [["foxes data"];[400u];"dogs"]; + [["foxes data"];[400u];"foxes"]; + [["cats cats data"];[300u];"love"]; + [["foxes data"];[400u];"love"]; + [["cats data"];[100u];"small"]; + ])", NYdb::FormatResultSetYson(index)); + + { // DeleteRow by filter + TString query = R"sql( + DELETE FROM `/Root/Texts` WHERE Data = "foxes data" + RETURNING * + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([ + [["foxes data"];[400u];["Foxes love dogs."]] + ])", NYdb::FormatResultSetYson(result.GetResultSet(0))); + } + index = ReadIndex(db); + CompareYson(R"([ + [["cats data"];[100u];"animals"]; + [["cats data"];[100u];"cats"]; + [["cats cats data"];[300u];"cats"]; + [["cats data"];[100u];"chase"]; + [["cats cats data"];[300u];"love"]; + [["cats data"];[100u];"small"]; + ])", NYdb::FormatResultSetYson(index)); + { // DeleteRow by ON + TString query = R"sql( + DELETE FROM `/Root/Texts` ON SELECT 300 AS `Key` + RETURNING * + )sql"; + auto result = db.ExecuteQuery(query, NYdb::NQuery::TTxControl::NoTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([ + [["cats cats data"];[300u];["Cats love cats."]] + ])", NYdb::FormatResultSetYson(result.GetResultSet(0))); + } + index = ReadIndex(db); + CompareYson(R"([ + [["cats data"];[100u];"animals"]; + [["cats data"];[100u];"cats"]; + [["cats data"];[100u];"chase"]; + [["cats data"];[100u];"small"]; + ])", NYdb::FormatResultSetYson(index)); +} + +Y_UNIT_TEST(UpdateRow) { // TODO: test update of key, text, data } Y_UNIT_TEST(UpdateRowCovered) { - // TODO: deletes are not implemented - // TODO: test update of key, text, data } @@ -443,10 +800,10 @@ Y_UNIT_TEST(CreateTableCovered) { [["cats data"];[100u];"chase"]; [["dogs data"];[200u];"chase"]; [["dogs data"];[200u];"dogs"]; - [["fox data"];[400u];"dogs"]; - [["fox data"];[400u];"foxes"]; + [["foxes data"];[400u];"dogs"]; + [["foxes data"];[400u];"foxes"]; [["cats cats data"];[300u];"love"]; - [["fox data"];[400u];"love"]; + [["foxes data"];[400u];"love"]; [["cats data"];[100u];"small"]; [["dogs data"];[200u];"small"] ])", NYdb::FormatResultSetYson(index));