This is an automated email from the ASF dual-hosted git repository.
gavinchou pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/doris.git
The following commit(s) were added to refs/heads/master by this push:
new 9512c3c43ac [fix](binlog) wrap Row Binlog AFTER columns as nullable
before conversion (#66553)
9512c3c43ac is described below
commit 9512c3c43acfb66da6b53709158962d95a099887
Author: Luwei <[email protected]>
AuthorDate: Mon Aug 10 11:40:01 2026 +0800
[fix](binlog) wrap Row Binlog AFTER columns as nullable before conversion
(#66553)
Issue Number: close #65951
Related PR: #65810
Problem Summary: Row Binlog makes normal non-key AFTER columns nullable
even when their source columns are NOT NULL. `OlapBlockDataConvertor`
derives the null map from the runtime source column, so a non-nullable
complex source provides no top-level null map. Nullable ARRAY, MAP, and
STRUCT writers can then leave their physical null child empty and abort
during segment finalization.
This PR keeps the fix local to Row Binlog:
- Wrap normal non-key AFTER source columns as `ColumnNullable<original>`
with an all-zero null map before conversion.
- Keep key columns unchanged, preserve original source columns in
`full_block`, and do not mutate the shared input Block.
- For partial-update missing AFTER columns, wrap each column filled from
the historical Rowset before conversion and append.
- Restore the original generic `ColumnWriter::append()` behavior and
remove the implicit null-map state, avoiding behavior changes in Scalar,
VARIANT, and other common write paths.
The test covers full updates, explicit partial updates, and partial
updates whose NOT NULL MAP/STRUCT values are filled from a historical
Rowset. It verifies logical readback and the physical top-level
null-child row count.
### Release note
Fix a BE crash when Row Binlog writes NOT NULL complex columns into
nullable AFTER columns.
---
.../storage/segment/row_binlog_segment_writer.cpp | 36 ++-
be/src/storage/segment/row_binlog_segment_writer.h | 2 +
.../storage/rowset/segment_flusher_format_test.cpp | 324 ++++++++++++++++++++-
3 files changed, 349 insertions(+), 13 deletions(-)
diff --git a/be/src/storage/segment/row_binlog_segment_writer.cpp
b/be/src/storage/segment/row_binlog_segment_writer.cpp
index fbfcb54d8b6..807330b9496 100644
--- a/be/src/storage/segment/row_binlog_segment_writer.cpp
+++ b/be/src/storage/segment/row_binlog_segment_writer.cpp
@@ -24,6 +24,7 @@
#include "core/block/column_with_type_and_name.h"
#include "core/column/column_nullable.h"
#include "core/column/column_vector.h"
+#include "core/data_type/data_type_nullable.h"
#include "storage/binlog.h"
#include "storage/iterator/olap_data_convertor.h"
#include "storage/olap_utils.h"
@@ -32,6 +33,17 @@
namespace doris {
namespace segment_v2 {
+namespace {
+
+ColumnWithTypeAndName make_nullable_row_binlog_value_column(const
ColumnWithTypeAndName& source) {
+ auto result = source;
+ result.column = make_nullable(source.column);
+ result.type = make_nullable(source.type);
+ return result;
+}
+
+} // namespace
+
RowBinlogSourceDataWriter::RowBinlogSourceDataWriter(const
SegmentWriteBinlogOptions& opt)
: _opt(opt) {}
@@ -208,9 +220,9 @@ Status RowBinlogSegmentWriter::append_block(const Block*
block, size_t row_pos,
std::vector<uint32_t> row_binlog_missing_column_ids;
row_binlog_missing_column_ids.reserve(
_binlog_opts.source.partial_update_info->missing_cids.size());
- // Missing cids are source cids too. Only normal columns have AFTER
writers.
+ // Missing cids are source cids. Only Row Binlog value columns have
AFTER writers.
for (uint32_t cid :
_binlog_opts.source.partial_update_info->missing_cids) {
- if (_source_data_writer->is_normal_column(cid)) {
+ if (_source_data_writer->is_row_binlog_value_column(cid)) {
row_binlog_missing_column_ids.emplace_back(cid);
}
}
@@ -220,9 +232,11 @@ Status RowBinlogSegmentWriter::append_block(const Block*
block, size_t row_pos,
// write AFTER missing columns from full_block to segment
auto& after_convertor = _source_data_writer->olap_data_convertor();
-
RETURN_IF_ERROR(after_convertor->set_source_content_with_specifid_columns(
- &full_block, row_pos, num_rows,
row_binlog_missing_column_ids));
for (auto cid : row_binlog_missing_column_ids) {
+ const auto nullable_source_column_for_after =
+
make_nullable_row_binlog_value_column(full_block.get_by_position(cid));
+
RETURN_IF_ERROR(after_convertor->set_source_content_with_specifid_column(
+ nullable_source_column_for_after, row_pos, num_rows, cid));
auto converted_cid =
_normal_col_start_id +
_source_data_writer->normal_column_ordinal(cid);
auto converted_result = after_convertor->convert_column_data(cid);
@@ -449,6 +463,12 @@ bool RowBinlogSourceDataWriter::is_normal_column(uint32_t
source_cid) const {
_normal_column_ids.end();
}
+bool RowBinlogSourceDataWriter::is_row_binlog_value_column(uint32_t
source_cid) const {
+ DCHECK(_opt.source.tablet_schema != nullptr);
+ DCHECK_LT(source_cid, _opt.source.tablet_schema->num_columns());
+ return is_normal_column(source_cid) &&
!_opt.source.tablet_schema->column(source_cid).is_key();
+}
+
size_t RowBinlogSourceDataWriter::normal_column_ordinal(uint32_t source_cid)
const {
auto it = std::find(_normal_column_ids.begin(), _normal_column_ids.end(),
source_cid);
DCHECK(it != _normal_column_ids.end()) << source_cid;
@@ -499,16 +519,18 @@ Status RowBinlogSourceDataWriter::prepare_by_source_block(
const ColumnWithTypeAndName& col =
block->get_by_position(is_partial_update ? col_pos_in_block++
: source_cid);
+ full_block->replace_by_position(source_cid, col.column);
+ const auto source_column_for_after =
is_row_binlog_value_column(source_cid)
+ ?
make_nullable_row_binlog_value_column(col)
+ : col;
RETURN_IF_ERROR(_olap_data_convertor->set_source_content_with_specifid_column(
- col, row_pos, num_rows, source_cid));
+ source_column_for_after, row_pos, num_rows, source_cid));
// olap data convertor alway start from id = 0
auto converted_result =
_olap_data_convertor->convert_column_data(source_cid);
if (!converted_result.first.ok()) {
return converted_result.first;
}
_converted_columns[source_cid] = converted_result.second;
-
- full_block->replace_by_position(source_cid, col.column);
}
for (uint32_t cid : _normal_column_ids) {
if (!tablet_schema->column(cid).is_key()) {
diff --git a/be/src/storage/segment/row_binlog_segment_writer.h
b/be/src/storage/segment/row_binlog_segment_writer.h
index 69023d8a3a7..4bd166d658a 100644
--- a/be/src/storage/segment/row_binlog_segment_writer.h
+++ b/be/src/storage/segment/row_binlog_segment_writer.h
@@ -55,6 +55,8 @@ public:
bool is_normal_column(uint32_t source_cid) const;
+ bool is_row_binlog_value_column(uint32_t source_cid) const;
+
size_t normal_column_ordinal(uint32_t source_cid) const;
private:
diff --git a/be/test/storage/rowset/segment_flusher_format_test.cpp
b/be/test/storage/rowset/segment_flusher_format_test.cpp
index 76199f8eb02..f39eb3e4f54 100644
--- a/be/test/storage/rowset/segment_flusher_format_test.cpp
+++ b/be/test/storage/rowset/segment_flusher_format_test.cpp
@@ -873,6 +873,121 @@ Result<Block> create_complex_value_block(const
TabletSchemaSPtr& schema, int seg
return block;
}
+struct ComplexRowBinlogSchemas {
+ TabletSchemaSPtr source;
+ TabletSchemaSPtr row_binlog;
+};
+
+ComplexRowBinlogSchemas create_complex_row_binlog_schemas() {
+ constexpr TypeCase array_type {.name = "array",
+ .storage_type = "ARRAY",
+ .length = OLAP_ARRAY_MAX_LENGTH,
+ .index_length = OLAP_ARRAY_MAX_LENGTH,
+ .precision = 0,
+ .scale = 0,
+ .values = {}};
+ constexpr TypeCase map_type {.name = "map",
+ .storage_type = "MAP",
+ .length = OLAP_MAP_MAX_LENGTH,
+ .index_length = OLAP_MAP_MAX_LENGTH,
+ .precision = 0,
+ .scale = 0,
+ .values = {}};
+ constexpr TypeCase struct_type {.name = "struct",
+ .storage_type = "STRUCT",
+ .length = OLAP_STRUCT_MAX_LENGTH,
+ .index_length = OLAP_STRUCT_MAX_LENGTH,
+ .precision = 0,
+ .scale = 0,
+ .values = {}};
+
+ TabletSchemaPB source_pb;
+ source_pb.set_keys_type(UNIQUE_KEYS);
+ source_pb.set_num_short_key_columns(1);
+ source_pb.set_num_rows_per_row_block(2);
+ source_pb.set_compression_type(LZ4F);
+ source_pb.set_storage_format(TABLET_STORAGE_FORMAT_V2);
+ add_column(&source_pb, 0, "k1", kKeyTypes[3], true, false);
+ auto* array = add_column(&source_pb, 1, "v_array", array_type, false,
false);
+ add_child_column(array, 101, "item", kKeyTypes[3], true);
+ auto* map = add_column(&source_pb, 2, "v_map", map_type, false, false);
+ add_child_column(map, 201, "key", kKeyTypes[12], true);
+ add_child_column(map, 202, "value", kKeyTypes[3], true);
+ auto* structure = add_column(&source_pb, 3, "v_struct", struct_type,
false, false);
+ add_child_column(structure, 301, "f_int", kKeyTypes[3], true);
+ add_child_column(structure, 302, "f_text", kKeyTypes[12], true);
+ add_hidden_column(&source_pb, 4, DELETE_SIGN, kKeyTypes[1], "NONE", "0");
+ source_pb.set_delete_sign_idx(4);
+ add_hidden_column(&source_pb, 5, VERSION_COL, kKeyTypes[4], "NONE", "0");
+ source_pb.set_version_col_idx(5);
+ add_hidden_column(&source_pb, 6, COMMIT_TSO_COL, kKeyTypes[4], "NONE",
"0");
+ source_pb.set_commit_tso_col_idx(6);
+ source_pb.set_next_column_unique_id(7);
+
+ TabletSchemaPB row_binlog_pb;
+ row_binlog_pb.set_keys_type(DUP_KEYS);
+ row_binlog_pb.set_num_short_key_columns(1);
+ row_binlog_pb.set_num_rows_per_row_block(2);
+ row_binlog_pb.set_compression_type(LZ4F);
+ row_binlog_pb.set_storage_format(TABLET_STORAGE_FORMAT_V2);
+ for (int cid = 0; cid < 4; ++cid) {
+ row_binlog_pb.add_column()->CopyFrom(source_pb.column(cid));
+ }
+ for (int cid = 1; cid < 4; ++cid) {
+ row_binlog_pb.mutable_column(cid)->set_is_nullable(true);
+ }
+ auto* tso = add_column(&row_binlog_pb, 4, BINLOG_TSO_COL, kKeyTypes[4],
false, true);
+ tso->set_visible(false);
+ row_binlog_pb.set_binlog_tso_col_idx(4);
+ auto* lsn = add_column(&row_binlog_pb, 5, BINLOG_LSN_COL, kKeyTypes[4],
false, false);
+ lsn->set_visible(false);
+ row_binlog_pb.set_binlog_lsn_col_idx(5);
+ auto* op = add_column(&row_binlog_pb, 6, BINLOG_OP_COL, kKeyTypes[4],
false, false);
+ op->set_visible(false);
+ row_binlog_pb.set_binlog_op_col_idx(6);
+ row_binlog_pb.set_next_column_unique_id(7);
+
+ auto source = std::make_shared<TabletSchema>();
+ source->init_from_pb(source_pb);
+ auto row_binlog = std::make_shared<TabletSchema>();
+ row_binlog->init_from_pb(row_binlog_pb);
+ return {.source = std::move(source), .row_binlog = std::move(row_binlog)};
+}
+
+Result<Block> create_complex_row_binlog_block(
+ const TabletSchemaSPtr& schema, int segment_ordinal,
+ const std::shared_ptr<PartialUpdateInfo>& partial_update_info =
nullptr) {
+ const std::array<std::string_view, 3> arrays {"[10,20]", "[]",
"[30,null]"};
+ const std::array<std::string_view, 3> maps {R"({"a":10,"b":20})", "{}",
R"({"c":null})"};
+ const std::array<std::string_view, 3> structs
{R"({"f_int":10,"f_text":"ten"})", "{}",
+
R"({"f_int":30,"f_text":"thirty"})"};
+ Block block = partial_update_info == nullptr
+ ? schema->create_block()
+ :
schema->create_block_by_cids(partial_update_info->update_cids);
+ for (size_t row = 0; row < 3; ++row) {
+ const auto value_index = (row + segment_ordinal) % arrays.size();
+ for (size_t column_index = 0; column_index < block.columns();
++column_index) {
+ const auto& name = block.get_by_position(column_index).name;
+ if (name == "k1") {
+ RETURN_IF_ERROR_RESULT(append_text_value(
+ &block, column_index,
+ std::to_string(segment_ordinal * 10 +
static_cast<int>(row))));
+ } else if (name == "v_array") {
+ RETURN_IF_ERROR_RESULT(
+ append_text_value(&block, column_index,
arrays[value_index]));
+ } else if (name == "v_map") {
+ RETURN_IF_ERROR_RESULT(append_text_value(&block, column_index,
maps[value_index]));
+ } else if (name == "v_struct") {
+ RETURN_IF_ERROR_RESULT(
+ append_text_value(&block, column_index,
structs[value_index]));
+ } else {
+
block.get_by_position(column_index).column->assert_mutable()->insert_default();
+ }
+ }
+ }
+ return block;
+}
+
TabletSchemaSPtr create_row_store_schema(KeysType keys_type = DUP_KEYS, bool
enable_mow = false) {
DORIS_CHECK(!enable_mow || keys_type == UNIQUE_KEYS);
TabletSchemaPB schema_pb;
@@ -2173,6 +2288,61 @@ Status verify_row_binlog_partial_update_segment(const
TabletSharedPtr& tablet,
return Status::OK();
}
+Status verify_complex_row_binlog_segment(const TabletSharedPtr& tablet,
std::string_view case_name,
+ uint32_t segment_id, const Block&
expected) {
+ auto block_result = read_row_binlog_segment(tablet, case_name, segment_id);
+ if (!block_result.has_value()) {
+ return block_result.error();
+ }
+ const auto& actual = block_result.value();
+ if (actual.rows() != expected.rows()) {
+ return Status::InternalError("{} segment {} has {} rows, expected {}",
case_name,
+ segment_id, actual.rows(),
expected.rows());
+ }
+ for (const auto* column_name : {"v_array", "v_map", "v_struct"}) {
+ const auto actual_position = actual.get_position_by_name(column_name);
+ const auto expected_position =
expected.get_position_by_name(column_name);
+ if (actual_position < 0 || expected_position < 0) {
+ return Status::InternalError("{} segment {} is missing column {}",
case_name,
+ segment_id, column_name);
+ }
+ for (size_t row = 0; row < expected.rows(); ++row) {
+ Field actual_value;
+ Field expected_value;
+ actual.get_by_position(actual_position).column->get(row,
actual_value);
+ expected.get_by_position(expected_position).column->get(row,
expected_value);
+ if (!logical_field_equal(actual_value, expected_value)) {
+ return Status::InternalError("{} segment {} column {} row {}
has value mismatch",
+ case_name, segment_id,
column_name, row);
+ }
+ }
+ }
+
+ const auto path = fmt::format("{}/{}/segment_{}.dat", kTestDir, case_name,
segment_id);
+ auto footer_result = read_segment_footer(path);
+ if (!footer_result.has_value()) {
+ return footer_result.error();
+ }
+ const auto& footer = footer_result.value();
+ for (uint32_t cid = 1; cid <= 3; ++cid) {
+ const auto column_meta =
+ std::find_if(footer.columns().begin(), footer.columns().end(),
+ [cid](const auto& meta) { return meta.column_id()
== cid; });
+ if (column_meta == footer.columns().end() ||
column_meta->children_columns_size() == 0) {
+ return Status::InternalError("{} segment {} has incomplete
metadata for column {}",
+ case_name, segment_id, cid);
+ }
+ const auto& null_meta =
+
column_meta->children_columns(column_meta->children_columns_size() - 1);
+ if (null_meta.num_rows() != column_meta->num_rows()) {
+ return Status::InternalError(
+ "{} segment {} column {} null child has {} rows, expected
{}", case_name,
+ segment_id, cid, null_meta.num_rows(),
column_meta->num_rows());
+ }
+ }
+ return Status::OK();
+}
+
struct RowBinlogBeforeVerificationOptions {
std::string_view case_name;
};
@@ -2694,7 +2864,7 @@ protected:
Result<RowsetSharedPtr> write_mow_history(const TabletSharedPtr& tablet,
const TabletSchemaSPtr& schema,
- int64_t rowset_numeric_id) {
+ int64_t rowset_numeric_id, Block
block) {
RowsetWriterContext context;
context.rowset_id.init(rowset_numeric_id);
context.tablet_id = tablet->tablet_id();
@@ -2717,11 +2887,6 @@ protected:
return unexpected(writer_result.error());
}
auto writer = std::move(writer_result).value();
- auto block_result = create_mow_history_block(schema);
- if (!block_result.has_value()) {
- return unexpected(block_result.error());
- }
- Block block = std::move(block_result).value();
RETURN_IF_ERROR_RESULT(writer->add_block(&block));
RETURN_IF_ERROR_RESULT(writer->flush());
RowsetSharedPtr rowset;
@@ -2729,6 +2894,17 @@ protected:
return rowset;
}
+ Result<RowsetSharedPtr> write_mow_history(const TabletSharedPtr& tablet,
+ const TabletSchemaSPtr& schema,
+ int64_t rowset_numeric_id) {
+ auto block_result = create_mow_history_block(schema);
+ if (!block_result.has_value()) {
+ return unexpected(block_result.error());
+ }
+ return write_mow_history(tablet, schema, rowset_numeric_id,
+ std::move(block_result).value());
+ }
+
struct BinlogTabletPair {
TabletSharedPtr source_tablet;
TabletSharedPtr binlog_tablet;
@@ -2850,6 +3026,55 @@ protected:
return {source_tablet, binlog_tablet};
}
+ BinlogTabletPair create_complex_row_binlog_tablets(const
ComplexRowBinlogSchemas& schemas,
+ int64_t tablet_id) {
+ auto source_tablet_meta = std::make_shared<TabletMeta>();
+ source_tablet_meta->_tablet_id = tablet_id;
+ DORIS_CHECK(source_tablet_meta->set_partition_id(10).ok());
+ source_tablet_meta->_schema = schemas.source;
+ source_tablet_meta->_enable_unique_key_merge_on_write = true;
+ auto source_tablet = std::make_shared<Tablet>(*_engine,
source_tablet_meta, _data_dir.get(),
+
fmt::format("format_ut_{}", tablet_id));
+
+ auto binlog_tablet_meta = std::make_shared<TabletMeta>();
+ binlog_tablet_meta->_tablet_id = tablet_id + 10000;
+ DORIS_CHECK(binlog_tablet_meta->set_partition_id(10).ok());
+ binlog_tablet_meta->_schema = schemas.row_binlog;
+
binlog_tablet_meta->set_tablet_role(TabletRolePB::TABLET_ROLE_ROW_BINLOG);
+ auto binlog_tablet = std::make_shared<Tablet>(
+ *_engine, binlog_tablet_meta, _data_dir.get(),
+ fmt::format("format_ut_{}", binlog_tablet_meta->_tablet_id));
+
+ _tablets.push_back(source_tablet);
+ _tablets.push_back(binlog_tablet);
+ return {source_tablet, binlog_tablet};
+ }
+
+ Status flush_row_binlog_block(
+ std::string_view case_name, const TabletSchemaSPtr& schema, Block
block,
+ const std::function<void(RowsetWriterContext&)>&
configure_context) const {
+ const auto directory = fmt::format("{}/{}", kTestDir, case_name);
+
RETURN_IF_ERROR(io::global_local_filesystem()->create_directory(directory));
+ auto file_writer_creator =
+ std::make_shared<LocalSegmentFileWriterCreator>(directory,
schema);
+ RowsetWriterContext context;
+ context.tablet_schema = schema;
+ context.tablet_path = directory;
+ context.tablet_id = 10001;
+ context.rowset_id.init(10002);
+ context.max_rows_per_segment = 1024;
+ context.write_type = DataWriteType::TYPE_DIRECT;
+ context.file_writer_creator = file_writer_creator;
+ context.segment_collector = std::make_shared<TestSegmentCollector>();
+ configure_context(context);
+
+ SegmentFileCollection segment_files;
+ InvertedIndexFileCollection index_files;
+ SegmentFlusher flusher(context, segment_files, index_files);
+ RETURN_IF_ERROR(flusher.flush_single_block(&block, 0));
+ return flusher.close();
+ }
+
void configure_partial_update_context(
RowsetWriterContext& context, const TabletSharedPtr& tablet,
const std::shared_ptr<PartialUpdateInfo>& partial_update_info,
@@ -2900,6 +3125,93 @@ protected:
std::string _storage_root_path;
};
+TEST_F(SegmentFlusherTransformFormatTest,
+ RowBinlogWritesNotNullComplexColumnsToNullableAfterColumns) {
+ const auto schemas = create_complex_row_binlog_schemas();
+
+ auto full_update_tablets = create_complex_row_binlog_tablets(schemas,
22005);
+ auto full_update_block_result =
create_complex_row_binlog_block(schemas.source, 0);
+ ASSERT_TRUE(full_update_block_result.has_value()) <<
full_update_block_result.error();
+ auto full_update_block = std::move(full_update_block_result).value();
+ const auto expected_full_update = full_update_block;
+ ASSERT_TRUE(flush_row_binlog_block("complex_row_binlog_full_update",
schemas.row_binlog,
+ std::move(full_update_block),
+ [this,
full_update_tablets](RowsetWriterContext& context) {
+ configure_row_binlog_context(
+ context,
full_update_tablets.source_tablet,
+
full_update_tablets.binlog_tablet);
+ })
+ .ok());
+
ASSERT_TRUE(verify_complex_row_binlog_segment(full_update_tablets.binlog_tablet,
+
"complex_row_binlog_full_update", 0,
+ expected_full_update)
+ .ok());
+
+ auto partial_update_tablets = create_complex_row_binlog_tablets(schemas,
22006);
+ auto partial_update_info = std::make_shared<PartialUpdateInfo>();
+ ASSERT_TRUE(partial_update_info
+
->init(partial_update_tablets.source_tablet->tablet_id(), 1,
+ *schemas.source,
UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS,
+ PartialUpdateNewRowPolicyPB::APPEND,
+ {"k1", "v_array", "v_map", "v_struct"}, false,
0, 0, "UTC", "")
+ .ok());
+ auto partial_update_block_result =
+ create_complex_row_binlog_block(schemas.source, 1,
partial_update_info);
+ ASSERT_TRUE(partial_update_block_result.has_value()) <<
partial_update_block_result.error();
+ auto partial_update_block = std::move(partial_update_block_result).value();
+ const auto expected_partial_update = partial_update_block;
+ ASSERT_TRUE(flush_row_binlog_block("complex_row_binlog_partial_update",
schemas.row_binlog,
+ std::move(partial_update_block),
+ [this, partial_update_tablets,
+
partial_update_info](RowsetWriterContext& context) {
+ configure_row_binlog_context(
+ context,
partial_update_tablets.source_tablet,
+
partial_update_tablets.binlog_tablet,
+ partial_update_info);
+ })
+ .ok());
+
ASSERT_TRUE(verify_complex_row_binlog_segment(partial_update_tablets.binlog_tablet,
+
"complex_row_binlog_partial_update", 0,
+ expected_partial_update)
+ .ok());
+
+ auto missing_update_tablets = create_complex_row_binlog_tablets(schemas,
22007);
+ auto history_block_result =
create_complex_row_binlog_block(schemas.source, 0);
+ ASSERT_TRUE(history_block_result.has_value()) <<
history_block_result.error();
+ auto history_result =
write_mow_history(missing_update_tablets.source_tablet, schemas.source,
+ 31011,
std::move(history_block_result).value());
+ ASSERT_TRUE(history_result.has_value()) << history_result.error();
+ std::vector<RowsetSharedPtr> history {history_result.value()};
+
+ auto missing_update_info = std::make_shared<PartialUpdateInfo>();
+ ASSERT_TRUE(missing_update_info
+
->init(missing_update_tablets.source_tablet->tablet_id(), 1,
+ *schemas.source,
UniqueKeyUpdateModePB::UPDATE_FIXED_COLUMNS,
+ PartialUpdateNewRowPolicyPB::APPEND, {"k1",
"v_array"}, false, 0, 0,
+ "UTC", "")
+ .ok());
+ auto missing_update_block_result =
+ create_complex_row_binlog_block(schemas.source, 0,
missing_update_info);
+ ASSERT_TRUE(missing_update_block_result.has_value()) <<
missing_update_block_result.error();
+ auto expected_missing_update_result =
create_complex_row_binlog_block(schemas.source, 0);
+ ASSERT_TRUE(expected_missing_update_result.has_value())
+ << expected_missing_update_result.error();
+ ASSERT_TRUE(flush_row_binlog_block("complex_row_binlog_missing_update",
schemas.row_binlog,
+
std::move(missing_update_block_result).value(),
+ [this, missing_update_tablets,
missing_update_info,
+ history](RowsetWriterContext& context)
{
+ configure_row_binlog_context(
+ context,
missing_update_tablets.source_tablet,
+
missing_update_tablets.binlog_tablet,
+ missing_update_info,
history);
+ })
+ .ok());
+
ASSERT_TRUE(verify_complex_row_binlog_segment(missing_update_tablets.binlog_tablet,
+
"complex_row_binlog_missing_update", 0,
+
expected_missing_update_result.value())
+ .ok());
+}
+
TEST_F(SegmentFlusherFormatTest, VariantLogicalComparisonPreservesScalarTypes)
{
VariantMap boolean_object;
boolean_object.try_emplace(
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]