blob: ea52d64c020a0124ea761b0a7797be7bbf404fb0 [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 <cstdint>
#include <memory>
#include <mutex>
#include <span>
#include <utility>
#include <vector>
#include <arrow/array/array_primitive.h>
#include <arrow/buffer.h>
#include <arrow/c/bridge.h>
#include <arrow/compute/api_vector.h>
#include <arrow/record_batch.h>
#include <nanoarrow/nanoarrow.h>
#include "iceberg/arrow/arrow_status_internal.h"
#include "iceberg/arrow/nanoarrow_status_internal.h"
#include "iceberg/arrow_c_data_guard_internal.h"
#include "iceberg/arrow_c_data_util_internal.h"
#include "iceberg/result.h"
#include "iceberg/util/macros.h"
namespace iceberg {
namespace {
struct ArrowProjectBatchState {
std::shared_ptr<::arrow::Schema> input_schema;
std::shared_ptr<::arrow::Schema> output_schema;
};
Result<std::shared_ptr<::arrow::Schema>> ImportArrowSchema(
const ArrowSchema& arrow_schema) {
ArrowSchema schema_copy;
ICEBERG_NANOARROW_RETURN_UNEXPECTED(ArrowSchemaDeepCopy(&arrow_schema, &schema_copy));
internal::ArrowSchemaGuard schema_copy_guard(&schema_copy);
ICEBERG_ARROW_ASSIGN_OR_RETURN(auto schema, ::arrow::ImportSchema(&schema_copy));
return schema;
}
Result<std::shared_ptr<ArrowProjectBatchState>> GetArrowProjectBatchState(
ProjectionContext& projection) {
auto state =
std::static_pointer_cast<ArrowProjectBatchState>(projection.project_batch_state());
if (state != nullptr) {
return state;
}
ICEBERG_ASSIGN_OR_RAISE(auto input_schema,
ImportArrowSchema(projection.input_arrow_schema()));
ICEBERG_ASSIGN_OR_RAISE(auto output_schema,
ImportArrowSchema(projection.output_arrow_schema()));
state = std::make_shared<ArrowProjectBatchState>(
ArrowProjectBatchState{.input_schema = std::move(input_schema),
.output_schema = std::move(output_schema)});
projection.project_batch_state() = state;
return state;
}
Result<ArrowArray> ProjectBatchArrowCompute(ArrowArray* input_batch,
std::span<const int32_t> row_indices,
ProjectionContext& projection) {
ICEBERG_PRECHECK(input_batch != nullptr, "input_batch must not be null");
ICEBERG_ASSIGN_OR_RAISE(auto state, GetArrowProjectBatchState(projection));
ICEBERG_ARROW_ASSIGN_OR_RETURN(
auto input_record_batch,
::arrow::ImportRecordBatch(input_batch, state->input_schema));
const int32_t empty_index = 0;
// Buffer::Wrap needs a valid pointer even when the zero-length buffer is never read.
const int32_t* row_indices_data =
row_indices.empty() ? &empty_index : row_indices.data();
auto index_array = std::make_shared<::arrow::Int32Array>(
static_cast<int64_t>(row_indices.size()),
::arrow::Buffer::Wrap(row_indices_data, row_indices.size()));
std::vector<std::shared_ptr<::arrow::Array>> output_columns;
output_columns.reserve(projection.selected_field_indices().size());
for (int32_t input_index : projection.selected_field_indices()) {
ICEBERG_PRECHECK(input_index >= 0 && input_index < input_record_batch->num_columns(),
"Input field index {} out of range for batch with {} columns",
input_index, input_record_batch->num_columns());
ICEBERG_ARROW_ASSIGN_OR_RETURN(
auto taken_column,
::arrow::compute::Take(*input_record_batch->column(input_index), *index_array));
output_columns.push_back(std::move(taken_column));
}
auto output_record_batch = ::arrow::RecordBatch::Make(
state->output_schema, static_cast<int64_t>(row_indices.size()),
std::move(output_columns));
ArrowArray output_array;
ICEBERG_ARROW_RETURN_NOT_OK(
::arrow::ExportRecordBatch(*output_record_batch, &output_array));
internal::ArrowArrayGuard output_array_guard(&output_array);
return std::exchange(output_array, ArrowArray{});
}
} // namespace
void RegisterArrowProjectBatch() {
static std::once_flag flag;
std::call_once(flag, []() {
ProjectionContext::RegisterProjectBatchFunction(&ProjectBatchArrowCompute);
});
}
} // namespace iceberg