diff options
author | dcherednik <dcherednik@ydb.tech> | 2023-11-01 13:15:22 +0300 |
---|---|---|
committer | dcherednik <dcherednik@ydb.tech> | 2023-11-01 13:46:04 +0300 |
commit | 42a1fee25dda68794ca60302fec5673206fe106f (patch) | |
tree | 8a21abbe1f4db376187745e6ba654c359bad1485 | |
parent | 9793ffdc8db2b9940f15deebe3b951504bac64be (diff) | |
download | ydb-42a1fee25dda68794ca60302fec5673206fe106f.tar.gz |
Do not try to add dict for index if no input for column. Add idx test over uniq index KIKIMR-19872
-rw-r--r-- | ydb/core/kqp/opt/kqp_opt_kql.cpp | 2 | ||||
-rw-r--r-- | ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.cpp | 24 | ||||
-rw-r--r-- | ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.h | 14 | ||||
-rw-r--r-- | ydb/core/kqp/opt/physical/effects/kqp_opt_phy_upsert_index.cpp | 39 | ||||
-rw-r--r-- | ydb/core/kqp/ut/idx_test/ydb_index_ut.cpp | 70 | ||||
-rw-r--r-- | ydb/core/kqp/ut/indexes/kqp_indexes_multishard_ut.cpp | 22 | ||||
-rw-r--r-- | ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp | 246 |
7 files changed, 376 insertions, 41 deletions
diff --git a/ydb/core/kqp/opt/kqp_opt_kql.cpp b/ydb/core/kqp/opt/kqp_opt_kql.cpp index ecee50c446..2ea11132ab 100644 --- a/ydb/core/kqp/opt/kqp_opt_kql.cpp +++ b/ydb/core/kqp/opt/kqp_opt_kql.cpp @@ -593,7 +593,7 @@ TVector<TExprBase> BuildUpdateTableWithIndex(const TKiUpdateTable& update, const return x.second->Type == TIndexDescription::EType::GlobalSyncUnique; }; - bool hasUniqIndex = std::find_if(indexes.begin(), indexes.end(), is_uniq) != indexes.end(); + const bool hasUniqIndex = std::find_if(indexes.begin(), indexes.end(), is_uniq) != indexes.end(); // For uniq index rewrite UPDATE in to UPDATE ON if (hasUniqIndex) { diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.cpp b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.cpp index ea2b23015a..e6c7c4b759 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.cpp +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.cpp @@ -41,7 +41,7 @@ class TInsertUniqBuildHelper : public TUniqBuildHelper { public: TInsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, NYql::TExprContext& ctx) - : TUniqBuildHelper(table, pos, ctx, false) + : TUniqBuildHelper(table, nullptr, pos, ctx, false) {} private: @@ -119,9 +119,9 @@ private: class TUpsertUniqBuildHelper : public TUniqBuildHelper { public: - TUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, - NYql::TExprContext& ctx) - : TUniqBuildHelper(table, pos, ctx, true) + TUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, const THashSet<TString>& usedIndexes, + NYql::TPositionHandle pos, NYql::TExprContext& ctx) + : TUniqBuildHelper(table, &usedIndexes, pos, ctx, true) , PkDict(MakeUniqCheckDict(MakeTableKeySelector(table.Metadata, pos, ctx), RowsListArg, pos, ctx)) {} @@ -243,7 +243,7 @@ private: } TVector<TUniqBuildHelper::TUniqCheckNodes> TUniqBuildHelper::Prepare(const TCoArgument& rowsListArg, - const TKikimrTableDescription& table, + const TKikimrTableDescription& table, const THashSet<TString>* usedIndexes, TPositionHandle pos, TExprContext& ctx, bool skipPkCheck) { TVector<TUniqCheckNodes> checks; @@ -258,6 +258,8 @@ TVector<TUniqBuildHelper::TUniqCheckNodes> TUniqBuildHelper::Prepare(const TCoAr continue; if (table.Metadata->Indexes[i].Type != TIndexDescription::EType::GlobalSyncUnique) continue; + if (usedIndexes && !usedIndexes->contains(table.Metadata->Indexes[i].Name)) + continue; // Compatibility with PG semantic - allow multiple null in columns with unique constaint TVector<TCoAtom> skipNullColumns; @@ -283,11 +285,11 @@ TVector<TUniqBuildHelper::TUniqCheckNodes> TUniqBuildHelper::Prepare(const TCoAr return checks; } -TUniqBuildHelper::TUniqBuildHelper(const TKikimrTableDescription& table, +TUniqBuildHelper::TUniqBuildHelper(const TKikimrTableDescription& table, const THashSet<TString>* usedIndexes, TPositionHandle pos, TExprContext& ctx, bool skipPkCheck) : RowsListArg(ctx.NewArgument(pos, "rows_list")) , False(MakeBool(pos, false, ctx)) - , Checks(Prepare(RowsListArg, table, pos, ctx, skipPkCheck)) + , Checks(Prepare(RowsListArg, table, usedIndexes, pos, ctx, skipPkCheck)) {} TUniqBuildHelper::TUniqCheckNodes TUniqBuildHelper::MakeUniqCheckNodes(const TCoLambda& selector, @@ -507,13 +509,13 @@ namespace NKikimr::NKqp::NOpt { TUniqBuildHelper::TPtr CreateInsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, NYql::TExprContext& ctx) { - return std::make_unique<TInsertUniqBuildHelper>(table, pos, ctx); + return std::make_unique<TInsertUniqBuildHelper>(table, pos, ctx); } -TUniqBuildHelper::TPtr CreateUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, - NYql::TExprContext& ctx) +TUniqBuildHelper::TPtr CreateUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, + const THashSet<TString>& usedIndexes, NYql::TPositionHandle pos, NYql::TExprContext& ctx) { - return std::make_unique<TUpsertUniqBuildHelper>(table, pos, ctx); + return std::make_unique<TUpsertUniqBuildHelper>(table, usedIndexes, pos, ctx); } } diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.h b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.h index a22f4f9edf..392c1dcb76 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.h +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.h @@ -28,7 +28,7 @@ public: protected: // table - metadata of table // skipPkCheck - false for insert mode, generate check on PK to issue an arror on PK conflict - TUniqBuildHelper(const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, + TUniqBuildHelper(const NYql::TKikimrTableDescription& table, const THashSet<TString>* usedIndexes, NYql::TPositionHandle pos, NYql::TExprContext& ctx, bool skipPkCheck); size_t CalcComputeKeysStageOutputNum() const; @@ -63,8 +63,8 @@ private: static TUniqCheckNodes MakeUniqCheckNodes(const NYql::NNodes::TCoLambda& selector, const NYql::NNodes::TExprBase& rowsListArg, NYql::TPositionHandle pos, NYql::TExprContext& ctx); static TVector<TUniqCheckNodes> Prepare(const NYql::NNodes::TCoArgument& rowsListArg, - const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, NYql::TExprContext& ctx, - bool skipPkCheck); + const NYql::TKikimrTableDescription& table, const THashSet<TString>* usedIndexes, NYql::TPositionHandle pos, + NYql::TExprContext& ctx, bool skipPkCheck); virtual NYql::NNodes::TDqCnUnionAll CreateLookupStageWithConnection(const NYql::NNodes::TDqStage& computeKeysStage, size_t stageOut, const NYql::TKikimrTableMetadata& mainTableMeta, TUniqCheckNodes::TIndexId indexId, @@ -78,9 +78,9 @@ private: const TChecks Checks; }; -TUniqBuildHelper::TPtr CreateInsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, - NYql::TExprContext& ctx); +TUniqBuildHelper::TPtr CreateInsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, + NYql::TPositionHandle pos, NYql::TExprContext& ctx); -TUniqBuildHelper::TPtr CreateUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, - NYql::TExprContext& ctx); +TUniqBuildHelper::TPtr CreateUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, + const THashSet<TString>& usedIndexes, NYql::TPositionHandle pos, NYql::TExprContext& ctx); } diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_upsert_index.cpp b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_upsert_index.cpp index 8aeaab248d..d5b2d71cd7 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_upsert_index.cpp +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_upsert_index.cpp @@ -301,14 +301,30 @@ TExprBase MakeUpsertIndexRows(TKqpPhyUpsertIndexMode mode, const TDqPhyPrecomput .Done(); } -TMaybe<TCondenseInputResult> CheckUniqueConstraint(const TExprBase& inputRows, const TKikimrTableDescription& table, TPositionHandle pos, TExprContext& ctx) +TMaybe<TCondenseInputResult> CheckUniqueConstraint(const TExprBase& inputRows, const THashSet<TStringBuf> inputColumns, + const TKikimrTableDescription& table, const TSecondaryIndexes& indexes, TPositionHandle pos, TExprContext& ctx) { auto condenseResult = CondenseInput(inputRows, ctx); if (!condenseResult) { return {}; } - auto helper = CreateUpsertUniqBuildHelper(table, pos, ctx); + // Check uniq constraint for indexes which will be updated by input data. + // but skip main table pk columns - handle case where we have a complex index is a tuple contains pk + const auto& mainPk = table.Metadata->KeyColumnNames; + THashSet<TString> usedIndexes; + for (const auto& [_, indexDesc] : indexes) { + for (const auto& indexKeyCol : indexDesc->KeyColumns) { + if (inputColumns.contains(indexKeyCol) && + (std::find(mainPk.begin(), mainPk.end(), indexKeyCol) == mainPk.end())) + { + usedIndexes.insert(indexDesc->Name); + break; + } + } + } + + auto helper = CreateUpsertUniqBuildHelper(table, usedIndexes, pos, ctx); if (helper->GetChecksNum() == 0) { return condenseResult; } @@ -411,7 +427,15 @@ TMaybeNode<TExprList> KqpPhyUpsertIndexEffectsImpl(TKqpPhyUpsertIndexMode mode, const auto& pk = table.Metadata->KeyColumnNames; - auto checkedInput = CheckUniqueConstraint(inputRows, table, pos, ctx); + THashSet<TStringBuf> inputColumnsSet; + for (const auto& column : inputColumns) { + inputColumnsSet.emplace(column.Value()); + } + + auto filter = (mode == TKqpPhyUpsertIndexMode::UpdateOn) ? &inputColumnsSet : nullptr; + const auto indexes = BuildSecondaryIndexVector(table, pos, ctx, filter); + + auto checkedInput = CheckUniqueConstraint(inputRows, inputColumnsSet, table, indexes, pos, ctx); if (!checkedInput) { return {}; } @@ -420,14 +444,7 @@ TMaybeNode<TExprList> KqpPhyUpsertIndexEffectsImpl(TKqpPhyUpsertIndexMode mode, auto inputRowsAndKeys = PrecomputeRowsAndKeys(condenseInputResult, table, pos, ctx); - THashSet<TStringBuf> inputColumnsSet; - for (const auto& column : inputColumns) { - inputColumnsSet.emplace(column.Value()); - } - - auto filter = (mode == TKqpPhyUpsertIndexMode::UpdateOn) ? &inputColumnsSet : nullptr; - const auto indexes = BuildSecondaryIndexVector(table, pos, ctx, filter); - // For UPSERT check that indexes is not empty for UPSERT + // For UPSERT check that indexes is not empty YQL_ENSURE(mode == TKqpPhyUpsertIndexMode::UpdateOn || indexes); THashSet<TString> indexDataColumns = CreateDataColumnSetToRead(indexes, inputColumnsSet); diff --git a/ydb/core/kqp/ut/idx_test/ydb_index_ut.cpp b/ydb/core/kqp/ut/idx_test/ydb_index_ut.cpp index 0cefec7e2b..3552a8f931 100644 --- a/ydb/core/kqp/ut/idx_test/ydb_index_ut.cpp +++ b/ydb/core/kqp/ut/idx_test/ydb_index_ut.cpp @@ -10,6 +10,7 @@ struct TRunSettings { const bool PkOverlap; const bool IndexOverlap; const bool WithDataColumn; + const bool UniqIndex; }; static const TString TABLE_PATH = "Root/TestIdx"; @@ -18,6 +19,7 @@ static void RunTest(ui32 shardsCount, ui32 rowsCount, ui32 indexCount, const TRu bool pkOverlap = settings.PkOverlap; bool indexOverlap = settings.IndexOverlap; bool withDataColumn = settings.WithDataColumn; + bool uniqIndex = settings.UniqIndex; TKikimrRunner kikimr(SyntaxV1Settings()); @@ -42,12 +44,25 @@ static void RunTest(ui32 shardsCount, ui32 rowsCount, ui32 indexCount, const TRu for (ui32 i = 0; i < indexCount; i++) { TStringStream ss; ss << "index_" << i; - builder.AddNullableColumn(ss.Str(), EPrimitiveType::Utf8); + if (uniqIndex && i == 0) { + builder.AddNullableColumn(ss.Str(), EPrimitiveType::Int8); + } else { + builder.AddNullableColumn(ss.Str(), EPrimitiveType::Utf8); + } + if (!pkOverlap) { - builder.AddSecondaryIndex(ss.Str() + "_name", TVector<TString>{ss.Str()}, dataColumn); + if (uniqIndex) { + builder.AddUniqueSecondaryIndex(ss.Str() + "_name", TVector<TString>{ss.Str()}, dataColumn); + } else { + builder.AddSecondaryIndex(ss.Str() + "_name", TVector<TString>{ss.Str()}, dataColumn); + } } else { - builder.AddSecondaryIndex(ss.Str() + "_name", TVector<TString>{ss.Str(), keyColumnName}, dataColumn); + if (uniqIndex) { + builder.AddUniqueSecondaryIndex(ss.Str() + "_name", TVector<TString>{ss.Str(), keyColumnName}, dataColumn); + } else { + builder.AddSecondaryIndex(ss.Str() + "_name", TVector<TString>{ss.Str(), keyColumnName}, dataColumn); + } } if (indexOverlap) { pks.push_back(ss.Str()); @@ -77,7 +92,26 @@ Y_UNIT_TEST_SUITE(YdbIndexTable) { UNIT_ASSERT_NO_EXCEPTION(RunTest(10, 1000, 1, TRunSettings { .PkOverlap = true, .IndexOverlap = false, - .WithDataColumn = false + .WithDataColumn = false, + .UniqIndex = false + })); + } + + Y_UNIT_TEST(MultiShardTableOneUniqIndex) { + UNIT_ASSERT_NO_EXCEPTION(RunTest(10, 20000, 1, TRunSettings { + .PkOverlap = true, + .IndexOverlap = false, + .WithDataColumn = false, + .UniqIndex = true + })); + } + + Y_UNIT_TEST(MultiShardTableUniqAndNonUniqIndex) { + UNIT_ASSERT_NO_EXCEPTION(RunTest(10, 20000, 2, TRunSettings { + .PkOverlap = true, + .IndexOverlap = false, + .WithDataColumn = false, + .UniqIndex = true })); } @@ -85,7 +119,17 @@ Y_UNIT_TEST_SUITE(YdbIndexTable) { UNIT_ASSERT_NO_EXCEPTION(RunTest(10, 1000, 1, TRunSettings{ .PkOverlap = true, .IndexOverlap = false, - .WithDataColumn = true + .WithDataColumn = true, + .UniqIndex = false + })); + } + + Y_UNIT_TEST(MultiShardTableOneUniqIndexDataColumn) { + UNIT_ASSERT_NO_EXCEPTION(RunTest(10, 20000, 1, TRunSettings { + .PkOverlap = true, + .IndexOverlap = false, + .WithDataColumn = true, + .UniqIndex = true })); } @@ -93,7 +137,8 @@ Y_UNIT_TEST_SUITE(YdbIndexTable) { UNIT_ASSERT_NO_EXCEPTION(RunTest(10, 1000, 1, TRunSettings{ .PkOverlap = false, .IndexOverlap = true, - .WithDataColumn = false + .WithDataColumn = false, + .UniqIndex = false })); } @@ -101,15 +146,17 @@ Y_UNIT_TEST_SUITE(YdbIndexTable) { UNIT_ASSERT_NO_EXCEPTION(RunTest(10, 1000, 1, TRunSettings{ .PkOverlap = false, .IndexOverlap = true, - .WithDataColumn = true + .WithDataColumn = true, + .UniqIndex = false })); } Y_UNIT_TEST(MultiShardTableOneIndexPkOverlap) { UNIT_ASSERT_NO_EXCEPTION(RunTest(10, 1000, 1, TRunSettings{ - .PkOverlap = true, - .IndexOverlap = false, - .WithDataColumn = false + .PkOverlap = true, + .IndexOverlap = false, + .WithDataColumn = false, + .UniqIndex = false })); } @@ -117,7 +164,8 @@ Y_UNIT_TEST_SUITE(YdbIndexTable) { UNIT_ASSERT_NO_EXCEPTION(RunTest(10, 1000, 2, TRunSettings{ .PkOverlap = false, .IndexOverlap = false, - .WithDataColumn = false + .WithDataColumn = false, + .UniqIndex = false })); } diff --git a/ydb/core/kqp/ut/indexes/kqp_indexes_multishard_ut.cpp b/ydb/core/kqp/ut/indexes/kqp_indexes_multishard_ut.cpp index 32eab8a75e..57944ffa03 100644 --- a/ydb/core/kqp/ut/indexes/kqp_indexes_multishard_ut.cpp +++ b/ydb/core/kqp/ut/indexes/kqp_indexes_multishard_ut.cpp @@ -480,6 +480,28 @@ return; } } + Y_UNIT_TEST(UpdateFkPkOverlap) { + TKikimrRunner kikimr(SyntaxV1Settings()); + CreateTableWithMultishardIndexComplexFkPk(kikimr.GetTestClient(), IG_UNIQUE); + auto db = kikimr.GetTableClient(); + auto session = db.CreateSession().GetValueSync().GetSession(); + FillTable(session); + + { + const TString query(Q_(R"( + UPDATE `/Root/MultiShardIndexed` SET fk = 1000000000 WHERE key = 2; + )")); + + auto result = ExecuteDataQuery(session, query); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::SUCCESS, result.GetIssues().ToString()); + } + { + const auto yson = ReadTableToYson(session, "/Root/MultiShardIndexed/index/indexImplTable"); + const TString expected = R"([[[1000000000u];[1u]];[[1000000000u];[2u]];[[3000000000u];[3u]];[[4294967295u];[4u]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + } + Y_UNIT_TEST(InsertNullInPk) { TKikimrRunner kikimr(SyntaxV1Settings()); CreateTableWithMultishardIndex(kikimr.GetTestClient(), IG_UNIQUE); diff --git a/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp b/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp index e698174358..35677c35d0 100644 --- a/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp +++ b/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp @@ -2138,6 +2138,251 @@ R"([[#;#;["Primary1"];[41u]];[["Secondary2"];[2u];["Primary2"];[42u]];[["Seconda } } + Y_UNIT_TEST(UniqAndNoUniqSecondaryIndex) { + auto setting = NKikimrKqp::TKqpSetting(); + auto serverSettings = TKikimrSettings() + .SetKqpSettings({setting}); + TKikimrRunner kikimr(serverSettings); + auto db = kikimr.GetTableClient(); + auto session = db.CreateSession().GetValueSync().GetSession(); + + { + auto tableBuilder = db.GetTableBuilder(); + tableBuilder + .AddNullableColumn("Key", EPrimitiveType::String) + .AddNullableColumn("Value1", EPrimitiveType::String) + .AddNullableColumn("Value2", EPrimitiveType::String); + tableBuilder.SetPrimaryKeyColumns(TVector<TString>{"Key"}); + tableBuilder.AddUniqueSecondaryIndex("Index1Uniq", {"Value1"}); + tableBuilder.AddSecondaryIndex("Index2NotUniq", "Value2"); + auto result = session.CreateTable("/Root/TestTable", tableBuilder.Build()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(result.IsTransportError(), false); + UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS); + } + + { + const TString query1(Q_(R"( + UPSERT INTO `/Root/TestTable` (Key, Value1, Value2) VALUES + ("Primary1", "Val1", "Val2"); + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT(result.IsSuccess()); + } + + { + const TString query1(Q_(R"( + UPSERT INTO `/Root/TestTable` (Key, Value1, Value2) VALUES + ("Primary2", "Val1", "blabla"); + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + const TString query1(Q_(R"( + INSERT INTO `/Root/TestTable` (Key, Value1, Value2) VALUES + ("Primary3", "Val1", "blabla"); + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + const TString query1(Q_(R"( + REPLACE INTO `/Root/TestTable` (Key, Value1, Value2) VALUES + ("Primary3", "Val1", "blabla"); + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable/Index1Uniq/indexImplTable"); + const TString expected = R"([[["Val1"];["Primary1"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable/Index2NotUniq/indexImplTable"); + const TString expected = R"([[["Val2"];["Primary1"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable"); + const TString expected = R"([[["Primary1"];["Val1"];["Val2"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + } + + { + const TString query1(Q_(R"( + UPDATE `/Root/TestTable` SET Value2 = "Val2_1" WHERE Key = "Primary1"; + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT(result.IsSuccess()); + + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable/Index1Uniq/indexImplTable"); + const TString expected = R"([[["Val1"];["Primary1"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable/Index2NotUniq/indexImplTable"); + const TString expected = R"([[["Val2_1"];["Primary1"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable"); + const TString expected = R"([[["Primary1"];["Val1"];["Val2_1"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + } + } + + Y_UNIT_TEST(UniqAndNoUniqSecondaryIndexWithCover) { + auto setting = NKikimrKqp::TKqpSetting(); + auto serverSettings = TKikimrSettings() + .SetKqpSettings({setting}); + TKikimrRunner kikimr(serverSettings); + auto db = kikimr.GetTableClient(); + auto session = db.CreateSession().GetValueSync().GetSession(); + + { + auto tableBuilder = db.GetTableBuilder(); + tableBuilder + .AddNullableColumn("Key", EPrimitiveType::String) + .AddNullableColumn("Value1", EPrimitiveType::String) + .AddNullableColumn("Value2", EPrimitiveType::String); + tableBuilder.SetPrimaryKeyColumns(TVector<TString>{"Key"}); + tableBuilder.AddUniqueSecondaryIndex("Index1Uniq", {"Value1"}, {"Value2"}); + tableBuilder.AddSecondaryIndex("Index2NotUniq", {"Value2"}, {"Value1"}); + auto result = session.CreateTable("/Root/TestTable", tableBuilder.Build()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL(result.IsTransportError(), false); + UNIT_ASSERT_VALUES_EQUAL(result.GetStatus(), EStatus::SUCCESS); + } + + { + const TString query1(Q_(R"( + UPSERT INTO `/Root/TestTable` (Key, Value1, Value2) VALUES + ("Primary1", "Val1", "Val2"); + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT(result.IsSuccess()); + } + + { + const TString query1(Q_(R"( + UPSERT INTO `/Root/TestTable` (Key, Value1, Value2) VALUES + ("Primary2", "Val1", "blabla"); + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + const TString query1(Q_(R"( + INSERT INTO `/Root/TestTable` (Key, Value1, Value2) VALUES + ("Primary3", "Val1", "blabla"); + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + const TString query1(Q_(R"( + REPLACE INTO `/Root/TestTable` (Key, Value1, Value2) VALUES + ("Primary3", "Val1", "blabla"); + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable/Index1Uniq/indexImplTable"); + const TString expected = R"([[["Val1"];["Primary1"];["Val2"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable/Index2NotUniq/indexImplTable"); + const TString expected = R"([[["Val2"];["Primary1"];["Val1"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable"); + const TString expected = R"([[["Primary1"];["Val1"];["Val2"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + } + + { + const TString query1(Q_(R"( + UPDATE `/Root/TestTable` SET Value2 = "Val2_1" WHERE Key = "Primary1"; + )")); + + auto result = session.ExecuteDataQuery( + query1, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString()); + + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable/Index1Uniq/indexImplTable"); + const TString expected = R"([[["Val1"];["Primary1"];["Val2_1"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable/Index2NotUniq/indexImplTable"); + const TString expected = R"([[["Val2_1"];["Primary1"];["Val1"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + { + const auto& yson = ReadTablePartToYson(session, "/Root/TestTable"); + const TString expected = R"([[["Primary1"];["Val1"];["Val2_1"]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + } + } + + Y_UNIT_TEST(MultipleSecondaryIndexWithSameComulns) { auto setting = NKikimrKqp::TKqpSetting(); auto serverSettings = TKikimrSettings() @@ -3644,6 +3889,7 @@ R"([[#;#;["Primary1"];[41u]];[["Secondary2"];[2u];["Primary2"];[42u]];[["Seconda query1, TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) .ExtractValueSync(); + UNIT_ASSERT(result.IsSuccess()); } |