| /* |
| * 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 |