Skip to content

Commit ae1371f

Browse files
authored
Merge 6e1422e into 96abd52
2 parents 96abd52 + 6e1422e commit ae1371f

File tree

2 files changed

+70
-25
lines changed

2 files changed

+70
-25
lines changed

ydb/core/kqp/ut/olap/kqp_olap_ut.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3074,6 +3074,54 @@ Y_UNIT_TEST_SUITE(KqpOlap) {
30743074
Y_UNIT_TEST_TWIN(DeleteAbsentMultipleShards, Reboot) {
30753075
TestDeleteAbsent(2, Reboot);
30763076
}
3077+
3078+
Y_UNIT_TEST(InsertIntoNullablePK) {
3079+
NKikimrConfig::TAppConfig appConfig;
3080+
appConfig.MutableColumnShardConfig()->SetAllowNullableColumnsInPK(true);
3081+
auto settings = TKikimrSettings().SetAppConfig(appConfig).SetWithSampleTables(false);
3082+
TTestHelper testHelper(settings);
3083+
3084+
TVector<TTestHelper::TColumnSchema> schema = {
3085+
TTestHelper::TColumnSchema().SetName("pk1").SetType(NScheme::NTypeIds::Int64).SetNullable(true),
3086+
TTestHelper::TColumnSchema().SetName("pk2").SetType(NScheme::NTypeIds::Int32).SetNullable(true),
3087+
TTestHelper::TColumnSchema().SetName("value").SetType(NScheme::NTypeIds::String).SetNullable(true),
3088+
};
3089+
TTestHelper::TColumnTable testTable;
3090+
testTable.SetName("/Root/ttt").SetPrimaryKey({ "pk1", "pk2" }).SetSharding({ "pk1" }).SetSchema(schema);
3091+
testHelper.CreateTable(testTable);
3092+
auto client = testHelper.GetKikimr().GetQueryClient();
3093+
const auto result = client
3094+
.ExecuteQuery(
3095+
R"(
3096+
INSERT INTO `/Root/ttt` (pk1, pk2, value) VALUES
3097+
(1, 2, "value"),
3098+
(null, 2, "value"),
3099+
(1, null, "value"),
3100+
(null, null, "value")
3101+
)",
3102+
NYdb::NQuery::TTxControl::BeginTx().CommitTx())
3103+
.GetValueSync();
3104+
UNIT_ASSERT_C(result.IsSuccess(), result.GetIssues().ToString());
3105+
{
3106+
const auto resultSelect = client
3107+
.ExecuteQuery(
3108+
"SELECT * FROM `/Root/ttt` ORDER BY pk1, pk2",
3109+
NYdb::NQuery::TTxControl::BeginTx().CommitTx())
3110+
.GetValueSync();
3111+
UNIT_ASSERT_C(resultSelect.IsSuccess(), resultSelect.GetIssues().ToString());
3112+
const auto resultSets = resultSelect.GetResultSets();
3113+
UNIT_ASSERT_VALUES_EQUAL(resultSets.size(), 1);
3114+
const auto resultSet = resultSets[0];
3115+
CompareYson(R"(
3116+
[
3117+
[#;#;["value"]];
3118+
[#;[2];["value"]];
3119+
[[1];#;["value"]];
3120+
[[1];[2];["value"]]
3121+
]
3122+
)", FormatResultSetYson(resultSet));
3123+
}
3124+
}
30773125
}
30783126

30793127
}

ydb/core/tx/columnshard/engines/scheme/versions/abstract_scheme.cpp

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <ydb/core/tx/columnshard/splitter/batch_slice.h>
1111

1212
#include <ydb/library/formats/arrow/simple_arrays_cache.h>
13+
#include <ydb/core/base/appdata_fwd.h>
14+
#include <ydb/core/protos/config.pb.h>
1315

1416
#include <util/string/join.h>
1517

@@ -92,36 +94,31 @@ TConclusion<std::shared_ptr<arrow::RecordBatch>> ISnapshotSchema::PrepareForModi
9294
if (targetIdx == -1) {
9395
return TConclusionStatus::Success();
9496
}
97+
const auto hasNull = NArrow::HasNulls(incomingBatch->column(incomingIdx));
9598
const std::optional<i32> pkFieldIdx = GetIndexInfo().GetPKColumnIndexByIndexVerified(targetIdx);
96-
if (!NArrow::HasNulls(incomingBatch->column(incomingIdx))) {
97-
if (pkFieldIdx) {
98-
AFL_VERIFY(*pkFieldIdx < (i32)pkColumns.size());
99-
AFL_VERIFY(!pkColumns[*pkFieldIdx]);
100-
pkColumns[*pkFieldIdx] = incomingBatch->column(incomingIdx);
101-
++pkColumnsCount;
102-
}
103-
return TConclusionStatus::Success();
104-
}
105-
if (pkFieldIdx) {
99+
if (pkFieldIdx && hasNull && !AppData()->ColumnShardConfig.GetAllowNullableColumnsInPK()) {
106100
return TConclusionStatus::Fail("null data for pk column is impossible for '" + dstSchema.field(targetIdx)->name() + "'");
107101
}
108-
switch (mType) {
109-
case NEvWrite::EModificationType::Replace:
110-
case NEvWrite::EModificationType::Insert:
111-
case NEvWrite::EModificationType::Upsert: {
112-
if (GetIndexInfo().IsNullableVerifiedByIndex(targetIdx)) {
113-
return TConclusionStatus::Success();
114-
}
115-
if (GetIndexInfo().GetColumnExternalDefaultValueByIndexVerified(targetIdx)) {
116-
return TConclusionStatus::Success();
117-
} else {
118-
return TConclusionStatus::Fail("empty field for non-default column: '" + dstSchema.field(targetIdx)->name() + "'");
119-
}
102+
if (hasNull) {
103+
switch (mType) {
104+
case NEvWrite::EModificationType::Replace:
105+
case NEvWrite::EModificationType::Insert:
106+
case NEvWrite::EModificationType::Upsert: {
107+
if (!GetIndexInfo().IsNullableVerifiedByIndex(targetIdx) && !GetIndexInfo().GetColumnExternalDefaultValueByIndexVerified(targetIdx))
108+
return TConclusionStatus::Fail("empty field for non-default column: '" + dstSchema.field(targetIdx)->name() + "'");
109+
}
110+
case NEvWrite::EModificationType::Delete:
111+
case NEvWrite::EModificationType::Update:
112+
break;
120113
}
121-
case NEvWrite::EModificationType::Delete:
122-
case NEvWrite::EModificationType::Update:
123-
return TConclusionStatus::Success();
124114
}
115+
if (pkFieldIdx) {
116+
AFL_VERIFY(*pkFieldIdx < (i32)pkColumns.size());
117+
AFL_VERIFY(!pkColumns[*pkFieldIdx]);
118+
pkColumns[*pkFieldIdx] = incomingBatch->column(incomingIdx);
119+
++pkColumnsCount;
120+
}
121+
return TConclusionStatus::Success();
125122
};
126123
const auto nameResolver = [&](const std::string& fieldName) -> i32 {
127124
return GetIndexInfo().GetColumnIndexOptional(fieldName).value_or(-1);

0 commit comments

Comments
 (0)