Skip to content

Commit 5d8049c

Browse files
committed
datashard: more checks for NON NULL columns in TValidatedWriteTxOperation::ParseOperation
1 parent 07355d0 commit 5d8049c

File tree

2 files changed

+71
-13
lines changed

2 files changed

+71
-13
lines changed

ydb/core/tx/datashard/datashard_ut_write.cpp

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -557,20 +557,33 @@ Y_UNIT_TEST_SUITE(DataShardWrite) {
557557
Y_UNIT_TEST(WriteImmediateBadRequest) {
558558
auto [runtime, server, sender] = TestCreateServer();
559559

560-
auto opts = TShardedTableOptions().Columns({{"key", "Utf8", true, true}});
560+
auto opts = TShardedTableOptions().Columns({
561+
{"key", "Utf8", true, true},
562+
{"value1", "Uint32", false, false},
563+
{"value2", "Uint32", false, true},
564+
});
561565
auto [shards, tableId] = CreateShardedTable(server, sender, "/Root", "table-1", opts);
562566
const ui64 shard = shards[0];
563567

568+
auto prepareEvWrite = [&](
569+
NKikimrDataEvents::TEvWrite::TOperation::EOperationType opType,
570+
TSerializedCellMatrix matrix,
571+
const std::vector<ui32>& columnIds) {
572+
auto evWrite = std::make_unique<NKikimr::NEvents::TDataEvents::TEvWrite>(100, NKikimrDataEvents::TEvWrite::MODE_IMMEDIATE);
573+
ui64 payloadIndex = NKikimr::NEvWrite::TPayloadWriter<NKikimr::NEvents::TDataEvents::TEvWrite>(*evWrite).AddDataToPayload(matrix.ReleaseBuffer());
574+
evWrite->AddOperation(opType, tableId, columnIds, payloadIndex, NKikimrDataEvents::FORMAT_CELLVEC);
575+
return evWrite;
576+
};
577+
564578
Cout << "========= Send immediate write with huge key=========\n";
565579
{
566580
TString hugeStringValue(NLimits::MaxWriteKeySize + 1, 'X');
567-
TSerializedCellMatrix matrix({TCell(hugeStringValue.c_str(), hugeStringValue.size())}, 1, 1);
568-
569-
auto evWrite = std::make_unique<NKikimr::NEvents::TDataEvents::TEvWrite>(100, NKikimrDataEvents::TEvWrite::MODE_IMMEDIATE);
570-
ui64 payloadIndex = NKikimr::NEvWrite::TPayloadWriter<NKikimr::NEvents::TDataEvents::TEvWrite>(*evWrite).AddDataToPayload(matrix.ReleaseBuffer());
571-
evWrite->AddOperation(NKikimrDataEvents::TEvWrite::TOperation::OPERATION_UPSERT, tableId, {1}, payloadIndex, NKikimrDataEvents::FORMAT_CELLVEC);
581+
TSerializedCellMatrix matrix({TCell(hugeStringValue.c_str(), hugeStringValue.size()), TCell::Make(ui32(123))}, 1, 2);
572582

573-
const auto writeResult = Write(runtime, sender, shard, std::move(evWrite), NKikimrDataEvents::TEvWriteResult::STATUS_BAD_REQUEST);
583+
const auto writeResult = Write(
584+
runtime, sender, shard,
585+
prepareEvWrite(NKikimrDataEvents::TEvWrite::TOperation::OPERATION_UPSERT, std::move(matrix), {1, 3}),
586+
NKikimrDataEvents::TEvWriteResult::STATUS_BAD_REQUEST);
574587
UNIT_ASSERT_VALUES_EQUAL(writeResult.GetIssues().size(), 1);
575588
UNIT_ASSERT(writeResult.GetIssues(0).message().Contains("Row key size of 1049601 bytes is larger than the allowed threshold 1049600"));
576589
}
@@ -598,16 +611,40 @@ Y_UNIT_TEST_SUITE(DataShardWrite) {
598611

599612
Cout << "========= Send immediate write with NULL value for NOT NULL column=========\n";
600613
{
601-
TSerializedCellMatrix matrix({TCell{}}, 1, 1);
614+
TString stringValue("KEY");
615+
TSerializedCellMatrix matrix(
616+
{TCell(stringValue.data(), stringValue.size()), TCell()},
617+
1, 2);
602618

603-
auto evWrite = std::make_unique<NKikimr::NEvents::TDataEvents::TEvWrite>(100, NKikimrDataEvents::TEvWrite::MODE_IMMEDIATE);
604-
ui64 payloadIndex = NKikimr::NEvWrite::TPayloadWriter<NKikimr::NEvents::TDataEvents::TEvWrite>(*evWrite).AddDataToPayload(matrix.ReleaseBuffer());
605-
evWrite->AddOperation(NKikimrDataEvents::TEvWrite::TOperation::OPERATION_UPSERT, tableId, {1}, payloadIndex, NKikimrDataEvents::FORMAT_CELLVEC);
606-
607-
const auto writeResult = Write(runtime, sender, shard, std::move(evWrite), NKikimrDataEvents::TEvWriteResult::STATUS_BAD_REQUEST);
619+
const auto writeResult = Write(
620+
runtime, sender, shard,
621+
prepareEvWrite(NKikimrDataEvents::TEvWrite::TOperation::OPERATION_UPSERT, matrix, {1, 3}),
622+
NKikimrDataEvents::TEvWriteResult::STATUS_BAD_REQUEST);
608623
UNIT_ASSERT_VALUES_EQUAL(writeResult.GetIssues().size(), 1);
609624
UNIT_ASSERT(writeResult.GetIssues(0).message().Contains("NULL value for NON NULL column"));
610625
}
626+
627+
Cout << "========= Send immediate write without data for NOT NULL column=========\n";
628+
{
629+
TString stringValue("KEY");
630+
TSerializedCellMatrix matrix(
631+
{TCell(stringValue.data(), stringValue.size()), TCell::Make(ui32(123))},
632+
1, 2);
633+
634+
// Insert should fail.
635+
const auto insertResult = Write(
636+
runtime, sender, shard,
637+
prepareEvWrite(NKikimrDataEvents::TEvWrite::TOperation::OPERATION_INSERT, matrix, {1, 2}),
638+
NKikimrDataEvents::TEvWriteResult::STATUS_BAD_REQUEST);
639+
UNIT_ASSERT_VALUES_EQUAL(insertResult.GetIssues().size(), 1);
640+
UNIT_ASSERT(insertResult.GetIssues(0).message().Contains("Missing inserted values for NON NULL column"));
641+
642+
// Update should complete successfully.
643+
const auto updateResult = Write(
644+
runtime, sender, shard,
645+
prepareEvWrite(NKikimrDataEvents::TEvWrite::TOperation::OPERATION_UPDATE, matrix, {1, 2}),
646+
NKikimrDataEvents::TEvWriteResult::STATUS_COMPLETED);
647+
}
611648
}
612649

613650
Y_UNIT_TEST(WriteImmediateSeveralOperations) {

ydb/core/tx/datashard/datashard_write_operation.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,27 @@ std::tuple<NKikimrTxDataShard::TError::EKind, TString> TValidatedWriteTxOperatio
162162
}
163163
}
164164

165+
switch (OperationType) {
166+
case NKikimrDataEvents::TEvWrite::TOperation::OPERATION_UPSERT:
167+
case NKikimrDataEvents::TEvWrite::TOperation::OPERATION_DELETE:
168+
case NKikimrDataEvents::TEvWrite::TOperation::OPERATION_REPLACE:
169+
case NKikimrDataEvents::TEvWrite::TOperation::OPERATION_UPDATE:
170+
case NKikimrDataEvents::TEvWrite::TOperation::OPERATION_INCREMENT:
171+
case NKikimrDataEvents::TEvWrite::TOperation::OPERATION_UNSPECIFIED:
172+
break;
173+
case NKikimrDataEvents::TEvWrite::TOperation::OPERATION_INSERT: {
174+
// If we are inserting new rows, check that all NON NULL columns are present in data.
175+
// Note that UPSERT can also insert new rows, but we don't know if this actually happens
176+
// at this stage, so we skip the check for UPSERT.
177+
auto columnIdsSet = THashSet<ui32>(ColumnIds.begin(), ColumnIds.end());
178+
for (const auto& [id, column] : tableInfo.Columns) {
179+
if (column.NotNull && !columnIdsSet.contains(id)) {
180+
return {NKikimrTxDataShard::TError::BAD_ARGUMENT, TStringBuilder() << "Missing inserted values for NON NULL column " << id};
181+
}
182+
}
183+
}
184+
}
185+
165186
for (ui32 rowIdx = 0; rowIdx < Matrix.GetRowCount(); ++rowIdx)
166187
{
167188
ui64 keyBytes = 0;

0 commit comments

Comments
 (0)