blob: 0a0a858d1bb2d15e9fa0214f2226b24c48afffdb [file]
/*
* 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/avro/avro_file_batch_reader.h"
#include <memory>
#include <string>
#include <utility>
#include "arrow/api.h"
#include "arrow/c/bridge.h"
#include "arrow/ipc/api.h"
#include "gtest/gtest.h"
#include "paimon/common/utils/path_util.h"
#include "paimon/core/manifest/manifest_file.h"
#include "paimon/core/manifest/manifest_list.h"
#include "paimon/format/file_format.h"
#include "paimon/format/file_format_factory.h"
#include "paimon/fs/local/local_file_system.h"
#include "paimon/testing/utils/read_result_collector.h"
#include "paimon/testing/utils/testharness.h"
#include "paimon/testing/utils/timezone_guard.h"
namespace paimon::avro::test {
class AvroFileBatchReaderTest : public ::testing::Test, public ::testing::WithParamInterface<bool> {
public:
void SetUp() override {
ASSERT_OK_AND_ASSIGN(file_format_,
FileFormatFactory::Get("avro", {{Options::FILE_FORMAT, "avro"}}));
fs_ = std::make_shared<LocalFileSystem>();
dir_ = ::paimon::test::UniqueTestDirectory::Create();
ASSERT_TRUE(dir_);
pool_ = GetDefaultPool();
}
void TearDown() override {}
void WriteData(const std::shared_ptr<arrow::Array>& src_array, const std::string& file_path,
const std::string& compression) {
arrow::Schema src_schema(src_array->type()->fields());
::ArrowSchema c_schema;
ASSERT_TRUE(arrow::ExportSchema(src_schema, &c_schema).ok());
ASSERT_OK_AND_ASSIGN(auto writer_builder,
file_format_->CreateWriterBuilder(&c_schema, /*batch_size=*/-1));
ASSERT_OK_AND_ASSIGN(std::shared_ptr<OutputStream> out,
fs_->Create(file_path, /*overwrite=*/false));
ASSERT_OK_AND_ASSIGN(auto writer, writer_builder->Build(out, compression));
::ArrowArray arrow_array;
ASSERT_TRUE(arrow::ExportArray(*src_array, &arrow_array).ok());
ASSERT_OK(writer->AddBatch(&arrow_array));
ASSERT_OK(writer->Flush());
ASSERT_OK(writer->Finish());
ASSERT_OK(out->Flush());
ASSERT_OK(out->Close());
}
std::pair<std::unique_ptr<FileBatchReader>, std::shared_ptr<arrow::ChunkedArray>> ReadData(
const std::string& file_path, int32_t read_batch_size) {
EXPECT_OK_AND_ASSIGN(auto reader_builder,
file_format_->CreateReaderBuilder(read_batch_size));
EXPECT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, fs_->Open(file_path));
EXPECT_OK_AND_ASSIGN(auto batch_reader, reader_builder->Build(in));
EXPECT_OK_AND_ASSIGN(auto result_array, ::paimon::test::ReadResultCollector::CollectResult(
batch_reader.get()));
return std::make_pair(std::move(batch_reader), result_array);
}
private:
std::shared_ptr<MemoryPool> pool_;
std::shared_ptr<FileFormat> file_format_;
std::shared_ptr<FileSystem> fs_;
std::unique_ptr<paimon::test::UniqueTestDirectory> dir_;
};
TEST_F(AvroFileBatchReaderTest, TestReadDataWithNull) {
std::string path = paimon::test::GetDataDir() + "/avro/data/avro_with_null";
auto [reader_holder, result_array] = ReadData(path, /*read_batch_size=*/1024);
arrow::FieldVector fields = {
arrow::field("_KEY_f0", arrow::utf8(), /*nullable=*/true),
arrow::field("_SEQUENCE_NUMBER", arrow::int64(), /*nullable=*/true),
arrow::field("_VALUE_KIND", arrow::int32(), /*nullable=*/true),
arrow::field("f0", arrow::utf8(), /*nullable=*/true),
arrow::field("f1", arrow::utf8(), /*nullable=*/true),
arrow::field("f2", arrow::int32(), /*nullable=*/true),
arrow::field("f3", arrow::float64(), /*nullable=*/true)};
auto arrow_data_type = arrow::struct_(fields);
std::shared_ptr<arrow::ChunkedArray> expected_array;
auto array_status = arrow::ipc::internal::json::ChunkedArrayFromJSON(arrow_data_type, {R"([
["Alex", 2, 3, "Alex", "20250326", 18, 10.1],
["Bob", 3, 3, "Bob", "20250326", 19, 11.1],
["Evan", 1, 0, "Evan", "20250326", null, 14.1]
])"},
&expected_array);
ASSERT_TRUE(array_status.ok()) << array_status.ToString();
ASSERT_TRUE(result_array->Equals(expected_array));
ASSERT_TRUE(expected_array->Equals(result_array));
auto read_metrics = reader_holder->GetReaderMetrics();
ASSERT_TRUE(read_metrics);
}
TEST_F(AvroFileBatchReaderTest, TestReadWithDifferentBatchSize) {
std::string file_path = PathUtil::JoinPath(dir_->Str(), "file.avro");
arrow::FieldVector fields = {
arrow::field("f0", arrow::boolean()), arrow::field("f1", arrow::int32()),
arrow::field("f2", arrow::int64()), arrow::field("f3", arrow::float32()),
arrow::field("f4", arrow::float64()), arrow::field("f5", arrow::utf8()),
arrow::field("f6", arrow::binary())};
auto arrow_data_type = arrow::struct_(fields);
size_t length = 600;
std::string data_str = "[";
for (size_t i = 0; i < length; i++) {
if (i % 3 == 0) {
data_str.append(fmt::format(R"([{}, {}, {}, {}, {}, "str_{}", "bin_{}"])", "true", i,
i * 100000000000L, i * 0.12, i * 123.45678901, i, i));
} else if (i % 3 == 1) {
data_str.append(fmt::format(R"([{}, -{}, -{}, -{}, -{}, "string_{}", "binary_{}"])",
"false", i, i * 100000000000L, i * 0.12, i * 123.45678901,
i, i));
} else {
data_str.append("[null, null, null, null, null, null, null]");
}
if (i != length - 1) {
data_str.append(",");
}
}
data_str.append("]");
std::shared_ptr<arrow::Array> src_array =
arrow::ipc::internal::json::ArrayFromJSON(arrow_data_type, data_str).ValueOrDie();
ASSERT_TRUE(src_array);
WriteData(src_array, file_path, /*compression=*/"zstd");
for (int32_t batch_size : {1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1}) {
auto [reader_holder, result_array] = ReadData(file_path, batch_size);
std::shared_ptr<arrow::ChunkedArray> expected_array;
auto array_status = arrow::ipc::internal::json::ChunkedArrayFromJSON(
arrow_data_type, {data_str}, &expected_array);
ASSERT_TRUE(array_status.ok()) << array_status.ToString();
ASSERT_TRUE(result_array->Equals(expected_array));
ASSERT_TRUE(expected_array->Equals(result_array));
}
}
TEST_F(AvroFileBatchReaderTest, TestReadAllTypes) {
std::string path = paimon::test::GetDataDir() + "/avro/data/avro_all_types";
auto [reader_holder, result_array] = ReadData(path, /*read_batch_size=*/1024);
arrow::FieldVector fields = {
arrow::field("f0", arrow::boolean()),
arrow::field("f1", arrow::int32()),
arrow::field("f2", arrow::int32()),
arrow::field("f3", arrow::int32()),
arrow::field("f4", arrow::int64()),
arrow::field("f5", arrow::float32()),
arrow::field("f6", arrow::float64()),
arrow::field("f7", arrow::utf8()),
arrow::field("f8", arrow::binary()),
arrow::field("f10", arrow::list(arrow::float32())),
arrow::field("f11", arrow::struct_({arrow::field("f0", arrow::boolean()),
arrow::field("f1", arrow::int64())})),
arrow::field("f12", arrow::timestamp(arrow::TimeUnit::MICRO)),
arrow::field("f13", arrow::date32()),
arrow::field("f14", arrow::decimal128(2, 2)),
arrow::field("f15", arrow::decimal128(10, 10)),
arrow::field("f16", arrow::decimal128(19, 19))};
auto arrow_data_type = arrow::struct_(fields);
std::shared_ptr<arrow::ChunkedArray> expected_array;
auto array_status = arrow::ipc::internal::json::ChunkedArrayFromJSON(arrow_data_type, {R"([
[true, 127, 32767, 2147483647, 9999999999999, 1234.56, 1234567890.0987654321, "aa", "qq", [0.1, 0.2], [true, null], "1970-01-01 00:02:03.123123", 2456, "0.22", "0.1234567890", "0.1234567890987654321"],
[false, -128, -32768, -2147483648, -9999999999999, -1234.56, -1234567890.0987654321, null, "ww", [-0.1, -0.2, null, 0.3, 0.4], [null, 2], "1970-01-01 00:16:39.999999", null, "-0.22", "-0.1234567890", null],
[null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null]
])"},
&expected_array);
ASSERT_TRUE(array_status.ok()) << array_status.ToString();
ASSERT_TRUE(result_array->Equals(expected_array)) << result_array->ToString();
ASSERT_TRUE(expected_array->Equals(result_array)) << result_array->ToString();
}
TEST_P(AvroFileBatchReaderTest, TestReadTimestampTypes) {
auto enable_tz = GetParam();
std::string timezone_str = enable_tz ? "Asia/Tokyo" : "Asia/Shanghai";
paimon::test::TimezoneGuard tz_guard(timezone_str);
std::string path = paimon::test::GetDataDir() +
"/avro/append_with_multiple_ts_precision_and_timezone.db/"
"append_with_multiple_ts_precision_and_timezone/bucket-0/"
"data-441e233b-529d-4a8f-a0a4-25c2c84fb965-0.avro";
ASSERT_OK_AND_ASSIGN(auto reader_builder,
file_format_->CreateReaderBuilder(/*batch_size=*/1024));
ASSERT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, fs_->Open(path));
ASSERT_OK_AND_ASSIGN(auto batch_reader, reader_builder->Build(in));
auto timezone = DateTimeUtils::GetLocalTimezoneName();
arrow::FieldVector read_fields = {
arrow::field("ts_sec", arrow::timestamp(arrow::TimeUnit::SECOND)),
arrow::field("ts_milli", arrow::timestamp(arrow::TimeUnit::MILLI)),
arrow::field("ts_micro", arrow::timestamp(arrow::TimeUnit::MICRO)),
arrow::field("ts_tz_sec", arrow::timestamp(arrow::TimeUnit::SECOND, timezone)),
arrow::field("ts_tz_milli", arrow::timestamp(arrow::TimeUnit::MILLI, timezone)),
arrow::field("ts_tz_micro", arrow::timestamp(arrow::TimeUnit::MICRO, timezone)),
};
auto read_schema = arrow::schema(read_fields);
std::unique_ptr<ArrowSchema> c_schema = std::make_unique<ArrowSchema>();
ASSERT_TRUE(arrow::ExportSchema(*read_schema, c_schema.get()).ok());
EXPECT_OK(batch_reader->SetReadSchema(c_schema.get(), /*predicate=*/nullptr,
/*selection_bitmap=*/std::nullopt));
// check array
ASSERT_OK_AND_ASSIGN(auto result_array,
::paimon::test::ReadResultCollector::CollectResult(batch_reader.get()));
std::shared_ptr<arrow::ChunkedArray> expected_array;
auto array_status =
arrow::ipc::internal::json::ChunkedArrayFromJSON(arrow::struct_(read_fields), {R"([
["1970-01-01T00:00:01","1970-01-01T00:00:00.001","1970-01-01T00:00:00.000001","1970-01-01T00:00:02","1970-01-01T00:00:00.002","1970-01-01T00:00:00.000002"],
[null,"1970-01-01T00:00:00.003",null,null,"1970-01-01T00:00:00.004",null],
["1970-01-01T00:00:05",null,"1970-01-01T00:00:00.000005","1970-01-01T00:00:06",null,"1970-01-01T00:00:00.000006"]
])"},
&expected_array);
ASSERT_TRUE(array_status.ok()) << array_status.ToString();
ASSERT_TRUE(result_array->Equals(expected_array)) << result_array->ToString();
ASSERT_TRUE(expected_array->Equals(result_array));
}
TEST_F(AvroFileBatchReaderTest, TestReadMapTypes) {
std::string path = paimon::test::GetDataDir() +
"/avro/append_with_multiple_map.db/"
"append_with_multiple_map/bucket-0/"
"data-72442742-e49e-48a4-a736-a2475aac2d2c-0.avro";
ASSERT_OK_AND_ASSIGN(auto reader_builder,
file_format_->CreateReaderBuilder(/*batch_size=*/1024));
ASSERT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, fs_->Open(path));
ASSERT_OK_AND_ASSIGN(auto batch_reader, reader_builder->Build(in));
arrow::FieldVector read_fields = {
arrow::field("f0", arrow::map(arrow::int32(), arrow::int32())),
arrow::field("f1", arrow::map(arrow::float64(), arrow::float64())),
arrow::field("f2", arrow::map(arrow::utf8(), arrow::utf8())),
arrow::field("f3", arrow::map(arrow::utf8(), arrow::binary())),
arrow::field("f4", arrow::map(arrow::timestamp(arrow::TimeUnit::MICRO),
arrow::timestamp(arrow::TimeUnit::MICRO))),
arrow::field("f5", arrow::map(arrow::utf8(), arrow::list(arrow::float64()))),
arrow::field("f6", arrow::map(arrow::utf8(), arrow::map(arrow::float64(), arrow::utf8()))),
arrow::field("f7", arrow::map(arrow::int64(),
arrow::struct_({field("f0", arrow::int32()),
field("f1", arrow::utf8()),
field("f2", arrow::decimal128(5, 2))})))};
auto read_schema = arrow::schema(read_fields);
std::unique_ptr<ArrowSchema> c_schema = std::make_unique<ArrowSchema>();
ASSERT_TRUE(arrow::ExportSchema(*read_schema, c_schema.get()).ok());
EXPECT_OK(batch_reader->SetReadSchema(c_schema.get(), /*predicate=*/nullptr,
/*selection_bitmap=*/std::nullopt));
// check array
ASSERT_OK_AND_ASSIGN(auto result_array,
::paimon::test::ReadResultCollector::CollectResult(batch_reader.get()));
std::shared_ptr<arrow::ChunkedArray> expected_array;
auto array_status =
arrow::ipc::internal::json::ChunkedArrayFromJSON(arrow::struct_(read_fields), {R"([
[
[[1,10],[2,20]],
[[1.1,10.1],[2.2,20.2]],
[["key1","val1"],["key2","val2"]],
[["123456","abcdef"]],
[["2023-01-01 12:00:00.123000","2023-01-01 12:00:00.123000"],["2023-01-02 13:30:00.456000","2023-01-02 13:30:00.456000"]],
[["arr_key",[1.5, 2.5, 3.5]]],
[["outer_key",[[99.9,"nested_val"]]]],
[[1000, [42, "row_str", "123.45"]]]
]
])"},
&expected_array);
ASSERT_TRUE(array_status.ok()) << array_status.ToString();
ASSERT_TRUE(result_array->Equals(expected_array)) << result_array->ToString() << std::endl;
ASSERT_TRUE(expected_array->Equals(result_array));
}
TEST_F(AvroFileBatchReaderTest, TestSetReadSchemaRejectNestedSubFieldProjection) {
std::string path = PathUtil::JoinPath(dir_->Str(), "nested_projection_unsupported.avro");
arrow::FieldVector write_fields = {
arrow::field("f0", arrow::int32()),
arrow::field("f1", arrow::struct_({arrow::field("a", arrow::int32()),
arrow::field("b", arrow::utf8())}))};
auto write_type = arrow::struct_(write_fields);
auto write_array = arrow::ipc::internal::json::ArrayFromJSON(write_type, R"([
[1, [10, "x"]],
[2, [20, "y"]]
])")
.ValueOrDie();
WriteData(write_array, path, /*compression=*/"null");
ASSERT_OK_AND_ASSIGN(auto reader_builder,
file_format_->CreateReaderBuilder(/*batch_size=*/1024));
ASSERT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, fs_->Open(path));
ASSERT_OK_AND_ASSIGN(auto batch_reader, reader_builder->Build(in));
arrow::FieldVector read_fields = {
arrow::field("f0", arrow::int32()),
arrow::field("f1", arrow::struct_({arrow::field("a", arrow::int32())}))};
auto read_schema = arrow::schema(read_fields);
std::unique_ptr<ArrowSchema> c_schema = std::make_unique<ArrowSchema>();
ASSERT_TRUE(arrow::ExportSchema(*read_schema, c_schema.get()).ok());
ASSERT_NOK_WITH_MSG(batch_reader->SetReadSchema(c_schema.get(), /*predicate=*/nullptr,
/*selection_bitmap=*/std::nullopt),
"does not support nested sub-field projection");
}
TEST_F(AvroFileBatchReaderTest, TestGetPreviousBatchFileRowId) {
std::string path = paimon::test::GetDataDir() +
"/avro/append_simple.db/"
"append_simple/bucket-0/"
"data-d7d1c416-6e34-4834-af87-341d09418f0c-0.avro";
ASSERT_OK_AND_ASSIGN(auto reader_builder, file_format_->CreateReaderBuilder(/*batch_size=*/1));
ASSERT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, fs_->Open(path));
ASSERT_OK_AND_ASSIGN(auto reader, reader_builder->Build(in));
arrow::FieldVector read_fields = {
arrow::field("f0", arrow::int32()), arrow::field("f1", arrow::float64()),
arrow::field("f2", arrow::utf8()),
arrow::field("f3",
arrow::struct_({arrow::field("f0", arrow::map(arrow::utf8(), arrow::int32())),
arrow::field("f1", arrow::list(arrow::int32()))}))};
auto read_schema = arrow::schema(read_fields);
std::unique_ptr<ArrowSchema> c_schema = std::make_unique<ArrowSchema>();
ASSERT_TRUE(arrow::ExportSchema(*read_schema, c_schema.get()).ok());
EXPECT_OK(reader->SetReadSchema(c_schema.get(), /*predicate=*/nullptr,
/*selection_bitmap=*/std::nullopt));
ASSERT_OK_AND_ASSIGN(auto num_rows, reader->GetNumberOfRows());
ASSERT_EQ(4, num_rows);
ASSERT_NOK(reader->GetPreviousBatchFileRowId(0));
ASSERT_OK_AND_ASSIGN(auto batch1, reader->NextBatch());
ArrowArrayRelease(batch1.first.get());
ArrowSchemaRelease(batch1.second.get());
ASSERT_EQ(0, reader->GetPreviousBatchFileRowId(0).value());
ASSERT_OK_AND_ASSIGN(auto batch2, reader->NextBatch());
ASSERT_EQ(1, reader->GetPreviousBatchFileRowId(0).value());
ArrowArrayRelease(batch2.first.get());
ArrowSchemaRelease(batch2.second.get());
ASSERT_OK_AND_ASSIGN(auto batch3, reader->NextBatch());
ASSERT_EQ(2, reader->GetPreviousBatchFileRowId(0).value());
ArrowArrayRelease(batch3.first.get());
ArrowSchemaRelease(batch3.second.get());
ASSERT_OK_AND_ASSIGN(auto batch4, reader->NextBatch());
ASSERT_EQ(3, reader->GetPreviousBatchFileRowId(0).value());
ArrowArrayRelease(batch4.first.get());
ArrowSchemaRelease(batch4.second.get());
ASSERT_OK_AND_ASSIGN(auto batch5, reader->NextBatch());
ASSERT_NOK(reader->GetPreviousBatchFileRowId(0));
ASSERT_TRUE(BatchReader::IsEofBatch(batch5));
}
TEST_F(AvroFileBatchReaderTest, TestSetReadSchemaResetsReaderToFirstRow) {
std::string file_path = PathUtil::JoinPath(dir_->Str(), "file.avro");
arrow::FieldVector fields = {
arrow::field("f0", arrow::int32()),
arrow::field("f1", arrow::int32()),
};
auto file_data_type = arrow::struct_(fields);
auto src_array = arrow::ipc::internal::json::ArrayFromJSON(file_data_type, R"([
[1, 10],
[2, 20],
[3, 30],
[4, 40]
])")
.ValueOrDie();
WriteData(src_array, file_path, /*compression=*/"null");
ASSERT_OK_AND_ASSIGN(auto reader_builder, file_format_->CreateReaderBuilder(/*batch_size=*/2));
ASSERT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, fs_->Open(file_path));
ASSERT_OK_AND_ASSIGN(auto reader, reader_builder->Build(in));
ASSERT_OK_AND_ASSIGN(auto first_batch, reader->NextBatch());
ASSERT_EQ(0, reader->GetPreviousBatchFileRowId(0).value());
auto first_array =
arrow::ImportArray(first_batch.first.get(), first_batch.second.get()).ValueOrDie();
ASSERT_TRUE(first_array->Equals(src_array->Slice(0, 2))) << first_array->ToString();
auto read_schema = arrow::schema({arrow::field("f1", arrow::int32())});
std::unique_ptr<ArrowSchema> c_schema = std::make_unique<ArrowSchema>();
ASSERT_TRUE(arrow::ExportSchema(*read_schema, c_schema.get()).ok());
ASSERT_OK(reader->SetReadSchema(c_schema.get(), /*predicate=*/nullptr,
/*selection_bitmap=*/std::nullopt));
ASSERT_NOK(reader->GetPreviousBatchFileRowId(0));
ASSERT_OK_AND_ASSIGN(auto projected_batch, reader->NextBatch());
ASSERT_EQ(0, reader->GetPreviousBatchFileRowId(0).value());
auto projected_array =
arrow::ImportArray(projected_batch.first.get(), projected_batch.second.get()).ValueOrDie();
auto expected_projected_array = arrow::ipc::internal::json::ArrayFromJSON(
arrow::struct_({arrow::field("f1", arrow::int32())}),
R"([
[10],
[20]
])")
.ValueOrDie();
ASSERT_TRUE(projected_array->Equals(expected_projected_array)) << projected_array->ToString();
}
TEST_F(AvroFileBatchReaderTest, TestGetNumberOfRows) {
std::string file_path = PathUtil::JoinPath(dir_->Str(), "file.avro");
arrow::FieldVector fields = {
arrow::field("f0", arrow::boolean()), arrow::field("f1", arrow::int32()),
arrow::field("f2", arrow::int64()), arrow::field("f3", arrow::float32()),
arrow::field("f4", arrow::float64()), arrow::field("f5", arrow::utf8()),
arrow::field("f6", arrow::binary())};
auto arrow_data_type = arrow::struct_(fields);
size_t length = 102400;
std::string data_str = "[";
for (size_t i = 0; i < length; i++) {
data_str.append(fmt::format(R"([{}, {}, {}, {}, {}, "str_{}", "bin_{}"])", "true", i,
i * 100000000000L, i * 0.12, i * 123.45678901, i, i));
if (i != length - 1) {
data_str.append(",");
}
}
data_str.append("]");
std::shared_ptr<arrow::Array> src_array =
arrow::ipc::internal::json::ArrayFromJSON(arrow_data_type, data_str).ValueOrDie();
ASSERT_TRUE(src_array);
WriteData(src_array, file_path, /*compression=*/"null");
ASSERT_OK_AND_ASSIGN(auto reader_builder, file_format_->CreateReaderBuilder(25600));
// check GetNumberOfRows can be called at any position, and continue read
int32_t expected_batches = 4;
for (int32_t pos = 0; pos < expected_batches; pos++) {
ASSERT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, fs_->Open(file_path));
ASSERT_OK_AND_ASSIGN(auto reader, reader_builder->Build(in));
arrow::ArrayVector result_array_vector;
for (int32_t i = 0; i < pos; i++) {
ASSERT_OK_AND_ASSIGN(auto batch, reader->NextBatch());
auto result_array =
arrow::ImportArray(batch.first.get(), batch.second.get()).ValueOrDie();
result_array_vector.push_back(result_array);
}
// check number of rows, and continue read
ASSERT_OK_AND_ASSIGN(auto num_rows, reader->GetNumberOfRows());
ASSERT_EQ(length, num_rows);
for (int32_t i = pos; i < expected_batches; i++) {
ASSERT_OK_AND_ASSIGN(auto batch, reader->NextBatch());
auto result_array =
arrow::ImportArray(batch.first.get(), batch.second.get()).ValueOrDie();
result_array_vector.push_back(result_array);
}
ASSERT_OK_AND_ASSIGN(auto eof_batch, reader->NextBatch());
ASSERT_TRUE(BatchReader::IsEofBatch(eof_batch));
ASSERT_OK_AND_ASSIGN(num_rows, reader->GetNumberOfRows());
ASSERT_EQ(length, num_rows);
auto result_array = arrow::ChunkedArray(result_array_vector);
ASSERT_TRUE(result_array.Equals(arrow::ChunkedArray(src_array)));
}
}
TEST_F(AvroFileBatchReaderTest, TestReadBinaryWrittenFromBinaryAndLargeBinary) {
auto check_binary_read_result = [&](const std::shared_ptr<arrow::DataType>& write_type,
const std::string& file_name) {
std::string data_json = R"([
["descriptor-1"],
[""],
[null],
["descriptor-2"]
])";
auto write_field = arrow::field("f0", write_type);
auto write_data_type = arrow::struct_({write_field});
auto write_array =
arrow::ipc::internal::json::ArrayFromJSON(write_data_type, data_json).ValueOrDie();
std::string file_path = PathUtil::JoinPath(dir_->Str(), file_name);
WriteData(write_array, file_path, /*compression=*/"null");
// Read back with binary schema
auto read_field = arrow::field("f0", arrow::binary());
auto read_data_type = arrow::struct_({read_field});
ASSERT_OK_AND_ASSIGN(auto reader_builder,
file_format_->CreateReaderBuilder(/*batch_size=*/1024));
ASSERT_OK_AND_ASSIGN(std::shared_ptr<InputStream> in, fs_->Open(file_path));
ASSERT_OK_AND_ASSIGN(auto batch_reader, reader_builder->Build(in));
// Check GetFileSchema: regardless of write type, avro file schema is always binary
ASSERT_OK_AND_ASSIGN(auto c_file_schema, batch_reader->GetFileSchema());
auto file_schema = arrow::ImportSchema(c_file_schema.get()).ValueOrDie();
arrow::Schema expected_file_schema({read_field});
ASSERT_TRUE(file_schema->Equals(expected_file_schema));
auto read_schema = arrow::schema({read_field});
std::unique_ptr<ArrowSchema> c_schema = std::make_unique<ArrowSchema>();
ASSERT_TRUE(arrow::ExportSchema(*read_schema, c_schema.get()).ok());
EXPECT_OK(batch_reader->SetReadSchema(c_schema.get(), /*predicate=*/nullptr,
/*selection_bitmap=*/std::nullopt));
ASSERT_OK_AND_ASSIGN(auto result_array, ::paimon::test::ReadResultCollector::CollectResult(
batch_reader.get()));
auto expected_array =
arrow::ipc::internal::json::ArrayFromJSON(read_data_type, data_json).ValueOrDie();
auto expected_chunked_array = std::make_shared<arrow::ChunkedArray>(expected_array);
ASSERT_TRUE(result_array->Equals(expected_chunked_array));
};
check_binary_read_result(arrow::binary(), "binary.avro");
check_binary_read_result(arrow::large_binary(), "large-binary.avro");
}
INSTANTIATE_TEST_SUITE_P(TestParam, AvroFileBatchReaderTest, ::testing::Values(false, true));
} // namespace paimon::avro::test