Skip to content

Commit 10951bf

Browse files
authored
datashard: add NULL value check to TValidatedWriteTxOperation::ParseOperation (#21560)
1 parent 40cb259 commit 10951bf

File tree

2 files changed

+80
-8
lines changed

2 files changed

+80
-8
lines changed

ydb/core/tx/datashard/datashard_ut_write.cpp

Lines changed: 57 additions & 7 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, false}});
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);
581+
TSerializedCellMatrix matrix({TCell(hugeStringValue.c_str(), hugeStringValue.size()), TCell::Make(ui32(123))}, 1, 2);
568582

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);
572-
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
}
@@ -595,6 +608,43 @@ Y_UNIT_TEST_SUITE(DataShardWrite) {
595608
UNIT_ASSERT_VALUES_EQUAL(writeResult.GetIssues().size(), 1);
596609
UNIT_ASSERT(writeResult.GetIssues(0).message().Contains("OPERATION_UNSPECIFIED operation is not supported now"));
597610
}
611+
612+
Cout << "========= Send immediate write with NULL value for NOT NULL column=========\n";
613+
{
614+
TString stringValue("KEY");
615+
TSerializedCellMatrix matrix(
616+
{TCell(stringValue.data(), stringValue.size()), TCell()},
617+
1, 2);
618+
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);
623+
UNIT_ASSERT_VALUES_EQUAL(writeResult.GetIssues().size(), 1);
624+
UNIT_ASSERT(writeResult.GetIssues(0).message().Contains("NULL value for NON NULL column"));
625+
}
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+
}
598648
}
599649

600650
Y_UNIT_TEST(WriteImmediateSeveralOperations) {

ydb/core/tx/datashard/datashard_write_operation.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,10 +146,32 @@ std::tuple<NKikimrTxDataShard::TError::EKind, TString> TValidatedWriteTxOperatio
146146
return {NKikimrTxDataShard::TError::SCHEME_ERROR, TStringBuilder() << "Key column schema at position " << i};
147147
}
148148

149-
for (ui32 columnTag : ColumnIds) {
149+
for (ui16 colIdx = 0; colIdx < ColumnIds.size(); ++colIdx) {
150+
ui32 columnTag = ColumnIds[colIdx];
150151
auto* col = tableInfo.Columns.FindPtr(columnTag);
151152
if (!col)
152153
return {NKikimrTxDataShard::TError::SCHEME_ERROR, TStringBuilder() << "Missing column with id " << columnTag};
154+
155+
if (col->NotNull) {
156+
for (ui32 rowIdx = 0; rowIdx < Matrix.GetRowCount(); ++rowIdx) {
157+
const TCell& cell = Matrix.GetCell(rowIdx, colIdx);
158+
if (cell.IsNull()) {
159+
return {NKikimrTxDataShard::TError::BAD_ARGUMENT, TStringBuilder() << "NULL value for NON NULL column " << columnTag};
160+
}
161+
}
162+
}
163+
}
164+
165+
if (OperationType == NKikimrDataEvents::TEvWrite::TOperation::OPERATION_INSERT) {
166+
// If we are inserting new rows, check that all NON NULL columns are present in data.
167+
// Note that UPSERT can also insert new rows, but we don't know if this actually happens
168+
// at this stage, so we skip the check for UPSERT.
169+
auto columnIdsSet = THashSet<ui32>(ColumnIds.begin(), ColumnIds.end());
170+
for (const auto& [id, column] : tableInfo.Columns) {
171+
if (column.NotNull && !columnIdsSet.contains(id)) {
172+
return {NKikimrTxDataShard::TError::BAD_ARGUMENT, TStringBuilder() << "Missing inserted values for NON NULL column " << id};
173+
}
174+
}
153175
}
154176

155177
for (ui32 rowIdx = 0; rowIdx < Matrix.GetRowCount(); ++rowIdx)

0 commit comments

Comments
 (0)