blob: bbfc21ab8de11141a29af22da7b91d81afb1967b [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.
*/
#pragma once
#include <unistd.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "arrow/api.h"
#include "arrow/c/bridge.h"
#include "arrow/compute/api.h"
#include "paimon/common/reader/reader_utils.h"
#include "paimon/common/utils/arrow/status_utils.h"
#include "paimon/common/utils/date_time_utils.h"
#include "paimon/core/io/key_value_data_file_record_reader.h"
#include "paimon/core/key_value.h"
#include "paimon/reader/batch_reader.h"
#include "paimon/testing/utils/dict_array_converter.h"
namespace paimon::test {
class ReadResultCollector {
public:
ReadResultCollector() = delete;
~ReadResultCollector() = delete;
template <typename ReaderType, typename IteratorType>
static Result<std::vector<KeyValue>> CollectKeyValueResult(ReaderType* reader) {
std::vector<KeyValue> results;
while (true) {
PAIMON_ASSIGN_OR_RAISE(std::unique_ptr<IteratorType> iterator, reader->NextBatch());
if (iterator == nullptr) {
break;
}
while (true) {
if constexpr (std::is_same_v<IteratorType, KeyValueRecordReader::Iterator>) {
PAIMON_ASSIGN_OR_RAISE(bool has_next, iterator->HasNext());
if (!has_next) {
break;
}
PAIMON_ASSIGN_OR_RAISE(KeyValue kv, iterator->Next());
results.emplace_back(std::move(kv));
} else {
PAIMON_ASSIGN_OR_RAISE(bool has_next, iterator->HasNext());
if (!has_next) {
break;
}
KeyValue kv = iterator->Next();
results.emplace_back(std::move(kv));
}
}
}
return results;
}
static Result<std::shared_ptr<arrow::ChunkedArray>> CollectResult(BatchReader* batch_reader) {
return CollectResult(batch_reader, /*max simulated data processing time*/ 0);
}
// will convert dictionary array to string array for comparing results
static Result<std::shared_ptr<arrow::ChunkedArray>> CollectResult(
BatchReader* batch_reader, int64_t max_data_processing_time_in_us) {
arrow::ArrayVector result_array_vector;
int64_t seed = DateTimeUtils::GetCurrentUTCTimeUs();
std::srand(seed);
while (true) {
// Prioritize calling NextBatch. If it fails (paimon inner reader e.g.,
// PrefetchBatchReader, ApplyBitmapIndexBatchReader...), call NextBatchWithBitmap.
auto batch_result = batch_reader->NextBatch();
BatchReader::ReadBatch batch;
if (!batch_result.ok()) {
if (batch_result.status().ToString().find("should use NextBatchWithBitmap") !=
std::string::npos) {
PAIMON_ASSIGN_OR_RAISE(BatchReader::ReadBatchWithBitmap batch_with_bitmap,
batch_reader->NextBatchWithBitmap());
if (BatchReader::IsEofBatch(batch_with_bitmap)) {
break;
}
assert(!batch_with_bitmap.second.IsEmpty());
PAIMON_ASSIGN_OR_RAISE(
batch, ReaderUtils::ApplyBitmapToReadBatch(std::move(batch_with_bitmap),
arrow::default_memory_pool()));
} else {
return batch_result.status();
}
} else {
batch = std::move(batch_result).value();
if (BatchReader::IsEofBatch(batch)) {
break;
}
}
auto& [c_array, c_schema] = batch;
assert(c_array->length > 0);
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(auto result_array,
arrow::ImportArray(c_array.get(), c_schema.get()));
result_array_vector.push_back(result_array);
if (max_data_processing_time_in_us > 0) {
usleep(std::rand() % max_data_processing_time_in_us);
}
}
if (result_array_vector.empty()) {
return std::shared_ptr<arrow::ChunkedArray>();
}
// accumulate all the batch array and convert dictionary to string array together to avoid
// the problem (multiple batches in multiple stripes overlap dictionary data) being
// difficult to expose
arrow::ArrayVector converted_array_vector;
for (const auto& array : result_array_vector) {
PAIMON_ASSIGN_OR_RAISE(
std::shared_ptr<arrow::Array> converted_array,
DictArrayConverter::ConvertDictArray(array, arrow::default_memory_pool()));
converted_array_vector.push_back(converted_array);
}
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(auto chunk_array,
arrow::ChunkedArray::Make(converted_array_vector));
return chunk_array;
}
static Result<std::shared_ptr<arrow::Array>> GetArray(BatchReader::ReadBatch&& batch) {
if (BatchReader::IsEofBatch(batch)) {
return std::shared_ptr<arrow::Array>();
}
auto& [c_array, c_schema] = batch;
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(auto array,
arrow::ImportArray(c_array.get(), c_schema.get()));
return DictArrayConverter::ConvertDictArray(array, arrow::default_memory_pool());
}
static Result<BatchReader::ReadBatch> GetReadBatch(const std::shared_ptr<arrow::Array>& array) {
auto c_array = std::make_unique<ArrowArray>();
auto c_schema = std::make_unique<ArrowSchema>();
PAIMON_RETURN_NOT_OK_FROM_ARROW(arrow::ExportArray(*array, c_array.get(), c_schema.get()));
return std::make_pair(std::move(c_array), std::move(c_schema));
}
// Noted that, sort chunked array by multiple key for timestamp type may cause
// coredump in arrow, refer to https://github.com/apache/arrow/issues/47252
static Result<std::shared_ptr<arrow::ChunkedArray>> SortArray(
const std::shared_ptr<arrow::ChunkedArray>& array,
const std::shared_ptr<arrow::Schema>& schema) {
std::vector<arrow::compute::SortKey> sort_keys;
for (const auto& name : schema->field_names()) {
sort_keys.emplace_back(name, arrow::compute::SortOrder::Ascending);
}
auto sort_options = arrow::compute::SortOptions(sort_keys);
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(
auto sorted_indices, arrow::compute::SortIndices(arrow::Datum(array), sort_options));
PAIMON_ASSIGN_OR_RAISE_FROM_ARROW(
auto sorted_batch,
arrow::compute::Take(arrow::Datum(array), arrow::Datum(sorted_indices)));
return sorted_batch.chunked_array();
}
};
} // namespace paimon::test