Skip to content

Commit 91dd359

Browse files
authored
feat(format): introduce avro writer components (#88)
1 parent 990580f commit 91dd359

11 files changed

Lines changed: 1878 additions & 0 deletions
Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#include "paimon/format/avro/avro_file_batch_reader.h"
20+
21+
#include <memory>
22+
#include <utility>
23+
24+
#include "arrow/c/bridge.h"
25+
#include "fmt/format.h"
26+
#include "paimon/common/metrics/metrics_impl.h"
27+
#include "paimon/common/utils/arrow/arrow_utils.h"
28+
#include "paimon/common/utils/arrow/mem_utils.h"
29+
#include "paimon/common/utils/arrow/status_utils.h"
30+
#include "paimon/common/utils/scope_guard.h"
31+
#include "paimon/format/avro/avro_input_stream_impl.h"
32+
#include "paimon/format/avro/avro_schema_converter.h"
33+
#include "paimon/reader/batch_reader.h"
34+
35+
namespace paimon::avro {
36+
37+
AvroFileBatchReader::AvroFileBatchReader(const std::shared_ptr<InputStream>& input_stream,
38+
const std::shared_ptr<::arrow::DataType>& file_data_type,
39+
std::unique_ptr<::avro::DataFileReaderBase>&& reader,
40+
std::unique_ptr<arrow::ArrayBuilder>&& array_builder,
41+
std::unique_ptr<arrow::MemoryPool>&& arrow_pool,
42+
int32_t batch_size,
43+
const std::shared_ptr<MemoryPool>& pool)
44+
: pool_(pool),
45+
arrow_pool_(std::move(arrow_pool)),
46+
input_stream_(input_stream),
47+
file_data_type_(file_data_type),
48+
reader_(std::move(reader)),
49+
array_builder_(std::move(array_builder)),
50+
batch_size_(batch_size),
51+
metrics_(std::make_shared<MetricsImpl>()) {}
52+
53+
AvroFileBatchReader::~AvroFileBatchReader() {
54+
DoClose();
55+
}
56+
57+
void AvroFileBatchReader::DoClose() {
58+
if (!close_) {
59+
reader_->close();
60+
close_ = true;
61+
}
62+
}
63+
64+
Result<std::unique_ptr<AvroFileBatchReader>> AvroFileBatchReader::Create(
65+
const std::shared_ptr<InputStream>& input_stream, int32_t batch_size,
66+
const std::shared_ptr<MemoryPool>& pool) {
67+
if (batch_size <= 0) {
68+
return Status::Invalid(
69+
fmt::format("invalid batch size {}, must be larger than 0", batch_size));
70+
}
71+
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<::avro::DataFileReaderBase> reader,
72+
CreateDataFileReader(input_stream, pool));
73+
const auto& avro_file_schema = reader->dataSchema();
74+
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<::arrow::DataType> file_data_type,
75+
AvroSchemaConverter::AvroSchemaToArrowDataType(avro_file_schema));
76+
auto arrow_pool = GetArrowPool(pool);
77+
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::unique_ptr<arrow::ArrayBuilder> array_builder,
78+
arrow::MakeBuilder(file_data_type, arrow_pool.get()));
79+
return std::unique_ptr<AvroFileBatchReader>(
80+
new AvroFileBatchReader(input_stream, file_data_type, std::move(reader),
81+
std::move(array_builder), std::move(arrow_pool), batch_size, pool));
82+
}
83+
84+
Result<std::unique_ptr<::avro::DataFileReaderBase>> AvroFileBatchReader::CreateDataFileReader(
85+
const std::shared_ptr<InputStream>& input_stream, const std::shared_ptr<MemoryPool>& pool) {
86+
PAIMON_RETURN_NOT_OK(input_stream->Seek(0, SeekOrigin::FS_SEEK_SET));
87+
try {
88+
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<::avro::InputStream> in,
89+
AvroInputStreamImpl::Create(input_stream, BUFFER_SIZE, pool));
90+
auto reader = std::make_unique<::avro::DataFileReaderBase>(std::move(in));
91+
reader->init();
92+
return reader;
93+
} catch (const ::avro::Exception& e) {
94+
return Status::Invalid(fmt::format("build avro reader failed. {}", e.what()));
95+
} catch (const std::exception& e) {
96+
return Status::Invalid(fmt::format("build avro reader failed. {}", e.what()));
97+
} catch (...) {
98+
return Status::Invalid("build avro reader failed. unknown error");
99+
}
100+
}
101+
102+
Result<BatchReader::ReadBatch> AvroFileBatchReader::NextBatch() {
103+
if (next_row_to_read_ == std::numeric_limits<uint64_t>::max()) {
104+
next_row_to_read_ = 0;
105+
}
106+
try {
107+
while (array_builder_->length() < batch_size_) {
108+
if (!reader_->hasMore()) {
109+
break;
110+
}
111+
reader_->decr();
112+
PAIMON_RETURN_NOT_OK(AvroDirectDecoder::DecodeAvroToBuilder(
113+
reader_->dataSchema().root(), read_fields_projection_, &reader_->decoder(),
114+
array_builder_.get(), &decode_context_));
115+
}
116+
previous_first_row_ = next_row_to_read_;
117+
next_row_to_read_ += array_builder_->length();
118+
if (array_builder_->length() == 0) {
119+
return BatchReader::MakeEofBatch();
120+
}
121+
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Array> array,
122+
array_builder_->Finish());
123+
std::unique_ptr<ArrowArray> c_array = std::make_unique<ArrowArray>();
124+
std::unique_ptr<ArrowSchema> c_schema = std::make_unique<ArrowSchema>();
125+
PAIMON_RETURN_NOT_OK_FROM_ARROW(arrow::ExportArray(*array, c_array.get(), c_schema.get()));
126+
return make_pair(std::move(c_array), std::move(c_schema));
127+
} catch (const ::avro::Exception& e) {
128+
return Status::Invalid(fmt::format("avro reader next batch failed. {}", e.what()));
129+
} catch (const std::exception& e) {
130+
return Status::Invalid(fmt::format("avro reader next batch failed. {}", e.what()));
131+
} catch (...) {
132+
return Status::Invalid("avro reader next batch failed. unknown error");
133+
}
134+
}
135+
136+
Status AvroFileBatchReader::SetReadSchema(::ArrowSchema* read_schema,
137+
const std::shared_ptr<Predicate>& predicate,
138+
const std::optional<RoaringBitmap32>& selection_bitmap) {
139+
if (!read_schema) {
140+
return Status::Invalid("SetReadSchema failed: read schema cannot be nullptr");
141+
}
142+
// TODO(menglingda.mld): support predicate
143+
if (selection_bitmap) {
144+
// TODO(menglingda.mld): support bitmap
145+
}
146+
previous_first_row_ = std::numeric_limits<uint64_t>::max();
147+
next_row_to_read_ = std::numeric_limits<uint64_t>::max();
148+
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<arrow::Schema> arrow_read_schema,
149+
arrow::ImportSchema(read_schema));
150+
PAIMON_ASSIGN_OR_RAISE(std::shared_ptr<arrow::Schema> file_schema,
151+
ArrowUtils::DataTypeToSchema(file_data_type_));
152+
PAIMON_ASSIGN_OR_RAISE(read_fields_projection_,
153+
CalculateReadFieldsProjection(file_schema, arrow_read_schema->fields()));
154+
array_builder_->Reset();
155+
std::shared_ptr<::arrow::DataType> read_data_type = arrow::struct_(arrow_read_schema->fields());
156+
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(array_builder_,
157+
arrow::MakeBuilder(read_data_type, arrow_pool_.get()));
158+
return Status::OK();
159+
}
160+
161+
Result<std::set<size_t>> AvroFileBatchReader::CalculateReadFieldsProjection(
162+
const std::shared_ptr<::arrow::Schema>& file_schema, const arrow::FieldVector& read_fields) {
163+
std::set<size_t> projection_set;
164+
PAIMON_ASSIGN_OR_RAISE(std::vector<int32_t> projection,
165+
ArrowUtils::CreateProjection(file_schema, read_fields));
166+
int32_t prev_index = -1;
167+
for (auto& index : projection) {
168+
if (index <= prev_index) {
169+
return Status::Invalid(
170+
"SetReadSchema failed: read schema fields order is different from file schema");
171+
}
172+
prev_index = index;
173+
projection_set.insert(index);
174+
}
175+
return projection_set;
176+
}
177+
178+
Result<std::unique_ptr<::ArrowSchema>> AvroFileBatchReader::GetFileSchema() const {
179+
assert(reader_);
180+
auto c_schema = std::make_unique<::ArrowSchema>();
181+
PAIMON_RETURN_NOT_OK_FROM_ARROW(arrow::ExportType(*file_data_type_, c_schema.get()));
182+
return c_schema;
183+
}
184+
185+
Result<uint64_t> AvroFileBatchReader::GetNumberOfRows() const {
186+
if (!total_rows_) {
187+
PAIMON_ASSIGN_OR_RAISE(int64_t current_pos, input_stream_->GetPos());
188+
ScopeGuard stream_guard([this, current_pos]() -> void {
189+
// reset input stream position to original position
190+
Status status = input_stream_->Seek(current_pos, SeekOrigin::FS_SEEK_SET);
191+
(void)status;
192+
});
193+
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<::avro::DataFileReaderBase> reader,
194+
CreateDataFileReader(input_stream_, pool_));
195+
ScopeGuard reader_guard([&reader]() -> void { reader->close(); });
196+
try {
197+
while (reader->hasMore()) {
198+
reader->decr();
199+
total_rows_ = total_rows_.value_or(0) + 1;
200+
}
201+
} catch (const ::avro::Exception& e) {
202+
return Status::Invalid(fmt::format("avro reader GetNumberOfRows failed. {}", e.what()));
203+
} catch (const std::exception& e) {
204+
return Status::Invalid(fmt::format("avro reader GetNumberOfRows failed. {}", e.what()));
205+
} catch (...) {
206+
return Status::Invalid("avro reader GetNumberOfRows failed. unknown error");
207+
}
208+
}
209+
return *total_rows_;
210+
}
211+
212+
} // namespace paimon::avro
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
#pragma once
20+
21+
#include <memory>
22+
#include <set>
23+
#include <utility>
24+
#include <vector>
25+
26+
#include "avro/DataFile.hh"
27+
#include "paimon/format/avro/avro_direct_decoder.h"
28+
#include "paimon/memory/memory_pool.h"
29+
#include "paimon/metrics.h"
30+
#include "paimon/reader/file_batch_reader.h"
31+
#include "paimon/result.h"
32+
33+
namespace paimon::avro {
34+
35+
class AvroFileBatchReader : public FileBatchReader {
36+
public:
37+
static Result<std::unique_ptr<AvroFileBatchReader>> Create(
38+
const std::shared_ptr<InputStream>& input_stream, int32_t batch_size,
39+
const std::shared_ptr<MemoryPool>& pool);
40+
41+
~AvroFileBatchReader() override;
42+
43+
Result<BatchReader::ReadBatch> NextBatch() override;
44+
45+
Result<std::unique_ptr<::ArrowSchema>> GetFileSchema() const override;
46+
47+
Status SetReadSchema(::ArrowSchema* read_schema, const std::shared_ptr<Predicate>& predicate,
48+
const std::optional<RoaringBitmap32>& selection_bitmap) override;
49+
50+
Result<uint64_t> GetPreviousBatchFirstRowNumber() const override {
51+
return previous_first_row_;
52+
}
53+
54+
Result<uint64_t> GetNumberOfRows() const override;
55+
56+
std::shared_ptr<Metrics> GetReaderMetrics() const override {
57+
return metrics_;
58+
}
59+
60+
void Close() override {
61+
DoClose();
62+
}
63+
64+
bool SupportPreciseBitmapSelection() const override {
65+
return false;
66+
}
67+
68+
private:
69+
void DoClose();
70+
71+
static Result<std::unique_ptr<::avro::DataFileReaderBase>> CreateDataFileReader(
72+
const std::shared_ptr<InputStream>& input_stream, const std::shared_ptr<MemoryPool>& pool);
73+
74+
static Result<std::set<size_t>> CalculateReadFieldsProjection(
75+
const std::shared_ptr<::arrow::Schema>& file_schema, const arrow::FieldVector& read_fields);
76+
77+
AvroFileBatchReader(const std::shared_ptr<InputStream>& input_stream,
78+
const std::shared_ptr<::arrow::DataType>& file_data_type,
79+
std::unique_ptr<::avro::DataFileReaderBase>&& reader,
80+
std::unique_ptr<arrow::ArrayBuilder>&& array_builder,
81+
std::unique_ptr<arrow::MemoryPool>&& arrow_pool, int32_t batch_size,
82+
const std::shared_ptr<MemoryPool>& pool);
83+
84+
static constexpr size_t BUFFER_SIZE = 1024 * 1024; // 1M
85+
86+
std::shared_ptr<MemoryPool> pool_;
87+
std::unique_ptr<arrow::MemoryPool> arrow_pool_;
88+
std::shared_ptr<InputStream> input_stream_;
89+
std::shared_ptr<::arrow::DataType> file_data_type_;
90+
std::unique_ptr<::avro::DataFileReaderBase> reader_;
91+
std::unique_ptr<arrow::ArrayBuilder> array_builder_;
92+
std::optional<std::set<size_t>> read_fields_projection_;
93+
uint64_t previous_first_row_ = std::numeric_limits<uint64_t>::max();
94+
uint64_t next_row_to_read_ = std::numeric_limits<uint64_t>::max();
95+
mutable std::optional<uint64_t> total_rows_ = std::nullopt;
96+
const int32_t batch_size_;
97+
bool close_ = false;
98+
std::shared_ptr<Metrics> metrics_;
99+
// Decode context for reusing scratch buffers
100+
AvroDirectDecoder::DecodeContext decode_context_;
101+
};
102+
103+
} // namespace paimon::avro

0 commit comments

Comments
 (0)