Contains logic for writing a single leaf node to parquet. This tracks the path from root to leaf. |writer| will be called after all of the definition/repetition values have been calculated for root_range with the calculated values. It is intended to abstract the complexity of writing the levels and values to parquet.
| 537 | /// values. It is intended to abstract the complexity of writing |
| 538 | /// the levels and values to parquet. |
| 539 | Status WritePath(ElementRange root_range, PathInfo* path_info, |
| 540 | ArrowWriteContext* arrow_context, |
| 541 | MultipathLevelBuilder::CallbackFunction writer) { |
| 542 | std::vector<ElementRange> stack(path_info->path.size()); |
| 543 | MultipathLevelBuilderResult builder_result; |
| 544 | builder_result.leaf_array = path_info->primitive_array; |
| 545 | builder_result.leaf_is_nullable = path_info->leaf_is_nullable; |
| 546 | |
| 547 | if (path_info->max_def_level == 0) { |
| 548 | // This case only occurs when there are no nullable or repeated |
| 549 | // columns in the path from the root to leaf. |
| 550 | int64_t leaf_length = builder_result.leaf_array->length(); |
| 551 | builder_result.def_rep_level_count = leaf_length; |
| 552 | builder_result.post_list_visited_elements.push_back({0, leaf_length}); |
| 553 | return writer(builder_result); |
| 554 | } |
| 555 | stack[0] = root_range; |
| 556 | RETURN_NOT_OK( |
| 557 | arrow_context->def_levels_buffer->Resize(/*new_size=*/0, /*shrink_to_fit*/ false)); |
| 558 | PathWriteContext context(arrow_context->memory_pool, arrow_context->def_levels_buffer); |
| 559 | // We should need at least this many entries so reserve the space ahead of time. |
| 560 | RETURN_NOT_OK(context.def_levels.Reserve(root_range.Size())); |
| 561 | if (path_info->max_rep_level > 0) { |
| 562 | RETURN_NOT_OK(context.rep_levels.Reserve(root_range.Size())); |
| 563 | } |
| 564 | |
| 565 | auto stack_base = &stack[0]; |
| 566 | auto stack_position = stack_base; |
| 567 | // This is the main loop for calculated rep/def levels. The nodes |
| 568 | // in the path implement a chain-of-responsibility like pattern |
| 569 | // where each node can add some number of repetition/definition |
| 570 | // levels to PathWriteContext and also delegate to the next node |
| 571 | // in the path to add values. The values are added through each Run(...) |
| 572 | // call and the choice to delegate to the next node (or return to the |
| 573 | // previous node) is communicated by the return value of Run(...). |
| 574 | // The loop terminates after the first node indicates all values in |
| 575 | // |root_range| are processed. |
| 576 | while (stack_position >= stack_base) { |
| 577 | PathInfo::Node& node = path_info->path[stack_position - stack_base]; |
| 578 | struct { |
| 579 | IterationResult operator()(NullableNode& node) { |
| 580 | return node.Run(stack_position, stack_position + 1, context); |
| 581 | } |
| 582 | IterationResult operator()(ListNode& node) { |
| 583 | return node.Run(stack_position, stack_position + 1, context); |
| 584 | } |
| 585 | IterationResult operator()(NullableTerminalNode& node) { |
| 586 | return node.Run(*stack_position, context); |
| 587 | } |
| 588 | IterationResult operator()(FixedSizeListNode& node) { |
| 589 | return node.Run(stack_position, stack_position + 1, context); |
| 590 | } |
| 591 | IterationResult operator()(AllPresentTerminalNode& node) { |
| 592 | return node.Run(*stack_position, context); |
| 593 | } |
| 594 | IterationResult operator()(AllNullsTerminalNode& node) { |
| 595 | return node.Run(*stack_position, context); |
| 596 | } |