diff options
author | Daniil Cherednik <dcherednik@ydb.tech> | 2024-01-17 17:34:45 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-01-17 17:34:45 +0100 |
commit | 2b89c7dae188c69cccac4defc51e710d37a3dba6 (patch) | |
tree | 66fbf6ac0ab5c4ca5200228624165d47bf6c9b54 | |
parent | 39325bd60d9c436b7d613e8c186b9e71c83a901f (diff) | |
download | ydb-2b89c7dae188c69cccac4defc51e710d37a3dba6.tar.gz |
Allow partial column set in case of insert (#1056)
* Allow partial column set in case of insert
into table with unique constraint.
Co-authored-by: qrort <31865255+qrort@users.noreply.github.com>
9 files changed, 275 insertions, 37 deletions
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 3765e156ec8..bbb42ef5023 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 @@ -69,7 +69,8 @@ NYql::NNodes::TExprBase MakeRowsFromTupleDict(const NYql::NNodes::TDqPhyPrecompu const THashSet<TStringBuf>& columns, NYql::TPositionHandle pos, NYql::TExprContext& ctx); NYql::NNodes::TMaybeNode<NYql::NNodes::TDqCnUnionAll> MakeConditionalInsertRows(const NYql::NNodes::TExprBase& input, - const NYql::TKikimrTableDescription& table, bool abortOnError, NYql::TPositionHandle pos, NYql::TExprContext& ctx); + const NYql::TKikimrTableDescription& table, const TMaybe<THashSet<TStringBuf>>& inputColumn, bool abortOnError, + NYql::TPositionHandle pos, NYql::TExprContext& ctx); enum class TKqpPhyUpsertIndexMode { Upsert, diff --git a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert.cpp b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert.cpp index 8075aafc877..35f106d6b37 100644 --- a/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert.cpp +++ b/ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert.cpp @@ -9,14 +9,14 @@ using namespace NYql::NDq; using namespace NYql::NNodes; TMaybeNode<TDqCnUnionAll> MakeConditionalInsertRows(const TExprBase& input, const TKikimrTableDescription& table, - bool abortOnError, TPositionHandle pos, TExprContext& ctx) + const TMaybe<THashSet<TStringBuf>>& inputColumns, bool abortOnError, TPositionHandle pos, TExprContext& ctx) { auto condenseResult = CondenseInput(input, ctx); if (!condenseResult) { return {}; } - auto helper = CreateInsertUniqBuildHelper(table, pos, ctx); + auto helper = CreateInsertUniqBuildHelper(table, inputColumns, pos, ctx); auto computeKeysStage = helper->CreateComputeKeysStage(condenseResult.GetRef(), pos, ctx); auto inputPrecompute = helper->CreateInputPrecompute(computeKeysStage, pos, ctx); @@ -127,7 +127,8 @@ TExprBase KqpBuildInsertStages(TExprBase node, TExprContext& ctx, const TKqpOpti bool abortOnError = insert.OnConflict().Value() == "abort"sv; const auto& table = kqpCtx.Tables->ExistingTable(kqpCtx.Cluster, insert.Table().Path()); - auto insertRows = MakeConditionalInsertRows(insert.Input(), table, abortOnError, insert.Pos(), ctx); + const static TMaybe<THashSet<TStringBuf>> empty; + auto insertRows = MakeConditionalInsertRows(insert.Input(), table, empty, abortOnError, insert.Pos(), ctx); if (!insertRows) { return node; } 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 8c857962c91..6553dc98514 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 @@ -85,7 +85,12 @@ TExprBase KqpBuildInsertIndexStages(TExprBase node, TExprContext& ctx, const TKq bool abortOnError = insert.OnConflict().Value() == "abort"sv; const auto& table = kqpCtx.Tables->ExistingTable(kqpCtx.Cluster, insert.Table().Path()); - auto insertRows = MakeConditionalInsertRows(insert.Input(), table, abortOnError, insert.Pos(), ctx); + THashSet<TStringBuf> inputColumnsSet; + for (const auto& column : insert.Columns()) { + inputColumnsSet.emplace(column.Value()); + } + + auto insertRows = MakeConditionalInsertRows(insert.Input(), table, inputColumnsSet, abortOnError, insert.Pos(), ctx); if (!insertRows) { return node; } @@ -94,11 +99,6 @@ TExprBase KqpBuildInsertIndexStages(TExprBase node, TExprContext& ctx, const TKq .Connection(insertRows.Cast()) .Done(); - THashSet<TStringBuf> inputColumnsSet; - for (const auto& column : insert.Columns()) { - inputColumnsSet.emplace(column.Value()); - } - auto indexes = BuildSecondaryIndexVector(table, insert.Pos(), ctx); YQL_ENSURE(indexes); 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 31dcdbd928d..5ccf8ae7d30 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 @@ -39,9 +39,9 @@ NYql::TExprNode::TPtr MakeUniqCheckDict(const TCoLambda& selector, class TInsertUniqBuildHelper : public TUniqBuildHelper { public: - TInsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, - NYql::TExprContext& ctx) - : TUniqBuildHelper(table, nullptr, nullptr, pos, ctx, false) + TInsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, const TMaybe<THashSet<TStringBuf>>& inputColumns, + NYql::TPositionHandle pos, NYql::TExprContext& ctx) + : TUniqBuildHelper(table, inputColumns, nullptr, pos, ctx, true) {} private: @@ -119,9 +119,9 @@ private: class TUpsertUniqBuildHelper : public TUniqBuildHelper { public: - TUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, const THashSet<TStringBuf>* inputColumns, const THashSet<TString>& usedIndexes, + TUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, const TMaybe<THashSet<TStringBuf>>& inputColumns, const THashSet<TString>& usedIndexes, NYql::TPositionHandle pos, NYql::TExprContext& ctx) - : TUniqBuildHelper(table, inputColumns, &usedIndexes, pos, ctx, true) + : TUniqBuildHelper(table, inputColumns, &usedIndexes, pos, ctx, false) , PkDict(MakeUniqCheckDict(MakeTableKeySelector(table.Metadata, pos, ctx), RowsListArg, pos, ctx)) {} @@ -243,12 +243,12 @@ private: } TVector<TUniqBuildHelper::TUniqCheckNodes> TUniqBuildHelper::Prepare(const TCoArgument& rowsListArg, - const TKikimrTableDescription& table, const THashSet<TStringBuf>* inputColumns, - const THashSet<TString>* usedIndexes, TPositionHandle pos, TExprContext& ctx, bool skipPkCheck) + const TKikimrTableDescription& table, const TMaybe<THashSet<TStringBuf>>& inputColumns, + const THashSet<TString>* usedIndexes, TPositionHandle pos, TExprContext& ctx, bool insertMode) { TVector<TUniqCheckNodes> checks; - if (!skipPkCheck) { + if (insertMode) { checks.emplace_back(MakeUniqCheckNodes(MakeTableKeySelector(table.Metadata, pos, ctx), rowsListArg, pos, ctx)); } @@ -262,19 +262,34 @@ TVector<TUniqBuildHelper::TUniqCheckNodes> TUniqBuildHelper::Prepare(const TCoAr continue; // Compatibility with PG semantic - allow multiple null in columns with unique constaint + // NOTE: to change this it's important to consider insert and replace in case of partial column set TVector<TCoAtom> skipNullColumns; skipNullColumns.reserve(table.Metadata->Indexes[i].KeyColumns.size()); bool used = false; + bool skip = false; + + YQL_ENSURE(inputColumns, "Attempt to check uniq constraint without given columns"); + for (const auto& column : table.Metadata->Indexes[i].KeyColumns) { - used |= (!inputColumns || inputColumns->contains(column)); + if (inputColumns->contains(column)) { + // Skip check if no input for index update + used = true; + } else if (insertMode) { + // In case of insert, 'column' will contain NULL for the new PK (or query will fail in case of NOT NULL) + // NULL != NULL and NULL != "any other value" so we can just skip uniq check. + skip = true; + continue; + } TCoAtom atom(ctx.NewAtom(pos, column)); skipNullColumns.emplace_back(atom); } - // Just to doublecheck we are not trying to update index without data to update - YQL_ENSURE(used, "Index is used but not input columns for update. Probably it's a bug." - " Index: " << table.Metadata->Indexes[i].Name); + if (!used) + continue; + + if (skip) + continue; auto skipNull = Build<TCoSkipNullMembers>(ctx, pos) .Input(rowsListArg) @@ -292,7 +307,7 @@ TVector<TUniqBuildHelper::TUniqCheckNodes> TUniqBuildHelper::Prepare(const TCoAr return checks; } -static TExprNode::TPtr CreateRowsToPass(const TCoArgument& rowsListArg, const THashSet<TStringBuf>* inputColumns, +static TExprNode::TPtr CreateRowsToPass(const TCoArgument& rowsListArg, const TMaybe<THashSet<TStringBuf>>& inputColumns, TPositionHandle pos, TExprContext& ctx) { if (!inputColumns) { @@ -326,11 +341,11 @@ static TExprNode::TPtr CreateRowsToPass(const TCoArgument& rowsListArg, const TH .Done().Ptr(); } -TUniqBuildHelper::TUniqBuildHelper(const TKikimrTableDescription& table, const THashSet<TStringBuf>* inputColumns, const THashSet<TString>* usedIndexes, - TPositionHandle pos, TExprContext& ctx, bool skipPkCheck) +TUniqBuildHelper::TUniqBuildHelper(const TKikimrTableDescription& table, const TMaybe<THashSet<TStringBuf>>& inputColumns, const THashSet<TString>* usedIndexes, + TPositionHandle pos, TExprContext& ctx, bool insertMode) : RowsListArg(ctx.NewArgument(pos, "rows_list")) , False(MakeBool(pos, false, ctx)) - , Checks(Prepare(RowsListArg, table, inputColumns, usedIndexes, pos, ctx, skipPkCheck)) + , Checks(Prepare(RowsListArg, table, inputColumns, usedIndexes, pos, ctx, insertMode)) , RowsToPass(CreateRowsToPass(RowsListArg, inputColumns, pos, ctx)) {} @@ -548,14 +563,15 @@ TDqStage TUniqBuildHelper::CreateLookupExistStage(const TDqStage& computeKeysSta namespace NKikimr::NKqp::NOpt { -TUniqBuildHelper::TPtr CreateInsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, NYql::TPositionHandle pos, - NYql::TExprContext& ctx) +TUniqBuildHelper::TPtr CreateInsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, + const TMaybe<THashSet<TStringBuf>>& inputColumns, NYql::TPositionHandle pos, + NYql::TExprContext& ctx) { - return std::make_unique<TInsertUniqBuildHelper>(table, pos, ctx); + return std::make_unique<TInsertUniqBuildHelper>(table, inputColumns, pos, ctx); } TUniqBuildHelper::TPtr CreateUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, - const THashSet<TStringBuf>* inputColumns, + const TMaybe<THashSet<TStringBuf>>& inputColumns, const THashSet<TString>& usedIndexes, NYql::TPositionHandle pos, NYql::TExprContext& ctx) { return std::make_unique<TUpsertUniqBuildHelper>(table, inputColumns, 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 285dfca5766..bd6a79efb4f 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 @@ -27,9 +27,8 @@ public: virtual ~TUniqBuildHelper() = default; 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, const THashSet<TStringBuf>* inputColumns, const THashSet<TString>* usedIndexes, NYql::TPositionHandle pos, - NYql::TExprContext& ctx, bool skipPkCheck); + TUniqBuildHelper(const NYql::TKikimrTableDescription& table, const TMaybe<THashSet<TStringBuf>>& inputColumns, + const THashSet<TString>* usedIndexes, NYql::TPositionHandle pos, NYql::TExprContext& ctx, bool insertMode); size_t CalcComputeKeysStageOutputNum() const; struct TUniqCheckNodes { @@ -63,7 +62,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, const THashSet<TStringBuf>* inputColumns, const THashSet<TString>* usedIndexes, NYql::TPositionHandle pos, + const NYql::TKikimrTableDescription& table, const TMaybe<THashSet<TStringBuf>>& inputColumns, + const THashSet<TString>* usedIndexes, NYql::TPositionHandle pos, NYql::TExprContext& ctx, bool skipPkCheck); virtual NYql::NNodes::TDqCnUnionAll CreateLookupStageWithConnection(const NYql::NNodes::TDqStage& computeKeysStage, @@ -80,9 +80,9 @@ private: }; TUniqBuildHelper::TPtr CreateInsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, - NYql::TPositionHandle pos, NYql::TExprContext& ctx); + const TMaybe<THashSet<TStringBuf>>& inputColumns, NYql::TPositionHandle pos, NYql::TExprContext& ctx); TUniqBuildHelper::TPtr CreateUpsertUniqBuildHelper(const NYql::TKikimrTableDescription& table, - const THashSet<TStringBuf>* inputColumns, + const TMaybe<THashSet<TStringBuf>>& inputColumns, 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 c868d065cef..e76e4f2e33c 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 @@ -510,7 +510,7 @@ TMaybe<TCondenseInputResult> RewriteInputForConstraint(const TExprBase& inputRow YQL_ENSURE(condenseResult); } - auto helper = CreateUpsertUniqBuildHelper(table, &inputColumns, usedIndexes, pos, ctx); + auto helper = CreateUpsertUniqBuildHelper(table, inputColumns, usedIndexes, pos, ctx); if (helper->GetChecksNum() == 0) { return condenseResult; } 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 4367907ca95..8d9d89c977c 100644 --- a/ydb/core/kqp/ut/indexes/kqp_indexes_multishard_ut.cpp +++ b/ydb/core/kqp/ut/indexes/kqp_indexes_multishard_ut.cpp @@ -190,6 +190,54 @@ Y_UNIT_TEST_SUITE(KqpUniqueIndex) { } } + Y_UNIT_TEST(InsertFkPartialColumnSet) { + TKikimrRunner kikimr(SyntaxV1Settings()); + CreateTableWithMultishardIndex(kikimr.GetTestClient(), IG_UNIQUE); + auto db = kikimr.GetTableClient(); + auto session = db.CreateSession().GetValueSync().GetSession(); + FillTable(session); + + { + const TString query(Q_(R"( + INSERT INTO `/Root/MultiShardIndexed` (key, value) VALUES + (1173915, "v1"); + )")); + + 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"([[#;[1173915u]];[[1000000000u];[1u]];[[2000000000u];[2u]];[[3000000000u];[3u]];[[4294967295u];[4u]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + } + + Y_UNIT_TEST(ReplaceFkPartialColumnSet) { + TKikimrRunner kikimr(SyntaxV1Settings()); + CreateTableWithMultishardIndex(kikimr.GetTestClient(), IG_UNIQUE); + auto db = kikimr.GetTableClient(); + auto session = db.CreateSession().GetValueSync().GetSession(); + FillTable(session); + + { + const TString query(Q_(R"( + REPLACE INTO `/Root/MultiShardIndexed` (key, value) VALUES + (1173915, "v1"); + )")); + + 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"([[#;[1173915u]];[[1000000000u];[1u]];[[2000000000u];[2u]];[[3000000000u];[3u]];[[4294967295u];[4u]]])"; + UNIT_ASSERT_VALUES_EQUAL(yson, expected); + } + } + Y_UNIT_TEST(ReplaceFkAlreadyExist) { 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 a7feb778cf0..89d94c0adc8 100644 --- a/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp +++ b/ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp @@ -737,6 +737,45 @@ Y_UNIT_TEST_SUITE(KqpIndexes) { UNIT_ASSERT_VALUES_EQUAL(yson, expected); } + { + // Insert - do nothing + const TString query2 = Q1_(R"( + INSERT INTO `/Root/TestTable` (k1, k2, fk1) VALUES + ("p1str5", "p2str3", "fk1_str"); + )"); + + auto result = session.ExecuteDataQuery( + query2, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + const TString query2 = Q1_(R"( + UPDATE `/Root/TestTable` ON (k1, k2, fk1) VALUES + ("p1str5", "p2str3", "fk1_str"); + )"); + + auto result = session.ExecuteDataQuery( + query2, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + const TString query2 = Q1_(R"( + UPDATE `/Root/TestTable` SET fk1 = "fk1_str" + WHERE k1 = "p1str5" AND k2 = "p2str3"; + )"); + + auto result = session.ExecuteDataQuery( + query2, + TTxControl::BeginTx(TTxSettings::SerializableRW()).CommitTx()) + .ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), NYdb::EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } } Y_UNIT_TEST(UpsertMultipleUniqIndexes) { diff --git a/ydb/core/kqp/ut/pg/kqp_pg_ut.cpp b/ydb/core/kqp/ut/pg/kqp_pg_ut.cpp index 0beef716ccd..5f32e98be20 100644 --- a/ydb/core/kqp/ut/pg/kqp_pg_ut.cpp +++ b/ydb/core/kqp/ut/pg/kqp_pg_ut.cpp @@ -1851,12 +1851,145 @@ Y_UNIT_TEST_SUITE(KqpPg) { { const auto query = Q_(R"( --!syntax_pg + INSERT INTO Pg (key, value) VALUES (120, 120), (121, 121); + )"); + + auto result = session.ExecuteDataQuery(query, TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + + { + const auto query = Q_(R"( + --!syntax_pg + UPDATE Pg SET value = 120 WHERE key = 121; + )"); + + auto result = session.ExecuteDataQuery(query, TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + auto result = session.ExecuteDataQuery(R"( + --!syntax_pg + SELECT * FROM Pg; + )", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([["120";"120"];["121";"121"]])", FormatResultSetYson(result.GetResultSet(0))); + } + + { + const auto query = Q_(R"( + --!syntax_pg INSERT INTO Pg1 (key, value) VALUES (120, 120), (121, 120); )"); auto result = session.ExecuteDataQuery(query, TTxControl::BeginTx().CommitTx()).ExtractValueSync(); UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); } + + { + auto result = session.ExecuteDataQuery(R"( + --!syntax_pg + SELECT * FROM Pg1; + )", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([["120";"120"];["121";"120"]])", FormatResultSetYson(result.GetResultSet(0))); + } + + { + const auto query = Q_(R"( + --!syntax_pg + UPDATE Pg1 SET value = 121 WHERE key = 121; + )"); + + auto result = session.ExecuteDataQuery(query, TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + + { + auto result = session.ExecuteDataQuery(R"( + --!syntax_pg + SELECT * FROM Pg1; + )", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([["120";"120"];["121";"121"]])", FormatResultSetYson(result.GetResultSet(0))); + } + } + + Y_UNIT_TEST(CreateUniqComplexPgColumn) { + TKikimrRunner kikimr(NKqp::TKikimrSettings().SetWithSampleTables(false)); + auto client = kikimr.GetTableClient(); + auto session = client.CreateSession().GetValueSync().GetSession(); + { + const auto query = Q_(R"( + --!syntax_pg + CREATE TABLE Pg ( + key int4 PRIMARY KEY, + value1 int4, + value2 int4, + UNIQUE(value1, value2) + ))"); + + auto result = session.ExecuteSchemeQuery(query).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + + { + const auto query = Q_(R"( + --!syntax_pg + INSERT INTO Pg (key, value1) VALUES (120, 120), (121, 120); + )"); + + auto result = session.ExecuteDataQuery(query, TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + + { + auto result = session.ExecuteDataQuery(R"( + --!syntax_pg + SELECT * FROM Pg; + )", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([["120";"120";#];["121";"120";#]])", FormatResultSetYson(result.GetResultSet(0))); + } + + { + const auto query = Q_(R"( + --!syntax_pg + UPDATE Pg SET value2 = 111 WHERE key = 120; + )"); + + auto result = session.ExecuteDataQuery(query, TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + } + + { + auto result = session.ExecuteDataQuery(R"( + --!syntax_pg + SELECT * FROM Pg; + )", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([["120";"120";"111"];["121";"120";#]])", FormatResultSetYson(result.GetResultSet(0))); + } + + { + const auto query = Q_(R"( + --!syntax_pg + UPDATE Pg SET value2 = 111 WHERE key = 121; + )"); + + auto result = session.ExecuteDataQuery(query, TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::PRECONDITION_FAILED, result.GetIssues().ToString()); + } + + { + auto result = session.ExecuteDataQuery(R"( + --!syntax_pg + SELECT * FROM Pg; + )", TTxControl::BeginTx().CommitTx()).ExtractValueSync(); + UNIT_ASSERT_VALUES_EQUAL_C(result.GetStatus(), EStatus::SUCCESS, result.GetIssues().ToString()); + CompareYson(R"([["120";"120";"111"];["121";"120";#]])", FormatResultSetYson(result.GetResultSet(0))); + } } Y_UNIT_TEST(CreateNotNullPgColumn) { |