aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniil Cherednik <dcherednik@ydb.tech>2024-01-17 17:34:45 +0100
committerGitHub <noreply@github.com>2024-01-17 17:34:45 +0100
commit2b89c7dae188c69cccac4defc51e710d37a3dba6 (patch)
tree66fbf6ac0ab5c4ca5200228624165d47bf6c9b54
parent39325bd60d9c436b7d613e8c186b9e71c83a901f (diff)
downloadydb-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>
-rw-r--r--ydb/core/kqp/opt/physical/effects/kqp_opt_phy_effects_impl.h3
-rw-r--r--ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert.cpp7
-rw-r--r--ydb/core/kqp/opt/physical/effects/kqp_opt_phy_insert_index.cpp12
-rw-r--r--ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.cpp56
-rw-r--r--ydb/core/kqp/opt/physical/effects/kqp_opt_phy_uniq_helper.h12
-rw-r--r--ydb/core/kqp/opt/physical/effects/kqp_opt_phy_upsert_index.cpp2
-rw-r--r--ydb/core/kqp/ut/indexes/kqp_indexes_multishard_ut.cpp48
-rw-r--r--ydb/core/kqp/ut/indexes/kqp_indexes_ut.cpp39
-rw-r--r--ydb/core/kqp/ut/pg/kqp_pg_ut.cpp133
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) {