|
29 | 29 | #include "core/column/column_vector.h" |
30 | 30 | #include "core/column/variant_column_utils.h" |
31 | 31 | #include "core/column/variant_v2/column_variant_v2.h" |
| 32 | +#include "core/data_type/data_type_nullable.h" |
32 | 33 | #include "core/value/variant/variant_batch_builder.h" |
33 | 34 | #include "core/value/variant/variant_parquet_encoding.h" |
34 | 35 | #include "exprs/function/parse/variant_jsonb_parse.h" |
@@ -298,6 +299,89 @@ struct PreparedHierarchicalBatch { |
298 | 299 | DorisVector<variant_assembler_detail::PreparedMaterializedColumn> materialized; |
299 | 300 | }; |
300 | 301 |
|
| 302 | +bool can_assemble_flat_materialized(StorageMapKind storage_map_kind, const PathInData& requested, |
| 303 | + std::span<const MaterializedSlot> materialized_slots) { |
| 304 | + if (storage_map_kind != StorageMapKind::NONE || !requested.empty() || |
| 305 | + materialized_slots.empty()) { |
| 306 | + return false; |
| 307 | + } |
| 308 | + for (size_t index = 0; index < materialized_slots.size(); ++index) { |
| 309 | + const MaterializedSlot& slot = materialized_slots[index]; |
| 310 | + if (slot.relative_path.get_parts().size() != 1 || |
| 311 | + remove_nullable(slot.type)->get_primitive_type() == TYPE_ARRAY || |
| 312 | + (index != 0 && slot.relative_path == materialized_slots[index - 1].relative_path)) { |
| 313 | + return false; |
| 314 | + } |
| 315 | + } |
| 316 | + return true; |
| 317 | +} |
| 318 | + |
| 319 | +bool has_only_empty_root_payload(const PreparedHierarchicalBatch& batch, size_t rows) { |
| 320 | + if (batch.root_values == nullptr) { |
| 321 | + return true; |
| 322 | + } |
| 323 | + for (size_t row = 0; row < rows; ++row) { |
| 324 | + if ((batch.root_nulls == nullptr || batch.root_nulls[row] == 0) && |
| 325 | + batch.root_values->get_data_at(row).size != 0) { |
| 326 | + return false; |
| 327 | + } |
| 328 | + } |
| 329 | + return true; |
| 330 | +} |
| 331 | + |
| 332 | +template <typename Visitor> |
| 333 | +void visit_visible_scalar_rows(const variant_assembler_detail::PreparedMaterializedColumn& column, |
| 334 | + size_t rows, Visitor&& visitor) { |
| 335 | + DCHECK_NE(column.primitive, TYPE_ARRAY); |
| 336 | + if (column.nulls == nullptr) { |
| 337 | + for (size_t row = 0; row < rows; ++row) { |
| 338 | + visitor(row); |
| 339 | + } |
| 340 | + return; |
| 341 | + } |
| 342 | + const uint8_t* current = column.nulls; |
| 343 | + const uint8_t* end = current + rows; |
| 344 | + while (current != end) { |
| 345 | + const auto* visible = static_cast<const uint8_t*>(std::memchr(current, 0, end - current)); |
| 346 | + if (visible == nullptr) { |
| 347 | + return; |
| 348 | + } |
| 349 | + visitor(static_cast<size_t>(visible - column.nulls)); |
| 350 | + current = visible + 1; |
| 351 | + } |
| 352 | +} |
| 353 | + |
| 354 | +struct ActiveMaterializedRows { |
| 355 | + DorisVector<size_t> offsets; |
| 356 | + DorisVector<size_t> slots; |
| 357 | +}; |
| 358 | + |
| 359 | +ActiveMaterializedRows index_active_materialized_rows(const PreparedHierarchicalBatch& batch, |
| 360 | + size_t rows) { |
| 361 | + ActiveMaterializedRows result; |
| 362 | + result.offsets.resize(rows + 1); |
| 363 | + for (const auto& column : batch.materialized) { |
| 364 | + visit_visible_scalar_rows(column, rows, [&](size_t row) { |
| 365 | + if (batch.root_nulls == nullptr || batch.root_nulls[row] == 0) { |
| 366 | + ++result.offsets[row + 1]; |
| 367 | + } |
| 368 | + }); |
| 369 | + } |
| 370 | + for (size_t row = 0; row < rows; ++row) { |
| 371 | + result.offsets[row + 1] += result.offsets[row]; |
| 372 | + } |
| 373 | + result.slots.resize(result.offsets.back()); |
| 374 | + DorisVector<size_t> positions = result.offsets; |
| 375 | + for (size_t slot = 0; slot < batch.materialized.size(); ++slot) { |
| 376 | + visit_visible_scalar_rows(batch.materialized[slot], rows, [&](size_t row) { |
| 377 | + if (batch.root_nulls == nullptr || batch.root_nulls[row] == 0) { |
| 378 | + result.slots[positions[row]++] = slot; |
| 379 | + } |
| 380 | + }); |
| 381 | + } |
| 382 | + return result; |
| 383 | +} |
| 384 | + |
301 | 385 | bool has_materialized_value( |
302 | 386 | std::span<const variant_assembler_detail::PreparedMaterializedColumn> materialized, |
303 | 387 | std::span<const MaterializedSlot> materialized_slots, size_t row, |
@@ -557,14 +641,49 @@ Status assemble_hierarchical_row(StorageMapKind storage_map_kind, bool has_root, |
557 | 641 | return Status::OK(); |
558 | 642 | } |
559 | 643 |
|
| 644 | +Status assemble_flat_materialized(std::span<const MaterializedSlot> materialized_slots, |
| 645 | + const PreparedHierarchicalBatch& batch, size_t rows, |
| 646 | + ColumnNullable::MutablePtr* output) { |
| 647 | + ActiveMaterializedRows active = index_active_materialized_rows(batch, rows); |
| 648 | + VariantBatchBuilder builder({.rows = rows, .metadata_keys = materialized_slots.size()}); |
| 649 | + auto outer = ColumnUInt8::create(); |
| 650 | + outer->reserve(rows); |
| 651 | + for (size_t row_index = 0; row_index < rows; ++row_index) { |
| 652 | + auto row = builder.begin_row(); |
| 653 | + if (batch.root_nulls != nullptr && batch.root_nulls[row_index] != 0) { |
| 654 | + outer->insert_value(1); |
| 655 | + row.add_null(); |
| 656 | + row.finish(); |
| 657 | + continue; |
| 658 | + } |
| 659 | + auto object = row.start_object(); |
| 660 | + for (size_t active_index = active.offsets[row_index]; |
| 661 | + active_index < active.offsets[row_index + 1]; ++active_index) { |
| 662 | + const size_t slot_index = active.slots[active_index]; |
| 663 | + const std::string& path = materialized_slots[slot_index].relative_path.get_path(); |
| 664 | + object.add_key({path.data(), path.size()}); |
| 665 | + RETURN_IF_ERROR(variant_assembler_detail::append_materialized_value( |
| 666 | + batch.materialized[slot_index], row_index, row, 1)); |
| 667 | + } |
| 668 | + object.finish(); |
| 669 | + outer->insert_value(0); |
| 670 | + row.finish(); |
| 671 | + } |
| 672 | + publish_encoded(&builder, std::move(outer), output); |
| 673 | + return Status::OK(); |
| 674 | +} |
| 675 | + |
560 | 676 | Status assemble_hierarchical(StorageMapKind storage_map_kind, bool has_root, |
561 | 677 | const PathInData& requested, |
562 | 678 | std::span<const MaterializedSlot> materialized_slots, |
563 | | - const VariantAssemblerBatchView& batch, |
| 679 | + bool can_assemble_flat, const VariantAssemblerBatchView& batch, |
564 | 680 | ColumnNullable::MutablePtr* output) { |
565 | 681 | StorageMapRowCursor map_cursor; |
566 | 682 | PreparedHierarchicalBatch prepared = prepare_hierarchical_batch( |
567 | 683 | storage_map_kind, has_root, materialized_slots, batch, &map_cursor); |
| 684 | + if (can_assemble_flat && has_only_empty_root_payload(prepared, batch.num_rows)) { |
| 685 | + return assemble_flat_materialized(materialized_slots, prepared, batch.num_rows, output); |
| 686 | + } |
568 | 687 | VariantBatchBuilder builder( |
569 | 688 | {.rows = batch.num_rows, .metadata_keys = materialized_slots.size() + 8}); |
570 | 689 | auto outer = ColumnUInt8::create(); |
@@ -652,26 +771,31 @@ Result<std::unique_ptr<VariantAssembler>> VariantAssembler::create( |
652 | 771 | VariantAssemblerOptions options) { |
653 | 772 | RETURN_IF_ERROR_RESULT(check_options(options)); |
654 | 773 | DorisVector<MaterializedSlot> materialized = build_materialized_slots(options); |
| 774 | + const bool can_assemble_flat = can_assemble_flat_materialized( |
| 775 | + options.storage_map_kind, options.requested_path, materialized); |
655 | 776 | return std::unique_ptr<VariantAssembler>( |
656 | 777 | new VariantAssembler(options.storage_map_kind, options.has_root, options.requested_path, |
657 | | - std::move(materialized))); |
| 778 | + std::move(materialized), can_assemble_flat)); |
658 | 779 | } |
659 | 780 |
|
660 | 781 | VariantAssembler::VariantAssembler( |
661 | 782 | StorageMapKind storage_map_kind, bool has_root, const PathInData& requested, |
662 | | - DorisVector<variant_assembler_detail::MaterializedSlot> materialized) |
| 783 | + DorisVector<variant_assembler_detail::MaterializedSlot> materialized, |
| 784 | + bool can_assemble_flat_materialized) |
663 | 785 | : _storage_map_kind(storage_map_kind), |
664 | 786 | _has_root(has_root), |
665 | 787 | _requested(requested), |
666 | | - _materialized(std::move(materialized)) {} |
| 788 | + _materialized(std::move(materialized)), |
| 789 | + _can_assemble_flat_materialized(can_assemble_flat_materialized) {} |
667 | 790 |
|
668 | 791 | Status VariantAssembler::assemble(const VariantAssemblerBatchView& batch, |
669 | 792 | ColumnNullable::MutablePtr* output) const { |
670 | 793 | DORIS_CHECK(output != nullptr); |
671 | 794 | try { |
672 | 795 | ColumnNullable::MutablePtr result; |
673 | | - const Status status = assemble_hierarchical(_storage_map_kind, _has_root, _requested, |
674 | | - _materialized, batch, &result); |
| 796 | + const Status status = |
| 797 | + assemble_hierarchical(_storage_map_kind, _has_root, _requested, _materialized, |
| 798 | + _can_assemble_flat_materialized, batch, &result); |
675 | 799 | if (!status.ok()) { |
676 | 800 | return status; |
677 | 801 | } |
|
0 commit comments