Skip to content

Commit 2775152

Browse files
committed
feat(core): introduce ColumnarRowRef with shared batch context
Add ColumnarBatchContext and ColumnarRowRef to reduce per-row construction overhead in KeyValueDataFileRecordReader. Switch key/value row construction in KeyValueDataFileRecordReader to ColumnarRowRef and manage batch-level contexts through reader lifecycle. Add unit coverage for ColumnarRowRef basic access and RowKind behavior.
1 parent cbacf3a commit 2775152

7 files changed

Lines changed: 305 additions & 8 deletions

File tree

src/paimon/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ set(PAIMON_COMMON_SRCS
2929
common/data/columnar/columnar_array.cpp
3030
common/data/columnar/columnar_map.cpp
3131
common/data/columnar/columnar_row.cpp
32+
common/data/columnar/columnar_row_ref.cpp
3233
common/data/decimal.cpp
3334
common/data/internal_row.cpp
3435
common/data/record_batch.cpp
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* Copyright 2024-present Alibaba Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <memory>
20+
#include <vector>
21+
22+
#include "arrow/array/array_base.h"
23+
24+
namespace arrow {
25+
class StructArray;
26+
} // namespace arrow
27+
28+
namespace paimon {
29+
class MemoryPool;
30+
31+
struct ColumnarBatchContext {
32+
ColumnarBatchContext(std::shared_ptr<arrow::StructArray> struct_array_in,
33+
const arrow::ArrayVector& array_vec_in,
34+
std::shared_ptr<MemoryPool> pool_in)
35+
: struct_array(std::move(struct_array_in)),
36+
pool(std::move(pool_in)),
37+
array_vec_holder(array_vec_in) {
38+
array_vec.reserve(array_vec_holder.size());
39+
for (const auto& array : array_vec_holder) {
40+
array_vec.push_back(array.get());
41+
}
42+
}
43+
44+
std::shared_ptr<arrow::StructArray> struct_array;
45+
std::shared_ptr<MemoryPool> pool;
46+
arrow::ArrayVector array_vec_holder;
47+
std::vector<const arrow::Array*> array_vec;
48+
};
49+
} // namespace paimon
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2024-present Alibaba Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include "paimon/common/data/columnar/columnar_row_ref.h"
18+
19+
#include <cassert>
20+
21+
#include "arrow/array/array_decimal.h"
22+
#include "arrow/array/array_nested.h"
23+
#include "arrow/array/array_primitive.h"
24+
#include "arrow/type_traits.h"
25+
#include "arrow/util/checked_cast.h"
26+
#include "arrow/util/decimal.h"
27+
#include "paimon/common/data/columnar/columnar_array.h"
28+
#include "paimon/common/data/columnar/columnar_map.h"
29+
#include "paimon/common/utils/date_time_utils.h"
30+
31+
namespace paimon {
32+
Decimal ColumnarRowRef::GetDecimal(int32_t pos, int32_t precision, int32_t scale) const {
33+
using ArrayType = typename arrow::TypeTraits<arrow::Decimal128Type>::ArrayType;
34+
auto array = arrow::internal::checked_cast<const ArrayType*>(ctx_->array_vec[pos]);
35+
assert(array);
36+
arrow::Decimal128 decimal(array->GetValue(row_id_));
37+
return Decimal(precision, scale,
38+
static_cast<Decimal::int128_t>(decimal.high_bits()) << 64 | decimal.low_bits());
39+
}
40+
41+
Timestamp ColumnarRowRef::GetTimestamp(int32_t pos, int32_t precision) const {
42+
using ArrayType = typename arrow::TypeTraits<arrow::TimestampType>::ArrayType;
43+
auto array = arrow::internal::checked_cast<const ArrayType*>(ctx_->array_vec[pos]);
44+
assert(array);
45+
int64_t data = array->Value(row_id_);
46+
auto timestamp_type =
47+
arrow::internal::checked_pointer_cast<arrow::TimestampType>(array->type());
48+
// for orc format, data is saved as nano, therefore, Timestamp convert should consider precision
49+
// in arrow array rather than input precision
50+
DateTimeUtils::TimeType time_type = DateTimeUtils::GetTimeTypeFromArrowType(timestamp_type);
51+
auto [milli, nano] = DateTimeUtils::TimestampConverter(
52+
data, time_type, DateTimeUtils::TimeType::MILLISECOND, DateTimeUtils::TimeType::NANOSECOND);
53+
return Timestamp(milli, nano);
54+
}
55+
56+
std::shared_ptr<InternalRow> ColumnarRowRef::GetRow(int32_t pos, int32_t num_fields) const {
57+
auto struct_array =
58+
arrow::internal::checked_pointer_cast<arrow::StructArray>(ctx_->array_vec_holder[pos]);
59+
assert(struct_array);
60+
auto nested_ctx = std::make_shared<ColumnarBatchContext>(
61+
struct_array, struct_array->fields(), ctx_->pool);
62+
return std::make_shared<ColumnarRowRef>(std::move(nested_ctx), row_id_);
63+
}
64+
65+
std::shared_ptr<InternalArray> ColumnarRowRef::GetArray(int32_t pos) const {
66+
auto list_array = arrow::internal::checked_cast<const arrow::ListArray*>(ctx_->array_vec[pos]);
67+
assert(list_array);
68+
int32_t offset = list_array->value_offset(row_id_);
69+
int32_t length = list_array->value_length(row_id_);
70+
return std::make_shared<ColumnarArray>(list_array->values(), ctx_->pool, offset, length);
71+
}
72+
73+
std::shared_ptr<InternalMap> ColumnarRowRef::GetMap(int32_t pos) const {
74+
auto map_array = arrow::internal::checked_cast<const arrow::MapArray*>(ctx_->array_vec[pos]);
75+
assert(map_array);
76+
int32_t offset = map_array->value_offset(row_id_);
77+
int32_t length = map_array->value_length(row_id_);
78+
return std::make_shared<ColumnarMap>(map_array->keys(), map_array->items(), ctx_->pool, offset,
79+
length);
80+
}
81+
82+
} // namespace paimon
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright 2024-present Alibaba Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <cstdint>
20+
#include <memory>
21+
#include <string>
22+
#include <string_view>
23+
24+
#include "fmt/format.h"
25+
#include "paimon/common/data/binary_string.h"
26+
#include "paimon/common/data/columnar/columnar_batch_context.h"
27+
#include "paimon/common/data/columnar/columnar_utils.h"
28+
#include "paimon/common/data/internal_array.h"
29+
#include "paimon/common/data/internal_map.h"
30+
#include "paimon/common/data/internal_row.h"
31+
#include "paimon/common/types/row_kind.h"
32+
#include "paimon/data/decimal.h"
33+
#include "paimon/data/timestamp.h"
34+
#include "paimon/result.h"
35+
36+
namespace paimon {
37+
class Bytes;
38+
39+
/// Columnar row view which shares batch-level context to reduce per-row overhead.
40+
class ColumnarRowRef : public InternalRow {
41+
public:
42+
ColumnarRowRef(std::shared_ptr<ColumnarBatchContext> ctx, int64_t row_id)
43+
: ctx_(std::move(ctx)), row_id_(row_id) {}
44+
45+
Result<const RowKind*> GetRowKind() const override {
46+
return row_kind_;
47+
}
48+
49+
void SetRowKind(const RowKind* kind) override {
50+
row_kind_ = kind;
51+
}
52+
53+
int32_t GetFieldCount() const override {
54+
return static_cast<int32_t>(ctx_->array_vec.size());
55+
}
56+
57+
bool IsNullAt(int32_t pos) const override {
58+
return ctx_->array_vec[pos]->IsNull(row_id_);
59+
}
60+
61+
bool GetBoolean(int32_t pos) const override {
62+
return ColumnarUtils::GetGenericValue<arrow::BooleanType, bool>(ctx_->array_vec[pos],
63+
row_id_);
64+
}
65+
66+
char GetByte(int32_t pos) const override {
67+
return ColumnarUtils::GetGenericValue<arrow::Int8Type, char>(ctx_->array_vec[pos], row_id_);
68+
}
69+
70+
int16_t GetShort(int32_t pos) const override {
71+
return ColumnarUtils::GetGenericValue<arrow::Int16Type, int16_t>(ctx_->array_vec[pos],
72+
row_id_);
73+
}
74+
75+
int32_t GetInt(int32_t pos) const override {
76+
return ColumnarUtils::GetGenericValue<arrow::Int32Type, int32_t>(ctx_->array_vec[pos],
77+
row_id_);
78+
}
79+
80+
int32_t GetDate(int32_t pos) const override {
81+
return ColumnarUtils::GetGenericValue<arrow::Date32Type, int32_t>(ctx_->array_vec[pos],
82+
row_id_);
83+
}
84+
85+
int64_t GetLong(int32_t pos) const override {
86+
return ColumnarUtils::GetGenericValue<arrow::Int64Type, int64_t>(ctx_->array_vec[pos],
87+
row_id_);
88+
}
89+
90+
float GetFloat(int32_t pos) const override {
91+
return ColumnarUtils::GetGenericValue<arrow::FloatType, float>(ctx_->array_vec[pos],
92+
row_id_);
93+
}
94+
95+
double GetDouble(int32_t pos) const override {
96+
return ColumnarUtils::GetGenericValue<arrow::DoubleType, double>(ctx_->array_vec[pos],
97+
row_id_);
98+
}
99+
100+
BinaryString GetString(int32_t pos) const override {
101+
auto bytes = ColumnarUtils::GetBytes<arrow::StringType>(ctx_->array_vec[pos], row_id_,
102+
ctx_->pool.get());
103+
return BinaryString::FromBytes(bytes);
104+
}
105+
106+
std::string_view GetStringView(int32_t pos) const override {
107+
return ColumnarUtils::GetView(ctx_->array_vec[pos], row_id_);
108+
}
109+
110+
Decimal GetDecimal(int32_t pos, int32_t precision, int32_t scale) const override;
111+
112+
Timestamp GetTimestamp(int32_t pos, int32_t precision) const override;
113+
114+
std::shared_ptr<Bytes> GetBinary(int32_t pos) const override {
115+
return ColumnarUtils::GetBytes<arrow::BinaryType>(ctx_->array_vec[pos], row_id_,
116+
ctx_->pool.get());
117+
}
118+
119+
std::shared_ptr<InternalRow> GetRow(int32_t pos, int32_t num_fields) const override;
120+
121+
std::shared_ptr<InternalArray> GetArray(int32_t pos) const override;
122+
123+
std::shared_ptr<InternalMap> GetMap(int32_t pos) const override;
124+
125+
std::string ToString() const override {
126+
return fmt::format("ColumnarRowRef, row_id {}", row_id_);
127+
}
128+
129+
private:
130+
std::shared_ptr<ColumnarBatchContext> ctx_;
131+
const RowKind* row_kind_ = RowKind::Insert();
132+
int64_t row_id_;
133+
};
134+
} // namespace paimon

src/paimon/common/data/columnar/columnar_row_test.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616

1717
#include "paimon/common/data/columnar/columnar_row.h"
18+
#include "paimon/common/data/columnar/columnar_row_ref.h"
1819

1920
#include <utility>
2021

@@ -70,6 +71,32 @@ TEST(ColumnarRowTest, TestSimple) {
7071
ASSERT_EQ(std::string(row.GetStringView(7)), "Hello");
7172
}
7273

74+
TEST(ColumnarRowRefTest, TestSimple) {
75+
auto pool = GetDefaultPool();
76+
std::shared_ptr<arrow::DataType> target_type =
77+
arrow::struct_({arrow::field("f1", arrow::int32()), arrow::field("f2", arrow::utf8())});
78+
auto f1 =
79+
arrow::ipc::internal::json::ArrayFromJSON(arrow::int32(), R"([1, 2, 3])").ValueOrDie();
80+
auto f2 = arrow::ipc::internal::json::ArrayFromJSON(arrow::utf8(),
81+
R"(["alpha", "beta", "gamma"])")
82+
.ValueOrDie();
83+
auto data = arrow::StructArray::Make({f1, f2}, target_type->fields()).ValueOrDie();
84+
85+
auto ctx = std::make_shared<ColumnarBatchContext>(data, data->fields(), pool);
86+
ColumnarRowRef row(ctx, 1);
87+
ASSERT_EQ(row.GetFieldCount(), 2);
88+
ASSERT_EQ(row.GetInt(0), 2);
89+
ASSERT_EQ(std::string(row.GetStringView(1)), "beta");
90+
91+
auto row_kind = row.GetRowKind();
92+
ASSERT_TRUE(row_kind.ok());
93+
ASSERT_EQ(row_kind.value(), RowKind::Insert());
94+
row.SetRowKind(RowKind::Delete());
95+
auto updated_kind = row.GetRowKind();
96+
ASSERT_TRUE(updated_kind.ok());
97+
ASSERT_EQ(updated_kind.value(), RowKind::Delete());
98+
}
99+
73100
TEST(ColumnarRowTest, TestComplexAndNestedType) {
74101
auto pool = GetDefaultPool();
75102
std::shared_ptr<arrow::DataType> target_type = arrow::struct_({

src/paimon/core/io/key_value_data_file_record_reader.cpp

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@
2828
#include "arrow/type.h"
2929
#include "arrow/util/checked_cast.h"
3030
#include "fmt/format.h"
31-
#include "paimon/common/data/columnar/columnar_row.h"
32-
#include "paimon/common/data/internal_row.h"
31+
#include "paimon/common/data/columnar/columnar_row_ref.h"
3332
#include "paimon/common/table/special_fields.h"
3433
#include "paimon/common/types/row_kind.h"
3534
#include "paimon/common/utils/arrow/status_utils.h"
@@ -69,12 +68,11 @@ bool KeyValueDataFileRecordReader::Iterator::HasNext() const {
6968

7069
Result<KeyValue> KeyValueDataFileRecordReader::Iterator::Next() {
7170
assert(HasNext());
72-
// as key is only used in merge sort, do not hold the data in ColumnarRow
73-
auto key = std::make_unique<ColumnarRow>(reader_->key_fields_, reader_->pool_, cursor_);
71+
// as key is only used in merge sort, do not hold the data in ColumnarRowRef
72+
auto key = std::make_unique<ColumnarRowRef>(reader_->key_ctx_, cursor_);
7473
// as value is used in merge sort and projection (maybe async and multi-thread), hold the data
75-
// in ColumnarRow
76-
auto value = std::make_unique<ColumnarRow>(reader_->value_struct_array_, reader_->value_fields_,
77-
reader_->pool_, cursor_);
74+
// in ColumnarRowRef
75+
auto value = std::make_unique<ColumnarRowRef>(reader_->value_ctx_, cursor_);
7876
PAIMON_ASSIGN_OR_RAISE(const RowKind* row_kind,
7977
RowKind::FromByteValue(reader_->row_kind_array_->Value(cursor_)));
8078
int64_t sequence_number = reader_->sequence_number_array_->Value(cursor_);
@@ -137,6 +135,8 @@ Result<std::unique_ptr<KeyValueRecordReader::Iterator>> KeyValueDataFileRecordRe
137135
arrow::StructArray::Make(value_fields_, value_names_));
138136
selection_bitmap_ = std::move(bitmap);
139137
value_fields_ = value_struct_array_->fields();
138+
key_ctx_ = std::make_shared<ColumnarBatchContext>(nullptr, key_fields_, pool_);
139+
value_ctx_ = std::make_shared<ColumnarBatchContext>(value_struct_array_, value_fields_, pool_);
140140
TraverseArray(value_struct_array_);
141141
return std::make_unique<KeyValueDataFileRecordReader::Iterator>(this);
142142
}
@@ -148,6 +148,8 @@ void KeyValueDataFileRecordReader::Reset() {
148148
value_struct_array_.reset();
149149
sequence_number_array_.reset();
150150
row_kind_array_.reset();
151+
key_ctx_.reset();
152+
value_ctx_.reset();
151153
}
152154

153155
void KeyValueDataFileRecordReader::TraverseArray(const std::shared_ptr<arrow::Array>& array) {

src/paimon/core/io/key_value_data_file_record_reader.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@
2222
#include <vector>
2323

2424
#include "arrow/type_fwd.h"
25-
#include "paimon/common/data/columnar/columnar_row.h"
2625
#include "paimon/core/io/key_value_record_reader.h"
2726
#include "paimon/core/key_value.h"
2827
#include "paimon/reader/batch_reader.h"
@@ -42,6 +41,7 @@ class NumericArray;
4241
namespace paimon {
4342
class MemoryPool;
4443
class Metrics;
44+
struct ColumnarBatchContext;
4545

4646
// Convert the arrow array of data file into a KeyValue object iterator (parsing SEQUENCE_NUMBER and
4747
// VALUE_KIND columns)
@@ -95,5 +95,7 @@ class KeyValueDataFileRecordReader : public KeyValueRecordReader {
9595
arrow::ArrayVector value_fields_;
9696
std::shared_ptr<arrow::NumericArray<arrow::Int64Type>> sequence_number_array_;
9797
std::shared_ptr<arrow::NumericArray<arrow::Int8Type>> row_kind_array_;
98+
std::shared_ptr<ColumnarBatchContext> key_ctx_;
99+
std::shared_ptr<ColumnarBatchContext> value_ctx_;
98100
};
99101
} // namespace paimon

0 commit comments

Comments
 (0)