Skip to content

Commit 83a2f01

Browse files
committed
[improvement](be) Batch constant fills in rowid direct reads
### What problem does this PR solve? Issue Number: None Related PR: #68125 Problem Summary: `read_slot_column_by_rowid()` appended a VERSION/COMMIT_TSO constant with `insert(Field)` once per row. The caller already reserves the batch capacity in `Block(slots, row_ids.size())`, so this commit replaces the per-row virtual insert, `Field` extraction and push-back with one batched fill. `ColumnVector` now overrides `insert_duplicate_fields()` to delegate to `insert_many_vals(x.get<T>(), n)`, which performs one resize followed by `std::fill`; the generic inherited implementation still looped over `insert(Field)`. `ColumnNullable::insert_duplicate_fields()` keeps its batched null-map handling and delegates the nested constant to the new override. Zero-count and append-after-existing-rows semantics are covered by tests. ### Release note None ### Check List (For Author) - Test: Unit Test / Regression test - BE UT: `ColumnVectorTest.insert_duplicate_fields` (all ColumnVector element types, zero count and append) and `ColumnNullableTest.InsertDuplicateFieldsKeepsBatchedNullMap` passed. - Local regression: `point_query_p0/test_point_query_read_time_hidden_columns` passed on the direct-read path. - Behavior changed: No - Does this need documentation: No
1 parent 522030c commit 83a2f01

5 files changed

Lines changed: 69 additions & 7 deletions

File tree

‎be/src/core/column/column_vector.h‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ class ColumnVector final : public COWHelper<IColumn, ColumnVector<T>> {
117117
std::fill(data.data() + old_size, data.data() + old_size + n, val);
118118
}
119119

120+
// Batched constant append: one resize and fill instead of one insert(Field) per row.
121+
void insert_duplicate_fields(const Field& x, const size_t n) override {
122+
insert_many_vals(x.get<T>(), n);
123+
}
124+
120125
void insert_many_from(const IColumn& src, size_t position, size_t length) override;
121126

122127
void insert_range_of_integer(value_type begin, value_type end) {

‎be/src/exec/rowid_fetcher.cpp‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include <algorithm>
3030
#include <cstdint>
3131
#include <memory>
32+
#include <optional>
3233
#include <ostream>
3334
#include <string>
3435
#include <unordered_map>
@@ -175,13 +176,12 @@ static Status read_slot_column_by_rowid(
175176
OlapReaderStatistics& stats, io::FileCacheMissPolicy file_cache_miss_policy,
176177
std::unordered_map<IteratorKey, IteratorItem, HashOfIteratorKey>& iterator_map,
177178
MutableColumnPtr& column) {
178-
if (auto value =
179-
get_read_time_hidden_column_value(get_read_time_hidden_column_type(read_column),
180-
rowset.version(), rowset.commit_tso());
181-
value.has_value()) {
182-
for (size_t i = 0; i < row_ids.size(); ++i) {
183-
column->insert(*value);
184-
}
179+
std::optional<Field> constant_value;
180+
RETURN_IF_ERROR(get_read_time_hidden_column_value(get_read_time_hidden_column_type(read_column),
181+
rowset.version(), rowset.commit_tso(),
182+
&constant_value));
183+
if (constant_value.has_value()) {
184+
column->insert_duplicate_fields(*constant_value, row_ids.size());
185185
return Status::OK();
186186
}
187187
IteratorKey iterator_key {.tablet_id = seg_key.tablet_id,

‎be/test/core/column/column_nullable_test.cpp‎

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,4 +492,27 @@ TEST(ColumnNullableTest, ScalaTypeNullStringTest2erase) {
492492
}
493493
}
494494

495+
TEST(ColumnNullableTest, InsertDuplicateFieldsKeepsBatchedNullMap) {
496+
auto column = ColumnNullable::create(ColumnInt64::create(), ColumnUInt8::create());
497+
column->insert_duplicate_fields(Field::create_field<TYPE_BIGINT>(7), 2);
498+
ASSERT_EQ(2, column->size());
499+
EXPECT_FALSE(column->has_null());
500+
const auto& nested = assert_cast<const ColumnInt64&>(column->get_nested_column());
501+
EXPECT_EQ(7, nested.get_element(0));
502+
EXPECT_EQ(7, nested.get_element(1));
503+
504+
// A zero-count append must not change the size or the null map.
505+
column->insert_duplicate_fields(Field::create_field<TYPE_BIGINT>(8), 0);
506+
ASSERT_EQ(2, column->size());
507+
EXPECT_FALSE(column->has_null());
508+
509+
// ColumnNullable keeps its batched null-map handling for a null constant.
510+
column->insert_duplicate_fields(Field(), 3);
511+
ASSERT_EQ(5, column->size());
512+
EXPECT_TRUE(column->has_null());
513+
EXPECT_EQ(1, column->get_null_map_data()[2]);
514+
EXPECT_EQ(1, column->get_null_map_data()[3]);
515+
EXPECT_EQ(1, column->get_null_map_data()[4]);
516+
}
517+
495518
} // namespace doris

‎be/test/core/column/column_vector_test.cpp‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,9 @@ TEST_F(ColumnVectorTest, insert_data) {
239239
TEST_F(ColumnVectorTest, insert_many_vals) {
240240
_column_vector_common_test(assert_column_vector_insert_many_vals_callback);
241241
}
242+
TEST_F(ColumnVectorTest, insert_duplicate_fields) {
243+
_column_vector_common_test(assert_column_vector_insert_duplicate_fields_callback);
244+
}
242245
TEST_F(ColumnVectorTest, insert_many_from) {
243246
_column_vector_common_test(assert_column_vector_insert_many_from_callback);
244247
}

‎be/test/core/column/common_column_test.h‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2599,6 +2599,37 @@ auto assert_column_vector_insert_many_vals_callback = [](auto x,
25992599
test_func(0);
26002600
test_func(10);
26012601
};
2602+
template <PrimitiveType PType>
2603+
auto assert_column_vector_insert_duplicate_fields_callback =
2604+
[](auto x, const MutableColumnPtr& source_column) {
2605+
auto* col_vec_src = assert_cast<ColumnVector<PType>*>(source_column.get());
2606+
auto src_size = source_column->size();
2607+
std::vector<size_t> insert_vals_count = {0, 10, 1000};
2608+
std::vector<size_t> src_data_indices = {0, src_size - 1, (src_size + 1) >> 1};
2609+
for (size_t pos : src_data_indices) {
2610+
if (pos >= src_size) {
2611+
continue;
2612+
}
2613+
Field value;
2614+
source_column->get(pos, value);
2615+
for (size_t n : insert_vals_count) {
2616+
auto target_column = source_column->clone_empty();
2617+
auto* col_vec_target = assert_cast<ColumnVector<PType>*>(target_column.get());
2618+
col_vec_target->insert_duplicate_fields(value, n);
2619+
ASSERT_EQ(col_vec_target->size(), n);
2620+
for (size_t i = 0; i < n; ++i) {
2621+
ASSERT_EQ(col_vec_target->get_element(i), col_vec_src->get_element(pos));
2622+
}
2623+
// Appending after existing rows preserves the batch and its order.
2624+
col_vec_target->insert_duplicate_fields(value, n + 1);
2625+
ASSERT_EQ(col_vec_target->size(), n * 2 + 1);
2626+
for (size_t i = 0; i < col_vec_target->size(); ++i) {
2627+
ASSERT_EQ(col_vec_target->get_element(i), col_vec_src->get_element(pos));
2628+
}
2629+
}
2630+
}
2631+
};
2632+
26022633
template <PrimitiveType PType>
26032634
auto assert_column_vector_insert_from_callback = [](auto x, const MutableColumnPtr& source_column) {
26042635
auto target_column = source_column->clone_empty();

0 commit comments

Comments
 (0)