| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "paimon/format/parquet/parquet_format_writer.h" |
| |
| #include <map> |
| #include <string> |
| #include <string_view> |
| #include <utility> |
| |
| #include "arrow/c/bridge.h" |
| #include "arrow/memory_pool.h" |
| #include "arrow/record_batch.h" |
| #include "arrow/util/base64.h" |
| #include "arrow/util/key_value_metadata.h" |
| #include "paimon/common/metrics/metrics_impl.h" |
| #include "paimon/common/utils/arrow/arrow_output_stream_adapter.h" |
| #include "paimon/common/utils/arrow/status_utils.h" |
| #include "paimon/format/parquet/parquet_format_defs.h" |
| #include "parquet/arrow/writer.h" |
| #include "parquet/properties.h" |
| |
| namespace arrow { |
| class MemoryPool; |
| class Schema; |
| } // namespace arrow |
| namespace paimon { |
| class OutputStream; |
| } // namespace paimon |
| struct ArrowArray; |
| |
| namespace paimon::parquet { |
| |
| Result<std::unique_ptr<ParquetFormatWriter>> ParquetFormatWriter::Create( |
| const std::shared_ptr<OutputStream>& output_stream, |
| const std::shared_ptr<arrow::Schema>& schema, |
| const std::shared_ptr<::parquet::WriterProperties>& writer_properties, uint64_t max_memory_use, |
| const std::shared_ptr<arrow::MemoryPool>& pool) { |
| auto out = std::make_shared<ArrowOutputStreamAdapter>(output_stream); |
| ::parquet::ArrowWriterProperties::Builder arrow_properties_builder; |
| auto arrow_writer_properties = |
| arrow_properties_builder.enable_deprecated_int96_timestamps()->build(); |
| PAIMON_ASSIGN_OR_RAISE_FROM_ARROW( |
| std::unique_ptr<::parquet::arrow::FileWriter> file_writer, |
| ::parquet::arrow::FileWriter::Open(*schema, pool.get(), out, writer_properties, |
| arrow_writer_properties)); |
| return std::unique_ptr<ParquetFormatWriter>( |
| new ParquetFormatWriter(std::move(file_writer), out, schema, max_memory_use, pool)); |
| } |
| |
| Status ParquetFormatWriter::AddBatch(ArrowArray* batch) { |
| PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(std::shared_ptr<::arrow::RecordBatch> record_batch, |
| arrow::ImportRecordBatch(batch, schema_)); |
| if (static_cast<uint64_t>(pool_->bytes_allocated()) > max_memory_use_) { |
| PAIMON_RETURN_NOT_OK_FROM_ARROW(writer_->NewBufferedRowGroup()); |
| } |
| PAIMON_RETURN_NOT_OK_FROM_ARROW(writer_->WriteRecordBatch(*record_batch)); |
| total_records_written_ += (*record_batch).num_rows(); |
| return Status::OK(); |
| } |
| |
| Status ParquetFormatWriter::Flush() { |
| metrics_->SetCounter(ParquetMetrics::WRITE_RECORD_COUNT, total_records_written_); |
| return Status::OK(); |
| } |
| |
| Status ParquetFormatWriter::Finish() { |
| PAIMON_RETURN_NOT_OK(Flush()); |
| PAIMON_RETURN_NOT_OK_FROM_ARROW(writer_->Close()); |
| return Status::OK(); |
| } |
| |
| Status ParquetFormatWriter::AddMetadata(const std::map<std::string, std::string>& metadata) { |
| if (metadata.empty()) { |
| return Status::OK(); |
| } |
| auto key_value_metadata = std::make_shared<arrow::KeyValueMetadata>(); |
| for (const auto& [key, value] : metadata) { |
| key_value_metadata->Append(key, arrow::util::base64_encode(std::string_view(value))); |
| } |
| PAIMON_RETURN_NOT_OK_FROM_ARROW(writer_->AddKeyValueMetadata(key_value_metadata)); |
| return Status::OK(); |
| } |
| |
| Result<bool> ParquetFormatWriter::ReachTargetSize(bool suggested_check, int64_t target_size) const { |
| if (suggested_check) { |
| PAIMON_ASSIGN_OR_RAISE(const uint64_t length, GetEstimateLength()); |
| return length >= static_cast<uint64_t>(target_size); |
| } |
| return false; |
| } |
| |
| Result<uint64_t> ParquetFormatWriter::GetEstimateLength() const { |
| PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(int64_t written_bytes, out_->Tell()); |
| return writer_->GetBufferedSize() + written_bytes; |
| } |
| |
| ParquetFormatWriter::ParquetFormatWriter(std::unique_ptr<::parquet::arrow::FileWriter> writer, |
| const std::shared_ptr<ArrowOutputStreamAdapter>& out, |
| const std::shared_ptr<arrow::Schema>& schema, |
| uint64_t max_memory_use, |
| const std::shared_ptr<arrow::MemoryPool>& pool) |
| : pool_(pool), |
| out_(out), |
| writer_(std::move(writer)), |
| schema_(schema), |
| metrics_(std::make_shared<MetricsImpl>()), |
| max_memory_use_(max_memory_use) {} |
| |
| } // namespace paimon::parquet |