aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordcherednik <dcherednik@ydb.tech>2023-11-01 13:15:22 +0300
committerdcherednik <dcherednik@ydb.tech>2023-11-01 13:46:04 +0300
commit42a1fee25dda68794ca60302fec5673206fe106f (patch)
tree8a21abbe1f4db376187745e6ba654c359bad1485
parent9793ffdc8db2b9940f15deebe3b951504bac64be (diff)
downloadydb-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.cpp2
-rw-r--r--ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.cpp24
-rw-r--r--ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.h14
-rw-r--r--ydb/core/kqp/opt/physical/effects/kqp_opt_phy_upsert_index.cpp39
-rw-r--r--ydb/core/kqp/ut/idx_test/ydb_index_ut.cpp70
-rw-r--r--ydb/core/kqp/ut/indexes/kqp_indexes_multishard_ut.cpp22
-rw-r--r--ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp246
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());
}