@@ -47,6 +47,16 @@ Result<std::shared_ptr<::arrow::Array>> ProjectNestedArray(
4747 const arrow::MetadataColumnContext& metadata_context,
4848 const std::shared_ptr<::arrow::Int64Array>& row_positions, ::arrow::MemoryPool* pool);
4949
50+ // The reader currently supplies row positions only for top-level projection.
51+ Result<std::shared_ptr<::arrow::Array>> ProjectNestedArray (
52+ const std::shared_ptr<::arrow::Array>& array,
53+ const std::shared_ptr<::arrow::DataType>& output_arrow_type,
54+ const NestedType& nested_type, std::span<const FieldProjection> projections,
55+ const arrow::MetadataColumnContext& metadata_context, ::arrow::MemoryPool* pool) {
56+ return ProjectNestedArray (array, output_arrow_type, nested_type, projections,
57+ metadata_context, /* row_positions=*/ nullptr , pool);
58+ }
59+
5060// / \brief Create a null array of the given type and length.
5161Result<std::shared_ptr<::arrow::Array>> MakeNullArray (
5262 const std::shared_ptr<::arrow::DataType>& type, int64_t length,
@@ -142,6 +152,9 @@ Result<std::shared_ptr<::arrow::Array>> ProjectStructArray(
142152 " Inconsistent number of fields ({}) and number of output fields ({})" ,
143153 struct_type.fields ().size (), output_struct_type->num_fields ());
144154 }
155+ ICEBERG_PRECHECK (
156+ row_positions != nullptr || !NeedsRowPositions (struct_type, metadata_context),
157+ " Row positions are required to project _pos or inherit _row_id" );
145158
146159 std::vector<std::shared_ptr<::arrow::Array>> projected_arrays;
147160 projected_arrays.reserve (projections.size ());
@@ -164,12 +177,10 @@ Result<std::shared_ptr<::arrow::Array>> ProjectStructArray(
164177 if (projected_field.type ()->is_nested ()) {
165178 const auto & nested_type =
166179 internal::checked_cast<const NestedType&>(*projected_field.type ());
167- // Metadata columns belong to top-level rows; nested fields need no positions.
168180 ICEBERG_ASSIGN_OR_RAISE (
169181 projected_array,
170182 ProjectNestedArray (parquet_array, output_arrow_type, nested_type,
171- field_projection.children , metadata_context,
172- /* row_positions=*/ nullptr , pool));
183+ field_projection.children , metadata_context, pool));
173184 } else if (MetadataColumns::IsRowLineageColumn (projected_field.field_id ())) {
174185 ICEBERG_ASSIGN_OR_RAISE (
175186 projected_array,
@@ -255,12 +266,10 @@ Result<std::shared_ptr<::arrow::Array>> ProjectListArrayImpl(
255266 } else if (element_field.type ()->is_nested ()) {
256267 const auto & nested_type =
257268 internal::checked_cast<const NestedType&>(*element_field.type ());
258- // Metadata columns belong to top-level rows; list elements need no positions.
259269 ICEBERG_ASSIGN_OR_RAISE (
260270 projected_values,
261271 ProjectNestedArray (list_array->values (), output_element_type, nested_type,
262- element_projection.children , metadata_context,
263- /* row_positions=*/ nullptr , pool));
272+ element_projection.children , metadata_context, pool));
264273 } else {
265274 ICEBERG_ASSIGN_OR_RAISE (
266275 projected_values,
@@ -316,12 +325,10 @@ Result<std::shared_ptr<::arrow::Array>> ProjectMapArray(
316325 ToString (key_projection.kind ));
317326 } else if (key_type->is_nested ()) {
318327 const auto & nested_type = internal::checked_cast<const NestedType&>(*key_type);
319- // Metadata columns belong to top-level rows; map keys need no positions.
320328 ICEBERG_ASSIGN_OR_RAISE (
321329 projected_keys,
322330 ProjectNestedArray (map_array->keys (), output_map_type->key_type (), nested_type,
323- key_projection.children , metadata_context,
324- /* row_positions=*/ nullptr , pool));
331+ key_projection.children , metadata_context, pool));
325332 } else {
326333 ICEBERG_ASSIGN_OR_RAISE (
327334 projected_keys,
@@ -339,12 +346,10 @@ Result<std::shared_ptr<::arrow::Array>> ProjectMapArray(
339346 ToString (value_projection.kind ));
340347 } else if (value_type->is_nested ()) {
341348 const auto & nested_type = internal::checked_cast<const NestedType&>(*value_type);
342- // Metadata columns belong to top-level rows; map values need no positions.
343349 ICEBERG_ASSIGN_OR_RAISE (
344350 projected_items,
345351 ProjectNestedArray (map_array->items (), output_map_type->item_type (), nested_type,
346- value_projection.children , metadata_context,
347- /* row_positions=*/ nullptr , pool));
352+ value_projection.children , metadata_context, pool));
348353 } else {
349354 ICEBERG_ASSIGN_OR_RAISE (
350355 projected_items,
@@ -419,9 +424,9 @@ Result<std::shared_ptr<::arrow::Array>> ProjectNestedArray(
419424
420425} // namespace
421426
422- bool NeedsRowPositions (const Schema& read_schema ,
427+ bool NeedsRowPositions (const StructType& struct_type ,
423428 const arrow::MetadataColumnContext& metadata_context) {
424- return std::ranges::any_of (read_schema .fields (), [&](const SchemaField& field) {
429+ return std::ranges::any_of (struct_type .fields (), [&](const SchemaField& field) {
425430 return field.field_id () == MetadataColumns::kFilePositionColumnId ||
426431 (field.field_id () == MetadataColumns::kRowIdColumnId &&
427432 metadata_context.first_row_id .has_value ());
@@ -435,15 +440,13 @@ Result<std::shared_ptr<::arrow::RecordBatch>> ProjectRecordBatch(
435440 const arrow::MetadataColumnContext& metadata_context,
436441 const std::shared_ptr<::arrow::Int64Array>& row_positions,
437442 ::arrow::MemoryPool* pool) {
438- if (row_positions == nullptr && NeedsRowPositions (projected_schema, metadata_context)) {
439- return InvalidArgument (
440- " Row positions are required to project _pos or inherit _row_id" );
441- }
442- if (row_positions != nullptr && (row_positions->length () != record_batch->num_rows () ||
443- row_positions->null_count () != 0 )) {
444- return InvalidArgument (" Expected {} non-null row positions" ,
445- record_batch->num_rows ());
446- }
443+ ICEBERG_PRECHECK (
444+ row_positions != nullptr || !NeedsRowPositions (projected_schema, metadata_context),
445+ " Row positions are required to project _pos or inherit _row_id" );
446+ ICEBERG_PRECHECK (
447+ row_positions == nullptr || (row_positions->length () == record_batch->num_rows () &&
448+ row_positions->null_count () == 0 ),
449+ " Expected {} non-null row positions" , record_batch->num_rows ());
447450 auto array = std::make_shared<::arrow::StructArray>(
448451 ::arrow::struct_ (record_batch->schema ()->fields()), record_batch->num_rows(),
449452 record_batch->columns());
0 commit comments